cg_autodrop should work now
This commit is contained in:
perle 2024-07-26 23:12:59 +02:00
commit d4b7971c8f
46 changed files with 1586 additions and 426 deletions

View file

@ -6,16 +6,16 @@ jobs:
name: Linux name: Linux
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Install Dependencies - name: Install Dependencies
run: | run: |
sudo apt-get update sudo apt-get update
sudo apt-get install libsdl2-dev sudo apt-get install libsdl2-dev
- name: Compile - name: Compile
run: make release -C engine run: make release -j$(nproc) -C engine
env: env:
ARCHIVE: 1 ARCHIVE: 1
- uses: actions/upload-artifact@v3 - uses: actions/upload-artifact@v4
with: with:
name: Linux name: Linux
path: engine/build/*.zip path: engine/build/*.zip
@ -23,27 +23,50 @@ jobs:
name: Windows name: Windows
runs-on: windows-2019 runs-on: windows-2019
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Compile - name: Compile
run: | run: |
choco install zip choco install zip
make release -C engine make release -j $env:NUMBER_OF_PROCESSORS -C engine
env: env:
ARCHIVE: 1 ARCHIVE: 1
- uses: actions/upload-artifact@v3 - uses: actions/upload-artifact@v4
with: with:
name: Windows name: Windows
path: engine/build/*.zip path: engine/build/*.zip
macos: macos:
name: macOS name: macOS
runs-on: macos-11 runs-on: macos-12
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Compile - name: Compile
run: make release -C engine run: make release -j$(sysctl -n hw.logicalcpu) -C engine
env: env:
ARCHIVE: 1 ARCHIVE: 1
- uses: actions/upload-artifact@v3 - uses: actions/upload-artifact@v4
with: with:
name: macOS name: macOS
path: engine/build/*.zip path: engine/build/*.zip
web:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v4
with:
repository: emscripten-core/emsdk
path: emsdk
- name: Install Dependencies
run: |
cd emsdk
./emsdk install 3.1.58
./emsdk activate 3.1.58
- name: Compile
env:
ARCHIVE: 1
run: |
source emsdk/emsdk_env.sh
emmake make release -j$(nproc) -C engine
- uses: actions/upload-artifact@v4
with:
name: Web
path: engine/build/*.zip

BIN
baseq3r/music/credits.ogg Normal file

Binary file not shown.

View file

@ -40,6 +40,9 @@ endif
ifndef BUILD_MISSIONPACK ifndef BUILD_MISSIONPACK
BUILD_MISSIONPACK= 0 BUILD_MISSIONPACK= 0
endif endif
ifndef BUILD_RENDERER_OPENGL1
BUILD_RENDERER_OPENGL1=
endif
ifndef BUILD_RENDERER_OPENGL2 ifndef BUILD_RENDERER_OPENGL2
BUILD_RENDERER_OPENGL2= BUILD_RENDERER_OPENGL2=
endif endif
@ -48,7 +51,7 @@ ifndef BUILD_AUTOUPDATER # DON'T build unless you mean to!
endif endif
# ioquake3 git commit that this is based on # ioquake3 git commit that this is based on
IOQ3_REVISION = 5ede35d8 IOQ3_REVISION = c1ab47a7
############################################################################# #############################################################################
# #
@ -64,6 +67,11 @@ ifeq ($(COMPILE_PLATFORM),cygwin)
PLATFORM=mingw32 PLATFORM=mingw32
endif endif
# detect "emmake make"
ifeq ($(findstring /emcc,$(CC)),/emcc)
PLATFORM=emscripten
endif
ifndef PLATFORM ifndef PLATFORM
PLATFORM=$(COMPILE_PLATFORM) PLATFORM=$(COMPILE_PLATFORM)
endif endif
@ -295,6 +303,7 @@ LIBTOMCRYPTSRCDIR=$(AUTOUPDATERSRCDIR)/rsa_tools/libtomcrypt-1.17
TOMSFASTMATHSRCDIR=$(AUTOUPDATERSRCDIR)/rsa_tools/tomsfastmath-0.13.1 TOMSFASTMATHSRCDIR=$(AUTOUPDATERSRCDIR)/rsa_tools/tomsfastmath-0.13.1
LOKISETUPDIR=misc/setup LOKISETUPDIR=misc/setup
NSISDIR=misc/nsis NSISDIR=misc/nsis
WEBDIR=$(MOUNT_DIR)/web
SDLHDIR=$(MOUNT_DIR)/SDL2 SDLHDIR=$(MOUNT_DIR)/SDL2
LIBSDIR=$(MOUNT_DIR)/libs LIBSDIR=$(MOUNT_DIR)/libs
@ -826,6 +835,8 @@ else # ifdef MINGW
############################################################################# #############################################################################
ifeq ($(PLATFORM),freebsd) ifeq ($(PLATFORM),freebsd)
# Use the default C compiler
TOOLS_CC=cc
# flags # flags
BASE_CFLAGS = \ BASE_CFLAGS = \
@ -1061,6 +1072,67 @@ ifeq ($(PLATFORM),sunos)
else # ifeq sunos else # ifeq sunos
#############################################################################
# SETUP AND BUILD -- emscripten
#############################################################################
ifeq ($(PLATFORM),emscripten)
ifneq ($(findstring /emcc,$(CC)),/emcc)
CC=emcc
endif
ARCH=wasm32
BINEXT=.js
# dlopen(), opengl1, and networking are not functional
USE_RENDERER_DLOPEN=0
USE_OPENAL_DLOPEN=0
BUILD_GAME_SO=0
BUILD_RENDERER_OPENGL1=0
BUILD_SERVER=0
CLIENT_CFLAGS+=-s USE_SDL=2
CLIENT_LDFLAGS+=-s TOTAL_MEMORY=256MB
CLIENT_LDFLAGS+=-s STACK_SIZE=5MB
CLIENT_LDFLAGS+=-s MIN_WEBGL_VERSION=1 -s MAX_WEBGL_VERSION=2
# The HTML file can use these functions to load extra files before the game starts.
CLIENT_LDFLAGS+=-s EXPORTED_RUNTIME_METHODS=FS,addRunDependency,removeRunDependency
CLIENT_LDFLAGS+=-s EXIT_RUNTIME=1
CLIENT_LDFLAGS+=-s EXPORT_ES6
CLIENT_LDFLAGS+=-s EXPORT_NAME=ioquake3
# Game data files can be packaged by emcc into a .data file that lives next to the wasm bundle
# and added to the virtual filesystem before the game starts. This requires the game data to be
# present at build time and it can't be changed afterward.
# For more flexibility, game data files can be loaded from a web server at runtime by listing
# them in client-config.json. This way they don't have to be present at build time and can be
# changed later.
ifeq ($(EMSCRIPTEN_PRELOAD_FILE),1)
ifeq ($(wildcard $(BASEGAME)/*),)
$(error "No files in '$(BASEGAME)' directory for emscripten to preload.")
endif
CLIENT_LDFLAGS+=--preload-file $(BASEGAME)
endif
OPTIMIZEVM = -O3
OPTIMIZE = $(OPTIMIZEVM) -ffast-math
# These allow a warning-free build.
# Some of these warnings may actually be legit problems and should be fixed at some point.
BASE_CFLAGS+=-Wno-deprecated-non-prototype -Wno-dangling-else -Wno-implicit-const-int-float-conversion -Wno-misleading-indentation -Wno-format-overflow -Wno-logical-not-parentheses -Wno-absolute-value
DEBUG_CFLAGS=-g3 -O0 # -fsanitize=address -fsanitize=undefined
# Emscripten needs debug compiler flags to be passed to the linker as well
DEBUG_LDFLAGS=$(DEBUG_CFLAGS)
SHLIBEXT=wasm
SHLIBCFLAGS=-fPIC
SHLIBLDFLAGS=-s SIDE_MODULE
else # ifeq emscripten
############################################################################# #############################################################################
# SETUP AND BUILD -- GENERIC # SETUP AND BUILD -- GENERIC
############################################################################# #############################################################################
@ -1079,6 +1151,7 @@ endif #OpenBSD
endif #NetBSD endif #NetBSD
endif #IRIX endif #IRIX
endif #SunOS endif #SunOS
endif #emscripten
ifndef CC ifndef CC
CC=gcc CC=gcc
@ -1115,12 +1188,18 @@ endif
ifneq ($(BUILD_CLIENT),0) ifneq ($(BUILD_CLIENT),0)
ifneq ($(USE_RENDERER_DLOPEN),0) ifneq ($(USE_RENDERER_DLOPEN),0)
TARGETS += $(B)/$(CLIENTBIN)$(FULLBINEXT) $(B)/renderer_opengl1_$(SHLIBNAME) TARGETS += $(B)/$(CLIENTBIN)$(FULLBINEXT)
ifneq ($(BUILD_RENDERER_OPENGL1),0)
TARGETS += $(B)/renderer_opengl1_$(SHLIBNAME)
endif
ifneq ($(BUILD_RENDERER_OPENGL2),0) ifneq ($(BUILD_RENDERER_OPENGL2),0)
TARGETS += $(B)/renderer_opengl2_$(SHLIBNAME) TARGETS += $(B)/renderer_opengl2_$(SHLIBNAME)
endif endif
else else
TARGETS += $(B)/$(CLIENTBIN)$(FULLBINEXT) ifneq ($(BUILD_RENDERER_OPENGL1),0)
TARGETS += $(B)/$(CLIENTBIN)$(FULLBINEXT)
endif
ifneq ($(BUILD_RENDERER_OPENGL2),0) ifneq ($(BUILD_RENDERER_OPENGL2),0)
TARGETS += $(B)/$(CLIENTBIN)_opengl2$(FULLBINEXT) TARGETS += $(B)/$(CLIENTBIN)_opengl2$(FULLBINEXT)
endif endif
@ -1167,6 +1246,42 @@ ifneq ($(BUILD_AUTOUPDATER),0)
TARGETS += $(B)/$(AUTOUPDATER_BIN) TARGETS += $(B)/$(AUTOUPDATER_BIN)
endif endif
ifeq ($(PLATFORM),emscripten)
ifneq ($(BUILD_SERVER),0)
GENERATEDTARGETS += $(B)/$(SERVERBIN).$(ARCH).wasm
ifeq ($(EMSCRIPTEN_PRELOAD_FILE),1)
GENERATEDTARGETS += $(B)/$(SERVERBIN).$(ARCH).data
endif
endif
ifneq ($(BUILD_CLIENT),0)
TARGETS += $(B)/$(CLIENTBIN).html
ifneq ($(EMSCRIPTEN_PRELOAD_FILE),1)
TARGETS += $(B)/$(CLIENTBIN)-config.json
endif
ifneq ($(USE_RENDERER_DLOPEN),0)
GENERATEDTARGETS += $(B)/$(CLIENTBIN).$(ARCH).wasm
ifeq ($(EMSCRIPTEN_PRELOAD_FILE),1)
GENERATEDTARGETS += $(B)/$(CLIENTBIN).$(ARCH).data
endif
else
ifneq ($(BUILD_RENDERER_OPENGL1),0)
GENERATEDTARGETS += $(B)/$(CLIENTBIN).$(ARCH).wasm
ifeq ($(EMSCRIPTEN_PRELOAD_FILE),1)
GENERATEDTARGETS += $(B)/$(CLIENTBIN).$(ARCH).data
endif
endif
ifneq ($(BUILD_RENDERER_OPENGL2),0)
GENERATEDTARGETS += $(B)/$(CLIENTBIN)_opengl2.$(ARCH).wasm
ifeq ($(EMSCRIPTEN_PRELOAD_FILE),1)
GENERATEDTARGETS += $(B)/$(CLIENTBIN)_opengl2.$(ARCH).data
endif
endif
endif
endif
endif
ifeq ($(USE_OPENAL),1) ifeq ($(USE_OPENAL),1)
CLIENT_CFLAGS += -DUSE_OPENAL CLIENT_CFLAGS += -DUSE_OPENAL
ifeq ($(USE_OPENAL_DLOPEN),1) ifeq ($(USE_OPENAL_DLOPEN),1)
@ -1451,7 +1566,8 @@ all: debug release
debug: debug:
@$(MAKE) targets B=$(BD) CFLAGS="$(CFLAGS) $(BASE_CFLAGS) $(DEPEND_CFLAGS)" \ @$(MAKE) targets B=$(BD) CFLAGS="$(CFLAGS) $(BASE_CFLAGS) $(DEPEND_CFLAGS)" \
OPTIMIZE="$(DEBUG_CFLAGS)" OPTIMIZEVM="$(DEBUG_CFLAGS)" \ OPTIMIZE="$(DEBUG_CFLAGS)" OPTIMIZEVM="$(DEBUG_CFLAGS)" \
CLIENT_CFLAGS="$(CLIENT_CFLAGS)" SERVER_CFLAGS="$(SERVER_CFLAGS)" V=$(V) CLIENT_CFLAGS="$(CLIENT_CFLAGS)" SERVER_CFLAGS="$(SERVER_CFLAGS)" V=$(V) \
LDFLAGS="$(LDFLAGS) $(DEBUG_LDFLAGS)"
release: release:
@$(MAKE) targets B=$(BR) CFLAGS="$(CFLAGS) $(BASE_CFLAGS) $(DEPEND_CFLAGS)" \ @$(MAKE) targets B=$(BR) CFLAGS="$(CFLAGS) $(BASE_CFLAGS) $(DEPEND_CFLAGS)" \
@ -1488,6 +1604,7 @@ ifneq ($(BUILD_CLIENT),0)
endif endif
NAKED_TARGETS=$(shell echo $(TARGETS) | sed -e "s!$(B)/!!g") NAKED_TARGETS=$(shell echo $(TARGETS) | sed -e "s!$(B)/!!g")
NAKED_GENERATEDTARGETS=$(shell echo $(GENERATEDTARGETS) | sed -e "s!$(B)/!!g")
print_list=-@for i in $(1); \ print_list=-@for i in $(1); \
do \ do \
@ -1546,6 +1663,7 @@ endif
@echo "" @echo ""
@echo " Output:" @echo " Output:"
$(call print_list, $(NAKED_TARGETS)) $(call print_list, $(NAKED_TARGETS))
$(call print_list, $(NAKED_GENERATEDTARGETS))
@echo "" @echo ""
ifneq ($(TARGETS),) ifneq ($(TARGETS),)
ifndef DEBUG_MAKEFILE ifndef DEBUG_MAKEFILE
@ -1562,9 +1680,10 @@ endif
ifneq ($(PLATFORM),darwin) ifneq ($(PLATFORM),darwin)
ifdef ARCHIVE ifdef ARCHIVE
@rm -f $@ @rm -f $@
@(cd $(B) && zip -r9 ../../$@ $(NAKED_TARGETS)) @(cd $(B) && zip -r9 ../../$@ $(NAKED_TARGETS) $(NAKED_GENERATEDTARGETS))
endif endif
endif endif
@:
makedirs: makedirs:
@$(MKDIR) $(B)/autoupdater @$(MKDIR) $(B)/autoupdater
@ -1920,10 +2039,15 @@ Q3OBJ = \
ifdef MINGW ifdef MINGW
Q3OBJ += \ Q3OBJ += \
$(B)/client/con_passive.o $(B)/client/con_passive.o
else
ifeq ($(PLATFORM),emscripten)
Q3OBJ += \
$(B)/client/con_passive.o
else else
Q3OBJ += \ Q3OBJ += \
$(B)/client/con_tty.o $(B)/client/con_tty.o
endif endif
endif
Q3R2OBJ = \ Q3R2OBJ = \
$(B)/renderergl2/tr_animation.o \ $(B)/renderergl2/tr_animation.o \
@ -3084,6 +3208,19 @@ $(B)/$(MISSIONPACK)/qcommon/%.asm: $(CMDIR)/%.c $(Q3LCC)
$(DO_Q3LCC_MISSIONPACK) $(DO_Q3LCC_MISSIONPACK)
#############################################################################
# EMSCRIPTEN
#############################################################################
$(B)/$(CLIENTBIN).html: $(WEBDIR)/client.html
$(echo_cmd) "SED $@"
$(Q)sed 's/__CLIENTBIN__/$(CLIENTBIN)/g;s/__BASEGAME__/$(BASEGAME)/g;s/__EMSCRIPTEN_PRELOAD_FILE__/$(EMSCRIPTEN_PRELOAD_FILE)/g' < $< > $@
$(B)/$(CLIENTBIN)-config.json: $(WEBDIR)/client-config.json
$(echo_cmd) "CP $@"
$(Q)cp $< $@
############################################################################# #############################################################################
# MISC # MISC
############################################################################# #############################################################################
@ -3107,13 +3244,18 @@ ifneq ($(BUILD_GAME_SO),0)
endif endif
ifneq ($(BUILD_CLIENT),0) ifneq ($(BUILD_CLIENT),0)
$(INSTALL) $(STRIP_FLAG) -m 0755 $(BR)/$(CLIENTBIN)$(FULLBINEXT) $(COPYBINDIR)/$(CLIENTBIN)$(FULLBINEXT)
ifneq ($(USE_RENDERER_DLOPEN),0) ifneq ($(USE_RENDERER_DLOPEN),0)
$(INSTALL) $(STRIP_FLAG) -m 0755 $(BR)/$(CLIENTBIN)$(FULLBINEXT) $(COPYBINDIR)/$(CLIENTBIN)$(FULLBINEXT)
ifneq ($(BUILD_RENDERER_OPENGL1),0)
$(INSTALL) $(STRIP_FLAG) -m 0755 $(BR)/renderer_opengl1_$(SHLIBNAME) $(COPYBINDIR)/renderer_opengl1_$(SHLIBNAME) $(INSTALL) $(STRIP_FLAG) -m 0755 $(BR)/renderer_opengl1_$(SHLIBNAME) $(COPYBINDIR)/renderer_opengl1_$(SHLIBNAME)
endif
ifneq ($(BUILD_RENDERER_OPENGL2),0) ifneq ($(BUILD_RENDERER_OPENGL2),0)
$(INSTALL) $(STRIP_FLAG) -m 0755 $(BR)/renderer_opengl2_$(SHLIBNAME) $(COPYBINDIR)/renderer_opengl2_$(SHLIBNAME) $(INSTALL) $(STRIP_FLAG) -m 0755 $(BR)/renderer_opengl2_$(SHLIBNAME) $(COPYBINDIR)/renderer_opengl2_$(SHLIBNAME)
endif endif
else else
ifneq ($(BUILD_RENDERER_OPENGL1),0)
$(INSTALL) $(STRIP_FLAG) -m 0755 $(BR)/$(CLIENTBIN)$(FULLBINEXT) $(COPYBINDIR)/$(CLIENTBIN)$(FULLBINEXT)
endif
ifneq ($(BUILD_RENDERER_OPENGL2),0) ifneq ($(BUILD_RENDERER_OPENGL2),0)
$(INSTALL) $(STRIP_FLAG) -m 0755 $(BR)/$(CLIENTBIN)_opengl2$(FULLBINEXT) $(COPYBINDIR)/$(CLIENTBIN)_opengl2$(FULLBINEXT) $(INSTALL) $(STRIP_FLAG) -m 0755 $(BR)/$(CLIENTBIN)_opengl2$(FULLBINEXT) $(COPYBINDIR)/$(CLIENTBIN)_opengl2$(FULLBINEXT)
endif endif
@ -3164,6 +3306,7 @@ clean2:
@rm -f $(OBJ_D_FILES) @rm -f $(OBJ_D_FILES)
@rm -f $(STRINGOBJ) @rm -f $(STRINGOBJ)
@rm -f $(TARGETS) @rm -f $(TARGETS)
@rm -f $(GENERATEDTARGETS)
toolsclean: toolsclean-debug toolsclean-release toolsclean: toolsclean-debug toolsclean-release
@ -3203,6 +3346,11 @@ dist:
# DEPENDENCIES # DEPENDENCIES
############################################################################# #############################################################################
# Rebuild every target if Makefile or Makefile.local changes
ifneq ($(DEPEND_MAKEFILE),0)
.EXTRA_PREREQS:= $(MAKEFILE_LIST)
endif
ifneq ($(B),) ifneq ($(B),)
OBJ_D_FILES=$(filter %.d,$(OBJ:%.o=%.d)) OBJ_D_FILES=$(filter %.d,$(OBJ:%.o=%.d))
TOOLSOBJ_D_FILES=$(filter %.d,$(TOOLSOBJ:%.o=%.d)) TOOLSOBJ_D_FILES=$(filter %.d,$(TOOLSOBJ:%.o=%.d))

View file

@ -933,10 +933,13 @@ static void waitToApplyUpdates(void)
OS forcibly closes the pipe), we will unblock. Then we can loop on OS forcibly closes the pipe), we will unblock. Then we can loop on
kill() until the process is truly gone. */ kill() until the process is truly gone. */
int x = 0; int x = 0;
struct timespec req;
req.tv_sec = 0;
req.tv_nsec = 100000000;
read(3, &x, sizeof (x)); read(3, &x, sizeof (x));
info("Pipe has closed, waiting for process to fully go away now."); info("Pipe has closed, waiting for process to fully go away now.");
while (kill(options.waitforprocess, 0) == 0) { while (kill(options.waitforprocess, 0) == 0) {
usleep(100000); nanosleep(&req, NULL);
} }
#endif #endif
} }

View file

