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
+508
View File
@@ -0,0 +1,508 @@
#include "array.h"
#include "debug.h"
#include "enum_info.h"
#include "glcase.h"
#include "light.h"
#include "state.h"
GLvoid *copy_gl_array(const GLvoid *src,
GLenum from, GLsizei width, GLsizei stride,
GLenum to, GLsizei to_width, GLsizei skip, GLsizei count, void* dst) {
if (! src || !count)
return NULL;
if (! stride)
stride = width * gl_sizeof(from);
const char *unknown_str = "LIBGL: copy_gl_array -> unknown type: %x\n";
if(!dst) {
dst = malloc((count-skip) * to_width * gl_sizeof(to));
}
GLsizei from_size = gl_sizeof(from) * width;
if (to_width < width) {
/* printf("Warning: copy_gl_array: %i < %i\n", to_width, width);
return NULL;*/
width = to_width;
}
// quick path if just copying same to same and no specific stride involved
if(stride == (to_width*gl_sizeof(to)) && width==to_width && from==to) {
memcpy(dst, (char*)src + stride*skip, (count-skip) * to_width * gl_sizeof(to));
return dst;
}
// if stride is weird, we need to be able to arbitrarily shift src
// so we leave it in a uintptr_t and cast after incrementing
uintptr_t in = (uintptr_t)src;
in += stride*skip;
if (from == to && to_width >= width) {
GL_TYPE_SWITCH(out, dst, to,
for (int i = skip; i < count; i++) {
memcpy(out, (GLvoid *)in, from_size);
for (int j = width; j < to_width; j++) {
out[j] = 0;
}
out += to_width;
in += stride;
},
default:
printf(unknown_str, from);
return NULL;
)
} else {
GL_TYPE_SWITCH(out, dst, to,
for (int i = skip; i < count; i++) {
GL_TYPE_SWITCH(input, in, from,
for (int j = 0; j < width; j++) {
out[j] = input[j];
}
for (int j = width; j < to_width; j++) {
out[j] = 0;
}
out += to_width;
in += stride;
,
default:
printf(unknown_str, from);
return NULL;
)
},
default:
printf(unknown_str, to);
return NULL;
)
}
return dst;
}
GLvoid *copy_gl_array_texcoord(const GLvoid *src,
GLenum from, GLsizei width, GLsizei stride,
GLsizei to_width, GLsizei skip, GLsizei count, void* dst) {
if (! src || !count)
return NULL;
if (! stride)
stride = width * gl_sizeof(from);
static const char *unknown_str = "LIBGL: copy_gl_array -> unknown type: %x\n";
if(!dst)
dst = malloc((count-skip) * to_width * gl_sizeof(GL_FLOAT));
GLsizei from_size = gl_sizeof(from) * width;
GLsizei to_elem = gl_sizeof(GL_FLOAT);
uintptr_t in = (uintptr_t)src;
in += stride*skip;
GLfloat* out = (GLfloat*)dst;
if (from == GL_FLOAT && to_width >= width) {
for (int i = skip; i < count; i++) {
GLfloat* input = (GLfloat*)in;
//memcpy(out, (GLvoid *)in, from_size); // which one is faster ?
for (int j = 0; j < width; j++) {
out[j] = input[j];
}
for (int j = width; j < to_width; j++) {
if(j==3)
out[j] = 1.0f;
else
out[j] = 0.0f;
}
out += to_width;
in += stride;
}
} else {
for (int i = skip; i < count; i++) {
GL_TYPE_SWITCH(input, in, from,
for (int j = 0; j < width; j++) {
out[j] = input[j];
}
for (int j = width; j < to_width; j++) {
if(j==3)
out[j] = 1.0f;
else
out[j] = 0.0f;
}
out += to_width;
in += stride;
,
default:
printf(unknown_str, from);
return NULL;
)
}
}
return dst;
}
GLvoid *copy_gl_array_quickconvert(const GLvoid *src,
GLenum from, GLsizei stride,
GLsizei skip, GLsizei count, void* dest) {
if (! stride)
stride = 4 * gl_sizeof(from);
const char *unknown_str = "LIBGL: copy_gl_array_quickconvert -> unknown type: %x\n";
GLvoid *dst = (dest)?dest:malloc((count-skip) * 4 * gl_sizeof(GL_FLOAT));
uintptr_t in = (uintptr_t)src;
in += stride*skip;
int j;
GLfloat *out = (GLfloat*)dst;
GL_TYPE_SWITCH2(input, in, from,
const GLfloat maxf = 1.0f/gl_max_value(from);
for (int i = skip; i < count; i++)
,
for (j = 0; j < 4; j++) {
out[j] = ((GLfloat)input[j])*maxf;
}
out += 4;
in += stride;
,
default:
printf(unknown_str, from);
return NULL;
)
return dst;
}
GLvoid *copy_gl_array_convert(const GLvoid *src,
GLenum from, GLsizei width, GLsizei stride,
GLenum to, GLsizei to_width, GLsizei skip, GLsizei count, GLvoid* filler, void* dst) {
if (! src || !count)
return NULL;
if (! stride)
stride = width * gl_sizeof(from);
GLsizei from_size = gl_sizeof(from) * width;
if(to==from && width==to_width && stride==(to_width * gl_sizeof(to))) {
if(!dst) dst = malloc((count-skip) * stride);
memcpy(dst, (char*)src+stride*skip, (count-skip)*stride);
return dst;
}
if(to==GL_FLOAT && width==to_width && width==4)
return copy_gl_array_quickconvert(src, from, stride, skip, count, dst);
if(to==GL_FLOAT && from==GL_FLOAT)
if(width<4 || *(GLfloat*)filler==1.0f) // no need to convert
return copy_gl_array_texcoord(src, from, width, stride, to_width, skip, count, dst);
else
return copy_gl_array(src, from, width, stride, to, to_width, skip, count, dst);
const char *unknown_str = "LIBGL: copy_gl_array_convert -> unknown type: %x\n";
if(!dst) dst = malloc((count-skip) * to_width * gl_sizeof(to));
if (to_width < width) {
/*printf("Warning: copy_gl_array: %i < %i\n", to_width, width);
return NULL;*/
width = to_width;
}
// if stride is weird, we need to be able to arbitrarily shift src
// so we leave it in a uintptr_t and cast after incrementing
uintptr_t in = (uintptr_t)src;
in += stride*skip;
int j;
if (from == to && to_width >= width) {
GL_TYPE_SWITCH(out, dst, to,
for (int i = skip; i < count; i++) {
memcpy(out, (GLvoid *)in, from_size);
for (j = width; j < to_width-1; j++) {
out[j]=0;
}
for (; j < to_width; j++) {
memcpy(&out[j], filler, gl_sizeof(to));
}
out += to_width;
in += stride;
},
default:
printf(unknown_str, from);
return NULL;
)
} else {
GL_TYPE_SWITCH_MAX(out, dst, to,
GL_TYPE_SWITCH2(input, in, from,
const GLuint maxf = gl_max_value(from);
for (int i = skip; i < count; i++)
,
for (j = 0; j < width; j++) {
out[j] = input[j]*maxv/maxf;
}
for (; j < to_width-1; j++) {
out[j]=0;
}
for (; j < to_width; j++) {
memcpy(&out[j], filler, gl_sizeof(to));
}
out += to_width;
in += stride;
,
default:
printf(unknown_str, from);
return NULL;
),
default:
printf(unknown_str, to);
return NULL;
)
}
return dst;
}
GLvoid *copy_gl_pointer(vertexattrib_t *ptr, GLsizei width, GLsizei skip, GLsizei count) {
float filler = 0.0f;
return copy_gl_array_convert((const GLvoid*)((uintptr_t)ptr->pointer+(uintptr_t)ptr->real_pointer), ptr->type, ptr->size, ptr->stride,
GL_FLOAT, width, skip, count, &filler, NULL);
}
GLvoid *copy_gl_pointer_color(vertexattrib_t *ptr, GLsizei width, GLsizei skip, GLsizei count) {
float filler = 1.0f;
return copy_gl_array_convert((const GLvoid*)((uintptr_t)ptr->pointer+(uintptr_t)ptr->real_pointer), ptr->type, ptr->size, ptr->stride,
GL_FLOAT, width, skip, count, &filler, NULL);
}
GLvoid *copy_gl_pointer_bytecolor(vertexattrib_t *ptr, GLsizei width, GLsizei skip, GLsizei count) {
GLubyte filler = 255;
return copy_gl_array_convert((const GLvoid*)((uintptr_t)ptr->pointer+(uintptr_t)ptr->real_pointer), ptr->type, ptr->size, ptr->stride,
GL_UNSIGNED_BYTE, width, skip, count, &filler, NULL);
}
GLvoid *copy_gl_pointer_raw(vertexattrib_t *ptr, GLsizei width, GLsizei skip, GLsizei count) {
return copy_gl_array((const GLvoid*)((uintptr_t)ptr->pointer+(uintptr_t)ptr->real_pointer), ptr->type, ptr->size, ptr->stride,
GL_FLOAT, width, skip, count, NULL);
}
GLvoid *copy_gl_pointer_tex(vertexattrib_t *ptr, GLsizei width, GLsizei skip, GLsizei count) {
//float filler = 1.0f;
return copy_gl_array_texcoord((const GLvoid*)((uintptr_t)ptr->pointer+(uintptr_t)ptr->real_pointer), ptr->type, ptr->size, ptr->stride,
/*GL_FLOAT,*/ width, skip, count, /*&filler,*/ NULL);
}
void copy_gl_pointer_noalloc(void* dest, vertexattrib_t *ptr, GLsizei width, GLsizei skip, GLsizei count) {
float filler = 0.0f;
copy_gl_array_convert((const GLvoid*)((uintptr_t)ptr->pointer+(uintptr_t)ptr->real_pointer), ptr->type, ptr->size, ptr->stride,
GL_FLOAT, width, skip, count, &filler, dest);
}
void copy_gl_pointer_color_noalloc(void* dest, vertexattrib_t *ptr, GLsizei width, GLsizei skip, GLsizei count) {
float filler = 1.0f;
copy_gl_array_convert((const GLvoid*)((uintptr_t)ptr->pointer+(uintptr_t)ptr->real_pointer), ptr->type, ptr->size, ptr->stride,
GL_FLOAT, width, skip, count, &filler, dest);
}
void copy_gl_pointer_bytecolor_noalloc(void* dest, vertexattrib_t *ptr, GLsizei width, GLsizei skip, GLsizei count) {
GLubyte filler = 255;
copy_gl_array_convert((const GLvoid*)((uintptr_t)ptr->pointer+(uintptr_t)ptr->real_pointer), ptr->type, ptr->size, ptr->stride,
GL_UNSIGNED_BYTE, width, skip, count, &filler, dest);
}
void copy_gl_pointer_raw_noalloc(void* dest, vertexattrib_t *ptr, GLsizei width, GLsizei skip, GLsizei count) {
copy_gl_array((const GLvoid*)((uintptr_t)ptr->pointer+(uintptr_t)ptr->real_pointer), ptr->type, ptr->size, ptr->stride,
GL_FLOAT, width, skip, count, dest);
}
void copy_gl_pointer_tex_noalloc(void* dest, vertexattrib_t *ptr, GLsizei width, GLsizei skip, GLsizei count) {
//float filler = 1.0f;
copy_gl_array_texcoord((const GLvoid*)((uintptr_t)ptr->pointer+(uintptr_t)ptr->real_pointer), ptr->type, ptr->size, ptr->stride,
/*GL_FLOAT,*/ width, skip, count, /*&filler,*/ dest);
}
GLfloat *gl_pointer_index(vertexattrib_t *p, GLint index) {
static GLfloat buf[4];
GLsizei size = gl_sizeof(p->type);
GLsizei stride = p->stride ? p->stride : (size * p->size);
uintptr_t ptr = (uintptr_t)p->pointer+(uintptr_t)p->real_pointer + (stride * index);
GL_TYPE_SWITCH(src, ptr, p->type,
for (int i = 0; i < p->size; i++) {
buf[i] = src[i];
}
// zero anything not set by the pointer
for (int i = p->size; i < 4; i++) {
buf[i] = 0;
},
default:
printf("LIBGL: unknown pointer type: 0x%x\n", p->type);
)
return buf;
}
GLfloat *copy_eval_double1(GLenum target, GLint ustride, GLint uorder,
const GLdouble *src) {
GLsizei width = get_map_width(target);
GLfloat* out;
if(!src || !width)
return NULL;
out = malloc(uorder*width*sizeof(GLfloat));
GLfloat *p = out;
for (int i=0; i<uorder; i++, src+=ustride)
for (int k=0; k<width; k++)
*p++ = src[k];
return out;
}
GLfloat *copy_eval_float1(GLenum target, GLint ustride, GLint uorder,
const GLfloat *src) {
GLsizei width = get_map_width(target);
GLfloat* out;
if(!src || !width)
return NULL;
out = malloc(uorder*width*sizeof(GLfloat));
GLfloat *p = out;
for (int i=0; i<uorder; i++, src+=ustride)
for (int k=0; k<width; k++)
*p++ = src[k];
return out;
}
GLfloat *copy_eval_double2(GLenum target, GLint ustride, GLint uorder,
GLint vstride, GLint vorder,
const GLdouble *src) {
/* Additional memory is allocated to be used by the horner and
* de Casteljau evaluation schemes.*/
GLsizei width = get_map_width(target);
GLfloat* out;
if(!src || !width)
return NULL;
int dsize = (uorder == 2 && vorder == 2)? 0 : uorder*vorder;
int hsize = (uorder > vorder ? uorder : vorder)*width;
if(hsize>dsize)
out = malloc((uorder*vorder*width+hsize)*sizeof(GLfloat));
else
out = malloc((uorder*vorder*width+dsize)*sizeof(GLfloat));
int uinc = ustride - vorder*vstride;
GLfloat* p = out;
for (int i=0; i<uorder; i++, src += uinc)
for (int j=0; j<vorder; j++, src += vstride)
for (int k=0; k<width; k++)
*p++ = src[k];
return out;
}
GLfloat *copy_eval_float2(GLenum target, GLint ustride, GLint uorder,
GLint vstride, GLint vorder,
const GLfloat *src) {
/* Additional memory is allocated to be used by the horner and
* de Casteljau evaluation schemes.*/
GLsizei width = get_map_width(target);
GLfloat* out;
if(!src || !width)
return NULL;
int dsize = (uorder == 2 && vorder == 2)? 0 : uorder*vorder;
int hsize = (uorder > vorder ? uorder : vorder)*width;
if(hsize>dsize)
out = malloc((uorder*vorder*width+hsize)*sizeof(GLfloat));
else
out = malloc((uorder*vorder*width+dsize)*sizeof(GLfloat));
int uinc = ustride - vorder*vstride;
GLfloat* p = out;
for (int i=0; i<uorder; i++, src += uinc)
for (int j=0; j<vorder; j++, src += vstride)
for (int k=0; k<width; k++)
*p++ = src[k];
return out;
}
void getminmax_indices_us(const GLushort *indices, GLsizei *max, GLsizei *min, GLsizei count) {
if (!count) return;
*max = indices[0];
*min = indices[0];
for (int i = 1; i < count; i++) {
GLsizei n = indices[i];
if( n < *min) *min = n;
if (n > *max) *max = n;
}
}
void normalize_indices_us(GLushort *indices, GLsizei *max, GLsizei *min, GLsizei count) {
getminmax_indices_us(indices, max, min, count);
for (int i = 0; i < count; i++) {
indices[i] -= *min;
}
}
void getminmax_indices_ui(const GLuint *indices, GLsizei *max, GLsizei *min, GLsizei count) {
if (!count) return;
*max = indices[0];
*min = indices[0];
for (int i = 1; i < count; i++) {
GLsizei n = indices[i];
if( n < *min) *min = n;
if (n > *max) *max = n;
}
}
void normalize_indices_ui(GLuint *indices, GLsizei *max, GLsizei *min, GLsizei count) {
getminmax_indices_ui(indices, max, min, count);
for (int i = 0; i < count; i++) {
indices[i] -= *min;
}
}
void *copy_gl_array_bgra(void* dest, const void *ptr, GLint stride, GLsizei width, GLsizei skip, GLsizei count) {
// this one only convert from BGRA (unsigned byte) to RGBA FLOAT
GLubyte* src = (GLubyte*)ptr;
if (! src || !(count-skip))
return NULL;
if (! stride)
stride = 4;
if(!dest)
dest = malloc(4*sizeof(GLfloat)*(count-skip));
GLfloat* dst = dest;
src += skip*(stride);
static const float d = 1.0f/255.0f;
for (int i=skip; i<count; i++) {
#if defined(__ARM_NEON__) && !defined(__APPLE__)
int lsrc = *(int*)src;
lsrc = (lsrc&0xff00ff00) | ((lsrc&0x00ff0000)>>16) | ((lsrc&0x000000ff)<<16);
asm volatile (
"vmov s12, %1 \n\t" // because you cannot vmovl.u8 d6, s11
"vmovl.u8 q3, d6 \n\t" // Expand to 16-bit (so unsetuped s13 is expanded in d7)
"vmovl.u16 q3, d6 \n\t" // Expand to 32-bit, ignoring expanded d7
"vcvt.f32.u32 q3, q3 \n\t" // Convert to float
"vmul.f32 q3, q3, %y2 \n\t" // Normalize
"vst1.f32 {q3}, [%0]! \n\t" // Store, next
:"+r"(dst) :"r"(lsrc), "w"(d)
: "q3", "memory"
);
#else
const GLubyte b = src[0], g = src[1], r = src[2], a = src[3];
*dst++ = r*d;
*dst++ = g*d;
*dst++ = b*d;
*dst++ = a*d;
#endif
src+=stride;
}
return dest;
}
GLvoid *copy_gl_pointer_color_bgra(const void *ptr, GLint stride, GLsizei width, GLsizei skip, GLsizei count) {
return copy_gl_array_bgra(NULL, ptr, stride, width, skip, count);
}
void copy_gl_pointer_color_bgra_noalloc(void* dest, const void *ptr, GLint stride, GLsizei width, GLsizei skip, GLsizei count)
{
copy_gl_array_bgra(dest, ptr, stride, width, skip, count);
}