diff --git a/CMakeLists.txt b/CMakeLists.txt index 84e260d1..df9abb6e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,6 +40,7 @@ string(REPLACE "-O3" "-O2" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}") option(ZIP_SUPPORT "ZIP support" ON) option(OGG_SUPPORT "OGG Vorbis playback support (Music)" ON) option(OPENAL_SUPPORT "OpenAL support" ON) +option(SOFTRENDERER_SUPPORT "Enabled the software renderer" ON) option(SYSTEMWIDE_SUPPORT "Enable systemwide installation of game assets" OFF) # These variables will act as our list of include folders and linker flags @@ -58,7 +59,7 @@ set(COMMON_SRC_DIR ${SOURCE_DIR}/common) set(GAME_SRC_DIR ${SOURCE_DIR}/game) set(SERVER_SRC_DIR ${SOURCE_DIR}/server) set(CLIENT_SRC_DIR ${SOURCE_DIR}/client) -set(GL_SRC_DIR ${SOURCE_DIR}/client/refresh) +set(REF_SRC_DIR ${SOURCE_DIR}/client/refresh) # Operating system set(YQ2OSTYPE "${CMAKE_SYSTEM_NAME}" CACHE STRING "Override operation system type") @@ -131,6 +132,10 @@ if(${OPENAL_SUPPORT}) endif() endif() +if (${SOFTRENDERER_SUPPORT}) + add_definitions(-DREFSOFT) +endif() + if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") list(APPEND yquake2LinkerFlags "-lm -static-libgcc") else() @@ -201,21 +206,21 @@ set(Backends-Windows-Header ${BACKENDS_SRC_DIR}/windows/header/winquake.h ) -set(GL-Windows-Source +set(REF-Windows-Source ${BACKENDS_SRC_DIR}/windows/shared/mem.c ) -set(GL-Unix-Source +set(REF-Unix-Source ${BACKENDS_SRC_DIR}/unix/shared/hunk.c ) # Set the nessesary platform specific source if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") set(Platform-Specific-Source ${Backends-Windows-Source} ${Backends-Windows-Header}) - set(GL-Platform-Specific-Source ${GL-Windows-Source}) + set(REF-Platform-Specific-Source ${REF-Windows-Source}) else() set(Platform-Specific-Source ${Backends-Unix-Source} ${Backends-Unix-Header}) - set(GL-Platform-Specific-Source ${GL-Unix-Source}) + set(REF-Platform-Specific-Source ${REF-Unix-Source}) endif() set(Game-Source @@ -427,78 +432,112 @@ set(Server-Header ) set(GL1-Source - ${GL_SRC_DIR}/gl/qgl.c - ${GL_SRC_DIR}/gl/r_draw.c - ${GL_SRC_DIR}/gl/r_image.c - ${GL_SRC_DIR}/gl/r_light.c - ${GL_SRC_DIR}/gl/r_lightmap.c - ${GL_SRC_DIR}/gl/r_main.c - ${GL_SRC_DIR}/gl/r_mesh.c - ${GL_SRC_DIR}/gl/r_misc.c - ${GL_SRC_DIR}/gl/r_model.c - ${GL_SRC_DIR}/gl/r_scrap.c - ${GL_SRC_DIR}/gl/r_surf.c - ${GL_SRC_DIR}/gl/r_warp.c - ${GL_SRC_DIR}/gl/r_sdl.c - ${GL_SRC_DIR}/gl/r_md2.c - ${GL_SRC_DIR}/gl/r_sp2.c - ${GL_SRC_DIR}/files/pcx.c - ${GL_SRC_DIR}/files/stb.c - ${GL_SRC_DIR}/files/wal.c + ${REF_SRC_DIR}/gl/qgl.c + ${REF_SRC_DIR}/gl/r_draw.c + ${REF_SRC_DIR}/gl/r_image.c + ${REF_SRC_DIR}/gl/r_light.c + ${REF_SRC_DIR}/gl/r_lightmap.c + ${REF_SRC_DIR}/gl/r_main.c + ${REF_SRC_DIR}/gl/r_mesh.c + ${REF_SRC_DIR}/gl/r_misc.c + ${REF_SRC_DIR}/gl/r_model.c + ${REF_SRC_DIR}/gl/r_scrap.c + ${REF_SRC_DIR}/gl/r_surf.c + ${REF_SRC_DIR}/gl/r_warp.c + ${REF_SRC_DIR}/gl/r_sdl.c + ${REF_SRC_DIR}/gl/r_md2.c + ${REF_SRC_DIR}/gl/r_sp2.c + ${REF_SRC_DIR}/files/pcx.c + ${REF_SRC_DIR}/files/stb.c + ${REF_SRC_DIR}/files/wal.c ${COMMON_SRC_DIR}/shared/shared.c ${COMMON_SRC_DIR}/md4.c ) set(GL1-Header - ${GL_SRC_DIR}/ref_shared.h - ${GL_SRC_DIR}/constants/anorms.h - ${GL_SRC_DIR}/constants/anormtab.h - ${GL_SRC_DIR}/constants/warpsin.h - ${GL_SRC_DIR}/files/stb_image.h - ${GL_SRC_DIR}/gl/header/local.h - ${GL_SRC_DIR}/gl/header/model.h - ${GL_SRC_DIR}/gl/header/qgl.h + ${REF_SRC_DIR}/ref_shared.h + ${REF_SRC_DIR}/constants/anorms.h + ${REF_SRC_DIR}/constants/anormtab.h + ${REF_SRC_DIR}/constants/warpsin.h + ${REF_SRC_DIR}/files/stb_image.h + ${REF_SRC_DIR}/gl/header/local.h + ${REF_SRC_DIR}/gl/header/model.h + ${REF_SRC_DIR}/gl/header/qgl.h ${COMMON_SRC_DIR}/header/shared.h ) set(GL3-Source - ${GL_SRC_DIR}/gl3/gl3_draw.c - ${GL_SRC_DIR}/gl3/gl3_image.c - ${GL_SRC_DIR}/gl3/gl3_light.c - ${GL_SRC_DIR}/gl3/gl3_lightmap.c - ${GL_SRC_DIR}/gl3/gl3_main.c - ${GL_SRC_DIR}/gl3/gl3_mesh.c - ${GL_SRC_DIR}/gl3/gl3_misc.c - ${GL_SRC_DIR}/gl3/gl3_model.c - ${GL_SRC_DIR}/gl3/gl3_sdl.c - ${GL_SRC_DIR}/gl3/gl3_surf.c - ${GL_SRC_DIR}/gl3/gl3_warp.c - ${GL_SRC_DIR}/gl3/gl3_shaders.c - ${GL_SRC_DIR}/gl3/gl3_md2.c - ${GL_SRC_DIR}/gl3/gl3_sp2.c - ${GL_SRC_DIR}/gl3/glad/src/glad.c - ${GL_SRC_DIR}/files/pcx.c - ${GL_SRC_DIR}/files/stb.c - ${GL_SRC_DIR}/files/wal.c + ${REF_SRC_DIR}/gl3/gl3_draw.c + ${REF_SRC_DIR}/gl3/gl3_image.c + ${REF_SRC_DIR}/gl3/gl3_light.c + ${REF_SRC_DIR}/gl3/gl3_lightmap.c + ${REF_SRC_DIR}/gl3/gl3_main.c + ${REF_SRC_DIR}/gl3/gl3_mesh.c + ${REF_SRC_DIR}/gl3/gl3_misc.c + ${REF_SRC_DIR}/gl3/gl3_model.c + ${REF_SRC_DIR}/gl3/gl3_sdl.c + ${REF_SRC_DIR}/gl3/gl3_surf.c + ${REF_SRC_DIR}/gl3/gl3_warp.c + ${REF_SRC_DIR}/gl3/gl3_shaders.c + ${REF_SRC_DIR}/gl3/gl3_md2.c + ${REF_SRC_DIR}/gl3/gl3_sp2.c + ${REF_SRC_DIR}/gl3/glad/src/glad.c + ${REF_SRC_DIR}/files/pcx.c + ${REF_SRC_DIR}/files/stb.c + ${REF_SRC_DIR}/files/wal.c ${COMMON_SRC_DIR}/shared/shared.c ${COMMON_SRC_DIR}/md4.c ) set(GL3-Header - ${GL_SRC_DIR}/ref_shared.h - ${GL_SRC_DIR}/constants/anorms.h - ${GL_SRC_DIR}/constants/anormtab.h - ${GL_SRC_DIR}/constants/warpsin.h - ${GL_SRC_DIR}/files/stb_image.h - ${GL_SRC_DIR}/gl3/glad/include/glad/glad.h - ${GL_SRC_DIR}/gl3/glad/include/KHR/khrplatform.h - ${GL_SRC_DIR}/gl3/header/DG_dynarr.h - ${GL_SRC_DIR}/gl3/header/HandmadeMath.h - ${GL_SRC_DIR}/gl3/header/local.h - ${GL_SRC_DIR}/gl3/header/model.h + ${REF_SRC_DIR}/ref_shared.h + ${REF_SRC_DIR}/constants/anorms.h + ${REF_SRC_DIR}/constants/anormtab.h + ${REF_SRC_DIR}/constants/warpsin.h + ${REF_SRC_DIR}/files/stb_image.h + ${REF_SRC_DIR}/gl3/glad/include/glad/glad.h + ${REF_SRC_DIR}/gl3/glad/include/KHR/khrplatform.h + ${REF_SRC_DIR}/gl3/header/DG_dynarr.h + ${REF_SRC_DIR}/gl3/header/HandmadeMath.h + ${REF_SRC_DIR}/gl3/header/local.h + ${REF_SRC_DIR}/gl3/header/model.h ${COMMON_SRC_DIR}/header/shared.h ) +if (${SOFTRENDERER_SUPPORT}) + set(SOFT-Source + ${REF_SRC_DIR}/soft/r_aclip.c + ${REF_SRC_DIR}/soft/r_alias.c + ${REF_SRC_DIR}/soft/r_bsp.c + ${REF_SRC_DIR}/soft/r_draw.c + ${REF_SRC_DIR}/soft/r_edge.c + ${REF_SRC_DIR}/soft/r_image.c + ${REF_SRC_DIR}/soft/r_light.c + ${REF_SRC_DIR}/soft/r_main.c + ${REF_SRC_DIR}/soft/r_misc.c + ${REF_SRC_DIR}/soft/r_model.c + ${REF_SRC_DIR}/soft/r_part.c + ${REF_SRC_DIR}/soft/r_poly.c + ${REF_SRC_DIR}/soft/r_polyse.c + ${REF_SRC_DIR}/soft/r_rast.c + ${REF_SRC_DIR}/soft/r_scan.c + ${REF_SRC_DIR}/soft/r_sprite.c + ${REF_SRC_DIR}/soft/r_surf.c + ${REF_SRC_DIR}/files/pcx.c + ${REF_SRC_DIR}/files/stb.c + ${REF_SRC_DIR}/files/wal.c + ${COMMON_SRC_DIR}/shared/shared.c + ${COMMON_SRC_DIR}/md4.c + ) + + set(SOFT-Header + ${REF_SRC_DIR}/soft/adivtab.h + ${REF_SRC_DIR}/soft/r_local.h + ${REF_SRC_DIR}/soft/r_model.h + ${COMMON_SRC_DIR}/header/shared.h + ) +endif () + # Main Quake 2 executable add_executable(quake2 ${Client-Source} ${Client-Header} ${Platform-Specific-Source} ${Backends-Generic-Source} ${Backends-Generic-Header}) @@ -536,7 +575,7 @@ set_target_properties(game PROPERTIES target_link_libraries(game ${yquake2LinkerFlags}) # Build the GL1 dynamic library -add_library(ref_gl1 MODULE ${GL1-Source} ${GL1-Header} ${GL-Platform-Specific-Source}) +add_library(ref_gl1 MODULE ${GL1-Source} ${GL1-Header} ${REF-Platform-Specific-Source}) set_target_properties(ref_gl1 PROPERTIES PREFIX "" LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/release @@ -546,7 +585,7 @@ set_target_properties(ref_gl1 PROPERTIES target_link_libraries(ref_gl1 ${yquake2LinkerFlags} ${yquake2OpenGLLinkerFlags} ${yquake2SDLLinkerFlags}) # Build the GL3 dynamic library -add_library(ref_gl3 MODULE ${GL3-Source} ${GL3-Header} ${GL-Platform-Specific-Source}) +add_library(ref_gl3 MODULE ${GL3-Source} ${GL3-Header} ${REF-Platform-Specific-Source}) set_target_properties(ref_gl3 PROPERTIES PREFIX "" LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/release @@ -554,3 +593,15 @@ set_target_properties(ref_gl3 PROPERTIES SUFFIX ${CMAKE_SHARED_LIBRARY_SUFFIX} ) target_link_libraries(ref_gl3 ${yquake2LinkerFlags} ${yquake2SDLLinkerFlags}) + +# Build the soft renderer dynamic library +if (${SOFTRENDERER_SUPPORT}) + add_library(ref_soft MODULE ${SOFT-Source} ${SOFT-Header} ${REF-Platform-Specific-Source}) + set_target_properties(ref_soft PROPERTIES + PREFIX "" + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/release + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/release + SUFFIX ${CMAKE_SHARED_LIBRARY_SUFFIX} + ) + target_link_libraries(ref_soft ${yquake2LinkerFlags} ${yquake2SDLLinkerFlags}) +endif() diff --git a/Makefile b/Makefile index a34985f1..6ac9d75f 100755 --- a/Makefile +++ b/Makefile @@ -27,6 +27,9 @@ # User configurable options # ------------------------- +# Build soft render +WITH_REFSOFT:=yes + # Enables CD audio playback. CD audio playback is used # for the background music and doesn't add any further # dependencies. It should work on all platforms where @@ -326,12 +329,12 @@ endif # ---------- # Phony targets -.PHONY : all client game icon server ref_gl1 ref_gl3 +.PHONY : all client game icon server ref_gl1 ref_gl3 ref_soft # ---------- # Builds everything -all: config client server game ref_gl1 ref_gl3 +all: config client server game ref_gl1 ref_gl3 ref_soft # ---------- @@ -346,6 +349,7 @@ config: @echo "WITH_ZIP = $(WITH_ZIP)" @echo "WITH_SYSTEMWIDE = $(WITH_SYSTEMWIDE)" @echo "WITH_SYSTEMDIR = $(WITH_SYSTEMDIR)" + @echo "WITH_REFSOFT = $(WITH_REFSOFT)" @echo "============================" @echo "" ifeq ($(WITH_SDL2),yes) @@ -354,7 +358,7 @@ ifeq ($(CDA_DISABLED),yes) @echo "" endif endif - + # ---------- # Special target to compile @@ -409,6 +413,10 @@ release/quake2.exe : CFLAGS += -DZIP -DNOUNCRYPT release/quake2.exe : LDFLAGS += -lz endif +ifeq ($(WITH_REFSOFT),yes) +release/quake2.exe : CFLAGS += -DREFSOFT +endif + ifeq ($(WITH_SDL2),yes) release/quake2.exe : CFLAGS += -DSDL2 endif @@ -474,6 +482,10 @@ ifeq ($(WITH_X11GAMMA),yes) release/quake2 : CFLAGS += -DX11GAMMA endif +ifeq ($(WITH_REFSOFT),yes) +release/quake2 : CFLAGS += -DREFSOFT +endif + ifeq ($(WITH_SDL2),yes) release/quake2 : CFLAGS += -DSDL2 endif @@ -651,6 +663,70 @@ build/ref_gl3/%.o: %.c # ---------- +# The soft renderer lib + +ifeq ($(YQ2_OSTYPE), Windows) + +ifeq ($(WITH_REFSOFT),yes) +ref_soft: + @echo "===> Building ref_soft.dll" + $(MAKE) release/ref_soft.dll +else +ref_soft: + @echo "===> No soft render" +endif + +ifeq ($(WITH_SDL2),yes) +release/ref_soft.dll : CFLAGS += -DSDL2 +endif + +release/ref_soft.dll : LDFLAGS += -shared + +else ifeq ($(YQ2_OSTYPE), Darwin) + +ifeq ($(WITH_REFSOFT),yes) +ref_soft: + @echo "===> Building ref_soft.dylib" + $(MAKE) release/ref_soft.dylib +else +ref_soft: + @echo "===> No soft render" +endif + + +ifeq ($(WITH_SDL2),yes) +release/ref_soft.dylib : CFLAGS += -DSDL2 +endif + +release/ref_soft.dylib : LDFLAGS += -shared + +else # not Windows or Darwin + +ifeq ($(WITH_REFSOFT),yes) +ref_soft: + @echo "===> Building ref_soft.so" + $(MAKE) release/ref_soft.so +else +ref_soft: + @echo "===> No soft render" +endif + +release/ref_soft.so : CFLAGS += -fPIC +release/ref_soft.so : LDFLAGS += -shared + +ifeq ($(WITH_SDL2),yes) +release/ref_soft.so : CFLAGS += -DSDL2 +endif + +endif # OS specific ref_soft shit + +build/ref_soft/%.o: %.c + @echo "===> CC $<" + ${Q}mkdir -p $(@D) + ${Q}$(CC) -c $(CFLAGS) $(SDLCFLAGS) $(INCLUDE) $(GLAD_INCLUDE) -o $@ $< + +# ---------- + # The baseq2 game ifeq ($(YQ2_OSTYPE), Windows) game: @@ -809,7 +885,7 @@ CLIENT_OBJS_ := \ src/server/sv_save.o \ src/server/sv_send.o \ src/server/sv_user.o \ - src/server/sv_world.o + src/server/sv_world.o ifeq ($(YQ2_OSTYPE), Windows) CLIENT_OBJS_ += \ @@ -848,7 +924,7 @@ REFGL1_OBJS_ := \ src/client/refresh/files/wal.o \ src/common/shared/shared.o \ src/common/md4.o - + ifeq ($(YQ2_OSTYPE), Windows) REFGL1_OBJS_ += \ src/backends/windows/shared/mem.o @@ -891,6 +967,40 @@ endif # ---------- +REFSOFT_OBJS_ := \ + src/client/refresh/soft/r_aclip.o \ + src/client/refresh/soft/r_alias.o \ + src/client/refresh/soft/r_bsp.o \ + src/client/refresh/soft/r_draw.o \ + src/client/refresh/soft/r_edge.o \ + src/client/refresh/soft/r_image.o \ + src/client/refresh/soft/r_light.o \ + src/client/refresh/soft/r_main.o \ + src/client/refresh/soft/r_misc.o \ + src/client/refresh/soft/r_model.o \ + src/client/refresh/soft/r_part.o \ + src/client/refresh/soft/r_poly.o \ + src/client/refresh/soft/r_polyse.o \ + src/client/refresh/soft/r_rast.o \ + src/client/refresh/soft/r_scan.o \ + src/client/refresh/soft/r_sprite.o \ + src/client/refresh/soft/r_surf.o \ + src/client/refresh/files/pcx.o \ + src/client/refresh/files/stb.o \ + src/client/refresh/files/wal.o \ + src/common/shared/shared.o \ + src/common/md4.o + +ifeq ($(YQ2_OSTYPE), Windows) +REFSOFT_OBJS_ += \ + src/backends/windows/shared/mem.o +else # not Windows +REFSOFT_OBJS_ += \ + src/backends/unix/shared/hunk.o +endif + +# ---------- + # Used by the server SERVER_OBJS_ := \ src/common/argproc.o \ @@ -944,6 +1054,7 @@ endif CLIENT_OBJS = $(patsubst %,build/client/%,$(CLIENT_OBJS_)) REFGL1_OBJS = $(patsubst %,build/ref_gl1/%,$(REFGL1_OBJS_)) REFGL3_OBJS = $(patsubst %,build/ref_gl3/%,$(REFGL3_OBJS_)) +REFSOFT_OBJS = $(patsubst %,build/ref_soft/%,$(REFSOFT_OBJS_)) SERVER_OBJS = $(patsubst %,build/server/%,$(SERVER_OBJS_)) GAME_OBJS = $(patsubst %,build/baseq2/%,$(GAME_OBJS_)) @@ -953,6 +1064,7 @@ GAME_OBJS = $(patsubst %,build/baseq2/%,$(GAME_OBJS_)) CLIENT_DEPS= $(CLIENT_OBJS:.o=.d) REFGL1_DEPS= $(REFGL1_OBJS:.o=.d) REFGL3_DEPS= $(REFGL3_OBJS:.o=.d) +REFSOFT_DEPS= $(REFSOFT_OBJS:.o=.d) SERVER_DEPS= $(SERVER_OBJS:.o=.d) GAME_DEPS= $(GAME_OBJS:.o=.d) @@ -1023,6 +1135,22 @@ release/ref_gl3.so : $(REFGL3_OBJS) ${Q}$(CC) $(REFGL3_OBJS) $(LDFLAGS) $(SDLLDFLAGS) -o $@ endif +# release/ref_soft.so +ifeq ($(YQ2_OSTYPE), Windows) +release/ref_soft.dll : $(REFSOFT_OBJS) + @echo "===> LD $@" + ${Q}$(CC) $(REFSOFT_OBJS) $(LDFLAGS) $(DLL_SDLLDFLAGS) -o $@ + $(Q)strip $@ +else ifeq ($(YQ2_OSTYPE), Darwin) +release/ref_soft.dylib : $(REFSOFT_OBJS) + @echo "===> LD $@" + ${Q}$(CC) $(REFSOFT_OBJS) $(LDFLAGS) $(SDLLDFLAGS) -o $@ +else +release/ref_soft.so : $(REFSOFT_OBJS) + @echo "===> LD $@" + ${Q}$(CC) $(REFSOFT_OBJS) $(LDFLAGS) $(SDLLDFLAGS) -o $@ +endif + # release/baseq2/game.so ifeq ($(YQ2_OSTYPE), Windows) release/baseq2/game.dll : $(GAME_OBJS) diff --git a/src/backends/sdl/sound.c b/src/backends/sdl/sound.c index 9b3c6a4c..4bf01383 100644 --- a/src/backends/sdl/sound.c +++ b/src/backends/sdl/sound.c @@ -811,7 +811,7 @@ SDL_ClearBuffer(void) int i; unsigned char *ptr = sound.buffer; - if (!sound_started) + if (sound_started == SS_NOT) { return; } @@ -1460,7 +1460,7 @@ SDL_BackendInit(void) soundtime = 0; snd_inited = 1; - + return 1; } diff --git a/src/backends/unix/shared/hunk.c b/src/backends/unix/shared/hunk.c index 64dba68d..6c9a335a 100644 --- a/src/backends/unix/shared/hunk.c +++ b/src/backends/unix/shared/hunk.c @@ -160,10 +160,10 @@ Hunk_End(void) void Hunk_Free(void *base) { - byte *m; - if (base) { + byte *m; + m = ((byte *)base) - sizeof(int); if (munmap(m, *((int *)m))) diff --git a/src/backends/unix/system.c b/src/backends/unix/system.c index 1b6409a0..524bb949 100644 --- a/src/backends/unix/system.c +++ b/src/backends/unix/system.c @@ -117,7 +117,7 @@ Sys_Microseconds(void) long long sec = now.tv_sec; // set back first by 1ms so neither this function nor Sys_Milliseconds() // (which calls this) will ever return 0 - nsec -= 1000000; + nsec -= 1000000; if(nsec < 0) { nsec += 1000000000ll; // 1s in ns => definitely positive now @@ -411,7 +411,6 @@ Sys_GetGameAPI(void *parms) { void *(*GetGameAPI)(void *); - FILE *fp; char name[MAX_OSPATH]; char *path; char *str_p; @@ -436,6 +435,8 @@ Sys_GetGameAPI(void *parms) while (1) { + FILE *fp; + path = FS_NextPath(path); if (!path) diff --git a/src/client/cl_cin.c b/src/client/cl_cin.c index 62644e49..7e0bac1a 100644 --- a/src/client/cl_cin.c +++ b/src/client/cl_cin.c @@ -505,6 +505,31 @@ SCR_RunCinematic(void) } } +static int +SCR_MinimalColor(void) +{ + int i, min_color, min_index; + + min_color = 255 * 3; + min_index = 0; + + for(i=0; i<255; i++) + { + int current_color = (cl.cinematicpalette[i*3+0] + + cl.cinematicpalette[i*3+1] + + cl.cinematicpalette[i*3+2]); + + if (min_color > current_color) + { + min_color = current_color; + min_index = i; + } + } + + return min_index; +} + + /* * Returns true if a cinematic is active, meaning the * view rendering should be skipped @@ -512,7 +537,7 @@ SCR_RunCinematic(void) qboolean SCR_DrawCinematic(void) { - int x, y, w, h; + int x, y, w, h, color; if (cl.cinematictime <= 0) { @@ -557,21 +582,23 @@ SCR_DrawCinematic(void) h = viddef.height; } + color = SCR_MinimalColor(); + if (x > 0) { - Draw_Fill(0, 0, x, viddef.height, 0); + Draw_Fill(0, 0, x, viddef.height, color); } if (x + w < viddef.width) { - Draw_Fill(x + w, 0, viddef.width - (x + w), viddef.height, 0); + Draw_Fill(x + w, 0, viddef.width - (x + w), viddef.height, color); } if (y > 0) { - Draw_Fill(x, 0, w, y, 0); + Draw_Fill(x, 0, w, y, color); } if (y + h < viddef.height) { - Draw_Fill(x, y + h, w, viddef.height - (y + h), 0); + Draw_Fill(x, y + h, w, viddef.height - (y + h), color); } Draw_StretchRaw(x, y, w, h, cin.width, cin.height, cin.pic); diff --git a/src/client/menu/menu.c b/src/client/menu/menu.c index 93336f17..dfb201b8 100644 --- a/src/client/menu/menu.c +++ b/src/client/menu/menu.c @@ -1650,6 +1650,7 @@ static const char *idcredits[] = { "Yamagi Burmeister", "Daniel Gibson", "Sander van Dijk", + "Denis Pauk", "", "Quake II(tm) (C)1997 Id Software, Inc.", "All Rights Reserved. Distributed by", diff --git a/src/client/menu/videomenu.c b/src/client/menu/videomenu.c index 8511907a..9d31804b 100644 --- a/src/client/menu/videomenu.c +++ b/src/client/menu/videomenu.c @@ -32,6 +32,9 @@ extern void M_ForceMenuOff(void); static cvar_t *gl_mode; +#ifdef REFSOFT +static cvar_t *sw_mode; +#endif static cvar_t *gl_hudscale; static cvar_t *gl_consolescale; static cvar_t *gl_menuscale; @@ -73,10 +76,21 @@ GetRenderer(void) { return 1; } +#ifdef REFSOFT + else if (Q_stricmp(vid_renderer->string, "soft") == 0) + { + return 2; + } + else + { + return 3; + } +#else else { return 2; } +#endif } static int @@ -157,8 +171,31 @@ ApplyChanges(void *unused) Cvar_Set("vid_renderer", "gl3"); restart = true; } +#ifdef REFSOFT + else if (s_renderer_list.curvalue == 2) + { + Cvar_Set("vid_renderer", "soft"); + restart = true; + } +#endif } +#ifdef REFSOFT + if (s_renderer_list.curvalue == 2) { + /* custom mode */ + if (s_mode_list.curvalue != GetCustomValue(&s_mode_list)) + { + /* Restarts automatically */ + Cvar_SetValue("sw_mode", s_mode_list.curvalue); + } + else + { + /* Restarts automatically */ + Cvar_SetValue("sw_mode", -1); + } + } + else +#endif /* custom mode */ if (s_mode_list.curvalue != GetCustomValue(&s_mode_list)) { @@ -224,6 +261,20 @@ ApplyChanges(void *unused) M_ForceMenuOff(); } +#ifdef REFSOFT +static void DriverCallback( void *unused ) +{ + if (s_renderer_list.curvalue == 2) + { + s_mode_list.curvalue = sw_mode->value; + } + else + { + s_mode_list.curvalue = gl_mode->value; + } +} +#endif + void VID_MenuInit(void) { @@ -232,6 +283,9 @@ VID_MenuInit(void) static const char *renderers[] = { "[OpenGL 1.4]", "[OpenGL 3.2]", +#ifdef REFSOFT + "[Software ]", +#endif "[Custom ]", 0 }; @@ -261,14 +315,14 @@ VID_MenuInit(void) "[1920 1080 ]", "[1920 1200 ]", "[2048 1536 ]", - "[2560x1080 ]", - "[2560x1440 ]", - "[2560x1600 ]", - "[3440x1440 ]", - "[3840x1600 ]", - "[3840x2160 ]", - "[4096x2160 ]", - "[5120x2880 ]", + "[2560 1080 ]", + "[2560 1440 ]", + "[2560 1600 ]", + "[3440 1440 ]", + "[3840 1600 ]", + "[3840 2160 ]", + "[4096 2160 ]", + "[5120 2880 ]", "[custom ]", 0 }; @@ -312,6 +366,13 @@ VID_MenuInit(void) gl_mode = Cvar_Get("gl_mode", "4", 0); } +#ifdef REFSOFT + if (!sw_mode) + { + sw_mode = Cvar_Get("sw_mode", "4", 0); + } +#endif + if (!gl_hudscale) { gl_hudscale = Cvar_Get("gl_hudscale", "-1", CVAR_ARCHIVE); @@ -371,12 +432,23 @@ VID_MenuInit(void) s_renderer_list.generic.y = (y = 0); s_renderer_list.itemnames = renderers; s_renderer_list.curvalue = GetRenderer(); +#ifdef REFSOFT + s_renderer_list.generic.callback = DriverCallback; +#endif s_mode_list.generic.type = MTYPE_SPINCONTROL; s_mode_list.generic.name = "video mode"; s_mode_list.generic.x = 0; s_mode_list.generic.y = (y += 10); s_mode_list.itemnames = resolutions; + +#ifdef REFSOFT + if (s_renderer_list.curvalue == 2 && sw_mode->value >= 0) + { + s_mode_list.curvalue = sw_mode->value; + } + else +#endif if (gl_mode->value >= 0) { s_mode_list.curvalue = gl_mode->value; diff --git a/src/client/refresh/files/pcx.c b/src/client/refresh/files/pcx.c index 926e0ea3..11282479 100644 --- a/src/client/refresh/files/pcx.c +++ b/src/client/refresh/files/pcx.c @@ -175,11 +175,18 @@ LoadPCX(char *origname, byte **pic, byte **palette, int *width, int *height) (pcx_width >= 4096) || (pcx_height >= 4096)) { R_Printf(PRINT_ALL, "Bad pcx file %s\n", filename); + ri.FS_FreeFile(pcx); return; } full_size = (pcx_height + 1) * (pcx_width + 1); out = malloc(full_size); + if (!out) + { + R_Printf(PRINT_ALL, "Can't allocate\n"); + ri.FS_FreeFile(pcx); + return; + } *pic = out; @@ -188,6 +195,13 @@ LoadPCX(char *origname, byte **pic, byte **palette, int *width, int *height) if (palette) { *palette = malloc(768); + if (!(*palette)) + { + R_Printf(PRINT_ALL, "Can't allocate\n"); + free(out); + ri.FS_FreeFile(pcx); + return; + } if (len > 768) { memcpy(*palette, (byte *)pcx + len - 768, 768); diff --git a/src/client/refresh/gl/r_light.c b/src/client/refresh/gl/r_light.c index 16d45b3e..7d977b8f 100644 --- a/src/client/refresh/gl/r_light.c +++ b/src/client/refresh/gl/r_light.c @@ -299,17 +299,19 @@ R_RecursiveLightPoint(mnode_t *node, vec3_t start, vec3_t end) if (lightmap) { - vec3_t scale; + vec3_t scale; lightmap += 3 * (dt * ((surf->extents[0] >> 4) + 1) + ds); for (maps = 0; maps < MAXLIGHTMAPS && surf->styles[maps] != 255; maps++) { - for (i = 0; i < 3; i++) + int j; + + for (j = 0; j < 3; j++) { - scale[i] = gl_modulate->value * - r_newrefdef.lightstyles[surf->styles[maps]].rgb[i]; + scale[j] = gl_modulate->value * + r_newrefdef.lightstyles[surf->styles[maps]].rgb[j]; } pointcolor[0] += lightmap[0] * scale[0] * (1.0 / 255); diff --git a/src/client/refresh/soft/adivtab.h b/src/client/refresh/soft/adivtab.h new file mode 100644 index 00000000..9b7be2ce --- /dev/null +++ b/src/client/refresh/soft/adivtab.h @@ -0,0 +1,1077 @@ +/* +Copyright (C) 1997-2001 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +// table of quotients and remainders for [-15...16] / [-15...16] + +// numerator = -15 +{1, 0}, +{1, -1}, +{1, -2}, +{1, -3}, +{1, -4}, +{1, -5}, +{1, -6}, +{1, -7}, +{2, -1}, +{2, -3}, +{3, 0}, +{3, -3}, +{5, 0}, +{7, -1}, +{15, 0}, +{0, 0}, +{-15, 0}, +{-8, 1}, +{-5, 0}, +{-4, 1}, +{-3, 0}, +{-3, 3}, +{-3, 6}, +{-2, 1}, +{-2, 3}, +{-2, 5}, +{-2, 7}, +{-2, 9}, +{-2, 11}, +{-2, 13}, +{-1, 0}, +{-1, 1}, +// numerator = -14 +{0, -14}, +{1, 0}, +{1, -1}, +{1, -2}, +{1, -3}, +{1, -4}, +{1, -5}, +{1, -6}, +{2, 0}, +{2, -2}, +{2, -4}, +{3, -2}, +{4, -2}, +{7, 0}, +{14, 0}, +{0, 0}, +{-14, 0}, +{-7, 0}, +{-5, 1}, +{-4, 2}, +{-3, 1}, +{-3, 4}, +{-2, 0}, +{-2, 2}, +{-2, 4}, +{-2, 6}, +{-2, 8}, +{-2, 10}, +{-2, 12}, +{-1, 0}, +{-1, 1}, +{-1, 2}, +// numerator = -13 +{0, -13}, +{0, -13}, +{1, 0}, +{1, -1}, +{1, -2}, +{1, -3}, +{1, -4}, +{1, -5}, +{1, -6}, +{2, -1}, +{2, -3}, +{3, -1}, +{4, -1}, +{6, -1}, +{13, 0}, +{0, 0}, +{-13, 0}, +{-7, 1}, +{-5, 2}, +{-4, 3}, +{-3, 2}, +{-3, 5}, +{-2, 1}, +{-2, 3}, +{-2, 5}, +{-2, 7}, +{-2, 9}, +{-2, 11}, +{-1, 0}, +{-1, 1}, +{-1, 2}, +{-1, 3}, +// numerator = -12 +{0, -12}, +{0, -12}, +{0, -12}, +{1, 0}, +{1, -1}, +{1, -2}, +{1, -3}, +{1, -4}, +{1, -5}, +{2, 0}, +{2, -2}, +{3, 0}, +{4, 0}, +{6, 0}, +{12, 0}, +{0, 0}, +{-12, 0}, +{-6, 0}, +{-4, 0}, +{-3, 0}, +{-3, 3}, +{-2, 0}, +{-2, 2}, +{-2, 4}, +{-2, 6}, +{-2, 8}, +{-2, 10}, +{-1, 0}, +{-1, 1}, +{-1, 2}, +{-1, 3}, +{-1, 4}, +// numerator = -11 +{0, -11}, +{0, -11}, +{0, -11}, +{0, -11}, +{1, 0}, +{1, -1}, +{1, -2}, +{1, -3}, +{1, -4}, +{1, -5}, +{2, -1}, +{2, -3}, +{3, -2}, +{5, -1}, +{11, 0}, +{0, 0}, +{-11, 0}, +{-6, 1}, +{-4, 1}, +{-3, 1}, +{-3, 4}, +{-2, 1}, +{-2, 3}, +{-2, 5}, +{-2, 7}, +{-2, 9}, +{-1, 0}, +{-1, 1}, +{-1, 2}, +{-1, 3}, +{-1, 4}, +{-1, 5}, +// numerator = -10 +{0, -10}, +{0, -10}, +{0, -10}, +{0, -10}, +{0, -10}, +{1, 0}, +{1, -1}, +{1, -2}, +{1, -3}, +{1, -4}, +{2, 0}, +{2, -2}, +{3, -1}, +{5, 0}, +{10, 0}, +{0, 0}, +{-10, 0}, +{-5, 0}, +{-4, 2}, +{-3, 2}, +{-2, 0}, +{-2, 2}, +{-2, 4}, +{-2, 6}, +{-2, 8}, +{-1, 0}, +{-1, 1}, +{-1, 2}, +{-1, 3}, +{-1, 4}, +{-1, 5}, +{-1, 6}, +// numerator = -9 +{0, -9}, +{0, -9}, +{0, -9}, +{0, -9}, +{0, -9}, +{0, -9}, +{1, 0}, +{1, -1}, +{1, -2}, +{1, -3}, +{1, -4}, +{2, -1}, +{3, 0}, +{4, -1}, +{9, 0}, +{0, 0}, +{-9, 0}, +{-5, 1}, +{-3, 0}, +{-3, 3}, +{-2, 1}, +{-2, 3}, +{-2, 5}, +{-2, 7}, +{-1, 0}, +{-1, 1}, +{-1, 2}, +{-1, 3}, +{-1, 4}, +{-1, 5}, +{-1, 6}, +{-1, 7}, +// numerator = -8 +{0, -8}, +{0, -8}, +{0, -8}, +{0, -8}, +{0, -8}, +{0, -8}, +{0, -8}, +{1, 0}, +{1, -1}, +{1, -2}, +{1, -3}, +{2, 0}, +{2, -2}, +{4, 0}, +{8, 0}, +{0, 0}, +{-8, 0}, +{-4, 0}, +{-3, 1}, +{-2, 0}, +{-2, 2}, +{-2, 4}, +{-2, 6}, +{-1, 0}, +{-1, 1}, +{-1, 2}, +{-1, 3}, +{-1, 4}, +{-1, 5}, +{-1, 6}, +{-1, 7}, +{-1, 8}, +// numerator = -7 +{0, -7}, +{0, -7}, +{0, -7}, +{0, -7}, +{0, -7}, +{0, -7}, +{0, -7}, +{0, -7}, +{1, 0}, +{1, -1}, +{1, -2}, +{1, -3}, +{2, -1}, +{3, -1}, +{7, 0}, +{0, 0}, +{-7, 0}, +{-4, 1}, +{-3, 2}, +{-2, 1}, +{-2, 3}, +{-2, 5}, +{-1, 0}, +{-1, 1}, +{-1, 2}, +{-1, 3}, +{-1, 4}, +{-1, 5}, +{-1, 6}, +{-1, 7}, +{-1, 8}, +{-1, 9}, +// numerator = -6 +{0, -6}, +{0, -6}, +{0, -6}, +{0, -6}, +{0, -6}, +{0, -6}, +{0, -6}, +{0, -6}, +{0, -6}, +{1, 0}, +{1, -1}, +{1, -2}, +{2, 0}, +{3, 0}, +{6, 0}, +{0, 0}, +{-6, 0}, +{-3, 0}, +{-2, 0}, +{-2, 2}, +{-2, 4}, +{-1, 0}, +{-1, 1}, +{-1, 2}, +{-1, 3}, +{-1, 4}, +{-1, 5}, +{-1, 6}, +{-1, 7}, +{-1, 8}, +{-1, 9}, +{-1, 10}, +// numerator = -5 +{0, -5}, +{0, -5}, +{0, -5}, +{0, -5}, +{0, -5}, +{0, -5}, +{0, -5}, +{0, -5}, +{0, -5}, +{0, -5}, +{1, 0}, +{1, -1}, +{1, -2}, +{2, -1}, +{5, 0}, +{0, 0}, +{-5, 0}, +{-3, 1}, +{-2, 1}, +{-2, 3}, +{-1, 0}, +{-1, 1}, +{-1, 2}, +{-1, 3}, +{-1, 4}, +{-1, 5}, +{-1, 6}, +{-1, 7}, +{-1, 8}, +{-1, 9}, +{-1, 10}, +{-1, 11}, +// numerator = -4 +{0, -4}, +{0, -4}, +{0, -4}, +{0, -4}, +{0, -4}, +{0, -4}, +{0, -4}, +{0, -4}, +{0, -4}, +{0, -4}, +{0, -4}, +{1, 0}, +{1, -1}, +{2, 0}, +{4, 0}, +{0, 0}, +{-4, 0}, +{-2, 0}, +{-2, 2}, +{-1, 0}, +{-1, 1}, +{-1, 2}, +{-1, 3}, +{-1, 4}, +{-1, 5}, +{-1, 6}, +{-1, 7}, +{-1, 8}, +{-1, 9}, +{-1, 10}, +{-1, 11}, +{-1, 12}, +// numerator = -3 +{0, -3}, +{0, -3}, +{0, -3}, +{0, -3}, +{0, -3}, +{0, -3}, +{0, -3}, +{0, -3}, +{0, -3}, +{0, -3}, +{0, -3}, +{0, -3}, +{1, 0}, +{1, -1}, +{3, 0}, +{0, 0}, +{-3, 0}, +{-2, 1}, +{-1, 0}, +{-1, 1}, +{-1, 2}, +{-1, 3}, +{-1, 4}, +{-1, 5}, +{-1, 6}, +{-1, 7}, +{-1, 8}, +{-1, 9}, +{-1, 10}, +{-1, 11}, +{-1, 12}, +{-1, 13}, +// numerator = -2 +{0, -2}, +{0, -2}, +{0, -2}, +{0, -2}, +{0, -2}, +{0, -2}, +{0, -2}, +{0, -2}, +{0, -2}, +{0, -2}, +{0, -2}, +{0, -2}, +{0, -2}, +{1, 0}, +{2, 0}, +{0, 0}, +{-2, 0}, +{-1, 0}, +{-1, 1}, +{-1, 2}, +{-1, 3}, +{-1, 4}, +{-1, 5}, +{-1, 6}, +{-1, 7}, +{-1, 8}, +{-1, 9}, +{-1, 10}, +{-1, 11}, +{-1, 12}, +{-1, 13}, +{-1, 14}, +// numerator = -1 +{0, -1}, +{0, -1}, +{0, -1}, +{0, -1}, +{0, -1}, +{0, -1}, +{0, -1}, +{0, -1}, +{0, -1}, +{0, -1}, +{0, -1}, +{0, -1}, +{0, -1}, +{0, -1}, +{1, 0}, +{0, 0}, +{-1, 0}, +{-1, 1}, +{-1, 2}, +{-1, 3}, +{-1, 4}, +{-1, 5}, +{-1, 6}, +{-1, 7}, +{-1, 8}, +{-1, 9}, +{-1, 10}, +{-1, 11}, +{-1, 12}, +{-1, 13}, +{-1, 14}, +{-1, 15}, +// numerator = 0 +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +// numerator = 1 +{-1, -14}, +{-1, -13}, +{-1, -12}, +{-1, -11}, +{-1, -10}, +{-1, -9}, +{-1, -8}, +{-1, -7}, +{-1, -6}, +{-1, -5}, +{-1, -4}, +{-1, -3}, +{-1, -2}, +{-1, -1}, +{-1, 0}, +{0, 0}, +{1, 0}, +{0, 1}, +{0, 1}, +{0, 1}, +{0, 1}, +{0, 1}, +{0, 1}, +{0, 1}, +{0, 1}, +{0, 1}, +{0, 1}, +{0, 1}, +{0, 1}, +{0, 1}, +{0, 1}, +{0, 1}, +// numerator = 2 +{-1, -13}, +{-1, -12}, +{-1, -11}, +{-1, -10}, +{-1, -9}, +{-1, -8}, +{-1, -7}, +{-1, -6}, +{-1, -5}, +{-1, -4}, +{-1, -3}, +{-1, -2}, +{-1, -1}, +{-1, 0}, +{-2, 0}, +{0, 0}, +{2, 0}, +{1, 0}, +{0, 2}, +{0, 2}, +{0, 2}, +{0, 2}, +{0, 2}, +{0, 2}, +{0, 2}, +{0, 2}, +{0, 2}, +{0, 2}, +{0, 2}, +{0, 2}, +{0, 2}, +{0, 2}, +// numerator = 3 +{-1, -12}, +{-1, -11}, +{-1, -10}, +{-1, -9}, +{-1, -8}, +{-1, -7}, +{-1, -6}, +{-1, -5}, +{-1, -4}, +{-1, -3}, +{-1, -2}, +{-1, -1}, +{-1, 0}, +{-2, -1}, +{-3, 0}, +{0, 0}, +{3, 0}, +{1, 1}, +{1, 0}, +{0, 3}, +{0, 3}, +{0, 3}, +{0, 3}, +{0, 3}, +{0, 3}, +{0, 3}, +{0, 3}, +{0, 3}, +{0, 3}, +{0, 3}, +{0, 3}, +{0, 3}, +// numerator = 4 +{-1, -11}, +{-1, -10}, +{-1, -9}, +{-1, -8}, +{-1, -7}, +{-1, -6}, +{-1, -5}, +{-1, -4}, +{-1, -3}, +{-1, -2}, +{-1, -1}, +{-1, 0}, +{-2, -2}, +{-2, 0}, +{-4, 0}, +{0, 0}, +{4, 0}, +{2, 0}, +{1, 1}, +{1, 0}, +{0, 4}, +{0, 4}, +{0, 4}, +{0, 4}, +{0, 4}, +{0, 4}, +{0, 4}, +{0, 4}, +{0, 4}, +{0, 4}, +{0, 4}, +{0, 4}, +// numerator = 5 +{-1, -10}, +{-1, -9}, +{-1, -8}, +{-1, -7}, +{-1, -6}, +{-1, -5}, +{-1, -4}, +{-1, -3}, +{-1, -2}, +{-1, -1}, +{-1, 0}, +{-2, -3}, +{-2, -1}, +{-3, -1}, +{-5, 0}, +{0, 0}, +{5, 0}, +{2, 1}, +{1, 2}, +{1, 1}, +{1, 0}, +{0, 5}, +{0, 5}, +{0, 5}, +{0, 5}, +{0, 5}, +{0, 5}, +{0, 5}, +{0, 5}, +{0, 5}, +{0, 5}, +{0, 5}, +// numerator = 6 +{-1, -9}, +{-1, -8}, +{-1, -7}, +{-1, -6}, +{-1, -5}, +{-1, -4}, +{-1, -3}, +{-1, -2}, +{-1, -1}, +{-1, 0}, +{-2, -4}, +{-2, -2}, +{-2, 0}, +{-3, 0}, +{-6, 0}, +{0, 0}, +{6, 0}, +{3, 0}, +{2, 0}, +{1, 2}, +{1, 1}, +{1, 0}, +{0, 6}, +{0, 6}, +{0, 6}, +{0, 6}, +{0, 6}, +{0, 6}, +{0, 6}, +{0, 6}, +{0, 6}, +{0, 6}, +// numerator = 7 +{-1, -8}, +{-1, -7}, +{-1, -6}, +{-1, -5}, +{-1, -4}, +{-1, -3}, +{-1, -2}, +{-1, -1}, +{-1, 0}, +{-2, -5}, +{-2, -3}, +{-2, -1}, +{-3, -2}, +{-4, -1}, +{-7, 0}, +{0, 0}, +{7, 0}, +{3, 1}, +{2, 1}, +{1, 3}, +{1, 2}, +{1, 1}, +{1, 0}, +{0, 7}, +{0, 7}, +{0, 7}, +{0, 7}, +{0, 7}, +{0, 7}, +{0, 7}, +{0, 7}, +{0, 7}, +// numerator = 8 +{-1, -7}, +{-1, -6}, +{-1, -5}, +{-1, -4}, +{-1, -3}, +{-1, -2}, +{-1, -1}, +{-1, 0}, +{-2, -6}, +{-2, -4}, +{-2, -2}, +{-2, 0}, +{-3, -1}, +{-4, 0}, +{-8, 0}, +{0, 0}, +{8, 0}, +{4, 0}, +{2, 2}, +{2, 0}, +{1, 3}, +{1, 2}, +{1, 1}, +{1, 0}, +{0, 8}, +{0, 8}, +{0, 8}, +{0, 8}, +{0, 8}, +{0, 8}, +{0, 8}, +{0, 8}, +// numerator = 9 +{-1, -6}, +{-1, -5}, +{-1, -4}, +{-1, -3}, +{-1, -2}, +{-1, -1}, +{-1, 0}, +{-2, -7}, +{-2, -5}, +{-2, -3}, +{-2, -1}, +{-3, -3}, +{-3, 0}, +{-5, -1}, +{-9, 0}, +{0, 0}, +{9, 0}, +{4, 1}, +{3, 0}, +{2, 1}, +{1, 4}, +{1, 3}, +{1, 2}, +{1, 1}, +{1, 0}, +{0, 9}, +{0, 9}, +{0, 9}, +{0, 9}, +{0, 9}, +{0, 9}, +{0, 9}, +// numerator = 10 +{-1, -5}, +{-1, -4}, +{-1, -3}, +{-1, -2}, +{-1, -1}, +{-1, 0}, +{-2, -8}, +{-2, -6}, +{-2, -4}, +{-2, -2}, +{-2, 0}, +{-3, -2}, +{-4, -2}, +{-5, 0}, +{-10, 0}, +{0, 0}, +{10, 0}, +{5, 0}, +{3, 1}, +{2, 2}, +{2, 0}, +{1, 4}, +{1, 3}, +{1, 2}, +{1, 1}, +{1, 0}, +{0, 10}, +{0, 10}, +{0, 10}, +{0, 10}, +{0, 10}, +{0, 10}, +// numerator = 11 +{-1, -4}, +{-1, -3}, +{-1, -2}, +{-1, -1}, +{-1, 0}, +{-2, -9}, +{-2, -7}, +{-2, -5}, +{-2, -3}, +{-2, -1}, +{-3, -4}, +{-3, -1}, +{-4, -1}, +{-6, -1}, +{-11, 0}, +{0, 0}, +{11, 0}, +{5, 1}, +{3, 2}, +{2, 3}, +{2, 1}, +{1, 5}, +{1, 4}, +{1, 3}, +{1, 2}, +{1, 1}, +{1, 0}, +{0, 11}, +{0, 11}, +{0, 11}, +{0, 11}, +{0, 11}, +// numerator = 12 +{-1, -3}, +{-1, -2}, +{-1, -1}, +{-1, 0}, +{-2, -10}, +{-2, -8}, +{-2, -6}, +{-2, -4}, +{-2, -2}, +{-2, 0}, +{-3, -3}, +{-3, 0}, +{-4, 0}, +{-6, 0}, +{-12, 0}, +{0, 0}, +{12, 0}, +{6, 0}, +{4, 0}, +{3, 0}, +{2, 2}, +{2, 0}, +{1, 5}, +{1, 4}, +{1, 3}, +{1, 2}, +{1, 1}, +{1, 0}, +{0, 12}, +{0, 12}, +{0, 12}, +{0, 12}, +// numerator = 13 +{-1, -2}, +{-1, -1}, +{-1, 0}, +{-2, -11}, +{-2, -9}, +{-2, -7}, +{-2, -5}, +{-2, -3}, +{-2, -1}, +{-3, -5}, +{-3, -2}, +{-4, -3}, +{-5, -2}, +{-7, -1}, +{-13, 0}, +{0, 0}, +{13, 0}, +{6, 1}, +{4, 1}, +{3, 1}, +{2, 3}, +{2, 1}, +{1, 6}, +{1, 5}, +{1, 4}, +{1, 3}, +{1, 2}, +{1, 1}, +{1, 0}, +{0, 13}, +{0, 13}, +{0, 13}, +// numerator = 14 +{-1, -1}, +{-1, 0}, +{-2, -12}, +{-2, -10}, +{-2, -8}, +{-2, -6}, +{-2, -4}, +{-2, -2}, +{-2, 0}, +{-3, -4}, +{-3, -1}, +{-4, -2}, +{-5, -1}, +{-7, 0}, +{-14, 0}, +{0, 0}, +{14, 0}, +{7, 0}, +{4, 2}, +{3, 2}, +{2, 4}, +{2, 2}, +{2, 0}, +{1, 6}, +{1, 5}, +{1, 4}, +{1, 3}, +{1, 2}, +{1, 1}, +{1, 0}, +{0, 14}, +{0, 14}, +// numerator = 15 +{-1, 0}, +{-2, -13}, +{-2, -11}, +{-2, -9}, +{-2, -7}, +{-2, -5}, +{-2, -3}, +{-2, -1}, +{-3, -6}, +{-3, -3}, +{-3, 0}, +{-4, -1}, +{-5, 0}, +{-8, -1}, +{-15, 0}, +{0, 0}, +{15, 0}, +{7, 1}, +{5, 0}, +{3, 3}, +{3, 0}, +{2, 3}, +{2, 1}, +{1, 7}, +{1, 6}, +{1, 5}, +{1, 4}, +{1, 3}, +{1, 2}, +{1, 1}, +{1, 0}, +{0, 15}, +// numerator = 16 +{-2, -14}, +{-2, -12}, +{-2, -10}, +{-2, -8}, +{-2, -6}, +{-2, -4}, +{-2, -2}, +{-2, 0}, +{-3, -5}, +{-3, -2}, +{-4, -4}, +{-4, 0}, +{-6, -2}, +{-8, 0}, +{-16, 0}, +{0, 0}, +{16, 0}, +{8, 0}, +{5, 1}, +{4, 0}, +{3, 1}, +{2, 4}, +{2, 2}, +{2, 0}, +{1, 7}, +{1, 6}, +{1, 5}, +{1, 4}, +{1, 3}, +{1, 2}, +{1, 1}, +{1, 0}, diff --git a/src/client/refresh/soft/r_aclip.c b/src/client/refresh/soft/r_aclip.c new file mode 100644 index 00000000..619bcef3 --- /dev/null +++ b/src/client/refresh/soft/r_aclip.c @@ -0,0 +1,317 @@ +/* +Copyright (C) 1997-2001 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +// r_aclip.c: clip routines for drawing Alias models directly to the screen + +#include "r_local.h" + +static finalvert_t fv[2][8]; + +void R_AliasProjectAndClipTestFinalVert (finalvert_t *fv); +void R_Alias_clip_top (finalvert_t *pfv0, finalvert_t *pfv1, + finalvert_t *out); +void R_Alias_clip_bottom (finalvert_t *pfv0, finalvert_t *pfv1, + finalvert_t *out); +void R_Alias_clip_left (finalvert_t *pfv0, finalvert_t *pfv1, + finalvert_t *out); +void R_Alias_clip_right (finalvert_t *pfv0, finalvert_t *pfv1, + finalvert_t *out); + + +/* +================ +R_Alias_clip_z + +pfv0 is the unclipped vertex, pfv1 is the z-clipped vertex +================ +*/ +void R_Alias_clip_z (finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out) +{ + float scale; + + scale = (ALIAS_Z_CLIP_PLANE - pfv0->xyz[2]) / + (pfv1->xyz[2] - pfv0->xyz[2]); + + out->xyz[0] = pfv0->xyz[0] + (pfv1->xyz[0] - pfv0->xyz[0]) * scale; + out->xyz[1] = pfv0->xyz[1] + (pfv1->xyz[1] - pfv0->xyz[1]) * scale; + out->xyz[2] = ALIAS_Z_CLIP_PLANE; + + out->s = pfv0->s + (pfv1->s - pfv0->s) * scale; + out->t = pfv0->t + (pfv1->t - pfv0->t) * scale; + out->l = pfv0->l + (pfv1->l - pfv0->l) * scale; + + R_AliasProjectAndClipTestFinalVert (out); +} + +void R_Alias_clip_left (finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out) +{ + float scale; + + if (pfv0->v >= pfv1->v ) + { + scale = (float)(r_refdef.aliasvrect.x - pfv0->u) / + (pfv1->u - pfv0->u); + out->u = pfv0->u + ( pfv1->u - pfv0->u ) * scale + 0.5; + out->v = pfv0->v + ( pfv1->v - pfv0->v ) * scale + 0.5; + out->s = pfv0->s + ( pfv1->s - pfv0->s ) * scale + 0.5; + out->t = pfv0->t + ( pfv1->t - pfv0->t ) * scale + 0.5; + out->l = pfv0->l + ( pfv1->l - pfv0->l ) * scale + 0.5; + out->zi = pfv0->zi + ( pfv1->zi - pfv0->zi) * scale + 0.5; + } + else + { + scale = (float)(r_refdef.aliasvrect.x - pfv1->u) / + (pfv0->u - pfv1->u); + out->u = pfv1->u + ( pfv0->u - pfv1->u ) * scale + 0.5; + out->v = pfv1->v + ( pfv0->v - pfv1->v ) * scale + 0.5; + out->s = pfv1->s + ( pfv0->s - pfv1->s ) * scale + 0.5; + out->t = pfv1->t + ( pfv0->t - pfv1->t ) * scale + 0.5; + out->l = pfv1->l + ( pfv0->l - pfv1->l ) * scale + 0.5; + out->zi = pfv1->zi + ( pfv0->zi - pfv1->zi) * scale + 0.5; + } +} + +void R_Alias_clip_right (finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out) +{ + float scale; + + if ( pfv0->v >= pfv1->v ) + { + scale = (float)(r_refdef.aliasvrectright - pfv0->u ) / + (pfv1->u - pfv0->u ); + out->u = pfv0->u + ( pfv1->u - pfv0->u ) * scale + 0.5; + out->v = pfv0->v + ( pfv1->v - pfv0->v ) * scale + 0.5; + out->s = pfv0->s + ( pfv1->s - pfv0->s ) * scale + 0.5; + out->t = pfv0->t + ( pfv1->t - pfv0->t ) * scale + 0.5; + out->l = pfv0->l + ( pfv1->l - pfv0->l ) * scale + 0.5; + out->zi = pfv0->zi + ( pfv1->zi - pfv0->zi) * scale + 0.5; + } + else + { + scale = (float)(r_refdef.aliasvrectright - pfv1->u ) / + (pfv0->u - pfv1->u ); + out->u = pfv1->u + ( pfv0->u - pfv1->u ) * scale + 0.5; + out->v = pfv1->v + ( pfv0->v - pfv1->v ) * scale + 0.5; + out->s = pfv1->s + ( pfv0->s - pfv1->s ) * scale + 0.5; + out->t = pfv1->t + ( pfv0->t - pfv1->t ) * scale + 0.5; + out->l = pfv1->l + ( pfv0->l - pfv1->l ) * scale + 0.5; + out->zi = pfv1->zi + ( pfv0->zi - pfv1->zi) * scale + 0.5; + } +} + + +void R_Alias_clip_top (finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out) +{ + float scale; + + if (pfv0->v >= pfv1->v) + { + scale = (float)(r_refdef.aliasvrect.y - pfv0->v) / + (pfv1->v - pfv0->v); + out->u = pfv0->u + ( pfv1->u - pfv0->u ) * scale + 0.5; + out->v = pfv0->v + ( pfv1->v - pfv0->v ) * scale + 0.5; + out->s = pfv0->s + ( pfv1->s - pfv0->s ) * scale + 0.5; + out->t = pfv0->t + ( pfv1->t - pfv0->t ) * scale + 0.5; + out->l = pfv0->l + ( pfv1->l - pfv0->l ) * scale + 0.5; + out->zi = pfv0->zi + ( pfv1->zi - pfv0->zi) * scale + 0.5; + } + else + { + scale = (float)(r_refdef.aliasvrect.y - pfv1->v) / + (pfv0->v - pfv1->v); + out->u = pfv1->u + ( pfv0->u - pfv1->u ) * scale + 0.5; + out->v = pfv1->v + ( pfv0->v - pfv1->v ) * scale + 0.5; + out->s = pfv1->s + ( pfv0->s - pfv1->s ) * scale + 0.5; + out->t = pfv1->t + ( pfv0->t - pfv1->t ) * scale + 0.5; + out->l = pfv1->l + ( pfv0->l - pfv1->l ) * scale + 0.5; + out->zi = pfv1->zi + ( pfv0->zi - pfv1->zi) * scale + 0.5; + } +} + + +void R_Alias_clip_bottom (finalvert_t *pfv0, finalvert_t *pfv1, + finalvert_t *out) +{ + float scale; + + if (pfv0->v >= pfv1->v) + { + scale = (float)(r_refdef.aliasvrectbottom - pfv0->v) / + (pfv1->v - pfv0->v); + + out->u = pfv0->u + ( pfv1->u - pfv0->u ) * scale + 0.5; + out->v = pfv0->v + ( pfv1->v - pfv0->v ) * scale + 0.5; + out->s = pfv0->s + ( pfv1->s - pfv0->s ) * scale + 0.5; + out->t = pfv0->t + ( pfv1->t - pfv0->t ) * scale + 0.5; + out->l = pfv0->l + ( pfv1->l - pfv0->l ) * scale + 0.5; + out->zi = pfv0->zi + ( pfv1->zi - pfv0->zi) * scale + 0.5; + } + else + { + scale = (float)(r_refdef.aliasvrectbottom - pfv1->v) / + (pfv0->v - pfv1->v); + + out->u = pfv1->u + ( pfv0->u - pfv1->u ) * scale + 0.5; + out->v = pfv1->v + ( pfv0->v - pfv1->v ) * scale + 0.5; + out->s = pfv1->s + ( pfv0->s - pfv1->s ) * scale + 0.5; + out->t = pfv1->t + ( pfv0->t - pfv1->t ) * scale + 0.5; + out->l = pfv1->l + ( pfv0->l - pfv1->l ) * scale + 0.5; + out->zi = pfv1->zi + ( pfv0->zi - pfv1->zi) * scale + 0.5; + } +} + + +int R_AliasClip (finalvert_t *in, finalvert_t *out, int flag, int count, + void(*clip)(finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out) ) +{ + int i,j,k; + + j = count-1; + k = 0; + for (i=0 ; i r_refdef.aliasvrectright) + out[k].flags |= ALIAS_RIGHT_CLIP; + if (out[k].v > r_refdef.aliasvrectbottom) + out[k].flags |= ALIAS_BOTTOM_CLIP; + k++; + } + if (!flags) + { + out[k] = in[i]; + k++; + } + } + + return k; +} + + +/* +================ +R_AliasClipTriangle +================ +*/ +void R_AliasClipTriangle (finalvert_t *index0, finalvert_t *index1, finalvert_t *index2) +{ + int i, k, pingpong; + unsigned clipflags; + + // copy vertexes and fix seam texture coordinates + fv[0][0] = *index0; + fv[0][1] = *index1; + fv[0][2] = *index2; + + // clip + clipflags = fv[0][0].flags | fv[0][1].flags | fv[0][2].flags; + + if (clipflags & ALIAS_Z_CLIP) + { + k = R_AliasClip (fv[0], fv[1], ALIAS_Z_CLIP, 3, R_Alias_clip_z); + if (k == 0) + return; + + pingpong = 1; + clipflags = fv[1][0].flags | fv[1][1].flags | fv[1][2].flags; + } + else + { + pingpong = 0; + k = 3; + } + + if (clipflags & ALIAS_LEFT_CLIP) + { + k = R_AliasClip (fv[pingpong], fv[pingpong ^ 1], + ALIAS_LEFT_CLIP, k, R_Alias_clip_left); + if (k == 0) + return; + + pingpong ^= 1; + } + + if (clipflags & ALIAS_RIGHT_CLIP) + { + k = R_AliasClip (fv[pingpong], fv[pingpong ^ 1], + ALIAS_RIGHT_CLIP, k, R_Alias_clip_right); + if (k == 0) + return; + + pingpong ^= 1; + } + + if (clipflags & ALIAS_BOTTOM_CLIP) + { + k = R_AliasClip (fv[pingpong], fv[pingpong ^ 1], + ALIAS_BOTTOM_CLIP, k, R_Alias_clip_bottom); + if (k == 0) + return; + + pingpong ^= 1; + } + + if (clipflags & ALIAS_TOP_CLIP) + { + k = R_AliasClip (fv[pingpong], fv[pingpong ^ 1], + ALIAS_TOP_CLIP, k, R_Alias_clip_top); + if (k == 0) + return; + + pingpong ^= 1; + } + + for (i=0 ; i r_refdef.aliasvrectright) + fv[pingpong][i].u = r_refdef.aliasvrectright; + + if (fv[pingpong][i].v < r_refdef.aliasvrect.y) + fv[pingpong][i].v = r_refdef.aliasvrect.y; + else if (fv[pingpong][i].v > r_refdef.aliasvrectbottom) + fv[pingpong][i].v = r_refdef.aliasvrectbottom; + + fv[pingpong][i].flags = 0; + } + + // draw triangles + for (i=1 ; i +#include "r_local.h" + +#define LIGHT_MIN 5 // lowest light value we'll allow, to avoid the + // need for inner-loop light clamping + +//PGM +extern byte iractive; +//PGM + +int r_amodels_drawn; + +affinetridesc_t r_affinetridesc; + +vec3_t r_plightvec; +vec3_t r_lerped[1024]; +vec3_t r_lerp_frontv, r_lerp_backv, r_lerp_move; + +int r_ambientlight; +int r_aliasblendcolor; +float r_shadelight; + + +daliasframe_t *r_thisframe, *r_lastframe; +dmdl_t *s_pmdl; + +float aliastransform[3][4]; +float aliasworldtransform[3][4]; +float aliasoldworldtransform[3][4]; + +static float s_ziscale; +static vec3_t s_alias_forward, s_alias_right, s_alias_up; + + +#define NUMVERTEXNORMALS 162 + +float r_avertexnormals[NUMVERTEXNORMALS][3] = { +#include "../constants/anorms.h" +}; + + +void R_AliasSetUpLerpData( dmdl_t *pmdl, float backlerp ); +void R_AliasSetUpTransform (void); +void R_AliasTransformVector (vec3_t in, vec3_t out, float m[3][4] ); +void R_AliasProjectAndClipTestFinalVert (finalvert_t *fv); + +void R_AliasTransformFinalVerts( int numpoints, finalvert_t *fv, dtrivertx_t *oldv, dtrivertx_t *newv ); + +void R_AliasLerpFrames( dmdl_t *paliashdr, float backlerp ); + +/* +================ +R_AliasCheckBBox +================ +*/ + +#define BBOX_TRIVIAL_ACCEPT 0 +#define BBOX_MUST_CLIP_XY 1 +#define BBOX_MUST_CLIP_Z 2 +#define BBOX_TRIVIAL_REJECT 8 + +/* +** R_AliasCheckFrameBBox +** +** Checks a specific alias frame bounding box +*/ +unsigned long R_AliasCheckFrameBBox( daliasframe_t *frame, float worldxf[3][4] ) +{ + unsigned long aggregate_and_clipcode = ~0U, + aggregate_or_clipcode = 0; + int i; + vec3_t mins, maxs; + vec3_t transformed_min, transformed_max; + qboolean zclipped = false, zfullyclipped = true; + + /* + ** get the exact frame bounding box + */ + for (i=0 ; i<3 ; i++) + { + mins[i] = frame->translate[i]; + maxs[i] = mins[i] + frame->scale[i]*255; + } + + /* + ** transform the min and max values into view space + */ + R_AliasTransformVector( mins, transformed_min, aliastransform ); + R_AliasTransformVector( maxs, transformed_max, aliastransform ); + + if ( transformed_min[2] >= ALIAS_Z_CLIP_PLANE ) + zfullyclipped = false; + if ( transformed_max[2] >= ALIAS_Z_CLIP_PLANE ) + zfullyclipped = false; + + if ( zfullyclipped ) + { + return BBOX_TRIVIAL_REJECT; + } + if ( zclipped ) + { + return ( BBOX_MUST_CLIP_XY | BBOX_MUST_CLIP_Z ); + } + + /* + ** build a transformed bounding box from the given min and max + */ + for ( i = 0; i < 8; i++ ) + { + int j; + vec3_t tmp, transformed; + unsigned long clipcode = 0; + + if ( i & 1 ) + tmp[0] = mins[0]; + else + tmp[0] = maxs[0]; + + if ( i & 2 ) + tmp[1] = mins[1]; + else + tmp[1] = maxs[1]; + + if ( i & 4 ) + tmp[2] = mins[2]; + else + tmp[2] = maxs[2]; + + R_AliasTransformVector( tmp, transformed, worldxf ); + + for ( j = 0; j < 4; j++ ) + { + float dp = DotProduct( transformed, view_clipplanes[j].normal ); + + if ( ( dp - view_clipplanes[j].dist ) < 0.0F ) + clipcode |= 1 << j; + } + + aggregate_and_clipcode &= clipcode; + aggregate_or_clipcode |= clipcode; + } + + if ( aggregate_and_clipcode ) + { + return BBOX_TRIVIAL_REJECT; + } + if ( !aggregate_or_clipcode ) + { + return BBOX_TRIVIAL_ACCEPT; + } + + return BBOX_MUST_CLIP_XY; +} + +qboolean R_AliasCheckBBox (void) +{ + unsigned long ccodes[2] = { 0, 0 }; + + ccodes[0] = R_AliasCheckFrameBBox( r_thisframe, aliasworldtransform ); + + /* + ** non-lerping model + */ + if ( currententity->backlerp == 0 ) + { + if ( ccodes[0] == BBOX_TRIVIAL_ACCEPT ) + return BBOX_TRIVIAL_ACCEPT; + else if ( ccodes[0] & BBOX_TRIVIAL_REJECT ) + return BBOX_TRIVIAL_REJECT; + else + return ( ccodes[0] & ~BBOX_TRIVIAL_REJECT ); + } + + ccodes[1] = R_AliasCheckFrameBBox( r_lastframe, aliasoldworldtransform ); + + if ( ( ccodes[0] | ccodes[1] ) == BBOX_TRIVIAL_ACCEPT ) + return BBOX_TRIVIAL_ACCEPT; + else if ( ( ccodes[0] & ccodes[1] ) & BBOX_TRIVIAL_REJECT ) + return BBOX_TRIVIAL_REJECT; + else + return ( ccodes[0] | ccodes[1] ) & ~BBOX_TRIVIAL_REJECT; +} + + +/* +================ +R_AliasTransformVector +================ +*/ +void R_AliasTransformVector(vec3_t in, vec3_t out, float xf[3][4] ) +{ + out[0] = DotProduct(in, xf[0]) + xf[0][3]; + out[1] = DotProduct(in, xf[1]) + xf[1][3]; + out[2] = DotProduct(in, xf[2]) + xf[2][3]; +} + + +/* +================ +R_AliasPreparePoints + +General clipped case +================ +*/ +typedef struct +{ + int num_points; + dtrivertx_t *last_verts; // verts from the last frame + dtrivertx_t *this_verts; // verts from this frame + finalvert_t *dest_verts; // destination for transformed verts +} aliasbatchedtransformdata_t; + +aliasbatchedtransformdata_t aliasbatchedtransformdata; +finalvert_t *finalverts; + +void R_AliasPreparePoints (void) +{ + int i; + dstvert_t *pstverts; + dtriangle_t *ptri; + finalvert_t *pfv[3]; + finalvert_t *pfinalverts; + + // PGM + iractive = (r_newrefdef.rdflags & RDF_IRGOGGLES && currententity->flags & RF_IR_VISIBLE); + // iractive = 0; + // if(r_newrefdef.rdflags & RDF_IRGOGGLES && currententity->flags & RF_IR_VISIBLE) + // iractive = 1; + // PGM + + // put work vertexes on stack, cache aligned + pfinalverts = finalverts; + + aliasbatchedtransformdata.num_points = s_pmdl->num_xyz; + aliasbatchedtransformdata.last_verts = r_lastframe->verts; + aliasbatchedtransformdata.this_verts = r_thisframe->verts; + aliasbatchedtransformdata.dest_verts = pfinalverts; + + R_AliasTransformFinalVerts( aliasbatchedtransformdata.num_points, + aliasbatchedtransformdata.dest_verts, + aliasbatchedtransformdata.last_verts, + aliasbatchedtransformdata.this_verts ); + + // clip and draw all triangles + // + pstverts = (dstvert_t *)((byte *)s_pmdl + s_pmdl->ofs_st); + ptri = (dtriangle_t *)((byte *)s_pmdl + s_pmdl->ofs_tris); + + if ( ( currententity->flags & RF_WEAPONMODEL ) && ( r_lefthand->value == 1.0F ) ) + { + for (i=0 ; inum_tris ; i++, ptri++) + { + pfv[0] = &pfinalverts[ptri->index_xyz[0]]; + pfv[1] = &pfinalverts[ptri->index_xyz[1]]; + pfv[2] = &pfinalverts[ptri->index_xyz[2]]; + + if ( pfv[0]->flags & pfv[1]->flags & pfv[2]->flags ) + continue; // completely clipped + + // insert s/t coordinates + pfv[0]->s = pstverts[ptri->index_st[0]].s << 16; + pfv[0]->t = pstverts[ptri->index_st[0]].t << 16; + + pfv[1]->s = pstverts[ptri->index_st[1]].s << 16; + pfv[1]->t = pstverts[ptri->index_st[1]].t << 16; + + pfv[2]->s = pstverts[ptri->index_st[2]].s << 16; + pfv[2]->t = pstverts[ptri->index_st[2]].t << 16; + + if ( ! (pfv[0]->flags | pfv[1]->flags | pfv[2]->flags) ) + { // totally unclipped + aliastriangleparms.a = pfv[2]; + aliastriangleparms.b = pfv[1]; + aliastriangleparms.c = pfv[0]; + + R_DrawTriangle(); + } + else + { + R_AliasClipTriangle (pfv[2], pfv[1], pfv[0]); + } + } + } + else + { + for (i=0 ; inum_tris ; i++, ptri++) + { + pfv[0] = &pfinalverts[ptri->index_xyz[0]]; + pfv[1] = &pfinalverts[ptri->index_xyz[1]]; + pfv[2] = &pfinalverts[ptri->index_xyz[2]]; + + if ( pfv[0]->flags & pfv[1]->flags & pfv[2]->flags ) + continue; // completely clipped + + // insert s/t coordinates + pfv[0]->s = pstverts[ptri->index_st[0]].s << 16; + pfv[0]->t = pstverts[ptri->index_st[0]].t << 16; + + pfv[1]->s = pstverts[ptri->index_st[1]].s << 16; + pfv[1]->t = pstverts[ptri->index_st[1]].t << 16; + + pfv[2]->s = pstverts[ptri->index_st[2]].s << 16; + pfv[2]->t = pstverts[ptri->index_st[2]].t << 16; + + if ( ! (pfv[0]->flags | pfv[1]->flags | pfv[2]->flags) ) + { // totally unclipped + aliastriangleparms.a = pfv[0]; + aliastriangleparms.b = pfv[1]; + aliastriangleparms.c = pfv[2]; + + R_DrawTriangle(); + } + else + { // partially clipped + R_AliasClipTriangle (pfv[0], pfv[1], pfv[2]); + } + } + } +} + + +/* +================ +R_AliasSetUpTransform +================ +*/ +void R_AliasSetUpTransform (void) +{ + int i; + static float viewmatrix[3][4]; + vec3_t angles; + + // TODO: should really be stored with the entity instead of being reconstructed + // TODO: should use a look-up table + // TODO: could cache lazily, stored in the entity + // + angles[ROLL] = currententity->angles[ROLL]; + angles[PITCH] = currententity->angles[PITCH]; + angles[YAW] = currententity->angles[YAW]; + AngleVectors( angles, s_alias_forward, s_alias_right, s_alias_up ); + + // TODO: can do this with simple matrix rearrangement + memset( aliasworldtransform, 0, sizeof( aliasworldtransform ) ); + memset( aliasoldworldtransform, 0, sizeof( aliasworldtransform ) ); + + for (i=0 ; i<3 ; i++) + { + aliasoldworldtransform[i][0] = aliasworldtransform[i][0] = s_alias_forward[i]; + aliasoldworldtransform[i][0] = aliasworldtransform[i][1] = -s_alias_right[i]; + aliasoldworldtransform[i][0] = aliasworldtransform[i][2] = s_alias_up[i]; + } + + aliasworldtransform[0][3] = currententity->origin[0]-r_origin[0]; + aliasworldtransform[1][3] = currententity->origin[1]-r_origin[1]; + aliasworldtransform[2][3] = currententity->origin[2]-r_origin[2]; + + aliasoldworldtransform[0][3] = currententity->oldorigin[0]-r_origin[0]; + aliasoldworldtransform[1][3] = currententity->oldorigin[1]-r_origin[1]; + aliasoldworldtransform[2][3] = currententity->oldorigin[2]-r_origin[2]; + + // FIXME: can do more efficiently than full concatenation + // memcpy( rotationmatrix, t2matrix, sizeof( rotationmatrix ) ); + + // R_ConcatTransforms (t2matrix, tmatrix, rotationmatrix); + + // TODO: should be global, set when vright, etc., set + VectorCopy (vright, viewmatrix[0]); + VectorCopy (vup, viewmatrix[1]); + VectorInverse (viewmatrix[1]); + VectorCopy (vpn, viewmatrix[2]); + + viewmatrix[0][3] = 0; + viewmatrix[1][3] = 0; + viewmatrix[2][3] = 0; + + // memcpy( aliasworldtransform, rotationmatrix, sizeof( aliastransform ) ); + + R_ConcatTransforms (viewmatrix, aliasworldtransform, aliastransform); + + aliasworldtransform[0][3] = currententity->origin[0]; + aliasworldtransform[1][3] = currententity->origin[1]; + aliasworldtransform[2][3] = currententity->origin[2]; + + aliasoldworldtransform[0][3] = currententity->oldorigin[0]; + aliasoldworldtransform[1][3] = currententity->oldorigin[1]; + aliasoldworldtransform[2][3] = currententity->oldorigin[2]; +} + + +/* +================ +R_AliasTransformFinalVerts +================ +*/ +void R_AliasTransformFinalVerts( int numpoints, finalvert_t *fv, dtrivertx_t *oldv, dtrivertx_t *newv ) +{ + int i; + + for ( i = 0; i < numpoints; i++, fv++, oldv++, newv++ ) + { + int temp; + float lightcos, *plightnormal; + vec3_t lerped_vert; + + lerped_vert[0] = r_lerp_move[0] + oldv->v[0]*r_lerp_backv[0] + newv->v[0]*r_lerp_frontv[0]; + lerped_vert[1] = r_lerp_move[1] + oldv->v[1]*r_lerp_backv[1] + newv->v[1]*r_lerp_frontv[1]; + lerped_vert[2] = r_lerp_move[2] + oldv->v[2]*r_lerp_backv[2] + newv->v[2]*r_lerp_frontv[2]; + + plightnormal = r_avertexnormals[newv->lightnormalindex]; + + // PMM - added double damage shell + if ( currententity->flags & ( RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE | RF_SHELL_DOUBLE | RF_SHELL_HALF_DAM) ) + { + lerped_vert[0] += plightnormal[0] * POWERSUIT_SCALE; + lerped_vert[1] += plightnormal[1] * POWERSUIT_SCALE; + lerped_vert[2] += plightnormal[2] * POWERSUIT_SCALE; + } + + fv->xyz[0] = DotProduct(lerped_vert, aliastransform[0]) + aliastransform[0][3]; + fv->xyz[1] = DotProduct(lerped_vert, aliastransform[1]) + aliastransform[1][3]; + fv->xyz[2] = DotProduct(lerped_vert, aliastransform[2]) + aliastransform[2][3]; + + fv->flags = 0; + + // lighting + lightcos = DotProduct (plightnormal, r_plightvec); + temp = r_ambientlight; + + if (lightcos < 0) + { + temp += (int)(r_shadelight * lightcos); + + // clamp; because we limited the minimum ambient and shading light, we + // don't have to clamp low light, just bright + if (temp < 0) + temp = 0; + } + + fv->l = temp; + + if ( fv->xyz[2] < ALIAS_Z_CLIP_PLANE ) + { + fv->flags |= ALIAS_Z_CLIP; + } + else + { + R_AliasProjectAndClipTestFinalVert( fv ); + } + } +} + +/* +================ +R_AliasProjectAndClipTestFinalVert +================ +*/ +void R_AliasProjectAndClipTestFinalVert( finalvert_t *fv ) +{ + float zi; + float x, y, z; + + // project points + x = fv->xyz[0]; + y = fv->xyz[1]; + z = fv->xyz[2]; + zi = 1.0 / z; + + fv->zi = zi * s_ziscale; + + fv->u = (x * aliasxscale * zi) + aliasxcenter; + fv->v = (y * aliasyscale * zi) + aliasycenter; + + if (fv->u < r_refdef.aliasvrect.x) + fv->flags |= ALIAS_LEFT_CLIP; + if (fv->v < r_refdef.aliasvrect.y) + fv->flags |= ALIAS_TOP_CLIP; + if (fv->u > r_refdef.aliasvrectright) + fv->flags |= ALIAS_RIGHT_CLIP; + if (fv->v > r_refdef.aliasvrectbottom) + fv->flags |= ALIAS_BOTTOM_CLIP; +} + +/* +=============== +R_AliasSetupSkin +=============== +*/ +static qboolean R_AliasSetupSkin (void) +{ + image_t *pskindesc; + + if (currententity->skin) + pskindesc = currententity->skin; + else + { + int skinnum; + + skinnum = currententity->skinnum; + if ((skinnum >= s_pmdl->num_skins) || (skinnum < 0)) + { + R_Printf(PRINT_ALL, "R_AliasSetupSkin %s: no such skin # %d\n", + currentmodel->name, skinnum); + skinnum = 0; + } + + pskindesc = currentmodel->skins[skinnum]; + } + + if ( !pskindesc ) + return false; + + r_affinetridesc.pskin = pskindesc->pixels[0]; + r_affinetridesc.skinwidth = pskindesc->width; + r_affinetridesc.skinheight = pskindesc->height; + + R_PolysetUpdateTables (); // FIXME: precalc edge lookups + + return true; +} + + +/* +================ +R_AliasSetupLighting + + FIXME: put lighting into tables +================ +*/ +void R_AliasSetupLighting (void) +{ + alight_t lighting; + float lightvec[3] = {-1, 0, 0}; + vec3_t light; + int i, j; + + // all components of light should be identical in software + if ( currententity->flags & RF_FULLBRIGHT ) + { + for (i=0 ; i<3 ; i++) + light[i] = 1.0; + } + else + { + R_LightPoint (currententity->origin, light); + } + + // save off light value for server to look at (BIG HACK!) + if ( currententity->flags & RF_WEAPONMODEL ) + r_lightlevel->value = 150.0 * light[0]; + + + if ( currententity->flags & RF_MINLIGHT ) + { + for (i=0 ; i<3 ; i++) + if (light[i] < 0.1) + light[i] = 0.1; + } + + if ( currententity->flags & RF_GLOW ) + { // bonus items will pulse with time + float scale; + + scale = 0.1 * sin(r_newrefdef.time*7); + for (i=0 ; i<3 ; i++) + { + float min; + + min = light[i] * 0.8; + light[i] += scale; + if (light[i] < min) + light[i] = min; + } + } + + j = (light[0] + light[1] + light[2])*0.3333*255; + + lighting.ambientlight = j; + lighting.shadelight = j; + + lighting.plightvec = lightvec; + + // clamp lighting so it doesn't overbright as much + if (lighting.ambientlight > 128) + lighting.ambientlight = 128; + if (lighting.ambientlight + lighting.shadelight > 192) + lighting.shadelight = 192 - lighting.ambientlight; + + // guarantee that no vertex will ever be lit below LIGHT_MIN, so we don't have + // to clamp off the bottom + r_ambientlight = lighting.ambientlight; + + if (r_ambientlight < LIGHT_MIN) + r_ambientlight = LIGHT_MIN; + + r_ambientlight = (255 - r_ambientlight) << VID_CBITS; + + if (r_ambientlight < LIGHT_MIN) + r_ambientlight = LIGHT_MIN; + + r_shadelight = lighting.shadelight; + + if (r_shadelight < 0) + r_shadelight = 0; + + r_shadelight *= VID_GRADES; + + // rotate the lighting vector into the model's frame of reference + r_plightvec[0] = DotProduct( lighting.plightvec, s_alias_forward ); + r_plightvec[1] = -DotProduct( lighting.plightvec, s_alias_right ); + r_plightvec[2] = DotProduct( lighting.plightvec, s_alias_up ); +} + + +/* +================= +R_AliasSetupFrames + +================= +*/ +void R_AliasSetupFrames( dmdl_t *pmdl ) +{ + int thisframe = currententity->frame; + int lastframe = currententity->oldframe; + + if ( ( thisframe >= pmdl->num_frames ) || ( thisframe < 0 ) ) + { + R_Printf(PRINT_ALL, "R_AliasSetupFrames %s: no such thisframe %d\n", + currentmodel->name, thisframe); + thisframe = 0; + } + if ( ( lastframe >= pmdl->num_frames ) || ( lastframe < 0 ) ) + { + R_Printf(PRINT_ALL, "R_AliasSetupFrames %s: no such lastframe %d\n", + currentmodel->name, lastframe); + lastframe = 0; + } + + r_thisframe = (daliasframe_t *)((byte *)pmdl + pmdl->ofs_frames + + thisframe * pmdl->framesize); + + r_lastframe = (daliasframe_t *)((byte *)pmdl + pmdl->ofs_frames + + lastframe * pmdl->framesize); +} + +/* +** R_AliasSetUpLerpData +** +** Precomputes lerp coefficients used for the whole frame. +*/ +void R_AliasSetUpLerpData( dmdl_t *pmdl, float backlerp ) +{ + float frontlerp; + vec3_t translation, vectors[3]; + int i; + + frontlerp = 1.0F - backlerp; + + /* + ** convert entity's angles into discrete vectors for R, U, and F + */ + AngleVectors (currententity->angles, vectors[0], vectors[1], vectors[2]); + + /* + ** translation is the vector from last position to this position + */ + VectorSubtract (currententity->oldorigin, currententity->origin, translation); + + /* + ** move should be the delta back to the previous frame * backlerp + */ + r_lerp_move[0] = DotProduct(translation, vectors[0]); // forward + r_lerp_move[1] = -DotProduct(translation, vectors[1]); // left + r_lerp_move[2] = DotProduct(translation, vectors[2]); // up + + VectorAdd( r_lerp_move, r_lastframe->translate, r_lerp_move ); + + for (i=0 ; i<3 ; i++) + { + r_lerp_move[i] = backlerp*r_lerp_move[i] + frontlerp * r_thisframe->translate[i]; + } + + for (i=0 ; i<3 ; i++) + { + r_lerp_frontv[i] = frontlerp * r_thisframe->scale[i]; + r_lerp_backv[i] = backlerp * r_lastframe->scale[i]; + } +} + +/* +================ +R_AliasDrawModel +================ +*/ +void R_AliasDrawModel (void) +{ + extern void (*d_pdrawspans)(void *); + extern void R_PolysetDrawSpans8_Opaque( void * ); + extern void R_PolysetDrawSpans8_33( void * ); + extern void R_PolysetDrawSpans8_66( void * ); + extern void R_PolysetDrawSpansConstant8_33( void * ); + extern void R_PolysetDrawSpansConstant8_66( void * ); + + s_pmdl = (dmdl_t *)currentmodel->extradata; + + if ( r_lerpmodels->value == 0 ) + currententity->backlerp = 0; + + if ( currententity->flags & RF_WEAPONMODEL ) + { + if ( r_lefthand->value == 1.0F ) + aliasxscale = -aliasxscale; + else if ( r_lefthand->value == 2.0F ) + return; + } + + /* + ** we have to set our frame pointers and transformations before + ** doing any real work + */ + R_AliasSetupFrames( s_pmdl ); + R_AliasSetUpTransform(); + + // see if the bounding box lets us trivially reject, also sets + // trivial accept status + if ( R_AliasCheckBBox() == BBOX_TRIVIAL_REJECT ) + { + if ( ( currententity->flags & RF_WEAPONMODEL ) && ( r_lefthand->value == 1.0F ) ) + { + aliasxscale = -aliasxscale; + } + return; + } + + // set up the skin and verify it exists + if ( !R_AliasSetupSkin () ) + { + R_Printf( PRINT_ALL, "R_AliasDrawModel %s: NULL skin found\n", + currentmodel->name); + return; + } + + r_amodels_drawn++; + R_AliasSetupLighting (); + + /* + ** select the proper span routine based on translucency + */ + // PMM - added double damage shell + // PMM - reordered to handle blending + if ( currententity->flags & ( RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE | RF_SHELL_DOUBLE | RF_SHELL_HALF_DAM) ) + { + int color; + + // PMM - added double + color = currententity->flags & ( RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE | RF_SHELL_DOUBLE | RF_SHELL_HALF_DAM); + // PMM - reordered, new shells after old shells (so they get overriden) + + if ( color == RF_SHELL_RED ) + r_aliasblendcolor = SHELL_RED_COLOR; + else if ( color == RF_SHELL_GREEN ) + r_aliasblendcolor = SHELL_GREEN_COLOR; + else if ( color == RF_SHELL_BLUE ) + r_aliasblendcolor = SHELL_BLUE_COLOR; + else if ( color == (RF_SHELL_RED | RF_SHELL_GREEN) ) + r_aliasblendcolor = SHELL_RG_COLOR; + else if ( color == (RF_SHELL_RED | RF_SHELL_BLUE) ) + r_aliasblendcolor = SHELL_RB_COLOR; + else if ( color == (RF_SHELL_BLUE | RF_SHELL_GREEN) ) + r_aliasblendcolor = SHELL_BG_COLOR; + // PMM - added this .. it's yellowish + else if ( color == (RF_SHELL_DOUBLE) ) + r_aliasblendcolor = SHELL_DOUBLE_COLOR; + else if ( color == (RF_SHELL_HALF_DAM) ) + r_aliasblendcolor = SHELL_HALF_DAM_COLOR; + // pmm + else + r_aliasblendcolor = SHELL_WHITE_COLOR; + /* + if ( color & RF_SHELL_RED ) + { + if ( ( color & RF_SHELL_BLUE) && ( color & RF_SHELL_GREEN) ) + r_aliasblendcolor = SHELL_WHITE_COLOR; + else if ( color & (RF_SHELL_BLUE | RF_SHELL_DOUBLE)) + r_aliasblendcolor = SHELL_RB_COLOR; + else + r_aliasblendcolor = SHELL_RED_COLOR; + } + else if ( color & RF_SHELL_BLUE) + { + if ( color & RF_SHELL_DOUBLE ) + r_aliasblendcolor = SHELL_CYAN_COLOR; + else + r_aliasblendcolor = SHELL_BLUE_COLOR; + } + else if ( color & (RF_SHELL_DOUBLE) ) + r_aliasblendcolor = SHELL_DOUBLE_COLOR; + else if ( color & (RF_SHELL_HALF_DAM) ) + r_aliasblendcolor = SHELL_HALF_DAM_COLOR; + else if ( color & RF_SHELL_GREEN ) + r_aliasblendcolor = SHELL_GREEN_COLOR; + else + r_aliasblendcolor = SHELL_WHITE_COLOR; + */ + + if ( currententity->alpha > 0.33 ) + d_pdrawspans = R_PolysetDrawSpansConstant8_66; + else + d_pdrawspans = R_PolysetDrawSpansConstant8_33; + } + else if ( currententity->flags & RF_TRANSLUCENT ) + { + if ( currententity->alpha > 0.66 ) + d_pdrawspans = R_PolysetDrawSpans8_Opaque; + else if ( currententity->alpha > 0.33 ) + d_pdrawspans = R_PolysetDrawSpans8_66; + else + d_pdrawspans = R_PolysetDrawSpans8_33; + } + else + { + d_pdrawspans = R_PolysetDrawSpans8_Opaque; + } + + /* + ** compute this_frame and old_frame addresses + */ + R_AliasSetUpLerpData( s_pmdl, currententity->backlerp ); + + if (currententity->flags & RF_DEPTHHACK) + s_ziscale = (float)0x8000 * (float)0x10000 * 3.0; + else + s_ziscale = (float)0x8000 * (float)0x10000; + + R_AliasPreparePoints (); + + if ( ( currententity->flags & RF_WEAPONMODEL ) && ( r_lefthand->value == 1.0F ) ) + { + aliasxscale = -aliasxscale; + } +} diff --git a/src/client/refresh/soft/r_bsp.c b/src/client/refresh/soft/r_bsp.c new file mode 100644 index 00000000..8cb2bfa7 --- /dev/null +++ b/src/client/refresh/soft/r_bsp.c @@ -0,0 +1,642 @@ +/* +Copyright (C) 1997-2001 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +// r_bsp.c + +#include "r_local.h" + +// +// current entity info +// +qboolean insubmodel; +entity_t *currententity; +vec3_t modelorg; // modelorg is the viewpoint reletive to + // the currently rendering entity +vec3_t r_entorigin; // the currently rendering entity in world + // coordinates + +float entity_rotation[3][3]; + +int r_currentbkey; + +typedef enum {touchessolid, drawnode, nodrawnode} solidstate_t; + +#define MAX_BMODEL_VERTS 500 // 6K +#define MAX_BMODEL_EDGES 1000 // 12K + +static mvertex_t *pbverts; +static bedge_t *pbedges; +static int numbverts, numbedges; +static mvertex_t bverts[MAX_BMODEL_VERTS]; +static bedge_t bedges[MAX_BMODEL_EDGES]; + +static mvertex_t *pfrontenter, *pfrontexit; + +static qboolean makeclippededge; + + +//=========================================================================== + +/* +================ +R_EntityRotate +================ +*/ +void R_EntityRotate (vec3_t vec) +{ + vec3_t tvec; + + VectorCopy (vec, tvec); + vec[0] = DotProduct (entity_rotation[0], tvec); + vec[1] = DotProduct (entity_rotation[1], tvec); + vec[2] = DotProduct (entity_rotation[2], tvec); +} + + +/* +================ +R_RotateBmodel +================ +*/ +void R_RotateBmodel (void) +{ + float angle, s, c, temp1[3][3], temp2[3][3], temp3[3][3]; + + // TODO: should use a look-up table + // TODO: should really be stored with the entity instead of being reconstructed + // TODO: could cache lazily, stored in the entity + // TODO: share work with R_SetUpAliasTransform + + // yaw + angle = currententity->angles[YAW]; + angle = angle * M_PI*2 / 360; + s = sin(angle); + c = cos(angle); + + temp1[0][0] = c; + temp1[0][1] = s; + temp1[0][2] = 0; + temp1[1][0] = -s; + temp1[1][1] = c; + temp1[1][2] = 0; + temp1[2][0] = 0; + temp1[2][1] = 0; + temp1[2][2] = 1; + + + // pitch + angle = currententity->angles[PITCH]; + angle = angle * M_PI*2 / 360; + s = sin(angle); + c = cos(angle); + + temp2[0][0] = c; + temp2[0][1] = 0; + temp2[0][2] = -s; + temp2[1][0] = 0; + temp2[1][1] = 1; + temp2[1][2] = 0; + temp2[2][0] = s; + temp2[2][1] = 0; + temp2[2][2] = c; + + R_ConcatRotations (temp2, temp1, temp3); + + // roll + angle = currententity->angles[ROLL]; + angle = angle * M_PI*2 / 360; + s = sin(angle); + c = cos(angle); + + temp1[0][0] = 1; + temp1[0][1] = 0; + temp1[0][2] = 0; + temp1[1][0] = 0; + temp1[1][1] = c; + temp1[1][2] = s; + temp1[2][0] = 0; + temp1[2][1] = -s; + temp1[2][2] = c; + + R_ConcatRotations (temp1, temp3, entity_rotation); + + // + // rotate modelorg and the transformation matrix + // + R_EntityRotate (modelorg); + R_EntityRotate (vpn); + R_EntityRotate (vright); + R_EntityRotate (vup); + + R_TransformFrustum (); +} + + +/* +================ +R_RecursiveClipBPoly + +Clip a bmodel poly down the world bsp tree +================ +*/ +void R_RecursiveClipBPoly (bedge_t *pedges, mnode_t *pnode, msurface_t *psurf) +{ + bedge_t *psideedges[2], *pnextedge, *ptedge; + int i, side, lastside; + float frac; + mplane_t *splitplane, tplane; + mvertex_t *pvert, *plastvert, *ptvert; + mnode_t *pn; + int area; + + psideedges[0] = psideedges[1] = NULL; + + makeclippededge = false; + + // transform the BSP plane into model space + // FIXME: cache these? + splitplane = pnode->plane; + tplane.dist = splitplane->dist - + DotProduct(r_entorigin, splitplane->normal); + tplane.normal[0] = DotProduct (entity_rotation[0], splitplane->normal); + tplane.normal[1] = DotProduct (entity_rotation[1], splitplane->normal); + tplane.normal[2] = DotProduct (entity_rotation[2], splitplane->normal); + + // clip edges to BSP plane + for (; pedges; pedges = pnextedge) + { + float dist, lastdist; + + pnextedge = pedges->pnext; + + // set the status for the last point as the previous point + // FIXME: cache this stuff somehow? + plastvert = pedges->v[0]; + lastdist = DotProduct (plastvert->position, tplane.normal) - + tplane.dist; + + if (lastdist > 0) + lastside = 0; + else + lastside = 1; + + pvert = pedges->v[1]; + + dist = DotProduct (pvert->position, tplane.normal) - tplane.dist; + + if (dist > 0) + side = 0; + else + side = 1; + + if (side != lastside) + { + // clipped + if (numbverts >= MAX_BMODEL_VERTS) + return; + + // generate the clipped vertex + frac = lastdist / (lastdist - dist); + ptvert = &pbverts[numbverts++]; + ptvert->position[0] = plastvert->position[0] + + frac * (pvert->position[0] - + plastvert->position[0]); + ptvert->position[1] = plastvert->position[1] + + frac * (pvert->position[1] - + plastvert->position[1]); + ptvert->position[2] = plastvert->position[2] + + frac * (pvert->position[2] - + plastvert->position[2]); + + // split into two edges, one on each side, and remember entering + // and exiting points + // FIXME: share the clip edge by having a winding direction flag? + if (numbedges >= (MAX_BMODEL_EDGES - 1)) + { + R_Printf(PRINT_ALL,"Out of edges for bmodel\n"); + return; + } + + ptedge = &pbedges[numbedges]; + ptedge->pnext = psideedges[lastside]; + psideedges[lastside] = ptedge; + ptedge->v[0] = plastvert; + ptedge->v[1] = ptvert; + + ptedge = &pbedges[numbedges + 1]; + ptedge->pnext = psideedges[side]; + psideedges[side] = ptedge; + ptedge->v[0] = ptvert; + ptedge->v[1] = pvert; + + numbedges += 2; + + if (side == 0) + { + // entering for front, exiting for back + pfrontenter = ptvert; + makeclippededge = true; + } + else + { + pfrontexit = ptvert; + makeclippededge = true; + } + } + else + { + // add the edge to the appropriate side + pedges->pnext = psideedges[side]; + psideedges[side] = pedges; + } + } + + // if anything was clipped, reconstitute and add the edges along the clip + // plane to both sides (but in opposite directions) + if (makeclippededge) + { + if (numbedges >= (MAX_BMODEL_EDGES - 2)) + { + R_Printf(PRINT_ALL,"Out of edges for bmodel\n"); + return; + } + + ptedge = &pbedges[numbedges]; + ptedge->pnext = psideedges[0]; + psideedges[0] = ptedge; + ptedge->v[0] = pfrontexit; + ptedge->v[1] = pfrontenter; + + ptedge = &pbedges[numbedges + 1]; + ptedge->pnext = psideedges[1]; + psideedges[1] = ptedge; + ptedge->v[0] = pfrontenter; + ptedge->v[1] = pfrontexit; + + numbedges += 2; + } + + // draw or recurse further + for (i=0 ; i<2 ; i++) + { + if (psideedges[i]) + { + // draw if we've reached a non-solid leaf, done if all that's left is a + // solid leaf, and continue down the tree if it's not a leaf + pn = pnode->children[i]; + + // we're done with this branch if the node or leaf isn't in the PVS + if (pn->visframe == r_visframecount) + { + if (pn->contents != CONTENTS_NODE) + { + if (pn->contents != CONTENTS_SOLID) + { + if (r_newrefdef.areabits) + { + area = ((mleaf_t *)pn)->area; + if (! (r_newrefdef.areabits[area>>3] & (1<<(area&7)) ) ) + continue; // not visible + } + + r_currentbkey = ((mleaf_t *)pn)->key; + R_RenderBmodelFace (psideedges[i], psurf); + } + } + else + { + R_RecursiveClipBPoly (psideedges[i], pnode->children[i], + psurf); + } + } + } + } +} + +/* +================ +R_DrawSolidClippedSubmodelPolygons + +Bmodel crosses multiple leafs +================ +*/ +void R_DrawSolidClippedSubmodelPolygons (model_t *pmodel, mnode_t *topnode) +{ + int i, j, lindex; + vec_t dot; + msurface_t *psurf; + int numsurfaces; + bedge_t *pbedge; + medge_t *pedge, *pedges; + + // FIXME: use bounding-box-based frustum clipping info? + psurf = &pmodel->surfaces[pmodel->firstmodelsurface]; + numsurfaces = pmodel->nummodelsurfaces; + pedges = pmodel->edges; + + for (i=0 ; iplane; + + dot = DotProduct (modelorg, pplane->normal) - pplane->dist; + + // draw the polygon + if (( !(psurf->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) || + ((psurf->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON))) + continue; + + // FIXME: use bounding-box-based frustum clipping info? + + // copy the edges to bedges, flipping if necessary so always + // clockwise winding + // FIXME: if edges and vertices get caches, these assignments must move + // outside the loop, and overflow checking must be done here + pbverts = bverts; + pbedges = bedges; + numbverts = numbedges = 0; + pbedge = &bedges[numbedges]; + numbedges += psurf->numedges; + + for (j=0 ; jnumedges ; j++) + { + lindex = pmodel->surfedges[psurf->firstedge+j]; + + if (lindex > 0) + { + pedge = &pedges[lindex]; + pbedge[j].v[0] = &r_pcurrentvertbase[pedge->v[0]]; + pbedge[j].v[1] = &r_pcurrentvertbase[pedge->v[1]]; + } + else + { + lindex = -lindex; + pedge = &pedges[lindex]; + pbedge[j].v[0] = &r_pcurrentvertbase[pedge->v[1]]; + pbedge[j].v[1] = &r_pcurrentvertbase[pedge->v[0]]; + } + + pbedge[j].pnext = &pbedge[j+1]; + } + + pbedge[j-1].pnext = NULL; // mark end of edges + + if ( !( psurf->texinfo->flags & ( SURF_TRANS66 | SURF_TRANS33 ) ) ) + R_RecursiveClipBPoly (pbedge, topnode, psurf); + else + R_RenderBmodelFace( pbedge, psurf ); + } +} + + +/* +================ +R_DrawSubmodelPolygons + +All in one leaf +================ +*/ +void R_DrawSubmodelPolygons (model_t *pmodel, int clipflags, mnode_t *topnode) +{ + int i; + vec_t dot; + msurface_t *psurf; + int numsurfaces; + + // FIXME: use bounding-box-based frustum clipping info? + psurf = &pmodel->surfaces[pmodel->firstmodelsurface]; + numsurfaces = pmodel->nummodelsurfaces; + + for (i=0 ; iplane; + + dot = DotProduct (modelorg, pplane->normal) - pplane->dist; + + // draw the polygon + if (((psurf->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) || + (!(psurf->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON))) + { + r_currentkey = ((mleaf_t *)topnode)->key; + + // FIXME: use bounding-box-based frustum clipping info? + R_RenderFace (psurf, clipflags); + } + } +} + + +int c_drawnode; + +/* +================ +R_RecursiveWorldNode +================ +*/ +void R_RecursiveWorldNode (mnode_t *node, int clipflags) +{ + int c; + vec3_t acceptpt, rejectpt; + mleaf_t *pleaf; + + if (node->contents == CONTENTS_SOLID) + return; // solid + + if (node->visframe != r_visframecount) + return; + + // cull the clipping planes if not trivial accept + // FIXME: the compiler is doing a lousy job of optimizing here; it could be + // twice as fast in ASM + if (clipflags) + { + int i; + for (i=0 ; i<4 ; i++) + { + int *pindex; + float d; + if (! (clipflags & (1<minmaxs[pindex[0]]; + rejectpt[1] = (float)node->minmaxs[pindex[1]]; + rejectpt[2] = (float)node->minmaxs[pindex[2]]; + + d = DotProduct (rejectpt, view_clipplanes[i].normal); + d -= view_clipplanes[i].dist; + if (d <= 0) + return; + acceptpt[0] = (float)node->minmaxs[pindex[3+0]]; + acceptpt[1] = (float)node->minmaxs[pindex[3+1]]; + acceptpt[2] = (float)node->minmaxs[pindex[3+2]]; + + d = DotProduct (acceptpt, view_clipplanes[i].normal); + d -= view_clipplanes[i].dist; + + if (d >= 0) + clipflags &= ~(1<contents != -1) + { + msurface_t **mark; + pleaf = (mleaf_t *)node; + + // check for door connected areas + if (r_newrefdef.areabits) + { + if (! (r_newrefdef.areabits[pleaf->area>>3] & (1<<(pleaf->area&7)) ) ) + return; // not visible + } + + mark = pleaf->firstmarksurface; + c = pleaf->nummarksurfaces; + + if (c) + { + do + { + (*mark)->visframe = r_framecount; + mark++; + } while (--c); + } + + pleaf->key = r_currentkey; + r_currentkey++; // all bmodels in a leaf share the same key + } + else + { + float dot; + int side; + mplane_t *plane; + + // node is just a decision point, so go down the apropriate sides + // find which side of the node we are on + plane = node->plane; + + switch (plane->type) + { + case PLANE_X: + dot = modelorg[0] - plane->dist; + break; + case PLANE_Y: + dot = modelorg[1] - plane->dist; + break; + case PLANE_Z: + dot = modelorg[2] - plane->dist; + break; + default: + dot = DotProduct (modelorg, plane->normal) - plane->dist; + break; + } + + if (dot >= 0) + side = 0; + else + side = 1; + + // recurse down the children, front side first + R_RecursiveWorldNode (node->children[side], clipflags); + + // draw stuff + c = node->numsurfaces; + + if (c) + { + msurface_t *surf; + + surf = r_worldmodel->surfaces + node->firstsurface; + + if (dot < -BACKFACE_EPSILON) + { + do + { + if ((surf->flags & SURF_PLANEBACK) && + (surf->visframe == r_framecount)) + { + R_RenderFace (surf, clipflags); + } + + surf++; + } while (--c); + } + else if (dot > BACKFACE_EPSILON) + { + do + { + if (!(surf->flags & SURF_PLANEBACK) && + (surf->visframe == r_framecount)) + { + R_RenderFace (surf, clipflags); + } + + surf++; + } while (--c); + } + + // all surfaces on the same node share the same sequence number + r_currentkey++; + } + + // recurse down the back side + R_RecursiveWorldNode (node->children[!side], clipflags); + } +} + + + +/* +================ +R_RenderWorld +================ +*/ +void R_RenderWorld (void) +{ + + if (!r_drawworld->value) + return; + if ( r_newrefdef.rdflags & RDF_NOWORLDMODEL ) + return; + + c_drawnode=0; + + // auto cycle the world frame for texture animation + r_worldentity.frame = (int)(r_newrefdef.time*2); + currententity = &r_worldentity; + + VectorCopy (r_origin, modelorg); + currentmodel = r_worldmodel; + r_pcurrentvertbase = currentmodel->vertexes; + + R_RecursiveWorldNode (currentmodel->nodes, 15); +} diff --git a/src/client/refresh/soft/r_draw.c b/src/client/refresh/soft/r_draw.c new file mode 100644 index 00000000..f557d6a4 --- /dev/null +++ b/src/client/refresh/soft/r_draw.c @@ -0,0 +1,424 @@ +/* +Copyright (C) 1997-2001 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +// draw.c + +#include "r_local.h" + + +image_t *draw_chars; // 8*8 graphic characters + +//============================================================================= + +/* +================ +RE_Draw_FindPic +================ +*/ +image_t *RE_Draw_FindPic (char *name) +{ + image_t *image; + + if (name[0] != '/' && name[0] != '\\') + { + char fullname[MAX_QPATH]; + + Com_sprintf (fullname, sizeof(fullname), "pics/%s.pcx", name); + image = R_FindImage (fullname, it_pic); + } + else + image = R_FindImage (name+1, it_pic); + + return image; +} + + + +/* +=============== +Draw_InitLocal +=============== +*/ +void Draw_InitLocal (void) +{ + draw_chars = RE_Draw_FindPic ("conchars"); +} + + + +/* +================ +Draw_Char + +Draws one 8*8 graphics character +It can be clipped to the top of the screen to allow the console to be +smoothly scrolled off. +================ +*/ +void RE_Draw_CharScaled(int x, int y, int num, float scale) +{ + pixel_t *dest; + byte *source; + int drawline; + int row, col, u, xpos, ypos, iscale; + + iscale = (int) scale; + + num &= 255; + + if (num == 32 || num == 32+128) + return; + + if (y <= -8) + return; // totally off screen + + // if ( ( y + 8 ) >= vid.height ) + if ( ( y + 8 ) > vid.height ) // PGM - status text was missing in sw... + return; + + row = num>>4; + col = num&15; + source = draw_chars->pixels[0] + (row<<10) + (col<<3); + + if (y < 0) + { // clipped + drawline = 8 + y; + source -= 128*y; + y = 0; + } + else + drawline = 8; + + dest = vid_buffer + y * vid.width + x; + + while (drawline--) + { + for (ypos=0; ypos < iscale; ypos ++) + { + for(u=0; u < 8; u++) + { + if (source[u] != TRANSPARENT_COLOR) + for (xpos=0; xpos < iscale; xpos ++) + { + dest[u * iscale + xpos] = source[u]; + } + } + dest += vid.width; + } + source += 128; + } +} + +/* +============= +RE_Draw_GetPicSize +============= +*/ +void RE_Draw_GetPicSize (int *w, int *h, char *pic) +{ + image_t *gl; + + gl = RE_Draw_FindPic (pic); + if (!gl) + { + *w = *h = -1; + return; + } + *w = gl->width; + *h = gl->height; +} + +/* +============= +RE_Draw_StretchPicImplementation +============= +*/ +void RE_Draw_StretchPicImplementation (int x, int y, int w, int h, image_t *pic) +{ + pixel_t *dest; + byte *source; + int v, u; + int height; + int f, fstep; + int skip; + + if ((x < 0) || + (x + w > vid.width) || + (y + h > vid.height)) + { + ri.Sys_Error (ERR_FATAL,"Draw_Pic: bad coordinates"); + } + + height = h; + if (y < 0) + { + skip = -y; + height += y; + y = 0; + } + else + skip = 0; + + dest = vid_buffer + y * vid.width + x; + + for (v=0 ; vheight/h; + source = pic->pixels[0] + sv*pic->width; + if (w == pic->width) + memcpy (dest, source, w); + else + { + f = 0; + fstep = pic->width*0x10000/w; + for (u=0 ; u>16]; + f += fstep; + } + } + } +} + +/* +============= +RE_Draw_StretchPic +============= +*/ +void RE_Draw_StretchPic (int x, int y, int w, int h, char *name) +{ + image_t *pic; + + pic = RE_Draw_FindPic (name); + if (!pic) + { + R_Printf(PRINT_ALL, "Can't find pic: %s\n", name); + return; + } + RE_Draw_StretchPicImplementation (x, y, w, h, pic); +} + +/* +============= +RE_Draw_StretchRaw +============= +*/ +void RE_Draw_StretchRaw (int x, int y, int w, int h, int cols, int rows, byte *data) +{ + image_t pic; + + pic.pixels[0] = data; + pic.width = cols; + pic.height = rows; + RE_Draw_StretchPicImplementation (x, y, w, h, &pic); +} + +/* +============= +Draw_Pic +============= +*/ +void RE_Draw_PicScaled(int x, int y, char *name, float scale) +{ + image_t *pic; + pixel_t *dest; + byte *source; + int v, u, xpos, ypos, iscale; + int height; + + iscale = (int)scale; + pic = RE_Draw_FindPic (name); + if (!pic) + { + R_Printf(PRINT_ALL, "Can't find pic: %s\n", name); + return; + } + + if ((x < 0) || + (x + pic->width > vid.width) || + (y + pic->height > vid.height)) + { + R_Printf(PRINT_ALL, "Draw_Pic: bad coordinates\n"); + return; + } + + height = pic->height; + source = pic->pixels[0]; + if (y < 0) + { + height += y; + source += pic->width*-y; + y = 0; + } + + dest = vid_buffer + y * vid.width + x; + + if (!pic->transparent) + { + for (v=0; vwidth; u++) + { + for(xpos=0; xpos < iscale; xpos++) + { + dest[u * iscale + xpos] = source[u]; + } + } + dest += vid.width; + } + source += pic->width; + } + } + else + { + for (v=0; vwidth; u++) + { + if (source[u] != TRANSPARENT_COLOR) + for(xpos=0; xpos < iscale; xpos++) + { + dest[u * iscale + xpos] = source[u]; + } + } + dest += vid.width; + } + source += pic->width; + } + } +} + +/* +============= +RE_Draw_TileClear + +This repeats a 64*64 tile graphic to fill the screen around a sized down +refresh window. +============= +*/ +void RE_Draw_TileClear (int x, int y, int w, int h, char *name) +{ + int i, j; + byte *psrc; + pixel_t *pdest; + image_t *pic; + int x2; + + if (x < 0) + { + w += x; + x = 0; + } + if (y < 0) + { + h += y; + y = 0; + } + if (x + w > vid.width) + w = vid.width - x; + if (y + h > vid.height) + h = vid.height - y; + if (w <= 0 || h <= 0) + return; + + pic = RE_Draw_FindPic (name); + if (!pic) + { + R_Printf(PRINT_ALL, "Can't find pic: %s\n", name); + return; + } + x2 = x + w; + pdest = vid_buffer + y * vid.width; + for (i=0 ; ipixels[0] + pic->width * ((i+y)&63); + for (j=x ; j vid.width) + w = vid.width - x; + if (y+h > vid.height) + h = vid.height - y; + + if (x < 0) + { + w += x; + x = 0; + } + if (y < 0) + { + h += y; + y = 0; + } + + if (w < 0 || h < 0) + return; + + dest = vid_buffer + y * vid.width + x; + for (v=0 ; v +#include +#include "r_local.h" +/* +the complex cases add new polys on most lines, so dont optimize for keeping them the same +have multiple free span lists to try to get better coherence? +low depth complexity -- 1 to 3 or so + +have a sentinal at both ends? +*/ + + +edge_t *auxedges; +edge_t *r_edges, *edge_p, *edge_max; + +surf_t *surfaces, *surface_p, *surf_max; + +// surfaces are generated in back to front order by the bsp, so if a surf +// pointer is greater than another one, it should be drawn in front +// surfaces[1] is the background, and is used as the active surface stack + +edge_t **newedges; +edge_t **removeedges; + +espan_t *span_p, *max_span_p; + +int r_currentkey; + +shift20_t current_iv; +shift20_t edge_head_u_shift20, edge_tail_u_shift20; + +static void (*pdrawfunc)(void); + +edge_t edge_head; +edge_t edge_tail; +edge_t edge_aftertail; +edge_t edge_sentinel; + +float fv; + +static int miplevel; + +float scale_for_mip; +int ubasestep, errorterm, erroradjustup, erroradjustdown; + +// FIXME: should go away +extern void R_RotateBmodel (void); +extern void R_TransformFrustum (void); + +void R_GenerateSpans (void); +void R_GenerateSpansBackward (void); + +void R_LeadingEdge (edge_t *edge); +void R_LeadingEdgeBackwards (edge_t *edge); +void R_TrailingEdge (surf_t *surf, edge_t *edge); + +/* +=============================================================================== + +EDGE SCANNING + +=============================================================================== +*/ + +/* +============== +R_BeginEdgeFrame +============== +*/ +void R_BeginEdgeFrame (void) +{ + edge_p = r_edges; + edge_max = &r_edges[r_numallocatededges]; + + surface_p = &surfaces[2]; // background is surface 1, + // surface 0 is a dummy + surfaces[1].spans = NULL; // no background spans yet + surfaces[1].flags = SURF_DRAWBACKGROUND; + + // put the background behind everything in the world + if (sw_draworder->value) + { + pdrawfunc = R_GenerateSpansBackward; + surfaces[1].key = 0; + r_currentkey = 1; + } + else + { + pdrawfunc = R_GenerateSpans; + surfaces[1].key = 0x7FFfFFFF; + r_currentkey = 0; + } + + if ((r_refdef.vrectbottom - r_refdef.vrect.y) > 0) + { + memset(newedges + r_refdef.vrect.y, 0, + (r_refdef.vrectbottom - r_refdef.vrect.y) * sizeof(edge_t*)); + memset(removeedges + r_refdef.vrect.y, 0, + (r_refdef.vrectbottom - r_refdef.vrect.y) * sizeof(edge_t*)); + } +} + +/* +============== +R_InsertNewEdges + +Adds the edges in the linked list edgestoadd, adding them to the edges in the +linked list edgelist. edgestoadd is assumed to be sorted on u, and non-empty (this is actually newedges[v]). edgelist is assumed to be sorted on u, with a +sentinel at the end (actually, this is the active edge table starting at +edge_head.next). +============== +*/ +void R_InsertNewEdges (edge_t *edgestoadd, edge_t *edgelist) +{ + edge_t *next_edge; + + do + { + next_edge = edgestoadd->next; +edgesearch: + if (edgelist->u >= edgestoadd->u) + goto addedge; + edgelist=edgelist->next; + if (edgelist->u >= edgestoadd->u) + goto addedge; + edgelist=edgelist->next; + if (edgelist->u >= edgestoadd->u) + goto addedge; + edgelist=edgelist->next; + if (edgelist->u >= edgestoadd->u) + goto addedge; + edgelist=edgelist->next; + goto edgesearch; + + // insert edgestoadd before edgelist +addedge: + edgestoadd->next = edgelist; + edgestoadd->prev = edgelist->prev; + edgelist->prev->next = edgestoadd; + edgelist->prev = edgestoadd; + } while ((edgestoadd = next_edge) != NULL); +} + +/* +============== +R_RemoveEdges +============== +*/ +void R_RemoveEdges (edge_t *pedge) +{ + + do + { + pedge->next->prev = pedge->prev; + pedge->prev->next = pedge->next; + } while ((pedge = pedge->nextremove) != NULL); +} + +/* +============== +R_StepActiveU +============== +*/ +void R_StepActiveU (edge_t *pedge) +{ + edge_t *pnext_edge, *pwedge; + + while (1) + { + pedge->u += pedge->u_step; + if (pedge->u < pedge->prev->u) + { + if (pedge == &edge_aftertail) + return; + + if (pedge->prev == &edge_head) + { + R_Printf(PRINT_ALL,"Already in head.\n"); + } + + // push it back to keep it sorted + pnext_edge = pedge->next; + + // pull the edge out of the edge list + pedge->next->prev = pedge->prev; + pedge->prev->next = pedge->next; + + // find out where the edge goes in the edge list + pwedge = pedge->prev->prev; + + while (pwedge && (pwedge->u > pedge->u)) + { + pwedge = pwedge->prev; + } + + // put the edge back into the edge list + pedge->next = pwedge->next; + pedge->prev = pwedge; + pedge->next->prev = pedge; + pwedge->next = pedge; + + pedge = pnext_edge; + if (pedge == &edge_tail) + return; + } + else + { + pedge = pedge->next; + } + } +} + +/* +============== +R_CleanupSpan +============== +*/ +void R_CleanupSpan (void) +{ + surf_t *surf; + shift20_t iu; + espan_t *span; + + // now that we've reached the right edge of the screen, we're done with any + // unfinished surfaces, so emit a span for whatever's on top + surf = surfaces[1].next; + iu = edge_tail_u_shift20; + if (iu > surf->last_u) + { + span = span_p++; + span->u = surf->last_u; + span->count = iu - span->u; + span->v = current_iv; + span->pnext = surf->spans; + surf->spans = span; + } + + // reset spanstate for all surfaces in the surface stack + do + { + surf->spanstate = 0; + surf = surf->next; + } while (surf != &surfaces[1]); +} + + +/* +============== +R_LeadingEdgeBackwards +============== +*/ +void R_LeadingEdgeBackwards (edge_t *edge) +{ + espan_t *span; + surf_t *surf, *surf2; + shift20_t iu; + + // it's adding a new surface in, so find the correct place + surf = &surfaces[edge->surfs[1]]; + + // don't start a span if this is an inverted span, with the end + // edge preceding the start edge (that is, we've already seen the + // end edge) + if (++surf->spanstate == 1) + { + surf2 = surfaces[1].next; + + if (surf->key > surf2->key) + goto newtop; + + // if it's two surfaces on the same plane, the one that's already + // active is in front, so keep going unless it's a bmodel + if (surf->insubmodel && (surf->key == surf2->key)) + { + // must be two bmodels in the same leaf; don't care, because they'll + // never be farthest anyway + goto newtop; + } + +continue_search: + do + { + surf2 = surf2->next; + } while (surf->key < surf2->key); + + if (surf->key == surf2->key) + { + // if it's two surfaces on the same plane, the one that's already + // active is in front, so keep going unless it's a bmodel + if (!surf->insubmodel) + goto continue_search; + + // must be two bmodels in the same leaf; don't care which is really + // in front, because they'll never be farthest anyway + } + + goto gotposition; + +newtop: + // emit a span (obscures current top) + iu = edge->u >> shift_size; + + if (iu > surf2->last_u) + { + span = span_p++; + span->u = surf2->last_u; + span->count = iu - span->u; + span->v = current_iv; + span->pnext = surf2->spans; + surf2->spans = span; + } + + // set last_u on the new span + surf->last_u = iu; + +gotposition: + // insert before surf2 + surf->next = surf2; + surf->prev = surf2->prev; + surf2->prev->next = surf; + surf2->prev = surf; + } +} + + +/* +============== +R_TrailingEdge +============== +*/ +void R_TrailingEdge (surf_t *surf, edge_t *edge) +{ + espan_t *span; + + // don't generate a span if this is an inverted span, with the end + // edge preceding the start edge (that is, we haven't seen the + // start edge yet) + if (--surf->spanstate == 0) + { + if (surf == surfaces[1].next) + { + shift20_t iu; + + // emit a span (current top going away) + iu = edge->u >> shift_size; + if (iu > surf->last_u) + { + span = span_p++; + span->u = surf->last_u; + span->count = iu - span->u; + span->v = current_iv; + span->pnext = surf->spans; + surf->spans = span; + } + + // set last_u on the surface below + surf->next->last_u = iu; + } + + surf->prev->next = surf->next; + surf->next->prev = surf->prev; + } +} + +/* +============== +R_LeadingEdge +============== +*/ +void R_LeadingEdge (edge_t *edge) +{ + if (edge->surfs[1]) + { + espan_t *span; + surf_t *surf, *surf2; + shift20_t iu; + float fu, newzi, testzi, newzitop, newzibottom; + + // it's adding a new surface in, so find the correct place + surf = &surfaces[edge->surfs[1]]; + + // don't start a span if this is an inverted span, with the end + // edge preceding the start edge (that is, we've already seen the + // end edge) + if (++surf->spanstate == 1) + { + surf2 = surfaces[1].next; + + if (surf->key < surf2->key) + goto newtop; + + // if it's two surfaces on the same plane, the one that's already + // active is in front, so keep going unless it's a bmodel + if (surf->insubmodel && (surf->key == surf2->key)) + { + // must be two bmodels in the same leaf; sort on 1/z + fu = (float)(edge->u - (1<d_ziorigin + fv*surf->d_zistepv + + fu*surf->d_zistepu; + newzibottom = newzi * 0.99; + + testzi = surf2->d_ziorigin + fv*surf2->d_zistepv + + fu*surf2->d_zistepu; + + if (newzibottom >= testzi) + { + goto newtop; + } + + newzitop = newzi * 1.01; + if (newzitop >= testzi) + { + if (surf->d_zistepu >= surf2->d_zistepu) + { + goto newtop; + } + } + } + +continue_search: + + do + { + surf2 = surf2->next; + } while (surf->key > surf2->key); + + if (surf->key == surf2->key) + { + // if it's two surfaces on the same plane, the one that's already + // active is in front, so keep going unless it's a bmodel + if (!surf->insubmodel) + goto continue_search; + + // must be two bmodels in the same leaf; sort on 1/z + fu = (float)(edge->u - (1<d_ziorigin + fv*surf->d_zistepv + + fu*surf->d_zistepu; + newzibottom = newzi * 0.99; + + testzi = surf2->d_ziorigin + fv*surf2->d_zistepv + + fu*surf2->d_zistepu; + + if (newzibottom >= testzi) + { + goto gotposition; + } + + newzitop = newzi * 1.01; + if (newzitop >= testzi) + { + if (surf->d_zistepu >= surf2->d_zistepu) + { + goto gotposition; + } + } + + goto continue_search; + } + + goto gotposition; + +newtop: + // emit a span (obscures current top) + iu = edge->u >> shift_size; + + if (iu > surf2->last_u) + { + span = span_p++; + span->u = surf2->last_u; + span->count = iu - span->u; + span->v = current_iv; + span->pnext = surf2->spans; + surf2->spans = span; + } + + // set last_u on the new span + surf->last_u = iu; + +gotposition: + // insert before surf2 + surf->next = surf2; + surf->prev = surf2->prev; + surf2->prev->next = surf; + surf2->prev = surf; + } + } +} + +/* +============== +R_GenerateSpans +============== +*/ +void R_GenerateSpans (void) +{ + edge_t *edge; + surf_t *surf; + + // clear active surfaces to just the background surface + surfaces[1].next = surfaces[1].prev = &surfaces[1]; + surfaces[1].last_u = edge_head_u_shift20; + + // generate spans + for (edge=edge_head.next ; edge != &edge_tail; edge=edge->next) + { + if (edge->surfs[0]) + { + // it has a left surface, so a surface is going away for this span + surf = &surfaces[edge->surfs[0]]; + + R_TrailingEdge (surf, edge); + + if (!edge->surfs[1]) + continue; + } + + R_LeadingEdge (edge); + } + + R_CleanupSpan (); +} + +/* +============== +R_GenerateSpansBackward +============== +*/ +void R_GenerateSpansBackward (void) +{ + edge_t *edge; + + // clear active surfaces to just the background surface + surfaces[1].next = surfaces[1].prev = &surfaces[1]; + surfaces[1].last_u = edge_head_u_shift20; + + // generate spans + for (edge=edge_head.next ; edge != &edge_tail; edge=edge->next) + { + if (edge->surfs[0]) + R_TrailingEdge (&surfaces[edge->surfs[0]], edge); + + if (edge->surfs[1]) + R_LeadingEdgeBackwards (edge); + } + + R_CleanupSpan (); +} + +/* +============== +R_ScanEdges + +Input: +newedges[] array + this has links to edges, which have links to surfaces + +Output: +Each surface has a linked list of its visible spans + +============== +*/ +void R_ScanEdges (void) +{ + shift20_t iv, bottom; + espan_t *basespan_p; + surf_t *s; + + basespan_p = edge_basespans; + max_span_p = edge_basespans + vid.width * 2 - r_refdef.vrect.width; + if ((vid.width * 2 - r_refdef.vrect.width) < 0) + { + R_Printf(PRINT_ALL,"No space in edge_basespans\n"); + return; + } + + span_p = basespan_p; + + // clear active edges to just the background edges around the whole screen + // FIXME: most of this only needs to be set up once + edge_head.u = r_refdef.vrect.x << shift_size; + edge_head_u_shift20 = edge_head.u >> shift_size; + edge_head.u_step = 0; + edge_head.prev = NULL; + edge_head.next = &edge_tail; + edge_head.surfs[0] = 0; + edge_head.surfs[1] = 1; + + edge_tail.u = (r_refdef.vrectright << shift_size) + (1 << shift_size) - 1; + edge_tail_u_shift20 = edge_tail.u >> shift_size; + edge_tail.u_step = 0; + edge_tail.prev = &edge_head; + edge_tail.next = &edge_aftertail; + edge_tail.surfs[0] = 1; + edge_tail.surfs[1] = 0; + + edge_aftertail.u = -1; // force a move + edge_aftertail.u_step = 0; + edge_aftertail.next = &edge_sentinel; + edge_aftertail.prev = &edge_tail; + + // FIXME: do we need this now that we clamp x in r_draw.c? + edge_sentinel.u = INT_MAX; // make sure nothing sorts past this + edge_sentinel.prev = &edge_aftertail; + + // + // process all scan lines + // + bottom = r_refdef.vrectbottom - 1; + + for (iv=r_refdef.vrect.y ; iv max_span_p) + { + D_DrawSurfaces (); + + // clear the surface span pointers + for (s = &surfaces[1] ; sspans = NULL; + + span_p = basespan_p; + } + + if (removeedges[iv]) + R_RemoveEdges (removeedges[iv]); + + if (edge_head.next != &edge_tail) + R_StepActiveU (edge_head.next); + } + + // do the last scan (no need to step or sort or remove on the last scan) + current_iv = iv; + fv = (float)iv; + + // mark that the head (background start) span is pre-included + surfaces[1].spanstate = 1; + + if (newedges[iv]) + R_InsertNewEdges (newedges[iv], edge_head.next); + + (*pdrawfunc) (); + + // draw whatever's left in the span list + D_DrawSurfaces (); +} + + +/* +========================================================================= + +SURFACE FILLING + +========================================================================= +*/ + +msurface_t *pface; +surfcache_t *pcurrentcache; +vec3_t transformed_modelorg; +vec3_t world_transformed_modelorg; +vec3_t local_modelorg; + +/* +============= +D_MipLevelForScale +============= +*/ +int D_MipLevelForScale (float scale) +{ + int lmiplevel; + + if (scale >= d_scalemip[0] ) + lmiplevel = 0; + else if (scale >= d_scalemip[1] ) + lmiplevel = 1; + else if (scale >= d_scalemip[2] ) + lmiplevel = 2; + else + lmiplevel = 3; + + if (lmiplevel < d_minmip) + lmiplevel = d_minmip; + + return lmiplevel; +} + + +/* +============== +D_FlatFillSurface + +Simple single color fill with no texture mapping +============== +*/ +void D_FlatFillSurface (surf_t *surf, int color) +{ + espan_t *span; + + for (span=surf->spans ; span ; span=span->pnext) + { + pixel_t *pdest; + shift20_t u, u2; + + pdest = d_viewbuffer + r_screenwidth*span->v; + u = span->u; + u2 = span->u + span->count - 1; + for ( ; u <= u2 ; u++) + pdest[u] = color; + } +} + + +/* +============== +D_CalcGradients +============== +*/ +void D_CalcGradients (msurface_t *pface) +{ + float mipscale; + vec3_t p_temp1; + vec3_t p_saxis, p_taxis; + float t; + + mipscale = 1.0 / (float)(1 << miplevel); + + TransformVector (pface->texinfo->vecs[0], p_saxis); + TransformVector (pface->texinfo->vecs[1], p_taxis); + + t = xscaleinv * mipscale; + d_sdivzstepu = p_saxis[0] * t; + d_tdivzstepu = p_taxis[0] * t; + + t = yscaleinv * mipscale; + d_sdivzstepv = -p_saxis[1] * t; + d_tdivzstepv = -p_taxis[1] * t; + + d_sdivzorigin = p_saxis[2] * mipscale - xcenter * d_sdivzstepu - + ycenter * d_sdivzstepv; + d_tdivzorigin = p_taxis[2] * mipscale - xcenter * d_tdivzstepu - + ycenter * d_tdivzstepv; + + VectorScale (transformed_modelorg, mipscale, p_temp1); + + t = 0x10000*mipscale; + sadjust = ((int)(DotProduct (p_temp1, p_saxis) * 0x10000 + 0.5)) - + ((pface->texturemins[0] << 16) >> miplevel) + + pface->texinfo->vecs[0][3]*t; + tadjust = ((int)(DotProduct (p_temp1, p_taxis) * 0x10000 + 0.5)) - + ((pface->texturemins[1] << 16) >> miplevel) + + pface->texinfo->vecs[1][3]*t; + + // PGM - changing flow speed for non-warping textures. + if (pface->texinfo->flags & SURF_FLOWING) + { + if(pface->texinfo->flags & SURF_WARP) + sadjust += 0x10000 * (-128 * ( (r_newrefdef.time * 0.25) - (int)(r_newrefdef.time * 0.25) )); + else + sadjust += 0x10000 * (-128 * ( (r_newrefdef.time * 0.77) - (int)(r_newrefdef.time * 0.77) )); + } + // PGM + + // + // -1 (-epsilon) so we never wander off the edge of the texture + // + bbextents = ((pface->extents[0] << 16) >> miplevel) - 1; + bbextentt = ((pface->extents[1] << 16) >> miplevel) - 1; +} + + +/* +============== +D_BackgroundSurf + +The grey background filler seen when there is a hole in the map +============== +*/ +void D_BackgroundSurf (surf_t *s) +{ + // set up a gradient for the background surface that places it + // effectively at infinity distance from the viewpoint + d_zistepu = 0; + d_zistepv = 0; + d_ziorigin = -0.9; + + D_FlatFillSurface (s, (int)sw_clearcolor->value & 0xFF); + D_DrawZSpans (s->spans); +} + +/* +================= +D_TurbulentSurf +================= +*/ +void D_TurbulentSurf (surf_t *s) +{ + d_zistepu = s->d_zistepu; + d_zistepv = s->d_zistepv; + d_ziorigin = s->d_ziorigin; + + pface = s->msurf; + miplevel = 0; + cacheblock = pface->texinfo->image->pixels[0]; + cachewidth = 64; + + if (s->insubmodel) + { + // FIXME: we don't want to do all this for every polygon! + // TODO: store once at start of frame + currententity = s->entity; //FIXME: make this passed in to + // R_RotateBmodel () + VectorSubtract (r_origin, currententity->origin, + local_modelorg); + TransformVector (local_modelorg, transformed_modelorg); + + R_RotateBmodel (); // FIXME: don't mess with the frustum, + // make entity passed in + } + + D_CalcGradients (pface); + + //============ + //PGM + // textures that aren't warping are just flowing. Use NonTurbulent8 instead + if(!(pface->texinfo->flags & SURF_WARP)) + NonTurbulent8 (s->spans); + else + Turbulent8 (s->spans); + //PGM + //============ + + D_DrawZSpans (s->spans); + + if (s->insubmodel) + { + // + // restore the old drawing state + // FIXME: we don't want to do this every time! + // TODO: speed up + // + currententity = NULL; // &r_worldentity; + VectorCopy (world_transformed_modelorg, + transformed_modelorg); + VectorCopy (base_vpn, vpn); + VectorCopy (base_vup, vup); + VectorCopy (base_vright, vright); + R_TransformFrustum (); + } +} + +/* +============== +D_SkySurf +============== +*/ +void D_SkySurf (surf_t *s) +{ + pface = s->msurf; + miplevel = 0; + if (!pface->texinfo->image) + return; + cacheblock = pface->texinfo->image->pixels[0]; + cachewidth = 256; + + d_zistepu = s->d_zistepu; + d_zistepv = s->d_zistepv; + d_ziorigin = s->d_ziorigin; + + D_CalcGradients (pface); + + D_DrawSpans16 (s->spans); + + // set up a gradient for the background surface that places it + // effectively at infinity distance from the viewpoint + d_zistepu = 0; + d_zistepv = 0; + d_ziorigin = -0.9; + + D_DrawZSpans (s->spans); +} + +/* +============== +D_SolidSurf + +Normal surface cached, texture mapped surface +============== +*/ +void D_SolidSurf (surf_t *s) +{ + d_zistepu = s->d_zistepu; + d_zistepv = s->d_zistepv; + d_ziorigin = s->d_ziorigin; + + if (s->insubmodel) + { + // FIXME: we don't want to do all this for every polygon! + // TODO: store once at start of frame + currententity = s->entity; // FIXME: make this passed in to + // R_RotateBmodel () + VectorSubtract (r_origin, currententity->origin, local_modelorg); + TransformVector (local_modelorg, transformed_modelorg); + + R_RotateBmodel (); // FIXME: don't mess with the frustum, + // make entity passed in + } + else + currententity = &r_worldentity; + + pface = s->msurf; + miplevel = D_MipLevelForScale(s->nearzi * scale_for_mip * pface->texinfo->mipadjust); + + // FIXME: make this passed in to D_CacheSurface + pcurrentcache = D_CacheSurface (pface, miplevel); + + cacheblock = (pixel_t *)pcurrentcache->data; + cachewidth = pcurrentcache->width; + + D_CalcGradients (pface); + + D_DrawSpans16 (s->spans); + + D_DrawZSpans (s->spans); + + if (s->insubmodel) + { + // + // restore the old drawing state + // FIXME: we don't want to do this every time! + // TODO: speed up + // + VectorCopy (world_transformed_modelorg, + transformed_modelorg); + VectorCopy (base_vpn, vpn); + VectorCopy (base_vup, vup); + VectorCopy (base_vright, vright); + R_TransformFrustum (); + currententity = NULL; //&r_worldentity; + } +} + +/* +============= +D_DrawflatSurfaces + +To allow developers to see the polygon carving of the world +============= +*/ +void D_DrawflatSurfaces (void) +{ + surf_t *s; + int color = 0; + + for (s = &surfaces[1] ; sspans) + continue; + + d_zistepu = s->d_zistepu; + d_zistepv = s->d_zistepv; + d_ziorigin = s->d_ziorigin; + + // make a stable color for each surface by taking the low + // bits of the msurface pointer + D_FlatFillSurface (s, color & 0xFF); + D_DrawZSpans (s->spans); + + color ++; + } +} + +/* +============== +D_DrawSurfaces + +Rasterize all the span lists. Guaranteed zero overdraw. +May be called more than once a frame if the surf list overflows (higher res) +============== +*/ +void D_DrawSurfaces (void) +{ + // currententity = NULL; + // &r_worldentity; + VectorSubtract (r_origin, vec3_origin, modelorg); + TransformVector (modelorg, transformed_modelorg); + VectorCopy (transformed_modelorg, world_transformed_modelorg); + + if (!sw_drawflat->value) + { + surf_t *s; + + for (s = &surfaces[1] ; sspans) + continue; + + r_drawnpolycount++; + + if (! (s->flags & (SURF_DRAWSKYBOX|SURF_DRAWBACKGROUND|SURF_DRAWTURB) ) ) + D_SolidSurf (s); + else if (s->flags & SURF_DRAWSKYBOX) + D_SkySurf (s); + else if (s->flags & SURF_DRAWBACKGROUND) + D_BackgroundSurf (s); + else if (s->flags & SURF_DRAWTURB) + D_TurbulentSurf (s); + } + } + else + D_DrawflatSurfaces (); + + currententity = NULL; //&r_worldentity; + VectorSubtract (r_origin, vec3_origin, modelorg); + R_TransformFrustum (); +} diff --git a/src/client/refresh/soft/r_image.c b/src/client/refresh/soft/r_image.c new file mode 100644 index 00000000..fbb9fc33 --- /dev/null +++ b/src/client/refresh/soft/r_image.c @@ -0,0 +1,315 @@ +/* +Copyright (C) 1997-2001 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "r_local.h" + + +#define MAX_RIMAGES 1024 +image_t r_images[MAX_RIMAGES]; +int numr_images; + + +/* +=============== +R_ImageList_f +=============== +*/ +void R_ImageList_f (void) +{ + int i; + image_t *image; + int texels; + + R_Printf(PRINT_ALL, "------------------\n"); + texels = 0; + + for (i=0, image=r_images ; iregistration_sequence <= 0) + continue; + texels += image->width*image->height; + switch (image->type) + { + case it_skin: + R_Printf(PRINT_ALL, "M"); + break; + case it_sprite: + R_Printf(PRINT_ALL, "S"); + break; + case it_wall: + R_Printf(PRINT_ALL, "W"); + break; + case it_pic: + R_Printf(PRINT_ALL, "P"); + break; + default: + R_Printf(PRINT_ALL, " "); + break; + } + + R_Printf(PRINT_ALL, " %3i %3i : %s\n", + image->width, image->height, image->name); + } + R_Printf(PRINT_ALL, "Total texel count: %i\n", texels); +} + +//======================================================= + +image_t *R_FindFreeImage (void) +{ + image_t *image; + int i; + + // find a free image_t + for (i=0, image=r_images ; iregistration_sequence) + break; + } + if (i == numr_images) + { + if (numr_images == MAX_RIMAGES) + ri.Sys_Error (ERR_DROP, "MAX_RIMAGES"); + numr_images++; + } + image = &r_images[i]; + + return image; +} + +/* +================ +R_LoadPic + +================ +*/ +image_t *R_LoadPic (char *name, byte *pic, int width, int height, imagetype_t type) +{ + image_t *image; + int i, c; + + image = R_FindFreeImage (); + if (strlen(name) >= sizeof(image->name)) + ri.Sys_Error(ERR_DROP, "Draw_LoadPic: \"%s\" is too long", name); + strcpy (image->name, name); + image->registration_sequence = registration_sequence; + + image->width = width; + image->height = height; + image->type = type; + + c = width*height; + image->pixels[0] = malloc (c); + image->transparent = false; + for (i=0 ; itransparent = true; + image->pixels[0][i] = b; + } + + return image; +} + +/* +================ +R_LoadWal +================ +*/ +image_t *R_LoadWal (char *name) +{ + miptex_t *mt; + int ofs; + image_t *image; + int size; + + ri.FS_LoadFile (name, (void **)&mt); + if (!mt) + { + R_Printf(PRINT_ALL, "R_LoadWal: can't load %s\n", name); + return r_notexture_mip; + } + + image = R_FindFreeImage (); + strcpy (image->name, name); + image->width = LittleLong (mt->width); + image->height = LittleLong (mt->height); + image->type = it_wall; + image->registration_sequence = registration_sequence; + + size = image->width*image->height * (256+64+16+4)/256; + image->pixels[0] = malloc (size); + image->pixels[1] = image->pixels[0] + image->width*image->height; + image->pixels[2] = image->pixels[1] + image->width*image->height/4; + image->pixels[3] = image->pixels[2] + image->width*image->height/16; + + ofs = LittleLong (mt->offsets[0]); + memcpy ( image->pixels[0], (byte *)mt + ofs, size); + + ri.FS_FreeFile ((void *)mt); + + return image; +} + + +/* +=============== +R_FindImage + +Finds or loads the given image +=============== +*/ +image_t *R_FindImage (char *name, imagetype_t type) +{ + image_t *image; + int i, len; + byte *pic, *palette; + int width, height; + char *ptr; + + if (!name) + return NULL; // ri.Sys_Error (ERR_DROP, "R_FindImage: NULL name"); + len = strlen(name); + if (len<5) + return NULL; // ri.Sys_Error (ERR_DROP, "R_FindImage: bad name: %s", name); + +#ifndef _WIN32 + // fix backslashes + while ((ptr=strchr(name,'\\'))) { + *ptr = '/'; + } +#endif + + // look for it + for (i=0, image=r_images ; iname)) + { + image->registration_sequence = registration_sequence; + return image; + } + } + + // + // load the pic from disk + // + pic = NULL; + palette = NULL; + if (!strcmp(name+len-4, ".pcx")) + { + LoadPCX (name, &pic, &palette, &width, &height); + if (!pic) + return NULL; // ri.Sys_Error (ERR_DROP, "R_FindImage: can't load %s", name); + image = R_LoadPic (name, pic, width, height, type); + } + else if (!strcmp(name+len-4, ".wal")) + { + image = R_LoadWal (name); + } + else if (!strcmp(name+len-4, ".tga")) + return NULL; // ri.Sys_Error (ERR_DROP, "R_FindImage: can't load %s in software renderer", name); + else + return NULL; // ri.Sys_Error (ERR_DROP, "R_FindImage: bad extension on: %s", name); + + if (pic) + free(pic); + if (palette) + free(palette); + + return image; +} + + + +/* +=============== +RE_RegisterSkin +=============== +*/ +struct image_s *RE_RegisterSkin (char *name) +{ + return R_FindImage (name, it_skin); +} + + +/* +================ +R_FreeUnusedImages + +Any image that was not touched on this registration sequence +will be freed. +================ +*/ +void R_FreeUnusedImages (void) +{ + int i; + image_t *image; + + for (i=0, image=r_images ; iregistration_sequence == registration_sequence) + { + Com_PageInMemory ((byte *)image->pixels[0], image->width*image->height); + continue; // used this sequence + } + if (!image->registration_sequence) + continue; // free texture + if (image->type == it_pic) + continue; // don't free pics + // free it + free (image->pixels[0]); // the other mip levels just follow + memset (image, 0, sizeof(*image)); + } +} + + + +/* +=============== +R_InitImages +=============== +*/ +void R_InitImages (void) +{ + registration_sequence = 1; +} + +/* +=============== +R_ShutdownImages +=============== +*/ +void R_ShutdownImages (void) +{ + int i; + image_t *image; + + for (i=0, image=r_images ; iregistration_sequence) + continue; // free texture + // free it + free (image->pixels[0]); // the other mip levels just follow + memset (image, 0, sizeof(*image)); + } +} diff --git a/src/client/refresh/soft/r_light.c b/src/client/refresh/soft/r_light.c new file mode 100644 index 00000000..9db0d2a4 --- /dev/null +++ b/src/client/refresh/soft/r_light.c @@ -0,0 +1,446 @@ +/* +Copyright (C) 1997-2001 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +// r_light.c + +#include "r_local.h" + +int r_dlightframecount; + + +/* +============================================================================= + +DYNAMIC LIGHTS + +============================================================================= +*/ + +/* +============= +R_MarkLights +============= +*/ +void R_MarkLights (dlight_t *light, int bit, mnode_t *node) +{ + mplane_t *splitplane; + float dist; + msurface_t *surf; + int i; + + if (node->contents != -1) + return; + + splitplane = node->plane; + dist = DotProduct (light->origin, splitplane->normal) - splitplane->dist; + + //===== + //PGM + i=light->intensity; + if(i<0) + i=-i; + //PGM + //===== + + if (dist > i) // PGM (dist > light->intensity) + { + R_MarkLights (light, bit, node->children[0]); + return; + } + if (dist < -i) // PGM (dist < -light->intensity) + { + R_MarkLights (light, bit, node->children[1]); + return; + } + + // mark the polygons + surf = r_worldmodel->surfaces + node->firstsurface; + for (i=0 ; inumsurfaces ; i++, surf++) + { + if (surf->dlightframe != r_dlightframecount) + { + surf->dlightbits = 0; + surf->dlightframe = r_dlightframecount; + } + surf->dlightbits |= bit; + } + + R_MarkLights (light, bit, node->children[0]); + R_MarkLights (light, bit, node->children[1]); +} + + +/* +============= +R_PushDlights +============= +*/ +void R_PushDlights (model_t *model) +{ + int i; + dlight_t *l; + + r_dlightframecount = r_framecount; + for (i=0, l = r_newrefdef.dlights ; inodes + model->firstnode); + } +} + + +/* +============================================================================= + +LIGHT SAMPLING + +============================================================================= +*/ + +vec3_t pointcolor; +mplane_t *lightplane; // used as shadow plane +vec3_t lightspot; + +int RecursiveLightPoint (mnode_t *node, vec3_t start, vec3_t end) +{ + float front, back, frac; + qboolean side; + mplane_t *plane; + vec3_t mid; + msurface_t *surf; + int s, t, ds, dt; + int i; + mtexinfo_t *tex; + byte *lightmap; + float *scales; + int maps; + float samp; + int r; + + if (node->contents != -1) + return -1; // didn't hit anything + + // calculate mid point + + // FIXME: optimize for axial + plane = node->plane; + front = DotProduct (start, plane->normal) - plane->dist; + back = DotProduct (end, plane->normal) - plane->dist; + side = front < 0; + + if ( (back < 0) == side) + return RecursiveLightPoint (node->children[side], start, end); + + frac = front / (front-back); + mid[0] = start[0] + (end[0] - start[0])*frac; + mid[1] = start[1] + (end[1] - start[1])*frac; + mid[2] = start[2] + (end[2] - start[2])*frac; + if (plane->type < 3) // axial planes + mid[plane->type] = plane->dist; + + // go down front side + r = RecursiveLightPoint (node->children[side], start, mid); + if (r >= 0) + return r; // hit something + + if ((back < 0) == side) + return -1; // didn't hit anything + + // check for impact on this node + VectorCopy (mid, lightspot); + lightplane = plane; + + surf = r_worldmodel->surfaces + node->firstsurface; + for (i=0 ; inumsurfaces ; i++, surf++) + { + if (surf->flags&(SURF_DRAWTURB|SURF_DRAWSKY)) + continue; // no lightmaps + + tex = surf->texinfo; + + s = DotProduct (mid, tex->vecs[0]) + tex->vecs[0][3]; + t = DotProduct (mid, tex->vecs[1]) + tex->vecs[1][3]; + if (s < surf->texturemins[0] || + t < surf->texturemins[1]) + continue; + + ds = s - surf->texturemins[0]; + dt = t - surf->texturemins[1]; + + if ( ds > surf->extents[0] || dt > surf->extents[1] ) + continue; + + if (!surf->samples) + return 0; + + ds >>= 4; + dt >>= 4; + + lightmap = surf->samples; + VectorCopy (vec3_origin, pointcolor); + if (lightmap) + { + lightmap += dt * ((surf->extents[0]>>4)+1) + ds; + + for (maps = 0 ; maps < MAXLIGHTMAPS && surf->styles[maps] != 255 ; + maps++) + { + samp = *lightmap * /* 0.5 * */ (1.0/255); // adjust for gl scale + scales = r_newrefdef.lightstyles[surf->styles[maps]].rgb; + VectorMA (pointcolor, samp, scales, pointcolor); + lightmap += ((surf->extents[0]>>4)+1) * + ((surf->extents[1]>>4)+1); + } + } + + return 1; + } + + // go down back side + return RecursiveLightPoint (node->children[!side], mid, end); +} + +/* +=============== +R_LightPoint +=============== +*/ +void R_LightPoint (vec3_t p, vec3_t color) +{ + vec3_t end; + float r; + int lnum; + dlight_t *dl; + vec3_t dist; + + if (!r_worldmodel->lightdata) + { + color[0] = color[1] = color[2] = 1.0; + return; + } + + end[0] = p[0]; + end[1] = p[1]; + end[2] = p[2] - 2048; + + r = RecursiveLightPoint (r_worldmodel->nodes, p, end); + + if (r == -1) + { + VectorCopy (vec3_origin, color); + } + else + { + VectorCopy (pointcolor, color); + } + + // + // add dynamic lights + // + for (lnum=0 ; lnumorigin, + dl->origin, + dist); + add = dl->intensity - VectorLength(dist); + add *= (1.0/256); + if (add > 0) + { + VectorMA (color, add, dl->color, color); + } + } +} + +//=================================================================== + + +unsigned blocklights[1024]; // allow some very large lightmaps + +/* +=============== +R_AddDynamicLights +=============== +*/ +void R_AddDynamicLights (void) +{ + msurface_t *surf; + int lnum; + int sd, td; + float dist, rad, minlight; + vec3_t impact, local; + int s, t; + int i; + int smax, tmax; + mtexinfo_t *tex; + dlight_t *dl; + int negativeLight; //PGM + + surf = r_drawsurf.surf; + smax = (surf->extents[0]>>4)+1; + tmax = (surf->extents[1]>>4)+1; + tex = surf->texinfo; + + for (lnum=0 ; lnumdlightbits & (1<intensity; + + //===== + //PGM + negativeLight = 0; + if(rad < 0) + { + negativeLight = 1; + rad = -rad; + } + //PGM + //===== + + dist = DotProduct (dl->origin, surf->plane->normal) - + surf->plane->dist; + rad -= fabs(dist); + minlight = 32; // dl->minlight; + if (rad < minlight) + continue; + minlight = rad - minlight; + + for (i=0 ; i<3 ; i++) + { + impact[i] = dl->origin[i] - + surf->plane->normal[i]*dist; + } + + local[0] = DotProduct (impact, tex->vecs[0]) + tex->vecs[0][3]; + local[1] = DotProduct (impact, tex->vecs[1]) + tex->vecs[1][3]; + + local[0] -= surf->texturemins[0]; + local[1] -= surf->texturemins[1]; + + for (t = 0 ; t td) + dist = sd + (td>>1); + else + dist = td + (sd>>1); + + //==== + //PGM + if(!negativeLight) + { + if (dist < minlight) + blocklights[t*smax + s] += (rad - dist)*256; + } + else + { + if (dist < minlight) + blocklights[t*smax + s] -= (rad - dist)*256; + if(blocklights[t*smax + s] < minlight) + blocklights[t*smax + s] = minlight; + } + //PGM + //==== + } + } + } +} + +/* +=============== +R_BuildLightMap + +Combine and scale multiple lightmaps into the 8.8 format in blocklights +=============== +*/ +void R_BuildLightMap (void) +{ + int smax, tmax; + int i, size; + byte *lightmap; + msurface_t *surf; + + surf = r_drawsurf.surf; + + smax = (surf->extents[0]>>4)+1; + tmax = (surf->extents[1]>>4)+1; + size = smax*tmax; + + if (r_fullbright->value || !r_worldmodel->lightdata) + { + for (i=0 ; isamples; + if (lightmap) + { + int maps; + + for (maps = 0 ; maps < MAXLIGHTMAPS && surf->styles[maps] != 255 ; + maps++) + { + unsigned scale; + + scale = r_drawsurf.lightadj[maps]; // 8.8 fraction + for (i=0 ; idlightframe == r_framecount) + R_AddDynamicLights (); + + // bound, invert, and shift + for (i=0 ; i> (8 - VID_CBITS); + + if (t < (1 << 6)) + t = (1 << 6); + + blocklights[i] = t; + } +} diff --git a/src/client/refresh/soft/r_local.h b/src/client/refresh/soft/r_local.h new file mode 100644 index 00000000..c7063389 --- /dev/null +++ b/src/client/refresh/soft/r_local.h @@ -0,0 +1,755 @@ +/* +Copyright (C) 1997-2001 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#ifndef __R_LOCAL__ +#define __R_LOCAL__ + +#include +#include +#include + +#include "../ref_shared.h" + +#include +#include +#include + +#define REF_VERSION "SOFT 0.01" + +// up / down +#define PITCH 0 + +// left / right +#define YAW 1 + +// fall over +#define ROLL 2 + + +/* + + skins will be outline flood filled and mip mapped + pics and sprites with alpha will be outline flood filled + pic won't be mip mapped + + model skin + sprite frame + wall texture + pic + +*/ + +typedef struct image_s +{ + char name[MAX_QPATH]; // game path, including extension + imagetype_t type; + int width, height; + qboolean transparent; // true if any 255 pixels in image + int registration_sequence; // 0 = free + byte *pixels[4]; // mip levels +} image_t; + + +//=================================================================== + +typedef unsigned char pixel_t; +typedef int shift20_t; +typedef int zvalue_t; + +typedef enum +{ + rserr_ok, + + rserr_invalid_fullscreen, + rserr_invalid_mode, + + rserr_unknown +} rserr_t; + +extern viddef_t vid; +extern pixel_t *vid_buffer; // invisible buffer +extern pixel_t *vid_colormap; // 256 * VID_GRADES size +extern pixel_t *vid_alphamap; // 256 * 256 translucency map +extern char shift_size; // shift size in fixed-point + +typedef struct +{ + vrect_t vrect; // subwindow in video for refresh + // FIXME: not need vrect next field here? + vrect_t aliasvrect; // scaled Alias version + shift20_t vrectright, vrectbottom; // right & bottom screen coords + shift20_t aliasvrectright, aliasvrectbottom; // scaled Alias versions + float vrectrightedge; // rightmost right edge we care about, + // for use in edge list + float fvrectx, fvrecty; // for floating-point compares + float fvrectx_adj, fvrecty_adj; // left and top edges, for clamping + shift20_t vrect_x_adj_shift20; // (vrect.x + 0.5 - epsilon) << 20 + shift20_t vrectright_adj_shift20; // (vrectright + 0.5 - epsilon) << 20 + float fvrectright_adj, fvrectbottom_adj; // right and bottom edges, for clamping + float fvrectright; // rightmost edge, for Alias clamping + float fvrectbottom; // bottommost edge, for Alias clamping + float horizontalFieldOfView; // at Z = 1.0, this many X is visible + // 2.0 = 90 degrees + float xOrigin; // should probably always be 0.5 + float yOrigin; // between be around 0.3 to 0.5 + + vec3_t vieworg; + vec3_t viewangles; + + int ambientlight; +} oldrefdef_t; + +extern oldrefdef_t r_refdef; + +#include "r_model.h" + +/* +==================================================== + + CONSTANTS + +==================================================== +*/ + +#define VID_CBITS 6 +#define VID_GRADES (1 << VID_CBITS) + + +// r_shared.h: general refresh-related stuff shared between the refresh and the +// driver + + +#define MAXVERTS 64 // max points in a surface polygon +#define MAXWORKINGVERTS (MAXVERTS+4) // max points in an intermediate + // polygon (while processing) + +#define WARP_WIDTH 320 +#define WARP_HEIGHT 240 + +#define PARTICLE_Z_CLIP 8.0 + +#define TRANSPARENT_COLOR 0xFF + +#define TURB_TEX_SIZE 64 // base turbulent texture size + +#define CYCLE 128 // turbulent cycle size + +#define SCANBUFFERPAD 0x1000 + +#define DS_SPAN_LIST_END -128 + +#define NUMSTACKEDGES 2000 +#define MINEDGES NUMSTACKEDGES +#define NUMSTACKSURFACES 1000 +#define MINSURFACES NUMSTACKSURFACES +#define MAXSPANS 3000 + +// flags in finalvert_t.flags +#define ALIAS_LEFT_CLIP 0x0001 +#define ALIAS_TOP_CLIP 0x0002 +#define ALIAS_RIGHT_CLIP 0x0004 +#define ALIAS_BOTTOM_CLIP 0x0008 +#define ALIAS_Z_CLIP 0x0010 +#define ALIAS_XY_CLIP_MASK 0x000F + +#define SURFCACHE_SIZE_AT_320X240 1024*768 + +#define BMODEL_FULLY_CLIPPED 0x10 // value returned by R_BmodelCheckBBox () + // if bbox is trivially rejected + +#define XCENTERING (1.0 / 2.0) +#define YCENTERING (1.0 / 2.0) + +#define CLIP_EPSILON 0.001 + +#define BACKFACE_EPSILON 0.01 + +#define NEAR_CLIP 0.01 + + +#define MAXALIASVERTS 2000 // TODO: tune this +#define ALIAS_Z_CLIP_PLANE 4 + +// turbulence stuff +#define AMP 8*0x10000 +#define AMP2 3 +#define SPEED 20 + + +/* +==================================================== + +TYPES + +==================================================== +*/ + +typedef struct +{ + float u, v; + float s, t; + float zi; +} emitpoint_t; + +/* +** if you change this structure be sure to change the #defines +** listed after it! +*/ +typedef struct finalvert_s { + int u, v, s, t; + int l; + int zi; + int flags; + float xyz[3]; // eye space +} finalvert_t; + +#define FINALVERT_V0 0 +#define FINALVERT_V1 4 +#define FINALVERT_V2 8 +#define FINALVERT_V3 12 +#define FINALVERT_V4 16 +#define FINALVERT_V5 20 +#define FINALVERT_FLAGS 24 +#define FINALVERT_X 28 +#define FINALVERT_Y 32 +#define FINALVERT_Z 36 +#define FINALVERT_SIZE 40 + +typedef struct +{ + void *pskin; + int skinwidth; + int skinheight; +} affinetridesc_t; + +typedef struct +{ + byte *surfdat; // destination for generated surface + int rowbytes; // destination logical width in bytes + msurface_t *surf; // description for surface to generate + fixed8_t lightadj[MAXLIGHTMAPS]; // adjust for lightmap levels for dynamic lighting + image_t *image; + int surfmip; // mipmapped ratio of surface texels / world pixels + int surfwidth; // in mipmapped texels + int surfheight; // in mipmapped texels +} drawsurf_t; + +typedef struct { + int ambientlight; + int shadelight; + float *plightvec; +} alight_t; + +// clipped bmodel edges +typedef struct bedge_s +{ + mvertex_t *v[2]; + struct bedge_s *pnext; +} bedge_t; + +typedef struct clipplane_s +{ + vec3_t normal; + float dist; + struct clipplane_s *next; + byte leftedge; + byte rightedge; + byte reserved[2]; +} clipplane_t; + +typedef struct surfcache_s +{ + struct surfcache_s *next; + struct surfcache_s **owner; // NULL is an empty chunk of memory + int lightadj[MAXLIGHTMAPS]; // checked for strobe flush + int dlight; + int size; // including header + unsigned width; + unsigned height; // DEBUG only needed for debug + float mipscale; + image_t *image; + byte data[4]; // width*height elements +} surfcache_t; + +typedef struct espan_s +{ + int u, v, count; + struct espan_s *pnext; +} espan_t; +extern espan_t *vid_polygon_spans; // space for spans in r_poly + +// used by the polygon drawer (R_POLY.C) and sprite setup code (R_SPRITE.C) +typedef struct +{ + int nump; + emitpoint_t *pverts; + byte *pixels; // image + int pixel_width; // image width + int pixel_height; // image height + vec3_t vup, vright, vpn; // in worldspace, for plane eq + float dist; + float s_offset, t_offset; + float viewer_position[3]; + void (*drawspanlet)(void); + int stipple_parity; +} polydesc_t; + +// FIXME: compress, make a union if that will help +// insubmodel is only 1, flags is fewer than 32, spanstate could be a byte +typedef struct surf_s +{ + struct surf_s *next; // active surface stack in r_edge.c + struct surf_s *prev; // used in r_edge.c for active surf stack + struct espan_s *spans; // pointer to linked list of spans to draw + int key; // sorting key (BSP order) + shift20_t last_u; // set during tracing + int spanstate; // 0 = not in span + // 1 = in span + // -1 = in inverted span (end before + // start) + int flags; // currentface flags + msurface_t *msurf; + entity_t *entity; + float nearzi; // nearest 1/z on surface, for mipmapping + qboolean insubmodel; + float d_ziorigin, d_zistepu, d_zistepv; + + int pad[2]; // to 64 bytes +} surf_t; + +typedef struct edge_s +{ + shift20_t u; + shift20_t u_step; + struct edge_s *prev, *next; + unsigned short surfs[2]; + struct edge_s *nextremove; + float nearzi; + medge_t *owner; +} edge_t; + + +/* +==================================================== + +VARS + +==================================================== +*/ +extern int d_spanpixcount; +extern int r_framecount; // sequence # of current frame since Quake + // started +extern float r_aliasuvscale; // scale-up factor for screen u and v + // on Alias vertices passed to driver +extern qboolean r_dowarp; + +extern affinetridesc_t r_affinetridesc; + +extern vec3_t r_pright, r_pup, r_ppn; + +void D_DrawSurfaces(void); +void R_DrawParticle(void); +void D_ViewChanged(void); +void D_WarpScreen(void); +void R_PolysetUpdateTables(void); + +//=======================================================================// + +// callbacks to Quake + +extern drawsurf_t r_drawsurf; + +void R_DrawSurface(void); + +extern int c_surf; + +extern pixel_t *r_warpbuffer; + +extern float scale_for_mip; + +extern qboolean d_roverwrapped; +extern surfcache_t *sc_rover; +extern surfcache_t *d_initial_rover; + +extern float d_sdivzstepu, d_tdivzstepu, d_zistepu; +extern float d_sdivzstepv, d_tdivzstepv, d_zistepv; +extern float d_sdivzorigin, d_tdivzorigin, d_ziorigin; + +extern int sadjust, tadjust; +extern int bbextents, bbextentt; + + +void D_DrawSpans16(espan_t *pspans); +void D_DrawZSpans(espan_t *pspans); +void Turbulent8(espan_t *pspan); +void NonTurbulent8(espan_t *pspan); //PGM + +surfcache_t *D_CacheSurface (msurface_t *surface, int miplevel); + +extern int d_vrectx, d_vrecty, d_vrectright_particle, d_vrectbottom_particle; + +extern int d_pix_min, d_pix_max, d_pix_shift; + +extern pixel_t *d_viewbuffer; +extern zvalue_t *d_pzbuffer; +extern unsigned int d_zrowbytes, d_zwidth; + +extern int d_minmip; +extern float d_scalemip[3]; + +//=================================================================== + +extern int cachewidth; +extern pixel_t *cacheblock; +extern int r_screenwidth; + +extern int r_drawnpolycount; + +extern int *sintable; +extern int *intsintable; +extern int *blanktable; // PGM + +extern vec3_t vup, base_vup; +extern vec3_t vpn, base_vpn; +extern vec3_t vright, base_vright; + +extern surf_t *surfaces, *surface_p, *surf_max; + +// surfaces are generated in back to front order by the bsp, so if a surf +// pointer is greater than another one, it should be drawn in front +// surfaces[1] is the background, and is used as the active surface stack. +// surfaces[0] is a dummy, because index 0 is used to indicate no surface +// attached to an edge_t + +//=================================================================== + +extern vec3_t sxformaxis[4]; // s axis transformed into viewspace +extern vec3_t txformaxis[4]; // t axis transformed into viewspac + +extern float xcenter, ycenter; +extern float xscale, yscale; +extern float xscaleinv, yscaleinv; +extern float xscaleshrink, yscaleshrink; + +extern void TransformVector(vec3_t in, vec3_t out); +extern void SetUpForLineScan(fixed8_t startvertu, fixed8_t startvertv, + fixed8_t endvertu, fixed8_t endvertv); + +extern int ubasestep, errorterm, erroradjustup, erroradjustdown; + +//=========================================================================== + +extern cvar_t *sw_aliasstats; +extern cvar_t *sw_clearcolor; +extern cvar_t *sw_drawflat; +extern cvar_t *sw_draworder; +extern cvar_t *sw_maxedges; +extern cvar_t *sw_maxsurfs; +extern cvar_t *sw_mipcap; +extern cvar_t *sw_mipscale; +extern cvar_t *sw_mode; +extern cvar_t *sw_reportsurfout; +extern cvar_t *sw_reportedgeout; +extern cvar_t *sw_stipplealpha; +extern cvar_t *sw_surfcacheoverride; +extern cvar_t *sw_waterwarp; + +extern cvar_t *r_fullbright; +extern cvar_t *r_lefthand; +extern cvar_t *r_drawentities; +extern cvar_t *r_drawworld; +extern cvar_t *r_dspeeds; +extern cvar_t *r_lerpmodels; + +extern cvar_t *r_speeds; + +extern cvar_t *r_lightlevel; //FIXME HACK + +extern cvar_t *vid_fullscreen; +extern cvar_t *vid_gamma; + + +extern clipplane_t view_clipplanes[4]; +extern int *pfrustum_indexes[4]; + + +//============================================================================= + +void R_RenderWorld(void); + +//============================================================================= + +extern mplane_t screenedge[4]; + +extern vec3_t r_origin; + +extern entity_t r_worldentity; +extern model_t *currentmodel; +extern entity_t *currententity; +extern vec3_t modelorg; +extern vec3_t r_entorigin; + +extern float verticalFieldOfView; +extern float xOrigin, yOrigin; + +extern int r_visframecount; + +extern msurface_t *r_alpha_surfaces; + +//============================================================================= + +void R_ClearPolyList(void); +void R_DrawPolyList(void); + +// +// current entity info +// +extern qboolean insubmodel; + +void R_DrawAlphaSurfaces(void); + +void R_DrawSprite(void); +void R_DrawBeam(entity_t *e); + +void R_RenderFace(msurface_t *fa, int clipflags); +void R_RenderBmodelFace(bedge_t *pedges, msurface_t *psurf); +void R_TransformPlane(mplane_t *p, float *normal, float *dist); +void R_TransformFrustum(void); +void R_DrawSurfaceBlock16(void); +void R_DrawSurfaceBlock8(void); + +void R_GenSkyTile(void *pdest); +void R_GenSkyTile16(void *pdest); +void R_Surf8Patch(void); +void R_Surf16Patch(void); +void R_DrawSubmodelPolygons(model_t *pmodel, int clipflags, mnode_t *topnode); +void R_DrawSolidClippedSubmodelPolygons(model_t *pmodel, mnode_t *topnode); + +void R_AddPolygonEdges(emitpoint_t *pverts, int numverts, int miplevel); +surf_t *R_GetSurf(void); +void R_AliasDrawModel(void); +void R_BeginEdgeFrame(void); +void R_ScanEdges(void); +void D_DrawSurfaces(void); +void R_InsertNewEdges(edge_t *edgestoadd, edge_t *edgelist); +void R_StepActiveU(edge_t *pedge); +void R_RemoveEdges(edge_t *pedge); +void R_PushDlights(model_t *model); + +extern void R_Surf8Start(void); +extern void R_Surf8End(void); +extern void R_Surf16Start(void); +extern void R_Surf16End(void); +extern void R_EdgeCodeStart (void); +extern void R_EdgeCodeEnd (void); + +extern void R_RotateBmodel (void); + +extern int c_faceclip; +extern int r_polycount; +extern int r_wholepolycount; + +extern int ubasestep, errorterm, erroradjustup, erroradjustdown; + +extern int sadjust, tadjust; +extern int bbextents, bbextentt; + +extern mvertex_t *r_ptverts, *r_ptvertsmax; + +extern float entity_rotation[3][3]; +extern int r_currentkey; +extern int r_currentbkey; + +void R_InitTurb (void); + +void R_DrawParticles (void); + +extern int r_amodels_drawn; +extern edge_t *auxedges; +extern int r_numallocatededges; +extern edge_t *r_edges, *edge_p, *edge_max; + +extern edge_t **newedges; +extern edge_t **removeedges; + +typedef struct { + pixel_t *pdest; + zvalue_t *pz; + int count; + pixel_t *ptex; + int sfrac, tfrac, light, zi; +} spanpackage_t; +extern spanpackage_t *triangle_spans; + +extern byte **warp_rowptr; +extern int *warp_column; +extern espan_t *edge_basespans; +extern finalvert_t *finalverts; + +extern int r_aliasblendcolor; + +extern float aliasxscale, aliasyscale, aliasxcenter, aliasycenter; + +extern int r_outofsurfaces; +extern int r_outofedges; + +extern mvertex_t *r_pcurrentvertbase; +extern int r_maxvalidedgeoffset; + +typedef struct +{ + finalvert_t *a, *b, *c; +} aliastriangleparms_t; + +extern aliastriangleparms_t aliastriangleparms; + +void R_DrawTriangle( void ); +//void R_DrawTriangle (finalvert_t *index0, finalvert_t *index1, finalvert_t *index2); +void R_AliasClipTriangle (finalvert_t *index0, finalvert_t *index1, finalvert_t *index2); + + +extern float r_time1; +extern float da_time1, da_time2; +extern float dp_time1, dp_time2, db_time1, db_time2, rw_time1, rw_time2; +extern float se_time1, se_time2, de_time1, de_time2, dv_time1, dv_time2; +extern int r_frustum_indexes[4*6]; +extern int r_maxsurfsseen, r_maxedgesseen, r_cnumsurfs; +extern qboolean r_surfsonstack; + +extern mleaf_t *r_viewleaf; +extern int r_viewcluster, r_oldviewcluster; + +extern int r_clipflags; +extern int r_dlightframecount; +extern qboolean r_fov_greater_than_90; + +extern image_t *r_notexture_mip; +extern model_t *r_worldmodel; + +void R_PrintAliasStats (void); +void R_PrintTimes (void); +void R_PrintDSpeeds (void); +void R_AnimateLight (void); +void R_LightPoint (vec3_t p, vec3_t color); +void R_SetupFrame (void); +void R_cshift_f (void); +void R_EmitEdge (mvertex_t *pv0, mvertex_t *pv1); +void R_ClipEdge (mvertex_t *pv0, mvertex_t *pv1, clipplane_t *clip); +void R_SplitEntityOnNode2 (mnode_t *node); + +extern refdef_t r_newrefdef; + +extern surfcache_t *sc_rover, *sc_base; + +extern void *colormap; + +//==================================================================== + +float R_DLightPoint (vec3_t p); + +void R_NewMap (void); +void R_Register (void); +void R_UnRegister (void); +void Draw_InitLocal(void); +qboolean RE_Init(void); +void RE_Shutdown(void); +void R_InitCaches(void); +void D_FlushCaches(void); + +void R_ScreenShot_f( void ); +void RE_BeginRegistration (char *map); +struct model_s *RE_RegisterModel (char *name); +void RE_EndRegistration (void); + +void RE_RenderFrame (refdef_t *fd); + +struct image_s *RE_Draw_FindPic (char *name); + +void RE_Draw_GetPicSize (int *w, int *h, char *name); +void RE_Draw_PicScaled (int x, int y, char *name, float factor); +void RE_Draw_StretchPic (int x, int y, int w, int h, char *name); +void RE_Draw_StretchRaw (int x, int y, int w, int h, int cols, int rows, byte *data); +void RE_Draw_CharScaled (int x, int y, int c, float scale); +void RE_Draw_TileClear (int x, int y, int w, int h, char *name); +void RE_Draw_Fill (int x, int y, int w, int h, int c); +void RE_Draw_FadeScreen (void); + +void Draw_GetPalette (void); + +void RE_BeginFrame( float camera_separation ); + +void RE_SetPalette( const unsigned char *palette ); + +extern unsigned d_8to24table[256]; // base + +void Sys_MakeCodeWriteable (unsigned long startaddr, unsigned long length); +void Sys_SetFPCW (void); + +void LoadPCX (char *filename, byte **pic, byte **palette, int *width, int *height); + +void R_InitImages(void); +void R_ShutdownImages(void); +image_t *R_FindImage(char *name, imagetype_t type); +void R_FreeUnusedImages(void); + +void R_GammaCorrectAndSetPalette(const unsigned char *pal); + +extern mtexinfo_t *sky_texinfo[6]; + +void R_InitSkyBox(void); + +typedef struct swstate_s +{ + qboolean fullscreen; + int prev_mode; // last valid SW mode + + unsigned char gammatable[256]; + unsigned char currentpalette[1024]; + +} swstate_t; + +void R_IMFlatShadedQuad( vec3_t a, vec3_t b, vec3_t c, vec3_t d, int color, float alpha ); + +extern swstate_t sw_state; + +/* +==================================================================== + +IMPORTED FUNCTIONS + +==================================================================== +*/ +extern refimport_t ri; + +/* +==================================================================== + +IMPLEMENTATION FUNCTIONS + +==================================================================== +*/ +void SWimp_BeginFrame(float camera_separation); +void RE_EndFrame(void); +int SWimp_Init(void); +void RE_SetPalette(const unsigned char *palette); +void SWimp_Shutdown(void ); +rserr_t SWimp_SetMode(int *pwidth, int *pheight, int mode, qboolean fullscreen); + +#endif diff --git a/src/client/refresh/soft/r_main.c b/src/client/refresh/soft/r_main.c new file mode 100644 index 00000000..944e5de7 --- /dev/null +++ b/src/client/refresh/soft/r_main.c @@ -0,0 +1,2074 @@ +/* +Copyright (C) 1997-2001 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +// r_main.c +#include + +#ifdef SDL2 +#include +#include + +#else // SDL1.2 +#include +#endif //SDL2 + +#include "r_local.h" + +viddef_t vid; +pixel_t *vid_buffer = NULL; +espan_t *vid_polygon_spans = NULL; +pixel_t *vid_colormap = NULL; +pixel_t *vid_alphamap = NULL; + +refimport_t ri; + +unsigned d_8to24table[256]; + +entity_t r_worldentity; + +char skyname[MAX_QPATH]; +float skyrotate; +vec3_t skyaxis; +image_t *sky_images[6]; + +refdef_t r_newrefdef; +model_t *currentmodel; + +model_t *r_worldmodel; + +pixel_t *r_warpbuffer; + +swstate_t sw_state; + +void *colormap; +vec3_t viewlightvec; +alight_t r_viewlighting = {128, 192, viewlightvec}; +float r_time1; +int r_numallocatededges; +float r_aliasuvscale = 1.0; +int r_outofsurfaces; +int r_outofedges; + +qboolean r_dowarp; + +mvertex_t *r_pcurrentvertbase; + +int c_surf; +int r_maxsurfsseen, r_maxedgesseen, r_cnumsurfs; +qboolean r_surfsonstack; +int r_clipflags; + +// +// view origin +// +vec3_t vup, base_vup; +vec3_t vpn, base_vpn; +vec3_t vright, base_vright; +vec3_t r_origin; + +// +// screen size info +// +oldrefdef_t r_refdef; +float xcenter, ycenter; +float xscale, yscale; +float xscaleinv, yscaleinv; +float xscaleshrink, yscaleshrink; +float aliasxscale, aliasyscale, aliasxcenter, aliasycenter; + +int r_screenwidth; + +float verticalFieldOfView; +float xOrigin, yOrigin; + +mplane_t screenedge[4]; + +// +// refresh flags +// +int r_framecount = 1; // so frame counts initialized to 0 don't match +int r_visframecount; +int d_spanpixcount; +int r_polycount; +int r_drawnpolycount; +int r_wholepolycount; + +int *pfrustum_indexes[4]; +int r_frustum_indexes[4*6]; + +mleaf_t *r_viewleaf; +int r_viewcluster, r_oldviewcluster; + +image_t *r_notexture_mip; + +float da_time1, da_time2, dp_time1, dp_time2, db_time1, db_time2, rw_time1, rw_time2; +float se_time1, se_time2, de_time1, de_time2; + +void R_MarkLeaves (void); + +cvar_t *r_lefthand; +cvar_t *sw_aliasstats; +cvar_t *sw_allow_modex; +cvar_t *sw_clearcolor; +cvar_t *sw_drawflat; +cvar_t *sw_draworder; +cvar_t *sw_maxedges; +cvar_t *sw_maxsurfs; +cvar_t *sw_mode; +cvar_t *sw_reportedgeout; +cvar_t *sw_reportsurfout; +cvar_t *sw_stipplealpha; +cvar_t *sw_surfcacheoverride; +cvar_t *sw_waterwarp; +cvar_t *sw_overbrightbits; + +cvar_t *r_drawworld; +cvar_t *r_drawentities; +cvar_t *r_dspeeds; +cvar_t *r_fullbright; +cvar_t *r_lerpmodels; +cvar_t *r_novis; + +cvar_t *r_speeds; +cvar_t *r_lightlevel; //FIXME HACK + +cvar_t *vid_fullscreen; +cvar_t *vid_gamma; + +//PGM +cvar_t *sw_lockpvs; +//PGM + +#define STRINGER(x) "x" + +// r_vars.c + +// all global and static refresh variables are collected in a contiguous block +// to avoid cache conflicts. + +//------------------------------------------------------- +// global refresh variables +//------------------------------------------------------- + +// FIXME: make into one big structure, like cl or sv +// FIXME: do separately for refresh engine and driver + + +// d_vars.c + +// all global and static refresh variables are collected in a contiguous block +// to avoid cache conflicts. + +//------------------------------------------------------- +// global refresh variables +//------------------------------------------------------- + +// FIXME: make into one big structure, like cl or sv +// FIXME: do separately for refresh engine and driver + +float d_sdivzstepu, d_tdivzstepu, d_zistepu; +float d_sdivzstepv, d_tdivzstepv, d_zistepv; +float d_sdivzorigin, d_tdivzorigin, d_ziorigin; + +int sadjust, tadjust, bbextents, bbextentt; + +pixel_t *cacheblock; +int cachewidth; +pixel_t *d_viewbuffer; +zvalue_t *d_pzbuffer; +unsigned int d_zrowbytes; +unsigned int d_zwidth; + +struct texture_buffer { + image_t image; + byte buffer[1024]; +} r_notexture_buffer; + +/* +================== +R_InitTextures +================== +*/ +void R_InitTextures (void) +{ + int x,y, m; + + // create a simple checkerboard texture for the default + r_notexture_mip = &r_notexture_buffer.image; + + r_notexture_mip->width = r_notexture_mip->height = 16; + r_notexture_mip->pixels[0] = r_notexture_buffer.buffer; + r_notexture_mip->pixels[1] = r_notexture_mip->pixels[0] + 16*16; + r_notexture_mip->pixels[2] = r_notexture_mip->pixels[1] + 8*8; + r_notexture_mip->pixels[3] = r_notexture_mip->pixels[2] + 4*4; + + for (m=0 ; m<4 ; m++) + { + byte *dest; + + dest = r_notexture_mip->pixels[m]; + for (y=0 ; y< (16>>m) ; y++) + for (x=0 ; x< (16>>m) ; x++) + { + if ( (y< (8>>m) ) ^ (x< (8>>m) ) ) + + *dest++ = 0; + else + *dest++ = 0xff; + } + } +} + + +/* +================ +R_InitTurb +================ +*/ +void R_InitTurb (void) +{ + int i; + + memset(blanktable, 0, (vid.width+CYCLE) * sizeof(int)); + for (i = 0; i < (vid.width+CYCLE); i++) + { + sintable[i] = AMP + sin(i*3.14159*2/CYCLE)*AMP; + intsintable[i] = AMP2 + sin(i*3.14159*2/CYCLE)*AMP2; // AMP2, not 20 + } +} + +void R_ImageList_f( void ); + +void R_Register (void) +{ + sw_aliasstats = ri.Cvar_Get ("sw_polymodelstats", "0", 0); + sw_allow_modex = ri.Cvar_Get( "sw_allow_modex", "1", CVAR_ARCHIVE ); + sw_clearcolor = ri.Cvar_Get ("sw_clearcolor", "2", 0); + sw_drawflat = ri.Cvar_Get ("sw_drawflat", "0", 0); + sw_draworder = ri.Cvar_Get ("sw_draworder", "0", 0); + sw_maxedges = ri.Cvar_Get ("sw_maxedges", STRINGER(MAXSTACKSURFACES), 0); + sw_maxsurfs = ri.Cvar_Get ("sw_maxsurfs", "0", 0); + sw_mipcap = ri.Cvar_Get ("sw_mipcap", "0", 0); + sw_mipscale = ri.Cvar_Get ("sw_mipscale", "1", 0); + sw_reportedgeout = ri.Cvar_Get ("sw_reportedgeout", "0", 0); + sw_reportsurfout = ri.Cvar_Get ("sw_reportsurfout", "0", 0); + sw_stipplealpha = ri.Cvar_Get( "sw_stipplealpha", "0", CVAR_ARCHIVE ); + sw_surfcacheoverride = ri.Cvar_Get ("sw_surfcacheoverride", "0", 0); + sw_waterwarp = ri.Cvar_Get ("sw_waterwarp", "1", 0); + sw_overbrightbits = ri.Cvar_Get("sw_overbrightbits", "1.0", CVAR_ARCHIVE); + sw_mode = ri.Cvar_Get( "sw_mode", "0", CVAR_ARCHIVE ); + + r_lefthand = ri.Cvar_Get( "hand", "0", CVAR_USERINFO | CVAR_ARCHIVE ); + r_speeds = ri.Cvar_Get ("r_speeds", "0", 0); + r_fullbright = ri.Cvar_Get ("r_fullbright", "0", 0); + r_drawentities = ri.Cvar_Get ("r_drawentities", "1", 0); + r_drawworld = ri.Cvar_Get ("r_drawworld", "1", 0); + r_dspeeds = ri.Cvar_Get ("r_dspeeds", "0", 0); + r_lightlevel = ri.Cvar_Get ("r_lightlevel", "0", 0); + r_lerpmodels = ri.Cvar_Get( "r_lerpmodels", "1", 0 ); + r_novis = ri.Cvar_Get( "r_novis", "0", 0 ); + + vid_fullscreen = ri.Cvar_Get( "vid_fullscreen", "0", CVAR_ARCHIVE ); + vid_gamma = ri.Cvar_Get( "vid_gamma", "1.0", CVAR_ARCHIVE ); + + ri.Cmd_AddCommand("modellist", Mod_Modellist_f); + ri.Cmd_AddCommand("screenshot", R_ScreenShot_f); + ri.Cmd_AddCommand("imagelist", R_ImageList_f); + + sw_mode->modified = true; // force us to do mode specific stuff later + vid_gamma->modified = true; // force us to rebuild the gamma table later + sw_overbrightbits->modified = true; // force us to rebuild pallete later + + //PGM + sw_lockpvs = ri.Cvar_Get ("sw_lockpvs", "0", 0); + //PGM +} + +void R_UnRegister (void) +{ + ri.Cmd_RemoveCommand( "screenshot" ); + ri.Cmd_RemoveCommand ("modellist"); + ri.Cmd_RemoveCommand( "imagelist" ); +} + +/* +=============== +R_Init +=============== +*/ +qboolean RE_Init(void) +{ + R_InitImages (); + Mod_Init (); + Draw_InitLocal (); + R_InitTextures (); + + view_clipplanes[0].leftedge = true; + view_clipplanes[1].rightedge = true; + view_clipplanes[1].leftedge = view_clipplanes[2].leftedge = + view_clipplanes[3].leftedge = false; + view_clipplanes[0].rightedge = view_clipplanes[2].rightedge = + view_clipplanes[3].rightedge = false; + + r_refdef.xOrigin = XCENTERING; + r_refdef.yOrigin = YCENTERING; + + r_aliasuvscale = 1.0; + + R_Register (); + Draw_GetPalette (); + if (SWimp_Init() == false) + return false; + + // create the window + RE_BeginFrame( 0 ); + + R_Printf(PRINT_ALL, "ref_soft version: "REF_VERSION"\n"); + + return true; +} + +/* +=============== +RE_Shutdown +=============== +*/ +void RE_Shutdown (void) +{ + // free z buffer + if (d_pzbuffer) + { + free (d_pzbuffer); + d_pzbuffer = NULL; + } + // free surface cache + if (sc_base) + { + D_FlushCaches (); + free (sc_base); + sc_base = NULL; + } + + // free colormap + if (vid_colormap) + { + free (vid_colormap); + vid_colormap = NULL; + } + R_UnRegister (); + Mod_FreeAll (); + R_ShutdownImages (); + + SWimp_Shutdown(); +} + +/* +=============== +R_NewMap +=============== +*/ +void R_NewMap (void) +{ + r_viewcluster = -1; + + r_cnumsurfs = sw_maxsurfs->value; + + if (r_cnumsurfs <= MINSURFACES) + r_cnumsurfs = MINSURFACES; + + if (r_cnumsurfs > NUMSTACKSURFACES) + { + surfaces = malloc (r_cnumsurfs * sizeof(surf_t)); + if (!surfaces) + { + R_Printf(PRINT_ALL, "R_NewMap: Couldn't malloc %ld bytes\n", r_cnumsurfs * sizeof(surf_t)); + return; + } + + surface_p = surfaces; + surf_max = &surfaces[r_cnumsurfs]; + r_surfsonstack = false; + // surface 0 doesn't really exist; it's just a dummy because index 0 + // is used to indicate no edge attached to surface + surfaces--; + } + else + { + r_surfsonstack = true; + } + + r_maxedgesseen = 0; + r_maxsurfsseen = 0; + + r_numallocatededges = sw_maxedges->value; + + if (r_numallocatededges < MINEDGES) + r_numallocatededges = MINEDGES; + + if (r_numallocatededges <= NUMSTACKEDGES) + { + auxedges = NULL; + } + else + { + auxedges = malloc (r_numallocatededges * sizeof(edge_t)); + } +} + + +/* +=============== +R_MarkLeaves + +Mark the leaves and nodes that are in the PVS for the current +cluster +=============== +*/ +void R_MarkLeaves (void) +{ + byte *vis; + mnode_t *node; + int i; + mleaf_t *leaf; + + if (r_oldviewcluster == r_viewcluster && !r_novis->value && r_viewcluster != -1) + return; + + // development aid to let you run around and see exactly where + // the pvs ends + if (sw_lockpvs->value) + return; + + r_visframecount++; + r_oldviewcluster = r_viewcluster; + + if (r_novis->value || r_viewcluster == -1 || !r_worldmodel->vis) + { + // mark everything + for (i=0 ; inumleafs ; i++) + r_worldmodel->leafs[i].visframe = r_visframecount; + for (i=0 ; inumnodes ; i++) + r_worldmodel->nodes[i].visframe = r_visframecount; + return; + } + + vis = Mod_ClusterPVS (r_viewcluster, r_worldmodel); + + for (i=0,leaf=r_worldmodel->leafs ; inumleafs ; i++, leaf++) + { + int cluster; + + cluster = leaf->cluster; + if (cluster == -1) + continue; + if (vis[cluster>>3] & (1<<(cluster&7))) + { + node = (mnode_t *)leaf; + do + { + if (node->visframe == r_visframecount) + break; + node->visframe = r_visframecount; + node = node->parent; + } while (node); + } + } +} + +/* +** R_DrawNullModel +** +** IMPLEMENT THIS! +*/ +void R_DrawNullModel( void ) +{ +} + +/* +============= +R_DrawEntitiesOnList +============= +*/ +void R_DrawEntitiesOnList (void) +{ + int i; + qboolean translucent_entities = false; + + if (!r_drawentities->value) + return; + + // all bmodels have already been drawn by the edge list + for (i=0 ; iflags & RF_TRANSLUCENT ) + { + translucent_entities = true; + continue; + } + + if ( currententity->flags & RF_BEAM ) + { + modelorg[0] = -r_origin[0]; + modelorg[1] = -r_origin[1]; + modelorg[2] = -r_origin[2]; + VectorCopy( vec3_origin, r_entorigin ); + R_DrawBeam( currententity ); + } + else + { + currentmodel = currententity->model; + if (!currentmodel) + { + R_DrawNullModel(); + continue; + } + VectorCopy (currententity->origin, r_entorigin); + VectorSubtract (r_origin, r_entorigin, modelorg); + + switch (currentmodel->type) + { + case mod_sprite: + R_DrawSprite (); + break; + + case mod_alias: + R_AliasDrawModel (); + break; + + default: + break; + } + } + } + + if ( !translucent_entities ) + return; + + for (i=0 ; iflags & RF_TRANSLUCENT ) ) + continue; + + if ( currententity->flags & RF_BEAM ) + { + modelorg[0] = -r_origin[0]; + modelorg[1] = -r_origin[1]; + modelorg[2] = -r_origin[2]; + VectorCopy( vec3_origin, r_entorigin ); + R_DrawBeam( currententity ); + } + else + { + currentmodel = currententity->model; + if (!currentmodel) + { + R_DrawNullModel(); + continue; + } + VectorCopy (currententity->origin, r_entorigin); + VectorSubtract (r_origin, r_entorigin, modelorg); + + switch (currentmodel->type) + { + case mod_sprite: + R_DrawSprite (); + break; + + case mod_alias: + R_AliasDrawModel (); + break; + + default: + break; + } + } + } +} + + +/* +============= +R_BmodelCheckBBox +============= +*/ +int R_BmodelCheckBBox (float *minmaxs) +{ + int i, clipflags; + + clipflags = 0; + + for (i=0 ; i<4 ; i++) + { + vec3_t acceptpt, rejectpt; + int *pindex; + float d; + + // generate accept and reject points + // FIXME: do with fast look-ups or integer tests based on the sign bit + // of the floating point values + pindex = pfrustum_indexes[i]; + + rejectpt[0] = minmaxs[pindex[0]]; + rejectpt[1] = minmaxs[pindex[1]]; + rejectpt[2] = minmaxs[pindex[2]]; + + d = DotProduct (rejectpt, view_clipplanes[i].normal); + d -= view_clipplanes[i].dist; + + if (d <= 0) + return BMODEL_FULLY_CLIPPED; + + acceptpt[0] = minmaxs[pindex[3+0]]; + acceptpt[1] = minmaxs[pindex[3+1]]; + acceptpt[2] = minmaxs[pindex[3+2]]; + + d = DotProduct (acceptpt, view_clipplanes[i].normal); + d -= view_clipplanes[i].dist; + + if (d <= 0) + clipflags |= (1<nodes; + + while (1) + { + mplane_t *splitplane; + int sides; + + if (node->visframe != r_visframecount) + return NULL; // not visible at all + + if (node->contents != CONTENTS_NODE) + { + if (node->contents != CONTENTS_SOLID) + return node; // we've reached a non-solid leaf, so it's + // visible and not BSP clipped + return NULL; // in solid, so not visible + } + + splitplane = node->plane; + sides = BOX_ON_PLANE_SIDE(mins, maxs, (cplane_t *)splitplane); + + if (sides == 3) + return node; // this is the splitter + + // not split yet; recurse down the contacted side + if (sides & 1) + node = node->children[0]; + else + node = node->children[1]; + } +} + + +/* +============= +RotatedBBox + +Returns an axially aligned box that contains the input box at the given rotation +============= +*/ +void RotatedBBox (vec3_t mins, vec3_t maxs, vec3_t angles, vec3_t tmins, vec3_t tmaxs) +{ + vec3_t tmp, v; + int i, j; + vec3_t forward, right, up; + + if (!angles[0] && !angles[1] && !angles[2]) + { + VectorCopy (mins, tmins); + VectorCopy (maxs, tmaxs); + return; + } + + for (i=0 ; i<3 ; i++) + { + tmins[i] = 99999; + tmaxs[i] = -99999; + } + + AngleVectors (angles, forward, right, up); + + for ( i = 0; i < 8; i++ ) + { + if ( i & 1 ) + tmp[0] = mins[0]; + else + tmp[0] = maxs[0]; + + if ( i & 2 ) + tmp[1] = mins[1]; + else + tmp[1] = maxs[1]; + + if ( i & 4 ) + tmp[2] = mins[2]; + else + tmp[2] = maxs[2]; + + + VectorScale (forward, tmp[0], v); + VectorMA (v, -tmp[1], right, v); + VectorMA (v, tmp[2], up, v); + + for (j=0 ; j<3 ; j++) + { + if (v[j] < tmins[j]) + tmins[j] = v[j]; + if (v[j] > tmaxs[j]) + tmaxs[j] = v[j]; + } + } +} + +/* +============= +R_DrawBEntitiesOnList +============= +*/ +void R_DrawBEntitiesOnList (void) +{ + int i, clipflags; + vec3_t oldorigin; + vec3_t mins, maxs; + float minmaxs[6]; + mnode_t *topnode; + + if (!r_drawentities->value) + return; + + VectorCopy (modelorg, oldorigin); + insubmodel = true; + r_dlightframecount = r_framecount; + + for (i=0 ; imodel; + if (!currentmodel) + continue; + if (currentmodel->nummodelsurfaces == 0) + continue; // clip brush only + if ( currententity->flags & RF_BEAM ) + continue; + if (currentmodel->type != mod_brush) + continue; + // see if the bounding box lets us trivially reject, also sets + // trivial accept status + RotatedBBox (currentmodel->mins, currentmodel->maxs, + currententity->angles, mins, maxs); + VectorAdd (mins, currententity->origin, minmaxs); + VectorAdd (maxs, currententity->origin, (minmaxs+3)); + + clipflags = R_BmodelCheckBBox (minmaxs); + if (clipflags == BMODEL_FULLY_CLIPPED) + continue; // off the edge of the screen + + topnode = R_FindTopnode (minmaxs, minmaxs+3); + if (!topnode) + continue; // no part in a visible leaf + + VectorCopy (currententity->origin, r_entorigin); + VectorSubtract (r_origin, r_entorigin, modelorg); + + r_pcurrentvertbase = currentmodel->vertexes; + + // FIXME: stop transforming twice + R_RotateBmodel (); + + // calculate dynamic lighting for bmodel + R_PushDlights (currentmodel); + + if (topnode->contents == CONTENTS_NODE) + { + // not a leaf; has to be clipped to the world BSP + r_clipflags = clipflags; + R_DrawSolidClippedSubmodelPolygons (currentmodel, topnode); + } + else + { + // falls entirely in one leaf, so we just put all the + // edges in the edge list and let 1/z sorting handle + // drawing order + R_DrawSubmodelPolygons (currentmodel, clipflags, topnode); + } + + // put back world rotation and frustum clipping + // FIXME: R_RotateBmodel should just work off base_vxx + VectorCopy (base_vpn, vpn); + VectorCopy (base_vup, vup); + VectorCopy (base_vright, vright); + VectorCopy (oldorigin, modelorg); + R_TransformFrustum (); + } + + insubmodel = false; +} + +edge_t *ledges; +surf_t *lsurfs; + +/* +================ +R_EdgeDrawing +================ +*/ +void R_EdgeDrawing (void) +{ + if ( r_newrefdef.rdflags & RDF_NOWORLDMODEL ) + return; + + if (auxedges) + { + r_edges = auxedges; + } + else + { + r_edges = ledges; + } + + if (r_surfsonstack) + { + surfaces = lsurfs; + surf_max = &surfaces[r_cnumsurfs]; + // surface 0 doesn't really exist; it's just a dummy because index 0 + // is used to indicate no edge attached to surface + surfaces--; + } + + R_BeginEdgeFrame (); + + if (r_dspeeds->value) + { + rw_time1 = SDL_GetTicks(); + } + + R_RenderWorld (); + + if (r_dspeeds->value) + { + rw_time2 = SDL_GetTicks(); + db_time1 = rw_time2; + } + + R_DrawBEntitiesOnList (); + + if (r_dspeeds->value) + { + db_time2 = SDL_GetTicks(); + se_time1 = db_time2; + } + + R_ScanEdges (); +} + +//======================================================================= + + +/* +============= +R_CalcPalette + +============= +*/ +void R_CalcPalette (void) +{ + static qboolean modified; + byte palette[256][4], *in, *out; + int i, j; + float alpha, one_minus_alpha; + vec3_t premult; + int v; + + alpha = r_newrefdef.blend[3]; + if (alpha <= 0) + { + if (modified) + { // set back to default + modified = false; + R_GammaCorrectAndSetPalette( ( const unsigned char * ) d_8to24table ); + return; + } + return; + } + + modified = true; + if (alpha > 1) + alpha = 1; + + premult[0] = r_newrefdef.blend[0]*alpha*255; + premult[1] = r_newrefdef.blend[1]*alpha*255; + premult[2] = r_newrefdef.blend[2]*alpha*255; + + one_minus_alpha = (1.0 - alpha); + + in = (byte *)d_8to24table; + out = palette[0]; + for (i=0 ; i<256 ; i++, in+=4, out+=4) + { + for (j=0 ; j<3 ; j++) + { + v = premult[j] + one_minus_alpha * in[j]; + if (v > 255) + v = 255; + out[j] = v; + } + out[3] = 255; + } + + R_GammaCorrectAndSetPalette( ( const unsigned char * ) palette[0] ); +} + +//======================================================================= + +void R_SetLightLevel (void) +{ + vec3_t light; + + if ((r_newrefdef.rdflags & RDF_NOWORLDMODEL) || (!r_drawentities->value) || (!currententity)) + { + r_lightlevel->value = 150.0; + return; + } + + // save off light value for server to look at (BIG HACK!) + R_LightPoint (r_newrefdef.vieworg, light); + r_lightlevel->value = 150.0 * light[0]; +} + + +/* +================ +RE_RenderFrame + +================ +*/ +void RE_RenderFrame (refdef_t *fd) +{ + r_newrefdef = *fd; + + if (!r_worldmodel && !( r_newrefdef.rdflags & RDF_NOWORLDMODEL ) ) + ri.Sys_Error (ERR_FATAL,"R_RenderView: NULL worldmodel"); + + VectorCopy (fd->vieworg, r_refdef.vieworg); + VectorCopy (fd->viewangles, r_refdef.viewangles); + + if (r_speeds->value || r_dspeeds->value) + r_time1 = SDL_GetTicks(); + + R_SetupFrame (); + + R_MarkLeaves (); // done here so we know if we're in water + + R_PushDlights (r_worldmodel); + + R_EdgeDrawing (); + + if (r_dspeeds->value) + { + se_time2 = SDL_GetTicks(); + de_time1 = se_time2; + } + + R_DrawEntitiesOnList (); + + if (r_dspeeds->value) + { + de_time2 = SDL_GetTicks(); + dp_time1 = SDL_GetTicks(); + } + + R_DrawParticles (); + + if (r_dspeeds->value) + dp_time2 = SDL_GetTicks(); + + R_DrawAlphaSurfaces(); + + R_SetLightLevel (); + + if (r_dowarp) + D_WarpScreen (); + + if (r_dspeeds->value) + da_time1 = SDL_GetTicks(); + + if (r_dspeeds->value) + da_time2 = SDL_GetTicks(); + + R_CalcPalette (); + + if (sw_aliasstats->value) + R_PrintAliasStats (); + + if (r_speeds->value) + R_PrintTimes (); + + if (r_dspeeds->value) + R_PrintDSpeeds (); + + if (sw_reportsurfout->value && r_outofsurfaces) + R_Printf(PRINT_ALL,"Short %d surfaces\n", r_outofsurfaces); + + if (sw_reportedgeout->value && r_outofedges) + R_Printf(PRINT_ALL,"Short roughly %d edges\n", r_outofedges * 2 / 3); +} + +/* +** R_InitGraphics +*/ +void R_InitGraphics( int width, int height ) +{ + vid.width = width; + vid.height = height; + + // free z buffer + if ( d_pzbuffer ) + { + free(d_pzbuffer); + d_pzbuffer = NULL; + } + + // free surface cache + if ( sc_base ) + { + D_FlushCaches(); + free(sc_base); + sc_base = NULL; + } + + d_pzbuffer = malloc(vid.width * vid.height * sizeof(zvalue_t)); + + R_InitCaches(); + + R_GammaCorrectAndSetPalette((const unsigned char *)d_8to24table); +} + +/* +** RE_BeginFrame +*/ +void RE_BeginFrame( float camera_separation ) +{ + extern void Draw_BuildGammaTable( void ); + + /* + ** rebuild the gamma correction palette if necessary + */ + if ( vid_gamma->modified || sw_overbrightbits->modified ) + { + Draw_BuildGammaTable(); + R_GammaCorrectAndSetPalette((const unsigned char * )d_8to24table); + + vid_gamma->modified = false; + sw_overbrightbits->modified = false; + } + + while (sw_mode->modified || vid_fullscreen->modified) + { + rserr_t err; + + /* + ** if this returns rserr_invalid_fullscreen then it set the mode but not as a + ** fullscreen mode, e.g. 320x200 on a system that doesn't support that res + */ + if ((err = SWimp_SetMode( &vid.width, &vid.height, sw_mode->value, vid_fullscreen->value)) == rserr_ok ) + { + R_InitGraphics( vid.width, vid.height ); + + sw_state.prev_mode = sw_mode->value; + vid_fullscreen->modified = false; + sw_mode->modified = false; + } + else + { + if ( err == rserr_invalid_mode ) + { + ri.Cvar_SetValue( "sw_mode", sw_state.prev_mode ); + R_Printf( PRINT_ALL, "ref_soft::RE_BeginFrame() - could not set mode\n" ); + } + else if ( err == rserr_invalid_fullscreen ) + { + R_InitGraphics( vid.width, vid.height ); + + ri.Cvar_SetValue( "vid_fullscreen", 0); + R_Printf( PRINT_ALL, "ref_soft::RE_BeginFrame() - fullscreen unavailable in this mode\n" ); + sw_state.prev_mode = sw_mode->value; + } + else + { + ri.Sys_Error( ERR_FATAL, "ref_soft::RE_BeginFrame() - catastrophic mode change failure\n" ); + } + } + } +} + +/* +** R_GammaCorrectAndSetPalette +*/ +void R_GammaCorrectAndSetPalette( const unsigned char *palette ) +{ + int i; + + for ( i = 0; i < 256; i++ ) + { + sw_state.currentpalette[i*4+0] = sw_state.gammatable[palette[i*4+0]]; + sw_state.currentpalette[i*4+1] = sw_state.gammatable[palette[i*4+1]]; + sw_state.currentpalette[i*4+2] = sw_state.gammatable[palette[i*4+2]]; + } +} + +/* +** RE_SetPalette +*/ +void RE_SetPalette(const unsigned char *palette) +{ + byte palette32[1024]; + int i; + + // clear screen to black to avoid any palette flash + memset(vid_buffer, 0, vid.height * vid.width * sizeof(pixel_t)); + + // flush it to the screen + RE_EndFrame (); + + if (palette) + { + for ( i = 0; i < 256; i++ ) + { + palette32[i*4+0] = palette[i*3+0]; + palette32[i*4+1] = palette[i*3+1]; + palette32[i*4+2] = palette[i*3+2]; + palette32[i*4+3] = 0xFF; + } + + R_GammaCorrectAndSetPalette( palette32 ); + } + else + { + R_GammaCorrectAndSetPalette((const unsigned char *)d_8to24table); + } +} + +/* +================ +Draw_BuildGammaTable +================ +*/ +void Draw_BuildGammaTable (void) +{ + int i; + float g; + float overbright; + + overbright = sw_overbrightbits->value; + + if(overbright < 0.5) + overbright = 0.5; + + if(overbright > 4.0) + overbright = 4.0; + + g = (2.1 - vid_gamma->value); + + + if (g == 1.0) + { + for (i=0 ; i<256 ; i++) { + int inf; + + inf = i * overbright; + + if (inf < 0) + inf = 0; + if (inf > 255) + inf = 255; + + sw_state.gammatable[i] = inf; + } + } + else + for (i=0 ; i<256 ; i++) + { + int inf; + + inf = (255 * pow ( (i+0.5)/255.5 , g ) + 0.5) * overbright; + if (inf < 0) + inf = 0; + if (inf > 255) + inf = 255; + sw_state.gammatable[i] = inf; + } +} + +/* +** R_DrawBeam +*/ +void R_DrawBeam( entity_t *e ) +{ +#define NUM_BEAM_SEGS 6 + + int i; + + vec3_t perpvec; + vec3_t direction, normalized_direction; + vec3_t start_points[NUM_BEAM_SEGS], end_points[NUM_BEAM_SEGS]; + vec3_t oldorigin, origin; + + oldorigin[0] = e->oldorigin[0]; + oldorigin[1] = e->oldorigin[1]; + oldorigin[2] = e->oldorigin[2]; + + origin[0] = e->origin[0]; + origin[1] = e->origin[1]; + origin[2] = e->origin[2]; + + normalized_direction[0] = direction[0] = oldorigin[0] - origin[0]; + normalized_direction[1] = direction[1] = oldorigin[1] - origin[1]; + normalized_direction[2] = direction[2] = oldorigin[2] - origin[2]; + + if ( VectorNormalize( normalized_direction ) == 0 ) + return; + + PerpendicularVector( perpvec, normalized_direction ); + VectorScale( perpvec, e->frame / 2, perpvec ); + + for ( i = 0; i < NUM_BEAM_SEGS; i++ ) + { + RotatePointAroundVector( start_points[i], normalized_direction, perpvec, (360.0/NUM_BEAM_SEGS)*i ); + VectorAdd( start_points[i], origin, start_points[i] ); + VectorAdd( start_points[i], direction, end_points[i] ); + } + + for ( i = 0; i < NUM_BEAM_SEGS; i++ ) + { + R_IMFlatShadedQuad( start_points[i], + end_points[i], + end_points[(i+1)%NUM_BEAM_SEGS], + start_points[(i+1)%NUM_BEAM_SEGS], + e->skinnum & 0xFF, + e->alpha ); + } +} + + +//=================================================================== + +/* +============ +RE_SetSky +============ +*/ +// 3dstudio environment map names +char *suf[6] = {"rt", "bk", "lf", "ft", "up", "dn"}; +int r_skysideimage[6] = {5, 2, 4, 1, 0, 3}; +extern mtexinfo_t r_skytexinfo[6]; +void RE_SetSky (char *name, float rotate, vec3_t axis) +{ + int i; + char pathname[MAX_QPATH]; + + strncpy (skyname, name, sizeof(skyname)-1); + skyrotate = rotate; + VectorCopy (axis, skyaxis); + + for (i=0 ; i<6 ; i++) + { + Com_sprintf (pathname, sizeof(pathname), "env/%s%s.pcx", skyname, suf[r_skysideimage[i]]); + r_skytexinfo[i].image = R_FindImage (pathname, it_sky); + } +} + + +/* +=============== +Draw_GetPalette +=============== +*/ +void Draw_GetPalette (void) +{ + byte *pal, *out; + int i; + + // get the palette and colormap + LoadPCX ("pics/colormap.pcx", &vid_colormap, &pal, NULL, NULL); + if (!vid_colormap) + ri.Sys_Error (ERR_FATAL, "Couldn't load pics/colormap.pcx"); + vid_alphamap = vid_colormap + 64*256; + + out = (byte *)d_8to24table; + for (i=0 ; i<256 ; i++, out+=4) + { + int r, g, b; + + r = pal[i*3+0]; + g = pal[i*3+1]; + b = pal[i*3+2]; + + out[0] = r; + out[1] = g; + out[2] = b; + } + + free (pal); +} + +struct image_s *RE_RegisterSkin (char *name); + +void R_Printf(int level, const char* msg, ...) +{ + va_list argptr; + va_start(argptr, msg); + ri.Com_VPrintf(level, msg, argptr); + va_end(argptr); +} + +qboolean RE_IsVsyncActive(void) +{ + return true; +} + +/* +=============== +GetRefAPI +=============== +*/ +Q2_DLL_EXPORTED refexport_t +GetRefAPI(refimport_t imp) +{ + refexport_t re; + + memset(&re, 0, sizeof(refexport_t)); + ri = imp; + + re.api_version = API_VERSION; + + re.BeginRegistration = RE_BeginRegistration; + re.RegisterModel = RE_RegisterModel; + re.RegisterSkin = RE_RegisterSkin; + re.DrawFindPic = RE_Draw_FindPic; + re.SetSky = RE_SetSky; + re.EndRegistration = RE_EndRegistration; + + re.RenderFrame = RE_RenderFrame; + + re.DrawGetPicSize = RE_Draw_GetPicSize; + + re.DrawPicScaled = RE_Draw_PicScaled; + re.DrawStretchPic = RE_Draw_StretchPic; + re.DrawCharScaled = RE_Draw_CharScaled; + re.DrawTileClear = RE_Draw_TileClear; + re.DrawFill = RE_Draw_Fill; + re.DrawFadeScreen = RE_Draw_FadeScreen; + + re.DrawStretchRaw = RE_Draw_StretchRaw; + + re.Init = RE_Init; + re.IsVSyncActive = RE_IsVsyncActive; + re.Shutdown = RE_Shutdown; + + re.SetPalette = RE_SetPalette; + re.BeginFrame = RE_BeginFrame; + re.EndFrame = RE_EndFrame; + + Swap_Init (); + + return re; +} + +#if SDL_VERSION_ATLEAST(2, 0, 0) +static SDL_Window* window = NULL; +static SDL_Surface *surface = NULL; +static SDL_Texture *texture = NULL; +static SDL_Renderer *renderer = NULL; +#else +static SDL_Surface* window = NULL; +#endif +static qboolean X11_active = false; + +/* +** SWimp_Init +** +** This routine is responsible for initializing the implementation +** specific stuff in a software rendering subsystem. +*/ +int SWimp_Init(void) +{ + if (!SDL_WasInit(SDL_INIT_VIDEO)) + { + + if (SDL_Init(SDL_INIT_VIDEO) == -1) + { + Com_Printf("Couldn't init SDL video: %s.\n", SDL_GetError()); + return false; + } + + SDL_version version; + +#if SDL_VERSION_ATLEAST(2, 0, 0) + SDL_GetVersion(&version); + const char* driverName = SDL_GetCurrentVideoDriver(); +#else + char driverName[64]; + SDL_VideoDriverName(driverName, sizeof(driverName)); + version = *SDL_Linked_Version(); +#endif + Com_Printf("SDL version is: %i.%i.%i\n", (int)version.major, (int)version.minor, (int)version.patch); + Com_Printf("SDL video driver is \"%s\".\n", driverName); + } + + return true; +} + +/* + * Sets the window icon + */ +#if SDL_VERSION_ATLEAST(2, 0, 0) + +/* The 64x64 32bit window icon */ +#include "../../../backends/sdl/icon/q2icon64.h" + +static void +SetSDLIcon() +{ + /* these masks are needed to tell SDL_CreateRGBSurface(From) + to assume the data it gets is byte-wise RGB(A) data */ + Uint32 rmask, gmask, bmask, amask; +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + int shift = (q2icon64.bytes_per_pixel == 3) ? 8 : 0; + rmask = 0xff000000 >> shift; + gmask = 0x00ff0000 >> shift; + bmask = 0x0000ff00 >> shift; + amask = 0x000000ff >> shift; +#else /* little endian, like x86 */ + rmask = 0x000000ff; + gmask = 0x0000ff00; + bmask = 0x00ff0000; + amask = (q2icon64.bytes_per_pixel == 3) ? 0 : 0xff000000; +#endif + + SDL_Surface* icon = SDL_CreateRGBSurfaceFrom((void*)q2icon64.pixel_data, q2icon64.width, + q2icon64.height, q2icon64.bytes_per_pixel*8, q2icon64.bytes_per_pixel*q2icon64.width, + rmask, gmask, bmask, amask); + + SDL_SetWindowIcon(window, icon); + + SDL_FreeSurface(icon); +} + +#else /* SDL 1.2 */ + +/* The window icon */ +#include "../../../backends/sdl/icon/q2icon.xbm" + +static void +SetSDLIcon() +{ + SDL_Surface *icon; + SDL_Color transColor, solidColor; + Uint8 *ptr; + int i; + int mask; + + icon = SDL_CreateRGBSurface(SDL_SWSURFACE, + q2icon_width, q2icon_height, 8, + 0, 0, 0, 0); + + if (icon == NULL) + { + return; + } + + SDL_SetColorKey(icon, SDL_SRCCOLORKEY, 0); + + transColor.r = 255; + transColor.g = 255; + transColor.b = 255; + + solidColor.r = 0; + solidColor.g = 0; + solidColor.b = 0; + + SDL_SetColors(icon, &transColor, 0, 1); + SDL_SetColors(icon, &solidColor, 1, 1); + + ptr = (Uint8 *)icon->pixels; + + for (i = 0; i < sizeof(q2icon_bits); i++) + { + for (mask = 1; mask != 0x100; mask <<= 1) + { + *ptr = (q2icon_bits[i] & mask) ? 1 : 0; + ptr++; + } + } + + SDL_WM_SetIcon(icon, NULL); + + SDL_FreeSurface(icon); +} +#endif /* SDL 1.2 */ + +static int IsFullscreen() +{ +#if SDL_VERSION_ATLEAST(2, 0, 0) + if (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN_DESKTOP) { + return 1; + } else if (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN) { + return 2; + } else { + return 0; + } +#else + return !!(window->flags & SDL_FULLSCREEN); +#endif +} + +static qboolean GetWindowSize(int* w, int* h) +{ + if(window == NULL || w == NULL || h == NULL) + return false; + +#if SDL_VERSION_ATLEAST(2, 0, 0) + SDL_DisplayMode m; + if(SDL_GetWindowDisplayMode(window, &m) != 0) + { + Com_Printf("Can't get Displaymode: %s\n", SDL_GetError()); + return false; + } + *w = m.w; + *h = m.h; +#else + *w = window->w; + *h = window->h; +#endif + + return true; +} + +int R_InitContext(void* win) +{ + char title[40] = {0}; + + if(win == NULL) + { + ri.Sys_Error(ERR_FATAL, "R_InitContext() must not be called with NULL argument!"); + return false; + } + +#if SDL_VERSION_ATLEAST(2, 0, 0) + window = (SDL_Window*)win; +#else // SDL 1.2 + window = (SDL_Surface*)win; +#endif + + /* Window title - set here so we can display renderer name in it */ + snprintf(title, sizeof(title), "Yamagi Quake II %s - Soft Render", YQ2VERSION); +#if SDL_VERSION_ATLEAST(2, 0, 0) + SDL_SetWindowTitle(window, title); +#else + SDL_WM_SetCaption(title, title); +#endif + + return true; +} + +static qboolean CreateSDLWindow(int flags, int w, int h) +{ +#if SDL_VERSION_ATLEAST(2, 0, 0) + Uint32 Rmask, Gmask, Bmask, Amask; + int bpp; + int windowPos = SDL_WINDOWPOS_UNDEFINED; + if (!SDL_PixelFormatEnumToMasks(SDL_PIXELFORMAT_ARGB8888, &bpp, &Rmask, &Gmask, &Bmask, &Amask)) + return 0; + + // TODO: support fullscreen on different displays with SDL_WINDOWPOS_UNDEFINED_DISPLAY(displaynum) + window = SDL_CreateWindow("Yamagi Quake II", windowPos, windowPos, w, h, flags); + + renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); + surface = SDL_CreateRGBSurface(0, w, h, bpp, Rmask, Gmask, Bmask, Amask); + + texture = SDL_CreateTexture(renderer, + SDL_PIXELFORMAT_ARGB8888, + SDL_TEXTUREACCESS_STREAMING, + w, h); + return window != NULL; +#else + window = SDL_SetVideoMode(w, h, 0, flags); + SDL_EnableUNICODE(SDL_TRUE); + return window != NULL; +#endif +} + +static void SWimp_DestroyRender(void) +{ + if (vid_buffer) + { + free(vid_buffer); + } + vid_buffer = NULL; + + if (sintable) + { + free(sintable); + } + sintable = NULL; + + if (intsintable) + { + free(intsintable); + } + intsintable = NULL; + + if (blanktable) + { + free(blanktable); + } + blanktable = NULL; + + if (vid_polygon_spans) + { + free(vid_polygon_spans); + } + vid_polygon_spans = NULL; + + if (newedges) + { + free(newedges); + } + newedges = NULL; + + if (removeedges) + { + free(removeedges); + } + removeedges = NULL; + + if (triangle_spans) + { + free(triangle_spans); + } + triangle_spans = NULL; + + if (warp_rowptr) + { + free(warp_rowptr); + } + warp_rowptr = NULL; + + if (warp_column) + { + free(warp_column); + } + warp_column = NULL; + + if (edge_basespans) + { + free(edge_basespans); + } + edge_basespans = NULL; + + if (finalverts) + { + free(finalverts); + } + finalverts = NULL; + + if(ledges) + { + free(ledges); + } + ledges = NULL; + + if(lsurfs) + { + free(lsurfs); + } + lsurfs = NULL; + + if(r_warpbuffer) + { + free(r_warpbuffer); + } + r_warpbuffer = NULL; + +#if SDL_VERSION_ATLEAST(2, 0, 0) + if (texture) + { + SDL_DestroyTexture(texture); + } + texture = NULL; + + if (surface) + { + SDL_FreeSurface(surface); + } + surface = NULL; + + if (renderer) + { + SDL_DestroyRenderer(renderer); + } + renderer = NULL; + + /* Is the surface used? */ + if (window) + SDL_DestroyWindow(window); +#else + /* Is the surface used? */ + if (window) + SDL_FreeSurface(window); +#endif + window = NULL; +} + +/* +be careful if you ever want to change width: 12.20 fixed +point math used in R_ScanEdges() overflows at width 2048 !! +*/ +char shift_size; + +/* +** SWimp_InitGraphics +** +** This initializes the software refresh's implementation specific +** graphics subsystem. In the case of Windows it creates DIB or +** DDRAW surfaces. +** +** The necessary width and height parameters are grabbed from +** vid.width and vid.height. +*/ +static qboolean SWimp_InitGraphics(qboolean fullscreen, int *pwidth, int *pheight) +{ + int flags; + int curWidth, curHeight; + int width = *pwidth; + int height = *pheight; + unsigned int fs_flag = 0; + +#if SDL_VERSION_ATLEAST(2, 0, 0) + if (fullscreen == 1) { + fs_flag = SDL_WINDOW_FULLSCREEN_DESKTOP; + } else if (fullscreen == 2) { + fs_flag = SDL_WINDOW_FULLSCREEN; + } +#else + if (fullscreen) { + fs_flag = SDL_FULLSCREEN; + } +#endif + + if (GetWindowSize(&curWidth, &curHeight) && (curWidth == width) && (curHeight == height)) + { + /* If we want fullscreen, but aren't */ + if (fullscreen != IsFullscreen()) + { +#if SDL_VERSION_ATLEAST(2, 0, 0) + SDL_SetWindowFullscreen(window, fs_flag); +#else + SDL_WM_ToggleFullScreen(window); +#endif + + ri.Cvar_SetValue("vid_fullscreen", fullscreen); + } + + /* Are we now? */ + if (fullscreen == IsFullscreen()) + { + return true; + } + } + + SWimp_DestroyRender(); + + // let the sound and input subsystems know about the new window + ri.Vid_NewWindow (vid.width, vid.height); + +#if !SDL_VERSION_ATLEAST(2, 0, 0) + /* Set window icon - For SDL1.2, this must be done before creating the window */ + SetSDLIcon(); +#endif + + flags = SDL_SWSURFACE; + if (fs_flag) + { + flags |= fs_flag; + } + + while (1) + { + if (!CreateSDLWindow(flags, width, height)) + { + Sys_Error("(SOFTSDL) SDL SetVideoMode failed: %s\n", SDL_GetError()); + return false; + } + else + { + break; + } + } + + if(!R_InitContext(window)) + { + // InitContext() should have logged an error + return false; + } + + /* Note: window title is now set in re.InitContext() to include renderer name */ +#if SDL_VERSION_ATLEAST(2, 0, 0) + /* Set the window icon - For SDL2, this must be done after creating the window */ + SetSDLIcon(); +#endif + + /* No cursor */ + SDL_ShowCursor(0); + + vid_buffer = malloc(vid.height * vid.width * sizeof(pixel_t)); + + sintable = malloc((vid.width+CYCLE) * sizeof(int)); + intsintable = malloc((vid.width+CYCLE) * sizeof(int)); + blanktable = malloc((vid.width+CYCLE) * sizeof(int)); + + newedges = malloc(vid.width * sizeof(edge_t *)); + removeedges = malloc(vid.width * sizeof(edge_t *)); + + // 1 extra for spanpackage that marks end + triangle_spans = malloc((vid.width + 1) * sizeof(spanpackage_t)); + + warp_rowptr = malloc((vid.width+AMP2*2) * sizeof(byte*)); + warp_column = malloc((vid.width+AMP2*2) * sizeof(int)); + + edge_basespans = malloc((vid.width*2) * sizeof(espan_t)); + finalverts = malloc((MAXALIASVERTS + 3) * sizeof(finalvert_t)); + ledges = malloc((NUMSTACKEDGES + 1) * sizeof(edge_t)); + lsurfs = malloc((NUMSTACKSURFACES + 1) * sizeof(surf_t)); + r_warpbuffer = malloc(WARP_WIDTH * WARP_HEIGHT * sizeof(pixel_t)); + + if ((vid.width >= 2048) && (sizeof(shift20_t) == 4)) // 2k+ resolution and 32 == shift20_t + { + shift_size = 18; + } + else + { + shift_size = 20; + } + + R_InitTurb (); + + vid_polygon_spans = malloc(sizeof(espan_t) * (vid.height + 1)); + + memset(sw_state.currentpalette, 0, sizeof(sw_state.currentpalette)); + + X11_active = true; + + return true; +} + +/* +** RE_EndFrame +** +** This does an implementation specific copy from the backbuffer to the +** front buffer. In the Win32 case it uses BitBlt or BltFast depending +** on whether we're using DIB sections/GDI or DDRAW. +*/ + +void RE_EndFrame (void) +{ + int y,x, i; + const unsigned char *pallete = sw_state.currentpalette; + Uint32 pallete_colors[256]; + + for(i=0; i < 256; i++) + { +#if SDL_VERSION_ATLEAST(2, 0, 0) + pallete_colors[i] = SDL_MapRGB(surface->format, + pallete[i * 4 + 0], // red + pallete[i * 4 + 1], // green + pallete[i * 4 + 2] //blue + ); +#else + pallete_colors[i] = SDL_MapRGB(window->format, + pallete[i * 4 + 0], // red + pallete[i * 4 + 1], // green + pallete[i * 4 + 2] //blue + ); +#endif + } + +#if SDL_VERSION_ATLEAST(2, 0, 0) + Uint32 * pixels = (Uint32 *)surface->pixels; +#else + Uint32 * pixels = (Uint32 *)window->pixels; +#endif + for (y=0; y < vid.height; y++) + { + for (x=0; x < vid.width; x ++) + { + int buffer_pos = y * vid.width + x; + Uint32 color = pallete_colors[vid_buffer[buffer_pos]]; +#if SDL_VERSION_ATLEAST(2, 0, 0) + pixels[y * surface->pitch / sizeof(Uint32) + x] = color; +#else + pixels[y * window->pitch / sizeof(Uint32) + x] = color; +#endif + } + } + +#if SDL_VERSION_ATLEAST(2, 0, 0) + SDL_UpdateTexture(texture, NULL, surface->pixels, surface->pitch); + SDL_RenderClear(renderer); + SDL_RenderCopy(renderer, texture, NULL, NULL); + SDL_RenderPresent(renderer); +#else + /* SDL_Flip(window); */ + SDL_UpdateRect(window, 0, 0, 0, 0); +#endif +} + +/* +** SWimp_SetMode +*/ +rserr_t SWimp_SetMode(int *pwidth, int *pheight, int mode, qboolean fullscreen ) +{ + rserr_t retval = rserr_ok; + + R_Printf (PRINT_ALL, "setting mode %d:", mode ); + + if ( !ri.Vid_GetModeInfo( pwidth, pheight, mode ) ) + { + R_Printf( PRINT_ALL, " invalid mode\n" ); + return rserr_invalid_mode; + } + + R_Printf( PRINT_ALL, " %d %d\n", *pwidth, *pheight); + + if ( !SWimp_InitGraphics(fullscreen, pwidth, pheight) ) { + // failed to set a valid mode in windowed mode + return rserr_invalid_mode; + } + + R_GammaCorrectAndSetPalette( ( const unsigned char * ) d_8to24table ); + + return retval; +} + +/* +** SWimp_Shutdown +** +** System specific graphics subsystem shutdown routine. Destroys +** DIBs or DDRAW surfaces as appropriate. +*/ + +void SWimp_Shutdown( void ) +{ + SWimp_DestroyRender(); + + if (SDL_WasInit(SDL_INIT_EVERYTHING) == SDL_INIT_VIDEO) + SDL_Quit(); + else + SDL_QuitSubSystem(SDL_INIT_VIDEO); + + X11_active = false; +} + +// this is only here so the functions in q_shared.c and q_shwin.c can link +void Sys_Error (char *error, ...) +{ + va_list argptr; + char text[1024]; + + va_start (argptr, error); + vsprintf (text, error, argptr); + va_end (argptr); + + ri.Sys_Error (ERR_FATAL, "%s", text); +} + +void Com_Printf (char *fmt, ...) +{ + va_list argptr; + char text[1024]; + + va_start (argptr, fmt); + vsprintf (text, fmt, argptr); + va_end (argptr); + + R_Printf(PRINT_ALL, "%s", text); +} + +/* +============================================================================== + +SCREEN SHOTS + +============================================================================== +*/ + +/* +================== +R_ScreenShot_f +================== +*/ +void +R_ScreenShot_f(void) +{ + int x, y; + byte *buffer = malloc(vid.width * vid.height * 3); + const unsigned char *pallete = sw_state.currentpalette; + + if (!buffer) + { + R_Printf(PRINT_ALL, "R_ScreenShot: Couldn't malloc %d bytes\n", vid.width * vid.height * 3); + return; + } + + for (x=0; x < vid.width; x ++) + { + for (y=0; y < vid.height; y ++) { + int buffer_pos = y * vid.width + x; + buffer[buffer_pos * 3 + 0] = pallete[vid_buffer[buffer_pos] * 4 + 0]; // red + buffer[buffer_pos * 3 + 1] = pallete[vid_buffer[buffer_pos] * 4 + 1]; // green + buffer[buffer_pos * 3 + 2] = pallete[vid_buffer[buffer_pos] * 4 + 2]; // blue + } + } + + ri.Vid_WriteScreenshot(vid.width, vid.height, 3, buffer); + + free(buffer); +} diff --git a/src/client/refresh/soft/r_misc.c b/src/client/refresh/soft/r_misc.c new file mode 100644 index 00000000..343b8079 --- /dev/null +++ b/src/client/refresh/soft/r_misc.c @@ -0,0 +1,438 @@ +/* +Copyright (C) 1997-2001 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +// r_misc.c +#ifdef SDL2 +#include +#else // SDL1.2 +#include +#endif //SDL2 + +#include "r_local.h" + +#define NUM_MIPS 4 + +cvar_t *sw_mipcap; +cvar_t *sw_mipscale; + +surfcache_t *d_initial_rover; +qboolean d_roverwrapped; +int d_minmip; +float d_scalemip[NUM_MIPS-1]; + +static float basemip[NUM_MIPS-1] = {1.0, 0.5*0.8, 0.25*0.8}; + +extern int d_aflatcolor; + +int d_vrectx, d_vrecty, d_vrectright_particle, d_vrectbottom_particle; + +int d_pix_min, d_pix_max, d_pix_shift; + +/* +================ +D_Patch +================ +*/ +void D_Patch (void) +{ +} +/* +================ +D_ViewChanged +================ +*/ +unsigned char *alias_colormap; + +void D_ViewChanged (void) +{ + scale_for_mip = xscale; + if (yscale > xscale) + scale_for_mip = yscale; + + d_zrowbytes = vid.width * 2; + d_zwidth = vid.width; + + d_pix_min = r_refdef.vrect.width / 320; + if (d_pix_min < 1) + d_pix_min = 1; + + d_pix_max = (int)((float)r_refdef.vrect.width / (320.0 / 4.0) + 0.5); + d_pix_shift = 8 - (int)((float)r_refdef.vrect.width / 320.0 + 0.5); + if (d_pix_max < 1) + d_pix_max = 1; + + d_vrectx = r_refdef.vrect.x; + d_vrecty = r_refdef.vrect.y; + d_vrectright_particle = r_refdef.vrectright - d_pix_max; + d_vrectbottom_particle = + r_refdef.vrectbottom - d_pix_max; + + /* + ** clear Z-buffer and color-buffers if we're doing the gallery + */ + if ( r_newrefdef.rdflags & RDF_NOWORLDMODEL ) + { + memset( d_pzbuffer, 0xff, vid.width * vid.height * sizeof(zvalue_t) ); + RE_Draw_Fill( r_newrefdef.x, r_newrefdef.y, r_newrefdef.width, r_newrefdef.height,( int ) sw_clearcolor->value & 0xff ); + } + + alias_colormap = vid_colormap; + + D_Patch (); +} + + + +/* +============= +R_PrintTimes +============= +*/ +void R_PrintTimes (void) +{ + int r_time2; + int ms; + + r_time2 = SDL_GetTicks(); + + ms = r_time2 - r_time1; + + R_Printf(PRINT_ALL,"%5i ms %3i/%3i/%3i poly %3i surf\n", + ms, c_faceclip, r_polycount, r_drawnpolycount, c_surf); + c_surf = 0; +} + + +/* +============= +R_PrintDSpeeds +============= +*/ +void R_PrintDSpeeds (void) +{ + int ms, dp_time, r_time2, rw_time, db_time, se_time, de_time, da_time; + + r_time2 = SDL_GetTicks(); + + da_time = (da_time2 - da_time1); + dp_time = (dp_time2 - dp_time1); + rw_time = (rw_time2 - rw_time1); + db_time = (db_time2 - db_time1); + se_time = (se_time2 - se_time1); + de_time = (de_time2 - de_time1); + ms = (r_time2 - r_time1); + + R_Printf(PRINT_ALL,"%3i %2ip %2iw %2ib %2is %2ie %2ia\n", + ms, dp_time, rw_time, db_time, se_time, de_time, da_time); +} + + +/* +============= +R_PrintAliasStats +============= +*/ +void R_PrintAliasStats (void) +{ + R_Printf(PRINT_ALL,"%3i polygon model drawn\n", r_amodels_drawn); +} + + + +/* +=================== +R_TransformFrustum +=================== +*/ +void R_TransformFrustum (void) +{ + int i; + vec3_t v, v2; + + for (i=0 ; i<4 ; i++) + { + v[0] = screenedge[i].normal[2]; + v[1] = -screenedge[i].normal[0]; + v[2] = screenedge[i].normal[1]; + + v2[0] = v[1]*vright[0] + v[2]*vup[0] + v[0]*vpn[0]; + v2[1] = v[1]*vright[1] + v[2]*vup[1] + v[0]*vpn[1]; + v2[2] = v[1]*vright[2] + v[2]*vup[2] + v[0]*vpn[2]; + + VectorCopy (v2, view_clipplanes[i].normal); + + view_clipplanes[i].dist = DotProduct (modelorg, v2); + } +} + + +/* +================ +TransformVector +================ +*/ +void TransformVector (vec3_t in, vec3_t out) +{ + out[0] = DotProduct(in,vright); + out[1] = DotProduct(in,vup); + out[2] = DotProduct(in,vpn); +} + +/* +================ +R_TransformPlane +================ +*/ +void R_TransformPlane (mplane_t *p, float *normal, float *dist) +{ + float d; + + d = DotProduct (r_origin, p->normal); + *dist = p->dist - d; +// TODO: when we have rotating entities, this will need to use the view matrix + TransformVector (p->normal, normal); +} + + +/* +=============== +R_SetUpFrustumIndexes +=============== +*/ +void R_SetUpFrustumIndexes (void) +{ + int i, j, *pindex; + + pindex = r_frustum_indexes; + + for (i=0 ; i<4 ; i++) + { + for (j=0 ; j<3 ; j++) + { + if (view_clipplanes[i].normal[j] < 0) + { + pindex[j] = j; + pindex[j+3] = j+3; + } + else + { + pindex[j] = j+3; + pindex[j+3] = j; + } + } + + // FIXME: do just once at start + pfrustum_indexes[i] = pindex; + pindex += 6; + } +} + +/* +=============== +R_ViewChanged + +Called every time the vid structure or r_refdef changes. +Guaranteed to be called before the first refresh +=============== +*/ +void R_ViewChanged (vrect_t *vr) +{ + int i; + + r_refdef.vrect = *vr; + + r_refdef.horizontalFieldOfView = 2*tan((float)r_newrefdef.fov_x/360*M_PI);; + verticalFieldOfView = 2*tan((float)r_newrefdef.fov_y/360*M_PI); + + r_refdef.fvrectx = (float)r_refdef.vrect.x; + r_refdef.fvrectx_adj = (float)r_refdef.vrect.x - 0.5; + r_refdef.vrect_x_adj_shift20 = (r_refdef.vrect.x<modified) + { + r_fullbright->modified = false; + D_FlushCaches (); // so all lighting changes + } + + r_framecount++; + + + // build the transformation matrix for the given view angles + VectorCopy (r_refdef.vieworg, modelorg); + VectorCopy (r_refdef.vieworg, r_origin); + + AngleVectors (r_refdef.viewangles, vpn, vright, vup); + + // current viewleaf + if ( !( r_newrefdef.rdflags & RDF_NOWORLDMODEL ) ) + { + r_viewleaf = Mod_PointInLeaf (r_origin, r_worldmodel); + r_viewcluster = r_viewleaf->cluster; + } + + if (sw_waterwarp->value && (r_newrefdef.rdflags & RDF_UNDERWATER) ) + r_dowarp = true; + else + r_dowarp = false; + + if (r_dowarp) + { + // warp into off screen buffer + vrect.x = 0; + vrect.y = 0; + vrect.width = r_newrefdef.width < WARP_WIDTH ? r_newrefdef.width : WARP_WIDTH; + vrect.height = r_newrefdef.height < WARP_HEIGHT ? r_newrefdef.height : WARP_HEIGHT; + + d_viewbuffer = r_warpbuffer; + r_screenwidth = WARP_WIDTH; + } + else + { + vrect.x = r_newrefdef.x; + vrect.y = r_newrefdef.y; + vrect.width = r_newrefdef.width; + vrect.height = r_newrefdef.height; + + d_viewbuffer = vid_buffer; + r_screenwidth = vid.width; + } + + R_ViewChanged (&vrect); + + // start off with just the four screen edge clip planes + R_TransformFrustum (); + R_SetUpFrustumIndexes (); + + // save base values + VectorCopy (vpn, base_vpn); + VectorCopy (vright, base_vright); + VectorCopy (vup, base_vup); + + // clear frame counts + c_faceclip = 0; + d_spanpixcount = 0; + r_polycount = 0; + r_drawnpolycount = 0; + r_wholepolycount = 0; + r_amodels_drawn = 0; + r_outofsurfaces = 0; + r_outofedges = 0; + + // d_setup + d_roverwrapped = false; + d_initial_rover = sc_rover; + + d_minmip = sw_mipcap->value; + if (d_minmip > 3) + d_minmip = 3; + else if (d_minmip < 0) + d_minmip = 0; + + for (i=0 ; i<(NUM_MIPS-1) ; i++) + d_scalemip[i] = basemip[i] * sw_mipscale->value; + + d_aflatcolor = 0; +} diff --git a/src/client/refresh/soft/r_model.c b/src/client/refresh/soft/r_model.c new file mode 100644 index 00000000..13187d46 --- /dev/null +++ b/src/client/refresh/soft/r_model.c @@ -0,0 +1,1265 @@ +/* +Copyright (C) 1997-2001 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +// models.c -- model loading and caching + +// models are the only shared resource between a client and server running +// on the same machine. + +#include "r_local.h" + +model_t *loadmodel; +char loadname[32]; // for hunk tags + +void Mod_LoadSpriteModel (model_t *mod, void *buffer); +void Mod_LoadBrushModel (model_t *mod, void *buffer); +void Mod_LoadAliasModel (model_t *mod, void *buffer); +model_t *Mod_LoadModel (model_t *mod, qboolean crash); + +byte mod_novis[MAX_MAP_LEAFS/8]; + +#define MAX_MOD_KNOWN 256 +model_t mod_known[MAX_MOD_KNOWN]; +int mod_numknown; + +// the inline * models from the current map are kept seperate +model_t mod_inline[MAX_MOD_KNOWN]; + +int registration_sequence; +int modfilelen; + +//=============================================================================== + + +/* +================ +Mod_Modellist_f +================ +*/ +void Mod_Modellist_f (void) +{ + int i; + model_t *mod; + int total; + + total = 0; + R_Printf(PRINT_ALL,"Loaded models:\n"); + for (i=0, mod=mod_known ; i < mod_numknown ; i++, mod++) + { + if (!mod->name[0]) + continue; + R_Printf(PRINT_ALL, "%8i : %s\n",mod->extradatasize, mod->name); + total += mod->extradatasize; + } + R_Printf(PRINT_ALL, "Total resident: %i\n", total); +} + +/* +=============== +Mod_Init +=============== +*/ +void Mod_Init (void) +{ + memset (mod_novis, 0xff, sizeof(mod_novis)); +} + +/* +================== +Mod_ForName + +Loads in a model for the given name +================== +*/ +model_t *Mod_ForName (char *name, qboolean crash) +{ + model_t *mod; + unsigned *buf; + int i; + + if (!name[0]) + ri.Sys_Error (ERR_DROP,"Mod_ForName: NULL name"); + + // + // inline models are grabbed only from worldmodel + // + if (name[0] == '*') + { + i = atoi(name+1); + if (i < 1 || !r_worldmodel || i >= r_worldmodel->numsubmodels) + ri.Sys_Error (ERR_DROP, "bad inline model number"); + return &mod_inline[i]; + } + + // + // search the currently loaded models + // + for (i=0 , mod=mod_known ; iname, name) ) + return mod; + + // + // find a free model slot spot + // + for (i=0 , mod=mod_known ; iname[0]) + break; // free spot + } + if (i == mod_numknown) + { + if (mod_numknown == MAX_MOD_KNOWN) + ri.Sys_Error (ERR_DROP, "mod_numknown == MAX_MOD_KNOWN"); + mod_numknown++; + } + strcpy (mod->name, name); + + // + // load the file + // + modfilelen = ri.FS_LoadFile (mod->name, (void **)&buf); + if (!buf) + { + if (crash) + ri.Sys_Error (ERR_DROP,"Mod_NumForName: %s not found", mod->name); + memset (mod->name, 0, sizeof(mod->name)); + return NULL; + } + + loadmodel = mod; + + // + // fill it in + // + + // call the apropriate loader + + switch (LittleLong(*(unsigned *)buf)) + { + case IDALIASHEADER: + loadmodel->extradata = Hunk_Begin (0x200000); + Mod_LoadAliasModel (mod, buf); + break; + + case IDSPRITEHEADER: + loadmodel->extradata = Hunk_Begin (0x10000); + Mod_LoadSpriteModel (mod, buf); + break; + + case IDBSPHEADER: + loadmodel->extradata = Hunk_Begin (0x1000000); + Mod_LoadBrushModel (mod, buf); + break; + + default: + ri.Sys_Error (ERR_DROP,"Mod_NumForName: unknown fileid for %s", mod->name); + break; + } + + loadmodel->extradatasize = Hunk_End (); + + ri.FS_FreeFile (buf); + + return mod; +} + + +/* +=============== +Mod_PointInLeaf +=============== +*/ +mleaf_t *Mod_PointInLeaf (vec3_t p, model_t *model) +{ + mnode_t *node; + + if (!model || !model->nodes) + ri.Sys_Error (ERR_DROP, "Mod_PointInLeaf: bad model"); + + node = model->nodes; + while (1) + { + float d; + mplane_t *plane; + + if (node->contents != -1) + return (mleaf_t *)node; + plane = node->plane; + d = DotProduct (p,plane->normal) - plane->dist; + if (d > 0) + node = node->children[0]; + else + node = node->children[1]; + } + + return NULL; // never reached +} + + +/* +=================== +Mod_DecompressVis +=================== +*/ +byte *Mod_DecompressVis (byte *in, model_t *model) +{ + static byte decompressed[MAX_MAP_LEAFS/8]; + int c; + byte *out; + int row; + + row = (model->vis->numclusters+7)>>3; + out = decompressed; + + if (!in) + { + // no vis info, so make all visible + while (row) + { + *out++ = 0xff; + row--; + } + return decompressed; + } + + do + { + if (*in) + { + *out++ = *in++; + continue; + } + + c = in[1]; + in += 2; + while (c) + { + *out++ = 0; + c--; + } + } while (out - decompressed < row); + + return decompressed; +} + +/* +============== +Mod_ClusterPVS +============== +*/ +byte *Mod_ClusterPVS (int cluster, model_t *model) +{ + if (cluster == -1 || !model->vis) + return mod_novis; + return Mod_DecompressVis ( (byte *)model->vis + model->vis->bitofs[cluster][DVIS_PVS], + model); +} + +/* +=============================================================================== + + BRUSHMODEL LOADING + +=============================================================================== +*/ + +byte *mod_base; + + +/* +================= +Mod_LoadLighting + +Converts the 24 bit lighting down to 8 bit +by taking the brightest component +================= +*/ +void Mod_LoadLighting (lump_t *l) +{ + int i, size; + byte *in; + + if (!l->filelen) + { + loadmodel->lightdata = NULL; + return; + } + size = l->filelen/3; + loadmodel->lightdata = Hunk_Alloc (size); + in = (void *)(mod_base + l->fileofs); + for (i=0 ; i in[1] && in[0] > in[2]) + loadmodel->lightdata[i] = in[0]; + else if (in[1] > in[0] && in[1] > in[2]) + loadmodel->lightdata[i] = in[1]; + else + loadmodel->lightdata[i] = in[2]; + } +} + + +int r_leaftovis[MAX_MAP_LEAFS]; +int r_vistoleaf[MAX_MAP_LEAFS]; +int r_numvisleafs; + +void R_NumberLeafs (mnode_t *node) +{ + if (node->contents != -1) + { + mleaf_t *leaf; + int leafnum; + + leaf = (mleaf_t *)node; + leafnum = leaf - loadmodel->leafs; + if (leaf->contents & CONTENTS_SOLID) + return; + r_leaftovis[leafnum] = r_numvisleafs; + r_vistoleaf[r_numvisleafs] = leafnum; + r_numvisleafs++; + return; + } + + R_NumberLeafs (node->children[0]); + R_NumberLeafs (node->children[1]); +} + + +/* +================= +Mod_LoadVisibility +================= +*/ +void Mod_LoadVisibility (lump_t *l) +{ + int i; + + if (!l->filelen) + { + loadmodel->vis = NULL; + return; + } + loadmodel->vis = Hunk_Alloc ( l->filelen); + memcpy (loadmodel->vis, mod_base + l->fileofs, l->filelen); + + loadmodel->vis->numclusters = LittleLong (loadmodel->vis->numclusters); + for (i=0 ; ivis->numclusters ; i++) + { + loadmodel->vis->bitofs[i][0] = LittleLong (loadmodel->vis->bitofs[i][0]); + loadmodel->vis->bitofs[i][1] = LittleLong (loadmodel->vis->bitofs[i][1]); + } +} + + +/* +================= +Mod_LoadVertexes +================= +*/ +void Mod_LoadVertexes (lump_t *l) +{ + dvertex_t *in; + mvertex_t *out; + int i, count; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + ri.Sys_Error (ERR_DROP,"MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_Alloc ( (count+8)*sizeof(*out)); // extra for skybox + /* + * PATCH: eliasm + * + * This patch fixes the problem where the games dumped core + * when changing levels. + */ + memset( out, 0, (count + 6) * sizeof( *out ) ); + /* END OF PATCH */ + + loadmodel->vertexes = out; + loadmodel->numvertexes = count; + + for ( i=0 ; iposition[0] = LittleFloat (in->point[0]); + out->position[1] = LittleFloat (in->point[1]); + out->position[2] = LittleFloat (in->point[2]); + } +} + +/* +================= +Mod_LoadSubmodels +================= +*/ +void Mod_LoadSubmodels (lump_t *l) +{ + dmodel_t *in; + dmodel_t *out; + int i, j, count; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + ri.Sys_Error (ERR_DROP,"MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_Alloc ( count*sizeof(*out)); + + loadmodel->submodels = out; + loadmodel->numsubmodels = count; + + for ( i=0 ; imins[j] = LittleFloat (in->mins[j]) - 1; + out->maxs[j] = LittleFloat (in->maxs[j]) + 1; + out->origin[j] = LittleFloat (in->origin[j]); + } + out->headnode = LittleLong (in->headnode); + out->firstface = LittleLong (in->firstface); + out->numfaces = LittleLong (in->numfaces); + } +} + +/* +================= +Mod_LoadEdges +================= +*/ +void Mod_LoadEdges (lump_t *l) +{ + dedge_t *in; + medge_t *out; + int i, count; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + ri.Sys_Error (ERR_DROP,"MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_Alloc ( (count + 13) * sizeof(*out)); // extra for skybox + + loadmodel->edges = out; + loadmodel->numedges = count; + + for ( i=0 ; iv[0] = (unsigned short)LittleShort(in->v[0]); + out->v[1] = (unsigned short)LittleShort(in->v[1]); + } +} + +/* +================= +Mod_LoadTexinfo +================= +*/ +void Mod_LoadTexinfo (lump_t *l) +{ + texinfo_t *in; + mtexinfo_t *out, *step; + int i, count; + char name[MAX_QPATH]; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + ri.Sys_Error (ERR_DROP,"Mod_LoadTexinfo: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_Alloc ( (count+6)*sizeof(*out)); // extra for skybox + + loadmodel->texinfo = out; + loadmodel->numtexinfo = count; + + for ( i=0 ; ivecs[0][j] = LittleFloat(in->vecs[0][j]); + out->vecs[1][j] = LittleFloat(in->vecs[1][j]); + } + len1 = VectorLength (out->vecs[0]); + len2 = VectorLength (out->vecs[1]); + len1 = (len1 + len2)/2; + if (len1 < 0.32) + out->mipadjust = 4; + else if (len1 < 0.49) + out->mipadjust = 3; + else if (len1 < 0.99) + out->mipadjust = 2; + else + out->mipadjust = 1; + + out->flags = LittleLong (in->flags); + + next = LittleLong (in->nexttexinfo); + if (next > 0) + out->next = loadmodel->texinfo + next; + /* + * PATCH: eliasm + * + * This patch fixes the problem where the game + * domed core when loading a new level. + */ + else { + out->next = NULL; + } + /* END OF PATCH */ + + Com_sprintf (name, sizeof(name), "textures/%s.wal", in->texture); + out->image = R_FindImage (name, it_wall); + if (!out->image) + { + out->image = r_notexture_mip; // texture not found + out->flags = 0; + } + } + + // count animation frames + for (i=0 ; itexinfo[i]; + out->numframes = 1; + for (step = out->next ; step && step != out ; step=step->next) + out->numframes++; + } +} + +/* +================ +CalcSurfaceExtents + +Fills in s->texturemins[] and s->extents[] +================ +*/ +void CalcSurfaceExtents (msurface_t *s) +{ + float mins[2], maxs[2], val; + int i; + mtexinfo_t *tex; + int bmins[2], bmaxs[2]; + + mins[0] = mins[1] = 999999; + maxs[0] = maxs[1] = -99999; + + tex = s->texinfo; + + for (i=0 ; inumedges ; i++) + { + int e, j; + mvertex_t *v; + + e = loadmodel->surfedges[s->firstedge+i]; + if (e >= 0) + v = &loadmodel->vertexes[loadmodel->edges[e].v[0]]; + else + v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]]; + + for (j=0 ; j<2 ; j++) + { + val = v->position[0] * tex->vecs[j][0] + + v->position[1] * tex->vecs[j][1] + + v->position[2] * tex->vecs[j][2] + + tex->vecs[j][3]; + if (val < mins[j]) + mins[j] = val; + if (val > maxs[j]) + maxs[j] = val; + } + } + + for (i=0 ; i<2 ; i++) + { + bmins[i] = floor(mins[i]/16); + bmaxs[i] = ceil(maxs[i]/16); + + s->texturemins[i] = bmins[i] * 16; + s->extents[i] = (bmaxs[i] - bmins[i]) * 16; + if (s->extents[i] < 16) + s->extents[i] = 16; // take at least one cache block + if ( !(tex->flags & (SURF_WARP|SURF_SKY)) && s->extents[i] > 256) + ri.Sys_Error (ERR_DROP,"Bad surface extents"); + } +} + + +/* +================= +Mod_LoadFaces +================= +*/ +void Mod_LoadFaces (lump_t *l) +{ + dface_t *in; + msurface_t *out; + int i, count, surfnum; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + ri.Sys_Error (ERR_DROP,"MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_Alloc ( (count+6)*sizeof(*out)); // extra for skybox + + loadmodel->surfaces = out; + loadmodel->numsurfaces = count; + + for ( surfnum=0 ; surfnumfirstedge = LittleLong(in->firstedge); + out->numedges = LittleShort(in->numedges); + if (out->numedges < 3) + ri.Sys_Error (ERR_DROP,"Surface with %d edges", out->numedges); + out->flags = 0; + + planenum = LittleShort(in->planenum); + side = LittleShort(in->side); + if (side) + out->flags |= SURF_PLANEBACK; + + out->plane = loadmodel->planes + planenum; + + out->texinfo = loadmodel->texinfo + LittleShort (in->texinfo); + + CalcSurfaceExtents (out); + + // lighting info is converted from 24 bit on disk to 8 bit + for (i=0 ; istyles[i] = in->styles[i]; + i = LittleLong(in->lightofs); + if (i == -1) + out->samples = NULL; + else + out->samples = loadmodel->lightdata + i/3; + + // set the drawing flags flag + + if (!out->texinfo->image) + continue; + if (out->texinfo->flags & SURF_SKY) + { + out->flags |= SURF_DRAWSKY; + continue; + } + + if (out->texinfo->flags & SURF_WARP) + { + out->flags |= SURF_DRAWTURB; + for (i=0 ; i<2 ; i++) + { + out->extents[i] = 16384; + out->texturemins[i] = -8192; + } + continue; + } + + //============== + //PGM + // this marks flowing surfaces as turbulent, but with the new + // SURF_FLOW flag. + if (out->texinfo->flags & SURF_FLOWING) + { + out->flags |= SURF_DRAWTURB | SURF_FLOW; + for (i=0 ; i<2 ; i++) + { + out->extents[i] = 16384; + out->texturemins[i] = -8192; + } + continue; + } + //PGM + //============== + } +} + + +/* +================= +Mod_SetParent +================= +*/ +void Mod_SetParent (mnode_t *node, mnode_t *parent) +{ + node->parent = parent; + if (node->contents != -1) + return; + Mod_SetParent (node->children[0], node); + Mod_SetParent (node->children[1], node); +} + +/* +================= +Mod_LoadNodes +================= +*/ +void Mod_LoadNodes (lump_t *l) +{ + int i, count; + dnode_t *in; + mnode_t *out; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + ri.Sys_Error (ERR_DROP,"MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_Alloc ( count*sizeof(*out)); + + loadmodel->nodes = out; + loadmodel->numnodes = count; + + for ( i=0 ; iminmaxs[j] = LittleShort (in->mins[j]); + out->minmaxs[3+j] = LittleShort (in->maxs[j]); + } + + p = LittleLong(in->planenum); + out->plane = loadmodel->planes + p; + + out->firstsurface = LittleShort (in->firstface); + out->numsurfaces = LittleShort (in->numfaces); + out->contents = CONTENTS_NODE; // differentiate from leafs + + for (j=0 ; j<2 ; j++) + { + p = LittleLong (in->children[j]); + if (p >= 0) + out->children[j] = loadmodel->nodes + p; + else + out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p)); + } + } + + Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs +} + +/* +================= +Mod_LoadLeafs +================= +*/ +void Mod_LoadLeafs (lump_t *l) +{ + dleaf_t *in; + mleaf_t *out; + int i, j, count; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + ri.Sys_Error (ERR_DROP,"MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_Alloc ( count*sizeof(*out)); + + loadmodel->leafs = out; + loadmodel->numleafs = count; + + for ( i=0 ; iminmaxs[j] = LittleShort (in->mins[j]); + out->minmaxs[3+j] = LittleShort (in->maxs[j]); + } + + out->contents = LittleLong(in->contents); + out->cluster = LittleShort(in->cluster); + out->area = LittleShort(in->area); + + out->firstmarksurface = loadmodel->marksurfaces + + LittleShort(in->firstleafface); + out->nummarksurfaces = LittleShort(in->numleaffaces); + } +} + + +/* +================= +Mod_LoadMarksurfaces +================= +*/ +void Mod_LoadMarksurfaces (lump_t *l) +{ + int i, count; + short *in; + msurface_t **out; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + ri.Sys_Error (ERR_DROP,"MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_Alloc ( count*sizeof(*out)); + + loadmodel->marksurfaces = out; + loadmodel->nummarksurfaces = count; + + for ( i=0 ; i= loadmodel->numsurfaces) + ri.Sys_Error (ERR_DROP,"Mod_ParseMarksurfaces: bad surface number"); + out[i] = loadmodel->surfaces + j; + } +} + +/* +================= +Mod_LoadSurfedges +================= +*/ +void Mod_LoadSurfedges (lump_t *l) +{ + int i, count; + int *in, *out; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + ri.Sys_Error (ERR_DROP,"MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_Alloc ( (count+24)*sizeof(*out)); // extra for skybox + + loadmodel->surfedges = out; + loadmodel->numsurfedges = count; + + for ( i=0 ; ifileofs); + if (l->filelen % sizeof(*in)) + ri.Sys_Error (ERR_DROP,"MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_Alloc ( (count+6)*sizeof(*out)); // extra for skybox + + loadmodel->planes = out; + loadmodel->numplanes = count; + + for ( i=0 ; inormal[j] = LittleFloat (in->normal[j]); + if (out->normal[j] < 0) + bits |= 1<dist = LittleFloat (in->dist); + out->type = LittleLong (in->type); + out->signbits = bits; + } +} + + +/* +================= +Mod_LoadBrushModel +================= +*/ +void Mod_LoadBrushModel (model_t *mod, void *buffer) +{ + int i; + dheader_t *header; + dmodel_t *bm; + + loadmodel->type = mod_brush; + if (loadmodel != mod_known) + ri.Sys_Error (ERR_DROP, "Loaded a brush model after the world"); + + header = (dheader_t *)buffer; + + i = LittleLong (header->version); + if (i != BSPVERSION) + ri.Sys_Error (ERR_DROP,"Mod_LoadBrushModel: %s has wrong version number (%i should be %i)", mod->name, i, BSPVERSION); + + // swap all the lumps + mod_base = (byte *)header; + + for (i=0 ; ilumps[LUMP_VERTEXES]); + Mod_LoadEdges (&header->lumps[LUMP_EDGES]); + Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]); + Mod_LoadLighting (&header->lumps[LUMP_LIGHTING]); + Mod_LoadPlanes (&header->lumps[LUMP_PLANES]); + Mod_LoadTexinfo (&header->lumps[LUMP_TEXINFO]); + Mod_LoadFaces (&header->lumps[LUMP_FACES]); + Mod_LoadMarksurfaces (&header->lumps[LUMP_LEAFFACES]); + Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]); + Mod_LoadLeafs (&header->lumps[LUMP_LEAFS]); + Mod_LoadNodes (&header->lumps[LUMP_NODES]); + Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]); + r_numvisleafs = 0; + R_NumberLeafs (loadmodel->nodes); + + // + // set up the submodels + // + for (i=0 ; inumsubmodels ; i++) + { + model_t *starmod; + + bm = &mod->submodels[i]; + starmod = &mod_inline[i]; + + *starmod = *loadmodel; + + starmod->firstmodelsurface = bm->firstface; + starmod->nummodelsurfaces = bm->numfaces; + starmod->firstnode = bm->headnode; + if (starmod->firstnode >= loadmodel->numnodes) + ri.Sys_Error (ERR_DROP, "Inline model %i has bad firstnode", i); + + VectorCopy (bm->maxs, starmod->maxs); + VectorCopy (bm->mins, starmod->mins); + + if (i == 0) + *loadmodel = *starmod; + } + + R_InitSkyBox (); +} + +/* +============================================================================== + +ALIAS MODELS + +============================================================================== +*/ + +/* +================= +Mod_LoadAliasModel +================= +*/ +void Mod_LoadAliasModel (model_t *mod, void *buffer) +{ + int i, j; + dmdl_t *pinmodel, *pheader; + dstvert_t *pinst, *poutst; + dtriangle_t *pintri, *pouttri; + int *pincmd, *poutcmd; + int version; + + pinmodel = (dmdl_t *)buffer; + + version = LittleLong (pinmodel->version); + if (version != ALIAS_VERSION) + ri.Sys_Error (ERR_DROP, "%s has wrong version number (%i should be %i)", + mod->name, version, ALIAS_VERSION); + + pheader = Hunk_Alloc (LittleLong(pinmodel->ofs_end)); + + // byte swap the header fields and sanity check + for (i=0 ; iskinheight > MAX_LBM_HEIGHT) + ri.Sys_Error (ERR_DROP, "model %s has a skin taller than %d", mod->name, + MAX_LBM_HEIGHT); + + if (pheader->num_xyz <= 0) + ri.Sys_Error (ERR_DROP, "model %s has no vertices", mod->name); + + if (pheader->num_xyz > MAX_VERTS) + ri.Sys_Error (ERR_DROP, "model %s has too many vertices", mod->name); + + if (pheader->num_st <= 0) + ri.Sys_Error (ERR_DROP, "model %s has no st vertices", mod->name); + + if (pheader->num_tris <= 0) + ri.Sys_Error (ERR_DROP, "model %s has no triangles", mod->name); + + if (pheader->num_frames <= 0) + ri.Sys_Error (ERR_DROP, "model %s has no frames", mod->name); + + // + // load base s and t vertices (not used in gl version) + // + pinst = (dstvert_t *) ((byte *)pinmodel + pheader->ofs_st); + poutst = (dstvert_t *) ((byte *)pheader + pheader->ofs_st); + + for (i=0 ; inum_st ; i++) + { + poutst[i].s = LittleShort (pinst[i].s); + poutst[i].t = LittleShort (pinst[i].t); + } + + // + // load triangle lists + // + pintri = (dtriangle_t *) ((byte *)pinmodel + pheader->ofs_tris); + pouttri = (dtriangle_t *) ((byte *)pheader + pheader->ofs_tris); + + for (i=0 ; inum_tris ; i++) + { + for (j=0 ; j<3 ; j++) + { + pouttri[i].index_xyz[j] = LittleShort (pintri[i].index_xyz[j]); + pouttri[i].index_st[j] = LittleShort (pintri[i].index_st[j]); + } + } + + // + // load the frames + // + for (i=0 ; inum_frames ; i++) + { + daliasframe_t *pinframe, *poutframe; + + pinframe = (daliasframe_t *) ((byte *)pinmodel + + pheader->ofs_frames + i * pheader->framesize); + poutframe = (daliasframe_t *) ((byte *)pheader + + pheader->ofs_frames + i * pheader->framesize); + + memcpy (poutframe->name, pinframe->name, sizeof(poutframe->name)); + for (j=0 ; j<3 ; j++) + { + poutframe->scale[j] = LittleFloat (pinframe->scale[j]); + poutframe->translate[j] = LittleFloat (pinframe->translate[j]); + } + // verts are all 8 bit, so no swapping needed + memcpy (poutframe->verts, pinframe->verts, + pheader->num_xyz*sizeof(dtrivertx_t)); + + } + + mod->type = mod_alias; + + // + // load the glcmds + // + pincmd = (int *) ((byte *)pinmodel + pheader->ofs_glcmds); + poutcmd = (int *) ((byte *)pheader + pheader->ofs_glcmds); + for (i=0 ; inum_glcmds ; i++) + poutcmd[i] = LittleLong (pincmd[i]); + + + // register all skins + memcpy ((char *)pheader + pheader->ofs_skins, (char *)pinmodel + pheader->ofs_skins, + pheader->num_skins*MAX_SKINNAME); + for (i=0 ; inum_skins ; i++) + { + mod->skins[i] = R_FindImage ((char *)pheader + pheader->ofs_skins + i*MAX_SKINNAME, it_skin); + } +} + +/* +============================================================================== + +SPRITE MODELS + +============================================================================== +*/ + +/* +================= +Mod_LoadSpriteModel +================= +*/ +void Mod_LoadSpriteModel (model_t *mod, void *buffer) +{ + dsprite_t *sprin, *sprout; + int i; + + sprin = (dsprite_t *)buffer; + sprout = Hunk_Alloc (modfilelen); + + sprout->ident = LittleLong (sprin->ident); + sprout->version = LittleLong (sprin->version); + sprout->numframes = LittleLong (sprin->numframes); + + if (sprout->version != SPRITE_VERSION) + ri.Sys_Error (ERR_DROP, "%s has wrong version number (%i should be %i)", + mod->name, sprout->version, SPRITE_VERSION); + + if (sprout->numframes > MAX_MD2SKINS) + ri.Sys_Error (ERR_DROP, "%s has too many frames (%i > %i)", + mod->name, sprout->numframes, MAX_MD2SKINS); + + // byte swap everything + for (i=0 ; inumframes ; i++) + { + sprout->frames[i].width = LittleLong (sprin->frames[i].width); + sprout->frames[i].height = LittleLong (sprin->frames[i].height); + sprout->frames[i].origin_x = LittleLong (sprin->frames[i].origin_x); + sprout->frames[i].origin_y = LittleLong (sprin->frames[i].origin_y); + memcpy (sprout->frames[i].name, sprin->frames[i].name, MAX_SKINNAME); + mod->skins[i] = R_FindImage (sprout->frames[i].name, it_sprite); + } + + mod->type = mod_sprite; +} + +//============================================================================= + +/* +@@@@@@@@@@@@@@@@@@@@@ +RE_BeginRegistration + +Specifies the model that will be used as the world +@@@@@@@@@@@@@@@@@@@@@ +*/ +void RE_BeginRegistration (char *model) +{ + char fullname[MAX_QPATH]; + cvar_t *flushmap; + + registration_sequence++; + r_oldviewcluster = -1; // force markleafs + Com_sprintf (fullname, sizeof(fullname), "maps/%s.bsp", model); + + D_FlushCaches (); + // explicitly free the old map if different + // this guarantees that mod_known[0] is the world map + flushmap = ri.Cvar_Get ("flushmap", "0", 0); + if ( strcmp(mod_known[0].name, fullname) || flushmap->value) + Mod_Free (&mod_known[0]); + r_worldmodel = RE_RegisterModel (fullname); + R_NewMap (); +} + + +/* +@@@@@@@@@@@@@@@@@@@@@ +RE_RegisterModel + +@@@@@@@@@@@@@@@@@@@@@ +*/ +struct model_s *RE_RegisterModel (char *name) +{ + model_t *mod; + + mod = Mod_ForName (name, false); + if (mod) + { + int i; + + mod->registration_sequence = registration_sequence; + + // register any images used by the models + if (mod->type == mod_sprite) + { + dsprite_t *sprout; + + sprout = (dsprite_t *)mod->extradata; + for (i=0 ; inumframes ; i++) + mod->skins[i] = R_FindImage (sprout->frames[i].name, it_sprite); + } + else if (mod->type == mod_alias) + { + dmdl_t *pheader; + + pheader = (dmdl_t *)mod->extradata; + for (i=0 ; inum_skins ; i++) + mod->skins[i] = R_FindImage ((char *)pheader + pheader->ofs_skins + i*MAX_SKINNAME, it_skin); + //PGM + mod->numframes = pheader->num_frames; + //PGM + } + else if (mod->type == mod_brush) + { + for (i=0 ; inumtexinfo ; i++) + mod->texinfo[i].image->registration_sequence = registration_sequence; + } + } + return mod; +} + +/* +@@@@@@@@@@@@@@@@@@@@@ +RE_EndRegistration + +@@@@@@@@@@@@@@@@@@@@@ +*/ +void RE_EndRegistration (void) +{ + int i; + model_t *mod; + + for (i=0, mod=mod_known ; iname[0]) + continue; + if (mod->registration_sequence != registration_sequence) + { // don't need this model + Hunk_Free (mod->extradata); + memset (mod, 0, sizeof(*mod)); + } + else + { // make sure it is paged in + Com_PageInMemory (mod->extradata, mod->extradatasize); + } + } + + R_FreeUnusedImages (); +} + + +//============================================================================= + +/* +================ +Mod_Free +================ +*/ +void Mod_Free (model_t *mod) +{ + Hunk_Free (mod->extradata); + memset (mod, 0, sizeof(*mod)); +} + +/* +================ +Mod_FreeAll +================ +*/ +void Mod_FreeAll (void) +{ + int i; + + for (i=0 ; isurfedges[], negative numbers + int numedges; // are backwards edges + + // surface generation data + struct surfcache_s *cachespots[MIPLEVELS]; + + short texturemins[2]; + short extents[2]; + + mtexinfo_t *texinfo; + + // lighting info + byte styles[MAXLIGHTMAPS]; + byte *samples; // [numstyles*surfsize] + + struct msurface_s *nextalphasurface; +} msurface_t; + + +#define CONTENTS_NODE -1 +typedef struct mnode_s +{ +// common with leaf + int contents; // CONTENTS_NODE, to differentiate from leafs + int visframe; // node needs to be traversed if current + + short minmaxs[6]; // for bounding box culling + + struct mnode_s *parent; + + // node specific + mplane_t *plane; + struct mnode_s *children[2]; + + unsigned short firstsurface; + unsigned short numsurfaces; +} mnode_t; + +typedef struct mleaf_s +{ + // common with node + int contents; // wil be something other than CONTENTS_NODE + int visframe; // node needs to be traversed if current + + short minmaxs[6]; // for bounding box culling + + struct mnode_s *parent; + + // leaf specific + int cluster; + int area; + + msurface_t **firstmarksurface; + int nummarksurfaces; + int key; // BSP sequence number for leaf's contents +} mleaf_t; + + +//=================================================================== + +// +// Whole model +// + +typedef struct model_s +{ + char name[MAX_QPATH]; + + int registration_sequence; + + modtype_t type; + int numframes; + + int flags; + + // + // volume occupied by the model graphics + // + vec3_t mins, maxs; + + // + // solid volume for clipping (sent from server) + // + qboolean clipbox; + vec3_t clipmins, clipmaxs; + + // + // brush model + // + int firstmodelsurface, nummodelsurfaces; + + int numsubmodels; + dmodel_t *submodels; + + int numplanes; + mplane_t *planes; + + int numleafs; // number of visible leafs, not counting 0 + mleaf_t *leafs; + + int numvertexes; + mvertex_t *vertexes; + + int numedges; + medge_t *edges; + + int numnodes; + int firstnode; + mnode_t *nodes; + + int numtexinfo; + mtexinfo_t *texinfo; + + int numsurfaces; + msurface_t *surfaces; + + int numsurfedges; + int *surfedges; + + int nummarksurfaces; + msurface_t **marksurfaces; + + dvis_t *vis; + + byte *lightdata; + + // for alias models and sprites + image_t *skins[MAX_MD2SKINS]; + void *extradata; + int extradatasize; +} model_t; + +//============================================================================ + +void Mod_Init(void); +void Mod_ClearAll(void); +model_t *Mod_ForName(char *name, qboolean crash); +void *Mod_Extradata(model_t *mod); // handles caching +void Mod_TouchModel(char *name); + +mleaf_t *Mod_PointInLeaf(float *p, model_t *model); +byte *Mod_ClusterPVS(int cluster, model_t *model); + +void Mod_Modellist_f(void); +void Mod_FreeAll(void); +void Mod_Free(model_t *mod); + +extern int registration_sequence; + +#endif // __MODEL__ diff --git a/src/client/refresh/soft/r_part.c b/src/client/refresh/soft/r_part.c new file mode 100644 index 00000000..cff4a87f --- /dev/null +++ b/src/client/refresh/soft/r_part.c @@ -0,0 +1,190 @@ +/* +Copyright (C) 1997-2001 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +#include "r_local.h" + +vec3_t r_pright, r_pup, r_ppn; + +#define PARTICLE_33 0 +#define PARTICLE_66 1 +#define PARTICLE_OPAQUE 2 + +typedef struct +{ + particle_t *particle; + int level; + int color; +} partparms_t; + +static partparms_t partparms; + +/* +** R_DrawParticle +** +** Yes, this is amazingly slow, but it's the C reference +** implementation and should be both robust and vaguely +** understandable. The only time this path should be +** executed is if we're debugging on x86 or if we're +** recompiling and deploying on a non-x86 platform. +** +** To minimize error and improve readability I went the +** function pointer route. This exacts some overhead, but +** it pays off in clean and easy to understand code. +*/ +void R_DrawParticle( void ) +{ + particle_t *pparticle = partparms.particle; + int level = partparms.level; + vec3_t local, transformed; + float zi; + byte *pdest; + zvalue_t *pz; + int color = pparticle->color; + int i, izi, pix, count, u, v; + + /* + ** transform the particle + */ + VectorSubtract (pparticle->origin, r_origin, local); + + transformed[0] = DotProduct(local, r_pright); + transformed[1] = DotProduct(local, r_pup); + transformed[2] = DotProduct(local, r_ppn); + + if (transformed[2] < PARTICLE_Z_CLIP) + return; + + /* + ** project the point + */ + // FIXME: preadjust xcenter and ycenter + zi = 1.0 / transformed[2]; + u = (int)(xcenter + zi * transformed[0] + 0.5); + v = (int)(ycenter - zi * transformed[1] + 0.5); + + if ((v > d_vrectbottom_particle) || + (u > d_vrectright_particle) || + (v < d_vrecty) || + (u < d_vrectx)) + { + return; + } + + /* + ** compute addresses of zbuffer, framebuffer, and + ** compute the Z-buffer reference value. + */ + pz = d_pzbuffer + (d_zwidth * v) + u; + pdest = d_viewbuffer + r_screenwidth * v + u; + izi = (int)(zi * 0x8000); + + /* + ** determine the screen area covered by the particle, + ** which also means clamping to a min and max + */ + pix = izi >> d_pix_shift; + if (pix < d_pix_min) + pix = d_pix_min; + else if (pix > d_pix_max) + pix = d_pix_max; + + /* + ** render the appropriate pixels + */ + count = pix; + + switch (level) { + case PARTICLE_33 : + for ( ; count ; count--, pz += d_zwidth, pdest += r_screenwidth) + { + //FIXME--do it in blocks of 8? + for (i=0 ; ialpha > 0.66 ) + partparms.level = PARTICLE_OPAQUE; + else if ( p->alpha > 0.33 ) + partparms.level = PARTICLE_66; + else + partparms.level = PARTICLE_33; + + partparms.particle = p; + partparms.color = p->color; + + R_DrawParticle(); + } +} diff --git a/src/client/refresh/soft/r_poly.c b/src/client/refresh/soft/r_poly.c new file mode 100644 index 00000000..46158066 --- /dev/null +++ b/src/client/refresh/soft/r_poly.c @@ -0,0 +1,1261 @@ +/* +Copyright (C) 1997-2001 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +#include +#include "r_local.h" + +#define AFFINE_SPANLET_SIZE 16 +#define AFFINE_SPANLET_SIZE_BITS 4 + +typedef struct +{ + pixel_t *pbase, *pdest; + zvalue_t *pz; + int s, t; + int sstep, tstep; + int izi, izistep, izistep_times_2; + int spancount; + unsigned u, v; +} spanletvars_t; + +spanletvars_t s_spanletvars; + +static int r_polyblendcolor; + +polydesc_t r_polydesc; + +msurface_t *r_alpha_surfaces; + +extern int *r_turb_turb; + +static int clip_current; +vec5_t r_clip_verts[2][MAXWORKINGVERTS+2]; +emitpoint_t outverts[MAXWORKINGVERTS+3]; + +static int s_minindex, s_maxindex; + +static void R_DrawPoly(int iswater); + +/* +** R_DrawSpanletOpaque +*/ +void R_DrawSpanletOpaque( void ) +{ + do + { + unsigned btemp; + unsigned ts, tt; + + ts = s_spanletvars.s >> 16; + tt = s_spanletvars.t >> 16; + + btemp = *(s_spanletvars.pbase + (ts) + (tt) * cachewidth); + if (btemp != 255) + { + if (*s_spanletvars.pz <= (s_spanletvars.izi >> 16)) + { + *s_spanletvars.pz = s_spanletvars.izi >> 16; + *s_spanletvars.pdest = btemp; + } + } + + s_spanletvars.izi += s_spanletvars.izistep; + s_spanletvars.pdest++; + s_spanletvars.pz++; + s_spanletvars.s += s_spanletvars.sstep; + s_spanletvars.t += s_spanletvars.tstep; + } while (--s_spanletvars.spancount > 0); +} + +/* +** R_DrawSpanletTurbulentStipple33 +*/ +void R_DrawSpanletTurbulentStipple33( void ) +{ + pixel_t *pdest = s_spanletvars.pdest; + zvalue_t *pz = s_spanletvars.pz; + int izi = s_spanletvars.izi; + + if ( s_spanletvars.v & 1 ) + { + s_spanletvars.pdest += s_spanletvars.spancount; + s_spanletvars.pz += s_spanletvars.spancount; + + if ( s_spanletvars.spancount == AFFINE_SPANLET_SIZE ) + s_spanletvars.izi += s_spanletvars.izistep << AFFINE_SPANLET_SIZE_BITS; + else + s_spanletvars.izi += s_spanletvars.izistep * s_spanletvars.izistep; + + if ( s_spanletvars.u & 1 ) + { + izi += s_spanletvars.izistep; + s_spanletvars.s += s_spanletvars.sstep; + s_spanletvars.t += s_spanletvars.tstep; + + pdest++; + pz++; + s_spanletvars.spancount--; + } + + s_spanletvars.sstep *= 2; + s_spanletvars.tstep *= 2; + + while ( s_spanletvars.spancount > 0 ) + { + unsigned btemp; + int sturb, tturb; + + sturb = ((s_spanletvars.s + r_turb_turb[(s_spanletvars.t>>16)&(CYCLE-1)])>>16)&63; + tturb = ((s_spanletvars.t + r_turb_turb[(s_spanletvars.s>>16)&(CYCLE-1)])>>16)&63; + + btemp = *( s_spanletvars.pbase + ( sturb ) + ( tturb << 6 ) ); + + if ( *pz <= ( izi >> 16 ) ) + *pdest = btemp; + + izi += s_spanletvars.izistep_times_2; + s_spanletvars.s += s_spanletvars.sstep; + s_spanletvars.t += s_spanletvars.tstep; + + pdest += 2; + pz += 2; + + s_spanletvars.spancount -= 2; + } + } +} + +/* +** R_DrawSpanletTurbulentStipple66 +*/ +void R_DrawSpanletTurbulentStipple66( void ) +{ + unsigned btemp; + int sturb, tturb; + pixel_t *pdest = s_spanletvars.pdest; + zvalue_t *pz = s_spanletvars.pz; + int izi = s_spanletvars.izi; + + if ( !( s_spanletvars.v & 1 ) ) + { + s_spanletvars.pdest += s_spanletvars.spancount; + s_spanletvars.pz += s_spanletvars.spancount; + + if ( s_spanletvars.spancount == AFFINE_SPANLET_SIZE ) + s_spanletvars.izi += s_spanletvars.izistep << AFFINE_SPANLET_SIZE_BITS; + else + s_spanletvars.izi += s_spanletvars.izistep * s_spanletvars.izistep; + + if ( s_spanletvars.u & 1 ) + { + izi += s_spanletvars.izistep; + s_spanletvars.s += s_spanletvars.sstep; + s_spanletvars.t += s_spanletvars.tstep; + + pdest++; + pz++; + s_spanletvars.spancount--; + } + + s_spanletvars.sstep *= 2; + s_spanletvars.tstep *= 2; + + while ( s_spanletvars.spancount > 0 ) + { + sturb = ((s_spanletvars.s + r_turb_turb[(s_spanletvars.t>>16)&(CYCLE-1)])>>16)&63; + tturb = ((s_spanletvars.t + r_turb_turb[(s_spanletvars.s>>16)&(CYCLE-1)])>>16)&63; + + btemp = *( s_spanletvars.pbase + ( sturb ) + ( tturb << 6 ) ); + + if ( *pz <= ( izi >> 16 ) ) + *pdest = btemp; + + izi += s_spanletvars.izistep_times_2; + s_spanletvars.s += s_spanletvars.sstep; + s_spanletvars.t += s_spanletvars.tstep; + + pdest += 2; + pz += 2; + + s_spanletvars.spancount -= 2; + } + } + else + { + s_spanletvars.pdest += s_spanletvars.spancount; + s_spanletvars.pz += s_spanletvars.spancount; + + if ( s_spanletvars.spancount == AFFINE_SPANLET_SIZE ) + s_spanletvars.izi += s_spanletvars.izistep << AFFINE_SPANLET_SIZE_BITS; + else + s_spanletvars.izi += s_spanletvars.izistep * s_spanletvars.izistep; + + while ( s_spanletvars.spancount > 0 ) + { + sturb = ((s_spanletvars.s + r_turb_turb[(s_spanletvars.t>>16)&(CYCLE-1)])>>16)&63; + tturb = ((s_spanletvars.t + r_turb_turb[(s_spanletvars.s>>16)&(CYCLE-1)])>>16)&63; + + btemp = *( s_spanletvars.pbase + ( sturb ) + ( tturb << 6 ) ); + + if ( *pz <= ( izi >> 16 ) ) + *pdest = btemp; + + izi += s_spanletvars.izistep; + s_spanletvars.s += s_spanletvars.sstep; + s_spanletvars.t += s_spanletvars.tstep; + + pdest++; + pz++; + + s_spanletvars.spancount--; + } + } +} + +/* +** R_DrawSpanletTurbulentBlended +*/ +void R_DrawSpanletTurbulentBlended66( void ) +{ + do + { + unsigned btemp; + int sturb, tturb; + + sturb = ((s_spanletvars.s + r_turb_turb[(s_spanletvars.t>>16)&(CYCLE-1)])>>16)&63; + tturb = ((s_spanletvars.t + r_turb_turb[(s_spanletvars.s>>16)&(CYCLE-1)])>>16)&63; + + btemp = *( s_spanletvars.pbase + ( sturb ) + ( tturb << 6 ) ); + + if ( *s_spanletvars.pz <= ( s_spanletvars.izi >> 16 ) ) + *s_spanletvars.pdest = vid_alphamap[btemp*256+*s_spanletvars.pdest]; + + s_spanletvars.izi += s_spanletvars.izistep; + s_spanletvars.pdest++; + s_spanletvars.pz++; + s_spanletvars.s += s_spanletvars.sstep; + s_spanletvars.t += s_spanletvars.tstep; + + } while ( --s_spanletvars.spancount > 0 ); +} + +void R_DrawSpanletTurbulentBlended33( void ) +{ + do + { + unsigned btemp; + int sturb, tturb; + + sturb = ((s_spanletvars.s + r_turb_turb[(s_spanletvars.t>>16)&(CYCLE-1)])>>16)&63; + tturb = ((s_spanletvars.t + r_turb_turb[(s_spanletvars.s>>16)&(CYCLE-1)])>>16)&63; + + btemp = *( s_spanletvars.pbase + ( sturb ) + ( tturb << 6 ) ); + + if ( *s_spanletvars.pz <= ( s_spanletvars.izi >> 16 ) ) + *s_spanletvars.pdest = vid_alphamap[btemp+*s_spanletvars.pdest*256]; + + s_spanletvars.izi += s_spanletvars.izistep; + s_spanletvars.pdest++; + s_spanletvars.pz++; + s_spanletvars.s += s_spanletvars.sstep; + s_spanletvars.t += s_spanletvars.tstep; + + } while ( --s_spanletvars.spancount > 0 ); +} + +/* +** R_DrawSpanlet33 +*/ +void R_DrawSpanlet33( void ) +{ + do + { + unsigned btemp; + unsigned ts, tt; + + ts = s_spanletvars.s >> 16; + tt = s_spanletvars.t >> 16; + + btemp = *(s_spanletvars.pbase + (ts) + (tt) * cachewidth); + + if ( btemp != 255 ) + { + if (*s_spanletvars.pz <= (s_spanletvars.izi >> 16)) + { + *s_spanletvars.pdest = vid_alphamap[btemp+*s_spanletvars.pdest*256]; + } + } + + s_spanletvars.izi += s_spanletvars.izistep; + s_spanletvars.pdest++; + s_spanletvars.pz++; + s_spanletvars.s += s_spanletvars.sstep; + s_spanletvars.t += s_spanletvars.tstep; + } while (--s_spanletvars.spancount > 0); +} + +void R_DrawSpanletConstant33( void ) +{ + do + { + if (*s_spanletvars.pz <= (s_spanletvars.izi >> 16)) + { + *s_spanletvars.pdest = vid_alphamap[r_polyblendcolor+*s_spanletvars.pdest*256]; + } + + s_spanletvars.izi += s_spanletvars.izistep; + s_spanletvars.pdest++; + s_spanletvars.pz++; + } while (--s_spanletvars.spancount > 0); +} + +/* +** R_DrawSpanlet66 +*/ +void R_DrawSpanlet66( void ) +{ + do + { + unsigned btemp; + unsigned ts, tt; + + ts = s_spanletvars.s >> 16; + tt = s_spanletvars.t >> 16; + + btemp = *(s_spanletvars.pbase + (ts) + (tt) * cachewidth); + + if ( btemp != 255 ) + { + if (*s_spanletvars.pz <= (s_spanletvars.izi >> 16)) + { + *s_spanletvars.pdest = vid_alphamap[btemp*256+*s_spanletvars.pdest]; + } + } + + s_spanletvars.izi += s_spanletvars.izistep; + s_spanletvars.pdest++; + s_spanletvars.pz++; + s_spanletvars.s += s_spanletvars.sstep; + s_spanletvars.t += s_spanletvars.tstep; + } while (--s_spanletvars.spancount > 0); +} + +/* +** R_DrawSpanlet33Stipple +*/ +void R_DrawSpanlet33Stipple( void ) +{ + pixel_t *pdest = s_spanletvars.pdest; + zvalue_t *pz = s_spanletvars.pz; + int izi = s_spanletvars.izi; + + if ( r_polydesc.stipple_parity ^ ( s_spanletvars.v & 1 ) ) + { + s_spanletvars.pdest += s_spanletvars.spancount; + s_spanletvars.pz += s_spanletvars.spancount; + + if ( s_spanletvars.spancount == AFFINE_SPANLET_SIZE ) + s_spanletvars.izi += s_spanletvars.izistep << AFFINE_SPANLET_SIZE_BITS; + else + s_spanletvars.izi += s_spanletvars.izistep * s_spanletvars.izistep; + + if ( r_polydesc.stipple_parity ^ ( s_spanletvars.u & 1 ) ) + { + izi += s_spanletvars.izistep; + s_spanletvars.s += s_spanletvars.sstep; + s_spanletvars.t += s_spanletvars.tstep; + + pdest++; + pz++; + s_spanletvars.spancount--; + } + + s_spanletvars.sstep *= 2; + s_spanletvars.tstep *= 2; + + while ( s_spanletvars.spancount > 0 ) + { + unsigned btemp; + unsigned s = s_spanletvars.s >> 16; + unsigned t = s_spanletvars.t >> 16; + + btemp = *( s_spanletvars.pbase + ( s ) + ( t * cachewidth ) ); + + if ( btemp != 255 ) + { + if ( *pz <= ( izi >> 16 ) ) + *pdest = btemp; + } + + izi += s_spanletvars.izistep_times_2; + s_spanletvars.s += s_spanletvars.sstep; + s_spanletvars.t += s_spanletvars.tstep; + + pdest += 2; + pz += 2; + + s_spanletvars.spancount -= 2; + } + } +} + +/* +** R_DrawSpanlet66Stipple +*/ +void R_DrawSpanlet66Stipple( void ) +{ + unsigned btemp; + pixel_t *pdest = s_spanletvars.pdest; + zvalue_t *pz = s_spanletvars.pz; + int izi = s_spanletvars.izi; + + s_spanletvars.pdest += s_spanletvars.spancount; + s_spanletvars.pz += s_spanletvars.spancount; + + if ( s_spanletvars.spancount == AFFINE_SPANLET_SIZE ) + s_spanletvars.izi += s_spanletvars.izistep << AFFINE_SPANLET_SIZE_BITS; + else + s_spanletvars.izi += s_spanletvars.izistep * s_spanletvars.izistep; + + if ( r_polydesc.stipple_parity ^ ( s_spanletvars.v & 1 ) ) + { + if ( r_polydesc.stipple_parity ^ ( s_spanletvars.u & 1 ) ) + { + izi += s_spanletvars.izistep; + s_spanletvars.s += s_spanletvars.sstep; + s_spanletvars.t += s_spanletvars.tstep; + + pdest++; + pz++; + s_spanletvars.spancount--; + } + + s_spanletvars.sstep *= 2; + s_spanletvars.tstep *= 2; + + while ( s_spanletvars.spancount > 0 ) + { + unsigned s = s_spanletvars.s >> 16; + unsigned t = s_spanletvars.t >> 16; + + btemp = *( s_spanletvars.pbase + ( s ) + ( t * cachewidth ) ); + + if ( btemp != 255 ) + { + if ( *pz <= ( izi >> 16 ) ) + *pdest = btemp; + } + + izi += s_spanletvars.izistep_times_2; + s_spanletvars.s += s_spanletvars.sstep; + s_spanletvars.t += s_spanletvars.tstep; + + pdest += 2; + pz += 2; + + s_spanletvars.spancount -= 2; + } + } + else + { + while ( s_spanletvars.spancount > 0 ) + { + unsigned s = s_spanletvars.s >> 16; + unsigned t = s_spanletvars.t >> 16; + + btemp = *( s_spanletvars.pbase + ( s ) + ( t * cachewidth ) ); + + if ( btemp != 255 ) + { + if ( *pz <= ( izi >> 16 ) ) + *pdest = btemp; + } + + izi += s_spanletvars.izistep; + s_spanletvars.s += s_spanletvars.sstep; + s_spanletvars.t += s_spanletvars.tstep; + + pdest++; + pz++; + + s_spanletvars.spancount--; + } + } +} + +/* +** R_ClipPolyFace +** +** Clips the winding at clip_verts[clip_current] and changes clip_current +** Throws out the back side +*/ +int R_ClipPolyFace (int nump, clipplane_t *pclipplane) +{ + int i, outcount; + float dists[MAXWORKINGVERTS+3]; + float frac, clipdist, *pclipnormal; + float *in, *instep, *outstep, *vert2; + + clipdist = pclipplane->dist; + pclipnormal = pclipplane->normal; + + // calc dists + if (clip_current) + { + in = r_clip_verts[1][0]; + outstep = r_clip_verts[0][0]; + clip_current = 0; + } + else + { + in = r_clip_verts[0][0]; + outstep = r_clip_verts[1][0]; + clip_current = 1; + } + + instep = in; + for (i=0 ; i= 0) + { + memcpy (outstep, instep, sizeof (vec5_t)); + outstep += sizeof (vec5_t) / sizeof (vec_t); + outcount++; + } + + if (dists[i] == 0 || dists[i+1] == 0) + continue; + + if ( (dists[i] > 0) == (dists[i+1] > 0) ) + continue; + + // split it into a new vertex + frac = dists[i] / (dists[i] - dists[i+1]); + + vert2 = instep + sizeof (vec5_t) / sizeof (vec_t); + + outstep[0] = instep[0] + frac*(vert2[0] - instep[0]); + outstep[1] = instep[1] + frac*(vert2[1] - instep[1]); + outstep[2] = instep[2] + frac*(vert2[2] - instep[2]); + outstep[3] = instep[3] + frac*(vert2[3] - instep[3]); + outstep[4] = instep[4] + frac*(vert2[4] - instep[4]); + + outstep += sizeof (vec5_t) / sizeof (vec_t); + outcount++; + } + + return outcount; +} + +/* +** R_PolygonDrawSpans +*/ +// PGM - iswater was qboolean. changed to allow passing more flags +void R_PolygonDrawSpans(espan_t *pspan, int iswater ) +{ + int count; + int snext, tnext; + float sdivz, tdivz, zi, z, du, dv, spancountminus1; + float sdivzspanletstepu, tdivzspanletstepu, zispanletstepu; + + s_spanletvars.pbase = cacheblock; + + //PGM + if ( iswater & SURF_WARP) + r_turb_turb = sintable + ((int)(r_newrefdef.time*SPEED)&(CYCLE-1)); + else if (iswater & SURF_FLOWING) + r_turb_turb = blanktable; + //PGM + + sdivzspanletstepu = d_sdivzstepu * AFFINE_SPANLET_SIZE; + tdivzspanletstepu = d_tdivzstepu * AFFINE_SPANLET_SIZE; + zispanletstepu = d_zistepu * AFFINE_SPANLET_SIZE; + + // we count on FP exceptions being turned off to avoid range problems + s_spanletvars.izistep = (int)(d_zistepu * 0x8000 * 0x10000); + s_spanletvars.izistep_times_2 = s_spanletvars.izistep * 2; + + s_spanletvars.pz = 0; + + do + { + s_spanletvars.pdest = d_viewbuffer + (r_screenwidth * pspan->v) + pspan->u; + s_spanletvars.pz = d_pzbuffer + (d_zwidth * pspan->v) + pspan->u; + s_spanletvars.u = pspan->u; + s_spanletvars.v = pspan->v; + count = pspan->count; + + if (count > 0) + { + // calculate the initial s/z, t/z, 1/z, s, and t and clamp + du = (float)pspan->u; + dv = (float)pspan->v; + + sdivz = d_sdivzorigin + dv*d_sdivzstepv + du*d_sdivzstepu; + tdivz = d_tdivzorigin + dv*d_tdivzstepv + du*d_tdivzstepu; + + zi = d_ziorigin + dv*d_zistepv + du*d_zistepu; + z = (float)0x10000 / zi; // prescale to 16.16 fixed-point + // we count on FP exceptions being turned off to avoid range problems + s_spanletvars.izi = (int)(zi * 0x8000 * 0x10000); + + s_spanletvars.s = (int)(sdivz * z) + sadjust; + s_spanletvars.t = (int)(tdivz * z) + tadjust; + + if ( !iswater ) + { + if (s_spanletvars.s > bbextents) + s_spanletvars.s = bbextents; + else if (s_spanletvars.s < 0) + s_spanletvars.s = 0; + + if (s_spanletvars.t > bbextentt) + s_spanletvars.t = bbextentt; + else if (s_spanletvars.t < 0) + s_spanletvars.t = 0; + } + + do + { + // calculate s and t at the far end of the span + if (count >= AFFINE_SPANLET_SIZE ) + s_spanletvars.spancount = AFFINE_SPANLET_SIZE; + else + s_spanletvars.spancount = count; + + count -= s_spanletvars.spancount; + + if (count) + { + // calculate s/z, t/z, zi->fixed s and t at far end of span, + // calculate s and t steps across span by shifting + sdivz += sdivzspanletstepu; + tdivz += tdivzspanletstepu; + zi += zispanletstepu; + z = (float)0x10000 / zi; // prescale to 16.16 fixed-point + + snext = (int)(sdivz * z) + sadjust; + tnext = (int)(tdivz * z) + tadjust; + + if ( !iswater ) + { + if (snext > bbextents) + snext = bbextents; + else if (snext < AFFINE_SPANLET_SIZE) + snext = AFFINE_SPANLET_SIZE; // prevent round-off error on <0 steps from + // from causing overstepping & running off the + // edge of the texture + + if (tnext > bbextentt) + tnext = bbextentt; + else if (tnext < AFFINE_SPANLET_SIZE) + tnext = AFFINE_SPANLET_SIZE; // guard against round-off error on <0 steps + } + + s_spanletvars.sstep = (snext - s_spanletvars.s) >> AFFINE_SPANLET_SIZE_BITS; + s_spanletvars.tstep = (tnext - s_spanletvars.t) >> AFFINE_SPANLET_SIZE_BITS; + } + else + { + // calculate s/z, t/z, zi->fixed s and t at last pixel in span (so + // can't step off polygon), clamp, calculate s and t steps across + // span by division, biasing steps low so we don't run off the + // texture + spancountminus1 = (float)(s_spanletvars.spancount - 1); + sdivz += d_sdivzstepu * spancountminus1; + tdivz += d_tdivzstepu * spancountminus1; + zi += d_zistepu * spancountminus1; + z = (float)0x10000 / zi; // prescale to 16.16 fixed-point + snext = (int)(sdivz * z) + sadjust; + tnext = (int)(tdivz * z) + tadjust; + + if ( !iswater ) + { + if (snext > bbextents) + snext = bbextents; + else if (snext < AFFINE_SPANLET_SIZE) + snext = AFFINE_SPANLET_SIZE; // prevent round-off error on <0 steps from + // from causing overstepping & running off the + // edge of the texture + + if (tnext > bbextentt) + tnext = bbextentt; + else if (tnext < AFFINE_SPANLET_SIZE) + tnext = AFFINE_SPANLET_SIZE; // guard against round-off error on <0 steps + } + + if (s_spanletvars.spancount > 1) + { + s_spanletvars.sstep = (snext - s_spanletvars.s) / (s_spanletvars.spancount - 1); + s_spanletvars.tstep = (tnext - s_spanletvars.t) / (s_spanletvars.spancount - 1); + } + } + + if ( iswater ) + { + s_spanletvars.s = s_spanletvars.s & ((CYCLE<<16)-1); + s_spanletvars.t = s_spanletvars.t & ((CYCLE<<16)-1); + } + + r_polydesc.drawspanlet(); + + s_spanletvars.s = snext; + s_spanletvars.t = tnext; + + } while (count > 0); + } + + pspan++; + + } while (pspan->count != DS_SPAN_LIST_END); +} + +/* +** +** R_PolygonScanLeftEdge +** +** Goes through the polygon and scans the left edge, filling in +** screen coordinate data for the spans +*/ +void R_PolygonScanLeftEdge (espan_t *s_polygon_spans) +{ + int i, v, itop, ibottom, lmaxindex; + emitpoint_t *pvert, *pnext; + espan_t *pspan; + float du, dv, vtop, slope; + int u, u_step; + + pspan = s_polygon_spans; + i = s_minindex; + if (i == 0) + i = r_polydesc.nump; + + lmaxindex = s_maxindex; + if (lmaxindex == 0) + lmaxindex = r_polydesc.nump; + + vtop = ceil (r_polydesc.pverts[i].v); + + do + { + float vbottom; + + pvert = &r_polydesc.pverts[i]; + pnext = pvert - 1; + + vbottom = ceil (pnext->v); + + if (vtop < vbottom) + { + du = pnext->u - pvert->u; + dv = pnext->v - pvert->v; + + slope = du / dv; + u_step = (int)(slope * 0x10000); + // adjust u to ceil the integer portion + u = (int)((pvert->u + (slope * (vtop - pvert->v))) * 0x10000) + + (0x10000 - 1); + itop = (int)vtop; + ibottom = (int)vbottom; + + for (v=itop ; vu = u >> 16; + pspan->v = v; + u += u_step; + pspan++; + } + } + + vtop = vbottom; + + i--; + if (i == 0) + i = r_polydesc.nump; + + } while (i != lmaxindex); +} + +/* +** R_PolygonScanRightEdge +** +** Goes through the polygon and scans the right edge, filling in +** count values. +*/ +void R_PolygonScanRightEdge(espan_t *s_polygon_spans) +{ + int i, v, itop, ibottom; + emitpoint_t *pvert, *pnext; + espan_t *pspan; + float du, dv, vtop, slope, uvert, unext, vvert; + int u, u_step; + + pspan = s_polygon_spans; + i = s_minindex; + + vvert = r_polydesc.pverts[i].v; + if (vvert < r_refdef.fvrecty_adj) + vvert = r_refdef.fvrecty_adj; + if (vvert > r_refdef.fvrectbottom_adj) + vvert = r_refdef.fvrectbottom_adj; + + vtop = ceil (vvert); + + do + { + float vbottom, vnext; + + pvert = &r_polydesc.pverts[i]; + pnext = pvert + 1; + + vnext = pnext->v; + if (vnext < r_refdef.fvrecty_adj) + vnext = r_refdef.fvrecty_adj; + if (vnext > r_refdef.fvrectbottom_adj) + vnext = r_refdef.fvrectbottom_adj; + + vbottom = ceil (vnext); + + if (vtop < vbottom) + { + uvert = pvert->u; + if (uvert < r_refdef.fvrectx_adj) + uvert = r_refdef.fvrectx_adj; + if (uvert > r_refdef.fvrectright_adj) + uvert = r_refdef.fvrectright_adj; + + unext = pnext->u; + if (unext < r_refdef.fvrectx_adj) + unext = r_refdef.fvrectx_adj; + if (unext > r_refdef.fvrectright_adj) + unext = r_refdef.fvrectright_adj; + + du = unext - uvert; + dv = vnext - vvert; + slope = du / dv; + u_step = (int)(slope * 0x10000); + // adjust u to ceil the integer portion + u = (int)((uvert + (slope * (vtop - vvert))) * 0x10000) + + (0x10000 - 1); + itop = (int)vtop; + ibottom = (int)vbottom; + + for (v=itop ; vcount = (u >> 16) - pspan->u; + u += u_step; + pspan++; + } + } + + vtop = vbottom; + vvert = vnext; + + i++; + if (i == r_polydesc.nump) + i = 0; + + } while (i != s_maxindex); + + pspan->count = DS_SPAN_LIST_END; // mark the end of the span list +} + +/* +** R_ClipAndDrawPoly +*/ +// PGM - isturbulent was qboolean. changed to int to allow passing more flags +void R_ClipAndDrawPoly ( float alpha, int isturbulent, qboolean textured ) +{ + vec_t *pv; + int i, nump; + vec3_t transformed, local; + + if ( !textured ) + { + r_polydesc.drawspanlet = R_DrawSpanletConstant33; + } + else + { + + /* + ** choose the correct spanlet routine based on alpha + */ + if ( alpha == 1 ) + { + // isturbulent is ignored because we know that turbulent surfaces + // can't be opaque + r_polydesc.drawspanlet = R_DrawSpanletOpaque; + } + else + { + if ( sw_stipplealpha->value ) + { + if ( isturbulent ) + { + if ( alpha > 0.33 ) + r_polydesc.drawspanlet = R_DrawSpanletTurbulentStipple66; + else + r_polydesc.drawspanlet = R_DrawSpanletTurbulentStipple33; + } + else + { + if ( alpha > 0.33 ) + r_polydesc.drawspanlet = R_DrawSpanlet66Stipple; + else + r_polydesc.drawspanlet = R_DrawSpanlet33Stipple; + } + } + else + { + if ( isturbulent ) + { + if ( alpha > 0.33 ) + r_polydesc.drawspanlet = R_DrawSpanletTurbulentBlended66; + else + r_polydesc.drawspanlet = R_DrawSpanletTurbulentBlended33; + } + else + { + if ( alpha > 0.33 ) + r_polydesc.drawspanlet = R_DrawSpanlet66; + else + r_polydesc.drawspanlet = R_DrawSpanlet33; + } + } + } + } + + // clip to the frustum in worldspace + nump = r_polydesc.nump; + clip_current = 0; + + for (i=0 ; i<4 ; i++) + { + nump = R_ClipPolyFace (nump, &view_clipplanes[i]); + if (nump < 3) + return; + if (nump > MAXWORKINGVERTS) + ri.Sys_Error(ERR_DROP, "R_ClipAndDrawPoly: too many points: %d", nump ); + } + + // transform vertices into viewspace and project + pv = &r_clip_verts[clip_current][0][0]; + + for (i=0 ; izi = 1.0 / transformed[2]; + + pout->s = pv[3]; + pout->t = pv[4]; + + scale = xscale * pout->zi; + pout->u = (xcenter + scale * transformed[0]); + + scale = yscale * pout->zi; + pout->v = (ycenter - scale * transformed[1]); + + pv += sizeof (vec5_t) / sizeof (vec_t); + } + + // draw it + r_polydesc.nump = nump; + r_polydesc.pverts = outverts; + + R_DrawPoly(isturbulent); +} + +/* +** R_BuildPolygonFromSurface +*/ +void R_BuildPolygonFromSurface(msurface_t *fa) +{ + int i, lnumverts; + medge_t *pedges, *r_pedge; + float *vec; + vec5_t *pverts; + float tmins[2] = { 0, 0 }; + + r_polydesc.nump = 0; + + // reconstruct the polygon + pedges = currentmodel->edges; + lnumverts = fa->numedges; + + pverts = r_clip_verts[0]; + + for (i=0 ; isurfedges[fa->firstedge + i]; + + if (lindex > 0) + { + r_pedge = &pedges[lindex]; + vec = currentmodel->vertexes[r_pedge->v[0]].position; + } + else + { + r_pedge = &pedges[-lindex]; + vec = currentmodel->vertexes[r_pedge->v[1]].position; + } + + VectorCopy (vec, pverts[i] ); + } + + VectorCopy( fa->texinfo->vecs[0], r_polydesc.vright ); + VectorCopy( fa->texinfo->vecs[1], r_polydesc.vup ); + VectorCopy( fa->plane->normal, r_polydesc.vpn ); + VectorCopy( r_origin, r_polydesc.viewer_position ); + + if ( fa->flags & SURF_PLANEBACK ) + { + VectorSubtract( vec3_origin, r_polydesc.vpn, r_polydesc.vpn ); + } + + // PGM 09/16/98 + if ( fa->texinfo->flags & (SURF_WARP|SURF_FLOWING) ) + { + r_polydesc.pixels = fa->texinfo->image->pixels[0]; + r_polydesc.pixel_width = fa->texinfo->image->width; + r_polydesc.pixel_height = fa->texinfo->image->height; + } + // PGM 09/16/98 + else + { + surfcache_t *scache; + + scache = D_CacheSurface( fa, 0 ); + + r_polydesc.pixels = scache->data; + r_polydesc.pixel_width = scache->width; + r_polydesc.pixel_height = scache->height; + + tmins[0] = fa->texturemins[0]; + tmins[1] = fa->texturemins[1]; + } + + r_polydesc.dist = DotProduct( r_polydesc.vpn, pverts[0] ); + + r_polydesc.s_offset = fa->texinfo->vecs[0][3] - tmins[0]; + r_polydesc.t_offset = fa->texinfo->vecs[1][3] - tmins[1]; + + // scrolling texture addition + if (fa->texinfo->flags & SURF_FLOWING) + { + r_polydesc.s_offset += -128 * ( (r_newrefdef.time*0.25) - (int)(r_newrefdef.time*0.25) ); + } + + r_polydesc.nump = lnumverts; +} + +/* +** R_PolygonCalculateGradients +*/ +void R_PolygonCalculateGradients (void) +{ + vec3_t p_normal, p_saxis, p_taxis; + float distinv; + + TransformVector (r_polydesc.vpn, p_normal); + TransformVector (r_polydesc.vright, p_saxis); + TransformVector (r_polydesc.vup, p_taxis); + + distinv = 1.0 / (-(DotProduct (r_polydesc.viewer_position, r_polydesc.vpn)) + r_polydesc.dist ); + + d_sdivzstepu = p_saxis[0] * xscaleinv; + d_sdivzstepv = -p_saxis[1] * yscaleinv; + d_sdivzorigin = p_saxis[2] - xcenter * d_sdivzstepu - ycenter * d_sdivzstepv; + + d_tdivzstepu = p_taxis[0] * xscaleinv; + d_tdivzstepv = -p_taxis[1] * yscaleinv; + d_tdivzorigin = p_taxis[2] - xcenter * d_tdivzstepu - ycenter * d_tdivzstepv; + + d_zistepu = p_normal[0] * xscaleinv * distinv; + d_zistepv = -p_normal[1] * yscaleinv * distinv; + d_ziorigin = p_normal[2] * distinv - xcenter * d_zistepu - ycenter * d_zistepv; + + sadjust = (int) ( ( DotProduct( r_polydesc.viewer_position, r_polydesc.vright) + r_polydesc.s_offset ) * 0x10000 ); + tadjust = (int) ( ( DotProduct( r_polydesc.viewer_position, r_polydesc.vup ) + r_polydesc.t_offset ) * 0x10000 ); + + // -1 (-epsilon) so we never wander off the edge of the texture + bbextents = (r_polydesc.pixel_width << 16) - 1; + bbextentt = (r_polydesc.pixel_height << 16) - 1; +} + +/* +** R_DrawPoly +** +** Polygon drawing function. Uses the polygon described in r_polydesc +** to calculate edges and gradients, then renders the resultant spans. +** +** This should NOT be called externally since it doesn't do clipping! +*/ +// PGM - iswater was qboolean. changed to support passing more flags +static void R_DrawPoly(int iswater) +{ + int i, nump; + float ymin, ymax; + emitpoint_t *pverts; + espan_t *spans; + spans = vid_polygon_spans; + + // find the top and bottom vertices, and make sure there's at least one scan to + // draw + ymin = 999999.9; + ymax = -999999.9; + pverts = r_polydesc.pverts; + + for (i=0 ; iv < ymin) + { + ymin = pverts->v; + s_minindex = i; + } + + if (pverts->v > ymax) + { + ymax = pverts->v; + s_maxindex = i; + } + + pverts++; + } + + ymin = ceil (ymin); + ymax = ceil (ymax); + + if (ymin >= ymax) + return; // doesn't cross any scans at all + + cachewidth = r_polydesc.pixel_width; + cacheblock = r_polydesc.pixels; + + // copy the first vertex to the last vertex, so we don't have to deal with + // wrapping + nump = r_polydesc.nump; + pverts = r_polydesc.pverts; + pverts[nump] = pverts[0]; + + R_PolygonCalculateGradients(); + R_PolygonScanLeftEdge(vid_polygon_spans); + R_PolygonScanRightEdge(vid_polygon_spans); + + R_PolygonDrawSpans(spans, iswater); +} + +/* +** R_DrawAlphaSurfaces +*/ +void R_DrawAlphaSurfaces( void ) +{ + msurface_t *s = r_alpha_surfaces; + + currentmodel = r_worldmodel; + + modelorg[0] = -r_origin[0]; + modelorg[1] = -r_origin[1]; + modelorg[2] = -r_origin[2]; + + while ( s ) + { + R_BuildPolygonFromSurface( s ); + + //======= + //PGM + // if (s->texinfo->flags & SURF_TRANS66) + // R_ClipAndDrawPoly( 0.60f, ( s->texinfo->flags & SURF_WARP) != 0, true ); + // else + // R_ClipAndDrawPoly( 0.30f, ( s->texinfo->flags & SURF_WARP) != 0, true ); + + // PGM - pass down all the texinfo flags, not just SURF_WARP. + if (s->texinfo->flags & SURF_TRANS66) + R_ClipAndDrawPoly( 0.60f, (s->texinfo->flags & (SURF_WARP|SURF_FLOWING)), true ); + else + R_ClipAndDrawPoly( 0.30f, (s->texinfo->flags & (SURF_WARP|SURF_FLOWING)), true ); + //PGM + //======= + + s = s->nextalphasurface; + } + + r_alpha_surfaces = NULL; +} + +/* +** R_IMFlatShadedQuad +*/ +void R_IMFlatShadedQuad( vec3_t a, vec3_t b, vec3_t c, vec3_t d, int color, float alpha ) +{ + vec3_t s0, s1; + + r_polydesc.nump = 4; + VectorCopy( r_origin, r_polydesc.viewer_position ); + + VectorCopy( a, r_clip_verts[0][0] ); + VectorCopy( b, r_clip_verts[0][1] ); + VectorCopy( c, r_clip_verts[0][2] ); + VectorCopy( d, r_clip_verts[0][3] ); + + r_clip_verts[0][0][3] = 0; + r_clip_verts[0][1][3] = 0; + r_clip_verts[0][2][3] = 0; + r_clip_verts[0][3][3] = 0; + + r_clip_verts[0][0][4] = 0; + r_clip_verts[0][1][4] = 0; + r_clip_verts[0][2][4] = 0; + r_clip_verts[0][3][4] = 0; + + VectorSubtract( d, c, s0 ); + VectorSubtract( c, b, s1 ); + CrossProduct( s0, s1, r_polydesc.vpn ); + VectorNormalize( r_polydesc.vpn ); + + r_polydesc.dist = DotProduct( r_polydesc.vpn, r_clip_verts[0][0] ); + + r_polyblendcolor = color; + + R_ClipAndDrawPoly( alpha, false, false ); +} diff --git a/src/client/refresh/soft/r_polyse.c b/src/client/refresh/soft/r_polyse.c new file mode 100644 index 00000000..95a94d75 --- /dev/null +++ b/src/client/refresh/soft/r_polyse.c @@ -0,0 +1,1143 @@ +/* +Copyright (C) 1997-2001 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +// d_polyset.c: routines for drawing sets of polygons sharing the same +// texture (used for Alias models) + +#include "r_local.h" + +#define MASK_1K 0x3FF + +typedef struct { + int isflattop; + int numleftedges; + int *pleftedgevert0; + int *pleftedgevert1; + int *pleftedgevert2; + int numrightedges; + int *prightedgevert0; + int *prightedgevert1; + int *prightedgevert2; +} edgetable; + +aliastriangleparms_t aliastriangleparms; + +int r_p0[6], r_p1[6], r_p2[6]; + +byte *d_pcolormap; +int d_aflatcolor; +int d_xdenom; + +edgetable *pedgetable; + +edgetable edgetables[12] = { + {0, 1, r_p0, r_p2, NULL, 2, r_p0, r_p1, r_p2}, + {0, 2, r_p1, r_p0, r_p2, 1, r_p1, r_p2, NULL}, + {1, 1, r_p0, r_p2, NULL, 1, r_p1, r_p2, NULL}, + {0, 1, r_p1, r_p0, NULL, 2, r_p1, r_p2, r_p0}, + {0, 2, r_p0, r_p2, r_p1, 1, r_p0, r_p1, NULL}, + {0, 1, r_p2, r_p1, NULL, 1, r_p2, r_p0, NULL}, + {0, 1, r_p2, r_p1, NULL, 2, r_p2, r_p0, r_p1}, + {0, 2, r_p2, r_p1, r_p0, 1, r_p2, r_p0, NULL}, + {0, 1, r_p1, r_p0, NULL, 1, r_p1, r_p2, NULL}, + {1, 1, r_p2, r_p1, NULL, 1, r_p0, r_p1, NULL}, + {1, 1, r_p1, r_p0, NULL, 1, r_p2, r_p0, NULL}, + {0, 1, r_p0, r_p2, NULL, 1, r_p0, r_p1, NULL}, +}; + +// FIXME: some of these can become statics +int a_sstepxfrac, a_tstepxfrac, r_lstepx, a_ststepxwhole; +int r_sstepx, r_tstepx, r_lstepy, r_sstepy, r_tstepy; +int r_zistepx, r_zistepy; +int d_aspancount, d_countextrastep; + +static spanpackage_t *a_spans; +static spanpackage_t *d_pedgespanpackage; + +spanpackage_t *triangle_spans; + +static int ystart; +pixel_t *d_pdest, *d_ptex; +zvalue_t *d_pz; +int d_sfrac, d_tfrac, d_light, d_zi; +int d_ptexextrastep, d_sfracextrastep; +int d_tfracextrastep, d_lightextrastep, d_pdestextrastep; +int d_lightbasestep, d_pdestbasestep, d_ptexbasestep; +int d_sfracbasestep, d_tfracbasestep; +int d_ziextrastep, d_zibasestep; +int d_pzextrastep, d_pzbasestep; + +typedef struct { + int quotient; + int remainder; +} adivtab_t; + +static adivtab_t adivtab[32*32] = { +#include "adivtab.h" +}; + +byte *skintable[MAX_LBM_HEIGHT]; +int skinwidth; +byte *skinstart; + +void (*d_pdrawspans)(spanpackage_t *pspanpackage); + +void R_PolysetDrawSpans8_33 (spanpackage_t *pspanpackage); +void R_PolysetDrawSpans8_66 (spanpackage_t *pspanpackage); +void R_PolysetDrawSpans8_Opaque (spanpackage_t *pspanpackage); + +void R_PolysetDrawThreshSpans8 (spanpackage_t *pspanpackage); +void R_PolysetCalcGradients (int skinwidth); +void R_DrawNonSubdiv (void); +void R_PolysetSetEdgeTable (void); +void R_RasterizeAliasPolySmooth (void); +void R_PolysetScanLeftEdge(int height); +void R_PolysetScanLeftEdge_C(int height); + +// ====================== +// PGM +// 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 +byte iractive = 0; +byte irtable[256] = { 79, 78, 77, 76, 75, 74, 73, 72, // black/white + 71, 70, 69, 68, 67, 66, 65, 64, + 64, 65, 66, 67, 68, 69, 70, 71, // dark taupe + 72, 73, 74, 75, 76, 77, 78, 79, + + 64, 65, 66, 67, 68, 69, 70, 71, // slate grey + 72, 73, 74, 75, 76, 77, 78, 79, + 208, 208, 208, 208, 208, 208, 208, 208, // unused?' + 64, 66, 68, 70, 72, 74, 76, 78, // dark yellow + + 64, 65, 66, 67, 68, 69, 70, 71, // dark red + 72, 73, 74, 75, 76, 77, 78, 79, + 64, 65, 66, 67, 68, 69, 70, 71, // grey/tan + 72, 73, 74, 75, 76, 77, 78, 79, + + 64, 66, 68, 70, 72, 74, 76, 78, // chocolate + 68, 67, 66, 65, 64, 65, 66, 67, // mauve / teal + 68, 69, 70, 71, 72, 73, 74, 75, + 76, 76, 77, 77, 78, 78, 79, 79, + + 64, 65, 66, 67, 68, 69, 70, 71, // more mauve + 72, 73, 74, 75, 76, 77, 78, 79, + 64, 65, 66, 67, 68, 69, 70, 71, // olive + 72, 73, 74, 75, 76, 77, 78, 79, + + 64, 65, 66, 67, 68, 69, 70, 71, // maroon + 72, 73, 74, 75, 76, 77, 78, 79, + 64, 65, 66, 67, 68, 69, 70, 71, // sky blue + 72, 73, 74, 75, 76, 77, 78, 79, + + 64, 65, 66, 67, 68, 69, 70, 71, // olive again + 72, 73, 74, 75, 76, 77, 78, 79, + 64, 65, 66, 67, 68, 69, 70, 71, // nuclear green + 64, 65, 66, 67, 68, 69, 70, 71, // bright yellow + + 64, 65, 66, 67, 68, 69, 70, 71, // fire colors + 72, 73, 74, 75, 76, 77, 78, 79, + 208, 208, 64, 64, 70, 71, 72, 64, // mishmash1 + 66, 68, 70, 64, 65, 66, 67, 68}; // mishmash2 +// PGM +// ====================== + +/* +================ +R_PolysetUpdateTables +================ +*/ +void R_PolysetUpdateTables (void) +{ + byte *s; + + if (r_affinetridesc.skinwidth != skinwidth || + r_affinetridesc.pskin != skinstart) + { + int i; + + skinwidth = r_affinetridesc.skinwidth; + skinstart = r_affinetridesc.pskin; + s = skinstart; + for (i=0 ; iv[1] - aliastriangleparms.b->v[1] ) * ( aliastriangleparms.a->v[0] - aliastriangleparms.c->v[0] ) - + ( aliastriangleparms.a->v[0] - aliastriangleparms.b->v[0] ) * ( aliastriangleparms.a->v[1] - aliastriangleparms.c->v[1] ); + */ + + dv0_ab = aliastriangleparms.a->u - aliastriangleparms.b->u; + dv1_ab = aliastriangleparms.a->v - aliastriangleparms.b->v; + + if ( !( dv0_ab | dv1_ab ) ) + return; + + dv0_ac = aliastriangleparms.a->u - aliastriangleparms.c->u; + dv1_ac = aliastriangleparms.a->v - aliastriangleparms.c->v; + + if ( !( dv0_ac | dv1_ac ) ) + return; + + d_xdenom = ( dv0_ac * dv1_ab ) - ( dv0_ab * dv1_ac ); + + if ( d_xdenom < 0 ) + { + a_spans = triangle_spans; + + r_p0[0] = aliastriangleparms.a->u; // u + r_p0[1] = aliastriangleparms.a->v; // v + r_p0[2] = aliastriangleparms.a->s; // s + r_p0[3] = aliastriangleparms.a->t; // t + r_p0[4] = aliastriangleparms.a->l; // light + r_p0[5] = aliastriangleparms.a->zi; // iz + + r_p1[0] = aliastriangleparms.b->u; + r_p1[1] = aliastriangleparms.b->v; + r_p1[2] = aliastriangleparms.b->s; + r_p1[3] = aliastriangleparms.b->t; + r_p1[4] = aliastriangleparms.b->l; + r_p1[5] = aliastriangleparms.b->zi; + + r_p2[0] = aliastriangleparms.c->u; + r_p2[1] = aliastriangleparms.c->v; + r_p2[2] = aliastriangleparms.c->s; + r_p2[3] = aliastriangleparms.c->t; + r_p2[4] = aliastriangleparms.c->l; + r_p2[5] = aliastriangleparms.c->zi; + + R_PolysetSetEdgeTable (); + R_RasterizeAliasPolySmooth (); + } +} + + +/* +=================== +R_PolysetScanLeftEdge_C +==================== +*/ +void R_PolysetScanLeftEdge_C(int height) +{ + do + { + d_pedgespanpackage->pdest = d_pdest; + d_pedgespanpackage->pz = d_pz; + d_pedgespanpackage->count = d_aspancount; + d_pedgespanpackage->ptex = d_ptex; + + d_pedgespanpackage->sfrac = d_sfrac; + d_pedgespanpackage->tfrac = d_tfrac; + + // FIXME: need to clamp l, s, t, at both ends? + d_pedgespanpackage->light = d_light; + d_pedgespanpackage->zi = d_zi; + + d_pedgespanpackage++; + + errorterm += erroradjustup; + if (errorterm >= 0) + { + d_pdest += d_pdestextrastep; + d_pz += d_pzextrastep; + d_aspancount += d_countextrastep; + d_ptex += d_ptexextrastep; + d_sfrac += d_sfracextrastep; + d_ptex += d_sfrac >> 16; + + d_sfrac &= 0xFFFF; + d_tfrac += d_tfracextrastep; + if (d_tfrac & 0x10000) + { + d_ptex += r_affinetridesc.skinwidth; + d_tfrac &= 0xFFFF; + } + d_light += d_lightextrastep; + d_zi += d_ziextrastep; + errorterm -= erroradjustdown; + } + else + { + d_pdest += d_pdestbasestep; + d_pz += d_pzbasestep; + d_aspancount += ubasestep; + d_ptex += d_ptexbasestep; + d_sfrac += d_sfracbasestep; + d_ptex += d_sfrac >> 16; + d_sfrac &= 0xFFFF; + d_tfrac += d_tfracbasestep; + if (d_tfrac & 0x10000) + { + d_ptex += r_affinetridesc.skinwidth; + d_tfrac &= 0xFFFF; + } + d_light += d_lightbasestep; + d_zi += d_zibasestep; + } + } while (--height); +} + +/* +=================== +FloorDivMod + +Returns mathematically correct (floor-based) quotient and remainder for +numer and denom, both of which should contain no fractional part. The +quotient must fit in 32 bits. +FIXME: GET RID OF THIS! (FloorDivMod) +==================== +*/ +void FloorDivMod (float numer, float denom, int *quotient, + int *rem) +{ + int q, r; + float x; + + if (numer >= 0.0) + { + + x = floor(numer / denom); + q = (int)x; + r = (int)floor(numer - (x * denom)); + } + else + { + // + // perform operations with positive values, and fix mod to make floor-based + // + x = floor(-numer / denom); + q = -(int)x; + r = (int)floor(-numer - (x * denom)); + if (r != 0) + { + q--; + r = (int)denom - r; + } + } + + *quotient = q; + *rem = r; +} + + +/* +=================== +R_PolysetSetUpForLineScan +==================== +*/ +void R_PolysetSetUpForLineScan(fixed8_t startvertu, fixed8_t startvertv, + fixed8_t endvertu, fixed8_t endvertv) +{ + int tm, tn; + adivtab_t *ptemp; + + // TODO: implement x86 version + + errorterm = -1; + + tm = endvertu - startvertu; + tn = endvertv - startvertv; + + if (((tm <= 16) && (tm >= -15)) && + ((tn <= 16) && (tn >= -15))) + { + ptemp = &adivtab[((tm+15) << 5) + (tn+15)]; + ubasestep = ptemp->quotient; + erroradjustup = ptemp->remainder; + erroradjustdown = tn; + } + else + { + float dm, dn; + + dm = tm; + dn = tn; + + FloorDivMod (dm, dn, &ubasestep, &erroradjustup); + + erroradjustdown = dn; + } +} + + + +/* +================ +R_PolysetCalcGradients +================ +*/ +void R_PolysetCalcGradients (int skinwidth) +{ + float xstepdenominv, ystepdenominv, t0, t1; + float p01_minus_p21, p11_minus_p21, p00_minus_p20, p10_minus_p20; + + p00_minus_p20 = r_p0[0] - r_p2[0]; + p01_minus_p21 = r_p0[1] - r_p2[1]; + p10_minus_p20 = r_p1[0] - r_p2[0]; + p11_minus_p21 = r_p1[1] - r_p2[1]; + + xstepdenominv = 1.0 / (float)d_xdenom; + + ystepdenominv = -xstepdenominv; + + // ceil () for light so positive steps are exaggerated, negative steps + // diminished, pushing us away from underflow toward overflow. Underflow is + // very visible, overflow is very unlikely, because of ambient lighting + t0 = r_p0[4] - r_p2[4]; + t1 = r_p1[4] - r_p2[4]; + r_lstepx = (int) + ceil((t1 * p01_minus_p21 - t0 * p11_minus_p21) * xstepdenominv); + r_lstepy = (int) + ceil((t1 * p00_minus_p20 - t0 * p10_minus_p20) * ystepdenominv); + + t0 = r_p0[2] - r_p2[2]; + t1 = r_p1[2] - r_p2[2]; + r_sstepx = (int)((t1 * p01_minus_p21 - t0 * p11_minus_p21) * + xstepdenominv); + r_sstepy = (int)((t1 * p00_minus_p20 - t0* p10_minus_p20) * + ystepdenominv); + + t0 = r_p0[3] - r_p2[3]; + t1 = r_p1[3] - r_p2[3]; + r_tstepx = (int)((t1 * p01_minus_p21 - t0 * p11_minus_p21) * + xstepdenominv); + r_tstepy = (int)((t1 * p00_minus_p20 - t0 * p10_minus_p20) * + ystepdenominv); + + t0 = r_p0[5] - r_p2[5]; + t1 = r_p1[5] - r_p2[5]; + r_zistepx = (int)((t1 * p01_minus_p21 - t0 * p11_minus_p21) * + xstepdenominv); + r_zistepy = (int)((t1 * p00_minus_p20 - t0 * p10_minus_p20) * + ystepdenominv); + + { + a_sstepxfrac = r_sstepx & 0xFFFF; + a_tstepxfrac = r_tstepx & 0xFFFF; + } + + a_ststepxwhole = skinwidth * (r_tstepx >> 16) + (r_sstepx >> 16); +} + +/* +================ +R_PolysetDrawThreshSpans8 + +Random fizzle fade rasterizer +================ +*/ +void R_PolysetDrawThreshSpans8 (spanpackage_t *pspanpackage) +{ + byte *lpdest; + byte *lptex; + int lsfrac, ltfrac; + int llight; + int lzi; + zvalue_t *lpz; + + do + { + int lcount; + + lcount = d_aspancount - pspanpackage->count; + + errorterm += erroradjustup; + if (errorterm >= 0) + { + d_aspancount += d_countextrastep; + errorterm -= erroradjustdown; + } + else + { + d_aspancount += ubasestep; + } + + if (lcount) + { + lpdest = pspanpackage->pdest; + lptex = pspanpackage->ptex; + lpz = pspanpackage->pz; + lsfrac = pspanpackage->sfrac; + ltfrac = pspanpackage->tfrac; + llight = pspanpackage->light; + lzi = pspanpackage->zi; + + do + { + lpdest++; + lzi += r_zistepx; + lpz++; + llight += r_lstepx; + lptex += a_ststepxwhole; + lsfrac += a_sstepxfrac; + lptex += lsfrac >> 16; + lsfrac &= 0xFFFF; + ltfrac += a_tstepxfrac; + if (ltfrac & 0x10000) + { + lptex += r_affinetridesc.skinwidth; + ltfrac &= 0xFFFF; + } + } while (--lcount); + } + + pspanpackage++; + } while (pspanpackage->count != -999999); +} + + +/* +================ +R_PolysetDrawSpans8 +================ +*/ +void R_PolysetDrawSpans8_33( spanpackage_t *pspanpackage) +{ + byte *lpdest; + byte *lptex; + int lsfrac, ltfrac; + int llight; + int lzi; + zvalue_t *lpz; + + do + { + int lcount; + + lcount = d_aspancount - pspanpackage->count; + + errorterm += erroradjustup; + if (errorterm >= 0) + { + d_aspancount += d_countextrastep; + errorterm -= erroradjustdown; + } + else + { + d_aspancount += ubasestep; + } + + if (lcount) + { + lpdest = pspanpackage->pdest; + lptex = pspanpackage->ptex; + lpz = pspanpackage->pz; + lsfrac = pspanpackage->sfrac; + ltfrac = pspanpackage->tfrac; + llight = pspanpackage->light; + lzi = pspanpackage->zi; + + do + { + if ((lzi >> 16) >= *lpz) + { + int temp = vid_colormap[*lptex + ( llight & 0xFF00 )]; + + *lpdest = vid_alphamap[temp+ *lpdest*256]; + } + lpdest++; + lzi += r_zistepx; + lpz++; + llight += r_lstepx; + lptex += a_ststepxwhole; + lsfrac += a_sstepxfrac; + lptex += lsfrac >> 16; + lsfrac &= 0xFFFF; + ltfrac += a_tstepxfrac; + if (ltfrac & 0x10000) + { + lptex += r_affinetridesc.skinwidth; + ltfrac &= 0xFFFF; + } + } while (--lcount); + } + + pspanpackage++; + } while (pspanpackage->count != -999999); +} + +void R_PolysetDrawSpansConstant8_33( spanpackage_t *pspanpackage) +{ + pixel_t *lpdest; + int lzi; + zvalue_t *lpz; + + do + { + int lcount; + + lcount = d_aspancount - pspanpackage->count; + + errorterm += erroradjustup; + if (errorterm >= 0) + { + d_aspancount += d_countextrastep; + errorterm -= erroradjustdown; + } + else + { + d_aspancount += ubasestep; + } + + if (lcount) + { + lpdest = pspanpackage->pdest; + lpz = pspanpackage->pz; + lzi = pspanpackage->zi; + + do + { + if ((lzi >> 16) >= *lpz) + { + *lpdest = vid_alphamap[r_aliasblendcolor + *lpdest*256]; + } + lpdest++; + lzi += r_zistepx; + lpz++; + } while (--lcount); + } + + pspanpackage++; + } while (pspanpackage->count != -999999); +} + +void R_PolysetDrawSpans8_66(spanpackage_t *pspanpackage) +{ + pixel_t *lpdest; + pixel_t *lptex; + int lsfrac, ltfrac; + int llight; + int lzi; + zvalue_t *lpz; + + do + { + int lcount; + + lcount = d_aspancount - pspanpackage->count; + + errorterm += erroradjustup; + if (errorterm >= 0) + { + d_aspancount += d_countextrastep; + errorterm -= erroradjustdown; + } + else + { + d_aspancount += ubasestep; + } + + if (lcount) + { + lpdest = pspanpackage->pdest; + lptex = pspanpackage->ptex; + lpz = pspanpackage->pz; + lsfrac = pspanpackage->sfrac; + ltfrac = pspanpackage->tfrac; + llight = pspanpackage->light; + lzi = pspanpackage->zi; + + do + { + if ((lzi >> 16) >= *lpz) + { + int temp = vid_colormap[*lptex + ( llight & 0xFF00 )]; + + *lpdest = vid_alphamap[temp*256 + *lpdest]; + *lpz = lzi >> 16; + } + lpdest++; + lzi += r_zistepx; + lpz++; + llight += r_lstepx; + lptex += a_ststepxwhole; + lsfrac += a_sstepxfrac; + lptex += lsfrac >> 16; + lsfrac &= 0xFFFF; + ltfrac += a_tstepxfrac; + if (ltfrac & 0x10000) + { + lptex += r_affinetridesc.skinwidth; + ltfrac &= 0xFFFF; + } + } while (--lcount); + } + + pspanpackage++; + } while (pspanpackage->count != -999999); +} + +void R_PolysetDrawSpansConstant8_66( spanpackage_t *pspanpackage) +{ + pixel_t *lpdest; + int lzi; + zvalue_t *lpz; + + do + { + int lcount; + + lcount = d_aspancount - pspanpackage->count; + + errorterm += erroradjustup; + if (errorterm >= 0) + { + d_aspancount += d_countextrastep; + errorterm -= erroradjustdown; + } + else + { + d_aspancount += ubasestep; + } + + if (lcount) + { + lpdest = pspanpackage->pdest; + lpz = pspanpackage->pz; + lzi = pspanpackage->zi; + + do + { + if ((lzi >> 16) >= *lpz) + { + *lpdest = vid_alphamap[r_aliasblendcolor*256 + *lpdest]; + } + lpdest++; + lzi += r_zistepx; + lpz++; + } while (--lcount); + } + + pspanpackage++; + } while (pspanpackage->count != -999999); +} + +void R_PolysetDrawSpans8_Opaque (spanpackage_t *pspanpackage) +{ + do + { + int lcount; + + lcount = d_aspancount - pspanpackage->count; + errorterm += erroradjustup; + if (errorterm >= 0) + { + d_aspancount += d_countextrastep; + errorterm -= erroradjustdown; + } + else + { + d_aspancount += ubasestep; + } + + if (lcount) + { + int lsfrac, ltfrac; + pixel_t *lpdest; + pixel_t *lptex; + int llight; + int lzi; + zvalue_t *lpz; + + lpdest = pspanpackage->pdest; + lptex = pspanpackage->ptex; + lpz = pspanpackage->pz; + lsfrac = pspanpackage->sfrac; + ltfrac = pspanpackage->tfrac; + llight = pspanpackage->light; + lzi = pspanpackage->zi; + + do + { + if ((lzi >> 16) >= *lpz) + { + //PGM + if(r_newrefdef.rdflags & RDF_IRGOGGLES && currententity->flags & RF_IR_VISIBLE) + *lpdest = ((byte *)vid_colormap)[irtable[*lptex]]; + else + *lpdest = ((byte *)vid_colormap)[*lptex + (llight & 0xFF00)]; + //PGM + + *lpz = lzi >> 16; + } + lpdest++; + lzi += r_zistepx; + lpz++; + llight += r_lstepx; + lptex += a_ststepxwhole; + lsfrac += a_sstepxfrac; + lptex += lsfrac >> 16; + lsfrac &= 0xFFFF; + ltfrac += a_tstepxfrac; + if (ltfrac & 0x10000) + { + lptex += r_affinetridesc.skinwidth; + ltfrac &= 0xFFFF; + } + } while (--lcount); + } + + pspanpackage++; + } while (pspanpackage->count != -999999); +} + +/* +================ +R_PolysetFillSpans8 +================ +*/ +void R_PolysetFillSpans8 (spanpackage_t *pspanpackage) +{ + int color; + + // FIXME: do z buffering + color = d_aflatcolor++; + + while (1) + { + int lcount; + + lcount = pspanpackage->count; + if (lcount == -1) + return; + + if (lcount) + { + byte *lpdest; + + lpdest = pspanpackage->pdest; + + do + { + *lpdest++ = color; + } while (--lcount); + } + + pspanpackage++; + } +} + +/* +================ +R_RasterizeAliasPolySmooth +================ +*/ +void R_RasterizeAliasPolySmooth (void) +{ + int initialleftheight, initialrightheight; + int *plefttop, *prighttop, *pleftbottom, *prightbottom; + int working_lstepx, originalcount; + + plefttop = pedgetable->pleftedgevert0; + prighttop = pedgetable->prightedgevert0; + + pleftbottom = pedgetable->pleftedgevert1; + prightbottom = pedgetable->prightedgevert1; + + initialleftheight = pleftbottom[1] - plefttop[1]; + initialrightheight = prightbottom[1] - prighttop[1]; + + // + // set the s, t, and light gradients, which are consistent across the triangle + // because being a triangle, things are affine + // + R_PolysetCalcGradients (r_affinetridesc.skinwidth); + // + // rasterize the polygon + // + + // + // scan out the top (and possibly only) part of the left edge + // + d_pedgespanpackage = a_spans; + + ystart = plefttop[1]; + d_aspancount = plefttop[0] - prighttop[0]; + + d_ptex = (byte *)r_affinetridesc.pskin + (plefttop[2] >> 16) + + (plefttop[3] >> 16) * r_affinetridesc.skinwidth; + { + d_sfrac = plefttop[2] & 0xFFFF; + d_tfrac = plefttop[3] & 0xFFFF; + } + d_light = plefttop[4]; + d_zi = plefttop[5]; + + d_pdest = d_viewbuffer + ystart * r_screenwidth + plefttop[0]; + d_pz = d_pzbuffer + ystart * d_zwidth + plefttop[0]; + + if (initialleftheight == 1) + { + d_pedgespanpackage->pdest = d_pdest; + d_pedgespanpackage->pz = d_pz; + d_pedgespanpackage->count = d_aspancount; + d_pedgespanpackage->ptex = d_ptex; + + d_pedgespanpackage->sfrac = d_sfrac; + d_pedgespanpackage->tfrac = d_tfrac; + + // FIXME: need to clamp l, s, t, at both ends? + d_pedgespanpackage->light = d_light; + d_pedgespanpackage->zi = d_zi; + + d_pedgespanpackage++; + } + else + { + R_PolysetSetUpForLineScan(plefttop[0], plefttop[1], + pleftbottom[0], pleftbottom[1]); + { + d_pzbasestep = d_zwidth + ubasestep; + d_pzextrastep = d_pzbasestep + 1; + } + + d_pdestbasestep = r_screenwidth + ubasestep; + d_pdestextrastep = d_pdestbasestep + 1; + + // TODO: can reuse partial expressions here + + // for negative steps in x along left edge, bias toward overflow rather than + // underflow (sort of turning the floor () we did in the gradient calcs into + // ceil (), but plus a little bit) + if (ubasestep < 0) + working_lstepx = r_lstepx - 1; + else + working_lstepx = r_lstepx; + + d_countextrastep = ubasestep + 1; + d_ptexbasestep = ((r_sstepy + r_sstepx * ubasestep) >> 16) + + ((r_tstepy + r_tstepx * ubasestep) >> 16) * + r_affinetridesc.skinwidth; + { + d_sfracbasestep = (r_sstepy + r_sstepx * ubasestep) & 0xFFFF; + d_tfracbasestep = (r_tstepy + r_tstepx * ubasestep) & 0xFFFF; + } + d_lightbasestep = r_lstepy + working_lstepx * ubasestep; + d_zibasestep = r_zistepy + r_zistepx * ubasestep; + + d_ptexextrastep = ((r_sstepy + r_sstepx * d_countextrastep) >> 16) + + ((r_tstepy + r_tstepx * d_countextrastep) >> 16) * + r_affinetridesc.skinwidth; + { + d_sfracextrastep = (r_sstepy + r_sstepx*d_countextrastep) & 0xFFFF; + d_tfracextrastep = (r_tstepy + r_tstepx*d_countextrastep) & 0xFFFF; + } + d_lightextrastep = d_lightbasestep + working_lstepx; + d_ziextrastep = d_zibasestep + r_zistepx; + + { + R_PolysetScanLeftEdge_C(initialleftheight); + } + } + + // + // scan out the bottom part of the left edge, if it exists + // + if (pedgetable->numleftedges == 2) + { + int height; + + plefttop = pleftbottom; + pleftbottom = pedgetable->pleftedgevert2; + + height = pleftbottom[1] - plefttop[1]; + + // TODO: make this a function; modularize this function in general + + ystart = plefttop[1]; + d_aspancount = plefttop[0] - prighttop[0]; + d_ptex = (byte *)r_affinetridesc.pskin + (plefttop[2] >> 16) + + (plefttop[3] >> 16) * r_affinetridesc.skinwidth; + d_sfrac = 0; + d_tfrac = 0; + d_light = plefttop[4]; + d_zi = plefttop[5]; + + d_pdest = d_viewbuffer + ystart * r_screenwidth + plefttop[0]; + d_pz = d_pzbuffer + ystart * d_zwidth + plefttop[0]; + + if (height == 1) + { + d_pedgespanpackage->pdest = d_pdest; + d_pedgespanpackage->pz = d_pz; + d_pedgespanpackage->count = d_aspancount; + d_pedgespanpackage->ptex = d_ptex; + + d_pedgespanpackage->sfrac = d_sfrac; + d_pedgespanpackage->tfrac = d_tfrac; + + // FIXME: need to clamp l, s, t, at both ends? + d_pedgespanpackage->light = d_light; + d_pedgespanpackage->zi = d_zi; + + d_pedgespanpackage++; + } + else + { + R_PolysetSetUpForLineScan(plefttop[0], plefttop[1], + pleftbottom[0], pleftbottom[1]); + + d_pdestbasestep = r_screenwidth + ubasestep; + d_pdestextrastep = d_pdestbasestep + 1; + + { + d_pzbasestep = d_zwidth + ubasestep; + d_pzextrastep = d_pzbasestep + 1; + } + + if (ubasestep < 0) + working_lstepx = r_lstepx - 1; + else + working_lstepx = r_lstepx; + + d_countextrastep = ubasestep + 1; + d_ptexbasestep = ((r_sstepy + r_sstepx * ubasestep) >> 16) + + ((r_tstepy + r_tstepx * ubasestep) >> 16) * + r_affinetridesc.skinwidth; + { + d_sfracbasestep = (r_sstepy + r_sstepx * ubasestep) & 0xFFFF; + d_tfracbasestep = (r_tstepy + r_tstepx * ubasestep) & 0xFFFF; + } + d_lightbasestep = r_lstepy + working_lstepx * ubasestep; + d_zibasestep = r_zistepy + r_zistepx * ubasestep; + + d_ptexextrastep = ((r_sstepy + r_sstepx * d_countextrastep) >> 16) + + ((r_tstepy + r_tstepx * d_countextrastep) >> 16) * + r_affinetridesc.skinwidth; + { + d_sfracextrastep = (r_sstepy+r_sstepx*d_countextrastep) & 0xFFFF; + d_tfracextrastep = (r_tstepy+r_tstepx*d_countextrastep) & 0xFFFF; + } + d_lightextrastep = d_lightbasestep + working_lstepx; + d_ziextrastep = d_zibasestep + r_zistepx; + + { + R_PolysetScanLeftEdge_C(height); + } + } + } + + // scan out the top (and possibly only) part of the right edge, updating the + // count field + d_pedgespanpackage = a_spans; + + R_PolysetSetUpForLineScan(prighttop[0], prighttop[1], + prightbottom[0], prightbottom[1]); + d_aspancount = 0; + d_countextrastep = ubasestep + 1; + originalcount = a_spans[initialrightheight].count; + a_spans[initialrightheight].count = -999999; // mark end of the spanpackages + (*d_pdrawspans) (a_spans); + + // scan out the bottom part of the right edge, if it exists + if (pedgetable->numrightedges == 2) + { + int height; + spanpackage_t *pstart; + + pstart = a_spans + initialrightheight; + pstart->count = originalcount; + + d_aspancount = prightbottom[0] - prighttop[0]; + + prighttop = prightbottom; + prightbottom = pedgetable->prightedgevert2; + + height = prightbottom[1] - prighttop[1]; + + R_PolysetSetUpForLineScan(prighttop[0], prighttop[1], + prightbottom[0], prightbottom[1]); + + d_countextrastep = ubasestep + 1; + a_spans[initialrightheight + height].count = -999999; // mark end of the spanpackages + (*d_pdrawspans) (pstart); + } +} + + +/* +================ +R_PolysetSetEdgeTable +================ +*/ +void R_PolysetSetEdgeTable (void) +{ + int edgetableindex; + + edgetableindex = 0; // assume the vertices are already in + // top to bottom order + + // + // determine which edges are right & left, and the order in which + // to rasterize them + // + if (r_p0[1] >= r_p1[1]) + { + if (r_p0[1] == r_p1[1]) + { + if (r_p0[1] < r_p2[1]) + pedgetable = &edgetables[2]; + else + pedgetable = &edgetables[5]; + + return; + } + else + { + edgetableindex = 1; + } + } + + if (r_p0[1] == r_p2[1]) + { + if (edgetableindex) + pedgetable = &edgetables[8]; + else + pedgetable = &edgetables[9]; + + return; + } + else if (r_p1[1] == r_p2[1]) + { + if (edgetableindex) + pedgetable = &edgetables[10]; + else + pedgetable = &edgetables[11]; + + return; + } + + if (r_p0[1] > r_p2[1]) + edgetableindex += 2; + + if (r_p1[1] > r_p2[1]) + edgetableindex += 4; + + pedgetable = &edgetables[edgetableindex]; +} diff --git a/src/client/refresh/soft/r_rast.c b/src/client/refresh/soft/r_rast.c new file mode 100644 index 00000000..2d2333c0 --- /dev/null +++ b/src/client/refresh/soft/r_rast.c @@ -0,0 +1,840 @@ +/* +Copyright (C) 1997-2001 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +// r_rast.c + +#include +#include + +#include "r_local.h" + +#define MAXLEFTCLIPEDGES 100 + +#define FULLY_CLIPPED_CACHED 0x80000000 +#define FRAMECOUNT_MASK 0x7FFFFFFF + +unsigned int cacheoffset; + +int c_faceclip; // number of faces clipped + + +clipplane_t *entity_clipplanes; +clipplane_t view_clipplanes[4]; +clipplane_t world_clipplanes[16]; + +medge_t *r_pedge; + +qboolean r_leftclipped, r_rightclipped; +static qboolean makeleftedge, makerightedge; +qboolean r_nearzionly; + +int *sintable; +int *intsintable; +int *blanktable; // PGM + +mvertex_t r_leftenter, r_leftexit; +mvertex_t r_rightenter, r_rightexit; + +int r_emitted; +float r_nearzi; +float r_u1, r_v1, r_lzi1; +int r_ceilv1; + +qboolean r_lastvertvalid; +int r_skyframe; + +msurface_t *r_skyfaces; +mplane_t r_skyplanes[6]; +mtexinfo_t r_skytexinfo[6]; +mvertex_t *r_skyverts; +medge_t *r_skyedges; +int *r_skysurfedges; + +// I just copied this data from a box map... +int skybox_planes[12] = {2,-128, 0,-128, 2,128, 1,128, 0,128, 1,-128}; + +int box_surfedges[24] = { 1,2,3,4, -1,5,6,7, 8,9,-6,10, -2,-7,-9,11, + 12,-3,-11,-8, -12,-10,-5,-4}; +int box_edges[24] = { 1,2, 2,3, 3,4, 4,1, 1,5, 5,6, 6,2, 7,8, 8,6, 5,7, 8,3, 7,4}; + +int box_faces[6] = {0,0,2,2,2,0}; + +vec3_t box_vecs[6][2] = { + { {0,-1,0}, {-1,0,0} }, + { {0,1,0}, {0,0,-1} }, + { {0,-1,0}, {1,0,0} }, + { {1,0,0}, {0,0,-1} }, + { {0,-1,0}, {0,0,-1} }, + { {-1,0,0}, {0,0,-1} } +}; + +float box_verts[8][3] = { + {-1,-1,-1}, + {-1,1,-1}, + {1,1,-1}, + {1,-1,-1}, + {-1,-1,1}, + {-1,1,1}, + {1,-1,1}, + {1,1,1} +}; + +// down, west, up, north, east, south +// {"rt", "bk", "lf", "ft", "up", "dn"}; + +/* +================ +R_InitSkyBox + +================ +*/ +void R_InitSkyBox (void) +{ + int i; + extern model_t *loadmodel; + + r_skyfaces = loadmodel->surfaces + loadmodel->numsurfaces; + loadmodel->numsurfaces += 6; + r_skyverts = loadmodel->vertexes + loadmodel->numvertexes; + loadmodel->numvertexes += 8; + r_skyedges = loadmodel->edges + loadmodel->numedges; + loadmodel->numedges += 12; + r_skysurfedges = loadmodel->surfedges + loadmodel->numsurfedges; + loadmodel->numsurfedges += 24; + if (loadmodel->numsurfaces > MAX_MAP_FACES + || loadmodel->numvertexes > MAX_MAP_VERTS + || loadmodel->numedges > MAX_MAP_EDGES) + ri.Sys_Error (ERR_DROP, "InitSkyBox: map overflow"); + + memset (r_skyfaces, 0, 6*sizeof(*r_skyfaces)); + for (i=0 ; i<6 ; i++) + { + r_skyplanes[i].normal[skybox_planes[i*2]] = 1; + r_skyplanes[i].dist = skybox_planes[i*2+1]; + + VectorCopy (box_vecs[i][0], r_skytexinfo[i].vecs[0]); + VectorCopy (box_vecs[i][1], r_skytexinfo[i].vecs[1]); + + r_skyfaces[i].plane = &r_skyplanes[i]; + r_skyfaces[i].numedges = 4; + r_skyfaces[i].flags = box_faces[i] | SURF_DRAWSKYBOX; + r_skyfaces[i].firstedge = loadmodel->numsurfedges-24+i*4; + r_skyfaces[i].texinfo = &r_skytexinfo[i]; + r_skyfaces[i].texturemins[0] = -128; + r_skyfaces[i].texturemins[1] = -128; + r_skyfaces[i].extents[0] = 256; + r_skyfaces[i].extents[1] = 256; + } + + for (i=0 ; i<24 ; i++) + if (box_surfedges[i] > 0) + r_skysurfedges[i] = loadmodel->numedges-13 + box_surfedges[i]; + else + r_skysurfedges[i] = - (loadmodel->numedges-13 + -box_surfedges[i]); + + for(i=0 ; i<12 ; i++) + { + r_skyedges[i].v[0] = loadmodel->numvertexes-9+box_edges[i*2+0]; + r_skyedges[i].v[1] = loadmodel->numvertexes-9+box_edges[i*2+1]; + r_skyedges[i].cachededgeoffset = 0; + } +} + +/* +================ +R_EmitSkyBox +================ +*/ +void R_EmitSkyBox (void) +{ + int i, j; + int oldkey; + + if (insubmodel) + return; // submodels should never have skies + if (r_skyframe == r_framecount) + return; // already set this frame + + r_skyframe = r_framecount; + + // set the eight fake vertexes + for (i=0 ; i<8 ; i++) + for (j=0 ; j<3 ; j++) + r_skyverts[i].position[j] = r_origin[j] + box_verts[i][j]*128; + + // set the six fake planes + for (i=0 ; i<6 ; i++) + if (skybox_planes[i*2+1] > 0) + r_skyplanes[i].dist = r_origin[skybox_planes[i*2]]+128; + else + r_skyplanes[i].dist = r_origin[skybox_planes[i*2]]-128; + + // fix texture offseets + for (i=0 ; i<6 ; i++) + { + r_skytexinfo[i].vecs[0][3] = -DotProduct (r_origin, r_skytexinfo[i].vecs[0]); + r_skytexinfo[i].vecs[1][3] = -DotProduct (r_origin, r_skytexinfo[i].vecs[1]); + } + + // emit the six faces + oldkey = r_currentkey; + r_currentkey = 0x7ffffff0; + for (i=0 ; i<6 ; i++) + { + R_RenderFace (r_skyfaces + i, 15); + } + r_currentkey = oldkey; // bsp sorting order +} + +/* +================ +R_EmitEdge +================ +*/ +void R_EmitEdge (mvertex_t *pv0, mvertex_t *pv1) +{ + edge_t *edge, *pcheck; + int u_check; + float u, u_step; + vec3_t local, transformed; + float *world; + int v, v2, ceilv0; + float scale, lzi0, u0, v0; + int side; + + if (r_lastvertvalid) + { + u0 = r_u1; + v0 = r_v1; + lzi0 = r_lzi1; + ceilv0 = r_ceilv1; + } + else + { + world = &pv0->position[0]; + + // transform and project + VectorSubtract (world, modelorg, local); + TransformVector (local, transformed); + + if (transformed[2] < NEAR_CLIP) + transformed[2] = NEAR_CLIP; + + lzi0 = 1.0 / transformed[2]; + + // FIXME: build x/yscale into transform? + scale = xscale * lzi0; + u0 = (xcenter + scale*transformed[0]); + if (u0 < r_refdef.fvrectx_adj) + u0 = r_refdef.fvrectx_adj; + if (u0 > r_refdef.fvrectright_adj) + u0 = r_refdef.fvrectright_adj; + + scale = yscale * lzi0; + v0 = (ycenter - scale*transformed[1]); + if (v0 < r_refdef.fvrecty_adj) + v0 = r_refdef.fvrecty_adj; + if (v0 > r_refdef.fvrectbottom_adj) + v0 = r_refdef.fvrectbottom_adj; + + ceilv0 = (int) ceil(v0); + } + + world = &pv1->position[0]; + + // transform and project + VectorSubtract (world, modelorg, local); + TransformVector (local, transformed); + + if (transformed[2] < NEAR_CLIP) + transformed[2] = NEAR_CLIP; + + r_lzi1 = 1.0 / transformed[2]; + + scale = xscale * r_lzi1; + r_u1 = (xcenter + scale*transformed[0]); + if (r_u1 < r_refdef.fvrectx_adj) + r_u1 = r_refdef.fvrectx_adj; + if (r_u1 > r_refdef.fvrectright_adj) + r_u1 = r_refdef.fvrectright_adj; + + scale = yscale * r_lzi1; + r_v1 = (ycenter - scale*transformed[1]); + if (r_v1 < r_refdef.fvrecty_adj) + r_v1 = r_refdef.fvrecty_adj; + if (r_v1 > r_refdef.fvrectbottom_adj) + r_v1 = r_refdef.fvrectbottom_adj; + + if (r_lzi1 > lzi0) + lzi0 = r_lzi1; + + if (lzi0 > r_nearzi) // for mipmap finding + r_nearzi = lzi0; + + // for right edges, all we want is the effect on 1/z + if (r_nearzionly) + return; + + r_emitted = 1; + + r_ceilv1 = (int) ceil(r_v1); + + + // create the edge + if (ceilv0 == r_ceilv1) + { + // we cache unclipped horizontal edges as fully clipped + if (cacheoffset != 0x7FFFFFFF) + { + cacheoffset = FULLY_CLIPPED_CACHED | + (r_framecount & FRAMECOUNT_MASK); + } + + return; // horizontal edge + } + + side = ceilv0 > r_ceilv1; + + edge = edge_p++; + + edge->owner = r_pedge; + + edge->nearzi = lzi0; + + if (side == 0) + { + // trailing edge (go from p1 to p2) + v = ceilv0; + v2 = r_ceilv1 - 1; + + edge->surfs[0] = surface_p - surfaces; + edge->surfs[1] = 0; + + u_step = ((r_u1 - u0) / (r_v1 - v0)); + u = u0 + ((float)v - v0) * u_step; + } + else + { + // leading edge (go from p2 to p1) + v2 = ceilv0 - 1; + v = r_ceilv1; + + edge->surfs[0] = 0; + edge->surfs[1] = surface_p - surfaces; + + u_step = ((u0 - r_u1) / (v0 - r_v1)); + u = r_u1 + ((float)v - r_v1) * u_step; + } + + edge->u_step = u_step*(1<u = u*(1<u < r_refdef.vrect_x_adj_shift20) + edge->u = r_refdef.vrect_x_adj_shift20; + if (edge->u > r_refdef.vrectright_adj_shift20) + edge->u = r_refdef.vrectright_adj_shift20; + + // + // sort the edge in normally + // + u_check = edge->u; + if (edge->surfs[0]) + u_check++; // sort trailers after leaders + + if (!newedges[v] || newedges[v]->u >= u_check) + { + edge->next = newedges[v]; + newedges[v] = edge; + } + else + { + pcheck = newedges[v]; + while (pcheck->next && pcheck->next->u < u_check) + pcheck = pcheck->next; + edge->next = pcheck->next; + pcheck->next = edge; + } + + edge->nextremove = removeedges[v2]; + removeedges[v2] = edge; +} + + +/* +================ +R_ClipEdge +================ +*/ +void R_ClipEdge (mvertex_t *pv0, mvertex_t *pv1, clipplane_t *clip) +{ + if (clip) + { + do + { + float d0, d1, f; + mvertex_t clipvert; + + d0 = DotProduct (pv0->position, clip->normal) - clip->dist; + d1 = DotProduct (pv1->position, clip->normal) - clip->dist; + + if (d0 >= 0) + { + // point 0 is unclipped + if (d1 >= 0) + { + // both points are unclipped + continue; + } + + // only point 1 is clipped + // we don't cache clipped edges + cacheoffset = 0x7FFFFFFF; + + f = d0 / (d0 - d1); + clipvert.position[0] = pv0->position[0] + + f * (pv1->position[0] - pv0->position[0]); + clipvert.position[1] = pv0->position[1] + + f * (pv1->position[1] - pv0->position[1]); + clipvert.position[2] = pv0->position[2] + + f * (pv1->position[2] - pv0->position[2]); + + if (clip->leftedge) + { + r_leftclipped = true; + r_leftexit = clipvert; + } + else if (clip->rightedge) + { + r_rightclipped = true; + r_rightexit = clipvert; + } + + R_ClipEdge (pv0, &clipvert, clip->next); + return; + } + else + { + // point 0 is clipped + if (d1 < 0) + { + // both points are clipped + // we do cache fully clipped edges + if (!r_leftclipped) + cacheoffset = FULLY_CLIPPED_CACHED | + (r_framecount & FRAMECOUNT_MASK); + return; + } + + // only point 0 is clipped + r_lastvertvalid = false; + + // we don't cache partially clipped edges + cacheoffset = 0x7FFFFFFF; + + f = d0 / (d0 - d1); + clipvert.position[0] = pv0->position[0] + + f * (pv1->position[0] - pv0->position[0]); + clipvert.position[1] = pv0->position[1] + + f * (pv1->position[1] - pv0->position[1]); + clipvert.position[2] = pv0->position[2] + + f * (pv1->position[2] - pv0->position[2]); + + if (clip->leftedge) + { + r_leftclipped = true; + r_leftenter = clipvert; + } + else if (clip->rightedge) + { + r_rightclipped = true; + r_rightenter = clipvert; + } + + R_ClipEdge (&clipvert, pv1, clip->next); + return; + } + } while ((clip = clip->next) != NULL); + } + + // add the edge + R_EmitEdge (pv0, pv1); +} + +/* +================ +R_EmitCachedEdge +================ +*/ +void R_EmitCachedEdge (void) +{ + edge_t *pedge_t; + + pedge_t = (edge_t *)((uintptr_t)r_edges + r_pedge->cachededgeoffset); + + if (!pedge_t->surfs[0]) + pedge_t->surfs[0] = surface_p - surfaces; + else + pedge_t->surfs[1] = surface_p - surfaces; + + if (pedge_t->nearzi > r_nearzi) // for mipmap finding + r_nearzi = pedge_t->nearzi; + + r_emitted = 1; +} + + +/* +================ +R_RenderFace +================ +*/ +void R_RenderFace (msurface_t *fa, int clipflags) +{ + int i; + unsigned mask; + mplane_t *pplane; + float distinv; + vec3_t p_normal; + medge_t *pedges, tedge; + clipplane_t *pclip; + + // translucent surfaces are not drawn by the edge renderer + if (fa->texinfo->flags & (SURF_TRANS33|SURF_TRANS66)) + { + fa->nextalphasurface = r_alpha_surfaces; + r_alpha_surfaces = fa; + return; + } + + // sky surfaces encountered in the world will cause the + // environment box surfaces to be emited + if ( fa->texinfo->flags & SURF_SKY ) + { + R_EmitSkyBox (); + return; + } + + // skip out if no more surfs + if ((surface_p) >= surf_max) + { + r_outofsurfaces++; + return; + } + + // ditto if not enough edges left, or switch to auxedges if possible + if ((edge_p + fa->numedges + 4) >= edge_max) + { + r_outofedges += fa->numedges; + return; + } + + c_faceclip++; + + // set up clip planes + pclip = NULL; + + for (i=3, mask = 0x08 ; i>=0 ; i--, mask >>= 1) + { + if (clipflags & mask) + { + view_clipplanes[i].next = pclip; + pclip = &view_clipplanes[i]; + } + } + + // push the edges through + r_emitted = 0; + r_nearzi = 0; + r_nearzionly = false; + makeleftedge = makerightedge = false; + pedges = currentmodel->edges; + r_lastvertvalid = false; + + for (i=0 ; inumedges ; i++) + { + int lindex; + + lindex = currentmodel->surfedges[fa->firstedge + i]; + + if (lindex > 0) + { + r_pedge = &pedges[lindex]; + + // if the edge is cached, we can just reuse the edge + if (!insubmodel) + { + if (r_pedge->cachededgeoffset & FULLY_CLIPPED_CACHED) + { + if ((r_pedge->cachededgeoffset & FRAMECOUNT_MASK) == + r_framecount) + { + r_lastvertvalid = false; + continue; + } + } + else + { + if ((((uintptr_t)edge_p - (uintptr_t)r_edges) > + r_pedge->cachededgeoffset) && + (((edge_t *)((uintptr_t)r_edges + + r_pedge->cachededgeoffset))->owner == r_pedge)) + { + R_EmitCachedEdge (); + r_lastvertvalid = false; + continue; + } + } + } + + // assume it's cacheable + cacheoffset = (byte *)edge_p - (byte *)r_edges; + r_leftclipped = r_rightclipped = false; + R_ClipEdge (&r_pcurrentvertbase[r_pedge->v[0]], + &r_pcurrentvertbase[r_pedge->v[1]], + pclip); + r_pedge->cachededgeoffset = cacheoffset; + + if (r_leftclipped) + makeleftedge = true; + if (r_rightclipped) + makerightedge = true; + r_lastvertvalid = true; + } + else + { + lindex = -lindex; + r_pedge = &pedges[lindex]; + // if the edge is cached, we can just reuse the edge + if (!insubmodel) + { + if (r_pedge->cachededgeoffset & FULLY_CLIPPED_CACHED) + { + if ((r_pedge->cachededgeoffset & FRAMECOUNT_MASK) == + r_framecount) + { + r_lastvertvalid = false; + continue; + } + } + else + { + // it's cached if the cached edge is valid and is owned + // by this medge_t + if ((((uintptr_t)edge_p - (uintptr_t)r_edges) > + r_pedge->cachededgeoffset) && + (((edge_t *)((uintptr_t)r_edges + + r_pedge->cachededgeoffset))->owner == r_pedge)) + { + R_EmitCachedEdge (); + r_lastvertvalid = false; + continue; + } + } + } + + // assume it's cacheable + cacheoffset = (byte *)edge_p - (byte *)r_edges; + r_leftclipped = r_rightclipped = false; + R_ClipEdge (&r_pcurrentvertbase[r_pedge->v[1]], + &r_pcurrentvertbase[r_pedge->v[0]], + pclip); + r_pedge->cachededgeoffset = cacheoffset; + + if (r_leftclipped) + makeleftedge = true; + if (r_rightclipped) + makerightedge = true; + r_lastvertvalid = true; + } + } + + // if there was a clip off the left edge, add that edge too + // FIXME: faster to do in screen space? + // FIXME: share clipped edges? + if (makeleftedge) + { + r_pedge = &tedge; + r_lastvertvalid = false; + R_ClipEdge (&r_leftexit, &r_leftenter, pclip->next); + } + + // if there was a clip off the right edge, get the right r_nearzi + if (makerightedge) + { + r_pedge = &tedge; + r_lastvertvalid = false; + r_nearzionly = true; + R_ClipEdge (&r_rightexit, &r_rightenter, view_clipplanes[1].next); + } + + // if no edges made it out, return without posting the surface + if (!r_emitted) + return; + + r_polycount++; + + surface_p->msurf = fa; + surface_p->nearzi = r_nearzi; + surface_p->flags = fa->flags; + surface_p->insubmodel = insubmodel; + surface_p->spanstate = 0; + surface_p->entity = currententity; + surface_p->key = r_currentkey++; + surface_p->spans = NULL; + + pplane = fa->plane; + // FIXME: cache this? + TransformVector (pplane->normal, p_normal); + // FIXME: cache this? + distinv = 1.0 / (pplane->dist - DotProduct (modelorg, pplane->normal)); + + surface_p->d_zistepu = p_normal[0] * xscaleinv * distinv; + surface_p->d_zistepv = -p_normal[1] * yscaleinv * distinv; + surface_p->d_ziorigin = p_normal[2] * distinv - + xcenter * surface_p->d_zistepu - + ycenter * surface_p->d_zistepv; + + surface_p++; +} + + +/* +================ +R_RenderBmodelFace +================ +*/ +void R_RenderBmodelFace (bedge_t *pedges, msurface_t *psurf) +{ + int i; + unsigned mask; + mplane_t *pplane; + float distinv; + vec3_t p_normal; + medge_t tedge; + clipplane_t *pclip; + + if (psurf->texinfo->flags & (SURF_TRANS33|SURF_TRANS66)) + { + psurf->nextalphasurface = r_alpha_surfaces; + r_alpha_surfaces = psurf; + return; + } + + // skip out if no more surfs + if (surface_p >= surf_max) + { + r_outofsurfaces++; + return; + } + + // ditto if not enough edges left, or switch to auxedges if possible + if ((edge_p + psurf->numedges + 4) >= edge_max) + { + r_outofedges += psurf->numedges; + return; + } + + c_faceclip++; + + // this is a dummy to give the caching mechanism someplace to write to + r_pedge = &tedge; + + // set up clip planes + pclip = NULL; + + for (i=3, mask = 0x08 ; i>=0 ; i--, mask >>= 1) + { + if (r_clipflags & mask) + { + view_clipplanes[i].next = pclip; + pclip = &view_clipplanes[i]; + } + } + + // push the edges through + r_emitted = 0; + r_nearzi = 0; + r_nearzionly = false; + makeleftedge = makerightedge = false; + // FIXME: keep clipped bmodel edges in clockwise order so last vertex caching + // can be used? + r_lastvertvalid = false; + + for ( ; pedges ; pedges = pedges->pnext) + { + r_leftclipped = r_rightclipped = false; + R_ClipEdge (pedges->v[0], pedges->v[1], pclip); + + if (r_leftclipped) + makeleftedge = true; + if (r_rightclipped) + makerightedge = true; + } + + // if there was a clip off the left edge, add that edge too + // FIXME: faster to do in screen space? + // FIXME: share clipped edges? + if (makeleftedge) + { + r_pedge = &tedge; + R_ClipEdge (&r_leftexit, &r_leftenter, pclip->next); + } + + // if there was a clip off the right edge, get the right r_nearzi + if (makerightedge) + { + r_pedge = &tedge; + r_nearzionly = true; + R_ClipEdge (&r_rightexit, &r_rightenter, view_clipplanes[1].next); + } + + // if no edges made it out, return without posting the surface + if (!r_emitted) + return; + + r_polycount++; + + surface_p->msurf = psurf; + surface_p->nearzi = r_nearzi; + surface_p->flags = psurf->flags; + surface_p->insubmodel = true; + surface_p->spanstate = 0; + surface_p->entity = currententity; + surface_p->key = r_currentbkey; + surface_p->spans = NULL; + + pplane = psurf->plane; + // FIXME: cache this? + TransformVector (pplane->normal, p_normal); + // FIXME: cache this? + distinv = 1.0 / (pplane->dist - DotProduct (modelorg, pplane->normal)); + + surface_p->d_zistepu = p_normal[0] * xscaleinv * distinv; + surface_p->d_zistepv = -p_normal[1] * yscaleinv * distinv; + surface_p->d_ziorigin = p_normal[2] * distinv - + xcenter * surface_p->d_zistepu - + ycenter * surface_p->d_zistepv; + + surface_p++; +} diff --git a/src/client/refresh/soft/r_scan.c b/src/client/refresh/soft/r_scan.c new file mode 100644 index 00000000..c3fb92ab --- /dev/null +++ b/src/client/refresh/soft/r_scan.c @@ -0,0 +1,564 @@ +/* +Copyright (C) 1997-2001 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +// d_scan.c +// +// Portable C scan-level rasterization code, all pixel depths. + +#include "r_local.h" + +pixel_t *r_turb_pbase, *r_turb_pdest; +int r_turb_s, r_turb_t, r_turb_sstep, r_turb_tstep; +int *r_turb_turb; +int r_turb_spancount; + +void D_DrawTurbulent8Span (void); + +byte **warp_rowptr; +int *warp_column; +espan_t *edge_basespans; + +/* +============= +D_WarpScreen + +this performs a slight compression of the screen at the same time as +the sine warp, to keep the edges from wrapping +============= +*/ +void D_WarpScreen (void) +{ + int w, h; + int u,v; + pixel_t *dest; + int *turb; + byte **row; + + static int cached_width, cached_height; + + // + // these are constant over resolutions, and can be saved + // + w = r_newrefdef.width; + h = r_newrefdef.height; + if (w != cached_width || h != cached_height) + { + cached_width = w; + cached_height = h; + for (v=0 ; v>16)&(CYCLE-1)])>>16)&63; + tturb = ((r_turb_t + r_turb_turb[(r_turb_s>>16)&(CYCLE-1)])>>16)&63; + *r_turb_pdest++ = *(r_turb_pbase + (tturb<<6) + sturb); + r_turb_s += r_turb_sstep; + r_turb_t += r_turb_tstep; + } while (--r_turb_spancount > 0); +} + +/* +============= +Turbulent8 +============= +*/ +void Turbulent8 (espan_t *pspan) +{ + int snext, tnext; + float spancountminus1; + float sdivz16stepu, tdivz16stepu, zi16stepu; + + r_turb_turb = sintable + ((int)(r_newrefdef.time*SPEED)&(CYCLE-1)); + + r_turb_sstep = 0; // keep compiler happy + r_turb_tstep = 0; // ditto + + r_turb_pbase = (unsigned char *)cacheblock; + + sdivz16stepu = d_sdivzstepu * 16; + tdivz16stepu = d_tdivzstepu * 16; + zi16stepu = d_zistepu * 16; + + do + { + int count; + float sdivz, tdivz, zi, z, du, dv; + + r_turb_pdest = d_viewbuffer + (r_screenwidth * pspan->v) + pspan->u; + + count = pspan->count; + + // calculate the initial s/z, t/z, 1/z, s, and t and clamp + du = (float)pspan->u; + dv = (float)pspan->v; + + sdivz = d_sdivzorigin + dv*d_sdivzstepv + du*d_sdivzstepu; + tdivz = d_tdivzorigin + dv*d_tdivzstepv + du*d_tdivzstepu; + zi = d_ziorigin + dv*d_zistepv + du*d_zistepu; + z = (float)0x10000 / zi; // prescale to 16.16 fixed-point + + r_turb_s = (int)(sdivz * z) + sadjust; + if (r_turb_s > bbextents) + r_turb_s = bbextents; + else if (r_turb_s < 0) + r_turb_s = 0; + + r_turb_t = (int)(tdivz * z) + tadjust; + if (r_turb_t > bbextentt) + r_turb_t = bbextentt; + else if (r_turb_t < 0) + r_turb_t = 0; + + do + { + // calculate s and t at the far end of the span + if (count >= 16) + r_turb_spancount = 16; + else + r_turb_spancount = count; + + count -= r_turb_spancount; + + if (count) + { + // calculate s/z, t/z, zi->fixed s and t at far end of span, + // calculate s and t steps across span by shifting + sdivz += sdivz16stepu; + tdivz += tdivz16stepu; + zi += zi16stepu; + z = (float)0x10000 / zi; // prescale to 16.16 fixed-point + + snext = (int)(sdivz * z) + sadjust; + if (snext > bbextents) + snext = bbextents; + else if (snext < 16) + snext = 16; // prevent round-off error on <0 steps from + // from causing overstepping & running off the + // edge of the texture + + tnext = (int)(tdivz * z) + tadjust; + if (tnext > bbextentt) + tnext = bbextentt; + else if (tnext < 16) + tnext = 16; // guard against round-off error on <0 steps + + r_turb_sstep = (snext - r_turb_s) >> 4; + r_turb_tstep = (tnext - r_turb_t) >> 4; + } + else + { + // calculate s/z, t/z, zi->fixed s and t at last pixel in span (so + // can't step off polygon), clamp, calculate s and t steps across + // span by division, biasing steps low so we don't run off the + // texture + spancountminus1 = (float)(r_turb_spancount - 1); + sdivz += d_sdivzstepu * spancountminus1; + tdivz += d_tdivzstepu * spancountminus1; + zi += d_zistepu * spancountminus1; + z = (float)0x10000 / zi; // prescale to 16.16 fixed-point + snext = (int)(sdivz * z) + sadjust; + if (snext > bbextents) + snext = bbextents; + else if (snext < 16) + snext = 16; // prevent round-off error on <0 steps from + // from causing overstepping & running off the + // edge of the texture + + tnext = (int)(tdivz * z) + tadjust; + if (tnext > bbextentt) + tnext = bbextentt; + else if (tnext < 16) + tnext = 16; // guard against round-off error on <0 steps + + if (r_turb_spancount > 1) + { + r_turb_sstep = (snext - r_turb_s) / (r_turb_spancount - 1); + r_turb_tstep = (tnext - r_turb_t) / (r_turb_spancount - 1); + } + } + + r_turb_s = r_turb_s & ((CYCLE<<16)-1); + r_turb_t = r_turb_t & ((CYCLE<<16)-1); + + D_DrawTurbulent8Span (); + + r_turb_s = snext; + r_turb_t = tnext; + + } while (count > 0); + + } while ((pspan = pspan->pnext) != NULL); +} + +//==================== +//PGM +/* +============= +NonTurbulent8 - this is for drawing scrolling textures. they're warping water textures + but the turbulence is automatically 0. +============= +*/ +void NonTurbulent8 (espan_t *pspan) +{ + int snext, tnext; + float spancountminus1; + float sdivz16stepu, tdivz16stepu, zi16stepu; + + r_turb_turb = blanktable; + + r_turb_sstep = 0; // keep compiler happy + r_turb_tstep = 0; // ditto + + r_turb_pbase = (unsigned char *)cacheblock; + + sdivz16stepu = d_sdivzstepu * 16; + tdivz16stepu = d_tdivzstepu * 16; + zi16stepu = d_zistepu * 16; + + do + { + int count; + float sdivz, tdivz, zi, z, dv, du; + + r_turb_pdest = d_viewbuffer + (r_screenwidth * pspan->v) + pspan->u; + + count = pspan->count; + + // calculate the initial s/z, t/z, 1/z, s, and t and clamp + du = (float)pspan->u; + dv = (float)pspan->v; + + sdivz = d_sdivzorigin + dv*d_sdivzstepv + du*d_sdivzstepu; + tdivz = d_tdivzorigin + dv*d_tdivzstepv + du*d_tdivzstepu; + zi = d_ziorigin + dv*d_zistepv + du*d_zistepu; + z = (float)0x10000 / zi; // prescale to 16.16 fixed-point + + r_turb_s = (int)(sdivz * z) + sadjust; + if (r_turb_s > bbextents) + r_turb_s = bbextents; + else if (r_turb_s < 0) + r_turb_s = 0; + + r_turb_t = (int)(tdivz * z) + tadjust; + if (r_turb_t > bbextentt) + r_turb_t = bbextentt; + else if (r_turb_t < 0) + r_turb_t = 0; + + do + { + // calculate s and t at the far end of the span + if (count >= 16) + r_turb_spancount = 16; + else + r_turb_spancount = count; + + count -= r_turb_spancount; + + if (count) + { + // calculate s/z, t/z, zi->fixed s and t at far end of span, + // calculate s and t steps across span by shifting + sdivz += sdivz16stepu; + tdivz += tdivz16stepu; + zi += zi16stepu; + z = (float)0x10000 / zi; // prescale to 16.16 fixed-point + + snext = (int)(sdivz * z) + sadjust; + if (snext > bbextents) + snext = bbextents; + else if (snext < 16) + snext = 16; // prevent round-off error on <0 steps from + // from causing overstepping & running off the + // edge of the texture + + tnext = (int)(tdivz * z) + tadjust; + if (tnext > bbextentt) + tnext = bbextentt; + else if (tnext < 16) + tnext = 16; // guard against round-off error on <0 steps + + r_turb_sstep = (snext - r_turb_s) >> 4; + r_turb_tstep = (tnext - r_turb_t) >> 4; + } + else + { + // calculate s/z, t/z, zi->fixed s and t at last pixel in span (so + // can't step off polygon), clamp, calculate s and t steps across + // span by division, biasing steps low so we don't run off the + // texture + spancountminus1 = (float)(r_turb_spancount - 1); + sdivz += d_sdivzstepu * spancountminus1; + tdivz += d_tdivzstepu * spancountminus1; + zi += d_zistepu * spancountminus1; + z = (float)0x10000 / zi; // prescale to 16.16 fixed-point + snext = (int)(sdivz * z) + sadjust; + if (snext > bbextents) + snext = bbextents; + else if (snext < 16) + snext = 16; // prevent round-off error on <0 steps from + // from causing overstepping & running off the + // edge of the texture + + tnext = (int)(tdivz * z) + tadjust; + if (tnext > bbextentt) + tnext = bbextentt; + else if (tnext < 16) + tnext = 16; // guard against round-off error on <0 steps + + if (r_turb_spancount > 1) + { + r_turb_sstep = (snext - r_turb_s) / (r_turb_spancount - 1); + r_turb_tstep = (tnext - r_turb_t) / (r_turb_spancount - 1); + } + } + + r_turb_s = r_turb_s & ((CYCLE<<16)-1); + r_turb_t = r_turb_t & ((CYCLE<<16)-1); + + D_DrawTurbulent8Span (); + + r_turb_s = snext; + r_turb_t = tnext; + + } while (count > 0); + + } while ((pspan = pspan->pnext) != NULL); +} +//PGM +//==================== + +/* +============= +D_DrawSpans16 + + FIXME: actually make this subdivide by 16 instead of 8!!! +============= +*/ +void D_DrawSpans16 (espan_t *pspan) +{ + int spancount; + unsigned char *pbase; + int snext, tnext, sstep, tstep; + float spancountminus1; + float sdivz8stepu, tdivz8stepu, zi8stepu; + + sstep = 0; // keep compiler happy + tstep = 0; // ditto + + pbase = (unsigned char *)cacheblock; + + sdivz8stepu = d_sdivzstepu * 8; + tdivz8stepu = d_tdivzstepu * 8; + zi8stepu = d_zistepu * 8; + + do + { + pixel_t *pdest; + int count, s, t; + float sdivz, tdivz, zi, z, du, dv; + + pdest = d_viewbuffer + (r_screenwidth * pspan->v) + pspan->u; + + count = pspan->count; + + // calculate the initial s/z, t/z, 1/z, s, and t and clamp + du = (float)pspan->u; + dv = (float)pspan->v; + + sdivz = d_sdivzorigin + dv*d_sdivzstepv + du*d_sdivzstepu; + tdivz = d_tdivzorigin + dv*d_tdivzstepv + du*d_tdivzstepu; + zi = d_ziorigin + dv*d_zistepv + du*d_zistepu; + z = (float)0x10000 / zi; // prescale to 16.16 fixed-point + + s = (int)(sdivz * z) + sadjust; + if (s > bbextents) + s = bbextents; + else if (s < 0) + s = 0; + + t = (int)(tdivz * z) + tadjust; + if (t > bbextentt) + t = bbextentt; + else if (t < 0) + t = 0; + + do + { + // calculate s and t at the far end of the span + if (count >= 8) + spancount = 8; + else + spancount = count; + + count -= spancount; + + if (count) + { + // calculate s/z, t/z, zi->fixed s and t at far end of span, + // calculate s and t steps across span by shifting + sdivz += sdivz8stepu; + tdivz += tdivz8stepu; + zi += zi8stepu; + z = (float)0x10000 / zi; // prescale to 16.16 fixed-point + + snext = (int)(sdivz * z) + sadjust; + if (snext > bbextents) + snext = bbextents; + else if (snext < 8) + snext = 8; // prevent round-off error on <0 steps from + // from causing overstepping & running off the + // edge of the texture + + tnext = (int)(tdivz * z) + tadjust; + if (tnext > bbextentt) + tnext = bbextentt; + else if (tnext < 8) + tnext = 8; // guard against round-off error on <0 steps + + sstep = (snext - s) >> 3; + tstep = (tnext - t) >> 3; + } + else + { + // calculate s/z, t/z, zi->fixed s and t at last pixel in span (so + // can't step off polygon), clamp, calculate s and t steps across + // span by division, biasing steps low so we don't run off the + // texture + spancountminus1 = (float)(spancount - 1); + sdivz += d_sdivzstepu * spancountminus1; + tdivz += d_tdivzstepu * spancountminus1; + zi += d_zistepu * spancountminus1; + z = (float)0x10000 / zi; // prescale to 16.16 fixed-point + snext = (int)(sdivz * z) + sadjust; + if (snext > bbextents) + snext = bbextents; + else if (snext < 8) + snext = 8; // prevent round-off error on <0 steps from + // from causing overstepping & running off the + // edge of the texture + + tnext = (int)(tdivz * z) + tadjust; + if (tnext > bbextentt) + tnext = bbextentt; + else if (tnext < 8) + tnext = 8; // guard against round-off error on <0 steps + + if (spancount > 1) + { + sstep = (snext - s) / (spancount - 1); + tstep = (tnext - t) / (spancount - 1); + } + } + + do + { + *pdest++ = *(pbase + (s >> 16) + (t >> 16) * cachewidth); + s += sstep; + t += tstep; + } while (--spancount > 0); + + s = snext; + t = tnext; + + } while (count > 0); + + } while ((pspan = pspan->pnext) != NULL); +} + +/* +============= +D_DrawZSpans +============= +*/ +void D_DrawZSpans (espan_t *pspan) +{ + int izistep; + + // FIXME: check for clamping/range problems + // we count on FP exceptions being turned off to avoid range problems + izistep = (int)(d_zistepu * 0x8000 * 0x10000); + + do + { + int count; + int izi; + zvalue_t *pdest; + float zi; + float du, dv; + + pdest = d_pzbuffer + (d_zwidth * pspan->v) + pspan->u; + + count = pspan->count; + + // calculate the initial 1/z + du = (float)pspan->u; + dv = (float)pspan->v; + + zi = d_ziorigin + dv*d_zistepv + du*d_zistepu; + // we count on FP exceptions being turned off to avoid range problems + izi = (int)(zi * 0x8000 * 0x10000); + + while (count > 0) + { + *pdest++ = izi >> 16; + izi += izistep; + count--; + } + } while ((pspan = pspan->pnext) != NULL); +} diff --git a/src/client/refresh/soft/r_sprite.c b/src/client/refresh/soft/r_sprite.c new file mode 100644 index 00000000..2de1e499 --- /dev/null +++ b/src/client/refresh/soft/r_sprite.c @@ -0,0 +1,113 @@ +/* +Copyright (C) 1997-2001 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +// r_sprite.c +#include "r_local.h" + +extern polydesc_t r_polydesc; + +void R_BuildPolygonFromSurface(msurface_t *fa); +void R_PolygonCalculateGradients (void); + +extern void R_PolyChooseSpanletRoutine( float alpha, qboolean isturbulent ); + +extern vec5_t r_clip_verts[2][MAXWORKINGVERTS+2]; + +extern void R_ClipAndDrawPoly( float alpha, qboolean isturbulent, qboolean textured ); + +/* +** R_DrawSprite +** +** Draw currententity / currentmodel as a single texture +** mapped polygon +*/ +void R_DrawSprite (void) +{ + vec5_t *pverts; + vec3_t left, up, right, down; + dsprite_t *s_psprite; + dsprframe_t *s_psprframe; + + + s_psprite = (dsprite_t *)currentmodel->extradata; + currententity->frame %= s_psprite->numframes; + + s_psprframe = &s_psprite->frames[currententity->frame]; + + r_polydesc.pixels = currentmodel->skins[currententity->frame]->pixels[0]; + r_polydesc.pixel_width = s_psprframe->width; + r_polydesc.pixel_height = s_psprframe->height; + r_polydesc.dist = 0; + + // generate the sprite's axes, completely parallel to the viewplane. + VectorCopy (vup, r_polydesc.vup); + VectorCopy (vright, r_polydesc.vright); + VectorCopy (vpn, r_polydesc.vpn); + + // build the sprite poster in worldspace + VectorScale (r_polydesc.vright, + s_psprframe->width - s_psprframe->origin_x, right); + VectorScale (r_polydesc.vup, + s_psprframe->height - s_psprframe->origin_y, up); + VectorScale (r_polydesc.vright, + -s_psprframe->origin_x, left); + VectorScale (r_polydesc.vup, + -s_psprframe->origin_y, down); + + // invert UP vector for sprites + VectorInverse( r_polydesc.vup ); + + pverts = r_clip_verts[0]; + + pverts[0][0] = r_entorigin[0] + up[0] + left[0]; + pverts[0][1] = r_entorigin[1] + up[1] + left[1]; + pverts[0][2] = r_entorigin[2] + up[2] + left[2]; + pverts[0][3] = 0; + pverts[0][4] = 0; + + pverts[1][0] = r_entorigin[0] + up[0] + right[0]; + pverts[1][1] = r_entorigin[1] + up[1] + right[1]; + pverts[1][2] = r_entorigin[2] + up[2] + right[2]; + pverts[1][3] = s_psprframe->width; + pverts[1][4] = 0; + + pverts[2][0] = r_entorigin[0] + down[0] + right[0]; + pverts[2][1] = r_entorigin[1] + down[1] + right[1]; + pverts[2][2] = r_entorigin[2] + down[2] + right[2]; + pverts[2][3] = s_psprframe->width; + pverts[2][4] = s_psprframe->height; + + pverts[3][0] = r_entorigin[0] + down[0] + left[0]; + pverts[3][1] = r_entorigin[1] + down[1] + left[1]; + pverts[3][2] = r_entorigin[2] + down[2] + left[2]; + pverts[3][3] = 0; + pverts[3][4] = s_psprframe->height; + + r_polydesc.nump = 4; + r_polydesc.s_offset = ( r_polydesc.pixel_width >> 1); + r_polydesc.t_offset = ( r_polydesc.pixel_height >> 1); + VectorCopy( modelorg, r_polydesc.viewer_position ); + + r_polydesc.stipple_parity = 1; + if ( currententity->flags & RF_TRANSLUCENT ) + R_ClipAndDrawPoly ( currententity->alpha, false, true ); + else + R_ClipAndDrawPoly ( 1.0F, false, true ); + r_polydesc.stipple_parity = 0; +} diff --git a/src/client/refresh/soft/r_surf.c b/src/client/refresh/soft/r_surf.c new file mode 100644 index 00000000..620abb09 --- /dev/null +++ b/src/client/refresh/soft/r_surf.c @@ -0,0 +1,628 @@ +/* +Copyright (C) 1997-2001 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +// r_surf.c: surface-related refresh code + +#include "r_local.h" + +drawsurf_t r_drawsurf; + +int lightleft, sourcesstep, blocksize, sourcetstep; +int lightdelta, lightdeltastep; +int lightright, lightleftstep, lightrightstep, blockdivshift; +unsigned blockdivmask; +void *prowdestbase; +unsigned char *pbasesource; +int surfrowbytes; // used by ASM files +unsigned *r_lightptr; +int r_stepback; +int r_lightwidth; +int r_numhblocks, r_numvblocks; +unsigned char *r_source, *r_sourcemax; + +void R_DrawSurfaceBlock8_mip0 (void); +void R_DrawSurfaceBlock8_mip1 (void); +void R_DrawSurfaceBlock8_mip2 (void); +void R_DrawSurfaceBlock8_mip3 (void); + +static void (*surfmiptable[4])(void) = { + R_DrawSurfaceBlock8_mip0, + R_DrawSurfaceBlock8_mip1, + R_DrawSurfaceBlock8_mip2, + R_DrawSurfaceBlock8_mip3 +}; + +void R_BuildLightMap (void); +extern unsigned blocklights[1024]; // allow some very large lightmaps + +float surfscale; +qboolean r_cache_thrash; // set if surface cache is thrashing + +int sc_size; +surfcache_t *sc_rover, *sc_base; + +/* +=============== +R_TextureAnimation + +Returns the proper texture for a given time and base texture +=============== +*/ +image_t *R_TextureAnimation (mtexinfo_t *tex) +{ + int c; + + if (!tex->next) + return tex->image; + + c = currententity->frame % tex->numframes; + while (c) + { + tex = tex->next; + c--; + } + + return tex->image; +} + + +/* +=============== +R_DrawSurface +=============== +*/ +void R_DrawSurface (void) +{ + unsigned char *basetptr; + int smax, tmax, twidth; + int u; + int soffset, basetoffset, texwidth; + int horzblockstep; + unsigned char *pcolumndest; + void (*pblockdrawer)(void); + image_t *mt; + + surfrowbytes = r_drawsurf.rowbytes; + + mt = r_drawsurf.image; + + r_source = mt->pixels[r_drawsurf.surfmip]; + + // the fractional light values should range from 0 to (VID_GRADES - 1) << 16 + // from a source range of 0 - 255 + + texwidth = mt->width >> r_drawsurf.surfmip; + + blocksize = 16 >> r_drawsurf.surfmip; + blockdivshift = 4 - r_drawsurf.surfmip; + blockdivmask = (1 << blockdivshift) - 1; + + r_lightwidth = (r_drawsurf.surf->extents[0]>>4)+1; + + r_numhblocks = r_drawsurf.surfwidth >> blockdivshift; + r_numvblocks = r_drawsurf.surfheight >> blockdivshift; + + //============================== + + pblockdrawer = surfmiptable[r_drawsurf.surfmip]; + // TODO: only needs to be set when there is a display settings change + horzblockstep = blocksize; + + smax = mt->width >> r_drawsurf.surfmip; + twidth = texwidth; + tmax = mt->height >> r_drawsurf.surfmip; + sourcetstep = texwidth; + r_stepback = tmax * twidth; + + r_sourcemax = r_source + (tmax * smax); + + soffset = r_drawsurf.surf->texturemins[0]; + basetoffset = r_drawsurf.surf->texturemins[1]; + + // << 16 components are to guarantee positive values for % + soffset = ((soffset >> r_drawsurf.surfmip) + (smax << 16)) % smax; + basetptr = &r_source[((((basetoffset >> r_drawsurf.surfmip) + + (tmax << 16)) % tmax) * twidth)]; + + pcolumndest = r_drawsurf.surfdat; + + for (u=0 ; u= smax) + soffset = 0; + + pcolumndest += horzblockstep; + } +} + + +//============================================================================= + +/* +================ +R_DrawSurfaceBlock8_mip0 +================ +*/ +void R_DrawSurfaceBlock8_mip0 (void) +{ + int v, i, b, lightstep, lighttemp, light; + unsigned char pix, *psource, *prowdest; + + psource = pbasesource; + prowdest = prowdestbase; + + for (v=0 ; v> 4; + lightrightstep = (r_lightptr[1] - lightright) >> 4; + + for (i=0 ; i<16 ; i++) + { + lighttemp = lightleft - lightright; + lightstep = lighttemp >> 4; + + light = lightright; + + for (b=15; b>=0; b--) + { + pix = psource[b]; + prowdest[b] = ((unsigned char *)vid_colormap) + [(light & 0xFF00) + pix]; + light += lightstep; + } + + psource += sourcetstep; + lightright += lightrightstep; + lightleft += lightleftstep; + prowdest += surfrowbytes; + } + + if (psource >= r_sourcemax) + psource -= r_stepback; + } +} + + +/* +================ +R_DrawSurfaceBlock8_mip1 +================ +*/ +void R_DrawSurfaceBlock8_mip1 (void) +{ + int v, i, b, lightstep, lighttemp, light; + unsigned char pix, *psource, *prowdest; + + psource = pbasesource; + prowdest = prowdestbase; + + for (v=0 ; v> 3; + lightrightstep = (r_lightptr[1] - lightright) >> 3; + + for (i=0 ; i<8 ; i++) + { + lighttemp = lightleft - lightright; + lightstep = lighttemp >> 3; + + light = lightright; + + for (b=7; b>=0; b--) + { + pix = psource[b]; + prowdest[b] = ((unsigned char *)vid_colormap) + [(light & 0xFF00) + pix]; + light += lightstep; + } + + psource += sourcetstep; + lightright += lightrightstep; + lightleft += lightleftstep; + prowdest += surfrowbytes; + } + + if (psource >= r_sourcemax) + psource -= r_stepback; + } +} + + +/* +================ +R_DrawSurfaceBlock8_mip2 +================ +*/ +void R_DrawSurfaceBlock8_mip2 (void) +{ + int v, i, b, lightstep, lighttemp, light; + unsigned char pix, *psource, *prowdest; + + psource = pbasesource; + prowdest = prowdestbase; + + for (v=0 ; v> 2; + lightrightstep = (r_lightptr[1] - lightright) >> 2; + + for (i=0 ; i<4 ; i++) + { + lighttemp = lightleft - lightright; + lightstep = lighttemp >> 2; + + light = lightright; + + for (b=3; b>=0; b--) + { + pix = psource[b]; + prowdest[b] = ((unsigned char *)vid_colormap) + [(light & 0xFF00) + pix]; + light += lightstep; + } + + psource += sourcetstep; + lightright += lightrightstep; + lightleft += lightleftstep; + prowdest += surfrowbytes; + } + + if (psource >= r_sourcemax) + psource -= r_stepback; + } +} + + +/* +================ +R_DrawSurfaceBlock8_mip3 +================ +*/ +void R_DrawSurfaceBlock8_mip3 (void) +{ + int v, i, b, lightstep, lighttemp, light; + unsigned char pix, *psource, *prowdest; + + psource = pbasesource; + prowdest = prowdestbase; + + for (v=0 ; v> 1; + lightrightstep = (r_lightptr[1] - lightright) >> 1; + + for (i=0 ; i<2 ; i++) + { + lighttemp = lightleft - lightright; + lightstep = lighttemp >> 1; + + light = lightright; + + for (b=1; b>=0; b--) + { + pix = psource[b]; + prowdest[b] = ((unsigned char *)vid_colormap) + [(light & 0xFF00) + pix]; + light += lightstep; + } + + psource += sourcetstep; + lightright += lightrightstep; + lightleft += lightleftstep; + prowdest += surfrowbytes; + } + + if (psource >= r_sourcemax) + psource -= r_stepback; + } +} + + +//============================================================================ + + +/* +================ +R_InitCaches + +================ +*/ +void R_InitCaches (void) +{ + int size; + + // calculate size to allocate + if (sw_surfcacheoverride->value) + { + size = sw_surfcacheoverride->value; + } + else + { + int pix; + size = SURFCACHE_SIZE_AT_320X240; + + pix = vid.width*vid.height; + if (pix > 64000) + size += (pix-64000)*3; + } + + // round up to page size + size = (size + 8191) & ~8191; + + R_Printf(PRINT_ALL,"%ik surface cache\n", size/1024); + + sc_size = size; + sc_base = (surfcache_t *)malloc(size); + sc_rover = sc_base; + + sc_base->next = NULL; + sc_base->owner = NULL; + sc_base->size = sc_size; +} + + +/* +================== +D_FlushCaches +================== +*/ +void D_FlushCaches (void) +{ + surfcache_t *c; + + if (!sc_base) + return; + + for (c = sc_base ; c ; c = c->next) + { + if (c->owner) + *c->owner = NULL; + } + + sc_rover = sc_base; + sc_base->next = NULL; + sc_base->owner = NULL; + sc_base->size = sc_size; +} + +/* +================= +D_SCAlloc +================= +*/ +surfcache_t *D_SCAlloc (int width, int size) +{ + surfcache_t *new; + qboolean wrapped_this_time; + + if ((width < 0) || (width > 256)) + ri.Sys_Error (ERR_FATAL,"D_SCAlloc: bad cache width %d\n", width); + + if ((size <= 0) || (size > 0x10000)) + ri.Sys_Error (ERR_FATAL,"D_SCAlloc: bad cache size %d\n", size); + + // Add header size + size += ((char*)sc_base->data - (char*)sc_base); + size = (size + 3) & ~3; + if (size > sc_size) + ri.Sys_Error (ERR_FATAL,"D_SCAlloc: %i > cache size of %i",size, sc_size); + + // if there is not size bytes after the rover, reset to the start + wrapped_this_time = false; + + if ( !sc_rover || (byte *)sc_rover - (byte *)sc_base > sc_size - size) + { + if (sc_rover) + { + wrapped_this_time = true; + } + sc_rover = sc_base; + } + + // colect and free surfcache_t blocks until the rover block is large enough + new = sc_rover; + if (sc_rover->owner) + *sc_rover->owner = NULL; + + while (new->size < size) + { + // free another + sc_rover = sc_rover->next; + if (!sc_rover) + ri.Sys_Error (ERR_FATAL,"D_SCAlloc: hit the end of memory"); + if (sc_rover->owner) + *sc_rover->owner = NULL; + + new->size += sc_rover->size; + new->next = sc_rover->next; + } + + // create a fragment out of any leftovers + if (new->size - size > 256) + { + sc_rover = (surfcache_t *)( (byte *)new + size); + sc_rover->size = new->size - size; + sc_rover->next = new->next; + sc_rover->width = 0; + sc_rover->owner = NULL; + new->next = sc_rover; + new->size = size; + } + else + sc_rover = new->next; + + new->width = width; + // DEBUG + if (width > 0) + new->height = (size - sizeof(*new) + sizeof(new->data)) / width; + + new->owner = NULL; // should be set properly after return + + if (d_roverwrapped) + { + if (wrapped_this_time || (sc_rover >= d_initial_rover)) + r_cache_thrash = true; + } + else if (wrapped_this_time) + { + d_roverwrapped = true; + } + + return new; +} + +//============================================================================= + +// if the num is not a power of 2, assume it will not repeat + +int MaskForNum (int num) +{ + if (num==128) + return 127; + if (num==64) + return 63; + if (num==32) + return 31; + if (num==16) + return 15; + return 255; +} + +int D_log2 (int num) +{ + int c; + + c = 0; + + while (num>>=1) + c++; + return c; +} + +//============================================================================= + +/* +================ +D_CacheSurface +================ +*/ +surfcache_t *D_CacheSurface (msurface_t *surface, int miplevel) +{ + surfcache_t *cache; + + // + // if the surface is animating or flashing, flush the cache + // + r_drawsurf.image = R_TextureAnimation (surface->texinfo); + r_drawsurf.lightadj[0] = r_newrefdef.lightstyles[surface->styles[0]].white*128; + r_drawsurf.lightadj[1] = r_newrefdef.lightstyles[surface->styles[1]].white*128; + r_drawsurf.lightadj[2] = r_newrefdef.lightstyles[surface->styles[2]].white*128; + r_drawsurf.lightadj[3] = r_newrefdef.lightstyles[surface->styles[3]].white*128; + + // + // see if the cache holds apropriate data + // + cache = surface->cachespots[miplevel]; + + if (cache && !cache->dlight && surface->dlightframe != r_framecount + && cache->image == r_drawsurf.image + && cache->lightadj[0] == r_drawsurf.lightadj[0] + && cache->lightadj[1] == r_drawsurf.lightadj[1] + && cache->lightadj[2] == r_drawsurf.lightadj[2] + && cache->lightadj[3] == r_drawsurf.lightadj[3] ) + return cache; + + // + // determine shape of surface + // + surfscale = 1.0 / (1<extents[0] >> miplevel; + r_drawsurf.rowbytes = r_drawsurf.surfwidth; + r_drawsurf.surfheight = surface->extents[1] >> miplevel; + + // + // allocate memory if needed + // + if (!cache) // if a texture just animated, don't reallocate it + { + cache = D_SCAlloc (r_drawsurf.surfwidth, + r_drawsurf.surfwidth * r_drawsurf.surfheight); + surface->cachespots[miplevel] = cache; + cache->owner = &surface->cachespots[miplevel]; + cache->mipscale = surfscale; + } + + if (surface->dlightframe == r_framecount) + cache->dlight = 1; + else + cache->dlight = 0; + + r_drawsurf.surfdat = (pixel_t *)cache->data; + + cache->image = r_drawsurf.image; + cache->lightadj[0] = r_drawsurf.lightadj[0]; + cache->lightadj[1] = r_drawsurf.lightadj[1]; + cache->lightadj[2] = r_drawsurf.lightadj[2]; + cache->lightadj[3] = r_drawsurf.lightadj[3]; + + // + // draw and light the surface texture + // + r_drawsurf.surf = surface; + + c_surf++; + + // calculate the lightings + R_BuildLightMap (); + + // rasterize the surface into the cache + R_DrawSurface (); + + return cache; +} diff --git a/src/client/sound/sound.c b/src/client/sound/sound.c index 36d384d6..ca937b2d 100644 --- a/src/client/sound/sound.c +++ b/src/client/sound/sound.c @@ -290,7 +290,7 @@ S_RegisterSound(char *name) { sfx_t *sfx; - if (!sound_started) + if (sound_started == SS_NOT) { return NULL; } @@ -310,8 +310,6 @@ struct sfx_s * S_RegisterSexedSound(entity_state_t *ent, char *base) { int n; - char *p; - int len; struct sfx_s *sfx; char model[MAX_QPATH]; char sexedFilename[MAX_QPATH]; @@ -323,6 +321,7 @@ S_RegisterSexedSound(entity_state_t *ent, char *base) if (cl.configstrings[n][0]) { + char *p; p = strchr(cl.configstrings[n], '\\'); if (p) @@ -351,6 +350,8 @@ S_RegisterSexedSound(entity_state_t *ent, char *base) if (!sfx) { + int len; + /* no, so see if it exists */ len = FS_LoadFile(&sexedFilename[1], NULL); @@ -622,7 +623,7 @@ S_StartSound(vec3_t origin, int entnum, int entchannel, sfx_t *sfx, sfxcache_t *sc; playsound_t *ps, *sort; - if (!sound_started) + if (sound_started == SS_NOT) { return; } @@ -724,7 +725,7 @@ S_StartLocalSound(char *sound) { sfx_t *sfx; - if (!sound_started) + if (sound_started == SS_NOT) { return; } @@ -748,7 +749,7 @@ S_StopAllSounds(void) { int i; - if (!sound_started) + if (sound_started == SS_NOT) { return; } @@ -828,7 +829,7 @@ void S_RawSamples(int samples, int rate, int width, int channels, byte *data, float volume) { - if (!sound_started) + if (sound_started == SS_NOT) { return; } @@ -862,7 +863,7 @@ void S_Update(vec3_t origin, vec3_t forward, vec3_t right, vec3_t up) { - if (!sound_started) + if (sound_started == SS_NOT) { return; } @@ -984,7 +985,7 @@ S_SoundList(void) void S_SoundInfo_f(void) { - if (!sound_started) + if (sound_started == SS_NOT) { Com_Printf("Sound system not started\n"); return; @@ -1086,7 +1087,7 @@ S_Shutdown(void) int i; sfx_t *sfx; - if (!sound_started) + if (sound_started == SS_NOT) { return; } diff --git a/src/client/sound/wave.c b/src/client/sound/wave.c index d5f6344b..f770e5a5 100644 --- a/src/client/sound/wave.c +++ b/src/client/sound/wave.c @@ -100,7 +100,6 @@ wavinfo_t GetWavinfo(char *name, byte *wav, int wavlength) { wavinfo_t info; - int i; int format; int samples; @@ -156,7 +155,7 @@ GetWavinfo(char *name, byte *wav, int wavlength) data_p += 32; info.loopstart = GetLittleLong(); - /* if the next chunk is a LIST chunk, + /* if the next chunk is a LIST chunk, look for a cue length marker */ FindNextChunk("LIST"); @@ -165,7 +164,9 @@ GetWavinfo(char *name, byte *wav, int wavlength) if (((data_p - wav) + 32 <= wavlength) && !strncmp((const char *)data_p + 28, "mark", 4)) { - /* this is not a proper parse, + int i; + + /* this is not a proper parse, but it works with cooledit... */ data_p += 24; i = GetLittleLong(); /* samples in loop */ diff --git a/src/game/g_target.c b/src/game/g_target.c index c1acac40..f728c31f 100644 --- a/src/game/g_target.c +++ b/src/game/g_target.c @@ -373,9 +373,13 @@ target_explosion_explode(edict_t *self) void use_target_explosion(edict_t *self, edict_t *other /* unused */, edict_t *activator) { + if (!self) + { + return; + } self->activator = activator; - if (!self || !activator) + if (!activator) { return; } diff --git a/src/game/player/client.c b/src/game/player/client.c index baaeb4cf..7981e28f 100644 --- a/src/game/player/client.c +++ b/src/game/player/client.c @@ -488,7 +488,7 @@ ClientObituary(edict_t *self, edict_t *inflictor /* unused */, return; } - if (coop->value && attacker->client) + if (coop->value && attacker && attacker->client) { meansOfDeath |= MOD_FRIENDLY_FIRE; } diff --git a/src/server/sv_world.c b/src/server/sv_world.c index ec9c6583..1b7bdce9 100644 --- a/src/server/sv_world.c +++ b/src/server/sv_world.c @@ -227,7 +227,7 @@ SV_LinkEdict(edict_t *ent) /* set the abs box */ if ((ent->solid == SOLID_BSP) && - (ent->s.angles[0] || ent->s.angles[1] || + (ent->s.angles[0] || ent->s.angles[1] || ent->s.angles[2])) { /* expand for rotation */ @@ -527,18 +527,18 @@ typedef struct /* * Returns a headnode that can be used for testing or clipping an - * object of mins/maxs size. Offset is filled in to contain the + * object of mins/maxs size. Offset is filled in to contain the * adjustment that must be added to the testing object's origin * to get a point to use with the returned hull. */ int SV_HullForEntity(edict_t *ent) { - cmodel_t *model; - /* decide which clipping hull to use, based on the size */ if (ent->solid == SOLID_BSP) { + cmodel_t *model; + /* explicit hulls in the BSP model */ model = sv.models[ent->s.modelindex];