android port updated. egl now handled by native code, which means we now have proper control over everything and can default to gles2. requires android 2.0+. vulkan-on-android renderer added, but not tested.

added .ktx image support as an alternative to .dds, primarily for etc2 compression (which should work on all gles3 devices, or gl4.3), only known representations will work.
rework z-fighting workaround. now only enabled on known vanilla maps.
added splitscreen option to the singleplayer menu.

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5154 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2017-10-13 17:50:28 +00:00
parent 696c7e8260
commit 7dc01f7362
38 changed files with 1564 additions and 1083 deletions

View File

@ -225,27 +225,33 @@ ifeq ($(FTE_TARGET),droid)
#DROID_ABI_VER____mips64=$(DROID_ABI_VER____mips)
#DROID_ABI_CFLAGS_mips64=-m64
#try and make sense of the above nonsense.
DROID_ABI:=$(DROID_ABI_CFLAGS_$(DROID_ARCH))
TOOLCHAINPATH:=$(ANDROID_NDK_ROOT)/toolchains/$(DROID_ABI_NAME___$(DROID_ARCH))-$(DROID_ABI_VER____$(DROID_ARCH))/prebuilt/$(ANDROID_HOSTSYSTEM)/bin/
TOOLCHAIN:=$(TOOLCHAINPATH)$(DROID_ABI_PREFIX_$(DROID_ARCH))-
ifeq (1,$(words [$(DROID_ARCH)]))
#try and make sense of the above nonsense.
DROID_ABI:=$(DROID_ABI_CFLAGS_$(DROID_ARCH))
TOOLCHAINPATH:=$(ANDROID_NDK_ROOT)/toolchains/$(DROID_ABI_NAME___$(DROID_ARCH))-$(DROID_ABI_VER____$(DROID_ARCH))/prebuilt/$(ANDROID_HOSTSYSTEM)/bin/
TOOLCHAIN:=$(TOOLCHAINPATH)$(DROID_ABI_PREFIX_$(DROID_ARCH))-
#4 is the min that fte requires
DROID_API_LEVEL?=4
DROID_API_NAME?=android-$(DROID_API_LEVEL)
DROID_PLAT_INC=arch-$(DROID_ABI_ARCH___$(DROID_ARCH))
ifeq ($(DROID_ARCH),x86)
#google fecked up. anything before api_level 9 will fail to compile on x86
DROID_API_LEVEL=9
endif
DROIDSYSROOT=$(realpath $(ANDROID_NDK_ROOT)/platforms/$(DROID_API_NAME)/$(DROID_PLAT_INC))
ifeq ($(DROIDSYSROOT),) #its possible that google removed whatever api we're trying to target, just switch up to the new default.
DROID_API_LEVEL=9
#4 is the min that fte requires
DROID_API_LEVEL?=9
ifeq ($(DROID_ARCH),x86)
#google fecked up. anything before api_level 9 will fail to compile on x86
DROID_API_LEVEL=9
endif
DROID_API_NAME?=android-$(DROID_API_LEVEL)
BITCHANDMOAN:=$(shell echo targetting \"$(DROID_API_NAME)\" instead of \"android-$(DROID_API_LEVEL)\" 1>&2)
DROID_PLAT_INC=arch-$(DROID_ABI_ARCH___$(DROID_ARCH))
DROIDSYSROOT=$(realpath $(ANDROID_NDK_ROOT)/platforms/$(DROID_API_NAME)/$(DROID_PLAT_INC))
ifeq ($(DROIDSYSROOT),) #its possible that google removed whatever api we're trying to target, just switch up to the new default.
BITCHANDMOAN:=$(shell echo targetting \"android-9\" instead of \"android-$(DROID_API_LEVEL)\" 1>&2)
DROID_API_LEVEL=9
DROID_API_NAME=android-$(DROID_API_LEVEL)
DROIDSYSROOT=$(realpath $(ANDROID_NDK_ROOT)/platforms/$(DROID_API_NAME)/$(DROID_PLAT_INC))
ifeq ($(DROIDSYSROOT),) #its possible that google removed whatever api we're trying to target, just switch up to the new default.
BITCHANDMOAN:=$(shell echo $(DROID_API_NAME) not available either - $(DROID_ARCH) 1>&2)
endif
endif
DROIDSYSROOT:=$(DROIDSYSROOT)
endif
DROIDSYSROOT:=$(DROIDSYSROOT)
#if we're running under windows, then we want to run some other binary
ifeq ($(shell uname -o 2>&1 | grep Cygwin),)
@ -1481,17 +1487,26 @@ endif
ifeq ($(FTE_TARGET),droid)
BASELDFLAGS=-lz
SYS_DROID_O=sys_droid.o sys_linux_threads.o
GL_DROID_O=gl_viddroid.o $(SYS_DROID_O)
SV_CFLAGS=$(SERVER_ONLY_CFLAGS) $(W32_CFLAGS)
SV_LDFLAGS=
SV_DIR=sv_droid-$(DROID_ARCH)
SV_OBJS=$(COMMON_OBJS) $(SERVER_OBJS) $(PROGS_OBJS) $(BOTLIB_OBJS) sys_droid.o
SV_OBJS=$(COMMON_OBJS) $(SERVER_OBJS) $(PROGS_OBJS) $(BOTLIB_OBJS) $(SYS_DROID_O)
SV_EXE_NAME=libftedroid.so
GL_CFLAGS=$(GLCFLAGS)
GL_LDFLAGS=$(GLLDFLAGS)
GLCL_OBJS=$(GL_OBJS) $(D3DGL_OBJS) $(GLQUAKE_OBJS) $(BOTLIB_OBJS) gl_viddroid.o sys_droid.o cd_null.o snd_droid.o
GL_LDFLAGS=$(GLLDFLAGS) -landroid -lEGL
GLCL_OBJS=$(GL_OBJS) $(D3DGL_OBJS) $(GLQUAKE_OBJS) $(BOTLIB_OBJS) $(GL_DROID_O) cd_null.o snd_droid.o
GLB_DIR=gl_droid-$(DROID_ARCH)
GL_EXE_NAME=libftedroid.so
GL_EXE_NAME=libftedroid.so
M_CFLAGS=$(VKCFLAGS) $(GLCFLAGS) -DMULTITHREAD
M_LDFLAGS=$(GLLDFLAGS) -landroid -lEGL
MCL_OBJS=$(GL_OBJS) $(D3DGL_OBJS) $(GLQUAKE_OBJS) $(BOTLIB_OBJS) $(GL_DROID_O) cd_null.o snd_droid.o
MB_DIR=m_droid-$(DROID_ARCH)
M_EXE_NAME=libftedroid.so
endif
ifeq ($(FTE_TARGET),web)
@ -2015,9 +2030,9 @@ endif
droid-rel:
$(MAKE) FTE_TARGET=droid droid/build.xml droid/ftekeystore
$(foreach a, $(DROID_ARCH), $(MAKE) FTE_TARGET=droid gl-rel DROID_ARCH=$a; )
$(foreach a, $(DROID_ARCH), $(MAKE) FTE_TARGET=droid m-rel DROID_ARCH=$a; )
@$(foreach a, $(DROID_ARCH), mkdir -p droid/libs/$a; )
-@$(foreach a, $(DROID_ARCH), cp $(RELEASE_DIR)/gl_droid-$a/libftedroid.so droid/libs/$a/libftedroid.so; )
-@$(foreach a, $(DROID_ARCH), cp $(RELEASE_DIR)/m_droid-$a/libftedroid.so droid/libs/$a/libftedroid.so; )
@cd droid && $(ANT) release
ifneq ($(DROID_PACKSU),)
@ -2051,9 +2066,9 @@ droid-opt:
#build FTE as a library, then build the java+package (release). also installs it onto the 'current' device.
droid-dbg:
$(MAKE) FTE_TARGET=droid droid/build.xml
$(foreach a, $(DROID_ARCH), $(MAKE) FTE_TARGET=droid gl-dbg DROID_ARCH=$a; )
$(foreach a, $(DROID_ARCH), $(MAKE) FTE_TARGET=droid m-dbg DROID_ARCH=$a; )
@$(foreach a, $(DROID_ARCH), mkdir -p droid/libs/$a; )
-@$(foreach a, $(DROID_ARCH), cp $(DEBUG_DIR)/gl_droid-$a/libftedroid.so droid/libs/$a/libftedroid.so; )
-@$(foreach a, $(DROID_ARCH), cp $(DEBUG_DIR)/m_droid-$a/libftedroid.so droid/libs/$a/libftedroid.so; )
@cd droid && $(ANT) debug #&& $(ANT) debug install
cp droid/bin/FTEDroid-debug.apk $(DEBUG_DIR)/FTEDroid.apk

View File

@ -1,5 +1,6 @@
#include "quakedef.h"
#include "shader.h"
#include "glquake.h" //we need some of the gl format enums
//#define PURGEIMAGES //somewhat experimental still. we're still flushing more than we should.
@ -18,6 +19,9 @@ cvar_t r_dodgypcxfiles = CVARD("r_dodgypcxfiles", "0", "When enabled, this will
cvar_t r_dodgymiptex = CVARD("r_dodgymiptex", "1", "When enabled, this will force regeneration of mipmaps, discarding mips1-4 like glquake did. This may eg solve fullbright issues with some maps, but may reduce distant detail levels.");
char *r_defaultimageextensions =
#ifdef IMAGEFMT_KTX
"ktx " //compressed or something
#endif
#ifdef IMAGEFMT_DDS
"dds " //compressed or something
#endif
@ -2595,6 +2599,198 @@ static void Image_LoadTextureMips(void *ctx, void *data, size_t a, size_t b)
//FIXME: check loaded wad files too.
}
#ifdef IMAGEFMT_KTX
typedef struct
{
char magic[12];
unsigned int endianness;
unsigned int gltype;
unsigned int gltypesize;
unsigned int glformat;
unsigned int glinternalformat;
unsigned int glbaseinternalformat;
unsigned int pixelwidth;
unsigned int pixelheight;
unsigned int pixeldepth;
unsigned int numberofarrayelements;
unsigned int numberoffaces;
unsigned int numberofmipmaplevels;
unsigned int bytesofkeyvaluedata;
} ktxheader_t;
static qboolean Image_ReadKTXFile(texid_t tex, unsigned int flags, char *fname, qbyte *filedata, size_t filesize)
{
static const char magic[12] = {0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A};
ktxheader_t *header;
int nummips;
int mipnum;
int face;
int datasize;
unsigned int w, h, x, y;
struct pendingtextureinfo *mips;
int encoding;
qbyte *out, *in;
if (memcmp(filedata, magic, sizeof(magic)))
return false; //not a ktx file
header = (ktxheader_t*)filedata;
nummips = header->numberofmipmaplevels;
if (nummips < 1)
nummips = 1;
if (header->numberofarrayelements != 0)
return false; //don't support array textures
if (header->numberoffaces == 1)
; //non-cubemap
else if (header->numberoffaces == 6)
{
if (header->pixeldepth != 0)
return false;
if (header->numberofmipmaplevels != 1)
return false; //only allow cubemaps that have no mips
}
else
return false; //don't allow weird cubemaps
if (header->pixeldepth && header->pixelwidth != header->pixeldepth && header->pixelheight != header->pixeldepth)
return false; //we only support 3d textures where width+height+depth are the same. too lazy to change it now.
switch(header->glinternalformat)
{
case GL_ETC1_RGB8_OES:
encoding = PTI_ETC1_RGB8;
break;
case GL_COMPRESSED_RGB8_ETC2:
case GL_COMPRESSED_SRGB8_ETC2:
encoding = PTI_ETC2_RGB8;
break;
case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2:
case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2:
encoding = PTI_ETC2_RGB8A1;
break;
case GL_COMPRESSED_RGBA8_ETC2_EAC:
case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:
encoding = PTI_ETC2_RGB8A8;
break;
case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
encoding = PTI_S3RGB1;
break;
case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
encoding = PTI_S3RGBA1;
break;
case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
encoding = PTI_S3RGBA3;
break;
case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
encoding = PTI_S3RGBA5;
break;
case GL_BGRA_EXT:
encoding = PTI_BGRA8;
break;
case GL_RGBA:
encoding = PTI_RGBA8;
break;
case GL_SRGB8_ALPHA8_EXT:
encoding = PTI_RGBA8_SRGB;
break;
case 0x8045/*GL_LUMINANCE8_ALPHA8*/:
encoding = PTI_RGBA8;
break;
case 0x8051/*GL_RGB8*/:
encoding = PTI_RGBX8;
break;
//note: this could be pretty much anything, including 24bit data.
default:
Con_Printf("Unsupported ktx internalformat %x in %s\n", header->glinternalformat, fname);
return false;
}
if (!sh_config.texfmt[encoding])
{
Con_Printf("KTX %s: encoding %x not supported on this system\n", fname, header->glinternalformat);
return false;
}
mips = Z_Malloc(sizeof(*mips));
mips->mipcount = 0;
if (header->pixeldepth)
mips->type = PTI_3D;
else if (header->numberoffaces==6)
mips->type = PTI_CUBEMAP;
else
mips->type = PTI_2D;
mips->extrafree = filedata;
mips->encoding = encoding;
filedata += sizeof(*header); //skip the header...
filedata += header->bytesofkeyvaluedata; //skip the keyvalue stuff
w = header->pixelwidth;
h = header->pixelheight;
for (mipnum = 0; mipnum < nummips; mipnum++)
{
datasize = *(int*)filedata;
filedata += 4;
for (face = 0; face < header->numberoffaces; face++)
{
if (mips->mipcount >= countof(mips->mip))
break;
mips->mip[mips->mipcount].data = in = filedata;
mips->mip[mips->mipcount].datasize = datasize;
mips->mip[mips->mipcount].width = w;
mips->mip[mips->mipcount].height = h;
//some formats are old and not really supported. we convert.
switch(header->glinternalformat)
{
case 0x8045/*GL_LUMINANCE8_ALPHA8*/:
mips->mip[mips->mipcount].needfree = true;
mips->mip[mips->mipcount].data = out = BZ_Malloc(datasize*2);
mips->mip[mips->mipcount].datasize = datasize * 2;
//fixme: input must be a multiple of 2...
for (y = 0; y < h; y++)
for (x = 0; x < w; x++, out+=4)
{
out[0] = out[1] = out[2] = *in++;
out[3] = *in++;
}
break;
case 0x8051/*GL_RGB8*/:
// case GL_BGR8_EXT:
mips->mip[mips->mipcount].needfree = true;
mips->mip[mips->mipcount].datasize = (datasize/3)*4;
mips->mip[mips->mipcount].data = out = BZ_Malloc((datasize/3)*4);
//fixme: input must be a multiple of 4...
for (y = 0; y < h; y++)
for (x = 0; x < w; x++, out+=4)
{
out[0] = *in++;
out[1] = *in++;
out[2] = *in++;
out[3] = 255;
}
break;
}
mips->mipcount++;
filedata += datasize;
if (datasize & 3)
filedata += 4-(datasize&3);
}
w = (w+1)>>1;
h = (h+1)>>1;
}
if (flags & IF_NOWORKER)
Image_LoadTextureMips(tex, mips, 0, 0);
else
COM_AddWork(WG_MAIN, Image_LoadTextureMips, tex, mips, 0, 0);
return true;
}
#endif
#ifdef IMAGEFMT_DDS
typedef struct {
unsigned int dwSize;
@ -4316,6 +4512,8 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag
case PTI_S3RGBA1: //mostly compatible, but I don't want to push it.
case PTI_S3RGBA3:
case PTI_S3RGBA5:
case PTI_ETC2_RGB8A1:
case PTI_ETC2_RGB8A8:
case PTI_WHOLEFILE:
//erk. meh.
break;
@ -4327,6 +4525,8 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag
case PTI_RGBX8_SRGB:
case PTI_BGRX8_SRGB:
case PTI_S3RGB1:
case PTI_ETC1_RGB8:
case PTI_ETC2_RGB8:
break; //already no alpha in these formats
case PTI_DEPTH16:
case PTI_DEPTH24:
@ -4497,6 +4697,10 @@ qboolean Image_LoadTextureFromMemory(texid_t tex, int flags, const char *iname,
int imgwidth, imgheight;
//these formats have special handling, because they cannot be implemented via Read32BitImageFile - they don't result in rgba images.
#ifdef IMAGEFMT_KTX
if (Image_ReadKTXFile(tex, flags, fname, filedata, filesize))
return true;
#endif
#ifdef IMAGEFMT_DDS
if (Image_ReadDDSFile(tex, flags, fname, filedata, filesize))
return true;

View File

@ -125,7 +125,9 @@ struct eventlist_s
IEV_KEYRELEASE,
IEV_MOUSEABS,
IEV_MOUSEDELTA,
IEV_JOYAXIS
IEV_JOYAXIS,
IEV_ACCELEROMETER,
IEV_GYROSCOPE,
} type;
unsigned int devid;
@ -145,6 +147,14 @@ struct eventlist_s
int axis;
float value;
} joy;
struct
{ //metres per second, ish.
float x, y, z;
} accel;
struct
{ //these are in radians, not degrees.
float pitch, yaw, roll;
} gyro;
};
} eventlist[EVENTQUEUELENGTH];
volatile int events_avail; /*volatile to make sure the cc doesn't try leaving these cached in a register*/
@ -511,6 +521,16 @@ void IN_Commands(void)
}
}
break;
case IEV_ACCELEROMETER:
//down: x= +9.8
//left: y= -9.8
//up: z= +9.8
CSQC_Accelerometer(ev->accel.x, ev->accel.y, ev->accel.z);
break;
case IEV_GYROSCOPE:
CSQC_Gyroscope(ev->gyro.pitch * 180.0/M_PI, ev->gyro.yaw * 180.0/M_PI, ev->gyro.roll * 180.0/M_PI);
break;
}
events_used++;
}
@ -988,3 +1008,28 @@ void IN_MouseMove(unsigned int devid, int abs, float x, float y, float z, float
ev->mouse.tsize = size;
in_finishevent();
}
void IN_Accelerometer(unsigned int devid, float x, float y, float z)
{
struct eventlist_s *ev = in_newevent();
if (!ev)
return;
ev->devid = devid;
ev->type = IEV_ACCELEROMETER;
ev->accel.x = x;
ev->accel.y = y;
ev->accel.z = z;
in_finishevent();
}
void IN_Gyroscope(unsigned int devid, float pitch, float yaw, float roll)
{
struct eventlist_s *ev = in_newevent();
if (!ev)
return;
ev->devid = devid;
ev->type = IEV_GYROSCOPE;
ev->gyro.pitch = pitch;
ev->gyro.yaw = yaw;
ev->gyro.roll = roll;
in_finishevent();
}