@ -42,6 +42,8 @@ char systemChat[256];
char teamChat1[256]; char teamChat1[256];
char teamChat2[256]; char teamChat2[256];
static void CG_DrawRallyPowerups( void );
#ifdef MISSIONPACK #ifdef MISSIONPACK
int CG_Text_Width(const char *text, float scale, int limit) { int CG_Text_Width(const char *text, float scale, int limit) {
@ -746,6 +748,7 @@ static void CG_DrawRallyStatusBar( void ) {
return; return;
} }
CG_SetScreenPlacement(PLACE_CENTER, PLACE_BOTTOM);
// draw the dtf sigils // draw the dtf sigils
@ -785,7 +788,7 @@ static void CG_DrawRallyStatusBar( void ) {
// draw ammo background // draw ammo background
value = ps->ammo[cent->currentState.weapon]; value = ps->ammo[cent->currentState.weapon];
if ( value > -1 ) if ( cent->currentState.weapon && value > -1 )
CG_FillRect( 20, 476 - 30, 90, 24, bg_color ); CG_FillRect( 20, 476 - 30, 90, 24, bg_color );
// health background // health background
@ -838,6 +841,8 @@ static void CG_DrawRallyStatusBar( void ) {
healthModel, 0, origin, angles ); healthModel, 0, origin, angles );
} }
CG_DrawRallyPowerups();
if (cg.predictedPlayerState.powerups[PW_REDFLAG]) if (cg.predictedPlayerState.powerups[PW_REDFLAG])
CG_DrawStatusBarFlag( 495, TEAM_RED); CG_DrawStatusBarFlag( 495, TEAM_RED);
else if (cg.predictedPlayerState.powerups[PW_BLUEFLAG]) else if (cg.predictedPlayerState.powerups[PW_BLUEFLAG])
@ -1093,7 +1098,7 @@ static float CG_DrawPowerups( float y ) {
CG_DrawRallyPowerups CG_DrawRallyPowerups
==================== ====================
*/ */
static float CG_DrawRallyPowerups( float y ) { static void CG_DrawRallyPowerups( void ) {
int sorted[MAX_POWERUPS]; int sorted[MAX_POWERUPS];
int sortedTime[MAX_POWERUPS]; int sortedTime[MAX_POWERUPS];
int i, j, k; int i, j, k;
@ -1114,7 +1119,7 @@ static float CG_DrawRallyPowerups( float y ) {
ps = &cg.snap->ps; ps = &cg.snap->ps;
if ( ps->stats[STAT_HEALTH] <= 0 ) { if ( ps->stats[STAT_HEALTH] <= 0 ) {
return y; return;
} }
switch (cgs.clientinfo[cg.snap->ps.clientNum].team){ switch (cgs.clientinfo[cg.snap->ps.clientNum].team){
@ -1181,10 +1186,10 @@ static float CG_DrawRallyPowerups( float y ) {
color = 1; color = 1;
CG_FillRect( 402, 476 - 28, 90, 24, bg_color ); CG_FillRect( 402, 476 - 30, 90, 24, bg_color );
trap_R_SetColor( colors[color] ); trap_R_SetColor( colors[color] );
CG_DrawField( 424 + CHAR_WIDTH, 476 - 26, 2, sortedTime[ i ] / 1000 ); CG_DrawField( 424 + CHAR_WIDTH, 476 - 28, 2, sortedTime[ i ] / 1000 );
t = ps->powerups[ sorted[i] ]; t = ps->powerups[ sorted[i] ];
if ( t - cg.time >= POWERUP_BLINKS * POWERUP_BLINK_TIME ) { if ( t - cg.time >= POWERUP_BLINKS * POWERUP_BLINK_TIME ) {
@ -1211,13 +1216,9 @@ static float CG_DrawRallyPowerups( float y ) {
size = 19; size = 19;
} }
CG_DrawPic( 408, 476 - 25, size, size, trap_R_RegisterShader( item->icon ) ); CG_DrawPic( 408, 476 - 27, size, size, trap_R_RegisterShader( item->icon ) );
// y -= 36;
} }
trap_R_SetColor( NULL ); trap_R_SetColor( NULL );
return y;
} }
#endif // MISSIONPACK #endif // MISSIONPACK
// Q3Rally Code END // Q3Rally Code END
@ -1846,8 +1847,6 @@ static void CG_DrawLowerRight( void ) {
if ( isRaceObserver( cg.snap->ps.clientNum ) ) if ( isRaceObserver( cg.snap->ps.clientNum ) )
return; return;
CG_DrawRallyPowerups( 476 );
y = CG_DrawLowerRightHUD( y ); y = CG_DrawLowerRightHUD( y );
if ( cgs.gametype >= GT_TEAM && cg_drawTeamOverlay.integer == 2 ) { if ( cgs.gametype >= GT_TEAM && cg_drawTeamOverlay.integer == 2 ) {

View file

@ -36,14 +36,6 @@ void CG_CheckAmmo( void ) {
int previous; int previous;
int weapons; int weapons;
// Q3Rally Code Start
if ( cg.snap->ps.weapon == WP_NONE ||
cg.snap->ps.weapon == WP_GAUNTLET ) {
cg.lowAmmoWarning = 0;
return;
}
// Q3Rally Code END
// see about how many seconds of ammo we have remaining // see about how many seconds of ammo we have remaining
weapons = cg.snap->ps.stats[ STAT_WEAPONS ]; weapons = cg.snap->ps.stats[ STAT_WEAPONS ];
total = 0; total = 0;

View file

@ -27,13 +27,13 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
void CG_DrawCheckpointLinks(void) void CG_DrawCheckpointLinks(void)
{ {
int i, j; int i, j;
centity_t *cents[40]; centity_t *cents[100];
qboolean checkpointFound; qboolean checkpointFound;
int numCheckpoints = 0; int numCheckpoints = 0;
vec3_t handle; vec3_t handle;
// FIXME: max of 40 checkpoints // Checkpoint limit
for (i = 0; i < 40; i++) for (i = 0; i < 100; i++)
{ {
checkpointFound = qfalse; checkpointFound = qfalse;
for (j = 0; j < MAX_GENTITIES; j++) for (j = 0; j < MAX_GENTITIES; j++)

View file

@ -357,11 +357,12 @@ qboolean CL_OpenAVIForWriting( const char *fileName )
else else
afd.motionJpeg = qfalse; afd.motionJpeg = qfalse;
// Buffers only need to store RGB pixels. // Capture buffer stores RGB pixels but OpenGL ES reads RGBA and converts to RGB in-place.
// Encode buffer only needs to store RGB pixels.
// Allocate a bit more space for the capture buffer to account for possible // Allocate a bit more space for the capture buffer to account for possible
// padding at the end of pixel lines, and padding for alignment // padding at the end of pixel lines, and padding for alignment
#define MAX_PACK_LEN 16 #define MAX_PACK_LEN 16
afd.cBuffer = Z_Malloc((afd.width * 3 + MAX_PACK_LEN - 1) * afd.height + MAX_PACK_LEN - 1); afd.cBuffer = Z_Malloc((afd.width * 4 + MAX_PACK_LEN - 1) * afd.height + MAX_PACK_LEN - 1);
// raw avi files have pixel lines start on 4-byte boundaries // raw avi files have pixel lines start on 4-byte boundaries
afd.eBuffer = Z_Malloc(PAD(afd.width * 3, AVI_LINE_PADDING) * afd.height); afd.eBuffer = Z_Malloc(PAD(afd.width * 3, AVI_LINE_PADDING) * afd.height);

View file

@ -1964,7 +1964,7 @@ void Cmd_SaveBPoints_f( gentity_t *other )
Com_Printf( "Writing out bezier path information to: '%s'\n", buffer ); Com_Printf( "Writing out bezier path information to: '%s'\n", buffer );
trap_FS_FOpenFile( buffer, &f, FS_WRITE ); trap_FS_FOpenFile( buffer, &f, FS_WRITE );
for (i = 1; i < 40; i++) for (i = 1; i < 100; i++)
{ {
ent = NULL; ent = NULL;
while ((ent = G_Find (ent, FOFS(classname), "rally_checkpoint")) != NULL) { while ((ent = G_Find (ent, FOFS(classname), "rally_checkpoint")) != NULL) {

View file

@ -181,7 +181,7 @@ static cvarTable_t gameCvarTable[] = {
{ &g_speed, "g_speed", "320", 0, 0, qtrue }, { &g_speed, "g_speed", "320", 0, 0, qtrue },
// STONELANCE // STONELANCE
// { &g_gravity, "g_gravity", "800", 0, 0, qtrue }, // { &g_gravity, "g_gravity", "800", 0, 0, qtrue },
{ &g_gravity, "g_gravity", "1100", 0, 0, qtrue }, { &g_gravity, "g_gravity", "1400", 0, 0, qtrue },
// END // END
{ &g_knockback, "g_knockback", "1000", 0, 0, qtrue }, { &g_knockback, "g_knockback", "1000", 0, 0, qtrue },
{ &g_quadfactor, "g_quadfactor", "3", 0, 0, qtrue }, { &g_quadfactor, "g_quadfactor", "3", 0, 0, qtrue },

View file

@ -701,7 +701,7 @@ void SP_worldspawn( void ) {
// STONELANCE // STONELANCE
// G_SpawnString( "gravity", "800", &s ); // G_SpawnString( "gravity", "800", &s );
G_SpawnString( "gravity", "1100", &s ); G_SpawnString( "gravity", "1400", &s );
// END // END
trap_Cvar_Set( "g_gravity", s ); trap_Cvar_Set( "g_gravity", s );

View file

@ -302,7 +302,7 @@ void UI_CreditMenu( void ) {
mvolume = trap_Cvar_VariableValue( "s_musicvolume" ); mvolume = trap_Cvar_VariableValue( "s_musicvolume" );
if(mvolume < 0.5) if(mvolume < 0.5)
trap_Cmd_ExecuteText( EXEC_APPEND, "s_musicvolume 0.5\n" ); trap_Cmd_ExecuteText( EXEC_APPEND, "s_musicvolume 0.5\n" );
trap_Cmd_ExecuteText( EXEC_APPEND, "music music/amp6_22k\n" ); trap_Cmd_ExecuteText( EXEC_APPEND, "music music/credits\n" );
// load the background shader // load the background shader
#ifdef BACKGROUND_SHADER #ifdef BACKGROUND_SHADER

View file

@ -595,7 +595,7 @@ void UI_MainMenu( void ) {
s_main.discordlogo.generic.type = MTYPE_BITMAP; s_main.discordlogo.generic.type = MTYPE_BITMAP;
s_main.discordlogo.generic.flags = QMF_INACTIVE; s_main.discordlogo.generic.flags = QMF_INACTIVE;
s_main.discordlogo.generic.name = ART_DISCORDLOGO; s_main.discordlogo.generic.name = ART_DISCORDLOGO;
s_main.discordlogo.generic.x = 0; s_main.discordlogo.generic.x = -50;
s_main.discordlogo.generic.y = 461; s_main.discordlogo.generic.y = 461;
s_main.discordlogo.width = 442 / 3; s_main.discordlogo.width = 442 / 3;
s_main.discordlogo.height = 40 / 3; s_main.discordlogo.height = 40 / 3;

View file

@ -290,6 +290,22 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#endif #endif
//================================================================== EMSCRIPTEN ===
#ifdef __EMSCRIPTEN__
#define OS_STRING "emscripten"
#define ID_INLINE inline
#define PATH_SEP '/'
#define ARCH_STRING "wasm32"
#define Q3_LITTLE_ENDIAN
#define DLL_EXT ".wasm"
#endif
//================================================================== Q3VM === //================================================================== Q3VM ===
#ifdef Q3_VM #ifdef Q3_VM

View file

@ -80,7 +80,6 @@ extern void (APIENTRYP qglUnlockArraysEXT) (void);
GLE(void, TexParameterf, GLenum target, GLenum pname, GLfloat param) \ GLE(void, TexParameterf, GLenum target, GLenum pname, GLfloat param) \
GLE(void, TexParameteri, GLenum target, GLenum pname, GLint param) \ GLE(void, TexParameteri, GLenum target, GLenum pname, GLint param) \
GLE(void, TexSubImage2D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels) \ GLE(void, TexSubImage2D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels) \
GLE(void, Translatef, GLfloat x, GLfloat y, GLfloat z) \
GLE(void, Viewport, GLint x, GLint y, GLsizei width, GLsizei height) \ GLE(void, Viewport, GLint x, GLint y, GLsizei width, GLsizei height) \
// OpenGL 1.0/1.1 and OpenGL ES 1.x but not OpenGL 3.2 core profile // OpenGL 1.0/1.1 and OpenGL ES 1.x but not OpenGL 3.2 core profile
@ -98,6 +97,7 @@ extern void (APIENTRYP qglUnlockArraysEXT) (void);
GLE(void, ShadeModel, GLenum mode) \ GLE(void, ShadeModel, GLenum mode) \
GLE(void, TexCoordPointer, GLint size, GLenum type, GLsizei stride, const GLvoid *ptr) \ GLE(void, TexCoordPointer, GLint size, GLenum type, GLsizei stride, const GLvoid *ptr) \
GLE(void, TexEnvf, GLenum target, GLenum pname, GLfloat param) \ GLE(void, TexEnvf, GLenum target, GLenum pname, GLfloat param) \
GLE(void, Translatef, GLfloat x, GLfloat y, GLfloat z) \
GLE(void, VertexPointer, GLint size, GLenum type, GLsizei stride, const GLvoid *ptr) \ GLE(void, VertexPointer, GLint size, GLenum type, GLsizei stride, const GLvoid *ptr) \
// OpenGL 1.0/1.1 and 3.2 core profile but not OpenGL ES 1.x // OpenGL 1.0/1.1 and 3.2 core profile but not OpenGL ES 1.x

View file

@ -387,12 +387,17 @@ static void DrawSkySide( struct image_s *image, const int mins[2], const int max
static void DrawSkyBox( shader_t *shader ) static void DrawSkyBox( shader_t *shader )
{ {
int i; int i;
float w_offset, w_scale;
float h_offset, h_scale;
sky_min = 0; sky_min = 0;
sky_max = 1; sky_max = 1;
Com_Memset( s_skyTexCoords, 0, sizeof( s_skyTexCoords ) ); Com_Memset( s_skyTexCoords, 0, sizeof( s_skyTexCoords ) );
w_offset = h_offset = 0;
w_scale = h_scale = 1;
for (i=0 ; i<6 ; i++) for (i=0 ; i<6 ; i++)
{ {
int sky_mins_subd[2], sky_maxs_subd[2]; int sky_mins_subd[2], sky_maxs_subd[2];
@ -432,6 +437,15 @@ static void DrawSkyBox( shader_t *shader )
else if ( sky_maxs_subd[1] > HALF_SKY_SUBDIVISIONS ) else if ( sky_maxs_subd[1] > HALF_SKY_SUBDIVISIONS )
sky_maxs_subd[1] = HALF_SKY_SUBDIVISIONS; sky_maxs_subd[1] = HALF_SKY_SUBDIVISIONS;
if ( !haveClampToEdge )
{
w_offset = 0.5f / shader->sky.outerbox[sky_texorder[i]]->width;
h_offset = 0.5f / shader->sky.outerbox[sky_texorder[i]]->height;
w_scale = 1.0f - w_offset * 2;
h_scale = 1.0f - h_offset * 2;
}
// //
// iterate through the subdivisions // iterate through the subdivisions
// //
@ -444,6 +458,12 @@ static void DrawSkyBox( shader_t *shader )
i, i,
s_skyTexCoords[t][s], s_skyTexCoords[t][s],
s_skyPoints[t][s] ); s_skyPoints[t][s] );
s_skyTexCoords[t][s][0] *= w_scale;
s_skyTexCoords[t][s][0] += w_offset;
s_skyTexCoords[t][s][1] *= h_scale;
s_skyTexCoords[t][s][1] += h_offset;
} }
} }

View file

@ -52,10 +52,9 @@ vec4 depthGaussian1D(sampler2D imageMap, sampler2D depthMap, vec2 tex, float zFa
#endif #endif
float zLimit = 5.0 / zFar; float zLimit = 5.0 / zFar;
int i, j; for (int i = 0; i < 2; i++)
for (i = 0; i < 2; i++)
{ {
for (j = 1; j < BLUR_SIZE; j++) for (int j = 1; j < BLUR_SIZE; j++)
{ {
vec2 offset = direction * (float(j) - 0.25) + nudge; vec2 offset = direction * (float(j) - 0.25) + nudge;
#if defined(USE_DEPTH) #if defined(USE_DEPTH)

View file

@ -16,8 +16,16 @@ attribute vec4 attr_TexCoord0;
attribute vec4 attr_TexCoord1; attribute vec4 attr_TexCoord1;
#endif #endif
uniform vec4 u_DiffuseTexMatrix; #if defined(USE_TCMOD)
uniform vec4 u_DiffuseTexOffTurb; uniform vec4 u_DiffuseTexMatrix0;
uniform vec4 u_DiffuseTexMatrix1;
uniform vec4 u_DiffuseTexMatrix2;
uniform vec4 u_DiffuseTexMatrix3;
uniform vec4 u_DiffuseTexMatrix4;
uniform vec4 u_DiffuseTexMatrix5;
uniform vec4 u_DiffuseTexMatrix6;
uniform vec4 u_DiffuseTexMatrix7;
#endif
#if defined(USE_TCGEN) || defined(USE_RGBAGEN) #if defined(USE_TCGEN) || defined(USE_RGBAGEN)
uniform vec3 u_LocalViewOrigin; uniform vec3 u_LocalViewOrigin;
@ -140,19 +148,28 @@ vec2 GenTexCoords(int TCGen, vec3 position, vec3 normal, vec3 TCGenVector0, vec3
#endif #endif
#if defined(USE_TCMOD) #if defined(USE_TCMOD)
vec2 ModTexCoords(vec2 st, vec3 position, vec4 texMatrix, vec4 offTurb) vec2 ModTexCoords(vec2 st, vec3 position, vec4 texMatrix[8])
{ {
float amplitude = offTurb.z; vec2 st2 = st;
float phase = offTurb.w * 2.0 * M_PI;
vec2 st2;
st2.x = st.x * texMatrix.x + (st.y * texMatrix.z + offTurb.x);
st2.y = st.x * texMatrix.y + (st.y * texMatrix.w + offTurb.y);
vec2 offsetPos = vec2(position.x + position.z, position.y); vec2 offsetPos = vec2(position.x + position.z, position.y);
vec2 texOffset = sin(offsetPos * (2.0 * M_PI / 1024.0) + vec2(phase)); st2 = vec2(st2.x * texMatrix[0].x + st2.y * texMatrix[0].y + texMatrix[0].z,
st2.x * texMatrix[1].x + st2.y * texMatrix[1].y + texMatrix[1].z);
return st2 + texOffset * amplitude; st2 += texMatrix[0].w * sin(offsetPos * (2.0 * M_PI / 1024.0) + vec2(texMatrix[1].w * 2.0 * M_PI));
st2 = vec2(st2.x * texMatrix[2].x + st2.y * texMatrix[2].y + texMatrix[2].z,
st2.x * texMatrix[3].x + st2.y * texMatrix[3].y + texMatrix[3].z);
st2 += texMatrix[2].w * sin(offsetPos * (2.0 * M_PI / 1024.0) + vec2(texMatrix[3].w * 2.0 * M_PI));
st2 = vec2(st2.x * texMatrix[4].x + st2.y * texMatrix[4].y + texMatrix[4].z,
st2.x * texMatrix[5].x + st2.y * texMatrix[5].y + texMatrix[5].z);
st2 += texMatrix[4].w * sin(offsetPos * (2.0 * M_PI / 1024.0) + vec2(texMatrix[5].w * 2.0 * M_PI));
st2 = vec2(st2.x * texMatrix[6].x + st2.y * texMatrix[6].y + texMatrix[6].z,
st2.x * texMatrix[7].x + st2.y * texMatrix[7].y + texMatrix[7].z);
st2 += texMatrix[6].w * sin(offsetPos * (2.0 * M_PI / 1024.0) + vec2(texMatrix[7].w * 2.0 * M_PI));
return st2;
} }
#endif #endif
@ -236,7 +253,16 @@ void main()
#endif #endif
#if defined(USE_TCMOD) #if defined(USE_TCMOD)
var_DiffuseTex = ModTexCoords(tex, position, u_DiffuseTexMatrix, u_DiffuseTexOffTurb); vec4 diffuseTexMatrix[8];
diffuseTexMatrix[0] = u_DiffuseTexMatrix0;
diffuseTexMatrix[1] = u_DiffuseTexMatrix1;
diffuseTexMatrix[2] = u_DiffuseTexMatrix2;
diffuseTexMatrix[3] = u_DiffuseTexMatrix3;
diffuseTexMatrix[4] = u_DiffuseTexMatrix4;
diffuseTexMatrix[5] = u_DiffuseTexMatrix5;
diffuseTexMatrix[6] = u_DiffuseTexMatrix6;
diffuseTexMatrix[7] = u_DiffuseTexMatrix7;
var_DiffuseTex = ModTexCoords(tex, position, diffuseTexMatrix);
#else #else
var_DiffuseTex = tex; var_DiffuseTex = tex;
#endif #endif

View file

@ -37,8 +37,14 @@ uniform vec3 u_LocalViewOrigin;
#endif #endif
#if defined(USE_TCMOD) #if defined(USE_TCMOD)
uniform vec4 u_DiffuseTexMatrix; uniform vec4 u_DiffuseTexMatrix0;
uniform vec4 u_DiffuseTexOffTurb; uniform vec4 u_DiffuseTexMatrix1;
uniform vec4 u_DiffuseTexMatrix2;
uniform vec4 u_DiffuseTexMatrix3;
uniform vec4 u_DiffuseTexMatrix4;
uniform vec4 u_DiffuseTexMatrix5;
uniform vec4 u_DiffuseTexMatrix6;
uniform vec4 u_DiffuseTexMatrix7;
#endif #endif
uniform mat4 u_ModelViewProjectionMatrix; uniform mat4 u_ModelViewProjectionMatrix;
@ -114,19 +120,28 @@ vec2 GenTexCoords(int TCGen, vec3 position, vec3 normal, vec3 TCGenVector0, vec3
#endif #endif
#if defined(USE_TCMOD) #if defined(USE_TCMOD)
vec2 ModTexCoords(vec2 st, vec3 position, vec4 texMatrix, vec4 offTurb) vec2 ModTexCoords(vec2 st, vec3 position, vec4 texMatrix[8])
{ {
float amplitude = offTurb.z; vec2 st2 = st;
float phase = offTurb.w * 2.0 * M_PI;
vec2 st2;
st2.x = st.x * texMatrix.x + (st.y * texMatrix.z + offTurb.x);
st2.y = st.x * texMatrix.y + (st.y * texMatrix.w + offTurb.y);
vec2 offsetPos = vec2(position.x + position.z, position.y); vec2 offsetPos = vec2(position.x + position.z, position.y);
vec2 texOffset = sin(offsetPos * (2.0 * M_PI / 1024.0) + vec2(phase)); st2 = vec2(st2.x * texMatrix[0].x + st2.y * texMatrix[0].y + texMatrix[0].z,
st2.x * texMatrix[1].x + st2.y * texMatrix[1].y + texMatrix[1].z);
st2 += texMatrix[0].w * sin(offsetPos * (2.0 * M_PI / 1024.0) + vec2(texMatrix[1].w * 2.0 * M_PI));
return st2 + texOffset * amplitude; st2 = vec2(st2.x * texMatrix[2].x + st2.y * texMatrix[2].y + texMatrix[2].z,
st2.x * texMatrix[3].x + st2.y * texMatrix[3].y + texMatrix[3].z);
st2 += texMatrix[2].w * sin(offsetPos * (2.0 * M_PI / 1024.0) + vec2(texMatrix[3].w * 2.0 * M_PI));
st2 = vec2(st2.x * texMatrix[4].x + st2.y * texMatrix[4].y + texMatrix[4].z,
st2.x * texMatrix[5].x + st2.y * texMatrix[5].y + texMatrix[5].z);
st2 += texMatrix[4].w * sin(offsetPos * (2.0 * M_PI / 1024.0) + vec2(texMatrix[5].w * 2.0 * M_PI));
st2 = vec2(st2.x * texMatrix[6].x + st2.y * texMatrix[6].y + texMatrix[6].z,
st2.x * texMatrix[7].x + st2.y * texMatrix[7].y + texMatrix[7].z);
st2 += texMatrix[6].w * sin(offsetPos * (2.0 * M_PI / 1024.0) + vec2(texMatrix[7].w * 2.0 * M_PI));
return st2;
} }
#endif #endif
@ -183,7 +198,16 @@ void main()
#endif #endif
#if defined(USE_TCMOD) #if defined(USE_TCMOD)
var_TexCoords.xy = ModTexCoords(texCoords, position, u_DiffuseTexMatrix, u_DiffuseTexOffTurb); vec4 diffuseTexMatrix[8];
diffuseTexMatrix[0] = u_DiffuseTexMatrix0;
diffuseTexMatrix[1] = u_DiffuseTexMatrix1;
diffuseTexMatrix[2] = u_DiffuseTexMatrix2;
diffuseTexMatrix[3] = u_DiffuseTexMatrix3;
diffuseTexMatrix[4] = u_DiffuseTexMatrix4;
diffuseTexMatrix[5] = u_DiffuseTexMatrix5;
diffuseTexMatrix[6] = u_DiffuseTexMatrix6;
diffuseTexMatrix[7] = u_DiffuseTexMatrix7;
var_TexCoords.xy = ModTexCoords(texCoords, position, diffuseTexMatrix);
#else #else
var_TexCoords.xy = texCoords; var_TexCoords.xy = texCoords;
#endif #endif

View file

@ -77,8 +77,7 @@ float ambientOcclusion(sampler2D depthMap, const vec2 tex, const float zFarDivZN
float invZFar = 1.0 / zFar; float invZFar = 1.0 / zFar;
float zLimit = 20.0 * invZFar; float zLimit = 20.0 * invZFar;
int i; for (int i = 0; i < NUM_SAMPLES; i++)
for (i = 0; i < NUM_SAMPLES; i++)
{ {
vec2 offset = rmat * poissonDisc[i] * offsetScale; vec2 offset = rmat * poissonDisc[i] * offsetScale;
float sampleDiff = getLinearDepth(depthMap, tex + offset, zFarDivZNear) - sampleZ; float sampleDiff = getLinearDepth(depthMap, tex + offset, zFarDivZNear) - sampleZ;

View file

@ -342,7 +342,7 @@ void RB_BeginDrawingView (void) {
{ {
FBO_t *fbo = backEnd.viewParms.targetFbo; FBO_t *fbo = backEnd.viewParms.targetFbo;
if (fbo == NULL && (!r_postProcess->integer || !(backEnd.refdef.rdflags & RDF_NOWORLDMODEL))) if (fbo == NULL)
fbo = tr.renderFbo; fbo = tr.renderFbo;
if (tr.renderCubeFbo && fbo == tr.renderCubeFbo) if (tr.renderCubeFbo && fbo == tr.renderCubeFbo)
@ -455,7 +455,7 @@ void RB_RenderDrawSurfList( drawSurf_t *drawSurfs, int numDrawSurfs ) {
for (i = 0, drawSurf = drawSurfs ; i < numDrawSurfs ; i++, drawSurf++) { for (i = 0, drawSurf = drawSurfs ; i < numDrawSurfs ; i++, drawSurf++) {
if ( drawSurf->sort == oldSort && drawSurf->cubemapIndex == oldCubemapIndex) { if ( drawSurf->sort == oldSort && drawSurf->cubemapIndex == oldCubemapIndex) {
if (backEnd.depthFill && shader && shader->sort != SS_OPAQUE) if (backEnd.depthFill && shader && (shader->sort != SS_OPAQUE && shader->sort != SS_PORTAL))
continue; continue;
// fast path, same as previous sort // fast path, same as previous sort
@ -484,7 +484,7 @@ void RB_RenderDrawSurfList( drawSurf_t *drawSurfs, int numDrawSurfs ) {
oldCubemapIndex = cubemapIndex; oldCubemapIndex = cubemapIndex;
} }
if (backEnd.depthFill && shader && shader->sort != SS_OPAQUE) if (backEnd.depthFill && shader && (shader->sort != SS_OPAQUE && shader->sort != SS_PORTAL))
continue; continue;
// //
@ -708,7 +708,7 @@ void RE_StretchRaw (int x, int y, int w, int h, int cols, int rows, const byte *
if (glRefConfig.framebufferObject) if (glRefConfig.framebufferObject)
{ {
FBO_Bind(r_postProcess->integer ? NULL : tr.renderFbo); FBO_Bind(tr.renderFbo);
} }
RB_SetGL2D(); RB_SetGL2D();
@ -732,6 +732,7 @@ 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) { void RE_UploadCinematic (int w, int h, int cols, int rows, const byte *data, int client, qboolean dirty) {
byte *buffer;
GLuint texture; GLuint texture;
if (!tr.scratchImage[client]) if (!tr.scratchImage[client])
@ -746,7 +747,18 @@ void RE_UploadCinematic (int w, int h, int cols, int rows, const byte *data, int
if ( cols != tr.scratchImage[client]->width || rows != tr.scratchImage[client]->height ) { if ( cols != tr.scratchImage[client]->width || rows != tr.scratchImage[client]->height ) {
tr.scratchImage[client]->width = tr.scratchImage[client]->uploadWidth = cols; tr.scratchImage[client]->width = tr.scratchImage[client]->uploadWidth = cols;
tr.scratchImage[client]->height = tr.scratchImage[client]->uploadHeight = rows; tr.scratchImage[client]->height = tr.scratchImage[client]->uploadHeight = rows;
qglTextureImage2DEXT(texture, GL_TEXTURE_2D, 0, GL_RGB8, cols, rows, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
if ( qglesMajorVersion >= 1 ) {
buffer = ri.Hunk_AllocateTempMemory( 3 * cols * rows );
R_ConvertTextureFormat( data, cols, rows, GL_RGB, GL_UNSIGNED_BYTE, buffer );
qglTextureImage2DEXT(texture, GL_TEXTURE_2D, 0, GL_RGB, cols, rows, 0, GL_RGB, GL_UNSIGNED_BYTE, buffer);
ri.Hunk_FreeTempMemory( buffer );
} else {
qglTextureImage2DEXT(texture, GL_TEXTURE_2D, 0, GL_RGB8, cols, rows, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
}
qglTextureParameterfEXT(texture, GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); qglTextureParameterfEXT(texture, GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
qglTextureParameterfEXT(texture, GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); qglTextureParameterfEXT(texture, GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
qglTextureParameterfEXT(texture, GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); qglTextureParameterfEXT(texture, GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
@ -755,7 +767,16 @@ void RE_UploadCinematic (int w, int h, int cols, int rows, const byte *data, int
if (dirty) { if (dirty) {
// otherwise, just subimage upload it so that drivers can tell we are going to be changing // 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 // it and don't try and do a texture compression
qglTextureSubImage2DEXT(texture, GL_TEXTURE_2D, 0, 0, 0, cols, rows, GL_RGBA, GL_UNSIGNED_BYTE, data); if ( qglesMajorVersion >= 1 ) {
buffer = ri.Hunk_AllocateTempMemory( 3 * cols * rows );
R_ConvertTextureFormat( data, cols, rows, GL_RGB, GL_UNSIGNED_BYTE, buffer );
qglTextureSubImage2DEXT(texture, GL_TEXTURE_2D, 0, 0, 0, cols, rows, GL_RGB, GL_UNSIGNED_BYTE, buffer);
ri.Hunk_FreeTempMemory( buffer );
} else {
qglTextureSubImage2DEXT(texture, GL_TEXTURE_2D, 0, 0, 0, cols, rows, GL_RGBA, GL_UNSIGNED_BYTE, data);
}
} }
} }
} }
@ -793,7 +814,7 @@ const void *RB_StretchPic ( const void *data ) {
cmd = (const stretchPicCommand_t *)data; cmd = (const stretchPicCommand_t *)data;
if (glRefConfig.framebufferObject) if (glRefConfig.framebufferObject)
FBO_Bind(r_postProcess->integer ? NULL : tr.renderFbo); FBO_Bind(tr.renderFbo);
RB_SetGL2D(); RB_SetGL2D();
@ -1140,14 +1161,14 @@ const void *RB_DrawSurfs( const void *data ) {
if (glRefConfig.occlusionQuery) if (glRefConfig.occlusionQuery)
{ {
tr.sunFlareQueryActive[tr.sunFlareQueryIndex] = qtrue; tr.sunFlareQueryActive[tr.sunFlareQueryIndex] = qtrue;
qglBeginQuery(GL_SAMPLES_PASSED, tr.sunFlareQuery[tr.sunFlareQueryIndex]); qglBeginQuery(glRefConfig.occlusionQueryTarget, tr.sunFlareQuery[tr.sunFlareQueryIndex]);
} }
RB_DrawSun(0.3, tr.sunFlareShader); RB_DrawSun(0.3, tr.sunFlareShader);
if (glRefConfig.occlusionQuery) if (glRefConfig.occlusionQuery)
{ {
qglEndQuery(GL_SAMPLES_PASSED); qglEndQuery(glRefConfig.occlusionQueryTarget);
} }
FBO_Bind(oldFbo); FBO_Bind(oldFbo);
@ -1202,15 +1223,12 @@ const void *RB_DrawBuffer( const void *data ) {
// clear screen for debugging // clear screen for debugging
if ( r_clear->integer ) { if ( r_clear->integer ) {
qglClearColor( 1, 0, 0.5, 1 );
qglClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
if (glRefConfig.framebufferObject && tr.renderFbo) { if (glRefConfig.framebufferObject && tr.renderFbo) {
FBO_Bind(tr.renderFbo); FBO_Bind(tr.renderFbo);
qglClearColor( 1, 0, 0.5, 1 );
qglClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
} }
qglClearColor( 1, 0, 0.5, 1 );
qglClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
} }
return (const void *)(cmd + 1); return (const void *)(cmd + 1);
@ -1381,18 +1399,15 @@ const void *RB_SwapBuffers( const void *data ) {
if (glRefConfig.framebufferObject) if (glRefConfig.framebufferObject)
{ {
if (!r_postProcess->integer) if (tr.msaaResolveFbo && r_hdr->integer)
{ {
if (tr.msaaResolveFbo && r_hdr->integer) // Resolving an RGB16F MSAA FBO to the screen messes with the brightness, so resolve to an RGB16F FBO first
{ FBO_FastBlit(tr.renderFbo, NULL, tr.msaaResolveFbo, NULL, GL_COLOR_BUFFER_BIT, GL_NEAREST);
// Resolving an RGB16F MSAA FBO to the screen messes with the brightness, so resolve to an RGB16F FBO first FBO_FastBlit(tr.msaaResolveFbo, NULL, NULL, NULL, GL_COLOR_BUFFER_BIT, GL_NEAREST);
FBO_FastBlit(tr.renderFbo, NULL, tr.msaaResolveFbo, NULL, GL_COLOR_BUFFER_BIT, GL_NEAREST); }
FBO_FastBlit(tr.msaaResolveFbo, NULL, NULL, NULL, GL_COLOR_BUFFER_BIT, GL_NEAREST); else if (tr.renderFbo)
} {
else if (tr.renderFbo) FBO_FastBlit(tr.renderFbo, NULL, NULL, NULL, GL_COLOR_BUFFER_BIT, GL_NEAREST);
{
FBO_FastBlit(tr.renderFbo, NULL, NULL, NULL, GL_COLOR_BUFFER_BIT, GL_NEAREST);
}
} }
} }
@ -1454,7 +1469,7 @@ RB_PostProcess
const void *RB_PostProcess(const void *data) const void *RB_PostProcess(const void *data)
{ {
const postProcessCommand_t *cmd = data; const postProcessCommand_t *cmd = data;
FBO_t *srcFbo; FBO_t *srcFbo, *dstFbo;
ivec4_t srcBox, dstBox; ivec4_t srcBox, dstBox;
qboolean autoExposure; qboolean autoExposure;
@ -1475,6 +1490,8 @@ const void *RB_PostProcess(const void *data)
} }
srcFbo = tr.renderFbo; srcFbo = tr.renderFbo;
dstFbo = tr.renderFbo;
if (tr.msaaResolveFbo) if (tr.msaaResolveFbo)
{ {
// Resolve the MSAA before anything else // Resolve the MSAA before anything else
@ -1508,13 +1525,13 @@ const void *RB_PostProcess(const void *data)
if (r_hdr->integer && (r_toneMap->integer || r_forceToneMap->integer)) if (r_hdr->integer && (r_toneMap->integer || r_forceToneMap->integer))
{ {
autoExposure = r_autoExposure->integer || r_forceAutoExposure->integer; autoExposure = r_autoExposure->integer || r_forceAutoExposure->integer;
RB_ToneMap(srcFbo, srcBox, NULL, dstBox, autoExposure);
// Use an intermediate FBO because it can't blit to the same FBO directly
// and can't read from an MSAA dstFbo later.
RB_ToneMap(srcFbo, srcBox, tr.screenScratchFbo, srcBox, autoExposure);
FBO_FastBlit(tr.screenScratchFbo, srcBox, srcFbo, srcBox, GL_COLOR_BUFFER_BIT, GL_NEAREST);
} }
else if (r_cameraExposure->value == 0.0f) else if (r_cameraExposure->value != 0.0f)
{
FBO_FastBlit(srcFbo, srcBox, NULL, dstBox, GL_COLOR_BUFFER_BIT, GL_NEAREST);
}
else
{ {
vec4_t color; vec4_t color;
@ -1523,17 +1540,20 @@ const void *RB_PostProcess(const void *data)
color[2] = pow(2, r_cameraExposure->value); //exp2(r_cameraExposure->value); color[2] = pow(2, r_cameraExposure->value); //exp2(r_cameraExposure->value);
color[3] = 1.0f; color[3] = 1.0f;
FBO_Blit(srcFbo, srcBox, NULL, NULL, dstBox, NULL, color, 0); FBO_BlitFromTexture(tr.whiteImage, NULL, NULL, srcFbo, srcBox, NULL, color, GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ZERO);
} }
} }
if (r_drawSunRays->integer) if (r_drawSunRays->integer)
RB_SunRays(NULL, srcBox, NULL, dstBox); RB_SunRays(srcFbo, srcBox, srcFbo, srcBox);
if (1) if (1)
RB_BokehBlur(NULL, srcBox, NULL, dstBox, backEnd.refdef.blurFactor); RB_BokehBlur(srcFbo, srcBox, srcFbo, srcBox, backEnd.refdef.blurFactor);
else else
RB_GaussianBlur(backEnd.refdef.blurFactor); RB_GaussianBlur(srcFbo, srcFbo, backEnd.refdef.blurFactor);
if (srcFbo != dstFbo)
FBO_FastBlit(srcFbo, srcBox, dstFbo, dstBox, GL_COLOR_BUFFER_BIT, GL_NEAREST);
#if 0 #if 0
if (0) if (0)
@ -1549,7 +1569,7 @@ const void *RB_PostProcess(const void *data)
if (scale < 0.01f) if (scale < 0.01f)
scale = 5.0f; scale = 5.0f;
FBO_FastBlit(NULL, NULL, tr.quarterFbo[0], NULL, GL_COLOR_BUFFER_BIT, GL_LINEAR); FBO_FastBlit(dstFbo, NULL, tr.quarterFbo[0], NULL, GL_COLOR_BUFFER_BIT, GL_LINEAR);
iQtrBox[0] = backEnd.viewParms.viewportX * tr.quarterImage[0]->width / (float)glConfig.vidWidth; iQtrBox[0] = backEnd.viewParms.viewportX * tr.quarterImage[0]->width / (float)glConfig.vidWidth;
iQtrBox[1] = backEnd.viewParms.viewportY * tr.quarterImage[0]->height / (float)glConfig.vidHeight; iQtrBox[1] = backEnd.viewParms.viewportY * tr.quarterImage[0]->height / (float)glConfig.vidHeight;
@ -1595,7 +1615,7 @@ const void *RB_PostProcess(const void *data)
SetViewportAndScissor(); SetViewportAndScissor();
FBO_FastBlit(tr.quarterFbo[1], NULL, NULL, NULL, GL_COLOR_BUFFER_BIT, GL_LINEAR); FBO_FastBlit(tr.quarterFbo[1], NULL, dstFbo, NULL, GL_COLOR_BUFFER_BIT, GL_LINEAR);
FBO_Bind(NULL); FBO_Bind(NULL);
} }
#endif #endif
@ -1604,42 +1624,42 @@ const void *RB_PostProcess(const void *data)
{ {
ivec4_t dstBox; ivec4_t dstBox;
VectorSet4(dstBox, 0, glConfig.vidHeight - 128, 128, 128); VectorSet4(dstBox, 0, glConfig.vidHeight - 128, 128, 128);
FBO_BlitFromTexture(tr.sunShadowDepthImage[0], NULL, NULL, NULL, dstBox, NULL, NULL, 0); FBO_BlitFromTexture(tr.sunShadowDepthImage[0], NULL, NULL, dstFbo, dstBox, NULL, NULL, 0);
VectorSet4(dstBox, 128, glConfig.vidHeight - 128, 128, 128); VectorSet4(dstBox, 128, glConfig.vidHeight - 128, 128, 128);
FBO_BlitFromTexture(tr.sunShadowDepthImage[1], NULL, NULL, NULL, dstBox, NULL, NULL, 0); FBO_BlitFromTexture(tr.sunShadowDepthImage[1], NULL, NULL, dstFbo, dstBox, NULL, NULL, 0);
VectorSet4(dstBox, 256, glConfig.vidHeight - 128, 128, 128); VectorSet4(dstBox, 256, glConfig.vidHeight - 128, 128, 128);
FBO_BlitFromTexture(tr.sunShadowDepthImage[2], NULL, NULL, NULL, dstBox, NULL, NULL, 0); FBO_BlitFromTexture(tr.sunShadowDepthImage[2], NULL, NULL, dstFbo, dstBox, NULL, NULL, 0);
VectorSet4(dstBox, 384, glConfig.vidHeight - 128, 128, 128); VectorSet4(dstBox, 384, glConfig.vidHeight - 128, 128, 128);
FBO_BlitFromTexture(tr.sunShadowDepthImage[3], NULL, NULL, NULL, dstBox, NULL, NULL, 0); FBO_BlitFromTexture(tr.sunShadowDepthImage[3], NULL, NULL, dstFbo, dstBox, NULL, NULL, 0);
} }
if (0 && r_shadows->integer == 4) if (0 && r_shadows->integer == 4)
{ {
ivec4_t dstBox; ivec4_t dstBox;
VectorSet4(dstBox, 512 + 0, glConfig.vidHeight - 128, 128, 128); VectorSet4(dstBox, 512 + 0, glConfig.vidHeight - 128, 128, 128);
FBO_BlitFromTexture(tr.pshadowMaps[0], NULL, NULL, NULL, dstBox, NULL, NULL, 0); FBO_BlitFromTexture(tr.pshadowMaps[0], NULL, NULL, dstFbo, dstBox, NULL, NULL, 0);
VectorSet4(dstBox, 512 + 128, glConfig.vidHeight - 128, 128, 128); VectorSet4(dstBox, 512 + 128, glConfig.vidHeight - 128, 128, 128);
FBO_BlitFromTexture(tr.pshadowMaps[1], NULL, NULL, NULL, dstBox, NULL, NULL, 0); FBO_BlitFromTexture(tr.pshadowMaps[1], NULL, NULL, dstFbo, dstBox, NULL, NULL, 0);
VectorSet4(dstBox, 512 + 256, glConfig.vidHeight - 128, 128, 128); VectorSet4(dstBox, 512 + 256, glConfig.vidHeight - 128, 128, 128);
FBO_BlitFromTexture(tr.pshadowMaps[2], NULL, NULL, NULL, dstBox, NULL, NULL, 0); FBO_BlitFromTexture(tr.pshadowMaps[2], NULL, NULL, dstFbo, dstBox, NULL, NULL, 0);
VectorSet4(dstBox, 512 + 384, glConfig.vidHeight - 128, 128, 128); VectorSet4(dstBox, 512 + 384, glConfig.vidHeight - 128, 128, 128);
FBO_BlitFromTexture(tr.pshadowMaps[3], NULL, NULL, NULL, dstBox, NULL, NULL, 0); FBO_BlitFromTexture(tr.pshadowMaps[3], NULL, NULL, dstFbo, dstBox, NULL, NULL, 0);
} }
if (0) if (0)
{ {
ivec4_t dstBox; ivec4_t dstBox;
VectorSet4(dstBox, 256, glConfig.vidHeight - 256, 256, 256); VectorSet4(dstBox, 256, glConfig.vidHeight - 256, 256, 256);
FBO_BlitFromTexture(tr.renderDepthImage, NULL, NULL, NULL, dstBox, NULL, NULL, 0); FBO_BlitFromTexture(tr.renderDepthImage, NULL, NULL, dstFbo, dstBox, NULL, NULL, 0);
VectorSet4(dstBox, 512, glConfig.vidHeight - 256, 256, 256); VectorSet4(dstBox, 512, glConfig.vidHeight - 256, 256, 256);
FBO_BlitFromTexture(tr.screenShadowImage, NULL, NULL, NULL, dstBox, NULL, NULL, 0); FBO_BlitFromTexture(tr.screenShadowImage, NULL, NULL, dstFbo, dstBox, NULL, NULL, 0);
} }
if (0) if (0)
{ {
ivec4_t dstBox; ivec4_t dstBox;
VectorSet4(dstBox, 256, glConfig.vidHeight - 256, 256, 256); VectorSet4(dstBox, 256, glConfig.vidHeight - 256, 256, 256);
FBO_BlitFromTexture(tr.sunRaysImage, NULL, NULL, NULL, dstBox, NULL, NULL, 0); FBO_BlitFromTexture(tr.sunRaysImage, NULL, NULL, dstFbo, dstBox, NULL, NULL, 0);
} }
#if 0 #if 0
@ -1651,8 +1671,8 @@ const void *RB_PostProcess(const void *data)
if (cubemapIndex) if (cubemapIndex)
{ {
VectorSet4(dstBox, 0, glConfig.vidHeight - 256, 256, 256); VectorSet4(dstBox, 0, glConfig.vidHeight - 256, 256, 256);
//FBO_BlitFromTexture(tr.renderCubeImage, NULL, NULL, NULL, dstBox, &tr.testcubeShader, NULL, 0); //FBO_BlitFromTexture(tr.renderCubeImage, NULL, NULL, dstFbo, dstBox, &tr.testcubeShader, NULL, 0);
FBO_BlitFromTexture(tr.cubemaps[cubemapIndex - 1].image, NULL, NULL, NULL, dstBox, &tr.testcubeShader, NULL, 0); FBO_BlitFromTexture(tr.cubemaps[cubemapIndex - 1].image, NULL, NULL, dstFbo, dstBox, &tr.testcubeShader, NULL, 0);
} }
} }
#endif #endif

View file

@ -276,7 +276,7 @@ static void R_LoadLightmaps( lump_t *l, lump_t *surfs ) {
tr.deluxemaps = ri.Hunk_Alloc( tr.numLightmaps * sizeof(image_t *), h_low ); tr.deluxemaps = ri.Hunk_Alloc( tr.numLightmaps * sizeof(image_t *), h_low );
textureInternalFormat = GL_RGBA8; textureInternalFormat = GL_RGBA8;
if (r_hdr->integer) if (r_hdr->integer && !qglesMajorVersion)
{ {
// Check for the first hdr lightmap, if it exists, use GL_RGBA16 for textures. // Check for the first hdr lightmap, if it exists, use GL_RGBA16 for textures.
char filename[MAX_QPATH]; char filename[MAX_QPATH];
@ -617,7 +617,7 @@ static shader_t *ShaderForShaderNum( int shaderNum, int lightmapNum ) {
lightmapNum = LIGHTMAP_WHITEIMAGE; lightmapNum = LIGHTMAP_WHITEIMAGE;
} }
shader = R_FindShader( dsh->shader, lightmapNum, qtrue ); shader = R_FindShaderEx( dsh->shader, FatLightmap( lightmapNum ), qtrue, lightmapNum );
// if the shader had errors, just use default shader // if the shader had errors, just use default shader
if ( shader->defaultShader ) { if ( shader->defaultShader ) {
@ -706,7 +706,7 @@ static void ParseFace( dsurface_t *ds, drawVert_t *verts, float *hdrVertColors,
surf->fogIndex = LittleLong( ds->fogNum ) + 1; surf->fogIndex = LittleLong( ds->fogNum ) + 1;
// get shader value // get shader value
surf->shader = ShaderForShaderNum( ds->shaderNum, FatLightmap(realLightmapNum) ); surf->shader = ShaderForShaderNum( ds->shaderNum, realLightmapNum );
if ( r_singleShader->integer && !surf->shader->isSky ) { if ( r_singleShader->integer && !surf->shader->isSky ) {
surf->shader = tr.defaultShader; surf->shader = tr.defaultShader;
} }
@ -813,7 +813,7 @@ static void ParseMesh ( dsurface_t *ds, drawVert_t *verts, float *hdrVertColors,
surf->fogIndex = LittleLong( ds->fogNum ) + 1; surf->fogIndex = LittleLong( ds->fogNum ) + 1;
// get shader value // get shader value
surf->shader = ShaderForShaderNum( ds->shaderNum, FatLightmap(realLightmapNum) ); surf->shader = ShaderForShaderNum( ds->shaderNum, realLightmapNum );
if ( r_singleShader->integer && !surf->shader->isSky ) { if ( r_singleShader->integer && !surf->shader->isSky ) {
surf->shader = tr.defaultShader; surf->shader = tr.defaultShader;
} }

View file

@ -348,7 +348,13 @@ void RE_BeginFrame( stereoFrame_t stereoFrame ) {
// //
if ( r_measureOverdraw->integer ) if ( r_measureOverdraw->integer )
{ {
if ( glConfig.stencilBits < 4 ) if ( qglesMajorVersion >= 1 && !glRefConfig.readStencil )
{
ri.Printf( PRINT_WARNING, "OpenGL ES needs GL_NV_read_stencil to read stencil bits to measure overdraw\n" );
ri.Cvar_Set( "r_measureOverdraw", "0" );
r_measureOverdraw->modified = qfalse;
}
else if ( glConfig.stencilBits < 4 )
{ {
ri.Printf( PRINT_ALL, "Warning: not enough stencil bits to measure overdraw: %d\n", glConfig.stencilBits ); ri.Printf( PRINT_ALL, "Warning: not enough stencil bits to measure overdraw: %d\n", glConfig.stencilBits );
ri.Cvar_Set( "r_measureOverdraw", "0" ); ri.Cvar_Set( "r_measureOverdraw", "0" );
@ -426,6 +432,13 @@ void RE_BeginFrame( stereoFrame_t stereoFrame ) {
} }
else else
{ {
if (qglesMajorVersion >= 1 && r_anaglyphMode->integer)
{
ri.Printf( PRINT_WARNING, "OpenGL ES does not support drawing to separate buffer for anaglyph mode\n" );
ri.Cvar_Set( "r_anaglyphMode", "0" );
r_anaglyphMode->modified = qfalse;
}
if(r_anaglyphMode->integer) if(r_anaglyphMode->integer)
{ {
if(r_anaglyphMode->modified) if(r_anaglyphMode->modified)

View file

@ -45,6 +45,17 @@ void GLimp_InitExtraExtensions(void)
if (strstr((char *)qglGetString(GL_RENDERER), "Intel")) if (strstr((char *)qglGetString(GL_RENDERER), "Intel"))
glRefConfig.intelGraphics = qtrue; glRefConfig.intelGraphics = qtrue;
if (qglesMajorVersion)
{
glRefConfig.vaoCacheGlIndexType = GL_UNSIGNED_SHORT;
glRefConfig.vaoCacheGlIndexSize = sizeof(unsigned short);
}
else
{
glRefConfig.vaoCacheGlIndexType = GL_UNSIGNED_INT;
glRefConfig.vaoCacheGlIndexSize = sizeof(unsigned int);
}
// set DSA fallbacks // set DSA fallbacks
#define GLE(ret, name, ...) qgl##name = GLDSA_##name; #define GLE(ret, name, ...) qgl##name = GLDSA_##name;
QGL_EXT_direct_state_access_PROCS; QGL_EXT_direct_state_access_PROCS;
@ -53,8 +64,107 @@ void GLimp_InitExtraExtensions(void)
// GL function loader, based on https://gist.github.com/rygorous/16796a0c876cf8a5f542caddb55bce8a // GL function loader, based on https://gist.github.com/rygorous/16796a0c876cf8a5f542caddb55bce8a
#define GLE(ret, name, ...) qgl##name = (name##proc *) SDL_GL_GetProcAddress("gl" #name); #define GLE(ret, name, ...) qgl##name = (name##proc *) SDL_GL_GetProcAddress("gl" #name);
//
// OpenGL ES extensions
//
if (qglesMajorVersion)
{
if (!r_allowExtensions->integer)
goto done;
extension = "GL_EXT_occlusion_query_boolean";
if (qglesMajorVersion >= 3 || SDL_GL_ExtensionSupported(extension))
{
glRefConfig.occlusionQuery = qtrue;
glRefConfig.occlusionQueryTarget = GL_ANY_SAMPLES_PASSED;
if (qglesMajorVersion >= 3) {
QGL_ARB_occlusion_query_PROCS;
} else {
// GL_EXT_occlusion_query_boolean uses EXT suffix
#undef GLE
#define GLE(ret, name, ...) qgl##name = (name##proc *) SDL_GL_GetProcAddress("gl" #name "EXT");
QGL_ARB_occlusion_query_PROCS;
#undef GLE
#define GLE(ret, name, ...) qgl##name = (name##proc *) SDL_GL_GetProcAddress("gl" #name);
}
ri.Printf(PRINT_ALL, result[glRefConfig.occlusionQuery], extension);
}
else
{
ri.Printf(PRINT_ALL, result[2], extension);
}
// GL_NV_read_depth
extension = "GL_NV_read_depth";
if (SDL_GL_ExtensionSupported(extension))
{
glRefConfig.readDepth = qtrue;
ri.Printf(PRINT_ALL, result[glRefConfig.readDepth], extension);
}
else
{
ri.Printf(PRINT_ALL, result[2], extension);
}
// GL_NV_read_stencil
extension = "GL_NV_read_stencil";
if (SDL_GL_ExtensionSupported(extension))
{
glRefConfig.readStencil = qtrue;
ri.Printf(PRINT_ALL, result[glRefConfig.readStencil], extension);
}
else
{
ri.Printf(PRINT_ALL, result[2], extension);
}
// GL_EXT_shadow_samplers
extension = "GL_EXT_shadow_samplers";
if (qglesMajorVersion >= 3 || SDL_GL_ExtensionSupported(extension))
{
glRefConfig.shadowSamplers = qtrue;
ri.Printf(PRINT_ALL, result[glRefConfig.shadowSamplers], extension);
}
else
{
ri.Printf(PRINT_ALL, result[2], extension);
}
// GL_OES_standard_derivatives
extension = "GL_OES_standard_derivatives";
if (qglesMajorVersion >= 3 || SDL_GL_ExtensionSupported(extension))
{
glRefConfig.standardDerivatives = qtrue;
ri.Printf(PRINT_ALL, result[glRefConfig.standardDerivatives], extension);
}
else
{
ri.Printf(PRINT_ALL, result[2], extension);
}
// GL_OES_element_index_uint
extension = "GL_OES_element_index_uint";
if (qglesMajorVersion >= 3 || SDL_GL_ExtensionSupported(extension))
{
glRefConfig.vaoCacheGlIndexType = GL_UNSIGNED_INT;
glRefConfig.vaoCacheGlIndexSize = sizeof(unsigned int);
ri.Printf(PRINT_ALL, result[1], extension);
}
else
{
ri.Printf(PRINT_ALL, result[2], extension);
}
goto done;
}
// OpenGL 1.5 - GL_ARB_occlusion_query // OpenGL 1.5 - GL_ARB_occlusion_query
glRefConfig.occlusionQuery = qtrue; glRefConfig.occlusionQuery = qtrue;
glRefConfig.occlusionQueryTarget = GL_SAMPLES_PASSED;
QGL_ARB_occlusion_query_PROCS; QGL_ARB_occlusion_query_PROCS;
// OpenGL 3.0 - GL_ARB_framebuffer_object // OpenGL 3.0 - GL_ARB_framebuffer_object
@ -146,18 +256,6 @@ void GLimp_InitExtraExtensions(void)
ri.Printf(PRINT_ALL, result[2], extension); ri.Printf(PRINT_ALL, result[2], extension);
} }
// Determine GLSL version
if (1)
{
char version[256];
Q_strncpyz(version, (char *)qglGetString(GL_SHADING_LANGUAGE_VERSION), sizeof(version));
sscanf(version, "%d.%d", &glRefConfig.glslMajorVersion, &glRefConfig.glslMinorVersion);
ri.Printf(PRINT_ALL, "...using GLSL version %s\n", version);
}
glRefConfig.memInfo = MI_NONE; glRefConfig.memInfo = MI_NONE;
// GL_NVX_gpu_memory_info // GL_NVX_gpu_memory_info
@ -249,5 +347,26 @@ void GLimp_InitExtraExtensions(void)
ri.Printf(PRINT_ALL, result[2], extension); ri.Printf(PRINT_ALL, result[2], extension);
} }
done:
// Determine GLSL version
if (1)
{
char version[256], *version_p;
Q_strncpyz(version, (char *)qglGetString(GL_SHADING_LANGUAGE_VERSION), sizeof(version));
// Skip leading text such as "OpenGL ES GLSL ES "
version_p = version;
while ( *version_p && !isdigit( *version_p ) )
{
version_p++;
}
sscanf(version_p, "%d.%d", &glRefConfig.glslMajorVersion, &glRefConfig.glslMinorVersion);
ri.Printf(PRINT_ALL, "...using GLSL version %s\n", version);
}
#undef GLE #undef GLE
} }

View file

@ -648,10 +648,30 @@ void FBO_FastBlit(FBO_t *src, ivec4_t srcBox, FBO_t *dst, ivec4_t dstBox, int bu
int width = dst ? dst->width : glConfig.vidWidth; int width = dst ? dst->width : glConfig.vidWidth;
int height = dst ? dst->height : glConfig.vidHeight; int height = dst ? dst->height : glConfig.vidHeight;
qglScissor(0, 0, width, height);
VectorSet4(dstBoxFinal, 0, 0, width, height); VectorSet4(dstBoxFinal, 0, 0, width, height);
} }
else else
{ {
ivec4_t scissorBox;
Vector4Copy(dstBox, scissorBox);
if (scissorBox[2] < 0)
{
scissorBox[0] += scissorBox[2];
scissorBox[2] = fabsf(scissorBox[2]);
}
if (scissorBox[3] < 0)
{
scissorBox[1] += scissorBox[3];
scissorBox[3] = fabsf(scissorBox[3]);
}
qglScissor(scissorBox[0], scissorBox[1], scissorBox[2], scissorBox[3]);
VectorSet4(dstBoxFinal, dstBox[0], dstBox[1], dstBox[0] + dstBox[2], dstBox[1] + dstBox[3]); VectorSet4(dstBoxFinal, dstBox[0], dstBox[1], dstBox[0] + dstBox[2], dstBox[1] + dstBox[3]);
} }

View file

@ -478,6 +478,14 @@ void RB_RenderFlares (void) {
return; return;
} }
if ( r_flares->modified ) {
if ( qglesMajorVersion >= 1 && !glRefConfig.readDepth ) {
ri.Printf( PRINT_WARNING, "OpenGL ES needs GL_NV_read_depth to read depth to determine if flares are visible\n" );
ri.Cvar_Set( "r_flares", "0" );
}
r_flares->modified = qfalse;
}
if(r_flareCoeff->modified) if(r_flareCoeff->modified)
{ {
R_SetFlareCoeff(); R_SetFlareCoeff();

View file

@ -88,8 +88,14 @@ static uniformInfo_t uniformsInfo[] =
{ "u_EnableTextures", GLSL_VEC4 }, { "u_EnableTextures", GLSL_VEC4 },
{ "u_DiffuseTexMatrix", GLSL_VEC4 }, { "u_DiffuseTexMatrix0", GLSL_VEC4 },
{ "u_DiffuseTexOffTurb", GLSL_VEC4 }, { "u_DiffuseTexMatrix1", GLSL_VEC4 },
{ "u_DiffuseTexMatrix2", GLSL_VEC4 },
{ "u_DiffuseTexMatrix3", GLSL_VEC4 },
{ "u_DiffuseTexMatrix4", GLSL_VEC4 },
{ "u_DiffuseTexMatrix5", GLSL_VEC4 },
{ "u_DiffuseTexMatrix6", GLSL_VEC4 },
{ "u_DiffuseTexMatrix7", GLSL_VEC4 },
{ "u_TCGen0", GLSL_INT }, { "u_TCGen0", GLSL_INT },
{ "u_TCGen0Vector0", GLSL_VEC3 }, { "u_TCGen0Vector0", GLSL_VEC3 },
@ -243,11 +249,25 @@ static void GLSL_GetShaderHeader( GLenum shaderType, const GLchar *extra, char *
// HACK: abuse the GLSL preprocessor to turn GLSL 1.20 shaders into 1.30 ones // HACK: abuse the GLSL preprocessor to turn GLSL 1.20 shaders into 1.30 ones
if(glRefConfig.glslMajorVersion > 1 || (glRefConfig.glslMajorVersion == 1 && glRefConfig.glslMinorVersion >= 30)) if(glRefConfig.glslMajorVersion > 1 || (glRefConfig.glslMajorVersion == 1 && glRefConfig.glslMinorVersion >= 30))
{ {
if (glRefConfig.glslMajorVersion > 1 || (glRefConfig.glslMajorVersion == 1 && glRefConfig.glslMinorVersion >= 50)) if (qglesMajorVersion >= 3 && glRefConfig.glslMajorVersion >= 3)
Q_strcat(dest, size, "#version 300 es\n");
else if (glRefConfig.glslMajorVersion > 1 || (glRefConfig.glslMajorVersion == 1 && glRefConfig.glslMinorVersion >= 50))
Q_strcat(dest, size, "#version 150\n"); Q_strcat(dest, size, "#version 150\n");
else else
Q_strcat(dest, size, "#version 130\n"); Q_strcat(dest, size, "#version 130\n");
// `extra' may contain #extension which must be directly after #version
if (extra)
{
Q_strcat(dest, size, extra);
}
if (qglesMajorVersion >= 2)
{
Q_strcat(dest, size, "precision mediump float;\n");
Q_strcat(dest, size, "precision mediump sampler2DShadow;\n");
}
if(shaderType == GL_VERTEX_SHADER) if(shaderType == GL_VERTEX_SHADER)
{ {
Q_strcat(dest, size, "#define attribute in\n"); Q_strcat(dest, size, "#define attribute in\n");
@ -266,8 +286,34 @@ static void GLSL_GetShaderHeader( GLenum shaderType, const GLchar *extra, char *
} }
else else
{ {
Q_strcat(dest, size, "#version 120\n"); if (qglesMajorVersion >= 2)
Q_strcat(dest, size, "#define shadow2D(a,b) shadow2D(a,b).r \n"); {
Q_strcat(dest, size, "#version 100\n");
if (extra)
{
Q_strcat(dest, size, extra);
}
Q_strcat(dest, size, "precision mediump float;\n");
if (glRefConfig.shadowSamplers)
{
Q_strcat(dest, size, "precision mediump sampler2DShadow;\n");
Q_strcat(dest, size, "#define shadow2D(a,b) shadow2DEXT(a,b)\n");
}
}
else
{
Q_strcat(dest, size, "#version 120\n");
if (extra)
{
Q_strcat(dest, size, extra);
}
Q_strcat(dest, size, "#define shadow2D(a,b) shadow2D(a,b).r\n");
}
} }
// HACK: add some macros to avoid extra uniforms and save speed and code maintenance // HACK: add some macros to avoid extra uniforms and save speed and code maintenance
@ -355,11 +401,6 @@ static void GLSL_GetShaderHeader( GLenum shaderType, const GLchar *extra, char *
Q_strcat(dest, size, va("#define ROUGHNESS_MIPS float(%d)\n", numRoughnessMips)); Q_strcat(dest, size, va("#define ROUGHNESS_MIPS float(%d)\n", numRoughnessMips));
} }
if (extra)
{
Q_strcat(dest, size, extra);
}
// OK we added a lot of stuff but if we do something bad in the GLSL shaders then we want the proper line // OK we added a lot of stuff but if we do something bad in the GLSL shaders then we want the proper line
// so we have to reset the line counting // so we have to reset the line counting
Q_strcat(dest, size, "#line 0\n"); Q_strcat(dest, size, "#line 0\n");
@ -927,6 +968,15 @@ void GLSL_InitGPUShaders(void)
startTime = ri.Milliseconds(); startTime = ri.Milliseconds();
// OpenGL ES may not have enough attributes to fit ones used for vertex animation
if ( glRefConfig.maxVertexAttribs > ATTR_INDEX_NORMAL2 ) {
ri.Printf(PRINT_ALL, "Using GPU vertex animation\n");
glRefConfig.gpuVertexAnimation = qtrue;
} else {
ri.Printf(PRINT_ALL, "Using CPU vertex animation\n");
glRefConfig.gpuVertexAnimation = qfalse;
}
for (i = 0; i < GENERICDEF_COUNT; i++) for (i = 0; i < GENERICDEF_COUNT; i++)
{ {
if ((i & GENERICDEF_USE_VERTEX_ANIMATION) && (i & GENERICDEF_USE_BONE_ANIMATION)) if ((i & GENERICDEF_USE_VERTEX_ANIMATION) && (i & GENERICDEF_USE_BONE_ANIMATION))
@ -949,6 +999,9 @@ void GLSL_InitGPUShaders(void)
if (i & GENERICDEF_USE_VERTEX_ANIMATION) if (i & GENERICDEF_USE_VERTEX_ANIMATION)
{ {
if (!glRefConfig.gpuVertexAnimation)
continue;
Q_strcat(extradefines, 1024, "#define USE_VERTEX_ANIMATION\n"); Q_strcat(extradefines, 1024, "#define USE_VERTEX_ANIMATION\n");
attribs |= ATTR_POSITION2 | ATTR_NORMAL2; attribs |= ATTR_POSITION2 | ATTR_NORMAL2;
} }
@ -1000,6 +1053,9 @@ void GLSL_InitGPUShaders(void)
if ((i & FOGDEF_USE_VERTEX_ANIMATION) && (i & FOGDEF_USE_BONE_ANIMATION)) if ((i & FOGDEF_USE_VERTEX_ANIMATION) && (i & FOGDEF_USE_BONE_ANIMATION))
continue; continue;
if ((i & FOGDEF_USE_VERTEX_ANIMATION) && !glRefConfig.gpuVertexAnimation)
continue;
if ((i & FOGDEF_USE_BONE_ANIMATION) && !glRefConfig.glslMaxAnimatedBones) if ((i & FOGDEF_USE_BONE_ANIMATION) && !glRefConfig.glslMaxAnimatedBones)
continue; continue;
@ -1180,12 +1236,17 @@ void GLSL_InitGPUShaders(void)
if (i & LIGHTDEF_ENTITY_VERTEX_ANIMATION) if (i & LIGHTDEF_ENTITY_VERTEX_ANIMATION)
{ {
Q_strcat(extradefines, 1024, "#define USE_VERTEX_ANIMATION\n#define USE_MODELMATRIX\n"); Q_strcat(extradefines, 1024, "#define USE_MODELMATRIX\n");
attribs |= ATTR_POSITION2 | ATTR_NORMAL2;
if (r_normalMapping->integer) if (glRefConfig.gpuVertexAnimation)
{ {
attribs |= ATTR_TANGENT2; Q_strcat(extradefines, 1024, "#define USE_VERTEX_ANIMATION\n");
attribs |= ATTR_POSITION2 | ATTR_NORMAL2;
if (r_normalMapping->integer)
{
attribs |= ATTR_TANGENT2;
}
} }
} }
else if (i & LIGHTDEF_ENTITY_BONE_ANIMATION) else if (i & LIGHTDEF_ENTITY_BONE_ANIMATION)
@ -1220,6 +1281,9 @@ void GLSL_InitGPUShaders(void)
if ((i & SHADOWMAPDEF_USE_VERTEX_ANIMATION) && (i & SHADOWMAPDEF_USE_BONE_ANIMATION)) if ((i & SHADOWMAPDEF_USE_VERTEX_ANIMATION) && (i & SHADOWMAPDEF_USE_BONE_ANIMATION))
continue; continue;
if ((i & SHADOWMAPDEF_USE_VERTEX_ANIMATION) && !glRefConfig.gpuVertexAnimation)
continue;
if ((i & SHADOWMAPDEF_USE_BONE_ANIMATION) && !glRefConfig.glslMaxAnimatedBones) if ((i & SHADOWMAPDEF_USE_BONE_ANIMATION) && !glRefConfig.glslMaxAnimatedBones)
continue; continue;
@ -1344,86 +1408,110 @@ void GLSL_InitGPUShaders(void)
} }
attribs = ATTR_POSITION | ATTR_TEXCOORD; // GLSL 1.10+ or GL_EXT_shadow_samplers extension are required for sampler2DShadow type
extradefines[0] = '\0'; if (glRefConfig.glslMajorVersion > 1 || (glRefConfig.glslMajorVersion == 1 && glRefConfig.glslMinorVersion >= 10)
|| glRefConfig.shadowSamplers)
if (r_shadowFilter->integer >= 1)
Q_strcat(extradefines, 1024, "#define USE_SHADOW_FILTER\n");
if (r_shadowFilter->integer >= 2)
Q_strcat(extradefines, 1024, "#define USE_SHADOW_FILTER2\n");
if (r_shadowCascadeZFar->integer != 0)
Q_strcat(extradefines, 1024, "#define USE_SHADOW_CASCADE\n");
Q_strcat(extradefines, 1024, va("#define r_shadowMapSize %f\n", r_shadowMapSize->value));
Q_strcat(extradefines, 1024, va("#define r_shadowCascadeZFar %f\n", r_shadowCascadeZFar->value));
if (!GLSL_InitGPUShader(&tr.shadowmaskShader, "shadowmask", attribs, qtrue, extradefines, qtrue, fallbackShader_shadowmask_vp, fallbackShader_shadowmask_fp))
{
ri.Error(ERR_FATAL, "Could not load shadowmask shader!");
}
GLSL_InitUniforms(&tr.shadowmaskShader);
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);
GLSL_FinishGPUShader(&tr.shadowmaskShader);
numEtcShaders++;
attribs = ATTR_POSITION | ATTR_TEXCOORD;
extradefines[0] = '\0';
if (!GLSL_InitGPUShader(&tr.ssaoShader, "ssao", attribs, qtrue, extradefines, qtrue, fallbackShader_ssao_vp, fallbackShader_ssao_fp))
{
ri.Error(ERR_FATAL, "Could not load ssao shader!");
}
GLSL_InitUniforms(&tr.ssaoShader);
GLSL_SetUniformInt(&tr.ssaoShader, UNIFORM_SCREENDEPTHMAP, TB_COLORMAP);
GLSL_FinishGPUShader(&tr.ssaoShader);
numEtcShaders++;
for (i = 0; i < 4; i++)
{ {
attribs = ATTR_POSITION | ATTR_TEXCOORD; attribs = ATTR_POSITION | ATTR_TEXCOORD;
extradefines[0] = '\0'; extradefines[0] = '\0';
if (i & 1) if (qglesMajorVersion < 3 && glRefConfig.shadowSamplers)
Q_strcat(extradefines, 1024, "#define USE_VERTICAL_BLUR\n");
else
Q_strcat(extradefines, 1024, "#define USE_HORIZONTAL_BLUR\n");
if (!(i & 2))
Q_strcat(extradefines, 1024, "#define USE_DEPTH\n");
if (!GLSL_InitGPUShader(&tr.depthBlurShader[i], "depthBlur", attribs, qtrue, extradefines, qtrue, fallbackShader_depthblur_vp, fallbackShader_depthblur_fp))
{ {
ri.Error(ERR_FATAL, "Could not load depthBlur shader!"); Q_strcat(extradefines, 1024, "#extension GL_EXT_shadow_samplers : enable\n");
} }
GLSL_InitUniforms(&tr.depthBlurShader[i]);
GLSL_SetUniformInt(&tr.depthBlurShader[i], UNIFORM_SCREENIMAGEMAP, TB_COLORMAP); if (r_shadowFilter->integer >= 1)
GLSL_SetUniformInt(&tr.depthBlurShader[i], UNIFORM_SCREENDEPTHMAP, TB_LIGHTMAP); Q_strcat(extradefines, 1024, "#define USE_SHADOW_FILTER\n");
GLSL_FinishGPUShader(&tr.depthBlurShader[i]); if (r_shadowFilter->integer >= 2)
Q_strcat(extradefines, 1024, "#define USE_SHADOW_FILTER2\n");
if (r_shadowCascadeZFar->integer != 0)
Q_strcat(extradefines, 1024, "#define USE_SHADOW_CASCADE\n");
Q_strcat(extradefines, 1024, va("#define r_shadowMapSize %f\n", r_shadowMapSize->value));
Q_strcat(extradefines, 1024, va("#define r_shadowCascadeZFar %f\n", r_shadowCascadeZFar->value));
if (!GLSL_InitGPUShader(&tr.shadowmaskShader, "shadowmask", attribs, qtrue, extradefines, qtrue, fallbackShader_shadowmask_vp, fallbackShader_shadowmask_fp))
{
ri.Error(ERR_FATAL, "Could not load shadowmask shader!");
}
GLSL_InitUniforms(&tr.shadowmaskShader);
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);
GLSL_FinishGPUShader(&tr.shadowmaskShader);
numEtcShaders++; numEtcShaders++;
} }
// GLSL 1.10+ or GL_OES_standard_derivatives extension are required for dFdx() and dFdy() GLSL functions
if (glRefConfig.glslMajorVersion > 1 || (glRefConfig.glslMajorVersion == 1 && glRefConfig.glslMinorVersion >= 10)
|| glRefConfig.standardDerivatives)
{
attribs = ATTR_POSITION | ATTR_TEXCOORD;
extradefines[0] = '\0';
if (qglesMajorVersion < 3 && glRefConfig.standardDerivatives)
{
Q_strcat(extradefines, 1024, "#extension GL_OES_standard_derivatives : enable\n");
}
if (!GLSL_InitGPUShader(&tr.ssaoShader, "ssao", attribs, qtrue, extradefines, qtrue, fallbackShader_ssao_vp, fallbackShader_ssao_fp))
{
ri.Error(ERR_FATAL, "Could not load ssao shader!");
}
GLSL_InitUniforms(&tr.ssaoShader);
GLSL_SetUniformInt(&tr.ssaoShader, UNIFORM_SCREENDEPTHMAP, TB_COLORMAP);
GLSL_FinishGPUShader(&tr.ssaoShader);
numEtcShaders++;
for (i = 0; i < 4; i++)
{
attribs = ATTR_POSITION | ATTR_TEXCOORD;
extradefines[0] = '\0';
if (qglesMajorVersion < 3 && glRefConfig.standardDerivatives)
{
Q_strcat(extradefines, 1024, "#extension GL_OES_standard_derivatives : enable\n");
}
if (i & 1)
Q_strcat(extradefines, 1024, "#define USE_VERTICAL_BLUR\n");
else
Q_strcat(extradefines, 1024, "#define USE_HORIZONTAL_BLUR\n");
if (!(i & 2))
Q_strcat(extradefines, 1024, "#define USE_DEPTH\n");
if (!GLSL_InitGPUShader(&tr.depthBlurShader[i], "depthBlur", attribs, qtrue, extradefines, qtrue, fallbackShader_depthblur_vp, fallbackShader_depthblur_fp))
{
ri.Error(ERR_FATAL, "Could not load depthBlur shader!");
}
GLSL_InitUniforms(&tr.depthBlurShader[i]);
GLSL_SetUniformInt(&tr.depthBlurShader[i], UNIFORM_SCREENIMAGEMAP, TB_COLORMAP);
GLSL_SetUniformInt(&tr.depthBlurShader[i], UNIFORM_SCREENDEPTHMAP, TB_LIGHTMAP);
GLSL_FinishGPUShader(&tr.depthBlurShader[i]);
numEtcShaders++;
}
}
#if 0 #if 0
attribs = ATTR_POSITION | ATTR_TEXCOORD; attribs = ATTR_POSITION | ATTR_TEXCOORD;
extradefines[0] = '\0'; extradefines[0] = '\0';
@ -1456,7 +1544,7 @@ void GLSL_ShutdownGPUShaders(void)
ri.Printf(PRINT_ALL, "------- GLSL_ShutdownGPUShaders -------\n"); ri.Printf(PRINT_ALL, "------- GLSL_ShutdownGPUShaders -------\n");
for (i = 0; i < ATTR_INDEX_COUNT; i++) for (i = 0; i < ATTR_INDEX_COUNT && i < glRefConfig.maxVertexAttribs; i++)
qglDisableVertexAttribArray(i); qglDisableVertexAttribArray(i);
GL_BindNullProgram(); GL_BindNullProgram();

View file

@ -1455,6 +1455,106 @@ byte mipBlendColors[16][4] = {
{0,0,255,128}, {0,0,255,128},
}; };
/*
==================
R_ConvertTextureFormat
Convert RGBA unsigned byte to specified format and type
==================
*/
#define ROW_PADDING( width, bpp, alignment ) PAD( (width) * (bpp), (alignment) ) - (width) * (bpp)
void R_ConvertTextureFormat( const byte *in, int width, int height, GLenum format, GLenum type, byte *out )
{
int x, y, rowPadding;
int unpackAlign = 4; // matches GL_UNPACK_ALIGNMENT default
if ( format == GL_RGB && type == GL_UNSIGNED_BYTE )
{
rowPadding = ROW_PADDING( width, 3, unpackAlign );
for ( y = 0; y < height; y++ )
{
for ( x = 0; x < width; x++ )
{
*out++ = *in++;
*out++ = *in++;
*out++ = *in++;
in++;
}
out += rowPadding;
}
}
else if ( format == GL_LUMINANCE && type == GL_UNSIGNED_BYTE )
{
rowPadding = ROW_PADDING( width, 1, unpackAlign );
for ( y = 0; y < height; y++ )
{
for ( x = 0; x < width; x++ )
{
*out++ = *in++; // red
in += 3;
}
out += rowPadding;
}
}
else if ( format == GL_LUMINANCE_ALPHA && type == GL_UNSIGNED_BYTE )
{
rowPadding = ROW_PADDING( width, 2, unpackAlign );
for ( y = 0; y < height; y++ )
{
for ( x = 0; x < width; x++ )
{
*out++ = *in++; // red
in += 2;
*out++ = *in++; // alpha
}
out += rowPadding;
}
}
else if ( format == GL_RGB && type == GL_UNSIGNED_SHORT_5_6_5 )
{
rowPadding = ROW_PADDING( width, 2, unpackAlign );
for ( y = 0; y < height; y++ )
{
for ( x = 0; x < width; x++, in += 4, out += 2 )
{
*((unsigned short*)out) = ( (unsigned short)( in[0] >> 3 ) << 11 )
| ( (unsigned short)( in[1] >> 2 ) << 5 )
| ( (unsigned short)( in[2] >> 3 ) << 0 );
}
out += rowPadding;
}
}
else if ( format == GL_RGBA && type == GL_UNSIGNED_SHORT_4_4_4_4 )
{
rowPadding = ROW_PADDING( width, 2, unpackAlign );
for ( y = 0; y < height; y++ )
{
for ( x = 0; x < width; x++, in += 4, out += 2 )
{
*((unsigned short*)out) = ( (unsigned short)( in[0] >> 4 ) << 12 )
| ( (unsigned short)( in[1] >> 4 ) << 8 )
| ( (unsigned short)( in[2] >> 4 ) << 4 )
| ( (unsigned short)( in[3] >> 4 ) << 0 );
}
out += rowPadding;
}
}
else
{
ri.Error( ERR_DROP, "Unable to convert RGBA image to OpenGL format 0x%X and type 0x%X", format, type );
}
}
static void RawImage_SwizzleRA( byte *data, int width, int height ) static void RawImage_SwizzleRA( byte *data, int width, int height )
{ {
int i; int i;
@ -1944,18 +2044,20 @@ static GLenum PixelDataFormatFromInternalFormat(GLenum internalFormat)
} }
} }
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 ) static void RawImage_UploadTexture(GLuint texture, byte *data, int x, int y, int width, int height, GLenum target, GLenum picFormat, GLenum dataFormat, GLenum dataType, int numMips, GLenum internalFormat, imgType_t type, imgFlags_t flags, qboolean subtexture )
{ {
GLenum dataFormat, dataType;
qboolean rgtc = internalFormat == GL_COMPRESSED_RG_RGTC2; qboolean rgtc = internalFormat == GL_COMPRESSED_RG_RGTC2;
qboolean rgba8 = picFormat == GL_RGBA8 || picFormat == GL_SRGB8_ALPHA8_EXT; qboolean rgba8 = picFormat == GL_RGBA8 || picFormat == GL_SRGB8_ALPHA8_EXT;
qboolean rgba = rgba8 || picFormat == GL_RGBA16; qboolean rgba = rgba8 || picFormat == GL_RGBA16;
qboolean mipmap = !!(flags & IMGFLAG_MIPMAP); qboolean mipmap = !!(flags & IMGFLAG_MIPMAP);
int size, miplevel; int size, miplevel;
qboolean lastMip = qfalse; qboolean lastMip = qfalse;
byte *formatBuffer = NULL;
dataFormat = PixelDataFormatFromInternalFormat(internalFormat); if (qglesMajorVersion && rgba8 && (dataFormat != GL_RGBA || dataType != GL_UNSIGNED_BYTE))
dataType = picFormat == GL_RGBA16 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_BYTE; {
formatBuffer = ri.Hunk_AllocateTempMemory(4 * width * height);
}
miplevel = 0; miplevel = 0;
do do
@ -1974,6 +2076,11 @@ static void RawImage_UploadTexture(GLuint texture, byte *data, int x, int y, int
if (rgba8 && rgtc) if (rgba8 && rgtc)
RawImage_UploadToRgtc2Texture(texture, miplevel, x, y, width, height, data); RawImage_UploadToRgtc2Texture(texture, miplevel, x, y, width, height, data);
else if (formatBuffer)
{
R_ConvertTextureFormat(data, width, height, dataFormat, dataType, formatBuffer);
qglTextureSubImage2DEXT(texture, target, miplevel, x, y, width, height, dataFormat, dataType, formatBuffer);
}
else else
qglTextureSubImage2DEXT(texture, target, miplevel, x, y, width, height, dataFormat, dataType, data); qglTextureSubImage2DEXT(texture, target, miplevel, x, y, width, height, dataFormat, dataType, data);
} }
@ -2007,6 +2114,9 @@ static void RawImage_UploadTexture(GLuint texture, byte *data, int x, int y, int
} }
} }
while (!lastMip); while (!lastMip);
if (formatBuffer != NULL)
ri.Hunk_FreeTempMemory(formatBuffer);
} }
@ -2016,7 +2126,7 @@ Upload32
=============== ===============
*/ */
static void Upload32(byte *data, int x, int y, int width, int height, GLenum picFormat, int numMips, image_t *image, qboolean scaled) static void Upload32(byte *data, int x, int y, int width, int height, GLenum picFormat, GLenum dataFormat, GLenum dataType, int numMips, image_t *image, qboolean scaled)
{ {
int i, c; int i, c;
byte *scan; byte *scan;
@ -2071,7 +2181,7 @@ static void Upload32(byte *data, int x, int y, int width, int height, GLenum pic
for (i = 0; i < 6; i++) for (i = 0; i < 6; i++)
{ {
int w2 = width, h2 = height; int w2 = width, h2 = height;
RawImage_UploadTexture(image->texnum, data, x, y, width, height, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, picFormat, numMips, internalFormat, type, flags, qfalse); RawImage_UploadTexture(image->texnum, data, x, y, width, height, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, picFormat, dataFormat, dataType, numMips, internalFormat, type, flags, qfalse);
for (c = numMips; c; c--) for (c = numMips; c; c--)
{ {
data += CalculateMipSize(w2, h2, picFormat); data += CalculateMipSize(w2, h2, picFormat);
@ -2082,7 +2192,7 @@ static void Upload32(byte *data, int x, int y, int width, int height, GLenum pic
} }
else else
{ {
RawImage_UploadTexture(image->texnum, data, x, y, width, height, GL_TEXTURE_2D, picFormat, numMips, internalFormat, type, flags, qfalse); RawImage_UploadTexture(image->texnum, data, x, y, width, height, GL_TEXTURE_2D, picFormat, dataFormat, dataType, numMips, internalFormat, type, flags, qfalse);
} }
GL_CheckErrors(); GL_CheckErrors();
@ -2108,7 +2218,7 @@ image_t *R_CreateImage2( const char *name, byte *pic, int width, int height, GLe
qboolean picmip = !!(flags & IMGFLAG_PICMIP); qboolean picmip = !!(flags & IMGFLAG_PICMIP);
qboolean lastMip; qboolean lastMip;
GLenum textureTarget = cubemap ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D; GLenum textureTarget = cubemap ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D;
GLenum dataFormat; GLenum dataFormat, dataType;
if (strlen(name) >= MAX_QPATH ) { if (strlen(name) >= MAX_QPATH ) {
ri.Error (ERR_DROP, "R_CreateImage: \"%s\" is too long", name); ri.Error (ERR_DROP, "R_CreateImage: \"%s\" is too long", name);
@ -2140,6 +2250,53 @@ image_t *R_CreateImage2( const char *name, byte *pic, int width, int height, GLe
if (!internalFormat) if (!internalFormat)
internalFormat = RawImage_GetFormat(pic, width * height, picFormat, isLightmap, image->type, image->flags); internalFormat = RawImage_GetFormat(pic, width * height, picFormat, isLightmap, image->type, image->flags);
dataFormat = PixelDataFormatFromInternalFormat(internalFormat);
dataType = picFormat == GL_RGBA16 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_BYTE;
// Convert image data format for OpenGL ES, data is converted for each mip level
if (qglesMajorVersion)
{
switch (internalFormat)
{
case GL_LUMINANCE:
case GL_LUMINANCE8:
internalFormat = GL_LUMINANCE;
dataFormat = GL_LUMINANCE;
dataType = GL_UNSIGNED_BYTE;
break;
case GL_LUMINANCE_ALPHA:
case GL_LUMINANCE8_ALPHA8:
internalFormat = GL_LUMINANCE_ALPHA;
dataFormat = GL_LUMINANCE_ALPHA;
dataType = GL_UNSIGNED_BYTE;
break;
case GL_RGB:
case GL_RGB8:
internalFormat = GL_RGB;
dataFormat = GL_RGB;
dataType = GL_UNSIGNED_BYTE;
break;
case GL_RGB5:
internalFormat = GL_RGB;
dataFormat = GL_RGB;
dataType = GL_UNSIGNED_SHORT_5_6_5;
break;
case GL_RGBA:
case GL_RGBA8:
internalFormat = GL_RGBA;
dataFormat = GL_RGBA;
dataType = GL_UNSIGNED_BYTE;
break;
case GL_RGBA4:
internalFormat = GL_RGBA;
dataFormat = GL_RGBA;
dataType = GL_UNSIGNED_SHORT_4_4_4_4;
break;
default:
ri.Error( ERR_DROP, "Missing OpenGL ES support for image '%s' with internal format 0x%X\n", name, internalFormat );
}
}
image->internalFormat = internalFormat; image->internalFormat = internalFormat;
// Possibly scale image before uploading. // Possibly scale image before uploading.
@ -2164,7 +2321,6 @@ image_t *R_CreateImage2( const char *name, byte *pic, int width, int height, GLe
image->uploadHeight = height; image->uploadHeight = height;
// Allocate texture storage so we don't have to worry about it later. // Allocate texture storage so we don't have to worry about it later.
dataFormat = PixelDataFormatFromInternalFormat(internalFormat);
mipWidth = width; mipWidth = width;
mipHeight = height; mipHeight = height;
miplevel = 0; miplevel = 0;
@ -2176,11 +2332,11 @@ image_t *R_CreateImage2( const char *name, byte *pic, int width, int height, GLe
int i; int i;
for (i = 0; i < 6; i++) for (i = 0; i < 6; i++)
qglTextureImage2DEXT(image->texnum, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, miplevel, internalFormat, mipWidth, mipHeight, 0, dataFormat, GL_UNSIGNED_BYTE, NULL); qglTextureImage2DEXT(image->texnum, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, miplevel, internalFormat, mipWidth, mipHeight, 0, dataFormat, dataType, NULL);
} }
else else
{ {
qglTextureImage2DEXT(image->texnum, GL_TEXTURE_2D, miplevel, internalFormat, mipWidth, mipHeight, 0, dataFormat, GL_UNSIGNED_BYTE, NULL); qglTextureImage2DEXT(image->texnum, GL_TEXTURE_2D, miplevel, internalFormat, mipWidth, mipHeight, 0, dataFormat, dataType, NULL);
} }
mipWidth = MAX(1, mipWidth >> 1); mipWidth = MAX(1, mipWidth >> 1);
@ -2191,7 +2347,7 @@ image_t *R_CreateImage2( const char *name, byte *pic, int width, int height, GLe
// Upload data. // Upload data.
if (pic) if (pic)
Upload32(pic, 0, 0, width, height, picFormat, numMips, image, scaled); Upload32(pic, 0, 0, width, height, picFormat, dataFormat, dataType, numMips, image, scaled);
if (resampledBuffer != NULL) if (resampledBuffer != NULL)
ri.Hunk_FreeTempMemory(resampledBuffer); ri.Hunk_FreeTempMemory(resampledBuffer);
@ -2252,7 +2408,13 @@ image_t *R_CreateImage(const char *name, byte *pic, int width, int height, imgTy
void R_UpdateSubImage( image_t *image, byte *pic, int x, int y, int width, int height, GLenum picFormat ) void R_UpdateSubImage( image_t *image, byte *pic, int x, int y, int width, int height, GLenum picFormat )
{ {
Upload32(pic, x, y, width, height, picFormat, 0, image, qfalse); GLenum dataFormat, dataType;
// TODO: This is fine for lightmaps but (unused) general RGBA images need to store dataFormat / dataType in image_t for OpenGL ES?
dataFormat = PixelDataFormatFromInternalFormat(image->internalFormat);
dataType = picFormat == GL_RGBA16 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_BYTE;
Upload32(pic, x, y, width, height, picFormat, dataFormat, dataType, 0, image, qfalse);
} }
//=================================================================== //===================================================================
@ -2766,7 +2928,7 @@ void R_CreateBuiltinImages( void ) {
tr.renderImage = R_CreateImage("_render", NULL, width, height, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, hdrFormat); tr.renderImage = R_CreateImage("_render", NULL, width, height, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, hdrFormat);
if (r_shadowBlur->integer) if (r_shadowBlur->integer || r_hdr->integer)
tr.screenScratchImage = R_CreateImage("screenScratch", NULL, width, height, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, rgbFormat); tr.screenScratchImage = R_CreateImage("screenScratch", NULL, width, height, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, rgbFormat);
if (r_shadowBlur->integer || r_ssao->integer) if (r_shadowBlur->integer || r_ssao->integer)

View file

@ -279,8 +279,16 @@ static void InitOpenGL( void )
qglGetIntegerv( GL_MAX_TEXTURE_IMAGE_UNITS, &temp ); qglGetIntegerv( GL_MAX_TEXTURE_IMAGE_UNITS, &temp );
glConfig.numTextureUnits = temp; glConfig.numTextureUnits = temp;
qglGetIntegerv( GL_MAX_VERTEX_ATTRIBS, &temp );
glRefConfig.maxVertexAttribs = temp;
// reserve 160 components for other uniforms // reserve 160 components for other uniforms
qglGetIntegerv( GL_MAX_VERTEX_UNIFORM_COMPONENTS, &temp ); if ( qglesMajorVersion ) {
qglGetIntegerv( GL_MAX_VERTEX_UNIFORM_VECTORS, &temp );
temp *= 4;
} else {
qglGetIntegerv( GL_MAX_VERTEX_UNIFORM_COMPONENTS, &temp );
}
glRefConfig.glslMaxAnimatedBones = Com_Clamp( 0, IQM_MAX_JOINTS, ( temp - 160 ) / 16 ); glRefConfig.glslMaxAnimatedBones = Com_Clamp( 0, IQM_MAX_JOINTS, ( temp - 160 ) / 16 );
if ( glRefConfig.glslMaxAnimatedBones < 12 ) { if ( glRefConfig.glslMaxAnimatedBones < 12 ) {
glRefConfig.glslMaxAnimatedBones = 0; glRefConfig.glslMaxAnimatedBones = 0;
@ -451,21 +459,43 @@ Return value must be freed with ri.Hunk_FreeTempMemory()
byte *RB_ReadPixels(int x, int y, int width, int height, size_t *offset, int *padlen) byte *RB_ReadPixels(int x, int y, int width, int height, size_t *offset, int *padlen)
{ {
byte *buffer, *bufstart; byte *buffer, *bufstart;
int padwidth, linelen; int padwidth, linelen, bytesPerPixel;
GLint packAlign; int yin, xin, xout;
GLint packAlign, format;
// OpenGL ES is only required to support reading GL_RGBA
if (qglesMajorVersion >= 1) {
format = GL_RGBA;
bytesPerPixel = 4;
} else {
format = GL_RGB;
bytesPerPixel = 3;
}
qglGetIntegerv(GL_PACK_ALIGNMENT, &packAlign); qglGetIntegerv(GL_PACK_ALIGNMENT, &packAlign);
linelen = width * 3; linelen = width * bytesPerPixel;
padwidth = PAD(linelen, packAlign); padwidth = PAD(linelen, packAlign);
// Allocate a few more bytes so that we can choose an alignment we like // Allocate a few more bytes so that we can choose an alignment we like
buffer = ri.Hunk_AllocateTempMemory(padwidth * height + *offset + packAlign - 1); 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); bufstart = PADP((intptr_t) buffer + *offset, packAlign);
qglReadPixels(x, y, width, height, format, GL_UNSIGNED_BYTE, bufstart);
linelen = width * 3;
// Convert RGBA to RGB, in place, line by line
if (format == GL_RGBA) {
for (yin = 0; yin < height; yin++) {
for (xin = 0, xout = 0; xout < linelen; xin += 4, xout += 3) {
bufstart[yin*padwidth + xout + 0] = bufstart[yin*padwidth + xin + 0];
bufstart[yin*padwidth + xout + 1] = bufstart[yin*padwidth + xin + 1];
bufstart[yin*padwidth + xout + 2] = bufstart[yin*padwidth + xin + 2];
}
}
}
*offset = bufstart - buffer; *offset = bufstart - buffer;
*padlen = padwidth - linelen; *padlen = padwidth - linelen;
@ -877,9 +907,10 @@ const void *RB_TakeVideoFrameCmd( const void *data )
{ {
const videoFrameCommand_t *cmd; const videoFrameCommand_t *cmd;
byte *cBuf; byte *cBuf;
size_t memcount, linelen; size_t memcount, bytesPerPixel, linelen, avilinelen;
int padwidth, avipadwidth, padlen, avipadlen; int padwidth, avipadwidth, padlen, avipadlen;
GLint packAlign; int yin, xin, xout;
GLint packAlign, format;
// finish any 2D drawing if needed // finish any 2D drawing if needed
if(tess.numIndexes) if(tess.numIndexes)
@ -887,20 +918,32 @@ const void *RB_TakeVideoFrameCmd( const void *data )
cmd = (const videoFrameCommand_t *)data; cmd = (const videoFrameCommand_t *)data;
// OpenGL ES is only required to support reading GL_RGBA
if (qglesMajorVersion >= 1) {
format = GL_RGBA;
bytesPerPixel = 4;
} else {
format = GL_RGB;
bytesPerPixel = 3;
}
qglGetIntegerv(GL_PACK_ALIGNMENT, &packAlign); qglGetIntegerv(GL_PACK_ALIGNMENT, &packAlign);
linelen = cmd->width * 3; linelen = cmd->width * bytesPerPixel;
// Alignment stuff for glReadPixels // Alignment stuff for glReadPixels
padwidth = PAD(linelen, packAlign); padwidth = PAD(linelen, packAlign);
padlen = padwidth - linelen; padlen = padwidth - linelen;
avilinelen = cmd->width * 3;
// AVI line padding // AVI line padding
avipadwidth = PAD(linelen, AVI_LINE_PADDING); avipadwidth = PAD(avilinelen, AVI_LINE_PADDING);
avipadlen = avipadwidth - linelen; avipadlen = avipadwidth - avilinelen;
cBuf = PADP(cmd->captureBuffer, packAlign); cBuf = PADP(cmd->captureBuffer, packAlign);
qglReadPixels(0, 0, cmd->width, cmd->height, GL_RGB, qglReadPixels(0, 0, cmd->width, cmd->height, format,
GL_UNSIGNED_BYTE, cBuf); GL_UNSIGNED_BYTE, cBuf);
memcount = padwidth * cmd->height; memcount = padwidth * cmd->height;
@ -911,7 +954,21 @@ const void *RB_TakeVideoFrameCmd( const void *data )
if(cmd->motionJpeg) if(cmd->motionJpeg)
{ {
memcount = RE_SaveJPGToBuffer(cmd->encodeBuffer, linelen * cmd->height, // Convert RGBA to RGB, in place, line by line
if (format == GL_RGBA) {
linelen = cmd->width * 3;
padlen = padwidth - linelen;
for (yin = 0; yin < cmd->height; yin++) {
for (xin = 0, xout = 0; xout < linelen; xin += 4, xout += 3) {
cBuf[yin*padwidth + xout + 0] = cBuf[yin*padwidth + xin + 0];
cBuf[yin*padwidth + xout + 1] = cBuf[yin*padwidth + xin + 1];
cBuf[yin*padwidth + xout + 2] = cBuf[yin*padwidth + xin + 2];
}
}
}
memcount = RE_SaveJPGToBuffer(cmd->encodeBuffer, avilinelen * cmd->height,
r_aviMotionJpegQuality->integer, r_aviMotionJpegQuality->integer,
cmd->width, cmd->height, cBuf, padlen); cmd->width, cmd->height, cBuf, padlen);
ri.CL_WriteAVIVideoFrame(cmd->encodeBuffer, memcount); ri.CL_WriteAVIVideoFrame(cmd->encodeBuffer, memcount);
@ -934,7 +991,7 @@ const void *RB_TakeVideoFrameCmd( const void *data )
*destptr++ = srcptr[2]; *destptr++ = srcptr[2];
*destptr++ = srcptr[1]; *destptr++ = srcptr[1];
*destptr++ = srcptr[0]; *destptr++ = srcptr[0];
srcptr += 3; srcptr += bytesPerPixel;
} }
Com_Memset(destptr, '\0', avipadlen); Com_Memset(destptr, '\0', avipadlen);

View file

@ -49,8 +49,10 @@ QGL_ARB_vertex_array_object_PROCS;
QGL_EXT_direct_state_access_PROCS; QGL_EXT_direct_state_access_PROCS;
#undef GLE #undef GLE
#define GL_INDEX_TYPE GL_UNSIGNED_INT #define GL_INDEX_TYPE GL_UNSIGNED_SHORT
typedef unsigned int glIndex_t; typedef unsigned short glIndex_t;
typedef unsigned int vaoCacheGlIndex_t;
#define BUFFER_OFFSET(i) ((char *)NULL + (i)) #define BUFFER_OFFSET(i) ((char *)NULL + (i))
@ -637,8 +639,14 @@ typedef enum
UNIFORM_ENABLETEXTURES, UNIFORM_ENABLETEXTURES,
UNIFORM_DIFFUSETEXMATRIX, UNIFORM_DIFFUSETEXMATRIX0,
UNIFORM_DIFFUSETEXOFFTURB, UNIFORM_DIFFUSETEXMATRIX1,
UNIFORM_DIFFUSETEXMATRIX2,
UNIFORM_DIFFUSETEXMATRIX3,
UNIFORM_DIFFUSETEXMATRIX4,
UNIFORM_DIFFUSETEXMATRIX5,
UNIFORM_DIFFUSETEXMATRIX6,
UNIFORM_DIFFUSETEXMATRIX7,
UNIFORM_TCGEN0, UNIFORM_TCGEN0,
UNIFORM_TCGEN0VECTOR0, UNIFORM_TCGEN0VECTOR0,
@ -1400,6 +1408,7 @@ typedef struct {
qboolean intelGraphics; qboolean intelGraphics;
qboolean occlusionQuery; qboolean occlusionQuery;
GLenum occlusionQueryTarget;
int glslMajorVersion; int glslMajorVersion;
int glslMinorVersion; int glslMinorVersion;
@ -1423,6 +1432,18 @@ typedef struct {
qboolean vertexArrayObject; qboolean vertexArrayObject;
qboolean directStateAccess; qboolean directStateAccess;
int maxVertexAttribs;
qboolean gpuVertexAnimation;
GLenum vaoCacheGlIndexType; // GL_UNSIGNED_INT or GL_UNSIGNED_SHORT
size_t vaoCacheGlIndexSize; // must be <= sizeof( vaoCacheGlIndex_t )
// OpenGL ES extensions
qboolean readDepth;
qboolean readStencil;
qboolean shadowSamplers;
qboolean standardDerivatives;
} glRefConfig_t; } glRefConfig_t;
@ -1989,6 +2010,7 @@ const void *RB_TakeVideoFrameCmd( const void *data );
// tr_shader.c // tr_shader.c
// //
shader_t *R_FindShader( const char *name, int lightmapIndex, qboolean mipRawImage ); shader_t *R_FindShader( const char *name, int lightmapIndex, qboolean mipRawImage );
shader_t *R_FindShaderEx( const char *name, int lightmapIndex, qboolean mipRawImage, int realLightmapIndex );
shader_t *R_GetShaderByHandle( qhandle_t hShader ); shader_t *R_GetShaderByHandle( qhandle_t hShader );
shader_t *R_GetShaderByState( int index, long *cycleTime ); shader_t *R_GetShaderByState( int index, long *cycleTime );
shader_t *R_FindShaderByName( const char *name ); shader_t *R_FindShaderByName( const char *name );
@ -2206,6 +2228,7 @@ void R_VaoList_f(void);
void RB_UpdateTessVao(unsigned int attribBits); void RB_UpdateTessVao(unsigned int attribBits);
void VaoCache_Commit(void); void VaoCache_Commit(void);
void VaoCache_DrawElements(int numIndexes, int firstIndex);
void VaoCache_Init(void); void VaoCache_Init(void);
void VaoCache_BindVao(void); void VaoCache_BindVao(void);
void VaoCache_CheckAdd(qboolean *endSurface, qboolean *recycleVertexBuffer, qboolean *recycleIndexBuffer, int numVerts, int numIndexes); void VaoCache_CheckAdd(qboolean *endSurface, qboolean *recycleVertexBuffer, qboolean *recycleIndexBuffer, int numVerts, int numIndexes);
@ -2495,5 +2518,7 @@ size_t RE_SaveJPGToBuffer(byte *buffer, size_t bufSize, int quality,
void RE_TakeVideoFrame( int width, int height, void RE_TakeVideoFrame( int width, int height,
byte *captureBuffer, byte *encodeBuffer, qboolean motionJpeg ); byte *captureBuffer, byte *encodeBuffer, qboolean motionJpeg );
void R_ConvertTextureFormat( const byte *in, int width, int height, GLenum format, GLenum type, byte *out );
#endif //TR_LOCAL_H #endif //TR_LOCAL_H

View file

@ -282,9 +282,10 @@ R_AddMD3Surfaces
*/ */
void R_AddMD3Surfaces( trRefEntity_t *ent ) { void R_AddMD3Surfaces( trRefEntity_t *ent ) {
int i; int i;
mdvModel_t *model = NULL; mdvModel_t *model;
mdvSurface_t *surface = NULL; mdvSurface_t *surface;
shader_t *shader = NULL; void *drawSurf;
shader_t *shader;
int cull; int cull;
int lod; int lod;
int fogNum; int fogNum;
@ -382,6 +383,12 @@ void R_AddMD3Surfaces( trRefEntity_t *ent ) {
shader = tr.shaders[ surface->shaderIndexes[ ent->e.skinNum % surface->numShaderIndexes ] ]; shader = tr.shaders[ surface->shaderIndexes[ ent->e.skinNum % surface->numShaderIndexes ] ];
} }
if ( model->numVaoSurfaces > 0 ) {
drawSurf = &model->vaoSurfaces[i];
} else {
drawSurf = surface;
}
// we will add shadows even if the main object isn't visible in the view // we will add shadows even if the main object isn't visible in the view
// stencil shadows can't do personal models unless I polyhedron clip // stencil shadows can't do personal models unless I polyhedron clip
@ -390,7 +397,7 @@ void R_AddMD3Surfaces( trRefEntity_t *ent ) {
&& fogNum == 0 && fogNum == 0
&& !(ent->e.renderfx & ( RF_NOSHADOW | RF_DEPTHHACK ) ) && !(ent->e.renderfx & ( RF_NOSHADOW | RF_DEPTHHACK ) )
&& shader->sort == SS_OPAQUE ) { && shader->sort == SS_OPAQUE ) {
R_AddDrawSurf( (void *)&model->vaoSurfaces[i], tr.shadowShader, 0, qfalse, qfalse, 0 ); R_AddDrawSurf( drawSurf, tr.shadowShader, 0, qfalse, qfalse, 0 );
} }
// projection shadows work fine with personal models // projection shadows work fine with personal models
@ -398,12 +405,12 @@ void R_AddMD3Surfaces( trRefEntity_t *ent ) {
&& fogNum == 0 && fogNum == 0
&& (ent->e.renderfx & RF_SHADOW_PLANE ) && (ent->e.renderfx & RF_SHADOW_PLANE )
&& shader->sort == SS_OPAQUE ) { && shader->sort == SS_OPAQUE ) {
R_AddDrawSurf( (void *)&model->vaoSurfaces[i], tr.projectionShadowShader, 0, qfalse, qfalse, 0 ); R_AddDrawSurf( drawSurf, tr.projectionShadowShader, 0, qfalse, qfalse, 0 );
} }
// don't add third_person objects if not viewing through a portal // don't add third_person objects if not viewing through a portal
if ( !personalModel ) { if ( !personalModel ) {
R_AddDrawSurf((void *)&model->vaoSurfaces[i], shader, fogNum, qfalse, qfalse, cubemapIndex ); R_AddDrawSurf( drawSurf, shader, fogNum, qfalse, qfalse, cubemapIndex );
} }
surface++; surface++;

View file

@ -664,6 +664,12 @@ static qboolean R_LoadMD3(model_t * mod, int lod, void *buffer, int bufferSize,
surf++; surf++;
} }
if (mdvModel->numFrames > 1 && !glRefConfig.gpuVertexAnimation)
{
mdvModel->numVaoSurfaces = 0;
mdvModel->vaoSurfaces = NULL;
}
else
{ {
srfVaoMdvMesh_t *vaoSurf; srfVaoMdvMesh_t *vaoSurf;

View file

@ -290,6 +290,8 @@ static qboolean RB_UpdateSunFlareVis(void)
ri.Printf(PRINT_DEVELOPER, "Waited %d iterations\n", iter); ri.Printf(PRINT_DEVELOPER, "Waited %d iterations\n", iter);
} }
// Note: On desktop OpenGL this is a sample count (glRefConfig.occlusionQueryTarget == GL_SAMPLES_PASSED)
// but on OpenGL ES this is a boolean (glRefConfig.occlusionQueryTarget == GL_ANY_SAMPLES_PASSED)
qglGetQueryObjectuiv(tr.sunFlareQuery[tr.sunFlareQueryIndex], GL_QUERY_RESULT, &sampleCount); qglGetQueryObjectuiv(tr.sunFlareQuery[tr.sunFlareQueryIndex], GL_QUERY_RESULT, &sampleCount);
return sampleCount > 0; return sampleCount > 0;
} }
@ -447,7 +449,7 @@ static void RB_VBlur(FBO_t *srcFbo, FBO_t *dstFbo, float strength)
RB_BlurAxis(srcFbo, dstFbo, strength, qfalse); RB_BlurAxis(srcFbo, dstFbo, strength, qfalse);
} }
void RB_GaussianBlur(float blur) void RB_GaussianBlur(FBO_t *srcFbo, FBO_t *dstFbo, float blur)
{ {
//float mul = 1.f; //float mul = 1.f;
float factor = Com_Clamp(0.f, 1.f, blur); float factor = Com_Clamp(0.f, 1.f, blur);
@ -462,7 +464,7 @@ void RB_GaussianBlur(float blur)
VectorSet4(color, 1, 1, 1, 1); VectorSet4(color, 1, 1, 1, 1);
// first, downsample the framebuffer // first, downsample the framebuffer
FBO_FastBlit(NULL, NULL, tr.quarterFbo[0], NULL, GL_COLOR_BUFFER_BIT, GL_LINEAR); FBO_FastBlit(srcFbo, NULL, tr.quarterFbo[0], NULL, GL_COLOR_BUFFER_BIT, GL_LINEAR);
FBO_FastBlit(tr.quarterFbo[0], NULL, tr.textureScratchFbo[0], NULL, GL_COLOR_BUFFER_BIT, GL_LINEAR); FBO_FastBlit(tr.quarterFbo[0], NULL, tr.textureScratchFbo[0], NULL, GL_COLOR_BUFFER_BIT, GL_LINEAR);
// set the alpha channel // set the alpha channel
@ -478,6 +480,6 @@ void RB_GaussianBlur(float blur)
VectorSet4(srcBox, 0, 0, tr.textureScratchFbo[0]->width, tr.textureScratchFbo[0]->height); VectorSet4(srcBox, 0, 0, tr.textureScratchFbo[0]->width, tr.textureScratchFbo[0]->height);
VectorSet4(dstBox, 0, 0, glConfig.vidWidth, glConfig.vidHeight); VectorSet4(dstBox, 0, 0, glConfig.vidWidth, glConfig.vidHeight);
color[3] = factor; color[3] = factor;
FBO_Blit(tr.textureScratchFbo[0], srcBox, NULL, NULL, dstBox, NULL, color, GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA); FBO_Blit(tr.textureScratchFbo[0], srcBox, NULL, dstFbo, dstBox, NULL, color, GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA);
} }
} }

View file

@ -28,6 +28,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
void RB_ToneMap(FBO_t *hdrFbo, ivec4_t hdrBox, FBO_t *ldrFbo, ivec4_t ldrBox, int autoExposure); void RB_ToneMap(FBO_t *hdrFbo, ivec4_t hdrBox, FBO_t *ldrFbo, ivec4_t ldrBox, int autoExposure);
void RB_BokehBlur(FBO_t *src, ivec4_t srcBox, FBO_t *dst, ivec4_t dstBox, float blur); void RB_BokehBlur(FBO_t *src, ivec4_t srcBox, FBO_t *dst, ivec4_t dstBox, float blur);
void RB_SunRays(FBO_t *srcFbo, ivec4_t srcBox, FBO_t *dstFbo, ivec4_t dstBox); void RB_SunRays(FBO_t *srcFbo, ivec4_t srcBox, FBO_t *dstFbo, ivec4_t dstBox);
void RB_GaussianBlur(float blur); void RB_GaussianBlur(FBO_t *srcFbo, FBO_t *dstFbo, float blur);
#endif #endif

View file

@ -40,7 +40,14 @@ R_DrawElements
void R_DrawElements( int numIndexes, int firstIndex ) void R_DrawElements( int numIndexes, int firstIndex )
{ {
qglDrawElements(GL_TRIANGLES, numIndexes, GL_INDEX_TYPE, BUFFER_OFFSET(firstIndex * sizeof(glIndex_t))); if (tess.useCacheVao)
{
VaoCache_DrawElements(numIndexes, firstIndex);
}
else
{
qglDrawElements(GL_TRIANGLES, numIndexes, GL_INDEX_TYPE, BUFFER_OFFSET(firstIndex * sizeof(glIndex_t)));
}
} }
@ -181,33 +188,30 @@ extern float EvalWaveForm( const waveForm_t *wf );
extern float EvalWaveFormClamped( const waveForm_t *wf ); extern float EvalWaveFormClamped( const waveForm_t *wf );
static void ComputeTexMods( shaderStage_t *pStage, int bundleNum, float *outMatrix, float *outOffTurb) static void ComputeTexMods( shaderStage_t *pStage, int bundleNum, vec4_t outMatrix[8])
{ {
int tm; int tm;
float matrix[6], currentmatrix[6]; float matrix[6];
float tmpmatrix[6];
float currentmatrix[6];
float turb[2];
textureBundle_t *bundle = &pStage->bundle[bundleNum]; textureBundle_t *bundle = &pStage->bundle[bundleNum];
qboolean hasTurb = qfalse;
matrix[0] = 1.0f; matrix[2] = 0.0f; matrix[4] = 0.0f;
matrix[1] = 0.0f; matrix[3] = 1.0f; matrix[5] = 0.0f;
currentmatrix[0] = 1.0f; currentmatrix[2] = 0.0f; currentmatrix[4] = 0.0f; currentmatrix[0] = 1.0f; currentmatrix[2] = 0.0f; currentmatrix[4] = 0.0f;
currentmatrix[1] = 0.0f; currentmatrix[3] = 1.0f; currentmatrix[5] = 0.0f; currentmatrix[1] = 0.0f; currentmatrix[3] = 1.0f; currentmatrix[5] = 0.0f;
outMatrix[0] = 1.0f; outMatrix[2] = 0.0f;
outMatrix[1] = 0.0f; outMatrix[3] = 1.0f;
outOffTurb[0] = 0.0f; outOffTurb[1] = 0.0f; outOffTurb[2] = 0.0f; outOffTurb[3] = 0.0f;
for ( tm = 0; tm < bundle->numTexMods ; tm++ ) { for ( tm = 0; tm < bundle->numTexMods ; tm++ ) {
switch ( bundle->texMods[tm].type ) switch ( bundle->texMods[tm].type )
{ {
case TMOD_NONE: case TMOD_NONE:
tm = TR_MAX_TEXMODS; // break out of for loop matrix[0] = 1.0f; matrix[2] = 0.0f; matrix[4] = 0.0f;
matrix[1] = 0.0f; matrix[3] = 1.0f; matrix[5] = 0.0f;
break; break;
case TMOD_TURBULENT: case TMOD_TURBULENT:
RB_CalcTurbulentFactors(&bundle->texMods[tm].wave, &outOffTurb[2], &outOffTurb[3]); RB_CalcTurbulentFactors(&bundle->texMods[tm].wave, &turb[0], &turb[1]);
break; break;
case TMOD_ENTITY_TRANSLATE: case TMOD_ENTITY_TRANSLATE:
@ -246,35 +250,68 @@ static void ComputeTexMods( shaderStage_t *pStage, int bundleNum, float *outMatr
switch ( bundle->texMods[tm].type ) switch ( bundle->texMods[tm].type )
{ {
case TMOD_NONE:
case TMOD_TURBULENT: case TMOD_TURBULENT:
default: outMatrix[tm*2+0][0] = 1; outMatrix[tm*2+0][1] = 0; outMatrix[tm*2+0][2] = 0;
outMatrix[tm*2+1][0] = 0; outMatrix[tm*2+1][1] = 1; outMatrix[tm*2+1][2] = 0;
outMatrix[tm*2+0][3] = turb[0];
outMatrix[tm*2+1][3] = turb[1];
hasTurb = qtrue;
break; break;
case TMOD_NONE:
case TMOD_ENTITY_TRANSLATE: case TMOD_ENTITY_TRANSLATE:
case TMOD_SCROLL: case TMOD_SCROLL:
case TMOD_SCALE: case TMOD_SCALE:
case TMOD_STRETCH: case TMOD_STRETCH:
case TMOD_TRANSFORM: case TMOD_TRANSFORM:
case TMOD_ROTATE: case TMOD_ROTATE:
outMatrix[0] = matrix[0] * currentmatrix[0] + matrix[2] * currentmatrix[1]; default:
outMatrix[1] = matrix[1] * currentmatrix[0] + matrix[3] * currentmatrix[1]; outMatrix[tm*2+0][0] = matrix[0]; outMatrix[tm*2+0][1] = matrix[2]; outMatrix[tm*2+0][2] = matrix[4];
outMatrix[tm*2+1][0] = matrix[1]; outMatrix[tm*2+1][1] = matrix[3]; outMatrix[tm*2+1][2] = matrix[5];
outMatrix[2] = matrix[0] * currentmatrix[2] + matrix[2] * currentmatrix[3]; outMatrix[tm*2+0][3] = 0;
outMatrix[3] = matrix[1] * currentmatrix[2] + matrix[3] * currentmatrix[3]; outMatrix[tm*2+1][3] = 0;
outOffTurb[0] = matrix[0] * currentmatrix[4] + matrix[2] * currentmatrix[5] + matrix[4]; tmpmatrix[0] = matrix[0] * currentmatrix[0] + matrix[2] * currentmatrix[1];
outOffTurb[1] = matrix[1] * currentmatrix[4] + matrix[3] * currentmatrix[5] + matrix[5]; tmpmatrix[1] = matrix[1] * currentmatrix[0] + matrix[3] * currentmatrix[1];
currentmatrix[0] = outMatrix[0]; tmpmatrix[2] = matrix[0] * currentmatrix[2] + matrix[2] * currentmatrix[3];
currentmatrix[1] = outMatrix[1]; tmpmatrix[3] = matrix[1] * currentmatrix[2] + matrix[3] * currentmatrix[3];
currentmatrix[2] = outMatrix[2];
currentmatrix[3] = outMatrix[3]; tmpmatrix[4] = matrix[0] * currentmatrix[4] + matrix[2] * currentmatrix[5] + matrix[4];
currentmatrix[4] = outOffTurb[0]; tmpmatrix[5] = matrix[1] * currentmatrix[4] + matrix[3] * currentmatrix[5] + matrix[5];
currentmatrix[5] = outOffTurb[1];
currentmatrix[0] = tmpmatrix[0];
currentmatrix[1] = tmpmatrix[1];
currentmatrix[2] = tmpmatrix[2];
currentmatrix[3] = tmpmatrix[3];
currentmatrix[4] = tmpmatrix[4];
currentmatrix[5] = tmpmatrix[5];
break; break;
} }
} }
// if turb isn't used, only one matrix is needed
if ( !hasTurb ) {
tm = 0;
outMatrix[tm*2+0][0] = currentmatrix[0]; outMatrix[tm*2+0][1] = currentmatrix[2]; outMatrix[tm*2+0][2] = currentmatrix[4];
outMatrix[tm*2+1][0] = currentmatrix[1]; outMatrix[tm*2+1][1] = currentmatrix[3]; outMatrix[tm*2+1][2] = currentmatrix[5];
outMatrix[tm*2+0][3] = 0;
outMatrix[tm*2+1][3] = 0;
tm++;
}
for ( ; tm < TR_MAX_TEXMODS ; tm++ ) {
outMatrix[tm*2+0][0] = 1; outMatrix[tm*2+0][1] = 0; outMatrix[tm*2+0][2] = 0;
outMatrix[tm*2+1][0] = 0; outMatrix[tm*2+1][1] = 1; outMatrix[tm*2+1][2] = 0;
outMatrix[tm*2+0][3] = 0;
outMatrix[tm*2+1][3] = 0;
}
} }
@ -665,8 +702,7 @@ static void ForwardDlight( void ) {
dlight_t *dl; dlight_t *dl;
shaderProgram_t *sp; shaderProgram_t *sp;
vec4_t vector; vec4_t vector;
vec4_t texMatrix; vec4_t texMatrix[8];
vec4_t texOffTurb;
if ( !( tess.dlightBits & ( 1 << l ) ) ) { if ( !( tess.dlightBits & ( 1 << l ) ) ) {
continue; // this surface definitely doesn't have any of this light continue; // this surface definitely doesn't have any of this light
@ -792,9 +828,15 @@ static void ForwardDlight( void ) {
if (r_dlightMode->integer >= 2) if (r_dlightMode->integer >= 2)
GL_BindToTMU(tr.shadowCubemaps[l], TB_SHADOWMAP); GL_BindToTMU(tr.shadowCubemaps[l], TB_SHADOWMAP);
ComputeTexMods( pStage, TB_DIFFUSEMAP, texMatrix, texOffTurb ); ComputeTexMods( pStage, TB_DIFFUSEMAP, texMatrix );
GLSL_SetUniformVec4(sp, UNIFORM_DIFFUSETEXMATRIX, texMatrix); GLSL_SetUniformVec4(sp, UNIFORM_DIFFUSETEXMATRIX0, texMatrix[0]);
GLSL_SetUniformVec4(sp, UNIFORM_DIFFUSETEXOFFTURB, texOffTurb); GLSL_SetUniformVec4(sp, UNIFORM_DIFFUSETEXMATRIX1, texMatrix[1]);
GLSL_SetUniformVec4(sp, UNIFORM_DIFFUSETEXMATRIX2, texMatrix[2]);
GLSL_SetUniformVec4(sp, UNIFORM_DIFFUSETEXMATRIX3, texMatrix[3]);
GLSL_SetUniformVec4(sp, UNIFORM_DIFFUSETEXMATRIX4, texMatrix[4]);
GLSL_SetUniformVec4(sp, UNIFORM_DIFFUSETEXMATRIX5, texMatrix[5]);
GLSL_SetUniformVec4(sp, UNIFORM_DIFFUSETEXMATRIX6, texMatrix[6]);
GLSL_SetUniformVec4(sp, UNIFORM_DIFFUSETEXMATRIX7, texMatrix[7]);
GLSL_SetUniformInt(sp, UNIFORM_TCGEN0, pStage->bundle[0].tcGen); GLSL_SetUniformInt(sp, UNIFORM_TCGEN0, pStage->bundle[0].tcGen);
@ -996,8 +1038,7 @@ static void RB_IterateStagesGeneric( shaderCommands_t *input )
{ {
shaderStage_t *pStage = input->xstages[stage]; shaderStage_t *pStage = input->xstages[stage];
shaderProgram_t *sp; shaderProgram_t *sp;
vec4_t texMatrix; vec4_t texMatrix[8];
vec4_t texOffTurb;
if ( !pStage ) if ( !pStage )
{ {
@ -1184,19 +1225,31 @@ static void RB_IterateStagesGeneric( shaderCommands_t *input )
if (r_lightmap->integer) if (r_lightmap->integer)
{ {
vec4_t v; vec4_t st[2];
VectorSet4(v, 1.0f, 0.0f, 0.0f, 1.0f); VectorSet4(st[0], 1.0f, 0.0f, 0.0f, 0.0f);
GLSL_SetUniformVec4(sp, UNIFORM_DIFFUSETEXMATRIX, v); VectorSet4(st[1], 0.0f, 1.0f, 0.0f, 0.0f);
VectorSet4(v, 0.0f, 0.0f, 0.0f, 0.0f); GLSL_SetUniformVec4(sp, UNIFORM_DIFFUSETEXMATRIX0, st[0]);
GLSL_SetUniformVec4(sp, UNIFORM_DIFFUSETEXOFFTURB, v); GLSL_SetUniformVec4(sp, UNIFORM_DIFFUSETEXMATRIX1, st[1]);
GLSL_SetUniformVec4(sp, UNIFORM_DIFFUSETEXMATRIX2, st[0]);
GLSL_SetUniformVec4(sp, UNIFORM_DIFFUSETEXMATRIX3, st[1]);
GLSL_SetUniformVec4(sp, UNIFORM_DIFFUSETEXMATRIX4, st[0]);
GLSL_SetUniformVec4(sp, UNIFORM_DIFFUSETEXMATRIX5, st[1]);
GLSL_SetUniformVec4(sp, UNIFORM_DIFFUSETEXMATRIX6, st[0]);
GLSL_SetUniformVec4(sp, UNIFORM_DIFFUSETEXMATRIX7, st[1]);
GLSL_SetUniformInt(sp, UNIFORM_TCGEN0, TCGEN_LIGHTMAP); GLSL_SetUniformInt(sp, UNIFORM_TCGEN0, TCGEN_LIGHTMAP);
} }
else else
{ {
ComputeTexMods(pStage, TB_DIFFUSEMAP, texMatrix, texOffTurb); ComputeTexMods(pStage, TB_DIFFUSEMAP, texMatrix);
GLSL_SetUniformVec4(sp, UNIFORM_DIFFUSETEXMATRIX, texMatrix); GLSL_SetUniformVec4(sp, UNIFORM_DIFFUSETEXMATRIX0, texMatrix[0]);
GLSL_SetUniformVec4(sp, UNIFORM_DIFFUSETEXOFFTURB, texOffTurb); GLSL_SetUniformVec4(sp, UNIFORM_DIFFUSETEXMATRIX1, texMatrix[1]);
GLSL_SetUniformVec4(sp, UNIFORM_DIFFUSETEXMATRIX2, texMatrix[2]);
GLSL_SetUniformVec4(sp, UNIFORM_DIFFUSETEXMATRIX3, texMatrix[3]);
GLSL_SetUniformVec4(sp, UNIFORM_DIFFUSETEXMATRIX4, texMatrix[4]);
GLSL_SetUniformVec4(sp, UNIFORM_DIFFUSETEXMATRIX5, texMatrix[5]);
GLSL_SetUniformVec4(sp, UNIFORM_DIFFUSETEXMATRIX6, texMatrix[6]);
GLSL_SetUniformVec4(sp, UNIFORM_DIFFUSETEXMATRIX7, texMatrix[7]);
GLSL_SetUniformInt(sp, UNIFORM_TCGEN0, pStage->bundle[0].tcGen); GLSL_SetUniformInt(sp, UNIFORM_TCGEN0, pStage->bundle[0].tcGen);
if (pStage->bundle[0].tcGen == TCGEN_VECTOR) if (pStage->bundle[0].tcGen == TCGEN_VECTOR)
@ -1674,6 +1727,8 @@ void RB_EndSurface( void ) {
tess.numIndexes = 0; tess.numIndexes = 0;
tess.numVertexes = 0; tess.numVertexes = 0;
tess.firstIndex = 0; tess.firstIndex = 0;
tess.useCacheVao = qfalse;
tess.useInternalVao = qfalse;
GLimp_LogComment( "----------\n" ); GLimp_LogComment( "----------\n" );
} }

View file

@ -30,6 +30,7 @@ static char *s_shaderText;
static shaderStage_t stages[MAX_SHADER_STAGES]; static shaderStage_t stages[MAX_SHADER_STAGES];
static shader_t shader; static shader_t shader;
static texModInfo_t texMods[MAX_SHADER_STAGES][TR_MAX_TEXMODS]; static texModInfo_t texMods[MAX_SHADER_STAGES][TR_MAX_TEXMODS];
static int shader_realLightmapIndex;
#define FILE_HASH_SIZE 1024 #define FILE_HASH_SIZE 1024
static shader_t* hashTable[FILE_HASH_SIZE]; static shader_t* hashTable[FILE_HASH_SIZE];
@ -2929,7 +2930,7 @@ static void FixFatLightmapTexCoords(void)
return; return;
} }
lightmapnum = shader.lightmapIndex; lightmapnum = shader_realLightmapIndex;
if (tr.worldDeluxeMapping) if (tr.worldDeluxeMapping)
lightmapnum >>= 1; lightmapnum >>= 1;
@ -2943,21 +2944,44 @@ static void FixFatLightmapTexCoords(void)
break; break;
} }
// fix tcMod transform for internal lightmaps, it may be used by q3map2 lightstyles
if ( pStage->bundle[0].isLightmap ) { if ( pStage->bundle[0].isLightmap ) {
for ( i = 0; i < pStage->bundle[0].numTexMods; i++ ) { // fix tcMod transform for internal lightmaps, it may be used by q3map2 lightstyles
tmi = &pStage->bundle[0].texMods[i]; if ( pStage->bundle[0].tcGen == TCGEN_LIGHTMAP ) {
for ( i = 0; i < pStage->bundle[0].numTexMods; i++ ) {
tmi = &pStage->bundle[0].texMods[i];
if ( tmi->type == TMOD_TRANSFORM ) { if ( tmi->type == TMOD_TRANSFORM ) {
tmi->translate[0] /= (float)tr.fatLightmapCols; tmi->translate[0] /= (float)tr.fatLightmapCols;
tmi->translate[1] /= (float)tr.fatLightmapRows; tmi->translate[1] /= (float)tr.fatLightmapRows;
}
}
}
// fix tcGen environment for internal lightmaps to be limited to the sub-image of the atlas
// this is done last so other tcMods are applied first in the 0.0 to 1.0 space
if ( pStage->bundle[0].tcGen == TCGEN_ENVIRONMENT_MAPPED ) {
if ( pStage->bundle[0].numTexMods == TR_MAX_TEXMODS ) {
ri.Printf( PRINT_DEVELOPER, "WARNING: too many tcmods to fix lightmap texcoords for r_mergeLightmaps in shader '%s'", shader.name );
} else {
tmi = &pStage->bundle[0].texMods[pStage->bundle[0].numTexMods];
pStage->bundle[0].numTexMods++;
tmi->matrix[0][0] = 1.0f / tr.fatLightmapCols;
tmi->matrix[0][1] = 0;
tmi->matrix[1][0] = 0;
tmi->matrix[1][1] = 1.0f / tr.fatLightmapRows;
tmi->translate[0] = ( lightmapnum % tr.fatLightmapCols ) / (float)tr.fatLightmapCols;
tmi->translate[1] = ( lightmapnum / tr.fatLightmapCols ) / (float)tr.fatLightmapRows;
tmi->type = TMOD_TRANSFORM;
} }
} }
} }
// add a tcMod transform for external lightmaps to convert back to the original texcoords // add a tcMod transform for external lightmaps to convert back to the original texcoords
else if ( pStage->bundle[0].tcGen == TCGEN_LIGHTMAP ) { else if ( pStage->bundle[0].tcGen == TCGEN_LIGHTMAP ) {
if ( pStage->bundle[0].numTexMods == TR_MAX_TEXMODS ) { if ( pStage->bundle[0].numTexMods == TR_MAX_TEXMODS ) {
ri.Printf( PRINT_DEVELOPER, "WARNING: too many tcmods to fix external lightmap texcoords for r_mergeLightmaps in shader '%s'", shader.name ); ri.Printf( PRINT_DEVELOPER, "WARNING: too many tcmods to fix lightmap texcoords for r_mergeLightmaps in shader '%s'", shader.name );
} else { } else {
size = pStage->bundle[0].numTexMods * sizeof( texModInfo_t ); size = pStage->bundle[0].numTexMods * sizeof( texModInfo_t );
@ -2973,8 +2997,8 @@ static void FixFatLightmapTexCoords(void)
tmi->matrix[1][0] = 0; tmi->matrix[1][0] = 0;
tmi->matrix[1][1] = tr.fatLightmapRows; tmi->matrix[1][1] = tr.fatLightmapRows;
tmi->translate[0] = -(lightmapnum % tr.fatLightmapCols); tmi->translate[0] = -( lightmapnum % tr.fatLightmapCols );
tmi->translate[1] = -(lightmapnum / tr.fatLightmapCols); tmi->translate[1] = -( lightmapnum / tr.fatLightmapCols );
tmi->type = TMOD_TRANSFORM; tmi->type = TMOD_TRANSFORM;
} }
@ -2987,7 +3011,7 @@ static void FixFatLightmapTexCoords(void)
InitShader InitShader
=============== ===============
*/ */
static void InitShader( const char *name, int lightmapIndex ) { static void InitShaderEx( const char *name, int lightmapIndex, int realLightmapIndex ) {
int i; int i;
// clear the global shader // clear the global shader
@ -2996,6 +3020,7 @@ static void InitShader( const char *name, int lightmapIndex ) {
Q_strncpyz( shader.name, name, sizeof( shader.name ) ); Q_strncpyz( shader.name, name, sizeof( shader.name ) );
shader.lightmapIndex = lightmapIndex; shader.lightmapIndex = lightmapIndex;
shader_realLightmapIndex = realLightmapIndex;
for ( i = 0 ; i < MAX_SHADER_STAGES ; i++ ) { for ( i = 0 ; i < MAX_SHADER_STAGES ; i++ ) {
stages[i].bundle[0].texMods = texMods[i]; stages[i].bundle[0].texMods = texMods[i];
@ -3016,6 +3041,10 @@ static void InitShader( const char *name, int lightmapIndex ) {
} }
} }
static void InitShader( const char *name, int lightmapIndex ) {
InitShaderEx( name, lightmapIndex, lightmapIndex );
}
/* /*
========================= =========================
FinishShader FinishShader
@ -3337,6 +3366,10 @@ most world construction surfaces.
=============== ===============
*/ */
shader_t *R_FindShader( const char *name, int lightmapIndex, qboolean mipRawImage ) { shader_t *R_FindShader( const char *name, int lightmapIndex, qboolean mipRawImage ) {
return R_FindShaderEx( name, lightmapIndex, mipRawImage, lightmapIndex );
}
shader_t *R_FindShaderEx( const char *name, int lightmapIndex, qboolean mipRawImage, int realLightmapIndex ) {
char strippedName[MAX_QPATH]; char strippedName[MAX_QPATH];
int hash; int hash;
char *shaderText; char *shaderText;
@ -3376,7 +3409,7 @@ shader_t *R_FindShader( const char *name, int lightmapIndex, qboolean mipRawImag
} }
} }
InitShader( strippedName, lightmapIndex ); InitShaderEx( strippedName, lightmapIndex, realLightmapIndex );
// //
// attempt to define shader from an explicit parameter file // attempt to define shader from an explicit parameter file

View file

@ -435,7 +435,7 @@ static void DrawSkySide( struct image_s *image, const int mins[2], const int max
*/ */
{ {
shaderProgram_t *sp = &tr.lightallShader[0]; shaderProgram_t *sp = &tr.lightallShader[0];
vec4_t vector; vec4_t st[2];
GLSL_BindProgram(sp); GLSL_BindProgram(sp);
@ -453,11 +453,16 @@ static void DrawSkySide( struct image_s *image, const int mins[2], const int max
color[3] = 0.0f; color[3] = 0.0f;
GLSL_SetUniformVec4(sp, UNIFORM_VERTCOLOR, color); GLSL_SetUniformVec4(sp, UNIFORM_VERTCOLOR, color);
VectorSet4(vector, 1.0, 0.0, 0.0, 1.0); VectorSet4(st[0], 1.0f, 0.0f, 0.0f, 0.0f);
GLSL_SetUniformVec4(sp, UNIFORM_DIFFUSETEXMATRIX, vector); VectorSet4(st[1], 0.0f, 1.0f, 0.0f, 0.0f);
GLSL_SetUniformVec4(sp, UNIFORM_DIFFUSETEXMATRIX0, st[0]);
VectorSet4(vector, 0.0, 0.0, 0.0, 0.0); GLSL_SetUniformVec4(sp, UNIFORM_DIFFUSETEXMATRIX1, st[1]);
GLSL_SetUniformVec4(sp, UNIFORM_DIFFUSETEXOFFTURB, vector); GLSL_SetUniformVec4(sp, UNIFORM_DIFFUSETEXMATRIX2, st[0]);
GLSL_SetUniformVec4(sp, UNIFORM_DIFFUSETEXMATRIX3, st[1]);
GLSL_SetUniformVec4(sp, UNIFORM_DIFFUSETEXMATRIX4, st[0]);
GLSL_SetUniformVec4(sp, UNIFORM_DIFFUSETEXMATRIX5, st[1]);
GLSL_SetUniformVec4(sp, UNIFORM_DIFFUSETEXMATRIX6, st[0]);
GLSL_SetUniformVec4(sp, UNIFORM_DIFFUSETEXMATRIX7, st[1]);
GLSL_SetUniformInt(sp, UNIFORM_ALPHATEST, 0); GLSL_SetUniformInt(sp, UNIFORM_ALPHATEST, 0);
} }

View file

@ -676,7 +676,7 @@ static struct
srfVert_t vertexes[VAOCACHE_QUEUE_MAX_VERTEXES]; srfVert_t vertexes[VAOCACHE_QUEUE_MAX_VERTEXES];
int vertexCommitSize; int vertexCommitSize;
glIndex_t indexes[VAOCACHE_QUEUE_MAX_INDEXES]; vaoCacheGlIndex_t indexes[VAOCACHE_QUEUE_MAX_INDEXES];
int indexCommitSize; int indexCommitSize;
} }
vcq; vcq;
@ -687,18 +687,13 @@ vcq;
// srfVert_t is 60 bytes // srfVert_t is 60 bytes
// assuming each vert is referenced 4 times, need 16 bytes (4 glIndex_t) per vert // assuming each vert is referenced 4 times, need 16 bytes (4 glIndex_t) per vert
// -> need about 4/15ths the space for indexes as vertexes // -> need about 4/15ths the space for indexes as vertexes
#if GL_INDEX_TYPE == GL_UNSIGNED_SHORT
#define VAOCACHE_VERTEX_BUFFER_SIZE (sizeof(srfVert_t) * USHRT_MAX)
#define VAOCACHE_INDEX_BUFFER_SIZE (sizeof(glIndex_t) * USHRT_MAX * 4)
#else // GL_UNSIGNED_INT
#define VAOCACHE_VERTEX_BUFFER_SIZE (16 * 1024 * 1024) #define VAOCACHE_VERTEX_BUFFER_SIZE (16 * 1024 * 1024)
#define VAOCACHE_INDEX_BUFFER_SIZE (5 * 1024 * 1024) #define VAOCACHE_INDEX_BUFFER_SIZE (5 * 1024 * 1024)
#endif
typedef struct buffered_s typedef struct buffered_s
{ {
void *data; glIndex_t *indexes;
int size; int numIndexes;
int bufferOffset; int bufferOffset;
} }
buffered_t; buffered_t;
@ -736,7 +731,7 @@ void VaoCache_Commit(void)
buffered_t *indexSet2 = indexSet; buffered_t *indexSet2 = indexSet;
for (surf = vcq.surfaces; surf < end; surf++, indexSet2++) for (surf = vcq.surfaces; surf < end; surf++, indexSet2++)
{ {
if (surf->indexes != indexSet2->data || (surf->numIndexes * sizeof(glIndex_t)) != indexSet2->size) if (surf->indexes != indexSet2->indexes || surf->numIndexes != indexSet2->numIndexes)
break; break;
} }
@ -750,7 +745,7 @@ void VaoCache_Commit(void)
// If found, use it // If found, use it
if (indexSet < vc.surfaceIndexSets + vc.numSurfaces) if (indexSet < vc.surfaceIndexSets + vc.numSurfaces)
{ {
tess.firstIndex = indexSet->bufferOffset / sizeof(glIndex_t); tess.firstIndex = indexSet->bufferOffset / glRefConfig.vaoCacheGlIndexSize;
//ri.Printf(PRINT_ALL, "firstIndex %d numIndexes %d as %d\n", tess.firstIndex, tess.numIndexes, (int)(batchLength - vc.batchLengths)); //ri.Printf(PRINT_ALL, "firstIndex %d numIndexes %d as %d\n", tess.firstIndex, tess.numIndexes, (int)(batchLength - vc.batchLengths));
//ri.Printf(PRINT_ALL, "vc.numSurfaces %d vc.numBatches %d\n", vc.numSurfaces, vc.numBatches); //ri.Printf(PRINT_ALL, "vc.numSurfaces %d vc.numBatches %d\n", vc.numSurfaces, vc.numBatches);
} }
@ -759,20 +754,21 @@ void VaoCache_Commit(void)
else else
{ {
srfVert_t *dstVertex = vcq.vertexes; srfVert_t *dstVertex = vcq.vertexes;
glIndex_t *dstIndex = vcq.indexes; vaoCacheGlIndex_t *dstIndex = vcq.indexes;
unsigned short *dstIndexUshort = (unsigned short *)vcq.indexes;
batchLength = vc.batchLengths + vc.numBatches; batchLength = vc.batchLengths + vc.numBatches;
*batchLength = vcq.numSurfaces; *batchLength = vcq.numSurfaces;
vc.numBatches++; vc.numBatches++;
tess.firstIndex = vc.indexOffset / sizeof(glIndex_t); tess.firstIndex = vc.indexOffset / glRefConfig.vaoCacheGlIndexSize;
vcq.vertexCommitSize = 0; vcq.vertexCommitSize = 0;
vcq.indexCommitSize = 0; vcq.indexCommitSize = 0;
for (surf = vcq.surfaces; surf < end; surf++) for (surf = vcq.surfaces; surf < end; surf++)
{ {
glIndex_t *srcIndex = surf->indexes; glIndex_t *srcIndex = surf->indexes;
int vertexesSize = surf->numVerts * sizeof(srfVert_t); int vertexesSize = surf->numVerts * sizeof(srfVert_t);
int indexesSize = surf->numIndexes * sizeof(glIndex_t); int indexesSize = surf->numIndexes * glRefConfig.vaoCacheGlIndexSize;
int i, indexOffset = (vc.vertexOffset + vcq.vertexCommitSize) / sizeof(srfVert_t); int i, indexOffset = (vc.vertexOffset + vcq.vertexCommitSize) / sizeof(srfVert_t);
Com_Memcpy(dstVertex, surf->vertexes, vertexesSize); Com_Memcpy(dstVertex, surf->vertexes, vertexesSize);
@ -781,13 +777,21 @@ void VaoCache_Commit(void)
vcq.vertexCommitSize += vertexesSize; vcq.vertexCommitSize += vertexesSize;
indexSet = vc.surfaceIndexSets + vc.numSurfaces; indexSet = vc.surfaceIndexSets + vc.numSurfaces;
indexSet->data = surf->indexes; indexSet->indexes = surf->indexes;
indexSet->size = indexesSize; indexSet->numIndexes = surf->numIndexes;
indexSet->bufferOffset = vc.indexOffset + vcq.indexCommitSize; indexSet->bufferOffset = vc.indexOffset + vcq.indexCommitSize;
vc.numSurfaces++; vc.numSurfaces++;
for (i = 0; i < surf->numIndexes; i++) if (glRefConfig.vaoCacheGlIndexType == GL_UNSIGNED_SHORT)
*dstIndex++ = *srcIndex++ + indexOffset; {
for (i = 0; i < surf->numIndexes; i++)
*dstIndexUshort++ = *srcIndex++ + indexOffset;
}
else
{
for (i = 0; i < surf->numIndexes; i++)
*dstIndex++ = *srcIndex++ + indexOffset;
}
vcq.indexCommitSize += indexesSize; vcq.indexCommitSize += indexesSize;
} }
@ -810,9 +814,30 @@ void VaoCache_Commit(void)
} }
} }
void VaoCache_DrawElements(int numIndexes, int firstIndex)
{
assert( glState.currentVao == vc.vao );
qglDrawElements(GL_TRIANGLES, numIndexes, glRefConfig.vaoCacheGlIndexType, BUFFER_OFFSET(firstIndex * glRefConfig.vaoCacheGlIndexSize));
}
void VaoCache_Init(void) void VaoCache_Init(void)
{ {
vc.vao = R_CreateVao("VaoCache", NULL, VAOCACHE_VERTEX_BUFFER_SIZE, NULL, VAOCACHE_INDEX_BUFFER_SIZE, VAO_USAGE_DYNAMIC); int vertexBufferSize;
int indexBufferSize;
if (glRefConfig.vaoCacheGlIndexType == GL_UNSIGNED_SHORT)
{
vertexBufferSize = sizeof(srfVert_t) * USHRT_MAX;
indexBufferSize = sizeof(unsigned short) * USHRT_MAX * 4;
}
else
{
vertexBufferSize = VAOCACHE_VERTEX_BUFFER_SIZE;
indexBufferSize = VAOCACHE_INDEX_BUFFER_SIZE;
}
vc.vao = R_CreateVao("VaoCache", NULL, vertexBufferSize, NULL, indexBufferSize, VAO_USAGE_DYNAMIC);
vc.vao->attribs[ATTR_INDEX_POSITION].enabled = 1; vc.vao->attribs[ATTR_INDEX_POSITION].enabled = 1;
vc.vao->attribs[ATTR_INDEX_TEXCOORD].enabled = 1; vc.vao->attribs[ATTR_INDEX_TEXCOORD].enabled = 1;
@ -881,7 +906,7 @@ void VaoCache_BindVao(void)
void VaoCache_CheckAdd(qboolean *endSurface, qboolean *recycleVertexBuffer, qboolean *recycleIndexBuffer, int numVerts, int numIndexes) void VaoCache_CheckAdd(qboolean *endSurface, qboolean *recycleVertexBuffer, qboolean *recycleIndexBuffer, int numVerts, int numIndexes)
{ {
int vertexesSize = sizeof(srfVert_t) * numVerts; int vertexesSize = sizeof(srfVert_t) * numVerts;
int indexesSize = sizeof(glIndex_t) * numIndexes; int indexesSize = glRefConfig.vaoCacheGlIndexSize * numIndexes;
if (vc.vao->vertexesSize < vc.vertexOffset + vcq.vertexCommitSize + vertexesSize) if (vc.vao->vertexesSize < vc.vertexOffset + vcq.vertexCommitSize + vertexesSize)
{ {
@ -924,7 +949,7 @@ void VaoCache_CheckAdd(qboolean *endSurface, qboolean *recycleVertexBuffer, qboo
*endSurface = qtrue; *endSurface = qtrue;
} }
if (VAOCACHE_QUEUE_MAX_INDEXES * sizeof(glIndex_t) < vcq.indexCommitSize + indexesSize) if (VAOCACHE_QUEUE_MAX_INDEXES * glRefConfig.vaoCacheGlIndexSize < vcq.indexCommitSize + indexesSize)
{ {
//ri.Printf(PRINT_ALL, "out of queued indexes\n"); //ri.Printf(PRINT_ALL, "out of queued indexes\n");
*endSurface = qtrue; *endSurface = qtrue;
@ -964,5 +989,5 @@ void VaoCache_AddSurface(srfVert_t *verts, int numVerts, glIndex_t *indexes, int
vcq.numSurfaces++; vcq.numSurfaces++;
vcq.vertexCommitSize += sizeof(srfVert_t) * numVerts; vcq.vertexCommitSize += sizeof(srfVert_t) * numVerts;
vcq.indexCommitSize += sizeof(glIndex_t) * numIndexes; vcq.indexCommitSize += glRefConfig.vaoCacheGlIndexSize * numIndexes;
} }

View file

@ -52,6 +52,7 @@ cvar_t *r_allowSoftwareGL; // Don't abort out if a hardware visual can't be obta
cvar_t *r_allowResize; // make window resizable cvar_t *r_allowResize; // make window resizable
cvar_t *r_centerWindow; cvar_t *r_centerWindow;
cvar_t *r_sdlDriver; cvar_t *r_sdlDriver;
cvar_t *r_preferOpenGLES;
int qglMajorVersion, qglMinorVersion; int qglMajorVersion, qglMinorVersion;
int qglesMajorVersion, qglesMinorVersion; int qglesMajorVersion, qglesMinorVersion;
@ -230,6 +231,27 @@ static void GLimp_DetectAvailableModes(void)
SDL_free( modes ); SDL_free( modes );
} }
/*
===============
OpenGL ES compatibility
===============
*/
static void APIENTRY GLimp_GLES_ClearDepth( GLclampd depth ) {
qglClearDepthf( depth );
}
static void APIENTRY GLimp_GLES_DepthRange( GLclampd near_val, GLclampd far_val ) {
qglDepthRangef( near_val, far_val );
}
static void APIENTRY GLimp_GLES_DrawBuffer( GLenum mode ) {
// unsupported
}
static void APIENTRY GLimp_GLES_PolygonMode( GLenum face, GLenum mode ) {
// unsupported
}
/* /*
=============== ===============
GLimp_GetProcAddresses GLimp_GetProcAddresses
@ -306,8 +328,11 @@ static qboolean GLimp_GetProcAddresses( qboolean fixedFunction ) {
QGL_1_3_PROCS; QGL_1_3_PROCS;
QGL_1_5_PROCS; QGL_1_5_PROCS;
QGL_2_0_PROCS; QGL_2_0_PROCS;
// error so this doesn't segfault due to NULL desktop GL functions being used
Com_Error( ERR_FATAL, "Unsupported OpenGL Version: %s", version ); qglClearDepth = GLimp_GLES_ClearDepth;
qglDepthRange = GLimp_GLES_DepthRange;
qglDrawBuffer = GLimp_GLES_DrawBuffer;
qglPolygonMode = GLimp_GLES_PolygonMode;
} else { } else {
Com_Error( ERR_FATAL, "Unsupported OpenGL Version (%s), OpenGL 2.0 is required", version ); Com_Error( ERR_FATAL, "Unsupported OpenGL Version (%s), OpenGL 2.0 is required", version );
} }
@ -369,6 +394,12 @@ GLimp_SetMode
*/ */
static int GLimp_SetMode(int mode, qboolean fullscreen, qboolean noborder, qboolean fixedFunction) static int GLimp_SetMode(int mode, qboolean fullscreen, qboolean noborder, qboolean fixedFunction)
{ {
struct GLimp_ContextType {
int profileMask;
int majorVersion;
int minorVersion;
} contexts[4];
int numContexts, type;
const char *glstring; const char *glstring;
int perChannelColorBits; int perChannelColorBits;
int colorBits, depthBits, stencilBits; int colorBits, depthBits, stencilBits;
@ -499,6 +530,63 @@ static int GLimp_SetMode(int mode, qboolean fullscreen, qboolean noborder, qbool
stencilBits = r_stencilbits->value; stencilBits = r_stencilbits->value;
samples = r_ext_multisample->value; samples = r_ext_multisample->value;
numContexts = 0;
if ( !fixedFunction ) {
int profileMask;
qboolean preferOpenGLES;
SDL_GL_ResetAttributes();
SDL_GL_GetAttribute( SDL_GL_CONTEXT_PROFILE_MASK, &profileMask );
preferOpenGLES = ( r_preferOpenGLES->integer == 1 ||
( r_preferOpenGLES->integer == -1 && profileMask == SDL_GL_CONTEXT_PROFILE_ES ) );
if ( preferOpenGLES ) {
#ifdef __EMSCRIPTEN__
// WebGL 2.0 isn't fully backward compatible so you have to ask for it specifically
contexts[numContexts].profileMask = SDL_GL_CONTEXT_PROFILE_ES;
contexts[numContexts].majorVersion = 3;
contexts[numContexts].minorVersion = 0;
numContexts++;
#endif
contexts[numContexts].profileMask = SDL_GL_CONTEXT_PROFILE_ES;
contexts[numContexts].majorVersion = 2;
contexts[numContexts].minorVersion = 0;
numContexts++;
}
contexts[numContexts].profileMask = SDL_GL_CONTEXT_PROFILE_CORE;
contexts[numContexts].majorVersion = 3;
contexts[numContexts].minorVersion = 2;
numContexts++;
contexts[numContexts].profileMask = 0;
contexts[numContexts].majorVersion = 2;
contexts[numContexts].minorVersion = 0;
numContexts++;
if ( !preferOpenGLES ) {
#ifdef __EMSCRIPTEN__
contexts[numContexts].profileMask = SDL_GL_CONTEXT_PROFILE_ES;
contexts[numContexts].majorVersion = 3;
contexts[numContexts].minorVersion = 0;
numContexts++;
#endif
contexts[numContexts].profileMask = SDL_GL_CONTEXT_PROFILE_ES;
contexts[numContexts].majorVersion = 2;
contexts[numContexts].minorVersion = 0;
numContexts++;
}
} else {
contexts[numContexts].profileMask = 0;
contexts[numContexts].majorVersion = 1;
contexts[numContexts].minorVersion = 1;
numContexts++;
}
for (i = 0; i < 16; i++) for (i = 0; i < 16; i++)
{ {
int testColorBits, testDepthBits, testStencilBits; int testColorBits, testDepthBits, testStencilBits;
@ -631,82 +719,68 @@ static int GLimp_SetMode(int mode, qboolean fullscreen, qboolean noborder, qbool
SDL_SetWindowIcon( SDL_window, icon ); SDL_SetWindowIcon( SDL_window, icon );
if (!fixedFunction) for ( type = 0; type < numContexts; type++ ) {
{ char contextName[32];
int profileMask, majorVersion, minorVersion;
SDL_GL_GetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, &profileMask);
SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &majorVersion);
SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &minorVersion);
ri.Printf(PRINT_ALL, "Trying to get an OpenGL 3.2 core context\n"); switch ( contexts[type].profileMask ) {
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); default:
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); case 0:
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); Com_sprintf( contextName, sizeof( contextName ), "OpenGL %d.%d",
if ((SDL_glContext = SDL_GL_CreateContext(SDL_window)) == NULL) contexts[type].majorVersion, contexts[type].minorVersion );
{ break;
ri.Printf(PRINT_ALL, "SDL_GL_CreateContext failed: %s\n", SDL_GetError()); case SDL_GL_CONTEXT_PROFILE_CORE:
ri.Printf(PRINT_ALL, "Reverting to default context\n"); Com_sprintf( contextName, sizeof( contextName ), "OpenGL %d.%d Core",
contexts[type].majorVersion, contexts[type].minorVersion );
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, profileMask); break;
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, majorVersion); case SDL_GL_CONTEXT_PROFILE_ES:
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, minorVersion); Com_sprintf( contextName, sizeof( contextName ), "OpenGL ES %d.%d",
contexts[type].majorVersion, contexts[type].minorVersion );
break;
} }
else
SDL_GL_SetAttribute( SDL_GL_CONTEXT_PROFILE_MASK, contexts[type].profileMask );
SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, contexts[type].majorVersion );
SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, contexts[type].minorVersion );
SDL_glContext = SDL_GL_CreateContext( SDL_window );
if ( !SDL_glContext )
{ {
const char *renderer; ri.Printf( PRINT_ALL, "SDL_GL_CreateContext() for %s context failed: %s\n", contextName, SDL_GetError() );
ri.Printf(PRINT_ALL, "SDL_GL_CreateContext succeeded.\n");
if ( GLimp_GetProcAddresses( fixedFunction ) )
{
renderer = (const char *)qglGetString(GL_RENDERER);
}
else
{
ri.Printf( PRINT_ALL, "GLimp_GetProcAddresses() failed for OpenGL 3.2 core context\n" );
renderer = NULL;
}
if (!renderer || (strstr(renderer, "Software Renderer") || strstr(renderer, "Software Rasterizer")))
{
if ( renderer )
ri.Printf(PRINT_ALL, "GL_RENDERER is %s, rejecting context\n", renderer);
GLimp_ClearProcAddresses();
SDL_GL_DeleteContext(SDL_glContext);
SDL_glContext = NULL;
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, profileMask);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, majorVersion);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, minorVersion);
}
}
}
else
{
SDL_glContext = NULL;
}
if ( !SDL_glContext )
{
if( ( SDL_glContext = SDL_GL_CreateContext( SDL_window ) ) == NULL )
{
ri.Printf( PRINT_DEVELOPER, "SDL_GL_CreateContext failed: %s\n", SDL_GetError( ) );
SDL_DestroyWindow( SDL_window );
SDL_window = NULL;
continue; continue;
} }
if ( !GLimp_GetProcAddresses( fixedFunction ) ) if ( !GLimp_GetProcAddresses( fixedFunction ) )
{ {
ri.Printf( PRINT_ALL, "GLimp_GetProcAddresses() failed\n" ); ri.Printf( PRINT_ALL, "GLimp_GetProcAddresses() for %s context failed\n", contextName );
GLimp_ClearProcAddresses(); GLimp_ClearProcAddresses();
SDL_GL_DeleteContext( SDL_glContext ); SDL_GL_DeleteContext( SDL_glContext );
SDL_glContext = NULL; SDL_glContext = NULL;
SDL_DestroyWindow( SDL_window );
SDL_window = NULL;
continue; continue;
} }
if ( contexts[type].profileMask == SDL_GL_CONTEXT_PROFILE_CORE ) {
const char *renderer;
renderer = (const char *)qglGetString( GL_RENDERER );
if ( !renderer || strstr( renderer, "Software Renderer" ) || strstr( renderer, "Software Rasterizer" ) )
{
ri.Printf( PRINT_ALL, "GL_RENDERER is %s, rejecting %s context\n", renderer, contextName );
GLimp_ClearProcAddresses();
SDL_GL_DeleteContext( SDL_glContext );
SDL_glContext = NULL;
continue;
}
}
break;
}
if ( !SDL_glContext ) {
SDL_DestroyWindow( SDL_window );
SDL_window = NULL;
continue;
} }
qglClearColor( 0, 0, 0, 1 ); qglClearColor( 0, 0, 0, 1 );
@ -815,7 +889,7 @@ static void GLimp_InitExtensions( qboolean fixedFunction )
glConfig.textureCompression = TC_NONE; glConfig.textureCompression = TC_NONE;
// GL_EXT_texture_compression_s3tc // GL_EXT_texture_compression_s3tc
if ( SDL_GL_ExtensionSupported( "GL_ARB_texture_compression" ) && if ( ( QGLES_VERSION_ATLEAST( 2, 0 ) || SDL_GL_ExtensionSupported( "GL_ARB_texture_compression" ) ) &&
SDL_GL_ExtensionSupported( "GL_EXT_texture_compression_s3tc" ) ) SDL_GL_ExtensionSupported( "GL_EXT_texture_compression_s3tc" ) )
{ {
if ( r_ext_compressed_textures->value ) if ( r_ext_compressed_textures->value )
@ -996,6 +1070,7 @@ void GLimp_Init( qboolean fixedFunction )
r_sdlDriver = ri.Cvar_Get( "r_sdlDriver", "", CVAR_ROM ); r_sdlDriver = ri.Cvar_Get( "r_sdlDriver", "", CVAR_ROM );
r_allowResize = ri.Cvar_Get( "r_allowResize", "0", CVAR_ARCHIVE | CVAR_LATCH ); r_allowResize = ri.Cvar_Get( "r_allowResize", "0", CVAR_ARCHIVE | CVAR_LATCH );
r_centerWindow = ri.Cvar_Get( "r_centerWindow", "0", CVAR_ARCHIVE | CVAR_LATCH ); r_centerWindow = ri.Cvar_Get( "r_centerWindow", "0", CVAR_ARCHIVE | CVAR_LATCH );
r_preferOpenGLES = ri.Cvar_Get( "r_preferOpenGLES", "-1", CVAR_ARCHIVE | CVAR_LATCH );
if( ri.Cvar_VariableIntegerValue( "com_abnormalExit" ) ) if( ri.Cvar_VariableIntegerValue( "com_abnormalExit" ) )
{ {

View file

@ -31,6 +31,10 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#include <ctype.h> #include <ctype.h>
#include <errno.h> #include <errno.h>
#ifdef __EMSCRIPTEN__
#include <emscripten/emscripten.h>
#endif
#ifndef DEDICATED #ifndef DEDICATED
#ifdef USE_LOCAL_HEADERS #ifdef USE_LOCAL_HEADERS
# include "SDL.h" # include "SDL.h"
@ -863,10 +867,14 @@ int main( int argc, char **argv )
signal( SIGTERM, Sys_SigHandler ); signal( SIGTERM, Sys_SigHandler );
signal( SIGINT, Sys_SigHandler ); signal( SIGINT, Sys_SigHandler );
#ifdef __EMSCRIPTEN__
emscripten_set_main_loop( Com_Frame, 0, 1 );
#else
while( 1 ) while( 1 )
{ {
Com_Frame( ); Com_Frame( );
} }
#endif
return 0; return 0;
} }

View file

@ -38,6 +38,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#include <fcntl.h> #include <fcntl.h>
#include <fenv.h> #include <fenv.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <time.h>
qboolean stdinIsATTY; qboolean stdinIsATTY;
@ -548,11 +549,15 @@ void Sys_Sleep( int msec )
} }
else else
{ {
struct timespec req;
// With nothing to select() on, we can't wait indefinitely // With nothing to select() on, we can't wait indefinitely
if( msec < 0 ) if( msec < 0 )
msec = 10; msec = 10;
usleep( msec * 1000 ); req.tv_sec = msec/1000;
req.tv_nsec = (msec%1000)*1000000;
nanosleep(&req, NULL);
} }
} }

View file

@ -20,6 +20,9 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
=========================================================================== ===========================================================================
*/ */
// Use EnumProcesses() with Windows XP compatibility
#define PSAPI_VERSION 1
#include "../qcommon/q_shared.h" #include "../qcommon/q_shared.h"
#include "../qcommon/qcommon.h" #include "../qcommon/qcommon.h"
#include "sys_local.h" #include "sys_local.h"

View file

@ -0,0 +1,11 @@
{
"baseq3r": {
"files": [
{"src": "baseq3r/pak0.pk3", "dst": "/baseq3r"},
{"src": "baseq3r/default.cfg", "dst": "/baseq3r"},
{"src": "baseq3r/vm/cgame.qvm", "dst": "/baseq3r/vm"},
{"src": "baseq3r/vm/qagame.qvm", "dst": "/baseq3r/vm"},
{"src": "baseq3r/vm/ui.qvm", "dst": "/baseq3r/vm"}
]
}
}

116
engine/code/web/client.html Normal file
View file

@ -0,0 +1,116 @@
<!DOCTYPE html><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
<title>__CLIENTBIN__ Emscripten demo</title>
<style>
html, body { margin: 0; padding: 0; width: 100%; height: 100%; overflow: hidden; background: rgb(0, 0, 0); display:flex; align-items: center; justify-content: center; }
canvas { max-width: 100%; max-height: 100%; min-width: 100%; min-height: 100%; object-fit: contain; }
</style>
<canvas id=canvas></canvas>
<script type=module>
// These strings are set in the generated HTML file in the build directory.
let CLIENTBIN = '__CLIENTBIN__';
let BASEGAME = '__BASEGAME__';
let EMSCRIPTEN_PRELOAD_FILE = Number('__EMSCRIPTEN_PRELOAD_FILE__');
// Detect if it's not the generated HTML file.
let clientHtmlFallback = (CLIENTBIN === '\_\_CLIENTBIN\_\_');
// Path or URL containing the client engine .js, .wasm, and possibly .data.
let enginePath = './';
// Path or URL containing fs_game directories.
let dataPath = './';
// Path or URL for config file that specifies the files to load for each fs_game.
let configFilename = `./${CLIENTBIN}-config.json`;
// If displaying the unmodified HTML file, fallback to defaults.
if (clientHtmlFallback) {
CLIENTBIN='ioquake3';
BASEGAME='baseq3';
EMSCRIPTEN_PRELOAD_FILE=0;
configFilename='./client-config.json';
}
if (window.location.protocol === 'file:') throw new Error(`Unfortunately browser security restrictions prevent loading wasm from a file: URL. This file must be loaded from a web server. The easiest way to do this is probably to use Python\'s built-in web server by running \`python3 -m http.server\` in the top level source directory and then navigate to http://localhost:8000/build/debug-emscripten-wasm32/${CLIENTBIN}.html`);
// First set up the command line arguments and the Emscripten filesystem.
const urlParams = new URLSearchParams(window.location.search);
const com_basegame = urlParams.get('com_basegame') || BASEGAME;
const fs_basegame = urlParams.get('fs_basegame') || '';
const fs_game = urlParams.get('fs_game') || '';
let generatedArguments = `
+set sv_pure 0
+set net_enabled 0
+set r_mode -2
+set com_basegame "${com_basegame}"
+set fs_basegame "${fs_basegame}"
+set fs_game "${fs_game}"
`;
// Note that unfortunately "+" needs to be encoded as "%2b" in URL query strings or it will be stripped by the browser.
const queryArgs = urlParams.get('args');
if (queryArgs) generatedArguments += ` ${queryArgs} `;
// If displaying the unmodified HTML file, the engine and data are probably located in a different directory.
if (clientHtmlFallback) {
// If buildPath is not specified, try to find a build in one of a few default paths.
let buildPath = urlParams.get('buildPath');
if (buildPath && !buildPath.endsWith('/')) buildPath += '/';
const buildPaths = buildPath ? [buildPath] : ['../../build/debug-emscripten-wasm32/', '../../build/release-emscripten-wasm32/', './'];
const scriptPaths = buildPaths.map(buildPath => buildPath + `${CLIENTBIN}_opengl2.wasm32.js`);
const scriptResponses = await Promise.all(scriptPaths.map(p => fetch(p, {method: 'HEAD'})));
const validBuilds = scriptResponses.filter(r => r.ok).length;
const goodURL = (newPath) => {
const url = new URL(window.location);
url.searchParams.set('buildPath', newPath);
return url.toString().replace(/%2f/gi, '/');
};
if (validBuilds === 0) throw new Error(`Didn't find any wasm builds. Run \`emmake make debug\` to build one, or use the buildPath query parameter to specify a directory containing ${CLIENTBIN}_opengl2.wasm32.[js,wasm,data], e.g. ${goodURL('../../build/debug-emscripten-wasm32/')}`);
if (validBuilds > 1) throw new Error(`Found multiple valid builds at the following paths: [${buildPaths.filter((path, i)=>scriptResponses[i].ok)}]. Please specify which one to run by adding a buildPath query parameter to the URL, e.g. ${goodURL(buildPaths.filter((path, i)=>scriptResponses[i].ok)[0])}`);
const buildIndex = scriptResponses.findIndex(r => r.ok);
enginePath = buildPaths[buildIndex];
dataPath = buildPaths[buildIndex];
}
const dataURL = new URL(dataPath, location.origin + location.pathname);
const configPromise = ( EMSCRIPTEN_PRELOAD_FILE === 1 ) ? Promise.resolve({[BASEGAME]: {files: []}})
: fetch(configFilename).then(r => r.ok ? r.json() : { /* empty config */ });
const ioquake3 = (await import(enginePath + `${CLIENTBIN}_opengl2.wasm32.js`)).default;
ioquake3({
canvas: canvas,
arguments: generatedArguments.trim().split(/\s+/),
locateFile: (file) => enginePath + file,
preRun: [async (module) => {
module.addRunDependency('setup-ioq3-filesystem');
try {
const config = await configPromise;
const gamedirs = [com_basegame,fs_basegame,fs_game];
for (let g = 0; g < gamedirs.length; g++) {
const gamedir = gamedirs[g];
if (gamedir === '') {
continue;
}
if (config[gamedir] === null
|| config[gamedir].files === null) {
console.warn(`Game directory '${gamedir}' cannot be used. It must have files listed in ${configFilename}.`);
continue;
}
const files = config[gamedir].files;
const fetches = files.map(file => fetch(new URL(file.src, dataURL)));
for (let i = 0; i < files.length; i++) {
const response = await fetches[i];
if (!response.ok) continue;
const data = await response.arrayBuffer();
let name = files[i].src.match(/[^/]+$/)[0];
let dir = files[i].dst;
module.FS.mkdirTree(dir);
module.FS.writeFile(`${dir}/${name}`, new Uint8Array(data));
}
}
} finally {
module.removeRunDependency('setup-ioq3-filesystem');
}
}],
});
</script>

View file

@ -31,6 +31,7 @@ Some of the major features currently implemented are:
* Multiuser support on Windows systems (user specific game data * Multiuser support on Windows systems (user specific game data
is stored in "%APPDATA%\Quake3") is stored in "%APPDATA%\Quake3")
* PNG support * PNG support
* Web support via Emscripten
* Many, many bug fixes * Many, many bug fixes
The map editor and associated compiling tools are not included. We suggest you The map editor and associated compiling tools are not included. We suggest you
@ -98,6 +99,20 @@ For macOS, building a Universal Binary 2 (macOS 10.9+, arm64, x86_64)
4. Copy the resulting ioquake3.app in /build/release-darwin-universal2 4. Copy the resulting ioquake3.app in /build/release-darwin-universal2
to your /Applications/ioquake3 folder. to your /Applications/ioquake3 folder.
For Web, building with Emscripten
1. Follow the installation instructions for the Emscripten SDK including
setting up the environment with emsdk_env.
2. Run `emmake make debug` (or release).
3. Copy or symlink your baseq3 pk3 files into the `build/debug-emscripten-wasm32/baseq3`
directory so they can be loaded at run-time. Only game files listed in
`client-config.json` will be loaded.
4. Start a web server serving this directory. `python3 -m http.server`
is an easy default that you may already have installed.
5. Open `http://localhost:8000/build/debug-emscripten-wasm32/ioquake3.html`
in a web browser. Open the developer console to see errors and warnings.
6. Debugging the C code is possible using a Chrome extension. For details
see https://developer.chrome.com/blog/wasm-debugging-2020
Installation, for *nix Installation, for *nix
1. Set the COPYDIR variable in the shell to be where you installed Quake 3 1. Set the COPYDIR variable in the shell to be where you installed Quake 3
to. By default it will be /usr/local/games/quake3 if you haven't set it. to. By default it will be /usr/local/games/quake3 if you haven't set it.
@ -115,6 +130,8 @@ The following variables may be set, either on the command line or in
Makefile.local: Makefile.local:
``` ```
DEPEND_MAKEFILE - set to 0 to disable rebuilding all targets when
the Makefile or Makefile.local is changed
CFLAGS - use this for custom CFLAGS CFLAGS - use this for custom CFLAGS
V - set to show cc command line when building V - set to show cc command line when building
DEFAULT_BASEDIR - extra path to search for baseq3 and such DEFAULT_BASEDIR - extra path to search for baseq3 and such
@ -128,6 +145,8 @@ Makefile.local:
SERVERBIN - rename 'ioq3ded' server binary SERVERBIN - rename 'ioq3ded' server binary
CLIENTBIN - rename 'ioquake3' client binary CLIENTBIN - rename 'ioquake3' client binary
USE_RENDERER_DLOPEN - build and use the renderer in a library USE_RENDERER_DLOPEN - build and use the renderer in a library
BUILD_RENDERER_OPENGL1 build the opengl1 client / renderer library
BUILD_RENDERER_OPENGL2 build the opengl2 client / renderer library
USE_YACC - use yacc to update code/tools/lcc/lburg/gram.c USE_YACC - use yacc to update code/tools/lcc/lburg/gram.c
BASEGAME - rename 'baseq3' BASEGAME - rename 'baseq3'
BASEGAME_CFLAGS - custom CFLAGS for basegame BASEGAME_CFLAGS - custom CFLAGS for basegame
@ -155,11 +174,31 @@ Makefile.local:
DEBUG_CFLAGS - C compiler flags to use for building debug version DEBUG_CFLAGS - C compiler flags to use for building debug version
COPYDIR - the target installation directory COPYDIR - the target installation directory
TEMPDIR - specify user defined directory for temp files TEMPDIR - specify user defined directory for temp files
EMSCRIPTEN_PRELOAD_FILE - set to 1 to package 'baseq3' (BASEGAME) directory
containing pk3s and loose files as a single
.data file that is loaded instead of listing
individual files in client-config.json
``` ```
The defaults for these variables differ depending on the target platform. The defaults for these variables differ depending on the target platform.
# OpenGL ES support
The opengl2 renderer (the default) supports OpenGL ES 2+. Though there
are many missing features and the performance may not be sufficient for
embedded System-on-a-Chip and mobile platforms.
The opengl1 renderer does not have OpenGL ES support.
The opengl2 renderer will try both OpenGL and OpenGL ES APIs to find one that
works. The `r_preferOpenGLES` cvar controls which API to try first.
Set it to -1 for auto (default), 0 for OpenGL, and 1 for OpenGL ES. It should be
set using command line arguments:
ioquake3 +set cl_renderer opengl2 +set r_preferOpenGLES 1
# Console # Console
## New cvars ## New cvars

View file

@ -63,6 +63,14 @@ For Win32:
CVARS CVARS
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Cvars for API:
* `r_preferOpenGLES` - This sets the preference for using OpenGL or OpenGL ES 2.
Many features are not supported when using OpenGL ES such as sun shadows and HDR.
1 - Prefer OpenGL ES 2+.
0 - Prefer desktop OpenGL.
-1 - Automatically pick (default).
Cvars for simple rendering features: Cvars for simple rendering features:
* `r_ext_compressed_textures` - Automatically compress textures. * `r_ext_compressed_textures` - Automatically compress textures.