blob: 02588e51bfb7f5d2bb857a0a59eb4c7eb3e326fb [file] [log] [blame]
/*
Copyright (C) 2007 The Android Open Source Project
*/
#include "quakedef.h"
unsigned short d_8to16table[256];
unsigned d_8to24table[256];
#ifdef SUPPORT_8BIT_MIPMAPGENERATION
unsigned char d_15to8table[65536];
#endif
cvar_t mouse_button_commands[3] =
{
CVAR2("mouse1","+attack"),
CVAR2("mouse2","+strafe"),
CVAR2("mouse3","+forward"),
};
static const int MOUSE_LEFTBUTTON = 1;
static const int MOUSE_MIDDLEBUTTON = 2;
static const int MOUSE_RIGHTBUTTON = 4;
bool mouse_tap;
float mouse_x, mouse_y;
float old_mouse_x, old_mouse_y;
int mx, my;
bool mouse_buttonstate;
bool mouse_oldbuttonstate;
cvar_t m_filter = CVAR2("m_filter","1");
int scr_width, scr_height;
cvar_t _windowed_mouse = CVAR3("_windowed_mouse","0", true);
/*-----------------------------------------------------------------------*/
//int texture_mode = GL_NEAREST;
//int texture_mode = GL_NEAREST_MIPMAP_NEAREST;
//int texture_mode = GL_NEAREST_MIPMAP_LINEAR;
int texture_mode = GL_LINEAR;
// int texture_mode = GL_LINEAR_MIPMAP_NEAREST;
//int texture_mode = GL_LINEAR_MIPMAP_LINEAR;
int texture_extension_number = 1;
float gldepthmin, gldepthmax;
cvar_t gl_ztrick = CVAR2("gl_ztrick","0");
const char *gl_vendor;
const char *gl_renderer;
const char *gl_version;
const char *gl_extensions;
qboolean is8bit = false;
qboolean isPermedia = false;
qboolean gl_mtexable = false;
/*-----------------------------------------------------------------------*/
void D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height)
{
}
void D_EndDirectRect (int x, int y, int width, int height)
{
}
void VID_Shutdown(void)
{
}
void VID_ShiftPalette(unsigned char *p)
{
// VID_SetPalette(p);
}
void VID_SetPalette (unsigned char *palette)
{
byte *pal;
unsigned r,g,b;
unsigned v;
int r1,g1,b1;
int k;
unsigned short i;
unsigned *table;
FILE *f;
char s[255];
int dist, bestdist;
static qboolean palflag = false;
PMPBEGIN(("VID_SetPalette"));
//
// 8 8 8 encoding
//
Con_Printf("Converting 8to24\n");
pal = palette;
table = d_8to24table;
for (i=0 ; i<256 ; i++)
{
r = pal[0];
g = pal[1];
b = pal[2];
pal += 3;
// v = (255<<24) + (r<<16) + (g<<8) + (b<<0);
// v = (255<<0) + (r<<8) + (g<<16) + (b<<24);
v = (255<<24) + (r<<0) + (g<<8) + (b<<16);
*table++ = v;
}
d_8to24table[255] &= 0xffffff; // 255 is transparent
#ifdef SUPPORT_8BIT_MIPMAPGENERATION
// JACK: 3D distance calcs - k is last closest, l is the distance.
// FIXME: Precalculate this and cache to disk.
if (palflag)
return;
palflag = true;
COM_FOpenFile("glquake/15to8.pal", &f);
if (f) {
fread(d_15to8table, 1<<15, 1, f);
fclose(f);
} else {
PMPBEGIN(("Creating 15to8 palette"));
for (i=0; i < (1<<15); i++) {
/* Maps
0000.0000.0000.0000
0000.0000.0001.1111 = Red = 0x001F
0000.0011.1110.0000 = Green = 0x03E0
0111.1100.0000.0000 = Blue = 0x7C00
*/
r = ((i & 0x1F) << 3)+4;
g = ((i & 0x03E0) >> (5-3)) +4;
b = ((i & 0x7C00) >> (10-3))+4;
pal = (unsigned char *)d_8to24table;
for (v=0,k=0,bestdist=0x7FFFFFFF; v<256; v++,pal+=4) {
r1 = (int)r - (int)pal[0];
g1 = (int)g - (int)pal[1];
b1 = (int)b - (int)pal[2];
dist = ((r1*r1)+(g1*g1)+(b1*b1));
if (dist < bestdist) {
k=v;
bestdist = dist;
}
}
d_15to8table[i]=k;
}
PMPEND(("Creating 15to8 palette"));
sprintf(s, "%s/glquake", com_gamedir);
Sys_mkdir (s);
sprintf(s, "%s/glquake/15to8.pal", com_gamedir);
if ((f = fopen(s, "wb")) != NULL) {
fwrite(d_15to8table, 1<<15, 1, f);
fclose(f);
}
else
{
Con_Printf("Could not write %s\n", s);
}
}
#endif // SUPPORT_8BIT_MIPMAPGENERATION
PMPEND(("VID_SetPalette"));
}
/*
===============
GL_Init
===============
*/
void GL_Init (void)
{
gl_vendor = (char*) glGetString (GL_VENDOR);
Con_Printf ("GL_VENDOR: %s\n", gl_vendor);
GLCHECK("glGetString");
gl_renderer = (char*) glGetString (GL_RENDERER);
Con_Printf ("GL_RENDERER: %s\n", gl_renderer);
GLCHECK("glGetString");
gl_version = (char*) glGetString (GL_VERSION);
Con_Printf ("GL_VERSION: %s\n", gl_version);
GLCHECK("glGetString");
gl_extensions = (char*) glGetString (GL_EXTENSIONS);
Con_Printf ("GL_EXTENSIONS: %s\n", gl_extensions);
GLCHECK("glGetString");
// Con_Printf ("%s %s\n", gl_renderer, gl_version);
// Enable/disable multitexture:
gl_mtexable = true;
glClearColor (1,0,0,0);
glCullFace(GL_FRONT);
glEnable(GL_TEXTURE_2D);
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_GREATER, 0.666);
#ifdef USE_OPENGLES
#else
glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
#endif
glShadeModel(GL_FLAT);
glDisable(GL_DITHER);
// perspective correction don't work well currently
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
#ifdef USE_OPENGLES
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
#endif
}
/*
=================
GL_BeginRendering
=================
*/
void GL_BeginRendering (int *x, int *y, int *width, int *height)
{
extern cvar_t gl_clear;
*x = *y = 0;
*width = scr_width;
*height = scr_height;
// if (!wglMakeCurrent( maindc, baseRC ))
// Sys_Error ("wglMakeCurrent failed");
// glViewport (*x, *y, *width, *height);
}
void GL_EndRendering (void)
{
//glFlush();
// !!! Swap buffers.
}
void Init_KBD(void)
{
}
// This function controls whether or not 8-bit paletted textures are used:
qboolean VID_Is8bit(void)
{
return 0;
}
static void Check_Gamma (unsigned char *pal)
{
float vid_gamma;
float f, inf;
unsigned char palette[768];
int i;
if ((i = COM_CheckParm("-gamma")) == 0) {
vid_gamma = 0.5; // brighten up game.
} else
vid_gamma = Q_atof(com_argv[i+1]);
if(vid_gamma != 1)
{
for (i=0 ; i<768 ; i++)
{
f = pow ( (pal[i]+1)/256.0 , vid_gamma );
inf = f*255 + 0.5;
if (inf < 0)
inf = 0;
if (inf > 255)
inf = 255;
palette[i] = (unsigned char) inf;
}
}
memcpy (pal, palette, sizeof(palette));
}
void VID_Init(unsigned char *palette)
{
int i;
GLint attribs[32];
char gldir[MAX_OSPATH];
int width = scr_width, height = scr_height;
S_Init();
Init_KBD();
Cvar_RegisterVariable (&gl_ztrick);
vid.maxwarpwidth = scr_width;
vid.maxwarpheight = height;
vid.colormap = host_colormap;
vid.fullbright = 0xffff;
vid.aspect = (float) scr_width / (float) scr_height;
vid.numpages = 2;
vid.rowbytes = 2 * scr_width;
vid.width = scr_width;
vid.height = scr_height;
vid.conwidth = scr_width;
vid.conheight = scr_height;
// interpret command-line params
// set vid parameters
GL_Init();
sprintf (gldir, "%s/glquake", com_gamedir);
Sys_mkdir (gldir);
Check_Gamma(palette);
VID_SetPalette(palette);
Con_SafePrintf ("Video mode %dx%d initialized.\n", width, height);
vid.recalc_refdef = 1; // force a surface cache flush
}
// Android Key event codes. Some of these are
// only generated from the simulator.
// Not all Android devices can generate all codes.
byte scantokey[] =
{
'$', K_ESCAPE, '$', '$', K_ESCAPE, '$', '$', '0', // 0.. 7
'1', '2', '3', '4', '5', '6', '7', '8', // 8..15
'9', '$', '$', K_UPARROW, K_DOWNARROW, K_LEFTARROW, K_RIGHTARROW, K_ENTER, // 16..23
'$', '$', '$', '$', '$', 'a', 'b', 'c', // 24..31
'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', // 32..39
'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', // 40..47
't', 'u', 'v', 'w', 'x', 'y', 'z', ',', // 48..55
'.', K_CTRL, K_SHIFT, K_TAB, ' ', '$', '$', '$', // 56..63
'$', '$', K_ENTER, K_BACKSPACE, '`', '-', '=', '[', // 64..71
']', '\\', ';', '\'', '/', '@', '#', '$', // 72..79
'$', '$', K_ESCAPE, '$' // 80..83
};
byte scantokeyAlt[] =
{
0, 0, 0, 0, 0, 0, 0, 0, // 0.. 7
0, 0, 0, 0, 0, 0, 0, 0, // 8..15
0, 0, 0, 0, 0, 0, 0, 0, // 16..23
0, 0, 0, 0, 0, '%', '=', '8', // 24..31
'5', '2', '6', '-', '[', '$', ']', '"', // 32..39
'\'', '>', '<', '(', ')', '*', '3', '4', // 40..47
'+', '&', '9', '1', '7', '!', '#', ';', // 48..55
':', 0, 0, 0, K_TAB, 0, 0, 0, // 56..63
0, 0, 0, 0, 0, 0, 0, 0, // 64..71
0, 0, '?', '0', 0, 0, 0, 0, // 72..79
0, 0, K_ESCAPE, 0 // 80..83
};
#if 0
byte scantokeyCap[] =
{
0, 0, 0, 0, 0, 0, 0, 0, // 0.. 7
0, 0, 0, 0, 0, 0, 0, 0, // 8..15
0, 0, 0, 0, 0, 0, 0, 0, // 16..23
0, 0, 0, 0, 0, 'A', 'B', 'C', // 24..31
'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', // 32..39
'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', // 40..47
'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 0, // 48..55
0, 0, 0, 0, 0, 0, 0, 0, // 56..63
0, 0, 0, 0, 0, 0, 0, 0, // 64..71
0, 0, 0, 0, 0, 0, 0, 0, // 72..79
0, 0, K_ESCAPE, 0 // 80..83
};
#endif
byte scantokeySym[] =
{
0, 0, 0, 0, 0, 0, 0, 0, // 0.. 7
0, 0, 0, 0, 0, 0, 0, 0, // 8..15
0, 0, 0, 0, 0, 0, 0, 0, // 16..23
0, 0, 0, 0, 0, 0, 0, K_F8, // 24..31
K_F5, K_F2, K_F6, '_', 0, 0, 0, 0, // 32..39
0, 0, 0, 0, 0, 0, K_F3, K_F4, // 40..47
K_F11, 0, K_F9, K_F1, K_F7, K_F12, K_PAUSE, 0, // 48..55
0, 0, 0, 0, 0, 0, 0, 0, // 56..63
0, 0, 0, 0, 0, 0, 0, 0, // 64..71
0, 0, '`', K_F10, 0, 0, 0, 0, // 72..79
0, 0, K_ESCAPE, 0 // 80..83
};
#define ALT_KEY_VALUE 57
// #define CAPS_KEY_VALUE 58
#define SYM_KEY_VALUE 61
byte modifierKeyInEffect;
qboolean symKeyDown;
byte symKeyCode;
// Called from stand-alone main() function to process an event.
// Return non-zero if the event is handled.
int AndroidEvent(int type, int value)
{
if(value >= 0 && value < (int) sizeof(scantokey))
{
byte key;
qboolean isPress = type != 0;
qboolean isModifier = value == ALT_KEY_VALUE || value == SYM_KEY_VALUE;
if(isModifier)
{
if(isPress)
{
if(modifierKeyInEffect == value)
{
// Press modifier twice to cancel modifier
modifierKeyInEffect = 0;
}
else
{
// Most recent modifier key wins
modifierKeyInEffect = value;
}
}
return 1;
}
else
{
switch(modifierKeyInEffect)
{
default: key = scantokey[value]; break;
case ALT_KEY_VALUE: key = scantokeyAlt[value]; break;
// case CAP_KEY_VALUE: key = scantokeyCap[value]; break;
case SYM_KEY_VALUE: key = scantokeySym[value]; break;
}
if(!key)
{
key = scantokey[value];
}
// Hack: Remap @ and / to K_CTRL in game mode
if(key_dest == key_game && ! modifierKeyInEffect && (key == '@' || key == '/'))
{
key = K_CTRL;
}
if(!isPress)
{
modifierKeyInEffect = 0;
}
}
Key_Event(key, type);
// PMPLOG(("type: %d, value: %d -> %d '%c'\n", type, value, key, key));
return 1;
}
else
{
PMPWARNING(("unexpected event type: %d, value: %d\n", type, value));
}
return 0;
}
// Called from Java to process an event.
// Return non-zero if the event is handled.
#if !defined(__clang__)
int AndroidEvent2(int type, int value)
#else
extern "C" int AndroidEvent2_LLVM(int type, int value)
#endif
{
Key_Event(value, type);
return 0;
}
static const int MOTION_DOWN = 0;
static const int MOTION_UP = 1;
static const int MOTION_MOVE = 2;
static const int MOTION_CANCEL = 3;
class GestureDetector {
private:
bool mIsScroll;
bool mIsTap;
bool mIsDoubleTap;
float mScrollX;
float mScrollY;
static const unsigned long long TAP_TIME_MS = 200;
static const unsigned long long DOUBLE_TAP_TIME_MS = 400;
static const int TAP_REGION_MANHATTAN_DISTANCE = 10;
bool mAlwaysInTapRegion;
float mDownX;
float mDownY;
unsigned long long mDownTime;
unsigned long long mPreviousDownTime;
/**
* Position of the last motion event.
*/
float mLastMotionY;
float mLastMotionX;
public:
/**
* Analyze a motion event. Call this once for each motion event
* that is received by a view.
* @param ev the motion event to analyze.
*/
void analyze(unsigned long long eventTime, int action,
float x, float y, float pressure, float size, int deviceId) {
mIsScroll = false;
mIsTap = false;
mIsDoubleTap = false;
switch (action) {
case MOTION_DOWN:
printf("Down");
// Remember where the motion event started
mLastMotionX = x;
mLastMotionY = y;
mDownX = x;
mDownY = y;
mPreviousDownTime = mDownTime;
mDownTime = eventTime;
mAlwaysInTapRegion = true;
break;
case MOTION_MOVE:
{
mIsScroll = true;
mScrollX = mLastMotionX - x;
mScrollY = mLastMotionY - y;
mLastMotionX = x;
mLastMotionY = y;
int manhattanTapDistance = (int) (absf(x - mDownX) +
absf(y - mDownY));
if (manhattanTapDistance > TAP_REGION_MANHATTAN_DISTANCE) {
mAlwaysInTapRegion = false;
}
}
break;
case MOTION_UP:
{
unsigned long long doubleTapDelta =
eventTime - mPreviousDownTime;
unsigned long long singleTapDelta =
eventTime - mDownTime;
if (mAlwaysInTapRegion) {
if (doubleTapDelta < DOUBLE_TAP_TIME_MS) {
mIsDoubleTap = true;
}
else if (singleTapDelta < TAP_TIME_MS) {
mIsTap = true;
}
}
}
break;
}
}
/**
* @return true if the current motion event is a scroll
* event.
*/
bool isScroll() {
return mIsScroll;
}
/**
* This value is only defined if {@link #isScroll} is true.
* @return the X position of the current scroll event.
* event.
*/
float scrollX() {
return mScrollX;
}
/**
* This value is only defined if {@link #isScroll} is true.
* @return the Y position of the current scroll event.
* event.
*/
float scrollY() {
return mScrollY;
}
/**
* @return true if the current motion event is a single-tap
* event.
*/
bool isTap() {
return mIsTap;
}
/**
* This value is only defined if either {@link #isTap} or
* {@link #isDoubleTap} is true.
* @return the X position of the current tap event.
* event.
*/
float tapX() {
return mDownX;
}
/**
* This value is only defined if either {@link #isTap} or
* {@link #isDoubleTap} is true.
* @return the Y position of the current tap event.
* event.
*/
float tapY() {
return mDownY;
}
/**
* @return true if the current motion event is a double-tap
* event.
*/
bool isDoubleTap() {
return mIsDoubleTap;
}
private:
inline float absf(float a) {
return a >= 0.0f ? a : -a;
}
};
GestureDetector gGestureDetector;
#if !defined(__clang__)
int AndroidMotionEvent
#else
extern "C" int AndroidMotionEvent_LLVM
#endif
(unsigned long long eventTime, int action,
float x, float y, float pressure, float size, int deviceId)
{
gGestureDetector.analyze(eventTime, action, x, y, pressure, size, deviceId);
if (gGestureDetector.isTap()) {
mouse_tap = true;
}
else if (gGestureDetector.isScroll()) {
mx += (int) gGestureDetector.scrollX();
my += (int) gGestureDetector.scrollY();
}
return true;
}
#if !defined(__clang__)
int AndroidTrackballEvent
#else
extern "C" int AndroidTrackballEvent_LLVM
#endif
(unsigned long long eventTime, int action,
float x, float y)
{
switch (action ) {
case MOTION_DOWN:
mouse_buttonstate = true;
break;
case MOTION_UP:
mouse_buttonstate = false;
break;
case MOTION_MOVE:
mx += (int) (20.0f * x);
my += (int) (20.0f * y);
break;
}
return true;
}
void Sys_SendKeyEvents(void)
{
// Used to poll keyboards on systems that need to poll keyboards.
}
void Force_CenterView_f (void)
{
cl.viewangles[PITCH] = 0;
}
void IN_Init(void)
{
Cvar_RegisterVariable (&mouse_button_commands[0]);
Cvar_RegisterVariable (&mouse_button_commands[1]);
Cvar_RegisterVariable (&mouse_button_commands[2]);
Cmd_AddCommand ("force_centerview", Force_CenterView_f);
}
void IN_Shutdown(void)
{
}
/*
===========
IN_Commands
===========
*/
void IN_Commands (void)
{
// perform button actions
if (mouse_tap) {
Key_Event (K_MOUSE1, true);
Key_Event (K_MOUSE1, false);
mouse_tap = false;
}
if (mouse_buttonstate != mouse_oldbuttonstate) {
Key_Event (K_MOUSE1, mouse_buttonstate ? true : false);
mouse_oldbuttonstate = mouse_buttonstate;
}
}
/*
===========
IN_Move
===========
*/
void IN_MouseMove (usercmd_t *cmd)
{
#if 0
if (m_filter.value)
{
mouse_x = (mx + old_mouse_x) * 0.5;
mouse_y = (my + old_mouse_y) * 0.5;
}
else
#endif
{
mouse_x = mx;
mouse_y = my;
}
old_mouse_x = mx;
old_mouse_y = my;
mx = my = 0; // clear for next update
mouse_x *= 5.0f * sensitivity.value;
mouse_y *= 5.0f * sensitivity.value;
// add mouse X/Y movement to cmd
if ( (in_strafe.state & 1) || (lookstrafe.value && (in_mlook.state & 1) ))
cmd->sidemove += m_side.value * mouse_x;
else
cl.viewangles[YAW] -= m_yaw.value * mouse_x;
if (in_mlook.state & 1)
V_StopPitchDrift ();
if ( (in_mlook.state & 1) && !(in_strafe.state & 1))
{
cl.viewangles[PITCH] += m_pitch.value * mouse_y;
if (cl.viewangles[PITCH] > 80)
cl.viewangles[PITCH] = 80;
if (cl.viewangles[PITCH] < -70)
cl.viewangles[PITCH] = -70;
}
else
{
if ((in_strafe.state & 1) && noclip_anglehack)
cmd->upmove -= m_forward.value * mouse_y;
else
cmd->forwardmove -= m_forward.value * mouse_y;
}
}
void IN_Move (usercmd_t *cmd)
{
IN_MouseMove(cmd);
}