Files
ml d2dc58391b
build / build (push) Successful in 2m26s
Add controller input and a laser-pointer-driven menu quad
- xr_input.c/h: an OpenXR action set (aim pose, trigger/select click, a
  menu-toggle button) plus ray/quad hit-testing, and a visible
  laser-pointer line drawn from the controller toward the hit point.
- xr_menu.c/h + MenuOverlay.java: a second composition-layer quad,
  toggled by a controller button, showing an off-screen, never-attached
  Android View tree (one "Keyboard" button so far) driven by synthetic
  touch events computed from the laser ray's hit UV.
- Migrate gl4es from a prebuilt binary baked into the Docker build-image
  to a vendored source snapshot (android/gl4es-src/) compiled from
  source at APK build time via CMake add_subdirectory(), patched through
  a new android/gl4es-patches/ (mirrors the existing engine-patches/
  pattern). This was needed to track down and fix a bug hit while
  building the menu: gl4es's fixed-pipeline emulation (fpe.c) was
  unconditionally substituting its own shader onto the menu's draw call
  (fpe_ReleventState() always sets alphafunc to a nonzero sentinel, so
  its fpe_IsEmpty() check could never see the state as empty), making
  the menu quad show the game's own rendering instead of its own
  content. Fixed by routing the menu's blit through a real
  glBlitFramebuffer() call instead of a shader-based draw, which gl4es's
  fpe.c has nothing to intercept - documented in README.md's new
  "Debugging notes" section.
- Renumber android/engine-patches/ to close the gap left by removing an
  unrelated diagnostic-only patch, and strip investigation-journal
  comments and dead diagnostic code (temporary tracing, env-var probes)
  left over from finding the bug above.
- Restructure README.md into numbered sections, document every
  engine/gl4es patch, add Android Studio dev/testing-cycle instructions,
  and generalize Quest-specific wording to any OpenXR headset. Update
  NOTICE.txt to match (gl4es is now vendored/patched source, not a
  prebuilt binary; add the OpenXR-SDK loader).
2026-08-02 06:27:49 +02:00

170 lines
4.5 KiB
Python
Executable File

#!/usr/bin/env python
import argparse
import jinja2
import re
from yaml import safe_load
split_re = re.compile(r'^(?P<type>.*?)\s*(?P<name>\w+)$')
env = jinja2.Environment(
trim_blocks=True,
lstrip_blocks=True,
loader=jinja2.FileSystemLoader('template'),
)
def args(args, add_type=True):
return ', '.join(
'{} {}'.format(arg['type'], arg['name']) if add_type else arg['name']
for arg in args
)
f = '0.2f'
printf_lookup = {
'GLbitfield': 'd',
'GLboolean': 'd',
'GLbyte': 'c',
'GLubyte': 'c',
'GLchar': 'c',
'GLdouble': '0.2f',
'GLenum': 'u',
'GLfloat': '0.2f',
'GLint': 'd',
'GLintptr': 'd',
'GLintptrARB': 'd',
'GLshort': 'd',
'GLsizei': 'd',
'GLsizeiptr': 'd',
'GLsizeiptrARB': 'd',
'GLuint': 'u',
'GLushort': 'u',
'GLvoid': 'p',
}
def printf(args):
types = []
for arg in args:
typ = arg['type']
if '*' in typ:
t = 'p'
else:
t = printf_lookup.get(typ, 'p')
types.append(t)
return ', '.join('%' + t for t in types)
def unconst(s):
split = s.split(' ')
while 'const' in split:
split.remove('const')
return ' '.join(split)
env.filters['args'] = args
env.filters['printf'] = printf
env.filters['unconst'] = unconst
def split_arg(arg):
match = split_re.match(arg)
if match:
return match.groupdict()
else:
return {'type': 'unknown', 'name': arg}
def gen(files, template, guard_name, headers,
deep=False, cats=(), ifdef=None, ifndef=None):
funcs = {}
formats = []
unique_formats = set()
for data in files:
if deep and not isinstance(data.values()[0], list):
functions = []
for cat, f in data.items():
if not cats or cat in cats:
functions.extend(f.items())
else:
functions = data.items()
for name, args in sorted(functions):
props = {}
if args:
ret = args.pop(0)
else:
ret = 'void'
loadlib = 'LOAD_GLES'
if name.endswith('_OES_'):
loadlib = 'LOAD_GLES_OES'
name = name[:-5]
elif name.endswith('_EXT_'):
loadlib = 'LOAD_GLES_EXT'
name = name[:-5]
args = [split_arg(arg) for arg in args if not arg == 'void']
if any(arg.get('type') == 'unknown' for arg in args):
continue
if args:
args[0]['first'] = True
args[-1]['last'] = True
for i, arg in enumerate(args):
arg['index'] = i
types = '_'.join(
arg['type'].replace(' ', '_').replace('*', '__GENPT__')
for arg in [{'type': ret}] + args)
props.update({
'return': ret,
'name': name,
'args': args,
'types': types,
'void': ret == 'void',
'loadlib': loadlib,
})
if not types in unique_formats:
unique_formats.add(types)
formats.append(props)
funcs[name] = props
context = {
'functions': [i[1] for i in sorted(funcs.items())],
'formats': formats,
'headers': headers,
'name': guard_name,
'ifdef': ifdef,
'ifndef': ifndef,
}
t = env.get_template(template)
return t.render(**context).rstrip('\n')
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Generate code with yml/jinja.')
parser.add_argument('yaml', help='spec files')
parser.add_argument('template', help='jinja template to load')
parser.add_argument('name', help='header guard name')
parser.add_argument('headers', nargs='*', help='headers to include')
parser.add_argument('--deep', help='nested definitions', action='store_true')
parser.add_argument('--cats', help='deep category filter')
parser.add_argument('--ifdef', help='wrap with ifdef')
parser.add_argument('--ifndef', help='wrap with ifndef')
args = parser.parse_args()
files = []
for name in args.yaml.split(','):
with open(name) as f:
data = safe_load(f)
if data:
files.append(data)
if args.cats:
cats = args.cats.split(',')
else:
cats = None
print(gen(files, args.template, args.name,
args.headers, args.deep, cats,
args.ifdef, args.ifndef))