View File

@ -49,6 +49,8 @@ int CL_TargettedSplit(qboolean nowrap);
void IN_KeyEvent(unsigned int devid, int down, int keycode, int unicode); //don't use IN_KeyEvent for mice if you ever use abs mice...
void IN_MouseMove(unsigned int devid, int abs, float x, float y, float z, float size);
void IN_JoystickAxisEvent(unsigned int devid, int axis, float value);
void IN_Accelerometer(unsigned int devid, float x, float y, float z);
void IN_Gyroscope(unsigned int devid, float pitch, float yaw, float roll);
//system-specific functions
void INS_Move (float *movements, int pnum);

View File

@ -1262,35 +1262,41 @@ static void M_QuickConnect_PreDraw(menu_t *menu)
serverinfo_t *best = NULL;
serverinfo_t *s;
char adr[MAX_ADR_SIZE];
int ping;
Master_CheckPollSockets(); //see if we were told something important.
CL_QueryServers();
if (Sys_DoubleTime() > quickconnecttimeout)
{
for (s = firstserver; s; s = s->next)
quickconnecttimeout = Sys_DoubleTime() + 15;
for (ping = 50; ping < 200 && !best; ping += 50)
{
if (!s->maxplayers) //no response?
continue;
if (s->players == s->maxplayers)
continue; //server is full already
if (s->special & SS_PROXY)
continue; //don't quickconnect to a proxy. their player counts are often wrong (especially with qtv)
if (s->ping < 50) //don't like servers with too high a ping
for (s = firstserver; s; s = s->next)
{
if (s->players > 0)
if (!s->maxplayers) //no response?
continue;
if (s->players == s->maxplayers)
continue; //server is full already
if (s->special & SS_PROXY)
continue; //don't quickconnect to a proxy. their player counts are often wrong (especially with qtv)
if (s->ping < ping) //don't like servers with too high a ping
{
if (best)
if (best->players > s->players)
continue; //go for the one with most players
best = s;
if (s->numhumans > 0)
{
if (best)
if (best->numhumans > s->numhumans)
continue; //go for the one with most players
best = s;
}
}
}
}
if (best)
{
Con_Printf("Quick connect found %s (gamedir %s, players %i/%i, ping %ims)\n", best->name, best->gamedir, best->players, best->maxplayers, best->ping);
Con_Printf("Quick connect found %s (gamedir %s, players %i/%i/%i, ping %ims)\n", best->name, best->gamedir, best->numhumans, best->players, best->maxplayers, best->ping);
if ((best->special & SS_PROTOCOLMASK) == SS_NETQUAKE)
Cbuf_AddText(va("nqconnect %s\n", NET_AdrToString(adr, sizeof(adr), &best->adr)), RESTRICT_LOCAL);
@ -1304,8 +1310,6 @@ static void M_QuickConnect_PreDraw(menu_t *menu)
//retry
MasterInfo_Refresh();
isrefreshing = true;
quickconnecttimeout = Sys_DoubleTime() + 5;
}
}

View File

@ -76,6 +76,8 @@ void M_Menu_MultiPlayer_f (void)
}
else
{
int width;
p = R2D_SafeCachePic("gfx/mp_menu.lmp");
if (p)
{
@ -84,26 +86,29 @@ void M_Menu_MultiPlayer_f (void)
MC_AddPicture(menu, 72, 32, 232, 64, "gfx/mp_menu.lmp");
}
if (R_GetShaderSizes(p, &width, NULL, true) <= 0)
width = 232;
b = MC_AddConsoleCommand(menu, 72, 320, 32, "", "menu_slist\n");
menu->selecteditem = (menuoption_t*)b;
b->common.height = 20;
b->common.width = p?p->width:320;
b->common.width = width;
b = MC_AddConsoleCommand(menu, 72, 320, 52, "", "menu_newmulti\n");
b->common.height = 20;
b->common.width = p?p->width:320;
b->common.width = width;
b = MC_AddConsoleCommand(menu, 72, 320, 72, "", "menu_setup\n");
b->common.height = 20;
b->common.width = p?p->width:320;
b->common.width = width;
b = MC_AddConsoleCommand(menu, 72, 320, 92, "", "menu_demo\n");
MC_AddWhiteText(menu, 72, 0, 92+20/2-6, "Demos", false);
b->common.height = 20/2+2;
b->common.width = p?p->width:320;
MC_AddWhiteText(menu, 72, 0, 92+20/2-6, "^aDemos", false);
b->common.height = 20;
b->common.width = width;
b = MC_AddConsoleCommand(menu, 72, 320, 112, "", "quickconnect qw\n");
MC_AddWhiteText(menu, 72, 0, 112+20/2-6, "Quick Connect", false);
b->common.height = 20/2+2;
b->common.width = p?p->width:320;
MC_AddWhiteText(menu, 72, 0, 112+20/2-6, "^aQuick Connect", false);
b->common.height = 20;
b->common.width = width;
}
menu->cursoritem = (menuoption_t*)MC_AddCursor(menu, &resel, 54, 32);
@ -132,7 +137,7 @@ qboolean ApplySetupMenu (union menuoption_s *option,struct menu_s *menu, int key
{
char bot[64], top[64];
setupmenu_t *info = menu->data;
if (key != K_ENTER && key != K_KP_ENTER && key != K_GP_START)
if (key != K_MOUSE1 && key != K_ENTER && key != K_KP_ENTER && key != K_GP_START)
return false;
Cvar_Set(&name, info->nameedit->text);
Cvar_Set(&team, info->teamedit->text);
@ -395,7 +400,10 @@ void MSetup_TransDraw (int x, int y, menucustom_t *option, menu_t *menu)
else
#endif
{
FS_LoadFile(va("gfx/player/%s.lmp", info->skinedit->text), &f);
if (*info->skinedit->text)
FS_LoadFile(va("gfx/player/%s.lmp", info->skinedit->text), &f);
else
f = NULL;
if (!f)
FS_LoadFile("gfx/menuplyr.lmp", &f);
}
@ -509,10 +517,10 @@ void M_Menu_Setup_f (void)
MC_AddCommand(menu, 64, 160, 96, "Top colour", SetupMenuColour);
MC_AddCommand(menu, 64, 160, 120, "Lower colour", SetupMenuColour);
MC_AddCommand(menu, 64, 160, 152, "Accept changes", ApplySetupMenu);
b = MC_AddConsoleCommand(menu, 64, 160, 168, "Network Settings", "menu_network\n");
MC_AddCommand(menu, 64, 320, 152, "Accept changes", ApplySetupMenu);
b = MC_AddConsoleCommand(menu, 64, 320, 168, "Network Settings", "menu_network\n");
b->common.tooltip = "Change network and client prediction settings.";
b = MC_AddConsoleCommand(menu, 64, 160, 176, "Teamplay Settings", "menu_teamplay\n");
b = MC_AddConsoleCommand(menu, 64, 320, 176, "Teamplay Settings", "menu_teamplay\n");
b->common.tooltip = "Change teamplay macro settings.";
menu->cursoritem = (menuoption_t*)MC_AddCursorSmall(menu, &resel, 54, 32);

View File

@ -2516,17 +2516,6 @@ void M_Menu_Video_f (void)
"reverseportrait",
NULL
};
extern cvar_t sys_glesversion_cvar;
static const char *glesopts[] = {
"GLES 1",
"GLES 2",
NULL
};
static const char *glesvalues[] = {
"1",
"2",
NULL
};
#else
extern cvar_t vid_renderer;
static const char *rendererops[] =
@ -2701,7 +2690,6 @@ void M_Menu_Video_f (void)
MB_TEXT("^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082", true),
#ifdef ANDROID
MB_COMBOCVAR("Orientation", sys_orientation, orientationopts, orientationvalues, NULL),
MB_COMBOCVAR("GLES Version", sys_glesversion_cvar, glesopts, glesvalues, NULL),
#else
MB_COMBOCVAR("Renderer", vid_renderer, rendererops, renderervalues, NULL),
MB_COMBOCVARRETURN("Display Mode", vid_fullscreen, fullscreenopts, fullscreenvalues, info->dispmode, vid_fullscreen.description),

View File

@ -473,7 +473,7 @@ void M_Menu_SinglePlayer_f (void)
MC_AddCenterPicture(menu, 4, 24, "gfx/ttl_sgl.lmp");
menu->selecteditem = (menuoption_t*)
MC_AddConsoleCommandQBigFont (menu, 72, 32, "New Game", va("closemenu;disconnect;maxclients 1;deathmatch 0;coop %i;startmap_sp\n", cl_splitscreen.ival>0));
MC_AddConsoleCommandQBigFont (menu, 72, 32, "New Game", "closemenu;disconnect;maxclients 1;samelevel \"\";deathmatch \"\";set_calc coop ($cl_splitscreen>0);startmap_sp\n");
MC_AddConsoleCommandQBigFont (menu, 72, 52, "Load Game", "menu_load\n");
MC_AddConsoleCommandQBigFont (menu, 72, 72, "Save Game", "menu_save\n");
@ -499,19 +499,44 @@ void M_Menu_SinglePlayer_f (void)
}
else
{
const char *opts[] =
{
"Single",
"Dual",
"Tripple",
"QUAD",
NULL
};
const char *vals[] =
{
"0",
"1",
"2",
"3",
NULL
};
int width;
if (R_GetShaderSizes(p, &width, NULL, true) <= 0)
width = 232;
MC_AddPicture(menu, 72, 32, 232, 64, "gfx/sp_menu.lmp");
b = MC_AddConsoleCommand (menu, 16, 304, 32, "", va("closemenu;disconnect;maxclients 1;samelevel 0;deathmatch 0;coop %i;startmap_sp\n", cl_splitscreen.ival>0));
b = MC_AddConsoleCommand (menu, 72, 304, 32, "", "closemenu;disconnect;maxclients 1;samelevel \"\";deathmatch \"\";set_calc coop ($cl_splitscreen>0);startmap_sp\n");
menu->selecteditem = (menuoption_t *)b;
b->common.width = p->width;
b->common.width = width;
b->common.height = 20;
b = MC_AddConsoleCommand (menu, 16, 304, 52, "", "menu_load\n");
b->common.width = p->width;
b = MC_AddConsoleCommand (menu, 72, 304, 52, "", "menu_load\n");
b->common.width = width;
b->common.height = 20;
b = MC_AddConsoleCommand (menu, 16, 304, 72, "", "menu_save\n");
b->common.width = p->width;
b = MC_AddConsoleCommand (menu, 72, 304, 72, "", "menu_save\n");
b->common.width = width;
b->common.height = 20;
b = (menubutton_t*)MC_AddCvarCombo(menu, 72, 72+width/2, 92, "", &cl_splitscreen, opts, vals);
MC_AddWhiteText(menu, 72, 0, 92, "^aSplitscreen", false);
b->common.height = 20;
b->common.width = width;
menu->cursoritem = (menuoption_t*)MC_AddCursor(menu, &resel, 54, 32);
}
#endif

View File

@ -308,11 +308,16 @@ struct pendingtextureinfo
//small formats.
PTI_R8,
PTI_RG8,
//compressed formats
//(desktop) compressed formats
PTI_S3RGB1,
PTI_S3RGBA1,
PTI_S3RGBA3,
PTI_S3RGBA5,
//(mobile) compressed formats
PTI_ETC1_RGB8, //limited form
PTI_ETC2_RGB8, //extended form
PTI_ETC2_RGB8A1,
PTI_ETC2_RGB8A8,
//weird specialcase mess to take advantage of webgl so we don't need redundant bloat where we're already strugging with potential heap limits
PTI_WHOLEFILE,
//depth formats

View File

