diff --git a/Makefile b/Makefile index 2c22c19e..7557835a 100644 --- a/Makefile +++ b/Makefile @@ -202,10 +202,6 @@ ifndef USE_INTERNAL_LIBS USE_INTERNAL_LIBS=1 endif -ifndef USE_INTERNAL_SPEEX -USE_INTERNAL_SPEEX=$(USE_INTERNAL_LIBS) -endif - ifndef USE_INTERNAL_OGG USE_INTERNAL_OGG=$(USE_INTERNAL_LIBS) endif @@ -258,7 +254,6 @@ NDIR=$(MOUNT_DIR)/null UIDIR=$(MOUNT_DIR)/ui Q3UIDIR=$(MOUNT_DIR)/ui JPDIR=$(MOUNT_DIR)/jpeg-8c -SPEEXDIR=$(MOUNT_DIR)/libspeex OGGDIR=$(MOUNT_DIR)/libogg-1.3.1 VORBISDIR=$(MOUNT_DIR)/libvorbis-1.3.4 OPUSDIR=$(MOUNT_DIR)/opus-1.1 @@ -571,7 +566,7 @@ ifdef MINGW SHLIBEXT=dll SHLIBCFLAGS= - SHLIBLDFLAGS=-shared -static-libgcc $(LDFLAGS) + SHLIBLDFLAGS=-shared $(LDFLAGS) BINEXT=.exe @@ -991,8 +986,18 @@ ifeq ($(USE_CURL),1) endif endif +ifeq ($(USE_VOIP),1) + CLIENT_CFLAGS += -DUSE_VOIP + SERVER_CFLAGS += -DUSE_VOIP + NEED_OPUS=1 +endif + ifeq ($(USE_CODEC_OPUS),1) CLIENT_CFLAGS += -DUSE_CODEC_OPUS + NEED_OPUS=1 +endif + +ifeq ($(NEED_OPUS),1) ifeq ($(USE_INTERNAL_OPUS),1) OPUS_CFLAGS = -DOPUS_BUILD -DHAVE_LRINTF -DFLOATING_POINT -DUSE_ALLOCA \ -I$(OPUSDIR)/include -I$(OPUSDIR)/celt -I$(OPUSDIR)/silk \ @@ -1038,19 +1043,6 @@ ifeq ($(USE_MUMBLE),1) CLIENT_CFLAGS += -DUSE_MUMBLE endif -ifeq ($(USE_VOIP),1) - CLIENT_CFLAGS += -DUSE_VOIP - SERVER_CFLAGS += -DUSE_VOIP - ifeq ($(USE_INTERNAL_SPEEX),1) - SPEEX_CFLAGS += -DFLOATING_POINT -DUSE_ALLOCA -I$(SPEEXDIR)/include - else - SPEEX_CFLAGS ?= $(shell pkg-config --silence-errors --cflags speex speexdsp || true) - SPEEX_LIBS ?= $(shell pkg-config --silence-errors --libs speex speexdsp || echo -lspeex -lspeexdsp) - endif - CLIENT_CFLAGS += $(SPEEX_CFLAGS) - CLIENT_LIBS += $(SPEEX_LIBS) -endif - ifeq ($(USE_INTERNAL_ZLIB),1) ZLIB_CFLAGS = -DNO_GZIP -I$(ZDIR) else @@ -1261,7 +1253,7 @@ endif NAKED_TARGETS=$(shell echo $(TARGETS) | sed -e "s!$(B)/!!g") -print_list=@for i in $(1); \ +print_list=-@for i in $(1); \ do \ echo " $$i"; \ done @@ -1652,6 +1644,7 @@ Q3R2OBJ = \ $(B)/renderergl2/tr_bsp.o \ $(B)/renderergl2/tr_cmds.o \ $(B)/renderergl2/tr_curve.o \ + $(B)/renderergl2/tr_dsa.o \ $(B)/renderergl2/tr_extramath.o \ $(B)/renderergl2/tr_extensions.o \ $(B)/renderergl2/tr_fbo.o \ @@ -1664,6 +1657,7 @@ Q3R2OBJ = \ $(B)/renderergl2/tr_image_pcx.o \ $(B)/renderergl2/tr_image_png.o \ $(B)/renderergl2/tr_image_tga.o \ + $(B)/renderergl2/tr_image_dds.o \ $(B)/renderergl2/tr_init.o \ $(B)/renderergl2/tr_light.o \ $(B)/renderergl2/tr_main.o \ @@ -1827,53 +1821,7 @@ ifeq ($(ARCH),x86_64) $(B)/client/ftola.o endif -ifeq ($(USE_VOIP),1) -ifeq ($(USE_INTERNAL_SPEEX),1) -Q3OBJ += \ - $(B)/client/bits.o \ - $(B)/client/buffer.o \ - $(B)/client/cb_search.o \ - $(B)/client/exc_10_16_table.o \ - $(B)/client/exc_10_32_table.o \ - $(B)/client/exc_20_32_table.o \ - $(B)/client/exc_5_256_table.o \ - $(B)/client/exc_5_64_table.o \ - $(B)/client/exc_8_128_table.o \ - $(B)/client/fftwrap.o \ - $(B)/client/filterbank.o \ - $(B)/client/filters.o \ - $(B)/client/gain_table.o \ - $(B)/client/gain_table_lbr.o \ - $(B)/client/hexc_10_32_table.o \ - $(B)/client/hexc_table.o \ - $(B)/client/high_lsp_tables.o \ - $(B)/client/jitter.o \ - $(B)/client/kiss_fft.o \ - $(B)/client/kiss_fftr.o \ - $(B)/client/lpc.o \ - $(B)/client/lsp.o \ - $(B)/client/lsp_tables_nb.o \ - $(B)/client/ltp.o \ - $(B)/client/mdf.o \ - $(B)/client/modes.o \ - $(B)/client/modes_wb.o \ - $(B)/client/nb_celp.o \ - $(B)/client/preprocess.o \ - $(B)/client/quant_lsp.o \ - $(B)/client/resample.o \ - $(B)/client/sb_celp.o \ - $(B)/client/smallft.o \ - $(B)/client/speex.o \ - $(B)/client/speex_callbacks.o \ - $(B)/client/speex_header.o \ - $(B)/client/stereo.o \ - $(B)/client/vbr.o \ - $(B)/client/vq.o \ - $(B)/client/window.o -endif -endif - -ifeq ($(USE_CODEC_OPUS),1) +ifeq ($(NEED_OPUS),1) ifeq ($(USE_INTERNAL_OPUS),1) Q3OBJ += \ $(B)/client/opus/analysis.o \ @@ -2556,9 +2504,6 @@ $(B)/client/%.o: $(CMDIR)/%.c $(B)/client/%.o: $(BLIBDIR)/%.c $(DO_BOT_CC) -$(B)/client/%.o: $(SPEEXDIR)/%.c - $(DO_CC) - $(B)/client/%.o: $(OGGDIR)/src/%.c $(DO_CC) @@ -2849,7 +2794,6 @@ ifdef MINGW USE_OPENAL_DLOPEN=$(USE_OPENAL_DLOPEN) \ USE_CURL_DLOPEN=$(USE_CURL_DLOPEN) \ USE_INTERNAL_OPUS=$(USE_INTERNAL_OPUS) \ - USE_INTERNAL_SPEEX=$(USE_INTERNAL_SPEEX) \ USE_INTERNAL_ZLIB=$(USE_INTERNAL_ZLIB) \ USE_INTERNAL_JPEG=$(USE_INTERNAL_JPEG) else diff --git a/code/client/cl_cgame.c b/code/client/cl_cgame.c index 65a4bffb..04d56b5e 100644 --- a/code/client/cl_cgame.c +++ b/code/client/cl_cgame.c @@ -919,37 +919,27 @@ void CL_FirstSnapshot( void ) { #endif #ifdef USE_VOIP - if (!clc.speexInitialized) { + if (!clc.voipCodecInitialized) { int i; - speex_bits_init(&clc.speexEncoderBits); - speex_bits_reset(&clc.speexEncoderBits); + int error; - clc.speexEncoder = speex_encoder_init(&speex_nb_mode); + clc.opusEncoder = opus_encoder_create(48000, 1, OPUS_APPLICATION_VOIP, &error); - speex_encoder_ctl(clc.speexEncoder, SPEEX_GET_FRAME_SIZE, - &clc.speexFrameSize); - speex_encoder_ctl(clc.speexEncoder, SPEEX_GET_SAMPLING_RATE, - &clc.speexSampleRate); - - clc.speexPreprocessor = speex_preprocess_state_init(clc.speexFrameSize, - clc.speexSampleRate); - - i = 1; - speex_preprocess_ctl(clc.speexPreprocessor, - SPEEX_PREPROCESS_SET_DENOISE, &i); - - i = 1; - speex_preprocess_ctl(clc.speexPreprocessor, - SPEEX_PREPROCESS_SET_AGC, &i); + if ( error ) { + Com_DPrintf("VoIP: Error opus_encoder_create %d\n", error); + return; + } for (i = 0; i < MAX_CLIENTS; i++) { - speex_bits_init(&clc.speexDecoderBits[i]); - speex_bits_reset(&clc.speexDecoderBits[i]); - clc.speexDecoder[i] = speex_decoder_init(&speex_nb_mode); + clc.opusDecoder[i] = opus_decoder_create(48000, 1, &error); + if ( error ) { + Com_DPrintf("VoIP: Error opus_decoder_create(%d) %d\n", i, error); + return; + } clc.voipIgnore[i] = qfalse; clc.voipGain[i] = 1.0f; } - clc.speexInitialized = qtrue; + clc.voipCodecInitialized = qtrue; clc.voipMuteAll = qfalse; Cmd_AddCommand ("voip", CL_Voip_f); Cvar_Set("cl_voipSendTarget", "spatial"); diff --git a/code/client/cl_input.c b/code/client/cl_input.c index 6e19263c..296a7cc0 100644 --- a/code/client/cl_input.c +++ b/code/client/cl_input.c @@ -632,6 +632,12 @@ void CL_CreateNewCommands( void ) { frame_msec = com_frameTime - old_com_frameTime; + // if running over 1000fps, act as if each frame is 1ms + // prevents divisions by zero + if ( frame_msec < 1 ) { + frame_msec = 1; + } + // if running less than 5fps, truncate the extra time to prevent // unexpected moves after a hitch if ( frame_msec > 200 ) { @@ -788,7 +794,7 @@ void CL_WritePacket( void ) { { if((clc.voipFlags & VOIP_SPATIAL) || Com_IsVoipTarget(clc.voipTargets, sizeof(clc.voipTargets), -1)) { - MSG_WriteByte (&buf, clc_voip); + MSG_WriteByte (&buf, clc_voipOpus); MSG_WriteByte (&buf, clc.voipOutgoingGeneration); MSG_WriteLong (&buf, clc.voipOutgoingSequence); MSG_WriteByte (&buf, clc.voipOutgoingDataFrames); @@ -809,7 +815,7 @@ void CL_WritePacket( void ) { MSG_Init (&fakemsg, fakedata, sizeof (fakedata)); MSG_Bitstream (&fakemsg); MSG_WriteLong (&fakemsg, clc.reliableAcknowledge); - MSG_WriteByte (&fakemsg, svc_voip); + MSG_WriteByte (&fakemsg, svc_voipOpus); MSG_WriteShort (&fakemsg, clc.clientNum); MSG_WriteByte (&fakemsg, clc.voipOutgoingGeneration); MSG_WriteLong (&fakemsg, clc.voipOutgoingSequence); diff --git a/code/client/cl_main.c b/code/client/cl_main.c index 7567ae2e..81f82d09 100644 --- a/code/client/cl_main.c +++ b/code/client/cl_main.c @@ -44,6 +44,7 @@ cvar_t *cl_voipSendTarget; cvar_t *cl_voipGainDuringCapture; cvar_t *cl_voipCaptureMult; cvar_t *cl_voipShowMeter; +cvar_t *cl_voipProtocol; cvar_t *cl_voip; #endif @@ -250,8 +251,8 @@ void CL_Voip_f( void ) if (clc.state != CA_ACTIVE) reason = "Not connected to a server"; - else if (!clc.speexInitialized) - reason = "Speex not initialized"; + else if (!clc.voipCodecInitialized) + reason = "Voip codec not initialized"; else if (!clc.voipEnabled) reason = "Server doesn't support VoIP"; else if (!clc.demoplaying && (Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER || Cvar_VariableValue("ui_singlePlayerActive"))) @@ -306,6 +307,8 @@ void CL_VoipNewGeneration(void) clc.voipOutgoingGeneration = 1; clc.voipPower = 0.0f; clc.voipOutgoingSequence = 0; + + opus_encoder_ctl(clc.opusEncoder, OPUS_RESET_STATE); } /* @@ -394,7 +397,7 @@ void CL_VoipParseTargets(void) =============== CL_CaptureVoip -Record more audio from the hardware if required and encode it into Speex +Record more audio from the hardware if required and encode it into Opus data for later transmission. =============== */ @@ -424,11 +427,12 @@ void CL_CaptureVoip(void) Com_Printf("Until then, VoIP is disabled.\n"); Cvar_Set("cl_voip", "0"); } + Cvar_Set("cl_voipProtocol", cl_voip->integer ? "opus" : ""); cl_voip->modified = qfalse; cl_rate->modified = qfalse; } - if (!clc.speexInitialized) + if (!clc.voipCodecInitialized) return; // just in case this gets called at a bad time. if (clc.voipOutgoingDataSize > 0) @@ -481,80 +485,67 @@ void CL_CaptureVoip(void) if ((cl_voipSend->integer) || (finalFrame)) { // user wants to capture audio? int samples = S_AvailableCaptureSamples(); - const int mult = (finalFrame) ? 1 : 4; // 4 == 80ms of audio. + const int packetSamples = (finalFrame) ? VOIP_MAX_FRAME_SAMPLES : VOIP_MAX_PACKET_SAMPLES; // enough data buffered in audio hardware to process yet? - if (samples >= (clc.speexFrameSize * mult)) { - // audio capture is always MONO16 (and that's what speex wants!). - // 2048 will cover 12 uncompressed frames in narrowband mode. - static int16_t sampbuffer[2048]; + if (samples >= packetSamples) { + // audio capture is always MONO16. + static int16_t sampbuffer[VOIP_MAX_PACKET_SAMPLES]; float voipPower = 0.0f; - int speexFrames = 0; - int wpos = 0; - int pos = 0; + int voipFrames; + int i, bytes; - if (samples > (clc.speexFrameSize * 4)) - samples = (clc.speexFrameSize * 4); + if (samples > VOIP_MAX_PACKET_SAMPLES) + samples = VOIP_MAX_PACKET_SAMPLES; // !!! FIXME: maybe separate recording from encoding, so voipPower // !!! FIXME: updates faster than 4Hz? - samples -= samples % clc.speexFrameSize; + samples -= samples % VOIP_MAX_FRAME_SAMPLES; + if (samples != 120 && samples != 240 && samples != 480 && samples != 960 && samples != 1920 && samples != 2880 ) { + Com_Printf("Voip: bad number of samples %d\n", samples); + return; + } + voipFrames = samples / VOIP_MAX_FRAME_SAMPLES; + S_Capture(samples, (byte *) sampbuffer); // grab from audio card. - // this will probably generate multiple speex packets each time. - while (samples > 0) { - int16_t *sampptr = &sampbuffer[pos]; - int i, bytes; + // check the "power" of this packet... + for (i = 0; i < samples; i++) { + const float flsamp = (float) sampbuffer[i]; + const float s = fabs(flsamp); + voipPower += s * s; + sampbuffer[i] = (int16_t) ((flsamp) * audioMult); + } - // preprocess samples to remove noise... - speex_preprocess_run(clc.speexPreprocessor, sampptr); - - // check the "power" of this packet... - for (i = 0; i < clc.speexFrameSize; i++) { - const float flsamp = (float) sampptr[i]; - const float s = fabs(flsamp); - voipPower += s * s; - sampptr[i] = (int16_t) ((flsamp) * audioMult); - } - - // encode raw audio samples into Speex data... - speex_bits_reset(&clc.speexEncoderBits); - speex_encode_int(clc.speexEncoder, sampptr, - &clc.speexEncoderBits); - bytes = speex_bits_write(&clc.speexEncoderBits, - (char *) &clc.voipOutgoingData[wpos+1], - sizeof (clc.voipOutgoingData) - (wpos+1)); - assert((bytes > 0) && (bytes < 256)); - clc.voipOutgoingData[wpos] = (byte) bytes; - wpos += bytes + 1; - - // look at the data for the next packet... - pos += clc.speexFrameSize; - samples -= clc.speexFrameSize; - speexFrames++; + // encode raw audio samples into Opus data... + bytes = opus_encode(clc.opusEncoder, sampbuffer, samples, + (unsigned char *) clc.voipOutgoingData, + sizeof (clc.voipOutgoingData)); + if ( bytes <= 0 ) { + Com_DPrintf("VoIP: Error encoding %d samples\n", samples); + bytes = 0; } clc.voipPower = (voipPower / (32768.0f * 32768.0f * - ((float) (clc.speexFrameSize * speexFrames)))) * - 100.0f; + ((float) samples))) * 100.0f; if ((useVad) && (clc.voipPower < cl_voipVADThreshold->value)) { CL_VoipNewGeneration(); // no "talk" for at least 1/4 second. } else { - clc.voipOutgoingDataSize = wpos; - clc.voipOutgoingDataFrames = speexFrames; + clc.voipOutgoingDataSize = bytes; + clc.voipOutgoingDataFrames = voipFrames; Com_DPrintf("VoIP: Send %d frames, %d bytes, %f power\n", - speexFrames, wpos, clc.voipPower); + voipFrames, bytes, clc.voipPower); #if 0 static FILE *encio = NULL; if (encio == NULL) encio = fopen("voip-outgoing-encoded.bin", "wb"); - if (encio != NULL) { fwrite(clc.voipOutgoingData, wpos, 1, encio); fflush(encio); } + if (encio != NULL) { fwrite(clc.voipOutgoingData, bytes, 1, encio); fflush(encio); } static FILE *decio = NULL; if (decio == NULL) decio = fopen("voip-outgoing-decoded.bin", "wb"); - if (decio != NULL) { fwrite(sampbuffer, speexFrames * clc.speexFrameSize * 2, 1, decio); fflush(decio); } + if (decio != NULL) { fwrite(sampbuffer, voipFrames * VOIP_MAX_FRAME_SAMPLES * 2, 1, decio); fflush(decio); } #endif } } @@ -1420,14 +1411,11 @@ void CL_Disconnect( qboolean showMainMenu ) { cl_voipUseVAD->integer = tmp; } - if (clc.speexInitialized) { + if (clc.voipCodecInitialized) { int i; - speex_bits_destroy(&clc.speexEncoderBits); - speex_encoder_destroy(clc.speexEncoder); - speex_preprocess_state_destroy(clc.speexPreprocessor); + opus_encoder_destroy(clc.opusEncoder); for (i = 0; i < MAX_CLIENTS; i++) { - speex_bits_destroy(&clc.speexDecoderBits[i]); - speex_decoder_destroy(clc.speexDecoder[i]); + opus_decoder_destroy(clc.opusDecoder[i]); } } Cmd_RemoveCommand ("voip"); @@ -1808,6 +1796,50 @@ static void CL_CompleteRcon( char *args, int argNum ) } } +/* +================== +CL_CompletePlayerName +================== +*/ +static void CL_CompletePlayerName( char *args, int argNum ) +{ + if( argNum == 2 ) + { + char names[MAX_CLIENTS][MAX_NAME_LENGTH]; + char *namesPtr[MAX_CLIENTS]; + int i; + int clientCount; + int nameCount; + const char *info; + const char *name; + + //configstring + info = cl.gameState.stringData + cl.gameState.stringOffsets[CS_SERVERINFO]; + clientCount = atoi( Info_ValueForKey( info, "sv_maxclients" ) ); + + nameCount = 0; + + for( i = 0; i < clientCount; i++ ) { + if( i == clc.clientNum ) + continue; + + info = cl.gameState.stringData + cl.gameState.stringOffsets[CS_PLAYERS+i]; + + name = Info_ValueForKey( info, "n" ); + if( name[0] == '\0' ) + continue; + Q_strncpyz( names[nameCount], name, sizeof(names[nameCount]) ); + Q_CleanStr( names[nameCount] ); + + namesPtr[nameCount] = names[nameCount]; + nameCount++; + } + qsort( (void*)namesPtr, nameCount, sizeof( namesPtr[0] ), Com_strCompare ); + + Field_CompletePlayerName( namesPtr, nameCount ); + } +} + /* ===================== CL_Rcon_f @@ -1820,7 +1852,7 @@ void CL_Rcon_f( void ) { char message[MAX_RCON_MESSAGE]; netadr_t to; - if ( !rcon_client_password->string ) { + if ( !rcon_client_password->string[0] ) { Com_Printf ("You must set 'rconpassword' before\n" "issuing an rcon command.\n"); return; @@ -3396,6 +3428,56 @@ static void CL_GenerateQKey(void) } } +void CL_Sayto_f( void ) { + char *rawname; + char name[MAX_NAME_LENGTH]; + char cleanName[MAX_NAME_LENGTH]; + const char *info; + int count; + int i; + int clientNum; + char *p; + + if ( Cmd_Argc() < 3 ) { + Com_Printf ("sayto \n"); + return; + } + + rawname = Cmd_Argv(1); + + Com_FieldStringToPlayerName( name, MAX_NAME_LENGTH, rawname ); + + info = cl.gameState.stringData + cl.gameState.stringOffsets[CS_SERVERINFO]; + count = atoi( Info_ValueForKey( info, "sv_maxclients" ) ); + + clientNum = -1; + for( i = 0; i < count; i++ ) { + + info = cl.gameState.stringData + cl.gameState.stringOffsets[CS_PLAYERS+i]; + Q_strncpyz( cleanName, Info_ValueForKey( info, "n" ), sizeof(cleanName) ); + Q_CleanStr( cleanName ); + + if ( !Q_stricmp( cleanName, name ) ) { + clientNum = i; + break; + } + } + if( clientNum <= -1 ) + { + Com_Printf ("No such player name: %s.\n", name); + return; + } + + p = Cmd_ArgsFrom(2); + + if ( *p == '"' ) { + p++; + p[strlen(p)-1] = 0; + } + + CL_AddReliableCommand(va("tell %i \"%s\"", clientNum, p ), qfalse); +} + /* ==================== CL_Init @@ -3559,9 +3641,9 @@ void CL_Init( void ) { cl_voipVADThreshold = Cvar_Get ("cl_voipVADThreshold", "0.25", CVAR_ARCHIVE); cl_voipShowMeter = Cvar_Get ("cl_voipShowMeter", "1", CVAR_ARCHIVE); - // This is a protocol version number. - cl_voip = Cvar_Get ("cl_voip", "1", CVAR_USERINFO | CVAR_ARCHIVE); + cl_voip = Cvar_Get ("cl_voip", "1", CVAR_ARCHIVE); Cvar_CheckRange( cl_voip, 0, 1, qtrue ); + cl_voipProtocol = Cvar_Get ("cl_voipProtocol", cl_voip->integer ? "opus" : "", CVAR_USERINFO | CVAR_ROM); #endif @@ -3598,6 +3680,10 @@ void CL_Init( void ) { Cmd_AddCommand ("model", CL_SetModel_f ); Cmd_AddCommand ("video", CL_Video_f ); Cmd_AddCommand ("stopvideo", CL_StopVideo_f ); + if( !com_dedicated->integer ) { + Cmd_AddCommand ("sayto", CL_Sayto_f ); + Cmd_SetCommandCompletionFunc( "sayto", CL_CompletePlayerName ); + } CL_InitRef(); SCR_Init (); diff --git a/code/client/cl_parse.c b/code/client/cl_parse.c index 5c9c1471..39682507 100644 --- a/code/client/cl_parse.c +++ b/code/client/cl_parse.c @@ -34,7 +34,8 @@ char *svc_strings[256] = { "svc_download", "svc_snapshot", "svc_EOF", - "svc_voip", + "svc_voipSpeex", + "svc_voipOpus", }; void SHOWNET( msg_t *msg, char *s) { @@ -359,8 +360,8 @@ void CL_SystemInfoChanged( void ) { else #endif { - s = Info_ValueForKey( systemInfo, "sv_voip" ); - clc.voipEnabled = atoi(s); + s = Info_ValueForKey( systemInfo, "sv_voipProtocol" ); + clc.voipEnabled = !Q_stricmp(s, "opus"); } #endif @@ -680,13 +681,13 @@ static void CL_PlayVoip(int sender, int samplecnt, const byte *data, int flags) { if(flags & VOIP_DIRECT) { - S_RawSamples(sender + 1, samplecnt, clc.speexSampleRate, 2, 1, + S_RawSamples(sender + 1, samplecnt, 48000, 2, 1, data, clc.voipGain[sender], -1); } if(flags & VOIP_SPATIAL) { - S_RawSamples(sender + MAX_CLIENTS + 1, samplecnt, clc.speexSampleRate, 2, 1, + S_RawSamples(sender + MAX_CLIENTS + 1, samplecnt, 48000, 2, 1, data, 1.0f, sender); } } @@ -699,8 +700,8 @@ A VoIP message has been received from the server ===================== */ static -void CL_ParseVoip ( msg_t *msg ) { - static short decoded[4096]; // !!! FIXME: don't hardcode. +void CL_ParseVoip ( msg_t *msg, qboolean ignoreData ) { + static short decoded[VOIP_MAX_PACKET_SAMPLES*4]; // !!! FIXME: don't hard code const int sender = MSG_ReadShort(msg); const int generation = MSG_ReadByte(msg); @@ -708,7 +709,8 @@ void CL_ParseVoip ( msg_t *msg ) { const int frames = MSG_ReadByte(msg); const int packetsize = MSG_ReadShort(msg); const int flags = MSG_ReadBits(msg, VOIP_FLAGCNT); - char encoded[1024]; + unsigned char encoded[4000]; + int numSamples; int seqdiff; int written = 0; int i; @@ -738,14 +740,15 @@ void CL_ParseVoip ( msg_t *msg ) { return; // overlarge packet, bail. } - if (!clc.speexInitialized) { - MSG_ReadData(msg, encoded, packetsize); // skip payload. - return; // can't handle VoIP without libspeex! + MSG_ReadData(msg, encoded, packetsize); + + if (ignoreData) { + return; // just ignore legacy speex voip data + } else if (!clc.voipCodecInitialized) { + return; // can't handle VoIP without libopus! } else if (sender >= MAX_CLIENTS) { - MSG_ReadData(msg, encoded, packetsize); // skip payload. return; // bogus sender. } else if (CL_ShouldIgnoreVoipSender(sender)) { - MSG_ReadData(msg, encoded, packetsize); // skip payload. return; // Channel is muted, bail. } @@ -758,70 +761,59 @@ void CL_ParseVoip ( msg_t *msg ) { // This is a new "generation" ... a new recording started, reset the bits. if (generation != clc.voipIncomingGeneration[sender]) { Com_DPrintf("VoIP: new generation %d!\n", generation); - speex_bits_reset(&clc.speexDecoderBits[sender]); + opus_decoder_ctl(clc.opusDecoder[sender], OPUS_RESET_STATE); clc.voipIncomingGeneration[sender] = generation; seqdiff = 0; } else if (seqdiff < 0) { // we're ahead of the sequence?! // This shouldn't happen unless the packet is corrupted or something. Com_DPrintf("VoIP: misordered sequence! %d < %d!\n", sequence, clc.voipIncomingSequence[sender]); - // reset the bits just in case. - speex_bits_reset(&clc.speexDecoderBits[sender]); + // reset the decoder just in case. + opus_decoder_ctl(clc.opusDecoder[sender], OPUS_RESET_STATE); seqdiff = 0; - } else if (seqdiff * clc.speexFrameSize * 2 >= sizeof (decoded)) { // dropped more than we can handle? + } else if (seqdiff * VOIP_MAX_PACKET_SAMPLES*2 >= sizeof (decoded)) { // dropped more than we can handle? // just start over. Com_DPrintf("VoIP: Dropped way too many (%d) frames from client #%d\n", seqdiff, sender); - speex_bits_reset(&clc.speexDecoderBits[sender]); + opus_decoder_ctl(clc.opusDecoder[sender], OPUS_RESET_STATE); seqdiff = 0; } if (seqdiff != 0) { Com_DPrintf("VoIP: Dropped %d frames from client #%d\n", seqdiff, sender); - // tell speex that we're missing frames... + // tell opus that we're missing frames... for (i = 0; i < seqdiff; i++) { - assert((written + clc.speexFrameSize) * 2 < sizeof (decoded)); - speex_decode_int(clc.speexDecoder[sender], NULL, decoded + written); - written += clc.speexFrameSize; + assert((written + VOIP_MAX_PACKET_SAMPLES) * 2 < sizeof (decoded)); + numSamples = opus_decode(clc.opusDecoder[sender], NULL, 0, decoded + written, VOIP_MAX_PACKET_SAMPLES, 0); + if ( numSamples <= 0 ) { + Com_DPrintf("VoIP: Error decoding frame %d from client #%d\n", i, sender); + continue; + } + written += numSamples; } } - for (i = 0; i < frames; i++) { - const int len = MSG_ReadByte(msg); - if (len < 0) { - Com_DPrintf("VoIP: Short packet!\n"); - break; - } - MSG_ReadData(msg, encoded, len); + numSamples = opus_decode(clc.opusDecoder[sender], encoded, packetsize, decoded + written, ARRAY_LEN(decoded) - written, 0); - // shouldn't happen, but just in case... - if ((written + clc.speexFrameSize) * 2 > sizeof (decoded)) { - Com_DPrintf("VoIP: playback %d bytes, %d samples, %d frames\n", - written * 2, written, i); - - CL_PlayVoip(sender, written, (const byte *) decoded, flags); - written = 0; - } - - speex_bits_read_from(&clc.speexDecoderBits[sender], encoded, len); - speex_decode_int(clc.speexDecoder[sender], - &clc.speexDecoderBits[sender], decoded + written); - - #if 0 - static FILE *encio = NULL; - if (encio == NULL) encio = fopen("voip-incoming-encoded.bin", "wb"); - if (encio != NULL) { fwrite(encoded, len, 1, encio); fflush(encio); } - static FILE *decio = NULL; - if (decio == NULL) decio = fopen("voip-incoming-decoded.bin", "wb"); - if (decio != NULL) { fwrite(decoded+written, clc.speexFrameSize*2, 1, decio); fflush(decio); } - #endif - - written += clc.speexFrameSize; + if ( numSamples <= 0 ) { + Com_DPrintf("VoIP: Error decoding voip data from client #%d\n", sender); + numSamples = 0; } + #if 0 + static FILE *encio = NULL; + if (encio == NULL) encio = fopen("voip-incoming-encoded.bin", "wb"); + if (encio != NULL) { fwrite(encoded, len, 1, encio); fflush(encio); } + static FILE *decio = NULL; + if (decio == NULL) decio = fopen("voip-incoming-decoded.bin", "wb"); + if (decio != NULL) { fwrite(decoded+written, clc.speexFrameSize*2, 1, decio); fflush(decio); } + #endif + + written += numSamples; + Com_DPrintf("VoIP: playback %d bytes, %d samples, %d frames\n", - written * 2, written, i); + written * 2, written, frames); if(written > 0) CL_PlayVoip(sender, written, (const byte *) decoded, flags); @@ -924,9 +916,14 @@ void CL_ParseServerMessage( msg_t *msg ) { case svc_download: CL_ParseDownload( msg ); break; - case svc_voip: + case svc_voipSpeex: #ifdef USE_VOIP - CL_ParseVoip( msg ); + CL_ParseVoip( msg, qtrue ); +#endif + break; + case svc_voipOpus: +#ifdef USE_VOIP + CL_ParseVoip( msg, !clc.voipEnabled ); #endif break; } diff --git a/code/client/client.h b/code/client/client.h index a9b3e51e..0fe3889c 100644 --- a/code/client/client.h +++ b/code/client/client.h @@ -35,8 +35,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #endif /* USE_CURL */ #ifdef USE_VOIP -#include "speex/speex.h" -#include "speex/speex_preprocess.h" +#include #endif // file full of random crap that gets used to create cl_guid @@ -238,14 +237,11 @@ typedef struct { #ifdef USE_VOIP qboolean voipEnabled; - qboolean speexInitialized; - int speexFrameSize; - int speexSampleRate; + qboolean voipCodecInitialized; // incoming data... // !!! FIXME: convert from parallel arrays to array of a struct. - SpeexBits speexDecoderBits[MAX_CLIENTS]; - void *speexDecoder[MAX_CLIENTS]; + OpusDecoder *opusDecoder[MAX_CLIENTS]; byte voipIncomingGeneration[MAX_CLIENTS]; int voipIncomingSequence[MAX_CLIENTS]; float voipGain[MAX_CLIENTS]; @@ -257,9 +253,7 @@ typedef struct { // then we are sending to clientnum i. uint8_t voipTargets[(MAX_CLIENTS + 7) / 8]; uint8_t voipFlags; - SpeexPreprocessState *speexPreprocessor; - SpeexBits speexEncoderBits; - void *speexEncoder; + OpusEncoder *opusEncoder; int voipOutgoingDataSize; int voipOutgoingDataFrames; int voipOutgoingSequence; @@ -447,6 +441,13 @@ extern cvar_t *cl_voipGainDuringCapture; extern cvar_t *cl_voipCaptureMult; extern cvar_t *cl_voipShowMeter; extern cvar_t *cl_voip; + +// 20ms at 48k +#define VOIP_MAX_FRAME_SAMPLES ( 20 * 48 ) + +// 3 frame is 60ms of audio, the max opus will encode at once +#define VOIP_MAX_PACKET_FRAMES 3 +#define VOIP_MAX_PACKET_SAMPLES ( VOIP_MAX_FRAME_SAMPLES * VOIP_MAX_PACKET_FRAMES ) #endif //================================================= diff --git a/code/client/snd_openal.c b/code/client/snd_openal.c index bec291dc..a1db8da3 100644 --- a/code/client/snd_openal.c +++ b/code/client/snd_openal.c @@ -3443,16 +3443,12 @@ qboolean S_AL_Init( soundInterface_t *si ) s_alAvailableInputDevices = Cvar_Get("s_alAvailableInputDevices", inputdevicenames, CVAR_ROM | CVAR_NORESTART); - // !!! FIXME: 8000Hz is what Speex narrowband mode needs, but we - // !!! FIXME: should probably open the capture device after - // !!! FIXME: initializing Speex so we can change to wideband - // !!! FIXME: if we like. Com_Printf("OpenAL default capture device is '%s'\n", defaultinputdevice ? defaultinputdevice : "none"); - alCaptureDevice = qalcCaptureOpenDevice(inputdevice, 8000, AL_FORMAT_MONO16, 4096); + alCaptureDevice = qalcCaptureOpenDevice(inputdevice, 48000, AL_FORMAT_MONO16, VOIP_MAX_PACKET_SAMPLES*4); if( !alCaptureDevice && inputdevice ) { Com_Printf( "Failed to open OpenAL Input device '%s', trying default.\n", inputdevice ); - alCaptureDevice = qalcCaptureOpenDevice(NULL, 8000, AL_FORMAT_MONO16, 4096); + alCaptureDevice = qalcCaptureOpenDevice(NULL, 48000, AL_FORMAT_MONO16, VOIP_MAX_PACKET_SAMPLES*4); } Com_Printf( "OpenAL capture device %s.\n", (alCaptureDevice == NULL) ? "failed to open" : "opened"); diff --git a/code/game/g_cmds.c b/code/game/g_cmds.c index 71d0f7af..d6e9818c 100644 --- a/code/game/g_cmds.c +++ b/code/game/g_cmds.c @@ -1752,7 +1752,7 @@ static void Cmd_Tell_f(gentity_t * ent) } //Slicer : no TELL FOR TP - if (!g_gametype.integer < GT_TEAM) + if (g_gametype.integer >= GT_TEAM) return; trap_Argv(1, arg, sizeof(arg)); diff --git a/code/qcommon/common.c b/code/qcommon/common.c index 53bb1713..b7d7fd89 100644 --- a/code/qcommon/common.c +++ b/code/qcommon/common.c @@ -3585,3 +3585,177 @@ qboolean Com_IsVoipTarget(uint8_t *voipTargets, int voipTargetsSize, int clientN return qfalse; } + +/* +=============== +Field_CompletePlayerName +=============== +*/ +static qboolean Field_CompletePlayerNameFinal( qboolean whitespace ) +{ + int completionOffset; + + if( matchCount == 0 ) + return qtrue; + + completionOffset = strlen( completionField->buffer ) - strlen( completionString ); + + Q_strncpyz( &completionField->buffer[ completionOffset ], shortestMatch, + sizeof( completionField->buffer ) - completionOffset ); + + completionField->cursor = strlen( completionField->buffer ); + + if( matchCount == 1 && whitespace ) + { + Q_strcat( completionField->buffer, sizeof( completionField->buffer ), " " ); + completionField->cursor++; + return qtrue; + } + + return qfalse; +} + +static void Name_PlayerNameCompletion( const char **names, int nameCount, void(*callback)(const char *s) ) +{ + int i; + + for( i = 0; i < nameCount; i++ ) { + callback( names[ i ] ); + } +} + +qboolean Com_FieldStringToPlayerName( char *name, int length, const char *rawname ) +{ + char hex[5]; + int i; + int ch; + + if( name == NULL || rawname == NULL ) + return qfalse; + + if( length <= 0 ) + return qtrue; + + for( i = 0; *rawname && i + 1 <= length; rawname++, i++ ) { + if( *rawname == '\\' ) { + Q_strncpyz( hex, rawname + 1, sizeof(hex) ); + ch = Com_HexStrToInt( hex ); + if( ch > -1 ) { + name[i] = ch; + rawname += 4; //hex string length, 0xXX + } else { + name[i] = *rawname; + } + } else { + name[i] = *rawname; + } + } + name[i] = '\0'; + + return qtrue; +} + +qboolean Com_PlayerNameToFieldString( char *str, int length, const char *name ) +{ + const char *p; + int i; + int x1, x2; + + if( str == NULL || name == NULL ) + return qfalse; + + if( length <= 0 ) + return qtrue; + + *str = '\0'; + p = name; + + for( i = 0; *p != '\0'; i++, p++ ) + { + if( i + 1 >= length ) + break; + + if( *p <= ' ' ) + { + if( i + 5 + 1 >= length ) + break; + + x1 = *p >> 4; + x2 = *p & 15; + + str[i+0] = '\\'; + str[i+1] = '0'; + str[i+2] = 'x'; + str[i+3] = x1 > 9 ? x1 - 10 + 'a' : x1 + '0'; + str[i+4] = x2 > 9 ? x2 - 10 + 'a' : x2 + '0'; + + i += 4; + } else { + str[i] = *p; + } + } + str[i] = '\0'; + + return qtrue; +} + +void Field_CompletePlayerName( char **names, int nameCount ) +{ + qboolean whitespace; + + matchCount = 0; + shortestMatch[ 0 ] = 0; + + if( nameCount <= 0 ) + return; + + Name_PlayerNameCompletion( names, nameCount, FindMatches ); + + if( completionString[0] == '\0' ) + { + Com_PlayerNameToFieldString( shortestMatch, sizeof( shortestMatch ), names[ 0 ] ); + } + + //allow to tab player names + //if full player name switch to next player name + if( completionString[0] != '\0' + && Q_stricmp( shortestMatch, completionString ) == 0 + && nameCount > 1 ) + { + int i; + + for( i = 0; i < nameCount; i++ ) { + if( Q_stricmp( names[ i ], completionString ) == 0 ) + { + i++; + if( i >= nameCount ) + { + i = 0; + } + + Com_PlayerNameToFieldString( shortestMatch, sizeof( shortestMatch ), names[ i ] ); + break; + } + } + } + + if( matchCount > 1 ) + { + Com_Printf( "]%s\n", completionField->buffer ); + + Name_PlayerNameCompletion( names, nameCount, PrintMatches ); + } + + whitespace = nameCount == 1? qtrue: qfalse; + if( !Field_CompletePlayerNameFinal( whitespace ) ) + { + + } +} + +int QDECL Com_strCompare( const void *a, const void *b ) +{ + const char **pa = (const char **)a; + const char **pb = (const char **)b; + return strcmp( *pa, *pb ); +} diff --git a/code/qcommon/json.h b/code/qcommon/json.h new file mode 100644 index 00000000..cfc5b3ca --- /dev/null +++ b/code/qcommon/json.h @@ -0,0 +1,353 @@ +/* +=========================================================================== +Copyright (C) 2016 James Canete + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +=========================================================================== +*/ + +#ifndef JSON_H +#define JSON_H + +enum +{ + JSONTYPE_STRING, // string + JSONTYPE_OBJECT, // object + JSONTYPE_ARRAY, // array + JSONTYPE_VALUE, // number, true, false, or null + JSONTYPE_ERROR // out of data +}; + +// -------------------------------------------------------------------------- +// Array Functions +// -------------------------------------------------------------------------- + +// Get pointer to first value in array +// When given pointer to an array, returns pointer to the first +// returns NULL if array is empty or not an array. +const char *JSON_ArrayGetFirstValue(const char *json, const char *jsonEnd); + +// Get pointer to next value in array +// When given pointer to a value, returns pointer to the next value +// returns NULL when no next value. +const char *JSON_ArrayGetNextValue(const char *json, const char *jsonEnd); + +// Get pointers to values in an array +// returns 0 if not an array, array is empty, or out of data +// returns number of values in the array and copies into index if successful +unsigned int JSON_ArrayGetIndex(const char *json, const char *jsonEnd, const char **indexes, unsigned int numIndexes); + +// Get pointer to indexed value from array +// returns NULL if not an array, no index, or out of data +const char *JSON_ArrayGetValue(const char *json, const char *jsonEnd, unsigned int index); + +// -------------------------------------------------------------------------- +// Object Functions +// -------------------------------------------------------------------------- + +// Get pointer to named value from object +// returns NULL if not an object, name not found, or out of data +const char *JSON_ObjectGetNamedValue(const char *json, const char *jsonEnd, const char *name); + +// -------------------------------------------------------------------------- +// Value Functions +// -------------------------------------------------------------------------- + +// Get type of value +// returns JSONTYPE_ERROR if out of data +unsigned int JSON_ValueGetType(const char *json, const char *jsonEnd); + +// Get value as string +// returns 0 if out of data +// returns length and copies into string if successful, including terminating nul. +// string values are stripped of enclosing quotes but not escaped +unsigned int JSON_ValueGetString(const char *json, const char *jsonEnd, char *outString, unsigned int stringLen); + +// Get value as appropriate type +// returns 0 if value is false, value is null, or out of data +// returns 1 if value is true +// returns value otherwise +double JSON_ValueGetDouble(const char *json, const char *jsonEnd); +float JSON_ValueGetFloat(const char *json, const char *jsonEnd); +int JSON_ValueGetInt(const char *json, const char *jsonEnd); + +#endif + +#ifdef JSON_IMPLEMENTATION +#include + +// -------------------------------------------------------------------------- +// Internal Functions +// -------------------------------------------------------------------------- + +static const char *JSON_SkipSeparators(const char *json, const char *jsonEnd); +static const char *JSON_SkipString(const char *json, const char *jsonEnd); +static const char *JSON_SkipStruct(const char *json, const char *jsonEnd); +static const char *JSON_SkipValue(const char *json, const char *jsonEnd); +static const char *JSON_SkipValueAndSeparators(const char *json, const char *jsonEnd); + +#define IS_SEPARATOR(x) ((x) == ' ' || (x) == '\t' || (x) == '\n' || (x) == '\r' || (x) == ',' || (x) == ':') +#define IS_STRUCT_OPEN(x) ((x) == '{' || (x) == '[') +#define IS_STRUCT_CLOSE(x) ((x) == '}' || (x) == ']') + +static const char *JSON_SkipSeparators(const char *json, const char *jsonEnd) +{ + while (json < jsonEnd && IS_SEPARATOR(*json)) + json++; + + return json; +} + +static const char *JSON_SkipString(const char *json, const char *jsonEnd) +{ + for (json++; json < jsonEnd && *json != '"'; json++) + if (*json == '\\') + json++; + + return (json + 1 > jsonEnd) ? jsonEnd : json + 1; +} + +static const char *JSON_SkipStruct(const char *json, const char *jsonEnd) +{ + json = JSON_SkipSeparators(json + 1, jsonEnd); + while (json < jsonEnd && !IS_STRUCT_CLOSE(*json)) + json = JSON_SkipValueAndSeparators(json, jsonEnd); + + return (json + 1 > jsonEnd) ? jsonEnd : json + 1; +} + +static const char *JSON_SkipValue(const char *json, const char *jsonEnd) +{ + if (json >= jsonEnd) + return jsonEnd; + else if (*json == '"') + json = JSON_SkipString(json, jsonEnd); + else if (IS_STRUCT_OPEN(*json)) + json = JSON_SkipStruct(json, jsonEnd); + else + { + while (json < jsonEnd && !IS_SEPARATOR(*json) && !IS_STRUCT_CLOSE(*json)) + json++; + } + + return json; +} + +static const char *JSON_SkipValueAndSeparators(const char *json, const char *jsonEnd) +{ + json = JSON_SkipValue(json, jsonEnd); + return JSON_SkipSeparators(json, jsonEnd); +} + +// returns 0 if value requires more parsing, 1 if no more data/false/null, 2 if true +static unsigned int JSON_NoParse(const char *json, const char *jsonEnd) +{ + if (!json || json >= jsonEnd || *json == 'f' || *json == 'n') + return 1; + + if (*json == 't') + return 2; + + return 0; +} + +// -------------------------------------------------------------------------- +// Array Functions +// -------------------------------------------------------------------------- + +const char *JSON_ArrayGetFirstValue(const char *json, const char *jsonEnd) +{ + if (!json || json >= jsonEnd || !IS_STRUCT_OPEN(*json)) + return NULL; + + json = JSON_SkipSeparators(json + 1, jsonEnd); + + return (json >= jsonEnd || IS_STRUCT_CLOSE(*json)) ? NULL : json; +} + +const char *JSON_ArrayGetNextValue(const char *json, const char *jsonEnd) +{ + if (!json || json >= jsonEnd || IS_STRUCT_CLOSE(*json)) + return NULL; + + json = JSON_SkipValueAndSeparators(json, jsonEnd); + + return (json >= jsonEnd || IS_STRUCT_CLOSE(*json)) ? NULL : json; +} + +unsigned int JSON_ArrayGetIndex(const char *json, const char *jsonEnd, const char **indexes, unsigned int numIndexes) +{ + unsigned int length = 0; + + for (json = JSON_ArrayGetFirstValue(json, jsonEnd); json; json = JSON_ArrayGetNextValue(json, jsonEnd)) + { + if (indexes && numIndexes) + { + *indexes++ = json; + numIndexes--; + } + length++; + } + + return length; +} + +const char *JSON_ArrayGetValue(const char *json, const char *jsonEnd, unsigned int index) +{ + for (json = JSON_ArrayGetFirstValue(json, jsonEnd); json && index; json = JSON_ArrayGetNextValue(json, jsonEnd)) + index--; + + return json; +} + +// -------------------------------------------------------------------------- +// Object Functions +// -------------------------------------------------------------------------- + +const char *JSON_ObjectGetNamedValue(const char *json, const char *jsonEnd, const char *name) +{ + unsigned int nameLen = strlen(name); + + for (json = JSON_ArrayGetFirstValue(json, jsonEnd); json; json = JSON_ArrayGetNextValue(json, jsonEnd)) + { + if (*json == '"') + { + const char *thisNameStart, *thisNameEnd; + + thisNameStart = json + 1; + json = JSON_SkipString(json, jsonEnd); + thisNameEnd = json - 1; + json = JSON_SkipSeparators(json, jsonEnd); + + if ((unsigned int)(thisNameEnd - thisNameStart) == nameLen) + if (strncmp(thisNameStart, name, nameLen) == 0) + return json; + } + } + + return NULL; +} + +// -------------------------------------------------------------------------- +// Value Functions +// -------------------------------------------------------------------------- + +unsigned int JSON_ValueGetType(const char *json, const char *jsonEnd) +{ + if (!json || json >= jsonEnd) + return JSONTYPE_ERROR; + else if (*json == '"') + return JSONTYPE_STRING; + else if (*json == '{') + return JSONTYPE_OBJECT; + else if (*json == '[') + return JSONTYPE_ARRAY; + + return JSONTYPE_VALUE; +} + +unsigned int JSON_ValueGetString(const char *json, const char *jsonEnd, char *outString, unsigned int stringLen) +{ + const char *stringEnd, *stringStart; + + if (!json) + { + *outString = '\0'; + return 0; + } + + stringStart = json; + stringEnd = JSON_SkipValue(stringStart, jsonEnd); + if (stringEnd >= jsonEnd) + { + *outString = '\0'; + return 0; + } + + // skip enclosing quotes if they exist + if (*stringStart == '"') + stringStart++; + + if (*(stringEnd - 1) == '"') + stringEnd--; + + stringLen--; + if (stringLen > stringEnd - stringStart) + stringLen = stringEnd - stringStart; + + json = stringStart; + while (stringLen--) + *outString++ = *json++; + *outString = '\0'; + + return stringEnd - stringStart; +} + +double JSON_ValueGetDouble(const char *json, const char *jsonEnd) +{ + char cValue[256]; + double dValue = 0.0; + unsigned int np = JSON_NoParse(json, jsonEnd); + + if (np) + return (double)(np - 1); + + if (!JSON_ValueGetString(json, jsonEnd, cValue, 256)) + return 0.0; + + sscanf(cValue, "%lf", &dValue); + + return dValue; +} + +float JSON_ValueGetFloat(const char *json, const char *jsonEnd) +{ + char cValue[256]; + float fValue = 0.0f; + unsigned int np = JSON_NoParse(json, jsonEnd); + + if (np) + return (float)(np - 1); + + if (!JSON_ValueGetString(json, jsonEnd, cValue, 256)) + return 0.0f; + + sscanf(cValue, "%f", &fValue); + + return fValue; +} + +int JSON_ValueGetInt(const char *json, const char *jsonEnd) +{ + char cValue[256]; + int iValue = 0; + unsigned int np = JSON_NoParse(json, jsonEnd); + + if (np) + return np - 1; + + if (!JSON_ValueGetString(json, jsonEnd, cValue, 256)) + return 0; + + sscanf(cValue, "%d", &iValue); + + return iValue; +} + +#undef IS_SEPARATOR +#undef IS_STRUCT_OPEN +#undef IS_STRUCT_CLOSE + +#endif diff --git a/code/qcommon/q_shared.h b/code/qcommon/q_shared.h index 2f74ef6d..e94c3d9b 100644 --- a/code/qcommon/q_shared.h +++ b/code/qcommon/q_shared.h @@ -27,26 +27,26 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA // A user mod should never modify this file #ifdef STANDALONE - #define PRODUCT_NAME "Reaction" - #define BASEGAME "Boomstick" + #define PRODUCT_NAME "Reaction" + #define BASEGAME "Boomstick" #define CLIENT_WINDOW_TITLE "Reaction" #define CLIENT_WINDOW_MIN_TITLE "Reaction" #define HOMEPATH_NAME_UNIX ".Reaction" #define HOMEPATH_NAME_WIN "Reaction" #define HOMEPATH_NAME_MACOSX HOMEPATH_NAME_WIN -// #define STEAMPATH_NAME "Foo Bar" +// #define STEAMPATH_NAME "Foo Bar" // #define STEAMPATH_APPID "" #define GAMENAME_FOR_MASTER "Reaction" // #define LEGACY_PROTOCOL // You probably don't need this for your standalone game #else - #define PRODUCT_NAME "Reaction" - #define BASEGAME "Boomstick" + #define PRODUCT_NAME "Reaction" + #define BASEGAME "Boomstick" #define CLIENT_WINDOW_TITLE "Reaction" #define CLIENT_WINDOW_MIN_TITLE "Reaction" #define HOMEPATH_NAME_UNIX ".Reaction" #define HOMEPATH_NAME_WIN "Reaction" #define HOMEPATH_NAME_MACOSX HOMEPATH_NAME_WIN -// #define STEAMPATH_NAME "Foo Bar" +// #define STEAMPATH_NAME "Foo Bar" // #define STEAMPATH_APPID "" #define GAMENAME_FOR_MASTER "Reaction" // #define LEGACY_PROTOCOL diff --git a/code/qcommon/qcommon.h b/code/qcommon/qcommon.h index 017ac47f..101b5459 100644 --- a/code/qcommon/qcommon.h +++ b/code/qcommon/qcommon.h @@ -300,7 +300,8 @@ enum svc_ops_e { svc_EOF, // new commands, supported only by ioquake3 protocol but not legacy - svc_voip, // not wrapped in USE_VOIP, so this value is reserved. + svc_voipSpeex, // not wrapped in USE_VOIP, so this value is reserved. + svc_voipOpus, // }; @@ -316,7 +317,8 @@ enum clc_ops_e { clc_EOF, // new commands, supported only by ioquake3 protocol but not legacy - clc_voip, // not wrapped in USE_VOIP, so this value is reserved. + clc_voipSpeex, // not wrapped in USE_VOIP, so this value is reserved. + clc_voipOpus, // }; /* @@ -760,6 +762,7 @@ void Field_CompleteFilename( const char *dir, const char *ext, qboolean stripExt, qboolean allowNonPureFilesOnDisk ); void Field_CompleteCommand( char *cmd, qboolean doCommands, qboolean doCvars ); +void Field_CompletePlayerName( char **names, int count ); /* ============================================================== @@ -839,6 +842,10 @@ void Com_StartupVariable( const char *match ); // if match is NULL, all set commands will be executed, otherwise // only a set with the exact name. Only used during startup. +qboolean Com_PlayerNameToFieldString( char *str, int length, const char *name ); +qboolean Com_FieldStringToPlayerName( char *name, int length, const char *rawname ); +int QDECL Com_strCompare( const void *a, const void *b ); + extern cvar_t *com_developer; extern cvar_t *com_dedicated; diff --git a/code/renderercommon/qgl.h b/code/renderercommon/qgl.h index 252e0bb2..971684fb 100644 --- a/code/renderercommon/qgl.h +++ b/code/renderercommon/qgl.h @@ -677,12 +677,12 @@ extern void (APIENTRY * qglRenderbufferStorageMultisampleEXT)(GLenum target, GLs #define GL_FRAMEBUFFER_SRGB_EXT 0x8DB9 #endif -#ifndef GL_EXT_texture_compression_latc -#define GL_EXT_texture_compression_latc -#define GL_COMPRESSED_LUMINANCE_LATC1_EXT 0x8C70 -#define GL_COMPRESSED_SIGNED_LUMINANCE_LATC1_EXT 0x8C71 -#define GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT 0x8C72 -#define GL_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2_EXT 0x8C73 +#ifndef GL_ARB_texture_compression_rgtc +#define GL_ARB_texture_compression_rgtc +#define GL_COMPRESSED_RED_RGTC1 0x8DBB +#define GL_COMPRESSED_SIGNED_RED_RGTC1 0x8DBC +#define GL_COMPRESSED_RG_RGTC2 0x8DBD +#define GL_COMPRESSED_SIGNED_RG_RGTC2 0x8DBE #endif #ifndef GL_ARB_texture_compression_bptc @@ -736,6 +736,51 @@ extern GLboolean (APIENTRY * qglIsVertexArrayARB)(GLuint array); #define GL_VERTEX_ARRAY_BINDING_ARB 0x85B5 #endif +// GL_EXT_direct_state_access +extern GLvoid(APIENTRY * qglBindMultiTexture)(GLenum texunit, GLenum target, GLuint texture); +extern GLvoid(APIENTRY * qglTextureParameterf)(GLuint texture, GLenum target, GLenum pname, GLfloat param); +extern GLvoid(APIENTRY * qglTextureParameteri)(GLuint texture, GLenum target, GLenum pname, GLint param); +extern GLvoid(APIENTRY * qglTextureImage2D)(GLuint texture, GLenum target, GLint level, GLint internalformat, + GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels); +extern GLvoid(APIENTRY * qglTextureSubImage2D)(GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, + GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels); +extern GLvoid(APIENTRY * qglCopyTextureImage2D)(GLuint texture, GLenum target, GLint level, GLenum internalformat, + GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +extern GLvoid(APIENTRY * qglCompressedTextureImage2D)(GLuint texture, GLenum target, GLint level, GLenum internalformat, + GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *data); +extern GLvoid(APIENTRY * qglCompressedTextureSubImage2D)(GLuint texture, GLenum target, GLint level, + GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, + GLsizei imageSize, const GLvoid *data); +extern GLvoid(APIENTRY * qglGenerateTextureMipmap)(GLuint texture, GLenum target); + +extern GLvoid(APIENTRY * qglProgramUniform1i)(GLuint program, GLint location, GLint v0); +extern GLvoid(APIENTRY * qglProgramUniform1f)(GLuint program, GLint location, GLfloat v0); +extern GLvoid(APIENTRY * qglProgramUniform2f)(GLuint program, GLint location, + GLfloat v0, GLfloat v1); +extern GLvoid(APIENTRY * qglProgramUniform3f)(GLuint program, GLint location, + GLfloat v0, GLfloat v1, GLfloat v2); +extern GLvoid(APIENTRY * qglProgramUniform4f)(GLuint program, GLint location, + GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +extern GLvoid(APIENTRY * qglProgramUniform1fv)(GLuint program, GLint location, + GLsizei count, const GLfloat *value); +extern GLvoid(APIENTRY * qglProgramUniformMatrix4fv)(GLuint program, GLint location, + GLsizei count, GLboolean transpose, + const GLfloat *value); + +extern GLvoid(APIENTRY * qglNamedRenderbufferStorage)(GLuint renderbuffer, + GLenum internalformat, GLsizei width, GLsizei height); + +extern GLvoid(APIENTRY * qglNamedRenderbufferStorageMultisample)(GLuint renderbuffer, + GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); + +extern GLenum(APIENTRY * qglCheckNamedFramebufferStatus)(GLuint framebuffer, GLenum target); +extern GLvoid(APIENTRY * qglNamedFramebufferTexture2D)(GLuint framebuffer, + GLenum attachment, GLenum textarget, GLuint texture, GLint level); +extern GLvoid(APIENTRY * qglNamedFramebufferRenderbuffer)(GLuint framebuffer, + GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); + + + #if defined(WIN32) // WGL_ARB_create_context diff --git a/code/renderercommon/tr_image_jpg.c b/code/renderercommon/tr_image_jpg.c index 3bf44bbb..a9bbca15 100644 --- a/code/renderercommon/tr_image_jpg.c +++ b/code/renderercommon/tr_image_jpg.c @@ -20,6 +20,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ +#include + #include "tr_common.h" /* @@ -42,16 +44,27 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # endif #endif -static void __attribute__((__noreturn__)) R_JPGErrorExit(j_common_ptr cinfo) +/* Catching errors, as done in libjpeg's example.c */ +typedef struct q_jpeg_error_mgr_s +{ + struct jpeg_error_mgr pub; /* "public" fields */ + + jmp_buf setjmp_buffer; /* for return to caller */ +} q_jpeg_error_mgr_t; + +static void R_JPGErrorExit(j_common_ptr cinfo) { char buffer[JMSG_LENGTH_MAX]; + + /* cinfo->err really points to a q_jpeg_error_mgr_s struct, so coerce pointer */ + q_jpeg_error_mgr_t *jerr = (q_jpeg_error_mgr_t *)cinfo->err; (*cinfo->err->format_message) (cinfo, buffer); - - /* Let the memory manager delete any temp files before we die */ - jpeg_destroy(cinfo); - - ri.Error(ERR_FATAL, "%s", buffer); + + ri.Printf(PRINT_ALL, "Error: %s", buffer); + + /* Return control to the setjmp point */ + longjmp(jerr->setjmp_buffer, 1); } static void R_JPGOutputMessage(j_common_ptr cinfo) @@ -83,7 +96,7 @@ void R_LoadJPG(const char *filename, unsigned char **pic, int *width, int *heigh * Note that this struct must live as long as the main JPEG parameter * struct, to avoid dangling-pointer problems. */ - struct jpeg_error_mgr jerr; + q_jpeg_error_mgr_t jerr; /* More stuff */ JSAMPARRAY buffer; /* Output row buffer */ unsigned int row_stride; /* physical row width in output buffer */ @@ -115,10 +128,24 @@ void R_LoadJPG(const char *filename, unsigned char **pic, int *width, int *heigh * This routine fills in the contents of struct jerr, and returns jerr's * address which we place into the link field in cinfo. */ - cinfo.err = jpeg_std_error(&jerr); + cinfo.err = jpeg_std_error(&jerr.pub); cinfo.err->error_exit = R_JPGErrorExit; cinfo.err->output_message = R_JPGOutputMessage; + /* Establish the setjmp return context for R_JPGErrorExit to use. */ + if (setjmp(jerr.setjmp_buffer)) + { + /* If we get here, the JPEG code has signaled an error. + * We need to clean up the JPEG object, close the input file, and return. + */ + jpeg_destroy_decompress(&cinfo); + ri.FS_FreeFile(fbuffer.v); + + /* Append the filename to the error for easier debugging */ + ri.Printf(PRINT_ALL, ", loading file %s\n", filename); + return; + } + /* Now we can initialize the JPEG decompression object. */ jpeg_create_decompress(&cinfo); @@ -361,17 +388,29 @@ size_t RE_SaveJPGToBuffer(byte *buffer, size_t bufSize, int quality, int image_width, int image_height, byte *image_buffer, int padding) { struct jpeg_compress_struct cinfo; - struct jpeg_error_mgr jerr; + q_jpeg_error_mgr_t jerr; JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */ my_dest_ptr dest; int row_stride; /* physical row width in image buffer */ size_t outcount; /* Step 1: allocate and initialize JPEG compression object */ - cinfo.err = jpeg_std_error(&jerr); + cinfo.err = jpeg_std_error(&jerr.pub); cinfo.err->error_exit = R_JPGErrorExit; cinfo.err->output_message = R_JPGOutputMessage; + /* Establish the setjmp return context for R_JPGErrorExit to use. */ + if (setjmp(jerr.setjmp_buffer)) + { + /* If we get here, the JPEG code has signaled an error. + * We need to clean up the JPEG object and return. + */ + jpeg_destroy_compress(&cinfo); + + ri.Printf(PRINT_ALL, "\n"); + return 0; + } + /* Now we can initialize the JPEG compression object. */ jpeg_create_compress(&cinfo); diff --git a/code/renderergl1/tr_backend.c b/code/renderergl1/tr_backend.c index a1122456..41fe47ac 100644 --- a/code/renderergl1/tr_backend.c +++ b/code/renderergl1/tr_backend.c @@ -495,8 +495,6 @@ void RB_BeginDrawingView (void) { } -#define MAC_EVENT_PUMP_MSEC 5 - /* ================== RB_RenderDrawSurfList diff --git a/code/renderergl1/tr_cmds.c b/code/renderergl1/tr_cmds.c index 00b819ac..89bb88a3 100644 --- a/code/renderergl1/tr_cmds.c +++ b/code/renderergl1/tr_cmds.c @@ -114,20 +114,20 @@ void R_IssuePendingRenderCommands( void ) { /* ============ -R_GetCommandBuffer +R_GetCommandBufferReserved make sure there is enough command space ============ */ -void *R_GetCommandBuffer( int bytes ) { +void *R_GetCommandBufferReserved( int bytes, int reservedBytes ) { renderCommandList_t *cmdList; cmdList = &backEndData->commands; bytes = PAD(bytes, sizeof(void *)); // always leave room for the end of list command - if ( cmdList->used + bytes + 4 > MAX_RENDER_COMMANDS ) { - if ( bytes > MAX_RENDER_COMMANDS - 4 ) { + if ( cmdList->used + bytes + sizeof( int ) + reservedBytes > MAX_RENDER_COMMANDS ) { + if ( bytes > MAX_RENDER_COMMANDS - sizeof( int ) ) { ri.Error( ERR_FATAL, "R_GetCommandBuffer: bad size %i", bytes ); } // if we run out of room, just start dropping commands @@ -139,6 +139,17 @@ void *R_GetCommandBuffer( int bytes ) { return cmdList->cmds + cmdList->used - bytes; } +/* +============= +R_GetCommandBuffer + +returns NULL if there is not enough space for important commands +============= +*/ +void *R_GetCommandBuffer( int bytes ) { + return R_GetCommandBufferReserved( bytes, PAD( sizeof( swapBuffersCommand_t ), sizeof(void *) ) ); +} + /* ============= @@ -454,7 +465,7 @@ void RE_EndFrame( int *frontEndMsec, int *backEndMsec ) { if ( !tr.registered ) { return; } - cmd = R_GetCommandBuffer( sizeof( *cmd ) ); + cmd = R_GetCommandBufferReserved( sizeof( *cmd ), 0 ); if ( !cmd ) { return; } diff --git a/code/renderergl1/tr_image.c b/code/renderergl1/tr_image.c index 7905842b..b131f366 100644 --- a/code/renderergl1/tr_image.c +++ b/code/renderergl1/tr_image.c @@ -557,6 +557,7 @@ static void Upload32( unsigned *data, qboolean mipmap, qboolean picmip, qboolean lightMap, + qboolean allowCompression, int *format, int *pUploadWidth, int *pUploadHeight ) { @@ -692,11 +693,11 @@ static void Upload32( unsigned *data, } else { - if ( glConfig.textureCompression == TC_S3TC_ARB ) + if ( allowCompression && glConfig.textureCompression == TC_S3TC_ARB ) { internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; } - else if ( glConfig.textureCompression == TC_S3TC ) + else if ( allowCompression && glConfig.textureCompression == TC_S3TC ) { internalFormat = GL_RGB4_S3TC; } @@ -892,6 +893,7 @@ image_t *R_CreateImage( const char *name, byte *pic, int width, int height, image->flags & IMGFLAG_MIPMAP, image->flags & IMGFLAG_PICMIP, isLightmap, + !(image->flags & IMGFLAG_NO_COMPRESSION), &image->internalFormat, &image->uploadWidth, &image->uploadHeight ); diff --git a/code/renderergl1/tr_main.c b/code/renderergl1/tr_main.c index e33eb952..0e388314 100644 --- a/code/renderergl1/tr_main.c +++ b/code/renderergl1/tr_main.c @@ -1146,13 +1146,6 @@ void R_SortDrawSurfs( drawSurf_t *drawSurfs, int numDrawSurfs ) { return; } - // if we overflowed MAX_DRAWSURFS, the drawsurfs - // wrapped around in the buffer and we will be missing - // the first surfaces, not the last ones - if ( numDrawSurfs > MAX_DRAWSURFS ) { - numDrawSurfs = MAX_DRAWSURFS; - } - // sort the drawsurfs by sort type, then orientation, then shader R_RadixSort( drawSurfs, numDrawSurfs ); @@ -1361,6 +1354,7 @@ or a mirror / remote location */ void R_RenderView (viewParms_t *parms) { int firstDrawSurf; + int numDrawSurfs; if ( parms->viewportWidth <= 0 || parms->viewportHeight <= 0 ) { return; @@ -1383,7 +1377,15 @@ void R_RenderView (viewParms_t *parms) { R_GenerateDrawSurfs(); - R_SortDrawSurfs( tr.refdef.drawSurfs + firstDrawSurf, tr.refdef.numDrawSurfs - firstDrawSurf ); + // if we overflowed MAX_DRAWSURFS, the drawsurfs + // wrapped around in the buffer and we will be missing + // the first surfaces, not the last ones + numDrawSurfs = tr.refdef.numDrawSurfs; + if ( numDrawSurfs > MAX_DRAWSURFS ) { + numDrawSurfs = MAX_DRAWSURFS; + } + + R_SortDrawSurfs( tr.refdef.drawSurfs + firstDrawSurf, numDrawSurfs - firstDrawSurf ); // draw main system development information (surface outlines, etc) R_DebugGraphics(); diff --git a/code/renderergl2/glsl/calclevels4x_fp.glsl b/code/renderergl2/glsl/calclevels4x_fp.glsl index 1de59e9f..8246c4b3 100644 --- a/code/renderergl2/glsl/calclevels4x_fp.glsl +++ b/code/renderergl2/glsl/calclevels4x_fp.glsl @@ -14,8 +14,8 @@ vec3 GetValues(vec2 offset, vec3 current) #ifdef FIRST_PASS - #if defined(r_framebufferGamma) - minAvgMax = pow(minAvgMax, vec3(r_framebufferGamma)); + #if defined(USE_PBR) + minAvgMax *= minAvgMax; #endif float lumi = max(dot(LUMINANCE_VECTOR, minAvgMax), 0.000001); diff --git a/code/renderergl2/glsl/lightall_fp.glsl b/code/renderergl2/glsl/lightall_fp.glsl index d1182781..6a0a5155 100644 --- a/code/renderergl2/glsl/lightall_fp.glsl +++ b/code/renderergl2/glsl/lightall_fp.glsl @@ -29,11 +29,6 @@ uniform samplerCube u_CubeMap; uniform vec4 u_EnableTextures; #endif -#if defined(USE_LIGHT_VECTOR) && !defined(USE_FAST_LIGHT) -uniform vec3 u_DirectedLight; -uniform vec3 u_AmbientLight; -#endif - #if defined(USE_PRIMARY_LIGHT) || defined(USE_SHADOWMAP) uniform vec3 u_PrimaryLightColor; uniform vec3 u_PrimaryLightAmbient; @@ -53,6 +48,9 @@ uniform vec4 u_CubeMapInfo; varying vec4 var_TexCoords; varying vec4 var_Color; +#if (defined(USE_LIGHT) && !defined(USE_FAST_LIGHT)) +varying vec4 var_ColorAmbient; +#endif #if (defined(USE_LIGHT) && !defined(USE_FAST_LIGHT)) #if defined(USE_VERT_TANGENT_SPACE) @@ -150,156 +148,35 @@ float RayIntersectDisplaceMap(vec2 dp, vec2 ds, sampler2D normalMap) } #endif -vec3 CalcDiffuse(vec3 diffuseAlbedo, vec3 N, vec3 L, vec3 E, float NE, float NL, float shininess) +vec3 CalcDiffuse(vec3 diffuseAlbedo, float NH, float EH, float roughness) { - #if defined(USE_OREN_NAYAR) || defined(USE_TRIACE_OREN_NAYAR) - float gamma = dot(E, L) - NE * NL; - float B = 2.22222 + 0.1 * shininess; - - #if defined(USE_OREN_NAYAR) - float A = 1.0 - 1.0 / (2.0 + 0.33 * shininess); - gamma = clamp(gamma, 0.0, 1.0); - #endif - - #if defined(USE_TRIACE_OREN_NAYAR) - float A = 1.0 - 1.0 / (2.0 + 0.65 * shininess); - - if (gamma >= 0.0) - #endif - { - B = max(B * max(NL, NE), EPSILON); - } - - return diffuseAlbedo * (A + gamma / B); - #else +#if defined(USE_BURLEY) + // modified from https://disney-animation.s3.amazonaws.com/library/s2012_pbs_disney_brdf_notes_v2.pdf + float fd90 = -0.5 + EH * EH * roughness; + float burley = 1.0 + fd90 * 0.04 / NH; + burley *= burley; + return diffuseAlbedo * burley; +#else return diffuseAlbedo; - #endif -} - -vec3 EnvironmentBRDF(float gloss, float NE, vec3 specular) -{ - #if 1 - // from http://blog.selfshadow.com/publications/s2013-shading-course/lazarov/s2013_pbs_black_ops_2_notes.pdf - vec4 t = vec4( 1.0/0.96, 0.475, (0.0275 - 0.25 * 0.04)/0.96,0.25 ) * gloss; - t += vec4( 0.0, 0.0, (0.015 - 0.75 * 0.04)/0.96,0.75 ); - float a0 = t.x * min( t.y, exp2( -9.28 * NE ) ) + t.z; - float a1 = t.w; - return clamp( a0 + specular * ( a1 - a0 ), 0.0, 1.0 ); - #elif 0 - // from http://seblagarde.wordpress.com/2011/08/17/hello-world/ - return specular + CalcFresnel(NE) * clamp(vec3(gloss) - specular, 0.0, 1.0); - #else - // from http://advances.realtimerendering.com/s2011/Lazarov-Physically-Based-Lighting-in-Black-Ops%20%28Siggraph%202011%20Advances%20in%20Real-Time%20Rendering%20Course%29.pptx - return mix(specular.rgb, vec3(1.0), CalcFresnel(NE) / (4.0 - 3.0 * gloss)); - #endif -} - -float CalcBlinn(float NH, float shininess) -{ -#if defined(USE_BLINN) || defined(USE_BLINN_FRESNEL) - // Normalized Blinn-Phong - float norm = shininess * 0.125 + 1.0; -#elif defined(USE_MCAULEY) - // Cook-Torrance as done by Stephen McAuley - // http://blog.selfshadow.com/publications/s2012-shading-course/mcauley/s2012_pbs_farcry3_notes_v2.pdf - float norm = shininess * 0.25 + 0.125; -#elif defined(USE_GOTANDA) - // Neumann-Neumann as done by Yoshiharu Gotanda - // http://research.tri-ace.com/Data/s2012_beyond_CourseNotes.pdf - float norm = shininess * 0.124858 + 0.269182; -#elif defined(USE_LAZAROV) - // Cook-Torrance as done by Dimitar Lazarov - // http://blog.selfshadow.com/publications/s2013-shading-course/lazarov/s2013_pbs_black_ops_2_notes.pdf - float norm = shininess * 0.125 + 0.25; -#else - float norm = 1.0; -#endif - -#if 0 - // from http://seblagarde.wordpress.com/2012/06/03/spherical-gaussien-approximation-for-blinn-phong-phong-and-fresnel/ - float a = shininess + 0.775; - return norm * exp(a * NH - a); -#else - return norm * pow(NH, shininess); #endif } -float CalcGGX(float NH, float gloss) +vec3 EnvironmentBRDF(float roughness, float NE, vec3 specular) { - // from http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf - float a_sq = exp2(gloss * -13.0 + 1.0); - float d = ((NH * NH) * (a_sq - 1.0) + 1.0); - return a_sq / (d * d); + // from http://community.arm.com/servlet/JiveServlet/download/96891546-19496/siggraph2015-mmg-renaldas-slides.pdf + float v = 1.0 - max(roughness, NE); + v *= v * v; + return vec3(v) + specular; } -float CalcFresnel(float EH) +vec3 CalcSpecular(vec3 specular, float NH, float EH, float roughness) { -#if 1 - // From http://blog.selfshadow.com/publications/s2013-shading-course/lazarov/s2013_pbs_black_ops_2_notes.pdf - // not accurate, but fast - return exp2(-10.0 * EH); -#elif 0 - // From http://seblagarde.wordpress.com/2012/06/03/spherical-gaussien-approximation-for-blinn-phong-phong-and-fresnel/ - return exp2((-5.55473 * EH - 6.98316) * EH); -#elif 0 - float blend = 1.0 - EH; - float blend2 = blend * blend; - blend *= blend2 * blend2; - - return blend; -#else - return pow(1.0 - EH, 5.0); -#endif -} - -float CalcVisibility(float NH, float NL, float NE, float EH, float gloss) -{ -#if defined(USE_GOTANDA) - // Neumann-Neumann as done by Yoshiharu Gotanda - // http://research.tri-ace.com/Data/s2012_beyond_CourseNotes.pdf - return 1.0 / max(max(NL, NE), EPSILON); -#elif defined(USE_LAZAROV) - // Cook-Torrance as done by Dimitar Lazarov - // http://blog.selfshadow.com/publications/s2013-shading-course/lazarov/s2013_pbs_black_ops_2_notes.pdf - float k = min(1.0, gloss + 0.545); - return 1.0 / (k * (EH * EH - 1.0) + 1.0); -#elif defined(USE_GGX) - float roughness = exp2(gloss * -6.5); - - // Modified from http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf - // NL, NE in numerator factored out from cook-torrance - float k = roughness + 1.0; - k *= k * 0.125; - - float k2 = 1.0 - k; - - float invGeo1 = NL * k2 + k; - float invGeo2 = NE * k2 + k; - - return 1.0 / (invGeo1 * invGeo2); -#else - return 1.0; -#endif -} - - -vec3 CalcSpecular(vec3 specular, float NH, float NL, float NE, float EH, float gloss, float shininess) -{ -#if defined(USE_GGX) - float distrib = CalcGGX(NH, gloss); -#else - float distrib = CalcBlinn(NH, shininess); -#endif - -#if defined(USE_BLINN) - vec3 fSpecular = specular; -#else - vec3 fSpecular = mix(specular, vec3(1.0), CalcFresnel(EH)); -#endif - - float vis = CalcVisibility(NH, NL, NE, EH, gloss); - - return fSpecular * (distrib * vis); + // from http://community.arm.com/servlet/JiveServlet/download/96891546-19496/siggraph2015-mmg-renaldas-slides.pdf + float rr = roughness*roughness; + float rrrr = rr*rr; + float d = (NH * NH) * (rrrr - 1.0) + 1.0; + float v = (EH * EH) * (roughness + 0.5); + return specular * (rrrr / (4.0 * d * d * v)); } @@ -341,7 +218,7 @@ mat3 cotangent_frame( vec3 N, vec3 p, vec2 uv ) void main() { - vec3 viewDir, lightColor, ambientColor; + vec3 viewDir, lightColor, ambientColor, reflectance; vec3 L, N, E, H; float NL, NH, NE, EH, attenuation; @@ -353,21 +230,20 @@ void main() mat3 tangentToWorld = cotangent_frame(var_Normal, -var_ViewDir, var_TexCoords.xy); viewDir = var_ViewDir; #endif - E = normalize(viewDir); - - L = var_LightDir.xyz; - #if defined(USE_DELUXEMAP) - L += (texture2D(u_DeluxeMap, var_TexCoords.zw).xyz - vec3(0.5)) * u_EnableTextures.y; - #endif - float sqrLightDist = dot(L, L); #endif + lightColor = var_Color.rgb; + #if defined(USE_LIGHTMAP) vec4 lightmapColor = texture2D(u_LightMap, var_TexCoords.zw); #if defined(RGBM_LIGHTMAP) lightmapColor.rgb *= lightmapColor.a; #endif + #if defined(USE_PBR) && !defined(USE_FAST_LIGHT) + lightmapColor.rgb *= lightmapColor.rgb; + #endif + lightColor *= lightmapColor.rgb; #endif vec2 texCoords = var_TexCoords.xy; @@ -383,17 +259,16 @@ void main() vec4 diffuse = texture2D(u_DiffuseMap, texCoords); #if defined(USE_LIGHT) && !defined(USE_FAST_LIGHT) - #if defined(USE_LIGHTMAP) - lightColor = lightmapColor.rgb * var_Color.rgb; - ambientColor = vec3(0.0); - attenuation = 1.0; - #elif defined(USE_LIGHT_VECTOR) - lightColor = u_DirectedLight * var_Color.rgb; - ambientColor = u_AmbientLight * var_Color.rgb; + L = var_LightDir.xyz; + #if defined(USE_DELUXEMAP) + L += (texture2D(u_DeluxeMap, var_TexCoords.zw).xyz - vec3(0.5)) * u_EnableTextures.y; + #endif + float sqrLightDist = dot(L, L); + L /= sqrt(sqrLightDist); + + #if defined(USE_LIGHT_VECTOR) attenuation = CalcLightAttenuation(float(var_LightDir.w > 0.0), var_LightDir.w / sqrLightDist); - #elif defined(USE_LIGHT_VERTEX) - lightColor = var_Color.rgb; - ambientColor = vec3(0.0); + #else attenuation = 1.0; #endif @@ -411,33 +286,20 @@ void main() #endif N = normalize(N); - L /= sqrt(sqrLightDist); #if defined(USE_SHADOWMAP) vec2 shadowTex = gl_FragCoord.xy * r_FBufScale; float shadowValue = texture2D(u_ShadowMap, shadowTex).r; // surfaces not facing the light are always shadowed - shadowValue *= float(dot(var_Normal.xyz, var_PrimaryLightDir.xyz) > 0.0); + shadowValue *= clamp(dot(var_Normal.xyz, var_PrimaryLightDir.xyz), 0.0, 1.0); #if defined(SHADOWMAP_MODULATE) - //vec3 shadowColor = min(u_PrimaryLightAmbient, lightColor); - vec3 shadowColor = u_PrimaryLightAmbient * lightColor; - - #if 0 - // Only shadow when the world light is parallel to the primary light - shadowValue = 1.0 + (shadowValue - 1.0) * clamp(dot(L, var_PrimaryLightDir.xyz), 0.0, 1.0); - #endif - lightColor = mix(shadowColor, lightColor, shadowValue); + lightColor *= shadowValue * (1.0 - u_PrimaryLightAmbient.r) + u_PrimaryLightAmbient.r; #endif #endif - #if defined(r_lightGamma) - lightColor = pow(lightColor, vec3(r_lightGamma)); - ambientColor = pow(ambientColor, vec3(r_lightGamma)); - #endif - - #if defined(USE_LIGHTMAP) || defined(USE_LIGHT_VERTEX) + #if !defined(USE_LIGHT_VECTOR) ambientColor = lightColor; float surfNL = clamp(dot(var_Normal.xyz, L), 0.0, 1.0); @@ -447,10 +309,10 @@ void main() // Recover any unused light as ambient, in case attenuation is over 4x or // light is below the surface - ambientColor = clamp(ambientColor - lightColor * surfNL, 0.0, 1.0); + ambientColor = max(ambientColor - lightColor * surfNL, vec3(0.0)); + #else + ambientColor = var_ColorAmbient.rgb; #endif - - vec3 reflectance; NL = clamp(dot(N, L), 0.0, 1.0); NE = clamp(dot(N, E), 0.0, 1.0); @@ -460,70 +322,36 @@ void main() #else vec4 specular = vec4(1.0); #endif - specular *= u_SpecularScale; - #if defined(r_materialGamma) - diffuse.rgb = pow(diffuse.rgb, vec3(r_materialGamma)); - specular.rgb = pow(specular.rgb, vec3(r_materialGamma)); + #if defined(USE_PBR) + diffuse.rgb *= diffuse.rgb; #endif - float gloss = specular.a; - float shininess = exp2(gloss * 13.0); - - #if defined(SPECULAR_IS_METALLIC) - // diffuse is actually base color, and red of specular is metallicness - float metallic = specular.r; - - specular.rgb = (0.96 * metallic) * diffuse.rgb + vec3(0.04); - diffuse.rgb *= 1.0 - metallic; + #if defined(USE_PBR) + // diffuse rgb is base color + // specular red is smoothness + // specular green is metallicness + float roughness = 1.0 - specular.r; + specular.rgb = specular.g * diffuse.rgb + vec3(0.04 - 0.04 * specular.g); + diffuse.rgb *= 1.0 - specular.g; #else + // diffuse rgb is diffuse + // specular rgb is specular reflectance at normal incidence + // specular alpha is gloss + float roughness = exp2(-3.0 * specular.a); + // adjust diffuse by specular reflectance, to maintain energy conservation diffuse.rgb *= vec3(1.0) - specular.rgb; #endif - reflectance = CalcDiffuse(diffuse.rgb, N, L, E, NE, NL, shininess); - - #if defined(r_deluxeSpecular) || defined(USE_LIGHT_VECTOR) - float adjGloss = gloss; - float adjShininess = shininess; - - #if !defined(USE_LIGHT_VECTOR) - adjGloss *= r_deluxeSpecular; - adjShininess = exp2(adjGloss * 13.0); - #endif - - H = normalize(L + E); - - EH = clamp(dot(E, H), 0.0, 1.0); - NH = clamp(dot(N, H), 0.0, 1.0); - - #if !defined(USE_LIGHT_VECTOR) - reflectance += CalcSpecular(specular.rgb, NH, NL, NE, EH, adjGloss, adjShininess) * r_deluxeSpecular; - #else - reflectance += CalcSpecular(specular.rgb, NH, NL, NE, EH, adjGloss, adjShininess); - #endif - #endif + reflectance = CalcDiffuse(diffuse.rgb, NH, EH, roughness); gl_FragColor.rgb = lightColor * reflectance * (attenuation * NL); - -#if 0 - vec3 aSpecular = EnvironmentBRDF(gloss, NE, specular.rgb); - - // do ambient as two hemisphere lights, one straight up one straight down - float hemiDiffuseUp = N.z * 0.5 + 0.5; - float hemiDiffuseDown = 1.0 - hemiDiffuseUp; - float hemiSpecularUp = mix(hemiDiffuseUp, float(N.z >= 0.0), gloss); - float hemiSpecularDown = 1.0 - hemiSpecularUp; - - gl_FragColor.rgb += ambientColor * 0.75 * (diffuse.rgb * hemiDiffuseUp + aSpecular * hemiSpecularUp); - gl_FragColor.rgb += ambientColor * 0.25 * (diffuse.rgb * hemiDiffuseDown + aSpecular * hemiSpecularDown); -#else - gl_FragColor.rgb += ambientColor * (diffuse.rgb + specular.rgb); -#endif + gl_FragColor.rgb += ambientColor * diffuse.rgb; #if defined(USE_CUBEMAP) - reflectance = EnvironmentBRDF(gloss, NE, specular.rgb); + reflectance = EnvironmentBRDF(roughness, NE, specular.rgb); vec3 R = reflect(E, N); @@ -531,15 +359,15 @@ void main() // from http://seblagarde.wordpress.com/2012/09/29/image-based-lighting-approaches-and-parallax-corrected-cubemap/ vec3 parallax = u_CubeMapInfo.xyz + u_CubeMapInfo.w * viewDir; - vec3 cubeLightColor = textureCubeLod(u_CubeMap, R + parallax, 7.0 - gloss * 7.0).rgb * u_EnableTextures.w; + vec3 cubeLightColor = textureCubeLod(u_CubeMap, R + parallax, 7.0 * roughness).rgb * u_EnableTextures.w; // normalize cubemap based on lowest mip (~diffuse) // multiplying cubemap values by lighting below depends on either this or the cubemap being normalized at generation //vec3 cubeLightDiffuse = max(textureCubeLod(u_CubeMap, N, 6.0).rgb, 0.5 / 255.0); //cubeLightColor /= dot(cubeLightDiffuse, vec3(0.2125, 0.7154, 0.0721)); - #if defined(r_framebufferGamma) - cubeLightColor = pow(cubeLightColor, vec3(r_framebufferGamma)); + #if defined(USE_PBR) + cubeLightColor *= cubeLightColor; #endif // multiply cubemap values by lighting @@ -549,7 +377,7 @@ void main() gl_FragColor.rgb += cubeLightColor * reflectance; #endif - #if defined(USE_PRIMARY_LIGHT) + #if defined(USE_PRIMARY_LIGHT) || defined(SHADOWMAP_MODULATE) vec3 L2, H2; float NL2, EH2, NH2; @@ -560,20 +388,19 @@ void main() //L2 /= sqrt(sqrLightDist); NL2 = clamp(dot(N, L2), 0.0, 1.0); - H2 = normalize(L2 + E); EH2 = clamp(dot(E, H2), 0.0, 1.0); NH2 = clamp(dot(N, H2), 0.0, 1.0); - reflectance = CalcDiffuse(diffuse.rgb, N, L2, E, NE, NL2, shininess); - reflectance += CalcSpecular(specular.rgb, NH2, NL2, NE, EH2, gloss, shininess); + reflectance = CalcSpecular(specular.rgb, NH2, EH2, roughness); - lightColor = u_PrimaryLightColor * var_Color.rgb; - - #if defined(r_lightGamma) - lightColor = pow(lightColor, vec3(r_lightGamma)); + // bit of a hack, with modulated shadowmaps, ignore diffuse + #if !defined(SHADOWMAP_MODULATE) + reflectance += CalcDiffuse(diffuse.rgb, NH2, EH2, roughness); #endif + lightColor = u_PrimaryLightColor; + #if defined(USE_SHADOWMAP) lightColor *= shadowValue; #endif @@ -583,28 +410,16 @@ void main() gl_FragColor.rgb += lightColor * reflectance * NL2; #endif + + #if defined(USE_PBR) + gl_FragColor.rgb = sqrt(gl_FragColor.rgb); + #endif + #else - lightColor = var_Color.rgb; - - #if defined(USE_LIGHTMAP) - lightColor *= lightmapColor.rgb; - #endif - - #if defined(r_lightGamma) - lightColor = pow(lightColor, vec3(r_lightGamma)); - #endif - - #if defined(r_materialGamma) - diffuse.rgb = pow(diffuse.rgb, vec3(r_materialGamma)); - #endif gl_FragColor.rgb = diffuse.rgb * lightColor; #endif -#if defined(r_framebufferGamma) - gl_FragColor.rgb = pow(gl_FragColor.rgb, vec3(1.0 / r_framebufferGamma)); -#endif - gl_FragColor.a = diffuse.a * var_Color.a; } diff --git a/code/renderergl2/glsl/lightall_vp.glsl b/code/renderergl2/glsl/lightall_vp.glsl index 59051d7c..783885e9 100644 --- a/code/renderergl2/glsl/lightall_vp.glsl +++ b/code/renderergl2/glsl/lightall_vp.glsl @@ -57,10 +57,8 @@ uniform float u_VertexLerp; #if defined(USE_LIGHT_VECTOR) uniform vec4 u_LightOrigin; uniform float u_LightRadius; - #if defined(USE_FAST_LIGHT) uniform vec3 u_DirectedLight; uniform vec3 u_AmbientLight; - #endif #endif #if defined(USE_PRIMARY_LIGHT) || defined(USE_SHADOWMAP) @@ -71,6 +69,9 @@ uniform float u_PrimaryLightRadius; varying vec4 var_TexCoords; varying vec4 var_Color; +#if defined(USE_LIGHT_VECTOR) && !defined(USE_FAST_LIGHT) +varying vec4 var_ColorAmbient; +#endif #if defined(USE_LIGHT) && !defined(USE_FAST_LIGHT) #if defined(USE_VERT_TANGENT_SPACE) @@ -208,12 +209,24 @@ void main() var_Color = u_VertColor * attr_Color + u_BaseColor; -#if defined(USE_LIGHT_VECTOR) && defined(USE_FAST_LIGHT) +#if defined(USE_LIGHT_VECTOR) + #if defined(USE_FAST_LIGHT) float sqrLightDist = dot(L, L); - float attenuation = CalcLightAttenuation(u_LightOrigin.w, u_LightRadius * u_LightRadius / sqrLightDist); float NL = clamp(dot(normalize(normal), L) / sqrt(sqrLightDist), 0.0, 1.0); + float attenuation = CalcLightAttenuation(u_LightOrigin.w, u_LightRadius * u_LightRadius / sqrLightDist); var_Color.rgb *= u_DirectedLight * (attenuation * NL) + u_AmbientLight; + #else + var_ColorAmbient.rgb = u_AmbientLight * var_Color.rgb; + var_Color.rgb *= u_DirectedLight; + #if defined(USE_PBR) + var_ColorAmbient.rgb *= var_ColorAmbient.rgb; + #endif + #endif +#endif + +#if defined(USE_LIGHT) && !defined(USE_FAST_LIGHT) && defined(USE_PBR) + var_Color.rgb *= var_Color.rgb; #endif #if defined(USE_PRIMARY_LIGHT) || defined(USE_SHADOWMAP) diff --git a/code/renderergl2/glsl/tonemap_fp.glsl b/code/renderergl2/glsl/tonemap_fp.glsl index 1368c5bd..9e24e24a 100644 --- a/code/renderergl2/glsl/tonemap_fp.glsl +++ b/code/renderergl2/glsl/tonemap_fp.glsl @@ -28,8 +28,8 @@ void main() { vec4 color = texture2D(u_TextureMap, var_TexCoords) * u_Color; -#if defined(r_framebufferGamma) - color.rgb = pow(color.rgb, vec3(r_framebufferGamma)); +#if defined(USE_PBR) + color.rgb *= color.rgb; #endif vec3 minAvgMax = texture2D(u_LevelsMap, var_TexCoords).rgb; @@ -46,9 +46,12 @@ void main() color.rgb = clamp(color.rgb * var_InvWhite, 0.0, 1.0); -#if defined(r_tonemapGamma) - color.rgb = pow(color.rgb, vec3(1.0 / r_tonemapGamma)); +#if defined(USE_PBR) + color.rgb = sqrt(color.rgb); #endif + // add a bit of dither to reduce banding + color.rgb += vec3(1.0/510.0 * mod(gl_FragCoord.x + gl_FragCoord.y, 2.0) - 1.0/1020.0); + gl_FragColor = color; } diff --git a/code/renderergl2/tr_backend.c b/code/renderergl2/tr_backend.c index c8c7200f..c02ba26f 100644 --- a/code/renderergl2/tr_backend.c +++ b/code/renderergl2/tr_backend.c @@ -20,6 +20,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #include "tr_local.h" +#include "tr_fbo.h" +#include "tr_dsa.h" backEndData_t *backEndData; backEndState_t backEnd; @@ -35,81 +37,28 @@ static float s_flipMatrix[16] = { }; -/* -** GL_Bind -*/ -void GL_Bind( image_t *image ) { - int texnum; - - if ( !image ) { - ri.Printf( PRINT_WARNING, "GL_Bind: NULL image\n" ); - texnum = tr.defaultImage->texnum; - } else { - texnum = image->texnum; - } - - if ( r_nobind->integer && tr.dlightImage ) { // performance evaluation option - texnum = tr.dlightImage->texnum; - } - - if ( glState.currenttextures[glState.currenttmu] != texnum ) { - if ( image ) { - image->frameUsed = tr.frameCount; - } - glState.currenttextures[glState.currenttmu] = texnum; - if (image && image->flags & IMGFLAG_CUBEMAP) - qglBindTexture( GL_TEXTURE_CUBE_MAP, texnum ); - else - qglBindTexture( GL_TEXTURE_2D, texnum ); - } -} - -/* -** GL_SelectTexture -*/ -void GL_SelectTexture( int unit ) -{ - if ( glState.currenttmu == unit ) - { - return; - } - - if (!(unit >= 0 && unit <= 31)) - ri.Error( ERR_DROP, "GL_SelectTexture: unit = %i", unit ); - - if (!qglActiveTextureARB) - ri.Error( ERR_DROP, "GL_SelectTexture: multitexture disabled" ); - - qglActiveTextureARB( GL_TEXTURE0_ARB + unit ); - - glState.currenttmu = unit; -} - /* ** GL_BindToTMU */ void GL_BindToTMU( image_t *image, int tmu ) { - int texnum; - int oldtmu = glState.currenttmu; + GLuint texture = (tmu == TB_COLORMAP) ? tr.defaultImage->texnum : 0; + GLenum target = GL_TEXTURE_2D; - if (!image) - texnum = 0; - else - texnum = image->texnum; + if (image) + { + if (image->flags & IMGFLAG_CUBEMAP) + target = GL_TEXTURE_CUBE_MAP; - if ( glState.currenttextures[tmu] != texnum ) { - GL_SelectTexture( tmu ); - if (image) - image->frameUsed = tr.frameCount; - glState.currenttextures[tmu] = texnum; - - if (image && (image->flags & IMGFLAG_CUBEMAP)) - qglBindTexture( GL_TEXTURE_CUBE_MAP, texnum ); - else - qglBindTexture( GL_TEXTURE_2D, texnum ); - GL_SelectTexture( oldtmu ); + image->frameUsed = tr.frameCount; + texture = image->texnum; } + else + { + ri.Printf(PRINT_WARNING, "GL_BindToTMU: NULL image\n"); + } + + GL_BindMultiTexture(GL_TEXTURE0_ARB + tmu, target, texture); } @@ -141,39 +90,6 @@ void GL_Cull( int cullType ) { glState.faceCulling = cullType; } -/* -** GL_TexEnv -*/ -void GL_TexEnv( int env ) -{ - if ( env == glState.texEnv[glState.currenttmu] ) - { - return; - } - - glState.texEnv[glState.currenttmu] = env; - - - switch ( env ) - { - case GL_MODULATE: - qglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); - break; - case GL_REPLACE: - qglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); - break; - case GL_DECAL: - qglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL ); - break; - case GL_ADD: - qglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD ); - break; - default: - ri.Error( ERR_DROP, "GL_TexEnv: invalid env '%d' passed", env ); - break; - } -} - /* ** GL_State ** @@ -462,30 +378,20 @@ void RB_BeginDrawingView (void) { if (glRefConfig.framebufferObject) { + FBO_t *fbo = backEnd.viewParms.targetFbo; + // FIXME: HUGE HACK: render to the screen fbo if we've already postprocessed the frame and aren't drawing more world // drawing more world check is in case of double renders, such as skyportals - if (backEnd.viewParms.targetFbo == NULL) - { - if (!tr.renderFbo || (backEnd.framePostProcessed && (backEnd.refdef.rdflags & RDF_NOWORLDMODEL))) - { - FBO_Bind(NULL); - } - else - { - FBO_Bind(tr.renderFbo); - } - } - else - { - FBO_Bind(backEnd.viewParms.targetFbo); + if (fbo == NULL && !(backEnd.framePostProcessed && (backEnd.refdef.rdflags & RDF_NOWORLDMODEL))) + fbo = tr.renderFbo; - // FIXME: hack for cubemap testing - if (tr.renderCubeFbo && backEnd.viewParms.targetFbo == tr.renderCubeFbo) - { - //qglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_CUBE_MAP_POSITIVE_X + backEnd.viewParms.targetFboLayer, backEnd.viewParms.targetFbo->colorImage[0]->texnum, 0); - qglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_CUBE_MAP_POSITIVE_X + backEnd.viewParms.targetFboLayer, tr.cubemaps[backEnd.viewParms.targetFboCubemapIndex]->texnum, 0); - } + if (tr.renderCubeFbo && fbo == tr.renderCubeFbo) + { + cubemap_t *cubemap = &tr.cubemaps[backEnd.viewParms.targetFboCubemapIndex]; + FBO_AttachImage(fbo, cubemap->image, GL_COLOR_ATTACHMENT0_EXT, backEnd.viewParms.targetFboLayer); } + + FBO_Bind(fbo); } // @@ -549,8 +455,6 @@ void RB_BeginDrawingView (void) { } -#define MAC_EVENT_PUMP_MSEC 5 - /* ================== RB_RenderDrawSurfList @@ -856,6 +760,7 @@ void RE_StretchRaw (int x, int y, int w, int h, int cols, int rows, const byte * } RE_UploadCinematic (w, h, cols, rows, data, client, dirty); + GL_BindToTMU(tr.scratchImage[client], TB_COLORMAP); if ( r_speeds->integer ) { end = ri.Milliseconds(); @@ -865,14 +770,7 @@ void RE_StretchRaw (int x, int y, int w, int h, int cols, int rows, const byte * // FIXME: HUGE hack if (glRefConfig.framebufferObject) { - if (!tr.renderFbo || backEnd.framePostProcessed) - { - FBO_Bind(NULL); - } - else - { - FBO_Bind(tr.renderFbo); - } + FBO_Bind(backEnd.framePostProcessed ? NULL : tr.renderFbo); } RB_SetGL2D(); @@ -896,23 +794,30 @@ void RE_StretchRaw (int x, int y, int w, int h, int cols, int rows, const byte * } void RE_UploadCinematic (int w, int h, int cols, int rows, const byte *data, int client, qboolean dirty) { + GLuint texture; - GL_Bind( tr.scratchImage[client] ); + if (!tr.scratchImage[client]) + { + ri.Printf(PRINT_WARNING, "RE_UploadCinematic: scratch images not initialized\n"); + return; + } + + texture = tr.scratchImage[client]->texnum; // if the scratchImage isn't in the format we want, specify it as a new texture if ( cols != tr.scratchImage[client]->width || rows != tr.scratchImage[client]->height ) { tr.scratchImage[client]->width = tr.scratchImage[client]->uploadWidth = cols; tr.scratchImage[client]->height = tr.scratchImage[client]->uploadHeight = rows; - qglTexImage2D( GL_TEXTURE_2D, 0, GL_RGB8, cols, rows, 0, GL_RGBA, GL_UNSIGNED_BYTE, data ); - qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); - qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); - qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); - qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + qglTextureImage2D(texture, GL_TEXTURE_2D, 0, GL_RGB8, cols, rows, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); + qglTextureParameterf(texture, GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + qglTextureParameterf(texture, GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + qglTextureParameterf(texture, GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + qglTextureParameterf(texture, GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } else { if (dirty) { // otherwise, just subimage upload it so that drivers can tell we are going to be changing // it and don't try and do a texture compression - qglTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, cols, rows, GL_RGBA, GL_UNSIGNED_BYTE, data ); + qglTextureSubImage2D(texture, GL_TEXTURE_2D, 0, 0, 0, cols, rows, GL_RGBA, GL_UNSIGNED_BYTE, data); } } } @@ -951,16 +856,7 @@ const void *RB_StretchPic ( const void *data ) { // FIXME: HUGE hack if (glRefConfig.framebufferObject) - { - if (!tr.renderFbo || backEnd.framePostProcessed) - { - FBO_Bind(NULL); - } - else - { - FBO_Bind(tr.renderFbo); - } - } + FBO_Bind(backEnd.framePostProcessed ? NULL : tr.renderFbo); RB_SetGL2D(); @@ -1072,11 +968,10 @@ const void *RB_DrawSurfs( const void *data ) { // If we're using multisampling, resolve the depth first FBO_FastBlit(tr.renderFbo, NULL, tr.msaaResolveFbo, NULL, GL_DEPTH_BUFFER_BIT, GL_NEAREST); } - else if (tr.renderFbo == NULL) + else if (tr.renderFbo == NULL && tr.renderDepthImage) { // If we're rendering directly to the screen, copy the depth to a texture - GL_BindToTMU(tr.renderDepthImage, 0); - qglCopyTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, 0, 0, glConfig.vidWidth, glConfig.vidHeight, 0); + qglCopyTextureImage2D(tr.renderDepthImage->texnum, GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, 0, 0, glConfig.vidWidth, glConfig.vidHeight, 0); } if (r_ssao->integer) @@ -1313,11 +1208,11 @@ const void *RB_DrawSurfs( const void *data ) { if (glRefConfig.framebufferObject && tr.renderCubeFbo && backEnd.viewParms.targetFbo == tr.renderCubeFbo) { + cubemap_t *cubemap = &tr.cubemaps[backEnd.viewParms.targetFboCubemapIndex]; + FBO_Bind(NULL); - GL_SelectTexture(TB_CUBEMAP); - GL_BindToTMU(tr.cubemaps[backEnd.viewParms.targetFboCubemapIndex], TB_CUBEMAP); - qglGenerateMipmapEXT(GL_TEXTURE_CUBE_MAP); - GL_SelectTexture(0); + if (cubemap && cubemap->image) + qglGenerateTextureMipmap(cubemap->image->texnum, GL_TEXTURE_CUBE_MAP); } return (const void *)(cmd + 1); @@ -1394,7 +1289,7 @@ void RB_ShowImages( void ) { { vec4_t quadVerts[4]; - GL_Bind(image); + GL_BindToTMU(image, TB_COLORMAP); VectorSet4(quadVerts[0], x, y, 0, 1); VectorSet4(quadVerts[1], x + w, y, 0, 1); @@ -1570,21 +1465,18 @@ const void *RB_CapShadowMap(const void *data) if (cmd->map != -1) { - GL_SelectTexture(0); if (cmd->cubeSide != -1) { if (tr.shadowCubemaps[cmd->map]) { - GL_Bind(tr.shadowCubemaps[cmd->map]); - qglCopyTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + cmd->cubeSide, 0, GL_RGBA8, backEnd.refdef.x, glConfig.vidHeight - ( backEnd.refdef.y + PSHADOW_MAP_SIZE ), PSHADOW_MAP_SIZE, PSHADOW_MAP_SIZE, 0); + qglCopyTextureImage2D(tr.shadowCubemaps[cmd->map]->texnum, GL_TEXTURE_CUBE_MAP_POSITIVE_X + cmd->cubeSide, 0, GL_RGBA8, backEnd.refdef.x, glConfig.vidHeight - ( backEnd.refdef.y + PSHADOW_MAP_SIZE ), PSHADOW_MAP_SIZE, PSHADOW_MAP_SIZE, 0); } } else { if (tr.pshadowMaps[cmd->map]) { - GL_Bind(tr.pshadowMaps[cmd->map]); - qglCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, backEnd.refdef.x, glConfig.vidHeight - ( backEnd.refdef.y + PSHADOW_MAP_SIZE ), PSHADOW_MAP_SIZE, PSHADOW_MAP_SIZE, 0); + qglCopyTextureImage2D(tr.pshadowMaps[cmd->map]->texnum, GL_TEXTURE_2D, 0, GL_RGBA8, backEnd.refdef.x, glConfig.vidHeight - (backEnd.refdef.y + PSHADOW_MAP_SIZE), PSHADOW_MAP_SIZE, PSHADOW_MAP_SIZE, 0); } } } @@ -1657,7 +1549,7 @@ const void *RB_PostProcess(const void *data) if (srcFbo) { - if (r_hdr->integer && (r_toneMap->integer || r_forceToneMap->integer) && qglActiveTextureARB) + if (r_hdr->integer && (r_toneMap->integer || r_forceToneMap->integer)) { autoExposure = r_autoExposure->integer || r_forceAutoExposure->integer; RB_ToneMap(srcFbo, srcBox, NULL, dstBox, autoExposure); @@ -1726,7 +1618,7 @@ const void *RB_PostProcess(const void *data) { VectorSet4(dstBox, 0, glConfig.vidHeight - 256, 256, 256); //FBO_BlitFromTexture(tr.renderCubeImage, NULL, NULL, NULL, dstBox, &tr.testcubeShader, NULL, 0); - FBO_BlitFromTexture(tr.cubemaps[cubemapIndex - 1], NULL, NULL, NULL, dstBox, &tr.testcubeShader, NULL, 0); + FBO_BlitFromTexture(tr.cubemaps[cubemapIndex - 1].image, NULL, NULL, NULL, dstBox, &tr.testcubeShader, NULL, 0); } } #endif @@ -1736,6 +1628,74 @@ const void *RB_PostProcess(const void *data) return (const void *)(cmd + 1); } +// FIXME: put this function declaration elsewhere +void R_SaveDDS(const char *filename, byte *pic, int width, int height, int depth); + +/* +============= +RB_ExportCubemaps + +============= +*/ +const void *RB_ExportCubemaps(const void *data) +{ + const exportCubemapsCommand_t *cmd = data; + + // finish any 2D drawing if needed + if (tess.numIndexes) + RB_EndSurface(); + + if (!glRefConfig.framebufferObject || !tr.world || tr.numCubemaps == 0) + { + // do nothing + ri.Printf(PRINT_ALL, "Nothing to export!\n"); + return (const void *)(cmd + 1); + } + + if (cmd) + { + FBO_t *oldFbo = glState.currentFBO; + int sideSize = r_cubemapSize->integer * r_cubemapSize->integer * 4; + byte *cubemapPixels = ri.Malloc(sideSize * 6); + int i, j; + + FBO_Bind(tr.renderCubeFbo); + + for (i = 0; i < tr.numCubemaps; i++) + { + char filename[MAX_QPATH]; + cubemap_t *cubemap = &tr.cubemaps[i]; + byte *p = cubemapPixels; + + for (j = 0; j < 6; j++) + { + FBO_AttachImage(tr.renderCubeFbo, cubemap->image, GL_COLOR_ATTACHMENT0_EXT, j); + qglReadPixels(0, 0, r_cubemapSize->integer, r_cubemapSize->integer, GL_RGBA, GL_UNSIGNED_BYTE, p); + p += sideSize; + } + + if (cubemap->name[0]) + { + COM_StripExtension(cubemap->name, filename, MAX_QPATH); + Q_strcat(filename, MAX_QPATH, ".dds"); + } + else + { + Com_sprintf(filename, MAX_QPATH, "cubemaps/%s/%03d.dds", tr.world->baseName, i); + } + + R_SaveDDS(filename, cubemapPixels, r_cubemapSize->integer, r_cubemapSize->integer, 6); + ri.Printf(PRINT_ALL, "Saved cubemap %d as %s\n", i, filename); + } + + FBO_Bind(oldFbo); + + ri.Free(cubemapPixels); + } + + return (const void *)(cmd + 1); +} + /* ==================== @@ -1784,6 +1744,9 @@ void RB_ExecuteRenderCommands( const void *data ) { case RC_POSTPROCESS: data = RB_PostProcess(data); break; + case RC_EXPORT_CUBEMAPS: + data = RB_ExportCubemaps(data); + break; case RC_END_OF_LIST: default: // finish any 2D drawing if needed diff --git a/code/renderergl2/tr_bsp.c b/code/renderergl2/tr_bsp.c index cf4ddca0..78728f81 100644 --- a/code/renderergl2/tr_bsp.c +++ b/code/renderergl2/tr_bsp.c @@ -23,6 +23,10 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include "tr_local.h" +#define JSON_IMPLEMENTATION +#include "../qcommon/json.h" +#undef JSON_IMPLEMENTATION + /* Loads and prepares a map file for scene rendering. @@ -101,7 +105,11 @@ static void R_ColorShiftLightingBytes( byte in[4], byte out[4] ) { int shift, r, g, b; // shift the color data based on overbright range +#if defined(USE_OVERBRIGHT) shift = r_mapOverBrightBits->integer - tr.overbrightBits; +#else + shift = 0; +#endif // shift the data based on overbright range r = in[0] << shift; @@ -136,7 +144,9 @@ static void R_ColorShiftLightingFloats(float in[4], float out[4], float scale ) { float r, g, b; - scale *= pow(2.0f, r_mapOverBrightBits->integer - tr.overbrightBits); +#if defined(USE_OVERBRIGHT) + scale *= 1 << (r_mapOverBrightBits->integer - tr.overbrightBits); +#endif r = in[0] * scale; g = in[1] * scale; @@ -2755,7 +2765,11 @@ void R_LoadLightGrid( lump_t *l ) { if (hdrLightGrid) { - float lightScale = pow(2, r_mapOverBrightBits->integer - tr.overbrightBits); +#if defined(USE_OVERBRIGHT) + float lightScale = 1 << (r_mapOverBrightBits->integer - tr.overbrightBits); +#else + float lightScale = 1.0f; +#endif //ri.Printf(PRINT_ALL, "found!\n"); @@ -2962,6 +2976,78 @@ qboolean R_ParseSpawnVars( char *spawnVarChars, int maxSpawnVarChars, int *numSp return qtrue; } +void R_LoadEnvironmentJson(const char *baseName) +{ + char filename[MAX_QPATH]; + + union { + char *c; + void *v; + } buffer; + char *bufferEnd; + + const char *cubemapArrayJson; + int filelen, i; + + Com_sprintf(filename, MAX_QPATH, "cubemaps/%s/env.json", baseName); + + filelen = ri.FS_ReadFile(filename, &buffer.v); + if (!buffer.c) + return; + bufferEnd = buffer.c + filelen; + + if (JSON_ValueGetType(buffer.c, bufferEnd) != JSONTYPE_OBJECT) + { + ri.Printf(PRINT_ALL, "Bad %s: does not start with a object\n", filename); + ri.FS_FreeFile(buffer.v); + return; + } + + cubemapArrayJson = JSON_ObjectGetNamedValue(buffer.c, bufferEnd, "Cubemaps"); + if (!cubemapArrayJson) + { + ri.Printf(PRINT_ALL, "Bad %s: no Cubemaps\n", filename); + ri.FS_FreeFile(buffer.v); + return; + } + + if (JSON_ValueGetType(cubemapArrayJson, bufferEnd) != JSONTYPE_ARRAY) + { + ri.Printf(PRINT_ALL, "Bad %s: Cubemaps not an array\n", filename); + ri.FS_FreeFile(buffer.v); + return; + } + + tr.numCubemaps = JSON_ArrayGetIndex(cubemapArrayJson, bufferEnd, NULL, 0); + tr.cubemaps = ri.Hunk_Alloc(tr.numCubemaps * sizeof(*tr.cubemaps), h_low); + memset(tr.cubemaps, 0, tr.numCubemaps * sizeof(*tr.cubemaps)); + + for (i = 0; i < tr.numCubemaps; i++) + { + cubemap_t *cubemap = &tr.cubemaps[i]; + const char *cubemapJson, *keyValueJson, *indexes[3]; + int j; + + cubemapJson = JSON_ArrayGetValue(cubemapArrayJson, bufferEnd, i); + + keyValueJson = JSON_ObjectGetNamedValue(cubemapJson, bufferEnd, "Name"); + if (!JSON_ValueGetString(keyValueJson, bufferEnd, cubemap->name, MAX_QPATH)) + cubemap->name[0] = '\0'; + + keyValueJson = JSON_ObjectGetNamedValue(cubemapJson, bufferEnd, "Position"); + JSON_ArrayGetIndex(keyValueJson, bufferEnd, indexes, 3); + for (j = 0; j < 3; j++) + cubemap->origin[j] = JSON_ValueGetFloat(indexes[j], bufferEnd); + + cubemap->parallaxRadius = 1000.0f; + keyValueJson = JSON_ObjectGetNamedValue(cubemapJson, bufferEnd, "Radius"); + if (keyValueJson) + cubemap->parallaxRadius = JSON_ValueGetFloat(keyValueJson, bufferEnd); + } + + ri.FS_FreeFile(buffer.v); +} + void R_LoadCubemapEntities(char *cubemapEntityName) { char spawnVarChars[2048]; @@ -2986,33 +3072,45 @@ void R_LoadCubemapEntities(char *cubemapEntityName) return; tr.numCubemaps = numCubemaps; - tr.cubemapOrigins = ri.Hunk_Alloc( tr.numCubemaps * sizeof(*tr.cubemapOrigins), h_low); - tr.cubemaps = ri.Hunk_Alloc( tr.numCubemaps * sizeof(*tr.cubemaps), h_low); + tr.cubemaps = ri.Hunk_Alloc(tr.numCubemaps * sizeof(*tr.cubemaps), h_low); + memset(tr.cubemaps, 0, tr.numCubemaps * sizeof(*tr.cubemaps)); numCubemaps = 0; while(R_ParseSpawnVars(spawnVarChars, sizeof(spawnVarChars), &numSpawnVars, spawnVars)) { int i; + char name[MAX_QPATH]; qboolean isCubemap = qfalse; - qboolean positionSet = qfalse; + qboolean originSet = qfalse; vec3_t origin; + float parallaxRadius = 1000.0f; + name[0] = '\0'; for (i = 0; i < numSpawnVars; i++) { if (!Q_stricmp(spawnVars[i][0], "classname") && !Q_stricmp(spawnVars[i][1], cubemapEntityName)) isCubemap = qtrue; + if (!Q_stricmp(spawnVars[i][0], "name")) + Q_strncpyz(name, spawnVars[i][1], MAX_QPATH); + if (!Q_stricmp(spawnVars[i][0], "origin")) { sscanf(spawnVars[i][1], "%f %f %f", &origin[0], &origin[1], &origin[2]); - positionSet = qtrue; + originSet = qtrue; + } + else if (!Q_stricmp(spawnVars[i][0], "radius")) + { + sscanf(spawnVars[i][1], "%f", ¶llaxRadius); } } - if (isCubemap && positionSet) + if (isCubemap && originSet) { - //ri.Printf(PRINT_ALL, "cubemap at %f %f %f\n", origin[0], origin[1], origin[2]); - VectorCopy(origin, tr.cubemapOrigins[numCubemaps]); + cubemap_t *cubemap = &tr.cubemaps[numCubemaps]; + Q_strncpyz(cubemap->name, name, MAX_QPATH); + VectorCopy(origin, cubemap->origin); + cubemap->parallaxRadius = parallaxRadius; numCubemaps++; } } @@ -3052,23 +3150,41 @@ void R_AssignCubemapsToWorldSurfaces(void) } -void R_RenderAllCubemaps(void) +void R_LoadCubemaps(void) { - int i, j; + int i; + imgFlags_t flags = IMGFLAG_CLAMPTOEDGE | IMGFLAG_MIPMAP | IMGFLAG_NOLIGHTSCALE | IMGFLAG_CUBEMAP; for (i = 0; i < tr.numCubemaps; i++) { - tr.cubemaps[i] = R_CreateImage(va("*cubeMap%d", i), NULL, CUBE_MAP_SIZE, CUBE_MAP_SIZE, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE | IMGFLAG_MIPMAP | IMGFLAG_CUBEMAP, GL_RGBA8); + char filename[MAX_QPATH]; + cubemap_t *cubemap = &tr.cubemaps[i]; + + Com_sprintf(filename, MAX_QPATH, "cubemaps/%s/%03d.dds", tr.world->baseName, i); + + cubemap->image = R_FindImageFile(filename, IMGTYPE_COLORALPHA, flags); } - +} + + +void R_RenderMissingCubemaps(void) +{ + int i, j; + imgFlags_t flags = IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE | IMGFLAG_MIPMAP | IMGFLAG_NOLIGHTSCALE | IMGFLAG_CUBEMAP; + for (i = 0; i < tr.numCubemaps; i++) { - for (j = 0; j < 6; j++) + if (!tr.cubemaps[i].image) { - RE_ClearScene(); - R_RenderCubemapSide(i, j, qfalse); - R_IssuePendingRenderCommands(); - R_InitNextFrame(); + tr.cubemaps[i].image = R_CreateImage(va("*cubeMap%d", i), NULL, r_cubemapSize->integer, r_cubemapSize->integer, IMGTYPE_COLORALPHA, flags, GL_RGBA8); + + for (j = 0; j < 6; j++) + { + RE_ClearScene(); + R_RenderCubemapSide(i, j, qfalse); + R_IssuePendingRenderCommands(); + R_InitNextFrame(); + } } } } @@ -3385,7 +3501,14 @@ void RE_LoadWorldMap( const char *name ) { // load cubemaps if (r_cubeMapping->integer) { - R_LoadCubemapEntities("misc_cubemap"); + // Try loading an env.json file first + R_LoadEnvironmentJson(s_worldData.baseName); + + if (!tr.numCubemaps) + { + R_LoadCubemapEntities("misc_cubemap"); + } + if (!tr.numCubemaps) { // use deathmatch spawn points as cubemaps @@ -3409,10 +3532,11 @@ void RE_LoadWorldMap( const char *name ) { // make sure the VAO glState entry is safe R_BindNullVao(); - // Render all cubemaps + // Render or load all cubemaps if (r_cubeMapping->integer && tr.numCubemaps) { - R_RenderAllCubemaps(); + R_LoadCubemaps(); + R_RenderMissingCubemaps(); } ri.FS_FreeFile( buffer.v ); diff --git a/code/renderergl2/tr_cmds.c b/code/renderergl2/tr_cmds.c index 18fd69c3..254bb0d7 100644 --- a/code/renderergl2/tr_cmds.c +++ b/code/renderergl2/tr_cmds.c @@ -121,20 +121,20 @@ void R_IssuePendingRenderCommands( void ) { /* ============ -R_GetCommandBuffer +R_GetCommandBufferReserved make sure there is enough command space ============ */ -void *R_GetCommandBuffer( int bytes ) { +void *R_GetCommandBufferReserved( int bytes, int reservedBytes ) { renderCommandList_t *cmdList; cmdList = &backEndData->commands; bytes = PAD(bytes, sizeof(void *)); // always leave room for the end of list command - if ( cmdList->used + bytes + 4 > MAX_RENDER_COMMANDS ) { - if ( bytes > MAX_RENDER_COMMANDS - 4 ) { + if ( cmdList->used + bytes + sizeof( int ) + reservedBytes > MAX_RENDER_COMMANDS ) { + if ( bytes > MAX_RENDER_COMMANDS - sizeof( int ) ) { ri.Error( ERR_FATAL, "R_GetCommandBuffer: bad size %i", bytes ); } // if we run out of room, just start dropping commands @@ -146,6 +146,17 @@ void *R_GetCommandBuffer( int bytes ) { return cmdList->cmds + cmdList->used - bytes; } +/* +============= +R_GetCommandBuffer + +returns NULL if there is not enough space for important commands +============= +*/ +void *R_GetCommandBuffer( int bytes ) { + return R_GetCommandBufferReserved( bytes, PAD( sizeof( swapBuffersCommand_t ), sizeof(void *) ) ); +} + /* ============= @@ -525,7 +536,7 @@ void RE_EndFrame( int *frontEndMsec, int *backEndMsec ) { if ( !tr.registered ) { return; } - cmd = R_GetCommandBuffer( sizeof( *cmd ) ); + cmd = R_GetCommandBufferReserved( sizeof( *cmd ), 0 ); if ( !cmd ) { return; } diff --git a/code/renderergl2/tr_dsa.c b/code/renderergl2/tr_dsa.c new file mode 100644 index 00000000..9cd481bd --- /dev/null +++ b/code/renderergl2/tr_dsa.c @@ -0,0 +1,287 @@ +/* +=========================================================================== +Copyright (C) 2016 James Canete + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +=========================================================================== +*/ + +#include "tr_local.h" + +#include "tr_dsa.h" + +static struct +{ + GLuint textures[NUM_TEXTURE_BUNDLES]; + GLenum texunit; + + GLuint program; + + GLuint drawFramebuffer; + GLuint readFramebuffer; + GLuint renderbuffer; +} +glDsaState; + +void GL_BindNullTextures() +{ + int i; + + if (glRefConfig.directStateAccess) + { + for (i = 0; i < NUM_TEXTURE_BUNDLES; i++) + { + qglBindMultiTexture(GL_TEXTURE0_ARB + i, GL_TEXTURE_2D, 0); + glDsaState.textures[i] = 0; + } + } + else + { + for (i = 0; i < NUM_TEXTURE_BUNDLES; i++) + { + qglActiveTextureARB(GL_TEXTURE0_ARB + i); + qglBindTexture(GL_TEXTURE_2D, 0); + glDsaState.textures[i] = 0; + } + + qglActiveTextureARB(GL_TEXTURE0_ARB); + glDsaState.texunit = GL_TEXTURE0_ARB; + } +} + +int GL_BindMultiTexture(GLenum texunit, GLenum target, GLuint texture) +{ + GLuint tmu = texunit - GL_TEXTURE0_ARB; + + if (glDsaState.textures[tmu] == texture) + return 0; + + if (target >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && target <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z) + target = GL_TEXTURE_CUBE_MAP; + + qglBindMultiTexture(texunit, target, texture); + glDsaState.textures[tmu] = texture; + return 1; +} + +GLvoid APIENTRY GLDSA_BindMultiTexture(GLenum texunit, GLenum target, GLuint texture) +{ + if (glDsaState.texunit != texunit) + { + qglActiveTextureARB(texunit); + glDsaState.texunit = texunit; + } + + qglBindTexture(target, texture); +} + +GLvoid APIENTRY GLDSA_TextureParameterf(GLuint texture, GLenum target, GLenum pname, GLfloat param) +{ + GL_BindMultiTexture(glDsaState.texunit, target, texture); + qglTexParameterf(target, pname, param); +} + +GLvoid APIENTRY GLDSA_TextureParameteri(GLuint texture, GLenum target, GLenum pname, GLint param) +{ + GL_BindMultiTexture(glDsaState.texunit, target, texture); + qglTexParameteri(target, pname, param); +} + +GLvoid APIENTRY GLDSA_TextureImage2D(GLuint texture, GLenum target, GLint level, GLint internalformat, + GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels) +{ + GL_BindMultiTexture(glDsaState.texunit, target, texture); + qglTexImage2D(target, level, internalformat, width, height, border, format, type, pixels); +} + +GLvoid APIENTRY GLDSA_TextureSubImage2D(GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, + GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels) +{ + GL_BindMultiTexture(glDsaState.texunit, target, texture); + qglTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels); +} + +GLvoid APIENTRY GLDSA_CopyTextureImage2D(GLuint texture, GLenum target, GLint level, GLenum internalformat, + GLint x, GLint y, GLsizei width, GLsizei height, GLint border) +{ + GL_BindMultiTexture(glDsaState.texunit, target, texture); + qglCopyTexImage2D(target, level, internalformat, x, y, width, height, border); +} + +GLvoid APIENTRY GLDSA_CompressedTextureImage2D(GLuint texture, GLenum target, GLint level, GLenum internalformat, + GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *data) +{ + GL_BindMultiTexture(glDsaState.texunit, target, texture); + qglCompressedTexImage2DARB(target, level, internalformat, width, height, border, imageSize, data); +} + +GLvoid APIENTRY GLDSA_CompressedTextureSubImage2D(GLuint texture, GLenum target, GLint level, + GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, + GLsizei imageSize, const GLvoid *data) +{ + GL_BindMultiTexture(glDsaState.texunit, target, texture); + qglCompressedTexSubImage2DARB(target, level, xoffset, yoffset, width, height, format, imageSize, data); +} + +GLvoid APIENTRY GLDSA_GenerateTextureMipmap(GLuint texture, GLenum target) +{ + GL_BindMultiTexture(glDsaState.texunit, target, texture); + qglGenerateMipmapEXT(target); +} + +void GL_BindNullProgram() +{ + qglUseProgramObjectARB(0); + glDsaState.program = 0; +} + +int GL_UseProgramObject(GLuint program) +{ + if (glDsaState.program == program) + return 0; + + qglUseProgramObjectARB(program); + glDsaState.program = program; + return 1; +} + +GLvoid APIENTRY GLDSA_ProgramUniform1i(GLuint program, GLint location, GLint v0) +{ + GL_UseProgramObject(program); + qglUniform1iARB(location, v0); +} + +GLvoid APIENTRY GLDSA_ProgramUniform1f(GLuint program, GLint location, GLfloat v0) +{ + GL_UseProgramObject(program); + qglUniform1fARB(location, v0); +} + +GLvoid APIENTRY GLDSA_ProgramUniform2f(GLuint program, GLint location, + GLfloat v0, GLfloat v1) +{ + GL_UseProgramObject(program); + qglUniform2fARB(location, v0, v1); +} + +GLvoid APIENTRY GLDSA_ProgramUniform3f(GLuint program, GLint location, + GLfloat v0, GLfloat v1, GLfloat v2) +{ + GL_UseProgramObject(program); + qglUniform3fARB(location, v0, v1, v2); +} + +GLvoid APIENTRY GLDSA_ProgramUniform4f(GLuint program, GLint location, + GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3) +{ + GL_UseProgramObject(program); + qglUniform4fARB(location, v0, v1, v2, v3); +} + +GLvoid APIENTRY GLDSA_ProgramUniform1fv(GLuint program, GLint location, + GLsizei count, const GLfloat *value) +{ + GL_UseProgramObject(program); + qglUniform1fvARB(location, count, value); +} + +GLvoid APIENTRY GLDSA_ProgramUniformMatrix4fv(GLuint program, GLint location, + GLsizei count, GLboolean transpose, + const GLfloat *value) +{ + GL_UseProgramObject(program); + qglUniformMatrix4fvARB(location, count, transpose, value); +} + +void GL_BindNullFramebuffers() +{ + qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + glDsaState.drawFramebuffer = glDsaState.readFramebuffer = 0; + qglBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); + glDsaState.renderbuffer = 0; +} + +void GL_BindFramebuffer(GLenum target, GLuint framebuffer) +{ + switch (target) + { + case GL_FRAMEBUFFER_EXT: + if (framebuffer != glDsaState.drawFramebuffer || framebuffer != glDsaState.readFramebuffer) + { + qglBindFramebufferEXT(target, framebuffer); + glDsaState.drawFramebuffer = glDsaState.readFramebuffer = framebuffer; + } + break; + + case GL_DRAW_FRAMEBUFFER_EXT: + if (framebuffer != glDsaState.drawFramebuffer) + { + qglBindFramebufferEXT(target, framebuffer); + glDsaState.drawFramebuffer = framebuffer; + } + break; + + case GL_READ_FRAMEBUFFER_EXT: + if (framebuffer != glDsaState.readFramebuffer) + { + qglBindFramebufferEXT(target, framebuffer); + glDsaState.readFramebuffer = framebuffer; + } + break; + } +} + +void GL_BindRenderbuffer(GLuint renderbuffer) +{ + if (renderbuffer != glDsaState.renderbuffer) + { + qglBindRenderbufferEXT(GL_RENDERBUFFER_EXT, renderbuffer); + glDsaState.renderbuffer = renderbuffer; + } +} + +GLvoid APIENTRY GLDSA_NamedRenderbufferStorage(GLuint renderbuffer, + GLenum internalformat, GLsizei width, GLsizei height) +{ + GL_BindRenderbuffer(renderbuffer); + qglRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, internalformat, width, height); +} + +GLvoid APIENTRY GLDSA_NamedRenderbufferStorageMultisample(GLuint renderbuffer, + GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) +{ + GL_BindRenderbuffer(renderbuffer); + qglRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, samples, internalformat, width, height); +} + +GLenum APIENTRY GLDSA_CheckNamedFramebufferStatus(GLuint framebuffer, GLenum target) +{ + GL_BindFramebuffer(target, framebuffer); + return qglCheckFramebufferStatusEXT(target); +} + +GLvoid APIENTRY GLDSA_NamedFramebufferTexture2D(GLuint framebuffer, + GLenum attachment, GLenum textarget, GLuint texture, GLint level) +{ + GL_BindFramebuffer(GL_FRAMEBUFFER_EXT, framebuffer); + qglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, attachment, textarget, texture, level); +} + +GLvoid APIENTRY GLDSA_NamedFramebufferRenderbuffer(GLuint framebuffer, + GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) +{ + GL_BindFramebuffer(GL_FRAMEBUFFER_EXT, framebuffer); + qglFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, attachment, renderbuffertarget, renderbuffer); +} diff --git a/code/renderergl2/tr_dsa.h b/code/renderergl2/tr_dsa.h new file mode 100644 index 00000000..e820fe00 --- /dev/null +++ b/code/renderergl2/tr_dsa.h @@ -0,0 +1,80 @@ +/* +=========================================================================== +Copyright (C) 2016 James Canete + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +=========================================================================== +*/ + +#ifndef __TR_DSA_H__ +#define __TR_DSA_H__ + +#include "../renderercommon/qgl.h" + +void GL_BindNullTextures(void); +int GL_BindMultiTexture(GLenum texunit, GLenum target, GLuint texture); + +GLvoid APIENTRY GLDSA_BindMultiTexture(GLenum texunit, GLenum target, GLuint texture); +GLvoid APIENTRY GLDSA_TextureParameterf(GLuint texture, GLenum target, GLenum pname, GLfloat param); +GLvoid APIENTRY GLDSA_TextureParameteri(GLuint texture, GLenum target, GLenum pname, GLint param); +GLvoid APIENTRY GLDSA_TextureImage2D(GLuint texture, GLenum target, GLint level, GLint internalformat, + GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels); +GLvoid APIENTRY GLDSA_TextureSubImage2D(GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, + GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels); +GLvoid APIENTRY GLDSA_CopyTextureImage2D(GLuint texture, GLenum target, GLint level, GLenum internalformat, + GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +GLvoid APIENTRY GLDSA_CompressedTextureImage2D(GLuint texture, GLenum target, GLint level, GLenum internalformat, + GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *data); +GLvoid APIENTRY GLDSA_CompressedTextureSubImage2D(GLuint texture, GLenum target, GLint level, + GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, + GLsizei imageSize, const GLvoid *data); + +GLvoid APIENTRY GLDSA_GenerateTextureMipmap(GLuint texture, GLenum target); + +void GL_BindNullProgram(void); +int GL_UseProgramObject(GLuint program); + +GLvoid APIENTRY GLDSA_ProgramUniform1i(GLuint program, GLint location, GLint v0); +GLvoid APIENTRY GLDSA_ProgramUniform1f(GLuint program, GLint location, GLfloat v0); +GLvoid APIENTRY GLDSA_ProgramUniform2f(GLuint program, GLint location, + GLfloat v0, GLfloat v1); +GLvoid APIENTRY GLDSA_ProgramUniform3f(GLuint program, GLint location, + GLfloat v0, GLfloat v1, GLfloat v2); +GLvoid APIENTRY GLDSA_ProgramUniform4f(GLuint program, GLint location, + GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +GLvoid APIENTRY GLDSA_ProgramUniform1fv(GLuint program, GLint location, + GLsizei count, const GLfloat *value); +GLvoid APIENTRY GLDSA_ProgramUniformMatrix4fv(GLuint program, GLint location, + GLsizei count, GLboolean transpose, + const GLfloat *value); + +void GL_BindNullFramebuffers(void); +void GL_BindFramebuffer(GLenum target, GLuint framebuffer); +void GL_BindRenderbuffer(GLuint renderbuffer); + +GLvoid APIENTRY GLDSA_NamedRenderbufferStorage(GLuint renderbuffer, + GLenum internalformat, GLsizei width, GLsizei height); + +GLvoid APIENTRY GLDSA_NamedRenderbufferStorageMultisample(GLuint renderbuffer, + GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); + +GLenum APIENTRY GLDSA_CheckNamedFramebufferStatus(GLuint framebuffer, GLenum target); +GLvoid APIENTRY GLDSA_NamedFramebufferTexture2D(GLuint framebuffer, + GLenum attachment, GLenum textarget, GLuint texture, GLint level); +GLvoid APIENTRY GLDSA_NamedFramebufferRenderbuffer(GLuint framebuffer, + GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); + + +#endif diff --git a/code/renderergl2/tr_extensions.c b/code/renderergl2/tr_extensions.c index 357d4795..b33968d4 100644 --- a/code/renderergl2/tr_extensions.c +++ b/code/renderergl2/tr_extensions.c @@ -28,6 +28,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #endif #include "tr_local.h" +#include "tr_dsa.h" // GL_EXT_draw_range_elements void (APIENTRY * qglDrawRangeElementsEXT) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices); @@ -184,6 +185,50 @@ void (APIENTRY * qglDeleteVertexArraysARB)(GLsizei n, const GLuint *arrays); void (APIENTRY * qglGenVertexArraysARB)(GLsizei n, GLuint *arrays); GLboolean (APIENTRY * qglIsVertexArrayARB)(GLuint array); +// GL_EXT_direct_state_access +GLvoid (APIENTRY * qglBindMultiTexture)(GLenum texunit, GLenum target, GLuint texture); +GLvoid (APIENTRY * qglTextureParameterf)(GLuint texture, GLenum target, GLenum pname, GLfloat param); +GLvoid (APIENTRY * qglTextureParameteri)(GLuint texture, GLenum target, GLenum pname, GLint param); +GLvoid (APIENTRY * qglTextureImage2D)(GLuint texture, GLenum target, GLint level, GLint internalformat, + GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels); +GLvoid (APIENTRY * qglTextureSubImage2D)(GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, + GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels); +GLvoid (APIENTRY * qglCopyTextureImage2D)(GLuint texture, GLenum target, GLint level, GLenum internalformat, + GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +GLvoid (APIENTRY * qglCompressedTextureImage2D)(GLuint texture, GLenum target, GLint level, GLenum internalformat, + GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *data); +GLvoid (APIENTRY * qglCompressedTextureSubImage2D)(GLuint texture, GLenum target, GLint level, + GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, + GLsizei imageSize, const GLvoid *data); +GLvoid (APIENTRY * qglGenerateTextureMipmap)(GLuint texture, GLenum target); + +GLvoid(APIENTRY * qglProgramUniform1i)(GLuint program, GLint location, GLint v0); +GLvoid(APIENTRY * qglProgramUniform1f)(GLuint program, GLint location, GLfloat v0); +GLvoid(APIENTRY * qglProgramUniform2f)(GLuint program, GLint location, + GLfloat v0, GLfloat v1); +GLvoid(APIENTRY * qglProgramUniform3f)(GLuint program, GLint location, + GLfloat v0, GLfloat v1, GLfloat v2); +GLvoid(APIENTRY * qglProgramUniform4f)(GLuint program, GLint location, + GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +GLvoid(APIENTRY * qglProgramUniform1fv)(GLuint program, GLint location, + GLsizei count, const GLfloat *value); +GLvoid(APIENTRY * qglProgramUniformMatrix4fv)(GLuint program, GLint location, + GLsizei count, GLboolean transpose, + const GLfloat *value); + +GLvoid(APIENTRY * qglNamedRenderbufferStorage)(GLuint renderbuffer, + GLenum internalformat, GLsizei width, GLsizei height); + +GLvoid(APIENTRY * qglNamedRenderbufferStorageMultisample)(GLuint renderbuffer, + GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); + +GLenum(APIENTRY * qglCheckNamedFramebufferStatus)(GLuint framebuffer, GLenum target); +GLvoid(APIENTRY * qglNamedFramebufferTexture2D)(GLuint framebuffer, + GLenum attachment, GLenum textarget, GLuint texture, GLint level); +GLvoid(APIENTRY * qglNamedFramebufferRenderbuffer)(GLuint framebuffer, + GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); + + static qboolean GLimp_HaveExtension(const char *ext) { const char *ptr = Q_stristr( glConfig.extensions_string, ext ); @@ -585,6 +630,22 @@ void GLimp_InitExtraExtensions() ri.Printf(PRINT_ALL, result[2], extension); } + // GL_ARB_texture_compression + extension = "GL_ARB_texture_compression"; + glRefConfig.arbTextureCompression = qfalse; + if (GLimp_HaveExtension(extension)) + { + qglCompressedTexImage3DARB = (void *)SDL_GL_GetProcAddress("glCompressedTexImage3DARB"); + qglCompressedTexImage2DARB = (void *)SDL_GL_GetProcAddress("glCompressedTexImage2DARB"); + qglCompressedTexImage1DARB = (void *)SDL_GL_GetProcAddress("glCompressedTexImage1DARB"); + qglCompressedTexSubImage3DARB = (void *)SDL_GL_GetProcAddress("glCompressedTexSubImage3DARB"); + qglCompressedTexSubImage2DARB = (void *)SDL_GL_GetProcAddress("glCompressedTexSubImage2DARB"); + qglCompressedTexSubImage1DARB = (void *)SDL_GL_GetProcAddress("glCompressedTexSubImage1DARB"); + qglGetCompressedTexImageARB = (void *)SDL_GL_GetProcAddress("glGetCompressedTexImageARB"); + glRefConfig.arbTextureCompression = qtrue; + ri.Printf(PRINT_ALL, result[glRefConfig.arbTextureCompression], extension); + } + // GL_EXT_framebuffer_multisample extension = "GL_EXT_framebuffer_multisample"; glRefConfig.framebufferMultisample = qfalse; @@ -601,12 +662,12 @@ void GLimp_InitExtraExtensions() glRefConfig.textureCompression = TCR_NONE; - // GL_EXT_texture_compression_latc - extension = "GL_EXT_texture_compression_latc"; + // GL_ARB_texture_compression_rgtc + extension = "GL_ARB_texture_compression_rgtc"; if (GLimp_HaveExtension(extension)) { - if (r_ext_compressed_textures->integer) - glRefConfig.textureCompression |= TCR_LATC; + if (r_ext_compressed_textures->integer && glRefConfig.arbTextureCompression) + glRefConfig.textureCompression |= TCR_RGTC; ri.Printf(PRINT_ALL, result[r_ext_compressed_textures->integer ? 1 : 0], extension); } @@ -615,6 +676,8 @@ void GLimp_InitExtraExtensions() ri.Printf(PRINT_ALL, result[2], extension); } + glRefConfig.swizzleNormalmap = r_ext_compressed_textures->integer && !(glRefConfig.textureCompression & TCR_RGTC); + // GL_ARB_texture_compression_bptc extension = "GL_ARB_texture_compression_bptc"; if (GLimp_HaveExtension(extension)) @@ -732,4 +795,68 @@ void GLimp_InitExtraExtensions() ri.Printf(PRINT_ALL, result[2], extension); } + // GL_EXT_direct_state_access + extension = "GL_EXT_direct_state_access"; + + qglBindMultiTexture = GLDSA_BindMultiTexture; + qglTextureParameterf = GLDSA_TextureParameterf; + qglTextureParameteri = GLDSA_TextureParameteri; + qglTextureImage2D = GLDSA_TextureImage2D; + qglTextureSubImage2D = GLDSA_TextureSubImage2D; + qglCopyTextureImage2D = GLDSA_CopyTextureImage2D; + qglCompressedTextureImage2D = GLDSA_CompressedTextureImage2D; + qglCompressedTextureSubImage2D = GLDSA_CompressedTextureSubImage2D; + qglGenerateTextureMipmap = GLDSA_GenerateTextureMipmap; + + qglProgramUniform1i = GLDSA_ProgramUniform1i; + qglProgramUniform1f = GLDSA_ProgramUniform1f; + qglProgramUniform2f = GLDSA_ProgramUniform2f; + qglProgramUniform3f = GLDSA_ProgramUniform3f; + qglProgramUniform4f = GLDSA_ProgramUniform4f; + qglProgramUniform1fv = GLDSA_ProgramUniform1fv; + qglProgramUniformMatrix4fv = GLDSA_ProgramUniformMatrix4fv; + + qglNamedRenderbufferStorage = GLDSA_NamedRenderbufferStorage; + qglNamedRenderbufferStorageMultisample = GLDSA_NamedRenderbufferStorageMultisample; + qglCheckNamedFramebufferStatus = GLDSA_CheckNamedFramebufferStatus; + qglNamedFramebufferTexture2D = GLDSA_NamedFramebufferTexture2D; + qglNamedFramebufferRenderbuffer = GLDSA_NamedFramebufferRenderbuffer; + + glRefConfig.directStateAccess = qfalse; + if (GLimp_HaveExtension(extension)) + { + if (r_ext_direct_state_access->integer) + { + glRefConfig.directStateAccess = qtrue; + qglBindMultiTexture = (void *)SDL_GL_GetProcAddress("glBindMultiTextureEXT"); + qglTextureParameterf = (void *)SDL_GL_GetProcAddress("glTextureParameterfEXT"); + qglTextureParameteri = (void *)SDL_GL_GetProcAddress("glTextureParameteriEXT"); + qglTextureImage2D = (void *)SDL_GL_GetProcAddress("glTextureImage2DEXT"); + qglTextureSubImage2D = (void *)SDL_GL_GetProcAddress("glTextureSubImage2DEXT"); + qglCopyTextureImage2D = (void *)SDL_GL_GetProcAddress("glCopyTextureImage2DEXT"); + qglCompressedTextureImage2D = (void *)SDL_GL_GetProcAddress("glCompressedTextureImage2DEXT"); + qglCompressedTextureSubImage2D = (void *)SDL_GL_GetProcAddress("glCompressedTextureSubImage2DEXT"); + qglGenerateTextureMipmap = (void *)SDL_GL_GetProcAddress("glGenerateTextureMipmapEXT"); + + qglProgramUniform1i = (void *)SDL_GL_GetProcAddress("glProgramUniform1iEXT"); + qglProgramUniform1f = (void *)SDL_GL_GetProcAddress("glProgramUniform1fEXT"); + qglProgramUniform2f = (void *)SDL_GL_GetProcAddress("glProgramUniform2fEXT"); + qglProgramUniform3f = (void *)SDL_GL_GetProcAddress("glProgramUniform3fEXT"); + qglProgramUniform4f = (void *)SDL_GL_GetProcAddress("glProgramUniform4fEXT"); + qglProgramUniform1fv = (void *)SDL_GL_GetProcAddress("glProgramUniform1fvEXT"); + qglProgramUniformMatrix4fv = (void *)SDL_GL_GetProcAddress("glProgramUniformMatrix4fvEXT"); + + qglNamedRenderbufferStorage = (void *)SDL_GL_GetProcAddress("glNamedRenderbufferStorageEXT"); + qglNamedRenderbufferStorageMultisample = (void *)SDL_GL_GetProcAddress("glNamedRenderbufferStorageMultisampleEXT"); + qglCheckNamedFramebufferStatus = (void *)SDL_GL_GetProcAddress("glCheckNamedFramebufferStatusEXT"); + qglNamedFramebufferTexture2D = (void *)SDL_GL_GetProcAddress("glNamedFramebufferTexture2DEXT"); + qglNamedFramebufferRenderbuffer = (void *)SDL_GL_GetProcAddress("glNamedFramebufferRenderbufferEXT"); + } + + ri.Printf(PRINT_ALL, result[glRefConfig.directStateAccess ? 1 : 0], extension); + } + else + { + ri.Printf(PRINT_ALL, result[2], extension); + } } diff --git a/code/renderergl2/tr_fbo.c b/code/renderergl2/tr_fbo.c index c8ea0aeb..12293766 100644 --- a/code/renderergl2/tr_fbo.c +++ b/code/renderergl2/tr_fbo.c @@ -23,6 +23,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA // tr_fbo.c #include "tr_local.h" +#include "tr_dsa.h" + /* ============= R_CheckFBO @@ -30,19 +32,10 @@ R_CheckFBO */ qboolean R_CheckFBO(const FBO_t * fbo) { - int code; - int id; - - qglGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &id); - qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo->frameBuffer); - - code = qglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); + GLenum code = qglCheckNamedFramebufferStatus(fbo->frameBuffer, GL_FRAMEBUFFER_EXT); if(code == GL_FRAMEBUFFER_COMPLETE_EXT) - { - qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, id); return qtrue; - } // an error occured switch (code) @@ -83,13 +76,9 @@ qboolean R_CheckFBO(const FBO_t * fbo) default: ri.Printf(PRINT_WARNING, "R_CheckFBO: (%s) unknown error 0x%X\n", fbo->name, code); - //ri.Error(ERR_FATAL, "R_CheckFBO: (%s) unknown error 0x%X", fbo->name, code); - //assert(0); break; } - qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, id); - return qfalse; } @@ -133,6 +122,11 @@ FBO_t *FBO_Create(const char *name, int width, int height) return fbo; } +/* +================= +FBO_CreateBuffer +================= +*/ void FBO_CreateBuffer(FBO_t *fbo, int format, int index, int multisample) { uint32_t *pRenderBuffer; @@ -189,115 +183,45 @@ void FBO_CreateBuffer(FBO_t *fbo, int format, int index, int multisample) if (absent) qglGenRenderbuffersEXT(1, pRenderBuffer); - qglBindRenderbufferEXT(GL_RENDERBUFFER_EXT, *pRenderBuffer); if (multisample && glRefConfig.framebufferMultisample) - { - qglRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, multisample, format, fbo->width, fbo->height); - } + qglNamedRenderbufferStorageMultisample(*pRenderBuffer, multisample, format, fbo->width, fbo->height); else - { - qglRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, format, fbo->width, fbo->height); - } + qglNamedRenderbufferStorage(*pRenderBuffer, format, fbo->width, fbo->height); if(absent) { if (attachment == 0) { - qglFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, *pRenderBuffer); - qglFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, *pRenderBuffer); + qglNamedFramebufferRenderbuffer(fbo->frameBuffer, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, *pRenderBuffer); + qglNamedFramebufferRenderbuffer(fbo->frameBuffer, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, *pRenderBuffer); } else - qglFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, attachment, GL_RENDERBUFFER_EXT, *pRenderBuffer); + { + qglNamedFramebufferRenderbuffer(fbo->frameBuffer, attachment, GL_RENDERBUFFER_EXT, *pRenderBuffer); + } } } /* ================= -R_AttachFBOTexture1D +FBO_AttachImage ================= */ -void R_AttachFBOTexture1D(int texId, int index) +void FBO_AttachImage(FBO_t *fbo, image_t *image, GLenum attachment, GLuint cubemapside) { - if(index < 0 || index >= glRefConfig.maxColorAttachments) - { - ri.Printf(PRINT_WARNING, "R_AttachFBOTexture1D: invalid attachment index %i\n", index); - return; - } + GLenum target = GL_TEXTURE_2D; + int index; - qglFramebufferTexture1DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT + index, GL_TEXTURE_1D, texId, 0); + if (image->flags & IMGFLAG_CUBEMAP) + target = GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB + cubemapside; + + qglNamedFramebufferTexture2D(fbo->frameBuffer, attachment, target, image->texnum, 0); + index = attachment - GL_COLOR_ATTACHMENT0_EXT; + if (index >= 0 && index <= 15) + fbo->colorImage[index] = image; } -/* -================= -R_AttachFBOTexture2D -================= -*/ -void R_AttachFBOTexture2D(int target, int texId, int index) -{ - if(target != GL_TEXTURE_2D && (target < GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB || target > GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB)) - { - ri.Printf(PRINT_WARNING, "R_AttachFBOTexture2D: invalid target %i\n", target); - return; - } - - if(index < 0 || index >= glRefConfig.maxColorAttachments) - { - ri.Printf(PRINT_WARNING, "R_AttachFBOTexture2D: invalid attachment index %i\n", index); - return; - } - - qglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT + index, target, texId, 0); -} - -/* -================= -R_AttachFBOTexture3D -================= -*/ -void R_AttachFBOTexture3D(int texId, int index, int zOffset) -{ - if(index < 0 || index >= glRefConfig.maxColorAttachments) - { - ri.Printf(PRINT_WARNING, "R_AttachFBOTexture3D: invalid attachment index %i\n", index); - return; - } - - qglFramebufferTexture3DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT + index, GL_TEXTURE_3D_EXT, texId, 0, zOffset); -} - -/* -================= -R_AttachFBOTextureDepth -================= -*/ -void R_AttachFBOTextureDepth(int texId) -{ - qglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, texId, 0); -} - -/* -================= -R_AttachFBOTexturePackedDepthStencil -================= -*/ -void R_AttachFBOTexturePackedDepthStencil(int texId) -{ - qglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, texId, 0); - qglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_TEXTURE_2D, texId, 0); -} - -void FBO_AttachTextureImage(image_t *img, int index) -{ - if (!glState.currentFBO) - { - ri.Printf(PRINT_WARNING, "FBO: attempted to attach a texture image with no FBO bound!\n"); - return; - } - - R_AttachFBOTexture2D(GL_TEXTURE_2D, img->texnum, index); - glState.currentFBO->colorImage[index] = img; -} /* ============ @@ -312,38 +236,10 @@ void FBO_Bind(FBO_t * fbo) if (r_logFile->integer) { // don't just call LogComment, or we will get a call to va() every frame! - if (fbo) - GLimp_LogComment(va("--- FBO_Bind( %s ) ---\n", fbo->name)); - else - GLimp_LogComment("--- FBO_Bind ( NULL ) ---\n"); + GLimp_LogComment(va("--- FBO_Bind( %s ) ---\n", fbo ? fbo->name : "NULL")); } - if (!fbo) - { - qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); - //qglBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); - glState.currentFBO = NULL; - - return; - } - - qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo->frameBuffer); - - /* - if(fbo->colorBuffers[0]) - { - qglBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fbo->colorBuffers[0]); - } - */ - - /* - if(fbo->depthBuffer) - { - qglBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fbo->depthBuffer); - qglFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, fbo->depthBuffer); - } - */ - + GL_BindFramebuffer(GL_FRAMEBUFFER_EXT, fbo ? fbo->frameBuffer : 0); glState.currentFBO = fbo; } @@ -355,8 +251,7 @@ FBO_Init void FBO_Init(void) { int i; - // int width, height, hdrFormat, multisample; - int hdrFormat, multisample; + int hdrFormat, multisample = 0; ri.Printf(PRINT_ALL, "------- FBO_Init -------\n"); @@ -369,73 +264,41 @@ void FBO_Init(void) R_IssuePendingRenderCommands(); -/* if(glRefConfig.textureNonPowerOfTwo) - { - width = glConfig.vidWidth; - height = glConfig.vidHeight; - } - else - { - width = NextPowerOfTwo(glConfig.vidWidth); - height = NextPowerOfTwo(glConfig.vidHeight); - } */ - hdrFormat = GL_RGBA8; if (r_hdr->integer && glRefConfig.framebufferObject && glRefConfig.textureFloat) - { hdrFormat = GL_RGBA16F_ARB; - } - qglGetIntegerv(GL_MAX_SAMPLES_EXT, &multisample); + if (glRefConfig.framebufferMultisample) + qglGetIntegerv(GL_MAX_SAMPLES_EXT, &multisample); if (r_ext_framebuffer_multisample->integer < multisample) - { multisample = r_ext_framebuffer_multisample->integer; - } if (multisample < 2 || !glRefConfig.framebufferBlit) multisample = 0; if (multisample != r_ext_framebuffer_multisample->integer) - { ri.Cvar_SetValue("r_ext_framebuffer_multisample", (float)multisample); - } // only create a render FBO if we need to resolve MSAA or do HDR // otherwise just render straight to the screen (tr.renderFbo = NULL) if (multisample && glRefConfig.framebufferMultisample) { tr.renderFbo = FBO_Create("_render", tr.renderDepthImage->width, tr.renderDepthImage->height); - FBO_Bind(tr.renderFbo); - FBO_CreateBuffer(tr.renderFbo, hdrFormat, 0, multisample); FBO_CreateBuffer(tr.renderFbo, GL_DEPTH_COMPONENT24_ARB, 0, multisample); - R_CheckFBO(tr.renderFbo); - tr.msaaResolveFbo = FBO_Create("_msaaResolve", tr.renderDepthImage->width, tr.renderDepthImage->height); - FBO_Bind(tr.msaaResolveFbo); - - //FBO_CreateBuffer(tr.msaaResolveFbo, hdrFormat, 0, 0); - FBO_AttachTextureImage(tr.renderImage, 0); - - //FBO_CreateBuffer(tr.msaaResolveFbo, GL_DEPTH_COMPONENT24_ARB, 0, 0); - R_AttachFBOTextureDepth(tr.renderDepthImage->texnum); - + FBO_AttachImage(tr.msaaResolveFbo, tr.renderImage, GL_COLOR_ATTACHMENT0_EXT, 0); + FBO_AttachImage(tr.msaaResolveFbo, tr.renderDepthImage, GL_DEPTH_ATTACHMENT_EXT, 0); R_CheckFBO(tr.msaaResolveFbo); } else if (r_hdr->integer) { tr.renderFbo = FBO_Create("_render", tr.renderDepthImage->width, tr.renderDepthImage->height); - FBO_Bind(tr.renderFbo); - - //FBO_CreateBuffer(tr.renderFbo, hdrFormat, 0, 0); - FBO_AttachTextureImage(tr.renderImage, 0); - - //FBO_CreateBuffer(tr.renderFbo, GL_DEPTH_COMPONENT24_ARB, 0, 0); - R_AttachFBOTextureDepth(tr.renderDepthImage->texnum); - + FBO_AttachImage(tr.renderFbo, tr.renderImage, GL_COLOR_ATTACHMENT0_EXT, 0); + FBO_AttachImage(tr.renderFbo, tr.renderDepthImage, GL_DEPTH_ATTACHMENT_EXT, 0); R_CheckFBO(tr.renderFbo); } @@ -443,20 +306,15 @@ void FBO_Init(void) // this fixes the corrupt screen bug with r_hdr 1 on older hardware if (tr.renderFbo) { - FBO_Bind(tr.renderFbo); + GL_BindFramebuffer(GL_FRAMEBUFFER_EXT, tr.renderFbo->frameBuffer); qglClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); - FBO_Bind(NULL); } if (r_drawSunRays->integer) { tr.sunRaysFbo = FBO_Create("_sunRays", tr.renderDepthImage->width, tr.renderDepthImage->height); - FBO_Bind(tr.sunRaysFbo); - - FBO_AttachTextureImage(tr.sunRaysImage, 0); - - R_AttachFBOTextureDepth(tr.renderDepthImage->texnum); - + FBO_AttachImage(tr.sunRaysFbo, tr.sunRaysImage, GL_COLOR_ATTACHMENT0_EXT, 0); + FBO_AttachImage(tr.sunRaysFbo, tr.renderDepthImage, GL_DEPTH_ATTACHMENT_EXT, 0); R_CheckFBO(tr.sunRaysFbo); } @@ -466,14 +324,8 @@ void FBO_Init(void) for( i = 0; i < MAX_DRAWN_PSHADOWS; i++) { tr.pshadowFbos[i] = FBO_Create(va("_shadowmap%d", i), tr.pshadowMaps[i]->width, tr.pshadowMaps[i]->height); - FBO_Bind(tr.pshadowFbos[i]); - - //FBO_CreateBuffer(tr.pshadowFbos[i], GL_RGBA8, 0, 0); - FBO_AttachTextureImage(tr.pshadowMaps[i], 0); - + FBO_AttachImage(tr.pshadowFbos[i], tr.pshadowMaps[i], GL_COLOR_ATTACHMENT0_EXT, 0); FBO_CreateBuffer(tr.pshadowFbos[i], GL_DEPTH_COMPONENT24_ARB, 0, 0); - //R_AttachFBOTextureDepth(tr.textureDepthImage->texnum); - R_CheckFBO(tr.pshadowFbos[i]); } } @@ -483,104 +335,67 @@ void FBO_Init(void) for ( i = 0; i < 4; i++) { tr.sunShadowFbo[i] = FBO_Create("_sunshadowmap", tr.sunShadowDepthImage[i]->width, tr.sunShadowDepthImage[i]->height); - FBO_Bind(tr.sunShadowFbo[i]); - - //FBO_CreateBuffer(tr.sunShadowFbo[i], GL_RGBA8, 0, 0); - //FBO_AttachTextureImage(tr.sunShadowImage, 0); - qglDrawBuffer(GL_NONE); - qglReadBuffer(GL_NONE); - - //FBO_CreateBuffer(tr.sunShadowFbo, GL_DEPTH_COMPONENT24_ARB, 0, 0); - R_AttachFBOTextureDepth(tr.sunShadowDepthImage[i]->texnum); - + // FIXME: this next line wastes 16mb with 4x1024x1024 sun shadow maps, skip if OpenGL 4.3+ or ARB_framebuffer_no_attachments + // This at least gets sun shadows working on older GPUs (Intel) + FBO_CreateBuffer(tr.sunShadowFbo[i], GL_RGBA8, 0, 0); + FBO_AttachImage(tr.sunShadowFbo[i], tr.sunShadowDepthImage[i], GL_DEPTH_ATTACHMENT_EXT, 0); R_CheckFBO(tr.sunShadowFbo[i]); - } tr.screenShadowFbo = FBO_Create("_screenshadow", tr.screenShadowImage->width, tr.screenShadowImage->height); - FBO_Bind(tr.screenShadowFbo); - - FBO_AttachTextureImage(tr.screenShadowImage, 0); - + FBO_AttachImage(tr.screenShadowFbo, tr.screenShadowImage, GL_COLOR_ATTACHMENT0_EXT, 0); R_CheckFBO(tr.screenShadowFbo); } for (i = 0; i < 2; i++) { tr.textureScratchFbo[i] = FBO_Create(va("_texturescratch%d", i), tr.textureScratchImage[i]->width, tr.textureScratchImage[i]->height); - FBO_Bind(tr.textureScratchFbo[i]); - - //FBO_CreateBuffer(tr.textureScratchFbo[i], GL_RGBA8, 0, 0); - FBO_AttachTextureImage(tr.textureScratchImage[i], 0); - + FBO_AttachImage(tr.textureScratchFbo[i], tr.textureScratchImage[i], GL_COLOR_ATTACHMENT0_EXT, 0); R_CheckFBO(tr.textureScratchFbo[i]); } { tr.calcLevelsFbo = FBO_Create("_calclevels", tr.calcLevelsImage->width, tr.calcLevelsImage->height); - FBO_Bind(tr.calcLevelsFbo); - - //FBO_CreateBuffer(tr.calcLevelsFbo, hdrFormat, 0, 0); - FBO_AttachTextureImage(tr.calcLevelsImage, 0); - + FBO_AttachImage(tr.calcLevelsFbo, tr.calcLevelsImage, GL_COLOR_ATTACHMENT0_EXT, 0); R_CheckFBO(tr.calcLevelsFbo); } { tr.targetLevelsFbo = FBO_Create("_targetlevels", tr.targetLevelsImage->width, tr.targetLevelsImage->height); - FBO_Bind(tr.targetLevelsFbo); - - //FBO_CreateBuffer(tr.targetLevelsFbo, hdrFormat, 0, 0); - FBO_AttachTextureImage(tr.targetLevelsImage, 0); - + FBO_AttachImage(tr.targetLevelsFbo, tr.targetLevelsImage, GL_COLOR_ATTACHMENT0_EXT, 0); R_CheckFBO(tr.targetLevelsFbo); } for (i = 0; i < 2; i++) { tr.quarterFbo[i] = FBO_Create(va("_quarter%d", i), tr.quarterImage[i]->width, tr.quarterImage[i]->height); - FBO_Bind(tr.quarterFbo[i]); - - //FBO_CreateBuffer(tr.quarterFbo[i], hdrFormat, 0, 0); - FBO_AttachTextureImage(tr.quarterImage[i], 0); - + FBO_AttachImage(tr.quarterFbo[i], tr.quarterImage[i], GL_COLOR_ATTACHMENT0_EXT, 0); R_CheckFBO(tr.quarterFbo[i]); } if (r_ssao->integer) { tr.hdrDepthFbo = FBO_Create("_hdrDepth", tr.hdrDepthImage->width, tr.hdrDepthImage->height); - FBO_Bind(tr.hdrDepthFbo); - - FBO_AttachTextureImage(tr.hdrDepthImage, 0); - + FBO_AttachImage(tr.hdrDepthFbo, tr.hdrDepthImage, GL_COLOR_ATTACHMENT0_EXT, 0); R_CheckFBO(tr.hdrDepthFbo); tr.screenSsaoFbo = FBO_Create("_screenssao", tr.screenSsaoImage->width, tr.screenSsaoImage->height); - FBO_Bind(tr.screenSsaoFbo); - - FBO_AttachTextureImage(tr.screenSsaoImage, 0); - + FBO_AttachImage(tr.screenSsaoFbo, tr.screenSsaoImage, GL_COLOR_ATTACHMENT0_EXT, 0); R_CheckFBO(tr.screenSsaoFbo); } if (tr.renderCubeImage) { tr.renderCubeFbo = FBO_Create("_renderCubeFbo", tr.renderCubeImage->width, tr.renderCubeImage->height); - FBO_Bind(tr.renderCubeFbo); - - //FBO_AttachTextureImage(tr.renderCubeImage, 0); - R_AttachFBOTexture2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB, tr.renderCubeImage->texnum, 0); - glState.currentFBO->colorImage[0] = tr.renderCubeImage; - + FBO_AttachImage(tr.renderCubeFbo, tr.renderCubeImage, GL_COLOR_ATTACHMENT0_EXT, 0); FBO_CreateBuffer(tr.renderCubeFbo, GL_DEPTH_COMPONENT24_ARB, 0, 0); - R_CheckFBO(tr.renderCubeFbo); } GL_CheckErrors(); - FBO_Bind(NULL); + GL_BindFramebuffer(GL_FRAMEBUFFER_EXT, 0); + glState.currentFBO = NULL; } /* @@ -663,7 +478,10 @@ void FBO_BlitFromTexture(struct image_s *src, ivec4_t inSrcBox, vec2_t inSrcTexS int width, height; if (!src) + { + ri.Printf(PRINT_WARNING, "Tried to blit from a NULL texture!\n"); return; + } if (inSrcBox) { @@ -849,12 +667,12 @@ void FBO_FastBlit(FBO_t *src, ivec4_t srcBox, FBO_t *dst, ivec4_t dstBox, int bu VectorSet4(dstBoxFinal, dstBox[0], dstBox[1], dstBox[0] + dstBox[2], dstBox[1] + dstBox[3]); } - qglBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, srcFb); - qglBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, dstFb); + GL_BindFramebuffer(GL_READ_FRAMEBUFFER_EXT, srcFb); + GL_BindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, dstFb); qglBlitFramebufferEXT(srcBoxFinal[0], srcBoxFinal[1], srcBoxFinal[2], srcBoxFinal[3], dstBoxFinal[0], dstBoxFinal[1], dstBoxFinal[2], dstBoxFinal[3], buffers, filter); - qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + GL_BindFramebuffer(GL_FRAMEBUFFER_EXT, 0); glState.currentFBO = NULL; } diff --git a/code/renderergl2/tr_fbo.h b/code/renderergl2/tr_fbo.h index b5ab18c1..3f23a353 100644 --- a/code/renderergl2/tr_fbo.h +++ b/code/renderergl2/tr_fbo.h @@ -52,6 +52,7 @@ typedef struct FBO_s int height; } FBO_t; +void FBO_AttachImage(FBO_t *fbo, image_t *image, GLenum attachment, GLuint cubemapside); void FBO_Bind(FBO_t *fbo); void FBO_Init(void); void FBO_Shutdown(void); diff --git a/code/renderergl2/tr_glsl.c b/code/renderergl2/tr_glsl.c index f526b8c6..f8637a66 100644 --- a/code/renderergl2/tr_glsl.c +++ b/code/renderergl2/tr_glsl.c @@ -22,7 +22,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA // tr_glsl.c #include "tr_local.h" -void GLSL_BindNullProgram(void); +#include "tr_dsa.h" extern const char *fallbackShader_bokeh_vp; extern const char *fallbackShader_bokeh_fp; @@ -322,17 +322,8 @@ static void GLSL_GetShaderHeader( GLenum shaderType, const GLcharARB *extra, cha Q_strcat(dest, size, va("#ifndef r_FBufScale\n#define r_FBufScale vec2(%f, %f)\n#endif\n", fbufWidthScale, fbufHeightScale)); - if (r_materialGamma->value != 1.0f) - Q_strcat(dest, size, va("#ifndef r_materialGamma\n#define r_materialGamma %f\n#endif\n", r_materialGamma->value)); - - if (r_lightGamma->value != 1.0f) - Q_strcat(dest, size, va("#ifndef r_lightGamma\n#define r_lightGamma %f\n#endif\n", r_lightGamma->value)); - - if (r_framebufferGamma->value != 1.0f) - Q_strcat(dest, size, va("#ifndef r_framebufferGamma\n#define r_framebufferGamma %f\n#endif\n", r_framebufferGamma->value)); - - if (r_tonemapGamma->value != 1.0f) - Q_strcat(dest, size, va("#ifndef r_tonemapGamma\n#define r_tonemapGamma %f\n#endif\n", r_tonemapGamma->value)); + if (r_pbr->integer) + Q_strcat(dest, size, "#define USE_PBR\n"); if (extra) { @@ -482,11 +473,6 @@ static void GLSL_ShowProgramUniforms(GLhandleARB program) GLenum type; char uniformName[1000]; - // install the executables in the program object as part of current state. - qglUseProgramObjectARB(program); - - // check for GL Errors - // query the number of active uniforms qglGetObjectParameterivARB(program, GL_OBJECT_ACTIVE_UNIFORMS_ARB, &count); @@ -497,8 +483,6 @@ static void GLSL_ShowProgramUniforms(GLhandleARB program) ri.Printf(PRINT_DEVELOPER, "active uniform: '%s'\n", uniformName); } - - qglUseProgramObjectARB(0); } static int GLSL_InitGPUShader2(shaderProgram_t * program, const char *name, int attribs, const char *vpCode, const char *fpCode) @@ -703,7 +687,7 @@ void GLSL_SetUniformInt(shaderProgram_t *program, int uniformNum, GLint value) *compare = value; - qglUniform1iARB(uniforms[uniformNum], value); + qglProgramUniform1i(program->program, uniforms[uniformNum], value); } void GLSL_SetUniformFloat(shaderProgram_t *program, int uniformNum, GLfloat value) @@ -727,7 +711,7 @@ void GLSL_SetUniformFloat(shaderProgram_t *program, int uniformNum, GLfloat valu *compare = value; - qglUniform1fARB(uniforms[uniformNum], value); + qglProgramUniform1f(program->program, uniforms[uniformNum], value); } void GLSL_SetUniformVec2(shaderProgram_t *program, int uniformNum, const vec2_t v) @@ -752,7 +736,7 @@ void GLSL_SetUniformVec2(shaderProgram_t *program, int uniformNum, const vec2_t compare[0] = v[0]; compare[1] = v[1]; - qglUniform2fARB(uniforms[uniformNum], v[0], v[1]); + qglProgramUniform2f(program->program, uniforms[uniformNum], v[0], v[1]); } void GLSL_SetUniformVec3(shaderProgram_t *program, int uniformNum, const vec3_t v) @@ -776,7 +760,7 @@ void GLSL_SetUniformVec3(shaderProgram_t *program, int uniformNum, const vec3_t VectorCopy(v, compare); - qglUniform3fARB(uniforms[uniformNum], v[0], v[1], v[2]); + qglProgramUniform3f(program->program, uniforms[uniformNum], v[0], v[1], v[2]); } void GLSL_SetUniformVec4(shaderProgram_t *program, int uniformNum, const vec4_t v) @@ -800,7 +784,7 @@ void GLSL_SetUniformVec4(shaderProgram_t *program, int uniformNum, const vec4_t VectorCopy4(v, compare); - qglUniform4fARB(uniforms[uniformNum], v[0], v[1], v[2], v[3]); + qglProgramUniform4f(program->program, uniforms[uniformNum], v[0], v[1], v[2], v[3]); } void GLSL_SetUniformFloat5(shaderProgram_t *program, int uniformNum, const vec5_t v) @@ -824,7 +808,7 @@ void GLSL_SetUniformFloat5(shaderProgram_t *program, int uniformNum, const vec5_ VectorCopy5(v, compare); - qglUniform1fvARB(uniforms[uniformNum], 5, v); + qglProgramUniform1fv(program->program, uniforms[uniformNum], 5, v); } void GLSL_SetUniformMat4(shaderProgram_t *program, int uniformNum, const mat4_t matrix) @@ -848,7 +832,7 @@ void GLSL_SetUniformMat4(shaderProgram_t *program, int uniformNum, const mat4_t Mat4Copy(matrix, compare); - qglUniformMatrix4fvARB(uniforms[uniformNum], 1, GL_FALSE, matrix); + qglProgramUniformMatrix4fv(program->program, uniforms[uniformNum], 1, GL_FALSE, matrix); } void GLSL_DeleteGPUShader(shaderProgram_t *program) @@ -925,10 +909,8 @@ void GLSL_InitGPUShaders(void) GLSL_InitUniforms(&tr.genericShader[i]); - qglUseProgramObjectARB(tr.genericShader[i].program); GLSL_SetUniformInt(&tr.genericShader[i], UNIFORM_DIFFUSEMAP, TB_DIFFUSEMAP); GLSL_SetUniformInt(&tr.genericShader[i], UNIFORM_LIGHTMAP, TB_LIGHTMAP); - qglUseProgramObjectARB(0); GLSL_FinishGPUShader(&tr.genericShader[i]); @@ -945,9 +927,7 @@ void GLSL_InitGPUShaders(void) GLSL_InitUniforms(&tr.textureColorShader); - qglUseProgramObjectARB(tr.textureColorShader.program); GLSL_SetUniformInt(&tr.textureColorShader, UNIFORM_TEXTUREMAP, TB_DIFFUSEMAP); - qglUseProgramObjectARB(0); GLSL_FinishGPUShader(&tr.textureColorShader); @@ -993,9 +973,7 @@ void GLSL_InitGPUShaders(void) GLSL_InitUniforms(&tr.dlightShader[i]); - qglUseProgramObjectARB(tr.dlightShader[i].program); GLSL_SetUniformInt(&tr.dlightShader[i], UNIFORM_DIFFUSEMAP, TB_DIFFUSEMAP); - qglUseProgramObjectARB(0); GLSL_FinishGPUShader(&tr.dlightShader[i]); @@ -1022,16 +1000,10 @@ void GLSL_InitGPUShaders(void) extradefines[0] = '\0'; - if (r_deluxeSpecular->value > 0.000001f) - Q_strcat(extradefines, 1024, va("#define r_deluxeSpecular %f\n", r_deluxeSpecular->value)); - - if (r_specularIsMetallic->value) - Q_strcat(extradefines, 1024, "#define SPECULAR_IS_METALLIC\n"); - if (r_dlightMode->integer >= 2) Q_strcat(extradefines, 1024, "#define USE_SHADOWMAP\n"); - if (1) + if (glRefConfig.swizzleNormalmap) Q_strcat(extradefines, 1024, "#define SWIZZLE_NORMALMAP\n"); if (r_hdr->integer && !glRefConfig.floatLightmap) @@ -1067,12 +1039,6 @@ void GLSL_InitGPUShaders(void) { Q_strcat(extradefines, 1024, "#define USE_NORMALMAP\n"); - if (r_normalMapping->integer == 2) - Q_strcat(extradefines, 1024, "#define USE_OREN_NAYAR\n"); - - if (r_normalMapping->integer == 3) - Q_strcat(extradefines, 1024, "#define USE_TRIACE_OREN_NAYAR\n"); - #ifdef USE_VERT_TANGENT_SPACE Q_strcat(extradefines, 1024, "#define USE_VERT_TANGENT_SPACE\n"); attribs |= ATTR_TANGENT; @@ -1087,34 +1053,8 @@ void GLSL_InitGPUShaders(void) } if (r_specularMapping->integer) - { Q_strcat(extradefines, 1024, "#define USE_SPECULARMAP\n"); - switch (r_specularMapping->integer) - { - case 1: - default: - Q_strcat(extradefines, 1024, "#define USE_BLINN\n"); - break; - - case 2: - Q_strcat(extradefines, 1024, "#define USE_BLINN_FRESNEL\n"); - break; - - case 3: - Q_strcat(extradefines, 1024, "#define USE_MCAULEY\n"); - break; - - case 4: - Q_strcat(extradefines, 1024, "#define USE_GOTANDA\n"); - break; - - case 5: - Q_strcat(extradefines, 1024, "#define USE_LAZAROV\n"); - break; - } - } - if (r_cubeMapping->integer) Q_strcat(extradefines, 1024, "#define USE_CUBEMAP\n"); } @@ -1155,7 +1095,6 @@ void GLSL_InitGPUShaders(void) GLSL_InitUniforms(&tr.lightallShader[i]); - qglUseProgramObjectARB(tr.lightallShader[i].program); GLSL_SetUniformInt(&tr.lightallShader[i], UNIFORM_DIFFUSEMAP, TB_DIFFUSEMAP); GLSL_SetUniformInt(&tr.lightallShader[i], UNIFORM_LIGHTMAP, TB_LIGHTMAP); GLSL_SetUniformInt(&tr.lightallShader[i], UNIFORM_NORMALMAP, TB_NORMALMAP); @@ -1163,7 +1102,6 @@ void GLSL_InitGPUShaders(void) GLSL_SetUniformInt(&tr.lightallShader[i], UNIFORM_SPECULARMAP, TB_SPECULARMAP); GLSL_SetUniformInt(&tr.lightallShader[i], UNIFORM_SHADOWMAP, TB_SHADOWMAP); GLSL_SetUniformInt(&tr.lightallShader[i], UNIFORM_CUBEMAP, TB_CUBEMAP); - qglUseProgramObjectARB(0); GLSL_FinishGPUShader(&tr.lightallShader[i]); @@ -1196,9 +1134,7 @@ void GLSL_InitGPUShaders(void) GLSL_InitUniforms(&tr.pshadowShader); - qglUseProgramObjectARB(tr.pshadowShader.program); GLSL_SetUniformInt(&tr.pshadowShader, UNIFORM_SHADOWMAP, TB_DIFFUSEMAP); - qglUseProgramObjectARB(0); GLSL_FinishGPUShader(&tr.pshadowShader); @@ -1215,9 +1151,7 @@ void GLSL_InitGPUShaders(void) GLSL_InitUniforms(&tr.down4xShader); - qglUseProgramObjectARB(tr.down4xShader.program); GLSL_SetUniformInt(&tr.down4xShader, UNIFORM_TEXTUREMAP, TB_DIFFUSEMAP); - qglUseProgramObjectARB(0); GLSL_FinishGPUShader(&tr.down4xShader); @@ -1234,9 +1168,7 @@ void GLSL_InitGPUShaders(void) GLSL_InitUniforms(&tr.bokehShader); - qglUseProgramObjectARB(tr.bokehShader.program); GLSL_SetUniformInt(&tr.bokehShader, UNIFORM_TEXTUREMAP, TB_DIFFUSEMAP); - qglUseProgramObjectARB(0); GLSL_FinishGPUShader(&tr.bokehShader); @@ -1253,10 +1185,8 @@ void GLSL_InitGPUShaders(void) GLSL_InitUniforms(&tr.tonemapShader); - qglUseProgramObjectARB(tr.tonemapShader.program); GLSL_SetUniformInt(&tr.tonemapShader, UNIFORM_TEXTUREMAP, TB_COLORMAP); GLSL_SetUniformInt(&tr.tonemapShader, UNIFORM_LEVELSMAP, TB_LEVELSMAP); - qglUseProgramObjectARB(0); GLSL_FinishGPUShader(&tr.tonemapShader); @@ -1278,9 +1208,7 @@ void GLSL_InitGPUShaders(void) GLSL_InitUniforms(&tr.calclevels4xShader[i]); - qglUseProgramObjectARB(tr.calclevels4xShader[i].program); GLSL_SetUniformInt(&tr.calclevels4xShader[i], UNIFORM_TEXTUREMAP, TB_DIFFUSEMAP); - qglUseProgramObjectARB(0); GLSL_FinishGPUShader(&tr.calclevels4xShader[i]); @@ -1311,13 +1239,11 @@ void GLSL_InitGPUShaders(void) GLSL_InitUniforms(&tr.shadowmaskShader); - qglUseProgramObjectARB(tr.shadowmaskShader.program); GLSL_SetUniformInt(&tr.shadowmaskShader, UNIFORM_SCREENDEPTHMAP, TB_COLORMAP); GLSL_SetUniformInt(&tr.shadowmaskShader, UNIFORM_SHADOWMAP, TB_SHADOWMAP); GLSL_SetUniformInt(&tr.shadowmaskShader, UNIFORM_SHADOWMAP2, TB_SHADOWMAP2); GLSL_SetUniformInt(&tr.shadowmaskShader, UNIFORM_SHADOWMAP3, TB_SHADOWMAP3); GLSL_SetUniformInt(&tr.shadowmaskShader, UNIFORM_SHADOWMAP4, TB_SHADOWMAP4); - qglUseProgramObjectARB(0); GLSL_FinishGPUShader(&tr.shadowmaskShader); @@ -1334,9 +1260,7 @@ void GLSL_InitGPUShaders(void) GLSL_InitUniforms(&tr.ssaoShader); - qglUseProgramObjectARB(tr.ssaoShader.program); GLSL_SetUniformInt(&tr.ssaoShader, UNIFORM_SCREENDEPTHMAP, TB_COLORMAP); - qglUseProgramObjectARB(0); GLSL_FinishGPUShader(&tr.ssaoShader); @@ -1361,10 +1285,8 @@ void GLSL_InitGPUShaders(void) GLSL_InitUniforms(&tr.depthBlurShader[i]); - qglUseProgramObjectARB(tr.depthBlurShader[i].program); GLSL_SetUniformInt(&tr.depthBlurShader[i], UNIFORM_SCREENIMAGEMAP, TB_COLORMAP); GLSL_SetUniformInt(&tr.depthBlurShader[i], UNIFORM_SCREENDEPTHMAP, TB_LIGHTMAP); - qglUseProgramObjectARB(0); GLSL_FinishGPUShader(&tr.depthBlurShader[i]); @@ -1382,9 +1304,7 @@ void GLSL_InitGPUShaders(void) GLSL_InitUniforms(&tr.testcubeShader); - qglUseProgramObjectARB(tr.testcubeShader.program); GLSL_SetUniformInt(&tr.testcubeShader, UNIFORM_TEXTUREMAP, TB_COLORMAP); - qglUseProgramObjectARB(0); GLSL_FinishGPUShader(&tr.testcubeShader); @@ -1408,7 +1328,7 @@ void GLSL_ShutdownGPUShaders(void) for (i = 0; i < ATTR_INDEX_COUNT; i++) qglDisableVertexAttribArrayARB(i); - GLSL_BindNullProgram(); + GL_BindNullProgram(); for ( i = 0; i < GENERICDEF_COUNT; i++) GLSL_DeleteGPUShader(&tr.genericShader[i]); @@ -1438,47 +1358,22 @@ void GLSL_ShutdownGPUShaders(void) for ( i = 0; i < 2; i++) GLSL_DeleteGPUShader(&tr.depthBlurShader[i]); - - glState.currentProgram = 0; - qglUseProgramObjectARB(0); } void GLSL_BindProgram(shaderProgram_t * program) { - if(!program) - { - GLSL_BindNullProgram(); - return; - } + GLuint programObject = program ? program->program : 0; + char *name = program ? program->name : "NULL"; if(r_logFile->integer) { // don't just call LogComment, or we will get a call to va() every frame! - GLimp_LogComment(va("--- GL_BindProgram( %s ) ---\n", program->name)); + GLimp_LogComment(va("--- GLSL_BindProgram( %s ) ---\n", name)); } - if(glState.currentProgram != program) - { - qglUseProgramObjectARB(program->program); - glState.currentProgram = program; + if (GL_UseProgramObject(programObject)) backEnd.pc.c_glslShaderBinds++; - } -} - - -void GLSL_BindNullProgram(void) -{ - if(r_logFile->integer) - { - GLimp_LogComment("--- GL_BindNullProgram ---\n"); - } - - if(glState.currentProgram) - { - qglUseProgramObjectARB(0); - glState.currentProgram = NULL; - } } diff --git a/code/renderergl2/tr_image.c b/code/renderergl2/tr_image.c index bb5759ee..2f5798ac 100644 --- a/code/renderergl2/tr_image.c +++ b/code/renderergl2/tr_image.c @@ -22,6 +22,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA // tr_image.c #include "tr_local.h" +#include "tr_dsa.h" + static byte s_intensitytable[256]; static unsigned char s_gammatable[256]; @@ -113,10 +115,9 @@ void GL_TextureMode( const char *string ) { // change all the existing mipmap texture objects for ( i = 0 ; i < tr.numImages ; i++ ) { glt = tr.images[ i ]; - if ( glt->flags & IMGFLAG_MIPMAP ) { - GL_Bind (glt); - qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); - qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); + if ( glt->flags & IMGFLAG_MIPMAP && !(glt->flags & IMGFLAG_CUBEMAP)) { + qglTextureParameterf(glt->texnum, GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); + qglTextureParameterf(glt->texnum, GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); } } } @@ -176,15 +177,20 @@ void R_ImageList_f( void ) { format = "sBPTC"; // 128 bits per 16 pixels, so 1 byte per pixel break; - case GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT: - format = "LATC "; + case GL_COMPRESSED_RG_RGTC2: + format = "RGTC2"; // 128 bits per 16 pixels, so 1 byte per pixel break; - case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: + case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: format = "DXT1 "; // 64 bits per 16 pixels, so 4 bits per pixel estSize /= 2; break; + case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: + format = "DXT1a"; + // 64 bits per 16 pixels, so 4 bits per pixel + estSize /= 2; + break; case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: format = "DXT5 "; // 128 bits per 16 pixels, so 1 byte per pixel @@ -1331,41 +1337,6 @@ static void R_MipMapsRGB( byte *in, int inWidth, int inHeight) } -static void R_MipMapLuminanceAlpha (const byte *in, byte *out, int width, int height) -{ - int i, j, row; - - if ( width == 1 && height == 1 ) { - return; - } - - row = width * 4; - width >>= 1; - height >>= 1; - - if ( width == 0 || height == 0 ) { - width += height; // get largest - for (i=0 ; i> 1; - out[3] = (in[3] + in[7]) >> 1; - } - return; - } - - for (i=0 ; i> 2; - out[3] = (in[3] + in[7] + in[row+3] + in[row+7]) >> 2; - } - } - -} - - static void R_MipMapNormalHeight (const byte *in, byte *out, int width, int height, qboolean swizzle) { int i, j; @@ -1481,7 +1452,7 @@ RawImage_ScaleToPower2 =============== */ -static void RawImage_ScaleToPower2( byte **data, int *inout_width, int *inout_height, int *inout_scaled_width, int *inout_scaled_height, imgType_t type, imgFlags_t flags, byte **resampledBuffer) +static qboolean RawImage_ScaleToPower2( byte **data, int *inout_width, int *inout_height, imgType_t type, imgFlags_t flags, byte **resampledBuffer) { int width = *inout_width; int height = *inout_height; @@ -1490,6 +1461,7 @@ static void RawImage_ScaleToPower2( byte **data, int *inout_width, int *inout_he qboolean picmip = flags & IMGFLAG_PICMIP; qboolean mipmap = flags & IMGFLAG_MIPMAP; qboolean clampToEdge = flags & IMGFLAG_CLAMPTOEDGE; + qboolean notScaled; // // convert to exact power of 2 sizes @@ -1536,22 +1508,9 @@ static void RawImage_ScaleToPower2( byte **data, int *inout_width, int *inout_he *resampledBuffer = ri.Hunk_AllocateTempMemory( finalwidth * finalheight * 4 ); if (scaled_width != width || scaled_height != height) - { ResampleTexture (*data, width, height, *resampledBuffer, scaled_width, scaled_height); - } else - { - byte *inbyte, *outbyte; - int i; - - inbyte = *data; - outbyte = *resampledBuffer; - - for (i = width * height * 4; i > 0; i--) - { - *outbyte++ = *inbyte++; - } - } + Com_Memcpy(*resampledBuffer, *data, width * height * 4); if (type == IMGTYPE_COLORALPHA) RGBAtoYCoCgA(*resampledBuffer, *resampledBuffer, scaled_width, scaled_height); @@ -1565,34 +1524,29 @@ static void RawImage_ScaleToPower2( byte **data, int *inout_width, int *inout_he } if (type == IMGTYPE_COLORALPHA) - { YCoCgAtoRGBA(*resampledBuffer, *resampledBuffer, scaled_width, scaled_height); - } else if (type == IMGTYPE_NORMAL || type == IMGTYPE_NORMALHEIGHT) - { FillInNormalizedZ(*resampledBuffer, *resampledBuffer, scaled_width, scaled_height); - } - //endTime = ri.Milliseconds(); //ri.Printf(PRINT_ALL, "upsampled %dx%d to %dx%d in %dms\n", width, height, scaled_width, scaled_height, endTime - startTime); *data = *resampledBuffer; - width = scaled_width; - height = scaled_height; } - else if ( scaled_width != width || scaled_height != height ) { + else if ( scaled_width != width || scaled_height != height ) + { if (data && resampledBuffer) { *resampledBuffer = ri.Hunk_AllocateTempMemory( scaled_width * scaled_height * 4 ); ResampleTexture (*data, width, height, *resampledBuffer, scaled_width, scaled_height); *data = *resampledBuffer; } - width = scaled_width; - height = scaled_height; } + width = scaled_width; + height = scaled_height; + // // perform optional picmip operation // @@ -1601,16 +1555,6 @@ static void RawImage_ScaleToPower2( byte **data, int *inout_width, int *inout_he scaled_height >>= r_picmip->integer; } - // - // clamp to minimum size - // - if (scaled_width < 1) { - scaled_width = 1; - } - if (scaled_height < 1) { - scaled_height = 1; - } - // // clamp to the current upper OpenGL limit // scale both axis down equally so we don't have to @@ -1622,10 +1566,35 @@ static void RawImage_ScaleToPower2( byte **data, int *inout_width, int *inout_he scaled_height >>= 1; } - *inout_width = width; - *inout_height = height; - *inout_scaled_width = scaled_width; - *inout_scaled_height = scaled_height; + // + // clamp to minimum size + // + scaled_width = MAX(1, scaled_width); + scaled_height = MAX(1, scaled_height); + + notScaled = (width == scaled_width) && (height == scaled_height); + + // + // rescale texture to new size using existing mipmap functions + // + if (data) + { + while (width > scaled_width || height > scaled_height) + { + if (type == IMGTYPE_NORMAL || type == IMGTYPE_NORMALHEIGHT) + R_MipMapNormalHeight(*data, *data, width, height, qfalse); + else + R_MipMapsRGB(*data, width, height); + + width = MAX(1, width >> 1); + height = MAX(1, height >> 1); + } + } + + *inout_width = width; + *inout_height = height; + + return notScaled; } @@ -1647,22 +1616,25 @@ static qboolean RawImage_HasAlpha(const byte *scan, int numPixels) return qfalse; } -static GLenum RawImage_GetFormat(const byte *data, int numPixels, qboolean lightMap, imgType_t type, imgFlags_t flags) +static GLenum RawImage_GetFormat(const byte *data, int numPixels, GLenum picFormat, qboolean lightMap, imgType_t type, imgFlags_t flags) { int samples = 3; GLenum internalFormat = GL_RGB; qboolean forceNoCompression = (flags & IMGFLAG_NO_COMPRESSION); qboolean normalmap = (type == IMGTYPE_NORMAL || type == IMGTYPE_NORMALHEIGHT); + if (picFormat != GL_RGBA8) + return picFormat; + if(normalmap) { - if ((!RawImage_HasAlpha(data, numPixels) || (type == IMGTYPE_NORMAL)) && !forceNoCompression && (glRefConfig.textureCompression & TCR_LATC)) + if ((type == IMGTYPE_NORMALHEIGHT) && RawImage_HasAlpha(data, numPixels)) { - internalFormat = GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT; - } - else - { - if ( !forceNoCompression && glConfig.textureCompression == TC_S3TC_ARB ) + if (!forceNoCompression && glRefConfig.textureCompression & TCR_BPTC) + { + internalFormat = GL_COMPRESSED_RGBA_BPTC_UNORM_ARB; + } + else if (!forceNoCompression && glConfig.textureCompression == TC_S3TC_ARB) { internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; } @@ -1679,6 +1651,33 @@ static GLenum RawImage_GetFormat(const byte *data, int numPixels, qboolean light internalFormat = GL_RGBA; } } + else + { + if (!forceNoCompression && glRefConfig.textureCompression & TCR_RGTC) + { + internalFormat = GL_COMPRESSED_RG_RGTC2; + } + else if (!forceNoCompression && glRefConfig.textureCompression & TCR_BPTC) + { + internalFormat = GL_COMPRESSED_RGBA_BPTC_UNORM_ARB; + } + else if (!forceNoCompression && glConfig.textureCompression == TC_S3TC_ARB) + { + internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + } + else if (r_texturebits->integer == 16) + { + internalFormat = GL_RGB5; + } + else if (r_texturebits->integer == 32) + { + internalFormat = GL_RGB8; + } + else + { + internalFormat = GL_RGB; + } + } } else if(lightMap) { @@ -1774,12 +1773,147 @@ static GLenum RawImage_GetFormat(const byte *data, int numPixels, qboolean light return internalFormat; } +static void CompressMonoBlock(byte outdata[8], const byte indata[16]) +{ + int hi, lo, diff, bias, outbyte, shift, i; + byte *p = outdata; -static void RawImage_UploadTexture( byte *data, int x, int y, int width, int height, GLenum internalFormat, imgType_t type, imgFlags_t flags, qboolean subtexture ) + hi = lo = indata[0]; + for (i = 1; i < 16; i++) + { + hi = MAX(indata[i], hi); + lo = MIN(indata[i], lo); + } + + *p++ = hi; + *p++ = lo; + + diff = hi - lo; + + if (diff == 0) + { + outbyte = (hi == 255) ? 255 : 0; + + for (i = 0; i < 6; i++) + *p++ = outbyte; + + return; + } + + bias = diff / 2 - lo * 7; + outbyte = shift = 0; + for (i = 0; i < 16; i++) + { + const byte fixIndex[8] = { 1, 7, 6, 5, 4, 3, 2, 0 }; + byte index = fixIndex[(indata[i] * 7 + bias) / diff]; + + outbyte |= index << shift; + shift += 3; + if (shift >= 8) + { + *p++ = outbyte & 0xff; + shift -= 8; + outbyte >>= 8; + } + } +} + +static void RawImage_UploadToRgtc2Texture(GLuint texture, byte *data, int width, int height, int mip) +{ + int wBlocks, hBlocks, y, x, size; + byte *compressedData, *p; + + wBlocks = (width + 3) / 4; + hBlocks = (height + 3) / 4; + size = wBlocks * hBlocks * 16; + + p = compressedData = ri.Hunk_AllocateTempMemory(size); + for (y = 0; y < height; y += 4) + { + int oh = MIN(4, height - y); + + for (x = 0; x < width; x += 4) + { + byte workingData[16]; + int component; + + int ow = MIN(4, width - x); + + for (component = 0; component < 2; component++) + { + int ox, oy; + + for (oy = 0; oy < oh; oy++) + for (ox = 0; ox < ow; ox++) + workingData[oy * 4 + ox] = data[((y + oy) * width + x + ox) * 4 + component]; + + // dupe data to fill + for (oy = 0; oy < 4; oy++) + for (ox = (oy < oh) ? ow : 0; ox < 4; ox++) + workingData[oy * 4 + ox] = workingData[(oy % oh) * 4 + ox % ow]; + + CompressMonoBlock(p, workingData); + p += 8; + } + } + } + + qglCompressedTextureImage2D(texture, GL_TEXTURE_2D, mip, GL_COMPRESSED_RG_RGTC2, width, height, 0, size, compressedData); + + ri.Hunk_FreeTempMemory(compressedData); +} + +static int CalculateMipSize(int width, int height, GLenum picFormat) +{ + int numBlocks = ((width + 3) / 4) * ((height + 3) / 4); + int numPixels = width * height; + + switch (picFormat) + { + case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: + case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT: + case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: + case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: + case GL_COMPRESSED_RED_RGTC1: + case GL_COMPRESSED_SIGNED_RED_RGTC1: + return numBlocks * 8; + + case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: + case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT: + case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: + case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: + case GL_COMPRESSED_RG_RGTC2: + case GL_COMPRESSED_SIGNED_RG_RGTC2: + case GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB: + case GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB: + case GL_COMPRESSED_RGBA_BPTC_UNORM_ARB: + case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB: + return numBlocks * 16; + + case GL_RGBA8: + case GL_SRGB8_ALPHA8_EXT: + return numPixels * 4; + + default: + ri.Printf(PRINT_ALL, "Unsupported texture format %08x\n", picFormat); + return 0; + } + + return 0; +} + + +static void RawImage_UploadTexture(GLuint texture, byte *data, int x, int y, int width, int height, GLenum target, GLenum picFormat, int numMips, GLenum internalFormat, imgType_t type, imgFlags_t flags, qboolean subtexture ) { int dataFormat, dataType; + qboolean rgtc = (internalFormat == GL_COMPRESSED_RG_RGTC2); + qboolean compressed = (!(picFormat == GL_RGBA8) || (picFormat == GL_SRGB8_ALPHA8_EXT)); + qboolean mipmap = !!(flags & IMGFLAG_MIPMAP); + qboolean picmip = !!(flags & IMGFLAG_PICMIP); + int size, miplevel; + qboolean lastMip = qfalse; - switch(internalFormat) + switch (internalFormat) { case GL_DEPTH_COMPONENT: case GL_DEPTH_COMPONENT16_ARB: @@ -1798,60 +1932,83 @@ static void RawImage_UploadTexture( byte *data, int x, int y, int width, int hei break; } - if ( subtexture ) - qglTexSubImage2D( GL_TEXTURE_2D, 0, x, y, width, height, dataFormat, dataType, data ); - else - qglTexImage2D (GL_TEXTURE_2D, 0, internalFormat, width, height, 0, dataFormat, dataType, data ); - - if (flags & IMGFLAG_MIPMAP) + if (!data) { - int miplevel; - miplevel = 0; - while (width > 1 || height > 1) + do { - if (data) - { - if (type == IMGTYPE_NORMAL || type == IMGTYPE_NORMALHEIGHT) - { - if (internalFormat == GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT) - { - R_MipMapLuminanceAlpha( data, data, width, height ); - } - else - { - R_MipMapNormalHeight( data, data, width, height, qtrue); - } - } - else - { - R_MipMapsRGB( data, width, height ); - } - } - - width >>= 1; - height >>= 1; - if (width < 1) - width = 1; - if (height < 1) - height = 1; + lastMip = (width == 1 && height == 1) || !mipmap; + qglTextureImage2D(texture, target, miplevel, internalFormat, width, height, 0, dataFormat, dataType, NULL); + + width = MAX(1, width >> 1); + height = MAX(1, height >> 1); miplevel++; + } + while (!lastMip); - if ( data && r_colorMipLevels->integer ) - R_BlendOverTexture( (byte *)data, width * height, mipBlendColors[miplevel] ); + return; + } - if ( subtexture ) - { - x >>= 1; - y >>= 1; - qglTexSubImage2D( GL_TEXTURE_2D, miplevel, x, y, width, height, dataFormat, dataType, data ); - } - else - { - qglTexImage2D (GL_TEXTURE_2D, miplevel, internalFormat, width, height, 0, dataFormat, dataType, data ); - } + if (compressed && picmip) + { + for (miplevel = r_picmip->integer; miplevel > 0 && numMips > 1; miplevel--, numMips--) + { + size = CalculateMipSize(width, height, picFormat); + x >>= 1; + y >>= 1; + width = MAX(1, width >> 1); + height = MAX(1, height >> 1); + data += size; } } + + miplevel = 0; + do + { + lastMip = (width == 1 && height == 1) || !mipmap; + size = CalculateMipSize(width, height, picFormat); + + if (compressed) + { + if (subtexture) + qglCompressedTextureSubImage2D(texture, target, miplevel, x, y, width, height, picFormat, size, data); + else + qglCompressedTextureImage2D(texture, target, miplevel, picFormat, width, height, 0, size, data); + } + else + { + if (miplevel != 0 && r_colorMipLevels->integer) + R_BlendOverTexture((byte *)data, width * height, mipBlendColors[miplevel]); + + if (rgtc) + RawImage_UploadToRgtc2Texture(texture, data, width, height, miplevel); + else if (subtexture) + qglTextureSubImage2D(texture, target, miplevel, x, y, width, height, dataFormat, dataType, data); + else + qglTextureImage2D(texture, target, miplevel, internalFormat, width, height, 0, dataFormat, dataType, data); + + if (!lastMip && numMips < 2) + { + if (type == IMGTYPE_NORMAL || type == IMGTYPE_NORMALHEIGHT) + R_MipMapNormalHeight(data, data, width, height, glRefConfig.swizzleNormalmap); + else + R_MipMapsRGB(data, width, height); + } + } + + x >>= 1; + y >>= 1; + width = MAX(1, width >> 1); + height = MAX(1, height >> 1); + miplevel++; + + if (numMips > 1) + { + data += size; + numMips--; + } + } + while (!lastMip); } @@ -1861,23 +2018,51 @@ Upload32 =============== */ -static void Upload32( byte *data, int width, int height, imgType_t type, imgFlags_t flags, - qboolean lightMap, GLenum internalFormat, int *pUploadWidth, int *pUploadHeight) +static void Upload32(byte *data, int x, int y, int width, int height, GLenum picFormat, int numMips, image_t *image) { - byte *scaledBuffer = NULL; byte *resampledBuffer = NULL; - int scaled_width, scaled_height; int i, c; byte *scan; - RawImage_ScaleToPower2(&data, &width, &height, &scaled_width, &scaled_height, type, flags, &resampledBuffer); + imgType_t type = image->type; + imgFlags_t flags = image->flags; + GLenum internalFormat = image->internalFormat; + qboolean subtexture = (x != 0) || (y != 0) || (width != image->width) || (height != image->height); + qboolean notScaled = qtrue; + qboolean compressed = (picFormat != GL_RGBA8 && picFormat != GL_SRGB8_ALPHA8_EXT); + qboolean mipmap = !!(flags & IMGFLAG_MIPMAP) && (!compressed || numMips > 1); + qboolean cubemap = !!(flags & IMGFLAG_CUBEMAP); + GLenum uploadTarget = cubemap ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : GL_TEXTURE_2D; + GLenum textureTarget = cubemap ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D; + int depth = cubemap ? 6 : 1; - scaledBuffer = ri.Hunk_AllocateTempMemory( sizeof( unsigned ) * scaled_width * scaled_height ); + if (!data) + { + RawImage_ScaleToPower2(NULL, &width, &height, type, flags, NULL); + for (i = 0; i < depth; i++) + RawImage_UploadTexture(image->texnum, NULL, 0, 0, width, height, uploadTarget + i, GL_RGBA8, 0, internalFormat, type, flags, qfalse); + goto done; + } + else if (!subtexture) + { + if (compressed || cubemap) + { + for (i = 0; i < depth; i++) + { + int w2 = width, h2 = height; + RawImage_UploadTexture(image->texnum, data, 0, 0, width, height, uploadTarget + i, picFormat, numMips, internalFormat, type, flags, qfalse); + for (c = numMips; c; c--) + { + data += CalculateMipSize(w2, h2, picFormat); + w2 = MAX(1, w2 >> 1); + h2 = MAX(1, h2 >> 1); + } + } + goto done; + } + notScaled = RawImage_ScaleToPower2(&data, &width, &height, type, flags, &resampledBuffer); + } - // - // scan the texture for each channel's max values - // and verify if the alpha channel is being used or not - // c = width*height; scan = data; @@ -1902,137 +2087,44 @@ static void Upload32( byte *data, int width, int height, imgType_t type, imgFlag } } - // normals are always swizzled - if (type == IMGTYPE_NORMAL || type == IMGTYPE_NORMALHEIGHT) - { + if (glRefConfig.swizzleNormalmap && (type == IMGTYPE_NORMAL || type == IMGTYPE_NORMALHEIGHT)) RawImage_SwizzleRA(data, width, height); - } - // LATC2 is only used for normals - if (internalFormat == GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT) + // This corresponds to what the OpenGL1 renderer does + if (!(flags & IMGFLAG_NOLIGHTSCALE) && (!notScaled || mipmap)) + R_LightScaleTexture(data, width, height, !mipmap); + + if (subtexture) { - byte *in = data; - int c = width * height; - while (c--) - { - in[0] = in[1]; - in[2] = in[1]; - in += 4; - } + // FIXME: Incorrect if original texture was not a power of 2 texture or picmipped + RawImage_UploadTexture(image->texnum, data, x, y, width, height, uploadTarget, GL_RGBA8, 0, internalFormat, type, flags, qtrue); + GL_CheckErrors(); + return; } - // copy or resample data as appropriate for first MIP level - if ( ( scaled_width == width ) && - ( scaled_height == height ) ) { - if (!(flags & IMGFLAG_MIPMAP)) - { - RawImage_UploadTexture( data, 0, 0, scaled_width, scaled_height, internalFormat, type, flags, qfalse ); - //qglTexImage2D (GL_TEXTURE_2D, 0, internalFormat, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); - *pUploadWidth = scaled_width; - *pUploadHeight = scaled_height; - - goto done; - } - Com_Memcpy (scaledBuffer, data, width*height*4); - } - else - { - // use the normal mip-mapping function to go down from here - while ( width > scaled_width || height > scaled_height ) { - - if (type == IMGTYPE_NORMAL || type == IMGTYPE_NORMALHEIGHT) - { - if (internalFormat == GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT) - { - R_MipMapLuminanceAlpha( data, data, width, height ); - } - else - { - R_MipMapNormalHeight( data, data, width, height, qtrue); - } - } - else - { - R_MipMapsRGB( data, width, height ); - } - - width >>= 1; - height >>= 1; - if ( width < 1 ) { - width = 1; - } - if ( height < 1 ) { - height = 1; - } - } - Com_Memcpy( scaledBuffer, data, width * height * 4 ); - } - - if (!(flags & IMGFLAG_NOLIGHTSCALE)) - R_LightScaleTexture (scaledBuffer, scaled_width, scaled_height, !(flags & IMGFLAG_MIPMAP) ); - - *pUploadWidth = scaled_width; - *pUploadHeight = scaled_height; - - RawImage_UploadTexture(scaledBuffer, 0, 0, scaled_width, scaled_height, internalFormat, type, flags, qfalse); + RawImage_UploadTexture(image->texnum, data, 0, 0, width, height, uploadTarget, GL_RGBA8, 0, internalFormat, type, flags, qfalse); done: - if (flags & IMGFLAG_MIPMAP) - { - if ( textureFilterAnisotropic ) - qglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, - (GLint)Com_Clamp( 1, maxAnisotropy, r_ext_max_anisotropy->integer ) ); + image->uploadWidth = width; + image->uploadHeight = height; - qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); - qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); + if (mipmap) + { + if (textureFilterAnisotropic && !cubemap) + qglTextureParameteri(image->texnum, textureTarget, GL_TEXTURE_MAX_ANISOTROPY_EXT, + (GLint)Com_Clamp(1, maxAnisotropy, r_ext_max_anisotropy->integer)); + + qglTextureParameterf(image->texnum, textureTarget, GL_TEXTURE_MIN_FILTER, gl_filter_min); + qglTextureParameterf(image->texnum, textureTarget, GL_TEXTURE_MAG_FILTER, gl_filter_max); } else { - if ( textureFilterAnisotropic ) - qglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1 ); + if (textureFilterAnisotropic && !cubemap) + qglTextureParameteri(image->texnum, textureTarget, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1); - qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); - qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); - } - - GL_CheckErrors(); - - if ( scaledBuffer != 0 ) - ri.Hunk_FreeTempMemory( scaledBuffer ); - if ( resampledBuffer != 0 ) - ri.Hunk_FreeTempMemory( resampledBuffer ); -} - - -static void EmptyTexture( int width, int height, imgType_t type, imgFlags_t flags, - qboolean lightMap, GLenum internalFormat, int *pUploadWidth, int *pUploadHeight ) -{ - int scaled_width, scaled_height; - - RawImage_ScaleToPower2(NULL, &width, &height, &scaled_width, &scaled_height, type, flags, NULL); - - *pUploadWidth = scaled_width; - *pUploadHeight = scaled_height; - - RawImage_UploadTexture(NULL, 0, 0, scaled_width, scaled_height, internalFormat, type, flags, qfalse); - - if (flags & IMGFLAG_MIPMAP) - { - if ( textureFilterAnisotropic ) - qglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, - (GLint)Com_Clamp( 1, maxAnisotropy, r_ext_max_anisotropy->integer ) ); - - qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); - qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); - } - else - { - if ( textureFilterAnisotropic ) - qglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1 ); - - qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); - qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + qglTextureParameterf(image->texnum, textureTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + qglTextureParameterf(image->texnum, textureTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } // Fix for sampling depth buffer on old nVidia cards @@ -2043,26 +2135,29 @@ static void EmptyTexture( int width, int height, imgType_t type, imgFlags_t flag case GL_DEPTH_COMPONENT16_ARB: case GL_DEPTH_COMPONENT24_ARB: case GL_DEPTH_COMPONENT32_ARB: - qglTexParameterf(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_LUMINANCE ); - qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); - qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + qglTextureParameterf(image->texnum, textureTarget, GL_DEPTH_TEXTURE_MODE, GL_LUMINANCE); + qglTextureParameterf(image->texnum, textureTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + qglTextureParameterf(image->texnum, textureTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST); break; default: break; } GL_CheckErrors(); + + if ( resampledBuffer != NULL ) + ri.Hunk_FreeTempMemory( resampledBuffer ); } /* ================ -R_CreateImage +R_CreateImage2 This is the only way any image_t are created ================ */ -image_t *R_CreateImage( const char *name, byte *pic, int width, int height, imgType_t type, imgFlags_t flags, int internalFormat ) { +image_t *R_CreateImage2( const char *name, byte *pic, int width, int height, GLenum picFormat, int numMips, imgType_t type, imgFlags_t flags, int internalFormat ) { image_t *image; qboolean isLightmap = qfalse; long hash; @@ -2096,81 +2191,24 @@ image_t *R_CreateImage( const char *name, byte *pic, int width, int height, imgT glWrapClampMode = GL_REPEAT; if (!internalFormat) - { - if (image->flags & IMGFLAG_CUBEMAP) - internalFormat = GL_RGBA8; - else - internalFormat = RawImage_GetFormat(pic, width * height, isLightmap, image->type, image->flags); - } + internalFormat = RawImage_GetFormat(pic, width * height, picFormat, isLightmap, image->type, image->flags); image->internalFormat = internalFormat; - - // lightmaps are always allocated on TMU 1 - if ( qglActiveTextureARB && isLightmap ) { - image->TMU = 1; - } else { - image->TMU = 0; - } - - if ( qglActiveTextureARB ) { - GL_SelectTexture( image->TMU ); - } + Upload32(pic, 0, 0, image->width, image->height, picFormat, numMips, image); if (image->flags & IMGFLAG_CUBEMAP) { - GL_Bind(image); - qglTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - qglTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - qglTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); - - qglTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - if (image->flags & IMGFLAG_MIPMAP) - { - qglTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - } - else - { - qglTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - } - - qglTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pic); - qglTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pic); - qglTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pic); - qglTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pic); - qglTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pic); - qglTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pic); - - if (image->flags & IMGFLAG_MIPMAP) - qglGenerateMipmapEXT(GL_TEXTURE_CUBE_MAP); - - image->uploadWidth = width; - image->uploadHeight = height; + qglTextureParameterf(image->texnum, GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, glWrapClampMode); + qglTextureParameterf(image->texnum, GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, glWrapClampMode); + qglTextureParameteri(image->texnum, GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, glWrapClampMode); } else { - GL_Bind(image); - - if (pic) - { - Upload32( pic, image->width, image->height, image->type, image->flags, - isLightmap, image->internalFormat, &image->uploadWidth, - &image->uploadHeight ); - } - else - { - EmptyTexture(image->width, image->height, image->type, image->flags, - isLightmap, image->internalFormat, &image->uploadWidth, - &image->uploadHeight ); - } - - qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, glWrapClampMode ); - qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, glWrapClampMode ); + qglTextureParameterf(image->texnum, GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, glWrapClampMode); + qglTextureParameterf(image->texnum, GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, glWrapClampMode); } - GL_SelectTexture( 0 ); - hash = generateHashValue(name); image->next = hashTable[hash]; hashTable[hash] = image; @@ -2178,114 +2216,30 @@ image_t *R_CreateImage( const char *name, byte *pic, int width, int height, imgT return image; } + +/* +================ +R_CreateImage + +Wrapper for R_CreateImage2(), for the old parameters. +================ +*/ +image_t *R_CreateImage(const char *name, byte *pic, int width, int height, imgType_t type, imgFlags_t flags, int internalFormat) +{ + return R_CreateImage2(name, pic, width, height, GL_RGBA8, 0, type, flags, internalFormat); +} + + void R_UpdateSubImage( image_t *image, byte *pic, int x, int y, int width, int height ) { - byte *scaledBuffer = NULL; - byte *resampledBuffer = NULL; - int scaled_width, scaled_height, scaled_x, scaled_y; - byte *data = pic; - - // normals are always swizzled - if (image->type == IMGTYPE_NORMAL || image->type == IMGTYPE_NORMALHEIGHT) - { - RawImage_SwizzleRA(pic, width, height); - } - - // LATC2 is only used for normals - if (image->internalFormat == GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT) - { - byte *in = data; - int c = width * height; - while (c--) - { - in[0] = in[1]; - in[2] = in[1]; - in += 4; - } - } - - - RawImage_ScaleToPower2(&pic, &width, &height, &scaled_width, &scaled_height, image->type, image->flags, &resampledBuffer); - - scaledBuffer = ri.Hunk_AllocateTempMemory( sizeof( unsigned ) * scaled_width * scaled_height ); - - if ( qglActiveTextureARB ) { - GL_SelectTexture( image->TMU ); - } - - GL_Bind(image); - - // copy or resample data as appropriate for first MIP level - if ( ( scaled_width == width ) && - ( scaled_height == height ) ) { - if (!(image->flags & IMGFLAG_MIPMAP)) - { - scaled_x = x * scaled_width / width; - scaled_y = y * scaled_height / height; - RawImage_UploadTexture( data, scaled_x, scaled_y, scaled_width, scaled_height, image->internalFormat, image->type, image->flags, qtrue ); - //qglTexSubImage2D( GL_TEXTURE_2D, 0, scaled_x, scaled_y, scaled_width, scaled_height, GL_RGBA, GL_UNSIGNED_BYTE, data ); - - GL_CheckErrors(); - goto done; - } - Com_Memcpy (scaledBuffer, data, width*height*4); - } - else - { - // use the normal mip-mapping function to go down from here - while ( width > scaled_width || height > scaled_height ) { - - if (image->type == IMGTYPE_NORMAL || image->type == IMGTYPE_NORMALHEIGHT) - { - if (image->internalFormat == GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT) - { - R_MipMapLuminanceAlpha( data, data, width, height ); - } - else - { - R_MipMapNormalHeight( data, data, width, height, qtrue); - } - } - else - { - R_MipMapsRGB( data, width, height ); - } - - width >>= 1; - height >>= 1; - x >>= 1; - y >>= 1; - if ( width < 1 ) { - width = 1; - } - if ( height < 1 ) { - height = 1; - } - } - Com_Memcpy( scaledBuffer, data, width * height * 4 ); - } - - if (!(image->flags & IMGFLAG_NOLIGHTSCALE)) - R_LightScaleTexture (scaledBuffer, scaled_width, scaled_height, !(image->flags & IMGFLAG_MIPMAP) ); - - scaled_x = x * scaled_width / width; - scaled_y = y * scaled_height / height; - RawImage_UploadTexture( (byte *)data, scaled_x, scaled_y, scaled_width, scaled_height, image->internalFormat, image->type, image->flags, qtrue ); - -done: - - GL_SelectTexture( 0 ); - - GL_CheckErrors(); - - if ( scaledBuffer != 0 ) - ri.Hunk_FreeTempMemory( scaledBuffer ); - if ( resampledBuffer != 0 ) - ri.Hunk_FreeTempMemory( resampledBuffer ); + Upload32(pic, x, y, width, height, GL_RGBA8, 0, image); } //=================================================================== +// Prototype for dds loader function which isn't common to both renderers +void R_LoadDDS(const char *filename, byte **pic, int *width, int *height, GLenum *picFormat, int *numMips); + typedef struct { char *ext; @@ -2314,7 +2268,7 @@ Loads any of the supported image types into a cannonical 32 bit format. ================= */ -void R_LoadImage( const char *name, byte **pic, int *width, int *height ) +void R_LoadImage( const char *name, byte **pic, int *width, int *height, GLenum *picFormat, int *numMips ) { qboolean orgNameFailed = qfalse; int orgLoader = -1; @@ -2326,11 +2280,28 @@ void R_LoadImage( const char *name, byte **pic, int *width, int *height ) *pic = NULL; *width = 0; *height = 0; + *picFormat = GL_RGBA8; + *numMips = 0; Q_strncpyz( localName, name, MAX_QPATH ); ext = COM_GetExtension( localName ); + // If compressed textures are enabled, try loading a DDS first, it'll load fastest + if (r_ext_compressed_textures->integer) + { + char ddsName[MAX_QPATH]; + + COM_StripExtension(name, ddsName, MAX_QPATH); + Q_strcat(ddsName, MAX_QPATH, ".dds"); + + R_LoadDDS(ddsName, pic, width, height, picFormat, numMips); + + // If loaded, we're done. + if (*pic) + return; + } + if( *ext ) { // Look for the correct loader and use it @@ -2402,7 +2373,10 @@ image_t *R_FindImageFile( const char *name, imgType_t type, imgFlags_t flags ) image_t *image; int width, height; byte *pic; + GLenum picFormat; + int picNumMips; long hash; + imgFlags_t checkFlagsTrue, checkFlagsFalse; if (!name) { return NULL; @@ -2428,12 +2402,15 @@ image_t *R_FindImageFile( const char *name, imgType_t type, imgFlags_t flags ) // // load the pic from disk // - R_LoadImage( name, &pic, &width, &height ); + R_LoadImage( name, &pic, &width, &height, &picFormat, &picNumMips ); if ( pic == NULL ) { return NULL; } - if (r_normalMapping->integer && !(type == IMGTYPE_NORMAL) && (flags & IMGFLAG_PICMIP) && (flags & IMGFLAG_MIPMAP) && (flags & IMGFLAG_GENNORMALMAP)) + checkFlagsTrue = IMGFLAG_PICMIP | IMGFLAG_MIPMAP | IMGFLAG_GENNORMALMAP; + checkFlagsFalse = IMGFLAG_CUBEMAP; + if (r_normalMapping->integer && (picFormat == GL_RGBA8) && (type == IMGTYPE_COLORALPHA) && + ((flags & checkFlagsTrue) == checkFlagsTrue) && !(flags & checkFlagsFalse)) { char normalName[MAX_QPATH]; image_t *normalImage; @@ -2536,7 +2513,21 @@ image_t *R_FindImageFile( const char *name, imgType_t type, imgFlags_t flags ) } } - image = R_CreateImage( ( char * ) name, pic, width, height, type, flags, 0 ); + // force mipmaps off if image is compressed but doesn't have enough mips + if ((flags & IMGFLAG_MIPMAP) && picFormat != GL_RGBA8 && picFormat != GL_SRGB8_ALPHA8_EXT) + { + int wh = MAX(width, height); + int neededMips = 0; + while (wh) + { + neededMips++; + wh >>= 1; + } + if (neededMips > picNumMips) + flags &= ~IMGFLAG_MIPMAP; + } + + image = R_CreateImage2( ( char * ) name, pic, width, height, picFormat, picNumMips, type, flags, 0 ); ri.Free( pic ); return image; } @@ -2826,11 +2817,8 @@ void R_CreateBuiltinImages( void ) { for ( x = 0; x < 4; x++) { tr.sunShadowDepthImage[x] = R_CreateImage(va("*sunshadowdepth%i", x), NULL, r_shadowMapSize->integer, r_shadowMapSize->integer, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, GL_DEPTH_COMPONENT24_ARB); - GL_Bind(tr.sunShadowDepthImage[x]); - qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); - qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); - qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE); - qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); + qglTextureParameterf(tr.sunShadowDepthImage[x]->texnum, GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE); + qglTextureParameterf(tr.sunShadowDepthImage[x]->texnum, GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); } tr.screenShadowImage = R_CreateImage("*screenShadow", NULL, width, height, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, GL_RGBA8); @@ -2838,7 +2826,7 @@ void R_CreateBuiltinImages( void ) { if (r_cubeMapping->integer) { - tr.renderCubeImage = R_CreateImage("*renderCube", NULL, CUBE_MAP_SIZE, CUBE_MAP_SIZE, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE | IMGFLAG_MIPMAP | IMGFLAG_CUBEMAP, rgbFormat); + tr.renderCubeImage = R_CreateImage("*renderCube", NULL, r_cubemapSize->integer, r_cubemapSize->integer, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE | IMGFLAG_MIPMAP | IMGFLAG_CUBEMAP, rgbFormat); } } } @@ -2855,7 +2843,11 @@ void R_SetColorMappings( void ) { int inf; // setup the overbright lighting +#if defined(USE_OVERBRIGHT) tr.overbrightBits = r_overBrightBits->integer; +#else + tr.overbrightBits = 0; +#endif // allow 2 overbright bits if ( tr.overbrightBits > 2 ) { @@ -2864,6 +2856,11 @@ void R_SetColorMappings( void ) { tr.overbrightBits = 0; } + // don't allow more overbright bits than map overbright bits + if ( tr.overbrightBits > r_mapOverBrightBits->integer ) { + tr.overbrightBits = r_mapOverBrightBits->integer; + } + tr.identityLight = 1.0f / ( 1 << tr.overbrightBits ); tr.identityLightByte = 255 * tr.identityLight; @@ -2939,15 +2936,7 @@ void R_DeleteTextures( void ) { tr.numImages = 0; - Com_Memset( glState.currenttextures, 0, sizeof( glState.currenttextures ) ); - if ( qglActiveTextureARB ) { - GL_SelectTexture( 1 ); - qglBindTexture( GL_TEXTURE_2D, 0 ); - GL_SelectTexture( 0 ); - qglBindTexture( GL_TEXTURE_2D, 0 ); - } else { - qglBindTexture( GL_TEXTURE_2D, 0 ); - } + GL_BindNullTextures(); } /* diff --git a/code/renderergl2/tr_image_dds.c b/code/renderergl2/tr_image_dds.c new file mode 100644 index 00000000..b509014d --- /dev/null +++ b/code/renderergl2/tr_image_dds.c @@ -0,0 +1,498 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + 2015 James Canete + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +#include "../renderercommon/tr_common.h" + +typedef unsigned int ui32_t; + +typedef struct ddsHeader_s +{ + ui32_t headerSize; + ui32_t flags; + ui32_t height; + ui32_t width; + ui32_t pitchOrFirstMipSize; + ui32_t volumeDepth; + ui32_t numMips; + ui32_t reserved1[11]; + ui32_t always_0x00000020; + ui32_t pixelFormatFlags; + ui32_t fourCC; + ui32_t rgbBitCount; + ui32_t rBitMask; + ui32_t gBitMask; + ui32_t bBitMask; + ui32_t aBitMask; + ui32_t caps; + ui32_t caps2; + ui32_t caps3; + ui32_t caps4; + ui32_t reserved2; +} +ddsHeader_t; + +// flags: +#define _DDSFLAGS_REQUIRED 0x001007 +#define _DDSFLAGS_PITCH 0x8 +#define _DDSFLAGS_MIPMAPCOUNT 0x20000 +#define _DDSFLAGS_FIRSTMIPSIZE 0x80000 +#define _DDSFLAGS_VOLUMEDEPTH 0x800000 + +// pixelFormatFlags: +#define DDSPF_ALPHAPIXELS 0x1 +#define DDSPF_ALPHA 0x2 +#define DDSPF_FOURCC 0x4 +#define DDSPF_RGB 0x40 +#define DDSPF_YUV 0x200 +#define DDSPF_LUMINANCE 0x20000 + +// caps: +#define DDSCAPS_COMPLEX 0x8 +#define DDSCAPS_MIPMAP 0x400000 +#define DDSCAPS_REQUIRED 0x1000 + +// caps2: +#define DDSCAPS2_CUBEMAP 0xFE00 +#define DDSCAPS2_VOLUME 0x200000 + +typedef struct ddsHeaderDxt10_s +{ + ui32_t dxgiFormat; + ui32_t dimensions; + ui32_t miscFlags; + ui32_t arraySize; + ui32_t miscFlags2; +} +ddsHeaderDxt10_t; + +// dxgiFormat +// from http://msdn.microsoft.com/en-us/library/windows/desktop/bb173059%28v=vs.85%29.aspx +typedef enum DXGI_FORMAT { + DXGI_FORMAT_UNKNOWN = 0, + DXGI_FORMAT_R32G32B32A32_TYPELESS = 1, + DXGI_FORMAT_R32G32B32A32_FLOAT = 2, + DXGI_FORMAT_R32G32B32A32_UINT = 3, + DXGI_FORMAT_R32G32B32A32_SINT = 4, + DXGI_FORMAT_R32G32B32_TYPELESS = 5, + DXGI_FORMAT_R32G32B32_FLOAT = 6, + DXGI_FORMAT_R32G32B32_UINT = 7, + DXGI_FORMAT_R32G32B32_SINT = 8, + DXGI_FORMAT_R16G16B16A16_TYPELESS = 9, + DXGI_FORMAT_R16G16B16A16_FLOAT = 10, + DXGI_FORMAT_R16G16B16A16_UNORM = 11, + DXGI_FORMAT_R16G16B16A16_UINT = 12, + DXGI_FORMAT_R16G16B16A16_SNORM = 13, + DXGI_FORMAT_R16G16B16A16_SINT = 14, + DXGI_FORMAT_R32G32_TYPELESS = 15, + DXGI_FORMAT_R32G32_FLOAT = 16, + DXGI_FORMAT_R32G32_UINT = 17, + DXGI_FORMAT_R32G32_SINT = 18, + DXGI_FORMAT_R32G8X24_TYPELESS = 19, + DXGI_FORMAT_D32_FLOAT_S8X24_UINT = 20, + DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS = 21, + DXGI_FORMAT_X32_TYPELESS_G8X24_UINT = 22, + DXGI_FORMAT_R10G10B10A2_TYPELESS = 23, + DXGI_FORMAT_R10G10B10A2_UNORM = 24, + DXGI_FORMAT_R10G10B10A2_UINT = 25, + DXGI_FORMAT_R11G11B10_FLOAT = 26, + DXGI_FORMAT_R8G8B8A8_TYPELESS = 27, + DXGI_FORMAT_R8G8B8A8_UNORM = 28, + DXGI_FORMAT_R8G8B8A8_UNORM_SRGB = 29, + DXGI_FORMAT_R8G8B8A8_UINT = 30, + DXGI_FORMAT_R8G8B8A8_SNORM = 31, + DXGI_FORMAT_R8G8B8A8_SINT = 32, + DXGI_FORMAT_R16G16_TYPELESS = 33, + DXGI_FORMAT_R16G16_FLOAT = 34, + DXGI_FORMAT_R16G16_UNORM = 35, + DXGI_FORMAT_R16G16_UINT = 36, + DXGI_FORMAT_R16G16_SNORM = 37, + DXGI_FORMAT_R16G16_SINT = 38, + DXGI_FORMAT_R32_TYPELESS = 39, + DXGI_FORMAT_D32_FLOAT = 40, + DXGI_FORMAT_R32_FLOAT = 41, + DXGI_FORMAT_R32_UINT = 42, + DXGI_FORMAT_R32_SINT = 43, + DXGI_FORMAT_R24G8_TYPELESS = 44, + DXGI_FORMAT_D24_UNORM_S8_UINT = 45, + DXGI_FORMAT_R24_UNORM_X8_TYPELESS = 46, + DXGI_FORMAT_X24_TYPELESS_G8_UINT = 47, + DXGI_FORMAT_R8G8_TYPELESS = 48, + DXGI_FORMAT_R8G8_UNORM = 49, + DXGI_FORMAT_R8G8_UINT = 50, + DXGI_FORMAT_R8G8_SNORM = 51, + DXGI_FORMAT_R8G8_SINT = 52, + DXGI_FORMAT_R16_TYPELESS = 53, + DXGI_FORMAT_R16_FLOAT = 54, + DXGI_FORMAT_D16_UNORM = 55, + DXGI_FORMAT_R16_UNORM = 56, + DXGI_FORMAT_R16_UINT = 57, + DXGI_FORMAT_R16_SNORM = 58, + DXGI_FORMAT_R16_SINT = 59, + DXGI_FORMAT_R8_TYPELESS = 60, + DXGI_FORMAT_R8_UNORM = 61, + DXGI_FORMAT_R8_UINT = 62, + DXGI_FORMAT_R8_SNORM = 63, + DXGI_FORMAT_R8_SINT = 64, + DXGI_FORMAT_A8_UNORM = 65, + DXGI_FORMAT_R1_UNORM = 66, + DXGI_FORMAT_R9G9B9E5_SHAREDEXP = 67, + DXGI_FORMAT_R8G8_B8G8_UNORM = 68, + DXGI_FORMAT_G8R8_G8B8_UNORM = 69, + DXGI_FORMAT_BC1_TYPELESS = 70, + DXGI_FORMAT_BC1_UNORM = 71, + DXGI_FORMAT_BC1_UNORM_SRGB = 72, + DXGI_FORMAT_BC2_TYPELESS = 73, + DXGI_FORMAT_BC2_UNORM = 74, + DXGI_FORMAT_BC2_UNORM_SRGB = 75, + DXGI_FORMAT_BC3_TYPELESS = 76, + DXGI_FORMAT_BC3_UNORM = 77, + DXGI_FORMAT_BC3_UNORM_SRGB = 78, + DXGI_FORMAT_BC4_TYPELESS = 79, + DXGI_FORMAT_BC4_UNORM = 80, + DXGI_FORMAT_BC4_SNORM = 81, + DXGI_FORMAT_BC5_TYPELESS = 82, + DXGI_FORMAT_BC5_UNORM = 83, + DXGI_FORMAT_BC5_SNORM = 84, + DXGI_FORMAT_B5G6R5_UNORM = 85, + DXGI_FORMAT_B5G5R5A1_UNORM = 86, + DXGI_FORMAT_B8G8R8A8_UNORM = 87, + DXGI_FORMAT_B8G8R8X8_UNORM = 88, + DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM = 89, + DXGI_FORMAT_B8G8R8A8_TYPELESS = 90, + DXGI_FORMAT_B8G8R8A8_UNORM_SRGB = 91, + DXGI_FORMAT_B8G8R8X8_TYPELESS = 92, + DXGI_FORMAT_B8G8R8X8_UNORM_SRGB = 93, + DXGI_FORMAT_BC6H_TYPELESS = 94, + DXGI_FORMAT_BC6H_UF16 = 95, + DXGI_FORMAT_BC6H_SF16 = 96, + DXGI_FORMAT_BC7_TYPELESS = 97, + DXGI_FORMAT_BC7_UNORM = 98, + DXGI_FORMAT_BC7_UNORM_SRGB = 99, + DXGI_FORMAT_AYUV = 100, + DXGI_FORMAT_Y410 = 101, + DXGI_FORMAT_Y416 = 102, + DXGI_FORMAT_NV12 = 103, + DXGI_FORMAT_P010 = 104, + DXGI_FORMAT_P016 = 105, + DXGI_FORMAT_420_OPAQUE = 106, + DXGI_FORMAT_YUY2 = 107, + DXGI_FORMAT_Y210 = 108, + DXGI_FORMAT_Y216 = 109, + DXGI_FORMAT_NV11 = 110, + DXGI_FORMAT_AI44 = 111, + DXGI_FORMAT_IA44 = 112, + DXGI_FORMAT_P8 = 113, + DXGI_FORMAT_A8P8 = 114, + DXGI_FORMAT_B4G4R4A4_UNORM = 115, + DXGI_FORMAT_FORCE_UINT = 0xffffffffUL +} DXGI_FORMAT; + +#define EncodeFourCC(x) ((((ui32_t)((x)[0])) ) | \ + (((ui32_t)((x)[1])) << 8 ) | \ + (((ui32_t)((x)[2])) << 16) | \ + (((ui32_t)((x)[3])) << 24) ) + + +void R_LoadDDS ( const char *filename, byte **pic, int *width, int *height, GLenum *picFormat, int *numMips ) +{ + union { + byte *b; + void *v; + } buffer; + int len; + ddsHeader_t *ddsHeader = NULL; + ddsHeaderDxt10_t *ddsHeaderDxt10 = NULL; + byte *data; + + if (!picFormat) + { + ri.Printf(PRINT_ERROR, "R_LoadDDS() called without picFormat parameter!"); + return; + } + + if (width) + *width = 0; + if (height) + *height = 0; + if (picFormat) + *picFormat = GL_RGBA8; + if (numMips) + *numMips = 1; + + *pic = NULL; + + // + // load the file + // + len = ri.FS_ReadFile( ( char * ) filename, &buffer.v); + if (!buffer.b || len < 0) { + return; + } + + // + // reject files that are too small to hold even a header + // + if (len < 4 + sizeof(*ddsHeader)) + { + ri.Printf(PRINT_ALL, "File %s is too small to be a DDS file.\n", filename); + ri.FS_FreeFile(buffer.v); + return; + } + + // + // reject files that don't start with "DDS " + // + if (*((ui32_t *)(buffer.b)) != EncodeFourCC("DDS ")) + { + ri.Printf(PRINT_ALL, "File %s is not a DDS file.\n", filename); + ri.FS_FreeFile(buffer.v); + return; + } + + // + // parse header and dx10 header if available + // + ddsHeader = (ddsHeader_t *)(buffer.b + 4); + if ((ddsHeader->pixelFormatFlags & DDSPF_FOURCC) && ddsHeader->fourCC == EncodeFourCC("DX10")) + { + if (len < 4 + sizeof(*ddsHeader) + sizeof(*ddsHeaderDxt10)) + { + ri.Printf(PRINT_ALL, "File %s indicates a DX10 header it is too small to contain.\n", filename); + ri.FS_FreeFile(buffer.v); + return; + } + + ddsHeaderDxt10 = (ddsHeaderDxt10_t *)(buffer.b + 4 + sizeof(ddsHeader_t)); + data = buffer.b + 4 + sizeof(*ddsHeader) + sizeof(*ddsHeaderDxt10); + len -= 4 + sizeof(*ddsHeader) + sizeof(*ddsHeaderDxt10); + } + else + { + data = buffer.b + 4 + sizeof(*ddsHeader); + len -= 4 + sizeof(*ddsHeader); + } + + if (width) + *width = ddsHeader->width; + if (height) + *height = ddsHeader->height; + + if (numMips) + { + if (ddsHeader->flags & _DDSFLAGS_MIPMAPCOUNT) + *numMips = ddsHeader->numMips; + else + *numMips = 1; + } + + // FIXME: handle cube map + //if ((ddsHeader->caps2 & DDSCAPS2_CUBEMAP) == DDSCAPS2_CUBEMAP) + + // + // Convert DXGI format/FourCC into OpenGL format + // + if (ddsHeaderDxt10) + { + switch (ddsHeaderDxt10->dxgiFormat) + { + case DXGI_FORMAT_BC1_TYPELESS: + case DXGI_FORMAT_BC1_UNORM: + // FIXME: check for GL_COMPRESSED_RGBA_S3TC_DXT1_EXT + *picFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT; + break; + + case DXGI_FORMAT_BC1_UNORM_SRGB: + // FIXME: check for GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT + *picFormat = GL_COMPRESSED_SRGB_S3TC_DXT1_EXT; + break; + + case DXGI_FORMAT_BC2_TYPELESS: + case DXGI_FORMAT_BC2_UNORM: + *picFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; + break; + + case DXGI_FORMAT_BC2_UNORM_SRGB: + *picFormat = GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT; + break; + + case DXGI_FORMAT_BC3_TYPELESS: + case DXGI_FORMAT_BC3_UNORM: + *picFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + break; + + case DXGI_FORMAT_BC3_UNORM_SRGB: + *picFormat = GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT; + break; + + case DXGI_FORMAT_BC4_TYPELESS: + case DXGI_FORMAT_BC4_UNORM: + *picFormat = GL_COMPRESSED_RED_RGTC1; + break; + + case DXGI_FORMAT_BC4_SNORM: + *picFormat = GL_COMPRESSED_SIGNED_RED_RGTC1; + break; + + case DXGI_FORMAT_BC5_TYPELESS: + case DXGI_FORMAT_BC5_UNORM: + *picFormat = GL_COMPRESSED_RG_RGTC2; + break; + + case DXGI_FORMAT_BC5_SNORM: + *picFormat = GL_COMPRESSED_SIGNED_RG_RGTC2; + break; + + case DXGI_FORMAT_BC6H_TYPELESS: + case DXGI_FORMAT_BC6H_UF16: + *picFormat = GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB; + break; + + case DXGI_FORMAT_BC6H_SF16: + *picFormat = GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB; + break; + + case DXGI_FORMAT_BC7_TYPELESS: + case DXGI_FORMAT_BC7_UNORM: + *picFormat = GL_COMPRESSED_RGBA_BPTC_UNORM_ARB; + break; + + case DXGI_FORMAT_BC7_UNORM_SRGB: + *picFormat = GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB; + break; + + case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: + *picFormat = GL_SRGB8_ALPHA8_EXT; + break; + + case DXGI_FORMAT_R8G8B8A8_UNORM: + case DXGI_FORMAT_R8G8B8A8_SNORM: + *picFormat = GL_RGBA8; + break; + + default: + ri.Printf(PRINT_ALL, "DDS File %s has unsupported DXGI format %d.", filename, ddsHeaderDxt10->dxgiFormat); + ri.FS_FreeFile(buffer.v); + return; + break; + } + } + else + { + if (ddsHeader->pixelFormatFlags & DDSPF_FOURCC) + { + if (ddsHeader->fourCC == EncodeFourCC("DXT1")) + *picFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT; + else if (ddsHeader->fourCC == EncodeFourCC("DXT2")) + *picFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; + else if (ddsHeader->fourCC == EncodeFourCC("DXT3")) + *picFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; + else if (ddsHeader->fourCC == EncodeFourCC("DXT4")) + *picFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + else if (ddsHeader->fourCC == EncodeFourCC("DXT5")) + *picFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + else if (ddsHeader->fourCC == EncodeFourCC("ATI1")) + *picFormat = GL_COMPRESSED_RED_RGTC1; + else if (ddsHeader->fourCC == EncodeFourCC("BC4U")) + *picFormat = GL_COMPRESSED_RED_RGTC1; + else if (ddsHeader->fourCC == EncodeFourCC("BC4S")) + *picFormat = GL_COMPRESSED_SIGNED_RED_RGTC1; + else if (ddsHeader->fourCC == EncodeFourCC("ATI2")) + *picFormat = GL_COMPRESSED_RG_RGTC2; + else if (ddsHeader->fourCC == EncodeFourCC("BC5U")) + *picFormat = GL_COMPRESSED_RG_RGTC2; + else if (ddsHeader->fourCC == EncodeFourCC("BC5S")) + *picFormat = GL_COMPRESSED_SIGNED_RG_RGTC2; + else + { + ri.Printf(PRINT_ALL, "DDS File %s has unsupported FourCC.", filename); + ri.FS_FreeFile(buffer.v); + return; + } + } + else if (ddsHeader->pixelFormatFlags == (DDSPF_RGB | DDSPF_ALPHAPIXELS) + && ddsHeader->rgbBitCount == 32 + && ddsHeader->rBitMask == 0x000000ff + && ddsHeader->gBitMask == 0x0000ff00 + && ddsHeader->bBitMask == 0x00ff0000 + && ddsHeader->aBitMask == 0xff000000) + { + *picFormat = GL_RGBA8; + } + else + { + ri.Printf(PRINT_ALL, "DDS File %s has unsupported RGBA format.", filename); + ri.FS_FreeFile(buffer.v); + return; + } + } + + *pic = ri.Malloc(len); + Com_Memcpy(*pic, data, len); + + ri.FS_FreeFile(buffer.v); +} + +void R_SaveDDS(const char *filename, byte *pic, int width, int height, int depth) +{ + byte *data; + ddsHeader_t *ddsHeader; + int picSize, size; + + if (!depth) + depth = 1; + + picSize = width * height * depth * 4; + size = 4 + sizeof(*ddsHeader) + picSize; + data = ri.Malloc(size); + + data[0] = 'D'; + data[1] = 'D'; + data[2] = 'S'; + data[3] = ' '; + + ddsHeader = (ddsHeader_t *)(data + 4); + memset(ddsHeader, 0, sizeof(ddsHeader_t)); + + ddsHeader->headerSize = 0x7c; + ddsHeader->flags = _DDSFLAGS_REQUIRED; + ddsHeader->height = height; + ddsHeader->width = width; + ddsHeader->always_0x00000020 = 0x00000020; + ddsHeader->caps = DDSCAPS_COMPLEX | DDSCAPS_REQUIRED; + + if (depth == 6) + ddsHeader->caps2 = DDSCAPS2_CUBEMAP; + + ddsHeader->pixelFormatFlags = DDSPF_RGB | DDSPF_ALPHAPIXELS; + ddsHeader->rgbBitCount = 32; + ddsHeader->rBitMask = 0x000000ff; + ddsHeader->gBitMask = 0x0000ff00; + ddsHeader->bBitMask = 0x00ff0000; + ddsHeader->aBitMask = 0xff000000; + + Com_Memcpy(data + 4 + sizeof(*ddsHeader), pic, picSize); + + ri.FS_WriteFile(filename, data, size); + + ri.Free(data); +} diff --git a/code/renderergl2/tr_init.c b/code/renderergl2/tr_init.c index 525dfba0..282ce926 100644 --- a/code/renderergl2/tr_init.c +++ b/code/renderergl2/tr_init.c @@ -23,6 +23,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include "tr_local.h" +#include "tr_dsa.h" + glconfig_t glConfig; glRefConfig_t glRefConfig; qboolean textureFilterAnisotropic = qfalse; @@ -106,6 +108,7 @@ cvar_t *r_ext_framebuffer_multisample; cvar_t *r_arb_seamless_cube_map; cvar_t *r_arb_vertex_type_2_10_10_10_rev; cvar_t *r_arb_vertex_array_object; +cvar_t *r_ext_direct_state_access; cvar_t *r_mergeMultidraws; cvar_t *r_mergeLeafSurfaces; @@ -129,11 +132,6 @@ cvar_t *r_forceAutoExposure; cvar_t *r_forceAutoExposureMin; cvar_t *r_forceAutoExposureMax; -cvar_t *r_materialGamma; -cvar_t *r_lightGamma; -cvar_t *r_framebufferGamma; -cvar_t *r_tonemapGamma; - cvar_t *r_depthPrepass; cvar_t *r_ssao; @@ -142,8 +140,8 @@ cvar_t *r_specularMapping; cvar_t *r_deluxeMapping; cvar_t *r_parallaxMapping; cvar_t *r_cubeMapping; -cvar_t *r_deluxeSpecular; -cvar_t *r_specularIsMetallic; +cvar_t *r_cubemapSize; +cvar_t *r_pbr; cvar_t *r_baseNormalX; cvar_t *r_baseNormalY; cvar_t *r_baseParallax; @@ -455,6 +453,7 @@ byte *RB_ReadPixels(int x, int y, int width, int height, size_t *offset, int *pa buffer = ri.Hunk_AllocateTempMemory(padwidth * height + *offset + packAlign - 1); bufstart = PADP((intptr_t) buffer + *offset, packAlign); + qglReadPixels(x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, bufstart); *offset = bufstart - buffer; @@ -830,6 +829,35 @@ void R_ScreenShotJPEG_f (void) { //============================================================================ +/* +================== +R_ExportCubemaps +================== +*/ +void R_ExportCubemaps(void) +{ + exportCubemapsCommand_t *cmd; + + cmd = R_GetCommandBuffer(sizeof(*cmd)); + if (!cmd) { + return; + } + cmd->commandId = RC_EXPORT_CUBEMAPS; +} + + +/* +================== +R_ExportCubemaps_f +================== +*/ +void R_ExportCubemaps_f(void) +{ + R_ExportCubemaps(); +} + +//============================================================================ + /* ================== RB_TakeVideoFrameCmd @@ -924,19 +952,11 @@ void GL_SetDefaultState( void ) qglColor4f (1,1,1,1); - // initialize downstream texture unit if we're running - // in a multitexture environment - if ( qglActiveTextureARB ) { - GL_SelectTexture( 1 ); - GL_TextureMode( r_textureMode->string ); - GL_TexEnv( GL_MODULATE ); - qglDisable( GL_TEXTURE_2D ); - GL_SelectTexture( 0 ); - } + GL_BindNullTextures(); + GL_BindNullFramebuffers(); qglEnable(GL_TEXTURE_2D); GL_TextureMode( r_textureMode->string ); - GL_TexEnv( GL_MODULATE ); //qglShadeModel( GL_SMOOTH ); qglDepthFunc( GL_LEQUAL ); @@ -949,8 +969,7 @@ void GL_SetDefaultState( void ) glState.faceCulling = CT_TWO_SIDED; glState.faceCullFront = qtrue; - glState.currentProgram = 0; - qglUseProgramObjectARB(0); + GL_BindNullProgram(); if (glRefConfig.vertexArrayObject) qglBindVertexArrayARB(0); @@ -1149,6 +1168,7 @@ void R_Register( void ) r_arb_seamless_cube_map = ri.Cvar_Get( "r_arb_seamless_cube_map", "0", CVAR_ARCHIVE | CVAR_LATCH); r_arb_vertex_type_2_10_10_10_rev = ri.Cvar_Get( "r_arb_vertex_type_2_10_10_10_rev", "1", CVAR_ARCHIVE | CVAR_LATCH); r_arb_vertex_array_object = ri.Cvar_Get( "r_arb_vertex_array_object", "1", CVAR_ARCHIVE | CVAR_LATCH); + r_ext_direct_state_access = ri.Cvar_Get("r_ext_direct_state_access", "1", CVAR_ARCHIVE | CVAR_LATCH); r_ext_texture_filter_anisotropic = ri.Cvar_Get( "r_ext_texture_filter_anisotropic", "0", CVAR_ARCHIVE | CVAR_LATCH ); @@ -1187,7 +1207,7 @@ void R_Register( void ) r_floatLightmap = ri.Cvar_Get( "r_floatLightmap", "0", CVAR_ARCHIVE | CVAR_LATCH ); r_postProcess = ri.Cvar_Get( "r_postProcess", "1", CVAR_ARCHIVE ); - r_toneMap = ri.Cvar_Get( "r_toneMap", "1", CVAR_ARCHIVE | CVAR_LATCH ); + r_toneMap = ri.Cvar_Get( "r_toneMap", "1", CVAR_ARCHIVE ); r_forceToneMap = ri.Cvar_Get( "r_forceToneMap", "0", CVAR_CHEAT ); r_forceToneMapMin = ri.Cvar_Get( "r_forceToneMapMin", "-8.0", CVAR_CHEAT ); r_forceToneMapAvg = ri.Cvar_Get( "r_forceToneMapAvg", "-2.0", CVAR_CHEAT ); @@ -1200,11 +1220,6 @@ void R_Register( void ) r_cameraExposure = ri.Cvar_Get( "r_cameraExposure", "0", CVAR_CHEAT ); - r_materialGamma = ri.Cvar_Get( "r_materialGamma", "1.0", CVAR_ARCHIVE | CVAR_LATCH ); - r_lightGamma = ri.Cvar_Get( "r_lightGamma", "1.0", CVAR_ARCHIVE | CVAR_LATCH ); - r_framebufferGamma = ri.Cvar_Get( "r_framebufferGamma", "1.0", CVAR_ARCHIVE | CVAR_LATCH ); - r_tonemapGamma = ri.Cvar_Get( "r_tonemapGamma", "1.0", CVAR_ARCHIVE | CVAR_LATCH ); - r_depthPrepass = ri.Cvar_Get( "r_depthPrepass", "1", CVAR_ARCHIVE ); r_ssao = ri.Cvar_Get( "r_ssao", "0", CVAR_LATCH | CVAR_ARCHIVE ); @@ -1213,8 +1228,8 @@ void R_Register( void ) r_deluxeMapping = ri.Cvar_Get( "r_deluxeMapping", "1", CVAR_ARCHIVE | CVAR_LATCH ); r_parallaxMapping = ri.Cvar_Get( "r_parallaxMapping", "0", CVAR_ARCHIVE | CVAR_LATCH ); r_cubeMapping = ri.Cvar_Get( "r_cubeMapping", "0", CVAR_ARCHIVE | CVAR_LATCH ); - r_deluxeSpecular = ri.Cvar_Get( "r_deluxeSpecular", "0.3", CVAR_ARCHIVE | CVAR_LATCH ); - r_specularIsMetallic = ri.Cvar_Get( "r_specularIsMetallic", "0", CVAR_ARCHIVE | CVAR_LATCH ); + r_cubemapSize = ri.Cvar_Get( "r_cubemapSize", "128", CVAR_ARCHIVE | CVAR_LATCH ); + r_pbr = ri.Cvar_Get("r_pbr", "0", CVAR_ARCHIVE | CVAR_LATCH); r_baseNormalX = ri.Cvar_Get( "r_baseNormalX", "1.0", CVAR_ARCHIVE | CVAR_LATCH ); r_baseNormalY = ri.Cvar_Get( "r_baseNormalY", "1.0", CVAR_ARCHIVE | CVAR_LATCH ); r_baseParallax = ri.Cvar_Get( "r_baseParallax", "0.05", CVAR_ARCHIVE | CVAR_LATCH ); @@ -1352,6 +1367,7 @@ void R_Register( void ) ri.Cmd_AddCommand( "gfxinfo", GfxInfo_f ); ri.Cmd_AddCommand( "minimize", GLimp_Minimize ); ri.Cmd_AddCommand( "gfxmeminfo", GfxMemInfo_f ); + ri.Cmd_AddCommand( "exportCubemaps", R_ExportCubemaps_f ); } void R_InitQueries(void) @@ -1497,6 +1513,7 @@ void RE_Shutdown( qboolean destroyWindow ) { ri.Cmd_RemoveCommand( "modelist" ); ri.Cmd_RemoveCommand( "shaderstate" ); ri.Cmd_RemoveCommand( "gfxmeminfo" ); + ri.Cmd_RemoveCommand( "exportCubemaps" ); if ( tr.registered ) { diff --git a/code/renderergl2/tr_light.c b/code/renderergl2/tr_light.c index 7d3ed2a5..a335eb6f 100644 --- a/code/renderergl2/tr_light.c +++ b/code/renderergl2/tr_light.c @@ -471,7 +471,7 @@ int R_CubemapForPoint( vec3_t point ) vec3_t diff; vec_t length; - VectorSubtract(point, tr.cubemapOrigins[i], diff); + VectorSubtract(point, tr.cubemaps[i].origin, diff); length = DotProduct(diff, diff); if (shortest > length) diff --git a/code/renderergl2/tr_local.h b/code/renderergl2/tr_local.h index 21b572f5..00d882cc 100644 --- a/code/renderergl2/tr_local.h +++ b/code/renderergl2/tr_local.h @@ -54,10 +54,16 @@ typedef unsigned int glIndex_t; #define MAX_CALC_PSHADOWS 64 #define MAX_DRAWN_PSHADOWS 16 // do not increase past 32, because bit flags are used on surfaces #define PSHADOW_MAP_SIZE 512 -#define CUBE_MAP_MIPS 7 -#define CUBE_MAP_SIZE (1 << CUBE_MAP_MIPS) #define USE_VERT_TANGENT_SPACE +#define USE_OVERBRIGHT + +typedef struct cubemap_s { + char name[MAX_QPATH]; + vec3_t origin; + float parallaxRadius; + image_t *image; +} cubemap_t; typedef struct dlight_s { vec3_t origin; @@ -1346,8 +1352,6 @@ typedef struct { // the renderer front end should never modify glstate_t typedef struct { - int currenttextures[NUM_TEXTURE_BUNDLES]; - int currenttmu; qboolean finishCalled; int texEnv[2]; int faceCulling; @@ -1357,7 +1361,6 @@ typedef struct { float vertexAttribsInterpolation; qboolean vertexAnimation; uint32_t vertexAttribsEnabled; // global if no VAOs, tess only otherwise - shaderProgram_t *currentProgram; FBO_t *currentFBO; vao_t *currentVao; mat4_t modelview; @@ -1373,7 +1376,7 @@ typedef enum { typedef enum { TCR_NONE = 0x0000, - TCR_LATC = 0x0001, + TCR_RGTC = 0x0001, TCR_BPTC = 0x0002, } textureCompressionRef_t; @@ -1397,7 +1400,9 @@ typedef struct { qboolean textureFloat; qboolean halfFloatPixel; qboolean packedDepthStencil; + qboolean arbTextureCompression; textureCompressionRef_t textureCompression; + qboolean swizzleNormalmap; qboolean framebufferMultisample; qboolean framebufferBlit; @@ -1413,6 +1418,7 @@ typedef struct { qboolean floatLightmap; qboolean vertexArrayObject; + qboolean directStateAccess; } glRefConfig_t; @@ -1559,8 +1565,7 @@ typedef struct { int fatLightmapStep; int numCubemaps; - vec3_t *cubemapOrigins; - image_t **cubemaps; + cubemap_t *cubemaps; trRefEntity_t *currentEntity; trRefEntity_t worldEntity; // point currentEntity at this when rendering world @@ -1712,6 +1717,7 @@ extern cvar_t *r_ext_framebuffer_multisample; extern cvar_t *r_arb_seamless_cube_map; extern cvar_t *r_arb_vertex_type_2_10_10_10_rev; extern cvar_t *r_arb_vertex_array_object; +extern cvar_t *r_ext_direct_state_access; extern cvar_t *r_nobind; // turns off binding to appropriate textures extern cvar_t *r_singleShader; // make most world faces use default shader @@ -1771,11 +1777,6 @@ extern cvar_t *r_forceAutoExposureMax; extern cvar_t *r_cameraExposure; -extern cvar_t *r_materialGamma; -extern cvar_t *r_lightGamma; -extern cvar_t *r_framebufferGamma; -extern cvar_t *r_tonemapGamma; - extern cvar_t *r_depthPrepass; extern cvar_t *r_ssao; @@ -1784,8 +1785,8 @@ extern cvar_t *r_specularMapping; extern cvar_t *r_deluxeMapping; extern cvar_t *r_parallaxMapping; extern cvar_t *r_cubeMapping; -extern cvar_t *r_deluxeSpecular; -extern cvar_t *r_specularIsMetallic; +extern cvar_t *r_cubemapSize; +extern cvar_t *r_pbr; extern cvar_t *r_baseNormalX; extern cvar_t *r_baseNormalY; extern cvar_t *r_baseParallax; @@ -1875,17 +1876,14 @@ void R_RotateForEntity( const trRefEntity_t *ent, const viewParms_t *viewParms, /* ** GL wrapper/helper functions */ -void GL_Bind( image_t *image ); void GL_BindToTMU( image_t *image, int tmu ); void GL_SetDefaultState (void); -void GL_SelectTexture( int unit ); void GL_TextureMode( const char *string ); void GL_CheckErrs( char *file, int line ); #define GL_CheckErrors(...) GL_CheckErrs(__FILE__, __LINE__) void GL_State( unsigned long stateVector ); void GL_SetProjectionMatrix(mat4_t matrix); void GL_SetModelviewMatrix(mat4_t matrix); -void GL_TexEnv( int env ); void GL_Cull( int cullType ); #define GLS_SRCBLEND_ZERO 0x00000001 @@ -2209,7 +2207,6 @@ void GLSL_InitGPUShaders(void); void GLSL_ShutdownGPUShaders(void); void GLSL_VertexAttribPointers(uint32_t attribBits); void GLSL_BindProgram(shaderProgram_t * program); -void GLSL_BindNullProgram(void); void GLSL_SetUniformInt(shaderProgram_t *program, int uniformNum, GLint value); void GLSL_SetUniformFloat(shaderProgram_t *program, int uniformNum, GLfloat value); @@ -2411,6 +2408,10 @@ typedef struct { viewParms_t viewParms; } postProcessCommand_t; +typedef struct { + int commandId; +} exportCubemapsCommand_t; + typedef enum { RC_END_OF_LIST, RC_SET_COLOR, @@ -2423,7 +2424,8 @@ typedef enum { RC_COLORMASK, RC_CLEARDEPTH, RC_CAPSHADOWMAP, - RC_POSTPROCESS + RC_POSTPROCESS, + RC_EXPORT_CUBEMAPS } renderCommand_t; diff --git a/code/renderergl2/tr_main.c b/code/renderergl2/tr_main.c index 6d397c4a..bd27018b 100644 --- a/code/renderergl2/tr_main.c +++ b/code/renderergl2/tr_main.c @@ -1820,13 +1820,6 @@ void R_SortDrawSurfs( drawSurf_t *drawSurfs, int numDrawSurfs ) { return; } - // if we overflowed MAX_DRAWSURFS, the drawsurfs - // wrapped around in the buffer and we will be missing - // the first surfaces, not the last ones - if ( numDrawSurfs > MAX_DRAWSURFS ) { - numDrawSurfs = MAX_DRAWSURFS; - } - // sort the drawsurfs by sort type, then orientation, then shader R_RadixSort( drawSurfs, numDrawSurfs ); @@ -2037,7 +2030,7 @@ void R_DebugGraphics( void ) { R_IssuePendingRenderCommands(); - GL_Bind( tr.whiteImage); + GL_BindToTMU(tr.whiteImage, TB_COLORMAP); GL_Cull( CT_FRONT_SIDED ); ri.CM_DrawDebugSurface( R_DebugPolygon ); } @@ -2053,6 +2046,7 @@ or a mirror / remote location */ void R_RenderView (viewParms_t *parms) { int firstDrawSurf; + int numDrawSurfs; if ( parms->viewportWidth <= 0 || parms->viewportHeight <= 0 ) { return; @@ -2075,7 +2069,15 @@ void R_RenderView (viewParms_t *parms) { R_GenerateDrawSurfs(); - R_SortDrawSurfs( tr.refdef.drawSurfs + firstDrawSurf, tr.refdef.numDrawSurfs - firstDrawSurf ); + // if we overflowed MAX_DRAWSURFS, the drawsurfs + // wrapped around in the buffer and we will be missing + // the first surfaces, not the last ones + numDrawSurfs = tr.refdef.numDrawSurfs; + if ( numDrawSurfs > MAX_DRAWSURFS ) { + numDrawSurfs = MAX_DRAWSURFS; + } + + R_SortDrawSurfs( tr.refdef.drawSurfs + firstDrawSurf, numDrawSurfs - firstDrawSurf ); // draw main system development information (surface outlines, etc) R_DebugGraphics(); @@ -2860,7 +2862,7 @@ void R_RenderCubemapSide( int cubemapIndex, int cubemapSide, qboolean subscene ) memset( &refdef, 0, sizeof( refdef ) ); refdef.rdflags = 0; - VectorCopy(tr.cubemapOrigins[cubemapIndex], refdef.vieworg); + VectorCopy(tr.cubemaps[cubemapIndex].origin, refdef.vieworg); switch(cubemapSide) { @@ -2934,7 +2936,7 @@ void R_RenderCubemapSide( int cubemapIndex, int cubemapSide, qboolean subscene ) // only print message for first side if (directed[0] + directed[1] + directed[2] == 0 && cubemapSide == 0) { - ri.Printf(PRINT_ALL, "cubemap %d (%f, %f, %f) is outside the lightgrid!\n", cubemapIndex, tr.refdef.vieworg[0], tr.refdef.vieworg[1], tr.refdef.vieworg[2]); + ri.Printf(PRINT_ALL, "cubemap %d %s (%f, %f, %f) is outside the lightgrid!\n", cubemapIndex, tr.cubemaps[cubemapIndex].name, tr.refdef.vieworg[0], tr.refdef.vieworg[1], tr.refdef.vieworg[2]); } } diff --git a/code/renderergl2/tr_postprocess.c b/code/renderergl2/tr_postprocess.c index 35982fce..1952ae96 100644 --- a/code/renderergl2/tr_postprocess.c +++ b/code/renderergl2/tr_postprocess.c @@ -183,7 +183,7 @@ void RB_BokehBlur(FBO_t *src, ivec4_t srcBox, FBO_t *dst, ivec4_t dstBox, float FBO_Blit(tr.textureScratchFbo[0], NULL, blurTexScale, tr.textureScratchFbo[1], NULL, &tr.bokehShader, color, 0); } - FBO_Blit(tr.textureScratchFbo[1], NULL, NULL, dst, dstBox, &tr.textureColorShader, NULL, 0); + FBO_Blit(tr.textureScratchFbo[1], NULL, NULL, dst, dstBox, NULL, NULL, 0); } #else // higher quality blur, but slower else if (blur > 1.0f) @@ -217,7 +217,7 @@ void RB_BokehBlur(FBO_t *src, ivec4_t srcBox, FBO_t *dst, ivec4_t dstBox, float FBO_Blit(tr.quarterFbo[0], NULL, blurTexScale, tr.quarterFbo[1], NULL, &tr.bokehShader, color, GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA); } - FBO_Blit(tr.quarterFbo[1], NULL, NULL, dst, dstBox, &tr.textureColorShader, NULL, 0); + FBO_Blit(tr.quarterFbo[1], NULL, NULL, dst, dstBox, NULL, NULL, 0); } #endif } @@ -232,49 +232,44 @@ static void RB_RadialBlur(FBO_t *srcFbo, FBO_t *dstFbo, int passes, float stretc const float mul = powf(stretch, inc); float scale; - { - vec2_t texScale; - - texScale[0] = - texScale[1] = 1.0f; - - alpha *= inc; - VectorSet4(color, alpha, alpha, alpha, 1.0f); + alpha *= inc; + VectorSet4(color, alpha, alpha, alpha, 1.0f); + if (srcFbo) VectorSet4(srcBox, 0, 0, srcFbo->width, srcFbo->height); - VectorSet4(dstBox, x, y, w, h); - FBO_Blit(srcFbo, srcBox, texScale, dstFbo, dstBox, &tr.textureColorShader, color, 0); + else + VectorSet4(srcBox, 0, 0, glConfig.vidWidth, glConfig.vidHeight); - --passes; - scale = mul; - while (passes > 0) + VectorSet4(dstBox, x, y, w, h); + FBO_Blit(srcFbo, srcBox, NULL, dstFbo, dstBox, NULL, color, 0); + + --passes; + scale = mul; + while (passes > 0) + { + float iscale = 1.f / scale; + float s0 = xcenter * (1.f - iscale); + float t0 = (1.0f - ycenter) * (1.f - iscale); + + if (srcFbo) { - float iscale = 1.f / scale; - float s0 = xcenter * (1.f - iscale); - float t0 = (1.0f - ycenter) * (1.f - iscale); - float s1 = iscale + s0; - float t1 = iscale + t0; - - if (srcFbo) - { - srcBox[0] = s0 * srcFbo->width; - srcBox[1] = t0 * srcFbo->height; - srcBox[2] = (s1 - s0) * srcFbo->width; - srcBox[3] = (t1 - t0) * srcFbo->height; - } - else - { - srcBox[0] = s0 * glConfig.vidWidth; - srcBox[1] = t0 * glConfig.vidHeight; - srcBox[2] = (s1 - s0) * glConfig.vidWidth; - srcBox[3] = (t1 - t0) * glConfig.vidHeight; - } - - FBO_Blit(srcFbo, srcBox, texScale, dstFbo, dstBox, &tr.textureColorShader, color, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE ); - - scale *= mul; - --passes; + srcBox[0] = s0 * srcFbo->width; + srcBox[1] = t0 * srcFbo->height; + srcBox[2] = iscale * srcFbo->width; + srcBox[3] = iscale * srcFbo->height; } + else + { + srcBox[0] = s0 * glConfig.vidWidth; + srcBox[1] = t0 * glConfig.vidHeight; + srcBox[2] = iscale * glConfig.vidWidth; + srcBox[3] = iscale * glConfig.vidHeight; + } + + FBO_Blit(srcFbo, srcBox, NULL, dstFbo, dstBox, NULL, color, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE ); + + scale *= mul; + --passes; } } @@ -329,7 +324,7 @@ void RB_SunRays(FBO_t *srcFbo, ivec4_t srcBox, FBO_t *dstFbo, ivec4_t dstBox) // From RB_DrawSun() { float dist; - mat4_t trans, model, mvp; + mat4_t trans, model; Mat4Translation( backEnd.viewParms.or.origin, trans ); Mat4Multiply( backEnd.viewParms.world.modelMatrix, trans, model ); @@ -353,12 +348,8 @@ void RB_SunRays(FBO_t *srcFbo, ivec4_t srcBox, FBO_t *dstFbo, ivec4_t dstBox) // initialize quarter buffers { float mul = 1.f; - vec2_t texScale; ivec4_t rayBox, quarterBox; - texScale[0] = - texScale[1] = 1.0f; - VectorSet4(color, mul, mul, mul, 1); if (srcFbo) @@ -408,14 +399,10 @@ void RB_SunRays(FBO_t *srcFbo, ivec4_t srcBox, FBO_t *dstFbo, ivec4_t dstBox) // add result back on top of the main buffer { float mul = 1.f; - vec2_t texScale; - - texScale[0] = - texScale[1] = 1.0f; VectorSet4(color, mul, mul, mul, 1); - FBO_Blit(tr.quarterFbo[0], NULL, texScale, dstFbo, dstBox, &tr.textureColorShader, color, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE); + FBO_Blit(tr.quarterFbo[0], NULL, NULL, dstFbo, dstBox, NULL, color, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE); } } @@ -443,31 +430,27 @@ static void RB_BlurAxis(FBO_t *srcFbo, FBO_t *dstFbo, float strength, qboolean h { ivec4_t srcBox, dstBox; vec4_t color; - vec2_t texScale; - - texScale[0] = - texScale[1] = 1.0f; VectorSet4(color, weights[0], weights[0], weights[0], 1.0f); VectorSet4(srcBox, 0, 0, srcFbo->width, srcFbo->height); VectorSet4(dstBox, 0, 0, dstFbo->width, dstFbo->height); - FBO_Blit(srcFbo, srcBox, texScale, dstFbo, dstBox, &tr.textureColorShader, color, 0 ); + FBO_Blit(srcFbo, srcBox, NULL, dstFbo, dstBox, NULL, color, 0); VectorSet4(color, weights[1], weights[1], weights[1], 1.0f); dx = offsets[1] * xmul; dy = offsets[1] * ymul; VectorSet4(srcBox, dx, dy, srcFbo->width, srcFbo->height); - FBO_Blit(srcFbo, srcBox, texScale, dstFbo, dstBox, &tr.textureColorShader, color, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE ); + FBO_Blit(srcFbo, srcBox, NULL, dstFbo, dstBox, NULL, color, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE); VectorSet4(srcBox, -dx, -dy, srcFbo->width, srcFbo->height); - FBO_Blit(srcFbo, srcBox, texScale, dstFbo, dstBox, &tr.textureColorShader, color, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE ); + FBO_Blit(srcFbo, srcBox, NULL, dstFbo, dstBox, NULL, color, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE); VectorSet4(color, weights[2], weights[2], weights[2], 1.0f); dx = offsets[2] * xmul; dy = offsets[2] * ymul; VectorSet4(srcBox, dx, dy, srcFbo->width, srcFbo->height); - FBO_Blit(srcFbo, srcBox, texScale, dstFbo, dstBox, &tr.textureColorShader, color, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE ); + FBO_Blit(srcFbo, srcBox, NULL, dstFbo, dstBox, NULL, color, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE); VectorSet4(srcBox, -dx, -dy, srcFbo->width, srcFbo->height); - FBO_Blit(srcFbo, srcBox, texScale, dstFbo, dstBox, &tr.textureColorShader, color, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE ); + FBO_Blit(srcFbo, srcBox, NULL, dstFbo, dstBox, NULL, color, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE); } } @@ -492,10 +475,6 @@ void RB_GaussianBlur(float blur) { ivec4_t srcBox, dstBox; vec4_t color; - vec2_t texScale; - - texScale[0] = - texScale[1] = 1.0f; VectorSet4(color, 1, 1, 1, 1); @@ -507,7 +486,7 @@ void RB_GaussianBlur(float blur) VectorSet4(srcBox, 0, 0, tr.whiteImage->width, tr.whiteImage->height); VectorSet4(dstBox, 0, 0, tr.textureScratchFbo[0]->width, tr.textureScratchFbo[0]->height); qglColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE); - FBO_BlitFromTexture(tr.whiteImage, srcBox, texScale, tr.textureScratchFbo[0], dstBox, &tr.textureColorShader, color, GLS_DEPTHTEST_DISABLE); + FBO_BlitFromTexture(tr.whiteImage, srcBox, NULL, tr.textureScratchFbo[0], dstBox, NULL, color, GLS_DEPTHTEST_DISABLE); qglColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // blur the tiny buffer horizontally and vertically @@ -518,6 +497,6 @@ void RB_GaussianBlur(float blur) VectorSet4(srcBox, 0, 0, tr.textureScratchFbo[0]->width, tr.textureScratchFbo[0]->height); VectorSet4(dstBox, 0, 0, glConfig.vidWidth, glConfig.vidHeight); color[3] = factor; - FBO_Blit(tr.textureScratchFbo[0], srcBox, texScale, NULL, dstBox, &tr.textureColorShader, color, GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA); + FBO_Blit(tr.textureScratchFbo[0], srcBox, NULL, NULL, dstBox, NULL, color, GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA); } } diff --git a/code/renderergl2/tr_scene.c b/code/renderergl2/tr_scene.c index 88eda479..410f81e4 100644 --- a/code/renderergl2/tr_scene.c +++ b/code/renderergl2/tr_scene.c @@ -335,31 +335,30 @@ void RE_BeginScene(const refdef_t *fd) } else { +#if defined(USE_OVERBRIGHT) + float scale = (1 << (r_mapOverBrightBits->integer - tr.overbrightBits)) / 255.0f; +#else + float scale = (1 << r_mapOverBrightBits->integer) / 255.0f; +#endif tr.refdef.colorScale = r_forceSun->integer ? r_forceSunMapLightScale->value : tr.mapLightScale; + if (r_forceSun->integer) + VectorScale(tr.sunLight, scale * r_forceSunLightScale->value, tr.refdef.sunCol); + else + VectorScale(tr.sunLight, scale, tr.refdef.sunCol); + if (r_sunlightMode->integer == 1) { - tr.refdef.sunCol[0] = - tr.refdef.sunCol[1] = - tr.refdef.sunCol[2] = 1.0f; - tr.refdef.sunAmbCol[0] = tr.refdef.sunAmbCol[1] = tr.refdef.sunAmbCol[2] = r_forceSun->integer ? r_forceSunAmbientScale->value : tr.sunShadowScale; } else { - float scale = pow(2, r_mapOverBrightBits->integer - tr.overbrightBits - 8); if (r_forceSun->integer) - { - VectorScale(tr.sunLight, scale * r_forceSunLightScale->value, tr.refdef.sunCol); VectorScale(tr.sunLight, scale * r_forceSunAmbientScale->value, tr.refdef.sunAmbCol); - } else - { - VectorScale(tr.sunLight, scale, tr.refdef.sunCol); VectorScale(tr.sunLight, scale * tr.sunShadowScale, tr.refdef.sunAmbCol); - } } } diff --git a/code/renderergl2/tr_shade.c b/code/renderergl2/tr_shade.c index 0a652fbb..ca2a02b6 100644 --- a/code/renderergl2/tr_shade.c +++ b/code/renderergl2/tr_shade.c @@ -101,11 +101,9 @@ static void R_BindAnimatedImageToTMU( textureBundle_t *bundle, int tmu ) { int index; if ( bundle->isVideoMap ) { - int oldtmu = glState.currenttmu; - GL_SelectTexture(tmu); ri.CIN_RunCinematic(bundle->videoMapHandle); ri.CIN_UploadCinematic(bundle->videoMapHandle); - GL_SelectTexture(oldtmu); + GL_BindToTMU(tr.scratchImage[bundle->videoMapHandle], tmu); return; } @@ -136,7 +134,7 @@ Draws triangle outlines for debugging ================ */ static void DrawTris (shaderCommands_t *input) { - GL_Bind( tr.whiteImage ); + GL_BindToTMU( tr.whiteImage, TB_COLORMAP ); GL_State( GLS_POLYMODE_LINE | GLS_DEPTHMASK_TRUE ); qglDepthRange( 0, 0 ); @@ -414,7 +412,7 @@ static void ProjectDlightTexture( void ) { vector[3] = scale; GLSL_SetUniformVec4(sp, UNIFORM_DLIGHTINFO, vector); - GL_Bind( tr.dlightImage ); + GL_BindToTMU( tr.dlightImage, TB_COLORMAP ); // include GLS_DEPTHFUNC_EQUAL so alpha tested surfaces don't add light // where they aren't rendered @@ -444,9 +442,21 @@ static void ProjectDlightTexture( void ) { static void ComputeShaderColors( shaderStage_t *pStage, vec4_t baseColor, vec4_t vertColor, int blend ) { + qboolean isBlend = ((blend & GLS_SRCBLEND_BITS) == GLS_SRCBLEND_DST_COLOR) + || ((blend & GLS_SRCBLEND_BITS) == GLS_SRCBLEND_ONE_MINUS_DST_COLOR) + || ((blend & GLS_DSTBLEND_BITS) == GLS_DSTBLEND_SRC_COLOR) + || ((blend & GLS_DSTBLEND_BITS) == GLS_DSTBLEND_ONE_MINUS_SRC_COLOR); + +#if defined(USE_OVERBRIGHT) + float exactLight = 1.0f; +#else + qboolean isWorldDraw = !(backEnd.refdef.rdflags & RDF_NOWORLDMODEL); + float exactLight = (isBlend || !isWorldDraw) ? 1.0f : (float)(1 << r_mapOverBrightBits->integer); +#endif + baseColor[0] = baseColor[1] = - baseColor[2] = + baseColor[2] = exactLight; baseColor[3] = 1.0f; vertColor[0] = @@ -473,7 +483,7 @@ static void ComputeShaderColors( shaderStage_t *pStage, vec4_t baseColor, vec4_t vertColor[0] = vertColor[1] = - vertColor[2] = + vertColor[2] = exactLight; vertColor[3] = 1.0f; break; case CGEN_CONST: @@ -601,11 +611,7 @@ static void ComputeShaderColors( shaderStage_t *pStage, vec4_t baseColor, vec4_t } // multiply color by overbrightbits if this isn't a blend - if (tr.overbrightBits - && !((blend & GLS_SRCBLEND_BITS) == GLS_SRCBLEND_DST_COLOR) - && !((blend & GLS_SRCBLEND_BITS) == GLS_SRCBLEND_ONE_MINUS_DST_COLOR) - && !((blend & GLS_DSTBLEND_BITS) == GLS_DSTBLEND_SRC_COLOR) - && !((blend & GLS_DSTBLEND_BITS) == GLS_DSTBLEND_ONE_MINUS_SRC_COLOR)) + if (tr.overbrightBits && !isBlend) { float scale = 1 << tr.overbrightBits; @@ -854,11 +860,7 @@ static void ForwardDlight( void ) { } if (r_dlightMode->integer >= 2) - { - GL_SelectTexture(TB_SHADOWMAP); - GL_Bind(tr.shadowCubemaps[l]); - GL_SelectTexture(0); - } + GL_BindToTMU(tr.shadowCubemaps[l], TB_SHADOWMAP); ComputeTexMods( pStage, TB_DIFFUSEMAP, texMatrix, texOffTurb ); GLSL_SetUniformVec4(sp, UNIFORM_DIFFUSETEXMATRIX, texMatrix); @@ -1258,7 +1260,7 @@ static void RB_IterateStagesGeneric( shaderCommands_t *input ) if ( backEnd.depthFill ) { if (!(pStage->stateBits & GLS_ATEST_BITS)) - GL_BindToTMU( tr.whiteImage, 0 ); + GL_BindToTMU( tr.whiteImage, TB_COLORMAP ); else if ( pStage->bundle[TB_COLORMAP].image[0] != 0 ) R_BindAnimatedImageToTMU( &pStage->bundle[TB_COLORMAP], TB_COLORMAP ); } @@ -1271,7 +1273,19 @@ static void RB_IterateStagesGeneric( shaderCommands_t *input ) { GL_BindToTMU(tr.screenShadowImage, TB_SHADOWMAP); GLSL_SetUniformVec3(sp, UNIFORM_PRIMARYLIGHTAMBIENT, backEnd.refdef.sunAmbCol); - GLSL_SetUniformVec3(sp, UNIFORM_PRIMARYLIGHTCOLOR, backEnd.refdef.sunCol); + if (r_pbr->integer) + { + vec3_t color; + + color[0] = backEnd.refdef.sunCol[0] * backEnd.refdef.sunCol[0]; + color[1] = backEnd.refdef.sunCol[1] * backEnd.refdef.sunCol[1]; + color[2] = backEnd.refdef.sunCol[2] * backEnd.refdef.sunCol[2]; + GLSL_SetUniformVec3(sp, UNIFORM_PRIMARYLIGHTCOLOR, color); + } + else + { + GLSL_SetUniformVec3(sp, UNIFORM_PRIMARYLIGHTCOLOR, backEnd.refdef.sunCol); + } GLSL_SetUniformVec4(sp, UNIFORM_PRIMARYLIGHTORIGIN, backEnd.refdef.sunDir); } @@ -1367,15 +1381,14 @@ static void RB_IterateStagesGeneric( shaderCommands_t *input ) if (!(tr.viewParms.flags & VPF_NOCUBEMAPS) && input->cubemapIndex && r_cubeMapping->integer) { vec4_t vec; + cubemap_t *cubemap = &tr.cubemaps[input->cubemapIndex - 1]; - GL_BindToTMU( tr.cubemaps[input->cubemapIndex - 1], TB_CUBEMAP); + GL_BindToTMU( cubemap->image, TB_CUBEMAP); - vec[0] = tr.cubemapOrigins[input->cubemapIndex - 1][0] - backEnd.viewParms.or.origin[0]; - vec[1] = tr.cubemapOrigins[input->cubemapIndex - 1][1] - backEnd.viewParms.or.origin[1]; - vec[2] = tr.cubemapOrigins[input->cubemapIndex - 1][2] - backEnd.viewParms.or.origin[2]; + VectorSubtract(cubemap->origin, backEnd.viewParms.or.origin, vec); vec[3] = 1.0f; - VectorScale4(vec, 1.0f / 1000.0f, vec); + VectorScale4(vec, 1.0f / cubemap->parallaxRadius, vec); GLSL_SetUniformVec4(sp, UNIFORM_CUBEMAPINFO, vec); } diff --git a/code/renderergl2/tr_shader.c b/code/renderergl2/tr_shader.c index 9bf2ec20..0ef90211 100644 --- a/code/renderergl2/tr_shader.c +++ b/code/renderergl2/tr_shader.c @@ -934,9 +934,18 @@ static qboolean ParseStage( shaderStage_t *stage, char **text ) ri.Printf( PRINT_WARNING, "WARNING: missing parameter for specular reflectance in shader '%s'\n", shader.name ); continue; } - stage->specularScale[0] = - stage->specularScale[1] = - stage->specularScale[2] = atof( token ); + + if (r_pbr->integer) + { + // interpret specularReflectance < 0.5 as nonmetal + stage->specularScale[1] = (atof(token) < 0.5f) ? 0.0f : 1.0f; + } + else + { + stage->specularScale[0] = + stage->specularScale[1] = + stage->specularScale[2] = atof( token ); + } } // // specularExponent @@ -954,17 +963,23 @@ static qboolean ParseStage( shaderStage_t *stage, char **text ) exponent = atof( token ); - // Change shininess to gloss - // FIXME: assumes max exponent of 8192 and min of 1, must change here if altered in lightall_fp.glsl - exponent = CLAMP(exponent, 1.0, 8192.0); - - stage->specularScale[3] = log(exponent) / log(8192.0); + if (r_pbr->integer) + stage->specularScale[0] = 1.0f - powf(2.0f / (exponent + 2.0), 0.25); + else + { + // Change shininess to gloss + // Assumes max exponent of 8190 and min of 0, must change here if altered in lightall_fp.glsl + exponent = CLAMP(exponent, 0.0f, 8190.0f); + stage->specularScale[3] = (log2f(exponent + 2.0f) - 1.0f) / 12.0f; + } } // // gloss // else if (!Q_stricmp(token, "gloss")) { + float gloss; + token = COM_ParseExt(text, qfalse); if ( token[0] == 0 ) { @@ -972,7 +987,38 @@ static qboolean ParseStage( shaderStage_t *stage, char **text ) continue; } - stage->specularScale[3] = atof( token ); + gloss = atof(token); + + if (r_pbr->integer) + stage->specularScale[0] = 1.0f - exp2f(-3.0f * gloss); + else + stage->specularScale[3] = gloss; + } + // + // roughness + // + else if (!Q_stricmp(token, "roughness")) + { + float roughness; + + token = COM_ParseExt(text, qfalse); + if (token[0] == 0) + { + ri.Printf(PRINT_WARNING, "WARNING: missing parameter for roughness in shader '%s'\n", shader.name); + continue; + } + + roughness = atof(token); + + if (r_pbr->integer) + stage->specularScale[0] = 1.0 - roughness; + else + { + if (roughness >= 0.125) + stage->specularScale[3] = log2f(1.0f / roughness) / 3.0f; + else + stage->specularScale[3] = 1.0f; + } } // // parallaxDepth @@ -1025,6 +1071,7 @@ static qboolean ParseStage( shaderStage_t *stage, char **text ) } // // specularScale + // or specularScale with r_pbr 1 // or specularScale // or specularScale // @@ -1051,10 +1098,19 @@ static qboolean ParseStage( shaderStage_t *stage, char **text ) token = COM_ParseExt(text, qfalse); if ( token[0] == 0 ) { - // two values, rgb then gloss - stage->specularScale[3] = stage->specularScale[1]; - stage->specularScale[1] = - stage->specularScale[2] = stage->specularScale[0]; + if (r_pbr->integer) + { + // two values, metallic then smoothness + float smoothness = stage->specularScale[1]; + stage->specularScale[1] = (stage->specularScale[0] < 0.5f) ? 0.0f : 1.0f; + stage->specularScale[0] = smoothness; + } + { + // two values, rgb then gloss + stage->specularScale[3] = stage->specularScale[1]; + stage->specularScale[1] = + stage->specularScale[2] = stage->specularScale[0]; + } continue; } @@ -2212,12 +2268,33 @@ static void CollapseStagesToLightall(shaderStage_t *diffuse, if (r_specularMapping->integer) { + image_t *diffuseImg; if (specular) { //ri.Printf(PRINT_ALL, ", specularmap %s", specular->bundle[0].image[0]->imgName); diffuse->bundle[TB_SPECULARMAP] = specular->bundle[0]; VectorCopy4(specular->specularScale, diffuse->specularScale); } + else if ((lightmap || useLightVector || useLightVertex) && (diffuseImg = diffuse->bundle[TB_DIFFUSEMAP].image[0])) + { + char specularName[MAX_QPATH]; + image_t *specularImg; + imgFlags_t specularFlags = (diffuseImg->flags & ~(IMGFLAG_GENNORMALMAP | IMGFLAG_SRGB)) | IMGFLAG_NOLIGHTSCALE; + + COM_StripExtension(diffuseImg->imgName, specularName, MAX_QPATH); + Q_strcat(specularName, MAX_QPATH, "_s"); + + specularImg = R_FindImageFile(specularName, IMGTYPE_COLORALPHA, specularFlags); + + if (specularImg) + { + diffuse->bundle[TB_SPECULARMAP] = diffuse->bundle[0]; + diffuse->bundle[TB_SPECULARMAP].numImageAnimations = 0; + diffuse->bundle[TB_SPECULARMAP].image[0] = specularImg; + + VectorSet4(diffuse->specularScale, 1.0f, 1.0f, 1.0f, 1.0f); + } + } } if (tcgen || diffuse->bundle[0].numTexMods) @@ -2799,10 +2876,17 @@ static void InitShader( const char *name, int lightmapIndex ) { // default normal/specular VectorSet4(stages[i].normalScale, 0.0f, 0.0f, 0.0f, 0.0f); - stages[i].specularScale[0] = - stages[i].specularScale[1] = - stages[i].specularScale[2] = r_baseSpecular->value; - stages[i].specularScale[3] = r_baseGloss->value; + if (r_pbr->integer) + { + stages[i].specularScale[0] = r_baseGloss->value; + } + else + { + stages[i].specularScale[0] = + stages[i].specularScale[1] = + stages[i].specularScale[2] = r_baseSpecular->value; + stages[i].specularScale[3] = r_baseGloss->value; + } } } diff --git a/code/renderergl2/tr_shadows.c b/code/renderergl2/tr_shadows.c index 13fbfdf7..76371752 100644 --- a/code/renderergl2/tr_shadows.c +++ b/code/renderergl2/tr_shadows.c @@ -206,7 +206,7 @@ void RB_ShadowTessEnd( void ) { // draw the silhouette edges - GL_Bind( tr.whiteImage ); + GL_BindToTMU( tr.whiteImage, TB_COLORMAP ); GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ZERO ); qglColor3f( 0.2f, 0.2f, 0.2f ); @@ -256,7 +256,7 @@ void RB_ShadowFinish( void ) { qglDisable (GL_CLIP_PLANE0); GL_Cull( CT_TWO_SIDED ); - GL_Bind( tr.whiteImage ); + GL_BindToTMU( tr.whiteImage, TB_COLORMAP ); qglLoadIdentity (); diff --git a/code/renderergl2/tr_sky.c b/code/renderergl2/tr_sky.c index 05ff8f94..840d0149 100644 --- a/code/renderergl2/tr_sky.c +++ b/code/renderergl2/tr_sky.c @@ -374,7 +374,7 @@ static void DrawSkySide( struct image_s *image, const int mins[2], const int max //tess.numIndexes = 0; tess.firstIndex = tess.numIndexes; - GL_Bind( image ); + GL_BindToTMU( image, TB_COLORMAP ); GL_Cull( CT_TWO_SIDED ); for ( t = mins[1]+HALF_SKY_SUBDIVISIONS; t <= maxs[1]+HALF_SKY_SUBDIVISIONS; t++ ) diff --git a/code/renderergl2/tr_surface.c b/code/renderergl2/tr_surface.c index c5b1b0f8..72cd610e 100644 --- a/code/renderergl2/tr_surface.c +++ b/code/renderergl2/tr_surface.c @@ -575,7 +575,7 @@ static void RB_SurfaceBeam( void ) VectorAdd( start_points[i], direction, end_points[i] ); } - GL_Bind( tr.whiteImage ); + GL_BindToTMU( tr.whiteImage, TB_COLORMAP ); GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE ); @@ -1518,7 +1518,7 @@ Draws x/y/z lines from the origin for orientation debugging static void RB_SurfaceAxis( void ) { // FIXME: implement this #if 0 - GL_Bind( tr.whiteImage ); + GL_BindToTMU( tr.whiteImage, TB_COLORMAP ); GL_State( GLS_DEFAULT ); qglLineWidth( 3 ); qglBegin( GL_LINES ); diff --git a/code/server/server.h b/code/server/server.h index 407d6ab6..39d6f4d3 100644 --- a/code/server/server.h +++ b/code/server/server.h @@ -44,7 +44,7 @@ typedef struct voipServerPacket_s int len; int sender; int flags; - byte data[1024]; + byte data[4000]; } voipServerPacket_t; #endif @@ -299,6 +299,7 @@ extern int serverBansCount; #ifdef USE_VOIP extern cvar_t *sv_voip; +extern cvar_t *sv_voipProtocol; #endif diff --git a/code/server/sv_ccmds.c b/code/server/sv_ccmds.c index 1b723399..506afcca 100644 --- a/code/server/sv_ccmds.c +++ b/code/server/sv_ccmds.c @@ -1303,6 +1303,71 @@ static void SV_ConTell_f(void) { } +/* +================== +SV_ConSayto_f +================== +*/ +static void SV_ConSayto_f(void) { + char *p; + char text[1024]; + client_t *cl; + char *rawname; + char name[MAX_NAME_LENGTH]; + char cleanName[MAX_NAME_LENGTH]; + client_t *saytocl; + int i; + + // make sure server is running + if ( !com_sv_running->integer ) { + Com_Printf( "Server is not running.\n" ); + return; + } + + if ( Cmd_Argc() < 3 ) { + Com_Printf ("Usage: sayto \n"); + return; + } + + rawname = Cmd_Argv(1); + + //allowing special characters in the console + //with hex strings for player names + Com_FieldStringToPlayerName( name, MAX_NAME_LENGTH, rawname ); + + saytocl = NULL; + for ( i=0, cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++ ) { + if ( !cl->state ) { + continue; + } + Q_strncpyz( cleanName, cl->name, sizeof(cleanName) ); + Q_CleanStr( cleanName ); + + if ( !Q_stricmp( cleanName, name ) ) { + saytocl = cl; + break; + } + } + if( !saytocl ) + { + Com_Printf ("No such player name: %s.\n", name); + return; + } + + strcpy (text, "console_sayto: "); + p = Cmd_ArgsFrom(2); + + if ( *p == '"' ) { + p++; + p[strlen(p)-1] = 0; + } + + strcat(text, p); + + SV_SendServerCommand(saytocl, "chat \"%s\"", text); +} + + /* ================== SV_Heartbeat_f @@ -1407,6 +1472,43 @@ static void SV_CompleteMapName( char *args, int argNum ) { } } +/* +================== +SV_CompletePlayerName +================== +*/ +static void SV_CompletePlayerName( char *args, int argNum ) { + if( argNum == 2 ) { + char names[MAX_CLIENTS][MAX_NAME_LENGTH]; + char *namesPtr[MAX_CLIENTS]; + client_t *cl; + int i; + int nameCount; + int clientCount; + + nameCount = 0; + clientCount = sv_maxclients->integer; + + for ( i=0, cl=svs.clients ; i < clientCount; i++,cl++ ) { + if ( !cl->state ) { + continue; + } + if( i >= MAX_CLIENTS ) { + break; + } + Q_strncpyz( names[nameCount], cl->name, sizeof(names[nameCount]) ); + Q_CleanStr( names[nameCount] ); + + namesPtr[nameCount] = names[nameCount]; + + nameCount++; + } + qsort( (void*)namesPtr, nameCount, sizeof( namesPtr[0] ), Com_strCompare ); + + Field_CompletePlayerName( namesPtr, nameCount ); + } +} + /* ================== SV_AddOperatorCommands @@ -1453,6 +1555,8 @@ void SV_AddOperatorCommands( void ) { if( com_dedicated->integer ) { Cmd_AddCommand ("say", SV_ConSay_f); Cmd_AddCommand ("tell", SV_ConTell_f); + Cmd_AddCommand ("sayto", SV_ConSayto_f); + Cmd_SetCommandCompletionFunc( "sayto", SV_CompletePlayerName ); } Cmd_AddCommand("rehashbans", SV_RehashBans_f); diff --git a/code/server/sv_client.c b/code/server/sv_client.c index 4b1895e5..d45c49d7 100644 --- a/code/server/sv_client.c +++ b/code/server/sv_client.c @@ -1459,8 +1459,8 @@ void SV_UserinfoChanged( client_t *cl ) { else #endif { - val = Info_ValueForKey(cl->userinfo, "cl_voip"); - cl->hasVoip = atoi(val); + val = Info_ValueForKey(cl->userinfo, "cl_voipProtocol"); + cl->hasVoip = !Q_stricmp( val, "opus" ); } #endif @@ -1794,7 +1794,7 @@ static qboolean SV_ShouldIgnoreVoipSender(const client_t *cl) } static -void SV_UserVoip(client_t *cl, msg_t *msg) +void SV_UserVoip(client_t *cl, msg_t *msg, qboolean ignoreData) { int sender, generation, sequence, frames, packetsize; uint8_t recips[(MAX_CLIENTS + 7) / 8]; @@ -1829,12 +1829,12 @@ void SV_UserVoip(client_t *cl, msg_t *msg) MSG_ReadData(msg, encoded, packetsize); - if (SV_ShouldIgnoreVoipSender(cl)) + if (ignoreData || SV_ShouldIgnoreVoipSender(cl)) return; // Blacklisted, disabled, etc. // !!! FIXME: see if we read past end of msg... - // !!! FIXME: reject if not speex narrowband codec. + // !!! FIXME: reject if not opus data. // !!! FIXME: decide if this is bogus data? // decide who needs this VoIP packet sent to them... @@ -1983,10 +1983,18 @@ void SV_ExecuteClientMessage( client_t *cl, msg_t *msg ) { } } while ( 1 ); - // read optional voip data - if ( c == clc_voip ) { + // skip legacy speex voip data + if ( c == clc_voipSpeex ) { #ifdef USE_VOIP - SV_UserVoip( cl, msg ); + SV_UserVoip( cl, msg, qtrue ); + c = MSG_ReadByte( msg ); +#endif + } + + // read optional voip data + if ( c == clc_voipOpus ) { +#ifdef USE_VOIP + SV_UserVoip( cl, msg, qfalse ); c = MSG_ReadByte( msg ); #endif } diff --git a/code/server/sv_init.c b/code/server/sv_init.c index d2ec4dc4..82db240f 100644 --- a/code/server/sv_init.c +++ b/code/server/sv_init.c @@ -656,8 +656,9 @@ void SV_Init (void) sv_serverid = Cvar_Get ("sv_serverid", "0", CVAR_SYSTEMINFO | CVAR_ROM ); sv_pure = Cvar_Get ("sv_pure", "1", CVAR_SYSTEMINFO ); #ifdef USE_VOIP - sv_voip = Cvar_Get("sv_voip", "1", CVAR_SYSTEMINFO | CVAR_LATCH); + sv_voip = Cvar_Get("sv_voip", "1", CVAR_LATCH); Cvar_CheckRange(sv_voip, 0, 1, qtrue); + sv_voipProtocol = Cvar_Get("sv_voipProtocol", sv_voip->integer ? "opus" : "", CVAR_SYSTEMINFO | CVAR_ROM ); #endif Cvar_Get ("sv_paks", "", CVAR_SYSTEMINFO | CVAR_ROM ); Cvar_Get ("sv_pakNames", "", CVAR_SYSTEMINFO | CVAR_ROM ); diff --git a/code/server/sv_main.c b/code/server/sv_main.c index 4169fd2e..993910fe 100644 --- a/code/server/sv_main.c +++ b/code/server/sv_main.c @@ -24,6 +24,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #ifdef USE_VOIP cvar_t *sv_voip; +cvar_t *sv_voipProtocol; #endif serverStatic_t svs; // persistant server info @@ -665,8 +666,8 @@ void SVC_Info( netadr_t from ) { Info_SetValueForKey(infostring, "g_needpass", va("%d", Cvar_VariableIntegerValue("g_needpass"))); #ifdef USE_VOIP - if (sv_voip->integer) { - Info_SetValueForKey( infostring, "voip", va("%i", sv_voip->integer ) ); + if (sv_voipProtocol->string && *sv_voipProtocol->string) { + Info_SetValueForKey( infostring, "voip", sv_voipProtocol->string ); } #endif diff --git a/code/server/sv_snapshot.c b/code/server/sv_snapshot.c index 6e4f2907..2237abd3 100644 --- a/code/server/sv_snapshot.c +++ b/code/server/sv_snapshot.c @@ -547,7 +547,7 @@ static void SV_WriteVoipToClient(client_t *cl, msg_t *msg) if (totalbytes > (msg->maxsize - msg->cursize) / 2) break; - MSG_WriteByte(msg, svc_voip); + MSG_WriteByte(msg, svc_voipOpus); MSG_WriteShort(msg, packet->sender); MSG_WriteByte(msg, (byte) packet->generation); MSG_WriteLong(msg, packet->sequence); diff --git a/code/sys/sys_main.c b/code/sys/sys_main.c index 46817c8a..60cdb50c 100644 --- a/code/sys/sys_main.c +++ b/code/sys/sys_main.c @@ -644,12 +644,6 @@ int main( int argc, char **argv ) // Set the initial time base Sys_Milliseconds( ); -#ifdef MACOS_X - // This is passed if we are launched by double-clicking - if ( argc >= 2 && Q_strncmp ( argv[1], "-psn", 4 ) == 0 ) - argc = 1; -#endif - #ifdef MACOS_X // This is passed if we are launched by double-clicking if ( argc >= 2 && Q_strncmp ( argv[1], "-psn", 4 ) == 0 ) diff --git a/code/sys/sys_win32.c b/code/sys/sys_win32.c index 24fe1cf5..da7241a7 100644 --- a/code/sys/sys_win32.c +++ b/code/sys/sys_win32.c @@ -142,14 +142,8 @@ char *Sys_SteamPath( void ) qboolean finishPath = qfalse; #ifdef STEAMPATH_APPID - if (!steamPath[0] && !RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Steam App " STEAMPATH_APPID, 0, KEY_QUERY_VALUE, &steamRegKey)) - { - pathLen = MAX_OSPATH; - if (RegQueryValueEx(steamRegKey, "InstallLocation", NULL, NULL, (LPBYTE)steamPath, &pathLen)) - steamPath[0] = '\0'; - } - - if (!steamPath[0] && !RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Steam App " STEAMPATH_APPID, 0, KEY_QUERY_VALUE, &steamRegKey)) + // Assuming Steam is a 32-bit app + if (!steamPath[0] && !RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Steam App " STEAMPATH_APPID, 0, KEY_QUERY_VALUE | KEY_WOW64_32KEY, &steamRegKey)) { pathLen = MAX_OSPATH; if (RegQueryValueEx(steamRegKey, "InstallLocation", NULL, NULL, (LPBYTE)steamPath, &pathLen)) diff --git a/opengl2-readme.md b/opengl2-readme.md index ccf89751..d2757d4b 100644 --- a/opengl2-readme.md +++ b/opengl2-readme.md @@ -18,7 +18,7 @@ compatibility with existing Quake 3 mods. - Texture upsampling. - Advanced materials support. - Advanced shading and specular methods. - - LATC and BPTC texture compression support. + - RGTC and BPTC texture compression support. - Screen-space ambient occlusion. @@ -67,7 +67,7 @@ Cvars for simple rendering features: * `r_ext_compressed_textures` - Automatically compress textures. 0 - No texture compression. (default) - 1 - DXT/LATC texture compression if + 1 - DXT/RGTC texture compression if supported. 2 - BPTC texture compression if supported. @@ -160,24 +160,15 @@ Cvars for HDR and tonemapping: Cvars for advanced material usage: -* `r_normalMapping` - Enable normal mapping for materials that - support it, and also specify advanced - shading techniques. +* `r_normalMapping` - Enable normal maps for materials that + support it. 0 - No. 1 - Yes. (default) - 2 - Yes, and use Oren-Nayar reflectance - model. - 3 - Yes, and use tri-Ace's Oren-Nayar - reflectance model. -* `r_specularMapping` - Enable specular mapping for materials that - support it, and also specify advanced - specular techniques. +* `r_specularMapping` - Enable specular maps for materials that + support it. 0 - No. - 1 - Yes, and use tri-Ace. (default) - 2 - Yes, and use Blinn-Phong. - 3 - Yes, and use Cook-Torrance. - 4 - Yes, and use Torrance-Sparrow. + 1 - Yes. (default) * `r_deluxeMapping` - Enable deluxe mapping. (Map is compiled with light directions.) Even if the map @@ -231,6 +222,12 @@ Cvars for advanced material usage: 0.05 - Standard depth. (default) 0.1 - Looks broken. +* `r_pbr` - Enable physically based rendering. + Experimental, will not look correct without + assets meant for it. + 0 - No. (default) + 1 - Yes. + Cvars for image interpolation and generation: * `r_imageUpsample` - Use interpolation to artifically increase @@ -333,13 +330,6 @@ Cvars that you probably don't care about or shouldn't mess with: 0 - No. 1 - Yes. (default) -* `r_normalAmbient` - Split map light into ambient and directed - portions when doing deluxe mapping. Not - very useful. - 0 - Don't. (default). - 0.3 - 30% ambient, 70% directed. - 1.0 - 100% ambient. - * `r_mergeLightmaps` - Merge the small (128x128) lightmaps into 2 or fewer giant (4096x4096) lightmaps. Easy speedup. @@ -355,20 +345,6 @@ Cvars that you probably don't care about or shouldn't mess with: * `r_shadowCascadeZBias` - Z-bias for shadow cascade frustums. -256 - Default. -* `r_materialGamma` - Gamma level for material textures. - (diffuse, specular) - 1.0 - Quake 3, fastest. (default) - -* `r_lightGamma` - Gamma level for light. - (lightmap, lightgrid, vertex lights) - 1.0 - Quake 3, fastest. (default) - -* `r_framebufferGamma` - Gamma level for framebuffers. - 1.0 - Quake 3, fastest. (default) - -* `r_tonemapGamma` - Gamma applied after tonemapping. - 1.0 - Quake 3, fastest. (default) - Cvars that have broken bits: * `r_dlightMode` - Change how dynamic lights look. diff --git a/travis-ci-build.sh b/travis-ci-build.sh index 57c6297a..008bc7e1 100755 --- a/travis-ci-build.sh +++ b/travis-ci-build.sh @@ -6,9 +6,10 @@ failed=0; (make clean release) || failed=1; if [ $failed -eq 1 ]; then - echo "Build failure."; + echo "Build failure."; else - echo "Build successful."; + echo "Build successful."; fi -exit $failed; \ No newline at end of file +exit $failed; +