diff --git a/engine/Makefile b/engine/Makefile index 1423b51b0..ab1d47874 100644 --- a/engine/Makefile +++ b/engine/Makefile @@ -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 diff --git a/engine/client/image.c b/engine/client/image.c index 27149a267..66b4dc0ad 100644 --- a/engine/client/image.c +++ b/engine/client/image.c @@ -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; diff --git a/engine/client/in_generic.c b/engine/client/in_generic.c index e57db370e..3a202d363 100644 --- a/engine/client/in_generic.c +++ b/engine/client/in_generic.c @@ -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(); +} diff --git a/engine/client/input.h b/engine/client/input.h index 6c6d0895b..b6305d988 100644 --- a/engine/client/input.h +++ b/engine/client/input.h @@ -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); diff --git a/engine/client/m_master.c b/engine/client/m_master.c index 8579d563f..631259518 100644 --- a/engine/client/m_master.c +++ b/engine/client/m_master.c @@ -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; } } diff --git a/engine/client/m_multi.c b/engine/client/m_multi.c index 792ae8779..540b85835 100644 --- a/engine/client/m_multi.c +++ b/engine/client/m_multi.c @@ -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); diff --git a/engine/client/m_options.c b/engine/client/m_options.c index a78b8826a..ab82ccbcf 100644 --- a/engine/client/m_options.c +++ b/engine/client/m_options.c @@ -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), diff --git a/engine/client/m_single.c b/engine/client/m_single.c index 88fd9b5ba..90efd4835 100644 --- a/engine/client/m_single.c +++ b/engine/client/m_single.c @@ -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 diff --git a/engine/client/merged.h b/engine/client/merged.h index 731c5a4b4..166cdcb54 100644 --- a/engine/client/merged.h +++ b/engine/client/merged.h @@ -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 diff --git a/engine/client/net_master.c b/engine/client/net_master.c index 3a380d3d0..060b3fa57 100644 --- a/engine/client/net_master.c +++ b/engine/client/net_master.c @@ -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)) diff --git a/engine/client/r_surf.c b/engine/client/r_surf.c index e58cbbf25..73635fe3a 100644 --- a/engine/client/r_surf.c +++ b/engine/client/r_surf.c @@ -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 (); diff --git a/engine/client/render.h b/engine/client/render.h index dd07c884f..519212765 100644 --- a/engine/client/render.h +++ b/engine/client/render.h @@ -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); diff --git a/engine/client/snd_dma.c b/engine/client/snd_dma.c index edb5a4b9c..40f912414 100644 --- a/engine/client/snd_dma.c +++ b/engine/client/snd_dma.c @@ -250,8 +250,28 @@ void S_SoundInfo_f(void) } #ifdef VOICECHAT +#ifdef SPEEX_STATIC #include #include +#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 { diff --git a/engine/common/bothdefs.h b/engine/common/bothdefs.h index ad036310c..28d50f479 100644 --- a/engine/common/bothdefs.h +++ b/engine/common/bothdefs.h @@ -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. diff --git a/engine/common/cmd.c b/engine/common/cmd.c index bae366f23..80e2c3702 100644 --- a/engine/common/cmd.c +++ b/engine/common/cmd.c @@ -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); diff --git a/engine/d3d/d3d8_backend.c b/engine/d3d/d3d8_backend.c index f5a759f06..76b167693 100644 --- a/engine/d3d/d3d8_backend.c +++ b/engine/d3d/d3d8_backend.c @@ -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; diff --git a/engine/d3d/d3d_backend.c b/engine/d3d/d3d_backend.c index b708cf597..1ff85f329 100644 --- a/engine/d3d/d3d_backend.c +++ b/engine/d3d/d3d_backend.c @@ -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; diff --git a/engine/dotnet2005/droid.vcproj b/engine/dotnet2005/droid.vcproj index bb21b60c6..c19950ddb 100644 --- a/engine/dotnet2005/droid.vcproj +++ b/engine/dotnet2005/droid.vcproj @@ -23,7 +23,7 @@ > - + diff --git a/engine/droid/src/com/fteqw/FTEDroidActivity.java b/engine/droid/src/com/fteqw/FTEDroidActivity.java index 6d88042eb..a1e25e717 100644 --- a/engine/droid/src/com/fteqw/FTEDroidActivity.java +++ b/engine/droid/src/com/fteqw/FTEDroidActivity.java @@ -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() { diff --git a/engine/droid/src/com/fteqw/FTEDroidEngine.java b/engine/droid/src/com/fteqw/FTEDroidEngine.java index f59bc534f..f1a20352d 100644 --- a/engine/droid/src/com/fteqw/FTEDroidEngine.java +++ b/engine/droid/src/com/fteqw/FTEDroidEngine.java @@ -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 { diff --git a/engine/gl/gl_backend.c b/engine/gl/gl_backend.c index abcec94bb..a29ec2318 100644 --- a/engine/gl/gl_backend.c +++ b/engine/gl/gl_backend.c @@ -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); diff --git a/engine/gl/gl_draw.c b/engine/gl/gl_draw.c index 834db4b10..528ae5bbc 100644 --- a/engine/gl/gl_draw.c +++ b/engine/gl/gl_draw.c @@ -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; } } } diff --git a/engine/gl/gl_shader.c b/engine/gl/gl_shader.c index baa9be96f..b0dd8f18d 100644 --- a/engine/gl/gl_shader.c +++ b/engine/gl/gl_shader.c @@ -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); diff --git a/engine/gl/gl_shadow.c b/engine/gl/gl_shadow.c index 6f886cfcf..2cb7b298e 100644 --- a/engine/gl/gl_shadow.c +++ b/engine/gl/gl_shadow.c @@ -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; diff --git a/engine/gl/gl_vidcommon.c b/engine/gl/gl_vidcommon.c index a2ca2f54d..c780020cb 100644 --- a/engine/gl/gl_vidcommon.c +++ b/engine/gl/gl_vidcommon.c @@ -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"); diff --git a/engine/gl/gl_viddroid.c b/engine/gl/gl_viddroid.c index aee656ff7..d2836163f 100644 --- a/engine/gl/gl_viddroid.c +++ b/engine/gl/gl_viddroid.c @@ -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 +#include +#include +#include -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 diff --git a/engine/gl/gl_videgl.c b/engine/gl/gl_videgl.c index 03751e85c..e9971026d 100644 --- a/engine/gl/gl_videgl.c +++ b/engine/gl/gl_videgl.c @@ -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) diff --git a/engine/gl/gl_warp.c b/engine/gl/gl_warp.c index 1de21f434..11dcb0d0b 100644 --- a/engine/gl/gl_warp.c +++ b/engine/gl/gl_warp.c @@ -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. diff --git a/engine/gl/glsupp.h b/engine/gl/glsupp.h index fd541532e..c7569d861 100644 --- a/engine/gl/glsupp.h +++ b/engine/gl/glsupp.h @@ -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 diff --git a/engine/gl/shader.h b/engine/gl/shader.h index 03a938a0a..422092b42 100644 --- a/engine/gl/shader.h +++ b/engine/gl/shader.h @@ -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 diff --git a/engine/server/sv_phys.c b/engine/server/sv_phys.c index 2d6cc6853..900de66ad 100644 --- a/engine/server/sv_phys.c +++ b/engine/server/sv_phys.c @@ -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(); diff --git a/engine/vk/vk_backend.c b/engine/vk/vk_backend.c index ff37c01ab..2d83a548e 100644 --- a/engine/vk/vk_backend.c +++ b/engine/vk/vk_backend.c @@ -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; diff --git a/engine/vk/vk_init.c b/engine/vk/vk_init.c index 7027980b1..cd27ca2d1 100644 --- a/engine/vk/vk_init.c +++ b/engine/vk/vk_init.c @@ -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; diff --git a/engine/vk/vkrenderer.h b/engine/vk/vkrenderer.h index d4738188b..a531c96e4 100644 --- a/engine/vk/vkrenderer.h +++ b/engine/vk/vkrenderer.h @@ -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) diff --git a/plugins/avplug/avaudio.c b/plugins/avplug/avaudio.c index 618a5aaa4..e0e7fcec0 100644 --- a/plugins/avplug/avaudio.c +++ b/plugins/avplug/avaudio.c @@ -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; ipFormatCtx->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; ipFormatCtx->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; +} + diff --git a/quakec/menusys/cs/entrypoints.qc b/quakec/menusys/cs/entrypoints.qc index 62a859e28..db26c1e7b 100644 --- a/quakec/menusys/cs/entrypoints.qc +++ b/quakec/menusys/cs/entrypoints.qc @@ -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(); }; };