Add controller input and a laser-pointer-driven menu quad
build / build (push) Successful in 2m26s

- 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).
This commit is contained in:
ml
2026-08-02 06:27:49 +02:00
parent 2300d081c3
commit d2dc58391b
310 changed files with 141458 additions and 226 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+112
View File
@@ -0,0 +1,112 @@
#!/usr/bin/env python
from collections import defaultdict
import re
import xml.etree.ElementTree as ET
import yaml
def etna_to_yml(xml):
defs = xml.find('functions')
functions = defaultdict(dict)
for f in defs.findall('function'):
name = f.get('name')
ret = f.find('return')
if ret is not None:
ret = ret.get('type')
if ret is None:
ret = 'void'
params = []
for p in f.findall('param'):
params.append('{} {}'.format(p.get('type'), p.get('name')))
functions[name] = [ret] + params
return functions
def lua_to_yml(xml):
typemap = xml.find('typemap')
types = {}
for t in typemap:
name = t.get('typename')
types[name] = t.get('C-lang', name)
defs = xml.find('functions').find('function-defs')
functions = defaultdict(dict)
for f in defs.findall('function'):
cat = f.get('category')
ret = f.get('return')
ret = types.get(ret, ret)
func = f.get('name')
params = []
for param in f.findall('param'):
typ = param.get('type')
typ = types.get(typ, typ)
name = param.get('name')
kind = param.get('kind')
if kind in ('array', 'reference', 'array[size]'):
typ = typ.rstrip()
if not typ.endswith('*') or kind == 'reference':
typ += ' *'
if not 'const' in typ and param.get('input', 'false') == 'true':
typ = 'const ' + typ
p = '{} {}'.format(typ, name)
p = p.replace('* ', '*')
params.append(p)
args = [ret]
args.extend(params)
functions[cat][func] = args
return functions
def khronos_to_yml(xml):
def extract(node):
return node.findtext('ptype') or node.text, node.findtext('name')
def clean(s):
return re.sub('\s+', ' ', s).strip()
defs = xml.find('commands')
functions = defaultdict(dict)
for f in defs.findall('command'):
proto = f.find('proto')
ret, name = extract(proto)
params = []
for param in f.findall('param'):
params.append(clean(' '.join((param.itertext()))))
functions[name] = [ret] + params
return functions
def to_yml(filename):
with open(filename, 'r') as f:
data = f.read()
data = re.sub(' xmlns="[^"]+"', '', data, count=1)
xml = ET.fromstring(data)
if xml.tag == 'root':
functions = etna_to_yml(xml)
elif xml.tag == 'specification':
functions = lua_to_yml(xml)
elif xml.tag == 'registry':
functions = khronos_to_yml(xml)
else:
print 'unrecognized root tag:', xml.tag
yml = yaml.dump(dict(functions))
with open(filename.replace('xml', 'yml'), 'w') as o:
o.write(yml)
if __name__ == '__main__':
import sys
if len(sys.argv) < 2:
print 'Usage: {} <file.xml> [file.xml...]'.format(sys.argv[0])
sys.exit(1)
for name in sys.argv[1:]:
to_yml(name)
File diff suppressed because it is too large Load Diff