mirror of
https://github.com/mauiaaron/apple2.git
synced 2025-01-10 23:29:43 +00:00
First cut at basic OpenGLES 2.0 renderer
* Compiles/runs on Linux * X11 renderer is now deprecated
This commit is contained in:
parent
eb2e038bf0
commit
df8ae3d018
17
Makefile.am
17
Makefile.am
@ -35,7 +35,8 @@ INTERFACE_CLASSIC_SRC = \
|
||||
src/interface.c
|
||||
|
||||
VIDEO_SRC = \
|
||||
src/video/xvideo.c
|
||||
src/video/xvideo.c \
|
||||
src/video/glvideo.c
|
||||
|
||||
AUDIO_SRC = \
|
||||
src/audio/soundcore.c src/audio/soundcore-openal.c src/audio/speaker.c \
|
||||
@ -90,7 +91,7 @@ A2_TEST_CFLAGS = -DTESTING=1 -Isrc/test
|
||||
TESTS = testcpu testdisplay testvm
|
||||
check_PROGRAMS = testcpu testdisplay testvm
|
||||
|
||||
testcpu_SOURCES = src/test/testcpu.c $(A2_TEST_SOURCES) $(META_SRC) $(VIDEO_SRC)
|
||||
testcpu_SOURCES = src/test/testcpu.c $(A2_TEST_SOURCES) $(META_SRC)
|
||||
testcpu_CFLAGS = $(apple2ix_CFLAGS) $(A2_TEST_CFLAGS) -UAUDIO_ENABLED -UINTERFACE_CLASSIC -DHEADLESS=1
|
||||
testcpu_LDFLAGS = $(apple2ix_LDFLAGS)
|
||||
testcpu_LDADD = @ASM_O@
|
||||
@ -98,23 +99,23 @@ testcpu_DEPENDENCIES = @ASM_O@ @META_O@
|
||||
|
||||
EXTRA_testcpu_SOURCES = $(ASM_SRC_x86)
|
||||
|
||||
testdisplay_SOURCES = src/test/testdisplay.c $(A2_TEST_SOURCES) $(META_SRC) $(VIDEO_SRC)
|
||||
testdisplay_SOURCES = src/test/testdisplay.c $(A2_TEST_SOURCES) $(META_SRC)
|
||||
# HACK FIXME TODO NOTE: why don't these CFLAGS here pass down to the .S and .c files in the subdirectories?
|
||||
testdisplay_CFLAGS = $(apple2ix_CFLAGS) $(A2_TEST_CFLAGS) -UAUDIO_ENABLED -UINTERFACE_CLASSIC -DHEADLESS=0
|
||||
testdisplay_LDFLAGS = $(apple2ix_LDFLAGS)
|
||||
testdisplay_LDADD = @ASM_O@
|
||||
testdisplay_LDADD = @ASM_O@ @VIDEO_O@
|
||||
testdisplay_DEPENDENCIES = @ASM_O@ @META_O@ @VIDEO_O@
|
||||
|
||||
EXTRA_testdisplay_SOURCES = $(ASM_SRC_x86)
|
||||
EXTRA_testdisplay_SOURCES = $(ASM_SRC_x86) $(VIDEO_SRC)
|
||||
|
||||
testvm_SOURCES = src/test/testvm.c $(A2_TEST_SOURCES) $(META_SRC) $(VIDEO_SRC)
|
||||
testvm_SOURCES = src/test/testvm.c $(A2_TEST_SOURCES) $(META_SRC)
|
||||
# HACK FIXME TODO NOTE: why don't these CFLAGS here pass down to the .S and .c files in the subdirectories?
|
||||
testvm_CFLAGS = $(apple2ix_CFLAGS) $(A2_TEST_CFLAGS) -UAUDIO_ENABLED -UINTERFACE_CLASSIC -DHEADLESS=0
|
||||
testvm_LDFLAGS = $(apple2ix_LDFLAGS)
|
||||
testvm_LDADD = @ASM_O@
|
||||
testvm_LDADD = @ASM_O@ @VIDEO_O@
|
||||
testvm_DEPENDENCIES = @ASM_O@ @META_O@ @VIDEO_O@
|
||||
|
||||
EXTRA_testvm_SOURCES = $(ASM_SRC_x86)
|
||||
EXTRA_testvm_SOURCES = $(ASM_SRC_x86) $(VIDEO_SRC)
|
||||
|
||||
###############################################################################
|
||||
# Misc
|
||||
|
71
configure.ac
71
configure.ac
@ -120,7 +120,7 @@ AM_PROG_LEX
|
||||
dnl AS_IF([test "x$LEX" = "xno"], [
|
||||
dnl AC_MSG_ERROR([Emulator needs lex/flex to build source...])
|
||||
dnl ], [
|
||||
dnl AC_MSG_WARN([Found lex $LEX])
|
||||
dnl AC_MSG_RESULT([Found lex $LEX])
|
||||
dnl ])
|
||||
|
||||
dnl POSIX high-precision clock
|
||||
@ -142,29 +142,70 @@ AC_SEARCH_LIBS(pthread_create, pthread, [], [
|
||||
AC_MSG_ERROR([Emulator requires pthread library to build...])
|
||||
], [])
|
||||
|
||||
AC_SEARCH_LIBS(sqrtf, m, [], [
|
||||
AC_MSG_ERROR([Emulator requires math library to build...])
|
||||
], [])
|
||||
|
||||
|
||||
dnl ---------------------------------------------------------------------------
|
||||
dnl Video ...
|
||||
|
||||
dnl currently X11 is required ...
|
||||
AC_PATH_XTRA
|
||||
|
||||
AC_CHECK_HEADER(X11/XKBlib.h, [], [
|
||||
AC_MSG_ERROR([Emulator needs X11 headers to build this software...])
|
||||
])
|
||||
AC_SEARCH_LIBS(XPutImage, [X11], [], [
|
||||
AC_MSG_ERROR([Emulator need X11 libraries to build the emulator...])
|
||||
], [-LX11])
|
||||
|
||||
AC_SEARCH_LIBS(XShmAttach, Xext, [
|
||||
AC_DEFINE(HAVE_X11_SHM, 1, [Enable X11 MIT SHM extension])
|
||||
opengl_selected='yes'
|
||||
AC_ARG_ENABLE([opengl], AS_HELP_STRING([--disable-opengl], [Disable OpenGL video driver (uses regular X11)]), [
|
||||
opengl_selected='no'
|
||||
], [
|
||||
AC_MSG_WARN([Building emulator without support of X11 MITSHM extension...])
|
||||
], [-lX11])
|
||||
AC_CHECK_HEADER(GL/glew.h, [
|
||||
AC_CHECK_HEADER(GL/freeglut.h, [
|
||||
AC_SEARCH_LIBS(glCreateProgram, [GL], [
|
||||
AC_SEARCH_LIBS(glutMainLoop, [glut freeglut], [
|
||||
AC_SEARCH_LIBS(glewInit, [GLEW glew], [
|
||||
opengl_supported='yes'
|
||||
AC_DEFINE(VIDEO_OPENGL, 1, [Use OpenGL])
|
||||
VIDEO_O="src/video/glvideo.o"
|
||||
AC_MSG_RESULT([Building emulator with OpenGL support, w00t!])
|
||||
], [
|
||||
AC_MSG_WARN([Did not find OpenGL GLEW library...])
|
||||
], [-lGL -lGLEW -lglut])
|
||||
], [
|
||||
AC_MSG_WARN([Did not find glut library...])
|
||||
], [-lGL -lGLEW -lglut])
|
||||
], [
|
||||
AC_MSG_WARN([Did not find OpenGL library...])
|
||||
], [-lGL])
|
||||
], [
|
||||
AC_MSG_WARN([Did not find GL/freeglut.h header ...])
|
||||
])
|
||||
], [
|
||||
AC_MSG_WARN([Did not find GL/glew.h header ...])
|
||||
])
|
||||
])
|
||||
|
||||
AS_IF([test "x$opengl_supported" = "xyes"], [
|
||||
], [
|
||||
dnl OpenGL not supported
|
||||
AS_IF([test "x$opengl_selected" = "xyes"], [
|
||||
AC_MSG_WARN([Did not find OpenGL libraries, will attempt to build legacy X11 variant ...])
|
||||
], [])
|
||||
|
||||
AC_CHECK_HEADER(X11/XKBlib.h, [
|
||||
AC_SEARCH_LIBS(XPutImage, [X11], [
|
||||
AC_SEARCH_LIBS(XShmAttach, Xext, [
|
||||
AC_DEFINE(HAVE_X11_SHM, 1, [Enable X11 MIT SHM extension])
|
||||
], [
|
||||
AC_MSG_WARN([Building emulator without support of X11 MITSHM extension...])
|
||||
], [-lX11])
|
||||
VIDEO_O="src/video/xvideo.o"
|
||||
], [
|
||||
AC_MSG_ERROR([Did not find OpenGL nor X11 libraries...])
|
||||
], [-LX11])
|
||||
], [
|
||||
AC_MSG_ERROR([Did not find OpenGL nor X11 headers...])
|
||||
])
|
||||
])
|
||||
|
||||
VIDEO_O="src/video/xvideo.o"
|
||||
AC_SUBST(VIDEO_O)
|
||||
|
||||
AC_DEFINE(HEADLESS, 0, [Set to 1 to disable video output driver])
|
||||
|
||||
dnl ---------------------------------------------------------------------------
|
||||
|
64
src/common.h
64
src/common.h
@ -54,6 +54,14 @@
|
||||
#include "uthash.h"
|
||||
#include "zlib-helpers.h"
|
||||
|
||||
|
||||
#if VIDEO_OPENGL
|
||||
#include "video/vgl.h"
|
||||
#else
|
||||
#define GLint int
|
||||
#define glGetError() 0
|
||||
#endif
|
||||
|
||||
#ifdef DEBUGGER
|
||||
#include "meta/debug.h"
|
||||
#endif
|
||||
@ -75,35 +83,60 @@
|
||||
extern bool do_logging;
|
||||
extern FILE *error_log;
|
||||
|
||||
#define QUIT_FUNCTION(x) exit(x)
|
||||
|
||||
#define _LOG(...) \
|
||||
int saverr = errno; \
|
||||
int _err = errno; \
|
||||
errno = 0; \
|
||||
fprintf(error_log, "%s:%d - ", __FILE__, __LINE__); \
|
||||
fprintf(error_log, __VA_ARGS__); \
|
||||
if (saverr) { \
|
||||
fprintf(error_log, " (syserr: %s)", strerror(saverr)); \
|
||||
if (_err) { \
|
||||
fprintf(error_log, " (syserr: %s)", strerror(_err)); \
|
||||
} \
|
||||
if (_glerr) { \
|
||||
fprintf(error_log, " (OOPS glerr:%04X)", _glerr); \
|
||||
} \
|
||||
fprintf(error_log, "\n");
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
||||
#define ERRLOG(...) \
|
||||
if (do_logging) { \
|
||||
_LOG(__VA_ARGS__); \
|
||||
}
|
||||
|
||||
#define ERRQUIT(...) \
|
||||
if (do_logging) { \
|
||||
_LOG(__VA_ARGS__); \
|
||||
} \
|
||||
exit(1);
|
||||
|
||||
#define LOG(...) \
|
||||
if (do_logging) { \
|
||||
errno = 0; \
|
||||
GLint _glerr = 0; \
|
||||
_LOG(__VA_ARGS__); \
|
||||
}
|
||||
|
||||
#define ERRLOG(...) \
|
||||
if (do_logging) { \
|
||||
GLint _glerr = glGetError(); \
|
||||
_LOG(__VA_ARGS__); \
|
||||
}
|
||||
|
||||
#define GL_ERRLOG(...) \
|
||||
if (do_logging) { \
|
||||
GLint _glerr = glGetError(); \
|
||||
if (_glerr) { \
|
||||
_LOG(__VA_ARGS__); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define ERRQUIT(...) \
|
||||
do { \
|
||||
GLint _glerr = glGetError(); \
|
||||
_LOG(__VA_ARGS__); \
|
||||
QUIT_FUNCTION(1); \
|
||||
} while(0)
|
||||
|
||||
#define GL_ERRQUIT(...) \
|
||||
do { \
|
||||
GLint _glerr = glGetError(); \
|
||||
if (_glerr) { \
|
||||
_LOG( __VA_ARGS__); \
|
||||
QUIT_FUNCTION(_glerr); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#else // NDEBUG
|
||||
|
||||
#define ERRLOG(...) \
|
||||
@ -115,7 +148,7 @@ extern FILE *error_log;
|
||||
#define LOG(...) \
|
||||
do { } while(0);
|
||||
|
||||
#endif
|
||||
#endif // NDEBUG
|
||||
|
||||
#define RELEASE_ERRLOG(...) \
|
||||
do { \
|
||||
@ -124,6 +157,7 @@ extern FILE *error_log;
|
||||
|
||||
#define RELEASE_LOG(...) \
|
||||
do { \
|
||||
GLint _glerr = glGetError(); \
|
||||
errno = 0; \
|
||||
_LOG(__VA_ARGS__); \
|
||||
} while(0);
|
||||
|
13
src/video/Basic.frag
Normal file
13
src/video/Basic.frag
Normal file
@ -0,0 +1,13 @@
|
||||
static const char * fragmentShader =
|
||||
"#version 100\n"
|
||||
|
||||
"varying lowp vec4 DestinationColor;\n"
|
||||
"varying mediump vec2 TextureCoordOut;\n"
|
||||
|
||||
"uniform sampler2D Sampler;\n"
|
||||
|
||||
"void main(void) {\n"
|
||||
" //gl_FragColor = texture2D(Sampler, TextureCoordOut) * DestinationColor;\n"
|
||||
" gl_FragColor = texture2D(Sampler, TextureCoordOut);\n"
|
||||
"}"
|
||||
;
|
24
src/video/Basic.vert
Normal file
24
src/video/Basic.vert
Normal file
@ -0,0 +1,24 @@
|
||||
static const char * vertexShader =
|
||||
"#version 100\n"
|
||||
|
||||
"attribute vec4 Position;\n"
|
||||
"attribute vec2 TextureCoord;\n"
|
||||
"//attribute vec4 TestColor;\n"
|
||||
|
||||
"// base screen scolor\n"
|
||||
"uniform vec4 SourceColor;\n"
|
||||
|
||||
"uniform mat4 Projection;\n"
|
||||
"uniform mat4 Modelview;\n"
|
||||
|
||||
"varying vec4 DestinationColor;\n"
|
||||
"varying vec2 TextureCoordOut;\n"
|
||||
|
||||
"void main(void) {\n"
|
||||
" DestinationColor = SourceColor;\n"
|
||||
" //DestinationColor = TestColor;\n"
|
||||
" //gl_Position = Projection * Modelview * Position;\n"
|
||||
" gl_Position = Position;\n"
|
||||
" TextureCoordOut = TextureCoord;\n"
|
||||
"}\n"
|
||||
;
|
702
src/video/glvideo.c
Normal file
702
src/video/glvideo.c
Normal file
@ -0,0 +1,702 @@
|
||||
/*
|
||||
* Apple // emulator for *nix
|
||||
*
|
||||
* This software package is subject to the GNU General Public License
|
||||
* version 2 or later (your choice) as published by the Free Software
|
||||
* Foundation.
|
||||
*
|
||||
* THERE ARE NO WARRANTIES WHATSOEVER.
|
||||
*
|
||||
*/
|
||||
|
||||
// glvideo -- Created by Aaron Culliney
|
||||
|
||||
#include "common.h"
|
||||
#include "video/vgl.h"
|
||||
#if 0
|
||||
#include "video/matmath.h"
|
||||
#endif
|
||||
|
||||
#include "video/Basic.vert"
|
||||
#include "video/Basic.frag"
|
||||
|
||||
#define DEBUG_GEOMETRY 0
|
||||
|
||||
typedef struct UniformHandles {
|
||||
GLuint modelview;
|
||||
GLuint projection;
|
||||
GLuint sourceColor;
|
||||
} UniformHandles;
|
||||
|
||||
typedef struct AttributeHandles {
|
||||
GLuint position;
|
||||
GLuint texCoord;
|
||||
#if DEBUG_GEOMETRY
|
||||
GLuint testColor;
|
||||
#endif
|
||||
} AttributeHandles;
|
||||
|
||||
typedef struct Drawable {
|
||||
GLuint vertexBuffer;
|
||||
GLuint indexBuffer;
|
||||
int vertexCount;
|
||||
int indexCount;
|
||||
bool allocated;
|
||||
} Drawable;
|
||||
|
||||
typedef struct ShaderInfo {
|
||||
GLuint shader;
|
||||
GLenum type;// GL shader type
|
||||
const char *shadername;
|
||||
const GLchar *source;
|
||||
UT_hash_handle hh;
|
||||
} ShaderInfo;
|
||||
|
||||
static GLuint colorRenderbuffer = 0;
|
||||
static int windowWidth = SCANWIDTH*1.5;
|
||||
static int windowHeight = SCANHEIGHT*1.5;
|
||||
|
||||
static int viewportX = 0;
|
||||
static int viewportY = 0;
|
||||
static int viewportWidth = SCANWIDTH*1.5;
|
||||
static int viewportHeight = SCANHEIGHT*1.5;
|
||||
//static GLuint depthRenderbuffer = 0;
|
||||
|
||||
#if 0
|
||||
static matT translation = {};
|
||||
#endif
|
||||
|
||||
static GLuint a2framebufferTexture = 0;
|
||||
static UniformHandles uniforms = { 0 };
|
||||
static AttributeHandles attributes = { 0 };
|
||||
static Drawable crtDrawable = { 0 };
|
||||
static ShaderInfo *allShaders = NULL;
|
||||
static GLuint program = 0;
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// compile/link/load shaders
|
||||
//
|
||||
|
||||
static GLuint _load_shaders(ShaderInfo *shaders) {
|
||||
if (shaders == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
GLuint program = glCreateProgram();
|
||||
if (!program) {
|
||||
GL_ERRLOG("glCreateProgram");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ShaderInfo *entry = shaders;
|
||||
while (entry->type != GL_NONE) {
|
||||
GLuint shader = glCreateShader(entry->type);
|
||||
entry->shader = shader;
|
||||
if (!shader || !entry->source) {
|
||||
for (entry = shaders; entry->type != GL_NONE; ++entry) {
|
||||
glDeleteShader(entry->shader);
|
||||
entry->shader = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
glShaderSource(shader, 1, &entry->source, NULL);
|
||||
glCompileShader( shader );
|
||||
GL_ERRLOG("glCompileShader");
|
||||
|
||||
GLint compiled = 0;
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
|
||||
if (!compiled) {
|
||||
GLsizei len = 0;
|
||||
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &len);
|
||||
GLchar* log = (GLchar*)malloc(sizeof(GLchar)*(len+1));
|
||||
glGetShaderInfoLog(shader, len, &len, log);
|
||||
ERRQUIT("Shader '%s' compilation failed: %s", entry->shadername, log);
|
||||
free(log);
|
||||
return 0;
|
||||
}
|
||||
|
||||
glAttachShader(program, shader);
|
||||
++entry;
|
||||
}
|
||||
|
||||
glLinkProgram(program);
|
||||
GL_ERRLOG("glLinkProgram");
|
||||
|
||||
GLint linked = 0;
|
||||
glGetProgramiv(program, GL_LINK_STATUS, &linked);
|
||||
if (!linked) {
|
||||
GLsizei len;
|
||||
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &len);
|
||||
GLchar* log = (GLchar*)malloc(sizeof(GLchar)*(len+1));
|
||||
glGetProgramInfoLog(program, len, &len, log);
|
||||
ERRQUIT("Shader '%s' linking failed: %s", entry->shadername, log);
|
||||
free(log);
|
||||
for (entry = shaders; entry->type != GL_NONE; ++entry) {
|
||||
glDeleteShader(entry->shader);
|
||||
entry->shader = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
static void _create_gl_program(void) {
|
||||
|
||||
ShaderInfo shaders[] = {
|
||||
{ .type=GL_VERTEX_SHADER, .shadername="vertex shader", .source=vertexShader },
|
||||
{ .type=GL_FRAGMENT_SHADER, .shadername="fragment shader", .source=fragmentShader },
|
||||
{ .type=GL_NONE, .shadername=NULL, .source=NULL }
|
||||
};
|
||||
program = _load_shaders(shaders);
|
||||
glUseProgram(program);
|
||||
GL_ERRLOG("glUseProgram");
|
||||
|
||||
ShaderInfo *info = calloc(1, sizeof(ShaderInfo));
|
||||
memcpy(info, &shaders[0], sizeof(ShaderInfo));
|
||||
HASH_ADD_INT(allShaders, /*index*/shader, /*new node*/info);
|
||||
|
||||
info = calloc(1, sizeof(ShaderInfo));
|
||||
memcpy(info, &shaders[1], sizeof(ShaderInfo));
|
||||
HASH_ADD_INT(allShaders, /*index*/shader, /*new node*/info);
|
||||
|
||||
// TODO : other shader effects, for example NTSC noise, CRT glitches, pixel bloom, etc ...
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// generate/manage vertices for the Cathode Ray Tube object
|
||||
//
|
||||
|
||||
static void _generate_crt_object(void) {
|
||||
// TODO FIXME: use actual 3D CRT surface with vertices/indices dynamically generated based on
|
||||
// screen dimensions / device DPI
|
||||
|
||||
if (crtDrawable.allocated) {
|
||||
glDeleteBuffers(1, &crtDrawable.vertexBuffer);
|
||||
glDeleteBuffers(1, &crtDrawable.indexBuffer);
|
||||
crtDrawable.allocated = false;
|
||||
}
|
||||
|
||||
#define STRIDE 9*sizeof(GLfloat)
|
||||
#define TEST_COLOR_OFF (GLvoid *)(3*sizeof(GLfloat))
|
||||
#define TEX_COORD_OFF (GLvoid *)(7*sizeof(GLfloat))
|
||||
|
||||
// NOTE: vertices in Normalized Device Coordinates
|
||||
const GLuint numverts = 6;
|
||||
GLfloat vrt[] = {
|
||||
-1.0, -1.0, 0.0, /*color*/1.0,1.0,1.0,1.0, /*interleaved tex coord:*/0.0, 1.0,// Triangle 1
|
||||
1.0, -1.0, 0.0, /*color*/1.0,1.0,1.0,1.0, /*interleaved tex coord:*/1.0, 1.0,
|
||||
-1.0, 1.0, 0.0, /*color*/1.0,1.0,1.0,1.0, /*interleaved tex coord:*/0.0, 0.0,
|
||||
-1.0, 1.0, 0.0, /*color*/1.0,1.0,1.0,1.0, /*interleaved tex coord:*/0.0, 0.0,// Triangle 2
|
||||
1.0, -1.0, 0.0, /*color*/1.0,1.0,1.0,1.0, /*interleaved tex coord:*/1.0, 1.0,
|
||||
1.0, 1.0, 0.0, /*color*/1.0,1.0,1.0,1.0, /*interleaved tex coord:*/1.0, 0.0,
|
||||
};
|
||||
const GLuint numindices = 6;
|
||||
const GLushort ind[] = {
|
||||
0, 1, 2, 3, 4, 5
|
||||
};
|
||||
|
||||
// Create VBO for the vertices
|
||||
glGenBuffers(1, &crtDrawable.vertexBuffer);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, crtDrawable.vertexBuffer);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(vrt), &vrt[0], GL_STATIC_DRAW);
|
||||
GL_ERRLOG("vertex buffer setup");
|
||||
|
||||
// Create VBO for the indices
|
||||
glGenBuffers(1, &crtDrawable.indexBuffer);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, crtDrawable.indexBuffer);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(ind), &ind[0], GL_STATIC_DRAW);
|
||||
GL_ERRLOG("element buffer setup");
|
||||
|
||||
crtDrawable.vertexCount = numverts;
|
||||
crtDrawable.indexCount = numindices;
|
||||
crtDrawable.allocated = true;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// initialization routines
|
||||
//
|
||||
|
||||
static void vdriver_init_common(void) {
|
||||
|
||||
glGenRenderbuffers(1, &colorRenderbuffer);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
|
||||
|
||||
_generate_crt_object();
|
||||
|
||||
// Extract width and height from the color buffer.
|
||||
int width, height;
|
||||
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &width);
|
||||
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &height);
|
||||
|
||||
#if 0
|
||||
// Create a depth buffer that has the same size as the color buffer.
|
||||
glGenRenderbuffers(1, &depthRenderbuffer);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbuffer);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);
|
||||
#endif
|
||||
|
||||
// Create the framebuffer object.
|
||||
GLuint framebuffer = 0;
|
||||
glGenFramebuffers(1, &framebuffer);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorRenderbuffer);
|
||||
//glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRenderbuffer);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
|
||||
GL_ERRLOG("renderbuffer setup");
|
||||
|
||||
// Create the GLSL program.
|
||||
_create_gl_program();
|
||||
|
||||
// Extract the handles to attributes and uniforms
|
||||
attributes.position = glGetAttribLocation(program, "Position");
|
||||
GL_ERRLOG("glGetAttribLocation");
|
||||
#if DEBUG_GEOMETRY
|
||||
attributes.testColor = glGetAttribLocation(program, "TestColor");
|
||||
GL_ERRQUIT("cannot get TestColor attribute location for DEBUG_GEOMETRY preprocessor configuration");
|
||||
#endif
|
||||
attributes.texCoord = glGetAttribLocation(program, "TextureCoord");
|
||||
uniforms.projection = glGetUniformLocation(program, "Projection");
|
||||
LOG("projection = %d", uniforms.projection);
|
||||
uniforms.modelview = glGetUniformLocation(program, "Modelview");
|
||||
LOG("modelview = %d", uniforms.modelview);
|
||||
uniforms.sourceColor = glGetUniformLocation(program, "SourceColor");
|
||||
LOG("sourceColor = %d", uniforms.sourceColor);
|
||||
|
||||
// Set up to use Apple //e framebuffer as texture
|
||||
glGenTextures(1, &a2framebufferTexture);
|
||||
GL_ERRLOG("glGenTextures");
|
||||
glBindTexture(GL_TEXTURE_2D, a2framebufferTexture);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR/*GL_NEAREST*/);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR/*GL_NEAREST*/);
|
||||
//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, /*level*/0, /*internal format*/GL_RGBA, width, height, /*border*/0, /*format*/GL_RGBA, GL_UNSIGNED_BYTE, 0);
|
||||
GL_ERRLOG("glTexImage2D");
|
||||
|
||||
// Initialize various state
|
||||
glEnableVertexAttribArray(attributes.position);
|
||||
GL_ERRLOG("glEnableVertexAttribArray");
|
||||
#if DEBUG_GEOMETRY
|
||||
glEnableVertexAttribArray(attributes.testColor);
|
||||
GL_ERRLOG("glEnableVertexAttribArray");
|
||||
#endif
|
||||
glEnableVertexAttribArray(attributes.texCoord);
|
||||
GL_ERRLOG("glEnableVertexAttribArray");
|
||||
|
||||
//glEnable(GL_DEPTH_TEST);
|
||||
glClearColor(0.2, 0.2f, 0.2f, 1);
|
||||
|
||||
#if 0
|
||||
// Set up transforms
|
||||
translation = mat4_translate_xyz(0.f, 0.f, -7.f, MAT4);
|
||||
#endif
|
||||
|
||||
#if !defined(__APPLE__)
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
GL_ERRLOG("bind main framebuffer");
|
||||
#endif
|
||||
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
if (status != GL_FRAMEBUFFER_COMPLETE) {
|
||||
ERRQUIT("framebuffer status: %04X", status);
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// keyboard
|
||||
//
|
||||
|
||||
// Map glut keys into Apple//ix internal-representation scancodes.
|
||||
static int _glutkey_to_scancode(int key) {
|
||||
static bool modified_caps_lock = false;
|
||||
|
||||
// NOTE : Unfortunately it appears that we cannot get a raw key down/up notification for CAPSlock, so hack that here
|
||||
// ALSO : Emulator initially sets CAPS state based on a user preference, but sync to system state if user changes it
|
||||
int modifiers = glutGetModifiers();
|
||||
if (!c_keys_is_shifted()) {
|
||||
if (modifiers & GLUT_ACTIVE_SHIFT) {
|
||||
modified_caps_lock = true;
|
||||
caps_lock = true;
|
||||
} else if (modified_caps_lock) {
|
||||
caps_lock = false;
|
||||
}
|
||||
}
|
||||
|
||||
switch (key) {
|
||||
case GLUT_KEY_F1:
|
||||
key = SCODE_F1;
|
||||
break;
|
||||
case GLUT_KEY_F2:
|
||||
key = SCODE_F2;
|
||||
break;
|
||||
case GLUT_KEY_F3:
|
||||
key = SCODE_F3;
|
||||
break;
|
||||
case GLUT_KEY_F4:
|
||||
key = SCODE_F4;
|
||||
break;
|
||||
case GLUT_KEY_F5:
|
||||
key = SCODE_F5;
|
||||
break;
|
||||
case GLUT_KEY_F6:
|
||||
key = SCODE_F6;
|
||||
break;
|
||||
case GLUT_KEY_F7:
|
||||
key = SCODE_F7;
|
||||
break;
|
||||
case GLUT_KEY_F8:
|
||||
key = SCODE_F8;
|
||||
break;
|
||||
case GLUT_KEY_F9:
|
||||
key = SCODE_F9;
|
||||
break;
|
||||
case GLUT_KEY_F10:
|
||||
key = SCODE_F10;
|
||||
break;
|
||||
case GLUT_KEY_F11:
|
||||
key = SCODE_F11;
|
||||
break;
|
||||
case GLUT_KEY_F12:
|
||||
key = SCODE_F12;
|
||||
break;
|
||||
|
||||
case GLUT_KEY_LEFT:
|
||||
key = SCODE_L;
|
||||
break;
|
||||
case GLUT_KEY_RIGHT:
|
||||
key = SCODE_R;
|
||||
break;
|
||||
case GLUT_KEY_DOWN:
|
||||
key = SCODE_D;
|
||||
break;
|
||||
case GLUT_KEY_UP:
|
||||
key = SCODE_U;
|
||||
break;
|
||||
|
||||
case GLUT_KEY_PAGE_UP:
|
||||
key = SCODE_PGUP;
|
||||
break;
|
||||
case GLUT_KEY_PAGE_DOWN:
|
||||
key = SCODE_PGDN;
|
||||
break;
|
||||
case GLUT_KEY_HOME:
|
||||
key = SCODE_HOME;
|
||||
break;
|
||||
case GLUT_KEY_END:
|
||||
key = SCODE_END;
|
||||
break;
|
||||
case GLUT_KEY_INSERT:
|
||||
key = SCODE_INS;
|
||||
break;
|
||||
|
||||
case GLUT_KEY_SHIFT_L:
|
||||
key = SCODE_L_SHIFT;
|
||||
break;
|
||||
case GLUT_KEY_SHIFT_R:
|
||||
key = SCODE_R_SHIFT;
|
||||
break;
|
||||
|
||||
case GLUT_KEY_CTRL_L:
|
||||
key = SCODE_L_CTRL;
|
||||
break;
|
||||
case GLUT_KEY_CTRL_R:
|
||||
key = SCODE_R_CTRL;
|
||||
break;
|
||||
|
||||
case GLUT_KEY_ALT_L:
|
||||
key = SCODE_L_ALT;
|
||||
break;
|
||||
case GLUT_KEY_ALT_R:
|
||||
key = SCODE_R_ALT;
|
||||
break;
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// GLUT does not appear to handle these PC keys ...
|
||||
#if 0
|
||||
case XK_Pause:
|
||||
key = SCODE_PAUSE;
|
||||
break;
|
||||
case XK_Break:
|
||||
/* Pause and Break are the same key, but have different
|
||||
* scancodes (on PC keyboards). Ctrl makes the difference.
|
||||
*
|
||||
* We assume the X server is passing along the distinction to us,
|
||||
* rather than making us check Ctrl manually.
|
||||
*/
|
||||
key = SCODE_BRK;
|
||||
break;
|
||||
case XK_Print:
|
||||
key = SCODE_PRNT;
|
||||
break;
|
||||
#endif
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// GLUT does not appear to differentiate keypad keys?
|
||||
//case XK_KP_5:
|
||||
case GLUT_KEY_BEGIN:
|
||||
key = SCODE_KPAD_C;
|
||||
break;
|
||||
#if 0
|
||||
case XK_KP_4:
|
||||
case XK_KP_Left:
|
||||
key = SCODE_KPAD_L;
|
||||
break;
|
||||
case XK_KP_8:
|
||||
case XK_KP_Up:
|
||||
key = SCODE_KPAD_U;
|
||||
break;
|
||||
case XK_KP_6:
|
||||
case XK_KP_Right:
|
||||
key = SCODE_KPAD_R;
|
||||
break;
|
||||
case XK_KP_2:
|
||||
case XK_KP_Down:
|
||||
key = SCODE_KPAD_D;
|
||||
break;
|
||||
case XK_KP_7:
|
||||
case XK_KP_Home:
|
||||
key = SCODE_KPAD_UL;
|
||||
break;
|
||||
case XK_KP_9:
|
||||
case XK_KP_Page_Up:
|
||||
key = SCODE_KPAD_UR;
|
||||
break;
|
||||
case XK_KP_1:
|
||||
case XK_KP_End:
|
||||
key = SCODE_KPAD_DL;
|
||||
break;
|
||||
case XK_KP_3:
|
||||
case XK_KP_Page_Down:
|
||||
key = SCODE_KPAD_DR;
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
key = c_keys_ascii_to_scancode(key);
|
||||
break;
|
||||
}
|
||||
|
||||
assert(key < 0x80);
|
||||
return key;
|
||||
}
|
||||
|
||||
#if !defined(TESTING)
|
||||
static void vdriver_on_key_down(unsigned char key, int x, int y) {
|
||||
// no input processing if test-driven ...
|
||||
int scancode = _glutkey_to_scancode(key);
|
||||
LOG("onKeyDown %02x '%c' -> %02X", key, key, scancode);
|
||||
c_keys_handle_input(scancode, 1);
|
||||
}
|
||||
|
||||
static void vdriver_on_key_up(unsigned char key, int x, int y) {
|
||||
int scancode = _glutkey_to_scancode(key);
|
||||
LOG("onKeyUp %02x '%c' -> %02X", key, key, scancode);
|
||||
c_keys_handle_input(scancode, 0);
|
||||
}
|
||||
|
||||
static void vdriver_on_key_special_down(int key, int x, int y) {
|
||||
int scancode = _glutkey_to_scancode(key);
|
||||
LOG("onKeySpecialDown %08x -> %02X", key, scancode);
|
||||
c_keys_handle_input(scancode, 1);
|
||||
}
|
||||
|
||||
static void vdriver_on_key_special_up(int key, int x, int y) {
|
||||
int scancode = _glutkey_to_scancode(key);
|
||||
LOG("onKeySpecialUp %08x -> %02X", key, scancode);
|
||||
c_keys_handle_input(scancode, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// mouse
|
||||
//
|
||||
#if 0
|
||||
static void vdriver_drag(int x, int y) {
|
||||
//LOG("drag %d,%d", x, y);
|
||||
ivec2 currentMouse(x, y);
|
||||
applicationEngine->OnFingerMove(previousMouse, currentMouse);
|
||||
previousMouse.x = x;
|
||||
previousMouse.y = y;
|
||||
}
|
||||
|
||||
static void vdriver_mouse(int button, int state, int x, int y) {
|
||||
LOG("mouse button:%d state:%d %d,%d", button, state, x, y);
|
||||
if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) {
|
||||
previousMouse.x = x;
|
||||
previousMouse.y = y;
|
||||
applicationEngine->OnFingerDown(ivec2(x, y));
|
||||
} else if (button == GLUT_LEFT_BUTTON && state == GLUT_UP) {
|
||||
applicationEngine->OnFingerUp(ivec2(x, y));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// update, display, reshape
|
||||
//
|
||||
static void vdriver_update(void) {
|
||||
glutPostRedisplay();
|
||||
}
|
||||
|
||||
static void vdriver_display(void) {
|
||||
if (is_headless) {
|
||||
return;
|
||||
}
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT/*| GL_DEPTH_BUFFER_BIT*/);
|
||||
|
||||
// Set the viewport transform.
|
||||
glViewport(viewportX, viewportY, viewportWidth, viewportHeight);
|
||||
|
||||
// Set the source color
|
||||
GLfloat sourceColor[] = { 1.f, 1.f, 1.f, 1.f };
|
||||
glUniform4fv(uniforms.sourceColor, 1, &sourceColor[0]);
|
||||
GL_ERRQUIT("glUniform4fv");
|
||||
|
||||
#if 0
|
||||
// Set the model-view transform.
|
||||
matT rotation = mat_identity();
|
||||
matT modelview = mat_mult_mat(rotation, translation, MAT4);
|
||||
glUniformMatrix4fv(uniforms.modelview, 1, 0, MAT_POINTER(modelview));
|
||||
GL_ERRQUIT("glUniformMatrix4fv");
|
||||
|
||||
// Set the projection transform.
|
||||
float h = 4.0f * windowHeight / windowWidth;
|
||||
matT projectionMatrix = mat4_frustum(-2, 2, -h / 2, h / 2, 5, 10);
|
||||
glUniformMatrix4fv(uniforms.projection, 1, 0, MAT_POINTER(projectionMatrix));
|
||||
GL_ERRQUIT("glUniformMatrix4fv");
|
||||
#endif
|
||||
|
||||
// texture ...
|
||||
const uint8_t * const fb = video_current_framebuffer();
|
||||
uint8_t index;
|
||||
#warning FIXME TODO use memcpy?
|
||||
unsigned int count = SCANWIDTH * SCANHEIGHT;
|
||||
char pixels[SCANWIDTH * SCANHEIGHT * 4];
|
||||
for (unsigned int i=0, j=0; i<count; i++, j+=4) {
|
||||
index = *(fb + i);
|
||||
*( (uint32_t*)(pixels + j) ) = (uint32_t)(
|
||||
((uint32_t)(colormap[index].red) << 0 ) |
|
||||
((uint32_t)(colormap[index].green) << 8 ) |
|
||||
((uint32_t)(colormap[index].blue) << 16) |
|
||||
((uint32_t)0xff << 24)
|
||||
);
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, a2framebufferTexture);
|
||||
#if 0
|
||||
int width, height;
|
||||
void *test_pixels = test_texture(&width, &height);
|
||||
glTexImage2D(GL_TEXTURE_2D, /*level*/0, /*internal format*/GL_RGBA, width, height, /*border*/0, /*format*/GL_RGBA, GL_UNSIGNED_BYTE, test_pixels);
|
||||
#else
|
||||
glTexImage2D(GL_TEXTURE_2D, /*level*/0, /*internal format*/GL_RGBA, SCANWIDTH, SCANHEIGHT, /*border*/0, /*format*/GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid *)&pixels[0]);
|
||||
#endif
|
||||
GL_ERRLOG("glTexImage2D");
|
||||
|
||||
// Draw the CRT object and the apple2 framebuffer as a texture
|
||||
glBindBuffer(GL_ARRAY_BUFFER, crtDrawable.vertexBuffer);
|
||||
glVertexAttribPointer(attributes.position, 3, GL_FLOAT, GL_FALSE, STRIDE, 0);
|
||||
#if DEBUG_GEOMETRY
|
||||
glVertexAttribPointer(attributes.testColor, 4, GL_FLOAT, GL_FALSE, STRIDE, TEST_COLOR_OFF);
|
||||
#endif
|
||||
glVertexAttribPointer(attributes.texCoord, 2, GL_FLOAT, GL_FALSE, STRIDE, TEX_COORD_OFF);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, crtDrawable.indexBuffer);
|
||||
glDrawElements(GL_TRIANGLES, crtDrawable.indexCount, GL_UNSIGNED_SHORT, 0);
|
||||
GL_ERRLOG("glDrawElements");
|
||||
|
||||
glutSwapBuffers();
|
||||
}
|
||||
|
||||
static void vdriver_reshape(int w, int h) {
|
||||
LOG("reshape to w:%d h:%d", w, h);
|
||||
windowWidth = w;
|
||||
windowHeight = h;
|
||||
|
||||
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;
|
||||
LOG("OK1 : x:%d,y:%d w:%d,h:%d", viewportX, viewportY, viewportWidth, viewportHeight);
|
||||
} else if (h2 <= h) {
|
||||
viewportX = 0;
|
||||
viewportY = (h-h2)/2;
|
||||
viewportWidth = w;
|
||||
viewportHeight = h2;
|
||||
LOG("OK2 : x:%d,y:%d w:%d,h:%d", viewportX, viewportY, viewportWidth, viewportHeight);
|
||||
} else {
|
||||
viewportX = 0;
|
||||
viewportY = 0;
|
||||
viewportWidth = w;
|
||||
viewportHeight = h;
|
||||
LOG("small viewport : x:%d,y:%d w:%d,h:%d", viewportX, viewportY, viewportWidth, viewportHeight);
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void video_set_mode(a2_video_mode_t mode) {
|
||||
// no-op ...
|
||||
}
|
||||
|
||||
void video_driver_init(void) {
|
||||
#if defined(__APPLE__)
|
||||
vdriver_init_common();
|
||||
#else
|
||||
glutInit(&argc, argv);
|
||||
glutInitDisplayMode(/*GLUT_DOUBLE|*/GLUT_RGBA/*|GLUT_DEPTH*/);
|
||||
glutInitWindowSize(windowWidth, windowHeight);
|
||||
//glutInitContextVersion(4, 0); -- Is this needed?
|
||||
glutInitContextProfile(GLUT_CORE_PROFILE);
|
||||
glutCreateWindow(PACKAGE_NAME);
|
||||
GL_ERRQUIT("GLUT initialization");
|
||||
|
||||
if (glewInit()) {
|
||||
ERRQUIT("Unable to initialize GLEW");
|
||||
}
|
||||
|
||||
vdriver_init_common();
|
||||
|
||||
glutIdleFunc(vdriver_update);
|
||||
glutDisplayFunc(vdriver_display);
|
||||
glutReshapeFunc(vdriver_reshape);
|
||||
//glutMouseFunc(vdriver_mouse);
|
||||
//glutMotionFunc(vdriver_drag);
|
||||
|
||||
#if !defined(TESTING)
|
||||
glutKeyboardFunc(vdriver_on_key_down);
|
||||
glutKeyboardUpFunc(vdriver_on_key_up);
|
||||
glutSpecialFunc(vdriver_on_key_special_down);
|
||||
glutSpecialUpFunc(vdriver_on_key_special_up);
|
||||
#endif
|
||||
#endif // !__APPLE__
|
||||
}
|
||||
|
||||
void video_driver_main_loop(void) {
|
||||
glutMainLoop();
|
||||
}
|
||||
|
||||
void video_driver_sync(void) {
|
||||
if (is_headless) {
|
||||
return;
|
||||
}
|
||||
glutPostRedisplay();
|
||||
}
|
||||
|
||||
void video_driver_shutdown(void) {
|
||||
// no-op ...
|
||||
}
|
||||
|
28
src/video/vgl.h
Normal file
28
src/video/vgl.h
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Apple // emulator for *nix
|
||||
*
|
||||
* This software package is subject to the GNU General Public License
|
||||
* version 2 or later (your choice) as published by the Free Software
|
||||
* Foundation.
|
||||
*
|
||||
* THERE ARE NO WARRANTIES WHATSOEVER.
|
||||
*
|
||||
*/
|
||||
|
||||
// OpenGL header includes
|
||||
|
||||
// #define USE_GL3W
|
||||
|
||||
#if defined(__APPLE__)
|
||||
# include <OpenGLES/ES2/gl.h>
|
||||
# include <OpenGLES/ES2/glext.h>
|
||||
#elif defined(USE_GL3W)
|
||||
# include <GL3/gl3.h>
|
||||
# include <GL3/gl3w.h>
|
||||
#else
|
||||
# define GLEW_STATIC
|
||||
# include <GL/glew.h>
|
||||
# define FREEGLUT_STATIC
|
||||
# include <GL/freeglut.h>
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user