Files
questshock/android/engine-patches/04-android-opengl-es-render.patch
T
2026-07-20 09:45:08 +02:00

160 lines
6.1 KiB
Diff

--- a/src/MacSrc/OpenGL.cc
+++ b/src/MacSrc/OpenGL.cc
@@ -7,6 +7,11 @@
#define GLEW_STATIC 1
#include <SDL.h>
#include <GL/glew.h>
+#elif defined(__ANDROID__)
+// GLES 3.2 (see Shock.c's context creation) - none of the desktop GL
+// headers below exist on Android; EGL only ever gives out GLES contexts.
+#include <SDL.h>
+#include <GLES3/gl32.h>
#else
#define GL_GLEXT_PROTOTYPES
#ifdef __APPLE__
@@ -20,6 +25,112 @@
#include <SDL_opengl.h>
#endif
+#ifdef __ANDROID__
+// GLES has no immediate-mode rendering (glBegin/glVertex3f/glEnd, used
+// throughout this file below) at all. These macros transparently replace
+// it with an equivalent that accumulates vertex data into arrays and
+// issues one glDrawArrays call at glEnd() time - every actual call site
+// below (all unchanged) looks identical on both platforms. "position",
+// bound to attribute location 0 in CreateShader() below, replaces
+// gl_Vertex (a desktop-only builtin android/gles-shaders/main.vert can't
+// use either).
+#define glBegin questshock_glBegin
+#define glVertex3f questshock_glVertex3f
+#define glEnd questshock_glEnd
+#define glVertexAttrib1f questshock_glVertexAttrib1f
+#define glVertexAttrib2f questshock_glVertexAttrib2f
+#define glPointSize questshock_glPointSize
+
+// Generous enough for both the largest opengl_draw_poly() polygon and a
+// full frame's worth of opengl_draw_star() points accumulated between one
+// opengl_begin_stars()/opengl_end_stars() pair.
+static const int QUESTSHOCK_MAX_IM_VERTS = 4096;
+static float questshock_im_pos[QUESTSHOCK_MAX_IM_VERTS * 3];
+static float questshock_im_tc[QUESTSHOCK_MAX_IM_VERTS * 2];
+static float questshock_im_light[QUESTSHOCK_MAX_IM_VERTS];
+static int questshock_im_count;
+static GLenum questshock_im_mode;
+static float questshock_im_cur_tc[2];
+static float questshock_im_cur_light;
+static float questshock_point_size = 1.0f;
+
+// GLES has no glPointSize() at all - point size is instead read from
+// gl_PointSize, set by a "pointSize" uniform in
+// android/gles-shaders/main.vert. Stashed here and applied in
+// questshock_glEnd() below, since opengl_begin_stars() calls this before
+// glUseProgram(starShaderProgram...) - the star shader isn't the current
+// program yet at this point.
+static void questshock_glPointSize(GLfloat size) { questshock_point_size = size; }
+
+static void questshock_glBegin(GLenum mode) {
+ questshock_im_mode = mode;
+ questshock_im_count = 0;
+ questshock_im_cur_tc[0] = questshock_im_cur_tc[1] = 0.0f;
+ questshock_im_cur_light = 0.0f;
+}
+
+static void questshock_glVertexAttrib1f(GLuint index, GLfloat v) {
+ // Only ever called for "light" at these call sites.
+ (void)index;
+ questshock_im_cur_light = v;
+}
+
+static void questshock_glVertexAttrib2f(GLuint index, GLfloat a, GLfloat b) {
+ // Only ever called for "texcoords" at these call sites.
+ (void)index;
+ questshock_im_cur_tc[0] = a;
+ questshock_im_cur_tc[1] = b;
+}
+
+static void questshock_glVertex3f(GLfloat x, GLfloat y, GLfloat z) {
+ if (questshock_im_count >= QUESTSHOCK_MAX_IM_VERTS)
+ return;
+ questshock_im_pos[questshock_im_count * 3 + 0] = x;
+ questshock_im_pos[questshock_im_count * 3 + 1] = y;
+ questshock_im_pos[questshock_im_count * 3 + 2] = z;
+ questshock_im_tc[questshock_im_count * 2 + 0] = questshock_im_cur_tc[0];
+ questshock_im_tc[questshock_im_count * 2 + 1] = questshock_im_cur_tc[1];
+ questshock_im_light[questshock_im_count] = questshock_im_cur_light;
+ questshock_im_count++;
+}
+
+static void questshock_glEnd() {
+ GLint program = 0;
+ glGetIntegerv(GL_CURRENT_PROGRAM, &program);
+ GLint posAttrib = glGetAttribLocation(program, "position");
+ GLint tcAttrib = glGetAttribLocation(program, "texcoords");
+ GLint lightAttrib = glGetAttribLocation(program, "light");
+
+ if (questshock_im_mode == GL_POINTS) {
+ GLint pointSizeUniform = glGetUniformLocation(program, "pointSize");
+ if (pointSizeUniform >= 0)
+ glUniform1f(pointSizeUniform, questshock_point_size);
+ }
+
+ if (posAttrib >= 0) {
+ glVertexAttribPointer(posAttrib, 3, GL_FLOAT, GL_FALSE, 0, questshock_im_pos);
+ glEnableVertexAttribArray(posAttrib);
+ }
+ if (tcAttrib >= 0) {
+ glVertexAttribPointer(tcAttrib, 2, GL_FLOAT, GL_FALSE, 0, questshock_im_tc);
+ glEnableVertexAttribArray(tcAttrib);
+ }
+ if (lightAttrib >= 0) {
+ glVertexAttribPointer(lightAttrib, 1, GL_FLOAT, GL_FALSE, 0, questshock_im_light);
+ glEnableVertexAttribArray(lightAttrib);
+ }
+
+ glDrawArrays(questshock_im_mode, 0, questshock_im_count);
+
+ if (posAttrib >= 0)
+ glDisableVertexAttribArray(posAttrib);
+ if (tcAttrib >= 0)
+ glDisableVertexAttribArray(tcAttrib);
+ if (lightAttrib >= 0)
+ glDisableVertexAttribArray(lightAttrib);
+}
+#endif // __ANDROID__
+
extern "C" {
#include "mainloop.h"
#include "map.h"
@@ -229,6 +340,12 @@
GLuint shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertShader);
glAttachShader(shaderProgram, fragShader);
+#ifdef __ANDROID__
+ // Must be bound before linking. Matches questshock_glEnd()'s
+ // glGetAttribLocation(program, "position") above and
+ // android/gles-shaders/main.vert's "position" attribute.
+ glBindAttribLocation(shaderProgram, 0, "position");
+#endif
glLinkProgram(shaderProgram);
glUseProgram(shaderProgram);
@@ -313,10 +430,17 @@
glEnable(GL_CULL_FACE);
glEnable(GL_BLEND);
+#ifndef __ANDROID__
+ // GLES has no fixed-function pipeline: no alpha test (emulated with a
+ // discard in android/gles-shaders/texture.frag instead), and point
+ // sprites are always implicitly on for GL_POINTS rendering (via
+ // gl_PointCoord in android/gles-shaders/star.frag) - no enum/enable
+ // for either exists in GLES at all.
glEnable(GL_ALPHA_TEST);
glEnable(GL_POINT_SPRITE);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glAlphaFunc(GL_GEQUAL, 0.05f);
+#endif
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
CreateShader("main.vert", "texture.frag", &textureShaderProgram);
CreateShader("main.vert", "color.frag", &colorShaderProgram);