A => autobind-cffi/.hg_archival.txt +6 -0
@@ 0,0 1,6 @@
+repo: 1f712da4b8a6d6a40aa8db53c48c02f3dd1fdb39
+node: 3e563174f4df61f33660c2327861bf03ed5b0913
+branch: default
+latesttag: null
+latesttagdistance: 14
+changessincelatesttag: 14
A => autobind-cffi/.hgignore +24 -0
@@ 0,0 1,24 @@
+syntax: glob
+
+.sconsign.dblite
+.settings
+*.egg-info
+*.pyc
+*.blend1
+*.blend2
+*.avi
+*.glc
+*.mp4
+*.p3d
+debug.txt
+__pycache__
+*.orig
+*.bgeconf
+.project
+.pydevproject
+
+syntax: regexp
+
+^build
+^dist
+
A => autobind-cffi/LICENSE +26 -0
@@ 0,0 1,26 @@
+
+Except when otherwise stated (look for LICENSE files in directories or
+information at the beginning of each file) all software and
+documentation is licensed as follows:
+
+ The MIT License
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation
+ files (the "Software"), to deal in the Software without
+ restriction, including without limitation the rights to use,
+ copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+
A => autobind-cffi/README +1 -0
@@ 0,0 1,1 @@
+Set of utility classes to help with writing Python-CFFI bindings for C classes. Acts as development/build-time dependency only.
A => autobind-cffi/autobind/__init__.py +489 -0
@@ 0,0 1,489 @@
+
+from __future__ import (print_function, division, absolute_import)
+
+# code generator
+
+import os
+import sys
+from tempfile import mktemp
+from subprocess import PIPE, Popen
+
+import re
+import traceback
+from cffi import FFI, CDefError, verifier
+from StringIO import StringIO
+from fnmatch import fnmatch
+
+IS_WIN32 = sys.platform == 'win32'
+
+# on windows, mingw\bin must be in path
+CPP_PATH = "cpp"
+
+################################################################################
+
+_TEMPLATE = """
+# this file is auto-generated. do not edit.
+from __future__ import (print_function, division, absolute_import)
+
+from cffi import FFI
+
+UNMANGLED_EXPORTS = [
+{all_unmangled}
+]
+
+EXPORTS = [
+{all}
+]
+
+INTERNAL_EXPORTS = [
+'_ffi',
+'EXPORTS',
+'UNMANGLED_EXPORTS',
+]
+
+__all__ = EXPORTS + UNMANGLED_EXPORTS + INTERNAL_EXPORTS
+
+from .internal import *
+
+_LIB = load_lib(\"""
+{cdefs}
+\""")
+
+{imports}
+{post_template}
+"""
+
+_IMPORT_FUNC_TEMPLATE = "{module_name} = lookup('{name}')\n"
+
+_IMPORT_SAFE_FUNC_TEMPLATE = "{module_name} = guard(lookup('{name}'))\n"
+
+################################################################################
+
+class AutoBind(object):
+ def __init__(self, options):
+ """
+ options to pass (as dict):
+
+ CDEF_PATH: path string to the folder where all cdef files are located
+ CDEF_FILES: a list of include files to parse, in correct order
+ FFI_INCLUDE: a list of ffi objects to include using FFI.include()
+ DEFINES_BLACKLIST: a list of defined symbols that should not be exported
+ DEFINES: a list of defines that help to resolve #ifdef blocks
+ PRIVATE_SYMBOLS: a list of functions that should be prepended with an underscore
+ AUTOCHECK_BLACKLIST: a list of functions that should not be wrapped with guard()
+ REPLACES: a list of tuples (A, B) to be applied for string replacement
+ PRE_REPLACES: a list of tuples (A, B) to be pre-applied for string replacement
+ LIBNAME: name of library that contains the symbols (e.g. "SDL2", "GL", ...)
+ VERIFY_SOURCE: source code containing includes necessary to verify cdefs
+ VERIFY_OPTIONS: dictionary of arguments to pass to verify (optional override)
+ AUTOMANGLE: if True, prefix an underscore to all functions dealing with pointers
+ PYPREDEFS: path to pypredefs to generate for editors such as Aptana Studio
+ OUTMODULE: path to python module that should be generated
+ GENPOSTFIX: string to append to generated module (optional)
+ STRIP: remove comments (default: true)
+ PREPROCESS: preprocess source (default: false)
+ PREPROCESS_OPTIONS: options for preprocessor, if PREPROCESS is True
+ DEBUGOUT: write debug.txt containing intermediate defs
+ FINDERROR: remove statements until problem is gone
+ AUXCACHEDIR: auxiliarly cache dir to be cleaned on build
+ """
+ self.options = options
+
+ def build_cdefs(self):
+ options = self.options
+
+ re_ifdef = re.compile(r'^\#\S+\s+(\S+)')
+ re_define = re.compile(r'^\#define\s+(\S+)(\s+\S+)?')
+ re_hex = re.compile(r'(.*)(0x[0-9A-Fa-f]+)([^0-9A-Fa-f]*.*)')
+
+ CDEF_PATH = options['CDEF_PATH']
+ PREPROCESS = options.get('PREPROCESS', False)
+ CDEF_FILES = options.get('CDEF_FILES', [])
+ _DEFINES_BLACKLIST = set(options.get('DEFINES_BLACKLIST', []))
+ _DEFINES = set(options.get('DEFINES', []))
+ _PRIVATE_SYMBOLS = set(options.get('PRIVATE_SYMBOLS', []))
+ _AUTOCHECK_BLACKLIST = set(options.get('AUTOCHECK_BLACKLIST', []))
+ REPLACES = options.get('REPLACES', [])
+ REPLACES.sort(key=(lambda k: k[0]), reverse=True)
+ PRE_REPLACES = options.get('PRE_REPLACES', [])
+ PRE_REPLACES.sort(key=(lambda k: k[0]), reverse=True)
+
+ assert os.path.isdir(CDEF_PATH)
+
+ cdefs = StringIO()
+ hdefs = StringIO()
+
+ for cdef_path in CDEF_FILES:
+ local_defines = set(_DEFINES)
+
+ cdef_path = os.path.join(CDEF_PATH, cdef_path)
+ with open(cdef_path,'r') as cdef_file:
+ cdef_header = cdef_file.read()
+
+ for a,b in PRE_REPLACES:
+ cdef_header = cdef_header.replace(a, b)
+
+ if PREPROCESS:
+ cdef_header = self.preprocess_cdefs(cdef_header)
+
+ clean_cdefs = ''
+ hdefs.write(cdef_header + '\n')
+
+ blockdesc = []
+ blockdef = []
+
+ def active_block_desc():
+ return blockdesc[-1]
+ def is_blocked():
+ return blockdef and (blockdef[-1] == False)
+ def is_meta_blocked():
+ return (len(blockdef) > 1) and (blockdef[-2] == False)
+ def block_push(value,line):
+ blockdesc.append(line)
+ blockdef.append(value)
+ def block_pop():
+ blockdesc.pop()
+ blockdef.pop()
+ def block_flip():
+ blockdef[-1] = not blockdef[-1]
+
+ eat_next = False
+ for line in cdef_header.splitlines():
+ line = line.strip()
+ # fix OCD preprocessor statements
+ if line.startswith('#'):
+ line = '#' + line[1:].strip()
+ if 0:
+ while '0x' in line:
+ # convert hexadecimal numbers to decimal ones
+ m = re_hex.search(line)
+ line = m.group(1) + str(int(m.group(2),16)) + m.group(3)
+ if eat_next:
+ if not line.endswith('\\'):
+ eat_next = False
+ elif line.startswith('#endif'):
+ block_pop()
+ elif line.startswith('#ifndef '):
+ if is_blocked():
+ block_push(False, line)
+ else:
+ m = re_ifdef.match(line)
+ block_push(not (m.group(1) in local_defines), line)
+ elif line.startswith('#ifdef '):
+ if is_blocked():
+ block_push(False, line)
+ else:
+ m = re_ifdef.match(line)
+ block_push(m.group(1) in local_defines, line)
+ elif line.startswith('#if '):
+ print('excluding',line)
+ block_push(False, line)
+ elif line.startswith('#elif'):
+ print('excluding',line)
+ elif line.startswith('#else'):
+ if is_meta_blocked():
+ pass
+ else:
+ block_flip()
+ #block_push(not is_blocked(), line)
+ elif is_blocked():
+ clean_cdefs += '//' + line + ' | excluded by: %s\n' % (active_block_desc(),)
+ elif line.startswith('#include'):
+ continue
+ elif line.startswith('#pragma'):
+ continue
+ elif line.startswith('#error'):
+ continue
+ elif line.startswith('#undef'):
+ continue
+ elif line.startswith('#define'):
+ m = re_define.match(line)
+ define = m.group(1)
+ var = m.group(2)
+ if line.endswith('\\'):
+ eat_next = True
+ if '(' in define:
+ # skip function macros
+ continue
+ local_defines.add(define)
+ if not var:
+ print('+',define)
+ elif not (define in _DEFINES_BLACKLIST) and not define.startswith('_'):
+ var = var.strip()
+ clean_cdefs += '#define ' + define + ' ... // ' + var + '\n'
+ elif line:
+ clean_cdefs += line + '\n'
+
+ # remove superfluous macros
+ for a,b in REPLACES:
+ clean_cdefs = clean_cdefs.replace(a, b)
+
+ cdefs.write(clean_cdefs + '\n')
+
+ return cdefs.getvalue(), hdefs.getvalue()
+
+ def preprocess_cdefs(self, cdefs):
+ options = self.options
+ CDEF_PATH = options['CDEF_PATH']
+ PREPROCESS_OPTIONS = options.get('PREPROCESS_OPTIONS', [])
+
+ tmppath = mktemp(suffix='.h')
+ file(tmppath, 'w').write(cdefs)
+ cmdline = ' '.join([
+ CPP_PATH,
+ '-I',CDEF_PATH,
+ ] + PREPROCESS_OPTIONS + [
+ '-P','-dD',
+ '-E',tmppath
+ ])
+ print(cmdline)
+ pipe = Popen(cmdline, shell=True, stdout=PIPE, universal_newlines=True)
+ text = pipe.communicate()[0]
+ outtext = ''
+ for line in text.splitlines():
+ line = line.strip()
+ if not line:
+ continue
+ outtext += line + '\n'
+ print(outtext)
+ return outtext
+
+ def remove_comments(self, cdefs):
+ """List cdefs in the current folder sorted by include order"""
+ tmppath = mktemp(suffix='.h')
+ file(tmppath, 'w').write(cdefs)
+ cmdline = CPP_PATH + ' -P -fpreprocessed -dD -E ' + tmppath
+ pipe = Popen(cmdline, shell=True, stdout=PIPE, universal_newlines=True)
+ text = pipe.communicate()[0]
+ outtext = ''
+ for line in text.splitlines():
+ line = line.strip()
+ if not line:
+ continue
+ outtext += line + '\n'
+ return outtext
+
+ def test_cdefs(self,cdefs,hdefs):
+ options = self.options
+ LIBNAME = options.get('LIBNAME',None)
+ DEF_VERIFY_OPTIONS = dict(
+ )
+
+ if LIBNAME:
+ DEF_VERIFY_OPTIONS['libraries'] = [LIBNAME]
+
+ VERIFY_SOURCE = options.get('VERIFY_SOURCE', hdefs)
+ VERIFY_OPTIONS = options.get('VERIFY_OPTIONS', DEF_VERIFY_OPTIONS)
+ FFI_INCLUDE = options.get('FFI_INCLUDE', [])
+ AUXCACHEDIR = options.get('AUXCACHEDIR',None)
+
+ verifier.cleanup_tmpdir()
+ if AUXCACHEDIR is not None and AUXCACHEDIR.endswith('__pycache__'):
+ verifier.cleanup_tmpdir(tmpdir=AUXCACHEDIR)
+
+ ffi = FFI()
+ for sub_ffi in FFI_INCLUDE:
+ ffi.include(sub_ffi)
+ ffi.cdef(cdefs)
+ print(VERIFY_OPTIONS)
+ capi = ffi.verify(VERIFY_SOURCE,
+ **VERIFY_OPTIONS)
+ # crosscheck
+ outcapi = {}
+ if LIBNAME:
+ dlffi = FFI()
+ for sub_ffi in FFI_INCLUDE:
+ dlffi.include(sub_ffi)
+ dlffi.cdef(cdefs)
+ dlcapi = dlffi.dlopen(LIBNAME)
+ else:
+ dlffi = ffi
+ dlcapi = capi
+ for name,value in capi.__dict__.items():
+ if name.startswith('__'):
+ print("skipping",name)
+ continue
+ if callable(value):
+ dlvalue = getattr(dlcapi, name)
+ outcapi[name] = dlvalue
+ elif isinstance(value, (int,float,long)):
+ outcapi[name] = value
+ else:
+ print("ignoring",name,value)
+ return dlffi, outcapi
+
+ def write_pypredef(self,CAPI):
+ PYPREDEFS = self.options['PYPREDEFS']
+
+ dirpath = os.path.dirname(PYPREDEFS)
+ if not os.path.isdir(dirpath):
+ os.makedirs(dirpath)
+
+ filepath = PYPREDEFS
+
+ print("writing",filepath)
+ with file(filepath, 'w') as f:
+ for name,value in CAPI.items():
+ if name.startswith('__'):
+ continue
+ if callable(value):
+ f.write('def {name}(*argv):\n """{name}"""\n\n'.format(
+ name=name))
+ if isinstance(value, (int,float,long)):
+ f.write('{name} = {typeid}\n'.format(name=name,typeid=type(value).__name__))
+
+ def safeguard_blacklisted(self,name):
+ _AUTOCHECK_BLACKLIST = set(self.options.get('AUTOCHECK_BLACKLIST',[]))
+ if name in _AUTOCHECK_BLACKLIST:
+ return True
+ for entry in _AUTOCHECK_BLACKLIST:
+ if fnmatch(name, entry):
+ return True
+ return False
+
+ def write_module(self,cdefs,hdefs,ffi,CAPI):
+ options = self.options
+ _PRIVATE_SYMBOLS = set(options.get('PRIVATE_SYMBOLS',[]))
+ AUTOMANGLE = options.get('AUTOMANGLE',False)
+ OUTMODULE = options['OUTMODULE']
+
+ f = StringIO()
+
+ imports = ''
+ exports = {
+ 'all' : '',
+ 'all_unmangled' : '',
+ }
+
+ def get_signature(value):
+ #resultsig = ffi.typeof(value).result
+ sig = ffi.typeof(value).args
+ return sig
+
+ def is_strptr_type(ctype):
+ return "'unsigned char *'" in repr(arg)
+
+ def is_ptr_type(ctype):
+ cname = ctype.cname
+ if ctype.cname == 'char *':
+ return False
+ return " *" in ctype.cname
+
+ for name in sorted(CAPI):
+ value = CAPI[name]
+ module_name = name
+ export_target = 'all'
+
+ if callable(value):
+ func_template = "lookup('{name}')"
+
+ mangle = False
+ if name in _PRIVATE_SYMBOLS:
+ mangle = True
+ elif AUTOMANGLE:
+ sig = get_signature(value)
+ for i,arg in enumerate(sig):
+ if is_ptr_type(arg):
+ print(name,"needs mangling")
+ mangle = True
+ break
+
+ if mangle:
+ module_name = '_' + module_name
+ export_target = 'all_unmangled'
+
+ if not self.safeguard_blacklisted(name):
+ func_template = "guard({func_template})".format(**locals())
+
+ func_template = func_template.format(**locals())
+ imports += "{module_name} = {func_template}\n".format(**locals())
+
+ elif isinstance(value, (int,float,long)):
+ imports += '{module_name} = {value}\n'.format(**locals())
+
+ exports[export_target] += '"{module_name}",\n'.format(**locals())
+
+ all = exports['all']
+ all_unmangled = exports['all_unmangled']
+
+ post_template = options.get('GENPOSTFIX','')
+
+ f.write(_TEMPLATE.format(**locals()))
+
+ filepath = OUTMODULE
+ print("writing",filepath)
+ with open(filepath, 'w') as outfile:
+ outfile.write(f.getvalue())
+
+ def find_error(self,cdefs):
+ lines = cdefs.splitlines()
+
+ FFI_INCLUDE = self.options.get('FFI_INCLUDE', [])
+
+ print("backtracking for broken part...")
+ upper = len(lines)
+ for step in (256,128,64,32,16,1):
+ last_parser_error = 0
+ offset = upper
+ while offset > 0:
+ print(upper, offset)
+ source = '\n'.join(lines[:offset])
+ try:
+ test_ffi = FFI()
+ for sub_ffi in FFI_INCLUDE:
+ test_ffi.include(sub_ffi)
+ test_ffi.cdef(source)
+ break
+ except CDefError:
+ last_parser_error = offset
+ offset += 1
+ except:
+ print(lines[offset-1])
+ #traceback.print_exc()
+ upper = offset
+ offset -= step
+ if offset == last_parser_error:
+ break
+
+ with file('debug.txt','w') as f:
+ print("writing to debug.txt...")
+ part_a = '\n'.join(lines[:offset])
+ part_b = '\n'.join(lines[offset:])
+ f.write(part_a)
+ f.write('\n <<<<<< BROKEN PART AROUND HERE >>>>>> \n')
+ f.write(part_b)
+ return
+
+ def build(self):
+ cdefs,hdefs = self.build_cdefs()
+ if self.options.get('STRIP',True):
+ cdefs = self.remove_comments(cdefs)
+ if self.options.get('DEBUGOUT',False):
+ print("writing debug.txt...")
+ with open('debug.txt','w') as f:
+ f.write(cdefs)
+ try:
+ ffi, CAPI = self.test_cdefs(cdefs,hdefs)
+ except:
+ traceback.print_exc()
+ if self.options.get('FINDERROR', False):
+ self.find_error(cdefs)
+ raise SystemExit, 255
+ self.write_module(cdefs, hdefs, ffi, CAPI)
+ if self.options.get('PYPREDEFS',None):
+ self.write_pypredef(CAPI)
+
+################################################################################
+
+def preprocess_c_header(text, cpp_options=[], cpp_path='mcpp'):
+ cmdline = ' '.join([
+ cpp_path,
+ ] + cpp_options + [
+ '-DSOIL_CDEF',
+ '-P',
+ ])
+ pipe = Popen(cmdline, shell=True,
+ stdin=PIPE, stdout=PIPE, universal_newlines=True)
+ result = pipe.communicate(input=text)[0]
+ assert pipe.returncode == 0
+ return result
A => autobind-cffi/setup.py +24 -0
@@ 0,0 1,24 @@
+
+import os
+from setuptools import setup
+
+def read(fname):
+ return open(os.path.join(os.path.dirname(__file__), fname)).read()
+
+setup(
+ name = "autobind-cffi",
+ version = "0.1",
+ author = "Leonard Ritter",
+ author_email = "contact@leonard-ritter.com",
+ description = ("Set of utility classes to help with writing Python-CFFI "
+ "bindings for C classes. Acts as development/build-time dependency only."),
+ license = "MIT",
+ keywords = "cffi pypy bindings",
+ url = "https://bitbucket.org/duangle/autobind-cffi",
+ packages=['autobind'],
+ long_description=read('README'),
+ classifiers=[
+ "Development Status :: 3 - Alpha",
+ "License :: OSI Approved :: MIT License",
+ ],
+)
No newline at end of file
A => glue.py/.hg_archival.txt +6 -0
@@ 0,0 1,6 @@
+repo: 60193f992356f6a6471e5ce769378b97ee892a1d
+node: 6e73417591f358b67f8f90f2ac81199bc1343237
+branch: default
+latesttag: release-0.5.1
+latesttagdistance: 31
+changessincelatesttag: 31
A => glue.py/.hgignore +14 -0
@@ 0,0 1,14 @@
+syntax: glob
+
+*.pyc
+__pycache__
+*.orig
+*.egg-info
+.project
+.pydevproject
+
+syntax: regexp
+
+^build
+^dist
+
A => glue.py/.hgtags +2 -0
@@ 0,0 1,2 @@
+b5e9614dbabbcad87f5d1ab25e506f102878b836 release-0.5
+0486868d6710e250ebd8a9f8ff7dc63a81cb77f0 release-0.5.1
A => glue.py/LICENSE +26 -0
@@ 0,0 1,26 @@
+
+Except when otherwise stated (look for LICENSE files in directories or
+information at the beginning of each file) all software and
+documentation is licensed as follows:
+
+ The MIT License
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation
+ files (the "Software"), to deal in the Software without
+ restriction, including without limitation the rights to use,
+ copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+
A => glue.py/README.text +209 -0
@@ 0,0 1,209 @@
+glue.py is a single lightweight utility module expanding Python with new
+powerful idioms for accessors, callsets, weak methods, enums, singletons and
+observable primitives that can easily be integrated with existing projects. It
+is particularly useful for writing model/view oriented applications.
+
+Installation
+============
+
+You'll require Python 2.7, Python 3+ or PyPy 1.9 to use glue.py. Earlier
+versions are not supported.
+
+glue.py can easily be installed from the Python package repository using
+distutils or pip:
+
+ pip install glue.py
+
+If you plan to make amendments to the codebase, or you're interested in the
+latest development release, it is best to check out the source tree and
+install glue.py in development mode:
+
+ python setup.py develop
+
+Usage
+=====
+
+glue.py provides a larger set of interoperating utility classes and functions,
+of which only the most important ones will be described here in detail. It's
+not a bad idea to just browse through the source and look what else is there
+besides what's covered in this introduction.
+
+Generated Accessors
+-------------------
+
+While Python supports accessors through properties, providing regulated access
+to class variables has been traditionally cumbersome and repetitive, even though
+the syntax has somewhat improved with recent versions. Consider this classical
+approach to property access:
+
+ from types import NoneType
+
+ class Sponge(object):
+ pass
+
+ class Pineapple(object):
+ on_owner_changed = None
+
+ def __init__(self):
+ self.__owner = None
+
+ def get_owner(self):
+ return self.__owner
+ def set_owner(self, value):
+ if value == self.__owner:
+ return
+ assert isinstance(value, (Sponge, NoneType))
+ self.__owner = value
+ if self.on_owner_changed:
+ self.on_owner_changed(self)
+ owner = property(get_owner, set_owner,
+ doc = "the owner of this Pineapple")
+
+apart from preventing a rewrite, the setter also provides an optional hook that
+can be triggered when the value changes. Notice how many times the word "owner"
+has to be repeated in one form or the other in order to expose this relatively
+simple attribute. Until Python 2.7, most people adapted this handy recipe to
+cut down on repetition:
+
+ def owner():
+ doc = """the owner of this pineapple"""
+ def fget(self):
+ return self.__owner
+ def fset(self, value):
+ if value == self.__owner:
+ return
+ assert isinstance(value, (Sponge, NoneType))
+ self.__owner = value
+ if self.on_owner_changed:
+ self.on_owner_changed(self)
+ return property(**locals())
+ owner = owner()
+
+That's slightly better, but not perfect, and also a little convoluted. These
+days, this approach is the status quo:
+
+ @property
+ def owner(self):
+ """The owner of this pineapple"""
+ return self.__owner
+ @owner.setter
+ def owner(self, value):
+ if value == self.__owner:
+ return
+ assert isinstance(value, (Sponge, NoneType))
+ self.__owner = value
+ if self.on_owner_changed:
+ self.on_owner_changed(self)
+
+it seems like we're mostly shuffling details around... all three solutions
+vary around 12 lines, and with a bunch of these, classes may seem a lot larger
+than they are. This is how glue.py does it:
+
+ from glue import (lazydecls, defproperty, autoinit, callset)
+ from types import NoneType
+
+ class Sponge(object):
+ pass
+
+ @lazydecls
+ class Pineapple(object):
+ on_owner_changed = callset()
+
+ owner = defproperty(
+ default = None, types=(Sponge, NoneType), hook = on_owner_changed,
+ doc = "The owner of this pineapple")
+
+ def __init__(self):
+ autoinit()
+
+...case closed. For more information, try `help(glue.defproperty)`.
+
+Callable Sets
+-------------
+
+You may have noticed that the `Pineapple` class exposes the `on_owner_changed`
+callback as a `callset`, which is just a short way of instantiating a
+`CallableSet`. Callable sets are a fast way to create observables:
+
+ from glue import callset
+
+ def func1(value):
+ print("func1 called with value",value)
+ return value+10
+
+ def func2(value):
+ print("func2 called with value",value)
+ return value+20
+
+ def func3(value):
+ print("func3 called with value",value)
+ return value+30
+
+Trying this on the command line:
+
+ >>> funcs = callset([func1,func2,func3])
+ >>> print(max(funcs(10)))
+ func1 called with value 10
+ func2 called with value 10
+ func3 called with value 10
+ 40
+
+In the pineapple example, this is how a view would typically subscribe to
+events sent by the `Pineapple` class:
+
+ @lazydecls
+ class Squid(object):
+ neighbor_house = defproperty()
+ annoying_coworkers = defproperty(default = set)
+
+ def __init__(self, **kwargs):
+ # auto-assign matching keyword args
+ autoinit(**kwargs)
+ # track if the owner of the neighbor house changes by registering
+ # Squid's callback. The callback will be weakly proxied, and
+ # automatically unregisters when this Squid instance is no longer
+ # referenced.
+ self.neighbor_house.on_owner_changed.addweak(self.someone_moved_in)
+
+ def someone_moved_in(self, event):
+ # since the on_owner_changed callset is global to Pineapple, Squid
+ # would be notified of any owner changes in Pineapples, so filter
+ # for the right one.
+ if not (event.instance is self.neighbor_house):
+ return
+ # check if the new tenant is one of our annoying coworkers
+ if event.value in self.annoying_coworkers:
+ # state opinion on the situation
+ print("Meh.")
+
+
+Trying this on the command line:
+
+ >>> house = Pineapple()
+ >>> bob = Sponge()
+ >>> squidward = Squid(neighbor_house = house)
+ >>> squidward.annoying_coworkers.add(bob)
+ >>> house.owner = bob
+ Meh.
+
+And that's the gist of it.
+
+Weak Callable Proxies
+---------------------
+
+TODO
+
+Observable Dicts, Lists and Sets
+--------------------------------
+
+TODO
+
+Enums
+-----
+
+TODO
+
+Singletons
+----------
+
+TODO
A => glue.py/glue.py +1539 -0
@@ 0,0 1,1539 @@
+# glue.py - agnostic model/view toolshed for Python 2.7, 3.x and PyPy
+# Copyright (c) 2013 Leonard Ritter <contact@leonard-ritter.com>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+"""
+glue.py is a single lightweight utility module expanding Python with
+powerful idioms for accessors, callsets, weak methods, enums, singletons and
+observable primitives that can be easily integrated with existing projects.
+"""
+
+from __future__ import (print_function, division, absolute_import)
+
+import collections
+import functools
+import gc
+import imp
+import inspect
+import sys
+import traceback
+import types
+import weakref
+import threading
+
+__all__ = [
+ 'Undefined', 'enum', 'FunctionProxy', 'MethodProxy', 'callproxy',
+ 'OrderedSet', 'StopCallableIteration', 'CallableSet', 'callset',
+ 'SetPropertyEvent', 'genproperty', 'defproperty', 'autoinit', 'safecall',
+ 'Dict', 'List', 'iterprops', 'Property', 'CallableQueue', 'callqueue',
+ 'classproperty', 'ClassProperty', 'expect_empty', 'singleton',
+ 'Singleton', 'History', 'deep_reload', 'Graph',
+ 'clamp', 'mix', 'Named', 'Managed', 'main'
+]
+
+################################################################################
+
+# True if running in Python 3.0+
+ispython3 = sys.version_info >= (3, 0)
+
+# to be used for defaults where None is ambiguous
+if ispython3:
+ Undefined = type('Undefined', (), dict(
+ __bool__ = lambda self: False
+ ))()
+else:
+ Undefined = type('Undefined', (), dict(
+ __nonzero__ = lambda self: False
+ ))()
+
+################################################################################
+
+def enum(*sequential, **named):
+ """creates a new enum type of sequential or named enums and enhances it
+ with a reverse lookup method.
+
+ enum can also be used as a class decorator.
+ """
+ if not named and len(sequential) == 1 and inspect.isclass(sequential[0]):
+ class_ = sequential[0]
+ keys = [k for k in class_.__dict__.keys()
+ if not k.startswith('_') and not callable(getattr(class_,k))]
+ enums = dict((k,getattr(class_,k)) for k in keys)
+ reverse = dict((value, key) for key, value in enums.iteritems())
+ class_.keys = set(keys)
+ class_.reverse_dict = reverse
+ class_.count = len(reverse)
+ return class_
+
+ enums = dict(zip(sequential, range(len(sequential))), **named)
+ reverse = dict((value, key) for key, value in enums.iteritems())
+ enums['reverse_dict'] = reverse
+ enums['count'] = len(reverse)
+ return type('Enum', (), enums)
+
+def expect_empty(args):
+ """Simple tool function to complain about excess arguments or keyword
+ arguments passed, after all required elements have been popped.
+
+ It is advisable to only make use of this check in baseclasses, where excess
+ keywords are not going to be processed by further baseclass constructors."""
+ if args:
+ if isinstance(args, dict):
+ msg = "Unexpected keyword(s): {}".format(args.keys())
+ else:
+ msg = "Unexpected argument(s): {}".format(args)
+ raise ValueError(msg)
+
+def _make_safecall(func, default = Undefined):
+ @functools.wraps(func)
+ def wrapper(*args, **kargs):
+ try:
+ return func(*args, **kargs)
+ except:
+ traceback.print_exc()
+ return default
+ return wrapper
+
+def safecall(*args, **kwargs):
+ """
+ A decorator that wraps function func with an exception catcher. If an
+ exception is raised, the traceback is printed and the function returns
+ a default value.
+
+ You can use the decorator with and without arguments. If the optional
+ keyword argument "default" is omitted, the function will return Undefined.
+ """
+ if args:
+ return _make_safecall(*args, **kwargs)
+ else:
+ return functools.partial(_make_safecall, **kwargs)
+
+def main(func):
+ """
+ A decorator that calls function func if it's defined in the main module.
+ This can be used to easily designate a function as the main function:
+
+ @main
+ def test():
+ print("main.")
+
+ or
+
+ def test():
+ print("main.")
+ main(test)
+
+ is equivalent to
+
+ if __name__ == '__main__':
+ print("main.")
+ """
+ if func.__module__ == '__main__':
+ func()
+
+################################################################################
+
+class FunctionProxy(object):
+ """a weak hashable and comparable function proxy."""
+
+ __slots__ = [
+ '_func',
+ '_hash',
+ '__weakref__',
+ '__call__'
+ ]
+
+ def __init__(self, func, callback = None):
+ self._hash = hash(func)
+ self._func = weakref.ref(func,
+ lambda r: callback(self) if callback else None)
+
+ def __repr__(self):
+ return 'FunctionProxy({!r})'.format(self._func())
+
+ def alive(self):
+ return True if self._func() else False
+
+ if ispython3:
+ def __bool__(self):
+ return self.alive()
+ else:
+ def __nonzero__(self):
+ return self.alive()
+
+ def __eq__(self, other):
+ return hash(self) == hash(other)
+
+ def __ne__(self, other):
+ return hash(self) != hash(other)
+
+ def get(self):
+ return self._func()
+
+ def __hash__(self):
+ return self._hash
+
+ def __call__(self, *args, **kwds):
+ func = self._func()
+ if not func:
+ return
+ return func(*args, **kwds)
+
+class MethodProxy(FunctionProxy):
+ """a weak hashable and comparable method proxy."""
+
+ __slots__ = [
+ '_self',
+ ]
+
+ def __init__(self, func, callback = None):
+ FunctionProxy.__init__(self, func.__func__)
+ self._hash = hash(func)
+ self._self = weakref.ref(func.__self__,
+ lambda r: callback(self) if callback else None)
+
+ def __repr__(self):
+ return 'MethodProxy({!r}.{!r})'.format(self._self(),self._func())
+
+ def alive(self):
+ return True if self._self() else False
+
+ def __call__(self, *args, **kwds):
+ self_ = self._self()
+ if not self_:
+ return
+ FunctionProxy.__call__(self, self_, *args, **kwds)
+
+def callproxy(callable_, callback = None):
+ """turns any callable into a weak proxy. If the object associated with
+ the callable disappears, calling the proxy turns into a non-op.
+
+ As with weakref.ref, callback will be invoked when the callable disappears.
+ """
+ assert callable(callable_), 'argument must be callable'
+ if inspect.ismethod(callable_):
+ return MethodProxy(callable_, callback)
+ else:
+ return FunctionProxy(callable_, callback)
+
+################################################################################
+
+def format_bytesize(num):
+ for x in ['bytes','KB','MB','GB']:
+ if num < 1024.0 and num > -1024.0:
+ return "%3.1f%s" % (num, x)
+ num /= 1024.0
+ return "%3.1f%s" % (num, 'TB')
+
+################################################################################
+
+class OrderedSet(collections.OrderedDict, collections.MutableSet):
+ """An ordered set where all write operations can be hooked."""
+
+ hook = None
+
+ class Event(object):
+ def __init__(self, set_, method, args, kwargs):
+ self.set = set_
+ self.method = method
+ self.args = args
+ self.kwargs = kwargs
+
+ def __repr__(self):
+ return 'Event({!r},{!r},{!r},{!r})'.format(
+ self.set, self.method, self.args, self.kwargs)
+
+ def _wrap(method): #@NoSelf
+ def wrapper(self, *args, **kwargs):
+ if self.hook:
+ self.hook(OrderedSet.Event(self, method, args, kwargs))
+ return method(self, *args, **kwargs)
+ return wrapper
+
+ def __init__(self, *args, **kwargs):
+ super(OrderedSet, self).__init__(self)
+ self.update(*args, **kwargs)
+
+ @_wrap
+ def update(self, *args, **kwargs):
+ if kwargs:
+ raise TypeError("update() takes no keyword arguments")
+ for s in args:
+ for e in s:
+ self.add(e)
+
+ @_wrap
+ def add(self, elem):
+ self[elem] = None
+
+ @_wrap
+ def discard(self, elem):
+ self.pop(elem, None)
+
+ def __hash__(self):
+ return object.__hash__(self)
+
+ def __le__(self, other):
+ return all(e in other for e in self)
+
+ def __lt__(self, other):
+ return self <= other and self != other
+
+ def __ge__(self, other):
+ return all(e in self for e in other)
+
+ def __gt__(self, other):
+ return self >= other and self != other
+
+ def __repr__(self):
+ return 'OrderedSet([%s])' % (', '.join(map(repr, self.keys())))
+
+ def __str__(self):
+ return '{%s}' % (', '.join(map(repr, self.keys())))
+
+ def discard_weakrefs(self):
+ for e in self.keys():
+ if isinstance(e, weakref.ref) and (e() is None):
+ self.discard(e)
+ if isinstance(e, FunctionProxy) and not e.alive():
+ self.discard(e)
+
+ __ior__ = _wrap(collections.MutableSet.__ior__)
+ __isub__ = _wrap(collections.MutableSet.__isub__)
+ __iand__ = _wrap(collections.MutableSet.__iand__)
+ __ixor__ = _wrap(collections.MutableSet.__ixor__)
+
+ difference = property(lambda self: self.__sub__)
+ difference_update = property(lambda self: self.__isub__)
+ intersection = property(lambda self: self.__and__)
+ intersection_update = property(lambda self: self.__iand__)
+ issubset = property(lambda self: self.__le__)
+ issuperset = property(lambda self: self.__ge__)
+ symmetric_difference = property(lambda self: self.__xor__)
+ symmetric_difference_update = property(lambda self: self.__ixor__)
+ union = property(lambda self: self.__or__)
+
+################################################################################
+
+class StopCallableIteration(Exception):
+ """Aborts a callset iteration if raised while being called by a callset."""
+ pass
+
+class CallableSet(OrderedSet):
+ """A set of callables. Implements what is known as 'observable' pattern.
+ Enables decoupling of components by allowing to multiplex a call into many
+ calls. Can also be understood as a function pointer that points to many
+ functions, or a virtual method that is configured from the outside.
+
+ When adding an observer's method to the set, use addweak() to wrap the
+ method in a callproxy to prevent the callable set from keeping your object
+ alive. When the object dies, the proxy will be removed from the set.
+ """
+
+ def __repr__(self):
+ return 'CallableSet([%s])' % (', '.join(map(repr, self.keys())))
+
+ def addweak(self, callable_):
+ if callable_ in self:
+ return
+ self.add(callproxy(callable_, self.remove))
+
+ def __call__(self, *args, **kargs):
+ try:
+ for callable_ in self:
+ callable_(*args,**kargs)
+ except StopCallableIteration:
+ pass
+
+ def call_list(self, *args, **kargs):
+ try:
+ return [callable_(*args,**kargs) for callable_ in self]
+ except StopCallableIteration:
+ pass
+
+ def call_parallel(self, *args, **kargs):
+ threads = []
+ for callable_ in self:
+ threads.append(threading.Thread(
+ target = callable_, args = args, kwargs = kargs))
+ for thread in threads:
+ thread.start()
+ for thread in threads:
+ thread.join()
+
+def callset(*args, **kargs):
+ """A shortcut for instantiating a CallableSet"""
+ return CallableSet(*args, **kargs)
+
+################################################################################
+
+class CallableSetBag(object):
+ """A collection (or so-called bag) of callable sets that can be accessed by
+ key or by attribute. The bag also contains an 'all' callset that allows to
+ subscribe to all callsets at once.
+
+ Use callable set bags where you need to expose a range of observables
+ in your class, e.g. events in models that views can subscribe to.
+ """
+
+ def __init__(self, *names):
+ self.__names = tuple(names)
+ all_events = callset()
+ events = dict(
+ all = all_events
+ )
+ for name in self.__names:
+ observers = callset()
+ observers.add(all_events)
+ events[name] = observers
+ self.__dict__.update(events)
+
+ def __iter__(self):
+ return ((name,self.__dict__[name]) for name in self.__names)
+
+ def __repr__(self):
+ return 'CallableSetBag{!r}'.format(self.__names)
+
+ def __getitem__(self, key):
+ return self.__dict__[key]
+
+def callsetbag(*names):
+ """A shortcut for instantiating a CallableSetBag"""
+ return CallableSetBag(*names)
+
+################################################################################
+
+def lazydecls(cls):
+ """A class decorator that searches a class for attributes carrying a
+ special '__lazydecl__' factory and uses it to instantiate the
+ attribute. This way, descriptors exposing a __lazydecl__ factory can
+ learn the class attribute they have been assigned to.
+
+ The lazydecls() functionality is used to enable properties generated
+ with defproperty() to function."""
+
+ items = dict(cls.__dict__).items()
+ for key, value in items:
+ if hasattr(value, '__lazydecl__'):
+ setattr(cls, key, value.__lazydecl__(cls, key))
+ return cls
+
+class LazyDecl(object):
+ """Descriptor implementing the __lazydecl__ operator."""
+
+ def __get__(self, *args):
+ raise ValueError('class needs @lazydecls decorator')
+
+ def __set__(self, *args):
+ raise ValueError('class needs @lazydecls decorator')
+
+ def __delete__(self, *args):
+ raise ValueError('class needs @lazydecls decorator')
+
+ def __lazydecl__(self, cls, name):
+ # implement this function
+ pass
+
+################################################################################
+
+class classproperty(property):
+ """A method decorator that allows to turn a classmethod into a class
+ property."""
+
+ def __get__(self, cls, owner):
+ return classmethod(self.fget).__get__(None, owner)()
+
+################################################################################
+
+class SetPropertyEvent(object):
+ """An event sent by properties generated with defproperty() on change."""
+
+ def __init__(self, instance, name, value):
+ self.instance = instance
+ self.name = name
+ self.value = value
+
+ def __repr__(self):
+ return 'Event({!r},{!r},{!r})'.format(
+ self.instance, self.name, self.value)
+
+class Property(property):
+ """The descriptor of properties generated with defproperty()"""
+
+ def __repr__(self):
+ return 'Property({!r})'.format(self.name)
+
+ def init(self, instance):
+ default = getattr(self, 'default', Undefined)
+ if default is Undefined:
+ return
+ if not callable(default):
+ return
+ setattr(instance, self.private, default())
+
+class ClassProperty(classproperty):
+ """An alternative descriptor for defproperty() that allows to generate
+ class properties."""
+
+ def __repr__(self):
+ return 'ClassProperty({!r})'.format(self.name)
+
+################################################################################
+
+if ispython3:
+ def _exec(s, globs):
+ exec(s, globs)
+else:
+ exec("""
+def _exec(s, globs):
+ exec s in globs
+""")
+
+################################################################################
+
+_next_order = 0
+def genproperty(name, private, **options):
+ """A versatile and powerful property generator that generates code for
+ accessors typically needed in Python classes.
+
+ This is the explicit function, which is rarely required. For more
+ documentation, see defproperty()."""
+
+ global _next_order
+ privname = private
+ readonly = options.pop('readonly', False)
+ default = options.pop('default', Undefined)
+ propcls = options.pop('class_', Property)
+ weak = options.pop('weak', False)
+ doc = options.pop('doc', None)
+ order = _next_order
+ _next_order += 1
+
+ attribs = dict(
+ name = name,
+ private = privname,
+ readonly = readonly,
+ order = order
+ )
+
+ if not (default is Undefined):
+ attribs.update(default=default)
+
+ s = 'def fget(self):\n'
+ if (default is Undefined) or callable(default):
+ s += ' value = getattr(self, privname)\n'
+ else:
+ s += ' value = getattr(self, privname, default)\n'
+ if weak:
+ attribs.update(weak=weak)
+ s += ' value = (value or None) and value()\n'
+ s += ' return value\n'
+
+ globs = dict(globals())
+ globs.update(locals())
+ _exec(s, globs)
+ fget = globs['fget']
+
+ ref = weakref.ref
+
+ if not readonly:
+ rewrite = options.pop('rewrite', False)
+ types = options.pop('types', None)
+ validate = options.pop('validate', None)
+ sanitize = options.pop('sanitize', None)
+ hook = options.pop('hook',None)
+ if hook is None:
+ hook = options.pop('changing', None)
+
+ s = 'def fset(self, value):\n'
+
+ if not (sanitize is None):
+ attribs.update(sanitize = sanitize)
+ s += ' value = sanitize(value)\n'
+
+ if not rewrite:
+ attribs.update(rewrite = rewrite)
+ s += ' if value == getattr(self, name, default):\n'
+ s += ' return\n'
+
+ if not (types is None):
+ attribs.update(types = types)
+ s += ' if not isinstance(value, types):\n'
+ s += ' raise TypeError("type(s) {} expected, got {}".format(types, type(value)))\n'
+
+ if not (validate is None):
+ attribs.update(validate = validate)
+ s += ' if not validate(value):\n'
+ s += ' raise ValueError("validation failed")\n'
+
+ if not (hook is None):
+ attribs.update(hook = hook)
+ s += ' hook(SetPropertyEvent(self, name, value))\n'
+
+ if weak:
+ s += ' value = None if value is None else ref(value)\n'
+
+ s += ' setattr(self, privname, value)\n'
+
+
+ globs = dict(globals())
+ globs.update(locals())
+ _exec(s, globs)
+ fset = globs['fset']
+
+ def fdel(self):
+ delattr(self, privname)
+ else:
+ fset = None
+ fdel = None
+
+ if options:
+ raise ValueError('Unexpected keyword(s): {}'.format(options.keys()))
+
+ prop = propcls(fget=fget, fset=fset, fdel=fdel, doc=doc)
+ prop.__doc__ = doc
+ for name, value in attribs.items():
+ setattr(prop, name, value)
+
+ return prop
+
+def defproperty(**options):
+ """Defines a versatile and powerful property and generates code for
+ accessors as needed. Classes using defproperty() require the @lazydecls
+ decorator and should call autoinit() during initialization.
+
+ While properties are stored in classes independent of order of declaration,
+ properties generated with defproperty() expose an integer 'order' attribute
+ which can be used as key by inspectors to sort enumerated properties.
+
+ In addition, following creation options are available, with the default
+ setting in brackets:
+
+ * readonly (False): if True, the property is read-only / protected and
+ can not be set. This setting can be read from the properties 'readonly'
+ attribute.
+
+ * default (Undefined): the default value for the property. If the value
+ is mutable or should be instantiated per class, a factory function can
+ be specified instead which will be used to initialize the instance
+ value. If you use a default factory, you must call autoinit() in
+ the classes __init__ method. If set, the setting can be read from the
+ properties 'default' attribute.
+
+ * private (__<attribute name>): The name of the attribute in which
+ the property instance's value will be stored. This defaults to a private
+ attribute which can only be accessed from within the declared class.
+ The private name can be read from the properties 'private' attribute.
+
+ * hook (None): [only when readonly = False] A callable (which includes
+ callsets) that will be called right before the property is changed, with
+ an event of type SetPropertyEvent as only argument. Views can use hooks to
+ be notified when a models attribute changes, and refresh accordingly.
+ If set, this setting can be read from the properties 'hook' attribute.
+ 'changing' is an alias for this option.
+
+ * sanitize (None): [only when readonly = False] A function of the format
+ sanitize(value) -> value. If specified, will be called before rewrite,
+ type and validate check. You can use types like int or float here. A
+ sanitizer does not raise errors, but attempts to turn a value into as
+ useful as possible before it is assigned, and equips a property with
+ "overload" capability. Typical use cases are type conversions and
+ clamping numerical values. If set, this setting can be read from the
+ properties 'sanitize' attribute.
+
+ * types (None): [only when readonly = False] A single type or a tuple of
+ types that a value should be checked against. If the value does not match
+ any type, the setter will raise a TypeError. If set, this setting can be
+ read from the properties 'types' attribute.
+
+ * validate (None): [only when readonly = False] A function of the format
+ validate(value) -> bool. If specified, will be called after the optional
+ type check, but before it is ultimately assigned. If validate returns
+ False, the setter will raise a ValueError. If set, this setting can be read
+ from the properties 'validate' attribute.
+
+ * rewrite (False): [only when readonly = False] By default, the generated
+ setter will check if the new value equals the old one. If rewrite is True,
+ this check will be skipped. If set, this setting can be read from the
+ properties 'rewrite' attribute.
+
+ * doc (None): The docstring for this property.
+
+ * weak (False): if True, the stored value is transparently weakly
+ referenced. Useful for pointers that the class instance should not own.
+ Will not work with callables. If the referenced object dies, accessing
+ the attribute will return None. If set, the setting can be read from the
+ properties 'weak' attribute.
+
+ * class_ (Property): the class used to instantiate the property. Rarely
+ required. Use ClassProperty if you want a class attribute.
+ """
+
+ class LazyPropertyDecl(LazyDecl):
+ def __lazydecl__(self, class_, name): #@NoSelf
+ opts = dict(options)
+ private = opts.pop('private', Undefined)
+ if private is Undefined:
+ private = '__' + name
+
+ if private.startswith('__'):
+ private = '_' + class_.__name__ + private
+
+ return genproperty(name, private, **opts)
+
+ return LazyPropertyDecl()
+
+def autoinit(cls = None, self = None, **kwargs):
+ """To be called in __init__ constructors of classes that make use of
+ defproperty(). Initializes (only) the properties within this class.
+
+ The function may be called explicitly or implicitly, similar to super():
+
+ autoinit(MyClass, self)
+
+ If self is omitted, autoinit() will try to infer the 'self' attribute from
+ the callers local scope. If class is omitted, autoinit() infers the
+ function containing the code, assumes that it is a constructor and checks
+ which of self's classes contains it.
+
+ For generic constructors that accept variable keyword arguments, the
+ dict can be passed on to autoinit, which pops all values from it for which
+ properties declared with defproperty() exist:
+
+ @lazydecls
+ class O(object):
+ name = defproperty()
+
+ def __init__(self, **kwargs):
+ expect_empty(autoinit(**kwargs))
+
+ # will assign name, but complain about spam
+ o = O(name = 'test', spam = True)
+
+ (Note: While it is convenient to use autoinit() without arguments, the
+ implementation may not work in all implementations of Python, as it
+ depends on interpreter internals. It has been tested with CPython 2.7,
+ 3.2 and PyPy 1.9.)
+ """
+
+ if (self and cls) is None:
+ callframe = inspect.currentframe().f_back
+ if self is None:
+ self = callframe.f_locals['self']
+ if cls is None:
+ code = callframe.f_code
+ for subcls in inspect.getmro(self.__class__):
+ method = subcls.__dict__.get('__init__', None)
+ if not method:
+ continue
+ if method.__code__ == code:
+ cls = subcls
+ break
+ assert not (cls is None), 'could not find class for constructor'
+
+ assert isinstance(self, cls)
+ for name, prop in cls.__dict__.items():
+ if not isinstance(prop, Property):
+ continue
+ value = kwargs.pop(name, Undefined)
+ if value is Undefined:
+ prop.init(self)
+ else:
+ setattr(self, name, value)
+
+ return kwargs
+
+def iterprops(obj):
+ """Iterate all properties defined by defproperty() in an object."""
+ for name, prop in inspect.getmembers(obj.__class__): #@UnusedVariable
+ if not isinstance(prop, Property):
+ continue
+ yield prop
+
+################################################################################
+
+class Dict(dict):
+ """An observable drop-in replacement for Python dicts"""
+
+ class Event(object):
+ def __init__(self, dict_, method, args, kwargs):
+ self.dict = dict_
+ self.method = method
+ self.args = args
+ self.kwargs = kwargs
+
+ def __repr__(self):
+ return 'Event({!r},{!r},{!r},{!r})'.format(
+ self.dict, self.method, self.args, self.kwargs)
+
+ hook = None
+
+ def __init__(self, *args, **kwargs):
+ dict.__init__(self, *args, **kwargs)
+
+ def _wrap(method): #@NoSelf
+ def wrapper(self, *args, **kwargs):
+ if self.hook:
+ self.hook(Dict.Event(self, method, args, kwargs))
+ return method(self, *args, **kwargs)
+ return wrapper
+ __delitem__ = _wrap(dict.__delitem__)
+ __setitem__ = _wrap(dict.__setitem__)
+ clear = _wrap(dict.clear)
+ pop = _wrap(dict.pop)
+ popitem = _wrap(dict.popitem)
+ setdefault = _wrap(dict.setdefault)
+ update = _wrap(dict.update)
+ del _wrap
+
+################################################################################
+
+class List(list):
+ """An observable drop-in replacement for Python lists"""
+
+ class Event(object):
+ def __init__(self, list_, method, args, kwargs):
+ self.list = list_
+ self.method = method
+ self.args = args
+ self.kwargs = kwargs
+
+ def __repr__(self):
+ return 'Event({!r},{!r},{!r},{!r})'.format(
+ self.list, self.method, self.args, self.kwargs)
+
+ hook = None
+
+ def __init__(self, *args, **kwargs):
+ list.__init__(self, *args, **kwargs)
+
+ def _wrap(method): #@NoSelf
+ def wrapper(self, *args, **kwargs):
+ if self.hook:
+ self.hook(List.Event(self, method, args, kwargs))
+ return method(self, *args, **kwargs)
+ return wrapper
+ __delitem__ = _wrap(list.__delitem__)
+ __setitem__ = _wrap(list.__setitem__)
+ if hasattr(list, '__delslice__'):
+ __delslice__ = _wrap(list.__delslice__)
+ if hasattr(list, '__setslice__'):
+ __setslice__ = _wrap(list.__setslice__)
+ pop = _wrap(list.pop)
+ append = _wrap(list.append)
+ insert = _wrap(list.insert)
+ __iadd__ = _wrap(list.__iadd__)
+ __imul__ = _wrap(list.__imul__)
+ remove = _wrap(list.remove)
+ sort = _wrap(list.sort)
+ extend = _wrap(list.extend)
+ reverse = _wrap(list.reverse)
+ del _wrap
+
+################################################################################
+
+class CallableQueue(object):
+ """a simple message queue that allows for callables to be propagated later"""
+
+ def __init__(self):
+ self.clear()
+
+ def clear(self):
+ self._todo = []
+
+ def iterate(self):
+ while self._todo:
+ func,args,kwargs = self._todo.pop(0)
+ func(*args,**kwargs)
+
+ def post(self, func, *args, **kwargs):
+ self._todo.append((func, args, kwargs))
+
+def callqueue():
+ return CallableQueue()
+
+################################################################################
+
+def singleton(factory, **kwargs):
+ """
+ Can be used as decorator on a factory function. Should not be used as
+ class decorator, as you hide the class type reference.
+
+ turns callable factory (usually the class) into a weak singleton
+ provider. On first call, the generated function will call the factory
+ to produce a new singleton instance. Unless all references to the instance
+ are cleared, this function will always return the same object."""
+
+ __instance = [None]
+ weak = kwargs.pop('weak', True)
+ if weak:
+ callback = kwargs.pop('callback', None)
+ def on_delete(ref):
+ __instance[0] = None
+ if callback:
+ callback(ref)
+ def instance():
+ if not __instance[0]:
+ instance = factory()
+ __instance[0] = weakref.ref(instance, on_delete)
+ return __instance[0]()
+ else:
+ def instance():
+ if not __instance[0]:
+ __instance[0] = factory()
+ return __instance[0]
+ expect_empty(kwargs)
+ return instance
+
+def get_last_descendant(cls):
+ """return the last declared descendant"""
+ while cls.__subclasses__():
+ cls = cls.__subclasses__()[-1]
+ return cls
+
+class Singleton(type):
+ """
+ Singleton implemented as a metaclass to support inheritance. Use it like
+ this:
+
+ # Python 2.7
+ class MyClass(object):
+ __metaclass__ = Singleton
+
+ # Python 3.x
+ class MyClass(metaclass=Singleton):
+ pass
+
+ The singleton will make sure calls to baseclasses will also resolve to
+ inheriting classes. When the class has already been inherited and the
+ base class is instantiated, it will instantiate the last declared
+ descending class instead. Therefore, avoid inheritance trees, as you
+ will most likely end up with unintended results.
+
+ Also, make sure all inheriting classes are declared before you instantiate
+ the singleton for the first time, or it will be re-instantiated.
+ """
+
+ _instances = {}
+
+ def __call__(cls, *args, **kwargs): #@NoSelf
+ # always take the last declared descendant
+ cls = get_last_descendant(cls)
+ result = cls._instances.get(cls, None)
+ if result:
+ return result
+ result = super(Singleton, cls).__call__(
+ *args, **kwargs)
+ cls._instances[cls] = result
+ return result
+
+ def del_singleton(cls): #@NoSelf
+ cls = get_last_descendant(cls)
+ del cls._instances[cls]
+
+################################################################################
+
+@lazydecls
+class History(object):
+ """Converts event driven notifications to polling, e.g. for interfacing
+ with web clients which can not keep a standing connection or deferral
+ for delayed processing. If you wish to subscribe to a lot of callsets owned
+ by a single model class, consider grouping all callsets with a callsetbag,
+ then just pass the bags' 'all' callset to the History constructor.
+ """
+
+ entries = defproperty(default = list)
+
+ def __init__(self, *callablesets):
+ autoinit()
+
+ for observers in callablesets:
+ observers.addweak(self.log_event)
+
+ def pop_entries(self):
+ entries = list(self.entries)
+ self.entries = []
+ return entries
+
+ def log_event(self, event):
+ self.entries.append(event)
+
+################################################################################
+
+def swap_classes(classmap):
+ """
+ Swaps classes using a old class -> new class dictionary passed as classmap.
+ The function uses the gc module to enumerate all currently tracked
+ instances of the old class, and exchanges their class attribute to the
+ new class. A list of objects that have been updated will be returned.
+
+ This function is part of the deep reload functionality.
+ """
+ classkeys = tuple(classmap.keys())
+
+ descendants = []
+ updated = []
+
+ for obj in gc.get_objects():
+ if inspect.isclass(obj):
+ if issubclass(obj, classkeys):
+ #if inspect.getmodule(obj) is module_:
+ # continue
+ for cls in classkeys:
+ if cls in obj.__bases__:
+ descendants.append((obj, cls))
+ elif isinstance(obj, classkeys):
+ cls = obj.__class__
+ if cls in classkeys:
+ try:
+ obj.__class__ = classmap[cls]
+ updated.append(obj)
+ except TypeError:
+ traceback.print_exc()
+
+ if descendants:
+ # TODO: update inheriting classes
+ pass
+
+ return updated
+
+def iter_module_content(module_):
+ """
+ iterate the contents of a module for the purpose of tracking nested classes.
+
+ This function is part of the deep reload functionality.
+ """
+ assert inspect.getmodule(module_) is module_
+ predicate = lambda x: inspect.getmodule(x) is module_
+ stack = [((),module_)]
+ walked = set()
+ while stack:
+ basename,namespace = stack.pop()
+ objid = id(namespace)
+ if objid in walked:
+ continue
+ walked.add(objid)
+ for name, obj in inspect.getmembers(namespace, predicate):
+ if inspect.isclass(obj) or inspect.isfunction(obj):
+ fullname = basename + (name,)
+ yield fullname, obj
+
+def resolve_namespace(module_, path):
+ """
+ Resolve a list of key names from module until the object is retrieved.
+
+ This function is part of the deep reload functionality.
+ """
+ obj = module_
+ for name in path:
+ obj = getattr(obj, name)
+ return obj
+
+def deep_reload(module_):
+ """
+ Reloads module and updates all objects using classes from this module as
+ far as possible to match updated methods. It returns a list of objects
+ that have been updated.
+ """
+ content = list(iter_module_content(module_))
+ try:
+ imp.reload(module_)
+ except:
+ traceback.print_exc()
+ return
+
+ classes = {}
+ for path, obj in content:
+ if inspect.isclass(obj):
+ newobj = resolve_namespace(module_, path)
+ if newobj is None:
+ continue
+ if obj is newobj:
+ print("object is same after reload?",obj,newobj)
+ continue
+ classes[obj] = newobj
+
+ return swap_classes(classes)
+
+################################################################################
+
+class Named(object):
+ """Many systems require objects to be distinguishable during debugging.
+ Countless objects therefore carry some kind of a name or id. This base class
+ facilitates this service and also supports assigning names by @lazydecl.
+
+ If a class is decorated with lazydecl and has unnamed Named objects declared
+ at class level, the objects will be named after the attribute they have
+ been assigned to."""
+
+ attributes = [
+ '_name',
+ ]
+
+ def __init__(self):
+ self._name = ""
+
+ def __repr__(self):
+ return '{0}({1})'.format(self.__class__.__name__, self._name or '?')
+
+ # auto-name from property if class is annotated with @lazydecls
+ def __lazydecl__(self, class_, name):
+ if self._name: return
+ self._name = name
+ return self
+
+ @property
+ def name(self):
+ return self._name
+ @name.setter
+ def name(self, value):
+ self._name = value
+
+################################################################################
+
+class Managed(object):
+ """This class provides a simple wrapper for objects wrapping a C pointer
+ that needs to be freed when the object is collected. It also provides
+ a resolve function that allows to retrieve an existing wrapper for any
+ pointer.
+
+ To use this class, inherit from it and assign _destroy_ptr(ptr) at class
+ level. _destroy_ptr should be a C function. You also need to assign _ffi
+ to the cffi instance you would like to be used to tag the weak reference.
+ """
+
+ attributes = [
+ '_ptr',
+ ]
+
+ # default implementation does nothing
+ _destroy_ptr = lambda ptr: None
+
+ _ffi = None
+ _users = weakref.WeakValueDictionary()
+
+ def __init__(self, ptr):
+ assert ptr
+ self._ptr = self._ffi.gc(ptr, self.__class__._destroy_ptr)
+ self._users[self._ptr] = self
+
+ def destroy(self):
+ self._ptr = None
+
+ @classmethod
+ def resolve(cls, ptr):
+ if not ptr: return None
+ return cls._users[ptr]
+
+ @classmethod
+ def wrap(cls, ptr):
+ obj = cls._users.get(ptr, None)
+ if obj: return obj
+ obj = cls.__new__(cls)
+ obj._ptr = ptr
+ cls._users[obj._ptr] = obj
+ return obj
+
+################################################################################
+
+class Graph(object):
+ def __init__(self):
+ self.clear()
+
+ def clear(self):
+ self._sorted = False
+ self._order = []
+ self.edges = {}
+ self.priorities = {}
+
+ def invalidate(self):
+ self._sorted = False
+
+ def priority(self, node, prio):
+ """node with higher priority is first in order"""
+ self.priorities[node] = prio
+ self.invalidate()
+
+ def depends(self, target, *sources):
+ """target depends on sources"""
+ sourceset = self.edges.setdefault(target, OrderedSet())
+ for source in sources:
+ sourceset.add(source)
+ self.invalidate()
+
+ def clear_sources(self, target):
+ self.edges.pop(target, None)
+ self.invalidate()
+
+ def sources(self, target):
+ return list(self.edges.get(target, []))
+
+ def targets(self, source):
+ result = OrderedSet()
+ for target,sources in self.edges.items():
+ if source in sources:
+ result.add(target)
+ return result
+
+ def calculate_order(self):
+ """traverses the graph and returns an ordered list of nodes"""
+ # find root nodes
+ roots = OrderedSet(self.edges.keys())
+ for target, sources in self.edges.iteritems():
+ roots.difference_update(sources)
+ if not roots:
+ return [] # cyclic dependency
+
+ visited = OrderedSet()
+ order = OrderedSet()
+ stack = list(roots)
+ stack.sort(key=lambda k: self.priorities.get(k, 0))
+
+ while stack:
+ node = stack[-1]
+ if node in visited:
+ order.add(node)
+ stack.pop()
+ else:
+ visited.add(node)
+ sources = list(self.edges.get(node, []))
+ sources.sort(key=lambda k: self.priorities.get(k, 0))
+ for source in sources:
+ if source in visited:
+ continue
+ stack.append(source)
+ return list(order)
+
+ def ensure_sorted(self):
+ if self._sorted:
+ return
+ self._order = self.calculate_order()
+ self._sorted = True
+
+ @property
+ def order(self):
+ self.ensure_sorted()
+ return self._order
+
+################################################################################
+
+def clamp(x, mn, mx):
+ if x != x: # nan
+ return mn
+ return min(max(x, mn), mx)
+
+def mix(a, b, x):
+ return a*(1-x) + b*x
+
+################################################################################
+
+def test_enums():
+ # enum as decorator
+ @enum
+ class MyEnum:
+ blue = 3
+ yellow = 5
+ green = 10
+
+ @classmethod
+ def my_classfunc(cls):
+ print("i am a classfunc")
+
+ def my_other_func(self):
+ pass
+
+ assert 'blue' in MyEnum.keys
+ assert MyEnum.reverse_dict[5] == 'yellow'
+ assert MyEnum.count == 3
+
+def test_callable_proxy_set():
+ class C(object):
+ A = []
+
+ def func1(self):
+ self.A.append(1)
+
+ def func2(self):
+ self.A.append(2)
+
+ cset = CallableSet()
+ def scoped():
+ c = C()
+ def func3():
+ C.A.append(3)
+
+ assert callproxy(c.func1) == callproxy(c.func1)
+ assert callproxy(func3) == callproxy(func3)
+ assert callproxy(func3) == func3
+ assert callproxy(c.func1) == c.func1
+ assert callproxy(c.func1) != c.func2
+ assert callproxy(c.func1) != callproxy(c.func2)
+ assert callproxy(c.func1) != callproxy(func3)
+ assert callproxy(c.func2) != callproxy(func3)
+
+ assert hash(callproxy(c.func1)) != hash(callproxy(c.func2))
+ assert hash(callproxy(c.func1)) != hash(callproxy(func3))
+ assert hash(callproxy(c.func2)) != hash(callproxy(func3))
+
+ cset.addweak(c.func1)
+ cset.addweak(c.func2)
+ cset.addweak(func3)
+ # should be ignored
+ cset.addweak(c.func1)
+ # should be ignored
+ cset.update([c.func2, c.func1])
+ assert c.func1 in cset
+ assert c.func2 in cset
+ assert func3 in cset
+ assert len(cset) == 3, len(cset)
+ cset()
+ assert c.A == [1,2,3], c.A
+ cset()
+ assert C.A == [1,2,3,1,2,3], C.A
+
+ proxy = callproxy(func3)
+ assert proxy.alive()
+ assert proxy
+
+ proxy2 = callproxy(c.func1)
+ assert proxy2.alive()
+ assert proxy2
+
+ return proxy, proxy2
+ proxy, proxy2 = scoped()
+ gc.collect() # pypy fix
+ assert not proxy.alive()
+ assert not proxy2.alive()
+ assert not proxy
+ assert not proxy2
+ cset()
+ assert C.A == [1,2,3,1,2,3], C.A
+ assert len(cset) == 0, len(cset)
+
+def test_threaded_callset():
+ import time
+ cset = callset()
+
+ things = []
+ cset.add(lambda k: things.append(k))
+ cset.add(lambda k: things.append(k+1))
+ cset.add(lambda k: things.append(k+2))
+ cset.add(time.sleep)
+ t = time.time()
+ cset.call_parallel(2)
+ assert time.time() - t >= 2
+ assert set(things) == set([2,3,4])
+
+def test_property():
+ # base class
+ @lazydecls
+ class U(object):
+ ignore = defproperty()
+
+ def __init__(self, **kwargs):
+ expect_empty(autoinit(**kwargs))
+
+ # base class
+ @lazydecls
+ class B(U):
+ ignore2 = defproperty()
+
+ def __init__(self, **kwargs):
+ U.__init__(self, **autoinit(**kwargs))
+
+ # demo class
+ @lazydecls
+ class C(B):
+ # an observable shared by all properties in this class
+ event_changing = callset()
+
+ # expand C by a property named "test" and autogenerate its accessors.
+ # By default, the internal name of the attribute will be "__test",
+ # but you can change this with the 'private' keyword.
+ # All attributes passed to the property can be read out from C.test
+ # later on by Views of this class to e.g. generate a GUI.
+ test = defproperty(
+ # optional: if set, will check if values assigned to this property
+ # are instances of these types. Can be a single class or a tuple
+ # of classes.
+ types = str,
+ # optional: an observer to be called when a new value is assigned
+ # to this property.
+ changing = event_changing,
+ # optional: a santizing function doing its best to translate the
+ # value to a valid form.
+ sanitize = lambda v: v.upper(),
+ # optional: a validation function returning True if the value meets
+ # constraints.
+ validate = lambda v: len(v) < 10,
+ # optional: the value this property will return by default.
+ default = lambda: 'hi')
+
+ def __init__(self, **kwargs):
+ B.__init__(self, **autoinit(**kwargs))
+ assert self.__test == 'hi'
+
+ # create an instance of C for playing around
+ c = C(ignore = True, ignore2 = True)
+ assert c.ignore
+ assert c.ignore2
+
+ assert sorted([p.name for p in iterprops(c)]) == ['ignore','ignore2','test']
+
+ # an array that will hold all values assigned to monitored properties of c
+ A = []
+
+ # a handler function that will be called when properties of C change
+ def func(event):
+ A.append(event.value)
+
+ # register our handler function with C
+ C.event_changing.add(func)
+
+ # default value should be as specified
+ assert c.test == 'hi'
+
+ # assign a new value that will be sanitized to uppercases
+ c.test = "hello"
+
+ # c.test should now hold our sanitized value
+ assert c.test == "HELLO"
+
+ # A should contain our santized value as well
+ assert A == ['HELLO']
+
+def test_dict():
+ k = {1:2,3:4}
+
+ def handler(event):
+ event.method(k, *event.args, **event.kwargs)
+
+ d = Dict(k)
+ k[4] = 5
+ d.hook = CallableSet()
+ d.hook.add(handler)
+ del d[1]
+ d["test"] = "test"
+ assert not k is d
+ assert k == {'test':'test', 3:4, 4:5}
+
+def test_list():
+ k = [10]
+
+ def handler(event):
+ event.method(k, *event.args, **event.kwargs)
+
+ l = List(k)
+ l.hook = callset()
+ l.hook.add(handler)
+ del l[0]
+ l += [1,2,3]
+ l.append(5)
+ assert k == l, "{} != {}".format(k,l)
+
+def test_classproperty():
+ class classprop(object):
+ _instance = 10
+
+ @classproperty
+ def instance(cls): #@NoSelf
+ return cls._instance
+
+ assert classprop.instance is 10
+ classprop._instance = 15
+ assert classprop.instance is 15
+
+def test_singleton():
+ class A(object):
+ pass
+
+ @singleton
+ def make_a():
+ return A()
+
+ a = make_a()
+ b = make_a()
+ c = make_a()
+ s = repr(a)
+ t = repr(b)
+ assert s == t and b is c
+ assert s == repr(make_a())
+
+ del a
+ del b
+ del c
+ gc.collect()
+
+ assert s != repr(make_a())
+
+ class B(object):
+ pass
+ class C(B):
+ __metaclass__ = Singleton
+ class G(C):
+ pass
+ class D(C): # will override G
+ pass
+ class E(D):
+ pass
+ class F(E):
+ pass
+
+ z = B()
+ a = C()
+ b = F()
+ c = E()
+ s = repr(a)
+ t = repr(b)
+ assert s == t and b is c
+ assert s == repr(C())
+ assert s != repr(B())
+ assert isinstance(a, F)
+ assert isinstance(z, B)
+
+def test_safecall():
+ @safecall
+ def noproblem(x,y):
+ return x/y
+ @safecall(default = 0)
+ def noproblem2(x,y):
+ return x/y
+ @safecall()
+ def noproblem3(x,y):
+ return x/y
+
+ assert noproblem(1,2) == 0.5
+ assert noproblem(1,0) is Undefined
+ assert noproblem2(4,2) == 2
+ assert noproblem2(1,0) == 0
+ assert noproblem3(1,2) == 0.5
+ assert noproblem3(1,0) is Undefined
+
+if __name__ == '__main__':
+ test_enums()
+ test_callable_proxy_set()
+ test_threaded_callset()
+ test_property()
+ test_dict()
+ test_list()
+ test_classproperty()
+ test_singleton()
+ test_safecall()
+ print("OK.")
A => glue.py/setup.py +29 -0
@@ 0,0 1,29 @@
+
+import os
+from setuptools import setup
+
+def read(fname):
+ return open(os.path.join(os.path.dirname(__file__), fname)).read()
+
+setup(
+ name = "glue.py",
+ version = "0.6",
+ author = "Leonard Ritter",
+ author_email = "contact@leonard-ritter.com",
+ description = ("The agnostic model/view toolshed"),
+ license = "MIT",
+ keywords = "python glue mvc observable idioms lightweight",
+ url = "https://bitbucket.org/duangle/glue.py",
+ py_modules=['glue'],
+ long_description=read('README.text'),
+ classifiers=[
+ "Development Status :: 5 - Production/Stable",
+ "Intended Audience :: Developers",
+ "Programming Language :: Python :: 2.7",
+ "Programming Language :: Python :: 3",
+ "Programming Language :: Python :: Implementation :: CPython",
+ "Programming Language :: Python :: Implementation :: PyPy",
+ "Topic :: Software Development :: Libraries",
+ "License :: OSI Approved :: MIT License",
+ ],
+)
A => liminal_legacy/.hg_archival.txt +6 -0
@@ 0,0 1,6 @@
+repo: 123b51019c6e65f82cc6267647cc56fe746419e4
+node: 36e8bfde49b262ac7195fe5e56e94395bb73a654
+branch: default
+latesttag: null
+latesttagdistance: 849
+changessincelatesttag: 852
A => liminal_legacy/.hgignore +39 -0
@@ 0,0 1,39 @@
+syntax: glob
+
+.sconsign.dblite
+.settings
+*.marsh
+*.egg-info
+*.pyc
+*.json
+*.blend1
+*.blend2
+*.blendc
+screenshot*.png
+batchgraph*.png
+*.avi
+*.glc
+*.mp4
+*.p3d
+debug.txt
+debuglog.txt
+__pycache__
+*.orig
+*.pyd
+*.soil
+*.cdef
+*.bgeconf
+*.so
+data.dat
+.project
+.pydevproject
+.liminal*.db
+.sass-cache
+
+syntax: regexp
+
+^build
+^doc/build
+^dist
+^py2exe/build
+^py2exe/dist
A => liminal_legacy/LICENSE +26 -0
@@ 0,0 1,26 @@
+
+Except when otherwise stated (look for LICENSE files in directories or
+information at the beginning of each file) all software and
+documentation is licensed as follows:
+
+ The MIT License
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation
+ files (the "Software"), to deal in the Software without
+ restriction, including without limitation the rights to use,
+ copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+
A => liminal_legacy/TODO +33 -0
@@ 0,0 1,33 @@
+
+Optimization ideas based on "Porting Source to Linux - Valve's Lessons Learned":
+https://developer.nvidia.com/sites/default/files/akamai/gamedev/docs/Porting%20Source%20to%20Linux.pdf
+
+* Replace VAOs with directly loading the buffers, as it's faster.
+ (see "Vertex Attribs – Alternative #1")
+
+* See if we can get RAD's Telemetry going
+
+* Use index buffers where possible
+
+* avoid binding objects, use EXT_direct_state_access
+
+* control vsync with EXT_swap_interval / EXT_swap_control_tear
+ ( http://www.opengl.org/wiki/Swap_Interval )
+
+* use NVX_gpu_memory_info / GL_ATI_meminfo
+
+* store texture settings with samplers, ARB_sampler_objects
+
+* avoid glGet*, glGetError, GL functions that return a value
+
+* use ARB_vertex_attrib_binding
+
+* use glBindMultiTextureEXT instead of glActiveTexture/glBindTexture
+
+* on-GPU texture compression: https://code.google.com/p/nvidia-texture-tools/
+ (more compression stuff here: http://developer.download.nvidia.com/SDK/10/opengl/samples.html)
+
+* copy rects: EXT_copy_texture, stretch rects: NV_draw_texture
+
+* stretch rect; MSAA blit scaled: EXT_framebuffer_multisample_blit_scaled
+
A => liminal_legacy/doc/Makefile +153 -0
@@ 0,0 1,153 @@
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS =
+SPHINXBUILD = sphinx-build
+PAPER =
+BUILDDIR = build
+
+# Internal variables.
+PAPEROPT_a4 = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
+# the i18n builder cannot share the environment and doctrees with the others
+I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
+
+.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
+
+help:
+ @echo "Please use \`make <target>' where <target> is one of"
+ @echo " html to make standalone HTML files"
+ @echo " dirhtml to make HTML files named index.html in directories"
+ @echo " singlehtml to make a single large HTML file"
+ @echo " pickle to make pickle files"
+ @echo " json to make JSON files"
+ @echo " htmlhelp to make HTML files and a HTML help project"
+ @echo " qthelp to make HTML files and a qthelp project"
+ @echo " devhelp to make HTML files and a Devhelp project"
+ @echo " epub to make an epub"
+ @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+ @echo " latexpdf to make LaTeX files and run them through pdflatex"
+ @echo " text to make text files"
+ @echo " man to make manual pages"
+ @echo " texinfo to make Texinfo files"
+ @echo " info to make Texinfo files and run them through makeinfo"
+ @echo " gettext to make PO message catalogs"
+ @echo " changes to make an overview of all changed/added/deprecated items"
+ @echo " linkcheck to check all external links for integrity"
+ @echo " doctest to run all doctests embedded in the documentation (if enabled)"
+
+clean:
+ -rm -rf $(BUILDDIR)/*
+
+html:
+ $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+dirhtml:
+ $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+singlehtml:
+ $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
+ @echo
+ @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
+
+pickle:
+ $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+ @echo
+ @echo "Build finished; now you can process the pickle files."
+
+json:
+ $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+ @echo
+ @echo "Build finished; now you can process the JSON files."
+
+htmlhelp:
+ $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+ @echo
+ @echo "Build finished; now you can run HTML Help Workshop with the" \
+ ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+qthelp:
+ $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+ @echo
+ @echo "Build finished; now you can run "qcollectiongenerator" with the" \
+ ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+ @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Beige.qhcp"
+ @echo "To view the help file:"
+ @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Beige.qhc"
+
+devhelp:
+ $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
+ @echo
+ @echo "Build finished."
+ @echo "To view the help file:"
+ @echo "# mkdir -p $$HOME/.local/share/devhelp/Beige"
+ @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Beige"
+ @echo "# devhelp"
+
+epub:
+ $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
+ @echo
+ @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+
+latex:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo
+ @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+ @echo "Run \`make' in that directory to run these through (pdf)latex" \
+ "(use \`make latexpdf' here to do that automatically)."
+
+latexpdf:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through pdflatex..."
+ $(MAKE) -C $(BUILDDIR)/latex all-pdf
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+text:
+ $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
+ @echo
+ @echo "Build finished. The text files are in $(BUILDDIR)/text."
+
+man:
+ $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
+ @echo
+ @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
+
+texinfo:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo
+ @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
+ @echo "Run \`make' in that directory to run these through makeinfo" \
+ "(use \`make info' here to do that automatically)."
+
+info:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo "Running Texinfo files through makeinfo..."
+ make -C $(BUILDDIR)/texinfo info
+ @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
+
+gettext:
+ $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
+ @echo
+ @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
+
+changes:
+ $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+ @echo
+ @echo "The overview file is in $(BUILDDIR)/changes."
+
+linkcheck:
+ $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+ @echo
+ @echo "Link check complete; look for any errors in the above output " \
+ "or in $(BUILDDIR)/linkcheck/output.txt."
+
+doctest:
+ $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+ @echo "Testing of doctests in the sources finished, look at the " \
+ "results in $(BUILDDIR)/doctest/output.txt."
A => liminal_legacy/doc/articles/linkdemo.py +262 -0
@@ 0,0 1,262 @@
+
+"""
+
+The Liminal RNode System - Scheduling Renderjobs In A Crazy World
+by Leonard Ritter, Duangle GbR (2014.4.11)
+
+
+
+Structuring rendering in a 3D application to allow for drastical alterations
+ in pipeline ordering isn't easy; you usually end up wanting all kinds of
+ permutations, like "i want _this_ actor with _this_ mesh, but this time into
+ _this_ framebuffer, but with the same camera data; screw that; we need
+ the same data in two different rendertargets, oh and I forgot we need
+ splitscreen, so all this needs to be split into four viewports - except
+ for this object, which needs to be on top of it all."
+
+...and the graphics engine coder works his ass off for two more weeks; usually
+these changes ripple through the entire codebase; starting at providing
+basic support at the engine level, over to the game programmer, who then has
+to make use of the new flags and refactor the rendering setup.
+
+"""
+
+# so here's how to setup scenes in our Liminal engine now;
+# it's a bit like linking generators and effects in a modular sequencer,
+# only that it works on packets (aka renderjobs) instead of streams of
+# audio impulses.
+
+# I haven't seen a game engine which organizes its renderpath like this yet,
+# so I'm pretty excited to demo this and get everyone's input on what I
+# think is a pretty lean and elegant way to organize 3D rendering in a game.
+
+
+# first, generate a default, uv mapped cube mesh with normals
+mesh = generate_cube_mesh()
+mesh.name = "cube"
+
+# setup a deferred material for the mesh
+material = GMaterial.new()
+material.name = "CubeMaterial"
+material.use_backface_culling = True
+material.use_shadow = True
+material.albedo = vec3(1.0,1.0,1.0)
+material.metallic_factor = 0.0
+material.roughness_factor = 1.0
+material.add_shader_texture('albedo_map', noise1_texture)
+material.add_shader_texture('height_map', noise1_texture)
+material.add_shader_texture('gloss_map', noise1_texture)
+material.add_shader_texture('glow_map', zero_texture())
+
+# create a new actor to give the mesh a location
+# the actor has no conception of anything else; just an orientation in space
+cube2 = Actor()
+cube2.name = 'cube2'
+# give it a physical body for collision testing
+cube2.body = GeomBody.new(ws.world, Box.new(ws.space, vec3(64,64,1)))
+cube2.body.kinematic = True
+# apply position and size
+cube2.position = vec3(0,0,-10)
+cube2.scale = vec3(32.0,32.0,0.5)
+
+# now concatenate our renderjob:
+# we want this mesh with this material at this actor's location,
+# so cube2 now depends on material, which depends on mesh
+cube2 << material << mesh
+
+# the order of assignments is only interesting in relation to the graph,
+# the only requirement is that the mesh is the a source, as meshes are
+# the only nodes that generate renderjobs; other nodes only augment them.
+#
+# this would be equally valid, and a good setup for when many actors
+# share the same material:
+material << cube2 << mesh
+
+# now setup an empty vizgroup which we use to bundle a group of objects,
+# possibly to control visibility later. This would be kind of like a "scene"
+vizgroup = RNode()
+vizgroup.name = 'scene'
+# make vizgroup depend on cube2
+vizgroup << cube2
+
+# The RNode is the basic building block for the render graph; materials, meshes,
+# actors, framebuffers, etc. and a lot of other default resources already
+# implement the RNode interface so they can be linked; But it's pretty easy
+# to write your own RNodes, or use a RNode as proxy to re-use another RNode in
+# two different unrelated paths.
+
+# get the singleton for the deferred renderer's resources, which is also
+# an RNode
+grm = GRenderManager()
+
+# link the vizgroup to the framebuffer for the deferred "scene" -
+grm.scene_framebuffer << vizgroup
+
+# now the last thing we need to complete the job is a camera to actually
+# specify where the scene is in relation to the viewer;
+camera = DebugCamera(lock_up=True)
+camera.name = 'camera'
+camera.far = 300.0
+camera.fov = 90.0
+# give the camera a collision box
+camera.body = GeomBody.new(ws.world, Box.new(ws.space, vec3(1,1,1)))
+camera.body.kinematic = True
+# and position it
+camera.look_at(vec3(0,-70.0,0), vec3(0,0,0), vec3(0,0,1))
+
+# all resources the deferred renderer has set up are set, except for the view
+# data, so let's just link the entire grm through it and we're done; the
+# scene is on screen.
+#
+# retrieve render manager singleton
+rm = RenderManager()
+# complete the chain; batch is the root node - now the renderer can 'see'
+# the content.
+rm.batch << camera << grm
+
+# For debugging, I have written a small 90 line function to have graphviz
+# draw a map of the graph, so I can see at any point what the pipeline looks
+# like
+
+"""
+
+"""
+
+# the declaration for RNodes is only 70 lines, and writing new RNodes is simple;
+# here is an RNode that changes the renderpass of all jobs going through it:
+class Renderpass(RNode):
+ def __init__(self, renderpass):
+ RNode.__init__(self)
+ self.renderpass = renderpass
+
+ def process(self, batch, jobs):
+ RNode.process(self, batch, jobs)
+ for job in jobs:
+ # we could also insert new orders into the jobs array here
+ # but let's just alter the renderpass
+ job.key.renderpass = self.renderpass
+
+# rnodes are only traversed on graph change, not per frame, and the work can
+# be done in a thread, as the renderer isn't required for processing.
+#
+# this is how the renderbatch then generates all jobs while traversing
+# the graph:
+
+def render_nodes(self):
+ # this could be done in a more conside way in python,
+ # but i wanted to keep the implementation parallel to a possible
+ # future C implementation; this one doesn't need any hash tables
+ # or binary searches, just arrays.
+
+ # get a new selection id
+ RNode.NODE_ID += 1
+ node_id = RNode.NODE_ID
+
+ # array of nodes in topological order
+ nodes = []
+ # how many times each node's content is referenced
+ noderefs = []
+
+ # recursive visitor function
+ def visit_node(node):
+ # if node has already been seen, skip
+ if node._node_id == node_id: return
+ # visit children first
+ for n in node.sources:
+ visit_node(n)
+ # increase noderefs for that node
+ noderefs[n._node_index] += 1
+ # assign the node's order index
+ node._node_index = len(nodes)
+ # update the select id to mark the node as visited
+ node._node_id = node_id
+ # append to topologically sorted array
+ nodes.append(node)
+ # and initialize the refcount for this node
+ noderefs.append(0)
+
+ # start at batch, which is the root
+ visit_node(self)
+
+ # debug out: print order in which nodes are processed, (which
+ # is not the order in which they are rendered, btw)
+ for i,n in enumerate(nodes):
+ print('#{}: {}'.format(i,n))
+
+ # array of each node's jobs
+ all_jobs = []
+ # linearly go through node in sorted order
+ for k,node in enumerate(nodes):
+ # list of jobs that this node will receive
+ jobs = []
+ # for all inputs of this node
+ for source in node.sources:
+ # retrieve the jobs that the predecessor has
+ src_index = source._node_index
+ src_jobs = all_jobs[src_index]
+ # make sure we have at least one reference left
+ assert noderefs[src_index] > 0
+ # if this is not the last reference
+ if noderefs[src_index] > 1:
+ # make a copy of the jobs and append
+ for job in src_jobs:
+ jobs.append(self.copy_job(job))
+ else:
+ # last reference: they terk er jerbs
+ for job in src_jobs:
+ jobs.append(job)
+ all_jobs[src_index] = None
+ # and decrease reference
+ noderefs[src_index] -= 1
+
+ # process the jobs
+ try:
+ node.process(self, jobs)
+ except:
+ print(node)
+ raise
+ # and append to job list
+ all_jobs.append(jobs)
+
+ # as jobs are already appended to the renderbatch on create/copy,
+ # we are already done; the batch can now be sorted and is ready
+ # for rendering.
+
+# And, just for completion, this is the basic RNode class declaration:
+
+class RNode(Named):
+ # selection id counter
+ NODE_ID = 0
+
+ def __init__(self):
+ Named.__init__(self)
+ # set of input nodes
+ self._sources = set()
+ # variables used during graph traversion
+ self._node_index = 0
+ self._node_id = 0
+
+ # this function does a number on the jobs; any operation is OK:
+ # alteration, duplication, deletion, etc. Change of order has no effect
+ # on anything.
+ def process(self, batch, jobs):
+ pass
+
+ @property
+ def sources(self):
+ return self._sources
+
+ # overloaded << operator to provide linking syntax sugar
+ def __lshift__(self, other):
+ assert self.name, '{} is unnamed'.format(self)
+ if isinstance(other, collections.Iterable):
+ for node in other:
+ assert node.name, '{} is unnamed'.format(other)
+ self.sources.update(other)
+ return other
+ else:
+ assert other.name, '{} is unnamed'.format(other)
+ self.sources.add(other)
+ return other
+
+# That's it. Thanks for reading :)
A => liminal_legacy/doc/articles/split-scheme.txt +146 -0
@@ 0,0 1,146 @@
+ An Inversible Split Scheme for Cascading Shadow Maps
+
+ by Leonard Ritter
+ Duangle GbR
+
+ First revision: 2013/12/18
+
+
+DISCLAIMER: This is original research and has not been verified by a
+second or third party yet.
+
+
+This is a proposal for a new function for the practical split
+scheme[1] for shadow map cascades by Zhang et al. The new
+function can be inverted in a shader to easily project
+distance z back to layer index i. An intuitive replacement
+for the weighing factor sigma is proposed as well.
+
+
+In the original article[1], a split scheme function was suggested to
+find good boundaries for individual shadow map cascades, so that
+given a layer index i, the optimal depth z for that index could
+be derived; given a normal scalar x which is retrieved via
+
+ i
+ x = -----------
+ layer_count
+
+and a sigma constant S in the range 0..1, which would allow the
+user interpolate linearly between an equidistant depth distribution
+and an ideal exponential one to bias the resolution distribution,
+the original split scheme equation would be
+
+ f
+ z = pow(-, x) n S + ((f - n) x + n) (1 - S) = f(x)
+ n
+
+n and f designate the distance of the near and far planes respectively.
+
+Unfortunately this function can't be inverted. With this solution,
+it is necessary to precompute and pass the layer boundaries to the
+pixel shader, which then performs a conditional range check
+for each fragment.
+
+
+The New Split Scheme
+====================
+
+Through experimentation and some guesswork, I found an alternative
+way to bias the distribution, which is purely exponential and
+has no additive polynomial component, so inversion is trivial.
+
+This bias effectively projects the near and far distances to a
+slope further up the exponential curve, which dampens the initial
+slow momentum and causes the function to approach a more linear
+scaling.
+
+The original sigma constant S is squared so that C = sqrt(S),
+and the split scheme function is altered so that
+
+ f f
+ z = (- + n - f) (pow(-------------, x) - 1.0) + n = f(x)
+ C (n - f) C + f
+
+At the original proposed default of S = 0.5, the function starts
+with near identical momentum, but converges marginally sooner
+towards f; across all values of S, the first half of the function
+distributes very similarly, so this function can easily be used
+as a drop-in replacement.
+
+
+Analysis
+========
+
+http://i.imgur.com/9OeUMBz.png
+
+The above picture shows the distribution for sigma = 0.5; the
+green and yellow curves show the boundaries at sigma 0.0 and 1.0,
+which are the same for both old and new functions.
+The purple curve is the original split scheme, the blue curve
+is the modified one.
+
+
+Shader Usage
+============
+
+In the shader, the layer index can now be retrieved by resolving x from a
+distance z and multiplying it with layer_count to retrieve the interpolated
+index of the layer to be retrieved.
+
+To calculate x from z, we inverse the new split scheme function so that
+
+ (z - far) C + far far
+ x = log(--------------------) / log(--------------------) = f(z)
+ (near - far) C + far (near - far) C + far
+
+For optimization, the inner factors and the second log term can be stored in
+three constant variables U, V, W:
+
+ d = far * (1.0 - C) + near * C
+
+ C
+ U = -
+ d
+
+ far - far * C
+ V = -------------
+ d
+
+ far
+ W = layer_count * log(2) / log(---)
+ d
+
+With these optimizations, the resolve reduces to
+
+ i = log2(z U + V) W
+
+
+Future Work
+===========
+
+I did not find the original weighing parameter sigma to be particularly
+easy to understand and apply. In practice, I found myself aiming to get
+an optimal balance between resolution and the range of the first cascade
+layer by guessing values for sigma; There was no clear relationship
+between the parameter and its implications.
+
+Ideally, I would prefer a new balancing variable z1 instead of sigma, which
+defines the distance at which the first shadow map layer ends, and is
+used to calculate the bias C.
+
+Unfortunately, I could not find a way to solve the equation towards C,
+so I leave this for further exploration. Until a simpler method can be found,
+bisecting towards C would be the nearest choice; A routine would
+methodically attempt to solve the split scheme function by adjusting C until
+z = z1 for x = 1 / layer_count.
+
+
+References
+==========
+
+[1] Parallel-Split Shadow Maps on Programmable GPUs
+ http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html
+
+
+Thanks to Sean Barrett and Fabian Giesen for help and suggestions.
A => liminal_legacy/doc/make.bat +190 -0
@@ 0,0 1,190 @@
+@ECHO OFF
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+ set SPHINXBUILD=sphinx-build
+)
+set BUILDDIR=build
+set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source
+set I18NSPHINXOPTS=%SPHINXOPTS% source
+if NOT "%PAPER%" == "" (
+ set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
+ set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
+)
+
+if "%1" == "" goto help
+
+if "%1" == "help" (
+ :help
+ echo.Please use `make ^<target^>` where ^<target^> is one of
+ echo. html to make standalone HTML files
+ echo. dirhtml to make HTML files named index.html in directories
+ echo. singlehtml to make a single large HTML file
+ echo. pickle to make pickle files
+ echo. json to make JSON files
+ echo. htmlhelp to make HTML files and a HTML help project
+ echo. qthelp to make HTML files and a qthelp project
+ echo. devhelp to make HTML files and a Devhelp project
+ echo. epub to make an epub
+ echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
+ echo. text to make text files
+ echo. man to make manual pages
+ echo. texinfo to make Texinfo files
+ echo. gettext to make PO message catalogs
+ echo. changes to make an overview over all changed/added/deprecated items
+ echo. linkcheck to check all external links for integrity
+ echo. doctest to run all doctests embedded in the documentation if enabled
+ goto end
+)
+
+if "%1" == "clean" (
+ for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
+ del /q /s %BUILDDIR%\*
+ goto end
+)
+
+if "%1" == "html" (
+ %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/html.
+ goto end
+)
+
+if "%1" == "dirhtml" (
+ %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
+ goto end
+)
+
+if "%1" == "singlehtml" (
+ %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
+ goto end
+)
+
+if "%1" == "pickle" (
+ %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can process the pickle files.
+ goto end
+)
+
+if "%1" == "json" (
+ %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can process the JSON files.
+ goto end
+)
+
+if "%1" == "htmlhelp" (
+ %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can run HTML Help Workshop with the ^
+.hhp project file in %BUILDDIR%/htmlhelp.
+ goto end
+)
+
+if "%1" == "qthelp" (
+ %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can run "qcollectiongenerator" with the ^
+.qhcp project file in %BUILDDIR%/qthelp, like this:
+ echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Beige.qhcp
+ echo.To view the help file:
+ echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Beige.ghc
+ goto end
+)
+
+if "%1" == "devhelp" (
+ %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished.
+ goto end
+)
+
+if "%1" == "epub" (
+ %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The epub file is in %BUILDDIR%/epub.
+ goto end
+)
+
+if "%1" == "latex" (
+ %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
+ goto end
+)
+
+if "%1" == "text" (
+ %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The text files are in %BUILDDIR%/text.
+ goto end
+)
+
+if "%1" == "man" (
+ %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The manual pages are in %BUILDDIR%/man.
+ goto end
+)
+
+if "%1" == "texinfo" (
+ %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
+ goto end
+)
+
+if "%1" == "gettext" (
+ %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
+ goto end
+)
+
+if "%1" == "changes" (
+ %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.The overview file is in %BUILDDIR%/changes.
+ goto end
+)
+
+if "%1" == "linkcheck" (
+ %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Link check complete; look for any errors in the above output ^
+or in %BUILDDIR%/linkcheck/output.txt.
+ goto end
+)
+
+if "%1" == "doctest" (
+ %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Testing of doctests in the sources finished, look at the ^
+results in %BUILDDIR%/doctest/output.txt.
+ goto end
+)
+
+:end
A => liminal_legacy/doc/source/_static/__donotdelete__ +0 -0
A => liminal_legacy/doc/source/_templates/__donotdelete__ +0 -0
A => liminal_legacy/doc/source/conf.py +242 -0
@@ 0,0 1,242 @@
+# -*- coding: utf-8 -*-
+#
+# Beige documentation build configuration file, created by
+# sphinx-quickstart on Thu Jan 3 13:01:37 2013.
+#
+# This file is execfile()d with the current directory set to its containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys, os
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+sys.path.insert(0, os.path.abspath('..'))
+
+# -- General configuration -----------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be extensions
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+extensions = ['sphinx.ext.autodoc', 'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.pngmath', 'sphinx.ext.ifconfig', 'sphinx.ext.viewcode']
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix of source filenames.
+source_suffix = '.txt'
+
+# The encoding of source files.
+#source_encoding = 'utf-8-sig'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'Beige'
+copyright = u'2013, Leonard Ritter'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = '0.1'
+# The full version, including alpha/beta/rc tags.
+release = '0.1'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+exclude_patterns = []
+
+# The reST default role (used for this markup: `text`) to use for all documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+
+# -- Options for HTML output ---------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+html_theme = 'default'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further. For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+#html_theme_path = []
+
+# The name for this set of Sphinx documents. If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar. Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_domain_indices = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+#html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+#html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it. The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = None
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'Beigedoc'
+
+
+# -- Options for LaTeX output --------------------------------------------------
+
+latex_elements = {
+# The paper size ('letterpaper' or 'a4paper').
+#'papersize': 'letterpaper',
+
+# The font size ('10pt', '11pt' or '12pt').
+#'pointsize': '10pt',
+
+# Additional stuff for the LaTeX preamble.
+#'preamble': '',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, documentclass [howto/manual]).
+latex_documents = [
+ ('index', 'Beige.tex', u'Beige Documentation',
+ u'Leonard Ritter', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# If true, show page references after internal links.
+#latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+#latex_show_urls = False
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_domain_indices = True
+
+
+# -- Options for manual page output --------------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+ ('index', 'liminal', u'Beige Documentation',
+ [u'Leonard Ritter'], 1)
+]
+
+# If true, show URL addresses after external links.
+#man_show_urls = False
+
+
+# -- Options for Texinfo output ------------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+# dir menu entry, description, category)
+texinfo_documents = [
+ ('index', 'Beige', u'Beige Documentation',
+ u'Leonard Ritter', 'Beige', 'One line description of project.',
+ 'Miscellaneous'),
+]
+
+# Documents to append as an appendix to all manuals.
+#texinfo_appendices = []
+
+# If false, no module index is generated.
+#texinfo_domain_indices = True
+
+# How to display URL addresses: 'footnote', 'no', or 'inline'.
+#texinfo_show_urls = 'footnote'
A => liminal_legacy/doc/source/index.txt +25 -0
@@ 0,0 1,25 @@
+.. Beige documentation master file, created by
+ sphinx-quickstart on Thu Jan 3 13:01:37 2013.
+ You can adapt this file completely to your liking, but it should at least
+ contain the root `toctree` directive.
+
+Welcome to Beige's documentation!
+=================================
+
+Contents:
+
+.. toctree::
+ :maxdepth: 2
+
+.. toctree::
+ :maxdepth: 3
+
+ reference
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
+
A => liminal_legacy/doc/source/reference.txt +171 -0
@@ 0,0 1,171 @@
+Beige API Reference
+===================
+
+beige.engine
+------------
+
+.. automodule:: beige.engine
+ :show-inheritance:
+ :members:
+ :undoc-members:
+
+beige.engine.audio
+------------------
+
+.. automodule:: beige.engine.audio
+ :show-inheritance:
+ :members:
+ :undoc-members:
+
+beige.engine.camera
+-------------------
+
+.. automodule:: beige.engine.camera
+ :show-inheritance:
+ :members:
+ :undoc-members:
+
+beige.engine.debugdraw
+----------------------
+
+.. automodule:: beige.engine.debugdraw
+ :show-inheritance:
+ :members:
+ :undoc-members:
+
+beige.engine.entity
+-------------------
+
+.. automodule:: beige.engine.entity
+ :show-inheritance:
+ :members:
+ :undoc-members:
+
+beige.engine.font
+-----------------
+
+.. automodule:: beige.engine.font
+ :show-inheritance:
+ :members:
+ :undoc-members:
+
+beige.engine.ghost
+------------------
+
+.. automodule:: beige.engine.ghost
+ :show-inheritance:
+ :members:
+ :undoc-members:
+
+beige.engine.input
+------------------
+
+.. automodule:: beige.engine.input
+ :show-inheritance:
+ :members:
+ :undoc-members:
+
+beige.engine.interface
+----------------------
+
+.. automodule:: beige.engine.interface
+ :show-inheritance:
+ :members:
+ :undoc-members:
+
+beige.engine.logic
+------------------
+
+.. automodule:: beige.engine.logic
+ :show-inheritance:
+ :members:
+ :undoc-members:
+
+beige.engine.material
+---------------------
+
+.. automodule:: beige.engine.material
+ :show-inheritance:
+ :members:
+ :undoc-members:
+
+beige.engine.mesh
+-----------------
+
+.. automodule:: beige.engine.mesh
+ :show-inheritance:
+ :members:
+ :undoc-members:
+
+beige.engine.node
+-----------------
+
+.. automodule:: beige.engine.node
+ :show-inheritance:
+ :members:
+ :undoc-members:
+
+beige.engine.ppfx
+-----------------
+
+.. automodule:: beige.engine.ppfx
+ :show-inheritance:
+ :members:
+ :undoc-members:
+
+beige.engine.render
+-------------------
+
+.. automodule:: beige.engine.render
+ :show-inheritance:
+ :members:
+ :undoc-members:
+
+beige.engine.scene
+------------------
+
+.. automodule:: beige.engine.scene
+ :show-inheritance:
+ :members:
+ :undoc-members:
+
+beige.engine.shader
+-------------------
+
+.. automodule:: beige.engine.shader
+ :show-inheritance:
+ :members:
+ :undoc-members:
+
+beige.engine.texture
+--------------------
+
+
+.. automodule:: beige.engine.texture
+ :show-inheritance:
+ :members:
+ :undoc-members:
+
+beige.builder
+-------------
+
+.. automodule:: beige.builder
+ :show-inheritance:
+ :members:
+ :undoc-members:
+
+beige.utils
+-----------
+
+.. automodule:: beige.utils
+ :show-inheritance:
+ :members:
+ :undoc-members:
+
+beige.runtime
+-------------
+
+.. automodule:: beige.runtime
+ :show-inheritance:
+ :members:
+ :undoc-members:
A => liminal_legacy/liminal/__init__.py +0 -0
A => liminal_legacy/liminal/assets/fonts/DejaVuSans.ttf +0 -0
A => liminal_legacy/liminal/assets/fonts/SourceSansPro-Black.ttf +0 -0
A => liminal_legacy/liminal/assets/fonts/SourceSansPro-BlackItalic.ttf +0 -0
A => liminal_legacy/liminal/assets/fonts/SourceSansPro-Bold.ttf +0 -0
A => liminal_legacy/liminal/assets/fonts/SourceSansPro-BoldItalic.ttf +0 -0
A => +0 -0
A => +0 -0
A => liminal_legacy/liminal/assets/fonts/SourceSansPro-Italic.ttf +0 -0
A => liminal_legacy/liminal/assets/fonts/SourceSansPro-Light.ttf +0 -0
A => liminal_legacy/liminal/assets/fonts/SourceSansPro-LightItalic.ttf +0 -0
A => liminal_legacy/liminal/assets/fonts/SourceSansPro-Regular.ttf +0 -0
A => liminal_legacy/liminal/assets/fonts/SourceSansPro-Semibold.ttf +0 -0
A => liminal_legacy/liminal/assets/fonts/SourceSansPro-SemiboldItalic.ttf +0 -0
A => liminal_legacy/liminal/assets/fonts/UbuntuMono-R.ttf +0 -0
A => liminal_legacy/liminal/assets/fonts/raleway_thin.ttf +0 -0
A => liminal_legacy/liminal/assets/liminal/a4epro2.beigefont +0 -0
A => liminal_legacy/liminal/assets/liminal/button_normal.png +0 -0
A => liminal_legacy/liminal/assets/liminal/button_pressed.png +0 -0
A => liminal_legacy/liminal/assets/liminal/cursor.png +0 -0
A => liminal_legacy/liminal/assets/liminal/dosis.beigefont +0 -0
A => liminal_legacy/liminal/assets/liminal/nevis.beigefont +0 -0
A => liminal_legacy/liminal/assets/liminal/window.png +0 -0
A => liminal_legacy/liminal/assets/liminal/window_bg.png +0 -0
A => liminal_legacy/liminal/assets/liminal/window_closing.png +0 -0
A => liminal_legacy/liminal/assets/shaders/flat/simple.glsl +130 -0
@@ 0,0 1,130 @@
+
+#include "std/std.glsl"
+#include "lib/math.glsl"
+
+#ifndef USE_COLOR_MAP
+#define USE_COLOR_MAP 0
+#endif
+
+#ifndef USE_VERTEX_COLOR
+#define USE_VERTEX_COLOR 0
+#endif
+
+#ifndef USE_DISTANCE_FADE
+#define USE_DISTANCE_FADE 0
+#endif
+
+#ifndef USE_NORMALS
+#define USE_NORMALS 0
+#endif
+#ifndef USE_RIMLIGHT
+#define USE_RIMLIGHT 0
+#endif
+
+#ifndef USE_FLAT_NORMALS
+#define USE_FLAT_NORMALS 0
+#endif
+
+#ifndef USE_VERTEX_ID
+#define USE_VERTEX_ID 1
+#endif
+
+#if USE_COLOR_MAP
+#include "lib/mapping.glsl"
+#endif
+
+#if USE_VERTEX_COLOR
+varying vec4 color;
+#endif
+#if USE_DISTANCE_FADE
+varying vec4 position;
+#endif
+#if USE_NORMALS
+#if USE_FLAT_NORMALS
+flat varying vec3 normal;
+#else
+varying vec3 normal;
+#endif
+#if USE_RIMLIGHT
+varying vec3 view_position;
+#if USE_FLAT_NORMALS
+flat varying vec3 view_normal;
+#else
+varying vec3 view_normal;
+#endif
+#endif
+#endif
+
+#if VERTEX_SHADER
+
+void main(void)
+{
+ vec4 wp = (mtx_model * in_Position);
+ vec4 vp = (mtx_view * wp);
+ gl_Position = mtx_proj * vp;
+
+#if USE_NORMALS
+ normal = mat3(mtx_model) * in_Normal; // normalize(in_Normal);
+#if USE_RIMLIGHT
+ view_position = vp.xyz;
+ view_normal = mat3(mtx_view) * normal;
+#endif
+#endif
+
+#if USE_COLOR_MAP
+ write_texcoord();
+#endif
+
+#if USE_VERTEX_COLOR
+ color = in_Color;
+#endif
+#if USE_DISTANCE_FADE
+ position = gl_Position;
+#endif
+}
+
+#elif FRAGMENT_SHADER
+
+#if USE_COLOR_MAP
+uniform SAMPLER2DTYPE color_map;
+#endif
+
+void main(void) {
+ vec4 final_color = vec4(1.0);
+
+#if USE_COLOR_MAP
+ final_color *= map_texture(color_map);
+#endif
+
+#if USE_QUILTING
+ vec3 spln = get_quilt_normal(normal);
+ vec3 view_spln = normalize(mat3(mtx_view) * spln);
+
+#define FRAG_NORMAL spln
+#define FRAG_VIEW_NORMAL view_spln
+#else // !USE_QUILTING
+#define FRAG_NORMAL normalize(normal)
+#define FRAG_VIEW_NORMAL normalize(view_normal)
+#endif // USE_QUILTING
+
+#if USE_VERTEX_COLOR
+ final_color *= color;
+#endif
+
+#if USE_DISTANCE_FADE
+ final_color *= (1.0 - pow(position.z / position.w, 4.0));
+#endif
+
+#if USE_NORMALS
+ final_color *= mix(0.2, 1.0, max(0.0,
+ dot(FRAG_NORMAL, vec3(0.26726,-0.53452,0.80178))));
+#if USE_RIMLIGHT
+ final_color.rgb += vec3(max(0.0,0.5-abs(dot(
+ FRAG_VIEW_NORMAL, normalize(view_position)))));
+#endif
+#endif
+
+ out_Color = final_color;
+}
+
+#endif // FRAGMENT_SHADER
No newline at end of file
A => liminal_legacy/liminal/assets/shaders/font/font.glsl +26 -0
@@ 0,0 1,26 @@
+
+varying float sharpness;
+varying vec2 texcoord;
+
+#if VERTEX_SHADER
+
+in float in_sharpness;
+
+void write_font_attribs() {
+ // coordinate of the 1st texture channel
+ texcoord = in_TexCoord0;
+ sharpness = in_sharpness;
+}
+
+#elif FRAGMENT_SHADER
+
+uniform sampler2D font_map;
+
+float read_font_alpha() {
+ vec2 uv = texcoord;
+ float C = max(1.0, sharpness / (max(abs(dFdx(uv.s)), abs(dFdy(uv.t))) * 32.0));
+ float d = texture(font_map, uv).r;
+ return clamp((d-0.5)*C+0.5, 0.0, 1.0);
+}
+
+#endif
No newline at end of file
A => liminal_legacy/liminal/assets/shaders/font/font_gshader.fs +22 -0
@@ 0,0 1,22 @@
+
+#include "gshader_frag.glsl"
+#include "font_frag.glsl"
+
+void main(void) {
+ vec2 uv = gl_TexCoord[0];
+
+ GBufferAttributes attribs = gba_default();
+ float d = read_font_alpha(uv);
+ vec3 color = gl_Color.rgb;
+ if (solid) {
+ if (d <= 0.5)
+ discard;
+ } else {
+ color *= d;
+ }
+ attribs.albedo *= color;
+ attribs.light *= color;
+
+
+ write_gbuffer(attribs);
+}
A => liminal_legacy/liminal/assets/shaders/font/font_gshader.vs +8 -0
@@ 0,0 1,8 @@
+#include "gshader_vert.glsl"
+#include "font_vert.glsl"
+
+void main(void)
+{
+ write_vertex_vars();
+ write_font_attribs();
+}
A => liminal_legacy/liminal/assets/shaders/font/simple.glsl +24 -0
@@ 0,0 1,24 @@
+
+#include "std/std.glsl"
+#include "font.glsl"
+
+varying vec4 color;
+
+#if VERTEX_SHADER
+
+void main(void)
+{
+ // original vertex position, no changes
+ gl_Position = mtx_proj * (mtx_view * (mtx_model * in_Position));
+ // vertex color
+ color = in_Color;
+ write_font_attribs();
+}
+#elif FRAGMENT_SHADER
+
+void main(void)
+{
+ float d = read_font_alpha();
+ out_Color = color * vec4(d);
+}
+#endif
No newline at end of file
A => liminal_legacy/liminal/assets/shaders/g/gbuffer_attr.glsl +18 -0
@@ 0,0 1,18 @@
+
+#ifndef GBA_USE_DEPTH
+#define GBA_USE_DEPTH 0
+#endif
+
+struct GBufferAttributes {
+#if GBA_USE_DEPTH
+ float depth;
+#endif
+ vec3 albedo;
+ float ao;
+ vec2 velocity;
+ vec3 normal;
+ float roughness;
+ float metallic;
+ float sky;
+ vec4 light;
+};
A => liminal_legacy/liminal/assets/shaders/g/gshader.glsl +224 -0
@@ 0,0 1,224 @@
+
+#include "std/std.glsl"
+
+#ifndef SHADOW_MATERIAL
+#define SHADOW_MATERIAL 0
+#endif
+
+#ifndef DISCARD_ALBEDO_ALPHA
+#define DISCARD_ALBEDO_ALPHA 0
+#endif
+
+#ifndef DISCARD_GLOW_ALPHA
+#define DISCARD_GLOW_ALPHA 0
+#endif
+
+#ifndef USE_GLOW_MAP
+#define USE_GLOW_MAP 0
+#endif
+
+#ifndef USE_GLOSS_MAP
+#define USE_GLOSS_MAP 0
+#endif
+#ifndef USE_HEIGHT_MAP
+#define USE_HEIGHT_MAP 0
+#endif
+#ifndef USE_DERIVATIVE_MAP
+#define USE_DERIVATIVE_MAP 0
+#endif
+#ifndef USE_ALBEDO_MAP
+#define USE_ALBEDO_MAP 0
+#endif
+
+#ifndef USE_TEXTURE_ARRAY
+#define USE_TEXTURE_ARRAY 0
+#endif
+
+
+
+#if SHADOW_MATERIAL
+#if ((USE_ALBEDO_MAP && DISCARD_ALBEDO_ALPHA) || (USE_GLOW_MAP && DISCARD_GLOW_ALPHA))
+#define USE_SHADOW_UV 1
+#else
+#define USE_SHADOW_UV 0
+#endif
+#endif // SHADOW_MATERIAL
+
+#include "lib/mapping.glsl"
+
+#if VERTEX_SHADER
+
+#if SHADOW_MATERIAL
+#if USE_SHADOW_UV
+out VECUVTYPE v_texcoord;
+#endif
+#else
+#include "gshader_vars.glsl"
+#include "gshader_vert.glsl"
+#endif
+
+void main(void)
+{
+#if SHADOW_MATERIAL
+#if USE_SHADOW_UV
+ v_texcoord = IN_TEXCOORD;
+#endif
+ gl_Position = mtx_proj * (mtx_view * (mtx_model * vec4(in_Position.xyz,1.0)));
+#else
+ write_vertex_vars();
+#endif
+}
+
+#elif GEOMETRY_SHADER
+
+layout(triangles) in;
+layout(triangle_strip, max_vertices = LAYER_GS_VERTEX_COUNT) out;
+
+uniform vec4 dm_offsets[LAYER_COUNT];
+
+#if USE_SHADOW_UV
+in VECUVTYPE v_texcoord[3];
+out VECUVTYPE f_texcoord;
+#endif
+
+void main() {
+ for (int k = 0; k < LAYER_COUNT; ++k) {
+ for(int i = 0; i < 3; ++i) {
+ gl_Layer = k;
+ vec4 p = gl_in[i].gl_Position;
+ vec4 o = dm_offsets[k];
+#if USE_SHADOW_UV
+ f_texcoord = v_texcoord[i];
+#endif
+ gl_Position = vec4(p.xy * o.xy + o.zw, p.zw);
+ EmitVertex();
+ }
+ EndPrimitive();
+ }
+}
+
+#elif FRAGMENT_SHADER
+
+#if SHADOW_MATERIAL
+#include "sm/shadow_frag.glsl"
+
+#if USE_SHADOW_UV
+in VECUVTYPE f_texcoord;
+#endif
+
+#else
+
+#include "gshader_vars.glsl"
+#include "gshader_frag.glsl"
+#include "lib/perturb.glsl"
+#include "lib/sh.glsl"
+
+#endif
+
+#ifndef HEIGHT_MAP_FACTOR
+#define HEIGHT_MAP_FACTOR 1.0
+#endif
+
+//#define USE_POM
+#ifdef USE_POM
+#include "pom/pom_frag.glsl"
+#endif
+
+#if USE_ALBEDO_MAP
+uniform SAMPLER2DTYPE albedo_map;
+#endif
+#if USE_HEIGHT_MAP
+uniform SAMPLER2DTYPE height_map;
+#endif
+#if USE_GLOSS_MAP
+uniform SAMPLER2DTYPE gloss_map;
+#endif
+#if USE_GLOW_MAP
+uniform SAMPLER2DTYPE glow_map;
+#endif
+
+#ifndef DISCARD_ALPHA_MAX
+#define DISCARD_ALPHA_MAX 0.729
+#endif
+
+void main(void) {
+#if SHADOW_MATERIAL
+#if USE_SHADOW_UV
+ VECUVTYPE uv = f_texcoord;
+#endif
+#else
+#if !USE_QUILTING
+ VECUVTYPE uv = texcoord;
+#endif
+#ifdef USE_POM
+ uv = calc_pom_parallax_uv(height_map, uv);
+#endif
+#endif
+
+#if USE_ALBEDO_MAP && (!SHADOW_MATERIAL || DISCARD_ALBEDO_ALPHA)
+ vec4 albedo = map_texture(albedo_map);
+#endif
+#if USE_GLOW_MAP && (!SHADOW_MATERIAL || DISCARD_GLOW_ALPHA)
+ vec4 glow = map_texture(glow_map);
+#endif
+
+#if !SHADOW_MATERIAL
+#if USE_GLOSS_MAP
+ float gloss = map_texture(gloss_map).r;
+#endif
+
+ GBufferAttributes attribs = gba_default();
+
+#if USE_HEIGHT_MAP
+ vec3 h_normal;
+#if USE_DERIVATIVE_MAP
+ h_normal = perturb_normal_derivative_mapped(mv_vertex, attribs.normal, height_map, uv);
+#else // !USE_DERIVATIVE_MAP
+ h_normal = perturb_normal_mapped(mv_vertex, attribs.normal, height_map, uv);
+#endif // !USE_DERIVATIVE_MAP
+#endif // USE_HEIGHT_MAP
+#endif
+
+#if USE_LRMANNEQ
+ vec2 mat0; vec4 mat1; vec3 nn; vec4 base_color;
+ map_texture_dm(albedo_map, mat0, mat1, nn, base_color);
+ attribs.normal = mat3(mtx_model) * nn;
+
+ albedo.rgb = base_color.xyz * mat1.x;
+ //attribs.ao *= base_color.w;
+ attribs.light.rgb *= base_color.xyz * base_color.w * mat1.y;
+ attribs.roughness *= mat1.z;
+ attribs.metallic *= mat1.w;
+#endif
+
+#if USE_ALBEDO_MAP && DISCARD_ALBEDO_ALPHA
+ if (albedo.a < DISCARD_ALPHA_MAX)
+ discard;
+#endif
+#if USE_GLOW_MAP && DISCARD_GLOW_ALPHA && (SHADOW_MATERIAL || !USE_ADDITIVE)
+ if (glow.a < DISCARD_ALPHA_MAX)
+ discard;
+#endif
+
+#if SHADOW_MATERIAL
+ write_shadow_frag();
+#else // !SHADOW_MATERIAL
+#if USE_ALBEDO_MAP
+ attribs.albedo *= albedo.rgb;
+#endif
+#if USE_GLOW_MAP
+ attribs.light.rgb *= glow.rgb;
+#endif
+#if USE_GLOSS_MAP
+ attribs.roughness *= gloss;
+#endif
+#if USE_HEIGHT_MAP
+ attribs.normal = normalize(mix(attribs.normal, h_normal, HEIGHT_MAP_FACTOR));
+#endif
+
+ write_gbuffer(attribs);
+#endif // !SHADOW_MATERIAL
+
+}
+
+#endif // FRAGMENT_SHADER
No newline at end of file
A => liminal_legacy/liminal/assets/shaders/g/gshader_frag.glsl +99 -0
@@ 0,0 1,99 @@
+#ifndef USE_ADDITIVE
+#define USE_ADDITIVE 0
+#endif
+
+#ifndef USE_VERTEX_COLOR
+#define USE_VERTEX_COLOR 0
+#endif
+
+#ifndef USE_BACKFACE_NORMALS
+#define USE_BACKFACE_NORMALS 0
+#endif
+
+#ifndef USE_GAMMA2
+#define USE_GAMMA2 0
+#endif
+
+// updates here must also be updated in FragDataLocations
+#define out_albedo out_Color
+out vec2 out_matfx;
+out vec2 out_normal;
+out vec4 out_light;
+out ivec2 out_velocity;
+
+#include "lib/normal_codec.glsl"
+#include "gbuffer_attr.glsl"
+
+#if USE_GAMMA2
+#define GAMMAFIX(X) ((X)*(X))
+#else
+#define GAMMAFIX(X) X
+#endif
+
+#if USE_VERTEX_COLOR
+#define VERTEX_COLOR(X) (vertex_color * X)
+#else
+#define VERTEX_COLOR(X) X
+#endif
+
+vec2 gba_default_velocity() {
+ vec2 p0 = (mvp_vertex.xy / mvp_vertex.w)*0.5 + 0.5;
+ vec2 p1 = (last_mvp_vertex.xy / last_mvp_vertex.w)*0.5 + 0.5;
+ return p0 - p1;
+}
+
+GBufferAttributes gba_default() {
+#if USE_BACKFACE_NORMALS
+ vec3 fixed_normal = normal * (float(gl_FrontFacing)*2.0 - 1.0);
+#define GBA_NORMAL fixed_normal
+#else
+#define GBA_NORMAL normal
+#endif
+ return GBufferAttributes(
+ VERTEX_COLOR(albedo),
+ 1.0,
+ gba_default_velocity(),
+ GBA_NORMAL,
+ roughness_factor,
+ metallic_factor,
+ 0.0,
+ vec4(VERTEX_COLOR(emissive_color) * emissive, 0.0));
+#undef GBA_NORMAL
+}
+
+GBufferAttributes gba_empty() {
+ return GBufferAttributes(
+ vec3(0.0),
+ 1.0,
+ vec2(0.0),
+ vec3(0.0),
+ 0.0,
+ 0.0,
+ 0.0,
+ vec4(0.0));
+}
+
+void write_gbuffer(in GBufferAttributes attribs) {
+#if USE_ADDITIVE
+ out_albedo.rgba = vec4(0.0, 0.0, 0.0, 0.0);
+ out_matfx.rg = vec2(0.0);
+ out_normal.rg = vec2(0.0);
+ out_velocity.rg = ivec2(0);
+#else
+ out_albedo.rgba = vec4(attribs.albedo, attribs.ao);
+ out_matfx.rg = vec2(
+ attribs.roughness + roughness_bias, attribs.metallic);
+ out_normal.rg = vec2(
+ encode_normal(normalize(attribs.normal)));
+ out_velocity.rg = ivec2(attribs.velocity*127.0);
+#endif
+ out_light = GAMMAFIX(attribs.light);
+}
+
+void write_gbuffer_depth_vel_fx(in GBufferAttributes attribs) {
+ out_albedo.rgba = vec4(0.0, 0.0, 0.0, 0.0);
+ out_matfx.rg = vec2(attribs.roughness, attribs.metallic);
+ out_normal.rg = vec2(0.0);
+ out_velocity.rg = ivec2(attribs.velocity*127.0);
+ out_light = vec4(0.0);
+}
A => liminal_legacy/liminal/assets/shaders/g/gshader_sdf.glsl +216 -0
@@ 0,0 1,216 @@
+/*
+These three functions must be declared outside, only for the fragment
+shader: dC and dF
+
+see below for signatures.
+*/
+
+#ifndef DEBUG_STEPS
+#define DEBUG_STEPS 0
+#endif
+
+#ifndef SHADOW_MATERIAL
+#define SHADOW_MATERIAL 0
+#endif
+
+struct VXData {
+ mat3 mtx_toray;
+ mat4 mtx_modelview4;
+ float view_z; // offset
+#if SHADOW_MATERIAL
+ vec3 vertex;
+#else
+ mat3 mtx_modelview3;
+ mat4 mtx_last_mvp;
+#endif
+};
+
+#if VERTEX_SHADER
+
+#if !SHADOW_MATERIAL
+#include "g/gshader_vert.glsl"
+#endif
+
+out VXData data;
+
+void main(void)
+{
+ data.mtx_modelview4 = mtx_view * mtx_model;
+ vec4 p = (data.mtx_modelview4 * in_Position);
+#if SHADOW_MATERIAL
+ gl_Position = mtx_proj * p;
+#else
+ write_vertex_vars();
+#endif
+ mat3 mv3 = mat3(data.mtx_modelview4);
+#if SHADOW_MATERIAL
+ data.vertex = in_Position.xyz;
+ data.view_z = 1.0;
+#else
+ data.mtx_modelview3 = mv3;
+ data.mtx_last_mvp = mtx_last_mvp();
+ data.view_z = -p.z;
+#endif
+ data.mtx_toray = transpose(mv3);
+}
+
+#elif GEOMETRY_SHADER // shadow material only
+
+layout(triangles) in;
+layout(triangle_strip, max_vertices = LAYER_GS_VERTEX_COUNT) out;
+
+in VXData data[3];
+out VXData data_out;
+
+uniform vec4 dm_offsets[LAYER_COUNT];
+
+void main() {
+ for (int k = 0; k < LAYER_COUNT; ++k) {
+ for(int i = 0; i < 3; ++i) {
+ gl_Layer = k;
+ vec4 p = gl_in[i].gl_Position;
+ vec4 o = dm_offsets[k];
+ data_out.mtx_toray = data[i].mtx_toray;
+ data_out.mtx_modelview4 = data[i].mtx_modelview4;
+ data_out.vertex = data[i].vertex;
+ data_out.view_z = data[i].view_z;
+ gl_Position = vec4(p.xy * o.xy + o.zw, p.zw);
+ EmitVertex();
+ }
+ EndPrimitive();
+ }
+}
+
+#elif FRAGMENT_SHADER
+
+#include "g/gshader_frag.glsl"
+
+#if SHADOW_MATERIAL
+#define data data_out
+#endif
+in VXData data;
+
+void dC(vec3 p, float z, inout GBufferAttributes attribs);
+float dF(vec3 p, float z);
+
+// max number of steps to trace for the raw function
+#ifndef RAY_STEP
+#define RAY_STEP 64
+#endif
+
+uniform float texel_size;
+
+#if SHADOW_MATERIAL
+#define TEXEL_SIZE (0.7071 / 1024.0)
+#else
+#define TEXEL_SIZE texel_size
+#endif
+
+// what delta width to pick for
+// normal derivation (width = 2*NORMAL_DX)
+#ifndef NORMAL_DX
+#define NORMAL_DX 0.01
+#endif
+
+// abort after which depth
+#ifndef MAX_DEPTH
+// diagonal of unit cube
+#define MAX_DEPTH 3.4641
+#endif
+
+//const vec2 dx = vec2(NORMAL_DX,0.0);
+
+void write_point(vec3 p, int steps, float t, float z) {
+#if SHADOW_MATERIAL
+ vec4 pw = data.mtx_modelview4 * vec4(p,1.0);
+
+ float depth = (mtx_proj * pw).z;
+ float d1=gl_DepthRange.far; float d0=gl_DepthRange.near;
+ gl_FragDepth = (((d1-d0) * depth) + d0 + d1) / 2.0;
+ out_Color = vec4(1.0);
+#else
+ //vec2 dx = vec2(max(NORMAL_DX,t), 0.0);
+ vec2 dx = vec2(t*4.0, 0.0);
+
+ vec3 n = vec3(
+ dF(p + dx.xyy,z) - dF(p - dx.xyy,z),
+ dF(p + dx.yxy,z) - dF(p - dx.yxy,z),
+ dF(p + dx.yyx,z) - dF(p - dx.yyx,z)
+ );
+
+ GBufferAttributes attribs = gba_default();
+
+ vec4 pw = data.mtx_modelview4 * vec4(p,1.0);
+ float lin_depth = pw.z;
+
+ vec4 v0 = mtx_proj * pw;
+ vec4 v1 = data.mtx_last_mvp * vec4(p,1.0);
+
+ vec2 p0 = (v0.xy / v0.w)*0.5 + 0.5;
+ vec2 p1 = (v1.xy / v1.w)*0.5 + 0.5;
+
+ attribs.depth = -lin_depth / far;
+ attribs.normal = data.mtx_modelview3 * n;
+ attribs.velocity = p0 - p1;
+#if DEBUG_STEPS
+ attribs.roughness = 1.0;
+ attribs.metallic = 0.0;
+ attribs.albedo = vec3(0.0);
+ float ds = mod(float(steps), 2.0);
+ float dm2 = step(float(RAY_STEP) / 2.0, float(steps));
+ float dm = step(float(RAY_STEP-2), float(steps));
+
+ attribs.light = 6.0 * vec3(2.0*dm2,ds,4.0*dm);
+#else
+ dC(p, z, attribs);
+#endif
+
+ write_gbuffer(attribs);
+#endif
+}
+
+vec3 vdir;
+float view_z_scale = 0.0;
+
+void trace(vec3 p) {
+ float d = 0.0;
+ float r = 0.0;
+ float t = 0.0;
+ float z = data.view_z;
+
+ for (int i = 0; i < RAY_STEP; ++i) {
+ d = dF(p, z);
+ t = TEXEL_SIZE * z;
+ if (d <= t) {
+ write_point(p, i, t, z);
+ return;
+ }
+ d = max(d, t);
+ r += d;
+ if (r >= MAX_DEPTH) {
+ discard;
+ }
+ z += d * view_z_scale;
+ p += vdir * d;
+ }
+ discard;
+}
+
+void main(void) {
+#if SHADOW_MATERIAL
+ vec3 model_ray = -data.mtx_toray[2];
+#else
+ vec3 model_ray = data.mtx_toray * mv_vertex.xyz;
+#endif
+
+ vdir = normalize(model_ray);
+#if SHADOW_MATERIAL
+ vec3 p = data.vertex;
+#else
+ view_z_scale = -(data.mtx_modelview3 * vdir).z;
+ vec3 p = vertex;
+#endif
+ trace(p);
+}
+
+#endif // FRAGMENT_SHADER
No newline at end of file
A => liminal_legacy/liminal/assets/shaders/g/gshader_vars.glsl +18 -0
@@ 0,0 1,18 @@
+varying vec3 vertex; // original vertex as in bufer
+varying vec3 m_vertex; // model transformed vertex (world space)
+varying vec3 mv_vertex; // model view transformed vertex (view space)
+varying vec3 normal; // normal (world space)
+varying vec4 mvp_vertex; // model view projection transformed vertex (screen space)
+varying vec4 last_mvp_vertex; // current vertex as transformed by previous frame (screen space)
+varying vec3 vertex_color; // vertex color
+varying vec4 light_color; // light color
+
+layout(std140) uniform GMaterialAttribs {
+ float roughness_factor;// = 1.0;
+ float roughness_bias;// = 0.0;
+ float metallic_factor;// = 1.0;
+ vec3 albedo;// = vec3(1.0);
+ vec3 emissive_color;// = vec3(1.0);
+ float emissive;// = 0.0;
+};
+
A => liminal_legacy/liminal/assets/shaders/g/gshader_vert.glsl +65 -0
@@ 0,0 1,65 @@
+#ifndef USE_BILLBOARD
+#define USE_BILLBOARD 0
+#endif
+
+#ifndef USE_PARTICLES
+#define USE_PARTICLES 0
+#endif
+
+vec3 orthogonal(vec3 v)
+{
+ return abs(v.x) > abs(v.z) ? vec3(-v.y, v.x, 0.0)
+ : vec3(0.0, -v.z, v.y);
+}
+
+void write_vertex_vars() {
+ vec4 world_vertex;
+ vec4 view_vertex;
+
+ vec4 inp = vec4(in_Position.xyz,1.0);
+#if USE_PARTICLES
+ vec3 up = normalize(in_Velocity);
+ vec3 right = orthogonal(up);
+ mat3 mtx_rotate = mat3(right, cross(up,right), up);
+ inp.xyz = (mtx_rotate * inp.xyz) + in_Origin;
+ vec4 last_inp = inp;
+ last_inp.xyz += in_Velocity * (1.0/60.0);
+#else
+#define last_inp inp
+#endif
+
+#if USE_BILLBOARD
+ const vec4 center = vec4(0.0,0.0,0.0,1.0);
+ vec4 offset = vec4(inp.xyz, 1.0);
+
+ world_vertex = mtx_model * center;
+ view_vertex = mtx_view * world_vertex + offset;
+ mvp_vertex = mtx_proj * view_vertex;
+ last_mvp_vertex = mtx_last_mvp() * center + mtx_proj * offset;
+#else
+ world_vertex = mtx_model * inp;
+ view_vertex = mtx_view * world_vertex;
+ mvp_vertex = mtx_proj * view_vertex;
+ last_mvp_vertex = mtx_last_mvp() * last_inp;
+#endif
+
+ vertex = inp.xyz;
+ m_vertex = world_vertex.xyz;
+ mv_vertex = view_vertex.xyz;
+
+ gl_Position = mvp_vertex;
+ write_texcoord();
+ vertex_color = in_Color.rgb;
+ light_color = in_Color2;
+
+#ifdef USE_POM
+ calc_pom_parallax();
+#endif
+
+
+#if USE_PARTICLES
+ normal = mat3(mtx_model) * (mtx_rotate * in_Normal);
+#else
+ normal = mat3(mtx_model) * in_Normal;
+#endif
+}
No newline at end of file
A => liminal_legacy/liminal/assets/shaders/g/lpv/propagate.glsl +210 -0
@@ 0,0 1,210 @@
+#include "std/std.glsl"
+
+// simulate occlusion
+#define USE_LPV_OCCLUSION 1
+
+// simulate light bounces
+#define USE_LPV_BOUNCE 1
+
+// clamp negative influx
+#define USE_LPV_CLAMP_INFLUX 1
+
+noperspective varying vec2 window_xy;
+noperspective varying vec2 griduv;
+noperspective varying vec2 uv;
+uniform int igridsize;
+float gridsize = float(igridsize);
+
+#ifndef FIRST_BOUNCE
+#define FIRST_BOUNCE 0
+#endif
+
+#if VERTEX_SHADER
+
+void main(void) {
+ gl_Position = vec4(in_Position.xy, 0.0, 1.0);
+ window_xy = in_Position.xy;
+
+ uv = (window_xy * 0.5 + 0.5);
+ vec2 size = vec2(gridsize*gridsize, gridsize*3.0);
+ griduv = uv*size;
+}
+
+#elif FRAGMENT_SHADER
+
+out vec4 out_accum;
+
+uniform sampler2D lpv;
+#if !FIRST_BOUNCE
+uniform sampler2D accum;
+#endif
+uniform sampler2D vocc;
+uniform sampler2D vcolor;
+uniform float occlusion_factor = 1.0;
+uniform float bounce_factor = 1.0;
+
+int channel;
+
+vec4 sh_project(vec3 n) {
+ return vec4(
+ 0.282094791773878140,
+ -0.488602511902919920 * n.y,
+ 0.488602511902919920 * n.z,
+ -0.488602511902919920 * n.x);
+}
+
+vec4 lpv_read1(sampler2D s, ivec3 p) {
+ float pmn = float(min(p.x,min(p.y,p.z)));
+ float pmx = float(max(p.x,max(p.y,p.z)));
+ float f = step(0.0, pmn) *
+ step(pmx, gridsize-1.0);
+ return texelFetch(s,
+ ivec2(p.x+p.y*igridsize, p.z), 0) * f;
+}
+
+vec4 lpv_read3(sampler2D s, ivec3 p) {
+ float pmn = float(min(p.x,min(p.y,p.z)));
+ float pmx = float(max(p.x,max(p.y,p.z)));
+ float f = step(0.0, pmn) *
+ step(pmx, gridsize-1.0);
+ return texelFetch(s, ivec2(p.x+p.y*igridsize,
+ p.z+channel*igridsize), 0) * f;
+}
+
+void main() {
+ ivec2 iuv = ivec2(griduv);
+ ivec3 ipos = ivec3(
+ iuv.x % igridsize,
+ iuv.x / igridsize,
+ iuv.y % igridsize);
+ channel = iuv.y / igridsize;
+
+ vec4 shsumcoeffs = vec4(0.0);
+
+ #if USE_LPV_OCCLUSION || USE_LPV_BOUNCE
+ vec4 gv4[6];
+ vec4 gv[8];
+ #if USE_LPV_BOUNCE
+ vec4 bc4[6];
+ vec4 bc[8];
+ #endif // USE_LPV_BOUNCE
+ #endif // USE_LPV_OCCLUSION || USE_LPV_BOUNCE
+
+ #if USE_LPV_OCCLUSION || USE_LPV_BOUNCE
+ gv[0] = lpv_read1(vocc, ipos + ivec3(0,0,0));
+ gv[1] = lpv_read1(vocc, ipos + ivec3(0,0,1));
+ gv[2] = lpv_read1(vocc, ipos + ivec3(0,1,0));
+ gv[3] = lpv_read1(vocc, ipos + ivec3(0,1,1));
+ gv[4] = lpv_read1(vocc, ipos + ivec3(1,0,0));
+ gv[5] = lpv_read1(vocc, ipos + ivec3(1,0,1));
+ gv[6] = lpv_read1(vocc, ipos + ivec3(1,1,0));
+ gv[7] = lpv_read1(vocc, ipos + ivec3(1,1,1));
+
+ #if USE_LPV_BOUNCE
+ bc[0] = lpv_read1(vcolor, ipos + ivec3(0,0,0));
+ bc[1] = lpv_read1(vcolor, ipos + ivec3(0,0,1));
+ bc[2] = lpv_read1(vcolor, ipos + ivec3(0,1,0));
+ bc[3] = lpv_read1(vcolor, ipos + ivec3(0,1,1));
+ bc[4] = lpv_read1(vcolor, ipos + ivec3(1,0,0));
+ bc[5] = lpv_read1(vcolor, ipos + ivec3(1,0,1));
+ bc[6] = lpv_read1(vcolor, ipos + ivec3(1,1,0));
+ bc[7] = lpv_read1(vcolor, ipos + ivec3(1,1,1));
+ #endif
+
+ gv4[0] = (gv[0]+gv[1]+gv[2]+gv[3])*0.25;
+ gv4[1] = (gv[4]+gv[5]+gv[6]+gv[7])*0.25;
+ gv4[2] = (gv[0]+gv[4]+gv[1]+gv[5])*0.25;
+ gv4[3] = (gv[2]+gv[6]+gv[3]+gv[7])*0.25;
+ gv4[4] = (gv[0]+gv[2]+gv[4]+gv[6])*0.25;
+ gv4[5] = (gv[1]+gv[3]+gv[5]+gv[7])*0.25;
+
+ #if USE_LPV_BOUNCE
+ bc4[0] = (bc[0]+bc[1]+bc[2]+bc[3])*0.25;
+ bc4[1] = (bc[4]+bc[5]+bc[6]+bc[7])*0.25;
+ bc4[2] = (bc[0]+bc[4]+bc[1]+bc[5])*0.25;
+ bc4[3] = (bc[2]+bc[6]+bc[3]+bc[7])*0.25;
+ bc4[4] = (bc[0]+bc[2]+bc[4]+bc[6])*0.25;
+ bc4[5] = (bc[1]+bc[3]+bc[5]+bc[7])*0.25;
+ #endif
+
+ #endif // USE_LPV_OCCLUSION || USE_LPV_BOUNCE
+
+ for (int neighbor = 0; neighbor < 6; neighbor++) {
+ ivec3 offset = ivec3(0);
+ int dim = 2-(neighbor>>1);
+ offset[dim] = ((neighbor&1)<<1)-1;
+
+ vec4 shcoeffs = lpv_read3(lpv, ipos+offset);
+
+ #if USE_LPV_OCCLUSION
+ vec4 gvcoeffs = gv4[neighbor];
+ #endif
+
+ vec3 foffset = vec3(offset);
+
+ for (int face = 0; face < 6; ++face) {
+ if (face == neighbor) continue;
+
+ int face_dim = 2-(face>>1);
+
+ vec3 face_offset = vec3(0.0);
+ face_offset[face_dim] = float(((face&1)<<1)-1)*0.5;
+
+ vec3 dirw = normalize(face_offset - foffset);
+
+ //angle = (4.0*atan(sqrt(11.0)/33.0));
+ //angle = (-M_PI/3.0+2.0*atan(sqrt(11.0)*3.0/11.0));
+ float solid_angle = (dim == face_dim)?0.4006696846462392:0.4234313544367392;
+
+ #if USE_LPV_BOUNCE
+ vec4 gvrefcoeffs = gv4[face];
+ vec4 gvrefcolor = bc4[face];
+ #endif
+
+ vec4 outdirsh = sh_project(dirw);
+ vec4 indirsh = outdirsh;
+ vec4 invindirsh = sh_project(-dirw);
+
+ // how much flux has been received
+ float influx = dot(shcoeffs, indirsh) * solid_angle;
+
+ #if USE_LPV_CLAMP_INFLUX
+ influx = max(0.0,influx);
+ #endif
+
+ // how much flux will be occluded
+ #if USE_LPV_OCCLUSION
+ float occluded = clamp(occlusion_factor*dot(gvcoeffs, indirsh),0.0,1.0);
+ #else
+ float occluded = 0.0;
+ #endif
+
+ // how much flux will be passed on
+ float outflux = (1.0 - occluded);
+
+ // how much flux will be reflected
+ #if USE_LPV_BOUNCE
+ float reflected = outflux * clamp(bounce_factor*dot(gvrefcoeffs, invindirsh),0.0,1.0);
+ if (reflected > 0.0) {
+ float wflux = reflected * influx * gvrefcolor[channel];
+ shsumcoeffs += gvrefcoeffs * wflux;
+ }
+ #endif
+
+ shsumcoeffs += outdirsh * (influx * outflux);
+ }
+ }
+
+ // write back flux
+
+ out_Color = shsumcoeffs;
+
+#if !FIRST_BOUNCE
+ vec4 shaccum = texelFetch(accum, iuv, 0);
+ out_accum = shaccum + shsumcoeffs;
+#else
+ out_accum = shsumcoeffs;
+#endif
+}
+
+#endif
A => liminal_legacy/liminal/assets/shaders/g/ppfx/dof-mblur.glsl +232 -0
@@ 0,0 1,232 @@
+#include "std/std.glsl"
+
+#include "g/ppfx/view_ray.glsl"
+
+#ifndef NUM_SAMPLES
+#if VIDEO_QUALITY >= VQ_HIGHEST
+#define NUM_SAMPLES 64
+#elif VIDEO_QUALITY >= VQ_HIGHER
+#define NUM_SAMPLES 16
+#else
+#define NUM_SAMPLES 8
+#endif
+#endif
+
+#ifndef NOISE_DITHER
+#define NOISE_DITHER 1
+#endif
+
+#ifndef USE_DOF
+#define USE_DOF 1
+#endif
+
+#ifndef DOF_RING
+#define DOF_RING 0
+#endif
+
+#ifndef USE_MBLUR
+#define USE_MBLUR 1
+#endif
+
+#ifndef DEBUG_FOCUS
+#define DEBUG_FOCUS 0
+#endif
+
+#if FRAGMENT_SHADER
+
+#include "lib/math.glsl"
+
+uniform sampler2D fb_color0;
+uniform vec2 fb_size;
+uniform vec2 fb_texel;
+
+#if USE_MBLUR
+uniform isampler2D tex_velocity;
+uniform float blur_scale = 1.0;
+#if 1
+#define OFFSET_CENTER - 0.5
+#else
+#define OFFSET_CENTER
+#endif
+#endif
+
+#if USE_DOF
+uniform sampler2D depth_texture;
+
+uniform vec2 focalDepthRange = vec2(1.0, 100.0); // min/max focal distance in m
+uniform float focalDepth = 1.0; //focal distance value as depth normal (0.0-1.0)
+uniform float focalLength = 35.0; //22.3; //focal length in mm (human eye)
+// The f-number of the human eye varies from about f/8.3 in a very brightly lit place to about f/2.1 in the dark
+// the focal length of the eye is a bit longer, resulting in minimum f-number of f/3.2.
+uniform float fstop = 3.2; //f-stop value
+
+const float CoC = 0.03;//circle of confusion size in mm (35mm film = 0.03mm)
+
+vec3 debugFocus(vec3 col, float blur, float depth)
+{
+ float edge = 0.002*depth; //distance based edge smoothing
+ float m = clamp(smoothstep(0.0,edge,blur),0.0,1.0);
+ float e = clamp(smoothstep(1.0-edge,1.0,blur),0.0,1.0);
+
+ col = mix(col,vec3(1.0,0.5,0.0),(1.0-m)*0.6);
+ col = mix(col,vec3(0.0,0.5,1.0),((1.0-e)-(1.0-m))*0.2);
+
+ return col;
+}
+
+float linearize(float z) {
+ float zn = 2.0*z-1.0;
+ float zd = (zn * mtx_proj[3][3] - mtx_proj[3][2]) / (zn * mtx_proj[2][3] - mtx_proj[2][2]);
+ return -view_ray.z * (zd / far);
+}
+
+float read_linear_z(vec2 uv) {
+ return linearize(texture(depth_texture, uv).r);
+}
+
+#if DOF_RING
+vec2 offset_spiral(float x) {
+ float a = x * 2.0 * M_PI;
+ return vec2(a, 1.0);
+}
+#else
+vec2 offset_spiral(float x) {
+#if NUM_SAMPLES == 16
+ float a = x * (2.0 * M_PI * 0.3819444444 * 16.0 * 23.0);
+#elif NUM_SAMPLES == 8
+ float a = x * (2.0 * M_PI * 0.3819444444 * 8.0 * 23.0);
+#else
+ float a = x * (2.0 * M_PI * 0.3819444444 * 64.0 * 67.0);
+#endif
+ return vec2(a, sqrt(x));
+}
+#endif
+
+#endif
+
+vec3 color(vec2 coords) //processing the sample
+{
+ return texture(fb_color0,coords).rgb;
+}
+
+vec2 read_velocity() {
+ return vec2(texture(tex_velocity, screen_texcoord).rg) / 127.0;
+}
+
+vec3 calc_dof_mblur()
+{
+#if USE_DOF
+ //scene depth calculation
+ float depth = read_linear_z(screen_texcoord);
+
+ //focal plane calculation
+
+ float fDepth = clamp(linearize(focalDepth), focalDepthRange.x, focalDepthRange.y);
+
+ //dof blur factor calculation
+
+ float blur = 0.0;
+ {
+ float f = focalLength; //focal length in mm
+ float d = fDepth*1000.0; //focal plane in mm
+ float o = depth*1000.0; //depth in mm
+
+ float a = (o*f)/(o-f);
+ float b = (d*f)/(d-f);
+ float c = (d-f)/(d*fstop*CoC);
+
+ blur = (abs(a-b)*c)/1000.0;
+ }
+
+ // calculation of pattern for ditering
+
+ // getting blur x and y step factor
+
+ vec2 stepf = vec2(blur, blur*fb_size.x/fb_size.y);
+ vec2 pcofs;
+ vec2 po;
+#endif
+
+#if USE_MBLUR
+ vec2 velocity = read_velocity() * blur_scale;
+ vec2 vo;
+#endif
+
+ // calculation of final color
+
+ vec3 col = vec3(0.0);
+
+ float d = 1.0 / float(NUM_SAMPLES);
+#if NOISE_DITHER
+ float o = d*random(vec3(screen_texcoord,mod(time,1.0)));
+#else
+ float o = 0.0;
+#endif
+ float r = 0.0;
+
+#if USE_MBLUR && USE_DOF
+ float w, b0, b1;
+ b0 = min(1.0, length(velocity)*fb_size.y*0.25);
+ b1 = 1.0 - b0;
+#define READ_SAMPLE() \
+ pcofs = offset_spiral(o); \
+ po = vec2(cos(pcofs.x),sin(pcofs.x))*pcofs.y; \
+ w = pcofs.y * b1 + b0; \
+ r += w; \
+ vo = velocity * (o OFFSET_CENTER); \
+ o += d; \
+ col += color(screen_texcoord + po*stepf + vo)*w;
+#elif USE_MBLUR
+#define READ_SAMPLE() \
+ r += 1.0; \
+ vo = velocity * (o OFFSET_CENTER); \
+ o += d; \
+ col += color(screen_texcoord + vo);
+#elif USE_DOF
+#define READ_SAMPLE() \
+ pcofs = offset_spiral(o); \
+ po = vec2(cos(pcofs.x),sin(pcofs.x))*pcofs.y; \
+ r += pcofs.y; \
+ o += d; \
+ col += color(screen_texcoord + po*stepf)*pcofs.y;
+#endif
+
+#if NUM_SAMPLES > 16
+ for (int i = 0; i < NUM_SAMPLES; ++i) {
+ READ_SAMPLE();
+ }
+#else
+ READ_SAMPLE();
+ READ_SAMPLE();
+ READ_SAMPLE();
+ READ_SAMPLE();
+ READ_SAMPLE();
+ READ_SAMPLE();
+ READ_SAMPLE();
+ READ_SAMPLE();
+#if NUM_SAMPLES > 8
+ READ_SAMPLE();
+ READ_SAMPLE();
+ READ_SAMPLE();
+ READ_SAMPLE();
+ READ_SAMPLE();
+ READ_SAMPLE();
+ READ_SAMPLE();
+ READ_SAMPLE();
+#endif
+#endif
+
+ col *= 1.0/r; //divide by sample count
+
+#if USE_DOF && DEBUG_FOCUS
+ col = debugFocus(col, blur, depth);
+#endif
+
+ return col;
+}
+
+void main() {
+ out_Color = vec4(calc_dof_mblur(), 1.0);
+}
+
+#endif
No newline at end of file
A => liminal_legacy/liminal/assets/shaders/g/ppfx/gshader.glsl +167 -0
@@ 0,0 1,167 @@
+#include "std/std.glsl"
+#include "view_ray.glsl"
+
+#if FRAGMENT_SHADER
+
+#define M_PI 3.141592653589793
+
+#include "lib/fog.glsl"
+#include "lib/hslhsv.glsl"
+
+#include "gshader_frag.glsl"
+
+#ifndef USE_SSAO
+#define USE_SSAO 0
+#endif
+
+#ifndef DEBUG_SSAO
+#define DEBUG_SSAO 0
+#endif
+
+#ifndef USE_FOG
+#define USE_FOG 0
+#endif
+
+#ifndef USE_IBL
+#define USE_IBL 0
+#endif
+
+#if USE_IBL
+uniform samplerCube ibl_cubemap;
+#endif
+
+#if USE_SSAO
+uniform sampler2D ssao_color0;
+#endif
+
+vec2 uv;
+ivec2 coord;
+GBufferAttributes gba;
+
+uniform vec3 ambient_color = vec3(1.0);
+uniform float ambient_power = 1.0;
+
+const vec3 sky_albedo = vec3(1.0, 1.0, 1.0);
+
+// normal of pixel
+vec3 surface_normal;
+
+vec3 cubemap_vector(in vec3 v) {
+ return vec3(v.xz, -v.y);
+}
+
+#define MAX_LOD 7.0
+
+float view_ray_length = length(view_ray);
+vec3 view_dir = view_ray / view_ray_length;
+
+vec3 world_dir = normalize(world_ray);
+
+#if USE_IBL
+vec3 shade_ibl_cubemap() {
+ return textureLod(ibl_cubemap, cubemap_vector(world_dir), 0.0).rgb * ambient_power;
+}
+
+vec3 brdf_sample_ibl_diffuse(in samplerCube cubemap, in vec3 normal) {
+ return textureLod(cubemap, cubemap_vector(normal), MAX_LOD).rgb;
+}
+
+vec3 brdf_sample_ibl_specular(in samplerCube cubemap, float roughness, in vec3 reflect) {
+ // LOD 10 ~= specular roughness 0.8
+ float a = roughness;
+ a = a / 0.8;
+
+ // specular IBL
+ float lod = sqrt(a);
+
+ lod = max(lod*MAX_LOD, 0.0);
+
+ return textureLod(cubemap, cubemap_vector(reflect), lod).rgb;
+}
+
+#include "lib/ibl_sample.glsl"
+#endif
+
+vec3 shade_surface() {
+ if (gba.sky == 1.0) {
+#if USE_IBL
+ return shade_ibl_cubemap();
+#else
+ return ambient_color * ambient_power;
+#endif
+ }
+ float roughness = gba.roughness;
+ float metallic = gba.metallic;
+#if USE_SSAO
+ // ambient occlusion term
+ float ssao_power = texture(ssao_color0, uv).r;
+#else
+ float ssao_power = 1.0;
+#endif
+ ssao_power *= gba.ao;
+#if !USE_IBL
+ return gba.albedo * ambient_color * (ambient_power * ssao_power / M_PI);
+#else
+ vec3 ibl_diff;
+ vec3 ibl_spec;
+ vec3 n = normalize(mat3(mtx_camera) * gba.normal);
+#if VIDEO_QUALITY >= VQ_LOWER
+ // setup specular+diffuse IBL
+ ibla = IBLAttributes(
+ gba.metallic,
+ gba.roughness,
+ gba.albedo,
+ -world_dir,
+ n
+ );
+ // diffuse IBL
+ ibl_diff = vec3(0.0);
+ ibl_spec = ibl_sample_specular_diffuse(ibl_cubemap, ibl_diff);
+#else
+ // diffuse IBL
+ ibl_diff = brdf_sample_ibl_diffuse(ibl_cubemap, n) / M_PI;
+ ibl_spec = vec3(0.0);
+#endif
+
+ vec3 color = ibl_diff * (1.0 - metallic) + ibl_spec / mix(M_PI, 1.0, gba.metallic);
+ color *= (ambient_color * ambient_power);
+#if USE_SSAO
+ color *= ssao_power;
+#endif
+ return color;
+#endif
+}
+
+uniform float rainbow_offset = 0.4;
+
+void main() {
+ uv = screen_texcoord;
+ coord = gbuffer_uv_to_texel(uv);
+ gba = read_gbuffer_DANRMSL(uv, coord);
+
+ vec3 color;
+#if USE_SSAO && DEBUG_SSAO
+ if (gba.sky == 1.0) {
+ color = shade_ibl_cubemap();
+ } else {
+ // ambient occlusion term
+ color = vec3(texture(ssao_color0, uv).r) * 0.5;
+ }
+#else
+ color = shade_surface();
+
+ // add light map
+ color = color * (1.0 - gba.light.a) + gba.light.rgb;
+
+#if USE_FOG
+ // add fog
+ float fog_intensity = get_fog_intensity(vec3(0.0), mtx_camera[3].xyz,
+ world_dir, view_ray_length * gba.depth);
+ color = mix_fog(color, fog_intensity, world_dir);
+#endif
+#endif
+
+ out_Color = vec4(color, 1.0);
+}
+
+#endif
No newline at end of file
A => liminal_legacy/liminal/assets/shaders/g/ppfx/gshader_frag.glsl +99 -0
@@ 0,0 1,99 @@
+
+#ifndef USE_VIEW_NORMAL
+#define USE_VIEW_NORMAL 1
+#endif
+
+uniform sampler2D fb_color0; // albedo, uvlight
+uniform sampler2D fb_color1; // matid
+uniform sampler2D fb_color2; // normal
+uniform sampler2D fb_color3; // light
+//uniform isampler2D fb_color4; // velocity
+uniform vec2 fb_texel;
+uniform vec2 fb_size;
+
+#include "lib/normal_codec.glsl"
+#define GBA_USE_DEPTH 1
+#include "g/gbuffer_attr.glsl"
+
+uniform sampler2D fb_depth; // depth
+
+float linear_z(float z) {
+ float zn = 2.0*z-1.0;
+ float zd = (zn * mtx_proj[3][3] - mtx_proj[3][2]) / (zn * mtx_proj[2][3] - mtx_proj[2][2]);
+ return zd / far;
+}
+
+float fetch_linear_z(ivec2 coord) {
+ return linear_z(texelFetch(fb_depth, coord, 0).r);
+}
+
+float read_linear_z(vec2 uv) {
+ return linear_z(texture(fb_depth, uv).r);
+}
+
+vec3 view_normal(vec3 normal) {
+#if USE_VIEW_NORMAL
+ return mat3(mtx_view) * normal;
+#else
+ return normal;
+#endif
+}
+
+ivec2 gbuffer_uv_to_texel(vec2 uv) {
+ return ivec2(uv * fb_size);
+}
+
+GBufferAttributes read_gbuffer_DANRMS(vec2 uv, ivec2 coord) {
+ vec4 color = texelFetch(fb_color0, coord, 0);
+ vec2 fx = texelFetch(fb_color1, coord, 0).rg;
+ vec2 w_norm = texelFetch(fb_color2, coord, 0).rg;
+ float depth = fetch_linear_z(coord);
+
+ return GBufferAttributes(
+ depth,
+ color.rgb, // albedo
+ color.a, // ao
+ vec2(0.0), // velocity, usually not required
+ view_normal(decode_normal(w_norm.rg)), // normal
+ fx.r, fx.g, // roughness, metallic,
+ step(1.0, depth), // sky,
+ vec4(0.0) // light
+ );
+}
+
+GBufferAttributes read_gbuffer_DANRMSL(vec2 uv, ivec2 coord) {
+ vec4 color = texelFetch(fb_color0, coord, 0);
+ vec2 fx = texelFetch(fb_color1, coord, 0).rg;
+ vec2 w_norm = texelFetch(fb_color2, coord, 0).rg;
+ float depth = fetch_linear_z(coord);
+ vec4 light = texelFetch(fb_color3, coord, 0);
+
+ return GBufferAttributes(
+ depth,
+ color.rgb, // albedo
+ color.a, // ao
+ vec2(0.0), // velocity, usually not required
+ view_normal(decode_normal(w_norm.rg)), // normal
+ fx.r, fx.g, // roughness, metallic,
+ step(1.0, depth), // sky,
+ light // light
+ );
+}
+
+float read_gbuffer_DN(in ivec2 coord, out vec3 normal) {
+ vec2 w_norm = texelFetch(fb_color2, coord, 0).rg;
+ normal = view_normal(decode_normal(w_norm.rg));
+ return fetch_linear_z(coord);
+}
+
+float fetch_gbuffer_D(ivec2 coord) {
+ return fetch_linear_z(coord);
+}
+
+float read_gbuffer_D(vec2 uv) {
+ return read_linear_z(uv);
+}
+
+vec3 read_gbuffer_N(ivec2 coord) {
+ return view_normal(decode_normal(texelFetch(fb_color2, coord, 0).rg));
+}
A => liminal_legacy/liminal/assets/shaders/g/ppfx/light_direct.glsl +308 -0
@@ 0,0 1,308 @@
+#include "std/std.glsl"
+
+#define VIEW_RAY_MAIN view_ray_main
+#include "view_ray.glsl"
+
+// light dir in view space
+flat varying vec3 light_dir_vs;
+
+#if VERTEX_SHADER
+
+uniform vec3 light_dir;
+
+void main(void) {
+ view_ray_main();
+ light_dir_vs = normalize(mat3(mtx_view) * light_dir);
+}
+
+#elif FRAGMENT_SHADER
+
+#include "gshader_frag.glsl"
+#include "lib/brdf.glsl"
+#include "lib/evsm.glsl"
+
+#if VIDEO_QUALITY < VQ_LOW
+#undef USE_VLS
+#define USE_VLS 0
+#endif
+
+#if USE_VLS
+#if VIDEO_QUALITY >= VQ_HIGHER
+#define VLS_STEPS 16
+#elif VIDEO_QUALITY >= VQ_MEDIUM
+#define VLS_STEPS 8
+#else
+#define VLS_STEPS 4
+#endif
+#define ABORT return
+const float vls_factor = 1.0/1.0;
+const float vls_density = 1.0/250.0;
+#else
+#define ABORT discard
+#endif
+
+#if EVSM_ENABLE
+#define TEXTURE_EVSM texture_evsm
+#else // EVSM_ENABLE
+#define TEXTURE_EVSM texture_vsm
+#endif // EVSM_ENABLE
+
+#ifndef USE_SSAO
+#define USE_SSAO 0
+#endif
+
+#ifndef USE_AMBIENT
+#define USE_AMBIENT 0
+#endif
+
+#if DEBUG_CASCADE
+#define LIGHT_COLOR vec3(1.0)
+#define ALBEDO_COLOR vec3(1.0)
+#define SHADOW_TYPE vec3
+#else
+#define SHADOW_TYPE float
+#define LIGHT_COLOR light_color
+#define ALBEDO_COLOR gba.albedo
+#endif
+
+#if USE_SHADOW
+#define SHADOW_FACTOR(X) lit * (X)
+#else
+#define SHADOW_FACTOR(X) X
+#endif
+
+uniform sampler2DArray dm_color0;
+uniform mat4 dm_projview_mtx[LAYER_COUNT];
+uniform vec3 dm_split_coeffs;
+uniform float dm_rolloff = 1.0;
+uniform vec3 light_color = vec3(1.0);
+uniform float light_power = 1.0;
+uniform vec3 light_pos;
+uniform vec2 dm_range;
+
+#if USE_SSAO
+uniform sampler2D ssao_color0;
+#endif
+
+/*
+float texture_vsm(in sampler2DArray smp, in vec4 uv) {
+ float depth = uv.z;
+ vec3 pos_dx = dFdx(uv.xyz);
+ vec3 pos_dy = dFdy(uv.xyz);
+
+ vec2 moments = textureGrad(smp, uv.xyw, pos_dx.xy, pos_dy.xy).rg;
+
+ return chebyshev_upper_bound(moments, depth, min_variance, light_bleeding_bias);
+}
+*/
+
+float texture_evsm(in sampler2DArray smp, in vec4 uv) {
+ float depth = uv.z;
+ vec3 pos_dx = dFdx(uv.xyz);
+ vec3 pos_dy = dFdy(uv.xyz);
+
+ vec2 warped_depth = warp_depth(depth, exponents);
+
+ vec4 moments = textureGrad(smp, uv.xyw, pos_dx.xy, pos_dy.xy);
+
+ // Derivative of warping at depth
+ vec2 depthScale = min_variance * exponents * warped_depth;
+ vec2 minVariance = depthScale * depthScale;
+
+#if EVSM4
+ float posContrib = chebyshev_upper_bound(moments.xz, warped_depth.x, minVariance.x, light_bleeding_bias);
+ float negContrib = chebyshev_upper_bound(moments.yw, warped_depth.y, minVariance.y, light_bleeding_bias);
+ return min(posContrib, negContrib);
+#else
+ // Positive only
+ return chebyshev_upper_bound(moments.xy, warped_depth.x, minVariance.x, light_bleeding_bias);
+#endif
+}
+
+float sample_shadow_layer_hard(in float layer, in vec3 p) {
+ int i = int(layer);
+ vec4 uv = dm_projview_mtx[i] * vec4(p, 1.0);
+ float d = warp_depth1(uv.z, exponents);
+ float z = texture(dm_color0, vec3(uv.xy, layer)).r;
+ return step(d, z);
+ //return clamp((z - d)*1000.0, 0.0, 1.0);
+}
+
+SHADOW_TYPE sample_shadow_layer(in float layer, in vec3 p) {
+ int i = int(layer);
+ vec4 uv = dm_projview_mtx[i] * vec4(p, 1.0);
+
+#if DEBUG_CASCADE
+ float depth = TEXTURE_EVSM(dm_color0, vec4(uv.xyz, layer));
+ vec3 layer_color;
+ switch(i % 6) {
+ case 0: layer_color = vec3(1.0, 0.2, 0.2); break;
+ case 1: layer_color = vec3(1.0, 1.0, 0.2); break;
+ case 2: layer_color = vec3(0.2, 1.0, 0.2); break;
+ case 3: layer_color = vec3(0.2, 1.0, 1.0); break;
+ case 4: layer_color = vec3(0.2, 0.2, 1.0); break;
+ case 5: layer_color = vec3(1.0, 0.2, 1.0); break;
+ default: layer_color = vec3(1.0, 1.0, 1.0); break;
+ }
+ return layer_color * mix(0.1, 1.0, depth);
+#else
+ return TEXTURE_EVSM(dm_color0, vec4(uv.xyz, layer));
+#endif
+}
+
+SHADOW_TYPE sample_shadow_cascade(in vec3 p, in float z) {
+ float l = log2(z * dm_split_coeffs.x + dm_split_coeffs.y) * dm_split_coeffs.z;
+ float l0 = floor(l);
+
+ float w = max(0.0, (l - l0 + dm_rolloff - 1.0) / dm_rolloff);
+ SHADOW_TYPE lit = sample_shadow_layer(l0, p);
+ SHADOW_TYPE lit2 = sample_shadow_layer(l0+1.0, p);
+ if (w > 0.0) {
+ lit = mix(lit, lit2, w);
+ }
+ return lit;
+}
+
+float sample_shadow_cascade_hard(in vec3 p, in float z) {
+ float l = log2(z * dm_split_coeffs.x + dm_split_coeffs.y) * dm_split_coeffs.z;
+ return sample_shadow_layer_hard(floor(l), p);
+}
+
+float rand(vec3 co){
+ return fract(sin(dot(co.xyz,vec3(12.9898,78.233,91.1743))) * 43758.5453);
+}
+
+float light_falloff(float d, float radius) {
+ float dmlr = d / radius;
+ float dmlr2 = dmlr*dmlr;
+ float falloff_n = clamp(1.0 - dmlr2*dmlr2, 0.0, 1.0);
+ return ((falloff_n*falloff_n) / (d*d+1.0));
+}
+
+#if USE_VLS
+vec4 gather_scatter_volume(float depth, vec2 uv) {
+ float scatter = 0.0;
+ float dlin = min(0.5, depth); //0.5;
+ float vls_z = -view_ray.z * dlin;// * depth;
+ vec3 vls_ray = world_ray * dlin;// * depth;
+ float max_z = -view_ray.z * depth;
+
+ vec3 o = mtx_camera[3].xyz;
+
+ float t_ofs = mod(time,1.0);
+ float vls_depth, z, d, w;
+ vec3 p;
+ float div_steps = 1.0 / float(VLS_STEPS);
+ float d_ofs = abs(rand(vec3(uv.xy, t_ofs))) * div_steps;
+
+#define GATHER_RAY(i) \
+ vls_depth = d_ofs + i * div_steps; \
+ p = o + vls_ray * vls_depth; \
+ z = vls_z * vls_depth; \
+ d = z * vls_density; \
+ scatter += sample_shadow_cascade_hard(p, z) \
+ * light_falloff(length(p - light_pos) / dm_range.y, 1.0);
+
+ GATHER_RAY(0.0);
+ GATHER_RAY(1.0);
+ GATHER_RAY(2.0);
+ GATHER_RAY(3.0);
+#if VLS_STEPS > 4
+ GATHER_RAY(4.0);
+ GATHER_RAY(5.0);
+ GATHER_RAY(6.0);
+ GATHER_RAY(7.0);
+#if VLS_STEPS > 8
+ GATHER_RAY(8.0);
+ GATHER_RAY(9.0);
+ GATHER_RAY(10.0);
+ GATHER_RAY(11.0);
+ GATHER_RAY(12.0);
+ GATHER_RAY(13.0);
+ GATHER_RAY(14.0);
+ GATHER_RAY(15.0);
+#endif
+#endif
+ d = vls_z * vls_density;
+ scatter *= vls_factor * div_steps * (1.0 - exp(-d*d));
+ return scatter * vec4(LIGHT_COLOR, 1.0);
+}
+#endif
+
+void main() {
+ vec2 uv = screen_texcoord;
+ vec3 view_dir = normalize(view_ray);
+
+ ivec2 coord = gbuffer_uv_to_texel(uv);
+
+ // retrieve pixel data
+ GBufferAttributes gba = read_gbuffer_DANRMS(uv, coord);
+
+#if USE_VLS
+ out_Color = gather_scatter_volume(gba.depth, uv);
+#endif
+
+ vec3 p = mtx_camera[3].xyz + world_ray * gba.depth;
+ float z = -view_ray.z * gba.depth;
+#if USE_SHADOW
+ SHADOW_TYPE lit = sample_shadow_cascade(p, z);
+#else
+ SHADOW_TYPE lit = 1.0;
+#endif
+
+ if (gba.sky == 1.0)
+ ABORT;
+
+ float cos_Ol = dot(gba.normal,light_dir_vs);
+
+#if USE_AMBIENT
+ float ambientf = (1.0 - gba.metallic) * mix(0.01, 0.1, max(0.0,-cos_Ol));
+#if USE_SSAO
+ float ssao_factor = texture(ssao_color0, uv).r;
+#if USE_SHADOW
+ ambientf *= ssao_factor;
+#endif
+#endif
+
+ // ambient bounce
+ vec3 color = ALBEDO_COLOR * ambientf;
+#else // USE_AMBIENT
+ vec3 color = vec3(0.0);
+#endif
+
+ if (cos_Ol > 0.0) {
+ cos_Ol = max(0.0, cos_Ol);
+#if USE_SHADOW && !DEBUG_CASCADE
+ if (lit > 0.0) {
+#endif // USE_SHADOW
+
+ brdfa = BRDFAttributes(
+ gba.metallic,
+ gba.roughness,
+ ALBEDO_COLOR,
+ cos_Ol,
+ light_dir_vs,
+ -view_dir,
+ gba.normal
+ );
+
+ color += brdf_evaluate() * (SHADOW_FACTOR(cos_Ol));
+#if USE_SHADOW && !DEBUG_CASCADE
+ }
+#endif
+ }
+
+#if USE_SSAO && !USE_SHADOW
+ color *= LIGHT_COLOR * light_power * ssao_factor;
+#else
+ color *= LIGHT_COLOR * light_power;
+#endif
+
+#if USE_VLS
+ out_Color = out_Color + vec4(color*(1.0 - out_Color.a), 0.0);
+#else
+ out_Color = vec4(color, 0.0);
+#endif
+}
+
+#endif
No newline at end of file
A => liminal_legacy/liminal/assets/shaders/g/ppfx/light_proj.glsl +188 -0
@@ 0,0 1,188 @@
+#include "std/std.glsl"
+
+#ifndef USE_SPOT
+#define USE_SPOT 0
+#endif
+
+#ifndef USE_ATTENUATION
+#define USE_ATTENUATION 1
+#endif
+
+#ifndef USE_BRDF
+#define USE_BRDF 1
+#endif
+
+#ifndef DEBUG_LIGHT
+#define DEBUG_LIGHT 1
+#endif
+
+#ifndef USE_TEXTURE
+#define USE_TEXTURE 0
+#endif
+
+uniform float light_radius = 1.0;
+#if USE_SPOT
+uniform mat4 dm_pv_mtx;
+uniform mat4 dm_pv_inv_mtx;
+#endif
+
+// light position in view space
+flat varying vec3 light_position_vs;
+noperspective varying vec2 window_xy;
+noperspective varying vec2 screen_texcoord;
+
+#if VERTEX_SHADER
+
+uniform vec3 light_position;
+
+void main(void) {
+ vec4 position = in_Position;
+#if USE_SPOT
+ position.z *= -1.0;
+ position = (dm_pv_inv_mtx * position);
+ position = position / position.w;
+ gl_Position = mtx_proj * (mtx_view * position);
+#else
+ position.xyz = position.xyz * light_radius;
+ gl_Position = mtx_proj * (mtx_view * (mtx_model * position));
+#endif
+ light_position_vs = (mtx_view * vec4(light_position,1.0)).xyz;
+
+ window_xy = gl_Position.xy / gl_Position.w;
+ vec2 absolute_texcoord = window_xy * 0.5 + 0.5;
+ screen_texcoord = absolute_texcoord * viewport_scale + viewport_offset;
+}
+
+#elif FRAGMENT_SHADER
+
+#define M_PI 3.141592653589793
+
+#include "gshader_frag.glsl"
+#if USE_BRDF
+#include "lib/brdf.glsl"
+#endif
+
+#if USE_TEXTURE
+#if USE_CUBEMAP
+uniform samplerCube light_texture;
+#else
+uniform sampler2D light_texture;
+#endif // USE_CUBEMAP
+#endif // USE_TEXTURE
+uniform vec3 light_color = vec3(1.0);
+uniform float light_power = 1.0;
+
+float light_falloff(float d, float radius) {
+ float dmlr = d / radius;
+ float dmlr2 = dmlr*dmlr;
+ float falloff_n = clamp(1.0 - dmlr2*dmlr2, 0.0, 1.0);
+ return ((falloff_n*falloff_n) / (d*d+1.0));
+}
+
+/*
+// L = vector from surface position to light center
+// n = normal
+// radius = radius of light sphere
+vec3 sphere_light_l(vec3 L, vec3 v, vec3 n, float radius) {
+ vec3 r = reflect(v, n);
+ vec3 ray_center = L - dot(L, r)*r;
+ vec3 closest = L + ray_center * clamp(radius / length(ray_center), 0.0, 1.0);
+ return normalize(closest);
+}
+*/
+
+void main() {
+ vec2 uv = screen_texcoord;
+ ivec2 coord = gbuffer_uv_to_texel(uv);
+
+ // retrieve pixel data
+ GBufferAttributes gba = read_gbuffer_DANRMS(uv, coord);
+ //if (gba.sky == 1.0) {
+ // discard;
+ //}
+
+ vec4 far_pos = mtx_inv_proj * vec4(window_xy, 1.0, 1.0);
+ far_pos /= far_pos.w;
+ vec3 view_ray = far_pos.xyz;
+
+ vec3 surface_pos = view_ray * gba.depth;
+
+ vec3 n = gba.normal;
+ vec3 v = -normalize(view_ray);
+ vec3 p = light_position_vs - surface_pos;
+ vec3 l = normalize(p);
+
+ float lit = light_power;
+
+#if USE_SPOT
+ vec4 world_pos = mtx_camera * vec4(surface_pos, 1.0);
+ vec4 spot_proj = (dm_pv_mtx * world_pos);
+ vec3 spot_uv = (spot_proj.xyz / spot_proj.w);
+ vec3 spot_range = abs(spot_uv);
+#define LIGHT_COLOR_OP(X) (spot_color*X)
+#if USE_TEXTURE
+ vec3 spot_color = textureLod(light_texture, spot_uv.xy*0.5+0.5, 0.0).rgb;
+#else
+ float cone_range = length(spot_range.xy);
+ vec3 spot_color = vec3(clamp((cone_range - 1.0) / (0.9 - 1.0), 0.0, 1.0));
+#endif
+#if DEBUG_LIGHT
+ lit *= step(max(max(spot_range.x,spot_range.y),spot_range.z),1.0);
+#endif
+#else // !USE_SPOT
+#if USE_TEXTURE
+#define LIGHT_COLOR_OP(X) (point_color*X)
+ vec3 wl = mat3(mtx_model) * (mat3(mtx_camera) * l);
+#if USE_CUBEMAP
+ vec3 point_color = textureLod(light_texture, wl, 0.0).rgb;
+#else
+ vec3 point_color = textureLod(light_texture, abs(wl*0.5 + 0.5).xy, 0.0).rgb;
+#endif // USE_CUBEMAP
+#else
+#define LIGHT_COLOR_OP(X) X
+#endif // USE_TEXTURE
+#endif // !USE_SPOT
+
+#if USE_ATTENUATION
+ float distance = length(p);
+ lit *= light_falloff(distance, light_radius);
+#endif
+
+#if !DEBUG_LIGHT
+ if (lit == 0.0)
+ discard;
+#endif
+
+#if USE_BRDF
+
+ float cos_Ol = max(dot(n,l),0.0);
+
+ brdfa = BRDFAttributes(
+ gba.metallic,
+ gba.roughness,
+ gba.albedo,
+ cos_Ol,
+ l,
+ v,
+ n
+ );
+
+ lit *= cos_Ol;
+ out_Color.rgba = vec4((LIGHT_COLOR_OP(light_color) * lit) * brdf_evaluate(),0.0);
+
+#else // !USE_BRDF
+
+ out_Color.rgba = vec4((LIGHT_COLOR_OP(light_color) * lit),0.0);
+
+#endif // USE_BRDF
+
+#if DEBUG_LIGHT
+#if USE_SPOT
+ out_Color += vec4(1.0,0.0,0.0,0.0);
+#else
+ out_Color += vec4(0.0,0.0,1.0,0.0);
+#endif
+#endif
+}
+
+#endif
No newline at end of file
A => liminal_legacy/liminal/assets/shaders/g/ppfx/light_shgrid.glsl +238 -0
@@ 0,0 1,238 @@
+#include "std/std.glsl"
+
+#define USE_VIEW_NORMAL 0
+#define USE_VIEW_RAY_MODEL 1
+#define VIEW_RAY_MAIN view_ray_main
+#include "view_ray.glsl"
+
+#ifndef USE_SSAO
+#define USE_SSAO 0
+#endif
+
+#ifndef DEBUG_GRID
+#define DEBUG_GRID 0
+#endif
+
+#if VERTEX_SHADER
+
+void main(void) {
+ view_ray_main();
+}
+
+#elif FRAGMENT_SHADER
+
+#include "gshader_frag.glsl"
+#include "lib/sh.glsl"
+#include "lib/math.glsl"
+
+//uniform sampler3D occluders;
+uniform sampler2D shgrid;
+uniform int igridsize;
+
+#if USE_SSAO
+uniform sampler2D ssao_color0;
+#endif
+
+uniform vec3 light_color = vec3(1.0);
+uniform float light_power = 1.0;
+
+uniform float test_bias = 0.5;
+float gridsize = float(igridsize);
+float invgridsize = 1.0/gridsize;
+float halfinvgridsize = invgridsize*0.5;
+
+vec3 cubemap_vector(in vec3 v) {
+ return vec3(v.xz, -v.y);
+}
+
+vec4 texture_fetch_clamp(sampler2D s, ivec3 p, int channel) {
+ float pmn = float(min(p.x,min(p.y,p.z)));
+ float pmx = float(max(p.x,max(p.y,p.z)));
+
+ float f = step(0.0, pmn) *
+ step(pmx, gridsize-1.0);
+ return texelFetch(s, ivec2(p.x+p.y*igridsize,
+ p.z+channel*igridsize), 0) * f;
+
+}
+
+float shade_probe(vec4 sh, vec4 shn) {
+ return sh_shade(sh_irradiance_probe(sh), shn);
+}
+
+float texture_fetch_2d_as_3d(sampler2D s, ivec3 ip,
+ vec3 mp, vec4 shn, int c) {
+
+ ivec2 o = ivec2(0, 1);
+ float sh[8];
+ sh[0] = shade_probe(texture_fetch_clamp(shgrid, ip + o.xxx, c), shn);
+ sh[1] = shade_probe(texture_fetch_clamp(shgrid, ip + o.yxx, c), shn);
+ sh[2] = shade_probe(texture_fetch_clamp(shgrid, ip + o.xyx, c), shn);
+ sh[3] = shade_probe(texture_fetch_clamp(shgrid, ip + o.yyx, c), shn);
+ sh[4] = shade_probe(texture_fetch_clamp(shgrid, ip + o.xxy, c), shn);
+ sh[5] = shade_probe(texture_fetch_clamp(shgrid, ip + o.yxy, c), shn);
+ sh[6] = shade_probe(texture_fetch_clamp(shgrid, ip + o.xyy, c), shn);
+ sh[7] = shade_probe(texture_fetch_clamp(shgrid, ip + o.yyy, c), shn);
+
+ return mix(
+ mix(mix(sh[0],sh[1],mp.x),
+ mix(sh[2],sh[3],mp.x),mp.y),
+ mix(mix(sh[4],sh[5],mp.x),
+ mix(sh[6],sh[7],mp.x),mp.y),
+ mp.z);
+}
+
+vec3 shade_grid(sampler2D shgrid, vec3 p, vec4 shn) {
+ p *= gridsize;
+ p -= 0.5;
+ ivec3 ip = ivec3(p);
+ p -= floor(p);
+
+ p = smoothstep(0.0, 1.0, p);
+
+ return vec3(
+ texture_fetch_2d_as_3d(shgrid, ip, p, shn, 0),
+ texture_fetch_2d_as_3d(shgrid, ip, p, shn, 1),
+ texture_fetch_2d_as_3d(shgrid, ip, p, shn, 2));
+}
+
+#if DEBUG_GRID
+
+float map_sphere_grid(vec3 p, float d) {
+ float radius = d / 8.0;
+ p = mod(p + 0.5*d, d) - 0.5*d;
+ return length(p) - radius;
+}
+
+float map(vec3 p) {
+ return map_sphere_grid(p, 10.0/64.0);
+}
+
+vec3 map_gradient(vec3 p) {
+ vec2 d = vec2(1e-5, 0.0);
+ return normalize(vec3(
+ map(p + d.xyy) - map(p - d.xyy),
+ map(p + d.yxy) - map(p - d.yxy),
+ map(p + d.yyx) - map(p - d.yyx)));
+}
+
+float trace_sphere_grid(vec3 ro, vec3 rd, float maxt) {
+ float h = 0.0;
+ float t = 0.0;
+ for (int i=0; i<128; i++) {
+ h = map(ro + t*rd);
+ if (abs(h) < 1e-5) return t;
+ t += h;
+ if (t > maxt) return -1.0f;
+ }
+ return -1.0f;
+}
+
+#endif
+
+void main() {
+ vec2 uv = screen_texcoord;
+ vec3 view_dir = normalize(view_ray);
+
+ ivec2 coord = gbuffer_uv_to_texel(uv);
+
+ // retrieve pixel data
+ GBufferAttributes gba = read_gbuffer_DANRMS(uv, coord);
+
+ //vec3 p = mtx_camera[3].xyz + world_ray * gba.depth;
+ vec3 p = model_origin + world_ray * gba.depth;
+ //float z = -view_ray.z * gba.depth;
+
+ p = p*0.5 + 0.5;
+
+
+ vec3 base_light_power = light_power * light_color;
+
+
+ vec4 shn;
+ shn = sh_project(-gba.normal);
+
+ vec3 lc;
+ if (true) { //uv.y > 0.5) {
+ lc = shade_grid(shgrid, p, shn);
+ } else {
+ lc = vec3(1.0);
+ }
+
+#if DEBUG_GRID // visualize shgrid
+ vec3 rd = normalize(world_ray);
+ float t = trace_sphere_grid(model_origin, rd, length(world_ray) * gba.depth);
+ if (t >= 0.0f) {
+ p = model_origin + rd*t;
+ vec3 n = map_gradient(p);
+ if (true) //uv.x < 0.5f) // true: surf response, false: light direction
+ shn = sh_project(-n);
+ else
+ shn = sh_project(n);
+
+ p = p*0.5 + 0.5;
+ out_Color = vec4(base_light_power *
+ shade_grid(shgrid, p, shn), 1.0);
+ return;
+ }
+#endif
+
+ lc *= gba.albedo;
+
+#if USE_SSAO
+ float lum = dot(lc, vec3(0.2126, 0.7152, 0.0722));
+ float ssao = texture(ssao_color0, uv).r;
+ lc = max(vec3(0.0), lc - (1.0 - ssao)*lum*0.5);
+ lc *= lum + (1.0 - lum)*ssao;
+#endif
+
+ out_Color = vec4(base_light_power * lc, 0.0);
+
+ //out_Color = vec4(gba.albedo, 0.0);
+
+#if 0
+ vec3 color = vec3(0.0);
+ vec3 v = -normalize(world_ray);
+ vec3 n = gba.normal;
+ vec3 d = reflect(-v, n);
+
+ vec3 l = d;
+
+ vec3 h = normalize(l+v);
+
+ float cos_Ol = max(0.0, dot(n, l));
+ float cos_Oh = max(0.0, dot(n, h));
+ float cos_Od = max(0.0, dot(v, h));
+ float cos_Ov = max(0.0, dot(n, v));
+
+ float r = gba.roughness;
+ float k = (r*r) / 2.0;
+ float ik = 1.0 - k;
+ float Gnlvh = cos_Ol;
+ float Gdlvh = (cos_Ov*ik + k) * (cos_Ol*ik + k);
+ float G = (Gnlvh / Gdlvh);
+
+ vec3 specular_color = mix(vec3(1.0), gba.albedo, gba.metallic);
+
+ float Fc = pow(1.0 - cos_Od, 5.0);
+ vec3 F = (1.0 - Fc) * specular_color + vec3(Fc);
+ float fs = 0.3 * G * cos_Od / cos_Oh;
+
+#if 0
+ out_Color.rgb = d*0.5+0.5;
+#else
+ shn = sh_project(-d);
+ float s = 0.0;
+ float t = 1.0;
+ for (int i = 0; i < 4; ++i) {
+ color += shade_grid(shgrid, p + d*t*invgridsize, shn)/(t*t);
+ t += 1.0;
+ }
+ out_Color.rgb += 8.0 * (color/4.0) * clamp(F*fs,0.0,1.0); //clamp(F * fs, 0.0, 1.0);
+#endif
+ out_Color.a = 0.0;
+#endif
+
+}
+
+#endif
No newline at end of file
A => liminal_legacy/liminal/assets/shaders/g/ppfx/ssao.glsl +112 -0
@@ 0,0 1,112 @@
+#include "std/std.glsl"
+#include "view_ray.glsl"
+
+#if FRAGMENT_SHADER
+
+#include "gshader_frag.glsl"
+
+uniform float radius = 0.03;
+
+#include "lib/math.glsl"
+
+#if VIDEO_QUALITY >= VQ_HIGHEST
+#define SAMPLE_KERNEL_COUNT 64
+#define HAMMERSLEY_DEPTH 128
+#elif VIDEO_QUALITY >= VQ_HIGHER
+#define SAMPLE_KERNEL_COUNT 16
+#define HAMMERSLEY_DEPTH 24
+#else
+#define SAMPLE_KERNEL_COUNT 8
+#define HAMMERSLEY_DEPTH 24
+#endif
+
+#include "lib/hammersley.glsl"
+#define HAMMERSLEY_DEPTH_U uint(HAMMERSLEY_DEPTH)
+int h_index;
+const float h_scale = 1.0 / float(SAMPLE_KERNEL_COUNT);
+
+vec3 origin;
+mat3 tbn;
+
+float read_sample(int i) {
+ // get sample position:
+ vec2 h = hammersley2d(uint(i + h_index), HAMMERSLEY_DEPTH_U);
+ vec3 s = tbn * (
+ hemisphere_sample_uniform(h.x, h.y) * (
+ h_scale * float(1+i)));
+ s = s * radius + origin;
+
+ // project sample position:
+ vec4 offset = vec4(s, 1.0);
+ offset = mtx_proj * offset;
+ offset.xy /= offset.w;
+ offset.xy = offset.xy * 0.5 + 0.5;
+
+ // get sample depth:
+ float sample_depth = read_gbuffer_D(
+ offset.xy * viewport_scale + viewport_offset);
+ sample_depth *= -view_ray.z;
+
+ // range check & accumulate:
+ float dist = abs(origin.z - sample_depth);
+ float range_check = clamp(2.0 - dist/radius, 0.0, 1.0);
+ return step(sample_depth, s.z) * range_check;
+}
+
+void main() {
+ vec2 uv = screen_texcoord;
+ ivec2 coord = gbuffer_uv_to_texel(uv);
+
+ vec3 normal;
+ float depth = read_gbuffer_DN(coord, normal);
+ if (depth == 1.0) {
+ out_Color.r = 1.0;
+ return;
+ }
+
+ origin = depth * -view_ray;
+ normal *= -1.0;
+
+ float k;
+ k = random(vec3(uv,mod(time,1.0)));
+
+ h_index = int(k*10000.0) % (
+ HAMMERSLEY_DEPTH-SAMPLE_KERNEL_COUNT);
+
+ vec3 rvec = vec3(cos(k), sin(k), 0.0);
+ vec3 tangent = normalize(rvec - normal * dot(rvec, normal));
+ vec3 bitangent = cross(normal, tangent);
+ tbn = mat3(tangent, bitangent, normal);
+
+#if SAMPLE_KERNEL_COUNT > 16
+ float occlusion = 0.0;
+ for (int i = 0; i < SAMPLE_KERNEL_COUNT; ++i) {
+ occlusion += read_sample(i);
+ }
+#else
+ float occlusion = read_sample(0);
+ occlusion += read_sample(1);
+ occlusion += read_sample(2);
+ occlusion += read_sample(3);
+ occlusion += read_sample(4);
+ occlusion += read_sample(5);
+ occlusion += read_sample(6);
+ occlusion += read_sample(7);
+#if SAMPLE_KERNEL_COUNT > 8
+ occlusion += read_sample(8);
+ occlusion += read_sample(9);
+ occlusion += read_sample(10);
+ occlusion += read_sample(11);
+ occlusion += read_sample(12);
+ occlusion += read_sample(13);
+ occlusion += read_sample(14);
+ occlusion += read_sample(15);
+#endif
+#endif
+
+ occlusion = 1.0 - (occlusion * h_scale);
+
+ out_Color.r = occlusion;
+}
+
+#endif
No newline at end of file
A => liminal_legacy/liminal/assets/shaders/g/ppfx/ssgi.glsl +136 -0
@@ 0,0 1,136 @@
+// a naive attempt at screen-space global illumination
+
+#include "std/std.glsl"
+#include "view_ray.glsl"
+
+#if FRAGMENT_SHADER
+
+#ifndef SSAO_DEBAND
+#define SSAO_DEBAND 1
+#endif
+
+#ifndef SSAO_HAMMERSLEY
+#define SSAO_HAMMERSLEY 1
+#endif
+
+#include "gshader_frag.glsl"
+
+#if SSAO_DEBAND
+float rand(vec3 co){
+ return fract(sin(dot(co.xyz,vec3(12.9898,78.233,91.1743))) * 43758.5453);
+}
+#else
+uniform sampler2D sm_noise;
+uniform vec2 noise_scale;
+#endif
+uniform float radius = 0.03;
+
+uniform sampler2D combined_color0;
+
+#include "lib/snoise3.glsl"
+
+#define SAMPLE_KERNEL_SIZE 16
+#define SAMPLE_KERNEL_COUNT 16
+#if SSAO_HAMMERSLEY
+#include "lib/hammersley.glsl"
+#define HAMMERSLEY_DEPTH 24
+#define HAMMERSLEY_DEPTH_U uint(HAMMERSLEY_DEPTH)
+int h_index;
+const float h_scale = 1.0 / float(SAMPLE_KERNEL_COUNT);
+#else
+uniform vec3 sample_kernel[SAMPLE_KERNEL_SIZE];
+#endif
+
+vec3 origin;
+mat3 tbn;
+
+vec3 read_sample(int i) {
+ // get sample position:
+#if SSAO_HAMMERSLEY
+ vec2 h = hammersley2d(uint(i + h_index), HAMMERSLEY_DEPTH_U);
+ vec3 s = tbn * (
+ hemisphere_sample_uniform(h.x, h.y) * (
+ h_scale * float(1+i)));
+#else
+ vec3 s = tbn * sample_kernel[i];
+#endif
+ s = s * radius + origin;
+
+ // project sample position:
+ vec4 offset = vec4(s, 1.0);
+ offset = mtx_proj * offset;
+ offset.xy /= offset.w;
+ offset.xy = offset.xy * 0.5 + 0.5;
+
+ vec2 s_uv = offset.xy * viewport_scale + viewport_offset;
+
+ // get sample depth:
+ float sample_depth = read_gbuffer_D(s_uv);
+ sample_depth *= -view_ray.z;
+
+ // range check & accumulate:
+ float dist = abs(origin.z - sample_depth);
+ float range_check = clamp(2.0 - dist/radius, 0.0, 1.0);
+ float f = step(sample_depth, s.z) * range_check;
+ return texture(combined_color0, s_uv).rgb * f;
+}
+
+void main() {
+ vec2 uv = screen_texcoord;
+ ivec2 coord = gbuffer_uv_to_texel(uv);
+ bool even = ycocg_even(coord);
+
+ vec3 normal;
+ GBufferAttributes gba = read_gbuffer_DANRMS(uv, coord, even);
+ //float depth = read_gbuffer_DN(coord, normal);
+ float depth = gba.depth;
+ normal = gba.normal;
+
+ vec3 original = texture(combined_color0, uv).rgb;
+
+ if (depth == 1.0) {
+ out_Color.rgb = original;
+ return;
+ }
+
+ origin = depth * -view_ray;
+ normal *= -1.0;
+
+ float k = rand(vec3(uv,mod(time + SSGI_PASS_ID*3.1415,1.0)));
+
+#if SSAO_HAMMERSLEY
+ h_index = int(k*10000.0) % (
+ HAMMERSLEY_DEPTH-SAMPLE_KERNEL_COUNT);
+#endif
+
+#if SSAO_DEBAND
+ vec3 rvec = vec3(cos(k), sin(k), 0.0);
+#else
+ vec3 rvec = vec3(texture(sm_noise, uv * noise_scale).rg, 0.0);
+#endif
+ vec3 tangent = normalize(rvec - normal * dot(rvec, normal));
+ vec3 bitangent = cross(normal, tangent);
+ tbn = mat3(tangent, bitangent, normal);
+
+ vec3 occlusion = read_sample(0);
+ occlusion += read_sample(1);
+ occlusion += read_sample(2);
+ occlusion += read_sample(3);
+ occlusion += read_sample(4);
+ occlusion += read_sample(5);
+ occlusion += read_sample(6);
+ occlusion += read_sample(7);
+ occlusion += read_sample(8);
+ occlusion += read_sample(9);
+ occlusion += read_sample(10);
+ occlusion += read_sample(11);
+ occlusion += read_sample(12);
+ occlusion += read_sample(13);
+ occlusion += read_sample(14);
+ occlusion += read_sample(15);
+
+ vec3 final = gba.albedo * ((1.0 - gba.metallic) * occlusion * (0.6 / float(SAMPLE_KERNEL_COUNT)));
+ out_Color.rgb = original + final;
+}
+
+#endif
No newline at end of file
A => liminal_legacy/liminal/assets/shaders/g/ppfx/view_ray.glsl +40 -0
@@ 0,0 1,40 @@
+noperspective varying vec2 window_xy;
+noperspective varying vec2 absolute_texcoord;
+noperspective varying vec2 screen_texcoord;
+
+varying vec3 view_ray;
+varying vec3 world_ray;
+
+#ifndef USE_VIEW_RAY_MODEL
+#define USE_VIEW_RAY_MODEL 0
+#endif
+
+#ifndef VIEW_RAY_MAIN
+#define VIEW_RAY_MAIN main
+#endif
+
+#if USE_VIEW_RAY_MODEL
+flat varying vec3 model_origin;
+#endif
+
+#if VERTEX_SHADER
+
+void VIEW_RAY_MAIN(void) {
+ gl_Position = vec4(in_Position.xy, 0.0, 1.0);
+ window_xy = in_Position.xy;
+ absolute_texcoord = window_xy * 0.5 + 0.5;
+ screen_texcoord = absolute_texcoord * viewport_scale + viewport_offset;
+
+ vec4 far_pos = mtx_inv_proj * vec4(in_Position.xy, 1.0, 1.0);
+ far_pos /= far_pos.w;
+ view_ray = far_pos.xyz;
+
+
+ world_ray = mat3(mtx_camera) * view_ray;
+#if USE_VIEW_RAY_MODEL
+ world_ray = mat3(mtx_inv_model) * world_ray;
+ model_origin = (mtx_inv_model * mtx_camera[3]).xyz;
+#endif
+}
+
+#endif
A => liminal_legacy/liminal/assets/shaders/g/skybox.glsl +30 -0
@@ 0,0 1,30 @@
+
+#include "std/std.glsl"
+
+#include "gshader_vars.glsl"
+
+#if VERTEX_SHADER
+
+void main(void) {
+ gl_Position = vec4(in_Position.xy, 0.99999, 1.0);
+
+ mvp_vertex = vec4(in_Position.xy, 1.0, 1.0);
+ vec4 world_vertex = mtx_camera * (mtx_inv_proj * mvp_vertex);
+ world_vertex /= world_vertex.w;
+ last_mvp_vertex = mtx_last_vp * world_vertex;
+}
+
+#elif FRAGMENT_SHADER
+
+#include "gshader_frag.glsl"
+
+void main(void) {
+ GBufferAttributes attribs = gba_empty();
+ attribs.sky = 1.0;
+ attribs.velocity = gba_default_velocity();
+
+ write_gbuffer_depth_vel_fx(attribs);
+
+}
+
+#endif // FRAGMENT_SHADER
No newline at end of file
A => liminal_legacy/liminal/assets/shaders/lib/blur.glsl +142 -0
@@ 0,0 1,142 @@
+// various blur tool functions
+// you must declare sample_blur_texture before including
+// format:
+// BLUR_TYPE sample_blur_texture(vec2 uv, vec2 offset)
+
+#ifndef BLUR_TYPE
+#define BLUR_TYPE vec4
+#endif
+
+#ifndef BLUR_TEXTURE
+#define BLUR_TEXTURE sample_blur_texture
+#endif
+
+// 4 bilinear lookups
+BLUR_TYPE blur_texture_fastgauss_3x3(vec2 uv, vec2 texel, vec2 size)
+{
+ vec2 uvp = uv * size;
+ vec2 base_uv = floor(uvp + 0.5);
+ float s = uvp.x + 0.5 - base_uv.x;
+ float t = uvp.y + 0.5 - base_uv.y;
+ base_uv -= vec2(0.5);
+ base_uv *= texel;
+
+ float uw0 = (3.0 - 2.0 * s);
+ float uw1 = (1.0 + 2.0 * s);
+
+ float u0 = (2.0 - s) / uw0 - 1.0;
+ float u1 = s / uw1 + 1.0;
+
+ float vw0 = (3.0 - 2.0 * t);
+ float vw1 = (1.0 + 2.0 * t);
+
+ float v0 = (2.0 - t) / vw0 - 1.0;
+ float v1 = t / vw1 + 1.0;
+
+ BLUR_TYPE
+ sum = uw0 * vw0 * BLUR_TEXTURE(base_uv, vec2(u0, v0) * texel);
+ sum += uw1 * vw0 * BLUR_TEXTURE(base_uv, vec2(u1, v0) * texel);
+ sum += uw0 * vw1 * BLUR_TEXTURE(base_uv, vec2(u0, v1) * texel);
+ sum += uw1 * vw1 * BLUR_TEXTURE(base_uv, vec2(u1, v1) * texel);
+
+ return sum / 16.0;
+}
+
+// 9 bilinear lookups
+BLUR_TYPE blur_texture_fastgauss_5x5(vec2 uv, vec2 texel, vec2 size)
+{
+ vec2 uvp = uv * size;
+ vec2 base_uv = floor(uvp + 0.5);
+ float s = uvp.x + 0.5 - base_uv.x;
+ float t = uvp.y + 0.5 - base_uv.y;
+ base_uv -= vec2(0.5);
+ base_uv *= texel;
+
+ float uw0 = (4.0 - 3.0 * s);
+ float uw1 = 7.0;
+ float uw2 = (1.0 + 3.0 * s);
+
+ float u0 = (3.0 - 2.0 * s) / uw0 - 2.0;
+ float u1 = (3.0 + s) / uw1;
+ float u2 = s / uw2 + 2.0;
+
+ float vw0 = (4.0 - 3.0 * t);
+ float vw1 = 7.0;
+ float vw2 = (1.0 + 3.0 * t);
+
+ float v0 = (3.0 - 2.0 * t) / vw0 - 2.0;
+ float v1 = (3.0 + t) / vw1;
+ float v2 = t / vw2 + 2.0;
+
+ BLUR_TYPE
+ sum = uw0 * vw0 * BLUR_TEXTURE(base_uv, vec2(u0, v0) * texel);
+ sum += uw1 * vw0 * BLUR_TEXTURE(base_uv, vec2(u1, v0) * texel);
+ sum += uw2 * vw0 * BLUR_TEXTURE(base_uv, vec2(u2, v0) * texel);
+
+ sum += uw0 * vw1 * BLUR_TEXTURE(base_uv, vec2(u0, v1) * texel);
+ sum += uw1 * vw1 * BLUR_TEXTURE(base_uv, vec2(u1, v1) * texel);
+ sum += uw2 * vw1 * BLUR_TEXTURE(base_uv, vec2(u2, v1) * texel);
+
+ sum += uw0 * vw2 * BLUR_TEXTURE(base_uv, vec2(u0, v2) * texel);
+ sum += uw1 * vw2 * BLUR_TEXTURE(base_uv, vec2(u1, v2) * texel);
+ sum += uw2 * vw2 * BLUR_TEXTURE(base_uv, vec2(u2, v2) * texel);
+
+ return sum / 144.0;
+}
+
+// 16 bilinear lookups
+BLUR_TYPE blur_texture_fastgauss_7x7(vec2 uv, vec2 texel, vec2 size)
+{
+ vec2 uvp = uv * size;
+ vec2 base_uv = floor(uvp + 0.5);
+ float s = uvp.x + 0.5 - base_uv.x;
+ float t = uvp.y + 0.5 - base_uv.y;
+ base_uv -= vec2(0.5);
+ base_uv *= texel;
+
+ float uw0 = (5.0 * s - 6.0);
+ float uw1 = (11.0 * s - 28.0);
+ float uw2 = -(11.0 * s + 17.0);
+ float uw3 = -(5.0 * s + 1.0);
+
+ float u0 = (4.0 * s - 5.0) / uw0 - 3.0;
+ float u1 = (4.0 * s - 16.0) / uw1 - 1.0;
+ float u2 = -(7.0 * s + 5.0) / uw2 + 1.0;
+ float u3 = -s / uw3 + 3.0;
+
+ float vw0 = (5.0 * t - 6.0);
+ float vw1 = (11.0 * t - 28.0);
+ float vw2 = -(11.0 * t + 17.0);
+ float vw3 = -(5.0 * t + 1.0);
+
+ float v0 = (4.0 * t - 5.0) / vw0 - 3.0;
+ float v1 = (4.0 * t - 16.0) / vw1 - 1.0;
+ float v2 = -(7.0 * t + 5.0) / vw2 + 1.0;
+ float v3 = -t / vw3 + 3.0;
+
+ BLUR_TYPE
+ sum = uw0 * vw0 * BLUR_TEXTURE(base_uv, vec2(u0, v0) * texel);
+ sum += uw1 * vw0 * BLUR_TEXTURE(base_uv, vec2(u1, v0) * texel);
+ sum += uw2 * vw0 * BLUR_TEXTURE(base_uv, vec2(u2, v0) * texel);
+ sum += uw3 * vw0 * BLUR_TEXTURE(base_uv, vec2(u3, v0) * texel);
+
+ sum += uw0 * vw1 * BLUR_TEXTURE(base_uv, vec2(u0, v1) * texel);
+ sum += uw1 * vw1 * BLUR_TEXTURE(base_uv, vec2(u1, v1) * texel);
+ sum += uw2 * vw1 * BLUR_TEXTURE(base_uv, vec2(u2, v1) * texel);
+ sum += uw3 * vw1 * BLUR_TEXTURE(base_uv, vec2(u3, v1) * texel);
+
+ sum += uw0 * vw2 * BLUR_TEXTURE(base_uv, vec2(u0, v2) * texel);
+ sum += uw1 * vw2 * BLUR_TEXTURE(base_uv, vec2(u1, v2) * texel);
+ sum += uw2 * vw2 * BLUR_TEXTURE(base_uv, vec2(u2, v2) * texel);
+ sum += uw3 * vw2 * BLUR_TEXTURE(base_uv, vec2(u3, v2) * texel);
+
+ sum += uw0 * vw3 * BLUR_TEXTURE(base_uv, vec2(u0, v3) * texel);
+ sum += uw1 * vw3 * BLUR_TEXTURE(base_uv, vec2(u1, v3) * texel);
+ sum += uw2 * vw3 * BLUR_TEXTURE(base_uv, vec2(u2, v3) * texel);
+ sum += uw3 * vw3 * BLUR_TEXTURE(base_uv, vec2(u3, v3) * texel);
+
+ return sum / 2704.0;
+}
+
+#undef BLUR_TYPE
+#undef BLUR_TEXTURE
A => liminal_legacy/liminal/assets/shaders/lib/brdf.glsl +130 -0
@@ 0,0 1,130 @@
+#ifndef M_PI
+#define M_PI 3.141592653589793
+#endif
+
+// implementation of BRDF model after
+// http://disney-animation.s3.amazonaws.com/library/s2012_pbs_disney_brdf_notes_v2.pdf
+// and
+// http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf
+// some more background on this blog:
+// http://graphicrants.blogspot.de/2013/08/specular-brdf-reference.html
+
+// reference:
+// cos(Ol) = dot(n,l)
+// cos(Ov) = dot(n,v)
+// cos(Oh) = dot(n,h)
+// cos(Od) = dot(l,h) [or: dot(v,h)]
+//
+// f(l,v) = diffuse + (D(Oh)*F(Od)*G(Ol,Ov) / (4.0*cos(Ol)*cos(Ov))
+//
+// D = microfacet distribution function, specular peak shape
+// F = fresnel reflection coefficient
+// G = geometric attenuation / shadowing factor
+
+#define BRDF_F0_SILICON 0.3561
+#define BRDF_F0_DIAMOND 0.17225
+#define BRDF_F0_GLASS 0.04
+#define BRDF_F0_WATER 0.0204
+#define BRDF_F0_ICE 0.017
+#define BRDF_F0_VACUUM = 0.0
+
+struct BRDFAttributes {
+ float metallic; // 0..1
+ float roughness; // 0..1
+ vec3 base_color; //
+ float cos_Ol; // dot(n,l)
+ vec3 l; // light vector (points towards light)
+ vec3 v; // eye/camera vector (points towards camera)
+ vec3 n; // unit normal (points away from surface)
+} brdfa;
+
+// generated by brdf_build_vars()
+struct BRDFVars {
+ float F0; // specular reflectance at n=1 (0.04 for glass)
+ vec3 h; // unit halfway vector (l+v)/(|l+v|)
+ float cos_Ov; // dot(n,v)
+ float cos_Oh; // dot(n,h)
+ float cos_Od; // dot(l,h) == dot(v,h)
+} brdfv;
+
+void brdf_build_vars() {
+ vec3 h = normalize(brdfa.l + brdfa.v);
+
+ brdfv = BRDFVars(
+ mix(BRDF_F0_GLASS, 0.9, brdfa.metallic),
+ h,
+ max(0.0, dot(brdfa.n, brdfa.v)),
+ max(0.0, dot(brdfa.n, h)),
+ max(0.0, dot(brdfa.v, h))
+ );
+}
+
+float brdf_lambert() {
+ return 1.0 / M_PI;
+}
+
+#define BRDF_SPEC_BIAS 0.00015
+#define BRDF_MAX_SPEC 128.0
+
+float brdf_specular_D_GCX() {
+ float a = brdfa.roughness*brdfa.roughness;
+ float a2 = a*a;
+
+ float Dd = max(BRDF_SPEC_BIAS, brdfv.cos_Oh*brdfv.cos_Oh*(a2 - 1.0)+1.0);
+
+ return a2/(M_PI*Dd*Dd);
+}
+
+float brdf_specular_Gd_Schlick() {
+ float r = (brdfa.roughness+1.0);
+ float k = (r*r) / 8.0;
+ float ik = 1.0 - k;
+
+ float Gdlvh = 4.0 * (brdfv.cos_Ov*ik + k) * (brdfa.cos_Ol*ik + k);
+
+ return (1.0 / Gdlvh);
+}
+
+float brdf_specular_F_Schlick() {
+ float p = (-5.55473*brdfv.cos_Od-6.98316)*brdfv.cos_Od;
+ return brdfv.F0 + (1.0 - brdfv.F0)*pow(2.0, p);
+}
+
+float brdf_specular() {
+ float Dh = brdf_specular_D_GCX();
+ float Fvh = brdf_specular_F_Schlick();
+ float Gdlvh = brdf_specular_Gd_Schlick();
+
+ return clamp((Dh*Fvh*Gdlvh), 0.0, BRDF_MAX_SPEC);
+}
+
+vec3 brdf_mix_colors(vec3 fd, vec3 fs, vec3 color, float m) {
+ return mix(fd, fs, m) * color + fs * (1.0 - m);
+}
+
+vec3 brdf_mix(float fd, float fs, vec3 color, float m) {
+ return mix(fd, fs, m) * color + (fs * (1.0 - m));
+}
+
+vec3 brdf_evaluate_fluorescent() {
+ brdf_build_vars();
+
+ float fs; // specular
+ fs = (brdfa.roughness > 0.0)?brdf_specular():0.0;
+ return fs * mix(vec3(1.0), brdfa.base_color, brdfa.metallic);
+}
+
+
+vec3 brdf_evaluate() {
+ brdf_build_vars();
+
+ float fd; // diffuse
+ float fs; // specular
+
+ fd = brdf_lambert();
+ fs = (brdfa.roughness > 0.0)?brdf_specular():0.0;
+
+ return brdf_mix(fd, fs, brdfa.base_color, brdfa.metallic);
+}
+
+
A => liminal_legacy/liminal/assets/shaders/lib/evsm.glsl +86 -0
@@ 0,0 1,86 @@
+
+
+#define EVSM_ENABLE 1
+
+#ifndef USE_EVSM4
+#define USE_EVSM4 0
+#endif
+
+#define EVSM4 USE_EVSM4
+
+// positive and negative exponents
+const vec2 exponents = vec2(10.0, 24.0);
+const float light_bleeding_bias = 0.2;
+#if EVSM_ENABLE
+const float min_variance = 1e-3;
+#else // !EVSM_ENABLE
+const float min_variance = 1e-5;
+#endif // !EVSM_ENABLE
+
+// Applies exponential warp to shadow map depth, input depth should be in [0, 1]
+vec2 warp_depth(float depth, vec2 exponents) {
+ // Rescale depth into [-1, 1]
+ depth = 2.0 * depth - 1.0;
+ float pos = exp( exponents.x * depth);
+ float neg = -exp(-exponents.y * depth);
+ return vec2(pos, neg);
+}
+
+float warp_depth1(float depth, vec2 exponents) {
+ // Rescale depth into [-1, 1]
+ depth = 2.0 * depth - 1.0;
+ return exp( exponents.x * depth);
+}
+
+float linstep(float a, float b, float v) {
+ return clamp((v - a) / (b - a), 0.0, 1.0);
+}
+
+// Reduces VSM light bleedning
+float reduce_light_bleeding(float p_max, float amount) {
+ // Remove the [0, amount] tail and linearly rescale (amount, 1].
+ return linstep(amount, 1.0, p_max);
+}
+
+float chebyshev_upper_bound(vec2 moments, float mean, float minVariance,
+ float lightBleedingReduction) {
+ // Compute variance
+ float variance = moments.y - (moments.x * moments.x);
+ variance = max(variance, minVariance);
+
+ // Compute probabilistic upper bound
+ float d = mean - moments.x;
+ float pMax = variance / (variance + (d * d));
+
+ pMax = reduce_light_bleeding(pMax, lightBleedingReduction);
+
+ // One-tailed Chebyshev
+ return (mean <= moments.x ? 1.0 : pMax);
+}
+
+/*
+vec4 convert_depth_vsm(float depth) {
+ float moment1 = depth;
+ float moment2 = depth * depth;
+
+ return vec4( moment1, moment2, 0.0, 0.0);
+}
+*/
+
+vec4 convert_depth_evsm(float depth) {
+ vec2 moment = warp_depth(depth, exponents);
+
+ vec4 moment4 = vec4(moment.xy, moment.xy*moment.xy);
+
+#if EVSM4
+ return moment4;
+#else
+ return moment4.xzxz;
+#endif
+}
+
+#if EVSM_ENABLE
+#define convert_depth convert_depth_evsm
+#else
+#define convert_depth convert_depth_vsm
+#endif
A => liminal_legacy/liminal/assets/shaders/lib/fog.glsl +78 -0
@@ 0,0 1,78 @@
+// implementation for fog / fog sphere
+
+#ifndef USE_FOG_SPHERE
+#define USE_FOG_SPHERE 0
+#endif
+
+#ifndef USE_FOG_RANGE
+#define USE_FOG_RANGE 0
+#endif
+
+#ifndef USE_FOG_TEXTURE
+#define USE_FOG_TEXTURE 0
+#endif
+
+// distance in units until fog reaches half intensity
+const float fog_radius = 0.0;
+#if USE_FOG_RANGE
+uniform float fog_range = 100.0;
+#else
+#define fog_range far
+#endif
+uniform float fog_power = 0.2;
+uniform float fog_thickness = 1.0;
+
+#if USE_FOG_TEXTURE
+uniform sampler2D fog_map;
+uniform vec4 fog_uv_scale_offset = vec4(1.0,1.0,0.0,0.0);
+#else
+uniform vec3 fog_color = vec3(0.3, 0.7, 1.0);
+#endif
+
+#if USE_FOG_SPHERE
+
+uniform float fog_falloff_radius = 20.0;
+
+vec3 closest_ray_point(in vec3 pos, in vec3 view_pos, in vec3 view_dir, in float surface_distance) {
+ float t0 = dot(view_dir, pos - view_pos) / dot(view_dir, view_dir);
+
+ return view_pos + view_dir * min(surface_distance, max(0.0, t0));
+}
+
+float get_fog_intensity(in vec3 center, in vec3 view_pos, in vec3 view_dir, in float surface_distance) {
+ vec3 fog_point = closest_ray_point(center, view_pos, view_dir, surface_distance);
+
+ float rc = length(fog_point);
+ float h = max((rc - fog_radius) / fog_falloff_radius, 0.0);
+ float height_intensity = exp(-h*h);
+
+ float d = surface_distance / fog_range;
+ float depth_intensity = 1.0 - exp(-d*d);
+
+ return height_intensity * depth_intensity;
+}
+
+# else
+
+float get_fog_intensity(in vec3 center, in vec3 view_pos, in vec3 view_dir, in float surface_distance) {
+ float d = surface_distance / fog_range;
+ float depth_intensity = max(1.0 - exp(-d*d),
+ // full fadeout towards end
+ clamp(3.0*(d-1.0)+1.0,0.0,1.0));
+
+ return depth_intensity;
+}
+
+#endif
+
+vec3 mix_fog(vec3 color, float fog_intensity, vec3 envdir) {
+ color = color * (1.0 - fog_intensity * fog_thickness);
+
+#if USE_FOG_TEXTURE
+ vec2 uv = vec2(envdir.z*0.5+0.5, fog_intensity);
+ vec3 fog_color = textureLod(fog_map,
+ uv*fog_uv_scale_offset.xy + fog_uv_scale_offset.zw, 0.0).rgb;
+#endif
+
+ return color + fog_color * (fog_power * fog_intensity);
+}
A => liminal_legacy/liminal/assets/shaders/lib/hammersley.glsl +31 -0
@@ 0,0 1,31 @@
+#ifndef M_PI
+#define M_PI 3.141592653589793
+#endif
+
+float hammersley_radical_inverse_VdC(uint bits) {
+ bits = (bits << 16u) | (bits >> 16u);
+ bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
+ bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
+ bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
+ bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
+ return float(bits) * 2.3283064365386963e-10; // / 0x100000000
+}
+
+vec2 hammersley2d(uint i, uint N) {
+ return vec2(float(i)/float(N), hammersley_radical_inverse_VdC(i));
+}
+
+ vec3 hemisphere_sample_uniform(float u, float v) {
+ float phi = v * 2.0 * M_PI;
+ float cosTheta = 1.0 - u;
+ float sinTheta = sqrt(1.0 - cosTheta * cosTheta);
+ return vec3(cos(phi) * sinTheta, sin(phi) * sinTheta, cosTheta);
+ }
+
+ vec3 hemisphere_sample_cos(float u, float v) {
+ float phi = v * 2.0 * M_PI;
+ float cosTheta = sqrt(1.0 - u);
+ float sinTheta = sqrt(1.0 - cosTheta * cosTheta);
+ return vec3(cos(phi) * sinTheta, sin(phi) * sinTheta, cosTheta);
+ }
+
No newline at end of file
A => liminal_legacy/liminal/assets/shaders/lib/hslhsv.glsl +29 -0
@@ 0,0 1,29 @@
+vec3 hue2rgb(float hue) {
+ return clamp(
+ abs(mod(hue * 6.0 + vec3(0.0, 4.0, 2.0), 6.0) - 3.0) - 1.0,
+ 0.0, 1.0);
+}
+
+vec3 hsl2rgb(vec3 c) {
+ vec3 rgb = hue2rgb(c.x);
+ return c.z + c.y * (rgb - 0.5) * (1.0 - abs(2.0 * c.z - 1.0));
+}
+
+vec3 hsv2rgb(vec3 c) {
+ vec3 rgb = hue2rgb(c.x);
+ return c.z * mix(vec3(1.0), rgb, c.y);
+}
+
+vec3 hsv2rgbc(vec3 c) {
+ vec3 rgb = hue2rgb(c.x);
+ // rgb = smoothstep(vec3(0.0),vec3(1.0),rgb);
+ rgb = rgb * rgb * (3.0 - 2.0*rgb);
+ return c.z * mix(vec3(1.0), rgb, c.y);
+}
+
+// ratio: 3 = neon, 4 = refracted, 5+ = approximate white
+vec3 physhue2rgb(float hue, float ratio) {
+ return smoothstep(
+ vec3(0.0),vec3(1.0),
+ abs(mod(hue + vec3(0.0,1.0,2.0)*(1.0/ratio),1.0)*2.0-1.0));
+}
A => liminal_legacy/liminal/assets/shaders/lib/ibl_sample.glsl +146 -0
@@ 0,0 1,146 @@
+
+#define IBL_MAX_SPEC 128.0
+#define IBL_F0_GLASS 0.04
+#define IBL_NUM_SAMPLES 1u
+
+// image-based lighting
+////////////////////////////////////////////////////////////////////////////////
+
+struct IBLAttributes {
+ float metallic; // 0..1
+ float roughness; // 0..1
+ vec3 base_color; //
+ vec3 v; // eye/camera vector (points towards camera)
+ vec3 n; // unit normal (points away from surface)
+} ibla;
+
+// generated by brdf_build_vars()
+struct IBLVars {
+ float F0; // specular reflectance at n=1 (0.04 for glass)
+ vec3 l; // light vector (generated)
+ vec3 h; // unit halfway vector (l+v)/(|l+v|)
+ float cos_Ov; // dot(n,v)
+ float cos_Ol; // dot(n,l)
+ float cos_Oh; // dot(n,h)
+ float cos_Od; // dot(l,h) == dot(v,h)
+} iblv;
+
+void ibl_sample_build_vars() {
+ iblv.F0 = mix(IBL_F0_GLASS, 0.9, ibla.metallic);
+ iblv.cos_Ov = max(0.0, dot(ibla.n, ibla.v));
+}
+
+void ibl_sample_specular_update_vars() {
+ // iblv.h must be set from outside
+ float cos_Od = dot(ibla.v, iblv.h);
+ iblv.l = 2.0 * cos_Od*iblv.h - ibla.v;
+
+ iblv.cos_Ol = max(0.0, dot(ibla.n, iblv.l));
+ iblv.cos_Oh = max(0.0, dot(ibla.n, iblv.h));
+ iblv.cos_Od = max(0.0, cos_Od);
+}
+
+void ibl_sample_diffuse_update_vars() {
+ // iblv.h must be set from outside
+ float cos_Od = dot(ibla.v, iblv.h);
+ iblv.l = iblv.h;
+
+ iblv.cos_Ol = max(0.0, dot(ibla.n, iblv.l));
+ iblv.cos_Oh = max(0.0, dot(ibla.n, iblv.h));
+ iblv.cos_Od = max(0.0, cos_Od);
+}
+
+vec3 ibl_specular_importance_sample_GCX( vec2 Xi)
+{
+ float a = ibla.roughness * ibla.roughness;
+ float Phi = 2.0 * M_PI * Xi.x;
+ float CosTheta = sqrt( (1.0 - Xi.y) / ( 1.0 + (a*a - 1.0) * Xi.y ) );
+ float SinTheta = sqrt( 1.0 - CosTheta * CosTheta );
+ vec3 H = vec3(
+ SinTheta * cos( Phi ),
+ SinTheta * sin( Phi ),
+ CosTheta);
+ vec3 UpVector = abs(ibla.n.z) < 0.999 ? vec3(0.0,0.0,1.0) : vec3(1.0,0.0,0.0);
+ vec3 TangentX = normalize( cross( UpVector, ibla.n ) );
+ vec3 TangentY = cross( ibla.n, TangentX );
+ // Tangent to world space
+ return TangentX * H.x + TangentY * H.y + ibla.n * H.z;
+}
+
+vec3 ibl_diffuse_importance_sample_GCX( vec2 Xi)
+{
+ float Phi = 2.0 * M_PI * Xi.x;
+ float CosTheta = sqrt( 1.0 - Xi.y );
+ float SinTheta = sqrt( 1.0 - CosTheta * CosTheta );
+ vec3 H = vec3(
+ SinTheta * cos( Phi ),
+ SinTheta * sin( Phi ),
+ CosTheta);
+ vec3 UpVector = abs(ibla.n.z) < 0.999 ? vec3(0.0,0.0,1.0) : vec3(1.0,0.0,0.0);
+ vec3 TangentX = normalize( cross( UpVector, ibla.n ) );
+ vec3 TangentY = cross( ibla.n, TangentX );
+ // Tangent to world space
+ return TangentX * H.x + TangentY * H.y + ibla.n * H.z;
+}
+
+vec3 ibl_cubemap_vector(in vec3 v) {
+ return vec3(v.xz, -v.y);
+}
+
+vec3 ibl_sample_cubemap(in samplerCube sampler, in vec3 l, in float lod) {
+ return textureLod(sampler, ibl_cubemap_vector(l), lod).rgb;
+}
+
+#include "hammersley.glsl"
+
+float ibl_specular_G_Smith() {
+ float r = ibla.roughness;
+ float k = (r*r) / 2.0;
+ float ik = 1.0 - k;
+
+ float Gnlvh = iblv.cos_Ol;
+ float Gdlvh = (iblv.cos_Ov*ik + k) * (iblv.cos_Ol*ik + k);
+
+ return (Gnlvh / Gdlvh);
+}
+
+vec3 ibl_sample_specular_diffuse(in samplerCube sampler, out vec3 diffuse)
+{
+ ibl_sample_build_vars();
+
+ float miplevel = ibla.roughness * 16.0;
+ vec3 specular_color = mix(vec3(1.0), ibla.base_color, ibla.metallic);
+ vec3 diffuse_color = ibla.base_color * (1.0 - ibla.metallic);
+
+ diffuse = vec3(0.0);
+ vec3 SpecularLighting = vec3(0.0);
+ const uint NumSamples = IBL_NUM_SAMPLES;
+ for( uint i = 0u; i < NumSamples; i++ )
+ {
+ vec2 Xi = hammersley2d( i, NumSamples );
+ iblv.h = ibl_specular_importance_sample_GCX(Xi);
+ ibl_sample_specular_update_vars();
+ if ( iblv.cos_Ol > 0.0 )
+ {
+ vec3 SampleColor = ibl_sample_cubemap(sampler, iblv.l, miplevel ).rgb;
+ float G = ibl_specular_G_Smith();
+ float Fc = pow(1.0 - iblv.cos_Od, 5.0 );
+ vec3 F = (1.0 - Fc) * specular_color + vec3(Fc);
+ // Incident light = SampleColor * NoL
+ // Microfacet specular = D*G*F / (4*NoL*NoV)
+ // pdf = D * NoH / (4 * VoH)
+ float fs = G * iblv.cos_Od / (iblv.cos_Oh);
+ SpecularLighting += SampleColor * F * fs;
+ }
+ iblv.h = ibl_diffuse_importance_sample_GCX(Xi);
+ ibl_sample_diffuse_update_vars();
+ if ( iblv.cos_Ol > 0.0 )
+ {
+ vec3 SampleColor = ibl_sample_cubemap(sampler, iblv.l, 16.0 ).rgb;
+ diffuse += SampleColor;
+ }
+ }
+ diffuse = ibla.base_color * clamp(diffuse / float(NumSamples), 0.0, IBL_MAX_SPEC);
+ return clamp(SpecularLighting / float(NumSamples), 0.0, IBL_MAX_SPEC);
+}
+
A => liminal_legacy/liminal/assets/shaders/lib/mapping.glsl +281 -0
@@ 0,0 1,281 @@
+
+//
+// as described in Realtime Texture Quilting
+// by Hugh Malan, Jeff Cairns
+// http://hl.udogs.net/files/Uploads/%20User%20Uploads/PunkUser's%20Uploads/Malan%20-%20Real-time%20Texture%20Quilting%20(Siggraph%202011%20Advances%20in%20Real-Time%20Rendering%20Course).pdf
+#ifndef USE_QUILTING
+#define USE_QUILTING 0
+#endif
+
+#ifndef USE_TEXTURE_ARRAY
+#define USE_TEXTURE_ARRAY 0
+#endif
+
+// combined luma/roughness/metallic/alpha/normal uv/emissive/quilt factor
+// read from two textures
+#ifndef USE_LRMANNEQ
+#define USE_LRMANNEQ 0
+#endif
+
+#if USE_TEXTURE_ARRAY
+#define SAMPLER2DTYPE sampler2DArray
+#define VECUVTYPE vec3
+#define IN_TEXCOORD in_TexCoord1
+#else
+#define SAMPLER2DTYPE sampler2D
+#define VECUVTYPE vec2
+#define IN_TEXCOORD in_TexCoord0
+#endif
+
+#if USE_QUILTING
+
+struct QuiltVertex {
+ vec3 tangent;
+ vec3 bitangent;
+ vec3 origin;
+ vec4 color;
+ VECUVTYPE texcoord;
+};
+
+varying QuiltVertex quilt_blended_vertex[3];
+varying vec3 quilt_position;
+varying vec3 quilt_weight;
+varying vec3 quilt_normal;
+varying float quilt_scale;
+
+#else // !USE_QUILTING
+
+varying VECUVTYPE texcoord;
+
+#endif // USE_QUILTING
+
+
+#if VERTEX_SHADER
+
+vec3 mapping_orthogonal(vec3 v)
+{
+ return abs(v.x) > abs(v.z) ? vec3(-v.y, v.x, 0.0)
+ : vec3(0.0, -v.z, v.y);
+}
+
+void write_texcoord() {
+#if USE_QUILTING
+
+ int index = int(in_Position.w + 0.5);
+ quilt_position = in_Position.xyz;
+
+ quilt_scale = length(in_Tangent);
+
+ quilt_normal = normalize(in_Normal); //normalize(in_Normal);
+ vec3 tangent = normalize(
+ normalize(in_Tangent)
+ - dot(in_Tangent, quilt_normal)*quilt_normal);
+ vec3 bitangent = cross(quilt_normal, tangent);
+
+ for (int i = 0; i < 3; ++i) {
+ if (i == index) {
+ quilt_blended_vertex[i].tangent = tangent;
+ quilt_blended_vertex[i].bitangent = bitangent;
+ quilt_blended_vertex[i].origin = in_Position.xyz;
+ quilt_blended_vertex[i].color = in_Color;
+ quilt_blended_vertex[i].texcoord = IN_TEXCOORD;
+ quilt_weight[i] = 1.0;
+ } else {
+ quilt_blended_vertex[i].tangent = vec3(0.0);
+ quilt_blended_vertex[i].bitangent = vec3(0.0);
+ quilt_blended_vertex[i].origin = vec3(0.0);
+ quilt_blended_vertex[i].color = vec4(0.0);
+ quilt_blended_vertex[i].texcoord = VECUVTYPE(0.0);
+ quilt_weight[i] = 0.0;
+ }
+ }
+
+#else // !USE_QUILTING
+
+ texcoord = IN_TEXCOORD;
+
+#endif // USE_QUILTING
+}
+
+#elif FRAGMENT_SHADER
+
+#if USE_QUILTING
+vec3 quilt_constant = 1.0 / quilt_weight;
+
+QuiltVertex quilt_get_vertex(int k) {
+ QuiltVertex qo = quilt_blended_vertex[k];
+ float w = quilt_constant[k];
+ return QuiltVertex(
+ qo.tangent * w,
+ qo.bitangent * w,
+ qo.origin * w,
+ qo.color * w,
+ qo.texcoord * w
+ );
+}
+
+QuiltVertex quilt_vertex[3] = QuiltVertex[](
+ quilt_get_vertex(0),
+ quilt_get_vertex(1),
+ quilt_get_vertex(2)
+);
+
+VECUVTYPE calc_quilt_uv(in QuiltVertex qv) {
+ vec3 d = quilt_position - qv.origin;
+
+ VECUVTYPE uv = qv.texcoord;
+ uv.xy += vec2(dot(d, qv.tangent), -dot(d, qv.bitangent))*quilt_scale + 0.5;
+
+ return uv;
+}
+
+VECUVTYPE quilt_uv[3] = VECUVTYPE[](
+ calc_quilt_uv(quilt_vertex[0]),
+ calc_quilt_uv(quilt_vertex[1]),
+ calc_quilt_uv(quilt_vertex[2])
+);
+
+uniform float quilt_transition_range = 0.5;
+vec3 calc_quilt_weights(vec3 fitness) {
+ float maxfit = max(fitness.x, max(fitness.y, fitness.z));
+ vec3 raw_weight = max(vec3(0.0),
+ fitness+quilt_transition_range-maxfit)*fitness;
+ return raw_weight / max(0.0001,dot(raw_weight, vec3(1.0)));
+}
+
+vec3 get_quilt_normal(in vec3 vertex_normal) {
+ vec3 a = quilt_vertex[1].origin - quilt_vertex[0].origin;
+ vec3 b = quilt_vertex[2].origin - quilt_vertex[0].origin;
+ vec3 n = mat3(mtx_model) * cross(a,b);
+ // reproject on vertex normal to reconstruct sign
+ return mix(vertex_normal,
+ normalize(n * dot(n, vertex_normal)), 0.5);
+}
+
+void quilt_restore_vertices() {
+ quilt_vertex[0] = quilt_get_vertex(0);
+ quilt_vertex[1] = quilt_get_vertex(1);
+ quilt_vertex[2] = quilt_get_vertex(2);
+}
+
+vec3 unpack_normal(vec2 nxy) {
+ nxy = nxy*2.0-1.0;
+ return vec3(nxy, sqrt(1.0-nxy.x*nxy.x-nxy.y*nxy.y));
+}
+
+mat3 quilt_normal_matrix(in QuiltVertex qv, in vec3 flat_normal, in float sm) {
+
+ vec3 n = normalize(
+ flat_normal + cross(qv.tangent, qv.bitangent)*sm);
+
+ return mat3(qv.tangent, qv.bitangent, n);
+}
+
+float df_weight(float x) {
+ return clamp((1.0-x)*2.0-1.0, 0.0, 1.0);
+}
+
+// mat0: (Nx, Ny), Depth, OBJID
+// mat1: AO, Emissive, Roughness, Metallic
+void map_texture_dm(in SAMPLER2DTYPE sampler,
+ out vec2 mat0, out vec4 mat1, out vec3 n, out vec4 color) {
+#if USE_TEXTURE_ARRAY
+ vec4 a0 = texture(sampler, quilt_uv[0]);
+ vec4 a1 = texture(sampler, quilt_uv[1]);
+ vec4 a2 = texture(sampler, quilt_uv[2]);
+
+ vec4 b0 = texture(sampler, quilt_uv[0] + vec3(0.0,0.0,1.0));
+ vec4 b1 = texture(sampler, quilt_uv[1] + vec3(0.0,0.0,1.0));
+ vec4 b2 = texture(sampler, quilt_uv[2] + vec3(0.0,0.0,1.0));
+
+ vec3 quilt_normal_weight = calc_quilt_weights(
+ vec3(a0.z, a1.z, a2.z) * quilt_weight);
+
+ color = clamp(quilt_vertex[0].color*quilt_normal_weight[0]
+ + quilt_vertex[1].color*quilt_normal_weight[1]
+ + quilt_vertex[2].color*quilt_normal_weight[2], 0.0, 1.0);
+
+#if 0
+ float border = 1.0;
+
+ if (quilt_vertex[0].color.w > 1.5) {
+ border = min(border, 1.0-quilt_normal_weight[0]);
+ }
+ if (quilt_vertex[1].color.w > 1.5) {
+ border = min(border, 1.0-quilt_normal_weight[1]);
+ }
+ if (quilt_vertex[2].color.w > 1.5) {
+ border = min(border, 1.0-quilt_normal_weight[2]);
+ }
+
+ if ((quilt_vertex[0].color.w > 1.5) && (quilt_vertex[1].color.w > 1.5)) {
+ border = min(border, quilt_normal_weight[2]);
+ }
+ if ((quilt_vertex[1].color.w > 1.5) && (quilt_vertex[2].color.w > 1.5)) {
+ border = min(border, quilt_normal_weight[0]);
+ }
+ if ((quilt_vertex[2].color.w > 1.5) && (quilt_vertex[0].color.w > 1.5)) {
+ border = min(border, quilt_normal_weight[1]);
+ }
+
+ min(quilt_normal_weight[0], min(quilt_normal_weight[1],
+ quilt_normal_weight[2])); */
+
+ float bs = (1.0 - clamp(border*16.0, 0.0, 1.0));
+
+ color += 0.3*bs;
+#endif
+
+ mat0 = a0.zw*quilt_normal_weight[0]
+ + a1.zw*quilt_normal_weight[1]
+ + a2.zw*quilt_normal_weight[2];
+
+ mat1 = b0*quilt_normal_weight[0]
+ + b1*quilt_normal_weight[1]
+ + b2*quilt_normal_weight[2];
+
+ float w = 0.0;
+
+ vec3 flat_normal = quilt_normal; //get_quilt_normal(quilt_normal)*(1.0 - w);
+
+ vec3 n0 = quilt_normal_matrix(quilt_vertex[0], flat_normal, w)
+ * unpack_normal(a0.xy);
+ vec3 n1 = quilt_normal_matrix(quilt_vertex[1], flat_normal, w)
+ * unpack_normal(a1.xy);
+ vec3 n2 = quilt_normal_matrix(quilt_vertex[2], flat_normal, w)
+ * unpack_normal(a2.xy);
+
+ n = normalize(n0*quilt_normal_weight[0]
+ + n1*quilt_normal_weight[1]
+ + n2*quilt_normal_weight[2]);
+ n = quilt_normal;
+
+ //color.w = bs;
+
+ //color = vec4(quilt_weight, 0.0);
+
+ //color = vec4(n*0.5+0.5, 0.0);
+
+#endif
+
+}
+
+vec4 map_texture(in SAMPLER2DTYPE sampler) {
+ vec3 quilt_normal_weight = calc_quilt_weights(quilt_weight);
+ return texture(sampler,
+ quilt_uv[0]) * quilt_normal_weight[0]
+ + texture(sampler,
+ quilt_uv[1]) * quilt_normal_weight[1]
+ + texture(sampler,
+ quilt_uv[2]) * quilt_normal_weight[2];
+}
+
+#else // !USE_QUILTING
+
+vec4 map_texture(in SAMPLER2DTYPE sampler) {
+ return texture(sampler, texcoord);
+}
+
+#endif // USE_QUILTING
+
+#endif // FRAGMENT_SHADER
No newline at end of file
A => liminal_legacy/liminal/assets/shaders/lib/math.glsl +90 -0
@@ 0,0 1,90 @@
+#ifndef M_PI
+#define M_PI 3.141592653589793
+#endif
+
+vec3 orthogonal(vec3 v)
+{
+ return abs(v.x) > abs(v.z) ? vec3(-v.y, v.x, 0.0)
+ : vec3(0.0, -v.z, v.y);
+}
+
+mat2 mat_rotate_xy(float a) {
+ float ca = cos(a);
+ float sa = sin(a);
+ return mat2(ca,-sa,sa,ca);
+}
+
+float random(vec3 co) {
+ return fract(sin(dot(co.xyz,vec3(12.9898,78.233,91.1743))) * 43758.5453);
+}
+
+float lintri(float x) {
+ return abs(mod(x, 2.0) - 1.0);
+}
+
+float tri(float x) {
+ return abs(mod(x, 2.0) - 1.0)*2.0-1.0;
+}
+
+float linsmoothtri(float x) {
+ return smoothstep(0.0,1.0,abs(mod(x,2.0) - 1.0));
+}
+
+// cos(x) ~= smoothtri(x/PI)
+float smoothtri(float x) {
+ return smoothstep(0.0,1.0,abs(mod(x,2.0) - 1.0))*2.0-1.0;
+}
+
+const mat4 dither_mtx = mat4(
+ vec4( 1.0, 33.0, 9.0, 41.0),
+ vec4(49.0, 17.0, 57.0, 25.0),
+ vec4(13.0, 45.0, 5.0, 37.0),
+ vec4(61.0, 29.0, 53.0, 21.0)
+);
+
+float dither_bayer_pattern(vec2 frag_coord)
+{
+ vec2 puv = mod(frag_coord, 4.0);
+ int x = int(puv.x);
+ int y = int(puv.y);
+ return (dither_mtx[x][y]+1.0)/64.0;
+}
+
+float dither_bayer_pattern_lsb_noise(vec2 frag_coord)
+{
+ vec2 puv = mod(frag_coord, 4.0);
+ int x = int(puv.x);
+ int y = int(puv.y);
+ return (dither_mtx[x][y]+random(vec3(frag_coord,mod(time,1.0))))/64.0;
+}
+
+vec2 mirror_hexagon(vec2 uv) {
+ float ca = sqrt(3.0) / 2.0;
+ mat2 m = mat2(0.5,-ca,ca,0.5);
+
+ uv.x = abs(uv.x);
+ uv = m * uv;
+ uv.x = abs(uv.x);
+ uv = m * uv;
+ uv.x = abs(uv.x);
+ return uv;
+}
+
+vec2 mirror_dodecagon(vec2 uv) {
+ float ca = sqrt(3.0) / 2.0;
+ mat2 m = mat2(0.5,-ca,ca,0.5);
+
+ uv.x = abs(uv.x);
+ uv = m * uv;
+ uv.x = abs(uv.x);
+ uv = m * uv;
+ uv.x = abs(uv.x);
+
+ m = mat2(ca,-0.5,0.5,ca);
+ uv = m * uv;
+ uv.x = abs(uv.x);
+
+ return uv;
+}
+
+
A => liminal_legacy/liminal/assets/shaders/lib/normal_codec.glsl +57 -0
@@ 0,0 1,57 @@
+
+vec2 encode_normal_xy(vec3 n) {
+ return n.xy;
+}
+
+vec3 decode_normal_xy(vec2 enc) {
+ vec3 n;
+ n.xy = enc;
+ n.z = sqrt(1.0-dot(n.xy, n.xy));
+ return n;
+}
+
+vec2 encode_normal_stereographic(vec3 n) {
+ float scale = 1.7777;
+ vec2 enc = n.xy / (n.z+1);
+ enc /= scale;
+ return enc*0.5+0.5;
+}
+
+vec3 decode_normal_stereographic(vec2 enc) {
+ float scale = 1.7777;
+ vec3 nn = vec3(enc.xy,0.0)*vec3(2*scale,2*scale,0.0) +
+ vec3(-scale,-scale,1.0);
+ float g = 2.0 / dot(nn,nn);
+ vec3 n;
+ n.xy = g*nn.xy;
+ n.z = g-1;
+ return n;
+}
+
+// octahedron packing as described in http://jcgt.org/published/0003/02/01/
+vec2 signNotZero(vec2 v) {
+ return vec2((v.x >= 0.0) ? 1.0 : -1.0, (v.y >= 0.0) ? 1.0 : -1.0);
+}
+
+vec2 encode_normal_octahedron(in vec3 v) {
+ vec2 p = v.xy * (1.0 / (abs(v.x) + abs(v.y) + abs(v.z)));
+ return ((v.z <= 0.0) ? ((1.0 - abs(p.yx)) * signNotZero(p)) : p)*0.5+0.5;
+}
+
+vec3 decode_normal_octahedron(vec2 e) {
+ e = e*2.0-1.0;
+ vec3 v = vec3(e.xy, 1.0 - abs(e.x) - abs(e.y));
+ if (v.z < 0) v.xy = (1.0 - abs(v.yx)) * signNotZero(v.xy);
+ return normalize(v);
+}
+
+#if 1
+#define encode_normal encode_normal_octahedron
+#define decode_normal decode_normal_octahedron
+#elif 0
+#define encode_normal encode_normal_stereographic
+#define decode_normal decode_normal_stereographic
+#else
+#define encode_normal encode_normal_xy
+#define decode_normal decode_normal_xy
+#endif
No newline at end of file
A => liminal_legacy/liminal/assets/shaders/lib/perturb.glsl +86 -0
@@ 0,0 1,86 @@
+#ifndef PERTURB_GLSL
+#define PERTURB_GLSL
+
+// from "Bump Mapping Unparametrized Surfaces on the GPU"
+// by Morten S. Mikkelsen
+
+// also read
+// http://www.rorydriscoll.com/2012/01/11/derivative-maps/
+// for additional improvements
+
+// position must be in same space as normal (e.g. modelview)
+
+// from any height source, but pixely artifacts
+vec3 perturb_normal_height(vec3 pos, vec3 normal, float height) {
+ vec3 vSigmaS = dFdx ( pos );
+ vec3 vSigmaT = dFdy ( pos );
+ vec3 vN = normal; // normalized
+ vec3 vR1 = cross ( vSigmaT , vN );
+ vec3 vR2 = cross (vN , vSigmaS );
+ float fDet = dot ( vSigmaS , vR1 );
+ float dBs = dFdx ( height );
+ float dBt = dFdy ( height );
+ vec3 vSurfGrad = sign ( fDet ) * ( dBs * vR1 + dBt * vR2 );
+ return normalize ( abs ( fDet )*vN - vSurfGrad );
+}
+
+// smoother, but only from bump map sources
+vec3 perturb_normal_mapped(vec3 pos, vec3 normal,
+ in sampler2D height_map, in vec2 uv) {
+
+ vec3 vSigmaS = dFdx ( pos );
+ vec3 vSigmaT = dFdy ( pos );
+ vec3 vN = normal; // normalized
+ vec3 vR1 = cross ( vSigmaT , vN );
+ vec3 vR2 = cross (vN , vSigmaS );
+ float fDet = dot ( vSigmaS , vR1 );
+
+ vec2 TexDx = dFdx ( uv );
+ vec2 TexDy = dFdy ( uv );
+ vec2 STll = uv;
+ vec2 STlr = uv + TexDx;
+ vec2 STul = uv + TexDy;
+ float Hll = texture(height_map, STll ).r;
+ float Hlr = texture(height_map, STlr ).r;
+ float Hul = texture(height_map, STul ).r;
+ float dBs = (Hlr - Hll);
+ float dBt = (Hul - Hll);
+
+ vec3 vSurfGrad = sign ( fDet ) * ( dBs * vR1 + dBt * vR2 );
+ return normalize ( abs ( fDet )*vN - vSurfGrad );
+}
+
+// perturb using derivative height map
+vec3 perturb_normal_derivative_mapped(vec3 pos, vec3 normal,
+ in sampler2D height_map, in vec2 uv) {
+
+ vec2 dBduv = (texture(height_map, uv ).xy*2.0 - 1.0);
+#if 1
+ vec2 dim = vec2(textureSize(height_map, 0));
+ dBduv *= dim;
+#endif
+ //dBduv.y *= -1.0; // if t is flipped, this needs be applied
+
+ vec3 vSigmaS = dFdx ( pos );
+ vec3 vSigmaT = dFdy ( pos );
+ vec3 vN = normal; // normalized
+ vec3 vR1 = cross ( vSigmaT , vN );
+ vec3 vR2 = cross (vN , vSigmaS );
+ float fDet = dot ( vSigmaS , vR1 );
+
+ vec2 TexDx = dFdx ( uv );
+ vec2 TexDy = dFdy ( uv );
+ float dBs = dot(dBduv, TexDx);
+ float dBt = dot(dBduv, TexDy);
+
+ vec3 vSurfGrad = sign ( fDet ) * ( dBs * vR1 + dBt * vR2 );
+ return normalize ( abs ( fDet )*vN - vSurfGrad );
+}
+
+// for spatial gradient maps
+vec3 perturb_normal_gradient(vec3 normal, vec3 gradient) {
+ vec3 vSurfGrad = gradient - normal * dot ( normal , gradient );
+ return normalize ( normal - vSurfGrad );
+}
+
+#endif // PERTURB_GLSL
No newline at end of file
A => liminal_legacy/liminal/assets/shaders/lib/sdf_math.glsl +128 -0
@@ 0,0 1,128 @@
+#include "lib/snoise3.glsl"
+
+float sdf_sphere(vec3 p, float radius) {
+ return length(p) - radius;
+}
+
+float sdf_plane(vec3 p, vec4 u) {
+ return dot(p,u.xyz)+u.w;
+}
+
+float sdf_torus(vec3 p, float inner_radius, float outer_radius) {
+ vec2 q = vec2(length(p.xy) - outer_radius, p.z);
+ return length(q)-inner_radius;
+}
+
+float sdf_box(vec3 p, vec3 size) {
+ vec3 d = abs(p) - size;
+ return min(max(d.x,max(d.y,d.z)),0.0) + length(max(d,0.0));
+}
+
+float sdf_noise(vec3 p, float alpha, float beta, int octaves, float amp) {
+ float val = 0.0;
+ float sum = 0.0;
+ float scale = 1.0;
+
+ for (int i = 0; i < octaves; ++i) {
+ val = snoise(p)*0.5;
+ sum += val / scale;
+ scale *= alpha;
+ p *= beta;
+ }
+ return sum*amp;
+}
+
+vec2 sdf_union(vec2 a, vec2 b) {
+ return (a.x < b.x)?a:b;
+}
+
+vec2 sdf_intersect(vec2 a, vec2 b) {
+ return (a.x > b.x)?a:b;
+}
+
+vec2 sdf_difference(vec2 a, vec2 b) {
+ return sdf_intersect(a, vec2(-b.x,b.y));
+}
+
+vec2 sdf_displace(vec2 a, vec2 b) {
+ return vec2(a.x+b.x, a.y);
+}
+
+vec2 sdf_smooth_union(vec2 a, vec2 b, float k) {
+ float h = clamp(0.5+0.5*(b.x-a.x)/k, 0.0, 1.0);
+ return vec2(b.x + (a.x-b.x)*h - k*h*(1.0-h), sdf_union(a,b).y);
+}
+
+float sdf_repeat(float x, float d) {
+ return mod(x+d*0.5,d)-d*0.5;
+}
+
+vec2 sdf_mixuv(float x, vec2 a, vec2 b, float k) {
+ float u = clamp(1.0+(x - 1.0)*k, 0.0, 1.0);
+ return vec2(mix(a.x,b.x,u), mix(a.y,b.y,step(u,0.5)));
+}
+
+/*
+
+vec3 calcNormal(in vec3 p) {
+ vec2 e = vec2(1.0/128.0, 0.0);
+ return normalize(vec3(
+ map(p + e.xyy) - map(p - e.xyy),
+ map(p + e.yxy) - map(p - e.yxy),
+ map(p + e.yyx) - map(p - e.yyx)));
+}
+
+float calcAO(vec3 p, vec3 n, float radius) {
+ float s = radius/3.0;
+ float ao = 0.0;
+ for (int i = 1; i <= 3; ++i) {
+ float dist = s * float(i);
+ float t = map(p + n*dist);
+ ao += max(0.0, (dist - t) / dist);
+ }
+ return 1.0 - (ao/3.0);
+}
+
+void main( void ) {
+
+ vec2 uv = gl_FragCoord.xy / resolution.xy;
+ vec2 p = 2.0*uv-1.0;
+ p.x *= resolution.x / resolution.y;
+
+ vec3 r0 = vec3(p.x,p.y,1.0);
+ float bias = 1.5;
+ vec3 rd = vec3(0.0,0.0,-1.0/bias);
+ float tmax = 2.0*bias;
+ float h = 0.0;
+ float t = 0.0;
+ for (int i=0; i<256; i++){
+ h = map(r0 + t*rd);
+ if (h < 0.001 || t > tmax){
+ break;
+ }
+ t += h;
+ }
+
+ vec3 col = vec3(0.0);
+ if (t < tmax) {
+ vec3 pos = r0 + t*rd;
+ vec3 norm = calcNormal(pos);
+ col = (norm*0.5+0.5)*calcAO(pos,norm,0.1);
+ }
+
+ gl_FragColor = vec4(pow(col,vec3(0.45)),1.0);
+}
+
+float softshadow( in vec3 ro, in vec3 rd, float mint, float k )
+{
+ float res = 1.0;
+ float t = mint;
+ for( int i=0; i<45; i++ )
+ {
+ float h = map(ro + rd*t).x;
+ res = min( res, k*h/t );
+ t += clamp( h, 0.04, 0.1 );
+ }
+ return clamp(res,0.0,1.0);
+}
+*/
A => liminal_legacy/liminal/assets/shaders/lib/sh.glsl +49 -0
@@ 0,0 1,49 @@
+
+#ifndef M_DIVPI
+#define M_DIVPI 0.3183098861837907
+#endif
+
+vec4 sh_project(vec3 n) {
+ const float f1 = 0.488602511902919920;
+ return vec4(
+ 0.282094791773878140,
+ vec3(-f1,f1,-f1) * n.yzx);
+}
+
+float sh_shade(vec4 vL, vec4 vN) {
+ return max(dot(vL, vN), 0.0) * M_DIVPI;
+}
+
+#define SHSharpness 1.0 // 2.0
+vec4 sh_irradiance_probe(vec4 v) {
+ const float sh_c0 = (2.0 - SHSharpness) * 1.0;
+ const float sh_c1 = SHSharpness * 2.0 / 3.0;
+ return vec4(v.x * sh_c0, v.yzw * sh_c1);
+}
+
+// from http://www.crytek.com/download/Light_Propagation_Volumes.pdf
+// input is direction and zonal harmonics coefficients
+vec4 sh_rotate(vec3 vcDir, vec2 vZHCoeffs) {
+ // compute sine and cosine of thetta angle
+ // beware of singularity when both x and y are 0 (no need to rotate at all)
+ vec2 theta12_cs = normalize(vcDir.xy);
+
+ // compute sine and cosine of phi angle
+ vec2 phi12_cs = vec2(
+ sqrt(1.0 - vcDir.z * vcDir.z),
+ vcDir.z
+ );
+
+ return vec4(
+ // The first band is rotation-independent
+ vZHCoeffs.x,
+ // rotating the second band of SH
+ -vZHCoeffs.y * phi12_cs.x * theta12_cs.y,
+ vZHCoeffs.y * phi12_cs.y,
+ -vZHCoeffs.y * phi12_cs.x * theta12_cs.x
+ );
+}
+
+vec4 sh_project_hclobe(vec3 vcDir) {
+ return sh_rotate(vcDir, vec2(0.25, 0.5));
+}
A => liminal_legacy/liminal/assets/shaders/lib/snoise3.glsl +102 -0
@@ 0,0 1,102 @@
+//
+// Description : Array and textureless GLSL 2D/3D/4D simplex
+// noise functions.
+// Author : Ian McEwan, Ashima Arts.
+// Maintainer : ijm
+// Lastmod : 20110822 (ijm)
+// License : Copyright (C) 2011 Ashima Arts. All rights reserved.
+// Distributed under the MIT License. See LICENSE file.
+// https://github.com/ashima/webgl-noise
+//
+
+vec3 mod289(vec3 x) {
+ return x - floor(x * (1.0 / 289.0)) * 289.0;
+}
+
+vec4 mod289(vec4 x) {
+ return x - floor(x * (1.0 / 289.0)) * 289.0;
+}
+
+vec4 permute(vec4 x) {
+ return mod289(((x*34.0)+1.0)*x);
+}
+
+vec4 taylorInvSqrt(vec4 r)
+{
+ return 1.79284291400159 - 0.85373472095314 * r;
+}
+
+float snoise(vec3 v)
+ {
+ const vec2 C = vec2(1.0/6.0, 1.0/3.0) ;
+ const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);
+
+// First corner
+ vec3 i = floor(v + dot(v, C.yyy) );
+ vec3 x0 = v - i + dot(i, C.xxx) ;
+
+// Other corners
+ vec3 g = step(x0.yzx, x0.xyz);
+ vec3 l = 1.0 - g;
+ vec3 i1 = min( g.xyz, l.zxy );
+ vec3 i2 = max( g.xyz, l.zxy );
+
+ // x0 = x0 - 0.0 + 0.0 * C.xxx;
+ // x1 = x0 - i1 + 1.0 * C.xxx;
+ // x2 = x0 - i2 + 2.0 * C.xxx;
+ // x3 = x0 - 1.0 + 3.0 * C.xxx;
+ vec3 x1 = x0 - i1 + C.xxx;
+ vec3 x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y
+ vec3 x3 = x0 - D.yyy; // -1.0+3.0*C.x = -0.5 = -D.y
+
+// Permutations
+ i = mod289(i);
+ vec4 p = permute( permute( permute(
+ i.z + vec4(0.0, i1.z, i2.z, 1.0 ))
+ + i.y + vec4(0.0, i1.y, i2.y, 1.0 ))
+ + i.x + vec4(0.0, i1.x, i2.x, 1.0 ));
+
+// Gradients: 7x7 points over a square, mapped onto an octahedron.
+// The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294)
+ float n_ = 0.142857142857; // 1.0/7.0
+ vec3 ns = n_ * D.wyz - D.xzx;
+
+ vec4 j = p - 49.0 * floor(p * ns.z * ns.z); // mod(p,7*7)
+
+ vec4 x_ = floor(j * ns.z);
+ vec4 y_ = floor(j - 7.0 * x_ ); // mod(j,N)
+
+ vec4 x = x_ *ns.x + ns.yyyy;
+ vec4 y = y_ *ns.x + ns.yyyy;
+ vec4 h = 1.0 - abs(x) - abs(y);
+
+ vec4 b0 = vec4( x.xy, y.xy );
+ vec4 b1 = vec4( x.zw, y.zw );
+
+ //vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0;
+ //vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0;
+ vec4 s0 = floor(b0)*2.0 + 1.0;
+ vec4 s1 = floor(b1)*2.0 + 1.0;
+ vec4 sh = -step(h, vec4(0.0));
+
+ vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ;
+ vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ;
+
+ vec3 p0 = vec3(a0.xy,h.x);
+ vec3 p1 = vec3(a0.zw,h.y);
+ vec3 p2 = vec3(a1.xy,h.z);
+ vec3 p3 = vec3(a1.zw,h.w);
+
+//Normalise gradients
+ vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3)));
+ p0 *= norm.x;
+ p1 *= norm.y;
+ p2 *= norm.z;
+ p3 *= norm.w;
+
+// Mix final noise value
+ vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);
+ m = m * m;
+ return 42.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1),
+ dot(p2,x2), dot(p3,x3) ) );
+ }
No newline at end of file
A => liminal_legacy/liminal/assets/shaders/lib/srgb.glsl +12 -0
@@ 0,0 1,12 @@
+
+vec3 srgb2lin(vec3 color) {
+ return color * (color * (
+ color * 0.305306011 + 0.682171111) + 0.012522878);
+}
+
+vec3 lin2srgb(vec3 color) {
+ vec3 S1 = sqrt(color);
+ vec3 S2 = sqrt(S1);
+ vec3 S3 = sqrt(S2);
+ return 0.585122381 * S1 + 0.783140355 * S2 - 0.368262736 * S3;
+}
A => liminal_legacy/liminal/assets/shaders/lib/ycbcr.glsl +50 -0
@@ 0,0 1,50 @@
+
+// ycbcr.glsl
+// by leonard ritter (contact@leonard-ritter.com)
+// Duangle GbR
+// this file is in the public domain
+
+// Y'CbCr conversion matrices
+// after http://www.mir.com/DMG/ycbcr.html
+
+// we assume r,g,b,y = [0..1] and cb,cr = [-0.5..+0.5]
+// multiply with either matrix to convert
+
+// also have a look at
+// http://www.pmavridis.com/research/fbcompression/#screen4-tab
+
+/*
+- uniform scales translate perfectly into Y'CbCr space, no change required
+- non-uniform scales (multiplying two colors in ycbcr space) requires a
+ transformation which has been optimized in ycbcr_mul()
+- addition and subtraction of Y'CbCr values is equivalent to the
+ same operation in RGB space.
+*/
+
+const mat3 rgb2ycbcr = mat3(
+ 0.299, -0.168736, 0.5,
+ 0.587, -0.331264, -0.418688,
+ 0.114, 0.5, -0.081312
+);
+
+const mat3 ycbcr2rgb = mat3(
+ 1.0, 1.0, 1.0,
+ 0.0, -0.344136, 1.772,
+ 1.402, -0.714136, 0.0
+);
+
+vec3 ycbcr_clamp(vec3 c) {
+ return clamp(c, vec3(0.0, -0.5, -0.5), vec3(1.0, 0.5, 0.5));
+}
+
+const mat3 ycbcrmul = mat3(
+ 0.144261, -0.081411, -0.102898,
+ 0.427476, 1.530761, -0.304903,
+ 0.887080, -0.500609,0.769275
+);
+
+vec3 ycbcr_mul(vec3 ya, vec3 y) {
+ vec3 p = ya * y;
+ vec3 q = ya * y.yzx + y * ya.yzx;
+ return (ycbcrmul * vec3(q.y, p.yz)) + vec3(p.x, q.xz);
+}
A => liminal_legacy/liminal/assets/shaders/lib/ycocg.glsl +111 -0
@@ 0,0 1,111 @@
+
+// based on
+// http://www.pmavridis.com/research/fbcompression/TheCompactYCoCgFB.pdf
+
+#if 1
+const mat3 rgb2ycocg = mat3(
+ 0.25, 0.5, -0.25,
+ 0.5, 0.0, 0.5,
+ 0.25, -0.5, -0.25
+);
+const mat3 ycocg2rgb = mat3(
+ 1.0, 1.0, 1.0,
+ 1.0, 0.0, -1.0,
+ -1.0, 1.0, -1.0
+);
+
+vec3 rgb_to_ycocg(vec3 color) {
+ return rgb2ycocg * color;
+}
+
+vec3 ycocg_to_rgb(vec3 color) {
+ return clamp(ycocg2rgb * color, 0.0, 1.0);
+}
+#else
+vec3 rgb_to_ycocg(vec3 color) {
+ float Co = color.r - color.b;
+ float t = color.b + Co*0.5;
+ float Cg = color.g - t;
+ float Y = t + Cg * 0.5;
+ return vec3(Y, Co, Cg);
+}
+
+vec3 ycocg_to_rgb(vec3 color) {
+ float t = color.x - color.z*0.5;
+ float G = color.z + t;
+ float B = t - color.y*0.5;
+ float R = color.y + B;
+ return vec3(R, G, B);
+}
+#endif
+
+ivec2 ycocg_coord() {
+ return ivec2(gl_FragCoord.xy);
+}
+
+vec3 unpack_ycocg(bool even, vec2 color, float v) {
+ return even ? vec3(color.rg,v):vec3(color.r, v, color.g);
+}
+
+vec2 pack_ycocg(bool even, vec3 color) {
+ return even ? color.rg:color.rb;
+}
+
+
+bool ycocg_even(ivec2 crd) {
+ return (crd.x&1) ==(crd.y&1);
+}
+
+#define YCOCG_THRESH vec4(30.0/255.0)
+//Returns the missing chrominance (Co or Cg) of a pixel.
+//a1-a4 are the 4 neighbors of the center pixel a0.
+float ycocg_filter(vec2 a0, vec2 a1, vec2 a2, vec2 a3, vec2 a4)
+{
+ vec4 lum = vec4(a1.x, a2.x , a3.x, a4.x);
+ vec4 l2 = vec4(a1.y, a2.y, a3.y, a4.y);
+ vec4 w = 1.0-step(YCOCG_THRESH, abs(lum - a0.x));
+ float W = w.x + w.y + w.z + w.w;
+ //handle the special case where all the weights are zero
+ vec2 ww = (W==0.0)?vec2(1.0,1.0):vec2(w.x,W);
+ //w.x = (W==0.0)? 1.0:w.x; W = (W==0.0)? 1.0:W;
+ return (dot(vec4(ww.x,w.yzw),l2))/ww.y;
+}
+
+vec3 ycocg_texture(in sampler2D sampler, in vec2 uv, in bool even) {
+ vec2 y0 = texture(sampler, uv).rg;
+ vec2 y1 = textureOffset(sampler, uv, ivec2( 1, 0)).rg;
+ vec2 y2 = textureOffset(sampler, uv, ivec2( 0, 1)).rg;
+ vec2 y3 = textureOffset(sampler, uv, ivec2(-1, 0)).rg;
+ vec2 y4 = textureOffset(sampler, uv, ivec2( 0,-1)).rg;
+ return unpack_ycocg(even, y0.rg, ycocg_filter(y0.rg, y1, y2, y3, y4));
+}
+
+vec3 ycocg_texture2(in sampler2D sampler, in vec2 uv, in bool even, out vec3 ycr2) {
+ vec4 y0 = texture(sampler, uv);
+ vec4 y1 = textureOffset(sampler, uv, ivec2( 1, 0));
+ vec4 y2 = textureOffset(sampler, uv, ivec2( 0, 1));
+ vec4 y3 = textureOffset(sampler, uv, ivec2(-1, 0));
+ vec4 y4 = textureOffset(sampler, uv, ivec2( 0,-1));
+ ycr2 = unpack_ycocg(even, y0.ba, ycocg_filter(y0.ba, y1.ba, y2.ba, y3.ba, y4.ba));
+ return unpack_ycocg(even, y0.rg, ycocg_filter(y0.rg, y1.rg, y2.rg, y3.rg, y4.rg));
+}
+
+vec2 ycocg_frag_rgb8(bool even, in vec3 color) {
+ vec2 yc = pack_ycocg(even, rgb_to_ycocg(color));
+ yc.g += 0.5;
+ return yc;
+}
+
+vec3 ycocg_texture_rg8(in sampler2D sampler, in vec2 uv, in bool even) {
+ vec3 ycr = ycocg_texture(sampler, uv, even);
+ ycr.gb -= vec2(0.5);
+ return ycocg_to_rgb(ycr);
+}
+
+vec3 ycocg_texture_rgba8(in sampler2D sampler, in vec2 uv, in bool even, out vec3 ycr2) {
+ vec3 ycr = ycocg_texture2(sampler, uv, even, ycr2);
+ ycr.gb -= vec2(0.5);
+ ycr2.gb -= vec2(0.5);
+ ycr2 = ycocg_to_rgb(ycr2);
+ return ycocg_to_rgb(ycr);
+}
A => liminal_legacy/liminal/assets/shaders/nm/nm_frag.glsl +9 -0
@@ 0,0 1,9 @@
+
+#include "nm/nm_vars.glsl"
+
+vec3 get_mapped_normal(in sampler2D normal_map, in vec2 uv) {
+ vec3 n = texture2D(normal_map, uv).rgb*2.0 - 1.0;
+ n.y *= -1.0;
+ return mtx_tangent * n;
+}
+
A => liminal_legacy/liminal/assets/shaders/nm/nm_vars.glsl +2 -0
@@ 0,0 1,2 @@
+
+varying mat3 mtx_tangent;
A => liminal_legacy/liminal/assets/shaders/nm/nm_vert.glsl +10 -0
@@ 0,0 1,10 @@
+
+#include "nm/nm_vars.glsl"
+
+void calc_mtx_tangent() {
+ mat3 mtx_mv3 = mat3(mtx_modelview);
+
+ vec3 tangent = mtx_mv3 * normalize(in_Tangent);
+ vec3 bitangent = mtx_mv3 * normalize(in_Bitangent);
+ mtx_tangent = mat3(tangent,bitangent,normal);
+}
A => liminal_legacy/liminal/assets/shaders/pom/pom_frag.glsl +27 -0
@@ 0,0 1,27 @@
+
+#include "pom/pom_vars.glsl"
+
+vec2 calc_pom_parallax_uv(in sampler2D height_map, in vec2 uv) {
+ float h0 = texture2D(height_map, uv - pom_parallax * 1.000).r;
+ float h1 = texture2D(height_map, uv - pom_parallax * 0.875).r;
+ float h2 = texture2D(height_map, uv - pom_parallax * 0.750).r;
+ float h3 = texture2D(height_map, uv - pom_parallax * 0.625).r;
+ float h4 = texture2D(height_map, uv - pom_parallax * 0.500).r;
+ float h5 = texture2D(height_map, uv - pom_parallax * 0.375).r;
+ float h6 = texture2D(height_map, uv - pom_parallax * 0.250).r;
+ float h7 = texture2D(height_map, uv - pom_parallax * 0.125).r;
+
+ float x,y, xh, yh;
+ if (h7 > 0.875) { x = 0.875; y = 1.000; xh = h7; yh = h7; }
+ else if (h6 > 0.750) { x = 0.750; y = 0.875; xh = h6; yh = h7; }
+ else if (h5 > 0.625) { x = 0.625; y = 0.750; xh = h5; yh = h6; }
+ else if (h4 > 0.500) { x = 0.500; y = 0.625; xh = h4; yh = h5; }
+ else if (h3 > 0.375) { x = 0.375; y = 0.500; xh = h3; yh = h4; }
+ else if (h2 > 0.250) { x = 0.250; y = 0.375; xh = h2; yh = h3; }
+ else if (h1 > 0.125) { x = 0.125; y = 0.250; xh = h1; yh = h2; }
+ else { x = 0.000; y = 0.125; xh = h0; yh = h1; }
+
+ float fx = (x * (y - yh) - y * (x - xh)) / ((y - yh) - (x - xh));
+
+ return uv - pom_parallax * (1.0 - fx);
+}
A => liminal_legacy/liminal/assets/shaders/pom/pom_vars.glsl +3 -0
@@ 0,0 1,3 @@
+
+varying vec2 pom_parallax;
+
A => liminal_legacy/liminal/assets/shaders/pom/pom_vert.glsl +24 -0
@@ 0,0 1,24 @@
+
+#include "pom/pom_vars.glsl"
+
+const float pom_height_scale = 0.5;
+const float pom_perspective_bias = 0.8;
+
+void calc_pom_parallax() {
+ mat3 mtx_mv3 = mat3(mtx_modelview);
+
+ vec3 view = -vec3(mtx_modelview * gl_Vertex);
+
+ vec3 vnormal = mtx_mv3 * normalize(cross(in_Tangent, in_Bitangent));
+ vec3 vtangent = mtx_mv3 * in_Tangent;
+ vec3 vbitangent = mtx_mv3 * in_Bitangent;
+
+ vec3 viewts = transpose(mat3(vtangent, vbitangent, vnormal)) * normalize(view);
+
+ vec2 parallax_dir = normalize(viewts.xy);
+ float parallax_len = -sqrt(1.0 - viewts.z * viewts.z) / viewts.z;
+ float parallax_bias = pom_perspective_bias + (1.0 - pom_perspective_bias) *
+ (2.0 * viewts.z - 1.0);
+
+ pom_parallax = -parallax_dir * parallax_len * parallax_bias * pom_height_scale;
+}
A => liminal_legacy/liminal/assets/shaders/ppfx/blit.glsl +11 -0
@@ 0,0 1,11 @@
+#include "std/std.glsl"
+#include "copy.glsl"
+
+#if FRAGMENT_SHADER
+
+uniform sampler2D fb_color0;
+
+void main() {
+ out_Color = texture(fb_color0, screen_texcoord);
+}
+#endif
A => liminal_legacy/liminal/assets/shaders/ppfx/blit_layers.glsl +63 -0
@@ 0,0 1,63 @@
+
+#ifndef INT_LAYER
+#define INT_LAYER 0
+#endif
+
+#ifndef FAKE_VERTEX_LAYER
+#define FAKE_VERTEX_LAYER 1
+#endif
+
+#if INT_LAYER
+#define LAYER_TYPE int
+#define LAYER_INIT 0
+#define DATA_QUAL flat
+#else
+#define LAYER_TYPE float
+#define LAYER_INIT 0.0
+#define DATA_QUAL
+#endif
+
+struct BlitLayerData {
+ LAYER_TYPE layer;
+};
+
+#if VERTEX_SHADER
+
+#if FAKE_VERTEX_LAYER
+DATA_QUAL out LAYER_TYPE vdata;
+#endif
+
+void main(void) {
+ gl_Position = vec4(in_Position.xy, 0.0, 1.0);
+#if FAKE_VERTEX_LAYER
+ vdata = LAYER_INIT;
+#endif
+}
+#elif GEOMETRY_SHADER
+layout(triangles) in;
+layout(triangle_strip, max_vertices = LAYER_GS_VERTEX_COUNT) out;
+
+#if FAKE_VERTEX_LAYER
+DATA_QUAL in LAYER_TYPE vdata[3];
+#endif
+DATA_QUAL out BlitLayerData data;
+
+void main() {
+ for (int k = 0; k < LAYER_COUNT; ++k) {
+ for(int i = 0; i < 3; ++i) {
+ gl_Layer = k;
+ // hack to fix buggy intel MESA driver: pass through
+ // fake layer attribute so fragment shader doesn't
+ // optimize it away.
+ data.layer =
+#if FAKE_VERTEX_LAYER
+ vdata[i] +
+#endif
+ LAYER_TYPE(k);
+ gl_Position = gl_in[i].gl_Position;
+ EmitVertex();
+ }
+ EndPrimitive();
+ }
+}
+#endif
No newline at end of file
A => liminal_legacy/liminal/assets/shaders/ppfx/bloom_mixdown.glsl +43 -0
@@ 0,0 1,43 @@
+#include "std/std.glsl"
+
+#ifndef SCALE_WITH_DEPTH
+#define SCALE_WITH_DEPTH 0
+#endif
+
+#if SCALE_WITH_DEPTH
+#include "g/ppfx/view_ray.glsl"
+#else
+#include "copy.glsl"
+#endif
+
+
+#if FRAGMENT_SHADER
+
+uniform sampler2D fb_color0;
+uniform sampler2D bloom_color0;
+uniform sampler2D depth_texture;
+uniform float mix_factor = 0.5;
+uniform vec2 depth_range = vec2(100.0, 200.0);
+
+void main() {
+ vec2 uv = screen_texcoord;
+#if SCALE_WITH_DEPTH
+ float depth = texture(depth_texture, uv).b;
+ float depth_intensity = clamp(
+ (length(view_ray * depth) - depth_range[0]) / (depth_range[1] - depth_range[0]), 0.0, 1.0);
+#endif
+
+#if SCALE_WITH_DEPTH
+ float f = mix(mix_factor, 1.0, depth_intensity);
+#else
+ float f = mix_factor;
+#endif
+
+ vec3 color = texture(fb_color0, uv).rgb;
+
+ color = mix(color, texture(bloom_color0, uv).rgb, f);
+
+ out_Color = vec4(color, 1.0);
+}
+
+#endif
No newline at end of file
A => liminal_legacy/liminal/assets/shaders/ppfx/blur.glsl +76 -0
@@ 0,0 1,76 @@
+#include "std/std.glsl"
+#include "copy.glsl"
+
+#if FRAGMENT_SHADER
+
+#ifndef BLUR_ADD
+#define BLUR_ADD 0
+#endif
+
+#ifndef SCALE_UV
+#define SCALE_UV 0
+#endif
+
+#ifndef MIX_DEPTH
+#define MIX_DEPTH 0
+#endif
+
+#ifndef RESCALE
+#define RESCALE 0
+#endif
+
+#ifndef KERNEL_RADIUS
+#define KERNEL_RADIUS 3
+#endif
+
+uniform sampler2D fb_color0;
+uniform vec2 fb_texel;
+uniform vec2 fb_size;
+
+#if BLUR_ADD
+uniform sampler2D fb_add_color0;
+#endif
+
+#if SCALE_UV
+uniform vec2 aspect;
+#endif
+
+vec3 sample_blur_texture(vec2 uv, vec2 offset) {
+ return texture(fb_color0, uv + offset).rgb;
+}
+
+#define BLUR_TYPE vec3
+#include "lib/blur.glsl"
+
+void main() {
+ vec2 uv = screen_texcoord;
+ vec3 color;
+#if SCALE_UV
+ uv *= aspect;
+#endif
+#if KERNEL_RADIUS == 7
+ color = blur_texture_fastgauss_7x7(uv, fb_texel, fb_size);
+#elif KERNEL_RADIUS == 5
+ color = blur_texture_fastgauss_5x5(uv, fb_texel, fb_size);
+#elif KERNEL_RADIUS == 3
+ color = blur_texture_fastgauss_3x3(uv, fb_texel, fb_size);
+#elif KERNEL_RADIUS == 2
+ // gaussian 4x4, using bilinear filtering to reduce fetches
+ color = texture(fb_color0, uv + 0.75*vec2( fb_texel.x, fb_texel.y)).rgb;
+ color += texture(fb_color0, uv + 0.75*vec2( fb_texel.x,-fb_texel.y)).rgb;
+ color += texture(fb_color0, uv + 0.75*vec2(-fb_texel.x,-fb_texel.y)).rgb;
+ color += texture(fb_color0, uv + 0.75*vec2(-fb_texel.x, fb_texel.y)).rgb;
+ color /= 4.0;
+#else
+ color = texture(fb_color0, uv).rgb;
+#endif
+#if BLUR_ADD
+ color += texture(fb_add_color0, uv).rgb;
+#endif
+#ifdef SCALE_COLOR
+ color *= SCALE_COLOR;
+#endif
+ out_Color = vec4(color, 1.0);
+}
+
+#endif
No newline at end of file
A => liminal_legacy/liminal/assets/shaders/ppfx/copy.glsl +14 -0
@@ 0,0 1,14 @@
+noperspective varying vec2 window_xy;
+noperspective varying vec2 absolute_texcoord;
+noperspective varying vec2 screen_texcoord;
+
+#if VERTEX_SHADER
+
+void main(void) {
+ gl_Position = vec4(in_Position.xy, 0.0, 1.0);
+ window_xy = in_Position.xy;
+ absolute_texcoord = window_xy * 0.5 + 0.5;
+ screen_texcoord = absolute_texcoord * viewport_scale + viewport_offset;
+}
+
+#endif
No newline at end of file
A => liminal_legacy/liminal/assets/shaders/ppfx/copy_luma_ds2.glsl +17 -0
@@ 0,0 1,17 @@
+#include "std/std.glsl"
+#include "copy.glsl"
+
+#if FRAGMENT_SHADER
+
+uniform sampler2D fb_color0;
+uniform vec2 fb_texel;
+
+void main() {
+ out_Color.r =
+ (texture(fb_color0, absolute_texcoord + vec2(-fb_texel.x*0.5,-fb_texel.y*0.5)).r
+ + texture(fb_color0, absolute_texcoord + vec2( fb_texel.x*0.5,-fb_texel.y*0.5)).r
+ + texture(fb_color0, absolute_texcoord + vec2(-fb_texel.x*0.5, fb_texel.y*0.5)).r
+ + texture(fb_color0, absolute_texcoord + vec2( fb_texel.x*0.5, fb_texel.y*0.5)).r) / 4.0;
+}
+
+#endif
No newline at end of file
A => liminal_legacy/liminal/assets/shaders/ppfx/debug_color.glsl +15 -0
@@ 0,0 1,15 @@
+#include "std/std.glsl"
+#include "copy.glsl"
+
+#if FRAGMENT_SHADER
+
+uniform sampler2D fb_color0;
+
+void main() {
+ vec2 uv = screen_texcoord;
+ uv = ((uv - viewport_offset) / viewport_scale);
+
+ out_Color = vec4(mod(texture(fb_color0, uv).rgb, vec3(2.0)), 1.0);
+}
+
+#endif
No newline at end of file
A => liminal_legacy/liminal/assets/shaders/ppfx/debug_layer_color.glsl +22 -0
@@ 0,0 1,22 @@
+#include "std/std.glsl"
+#include "copy.glsl"
+
+#if FRAGMENT_SHADER
+
+uniform sampler2DArray fb_color0;
+uniform float layers;
+
+const float stepsize = 100.0;
+
+void main() {
+ vec2 uv = screen_texcoord;
+ uv = ((uv - viewport_offset) / viewport_scale);
+
+ float layer = uv.x * layers;
+ layer -= mod(layer, 1.0);
+ vec3 rgb = mod(texture(fb_color0, vec3(uv,layer)).rgb, vec3(2.0));
+
+ out_Color = vec4(rgb, 1.0);
+}
+
+#endif
A => liminal_legacy/liminal/assets/shaders/ppfx/debug_layer_depth.glsl +22 -0
@@ 0,0 1,22 @@
+#include "std/std.glsl"
+#include "copy.glsl"
+
+#if FRAGMENT_SHADER
+
+uniform sampler2DArray fb_color0;
+uniform float layers;
+
+const float stepsize = 100.0;
+
+void main() {
+ vec2 uv = screen_texcoord;
+ uv = ((uv - viewport_offset) / viewport_scale);
+
+ float layer = uv.x * layers;
+ layer -= mod(layer, 1.0);
+ float d = mod(texture(fb_color0, vec3(uv,layer)).r * stepsize, 1.0);
+
+ out_Color = vec4(vec3(d), 1.0);
+}
+
+#endif
No newline at end of file
A => liminal_legacy/liminal/assets/shaders/ppfx/debug_material.glsl +175 -0
@@ 0,0 1,175 @@
+#include "std/std.glsl"
+#include "copy.glsl"
+
+#if FRAGMENT_SHADER
+
+#include "lib/brdf.glsl"
+#include "lib/srgb.glsl"
+#include "lib/ibl_sample.glsl"
+
+uniform sampler2D tex_mat0;
+uniform sampler2D tex_mat1;
+uniform samplerCube envmap;
+uniform vec3 light_pos = vec3(0.75, 0.75, 0.5);
+uniform float uv_scale = 1.0;
+uniform float ao_factor = 1.0;
+uniform vec3 light_color = vec3(1.0);
+uniform float ambient_factor = 1.0;
+uniform float roughness = 0.0;
+uniform float metallic = 0.0;
+uniform float emissive_factor = 0.5;
+uniform float emissive_offset = 0.5;
+uniform vec3 albedo_color = vec3(1.0);
+uniform vec3 glow_color = vec3(1.0);
+uniform bool show_objid = false;
+uniform bool use_metallic = true;
+uniform bool use_roughness = true;
+uniform int render_mode = 0;
+uniform float sharpness = 1.0;
+
+mat3 mtx_rot(float a) {
+ float sa = sin(a);
+ float ca = cos(a);
+ return mat3(
+ ca, 0.0, -sa,
+ 0.0, 1.0, 0.0,
+ sa, 0.0, ca);
+}
+
+vec3 cubemap_vector(in vec3 v) {
+ return vec3(v.xz, -v.y);
+}
+
+vec3 color_from_objid(int i) {
+ switch(i) {
+ case 1: return vec3(1.0,0.1,0.1);
+ case 2: return vec3(0.1,1.0,0.1);
+ case 3: return vec3(1.0,1.0,0.1);
+ case 4: return vec3(0.1,0.1,1.0);
+ case 5: return vec3(1.0,0.1,1.0);
+ case 6: return vec3(0.1,1.0,1.0);
+ default: return vec3(1.0);
+ }
+}
+
+vec3 ff_filmic_gamma3(vec3 linear) {
+ vec3 x = max(vec3(0.0), linear-0.004);
+ return (x*(x*6.2+0.5))/(x*(x*6.2+1.7)+0.06);
+}
+
+vec4 render_final(vec2 uv, vec4 mat0, vec4 mat1) {
+ vec3 position = vec3(uv, 0.0);
+
+ mat0.xy = mat0.xy*2.0 - 1.0;
+ vec3 normal = normalize(vec3(mat0.xy, sqrt(1.0-mat0.x*mat0.x-mat0.y*mat0.y)));
+
+ vec3 l = normalize(light_pos - position);
+ vec3 v = normalize(vec3(0.5, 0.5, 1.0) - position);
+ //vec3 v = vec3(0.0,0.0,1.0);
+
+ //float C1 = max(1.0, sharpness / fwidth(uv.(max(abs(dFdx(uv.s)), abs(dFdy(uv.t))) * 32.0));
+
+ vec2 fw = fwidth(uv);
+ float C1 = max(1.0, sharpness / max(fw.x,fw.y));
+ float C0 = 0.5*(1.0-C1);
+
+ float cos_Ol = max(0.0, dot(normal, l));
+ float ao = mix(1.0,mat1.x,ao_factor);
+ float e = clamp(C1*mat1.y + C0, 0.0, 1.0);
+ float r, m;
+ if (use_roughness)
+ r = mat1.z;
+ else
+ r = 0.5;
+ if (use_metallic)
+ m = mat1.w;
+ else
+ m = 0.5;
+
+ r = clamp(roughness + r, 0.0, 1.0);
+ m = clamp(metallic + m, 0.0, 1.0);
+
+ vec3 light = vec3(cos_Ol);
+
+ vec3 base_color;
+ if (show_objid)
+ base_color = color_from_objid(int(mat0.w*255.0));
+ else
+ base_color = mix(albedo_color, glow_color, e);
+
+ mat3 mtx = mtx_rot(time * 0.1);
+
+ // setup specular+diffuse IBL
+ ibla = IBLAttributes(
+ m,
+ r,
+ base_color,
+ mtx * v,
+ mtx * normal
+ );
+ // diffuse IBL
+ vec3 ibl_diff = vec3(0.0);
+ vec3 ibl_spec = ibl_sample_specular_diffuse(envmap, ibl_diff);
+
+ brdfa.metallic = m;
+ brdfa.roughness = r;
+ brdfa.base_color = base_color;
+ brdfa.cos_Ol = cos_Ol;
+ brdfa.l = l;
+ brdfa.v = v;
+ brdfa.n = normal;
+ vec3 color = brdf_evaluate() * cos_Ol;
+
+ color *= light * light_color;
+ //color += base_color * 0.05f * (1.0f - m);
+ color += (ibl_diff * (1.0 - metallic) + ibl_spec / mix(M_PI, 1.0, m)) * ambient_factor * 0.05f;
+
+ color *= ao;
+
+ color += base_color * e * emissive_factor * light;
+
+ color = clamp(ff_filmic_gamma3(color*2.0), 0.0, 1.0);
+ return vec4(color, 1.0);
+}
+
+void main() {
+ vec2 uv = screen_texcoord;
+ uv = ((uv - viewport_offset) / viewport_scale);
+
+ uv *= uv_scale;
+
+ // Nx, Ny, Depth, OBJID
+ vec4 mat0 = texture(tex_mat0, uv);
+ // AO, Emissive, Roughness, Metallic
+ vec4 mat1 = texture(tex_mat1, uv);
+
+ switch(render_mode) {
+ case 0: {
+ out_Color = render_final(uv, mat0, mat1);
+ } break;
+ case 1: {
+ out_Color = vec4(mat0.xy, 0.0, 1.0);
+ } break;
+ case 2: {
+ out_Color = vec4(vec3(mat0.z), 1.0);
+ } break;
+ case 3: {
+ out_Color = vec4(vec3(mat0.w), 1.0);
+ } break;
+ case 4: {
+ out_Color = vec4(vec3(mat1.x), 1.0);
+ } break;
+ case 5: {
+ out_Color = vec4(vec3(mat1.y), 1.0);
+ } break;
+ case 6: {
+ out_Color = vec4(vec3(mat1.z), 1.0);
+ } break;
+ case 7: {
+ out_Color = vec4(vec3(mat1.w), 1.0);
+ } break;
+ default: break;
+ }
+}
+
+#endif
No newline at end of file
A => liminal_legacy/liminal/assets/shaders/ppfx/debug_normal.glsl +18 -0
@@ 0,0 1,18 @@
+#include "std/std.glsl"
+#include "copy.glsl"
+#include "lib/normal_codec.glsl"
+
+#if FRAGMENT_SHADER
+
+uniform sampler2D fb_color0;
+
+void main() {
+ vec2 uv = screen_texcoord;
+ uv = ((uv - viewport_offset) / viewport_scale);
+
+ vec3 normal = decode_normal(texture(fb_color0, uv).rg)*0.5+0.5;
+
+ out_Color = vec4(normal, 1.0);
+}
+
+#endif
No newline at end of file
A => liminal_legacy/liminal/assets/shaders/ppfx/debug_velocity.glsl +19 -0
@@ 0,0 1,19 @@
+#include "std/std.glsl"
+#include "copy.glsl"
+
+#if FRAGMENT_SHADER
+
+uniform isampler2D fb_color0;
+
+void main() {
+ vec2 uv = screen_texcoord;
+ uv = ((uv - viewport_offset) / viewport_scale);
+
+ vec2 velocity = vec2(texture(fb_color0, uv).rg) / 127.0;
+ // exaggerate
+ velocity *= 100.0;
+
+ out_Color = vec4(velocity*0.5 + 0.5, 0.0, 1.0);
+}
+
+#endif
No newline at end of file
A => liminal_legacy/liminal/assets/shaders/ppfx/debug_ycocg.glsl +30 -0
@@ 0,0 1,30 @@
+#include "std/std.glsl"
+#include "copy.glsl"
+#include "lib/ycocg.glsl"
+
+#if FRAGMENT_SHADER
+
+uniform sampler2D fb_color0;
+uniform float fb_texel;
+uniform vec2 fb_size;
+
+
+void main() {
+ vec2 uv = screen_texcoord;
+ uv = ((uv - viewport_offset) / viewport_scale);
+
+ ivec2 coord = ivec2(uv * fb_size);
+ bool even = ycocg_even(coord);
+ vec2 fuv = (vec2(coord)+0.5) / fb_size;
+
+ vec3 uvlight;
+ vec3 color = ycocg_texture_rgba8(fb_color0, fuv, even, uvlight);
+
+ if (uv.x < 0.5) {
+ out_Color = vec4(color, 1.0);
+ } else {
+ out_Color = vec4(uvlight, 1.0);
+ }
+}
+
+#endif
No newline at end of file
A => liminal_legacy/liminal/assets/shaders/ppfx/derivative_map.glsl +26 -0
@@ 0,0 1,26 @@
+#include "std/std.glsl"
+#include "ppfx/copy.glsl"
+
+#if FRAGMENT_SHADER
+
+uniform sampler2D height_map;
+
+void main() {
+ vec2 uv = absolute_texcoord;
+
+#if 1
+ vec2 d = vec2(1.0) / vec2(textureSize(height_map, 0));
+ vec2 dx = vec2(d.x, 0.0);
+ vec2 dy = vec2(0.0, d.y);
+#else
+ vec2 dx = dFdx(uv);
+ vec2 dy = dFdy(uv);
+#endif
+
+ float x = texture(height_map, uv).r;
+ float s = texture(height_map, uv + dx).r - x;
+ float t = texture(height_map, uv + dy).r - x;
+
+ out_Color = vec4(vec2(s, t)*0.5 + 0.5, 0.0, 0.0);
+}
+#endif
A => liminal_legacy/liminal/assets/shaders/ppfx/fxaa3_11.glsl +2246 -0
@@ 0,0 1,2246 @@
+
+#include "std/std.glsl"
+
+#if VERTEX_SHADER
+
+noperspective varying vec2 texcoord;
+
+void main(void) {
+ gl_Position = vec4(in_Position.xy, 0.0, 1.0);
+ texcoord = (in_Position.xy + 1.0) / 2.0;
+ texcoord = texcoord * viewport_scale + viewport_offset;
+}
+
+#elif FRAGMENT_SHADER
+// modified shader with included lookup
+
+// custom configuration for beige
+#define FXAA_PC 1
+#define FXAA_GLSL_130 1
+#define FXAA_GREEN_AS_LUMA 0
+#define FXAA_GATHER4_ALPHA 0
+
+#if VIDEO_QUALITY > VQ_HIGHEST
+#define FXAA_QUALITY_PRESET 39
+#elif VIDEO_QUALITY >= VQ_HIGHEST
+#define FXAA_QUALITY_PRESET 29
+#elif VIDEO_QUALITY >= VQ_HIGHER
+#define FXAA_QUALITY_PRESET 20
+#else
+#define FXAA_QUALITY_PRESET 12
+#endif
+
+/*============================================================================
+
+
+ NVIDIA FXAA 3.11 by TIMOTHY LOTTES
+
+
+------------------------------------------------------------------------------
+COPYRIGHT (C) 2010, 2011 NVIDIA CORPORATION. ALL RIGHTS RESERVED.
+------------------------------------------------------------------------------
+TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THIS SOFTWARE IS PROVIDED
+*AS IS* AND NVIDIA AND ITS SUPPLIERS DISCLAIM ALL WARRANTIES, EITHER EXPRESS
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL NVIDIA
+OR ITS SUPPLIERS BE LIABLE FOR ANY SPECIAL, INCIDENTAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES WHATSOEVER (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR
+LOSS OF BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION,
+OR ANY OTHER PECUNIARY LOSS) ARISING OUT OF THE USE OF OR INABILITY TO USE
+THIS SOFTWARE, EVEN IF NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+------------------------------------------------------------------------------
+ INTEGRATION CHECKLIST
+------------------------------------------------------------------------------
+(1.)
+In the shader source, setup defines for the desired configuration.
+When providing multiple shaders (for different presets),
+simply setup the defines differently in multiple files.
+Example,
+
+ #define FXAA_PC 1
+ #define FXAA_HLSL_5 1
+ #define FXAA_QUALITY_PRESET 12
+
+Or,
+
+ #define FXAA_360 1
+
+Or,
+
+ #define FXAA_PS3 1
+
+Etc.
+
+(2.)
+Then include this file,
+
+ #include "Fxaa3_11.h"
+
+(3.)
+Then call the FXAA pixel shader from within your desired shader.
+Look at the FXAA Quality FxaaPixelShader() for docs on inputs.
+As for FXAA 3.11 all inputs for all shaders are the same
+to enable easy porting between platforms.
+
+ return FxaaPixelShader(...);
+
+(4.)
+Insure pass prior to FXAA outputs RGBL (see next section).
+Or use,
+
+ #define FXAA_GREEN_AS_LUMA 1
+
+(5.)
+Setup engine to provide the following constants
+which are used in the FxaaPixelShader() inputs,
+
+ FxaaFloat2 fxaaQualityRcpFrame,
+ FxaaFloat4 fxaaConsoleRcpFrameOpt,
+ FxaaFloat4 fxaaConsoleRcpFrameOpt2,
+ FxaaFloat4 fxaaConsole360RcpFrameOpt2,
+ FxaaFloat fxaaQualitySubpix,
+ FxaaFloat fxaaQualityEdgeThreshold,
+ FxaaFloat fxaaQualityEdgeThresholdMin,
+ FxaaFloat fxaaConsoleEdgeSharpness,
+ FxaaFloat fxaaConsoleEdgeThreshold,
+ FxaaFloat fxaaConsoleEdgeThresholdMin,
+ FxaaFloat4 fxaaConsole360ConstDir
+
+Look at the FXAA Quality FxaaPixelShader() for docs on inputs.
+
+(6.)
+Have FXAA vertex shader run as a full screen triangle,
+and output "pos" and "fxaaConsolePosPos"
+such that inputs in the pixel shader provide,
+
+ // {xy} = center of pixel
+ FxaaFloat2 pos,
+
+ // {xy__} = upper left of pixel
+ // {__zw} = lower right of pixel
+ FxaaFloat4 fxaaConsolePosPos,
+
+(7.)
+Insure the texture sampler(s) used by FXAA are set to bilinear filtering.
+
+
+------------------------------------------------------------------------------
+ INTEGRATION - RGBL AND COLORSPACE
+------------------------------------------------------------------------------
+FXAA3 requires RGBL as input unless the following is set,
+
+ #define FXAA_GREEN_AS_LUMA 1
+
+In which case the engine uses green in place of luma,
+and requires RGB input is in a non-linear colorspace.
+
+RGB should be LDR (low dynamic range).
+Specifically do FXAA after tonemapping.
+
+RGB data as returned by a texture fetch can be non-linear,
+or linear when FXAA_GREEN_AS_LUMA is not set.
+Note an "sRGB format" texture counts as linear,
+because the result of a texture fetch is linear data.
+Regular "RGBA8" textures in the sRGB colorspace are non-linear.
+
+If FXAA_GREEN_AS_LUMA is not set,
+luma must be stored in the alpha channel prior to running FXAA.
+This luma should be in a perceptual space (could be gamma 2.0).
+Example pass before FXAA where output is gamma 2.0 encoded,
+
+ color.rgb = ToneMap(color.rgb); // linear color output
+ color.rgb = sqrt(color.rgb); // gamma 2.0 color output
+ return color;
+
+To use FXAA,
+
+ color.rgb = ToneMap(color.rgb); // linear color output
+ color.rgb = sqrt(color.rgb); // gamma 2.0 color output
+ color.a = dot(color.rgb, FxaaFloat3(0.299, 0.587, 0.114)); // compute luma
+ return color;
+
+Another example where output is linear encoded,
+say for instance writing to an sRGB formated render target,
+where the render target does the conversion back to sRGB after blending,
+
+ color.rgb = ToneMap(color.rgb); // linear color output
+ return color;
+
+To use FXAA,
+
+ color.rgb = ToneMap(color.rgb); // linear color output
+ color.a = sqrt(dot(color.rgb, FxaaFloat3(0.299, 0.587, 0.114))); // compute luma
+ return color;
+
+Getting luma correct is required for the algorithm to work correctly.
+
+
+------------------------------------------------------------------------------
+ BEING LINEARLY CORRECT?
+------------------------------------------------------------------------------
+Applying FXAA to a framebuffer with linear RGB color will look worse.
+This is very counter intuitive, but happends to be true in this case.
+The reason is because dithering artifacts will be more visiable
+in a linear colorspace.
+
+
+------------------------------------------------------------------------------
+ COMPLEX INTEGRATION
+------------------------------------------------------------------------------
+Q. What if the engine is blending into RGB before wanting to run FXAA?
+
+A. In the last opaque pass prior to FXAA,
+ have the pass write out luma into alpha.
+ Then blend into RGB only.
+ FXAA should be able to run ok
+ assuming the blending pass did not any add aliasing.
+ This should be the common case for particles and common blending passes.
+
+A. Or use FXAA_GREEN_AS_LUMA.
+
+============================================================================*/
+
+/*============================================================================
+
+ INTEGRATION KNOBS
+
+============================================================================*/
+//
+// FXAA_PS3 and FXAA_360 choose the console algorithm (FXAA3 CONSOLE).
+// FXAA_360_OPT is a prototype for the new optimized 360 version.
+//
+// 1 = Use API.
+// 0 = Don't use API.
+//
+/*--------------------------------------------------------------------------*/
+#ifndef FXAA_PS3
+ #define FXAA_PS3 0
+#endif
+/*--------------------------------------------------------------------------*/
+#ifndef FXAA_360
+ #define FXAA_360 0
+#endif
+/*--------------------------------------------------------------------------*/
+#ifndef FXAA_360_OPT
+ #define FXAA_360_OPT 0
+#endif
+/*==========================================================================*/
+#ifndef FXAA_PC
+ //
+ // FXAA Quality
+ // The high quality PC algorithm.
+ //
+ #define FXAA_PC 0
+#endif
+/*--------------------------------------------------------------------------*/
+#ifndef FXAA_PC_CONSOLE
+ //
+ // The console algorithm for PC is included
+ // for developers targeting really low spec machines.
+ // Likely better to just run FXAA_PC, and use a really low preset.
+ //
+ #define FXAA_PC_CONSOLE 0
+#endif
+/*--------------------------------------------------------------------------*/
+#ifndef FXAA_GLSL_120
+ #define FXAA_GLSL_120 0
+#endif
+/*--------------------------------------------------------------------------*/
+#ifndef FXAA_GLSL_130
+ #define FXAA_GLSL_130 0
+#endif
+/*--------------------------------------------------------------------------*/
+#ifndef FXAA_HLSL_3
+ #define FXAA_HLSL_3 0
+#endif
+/*--------------------------------------------------------------------------*/
+#ifndef FXAA_HLSL_4
+ #define FXAA_HLSL_4 0
+#endif
+/*--------------------------------------------------------------------------*/
+#ifndef FXAA_HLSL_5
+ #define FXAA_HLSL_5 0
+#endif
+/*==========================================================================*/
+#ifndef FXAA_GREEN_AS_LUMA
+ //
+ // For those using non-linear color,
+ // and either not able to get luma in alpha, or not wanting to,
+ // this enables FXAA to run using green as a proxy for luma.
+ // So with this enabled, no need to pack luma in alpha.
+ //
+ // This will turn off AA on anything which lacks some amount of green.
+ // Pure red and blue or combination of only R and B, will get no AA.
+ //
+ // Might want to lower the settings for both,
+ // fxaaConsoleEdgeThresholdMin
+ // fxaaQualityEdgeThresholdMin
+ // In order to insure AA does not get turned off on colors
+ // which contain a minor amount of green.
+ //
+ // 1 = On.
+ // 0 = Off.
+ //
+ #define FXAA_GREEN_AS_LUMA 0
+#endif
+/*--------------------------------------------------------------------------*/
+#ifndef FXAA_EARLY_EXIT
+ //
+ // Controls algorithm's early exit path.
+ // On PS3 turning this ON adds 2 cycles to the shader.
+ // On 360 turning this OFF adds 10ths of a millisecond to the shader.
+ // Turning this off on console will result in a more blurry image.
+ // So this defaults to on.
+ //
+ // 1 = On.
+ // 0 = Off.
+ //
+ #define FXAA_EARLY_EXIT 1
+#endif
+/*--------------------------------------------------------------------------*/
+#ifndef FXAA_DISCARD
+ //
+ // Only valid for PC OpenGL currently.
+ // Probably will not work when FXAA_GREEN_AS_LUMA = 1.
+ //
+ // 1 = Use discard on pixels which don't need AA.
+ // For APIs which enable concurrent TEX+ROP from same surface.
+ // 0 = Return unchanged color on pixels which don't need AA.
+ //
+ #define FXAA_DISCARD 0
+#endif
+/*--------------------------------------------------------------------------*/
+#ifndef FXAA_FAST_PIXEL_OFFSET
+ //
+ // Used for GLSL 120 only.
+ //
+ // 1 = GL API supports fast pixel offsets
+ // 0 = do not use fast pixel offsets
+ //
+ #ifdef GL_EXT_gpu_shader4
+ #define FXAA_FAST_PIXEL_OFFSET 1
+ #endif
+ #ifdef GL_NV_gpu_shader5
+ #define FXAA_FAST_PIXEL_OFFSET 1
+ #endif
+ #ifdef GL_ARB_gpu_shader5
+ #define FXAA_FAST_PIXEL_OFFSET 1
+ #endif
+ #ifndef FXAA_FAST_PIXEL_OFFSET
+ #define FXAA_FAST_PIXEL_OFFSET 0
+ #endif
+#endif
+/*--------------------------------------------------------------------------*/
+#ifndef FXAA_GATHER4_ALPHA
+ //
+ // 1 = API supports gather4 on alpha channel.
+ // 0 = API does not support gather4 on alpha channel.
+ //
+ #if (FXAA_HLSL_5 == 1)
+ #define FXAA_GATHER4_ALPHA 1
+ #endif
+ #ifdef GL_ARB_gpu_shader5
+ #define FXAA_GATHER4_ALPHA 1
+ #endif
+ #ifdef GL_NV_gpu_shader5
+ #define FXAA_GATHER4_ALPHA 1
+ #endif
+ #ifndef FXAA_GATHER4_ALPHA
+ #define FXAA_GATHER4_ALPHA 0
+ #endif
+#endif
+
+/*============================================================================
+ FXAA CONSOLE PS3 - TUNING KNOBS
+============================================================================*/
+#ifndef FXAA_CONSOLE_PS3_EDGE_SHARPNESS
+ //
+ // Consoles the sharpness of edges on PS3 only.
+ // Non-PS3 tuning is done with shader input.
+ //
+ // Due to the PS3 being ALU bound,
+ // there are only two safe values here: 4 and 8.
+ // These options use the shaders ability to a free *|/ by 2|4|8.
+ //
+ // 8.0 is sharper
+ // 4.0 is softer
+ // 2.0 is really soft (good for vector graphics inputs)
+ //
+ #if 1
+ #define FXAA_CONSOLE_PS3_EDGE_SHARPNESS 8.0
+ #endif
+ #if 0
+ #define FXAA_CONSOLE_PS3_EDGE_SHARPNESS 4.0
+ #endif
+ #if 0
+ #define FXAA_CONSOLE_PS3_EDGE_SHARPNESS 2.0
+ #endif
+#endif
+/*--------------------------------------------------------------------------*/
+#ifndef FXAA_CONSOLE_PS3_EDGE_THRESHOLD
+ //
+ // Only effects PS3.
+ // Non-PS3 tuning is done with shader input.
+ //
+ // The minimum amount of local contrast required to apply algorithm.
+ // The console setting has a different mapping than the quality setting.
+ //
+ // This only applies when FXAA_EARLY_EXIT is 1.
+ //
+ // Due to the PS3 being ALU bound,
+ // there are only two safe values here: 0.25 and 0.125.
+ // These options use the shaders ability to a free *|/ by 2|4|8.
+ //
+ // 0.125 leaves less aliasing, but is softer
+ // 0.25 leaves more aliasing, and is sharper
+ //
+ #if 1
+ #define FXAA_CONSOLE_PS3_EDGE_THRESHOLD 0.125
+ #else
+ #define FXAA_CONSOLE_PS3_EDGE_THRESHOLD 0.25
+ #endif
+#endif
+
+/*============================================================================
+ FXAA QUALITY - TUNING KNOBS
+------------------------------------------------------------------------------
+NOTE the other tuning knobs are now in the shader function inputs!
+============================================================================*/
+#ifndef FXAA_QUALITY_PRESET
+ //
+ // Choose the quality preset.
+ // This needs to be compiled into the shader as it effects code.
+ // Best option to include multiple presets is to
+ // in each shader define the preset, then include this file.
+ //
+ // OPTIONS
+ // -----------------------------------------------------------------------
+ // 10 to 15 - default medium dither (10=fastest, 15=highest quality)
+ // 20 to 29 - less dither, more expensive (20=fastest, 29=highest quality)
+ // 39 - no dither, very expensive
+ //
+ // NOTES
+ // -----------------------------------------------------------------------
+ // 12 = slightly faster then FXAA 3.9 and higher edge quality (default)
+ // 13 = about same speed as FXAA 3.9 and better than 12
+ // 23 = closest to FXAA 3.9 visually and performance wise
+ // _ = the lowest digit is directly related to performance
+ // _ = the highest digit is directly related to style
+ //
+ #define FXAA_QUALITY_PRESET 12
+#endif
+
+
+/*============================================================================
+
+ FXAA QUALITY - PRESETS
+
+============================================================================*/
+
+/*============================================================================
+ FXAA QUALITY - MEDIUM DITHER PRESETS
+============================================================================*/
+#if (FXAA_QUALITY_PRESET == 10)
+ #define FXAA_QUALITY_PS 3
+ #define FXAA_QUALITY_P0 1.5
+ #define FXAA_QUALITY_P1 3.0
+ #define FXAA_QUALITY_P2 12.0
+#endif
+/*--------------------------------------------------------------------------*/
+#if (FXAA_QUALITY_PRESET == 11)
+ #define FXAA_QUALITY_PS 4
+ #define FXAA_QUALITY_P0 1.0
+ #define FXAA_QUALITY_P1 1.5
+ #define FXAA_QUALITY_P2 3.0
+ #define FXAA_QUALITY_P3 12.0
+#endif
+/*--------------------------------------------------------------------------*/
+#if (FXAA_QUALITY_PRESET == 12)
+ #define FXAA_QUALITY_PS 5
+ #define FXAA_QUALITY_P0 1.0
+ #define FXAA_QUALITY_P1 1.5
+ #define FXAA_QUALITY_P2 2.0
+ #define FXAA_QUALITY_P3 4.0
+ #define FXAA_QUALITY_P4 12.0
+#endif
+/*--------------------------------------------------------------------------*/
+#if (FXAA_QUALITY_PRESET == 13)
+ #define FXAA_QUALITY_PS 6
+ #define FXAA_QUALITY_P0 1.0
+ #define FXAA_QUALITY_P1 1.5
+ #define FXAA_QUALITY_P2 2.0
+ #define FXAA_QUALITY_P3 2.0
+ #define FXAA_QUALITY_P4 4.0
+ #define FXAA_QUALITY_P5 12.0
+#endif
+/*--------------------------------------------------------------------------*/
+#if (FXAA_QUALITY_PRESET == 14)
+ #define FXAA_QUALITY_PS 7
+ #define FXAA_QUALITY_P0 1.0
+ #define FXAA_QUALITY_P1 1.5
+ #define FXAA_QUALITY_P2 2.0
+ #define FXAA_QUALITY_P3 2.0
+ #define FXAA_QUALITY_P4 2.0
+ #define FXAA_QUALITY_P5 4.0
+ #define FXAA_QUALITY_P6 12.0
+#endif
+/*--------------------------------------------------------------------------*/
+#if (FXAA_QUALITY_PRESET == 15)
+ #define FXAA_QUALITY_PS 8
+ #define FXAA_QUALITY_P0 1.0
+ #define FXAA_QUALITY_P1 1.5
+ #define FXAA_QUALITY_P2 2.0
+ #define FXAA_QUALITY_P3 2.0
+ #define FXAA_QUALITY_P4 2.0
+ #define FXAA_QUALITY_P5 2.0
+ #define FXAA_QUALITY_P6 4.0
+ #define FXAA_QUALITY_P7 12.0
+#endif
+
+/*============================================================================
+ FXAA QUALITY - LOW DITHER PRESETS
+============================================================================*/
+#if (FXAA_QUALITY_PRESET == 20)
+ #define FXAA_QUALITY_PS 3
+ #define FXAA_QUALITY_P0 1.5
+ #define FXAA_QUALITY_P1 2.0
+ #define FXAA_QUALITY_P2 8.0
+#endif
+/*--------------------------------------------------------------------------*/
+#if (FXAA_QUALITY_PRESET == 21)
+ #define FXAA_QUALITY_PS 4
+ #define FXAA_QUALITY_P0 1.0
+ #define FXAA_QUALITY_P1 1.5
+ #define FXAA_QUALITY_P2 2.0
+ #define FXAA_QUALITY_P3 8.0
+#endif
+/*--------------------------------------------------------------------------*/
+#if (FXAA_QUALITY_PRESET == 22)
+ #define FXAA_QUALITY_PS 5
+ #define FXAA_QUALITY_P0 1.0
+ #define FXAA_QUALITY_P1 1.5
+ #define FXAA_QUALITY_P2 2.0
+ #define FXAA_QUALITY_P3 2.0
+ #define FXAA_QUALITY_P4 8.0
+#endif
+/*--------------------------------------------------------------------------*/
+#if (FXAA_QUALITY_PRESET == 23)
+ #define FXAA_QUALITY_PS 6
+ #define FXAA_QUALITY_P0 1.0
+ #define FXAA_QUALITY_P1 1.5
+ #define FXAA_QUALITY_P2 2.0
+ #define FXAA_QUALITY_P3 2.0
+ #define FXAA_QUALITY_P4 2.0
+ #define FXAA_QUALITY_P5 8.0
+#endif
+/*--------------------------------------------------------------------------*/
+#if (FXAA_QUALITY_PRESET == 24)
+ #define FXAA_QUALITY_PS 7
+ #define FXAA_QUALITY_P0 1.0
+ #define FXAA_QUALITY_P1 1.5
+ #define FXAA_QUALITY_P2 2.0
+ #define FXAA_QUALITY_P3 2.0
+ #define FXAA_QUALITY_P4 2.0
+ #define FXAA_QUALITY_P5 3.0
+ #define FXAA_QUALITY_P6 8.0
+#endif
+/*--------------------------------------------------------------------------*/
+#if (FXAA_QUALITY_PRESET == 25)
+ #define FXAA_QUALITY_PS 8
+ #define FXAA_QUALITY_P0 1.0
+ #define FXAA_QUALITY_P1 1.5
+ #define FXAA_QUALITY_P2 2.0
+ #define FXAA_QUALITY_P3 2.0
+ #define FXAA_QUALITY_P4 2.0
+ #define FXAA_QUALITY_P5 2.0
+ #define FXAA_QUALITY_P6 4.0
+ #define FXAA_QUALITY_P7 8.0
+#endif
+/*--------------------------------------------------------------------------*/
+#if (FXAA_QUALITY_PRESET == 26)
+ #define FXAA_QUALITY_PS 9
+ #define FXAA_QUALITY_P0 1.0
+ #define FXAA_QUALITY_P1 1.5
+ #define FXAA_QUALITY_P2 2.0
+ #define FXAA_QUALITY_P3 2.0
+ #define FXAA_QUALITY_P4 2.0
+ #define FXAA_QUALITY_P5 2.0
+ #define FXAA_QUALITY_P6 2.0
+ #define FXAA_QUALITY_P7 4.0
+ #define FXAA_QUALITY_P8 8.0
+#endif
+/*--------------------------------------------------------------------------*/
+#if (FXAA_QUALITY_PRESET == 27)
+ #define FXAA_QUALITY_PS 10
+ #define FXAA_QUALITY_P0 1.0
+ #define FXAA_QUALITY_P1 1.5
+ #define FXAA_QUALITY_P2 2.0
+ #define FXAA_QUALITY_P3 2.0
+ #define FXAA_QUALITY_P4 2.0
+ #define FXAA_QUALITY_P5 2.0
+ #define FXAA_QUALITY_P6 2.0
+ #define FXAA_QUALITY_P7 2.0
+ #define FXAA_QUALITY_P8 4.0
+ #define FXAA_QUALITY_P9 8.0
+#endif
+/*--------------------------------------------------------------------------*/
+#if (FXAA_QUALITY_PRESET == 28)
+ #define FXAA_QUALITY_PS 11
+ #define FXAA_QUALITY_P0 1.0
+ #define FXAA_QUALITY_P1 1.5
+ #define FXAA_QUALITY_P2 2.0
+ #define FXAA_QUALITY_P3 2.0
+ #define FXAA_QUALITY_P4 2.0
+ #define FXAA_QUALITY_P5 2.0
+ #define FXAA_QUALITY_P6 2.0
+ #define FXAA_QUALITY_P7 2.0
+ #define FXAA_QUALITY_P8 2.0
+ #define FXAA_QUALITY_P9 4.0
+ #define FXAA_QUALITY_P10 8.0
+#endif
+/*--------------------------------------------------------------------------*/
+#if (FXAA_QUALITY_PRESET == 29)
+ #define FXAA_QUALITY_PS 12
+ #define FXAA_QUALITY_P0 1.0
+ #define FXAA_QUALITY_P1 1.5
+ #define FXAA_QUALITY_P2 2.0
+ #define FXAA_QUALITY_P3 2.0
+ #define FXAA_QUALITY_P4 2.0
+ #define FXAA_QUALITY_P5 2.0
+ #define FXAA_QUALITY_P6 2.0
+ #define FXAA_QUALITY_P7 2.0
+ #define FXAA_QUALITY_P8 2.0
+ #define FXAA_QUALITY_P9 2.0
+ #define FXAA_QUALITY_P10 4.0
+ #define FXAA_QUALITY_P11 8.0
+#endif
+
+/*============================================================================
+ FXAA QUALITY - EXTREME QUALITY
+============================================================================*/
+#if (FXAA_QUALITY_PRESET == 39)
+ #define FXAA_QUALITY_PS 12
+ #define FXAA_QUALITY_P0 1.0
+ #define FXAA_QUALITY_P1 1.0
+ #define FXAA_QUALITY_P2 1.0
+ #define FXAA_QUALITY_P3 1.0
+ #define FXAA_QUALITY_P4 1.0
+ #define FXAA_QUALITY_P5 1.5
+ #define FXAA_QUALITY_P6 2.0
+ #define FXAA_QUALITY_P7 2.0
+ #define FXAA_QUALITY_P8 2.0
+ #define FXAA_QUALITY_P9 2.0
+ #define FXAA_QUALITY_P10 4.0
+ #define FXAA_QUALITY_P11 8.0
+#endif
+
+
+
+/*============================================================================
+
+ API PORTING
+
+============================================================================*/
+#if (FXAA_GLSL_120 == 1) || (FXAA_GLSL_130 == 1)
+ #define FxaaBool bool
+ #define FxaaDiscard discard
+ #define FxaaFloat float
+ #define FxaaFloat2 vec2
+ #define FxaaFloat3 vec3
+ #define FxaaFloat4 vec4
+ #define FxaaHalf float
+ #define FxaaHalf2 vec2
+ #define FxaaHalf3 vec3
+ #define FxaaHalf4 vec4
+ #define FxaaInt2 ivec2
+ #define FxaaSat(x) clamp(x, 0.0, 1.0)
+ #define FxaaTex sampler2D
+#else
+ #define FxaaBool bool
+ #define FxaaDiscard clip(-1)
+ #define FxaaFloat float
+ #define FxaaFloat2 float2
+ #define FxaaFloat3 float3
+ #define FxaaFloat4 float4
+ #define FxaaHalf half
+ #define FxaaHalf2 half2
+ #define FxaaHalf3 half3
+ #define FxaaHalf4 half4
+ #define FxaaSat(x) saturate(x)
+#endif
+/*--------------------------------------------------------------------------*/
+#if (FXAA_GLSL_120 == 1)
+ // Requires,
+ // #version 120
+ // And at least,
+ // #extension GL_EXT_gpu_shader4 : enable
+ // (or set FXAA_FAST_PIXEL_OFFSET 1 to work like DX9)
+ #define FxaaTexTop(t, p) texture2DLod(t, p, 0.0)
+ #if (FXAA_FAST_PIXEL_OFFSET == 1)
+ #define FxaaTexOff(t, p, o, r) texture2DLodOffset(t, p, 0.0, o)
+ #else
+ #define FxaaTexOff(t, p, o, r) texture2DLod(t, p + (o * r), 0.0)
+ #endif
+ #if (FXAA_GATHER4_ALPHA == 1)
+ // use #extension GL_ARB_gpu_shader5 : enable
+ #define FxaaTexAlpha4(t, p) textureGather(t, p, 3)
+ #define FxaaTexOffAlpha4(t, p, o) textureGatherOffset(t, p, o, 3)
+ #define FxaaTexGreen4(t, p) textureGather(t, p, 1)
+ #define FxaaTexOffGreen4(t, p, o) textureGatherOffset(t, p, o, 1)
+ #endif
+#endif
+/*--------------------------------------------------------------------------*/
+#if (FXAA_GLSL_130 == 1)
+ // Requires "#version 130" or better
+ #define FxaaTexTop(t, p) textureLod(t, p, 0.0)
+ #define FxaaTexOff(t, p, o, r) textureLodOffset(t, p, 0.0, o)
+ #if (FXAA_GATHER4_ALPHA == 1)
+ // use #extension GL_ARB_gpu_shader5 : enable
+ #define FxaaTexAlpha4(t, p) textureGather(t, p, 3)
+ #define FxaaTexOffAlpha4(t, p, o) textureGatherOffset(t, p, o, 3)
+ #define FxaaTexGreen4(t, p) textureGather(t, p, 1)
+ #define FxaaTexOffGreen4(t, p, o) textureGatherOffset(t, p, o, 1)
+ #endif
+#endif
+/*--------------------------------------------------------------------------*/
+#if (FXAA_HLSL_3 == 1) || (FXAA_360 == 1) || (FXAA_PS3 == 1)
+ #define FxaaInt2 float2
+ #define FxaaTex sampler2D
+ #define FxaaTexTop(t, p) tex2Dlod(t, float4(p, 0.0, 0.0))
+ #define FxaaTexOff(t, p, o, r) tex2Dlod(t, float4(p + (o * r), 0, 0))
+#endif
+/*--------------------------------------------------------------------------*/
+#if (FXAA_HLSL_4 == 1)
+ #define FxaaInt2 int2
+ struct FxaaTex { SamplerState smpl; Texture2D tex; };
+ #define FxaaTexTop(t, p) t.tex.SampleLevel(t.smpl, p, 0.0)
+ #define FxaaTexOff(t, p, o, r) t.tex.SampleLevel(t.smpl, p, 0.0, o)
+#endif
+/*--------------------------------------------------------------------------*/
+#if (FXAA_HLSL_5 == 1)
+ #define FxaaInt2 int2
+ struct FxaaTex { SamplerState smpl; Texture2D tex; };
+ #define FxaaTexTop(t, p) t.tex.SampleLevel(t.smpl, p, 0.0)
+ #define FxaaTexOff(t, p, o, r) t.tex.SampleLevel(t.smpl, p, 0.0, o)
+ #define FxaaTexAlpha4(t, p) t.tex.GatherAlpha(t.smpl, p)
+ #define FxaaTexOffAlpha4(t, p, o) t.tex.GatherAlpha(t.smpl, p, o)
+ #define FxaaTexGreen4(t, p) t.tex.GatherGreen(t.smpl, p)
+ #define FxaaTexOffGreen4(t, p, o) t.tex.GatherGreen(t.smpl, p, o)
+#endif
+
+
+/*============================================================================
+ GREEN AS LUMA OPTION SUPPORT FUNCTION
+============================================================================*/
+#if (FXAA_GREEN_AS_LUMA == 0)
+ FxaaFloat FxaaLuma(FxaaFloat4 rgba) { return rgba.w; }
+#else
+ FxaaFloat FxaaLuma(FxaaFloat4 rgba) { return rgba.y; }
+#endif
+
+
+
+
+/*============================================================================
+
+ FXAA3 QUALITY - PC
+
+============================================================================*/
+#if (FXAA_PC == 1)
+/*--------------------------------------------------------------------------*/
+FxaaFloat4 FxaaPixelShader(
+ //
+ // Use noperspective interpolation here (turn off perspective interpolation).
+ // {xy} = center of pixel
+ FxaaFloat2 pos,
+ //
+ // Used only for FXAA Console, and not used on the 360 version.
+ // Use noperspective interpolation here (turn off perspective interpolation).
+ // {xy__} = upper left of pixel
+ // {__zw} = lower right of pixel
+ FxaaFloat4 fxaaConsolePosPos,
+ //
+ // Input color texture.
+ // {rgb_} = color in linear or perceptual color space
+ // if (FXAA_GREEN_AS_LUMA == 0)
+ // {___a} = luma in perceptual color space (not linear)
+ FxaaTex tex,
+ //
+ // Only used on the optimized 360 version of FXAA Console.
+ // For everything but 360, just use the same input here as for "tex".
+ // For 360, same texture, just alias with a 2nd sampler.
+ // This sampler needs to have an exponent bias of -1.
+ FxaaTex fxaaConsole360TexExpBiasNegOne,
+ //
+ // Only used on the optimized 360 version of FXAA Console.
+ // For everything but 360, just use the same input here as for "tex".
+ // For 360, same texture, just alias with a 3nd sampler.
+ // This sampler needs to have an exponent bias of -2.
+ FxaaTex fxaaConsole360TexExpBiasNegTwo,
+ //
+ // Only used on FXAA Quality.
+ // This must be from a constant/uniform.
+ // {x_} = 1.0/screenWidthInPixels
+ // {_y} = 1.0/screenHeightInPixels
+ FxaaFloat2 fxaaQualityRcpFrame,
+ //
+ // Only used on FXAA Console.
+ // This must be from a constant/uniform.
+ // This effects sub-pixel AA quality and inversely sharpness.
+ // Where N ranges between,
+ // N = 0.50 (default)
+ // N = 0.33 (sharper)
+ // {x___} = -N/screenWidthInPixels
+ // {_y__} = -N/screenHeightInPixels
+ // {__z_} = N/screenWidthInPixels
+ // {___w} = N/screenHeightInPixels
+ FxaaFloat4 fxaaConsoleRcpFrameOpt,
+ //
+ // Only used on FXAA Console.
+ // Not used on 360, but used on PS3 and PC.
+ // This must be from a constant/uniform.
+ // {x___} = -2.0/screenWidthInPixels
+ // {_y__} = -2.0/screenHeightInPixels
+ // {__z_} = 2.0/screenWidthInPixels
+ // {___w} = 2.0/screenHeightInPixels
+ FxaaFloat4 fxaaConsoleRcpFrameOpt2,
+ //
+ // Only used on FXAA Console.
+ // Only used on 360 in place of fxaaConsoleRcpFrameOpt2.
+ // This must be from a constant/uniform.
+ // {x___} = 8.0/screenWidthInPixels
+ // {_y__} = 8.0/screenHeightInPixels
+ // {__z_} = -4.0/screenWidthInPixels
+ // {___w} = -4.0/screenHeightInPixels
+ FxaaFloat4 fxaaConsole360RcpFrameOpt2,
+ //
+ // Only used on FXAA Quality.
+ // This used to be the FXAA_QUALITY_SUBPIX define.
+ // It is here now to allow easier tuning.
+ // Choose the amount of sub-pixel aliasing removal.
+ // This can effect sharpness.
+ // 1.00 - upper limit (softer)
+ // 0.75 - default amount of filtering
+ // 0.50 - lower limit (sharper, less sub-pixel aliasing removal)
+ // 0.25 - almost off
+ // 0.00 - completely off
+ FxaaFloat fxaaQualitySubpix,
+ //
+ // Only used on FXAA Quality.
+ // This used to be the FXAA_QUALITY_EDGE_THRESHOLD define.
+ // It is here now to allow easier tuning.
+ // The minimum amount of local contrast required to apply algorithm.
+ // 0.333 - too little (faster)
+ // 0.250 - low quality
+ // 0.166 - default
+ // 0.125 - high quality
+ // 0.063 - overkill (slower)
+ FxaaFloat fxaaQualityEdgeThreshold,
+ //
+ // Only used on FXAA Quality.
+ // This used to be the FXAA_QUALITY_EDGE_THRESHOLD_MIN define.
+ // It is here now to allow easier tuning.
+ // Trims the algorithm from processing darks.
+ // 0.0833 - upper limit (default, the start of visible unfiltered edges)
+ // 0.0625 - high quality (faster)
+ // 0.0312 - visible limit (slower)
+ // Special notes when using FXAA_GREEN_AS_LUMA,
+ // Likely want to set this to zero.
+ // As colors that are mostly not-green
+ // will appear very dark in the green channel!
+ // Tune by looking at mostly non-green content,
+ // then start at zero and increase until aliasing is a problem.
+ FxaaFloat fxaaQualityEdgeThresholdMin,
+ //
+ // Only used on FXAA Console.
+ // This used to be the FXAA_CONSOLE_EDGE_SHARPNESS define.
+ // It is here now to allow easier tuning.
+ // This does not effect PS3, as this needs to be compiled in.
+ // Use FXAA_CONSOLE_PS3_EDGE_SHARPNESS for PS3.
+ // Due to the PS3 being ALU bound,
+ // there are only three safe values here: 2 and 4 and 8.
+ // These options use the shaders ability to a free *|/ by 2|4|8.
+ // For all other platforms can be a non-power of two.
+ // 8.0 is sharper (default!!!)
+ // 4.0 is softer
+ // 2.0 is really soft (good only for vector graphics inputs)
+ FxaaFloat fxaaConsoleEdgeSharpness,
+ //
+ // Only used on FXAA Console.
+ // This used to be the FXAA_CONSOLE_EDGE_THRESHOLD define.
+ // It is here now to allow easier tuning.
+ // This does not effect PS3, as this needs to be compiled in.
+ // Use FXAA_CONSOLE_PS3_EDGE_THRESHOLD for PS3.
+ // Due to the PS3 being ALU bound,
+ // there are only two safe values here: 1/4 and 1/8.
+ // These options use the shaders ability to a free *|/ by 2|4|8.
+ // The console setting has a different mapping than the quality setting.
+ // Other platforms can use other values.
+ // 0.125 leaves less aliasing, but is softer (default!!!)
+ // 0.25 leaves more aliasing, and is sharper
+ FxaaFloat fxaaConsoleEdgeThreshold,
+ //
+ // Only used on FXAA Console.
+ // This used to be the FXAA_CONSOLE_EDGE_THRESHOLD_MIN define.
+ // It is here now to allow easier tuning.
+ // Trims the algorithm from processing darks.
+ // The console setting has a different mapping than the quality setting.
+ // This only applies when FXAA_EARLY_EXIT is 1.
+ // This does not apply to PS3,
+ // PS3 was simplified to avoid more shader instructions.
+ // 0.06 - faster but more aliasing in darks
+ // 0.05 - default
+ // 0.04 - slower and less aliasing in darks
+ // Special notes when using FXAA_GREEN_AS_LUMA,
+ // Likely want to set this to zero.
+ // As colors that are mostly not-green
+ // will appear very dark in the green channel!
+ // Tune by looking at mostly non-green content,
+ // then start at zero and increase until aliasing is a problem.
+ FxaaFloat fxaaConsoleEdgeThresholdMin,
+ //
+ // Extra constants for 360 FXAA Console only.
+ // Use zeros or anything else for other platforms.
+ // These must be in physical constant registers and NOT immedates.
+ // Immedates will result in compiler un-optimizing.
+ // {xyzw} = float4(1.0, -1.0, 0.25, -0.25)
+ FxaaFloat4 fxaaConsole360ConstDir
+) {
+/*--------------------------------------------------------------------------*/
+ FxaaFloat2 posM;
+ posM.x = pos.x;
+ posM.y = pos.y;
+ #if (FXAA_GATHER4_ALPHA == 1)
+ #if (FXAA_DISCARD == 0)
+ FxaaFloat4 rgbyM = FxaaTexTop(tex, posM);
+ #if (FXAA_GREEN_AS_LUMA == 0)
+ #define lumaM rgbyM.w
+ #else
+ #define lumaM rgbyM.y
+ #endif
+ #endif
+ #if (FXAA_GREEN_AS_LUMA == 0)
+ FxaaFloat4 luma4A = FxaaTexAlpha4(tex, posM);
+ FxaaFloat4 luma4B = FxaaTexOffAlpha4(tex, posM, FxaaInt2(-1, -1));
+ #else
+ FxaaFloat4 luma4A = FxaaTexGreen4(tex, posM);
+ FxaaFloat4 luma4B = FxaaTexOffGreen4(tex, posM, FxaaInt2(-1, -1));
+ #endif
+ #if (FXAA_DISCARD == 1)
+ #define lumaM luma4A.w
+ #endif
+ #define lumaE luma4A.z
+ #define lumaS luma4A.x
+ #define lumaSE luma4A.y
+ #define lumaNW luma4B.w
+ #define lumaN luma4B.z
+ #define lumaW luma4B.x
+ #else
+ FxaaFloat4 rgbyM = FxaaTexTop(tex, posM);
+ #if (FXAA_GREEN_AS_LUMA == 0)
+ #define lumaM rgbyM.w
+ #else
+ #define lumaM rgbyM.y
+ #endif
+ FxaaFloat lumaS = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 0, 1), fxaaQualityRcpFrame.xy));
+ FxaaFloat lumaE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1, 0), fxaaQualityRcpFrame.xy));
+ FxaaFloat lumaN = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 0,-1), fxaaQualityRcpFrame.xy));
+ FxaaFloat lumaW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 0), fxaaQualityRcpFrame.xy));
+ #endif
+/*--------------------------------------------------------------------------*/
+ FxaaFloat maxSM = max(lumaS, lumaM);
+ FxaaFloat minSM = min(lumaS, lumaM);
+ FxaaFloat maxESM = max(lumaE, maxSM);
+ FxaaFloat minESM = min(lumaE, minSM);
+ FxaaFloat maxWN = max(lumaN, lumaW);
+ FxaaFloat minWN = min(lumaN, lumaW);
+ FxaaFloat rangeMax = max(maxWN, maxESM);
+ FxaaFloat rangeMin = min(minWN, minESM);
+ FxaaFloat rangeMaxScaled = rangeMax * fxaaQualityEdgeThreshold;
+ FxaaFloat range = rangeMax - rangeMin;
+ FxaaFloat rangeMaxClamped = max(fxaaQualityEdgeThresholdMin, rangeMaxScaled);
+ FxaaBool earlyExit = range < rangeMaxClamped;
+/*--------------------------------------------------------------------------*/
+ if(earlyExit)
+ #if (FXAA_DISCARD == 1)
+ FxaaDiscard;
+ #else
+ return rgbyM;
+ #endif
+/*--------------------------------------------------------------------------*/
+ #if (FXAA_GATHER4_ALPHA == 0)
+ FxaaFloat lumaNW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1,-1), fxaaQualityRcpFrame.xy));
+ FxaaFloat lumaSE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1, 1), fxaaQualityRcpFrame.xy));
+ FxaaFloat lumaNE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1,-1), fxaaQualityRcpFrame.xy));
+ FxaaFloat lumaSW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 1), fxaaQualityRcpFrame.xy));
+ #else
+ FxaaFloat lumaNE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(1, -1), fxaaQualityRcpFrame.xy));
+ FxaaFloat lumaSW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 1), fxaaQualityRcpFrame.xy));
+ #endif
+/*--------------------------------------------------------------------------*/
+ FxaaFloat lumaNS = lumaN + lumaS;
+ FxaaFloat lumaWE = lumaW + lumaE;
+ FxaaFloat subpixRcpRange = 1.0/range;
+ FxaaFloat subpixNSWE = lumaNS + lumaWE;
+ FxaaFloat edgeHorz1 = (-2.0 * lumaM) + lumaNS;
+ FxaaFloat edgeVert1 = (-2.0 * lumaM) + lumaWE;
+/*--------------------------------------------------------------------------*/
+ FxaaFloat lumaNESE = lumaNE + lumaSE;
+ FxaaFloat lumaNWNE = lumaNW + lumaNE;
+ FxaaFloat edgeHorz2 = (-2.0 * lumaE) + lumaNESE;
+ FxaaFloat edgeVert2 = (-2.0 * lumaN) + lumaNWNE;
+/*--------------------------------------------------------------------------*/
+ FxaaFloat lumaNWSW = lumaNW + lumaSW;
+ FxaaFloat lumaSWSE = lumaSW + lumaSE;
+ FxaaFloat edgeHorz4 = (abs(edgeHorz1) * 2.0) + abs(edgeHorz2);
+ FxaaFloat edgeVert4 = (abs(edgeVert1) * 2.0) + abs(edgeVert2);
+ FxaaFloat edgeHorz3 = (-2.0 * lumaW) + lumaNWSW;
+ FxaaFloat edgeVert3 = (-2.0 * lumaS) + lumaSWSE;
+ FxaaFloat edgeHorz = abs(edgeHorz3) + edgeHorz4;
+ FxaaFloat edgeVert = abs(edgeVert3) + edgeVert4;
+/*--------------------------------------------------------------------------*/
+ FxaaFloat subpixNWSWNESE = lumaNWSW + lumaNESE;
+ FxaaFloat lengthSign = fxaaQualityRcpFrame.x;
+ FxaaBool horzSpan = edgeHorz >= edgeVert;
+ FxaaFloat subpixA = subpixNSWE * 2.0 + subpixNWSWNESE;
+/*--------------------------------------------------------------------------*/
+ if(!horzSpan) lumaN = lumaW;
+ if(!horzSpan) lumaS = lumaE;
+ if(horzSpan) lengthSign = fxaaQualityRcpFrame.y;
+ FxaaFloat subpixB = (subpixA * (1.0/12.0)) - lumaM;
+/*--------------------------------------------------------------------------*/
+ FxaaFloat gradientN = lumaN - lumaM;
+ FxaaFloat gradientS = lumaS - lumaM;
+ FxaaFloat lumaNN = lumaN + lumaM;
+ FxaaFloat lumaSS = lumaS + lumaM;
+ FxaaBool pairN = abs(gradientN) >= abs(gradientS);
+ FxaaFloat gradient = max(abs(gradientN), abs(gradientS));
+ if(pairN) lengthSign = -lengthSign;
+ FxaaFloat subpixC = FxaaSat(abs(subpixB) * subpixRcpRange);
+/*--------------------------------------------------------------------------*/
+ FxaaFloat2 posB;
+ posB.x = posM.x;
+ posB.y = posM.y;
+ FxaaFloat2 offNP;
+ offNP.x = (!horzSpan) ? 0.0 : fxaaQualityRcpFrame.x;
+ offNP.y = ( horzSpan) ? 0.0 : fxaaQualityRcpFrame.y;
+ if(!horzSpan) posB.x += lengthSign * 0.5;
+ if( horzSpan) posB.y += lengthSign * 0.5;
+/*--------------------------------------------------------------------------*/
+ FxaaFloat2 posN;
+ posN.x = posB.x - offNP.x * FXAA_QUALITY_P0;
+ posN.y = posB.y - offNP.y * FXAA_QUALITY_P0;
+ FxaaFloat2 posP;
+ posP.x = posB.x + offNP.x * FXAA_QUALITY_P0;
+ posP.y = posB.y + offNP.y * FXAA_QUALITY_P0;
+ FxaaFloat subpixD = ((-2.0)*subpixC) + 3.0;
+ FxaaFloat lumaEndN = FxaaLuma(FxaaTexTop(tex, posN));
+ FxaaFloat subpixE = subpixC * subpixC;
+ FxaaFloat lumaEndP = FxaaLuma(FxaaTexTop(tex, posP));
+/*--------------------------------------------------------------------------*/
+ if(!pairN) lumaNN = lumaSS;
+ FxaaFloat gradientScaled = gradient * 1.0/4.0;
+ FxaaFloat lumaMM = lumaM - lumaNN * 0.5;
+ FxaaFloat subpixF = subpixD * subpixE;
+ FxaaBool lumaMLTZero = lumaMM < 0.0;
+/*--------------------------------------------------------------------------*/
+ lumaEndN -= lumaNN * 0.5;
+ lumaEndP -= lumaNN * 0.5;
+ FxaaBool doneN = abs(lumaEndN) >= gradientScaled;
+ FxaaBool doneP = abs(lumaEndP) >= gradientScaled;
+ if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P1;
+ if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P1;
+ FxaaBool doneNP = (!doneN) || (!doneP);
+ if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P1;
+ if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P1;
+/*--------------------------------------------------------------------------*/
+ if(doneNP) {
+ if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));
+ if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));
+ if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;
+ if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;
+ doneN = abs(lumaEndN) >= gradientScaled;
+ doneP = abs(lumaEndP) >= gradientScaled;
+ if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P2;
+ if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P2;
+ doneNP = (!doneN) || (!doneP);
+ if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P2;
+ if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P2;
+/*--------------------------------------------------------------------------*/
+ #if (FXAA_QUALITY_PS > 3)
+ if(doneNP) {
+ if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));
+ if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));
+ if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;
+ if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;
+ doneN = abs(lumaEndN) >= gradientScaled;
+ doneP = abs(lumaEndP) >= gradientScaled;
+ if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P3;
+ if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P3;
+ doneNP = (!doneN) || (!doneP);
+ if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P3;
+ if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P3;
+/*--------------------------------------------------------------------------*/
+ #if (FXAA_QUALITY_PS > 4)
+ if(doneNP) {
+ if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));
+ if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));
+ if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;
+ if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;
+ doneN = abs(lumaEndN) >= gradientScaled;
+ doneP = abs(lumaEndP) >= gradientScaled;
+ if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P4;
+ if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P4;
+ doneNP = (!doneN) || (!doneP);
+ if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P4;
+ if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P4;
+/*--------------------------------------------------------------------------*/
+ #if (FXAA_QUALITY_PS > 5)
+ if(doneNP) {
+ if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));
+ if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));
+ if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;
+ if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;
+ doneN = abs(lumaEndN) >= gradientScaled;
+ doneP = abs(lumaEndP) >= gradientScaled;
+ if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P5;
+ if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P5;
+ doneNP = (!doneN) || (!doneP);
+ if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P5;
+ if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P5;
+/*--------------------------------------------------------------------------*/
+ #if (FXAA_QUALITY_PS > 6)
+ if(doneNP) {
+ if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));
+ if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));
+ if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;
+ if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;
+ doneN = abs(lumaEndN) >= gradientScaled;
+ doneP = abs(lumaEndP) >= gradientScaled;
+ if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P6;
+ if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P6;
+ doneNP = (!doneN) || (!doneP);
+ if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P6;
+ if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P6;
+/*--------------------------------------------------------------------------*/
+ #if (FXAA_QUALITY_PS > 7)
+ if(doneNP) {
+ if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));
+ if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));
+ if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;
+ if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;
+ doneN = abs(lumaEndN) >= gradientScaled;
+ doneP = abs(lumaEndP) >= gradientScaled;
+ if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P7;
+ if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P7;
+ doneNP = (!doneN) || (!doneP);
+ if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P7;
+ if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P7;
+/*--------------------------------------------------------------------------*/
+ #if (FXAA_QUALITY_PS > 8)
+ if(doneNP) {
+ if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));
+ if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));
+ if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;
+ if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;
+ doneN = abs(lumaEndN) >= gradientScaled;
+ doneP = abs(lumaEndP) >= gradientScaled;
+ if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P8;
+ if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P8;
+ doneNP = (!doneN) || (!doneP);
+ if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P8;
+ if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P8;
+/*--------------------------------------------------------------------------*/
+ #if (FXAA_QUALITY_PS > 9)
+ if(doneNP) {
+ if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));
+ if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));
+ if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;
+ if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;
+ doneN = abs(lumaEndN) >= gradientScaled;
+ doneP = abs(lumaEndP) >= gradientScaled;
+ if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P9;
+ if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P9;
+ doneNP = (!doneN) || (!doneP);
+ if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P9;
+ if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P9;
+/*--------------------------------------------------------------------------*/
+ #if (FXAA_QUALITY_PS > 10)
+ if(doneNP) {
+ if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));
+ if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));
+ if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;
+ if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;
+ doneN = abs(lumaEndN) >= gradientScaled;
+ doneP = abs(lumaEndP) >= gradientScaled;
+ if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P10;
+ if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P10;
+ doneNP = (!doneN) || (!doneP);
+ if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P10;
+ if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P10;
+/*--------------------------------------------------------------------------*/
+ #if (FXAA_QUALITY_PS > 11)
+ if(doneNP) {
+ if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));
+ if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));
+ if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;
+ if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;
+ doneN = abs(lumaEndN) >= gradientScaled;
+ doneP = abs(lumaEndP) >= gradientScaled;
+ if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P11;
+ if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P11;
+ doneNP = (!doneN) || (!doneP);
+ if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P11;
+ if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P11;
+/*--------------------------------------------------------------------------*/
+ #if (FXAA_QUALITY_PS > 12)
+ if(doneNP) {
+ if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));
+ if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));
+ if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;
+ if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;
+ doneN = abs(lumaEndN) >= gradientScaled;
+ doneP = abs(lumaEndP) >= gradientScaled;
+ if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P12;
+ if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P12;
+ doneNP = (!doneN) || (!doneP);
+ if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P12;
+ if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P12;
+/*--------------------------------------------------------------------------*/
+ }
+ #endif
+/*--------------------------------------------------------------------------*/
+ }
+ #endif
+/*--------------------------------------------------------------------------*/
+ }
+ #endif
+/*--------------------------------------------------------------------------*/
+ }
+ #endif
+/*--------------------------------------------------------------------------*/
+ }
+ #endif
+/*--------------------------------------------------------------------------*/
+ }
+ #endif
+/*--------------------------------------------------------------------------*/
+ }
+ #endif
+/*--------------------------------------------------------------------------*/
+ }
+ #endif
+/*--------------------------------------------------------------------------*/
+ }
+ #endif
+/*--------------------------------------------------------------------------*/
+ }
+ #endif
+/*--------------------------------------------------------------------------*/
+ }
+/*--------------------------------------------------------------------------*/
+ FxaaFloat dstN = posM.x - posN.x;
+ FxaaFloat dstP = posP.x - posM.x;
+ if(!horzSpan) dstN = posM.y - posN.y;
+ if(!horzSpan) dstP = posP.y - posM.y;
+/*--------------------------------------------------------------------------*/
+ FxaaBool goodSpanN = (lumaEndN < 0.0) != lumaMLTZero;
+ FxaaFloat spanLength = (dstP + dstN);
+ FxaaBool goodSpanP = (lumaEndP < 0.0) != lumaMLTZero;
+ FxaaFloat spanLengthRcp = 1.0/spanLength;
+/*--------------------------------------------------------------------------*/
+ FxaaBool directionN = dstN < dstP;
+ FxaaFloat dst = min(dstN, dstP);
+ FxaaBool goodSpan = directionN ? goodSpanN : goodSpanP;
+ FxaaFloat subpixG = subpixF * subpixF;
+ FxaaFloat pixelOffset = (dst * (-spanLengthRcp)) + 0.5;
+ FxaaFloat subpixH = subpixG * fxaaQualitySubpix;
+/*--------------------------------------------------------------------------*/
+ FxaaFloat pixelOffsetGood = goodSpan ? pixelOffset : 0.0;
+ FxaaFloat pixelOffsetSubpix = max(pixelOffsetGood, subpixH);
+ if(!horzSpan) posM.x += pixelOffsetSubpix * lengthSign;
+ if( horzSpan) posM.y += pixelOffsetSubpix * lengthSign;
+ #if (FXAA_DISCARD == 1)
+ return FxaaTexTop(tex, posM);
+ #else
+ return FxaaFloat4(FxaaTexTop(tex, posM).xyz, lumaM);
+ #endif
+}
+/*==========================================================================*/
+#endif
+
+
+
+
+/*============================================================================
+
+ FXAA3 CONSOLE - PC VERSION
+
+------------------------------------------------------------------------------
+Instead of using this on PC, I'd suggest just using FXAA Quality with
+ #define FXAA_QUALITY_PRESET 10
+Or
+ #define FXAA_QUALITY_PRESET 20
+Either are higher qualilty and almost as fast as this on modern PC GPUs.
+============================================================================*/
+#if (FXAA_PC_CONSOLE == 1)
+/*--------------------------------------------------------------------------*/
+FxaaFloat4 FxaaPixelShader(
+ // See FXAA Quality FxaaPixelShader() source for docs on Inputs!
+ FxaaFloat2 pos,
+ FxaaFloat4 fxaaConsolePosPos,
+ FxaaTex tex,
+ FxaaTex fxaaConsole360TexExpBiasNegOne,
+ FxaaTex fxaaConsole360TexExpBiasNegTwo,
+ FxaaFloat2 fxaaQualityRcpFrame,
+ FxaaFloat4 fxaaConsoleRcpFrameOpt,
+ FxaaFloat4 fxaaConsoleRcpFrameOpt2,
+ FxaaFloat4 fxaaConsole360RcpFrameOpt2,
+ FxaaFloat fxaaQualitySubpix,
+ FxaaFloat fxaaQualityEdgeThreshold,
+ FxaaFloat fxaaQualityEdgeThresholdMin,
+ FxaaFloat fxaaConsoleEdgeSharpness,
+ FxaaFloat fxaaConsoleEdgeThreshold,
+ FxaaFloat fxaaConsoleEdgeThresholdMin,
+ FxaaFloat4 fxaaConsole360ConstDir
+) {
+/*--------------------------------------------------------------------------*/
+ FxaaFloat lumaNw = FxaaLuma(FxaaTexTop(tex, fxaaConsolePosPos.xy));
+ FxaaFloat lumaSw = FxaaLuma(FxaaTexTop(tex, fxaaConsolePosPos.xw));
+ FxaaFloat lumaNe = FxaaLuma(FxaaTexTop(tex, fxaaConsolePosPos.zy));
+ FxaaFloat lumaSe = FxaaLuma(FxaaTexTop(tex, fxaaConsolePosPos.zw));
+/*--------------------------------------------------------------------------*/
+ FxaaFloat4 rgbyM = FxaaTexTop(tex, pos.xy);
+ #if (FXAA_GREEN_AS_LUMA == 0)
+ FxaaFloat lumaM = rgbyM.w;
+ #else
+ FxaaFloat lumaM = rgbyM.y;
+ #endif
+/*--------------------------------------------------------------------------*/
+ FxaaFloat lumaMaxNwSw = max(lumaNw, lumaSw);
+ lumaNe += 1.0/384.0;
+ FxaaFloat lumaMinNwSw = min(lumaNw, lumaSw);
+/*--------------------------------------------------------------------------*/
+ FxaaFloat lumaMaxNeSe = max(lumaNe, lumaSe);
+ FxaaFloat lumaMinNeSe = min(lumaNe, lumaSe);
+/*--------------------------------------------------------------------------*/
+ FxaaFloat lumaMax = max(lumaMaxNeSe, lumaMaxNwSw);
+ FxaaFloat lumaMin = min(lumaMinNeSe, lumaMinNwSw);
+/*--------------------------------------------------------------------------*/
+ FxaaFloat lumaMaxScaled = lumaMax * fxaaConsoleEdgeThreshold;
+/*--------------------------------------------------------------------------*/
+ FxaaFloat lumaMinM = min(lumaMin, lumaM);
+ FxaaFloat lumaMaxScaledClamped = max(fxaaConsoleEdgeThresholdMin, lumaMaxScaled);
+ FxaaFloat lumaMaxM = max(lumaMax, lumaM);
+ FxaaFloat dirSwMinusNe = lumaSw - lumaNe;
+ FxaaFloat lumaMaxSubMinM = lumaMaxM - lumaMinM;
+ FxaaFloat dirSeMinusNw = lumaSe - lumaNw;
+ if(lumaMaxSubMinM < lumaMaxScaledClamped) return rgbyM;
+/*--------------------------------------------------------------------------*/
+ FxaaFloat2 dir;
+ dir.x = dirSwMinusNe + dirSeMinusNw;
+ dir.y = dirSwMinusNe - dirSeMinusNw;
+/*--------------------------------------------------------------------------*/
+ FxaaFloat2 dir1 = normalize(dir.xy);
+ FxaaFloat4 rgbyN1 = FxaaTexTop(tex, pos.xy - dir1 * fxaaConsoleRcpFrameOpt.zw);
+ FxaaFloat4 rgbyP1 = FxaaTexTop(tex, pos.xy + dir1 * fxaaConsoleRcpFrameOpt.zw);
+/*--------------------------------------------------------------------------*/
+ FxaaFloat dirAbsMinTimesC = min(abs(dir1.x), abs(dir1.y)) * fxaaConsoleEdgeSharpness;
+ FxaaFloat2 dir2 = clamp(dir1.xy / dirAbsMinTimesC, -2.0, 2.0);
+/*--------------------------------------------------------------------------*/
+ FxaaFloat4 rgbyN2 = FxaaTexTop(tex, pos.xy - dir2 * fxaaConsoleRcpFrameOpt2.zw);
+ FxaaFloat4 rgbyP2 = FxaaTexTop(tex, pos.xy + dir2 * fxaaConsoleRcpFrameOpt2.zw);
+/*--------------------------------------------------------------------------*/
+ FxaaFloat4 rgbyA = rgbyN1 + rgbyP1;
+ FxaaFloat4 rgbyB = ((rgbyN2 + rgbyP2) * 0.25) + (rgbyA * 0.25);
+/*--------------------------------------------------------------------------*/
+ #if (FXAA_GREEN_AS_LUMA == 0)
+ FxaaBool twoTap = (rgbyB.w < lumaMin) || (rgbyB.w > lumaMax);
+ #else
+ FxaaBool twoTap = (rgbyB.y < lumaMin) || (rgbyB.y > lumaMax);
+ #endif
+ if(twoTap) rgbyB.xyz = rgbyA.xyz * 0.5;
+ return rgbyB; }
+/*==========================================================================*/
+#endif
+
+
+
+/*============================================================================
+
+ FXAA3 CONSOLE - 360 PIXEL SHADER
+
+------------------------------------------------------------------------------
+This optimized version thanks to suggestions from Andy Luedke.
+Should be fully tex bound in all cases.
+As of the FXAA 3.11 release, I have still not tested this code,
+however I fixed a bug which was in both FXAA 3.9 and FXAA 3.10.
+And note this is replacing the old unoptimized version.
+If it does not work, please let me know so I can fix it.
+============================================================================*/
+#if (FXAA_360 == 1)
+/*--------------------------------------------------------------------------*/
+[reduceTempRegUsage(4)]
+float4 FxaaPixelShader(
+ // See FXAA Quality FxaaPixelShader() source for docs on Inputs!
+ FxaaFloat2 pos,
+ FxaaFloat4 fxaaConsolePosPos,
+ FxaaTex tex,
+ FxaaTex fxaaConsole360TexExpBiasNegOne,
+ FxaaTex fxaaConsole360TexExpBiasNegTwo,
+ FxaaFloat2 fxaaQualityRcpFrame,
+ FxaaFloat4 fxaaConsoleRcpFrameOpt,
+ FxaaFloat4 fxaaConsoleRcpFrameOpt2,
+ FxaaFloat4 fxaaConsole360RcpFrameOpt2,
+ FxaaFloat fxaaQualitySubpix,
+ FxaaFloat fxaaQualityEdgeThreshold,
+ FxaaFloat fxaaQualityEdgeThresholdMin,
+ FxaaFloat fxaaConsoleEdgeSharpness,
+ FxaaFloat fxaaConsoleEdgeThreshold,
+ FxaaFloat fxaaConsoleEdgeThresholdMin,
+ FxaaFloat4 fxaaConsole360ConstDir
+) {
+/*--------------------------------------------------------------------------*/
+ float4 lumaNwNeSwSe;
+ #if (FXAA_GREEN_AS_LUMA == 0)
+ asm {
+ tfetch2D lumaNwNeSwSe.w___, tex, pos.xy, OffsetX = -0.5, OffsetY = -0.5, UseComputedLOD=false
+ tfetch2D lumaNwNeSwSe._w__, tex, pos.xy, OffsetX = 0.5, OffsetY = -0.5, UseComputedLOD=false
+ tfetch2D lumaNwNeSwSe.__w_, tex, pos.xy, OffsetX = -0.5, OffsetY = 0.5, UseComputedLOD=false
+ tfetch2D lumaNwNeSwSe.___w, tex, pos.xy, OffsetX = 0.5, OffsetY = 0.5, UseComputedLOD=false
+ };
+ #else
+ asm {
+ tfetch2D lumaNwNeSwSe.y___, tex, pos.xy, OffsetX = -0.5, OffsetY = -0.5, UseComputedLOD=false
+ tfetch2D lumaNwNeSwSe._y__, tex, pos.xy, OffsetX = 0.5, OffsetY = -0.5, UseComputedLOD=false
+ tfetch2D lumaNwNeSwSe.__y_, tex, pos.xy, OffsetX = -0.5, OffsetY = 0.5, UseComputedLOD=false
+ tfetch2D lumaNwNeSwSe.___y, tex, pos.xy, OffsetX = 0.5, OffsetY = 0.5, UseComputedLOD=false
+ };
+ #endif
+/*--------------------------------------------------------------------------*/
+ lumaNwNeSwSe.y += 1.0/384.0;
+ float2 lumaMinTemp = min(lumaNwNeSwSe.xy, lumaNwNeSwSe.zw);
+ float2 lumaMaxTemp = max(lumaNwNeSwSe.xy, lumaNwNeSwSe.zw);
+ float lumaMin = min(lumaMinTemp.x, lumaMinTemp.y);
+ float lumaMax = max(lumaMaxTemp.x, lumaMaxTemp.y);
+/*--------------------------------------------------------------------------*/
+ float4 rgbyM = tex2Dlod(tex, float4(pos.xy, 0.0, 0.0));
+ #if (FXAA_GREEN_AS_LUMA == 0)
+ float lumaMinM = min(lumaMin, rgbyM.w);
+ float lumaMaxM = max(lumaMax, rgbyM.w);
+ #else
+ float lumaMinM = min(lumaMin, rgbyM.y);
+ float lumaMaxM = max(lumaMax, rgbyM.y);
+ #endif
+ if((lumaMaxM - lumaMinM) < max(fxaaConsoleEdgeThresholdMin, lumaMax * fxaaConsoleEdgeThreshold)) return rgbyM;
+/*--------------------------------------------------------------------------*/
+ float2 dir;
+ dir.x = dot(lumaNwNeSwSe, fxaaConsole360ConstDir.yyxx);
+ dir.y = dot(lumaNwNeSwSe, fxaaConsole360ConstDir.xyxy);
+ dir = normalize(dir);
+/*--------------------------------------------------------------------------*/
+ float4 dir1 = dir.xyxy * fxaaConsoleRcpFrameOpt.xyzw;
+/*--------------------------------------------------------------------------*/
+ float4 dir2;
+ float dirAbsMinTimesC = min(abs(dir.x), abs(dir.y)) * fxaaConsoleEdgeSharpness;
+ dir2 = saturate(fxaaConsole360ConstDir.zzww * dir.xyxy / dirAbsMinTimesC + 0.5);
+ dir2 = dir2 * fxaaConsole360RcpFrameOpt2.xyxy + fxaaConsole360RcpFrameOpt2.zwzw;
+/*--------------------------------------------------------------------------*/
+ float4 rgbyN1 = tex2Dlod(fxaaConsole360TexExpBiasNegOne, float4(pos.xy + dir1.xy, 0.0, 0.0));
+ float4 rgbyP1 = tex2Dlod(fxaaConsole360TexExpBiasNegOne, float4(pos.xy + dir1.zw, 0.0, 0.0));
+ float4 rgbyN2 = tex2Dlod(fxaaConsole360TexExpBiasNegTwo, float4(pos.xy + dir2.xy, 0.0, 0.0));
+ float4 rgbyP2 = tex2Dlod(fxaaConsole360TexExpBiasNegTwo, float4(pos.xy + dir2.zw, 0.0, 0.0));
+/*--------------------------------------------------------------------------*/
+ float4 rgbyA = rgbyN1 + rgbyP1;
+ float4 rgbyB = rgbyN2 + rgbyP2 + rgbyA * 0.5;
+/*--------------------------------------------------------------------------*/
+ float4 rgbyR = ((FxaaLuma(rgbyB) - lumaMax) > 0.0) ? rgbyA : rgbyB;
+ rgbyR = ((FxaaLuma(rgbyB) - lumaMin) > 0.0) ? rgbyR : rgbyA;
+ return rgbyR; }
+/*==========================================================================*/
+#endif
+
+
+
+/*============================================================================
+
+ FXAA3 CONSOLE - OPTIMIZED PS3 PIXEL SHADER (NO EARLY EXIT)
+
+==============================================================================
+The code below does not exactly match the assembly.
+I have a feeling that 12 cycles is possible, but was not able to get there.
+Might have to increase register count to get full performance.
+Note this shader does not use perspective interpolation.
+
+Use the following cgc options,
+
+ --fenable-bx2 --fastmath --fastprecision --nofloatbindings
+
+------------------------------------------------------------------------------
+ NVSHADERPERF OUTPUT
+------------------------------------------------------------------------------
+For reference and to aid in debug, output of NVShaderPerf should match this,
+
+Shader to schedule:
+ 0: texpkb h0.w(TRUE), v5.zyxx, #0
+ 2: addh h2.z(TRUE), h0.w, constant(0.001953, 0.000000, 0.000000, 0.000000).x
+ 4: texpkb h0.w(TRUE), v5.xwxx, #0
+ 6: addh h0.z(TRUE), -h2, h0.w
+ 7: texpkb h1.w(TRUE), v5, #0
+ 9: addh h0.x(TRUE), h0.z, -h1.w
+ 10: addh h3.w(TRUE), h0.z, h1
+ 11: texpkb h2.w(TRUE), v5.zwzz, #0
+ 13: addh h0.z(TRUE), h3.w, -h2.w
+ 14: addh h0.x(TRUE), h2.w, h0
+ 15: nrmh h1.xz(TRUE), h0_n
+ 16: minh_m8 h0.x(TRUE), |h1|, |h1.z|
+ 17: maxh h4.w(TRUE), h0, h1
+ 18: divx h2.xy(TRUE), h1_n.xzzw, h0_n
+ 19: movr r1.zw(TRUE), v4.xxxy
+ 20: madr r2.xz(TRUE), -h1, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).zzww, r1.zzww
+ 22: minh h5.w(TRUE), h0, h1
+ 23: texpkb h0(TRUE), r2.xzxx, #0
+ 25: madr r0.zw(TRUE), h1.xzxz, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w), r1
+ 27: maxh h4.x(TRUE), h2.z, h2.w
+ 28: texpkb h1(TRUE), r0.zwzz, #0
+ 30: addh_d2 h1(TRUE), h0, h1
+ 31: madr r0.xy(TRUE), -h2, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).xyxx, r1.zwzz
+ 33: texpkb h0(TRUE), r0, #0
+ 35: minh h4.z(TRUE), h2, h2.w
+ 36: fenct TRUE
+ 37: madr r1.xy(TRUE), h2, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).xyxx, r1.zwzz
+ 39: texpkb h2(TRUE), r1, #0
+ 41: addh_d2 h0(TRUE), h0, h2
+ 42: maxh h2.w(TRUE), h4, h4.x
+ 43: minh h2.x(TRUE), h5.w, h4.z
+ 44: addh_d2 h0(TRUE), h0, h1
+ 45: slth h2.x(TRUE), h0.w, h2
+ 46: sgth h2.w(TRUE), h0, h2
+ 47: movh h0(TRUE), h0
+ 48: addx.c0 rc(TRUE), h2, h2.w
+ 49: movh h0(c0.NE.x), h1
+
+IPU0 ------ Simplified schedule: --------
+Pass | Unit | uOp | PC: Op
+-----+--------+------+-------------------------
+ 1 | SCT0/1 | mov | 0: TXLr h0.w, g[TEX1].zyxx, const.xxxx, TEX0;
+ | TEX | txl | 0: TXLr h0.w, g[TEX1].zyxx, const.xxxx, TEX0;
+ | SCB1 | add | 2: ADDh h2.z, h0.--w-, const.--x-;
+ | | |
+ 2 | SCT0/1 | mov | 4: TXLr h0.w, g[TEX1].xwxx, const.xxxx, TEX0;
+ | TEX | txl | 4: TXLr h0.w, g[TEX1].xwxx, const.xxxx, TEX0;
+ | SCB1 | add | 6: ADDh h0.z,-h2, h0.--w-;
+ | | |
+ 3 | SCT0/1 | mov | 7: TXLr h1.w, g[TEX1], const.xxxx, TEX0;
+ | TEX | txl | 7: TXLr h1.w, g[TEX1], const.xxxx, TEX0;
+ | SCB0 | add | 9: ADDh h0.x, h0.z---,-h1.w---;
+ | SCB1 | add | 10: ADDh h3.w, h0.---z, h1;
+ | | |
+ 4 | SCT0/1 | mov | 11: TXLr h2.w, g[TEX1].zwzz, const.xxxx, TEX0;
+ | TEX | txl | 11: TXLr h2.w, g[TEX1].zwzz, const.xxxx, TEX0;
+ | SCB0 | add | 14: ADDh h0.x, h2.w---, h0;
+ | SCB1 | add | 13: ADDh h0.z, h3.--w-,-h2.--w-;
+ | | |
+ 5 | SCT1 | mov | 15: NRMh h1.xz, h0;
+ | SRB | nrm | 15: NRMh h1.xz, h0;
+ | SCB0 | min | 16: MINh*8 h0.x, |h1|, |h1.z---|;
+ | SCB1 | max | 17: MAXh h4.w, h0, h1;
+ | | |
+ 6 | SCT0 | div | 18: DIVx h2.xy, h1.xz--, h0;
+ | SCT1 | mov | 19: MOVr r1.zw, g[TEX0].--xy;
+ | SCB0 | mad | 20: MADr r2.xz,-h1, const.z-w-, r1.z-w-;
+ | SCB1 | min | 22: MINh h5.w, h0, h1;
+ | | |
+ 7 | SCT0/1 | mov | 23: TXLr h0, r2.xzxx, const.xxxx, TEX0;
+ | TEX | txl | 23: TXLr h0, r2.xzxx, const.xxxx, TEX0;
+ | SCB0 | max | 27: MAXh h4.x, h2.z---, h2.w---;
+ | SCB1 | mad | 25: MADr r0.zw, h1.--xz, const, r1;
+ | | |
+ 8 | SCT0/1 | mov | 28: TXLr h1, r0.zwzz, const.xxxx, TEX0;
+ | TEX | txl | 28: TXLr h1, r0.zwzz, const.xxxx, TEX0;
+ | SCB0/1 | add | 30: ADDh/2 h1, h0, h1;
+ | | |
+ 9 | SCT0 | mad | 31: MADr r0.xy,-h2, const.xy--, r1.zw--;
+ | SCT1 | mov | 33: TXLr h0, r0, const.zzzz, TEX0;
+ | TEX | txl | 33: TXLr h0, r0, const.zzzz, TEX0;
+ | SCB1 | min | 35: MINh h4.z, h2, h2.--w-;
+ | | |
+ 10 | SCT0 | mad | 37: MADr r1.xy, h2, const.xy--, r1.zw--;
+ | SCT1 | mov | 39: TXLr h2, r1, const.zzzz, TEX0;
+ | TEX | txl | 39: TXLr h2, r1, const.zzzz, TEX0;
+ | SCB0/1 | add | 41: ADDh/2 h0, h0, h2;
+ | | |
+ 11 | SCT0 | min | 43: MINh h2.x, h5.w---, h4.z---;
+ | SCT1 | max | 42: MAXh h2.w, h4, h4.---x;
+ | SCB0/1 | add | 44: ADDh/2 h0, h0, h1;
+ | | |
+ 12 | SCT0 | set | 45: SLTh h2.x, h0.w---, h2;
+ | SCT1 | set | 46: SGTh h2.w, h0, h2;
+ | SCB0/1 | mul | 47: MOVh h0, h0;
+ | | |
+ 13 | SCT0 | mad | 48: ADDxc0_s rc, h2, h2.w---;
+ | SCB0/1 | mul | 49: MOVh h0(NE0.xxxx), h1;
+
+Pass SCT TEX SCB
+ 1: 0% 100% 25%
+ 2: 0% 100% 25%
+ 3: 0% 100% 50%
+ 4: 0% 100% 50%
+ 5: 0% 0% 50%
+ 6: 100% 0% 75%
+ 7: 0% 100% 75%
+ 8: 0% 100% 100%
+ 9: 0% 100% 25%
+ 10: 0% 100% 100%
+ 11: 50% 0% 100%
+ 12: 50% 0% 100%
+ 13: 25% 0% 100%
+
+MEAN: 17% 61% 67%
+
+Pass SCT0 SCT1 TEX SCB0 SCB1
+ 1: 0% 0% 100% 0% 100%
+ 2: 0% 0% 100% 0% 100%
+ 3: 0% 0% 100% 100% 100%
+ 4: 0% 0% 100% 100% 100%
+ 5: 0% 0% 0% 100% 100%
+ 6: 100% 100% 0% 100% 100%
+ 7: 0% 0% 100% 100% 100%
+ 8: 0% 0% 100% 100% 100%
+ 9: 0% 0% 100% 0% 100%
+ 10: 0% 0% 100% 100% 100%
+ 11: 100% 100% 0% 100% 100%
+ 12: 100% 100% 0% 100% 100%
+ 13: 100% 0% 0% 100% 100%
+
+MEAN: 30% 23% 61% 76% 100%
+Fragment Performance Setup: Driver RSX Compiler, GPU RSX, Flags 0x5
+Results 13 cycles, 3 r regs, 923,076,923 pixels/s
+============================================================================*/
+#if (FXAA_PS3 == 1) && (FXAA_EARLY_EXIT == 0)
+/*--------------------------------------------------------------------------*/
+#pragma regcount 7
+#pragma disablepc all
+#pragma option O3
+#pragma option OutColorPrec=fp16
+#pragma texformat default RGBA8
+/*==========================================================================*/
+half4 FxaaPixelShader(
+ // See FXAA Quality FxaaPixelShader() source for docs on Inputs!
+ FxaaFloat2 pos,
+ FxaaFloat4 fxaaConsolePosPos,
+ FxaaTex tex,
+ FxaaTex fxaaConsole360TexExpBiasNegOne,
+ FxaaTex fxaaConsole360TexExpBiasNegTwo,
+ FxaaFloat2 fxaaQualityRcpFrame,
+ FxaaFloat4 fxaaConsoleRcpFrameOpt,
+ FxaaFloat4 fxaaConsoleRcpFrameOpt2,
+ FxaaFloat4 fxaaConsole360RcpFrameOpt2,
+ FxaaFloat fxaaQualitySubpix,
+ FxaaFloat fxaaQualityEdgeThreshold,
+ FxaaFloat fxaaQualityEdgeThresholdMin,
+ FxaaFloat fxaaConsoleEdgeSharpness,
+ FxaaFloat fxaaConsoleEdgeThreshold,
+ FxaaFloat fxaaConsoleEdgeThresholdMin,
+ FxaaFloat4 fxaaConsole360ConstDir
+) {
+/*--------------------------------------------------------------------------*/
+// (1)
+ half4 dir;
+ half4 lumaNe = h4tex2Dlod(tex, half4(fxaaConsolePosPos.zy, 0, 0));
+ #if (FXAA_GREEN_AS_LUMA == 0)
+ lumaNe.w += half(1.0/512.0);
+ dir.x = -lumaNe.w;
+ dir.z = -lumaNe.w;
+ #else
+ lumaNe.y += half(1.0/512.0);
+ dir.x = -lumaNe.y;
+ dir.z = -lumaNe.y;
+ #endif
+/*--------------------------------------------------------------------------*/
+// (2)
+ half4 lumaSw = h4tex2Dlod(tex, half4(fxaaConsolePosPos.xw, 0, 0));
+ #if (FXAA_GREEN_AS_LUMA == 0)
+ dir.x += lumaSw.w;
+ dir.z += lumaSw.w;
+ #else
+ dir.x += lumaSw.y;
+ dir.z += lumaSw.y;
+ #endif
+/*--------------------------------------------------------------------------*/
+// (3)
+ half4 lumaNw = h4tex2Dlod(tex, half4(fxaaConsolePosPos.xy, 0, 0));
+ #if (FXAA_GREEN_AS_LUMA == 0)
+ dir.x -= lumaNw.w;
+ dir.z += lumaNw.w;
+ #else
+ dir.x -= lumaNw.y;
+ dir.z += lumaNw.y;
+ #endif
+/*--------------------------------------------------------------------------*/
+// (4)
+ half4 lumaSe = h4tex2Dlod(tex, half4(fxaaConsolePosPos.zw, 0, 0));
+ #if (FXAA_GREEN_AS_LUMA == 0)
+ dir.x += lumaSe.w;
+ dir.z -= lumaSe.w;
+ #else
+ dir.x += lumaSe.y;
+ dir.z -= lumaSe.y;
+ #endif
+/*--------------------------------------------------------------------------*/
+// (5)
+ half4 dir1_pos;
+ dir1_pos.xy = normalize(dir.xyz).xz;
+ half dirAbsMinTimesC = min(abs(dir1_pos.x), abs(dir1_pos.y)) * half(FXAA_CONSOLE_PS3_EDGE_SHARPNESS);
+/*--------------------------------------------------------------------------*/
+// (6)
+ half4 dir2_pos;
+ dir2_pos.xy = clamp(dir1_pos.xy / dirAbsMinTimesC, half(-2.0), half(2.0));
+ dir1_pos.zw = pos.xy;
+ dir2_pos.zw = pos.xy;
+ half4 temp1N;
+ temp1N.xy = dir1_pos.zw - dir1_pos.xy * fxaaConsoleRcpFrameOpt.zw;
+/*--------------------------------------------------------------------------*/
+// (7)
+ temp1N = h4tex2Dlod(tex, half4(temp1N.xy, 0.0, 0.0));
+ half4 rgby1;
+ rgby1.xy = dir1_pos.zw + dir1_pos.xy * fxaaConsoleRcpFrameOpt.zw;
+/*--------------------------------------------------------------------------*/
+// (8)
+ rgby1 = h4tex2Dlod(tex, half4(rgby1.xy, 0.0, 0.0));
+ rgby1 = (temp1N + rgby1) * 0.5;
+/*--------------------------------------------------------------------------*/
+// (9)
+ half4 temp2N;
+ temp2N.xy = dir2_pos.zw - dir2_pos.xy * fxaaConsoleRcpFrameOpt2.zw;
+ temp2N = h4tex2Dlod(tex, half4(temp2N.xy, 0.0, 0.0));
+/*--------------------------------------------------------------------------*/
+// (10)
+ half4 rgby2;
+ rgby2.xy = dir2_pos.zw + dir2_pos.xy * fxaaConsoleRcpFrameOpt2.zw;
+ rgby2 = h4tex2Dlod(tex, half4(rgby2.xy, 0.0, 0.0));
+ rgby2 = (temp2N + rgby2) * 0.5;
+/*--------------------------------------------------------------------------*/
+// (11)
+ // compilier moves these scalar ops up to other cycles
+ #if (FXAA_GREEN_AS_LUMA == 0)
+ half lumaMin = min(min(lumaNw.w, lumaSw.w), min(lumaNe.w, lumaSe.w));
+ half lumaMax = max(max(lumaNw.w, lumaSw.w), max(lumaNe.w, lumaSe.w));
+ #else
+ half lumaMin = min(min(lumaNw.y, lumaSw.y), min(lumaNe.y, lumaSe.y));
+ half lumaMax = max(max(lumaNw.y, lumaSw.y), max(lumaNe.y, lumaSe.y));
+ #endif
+ rgby2 = (rgby2 + rgby1) * 0.5;
+/*--------------------------------------------------------------------------*/
+// (12)
+ #if (FXAA_GREEN_AS_LUMA == 0)
+ bool twoTapLt = rgby2.w < lumaMin;
+ bool twoTapGt = rgby2.w > lumaMax;
+ #else
+ bool twoTapLt = rgby2.y < lumaMin;
+ bool twoTapGt = rgby2.y > lumaMax;
+ #endif
+/*--------------------------------------------------------------------------*/
+// (13)
+ if(twoTapLt || twoTapGt) rgby2 = rgby1;
+/*--------------------------------------------------------------------------*/
+ return rgby2; }
+/*==========================================================================*/
+#endif
+
+
+
+/*============================================================================
+
+ FXAA3 CONSOLE - OPTIMIZED PS3 PIXEL SHADER (WITH EARLY EXIT)
+
+==============================================================================
+The code mostly matches the assembly.
+I have a feeling that 14 cycles is possible, but was not able to get there.
+Might have to increase register count to get full performance.
+Note this shader does not use perspective interpolation.
+
+Use the following cgc options,
+
+ --fenable-bx2 --fastmath --fastprecision --nofloatbindings
+
+Use of FXAA_GREEN_AS_LUMA currently adds a cycle (16 clks).
+Will look at fixing this for FXAA 3.12.
+------------------------------------------------------------------------------
+ NVSHADERPERF OUTPUT
+------------------------------------------------------------------------------
+For reference and to aid in debug, output of NVShaderPerf should match this,
+
+Shader to schedule:
+ 0: texpkb h0.w(TRUE), v5.zyxx, #0
+ 2: addh h2.y(TRUE), h0.w, constant(0.001953, 0.000000, 0.000000, 0.000000).x
+ 4: texpkb h1.w(TRUE), v5.xwxx, #0
+ 6: addh h0.x(TRUE), h1.w, -h2.y
+ 7: texpkb h2.w(TRUE), v5.zwzz, #0
+ 9: minh h4.w(TRUE), h2.y, h2
+ 10: maxh h5.x(TRUE), h2.y, h2.w
+ 11: texpkb h0.w(TRUE), v5, #0
+ 13: addh h3.w(TRUE), -h0, h0.x
+ 14: addh h0.x(TRUE), h0.w, h0
+ 15: addh h0.z(TRUE), -h2.w, h0.x
+ 16: addh h0.x(TRUE), h2.w, h3.w
+ 17: minh h5.y(TRUE), h0.w, h1.w
+ 18: nrmh h2.xz(TRUE), h0_n
+ 19: minh_m8 h2.w(TRUE), |h2.x|, |h2.z|
+ 20: divx h4.xy(TRUE), h2_n.xzzw, h2_n.w
+ 21: movr r1.zw(TRUE), v4.xxxy
+ 22: maxh h2.w(TRUE), h0, h1
+ 23: fenct TRUE
+ 24: madr r0.xy(TRUE), -h2.xzzw, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).zwzz, r1.zwzz
+ 26: texpkb h0(TRUE), r0, #0
+ 28: maxh h5.x(TRUE), h2.w, h5
+ 29: minh h5.w(TRUE), h5.y, h4
+ 30: madr r1.xy(TRUE), h2.xzzw, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).zwzz, r1.zwzz
+ 32: texpkb h2(TRUE), r1, #0
+ 34: addh_d2 h2(TRUE), h0, h2
+ 35: texpkb h1(TRUE), v4, #0
+ 37: maxh h5.y(TRUE), h5.x, h1.w
+ 38: minh h4.w(TRUE), h1, h5
+ 39: madr r0.xy(TRUE), -h4, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).xyxx, r1.zwzz
+ 41: texpkb h0(TRUE), r0, #0
+ 43: addh_m8 h5.z(TRUE), h5.y, -h4.w
+ 44: madr r2.xy(TRUE), h4, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).xyxx, r1.zwzz
+ 46: texpkb h3(TRUE), r2, #0
+ 48: addh_d2 h0(TRUE), h0, h3
+ 49: addh_d2 h3(TRUE), h0, h2
+ 50: movh h0(TRUE), h3
+ 51: slth h3.x(TRUE), h3.w, h5.w
+ 52: sgth h3.w(TRUE), h3, h5.x
+ 53: addx.c0 rc(TRUE), h3.x, h3
+ 54: slth.c0 rc(TRUE), h5.z, h5
+ 55: movh h0(c0.NE.w), h2
+ 56: movh h0(c0.NE.x), h1
+
+IPU0 ------ Simplified schedule: --------
+Pass | Unit | uOp | PC: Op
+-----+--------+------+-------------------------
+ 1 | SCT0/1 | mov | 0: TXLr h0.w, g[TEX1].zyxx, const.xxxx, TEX0;
+ | TEX | txl | 0: TXLr h0.w, g[TEX1].zyxx, const.xxxx, TEX0;
+ | SCB0 | add | 2: ADDh h2.y, h0.-w--, const.-x--;
+ | | |
+ 2 | SCT0/1 | mov | 4: TXLr h1.w, g[TEX1].xwxx, const.xxxx, TEX0;
+ | TEX | txl | 4: TXLr h1.w, g[TEX1].xwxx, const.xxxx, TEX0;
+ | SCB0 | add | 6: ADDh h0.x, h1.w---,-h2.y---;
+ | | |
+ 3 | SCT0/1 | mov | 7: TXLr h2.w, g[TEX1].zwzz, const.xxxx, TEX0;
+ | TEX | txl | 7: TXLr h2.w, g[TEX1].zwzz, const.xxxx, TEX0;
+ | SCB0 | max | 10: MAXh h5.x, h2.y---, h2.w---;
+ | SCB1 | min | 9: MINh h4.w, h2.---y, h2;
+ | | |
+ 4 | SCT0/1 | mov | 11: TXLr h0.w, g[TEX1], const.xxxx, TEX0;
+ | TEX | txl | 11: TXLr h0.w, g[TEX1], const.xxxx, TEX0;
+ | SCB0 | add | 14: ADDh h0.x, h0.w---, h0;
+ | SCB1 | add | 13: ADDh h3.w,-h0, h0.---x;
+ | | |
+ 5 | SCT0 | mad | 16: ADDh h0.x, h2.w---, h3.w---;
+ | SCT1 | mad | 15: ADDh h0.z,-h2.--w-, h0.--x-;
+ | SCB0 | min | 17: MINh h5.y, h0.-w--, h1.-w--;
+ | | |
+ 6 | SCT1 | mov | 18: NRMh h2.xz, h0;
+ | SRB | nrm | 18: NRMh h2.xz, h0;
+ | SCB1 | min | 19: MINh*8 h2.w, |h2.---x|, |h2.---z|;
+ | | |
+ 7 | SCT0 | div | 20: DIVx h4.xy, h2.xz--, h2.ww--;
+ | SCT1 | mov | 21: MOVr r1.zw, g[TEX0].--xy;
+ | SCB1 | max | 22: MAXh h2.w, h0, h1;
+ | | |
+ 8 | SCT0 | mad | 24: MADr r0.xy,-h2.xz--, const.zw--, r1.zw--;
+ | SCT1 | mov | 26: TXLr h0, r0, const.xxxx, TEX0;
+ | TEX | txl | 26: TXLr h0, r0, const.xxxx, TEX0;
+ | SCB0 | max | 28: MAXh h5.x, h2.w---, h5;
+ | SCB1 | min | 29: MINh h5.w, h5.---y, h4;
+ | | |
+ 9 | SCT0 | mad | 30: MADr r1.xy, h2.xz--, const.zw--, r1.zw--;
+ | SCT1 | mov | 32: TXLr h2, r1, const.xxxx, TEX0;
+ | TEX | txl | 32: TXLr h2, r1, const.xxxx, TEX0;
+ | SCB0/1 | add | 34: ADDh/2 h2, h0, h2;
+ | | |
+ 10 | SCT0/1 | mov | 35: TXLr h1, g[TEX0], const.xxxx, TEX0;
+ | TEX | txl | 35: TXLr h1, g[TEX0], const.xxxx, TEX0;
+ | SCB0 | max | 37: MAXh h5.y, h5.-x--, h1.-w--;
+ | SCB1 | min | 38: MINh h4.w, h1, h5;
+ | | |
+ 11 | SCT0 | mad | 39: MADr r0.xy,-h4, const.xy--, r1.zw--;
+ | SCT1 | mov | 41: TXLr h0, r0, const.zzzz, TEX0;
+ | TEX | txl | 41: TXLr h0, r0, const.zzzz, TEX0;
+ | SCB0 | mad | 44: MADr r2.xy, h4, const.xy--, r1.zw--;
+ | SCB1 | add | 43: ADDh*8 h5.z, h5.--y-,-h4.--w-;
+ | | |
+ 12 | SCT0/1 | mov | 46: TXLr h3, r2, const.xxxx, TEX0;
+ | TEX | txl | 46: TXLr h3, r2, const.xxxx, TEX0;
+ | SCB0/1 | add | 48: ADDh/2 h0, h0, h3;
+ | | |
+ 13 | SCT0/1 | mad | 49: ADDh/2 h3, h0, h2;
+ | SCB0/1 | mul | 50: MOVh h0, h3;
+ | | |
+ 14 | SCT0 | set | 51: SLTh h3.x, h3.w---, h5.w---;
+ | SCT1 | set | 52: SGTh h3.w, h3, h5.---x;
+ | SCB0 | set | 54: SLThc0 rc, h5.z---, h5;
+ | SCB1 | add | 53: ADDxc0_s rc, h3.---x, h3;
+ | | |
+ 15 | SCT0/1 | mul | 55: MOVh h0(NE0.wwww), h2;
+ | SCB0/1 | mul | 56: MOVh h0(NE0.xxxx), h1;
+
+Pass SCT TEX SCB
+ 1: 0% 100% 25%
+ 2: 0% 100% 25%
+ 3: 0% 100% 50%
+ 4: 0% 100% 50%
+ 5: 50% 0% 25%
+ 6: 0% 0% 25%
+ 7: 100% 0% 25%
+ 8: 0% 100% 50%
+ 9: 0% 100% 100%
+ 10: 0% 100% 50%
+ 11: 0% 100% 75%
+ 12: 0% 100% 100%
+ 13: 100% 0% 100%
+ 14: 50% 0% 50%
+ 15: 100% 0% 100%
+
+MEAN: 26% 60% 56%
+
+Pass SCT0 SCT1 TEX SCB0 SCB1
+ 1: 0% 0% 100% 100% 0%
+ 2: 0% 0% 100% 100% 0%
+ 3: 0% 0% 100% 100% 100%
+ 4: 0% 0% 100% 100% 100%
+ 5: 100% 100% 0% 100% 0%
+ 6: 0% 0% 0% 0% 100%
+ 7: 100% 100% 0% 0% 100%
+ 8: 0% 0% 100% 100% 100%
+ 9: 0% 0% 100% 100% 100%
+ 10: 0% 0% 100% 100% 100%
+ 11: 0% 0% 100% 100% 100%
+ 12: 0% 0% 100% 100% 100%
+ 13: 100% 100% 0% 100% 100%
+ 14: 100% 100% 0% 100% 100%
+ 15: 100% 100% 0% 100% 100%
+
+MEAN: 33% 33% 60% 86% 80%
+Fragment Performance Setup: Driver RSX Compiler, GPU RSX, Flags 0x5
+Results 15 cycles, 3 r regs, 800,000,000 pixels/s
+============================================================================*/
+#if (FXAA_PS3 == 1) && (FXAA_EARLY_EXIT == 1)
+/*--------------------------------------------------------------------------*/
+#pragma regcount 7
+#pragma disablepc all
+#pragma option O2
+#pragma option OutColorPrec=fp16
+#pragma texformat default RGBA8
+/*==========================================================================*/
+half4 FxaaPixelShader(
+ // See FXAA Quality FxaaPixelShader() source for docs on Inputs!
+ FxaaFloat2 pos,
+ FxaaFloat4 fxaaConsolePosPos,
+ FxaaTex tex,
+ FxaaTex fxaaConsole360TexExpBiasNegOne,
+ FxaaTex fxaaConsole360TexExpBiasNegTwo,
+ FxaaFloat2 fxaaQualityRcpFrame,
+ FxaaFloat4 fxaaConsoleRcpFrameOpt,
+ FxaaFloat4 fxaaConsoleRcpFrameOpt2,
+ FxaaFloat4 fxaaConsole360RcpFrameOpt2,
+ FxaaFloat fxaaQualitySubpix,
+ FxaaFloat fxaaQualityEdgeThreshold,
+ FxaaFloat fxaaQualityEdgeThresholdMin,
+ FxaaFloat fxaaConsoleEdgeSharpness,
+ FxaaFloat fxaaConsoleEdgeThreshold,
+ FxaaFloat fxaaConsoleEdgeThresholdMin,
+ FxaaFloat4 fxaaConsole360ConstDir
+) {
+/*--------------------------------------------------------------------------*/
+// (1)
+ half4 rgbyNe = h4tex2Dlod(tex, half4(fxaaConsolePosPos.zy, 0, 0));
+ #if (FXAA_GREEN_AS_LUMA == 0)
+ half lumaNe = rgbyNe.w + half(1.0/512.0);
+ #else
+ half lumaNe = rgbyNe.y + half(1.0/512.0);
+ #endif
+/*--------------------------------------------------------------------------*/
+// (2)
+ half4 lumaSw = h4tex2Dlod(tex, half4(fxaaConsolePosPos.xw, 0, 0));
+ #if (FXAA_GREEN_AS_LUMA == 0)
+ half lumaSwNegNe = lumaSw.w - lumaNe;
+ #else
+ half lumaSwNegNe = lumaSw.y - lumaNe;
+ #endif
+/*--------------------------------------------------------------------------*/
+// (3)
+ half4 lumaNw = h4tex2Dlod(tex, half4(fxaaConsolePosPos.xy, 0, 0));
+ #if (FXAA_GREEN_AS_LUMA == 0)
+ half lumaMaxNwSw = max(lumaNw.w, lumaSw.w);
+ half lumaMinNwSw = min(lumaNw.w, lumaSw.w);
+ #else
+ half lumaMaxNwSw = max(lumaNw.y, lumaSw.y);
+ half lumaMinNwSw = min(lumaNw.y, lumaSw.y);
+ #endif
+/*--------------------------------------------------------------------------*/
+// (4)
+ half4 lumaSe = h4tex2Dlod(tex, half4(fxaaConsolePosPos.zw, 0, 0));
+ #if (FXAA_GREEN_AS_LUMA == 0)
+ half dirZ = lumaNw.w + lumaSwNegNe;
+ half dirX = -lumaNw.w + lumaSwNegNe;
+ #else
+ half dirZ = lumaNw.y + lumaSwNegNe;
+ half dirX = -lumaNw.y + lumaSwNegNe;
+ #endif
+/*--------------------------------------------------------------------------*/
+// (5)
+ half3 dir;
+ dir.y = 0.0;
+ #if (FXAA_GREEN_AS_LUMA == 0)
+ dir.x = lumaSe.w + dirX;
+ dir.z = -lumaSe.w + dirZ;
+ half lumaMinNeSe = min(lumaNe, lumaSe.w);
+ #else
+ dir.x = lumaSe.y + dirX;
+ dir.z = -lumaSe.y + dirZ;
+ half lumaMinNeSe = min(lumaNe, lumaSe.y);
+ #endif
+/*--------------------------------------------------------------------------*/
+// (6)
+ half4 dir1_pos;
+ dir1_pos.xy = normalize(dir).xz;
+ half dirAbsMinTimes8 = min(abs(dir1_pos.x), abs(dir1_pos.y)) * half(FXAA_CONSOLE_PS3_EDGE_SHARPNESS);
+/*--------------------------------------------------------------------------*/
+// (7)
+ half4 dir2_pos;
+ dir2_pos.xy = clamp(dir1_pos.xy / dirAbsMinTimes8, half(-2.0), half(2.0));
+ dir1_pos.zw = pos.xy;
+ dir2_pos.zw = pos.xy;
+ #if (FXAA_GREEN_AS_LUMA == 0)
+ half lumaMaxNeSe = max(lumaNe, lumaSe.w);
+ #else
+ half lumaMaxNeSe = max(lumaNe, lumaSe.y);
+ #endif
+/*--------------------------------------------------------------------------*/
+// (8)
+ half4 temp1N;
+ temp1N.xy = dir1_pos.zw - dir1_pos.xy * fxaaConsoleRcpFrameOpt.zw;
+ temp1N = h4tex2Dlod(tex, half4(temp1N.xy, 0.0, 0.0));
+ half lumaMax = max(lumaMaxNwSw, lumaMaxNeSe);
+ half lumaMin = min(lumaMinNwSw, lumaMinNeSe);
+/*--------------------------------------------------------------------------*/
+// (9)
+ half4 rgby1;
+ rgby1.xy = dir1_pos.zw + dir1_pos.xy * fxaaConsoleRcpFrameOpt.zw;
+ rgby1 = h4tex2Dlod(tex, half4(rgby1.xy, 0.0, 0.0));
+ rgby1 = (temp1N + rgby1) * 0.5;
+/*--------------------------------------------------------------------------*/
+// (10)
+ half4 rgbyM = h4tex2Dlod(tex, half4(pos.xy, 0.0, 0.0));
+ #if (FXAA_GREEN_AS_LUMA == 0)
+ half lumaMaxM = max(lumaMax, rgbyM.w);
+ half lumaMinM = min(lumaMin, rgbyM.w);
+ #else
+ half lumaMaxM = max(lumaMax, rgbyM.y);
+ half lumaMinM = min(lumaMin, rgbyM.y);
+ #endif
+/*--------------------------------------------------------------------------*/
+// (11)
+ half4 temp2N;
+ temp2N.xy = dir2_pos.zw - dir2_pos.xy * fxaaConsoleRcpFrameOpt2.zw;
+ temp2N = h4tex2Dlod(tex, half4(temp2N.xy, 0.0, 0.0));
+ half4 rgby2;
+ rgby2.xy = dir2_pos.zw + dir2_pos.xy * fxaaConsoleRcpFrameOpt2.zw;
+ half lumaRangeM = (lumaMaxM - lumaMinM) / FXAA_CONSOLE_PS3_EDGE_THRESHOLD;
+/*--------------------------------------------------------------------------*/
+// (12)
+ rgby2 = h4tex2Dlod(tex, half4(rgby2.xy, 0.0, 0.0));
+ rgby2 = (temp2N + rgby2) * 0.5;
+/*--------------------------------------------------------------------------*/
+// (13)
+ rgby2 = (rgby2 + rgby1) * 0.5;
+/*--------------------------------------------------------------------------*/
+// (14)
+ #if (FXAA_GREEN_AS_LUMA == 0)
+ bool twoTapLt = rgby2.w < lumaMin;
+ bool twoTapGt = rgby2.w > lumaMax;
+ #else
+ bool twoTapLt = rgby2.y < lumaMin;
+ bool twoTapGt = rgby2.y > lumaMax;
+ #endif
+ bool earlyExit = lumaRangeM < lumaMax;
+ bool twoTap = twoTapLt || twoTapGt;
+/*--------------------------------------------------------------------------*/
+// (15)
+ if(twoTap) rgby2 = rgby1;
+ if(earlyExit) rgby2 = rgbyM;
+/*--------------------------------------------------------------------------*/
+ return rgby2; }
+/*==========================================================================*/
+#endif
+
+uniform sampler2D fb_color0;
+uniform vec2 fb_texel;
+noperspective varying vec2 texcoord;
+
+void main(void) {
+ out_Color = FxaaPixelShader(
+ //
+ // Use noperspective interpolation here (turn off perspective interpolation).
+ // {xy} = center of pixel
+ texcoord, // FxaaFloat2 pos,
+ //
+ // Used only for FXAA Console, and not used on the 360 version.
+ // Use noperspective interpolation here (turn off perspective interpolation).
+ // {xy__} = upper left of pixel
+ // {__zw} = lower right of pixel
+ vec4(0.0), // FxaaFloat4 fxaaConsolePosPos,
+ //
+ // Input color texture.
+ // {rgb_} = color in linear or perceptual color space
+ // if (FXAA_GREEN_AS_LUMA == 0)
+ // {___a} = luma in perceptual color space (not linear)
+ fb_color0, // FxaaTex tex,
+ //
+ // Only used on the optimized 360 version of FXAA Console.
+ // For everything but 360, just use the same input here as for "tex".
+ // For 360, same texture, just alias with a 2nd sampler.
+ // This sampler needs to have an exponent bias of -1.
+ fb_color0, // FxaaTex fxaaConsole360TexExpBiasNegOne,
+ //
+ // Only used on the optimized 360 version of FXAA Console.
+ // For everything but 360, just use the same input here as for "tex".
+ // For 360, same texture, just alias with a 3nd sampler.
+ // This sampler needs to have an exponent bias of -2.
+ fb_color0, // FxaaTex fxaaConsole360TexExpBiasNegTwo,
+ //
+ // Only used on FXAA Quality.
+ // This must be from a constant/uniform.
+ // {x_} = 1.0/screenWidthInPixels
+ // {_y} = 1.0/screenHeightInPixels
+ fb_texel, // FxaaFloat2 fxaaQualityRcpFrame,
+ //
+ // Only used on FXAA Console.
+ // This must be from a constant/uniform.
+ // This effects sub-pixel AA quality and inversely sharpness.
+ // Where N ranges between,
+ // N = 0.50 (default)
+ // N = 0.33 (sharper)
+ // {x___} = -N/screenWidthInPixels
+ // {_y__} = -N/screenHeightInPixels
+ // {__z_} = N/screenWidthInPixels
+ // {___w} = N/screenHeightInPixels
+ vec4(0.0), // FxaaFloat4 fxaaConsoleRcpFrameOpt,
+ //
+ // Only used on FXAA Console.
+ // Not used on 360, but used on PS3 and PC.
+ // This must be from a constant/uniform.
+ // {x___} = -2.0/screenWidthInPixels
+ // {_y__} = -2.0/screenHeightInPixels
+ // {__z_} = 2.0/screenWidthInPixels
+ // {___w} = 2.0/screenHeightInPixels
+ vec4(0.0), // FxaaFloat4 fxaaConsoleRcpFrameOpt2,
+ //
+ // Only used on FXAA Console.
+ // Only used on 360 in place of fxaaConsoleRcpFrameOpt2.
+ // This must be from a constant/uniform.
+ // {x___} = 8.0/screenWidthInPixels
+ // {_y__} = 8.0/screenHeightInPixels
+ // {__z_} = -4.0/screenWidthInPixels
+ // {___w} = -4.0/screenHeightInPixels
+ vec4(0.0), // FxaaFloat4 fxaaConsole360RcpFrameOpt2,
+ //
+ // Only used on FXAA Quality.
+ // This used to be the FXAA_QUALITY_SUBPIX define.
+ // It is here now to allow easier tuning.
+ // Choose the amount of sub-pixel aliasing removal.
+ // This can effect sharpness.
+ // 1.00 - upper limit (softer)
+ // 0.75 - default amount of filtering
+ // 0.50 - lower limit (sharper, less sub-pixel aliasing removal)
+ // 0.25 - almost off
+ // 0.00 - completely off
+ 1.0, // FxaaFloat fxaaQualitySubpix,
+ //
+ // Only used on FXAA Quality.
+ // This used to be the FXAA_QUALITY_EDGE_THRESHOLD define.
+ // It is here now to allow easier tuning.
+ // The minimum amount of local contrast required to apply algorithm.
+ // 0.333 - too little (faster)
+ // 0.250 - low quality
+ // 0.166 - default
+ // 0.125 - high quality
+ // 0.063 - overkill (slower)
+ 0.063, // FxaaFloat fxaaQualityEdgeThreshold,
+ //
+ // Only used on FXAA Quality.
+ // This used to be the FXAA_QUALITY_EDGE_THRESHOLD_MIN define.
+ // It is here now to allow easier tuning.
+ // Trims the algorithm from processing darks.
+ // 0.0833 - upper limit (default, the start of visible unfiltered edges)
+ // 0.0625 - high quality (faster)
+ // 0.0312 - visible limit (slower)
+ // Special notes when using FXAA_GREEN_AS_LUMA,
+ // Likely want to set this to zero.
+ // As colors that are mostly not-green
+ // will appear very dark in the green channel!
+ // Tune by looking at mostly non-green content,
+ // then start at zero and increase until aliasing is a problem.
+ 0.0312, // FxaaFloat fxaaQualityEdgeThresholdMin,
+ //
+ // Only used on FXAA Console.
+ // This used to be the FXAA_CONSOLE_EDGE_SHARPNESS define.
+ // It is here now to allow easier tuning.
+ // This does not effect PS3, as this needs to be compiled in.
+ // Use FXAA_CONSOLE_PS3_EDGE_SHARPNESS for PS3.
+ // Due to the PS3 being ALU bound,
+ // there are only three safe values here: 2 and 4 and 8.
+ // These options use the shaders ability to a free *|/ by 2|4|8.
+ // For all other platforms can be a non-power of two.
+ // 8.0 is sharper (default!!!)
+ // 4.0 is softer
+ // 2.0 is really soft (good only for vector graphics inputs)
+ 8.0, // FxaaFloat fxaaConsoleEdgeSharpness,
+ //
+ // Only used on FXAA Console.
+ // This used to be the FXAA_CONSOLE_EDGE_THRESHOLD define.
+ // It is here now to allow easier tuning.
+ // This does not effect PS3, as this needs to be compiled in.
+ // Use FXAA_CONSOLE_PS3_EDGE_THRESHOLD for PS3.
+ // Due to the PS3 being ALU bound,
+ // there are only two safe values here: 1/4 and 1/8.
+ // These options use the shaders ability to a free *|/ by 2|4|8.
+ // The console setting has a different mapping than the quality setting.
+ // Other platforms can use other values.
+ // 0.125 leaves less aliasing, but is softer (default!!!)
+ // 0.25 leaves more aliasing, and is sharper
+ 0.125, // FxaaFloat fxaaConsoleEdgeThreshold,
+ //
+ // Only used on FXAA Console.
+ // This used to be the FXAA_CONSOLE_EDGE_THRESHOLD_MIN define.
+ // It is here now to allow easier tuning.
+ // Trims the algorithm from processing darks.
+ // The console setting has a different mapping than the quality setting.
+ // This only applies when FXAA_EARLY_EXIT is 1.
+ // This does not apply to PS3,
+ // PS3 was simplified to avoid more shader instructions.
+ // 0.06 - faster but more aliasing in darks
+ // 0.05 - default
+ // 0.04 - slower and less aliasing in darks
+ // Special notes when using FXAA_GREEN_AS_LUMA,
+ // Likely want to set this to zero.
+ // As colors that are mostly not-green
+ // will appear very dark in the green channel!
+ // Tune by looking at mostly non-green content,
+ // then start at zero and increase until aliasing is a problem.
+ 0.05, // FxaaFloat fxaaConsoleEdgeThresholdMin,
+ //
+ // Extra constants for 360 FXAA Console only.
+ // Use zeros or anything else for other platforms.
+ // These must be in physical constant registers and NOT immedates.
+ // Immedates will result in compiler un-optimizing.
+ // {xyzw} = float4(1.0, -1.0, 0.25, -0.25)
+ vec4(0.0) // FxaaFloat4 fxaaConsole360ConstDir);
+ );
+}
+
+#endif // FRAGMENT_SHADER
A => liminal_legacy/liminal/assets/shaders/ppfx/luma.glsl +18 -0
@@ 0,0 1,18 @@
+#include "std/std.glsl"
+#include "copy.glsl"
+
+#if FRAGMENT_SHADER
+
+uniform sampler2D fb_color0;
+
+// CCIR 601
+const vec3 gray = vec3(0.299, 0.587, 0.114);
+// Rec. 709
+//const vec3 gray = vec3(0.2126, 0.7152, 0.0722);
+
+void main() {
+ vec3 color = texture(fb_color0, absolute_texcoord).rgb;
+ out_Color.r = dot(color, gray);
+}
+
+#endif
No newline at end of file
A => liminal_legacy/liminal/assets/shaders/ppfx/oculus_rift.glsl +63 -0
@@ 0,0 1,63 @@
+
+#include "std/std.glsl"
+#include "copy.glsl"
+
+#if FRAGMENT_SHADER
+
+uniform sampler2D fb_color0;
+
+uniform vec2 LensCenter;
+uniform vec2 ScreenCenter;
+uniform vec2 Scale;
+uniform vec2 ScaleIn;
+uniform vec4 HmdWarpParam;
+uniform vec4 ChromAbParam;
+
+// Scales input texture coordinates for distortion.
+// ScaleIn maps texture coordinates to Scales to ([-1, 1]), although top/bottom will be
+// larger due to aspect ratio.
+void main()
+{
+ vec2 lensCenter = LensCenter;
+ vec2 screenCenter = ScreenCenter;
+
+ vec2 oTexCoord = screen_texcoord;
+
+ float f = (eye_id+1.0)*0.5;
+ lensCenter.x = mix(LensCenter.x, 1.0 - LensCenter.x, f);
+ screenCenter.x = mix(ScreenCenter.x, 1.0 - ScreenCenter.x, f);
+
+ vec2 theta = (oTexCoord - lensCenter) * ScaleIn;
+ float rSq= theta.x * theta.x + theta.y * theta.y;
+ vec2 theta1 = theta * (HmdWarpParam.x + HmdWarpParam.y * rSq +
+ HmdWarpParam.z * rSq * rSq + HmdWarpParam.w * rSq * rSq * rSq);
+
+ // Detect whether blue texture coordinates are out of range since these will scaled out the furthest.
+ vec2 thetaBlue = theta1 * (ChromAbParam.z + ChromAbParam.w * rSq);
+ vec2 tcBlue = lensCenter + Scale * thetaBlue;
+
+ if (!all(equal(clamp(tcBlue, screenCenter-vec2(0.25,0.5), screenCenter+vec2(0.25,0.5)), tcBlue)))
+ {
+ out_Color = vec4(0.3,0.3,0.3,1.0);
+ return;
+ }
+
+ // Now do blue texture lookup.
+ float blue = texture(fb_color0, tcBlue).b;
+
+ // Do green lookup (no scaling).
+ vec2 tcGreen = lensCenter + Scale * theta1;
+ vec4 center = texture(fb_color0, tcGreen);
+
+ // Do red scale and lookup.
+ vec2 thetaRed = theta1 * (ChromAbParam.x + ChromAbParam.y * rSq);
+ vec2 tcRed = lensCenter + Scale * thetaRed;
+ float red = texture(fb_color0, tcRed).r;
+
+ out_Color = vec4(red, center.g, blue, 1);
+#if 0
+ out_Color.rgb += vec3(eye_id<0?1.0:0.0, eye_id>0?1.0:0.0, eye_id==0?1.0:0.0);
+#endif
+}
+
+#endif
A => liminal_legacy/liminal/assets/shaders/ppfx/ppfx_debug_depth.fs +12 -0
@@ 0,0 1,12 @@
+uniform sampler2D fb_color0;
+
+const float stepsize = 100.0;
+
+void main() {
+ vec2 uv = gl_TexCoord[0].st;
+ uv = ((uv - viewport_offset) / viewport_scale);
+
+ float d = mod(texture2D(fb_color0, uv).r * stepsize, 1.0);
+
+ gl_FragColor = vec4(vec3(d), 1.0);
+}
No newline at end of file
A => liminal_legacy/liminal/assets/shaders/ppfx/ppfx_hdr_clip.fs +8 -0
@@ 0,0 1,8 @@
+uniform sampler2D fb_color0;
+uniform sampler2D blurfx_color1;
+
+void main() {
+ vec3 color = texture2D(fb_color0, gl_TexCoord[0].st).rgb
+ + texture2D(blurfx_color1, gl_TexCoord[0].st).rgb;
+ gl_FragColor = vec4(color,1.0);
+}
A => liminal_legacy/liminal/assets/shaders/ppfx/ppfx_max.fs +7 -0
@@ 0,0 1,7 @@
+uniform sampler2D fbA_color0;
+uniform sampler2D fbB_color0;
+
+
+void main() {
+ gl_FragColor = max(texture2D(fbA_color0, gl_TexCoord[0].st), texture2D(fbB_color0, gl_TexCoord[0].st));
+}
No newline at end of file
A => liminal_legacy/liminal/assets/shaders/ppfx/tonemap.glsl +153 -0
@@ 0,0 1,153 @@
+
+#include "std/std.glsl"
+#include "copy.glsl"
+
+#ifndef USE_GRADING
+#define USE_GRADING 0
+#endif
+
+#ifndef ANIMATE_GRADING
+#define ANIMATE_GRADING 0
+#endif
+
+#ifndef USE_UV_LUT
+#define USE_UV_LUT 0
+#endif
+
+#ifndef UV_LUT_ABSOLUTE
+#define UV_LUT_ABSOLUTE 0
+#endif
+
+#ifndef USE_DITHER
+#if VIDEO_QUALITY >= VQ_HIGH
+#define USE_DITHER 1
+#else
+#define USE_DITHER 0
+#endif
+#endif
+
+#if FRAGMENT_SHADER
+
+#include "lib/srgb.glsl"
+
+uniform sampler2D fb_color0;
+
+uniform sampler3D lut_grading0;
+#if ANIMATE_GRADING
+uniform sampler3D lut_grading1;
+uniform float lut_grading_mix = 0.0;
+#endif
+
+#if USE_UV_LUT
+uniform sampler2D uv_lut;
+uniform vec2 uv_lut_scale = vec2(1.0);
+#endif
+uniform vec2 fb_size;
+
+const float shoulder_strength = 0.22; // 0.15
+const float linear_strength = 0.30; // 0.5
+const float linear_angle = 0.10;
+const float toe_strength = 0.20;
+const float toe_numerator = 0.01; // 0.02
+const float toe_denominator = 0.30;
+
+uniform float exposure = 2.0;
+
+const float linear_white = 11.2;
+const vec3 gray = vec3(0.299, 0.587, 0.114);
+
+float ff_filmic(float x) {
+ return (
+ (x*(shoulder_strength*x+linear_angle*linear_strength)+toe_strength*toe_numerator)/
+ (x*(shoulder_strength*x+linear_strength)+toe_strength*toe_denominator))
+ - toe_numerator/toe_denominator;
+}
+
+vec3 ff_filmic3(vec3 x) {
+ return (
+ (x*(shoulder_strength*x+linear_angle*linear_strength)+toe_strength*toe_numerator)/
+ (x*(shoulder_strength*x+linear_strength)+toe_strength*toe_denominator))
+ - toe_numerator/toe_denominator;
+}
+
+float ff_filmic_gamma(float linear) {
+ float x = max(0.0, linear-0.004);
+ return (x*(x*6.2+0.5))/(x*(x*6.2+1.7)+0.06);
+}
+
+vec3 ff_filmic_gamma3(vec3 linear) {
+ vec3 x = max(vec3(0.0), linear-0.004);
+ return (x*(x*6.2+0.5))/(x*(x*6.2+1.7)+0.06);
+}
+
+vec3 dither_find_closest(vec2 uv, vec3 c0)
+{
+ vec2 puv = mod(fb_size * uv, 4.0);
+ int x = int(puv.x);
+ int y = int(puv.y);
+
+ vec4 dither[4];
+
+ dither[0] = vec4( 1.0, 33.0, 9.0, 41.0);
+ dither[1] = vec4(49.0, 17.0, 57.0, 25.0);
+ dither[2] = vec4(13.0, 45.0, 5.0, 37.0);
+ dither[3] = vec4(61.0, 29.0, 53.0, 21.0);
+
+ float limit = 0.0;
+ limit = (dither[x][y]+1.0)/64.0;
+
+ return step(vec3(limit), c0);
+}
+
+vec3 dither(vec3 color) {
+ // dither 16 to 8 bit
+ vec3 bit_diff = mod(color*255.0, 1.0);
+ vec3 bit_step = dither_find_closest(screen_texcoord, bit_diff);
+ return color + (bit_step - bit_diff)/255.0;
+}
+
+void main() {
+ vec2 uv = screen_texcoord;
+
+#if USE_UV_LUT
+#if UV_LUT_ABSOLUTE
+ uv = texture(uv_lut, uv).rg;
+#else
+ uv += (texture(uv_lut, uv).rg*2.0 - 1.0) * uv_lut_scale;
+#endif
+#endif
+
+ vec3 color = texture(fb_color0, uv).rgb;
+
+ // filmic tonemapping
+ color = ff_filmic_gamma3(color * exposure);// / ff_filmic_gamma(linear_white);
+
+ // clamp
+ color = clamp(color, 0.0, 1.0);
+
+ // gamma
+ // color = lin2srgb(color);
+
+#if USE_GRADING
+ // final color grading
+#if !ANIMATE_GRADING
+ color = texture(lut_grading0, color).rgb;
+#else
+ color = mix(
+ texture(lut_grading0, color).rgb,
+ texture(lut_grading1, color).rgb,
+ lut_grading_mix);
+#endif // ANIMATE_GRADING
+#endif // USE_GRADING
+
+ float luma = dot(color, gray);
+
+#if USE_DITHER
+ // dither 16 to 8bit
+ color = dither(color);
+#endif
+
+ // store luma in alpha channel
+ out_Color = vec4(color, luma);
+}
+#endif
No newline at end of file
A => liminal_legacy/liminal/assets/shaders/profiler/simple.glsl +20 -0
@@ 0,0 1,20 @@
+#include "std/std.glsl"
+
+varying vec4 color;
+
+#if VERTEX_SHADER
+
+void main(void)
+{
+ // transformed position
+ gl_Position = mtx_proj * (mtx_view * (mtx_model * in_Position));
+ color = in_Color;
+}
+
+#elif FRAGMENT_SHADER
+
+void main() {
+ out_Color = color;
+}
+
+#endif
No newline at end of file
A => liminal_legacy/liminal/assets/shaders/sm/font_shadow.fs +11 -0
@@ 0,0 1,11 @@
+
+#include "sm/shadow_frag.glsl"
+#include "font_frag.glsl"
+
+void main(void)
+{
+ float d = read_font_alpha(gl_TexCoord[0]);
+ if (d <= 0.5)
+ discard;
+ write_shadow_frag();
+}
A => liminal_legacy/liminal/assets/shaders/sm/font_shadow.vs +9 -0
@@ 0,0 1,9 @@
+
+#include "sm/shadow_vars.glsl"
+#include "font_vert.glsl"
+
+void main(void)
+{
+ gl_Position = ftransform();
+ write_font_attribs();
+}
A => liminal_legacy/liminal/assets/shaders/sm/ppfx/blur_evsm.glsl +28 -0
@@ 0,0 1,28 @@
+#include "std/std.glsl"
+#include "ppfx/blit_layers.glsl"
+
+#if FRAGMENT_SHADER
+
+DATA_QUAL in BlitLayerData data;
+
+uniform sampler2DArray fb_color0;
+uniform vec2 fb_texel;
+uniform vec2 fb_size;
+
+vec4 sample_blur_texture(vec2 uv, vec2 offset) {
+ return texture(fb_color0, vec3(uv + offset, data.layer));
+}
+
+#include "lib/blur.glsl"
+
+void main() {
+ vec2 uv = gl_FragCoord.xy * fb_texel;
+ if (data.layer == LAYER_INIT) {
+ out_Color = blur_texture_fastgauss_5x5(uv, fb_texel, fb_size);
+ } else {
+ out_Color = blur_texture_fastgauss_3x3(uv, fb_texel, fb_size);
+ }
+ //out_Color = texelFetch(fb_color0, ivec3(gl_FragCoord.xy, layer), 0);
+}
+
+#endif
No newline at end of file
A => liminal_legacy/liminal/assets/shaders/sm/ppfx/convert_evsm.glsl +90 -0
@@ 0,0 1,90 @@
+#include "std/std.glsl"
+#define INT_LAYER 1
+#include "ppfx/blit_layers.glsl"
+
+#if FRAGMENT_SHADER
+
+DATA_QUAL in BlitLayerData data;
+
+#include "lib/evsm.glsl"
+
+#if MSAA
+uniform sampler2DMSArray fb_depth;
+#else
+uniform sampler2DArray fb_depth;
+#endif
+
+#define FETCH_EVSM_SAMPLE(X) \
+ convert_depth(texelFetch(fb_depth, \
+ ivec3(gl_FragCoord.xy, data.layer), X).r)
+
+void main() {
+#if MSAA == 16
+ vec4
+ average = FETCH_EVSM_SAMPLE(0);
+ average += FETCH_EVSM_SAMPLE(1);
+ average += FETCH_EVSM_SAMPLE(2);
+ average += FETCH_EVSM_SAMPLE(3);
+ average += FETCH_EVSM_SAMPLE(4);
+ average += FETCH_EVSM_SAMPLE(5);
+ average += FETCH_EVSM_SAMPLE(6);
+ average += FETCH_EVSM_SAMPLE(7);
+ average += FETCH_EVSM_SAMPLE(8);
+ average += FETCH_EVSM_SAMPLE(9);
+ average += FETCH_EVSM_SAMPLE(10);
+ average += FETCH_EVSM_SAMPLE(11);
+ average += FETCH_EVSM_SAMPLE(12);
+ average += FETCH_EVSM_SAMPLE(13);
+ average += FETCH_EVSM_SAMPLE(14);
+ average += FETCH_EVSM_SAMPLE(15);
+ out_Color = average * 0.0625;
+#elif MSAA == 12
+ vec4
+ average = FETCH_EVSM_SAMPLE(0);
+ average += FETCH_EVSM_SAMPLE(1);
+ average += FETCH_EVSM_SAMPLE(2);
+ average += FETCH_EVSM_SAMPLE(3);
+ average += FETCH_EVSM_SAMPLE(4);
+ average += FETCH_EVSM_SAMPLE(5);
+ average += FETCH_EVSM_SAMPLE(6);
+ average += FETCH_EVSM_SAMPLE(7);
+ average += FETCH_EVSM_SAMPLE(8);
+ average += FETCH_EVSM_SAMPLE(9);
+ average += FETCH_EVSM_SAMPLE(10);
+ average += FETCH_EVSM_SAMPLE(11);
+ out_Color = average / 12.0;
+#elif MSAA == 8
+ vec4
+ average = FETCH_EVSM_SAMPLE(0);
+ average += FETCH_EVSM_SAMPLE(1);
+ average += FETCH_EVSM_SAMPLE(2);
+ average += FETCH_EVSM_SAMPLE(3);
+ average += FETCH_EVSM_SAMPLE(4);
+ average += FETCH_EVSM_SAMPLE(5);
+ average += FETCH_EVSM_SAMPLE(6);
+ average += FETCH_EVSM_SAMPLE(7);
+ out_Color = average * 0.125;
+#elif MSAA == 4
+ vec4
+ average = FETCH_EVSM_SAMPLE(0);
+ average += FETCH_EVSM_SAMPLE(1);
+ average += FETCH_EVSM_SAMPLE(2);
+ average += FETCH_EVSM_SAMPLE(3);
+ out_Color = average * 0.25;
+#elif MSAA == 2
+ vec4
+ average = FETCH_EVSM_SAMPLE(0) + FETCH_EVSM_SAMPLE(1);
+ out_Color = average * 0.5;
+#elif MSAA
+ float weight = 1.0 / float(MSAA);
+ vec4 average = vec4(0.0);
+ for (int i = 0; i < MSAA; ++i) {
+ average += weight * FETCH_EVSM_SAMPLE(i);
+ }
+ out_Color = average;
+#else
+ out_Color = FETCH_EVSM_SAMPLE(0);
+#endif
+}
+
+#endif
A => liminal_legacy/liminal/assets/shaders/sm/shadow.glsl +40 -0
@@ 0,0 1,40 @@
+
+#include "std/std.glsl"
+
+#if VERTEX_SHADER
+
+void main(void)
+{
+ gl_Position = mtx_proj * (mtx_view * (mtx_model * vec4(in_Position.xyz,1.0)));
+}
+
+#elif GEOMETRY_SHADER
+
+layout(triangles) in;
+layout(triangle_strip, max_vertices = LAYER_GS_VERTEX_COUNT) out;
+
+uniform vec4 dm_offsets[LAYER_COUNT];
+
+void main() {
+ for (int k = 0; k < LAYER_COUNT; ++k) {
+ for(int i = 0; i < 3; ++i) {
+ gl_Layer = k;
+ vec4 p = gl_in[i].gl_Position;
+ vec4 o = dm_offsets[k];
+ gl_Position = vec4(p.xy * o.xy + o.zw, p.zw);
+ EmitVertex();
+ }
+ EndPrimitive();
+ }
+}
+
+#elif FRAGMENT_SHADER
+
+#include "sm/shadow_frag.glsl"
+
+void main(void)
+{
+ write_shadow_frag();
+}
+
+#endif
No newline at end of file
A => liminal_legacy/liminal/assets/shaders/sm/shadow_frag.glsl +4 -0
@@ 0,0 1,4 @@
+
+void write_shadow_frag() {
+ out_Color = vec4( 1.0 );
+}
A => liminal_legacy/liminal/assets/shaders/std/attrib.glsl +17 -0
@@ 0,0 1,17 @@
+
+#if VERTEX_SHADER
+in vec4 in_Position;
+in vec3 in_Normal;
+in vec4 in_Color;
+in vec4 in_Color2;
+in vec4 in_Color3;
+in vec4 in_Color4;
+in vec2 in_TexCoord0;
+in vec3 in_TexCoord1;
+in vec3 in_Tangent;
+in vec3 in_Bitangent;
+in vec3 in_Origin;
+in vec3 in_Velocity;
+#elif FRAGMENT_SHADER
+out vec4 out_Color;
+#endif
A => liminal_legacy/liminal/assets/shaders/std/compat.glsl +32 -0
@@ 0,0 1,32 @@
+#define texture1D texture
+#define texture2D texture
+#define texture3D texture
+#define textureCube texture
+#define texture1DLod textureLod
+#define texture2DLod textureLod
+#define texture3DLod textureLod
+#define textureCubeLod textureLod
+#define texture1DProj textureProj
+#define texture2DProj textureProj
+#define texture3DProj textureProj
+#define shadow2DProj custom_shadow2DProj
+
+#define gl_Vertex in_Position
+#define gl_Normal in_Normal
+#define gl_Color in_Color
+#define gl_MultiTexCoord0 in_TexCoord0
+#define gl_TexCoord inout_TexCoord
+#define gl_FrontColor inout_VertexColor
+#define ftransform custom_ftransform
+#define gl_ClipVertex out_ClipVertex
+
+#define gl_FragColor out_Color
+#define gl_Color inout_VertexColor
+#define gl_TexCoord inout_TexCoord
+#define gl_LightSource u_lightsource
+#define textureQueryLod custom_textureQueryLod
+#define textureQueryLOD custom_textureQueryLod
+
+vec4 custom_ftransform() {
+ return mtx_proj * (mtx_view * (mtx_model * in_Position));
+}
A => liminal_legacy/liminal/assets/shaders/std/std.glsl +5 -0
@@ 0,0 1,5 @@
+
+#include "std/vq.glsl"
+#include "std/ubo.glsl"
+#include "std/attrib.glsl"
+#include "std/varying.glsl"
A => liminal_legacy/liminal/assets/shaders/std/ubo.glsl +32 -0
@@ 0,0 1,32 @@
+
+layout(std140) uniform GlobalAttribs {
+ float time;
+};
+
+layout(std140) uniform CameraAttribs {
+ mat4 mtx_view;
+ mat4 mtx_camera;
+ mat4 mtx_proj;
+ mat4 mtx_viewproj;
+ mat4 mtx_inv_proj;
+ mat4 mtx_last_vp;
+ float near;
+ float far;
+};
+
+layout(std140) uniform ViewportAttribs {
+ vec2 viewport_offset;
+ vec2 viewport_scale;
+ float eye_id;
+};
+
+layout(std140) uniform ModelAttribs {
+ mat4 mtx_model;
+ mat4 mtx_last_model;
+ mat4 mtx_inv_model;
+};
+
+// model-view-projection matrix of last frame
+mat4 mtx_last_mvp() {
+ return mtx_last_vp * mtx_last_model;
+}
A => liminal_legacy/liminal/assets/shaders/std/varying.glsl +6 -0
@@ 0,0 1,6 @@
+
+#if VERTEX_SHADER
+#define varying out
+#elif FRAGMENT_SHADER
+#define varying in
+#endif // FRAGMENT_SHADER
No newline at end of file
A => liminal_legacy/liminal/assets/shaders/std/vq.glsl +7 -0
@@ 0,0 1,7 @@
+#define VQ_HIGHEST 6
+#define VQ_HIGHER 5
+#define VQ_HIGH 4
+#define VQ_MEDIUM 3
+#define VQ_LOW 2
+#define VQ_LOWER 1
+#define VQ_LOWEST 0
No newline at end of file
A => liminal_legacy/liminal/assets/shaders/ui2d/ninepatch2d.glsl +45 -0
@@ 0,0 1,45 @@
+
+#include "std/std.glsl"
+#include "ui2d/ui2d.glsl"
+
+varying vec2 texcoord;
+
+#if VERTEX_SHADER
+
+// x1, x2, y1, y2 (x0, y0 and x3,y3 are implicit (0..1))
+uniform ivec4 grid = ivec4(0, 0, 0, 0);
+uniform ivec2 gridsize = ivec2(0, 0);
+
+vec2 pixel2uv(vec2 p) {
+ p = p / vec2(gridsize);
+ //p.y = 1.0 - p.y;
+ return p;
+}
+
+void main(void) {
+ vec2 p0 = vec2(rectangle.xy);
+ vec2 p1 = vec2(grid.xy);
+ vec2 p3 = vec2(grid.zw);
+ vec2 p2 = vec2(rectangle.zw) - (p1 + p3);
+
+ vec2 tc1 = vec2(grid.xy);
+ vec2 tc3 = vec2(grid.zw);
+ vec2 tc2 = vec2(gridsize) - (tc1 + tc3);
+
+ vec2 v = in_Position.xy;
+ vec2 w0 = clamp(v, 0.0, 1.0);
+ vec2 w1 = clamp(v - 1.0, 0.0, 1.0);
+ vec2 w2 = clamp(v - 2.0, 0.0, 1.0);
+
+ gl_Position = mtx_proj * (mtx_view * (mtx_model * vec4(pixel2unit(
+ p0 + p1 * w0 + p2 * w1 + p3 * w2), 0.0, 1.0)));
+ texcoord = pixel2uv(tc1 * w0 + tc2 * w1 + tc3 * w2);
+}
+
+#elif FRAGMENT_SHADER
+
+void main() {
+ out_Color = texture(smp_texture, texcoord);
+}
+
+#endif
No newline at end of file
A => liminal_legacy/liminal/assets/shaders/ui2d/quad2d.glsl +26 -0
@@ 0,0 1,26 @@
+
+#include "std/std.glsl"
+
+#include "ui2d/ui2d.glsl"
+
+varying vec2 texcoord;
+
+#if VERTEX_SHADER
+
+void main(void) {
+ gl_Position = mtx_proj * (mtx_view * (mtx_model * vec4(
+ pixel2unit(rectangle.xy + rectangle.zw*in_Position.xy), 0.0, 1.0)));
+ texcoord = in_TexCoord0;
+}
+
+#elif FRAGMENT_SHADER
+
+uniform vec4 uvrect = vec4(0.0, 0.0, 1.0, 1.0);
+uniform vec4 color = vec4(1.0,1.0,1.0,1.0);
+
+void main() {
+ vec2 uv = uvrect.xy + texcoord * uvrect.zw;
+ out_Color = texture(smp_texture, uv) * color;
+}
+
+#endif
No newline at end of file
A => liminal_legacy/liminal/assets/shaders/ui2d/ui2d.glsl +12 -0
@@ 0,0 1,12 @@
+
+uniform vec2 screensize = vec2(800.0, 600.0);
+// x, y, width, height
+uniform ivec4 rectangle = ivec4(0, 0, 16, 16);
+uniform sampler2D smp_texture;
+
+vec2 pixel2unit(vec2 p) {
+ p = (p / screensize)*2.0 - 1.0;
+ p.y *= -1.0;
+ return p;
+}
+
A => liminal_legacy/liminal/assets/textures/blank.png +0 -0
A => liminal_legacy/liminal/assets/textures/blender_icons16.png +0 -0
A => liminal_legacy/liminal/assets/textures/lut_default.png +0 -0
A => liminal_legacy/liminal/assets/textures/spot.png +0 -0
A => liminal_legacy/liminal/assets/textures/zero.png +0 -0
A => liminal_legacy/liminal/blenderclient/__init__.py +386 -0
@@ 0,0 1,386 @@
+
+import os
+import sys
+import logging
+import weakref
+
+import bl_ui #@UnresolvedImport
+import bpy #@UnresolvedImport
+import mathutils #@UnresolvedImport
+# see http://www.blender.org/documentation/blender_python_api_2_63_0/bpy.app.handlers.html
+# for more infos
+from bpy.app.handlers import persistent #@UnresolvedImport
+
+from .structure import BlenderClient
+
+################################################################################
+
+__all__ = []
+
+################################################################################
+
+BEIGE_FILE_EXTENSION = '.blendc'
+
+#bpy.app.debug_events = True
+log = logging.getLogger('liminal.blender')
+
+################################################################################
+
+class BeigeRender(bpy.types.RenderEngine):
+ bl_idname = 'BEIGE_RENDER'
+ bl_label = "Beige"
+
+BLENDER_PANEL_BLACKLIST = {
+ 'RENDER_PT_embedded',
+ 'RENDER_PT_game_player',
+ 'RENDER_PT_game_stereo',
+ 'RENDER_PT_game_shading',
+}
+
+def augment_props(props):
+ # add liminal to all game engine panels
+ for key,value in props.__dict__.items():
+ if not hasattr(value, 'COMPAT_ENGINES'):
+ continue
+ if key in BLENDER_PANEL_BLACKLIST:
+ continue
+ if 'BLENDER_GAME' in value.COMPAT_ENGINES:
+ #print('"{0}",'.format(key))
+ value.COMPAT_ENGINES.add('BEIGE_RENDER')
+
+augment_props(bl_ui.properties_game)
+augment_props(bl_ui.properties_particle)
+augment_props(bl_ui.properties_material)
+augment_props(bl_ui.properties_data_mesh)
+augment_props(bl_ui.properties_texture)
+augment_props(bl_ui.properties_data_camera)
+augment_props(bl_ui.properties_data_lamp)
+augment_props(bl_ui.properties_world)
+augment_props(bl_ui.properties_scene)
+augment_props(bl_ui.properties_render)
+
+# liminal related properties
+################################################################################
+
+# new RNA props for scene
+class BeigeScene(bpy.types.PropertyGroup):
+ scenepass = bpy.props.IntProperty(
+ name="Scene Pass",
+ description = "Order priority / rank when rendering multiple scenes",
+ min = 0, max = 1000,
+ default = 0)
+ archive_path = bpy.props.StringProperty(
+ name="Archive",
+ description = "Where to export the blend file to",
+ default = "//data" + BEIGE_FILE_EXTENSION,
+ subtype = "FILE_PATH")
+ auto_export = bpy.props.BoolProperty(
+ name="Export on Start",
+ description = "Automatically update archive when starting player",
+ default = True)
+ fullscreen = bpy.props.BoolProperty(
+ name="Fullscreen",
+ description = "Start game in fullscreen mode",
+ default = False)
+ loglevel = bpy.props.EnumProperty(
+ items = [
+ ('debug', 'Debug', 'Include internal debugging messages'),
+ ('info', 'Info', 'Include informing messages'),
+ ('warn', 'Warn', 'Include warning messages'),
+ ('error', 'Error', 'Include error messages'),
+ ('fatal', 'Fatal', 'Print only fatal messages'),
+ ],
+ name="Loglevel",
+ description = "Loglevel of Engine when running game",
+ default = 'warn')
+ msaa = bpy.props.EnumProperty(
+ items = [
+ ('0', 'None', 'No anti-aliasing'),
+ ('2', '2x', '2x MSAA'),
+ ('4', '4x', '4x MSAA'),
+ ],
+ name="MSAA Level",
+ description = "Anti-aliasing level",
+ default = '0')
+
+bpy.utils.register_class(BeigeScene)
+bpy.types.Scene.liminal_settings = bpy.props.PointerProperty(type=BeigeScene)
+
+################################################################################
+
+class BeigeObject(bpy.types.PropertyGroup):
+ python_class = bpy.props.StringProperty(
+ name="Python Class",
+ description = "Actor class to instantiate (syntax: module.class)",
+ default = "")
+
+bpy.utils.register_class(BeigeObject)
+bpy.types.Object.liminal_settings = bpy.props.PointerProperty(type=BeigeObject)
+
+################################################################################
+
+class BeigeMaterial(bpy.types.PropertyGroup):
+ cutout = bpy.props.BoolProperty(
+ name="Cutout",
+ description = "Only render to Z-Buffer in Game Engine",
+ default = False)
+
+bpy.utils.register_class(BeigeMaterial)
+bpy.types.Material.liminal_settings = bpy.props.PointerProperty(type=BeigeMaterial)
+
+################################################################################
+
+class BeigePanel():
+ COMPAT_ENGINES = {'BEIGE_RENDER'}
+
+ @classmethod
+ def poll(cls, context):
+ rd = context.scene.render
+ return (rd.engine in cls.COMPAT_ENGINES)
+
+class BeigeScenePanel(BeigePanel, bpy.types.Panel):
+ bl_space_type = "PROPERTIES"
+ bl_region_type = "WINDOW"
+ bl_context = "render"
+ bl_label = "Beige"
+
+ def draw(self, context):
+ layout = self.layout
+
+ scene = context.scene
+ bs = scene.liminal_settings
+
+ row = layout.row()
+
+ col = row.column()
+ col.operator("view3d.liminal_start_player", text="Start")
+ col.operator("view3d.liminal_export", text="Export")
+
+ col = row.column()
+ col.prop(bs, "auto_export")
+ col.prop(bs, "fullscreen")
+ col.prop(bs, "loglevel")
+ col.prop(bs, "msaa")
+
+ row = layout.row()
+ row.prop(bs, "archive_path")
+
+ if 0:
+ row = layout.row()
+ if BlenderClient.instance:
+ button_label = "Disconnect"
+ count = BlenderClient.instance.get_todo_list_count() #@UndefinedVariable
+ if count > 0:
+ label = "Updating {0} items".format(count)
+ else:
+ label = "Connected"
+ else:
+ button_label = "Connect"
+ label = "Not connected"
+ row.operator("view3d.liminal_connect", text=button_label)
+ row.label(label)
+
+ row = layout.row()
+ row.prop(bs, "scenepass")
+
+################################################################################
+
+class BeigeObjectPanel(BeigePanel, bpy.types.Panel):
+ bl_space_type = "PROPERTIES"
+ bl_region_type = "WINDOW"
+ bl_context = "object"
+ bl_label = "Beige Object"
+
+ @classmethod
+ def poll(cls, context):
+ return (not (context.object is None)) and BeigePanel.poll(context)
+
+ def draw(self, context):
+ layout = self.layout
+
+ obj = context.object
+ bm = obj.liminal_settings
+
+ row = layout.row()
+ row.prop(bm, "python_class")
+
+################################################################################
+
+class BeigeMaterialPanel(BeigePanel, bpy.types.Panel):
+ bl_space_type = "PROPERTIES"
+ bl_region_type = "WINDOW"
+ bl_context = "material"
+ bl_label = "Beige Material"
+
+ @classmethod
+ def poll(cls, context):
+ return (not (context.material is None)) and BeigePanel.poll(context)
+
+ def draw(self, context):
+ layout = self.layout
+
+ mat = context.material
+ bm = mat.liminal_settings
+
+ row = layout.row(align=False)
+ row.prop(bm, "cutout")
+
+################################################################################
+
+def find_bin_path(bin_name):
+ if sys.platform == 'win32':
+ bin_name = bin_name + '.exe'
+ # find pypy in path
+ PATH = os.environ['PATH'].split(os.pathsep)
+ for dirpath in PATH:
+ fullpath = os.path.join(dirpath, bin_name)
+ if os.path.isfile(fullpath):
+ return fullpath
+
+def get_liminal_archive_path():
+ return bpy.path.abspath(bpy.context.scene.liminal_settings.archive_path)
+
+def launch_player():
+ #BlenderClient.start()
+ bs = bpy.context.scene.liminal_settings
+ archive_path = get_liminal_archive_path()
+ if bs.auto_export:
+ liminal_export()
+ msaa = bs.msaa
+ llevel = bs.loglevel
+ liminal_path = find_bin_path('liminal')
+ assert liminal_path, 'liminal not found in PATH'
+ args = [liminal_path,
+ '--loglevel',llevel,
+ '-a',archive_path,
+ '--msaa',msaa,
+ '--scene',bpy.context.scene.name]
+ if bs.fullscreen:
+ args += ['--fullscreen']
+ print('Launching {}...'.format(' '.join(args)))
+ pid = os.spawnv(os.P_NOWAIT, liminal_path, args)
+
+class LaunchPlayerOperator(bpy.types.Operator):
+ '''Start Beige player'''
+ bl_idname = "view3d.liminal_start_player"
+ bl_label = "Start Player"
+
+ @classmethod
+ def poll(cls, context):
+ return context.scene.render.engine == 'BEIGE_RENDER'
+
+ def execute(self, context):
+ launch_player()
+ return {'FINISHED'}
+
+################################################################################
+
+def liminal_export():
+ archive_path = get_liminal_archive_path()
+ BlenderClient.export(archive_path)
+
+class ExportOperator(bpy.types.Operator):
+ '''Export Beige archive'''
+ bl_idname = "view3d.liminal_export"
+ bl_label = "Export Beige Archive"
+
+ @classmethod
+ def poll(cls, context):
+ return context.scene.render.engine == 'BEIGE_RENDER'
+
+ def execute(self, context):
+ liminal_export()
+ return {'FINISHED'}
+
+class FileExportOperator(bpy.types.Operator):
+ '''Export Beige archive'''
+ bl_idname = "export.liminalblob"
+ bl_label = "Export Beige Archive"
+ filepath = bpy.props.StringProperty(subtype='FILE_PATH')
+ stamp = bpy.props.StringProperty()
+
+ def execute(self, context):
+ filepath = bpy.path.ensure_ext(self.filepath, BEIGE_FILE_EXTENSION)
+ BlenderClient.export(filepath, stamp=self.stamp)
+ return {'FINISHED'}
+
+ def invoke(self, context, event):
+ if not self.filepath:
+ self.filepath = bpy.path.ensure_ext(bpy.data.filepath,
+ BEIGE_FILE_EXTENSION)
+ WindowManager = context.window_manager
+ WindowManager.fileselect_add(self)
+ return {'RUNNING_MODAL'}
+
+def export_menu_func(self, context):
+ self.layout.operator(FileExportOperator.bl_idname,
+ text="Beige Archive ({})".format(BEIGE_FILE_EXTENSION))
+
+################################################################################
+
+class ToggleBeigeOperator(bpy.types.Operator):
+ '''Connect to Beige data server'''
+ bl_idname = "view3d.liminal_connect"
+ bl_label = "Connect to Beige"
+
+ @classmethod
+ def poll(cls, context):
+ return context.scene.render.engine == 'BEIGE_RENDER'
+
+ def execute(self, context):
+ BlenderClient.startstop()
+ return {'FINISHED'}
+
+################################################################################
+
+def fix_old_props():
+ return
+ for scene in bpy.data.scenes:
+ if hasattr(scene.game_settings,'scenepass'):
+ sp = getattr(scene.game_settings,'scenepass')
+ print('translating scenepass',sp)
+ scene.liminal_settings.scenepass = sp
+ for mat in bpy.data.materials:
+ if hasattr(mat.game_settings,'cutout'):
+ co = getattr(mat.game_settings,'cutout')
+ print('translating cutout',co)
+ mat.liminal_settings.cutout = co
+
+################################################################################
+
+@persistent
+def load_post(ctx):
+ fix_old_props()
+
+@persistent
+def scene_update_post(scene):
+ BlenderClient.update_post()
+
+################################################################################
+
+classes = [
+ BeigeRender,
+ BeigeScenePanel,
+ BeigeObjectPanel,
+ BeigeMaterialPanel,
+ LaunchPlayerOperator,
+ ExportOperator,
+ FileExportOperator,
+ ToggleBeigeOperator,
+]
+
+def register():
+ logging.basicConfig(level=logging.INFO)
+ for cls in classes:
+ bpy.utils.register_class(cls)
+ #bpy.app.handlers.load_post.append(load_post)
+ bpy.app.handlers.scene_update_post.append(scene_update_post)
+ bpy.types.INFO_MT_file_export.append(export_menu_func)
+ fix_old_props()
+
+def unregister():
+ bpy.types.INFO_MT_file_export.remove(export_menu_func)
+ for cls in classes:
+ bpy.utils.unregister_class(cls)
+
+################################################################################
A => liminal_legacy/liminal/blenderclient/structure.py +1122 -0
@@ 0,0 1,1122 @@
+
+import time
+import os
+import hashlib
+import binascii
+import sys
+import traceback
+import logging
+
+import bpy #@UnresolvedImport
+import gpu #@UnresolvedImport
+
+#from ..data.client import DataClient
+from ..data.source import DataArchive
+from ..extras.purify import *
+
+################################################################################
+
+log = logging.getLogger('liminal.blender')
+
+################################################################################
+
+def layers_to_flags(layers):
+ flags = 0
+ for i,layer in enumerate(layers):
+ if layer:
+ flags |= (1<<i)
+ return flags
+
+def items_to_names(items):
+ return [item.name for item in items]
+
+def matrix_to_list(mtx):
+ return [list(vec) for vec in mtx]
+
+def dirhash(path):
+ dirhash = hashlib.md5(
+ path.encode('utf-8')).hexdigest()
+ return dirhash[:8]
+
+def get_libpath(library):
+ if library:
+ filepath = library.filepath
+ else:
+ filepath = os.path.normpath(os.path.abspath(bpy.data.filepath))
+ return filepath
+
+def get_libid(library):
+ return dirhash(get_libpath(library))
+
+def resolve_libid(obj):
+ return get_libid(obj.library)
+
+def resolve_image_path(img):
+ return bpy.path.relpath(os.path.normpath(
+ bpy.path.abspath(img.filepath,
+ library = img.library)))
+
+def extract_material_shader(material):
+ shader = gpu.export_shader(bpy.context.scene, material)
+ for uniform in shader['uniforms']:
+ uniform.pop('lamp', None)
+ if 'image' in uniform:
+ uniform['image'] = resolve_image_path(uniform['image'])
+ if 'texpixels' in uniform:
+ uniform['texpixels'] = binascii.b2a_qp(
+ uniform['texpixels']).decode('utf-8')
+ return shader
+
+################################################################################
+
+def Layers():
+ return Auto(pack=layers_to_flags)
+
+def AutoRef():
+ return Auto(pack=lambda v:reference(v,uri_only=True))
+
+def AutoRefList():
+ return Auto(pack=lambda v:reference_list(v,uri_only=True))
+
+class FromLib(AutoClass):
+ @classmethod
+ def _on_purify(cls, value):
+ pass
+
+ @classmethod
+ def _reference(cls, value, **kargs):
+ cl = BlenderClient.instance
+ uri = cl.get_objroot(value) + '/' + cls._group + '/' + value.name
+ if kargs.get('uri_only', False):
+ return uri
+ if not cl.endp.hasitem(uri):
+ if kargs.get('immediate',False):
+ cl.commit(cls, uri, value)
+ else:
+ cl._todo_list[uri] = (cls, value)
+ return uri
+
+ library = Annotate(resolve_libid)
+ name = Auto()
+
+class Camera(FromLib):
+ _cls = bpy.types.Camera
+ _group = 'cameras'
+ _constants = dict(
+ type = 'camera'
+ )
+
+ angle_x = Auto()
+ angle_y = Auto()
+ angle = Auto()
+ clip_start = Auto()
+ clip_end = Auto()
+ sensor_width = Auto()
+ sensor_height = Auto()
+ ortho_scale = Auto()
+ sensor_fit = Auto()
+ shift_x = Auto()
+ shift_y = Auto()
+ mode = AutoAttr.o.type()
+
+class Scene(FromLib):
+ _cls = bpy.types.Scene
+ _group = 'scenes'
+ _constants = dict(
+ type = 'scene',
+ )
+
+ object_bases = Auto()
+ camera = AutoPointer()
+ layers = Layers()
+ background_set = AutoRef()
+ scenepass = AutoAttr.o.liminal_settings.scenepass()
+ exit_key = AutoAttr.o.game_settings.exit_key()
+ fps = AutoAttr.o.game_settings.fps()
+ logic_step_max = AutoAttr.o.game_settings.logic_step_max()
+ obstacle_simulation = AutoAttr.o.game_settings.obstacle_simulation()
+ occlusion_culling_resolution = AutoAttr.o.game_settings.occlusion_culling_resolution()
+ physics_engine = AutoAttr.o.game_settings.physics_engine()
+ physics_gravity = AutoAttr.o.game_settings.physics_gravity()
+ physics_step_max = AutoAttr.o.game_settings.physics_step_max()
+ physics_step_sub = AutoAttr.o.game_settings.physics_step_sub()
+ show_debug_properties = AutoAttr.o.game_settings.show_debug_properties()
+ show_framerate_profile = AutoAttr.o.game_settings.show_framerate_profile()
+ use_activity_culling = AutoAttr.o.game_settings.use_activity_culling()
+ use_occlusion_culling = AutoAttr.o.game_settings.use_occlusion_culling()
+
+class ObjectBase(AutoClass):
+ _cls = bpy.types.ObjectBase
+ _constants = dict(
+ type = 'object_base',
+ )
+
+ layers = Layers()
+ object = AutoPointer()
+
+def get_dupli_list(obj, attr):
+ dupli_list = []
+ if obj.dupli_type == 'GROUP':
+ obj.dupli_list_create(bpy.context.scene)
+ for dup in obj.dupli_list:
+ reference(dup.object, immediate=True)
+ dupli_list.append(dup)
+ obj.dupli_list_clear()
+
+ return dupli_list
+
+class Group(FromLib):
+ _cls = bpy.types.Group
+ _group = 'groups'
+ _constants = dict(
+ type = 'group',
+ )
+
+ dupli_offset = Auto()
+ layers = Layers()
+ objects = AutoPointerList()
+
+class Object(FromLib):
+ _cls = bpy.types.Object
+ _group = 'objects'
+ _constants = dict(
+ type = 'object',
+ )
+
+ data = AutoPointer()
+ hide = Auto()
+ hide_render = Auto()
+ matrix_basis = Auto()
+ matrix_local = Auto()
+ matrix_parent_inverse = Auto()
+ matrix_world = Auto()
+ dupli_group = AutoPointer()
+ #dupli_list = Auto(resolve=get_dupli_list)
+ actuators = AutoAttr.o.game.actuators()
+ sensors = AutoAttr.o.game.sensors()
+ controllers = AutoAttr.o.game.controllers()
+ game_properties = AutoAttr.o.game.properties()
+ children = AutoRefList()
+ physics_type = AutoAttr.o.game.physics_type()
+ use_actor = AutoAttr.o.game.use_actor()
+ use_ghost = AutoAttr.o.game.use_ghost()
+ radius = AutoAttr.o.game.radius()
+ use_collision_bounds = AutoAttr.o.game.use_collision_bounds()
+ collision_bounds_type = AutoAttr.o.game.collision_bounds_type()
+ collision_margin = AutoAttr.o.game.collision_margin()
+ use_collision_compound = AutoAttr.o.game.use_collision_compound()
+ mass = AutoAttr.o.game.mass()
+ form_factor = AutoAttr.o.game.form_factor()
+ velocity_min = AutoAttr.o.game.velocity_min()
+ velocity_max = AutoAttr.o.game.velocity_max()
+ damping = AutoAttr.o.game.damping()
+ rotation_damping = AutoAttr.o.game.rotation_damping()
+ use_sleep = AutoAttr.o.game.use_sleep()
+ use_rotate_from_normal = AutoAttr.o.game.use_rotate_from_normal()
+ use_material_physics_fh = AutoAttr.o.game.use_material_physics_fh()
+ python_class = AutoAttr.o.liminal_settings.python_class()
+
+#class DupliObject(AutoClass):
+# _cls = bpy.types.DupliObject
+#
+# hide = Auto()
+# matrix = Auto()
+# matrix_original = Auto()
+# object = AutoPointer()
+
+class GameProperty(AutoClass):
+ _cls = bpy.types.GameProperty
+
+ type = Auto() #@ReservedAssignment
+ name = Auto()
+ show_debug = Auto()
+ value = Auto()
+
+class Mesh(FromLib):
+ _cls = bpy.types.Mesh
+ _group = 'meshes'
+ _constants = dict(
+ type = 'mesh',
+ )
+
+ @classmethod
+ def _on_purify(cls, value):
+ value.update(calc_tessface=True)
+
+ vertices = Auto(resolve=lambda o,n:[vert.co for vert in o.vertices])
+ normals = Auto(resolve=lambda o,n:[vert.normal for vert in o.vertices])
+ material_indices = Auto(resolve=lambda o,n:[face.material_index for face in o.tessfaces])
+ faces = Auto(resolve=lambda o,n:[face.vertices for face in o.tessfaces])
+ vertex_colors = AutoAttr.o.tessface_vertex_colors()
+ texcoords = AutoAttr.o.tessface_uv_textures()
+ materials = AutoPointerList()
+
+class MeshColorLayer(AutoClass):
+ _cls = bpy.types.MeshColorLayer
+ _resolve = Convert.o.data()
+
+class MeshColor(AutoClass):
+ _cls = bpy.types.MeshColor
+ _resolve = lambda o: [o.color1,o.color2,o.color3,o.color4]
+
+class MeshTextureFaceLayer(AutoClass):
+ _cls = bpy.types.MeshTextureFaceLayer
+ _resolve = Convert.o.data()
+
+class MeshTextureFace(AutoClass):
+ _cls = bpy.types.MeshTextureFace
+ _resolve = Convert.o.uv()
+
+class Lamp(FromLib):
+ _cls = bpy.types.Lamp
+ _group = 'lamps'
+ _constants = dict(
+ type = 'lamp'
+ )
+
+class Material(FromLib):
+ _cls = bpy.types.Material
+ _group = 'materials'
+ _constants = dict(
+ type = 'material'
+ )
+
+ invisible = AutoAttr.o.game_settings.invisible()
+ use_backface_culling = AutoAttr.o.game_settings.use_backface_culling()
+ use_vertex_color_paint = Auto()
+ diffuse_color = Auto()
+ diffuse_intensity = Auto()
+ shader = Annotate(extract_material_shader)
+ textures = AutoAttr.o.texture_slots()
+ alpha_blend = AutoAttr.o.game_settings.alpha_blend()
+ use_transparency = Auto()
+ alpha = Auto()
+ cutout = AutoAttr.o.liminal_settings.cutout()
+ physics = AutoAttr.o.game_settings.physics()
+ elasticity = AutoAttr.o.physics.elasticity()
+ fh_damping = AutoAttr.o.physics.fh_damping()
+ fh_distance = AutoAttr.o.physics.fh_distance()
+ fh_force = AutoAttr.o.physics.fh_force()
+ friction = AutoAttr.o.physics.friction()
+ use_fh_normal = AutoAttr.o.physics.use_fh_normal()
+
+class MaterialTextureSlot(AutoClass):
+ _cls = bpy.types.MaterialTextureSlot
+ _constants = dict(
+ type = 'texture_slot'
+ )
+
+ texture = AutoPointer()
+ use_map_color_diffuse = Auto()
+ diffuse_color_factor = Auto()
+ use_map_alpha = Auto()
+ alpha_factor = Auto()
+
+class Texture(FromLib):
+ _cls = bpy.types.Texture
+ _group = 'textures'
+ _constants = dict(
+ type = 'texture'
+ )
+
+class ImageTexture(Texture):
+ _cls = bpy.types.ImageTexture
+ _constants = dict(
+ type = 'image_texture'
+ )
+
+ image = AutoPointer()
+ #use_alpha = Auto()
+ use_mipmap = Auto()
+ use_interpolation = Auto()
+
+class Image(FromLib):
+ _cls = bpy.types.Image
+ _group = 'images'
+ _constants = dict(
+ type = 'image'
+ )
+
+ filepath = Annotate(resolve_image_path)
+
+class Sensor(AutoClass):
+ #_cls = bpy.types.Sensor
+
+ # 'controllers',
+ name = Auto()
+ frequency = Auto()
+ invert = Auto()
+ use_level = Auto()
+ use_pulse_false_level = Auto()
+ use_pulse_true_level = Auto()
+ use_tap = Auto()
+ controllers = Auto(pack=items_to_names)
+
+class RaySensor(Sensor):
+ _cls = bpy.types.RaySensor
+ _constants = dict(
+ type = 'ray_sensor'
+ )
+
+ axis = Auto()
+ material = Auto()
+ property = Auto()
+ range = Auto()
+ ray_type = Auto()
+ use_x_ray = Auto()
+
+class PropertySensor(Sensor):
+ _cls = bpy.types.PropertySensor
+ _constants = dict(
+ type = 'property_sensor'
+ )
+
+ evaluation_type = Auto()
+ property = Auto()
+ value = Auto()
+ value_min = Auto()
+ value_max = Auto()
+
+
+class MessageSensor(Sensor):
+ _cls = bpy.types.MessageSensor
+ _constants = dict(
+ type = 'message_sensor'
+ )
+
+ subject = Auto()
+
+class TouchSensor(Sensor):
+ _cls = bpy.types.TouchSensor
+ _constants = dict(
+ type = 'touch_sensor'
+ )
+
+ material = AutoRef()
+
+class CollisionSensor(Sensor):
+ _cls = bpy.types.CollisionSensor
+ _constants = dict(
+ type = 'collision_sensor'
+ )
+
+ material = Auto()
+ property = Auto()
+ use_material = Auto()
+ use_pulse = Auto()
+
+class RadarSensor(Sensor):
+ _cls = bpy.types.RadarSensor
+ _constants = dict(
+ type = 'radar_sensor'
+ )
+
+ angle = Auto()
+ axis = Auto()
+ distance = Auto()
+ property = Auto()
+
+class DelaySensor(Sensor):
+ _cls = bpy.types.DelaySensor
+ _constants = dict(
+ type = 'delay_sensor'
+ )
+
+ delay = Auto()
+ duration = Auto()
+ use_repeat = Auto()
+
+class RandomSensor(Sensor):
+ _cls = bpy.types.RandomSensor
+ _constants = dict(
+ type = 'random_sensor'
+ )
+
+ seed = Auto()
+
+class AlwaysSensor(Sensor):
+ _cls = bpy.types.AlwaysSensor
+ _constants = dict(
+ type = 'always_sensor'
+ )
+
+class ActuatorSensor(Sensor):
+ _cls = bpy.types.ActuatorSensor
+ _constants = dict(
+ type = 'actuator_sensor'
+ )
+
+ actuator = Auto()
+
+class KeyboardSensor(Sensor):
+ _cls = bpy.types.KeyboardSensor
+ _constants = dict(
+ type = 'keyboard_sensor'
+ )
+
+ key = Auto()
+ log = Auto()
+ modifier_key_1 = Auto()
+ modifier_key_2 = Auto()
+ target = Auto()
+ use_all_keys = Auto()
+
+class JoystickSensor(Sensor):
+ _cls = bpy.types.JoystickSensor
+ _constants = dict(
+ type = 'joystick_sensor'
+ )
+
+ axis_direction = Auto()
+ axis_number = Auto()
+ axis_threshold = Auto()
+ button_number = Auto()
+ event_type = Auto()
+ hat_direction = Auto()
+ hat_number = Auto()
+ joystick_index = Auto()
+ single_axis_number = Auto()
+ use_all_events = Auto()
+
+class MouseSensor(Sensor):
+ _cls = bpy.types.MouseSensor
+ _constants = dict(
+ type = 'mouse_sensor'
+ )
+
+ mouse_event = Auto()
+
+class ArmatureSensor(Sensor):
+ _cls = bpy.types.ArmatureSensor
+ _constants = dict(
+ type = 'armature_sensor'
+ )
+
+ bone = Auto()
+ constraint = Auto()
+ test_type = Auto()
+ value = Auto()
+
+class NearSensor(Sensor):
+ _cls = bpy.types.NearSensor
+ _constants = dict(
+ type = 'near_sensor'
+ )
+
+ distance = Auto()
+ property = Auto()
+ reset_distance = Auto()
+
+class Actuator(AutoClass):
+ #_cls = bpy.types.Actuator
+
+ name = Auto()
+
+class ShapeActionActuator(Actuator):
+ _cls = bpy.types.ShapeActionActuator
+ _constants = dict(
+ type = 'shape_action_actuator'
+ )
+
+ #action =
+ frame_blend_in = Auto()
+ frame_end = Auto()
+ frame_property = Auto()
+ frame_start = Auto()
+ mode = Auto()
+ priority = Auto()
+ property = Auto()
+ use_continue_last_frame = Auto()
+
+class VisibilityActuator(Actuator):
+ _cls = bpy.types.VisibilityActuator
+ _constants = dict(
+ type = 'visibility_actuator'
+ )
+
+ apply_to_children = Auto()
+ use_occlusion = Auto()
+ use_visible = Auto()
+
+class MessageActuator(Actuator):
+ _cls = bpy.types.MessageActuator
+ _constants = dict(
+ type = 'message_actuator'
+ )
+
+ body_message = Auto()
+ body_property = Auto()
+ body_type = Auto()
+ subject = Auto()
+ to_property = Auto()
+
+class Filter2DActuator(Actuator):
+ _cls = bpy.types.Filter2DActuator
+ _constants = dict(
+ type = 'filter_2d_actuator'
+ )
+
+ filter_pass = Auto()
+ #glsl_shader = Auto()
+ mode = Auto()
+ motion_blur_factor = Auto()
+ use_motion_blur = Auto()
+
+class GameActuator(Actuator):
+ _cls = bpy.types.GameActuator
+ _constants = dict(
+ type = 'game_actuator'
+ )
+
+ filename = Auto()
+ mode = Auto()
+
+class RandomActuator(Actuator):
+ _cls = bpy.types.RandomActuator
+ _constants = dict(
+ type = 'random_actuator'
+ )
+
+ chance = Auto()
+ distribution = Auto()
+ float_max = Auto()
+ float_mean = Auto()
+ float_min = Auto()
+ float_value = Auto()
+ half_life_time = Auto()
+ int_max = Auto()
+ int_mean = Auto()
+ int_min = Auto()
+ int_value = Auto()
+ property = Auto()
+ seed = Auto()
+ standard_derivation = Auto()
+ use_always_true = Auto()
+
+class SteeringActuator(Actuator):
+ _cls = bpy.types.SteeringActuator
+ _constants = dict(
+ type = 'steering_actuator'
+ )
+
+ acceleration = Auto()
+ distance = Auto()
+ facing = Auto()
+ facing_axis = Auto()
+ mode = Auto()
+ #navmesh
+ normal_up = Auto()
+ self_terminated = Auto()
+ show_visualization = Auto()
+ target = Auto()
+ turn_speed = Auto()
+ update_period = Auto()
+ velocity = Auto()
+
+class ArmatureActuator(Actuator):
+ _cls = bpy.types.ArmatureActuator
+ _constants = dict(
+ type = 'armature_actuator'
+ )
+
+ bone = Auto()
+ constraint = Auto()
+ influence = Auto()
+ mode = Auto()
+ #secondary_target = Auto()
+ #target = Auto()
+ weight = Auto()
+
+class SoundActuator(Actuator):
+ _cls = bpy.types.SoundActuator
+ _constants = dict(
+ type = 'sound_actuator'
+ )
+
+ cone_inner_angle_3d = Auto()
+ cone_outer_angle_3d = Auto()
+ cone_outer_gain_3d = Auto()
+ distance_3d_max = Auto()
+ distance_3d_reference = Auto()
+ gain_3d_max = Auto()
+ gain_3d_min = Auto()
+ mode = Auto()
+ pitch = Auto()
+ rolloff_factor_3d = Auto()
+ #sound =
+ use_sound_3d = Auto()
+ volume = Auto()
+
+class ParentActuator(Actuator):
+ _cls = bpy.types.ParentActuator
+ _constants = dict(
+ type = 'parent_actuator'
+ )
+
+ mode = Auto()
+ #object =
+ use_compound = Auto()
+ use_ghost = Auto()
+
+class SceneActuator(Actuator):
+ _cls = bpy.types.SceneActuator
+ _constants = dict(
+ type = 'scene_actuator'
+ )
+
+ #camera = Auto()
+ mode = Auto()
+ #scene = Auto()
+
+class StateActuator(Actuator):
+ _cls = bpy.types.StateActuator
+ _constants = dict(
+ type = 'state_actuator'
+ )
+
+ operation = Auto()
+ states = Auto()
+
+class ActionActuator(Actuator):
+ _cls = bpy.types.ActionActuator
+ _constants = dict(
+ type = 'action_actuator'
+ )
+
+ #action =
+ apply_to_children = Auto()
+ frame_blend_in = Auto()
+ frame_end = Auto()
+ frame_property = Auto()
+ frame_start = Auto()
+ layer = Auto()
+ layer_weight = Auto()
+ play_mode = Auto()
+ priority = Auto()
+ property = Auto()
+ use_additive = Auto()
+ use_continue_last_frame = Auto()
+ use_force = Auto()
+ use_local = Auto()
+
+class CameraActuator(Actuator):
+ _cls = bpy.types.CameraActuator
+ _constants = dict(
+ type = 'camera_actuator'
+ )
+
+ axis = Auto()
+ damping = Auto()
+ height = Auto()
+ max = Auto()
+ min = Auto()
+ object = AutoRef()
+
+class ConstraintActuator(Actuator):
+ _cls = bpy.types.ConstraintActuator
+ _constants = dict(
+ type = 'constraint_actuator'
+ )
+
+ angle_max = Auto()
+ angle_min = Auto()
+ damping = Auto()
+ damping_rotation = Auto()
+ direction = Auto()
+ direction_axis = Auto()
+ direction_axis_pos = Auto()
+ distance = Auto()
+ fh_damping = Auto()
+ fh_force = Auto()
+ fh_height = Auto()
+ limit = Auto()
+ limit_max = Auto()
+ limit_min = Auto()
+ material = Auto()
+ mode = Auto()
+ property = Auto()
+ range = Auto()
+ rotation_max = Auto()
+ time = Auto()
+ use_fh_normal = Auto()
+ use_fh_paralel_axis = Auto()
+ use_force_distance = Auto()
+ use_local = Auto()
+ use_material_defect = Auto()
+ use_normal = Auto()
+ use_persistent = Auto()
+
+class PropertyActuator(Actuator):
+ _cls = bpy.types.PropertyActuator
+ _constants = dict(
+ type = 'property_actuator'
+ )
+
+ mode = Auto()
+ object = AutoRef()
+ object_property = Auto()
+ property = Auto()
+ value = Auto()
+
+class EditObjectActuator(Actuator):
+ _cls = bpy.types.EditObjectActuator
+ _constants = dict(
+ type = 'edit_object_actuator'
+ )
+
+ angular_velocity = Auto()
+ dynamic_operation = Auto()
+ linear_velocity = Auto()
+ mass = Auto()
+ mesh = AutoRef()
+ mode = Auto()
+ object = AutoRef()
+ time = Auto()
+ track_object = AutoRef()
+ use_3d_tracking = Auto()
+ use_local_angular_velocity = Auto()
+ use_local_linear_velocity = Auto()
+ use_replace_display_mesh = Auto()
+ use_replace_physics_mesh = Auto()
+
+class ObjectActuator(Actuator):
+ _cls = bpy.types.ObjectActuator
+ _constants = dict(
+ type = 'motion_actuator'
+ )
+
+ angular_velocity = Auto()
+ damping = Auto()
+ derivate_coefficient = Auto()
+ force = Auto()
+ force_min_x = Auto()
+ force_min_y = Auto()
+ force_min_z = Auto()
+ force_max_x = Auto()
+ force_max_y = Auto()
+ force_max_z = Auto()
+ integral_coefficient = Auto()
+ linear_velocity = Auto()
+ mode = Auto()
+ offset_location = Auto()
+ offset_rotation = Auto()
+ proportional_coefficient = Auto()
+ # reference_object =
+ torque = Auto()
+ use_add_linear_velocity = Auto()
+ use_local_angular_velocity = Auto()
+ use_local_force = Auto()
+ use_local_linear_velocity = Auto()
+ use_local_location = Auto()
+ use_local_rotation = Auto()
+ use_local_torque = Auto()
+ use_servo_limit_x = Auto()
+ use_servo_limit_y = Auto()
+ use_servo_limit_z = Auto()
+
+class Controller(AutoClass):
+ #_cls = bpy.types.Controller
+
+ name = Auto()
+ states = Auto()
+ use_priority = Auto()
+ actuators = Auto(pack=items_to_names)
+
+class ExpressionController(Controller):
+ _cls = bpy.types.ExpressionController
+ _constants = dict(
+ type = 'expression_controller'
+ )
+
+ expression = Auto()
+
+class XnorController(Controller):
+ _cls = bpy.types.XnorController
+ _constants = dict(
+ type = 'logic_xnor_controller'
+ )
+
+class AndController(Controller):
+ _cls = bpy.types.AndController
+ _constants = dict(
+ type = 'logic_and_controller'
+ )
+
+class NorController(Controller):
+ _cls = bpy.types.NorController
+ _constants = dict(
+ type = 'logic_nor_controller'
+ )
+
+class OrController(Controller):
+ _cls = bpy.types.OrController
+ _constants = dict(
+ type = 'logic_or_controller'
+ )
+
+class XorController(Controller):
+ _cls = bpy.types.XorController
+ _constants = dict(
+ type = 'logic_xor_controller'
+ )
+
+class NandController(Controller):
+ _cls = bpy.types.NandController
+ _constants = dict(
+ type = 'logic_nand_controller'
+ )
+
+class PythonController(Controller):
+ _cls = bpy.types.PythonController
+ _constants = dict(
+ type = 'python_controller'
+ )
+
+ mode = Auto()
+ module = Auto()
+ use_debug = Auto()
+
+class Curve(FromLib):
+ _cls = bpy.types.Curve
+ _group = 'curves'
+ _constants = dict(
+ type = 'curve'
+ )
+
+class SurfaceCurve(Curve):
+ _cls = bpy.types.SurfaceCurve
+ _constants = dict(
+ type = 'surface_curve'
+ )
+
+class TextBox(AutoClass):
+ _cls = bpy.types.TextBox
+ _resolve = lambda o: [o.x,o.y,o.width,o.height]
+
+class VectorFont(FromLib):
+ _cls = bpy.types.VectorFont
+ _group = 'fonts'
+ _constants = dict(
+ type = 'font'
+ )
+
+ filepath = Auto()
+
+class TextCharacterFormat(AutoClass):
+ _cls = bpy.types.TextCharacterFormat
+ _constants = dict(
+ type = 'text_character_format'
+ )
+ use_bold = Auto()
+ use_italic = Auto()
+ use_small_caps = Auto()
+ use_underline = Auto()
+
+class TextCurve(Curve):
+ _cls = bpy.types.TextCurve
+ _constants = dict(
+ type = 'text_curve'
+ )
+
+ active_textbox = Auto()
+ align = Auto()
+ body = Auto()
+ body_format = Auto()
+ edit_format = Auto()
+ family = Auto()
+ offset_x = Auto()
+ offset_y = Auto()
+ shear = Auto()
+ size = Auto()
+ small_caps_scale = Auto()
+ space_character = Auto()
+ space_line = Auto()
+ space_word = Auto()
+ underline_height = Auto()
+ underline_position = Auto()
+ text_boxes = Auto()
+ font = Auto()
+ font_bold = Auto()
+ font_bold_italic = Auto()
+ font_italic = Auto()
+
+################################################################################
+
+AutoClass._scan(locals())
+
+################################################################################
+
+class PropTracker(object):
+ def __init__(self, getfunc):
+ self.getfunc = getfunc
+ self.data = None
+
+ def changed_update(self):
+ if self.changed():
+ self.update()
+ return True
+ return False
+
+ def changed(self):
+ old_data = self.data
+ data = self.getfunc()
+ if old_data is data:
+ return False
+ return not (old_data == data)
+
+ def update(self):
+ self.data = self.getfunc()
+
+################################################################################
+
+class ArchiveClient(DataArchive):
+ def __init__(self):
+ DataArchive.__init__(self)
+
+ def poll(self):
+ pass
+
+ def close(self):
+ pass
+
+################################################################################
+
+class BlenderClient(object):
+ instance = None
+ MAX_TODO_TIME = 1.0/60.0
+
+ @classmethod
+ def start(cls):
+ if cls.instance:
+ return
+ client = BlenderClient()
+
+ @classmethod
+ def startstop(cls):
+ if not cls.instance:
+ cls.start()
+ else:
+ cls.instance.disconnect()
+
+ @classmethod
+ def update_post(cls):
+ client = cls.instance
+ if not client:
+ return
+ client.poll()
+
+ @classmethod
+ def export(cls, path, stamp = None):
+ if cls.instance:
+ cls.instance.disconnect()
+ t = time.time()
+ log.info("Serializing data for archive {}".format(path))
+ cls.start()
+ log.info("Data serialized in {:.3g}s.".format(time.time() - t))
+ assert not cls.instance._todo_list
+ cls.instance.endp.setitem('__stamp__/value', stamp)
+ cls.instance.endp.write_archive(path)
+ cls.instance.disconnect()
+
+ def __init__(self):
+ assert not BlenderClient.instance
+ BlenderClient.instance = self
+ self.ctx_scene = PropTracker(lambda: bpy.context.scene)
+ self.ctx_selected = PropTracker(lambda: list(bpy.context.selected_objects))
+ self.endp = ArchiveClient()
+ self._todo_list = {}
+ self.root_changed()
+
+ def disconnect(self):
+ BlenderClient.instance = None
+ self.endp.close()
+
+ def get_libroot(self, library):
+ return self.ns_root + '/libraries/' + get_libid(library)
+
+ def get_objroot(self, obj):
+ return self.get_libroot(obj.library)
+
+ def resolve_image_path(self, img):
+ return bpy.path.relpath(os.path.normpath(
+ bpy.path.abspath(img.filepath,
+ library = img.library)))
+
+ def root_changed(self):
+ filepath = os.path.normpath(os.path.abspath(bpy.data.filepath))
+
+ self.ns_root = self.get_root_ns()
+ self.endp.setitem(self.ns_root + '/filepath', filepath)
+
+ def add_ns(library):
+ libroot = self.get_libroot(library)
+ libpath = get_libpath(library)
+
+ self.endp.setitem(libroot + '/libpath', libpath)
+ self.endp.setitem(libroot + '/scenes', {})
+ self.endp.setitem(libroot + '/objects', {})
+ self.endp.setitem(libroot + '/cameras', {})
+ self.endp.setitem(libroot + '/meshes', {})
+ self.endp.setitem(libroot + '/materials', {})
+ self.endp.setitem(libroot + '/groups', {})
+
+ add_ns(None)
+ for lib in bpy.data.libraries:
+ add_ns(lib)
+
+ for scene in bpy.data.scenes:
+ log.debug("dumping scene {0}".format(scene))
+ reference(scene)
+
+ for group in bpy.data.groups:
+ log.debug("dumping group {0}".format(group))
+ reference(group)
+
+ self.handle_todo_list()
+
+ def get_todo_list_count(self):
+ return len(self._todo_list)
+
+ def commit(self, cls, uri, value):
+ log.debug('updating',uri)
+ cls._on_purify(value)
+ self.endp.setitem(uri, purify(value))
+
+ def handle_todo_list(self):
+ count = 0
+ timestamp = time.time()
+ while self._todo_list:
+ uri, (cls, value) = self._todo_list.popitem()
+ self.commit(cls, uri, value)
+ count += 1
+ if 0:
+ if (time.time() - timestamp) > self.MAX_TODO_TIME:
+ # need to do more next frame
+ break
+
+ def get_root_ns(self):
+ return ""
+
+ def check_changes(self):
+ update_ctx = False
+
+ if self.ctx_scene.changed_update():
+ scene_uri = reference(bpy.context.scene)
+ self.endp.setitem(self.ns_root + '/context/scene', scene_uri)
+
+ if self.ctx_selected.changed_update():
+ objects = reference_list(bpy.context.selected_objects)
+ self.endp.setitem(self.ns_root + '/context/selected_objects', objects)
+
+ for obj in bpy.context.selected_objects:
+ uri = reference(obj)
+ self.endp.setitem(
+ uri + '/matrix_world',
+ matrix_to_list(obj.matrix_world))
+
+ def poll(self):
+ self.handle_todo_list()
+ self.endp.poll()
+ self.check_changes()
+
+################################################################################
A => liminal_legacy/liminal/bootstrap.py +272 -0
@@ 0,0 1,272 @@
+from __future__ import (print_function, division, absolute_import)
+
+from glm import (
+ vec2,vec3,vec4,
+ ivec2,ivec3,ivec4,
+ mat2,mat3,mat4,
+ X_AXIS, Y_AXIS, Z_AXIS,
+ AABB)
+
+from .engine.actor import (
+ Actor)
+
+from .engine.audio import (
+ Sound,
+ Source,
+ Channel,
+ AudioManager)
+
+from .engine.archive import (
+ ArchiveManager,
+ resolve_path)
+
+from .engine.camera import (
+ ScreenCamera,
+ DebugCamera)
+
+from .engine.light import (
+ PointLight,
+ SpotLight,
+ DirectionalLight)
+
+from .engine.config import (
+ Config,
+ StereoMode)
+
+from .engine.core import (
+ FXAAMode,
+ RenderManager)
+
+from .engine.font import (
+ Font)
+
+from .engine.draw import (
+ LinePainter,
+ FontPainter,
+ PolygonPainter)
+
+from .engine.input import (
+ GamepadAxis,
+ GamepadButton,
+ GamepadHat,
+ InputState,
+ InputSet,
+ Mouse,
+ Binding,
+ BindingList)
+
+from .engine.interface import (
+ Named,
+ NamedSet,
+ RNode)
+
+from .engine.material import (
+ Material)
+
+from .engine.mesh import (
+ MeshFormats,
+ MeshBuffer,
+ VertexArray)
+
+from .engine.ppfx import (
+ PPFX)
+
+from .engine.deferred import (
+ GRenderManager,
+ GMaterial,
+ GMaterialSDF)
+
+from .engine.render import (
+ Viewport,
+ RProxy)
+
+from .engine.shader import (
+ Program)
+
+from .engine.system import (
+ System)
+
+from .engine.framebuffer import (
+ Framebuffer,
+ FramebufferTexture2D
+)
+
+from .engine.texture import (
+ Image,
+ BufferTexture1D,
+ ImageTexture1D,
+ ImageTexture2D,
+ ImageTexture2DArray,
+ ImageTexture3D,
+ ImageTextureCubeMap,
+ TextureWrap,
+ blank_texture,
+ zero_texture,
+ lut_default_texture,
+ CubeMapFormat)
+
+from .engine.timing import (
+ TimeSource,
+ Scheduler,
+ SchedulerThread,
+ TimeManager)
+
+from .engine.physics import (
+ PhysicsManager,
+ Space,
+ Body,
+ WorldSpace,
+ Box,
+ Sphere,
+ Plane,
+ Capsule,
+ Cylinder,
+ Ray,
+ Mass,
+ Density,
+ TriMeshData,
+ TriMesh,
+ ContactEvaluator,
+ BallJoint,
+ HingeJoint,
+ SliderJoint,
+ ContactJoint,
+ UniversalJoint,
+ Hinge2Joint,
+ PRJoint,
+ PUJoint,
+ PistonJoint,
+ FixedJoint,
+ AMotorJoint,
+ LMotorJoint,
+ Plane2DJoint,
+ Picker
+ )
+
+from .runtime import Runtime
+
+from .utils import (
+ linear_vec3,
+ linear_vec4,
+ mix,
+ clamp,
+ smoothstep,
+ srgb_decode,
+ srgb_encode
+ )
+
+from .spectre import Spectre
+
+from . import sigil
+from .sigil import DrawContext, DrawSurface
+
+__all__ = [
+ 'vec2','vec3','vec4',
+ 'ivec2','ivec3','ivec4',
+ 'mat2','mat3','mat4',
+ 'X_AXIS', 'Y_AXIS', 'Z_AXIS',
+ 'Sound',
+ 'Source',
+ 'Channel',
+ 'AudioManager',
+ 'Actor',
+ 'ArchiveManager',
+ 'resolve_path',
+ 'ScreenCamera',
+ 'DebugCamera',
+ 'PointLight',
+ 'SpotLight',
+ 'DirectionalLight',
+ 'Config',
+ 'StereoMode',
+ 'FXAAMode',
+ 'RenderManager',
+ 'Font',
+ 'LinePainter',
+ 'FontPainter',
+ 'PolygonPainter',
+ 'GamepadAxis',
+ 'GamepadButton',
+ 'GamepadHat',
+ 'InputState',
+ 'InputSet',
+ 'Mouse',
+ 'Binding',
+ 'BindingList',
+ 'Named',
+ 'NamedSet',
+ 'RNode',
+ 'RProxy',
+ 'Material',
+ 'AABB',
+ 'VertexArray',
+ 'MeshBuffer',
+ 'MeshFormats',
+ 'PPFX',
+ 'GRenderManager',
+ 'GMaterial',
+ 'GMaterialSDF',
+ 'Viewport',
+ 'Program',
+ 'System',
+ 'Image',
+ 'Framebuffer',
+ 'FramebufferTexture2D',
+ 'BufferTexture1D',
+ 'ImageTexture1D',
+ 'ImageTexture2D',
+ 'ImageTexture2DArray',
+ 'ImageTexture3D',
+ 'ImageTextureCubeMap',
+ 'TextureWrap',
+ 'blank_texture',
+ 'zero_texture',
+ 'lut_default_texture',
+ 'CubeMapFormat',
+ 'TimeSource',
+ 'Scheduler',
+ 'SchedulerThread',
+ 'TimeManager',
+ 'Runtime',
+ 'linear_vec3',
+ 'linear_vec4',
+ 'mix',
+ 'clamp',
+ 'smoothstep',
+ 'PhysicsManager',
+ 'WorldSpace',
+ 'Space',
+ 'Body',
+ 'Box',
+ 'Sphere',
+ 'Plane',
+ 'Capsule',
+ 'Cylinder',
+ 'Ray',
+ 'Mass',
+ 'Density',
+ 'TriMeshData',
+ 'TriMesh',
+ 'ContactEvaluator',
+ 'BallJoint',
+ 'HingeJoint',
+ 'SliderJoint',
+ 'ContactJoint',
+ 'UniversalJoint',
+ 'Hinge2Joint',
+ 'PRJoint',
+ 'PUJoint',
+ 'PistonJoint',
+ 'FixedJoint',
+ 'AMotorJoint',
+ 'LMotorJoint',
+ 'Plane2DJoint',
+ 'Picker',
+ 'srgb_decode',
+ 'srgb_encode',
+ 'Spectre',
+ 'sigil',
+ 'DrawContext',
+ 'DrawSurface'
+]
+
A => liminal_legacy/liminal/data/__init__.py +0 -0
A => liminal_legacy/liminal/data/builder.py +508 -0
@@ 0,0 1,508 @@
+"""
+The builder assembles engine objects from serialized server or archive data
+(effectively anything that implements the .source.DataSource interface.).
+
+Conversions are asynchroneous and may take up to a few seconds to complete.
+"""
+
+from __future__ import (print_function, division, absolute_import)
+
+import os
+import sys
+import logging
+import weakref
+import inspect
+import importlib
+from pprint import pprint
+
+from glm import (vec3, mat4, flatten) #@UnresolvedImport
+
+from ..extras.purify import AutoClass, Auto, Annotate, AutoAttr, assemble
+from ..engine.interface import NamedSet
+from ..engine.shader import CustomShader, BlenderShader
+from ..engine.mesh import GLMesh
+from ..engine.material import GLMaterial
+from ..engine.actor import Actor #@UnresolvedImport
+from ..engine.camera import Camera
+from ..engine.scene import Scene, SceneManager
+from ..engine.archive import ArchiveManager
+from .. import engine
+
+################################################################################
+
+log = logging.getLogger('liminal.builder')
+
+################################################################################
+
+def to_mat4(data):
+ return mat4(*flatten(data)).transpose()
+
+def uri_split(uri):
+ idx = uri.rindex('/')
+ return uri[:idx], uri[idx+1:]
+
+def uri_base(uri):
+ return uri[uri.rindex('/')+1:]
+
+def inject_material_shader(data):
+ shader = BlenderShader()
+ shader._name = data['name']
+ shader._shaderdef = data['shader']
+ return shader
+
+################################################################################
+
+class _BindMesh(AutoClass):
+ _cls = GLMesh
+ _group = 'meshes'
+ _constants = dict(
+ type = 'mesh'
+ )
+
+ name = Auto()
+ material_indices = Auto()
+ vertices = Auto()
+ normals = Auto()
+ faces = Auto()
+ vertex_colors = Auto()
+ texcoords = Auto()
+
+class _BindMaterial(AutoClass):
+ _cls = GLMaterial
+ _group = 'materials'
+ _constants = dict(
+ type = 'material'
+ )
+
+ name = Auto()
+ invisible = Auto()
+ use_backface_culling = Auto()
+ use_vertex_color_paint = Auto()
+ diffuse_color = Auto()
+ shader = Annotate(None, inject_material_shader)
+ alpha_blend = Auto()
+ use_transparency = Auto()
+ alpha = Auto()
+ cutout = Auto()
+
+class _BindScene(AutoClass):
+ _cls = Scene
+ _group = 'scenes'
+ _constants = dict(
+ type = 'scene',
+ )
+
+ scenepass = Auto()
+ name = Auto()
+ layers = Auto()
+
+ #fps = Auto()
+ #logic_step_max = Auto()
+ #obstacle_simulation = Auto()
+ #occlusion_culling_resolution = Auto()
+ physics_engine = Auto()
+ physics_gravity = Auto()
+ #physics_step_max = Auto()
+ #physics_step_sub = Auto()
+ #show_debug_properties = Auto()
+ #show_framerate_profile = Auto()
+ #use_activity_culling = Auto()
+ #use_occlusion_culling = Auto()
+
+def import_module_class(modulename, classname):
+ mod = importlib.import_module(modulename)
+ log.debug("retrieving class {0} from {1}".format(
+ classname, mod))
+ try:
+ class_ = getattr(mod, classname)
+ except AttributeError:
+ log.error("Module '{0}' has no attribute '{1}'".format(modulename, classname))
+ return
+
+ return class_
+
+class _BindObject(AutoClass):
+ _cls = Actor
+
+ @classmethod
+ def validate_module(cls, value):
+ return value
+
+ @classmethod
+ def _cls_factory(cls, props):
+ classpath = props.get('python_class',None)
+ if classpath:
+ classpath = cls.validate_module(classpath)
+ idx = classpath.rindex('.')
+ modulename,classname = classpath[:idx],classpath[idx+1:]
+ modulepath = modulename.replace('.','/')
+ modulepath = ArchiveManager().resolve_path('//' + modulepath + '.py') #@UndefinedVariable
+ log.debug("Initializing module {0}".format(modulepath))
+ assert os.path.isfile(modulepath), "file {0} does not exist".format(modulepath)
+ #basedir = os.path.dirname(modulepath)
+ #if not basedir in sys.path:
+ # sys.path.insert(0, basedir)
+ return import_module_class(modulename, classname)()
+ else:
+ return cls._cls()
+
+ _group = 'objects'
+ _constants = dict(
+ type = 'object',
+ )
+
+ name = Auto()
+ matrix_local = AutoAttr.o.transform(unpack=to_mat4)
+ game_properties = Auto()
+ hide_render = AutoAttr.o.visible(unpack=lambda v:not v)
+
+ physics_type = Auto()
+ use_collision_bounds = Auto()
+ collision_bounds_type = Auto()
+ mass = Auto()
+ radius = Auto()
+ damping = Auto()
+ rotation_damping = Auto()
+ use_actor = Auto()
+ use_ghost = Auto()
+
+ #python_class = Auto()
+
+ #collision_margin = AutoAttr.o.game.collision_margin()
+ #use_collision_compound = AutoAttr.o.game.use_collision_compound()
+ #use_actor = AutoAttr.o.game.use_actor()
+ #use_ghost = AutoAttr.o.game.use_ghost()
+
+
+class _BindLamp(_BindObject):
+ _group = 'lamps'
+ _constants = dict(
+ type = 'lamp',
+ )
+
+class _BindCamera(_BindObject):
+ _cls = Camera
+ _group = 'cameras'
+ _constants = dict(
+ type = 'camera'
+ )
+
+ angle_y = AutoAttr.o.lens()
+ clip_start = AutoAttr.o.near()
+ clip_end = AutoAttr.o.far()
+ sensor_width = Auto()
+ sensor_height = Auto()
+ ortho_scale = Auto()
+ #sensor_fit = Auto()
+ #shift_x = Auto()
+ #shift_y = Auto()
+ mode = AutoAttr.o.perspective(unpack=lambda k: k == 'PERSP')
+
+################################################################################
+
+AutoClass._scan(locals())
+
+################################################################################
+
+class Library(object):
+ def __init__(self, name, uri, path):
+ self.name = name
+ self.uri = uri
+ self.path = path
+ self.meshes = NamedSet()
+ self.materials = NamedSet()
+ self.actors = NamedSet()
+ self.scenes = NamedSet()
+
+################################################################################
+
+class SceneBuilder(object):
+ instance = None
+
+ def __init__(self, datasource):
+ self.src = datasource
+ SceneBuilder.instance = weakref.ref(self)
+ self.model = None
+ self.ns_libraries = '/libraries'
+ self.libs = dict()
+ self.groups = {}
+ self.meshes = {}
+ self.materials = {}
+ self.textures = {}
+
+ def lib(self, data=None):
+ libid = data and data.get('library',None)
+ return self.libs[libid]
+
+ def get_sessions(self):
+ keys = self.src.getkeys('/session')
+ result = {}
+ for entry in keys:
+ result[entry] = self.src.getitem('/session/' + entry + '/filepath')
+ return result
+
+ def init(self):
+ self.libs[None] = Library('<tmp>', None, None)
+
+ libnames = self.src.getkeys(self.ns_libraries)
+ for libname in libnames:
+ libkey = self.ns_libraries + '/' + libname
+ libpath = self.src.getitem(libkey + '/libpath')
+ libbasename = os.path.splitext(os.path.basename(libpath))[0]
+ self.libs[libname] = Library(libbasename, libkey, libpath)
+
+ ns_groups = libkey + '/groups'
+ for key in self.src.getkeys(ns_groups):
+ if key in self.groups:
+ log.warn('Duplicate group name: {}'.format(key))
+ continue
+ self.groups[key] = ns_groups + '/' + key
+
+ ns_meshes = libkey + '/meshes'
+ for key in self.src.getkeys(ns_meshes):
+ if key in self.meshes:
+ log.warn('Duplicate mesh name: {}'.format(key))
+ continue
+ self.meshes[key] = ns_meshes + '/' + key
+
+ ns_materials = libkey + '/materials'
+ for key in self.src.getkeys(ns_materials):
+ if key in self.materials:
+ log.warn('Duplicate material name: {}'.format(key))
+ continue
+ self.materials[key] = ns_materials + '/' + key
+
+ ns_textures = libkey + '/textures'
+ for key in self.src.getkeys(ns_textures):
+ if key in self.textures:
+ log.warn('Duplicate texture name: {}'.format(key))
+ continue
+ self.textures[key] = ns_textures + '/' + key
+
+ def add_scene(self, name):
+ for lib in self.libs.values():
+ if not lib.uri:
+ continue
+ ns_scenes = lib.uri + '/scenes'
+ scenekey = ns_scenes + '/' + name
+ if self.src.hasitem(scenekey):
+ scene = self.build_scene(self.src.getitem(scenekey))
+ SceneManager().add_scene(scene)
+ return scene
+
+ def build_scene_content(self, sc, scene):
+ if sc.get('background_set',None):
+ bgscene_uri = sc['background_set']
+ self.build_scene_content(self.src.getitem(bgscene_uri), scene)
+
+ child_uri_map = {}
+ dupli_groups = []
+
+ for ob_base in sc['object_bases']:
+ active = (ob_base['layers'] & scene.layers)
+ uri = ob_base['object']
+ obj, child_uris, dupli_group = self.build_object(uri)
+ if dupli_group:
+ dupli_groups.append((obj, dupli_group, active))
+ child_uri_map[uri] = obj, child_uris
+ if active:
+ scene.add_actor(obj)
+ else:
+ scene.add_inactive_actor(obj)
+
+ self.connect_children(child_uri_map)
+
+ self.build_dupli_groups(scene, dupli_groups)
+
+ def connect_children(self, child_uri_map):
+ top_uris = set(child_uri_map.keys())
+ # connect children to parents
+ for uri, (obj, child_uris) in child_uri_map.items():
+ for child_uri in child_uris:
+ top_uris.discard(child_uri)
+ child_obj = child_uri_map[child_uri][0]
+ obj.add_child(child_obj)
+ return top_uris
+
+ def build_dupli_group(self, parent, groupname, active = True):
+ """
+ parent: parent Actor to attach dupli group to
+ offset: vec3
+ uris: list of actors to replicate here
+ active: whether to add actors to active or inactive actors
+ """
+
+ group_uri = self.groups.get(groupname, None)
+ assert group_uri, "group {} not found in any library".format(groupname)
+
+ return self.build_dupli_groups(parent.scene, [
+ (parent, group_uri, active)
+ ])
+
+ def build_dupli_groups(self, scene, dupli_groups):
+ child_uri_map = {}
+
+ # add dupli groups
+ while dupli_groups:
+ parent_obj, group_uri, active = dupli_groups.pop(0)
+ group = self.src.getitem(group_uri)
+
+ ox,oy,oz = group['dupli_offset']
+
+ inactive_objects = set()
+
+ for uri in group['objects']:
+ obj, child_uris, dupli_group = self.build_object(uri, dupli = True)
+ if dupli_group:
+ dupli_groups.append((obj, dupli_group, active))
+ child_uri_map[uri] = obj, child_uris
+ obj.game_properties = tuple(obj.game_properties) + tuple(
+ parent_obj.game_properties)
+ if active:
+ scene.add_actor(obj)
+ else:
+ scene.add_inactive_actor(obj)
+ inactive_objects.add(uri)
+
+ top_uris = self.connect_children(child_uri_map)
+ for uri in top_uris:
+ obj = child_uri_map[uri][0]
+ mtx = obj.world_transform
+ mtx = mtx.translate_fff(-ox,-oy,-oz)
+ mtx = parent_obj.world_transform.mul_mat4(mtx)
+ obj._dupli_transform = parent_obj.world_transform.copy()
+ obj.world_transform = mtx
+ parent_obj._dupli_list.append(obj)
+
+ child_uri_map = {}
+
+ def build_scene(self, sc):
+ lib = self.lib(sc)
+ scene = lib.scenes.get(sc['name'], None)
+ if scene:
+ return scene
+ scene = assemble(sc)
+ lib.scenes.add(scene)
+
+ log.debug("Building scene {0}".format(scene._name))
+ self.build_scene_content(sc, scene)
+
+ cam_uri = sc['camera']
+ if cam_uri:
+ scene.active_camera = scene.cameras[uri_base(cam_uri)]
+ return scene
+
+ def get_object(self, uri):
+ ob = self.src.getitem(uri)
+ lib = self.lib(ob)
+ return lib.actors.get(ob['name'], None)
+
+ def build_object(self, uri, dupli = False):
+ ob = self.src.getitem(uri)
+ lib = self.lib(ob)
+ data_uri = ob['data']
+ if not (data_uri is None):
+ data = self.src.getitem(data_uri)
+ if data['type'] in ('lamp','camera'):
+ data.pop('name', None) # don't overwrite name attribute
+ ob.update(data)
+ else:
+ data = None
+ obj = assemble(ob)
+ if not dupli:
+ lib.actors.add(obj)
+ log.debug("Building object {0}".format(obj.name))
+
+ if data and data['type'] == 'mesh':
+ mesh = self.build_mesh(data, obj)
+ obj.add_mesh(mesh)
+
+ return obj, ob.get('children',[]), ob['dupli_group']
+
+ def build_lamp(self, data):
+ return Actor()
+
+ def build_camera(self, data):
+ return assemble(data)
+
+ def build_texture(self, uri):
+ if uri is None:
+ return
+ if not uri.startswith('/'):
+ tex_uri = self.textures.get(uri, None)
+ assert tex_uri, "texture {} not found in any library.".format(
+ uri)
+ uri = tex_uri
+
+ tex = self.src.getitem(uri)
+ img_uri = tex.get('image',None)
+ if not img_uri:
+ return None
+ img = self.src.getitem(img_uri)
+ image = engine.texture.Image()
+ image.filepath = img['filepath']
+ texture = engine.texture.GLImageTexture2D()
+ texture.image = image
+ return texture
+
+ def build_material(self, mat, obj = None):
+ if isinstance(mat, basestring):
+ mat_uri = self.materials.get(mat, None)
+ assert mat_uri, "material {} not found in any library.".format(
+ mat)
+
+ mat = self.src.getitem(mat_uri)
+
+ material = None
+ lib = self.lib(mat)
+ if mat:
+ log.debug("Building material {0}".format(mat['name']))
+ material = lib.materials.get(mat['name'], None)
+ if not material:
+ if mat:
+ material = assemble(mat)
+
+ lib.materials.add(material)
+ for i, texture_slot in enumerate(mat['textures']):
+ if texture_slot:
+ texture_uri = texture_slot['texture']
+ texture = self.build_texture(texture_uri)
+ material.set_texture(i, texture)
+ else:
+ material = GLMaterial()
+ matshader = BlenderShader()
+ material.shader = matshader
+
+ if obj and obj.has_shader_props():
+ log.debug("Assigning custom shader to {0}".format(material))
+ shader = CustomShader()
+ shader.set_var_source(obj)
+ material.shader = shader
+
+ return material
+
+ def build_mesh(self, data, obj = None):
+ if isinstance(data, basestring):
+ mesh_uri = self.meshes.get(data, None)
+ assert mesh_uri, "mesh {} not found in any library".format(data)
+ data = self.src.getitem(mesh_uri)
+
+ lib = self.lib(data)
+ mesh = lib.meshes.get(data['name'], None)
+ if mesh:
+ return mesh
+ mesh = assemble(data)
+ lib.meshes.add(mesh)
+
+ for i,mat_uri in enumerate(data['materials']):
+ if mat_uri:
+ mat = self.src.getitem(mat_uri)
+ else:
+ log.warn("mesh {0} has no material in slot {1}".format(mesh, i))
+ mat = None
+ material = self.build_material(mat, obj)
+ mesh.add_material(material)
+
+ return mesh
+
+################################################################################
A => liminal_legacy/liminal/data/export_blendc.py +37 -0
@@ 0,0 1,37 @@
+
+# should be run from within Blender
+
+import os
+import sys
+import bpy
+import argparse
+import addon_utils
+
+def run():
+ is_enabled, is_loaded = addon_utils.check('liminalclient')
+ if not is_loaded:
+ print("You need to install the liminal blenderclient first.")
+ raise SystemExit(255)
+ if not is_enabled:
+ bpy.ops.wm.addon_enable(module="liminalclient")
+
+ parser = argparse.ArgumentParser()
+ parser.add_argument('--liminalblob', dest='filepath', required = True,
+ help='The file to export the liminalblob to')
+ parser.add_argument('--stamp', dest='stamp',
+ help='The hash stamp to store in the file')
+
+ argv = sys.argv
+ try:
+ argv.remove('--')
+ except ValueError:
+ pass
+ args, rest = parser.parse_known_args(argv)
+
+ outfile = args.filepath
+ print("exporting to",outfile)
+ bpy.ops.export.liminalblob(filepath=outfile, stamp=args.stamp)
+ bpy.ops.wm.quit_blender()
+
+if __name__ == '__main__':
+ run()
A => liminal_legacy/liminal/data/mpc.py +374 -0
@@ 0,0 1,374 @@
+"""marshal based RPC server. loosely based off jpc."""
+
+import threading
+import logging
+import inspect
+import socket
+import sys
+
+try:
+ import thread
+except:
+ import _thread as thread
+
+ISPYPY = "__pypy__" in sys.modules
+ISPY3K = sys.version_info[0] >= 3
+
+if sys.platform == 'win32':
+ import pickle as marshal
+else:
+ import marshal
+
+if ISPY3K:
+ from io import BytesIO as StringIO
+else:
+ from StringIO import StringIO
+
+dumps = lambda v: marshal.dumps(v, 2)
+if ISPY3K:
+ def loads(s):
+ d = marshal.loads(s)
+ return dict([(key.decode('utf-8'),value) for key,value in d.items()])
+else:
+ loads = lambda s: marshal.loads(s)
+
+LOOPBACK = '127.0.0.1'
+DEFAULT_PORT = 52431
+BUFFER_SIZE = 4096
+
+MAX_THREADS = 128
+
+class ServerError(Exception):
+ """Wrap server errors by proxy."""
+
+
+
+class EofError(Exception):
+ """Socket end of file."""
+
+
+
+class BaseHandler(object):
+ """
+ Handle incomming requests.
+ Client can call public methods of derived classes.
+ """
+
+ def __init__(self, addr=None):
+ self._addr = addr
+ self._methods = {}
+
+
+ def _close(self):
+ self._methods = {}
+
+
+ def _get_method(self, name):
+ """
+ Get public method.
+ Verify attribute is public method and use cache for performance.
+ """
+
+ m = self._methods.get(name, None)
+ if m is not None:
+ return m
+
+ if name.startswith('_'):
+ logging.warning('Attempt to get non-public, attribute=%s.', name)
+ raise ValueError(name)
+
+ m = getattr(self, name)
+ if not inspect.ismethod(m):
+ logging.warning('Attempt to get non-method, attribute=%s.', name)
+ raise ValueError(name)
+
+ self._methods[name] = m
+
+ return m
+
+
+
+class EchoHandler(BaseHandler):
+ """Echo back call arguments for debugging."""
+
+ def echo(self, *args, **kwargs):
+ return {'*args': args, '**kwargs': kwargs}
+
+
+
+class ExampleHandler(BaseHandler):
+ """
+ Demonstrate handler inheritance.
+ Start server with: start_server(handler=ExampleHandler)
+ Client calls server with: Proxy(connection).add(...)
+ """
+
+ def add(self, x, y):
+ return x + y
+
+
+
+class Connection(object):
+ """Wrap socket with buffered read and length prefix for data."""
+
+ def __init__(self, conn):
+ self._conn = conn
+
+ def __getattr__(self, name):
+ """Delegate attributes of socket."""
+
+ return getattr(self._conn, name)
+
+
+ def close(self):
+ """Shut down and close socket."""
+
+ try:
+ self._conn.shutdown(socket.SHUT_RDWR)
+ except socket.error:
+ pass
+
+ self._conn.close()
+
+
+ def write(self, data):
+ """Write length prefixed data to socket."""
+
+ length = len(data)
+ l = '%08x' % length
+ if ISPY3K:
+ l = l.encode('utf-8')
+
+ self._conn.sendall(l + data)
+
+
+ def read(self):
+ """Read length prefixed data from socket."""
+
+ length = int(self._read(8), 16)
+ return self._read(length)
+
+
+ def _read(self, length):
+ buffer = StringIO()
+ readsize = 0
+ while readsize < length:
+ data = self._conn.recv(min(BUFFER_SIZE, length - readsize))
+ if not data:
+ raise EofError(readsize)
+ readsize += len(data)
+ buffer.write(data)
+ return buffer.getvalue()
+
+
+
+g_threads_semaphore = threading.Semaphore(MAX_THREADS)
+
+def threaded(foo):
+ """Run foo using bounded number of threads."""
+
+ def wrapper1(*args, **kwargs):
+ try:
+ foo(*args, **kwargs)
+ finally:
+ g_threads_semaphore.release()
+
+ def wrapper2(*args, **kwargs):
+ g_threads_semaphore.acquire()
+ thread.start_new_thread(wrapper1, args, kwargs)
+
+ return wrapper2
+
+
+
+def _serve_connection(conn, addr, handler_factory):
+ """
+ Serve acceptted connection.
+ Should be used in the context of a threaded server, see
+ threaded_connection(), or fork server (not implemented here).
+ """
+
+ logging.info('Enter, addr=%s.', addr)
+
+ c = Connection(conn)
+
+ try:
+ #
+ # Instantiate handler for the lifetime of the connection,
+ # making it possible to manage a state between calls.
+ #
+ handler = handler_factory(addr)
+
+ try:
+ while True:
+ data = c.read()
+
+ response = _dispatch(handler, data)
+ if response is None:
+ continue
+
+ c.write(response)
+
+ except EofError:
+ logging.debug('Caught end of file, error=%r.', sys.exc_info()[1])
+
+ finally:
+ c.close()
+ handler._close()
+
+
+
+threaded_connection = threaded(_serve_connection)
+
+def _dispatch(handler, data):
+ """
+ Dispatch call to handler.
+ Notifications arrive with no ID and get no response.
+ """
+
+ try:
+ id = None
+ work_item = loads(data)
+ id = work_item.get('id', None)
+
+ name = work_item['method']
+ foo = handler._get_method(name)
+
+ args = work_item['params']
+ kwargs = work_item.get('kwargs', {})
+
+ result = foo(*args, **kwargs)
+ if id is not None:
+ return dumps({'result': result, 'error': None, 'id': id})
+ except EofError:
+ # re-raise
+ raise
+ except Exception:
+ logging.warning('Caught exception raised by callable.', exc_info=True)
+ if id is not None:
+ return dumps({'result': None, 'error': repr(sys.exc_info()[1]), 'id': id})
+
+
+
+def start_server(host=LOOPBACK, port=DEFAULT_PORT, on_accept=threaded_connection, handler=EchoHandler, quit_check=lambda: False):
+ """Start server."""
+
+ logging.info('Enter, handler=%r, port=%d, host=%s.', handler, port, host)
+
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+
+ try:
+ s.bind((host, port))
+ s.listen(5)
+ s.settimeout(1)
+
+ while not quit_check():
+ try:
+ conn, addr = s.accept()
+ logging.info('Accepted connection from %s.', addr)
+ on_accept(conn, addr, handler)
+ except socket.timeout:
+ pass
+ logging.info("Shutting down...")
+ finally:
+ s.shutdown(socket.SHUT_RDWR)
+ s.close()
+
+
+
+def connect(host=LOOPBACK, port=DEFAULT_PORT, connection_type=Connection):
+ """Connect to server."""
+ if sys.platform != 'win32':
+ assert ISPYPY or ISPY3K, 'mpc requires pypy or python 3 to run correctly.'
+
+ logging.info('Enter, host=%s, port=%d.', host, port)
+
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ s.connect((host, port))
+
+ return connection_type(s)
+
+
+
+class Proxy(object):
+ """
+ Proxy methods of server handler.
+
+ Call Proxy(connection).foo(*args, **kwargs) to invoke method
+ handler.foo(*args, **kwargs) of server handler.
+ """
+
+ def __init__(self, conn):
+ self._conn = conn
+ self._id = 1
+
+
+ def _proxy(self, async, name, args, kwargs):
+ """
+ Call method on server.
+ Asynchhronous calls omit ID and do not wait for response.
+ """
+
+ d = {
+ 'method': name,
+ 'params': args,
+ }
+
+ if not async:
+ d['id'] = self._id
+ self._id += 1
+
+ if len(kwargs) > 0:
+ d['kwargs'] = kwargs
+
+ data = dumps(d)
+
+ self._conn.write(data)
+ if async:
+ return
+
+ response = self._conn.read()
+
+ r = loads(response)
+ if r.get('error', None) is not None:
+ logging.warning('Error returned by proxy, error=%s.', r['error'])
+ raise ServerError(r['error'])
+
+ if r['id'] != d['id']:
+ logging.error('Received unmatching id, sent=%s, received=%s.', d['id'], r['id'])
+ raise ValueError(r['id'])
+
+ return r['result']
+
+
+ def __getattr__(self, name):
+ """Return proxy version of method."""
+
+ def proxy(*args, **kwargs):
+ """Call method on server synchronously."""
+ return self._proxy(False, name, args, kwargs)
+
+ return proxy
+
+
+
+class Notifier(Proxy):
+ """
+ Proxy methods of server handler, asynchronously.
+ Call Notifier(connection).foo(*args, **kwargs) to invoke method
+ handler.foo(*args, **kwargs) of server handler.
+ """
+
+
+ def __getattr__(self, name):
+ """Return async proxy version of method."""
+
+ def proxy(*args, **kwargs):
+ """Call method on server asynchronously."""
+ return self._proxy(True, name, args, kwargs)
+
+ return proxy
+
+
+
A => liminal_legacy/liminal/data/source.py +326 -0
@@ 0,0 1,326 @@
+"""
+data sources offer filesystem-like access to an json-like object tree. The
+data source can map local resources such as archives and remote ones such as
+provided by the liminald data server.
+"""
+
+from __future__ import (print_function, division, absolute_import)
+
+import logging
+import time
+import os
+import tarfile
+import marshal
+import sys
+from io import BytesIO
+
+log = logging.getLogger('liminal.source')
+
+thisdir = os.path.dirname(os.path.abspath(__file__))
+
+################################################################################
+
+ARCHIVE_MAGICID = '1.6:'
+
+################################################################################
+
+class DataSource(object):
+ __slots__ = [
+ '__weakref__'
+ ]
+
+ def getkeys(self, path):
+ return []
+
+ def getitem(self, path):
+ return None
+
+ def setitem(self, path, value):
+ pass
+
+ def setitems(self, pairs):
+ return [self.setitem(path, value) for (path,value) in pairs]
+
+ def poll(self):
+ pass
+
+ def hasitem(self, path):
+ return False
+
+ def fspath(self, path):
+ return path.lstrip('//')
+
+ def add_search_path(self, path):
+ pass
+
+################################################################################
+
+class FileSource(DataSource):
+ __slots__ = [
+ '_searchpath',
+ ]
+
+ def __init__(self):
+ self._searchpath = []
+
+ @property
+ def search_path(self):
+ return self._searchpath
+
+
+ def fspath(self, path):
+ if not path.startswith('//'):
+ return path
+ cleanpath = path[2:]
+ for searchpath in self._searchpath:
+ fullpath = os.path.join(searchpath, cleanpath)
+ #log.debug("searching for {}".format(fullpath))
+ if os.path.isfile(fullpath):
+ return fullpath
+ if os.path.isdir(fullpath):
+ return fullpath
+ return path
+
+ def add_search_path(self, path):
+ self._searchpath.append(path)
+
+################################################################################
+
+class DataArchive(DataSource):
+ __slots__ = [
+ '_tree',
+ '_searchpath',
+ ]
+
+ def __init__(self, filepath = None):
+ self._tree = {}
+ self._searchpath = []
+ if filepath:
+ self.read_archive(filepath)
+
+ @property
+ def tree(self):
+ return self._tree
+
+ @property
+ def searchpath(self):
+ return self._searchpath
+
+ def write_archive(self, filepath):
+ root = self._tree
+ t = time.time()
+
+ #compression = 'gz'
+ compression = ''
+
+ log.info("Packing data to {}".format(filepath))
+ with tarfile.open(filepath, 'w:' + compression) as f:
+ def walk_dict(path, obj):
+ log.debug("Entering {}".format(path))
+ keys_added = 0
+ for key,value in obj.items():
+ subpath = path + '/' + key
+ if isinstance(value, dict):
+ walk_dict(subpath, value)
+ else:
+ keys_added += 1
+ data = marshal.dumps(value,2)
+ tinfo = tarfile.TarInfo(subpath.lstrip('/'))
+ tinfo.type = tarfile.REGTYPE
+ tinfo.size = len(data)
+ f.addfile(tinfo, BytesIO(data))
+ log.debug("Leaving {}: {} keys added".format(path, keys_added))
+ walk_dict('', root)
+
+ log.info("Packed in {:.3g}s.".format(time.time() - t))
+
+ @classmethod
+ def get_hash(cls, filepath):
+ if not os.path.isfile(filepath):
+ return None
+ with tarfile.open(filepath, 'r') as f:
+ tinfo = f.getmember('__stamp__/value')
+ value = marshal.loads(f.extractfile(tinfo).read())
+ return value
+
+ def read_archive(self, filepath):
+ with tarfile.open(filepath, 'r') as f:
+
+ for tinfo in f.getmembers():
+ root = self._tree
+ name = tinfo.name
+ path,key = self._splitpath(name)
+
+ for e in self._splitfullpath(path):
+ root = root.setdefault(e, {})
+
+ value = marshal.loads(f.extractfile(tinfo).read())
+ root[key] = value
+
+ def add_search_path(self, path):
+ self._searchpath.append(path)
+
+ def insert_search_path(self, index, path):
+ self._searchpath.insert(index, path)
+
+ def _splitpath(self, path):
+ try:
+ idx = path.rindex('/')
+ except ValueError:
+ return '',path
+ path,key = path[:idx],path[idx+1:]
+ assert not ('/' in key), "key contains /"
+ return path,key
+
+ def _splitfullpath(self, path):
+ path = path.lstrip('/')
+ for e in path.split('/'):
+ if not e:
+ continue
+ yield e
+
+ def _resolve(self, path):
+ root = self._tree
+ for e in self._splitfullpath(path):
+ root = root.get(e, None)
+ if not root:
+ return None
+ return root
+
+ def getkeys(self, path):
+ item = self._resolve(path)
+ if item and isinstance(item, dict):
+ return item.keys()
+ else:
+ return []
+
+ def hasitem(self, path):
+ path,key = self._splitpath(path)
+ root = self._resolve(path)
+ if root and isinstance(root, dict):
+ return key in root
+ else:
+ return False
+
+ def _getdefaultitem(self, path):
+ root = self._tree
+ pathlist = list(self._splitfullpath(path))
+ for name in pathlist:
+ # key accessor
+ root = root.setdefault(name,{})
+ return root, pathlist
+
+ def getitem(self, uri):
+ path,key = self._splitpath(uri)
+ root = self._resolve(path)
+ if root and isinstance(root, dict):
+ assert key in root, '{}: {} has no key {}'.format(uri, path, key)
+ return root[key]
+ else:
+ return None
+
+ def _setitem(self, path, value):
+ #log.debug('setitem("{path}",...)'.format(**locals()))
+ path,key = self._splitpath(path)
+ if key:
+ item,pathlist = self._getdefaultitem(path)
+ item[key] = value
+ pathlist.append(key)
+ else:
+ assert path in ('/',''),path
+ pathlist = ['','']
+ self._tree = value
+ return '/'.join(pathlist)
+
+ def setitem(self, path, value):
+ return self._setitem(path, value)
+
+ def fspath(self, path):
+ if not path.startswith('//'):
+ return path
+ cleanpath = path[2:]
+ for searchpath in self._searchpath:
+ fullpath = os.path.join(searchpath, cleanpath)
+ if os.path.isfile(fullpath):
+ return fullpath
+ if os.path.isdir(fullpath):
+ return fullpath
+ return path
+
+################################################################################
+
+def compile_blend(sourcepath, destpath):
+ import subprocess
+ import hashlib
+
+ def md5_for_file(f, block_size=2**20):
+ md5 = hashlib.md5()
+ while True:
+ data = f.read(block_size)
+ if not data:
+ break
+ md5.update(data)
+ return md5.hexdigest()
+
+ def find_blender_path():
+ bin_name = 'blender'
+ if sys.platform == 'win32':
+ bin_name = bin_name + '.exe'
+ # find pypy in path
+ PATH = os.environ['PATH'].split(os.pathsep)
+ for dirpath in PATH:
+ fullpath = os.path.join(dirpath, bin_name)
+ if os.path.isfile(fullpath):
+ return fullpath
+
+ sourcepath = os.path.abspath(sourcepath)
+ destpath = os.path.abspath(destpath)
+
+ assert os.path.isfile(sourcepath)
+ with open(sourcepath, 'rb') as f:
+ srchash = ARCHIVE_MAGICID + md5_for_file(f)
+
+ # aptana injects this, remove this for subprocess call
+ os.environ.pop('PYTHONPATH',None)
+
+ if os.path.isfile(destpath):
+ dsthash = DataArchive.get_hash(destpath)
+ if srchash == dsthash:
+ return
+ os.remove(destpath)
+
+ blenderpath = find_blender_path()
+ assert blenderpath, 'blender not found in PATH'
+ compilescript = os.path.join(thisdir, 'export_blendc.py')
+ assert os.path.isfile(compilescript)
+
+ args = [
+ blenderpath,
+ sourcepath,
+ '-P', compilescript,
+ '--',
+ '--stamp', srchash,
+ '--liminalblob', destpath,
+ ]
+ if 1:
+ retcode = subprocess.call(args)
+ assert retcode in (0,234)
+ else:
+ with open(os.devnull, 'w') as fp:
+ subprocess.check_call(args, stdout=fp, stderr=fp)
+
+def load_archive(filepath):
+ from ..engine.archive import resolve_path
+
+ sourcepath = resolve_path(filepath)
+
+ if os.path.isfile(sourcepath): # source file exists
+ cachepath = os.path.splitext(sourcepath)[0] + '.blendc'
+ compile_blend(sourcepath, cachepath)
+ else:
+ cachepath = resolve_path(os.path.splitext(filepath)[0] + '.blendc')
+ assert os.path.isfile(cachepath)
+ return DataArchive(cachepath)
+
+################################################################################
+
A => liminal_legacy/liminal/engine/__init__.py +26 -0
@@ 0,0 1,26 @@
+from __future__ import (print_function, division, absolute_import)
+
+import traceback
+import math
+import logging
+
+from . import core
+from . import system
+from . import camera
+from . import interface
+from . import logic
+from . import material
+from . import mesh
+from . import actor
+from . import render
+from . import shader
+from . import texture
+from . import input
+from . import font
+from . import audio
+from . import ppfx
+from . import archive
+from . import timing
+from ..utils import TODO
+
+################################################################################
A => liminal_legacy/liminal/engine/actor.py +169 -0
@@ 0,0 1,169 @@
+from __future__ import (print_function, division, absolute_import)
+
+import weakref
+import logging
+import traceback
+
+from glm import mat4, vec3, rotate, X_AXIS, Y_AXIS, Z_AXIS
+from glue import callproxy, callset
+
+from .render import ModelAttribs, RNode, RJCMD_DRAW
+from .core import RenderManager
+from .physics import Geom, Sphere
+
+################################################################################
+
+log = logging.getLogger('liminal.object')
+
+################################################################################
+
+class RActor(RNode):
+ def __init__(self, actor):
+ RNode.__init__(self)
+ self.actor = actor
+ self.name = actor.name
+
+ def process(self, batch, jobs):
+ RNode.process(self, batch, jobs)
+ for job in jobs:
+ if job.key.command != RJCMD_DRAW: continue
+ job.draw.ubo_model = self.actor.model_attribs
+
+################################################################################
+
+class Actor(RNode):
+ attributes = [
+ '_geom',
+ '_last_model_matrix',
+ '_model_attribs',
+ ]
+
+ def __init__(self, geom):
+ RNode.__init__(self)
+ assert isinstance(geom, Geom), geom
+ self._geom = geom
+ self._last_model_matrix = None
+ self._model_attribs = None
+
+ def process(self, batch, jobs):
+ RNode.process(self, batch, jobs)
+ self.invalidate_uniforms()
+ for job in jobs:
+ if job.key.command != RJCMD_DRAW: continue
+ job.draw.ubo_model = self.model_attribs
+
+ TRANSFORM_SCALE = vec3(1,1,1)
+
+ @property
+ def geom(self):
+ return self._geom
+ @property
+ def parent(self):
+ raise AttributeError, "no longer valid"
+
+ @property
+ def body(self):
+ return self._geom.body
+ @body.setter
+ def body(self, value):
+ if self._geom.body:
+ self._geom.body.moved_callback.discard(self.on_moved)
+ self._geom.body = value
+ if value is not None:
+ value.moved_callback.add(callproxy(self.on_moved))
+
+ @property
+ def world_transform(self):
+ raise AttributeError, "no longer valid"
+ @property
+ def world_position(self):
+ raise AttributeError, "no longer valid"
+ @property
+ def world_orientation(self):
+ raise AttributeError, "no longer valid"
+ @property
+ def world_scale(self):
+ raise AttributeError, "no longer valid"
+
+ @property
+ def scale(self):
+ return self._geom.scale
+ @scale.setter
+ def scale(self, value):
+ self._geom.scale = value
+ self.invalidate_uniforms()
+
+ @property
+ def position(self):
+ return self._geom.position
+ @position.setter
+ def position(self, value):
+ self._geom.position = value
+ self.invalidate_uniforms()
+
+ @property
+ def orientation(self):
+ return self._geom.orientation
+ @orientation.setter
+ def orientation(self, value):
+ self._geom.orientation = value
+ self.invalidate_uniforms()
+
+ def get_position_scale_orientation(self):
+ return self._geom.get_position_scale_orientation()
+
+ def set_position_scale_orientation(self, p, s, o):
+ self._geom.set_position_scale_orientation(p,s,o)
+ self.invalidate_uniforms()
+
+ @property
+ def transform(self):
+ return self._get_transform_matrix()
+ @transform.setter
+ def transform(self, value):
+ self._set_transform_matrix(value)
+
+ def _get_transform_matrix(self):
+ p,s,o = self.get_position_scale_orientation()
+ return mat4.compose(p,s,o)
+
+ def _set_transform_matrix(self, value):
+ p = value.get_translation()
+ s,o = value.get_scale_rotation()
+ self.set_position_scale_orientation(p,s,o)
+
+ def invalidate_uniforms(self):
+ RenderManager().invalidate_uniforms(self)
+
+ def on_moved(self):
+ self.invalidate_uniforms()
+
+ def write_model_attribs(self, attrib, data):
+ model_mtx = self.transform
+
+ last_model_matrix = self._last_model_matrix
+ if last_model_matrix is None:
+ last_model_matrix = model_mtx
+ self._last_model_matrix = model_mtx
+
+ data.mtx_model = model_mtx._ptr[0]
+ data.mtx_last_model = last_model_matrix._ptr[0]
+ data.mtx_inv_model = model_mtx.inverse()._ptr[0]
+
+ @property
+ def model_attribs(self):
+ attribs = self._model_attribs
+ if attribs is None:
+ attribs = ModelAttribs()
+ self._model_attribs = attribs
+ return attribs
+
+ def update_uniforms(self):
+ # non-renderable?
+ # if attribs is None, implement your own update_uniforms()
+ attribs = self.model_attribs
+ with attribs.map_write() as data:
+ self.write_model_attribs(attribs, data)
+
+################################################################################
+
No newline at end of file
A => liminal_legacy/liminal/engine/archive.py +43 -0
@@ 0,0 1,43 @@
+from __future__ import (print_function, division, absolute_import)
+
+import os
+import logging
+
+from glue import Singleton
+
+from ..data.source import FileSource
+from .config import Config
+
+################################################################################
+
+log = logging.getLogger('liminal.archive')
+THISDIR = os.path.abspath(os.path.dirname(__file__))
+
+################################################################################
+
+class ArchiveManager(FileSource):
+ __metaclass__ = Singleton
+
+ __slots__ = []
+
+ def __init__(self):
+ FileSource.__init__(self)
+ assert Config().base_path, 'Config not initialized yet'
+ self.add_search_path(Config().base_path)
+ self.add_search_path(os.path.abspath(
+ os.path.join(THISDIR, '..', 'assets')))
+
+ def resolve_path(self, path):
+ return self.fspath(path)
+
+ def load_text(self, name):
+ with self.open(name) as f:
+ return f.read()
+
+ def open(self, name, mode = 'r'):
+ return open(self.resolve_path(name), mode)
+
+################################################################################
+
+def resolve_path(path):
+ return ArchiveManager().resolve_path(path)
A => liminal_legacy/liminal/engine/audio.py +615 -0
@@ 0,0 1,615 @@
+from __future__ import (print_function, division, absolute_import)
+
+import os
+import logging
+import thread
+import time
+import weakref
+import wave
+
+from glue import Singleton, callproxy
+from glm import vec3
+
+from al import (alBuffer, AL_FORMAT_STEREO16, AL_FORMAT_MONO16, #@UnresolvedImport
+ alBufferData, alSource, alSourcef, alSource3f, alSourcei, AL_PITCH, #@UnresolvedImport
+ AL_GAIN, AL_POSITION, AL_VELOCITY, AL_LOOPING, AL_TRUE, AL_BUFFER,
+ alSourcePlay, AL_FALSE, alGetSourcef, alGetSourcei,
+ AL_PLAYING, AL_PAUSED, AL_STOPPED, AL_SOURCE_STATE, AL_INITIAL,
+ alSourceStop, AL_SEC_OFFSET, alcOpenDevice, alcCreateContext,
+ alcMakeContextCurrent, alcDestroyContext, alcCloseDevice,
+ alSourceQueueBuffers, alSourceUnqueueBuffers,
+ AL_DIRECTION, AL_ROLLOFF_FACTOR, AL_SOURCE_RELATIVE,
+ AL_BUFFERS_QUEUED, AL_BUFFERS_PROCESSED, ALC_FREQUENCY, ALC_MONO_SOURCES,
+ ALC_STEREO_SOURCES, alListener3f, AL_ORIENTATION, alListenerfv,
+ alGetBufferi)
+
+from stbvorbis import Vorbis
+
+from .interface import Named
+from .archive import resolve_path
+from al._al import AL_FREQUENCY, AL_BITS, AL_CHANNELS, AL_SIZE, AL_MIN_GAIN,\
+ AL_MAX_GAIN, AL_MAX_DISTANCE, AL_CONE_OUTER_GAIN, AL_CONE_INNER_ANGLE,\
+ AL_CONE_OUTER_ANGLE, AL_REFERENCE_DISTANCE, AL_VELOCITY, AL_DIRECTION,\
+ AL_NONE, AL_SOURCE_TYPE, AL_UNDETERMINED, AL_STATIC, AL_STREAMING,\
+ alSourcePause, alSourceRewind, AL_BYTE_OFFSET, AL_EXPONENT_DISTANCE_CLAMPED,\
+ AL_EXPONENT_DISTANCE, AL_LINEAR_DISTANCE_CLAMPED, AL_LINEAR_DISTANCE,\
+ AL_INVERSE_DISTANCE_CLAMPED, AL_INVERSE_DISTANCE, alGetInteger,\
+ AL_DISTANCE_MODEL, alDistanceModel, alDopplerFactor, alGetFloat,\
+ AL_DOPPLER_FACTOR, AL_SPEED_OF_SOUND, alSpeedOfSound
+from al.al import alGetSourcefv
+from .timing import TimeManager
+
+################################################################################
+
+log = logging.getLogger('liminal.audio')
+
+################################################################################
+
+class AudioError(Exception):
+ pass
+
+class SoundFormat:
+ Mono16 = AL_FORMAT_MONO16
+ Stereo16 = AL_FORMAT_STEREO16
+
+class SourceType:
+ Undetermined = AL_UNDETERMINED
+ Static = AL_STATIC
+ Streaming = AL_STREAMING
+
+class SourceState:
+ Initial = AL_INITIAL
+ Playing = AL_PLAYING
+ Paused = AL_PAUSED
+ Stopped = AL_STOPPED
+
+class DistanceModel:
+ Disabled = AL_NONE
+ InverseDistance = AL_INVERSE_DISTANCE
+ InverseDistanceClamped = AL_INVERSE_DISTANCE_CLAMPED
+ LinearDistance = AL_LINEAR_DISTANCE
+ LinearDistanceClamped = AL_LINEAR_DISTANCE_CLAMPED
+ ExponentDistance = AL_EXPONENT_DISTANCE
+ ExponentDistanceClamped = AL_EXPONENT_DISTANCE_CLAMPED
+ Default = InverseDistanceClamped # D3D model
+
+###############################################################################
+
+class Source(Named, alSource):
+ __slots__ = [
+ '_sound',
+ ]
+
+ _users = weakref.WeakValueDictionary()
+
+ def __init__(self):
+ Named.__init__(self)
+ alSource.__init__(self)
+ self._sound = None
+ self._users[int(self)] = self
+
+ def update_from_actor(self, actor):
+ self.position = actor.world_position
+ self.direction = actor.world_orientation.col1()
+ velocity = actor.body.linear_velocity
+ if velocity is None:
+ velocity = vec3(0,0,0)
+ self.velocity = velocity
+
+ @property
+ def pitch(self):
+ return alGetSourcef(self, AL_PITCH)
+ @pitch.setter
+ def pitch(self, value):
+ alSourcef(self, AL_PITCH, value)
+
+ @property
+ def gain(self):
+ return alGetSourcef(self, AL_GAIN)
+ @gain.setter
+ def gain(self, value):
+ alSourcef(self, AL_GAIN, value)
+
+ @property
+ def min_gain(self):
+ return alGetSourcef(self, AL_MIN_GAIN)
+ @min_gain.setter
+ def min_gain(self, value):
+ alSourcef(self, AL_MIN_GAIN, value)
+
+ @property
+ def max_gain(self):
+ return alGetSourcef(self, AL_MAX_GAIN)
+ @max_gain.setter
+ def max_gain(self, value):
+ alSourcef(self, AL_MAX_GAIN, value)
+
+ @property
+ def max_distance(self):
+ return alGetSourcef(self, AL_MAX_DISTANCE)
+ @max_distance.setter
+ def max_distance(self, value):
+ alSourcef(self, AL_MAX_DISTANCE, value)
+
+ @property
+ def rolloff_factor(self):
+ return alGetSourcef(self, AL_ROLLOFF_FACTOR)
+ @rolloff_factor.setter
+ def rolloff_factor(self, value):
+ alSourcef(self, AL_ROLLOFF_FACTOR, value)
+
+ @property
+ def cone_outer_gain(self):
+ return alGetSourcef(self, AL_CONE_OUTER_GAIN)
+ @cone_outer_gain.setter
+ def cone_outer_gain(self, value):
+ alSourcef(self, AL_CONE_OUTER_GAIN, value)
+
+ @property
+ def cone_inner_angle(self):
+ return alGetSourcef(self, AL_CONE_INNER_ANGLE)
+ @cone_inner_angle.setter
+ def cone_inner_angle(self, value):
+ alSourcef(self, AL_CONE_INNER_ANGLE, value)
+
+ @property
+ def cone_outer_angle(self):
+ return alGetSourcef(self, AL_CONE_OUTER_ANGLE)
+ @cone_outer_angle.setter
+ def cone_outer_angle(self, value):
+ alSourcef(self, AL_CONE_OUTER_ANGLE, value)
+
+ @property
+ def reference_distance(self):
+ return alGetSourcef(self, AL_REFERENCE_DISTANCE)
+ @reference_distance.setter
+ def reference_distance(self, value):
+ alSourcef(self, AL_REFERENCE_DISTANCE, value)
+
+ @property
+ def position(self):
+ return vec3(*alGetSourcefv(self, AL_POSITION, 3))
+ @position.setter
+ def position(self, value):
+ alSource3f(self, AL_POSITION, *value.to_tuple())
+
+ @property
+ def velocity(self):
+ return vec3(*alGetSourcefv(self, AL_VELOCITY, 3))
+ @velocity.setter
+ def velocity(self, value):
+ alSource3f(self, AL_VELOCITY, *value.to_tuple())
+
+ @property
+ def direction(self):
+ return vec3(*alGetSourcefv(self, AL_DIRECTION, 3))
+ @direction.setter
+ def direction(self, value):
+ alSource3f(self, AL_DIRECTION, *value.to_tuple())
+
+ @property
+ def relative(self):
+ return alGetSourcei(self, AL_SOURCE_RELATIVE)
+ @relative.setter
+ def relative(self, value):
+ alSourcei(self, AL_SOURCE_RELATIVE, value)
+
+ @property
+ def sound(self):
+ return self._sound
+ @sound.setter
+ def sound(self, value):
+ self._sound = value
+ self.buffer = value
+
+ @property
+ def buffer(self):
+ ptr = alGetSourcei(self, AL_BUFFER)
+ if ptr is AL_NONE:
+ return None
+ return Sound.resolve(ptr)
+ @buffer.setter
+ def buffer(self, value):
+ if value is None:
+ value = AL_NONE
+ assert isinstance(value, alBuffer)
+ alSourcei(self, AL_BUFFER, value)
+
+ @property
+ def state(self):
+ return alGetSourcei(self, AL_SOURCE_STATE)
+ @state.setter
+ def state(self, value):
+ alSourcei(self, AL_SOURCE_STATE, value)
+
+ @property
+ def type(self):
+ return alGetSourcei(self, AL_SOURCE_TYPE)
+ @type.setter
+ def type(self, value):
+ alSourcei(self, AL_SOURCE_TYPE, value)
+
+ @property
+ def buffers_queued(self):
+ return alGetSourcei(self, AL_BUFFERS_QUEUED)
+
+ @property
+ def buffers_processed(self):
+ return alGetSourcei(self, AL_BUFFERS_PROCESSED)
+
+ @property
+ def byte_offset(self):
+ return alGetSourcei(self, AL_BYTE_OFFSET)
+
+ @property
+ def sec_offset(self):
+ return alGetSourcef(self, AL_SEC_OFFSET)
+ @sec_offset.setter
+ def sec_offset(self, value):
+ alSourcef(self, AL_SEC_OFFSET, value)
+
+ @property
+ def looping(self):
+ return alGetSourcei(self, AL_LOOPING)
+ @looping.setter
+ def looping(self, value):
+ alSourcei(self, AL_LOOPING, value)
+
+ def play(self):
+ alSourcePlay(self)
+
+ def pause(self):
+ alSourcePause(self)
+
+ def stop(self):
+ alSourceStop(self)
+
+ def rewind(self):
+ alSourceRewind(self)
+
+################################################################################
+
+class Sound(Named, alBuffer):
+ __slots__ = [
+ ]
+
+ _users = weakref.WeakValueDictionary()
+
+ def __init__(self):
+ Named.__init__(self)
+ alBuffer.__init__(self)
+ self._users[int(self)] = self
+
+ @classmethod
+ def resolve(cls, ptr):
+ return cls._users.get(ptr, None)
+
+ def Data(self, audio_format, samples, sample_rate):
+ alBufferData(self, audio_format, samples, sample_rate)
+
+ @property
+ def frequency(self):
+ return alGetBufferi(self, AL_FREQUENCY)
+
+ @property
+ def bits(self):
+ return alGetBufferi(self, AL_BITS)
+
+ @property
+ def channels(self):
+ return alGetBufferi(self, AL_CHANNELS)
+
+ @property
+ def size(self): # in bytes
+ return alGetBufferi(self, AL_SIZE)
+
+ @property
+ def count(self): # in samples
+ return self.size * 8 // (self.channels * self.bits)
+
+ @property
+ def duration(self): # in seconds
+ return self.count / self.frequency
+
+ @classmethod
+ def format_from_channels(cls, channels):
+ if channels == 1:
+ return SoundFormat.Mono16
+ elif channels == 2:
+ return SoundFormat.Stereo16
+ else:
+ raise ValueError, \
+ 'number of channels ({}) not supported'.format(channels)
+
+ @classmethod
+ def from_buffer16(cls, channels, data, sample_rate = 44100):
+ sound = cls()
+ sound.Data(cls.format_from_channels(channels), data, sample_rate)
+ return sound
+
+ @classmethod
+ def from_path(cls, path):
+ path = resolve_path(path)
+ log.debug("Loading sound from {0}".format(path))
+ assert os.path.isfile(path), "Soundfile {0} does not exist.".format(path)
+
+ ext = os.path.splitext(path)[1].lower()
+ if ext == '.ogg':
+ vbuffer = Vorbis.open_filename(path)
+ info = vbuffer.get_info()
+ assert info.channels > 0 and info.channels <= 2
+ samples = vbuffer.get_samples_short_interleaved(info.channels,
+ vbuffer.stream_length_in_samples())
+ channels = info.channels
+ sample_rate = info.sample_rate
+ elif ext == '.wav':
+ # load wave file into buffer
+ f = wave.open(path, 'rb')
+ width = f.getsampwidth()
+ assert width == 2, '16-bit sample expected.'
+ assert f.getcomptype() == 'NONE', 'compression not supported.'
+ channels = f.getnchannels()
+ sample_rate = f.getframerate()
+ sample_count = f.getnframes()
+ samples = f.readframes(sample_count)
+ assert len(samples) == sample_count*width*channels
+ assert len(samples)%2 == 0
+ f.close()
+ else:
+ raise AudioError, \
+ 'Unsupported file format.'
+ sound = cls.from_buffer16(channels, samples, sample_rate)
+ sound.name = os.path.basename(path)
+ return sound
+
+################################################################################
+
+class Listener(Named):
+ __slots__ = [
+ ]
+
+ def __init__(self):
+ Named.__init__(self)
+
+ def set(self):
+ AudioManager().listener = self
+
+ def update_from_values(self, position, orientation, velocity):
+ if AudioManager().listener is not self:
+ return
+ alListener3f(AL_POSITION, *position.to_tuple())
+ at = orientation.col1().mul_f(-1).to_tuple()
+ up = orientation.col2().mul_f(-1).to_tuple()
+ alListenerfv(AL_ORIENTATION, at + up)
+ alListener3f(AL_VELOCITY, *velocity.to_tuple())
+
+################################################################################
+
+class Channel(Named):
+ __slots__ = [
+ '_sources',
+ '_next_source_idx',
+ ]
+
+ def __init__(self, voices = 1):
+ Named.__init__(self)
+ self._sources = [Source() for i in range(voices)]
+ self._next_source_idx = 0
+ AudioManager().channels.add(self)
+
+ def _get_next_source(self):
+ for source in self._sources:
+ if source.state != SourceState.Playing:
+ return source
+ idx = self._next_source_idx
+ self._next_source_idx = (idx+1) % len(self._sources)
+ return self._sources[idx]
+
+ def stop_all(self):
+ for source in self._sources:
+ source.stop()
+
+ def play_at(self, actor, sound):
+ source = self._get_next_source()
+ source.stop()
+ source.sound = sound
+ source.update_from_actor(actor)
+ source.play()
+
+################################################################################
+
+class AudioManager(object):
+ __metaclass__ = Singleton
+
+ __slots__ = [
+ '__weakref__',
+ '_aldevice',
+ '_alcontext',
+ '_last_position',
+ '_listener',
+ '_channels',
+ ]
+
+ def __init__(self):
+ self._aldevice = None
+ self._alcontext = None
+ self._listener = None
+ self._channels = weakref.WeakSet()
+
+ @property
+ def channels(self):
+ return self._channels
+
+ def on_frame(self):
+ if self._alcontext is None:
+ return
+ self.update_listener()
+
+ def update_listener(self):
+ if self._alcontext is None:
+ return
+ listener = self.listener
+ if listener is None:
+ # default setup
+ alListener3f(AL_POSITION, 0, 0, 0)
+ alListener3f(AL_VELOCITY, 0, 0, 0)
+ alListenerfv(AL_ORIENTATION, [0, 0, -1, 0, 1, 0])
+ return
+
+ position = listener.world_position
+ orientation = listener.world_orientation
+ velocity = listener.body.linear_velocity
+ if velocity is None:
+ last_position = self._last_position
+ if last_position is not None:
+ tm = TimeManager()
+ velocity = position.sub(last_position).mul_f(tm.avg_framerate)
+ else:
+ velocity = vec3(0,0,0)
+ self._last_position = position
+
+ alListener3f(AL_POSITION, *position.to_tuple())
+ at = orientation.col1().mul_f(-1).to_tuple()
+ up = orientation.col2().mul_f(-1).to_tuple()
+ alListenerfv(AL_ORIENTATION, at + up)
+ alListener3f(AL_VELOCITY, *velocity.to_tuple())
+
+
+ @property
+ def listener(self):
+ return self._listener and self._listener()
+ @listener.setter
+ def listener(self, value):
+ self._listener = weakref.ref(value)
+ self._last_position = None
+ #self.update_listener()
+
+ @property
+ def distance_model(self):
+ return alGetInteger(AL_DISTANCE_MODEL)
+ @distance_model.setter
+ def distance_model(self, value):
+ alDistanceModel(value)
+
+ @property
+ def doppler_factor(self):
+ return alGetFloat(AL_DOPPLER_FACTOR)
+ @doppler_factor.setter
+ def doppler_factor(self, value):
+ # default is 1
+ alDopplerFactor(value)
+
+ @property
+ def speed_of_sound(self):
+ return alGetFloat(AL_SPEED_OF_SOUND)
+ @speed_of_sound.setter
+ def speed_of_sound(self, value):
+ # default is 343.3
+ alSpeedOfSound(value)
+
+ def destroy(self):
+ if not (self._alcontext is None):
+ log.info("Destroying AL context...")
+ alcDestroyContext(self._alcontext)
+ if not (self._aldevice is None):
+ log.info("Closing AL device...")
+ alcCloseDevice(self._aldevice)
+
+ def init(self):
+ if self._aldevice:
+ return
+ log.info("Opening AL device...")
+ self._aldevice = alcOpenDevice(None)
+ assert self._alcontext is None
+ log.info("Creating AL context...")
+ self._alcontext = alcCreateContext(self._aldevice, [
+ ALC_FREQUENCY, 44100,
+ #ALC_REFRESH, 60,
+ #ALC_SYNC, 1,
+ ALC_MONO_SOURCES, 16,
+ ALC_STEREO_SOURCES, 8,
+ ])
+ result = alcMakeContextCurrent(self._alcontext)
+ assert result == AL_TRUE
+ from .system import System
+ System.event_frame.add(self.on_frame)
+
+################################################################################
+
+class Stream(object):
+ __slots__ = [
+ '_source',
+ '_buffers'
+ ]
+
+ def __init__(self, buffercount):
+ self._buffers = [alBuffer() for i in range(buffercount)]
+
+ source = alSource()
+ alSource3f(source, AL_POSITION, 0, 0, 0)
+ alSource3f(source, AL_VELOCITY, 0, 0, 0)
+ alSource3f(source, AL_DIRECTION, 0, 0, 0)
+ alSourcef(source, AL_ROLLOFF_FACTOR, 0)
+ alSourcei(source, AL_SOURCE_RELATIVE, AL_TRUE)
+ self._source = source
+ self._thread = None
+ self._exit_thread = False
+
+ def thread_update(self):
+ while not self._exit_thread:
+ time.sleep(0.05)
+ self.update()
+
+ def start_thread(self):
+ if self._thread:
+ return
+ self._thread = thread.start_new_thread(self.thread_update, ())
+ from .system import System
+ System().event_destroy.add(callproxy(self.stop_thread))
+
+ def stop_thread(self):
+ if self._thread is None:
+ return
+ self._exit_thread = True
+ self._thread = None
+ time.sleep(0.2)
+
+ def play(self):
+ for buffer in self._buffers:
+ self.stream(buffer)
+ alSourceQueueBuffers(self._source, self._buffers)
+ alSourcePlay(self._source)
+
+ def stop(self):
+ alSourceStop(self._source)
+ self.empty()
+
+ def empty(self):
+ queued = alGetSourcei(self._source, AL_BUFFERS_QUEUED)
+ for i in range(queued):
+ alSourceUnqueueBuffers(self._source, 1)
+
+ def update(self):
+ if (alGetSourcei(self._source, AL_SOURCE_STATE) == AL_STOPPED):
+ log.warn("Buffer underrun, restarting Stream")
+ self.empty()
+ self.play()
+ return True
+
+ active = True
+ processed = alGetSourcei(self._source, AL_BUFFERS_PROCESSED)
+ for i in range(processed):
+ buffer = alSourceUnqueueBuffers(self._source, 1)[0]
+ active = self.stream(buffer)
+ alSourceQueueBuffers(self._source, [buffer])
+ return active
+
+ def stream(self, buffer):
+ data = self.generate()
+ alBufferData(buffer, AL_FORMAT_STEREO16, data, 44100)
+ return False
+
+ def generate(self):
+ raise NotImplementedError("implement this function")
+
+################################################################################
A => liminal_legacy/liminal/engine/basebuffer.py +96 -0
@@ 0,0 1,96 @@
+
+from gl import (glBuffer, glBindBuffer, GL_DYNAMIC_DRAW, #@UnresolvedImport
+ GL_MAP_INVALIDATE_BUFFER_BIT,
+ GL_MAP_WRITE_BIT, GL_MAP_UNSYNCHRONIZED_BIT, glUnmapBuffer,
+ glMapBufferRange, glBufferData, GL_MAP_READ_BIT, GL_TRUE,
+ glBufferSubData)
+
+from .interface import Named
+
+################################################################################
+
+class Buffer(glBuffer, Named):
+ __slots__ = [
+ '_size',
+ ]
+
+ class MapBuffer(object):
+ def __init__(self, map_buffer, data):
+ self.map_buffer = map_buffer
+ self.data = data
+
+ def __enter__(self):
+ return self.data
+
+ def __exit__(self, type_, value, tb):
+ self.map_buffer.unmap()
+
+ TARGET = None
+ USAGE = None
+
+ def __init__(self):
+ Named.__init__(self)
+ glBuffer.__init__(self)
+ self._size = 0
+
+ @property
+ def size(self):
+ return self._size
+ @size.setter
+ def size(self, value):
+ if value is self._size:
+ return
+ self._size = value
+ self.reallocate()
+
+ @classmethod
+ def from_size(cls, size):
+ mesh = cls()
+ mesh.size = size
+ return mesh
+
+ def bind(self):
+ glBindBuffer(self.TARGET, self)
+
+ @classmethod
+ def unbind(cls):
+ glBindBuffer(cls.TARGET, 0)
+
+ def map(self, offset, length, flags):
+ assert offset+length <= self._size
+ self.bind()
+ data = glMapBufferRange(self.TARGET, offset, length, flags)
+ return self.MapBuffer(self, data)
+
+ def map_read(self):
+ self.bind()
+ flags = GL_MAP_READ_BIT #| GL_MAP_UNSYNCHRONIZED_BIT
+ return self.map(0, self._size, flags)
+
+ def map_write(self):
+ self.bind()
+ flags = GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT \
+ | GL_MAP_INVALIDATE_BUFFER_BIT
+ return self.map(0, self._size, flags)
+
+ def unmap(self):
+ self.bind()
+ result = glUnmapBuffer(self.TARGET)
+ assert result == GL_TRUE
+ self.unbind()
+
+ @classmethod
+ def Data(cls, size):
+ glBufferData(cls.TARGET, size, None, cls.USAGE)
+
+ @classmethod
+ def SubData(cls, size, offset, ptr):
+ glBufferSubData(cls.TARGET, size, offset, ptr)
+
+ def reallocate(self):
+ assert self._size > 0
+ self.bind()
+ self.Data(self._size)
+ self.unbind()
+
+################################################################################
A => liminal_legacy/liminal/engine/camera.py +578 -0
@@ 0,0 1,578 @@
+from __future__ import (print_function, division, absolute_import)
+
+import math
+import logging
+
+import sdl
+from glm import mat4, ortho2d, ortho3d, rotate, X_AXIS, Y_AXIS, Z_AXIS, vec3
+from glue import (lazydecls, defproperty, autoinit, expect_empty)
+
+from .actor import Actor
+from .interface import Named
+from .config import Config, StereoMode
+from .render import CameraAttribs, RNode, RJCMD_DRAW
+from .system import System
+from .hmd import HMDManager
+from .timing import TimeManager
+from .core import RenderManager
+from .input import Binding, InputSet, Mouse
+
+################################################################################
+
+log = logging.getLogger('liminal.camera')
+
+VEC_SCALE1 = vec3(1,1,1)
+
+################################################################################
+
+class ViewActor(Actor):
+ __slots__ = [
+ '_lens',
+ '_ortho_scale',
+ '_ortho_sensor',
+ '_near',
+ '_far',
+ '_perspective',
+ '_sensor_width',
+ '_sensor_height',
+ '_view_attribs',
+ '_stereo_view',
+ '_view_attrib_count',
+ ]
+
+ def __init__(self, geom):
+ Actor.__init__(self, geom)
+ self._lens = math.radians(90.0)
+ self._ortho_scale = 1.0
+ self._ortho_sensor = False
+ self._near = 0.1
+ self._far = 100.0
+ self._sensor_width = 1.0
+ self._sensor_height = 1.0
+ self._perspective = True
+ self._stereo_view = Config().stereo_mode != StereoMode.OFF
+ self._view_attrib_count = 1
+ self._view_attribs = None
+
+ def set_sensor(self, size):
+ self.sensor_width = size.x
+ self.sensor_height = size.y
+
+ @property
+ def stereo_view(self):
+ return self._stereo_view
+
+ @property
+ def sensor_width(self):
+ return self._sensor_width
+ @sensor_width.setter
+ def sensor_width(self, value):
+ self._sensor_width = value
+
+ @property
+ def sensor_height(self):
+ return self._sensor_height
+ @sensor_height.setter
+ def sensor_height(self, value):
+ self._sensor_height = value
+
+ @property
+ def ortho_sensor(self):
+ return self._ortho_sensor
+ @ortho_sensor.setter
+ def ortho_sensor(self, value):
+ self._ortho_sensor = value
+
+ @property
+ def aspect(self):
+ return self._sensor_width / self._sensor_height
+
+ @property
+ def lens(self):
+ return self._lens
+ @lens.setter
+ def lens(self, value):
+ self._lens = value
+
+ # (tfov = tan(fov/2))
+ # tfovx2 = aspect * tfovy2
+ # tfovy2 = tfovx2 / aspect
+
+ @property
+ def fov(self):
+ tfovx2 = math.tan(self._lens*0.5) * self.aspect
+ return math.degrees(math.atan(tfovx2) * 2.0)
+ @fov.setter
+ def fov(self, value):
+ tfovy2 = math.tan(math.radians(value)*0.5) / self.aspect
+ self._lens = math.atan(tfovy2) * 2.0
+
+ @property
+ def ortho_scale(self):
+ return self._ortho_scale
+ @ortho_scale.setter
+ def ortho_scale(self, value):
+ self._ortho_scale = value
+
+ @property
+ def near(self):
+ return self._near
+ @near.setter
+ def near(self, value):
+ self._near = value
+
+ @property
+ def far(self):
+ return self._far
+ @far.setter
+ def far(self, value):
+ self._far = value
+ self.invalidate_uniforms()
+
+ @property
+ def perspective(self):
+ return self._perspective
+ @perspective.setter
+ def perspective(self, value):
+ self._perspective = value
+
+ def adjust_eye_orientation(self, tf):
+ return tf
+
+ def adjust_eye_transform(self, tf):
+ return tf
+
+ def _get_transform_matrix(self):
+ p,s,o = self.get_position_scale_orientation()
+ return mat4.compose(p,VEC_SCALE1,o)
+
+ @property
+ def eye_orientation(self):
+ return self.adjust_eye_orientation(self.orientation)
+
+ @property
+ def eye_transform(self):
+ return self.adjust_eye_transform(self.transform)
+
+ @property
+ def world_eye_transform(self):
+ raise AttributeError, "no longer valid"
+
+ @property
+ def world_eye_orientation(self):
+ raise AttributeError, "no longer valid"
+
+ def look_at(self, eye, center, up=vec3(0,0,1)):
+ self.transform = mat4.look_at(eye, center, up)
+
+ @classmethod
+ def make_ray(cls, mtx, pos):
+ v0 = mtx.mul_vec3(vec3(pos.x,pos.y,0))
+ v1 = mtx.mul_vec3(vec3(pos.x,pos.y,-1))
+ return v0, v1.sub(v0).normalize()
+
+ def get_ray(self, pos):
+ return self.make_ray(self.eye_transform, pos)
+
+ def to_projection_eye_matrix(self, eye_id):
+ if self._perspective:
+ return mat4.perspective(
+ math.degrees(self.lens), self.aspect, self.near, self.far), None
+ else:
+ s = self._ortho_scale
+ sy = s*0.5
+ sx = s*0.5
+ if self._ortho_sensor:
+ sx *= self._sensor_width
+ sy *= self._sensor_height
+ else:
+ sx *= self.aspect
+ return ortho3d(-sx, sx, -sy, sy, self.near, self.far), None
+
+ def to_view_matrix(self):
+ return self.eye_transform.inverse()
+
+ def update_uniforms(self):
+ Actor.update_uniforms(self)
+ for k, attrib in enumerate(self.view_attribs):
+ with attrib.map_write() as data:
+ self.write_camera_attribs(attrib, data, k)
+
+ def adjust_proj_matrix(self, attrib, proj_mtx, view_mtx):
+ return proj_mtx
+
+ def write_camera_attribs(self, attrib, data, eye_id):
+ if self._stereo_view and eye_id == 0:
+ eye_id = -1
+
+ camera_mtx = self.eye_transform
+
+ proj_mtx,eye_mtx = self.to_projection_eye_matrix(eye_id)
+ if eye_mtx:
+ camera_mtx = camera_mtx.mul_mat4(eye_mtx)
+
+ view_mtx = camera_mtx.inverse()
+ proj_mtx = self.adjust_proj_matrix(attrib, proj_mtx, view_mtx)
+
+ inv_proj_matrix = proj_mtx.inverse()
+ viewproj_mtx = proj_mtx.mul_mat4(view_mtx)
+
+ data.mtx_view = view_mtx._ptr[0]
+ data.mtx_camera = camera_mtx._ptr[0]
+ data.mtx_proj = proj_mtx._ptr[0]
+ data.mtx_viewproj = viewproj_mtx._ptr[0]
+ data.mtx_inv_proj = inv_proj_matrix._ptr[0]
+ data.near = self.near
+ data.far = self.far
+ data.time = TimeManager().duration
+ attrib.view_matrix = view_mtx
+ attrib.projection_matrix = proj_mtx
+
+ @property
+ def view_attribs(self):
+ self.ensure_view_attribs()
+ return self._view_attribs
+
+ def get_view_attrib(self, eye_id):
+ return self.view_attribs[eye_id > 0]
+
+ def ensure_view_attribs(self):
+ if not self._view_attribs:
+ if self._stereo_view:
+ attrib_count = 2
+ else:
+ attrib_count = self._view_attrib_count
+ view_attribs = []
+ self._view_attribs = view_attribs
+ for i in range(attrib_count):
+ attrib = CameraAttribs()
+ attrib.layer_id = i
+ view_attribs.append(attrib)
+ self.update_uniforms()
+
+################################################################################
+
+class Camera(ViewActor):
+ __slots__ = [
+ '_framebuffer',
+ '_light_buffer',
+ '_clear_depth',
+ '_clear_color',
+ '_clear_stencil',
+ ]
+
+ def __init__(self, geom):
+ ViewActor.__init__(self, geom)
+ self._framebuffer = None
+ self._light_buffer = None
+ self._clear_depth = True
+ self._clear_color = None
+ self._clear_stencil = False
+
+ def process(self, batch, jobs):
+ RNode.process(self, batch, jobs)
+ self.invalidate_uniforms()
+ self.ensure_view_attribs()
+
+ vp_indices = [batch.get_viewport_index(vp)
+ for vp in RenderManager().render_viewports]
+
+ for job in list(jobs):
+ if job.key.command != RJCMD_DRAW: continue
+ for i in range(len(self._view_attribs)):
+ view_attrib = self.get_view_attrib(i)
+ if i > 0:
+ job = batch.copy_job(job)
+ jobs.append(job)
+ job.draw.ubo_camera = view_attrib
+ job.viewport_index = vp_indices[i]
+ job.key.viewport = job.viewport_index
+
+ @property
+ def light_buffer(self):
+ return self._light_buffer
+ @light_buffer.setter
+ def light_buffer(self, value):
+ self._light_buffer = value
+
+ @property
+ def framebuffer(self):
+ return self._framebuffer
+ @framebuffer.setter
+ def framebuffer(self, value):
+ self._framebuffer = value
+
+ def write_camera_attribs(self, attrib, data, eye_id):
+ ViewActor.write_camera_attribs(self, attrib, data, eye_id)
+
+ vp_matrix = tuple(data.mtx_viewproj.a[0:16])
+
+ last_vp_matrix = attrib.last_vp_matrix
+ if last_vp_matrix is None:
+ last_vp_matrix = vp_matrix
+ attrib.last_vp_matrix = vp_matrix
+
+ data.mtx_last_vp.a = last_vp_matrix
+
+ @property
+ def clear_depth(self):
+ return self._clear_depth
+ @clear_depth.setter
+ def clear_depth(self, value):
+ self._clear_depth = value
+
+ @property
+ def clear_color(self):
+ return self._clear_color
+ @clear_color.setter
+ def clear_color(self, value):
+ self._clear_color = value
+
+ @property
+ def clear_stencil(self):
+ return self._clear_stencil
+ @clear_stencil.setter
+ def clear_stencil(self, value):
+ self._clear_stencil = value
+
+################################################################################
+
+class ScreenCamera(Camera):
+ __slots__ = [
+ ]
+
+ def __init__(self, geom):
+ Camera.__init__(self, geom)
+ self.use_screen_sensor()
+ self.fov = 90.0
+
+ def update_uniforms(self):
+ Camera.update_uniforms(self)
+ if Config().stereo_mode == StereoMode.OCULUS_RIFT:
+ self.invalidate_uniforms()
+
+ def use_screen_sensor(self):
+ size = System().screensize
+ if Config().stereo_mode == StereoMode.OCULUS_RIFT:
+ size.x //= 2
+
+ self.set_sensor(size)
+
+ def to_projection_eye_matrix(self, eye_id):
+ cfg = Config()
+ if self._perspective:
+ if cfg.stereo_mode == StereoMode.OCULUS_RIFT:
+ hmd = HMDManager()
+ fov = math.degrees(hmd.get_fovy())
+
+ proj_mtx = mat4.perspective(
+ fov, self.aspect, self.near, self.far)
+
+ h = hmd.get_horizontal_center_offset()
+
+ H = mat4.translation_fff(
+ eye_id * -h, 0.0, 0.0)
+
+ dip = hmd.interpupillary_distance
+
+ V = mat4.translation_fff(
+ eye_id * (dip/2.0), 0.0, 0.0)
+
+ proj_mtx = H.mul_mat4(proj_mtx)
+ else:
+ proj_mtx = mat4.perspective(
+ math.degrees(self.lens), self.aspect, self.near, self.far)
+
+ H = mat4.translation_fff(
+ eye_id * cfg.fpd, 0.0, 0.0)
+
+ V = mat4.translation_fff(
+ eye_id * cfg.ipd, 0.0, 0.0)
+
+ proj_mtx = H.mul_mat4(proj_mtx)
+ return proj_mtx, V
+ else:
+ s = self._ortho_scale
+ if cfg.stereo_mode == StereoMode.OCULUS_RIFT:
+ s *= 3.0
+ sy = s*0.5
+ sx = s*0.5
+ if self._ortho_sensor:
+ sx *= self._sensor_width
+ sy *= self._sensor_height
+ else:
+ sx *= self.aspect
+ mtx = ortho2d(-sx, sx, -sy, sy)
+ if cfg.stereo_mode == StereoMode.OCULUS_RIFT:
+ hmd = HMDManager()
+ h = hmd.get_horizontal_center_offset()
+
+ H = mat4.translation_fff(
+ eye_id * -h, 0.0, 0.0)
+
+ mtx = H.mul_mat4(mtx)
+
+ return mtx, None
+
+ def adjust_eye_orientation(self, tf):
+ if self._perspective and Config().stereo_mode == StereoMode.OCULUS_RIFT:
+ mat_o = HMDManager().get_orientation().to_mat3()
+ return tf.mul_mat3(mat_o)
+ return tf
+
+ def adjust_eye_transform(self, tf):
+ if self._perspective and Config().stereo_mode == StereoMode.OCULUS_RIFT:
+ mat_o = HMDManager().get_orientation()
+ return tf.mul_mat4(mat_o).mul_mat4(mat4.translation_fff(0.0, 0.12, -0.08))
+ return tf
+
+################################################################################
+
+class FPSControls(InputSet):
+ move_left = Binding()
+ move_left.key(sdl.SDLK_a)
+
+ move_right = Binding()
+ move_right.key(sdl.SDLK_d)
+
+ move_forward = Binding()
+ move_forward.key(sdl.SDLK_w)
+
+ move_backward = Binding()
+ move_backward.key(sdl.SDLK_s)
+
+ move_up = Binding()
+ move_up.key(sdl.SDLK_r)
+
+ move_down = Binding()
+ move_down.key(sdl.SDLK_f)
+
+ roll_left = Binding()
+ roll_left.key(sdl.SDLK_q)
+
+ roll_right = Binding()
+ roll_right.key(sdl.SDLK_e)
+
+ run = Binding()
+ run.key(sdl.SDLK_LSHIFT)
+
+
+################################################################################
+
+@lazydecls
+class DebugCamera(ScreenCamera):
+ speed_walk = defproperty(default = 5.0)
+ speed_run = defproperty(default = 30.0)
+ allow_move = defproperty(default = True)
+ lock_x = defproperty(default = False)
+ lock_y = defproperty(default = False)
+ lock_z = defproperty(default = False)
+ lock_up = defproperty(default = True)
+ mouse_strafe = defproperty(default = False)
+
+ def __init__(self, geom, **kwargs):
+ ScreenCamera.__init__(self, geom)
+ self.name = 'debug-camera'
+ kwargs = autoinit(DebugCamera, self, **kwargs)
+ self.controls = FPSControls()
+ self.controls.event_frame.addweak(self.on_controls)
+ self.mouse = Mouse(self.controls)
+ self.transform = mat4.look_at(
+ vec3(0,-1,0), vec3(0,1,0), vec3(0,0,1))
+ if kwargs.pop('disable_on_grab_release', True):
+ self.controls.disable_on_grab_release()
+ expect_empty(kwargs)
+ self.controls.push()
+
+ def on_controls(self):
+ rift_mode = Config().stereo_mode == StereoMode.OCULUS_RIFT
+
+ mtx = self.transform
+ mtx_orient = self.eye_orientation
+
+ controls = self.controls
+
+ if controls.run.active:
+ SPEED = self.speed_run
+ else:
+ SPEED = self.speed_walk
+
+ SPEED *= TimeManager().timedelta
+
+ mouse_strafe = self.mouse_strafe
+
+ moved = 0
+
+ mouse = self.mouse
+
+ if not mouse_strafe:
+ x,y = mouse.smooth_relative
+ if x:
+ mtx = rotate(mtx, -x*0.1, Y_AXIS)
+ moved += 1
+ if y and not rift_mode:
+ mtx = rotate(mtx, -y*0.1, X_AXIS)
+ moved += 1
+
+ if self.allow_move:
+ if mouse_strafe:
+ if not self.lock_x:
+ mtx.col3_vec4(mtx.col3().sub_vec4(
+ mtx_orient.col0().to_vec4().mul_f(-x*0.001)))
+ moved += 1
+ if not self.lock_y:
+ mtx.col3_vec4(mtx.col3().sub_vec4(
+ mtx_orient.col1().to_vec4().mul_f(y*0.001)))
+ moved += 1
+
+ if not self.lock_up:
+ if controls.roll_left.active: #Q
+ mtx = rotate(mtx, 1.0, Z_AXIS)
+ moved += 1
+ if controls.roll_right.active: #E
+ mtx = rotate(mtx, -1.0, Z_AXIS)
+ moved += 1
+
+ if not self.lock_x:
+ if controls.move_left.active: #A
+ mtx.col3_vec4(mtx.col3().sub_vec4(
+ mtx_orient.col0().to_vec4().mul_f(SPEED)))
+ moved += 1
+ if controls.move_right.active: #D
+ mtx.col3_vec4(mtx.col3().add_vec4(
+ mtx_orient.col0().to_vec4().mul_f(SPEED)))
+ moved += 1
+ if not self.lock_y:
+ if controls.move_up.active: #R
+ mtx.col3_vec4(mtx.col3().add_vec4(
+ mtx_orient.col1().to_vec4().mul_f(SPEED)))
+ moved += 1
+ if controls.move_down.active: #F
+ mtx.col3_vec4(mtx.col3().sub_vec4(
+ mtx_orient.col1().to_vec4().mul_f(SPEED)))
+ moved += 1
+ if not self.lock_z:
+ if controls.move_forward.active: #W
+ mtx.col3_vec4(mtx.col3().sub_vec4(
+ mtx_orient.col2().to_vec4().mul_f(SPEED)))
+ moved += 1
+ if controls.move_backward.active: #S
+ mtx.col3_vec4(mtx.col3().add_vec4(
+ mtx_orient.col2().to_vec4().mul_f(SPEED)))
+ moved += 1
+
+ if not moved:
+ return
+
+ if self.lock_up:
+ pos = mtx.get_translation()
+ rot = mtx.to_mat3()
+ right = mtx.col0().xyz
+ right.z = 0.0
+ rot = rot.align_x_vec3(right, 1.0)
+ mtx = mat4.compose(pos, vec3(1,1,1), rot)
+
+ self.transform = mtx
+
+################################################################################
+
A => liminal_legacy/liminal/engine/config.py +392 -0
@@ 0,0 1,392 @@
+from __future__ import (print_function, division, absolute_import)
+
+import os
+import sys
+import json
+import logging
+
+from argparse import ArgumentParser
+from types import NoneType
+
+import gl
+
+from glue import lazydecls, callset, defproperty, Singleton, Undefined, autoinit
+from ..utils import loglevel_add_arguments, loglevel_apply_args, clamp
+
+################################################################################
+
+log = logging.getLogger('liminal.config')
+
+IS_DARWIN = sys.platform == 'darwin'
+IS_WIN32 = sys.platform == 'win32'
+
+################################################################################
+
+class StereoMode:
+ OFF = 0
+ SIDE_BY_SIDE = 1
+ OCULUS_RIFT = 2
+
+################################################################################
+
+class VideoQuality:
+ """
+ Video Quality regulates which non-essential effects are turned on at which
+ resolutions. Lower quality means less effect at lower sampling rates,
+ higher quality means more effects at higher sampling rates.
+
+ Here's a table of which effects are affected at each quality level, at
+ which quality level, with a special column for Oculus Rift mode.
+
+ - = disabled
+ * = as dictated by quality level
+
+ | OVR | --- | -- | - | = | + | ++ | +++ |
+ -------------------------+-----+-----+-----+-----+-----+-----+-----+-----+
+ SSAO | * | - | - | - | 8/2 | 8/1 |16/1 |16/1 |
+ -------------------------+-----+-----+-----+-----+-----+-----+-----+-----+
+ Bloom | * | - | /8 | /4 | /2 | /1 | /1 | /1 |
+ -------------------------+-----+-----+-----+-----+-----+-----+-----+-----+
+ Depth of Field | - | - | - | - | 8 | 8 | 16 | 16 |
+ -------------------------+-----+-----+-----+-----+-----+-----+-----+-----+
+ Motion Blur | - | - | - | - | 8 | 8 | 16 | 16 |
+ -------------------------+-----+-----+-----+-----+-----+-----+-----+-----+
+ EVSM Shadow Maps | * | ^9 | ^9 | ^9 | ^9 |^10x2|^10x2|^10x4|
+ -------------------------+-----+-----+-----+-----+-----+-----+-----+-----+
+ FXAA Anti-Aliasing | - | - | - | - | - | 12 | 20 | 29 |
+ -------------------------+-----+-----+-----+-----+-----+-----+-----+-----+
+ Shadow Map Cascades | * | 1 | 1 | 2+ | 3+ | 3+ | 4+ | 5+ |
+ -------------------------+-----+-----+-----+-----+-----+-----+-----+-----+
+ Normal Mapping | * | - | - | - | - | Y | Y | Y |
+ -------------------------+-----+-----+-----+-----+-----+-----+-----+-----+
+ """
+
+ # overkill - best suited to take beauty shots, not recommended for playing
+ HIGHEST = 6
+
+ # setting designed to deliver good quality for
+ # modern graphics card
+ HIGHER = 5
+
+ # more beauty, less performance: this is the default
+ HIGH = 4
+
+ # trade-off between looks and performance
+ MEDIUM = 3
+
+ # more performance, less beauty
+ LOW = 2
+
+ # lowest setting with only essential effects enabled
+ LOWER = 1
+
+ # total fallback - all effects off, least effort necessary to
+ # display a consistent picture.
+ LOWEST = 0
+
+ DEFAULT = HIGH
+
+################################################################################
+
+@lazydecls
+class Config(object):
+ __metaclass__ = Singleton
+
+ SETTINGS_VARS = {
+ 'fullscreen',
+ 'videoquality',
+ 'width',
+ 'height'
+ }
+
+ videomode = defproperty(default = None, types = (tuple, str, NoneType))
+ glversion = defproperty(default = None, types = (tuple, NoneType))
+ bail_on_error = defproperty(default = True, types = bool)
+ width = defproperty(default = None, types = (int, NoneType))
+ height = defproperty(default = None, types = (int, NoneType))
+ fullscreen = defproperty(default = False, types = bool)
+ vsync = defproperty(default = True, types = bool)
+ msaa = defproperty(default = None, types = (int, NoneType))
+ use_sound = defproperty(default = True, types = bool)
+ xpos = defproperty(default = -1, types = int)
+ ypos = defproperty(default = -1, types = int)
+ enable_profiler = defproperty(default = False, types = bool)
+ profiler_path = defproperty(default = None, types = (str, NoneType))
+ videoquality = defproperty(default = VideoQuality.DEFAULT, types = int)
+
+ disable_logic = defproperty(default = False, types = bool)
+ stereo_mode = defproperty(default = StereoMode.OFF, types = int)
+ enable_ffpemu = defproperty(default = True, types = bool)
+ print_fps = defproperty(default = False, types = bool)
+
+ """
+ how to turn sequence into gif:
+ convert -delay 3 -resize 320x180 -loop 0 *.png ../test_cube.gif
+
+ into video:
+ ffmpeg -start_number 0 -f image2 -i '%04d.png' -vcodec libx264 -b 800k ../video.avi
+ """
+ dump_image_path = defproperty(default = None, types = str)
+
+ # dump renderjobs to console
+ print_renderjobs = defproperty(default = False, types = bool)
+ # permutate framebuffer formats and print results
+ test_fb_formats = defproperty(default = False, types = bool)
+ # open debug GL context
+ debug_context = defproperty(default = False, types = bool)
+
+ # for stereo rendering:
+ fpd = defproperty(default = 0.0, types = float) # screen width
+ ipd = defproperty(default = 0.0, types = float) # interpupillary distance
+
+ base_name = defproperty(default = 'liminal', types = str)
+ company_name = defproperty(default = 'duangle', types = str)
+ base_path = defproperty(default = None, types = (str, NoneType))
+ userdir_path = defproperty(default = None, types = (str, NoneType))
+
+ headless = defproperty(default = False, types = bool)
+ editable = defproperty(default = False, types = bool)
+ rebuild_assets = defproperty(default = False, types = bool)
+ webui_port = defproperty(default = 5000, types = int)
+
+ def __init__(self):
+ self._args_parsed = False
+ autoinit(Config, self)
+
+ def parse_argv(self):
+ if self._args_parsed:
+ return
+ self._args_parsed = True
+ parser = ArgumentParser()
+ self.add_arguments(parser)
+ args = parser.parse_args()
+ self.apply_args(args)
+
+ @property
+ def settings_dict(self):
+ settings = {}
+ for varname in self.SETTINGS_VARS:
+ value = getattr(self, varname)
+ if value == getattr(Config, varname).default:
+ continue
+ settings[varname] = value
+ return settings
+
+ @settings_dict.setter
+ def settings_dict(self, settings):
+ for varname in self.SETTINGS_VARS:
+ value = settings.get(varname, Undefined)
+ if value is Undefined:
+ continue
+ setattr(self, varname, value)
+
+ @property
+ def settings_path(self):
+ return os.path.join(self.userdir_path, 'settings.json')
+
+ def add_arguments(self, parser):
+ loglevel_add_arguments(parser)
+
+ parser.add_argument('--videomode', dest='videomode', metavar='MODE',
+ help='Videomode of fullscreen. Use "list" for a list of modes.')
+ parser.add_argument('--novsync', action='store_true', dest="novsync",
+ help="disable vertical frequency synchronization")
+ parser.add_argument('--nosound', action='store_true', dest="nosound",
+ help="disable OpenAL audio engine")
+ parser.add_argument('--profiler', dest='profiler', metavar='FILEPATH',
+ help="Run main loop in profiler and save results to FILEPATH")
+ parser.add_argument('--fullscreen', action='store_true', dest="fullscreen",
+ help="enable fullscreen")
+ parser.add_argument('--gl-ignore-errors', action='store_true', dest="gl_ignore_errors",
+ help="ignore GL errors")
+ parser.add_argument('--ignore-errors', action='store_true', dest="ignore_errors",
+ help="Do not bail on errors")
+ parser.add_argument('--xpos', metavar='XPOS', dest="xpos", default=-1,
+ type=int,
+ help="X position of window")
+ parser.add_argument('--ypos', metavar='YPOS', dest="ypos", default=-1,
+ type=int,
+ help="Y position of window")
+ parser.add_argument('--width', metavar='WIDTH', dest="width", default=0,
+ type=int,
+ help="Width of window")
+ parser.add_argument('--height', metavar='HEIGHT', dest="height", default=0,
+ type=int,
+ help="Height of window")
+ parser.add_argument('--video-quality', metavar='QUALITY', dest="quality",
+ default=VideoQuality.DEFAULT, type=int,
+ help="Quality of shaders and rendering [0-6] (0 = fastest, 6 = prettiest)")
+ parser.add_argument('--glarb', metavar='GLARB', dest="glarb", default=None,
+ choices = ['3.2','3.3','4.0','4.1','4.2','4.3','4.4','0.0'], type=str,
+ help="use GL version (3.0 = shadermodel 1.30, 3.2 = shadermodel 1.50)")
+ parser.add_argument('--msaa', metavar='LEVEL', dest="msaa", default=0,
+ choices = range(33), type=int,
+ help="use anti-aliasing level (0..32)")
+
+ parser.add_argument('--nologic', action='store_true', dest="nologic",
+ help="disable handling of logic controllers")
+ parser.add_argument('--noffpemu', action='store_true', dest="noffpemu",
+ help="disable fixed function pipeline emulation interface")
+ parser.add_argument('--3d', metavar='MODE', dest="stereo_mode",
+ choices = ['sbs', 'or'], default = None, type = str,
+ help="Render in stereoscopic 3D (sbs: Side-By-Side, or: Oculus Rift)")
+ parser.add_argument('--dump-image-sequence', metavar='NAME', dest="dump_image_path",
+ default = None, type = str,
+ help="dump each frame to image to subfolder")
+ parser.add_argument('--printfps', action='store_true', dest="printfps",
+ help="Print FPS statistics in intervals.")
+ parser.add_argument('--debug-wireframe', action='store_true', dest="wireframe",
+ help="render all materials as wireframe")
+ parser.add_argument('--debug-renderjobs', action='store_true', dest="debug_renderjobs",
+ help="dump renderjobs of current frame to console")
+ parser.add_argument('--debug-context', action='store_true', dest="debug_context",
+ help="Open debug GL context, prints additional warnings to the console")
+ parser.add_argument('--debug-fb-formats', action='store_true', dest="debug_fb_formats",
+ help="permutate list of framebuffer formats and dump to console")
+ parser.add_argument('-p', '--project', dest="project",
+ help="Path to project folder")
+ parser.add_argument('--webui-port', dest="webui_port", type=int,
+ help="Port on which to run web UI", default=5000)
+ parser.add_argument('--headless', action='store_true', dest="headless",
+ help="run web UI only")
+ parser.add_argument('--rebuild-assets', action='store_true',
+ dest="rebuild_assets", help="rebuild editor assets on change")
+ parser.add_argument('--edit', action='store_true', default = False,
+ dest="editable", help="enable editing UI")
+
+
+ def apply_args(self, args):
+ loglevel_apply_args(args)
+
+ if self.base_path is None:
+ print("ERROR: Config().base_path is not set")
+ raise SystemExit, 255
+
+ if not os.path.isdir(self.base_path):
+ print("ERROR: {} is not a directory.".format(self.base_path))
+ raise SystemExit, 255
+
+ if IS_WIN32:
+ self.userdir_path = os.path.normpath(
+ '{APPDATA}/{companyname}/{basename}'.format(
+ companyname = self.company_name,
+ basename = self.base_name, **os.environ))
+ else:
+ CONFIG_HOME = os.path.expanduser('~/.config')
+ if 'XDG_CONFIG_HOME' in os.environ:
+ CONFIG_HOME = os.environ['XDG_CONFIG_HOME']
+ self.userdir_path = os.path.normpath(
+ os.path.join(CONFIG_HOME, self.company_name, self.base_name))
+
+ if not os.path.isdir(self.userdir_path):
+ os.makedirs(self.userdir_path)
+
+ if not self.settings_exist():
+ self.save_settings()
+ self.load_settings()
+
+ if args.videomode == 'list':
+ self.videomode = 'list'
+ elif args.videomode:
+ self.videomode = tuple([int(v) for v in args.videomode.split('.')])
+
+ if args.glarb:
+ self.glversion = tuple([int(v) for v in args.glarb.split('.')])
+
+ if args.gl_ignore_errors:
+ gl.internal.RAISE_ON_ERROR = False
+
+ if args.ignore_errors:
+ self.bail_on_error = False
+
+ self.videoquality = clamp(args.quality,
+ VideoQuality.LOWEST, VideoQuality.HIGHEST)
+
+ if args.width:
+ self.width = args.width
+ if args.height:
+ self.height = args.height
+ if args.fullscreen:
+ self.fullscreen = True
+ if args.novsync:
+ self.vsync = False
+ if args.msaa:
+ self.msaa = int(args.msaa)
+ if args.nosound:
+ self.use_sound = False
+ if args.xpos:
+ self.xpos = args.xpos
+ if args.ypos:
+ self.ypos = args.ypos
+ if args.profiler:
+ self.enable_profiler = True
+ self.profiler_path = args.profiler
+
+ if args.nologic:
+ self.disable_logic = True
+
+ if args.stereo_mode:
+ self.stereo_mode = {
+ 'sbs' : StereoMode.SIDE_BY_SIDE,
+ 'or' : StereoMode.OCULUS_RIFT
+ }[args.stereo_mode]
+ if self.stereo_mode == StereoMode.SIDE_BY_SIDE:
+ SCREEN_WIDTH = 0.505
+ EYE_WIDTH = 0.07 #0.067
+ # width of my screen: 0.505 (50.5cm)
+ # eye width 0.07 (70 mm)
+ self.ipd = EYE_WIDTH
+ self.fpd = self.ipd / SCREEN_WIDTH
+
+ if args.dump_image_path is not None:
+ self.dump_image_path = args.dump_image_path
+
+ if args.noffpemu:
+ self.enable_ffpemu = False
+
+ if args.printfps:
+ self.print_fps = True
+
+ if args.wireframe:
+ from . import material
+ material.GLMaterial.DEFAULT_WIREFRAME = True
+ if args.debug_renderjobs:
+ self.print_renderjobs = True
+
+ if args.debug_context:
+ self.debug_context = True
+
+ if args.debug_fb_formats:
+ self.test_fb_formats = True
+
+ if args.editable:
+ self.editable = True
+
+ if args.headless:
+ self.headless = args.headless
+ self.editable = True
+
+ if args.rebuild_assets:
+ self.rebuild_assets = args.rebuild_assets
+
+ if args.webui_port:
+ self.webui_port = args.webui_port
+
+
+ def settings_exist(self):
+ return os.path.isfile(self.settings_path)
+
+ def load_settings(self):
+ if not self.settings_exist():
+ return
+ log.info("Loading config from {}".format(self.settings_path))
+ with open(self.settings_path, 'r') as f:
+ obj = json.load(f)
+ self.settings_dict = obj
+
+ def save_settings(self):
+ with open(self.settings_path, 'w') as f:
+ json.dump(self.settings_dict, f)
+ log.info("Saved config to {}".format(self.settings_path))
+
+
No newline at end of file
A => liminal_legacy/liminal/engine/core.py +446 -0
@@ 0,0 1,446 @@
+from __future__ import (print_function, division, absolute_import)
+
+import logging
+import time
+import os
+import sys
+
+from glm import ivec4, vec4
+from glue import Singleton, callset
+from stbimage import stbi_write_png, stbi_flip_y
+
+from gl import glEnable, GL_TEXTURE_CUBE_MAP_SEAMLESS
+
+from .render import (Viewport, NullFramebuffer, RenderBatch,
+ RViewportNode, RClearNode)
+from .system import System
+from .interface import Named
+from .config import Config, StereoMode, VideoQuality
+from .framebuffer import Framebuffer
+from .hmd import HMDManager
+from .profiler import FrameProfiler
+from .timing import TimeManager
+from liminal.engine.render import RCustomNode
+
+IS_DARWIN = sys.platform == 'darwin'
+
+################################################################################
+
+log = logging.getLogger('liminal.core')
+
+################################################################################
+
+class FXAAMode:
+ Off = 0
+ #GreenAsLuma = 1
+ AlphaAsLuma = 2
+
+################################################################################
+
+class RenderManager(Named):
+ __metaclass__ = Singleton
+
+ __slots__ = [
+ '_stereo_depth',
+ '_stereo_mode',
+ '_framebuffer',
+ '_viewport',
+ '_framebuffer_viewport',
+ '_render_viewports',
+ '_batch',
+ '_ppfx',
+ '_show_profile',
+ '_fxaa_mode',
+ '_nodes',
+ '_debug_textures',
+ '_clear_node',
+ '_invalid_uniforms',
+
+ 'start_time',
+ 'last_timestamp',
+ 'frames_rendered',
+ 'frame_rate',
+
+ 'event_frame_rendered',
+ 'event_gather',
+ ]
+
+ FPS_MEASURE_TIME = 1
+
+ def __init__(self):
+ Named.__init__(self)
+ self.name = 'render_tree'
+ self.frame_rate = 60.0
+ self._viewport = None
+ self._render_viewports = ()
+ self._batch = None
+ self._stereo_depth = 1.0
+ self._clear_node = None
+ self._ppfx = None
+ if Config().videoquality >= VideoQuality.HIGH:
+ self._fxaa_mode = FXAAMode.AlphaAsLuma
+ else:
+ self._fxaa_mode = FXAAMode.Off
+ self._show_profile = False
+ self._debug_textures = []
+ self._framebuffer_viewport = None
+ self._invalid_uniforms = set()
+
+ self.frames_rendered = 0
+ self.start_time = time.time()
+ self.last_timestamp = self.start_time
+ self.event_frame_rendered = callset()
+ self.event_gather = callset()
+
+ System.event_resized.add(self.on_resized)
+ System.event_draw.add(self.render)
+
+ def init(self):
+ self._batch = RenderBatch()
+ self._batch.name = 'render'
+ self.init_viewport()
+ self.setup_nodes()
+
+ @property
+ def batch_profile(self):
+ return self._batch.profile
+
+ @property
+ def batch(self):
+ return self._batch
+
+ @property
+ def fxaa_mode(self):
+ return self._fxaa_mode
+ @fxaa_mode.setter
+ def fxaa_mode(self, value):
+ self._fxaa_mode = value
+
+ @property
+ def show_profile(self):
+ return self._show_profile
+ @show_profile.setter
+ def show_profile(self, value):
+ self._show_profile = value
+
+ @property
+ def stereo_depth(self):
+ return self._stereo_depth
+ @stereo_depth.setter
+ def stereo_depth(self, value):
+ self._stereo_depth = value
+
+ @property
+ def render_viewports(self):
+ return self._render_viewports
+
+ @property
+ def viewport(self):
+ return self._viewport
+
+ @property
+ def ppfx(self):
+ return self._ppfx
+
+ @property
+ def framebuffer(self):
+ return self._framebuffer
+
+ @property
+ def clear_node(self):
+ return self._clear_node
+
+ @property
+ def framebuffer_viewport(self):
+ return self._framebuffer_viewport
+
+ def set_root_ppfx(self, ppfx):
+ if ppfx == self._ppfx:
+ return
+ self._ppfx = ppfx
+ self._ppfx.add_framebuffer_textures(self._framebuffer)
+
+ def setup_nodes(self):
+ batch = self.batch
+
+ node_clear = RClearNode()
+ node_clear.name = 'root-clear'
+ node_clear.color = vec4(0,0,1,1)
+ self._clear_node = node_clear
+
+ node_viewport = RViewportNode(self._viewport)
+ node_viewport.name = 'root'
+ self._framebuffer_viewport = node_viewport
+
+ batch << self._framebuffer << node_viewport << node_clear
+
+ # bounce ppfx to final framebuffer
+ node_fb_null = NullFramebuffer
+
+ batch << node_fb_null
+
+ for vp in self._render_viewports:
+ node_viewport = RViewportNode(vp)
+ node_viewport.name = 'null-{}'.format(vp.name)
+ node_fb_null << node_viewport << self._ppfx
+
+
+ def _add_oculus_rift_ppfx(self):
+ from .ppfx import PPFX
+
+ #self._framebuffer = Framebuffer.from_system(scale=1.5, use_depth=True)
+ self._framebuffer = Framebuffer.from_system(use_depth=True)
+ self._framebuffer.name = 'root'
+
+ ppfx = PPFX('ppfx/oculus_rift')
+ ppfx.name = 'root-oculus-rift'
+ self.set_root_ppfx(ppfx)
+
+ hmd = HMDManager()
+ resolution = hmd.resolution
+
+ winw,winh = System().screensize.to_tuple()
+
+ x,y,w,h = 0,0,resolution.x*0.5,resolution.y
+ x /= winw
+ y /= winh
+ w /= winw
+ h /= winh
+
+ aspect = resolution.x * 0.5 / resolution.y
+
+ xcenteroffset = hmd.get_horizontal_center_offset()
+
+ scale = hmd.get_distortion_scale()
+ scale_factor = 1.0 / scale
+
+ ppfx.set_uniform_values(
+ LensCenter = (x + (w + xcenteroffset*0.5)*0.5, y + h*0.5),
+ ScreenCenter = (x + w*0.5, y + h*0.5),
+ Scale = ((w/2) * scale_factor, (h/2) * scale_factor * aspect),
+ ScaleIn = ((2/w), (2/h) / aspect),
+ HmdWarpParam = hmd.distortion_k,
+ ChromAbParam = hmd.chroma_ab_correction)
+
+ txcolor = ppfx.textures[0]
+ txcolor.bilinear = True
+ #self.debug_color_texture(txcolor)
+ #txcolor.anisotropic = True
+
+ def _add_default_ppfx(self):
+ from .ppfx import PPFX
+
+ if self._fxaa_mode == FXAAMode.AlphaAsLuma:
+ self._framebuffer = Framebuffer.from_system(
+ use_depth=True, use_alpha = True)
+ ppfx = PPFX('ppfx/fxaa3_11')
+ else:
+ self._framebuffer = Framebuffer.from_system(
+ use_depth=True)
+ ppfx = PPFX('ppfx/blit')
+ self._framebuffer.name = 'root'
+
+ ppfx.name = 'root'
+ self.set_root_ppfx(ppfx)
+
+ def init_viewport(self):
+ self._viewport = Viewport()
+
+ if Config().stereo_mode == StereoMode.OCULUS_RIFT:
+ self._add_oculus_rift_ppfx()
+ else:
+ self._add_default_ppfx()
+
+ stereo_mode = Config().stereo_mode
+ if stereo_mode != StereoMode.OFF:
+ x,y,w,h = self._viewport.rect.to_tuple()
+ w2 = w*0.5
+
+ vpL = Viewport()
+ vpL.rect = vec4(x,y,w2,h)
+ vpL.eye_id = -1
+
+ vpR = Viewport()
+ vpR.rect = vec4(x+w2,y,w2,h)
+ vpR.eye_id = 1
+
+ self._render_viewports = (vpL, vpR)
+ else:
+ self._render_viewports = (self._viewport,)
+
+ def on_resized(self, size):
+ # update engines root viewport
+ pass
+
+ def write_screen_capture(self, filepath):
+ log.info("Writing {}...".format(filepath))
+ system = System()
+ w,h = system.screensize.to_tuple()
+ w -= w % 4
+ h -= h % 4
+ data = Framebuffer.read_screen_pixels(ivec4(0,0,w,h))
+ comp = 3
+ stbi_flip_y(w, h, comp, data)
+ stbi_write_png(filepath, w, h, comp, data)
+
+ def make_screenshot(self):
+ basepath = Config().base_path
+ i = 0
+ basename = time.strftime('screenshot_%Y-%m-%d_%H-%M_')
+ while i < 9999:
+ i += 1
+ filepath = os.path.join(basepath, basename + '{:04d}.png'.format(i))
+ if not os.path.exists(filepath):
+ break
+ self.write_screen_capture(filepath)
+
+ def make_batchgraphviz(self):
+ basepath = Config().base_path
+ i = 0
+ basename = time.strftime('batchgraph_%Y-%m-%d_%H-%M_')
+ while i < 9999:
+ i += 1
+ filepath = os.path.join(basepath, basename + '{:04d}.png'.format(i))
+ if not os.path.exists(filepath):
+ break
+ self.batch.dump_graphviz(filepath)
+
+ def invalidate_framebuffer_order(self):
+ self._render_ctx.batch.invalidate_framebuffer_order()
+
+ def frame_rendered(self):
+ self.frames_rendered += 1
+ timestamp = time.time()
+ if (timestamp - self.last_timestamp) > self.FPS_MEASURE_TIME:
+ fps = self.frames_rendered / self.FPS_MEASURE_TIME
+ self.last_timestamp = timestamp
+ self.frames_rendered = 0
+ self.frame_rate = fps
+ TimeManager().measured_framerate = fps
+ if Config().print_fps:
+ log.info("%.1f FPS" % (fps,))
+ #FrameProfiler().print_profile()
+ self.event_frame_rendered()
+
+ def debug_depth_texture(self, tex):
+ from .ppfx import PPFX
+ ppfx = PPFX('ppfx_debug_depth')
+ ppfx.name = 'debug-ppfx-' + tex.name
+ ppfx.add_shader_texture('fb_color0', tex)
+ self.add_debug_ppfx(ppfx)
+
+ def debug_layer_depth_texture(self, tex):
+ from .ppfx import PPFX
+ ppfx = PPFX('ppfx/debug_layer_depth')
+ ppfx.name = 'debug-ppfx-' + tex.name
+ ppfx.add_shader_texture('fb_color0', tex)
+ ppfx.set_uniform_values(
+ layers = tex.layers
+ )
+ self.add_debug_ppfx(ppfx)
+
+ def debug_layer_color_texture(self, tex):
+ from .ppfx import PPFX
+ ppfx = PPFX('ppfx/debug_layer_color')
+ ppfx.name = 'debug-ppfx-' + tex.name
+ ppfx.add_shader_texture('fb_color0', tex)
+ ppfx.set_uniform_values(
+ layers = tex.layers
+ )
+ self.add_debug_ppfx(ppfx)
+
+ def debug_normal_texture(self, tex):
+ from .ppfx import PPFX
+ ppfx = PPFX('ppfx/debug_normal')
+ ppfx.name = 'debug-ppfx-' + tex.name
+ ppfx.add_shader_texture('fb_color0', tex)
+ self.add_debug_ppfx(ppfx)
+
+ def debug_velocity_texture(self, tex):
+ from .ppfx import PPFX
+ ppfx = PPFX('ppfx/debug_velocity')
+ ppfx.name = 'debug-ppfx-' + tex.name
+ ppfx.add_shader_texture('fb_color0', tex)
+ self.add_debug_ppfx(ppfx)
+
+ def debug_color_texture(self, tex, **kargs):
+ from .ppfx import PPFX
+ ppfx = PPFX('ppfx/debug_color')
+ ppfx.name = 'debug-ppfx-' + tex.name
+ ppfx.add_shader_texture('fb_color0', tex)
+ self.add_debug_ppfx(ppfx, **kargs)
+
+ def debug_ycocg_texture(self, tex):
+ from .ppfx import PPFX
+ ppfx = PPFX('ppfx/debug_ycocg')
+ ppfx.name = 'debug-ppfx-' + tex.name
+ ppfx.add_shader_texture('fb_color0', tex)
+ ppfx.uniforms.fb_size.i[0:2] = tex.size
+ self.add_debug_ppfx(ppfx)
+
+ def debug_framebuffer(self, fb):
+ for tex in fb.color_attachments:
+ self.debug_color_texture(tex)
+ if fb.depth_attachment:
+ self.debug_depth_texture(fb.depth_attachment)
+
+ def add_debug_ppfx(self, ppfx, rect=None):
+ TILES_X = 4
+ TILE_SIZE = 1.0 / TILES_X
+
+ x = (len(self._debug_textures) % TILES_X) * TILE_SIZE
+ y = (len(self._debug_textures) // TILES_X) * TILE_SIZE
+
+ vp = Viewport()
+ vp.name = 'debug-viewport'
+ if rect:
+ vp.rect = rect
+ else:
+ vp.rect = vec4(x,y,TILE_SIZE,TILE_SIZE)
+
+ self.framebuffer << RViewportNode(vp) << ppfx
+
+ #self._debug_textures.append((vp,ppfx))
+
+ def dump_frame(self):
+ self._batch.dump()
+
+ def render_debug_textures(self, ctx):
+ if not self._debug_textures:
+ return
+
+ for vp,ppfx in self._debug_textures:
+ ppfx_ctx = RenderContext(ctx)
+ rt_ppfx = ppfx_ctx.rt_root
+ rt_ppfx.viewport = vp
+ rt_ppfx.scenepass = 1<<47
+ ppfx.render(ppfx_ctx)
+
+ def invalidate_uniforms(self, actor):
+ assert actor is not None
+ self._invalid_uniforms.add(actor)
+
+ def update_uniforms(self):
+ invalid_uniforms = self._invalid_uniforms
+ self._invalid_uniforms = set()
+ for actor in invalid_uniforms:
+ actor.update_uniforms()
+
+ def render(self):
+ fp = FrameProfiler()
+
+ with fp.section('update_uniforms'):
+ self.update_uniforms()
+
+ tm = TimeManager()
+ with self._batch.global_attribs.map_write() as data:
+ data.time = tm.duration
+
+ self._batch.execute()
+
+ if self._show_profile:
+ fp.render_profile()
+
+ self.frame_rendered()
+
+################################################################################
A => liminal_legacy/liminal/engine/deferred.py +1423 -0
@@ 0,0 1,1423 @@
+#encoding: utf-8
+from __future__ import (print_function, division, absolute_import)
+
+import math
+
+from glue import callproxy, Singleton
+
+from gl import (glFenceSync, glClientWaitSync, glDeleteSync,
+ GL_SYNC_GPU_COMMANDS_COMPLETE, GL_CONDITION_SATISFIED,
+ GL_ALREADY_SIGNALED)
+from glm import vec2, vec3, vec4
+
+from .interface import Named, RNode
+from .texture import TextureWrap
+from .framebuffer import Framebuffer, FramebufferTexture2D
+from .shader import Program
+from .config import Config, StereoMode
+from .render import render_ffi, RClearNode
+from ..utils import clamp, Accum, f16_to_f32
+from .timing import TimeManager
+from .core import RenderManager
+from .config import VideoQuality
+from .pixelbuffer import PixelPackBuffer
+from .ppfx import PPFX, blur_pass_shader, PPFXResources
+from .material import Material
+from .actor import Actor
+from .camera import Camera
+
+################################################################################
+
+def Skybox():
+ mesh = PPFXResources().mesh
+ material = Material.from_files('g/skybox')
+ material.renderpass = 100
+ material.depth_test = True
+ material.depth_mask = False
+
+ material << mesh
+ return material
+
+################################################################################
+
+class GRenderManager(RNode):
+ __metaclass__ = Singleton
+
+ __slots__ = [
+ '_grading0',
+ '_grading1',
+ '_use_fog',
+ '_grading_mix',
+ '_framebuffer',
+ '_skybox',
+ '_scene_framebuffer',
+ '_use_motion_blur',
+ '_use_ssao',
+ '_ppfx_tonemap',
+ '_ppfx_gshader',
+ '_ppfx_ssao',
+ '_ppfx_mblur',
+ '_ppfx_bloom_mixdown',
+ '_ppfx_dof',
+ '_uv_distort_map',
+ '_uv_distort_scale',
+ '_uv_distort_absolute',
+ '_ibl_cubemap',
+ '_ssao_radius',
+ '_use_bloom',
+ '_use_dof',
+ '_bloom_grade',
+ '_exposure',
+ '_exposure_accum',
+ '_exposure_bias',
+ '_min_exposure',
+ '_max_exposure',
+ '_fb_luma1x1',
+ '_fb_ssao',
+ '_luma_scale',
+ '_luma_sync',
+ '_pbo_luma',
+ '_auto_exposure',
+ '_auto_exposure_frame',
+ '_exposure_adaption_up',
+ '_exposure_adaption_down',
+ '_pbo_dof',
+ '_dof_sync',
+ '_dof_focus',
+ '_dof_accum',
+ '_dof_range',
+ '_dof_fstop',
+ '_shadowmap',
+ '_light_buffer',
+ '_mblur_target_fps',
+ '_mblur_shutter',
+ '_scatter_range',
+ '_debug_ssao',
+ '_grease',
+ '_ambient_power',
+ ]
+
+ MIN_EXPOSURE = 5.0*10**-4.0 # 2.0**-4.0
+
+ def __init__(self):
+ RNode.__init__(self)
+ self.name = 'grenderman'
+ self._use_bloom = True
+ self._debug_ssao = False
+ self._bloom_grade = 1
+ self._use_motion_blur = True
+ self._scene_framebuffer = None
+ self._grading0 = None
+ self._grading1 = None
+ self._grading_mix = 0.0
+ self._ppfx_tonemap = None
+ self._ppfx_gshader = None
+ self._ppfx_ssao = None
+ self._ppfx_mblur = None
+ self._ppfx_dof = None
+ self._ppfx_bloom_mixdown = None
+ self._fb_ssao = None
+ self._fog_power = 0.0
+ self._fog_range = 100.0
+ self._fog_falloff_radius = 128.0
+ self._fog_thickness = 0.0
+ self._fog_color = vec3(0.3, 0.7, 1.0)
+ self._fog_map = None
+ self._fog_uv_scale_offset = vec4(1.0,1.0,0.0,0.0)
+ self._uv_distort_map = None
+ self._uv_distort_scale = vec2(1,1)
+ self._uv_distort_absolute = False
+ # how much bloom will be mixed in
+ self._grease = 1.0/8.0
+ self._ssao_radius = 0.001
+ self._exposure = 0.0 # in EV
+ self._auto_exposure = True
+ self._auto_exposure_frame = 0
+ self._exposure_adaption_up = 1.0 / 1.0
+ self._exposure_adaption_down = 1.0 / 0.3
+ self._exposure_accum = Accum(max_size=3)
+ self._exposure_bias = 0.0
+ self._fb_luma1x1 = None
+ self._min_exposure = -4.0
+ self._max_exposure = 4.0
+ self._ibl_cubemap = None
+ self._light_buffer = None
+ self._use_ssao = False
+ self._ambient_color = vec3(1.0,1.0,1.0)
+ self._ambient_power = 2.0**-5.0
+ self._use_dof = False
+ self._dof_range = vec2(0.1, 100.0)
+ self._dof_fstop = 3.2
+ self._luma_sync = None
+ self._use_fog = False
+ # big enough for a 2x1 or 1x2 RGBAF32 buffer
+ self._pbo_luma = PixelPackBuffer.from_size(2 * 1 * 4 * 4)
+ # 1x1 RGBAF32
+ self._pbo_dof = PixelPackBuffer.from_size(1 * 1 * 4 * 4)
+ self._dof_sync = None
+ self._scatter_range = None #(50.0, 100.0)
+ self._mblur_target_fps = 24.0
+ self._luma_scale = 1.0
+ self._dof_focus = 1.0
+ self._dof_accum = Accum(max_size=3)
+ # shutter is usually open only half of the time
+ # shutter time of 0.1: virtually no motion blur
+ # shutter time of 0.9: smooth motion
+ self._mblur_shutter = 0.5
+ self._skybox = None
+ self.use_quality_config()
+
+ @property
+ def skybox(self):
+ return self._skybox
+
+ @property
+ def use_fog(self):
+ return self._use_fog
+ @use_fog.setter
+ def use_fog(self, value):
+ self._use_fog = value
+
+ @property
+ def fog_map(self):
+ return self._fog_map
+ @fog_map.setter
+ def fog_map(self, value):
+ self._fog_map = value
+
+ @property
+ def fog_uv_scale_offset(self):
+ return self._fog_uv_scale_offset
+ @fog_uv_scale_offset.setter
+ def fog_uv_scale_offset(self, value):
+ self._fog_uv_scale_offset = value
+ self.update_gshader()
+
+ @property
+ def uv_distort_map(self):
+ return self._uv_distort_map
+ @uv_distort_map.setter
+ def uv_distort_map(self, value):
+ self._uv_distort_map = value
+
+ @property
+ def uv_distort_scale(self):
+ return self._uv_distort_scale
+ @uv_distort_scale.setter
+ def uv_distort_scale(self, value):
+ self._uv_distort_scale = value
+ self.update_tonemap()
+
+ @property
+ def uv_distort_absolute(self):
+ return self._uv_distort_absolute
+ @uv_distort_absolute.setter
+ def uv_distort_absolute(self, value):
+ self._uv_distort_absolute = value
+
+ @property
+ def fog_power(self):
+ return self._fog_power
+ @fog_power.setter
+ def fog_power(self, value):
+ self._fog_power = value
+ self.update_gshader()
+
+ @property
+ def fog_falloff_radius(self):
+ return self._fog_falloff_radius
+ @fog_falloff_radius.setter
+ def fog_falloff_radius(self, value):
+ self._fog_falloff_radius = value
+ self.update_gshader()
+
+ @property
+ def fog_color(self):
+ return self._fog_color
+ @fog_color.setter
+ def fog_color(self, value):
+ self._fog_color = value
+ self.update_gshader()
+
+ @property
+ def fog_range(self):
+ return self._fog_range
+ @fog_range.setter
+ def fog_range(self, value):
+ self._fog_range = value
+ self.update_gshader()
+
+ @property
+ def fog_thickness(self):
+ return self._fog_thickness
+ @fog_thickness.setter
+ def fog_thickness(self, value):
+ self._fog_thickness = value
+ self.update_gshader()
+
+ @property
+ def ambient_color(self):
+ return self._ambient_color
+ @ambient_color.setter
+ def ambient_color(self, value):
+ self._ambient_color = value
+ self.update_gshader()
+
+ @property
+ def use_dof(self):
+ return self._use_dof
+ @use_dof.setter
+ def use_dof(self, value):
+ self._use_dof = value
+
+ @property
+ def fb_ssao(self):
+ return self._fb_ssao
+
+ @property
+ def bloom_grade(self):
+ return self._bloom_grade
+ @bloom_grade.setter
+ def bloom_grade(self, value):
+ self._bloom_grade = value
+
+ @property
+ def debug_ssao(self):
+ return self._debug_ssao
+ @debug_ssao.setter
+ def debug_ssao(self, value):
+ self._debug_ssao = value
+
+ @property
+ def scatter_range(self):
+ return self._scatter_range
+ @scatter_range.setter
+ def scatter_range(self, value):
+ assert value is None or len(value) == 2
+ self._scatter_range = value
+
+ @property
+ def grease(self):
+ return self._grease
+ @grease.setter
+ def grease(self, value):
+ self._grease = value
+ self.update_bloom()
+
+ @property
+ def use_motion_blur(self):
+ return self._use_motion_blur
+ @use_motion_blur.setter
+ def use_motion_blur(self, value):
+ self._use_motion_blur = value
+
+ @property
+ def mblur_shutter(self):
+ return self._mblur_shutter
+ @mblur_shutter.setter
+ def mblur_shutter(self, value):
+ self._mblur_shutter = value
+
+ @property
+ def use_ssao(self):
+ return self._use_ssao
+ @use_ssao.setter
+ def use_ssao(self, value):
+ self._use_ssao = value
+
+ @property
+ def mblur_target_fps(self):
+ return self._mblur_target_fps
+ @mblur_target_fps.setter
+ def mblur_target_fps(self, value):
+ self._mblur_target_fps = value
+
+ @property
+ def scene_framebuffer(self):
+ return self._scene_framebuffer
+
+ @property
+ def light_buffer(self):
+ return self._light_buffer
+
+ @property
+ def ppfx_gshader(self):
+ return self._ppfx_gshader
+
+ @property
+ def depth_texture(self):
+ return self._depth_texture
+
+ @property
+ def ibl_cubemap(self):
+ return self._ibl_cubemap
+ @ibl_cubemap.setter
+ def ibl_cubemap(self, value):
+ self._ibl_cubemap = value
+
+ @property
+ def min_exposure(self):
+ return self._min_exposure
+ @min_exposure.setter
+ def min_exposure(self, value):
+ self._min_exposure = value
+
+ @property
+ def max_exposure(self):
+ return self._max_exposure
+ @max_exposure.setter
+ def max_exposure(self, value):
+ self._max_exposure = value
+
+ @property
+ def exposure_bias(self):
+ return self._exposure_bias
+ @exposure_bias.setter
+ def exposure_bias(self, value):
+ self._exposure_bias = value
+
+ @property
+ def exposure_adaption_up(self):
+ return self._exposure_adaption_up
+ @exposure_adaption_up.setter
+ def exposure_adaption_up(self, value):
+ self._exposure_adaption_up = value
+
+ @property
+ def exposure_adaption_down(self):
+ return self._exposure_adaption_down
+ @exposure_adaption_down.setter
+ def exposure_adaption_down(self, value):
+ self._exposure_adaption_down = value
+
+ @property
+ def auto_exposure(self):
+ return self._auto_exposure
+ @auto_exposure.setter
+ def auto_exposure(self, value):
+ self._auto_exposure = value
+
+ @property
+ def exposure(self):
+ return self._exposure
+ @exposure.setter
+ def exposure(self, value):
+ self._exposure = value
+ self.update_tonemap()
+
+ @property
+ def use_bloom(self):
+ return self._use_bloom
+ @use_bloom.setter
+ def use_bloom(self, value):
+ self._use_bloom = value
+
+ @property
+ def ssao_radius(self):
+ return self._ssao_radius
+ @ssao_radius.setter
+ def ssao_radius(self, value):
+ self._ssao_radius = value
+ self.update_ssao()
+
+ @property
+ def grading(self):
+ return self._grading0
+ @grading.setter
+ def grading(self, value):
+ self._grading0 = value
+ self._grading1 = None
+ self.grading_mix = 0.0
+
+ @property
+ def grading0(self):
+ return self._grading0
+ @grading0.setter
+ def grading0(self, value):
+ if value is self._grading0:
+ return
+ self._grading0 = value
+
+ @property
+ def grading1(self):
+ return self._grading1
+ @grading1.setter
+ def grading1(self, value):
+ if value is self._grading1:
+ return
+ self._grading1 = value
+
+ @property
+ def grading_mix(self):
+ return self._grading_mix
+ @grading_mix.setter
+ def grading_mix(self, value):
+ if value == self._grading_mix:
+ return
+ self._grading_mix = value
+ self.update_tonemap()
+
+ @property
+ def dof_range(self):
+ return self._dof_range
+ @dof_range.setter
+ def dof_range(self, value):
+ self._dof_range = value
+ self.update_dof_uniforms()
+
+ @property
+ def dof_fstop(self):
+ return self._dof_fstop
+ @dof_fstop.setter
+ def dof_fstop(self, value):
+ self._dof_fstop = value
+ self.update_dof_uniforms()
+
+ @property
+ def dof_focus(self):
+ return self._dof_focus
+ @dof_focus.setter
+ def dof_focus(self, value):
+ self._dof_focus = value
+ self.update_dof_uniforms()
+
+ @property
+ def ambient_power(self):
+ return self._ambient_power
+ @ambient_power.setter
+ def ambient_power(self, value):
+ self._ambient_power = value
+ self.update_gshader()
+
+ def update_bloom(self):
+ ppfx = self._ppfx_bloom_mixdown
+ if not ppfx:
+ return
+ ppfx.set_uniform_values(
+ mix_factor = self._grease
+ )
+
+ def update_dof_uniforms(self):
+ dof = self._ppfx_dof
+ if not dof:
+ return
+ uniforms = dof.uniforms
+ if hasattr(uniforms, 'focalDepth'):
+ uniforms.focalDepth = self._dof_focus
+ if hasattr(uniforms, 'focalDepthRange'):
+ uniforms.focalDepthRange = self._dof_range._ptr[0]
+ if hasattr(uniforms, 'fstop'):
+ uniforms.fstop = self._dof_fstop
+
+ def update_tonemap(self):
+ tonemap = self._ppfx_tonemap
+ if not tonemap:
+ return
+ uniforms = tonemap.uniforms
+ if hasattr(uniforms, 'exposure'):
+ uniforms.exposure = 2.0**self._exposure
+ if hasattr(uniforms, 'uv_lut_scale'):
+ uniforms.uv_lut_scale = self._uv_distort_scale._ptr[0]
+ if hasattr(uniforms, 'lut_grading_mix'):
+ uniforms.lut_grading_mix = self._grading_mix
+
+ def update_gshader(self):
+ ppfx = self._ppfx_gshader
+ if not ppfx:
+ return
+ ppfx.set_uniform_values(
+ ambient_power = self._ambient_power,
+ ambient_color = self._ambient_color.to_tuple())
+ if self._use_fog:
+ ppfx.set_uniform_values(
+ fog_power = self._fog_power,
+ fog_thickness = self._fog_thickness,
+ fog_range = self._fog_range,
+ fog_color = self._fog_color.to_tuple(),
+ fog_falloff_radius = self._fog_falloff_radius)
+ if self._fog_map:
+ ppfx.set_uniform_values(
+ fog_uv_scale_offset = self._fog_uv_scale_offset.to_tuple())
+
+ def update_ssao(self):
+ ssao = self._ppfx_ssao
+ if not ssao:
+ return
+ ssao.set_uniform_values(
+ radius = self._ssao_radius)
+
+ def update_uniforms(self):
+ pass
+
+ def update_all(self):
+ self.update_gshader()
+
+ def on_frame(self):
+ if self._auto_exposure:
+ self.update_exposure()
+ if self._use_dof:
+ self.update_dof()
+
+ tm = TimeManager()
+ rm = RenderManager()
+ if tm.adaptive:
+ frame_rate = rm.frame_rate
+ else:
+ frame_rate = tm.framerate
+
+ ppfx_mblur = self._ppfx_mblur
+ if ppfx_mblur:
+ SAMPLE_COUNT = 16
+ factor = (SAMPLE_COUNT-1) / float(SAMPLE_COUNT)
+
+ blur_scale = self._mblur_shutter * min(1.0,
+ frame_rate*factor / self._mblur_target_fps)
+ ppfx_mblur.set_uniform_value('blur_scale', blur_scale)
+
+ def use_quality_config(self):
+ quality = Config().videoquality
+
+ Y = True
+ N = False
+
+ self.use_motion_blur = [N,N,N,Y,Y,Y,Y][quality]
+ self.use_dof = [N,N,N,Y,Y,Y,Y][quality]
+ self.use_ssao = [N,N,N,Y,Y,Y,Y][quality]
+ self.use_bloom = [N,Y,Y,Y,Y,Y,Y][quality]
+ self.bloom_grade = [0,4,3,2,1,1,1][quality]
+
+ def update_dof(self):
+ tm = TimeManager()
+ if tm.frames < 10:
+ return
+
+ fb = self._scene_framebuffer
+ w,h = fb.size
+ pbo_dof = self._pbo_dof
+
+ sync = self._dof_sync
+ if sync is not None:
+ result = glClientWaitSync(sync, 0, 0)
+ if result == GL_CONDITION_SATISFIED or result == GL_ALREADY_SIGNALED:
+ glDeleteSync(sync)
+ sync = None
+ with pbo_dof.map_read() as data:
+ fdata = render_ffi.cast('unsigned int*',data)
+ depth32 = fdata[0]
+ # assuming 24bit depth 8bit stencil
+ depth = (depth32 >> 8) / 0xffffff
+ self._dof_accum.push(depth)
+ if sync is None:
+ # write
+ fb.read_pixels_from_depth(pbo_dof, (w//2,h//2,1,1))
+ self._dof_sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0)
+
+ if not self._dof_accum.values:
+ return
+ depth = self._dof_accum.average()
+
+ td = TimeManager().timefactor(10.0)
+ focus = self._dof_focus
+ self.dof_focus = focus + (depth - focus)*td
+
+ def update_exposure(self):
+ if not self._fb_luma1x1:
+ return
+
+ self._auto_exposure_frame += 1
+ if self._auto_exposure_frame < 10:
+ return
+
+ fb_luma1x1 = self._fb_luma1x1
+ pbo_luma = self._pbo_luma
+
+ sync = self._luma_sync
+ if sync is not None:
+ result = glClientWaitSync(sync, 0, 0)
+ if result == GL_CONDITION_SATISFIED or result == GL_ALREADY_SIGNALED:
+ glDeleteSync(sync)
+ sync = None
+ self._luma_sync = None
+ fbsize = fb_luma1x1.size
+ count = fbsize[0] * fbsize[1]
+ assert count <= 2
+ with pbo_luma.map_read() as data:
+ fdata = render_ffi.cast('float*',data)
+ col = vec3(*fdata[0:3])
+ if count > 1:
+ col = col.add(vec3(*fdata[4:7])).div_f(2)
+
+ col = vec3(0.299, 0.587, 0.114).dot(col) * self._luma_scale
+
+ minv = self.MIN_EXPOSURE
+ luma = max(col, minv)
+ blevel = math.log(luma) / math.log(2.0)
+ blevel = clamp(blevel-self._exposure_bias, self._min_exposure, self._max_exposure)
+
+ self._exposure_accum.push(blevel)
+ if sync is None:
+ # write
+ fb_luma1x1.read_pixels(pbo_luma)
+ self._luma_sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0)
+
+ if not self._exposure_accum.values:
+ return
+ blevel = self._exposure_accum.average()
+
+ target = clamp(-blevel+self._exposure_bias, self._min_exposure, self._max_exposure)
+
+ old_exp = int(self.exposure*100.0)
+
+ td = TimeManager().timedelta
+ if self.exposure < target:
+ self.exposure = min(self.exposure + self._exposure_adaption_up*td, target)
+ else:
+ self.exposure = max(self.exposure - self._exposure_adaption_down*td, target)
+
+ new_exp = int(self.exposure*100.0)
+ if 0 and old_exp != new_exp:
+ print(self.exposure, target)
+
+ def ensure_passes(self):
+ if self._scene_framebuffer:
+ return
+ self.create_passes()
+
+ if 0:
+ def inject_camera(self, camera):
+ assert self not in camera.ppfx_group.passes, \
+ "already assigned to camera"
+ self.ensure_passes()
+ self.framebuffer = camera.framebuffer
+ camera.framebuffer = self.scene_framebuffer
+ camera.light_buffer = self.light_buffer
+ camera.clear_stencil = True
+ camera.ppfx_group.add_pass(self)
+
+ def _create_ssao_pass(self, p0, normal_texture, depth_texture):
+ fw,fh = p0.size
+
+ if Config().videoquality < VideoQuality.HIGH:
+ ssao_blur_size = fw//2,fh//2
+ ssao_size = fw//2,fh//2
+ else:
+ ssao_size = ssao_blur_size = fw,fh
+
+ # SSAO pass
+ fb_ssao = Framebuffer()
+ fb_ssao.name = 'fb-ssao'
+ ssao_texture = FramebufferTexture2D(*ssao_size)
+ ssao_texture.name = 'ssao'
+ ssao_texture.use_r8()
+ ssao_texture.bilinear = True
+ fb_ssao.add_color_attachment(ssao_texture)
+
+ #if 1:
+ # RenderManager().debug_color_texture(ssao_texture)
+
+ ppfx = PPFX('g/ppfx/ssao')
+ ppfx.name = 'ppfx-ssao'
+ ppfx.add_shader_texture('fb_color2', normal_texture)
+ ppfx.add_shader_texture('fb_depth', depth_texture)
+ self._ppfx_ssao = ppfx
+
+ tw,th = depth_texture.size
+ ppfx.set_uniform_values(
+ fb_size = (tw,th),
+ fb_texel = (1.0/tw,1.0/th),
+ radius = self._ssao_radius)
+
+ self << fb_ssao << ppfx
+
+ SSAO_BLUR = True
+
+ if SSAO_BLUR:
+ fb_ssao_blur = Framebuffer()
+ fb_ssao_blur.name = 'fb-ssao-blur'
+ fb_ssao_blur_texture = FramebufferTexture2D(*ssao_blur_size)
+ fb_ssao_blur_texture.name = 'ssao-blur'
+ fb_ssao_blur_texture.use_r8()
+ fb_ssao_blur_texture.bilinear = True
+ fb_ssao_blur.add_color_attachment(fb_ssao_blur_texture)
+
+ ppfx = PPFX('ppfx/blur',defines=dict(
+ KERNEL_RADIUS = 3))
+ ppfx.add_framebuffer_textures(fb_ssao)
+ ppfx.name = 'ppfx-ssao-blur'
+
+ self << fb_ssao_blur << ppfx
+
+ return fb_ssao_blur
+ else:
+ return fb_ssao
+
+ def _create_dof_mblur_pass(self, p0, depth_texture, velocity_texture):
+ fw,fh = p0.size
+
+ size = fw,fh
+
+ # dof pass
+ fb = Framebuffer()
+ fb.name = 'fb-dof-mblur'
+ dof_texture = FramebufferTexture2D(*size)
+ dof_texture.name = 'dof-mblur'
+ dof_texture.use_rgb16F()
+ dof_texture.bilinear = True
+ dof_texture.wrap = TextureWrap.ClampToBorder
+ fb.add_color_attachment(dof_texture)
+
+ use_mblur = self._use_motion_blur and velocity_texture is not None
+ use_dof = self._use_dof and depth_texture is not None
+
+ ppfx = PPFX('g/ppfx/dof-mblur', defines=dict(
+ USE_MBLUR = use_mblur,
+ USE_DOF = use_dof
+ ))
+ ppfx.name = 'ppfx-dof-mblur'
+ ppfx.add_framebuffer_textures(p0)
+ if use_mblur:
+ ppfx.add_shader_texture('depth_texture',
+ depth_texture)
+ self._ppfx_mblur = ppfx
+ if use_dof:
+ ppfx.add_shader_texture('tex_velocity', velocity_texture)
+ self._ppfx_dof = ppfx
+
+ self << fb << ppfx
+
+ return fb
+
+ def _create_ssgi_pass(self, p0, pass_id):
+ fw,fh = p0.size
+
+ ssgi_size = fw,fh
+
+ # ssgi pass
+ fb_ssgi = Framebuffer()
+ fb_ssgi.name = 'fb-ssgi'
+ ssgi_texture = FramebufferTexture2D(*ssgi_size)
+ ssgi_texture.name = 'ssgi'
+ ssgi_texture.use_rgb16F()
+ ssgi_texture.bilinear = True
+ ssgi_texture.wrap = TextureWrap.ClampToBorder
+ fb_ssgi.add_color_attachment(ssgi_texture)
+
+ ppfx = PPFX('g/ppfx/ssgi', defines = dict(
+ SSGI_PASS_ID = pass_id))
+ ppfx.name = 'ppfx-ssgi'
+ ppfx.add_framebuffer_textures(self._scene_framebuffer)
+ ppfx.add_framebuffer_textures(p0, 'combined')
+ #self._ppfx_ssgi = ppfx
+
+ ppfx.material.set_uniform_values(
+ radius = self._ssao_radius)
+
+ self << fb_ssgi << ppfx
+
+ return fb_ssgi
+
+ def _create_exposure_pass(self, fb, level_count):
+ self._fb_luma1x1 = fb
+ self._luma_scale = level_count-1
+
+ def _create_bloom_pass(self, p0, depth_texture):
+ pO = p0
+ tx,ty = p0.size
+
+ bloom_grade = self._bloom_grade
+
+ # pow2 ceiling resolution
+ f2x = 1<<(tx-1).bit_length()
+ f2y = 1<<(ty-1).bit_length()
+
+ def make_bloom_fb(w, h, linear=True):
+ fb = Framebuffer()
+ fb.name = 'bloom-{}'.format(max(w,h))
+ color_texture = FramebufferTexture2D(w, h)
+ color_texture.name = 'bloom-{}'.format(max(w,h))
+ if min(w,h) == 1:
+ color_texture.use_rgb32F()
+ else:
+ color_texture.use_rgb16F()
+ color_texture.wrap = TextureWrap.ClampToEdge
+ color_texture.bilinear = linear
+ fb.add_color_attachment(color_texture)
+ return fb
+
+ blur_shader_upscale = Program.from_files('ppfx/blur',
+ defines = dict(KERNEL_RADIUS=0, SCALE_UV=True))
+ blur_shader_down = Program.from_files('ppfx/blur',
+ defines = dict(KERNEL_RADIUS=2, DOWNSCALE=True))
+
+ w,h = f2x,f2y
+ w >>= bloom_grade
+ h >>= bloom_grade
+
+ if tx != f2x or ty != f2y:
+ # scale to pow^2 texture
+ ppfx = PPFX(shader=blur_shader_upscale)
+ ppfx.set_uniform_values(
+ aspect = (f2x / tx, f2y / ty)
+ )
+ ppfx.add_framebuffer_textures(p0)
+ p0 = make_bloom_fb(w,h)
+ p0.name = 'bloom-upscale-pow2'
+
+ self << p0 << ppfx
+
+ textures = []
+
+ # scale down until we hit minimum image size
+ while w > 1 and h > 1:
+ # to properly sample 4x4 kernel
+ size = w,h
+ w >>= 1
+ h >>= 1
+ ppfx = PPFX(shader=blur_shader_down)
+ ppfx.add_framebuffer_textures(p0, size=size)
+ p0 = make_bloom_fb(w,h)
+ textures.append(p0)
+
+ self << p0 << ppfx
+
+ if self._auto_exposure: # use last texture as luma source
+ self._create_exposure_pass(textures[-1], len(textures))
+
+ blur_shader_up = Program.from_files('ppfx/blur',
+ defines = dict(KERNEL_RADIUS=3, BLUR_ADD=True))
+ blur_shader_up_final = Program.from_files('ppfx/blur',
+ defines = dict(KERNEL_RADIUS=3, BLUR_ADD=True,
+ SCALE_COLOR = 1.0 / len(textures),
+ SCALE_UV=True))
+
+ p0 = textures.pop()
+ # scale up and add up textures
+ while textures:
+ p0_next = textures.pop()
+ # to properly sample 4x4 kernel
+ last_size = w,h
+ w <<= 1
+ h <<= 1
+ last_pass = not textures
+ if last_pass:
+ ppfx = PPFX(shader=blur_shader_up_final)
+ ppfx.set_uniform_values(
+ aspect = (tx / f2x, ty / f2y),
+ mix_factor = self._grease,
+ depth_range = self._scatter_range
+ )
+ ppfx.add_shader_texture('depth_texture',
+ depth_texture)
+ else:
+ ppfx = PPFX(shader=blur_shader_up)
+ ppfx.add_framebuffer_textures(p0, size=last_size)
+ assert p0_next.size == (w,h)
+ ppfx.add_framebuffer_textures(p0_next, 'fb_add')
+ p0 = make_bloom_fb(w,h)
+ self << p0 << ppfx
+
+ if last_pass:
+ break
+
+ ppfx = PPFX('ppfx/bloom_mixdown',
+ defines = dict(
+ SCALE_WITH_DEPTH = True if self._scatter_range else False
+ ))
+ self._ppfx_bloom_mixdown = ppfx
+ ppfx.set_uniform_values(
+ mix_factor = self._grease
+ )
+ if self._scatter_range:
+ ppfx.set_uniform_values(
+ depth_range = self._scatter_range
+ )
+ ppfx.add_framebuffer_textures(pO)
+ ppfx.add_framebuffer_textures(p0, 'bloom')
+ ppfx.add_shader_texture('depth_texture',
+ depth_texture)
+ p0 = Framebuffer()
+ p0.name = 'fb-bloom-mixdown'
+ mixdown_texture = FramebufferTexture2D(*pO.size)
+ mixdown_texture.name = 'motion-blurred'
+ mixdown_texture.use_rgb16F()
+ mixdown_texture.bilinear = True
+ mixdown_texture.wrap = TextureWrap.ClampToEdge
+ p0.add_color_attachment(mixdown_texture)
+
+ self << p0 << ppfx
+
+ return p0
+
+ def create_passes(self):
+ assert not self._scene_framebuffer, "passes already created"
+
+ xdir = vec2(1,0)
+ ydir = vec2(0,1)
+
+ blur_shaders = {}
+ def get_blur_shader(*args, **kargs):
+ key = (args,tuple(kargs.items()))
+ shader = blur_shaders.get(key, None)
+ if shader is None:
+ shader = blur_pass_shader(*args, **kargs)
+ blur_shaders[key] = shader
+ return shader
+
+ from .core import RenderManager
+ rm = RenderManager()
+ fb_template = rm.framebuffer
+
+ ppfx = None
+
+ #
+ # short summary description of deferred renderer:
+ #
+ # GBuffer layout (24 bytes / pixel):
+ # | | | |
+ # A -> 8: Albedo R | 8: Albedo G | 8: Albedo B | 8: AO |
+ # E -> 8: Roughness| 8: Metallic |
+ # B -> 16F:Normal U | 16F: Normal V
+ # C -> 16F: Light R | 16F: Light G | 16F: Light B | 16F: Light A
+ # D -> 8I: Vel. X | 8I: Vel. Y
+ # X -> 24F: Depth | 8: Stencil
+ #
+ # 0) each light configures its own (VSM) shadow map target if applicable;
+ # enabled objects will render VBO into targets with special material.
+ #
+ # a) scene rasterizer writes to A, B, D, X
+ # additive and emissive materials additionally write to C
+ #
+ # b) lights read A, B, X, shadowmap, write diff+spec to C;
+ # albedo is premultiplied.
+ #
+ # c) SSAO shader reads B, X, writes to R8 target. also, 5x5 separable box filter
+ # is applied to result, no edge detection for now.
+ #
+ # d) mixdown shader reads and combines A, B, C, X, ssao, writes to final RGB16F target
+ # also does diff+spec IBL / ambient lighting from cubemap,
+ # and, inexplicably, fogging.
+ #
+ # e) motion blur shader reads D, does 8 fetches on mixdown,
+ # writes motion blurred mixdown.
+ #
+ # f) multi-tap blur does 3x3 separated gauss on mb mixdown,
+ # from 1/2 down to 1/128 sized rendertargets; then upsamples
+ # back from 1/64 to 1/1 (again 3x3 sep. gauss), adding all resolutions
+ # together. all this is done in RGB16F and has no visible banding.
+ #
+ # g) tonemap shader combines (mb mixdown + blur texture)/2,
+ # applies filmic, clamp, gamma 2.2, 32^3 LUT color grading
+ # (i end up using a passthrough bc the colors already look great),
+ # calcs luma, dithers 12bit to 8bit (4x4 bayer), then stores
+ # RGB8 + luma in alpha
+ #
+ # h) high quality FXAA reads RGBA8, writes antialiased texture.
+ #
+
+ fw,fh = fb_template.size
+ p0 = Framebuffer()
+ # albedo R,G,B, AO
+ albedo_texture = FramebufferTexture2D(fw, fh)
+ albedo_texture.name = 'gbuffer-albedo-uvlight'
+ albedo_texture.use_rgba8()
+ albedo_texture.bilinear = False
+ albedo_texture.wrap = TextureWrap.MirroredRepeat
+ p0.add_color_attachment(albedo_texture)
+
+ # roughness, metallic
+ matid_texture = FramebufferTexture2D(fw, fh)
+ matid_texture.name = 'material-fx'
+ matid_texture.wrap = TextureWrap.MirroredRepeat
+ matid_texture.use_rg8()
+ matid_texture.bilinear = False
+ p0.add_color_attachment(matid_texture)
+
+ # rg = normal x/y
+ normal_texture = FramebufferTexture2D(fw, fh)
+ normal_texture.name = 'gbuffer-normal'
+ normal_texture.wrap = TextureWrap.ClampToEdge
+ normal_texture.use_rg16()
+ normal_texture.bilinear = False
+ p0.add_color_attachment(normal_texture)
+
+ light_texture = FramebufferTexture2D(fw, fh)
+ light_texture.name = 'gbuffer-light'
+ light_texture.use_rgba16F()
+ light_texture.bilinear = False
+ p0.add_color_attachment(light_texture)
+ #rm = RenderManager()
+ #rm.debug_color_texture(light_texture)
+
+ if self._use_motion_blur:
+ # rg = velocity x/y
+ velocity_texture = FramebufferTexture2D(fw, fh)
+ velocity_texture.name = 'gbuffer-velocity'
+ velocity_texture.use_rg8i()
+ velocity_texture.bilinear = False
+ p0.add_color_attachment(velocity_texture)
+ else:
+ velocity_texture = None
+
+ depth_texture = FramebufferTexture2D(fw, fh)
+ depth_texture.name = 'gbuffer-depth'
+ depth_texture.use_depth24_stencil8()
+ p0.depth_attachment = depth_texture
+
+ p0.name = 'fb-scene'
+ self._scene_framebuffer = p0
+
+ self._skybox = Skybox()
+ p0 << self._skybox
+
+ rm = RenderManager()
+
+ DEPTH_TEXTURE = depth_texture
+
+ if 1:
+ #rm.debug_normal_texture(normal_texture)
+ #rm.debug_ycocg_texture(albedo_texture)
+ #rm.debug_color_texture(albedo_texture)
+ #rm.debug_color_texture(matid_texture)
+ #rm.debug_color_texture(light_texture)
+ #rm.debug_color_texture(fx_texture)
+ #rm.debug_depth_texture(depth_texture)
+ #rm.debug_depth_texture(depth_texture)
+ #rm.debug_velocity_texture(velocity_texture)
+ pass
+
+ node_clear = RClearNode()
+ node_clear.stencil = True
+ node_clear.name = 'clear-scene-fb'
+ self << self.scene_framebuffer << node_clear
+
+ if self._use_ssao:
+ fb_ssao = self._create_ssao_pass(p0, normal_texture, depth_texture)
+ self._fb_ssao = fb_ssao
+ else:
+ fb_ssao = None
+
+ fb_light = Framebuffer()
+ fb_light.name = 'fb-light'
+ fb_light.add_color_attachment(light_texture)
+ fb_light.depth_attachment = depth_texture
+ self._light_buffer = fb_light
+ if 0:
+ rm.debug_color_texture(fb_light.color_attachments[0])
+ self << fb_light
+
+ gs_defines = {}
+ if fb_ssao:
+ gs_defines['USE_SSAO'] = True
+ gs_defines['DEBUG_SSAO'] = self._debug_ssao
+ if self._use_fog:
+ gs_defines['USE_FOG'] = True
+ if self._fog_map:
+ gs_defines['USE_FOG_TEXTURE'] = True
+ if self._ibl_cubemap:
+ gs_defines['USE_IBL'] = True
+ ppfx = PPFX('g/ppfx/gshader',defines=gs_defines)
+ ppfx.name = 'ppfx-gshader'
+ ppfx.add_framebuffer_textures(p0)
+ self._ppfx_gshader = ppfx
+ if self._ibl_cubemap:
+ ppfx.add_shader_texture('ibl_cubemap', self._ibl_cubemap)
+ if self._use_fog and self._fog_map:
+ ppfx.add_shader_texture('fog_map', self._fog_map)
+ if fb_ssao:
+ ppfx.add_framebuffer_textures(fb_ssao, basename = 'ssao')
+
+ p0 = Framebuffer.from_template(fb_template, support_hdr=True)
+ p0.color_attachments[0].bilinear = True
+ p0.color_attachments[0].wrap = TextureWrap.ClampToBorder
+ p0.name = ppfx.name
+
+ self << p0 << ppfx
+
+ if 0:
+ p0 = self._create_ssgi_pass(p0, 0.0)
+
+ if Config().stereo_mode != StereoMode.OCULUS_RIFT:
+ if self._use_dof or self._use_motion_blur:
+ p0.color_attachments[0].wrap = TextureWrap.ClampToEdge
+ p0 = self._create_dof_mblur_pass(p0,
+ DEPTH_TEXTURE, velocity_texture)
+
+ if self._use_bloom:
+ p0 = self._create_bloom_pass(p0, DEPTH_TEXTURE)
+
+ TimeManager().event_frame.add(callproxy(self.on_frame))
+
+ ppfx = PPFX('ppfx/tonemap', defines=dict(
+ USE_GRADING = self._grading0 is not None,
+ ANIMATE_GRADING = self._grading1 is not None,
+ USE_UV_LUT = self._uv_distort_map is not None,
+ UV_LUT_ABSOLUTE = bool(self._uv_distort_absolute)
+ ))
+ self._ppfx_tonemap = ppfx
+ ppfx.add_framebuffer_textures(p0)
+ if self._grading0:
+ ppfx.add_shader_texture('lut_grading0', self._grading0)
+ if self._grading1:
+ ppfx.add_shader_texture('lut_grading1', self._grading1)
+ if self._uv_distort_map:
+ ppfx.add_shader_texture('uv_lut', self._uv_distort_map)
+
+ fbvp = RenderManager().framebuffer_viewport
+ fbvp << ppfx
+
+ self.update_all()
+
+################################################################################
+
+def try_assign(data, attr, value):
+ if not hasattr(data, attr):
+ return
+ setattr(data, attr, value)
+
+# material for deferred rendering, uses GShader
+class GMaterial(Material):
+ #gshaders = weakref.WeakValueDictionary()
+
+ DEFAULT_SOURCE = 'g/gshader'
+
+ __slots__ = [
+ '_albedo',
+ '_emissive_color',
+ '_emissive',
+ '_roughness_factor',
+ '_roughness_bias',
+ '_metallic_factor',
+ '_gmat_attribs',
+ ]
+
+ def __init__(self, shader):
+ Material.__init__(self, shader)
+ self._albedo = vec3(1,1,1)
+ self._emissive_color = vec3(1,1,1)
+ self._emissive = 0.0
+ self._roughness_factor = 1.0
+ self._roughness_bias = 0.0
+ self._metallic_factor = 1.0
+ self._gmat_attribs = self.new_uniform_buffer('GMaterialAttribs')
+
+ @classmethod
+ def new(cls, source_name=None, **kargs):
+ if source_name is None:
+ source_name = cls.DEFAULT_SOURCE
+ particles = kargs.pop('particles',False)
+ gamma2 = kargs.pop('gamma2',False)
+ additive = kargs.pop('additive',False)
+ billboard = kargs.pop('billboard',False)
+ quilting = kargs.pop('quilting',False)
+ fluorescent = kargs.pop('fluorescent',False)
+ discard_albedo_alpha = kargs.pop('discard_albedo_alpha',False)
+ discard_glow_alpha = kargs.pop('discard_glow_alpha',False)
+ albedo_map = kargs.pop('albedo_map',None)
+ vertex_color = kargs.pop('vertex_color',False)
+ gloss_map = kargs.pop('gloss_map',None)
+ glow_map = kargs.pop('glow_map',None)
+ derivative_map = kargs.pop('derivative_map', False)
+ height_map = kargs.pop('height_map',None)
+ height_map_factor = kargs.pop('height_map_factor',None)
+ shadow_light_source = kargs.pop('shadow_light_source',None)
+ if Config().videoquality < VideoQuality.HIGH:
+ height_map = None
+ use_backface_normals = kargs.pop('use_backface_normals', False)
+ defines = kargs.pop('defines',{})
+ if gamma2:
+ defines['USE_GAMMA2'] = True
+ if particles:
+ defines['USE_PARTICLES'] = True
+ if vertex_color:
+ defines['USE_VERTEX_COLOR'] = True
+ if use_backface_normals:
+ defines['USE_BACKFACE_NORMALS'] = True
+ if discard_albedo_alpha:
+ defines['DISCARD_ALBEDO_ALPHA'] = True
+ if discard_glow_alpha:
+ defines['DISCARD_GLOW_ALPHA'] = True
+ if additive:
+ defines['USE_ADDITIVE'] = True
+ if quilting:
+ defines['USE_QUILTING'] = True
+ if billboard:
+ defines['USE_BILLBOARD'] = True
+ from .texture import ImageTexture2DArray
+ if height_map is not None:
+ defines['USE_HEIGHT_MAP'] = True
+ if derivative_map:
+ defines['USE_DERIVATIVE_MAP'] = True
+ if height_map_factor is not None:
+ defines['HEIGHT_MAP_FACTOR'] = height_map_factor
+ if isinstance(height_map, ImageTexture2DArray):
+ defines['USE_TEXTURE_ARRAY'] = True
+ if gloss_map is not None:
+ defines['USE_GLOSS_MAP'] = True
+ if isinstance(gloss_map, ImageTexture2DArray):
+ defines['USE_TEXTURE_ARRAY'] = True
+ if glow_map is not None:
+ defines['USE_GLOW_MAP'] = True
+ if isinstance(glow_map, ImageTexture2DArray):
+ defines['USE_TEXTURE_ARRAY'] = True
+ if albedo_map is not None:
+ defines['USE_ALBEDO_MAP'] = True
+ if isinstance(albedo_map, ImageTexture2DArray):
+ defines['USE_TEXTURE_ARRAY'] = True
+ kargs['defines'] = defines
+ material = cls.from_files(source_name, **kargs)
+ material.use_backface_culling = not use_backface_normals
+ if additive:
+ material.set_additive()
+ material.renderpass = 200 # must be after skybox
+ shadow_material = None
+ if shadow_light_source:
+ defines = dict(defines)
+ defines.update(shadow_light_source.get_shadow_material_defines())
+ defines['SHADOW_MATERIAL'] = True
+ kargs['defines'] = defines
+ kargs['use_geometry'] = True
+ shadow_material = Material.from_files(source_name, **kargs)
+ shadow_light_source.add_shadow_material(shadow_material)
+ material.use_shadow = True
+ material.shadow_material = shadow_material
+ shadow_material.use_backface_culling = not use_backface_normals
+
+ if albedo_map is not None:
+ material.add_shader_texture('albedo_map', albedo_map)
+ if shadow_material and discard_albedo_alpha:
+ shadow_material.add_shader_texture('albedo_map', albedo_map)
+
+ if height_map is not None:
+ material.add_shader_texture('height_map', height_map)
+ if gloss_map is not None:
+ material.add_shader_texture('gloss_map', gloss_map)
+ if glow_map is not None:
+ material.add_shader_texture('glow_map', glow_map)
+ if shadow_material and discard_glow_alpha:
+ shadow_material.add_shader_texture('glow_map', glow_map)
+
+ return material
+
+
+ def invalidate_uniforms(self):
+ RenderManager().invalidate_uniforms(self)
+
+ def update_uniforms(self):
+ attribs = self._gmat_attribs
+ with attribs.map_write() as data:
+ data.albedo = self._albedo._ptr[0]
+ data.emissive_color = self._emissive_color._ptr[0]
+ data.emissive = self._emissive
+ data.roughness_bias = self._roughness_bias
+ data.roughness_factor = self._roughness_factor
+ data.metallic_factor = self._metallic_factor
+
+ def ensure_compiled(self):
+ Material.ensure_compiled(self)
+ self.update_uniforms()
+
+ @property
+ def emissive_color(self):
+ return self._emissive_color
+ @emissive_color.setter
+ def emissive_color(self, value):
+ self._emissive_color = value
+ self.invalidate_uniforms()
+
+ @property
+ def albedo(self):
+ return self._albedo
+ @albedo.setter
+ def albedo(self, value):
+ self._albedo = value
+ self.invalidate_uniforms()
+
+ @property
+ def emissive(self):
+ return self._emissive
+ @emissive.setter
+ def emissive(self, value):
+ self._emissive = value
+ self.invalidate_uniforms()
+
+ @property
+ def roughness_bias(self):
+ return self._roughness_bias
+ @roughness_bias.setter
+ def roughness_bias(self, value):
+ self._roughness_bias = value
+ self.invalidate_uniforms()
+
+ @property
+ def roughness_factor(self):
+ return self._roughness_factor
+ @roughness_factor.setter
+ def roughness_factor(self, value):
+ self._roughness_factor = value
+ self.invalidate_uniforms()
+
+ @property
+ def metallic_factor(self):
+ return self._metallic_factor
+ @metallic_factor.setter
+ def metallic_factor(self, value):
+ self._metallic_factor = value
+ self.invalidate_uniforms()
+
+################################################################################
+
+class GMaterialSDF(GMaterial):
+ DEFAULT_SOURCE = 'g/gshader_sdf'
+
+ __slots__ = [
+ '_camera',
+ ]
+
+ def __init__(self, shader):
+ GMaterial.__init__(self, shader)
+ self._camera = None
+
+ @property
+ def camera(self):
+ return self._camera
+ @camera.setter
+ def camera(self, value):
+ self._camera = value
+
+ def update_uniforms(self):
+ GMaterial.update_uniforms(self)
+ from .system import System
+ w = System().screensize.x
+ camera = self._camera
+ fov = camera.fov if self._camera else 90.0
+ f = (2.0**0.5) * 0.5 # fit square into circle
+ texel_size = math.tan(math.radians(fov)/2.0) * f / w
+ self.set_uniform_values(
+ texel_size = texel_size
+ )
+
+################################################################################
+
+if 0:
+ class GRNode(RNode):
+ __slots__ = [
+ 'skybox',
+ 'hdrm',
+ ]
+
+ def __init__(self):
+ RNode.__init__(self)
+ self.sources.add(Skybox())
+
+ # must be configured by user before inserting first camera
+ hdrm = HDRPPFXManager()
+ self.hdrm = hdrm
+
+ def _deep_child_added(self, obj):
+ Scene._deep_child_added(self, obj)
+ if isinstance(obj, Camera):
+ self.hdrm.inject_camera(obj)
+ if isinstance(obj, DirectionalLight):
+ obj.fb_ssao = self.hdrm.fb_ssao
+
+################################################################################
A => liminal_legacy/liminal/engine/draw.py +547 -0
@@ 0,0 1,547 @@
+from __future__ import (print_function, division, absolute_import)
+
+import math
+import logging
+import random
+
+from glm import vec2, vec3, vec4, X_AXIS, Y_AXIS, Z_AXIS, mat3, AABB
+from gl import (GL_LINES, GL_TRIANGLES)
+
+from .interface import Named
+from .mesh import MeshBuffer, VertexArray, MeshAttribute
+from .render import AttribLocations, RNode, RJCMD_DRAW
+from glue import mix
+
+################################################################################
+
+log = logging.getLogger('liminal.debugdraw')
+
+################################################################################
+
+def random_colors(count):
+ for i in range(count):
+ yield random_color()
+
+def random_color():
+ return vec4(
+ random.random(),
+ random.random(),
+ random.random(),
+ 1.0,
+ )
+
+################################################################################
+
+class VertexBufferPainter(RNode):
+ __slots__ = [
+ '_config',
+ '_vertex_offset',
+ '_mesh_buffer',
+ '_mesh',
+ '_material',
+ ]
+
+ RED = vec4(1,0,0,1)
+ GREEN = vec4(0,1,0,1)
+ BLUE = vec4(0,0,1,1)
+ YELLOW = vec4(1,1,0,1)
+ CYAN = vec4(0,1,1,1)
+ MAGENTA = vec4(1,0,1,1)
+ BLACK = vec4(0,0,0,1)
+ WHITE = vec4(1,1,1,1)
+
+ VERTEX_LIMIT = 1024
+ DRAW_ARRAY_MODE = GL_TRIANGLES
+
+ POSITION_BUFFER_NAME = 'in_Position'
+
+ def __init__(self, **kargs):
+ RNode.__init__(self)
+
+ name = kargs.pop('name', 'vertex_buffer_painter')
+ config = kargs.pop('config', 'pc')
+ vertex_limit = kargs.pop('vertex_limit', self.VERTEX_LIMIT)
+
+ self.name = name
+ self._config = config
+ self._vertex_offset = 0
+
+ self._mesh_buffer = MeshBuffer.from_format(config, vertex_limit)
+ self._mesh = VertexArray.from_mesh_buffer(self._mesh_buffer)
+ self._mesh.draw_array_mode = self.DRAW_ARRAY_MODE
+
+ @property
+ def material(self):
+ return self._material
+ @material.setter
+ def material(self, value):
+ self._material = value
+
+ @property
+ def vertex_array(self):
+ return self._mesh
+
+ @property
+ def mesh_buffer(self):
+ return self._mesh_buffer
+
+ def add_vertices(self, *vectorlists):
+ """add_vertices([vec3,...], [...], ...)"""
+
+ mesh_buffer = self._mesh_buffer
+ mesh_format = mesh_buffer.format
+
+ vertex_offset = self._vertex_offset
+
+ count = len(vectorlists[0])
+ if (vertex_offset + count) > mesh_buffer.vertex_capacity:
+ vertex_offset = 0
+
+ mesh_format.write_list(mesh_buffer, vertex_offset, *vectorlists)
+ self._mesh.update_from_mesh_buffer(mesh_buffer)
+
+ self._vertex_offset = vertex_offset + count
+
+ def process(self, batch, jobs):
+ self._mesh.process(batch, jobs)
+
+ def clear(self):
+ self._mesh_buffer.vertex_count = 0
+ self._mesh.update_from_mesh_buffer(self._mesh_buffer)
+ self._vertex_offset = 0
+
+################################################################################
+
+class PolygonPainter(VertexBufferPainter):
+ __slots__ = [
+ 'convex_polygon'
+ ]
+
+ DRAW_ARRAY_MODE = GL_TRIANGLES
+
+ def __init__(self, **kargs):
+ VertexBufferPainter.__init__(self, name = 'polygon_painter', **kargs)
+
+ def convex_polygon(self, *lists):
+ """convex_polygon([vec3,...], [...], ...)"""
+
+ new_lists = []
+ for items in lists:
+ new_items = []
+ for i in range(1, len(items)-1):
+ new_items.append(items[0])
+ new_items.append(items[i])
+ new_items.append(items[i+1])
+ new_lists.append(new_items)
+
+ self.add_vertices(*new_lists)
+
+ QUAD_TEXCOORDS = (
+ vec2(0.0, 0.0),
+ vec2(1.0, 0.0),
+ vec2(1.0, 1.0),
+ vec2(0.0, 1.0),
+ )
+
+ def rect_vec4(self, rect, color, *lists):
+ if not isinstance(color, (list, tuple)):
+ color = [color]*4
+ self.convex_polygon((
+ vec3(rect.x, rect.y, 0.0),
+ vec3(rect.x+rect.z, rect.y, 0.0),
+ vec3(rect.x+rect.z, rect.y+rect.w, 0.0),
+ vec3(rect.x, rect.y+rect.w, 0.0),
+ ), color, *lists)
+
+ def quadplane(self, origin, vx, vy, color, *lists):
+ vox = origin.add(vx)
+ self.convex_polygon((
+ origin,
+ vox,
+ vox.add(vy),
+ origin.add(vy),
+ ), [color]*4, *lists)
+
+ def screen(self, fov=math.pi, distance=1.0, height=1.0):
+ # needs pctn format
+ s = math.tan(fov*0.5)*distance
+ c = distance
+ colors = (vec4(1,1,1,1),)*4
+ normals = (vec3(0.0,-1.0,0.0),)*4
+ self.convex_polygon((
+ vec3(-s,c,-height*0.5),
+ vec3(s,c,-height*0.5),
+ vec3(s,c,height*0.5),
+ vec3(-s,c,height*0.5),
+ ), colors, (
+ vec2(0.0, 0.0),
+ vec2(1.0, 0.0),
+ vec2(1.0, 1.0),
+ vec2(0.0, 1.0),
+ ), normals)
+
+
+ def hemispheric_screen(self, fov=math.pi, radius=1.0, height=1.0, segments=24):
+ # needs pctn format
+ # height = fov*radius/aspect
+ # hemispheric screen
+ begin_angle = -fov*0.5
+ end_angle = fov*0.5
+ colors = (vec4(1,1,1,1),)*4
+ for i in range(segments):
+ u0 = i/segments
+ u1 = (i+1)/segments
+ a0 = mix(begin_angle, end_angle, u0)
+ a1 = mix(begin_angle, end_angle, u1)
+ s0,c0 = math.sin(a0),math.cos(a0)
+ s1,c1 = math.sin(a1),math.cos(a1)
+ ps0,pc0 = s0*radius,c0*radius
+ ps1,pc1 = s1*radius,c1*radius
+ self.convex_polygon((
+ vec3(ps0,pc0,-height*0.5),
+ vec3(ps1,pc1,-height*0.5),
+ vec3(ps1,pc1,height*0.5),
+ vec3(ps0,pc0,height*0.5),
+ ), colors, (
+ vec2(u0, 0.0),
+ vec2(u1, 0.0),
+ vec2(u1, 1.0),
+ vec2(u0, 1.0),
+ ), (
+ vec3(-s0,-c0,0.0),
+ vec3(-s1,-c1,0.0),
+ vec3(-s1,-c1,0.0),
+ vec3(-s0,-c0,0.0),
+ ))
+
+ def aabb(self, aabb, colors, wallflags = 0x3f):
+ texcoords = self.QUAD_TEXCOORDS
+
+ #wallflags = 0x4|0x20
+
+ minv = aabb.minv
+ maxv = aabb.maxv
+ size = aabb.size
+
+ if wallflags & 0x1: # -X
+ self.quadplane(minv,
+ vec3(0, 0, size.z),
+ vec3(0, size.y, 0),
+ colors[0], texcoords)
+ if wallflags & 0x2: # -Y
+ self.quadplane(minv,
+ vec3(size.x, 0, 0),
+ vec3(0, 0, size.z),
+ colors[1], texcoords)
+ if wallflags & 0x4: # -Z
+ self.quadplane(minv,
+ vec3(0, size.y, 0),
+ vec3(size.x, 0, 0),
+ colors[2], texcoords)
+ if wallflags & 0x8: # +X
+ self.quadplane(maxv,
+ vec3(0, -size.y, 0),
+ vec3(0, 0, -size.z),
+ colors[3], texcoords)
+ if wallflags & 0x10: # +Y
+ self.quadplane(maxv,
+ vec3(0, 0, -size.z),
+ vec3(-size.x, 0, 0),
+ colors[4], texcoords)
+ if wallflags & 0x20: # +Z
+ self.quadplane(maxv,
+ vec3(-size.x, 0, 0),
+ vec3(0, -size.y, 0),
+ colors[5], texcoords)
+
+################################################################################
+
+class FontPainter(PolygonPainter):
+ CONFIG = (
+ 'p','c','t', 'n',
+ MeshAttribute(name = 'sharpness',
+ elements = 1,
+ location = AttribLocations.in_sharpness
+ )
+ )
+
+ def __init__(self, **kargs):
+ font = kargs.pop('font', None)
+ PolygonPainter.__init__(self, config=self.CONFIG, **kargs)
+ self.font = font
+
+ IDENTITY = mat3.identity()
+
+ def extents(self, text):
+ return self.font.extents(text)
+
+ def text(self, label, origin, height, color, sharpness=1.1, mtx = None):
+ assert isinstance(origin, vec3)
+
+ font = self.font
+ font.ensure_loaded()
+
+ if mtx is None:
+ mtx = self.IDENTITY
+
+ n = mtx.to_mat3().mul_vec3(vec3(0,0,1))
+
+ sharp = [sharpness, sharpness, sharpness, sharpness]
+ colors = [color]*4
+ norms = [n]*4
+
+ for cx,cy,xofs,margins,rect,uvrect in font.iterate_chars(label):
+ if rect is None:
+ continue
+
+ (x0,y0,x1,y1) = rect
+ (u0,v0,u1,v1) = uvrect
+
+ self.convex_polygon([origin.add(pt.mul_f(height)) for pt in [
+ mtx.mul_vec3(vec3(x0,y0,0.0)),
+ mtx.mul_vec3(vec3(x1,y0,0.0)),
+ mtx.mul_vec3(vec3(x1,y1,0.0)),
+ mtx.mul_vec3(vec3(x0,y1,0.0)),
+ ]], colors, [
+ vec2(u0,v0),
+ vec2(u1,v0),
+ vec2(u1,v1),
+ vec2(u0,v1),
+ ], norms, sharp)
+
+################################################################################
+
+def proj_near_far(mtx):
+ p2z = mtx.i22
+ p2w = mtx.i32
+ p3z = mtx.i23
+ p3w = mtx.i33
+ near = (p3w + p2w) / (p3z + p2z)
+ far = (p3w - p2w) / (p3z - p2z)
+ return near, far
+
+################################################################################
+
+class LinePainter(VertexBufferPainter):
+ __slots__ = [
+ ]
+
+ DRAW_ARRAY_MODE = GL_LINES
+
+ def __init__(self, **kargs):
+ VertexBufferPainter.__init__(self, name = 'line_painter',
+ config = 'pc', **kargs)
+
+ def polyline(self, pts, color, loop = False):
+ """polyline([vec3,...], vec4, bool)"""
+
+ ptcount = len(pts)
+ offset = 0 if loop else 1
+
+ verts = []
+ colors = []
+
+ for i in range(offset,ptcount):
+ i0 = (i-1)%ptcount
+ verts.append(pts[i0])
+ verts.append(pts[i])
+ colors.append(color)
+ colors.append(color)
+
+ self.add_vertices(verts, colors)
+
+ def ray(self, p0, p1, color):
+ #self.cross_vec3(p1, t, color, color, color)
+ td = p1.sub(p0)
+ t = td.length() / 4.0
+ pz = td.normalize()
+ if pz is None:
+ return
+ px = pz.ortho()
+ py = px.cross(pz)
+ px = px.mul_f(t)
+ py = py.mul_f(t)
+ self.polyline((
+ p0.sub(px), p0.add(px), p1
+ ), color, loop=True)
+ self.polyline((
+ p0.sub(py), p0.add(py), p1
+ ), color, loop=True)
+
+ def normal_vec3(self, pt, norm, color):
+ self.ray(pt, pt.add(norm), color)
+
+ def plane_vec3(self, pt, norm, r, color):
+ self.normal_vec3(pt, norm.mul_f(r), color)
+ self.plane_disc_vec3(pt, norm, r, color)
+
+ def plane_disc_vec3(self, pt, norm, r, color):
+ b = norm.ortho().normalize()
+ c = norm.cross(b)
+ self.circle(pt, r, color, b, c, 8)
+
+ def cube(self, p, h2, color):
+ self.aabb(AABB(p.sub_f(h2), p.add_f(h2)), color)
+
+ def box(self, p, h2, color):
+ self.aabb(AABB(p.sub(h2), p.add(h2)), color)
+
+ def camera(self, camera, color, use_view=True):
+ cam_attribs = camera.get_view_attrib(0)
+ mtx_cam_p = cam_attribs.projection_matrix
+ mtx_cam_v = cam_attribs.view_matrix
+ if use_view:
+ mtx_cam_inv = mtx_cam_p.mul_mat4(mtx_cam_v).inverse()
+ else:
+ mtx_cam_inv = mtx_cam_p.inverse()
+ near,far = proj_near_far(mtx_cam_p)
+ #self.sphere(camera.world_position, near, color, 6)
+ dist = far - near
+ corners = []
+ for y in (-1,1):
+ for x in (-1,1):
+ r0 = mtx_cam_inv.mul_vec4(vec4(x,y,-1,1))
+ if r0.w:
+ r0 = r0.div_f(r0.w)
+ r0 = r0.to_vec3()
+ r1 = mtx_cam_inv.mul_vec4(vec4(x,y,1,1))
+ if r1.w:
+ r1 = r1.div_f(r1.w)
+ r1 = r1.to_vec3()
+ ray = r1.sub(r0)
+ p0 = r0.add(ray.mul_f((near - near) / dist))
+ p1 = r0.add(ray.mul_f((far - near) / dist))
+ for pt in p0,p1:
+ corners.append(pt)
+ self.frustum(corners, color)
+
+ def frustum(self, points, color):
+ p0,p1,p2,p3,p4,p5,p6,p7 = points
+ self.polyline((
+ p0,p1,p3,p2,
+ ), color, loop = True)
+ self.polyline((
+ p4,p5,p7,p6,
+ ), color, loop = True)
+ self.polyline((
+ p0, p4
+ ), color)
+ self.polyline((
+ p1, p5,
+ ), color)
+ self.polyline((
+ p3, p7,
+ ), color)
+ self.polyline((
+ p2, p6
+ ), color)
+
+ def aabb(self, aabb, color):
+ self.polyline((
+ vec3(aabb.minv.i0, aabb.minv.i1, aabb.minv.i2),
+ vec3(aabb.maxv.i0, aabb.minv.i1, aabb.minv.i2),
+ vec3(aabb.maxv.i0, aabb.maxv.i1, aabb.minv.i2),
+ vec3(aabb.minv.i0, aabb.maxv.i1, aabb.minv.i2),
+ ), color, loop = True)
+ self.polyline((
+ vec3(aabb.minv.i0, aabb.minv.i1, aabb.maxv.i2),
+ vec3(aabb.maxv.i0, aabb.minv.i1, aabb.maxv.i2),
+ vec3(aabb.maxv.i0, aabb.maxv.i1, aabb.maxv.i2),
+ vec3(aabb.minv.i0, aabb.maxv.i1, aabb.maxv.i2),
+ ), color, loop = True)
+ self.polyline((
+ vec3(aabb.minv.i0, aabb.minv.i1, aabb.minv.i2),
+ vec3(aabb.minv.i0, aabb.minv.i1, aabb.maxv.i2),
+ ), color)
+ self.polyline((
+ vec3(aabb.maxv.i0, aabb.minv.i1, aabb.minv.i2),
+ vec3(aabb.maxv.i0, aabb.minv.i1, aabb.maxv.i2),
+ ), color)
+ self.polyline((
+ vec3(aabb.maxv.i0, aabb.maxv.i1, aabb.minv.i2),
+ vec3(aabb.maxv.i0, aabb.maxv.i1, aabb.maxv.i2),
+ ), color)
+ self.polyline((
+ vec3(aabb.minv.i0, aabb.maxv.i1, aabb.minv.i2),
+ vec3(aabb.minv.i0, aabb.maxv.i1, aabb.maxv.i2),
+ ), color)
+
+ def circle(self, p, r, color, u_axis = X_AXIS, v_axis = Y_AXIS, segments = 16):
+ pts = []
+ for i in xrange(segments):
+ a = (2.0 * math.pi * i) / segments
+ u = math.sin(a) * r
+ v = math.cos(a) * r
+ pts.append(p.add(u_axis.mul_f(u).add(v_axis.mul_f(v))))
+ self.polyline(pts, color, loop = True)
+
+ def circle_x(self, p, r, color, segments = 16):
+ self.circle(p, r, color, Z_AXIS, Y_AXIS, segments)
+
+ def circle_y(self, p, r, color, segments = 16):
+ self.circle(p, r, color, X_AXIS, Z_AXIS, segments)
+
+ def circle_z(self, p, r, color, segments = 16):
+ self.circle(p, r, color, X_AXIS, Y_AXIS, segments)
+
+ def sphere(self, p, r, color, segments = 16):
+ self.circle_x(p, r, color, segments)
+ self.circle_y(p, r, color, segments)
+ self.circle_z(p, r, color, segments)
+
+ def grid_z(self, origin, size, count, color):
+ s2 = size/2.0
+ step = size/count
+ for i in xrange(count+1):
+ n = i*step - s2
+ self.polyline([
+ origin.add(vec3(n, -s2, 0)),
+ origin.add(vec3(n, s2, 0))
+ ], color)
+ self.polyline([
+ origin.add(vec3(-s2, n, 0)),
+ origin.add(vec3(s2, n, 0))
+ ], color)
+
+ def cross_vec3(self, pt, s, color_x, color_y = None, color_z = None):
+ axis_x = vec3(s, 0.0, 0.0)
+ axis_y = vec3(0.0, s, 0.0)
+ axis_z = vec3(0.0, 0.0, s)
+
+ if color_y is None:
+ color_y = color_x
+ if color_z is None:
+ color_z = color_x
+
+ self.polyline([pt.sub(axis_x), pt.add(axis_x)], color_x)
+ self.polyline([pt.sub(axis_y), pt.add(axis_y)], color_y)
+ self.polyline([pt.sub(axis_z), pt.add(axis_z)], color_z)
+
+ def axis_mat4(self, mtx):
+ origin = mtx.col3()
+ axis_x = origin.add(mtx.col0())
+ axis_y = origin.add(mtx.col1())
+ axis_z = origin.add(mtx.col2())
+ self.polyline([origin,axis_x], vec4(1.0, 0.3, 0.1, 1.0))
+ self.polyline([origin,axis_y], vec4(0.1, 1.0, 0.3, 1.0))
+ self.polyline([origin,axis_z], vec4(0.3, 0.1, 1.0, 1.0))
+
+################################################################################
+
+class DebugGrid(LinePainter):
+ LINE_COLOR = vec4(0.1, 0.2, 0.5, 1.0)
+
+ def __init__(self):
+ LinePainter.__init__(self, index_limit = 1024)
+ self.name = 'debug_grid'
+ self.material.set_additive()
+ self.update_grid()
+
+ def update_grid(self):
+ self.clear()
+ self.grid_z(vec3(0,0,0), 100.0, 100, self.LINE_COLOR)
+ self.grid_z(vec3(0,0,0), 10.0, 100, self.LINE_COLOR)
+ self.cross_vec3(vec3(0,0,0), 100.0,
+ vec4(1.0, 0.0, 0.0, 1.0),
+ vec4(0.0, 1.0, 0.0, 1.0),
+ vec4(0.0, 0.0, 1.0, 1.0))
A => liminal_legacy/liminal/engine/fileman.py +130 -0
@@ 0,0 1,130 @@
+from __future__ import (print_function, division, absolute_import)
+
+import os
+import shutil
+import traceback
+
+from glue import (defproperty, lazydecls, callsetbag, Singleton)
+from ..utils import DirChangeTracker
+from .config import Config
+
+################################################################################
+
+@lazydecls
+class FileManager(object):
+ __metaclass__ = Singleton
+
+ basepath = defproperty(readonly = True)
+ change_tracker = defproperty(readonly = True)
+
+ def __init__(self):
+ self.__basepath = Config().base_path
+ self.__change_tracker = DirChangeTracker(self.__basepath)
+ self.events = self.__change_tracker.events
+
+ def abspath(self, path):
+ if not os.path.isabs(path):
+ path = os.path.abspath(os.path.join(self.basepath, path))
+ assert path.startswith(self.basepath)
+ return path
+
+ def relpath(self, filepath):
+ if os.path.isabs(filepath):
+ filepath = os.path.relpath(filepath, self.basepath)
+ return filepath
+
+ def isfile(self, filepath):
+ return os.path.isfile(self.abspath(filepath))
+
+ def isdir(self, filepath):
+ return os.path.isdir(self.abspath(filepath))
+
+ def exists(self, filepath):
+ return os.path.exists(self.abspath(filepath))
+
+ def read(self, obj):
+ filepath = self.abspath(obj)
+ with file(filepath, 'r') as f:
+ data = f.read()
+ return data
+
+ def write(self, obj, value):
+ try:
+ assert self.isfile(obj)
+ filepath = self.abspath(obj)
+ with file(filepath, 'w') as f:
+ f.write(value)
+ except:
+ traceback.print_exc()
+ return False
+ return True
+
+ def run(self, obj):