2014-09-17 03:49:57 +00:00
|
|
|
/*
|
2015-10-22 05:13:26 +00:00
|
|
|
* Apple // emulator for *ix
|
2014-09-17 03:49:57 +00:00
|
|
|
*
|
|
|
|
* This software package is subject to the GNU General Public License
|
2015-10-22 05:13:26 +00:00
|
|
|
* version 3 or later (your choice) as published by the Free Software
|
2014-09-17 03:49:57 +00:00
|
|
|
* Foundation.
|
|
|
|
*
|
2015-10-22 05:13:26 +00:00
|
|
|
* Copyright 2013-2015 Aaron Culliney
|
2014-09-17 03:49:57 +00:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "common.h"
|
2015-03-23 01:53:13 +00:00
|
|
|
#include "video/glvideo.h"
|
2014-09-21 22:58:27 +00:00
|
|
|
#include "video/glinput.h"
|
2015-04-12 22:35:16 +00:00
|
|
|
#include "video/glnode.h"
|
2014-09-17 03:49:57 +00:00
|
|
|
|
2015-10-01 04:55:07 +00:00
|
|
|
#include <regex.h>
|
|
|
|
|
2014-11-29 21:31:21 +00:00
|
|
|
bool safe_to_do_opengl_logging = false;
|
2015-07-05 20:40:50 +00:00
|
|
|
bool renderer_shutting_down = false;
|
2014-11-29 21:31:21 +00:00
|
|
|
|
2015-07-12 20:59:44 +00:00
|
|
|
volatile unsigned long _backend_vid_dirty = 0;
|
2015-01-11 20:27:39 +00:00
|
|
|
|
2014-09-17 03:49:57 +00:00
|
|
|
static int viewportX = 0;
|
|
|
|
static int viewportY = 0;
|
|
|
|
static int viewportWidth = SCANWIDTH*1.5;
|
|
|
|
static int viewportHeight = SCANHEIGHT*1.5;
|
|
|
|
|
2015-09-27 18:45:37 +00:00
|
|
|
GLint texSamplerLoc = UNINITIALIZED_GL;
|
2015-03-23 01:53:13 +00:00
|
|
|
GLint alphaValue = UNINITIALIZED_GL;
|
2015-09-27 18:51:40 +00:00
|
|
|
GLuint mainShaderProgram = UNINITIALIZED_GL;
|
2015-10-01 04:55:07 +00:00
|
|
|
|
2015-09-27 18:51:40 +00:00
|
|
|
bool hackAroundBrokenAdreno200 = false;
|
2015-10-01 04:55:07 +00:00
|
|
|
bool hackAroundBrokenAdreno205 = false;
|
2015-09-27 18:51:40 +00:00
|
|
|
|
2016-01-03 19:59:11 +00:00
|
|
|
extern GLfloat mvpIdentity[16] = { 0 };
|
2015-03-23 01:53:13 +00:00
|
|
|
static GLint uniformMVPIdx = UNINITIALIZED_GL;
|
2015-04-10 05:49:53 +00:00
|
|
|
static GLModel *crtModel = NULL;
|
2015-02-18 03:54:47 +00:00
|
|
|
|
2015-11-07 06:18:16 +00:00
|
|
|
static GLuint vertexShader = UNINITIALIZED_GL;
|
|
|
|
static GLuint fragShader = UNINITIALIZED_GL;
|
|
|
|
|
2015-04-18 04:40:56 +00:00
|
|
|
static video_backend_s glvideo_backend = { 0 };
|
|
|
|
|
2015-03-23 01:53:13 +00:00
|
|
|
#if USE_GLUT
|
2015-10-01 04:58:33 +00:00
|
|
|
static int windowWidth = SCANWIDTH*1.5;
|
|
|
|
static int windowHeight = SCANHEIGHT*1.5;
|
2015-03-23 01:53:13 +00:00
|
|
|
static int glutWindow = -1;
|
|
|
|
#endif
|
|
|
|
|
2014-09-17 03:49:57 +00:00
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
2015-03-23 01:53:13 +00:00
|
|
|
static void gldriver_render(void);
|
|
|
|
|
2015-10-01 04:55:07 +00:00
|
|
|
static void _gldriver_setup_hackarounds(void) {
|
|
|
|
|
2015-09-27 18:51:40 +00:00
|
|
|
const char *vendor = (const char *)glGetString(GL_VENDOR);
|
|
|
|
const char *renderer = (const char *)glGetString(GL_RENDERER);
|
|
|
|
const char *version = (const char *)glGetString(GL_VERSION);
|
2015-09-27 21:29:10 +00:00
|
|
|
if (vendor && renderer && version) {
|
2015-10-01 04:55:07 +00:00
|
|
|
LOG("GL_VENDOR:[%s] GL_RENDERER:[%s] GL_VERSION:[%s]", vendor, renderer, version);
|
2015-09-27 21:29:10 +00:00
|
|
|
} else {
|
2015-10-01 04:55:07 +00:00
|
|
|
RELEASE_LOG("One or more of GL_VENDOR, GL_RENDERER, and GL_VERSION is NULL ... this is bad ...");
|
|
|
|
return;
|
2015-09-27 21:29:10 +00:00
|
|
|
}
|
2015-09-27 18:51:40 +00:00
|
|
|
|
2015-10-01 04:55:07 +00:00
|
|
|
do {
|
|
|
|
// As if we didn't have enough problems with Android ... Bionic's POSIX Regex support for android-10 appears
|
|
|
|
// very basic ... we can't match the word-boundary atomics \> \< \b ... sigh ... hopefully by the time there is
|
|
|
|
// an Adreno 2000 we can remove these hackarounds ;-)
|
|
|
|
|
2015-10-31 06:12:12 +00:00
|
|
|
regex_t qualcommRegex = { 0 };
|
2015-10-01 04:55:07 +00:00
|
|
|
int err = regcomp(&qualcommRegex, "qualcomm", REG_ICASE|REG_NOSUB|REG_EXTENDED);
|
|
|
|
if (err) {
|
|
|
|
LOG("Cannot compile regex : %d", err);
|
|
|
|
break;
|
2015-09-27 18:51:40 +00:00
|
|
|
}
|
2015-10-01 04:55:07 +00:00
|
|
|
int nomatch = regexec(&qualcommRegex, vendor, /*nmatch:*/0, /*pmatch:*/NULL, /*eflags:*/0);
|
2015-10-31 06:12:12 +00:00
|
|
|
regfree(&qualcommRegex);
|
2015-10-01 04:55:07 +00:00
|
|
|
if (nomatch) {
|
|
|
|
LOG("NO MATCH QUALCOMM >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-10-31 06:12:12 +00:00
|
|
|
regex_t adrenoRegex = { 0 };
|
2015-10-01 04:55:07 +00:00
|
|
|
err = regcomp(&adrenoRegex, "adreno", REG_ICASE|REG_NOSUB|REG_EXTENDED);
|
|
|
|
if (err) {
|
|
|
|
LOG("Cannot compile regex : %d", err);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
nomatch = regexec(&adrenoRegex, renderer, /*nmatch:*/0, /*pmatch:*/NULL, /*eflags:*/0);
|
2015-10-31 06:12:12 +00:00
|
|
|
regfree(&adrenoRegex);
|
2015-10-01 04:55:07 +00:00
|
|
|
if (nomatch) {
|
|
|
|
LOG("NO MATCH ADRENO >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-10-31 06:12:12 +00:00
|
|
|
regex_t twoHundredRegex = { 0 };
|
2015-10-01 04:55:07 +00:00
|
|
|
err = regcomp(&twoHundredRegex, "200", REG_ICASE|REG_NOSUB|REG_EXTENDED);
|
|
|
|
if (err) {
|
|
|
|
LOG("Cannot compile regex : %d", err);
|
|
|
|
break;
|
|
|
|
}
|
2015-10-31 06:12:12 +00:00
|
|
|
|
|
|
|
regex_t twoHundredFiveRegex = { 0 };
|
2015-10-01 04:55:07 +00:00
|
|
|
err = regcomp(&twoHundredFiveRegex, "205", REG_ICASE|REG_NOSUB|REG_EXTENDED);
|
|
|
|
if (err) {
|
|
|
|
LOG("Cannot compile regex : %d", err);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-11-03 04:46:16 +00:00
|
|
|
regex_t twoHundredXXRegex = { 0 };
|
|
|
|
err = regcomp(&twoHundredXXRegex, "2[2-9][0-9]", REG_ICASE|REG_NOSUB|REG_EXTENDED);
|
|
|
|
if (err) {
|
|
|
|
LOG("Cannot compile regex : %d", err);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-10-01 04:55:07 +00:00
|
|
|
int found200 = !regexec(&twoHundredRegex, renderer, /*nmatch:*/0, /*pmatch:*/NULL, /*eflags:*/0);
|
|
|
|
int found205 = !regexec(&twoHundredFiveRegex, renderer, /*nmatch:*/0, /*pmatch:*/NULL, /*eflags:*/0);
|
2015-11-03 04:46:16 +00:00
|
|
|
int found2XX = !regexec(&twoHundredXXRegex, renderer, /*nmatch:*/0, /*pmatch:*/NULL, /*eflags:*/0);
|
2015-10-31 06:12:12 +00:00
|
|
|
regfree(&twoHundredRegex);
|
|
|
|
regfree(&twoHundredFiveRegex);
|
2015-11-03 04:46:16 +00:00
|
|
|
regfree(&twoHundredXXRegex);
|
2015-10-31 06:12:12 +00:00
|
|
|
|
2015-10-01 04:55:07 +00:00
|
|
|
if (found200) {
|
|
|
|
LOG("HACKING AROUND BROKEN ADRENO 200");
|
|
|
|
hackAroundBrokenAdreno200 = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (found205) {
|
|
|
|
LOG("HACKING AROUND BROKEN ADRENO 205");
|
|
|
|
hackAroundBrokenAdreno200 = true;
|
|
|
|
hackAroundBrokenAdreno205 = true;
|
|
|
|
break;
|
|
|
|
}
|
2015-11-03 04:46:16 +00:00
|
|
|
if (found2XX) {
|
|
|
|
LOG("HACKING AROUND BROKEN ADRENO 2XX");
|
|
|
|
hackAroundBrokenAdreno200 = true;
|
|
|
|
break;
|
|
|
|
}
|
2015-10-01 04:55:07 +00:00
|
|
|
} while (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void gldriver_init_common(void) {
|
|
|
|
|
|
|
|
_gldriver_setup_hackarounds();
|
|
|
|
|
2016-01-03 19:59:11 +00:00
|
|
|
#if !PERSPECTIVE
|
|
|
|
mtxLoadIdentity(mvpIdentity);
|
|
|
|
#endif
|
|
|
|
|
2015-10-01 04:55:07 +00:00
|
|
|
GLint value = UNINITIALIZED_GL;
|
|
|
|
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &value);
|
|
|
|
LOG("GL_MAX_TEXTURE_SIZE:%d", value);
|
2014-09-17 03:49:57 +00:00
|
|
|
|
2015-07-05 20:40:50 +00:00
|
|
|
renderer_shutting_down = false;
|
|
|
|
|
2015-03-11 19:54:50 +00:00
|
|
|
if (!viewportWidth) {
|
|
|
|
viewportWidth = 400;
|
|
|
|
}
|
|
|
|
if (!viewportHeight) {
|
|
|
|
viewportHeight = 400;
|
|
|
|
}
|
2014-09-17 03:49:57 +00:00
|
|
|
|
2014-09-27 18:03:51 +00:00
|
|
|
// ----------------------------
|
2016-01-02 21:10:02 +00:00
|
|
|
// Create Cathode Ray Tube (CRT) model ... which currently is just a simple texture quad model ...
|
2014-09-17 03:49:57 +00:00
|
|
|
|
2016-01-02 21:10:02 +00:00
|
|
|
mdlDestroyModel(&crtModel);
|
|
|
|
glActiveTexture(TEXTURE_ACTIVE_FRAMEBUFFER);
|
|
|
|
#warning HACK FIXME TODO ^^^^^^^ is glActiveTexture() call needed here?
|
|
|
|
crtModel = mdlCreateQuad((GLModelParams_s){
|
|
|
|
.skew_x = -1.0, // model space coords
|
|
|
|
.skew_y = -1.0,
|
|
|
|
.z = 0.0,
|
|
|
|
.obj_w = 2.0, // entire model space (-1.0 to 1.0)
|
|
|
|
.obj_h = 2.0,
|
|
|
|
.positionUsageHint = GL_STATIC_DRAW, // positions don't change
|
|
|
|
.tex_w = SCANWIDTH,
|
|
|
|
.tex_h = SCANHEIGHT,
|
|
|
|
.texcoordUsageHint = GL_DYNAMIC_DRAW, // but texture (Apple //e framebuffer) does
|
|
|
|
}, (GLCustom){ 0 });
|
2014-09-27 18:03:51 +00:00
|
|
|
|
|
|
|
// ----------------------------
|
|
|
|
// Load/setup shaders
|
2014-09-17 03:49:57 +00:00
|
|
|
|
2016-01-02 18:53:28 +00:00
|
|
|
demoSource *vtxSource = glshader_createSource("Basic.vsh");
|
|
|
|
demoSource *frgSource = glshader_createSource("Basic.fsh");
|
|
|
|
|
|
|
|
assert(vtxSource && "Catastrophic failure if vertex shader did not compile");
|
|
|
|
assert(frgSource && "Catastrophic failure if fragment shader did not compile");
|
2014-09-27 18:03:51 +00:00
|
|
|
|
2014-11-22 22:08:53 +00:00
|
|
|
// Build/use Program
|
2016-01-02 21:10:02 +00:00
|
|
|
mainShaderProgram = glshader_buildProgram(vtxSource, frgSource, /*withTexcoord:*/true, &vertexShader, &fragShader);
|
2016-01-02 18:53:28 +00:00
|
|
|
|
|
|
|
glshader_destroySource(vtxSource);
|
|
|
|
glshader_destroySource(frgSource);
|
2014-09-27 18:03:51 +00:00
|
|
|
|
2016-01-02 18:53:28 +00:00
|
|
|
///////////////////////////////////////
|
|
|
|
// Setup common program input points //
|
|
|
|
///////////////////////////////////////
|
|
|
|
|
|
|
|
glUseProgram(mainShaderProgram);
|
|
|
|
|
|
|
|
texSamplerLoc = glGetUniformLocation(mainShaderProgram, "aTexture");
|
|
|
|
if (texSamplerLoc < 0) {
|
|
|
|
LOG("OOPS, no framebufferTexture shader : %d", texSamplerLoc);
|
|
|
|
} else {
|
|
|
|
glUniform1i(texSamplerLoc, TEXTURE_ID_FRAMEBUFFER);
|
|
|
|
}
|
|
|
|
|
|
|
|
GLint maxTextureUnits = -1;
|
|
|
|
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextureUnits);
|
|
|
|
|
|
|
|
if (maxTextureUnits < TEXTURE_ID_MAX) {
|
|
|
|
#warning FIXME TODO ... gracefully handle devices with low max texture units?
|
|
|
|
ERRLOG("OOPS ... MAX TEXTURE UNITS : %d (<%d)", maxTextureUnits, TEXTURE_ID_MAX);
|
|
|
|
} else {
|
|
|
|
LOG("GL_MAX_TEXTURE_IMAGE_UNITS : %d", maxTextureUnits);
|
|
|
|
}
|
|
|
|
|
|
|
|
uniformMVPIdx = glGetUniformLocation(mainShaderProgram, "modelViewProjectionMatrix");
|
|
|
|
if (uniformMVPIdx < 0) {
|
|
|
|
LOG("OOPS, no modelViewProjectionMatrix in shader : %d", uniformMVPIdx);
|
|
|
|
}
|
|
|
|
|
|
|
|
alphaValue = glGetUniformLocation(mainShaderProgram, "aValue");
|
|
|
|
if (alphaValue < 0) {
|
|
|
|
LOG("OOPS, no texture selector in shader : %d", alphaValue);
|
|
|
|
}
|
|
|
|
|
|
|
|
GL_ERRLOG("build program");
|
2014-09-27 18:03:51 +00:00
|
|
|
|
|
|
|
// ----------------------------
|
|
|
|
// setup static OpenGL state
|
|
|
|
|
2015-03-23 01:53:13 +00:00
|
|
|
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
|
|
|
|
2015-11-07 06:33:15 +00:00
|
|
|
// Set up to do blending of texture quads. Disabling DEPTH/CULL appears to fix blended quad/texture rendering on
|
|
|
|
// finicky Tegra 2. This generally appears to be the correct way to do it accoring to NVIDIA forums and:
|
|
|
|
// http://www.learnopengles.com/android-lesson-five-an-introduction-to-blending/
|
|
|
|
glDisable(GL_DEPTH_TEST);
|
|
|
|
glDisable(GL_CULL_FACE);
|
2015-03-23 01:53:13 +00:00
|
|
|
glEnable(GL_BLEND);
|
|
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
2014-09-27 18:03:51 +00:00
|
|
|
|
|
|
|
// Draw our scene once without presenting the rendered image.
|
|
|
|
// This is done in order to pre-warm OpenGL
|
|
|
|
// We don't need to present the buffer since we don't actually want the
|
|
|
|
// user to see this, we're only drawing as a pre-warm stage
|
2015-03-23 01:53:13 +00:00
|
|
|
gldriver_render();
|
2014-09-27 18:03:51 +00:00
|
|
|
|
|
|
|
// Check for errors to make sure all of our setup went ok
|
|
|
|
GL_ERRLOG("finished initialization");
|
2014-10-08 05:05:14 +00:00
|
|
|
|
|
|
|
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
|
|
|
if (status != GL_FRAMEBUFFER_COMPLETE) {
|
|
|
|
ERRQUIT("framebuffer status: %04X", status);
|
|
|
|
}
|
2014-09-27 18:03:51 +00:00
|
|
|
}
|
|
|
|
|
2015-05-31 19:59:26 +00:00
|
|
|
static void _gldriver_shutdown(void) {
|
2015-09-12 22:06:06 +00:00
|
|
|
LOG("Beginning GLDriver shutdown ...");
|
|
|
|
|
2014-09-27 18:03:51 +00:00
|
|
|
// Cleanup all OpenGL objects
|
2015-09-12 22:06:06 +00:00
|
|
|
|
2015-04-11 07:30:23 +00:00
|
|
|
mdlDestroyModel(&crtModel);
|
2015-09-12 22:06:06 +00:00
|
|
|
|
2015-11-07 06:18:16 +00:00
|
|
|
// detach and delete the main shaders
|
|
|
|
// 2015/11/06 NOTE : Tegra 2 for mobile has a bug whereby you cannot detach/delete shaders immediately after
|
|
|
|
// creating the program. So we delete them during the shutdown sequence instead.
|
|
|
|
// https://code.google.com/p/android/issues/detail?id=61832
|
|
|
|
glDetachShader(mainShaderProgram, vertexShader);
|
|
|
|
glDetachShader(mainShaderProgram, fragShader);
|
|
|
|
glDeleteShader(vertexShader);
|
|
|
|
glDeleteShader(fragShader);
|
|
|
|
vertexShader = UNINITIALIZED_GL;
|
|
|
|
fragShader = UNINITIALIZED_GL;
|
|
|
|
|
2015-09-12 22:06:06 +00:00
|
|
|
glUseProgram(0);
|
2015-09-27 18:51:40 +00:00
|
|
|
if (mainShaderProgram != UNINITIALIZED_GL) {
|
|
|
|
glDeleteProgram(mainShaderProgram);
|
|
|
|
mainShaderProgram = UNINITIALIZED_GL;
|
2015-09-12 22:06:06 +00:00
|
|
|
}
|
|
|
|
|
2015-04-12 22:35:16 +00:00
|
|
|
glnode_shutdownNodes();
|
2015-05-31 19:59:26 +00:00
|
|
|
LOG("Completed GLDriver shutdown ...");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void gldriver_shutdown(void) {
|
2015-09-12 22:06:06 +00:00
|
|
|
if (renderer_shutting_down) {
|
|
|
|
return;
|
|
|
|
}
|
2015-05-31 19:59:26 +00:00
|
|
|
#if USE_GLUT
|
|
|
|
glutLeaveMainLoop();
|
2015-09-11 07:00:04 +00:00
|
|
|
#endif
|
2015-07-05 20:40:50 +00:00
|
|
|
renderer_shutting_down = true;
|
2015-07-05 20:29:35 +00:00
|
|
|
_gldriver_shutdown();
|
2014-09-17 03:49:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
//
|
2014-09-27 18:03:51 +00:00
|
|
|
// update, render, reshape
|
2014-09-17 03:49:57 +00:00
|
|
|
//
|
2014-10-25 17:18:22 +00:00
|
|
|
#if USE_GLUT
|
2015-01-15 07:17:50 +00:00
|
|
|
static void gldriver_update(int unused) {
|
2016-01-02 21:10:02 +00:00
|
|
|
#if FPS_LOG
|
2015-01-18 19:10:16 +00:00
|
|
|
static uint32_t prevCount = 0;
|
|
|
|
static uint32_t idleCount = 0;
|
|
|
|
|
|
|
|
idleCount++;
|
|
|
|
|
|
|
|
static struct timespec prev = { 0 };
|
|
|
|
struct timespec now;
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
|
|
|
|
|
|
|
if (now.tv_sec != prev.tv_sec) {
|
|
|
|
LOG("gldriver_update() : %u", idleCount-prevCount);
|
|
|
|
prevCount = idleCount;
|
|
|
|
prev = now;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2014-09-22 00:26:37 +00:00
|
|
|
c_keys_handle_input(-1, 0, 0);
|
2015-02-25 04:53:19 +00:00
|
|
|
glutPostRedisplay();
|
2015-01-15 07:17:50 +00:00
|
|
|
glutTimerFunc(17, gldriver_update, 0);
|
2014-09-17 03:49:57 +00:00
|
|
|
}
|
2014-10-25 17:18:22 +00:00
|
|
|
#endif
|
2014-09-17 03:49:57 +00:00
|
|
|
|
2014-09-27 18:03:51 +00:00
|
|
|
static void gldriver_render(void) {
|
2015-10-04 21:21:28 +00:00
|
|
|
SCOPE_TRACE_VIDEO("glvideo render");
|
|
|
|
|
2015-01-24 07:37:43 +00:00
|
|
|
const uint8_t * const fb = video_current_framebuffer();
|
|
|
|
if (UNLIKELY(!fb)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-07-05 20:40:50 +00:00
|
|
|
if (UNLIKELY(renderer_shutting_down)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-11-07 06:33:15 +00:00
|
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
2015-03-11 19:54:50 +00:00
|
|
|
#if MOBILE_DEVICE
|
2015-10-01 04:58:33 +00:00
|
|
|
glViewport(viewportX, viewportY, viewportWidth, viewportHeight);
|
2015-03-11 19:54:50 +00:00
|
|
|
#endif
|
2014-09-17 03:49:57 +00:00
|
|
|
|
2014-09-27 18:03:51 +00:00
|
|
|
#if PERSPECTIVE
|
|
|
|
// Calculate modelview and projection matrices
|
|
|
|
GLfloat modelView[16];
|
|
|
|
GLfloat projection[16];
|
|
|
|
mtxLoadPerspective(projection, 90, (float)viewportWidth / (float)viewportHeight, 5.0, 10000);
|
|
|
|
#endif
|
2014-09-17 03:49:57 +00:00
|
|
|
|
2014-09-27 18:03:51 +00:00
|
|
|
// Calculate the modelview matrix to render our character
|
|
|
|
// at the proper position and rotation
|
|
|
|
#if PERSPECTIVE
|
2016-01-03 19:59:11 +00:00
|
|
|
GLfloat mvp[16];
|
2014-09-27 18:03:51 +00:00
|
|
|
// Create model-view-projection matrix
|
|
|
|
//mtxLoadTranslate(modelView, 0, 150, -450);
|
|
|
|
//mtxRotateXApply(modelView, -90.0f);
|
|
|
|
//mtxRotateApply(modelView, -45.0f, 0.7, 0.3, 1);
|
|
|
|
mtxMultiply(mvp, projection, modelView);
|
2014-09-17 03:49:57 +00:00
|
|
|
|
2014-09-27 18:03:51 +00:00
|
|
|
// Have our shader use the modelview projection matrix
|
|
|
|
// that we calculated above
|
|
|
|
glUniformMatrix4fv(uniformMVPIdx, 1, GL_FALSE, mvp);
|
2016-01-03 19:59:11 +00:00
|
|
|
#else
|
|
|
|
// Just load an identity matrix for a pure orthographic/non-perspective viewing
|
|
|
|
glUniformMatrix4fv(uniformMVPIdx, 1, GL_FALSE, mvpIdentity);
|
|
|
|
#endif
|
2014-09-27 18:03:51 +00:00
|
|
|
|
2015-07-12 20:59:44 +00:00
|
|
|
unsigned long wasDirty = video_clearDirty();
|
|
|
|
|
2016-01-02 21:10:02 +00:00
|
|
|
char *pixels = (char *)crtModel->texPixels;
|
2015-07-12 20:59:44 +00:00
|
|
|
if (wasDirty) {
|
2015-10-04 21:21:28 +00:00
|
|
|
SCOPE_TRACE_VIDEO("pixel convert");
|
2015-02-25 04:53:19 +00:00
|
|
|
// Update texture from indexed-color Apple //e internal framebuffer
|
|
|
|
unsigned int count = SCANWIDTH * SCANHEIGHT;
|
2015-05-30 21:50:51 +00:00
|
|
|
for (unsigned int i=0, j=0; i<count; i++, j+=sizeof(PIXEL_TYPE)) {
|
2015-02-25 04:53:19 +00:00
|
|
|
uint8_t index = *(fb + i);
|
2015-05-30 21:50:51 +00:00
|
|
|
*( (PIXEL_TYPE*)(pixels + j) ) = (PIXEL_TYPE)(
|
|
|
|
((PIXEL_TYPE)(colormap[index].red) << SHIFT_R) |
|
|
|
|
((PIXEL_TYPE)(colormap[index].green) << SHIFT_G) |
|
|
|
|
((PIXEL_TYPE)(colormap[index].blue) << SHIFT_B) |
|
2015-05-31 02:08:00 +00:00
|
|
|
((PIXEL_TYPE)MAX_SATURATION << SHIFT_A)
|
2015-02-25 04:53:19 +00:00
|
|
|
);
|
|
|
|
}
|
2014-09-17 03:49:57 +00:00
|
|
|
}
|
|
|
|
|
2015-03-23 01:53:13 +00:00
|
|
|
glActiveTexture(TEXTURE_ACTIVE_FRAMEBUFFER);
|
2016-01-02 21:10:02 +00:00
|
|
|
glBindTexture(GL_TEXTURE_2D, crtModel->textureName);
|
2015-09-27 18:45:37 +00:00
|
|
|
glUniform1i(texSamplerLoc, TEXTURE_ID_FRAMEBUFFER);
|
2015-07-12 20:59:44 +00:00
|
|
|
if (wasDirty) {
|
2015-10-04 21:21:28 +00:00
|
|
|
SCOPE_TRACE_VIDEO("glvideo texImage2D");
|
2016-01-02 21:10:02 +00:00
|
|
|
_HACKAROUND_GLTEXIMAGE2D_PRE(TEXTURE_ACTIVE_FRAMEBUFFER, crtModel->textureName);
|
2015-05-31 02:08:00 +00:00
|
|
|
glTexImage2D(GL_TEXTURE_2D, /*level*/0, TEX_FORMAT_INTERNAL, SCANWIDTH, SCANHEIGHT, /*border*/0, TEX_FORMAT, TEX_TYPE, (GLvoid *)&pixels[0]);
|
2015-02-25 04:53:19 +00:00
|
|
|
}
|
2014-09-17 03:49:57 +00:00
|
|
|
|
2014-09-27 18:03:51 +00:00
|
|
|
// Bind our vertex array object
|
2015-02-18 03:54:47 +00:00
|
|
|
#if USE_VAO
|
2016-01-02 21:10:02 +00:00
|
|
|
glBindVertexArray(crtModel->vaoName);
|
2015-02-18 03:54:47 +00:00
|
|
|
#else
|
2016-01-02 21:10:02 +00:00
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, crtModel->posBufferName);
|
2015-02-18 03:54:47 +00:00
|
|
|
|
2015-04-12 18:27:33 +00:00
|
|
|
GLsizei posTypeSize = getGLTypeSize(crtModel->positionType);
|
|
|
|
GLsizei texcoordTypeSize = getGLTypeSize(crtModel->texcoordType);
|
2015-02-18 03:54:47 +00:00
|
|
|
|
|
|
|
// Set up parmeters for position attribute in the VAO including, size, type, stride, and offset in the currenly
|
|
|
|
// bound VAO This also attaches the position VBO to the VAO
|
|
|
|
glVertexAttribPointer(POS_ATTRIB_IDX, // What attibute index will this array feed in the vertex shader (see buildProgram)
|
|
|
|
crtModel->positionSize, // How many elements are there per position?
|
|
|
|
crtModel->positionType, // What is the type of this data?
|
|
|
|
GL_FALSE, // Do we want to normalize this data (0-1 range for fixed-pont types)
|
|
|
|
crtModel->positionSize*posTypeSize, // What is the stride (i.e. bytes between positions)?
|
|
|
|
0); // What is the offset in the VBO to the position data?
|
|
|
|
glEnableVertexAttribArray(POS_ATTRIB_IDX);
|
|
|
|
|
|
|
|
// Set up parmeters for texcoord attribute in the VAO including, size, type, stride, and offset in the currenly
|
|
|
|
// bound VAO This also attaches the texcoord VBO to VAO
|
2016-01-02 21:10:02 +00:00
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, crtModel->texcoordBufferName);
|
2015-02-18 03:54:47 +00:00
|
|
|
glVertexAttribPointer(TEXCOORD_ATTRIB_IDX, // What attibute index will this array feed in the vertex shader (see buildProgram)
|
|
|
|
crtModel->texcoordSize, // How many elements are there per texture coord?
|
|
|
|
crtModel->texcoordType, // What is the type of this data in the array?
|
|
|
|
GL_TRUE, // Do we want to normalize this data (0-1 range for fixed-point types)
|
|
|
|
crtModel->texcoordSize*texcoordTypeSize, // What is the stride (i.e. bytes between texcoords)?
|
|
|
|
0); // What is the offset in the VBO to the texcoord data?
|
|
|
|
glEnableVertexAttribArray(TEXCOORD_ATTRIB_IDX);
|
2015-03-23 01:53:13 +00:00
|
|
|
|
2016-01-02 21:10:02 +00:00
|
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, crtModel->elementBufferName);
|
2015-02-18 03:54:47 +00:00
|
|
|
#endif
|
2014-09-27 18:03:51 +00:00
|
|
|
|
2015-03-23 01:53:13 +00:00
|
|
|
glUniform1f(alphaValue, 1.0);
|
|
|
|
|
2014-09-27 18:03:51 +00:00
|
|
|
// Cull back faces now that we no longer render
|
|
|
|
// with an inverted matrix
|
2015-03-23 01:53:13 +00:00
|
|
|
//glCullFace(GL_BACK);
|
2014-09-17 03:49:57 +00:00
|
|
|
|
2015-03-23 01:53:13 +00:00
|
|
|
// Draw the CRT object and others
|
2015-10-01 04:55:07 +00:00
|
|
|
_HACKAROUND_GLDRAW_PRE();
|
2016-01-02 21:10:02 +00:00
|
|
|
glDrawElements(GL_TRIANGLES, crtModel->numElements, crtModel->elementType, 0);
|
2014-10-08 05:05:14 +00:00
|
|
|
|
2015-04-12 22:35:16 +00:00
|
|
|
// Render HUD nodes
|
|
|
|
glnode_renderNodes();
|
2015-03-23 01:53:13 +00:00
|
|
|
|
2014-10-08 05:05:14 +00:00
|
|
|
#if USE_GLUT
|
|
|
|
glutSwapBuffers();
|
|
|
|
#endif
|
2015-03-23 01:53:13 +00:00
|
|
|
|
|
|
|
GL_ERRLOG("gldriver_render");
|
2014-09-17 03:49:57 +00:00
|
|
|
}
|
|
|
|
|
2014-09-21 22:58:27 +00:00
|
|
|
static void gldriver_reshape(int w, int h) {
|
2014-09-27 18:03:51 +00:00
|
|
|
//LOG("reshape to w:%d h:%d", w, h);
|
2015-10-01 04:58:33 +00:00
|
|
|
#if USE_GLUT
|
2014-09-17 03:49:57 +00:00
|
|
|
windowWidth = w;
|
|
|
|
windowHeight = h;
|
2015-10-01 04:58:33 +00:00
|
|
|
#endif
|
2014-09-17 03:49:57 +00:00
|
|
|
|
2015-03-11 19:54:50 +00:00
|
|
|
#if MOBILE_DEVICE
|
|
|
|
int viewportHeightPrevious = viewportHeight;
|
|
|
|
#endif
|
|
|
|
|
2014-09-17 03:49:57 +00:00
|
|
|
int w2 = ((float)h * (SCANWIDTH/(float)SCANHEIGHT));
|
|
|
|
int h2 = ((float)w / (SCANWIDTH/(float)SCANHEIGHT));
|
|
|
|
|
|
|
|
if (w2 <= w) {
|
|
|
|
// h is priority
|
|
|
|
viewportX = (w-w2)/2;
|
|
|
|
viewportY = 0;
|
|
|
|
viewportWidth = w2;
|
|
|
|
viewportHeight = h;
|
2014-09-27 18:03:51 +00:00
|
|
|
//LOG("OK1 : x:%d,y:%d w:%d,h:%d", viewportX, viewportY, viewportWidth, viewportHeight);
|
2014-09-17 03:49:57 +00:00
|
|
|
} else if (h2 <= h) {
|
|
|
|
viewportX = 0;
|
|
|
|
viewportY = (h-h2)/2;
|
|
|
|
viewportWidth = w;
|
|
|
|
viewportHeight = h2;
|
2014-09-27 18:03:51 +00:00
|
|
|
//LOG("OK2 : x:%d,y:%d w:%d,h:%d", viewportX, viewportY, viewportWidth, viewportHeight);
|
2014-09-17 03:49:57 +00:00
|
|
|
} else {
|
|
|
|
viewportX = 0;
|
|
|
|
viewportY = 0;
|
|
|
|
viewportWidth = w;
|
|
|
|
viewportHeight = h;
|
2014-09-27 18:03:51 +00:00
|
|
|
//LOG("small viewport : x:%d,y:%d w:%d,h:%d", viewportX, viewportY, viewportWidth, viewportHeight);
|
2014-09-17 03:49:57 +00:00
|
|
|
}
|
|
|
|
|
2014-09-27 18:03:51 +00:00
|
|
|
glViewport(viewportX, viewportY, viewportWidth, viewportHeight);
|
2015-04-02 02:59:38 +00:00
|
|
|
|
2015-04-12 22:35:16 +00:00
|
|
|
// Reshape HUD nodes
|
|
|
|
glnode_reshapeNodes(w, h);
|
2014-09-17 03:49:57 +00:00
|
|
|
}
|
|
|
|
|
2014-09-27 18:03:51 +00:00
|
|
|
#if USE_GLUT
|
2016-01-02 21:10:02 +00:00
|
|
|
static void gldriver_init_glut(void) {
|
2014-09-17 03:49:57 +00:00
|
|
|
glutInit(&argc, argv);
|
2015-11-07 06:33:15 +00:00
|
|
|
glutInitDisplayMode(/*GLUT_DOUBLE|*/GLUT_RGBA);
|
2014-09-17 03:49:57 +00:00
|
|
|
glutInitWindowSize(windowWidth, windowHeight);
|
|
|
|
//glutInitContextVersion(4, 0); -- Is this needed?
|
|
|
|
glutInitContextProfile(GLUT_CORE_PROFILE);
|
2014-12-21 21:51:45 +00:00
|
|
|
glutWindow = glutCreateWindow(PACKAGE_NAME);
|
2014-09-17 03:49:57 +00:00
|
|
|
GL_ERRQUIT("GLUT initialization");
|
|
|
|
|
|
|
|
if (glewInit()) {
|
|
|
|
ERRQUIT("Unable to initialize GLEW");
|
|
|
|
}
|
|
|
|
|
2014-09-21 22:58:27 +00:00
|
|
|
gldriver_init_common();
|
2014-09-17 03:49:57 +00:00
|
|
|
|
2015-01-15 07:17:50 +00:00
|
|
|
glutTimerFunc(16, gldriver_update, 0);
|
2014-09-27 18:03:51 +00:00
|
|
|
glutDisplayFunc(gldriver_render);
|
2014-09-21 22:58:27 +00:00
|
|
|
glutReshapeFunc(gldriver_reshape);
|
2015-09-12 22:06:06 +00:00
|
|
|
glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_GLUTMAINLOOP_RETURNS);
|
2014-09-17 03:49:57 +00:00
|
|
|
|
2015-02-16 02:32:45 +00:00
|
|
|
#if !TESTING
|
2014-09-21 22:58:27 +00:00
|
|
|
glutKeyboardFunc(gldriver_on_key_down);
|
|
|
|
glutKeyboardUpFunc(gldriver_on_key_up);
|
|
|
|
glutSpecialFunc(gldriver_on_key_special_down);
|
|
|
|
glutSpecialUpFunc(gldriver_on_key_special_up);
|
2014-11-22 22:08:53 +00:00
|
|
|
//glutMouseFunc(gldriver_mouse);
|
|
|
|
//glutMotionFunc(gldriver_mouse_drag);
|
2014-09-17 03:49:57 +00:00
|
|
|
#endif
|
2014-09-27 18:03:51 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
2015-03-23 01:53:13 +00:00
|
|
|
// backend renderer API
|
2014-09-27 18:03:51 +00:00
|
|
|
|
2016-01-02 21:10:02 +00:00
|
|
|
static void gldriver_init(void *unused) {
|
2014-11-29 21:31:21 +00:00
|
|
|
safe_to_do_opengl_logging = true;
|
2015-09-12 22:06:06 +00:00
|
|
|
#if defined(__APPLE__) || defined(ANDROID)
|
2015-02-18 03:54:47 +00:00
|
|
|
gldriver_init_common();
|
2014-09-27 18:03:51 +00:00
|
|
|
#elif USE_GLUT
|
2016-01-02 21:10:02 +00:00
|
|
|
gldriver_init_glut();
|
2014-09-27 18:03:51 +00:00
|
|
|
#else
|
|
|
|
#error no working codepaths
|
|
|
|
#endif
|
2015-04-12 22:35:16 +00:00
|
|
|
glnode_setupNodes();
|
2014-09-17 03:49:57 +00:00
|
|
|
}
|
|
|
|
|
2015-03-23 01:53:13 +00:00
|
|
|
static void gldriver_main_loop(void) {
|
2014-09-27 18:03:51 +00:00
|
|
|
#if USE_GLUT
|
2014-09-17 03:49:57 +00:00
|
|
|
glutMainLoop();
|
2015-05-31 19:59:26 +00:00
|
|
|
LOG("GLUT main loop finished...");
|
2014-09-27 18:03:51 +00:00
|
|
|
#endif
|
2015-03-23 01:53:13 +00:00
|
|
|
// fall through if not GLUT
|
2014-09-27 18:03:51 +00:00
|
|
|
}
|
|
|
|
|
2015-04-18 05:12:13 +00:00
|
|
|
__attribute__((constructor(CTOR_PRIORITY_EARLY)))
|
2015-04-18 04:40:56 +00:00
|
|
|
static void _init_glvideo(void) {
|
|
|
|
LOG("Initializing OpenGL renderer");
|
|
|
|
|
2015-06-23 05:21:27 +00:00
|
|
|
assert((video_backend == NULL) && "there can only be one!");
|
|
|
|
|
2015-04-18 04:40:56 +00:00
|
|
|
glvideo_backend.init = &gldriver_init;
|
|
|
|
glvideo_backend.main_loop = &gldriver_main_loop;
|
|
|
|
glvideo_backend.reshape = &gldriver_reshape;
|
|
|
|
glvideo_backend.render = &gldriver_render;
|
|
|
|
glvideo_backend.shutdown = &gldriver_shutdown;
|
|
|
|
|
|
|
|
video_backend = &glvideo_backend;
|
2014-09-17 03:49:57 +00:00
|
|
|
}
|
2014-11-22 22:08:53 +00:00
|
|
|
|