@ -3141,8 +3141,8 @@ int CL_ReadServerInfo(char *msg, enum masterprotocol_e prototype, qboolean favor
break;
details.players[clnum].time = atoi(token);
msg = token;
token = strchr(msg+1, ' ');
if (!token) //probably q2 response
token = COM_Parse(token);
if (!*token) //probably q2 response
{
//see if this is actually a Quake2 server.
token = strchr(msg+1, '\"');
@ -3275,6 +3275,15 @@ int CL_ReadServerInfo(char *msg, enum masterprotocol_e prototype, qboolean favor
msg++;
}
}
if ((info->special & SS_PROTOCOLMASK) == SS_DARKPLACES && !info->numbots)
{
info->numbots = atoi(Info_ValueForKey(details.info, "bots"));
if (info->numbots > info->players)
info->numbots = info->players;
info->numhumans -= info->numbots;
}
if (!info->moreinfo && ((slist_cacheinfo.value == 2 || NET_CompareAdr(&info->adr, &selectedserver.adr)) || (info->special & SS_KEEPINFO)))
info->moreinfo = Z_Malloc(sizeof(serverdetailedinfo_t));
if (NET_CompareAdr(&info->adr, &selectedserver.adr))

View File

@ -30,6 +30,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#if (defined(GLQUAKE) || defined(VKQUAKE)) && defined(MULTITHREAD)
#define THREADEDWORLD
#endif
#ifdef BEF_PUSHDEPTH
qboolean r_pushdepth;
#endif
extern cvar_t r_ambient;
@ -2545,7 +2548,14 @@ void Surf_GenBrushBatches(batch_t **batches, entity_t *ent)
currententity = NULL;
}
bef = BEF_PUSHDEPTH;
#ifdef BEF_PUSHDEPTH
if (r_pushdepth)
bef = BEF_PUSHDEPTH;
else
bef = 0;
#else
bef = 0;
#endif
if (ent->flags & RF_ADDITIVE)
bef |= BEF_FORCEADDITIVE;
#ifdef HEXEN2
@ -3662,6 +3672,10 @@ void Surf_NewMap (void)
{
char namebuf[MAX_QPATH];
extern cvar_t host_mapname;
#ifdef BEF_PUSHDEPTH
extern cvar_t r_polygonoffset_submodel_maps;
char *s;
#endif
int i;
memset (&r_worldentity, 0, sizeof(r_worldentity));
@ -3684,6 +3698,19 @@ void Surf_NewMap (void)
r_oldviewcluster = 0;
r_viewcluster2 = -1;
r_oldviewcluster2 = 0;
#ifdef BEF_PUSHDEPTH
r_pushdepth = false;
for (s = r_polygonoffset_submodel_maps.string; s && *s; )
{
s = COM_Parse(s);
if (*com_token)
if (wildcmp(com_token, namebuf))
{
r_pushdepth = true;
break;
}
}
#endif
TRACE(("dbg: Surf_NewMap: clear particles\n"));
P_ClearParticles ();

View File

@ -552,6 +552,7 @@ qbyte *R_MarkLeaves_Q2 (void);
qbyte *R_MarkLeaves_Q3 (void);
void R_SetFrustum (float projmat[16], float viewmat[16]);
void R_SetRenderer(rendererinfo_t *ri);
void R_RegisterRenderer(rendererinfo_t *ri);
void R_AnimateLight (void);
void R_UpdateHDR(vec3_t org);
void R_UpdateLightStyle(unsigned int style, const char *stylestring, float r, float g, float b);

View File

@ -250,8 +250,28 @@ void S_SoundInfo_f(void)
}
#ifdef VOICECHAT
#ifdef SPEEX_STATIC
#include <speex.h>
#include <speex_preprocess.h>
#else
typedef struct {int stuff[15];} SpeexBits;
typedef struct SpeexMode SpeexMode;
typedef struct SpeexPreprocessState SpeexPreprocessState;
typedef qint16_t spx_int16_t;
#define SPEEX_MODEID_NB 0
#define SPEEX_MODEID_WB 1
#define SPEEX_MODEID_UWB 2
#define SPEEX_GET_FRAME_SIZE 3
#define SPEEX_SET_SAMPLING_RATE 24
#define SPEEX_GET_SAMPLING_RATE 25
#define SPEEX_PREPROCESS_SET_DENOISE 0
#define SPEEX_PREPROCESS_SET_AGC 2
#define SPEEX_PREPROCESS_SET_AGC_MAX_GAIN 30
#endif
enum
{

View File

@ -292,6 +292,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define CL_MASTER //query master servers and stuff for a dynamic server listing.
#define R_XFLIP //allow view to be flipped horizontally
#define TEXTEDITOR
#define IMAGEFMT_KTX //Khronos TeXture. common on gles3 devices for etc2 compression
#define IMAGEFMT_DDS //a sort of image file format.
#define IMAGEFMT_BLP //a sort of image file format.
#ifndef RTLIGHTS
@ -530,13 +531,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#undef HEADLESSQUAKE
#endif
#ifdef ANDROID
#undef RTLIGHTS
#ifndef SPEEX_STATIC
#undef VOICECHAT
#endif
#undef TEXTEDITOR
#define GLESONLY //should reduce the conditions a little
#undef HEADLESSQUAKE
// #undef HEADLESSQUAKE
#endif
#if defined(NACL)
//stuff is sandboxed.

View File

@ -3987,6 +3987,7 @@ void Cmd_Init (void)
Cmd_AddCommand ("cvar_purgedefaults", Cvar_PurgeDefaults_f);
Cmd_AddCommandD ("apropos", Cmd_Apropos_f, "Lists all cvars or commands with the specified substring somewhere in their name or descrition.");
Cmd_AddCommandD ("find", Cmd_Apropos_f, "Lists all cvars or commands with the specified substring somewhere in their name or descrition.");
Cmd_AddMacro("random", Macro_Random, true);
Cmd_AddMacro("time", Macro_Time, true);

View File

@ -2052,7 +2052,7 @@ static void BE_DrawMeshChain_Internal(void)
float pushdepth = shaderstate.curshader->polyoffset.factor;
// float pushfactor;
#ifndef NOLEGACY
#ifdef BEF_PUSHDEPTH
if (shaderstate.flags & BEF_PUSHDEPTH)
{
extern cvar_t r_polygonoffset_submodel_factor;
@ -3314,7 +3314,7 @@ void D3D8BE_RenderShadowBuffer(unsigned int numverts, IDirect3DVertexBuffer8 *vb
{
#ifdef FIXME
float pushdepth = shaderstate.curshader->polyoffset.factor;
#ifndef NOLEGACY
#ifdef BEF_PUSHDEPTH
extern cvar_t r_polygonoffset_submodel_factor;
// if (shaderstate.flags & BEF_PUSHDEPTH)
pushdepth += r_polygonoffset_submodel_factor.value;

View File

@ -2202,7 +2202,7 @@ static void BE_DrawMeshChain_Internal(void)
float pushdepth = shaderstate.curshader->polyoffset.factor;
// float pushfactor;
#ifndef NOLEGACY
#ifdef BEF_PUSHDEPTH
if (shaderstate.flags & BEF_PUSHDEPTH)
{
extern cvar_t r_polygonoffset_submodel_factor;
@ -3484,7 +3484,7 @@ void D3D9BE_BaseEntTextures(void)
void D3D9BE_RenderShadowBuffer(unsigned int numverts, IDirect3DVertexBuffer9 *vbuf, unsigned int numindicies, IDirect3DIndexBuffer9 *ibuf)
{
float pushdepth = shaderstate.curshader->polyoffset.factor;
#ifndef NOLEGACY
#ifdef BEF_PUSHDEPTH
extern cvar_t r_polygonoffset_submodel_factor;
// if (shaderstate.flags & BEF_PUSHDEPTH)
pushdepth += r_polygonoffset_submodel_factor.value;

View File

@ -23,7 +23,7 @@
>
<Tool
Name="VCNMakeTool"
BuildCommandLine="cd $(InputDir)\.. &amp;&amp; vcify make droid-dbg ANDROID_HOME=C:/Games/tools/android-sdk ANDROID_NDK_ROOT=C:/Games/tools/android-ndk-r8e ANT=C:/Games/tools/apache-ant-1.8.2/bin/ant JAVATOOL=&quot;C:/Program\ Files/Java/jdk1.7.0_02/bin/&quot; -j8 DROID_ARCH=&quot;armeabi x86&quot;"
BuildCommandLine="cd $(InputDir)\.. &amp;&amp; vcify make droid-dbg ANDROID_HOME=C:/Games/tools/android-sdk ANDROID_NDK_ROOT=C:/Games/tools/android-ndk-r8e ANT=C:/Games/tools/apache-ant-1.8.2/bin/ant JAVATOOL=&quot;C:/Program\ Files/Java/jdk1.7.0_02/bin/&quot; -j8 DROID_ARCH=&quot;armeabi x86 armeabi-v7a&quot;"
ReBuildCommandLine=""
CleanCommandLine="cd $(InputDir)\.. &amp;&amp; vcify make clean -j8"
Output=""

View File

@ -352,6 +352,7 @@ Global
{4735677B-6D5A-4BE6-A945-CB32DEADBEEF}.GLRelease|Win32.ActiveCfg = Release|Win32
{4735677B-6D5A-4BE6-A945-CB32DEADBEEF}.GLRelease|x64.ActiveCfg = Release|Win32
{4735677B-6D5A-4BE6-A945-CB32DEADBEEF}.MDebug|Win32.ActiveCfg = Debug|Win32
{4735677B-6D5A-4BE6-A945-CB32DEADBEEF}.MDebug|Win32.Build.0 = Debug|Win32
{4735677B-6D5A-4BE6-A945-CB32DEADBEEF}.MDebug|x64.ActiveCfg = Release|Win32
{4735677B-6D5A-4BE6-A945-CB32DEADBEEF}.MinGLDebug|Win32.ActiveCfg = Debug|Win32
{4735677B-6D5A-4BE6-A945-CB32DEADBEEF}.MinGLDebug|x64.ActiveCfg = Debug|Win32

View File

@ -4,7 +4,7 @@
android:versionCode="1"
android:versionName="1.05"
android:installLocation="auto">
<uses-sdk android:minSdkVersion="4" android:targetSdkVersion="8"/>
<uses-sdk android:minSdkVersion="5" android:targetSdkVersion="8"/>
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
<uses-permission android:name="android.permission.VIBRATE"></uses-permission>

View File

@ -1,18 +1,12 @@
package com.fteqw;
import javax.microedition.khronos.egl.EGLContext;
import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.opengles.GL10;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;
import android.opengl.GLSurfaceView;
import android.view.SurfaceView;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.app.AlertDialog;
@ -39,11 +33,9 @@ public class FTEDroidActivity extends Activity
private Sensor sensoracc;
private Sensor sensorgyro;
private FTEView view;
float acc_x, acc_y, acc_z; /*might be some minor race condition on these*/
float gyro_x, gyro_y, gyro_z; /*might be some minor race condition on these*/
private String basedir, userdir;
private audiothreadclass audiothread;
private class audiothreadclass extends Thread
{
@ -110,6 +102,14 @@ public class FTEDroidActivity extends Activity
timetodie = false;
}
};
public void audioStop()
{
if (audiothread != null)
{
audiothread.killoff();
audiothread = null;
}
}
private void audioInit(int sspeed, int schannels, int sbits)
{
if (audiothread == null)
@ -121,14 +121,6 @@ public class FTEDroidActivity extends Activity
audiothread.start();
}
}
public void audioStop()
{
if (audiothread != null)
{
audiothread.killoff();
audiothread = null;
}
}
public void audioResume()
{
if (audiothread != null)
@ -137,9 +129,9 @@ public class FTEDroidActivity extends Activity
audiothread.start();
}
}
class FTEMultiTouchInputEvent extends FTELegacyInputEvent
{
/*Requires API level 5+ (android 2.0+)*/
@ -156,9 +148,9 @@ public class FTEDroidActivity extends Activity
int id;
float x, y, size;
final int act = event.getAction();
domove(event);
switch(act & event.ACTION_MASK)
{
case MotionEvent.ACTION_DOWN:
@ -215,362 +207,227 @@ public class FTEDroidActivity extends Activity
}
}
private class FTEEGLConfig implements GLSurfaceView.EGLConfigChooser
private class FTEView extends SurfaceView implements SensorEventListener,android.view.SurfaceHolder.Callback
{
int version;
public void setversion(FTEView view, int version)
{
this.version = version;
view.setEGLContextClientVersion(version);
}
public boolean CheckGLES2Support()
{
EGL10 egl = (EGL10) EGLContext.getEGL();
EGLDisplay display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
EGLConfig cfg;
int oldver = this.version;
int[] version = new int[2];
egl.eglInitialize(display, version);
this.version = 2;
cfg = chooseConfig(egl, display);
this.version = oldver;
int[] value = {0};
egl.eglGetConfigAttrib(display, cfg, EGL10.EGL_RENDERABLE_TYPE, value);
egl.eglTerminate(display);
return ((value[0] & 4) == 4);
}
public int notifiedflags;
final private FTEDroidActivity act;
private FTELegacyInputEvent inputevent;
@Override
public EGLConfig chooseConfig (EGL10 egl, EGLDisplay display)
{
int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
EGLConfig[] cfg = new EGLConfig[64];
int[] num_configs = {0};
int[] value = {0};
int i;
int[] attribs =
{
egl.EGL_RENDERABLE_TYPE, (version>=2?4:1)/*egl.EGL_OPENGL_ES2_BIT*/,
// egl.EGL_SURFACE_TYPE, egl.EGL_WINDOW_BIT,
egl.EGL_BLUE_SIZE, 5,
egl.EGL_GREEN_SIZE, 6,
egl.EGL_RED_SIZE, 5,
egl.EGL_DEPTH_SIZE, 16,
// egl.EGL_STENCIL_SIZE, 8,
egl.EGL_NONE, egl.EGL_NONE
};
private FTERenderThread rthread;
public boolean inited;
if (!egl.eglChooseConfig(display, attribs, cfg, 64, num_configs))
throw new IllegalArgumentException("eglChooseConfig failed");
if (num_configs[0] == 0)
class FTERenderThread extends Thread
{
private FTEView theview;
private android.view.SurfaceHolder surfaceholder;
private android.view.Surface surf;
private boolean doquit;
public FTERenderThread(FTEView parent, android.view.SurfaceHolder sh)
{
attribs[1] = 1; //egl.EGL_RENDERABLE_TYPE, 1/*egl.EGL_OPENGL_ES_BIT*/,
if (!egl.eglChooseConfig(display, attribs, cfg, 64, num_configs))
throw new IllegalArgumentException("eglChooseConfig failed");
if (num_configs[0] == 0)
{
throw new IllegalArgumentException("eglChooseConfig didn't report any valid configs");
}
surfaceholder = sh;
surf = sh.getSurface();
theview = parent;
}
android.util.Log.i("FTEDroid", "Found " + num_configs[0] + " EGL configs.");
//try to find a gles2 context instead.
for (i = 0; i < num_configs[0]; i++)
@Override
public void run()
{
android.util.Log.i("FTEDroid", "Config " + i + ":");
egl.eglGetConfigAttrib(display, cfg[i], egl.EGL_RED_SIZE, value);
android.util.Log.i("FTEDroid", "EGL_RED_SIZE " + value[0]);
egl.eglGetConfigAttrib(display, cfg[i], egl.EGL_GREEN_SIZE, value);
android.util.Log.i("FTEDroid", "EGL_GREEN_SIZE " + value[0]);
egl.eglGetConfigAttrib(display, cfg[i], egl.EGL_BLUE_SIZE, value);
android.util.Log.i("FTEDroid", "EGL_BLUE_SIZE " + value[0]);
egl.eglGetConfigAttrib(display, cfg[i], egl.EGL_DEPTH_SIZE, value);
android.util.Log.i("FTEDroid", "EGL_DEPTH_SIZE " + value[0]);
egl.eglGetConfigAttrib(display, cfg[i], egl.EGL_STENCIL_SIZE, value);
android.util.Log.i("FTEDroid", "EGL_STENCIL_SIZE " + value[0]);
egl.eglGetConfigAttrib(display, cfg[i], egl.EGL_RENDERABLE_TYPE, value);
android.util.Log.i("FTEDroid", "EGL_RENDERABLE_TYPE " + value[0]);
if ((value[0] & 4) == 4)
FTEDroidEngine.setwindow(surf);
if (!theview.inited)
{
android.util.Log.i("FTEDroid", "Found a GLES2 context!");
return cfg[i];
FTEDroidEngine.init(0, 0, basedir, userdir);
theview.inited = true;
}
}
return cfg[0];
}
}
private class FTERenderer implements GLSurfaceView.Renderer
{
private boolean inited;
public int glesversion;
FTEDroidActivity act;
FTEView theview;
FTEEGLConfig cfgchooser;
int notifiedflags;
void updateGLESVersion()
{
if (FTEDroidEngine.getpreferedglesversion() < 2)
{
android.util.Log.i("FTEDroid", "Using GLES1");
this.glesversion = 1;
}
else if (android.os.Build.VERSION.SDK_INT >= 8) //could be 5 with setEGLContextFactory instead of setEGLContextClientVersion
{
if (cfgchooser.CheckGLES2Support())
while (!doquit)
{
android.util.Log.i("FTEDroid", "Support for GLES2 detected");
this.glesversion = 2;
cfgchooser.setversion(theview, this.glesversion);
}
else
{
android.util.Log.i("FTEDroid", "GLES2 not supported. Using GLES1.");
this.glesversion = 1;
}
}
else
{
android.util.Log.i("FTEDroid", "GLES2 requires android 2.2+");
this.glesversion = 1;
}
}
FTERenderer(FTEView view, FTEDroidActivity parent)
{
act = parent;
theview = view;
if (!inited)
{
FTEDroidEngine.init(0, 0, 0, 0, 0, basedir, userdir);
inited = true;
}
cfgchooser = new FTEEGLConfig();
// theview.setEGLConfigChooser(cfgchooser);
updateGLESVersion();
}
@Override
public void onDrawFrame(GL10 gl)
{
if (inited == true)
{
int flags;
flags = FTEDroidEngine.frame(act.acc_x, act.acc_y, act.acc_z, act.gyro_x, act.gyro_y, act.gyro_z);
if (flags != notifiedflags)
{
if (((flags ^ notifiedflags) & 1) != 0)
{
final int fl = flags;
Runnable r = new Runnable()
{ //doing this on the ui thread because android sucks.
public void run()
{
InputMethodManager im = (InputMethodManager) act.getSystemService(Context.INPUT_METHOD_SERVICE);
if (im != null)
{
if ((fl & 1) != 0)
{
// getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
im.showSoftInput(theview, 0);//InputMethodManager.SHOW_FORCED);
}
else
{
// getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
im.hideSoftInputFromWindow(theview.getWindowToken(), 0);
}
}
else
android.util.Log.i("FTEDroid", "IMM failed");
}
};
act.runOnUiThread(r);
}
if (((flags ^ notifiedflags) & 2) != 0)
{
int dur = FTEDroidEngine.getvibrateduration();
flags &= ~2;
Vibrator vib = (Vibrator) act.getSystemService(Context.VIBRATOR_SERVICE);
if (vib != null)
{
android.util.Log.i("FTEDroid", "Vibrate " + dur + "ms");
vib.vibrate(dur);
}
else
android.util.Log.i("FTEDroid", "No vibrator device");
}
if (((flags ^ notifiedflags) & 4) != 0)
{
final int fl = flags;
Runnable r = new Runnable()
{
public void run()
{
if ((fl & 4) != 0)
act.getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
else
act.getWindow().setFlags(0, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
};
act.runOnUiThread(r);
}
if (((flags ^ notifiedflags) & 8) != 0)
{
final String errormsg = FTEDroidEngine.geterrormessage();
inited = false;
if (errormsg.equals(""))
{
finish();
System.exit(0);
}
//8 means sys error
Runnable r = new Runnable()
{
public void run()
{
theview.setVisibility(theview.GONE);
AlertDialog ad = new AlertDialog.Builder(act).create();
ad.setTitle("FTE ERROR");
ad.setMessage(errormsg);
ad.setCancelable(false);
ad.setButton("Ok", new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int which)
{
finish();
System.exit(0);
}
}
);
ad.show();
}
};
act.runOnUiThread(r);
}
if (((flags ^ notifiedflags) & 16) != 0)
{
//16 means orientation cvar change
Runnable r = new Runnable()
{
public void run()
{
String ors = FTEDroidEngine.getpreferedorientation();
int ori = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR;
if (ors.equalsIgnoreCase("unspecified"))
ori = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
else if (ors.equalsIgnoreCase("landscape"))
ori = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
else if (ors.equalsIgnoreCase("portrait"))
ori = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
else if (ors.equalsIgnoreCase("user"))
ori = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER;
else if (ors.equalsIgnoreCase("behind"))
ori = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
else if (ors.equalsIgnoreCase("sensor"))
ori = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR;
else if (ors.equalsIgnoreCase("nosensor"))
ori = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
//the following are api level 9+
else if (ors.equalsIgnoreCase("sensorlandscape"))
ori = 6;//android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE;
else if (ors.equalsIgnoreCase("sensorportrait"))
ori = 7;//android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT;
else if (ors.equalsIgnoreCase("reverselandscape"))
ori = 8;//android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
else if (ors.equalsIgnoreCase("reverseportrait"))
ori = 9;//android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
else if (ors.equalsIgnoreCase("fullsensor"))
ori = 10;//android.content.pm.ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR;
//and the default, because specifying it again is always useless.
else
ori = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR;
android.util.Log.i("FTEDroid", "Orientation changed to " + ori + " (" + ors + ").");
act.setRequestedOrientation(ori);
}
};
act.runOnUiThread(r);
int flags = FTEDroidEngine.frame();
//fixme: check return value for events to respond to
//fixme: move to proper vid_restart thing.
int wantver = FTEDroidEngine.getpreferedglesversion();
if (wantver != 0 && this.glesversion != 0 && wantver != this.glesversion)
if (flags != notifiedflags)
{
if (((flags ^ notifiedflags) & 1) != 0)
{
inited = false;
wantver = FTEDroidEngine.getpreferedglesversion();
android.util.Log.i("FTEDroid", "Killing old gl state");
FTEDroidEngine.killglcontext();
android.util.Log.i("FTEDroid", "old gl state killed, queueing context kill");
r = new Runnable()
{
final int fl = flags;
Runnable r = new Runnable()
{ //doing this on the ui thread because android sucks.
public void run()
{
android.util.Log.i("FTEDroid", "Attempting to restart view");
//create a new view and use that, because the desired gl context version might have changed
view = new FTEView(act);
setContentView(view);
InputMethodManager im = (InputMethodManager) act.getSystemService(Context.INPUT_METHOD_SERVICE);
if (im != null)
{
if ((fl & 1) != 0)
{
// getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
im.showSoftInput(theview, 0);//InputMethodManager.SHOW_FORCED);
}
else
{
// getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
im.hideSoftInputFromWindow(theview.getWindowToken(), 0);
}
}
else
android.util.Log.i("FTEDroid", "IMM failed");
}
};
act.runOnUiThread(r);
}
}
if (((flags ^ notifiedflags) & 32) != 0)
{
final int fl = flags;
Runnable r = new Runnable()
if (((flags ^ notifiedflags) & 2) != 0)
{
public void run()
int dur = FTEDroidEngine.getvibrateduration();
flags &= ~2;
Vibrator vib = (Vibrator) act.getSystemService(Context.VIBRATOR_SERVICE);
if (vib != null)
{
if ((fl & 32) != 0)
act.audioInit(FTEDroidEngine.audioinfo(0), FTEDroidEngine.audioinfo(1), FTEDroidEngine.audioinfo(2));
else
act.audioStop();
android.util.Log.i("FTEDroid", "Vibrate " + dur + "ms");
vib.vibrate(dur);
}
};
act.runOnUiThread(r);
else
android.util.Log.i("FTEDroid", "No vibrator device");
}
if (((flags ^ notifiedflags) & 4) != 0)
{
final int fl = flags;
Runnable r = new Runnable()
{
public void run()
{
if ((fl & 4) != 0)
act.getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
else
act.getWindow().setFlags(0, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
};
act.runOnUiThread(r);
}
if (((flags ^ notifiedflags) & 8) != 0)
{
final String errormsg = FTEDroidEngine.geterrormessage();
inited = false;
if (errormsg.equals(""))
{
finish();
System.exit(0);
}
//8 means sys error
Runnable r = new Runnable()
{
public void run()
{
theview.setVisibility(theview.GONE);
AlertDialog ad = new AlertDialog.Builder(act).create();
ad.setTitle("FTE ERROR");
ad.setMessage(errormsg);
ad.setCancelable(false);
ad.setButton("Ok", new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int which)
{
finish();
System.exit(0);
}
}
);
ad.show();
}
};
act.runOnUiThread(r);
}
if (((flags ^ notifiedflags) & 16) != 0)
{
//16 means orientation cvar change
Runnable r = new Runnable()
{
public void run()
{
String ors = FTEDroidEngine.getpreferedorientation();
int ori = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR;
if (ors.equalsIgnoreCase("unspecified"))
ori = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
else if (ors.equalsIgnoreCase("landscape"))
ori = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
else if (ors.equalsIgnoreCase("portrait"))
ori = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
else if (ors.equalsIgnoreCase("user"))
ori = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER;
else if (ors.equalsIgnoreCase("behind"))
ori = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
else if (ors.equalsIgnoreCase("sensor"))
ori = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR;
else if (ors.equalsIgnoreCase("nosensor"))
ori = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
//the following are api level 9+
else if (ors.equalsIgnoreCase("sensorlandscape"))
ori = 6;//android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE;
else if (ors.equalsIgnoreCase("sensorportrait"))
ori = 7;//android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT;
else if (ors.equalsIgnoreCase("reverselandscape"))
ori = 8;//android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
else if (ors.equalsIgnoreCase("reverseportrait"))
ori = 9;//android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
else if (ors.equalsIgnoreCase("fullsensor"))
ori = 10;//android.content.pm.ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR;
//and the default, because specifying it again is always useless.
else
ori = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR;
android.util.Log.i("FTEDroid", "Orientation changed to " + ori + " (" + ors + ").");
act.setRequestedOrientation(ori);
}
};
act.runOnUiThread(r);
}
if (((flags ^ notifiedflags) & 32) != 0)
{
final int fl = flags;
Runnable r = new Runnable()
{
public void run()
{
if ((fl & 32) != 0)
act.audioInit(FTEDroidEngine.audioinfo(0), FTEDroidEngine.audioinfo(1), FTEDroidEngine.audioinfo(2));
else
act.audioStop();
}
};
act.runOnUiThread(r);
}
//clear anything which is an impulse
notifiedflags = flags;
}
//clear anything which is an impulse
notifiedflags = flags;
}
FTEDroidEngine.setwindow(null);
}
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height)
{
android.util.Log.i("FTEDroid", "Surface changed, now " + width + " by " + height + ".");
if (glesversion != 0 && inited)
{
android.util.DisplayMetrics metrics = new android.util.DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
FTEDroidEngine.init(width, height, metrics.xdpi, metrics.ydpi, glesversion, basedir, userdir);
}
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config)
{
FTEDroidEngine.newglcontext();
}
}
private class FTEView extends GLSurfaceView implements SensorEventListener
{
private final FTERenderer rndr;
public void requestExitAndWait()
{
doquit = true;
try { join(); } catch(InterruptedException e) {}
}
}
//SensorEventListener
public void onAccuracyChanged(Sensor sensor, int accuracy)
{
}
//SensorEventListener
public void onSensorChanged(final SensorEvent event)
{
if (event.sensor == sensoracc)
{
FTEDroidEngine.accelerometer(event.values[0], event.values[1], event.values[2]);
}
else if (event.sensor == sensorgyro)
{
FTEDroidEngine.gyroscope(event.values[0], event.values[1], event.values[2]);
}
}
/* private FTEJoystickInputEvent joystickevent;
class FTEJoystickInputEvent
{
@ -595,12 +452,12 @@ public class FTEDroidActivity extends Activity
return false;
}
}
*/
private FTELegacyInputEvent inputevent;
*/
public FTEView(FTEDroidActivity context)
{
super(context);
act = context;
getHolder().addCallback(this);
if (android.os.Build.VERSION.SDK_INT >= 5)
inputevent = new FTEMultiTouchInputEvent();
@ -610,30 +467,71 @@ public class FTEDroidActivity extends Activity
// if (android.os.Build.VERSION.SDK_INT >= 12)
// joystickevent = new FTEJoystickInputEvent();
rndr = new FTERenderer(this, context);
setRenderer(rndr);
setFocusable(true);
setFocusableInTouchMode(true);
}
@Override
protected void onDraw(android.graphics.Canvas canvas)
{ //no race conditions please.
}
@Override
protected void onAttachedToWindow()
{
android.util.Log.i("FTEDroid", "onAttachedToWindow");
super.onAttachedToWindow();
/*
if (rthread != null)
rthread.requestExitAndWait();
rthread = new FTERenderThread(this);
rthread.start();
*/
}
@Override
protected void onDetachedFromWindow()
{
android.util.Log.i("FTEDroid", "onDetachedFromWindow");
if (rthread != null)
rthread.requestExitAndWait();
rthread = null;
super.onDetachedFromWindow();
}
public void surfaceDestroyed(android.view.SurfaceHolder holder)
{
android.util.Log.i("FTEDroid", "surfaceDestroyed");
if (rthread != null)
rthread.requestExitAndWait();
rthread = null;
}
public void surfaceChanged(android.view.SurfaceHolder holder, int format, int width, int height)
{
android.util.Log.i("FTEDroid", "surfaceChanged");
}
public void surfaceCreated(android.view.SurfaceHolder holder)
{
android.util.Log.i("FTEDroid", "surfaceCreated");
if (rthread != null)
rthread.requestExitAndWait();
rthread = new FTERenderThread(this, holder);
rthread.start();
}
private boolean sendKey(final boolean presseddown, final int qcode, final int unicode)
{
return 0!=FTEDroidEngine.keypress(presseddown?1:0, qcode, unicode);
}
//view (inputs)
@Override
public boolean onTouchEvent(MotionEvent event)
{
return inputevent.go(event);
}
/*
@Override
public boolean onTrackballEvent(MotionEvent event)
{
int act = event.getAction();
float x = event.getX();
float y = event.getY();
}
*/
private static final int K_ENTER = 13;
private static final int K_ESCAPE = 27;
private static final int K_DEL = 127;
@ -754,7 +652,8 @@ public class FTEDroidActivity extends Activity
}
return 0;
}
//view (inputs)
@Override
public boolean onKeyDown(int keyCode, KeyEvent event)
{
@ -763,6 +662,7 @@ public class FTEDroidActivity extends Activity
return sendKey(true, qc, uc);
}
//view (inputs)
@Override
public boolean onKeyUp(int keyCode, KeyEvent event)
{
@ -770,59 +670,14 @@ public class FTEDroidActivity extends Activity
int qc = mapKey(keyCode, uc);
return sendKey(false, qc, uc);
}
/*
@Override
public boolean onGenericMotionEvent(android.view.MotionEvent event)
{
if (joystickevent)
if (joystickevent.go(event))
return true;
//FIXME: handle mouse and mousewheel
return false;
}
*/
/*
@Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs)
{
Log.d(TAG, "onCreateInputConnection");
BaseInputConnection fic = new BaseInputConnection(this, false);
outAttrs.actionLabel = null;
outAttrs.inputType = InputType.TYPE_NULL;
outAttrs.imeOptions = EditorInfo.IME_ACTION_NEXT;
return fic;
}
*/
public void onAccuracyChanged(Sensor sensor, int accuracy)
{
}
public void onSensorChanged(final SensorEvent event)
{
if (event.sensor == sensoracc)
{
acc_x = event.values[0];
acc_y = event.values[1];
acc_z = event.values[2];
}
else if (event.sensor == sensorgyro)
{
gyro_x = event.values[0];
gyro_y = event.values[1];
gyro_z = event.values[2];
}
}
}
private boolean runningintheemulator()
{
android.util.Log.i("FTEDroid", "model: " + android.os.Build.MODEL + " product: " + android.os.Build.PRODUCT + " device: " + android.os.Build.DEVICE);
return android.os.Build.MODEL.equals("sdk") && android.os.Build.PRODUCT.equals("sdk") && android.os.Build.DEVICE.equals("generic");
}
@Override
public void onCreate(Bundle savedInstanceState)
{
@ -839,26 +694,20 @@ public class FTEDroidActivity extends Activity
{
/*oh well, can just use the homedir instead*/
}
// try
// {
userdir = Environment.getExternalStorageDirectory().getPath() + "/fte";
// }
// catch(foo)
// {
// }
userdir = Environment.getExternalStorageDirectory().getPath() + "/fte";
android.util.Log.i("FTEDroid", "Base dir is \"" + basedir + "\".");
android.util.Log.i("FTEDroid", "User dir is \"" + userdir + "\".");
//go full-screen
//go full-screen
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState);
android.util.Log.i("FTEDroid", "create view");
view = new FTEView(this);
setContentView(view);
if (runningintheemulator())
{
@ -891,7 +740,7 @@ public class FTEDroidActivity extends Activity
audioResume();
}
@Override
protected void onStart()
{

View File

@ -2,19 +2,20 @@ package com.fteqw;
public class FTEDroidEngine
{
public static native void init(int w, int h, float dpix, float dpiy, int gles2, String apkpath, String usrpath); /* init/reinit */
public static native int frame(float ax, float ay, float az, float gx, float gy, float gz);
public static native void init(float dpix, float dpiy, String apkpath, String usrpath); /* init/reinit */
public static native int frame();
public static native int openfile(String filename);
public static native int getvibrateduration(); //in ms
public static native int keypress(int down, int qkey, int unicode);
public static native void motion(int act, int pointerid, float x, float y, float size);
public static native int paintaudio(byte[] stream, int len);
public static native void accelerometer(float ax, float ay, float az);
public static native void gyroscope(float gx, float gy, float gz);
public static native int paintaudio(byte[] stream, int len);
public static native int audioinfo(int arg);
public static native String geterrormessage();
public static native String getpreferedorientation();
public static native int getpreferedglesversion();
public static native void killglcontext();
public static native void newglcontext();
public static native void setwindow(android.view.Surface window);
public static native void windowresized(int w, int h);
static
{

View File

@ -93,8 +93,7 @@ struct {
program_t *programfixedemu[8];
texid_t tex_gbuf_normals;
texid_t tex_gbuf_diffuse;
texid_t tex_gbuf[2];
int fbo_current; //the one currently being rendered to
texid_t tex_sourcecol; /*this is used by $sourcecolour tgen*/
texid_t tex_sourcedepth;
@ -204,14 +203,14 @@ struct {
batch_t *wbatches;
} shaderstate;
static void BE_PolyOffset(qboolean pushdepth)
static void BE_PolyOffset(void)
{
polyoffset_t po;
po.factor = shaderstate.curshader->polyoffset.factor;
po.unit = shaderstate.curshader->polyoffset.unit;
#ifndef NOLEGACY
if (pushdepth)
#ifdef BEF_PUSHDEPTH
if (shaderstate.flags & BEF_PUSHDEPTH)
{
/*some quake doors etc are flush with the walls that they're meant to be hidden behind, or plats the same height as the floor, etc
we move them back very slightly using polygonoffset to avoid really ugly z-fighting*/
@ -242,14 +241,19 @@ static void BE_PolyOffset(qboolean pushdepth)
}
}
void GLBE_PolyOffsetStencilShadow(qboolean pushdepth)
void GLBE_PolyOffsetStencilShadow
#ifdef BEF_PUSHDEPTH
(qboolean pushdepth)
#else
(void)
#endif
{
extern cvar_t r_polygonoffset_stencil_offset, r_polygonoffset_stencil_factor;
polyoffset_t po;
po.factor = r_polygonoffset_stencil_factor.value;
po.unit = r_polygonoffset_stencil_offset.value;
#ifndef NOLEGACY
#ifdef BEF_PUSHDEPTH
if (pushdepth)
{
/*some quake doors etc are flush with the walls that they're meant to be hidden behind, or plats the same height as the floor, etc
@ -274,11 +278,16 @@ void GLBE_PolyOffsetStencilShadow(qboolean pushdepth)
qglDisable(GL_POLYGON_OFFSET_FILL);
}
}
static void GLBE_PolyOffsetShadowMap(qboolean pushdepth)
static void GLBE_PolyOffsetShadowMap
#ifdef BEF_PUSHDEPTH
(qboolean pushdepth)
#else
(void)
#endif
{
extern cvar_t r_polygonoffset_shadowmap_offset, r_polygonoffset_shadowmap_factor;
polyoffset_t po;
#ifndef NOLEGACY
#ifdef BEF_PUSHDEPTH
if (pushdepth)
{
/*some quake doors etc are flush with the walls that they're meant to be hidden behind, or plats the same height as the floor, etc
@ -1415,15 +1424,13 @@ void GLBE_DestroyFBOs(void)
//nuke deferred rendering stuff
if (shaderstate.tex_gbuf_diffuse)
for (i = 0; i < countof(shaderstate.tex_gbuf); i++)
{
Image_DestroyTexture(shaderstate.tex_gbuf_diffuse);
shaderstate.tex_gbuf_diffuse = r_nulltex;
}
if (shaderstate.tex_gbuf_normals)
{
Image_DestroyTexture(shaderstate.tex_gbuf_normals);
shaderstate.tex_gbuf_normals = r_nulltex;
if (shaderstate.tex_gbuf[i])
{
Image_DestroyTexture(shaderstate.tex_gbuf[i]);
shaderstate.tex_gbuf[i] = r_nulltex;
}
}
}
@ -4147,7 +4154,7 @@ static void DrawMeshes(void)
return;
#endif
BE_PolyOffset(shaderstate.flags & BEF_PUSHDEPTH);
BE_PolyOffset();
switch(shaderstate.mode)
{
case BEM_STENCIL:
@ -5493,6 +5500,7 @@ void GLBE_RenderToTexture(texid_t sourcecol, texid_t sourcedepth, texid_t destco
void GLBE_DrawLightPrePass(void)
{
unsigned int i;
qboolean redefine = false;
/*
walls(bumps) -> normalbuffer
@ -5507,45 +5515,37 @@ void GLBE_DrawLightPrePass(void)
GLBE_SubmitMeshes(cl.worldmodel->batches, SHADER_SORT_PORTAL, SHADER_SORT_PORTAL);
BE_SelectMode(BEM_GBUFFER);
if (!TEXVALID(shaderstate.tex_gbuf_normals) || vid.fbpwidth != shaderstate.tex_gbuf_normals->width || vid.fbpheight != shaderstate.tex_gbuf_normals->height)
for (i = 0; i < countof(shaderstate.tex_gbuf); i++)
{
if (!shaderstate.tex_gbuf_normals)
if (!TEXVALID(shaderstate.tex_gbuf[i]) || vid.fbpwidth != shaderstate.tex_gbuf[i]->width || vid.fbpheight != shaderstate.tex_gbuf[i]->height)
{
shaderstate.tex_gbuf_normals = Image_CreateTexture("***prepass normals***", NULL, 0);
qglGenTextures(1, &shaderstate.tex_gbuf_normals->num);
if (!shaderstate.tex_gbuf[i])
{
shaderstate.tex_gbuf[i] = Image_CreateTexture(va("***prepass %u***", i), NULL, 0);
qglGenTextures(1, &shaderstate.tex_gbuf[i]->num);
}
shaderstate.tex_gbuf[i]->width = vid.fbpwidth;
shaderstate.tex_gbuf[i]->height = vid.fbpheight;
redefine = true;
}
shaderstate.tex_gbuf_normals->width = vid.fbpwidth;
shaderstate.tex_gbuf_normals->height = vid.fbpheight;
redefine = true;
}
if (!TEXVALID(shaderstate.tex_gbuf_diffuse) || vid.fbpwidth != shaderstate.tex_gbuf_diffuse->width || vid.fbpheight != shaderstate.tex_gbuf_diffuse->height)
{
if (!shaderstate.tex_gbuf_diffuse)
{
shaderstate.tex_gbuf_diffuse = Image_CreateTexture("***prepass diffuse***", NULL, 0);
qglGenTextures(1, &shaderstate.tex_gbuf_diffuse->num);
}
shaderstate.tex_gbuf_diffuse->width = vid.fbpwidth;
shaderstate.tex_gbuf_diffuse->height = vid.fbpheight;
redefine = true;
}
//something changed, redefine the textures.
if (redefine)
{
GL_MTBind(0, GL_TEXTURE_2D, shaderstate.tex_gbuf_diffuse);
GL_MTBind(0, GL_TEXTURE_2D, shaderstate.tex_gbuf[1]);
qglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F_ARB, vid.fbpwidth, vid.fbpheight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
GL_MTBind(0, GL_TEXTURE_2D, shaderstate.tex_gbuf_normals);
GL_MTBind(0, GL_TEXTURE_2D, shaderstate.tex_gbuf[0]);
qglTexImage2D(GL_TEXTURE_2D, 0, (r_lightprepass==2)?GL_RGBA32F_ARB:GL_RGBA16F_ARB, vid.fbpwidth, vid.fbpheight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
}
/*set the FB up to draw surface info*/
oldfbo = GLBE_FBO_Update(&shaderstate.fbo_lprepass, FBO_RB_DEPTH, &shaderstate.tex_gbuf_normals, 1, r_nulltex, vid.fbpwidth, vid.fbpheight, 0);
oldfbo = GLBE_FBO_Update(&shaderstate.fbo_lprepass, FBO_RB_DEPTH, &shaderstate.tex_gbuf[0], 1, r_nulltex, vid.fbpwidth, vid.fbpheight, 0);
GL_ForceDepthWritable();
//FIXME: should probably clear colour buffer too.
qglClear(GL_DEPTH_BUFFER_BIT);
@ -5560,8 +5560,8 @@ void GLBE_DrawLightPrePass(void)
GLBE_SubmitMeshes(cl.worldmodel->batches, SHADER_SORT_OPAQUE, SHADER_SORT_OPAQUE);
/*reconfigure - now drawing diffuse light info using the previous fb image as a source image*/
GLBE_FBO_Sources(shaderstate.tex_gbuf_normals, r_nulltex);
GLBE_FBO_Update(&shaderstate.fbo_lprepass, FBO_RB_DEPTH, &shaderstate.tex_gbuf_diffuse, 1, r_nulltex, vid.fbpwidth, vid.fbpheight, 0);
GLBE_FBO_Sources(shaderstate.tex_gbuf[0], r_nulltex);
GLBE_FBO_Update(&shaderstate.fbo_lprepass, FBO_RB_DEPTH, &shaderstate.tex_gbuf[1], 1, r_nulltex, vid.fbpwidth, vid.fbpheight, 0);
BE_SelectMode(BEM_STANDARD);
qglClearColor (0,0,0,1);
@ -5573,7 +5573,7 @@ void GLBE_DrawLightPrePass(void)
/*final reconfigure - now drawing final surface data onto true framebuffer*/
GLBE_FBO_Pop(oldfbo);
GLBE_FBO_Sources(shaderstate.tex_gbuf_diffuse, r_nulltex);
GLBE_FBO_Sources(shaderstate.tex_gbuf[1], r_nulltex);
if (!oldfbo)
qglDrawBuffer(GL_BACK);

View File

@ -231,14 +231,6 @@ void GL_Set2D (qboolean flipped)
//====================================================================
#ifndef GL_COMPRESSED_RGB_S3TC_DXT1_EXT
#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0
#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1
#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2
#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3
#endif
//note: needs to be bound first, so the 'targ' argument shouldn't be a problem.
static void GL_Texturemode_Apply(GLenum targ, unsigned int flags)
{
@ -360,7 +352,7 @@ qboolean GL_LoadTextureMips(texid_t tex, const struct pendingtextureinfo *mips)
{
if (mips->mip[i].width != max(1,(mips->mip[i-1].width>>1)) ||
mips->mip[i].height != max(1,(mips->mip[i-1].height>>1)))
{ //okay, this mip looks like it was sized wrongly. this can easily happen with dds files made for direct3d.
{ //okay, this mip looks like it was sized wrongly. this can easily happen with npot dds files made for direct3d.
nummips = i;
break;
}
@ -508,7 +500,7 @@ qboolean GL_LoadTextureMips(texid_t tex, const struct pendingtextureinfo *mips)
case PTI_RGB565:
qglTexImage2D(targface, j, GL_RGB, mips->mip[i].width, mips->mip[i].height, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, mips->mip[i].data);
break;
//compressed formats
//(desktop) compressed formats
case PTI_S3RGB1:
qglCompressedTexImage2DARB(targface, j, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, mips->mip[i].width, mips->mip[i].height, 0, mips->mip[i].datasize, mips->mip[i].data);
break;
@ -521,6 +513,21 @@ qboolean GL_LoadTextureMips(texid_t tex, const struct pendingtextureinfo *mips)
case PTI_S3RGBA5:
qglCompressedTexImage2DARB(targface, j, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, mips->mip[i].width, mips->mip[i].height, 0, mips->mip[i].datasize, mips->mip[i].data);
break;
//(mobile) compressed formats
case PTI_ETC1_RGB8:
case PTI_ETC2_RGB8:
//etc2 is a superset of etc1. we distinguish only for hardware that cannot recognise etc2's 'invalid' encodings
if (sh_config.texfmt[PTI_ETC2_RGB8])
qglCompressedTexImage2DARB(targface, j, GL_COMPRESSED_RGB8_ETC2, mips->mip[i].width, mips->mip[i].height, 0, mips->mip[i].datasize, mips->mip[i].data);
else
qglCompressedTexImage2DARB(targface, j, GL_ETC1_RGB8_OES, mips->mip[i].width, mips->mip[i].height, 0, mips->mip[i].datasize, mips->mip[i].data);
break;
case PTI_ETC2_RGB8A1:
qglCompressedTexImage2DARB(targface, j, GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2, mips->mip[i].width, mips->mip[i].height, 0, mips->mip[i].datasize, mips->mip[i].data);
break;
case PTI_ETC2_RGB8A8:
qglCompressedTexImage2DARB(targface, j, GL_COMPRESSED_RGBA8_ETC2_EAC, mips->mip[i].width, mips->mip[i].height, 0, mips->mip[i].datasize, mips->mip[i].data);
break;
}
}
}

View File

@ -290,6 +290,7 @@ static qboolean Shader_EvaluateCondition(shader_t *shader, char **ptr)
{
char *token;
cvar_t *cv;
float lhs;
qboolean conditiontrue = true;
token = COM_ParseExt(ptr, false, false);
if (*token == '!')
@ -297,9 +298,14 @@ static qboolean Shader_EvaluateCondition(shader_t *shader, char **ptr)
conditiontrue = false;
token++;
}
if (*token == '$')
if (*token >= '0' && *token <= '9')
lhs = strtod(token, NULL);
else
{
token++;
if (*token == '$')
token++;
if (*token == '#')
conditiontrue = conditiontrue == !!Shader_FloatArgument(shader, token);
else if (!Q_stricmp(token, "lpp"))
@ -348,17 +354,6 @@ static qboolean Shader_EvaluateCondition(shader_t *shader, char **ptr)
else if (!Q_stricmp(token, "loweroverlay"))
conditiontrue = conditiontrue == false;
else
{
Con_Printf("Unrecognised builtin shader condition '%s'\n", token);
conditiontrue = conditiontrue == false;
}
}
else
{
float lhs;
if (*token >= '0' && *token <= '9')
lhs = strtod(token, NULL);
else
{
cv = Cvar_Get(token, "", 0, "Shader Conditions");
if (cv)
@ -369,37 +364,37 @@ static qboolean Shader_EvaluateCondition(shader_t *shader, char **ptr)
else
{
Con_Printf("Shader_EvaluateCondition: '%s' is not a cvar\n", token);
return conditiontrue;
lhs = 0;
}
}
if (*token)
token = COM_ParseExt(ptr, false, false);
if (*token)
{
float rhs;
char cmp[4];
memcpy(cmp, token, 4);
token = COM_ParseExt(ptr, false, false);
rhs = atof(token);
if (!strcmp(cmp, "!="))
conditiontrue = lhs != rhs;
else if (!strcmp(cmp, "=="))
conditiontrue = lhs == rhs;
else if (!strcmp(cmp, "<"))
conditiontrue = lhs < rhs;
else if (!strcmp(cmp, "<="))
conditiontrue = lhs <= rhs;
else if (!strcmp(cmp, ">"))
conditiontrue = lhs > rhs;
else if (!strcmp(cmp, ">="))
conditiontrue = lhs >= rhs;
else
conditiontrue = false;
}
}
if (*token)
token = COM_ParseExt(ptr, false, false);
if (*token)
{
float rhs;
char cmp[4];
memcpy(cmp, token, 4);
token = COM_ParseExt(ptr, false, false);
rhs = atof(token);
if (!strcmp(cmp, "!="))
conditiontrue = lhs != rhs;
else if (!strcmp(cmp, "=="))
conditiontrue = lhs == rhs;
else if (!strcmp(cmp, "<"))
conditiontrue = lhs < rhs;
else if (!strcmp(cmp, "<="))
conditiontrue = lhs <= rhs;
else if (!strcmp(cmp, ">"))
conditiontrue = lhs > rhs;
else if (!strcmp(cmp, ">="))
conditiontrue = lhs >= rhs;
else
{
conditiontrue = conditiontrue == !!lhs;
}
conditiontrue = false;
}
else
{
conditiontrue = conditiontrue == !!lhs;
}
if (*token)
token = COM_ParseExt(ptr, false, false);

View File

@ -44,6 +44,10 @@ static void SHM_Shutdown(void);
#define PROJECTION_DISTANCE (float)(dl->radius*2)//0x7fffffff
#ifdef BEF_PUSHDEPTH
extern qboolean r_pushdepth;
#endif
#ifdef GLQUAKE
static texid_t shadowmap[2];
static int shadow_fbo_id;
@ -2806,7 +2810,11 @@ static void Sh_DrawBrushModelShadow(dlight_t *dl, entity_t *e)
GL_SelectEBO(0);
qglEnableClientState(GL_VERTEX_ARRAY);
GLBE_PolyOffsetStencilShadow(true);
#ifdef BEF_PUSHDEPTH
GLBE_PolyOffsetStencilShadow(r_pushdepth);
#else
GLBE_PolyOffsetStencilShadow();
#endif
model = e->model;
surf = model->surfaces+model->firstmodelsurface;

View File

@ -2850,6 +2850,26 @@ qboolean GL_Init(rendererstate_t *info, void *(*getglfunction) (char *name))
sh_config.texfmt[PTI_S3RGBA5] = GL_CheckExtension("GL_ANGLE_texture_compression_dxt5");
}
#ifdef FTE_TARGET_WEB
if (GL_CheckExtension("WEBGL_compressed_texture_etc"))
#else
if ((gl_config.gles && gl_config.glversion >= 3.0) || (!gl_config.gles && (gl_config.glversion >= 4.3 || GL_CheckExtension("GL_ARB_ES3_compatibility"))))
#endif
{ //gles3 and gl4.3 have mandatory support for etc2. probably desktop drivers will pre-decompress, but whatever.
//webgl tries to cater to d3d, so doesn't support this gles3 feature, because browser writers are lazy.
sh_config.texfmt[PTI_ETC1_RGB8] = true;
sh_config.texfmt[PTI_ETC2_RGB8] = true;
sh_config.texfmt[PTI_ETC2_RGB8A1] = true;
sh_config.texfmt[PTI_ETC2_RGB8A8] = true;
}
else
{
sh_config.texfmt[PTI_ETC2_RGB8] = GL_CheckExtension("GL_OES_compressed_ETC2_RGB8_texture");
sh_config.texfmt[PTI_ETC2_RGB8A1] = GL_CheckExtension("GL_OES_compressed_ETC2_punchthroughA_RGBA8_texture");
sh_config.texfmt[PTI_ETC2_RGB8] = GL_CheckExtension("GL_OES_compressed_ETC2_RGBA8_texture");
sh_config.texfmt[PTI_ETC1_RGB8] = sh_config.texfmt[PTI_ETC2_RGB8] || GL_CheckExtension("GL_OES_compressed_ETC1_RGB8_texture");
}
if (gl_config.gles)
{
qboolean srgb = false;//TEST ME GL_CheckExtension("GL_EXT_sRGB");

View File

@ -20,13 +20,14 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "quakedef.h"
#include "glquake.h"
extern int sys_glesversion;
extern float sys_dpi_x;
extern float sys_dpi_y;
extern cvar_t vid_vsync;
static dllhandle_t *sys_gl_module = NULL;
static rendererinfo_t gles1rendererinfo;
void *GLES_GetSymbol(char *symname)
static void *GLES_GetSymbol(char *symname)
{
void *ret;
@ -37,93 +38,72 @@ void *GLES_GetSymbol(char *symname)
return ret;
}
#if 1
void GLVID_SwapBuffers(void)
{
}
void GLVID_DeInit(void)
{
if (sys_gl_module)
Sys_CloseLibrary(sys_gl_module);
sys_gl_module = NULL;
}
qboolean GLVID_Init (rendererstate_t *info, unsigned char *palette)
{
/*
Android 2.2 does not provide EGL headers, thus the context was already created within java code.
This means that we cannot change width/height/bpp/etc.
But we still initialize everything as if we did.
Pros: Works on android 2.2, which reportedly is 50% of all androids right now.
Cons: vid_restart cannot change width/height/bpp/rate/etc.
Mneh: you probably couldn't change width/height/rate anyway.
Cons: any gl objects which were not destroyed properly will leak.
Mneh: we should have cleaned them up properly in the first place.
Cons: GL_EndRendering call will not swap buffers. Buffers will be swapped on return to java.
*/
if (!sys_glesversion)
{
Sys_Printf("GLES version not specified yet\n");
return false;
}
if (sys_glesversion >= 2)
Sys_Printf("Loading GLES2 driver\n");
else
Sys_Printf("Loading GLES1 driver\n");
sys_gl_module = Sys_LoadLibrary((sys_glesversion>=2)?"libGLESv2.so":"libGLESv1_CM.so", NULL);
if (!sys_gl_module)
{
GLVID_DeInit();
return false;
}
vid.dpi_x = sys_dpi_x;
vid.dpi_y = sys_dpi_y;
vid.activeapp = true;
return GL_Init(info, GLES_GetSymbol);
}
#else
#include <EGL/egl.h>
#include <android/native_window.h>
#include <jni.h>
#include <android/native_window_jni.h>
extern void *sys_window;
static EGLDisplay sys_display = EGL_NO_DISPLAY;
static EGLSurface sys_surface = EGL_NO_SURFACE;
static EGLContext sys_context = EGL_NO_CONTEXT;
/*android is a real fucking pain*/
static EGLDisplay sys_display;
static EGLSurface sys_surface;
static EGLContext sys_context;
static jobject sys_jsurface;
extern JNIEnv *sys_jenv;
extern qboolean r_forceheadless;
JNIEXPORT void JNICALL Java_com_fteqw_FTEDroidEngine_setwindow(JNIEnv *env, jobject obj,
jobject surface)
{
if (sys_jsurface)
(*env)->DeleteGlobalRef(sys_jenv, sys_jsurface);
sys_jenv = env;
sys_jsurface = surface?(*env)->NewGlobalRef(sys_jenv, surface):NULL;
r_forceheadless = (sys_jsurface == NULL);
if (qrenderer) //if the window changed then we need to restart everything to match it, BEFORE we return from this function... :(
R_RestartRenderer_f();
}
void GLVID_DeInit(void)
{
if (sys_display != EGL_NO_DISPLAY)
if (sys_display)
{
eglMakeCurrent(sys_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
if (sys_context != EGL_NO_CONTEXT)
if (sys_context)
eglDestroyContext(sys_display, sys_context);
if (sys_surface != EGL_NO_SURFACE)
eglDestroySurface(sys_display, sys_surface);
eglTerminate(sys_display);
sys_context = EGL_NO_CONTEXT;
sys_surface = EGL_NO_SURFACE;
sys_display = EGL_NO_DISPLAY;
}
if (sys_gl_module != NULL)
{
Sys_CloseLibrary(sys_gl_module);
sys_gl_module = NULL;
}
sys_display = EGL_NO_DISPLAY;
sys_context = EGL_NO_CONTEXT;
sys_surface = EGL_NO_SURFACE;
Sys_Printf("GLVID_DeInited\n");
}
qboolean GLVID_Init (rendererstate_t *info, unsigned char *palette)
{
vid.pixelwidth = info->width;
vid.pixelheight = info->height;
Sys_Printf("GLVID_Initing...\n");
if (!sys_jsurface)
{
Sys_Printf("GLVID_Init failed: no window known yet\n");
return false; //not at this time...
}
// vid.pixelwidth = ANativeWindow_getWidth(sys_window);
// vid.pixelheight = ANativeWindow_getHeight(sys_window);
vid.numpages = 3;
const EGLint attribs[] = {
EGL_RENDERABLE_TYPE, (sys_glesversion>=2)?EGL_OPENGL_ES2_BIT:EGL_OPENGL_ES_BIT,
EGLint attribs[] = {
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT,
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_BLUE_SIZE, (info->bpp==16)?5:8,
EGL_GREEN_SIZE, (info->bpp==16)?6:8,
@ -132,17 +112,10 @@ qboolean GLVID_Init (rendererstate_t *info, unsigned char *palette)
// EGL_STENCIL_SIZE, 8,
EGL_NONE
};
EGLint ctxattribs[] = {EGL_CONTEXT_CLIENT_VERSION, sys_glesversion, EGL_NONE};
EGLint w, h, dummy, format;
EGLint w, h, format;
EGLint numConfigs;
EGLConfig config;
sys_gl_module = Sys_LoadLibrary((sys_glesversion>=2)?"libGLESv2.so":"libGLESv1_CM.so", NULL);
if (!sys_gl_module)
{
GLVID_DeInit();
return false;
}
int glesversion;
sys_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (sys_display == EGL_NO_DISPLAY)
@ -155,14 +128,60 @@ qboolean GLVID_Init (rendererstate_t *info, unsigned char *palette)
GLVID_DeInit();
return false;
}
eglChooseConfig(sys_display, attribs, &config, 1, &numConfigs);
#ifdef EGL_VERSION_1_5
attribs[1] = EGL_OPENGL_ES3_BIT;
if (info->renderer==&gles1rendererinfo || !eglChooseConfig(sys_display, attribs, &config, 1, &numConfigs))
#endif
{
attribs[1] = EGL_OPENGL_ES2_BIT;
if (info->renderer==&gles1rendererinfo || !eglChooseConfig(sys_display, attribs, &config, 1, &numConfigs))
{
//gles2 was added in egl1.3
attribs[1] = EGL_OPENGL_ES_BIT;
if (!eglChooseConfig(sys_display, attribs, &config, 1, &numConfigs))
{
//EGL_RENDERABLE_TYPE added in egl1.2
if (!eglChooseConfig(sys_display, attribs+2, &config, 1, &numConfigs))
{
GLVID_DeInit();
return false;
}
}
}
}
eglGetConfigAttrib(sys_display, config, EGL_RENDERABLE_TYPE, &format);
if (info->renderer==&gles1rendererinfo && (format & EGL_OPENGL_ES_BIT))
glesversion = 1;
#ifdef EGL_VERSION_1_5
else if (format & EGL_OPENGL_ES3_BIT)
glesversion = 3;
#endif
else if (format & EGL_OPENGL_ES2_BIT)
glesversion = 2;
else
glesversion = 1;
Sys_Printf("Creating gles %i context\n", glesversion);
sys_gl_module = Sys_LoadLibrary((glesversion>=2)?"libGLESv2.so":"libGLESv1_CM.so", NULL);
if (!sys_gl_module)
{
GLVID_DeInit();
return false;
}
eglGetConfigAttrib(sys_display, config, EGL_NATIVE_VISUAL_ID, &format);
ANativeWindow *anwindow = ANativeWindow_fromSurface(sys_jenv, sys_jsurface);
ANativeWindow_setBuffersGeometry(anwindow, 0, 0, format);
ANativeWindow_setBuffersGeometry(sys_window, 0, 0, format);
sys_surface = eglCreateWindowSurface(sys_display, config, sys_window, NULL);
sys_context = eglCreateContext(sys_display, config, NULL, ctxattribs);
sys_surface = eglCreateWindowSurface(sys_display, config, anwindow, NULL);
ANativeWindow_release(anwindow);
if (!sys_surface)
return false;
EGLint ctxattribs[] = {EGL_CONTEXT_CLIENT_VERSION, glesversion, EGL_NONE};
sys_context = eglCreateContext(sys_display, config, NULL, glesversion>1?ctxattribs:NULL);
if (eglMakeCurrent(sys_display, sys_surface, sys_surface, sys_context) == EGL_FALSE)
@ -173,14 +192,39 @@ qboolean GLVID_Init (rendererstate_t *info, unsigned char *palette)
vid.pixelwidth = w;
vid.pixelheight = h;
return GL_Init(info, GLES_GetSymbol);
if (!GL_Init(info, GLES_GetSymbol))
return false;
Sys_Printf("GLVID_Inited...\n");
vid_vsync.modified = true;
return true;
}
void GLVID_SwapBuffers(void)
{
if (vid_vsync.modified)
{
int interval;
vid_vsync.modified = false;
if (*vid_vsync.string)
interval = vid_vsync.ival;
else
interval = 1; //default is to always vsync, according to EGL docs, so lets just do that.
eglSwapInterval(sys_display, interval);
}
eglSwapBuffers(sys_display, sys_surface);
EGLint w, h;
eglQuerySurface(sys_display, sys_surface, EGL_WIDTH, &w);
eglQuerySurface(sys_display, sys_surface, EGL_HEIGHT, &h);
if (w != vid.pixelwidth || h != vid.pixelheight)
{
vid.pixelwidth = w;
vid.pixelheight = h;
extern cvar_t vid_conautoscale;
Cvar_ForceCallback(&vid_conautoscale);
}
}
#endif
qboolean GLVID_ApplyGammaRamps (unsigned int gammarampsize, unsigned short *ramps)
{
@ -189,5 +233,134 @@ qboolean GLVID_ApplyGammaRamps (unsigned int gammarampsize, unsigned short *ramp
void GLVID_SetCaption(const const char *caption)
{
// :(
}
void VID_Register(void)
{
//many android devices have drivers for both gles1 AND gles2.
//we default to gles2 because its more capable, but some people might want to try using gles1. so register a renderer for that.
//the init code explicitly checks for our gles1rendererinfo and tries to create a gles1 context instead.
extern rendererinfo_t openglrendererinfo;
gles1rendererinfo = openglrendererinfo;
gles1rendererinfo.description = "OpenGL ES 1";
memset(&gles1rendererinfo.name, 0, sizeof(gles1rendererinfo.name)); //make sure there's no 'gl' etc names.
gles1rendererinfo.name[0] = "gles1";
R_RegisterRenderer(&gles1rendererinfo);
}
#ifdef VKQUAKE
#include "../vk/vkrenderer.h"
static qboolean VKVID_CreateSurface(void)
{
VkResult err;
VkAndroidSurfaceCreateInfoKHR createInfo = {VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR};
createInfo.flags = 0;
createInfo.window = ANativeWindow_fromSurface(sys_jenv, sys_jsurface);
err = vkCreateAndroidSurfaceKHR(vk.instance, &createInfo, NULL, &vk.surface);
ANativeWindow_release(createInfo.window);
switch(err)
{
default:
Con_Printf("Unknown vulkan device creation error: %x\n", err);
return false;
case VK_SUCCESS:
break;
}
return true;
}
static qboolean VKVID_Init (rendererstate_t *info, unsigned char *palette)
{
//this is simpler than most platforms, as the window itself is handled by java code, and we can't create/destroy it here
//(android surfaces can be resized/resampled separately from their window, and are always 'fullscreen' anyway, so this isn't actually an issue for once)
const char *extnames[] = {VK_KHR_ANDROID_SURFACE_EXTENSION_NAME, NULL};
if (!sys_jsurface)
{
Sys_Printf("VKVID_Init failed: no window known yet\n");
return false;
}
#ifdef VK_NO_PROTOTYPES
dllhandle_t *hInstVulkan = NULL;
if (!hInstVulkan)
hInstVulkan = *info->subrenderer?Sys_LoadLibrary(info->subrenderer, NULL):NULL;
if (!hInstVulkan)
hInstVulkan = Sys_LoadLibrary("libvulkan.so", NULL);
if (!hInstVulkan)
{
Con_Printf("Unable to load libvulkan.so\nNo Vulkan drivers are installed\n");
return false;
}
vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr) Sys_GetAddressForName(hInstVulkan, "vkGetInstanceProcAddr");
#endif
return VK_Init(info, extnames, VKVID_CreateSurface, NULL);
}
void VKVID_DeInit(void)
{
VK_Shutdown();
//that's all folks.
}
static void VKVID_SwapBuffers(void)
{
// :(
}
rendererinfo_t vkrendererinfo =
{
"Vulkan",
{
"vk",
"Vulkan"
},
QR_VULKAN,
VK_Draw_Init,
VK_Draw_Shutdown,
VK_UpdateFiltering,
VK_LoadTextureMips,
VK_DestroyTexture,
VK_R_Init,
VK_R_DeInit,
VK_R_RenderView,
VKVID_Init,
VKVID_DeInit,
VKVID_SwapBuffers,
GLVID_ApplyGammaRamps,
NULL,//_CreateCursor,
NULL,//_SetCursor,
NULL,//_DestroyCursor,
GLVID_SetCaption,
VKVID_GetRGBInfo,
VK_SCR_UpdateScreen,
VKBE_SelectMode,
VKBE_DrawMesh_List,
VKBE_DrawMesh_Single,
VKBE_SubmitBatch,
VKBE_GetTempBatch,
VKBE_DrawWorld,
VKBE_Init,
VKBE_GenBrushModelVBO,
VKBE_ClearVBO,
VKBE_UploadAllLightmaps,
VKBE_SelectEntity,
VKBE_SelectDLight,
VKBE_Scissor,
VKBE_LightCullModel,
VKBE_VBO_Begin,
VKBE_VBO_Data,
VKBE_VBO_Finish,
VKBE_VBO_Destroy,
VKBE_RenderToTextureUpdate2d,
"no more"
};
#endif

View File

@ -73,13 +73,17 @@ void *EGL_Proc(char *f)
}
*/
if (qeglGetProcAddress)
proc = qeglGetProcAddress(f);
if (!proc)
proc = Sys_GetAddressForName(eslibrary, f);
if (!proc)
proc = Sys_GetAddressForName(egllibrary, f);
//eglGetProcAddress functions must work regardless of context...
//FIXME: this means lots of false-positives.
//as well as lots of thunks, which will result in slowdowns.
if (qeglGetProcAddress)
proc = qeglGetProcAddress(f);
return proc;
}
@ -96,7 +100,12 @@ void EGL_UnloadLibrary(void)
qboolean EGL_LoadLibrary(char *driver)
{
/* apps seem to load glesv2 first for dependency issues */
/* linux seem to load glesv2 first for dependency issues.
(most things are expected to statically link to their libs)
strictly speaking, EGL says that functions should work regardless of context.
(which of course makes portability a nightmare, especially on windows where static linking is basically impossible)
(android's EGL bugs out if you use eglGetProcAddress for core functions too)
*/
Sys_Printf("Attempting to dlopen libGLESv2... ");
eslibrary = Sys_LoadLibrary("libGLESv2", NULL);
if (!eslibrary)

View File

@ -175,11 +175,11 @@ qboolean R_DrawSkyChain (batch_t *batch)
else
GL_DrawSkySphere(batch, skyshader);
}
else if (batch->meshes)
/*else if (batch->meshes)
{ //if you had wanted it invisible, you should have used nodraw.
R_DrawFastSky(batch);
return true; //depth will always be drawn with this pathway.
}
}*/
//neither skydomes nor skyboxes nor skygrids will have been drawn with the correct depth values for the sky.
//this can result in rooms behind the sky surfaces being visible.

View File

@ -615,6 +615,14 @@ typedef void (APIENTRYP PFNGLGETSHADERSOURCEARBPROC) (GLhandleARB obj, GLsizei
#define GL_FRAMEBUFFER_SRGB_CAPABLE_EXT 0x8DBA
#endif
#ifndef GL_COMPRESSED_RGB_S3TC_DXT1_EXT
#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0
#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1
#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2
#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3
#endif
#ifndef GL_EXT_texture_sRGB
#define GL_EXT_texture_sRGB 1
#define GL_SRGB_EXT 0x8C40
@ -635,6 +643,22 @@ typedef void (APIENTRYP PFNGLGETSHADERSOURCEARBPROC) (GLhandleARB obj, GLsizei
#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT 0x8C4F
#endif /* GL_EXT_texture_sRGB */
#ifndef GL_ETC1_RGB8_OES
#define GL_ETC1_RGB8_OES 0x8D64 //4*4 blocks of 8 bytes
#endif
#ifndef GL_COMPRESSED_RGB8_ETC2
//#define GL_COMPRESSED_R11_EAC 0x9270
//#define GL_COMPRESSED_SIGNED_R11_EAC 0x9271
//#define GL_COMPRESSED_RG11_EAC 0x9272
//#define GL_COMPRESSED_SIGNED_RG11_EAC 0x9273
#define GL_COMPRESSED_RGB8_ETC2 0x9274 //4*4 blocks of 8 bytes. also accepts valid etc1 images
#define GL_COMPRESSED_SRGB8_ETC2 0x9275
#define GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276 //
#define GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9277
#define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 //4*4 blocks of 8+8 bytes.
#define GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279
#endif
#ifndef GL_ARB_pixel_buffer_object
#define GL_PIXEL_PACK_BUFFER_ARB 0x88EB
#define GL_PIXEL_UNPACK_BUFFER_ARB 0x88EC

View File

@ -683,13 +683,15 @@ void Shader_ReleaseGeneric(program_t *prog);
mfog_t *Mod_FogForOrigin(model_t *wmodel, vec3_t org);
#ifndef NOLEGACY
#define BEF_FORCEDEPTHWRITE 1
#define BEF_FORCEDEPTHTEST 2
#define BEF_FORCEADDITIVE 4 //blend dest = GL_ONE
#define BEF_FORCETRANSPARENT 8 //texenv replace -> modulate
#define BEF_FORCENODEPTH 16 //disables any and all depth.
//FIXME: the above should really be legacy-only
#define BEF_PUSHDEPTH 32 //additional polygon offset
#endif
//FIXME: the above should really be legacy-only
#define BEF_NODLIGHT 64 //don't use a dlight pass
#define BEF_NOSHADOWS 128 //don't appear in shadows
#define BEF_FORCECOLOURMOD 256 //q3 shaders default to 'rgbgen identity', and ignore ent colours. this forces ent colours to be considered
@ -890,7 +892,11 @@ void BE_GenerateProgram(shader_t *shader);
void Sh_RegisterCvars(void);
#ifdef RTLIGHTS
//
#ifdef BEF_PUSHDEPTH
void GLBE_PolyOffsetStencilShadow(qboolean foobar);
#else
void GLBE_PolyOffsetStencilShadow(void);
#endif
//sets up gl for depth-only FIXME
int GLBE_SetupForShadowMap(texid_t shadowmaptex, int texwidth, int texheight, float shadowscale);
//Called from shadowmapping code into backend

View File

@ -1498,7 +1498,7 @@ void SV_ProgStartFrame (void)
pr_global_struct->time = sv.world.physicstime;
#ifdef VM_Q1
if (svs.gametype == GT_Q1QVM)
Q1QVM_StartFrame();
Q1QVM_StartFrame(false);
else
#endif
{
@ -2480,6 +2480,17 @@ qboolean SV_Physics (void)
int newbottime, ms;
client_t *oldhost;
edict_t *oldplayer;
#ifdef VM_Q1
if (svs.gametype == GT_Q1QVM)
{
pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv.world.edicts);
pr_global_struct->other = EDICT_TO_PROG(svprogfuncs, sv.world.edicts);
pr_global_struct->time = sv.world.physicstime;
Q1QVM_StartFrame(true);
}
#endif
host_frametime = (Sys_Milliseconds() - old_bot_time) / 1000.0f;
if (1 || host_frametime >= 1 / 72.0f)
{
@ -2499,16 +2510,24 @@ qboolean SV_Physics (void)
SV_PreRunCmd();
ucmd.msec = ms;
ucmd.angles[0] = (short)(sv_player->v->v_angle[0] * (65535/360.0f));
ucmd.angles[1] = (short)(sv_player->v->v_angle[1] * (65535/360.0f));
ucmd.angles[2] = (short)(sv_player->v->v_angle[2] * (65535/360.0f));
ucmd.forwardmove = sv_player->xv->movement[0];
ucmd.sidemove = sv_player->xv->movement[1];
ucmd.upmove = sv_player->xv->movement[2];
ucmd.buttons = (sv_player->v->button0?1:0) | (sv_player->v->button2?2:0);
if (svs.gametype == GT_Q1QVM)
{
ucmd = svs.clients[i-1].lastcmd;
ucmd.msec = ms;
}
else
{
ucmd.msec = ms;
ucmd.angles[0] = (short)(sv_player->v->v_angle[0] * (65535/360.0f));
ucmd.angles[1] = (short)(sv_player->v->v_angle[1] * (65535/360.0f));
ucmd.angles[2] = (short)(sv_player->v->v_angle[2] * (65535/360.0f));
ucmd.forwardmove = sv_player->xv->movement[0];
ucmd.sidemove = sv_player->xv->movement[1];
ucmd.upmove = sv_player->xv->movement[2];
ucmd.buttons = (sv_player->v->button0?1:0) | (sv_player->v->button2?2:0);
svs.clients[i-1].lastcmd = ucmd; //allow the other clients to predict this bot.
svs.clients[i-1].lastcmd = ucmd; //allow the other clients to predict this bot.
}
SV_RunCmd(&ucmd, false);
SV_PostRunCmd();

View File

@ -4183,6 +4183,7 @@ void VKBE_SetupLightCBuffer(dlight_t *l, vec3_t colour)
cbl->l_lightradius = l->radius;
#ifdef RTLIGHTS
if (shaderstate.curlmode & LSHADER_SPOT)
{
float view[16];
@ -4193,6 +4194,7 @@ void VKBE_SetupLightCBuffer(dlight_t *l, vec3_t colour)
Matrix4_Multiply(proj, view, cbl->l_cubematrix);
}
else
#endif
Matrix4x4_CM_LightMatrixFromAxis(cbl->l_cubematrix, l->axis[0], l->axis[1], l->axis[2], l->origin);
VectorCopy(l->origin, cbl->l_lightposition);
cbl->padl1 = 0;

View File

@ -58,7 +58,9 @@ const char *vklayerlist[] =
#endif
void VK_Submit_Work(VkCommandBuffer cmdbuf, VkSemaphore semwait, VkPipelineStageFlags semwaitstagemask, VkSemaphore semsignal, VkFence fencesignal, struct vkframe *presentframe, struct vk_fencework *fencedwork);
#ifdef MULTITHREAD
static int VK_Submit_Thread(void *arg);
#endif
static void VK_Submit_DoWork(void);
static void VK_DestroyRenderPass(void);
@ -884,6 +886,14 @@ vk_image_t VK_CreateTexture2DArray(uint32_t width, uint32_t height, uint32_t lay
format = VK_FORMAT_BC2_UNORM_BLOCK;
else if (encoding == PTI_S3RGBA5)
format = VK_FORMAT_BC3_UNORM_BLOCK;
else if (encoding == PTI_ETC1_RGB8) //vulkan doesn't support etc1, but etc2 is a superset so its all okay.
format = VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK;
else if (encoding == PTI_ETC2_RGB8)
format = VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK;
else if (encoding == PTI_ETC2_RGB8A1)
format = VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK;
else if (encoding == PTI_ETC2_RGB8A8)
format = VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK;
//depth formats
else if (encoding == PTI_DEPTH16)
format = VK_FORMAT_D16_UNORM;
@ -3334,6 +3344,11 @@ void VK_CheckTextureFormats(void)
{PTI_S3RGBA1, VK_FORMAT_BC1_RGBA_UNORM_BLOCK, VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},
{PTI_S3RGBA3, VK_FORMAT_BC2_UNORM_BLOCK, VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},
{PTI_S3RGBA5, VK_FORMAT_BC3_UNORM_BLOCK, VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},
{PTI_ETC1_RGB8, VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK, VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT}, //vulkan doesn't support etc1.
{PTI_ETC2_RGB8, VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK, VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},
{PTI_ETC2_RGB8A1, VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK,VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},
{PTI_ETC2_RGB8A8, VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK,VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},
};
unsigned int i;
VkPhysicalDeviceProperties props;

View File

@ -6,7 +6,10 @@
#define VKInstWin32Funcs VKFunc(CreateWin32SurfaceKHR)
#endif
#ifdef __linux__
#ifdef ANDROID
#define VK_USE_PLATFORM_ANDROID_KHR
#define VKInstXLibFuncs VKFunc(CreateAndroidSurfaceKHR)
#elif defined(__linux__)
#define VK_USE_PLATFORM_XLIB_KHR
#define VKInstXLibFuncs VKFunc(CreateXlibSurfaceKHR)

View File

@ -1,216 +1,216 @@
#include "../plugin.h"
#include "../engine.h"
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
static cvar_t *ffmpeg_audiodecoder;
struct avaudioctx
{
//raw file
uint8_t *filedata;
size_t fileofs;
size_t filesize;
//avformat stuff
AVFormatContext *pFormatCtx;
int audioStream;
AVCodecContext *pACodecCtx;
AVFrame *pAFrame;
//decoding
int64_t lasttime;
//output audio
//we throw away data if the format changes. which is awkward, but gah.
int64_t samples_start;
int samples_channels;
int samples_speed;
int samples_width;
qbyte *samples_buffer;
size_t samples_count;
size_t samples_max;
};
static void S_AV_Purge(sfx_t *s)
{
struct avaudioctx *ctx = (struct avaudioctx*)s->decoder.buf;
s->loadstate = SLS_NOTLOADED;
// Free the audio decoder
if (ctx->pACodecCtx)
avcodec_close(ctx->pACodecCtx);
av_free(ctx->pAFrame);
// Close the video file
avformat_close_input(&ctx->pFormatCtx);
//free the decoded buffer
free(ctx->samples_buffer);
//file storage will be cleared here too
free(ctx);
memset(&s->decoder, 0, sizeof(s->decoder));
}
static sfxcache_t *S_AV_Locate(sfx_t *sfx, sfxcache_t *buf, ssamplepos_t start, int length)
{ //warning: can be called on a different thread.
struct avaudioctx *ctx = (struct avaudioctx*)sfx->decoder.buf;
AVPacket packet;
int64_t curtime;
if (!buf)
return NULL;
curtime = start + length;
// curtime = (mediatime * ctx->denum) / ctx->num;
while (1)
{
if (ctx->lasttime > curtime)
break;
// We're ahead of the previous frame. try and read the next.
if (av_read_frame(ctx->pFormatCtx, &packet) < 0)
break;
// Is this a packet from the video stream?
if(packet.stream_index==ctx->audioStream)
{
int okay;
int len;
void *odata = packet.data;
while (packet.size > 0)
{
okay = false;
len = avcodec_decode_audio4(ctx->pACodecCtx, ctx->pAFrame, &okay, &packet);
if (len < 0)
break;
packet.size -= len;
packet.data += len;
if (okay)
{
int width = 2;
int channels = ctx->pACodecCtx->channels;
unsigned int auddatasize = av_samples_get_buffer_size(NULL, ctx->pACodecCtx->channels, ctx->pAFrame->nb_samples, ctx->pACodecCtx->sample_fmt, 1);
void *auddata = ctx->pAFrame->data[0];
switch(ctx->pACodecCtx->sample_fmt)
{ //we don't support planar audio. we just treat it as mono instead.
default:
auddatasize = 0;
break;
case AV_SAMPLE_FMT_U8P:
auddatasize /= channels;
channels = 1;
case AV_SAMPLE_FMT_U8:
width = 1;
break;
case AV_SAMPLE_FMT_S16P:
auddatasize /= channels;
channels = 1;
case AV_SAMPLE_FMT_S16:
width = 2;
break;
case AV_SAMPLE_FMT_FLTP:
auddatasize /= channels;
channels = 1;
case AV_SAMPLE_FMT_FLT:
//FIXME: support float audio internally.
{
float *in = (void*)auddata;
signed short *out = (void*)auddata;
int v;
unsigned int i;
for (i = 0; i < auddatasize/sizeof(*in); i++)
{
v = (short)(in[i]*32767);
if (v < -32767)
v = -32767;
else if (v > 32767)
v = 32767;
out[i] = v;
}
auddatasize/=2;
width = 2;
}
case AV_SAMPLE_FMT_DBLP:
auddatasize /= channels;
channels = 1;
case AV_SAMPLE_FMT_DBL:
{
double *in = (double*)auddata;
signed short *out = (void*)auddata;
int v;
unsigned int i;
for (i = 0; i < auddatasize/sizeof(*in); i++)
{
v = (short)(in[i]*32767);
if (v < -32767)
v = -32767;
else if (v > 32767)
v = 32767;
out[i] = v;
}
auddatasize/=4;
width = 2;
}
break;
}
if (ctx->samples_channels != channels || ctx->samples_speed != ctx->pACodecCtx->sample_rate || ctx->samples_width != width)
{ //something changed, update
ctx->samples_channels = channels;
ctx->samples_speed = ctx->pACodecCtx->sample_rate;
ctx->samples_width = width;
//and discard any decoded audio. this might loose some.
ctx->samples_start += ctx->samples_count;
ctx->samples_count = 0;
}
if (ctx->samples_max < (ctx->samples_count*ctx->samples_width*ctx->samples_channels)+auddatasize)
{
ctx->samples_max = (ctx->samples_count*ctx->samples_width*ctx->samples_channels)+auddatasize;
ctx->samples_max *= 2; //slop
ctx->samples_buffer = realloc(ctx->samples_buffer, ctx->samples_max);
}
if (width == 1)
{ //FTE uses signed 8bit audio. ffmpeg uses unsigned 8bit audio. *sigh*.
char *out = (char*)(ctx->samples_buffer + ctx->samples_count*(ctx->samples_width*ctx->samples_channels));
unsigned char *in = auddata;
int i;
for (i = 0; i < auddatasize; i++)
out[i] = in[i]-128;
}
else
memcpy(ctx->samples_buffer + ctx->samples_count*(ctx->samples_width*ctx->samples_channels), auddata, auddatasize);
ctx->samples_count += auddatasize/(ctx->samples_width*ctx->samples_channels);
}
}
packet.data = odata;
}
// Free the packet that was allocated by av_read_frame
av_packet_unref(&packet);
}
#include "../plugin.h"
#include "../engine.h"
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
static cvar_t *ffmpeg_audiodecoder;
struct avaudioctx
{
//raw file
uint8_t *filedata;
size_t fileofs;
size_t filesize;
//avformat stuff
AVFormatContext *pFormatCtx;
int audioStream;
AVCodecContext *pACodecCtx;
AVFrame *pAFrame;
//decoding
int64_t lasttime;
//output audio
//we throw away data if the format changes. which is awkward, but gah.
int64_t samples_start;
int samples_channels;
int samples_speed;
int samples_width;
qbyte *samples_buffer;
size_t samples_count;
size_t samples_max;
};
static void S_AV_Purge(sfx_t *s)
{
struct avaudioctx *ctx = (struct avaudioctx*)s->decoder.buf;
s->loadstate = SLS_NOTLOADED;
// Free the audio decoder
if (ctx->pACodecCtx)
avcodec_close(ctx->pACodecCtx);
av_free(ctx->pAFrame);
// Close the video file
avformat_close_input(&ctx->pFormatCtx);
//free the decoded buffer
free(ctx->samples_buffer);
//file storage will be cleared here too
free(ctx);
memset(&s->decoder, 0, sizeof(s->decoder));
}
static sfxcache_t *S_AV_Locate(sfx_t *sfx, sfxcache_t *buf, ssamplepos_t start, int length)
{ //warning: can be called on a different thread.
struct avaudioctx *ctx = (struct avaudioctx*)sfx->decoder.buf;
AVPacket packet;
int64_t curtime;
if (!buf)
return NULL;
curtime = start + length;
// curtime = (mediatime * ctx->denum) / ctx->num;
while (1)
{
if (ctx->lasttime > curtime)
break;
// We're ahead of the previous frame. try and read the next.
if (av_read_frame(ctx->pFormatCtx, &packet) < 0)
break;
// Is this a packet from the video stream?
if(packet.stream_index==ctx->audioStream)
{
int okay;
int len;
void *odata = packet.data;
while (packet.size > 0)
{
okay = false;
len = avcodec_decode_audio4(ctx->pACodecCtx, ctx->pAFrame, &okay, &packet);
if (len < 0)
break;
packet.size -= len;
packet.data += len;
if (okay)
{
int width = 2;
int channels = ctx->pACodecCtx->channels;
unsigned int auddatasize = av_samples_get_buffer_size(NULL, ctx->pACodecCtx->channels, ctx->pAFrame->nb_samples, ctx->pACodecCtx->sample_fmt, 1);
void *auddata = ctx->pAFrame->data[0];
switch(ctx->pACodecCtx->sample_fmt)
{ //we don't support planar audio. we just treat it as mono instead.
default:
auddatasize = 0;
break;
case AV_SAMPLE_FMT_U8P:
auddatasize /= channels;
channels = 1;
case AV_SAMPLE_FMT_U8:
width = 1;
break;
case AV_SAMPLE_FMT_S16P:
auddatasize /= channels;
channels = 1;
case AV_SAMPLE_FMT_S16:
width = 2;
break;
case AV_SAMPLE_FMT_FLTP:
auddatasize /= channels;
channels = 1;
case AV_SAMPLE_FMT_FLT:
//FIXME: support float audio internally.
{
float *in = (void*)auddata;
signed short *out = (void*)auddata;
int v;
unsigned int i;
for (i = 0; i < auddatasize/sizeof(*in); i++)
{
v = (short)(in[i]*32767);
if (v < -32767)
v = -32767;
else if (v > 32767)
v = 32767;
out[i] = v;
}
auddatasize/=2;
width = 2;
}
case AV_SAMPLE_FMT_DBLP:
auddatasize /= channels;
channels = 1;
case AV_SAMPLE_FMT_DBL:
{
double *in = (double*)auddata;
signed short *out = (void*)auddata;
int v;
unsigned int i;
for (i = 0; i < auddatasize/sizeof(*in); i++)
{
v = (short)(in[i]*32767);
if (v < -32767)
v = -32767;
else if (v > 32767)
v = 32767;
out[i] = v;
}
auddatasize/=4;
width = 2;
}
break;
}
if (ctx->samples_channels != channels || ctx->samples_speed != ctx->pACodecCtx->sample_rate || ctx->samples_width != width)
{ //something changed, update
ctx->samples_channels = channels;
ctx->samples_speed = ctx->pACodecCtx->sample_rate;
ctx->samples_width = width;
//and discard any decoded audio. this might loose some.
ctx->samples_start += ctx->samples_count;
ctx->samples_count = 0;
}
if (ctx->samples_max < (ctx->samples_count*ctx->samples_width*ctx->samples_channels)+auddatasize)
{
ctx->samples_max = (ctx->samples_count*ctx->samples_width*ctx->samples_channels)+auddatasize;
ctx->samples_max *= 2; //slop
ctx->samples_buffer = realloc(ctx->samples_buffer, ctx->samples_max);
}
if (width == 1)
{ //FTE uses signed 8bit audio. ffmpeg uses unsigned 8bit audio. *sigh*.
char *out = (char*)(ctx->samples_buffer + ctx->samples_count*(ctx->samples_width*ctx->samples_channels));
unsigned char *in = auddata;
int i;
for (i = 0; i < auddatasize; i++)
out[i] = in[i]-128;
}
else
memcpy(ctx->samples_buffer + ctx->samples_count*(ctx->samples_width*ctx->samples_channels), auddata, auddatasize);
ctx->samples_count += auddatasize/(ctx->samples_width*ctx->samples_channels);
}
}
packet.data = odata;
}
// Free the packet that was allocated by av_read_frame
av_packet_unref(&packet);
}
buf->length = ctx->samples_count;
buf->speed = ctx->samples_speed;
buf->width = ctx->samples_width;
buf->numchannels = ctx->samples_channels;
buf->soundoffset = ctx->samples_start;
buf->data = ctx->samples_buffer;
//if we couldn't return any new data, then we're at an eof, return NULL to signal that.
if (start == buf->soundoffset + buf->length && length > 0)
return NULL;
return buf;
}
buf->data = ctx->samples_buffer;
//if we couldn't return any new data, then we're at an eof, return NULL to signal that.
if (start == buf->soundoffset + buf->length && length > 0)
return NULL;
return buf;
}
static float S_AV_Query(struct sfx_s *sfx, struct sfxcache_s *buf, char *title, size_t titlesize)
{
struct avaudioctx *ctx = (struct avaudioctx*)sfx->decoder.buf;
@ -226,48 +226,48 @@ static float S_AV_Query(struct sfx_s *sfx, struct sfxcache_s *buf, char *title,
buf->width = ctx->samples_width;
}
return ctx->pFormatCtx->duration / (float)AV_TIME_BASE;
}
static int AVIO_Mem_Read(void *opaque, uint8_t *buf, int buf_size)
{
struct avaudioctx *ctx = opaque;
if (ctx->fileofs > ctx->filesize)
buf_size = 0;
if (buf_size > ctx->filesize-ctx->fileofs)
buf_size = ctx->filesize-ctx->fileofs;
if (buf_size > 0)
{
memcpy(buf, ctx->filedata + ctx->fileofs, buf_size);
ctx->fileofs += buf_size;
return buf_size;
}
return 0;
}
static int64_t AVIO_Mem_Seek(void *opaque, int64_t offset, int whence)
{
struct avaudioctx *ctx = opaque;
whence &= ~AVSEEK_FORCE;
switch(whence)
{
default:
return -1;
case SEEK_SET:
ctx->fileofs = offset;
break;
case SEEK_CUR:
ctx->fileofs += offset;
break;
case SEEK_END:
ctx->fileofs = ctx->filesize + offset;
break;
case AVSEEK_SIZE:
return ctx->filesize;
}
if (ctx->fileofs < 0)
ctx->fileofs = 0;
return ctx->fileofs;
}
}
static int AVIO_Mem_Read(void *opaque, uint8_t *buf, int buf_size)
{
struct avaudioctx *ctx = opaque;
if (ctx->fileofs > ctx->filesize)
buf_size = 0;
if (buf_size > ctx->filesize-ctx->fileofs)
buf_size = ctx->filesize-ctx->fileofs;
if (buf_size > 0)
{
memcpy(buf, ctx->filedata + ctx->fileofs, buf_size);
ctx->fileofs += buf_size;
return buf_size;
}
return 0;
}
static int64_t AVIO_Mem_Seek(void *opaque, int64_t offset, int whence)
{
struct avaudioctx *ctx = opaque;
whence &= ~AVSEEK_FORCE;
switch(whence)
{
default:
return -1;
case SEEK_SET:
ctx->fileofs = offset;
break;
case SEEK_CUR:
ctx->fileofs += offset;
break;
case SEEK_END:
ctx->fileofs = ctx->filesize + offset;
break;
case AVSEEK_SIZE:
return ctx->filesize;
}
if (ctx->fileofs < 0)
ctx->fileofs = 0;
return ctx->fileofs;
}
/*const char *COM_GetFileExtension (const char *in)
{
const char *dot;
@ -278,121 +278,121 @@ static int64_t AVIO_Mem_Seek(void *opaque, int64_t offset, int whence)
return "";
in = dot+1;
return in;
}*/
static qboolean QDECL S_LoadAVSound (sfx_t *s, qbyte *data, size_t datalen, int sndspeed)
{
struct avaudioctx *ctx;
int i;
AVCodec *pCodec;
const int iBufSize = 4 * 1024;
if (!ffmpeg_audiodecoder)
ffmpeg_audiodecoder = pCvar_GetNVFDG("ffmpeg_audiodecoder_wip", "0", 0, "Enables the use of ffmpeg's decoder for pure audio files.", "ffmpeg");
if (!ffmpeg_audiodecoder->value /* && *ffmpeg_audiodecoder.string */)
return false;
if (!data || !datalen)
return false;
}*/
static qboolean QDECL S_LoadAVSound (sfx_t *s, qbyte *data, size_t datalen, int sndspeed)
{
struct avaudioctx *ctx;
int i;
AVCodec *pCodec;
const int iBufSize = 4 * 1024;
if (!ffmpeg_audiodecoder)
ffmpeg_audiodecoder = pCvar_GetNVFDG("ffmpeg_audiodecoder_wip", "0", 0, "Enables the use of ffmpeg's decoder for pure audio files.", "ffmpeg");
if (!ffmpeg_audiodecoder->value /* && *ffmpeg_audiodecoder.string */)
return false;
if (!data || !datalen)
return false;
if (datalen >= 4 && !strncmp(data, "RIFF", 4))
return false; //ignore it if it looks like a wav file. that means we don't need to figure out how to calculate loopstart.
// if (strcasecmp(COM_GetFileExtension(s->name), "wav")) //don't do .wav - I've no idea how to read the loopstart tag with ffmpeg.
// return false;
s->decoder.buf = ctx = malloc(sizeof(*ctx) + datalen);
if (!ctx)
return false; //o.O
memset(ctx, 0, sizeof(*ctx));
// Create internal io buffer for FFmpeg
ctx->filedata = data; //defer that copy
ctx->filesize = datalen; //defer that copy
ctx->pFormatCtx = avformat_alloc_context();
ctx->pFormatCtx->pb = avio_alloc_context(av_malloc(iBufSize), iBufSize, 0, ctx, AVIO_Mem_Read, 0, AVIO_Mem_Seek);
// Open file
if(avformat_open_input(&ctx->pFormatCtx, s->name, NULL, NULL)==0)
{
// Retrieve stream information
if(avformat_find_stream_info(ctx->pFormatCtx, NULL)>=0)
{
ctx->audioStream=-1;
for(i=0; i<ctx->pFormatCtx->nb_streams; i++)
if(ctx->pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO)
{
ctx->audioStream=i;
break;
}
if(ctx->audioStream!=-1)
{
ctx->pACodecCtx=ctx->pFormatCtx->streams[ctx->audioStream]->codec;
pCodec=avcodec_find_decoder(ctx->pACodecCtx->codec_id);
ctx->pAFrame=av_frame_alloc();
if(pCodec!=NULL && ctx->pAFrame && avcodec_open2(ctx->pACodecCtx, pCodec, NULL) >= 0)
{ //success
}
else
ctx->audioStream = -1;
}
}
if (ctx->audioStream != -1)
// return false;
s->decoder.buf = ctx = malloc(sizeof(*ctx) + datalen);
if (!ctx)
return false; //o.O
memset(ctx, 0, sizeof(*ctx));
// Create internal io buffer for FFmpeg
ctx->filedata = data; //defer that copy
ctx->filesize = datalen; //defer that copy
ctx->pFormatCtx = avformat_alloc_context();
ctx->pFormatCtx->pb = avio_alloc_context(av_malloc(iBufSize), iBufSize, 0, ctx, AVIO_Mem_Read, 0, AVIO_Mem_Seek);
// Open file
if(avformat_open_input(&ctx->pFormatCtx, s->name, NULL, NULL)==0)
{
// Retrieve stream information
if(avformat_find_stream_info(ctx->pFormatCtx, NULL)>=0)
{
ctx->audioStream=-1;
for(i=0; i<ctx->pFormatCtx->nb_streams; i++)
if(ctx->pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO)
{
ctx->audioStream=i;
break;
}
if(ctx->audioStream!=-1)
{
ctx->pACodecCtx=ctx->pFormatCtx->streams[ctx->audioStream]->codec;
pCodec=avcodec_find_decoder(ctx->pACodecCtx->codec_id);
ctx->pAFrame=av_frame_alloc();
if(pCodec!=NULL && ctx->pAFrame && avcodec_open2(ctx->pACodecCtx, pCodec, NULL) >= 0)
{ //success
}
else
ctx->audioStream = -1;
}
}
if (ctx->audioStream != -1)
{
//sucky copy
ctx->filedata = (uint8_t*)(ctx+1);
ctx->filedata = (uint8_t*)(ctx+1);
memcpy(ctx->filedata, data, datalen);
s->decoder.ended = S_AV_Purge;
s->decoder.purge = S_AV_Purge;
s->decoder.decodedata = S_AV_Locate;
s->decoder.querydata = S_AV_Query;
return true;
}
}
S_AV_Purge(s);
return false;
}
static qboolean AVAudio_Init(void)
{
if (!pPlug_ExportNative("S_LoadSound", S_LoadAVSound))
{
Con_Printf("avplug: Engine doesn't support audio decoder plugins\n");
return false;
}
return true;
}
//generic module stuff. this has to go somewhere.
static void AVLogCallback(void *avcl, int level, const char *fmt, va_list vl)
s->decoder.querydata = S_AV_Query;
return true;
}
}
S_AV_Purge(s);
return false;
}
static qboolean AVAudio_Init(void)
{
if (!pPlug_ExportNative("S_LoadSound", S_LoadAVSound))
{
Con_Printf("avplug: Engine doesn't support audio decoder plugins\n");
return false;
}
return true;
}
//generic module stuff. this has to go somewhere.
static void AVLogCallback(void *avcl, int level, const char *fmt, va_list vl)
{ //needs to be reenterant
#ifdef _DEBUG
char string[1024];
Q_vsnprintf (string, sizeof(string), fmt, vl);
pCon_Print(string);
#endif
}
//get the encoder/decoders to register themselves with the engine, then make sure avformat/avcodec have registered all they have to give.
qboolean AVEnc_Init(void);
qboolean AVDec_Init(void);
qintptr_t Plug_Init(qintptr_t *args)
{
qboolean okay = false;
okay |= AVAudio_Init();
okay |= AVDec_Init();
okay |= AVEnc_Init();
if (okay)
{
av_register_all();
avcodec_register_all();
av_log_set_level(AV_LOG_WARNING);
av_log_set_callback(AVLogCallback);
}
return okay;
}
pCon_Print(string);
#endif
}
//get the encoder/decoders to register themselves with the engine, then make sure avformat/avcodec have registered all they have to give.
qboolean AVEnc_Init(void);
qboolean AVDec_Init(void);
qintptr_t Plug_Init(qintptr_t *args)
{
qboolean okay = false;
okay |= AVAudio_Init();
okay |= AVDec_Init();
okay |= AVEnc_Init();
if (okay)
{
av_register_all();
avcodec_register_all();
av_log_set_level(AV_LOG_WARNING);
av_log_set_callback(AVLogCallback);
}
return okay;
}

View File

@ -75,15 +75,14 @@ void(float apilevel, string enginename, float engineversion) CSQC_Init =
string post_data;
post_data = strcat("player_name=", self.netname, "&password_ip=", self.ip, "&minimum=0&version=fte");
//dprint("In TellBGMWereStillHere: about to send JSON for self.owner: ", self.owner.netname, "\n");
uri_post("http://www.attackersgored.com/biggame.php", 0, "application/x-www-form-urlencoded", post_data);
uri_post("http://localhost/biggame.php", 0, "application/x-www-form-urlencoded", post_data);
self.nextthink = time + 1+random();
};
for (float i = 0; i < 32; i++)
{
entity foo = spawn();
foo.nextthink = time+1;
foo.think = spam;
// foo.think = spam;
foo.netname = sprintf("test%g", i);
foo.ip = sprintf("127.0.0.%g", i+1);
}
@ -122,7 +121,7 @@ class mydesktop : mitem_desktop
}
*/
renderscene();
renderscene();
};
};