From d3f412521459f6c93458df1a01f70a84c99ebb2f Mon Sep 17 00:00:00 2001 From: VVD Date: Mon, 18 Oct 2004 17:44:52 +0000 Subject: [PATCH] This commit was generated by cvs2svn to compensate for changes in r30, which included commits to RCS files with non-trunk default branches. --- Makefile | 711 +++- source/adivtab.h | 2148 ++++++------ source/anorm_dots.h | 74 +- source/anorms.h | 362 +- source/asm_draw.h | 302 +- source/asm_i386.h | 194 +- source/block16.h | 246 +- source/block8.h | 248 +- source/bothdefs.h | 264 +- source/bspfile.h | 550 +-- source/cd_linux.c | 832 ++--- source/cd_null.c | 60 +- source/cd_win.c | 958 +++--- source/cdaudio.h | 54 +- source/cl_cam.c | 1334 ++++---- source/cl_cmd.c | 1601 ++++----- source/cl_demo.c | 2497 +++++++------- source/cl_ents.c | 3008 ++++++++--------- source/cl_input.c | 1286 +++---- source/cl_main.c | 2316 ++++++------- source/cl_math.s | 210 +- source/cl_parse.c | 3451 ++++++++++--------- source/cl_pred.c | 606 ++-- source/cl_slist.c | 498 +-- source/cl_slist.h | 106 +- source/cl_tent.c | 1064 +++--- source/client.h | 1140 +++---- source/cmd.c | 2338 ++++++------- source/cmd.h | 312 +- source/common.c | 4201 +++++++++++------------ source/common.h | 416 +-- source/console.c | 1446 ++++---- source/console.h | 122 +- source/crc.c | 186 +- source/crc.h | 50 +- source/cvar.c | 1105 +++--- source/cvar.h | 224 +- source/d_copy.s | 342 +- source/d_draw.s | 2086 ++++++------ source/d_draw16.s | 1960 +++++------ source/d_edge.c | 682 ++-- source/d_fill.c | 176 +- source/d_iface.h | 460 +-- source/d_ifacea.h | 196 +- source/d_init.c | 348 +- source/d_local.h | 222 +- source/d_modech.c | 214 +- source/d_part.c | 414 +-- source/d_parta.s | 966 +++--- source/d_polysa.s | 3500 +++++++++---------- source/d_polyse.c | 2128 ++++++------ source/d_scan.c | 892 ++--- source/d_scana.s | 190 +- source/d_sky.c | 264 +- source/d_spr8.s | 1812 +++++----- source/d_sprite.c | 884 ++--- source/d_surf.c | 694 ++-- source/d_vars.c | 100 +- source/d_varsa.s | 436 +-- source/d_zpoint.c | 94 +- source/draw.c | 2052 +++++------ source/draw.h | 90 +- source/gl_draw.c | 3010 ++++++++--------- source/gl_mesh.c | 732 ++-- source/gl_model.c | 3850 ++++++++++----------- source/gl_model.h | 890 ++--- source/gl_ngraph.c | 282 +- source/gl_refrag.c | 460 +-- source/gl_rlight.c | 768 ++--- source/gl_rmain.c | 2482 +++++++------- source/gl_rmisc.c | 976 +++--- source/gl_rsurf.c | 3563 ++++++++++---------- source/gl_screen.c | 2558 +++++++------- source/gl_test.c | 364 +- source/gl_vidlinux.c | 1628 ++++----- source/gl_vidlinux_svga.c | 1704 +++++----- source/gl_vidlinux_x11.c | 1734 +++++----- source/gl_vidlinuxglx.c | 1574 ++++----- source/gl_warp.c | 2174 ++++++------ source/gl_warp_sin.h | 102 +- source/glquake.h | 572 ++-- source/in_null.c | 58 +- source/in_win.c | 2630 +++++++-------- source/input.h | 68 +- source/keys.c | 2038 +++++------ source/keys.h | 336 +- source/math.s | 714 ++-- source/mathlib.c | 1166 +++---- source/mathlib.h | 192 +- source/mdfour.c | 448 +-- source/mdfour.h | 108 +- source/menu.c | 5057 +++++++++++++-------------- source/menu.h | 82 +- source/model.c | 3808 ++++++++++----------- source/model.h | 816 ++--- source/modelgen.h | 268 +- source/net.h | 226 +- source/net_chan.c | 908 ++--- source/net_udp.c | 586 ++-- source/net_wins.c | 744 ++-- source/nonintel.c | 124 +- source/pmove.c | 1952 +++++------ source/pmove.h | 210 +- source/pmovetst.c | 964 +++--- source/pr_cmds.c | 4210 +++++++++++++---------- source/pr_comp.h | 360 +- source/pr_edict.c | 2249 +++++++------ source/pr_exec.c | 1427 ++++---- source/progdefs.h | 318 +- source/progs.h | 297 +- source/protocol.h | 612 ++-- source/quakeasm.h | 542 +-- source/quakedef.h | 280 +- source/qwsv.dsp | 8 +- source/qwsvdef.h | 217 +- source/r_aclip.c | 700 ++-- source/r_aclipa.s | 444 +-- source/r_alias.c | 1536 ++++----- source/r_aliasa.s | 486 +-- source/r_bsp.c | 1348 ++++---- source/r_draw.c | 1816 +++++----- source/r_drawa.s | 1688 +++++----- source/r_edge.c | 1542 ++++----- source/r_edgea.s | 1512 ++++----- source/r_efrag.c | 550 +-- source/r_light.c | 520 +-- source/r_local.h | 632 ++-- source/r_main.c | 2234 ++++++------ source/r_misc.c | 1198 +++---- source/r_part.c | 1234 +++---- source/r_shared.h | 308 +- source/r_sky.c | 560 +-- source/r_sprite.c | 802 ++--- source/r_surf.c | 1622 ++++----- source/r_vars.c | 78 +- source/r_varsa.s | 138 +- source/render.h | 306 +- source/resource.h | 46 +- source/savegame.c | 592 ++-- source/sbar.c | 2806 +++++++-------- source/sbar.h | 78 +- source/screen.c | 2580 +++++++------- source/screen.h | 106 +- source/server.h | 1173 ++++--- source/skin.c | 672 ++-- source/snd_dma.c | 2012 +++++------ source/snd_linux.c | 504 +-- source/snd_mem.c | 688 ++-- source/snd_mix.c | 798 ++--- source/snd_mixa.s | 458 +-- source/snd_win.c | 1458 ++++---- source/sound.h | 348 +- source/spritegn.h | 220 +- source/surf16.s | 356 +- source/surf8.s | 1578 ++++----- source/sv_ccmds.c | 1978 +++++------ source/sv_demo.c | 2756 ++++++++------- source/sv_ents.c | 1245 ++++--- source/sv_init.c | 898 ++--- source/sv_login.c | 590 ++++ source/sv_main.c | 4262 ++++++++++++----------- source/sv_math.s | 662 ++-- source/sv_model.c | 2274 ++++++------- source/sv_move.c | 838 ++--- source/sv_nchan.c | 330 +- source/sv_phys.c | 1933 +++++------ source/sv_send.c | 2151 ++++++------ source/sv_sys_unix.c | 816 +++-- source/sv_sys_win.c | 944 +++--- source/sv_user.c | 3726 ++++++++++---------- source/sys.h | 188 +- source/sys_linux.c | 852 ++--- source/sys_null.c | 246 +- source/sys_win.c | 1098 +++--- source/sys_x86.s | 216 +- source/teamplay.c | 3506 +++++++++---------- source/teamplay.h | 138 +- source/test.bat | 2 + source/version.c | 156 +- source/version.h | 51 +- source/vid.h | 194 +- source/vid_null.c | 136 +- source/vid_svgalib.c | 2046 +++++------ source/vid_wgl.c | 3948 +++++++++++----------- source/vid_win.c | 6746 ++++++++++++++++++------------------- source/vid_x.c | 2228 ++++++------ source/view.c | 2403 ++++++------- source/view.h | 68 +- source/wad.c | 316 +- source/wad.h | 160 +- source/winquake.h | 228 +- source/world.c | 1848 +++++----- source/world.h | 186 +- source/worlda.s | 288 +- source/zone.c | 1928 +++++------ source/zone.h | 266 +- 196 files changed, 108417 insertions(+), 105323 deletions(-) create mode 100755 source/sv_login.c create mode 100755 source/test.bat diff --git a/Makefile b/Makefile index 2d1a7e1f..cbc6463e 100644 --- a/Makefile +++ b/Makefile @@ -34,6 +34,8 @@ SERVER_DIR=$(MAINDIR)/source MESA_DIR=/usr/local/src/Mesa-3.0 + + CC=gcc BASE_CFLAGS=-Wall -Dstricmp=strcasecmp -D_snprintf=snprintf -I$(CLIENT_DIR) -I$(SERVER_DIR) DEBUG_CFLAGS=$(BASE_CFLAGS) -g @@ -45,15 +47,23 @@ RELEASE_CFLAGS=$(BASE_CFLAGS) -m486 -O6 -ffast-math -funroll-loops \ -fomit-frame-pointer -fexpensive-optimizations -malign-loops=2 \ -malign-jumps=2 -malign-functions=2 endif +GLCFLAGS=-DGLQUAKE -I/usr/local/src/Mesa-3.0/include -I/usr/include/glide LDFLAGS=-lm +SVGALDFLAGS=-lvga XLDFLAGS=-L/usr/X11R6/lib -lX11 -lXext +GL_SVGA_LDFLAGS=-L/usr/X11/lib -L/usr/X11R6/lib -L/usr/local/src/Mesa-3.0/lib -lm -lGL -lglide3 -lX11 -lXext -lvga +GL_X11_LDFLAGS=-L/usr/X11R6/lib -L/usr/local/src/Mesa-3.0/lib -lm -lGL -lX11 -lXext + + DO_CC=$(CC) -Did386 $(CFLAGS) -o $@ -c $< DO_O_CC=$(CC) -O $(CFLAGS) -o $@ -c $< +DO_GL_CC=$(CC) $(CFLAGS) $(GLCFLAGS) -o $@ -c $< DO_SERVER_CC=$(CC) -Did386 -DSERVERONLY $(CFLAGS) -o $@ -c $< DO_AS=$(CC) -Did386 $(CFLAGS) -DELF -x assembler-with-cpp -o $@ -c $< +DO_GL_AS=$(CC) $(CFLAGS) $(GLCFLAGS) -DELF -x assembler-with-cpp -o $@ -c $< ############################################################################# # SETUP AND BUILD @@ -62,29 +72,46 @@ DO_AS=$(CC) -Did386 $(CFLAGS) -DELF -x assembler-with-cpp -o $@ -c $< ifeq ($(ARCH),axp) TARGETS=$(BUILDDIR)/mvdsv else -TARGETS=$(BUILDDIR)/mvdsv +TARGETS=$(BUILDDIR)/mvdsv $(BUILDDIR)/qwplayer $(BUILDDIR)/qwplayer.x11 $(BUILDDIR)/qwplayer-gl.x11 endif build_debug: @-mkdir $(BUILD_DEBUG_DIR) \ + $(BUILD_DEBUG_DIR)/client \ + $(BUILD_DEBUG_DIR)/glclient \ $(BUILD_DEBUG_DIR)/server $(MAKE) targets BUILDDIR=$(BUILD_DEBUG_DIR) CFLAGS="$(DEBUG_CFLAGS)" build_release: @-mkdir $(BUILD_RELEASE_DIR) \ + $(BUILD_RELEASE_DIR)/client \ + $(BUILD_RELEASE_DIR)/glclient \ $(BUILD_RELEASE_DIR)/server $(MAKE) targets BUILDDIR=$(BUILD_RELEASE_DIR) CFLAGS="$(RELEASE_CFLAGS)" +build_gl: + @-mkdir $(BUILD_RELEASE_DIR) \ + $(BUILD_RELEASE_DIR)/glclient + $(MAKE) tar_gl BUILDDIR=$(BUILD_RELEASE_DIR) CFLAGS="$(RELEASE_CFLAGS)" + +build_sv: + @-mkdir $(BUILD_RELEASE_DIR) \ + $(BUILD_RELEASE_DIR)/server + $(MAKE) tar_sv BUILDDIR=$(BUILD_RELEASE_DIR) CFLAGS="$(RELEASE_CFLAGS)" + all: build_debug build_release targets: $(TARGETS) +tar_gl: $(BUILDDIR)/qwplayer-gl.x11 +tar_sv: $(BUILDDIR)/mvdsv ############################################################################# # SERVER ############################################################################# QWSV_OBJS = \ - $(BUILDDIR)/server/sv_demo.o \ + $(BUILDDIR)/server/sv_demo.o \ + $(BUILDDIR)/server/sv_login.o \ $(BUILDDIR)/server/pr_cmds.o \ $(BUILDDIR)/server/pr_edict.o \ $(BUILDDIR)/server/pr_exec.o \ @@ -122,6 +149,8 @@ $(BUILDDIR)/mvdsv : $(QWSV_OBJS) $(BUILDDIR)/server/sv_demo.o : $(SERVER_DIR)/sv_demo.c $(DO_SERVER_CC) +$(BUILDDIR)/server/sv_login.o : $(SERVER_DIR)/sv_login.c + $(DO_SERVER_CC) $(BUILDDIR)/server/pr_cmds.o : $(SERVER_DIR)/pr_cmds.c $(DO_SERVER_CC) @@ -213,93 +242,611 @@ $(BUILDDIR)/server/math.o : $(CLIENT_DIR)/math.s $(BUILDDIR)/server/sys_x86.o : $(CLIENT_DIR)/sys_x86.s $(DO_AS) - ############################################################################# -# RPM +# CLIENT ############################################################################# -# Make RPMs. You need to be root to make this work -RPMROOT=/usr/src/redhat -RPM = rpm -RPMFLAGS = -bb -INSTALLDIR = /usr/local/games/quake - -rpm: rpm-qwsv -tar: tar-qwsv - -QWCL_RPMDIR=/var/tmp/qwcl-$(VERSION) -TDFXGL_DIR=/home/zoid/3dfxgl - -QWPROGS = \ - $(MAINDIR)/progs/buttons.qc \ - $(MAINDIR)/progs/client.qc \ - $(MAINDIR)/progs/combat.qc \ - $(MAINDIR)/progs/defs.qc \ - $(MAINDIR)/progs/doors.qc \ - $(MAINDIR)/progs/items.qc \ - $(MAINDIR)/progs/misc.qc \ - $(MAINDIR)/progs/models.qc \ - $(MAINDIR)/progs/plats.qc \ - $(MAINDIR)/progs/player.qc \ - $(MAINDIR)/progs/progdefs.h \ - $(MAINDIR)/progs/progs.src \ - $(MAINDIR)/progs/qwprogs.dat \ - $(MAINDIR)/progs/server.qc \ - $(MAINDIR)/progs/spectate.qc \ - $(MAINDIR)/progs/sprites.qc \ - $(MAINDIR)/progs/subs.qc \ - $(MAINDIR)/progs/triggers.qc \ - $(MAINDIR)/progs/weapons.qc \ - $(MAINDIR)/progs/world.qc - -QWSV_RPMDIR=/var/tmp/qwsv-$(VERSION) - -rpm-qwsv: qwsv.spec $(BUILD_RELEASE_DIR)/mvdsv $(QWPROGS) - touch $(RPMROOT)/SOURCES/qwsv-$(VERSION).tar.gz - if [ ! -d archives ];then mkdir archives;fi - $(MAKE) copyfiles-qwsv DESTDIR=$(QWSV_RPMDIR)/$(INSTALLDIR) - cp qwsv.spec $(RPMROOT)/SPECS/qwsv.spec - cp $(MAINDIR)/quake.gif $(RPMROOT)/SOURCES/quake.gif - cd $(RPMROOT)/SPECS; $(RPM) $(RPMFLAGS) qwsv.spec - cp $(RPMROOT)/RPMS/$(RPMARCH)/qwsv-$(VERSION)-$(RPM_RELEASE).$(RPMARCH).rpm archives/. - rm -rf $(QWSV_RPMDIR) - -tar-qwsv: $(BUILD_RELEASE_DIR)/mvdsv $(QWPROGS) - if [ ! -d archives ];then mkdir archives;fi - $(MAKE) copyfiles-qwsv DESTDIR=$(QWSV_RPMDIR)/$(INSTALLDIR) - cd $(QWSV_RPMDIR)/$(INSTALLDIR); tar czvf qwsv-$(VERSION)-$(RPMARCH)-unknown-linux2.0.tar.gz * - mv $(QWSV_RPMDIR)/$(INSTALLDIR)/*.tar.gz archives/. - rm -rf $(QWSV_RPMDIR) - -copyfiles-qwsv: - -mkdirhier $(DESTDIR) - -mkdirhier $(DESTDIR)/qw - -mkdirhier $(DESTDIR)/qw/skins - cp $(BUILD_RELEASE_DIR)/mvdsv $(DESTDIR)/. - strip $(DESTDIR)/mvdsv - chmod 755 $(DESTDIR)/mvdsv - cp $(MAINDIR)/docs/README.qwsv $(DESTDIR)/. - chmod 644 $(DESTDIR)/README.qwsv - cp $(QWPROGS) $(DESTDIR)/qw/. - cd $(DESTDIR)/qw; chmod 644 * - chmod 755 $(DESTDIR)/qw/skins - cp $(MAINDIR)/fixskins.sh $(DESTDIR)/qw/skins/. - chmod 755 $(DESTDIR)/qw/skins/fixskins.sh - -qwsv.spec : $(MAINDIR)/qwsv.spec.sh $(BUILD_RELEASE_DIR)/mvdsv - sh $< $(VERSION) $(RPM_RELEASE) $(INSTALLDIR) > $@ +QWCL_OBJS = \ + $(BUILDDIR)/client/cl_demo.o \ + $(BUILDDIR)/client/cl_ents.o \ + $(BUILDDIR)/client/cl_cmd.o \ + $(BUILDDIR)/client/cl_input.o \ + $(BUILDDIR)/client/cl_main.o \ + $(BUILDDIR)/client/cl_parse.o \ + $(BUILDDIR)/client/cl_pred.o \ + $(BUILDDIR)/client/cl_tent.o \ + $(BUILDDIR)/client/cl_cam.o \ + $(BUILDDIR)/client/cmd.o \ + $(BUILDDIR)/client/common.o \ + $(BUILDDIR)/client/console.o \ + $(BUILDDIR)/client/crc.o \ + $(BUILDDIR)/client/cvar.o \ + $(BUILDDIR)/client/d_edge.o \ + $(BUILDDIR)/client/d_fill.o \ + $(BUILDDIR)/client/d_init.o \ + $(BUILDDIR)/client/d_modech.o \ + $(BUILDDIR)/client/d_part.o \ + $(BUILDDIR)/client/d_polyse.o \ + $(BUILDDIR)/client/d_scan.o \ + $(BUILDDIR)/client/d_sky.o \ + $(BUILDDIR)/client/d_sprite.o \ + $(BUILDDIR)/client/d_surf.o \ + $(BUILDDIR)/client/d_vars.o \ + $(BUILDDIR)/client/d_zpoint.o \ + $(BUILDDIR)/client/draw.o \ + $(BUILDDIR)/client/keys.o \ + $(BUILDDIR)/client/mathlib.o \ + $(BUILDDIR)/client/mdfour.o \ + $(BUILDDIR)/client/menu.o \ + $(BUILDDIR)/client/model.o \ + $(BUILDDIR)/client/net_chan.o \ + $(BUILDDIR)/client/net_udp.o \ + $(BUILDDIR)/client/nonintel.o \ + $(BUILDDIR)/client/pmove.o \ + $(BUILDDIR)/client/pmovetst.o \ + $(BUILDDIR)/client/r_aclip.o \ + $(BUILDDIR)/client/r_alias.o \ + $(BUILDDIR)/client/r_bsp.o \ + $(BUILDDIR)/client/r_draw.o \ + $(BUILDDIR)/client/r_edge.o \ + $(BUILDDIR)/client/r_efrag.o \ + $(BUILDDIR)/client/r_light.o \ + $(BUILDDIR)/client/r_main.o \ + $(BUILDDIR)/client/r_misc.o \ + $(BUILDDIR)/client/r_part.o \ + $(BUILDDIR)/client/r_sky.o \ + $(BUILDDIR)/client/r_sprite.o \ + $(BUILDDIR)/client/r_surf.o \ + $(BUILDDIR)/client/r_vars.o \ + $(BUILDDIR)/client/sbar.o \ + $(BUILDDIR)/client/screen.o \ + $(BUILDDIR)/client/skin.o \ + $(BUILDDIR)/client/snd_dma.o \ + $(BUILDDIR)/client/snd_mem.o \ + $(BUILDDIR)/client/snd_mix.o \ + $(BUILDDIR)/client/view.o \ + $(BUILDDIR)/client/wad.o \ + $(BUILDDIR)/client/zone.o \ + $(BUILDDIR)/client/cd_linux.o \ + $(BUILDDIR)/client/sys_linux.o \ + $(BUILDDIR)/client/snd_linux.o \ + $(BUILDDIR)/client/version.o \ + $(BUILDDIR)/client/teamplay.o \ + $(BUILDDIR)/client/cl_slist.o \ + +ifeq ($(ARCH),i386) + QWCL_AS_OBJS = \ + $(BUILDDIR)/client/cl_math.o \ + $(BUILDDIR)/client/d_copy.o \ + $(BUILDDIR)/client/d_draw.o \ + $(BUILDDIR)/client/d_draw16.o \ + $(BUILDDIR)/client/d_parta.o \ + $(BUILDDIR)/client/d_polysa.o \ + $(BUILDDIR)/client/d_scana.o \ + $(BUILDDIR)/client/d_spr8.o \ + $(BUILDDIR)/client/d_varsa.o \ + $(BUILDDIR)/client/math.o \ + $(BUILDDIR)/client/r_aclipa.o \ + $(BUILDDIR)/client/r_aliasa.o \ + $(BUILDDIR)/client/r_drawa.o \ + $(BUILDDIR)/client/r_edgea.o \ + $(BUILDDIR)/client/r_varsa.o \ + $(BUILDDIR)/client/snd_mixa.o \ + $(BUILDDIR)/client/surf16.o \ + $(BUILDDIR)/client/surf8.o \ + $(BUILDDIR)/client/sys_x86.o +else + QWCL_AS_OBJS= +endif + +QWCL_SVGA_OBJS = $(BUILDDIR)/client/vid_svgalib.o +QWCL_X11_OBJS = $(BUILDDIR)/client/vid_x.o + +$(BUILDDIR)/qwplayer : $(QWCL_OBJS) $(QWCL_AS_OBJS) $(QWCL_SVGA_OBJS) + $(CC) $(CFLAGS) -o $@ $(QWCL_OBJS) $(QWCL_AS_OBJS) $(QWCL_SVGA_OBJS) \ + $(LDFLAGS) $(SVGALDFLAGS) + +$(BUILDDIR)/qwplayer.x11 : $(QWCL_OBJS) $(QWCL_AS_OBJS) $(QWCL_X11_OBJS) + $(CC) $(CFLAGS) -o $@ $(QWCL_OBJS) $(QWCL_AS_OBJS) $(QWCL_X11_OBJS) \ + $(LDFLAGS) $(XLDFLAGS) + +$(BUILDDIR)/client/cl_demo.o : $(CLIENT_DIR)/cl_demo.c + $(DO_CC) + +$(BUILDDIR)/client/cl_ents.o : $(CLIENT_DIR)/cl_ents.c + $(DO_CC) + +$(BUILDDIR)/client/cl_input.o : $(CLIENT_DIR)/cl_input.c + $(DO_CC) + +$(BUILDDIR)/client/cl_main.o : $(CLIENT_DIR)/cl_main.c + $(DO_CC) + +$(BUILDDIR)/client/cl_parse.o : $(CLIENT_DIR)/cl_parse.c + $(DO_CC) + +$(BUILDDIR)/client/cl_pred.o : $(CLIENT_DIR)/cl_pred.c + $(DO_CC) + +$(BUILDDIR)/client/cl_tent.o : $(CLIENT_DIR)/cl_tent.c + $(DO_CC) + +$(BUILDDIR)/client/cl_cam.o : $(CLIENT_DIR)/cl_cam.c + $(DO_CC) + +$(BUILDDIR)/client/cl_cmd.o : $(CLIENT_DIR)/cl_cmd.c + $(DO_CC) + +$(BUILDDIR)/client/cmd.o : $(CLIENT_DIR)/cmd.c + $(DO_CC) + +$(BUILDDIR)/client/common.o : $(CLIENT_DIR)/common.c + $(DO_CC) + +$(BUILDDIR)/client/console.o : $(CLIENT_DIR)/console.c + $(DO_CC) + +$(BUILDDIR)/client/crc.o : $(CLIENT_DIR)/crc.c + $(DO_CC) + +$(BUILDDIR)/client/cvar.o : $(CLIENT_DIR)/cvar.c + $(DO_CC) + +$(BUILDDIR)/client/d_edge.o : $(CLIENT_DIR)/d_edge.c + $(DO_CC) + +$(BUILDDIR)/client/d_fill.o : $(CLIENT_DIR)/d_fill.c + $(DO_CC) + +$(BUILDDIR)/client/d_init.o : $(CLIENT_DIR)/d_init.c + $(DO_CC) + +$(BUILDDIR)/client/d_modech.o : $(CLIENT_DIR)/d_modech.c + $(DO_CC) + +$(BUILDDIR)/client/d_part.o : $(CLIENT_DIR)/d_part.c + $(DO_CC) + +$(BUILDDIR)/client/d_polyse.o : $(CLIENT_DIR)/d_polyse.c + $(DO_CC) + +$(BUILDDIR)/client/d_scan.o : $(CLIENT_DIR)/d_scan.c + $(DO_CC) + +$(BUILDDIR)/client/d_sky.o : $(CLIENT_DIR)/d_sky.c + $(DO_CC) + +$(BUILDDIR)/client/d_sprite.o : $(CLIENT_DIR)/d_sprite.c + $(DO_CC) + +$(BUILDDIR)/client/d_surf.o : $(CLIENT_DIR)/d_surf.c + $(DO_CC) + +$(BUILDDIR)/client/d_vars.o : $(CLIENT_DIR)/d_vars.c + $(DO_CC) + +$(BUILDDIR)/client/d_zpoint.o : $(CLIENT_DIR)/d_zpoint.c + $(DO_CC) + +$(BUILDDIR)/client/draw.o : $(CLIENT_DIR)/draw.c + $(DO_CC) + +$(BUILDDIR)/client/keys.o : $(CLIENT_DIR)/keys.c + $(DO_CC) + +$(BUILDDIR)/client/mathlib.o : $(CLIENT_DIR)/mathlib.c + $(DO_CC) + +$(BUILDDIR)/client/mdfour.o : $(CLIENT_DIR)/mdfour.c + $(DO_CC) + +$(BUILDDIR)/client/menu.o : $(CLIENT_DIR)/menu.c + $(DO_CC) + +$(BUILDDIR)/client/model.o : $(CLIENT_DIR)/model.c + $(DO_CC) + +$(BUILDDIR)/client/net_chan.o : $(CLIENT_DIR)/net_chan.c + $(DO_CC) + +$(BUILDDIR)/client/net_udp.o : $(CLIENT_DIR)/net_udp.c + $(DO_CC) + +$(BUILDDIR)/client/nonintel.o : $(CLIENT_DIR)/nonintel.c + $(DO_CC) + +$(BUILDDIR)/client/pmove.o : $(CLIENT_DIR)/pmove.c + $(DO_CC) + +$(BUILDDIR)/client/pmovetst.o : $(CLIENT_DIR)/pmovetst.c + $(DO_CC) + +$(BUILDDIR)/client/r_aclip.o : $(CLIENT_DIR)/r_aclip.c + $(DO_CC) + +$(BUILDDIR)/client/r_alias.o : $(CLIENT_DIR)/r_alias.c + $(DO_CC) + +$(BUILDDIR)/client/r_bsp.o : $(CLIENT_DIR)/r_bsp.c + $(DO_CC) + +$(BUILDDIR)/client/r_draw.o : $(CLIENT_DIR)/r_draw.c + $(DO_CC) + +$(BUILDDIR)/client/r_edge.o : $(CLIENT_DIR)/r_edge.c + $(DO_CC) + +$(BUILDDIR)/client/r_efrag.o : $(CLIENT_DIR)/r_efrag.c + $(DO_CC) + +$(BUILDDIR)/client/r_light.o : $(CLIENT_DIR)/r_light.c + $(DO_CC) + +$(BUILDDIR)/client/r_main.o : $(CLIENT_DIR)/r_main.c + $(DO_CC) + +$(BUILDDIR)/client/r_misc.o : $(CLIENT_DIR)/r_misc.c + $(DO_CC) + +$(BUILDDIR)/client/r_part.o : $(CLIENT_DIR)/r_part.c + $(DO_CC) + +$(BUILDDIR)/client/r_sky.o : $(CLIENT_DIR)/r_sky.c + $(DO_CC) + +$(BUILDDIR)/client/r_sprite.o : $(CLIENT_DIR)/r_sprite.c + $(DO_CC) + +$(BUILDDIR)/client/r_surf.o : $(CLIENT_DIR)/r_surf.c + $(DO_CC) + +$(BUILDDIR)/client/r_vars.o : $(CLIENT_DIR)/r_vars.c + $(DO_CC) + +$(BUILDDIR)/client/sbar.o : $(CLIENT_DIR)/sbar.c + $(DO_CC) + +$(BUILDDIR)/client/screen.o : $(CLIENT_DIR)/screen.c + $(DO_CC) + +$(BUILDDIR)/client/skin.o : $(CLIENT_DIR)/skin.c + $(DO_CC) + +$(BUILDDIR)/client/snd_dma.o : $(CLIENT_DIR)/snd_dma.c + $(DO_CC) + +$(BUILDDIR)/client/snd_mem.o : $(CLIENT_DIR)/snd_mem.c + $(DO_CC) + +$(BUILDDIR)/client/snd_mix.o : $(CLIENT_DIR)/snd_mix.c + $(DO_CC) + +$(BUILDDIR)/client/view.o : $(CLIENT_DIR)/view.c + $(DO_CC) + +$(BUILDDIR)/client/wad.o : $(CLIENT_DIR)/wad.c + $(DO_CC) + +$(BUILDDIR)/client/zone.o : $(CLIENT_DIR)/zone.c + $(DO_CC) + +$(BUILDDIR)/client/cd_linux.o : $(CLIENT_DIR)/cd_linux.c + $(DO_CC) + +$(BUILDDIR)/client/sys_linux.o : $(CLIENT_DIR)/sys_linux.c + $(DO_CC) + +$(BUILDDIR)/client/snd_linux.o : $(CLIENT_DIR)/snd_linux.c + $(DO_CC) + +$(BUILDDIR)/client/version.o : $(CLIENT_DIR)/version.c + $(DO_CC) + +$(BUILDDIR)/client/teamplay.o : $(CLIENT_DIR)/teamplay.c + $(DO_CC) + +$(BUILDDIR)/client/cl_slist.o : $(CLIENT_DIR)/cl_slist.c + $(DO_CC) + +$(BUILDDIR)/client/d_copy.o : $(CLIENT_DIR)/d_copy.s + $(DO_AS) + +$(BUILDDIR)/client/d_draw.o : $(CLIENT_DIR)/d_draw.s + $(DO_AS) + +$(BUILDDIR)/client/d_draw16.o : $(CLIENT_DIR)/d_draw16.s + $(DO_AS) + +$(BUILDDIR)/client/d_parta.o : $(CLIENT_DIR)/d_parta.s + $(DO_AS) + +$(BUILDDIR)/client/d_polysa.o : $(CLIENT_DIR)/d_polysa.s + $(DO_AS) + +$(BUILDDIR)/client/d_scana.o : $(CLIENT_DIR)/d_scana.s + $(DO_AS) + +$(BUILDDIR)/client/d_spr8.o : $(CLIENT_DIR)/d_spr8.s + $(DO_AS) + +$(BUILDDIR)/client/d_varsa.o : $(CLIENT_DIR)/d_varsa.s + $(DO_AS) + +$(BUILDDIR)/client/cl_math.o : $(CLIENT_DIR)/cl_math.s + $(DO_AS) + +$(BUILDDIR)/client/math.o : $(CLIENT_DIR)/math.s + $(DO_AS) + +$(BUILDDIR)/client/r_aclipa.o : $(CLIENT_DIR)/r_aclipa.s + $(DO_AS) + +$(BUILDDIR)/client/r_aliasa.o : $(CLIENT_DIR)/r_aliasa.s + $(DO_AS) + +$(BUILDDIR)/client/r_drawa.o : $(CLIENT_DIR)/r_drawa.s + $(DO_AS) + +$(BUILDDIR)/client/r_edgea.o : $(CLIENT_DIR)/r_edgea.s + $(DO_AS) + +$(BUILDDIR)/client/r_varsa.o : $(CLIENT_DIR)/r_varsa.s + $(DO_AS) + +$(BUILDDIR)/client/snd_mixa.o : $(CLIENT_DIR)/snd_mixa.s + $(DO_AS) + +$(BUILDDIR)/client/surf16.o : $(CLIENT_DIR)/surf16.s + $(DO_AS) + +$(BUILDDIR)/client/surf8.o : $(CLIENT_DIR)/surf8.s + $(DO_AS) + +$(BUILDDIR)/client/sys_x86.o : $(CLIENT_DIR)/sys_x86.s + $(DO_AS) + +$(BUILDDIR)/client/vid_svgalib.o : $(CLIENT_DIR)/vid_svgalib.c + $(DO_O_CC) + +$(BUILDDIR)/client/vid_x.o : $(CLIENT_DIR)/vid_x.c + $(DO_CC) ############################################################################# -# MISC +# GL CLIENT ############################################################################# -clean: clean-debug clean-release +GLQWCL_OBJS = \ + $(BUILDDIR)/glclient/cl_demo.o \ + $(BUILDDIR)/glclient/cl_ents.o \ + $(BUILDDIR)/glclient/cl_cmd.o \ + $(BUILDDIR)/glclient/cl_input.o \ + $(BUILDDIR)/glclient/cl_main.o \ + $(BUILDDIR)/glclient/cl_parse.o \ + $(BUILDDIR)/glclient/cl_pred.o \ + $(BUILDDIR)/glclient/cl_tent.o \ + $(BUILDDIR)/glclient/cl_cam.o \ + $(BUILDDIR)/glclient/cmd.o \ + $(BUILDDIR)/glclient/common.o \ + $(BUILDDIR)/glclient/console.o \ + $(BUILDDIR)/glclient/crc.o \ + $(BUILDDIR)/glclient/cvar.o \ + $(BUILDDIR)/glclient/keys.o \ + $(BUILDDIR)/glclient/mathlib.o \ + $(BUILDDIR)/glclient/mdfour.o \ + $(BUILDDIR)/glclient/menu.o \ + $(BUILDDIR)/glclient/net_chan.o \ + $(BUILDDIR)/glclient/net_udp.o \ + $(BUILDDIR)/glclient/nonintel.o \ + $(BUILDDIR)/glclient/pmove.o \ + $(BUILDDIR)/glclient/pmovetst.o \ + $(BUILDDIR)/glclient/r_part.o \ + $(BUILDDIR)/glclient/sbar.o \ + $(BUILDDIR)/glclient/skin.o \ + $(BUILDDIR)/glclient/snd_dma.o \ + $(BUILDDIR)/glclient/snd_mem.o \ + $(BUILDDIR)/glclient/snd_mix.o \ + $(BUILDDIR)/glclient/view.o \ + $(BUILDDIR)/glclient/wad.o \ + $(BUILDDIR)/glclient/zone.o \ + $(BUILDDIR)/glclient/cd_linux.o \ + $(BUILDDIR)/glclient/sys_linux.o \ + $(BUILDDIR)/glclient/snd_linux.o \ + $(BUILDDIR)/glclient/version.o \ + $(BUILDDIR)/glclient/cl_slist.o \ + $(BUILDDIR)/glclient/teamplay.o \ + \ + $(BUILDDIR)/glclient/gl_draw.o \ + $(BUILDDIR)/glclient/gl_mesh.o \ + $(BUILDDIR)/glclient/gl_model.o \ + $(BUILDDIR)/glclient/gl_ngraph.o \ + $(BUILDDIR)/glclient/gl_refrag.o \ + $(BUILDDIR)/glclient/gl_rlight.o \ + $(BUILDDIR)/glclient/gl_rmain.o \ + $(BUILDDIR)/glclient/gl_rmisc.o \ + $(BUILDDIR)/glclient/gl_rsurf.o \ + $(BUILDDIR)/glclient/gl_screen.o \ + $(BUILDDIR)/glclient/gl_warp.o \ + \ +# $(BUILDDIR)/glclient/cl_math.o \ +# $(BUILDDIR)/glclient/math.o \ +# $(BUILDDIR)/glclient/snd_mixa.o \ +# $(BUILDDIR)/glclient/sys_x86.o + +GLQWCL_SVGA_OBJS = $(BUILDDIR)/glclient/gl_vidlinux.o +GLQWCL_X11_OBJS = $(BUILDDIR)/glclient/gl_vidlinuxglx.o + +$(BUILDDIR)/qwplayer-gl : $(GLQWCL_OBJS) $(GLQWCL_SVGA_OBJS) + $(CC) $(CFLAGS) -o $@ $(GLQWCL_OBJS) $(GLQWCL_SVGA_OBJS) $(LDFLAGS) $(GL_SVGA_LDFLAGS) + +$(BUILDDIR)/qwplayer-gl.x11 : $(GLQWCL_OBJS) $(GLQWCL_X11_OBJS) + $(CC) $(CFLAGS) -o $@ $(GLQWCL_OBJS) $(GLQWCL_X11_OBJS) $(LDFLAGS) $(GL_X11_LDFLAGS) + +$(BUILDDIR)/glclient/cl_demo.o : $(CLIENT_DIR)/cl_demo.c + $(DO_GL_CC) + +$(BUILDDIR)/glclient/cl_ents.o : $(CLIENT_DIR)/cl_ents.c + $(DO_GL_CC) + +$(BUILDDIR)/glclient/cl_input.o : $(CLIENT_DIR)/cl_input.c + $(DO_GL_CC) + +$(BUILDDIR)/glclient/cl_main.o : $(CLIENT_DIR)/cl_main.c + $(DO_GL_CC) + +$(BUILDDIR)/glclient/cl_parse.o : $(CLIENT_DIR)/cl_parse.c + $(DO_GL_CC) + +$(BUILDDIR)/glclient/cl_pred.o : $(CLIENT_DIR)/cl_pred.c + $(DO_GL_CC) + +$(BUILDDIR)/glclient/cl_tent.o : $(CLIENT_DIR)/cl_tent.c + $(DO_GL_CC) + +$(BUILDDIR)/glclient/cl_cam.o : $(CLIENT_DIR)/cl_cam.c + $(DO_GL_CC) +$(BUILDDIR)/glclient/cl_cmd.o : $(CLIENT_DIR)/cl_cmd.c + $(DO_GL_CC) + +$(BUILDDIR)/glclient/cmd.o : $(CLIENT_DIR)/cmd.c + $(DO_GL_CC) + +$(BUILDDIR)/glclient/common.o : $(CLIENT_DIR)/common.c + $(DO_GL_CC) + +$(BUILDDIR)/glclient/console.o : $(CLIENT_DIR)/console.c + $(DO_GL_CC) + +$(BUILDDIR)/glclient/crc.o : $(CLIENT_DIR)/crc.c + $(DO_GL_CC) + +$(BUILDDIR)/glclient/cvar.o : $(CLIENT_DIR)/cvar.c + $(DO_GL_CC) + +$(BUILDDIR)/glclient/keys.o : $(CLIENT_DIR)/keys.c + $(DO_GL_CC) + +$(BUILDDIR)/glclient/mathlib.o : $(CLIENT_DIR)/mathlib.c + $(DO_GL_CC) + +$(BUILDDIR)/glclient/mdfour.o : $(CLIENT_DIR)/mdfour.c + $(DO_GL_CC) + +$(BUILDDIR)/glclient/menu.o : $(CLIENT_DIR)/menu.c + $(DO_GL_CC) + +$(BUILDDIR)/glclient/net_chan.o : $(CLIENT_DIR)/net_chan.c + $(DO_GL_CC) + +$(BUILDDIR)/glclient/net_udp.o : $(CLIENT_DIR)/net_udp.c + $(DO_GL_CC) + +$(BUILDDIR)/glclient/nonintel.o : $(CLIENT_DIR)/nonintel.c + $(DO_GL_CC) + +$(BUILDDIR)/glclient/pmove.o : $(CLIENT_DIR)/pmove.c + $(DO_GL_CC) + +$(BUILDDIR)/glclient/pmovetst.o : $(CLIENT_DIR)/pmovetst.c + $(DO_GL_CC) + +$(BUILDDIR)/glclient/r_part.o : $(CLIENT_DIR)/r_part.c + $(DO_GL_CC) + +$(BUILDDIR)/glclient/sbar.o : $(CLIENT_DIR)/sbar.c + $(DO_GL_CC) + +$(BUILDDIR)/glclient/screen.o : $(CLIENT_DIR)/screen.c + $(DO_GL_CC) + +$(BUILDDIR)/glclient/skin.o : $(CLIENT_DIR)/skin.c + $(DO_GL_CC) + +$(BUILDDIR)/glclient/snd_dma.o : $(CLIENT_DIR)/snd_dma.c + $(DO_GL_CC) + +$(BUILDDIR)/glclient/snd_mem.o : $(CLIENT_DIR)/snd_mem.c + $(DO_GL_CC) + +$(BUILDDIR)/glclient/snd_mix.o : $(CLIENT_DIR)/snd_mix.c + $(DO_GL_CC) + +$(BUILDDIR)/glclient/view.o : $(CLIENT_DIR)/view.c + $(DO_GL_CC) + +$(BUILDDIR)/glclient/wad.o : $(CLIENT_DIR)/wad.c + $(DO_GL_CC) + +$(BUILDDIR)/glclient/zone.o : $(CLIENT_DIR)/zone.c + $(DO_GL_CC) + +$(BUILDDIR)/glclient/cd_linux.o : $(CLIENT_DIR)/cd_linux.c + $(DO_GL_CC) + +$(BUILDDIR)/glclient/sys_linux.o : $(CLIENT_DIR)/sys_linux.c + $(DO_GL_CC) + +$(BUILDDIR)/glclient/snd_linux.o : $(CLIENT_DIR)/snd_linux.c + $(DO_GL_CC) + +$(BUILDDIR)/glclient/cl_slist.o : $(CLIENT_DIR)/cl_slist.c + $(DO_GL_CC) + +$(BUILDDIR)/glclient/gl_draw.o : $(CLIENT_DIR)/gl_draw.c + $(DO_GL_CC) + +$(BUILDDIR)/glclient/gl_mesh.o : $(CLIENT_DIR)/gl_mesh.c + $(DO_GL_CC) + +$(BUILDDIR)/glclient/gl_model.o : $(CLIENT_DIR)/gl_model.c + $(DO_GL_CC) + +$(BUILDDIR)/glclient/gl_ngraph.o : $(CLIENT_DIR)/gl_ngraph.c + $(DO_GL_CC) + +$(BUILDDIR)/glclient/gl_refrag.o : $(CLIENT_DIR)/gl_refrag.c + $(DO_GL_CC) + +$(BUILDDIR)/glclient/gl_rlight.o : $(CLIENT_DIR)/gl_rlight.c + $(DO_GL_CC) + +$(BUILDDIR)/glclient/gl_rmain.o : $(CLIENT_DIR)/gl_rmain.c + $(DO_GL_CC) + +$(BUILDDIR)/glclient/gl_rmisc.o : $(CLIENT_DIR)/gl_rmisc.c + $(DO_GL_CC) + +$(BUILDDIR)/glclient/gl_rsurf.o : $(CLIENT_DIR)/gl_rsurf.c + $(DO_GL_CC) + +$(BUILDDIR)/glclient/gl_screen.o : $(CLIENT_DIR)/gl_screen.c + $(DO_GL_CC) + +$(BUILDDIR)/glclient/gl_vidlinux.o : $(CLIENT_DIR)/gl_vidlinux.c + $(DO_GL_CC) + +$(BUILDDIR)/glclient/gl_vidlinuxglx.o : $(CLIENT_DIR)/gl_vidlinuxglx.c + $(DO_GL_CC) + +$(BUILDDIR)/glclient/gl_warp.o : $(CLIENT_DIR)/gl_warp.c + $(DO_GL_CC) + +$(BUILDDIR)/glclient/version.o : $(CLIENT_DIR)/version.c + $(DO_GL_CC) + +$(BUILDDIR)/glclient/teamplay.o : $(CLIENT_DIR)/teamplay.c + $(DO_GL_CC) + + +$(BUILDDIR)/glclient/cl_math.o : $(CLIENT_DIR)/cl_math.s + $(DO_GL_AS) + +$(BUILDDIR)/glclient/math.o : $(CLIENT_DIR)/math.s + $(DO_GL_AS) + +$(BUILDDIR)/glclient/snd_mixa.o : $(CLIENT_DIR)/snd_mixa.s + $(DO_GL_AS) -clean-debug: - $(MAKE) clean2 BUILDDIR=$(BUILD_DEBUG_DIR) CFLAGS="$(DEBUG_CFLAGS)" +$(BUILDDIR)/glclient/sys_x86.o : $(CLIENT_DIR)/sys_x86.s + $(DO_GL_AS) -clean-release: - $(MAKE) clean2 BUILDDIR=$(BUILD_RELEASE_DIR) CFLAGS="$(DEBUG_CFLAGS)" -clean2: - -rm -f $(QWSV_OBJS) diff --git a/source/adivtab.h b/source/adivtab.h index db3a7c4d..5d556e4b 100644 --- a/source/adivtab.h +++ b/source/adivtab.h @@ -1,1074 +1,1074 @@ -/* -Copyright (C) 1999, 2000 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}, +/* +Copyright (C) 1999, 2000 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/source/anorm_dots.h b/source/anorm_dots.h index c11a678f..2845fa20 100644 --- a/source/anorm_dots.h +++ b/source/anorm_dots.h @@ -1,37 +1,37 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -{ -{1.23,1.30,1.47,1.35,1.56,1.71,1.37,1.38,1.59,1.60,1.79,1.97,1.88,1.92,1.79,1.02,0.93,1.07,0.82,0.87,0.88,0.94,0.96,1.14,1.11,0.82,0.83,0.89,0.89,0.86,0.94,0.91,1.00,1.21,0.98,1.48,1.30,1.57,0.96,1.07,1.14,1.60,1.61,1.40,1.37,1.72,1.78,1.79,1.93,1.99,1.90,1.68,1.71,1.86,1.60,1.68,1.78,1.86,1.93,1.99,1.97,1.44,1.22,1.49,0.93,0.99,0.99,1.23,1.22,1.44,1.49,0.89,0.89,0.97,0.91,0.98,1.19,0.82,0.76,0.82,0.71,0.72,0.73,0.76,0.79,0.86,0.83,0.72,0.76,0.76,0.89,0.82,0.89,0.82,0.89,0.91,0.83,0.96,1.14,0.97,1.40,1.19,0.98,0.94,1.00,1.07,1.37,1.21,1.48,1.30,1.57,1.61,1.37,0.86,0.83,0.91,0.82,0.82,0.88,0.89,0.96,1.14,0.98,0.87,0.93,0.94,1.02,1.30,1.07,1.35,1.38,1.11,1.56,1.92,1.79,1.79,1.59,1.60,1.72,1.90,1.79,0.80,0.85,0.79,0.93,0.80,0.85,0.77,0.74,0.72,0.77,0.74,0.72,0.70,0.70,0.71,0.76,0.73,0.79,0.79,0.73,0.76,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00}, -{1.26,1.26,1.48,1.23,1.50,1.71,1.14,1.19,1.38,1.46,1.64,1.94,1.87,1.84,1.71,1.02,0.92,1.00,0.79,0.85,0.84,0.91,0.90,0.98,0.99,0.77,0.77,0.83,0.82,0.79,0.86,0.84,0.92,0.99,0.91,1.24,1.03,1.33,0.88,0.94,0.97,1.41,1.39,1.18,1.11,1.51,1.61,1.59,1.80,1.91,1.76,1.54,1.65,1.76,1.70,1.70,1.85,1.85,1.97,1.99,1.93,1.28,1.09,1.39,0.92,0.97,0.99,1.18,1.26,1.52,1.48,0.83,0.85,0.90,0.88,0.93,1.00,0.77,0.73,0.78,0.72,0.71,0.74,0.75,0.79,0.86,0.81,0.75,0.81,0.79,0.96,0.88,0.94,0.86,0.93,0.92,0.85,1.08,1.33,1.05,1.55,1.31,1.01,1.05,1.27,1.31,1.60,1.47,1.70,1.54,1.76,1.76,1.57,0.93,0.90,0.99,0.88,0.88,0.95,0.97,1.11,1.39,1.20,0.92,0.97,1.01,1.10,1.39,1.22,1.51,1.58,1.32,1.64,1.97,1.85,1.91,1.77,1.74,1.88,1.99,1.91,0.79,0.86,0.80,0.94,0.84,0.88,0.74,0.74,0.71,0.82,0.77,0.76,0.70,0.73,0.72,0.73,0.70,0.74,0.85,0.77,0.82,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00}, -{1.34,1.27,1.53,1.17,1.46,1.71,0.98,1.05,1.20,1.34,1.48,1.86,1.82,1.71,1.62,1.09,0.94,0.99,0.79,0.85,0.82,0.90,0.87,0.93,0.96,0.76,0.74,0.79,0.76,0.74,0.79,0.78,0.85,0.92,0.85,1.00,0.93,1.06,0.81,0.86,0.89,1.16,1.12,0.97,0.95,1.28,1.38,1.35,1.60,1.77,1.57,1.33,1.50,1.58,1.69,1.63,1.82,1.74,1.91,1.92,1.80,1.04,0.97,1.21,0.90,0.93,0.97,1.05,1.21,1.48,1.37,0.77,0.80,0.84,0.85,0.88,0.92,0.73,0.71,0.74,0.74,0.71,0.75,0.73,0.79,0.84,0.78,0.79,0.86,0.81,1.05,0.94,0.99,0.90,0.95,0.92,0.86,1.24,1.44,1.14,1.59,1.34,1.02,1.27,1.50,1.49,1.80,1.69,1.86,1.72,1.87,1.80,1.69,1.00,0.98,1.23,0.95,0.96,1.09,1.16,1.37,1.63,1.46,0.99,1.10,1.25,1.24,1.51,1.41,1.67,1.77,1.55,1.72,1.95,1.89,1.98,1.91,1.86,1.97,1.99,1.94,0.81,0.89,0.85,0.98,0.90,0.94,0.75,0.78,0.73,0.89,0.83,0.82,0.72,0.77,0.76,0.72,0.70,0.71,0.91,0.83,0.89,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00}, -{1.46,1.34,1.60,1.16,1.46,1.71,0.94,0.99,1.05,1.26,1.33,1.74,1.76,1.57,1.54,1.23,0.98,1.05,0.83,0.89,0.84,0.92,0.87,0.91,0.96,0.78,0.74,0.79,0.72,0.72,0.75,0.76,0.80,0.88,0.83,0.94,0.87,0.95,0.76,0.80,0.82,0.97,0.96,0.89,0.88,1.08,1.11,1.10,1.37,1.59,1.37,1.07,1.27,1.34,1.57,1.45,1.69,1.55,1.77,1.79,1.60,0.93,0.90,0.99,0.86,0.87,0.93,0.96,1.07,1.35,1.18,0.73,0.76,0.77,0.81,0.82,0.85,0.70,0.71,0.72,0.78,0.73,0.77,0.73,0.79,0.82,0.76,0.83,0.90,0.84,1.18,0.98,1.03,0.92,0.95,0.90,0.86,1.32,1.45,1.15,1.53,1.27,0.99,1.42,1.65,1.58,1.93,1.83,1.94,1.81,1.88,1.74,1.70,1.19,1.17,1.44,1.11,1.15,1.36,1.41,1.61,1.81,1.67,1.22,1.34,1.50,1.42,1.65,1.61,1.82,1.91,1.75,1.80,1.89,1.89,1.98,1.99,1.94,1.98,1.92,1.87,0.86,0.95,0.92,1.14,0.98,1.03,0.79,0.84,0.77,0.97,0.90,0.89,0.76,0.82,0.82,0.74,0.72,0.71,0.98,0.89,0.97,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00}, -{1.60,1.44,1.68,1.22,1.49,1.71,0.93,0.99,0.99,1.23,1.22,1.60,1.68,1.44,1.49,1.40,1.14,1.19,0.89,0.96,0.89,0.97,0.89,0.91,0.98,0.82,0.76,0.82,0.71,0.72,0.73,0.76,0.79,0.86,0.83,0.91,0.83,0.89,0.72,0.76,0.76,0.89,0.89,0.82,0.82,0.98,0.96,0.97,1.14,1.40,1.19,0.94,1.00,1.07,1.37,1.21,1.48,1.30,1.57,1.61,1.37,0.86,0.83,0.91,0.82,0.82,0.88,0.89,0.96,1.14,0.98,0.70,0.72,0.73,0.77,0.76,0.79,0.70,0.72,0.71,0.82,0.77,0.80,0.74,0.79,0.80,0.74,0.87,0.93,0.85,1.23,1.02,1.02,0.93,0.93,0.87,0.85,1.30,1.35,1.07,1.38,1.11,0.94,1.47,1.71,1.56,1.97,1.88,1.92,1.79,1.79,1.59,1.60,1.30,1.35,1.56,1.37,1.38,1.59,1.60,1.79,1.92,1.79,1.48,1.57,1.72,1.61,1.78,1.79,1.93,1.99,1.90,1.86,1.78,1.86,1.93,1.99,1.97,1.90,1.79,1.72,0.94,1.07,1.00,1.37,1.21,1.30,0.86,0.91,0.83,1.14,0.98,0.96,0.82,0.88,0.89,0.79,0.76,0.73,1.07,0.94,1.11,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00}, -{1.74,1.57,1.76,1.33,1.54,1.71,0.94,1.05,0.99,1.26,1.16,1.46,1.60,1.34,1.46,1.59,1.37,1.37,0.97,1.11,0.96,1.10,0.95,0.94,1.08,0.89,0.82,0.88,0.72,0.76,0.75,0.80,0.80,0.88,0.87,0.91,0.83,0.87,0.72,0.76,0.74,0.83,0.84,0.78,0.79,0.96,0.89,0.92,0.98,1.23,1.05,0.86,0.92,0.95,1.11,0.98,1.22,1.03,1.34,1.42,1.14,0.79,0.77,0.84,0.78,0.76,0.82,0.82,0.89,0.97,0.90,0.70,0.71,0.71,0.73,0.72,0.74,0.73,0.76,0.72,0.86,0.81,0.82,0.76,0.79,0.77,0.73,0.90,0.95,0.86,1.18,1.03,0.98,0.92,0.90,0.83,0.84,1.19,1.17,0.98,1.15,0.97,0.89,1.42,1.65,1.44,1.93,1.83,1.81,1.67,1.61,1.36,1.41,1.32,1.45,1.58,1.57,1.53,1.74,1.70,1.88,1.94,1.81,1.69,1.77,1.87,1.79,1.89,1.92,1.98,1.99,1.98,1.89,1.65,1.80,1.82,1.91,1.94,1.75,1.61,1.50,1.07,1.34,1.27,1.60,1.45,1.55,0.93,0.99,0.90,1.35,1.18,1.07,0.87,0.93,0.96,0.85,0.82,0.77,1.15,0.99,1.27,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00}, -{1.86,1.71,1.82,1.48,1.62,1.71,0.98,1.20,1.05,1.34,1.17,1.34,1.53,1.27,1.46,1.77,1.60,1.57,1.16,1.38,1.12,1.35,1.06,1.00,1.28,0.97,0.89,0.95,0.76,0.81,0.79,0.86,0.85,0.92,0.93,0.93,0.85,0.87,0.74,0.78,0.74,0.79,0.82,0.76,0.79,0.96,0.85,0.90,0.94,1.09,0.99,0.81,0.85,0.89,0.95,0.90,0.99,0.94,1.10,1.24,0.98,0.75,0.73,0.78,0.74,0.72,0.77,0.76,0.82,0.89,0.83,0.73,0.71,0.71,0.71,0.70,0.72,0.77,0.80,0.74,0.90,0.85,0.84,0.78,0.79,0.75,0.73,0.92,0.95,0.86,1.05,0.99,0.94,0.90,0.86,0.79,0.81,1.00,0.98,0.91,0.96,0.89,0.83,1.27,1.50,1.23,1.80,1.69,1.63,1.46,1.37,1.09,1.16,1.24,1.44,1.49,1.69,1.59,1.80,1.69,1.87,1.86,1.72,1.82,1.91,1.94,1.92,1.95,1.99,1.98,1.91,1.97,1.89,1.51,1.72,1.67,1.77,1.86,1.55,1.41,1.25,1.33,1.58,1.50,1.80,1.63,1.74,1.04,1.21,0.97,1.48,1.37,1.21,0.93,0.97,1.05,0.92,0.88,0.84,1.14,1.02,1.34,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00}, -{1.94,1.84,1.87,1.64,1.71,1.71,1.14,1.38,1.19,1.46,1.23,1.26,1.48,1.26,1.50,1.91,1.80,1.76,1.41,1.61,1.39,1.59,1.33,1.24,1.51,1.18,0.97,1.11,0.82,0.88,0.86,0.94,0.92,0.99,1.03,0.98,0.91,0.90,0.79,0.84,0.77,0.79,0.84,0.77,0.83,0.99,0.85,0.91,0.92,1.02,1.00,0.79,0.80,0.86,0.88,0.84,0.92,0.88,0.97,1.10,0.94,0.74,0.71,0.74,0.72,0.70,0.73,0.72,0.76,0.82,0.77,0.77,0.73,0.74,0.71,0.70,0.73,0.83,0.85,0.78,0.92,0.88,0.86,0.81,0.79,0.74,0.75,0.92,0.93,0.85,0.96,0.94,0.88,0.86,0.81,0.75,0.79,0.93,0.90,0.85,0.88,0.82,0.77,1.05,1.27,0.99,1.60,1.47,1.39,1.20,1.11,0.95,0.97,1.08,1.33,1.31,1.70,1.55,1.76,1.57,1.76,1.70,1.54,1.85,1.97,1.91,1.99,1.97,1.99,1.91,1.77,1.88,1.85,1.39,1.64,1.51,1.58,1.74,1.32,1.22,1.01,1.54,1.76,1.65,1.93,1.70,1.85,1.28,1.39,1.09,1.52,1.48,1.26,0.97,0.99,1.18,1.00,0.93,0.90,1.05,1.01,1.31,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00}, -{1.97,1.92,1.88,1.79,1.79,1.71,1.37,1.59,1.38,1.60,1.35,1.23,1.47,1.30,1.56,1.99,1.93,1.90,1.60,1.78,1.61,1.79,1.57,1.48,1.72,1.40,1.14,1.37,0.89,0.96,0.94,1.07,1.00,1.21,1.30,1.14,0.98,0.96,0.86,0.91,0.83,0.82,0.88,0.82,0.89,1.11,0.87,0.94,0.93,1.02,1.07,0.80,0.79,0.85,0.82,0.80,0.87,0.85,0.93,1.02,0.93,0.77,0.72,0.74,0.71,0.70,0.70,0.71,0.72,0.77,0.74,0.82,0.76,0.79,0.72,0.73,0.76,0.89,0.89,0.82,0.93,0.91,0.86,0.83,0.79,0.73,0.76,0.91,0.89,0.83,0.89,0.89,0.82,0.82,0.76,0.72,0.76,0.86,0.83,0.79,0.82,0.76,0.73,0.94,1.00,0.91,1.37,1.21,1.14,0.98,0.96,0.88,0.89,0.96,1.14,1.07,1.60,1.40,1.61,1.37,1.57,1.48,1.30,1.78,1.93,1.79,1.99,1.92,1.90,1.79,1.59,1.72,1.79,1.30,1.56,1.35,1.38,1.60,1.11,1.07,0.94,1.68,1.86,1.71,1.97,1.68,1.86,1.44,1.49,1.22,1.44,1.49,1.22,0.99,0.99,1.23,1.19,0.98,0.97,0.97,0.98,1.19,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00}, -{1.94,1.97,1.87,1.91,1.85,1.71,1.60,1.77,1.58,1.74,1.51,1.26,1.48,1.39,1.64,1.99,1.97,1.99,1.70,1.85,1.76,1.91,1.76,1.70,1.88,1.55,1.33,1.57,0.96,1.08,1.05,1.31,1.27,1.47,1.54,1.39,1.20,1.11,0.93,0.99,0.90,0.88,0.95,0.88,0.97,1.32,0.92,1.01,0.97,1.10,1.22,0.84,0.80,0.88,0.79,0.79,0.85,0.86,0.92,1.02,0.94,0.82,0.76,0.77,0.72,0.73,0.70,0.72,0.71,0.74,0.74,0.88,0.81,0.85,0.75,0.77,0.82,0.94,0.93,0.86,0.92,0.92,0.86,0.85,0.79,0.74,0.79,0.88,0.85,0.81,0.82,0.83,0.77,0.78,0.73,0.71,0.75,0.79,0.77,0.74,0.77,0.73,0.70,0.86,0.92,0.84,1.14,0.99,0.98,0.91,0.90,0.84,0.83,0.88,0.97,0.94,1.41,1.18,1.39,1.11,1.33,1.24,1.03,1.61,1.80,1.59,1.91,1.84,1.76,1.64,1.38,1.51,1.71,1.26,1.50,1.23,1.19,1.46,0.99,1.00,0.91,1.70,1.85,1.65,1.93,1.54,1.76,1.52,1.48,1.26,1.28,1.39,1.09,0.99,0.97,1.18,1.31,1.01,1.05,0.90,0.93,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00}, -{1.86,1.95,1.82,1.98,1.89,1.71,1.80,1.91,1.77,1.86,1.67,1.34,1.53,1.51,1.72,1.92,1.91,1.99,1.69,1.82,1.80,1.94,1.87,1.86,1.97,1.59,1.44,1.69,1.05,1.24,1.27,1.49,1.50,1.69,1.72,1.63,1.46,1.37,1.00,1.23,0.98,0.95,1.09,0.96,1.16,1.55,0.99,1.25,1.10,1.24,1.41,0.90,0.85,0.94,0.79,0.81,0.85,0.89,0.94,1.09,0.98,0.89,0.82,0.83,0.74,0.77,0.72,0.76,0.73,0.75,0.78,0.94,0.86,0.91,0.79,0.83,0.89,0.99,0.95,0.90,0.90,0.92,0.84,0.86,0.79,0.75,0.81,0.85,0.80,0.78,0.76,0.77,0.73,0.74,0.71,0.71,0.73,0.74,0.74,0.71,0.76,0.72,0.70,0.79,0.85,0.78,0.98,0.92,0.93,0.85,0.87,0.82,0.79,0.81,0.89,0.86,1.16,0.97,1.12,0.95,1.06,1.00,0.93,1.38,1.60,1.35,1.77,1.71,1.57,1.48,1.20,1.28,1.62,1.27,1.46,1.17,1.05,1.34,0.96,0.99,0.90,1.63,1.74,1.50,1.80,1.33,1.58,1.48,1.37,1.21,1.04,1.21,0.97,0.97,0.93,1.05,1.34,1.02,1.14,0.84,0.88,0.92,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00}, -{1.74,1.89,1.76,1.98,1.89,1.71,1.93,1.99,1.91,1.94,1.82,1.46,1.60,1.65,1.80,1.79,1.77,1.92,1.57,1.69,1.74,1.87,1.88,1.94,1.98,1.53,1.45,1.70,1.18,1.32,1.42,1.58,1.65,1.83,1.81,1.81,1.67,1.61,1.19,1.44,1.17,1.11,1.36,1.15,1.41,1.75,1.22,1.50,1.34,1.42,1.61,0.98,0.92,1.03,0.83,0.86,0.89,0.95,0.98,1.23,1.14,0.97,0.89,0.90,0.78,0.82,0.76,0.82,0.77,0.79,0.84,0.98,0.90,0.98,0.83,0.89,0.97,1.03,0.95,0.92,0.86,0.90,0.82,0.86,0.79,0.77,0.84,0.81,0.76,0.76,0.72,0.73,0.70,0.72,0.71,0.73,0.73,0.72,0.74,0.71,0.78,0.74,0.72,0.75,0.80,0.76,0.94,0.88,0.91,0.83,0.87,0.84,0.79,0.76,0.82,0.80,0.97,0.89,0.96,0.88,0.95,0.94,0.87,1.11,1.37,1.10,1.59,1.57,1.37,1.33,1.05,1.08,1.54,1.34,1.46,1.16,0.99,1.26,0.96,1.05,0.92,1.45,1.55,1.27,1.60,1.07,1.34,1.35,1.18,1.07,0.93,0.99,0.90,0.93,0.87,0.96,1.27,0.99,1.15,0.77,0.82,0.85,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00}, -{1.60,1.78,1.68,1.93,1.86,1.71,1.97,1.99,1.99,1.97,1.93,1.60,1.68,1.78,1.86,1.61,1.57,1.79,1.37,1.48,1.59,1.72,1.79,1.92,1.90,1.38,1.35,1.60,1.23,1.30,1.47,1.56,1.71,1.88,1.79,1.92,1.79,1.79,1.30,1.56,1.35,1.37,1.59,1.38,1.60,1.90,1.48,1.72,1.57,1.61,1.79,1.21,1.00,1.30,0.89,0.94,0.96,1.07,1.14,1.40,1.37,1.14,0.96,0.98,0.82,0.88,0.82,0.89,0.83,0.86,0.91,1.02,0.93,1.07,0.87,0.94,1.11,1.02,0.93,0.93,0.82,0.87,0.80,0.85,0.79,0.80,0.85,0.77,0.72,0.74,0.71,0.70,0.70,0.71,0.72,0.77,0.74,0.72,0.76,0.73,0.82,0.79,0.76,0.73,0.79,0.76,0.93,0.86,0.91,0.83,0.89,0.89,0.82,0.72,0.76,0.76,0.89,0.82,0.89,0.82,0.89,0.91,0.83,0.96,1.14,0.97,1.40,1.44,1.19,1.22,0.99,0.98,1.49,1.44,1.49,1.22,0.99,1.23,0.98,1.19,0.97,1.21,1.30,1.00,1.37,0.94,1.07,1.14,0.98,0.96,0.86,0.91,0.83,0.88,0.82,0.89,1.11,0.94,1.07,0.73,0.76,0.79,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00}, -{1.46,1.65,1.60,1.82,1.80,1.71,1.93,1.91,1.99,1.94,1.98,1.74,1.76,1.89,1.89,1.42,1.34,1.61,1.11,1.22,1.36,1.50,1.61,1.81,1.75,1.15,1.17,1.41,1.18,1.19,1.42,1.44,1.65,1.83,1.67,1.94,1.81,1.88,1.32,1.58,1.45,1.57,1.74,1.53,1.70,1.98,1.69,1.87,1.77,1.79,1.92,1.45,1.27,1.55,0.97,1.07,1.11,1.34,1.37,1.59,1.60,1.35,1.07,1.18,0.86,0.93,0.87,0.96,0.90,0.93,0.99,1.03,0.95,1.15,0.90,0.99,1.27,0.98,0.90,0.92,0.78,0.83,0.77,0.84,0.79,0.82,0.86,0.73,0.71,0.73,0.72,0.70,0.73,0.72,0.76,0.81,0.76,0.76,0.82,0.77,0.89,0.85,0.82,0.75,0.80,0.80,0.94,0.88,0.94,0.87,0.95,0.96,0.88,0.72,0.74,0.76,0.83,0.78,0.84,0.79,0.87,0.91,0.83,0.89,0.98,0.92,1.23,1.34,1.05,1.16,0.99,0.96,1.46,1.57,1.54,1.33,1.05,1.26,1.08,1.37,1.10,0.98,1.03,0.92,1.14,0.86,0.95,0.97,0.90,0.89,0.79,0.84,0.77,0.82,0.76,0.82,0.97,0.89,0.98,0.71,0.72,0.74,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00}, -{1.34,1.51,1.53,1.67,1.72,1.71,1.80,1.77,1.91,1.86,1.98,1.86,1.82,1.95,1.89,1.24,1.10,1.41,0.95,0.99,1.09,1.25,1.37,1.63,1.55,0.96,0.98,1.16,1.05,1.00,1.27,1.23,1.50,1.69,1.46,1.86,1.72,1.87,1.24,1.49,1.44,1.69,1.80,1.59,1.69,1.97,1.82,1.94,1.91,1.92,1.99,1.63,1.50,1.74,1.16,1.33,1.38,1.58,1.60,1.77,1.80,1.48,1.21,1.37,0.90,0.97,0.93,1.05,0.97,1.04,1.21,0.99,0.95,1.14,0.92,1.02,1.34,0.94,0.86,0.90,0.74,0.79,0.75,0.81,0.79,0.84,0.86,0.71,0.71,0.73,0.76,0.73,0.77,0.74,0.80,0.85,0.78,0.81,0.89,0.84,0.97,0.92,0.88,0.79,0.85,0.86,0.98,0.92,1.00,0.93,1.06,1.12,0.95,0.74,0.74,0.78,0.79,0.76,0.82,0.79,0.87,0.93,0.85,0.85,0.94,0.90,1.09,1.27,0.99,1.17,1.05,0.96,1.46,1.71,1.62,1.48,1.20,1.34,1.28,1.57,1.35,0.90,0.94,0.85,0.98,0.81,0.89,0.89,0.83,0.82,0.75,0.78,0.73,0.77,0.72,0.76,0.89,0.83,0.91,0.71,0.70,0.72,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00}, -{1.26,1.39,1.48,1.51,1.64,1.71,1.60,1.58,1.77,1.74,1.91,1.94,1.87,1.97,1.85,1.10,0.97,1.22,0.88,0.92,0.95,1.01,1.11,1.39,1.32,0.88,0.90,0.97,0.96,0.93,1.05,0.99,1.27,1.47,1.20,1.70,1.54,1.76,1.08,1.31,1.33,1.70,1.76,1.55,1.57,1.88,1.85,1.91,1.97,1.99,1.99,1.70,1.65,1.85,1.41,1.54,1.61,1.76,1.80,1.91,1.93,1.52,1.26,1.48,0.92,0.99,0.97,1.18,1.09,1.28,1.39,0.94,0.93,1.05,0.92,1.01,1.31,0.88,0.81,0.86,0.72,0.75,0.74,0.79,0.79,0.86,0.85,0.71,0.73,0.75,0.82,0.77,0.83,0.78,0.85,0.88,0.81,0.88,0.97,0.90,1.18,1.00,0.93,0.86,0.92,0.94,1.14,0.99,1.24,1.03,1.33,1.39,1.11,0.79,0.77,0.84,0.79,0.77,0.84,0.83,0.90,0.98,0.91,0.85,0.92,0.91,1.02,1.26,1.00,1.23,1.19,0.99,1.50,1.84,1.71,1.64,1.38,1.46,1.51,1.76,1.59,0.84,0.88,0.80,0.94,0.79,0.86,0.82,0.77,0.76,0.74,0.74,0.71,0.73,0.70,0.72,0.82,0.77,0.85,0.74,0.70,0.73,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00} -} +/* +Copyright (C) 1996-1997 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. + +*/ +{ +{1.23,1.30,1.47,1.35,1.56,1.71,1.37,1.38,1.59,1.60,1.79,1.97,1.88,1.92,1.79,1.02,0.93,1.07,0.82,0.87,0.88,0.94,0.96,1.14,1.11,0.82,0.83,0.89,0.89,0.86,0.94,0.91,1.00,1.21,0.98,1.48,1.30,1.57,0.96,1.07,1.14,1.60,1.61,1.40,1.37,1.72,1.78,1.79,1.93,1.99,1.90,1.68,1.71,1.86,1.60,1.68,1.78,1.86,1.93,1.99,1.97,1.44,1.22,1.49,0.93,0.99,0.99,1.23,1.22,1.44,1.49,0.89,0.89,0.97,0.91,0.98,1.19,0.82,0.76,0.82,0.71,0.72,0.73,0.76,0.79,0.86,0.83,0.72,0.76,0.76,0.89,0.82,0.89,0.82,0.89,0.91,0.83,0.96,1.14,0.97,1.40,1.19,0.98,0.94,1.00,1.07,1.37,1.21,1.48,1.30,1.57,1.61,1.37,0.86,0.83,0.91,0.82,0.82,0.88,0.89,0.96,1.14,0.98,0.87,0.93,0.94,1.02,1.30,1.07,1.35,1.38,1.11,1.56,1.92,1.79,1.79,1.59,1.60,1.72,1.90,1.79,0.80,0.85,0.79,0.93,0.80,0.85,0.77,0.74,0.72,0.77,0.74,0.72,0.70,0.70,0.71,0.76,0.73,0.79,0.79,0.73,0.76,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00}, +{1.26,1.26,1.48,1.23,1.50,1.71,1.14,1.19,1.38,1.46,1.64,1.94,1.87,1.84,1.71,1.02,0.92,1.00,0.79,0.85,0.84,0.91,0.90,0.98,0.99,0.77,0.77,0.83,0.82,0.79,0.86,0.84,0.92,0.99,0.91,1.24,1.03,1.33,0.88,0.94,0.97,1.41,1.39,1.18,1.11,1.51,1.61,1.59,1.80,1.91,1.76,1.54,1.65,1.76,1.70,1.70,1.85,1.85,1.97,1.99,1.93,1.28,1.09,1.39,0.92,0.97,0.99,1.18,1.26,1.52,1.48,0.83,0.85,0.90,0.88,0.93,1.00,0.77,0.73,0.78,0.72,0.71,0.74,0.75,0.79,0.86,0.81,0.75,0.81,0.79,0.96,0.88,0.94,0.86,0.93,0.92,0.85,1.08,1.33,1.05,1.55,1.31,1.01,1.05,1.27,1.31,1.60,1.47,1.70,1.54,1.76,1.76,1.57,0.93,0.90,0.99,0.88,0.88,0.95,0.97,1.11,1.39,1.20,0.92,0.97,1.01,1.10,1.39,1.22,1.51,1.58,1.32,1.64,1.97,1.85,1.91,1.77,1.74,1.88,1.99,1.91,0.79,0.86,0.80,0.94,0.84,0.88,0.74,0.74,0.71,0.82,0.77,0.76,0.70,0.73,0.72,0.73,0.70,0.74,0.85,0.77,0.82,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00}, +{1.34,1.27,1.53,1.17,1.46,1.71,0.98,1.05,1.20,1.34,1.48,1.86,1.82,1.71,1.62,1.09,0.94,0.99,0.79,0.85,0.82,0.90,0.87,0.93,0.96,0.76,0.74,0.79,0.76,0.74,0.79,0.78,0.85,0.92,0.85,1.00,0.93,1.06,0.81,0.86,0.89,1.16,1.12,0.97,0.95,1.28,1.38,1.35,1.60,1.77,1.57,1.33,1.50,1.58,1.69,1.63,1.82,1.74,1.91,1.92,1.80,1.04,0.97,1.21,0.90,0.93,0.97,1.05,1.21,1.48,1.37,0.77,0.80,0.84,0.85,0.88,0.92,0.73,0.71,0.74,0.74,0.71,0.75,0.73,0.79,0.84,0.78,0.79,0.86,0.81,1.05,0.94,0.99,0.90,0.95,0.92,0.86,1.24,1.44,1.14,1.59,1.34,1.02,1.27,1.50,1.49,1.80,1.69,1.86,1.72,1.87,1.80,1.69,1.00,0.98,1.23,0.95,0.96,1.09,1.16,1.37,1.63,1.46,0.99,1.10,1.25,1.24,1.51,1.41,1.67,1.77,1.55,1.72,1.95,1.89,1.98,1.91,1.86,1.97,1.99,1.94,0.81,0.89,0.85,0.98,0.90,0.94,0.75,0.78,0.73,0.89,0.83,0.82,0.72,0.77,0.76,0.72,0.70,0.71,0.91,0.83,0.89,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00}, +{1.46,1.34,1.60,1.16,1.46,1.71,0.94,0.99,1.05,1.26,1.33,1.74,1.76,1.57,1.54,1.23,0.98,1.05,0.83,0.89,0.84,0.92,0.87,0.91,0.96,0.78,0.74,0.79,0.72,0.72,0.75,0.76,0.80,0.88,0.83,0.94,0.87,0.95,0.76,0.80,0.82,0.97,0.96,0.89,0.88,1.08,1.11,1.10,1.37,1.59,1.37,1.07,1.27,1.34,1.57,1.45,1.69,1.55,1.77,1.79,1.60,0.93,0.90,0.99,0.86,0.87,0.93,0.96,1.07,1.35,1.18,0.73,0.76,0.77,0.81,0.82,0.85,0.70,0.71,0.72,0.78,0.73,0.77,0.73,0.79,0.82,0.76,0.83,0.90,0.84,1.18,0.98,1.03,0.92,0.95,0.90,0.86,1.32,1.45,1.15,1.53,1.27,0.99,1.42,1.65,1.58,1.93,1.83,1.94,1.81,1.88,1.74,1.70,1.19,1.17,1.44,1.11,1.15,1.36,1.41,1.61,1.81,1.67,1.22,1.34,1.50,1.42,1.65,1.61,1.82,1.91,1.75,1.80,1.89,1.89,1.98,1.99,1.94,1.98,1.92,1.87,0.86,0.95,0.92,1.14,0.98,1.03,0.79,0.84,0.77,0.97,0.90,0.89,0.76,0.82,0.82,0.74,0.72,0.71,0.98,0.89,0.97,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00}, +{1.60,1.44,1.68,1.22,1.49,1.71,0.93,0.99,0.99,1.23,1.22,1.60,1.68,1.44,1.49,1.40,1.14,1.19,0.89,0.96,0.89,0.97,0.89,0.91,0.98,0.82,0.76,0.82,0.71,0.72,0.73,0.76,0.79,0.86,0.83,0.91,0.83,0.89,0.72,0.76,0.76,0.89,0.89,0.82,0.82,0.98,0.96,0.97,1.14,1.40,1.19,0.94,1.00,1.07,1.37,1.21,1.48,1.30,1.57,1.61,1.37,0.86,0.83,0.91,0.82,0.82,0.88,0.89,0.96,1.14,0.98,0.70,0.72,0.73,0.77,0.76,0.79,0.70,0.72,0.71,0.82,0.77,0.80,0.74,0.79,0.80,0.74,0.87,0.93,0.85,1.23,1.02,1.02,0.93,0.93,0.87,0.85,1.30,1.35,1.07,1.38,1.11,0.94,1.47,1.71,1.56,1.97,1.88,1.92,1.79,1.79,1.59,1.60,1.30,1.35,1.56,1.37,1.38,1.59,1.60,1.79,1.92,1.79,1.48,1.57,1.72,1.61,1.78,1.79,1.93,1.99,1.90,1.86,1.78,1.86,1.93,1.99,1.97,1.90,1.79,1.72,0.94,1.07,1.00,1.37,1.21,1.30,0.86,0.91,0.83,1.14,0.98,0.96,0.82,0.88,0.89,0.79,0.76,0.73,1.07,0.94,1.11,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00}, +{1.74,1.57,1.76,1.33,1.54,1.71,0.94,1.05,0.99,1.26,1.16,1.46,1.60,1.34,1.46,1.59,1.37,1.37,0.97,1.11,0.96,1.10,0.95,0.94,1.08,0.89,0.82,0.88,0.72,0.76,0.75,0.80,0.80,0.88,0.87,0.91,0.83,0.87,0.72,0.76,0.74,0.83,0.84,0.78,0.79,0.96,0.89,0.92,0.98,1.23,1.05,0.86,0.92,0.95,1.11,0.98,1.22,1.03,1.34,1.42,1.14,0.79,0.77,0.84,0.78,0.76,0.82,0.82,0.89,0.97,0.90,0.70,0.71,0.71,0.73,0.72,0.74,0.73,0.76,0.72,0.86,0.81,0.82,0.76,0.79,0.77,0.73,0.90,0.95,0.86,1.18,1.03,0.98,0.92,0.90,0.83,0.84,1.19,1.17,0.98,1.15,0.97,0.89,1.42,1.65,1.44,1.93,1.83,1.81,1.67,1.61,1.36,1.41,1.32,1.45,1.58,1.57,1.53,1.74,1.70,1.88,1.94,1.81,1.69,1.77,1.87,1.79,1.89,1.92,1.98,1.99,1.98,1.89,1.65,1.80,1.82,1.91,1.94,1.75,1.61,1.50,1.07,1.34,1.27,1.60,1.45,1.55,0.93,0.99,0.90,1.35,1.18,1.07,0.87,0.93,0.96,0.85,0.82,0.77,1.15,0.99,1.27,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00}, +{1.86,1.71,1.82,1.48,1.62,1.71,0.98,1.20,1.05,1.34,1.17,1.34,1.53,1.27,1.46,1.77,1.60,1.57,1.16,1.38,1.12,1.35,1.06,1.00,1.28,0.97,0.89,0.95,0.76,0.81,0.79,0.86,0.85,0.92,0.93,0.93,0.85,0.87,0.74,0.78,0.74,0.79,0.82,0.76,0.79,0.96,0.85,0.90,0.94,1.09,0.99,0.81,0.85,0.89,0.95,0.90,0.99,0.94,1.10,1.24,0.98,0.75,0.73,0.78,0.74,0.72,0.77,0.76,0.82,0.89,0.83,0.73,0.71,0.71,0.71,0.70,0.72,0.77,0.80,0.74,0.90,0.85,0.84,0.78,0.79,0.75,0.73,0.92,0.95,0.86,1.05,0.99,0.94,0.90,0.86,0.79,0.81,1.00,0.98,0.91,0.96,0.89,0.83,1.27,1.50,1.23,1.80,1.69,1.63,1.46,1.37,1.09,1.16,1.24,1.44,1.49,1.69,1.59,1.80,1.69,1.87,1.86,1.72,1.82,1.91,1.94,1.92,1.95,1.99,1.98,1.91,1.97,1.89,1.51,1.72,1.67,1.77,1.86,1.55,1.41,1.25,1.33,1.58,1.50,1.80,1.63,1.74,1.04,1.21,0.97,1.48,1.37,1.21,0.93,0.97,1.05,0.92,0.88,0.84,1.14,1.02,1.34,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00}, +{1.94,1.84,1.87,1.64,1.71,1.71,1.14,1.38,1.19,1.46,1.23,1.26,1.48,1.26,1.50,1.91,1.80,1.76,1.41,1.61,1.39,1.59,1.33,1.24,1.51,1.18,0.97,1.11,0.82,0.88,0.86,0.94,0.92,0.99,1.03,0.98,0.91,0.90,0.79,0.84,0.77,0.79,0.84,0.77,0.83,0.99,0.85,0.91,0.92,1.02,1.00,0.79,0.80,0.86,0.88,0.84,0.92,0.88,0.97,1.10,0.94,0.74,0.71,0.74,0.72,0.70,0.73,0.72,0.76,0.82,0.77,0.77,0.73,0.74,0.71,0.70,0.73,0.83,0.85,0.78,0.92,0.88,0.86,0.81,0.79,0.74,0.75,0.92,0.93,0.85,0.96,0.94,0.88,0.86,0.81,0.75,0.79,0.93,0.90,0.85,0.88,0.82,0.77,1.05,1.27,0.99,1.60,1.47,1.39,1.20,1.11,0.95,0.97,1.08,1.33,1.31,1.70,1.55,1.76,1.57,1.76,1.70,1.54,1.85,1.97,1.91,1.99,1.97,1.99,1.91,1.77,1.88,1.85,1.39,1.64,1.51,1.58,1.74,1.32,1.22,1.01,1.54,1.76,1.65,1.93,1.70,1.85,1.28,1.39,1.09,1.52,1.48,1.26,0.97,0.99,1.18,1.00,0.93,0.90,1.05,1.01,1.31,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00}, +{1.97,1.92,1.88,1.79,1.79,1.71,1.37,1.59,1.38,1.60,1.35,1.23,1.47,1.30,1.56,1.99,1.93,1.90,1.60,1.78,1.61,1.79,1.57,1.48,1.72,1.40,1.14,1.37,0.89,0.96,0.94,1.07,1.00,1.21,1.30,1.14,0.98,0.96,0.86,0.91,0.83,0.82,0.88,0.82,0.89,1.11,0.87,0.94,0.93,1.02,1.07,0.80,0.79,0.85,0.82,0.80,0.87,0.85,0.93,1.02,0.93,0.77,0.72,0.74,0.71,0.70,0.70,0.71,0.72,0.77,0.74,0.82,0.76,0.79,0.72,0.73,0.76,0.89,0.89,0.82,0.93,0.91,0.86,0.83,0.79,0.73,0.76,0.91,0.89,0.83,0.89,0.89,0.82,0.82,0.76,0.72,0.76,0.86,0.83,0.79,0.82,0.76,0.73,0.94,1.00,0.91,1.37,1.21,1.14,0.98,0.96,0.88,0.89,0.96,1.14,1.07,1.60,1.40,1.61,1.37,1.57,1.48,1.30,1.78,1.93,1.79,1.99,1.92,1.90,1.79,1.59,1.72,1.79,1.30,1.56,1.35,1.38,1.60,1.11,1.07,0.94,1.68,1.86,1.71,1.97,1.68,1.86,1.44,1.49,1.22,1.44,1.49,1.22,0.99,0.99,1.23,1.19,0.98,0.97,0.97,0.98,1.19,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00}, +{1.94,1.97,1.87,1.91,1.85,1.71,1.60,1.77,1.58,1.74,1.51,1.26,1.48,1.39,1.64,1.99,1.97,1.99,1.70,1.85,1.76,1.91,1.76,1.70,1.88,1.55,1.33,1.57,0.96,1.08,1.05,1.31,1.27,1.47,1.54,1.39,1.20,1.11,0.93,0.99,0.90,0.88,0.95,0.88,0.97,1.32,0.92,1.01,0.97,1.10,1.22,0.84,0.80,0.88,0.79,0.79,0.85,0.86,0.92,1.02,0.94,0.82,0.76,0.77,0.72,0.73,0.70,0.72,0.71,0.74,0.74,0.88,0.81,0.85,0.75,0.77,0.82,0.94,0.93,0.86,0.92,0.92,0.86,0.85,0.79,0.74,0.79,0.88,0.85,0.81,0.82,0.83,0.77,0.78,0.73,0.71,0.75,0.79,0.77,0.74,0.77,0.73,0.70,0.86,0.92,0.84,1.14,0.99,0.98,0.91,0.90,0.84,0.83,0.88,0.97,0.94,1.41,1.18,1.39,1.11,1.33,1.24,1.03,1.61,1.80,1.59,1.91,1.84,1.76,1.64,1.38,1.51,1.71,1.26,1.50,1.23,1.19,1.46,0.99,1.00,0.91,1.70,1.85,1.65,1.93,1.54,1.76,1.52,1.48,1.26,1.28,1.39,1.09,0.99,0.97,1.18,1.31,1.01,1.05,0.90,0.93,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00}, +{1.86,1.95,1.82,1.98,1.89,1.71,1.80,1.91,1.77,1.86,1.67,1.34,1.53,1.51,1.72,1.92,1.91,1.99,1.69,1.82,1.80,1.94,1.87,1.86,1.97,1.59,1.44,1.69,1.05,1.24,1.27,1.49,1.50,1.69,1.72,1.63,1.46,1.37,1.00,1.23,0.98,0.95,1.09,0.96,1.16,1.55,0.99,1.25,1.10,1.24,1.41,0.90,0.85,0.94,0.79,0.81,0.85,0.89,0.94,1.09,0.98,0.89,0.82,0.83,0.74,0.77,0.72,0.76,0.73,0.75,0.78,0.94,0.86,0.91,0.79,0.83,0.89,0.99,0.95,0.90,0.90,0.92,0.84,0.86,0.79,0.75,0.81,0.85,0.80,0.78,0.76,0.77,0.73,0.74,0.71,0.71,0.73,0.74,0.74,0.71,0.76,0.72,0.70,0.79,0.85,0.78,0.98,0.92,0.93,0.85,0.87,0.82,0.79,0.81,0.89,0.86,1.16,0.97,1.12,0.95,1.06,1.00,0.93,1.38,1.60,1.35,1.77,1.71,1.57,1.48,1.20,1.28,1.62,1.27,1.46,1.17,1.05,1.34,0.96,0.99,0.90,1.63,1.74,1.50,1.80,1.33,1.58,1.48,1.37,1.21,1.04,1.21,0.97,0.97,0.93,1.05,1.34,1.02,1.14,0.84,0.88,0.92,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00}, +{1.74,1.89,1.76,1.98,1.89,1.71,1.93,1.99,1.91,1.94,1.82,1.46,1.60,1.65,1.80,1.79,1.77,1.92,1.57,1.69,1.74,1.87,1.88,1.94,1.98,1.53,1.45,1.70,1.18,1.32,1.42,1.58,1.65,1.83,1.81,1.81,1.67,1.61,1.19,1.44,1.17,1.11,1.36,1.15,1.41,1.75,1.22,1.50,1.34,1.42,1.61,0.98,0.92,1.03,0.83,0.86,0.89,0.95,0.98,1.23,1.14,0.97,0.89,0.90,0.78,0.82,0.76,0.82,0.77,0.79,0.84,0.98,0.90,0.98,0.83,0.89,0.97,1.03,0.95,0.92,0.86,0.90,0.82,0.86,0.79,0.77,0.84,0.81,0.76,0.76,0.72,0.73,0.70,0.72,0.71,0.73,0.73,0.72,0.74,0.71,0.78,0.74,0.72,0.75,0.80,0.76,0.94,0.88,0.91,0.83,0.87,0.84,0.79,0.76,0.82,0.80,0.97,0.89,0.96,0.88,0.95,0.94,0.87,1.11,1.37,1.10,1.59,1.57,1.37,1.33,1.05,1.08,1.54,1.34,1.46,1.16,0.99,1.26,0.96,1.05,0.92,1.45,1.55,1.27,1.60,1.07,1.34,1.35,1.18,1.07,0.93,0.99,0.90,0.93,0.87,0.96,1.27,0.99,1.15,0.77,0.82,0.85,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00}, +{1.60,1.78,1.68,1.93,1.86,1.71,1.97,1.99,1.99,1.97,1.93,1.60,1.68,1.78,1.86,1.61,1.57,1.79,1.37,1.48,1.59,1.72,1.79,1.92,1.90,1.38,1.35,1.60,1.23,1.30,1.47,1.56,1.71,1.88,1.79,1.92,1.79,1.79,1.30,1.56,1.35,1.37,1.59,1.38,1.60,1.90,1.48,1.72,1.57,1.61,1.79,1.21,1.00,1.30,0.89,0.94,0.96,1.07,1.14,1.40,1.37,1.14,0.96,0.98,0.82,0.88,0.82,0.89,0.83,0.86,0.91,1.02,0.93,1.07,0.87,0.94,1.11,1.02,0.93,0.93,0.82,0.87,0.80,0.85,0.79,0.80,0.85,0.77,0.72,0.74,0.71,0.70,0.70,0.71,0.72,0.77,0.74,0.72,0.76,0.73,0.82,0.79,0.76,0.73,0.79,0.76,0.93,0.86,0.91,0.83,0.89,0.89,0.82,0.72,0.76,0.76,0.89,0.82,0.89,0.82,0.89,0.91,0.83,0.96,1.14,0.97,1.40,1.44,1.19,1.22,0.99,0.98,1.49,1.44,1.49,1.22,0.99,1.23,0.98,1.19,0.97,1.21,1.30,1.00,1.37,0.94,1.07,1.14,0.98,0.96,0.86,0.91,0.83,0.88,0.82,0.89,1.11,0.94,1.07,0.73,0.76,0.79,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00}, +{1.46,1.65,1.60,1.82,1.80,1.71,1.93,1.91,1.99,1.94,1.98,1.74,1.76,1.89,1.89,1.42,1.34,1.61,1.11,1.22,1.36,1.50,1.61,1.81,1.75,1.15,1.17,1.41,1.18,1.19,1.42,1.44,1.65,1.83,1.67,1.94,1.81,1.88,1.32,1.58,1.45,1.57,1.74,1.53,1.70,1.98,1.69,1.87,1.77,1.79,1.92,1.45,1.27,1.55,0.97,1.07,1.11,1.34,1.37,1.59,1.60,1.35,1.07,1.18,0.86,0.93,0.87,0.96,0.90,0.93,0.99,1.03,0.95,1.15,0.90,0.99,1.27,0.98,0.90,0.92,0.78,0.83,0.77,0.84,0.79,0.82,0.86,0.73,0.71,0.73,0.72,0.70,0.73,0.72,0.76,0.81,0.76,0.76,0.82,0.77,0.89,0.85,0.82,0.75,0.80,0.80,0.94,0.88,0.94,0.87,0.95,0.96,0.88,0.72,0.74,0.76,0.83,0.78,0.84,0.79,0.87,0.91,0.83,0.89,0.98,0.92,1.23,1.34,1.05,1.16,0.99,0.96,1.46,1.57,1.54,1.33,1.05,1.26,1.08,1.37,1.10,0.98,1.03,0.92,1.14,0.86,0.95,0.97,0.90,0.89,0.79,0.84,0.77,0.82,0.76,0.82,0.97,0.89,0.98,0.71,0.72,0.74,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00}, +{1.34,1.51,1.53,1.67,1.72,1.71,1.80,1.77,1.91,1.86,1.98,1.86,1.82,1.95,1.89,1.24,1.10,1.41,0.95,0.99,1.09,1.25,1.37,1.63,1.55,0.96,0.98,1.16,1.05,1.00,1.27,1.23,1.50,1.69,1.46,1.86,1.72,1.87,1.24,1.49,1.44,1.69,1.80,1.59,1.69,1.97,1.82,1.94,1.91,1.92,1.99,1.63,1.50,1.74,1.16,1.33,1.38,1.58,1.60,1.77,1.80,1.48,1.21,1.37,0.90,0.97,0.93,1.05,0.97,1.04,1.21,0.99,0.95,1.14,0.92,1.02,1.34,0.94,0.86,0.90,0.74,0.79,0.75,0.81,0.79,0.84,0.86,0.71,0.71,0.73,0.76,0.73,0.77,0.74,0.80,0.85,0.78,0.81,0.89,0.84,0.97,0.92,0.88,0.79,0.85,0.86,0.98,0.92,1.00,0.93,1.06,1.12,0.95,0.74,0.74,0.78,0.79,0.76,0.82,0.79,0.87,0.93,0.85,0.85,0.94,0.90,1.09,1.27,0.99,1.17,1.05,0.96,1.46,1.71,1.62,1.48,1.20,1.34,1.28,1.57,1.35,0.90,0.94,0.85,0.98,0.81,0.89,0.89,0.83,0.82,0.75,0.78,0.73,0.77,0.72,0.76,0.89,0.83,0.91,0.71,0.70,0.72,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00}, +{1.26,1.39,1.48,1.51,1.64,1.71,1.60,1.58,1.77,1.74,1.91,1.94,1.87,1.97,1.85,1.10,0.97,1.22,0.88,0.92,0.95,1.01,1.11,1.39,1.32,0.88,0.90,0.97,0.96,0.93,1.05,0.99,1.27,1.47,1.20,1.70,1.54,1.76,1.08,1.31,1.33,1.70,1.76,1.55,1.57,1.88,1.85,1.91,1.97,1.99,1.99,1.70,1.65,1.85,1.41,1.54,1.61,1.76,1.80,1.91,1.93,1.52,1.26,1.48,0.92,0.99,0.97,1.18,1.09,1.28,1.39,0.94,0.93,1.05,0.92,1.01,1.31,0.88,0.81,0.86,0.72,0.75,0.74,0.79,0.79,0.86,0.85,0.71,0.73,0.75,0.82,0.77,0.83,0.78,0.85,0.88,0.81,0.88,0.97,0.90,1.18,1.00,0.93,0.86,0.92,0.94,1.14,0.99,1.24,1.03,1.33,1.39,1.11,0.79,0.77,0.84,0.79,0.77,0.84,0.83,0.90,0.98,0.91,0.85,0.92,0.91,1.02,1.26,1.00,1.23,1.19,0.99,1.50,1.84,1.71,1.64,1.38,1.46,1.51,1.76,1.59,0.84,0.88,0.80,0.94,0.79,0.86,0.82,0.77,0.76,0.74,0.74,0.71,0.73,0.70,0.72,0.82,0.77,0.85,0.74,0.70,0.73,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00} +} diff --git a/source/anorms.h b/source/anorms.h index 88042267..11a9007e 100644 --- a/source/anorms.h +++ b/source/anorms.h @@ -1,181 +1,181 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -{-0.525731, 0.000000, 0.850651}, -{-0.442863, 0.238856, 0.864188}, -{-0.295242, 0.000000, 0.955423}, -{-0.309017, 0.500000, 0.809017}, -{-0.162460, 0.262866, 0.951056}, -{0.000000, 0.000000, 1.000000}, -{0.000000, 0.850651, 0.525731}, -{-0.147621, 0.716567, 0.681718}, -{0.147621, 0.716567, 0.681718}, -{0.000000, 0.525731, 0.850651}, -{0.309017, 0.500000, 0.809017}, -{0.525731, 0.000000, 0.850651}, -{0.295242, 0.000000, 0.955423}, -{0.442863, 0.238856, 0.864188}, -{0.162460, 0.262866, 0.951056}, -{-0.681718, 0.147621, 0.716567}, -{-0.809017, 0.309017, 0.500000}, -{-0.587785, 0.425325, 0.688191}, -{-0.850651, 0.525731, 0.000000}, -{-0.864188, 0.442863, 0.238856}, -{-0.716567, 0.681718, 0.147621}, -{-0.688191, 0.587785, 0.425325}, -{-0.500000, 0.809017, 0.309017}, -{-0.238856, 0.864188, 0.442863}, -{-0.425325, 0.688191, 0.587785}, -{-0.716567, 0.681718, -0.147621}, -{-0.500000, 0.809017, -0.309017}, -{-0.525731, 0.850651, 0.000000}, -{0.000000, 0.850651, -0.525731}, -{-0.238856, 0.864188, -0.442863}, -{0.000000, 0.955423, -0.295242}, -{-0.262866, 0.951056, -0.162460}, -{0.000000, 1.000000, 0.000000}, -{0.000000, 0.955423, 0.295242}, -{-0.262866, 0.951056, 0.162460}, -{0.238856, 0.864188, 0.442863}, -{0.262866, 0.951056, 0.162460}, -{0.500000, 0.809017, 0.309017}, -{0.238856, 0.864188, -0.442863}, -{0.262866, 0.951056, -0.162460}, -{0.500000, 0.809017, -0.309017}, -{0.850651, 0.525731, 0.000000}, -{0.716567, 0.681718, 0.147621}, -{0.716567, 0.681718, -0.147621}, -{0.525731, 0.850651, 0.000000}, -{0.425325, 0.688191, 0.587785}, -{0.864188, 0.442863, 0.238856}, -{0.688191, 0.587785, 0.425325}, -{0.809017, 0.309017, 0.500000}, -{0.681718, 0.147621, 0.716567}, -{0.587785, 0.425325, 0.688191}, -{0.955423, 0.295242, 0.000000}, -{1.000000, 0.000000, 0.000000}, -{0.951056, 0.162460, 0.262866}, -{0.850651, -0.525731, 0.000000}, -{0.955423, -0.295242, 0.000000}, -{0.864188, -0.442863, 0.238856}, -{0.951056, -0.162460, 0.262866}, -{0.809017, -0.309017, 0.500000}, -{0.681718, -0.147621, 0.716567}, -{0.850651, 0.000000, 0.525731}, -{0.864188, 0.442863, -0.238856}, -{0.809017, 0.309017, -0.500000}, -{0.951056, 0.162460, -0.262866}, -{0.525731, 0.000000, -0.850651}, -{0.681718, 0.147621, -0.716567}, -{0.681718, -0.147621, -0.716567}, -{0.850651, 0.000000, -0.525731}, -{0.809017, -0.309017, -0.500000}, -{0.864188, -0.442863, -0.238856}, -{0.951056, -0.162460, -0.262866}, -{0.147621, 0.716567, -0.681718}, -{0.309017, 0.500000, -0.809017}, -{0.425325, 0.688191, -0.587785}, -{0.442863, 0.238856, -0.864188}, -{0.587785, 0.425325, -0.688191}, -{0.688191, 0.587785, -0.425325}, -{-0.147621, 0.716567, -0.681718}, -{-0.309017, 0.500000, -0.809017}, -{0.000000, 0.525731, -0.850651}, -{-0.525731, 0.000000, -0.850651}, -{-0.442863, 0.238856, -0.864188}, -{-0.295242, 0.000000, -0.955423}, -{-0.162460, 0.262866, -0.951056}, -{0.000000, 0.000000, -1.000000}, -{0.295242, 0.000000, -0.955423}, -{0.162460, 0.262866, -0.951056}, -{-0.442863, -0.238856, -0.864188}, -{-0.309017, -0.500000, -0.809017}, -{-0.162460, -0.262866, -0.951056}, -{0.000000, -0.850651, -0.525731}, -{-0.147621, -0.716567, -0.681718}, -{0.147621, -0.716567, -0.681718}, -{0.000000, -0.525731, -0.850651}, -{0.309017, -0.500000, -0.809017}, -{0.442863, -0.238856, -0.864188}, -{0.162460, -0.262866, -0.951056}, -{0.238856, -0.864188, -0.442863}, -{0.500000, -0.809017, -0.309017}, -{0.425325, -0.688191, -0.587785}, -{0.716567, -0.681718, -0.147621}, -{0.688191, -0.587785, -0.425325}, -{0.587785, -0.425325, -0.688191}, -{0.000000, -0.955423, -0.295242}, -{0.000000, -1.000000, 0.000000}, -{0.262866, -0.951056, -0.162460}, -{0.000000, -0.850651, 0.525731}, -{0.000000, -0.955423, 0.295242}, -{0.238856, -0.864188, 0.442863}, -{0.262866, -0.951056, 0.162460}, -{0.500000, -0.809017, 0.309017}, -{0.716567, -0.681718, 0.147621}, -{0.525731, -0.850651, 0.000000}, -{-0.238856, -0.864188, -0.442863}, -{-0.500000, -0.809017, -0.309017}, -{-0.262866, -0.951056, -0.162460}, -{-0.850651, -0.525731, 0.000000}, -{-0.716567, -0.681718, -0.147621}, -{-0.716567, -0.681718, 0.147621}, -{-0.525731, -0.850651, 0.000000}, -{-0.500000, -0.809017, 0.309017}, -{-0.238856, -0.864188, 0.442863}, -{-0.262866, -0.951056, 0.162460}, -{-0.864188, -0.442863, 0.238856}, -{-0.809017, -0.309017, 0.500000}, -{-0.688191, -0.587785, 0.425325}, -{-0.681718, -0.147621, 0.716567}, -{-0.442863, -0.238856, 0.864188}, -{-0.587785, -0.425325, 0.688191}, -{-0.309017, -0.500000, 0.809017}, -{-0.147621, -0.716567, 0.681718}, -{-0.425325, -0.688191, 0.587785}, -{-0.162460, -0.262866, 0.951056}, -{0.442863, -0.238856, 0.864188}, -{0.162460, -0.262866, 0.951056}, -{0.309017, -0.500000, 0.809017}, -{0.147621, -0.716567, 0.681718}, -{0.000000, -0.525731, 0.850651}, -{0.425325, -0.688191, 0.587785}, -{0.587785, -0.425325, 0.688191}, -{0.688191, -0.587785, 0.425325}, -{-0.955423, 0.295242, 0.000000}, -{-0.951056, 0.162460, 0.262866}, -{-1.000000, 0.000000, 0.000000}, -{-0.850651, 0.000000, 0.525731}, -{-0.955423, -0.295242, 0.000000}, -{-0.951056, -0.162460, 0.262866}, -{-0.864188, 0.442863, -0.238856}, -{-0.951056, 0.162460, -0.262866}, -{-0.809017, 0.309017, -0.500000}, -{-0.864188, -0.442863, -0.238856}, -{-0.951056, -0.162460, -0.262866}, -{-0.809017, -0.309017, -0.500000}, -{-0.681718, 0.147621, -0.716567}, -{-0.681718, -0.147621, -0.716567}, -{-0.850651, 0.000000, -0.525731}, -{-0.688191, 0.587785, -0.425325}, -{-0.587785, 0.425325, -0.688191}, -{-0.425325, 0.688191, -0.587785}, -{-0.425325, -0.688191, -0.587785}, -{-0.587785, -0.425325, -0.688191}, -{-0.688191, -0.587785, -0.425325}, +/* +Copyright (C) 1996-1997 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. + +*/ +{-0.525731, 0.000000, 0.850651}, +{-0.442863, 0.238856, 0.864188}, +{-0.295242, 0.000000, 0.955423}, +{-0.309017, 0.500000, 0.809017}, +{-0.162460, 0.262866, 0.951056}, +{0.000000, 0.000000, 1.000000}, +{0.000000, 0.850651, 0.525731}, +{-0.147621, 0.716567, 0.681718}, +{0.147621, 0.716567, 0.681718}, +{0.000000, 0.525731, 0.850651}, +{0.309017, 0.500000, 0.809017}, +{0.525731, 0.000000, 0.850651}, +{0.295242, 0.000000, 0.955423}, +{0.442863, 0.238856, 0.864188}, +{0.162460, 0.262866, 0.951056}, +{-0.681718, 0.147621, 0.716567}, +{-0.809017, 0.309017, 0.500000}, +{-0.587785, 0.425325, 0.688191}, +{-0.850651, 0.525731, 0.000000}, +{-0.864188, 0.442863, 0.238856}, +{-0.716567, 0.681718, 0.147621}, +{-0.688191, 0.587785, 0.425325}, +{-0.500000, 0.809017, 0.309017}, +{-0.238856, 0.864188, 0.442863}, +{-0.425325, 0.688191, 0.587785}, +{-0.716567, 0.681718, -0.147621}, +{-0.500000, 0.809017, -0.309017}, +{-0.525731, 0.850651, 0.000000}, +{0.000000, 0.850651, -0.525731}, +{-0.238856, 0.864188, -0.442863}, +{0.000000, 0.955423, -0.295242}, +{-0.262866, 0.951056, -0.162460}, +{0.000000, 1.000000, 0.000000}, +{0.000000, 0.955423, 0.295242}, +{-0.262866, 0.951056, 0.162460}, +{0.238856, 0.864188, 0.442863}, +{0.262866, 0.951056, 0.162460}, +{0.500000, 0.809017, 0.309017}, +{0.238856, 0.864188, -0.442863}, +{0.262866, 0.951056, -0.162460}, +{0.500000, 0.809017, -0.309017}, +{0.850651, 0.525731, 0.000000}, +{0.716567, 0.681718, 0.147621}, +{0.716567, 0.681718, -0.147621}, +{0.525731, 0.850651, 0.000000}, +{0.425325, 0.688191, 0.587785}, +{0.864188, 0.442863, 0.238856}, +{0.688191, 0.587785, 0.425325}, +{0.809017, 0.309017, 0.500000}, +{0.681718, 0.147621, 0.716567}, +{0.587785, 0.425325, 0.688191}, +{0.955423, 0.295242, 0.000000}, +{1.000000, 0.000000, 0.000000}, +{0.951056, 0.162460, 0.262866}, +{0.850651, -0.525731, 0.000000}, +{0.955423, -0.295242, 0.000000}, +{0.864188, -0.442863, 0.238856}, +{0.951056, -0.162460, 0.262866}, +{0.809017, -0.309017, 0.500000}, +{0.681718, -0.147621, 0.716567}, +{0.850651, 0.000000, 0.525731}, +{0.864188, 0.442863, -0.238856}, +{0.809017, 0.309017, -0.500000}, +{0.951056, 0.162460, -0.262866}, +{0.525731, 0.000000, -0.850651}, +{0.681718, 0.147621, -0.716567}, +{0.681718, -0.147621, -0.716567}, +{0.850651, 0.000000, -0.525731}, +{0.809017, -0.309017, -0.500000}, +{0.864188, -0.442863, -0.238856}, +{0.951056, -0.162460, -0.262866}, +{0.147621, 0.716567, -0.681718}, +{0.309017, 0.500000, -0.809017}, +{0.425325, 0.688191, -0.587785}, +{0.442863, 0.238856, -0.864188}, +{0.587785, 0.425325, -0.688191}, +{0.688191, 0.587785, -0.425325}, +{-0.147621, 0.716567, -0.681718}, +{-0.309017, 0.500000, -0.809017}, +{0.000000, 0.525731, -0.850651}, +{-0.525731, 0.000000, -0.850651}, +{-0.442863, 0.238856, -0.864188}, +{-0.295242, 0.000000, -0.955423}, +{-0.162460, 0.262866, -0.951056}, +{0.000000, 0.000000, -1.000000}, +{0.295242, 0.000000, -0.955423}, +{0.162460, 0.262866, -0.951056}, +{-0.442863, -0.238856, -0.864188}, +{-0.309017, -0.500000, -0.809017}, +{-0.162460, -0.262866, -0.951056}, +{0.000000, -0.850651, -0.525731}, +{-0.147621, -0.716567, -0.681718}, +{0.147621, -0.716567, -0.681718}, +{0.000000, -0.525731, -0.850651}, +{0.309017, -0.500000, -0.809017}, +{0.442863, -0.238856, -0.864188}, +{0.162460, -0.262866, -0.951056}, +{0.238856, -0.864188, -0.442863}, +{0.500000, -0.809017, -0.309017}, +{0.425325, -0.688191, -0.587785}, +{0.716567, -0.681718, -0.147621}, +{0.688191, -0.587785, -0.425325}, +{0.587785, -0.425325, -0.688191}, +{0.000000, -0.955423, -0.295242}, +{0.000000, -1.000000, 0.000000}, +{0.262866, -0.951056, -0.162460}, +{0.000000, -0.850651, 0.525731}, +{0.000000, -0.955423, 0.295242}, +{0.238856, -0.864188, 0.442863}, +{0.262866, -0.951056, 0.162460}, +{0.500000, -0.809017, 0.309017}, +{0.716567, -0.681718, 0.147621}, +{0.525731, -0.850651, 0.000000}, +{-0.238856, -0.864188, -0.442863}, +{-0.500000, -0.809017, -0.309017}, +{-0.262866, -0.951056, -0.162460}, +{-0.850651, -0.525731, 0.000000}, +{-0.716567, -0.681718, -0.147621}, +{-0.716567, -0.681718, 0.147621}, +{-0.525731, -0.850651, 0.000000}, +{-0.500000, -0.809017, 0.309017}, +{-0.238856, -0.864188, 0.442863}, +{-0.262866, -0.951056, 0.162460}, +{-0.864188, -0.442863, 0.238856}, +{-0.809017, -0.309017, 0.500000}, +{-0.688191, -0.587785, 0.425325}, +{-0.681718, -0.147621, 0.716567}, +{-0.442863, -0.238856, 0.864188}, +{-0.587785, -0.425325, 0.688191}, +{-0.309017, -0.500000, 0.809017}, +{-0.147621, -0.716567, 0.681718}, +{-0.425325, -0.688191, 0.587785}, +{-0.162460, -0.262866, 0.951056}, +{0.442863, -0.238856, 0.864188}, +{0.162460, -0.262866, 0.951056}, +{0.309017, -0.500000, 0.809017}, +{0.147621, -0.716567, 0.681718}, +{0.000000, -0.525731, 0.850651}, +{0.425325, -0.688191, 0.587785}, +{0.587785, -0.425325, 0.688191}, +{0.688191, -0.587785, 0.425325}, +{-0.955423, 0.295242, 0.000000}, +{-0.951056, 0.162460, 0.262866}, +{-1.000000, 0.000000, 0.000000}, +{-0.850651, 0.000000, 0.525731}, +{-0.955423, -0.295242, 0.000000}, +{-0.951056, -0.162460, 0.262866}, +{-0.864188, 0.442863, -0.238856}, +{-0.951056, 0.162460, -0.262866}, +{-0.809017, 0.309017, -0.500000}, +{-0.864188, -0.442863, -0.238856}, +{-0.951056, -0.162460, -0.262866}, +{-0.809017, -0.309017, -0.500000}, +{-0.681718, 0.147621, -0.716567}, +{-0.681718, -0.147621, -0.716567}, +{-0.850651, 0.000000, -0.525731}, +{-0.688191, 0.587785, -0.425325}, +{-0.587785, 0.425325, -0.688191}, +{-0.425325, 0.688191, -0.587785}, +{-0.425325, -0.688191, -0.587785}, +{-0.587785, -0.425325, -0.688191}, +{-0.688191, -0.587785, -0.425325}, diff --git a/source/asm_draw.h b/source/asm_draw.h index fe14e062..5534ab63 100644 --- a/source/asm_draw.h +++ b/source/asm_draw.h @@ -1,151 +1,151 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// -// asm_draw.h -// -// Include file for asm drawing routines. -// - -// -// !!! note that this file must match the corresponding C structures at all -// times !!! -// - -// !!! if this is changed, it must be changed in r_local.h too !!! -#define NEAR_CLIP 0.01 - -// !!! if this is changed, it must be changed in r_local.h too !!! -#define CYCLE 128 - -// espan_t structure -// !!! if this is changed, it must be changed in r_shared.h too !!! -#define espan_t_u 0 -#define espan_t_v 4 -#define espan_t_count 8 -#define espan_t_pnext 12 -#define espan_t_size 16 - -// sspan_t structure -// !!! if this is changed, it must be changed in d_local.h too !!! -#define sspan_t_u 0 -#define sspan_t_v 4 -#define sspan_t_count 8 -#define sspan_t_size 12 - -// spanpackage_t structure -// !!! if this is changed, it must be changed in d_polyset.c too !!! -#define spanpackage_t_pdest 0 -#define spanpackage_t_pz 4 -#define spanpackage_t_count 8 -#define spanpackage_t_ptex 12 -#define spanpackage_t_sfrac 16 -#define spanpackage_t_tfrac 20 -#define spanpackage_t_light 24 -#define spanpackage_t_zi 28 -#define spanpackage_t_size 32 - -// edge_t structure -// !!! if this is changed, it must be changed in r_shared.h too !!! -#define et_u 0 -#define et_u_step 4 -#define et_prev 8 -#define et_next 12 -#define et_surfs 16 -#define et_nextremove 20 -#define et_nearzi 24 -#define et_owner 28 -#define et_size 32 - -// surf_t structure -// !!! if this is changed, it must be changed in r_shared.h too !!! -#define SURF_T_SHIFT 6 -#define st_next 0 -#define st_prev 4 -#define st_spans 8 -#define st_key 12 -#define st_last_u 16 -#define st_spanstate 20 -#define st_flags 24 -#define st_data 28 -#define st_entity 32 -#define st_nearzi 36 -#define st_insubmodel 40 -#define st_d_ziorigin 44 -#define st_d_zistepu 48 -#define st_d_zistepv 52 -#define st_pad 56 -#define st_size 64 - -// clipplane_t structure -// !!! if this is changed, it must be changed in r_local.h too !!! -#define cp_normal 0 -#define cp_dist 12 -#define cp_next 16 -#define cp_leftedge 20 -#define cp_rightedge 21 -#define cp_reserved 22 -#define cp_size 24 - -// medge_t structure -// !!! if this is changed, it must be changed in model.h too !!! -#define me_v 0 -#define me_cachededgeoffset 4 -#define me_size 8 - -// mvertex_t structure -// !!! if this is changed, it must be changed in model.h too !!! -#define mv_position 0 -#define mv_size 12 - -// refdef_t structure -// !!! if this is changed, it must be changed in render.h too !!! -#define rd_vrect 0 -#define rd_aliasvrect 20 -#define rd_vrectright 40 -#define rd_vrectbottom 44 -#define rd_aliasvrectright 48 -#define rd_aliasvrectbottom 52 -#define rd_vrectrightedge 56 -#define rd_fvrectx 60 -#define rd_fvrecty 64 -#define rd_fvrectx_adj 68 -#define rd_fvrecty_adj 72 -#define rd_vrect_x_adj_shift20 76 -#define rd_vrectright_adj_shift20 80 -#define rd_fvrectright_adj 84 -#define rd_fvrectbottom_adj 88 -#define rd_fvrectright 92 -#define rd_fvrectbottom 96 -#define rd_horizontalFieldOfView 100 -#define rd_xOrigin 104 -#define rd_yOrigin 108 -#define rd_vieworg 112 -#define rd_viewangles 124 -#define rd_ambientlight 136 -#define rd_size 140 - -// mtriangle_t structure -// !!! if this is changed, it must be changed in model.h too !!! -#define mtri_facesfront 0 -#define mtri_vertindex 4 -#define mtri_size 16 // !!! if this changes, array indexing in !!! - // !!! d_polysa.s must be changed to match !!! -#define mtri_shift 4 - +/* +Copyright (C) 1996-1997 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. + +*/ +// +// asm_draw.h +// +// Include file for asm drawing routines. +// + +// +// !!! note that this file must match the corresponding C structures at all +// times !!! +// + +// !!! if this is changed, it must be changed in r_local.h too !!! +#define NEAR_CLIP 0.01 + +// !!! if this is changed, it must be changed in r_local.h too !!! +#define CYCLE 128 + +// espan_t structure +// !!! if this is changed, it must be changed in r_shared.h too !!! +#define espan_t_u 0 +#define espan_t_v 4 +#define espan_t_count 8 +#define espan_t_pnext 12 +#define espan_t_size 16 + +// sspan_t structure +// !!! if this is changed, it must be changed in d_local.h too !!! +#define sspan_t_u 0 +#define sspan_t_v 4 +#define sspan_t_count 8 +#define sspan_t_size 12 + +// spanpackage_t structure +// !!! if this is changed, it must be changed in d_polyset.c too !!! +#define spanpackage_t_pdest 0 +#define spanpackage_t_pz 4 +#define spanpackage_t_count 8 +#define spanpackage_t_ptex 12 +#define spanpackage_t_sfrac 16 +#define spanpackage_t_tfrac 20 +#define spanpackage_t_light 24 +#define spanpackage_t_zi 28 +#define spanpackage_t_size 32 + +// edge_t structure +// !!! if this is changed, it must be changed in r_shared.h too !!! +#define et_u 0 +#define et_u_step 4 +#define et_prev 8 +#define et_next 12 +#define et_surfs 16 +#define et_nextremove 20 +#define et_nearzi 24 +#define et_owner 28 +#define et_size 32 + +// surf_t structure +// !!! if this is changed, it must be changed in r_shared.h too !!! +#define SURF_T_SHIFT 6 +#define st_next 0 +#define st_prev 4 +#define st_spans 8 +#define st_key 12 +#define st_last_u 16 +#define st_spanstate 20 +#define st_flags 24 +#define st_data 28 +#define st_entity 32 +#define st_nearzi 36 +#define st_insubmodel 40 +#define st_d_ziorigin 44 +#define st_d_zistepu 48 +#define st_d_zistepv 52 +#define st_pad 56 +#define st_size 64 + +// clipplane_t structure +// !!! if this is changed, it must be changed in r_local.h too !!! +#define cp_normal 0 +#define cp_dist 12 +#define cp_next 16 +#define cp_leftedge 20 +#define cp_rightedge 21 +#define cp_reserved 22 +#define cp_size 24 + +// medge_t structure +// !!! if this is changed, it must be changed in model.h too !!! +#define me_v 0 +#define me_cachededgeoffset 4 +#define me_size 8 + +// mvertex_t structure +// !!! if this is changed, it must be changed in model.h too !!! +#define mv_position 0 +#define mv_size 12 + +// refdef_t structure +// !!! if this is changed, it must be changed in render.h too !!! +#define rd_vrect 0 +#define rd_aliasvrect 20 +#define rd_vrectright 40 +#define rd_vrectbottom 44 +#define rd_aliasvrectright 48 +#define rd_aliasvrectbottom 52 +#define rd_vrectrightedge 56 +#define rd_fvrectx 60 +#define rd_fvrecty 64 +#define rd_fvrectx_adj 68 +#define rd_fvrecty_adj 72 +#define rd_vrect_x_adj_shift20 76 +#define rd_vrectright_adj_shift20 80 +#define rd_fvrectright_adj 84 +#define rd_fvrectbottom_adj 88 +#define rd_fvrectright 92 +#define rd_fvrectbottom 96 +#define rd_horizontalFieldOfView 100 +#define rd_xOrigin 104 +#define rd_yOrigin 108 +#define rd_vieworg 112 +#define rd_viewangles 124 +#define rd_ambientlight 136 +#define rd_size 140 + +// mtriangle_t structure +// !!! if this is changed, it must be changed in model.h too !!! +#define mtri_facesfront 0 +#define mtri_vertindex 4 +#define mtri_size 16 // !!! if this changes, array indexing in !!! + // !!! d_polysa.s must be changed to match !!! +#define mtri_shift 4 + diff --git a/source/asm_i386.h b/source/asm_i386.h index a5c24149..03dc2aae 100644 --- a/source/asm_i386.h +++ b/source/asm_i386.h @@ -1,97 +1,97 @@ -/* -Copyright (C) 1996-1997 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 __ASM_I386__ -#define __ASM_I386__ - -#ifdef ELF -#define C(label) label -#else -#define C(label) _##label -#endif - -// -// !!! note that this file must match the corresponding C structures at all -// times !!! -// - -// plane_t structure -// !!! if this is changed, it must be changed in model.h too !!! -// !!! if the size of this is changed, the array lookup in SV_HullPointContents -// must be changed too !!! -#define pl_normal 0 -#define pl_dist 12 -#define pl_type 16 -#define pl_signbits 17 -#define pl_pad 18 -#define pl_size 20 - -// hull_t structure -// !!! if this is changed, it must be changed in model.h too !!! -#define hu_clipnodes 0 -#define hu_planes 4 -#define hu_firstclipnode 8 -#define hu_lastclipnode 12 -#define hu_clip_mins 16 -#define hu_clip_maxs 28 -#define hu_size 40 - -// dnode_t structure -// !!! if this is changed, it must be changed in bspfile.h too !!! -#define nd_planenum 0 -#define nd_children 4 -#define nd_mins 8 -#define nd_maxs 20 -#define nd_firstface 32 -#define nd_numfaces 36 -#define nd_size 40 - -// sfxcache_t structure -// !!! if this is changed, it much be changed in sound.h too !!! -#define sfxc_length 0 -#define sfxc_loopstart 4 -#define sfxc_speed 8 -#define sfxc_width 12 -#define sfxc_stereo 16 -#define sfxc_data 20 - -// channel_t structure -// !!! if this is changed, it much be changed in sound.h too !!! -#define ch_sfx 0 -#define ch_leftvol 4 -#define ch_rightvol 8 -#define ch_end 12 -#define ch_pos 16 -#define ch_looping 20 -#define ch_entnum 24 -#define ch_entchannel 28 -#define ch_origin 32 -#define ch_dist_mult 44 -#define ch_master_vol 48 -#define ch_size 52 - -// portable_samplepair_t structure -// !!! if this is changed, it much be changed in sound.h too !!! -#define psp_left 0 -#define psp_right 4 -#define psp_size 8 - -#endif - +/* +Copyright (C) 1996-1997 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 __ASM_I386__ +#define __ASM_I386__ + +#ifdef ELF +#define C(label) label +#else +#define C(label) _##label +#endif + +// +// !!! note that this file must match the corresponding C structures at all +// times !!! +// + +// plane_t structure +// !!! if this is changed, it must be changed in model.h too !!! +// !!! if the size of this is changed, the array lookup in SV_HullPointContents +// must be changed too !!! +#define pl_normal 0 +#define pl_dist 12 +#define pl_type 16 +#define pl_signbits 17 +#define pl_pad 18 +#define pl_size 20 + +// hull_t structure +// !!! if this is changed, it must be changed in model.h too !!! +#define hu_clipnodes 0 +#define hu_planes 4 +#define hu_firstclipnode 8 +#define hu_lastclipnode 12 +#define hu_clip_mins 16 +#define hu_clip_maxs 28 +#define hu_size 40 + +// dnode_t structure +// !!! if this is changed, it must be changed in bspfile.h too !!! +#define nd_planenum 0 +#define nd_children 4 +#define nd_mins 8 +#define nd_maxs 20 +#define nd_firstface 32 +#define nd_numfaces 36 +#define nd_size 40 + +// sfxcache_t structure +// !!! if this is changed, it much be changed in sound.h too !!! +#define sfxc_length 0 +#define sfxc_loopstart 4 +#define sfxc_speed 8 +#define sfxc_width 12 +#define sfxc_stereo 16 +#define sfxc_data 20 + +// channel_t structure +// !!! if this is changed, it much be changed in sound.h too !!! +#define ch_sfx 0 +#define ch_leftvol 4 +#define ch_rightvol 8 +#define ch_end 12 +#define ch_pos 16 +#define ch_looping 20 +#define ch_entnum 24 +#define ch_entchannel 28 +#define ch_origin 32 +#define ch_dist_mult 44 +#define ch_master_vol 48 +#define ch_size 52 + +// portable_samplepair_t structure +// !!! if this is changed, it much be changed in sound.h too !!! +#define psp_left 0 +#define psp_right 4 +#define psp_size 8 + +#endif + diff --git a/source/block16.h b/source/block16.h index 73f1acf9..98a1cf70 100644 --- a/source/block16.h +++ b/source/block16.h @@ -1,123 +1,123 @@ -LEnter16_16: - movb (%esi),%al - movb (%esi,%ebx,),%cl - movb %dh,%ah - addl %ebp,%edx - movb %dh,%ch - leal (%esi,%ebx,2),%esi - movw 0x12345678(,%eax,2),%ax -LBPatch0: - addl %ebp,%edx - movw %ax,(%edi) - movw 0x12345678(,%ecx,2),%cx -LBPatch1: - movw %cx,2(%edi) - addl $0x4,%edi - - movb (%esi),%al - movb (%esi,%ebx,),%cl - movb %dh,%ah - addl %ebp,%edx - movb %dh,%ch - leal (%esi,%ebx,2),%esi - movw 0x12345678(,%eax,2),%ax -LBPatch2: - addl %ebp,%edx - movw %ax,(%edi) - movw 0x12345678(,%ecx,2),%cx -LBPatch3: - movw %cx,2(%edi) - addl $0x4,%edi - - movb (%esi),%al - movb (%esi,%ebx,),%cl - movb %dh,%ah - addl %ebp,%edx - movb %dh,%ch - leal (%esi,%ebx,2),%esi - movw 0x12345678(,%eax,2),%ax -LBPatch4: - addl %ebp,%edx - movw %ax,(%edi) - movw 0x12345678(,%ecx,2),%cx -LBPatch5: - movw %cx,2(%edi) - addl $0x4,%edi - - movb (%esi),%al - movb (%esi,%ebx,),%cl - movb %dh,%ah - addl %ebp,%edx - movb %dh,%ch - leal (%esi,%ebx,2),%esi - movw 0x12345678(,%eax,2),%ax -LBPatch6: - addl %ebp,%edx - movw %ax,(%edi) - movw 0x12345678(,%ecx,2),%cx -LBPatch7: - movw %cx,2(%edi) - addl $0x4,%edi - -LEnter8_16: - movb (%esi),%al - movb (%esi,%ebx,),%cl - movb %dh,%ah - addl %ebp,%edx - movb %dh,%ch - leal (%esi,%ebx,2),%esi - movw 0x12345678(,%eax,2),%ax -LBPatch8: - addl %ebp,%edx - movw %ax,(%edi) - movw 0x12345678(,%ecx,2),%cx -LBPatch9: - movw %cx,2(%edi) - addl $0x4,%edi - - movb (%esi),%al - movb (%esi,%ebx,),%cl - movb %dh,%ah - addl %ebp,%edx - movb %dh,%ch - leal (%esi,%ebx,2),%esi - movw 0x12345678(,%eax,2),%ax -LBPatch10: - addl %ebp,%edx - movw %ax,(%edi) - movw 0x12345678(,%ecx,2),%cx -LBPatch11: - movw %cx,2(%edi) - addl $0x4,%edi - -LEnter4_16: - movb (%esi),%al - movb (%esi,%ebx,),%cl - movb %dh,%ah - addl %ebp,%edx - movb %dh,%ch - leal (%esi,%ebx,2),%esi - movw 0x12345678(,%eax,2),%ax -LBPatch12: - addl %ebp,%edx - movw %ax,(%edi) - movw 0x12345678(,%ecx,2),%cx -LBPatch13: - movw %cx,2(%edi) - addl $0x4,%edi - -LEnter2_16: - movb (%esi),%al - movb (%esi,%ebx,),%cl - movb %dh,%ah - addl %ebp,%edx - movb %dh,%ch - leal (%esi,%ebx,2),%esi - movw 0x12345678(,%eax,2),%ax -LBPatch14: - addl %ebp,%edx - movw %ax,(%edi) - movw 0x12345678(,%ecx,2),%cx -LBPatch15: - movw %cx,2(%edi) - addl $0x4,%edi +LEnter16_16: + movb (%esi),%al + movb (%esi,%ebx,),%cl + movb %dh,%ah + addl %ebp,%edx + movb %dh,%ch + leal (%esi,%ebx,2),%esi + movw 0x12345678(,%eax,2),%ax +LBPatch0: + addl %ebp,%edx + movw %ax,(%edi) + movw 0x12345678(,%ecx,2),%cx +LBPatch1: + movw %cx,2(%edi) + addl $0x4,%edi + + movb (%esi),%al + movb (%esi,%ebx,),%cl + movb %dh,%ah + addl %ebp,%edx + movb %dh,%ch + leal (%esi,%ebx,2),%esi + movw 0x12345678(,%eax,2),%ax +LBPatch2: + addl %ebp,%edx + movw %ax,(%edi) + movw 0x12345678(,%ecx,2),%cx +LBPatch3: + movw %cx,2(%edi) + addl $0x4,%edi + + movb (%esi),%al + movb (%esi,%ebx,),%cl + movb %dh,%ah + addl %ebp,%edx + movb %dh,%ch + leal (%esi,%ebx,2),%esi + movw 0x12345678(,%eax,2),%ax +LBPatch4: + addl %ebp,%edx + movw %ax,(%edi) + movw 0x12345678(,%ecx,2),%cx +LBPatch5: + movw %cx,2(%edi) + addl $0x4,%edi + + movb (%esi),%al + movb (%esi,%ebx,),%cl + movb %dh,%ah + addl %ebp,%edx + movb %dh,%ch + leal (%esi,%ebx,2),%esi + movw 0x12345678(,%eax,2),%ax +LBPatch6: + addl %ebp,%edx + movw %ax,(%edi) + movw 0x12345678(,%ecx,2),%cx +LBPatch7: + movw %cx,2(%edi) + addl $0x4,%edi + +LEnter8_16: + movb (%esi),%al + movb (%esi,%ebx,),%cl + movb %dh,%ah + addl %ebp,%edx + movb %dh,%ch + leal (%esi,%ebx,2),%esi + movw 0x12345678(,%eax,2),%ax +LBPatch8: + addl %ebp,%edx + movw %ax,(%edi) + movw 0x12345678(,%ecx,2),%cx +LBPatch9: + movw %cx,2(%edi) + addl $0x4,%edi + + movb (%esi),%al + movb (%esi,%ebx,),%cl + movb %dh,%ah + addl %ebp,%edx + movb %dh,%ch + leal (%esi,%ebx,2),%esi + movw 0x12345678(,%eax,2),%ax +LBPatch10: + addl %ebp,%edx + movw %ax,(%edi) + movw 0x12345678(,%ecx,2),%cx +LBPatch11: + movw %cx,2(%edi) + addl $0x4,%edi + +LEnter4_16: + movb (%esi),%al + movb (%esi,%ebx,),%cl + movb %dh,%ah + addl %ebp,%edx + movb %dh,%ch + leal (%esi,%ebx,2),%esi + movw 0x12345678(,%eax,2),%ax +LBPatch12: + addl %ebp,%edx + movw %ax,(%edi) + movw 0x12345678(,%ecx,2),%cx +LBPatch13: + movw %cx,2(%edi) + addl $0x4,%edi + +LEnter2_16: + movb (%esi),%al + movb (%esi,%ebx,),%cl + movb %dh,%ah + addl %ebp,%edx + movb %dh,%ch + leal (%esi,%ebx,2),%esi + movw 0x12345678(,%eax,2),%ax +LBPatch14: + addl %ebp,%edx + movw %ax,(%edi) + movw 0x12345678(,%ecx,2),%cx +LBPatch15: + movw %cx,2(%edi) + addl $0x4,%edi diff --git a/source/block8.h b/source/block8.h index 0fda5239..d501379a 100644 --- a/source/block8.h +++ b/source/block8.h @@ -1,124 +1,124 @@ -LEnter16_8: - movb (%esi),%al - movb (%esi,%ebx,),%cl - movb %dh,%ah - addl %ebp,%edx - movb %dh,%ch - leal (%esi,%ebx,2),%esi - movb 0x12345678(%eax),%al -LBPatch0: - addl %ebp,%edx - movb %al,(%edi) - movb 0x12345678(%ecx),%cl -LBPatch1: - movb %cl,1(%edi) - addl $0x2,%edi - - movb (%esi),%al - movb (%esi,%ebx,),%cl - movb %dh,%ah - addl %ebp,%edx - movb %dh,%ch - leal (%esi,%ebx,2),%esi - movb 0x12345678(%eax),%al -LBPatch2: - addl %ebp,%edx - movb %al,(%edi) - movb 0x12345678(%ecx),%cl -LBPatch3: - movb %cl,1(%edi) - addl $0x2,%edi - - movb (%esi),%al - movb (%esi,%ebx,),%cl - movb %dh,%ah - addl %ebp,%edx - movb %dh,%ch - leal (%esi,%ebx,2),%esi - movb 0x12345678(%eax),%al -LBPatch4: - addl %ebp,%edx - movb %al,(%edi) - movb 0x12345678(%ecx),%cl -LBPatch5: - movb %cl,1(%edi) - addl $0x2,%edi - - movb (%esi),%al - movb (%esi,%ebx,),%cl - movb %dh,%ah - addl %ebp,%edx - movb %dh,%ch - leal (%esi,%ebx,2),%esi - movb 0x12345678(%eax),%al -LBPatch6: - addl %ebp,%edx - movb %al,(%edi) - movb 0x12345678(%ecx),%cl -LBPatch7: - movb %cl,1(%edi) - addl $0x2,%edi - -LEnter8_8: - movb (%esi),%al - movb (%esi,%ebx,),%cl - movb %dh,%ah - addl %ebp,%edx - movb %dh,%ch - leal (%esi,%ebx,2),%esi - movb 0x12345678(%eax),%al -LBPatch8: - addl %ebp,%edx - movb %al,(%edi) - movb 0x12345678(%ecx),%cl -LBPatch9: - movb %cl,1(%edi) - addl $0x2,%edi - - movb (%esi),%al - movb (%esi,%ebx,),%cl - movb %dh,%ah - addl %ebp,%edx - movb %dh,%ch - leal (%esi,%ebx,2),%esi - movb 0x12345678(%eax),%al -LBPatch10: - addl %ebp,%edx - movb %al,(%edi) - movb 0x12345678(%ecx),%cl -LBPatch11: - movb %cl,1(%edi) - addl $0x2,%edi - -LEnter4_8: - movb (%esi),%al - movb (%esi,%ebx,),%cl - movb %dh,%ah - addl %ebp,%edx - movb %dh,%ch - leal (%esi,%ebx,2),%esi - movb 0x12345678(%eax),%al -LBPatch12: - addl %ebp,%edx - movb %al,(%edi) - movb 0x12345678(%ecx),%cl -LBPatch13: - movb %cl,1(%edi) - addl $0x2,%edi - -LEnter2_8: - movb (%esi),%al - movb (%esi,%ebx,),%cl - movb %dh,%ah - addl %ebp,%edx - movb %dh,%ch - leal (%esi,%ebx,2),%esi - movb 0x12345678(%eax),%al -LBPatch14: - addl %ebp,%edx - movb %al,(%edi) - movb 0x12345678(%ecx),%cl -LBPatch15: - movb %cl,1(%edi) - addl $0x2,%edi - +LEnter16_8: + movb (%esi),%al + movb (%esi,%ebx,),%cl + movb %dh,%ah + addl %ebp,%edx + movb %dh,%ch + leal (%esi,%ebx,2),%esi + movb 0x12345678(%eax),%al +LBPatch0: + addl %ebp,%edx + movb %al,(%edi) + movb 0x12345678(%ecx),%cl +LBPatch1: + movb %cl,1(%edi) + addl $0x2,%edi + + movb (%esi),%al + movb (%esi,%ebx,),%cl + movb %dh,%ah + addl %ebp,%edx + movb %dh,%ch + leal (%esi,%ebx,2),%esi + movb 0x12345678(%eax),%al +LBPatch2: + addl %ebp,%edx + movb %al,(%edi) + movb 0x12345678(%ecx),%cl +LBPatch3: + movb %cl,1(%edi) + addl $0x2,%edi + + movb (%esi),%al + movb (%esi,%ebx,),%cl + movb %dh,%ah + addl %ebp,%edx + movb %dh,%ch + leal (%esi,%ebx,2),%esi + movb 0x12345678(%eax),%al +LBPatch4: + addl %ebp,%edx + movb %al,(%edi) + movb 0x12345678(%ecx),%cl +LBPatch5: + movb %cl,1(%edi) + addl $0x2,%edi + + movb (%esi),%al + movb (%esi,%ebx,),%cl + movb %dh,%ah + addl %ebp,%edx + movb %dh,%ch + leal (%esi,%ebx,2),%esi + movb 0x12345678(%eax),%al +LBPatch6: + addl %ebp,%edx + movb %al,(%edi) + movb 0x12345678(%ecx),%cl +LBPatch7: + movb %cl,1(%edi) + addl $0x2,%edi + +LEnter8_8: + movb (%esi),%al + movb (%esi,%ebx,),%cl + movb %dh,%ah + addl %ebp,%edx + movb %dh,%ch + leal (%esi,%ebx,2),%esi + movb 0x12345678(%eax),%al +LBPatch8: + addl %ebp,%edx + movb %al,(%edi) + movb 0x12345678(%ecx),%cl +LBPatch9: + movb %cl,1(%edi) + addl $0x2,%edi + + movb (%esi),%al + movb (%esi,%ebx,),%cl + movb %dh,%ah + addl %ebp,%edx + movb %dh,%ch + leal (%esi,%ebx,2),%esi + movb 0x12345678(%eax),%al +LBPatch10: + addl %ebp,%edx + movb %al,(%edi) + movb 0x12345678(%ecx),%cl +LBPatch11: + movb %cl,1(%edi) + addl $0x2,%edi + +LEnter4_8: + movb (%esi),%al + movb (%esi,%ebx,),%cl + movb %dh,%ah + addl %ebp,%edx + movb %dh,%ch + leal (%esi,%ebx,2),%esi + movb 0x12345678(%eax),%al +LBPatch12: + addl %ebp,%edx + movb %al,(%edi) + movb 0x12345678(%ecx),%cl +LBPatch13: + movb %cl,1(%edi) + addl $0x2,%edi + +LEnter2_8: + movb (%esi),%al + movb (%esi,%ebx,),%cl + movb %dh,%ah + addl %ebp,%edx + movb %dh,%ch + leal (%esi,%ebx,2),%esi + movb 0x12345678(%eax),%al +LBPatch14: + addl %ebp,%edx + movb %al,(%edi) + movb 0x12345678(%ecx),%cl +LBPatch15: + movb %cl,1(%edi) + addl $0x2,%edi + diff --git a/source/bothdefs.h b/source/bothdefs.h index 1687a217..d7230b52 100644 --- a/source/bothdefs.h +++ b/source/bothdefs.h @@ -1,132 +1,132 @@ -/* -Copyright (C) 1996-1997 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. - -*/ - -// defs common to client and server - - -#if id386 -#define UNALIGNED_OK 1 // set to 0 if unaligned accesses are not supported -#else -#define UNALIGNED_OK 0 -#endif - -// !!! if this is changed, it must be changed in d_ifacea.h too !!! -#define CACHE_SIZE 32 // used to align key data structures - -#define UNUSED(x) (x = x) // for pesky compiler / lint warnings - -#define MINIMUM_MEMORY 0x550000 - - -#define MAX_QPATH 64 // max length of a quake game pathname -#define MAX_OSPATH 128 // max length of a filesystem pathname - -#define ON_EPSILON 0.1 // point on plane side epsilon - -#define MAX_MSGLEN 1450 // max length of a reliable message -#define MAX_DATAGRAM 1450 // max length of unreliable message - -// -// per-level limits -// -#define MAX_EDICTS 768 // FIXME: ouch! ouch! ouch! -#define MAX_LIGHTSTYLES 64 -#define MAX_MODELS 256 // these are sent over the net as bytes -#define MAX_SOUNDS 256 // so they cannot be blindly increased - -#define MAX_STYLESTRING 64 - -// -// stats are integers communicated to the client by the server -// -#define MAX_CL_STATS 32 -#define STAT_HEALTH 0 -//define STAT_FRAGS 1 -#define STAT_WEAPON 2 -#define STAT_AMMO 3 -#define STAT_ARMOR 4 -//define STAT_WEAPONFRAME 5 -#define STAT_SHELLS 6 -#define STAT_NAILS 7 -#define STAT_ROCKETS 8 -#define STAT_CELLS 9 -#define STAT_ACTIVEWEAPON 10 -#define STAT_TOTALSECRETS 11 -#define STAT_TOTALMONSTERS 12 -#define STAT_SECRETS 13 // bumped on client side by svc_foundsecret -#define STAT_MONSTERS 14 // bumped by svc_killedmonster -#define STAT_ITEMS 15 -//define STAT_VIEWHEIGHT 16 - - -// -// item flags -// -#define IT_SHOTGUN 1 -#define IT_SUPER_SHOTGUN 2 -#define IT_NAILGUN 4 -#define IT_SUPER_NAILGUN 8 - -#define IT_GRENADE_LAUNCHER 16 -#define IT_ROCKET_LAUNCHER 32 -#define IT_LIGHTNING 64 -#define IT_SUPER_LIGHTNING 128 - -#define IT_SHELLS 256 -#define IT_NAILS 512 -#define IT_ROCKETS 1024 -#define IT_CELLS 2048 - -#define IT_AXE 4096 - -#define IT_ARMOR1 8192 -#define IT_ARMOR2 16384 -#define IT_ARMOR3 32768 - -#define IT_SUPERHEALTH 65536 - -#define IT_KEY1 131072 -#define IT_KEY2 262144 - -#define IT_INVISIBILITY 524288 - -#define IT_INVULNERABILITY 1048576 -#define IT_SUIT 2097152 -#define IT_QUAD 4194304 - -#define IT_SIGIL1 (1<<28) - -#define IT_SIGIL2 (1<<29) -#define IT_SIGIL3 (1<<30) -#define IT_SIGIL4 (1<<31) - -// -// print flags -// -#define PRINT_LOW 0 // pickup messages -#define PRINT_MEDIUM 1 // death messages -#define PRINT_HIGH 2 // critical messages -#define PRINT_CHAT 3 // chat messages - - -// game types sent by serverinfo -// these determine which intermission screen plays -#define GAME_COOP 0 -#define GAME_DEATHMATCH 1 +/* +Copyright (C) 1996-1997 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. + +*/ + +// defs common to client and server + + +#if id386 +#define UNALIGNED_OK 1 // set to 0 if unaligned accesses are not supported +#else +#define UNALIGNED_OK 0 +#endif + +// !!! if this is changed, it must be changed in d_ifacea.h too !!! +#define CACHE_SIZE 32 // used to align key data structures + +#define UNUSED(x) (x = x) // for pesky compiler / lint warnings + +#define MINIMUM_MEMORY 0x550000 + + +#define MAX_QPATH 64 // max length of a quake game pathname +#define MAX_OSPATH 128 // max length of a filesystem pathname + +#define ON_EPSILON 0.1 // point on plane side epsilon + +#define MAX_MSGLEN 1450 // max length of a reliable message +#define MAX_DATAGRAM 1450 // max length of unreliable message + +// +// per-level limits +// +#define MAX_EDICTS 768 // FIXME: ouch! ouch! ouch! +#define MAX_LIGHTSTYLES 64 +#define MAX_MODELS 256 // these are sent over the net as bytes +#define MAX_SOUNDS 256 // so they cannot be blindly increased + +#define MAX_STYLESTRING 64 + +// +// stats are integers communicated to the client by the server +// +#define MAX_CL_STATS 32 +#define STAT_HEALTH 0 +//define STAT_FRAGS 1 +#define STAT_WEAPON 2 +#define STAT_AMMO 3 +#define STAT_ARMOR 4 +//define STAT_WEAPONFRAME 5 +#define STAT_SHELLS 6 +#define STAT_NAILS 7 +#define STAT_ROCKETS 8 +#define STAT_CELLS 9 +#define STAT_ACTIVEWEAPON 10 +#define STAT_TOTALSECRETS 11 +#define STAT_TOTALMONSTERS 12 +#define STAT_SECRETS 13 // bumped on client side by svc_foundsecret +#define STAT_MONSTERS 14 // bumped by svc_killedmonster +#define STAT_ITEMS 15 +//define STAT_VIEWHEIGHT 16 + + +// +// item flags +// +#define IT_SHOTGUN 1 +#define IT_SUPER_SHOTGUN 2 +#define IT_NAILGUN 4 +#define IT_SUPER_NAILGUN 8 + +#define IT_GRENADE_LAUNCHER 16 +#define IT_ROCKET_LAUNCHER 32 +#define IT_LIGHTNING 64 +#define IT_SUPER_LIGHTNING 128 + +#define IT_SHELLS 256 +#define IT_NAILS 512 +#define IT_ROCKETS 1024 +#define IT_CELLS 2048 + +#define IT_AXE 4096 + +#define IT_ARMOR1 8192 +#define IT_ARMOR2 16384 +#define IT_ARMOR3 32768 + +#define IT_SUPERHEALTH 65536 + +#define IT_KEY1 131072 +#define IT_KEY2 262144 + +#define IT_INVISIBILITY 524288 + +#define IT_INVULNERABILITY 1048576 +#define IT_SUIT 2097152 +#define IT_QUAD 4194304 + +#define IT_SIGIL1 (1<<28) + +#define IT_SIGIL2 (1<<29) +#define IT_SIGIL3 (1<<30) +#define IT_SIGIL4 (1<<31) + +// +// print flags +// +#define PRINT_LOW 0 // pickup messages +#define PRINT_MEDIUM 1 // death messages +#define PRINT_HIGH 2 // critical messages +#define PRINT_CHAT 3 // chat messages + + +// game types sent by serverinfo +// these determine which intermission screen plays +#define GAME_COOP 0 +#define GAME_DEATHMATCH 1 diff --git a/source/bspfile.h b/source/bspfile.h index b46432c5..20d1dce5 100644 --- a/source/bspfile.h +++ b/source/bspfile.h @@ -1,275 +1,275 @@ -/* -Copyright (C) 1996-1997 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 __bspfile_h -#define __bspfile_h - -// upper design bounds - -#define MAX_MAP_HULLS 4 - -#define MAX_MAP_MODELS 256 -#define MAX_MAP_BRUSHES 4096 -#define MAX_MAP_ENTITIES 1024 -#define MAX_MAP_ENTSTRING 65536 - -#define MAX_MAP_PLANES 8192 -#define MAX_MAP_NODES 32767 // because negative shorts are contents -#define MAX_MAP_CLIPNODES 32767 // -#define MAX_MAP_LEAFS 32767 // -#define MAX_MAP_VERTS 65535 -#define MAX_MAP_FACES 65535 -#define MAX_MAP_MARKSURFACES 65535 -#define MAX_MAP_TEXINFO 4096 -#define MAX_MAP_EDGES 256000 -#define MAX_MAP_SURFEDGES 512000 -#define MAX_MAP_MIPTEX 0x200000 -#define MAX_MAP_LIGHTING 0x100000 -#define MAX_MAP_VISIBILITY 0x100000 - -// key / value pair sizes - -#define MAX_KEY 32 -#define MAX_VALUE 1024 - - -//============================================================================= - - -#define BSPVERSION 29 - -typedef struct -{ - int fileofs, filelen; -} lump_t; - -#define LUMP_ENTITIES 0 -#define LUMP_PLANES 1 -#define LUMP_TEXTURES 2 -#define LUMP_VERTEXES 3 -#define LUMP_VISIBILITY 4 -#define LUMP_NODES 5 -#define LUMP_TEXINFO 6 -#define LUMP_FACES 7 -#define LUMP_LIGHTING 8 -#define LUMP_CLIPNODES 9 -#define LUMP_LEAFS 10 -#define LUMP_MARKSURFACES 11 -#define LUMP_EDGES 12 -#define LUMP_SURFEDGES 13 -#define LUMP_MODELS 14 - -#define HEADER_LUMPS 15 - -typedef struct -{ - float mins[3], maxs[3]; - float origin[3]; - int headnode[MAX_MAP_HULLS]; - int visleafs; // not including the solid leaf 0 - int firstface, numfaces; -} dmodel_t; - -typedef struct -{ - int version; - lump_t lumps[HEADER_LUMPS]; -} dheader_t; - -typedef struct -{ - int nummiptex; - int dataofs[4]; // [nummiptex] -} dmiptexlump_t; - -#define MIPLEVELS 4 -typedef struct miptex_s -{ - char name[16]; - unsigned width, height; - unsigned offsets[MIPLEVELS]; // four mip maps stored -} miptex_t; - - -typedef struct -{ - float point[3]; -} dvertex_t; - - -// 0-2 are axial planes -#define PLANE_X 0 -#define PLANE_Y 1 -#define PLANE_Z 2 - -// 3-5 are non-axial planes snapped to the nearest -#define PLANE_ANYX 3 -#define PLANE_ANYY 4 -#define PLANE_ANYZ 5 - -typedef struct -{ - float normal[3]; - float dist; - int type; // PLANE_X - PLANE_ANYZ ?remove? trivial to regenerate -} dplane_t; - - - -#define CONTENTS_EMPTY -1 -#define CONTENTS_SOLID -2 -#define CONTENTS_WATER -3 -#define CONTENTS_SLIME -4 -#define CONTENTS_LAVA -5 -#define CONTENTS_SKY -6 - -// !!! if this is changed, it must be changed in asm_i386.h too !!! -typedef struct -{ - int planenum; - short children[2]; // negative numbers are -(leafs+1), not nodes - short mins[3]; // for sphere culling - short maxs[3]; - unsigned short firstface; - unsigned short numfaces; // counting both sides -} dnode_t; - -typedef struct -{ - int planenum; - short children[2]; // negative numbers are contents -} dclipnode_t; - - -typedef struct texinfo_s -{ - float vecs[2][4]; // [s/t][xyz offset] - int miptex; - int flags; -} texinfo_t; -#define TEX_SPECIAL 1 // sky or slime, no lightmap or 256 subdivision - -// note that edge 0 is never used, because negative edge nums are used for -// counterclockwise use of the edge in a face -typedef struct -{ - unsigned short v[2]; // vertex numbers -} dedge_t; - -#define MAXLIGHTMAPS 4 -typedef struct -{ - short planenum; - short side; - - int firstedge; // we must support > 64k edges - short numedges; - short texinfo; - -// lighting info - byte styles[MAXLIGHTMAPS]; - int lightofs; // start of [numstyles*surfsize] samples -} dface_t; - - - -#define AMBIENT_WATER 0 -#define AMBIENT_SKY 1 -#define AMBIENT_SLIME 2 -#define AMBIENT_LAVA 3 - -#define NUM_AMBIENTS 4 // automatic ambient sounds - -// leaf 0 is the generic CONTENTS_SOLID leaf, used for all solid areas -// all other leafs need visibility info -typedef struct -{ - int contents; - int visofs; // -1 = no visibility info - - short mins[3]; // for frustum culling - short maxs[3]; - - unsigned short firstmarksurface; - unsigned short nummarksurfaces; - - byte ambient_level[NUM_AMBIENTS]; -} dleaf_t; - -//============================================================================ - -#ifndef QUAKE_GAME - -// the utilities get to be lazy and just use large static arrays - -extern int nummodels; -extern dmodel_t dmodels[MAX_MAP_MODELS]; - -extern int visdatasize; -extern byte dvisdata[MAX_MAP_VISIBILITY]; - -extern int lightdatasize; -extern byte dlightdata[MAX_MAP_LIGHTING]; - -extern int texdatasize; -extern byte dtexdata[MAX_MAP_MIPTEX]; // (dmiptexlump_t) - -extern int entdatasize; -extern char dentdata[MAX_MAP_ENTSTRING]; - -extern int numleafs; -extern dleaf_t dleafs[MAX_MAP_LEAFS]; - -extern int numplanes; -extern dplane_t dplanes[MAX_MAP_PLANES]; - -extern int numvertexes; -extern dvertex_t dvertexes[MAX_MAP_VERTS]; - -extern int numnodes; -extern dnode_t dnodes[MAX_MAP_NODES]; - -extern int numtexinfo; -extern texinfo_t texinfo[MAX_MAP_TEXINFO]; - -extern int numfaces; -extern dface_t dfaces[MAX_MAP_FACES]; - -extern int numclipnodes; -extern dclipnode_t dclipnodes[MAX_MAP_CLIPNODES]; - -extern int numedges; -extern dedge_t dedges[MAX_MAP_EDGES]; - -extern int nummarksurfaces; -extern unsigned short dmarksurfaces[MAX_MAP_MARKSURFACES]; - -extern int numsurfedges; -extern int dsurfedges[MAX_MAP_SURFEDGES]; - - - -void LoadBSPFile (char *filename); -void WriteBSPFile (char *filename); -void PrintBSPFileSizes (void); - -#endif - -#endif // __bspfile_h +/* +Copyright (C) 1996-1997 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 __bspfile_h +#define __bspfile_h + +// upper design bounds + +#define MAX_MAP_HULLS 4 + +#define MAX_MAP_MODELS 256 +#define MAX_MAP_BRUSHES 4096 +#define MAX_MAP_ENTITIES 1024 +#define MAX_MAP_ENTSTRING 65536 + +#define MAX_MAP_PLANES 8192 +#define MAX_MAP_NODES 32767 // because negative shorts are contents +#define MAX_MAP_CLIPNODES 32767 // +#define MAX_MAP_LEAFS 32767 // +#define MAX_MAP_VERTS 65535 +#define MAX_MAP_FACES 65535 +#define MAX_MAP_MARKSURFACES 65535 +#define MAX_MAP_TEXINFO 4096 +#define MAX_MAP_EDGES 256000 +#define MAX_MAP_SURFEDGES 512000 +#define MAX_MAP_MIPTEX 0x200000 +#define MAX_MAP_LIGHTING 0x100000 +#define MAX_MAP_VISIBILITY 0x100000 + +// key / value pair sizes + +#define MAX_KEY 32 +#define MAX_VALUE 1024 + + +//============================================================================= + + +#define BSPVERSION 29 + +typedef struct +{ + int fileofs, filelen; +} lump_t; + +#define LUMP_ENTITIES 0 +#define LUMP_PLANES 1 +#define LUMP_TEXTURES 2 +#define LUMP_VERTEXES 3 +#define LUMP_VISIBILITY 4 +#define LUMP_NODES 5 +#define LUMP_TEXINFO 6 +#define LUMP_FACES 7 +#define LUMP_LIGHTING 8 +#define LUMP_CLIPNODES 9 +#define LUMP_LEAFS 10 +#define LUMP_MARKSURFACES 11 +#define LUMP_EDGES 12 +#define LUMP_SURFEDGES 13 +#define LUMP_MODELS 14 + +#define HEADER_LUMPS 15 + +typedef struct +{ + float mins[3], maxs[3]; + float origin[3]; + int headnode[MAX_MAP_HULLS]; + int visleafs; // not including the solid leaf 0 + int firstface, numfaces; +} dmodel_t; + +typedef struct +{ + int version; + lump_t lumps[HEADER_LUMPS]; +} dheader_t; + +typedef struct +{ + int nummiptex; + int dataofs[4]; // [nummiptex] +} dmiptexlump_t; + +#define MIPLEVELS 4 +typedef struct miptex_s +{ + char name[16]; + unsigned width, height; + unsigned offsets[MIPLEVELS]; // four mip maps stored +} miptex_t; + + +typedef struct +{ + float point[3]; +} dvertex_t; + + +// 0-2 are axial planes +#define PLANE_X 0 +#define PLANE_Y 1 +#define PLANE_Z 2 + +// 3-5 are non-axial planes snapped to the nearest +#define PLANE_ANYX 3 +#define PLANE_ANYY 4 +#define PLANE_ANYZ 5 + +typedef struct +{ + float normal[3]; + float dist; + int type; // PLANE_X - PLANE_ANYZ ?remove? trivial to regenerate +} dplane_t; + + + +#define CONTENTS_EMPTY -1 +#define CONTENTS_SOLID -2 +#define CONTENTS_WATER -3 +#define CONTENTS_SLIME -4 +#define CONTENTS_LAVA -5 +#define CONTENTS_SKY -6 + +// !!! if this is changed, it must be changed in asm_i386.h too !!! +typedef struct +{ + int planenum; + short children[2]; // negative numbers are -(leafs+1), not nodes + short mins[3]; // for sphere culling + short maxs[3]; + unsigned short firstface; + unsigned short numfaces; // counting both sides +} dnode_t; + +typedef struct +{ + int planenum; + short children[2]; // negative numbers are contents +} dclipnode_t; + + +typedef struct texinfo_s +{ + float vecs[2][4]; // [s/t][xyz offset] + int miptex; + int flags; +} texinfo_t; +#define TEX_SPECIAL 1 // sky or slime, no lightmap or 256 subdivision + +// note that edge 0 is never used, because negative edge nums are used for +// counterclockwise use of the edge in a face +typedef struct +{ + unsigned short v[2]; // vertex numbers +} dedge_t; + +#define MAXLIGHTMAPS 4 +typedef struct +{ + short planenum; + short side; + + int firstedge; // we must support > 64k edges + short numedges; + short texinfo; + +// lighting info + byte styles[MAXLIGHTMAPS]; + int lightofs; // start of [numstyles*surfsize] samples +} dface_t; + + + +#define AMBIENT_WATER 0 +#define AMBIENT_SKY 1 +#define AMBIENT_SLIME 2 +#define AMBIENT_LAVA 3 + +#define NUM_AMBIENTS 4 // automatic ambient sounds + +// leaf 0 is the generic CONTENTS_SOLID leaf, used for all solid areas +// all other leafs need visibility info +typedef struct +{ + int contents; + int visofs; // -1 = no visibility info + + short mins[3]; // for frustum culling + short maxs[3]; + + unsigned short firstmarksurface; + unsigned short nummarksurfaces; + + byte ambient_level[NUM_AMBIENTS]; +} dleaf_t; + +//============================================================================ + +#ifndef QUAKE_GAME + +// the utilities get to be lazy and just use large static arrays + +extern int nummodels; +extern dmodel_t dmodels[MAX_MAP_MODELS]; + +extern int visdatasize; +extern byte dvisdata[MAX_MAP_VISIBILITY]; + +extern int lightdatasize; +extern byte dlightdata[MAX_MAP_LIGHTING]; + +extern int texdatasize; +extern byte dtexdata[MAX_MAP_MIPTEX]; // (dmiptexlump_t) + +extern int entdatasize; +extern char dentdata[MAX_MAP_ENTSTRING]; + +extern int numleafs; +extern dleaf_t dleafs[MAX_MAP_LEAFS]; + +extern int numplanes; +extern dplane_t dplanes[MAX_MAP_PLANES]; + +extern int numvertexes; +extern dvertex_t dvertexes[MAX_MAP_VERTS]; + +extern int numnodes; +extern dnode_t dnodes[MAX_MAP_NODES]; + +extern int numtexinfo; +extern texinfo_t texinfo[MAX_MAP_TEXINFO]; + +extern int numfaces; +extern dface_t dfaces[MAX_MAP_FACES]; + +extern int numclipnodes; +extern dclipnode_t dclipnodes[MAX_MAP_CLIPNODES]; + +extern int numedges; +extern dedge_t dedges[MAX_MAP_EDGES]; + +extern int nummarksurfaces; +extern unsigned short dmarksurfaces[MAX_MAP_MARKSURFACES]; + +extern int numsurfedges; +extern int dsurfedges[MAX_MAP_SURFEDGES]; + + + +void LoadBSPFile (char *filename); +void WriteBSPFile (char *filename); +void PrintBSPFileSizes (void); + +#endif + +#endif // __bspfile_h diff --git a/source/cd_linux.c b/source/cd_linux.c index 10e92f31..c9a2f8d1 100644 --- a/source/cd_linux.c +++ b/source/cd_linux.c @@ -1,416 +1,416 @@ -/* -Copyright (C) 1996-1997 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 included (GNU.txt) 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. - -*/ -// Quake is a trademark of Id Software, Inc., (c) 1996 Id Software, Inc. All -// rights reserved. - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "quakedef.h" -#include "sound.h" - -static qboolean cdValid = false; -static qboolean playing = false; -static qboolean wasPlaying = false; -static qboolean initialized = false; -static qboolean enabled = true; -static qboolean playLooping = false; -static float cdvolume; -static byte remap[100]; -static byte playTrack; -static byte maxTrack; - -static int cdfile = -1; -static char cd_dev[64] = "/dev/cdrom"; - -static void CDAudio_Eject(void) -{ - if (cdfile == -1 || !enabled) - return; // no cd init'd - - if ( ioctl(cdfile, CDROMEJECT) == -1 ) - Con_DPrintf("ioctl cdromeject failed\n"); -} - - -static void CDAudio_CloseDoor(void) -{ - if (cdfile == -1 || !enabled) - return; // no cd init'd - - if ( ioctl(cdfile, CDROMCLOSETRAY) == -1 ) - Con_DPrintf("ioctl cdromclosetray failed\n"); -} - -static int CDAudio_GetAudioDiskInfo(void) -{ - struct cdrom_tochdr tochdr; - - cdValid = false; - - if ( ioctl(cdfile, CDROMREADTOCHDR, &tochdr) == -1 ) - { - Con_DPrintf("ioctl cdromreadtochdr failed\n"); - return -1; - } - - if (tochdr.cdth_trk0 < 1) - { - Con_DPrintf("CDAudio: no music tracks\n"); - return -1; - } - - cdValid = true; - maxTrack = tochdr.cdth_trk1; - - return 0; -} - - -void CDAudio_Play(byte track, qboolean looping) -{ - struct cdrom_tocentry entry; - struct cdrom_ti ti; - - if (cdfile == -1 || !enabled) - return; - - if (!cdValid) - { - CDAudio_GetAudioDiskInfo(); - if (!cdValid) - return; - } - - track = remap[track]; - - if (track < 1 || track > maxTrack) - { - Con_DPrintf("CDAudio: Bad track number %u.\n", track); - return; - } - - // don't try to play a non-audio track - entry.cdte_track = track; - entry.cdte_format = CDROM_MSF; - if ( ioctl(cdfile, CDROMREADTOCENTRY, &entry) == -1 ) - { - Con_DPrintf("ioctl cdromreadtocentry failed\n"); - return; - } - if (entry.cdte_ctrl == CDROM_DATA_TRACK) - { - Con_Printf("CDAudio: track %i is not audio\n", track); - return; - } - - if (playing) - { - if (playTrack == track) - return; - CDAudio_Stop(); - } - - ti.cdti_trk0 = track; - ti.cdti_trk1 = track; - ti.cdti_ind0 = 1; - ti.cdti_ind1 = 99; - - if ( ioctl(cdfile, CDROMPLAYTRKIND, &ti) == -1 ) - { - Con_DPrintf("ioctl cdromplaytrkind failed\n"); - return; - } - - if ( ioctl(cdfile, CDROMRESUME) == -1 ) - Con_DPrintf("ioctl cdromresume failed\n"); - - playLooping = looping; - playTrack = track; - playing = true; - - if (cdvolume == 0.0) - CDAudio_Pause (); -} - - -void CDAudio_Stop(void) -{ - if (cdfile == -1 || !enabled) - return; - - if (!playing) - return; - - if ( ioctl(cdfile, CDROMSTOP) == -1 ) - Con_DPrintf("ioctl cdromstop failed (%d)\n", errno); - - wasPlaying = false; - playing = false; -} - -void CDAudio_Pause(void) -{ - if (cdfile == -1 || !enabled) - return; - - if (!playing) - return; - - if ( ioctl(cdfile, CDROMPAUSE) == -1 ) - Con_DPrintf("ioctl cdrompause failed\n"); - - wasPlaying = playing; - playing = false; -} - - -void CDAudio_Resume(void) -{ - if (cdfile == -1 || !enabled) - return; - - if (!cdValid) - return; - - if (!wasPlaying) - return; - - if ( ioctl(cdfile, CDROMRESUME) == -1 ) - Con_DPrintf("ioctl cdromresume failed\n"); - playing = true; -} - -static void CD_f (void) -{ - char *command; - int ret; - int n; - - if (Cmd_Argc() < 2) - return; - - command = Cmd_Argv (1); - - if (Q_strcasecmp(command, "on") == 0) - { - enabled = true; - return; - } - - if (Q_strcasecmp(command, "off") == 0) - { - if (playing) - CDAudio_Stop(); - enabled = false; - return; - } - - if (Q_strcasecmp(command, "reset") == 0) - { - enabled = true; - if (playing) - CDAudio_Stop(); - for (n = 0; n < 100; n++) - remap[n] = n; - CDAudio_GetAudioDiskInfo(); - return; - } - - if (Q_strcasecmp(command, "remap") == 0) - { - ret = Cmd_Argc() - 2; - if (ret <= 0) - { - for (n = 1; n < 100; n++) - if (remap[n] != n) - Con_Printf(" %u -> %u\n", n, remap[n]); - return; - } - for (n = 1; n <= ret; n++) - remap[n] = Q_atoi(Cmd_Argv (n+1)); - return; - } - - if (Q_strcasecmp(command, "close") == 0) - { - CDAudio_CloseDoor(); - return; - } - - if (!cdValid) - { - CDAudio_GetAudioDiskInfo(); - if (!cdValid) - { - Con_Printf("No CD in player.\n"); - return; - } - } - - if (Q_strcasecmp(command, "play") == 0) - { - CDAudio_Play((byte)Q_atoi(Cmd_Argv (2)), false); - return; - } - - if (Q_strcasecmp(command, "loop") == 0) - { - CDAudio_Play((byte)Q_atoi(Cmd_Argv (2)), true); - return; - } - - if (Q_strcasecmp(command, "stop") == 0) - { - CDAudio_Stop(); - return; - } - - if (Q_strcasecmp(command, "pause") == 0) - { - CDAudio_Pause(); - return; - } - - if (Q_strcasecmp(command, "resume") == 0) - { - CDAudio_Resume(); - return; - } - - if (Q_strcasecmp(command, "eject") == 0) - { - if (playing) - CDAudio_Stop(); - CDAudio_Eject(); - cdValid = false; - return; - } - - if (Q_strcasecmp(command, "info") == 0) - { - Con_Printf("%u tracks\n", maxTrack); - if (playing) - Con_Printf("Currently %s track %u\n", playLooping ? "looping" : "playing", playTrack); - else if (wasPlaying) - Con_Printf("Paused %s track %u\n", playLooping ? "looping" : "playing", playTrack); - Con_Printf("Volume is %f\n", cdvolume); - return; - } -} - -void CDAudio_Update(void) -{ - struct cdrom_subchnl subchnl; - static time_t lastchk; - - if (!enabled) - return; - - if (bgmvolume.value != cdvolume) - { - if (cdvolume) - { - Cvar_SetValue (&bgmvolume, 0.0); - cdvolume = bgmvolume.value; - CDAudio_Pause (); - } - else - { - Cvar_SetValue (&bgmvolume, 1.0); - cdvolume = bgmvolume.value; - CDAudio_Resume (); - } - } - - if (playing && lastchk < time(NULL)) { - lastchk = time(NULL) + 2; //two seconds between chks - subchnl.cdsc_format = CDROM_MSF; - if (ioctl(cdfile, CDROMSUBCHNL, &subchnl) == -1 ) { - Con_DPrintf("ioctl cdromsubchnl failed\n"); - playing = false; - return; - } - if (subchnl.cdsc_audiostatus != CDROM_AUDIO_PLAY && - subchnl.cdsc_audiostatus != CDROM_AUDIO_PAUSED) { - playing = false; - if (playLooping) - CDAudio_Play(playTrack, true); - } - } -} - -int CDAudio_Init(void) -{ - int i; - -#if 0 - if (cls.state == ca_dedicated) - return -1; -#endif - - if (COM_CheckParm("-nocdaudio")) - return -1; - - if ((i = COM_CheckParm("-cddev")) != 0 && i < com_argc - 1) - Q_strncpyz (cd_dev, com_argv[i + 1], sizeof(cd_dev)); - - if ((cdfile = open(cd_dev, O_RDONLY)) == -1) { - Con_Printf("CDAudio_Init: open of \"%s\" failed (%i)\n", cd_dev, errno); - cdfile = -1; - return -1; - } - - for (i = 0; i < 100; i++) - remap[i] = i; - initialized = true; - enabled = true; - - if (CDAudio_GetAudioDiskInfo()) - { - Con_Printf("CDAudio_Init: No CD in player.\n"); - cdValid = false; - } - - Cmd_AddCommand ("cd", CD_f); - - Con_Printf("CD Audio Initialized\n"); - - return 0; -} - - -void CDAudio_Shutdown(void) -{ - if (!initialized) - return; - CDAudio_Stop(); - close(cdfile); - cdfile = -1; -} +/* +Copyright (C) 1996-1997 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 included (GNU.txt) 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. + +*/ +// Quake is a trademark of Id Software, Inc., (c) 1996 Id Software, Inc. All +// rights reserved. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "quakedef.h" +#include "sound.h" + +static qboolean cdValid = false; +static qboolean playing = false; +static qboolean wasPlaying = false; +static qboolean initialized = false; +static qboolean enabled = true; +static qboolean playLooping = false; +static float cdvolume; +static byte remap[100]; +static byte playTrack; +static byte maxTrack; + +static int cdfile = -1; +static char cd_dev[64] = "/dev/cdrom"; + +static void CDAudio_Eject(void) +{ + if (cdfile == -1 || !enabled) + return; // no cd init'd + + if ( ioctl(cdfile, CDROMEJECT) == -1 ) + Con_DPrintf("ioctl cdromeject failed\n"); +} + + +static void CDAudio_CloseDoor(void) +{ + if (cdfile == -1 || !enabled) + return; // no cd init'd + + if ( ioctl(cdfile, CDROMCLOSETRAY) == -1 ) + Con_DPrintf("ioctl cdromclosetray failed\n"); +} + +static int CDAudio_GetAudioDiskInfo(void) +{ + struct cdrom_tochdr tochdr; + + cdValid = false; + + if ( ioctl(cdfile, CDROMREADTOCHDR, &tochdr) == -1 ) + { + Con_DPrintf("ioctl cdromreadtochdr failed\n"); + return -1; + } + + if (tochdr.cdth_trk0 < 1) + { + Con_DPrintf("CDAudio: no music tracks\n"); + return -1; + } + + cdValid = true; + maxTrack = tochdr.cdth_trk1; + + return 0; +} + + +void CDAudio_Play(byte track, qboolean looping) +{ + struct cdrom_tocentry entry; + struct cdrom_ti ti; + + if (cdfile == -1 || !enabled) + return; + + if (!cdValid) + { + CDAudio_GetAudioDiskInfo(); + if (!cdValid) + return; + } + + track = remap[track]; + + if (track < 1 || track > maxTrack) + { + Con_DPrintf("CDAudio: Bad track number %u.\n", track); + return; + } + + // don't try to play a non-audio track + entry.cdte_track = track; + entry.cdte_format = CDROM_MSF; + if ( ioctl(cdfile, CDROMREADTOCENTRY, &entry) == -1 ) + { + Con_DPrintf("ioctl cdromreadtocentry failed\n"); + return; + } + if (entry.cdte_ctrl == CDROM_DATA_TRACK) + { + Con_Printf("CDAudio: track %i is not audio\n", track); + return; + } + + if (playing) + { + if (playTrack == track) + return; + CDAudio_Stop(); + } + + ti.cdti_trk0 = track; + ti.cdti_trk1 = track; + ti.cdti_ind0 = 1; + ti.cdti_ind1 = 99; + + if ( ioctl(cdfile, CDROMPLAYTRKIND, &ti) == -1 ) + { + Con_DPrintf("ioctl cdromplaytrkind failed\n"); + return; + } + + if ( ioctl(cdfile, CDROMRESUME) == -1 ) + Con_DPrintf("ioctl cdromresume failed\n"); + + playLooping = looping; + playTrack = track; + playing = true; + + if (cdvolume == 0.0) + CDAudio_Pause (); +} + + +void CDAudio_Stop(void) +{ + if (cdfile == -1 || !enabled) + return; + + if (!playing) + return; + + if ( ioctl(cdfile, CDROMSTOP) == -1 ) + Con_DPrintf("ioctl cdromstop failed (%d)\n", errno); + + wasPlaying = false; + playing = false; +} + +void CDAudio_Pause(void) +{ + if (cdfile == -1 || !enabled) + return; + + if (!playing) + return; + + if ( ioctl(cdfile, CDROMPAUSE) == -1 ) + Con_DPrintf("ioctl cdrompause failed\n"); + + wasPlaying = playing; + playing = false; +} + + +void CDAudio_Resume(void) +{ + if (cdfile == -1 || !enabled) + return; + + if (!cdValid) + return; + + if (!wasPlaying) + return; + + if ( ioctl(cdfile, CDROMRESUME) == -1 ) + Con_DPrintf("ioctl cdromresume failed\n"); + playing = true; +} + +static void CD_f (void) +{ + char *command; + int ret; + int n; + + if (Cmd_Argc() < 2) + return; + + command = Cmd_Argv (1); + + if (Q_strcasecmp(command, "on") == 0) + { + enabled = true; + return; + } + + if (Q_strcasecmp(command, "off") == 0) + { + if (playing) + CDAudio_Stop(); + enabled = false; + return; + } + + if (Q_strcasecmp(command, "reset") == 0) + { + enabled = true; + if (playing) + CDAudio_Stop(); + for (n = 0; n < 100; n++) + remap[n] = n; + CDAudio_GetAudioDiskInfo(); + return; + } + + if (Q_strcasecmp(command, "remap") == 0) + { + ret = Cmd_Argc() - 2; + if (ret <= 0) + { + for (n = 1; n < 100; n++) + if (remap[n] != n) + Con_Printf(" %u -> %u\n", n, remap[n]); + return; + } + for (n = 1; n <= ret; n++) + remap[n] = Q_atoi(Cmd_Argv (n+1)); + return; + } + + if (Q_strcasecmp(command, "close") == 0) + { + CDAudio_CloseDoor(); + return; + } + + if (!cdValid) + { + CDAudio_GetAudioDiskInfo(); + if (!cdValid) + { + Con_Printf("No CD in player.\n"); + return; + } + } + + if (Q_strcasecmp(command, "play") == 0) + { + CDAudio_Play((byte)Q_atoi(Cmd_Argv (2)), false); + return; + } + + if (Q_strcasecmp(command, "loop") == 0) + { + CDAudio_Play((byte)Q_atoi(Cmd_Argv (2)), true); + return; + } + + if (Q_strcasecmp(command, "stop") == 0) + { + CDAudio_Stop(); + return; + } + + if (Q_strcasecmp(command, "pause") == 0) + { + CDAudio_Pause(); + return; + } + + if (Q_strcasecmp(command, "resume") == 0) + { + CDAudio_Resume(); + return; + } + + if (Q_strcasecmp(command, "eject") == 0) + { + if (playing) + CDAudio_Stop(); + CDAudio_Eject(); + cdValid = false; + return; + } + + if (Q_strcasecmp(command, "info") == 0) + { + Con_Printf("%u tracks\n", maxTrack); + if (playing) + Con_Printf("Currently %s track %u\n", playLooping ? "looping" : "playing", playTrack); + else if (wasPlaying) + Con_Printf("Paused %s track %u\n", playLooping ? "looping" : "playing", playTrack); + Con_Printf("Volume is %f\n", cdvolume); + return; + } +} + +void CDAudio_Update(void) +{ + struct cdrom_subchnl subchnl; + static time_t lastchk; + + if (!enabled) + return; + + if (bgmvolume.value != cdvolume) + { + if (cdvolume) + { + Cvar_SetValue (&bgmvolume, 0.0); + cdvolume = bgmvolume.value; + CDAudio_Pause (); + } + else + { + Cvar_SetValue (&bgmvolume, 1.0); + cdvolume = bgmvolume.value; + CDAudio_Resume (); + } + } + + if (playing && lastchk < time(NULL)) { + lastchk = time(NULL) + 2; //two seconds between chks + subchnl.cdsc_format = CDROM_MSF; + if (ioctl(cdfile, CDROMSUBCHNL, &subchnl) == -1 ) { + Con_DPrintf("ioctl cdromsubchnl failed\n"); + playing = false; + return; + } + if (subchnl.cdsc_audiostatus != CDROM_AUDIO_PLAY && + subchnl.cdsc_audiostatus != CDROM_AUDIO_PAUSED) { + playing = false; + if (playLooping) + CDAudio_Play(playTrack, true); + } + } +} + +int CDAudio_Init(void) +{ + int i; + +#if 0 + if (cls.state == ca_dedicated) + return -1; +#endif + + if (COM_CheckParm("-nocdaudio")) + return -1; + + if ((i = COM_CheckParm("-cddev")) != 0 && i < com_argc - 1) + Q_strncpyz (cd_dev, com_argv[i + 1], sizeof(cd_dev)); + + if ((cdfile = open(cd_dev, O_RDONLY)) == -1) { + Con_Printf("CDAudio_Init: open of \"%s\" failed (%i)\n", cd_dev, errno); + cdfile = -1; + return -1; + } + + for (i = 0; i < 100; i++) + remap[i] = i; + initialized = true; + enabled = true; + + if (CDAudio_GetAudioDiskInfo()) + { + Con_Printf("CDAudio_Init: No CD in player.\n"); + cdValid = false; + } + + Cmd_AddCommand ("cd", CD_f); + + Con_Printf("CD Audio Initialized\n"); + + return 0; +} + + +void CDAudio_Shutdown(void) +{ + if (!initialized) + return; + CDAudio_Stop(); + close(cdfile); + cdfile = -1; +} diff --git a/source/cd_null.c b/source/cd_null.c index f9eb40f2..ce8027ff 100644 --- a/source/cd_null.c +++ b/source/cd_null.c @@ -1,31 +1,31 @@ -#include "quakedef.h" - -void CDAudio_Play(byte track, qboolean looping) -{ -} - - -void CDAudio_Stop(void) -{ -} - - -void CDAudio_Resume(void) -{ -} - - -void CDAudio_Update(void) -{ -} - - -int CDAudio_Init(void) -{ - return 0; -} - - -void CDAudio_Shutdown(void) -{ +#include "quakedef.h" + +void CDAudio_Play(byte track, qboolean looping) +{ +} + + +void CDAudio_Stop(void) +{ +} + + +void CDAudio_Resume(void) +{ +} + + +void CDAudio_Update(void) +{ +} + + +int CDAudio_Init(void) +{ + return 0; +} + + +void CDAudio_Shutdown(void) +{ } \ No newline at end of file diff --git a/source/cd_win.c b/source/cd_win.c index a8f50338..ea349f88 100644 --- a/source/cd_win.c +++ b/source/cd_win.c @@ -1,479 +1,479 @@ -/* -Copyright (C) 1996-1997 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 included (GNU.txt) 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. - -*/ -// Quake is a trademark of Id Software, Inc., (c) 1996 Id Software, Inc. All -// rights reserved. - -#include -#include "quakedef.h" - -extern HWND mainwindow; -extern cvar_t bgmvolume; - -static qboolean cdValid = false; -static qboolean playing = false; -static qboolean wasPlaying = false; -static qboolean initialized = false; -static qboolean enabled = false; -static qboolean playLooping = false; -static float cdvolume; -static byte remap[100]; -static byte cdrom; -static byte playTrack; -static byte maxTrack; - -UINT wDeviceID; - - -static void CDAudio_Eject(void) -{ - DWORD dwReturn; - - if (dwReturn = mciSendCommand(wDeviceID, MCI_SET, MCI_SET_DOOR_OPEN, (DWORD)NULL)) - Con_DPrintf("MCI_SET_DOOR_OPEN failed (%i)\n", dwReturn); -} - - -static void CDAudio_CloseDoor(void) -{ - DWORD dwReturn; - - if (dwReturn = mciSendCommand(wDeviceID, MCI_SET, MCI_SET_DOOR_CLOSED, (DWORD)NULL)) - Con_DPrintf("MCI_SET_DOOR_CLOSED failed (%i)\n", dwReturn); -} - - -static int CDAudio_GetAudioDiskInfo(void) -{ - DWORD dwReturn; - MCI_STATUS_PARMS mciStatusParms; - - - cdValid = false; - - mciStatusParms.dwItem = MCI_STATUS_READY; - dwReturn = mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, (DWORD) (LPVOID) &mciStatusParms); - if (dwReturn) - { - Con_DPrintf("CDAudio: drive ready test - get status failed\n"); - return -1; - } - if (!mciStatusParms.dwReturn) - { - Con_DPrintf("CDAudio: drive not ready\n"); - return -1; - } - - mciStatusParms.dwItem = MCI_STATUS_NUMBER_OF_TRACKS; - dwReturn = mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, (DWORD) (LPVOID) &mciStatusParms); - if (dwReturn) - { - Con_DPrintf("CDAudio: get tracks - status failed\n"); - return -1; - } - if (mciStatusParms.dwReturn < 1) - { - Con_DPrintf("CDAudio: no music tracks\n"); - return -1; - } - - cdValid = true; - maxTrack = mciStatusParms.dwReturn; - - return 0; -} - - -void CDAudio_Play(byte track, qboolean looping) -{ - DWORD dwReturn; - MCI_PLAY_PARMS mciPlayParms; - MCI_STATUS_PARMS mciStatusParms; - - if (!enabled) - return; - - if (!cdValid) - { - CDAudio_GetAudioDiskInfo(); - if (!cdValid) - return; - } - - track = remap[track]; - - if (track < 1 || track > maxTrack) - { - Con_DPrintf("CDAudio: Bad track number %u.\n", track); - return; - } - - // don't try to play a non-audio track - mciStatusParms.dwItem = MCI_CDA_STATUS_TYPE_TRACK; - mciStatusParms.dwTrack = track; - dwReturn = mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK | MCI_WAIT, (DWORD) (LPVOID) &mciStatusParms); - if (dwReturn) - { - Con_DPrintf("MCI_STATUS failed (%i)\n", dwReturn); - return; - } - if (mciStatusParms.dwReturn != MCI_CDA_TRACK_AUDIO) - { - Con_Printf("CDAudio: track %i is not audio\n", track); - return; - } - - // get the length of the track to be played - mciStatusParms.dwItem = MCI_STATUS_LENGTH; - mciStatusParms.dwTrack = track; - dwReturn = mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK | MCI_WAIT, (DWORD) (LPVOID) &mciStatusParms); - if (dwReturn) - { - Con_DPrintf("MCI_STATUS failed (%i)\n", dwReturn); - return; - } - - if (playing) - { - if (playTrack == track) - return; - CDAudio_Stop(); - } - - mciPlayParms.dwFrom = MCI_MAKE_TMSF(track, 0, 0, 0); - mciPlayParms.dwTo = (mciStatusParms.dwReturn << 8) | track; - mciPlayParms.dwCallback = (DWORD)mainwindow; - dwReturn = mciSendCommand(wDeviceID, MCI_PLAY, MCI_NOTIFY | MCI_FROM | MCI_TO, (DWORD)(LPVOID) &mciPlayParms); - if (dwReturn) - { - Con_DPrintf("CDAudio: MCI_PLAY failed (%i)\n", dwReturn); - return; - } - - playLooping = looping; - playTrack = track; - playing = true; - - if (cdvolume == 0.0) - CDAudio_Pause (); -} - - -void CDAudio_Stop(void) -{ - DWORD dwReturn; - - if (!enabled) - return; - - if (!playing) - return; - - if (dwReturn = mciSendCommand(wDeviceID, MCI_STOP, 0, (DWORD)NULL)) - Con_DPrintf("MCI_STOP failed (%i)", dwReturn); - - wasPlaying = false; - playing = false; -} - - -void CDAudio_Pause(void) -{ - DWORD dwReturn; - MCI_GENERIC_PARMS mciGenericParms; - - if (!enabled) - return; - - if (!playing) - return; - - mciGenericParms.dwCallback = (DWORD)mainwindow; - if (dwReturn = mciSendCommand(wDeviceID, MCI_PAUSE, 0, (DWORD)(LPVOID) &mciGenericParms)) - Con_DPrintf("MCI_PAUSE failed (%i)", dwReturn); - - wasPlaying = playing; - playing = false; -} - - -void CDAudio_Resume(void) -{ - DWORD dwReturn; - MCI_PLAY_PARMS mciPlayParms; - - if (!enabled) - return; - - if (!cdValid) - return; - - if (!wasPlaying) - return; - - mciPlayParms.dwFrom = MCI_MAKE_TMSF(playTrack, 0, 0, 0); - mciPlayParms.dwTo = MCI_MAKE_TMSF(playTrack + 1, 0, 0, 0); - mciPlayParms.dwCallback = (DWORD)mainwindow; - dwReturn = mciSendCommand(wDeviceID, MCI_PLAY, MCI_TO | MCI_NOTIFY, (DWORD)(LPVOID) &mciPlayParms); - if (dwReturn) - { - Con_DPrintf("CDAudio: MCI_PLAY failed (%i)\n", dwReturn); - return; - } - playing = true; -} - - -static void CD_f (void) -{ - char *command; - int ret; - int n; -// int startAddress; - - if (Cmd_Argc() < 2) - return; - - command = Cmd_Argv (1); - - if (Q_strcasecmp(command, "on") == 0) - { - enabled = true; - return; - } - - if (Q_strcasecmp(command, "off") == 0) - { - if (playing) - CDAudio_Stop(); - enabled = false; - return; - } - - if (Q_strcasecmp(command, "reset") == 0) - { - enabled = true; - if (playing) - CDAudio_Stop(); - for (n = 0; n < 100; n++) - remap[n] = n; - CDAudio_GetAudioDiskInfo(); - return; - } - - if (Q_strcasecmp(command, "remap") == 0) - { - ret = Cmd_Argc() - 2; - if (ret <= 0) - { - for (n = 1; n < 100; n++) - if (remap[n] != n) - Con_Printf(" %u -> %u\n", n, remap[n]); - return; - } - for (n = 1; n <= ret; n++) - remap[n] = Q_atoi(Cmd_Argv (n+1)); - return; - } - - if (Q_strcasecmp(command, "close") == 0) - { - CDAudio_CloseDoor(); - return; - } - - if (!cdValid) - { - CDAudio_GetAudioDiskInfo(); - if (!cdValid) - { - Con_Printf("No CD in player.\n"); - return; - } - } - - if (Q_strcasecmp(command, "play") == 0) - { - CDAudio_Play((byte)Q_atoi(Cmd_Argv (2)), false); - return; - } - - if (Q_strcasecmp(command, "loop") == 0) - { - CDAudio_Play((byte)Q_atoi(Cmd_Argv (2)), true); - return; - } - - if (Q_strcasecmp(command, "stop") == 0) - { - CDAudio_Stop(); - return; - } - - if (Q_strcasecmp(command, "pause") == 0) - { - CDAudio_Pause(); - return; - } - - if (Q_strcasecmp(command, "resume") == 0) - { - CDAudio_Resume(); - return; - } - - if (Q_strcasecmp(command, "eject") == 0) - { - if (playing) - CDAudio_Stop(); - CDAudio_Eject(); - cdValid = false; - return; - } - - if (Q_strcasecmp(command, "info") == 0) - { - Con_Printf("%u tracks\n", maxTrack); - if (playing) - Con_Printf("Currently %s track %u\n", playLooping ? "looping" : "playing", playTrack); - else if (wasPlaying) - Con_Printf("Paused %s track %u\n", playLooping ? "looping" : "playing", playTrack); - Con_Printf("Volume is %f\n", cdvolume); - return; - } -} - - -LONG CDAudio_MessageHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - if (lParam != wDeviceID) - return 1; - - switch (wParam) - { - case MCI_NOTIFY_SUCCESSFUL: - if (playing) - { - playing = false; - if (playLooping) - CDAudio_Play(playTrack, true); - } - break; - - case MCI_NOTIFY_ABORTED: - case MCI_NOTIFY_SUPERSEDED: - break; - - case MCI_NOTIFY_FAILURE: - Con_DPrintf("MCI_NOTIFY_FAILURE\n"); - CDAudio_Stop (); - cdValid = false; - break; - - default: - Con_DPrintf("Unexpected MM_MCINOTIFY type (%i)\n", wParam); - return 1; - } - - return 0; -} - - -void CDAudio_Update(void) -{ - if (!enabled) - return; - - if (bgmvolume.value != cdvolume) - { - if (cdvolume) - { - Cvar_SetValue (&bgmvolume, 0.0); - cdvolume = bgmvolume.value; - CDAudio_Pause (); - } - else - { - Cvar_SetValue (&bgmvolume, 1.0); - cdvolume = bgmvolume.value; - CDAudio_Resume (); - } - } -} - - -int CDAudio_Init(void) -{ - DWORD dwReturn; - MCI_OPEN_PARMS mciOpenParms; - MCI_SET_PARMS mciSetParms; - int n; - -#if 0 // QW - if (cls.state == ca_dedicated) - return -1; -#endif - if (COM_CheckParm("-nocdaudio")) - return -1; - - mciOpenParms.lpstrDeviceType = "cdaudio"; - if (dwReturn = mciSendCommand(0, MCI_OPEN, MCI_OPEN_TYPE | MCI_OPEN_SHAREABLE, (DWORD) (LPVOID) &mciOpenParms)) - { - Con_Printf("CDAudio_Init: MCI_OPEN failed (%i)\n", dwReturn); - return -1; - } - wDeviceID = mciOpenParms.wDeviceID; - - // Set the time format to track/minute/second/frame (TMSF). - mciSetParms.dwTimeFormat = MCI_FORMAT_TMSF; - if (dwReturn = mciSendCommand(wDeviceID, MCI_SET, MCI_SET_TIME_FORMAT, (DWORD)(LPVOID) &mciSetParms)) - { - Con_Printf("MCI_SET_TIME_FORMAT failed (%i)\n", dwReturn); - mciSendCommand(wDeviceID, MCI_CLOSE, 0, (DWORD)NULL); - return -1; - } - - for (n = 0; n < 100; n++) - remap[n] = n; - initialized = true; - enabled = true; - - if (CDAudio_GetAudioDiskInfo()) - { - Con_Printf("CDAudio_Init: No CD in player.\n"); - cdValid = false; - enabled = false; - } - - Cmd_AddCommand ("cd", CD_f); - -// Con_Printf("CD Audio Initialized\n"); - - return 0; -} - - -void CDAudio_Shutdown(void) -{ - if (!initialized) - return; - CDAudio_Stop(); - if (mciSendCommand(wDeviceID, MCI_CLOSE, MCI_WAIT, (DWORD)NULL)) - Con_DPrintf("CDAudio_Shutdown: MCI_CLOSE failed\n"); -} +/* +Copyright (C) 1996-1997 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 included (GNU.txt) 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. + +*/ +// Quake is a trademark of Id Software, Inc., (c) 1996 Id Software, Inc. All +// rights reserved. + +#include +#include "quakedef.h" + +extern HWND mainwindow; +extern cvar_t bgmvolume; + +static qboolean cdValid = false; +static qboolean playing = false; +static qboolean wasPlaying = false; +static qboolean initialized = false; +static qboolean enabled = false; +static qboolean playLooping = false; +static float cdvolume; +static byte remap[100]; +static byte cdrom; +static byte playTrack; +static byte maxTrack; + +UINT wDeviceID; + + +static void CDAudio_Eject(void) +{ + DWORD dwReturn; + + if (dwReturn = mciSendCommand(wDeviceID, MCI_SET, MCI_SET_DOOR_OPEN, (DWORD)NULL)) + Con_DPrintf("MCI_SET_DOOR_OPEN failed (%i)\n", dwReturn); +} + + +static void CDAudio_CloseDoor(void) +{ + DWORD dwReturn; + + if (dwReturn = mciSendCommand(wDeviceID, MCI_SET, MCI_SET_DOOR_CLOSED, (DWORD)NULL)) + Con_DPrintf("MCI_SET_DOOR_CLOSED failed (%i)\n", dwReturn); +} + + +static int CDAudio_GetAudioDiskInfo(void) +{ + DWORD dwReturn; + MCI_STATUS_PARMS mciStatusParms; + + + cdValid = false; + + mciStatusParms.dwItem = MCI_STATUS_READY; + dwReturn = mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, (DWORD) (LPVOID) &mciStatusParms); + if (dwReturn) + { + Con_DPrintf("CDAudio: drive ready test - get status failed\n"); + return -1; + } + if (!mciStatusParms.dwReturn) + { + Con_DPrintf("CDAudio: drive not ready\n"); + return -1; + } + + mciStatusParms.dwItem = MCI_STATUS_NUMBER_OF_TRACKS; + dwReturn = mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, (DWORD) (LPVOID) &mciStatusParms); + if (dwReturn) + { + Con_DPrintf("CDAudio: get tracks - status failed\n"); + return -1; + } + if (mciStatusParms.dwReturn < 1) + { + Con_DPrintf("CDAudio: no music tracks\n"); + return -1; + } + + cdValid = true; + maxTrack = mciStatusParms.dwReturn; + + return 0; +} + + +void CDAudio_Play(byte track, qboolean looping) +{ + DWORD dwReturn; + MCI_PLAY_PARMS mciPlayParms; + MCI_STATUS_PARMS mciStatusParms; + + if (!enabled) + return; + + if (!cdValid) + { + CDAudio_GetAudioDiskInfo(); + if (!cdValid) + return; + } + + track = remap[track]; + + if (track < 1 || track > maxTrack) + { + Con_DPrintf("CDAudio: Bad track number %u.\n", track); + return; + } + + // don't try to play a non-audio track + mciStatusParms.dwItem = MCI_CDA_STATUS_TYPE_TRACK; + mciStatusParms.dwTrack = track; + dwReturn = mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK | MCI_WAIT, (DWORD) (LPVOID) &mciStatusParms); + if (dwReturn) + { + Con_DPrintf("MCI_STATUS failed (%i)\n", dwReturn); + return; + } + if (mciStatusParms.dwReturn != MCI_CDA_TRACK_AUDIO) + { + Con_Printf("CDAudio: track %i is not audio\n", track); + return; + } + + // get the length of the track to be played + mciStatusParms.dwItem = MCI_STATUS_LENGTH; + mciStatusParms.dwTrack = track; + dwReturn = mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK | MCI_WAIT, (DWORD) (LPVOID) &mciStatusParms); + if (dwReturn) + { + Con_DPrintf("MCI_STATUS failed (%i)\n", dwReturn); + return; + } + + if (playing) + { + if (playTrack == track) + return; + CDAudio_Stop(); + } + + mciPlayParms.dwFrom = MCI_MAKE_TMSF(track, 0, 0, 0); + mciPlayParms.dwTo = (mciStatusParms.dwReturn << 8) | track; + mciPlayParms.dwCallback = (DWORD)mainwindow; + dwReturn = mciSendCommand(wDeviceID, MCI_PLAY, MCI_NOTIFY | MCI_FROM | MCI_TO, (DWORD)(LPVOID) &mciPlayParms); + if (dwReturn) + { + Con_DPrintf("CDAudio: MCI_PLAY failed (%i)\n", dwReturn); + return; + } + + playLooping = looping; + playTrack = track; + playing = true; + + if (cdvolume == 0.0) + CDAudio_Pause (); +} + + +void CDAudio_Stop(void) +{ + DWORD dwReturn; + + if (!enabled) + return; + + if (!playing) + return; + + if (dwReturn = mciSendCommand(wDeviceID, MCI_STOP, 0, (DWORD)NULL)) + Con_DPrintf("MCI_STOP failed (%i)", dwReturn); + + wasPlaying = false; + playing = false; +} + + +void CDAudio_Pause(void) +{ + DWORD dwReturn; + MCI_GENERIC_PARMS mciGenericParms; + + if (!enabled) + return; + + if (!playing) + return; + + mciGenericParms.dwCallback = (DWORD)mainwindow; + if (dwReturn = mciSendCommand(wDeviceID, MCI_PAUSE, 0, (DWORD)(LPVOID) &mciGenericParms)) + Con_DPrintf("MCI_PAUSE failed (%i)", dwReturn); + + wasPlaying = playing; + playing = false; +} + + +void CDAudio_Resume(void) +{ + DWORD dwReturn; + MCI_PLAY_PARMS mciPlayParms; + + if (!enabled) + return; + + if (!cdValid) + return; + + if (!wasPlaying) + return; + + mciPlayParms.dwFrom = MCI_MAKE_TMSF(playTrack, 0, 0, 0); + mciPlayParms.dwTo = MCI_MAKE_TMSF(playTrack + 1, 0, 0, 0); + mciPlayParms.dwCallback = (DWORD)mainwindow; + dwReturn = mciSendCommand(wDeviceID, MCI_PLAY, MCI_TO | MCI_NOTIFY, (DWORD)(LPVOID) &mciPlayParms); + if (dwReturn) + { + Con_DPrintf("CDAudio: MCI_PLAY failed (%i)\n", dwReturn); + return; + } + playing = true; +} + + +static void CD_f (void) +{ + char *command; + int ret; + int n; +// int startAddress; + + if (Cmd_Argc() < 2) + return; + + command = Cmd_Argv (1); + + if (Q_strcasecmp(command, "on") == 0) + { + enabled = true; + return; + } + + if (Q_strcasecmp(command, "off") == 0) + { + if (playing) + CDAudio_Stop(); + enabled = false; + return; + } + + if (Q_strcasecmp(command, "reset") == 0) + { + enabled = true; + if (playing) + CDAudio_Stop(); + for (n = 0; n < 100; n++) + remap[n] = n; + CDAudio_GetAudioDiskInfo(); + return; + } + + if (Q_strcasecmp(command, "remap") == 0) + { + ret = Cmd_Argc() - 2; + if (ret <= 0) + { + for (n = 1; n < 100; n++) + if (remap[n] != n) + Con_Printf(" %u -> %u\n", n, remap[n]); + return; + } + for (n = 1; n <= ret; n++) + remap[n] = Q_atoi(Cmd_Argv (n+1)); + return; + } + + if (Q_strcasecmp(command, "close") == 0) + { + CDAudio_CloseDoor(); + return; + } + + if (!cdValid) + { + CDAudio_GetAudioDiskInfo(); + if (!cdValid) + { + Con_Printf("No CD in player.\n"); + return; + } + } + + if (Q_strcasecmp(command, "play") == 0) + { + CDAudio_Play((byte)Q_atoi(Cmd_Argv (2)), false); + return; + } + + if (Q_strcasecmp(command, "loop") == 0) + { + CDAudio_Play((byte)Q_atoi(Cmd_Argv (2)), true); + return; + } + + if (Q_strcasecmp(command, "stop") == 0) + { + CDAudio_Stop(); + return; + } + + if (Q_strcasecmp(command, "pause") == 0) + { + CDAudio_Pause(); + return; + } + + if (Q_strcasecmp(command, "resume") == 0) + { + CDAudio_Resume(); + return; + } + + if (Q_strcasecmp(command, "eject") == 0) + { + if (playing) + CDAudio_Stop(); + CDAudio_Eject(); + cdValid = false; + return; + } + + if (Q_strcasecmp(command, "info") == 0) + { + Con_Printf("%u tracks\n", maxTrack); + if (playing) + Con_Printf("Currently %s track %u\n", playLooping ? "looping" : "playing", playTrack); + else if (wasPlaying) + Con_Printf("Paused %s track %u\n", playLooping ? "looping" : "playing", playTrack); + Con_Printf("Volume is %f\n", cdvolume); + return; + } +} + + +LONG CDAudio_MessageHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + if (lParam != wDeviceID) + return 1; + + switch (wParam) + { + case MCI_NOTIFY_SUCCESSFUL: + if (playing) + { + playing = false; + if (playLooping) + CDAudio_Play(playTrack, true); + } + break; + + case MCI_NOTIFY_ABORTED: + case MCI_NOTIFY_SUPERSEDED: + break; + + case MCI_NOTIFY_FAILURE: + Con_DPrintf("MCI_NOTIFY_FAILURE\n"); + CDAudio_Stop (); + cdValid = false; + break; + + default: + Con_DPrintf("Unexpected MM_MCINOTIFY type (%i)\n", wParam); + return 1; + } + + return 0; +} + + +void CDAudio_Update(void) +{ + if (!enabled) + return; + + if (bgmvolume.value != cdvolume) + { + if (cdvolume) + { + Cvar_SetValue (&bgmvolume, 0.0); + cdvolume = bgmvolume.value; + CDAudio_Pause (); + } + else + { + Cvar_SetValue (&bgmvolume, 1.0); + cdvolume = bgmvolume.value; + CDAudio_Resume (); + } + } +} + + +int CDAudio_Init(void) +{ + DWORD dwReturn; + MCI_OPEN_PARMS mciOpenParms; + MCI_SET_PARMS mciSetParms; + int n; + +#if 0 // QW + if (cls.state == ca_dedicated) + return -1; +#endif + if (COM_CheckParm("-nocdaudio")) + return -1; + + mciOpenParms.lpstrDeviceType = "cdaudio"; + if (dwReturn = mciSendCommand(0, MCI_OPEN, MCI_OPEN_TYPE | MCI_OPEN_SHAREABLE, (DWORD) (LPVOID) &mciOpenParms)) + { + Con_Printf("CDAudio_Init: MCI_OPEN failed (%i)\n", dwReturn); + return -1; + } + wDeviceID = mciOpenParms.wDeviceID; + + // Set the time format to track/minute/second/frame (TMSF). + mciSetParms.dwTimeFormat = MCI_FORMAT_TMSF; + if (dwReturn = mciSendCommand(wDeviceID, MCI_SET, MCI_SET_TIME_FORMAT, (DWORD)(LPVOID) &mciSetParms)) + { + Con_Printf("MCI_SET_TIME_FORMAT failed (%i)\n", dwReturn); + mciSendCommand(wDeviceID, MCI_CLOSE, 0, (DWORD)NULL); + return -1; + } + + for (n = 0; n < 100; n++) + remap[n] = n; + initialized = true; + enabled = true; + + if (CDAudio_GetAudioDiskInfo()) + { + Con_Printf("CDAudio_Init: No CD in player.\n"); + cdValid = false; + enabled = false; + } + + Cmd_AddCommand ("cd", CD_f); + +// Con_Printf("CD Audio Initialized\n"); + + return 0; +} + + +void CDAudio_Shutdown(void) +{ + if (!initialized) + return; + CDAudio_Stop(); + if (mciSendCommand(wDeviceID, MCI_CLOSE, MCI_WAIT, (DWORD)NULL)) + Con_DPrintf("CDAudio_Shutdown: MCI_CLOSE failed\n"); +} diff --git a/source/cdaudio.h b/source/cdaudio.h index 30dfd30a..80e975be 100644 --- a/source/cdaudio.h +++ b/source/cdaudio.h @@ -1,27 +1,27 @@ -/* -Copyright (C) 1996-1997 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. - -*/ - -int CDAudio_Init(void); -void CDAudio_Play(byte track, qboolean looping); -void CDAudio_Stop(void); -void CDAudio_Pause(void); -void CDAudio_Resume(void); -void CDAudio_Shutdown(void); -void CDAudio_Update(void); +/* +Copyright (C) 1996-1997 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. + +*/ + +int CDAudio_Init(void); +void CDAudio_Play(byte track, qboolean looping); +void CDAudio_Stop(void); +void CDAudio_Pause(void); +void CDAudio_Resume(void); +void CDAudio_Shutdown(void); +void CDAudio_Update(void); diff --git a/source/cl_cam.c b/source/cl_cam.c index 79198964..071b5b98 100644 --- a/source/cl_cam.c +++ b/source/cl_cam.c @@ -1,619 +1,715 @@ -/* -Copyright (C) 1996-1997 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 included (GNU.txt) 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. - -*/ -/* ZOID - * - * Player camera tracking in Spectator mode - * - * This takes over player controls for spectator automatic camera. - * Player moves as a spectator, but the camera tracks and enemy player - */ - -#include "quakedef.h" -#include "winquake.h" -#include "pmove.h" -#include "sbar.h" - -#define BUTTON_JUMP 2 -#define BUTTON_ATTACK 1 -#define MAX_ANGLE_TURN 10 - -static vec3_t desired_position; // where the camera wants to be -static qboolean locked = false; -static int oldbuttons; - -// track high fragger -cvar_t cl_hightrack = {"cl_hightrack", "0" }; -cvar_t cl_track_validonly = {"cl_track_validonly", "1" }; - -cvar_t cl_chasecam = {"cl_chasecam", "0"}; - -//cvar_t cl_camera_maxpitch = {"cl_camera_maxpitch", "10" }; -//cvar_t cl_camera_maxyaw = {"cl_camera_maxyaw", "30" }; - -qboolean cam_forceview; -vec3_t cam_viewangles; -double cam_lastviewtime; - -int spec_track = 0; // player# of who we are tracking -int ideal_track = 0; -float last_lock = 0; -int autocam = CAM_NONE; - -void vectoangles(vec3_t vec, vec3_t ang) -{ - float forward; - float yaw, pitch; - - if (vec[1] == 0 && vec[0] == 0) - { - yaw = 0; - if (vec[2] > 0) - pitch = 90; - else - pitch = 270; - } - else - { - yaw = (int) (atan2(vec[1], vec[0]) * 180 / M_PI); - if (yaw < 0) - yaw += 360; - - forward = sqrt (vec[0]*vec[0] + vec[1]*vec[1]); - pitch = (int) (atan2(vec[2], forward) * 180 / M_PI); - if (pitch < 0) - pitch += 360; - } - - ang[0] = pitch; - ang[1] = yaw; - ang[2] = 0; -} - -static float vlen(vec3_t v) -{ - return sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]); -} - -// returns true if weapon model should be drawn in camera mode -qboolean Cam_DrawViewModel(void) -{ - if (!cl.spectator) - return true; - - if (autocam && locked && cl_chasecam.value) - return true; - return false; -} - -// returns true if we should draw this player, we don't if we are chase camming -qboolean Cam_DrawPlayer(int playernum) -{ - if (cl.spectator && autocam && locked && cl_chasecam.value && - spec_track == playernum) - return false; - return true; -} - -int Cam_TrackNum(void) -{ - if (!autocam) - return -1; - return spec_track; -} -void TP_FixTeamSets(void); - -void Cam_Unlock(void) -{ - if (autocam) { - MSG_WriteByte (&cls.netchan.message, clc_stringcmd); - MSG_WriteString (&cls.netchan.message, "ptrack"); - autocam = CAM_NONE; - locked = false; - Sbar_Changed(); - TP_FixTeamSets(); - } -} - -void Cam_Lock(int playernum) -{ - char st[40]; - - sprintf(st, "ptrack %i", playernum); - if (cls.demoplayback2) { - memcpy(cl.stats, cl.players[playernum].stats, sizeof(cl.stats)); - } - - MSG_WriteByte (&cls.netchan.message, clc_stringcmd); - MSG_WriteString (&cls.netchan.message, st); - spec_track = playernum; - last_lock = realtime; - cam_forceview = true; - locked = false; - Sbar_Changed(); - TP_FixTeamSets(); -} - -pmtrace_t Cam_DoTrace(vec3_t vec1, vec3_t vec2) -{ -#if 0 - memset(&pmove, 0, sizeof(pmove)); - - pmove.numphysent = 1; - VectorCopy (vec3_origin, pmove.physents[0].origin); - pmove.physents[0].model = cl.worldmodel; -#endif - - VectorCopy (vec1, pmove.origin); - return PM_PlayerMove(pmove.origin, vec2); -} - -// Returns distance or 9999 if invalid for some reason -static float Cam_TryFlyby(player_state_t *self, player_state_t *player, vec3_t vec, qboolean checkvis) -{ - vec3_t v; - pmtrace_t trace; - float len; - - vectoangles(vec, v); -// v[0] = -v[0]; - VectorCopy (v, pmove.angles); - VectorNormalize(vec); - VectorMA(player->origin, 800, vec, v); - // v is endpos - // fake a player move - trace = Cam_DoTrace(player->origin, v); - if (/*trace.inopen ||*/ trace.inwater) - return 9999; - VectorCopy(trace.endpos, vec); - VectorSubtract(trace.endpos, player->origin, v); - len = sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]); - if (len < 32 || len > 800) - return 9999; - if (checkvis) { - VectorSubtract(trace.endpos, self->origin, v); - len = sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]); - - trace = Cam_DoTrace(self->origin, vec); - if (trace.fraction != 1 || trace.inwater) - return 9999; - } - return len; -} - -// Is player visible? -static qboolean Cam_IsVisible(player_state_t *player, vec3_t vec) -{ - pmtrace_t trace; - vec3_t v; - float d; - - trace = Cam_DoTrace(player->origin, vec); - if (trace.fraction != 1 || /*trace.inopen ||*/ trace.inwater) - return false; - // check distance, don't let the player get too far away or too close - VectorSubtract(player->origin, vec, v); - d = vlen(v); - if (d < 16) - return false; - return true; -} - -static qboolean InitFlyby(player_state_t *self, player_state_t *player, int checkvis) -{ - float f, max; - vec3_t vec, vec2; - vec3_t forward, right, up; - - VectorCopy(player->viewangles, vec); - vec[0] = 0; - AngleVectors (vec, forward, right, up); -// for (i = 0; i < 3; i++) -// forward[i] *= 3; - - max = 1000; - VectorAdd(forward, up, vec2); - VectorAdd(vec2, right, vec2); - if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < max) { - max = f; - VectorCopy(vec2, vec); - } - VectorAdd(forward, up, vec2); - VectorSubtract(vec2, right, vec2); - if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < max) { - max = f; - VectorCopy(vec2, vec); - } - VectorAdd(forward, right, vec2); - if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < max) { - max = f; - VectorCopy(vec2, vec); - } - VectorSubtract(forward, right, vec2); - if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < max) { - max = f; - VectorCopy(vec2, vec); - } - VectorAdd(forward, up, vec2); - if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < max) { - max = f; - VectorCopy(vec2, vec); - } - VectorSubtract(forward, up, vec2); - if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < max) { - max = f; - VectorCopy(vec2, vec); - } - VectorAdd(up, right, vec2); - VectorSubtract(vec2, forward, vec2); - if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < max) { - max = f; - VectorCopy(vec2, vec); - } - VectorSubtract(up, right, vec2); - VectorSubtract(vec2, forward, vec2); - if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < max) { - max = f; - VectorCopy(vec2, vec); - } - // invert - VectorSubtract(vec3_origin, forward, vec2); - if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < max) { - max = f; - VectorCopy(vec2, vec); - } - VectorCopy(forward, vec2); - if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < max) { - max = f; - VectorCopy(vec2, vec); - } - // invert - VectorSubtract(vec3_origin, right, vec2); - if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < max) { - max = f; - VectorCopy(vec2, vec); - } - VectorCopy(right, vec2); - if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < max) { - max = f; - VectorCopy(vec2, vec); - } - - // ack, can't find him - if (max >= 1000) { -// Cam_Unlock(); - return false; - } - locked = true; - VectorCopy(vec, desired_position); - return true; -} - -static void Cam_CheckHighTarget(void) -{ - int i, j, max; - player_info_t *s; - - j = -1; - for (i = 0, max = -9999; i < MAX_CLIENTS; i++) { - s = &cl.players[i]; - if (s->name[0] && !s->spectator && s->frags > max) { - max = s->frags; - j = i; - } - } - if (j >= 0) { - if (!locked || cl.players[j].frags > cl.players[spec_track].frags) - { - Cam_Lock(j); - ideal_track = spec_track; - } - } else - Cam_Unlock(); -} - -// ZOID -// -// Take over the user controls and track a player. -// We find a nice position to watch the player and move there -void Cam_Track(usercmd_t *cmd) -{ - player_state_t *player, *self; - frame_t *frame; - vec3_t vec; - float len; - - if (!cl.spectator) - return; - - if (cl_hightrack.value && !locked) - Cam_CheckHighTarget(); - - if (!autocam || cls.state != ca_active) - return; - - if (locked && (!cl.players[spec_track].name[0] || cl.players[spec_track].spectator)) { - locked = false; - if (cl_hightrack.value) - Cam_CheckHighTarget(); - else - Cam_Unlock(); - return; - } - - frame = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK]; - if (autocam && cls.demoplayback2 && cl_track_validonly.value) - { - if (ideal_track != spec_track && realtime - last_lock > 1 && frame->playerstate[ideal_track].messagenum == cl.parsecount) - Cam_Lock(ideal_track); - - if (frame->playerstate[spec_track].messagenum != cl.parsecount) - { - int i; - - for (i = 0; i < MAX_CLIENTS; i++) - { - if (frame->playerstate[i].messagenum == cl.parsecount) - break; - } - if (i < MAX_CLIENTS) - Cam_Lock(i); - } - } - - player = frame->playerstate + spec_track; - self = frame->playerstate + cl.playernum; - - if (!locked || !Cam_IsVisible(player, desired_position)) { - if (!locked || realtime - cam_lastviewtime > 0.1) { - if (!InitFlyby(self, player, true)) - InitFlyby(self, player, false); - cam_lastviewtime = realtime; - } - } else - cam_lastviewtime = realtime; - - // couldn't track for some reason - if (!locked || !autocam) - return; - - if (cl_chasecam.value) { - cmd->forwardmove = cmd->sidemove = cmd->upmove = 0; - - VectorCopy(player->viewangles, cl.viewangles); - VectorCopy(player->origin, desired_position); - if (memcmp(&desired_position, &self->origin, sizeof(desired_position)) != 0) { - MSG_WriteByte (&cls.netchan.message, clc_tmove); - MSG_WriteCoord (&cls.netchan.message, desired_position[0]); - MSG_WriteCoord (&cls.netchan.message, desired_position[1]); - MSG_WriteCoord (&cls.netchan.message, desired_position[2]); - // move there locally immediately - VectorCopy(desired_position, self->origin); - } - self->weaponframe = player->weaponframe; - - } else { - // Ok, move to our desired position and set our angles to view - // the player - VectorSubtract(desired_position, self->origin, vec); - len = vlen(vec); - cmd->forwardmove = cmd->sidemove = cmd->upmove = 0; - if (len > 16) { // close enough? - MSG_WriteByte (&cls.netchan.message, clc_tmove); - MSG_WriteCoord (&cls.netchan.message, desired_position[0]); - MSG_WriteCoord (&cls.netchan.message, desired_position[1]); - MSG_WriteCoord (&cls.netchan.message, desired_position[2]); - } - - // move there locally immediately - VectorCopy(desired_position, self->origin); - - VectorSubtract(player->origin, desired_position, vec); - vectoangles(vec, cl.viewangles); - cl.viewangles[0] = -cl.viewangles[0]; - } -} - -#if 0 -static float adjustang(float current, float ideal, float speed) -{ - float move; - - current = anglemod(current); - ideal = anglemod(ideal); - - if (current == ideal) - return current; - - move = ideal - current; - if (ideal > current) - { - if (move >= 180) - move = move - 360; - } - else - { - if (move <= -180) - move = move + 360; - } - if (move > 0) - { - if (move > speed) - move = speed; - } - else - { - if (move < -speed) - move = -speed; - } - -//Con_Printf("c/i: %4.2f/%4.2f move: %4.2f\n", current, ideal, move); - return anglemod (current + move); -} -#endif - -#if 0 -void Cam_SetView(void) -{ - return; - player_state_t *player, *self; - frame_t *frame; - vec3_t vec, vec2; - - if (cls.state != ca_active || !cl.spectator || - !autocam || !locked) - return; - - frame = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK]; - player = frame->playerstate + spec_track; - self = frame->playerstate + cl.playernum; - - VectorSubtract(player->origin, cl.simorg, vec); - if (cam_forceview) { - cam_forceview = false; - vectoangles(vec, cam_viewangles); - cam_viewangles[0] = -cam_viewangles[0]; - } else { - vectoangles(vec, vec2); - vec2[PITCH] = -vec2[PITCH]; - - cam_viewangles[PITCH] = adjustang(cam_viewangles[PITCH], vec2[PITCH], cl_camera_maxpitch.value); - cam_viewangles[YAW] = adjustang(cam_viewangles[YAW], vec2[YAW], cl_camera_maxyaw.value); - } - VectorCopy(cam_viewangles, cl.viewangles); - VectorCopy(cl.viewangles, cl.simangles); -} -#endif - -void Cam_FinishMove(usercmd_t *cmd) -{ - int i; - player_info_t *s; - int end; - - if (cls.state != ca_active) - return; - - if (!cl.spectator) // only in spectator mode - return; - -#if 0 - if (autocam && locked) { - frame = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK]; - player = frame->playerstate + spec_track; - self = frame->playerstate + cl.playernum; - - VectorSubtract(player->origin, self->origin, vec); - if (cam_forceview) { - cam_forceview = false; - vectoangles(vec, cam_viewangles); - cam_viewangles[0] = -cam_viewangles[0]; - } else { - vectoangles(vec, vec2); - vec2[PITCH] = -vec2[PITCH]; - - cam_viewangles[PITCH] = adjustang(cam_viewangles[PITCH], vec2[PITCH], cl_camera_maxpitch.value); - cam_viewangles[YAW] = adjustang(cam_viewangles[YAW], vec2[YAW], cl_camera_maxyaw.value); - } - VectorCopy(cam_viewangles, cl.viewangles); - } -#endif - - if (cmd->buttons & BUTTON_ATTACK) { - if (!(oldbuttons & BUTTON_ATTACK)) { - - oldbuttons |= BUTTON_ATTACK; - autocam++; - - if (autocam > CAM_TRACK) { - Cam_Unlock(); - VectorCopy(cl.viewangles, cmd->angles); - return; - } - } else - return; - } else { - oldbuttons &= ~BUTTON_ATTACK; - if (!autocam) - return; - } - - if (autocam && cl_hightrack.value) { - Cam_CheckHighTarget(); - return; - } - - if (locked) { - if ((cmd->buttons & BUTTON_JUMP) && (oldbuttons & BUTTON_JUMP)) { - return; // don't pogo stick - } - - if (!(cmd->buttons & BUTTON_JUMP)) { - oldbuttons &= ~BUTTON_JUMP; - return; - } - oldbuttons |= BUTTON_JUMP; // don't jump again until released - } - -// Con_Printf("Selecting track target...\n"); - - if (locked && autocam) - end = (ideal_track + 1) % MAX_CLIENTS; - else - end = ideal_track; - i = end; - do { - s = &cl.players[i]; - if (s->name[0] && !s->spectator) { - Cam_Lock(i); - Con_Printf("tracking %s\n", s->name); - ideal_track = i; - return; - } - i = (i + 1) % MAX_CLIENTS; - } while (i != end); - // stay on same guy? - i = ideal_track; - s = &cl.players[i]; - if (s->name[0] && !s->spectator) { - Cam_Lock(i); - ideal_track = i; - return; - } - Con_Printf("No target found ...\n"); - autocam = locked = false; -} - -void Cam_Reset(void) -{ - autocam = CAM_NONE; - spec_track = 0; - ideal_track = 0; -} - -void CL_InitCam(void) -{ - Cvar_RegisterVariable (&cl_hightrack); - Cvar_RegisterVariable (&cl_chasecam); - Cvar_RegisterVariable (&cl_track_validonly); -// Cvar_RegisterVariable (&cl_camera_maxpitch); -// Cvar_RegisterVariable (&cl_camera_maxyaw); -} - - +/* +Copyright (C) 1996-1997 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 included (GNU.txt) 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. + +*/ +/* ZOID + * + * Player camera tracking in Spectator mode + * + * This takes over player controls for spectator automatic camera. + * Player moves as a spectator, but the camera tracks and enemy player + */ + +#include "quakedef.h" +#include "winquake.h" +#include "pmove.h" +#include "sbar.h" + +#define BUTTON_JUMP 2 +#define BUTTON_ATTACK 1 +#define MAX_ANGLE_TURN 10 + +static vec3_t desired_position; // where the camera wants to be +static qboolean locked = false; +static int oldbuttons; + +// track high fragger +cvar_t cl_hightrack = {"cl_hightrack", "0" }; +cvar_t cl_track_validonly = {"cl_track_validonly", "1" }; +cvar_t cl_matrixcam_dist = {"cl_matrixcam_dist", "50" }; +cvar_t cl_matrixcam_speed = {"cl_matrixcam_speed", "3" }; + +cvar_t cl_chasecam = {"cl_chasecam", "0"}; + +//cvar_t cl_camera_maxpitch = {"cl_camera_maxpitch", "10" }; +//cvar_t cl_camera_maxyaw = {"cl_camera_maxyaw", "30" }; + +qboolean cam_forceview; +vec3_t cam_viewangles; +double cam_lastviewtime; + +int spec_track = 0; // player# of who we are tracking +int ideal_track = 0; +float last_lock = 0; +int autocam = CAM_NONE; + +void vectoangles(vec3_t vec, vec3_t ang) +{ + float forward; + float yaw, pitch; + + if (vec[1] == 0 && vec[0] == 0) + { + yaw = 0; + if (vec[2] > 0) + pitch = 90; + else + pitch = 270; + } + else + { + yaw = (int) (atan2(vec[1], vec[0]) * 180 / M_PI); + if (yaw < 0) + yaw += 360; + + forward = sqrt (vec[0]*vec[0] + vec[1]*vec[1]); + pitch = (int) (atan2(vec[2], forward) * 180 / M_PI); + if (pitch < 0) + pitch += 360; + } + + ang[0] = pitch; + ang[1] = yaw; + ang[2] = 0; +} + +static float vlen(vec3_t v) +{ + return sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]); +} + +// returns true if weapon model should be drawn in camera mode +qboolean Cam_DrawViewModel(void) +{ + if (!cl.spectator) + return true; + + if (autocam && locked && cl_chasecam.value == 1) + return true; + return false; +} + +static qboolean tooClose = false; + +// returns true if we should draw this player, we don't if we are chase camming +qboolean Cam_DrawPlayer(int playernum) +{ + if (cl.spectator && autocam && locked && spec_track == playernum) { + if (cl_chasecam.value == 1) + return false; + if (cl_chasecam.value == 3 && tooClose) + return false; + } + + return true; +} + +int Cam_TrackNum(void) +{ + if (!autocam) + return -1; + return spec_track; +} +void TP_FixTeamSets(void); + +void Cam_Unlock(void) +{ + if (autocam) { + MSG_WriteByte (&cls.netchan.message, clc_stringcmd); + MSG_WriteString (&cls.netchan.message, "ptrack"); + autocam = CAM_NONE; + locked = false; + Sbar_Changed(); + TP_FixTeamSets(); + } +} + +void Cam_Lock(int playernum) +{ + char st[40]; + + sprintf(st, "ptrack %i", playernum); + if (cls.demoplayback2) { + memcpy(cl.stats, cl.players[playernum].stats, sizeof(cl.stats)); + } + + MSG_WriteByte (&cls.netchan.message, clc_stringcmd); + MSG_WriteString (&cls.netchan.message, st); + spec_track = playernum; + last_lock = realtime; + cam_forceview = true; + locked = false; + Sbar_Changed(); + TP_FixTeamSets(); +} + +pmtrace_t Cam_DoTrace(vec3_t vec1, vec3_t vec2) +{ +#if 0 + memset(&pmove, 0, sizeof(pmove)); + + pmove.numphysent = 1; + VectorCopy (vec3_origin, pmove.physents[0].origin); + pmove.physents[0].model = cl.worldmodel; +#endif + + VectorCopy (vec1, pmove.origin); + return PM_PlayerMove(pmove.origin, vec2); +} + +// Returns distance or 9999 if invalid for some reason +static float Cam_TryFlyby(player_state_t *self, player_state_t *player, vec3_t vec, qboolean checkvis) +{ + vec3_t v; + pmtrace_t trace; + float len; + + vectoangles(vec, v); +// v[0] = -v[0]; + VectorCopy (v, pmove.angles); + VectorNormalize(vec); + VectorMA(player->origin, 800, vec, v); + // v is endpos + // fake a player move + trace = Cam_DoTrace(player->origin, v); + if (/*trace.inopen ||*/ trace.inwater) + return 9999; + VectorCopy(trace.endpos, vec); + VectorSubtract(trace.endpos, player->origin, v); + len = sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]); + if (len < 32 || len > 800) + return 9999; + if (checkvis) { + VectorSubtract(trace.endpos, self->origin, v); + len = sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]); + + trace = Cam_DoTrace(self->origin, vec); + if (trace.fraction != 1 || trace.inwater) + return 9999; + } + return len; +} + +// Is player visible? +static qboolean Cam_IsVisible(player_state_t *player, vec3_t vec) +{ + pmtrace_t trace; + vec3_t v; + float d; + + trace = Cam_DoTrace(player->origin, vec); + if (trace.fraction != 1 || /*trace.inopen ||*/ trace.inwater) + return false; + // check distance, don't let the player get too far away or too close + VectorSubtract(player->origin, vec, v); + d = vlen(v); + if (d < 16) + return false; + return true; +} + +static qboolean InitFlyby(player_state_t *self, player_state_t *player, int checkvis) +{ + float f, max; + vec3_t vec, vec2; + vec3_t forward, right, up; + + VectorCopy(player->viewangles, vec); + vec[0] = 0; + AngleVectors (vec, forward, right, up); +// for (i = 0; i < 3; i++) +// forward[i] *= 3; + + max = 1000; + VectorAdd(forward, up, vec2); + VectorAdd(vec2, right, vec2); + if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < max) { + max = f; + VectorCopy(vec2, vec); + } + VectorAdd(forward, up, vec2); + VectorSubtract(vec2, right, vec2); + if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < max) { + max = f; + VectorCopy(vec2, vec); + } + VectorAdd(forward, right, vec2); + if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < max) { + max = f; + VectorCopy(vec2, vec); + } + VectorSubtract(forward, right, vec2); + if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < max) { + max = f; + VectorCopy(vec2, vec); + } + VectorAdd(forward, up, vec2); + if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < max) { + max = f; + VectorCopy(vec2, vec); + } + VectorSubtract(forward, up, vec2); + if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < max) { + max = f; + VectorCopy(vec2, vec); + } + VectorAdd(up, right, vec2); + VectorSubtract(vec2, forward, vec2); + if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < max) { + max = f; + VectorCopy(vec2, vec); + } + VectorSubtract(up, right, vec2); + VectorSubtract(vec2, forward, vec2); + if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < max) { + max = f; + VectorCopy(vec2, vec); + } + // invert + VectorSubtract(vec3_origin, forward, vec2); + if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < max) { + max = f; + VectorCopy(vec2, vec); + } + VectorCopy(forward, vec2); + if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < max) { + max = f; + VectorCopy(vec2, vec); + } + // invert + VectorSubtract(vec3_origin, right, vec2); + if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < max) { + max = f; + VectorCopy(vec2, vec); + } + VectorCopy(right, vec2); + if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < max) { + max = f; + VectorCopy(vec2, vec); + } + + // ack, can't find him + if (max >= 1000) { +// Cam_Unlock(); + return false; + } + locked = true; + VectorCopy(vec, desired_position); + return true; +} + +static void Cam_CheckHighTarget(void) +{ + int i, j, max; + player_info_t *s; + + j = -1; + for (i = 0, max = -9999; i < MAX_CLIENTS; i++) { + s = &cl.players[i]; + if (s->name[0] && !s->spectator && s->frags > max) { + max = s->frags; + j = i; + } + } + if (j >= 0) { + if (!locked || cl.players[j].frags > cl.players[spec_track].frags) + { + Cam_Lock(j); + ideal_track = spec_track; + } + } else + Cam_Unlock(); +} + +void Transform(vec3_t in, double angle, vec3_t out) +{ + float si, co; + + si = sin(angle); + co = cos(angle); + + out[0] = in[0]*co + in[1]*si; + out[1] = -in[0]*si + in[1]*co; + out[2] = in[2]; +} + +void Cam_Matrix(usercmd_t *cmd, player_state_t *self, player_state_t *player) +{ + vec3_t ideal, dist, forward; + pmtrace_t trace; + float l, scale, len, camdist, camspeed; + + camdist = max(cl_matrixcam_dist.value, 5); + camspeed = max(cl_matrixcam_speed.value, 0); + + cmd->forwardmove = cmd->sidemove = cmd->upmove = 0; + + VectorCopy(self->origin, ideal); + ideal[2] = player->origin[2]; + + VectorSubtract(ideal, player->origin, dist); + + if ( (len = vlen(dist)) == 0 ) { + len = 1; + dist[0] = 1; + } + + Transform(dist, camspeed*real_frametime, forward); + + l = camdist - len; + if (camdist > len) + scale = min(l, 500*real_frametime); + else + scale = max(l, -500*real_frametime); + + VectorMA(player->origin, (len + scale)/len, forward, ideal); + + trace = Cam_DoTrace(player->origin, ideal); + //VectorCopy(trace.endpos, self->origin); + VectorCopy(trace.endpos, desired_position); + + //VectorSubtract(player->origin, self->origin, forward); + //vectoangles(forward, cl.viewangles); +} + +void Cam_Chase(usercmd_t *cmd, player_state_t *self, player_state_t *player) +{ + vec3_t ideal, forward, right, up; + pmtrace_t trace; + float camdist; + + camdist = max(cl_matrixcam_dist.value, 5); + + cmd->forwardmove = cmd->sidemove = cmd->upmove = 0; + + AngleVectors(player->viewangles, forward, right, up); + + VectorMA(player->origin, -camdist, forward, ideal); + ideal[2] += 10; + + trace = Cam_DoTrace(player->origin, ideal); + if (trace.fraction != 1.0) { + VectorMA(trace.endpos, -min(1.0 - trace.fraction, 16.0/camdist), forward, ideal); + VectorCopy(ideal, desired_position); + } else + VectorCopy(trace.endpos, desired_position); + + // if to close don't draw the player + VectorSubtract(desired_position, player->origin, ideal); + if (vlen(ideal) < 16) + tooClose = true; + else + tooClose = false; +} + +// ZOID +// +// Take over the user controls and track a player. +// We find a nice position to watch the player and move there +void Cam_Track(usercmd_t *cmd) +{ + player_state_t *player, *self; + frame_t *frame; + vec3_t vec; + float len; + + if (!cl.spectator) + return; + + if (cl_hightrack.value && !locked) + Cam_CheckHighTarget(); + + if (!autocam || cls.state != ca_active) + return; + + if (locked && (!cl.players[spec_track].name[0] || cl.players[spec_track].spectator)) { + locked = false; + if (cl_hightrack.value) + Cam_CheckHighTarget(); + else + Cam_Unlock(); + return; + } + + frame = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK]; + if (autocam && cls.demoplayback2 && cl_track_validonly.value) + { + if (ideal_track != spec_track && realtime - last_lock > 1 && frame->playerstate[ideal_track].messagenum == cl.parsecount) + Cam_Lock(ideal_track); + + if (frame->playerstate[spec_track].messagenum != cl.parsecount) + { + int i; + + for (i = 0; i < MAX_CLIENTS; i++) + { + if (frame->playerstate[i].messagenum == cl.parsecount) + break; + } + if (i < MAX_CLIENTS) + Cam_Lock(i); + } + } + + player = frame->playerstate + spec_track; + self = frame->playerstate + cl.playernum; + + if (!locked || !Cam_IsVisible(player, desired_position)) { + if (!locked || realtime - cam_lastviewtime > 0.1) { + if (!InitFlyby(self, player, true)) + InitFlyby(self, player, false); + cam_lastviewtime = realtime; + } + } else + cam_lastviewtime = realtime; + + // couldn't track for some reason + if (!locked || !autocam) + return; + + if (cl_chasecam.value == 1) { + cmd->forwardmove = cmd->sidemove = cmd->upmove = 0; + + VectorCopy(player->viewangles, cl.viewangles); + VectorCopy(player->origin, desired_position); + if (memcmp(&desired_position, &self->origin, sizeof(desired_position)) != 0) { + MSG_WriteByte (&cls.netchan.message, clc_tmove); + MSG_WriteCoord (&cls.netchan.message, desired_position[0]); + MSG_WriteCoord (&cls.netchan.message, desired_position[1]); + MSG_WriteCoord (&cls.netchan.message, desired_position[2]); + // move there locally immediately + VectorCopy(desired_position, self->origin); + } + self->weaponframe = player->weaponframe; + + } else { + if (cl_chasecam.value == 2) + Cam_Matrix(cmd, self, player); + if (cl_chasecam.value == 3) + Cam_Chase(cmd, self, player); + + // Ok, move to our desired position and set our angles to view + // the player + VectorSubtract(desired_position, self->origin, vec); + len = vlen(vec); + cmd->forwardmove = cmd->sidemove = cmd->upmove = 0; + if (len > 16 || cl_chasecam.value == 2) { // close enough? + MSG_WriteByte (&cls.netchan.message, clc_tmove); + MSG_WriteCoord (&cls.netchan.message, desired_position[0]); + MSG_WriteCoord (&cls.netchan.message, desired_position[1]); + MSG_WriteCoord (&cls.netchan.message, desired_position[2]); + } + + // move there locally immediately + VectorCopy(desired_position, self->origin); + + VectorSubtract(player->origin, desired_position, vec); + vectoangles(vec, cl.viewangles); + cl.viewangles[0] = -cl.viewangles[0]; + } +} + +#if 0 +static float adjustang(float current, float ideal, float speed) +{ + float move; + + current = anglemod(current); + ideal = anglemod(ideal); + + if (current == ideal) + return current; + + move = ideal - current; + if (ideal > current) + { + if (move >= 180) + move = move - 360; + } + else + { + if (move <= -180) + move = move + 360; + } + if (move > 0) + { + if (move > speed) + move = speed; + } + else + { + if (move < -speed) + move = -speed; + } + +//Con_Printf("c/i: %4.2f/%4.2f move: %4.2f\n", current, ideal, move); + return anglemod (current + move); +} +#endif + +#if 0 +void Cam_SetView(void) +{ + return; + player_state_t *player, *self; + frame_t *frame; + vec3_t vec, vec2; + + if (cls.state != ca_active || !cl.spectator || + !autocam || !locked) + return; + + frame = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK]; + player = frame->playerstate + spec_track; + self = frame->playerstate + cl.playernum; + + VectorSubtract(player->origin, cl.simorg, vec); + if (cam_forceview) { + cam_forceview = false; + vectoangles(vec, cam_viewangles); + cam_viewangles[0] = -cam_viewangles[0]; + } else { + vectoangles(vec, vec2); + vec2[PITCH] = -vec2[PITCH]; + + cam_viewangles[PITCH] = adjustang(cam_viewangles[PITCH], vec2[PITCH], cl_camera_maxpitch.value); + cam_viewangles[YAW] = adjustang(cam_viewangles[YAW], vec2[YAW], cl_camera_maxyaw.value); + } + VectorCopy(cam_viewangles, cl.viewangles); + VectorCopy(cl.viewangles, cl.simangles); +} +#endif + +void Cam_FinishMove(usercmd_t *cmd) +{ + int i; + player_info_t *s; + int end; + + if (cls.state != ca_active) + return; + + if (!cl.spectator) // only in spectator mode + return; + +#if 0 + if (autocam && locked) { + frame = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK]; + player = frame->playerstate + spec_track; + self = frame->playerstate + cl.playernum; + + VectorSubtract(player->origin, self->origin, vec); + if (cam_forceview) { + cam_forceview = false; + vectoangles(vec, cam_viewangles); + cam_viewangles[0] = -cam_viewangles[0]; + } else { + vectoangles(vec, vec2); + vec2[PITCH] = -vec2[PITCH]; + + cam_viewangles[PITCH] = adjustang(cam_viewangles[PITCH], vec2[PITCH], cl_camera_maxpitch.value); + cam_viewangles[YAW] = adjustang(cam_viewangles[YAW], vec2[YAW], cl_camera_maxyaw.value); + } + VectorCopy(cam_viewangles, cl.viewangles); + } +#endif + + if (cmd->buttons & BUTTON_ATTACK) { + if (!(oldbuttons & BUTTON_ATTACK)) { + + oldbuttons |= BUTTON_ATTACK; + autocam++; + + if (autocam > CAM_TRACK) { + Cam_Unlock(); + VectorCopy(cl.viewangles, cmd->angles); + return; + } + } else + return; + } else { + oldbuttons &= ~BUTTON_ATTACK; + if (!autocam) + return; + } + + if (autocam && cl_hightrack.value) { + Cam_CheckHighTarget(); + return; + } + + if (locked) { + if ((cmd->buttons & BUTTON_JUMP) && (oldbuttons & BUTTON_JUMP)) { + return; // don't pogo stick + } + + if (!(cmd->buttons & BUTTON_JUMP)) { + oldbuttons &= ~BUTTON_JUMP; + return; + } + oldbuttons |= BUTTON_JUMP; // don't jump again until released + } + +// Con_Printf("Selecting track target...\n"); + + if (locked && autocam) + end = (ideal_track + 1) % MAX_CLIENTS; + else + end = ideal_track; + i = end; + do { + s = &cl.players[i]; + if (s->name[0] && !s->spectator) { + Cam_Lock(i); + Con_Printf("tracking %s\n", s->name); + ideal_track = i; + return; + } + i = (i + 1) % MAX_CLIENTS; + } while (i != end); + // stay on same guy? + i = ideal_track; + s = &cl.players[i]; + if (s->name[0] && !s->spectator) { + Cam_Lock(i); + ideal_track = i; + return; + } + Con_Printf("No target found ...\n"); + autocam = locked = false; +} + +void Cam_Reset(void) +{ + autocam = CAM_NONE; + spec_track = 0; + ideal_track = 0; +} + +void CL_InitCam(void) +{ + Cvar_RegisterVariable (&cl_hightrack); + Cvar_RegisterVariable (&cl_chasecam); + Cvar_RegisterVariable (&cl_track_validonly); + Cvar_RegisterVariable (&cl_matrixcam_dist); + Cvar_RegisterVariable (&cl_matrixcam_speed); +// Cvar_RegisterVariable (&cl_camera_maxpitch); +// Cvar_RegisterVariable (&cl_camera_maxyaw); +} + + diff --git a/source/cl_cmd.c b/source/cl_cmd.c index 74729580..3ed51767 100644 --- a/source/cl_cmd.c +++ b/source/cl_cmd.c @@ -1,781 +1,820 @@ -/* -Copyright (C) 1996-1997 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 "quakedef.h" -#include "winquake.h" -#include "teamplay.h" -#include "version.h" - -void SCR_RSShot_f (void); -void CL_ProcessServerInfo (void); -void SV_Serverinfo_f (void); -void Key_WriteBindings (FILE *f); -void S_StopAllSounds (qboolean clear); - -/* -=================== -Cmd_ForwardToServer - -adds the current command line as a clc_stringcmd to the client message. -things like kill, say, etc, are commands directed to the server, -so when they are typed in at the console, they will need to be forwarded. -=================== -*/ -void Cmd_ForwardToServer (void) -{ - char *s; - - if (cls.state == ca_disconnected) - { - Con_Printf ("Can't \"%s\", not connected\n", Cmd_Argv(0)); - return; - } - - MSG_WriteByte (&cls.netchan.message, clc_stringcmd); - // lowercase command - for (s=Cmd_Argv(0) ; *s ; s++) - *s = (char)tolower(*s); - SZ_Print (&cls.netchan.message, Cmd_Argv(0)); - if (Cmd_Argc() > 1) - { - SZ_Print (&cls.netchan.message, " "); - SZ_Print (&cls.netchan.message, Cmd_Args()); - } -} - -// don't forward the first argument -void CL_ForwardToServer_f (void) -{ - if (cls.state == ca_disconnected) - { - Con_Printf ("Can't \"%s\", not connected\n", Cmd_Argv(0)); - return; - } - - if (Q_strcasecmp(Cmd_Argv(1), "snap") == 0) { - SCR_RSShot_f (); - return; - } - - if (cls.demoplayback) - return; // not really connected - - if (Cmd_Argc() > 1) - { - MSG_WriteByte (&cls.netchan.message, clc_stringcmd); - SZ_Print (&cls.netchan.message, Cmd_Args()); - } -} - -/* -=============== -CL_Say_f - -Handles both say and say_team -=============== -*/ -void CL_Say_f (void) -{ - char *s; - - if (cls.state == ca_disconnected) - { - Con_Printf ("Can't \"%s\", not connected\n", Cmd_Argv(0)); - return; - } - - MSG_WriteByte (&cls.netchan.message, clc_stringcmd); - // lowercase command - for (s=Cmd_Argv(0) ; *s ; s++) - *s = (char)tolower(*s); - SZ_Print (&cls.netchan.message, Cmd_Argv(0)); - if (Cmd_Argc() > 1) - { - SZ_Print (&cls.netchan.message, " "); - - s = TP_ParseMacroString(Cmd_Args()); - if (*s && *s < 32 && *s != 10) - { - SZ_Print (&cls.netchan.message, "\""); - SZ_Print (&cls.netchan.message, s); - SZ_Print (&cls.netchan.message, "\""); - } - else - SZ_Print (&cls.netchan.message, s); - } -} - - -/* -===================== -CL_Pause_f -===================== -*/ -void CL_Pause_f (void) -{ - if (cls.demoplayback) - cl.paused ^= 2; - else - Cmd_ForwardToServer(); -} - - -/* -==================== -CL_Packet_f - -packet - -Contents allows \n escape character -==================== -*/ -void CL_Packet_f (void) -{ - char send[2048]; - int i, l; - char *in, *out; - netadr_t adr; - - if (Cmd_Argc() != 3) - { - Con_Printf ("packet \n"); - return; - } - - if (!NET_StringToAdr (Cmd_Argv(1), &adr)) - { - Con_Printf ("Bad address\n"); - return; - } - - if (adr.port == 0) - adr.port = BigShort (27500); - - in = Cmd_Argv(2); - out = send+4; - send[0] = send[1] = send[2] = send[3] = 0xff; - - l = strlen (in); - for (i=0 ; i= ca_connected) - to = cls.netchan.remote_address; - else - { - if (!strlen(rcon_address.string)) - { - Con_Printf ("You must either be connected,\n" - "or set the 'rcon_address' cvar\n" - "to issue rcon commands\n"); - - return; - } - NET_StringToAdr (rcon_address.string, &to); - if (to.port == 0) - to.port = BigShort (27500); - } - - NET_SendPacket (net_clientsocket, strlen(message)+1, message - , to); -} - - -/* -===================== -CL_Download_f -===================== -*/ -void CL_Download_f (void) -{ - char *p, *q; - - if (cls.state == ca_disconnected) - { - Con_Printf ("Must be connected.\n"); - return; - } - - if (Cmd_Argc() != 2) - { - Con_Printf ("Usage: download \n"); - return; - } - - sprintf (cls.downloadname, "%s/%s", com_gamedir, Cmd_Argv(1)); - - p = cls.downloadname; - for (;;) { - if ((q = strchr(p, '/')) != NULL) { - *q = 0; - Sys_mkdir(cls.downloadname); - *q = '/'; - p = q + 1; - } else - break; - } - - if (cls.download) - { - fclose(cls.download); - } - - strcpy(cls.downloadtempname, cls.downloadname); - cls.download = fopen (cls.downloadname, "wb"); - cls.downloadtype = dl_single; - - MSG_WriteByte (&cls.netchan.message, clc_stringcmd); - SZ_Print (&cls.netchan.message, va("download %s\n",Cmd_Argv(1))); -} - - -/* -==================== -CL_User_f - -user - -Dump userdata / masterdata for a user -==================== -*/ -void CL_User_f (void) -{ - int uid; - int i; - - if (Cmd_Argc() != 2) - { - Con_Printf ("Usage: user \n"); - return; - } - - uid = atoi(Cmd_Argv(1)); - - for (i=0 ; i [0-13]\n"); - return; - } - - if (Cmd_Argc() == 2) - top = bottom = atoi(Cmd_Argv(1)); - else - { - top = atoi(Cmd_Argv(1)); - bottom = atoi(Cmd_Argv(2)); - } - - top &= 15; - if (top > 13) - top = 13; - bottom &= 15; - if (bottom > 13) - bottom = 13; - - sprintf (num, "%i", top); - Cvar_Set (&topcolor, num); - sprintf (num, "%i", bottom); - Cvar_Set (&bottomcolor, num); -} - - -/* -================== -CL_FullInfo_f - -usage: -fullinfo \name\unnamed\topcolor\0\bottomcolor\1, etc -================== -Casey was here :) -*/ -void CL_FullInfo_f (void) -{ - char key[512]; - char value[512]; - char *o; - char *s; - - if (Cmd_Argc() != 2) - { - Con_Printf ("fullinfo \n"); - return; - } - - s = Cmd_Argv(1); - if (*s == '\\') - s++; - while (*s) - { - o = key; - while (*s && *s != '\\') - *o++ = *s++; - *o = 0; - - if (!*s) - { - Con_Printf ("MISSING VALUE\n"); - return; - } - - o = value; - s++; - while (*s && *s != '\\') - *o++ = *s++; - *o = 0; - - if (*s) - s++; - - if (!stricmp(key, pmodel_name) || !stricmp(key, emodel_name)) - continue; - - Info_SetValueForKey (cls.userinfo, key, value, MAX_INFO_STRING); - } -} - -/* -================== -CL_SetInfo_f - -Allow clients to change userinfo -================== -*/ -void CL_SetInfo_f (void) -{ - if (Cmd_Argc() == 1) - { - Info_Print (cls.userinfo); - return; - } - if (Cmd_Argc() != 3) - { - Con_Printf ("usage: setinfo [ ]\n"); - return; - } - if (!stricmp(Cmd_Argv(1), pmodel_name) || !strcmp(Cmd_Argv(1), emodel_name)) - return; - - Info_SetValueForKey (cls.userinfo, Cmd_Argv(1), Cmd_Argv(2), MAX_INFO_STRING); - if (cls.state >= ca_connected) - Cmd_ForwardToServer (); -} - - -/* -================== -CL_Quit_f -================== -*/ -void CL_Quit_f (void) -{ - M_Menu_Quit_f (); - return; -#if 0 - CL_Disconnect (); - Sys_Quit (); -#endif -} - - -/* -=============== -CL_Windows_f -=============== -*/ -#ifdef _WINDOWS -void CL_Windows_f (void) -{ - SendMessage(mainwindow, WM_SYSKEYUP, VK_TAB, 1 | (0x0F << 16) | (1<<29)); -} -#endif - - -/* -=============== -CL_Serverinfo_f -=============== -*/ -void CL_Serverinfo_f (void) -{ - -// Tonik: no need to request serverinfo from server, because we -// already have it cached in cl.serverinfo -// this also lets us get serverinfo when playing a demo - //Cmd_ForwardToServer(); - if (cls.state >= ca_onserver && cl.serverinfo) - Info_Print (cl.serverinfo); - else - // so that it says we are not connected :) - Cmd_ForwardToServer(); -} - - -/* -=============== -CL_WriteConfig_f - -Writes key bindings and archived cvars to a custom config file -=============== -*/ -void CL_WriteConfig_f (void) -{ - FILE *f; - char name[MAX_QPATH]; - - if (Cmd_Argc() != 2) { - Con_Printf ("usage: writeconfig \n"); - return; - } - - Q_strncpyz (name, Cmd_Argv(1), sizeof(name)); - COM_ForceExtension (name, ".cfg"); - - Con_Printf ("Writing %s\n", name); - - f = fopen (va("%s/%s", com_gamedir, name), "w"); - if (!f) { - Con_Printf ("Couldn't write %s.\n", name); - return; - } - - Key_WriteBindings (f); - Cvar_WriteVariables (f); - - fclose (f); -} - -void CL_DemoJump_f (void) -{ - if (Cmd_Argc() != 2) { - Sys_Printf("usage: demojump \n"); - return; - } - - if (atoi(Cmd_Argv(1)) <= 0) - return; - - realtime += atoi(Cmd_Argv(1)); -} - - -void Host_Savegame_f(void); -void Host_Loadgame_f(void); - -void CL_InitCommands (void) -{ -// general commands - Cmd_AddCommand ("cmd", CL_ForwardToServer_f); - Cmd_AddCommand ("download", CL_Download_f); - Cmd_AddCommand ("packet", CL_Packet_f); - Cmd_AddCommand ("pause", CL_Pause_f); - Cmd_AddCommand ("quit", CL_Quit_f); - Cmd_AddCommand ("rcon", CL_Rcon_f); - Cmd_AddCommand ("say", CL_Say_f); - Cmd_AddCommand ("say_team", CL_Say_f); - Cmd_AddCommand ("serverinfo", CL_Serverinfo_f); - Cmd_AddCommand ("skins", Skin_Skins_f); - Cmd_AddCommand ("allskins", Skin_AllSkins_f); - Cmd_AddCommand ("user", CL_User_f); - Cmd_AddCommand ("users", CL_Users_f); - Cmd_AddCommand ("version", CL_Version_f); - Cmd_AddCommand ("writeconfig", CL_WriteConfig_f); - -// client info setting - Cmd_AddCommand ("color", CL_Color_f); - Cmd_AddCommand ("fullinfo", CL_FullInfo_f); - Cmd_AddCommand ("setinfo", CL_SetInfo_f); - -#if 0 - Cmd_AddCommand ("save", Host_Savegame_f); - Cmd_AddCommand ("load", Host_Loadgame_f); -#endif - -// demo recording & playback - Cmd_AddCommand ("record", CL_Record_f); - Cmd_AddCommand ("easyrecord", CL_EasyRecord_f); - Cmd_AddCommand ("rerecord", CL_ReRecord_f); - Cmd_AddCommand ("stop", CL_Stop_f); - Cmd_AddCommand ("playdemo", CL_PlayDemo_f); - Cmd_AddCommand ("timedemo", CL_TimeDemo_f); - Cmd_AddCommand ("demojump", CL_DemoJump_f); - -// -// forward to server commands -// - Cmd_AddCommand ("kill", NULL); - -// -// Windows commands -// -#ifdef _WINDOWS - Cmd_AddCommand ("windows", CL_Windows_f); -#endif -} - - -/* -============================================================================== - -SERVER COMMANDS - -Server commands are commands stuffed by server into client's cbuf -We use a separate command buffer for them -- there are several -reasons for that: -1. So that partially stuffed commands are always executed properly -2. Not to let players cheat in TF (v_cshift etc don't work in console) -3. To hide some commands the user doesn't need to know about, like -changing, fullserverinfo, nextul, stopul -============================================================================== -*/ - -/* -================= -CL_Changing_f - -Just sent as a hint to the client that they should -drop to full console -================= -*/ -void CL_Changing_f (void) -{ - if (cls.download) // don't change when downloading - return; - - S_StopAllSounds (true); - cl.intermission = 0; - cls.state = ca_connected; // not active anymore, but not disconnected - - Con_Printf ("\nChanging map...\n"); -} - - -/* -================== -CL_FullServerinfo_f - -Sent by server when serverinfo changes -================== -*/ -void CL_FullServerinfo_f (void) -{ - char *p; - float v; - - if (Cmd_Argc() != 2) - { - //Con_Printf ("usage: fullserverinfo \n"); - return; - } - - strcpy (cl.serverinfo, Cmd_Argv(1)); - - - server_version = 0; - - if ((p = Info_ValueForKey(cl.serverinfo, "*qwex_version")) && *p) { - v = Q_atof(p); - if (v) { - Con_Printf("QWExtended Server Version %s\n", p); - server_version = 2.40; - } - } - if ((p = Info_ValueForKey(cl.serverinfo, "*version")) && *p) { - v = Q_atof(p); - if (v) { - if (!server_version) - Con_Printf("Version %1.2f Server\n", v); - server_version = v; - } - } - - - CL_ProcessServerInfo (); -} - - -void CL_Fov_f (void) -{ - extern cvar_t scr_fov, default_fov; - - if (Cmd_Argc() == 1) - { - Con_Printf ("\"fov\" is \"%s\"\n", scr_fov.string); - return; - } - - if (Q_atof(Cmd_Argv(1)) == 90.0 && default_fov.value) - Cvar_SetValue (&scr_fov, default_fov.value); - else - Cvar_Set (&scr_fov, Cmd_Argv(1)); -} - - -void CL_R_DrawViewModel_f (void) -{ - extern cvar_t cl_filterdrawviewmodel; - - if (cl_filterdrawviewmodel.value) - return; - Cvar_Command (); -} - - -typedef struct { - char *name; - void (*func) (void); -} svcmd_t; - -svcmd_t svcmds[] = -{ - {"changing", CL_Changing_f}, - {"fullserverinfo", CL_FullServerinfo_f}, - {"nextul", CL_NextUpload}, - {"stopul", CL_StopUpload}, - {"fov", CL_Fov_f}, - {"r_drawviewmodel", CL_R_DrawViewModel_f}, - {NULL, NULL} -}; - -/* -================ -CL_CheckServerCommand - -Called by Cmd_ExecuteString if cbuf_current==&cbuf_svc -================ -*/ -qboolean CL_CheckServerCommand () -{ - svcmd_t *cmd; - char *s; - - s = Cmd_Argv (0); - for (cmd=svcmds ; cmd->name ; cmd++) - if (!strcmp (s, cmd->name) ) { - cmd->func (); - return true; - } - - return false; -} +/* +Copyright (C) 1996-1997 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 "quakedef.h" +#include "winquake.h" +#include "teamplay.h" +#include "version.h" + +void SCR_RSShot_f (void); +void CL_ProcessServerInfo (void); +void SV_Serverinfo_f (void); +void Key_WriteBindings (FILE *f); +void S_StopAllSounds (qboolean clear); + +/* +=================== +Cmd_ForwardToServer + +adds the current command line as a clc_stringcmd to the client message. +things like kill, say, etc, are commands directed to the server, +so when they are typed in at the console, they will need to be forwarded. +=================== +*/ +void Cmd_ForwardToServer (void) +{ + char *s; + + if (cls.state == ca_disconnected) + { + Con_Printf ("Can't \"%s\", not connected\n", Cmd_Argv(0)); + return; + } + + MSG_WriteByte (&cls.netchan.message, clc_stringcmd); + // lowercase command + for (s=Cmd_Argv(0) ; *s ; s++) + *s = (char)tolower(*s); + SZ_Print (&cls.netchan.message, Cmd_Argv(0)); + if (Cmd_Argc() > 1) + { + SZ_Print (&cls.netchan.message, " "); + SZ_Print (&cls.netchan.message, Cmd_Args()); + } +} + +// don't forward the first argument +void CL_ForwardToServer_f (void) +{ + if (cls.state == ca_disconnected) + { + Con_Printf ("Can't \"%s\", not connected\n", Cmd_Argv(0)); + return; + } + + if (Q_strcasecmp(Cmd_Argv(1), "snap") == 0) { + SCR_RSShot_f (); + return; + } + + if (cls.demoplayback) + return; // not really connected + + if (Cmd_Argc() > 1) + { + MSG_WriteByte (&cls.netchan.message, clc_stringcmd); + SZ_Print (&cls.netchan.message, Cmd_Args()); + } +} + +/* +=============== +CL_Say_f + +Handles both say and say_team +=============== +*/ +void CL_Say_f (void) +{ + char *s; + + if (cls.state == ca_disconnected) + { + Con_Printf ("Can't \"%s\", not connected\n", Cmd_Argv(0)); + return; + } + + MSG_WriteByte (&cls.netchan.message, clc_stringcmd); + // lowercase command + for (s=Cmd_Argv(0) ; *s ; s++) + *s = (char)tolower(*s); + SZ_Print (&cls.netchan.message, Cmd_Argv(0)); + if (Cmd_Argc() > 1) + { + SZ_Print (&cls.netchan.message, " "); + + s = TP_ParseMacroString(Cmd_Args()); + if (*s && *s < 32 && *s != 10) + { + SZ_Print (&cls.netchan.message, "\""); + SZ_Print (&cls.netchan.message, s); + SZ_Print (&cls.netchan.message, "\""); + } + else + SZ_Print (&cls.netchan.message, s); + } +} + + +/* +===================== +CL_Pause_f +===================== +*/ +void CL_Pause_f (void) +{ + if (cls.demoplayback) + cl.paused ^= 2; + else + Cmd_ForwardToServer(); +} + + +/* +==================== +CL_Packet_f + +packet + +Contents allows \n escape character +==================== +*/ +void CL_Packet_f (void) +{ + char send[2048]; + int i, l; + char *in, *out; + netadr_t adr; + + if (Cmd_Argc() != 3) + { + Con_Printf ("packet \n"); + return; + } + + if (!NET_StringToAdr (Cmd_Argv(1), &adr)) + { + Con_Printf ("Bad address\n"); + return; + } + + if (adr.port == 0) + adr.port = BigShort (27500); + + in = Cmd_Argv(2); + out = send+4; + send[0] = send[1] = send[2] = send[3] = 0xff; + + l = strlen (in); + for (i=0 ; i= ca_connected) + to = cls.netchan.remote_address; + else + { + if (!strlen(rcon_address.string)) + { + Con_Printf ("You must either be connected,\n" + "or set the 'rcon_address' cvar\n" + "to issue rcon commands\n"); + + return; + } + NET_StringToAdr (rcon_address.string, &to); + if (to.port == 0) + to.port = BigShort (27500); + } + + NET_SendPacket (net_clientsocket, strlen(message)+1, message + , to); +} + + +/* +===================== +CL_Download_f +===================== +*/ +void CL_Download_f (void) +{ + char *p, *q; + + if (cls.state == ca_disconnected) + { + Con_Printf ("Must be connected.\n"); + return; + } + + if (Cmd_Argc() != 2) + { + Con_Printf ("Usage: download \n"); + return; + } + + sprintf (cls.downloadname, "%s/%s", com_gamedir, Cmd_Argv(1)); + + p = cls.downloadname; + for (;;) { + if ((q = strchr(p, '/')) != NULL) { + *q = 0; + Sys_mkdir(cls.downloadname); + *q = '/'; + p = q + 1; + } else + break; + } + + if (cls.download) + { + fclose(cls.download); + cls.download = NULL; + } + + + strcpy(cls.downloadtempname, cls.downloadname); + cls.download = fopen (cls.downloadname, "wb"); + cls.downloadtype = dl_single; + + MSG_WriteByte (&cls.netchan.message, clc_stringcmd); + SZ_Print (&cls.netchan.message, va("download %s\n",Cmd_Argv(1))); +} + + +/* +==================== +CL_User_f + +user + +Dump userdata / masterdata for a user +==================== +*/ +void CL_User_f (void) +{ + int uid; + int i; + + if (Cmd_Argc() != 2) + { + Con_Printf ("Usage: user \n"); + return; + } + + uid = atoi(Cmd_Argv(1)); + + for (i=0 ; i [0-13]\n"); + return; + } + + if (Cmd_Argc() == 2) + top = bottom = atoi(Cmd_Argv(1)); + else + { + top = atoi(Cmd_Argv(1)); + bottom = atoi(Cmd_Argv(2)); + } + + top &= 15; + if (top > 13) + top = 13; + bottom &= 15; + if (bottom > 13) + bottom = 13; + + sprintf (num, "%i", top); + Cvar_Set (&topcolor, num); + sprintf (num, "%i", bottom); + Cvar_Set (&bottomcolor, num); +} + + +/* +================== +CL_FullInfo_f + +usage: +fullinfo \name\unnamed\topcolor\0\bottomcolor\1, etc +================== +Casey was here :) +*/ +void CL_FullInfo_f (void) +{ + char key[512]; + char value[512]; + char *o; + char *s; + + if (Cmd_Argc() != 2) + { + Con_Printf ("fullinfo \n"); + return; + } + + s = Cmd_Argv(1); + if (*s == '\\') + s++; + while (*s) + { + o = key; + while (*s && *s != '\\') + *o++ = *s++; + *o = 0; + + if (!*s) + { + Con_Printf ("MISSING VALUE\n"); + return; + } + + o = value; + s++; + while (*s && *s != '\\') + *o++ = *s++; + *o = 0; + + if (*s) + s++; + + if (!stricmp(key, pmodel_name) || !stricmp(key, emodel_name)) + continue; + + Info_SetValueForKey (cls.userinfo, key, value, MAX_INFO_STRING); + } +} + +/* +================== +CL_SetInfo_f + +Allow clients to change userinfo +================== +*/ +void CL_SetInfo_f (void) +{ + if (Cmd_Argc() == 1) + { + Info_Print (cls.userinfo); + return; + } + if (Cmd_Argc() != 3) + { + Con_Printf ("usage: setinfo [ ]\n"); + return; + } + if (!stricmp(Cmd_Argv(1), pmodel_name) || !strcmp(Cmd_Argv(1), emodel_name)) + return; + + Info_SetValueForKey (cls.userinfo, Cmd_Argv(1), Cmd_Argv(2), MAX_INFO_STRING); + if (cls.state >= ca_connected) + Cmd_ForwardToServer (); +} + + +/* +================== +CL_Quit_f +================== +*/ +void CL_Quit_f (void) +{ + M_Menu_Quit_f (); + return; +#if 0 + CL_Disconnect (); + Sys_Quit (); +#endif +} + + +/* +=============== +CL_Windows_f +=============== +*/ +#ifdef _WINDOWS +void CL_Windows_f (void) +{ + SendMessage(mainwindow, WM_SYSKEYUP, VK_TAB, 1 | (0x0F << 16) | (1<<29)); +} +#endif + + +/* +=============== +CL_Serverinfo_f +=============== +*/ +void CL_Serverinfo_f (void) +{ + +// Tonik: no need to request serverinfo from server, because we +// already have it cached in cl.serverinfo +// this also lets us get serverinfo when playing a demo + //Cmd_ForwardToServer(); + if (cls.state >= ca_onserver && cl.serverinfo) + Info_Print (cl.serverinfo); + else + // so that it says we are not connected :) + Cmd_ForwardToServer(); +} + + +/* +=============== +CL_WriteConfig_f + +Writes key bindings and archived cvars to a custom config file +=============== +*/ +void CL_WriteConfig_f (void) +{ + FILE *f; + char name[MAX_QPATH]; + + if (Cmd_Argc() != 2) { + Con_Printf ("usage: writeconfig \n"); + return; + } + + Q_strncpyz (name, Cmd_Argv(1), sizeof(name)); + COM_ForceExtension (name, ".cfg"); + + Con_Printf ("Writing %s\n", name); + + f = fopen (va("%s/%s", com_gamedir, name), "w"); + if (!f) { + Con_Printf ("Couldn't write %s.\n", name); + return; + } + + Key_WriteBindings (f); + Cvar_WriteVariables (f); + + fclose (f); +} + +void CL_DemoJump_f (void) +{ + if (Cmd_Argc() != 2) { + Sys_Printf("usage: demojump \n"); + return; + } + + if (atoi(Cmd_Argv(1)) <= 0) + return; + + realtime += atoi(Cmd_Argv(1)); +} + + +void Host_Savegame_f(void); +void Host_Loadgame_f(void); + +double ping_time; +void Cmd_Ping_f (void) +{ + char data[6]; + netadr_t adr; + + if (Cmd_Argc() != 2) + { + Con_Printf ("ping \n"); + return; + } + + if (!NET_StringToAdr (Cmd_Argv(1), &adr)) + { + Con_Printf ("Bad address\n"); + return; + } + + if (adr.port == 0) + adr.port = BigShort (27500); + + + data[0] = 0xff; + data[1] = 0xff; + data[2] = 0xff; + data[3] = 0xff; + data[4] = A2A_PING; + data[5] = 0; + + NET_SendPacket (net_clientsocket, 6, &data, adr); + ping_time = realtime; +} + + +void CL_InitCommands (void) +{ +// general commands + Cmd_AddCommand ("cmd", CL_ForwardToServer_f); + Cmd_AddCommand ("download", CL_Download_f); + Cmd_AddCommand ("packet", CL_Packet_f); + Cmd_AddCommand ("pause", CL_Pause_f); + Cmd_AddCommand ("quit", CL_Quit_f); + Cmd_AddCommand ("rcon", CL_Rcon_f); + Cmd_AddCommand ("say", CL_Say_f); + Cmd_AddCommand ("say_team", CL_Say_f); + Cmd_AddCommand ("serverinfo", CL_Serverinfo_f); + Cmd_AddCommand ("skins", Skin_Skins_f); + Cmd_AddCommand ("allskins", Skin_AllSkins_f); + Cmd_AddCommand ("user", CL_User_f); + Cmd_AddCommand ("users", CL_Users_f); + Cmd_AddCommand ("version", CL_Version_f); + Cmd_AddCommand ("writeconfig", CL_WriteConfig_f); + + Cmd_AddCommand ("ping", Cmd_Ping_f); + +// client info setting + Cmd_AddCommand ("color", CL_Color_f); + Cmd_AddCommand ("fullinfo", CL_FullInfo_f); + Cmd_AddCommand ("setinfo", CL_SetInfo_f); + +#if 0 + Cmd_AddCommand ("save", Host_Savegame_f); + Cmd_AddCommand ("load", Host_Loadgame_f); +#endif + +// demo recording & playback + Cmd_AddCommand ("record", CL_Record_f); + Cmd_AddCommand ("easyrecord", CL_EasyRecord_f); + Cmd_AddCommand ("rerecord", CL_ReRecord_f); + Cmd_AddCommand ("stop", CL_Stop_f); + Cmd_AddCommand ("playdemo", CL_PlayDemo_f); + Cmd_AddCommand ("timedemo", CL_TimeDemo_f); + Cmd_AddCommand ("demojump", CL_DemoJump_f); + +// +// forward to server commands +// + Cmd_AddCommand ("kill", NULL); + +// +// Windows commands +// +#ifdef _WINDOWS + Cmd_AddCommand ("windows", CL_Windows_f); +#endif +} + + +/* +============================================================================== + +SERVER COMMANDS + +Server commands are commands stuffed by server into client's cbuf +We use a separate command buffer for them -- there are several +reasons for that: +1. So that partially stuffed commands are always executed properly +2. Not to let players cheat in TF (v_cshift etc don't work in console) +3. To hide some commands the user doesn't need to know about, like +changing, fullserverinfo, nextul, stopul +============================================================================== +*/ + +/* +================= +CL_Changing_f + +Just sent as a hint to the client that they should +drop to full console +================= +*/ +void CL_Changing_f (void) +{ + if (cls.download) // don't change when downloading + return; + + S_StopAllSounds (true); + cl.intermission = 0; + cls.state = ca_connected; // not active anymore, but not disconnected + + Con_Printf ("\nChanging map...\n"); +} + + +/* +================== +CL_FullServerinfo_f + +Sent by server when serverinfo changes +================== +*/ +void CL_FullServerinfo_f (void) +{ + char *p; + float v; + + if (Cmd_Argc() != 2) + { + //Con_Printf ("usage: fullserverinfo \n"); + return; + } + + strcpy (cl.serverinfo, Cmd_Argv(1)); + + + server_version = 0; + + if ((p = Info_ValueForKey(cl.serverinfo, "*qwex_version")) && *p) { + v = Q_atof(p); + if (v) { + Con_Printf("QWExtended Server Version %s\n", p); + server_version = 2.40; + } + } + if ((p = Info_ValueForKey(cl.serverinfo, "*version")) && *p) { + v = Q_atof(p); + if (v) { + if (!server_version) + Con_Printf("Version %1.2f Server\n", v); + server_version = v; + } + } + + + CL_ProcessServerInfo (); +} + + +void CL_Fov_f (void) +{ + extern cvar_t scr_fov, default_fov; + + if (Cmd_Argc() == 1) + { + Con_Printf ("\"fov\" is \"%s\"\n", scr_fov.string); + return; + } + + if (Q_atof(Cmd_Argv(1)) == 90.0 && default_fov.value) + Cvar_SetValue (&scr_fov, default_fov.value); + else + Cvar_Set (&scr_fov, Cmd_Argv(1)); +} + + +void CL_R_DrawViewModel_f (void) +{ + extern cvar_t cl_filterdrawviewmodel; + + if (cl_filterdrawviewmodel.value) + return; + Cvar_Command (); +} + + +typedef struct { + char *name; + void (*func) (void); +} svcmd_t; + +svcmd_t svcmds[] = +{ + {"changing", CL_Changing_f}, + {"fullserverinfo", CL_FullServerinfo_f}, + {"nextul", CL_NextUpload}, + {"stopul", CL_StopUpload}, + {"fov", CL_Fov_f}, + {"r_drawviewmodel", CL_R_DrawViewModel_f}, + {NULL, NULL} +}; + +/* +================ +CL_CheckServerCommand + +Called by Cmd_ExecuteString if cbuf_current==&cbuf_svc +================ +*/ +qboolean CL_CheckServerCommand () +{ + svcmd_t *cmd; + char *s; + + s = Cmd_Argv (0); + for (cmd=svcmds ; cmd->name ; cmd++) + if (!strcmp (s, cmd->name) ) { + cmd->func (); + return true; + } + + return false; +} + diff --git a/source/cl_demo.c b/source/cl_demo.c index ff2b2c38..b361b39b 100644 --- a/source/cl_demo.c +++ b/source/cl_demo.c @@ -1,1250 +1,1247 @@ -/* -Copyright (C) 1996-1997 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 included (GNU.txt) 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 "quakedef.h" -#include "winquake.h" -#include "pmove.h" -#include "teamplay.h" - -void CL_FinishTimeDemo (void); - -// .qwz playback -static qboolean qwz_playback = false; -static qboolean qwz_unpacking = false; -#ifdef _WIN32 -static HANDLE hQizmoProcess = NULL; -static char tempqwd_name[256] = ""; // this file must be deleted - // after playback is finished -void CheckQizmoCompletion (); -void StopQWZPlayback (); -#endif - -/* -============================================================================== - -DEMO CODE - -When a demo is playing back, all NET_SendMessages are skipped, and -NET_GetMessages are read from the demo file. - -Whenever cl.time gets past the last received message, another message is -read from the demo file. -============================================================================== -*/ - -/* -============== -CL_StopPlayback - -Called when a demo file runs out, or the user starts a game -============== -*/ -void CL_StopPlayback (void) -{ - if (!cls.demoplayback) - return; - - if (cls.demofile) - fclose (cls.demofile); - cls.demofile = NULL; - cls.state = ca_disconnected; - cls.demoplayback = 0; - cls.demoplayback2 = 0; -#ifdef _WIN32 - if (qwz_playback) - StopQWZPlayback (); -#endif - - if (cls.timedemo) - CL_FinishTimeDemo (); - - cl.teamfortress = false; -} - -#define dem_cmd 0 -#define dem_read 1 -#define dem_set 2 - -/* -==================== -CL_WriteDemoCmd - -Writes the current user cmd -==================== -*/ -void CL_WriteDemoCmd (usercmd_t *pcmd) -{ - int i; - float fl; - byte c; - usercmd_t cmd; - -//Con_Printf("write: %ld bytes, %4.4f\n", msg->cursize, realtime); - - fl = LittleFloat((float)realtime); - fwrite (&fl, sizeof(fl), 1, cls.recordfile); - - c = dem_cmd; - fwrite (&c, sizeof(c), 1, cls.recordfile); - - // correct for byte order, bytes don't matter - cmd = *pcmd; - - for (i = 0; i < 3; i++) - cmd.angles[i] = LittleFloat(cmd.angles[i]); - cmd.forwardmove = LittleShort(cmd.forwardmove); - cmd.sidemove = LittleShort(cmd.sidemove); - cmd.upmove = LittleShort(cmd.upmove); - - fwrite(&cmd, sizeof(cmd), 1, cls.recordfile); - - for (i=0 ; i<3 ; i++) - { - fl = LittleFloat (cl.viewangles[i]); - fwrite (&fl, 4, 1, cls.recordfile); - } - - fflush (cls.recordfile); -} - -/* -==================== -CL_WriteDemoMessage - -Dumps the current net message, prefixed by the length and view angles -==================== -*/ -void CL_WriteDemoMessage (sizebuf_t *msg) -{ - int len; - float fl; - byte c; - -//Con_Printf("write: %ld bytes, %4.4f\n", msg->cursize, realtime); - - if (!cls.demorecording) - return; - - fl = LittleFloat((float)realtime); - fwrite (&fl, sizeof(fl), 1, cls.recordfile); - - c = dem_read; - fwrite (&c, sizeof(c), 1, cls.recordfile); - - len = LittleLong (msg->cursize); - fwrite (&len, 4, 1, cls.recordfile); - fwrite (msg->data, msg->cursize, 1, cls.recordfile); - - fflush (cls.recordfile); -} - -/* -==================== -CL_GetDemoMessage - - FIXME... -==================== -*/ -float olddemotime, nextdemotime; - -qboolean CL_GetDemoMessage (void) -{ - int r, i, j, size, tracknum; - float f, demotime; - byte newtime; - byte c; - usercmd_t *pcmd; - static float prevtime = 0.0; - - if (qwz_unpacking) - return 0; - - if (cl.paused & 2) - return 0; - - //if (realtime < nextdemotime) - // return 0; - - if (prevtime < nextdemotime) - prevtime = nextdemotime; - - if (!cls.demoplayback2) - nextdemotime = (float) realtime; - - if (realtime + 1.0 < nextdemotime) - { - realtime = nextdemotime - 1.0; - } - -nextdemomessage: - - size = 0; - // read the time from the packet - newtime = 0; - if (cls.demoplayback2) - { - fread(&newtime, sizeof(newtime), 1, cls.demofile); - demotime = prevtime + newtime*0.001; - } else { - fread(&demotime, sizeof(demotime), 1, cls.demofile); - demotime = LittleFloat(demotime); - if (!nextdemotime) - realtime = nextdemotime = demotime; - } - - if (realtime - nextdemotime > 0.0001) - { - if (nextdemotime != demotime) - { - olddemotime = nextdemotime; - if (cls.demoplayback2) { - cls.netchan.incoming_sequence++; - cls.netchan.incoming_acknowledged++; - cls.netchan.frame_latency = 0; - cls.netchan.last_received = realtime; // just to happy timeout check - //Con_Printf("%d\n", cls.netchan.incoming_sequence); - } - } - - nextdemotime = demotime; - } - -// decide if it is time to grab the next message - if (cls.timedemo) { - if (cls.td_lastframe < 0) - cls.td_lastframe = demotime; - else if (demotime > cls.td_lastframe) { - cls.td_lastframe = demotime; - // rewind back to time - if (cls.demoplayback2) - { - fseek(cls.demofile, ftell(cls.demofile) - sizeof(newtime), - SEEK_SET); - } else - fseek(cls.demofile, ftell(cls.demofile) - sizeof(demotime), - SEEK_SET); - return 0; // already read this frame's message - } - if (!cls.td_starttime && cls.state == ca_active) { - cls.td_starttime = Sys_DoubleTime(); - cls.td_startframe = host_framecount; - } - realtime = demotime; // warp - } else if (!(cl.paused & 1) && cls.state >= ca_onserver) { // always grab until fully connected - if (!cls.demoplayback2 && realtime + 1.0 < demotime) { - // too far back - realtime = demotime - 1.0; - // rewind back to time - fseek(cls.demofile, ftell(cls.demofile) - sizeof(demotime), - SEEK_SET); - return 0; - } else if (nextdemotime < demotime) { - // rewind back to time - if (cls.demoplayback2) - { - fseek(cls.demofile, ftell(cls.demofile) - sizeof(newtime), - SEEK_SET); - } else - fseek(cls.demofile, ftell(cls.demofile) - sizeof(demotime), - SEEK_SET); - return 0; // don't need another message yet - } - } else - realtime = demotime; // we're warping - - prevtime = demotime; - if (cls.state < ca_demostart) - Host_Error ("CL_GetDemoMessage: cls.state != ca_active"); - - // get the msg type - fread (&c, sizeof(c), 1, cls.demofile); - - switch (c&7) { - case dem_cmd : - // user sent input - i = cls.netchan.outgoing_sequence & UPDATE_MASK; - pcmd = &cl.frames[i].cmd; - r = fread (pcmd, sizeof(*pcmd), 1, cls.demofile); - if (r != 1) - { - CL_StopPlayback (); - return 0; - } - // byte order stuff - for (j = 0; j < 3; j++) - pcmd->angles[j] = LittleFloat(pcmd->angles[j]); - pcmd->forwardmove = LittleShort(pcmd->forwardmove); - pcmd->sidemove = LittleShort(pcmd->sidemove); - pcmd->upmove = LittleShort(pcmd->upmove); - cl.frames[i].senttime = demotime; - cl.frames[i].receivedtime = -1; // we haven't gotten a reply yet - //cl.frames[i].playerstate[cl.playernum].command = *pcmd; - cls.netchan.outgoing_sequence++; - for (j=0 ; j<3 ; j++) - { - r = fread (&f, 4, 1, cls.demofile); - cl.viewangles[j] = LittleFloat (f); - - } - break; - - case dem_read: -readit: - // get the next message - fread (&net_message.cursize, 4, 1, cls.demofile); - net_message.cursize = LittleLong (net_message.cursize); - - if (net_message.cursize > net_message.maxsize) - Host_EndGame ("Demo message > MAX_UDP_PACKET"); - r = fread (net_message.data, net_message.cursize, 1, cls.demofile); - if (r != 1) - { - CL_StopPlayback (); - return 0; - } - - if (cls.demoplayback2) - { - tracknum = Cam_TrackNum(); - - if (cls.lasttype == dem_multiple) { - if (tracknum == -1) - goto nextdemomessage; - - if (!(cls.lastto & (1 << (tracknum)))) - goto nextdemomessage; - } else if (cls.lasttype == dem_single) - { - if (tracknum == -1 || cls.lastto != spec_track) - goto nextdemomessage; - } - } - break; - - case dem_set : - fread (&i, 4, 1, cls.demofile); - cls.netchan.outgoing_sequence = LittleLong(i); - fread (&i, 4, 1, cls.demofile); - cls.netchan.incoming_sequence = LittleLong(i); - if (cls.demoplayback2) { - cls.netchan.incoming_acknowledged = cls.netchan.incoming_sequence; - goto nextdemomessage; - } - break; - - case dem_multiple: - r = fread (&i, 4, 1, cls.demofile); - if (r != 1) - { - CL_StopPlayback (); - return 0; - } - - cls.lastto = LittleLong(i); - cls.lasttype = dem_multiple; - goto readit; - - case dem_single: - cls.lastto = c>>3; - cls.lasttype = dem_single; - goto readit; - case dem_stats: - cls.lastto = c>>3; - cls.lasttype = dem_stats; - goto readit; - case dem_all: - cls.lastto = 0; - cls.lasttype = dem_all; - goto readit; - default : - Con_Printf("Corrupted demo.\n"); - CL_StopPlayback (); - return 0; - } - - return 1; -} - -/* -==================== -CL_GetMessage - -Handles recording and playback of demos, on top of NET_ code -==================== -*/ -qboolean CL_GetMessage (void) -{ -#ifdef _WIN32 - CheckQizmoCompletion (); -#endif - - if (cls.demoplayback) - return CL_GetDemoMessage (); - - if (!NET_GetPacket(net_clientsocket)) - return false; - - CL_WriteDemoMessage (&net_message); - - return true; -} - - -/* -==================== -CL_Stop_f - -stop recording a demo -==================== -*/ -void CL_Stop_f (void) -{ - if (!cls.demorecording) - { - Con_Printf ("Not recording a demo.\n"); - return; - } - -// write a disconnect message to the demo file - SZ_Clear (&net_message); - MSG_WriteLong (&net_message, -1); // -1 sequence means out of band - MSG_WriteByte (&net_message, svc_disconnect); - MSG_WriteString (&net_message, "EndOfDemo"); - CL_WriteDemoMessage (&net_message); - -// finish up - fclose (cls.recordfile); - cls.recordfile = NULL; - cls.demorecording = false; - Con_Printf ("Completed demo\n"); -} - - -/* -==================== -CL_WriteDemoMessage - -Dumps the current net message, prefixed by the length and view angles -==================== -*/ -void CL_WriteRecordDemoMessage (sizebuf_t *msg, int seq) -{ - int len; - int i; - float fl; - byte c; - -//Con_Printf("write: %ld bytes, %4.4f\n", msg->cursize, realtime); - - if (!cls.demorecording) - return; - - fl = LittleFloat((float)realtime); - fwrite (&fl, sizeof(fl), 1, cls.recordfile); - - c = dem_read; - fwrite (&c, sizeof(c), 1, cls.recordfile); - - len = LittleLong (msg->cursize + 8); - fwrite (&len, 4, 1, cls.recordfile); - - i = LittleLong(seq); - fwrite (&i, 4, 1, cls.recordfile); - fwrite (&i, 4, 1, cls.recordfile); - - fwrite (msg->data, msg->cursize, 1, cls.recordfile); - - fflush (cls.recordfile); -} - - -void CL_WriteSetDemoMessage (void) -{ - int len; - float fl; - byte c; - -//Con_Printf("write: %ld bytes, %4.4f\n", msg->cursize, realtime); - - if (!cls.demorecording) - return; - - fl = LittleFloat((float)realtime); - fwrite (&fl, sizeof(fl), 1, cls.recordfile); - - c = dem_set; - fwrite (&c, sizeof(c), 1, cls.recordfile); - - len = LittleLong(cls.netchan.outgoing_sequence); - fwrite (&len, 4, 1, cls.recordfile); - len = LittleLong(cls.netchan.incoming_sequence); - fwrite (&len, 4, 1, cls.recordfile); - - fflush (cls.recordfile); -} - - - - -static void CL_Record (void) -{ - sizebuf_t buf; - char buf_data[MAX_MSGLEN]; - int n, i, j; - char *s; - entity_t *ent; - entity_state_t *es, blankes; - player_info_t *player; - extern char gamedirfile[]; - int seq = 1; - - cls.demorecording = true; - - noscale_realtime = realtime; - -/*-------------------------------------------------*/ - -// serverdata - // send the info about the new client to all connected clients - memset(&buf, 0, sizeof(buf)); - buf.data = buf_data; - buf.maxsize = sizeof(buf_data); - -// send the serverdata - MSG_WriteByte (&buf, svc_serverdata); - MSG_WriteLong (&buf, PROTOCOL_VERSION); - MSG_WriteLong (&buf, cl.servercount); - MSG_WriteString (&buf, gamedirfile); - - if (cl.spectator) - MSG_WriteByte (&buf, cl.playernum | 128); - else - MSG_WriteByte (&buf, cl.playernum); - - // send full levelname - MSG_WriteString (&buf, cl.levelname); - - // send the movevars - MSG_WriteFloat(&buf, movevars.gravity); - MSG_WriteFloat(&buf, movevars.stopspeed); - MSG_WriteFloat(&buf, movevars.maxspeed); - MSG_WriteFloat(&buf, movevars.spectatormaxspeed); - MSG_WriteFloat(&buf, movevars.accelerate); - MSG_WriteFloat(&buf, movevars.airaccelerate); - MSG_WriteFloat(&buf, movevars.wateraccelerate); - MSG_WriteFloat(&buf, movevars.friction); - MSG_WriteFloat(&buf, movevars.waterfriction); - MSG_WriteFloat(&buf, movevars.entgravity); - - // send music - MSG_WriteByte (&buf, svc_cdtrack); - MSG_WriteByte (&buf, 0); // none in demos - - // send server info string - MSG_WriteByte (&buf, svc_stufftext); - MSG_WriteString (&buf, va("fullserverinfo \"%s\"\n", cl.serverinfo) ); - - // flush packet - CL_WriteRecordDemoMessage (&buf, seq++); - SZ_Clear (&buf); - -// soundlist - MSG_WriteByte (&buf, svc_soundlist); - MSG_WriteByte (&buf, 0); - - n = 0; - s = cl.sound_name[n+1]; - while (*s) { - MSG_WriteString (&buf, s); - if (buf.cursize > MAX_MSGLEN/2) { - MSG_WriteByte (&buf, 0); - MSG_WriteByte (&buf, n); - CL_WriteRecordDemoMessage (&buf, seq++); - SZ_Clear (&buf); - MSG_WriteByte (&buf, svc_soundlist); - MSG_WriteByte (&buf, n + 1); - } - n++; - s = cl.sound_name[n+1]; - } - if (buf.cursize) { - MSG_WriteByte (&buf, 0); - MSG_WriteByte (&buf, 0); - CL_WriteRecordDemoMessage (&buf, seq++); - SZ_Clear (&buf); - } - -// modellist - MSG_WriteByte (&buf, svc_modellist); - MSG_WriteByte (&buf, 0); - - n = 0; - s = cl.model_name[n+1]; - while (*s) { - MSG_WriteString (&buf, s); - if (buf.cursize > MAX_MSGLEN/2) { - MSG_WriteByte (&buf, 0); - MSG_WriteByte (&buf, n); - CL_WriteRecordDemoMessage (&buf, seq++); - SZ_Clear (&buf); - MSG_WriteByte (&buf, svc_modellist); - MSG_WriteByte (&buf, n + 1); - } - n++; - s = cl.model_name[n+1]; - } - if (buf.cursize) { - MSG_WriteByte (&buf, 0); - MSG_WriteByte (&buf, 0); - CL_WriteRecordDemoMessage (&buf, seq++); - SZ_Clear (&buf); - } - -// spawnstatic - - for (i = 0; i < cl.num_statics; i++) { - ent = cl_static_entities + i; - - MSG_WriteByte (&buf, svc_spawnstatic); - - for (j = 1; j < MAX_MODELS; j++) - if (ent->model == cl.model_precache[j]) - break; - if (j == MAX_MODELS) - MSG_WriteByte (&buf, 0); - else - MSG_WriteByte (&buf, j); - - MSG_WriteByte (&buf, ent->frame); - MSG_WriteByte (&buf, 0); - MSG_WriteByte (&buf, ent->skinnum); - for (j=0 ; j<3 ; j++) - { - MSG_WriteCoord (&buf, ent->origin[j]); - MSG_WriteAngle (&buf, ent->angles[j]); - } - - if (buf.cursize > MAX_MSGLEN/2) { - CL_WriteRecordDemoMessage (&buf, seq++); - SZ_Clear (&buf); - } - } - -// spawnstaticsound - // static sounds are skipped in demos, life is hard - -// baselines - - memset(&blankes, 0, sizeof(blankes)); - for (i = 0; i < MAX_EDICTS; i++) { - es = cl_baselines + i; - - if (memcmp(es, &blankes, sizeof(blankes))) { - MSG_WriteByte (&buf,svc_spawnbaseline); - MSG_WriteShort (&buf, i); - - MSG_WriteByte (&buf, es->modelindex); - MSG_WriteByte (&buf, es->frame); - MSG_WriteByte (&buf, es->colormap); - MSG_WriteByte (&buf, es->skinnum); - for (j=0 ; j<3 ; j++) - { - MSG_WriteCoord(&buf, es->origin[j]); - MSG_WriteAngle(&buf, es->angles[j]); - } - - if (buf.cursize > MAX_MSGLEN/2) { - CL_WriteRecordDemoMessage (&buf, seq++); - SZ_Clear (&buf); - } - } - } - - MSG_WriteByte (&buf, svc_stufftext); - MSG_WriteString (&buf, va("cmd spawn %i 0\n", cl.servercount) ); - - if (buf.cursize) { - CL_WriteRecordDemoMessage (&buf, seq++); - SZ_Clear (&buf); - } - -// send current status of all other players - - for (i = 0; i < MAX_CLIENTS; i++) { - player = cl.players + i; - - MSG_WriteByte (&buf, svc_updatefrags); - MSG_WriteByte (&buf, i); - MSG_WriteShort (&buf, player->frags); - - MSG_WriteByte (&buf, svc_updateping); - MSG_WriteByte (&buf, i); - MSG_WriteShort (&buf, player->ping); - - MSG_WriteByte (&buf, svc_updatepl); - MSG_WriteByte (&buf, i); - MSG_WriteByte (&buf, player->pl); - - MSG_WriteByte (&buf, svc_updateentertime); - MSG_WriteByte (&buf, i); - MSG_WriteFloat (&buf, realtime - player->entertime); // stupid, stupid, stupid! - - MSG_WriteByte (&buf, svc_updateuserinfo); - MSG_WriteByte (&buf, i); - MSG_WriteLong (&buf, player->userid); - MSG_WriteString (&buf, player->userinfo); - - if (buf.cursize > MAX_MSGLEN/2) { - CL_WriteRecordDemoMessage (&buf, seq++); - SZ_Clear (&buf); - } - } - -// send all current light styles - for (i=0 ; i MAX_MSGLEN/2) { - CL_WriteRecordDemoMessage (&buf, seq++); - SZ_Clear (&buf); - } - } - -#if 0 - MSG_WriteByte (&buf, svc_updatestatlong); - MSG_WriteByte (&buf, STAT_TOTALMONSTERS); - MSG_WriteLong (&buf, cl.stats[STAT_TOTALMONSTERS]); - - MSG_WriteByte (&buf, svc_updatestatlong); - MSG_WriteByte (&buf, STAT_SECRETS); - MSG_WriteLong (&buf, cl.stats[STAT_SECRETS]); - - MSG_WriteByte (&buf, svc_updatestatlong); - MSG_WriteByte (&buf, STAT_MONSTERS); - MSG_WriteLong (&buf, cl.stats[STAT_MONSTERS]); -#endif - - // get the client to check and download skins - // when that is completed, a begin command will be issued - MSG_WriteByte (&buf, svc_stufftext); - MSG_WriteString (&buf, va("skins\n") ); - - CL_WriteRecordDemoMessage (&buf, seq++); - - CL_WriteSetDemoMessage(); - - // done -} - -/* -==================== -CL_Record_f - -record -==================== -*/ -void CL_Record_f (void) -{ - int c; - char name[MAX_OSPATH]; - - c = Cmd_Argc(); - if (c != 2) - { - Con_Printf ("record \n"); - return; - } - - if (cls.state != ca_active) { - Con_Printf ("You must be connected to record.\n"); - return; - } - - if (cls.demorecording) - CL_Stop_f(); - - sprintf (name, "%s/%s", com_gamedir, Cmd_Argv(1)); - -// -// open the demo file -// - COM_ForceExtension (name, ".qwd"); - - cls.recordfile = fopen (name, "wb"); - if (!cls.recordfile) - { - Con_Printf ("ERROR: couldn't open.\n"); - return; - } - - Con_Printf ("recording to %s.\n", name); - CL_Record (); -} - - -/* -==================== -CL_EasyRecord_f - -easyrecord [demoname] -==================== -*/ -void CL_EasyRecord_f (void) -{ - int c; - char name[1024]; - char name2[MAX_OSPATH*2]; - int i; - unsigned char *p; - FILE *f; - - c = Cmd_Argc(); - if (c > 2) - { - Con_Printf ("easyrecord \n"); - return; - } - - if (cls.state != ca_active) { - Con_Printf ("You must be connected to record.\n"); - return; - } - - if (cls.demorecording) - CL_Stop_f(); - -/// FIXME: check buffer sizes!!! - - if (c == 2) - sprintf (name, "%s", Cmd_Argv(1)); - else if (cl.spectator) { - // FIXME: if tracking a player, use his name - sprintf (name, "spec_%s_%s", - TP_PlayerName(), - TP_MapName()); - } else { - // guess game type and write demo name - i = TP_CountPlayers(); - if (atoi(Info_ValueForKey(cl.serverinfo, "teamplay")) - && i >= 3) - { - // Teamplay - sprintf (name, "%s_%s_vs_%s_%s", - TP_PlayerName(), - TP_PlayerTeam(), - TP_EnemyTeam(), - TP_MapName); - } else { - if (i == 2) { - // Duel - sprintf (name, "%s_vs_%s_%s", - TP_PlayerName(), - TP_EnemyName(), - TP_MapName()); - } - else if (i > 2) { - // FFA - sprintf (name, "%s_ffa_%s", - TP_PlayerName, - TP_MapName()); - } - else { - // one player - sprintf (name, "%s_%s", - TP_PlayerName(), - TP_MapName()); - } - } - } - -// Make sure the filename doesn't contain illegal characters - for (p=name ; *p ; p++) { - char c; - *p &= 0x7F; // strip high bit - c = *p; - if (c<=' ' || c=='?' || c=='*' || c=='\\' || c=='/' || c==':' - || c=='<' || c=='>' || c=='"') - *p = '_'; - } - - Q_strncpyz (name, va("%s/%s", com_gamedir, name), MAX_OSPATH); - -// find a filename that doesn't exist yet - strcpy (name2, name); - COM_ForceExtension (name2, ".qwd"); - f = fopen (name2, "rb"); - if (f) { - i = 0; - do { - fclose (f); - strcpy (name2, va("%s_%02i", name, i)); - COM_ForceExtension (name2, ".qwd"); - f = fopen (name2, "rb"); - i++; - } while (f); - } - -// -// open the demo file -// - cls.recordfile = fopen (name2, "wb"); - if (!cls.recordfile) - { - Con_Printf ("ERROR: couldn't open.\n"); - return; - } - - Con_Printf ("recording to %s.\n", name2); - CL_Record (); -} - - -/* -==================== -CL_ReRecord_f - -record -==================== -*/ -void CL_ReRecord_f (void) -{ - int c; - char name[MAX_OSPATH]; - - c = Cmd_Argc(); - if (c != 2) - { - Con_Printf ("rerecord \n"); - return; - } - - if (!*cls.servername) { - Con_Printf("No server to reconnect to...\n"); - return; - } - - if (cls.demorecording) - CL_Stop_f(); - - sprintf (name, "%s/%s", com_gamedir, Cmd_Argv(1)); - -// -// open the demo file -// - COM_ForceExtension (name, ".qwd"); - - cls.recordfile = fopen (name, "wb"); - if (!cls.recordfile) - { - Con_Printf ("ERROR: couldn't open.\n"); - return; - } - - Con_Printf ("recording to %s.\n", name); - cls.demorecording = true; - - CL_Disconnect(); - CL_BeginServerConnect(); -} - - -//================================================================== -// .QWZ demos playback (via Qizmo) -// - -#ifdef _WIN32 -static void CheckQizmoCompletion () -{ - DWORD ExitCode; - - if (!hQizmoProcess) - return; - - if (!GetExitCodeProcess (hQizmoProcess, &ExitCode)) { - Con_Printf ("WARINING: GetExitCodeProcess failed\n"); - hQizmoProcess = NULL; - qwz_unpacking = false; - qwz_playback = false; - cls.demoplayback = false; - StopQWZPlayback (); - return; - } - - if (ExitCode == STILL_ACTIVE) - return; - - hQizmoProcess = NULL; - - if (!qwz_unpacking || !cls.demoplayback) { - StopQWZPlayback (); - return; - } - - qwz_unpacking = false; - - cls.demofile = fopen (tempqwd_name, "rb"); - if (!cls.demofile) { - Con_Printf ("Couldn't open %s\n", tempqwd_name); - qwz_playback = false; - cls.demoplayback = false; - return; - } - - // start playback - cls.demoplayback = true; - cls.state = ca_demostart; - Netchan_Setup (&cls.netchan, net_from, 0, net_clientsocket); - realtime = 0; -} - -static void StopQWZPlayback () -{ - if (!hQizmoProcess && tempqwd_name[0]) { - if (remove (tempqwd_name) != 0) - Con_Printf ("Couldn't delete %s\n", tempqwd_name); - tempqwd_name[0] = '\0'; - } - qwz_playback = false; -} - - -void PlayQWZDemo (void) -{ - extern cvar_t qizmo_dir; - STARTUPINFO si; - PROCESS_INFORMATION pi; - char *name; - char qwz_name[256]; - char cmdline[512]; - char *p; - - if (hQizmoProcess) { - Con_Printf ("Cannot unpack -- Qizmo still running!\n"); - return; - } - - name = Cmd_Argv(1); - - if (!strncmp(name, "../", 3) || !strncmp(name, "..\\", 3)) - Q_strncpyz (qwz_name, va("%s/%s", com_basedir, name+3), sizeof(qwz_name)); - else - if (name[0] == '/' || name[0] == '\\') - Q_strncpyz (qwz_name, va("%s/%s", com_gamedir, name+1), sizeof(qwz_name)); - else - Q_strncpyz (qwz_name, va("%s/%s", com_gamedir, name), sizeof(qwz_name)); - - // check if the file exists - cls.demofile = fopen (qwz_name, "rb"); - if (!cls.demofile) - { - Con_Printf ("Couldn't open %s\n", name); - return; - } - fclose (cls.demofile); - - Q_strncpyz (tempqwd_name, qwz_name, sizeof(tempqwd_name)-4); -#if 0 - // the right way - strcpy (tempqwd_name + strlen(tempqwd_name) - 4, ".qwd"); -#else - // the way Qizmo does it, sigh - p = strstr (tempqwd_name, ".qwz"); - if (!p) - p = strstr (tempqwd_name, ".QWZ"); - if (!p) - p = tempqwd_name + strlen(tempqwd_name); - strcpy (p, ".qwd"); -#endif - - cls.demofile = fopen (tempqwd_name, "rb"); - if (cls.demofile) { - // .qwd already exists, so just play it - cls.demoplayback = true; - cls.state = ca_demostart; - Netchan_Setup (&cls.netchan, net_from, 0, net_clientsocket); - realtime = 0; - return; - } - - Con_Printf ("unpacking %s...\n", name); - - // start Qizmo to unpack the demo - memset (&si, 0, sizeof(si)); - si.cb = sizeof(si); - si.wShowWindow = SW_HIDE; - si.dwFlags = STARTF_USESHOWWINDOW; - - Q_strncpyz (cmdline, va("%s/%s/qizmo.exe -D %s", com_basedir, - qizmo_dir.string, qwz_name), sizeof(cmdline)); - - if (!CreateProcess (NULL, cmdline, NULL, NULL, - FALSE, 0/* | HIGH_PRIORITY_CLASS*/, - NULL, va("%s/%s", com_basedir, qizmo_dir.string), &si, &pi)) - { - Con_Printf ("Couldn't execute %s/%s/qizmo.exe\n", - com_basedir, qizmo_dir.string); - return; - } - - hQizmoProcess = pi.hProcess; - qwz_unpacking = true; - qwz_playback = true; - - // demo playback doesn't actually start yet, we just set - // cls.demoplayback so that CL_StopPlayback() will be called - // if CL_Disconnect() is issued - cls.demoplayback = true; - cls.demofile = NULL; - cls.state = ca_demostart; - Netchan_Setup (&cls.netchan, net_from, 0, net_clientsocket); - realtime = 0; -} -#endif - -/* -==================== -CL_PlayDemo_f - -play [demoname] -==================== -*/ -void CL_ClearPredict(void); - -void CL_PlayDemo_f (void) -{ - extern float olddemotime; - char name[256]; - - if (Cmd_Argc() != 2) - { - Con_Printf ("play : plays a demo\n"); - return; - } - -// -// disconnect from server -// - CL_Disconnect (); - -// -// open the demo file -// - Q_strncpyz (name, Cmd_Argv(1), sizeof(name)-4); - - nextdemotime = 0; - -#ifdef _WIN32 - if (strlen(name) > 4 && !Q_strcasecmp(name + strlen(name) - 4, ".qwz")) { - PlayQWZDemo (); - return; - } -#endif - - //COM_DefaultExtension (name, ".qwd"); - COM_DefaultExtension (name, ".mvd"); - - Con_Printf ("Playing demo from %s.\n", name); - - if (!strncmp(name, "../", 3) || !strncmp(name, "..\\", 3)) - cls.demofile = fopen (va("%s/%s", com_basedir, name+3), "rb"); - else - COM_FOpenFile (name, &cls.demofile); - - if (!cls.demofile) - { - Con_Printf ("ERROR: couldn't open.\n"); - cls.demonum = -1; // stop demo loop - return; - } - - cls.demoplayback = true; - if (!strcmp(name + strlen(name)-3, "mvd")) { - cls.demoplayback2 = true; - Con_Printf("mvd\n"); - } else - Con_Printf("qwd\n"); - cls.state = ca_demostart; - olddemotime = 0; - Netchan_Setup (&cls.netchan, net_from, 0, net_clientsocket); - realtime = 0; - cls.findtrack = true; - cls.lasttype = 0; - cls.lastto = 0; - CL_ClearPredict(); -} - -/* -==================== -CL_FinishTimeDemo - -==================== -*/ -void CL_FinishTimeDemo (void) -{ - int frames; - float time; - - cls.timedemo = false; - -// the first frame didn't count - frames = (host_framecount - cls.td_startframe) - 1; - time = Sys_DoubleTime() - cls.td_starttime; - if (!time) - time = 1; - Con_Printf ("%i frames %5.1f seconds %5.1f fps\n", frames, time, frames/time); -} - -/* -==================== -CL_TimeDemo_f - -timedemo [demoname] -==================== -*/ -void CL_TimeDemo_f (void) -{ - if (Cmd_Argc() != 2) - { - Con_Printf ("timedemo : gets demo speeds\n"); - return; - } - - CL_PlayDemo_f (); - - if (cls.state != ca_demostart) - return; - -// cls.td_starttime will be grabbed at the second frame of the demo, so -// all the loading time doesn't get counted - - cls.timedemo = true; - cls.td_starttime = 0; - cls.td_startframe = host_framecount; - cls.td_lastframe = -1; // get a new message this frame -} - +/* +Copyright (C) 1996-1997 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 included (GNU.txt) 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 "quakedef.h" +#include "winquake.h" +#include "pmove.h" +#include "teamplay.h" + +void CL_FinishTimeDemo (void); + +// .qwz playback +static qboolean qwz_playback = false; +static qboolean qwz_unpacking = false; +#ifdef _WIN32 +static HANDLE hQizmoProcess = NULL; +static char tempqwd_name[256] = ""; // this file must be deleted + // after playback is finished +void CheckQizmoCompletion (); +void StopQWZPlayback (); +#endif + +/* +============================================================================== + +DEMO CODE + +When a demo is playing back, all NET_SendMessages are skipped, and +NET_GetMessages are read from the demo file. + +Whenever cl.time gets past the last received message, another message is +read from the demo file. +============================================================================== +*/ + +/* +============== +CL_StopPlayback + +Called when a demo file runs out, or the user starts a game +============== +*/ +void CL_StopPlayback (void) +{ + if (!cls.demoplayback) + return; + + if (cls.demofile) + fclose (cls.demofile); + cls.demofile = NULL; + cls.state = ca_disconnected; + cls.demoplayback = 0; + cls.demoplayback2 = 0; +#ifdef _WIN32 + if (qwz_playback) + StopQWZPlayback (); +#endif + + if (cls.timedemo) + CL_FinishTimeDemo (); + + cl.teamfortress = false; +} + +#define dem_cmd 0 +#define dem_read 1 +#define dem_set 2 + +/* +==================== +CL_WriteDemoCmd + +Writes the current user cmd +==================== +*/ +void CL_WriteDemoCmd (usercmd_t *pcmd) +{ + int i; + float fl; + byte c; + usercmd_t cmd; + +//Con_Printf("write: %ld bytes, %4.4f\n", msg->cursize, realtime); + + fl = LittleFloat((float)realtime); + fwrite (&fl, sizeof(fl), 1, cls.recordfile); + + c = dem_cmd; + fwrite (&c, sizeof(c), 1, cls.recordfile); + + // correct for byte order, bytes don't matter + cmd = *pcmd; + + for (i = 0; i < 3; i++) + cmd.angles[i] = LittleFloat(cmd.angles[i]); + cmd.forwardmove = LittleShort(cmd.forwardmove); + cmd.sidemove = LittleShort(cmd.sidemove); + cmd.upmove = LittleShort(cmd.upmove); + + fwrite(&cmd, sizeof(cmd), 1, cls.recordfile); + + for (i=0 ; i<3 ; i++) + { + fl = LittleFloat (cl.viewangles[i]); + fwrite (&fl, 4, 1, cls.recordfile); + } + + fflush (cls.recordfile); +} + +/* +==================== +CL_WriteDemoMessage + +Dumps the current net message, prefixed by the length and view angles +==================== +*/ +void CL_WriteDemoMessage (sizebuf_t *msg) +{ + int len; + float fl; + byte c; + +//Con_Printf("write: %ld bytes, %4.4f\n", msg->cursize, realtime); + + if (!cls.demorecording) + return; + + fl = LittleFloat((float)realtime); + fwrite (&fl, sizeof(fl), 1, cls.recordfile); + + c = dem_read; + fwrite (&c, sizeof(c), 1, cls.recordfile); + + len = LittleLong (msg->cursize); + fwrite (&len, 4, 1, cls.recordfile); + fwrite (msg->data, msg->cursize, 1, cls.recordfile); + + fflush (cls.recordfile); +} + +/* +==================== +CL_GetDemoMessage + + FIXME... +==================== +*/ +float olddemotime, nextdemotime; + +qboolean CL_GetDemoMessage (void) +{ + int r, i, j, size, tracknum; + float f, demotime; + byte newtime; + byte c; + usercmd_t *pcmd; + + if (qwz_unpacking) + return 0; + + if (cl.paused & 2) + return 0; + + if (!cls.demoplayback2) + nextdemotime = (float) realtime; + + if (realtime + 1.0 < nextdemotime) + { + realtime = nextdemotime - 1.0; + } + +nextdemomessage: + + size = 0; + // read the time from the packet + newtime = 0; + if (cls.demoplayback2) + { + fread(&newtime, sizeof(newtime), 1, cls.demofile); + demotime = cls.basetime + (cls.prevtime + newtime)*0.001; + } else { + fread(&demotime, sizeof(demotime), 1, cls.demofile); + demotime = LittleFloat(demotime); + if (!nextdemotime) + realtime = nextdemotime = demotime; + } + + if (realtime - nextdemotime > 0.0001) + { + if (nextdemotime != demotime) + { + olddemotime = nextdemotime; + if (cls.demoplayback2) { + Con_Printf("here\n"); + cls.netchan.incoming_sequence++; + cls.netchan.incoming_acknowledged++; + cls.netchan.frame_latency = 0; + cls.netchan.last_received = realtime; // just to happy timeout check + //Con_Printf("%d\n", cls.netchan.incoming_sequence); + } + } + + nextdemotime = demotime; + } + +// decide if it is time to grab the next message + if (cls.timedemo) { + if (cls.td_lastframe < 0) + cls.td_lastframe = demotime; + else if (demotime > cls.td_lastframe) { + cls.td_lastframe = demotime; + // rewind back to time + if (cls.demoplayback2) + { + fseek(cls.demofile, ftell(cls.demofile) - sizeof(newtime), + SEEK_SET); + } else + fseek(cls.demofile, ftell(cls.demofile) - sizeof(demotime), + SEEK_SET); + return 0; // already read this frame's message + } + if (!cls.td_starttime && cls.state == ca_active) { + cls.td_starttime = Sys_DoubleTime(); + cls.td_startframe = host_framecount; + } + realtime = demotime; // warp + } else if (!(cl.paused & 1) && cls.state >= ca_onserver) { // always grab until fully connected + if (!cls.demoplayback2 && realtime + 1.0 < demotime) { + // too far back + realtime = demotime - 1.0; + // rewind back to time + fseek(cls.demofile, ftell(cls.demofile) - sizeof(demotime), + SEEK_SET); + return 0; + } else if (nextdemotime < demotime) { + // rewind back to time + if (cls.demoplayback2) + { + fseek(cls.demofile, ftell(cls.demofile) - sizeof(newtime), + SEEK_SET); + } else + fseek(cls.demofile, ftell(cls.demofile) - sizeof(demotime), + SEEK_SET); + return 0; // don't need another message yet + } + } else + realtime = demotime; // we're warping + + cls.prevtime += newtime; + //prevtime = demotime; + if (cls.state < ca_demostart) + Host_Error ("CL_GetDemoMessage: cls.state != ca_active"); + + // get the msg type + fread (&c, sizeof(c), 1, cls.demofile); + + switch (c&7) { + case dem_cmd : + // user sent input + i = cls.netchan.outgoing_sequence & UPDATE_MASK; + pcmd = &cl.frames[i].cmd; + r = fread (pcmd, sizeof(*pcmd), 1, cls.demofile); + if (r != 1) + { + CL_StopPlayback (); + return 0; + } + // byte order stuff + for (j = 0; j < 3; j++) + pcmd->angles[j] = LittleFloat(pcmd->angles[j]); + pcmd->forwardmove = LittleShort(pcmd->forwardmove); + pcmd->sidemove = LittleShort(pcmd->sidemove); + pcmd->upmove = LittleShort(pcmd->upmove); + cl.frames[i].senttime = demotime; + cl.frames[i].receivedtime = -1; // we haven't gotten a reply yet + //cl.frames[i].playerstate[cl.playernum].command = *pcmd; + cls.netchan.outgoing_sequence++; + for (j=0 ; j<3 ; j++) + { + r = fread (&f, 4, 1, cls.demofile); + cl.viewangles[j] = LittleFloat (f); + + } + break; + + case dem_read: +readit: + // get the next message + fread (&net_message.cursize, 4, 1, cls.demofile); + net_message.cursize = LittleLong (net_message.cursize); + + if (net_message.cursize > net_message.maxsize) + Host_EndGame ("Demo message > MAX_UDP_PACKET"); + r = fread (net_message.data, net_message.cursize, 1, cls.demofile); + if (r != 1) + { + CL_StopPlayback (); + return 0; + } + + if (cls.demoplayback2) + { + tracknum = Cam_TrackNum(); + + if (cls.lasttype == dem_multiple) { + if (tracknum == -1) + goto nextdemomessage; + + if (!(cls.lastto & (1 << (tracknum)))) + goto nextdemomessage; + } else if (cls.lasttype == dem_single) + { + if (tracknum == -1 || cls.lastto != spec_track) + goto nextdemomessage; + } + } + break; + + case dem_set : + fread (&i, 4, 1, cls.demofile); + cls.netchan.outgoing_sequence = LittleLong(i); + fread (&i, 4, 1, cls.demofile); + cls.netchan.incoming_sequence = LittleLong(i); + if (cls.demoplayback2) { + cls.netchan.incoming_acknowledged = cls.netchan.incoming_sequence; + goto nextdemomessage; + } + break; + + case dem_multiple: + r = fread (&i, 4, 1, cls.demofile); + if (r != 1) + { + CL_StopPlayback (); + return 0; + } + + cls.lastto = LittleLong(i); + cls.lasttype = dem_multiple; + goto readit; + + case dem_single: + cls.lastto = c>>3; + cls.lasttype = dem_single; + goto readit; + case dem_stats: + cls.lastto = c>>3; + cls.lasttype = dem_stats; + goto readit; + case dem_all: + cls.lastto = 0; + cls.lasttype = dem_all; + goto readit; + default : + Con_Printf("Corrupted demo.\n"); + CL_StopPlayback (); + return 0; + } + + return 1; +} + +/* +==================== +CL_GetMessage + +Handles recording and playback of demos, on top of NET_ code +==================== +*/ +qboolean CL_GetMessage (void) +{ +#ifdef _WIN32 + CheckQizmoCompletion (); +#endif + + if (cls.demoplayback) + return CL_GetDemoMessage (); + + if (!NET_GetPacket(net_clientsocket)) + return false; + + CL_WriteDemoMessage (&net_message); + + return true; +} + + +/* +==================== +CL_Stop_f + +stop recording a demo +==================== +*/ +void CL_Stop_f (void) +{ + if (!cls.demorecording) + { + Con_Printf ("Not recording a demo.\n"); + return; + } + +// write a disconnect message to the demo file + SZ_Clear (&net_message); + MSG_WriteLong (&net_message, -1); // -1 sequence means out of band + MSG_WriteByte (&net_message, svc_disconnect); + MSG_WriteString (&net_message, "EndOfDemo"); + CL_WriteDemoMessage (&net_message); + +// finish up + fclose (cls.recordfile); + cls.recordfile = NULL; + cls.demorecording = false; + Con_Printf ("Completed demo\n"); +} + + +/* +==================== +CL_WriteDemoMessage + +Dumps the current net message, prefixed by the length and view angles +==================== +*/ +void CL_WriteRecordDemoMessage (sizebuf_t *msg, int seq) +{ + int len; + int i; + float fl; + byte c; + +//Con_Printf("write: %ld bytes, %4.4f\n", msg->cursize, realtime); + + if (!cls.demorecording) + return; + + fl = LittleFloat((float)realtime); + fwrite (&fl, sizeof(fl), 1, cls.recordfile); + + c = dem_read; + fwrite (&c, sizeof(c), 1, cls.recordfile); + + len = LittleLong (msg->cursize + 8); + fwrite (&len, 4, 1, cls.recordfile); + + i = LittleLong(seq); + fwrite (&i, 4, 1, cls.recordfile); + fwrite (&i, 4, 1, cls.recordfile); + + fwrite (msg->data, msg->cursize, 1, cls.recordfile); + + fflush (cls.recordfile); +} + + +void CL_WriteSetDemoMessage (void) +{ + int len; + float fl; + byte c; + +//Con_Printf("write: %ld bytes, %4.4f\n", msg->cursize, realtime); + + if (!cls.demorecording) + return; + + fl = LittleFloat((float)realtime); + fwrite (&fl, sizeof(fl), 1, cls.recordfile); + + c = dem_set; + fwrite (&c, sizeof(c), 1, cls.recordfile); + + len = LittleLong(cls.netchan.outgoing_sequence); + fwrite (&len, 4, 1, cls.recordfile); + len = LittleLong(cls.netchan.incoming_sequence); + fwrite (&len, 4, 1, cls.recordfile); + + fflush (cls.recordfile); +} + + + + +static void CL_Record (void) +{ + sizebuf_t buf; + char buf_data[MAX_MSGLEN]; + int n, i, j; + char *s; + entity_t *ent; + entity_state_t *es, blankes; + player_info_t *player; + extern char gamedirfile[]; + int seq = 1; + + cls.demorecording = true; + + noscale_realtime = realtime; + +/*-------------------------------------------------*/ + +// serverdata + // send the info about the new client to all connected clients + memset(&buf, 0, sizeof(buf)); + buf.data = buf_data; + buf.maxsize = sizeof(buf_data); + +// send the serverdata + MSG_WriteByte (&buf, svc_serverdata); + MSG_WriteLong (&buf, PROTOCOL_VERSION); + MSG_WriteLong (&buf, cl.servercount); + MSG_WriteString (&buf, gamedirfile); + + if (cl.spectator) + MSG_WriteByte (&buf, cl.playernum | 128); + else + MSG_WriteByte (&buf, cl.playernum); + + // send full levelname + MSG_WriteString (&buf, cl.levelname); + + // send the movevars + MSG_WriteFloat(&buf, movevars.gravity); + MSG_WriteFloat(&buf, movevars.stopspeed); + MSG_WriteFloat(&buf, movevars.maxspeed); + MSG_WriteFloat(&buf, movevars.spectatormaxspeed); + MSG_WriteFloat(&buf, movevars.accelerate); + MSG_WriteFloat(&buf, movevars.airaccelerate); + MSG_WriteFloat(&buf, movevars.wateraccelerate); + MSG_WriteFloat(&buf, movevars.friction); + MSG_WriteFloat(&buf, movevars.waterfriction); + MSG_WriteFloat(&buf, movevars.entgravity); + + // send music + MSG_WriteByte (&buf, svc_cdtrack); + MSG_WriteByte (&buf, 0); // none in demos + + // send server info string + MSG_WriteByte (&buf, svc_stufftext); + MSG_WriteString (&buf, va("fullserverinfo \"%s\"\n", cl.serverinfo) ); + + // flush packet + CL_WriteRecordDemoMessage (&buf, seq++); + SZ_Clear (&buf); + +// soundlist + MSG_WriteByte (&buf, svc_soundlist); + MSG_WriteByte (&buf, 0); + + n = 0; + s = cl.sound_name[n+1]; + while (*s) { + MSG_WriteString (&buf, s); + if (buf.cursize > MAX_MSGLEN/2) { + MSG_WriteByte (&buf, 0); + MSG_WriteByte (&buf, n); + CL_WriteRecordDemoMessage (&buf, seq++); + SZ_Clear (&buf); + MSG_WriteByte (&buf, svc_soundlist); + MSG_WriteByte (&buf, n + 1); + } + n++; + s = cl.sound_name[n+1]; + } + if (buf.cursize) { + MSG_WriteByte (&buf, 0); + MSG_WriteByte (&buf, 0); + CL_WriteRecordDemoMessage (&buf, seq++); + SZ_Clear (&buf); + } + +// modellist + MSG_WriteByte (&buf, svc_modellist); + MSG_WriteByte (&buf, 0); + + n = 0; + s = cl.model_name[n+1]; + while (*s) { + MSG_WriteString (&buf, s); + if (buf.cursize > MAX_MSGLEN/2) { + MSG_WriteByte (&buf, 0); + MSG_WriteByte (&buf, n); + CL_WriteRecordDemoMessage (&buf, seq++); + SZ_Clear (&buf); + MSG_WriteByte (&buf, svc_modellist); + MSG_WriteByte (&buf, n + 1); + } + n++; + s = cl.model_name[n+1]; + } + if (buf.cursize) { + MSG_WriteByte (&buf, 0); + MSG_WriteByte (&buf, 0); + CL_WriteRecordDemoMessage (&buf, seq++); + SZ_Clear (&buf); + } + +// spawnstatic + + for (i = 0; i < cl.num_statics; i++) { + ent = cl_static_entities + i; + + MSG_WriteByte (&buf, svc_spawnstatic); + + for (j = 1; j < MAX_MODELS; j++) + if (ent->model == cl.model_precache[j]) + break; + if (j == MAX_MODELS) + MSG_WriteByte (&buf, 0); + else + MSG_WriteByte (&buf, j); + + MSG_WriteByte (&buf, ent->frame); + MSG_WriteByte (&buf, 0); + MSG_WriteByte (&buf, ent->skinnum); + for (j=0 ; j<3 ; j++) + { + MSG_WriteCoord (&buf, ent->origin[j]); + MSG_WriteAngle (&buf, ent->angles[j]); + } + + if (buf.cursize > MAX_MSGLEN/2) { + CL_WriteRecordDemoMessage (&buf, seq++); + SZ_Clear (&buf); + } + } + +// spawnstaticsound + // static sounds are skipped in demos, life is hard + +// baselines + + memset(&blankes, 0, sizeof(blankes)); + for (i = 0; i < MAX_EDICTS; i++) { + es = cl_baselines + i; + + if (memcmp(es, &blankes, sizeof(blankes))) { + MSG_WriteByte (&buf,svc_spawnbaseline); + MSG_WriteShort (&buf, i); + + MSG_WriteByte (&buf, es->modelindex); + MSG_WriteByte (&buf, es->frame); + MSG_WriteByte (&buf, es->colormap); + MSG_WriteByte (&buf, es->skinnum); + for (j=0 ; j<3 ; j++) + { + MSG_WriteCoord(&buf, es->origin[j]); + MSG_WriteAngle(&buf, es->angles[j]); + } + + if (buf.cursize > MAX_MSGLEN/2) { + CL_WriteRecordDemoMessage (&buf, seq++); + SZ_Clear (&buf); + } + } + } + + MSG_WriteByte (&buf, svc_stufftext); + MSG_WriteString (&buf, va("cmd spawn %i 0\n", cl.servercount) ); + + if (buf.cursize) { + CL_WriteRecordDemoMessage (&buf, seq++); + SZ_Clear (&buf); + } + +// send current status of all other players + + for (i = 0; i < MAX_CLIENTS; i++) { + player = cl.players + i; + + MSG_WriteByte (&buf, svc_updatefrags); + MSG_WriteByte (&buf, i); + MSG_WriteShort (&buf, player->frags); + + MSG_WriteByte (&buf, svc_updateping); + MSG_WriteByte (&buf, i); + MSG_WriteShort (&buf, player->ping); + + MSG_WriteByte (&buf, svc_updatepl); + MSG_WriteByte (&buf, i); + MSG_WriteByte (&buf, player->pl); + + MSG_WriteByte (&buf, svc_updateentertime); + MSG_WriteByte (&buf, i); + MSG_WriteFloat (&buf, realtime - player->entertime); // stupid, stupid, stupid! + + MSG_WriteByte (&buf, svc_updateuserinfo); + MSG_WriteByte (&buf, i); + MSG_WriteLong (&buf, player->userid); + MSG_WriteString (&buf, player->userinfo); + + if (buf.cursize > MAX_MSGLEN/2) { + CL_WriteRecordDemoMessage (&buf, seq++); + SZ_Clear (&buf); + } + } + +// send all current light styles + for (i=0 ; i MAX_MSGLEN/2) { + CL_WriteRecordDemoMessage (&buf, seq++); + SZ_Clear (&buf); + } + } + +#if 0 + MSG_WriteByte (&buf, svc_updatestatlong); + MSG_WriteByte (&buf, STAT_TOTALMONSTERS); + MSG_WriteLong (&buf, cl.stats[STAT_TOTALMONSTERS]); + + MSG_WriteByte (&buf, svc_updatestatlong); + MSG_WriteByte (&buf, STAT_SECRETS); + MSG_WriteLong (&buf, cl.stats[STAT_SECRETS]); + + MSG_WriteByte (&buf, svc_updatestatlong); + MSG_WriteByte (&buf, STAT_MONSTERS); + MSG_WriteLong (&buf, cl.stats[STAT_MONSTERS]); +#endif + + // get the client to check and download skins + // when that is completed, a begin command will be issued + MSG_WriteByte (&buf, svc_stufftext); + MSG_WriteString (&buf, va("skins\n") ); + + CL_WriteRecordDemoMessage (&buf, seq++); + + CL_WriteSetDemoMessage(); + + // done +} + +/* +==================== +CL_Record_f + +record +==================== +*/ +void CL_Record_f (void) +{ + int c; + char name[MAX_OSPATH]; + + c = Cmd_Argc(); + if (c != 2) + { + Con_Printf ("record \n"); + return; + } + + if (cls.state != ca_active) { + Con_Printf ("You must be connected to record.\n"); + return; + } + + if (cls.demorecording) + CL_Stop_f(); + + sprintf (name, "%s/%s", com_gamedir, Cmd_Argv(1)); + +// +// open the demo file +// + COM_ForceExtension (name, ".qwd"); + + cls.recordfile = fopen (name, "wb"); + if (!cls.recordfile) + { + Con_Printf ("ERROR: couldn't open.\n"); + return; + } + + Con_Printf ("recording to %s.\n", name); + CL_Record (); +} + + +/* +==================== +CL_EasyRecord_f + +easyrecord [demoname] +==================== +*/ +void CL_EasyRecord_f (void) +{ + int c; + char name[1024]; + char name2[MAX_OSPATH*2]; + int i; + unsigned char *p; + FILE *f; + + c = Cmd_Argc(); + if (c > 2) + { + Con_Printf ("easyrecord \n"); + return; + } + + if (cls.state != ca_active) { + Con_Printf ("You must be connected to record.\n"); + return; + } + + if (cls.demorecording) + CL_Stop_f(); + +/// FIXME: check buffer sizes!!! + + if (c == 2) + sprintf (name, "%s", Cmd_Argv(1)); + else if (cl.spectator) { + // FIXME: if tracking a player, use his name + sprintf (name, "spec_%s_%s", + TP_PlayerName(), + TP_MapName()); + } else { + // guess game type and write demo name + i = TP_CountPlayers(); + if (atoi(Info_ValueForKey(cl.serverinfo, "teamplay")) + && i >= 3) + { + // Teamplay + sprintf (name, "%s_%s_vs_%s_%s", + TP_PlayerName(), + TP_PlayerTeam(), + TP_EnemyTeam(), + TP_MapName); + } else { + if (i == 2) { + // Duel + sprintf (name, "%s_vs_%s_%s", + TP_PlayerName(), + TP_EnemyName(), + TP_MapName()); + } + else if (i > 2) { + // FFA + sprintf (name, "%s_ffa_%s", + TP_PlayerName, + TP_MapName()); + } + else { + // one player + sprintf (name, "%s_%s", + TP_PlayerName(), + TP_MapName()); + } + } + } + +// Make sure the filename doesn't contain illegal characters + for (p=name ; *p ; p++) { + char c; + *p &= 0x7F; // strip high bit + c = *p; + if (c<=' ' || c=='?' || c=='*' || c=='\\' || c=='/' || c==':' + || c=='<' || c=='>' || c=='"') + *p = '_'; + } + + Q_strncpyz (name, va("%s/%s", com_gamedir, name), MAX_OSPATH); + +// find a filename that doesn't exist yet + strcpy (name2, name); + COM_ForceExtension (name2, ".qwd"); + f = fopen (name2, "rb"); + if (f) { + i = 0; + do { + fclose (f); + strcpy (name2, va("%s_%02i", name, i)); + COM_ForceExtension (name2, ".qwd"); + f = fopen (name2, "rb"); + i++; + } while (f); + } + +// +// open the demo file +// + cls.recordfile = fopen (name2, "wb"); + if (!cls.recordfile) + { + Con_Printf ("ERROR: couldn't open.\n"); + return; + } + + Con_Printf ("recording to %s.\n", name2); + CL_Record (); +} + + +/* +==================== +CL_ReRecord_f + +record +==================== +*/ +void CL_ReRecord_f (void) +{ + int c; + char name[MAX_OSPATH]; + + c = Cmd_Argc(); + if (c != 2) + { + Con_Printf ("rerecord \n"); + return; + } + + if (!*cls.servername) { + Con_Printf("No server to reconnect to...\n"); + return; + } + + if (cls.demorecording) + CL_Stop_f(); + + sprintf (name, "%s/%s", com_gamedir, Cmd_Argv(1)); + +// +// open the demo file +// + COM_ForceExtension (name, ".qwd"); + + cls.recordfile = fopen (name, "wb"); + if (!cls.recordfile) + { + Con_Printf ("ERROR: couldn't open.\n"); + return; + } + + Con_Printf ("recording to %s.\n", name); + cls.demorecording = true; + + CL_Disconnect(); + CL_BeginServerConnect(); +} + + +//================================================================== +// .QWZ demos playback (via Qizmo) +// + +#ifdef _WIN32 +static void CheckQizmoCompletion () +{ + DWORD ExitCode; + + if (!hQizmoProcess) + return; + + if (!GetExitCodeProcess (hQizmoProcess, &ExitCode)) { + Con_Printf ("WARINING: GetExitCodeProcess failed\n"); + hQizmoProcess = NULL; + qwz_unpacking = false; + qwz_playback = false; + cls.demoplayback = false; + StopQWZPlayback (); + return; + } + + if (ExitCode == STILL_ACTIVE) + return; + + hQizmoProcess = NULL; + + if (!qwz_unpacking || !cls.demoplayback) { + StopQWZPlayback (); + return; + } + + qwz_unpacking = false; + + cls.demofile = fopen (tempqwd_name, "rb"); + if (!cls.demofile) { + Con_Printf ("Couldn't open %s\n", tempqwd_name); + qwz_playback = false; + cls.demoplayback = false; + return; + } + + // start playback + cls.demoplayback = true; + cls.state = ca_demostart; + Netchan_Setup (&cls.netchan, net_from, 0, net_clientsocket); + realtime = 0; +} + +static void StopQWZPlayback () +{ + if (!hQizmoProcess && tempqwd_name[0]) { + if (remove (tempqwd_name) != 0) + Con_Printf ("Couldn't delete %s\n", tempqwd_name); + tempqwd_name[0] = '\0'; + } + qwz_playback = false; +} + + +void PlayQWZDemo (void) +{ + extern cvar_t qizmo_dir; + STARTUPINFO si; + PROCESS_INFORMATION pi; + char *name; + char qwz_name[256]; + char cmdline[512]; + char *p; + + if (hQizmoProcess) { + Con_Printf ("Cannot unpack -- Qizmo still running!\n"); + return; + } + + name = Cmd_Argv(1); + + if (!strncmp(name, "../", 3) || !strncmp(name, "..\\", 3)) + Q_strncpyz (qwz_name, va("%s/%s", com_basedir, name+3), sizeof(qwz_name)); + else + if (name[0] == '/' || name[0] == '\\') + Q_strncpyz (qwz_name, va("%s/%s", com_gamedir, name+1), sizeof(qwz_name)); + else + Q_strncpyz (qwz_name, va("%s/%s", com_gamedir, name), sizeof(qwz_name)); + + // check if the file exists + cls.demofile = fopen (qwz_name, "rb"); + if (!cls.demofile) + { + Con_Printf ("Couldn't open %s\n", name); + return; + } + fclose (cls.demofile); + + Q_strncpyz (tempqwd_name, qwz_name, sizeof(tempqwd_name)-4); +#if 0 + // the right way + strcpy (tempqwd_name + strlen(tempqwd_name) - 4, ".qwd"); +#else + // the way Qizmo does it, sigh + p = strstr (tempqwd_name, ".qwz"); + if (!p) + p = strstr (tempqwd_name, ".QWZ"); + if (!p) + p = tempqwd_name + strlen(tempqwd_name); + strcpy (p, ".qwd"); +#endif + + cls.demofile = fopen (tempqwd_name, "rb"); + if (cls.demofile) { + // .qwd already exists, so just play it + cls.demoplayback = true; + cls.state = ca_demostart; + Netchan_Setup (&cls.netchan, net_from, 0, net_clientsocket); + realtime = 0; + return; + } + + Con_Printf ("unpacking %s...\n", name); + + // start Qizmo to unpack the demo + memset (&si, 0, sizeof(si)); + si.cb = sizeof(si); + si.wShowWindow = SW_HIDE; + si.dwFlags = STARTF_USESHOWWINDOW; + + Q_strncpyz (cmdline, va("%s/%s/qizmo.exe -D %s", com_basedir, + qizmo_dir.string, qwz_name), sizeof(cmdline)); + + if (!CreateProcess (NULL, cmdline, NULL, NULL, + FALSE, 0/* | HIGH_PRIORITY_CLASS*/, + NULL, va("%s/%s", com_basedir, qizmo_dir.string), &si, &pi)) + { + Con_Printf ("Couldn't execute %s/%s/qizmo.exe\n", + com_basedir, qizmo_dir.string); + return; + } + + hQizmoProcess = pi.hProcess; + qwz_unpacking = true; + qwz_playback = true; + + // demo playback doesn't actually start yet, we just set + // cls.demoplayback so that CL_StopPlayback() will be called + // if CL_Disconnect() is issued + cls.demoplayback = true; + cls.demofile = NULL; + cls.state = ca_demostart; + Netchan_Setup (&cls.netchan, net_from, 0, net_clientsocket); + realtime = 0; +} +#endif + +/* +==================== +CL_PlayDemo_f + +play [demoname] +==================== +*/ +void CL_ClearPredict(void); + +void CL_PlayDemo_f (void) +{ + extern float olddemotime; + char name[256]; + + if (Cmd_Argc() != 2) + { + Con_Printf ("play : plays a demo\n"); + return; + } + +// +// disconnect from server +// + CL_Disconnect (); + +// +// open the demo file +// + Q_strncpyz (name, Cmd_Argv(1), sizeof(name)-4); + + nextdemotime = 0; + +#ifdef _WIN32 + if (strlen(name) > 4 && !Q_strcasecmp(name + strlen(name) - 4, ".qwz")) { + PlayQWZDemo (); + return; + } +#endif + + //COM_DefaultExtension (name, ".qwd"); + COM_DefaultExtension (name, ".mvd"); + + Con_Printf ("Playing demo from %s.\n", name); + + if (!strncmp(name, "../", 3) || !strncmp(name, "..\\", 3)) + cls.demofile = fopen (va("%s/%s", com_basedir, name+3), "rb"); + else + COM_FOpenFile (name, &cls.demofile); + + if (!cls.demofile) + { + Con_Printf ("ERROR: couldn't open.\n"); + cls.demonum = -1; // stop demo loop + return; + } + + cls.demoplayback = true; + if (!stricmp(name + strlen(name)-3, "mvd")) { + cls.demoplayback2 = true; + Con_Printf("mvd\n"); + } else + Con_Printf("qwd\n"); + cls.state = ca_demostart; + olddemotime = 0; + Netchan_Setup (&cls.netchan, net_from, 0, net_clientsocket); + realtime = 0; + cls.findtrack = true; + cls.lasttype = 0; + cls.lastto = 0; + cls.prevtime = 0; + cls.basetime = 0; + CL_ClearPredict(); +} + +/* +==================== +CL_FinishTimeDemo + +==================== +*/ +void CL_FinishTimeDemo (void) +{ + int frames; + float time; + + cls.timedemo = false; + +// the first frame didn't count + frames = (host_framecount - cls.td_startframe) - 1; + time = Sys_DoubleTime() - cls.td_starttime; + if (!time) + time = 1; + Con_Printf ("%i frames %5.1f seconds %5.1f fps\n", frames, time, frames/time); +} + +/* +==================== +CL_TimeDemo_f + +timedemo [demoname] +==================== +*/ +void CL_TimeDemo_f (void) +{ + if (Cmd_Argc() != 2) + { + Con_Printf ("timedemo : gets demo speeds\n"); + return; + } + + CL_PlayDemo_f (); + + if (cls.state != ca_demostart) + return; + +// cls.td_starttime will be grabbed at the second frame of the demo, so +// all the loading time doesn't get counted + + cls.timedemo = true; + cls.td_starttime = 0; + cls.td_startframe = host_framecount; + cls.td_lastframe = -1; // get a new message this frame +} + diff --git a/source/cl_ents.c b/source/cl_ents.c index e696e5f5..d41e1777 100644 --- a/source/cl_ents.c +++ b/source/cl_ents.c @@ -1,1504 +1,1504 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// cl_ents.c -- entity parsing and management - -#include "quakedef.h" -#include "pmove.h" -#include "teamplay.h" - -extern cvar_t cl_predict_players; -extern cvar_t cl_predict_players2; -extern cvar_t cl_solid_players; - -static struct predicted_player { - int flags; - qboolean active; - qboolean predict; - vec3_t origin; - vec3_t oldo; - vec3_t olda; - player_state_t *oldstate; -} predicted_players[MAX_CLIENTS]; - -//============================================================ - -/* -=============== -CL_AllocDlight - -=============== -*/ -dlight_t *CL_AllocDlight (int key) -{ - int i; - dlight_t *dl; - -// first look for an exact key match - if (key) - { - dl = cl_dlights; - for (i=0 ; ikey == key) - { - memset (dl, 0, sizeof(*dl)); - dl->key = key; - return dl; - } - } - } - -// then look for anything else - dl = cl_dlights; - for (i=0 ; idie < cl.time) - { - memset (dl, 0, sizeof(*dl)); - dl->key = key; - return dl; - } - } - - dl = &cl_dlights[0]; - memset (dl, 0, sizeof(*dl)); - dl->key = key; - return dl; -} - -/* -=============== -CL_NewDlight -=============== -*/ -void CL_NewDlight (int key, float x, float y, float z, float radius, float time, - int type) -{ - dlight_t *dl; - - dl = CL_AllocDlight (key); - dl->origin[0] = x; - dl->origin[1] = y; - dl->origin[2] = z; - dl->radius = radius; - dl->die = cl.time + time; - if (type == 0) { - dl->color[0] = 0.2; - dl->color[1] = 0.1; - dl->color[2] = 0.05; - dl->color[3] = 0.7; - } else if (type == 1) { - dl->color[0] = 0.05; - dl->color[1] = 0.05; - dl->color[2] = 0.3; - dl->color[3] = 0.7; - } else if (type == 2) { - dl->color[0] = 0.5; - dl->color[1] = 0.05; - dl->color[2] = 0.05; - dl->color[3] = 0.7; - } else if (type == 3) { - dl->color[0]=0.5; - dl->color[1] = 0.05; - dl->color[2] = 0.4; - dl->color[3] = 0.7; - } -} - - -/* -=============== -CL_DecayLights - -=============== -*/ -void CL_DecayLights (void) -{ - int i; - dlight_t *dl; - - dl = cl_dlights; - for (i=0 ; idie < cl.time || !dl->radius) - continue; - - dl->radius -= host_frametime*dl->decay; - if (dl->radius < 0) - dl->radius = 0; - } -} - - -/* -========================================================================= - -PACKET ENTITY PARSING / LINKING - -========================================================================= -*/ - -/* -================== -CL_ParseDelta - -Can go from either a baseline or a previous packet_entity -================== -*/ -int bitcounts[32]; /// just for protocol profiling -void CL_ParseDelta (entity_state_t *from, entity_state_t *to, int bits) -{ - int i; - - // set everything to the state we are delta'ing from - *to = *from; - - to->number = bits & 511; - bits &= ~511; - - if (bits & U_MOREBITS) - { // read in the low order bits - i = MSG_ReadByte (); - bits |= i; - } - - // count the bits for net profiling - for (i=0 ; i<16 ; i++) - if (bits&(1<flags = bits; - - if (bits & U_MODEL) - to->modelindex = MSG_ReadByte (); - - if (bits & U_FRAME) - to->frame = MSG_ReadByte (); - - if (bits & U_COLORMAP) - to->colormap = MSG_ReadByte(); - - if (bits & U_SKIN) - to->skinnum = MSG_ReadByte(); - - if (bits & U_EFFECTS) - to->effects = MSG_ReadByte(); - - if (bits & U_ORIGIN1) - to->origin[0] = MSG_ReadCoord (); - - if (bits & U_ANGLE1) - to->angles[0] = MSG_ReadAngle(); - - if (bits & U_ORIGIN2) - to->origin[1] = MSG_ReadCoord (); - - if (bits & U_ANGLE2) - to->angles[1] = MSG_ReadAngle(); - - if (bits & U_ORIGIN3) - to->origin[2] = MSG_ReadCoord (); - - if (bits & U_ANGLE3) - to->angles[2] = MSG_ReadAngle(); - - if (bits & U_SOLID) - { - // FIXME - } -} - - -/* -================= -FlushEntityPacket -================= -*/ -void FlushEntityPacket (void) -{ - int word; - entity_state_t olde, newe; - - Con_DPrintf ("FlushEntityPacket\n"); - - - memset (&olde, 0, sizeof(olde)); - - cl.validsequence = 0; // can't render a frame - cl.frames[cls.netchan.incoming_sequence&UPDATE_MASK].invalid = true; - - // read it all, but ignore it - while (1) - { - word = (unsigned short)MSG_ReadShort (); - if (msg_badread) - { // something didn't parse right... - Host_EndGame ("msg_badread in packetentities"); - return; - } - - if (!word) - break; // done - - CL_ParseDelta (&olde, &newe, word); - } -} - -void CL_InitEntInter(void) -{ - int i; - interpolate_t *ient; - packet_entities_t *pack; - entity_state_t *ent; - - pack = &cl.frames[cl.int_packet].packet_entities; - - for (i=0, ient = cl.int_entities, ent = pack->entities; i < pack->num_entities; i++, ent++, ient++) - { - if (!ient->interpolate) - continue; - - VectorCopy(ient->origin, ent->origin); - VectorCopy(ient->angles, ent->angles); - ient->interpolate = false; - } -} - -/* -================== -CL_ParsePacketEntities - -An svc_packetentities has just been parsed, deal with the -rest of the data stream. -================== -*/ -void CL_ParsePacketEntities (qboolean delta) -{ - int newpacket; - packet_entities_t *oldp, *newp, dummy; - int oldindex, newindex; - int word, newnum, oldnum; - qboolean full; - byte from; - int oldpacket; - - newpacket = cls.netchan.incoming_sequence&UPDATE_MASK; - newp = &cl.frames[newpacket].packet_entities; - cl.frames[newpacket].invalid = false; - - CL_InitEntInter(); - - if (delta) - { - from = MSG_ReadByte (); - - oldpacket = cl.frames[newpacket].delta_sequence; - if (cls.demoplayback2) - from = oldpacket = (cls.netchan.incoming_sequence-1); - - //Con_Printf("old:%d from:%d, new:%d, prev:%d\n", oldpacket, from, newpacket, (cls.netchan.incoming_sequence-1)&255); - - if (cls.netchan.outgoing_sequence - cls.netchan.incoming_sequence >= UPDATE_BACKUP-1) - { // there are no valid frames left, so drop it - FlushEntityPacket (); - return; - } - - if ( (from&UPDATE_MASK) != (oldpacket&UPDATE_MASK) ) - Con_DPrintf ("WARNING: from mismatch\n"); - - if (cls.netchan.outgoing_sequence - oldpacket >= UPDATE_BACKUP-1) - { // we can't use this, it is too old - FlushEntityPacket (); - return; - } - - cl.validsequence = cls.netchan.incoming_sequence; - oldp = &cl.frames[oldpacket&UPDATE_MASK].packet_entities; - full = false; - cl.int_packet = newpacket; - } - else - { // this is a full update that we can start delta compressing from now - oldp = &dummy; - dummy.num_entities = 0; - cl.validsequence = cls.netchan.incoming_sequence; - full = true; - } - - oldindex = 0; - newindex = 0; - newp->num_entities = 0; - - while (1) - { - word = (unsigned short)MSG_ReadShort (); - if (msg_badread) - { // something didn't parse right... - Host_EndGame ("msg_badread in packetentities"); - return; - } - - if (!word) - { - while (oldindex < oldp->num_entities) - { // copy all the rest of the entities from the old packet -//Con_Printf ("copy %i\n", oldp->entities[oldindex].number); - if (newindex >= MAX_PACKET_ENTITIES) - Host_EndGame ("CL_ParsePacketEntities: newindex == MAX_PACKET_ENTITIES"); - newp->entities[newindex] = oldp->entities[oldindex]; - newindex++; - oldindex++; - } - break; - } - newnum = word&511; - oldnum = oldindex >= oldp->num_entities ? 9999 : oldp->entities[oldindex].number; - - while (newnum > oldnum) - { - if (full) - { - Con_Printf ("WARNING: oldcopy on full update"); - FlushEntityPacket (); - return; - } - -//Con_Printf ("copy %i\n", oldnum); - // copy one of the old entities over to the new packet unchanged - if (newindex >= MAX_PACKET_ENTITIES) - Host_EndGame ("CL_ParsePacketEntities: newindex == MAX_PACKET_ENTITIES"); - newp->entities[newindex] = oldp->entities[oldindex]; - newindex++; - oldindex++; - oldnum = oldindex >= oldp->num_entities ? 9999 : oldp->entities[oldindex].number; - } - - if (newnum < oldnum) - { // new from baseline -//Con_Printf ("baseline %i\n", newnum); - if (word & U_REMOVE) - { - if (full) - { - cl.validsequence = 0; - Con_Printf ("WARNING: U_REMOVE on full update\n"); - FlushEntityPacket (); - return; - } - continue; - } - if (newindex >= MAX_PACKET_ENTITIES) - Host_EndGame ("CL_ParsePacketEntities: newindex == MAX_PACKET_ENTITIES"); - CL_ParseDelta (&cl_baselines[newnum], &newp->entities[newindex], word); - newindex++; - continue; - } - - if (newnum == oldnum) - { // delta from previous - if (full) - { - cl.validsequence = 0; - Con_Printf ("WARNING: delta on full update"); - } - if (word & U_REMOVE) - { - oldindex++; - continue; - } -//Con_Printf ("delta %i\n",newnum); - CL_ParseDelta (&oldp->entities[oldindex], &newp->entities[newindex], word); - cl.int_entities[newindex].interpolate = true; - cl.int_entities[newindex].oldindex = oldindex; - - newindex++; - oldindex++; - } - - } - - newp->num_entities = newindex; -} - - -extern int cl_playerindex; -extern int cl_h_playerindex, cl_gib1index, cl_gib2index, cl_gib3index; -extern int cl_rocketindex, cl_grenadeindex; - -/* -=============== -CL_LinkPacketEntities - -=============== -*/ -void CL_LinkPacketEntities (void) -{ - entity_t *ent; - packet_entities_t *pack; - entity_state_t *s1, *s2; - float f; - model_t *model; - vec3_t old_origin; - float autorotate; - int i; - int pnum; - dlight_t *dl; - - pack = &cl.frames[cls.netchan.incoming_sequence&UPDATE_MASK].packet_entities; - - autorotate = anglemod(100*cl.time); - - f = 0; // FIXME: no interpolation right now - - for (pnum=0 ; pnumnum_entities ; pnum++) - { - s1 = &pack->entities[pnum]; - s2 = s1; // FIXME: no interpolation right now - - // control powerup glow for bots - if (s1->modelindex != cl_playerindex || r_powerupglow.value) - { - // spawn light flashes, even ones coming from invisible objects - if ((s1->effects & (EF_BLUE | EF_RED)) == (EF_BLUE | EF_RED)) - CL_NewDlight (s1->number, s1->origin[0], s1->origin[1], s1->origin[2], 200 + (rand()&31), 0.1, 3); - else if (s1->effects & EF_BLUE) - CL_NewDlight (s1->number, s1->origin[0], s1->origin[1], s1->origin[2], 200 + (rand()&31), 0.1, 1); - else if (s1->effects & EF_RED) - CL_NewDlight (s1->number, s1->origin[0], s1->origin[1], s1->origin[2], 200 + (rand()&31), 0.1, 2); - else if (s1->effects & EF_BRIGHTLIGHT) - CL_NewDlight (s1->number, s1->origin[0], s1->origin[1], s1->origin[2] + 16, 400 + (rand()&31), 0.1, 0); - else if (s1->effects & EF_DIMLIGHT) - CL_NewDlight (s1->number, s1->origin[0], s1->origin[1], s1->origin[2], 200 + (rand()&31), 0.1, 0); - } - - if (cl_deadbodyfilter.value && s1->modelindex == cl_playerindex - && ( (i=s1->frame)==49 || i==60 || i==69 || i==84 || i==93 || i==102) ) - continue; - - if (cl_gibfilter.value) - if (s1->modelindex == cl_h_playerindex || s1->modelindex == cl_gib1index - || s1->modelindex == cl_gib2index || s1->modelindex == cl_gib3index) - continue; - - if (cl_rocket2grenade.value && cl_grenadeindex != -1) - if (s1->modelindex == cl_rocketindex) - s1->modelindex = cl_grenadeindex; - - // if set to invisible, skip - if (!s1->modelindex) - continue; - - // create a new entity - if (cl_numvisedicts == MAX_VISEDICTS) - break; // object list is full - - ent = &cl_visedicts[cl_numvisedicts]; - cl_numvisedicts++; - - ent->keynum = s1->number; - ent->model = model = cl.model_precache[s1->modelindex]; - - // set colormap - if (s1->colormap && (s1->colormap < MAX_CLIENTS) - && !strcmp(ent->model->name,"progs/player.mdl") ) - { - ent->colormap = cl.players[s1->colormap-1].translations; - ent->scoreboard = &cl.players[s1->colormap-1]; - } - else - { - ent->colormap = vid.colormap; - ent->scoreboard = NULL; - } - - // set skin - ent->skinnum = s1->skinnum; - - // set frame - ent->frame = s1->frame; - - // rotate binary objects locally - if (model->flags & EF_ROTATE) - { - ent->angles[0] = 0; - ent->angles[1] = autorotate; - ent->angles[2] = 0; - } - else - { - float a1, a2; - - for (i=0 ; i<3 ; i++) - { - a1 = s1->angles[i]; - a2 = s2->angles[i]; - if (a1 - a2 > 180) - a1 -= 360; - if (a1 - a2 < -180) - a1 += 360; - ent->angles[i] = a2 + f * (a1 - a2); - } - } - - // calculate origin - for (i=0 ; i<3 ; i++) - ent->origin[i] = s2->origin[i] + - f * (s1->origin[i] - s2->origin[i]); - - // add automatic particle trails - if (!model->flags) - continue; - - // scan the old entity display list for a matching - for (i=0 ; ikeynum) - { - VectorCopy (cl_oldvisedicts[i].origin, old_origin); - break; - } - } - if (i == cl_oldnumvisedicts) - continue; // not in last message - - for (i=0 ; i<3 ; i++) - if ( abs(old_origin[i] - ent->origin[i]) > 128) - { // no trail if too far - VectorCopy (ent->origin, old_origin); - break; - } - if (model->flags & EF_ROCKET) - { - if (r_rockettrail.value) { - if (r_rockettrail.value == 2) - R_RocketTrail (old_origin, ent->origin, 1); - else - R_RocketTrail (old_origin, ent->origin, 0); - } - - if (r_rocketlight.value) { - dl = CL_AllocDlight (s1->number); - VectorCopy (ent->origin, dl->origin); - dl->radius = 200; - dl->die = cl.time + 0.1; - } - } - else if (model->flags & EF_GRENADE && r_grenadetrail.value) - R_RocketTrail (old_origin, ent->origin, 1); - else if (model->flags & EF_GIB) - R_RocketTrail (old_origin, ent->origin, 2); - else if (model->flags & EF_ZOMGIB) - R_RocketTrail (old_origin, ent->origin, 4); - else if (model->flags & EF_TRACER) - R_RocketTrail (old_origin, ent->origin, 3); - else if (model->flags & EF_TRACER2) - R_RocketTrail (old_origin, ent->origin, 5); - else if (model->flags & EF_TRACER3) - R_RocketTrail (old_origin, ent->origin, 6); - } -} - - -/* -========================================================================= - -PROJECTILE PARSING / LINKING - -========================================================================= -*/ - -typedef struct -{ - int modelindex; - vec3_t origin; - vec3_t angles; - int num; -} projectile_t; - -projectile_t cl_projectiles[MAX_PROJECTILES], cl_oldprojectiles[MAX_PROJECTILES]; -int cl_num_projectiles, cl_num_oldprojectiles; - -extern int cl_spikeindex; - -void CL_ClearProjectiles (void) -{ - static int parsecount = 0; - - if (parsecount == cl.parsecount) - return; - - parsecount = cl.parsecount; - - memset(cl.int_projectiles, 0, sizeof(cl.int_projectiles)); - - cl_num_oldprojectiles = cl_num_projectiles; - memcpy(cl_oldprojectiles, cl_projectiles, sizeof(cl_projectiles)); - cl_num_projectiles = 0; -} - -/* -===================== -CL_ParseProjectiles - -Nails are passed as efficient temporary entities -===================== -*/ -void CL_ParseProjectiles (qboolean nail2) -{ - int i, c, j, num; - byte bits[6]; - projectile_t *pr; - interpolate_t *inter; - - c = MSG_ReadByte (); - for (i=0 ; imodelindex = cl_spikeindex; - pr->origin[0] = ( ( bits[0] + ((bits[1]&15)<<8) ) <<1) - 4096; - pr->origin[1] = ( ( (bits[1]>>4) + (bits[2]<<4) ) <<1) - 4096; - pr->origin[2] = ( ( bits[3] + ((bits[4]&15)<<8) ) <<1) - 4096; - pr->angles[0] = 360*(bits[4]>>4)/16; - pr->angles[1] = 360*bits[5]/256; - pr->num = num; - if (!num) - continue; - - for (j = 0; j < cl_num_oldprojectiles; j++) - if (cl_oldprojectiles[j].num == num) - { - inter->interpolate = true; - inter->oldindex = j; - VectorCopy(pr->origin, inter->origin); - if (!cl_oldprojectiles[j].origin[0]) - Con_Printf("parse:%d, %d, %d\n", j, num, cl_oldprojectiles[j].num); - break; - } - } -} - -/* -============= -CL_LinkProjectiles - -============= -*/ -void CL_LinkProjectiles (void) -{ - int i; - projectile_t *pr; - entity_t *ent; - - for (i=0, pr=cl_projectiles ; ikeynum = 0; - - if (pr->modelindex < 1) - continue; - ent->model = cl.model_precache[pr->modelindex]; - ent->skinnum = 0; - ent->frame = 0; - ent->colormap = vid.colormap; - ent->scoreboard = NULL; - VectorCopy (pr->origin, ent->origin); - VectorCopy (pr->angles, ent->angles); - } -} - -//======================================== - -extern int cl_spikeindex, cl_playerindex, cl_flagindex; - -entity_t *CL_NewTempEntity (void); - -/* -=================== -CL_ParsePlayerinfo -=================== -*/ -extern int parsecountmod; -extern double parsecounttime; - -#define DF_ORIGIN 1 -#define DF_ANGLES (1<<3) -#define DF_EFFECTS (1<<6) -#define DF_SKINNUM (1<<7) -#define DF_DEAD (1<<8) -#define DF_GIB (1<<9) -#define DF_WEAPONFRAME (1<<10) -#define DF_MODEL (1<<11) - -int TranslateFlags(int src) -{ - int dst = 0; - - if (src & DF_EFFECTS) - dst |= PF_EFFECTS; - if (src & DF_SKINNUM) - dst |= PF_SKINNUM; - if (src & DF_DEAD) - dst |= PF_DEAD; - if (src & DF_GIB) - dst |= PF_GIB; - if (src & DF_WEAPONFRAME) - dst |= PF_WEAPONFRAME; - if (src & DF_MODEL) - dst |= PF_MODEL; - - return dst; -} - - -void CL_ParsePlayerinfo (void) -{ - int msec; - int flags; - player_info_t *info; - player_state_t *state, *prevstate, dummy; - int num; - int i; - - num = MSG_ReadByte (); - if (num >= MAX_CLIENTS) - Sys_Error ("CL_ParsePlayerinfo: bad num"); - - info = &cl.players[num]; - - memset(&dummy, 0, sizeof(dummy)); - - state = &cl.frames[parsecountmod].playerstate[num]; - if (info->prevcount > cl.parsecount || !cl.parsecount) { - prevstate = &dummy; - } else { - if (cl.parsecount - info->prevcount >= UPDATE_BACKUP-1) - prevstate = &dummy; - else - prevstate = &cl.frames[info->prevcount&UPDATE_MASK].playerstate[num]; - } - - info->prevcount = cl.parsecount; - - if (cls.demoplayback2) - { - if (cls.findtrack && info->stats[STAT_HEALTH] != 0) - { - extern int autocam; - extern int ideal_track; - - autocam = CAM_TRACK; - Cam_Lock(num); - ideal_track = num; - cls.findtrack = false; - - } - - memcpy(state, prevstate, sizeof(player_state_t)); - flags = MSG_ReadShort (); - state->flags = TranslateFlags(flags); - - state->messagenum = cl.parsecount; - state->command.msec = 0; - - state->frame = MSG_ReadByte (); - - state->state_time = parsecounttime; - state->command.msec = 0; - - for (i=0; i <3; i++) - if (flags & (DF_ORIGIN << i)) - state->origin[i] = MSG_ReadCoord (); - - for (i=0; i <3; i++) - if (flags & (DF_ANGLES << i)) - state->command.angles[i] = MSG_ReadAngle16 (); - - - if (flags & DF_MODEL) - state->modelindex = MSG_ReadByte (); - - if (flags & DF_SKINNUM) - state->skinnum = MSG_ReadByte (); - - if (flags & DF_EFFECTS) - state->effects = MSG_ReadByte (); - - if (flags & DF_WEAPONFRAME) - state->weaponframe = MSG_ReadByte (); - - VectorCopy (state->command.angles, state->viewangles); - return; - } - - flags = state->flags = MSG_ReadShort (); - - state->messagenum = cl.parsecount; - state->origin[0] = MSG_ReadCoord (); - state->origin[1] = MSG_ReadCoord (); - state->origin[2] = MSG_ReadCoord (); - - state->frame = MSG_ReadByte (); - - // the other player's last move was likely some time - // before the packet was sent out, so accurately track - // the exact time it was valid at - if (flags & PF_MSEC) - { - msec = MSG_ReadByte (); - state->state_time = parsecounttime - msec*0.001; - } - else - state->state_time = parsecounttime; - - if (flags & PF_COMMAND) - MSG_ReadDeltaUsercmd (&nullcmd, &state->command); - - for (i=0 ; i<3 ; i++) - { - if (flags & (PF_VELOCITY1<velocity[i] = MSG_ReadShort(); - else - state->velocity[i] = 0; - } - if (flags & PF_MODEL) - state->modelindex = MSG_ReadByte (); - else - state->modelindex = cl_playerindex; - - if (flags & PF_SKINNUM) - state->skinnum = MSG_ReadByte (); - else - state->skinnum = 0; - - if (flags & PF_EFFECTS) - state->effects = MSG_ReadByte (); - else - state->effects = 0; - - if (flags & PF_WEAPONFRAME) - state->weaponframe = MSG_ReadByte (); - else - state->weaponframe = 0; - - VectorCopy (state->command.angles, state->viewangles); - -} - - -/* -================ -CL_AddFlagModels - -Called when the CTF flags are set -================ -*/ -void CL_AddFlagModels (entity_t *ent, int team) -{ - int i; - float f; - vec3_t v_forward, v_right, v_up; - entity_t *newent; - - if (cl_flagindex == -1) - return; - - f = 14; - if (ent->frame >= 29 && ent->frame <= 40) { - if (ent->frame >= 29 && ent->frame <= 34) { //axpain - if (ent->frame == 29) f = f + 2; - else if (ent->frame == 30) f = f + 8; - else if (ent->frame == 31) f = f + 12; - else if (ent->frame == 32) f = f + 11; - else if (ent->frame == 33) f = f + 10; - else if (ent->frame == 34) f = f + 4; - } else if (ent->frame >= 35 && ent->frame <= 40) { // pain - if (ent->frame == 35) f = f + 2; - else if (ent->frame == 36) f = f + 10; - else if (ent->frame == 37) f = f + 10; - else if (ent->frame == 38) f = f + 8; - else if (ent->frame == 39) f = f + 4; - else if (ent->frame == 40) f = f + 2; - } - } else if (ent->frame >= 103 && ent->frame <= 118) { - if (ent->frame >= 103 && ent->frame <= 104) f = f + 6; //nailattack - else if (ent->frame >= 105 && ent->frame <= 106) f = f + 6; //light - else if (ent->frame >= 107 && ent->frame <= 112) f = f + 7; //rocketattack - else if (ent->frame >= 112 && ent->frame <= 118) f = f + 7; //shotattack - } - - newent = CL_NewTempEntity (); - newent->model = cl.model_precache[cl_flagindex]; - newent->skinnum = team; - - AngleVectors (ent->angles, v_forward, v_right, v_up); - v_forward[2] = -v_forward[2]; // reverse z component - for (i=0 ; i<3 ; i++) - newent->origin[i] = ent->origin[i] - f*v_forward[i] + 22*v_right[i]; - newent->origin[2] -= 16; - - VectorCopy (ent->angles, newent->angles) - newent->angles[2] -= 45; -} - -/* -============= -CL_LinkPlayers - -Create visible entities in the correct position -for all current players -============= -*/ -void CL_LinkPlayers (void) -{ - int j; - player_info_t *info; - player_state_t *state; - player_state_t exact; - double playertime; - entity_t *ent; - int msec; - frame_t *frame; - int oldphysent; - vec3_t org; - int i; - - playertime = realtime - cls.latency + 0.02; - if (playertime > realtime) - playertime = realtime; - - frame = &cl.frames[cl.parsecount&UPDATE_MASK]; - - for (j=0, info=cl.players, state=frame->playerstate ; j < MAX_CLIENTS - ; j++, info++, state++) - { - if (state->messagenum != cl.parsecount) - continue; // not present this frame - - // spawn light flashes, even ones coming from invisible objects -#ifdef GLQUAKE - if (!gl_flashblend.value || j != cl.playernum) { -#endif - if (r_powerupglow.value && !(r_powerupglow.value == 2 && j == cl.playernum)) - { - if (j == cl.playernum) { - VectorCopy (cl.simorg, org); - } else - VectorCopy (state->origin, org); - - if ((state->effects & (EF_BLUE | EF_RED)) == (EF_BLUE | EF_RED)) - CL_NewDlight (j+1, org[0], org[1], org[2], 200 + (rand()&31), 0.1, 3); - else if (state->effects & EF_BLUE) - CL_NewDlight (j+1, org[0], org[1], org[2], 200 + (rand()&31), 0.1, 1); - else if (state->effects & EF_RED) - CL_NewDlight (j+1, org[0], org[1], org[2], 200 + (rand()&31), 0.1, 2); - else if (state->effects & EF_BRIGHTLIGHT) - CL_NewDlight (j+1, org[0], org[1], org[2] + 16, 400 + (rand()&31), 0.1, 0); - else if (state->effects & EF_DIMLIGHT) - CL_NewDlight (j+1, org[0], org[1], org[2], 200 + (rand()&31), 0.1, 0); - } -#ifdef GLQUAKE - } -#endif - - // the player object never gets added - if (j == cl.playernum) - continue; - - if (!state->modelindex) - continue; - - if (cl_deadbodyfilter.value && state->modelindex == cl_playerindex - && ( (i=state->frame)==49 || i==60 || i==69 || i==84 || i==93 || i==102) ) - continue; - - if (!Cam_DrawPlayer(j)) - continue; - - // grab an entity to fill in - if (cl_numvisedicts == MAX_VISEDICTS) - break; // object list is full - ent = &cl_visedicts[cl_numvisedicts]; - cl_numvisedicts++; - ent->keynum = 0; - - ent->model = cl.model_precache[state->modelindex]; - ent->skinnum = state->skinnum; - ent->frame = state->frame; - ent->colormap = info->translations; - if (state->modelindex == cl_playerindex) - ent->scoreboard = info; // use custom skin - else - ent->scoreboard = NULL; - - // - // angles - // - ent->angles[PITCH] = -state->viewangles[PITCH]/3; - ent->angles[YAW] = state->viewangles[YAW]; - ent->angles[ROLL] = 0; - ent->angles[ROLL] = V_CalcRoll (ent->angles, state->velocity)*4; - - // only predict half the move to minimize overruns - msec = 500*(playertime - state->state_time); - if (msec <= 0 || (!cl_predict_players.value && !cl_predict_players2.value)) - { - VectorCopy (state->origin, ent->origin); -//Con_DPrintf ("nopredict\n"); - } - else - { - // predict players movement - if (msec > 255) - msec = 255; - state->command.msec = msec; -//Con_DPrintf ("predict: %i\n", msec); - - oldphysent = pmove.numphysent; - CL_SetSolidPlayers (j); - CL_PredictUsercmd (state, &exact, &state->command, false); - pmove.numphysent = oldphysent; - VectorCopy (exact.origin, ent->origin); - } - - if (state->effects & EF_FLAG1) - CL_AddFlagModels (ent, 0); - else if (state->effects & EF_FLAG2) - CL_AddFlagModels (ent, 1); - - } -} - -//====================================================================== - -/* -=============== -CL_SetSolid - -Builds all the pmove physents for the current frame -=============== -*/ -void CL_SetSolidEntities (void) -{ - int i; - frame_t *frame; - packet_entities_t *pak; - entity_state_t *state; - - pmove.physents[0].model = cl.worldmodel; - VectorCopy (vec3_origin, pmove.physents[0].origin); - pmove.physents[0].info = 0; - pmove.numphysent = 1; - - frame = &cl.frames[parsecountmod]; - pak = &frame->packet_entities; - - for (i=0 ; inum_entities ; i++) - { - state = &pak->entities[i]; - - if (!state->modelindex) - continue; - if (!cl.model_precache[state->modelindex]) - continue; - if (pmove.numphysent + 1 == MAX_PHYSENTS) - continue; - - if ( cl.model_precache[state->modelindex]->hulls[1].firstclipnode - || cl.model_precache[state->modelindex]->clipbox ) - { - pmove.physents[pmove.numphysent].model = cl.model_precache[state->modelindex]; - VectorCopy (state->origin, pmove.physents[pmove.numphysent].origin); - pmove.numphysent++; - } - } - -} - -static float timediff; -extern float nextdemotime; - -static float adjustangle(float current, float ideal, float fraction) -{ - float move; - - move = ideal - current; - if (ideal > current) - { - - if (move >= 180) - move = move - 360; - } - else - { - if (move <= -180) - move = move + 360; - } - - move *= fraction; - - return (current + move); -} - -#define ISDEAD(i) ( (i) >=41 && (i) <=102 ) - -void CL_Interpolate(void) -{ - int i, j; - frame_t *frame, *oldframe; - struct predicted_player *pplayer; - player_state_t *state, *oldstate; - float f; - - if (!cl.validsequence || !timediff) - return; - - frame = &cl.frames[cl.parsecount&UPDATE_MASK]; - oldframe = &cl.frames[(cl.oldparsecount)&UPDATE_MASK]; - - f = (realtime - nextdemotime)/(timediff); - if (f > 0.0) - f = 0.0; - if (f < -1.0) - f = -1.0; - - // interpolate entities - for (i=0; i < frame->packet_entities.num_entities; i++) - { - if (!cl.int_entities[i].interpolate) - continue; - - for (j=0;j<3;j++) { - frame->packet_entities.entities[i].origin[j] = cl.int_entities[i].origin[j] + f*(cl.int_entities[i].origin[j] - oldframe->packet_entities.entities[cl.int_entities[i].oldindex].origin[j]); - frame->packet_entities.entities[i].angles[j] = adjustangle(oldframe->packet_entities.entities[cl.int_entities[i].oldindex].angles[j], cl.int_entities[i].angles[j],1.0+f); - } - } - - // interpolate nails - for (i=0; i < cl_num_projectiles; i++) - { - if (!cl.int_projectiles[i].interpolate) - continue; - - if (!cl_oldprojectiles[cl.int_projectiles[i].oldindex].origin[0]) - Con_Printf("%d %d, %d, %d\n", i, cl.int_projectiles[i].oldindex, cl_projectiles[i].num, i >= cl_num_oldprojectiles); - for (j=0;j<3;j++) { - cl_projectiles[i].origin[j] = cl.int_projectiles[i].origin[j] + f*(cl.int_projectiles[i].origin[j] - cl_oldprojectiles[cl.int_projectiles[i].oldindex].origin[j]); - } - } - - // interpolate clients - for (i=0, pplayer = predicted_players, state=frame->playerstate, oldstate=oldframe->playerstate; - i < MAX_CLIENTS; - i++, pplayer++, state++, oldstate++) - { - if (pplayer->predict) - { - for (j=0;j<3;j++) { - state->viewangles[j] = adjustangle(oldstate->command.angles[j], pplayer->olda[j], 1.0+f); - state->origin[j] = pplayer->oldo[j] + f*(pplayer->oldo[j]-oldstate->origin[j]); - } - if (i == cl.playernum) { - VectorCopy(state->viewangles, cl.viewangles); - } - } - } -} -int fixangle; - -void CL_InitInterpolation(float next, float old) -{ - float f; - int i; - struct predicted_player *pplayer; - frame_t *frame, *oldframe; - player_state_t *state, *oldstate; - vec3_t dist; - - if (!cl.validsequence) - return; - - timediff = next-old; - - frame = &cl.frames[cl.parsecount&UPDATE_MASK]; - oldframe = &cl.frames[(cl.oldparsecount)&UPDATE_MASK]; - - if (!timediff) { - return; - } - - f = (realtime - nextdemotime)/(timediff); - if (f > 0.0) - f = 0.0; - if (f < -1.0) - f = -1.0; - - // clients - for (i=0, pplayer = predicted_players, state=frame->playerstate, oldstate=oldframe->playerstate; - i < MAX_CLIENTS; - i++, pplayer++, state++, oldstate++) { - - if (pplayer->predict) - { - VectorCopy(pplayer->oldo, oldstate->origin); - VectorCopy(pplayer->olda, oldstate->command.angles); - } - - pplayer->predict = false; - - if (fixangle & 1 << i) - { - if (i == Cam_TrackNum()) { - VectorCopy(cl.viewangles, state->command.angles); - VectorCopy(cl.viewangles, state->viewangles); - } - - // no angle interpolation - VectorCopy(state->command.angles, oldstate->command.angles); - - fixangle &= ~(1 << i); - //continue; - } - - if (state->messagenum != cl.parsecount) { - continue; // not present this frame - } - - if (oldstate->messagenum != cl.oldparsecount || !oldstate->messagenum) { - continue; // not present last frame - } - - // we dont interpolate ourself if we are spectating - if (i == cl.playernum) { - if (cl.spectator) - continue; - } - - if (!ISDEAD(state->frame) && ISDEAD(oldstate->frame)) - continue; - - VectorSubtract(state->origin, oldstate->origin, dist); - if (Length(dist) > 150) - continue; - - VectorCopy(state->origin, pplayer->oldo); - VectorCopy(state->command.angles, pplayer->olda); - - pplayer->oldstate = oldstate; - pplayer->predict = true; - } - - // entities - for (i=0; i < frame->packet_entities.num_entities; i++) - { - if (!cl.int_entities[i].interpolate) - continue; - - VectorCopy(frame->packet_entities.entities[i].origin, cl.int_entities[i].origin); - VectorCopy(frame->packet_entities.entities[i].angles, cl.int_entities[i].angles); - } - - // nails - for (i=0; i < cl_num_projectiles; i++) { - if (!cl.int_projectiles[i].interpolate) - continue; - - VectorCopy(cl.int_projectiles[i].origin, cl_projectiles[i].origin); - } -} - -void CL_ClearPredict(void) -{ - memset(predicted_players, 0, sizeof(predicted_players)); - fixangle = 0; -} - -/* -=== -Calculate the new position of players, without other player clipping - -We do this to set up real player prediction. -Players are predicted twice, first without clipping other players, -then with clipping against them. -This sets up the first phase. -=== -*/ -void CL_SetUpPlayerPrediction(qboolean dopred) -{ - int j; - player_state_t *state, *oldstate; - player_state_t exact; - double playertime; - int msec; - frame_t *frame, *oldframe; - struct predicted_player *pplayer; - - playertime = realtime - cls.latency + 0.02; - if (playertime > realtime) - playertime = realtime; - - frame = &cl.frames[cl.parsecount&UPDATE_MASK]; - oldframe = &cl.frames[(cl.oldparsecount)&UPDATE_MASK]; - - for (j=0, pplayer = predicted_players, state=frame->playerstate, oldstate=oldframe->playerstate; - j < MAX_CLIENTS; - j++, pplayer++, state++, oldstate++) { - - pplayer->active = false; - - if (state->messagenum != cl.parsecount) - continue; // not present this frame - - if (!state->modelindex) - continue; - - pplayer->active = true; - pplayer->flags = state->flags; - - - // note that the local player is special, since he moves locally - // we use his last predicted postition - if (j == cl.playernum) { - VectorCopy(cl.frames[cls.netchan.outgoing_sequence&UPDATE_MASK].playerstate[cl.playernum].origin, - pplayer->origin); - } else { - // only predict half the move to minimize overruns - msec = 500*(playertime - state->state_time); - if (msec <= 0 || - (!cl_predict_players.value && !cl_predict_players2.value) || - !dopred || cls.demoplayback2) - { - - VectorCopy (state->origin, pplayer->origin); - } - else - { - // predict players movement - if (msec > 255) - msec = 255; - state->command.msec = msec; - //Con_DPrintf ("predict: %i\n", msec); - - CL_PredictUsercmd (state, &exact, &state->command, false); - VectorCopy (exact.origin, pplayer->origin); - } - - } - } -} - -/* -=============== -CL_SetSolid - -Builds all the pmove physents for the current frame -Note that CL_SetUpPlayerPrediction() must be called first! -pmove must be setup with world and solid entity hulls before calling -(via CL_PredictMove) -=============== -*/ -void CL_SetSolidPlayers (int playernum) -{ - int j; - extern vec3_t player_mins; - extern vec3_t player_maxs; - struct predicted_player *pplayer; - physent_t *pent; - - if (!cl_solid_players.value) - return; - - pent = pmove.physents + pmove.numphysent; - - for (j=0, pplayer = predicted_players; j < MAX_CLIENTS; j++, pplayer++) { - - if (!pplayer->active) - continue; // not present this frame - - // the player object never gets added - if (j == playernum) - continue; - - if (pplayer->flags & PF_DEAD) - continue; // dead players aren't solid - - if (pmove.numphysent + 1 == MAX_PHYSENTS) - continue; - - pent->model = 0; - VectorCopy(pplayer->origin, pent->origin); - VectorCopy(player_mins, pent->mins); - VectorCopy(player_maxs, pent->maxs); - pmove.numphysent++; - pent++; - } -} - - -/* -=============== -CL_EmitEntities - -Builds the visedicts array for cl.time - -Made up of: clients, packet_entities, nails, and tents -=============== -*/ -void CL_EmitEntities (void) -{ - static list_index = 0; - - if (cls.state != ca_active) - return; - if (!cl.validsequence) - return; - - // swap visedict lists - cl_oldnumvisedicts = cl_numvisedicts; - cl_oldvisedicts = cl_visedicts_list[list_index]; - list_index = 1 - list_index; - cl_visedicts = cl_visedicts_list[list_index]; - - cl_numvisedicts = 0; - - CL_LinkPlayers (); - CL_LinkPacketEntities (); - CL_LinkProjectiles (); - CL_UpdateTEnts (); -} - +/* +Copyright (C) 1996-1997 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. + +*/ +// cl_ents.c -- entity parsing and management + +#include "quakedef.h" +#include "pmove.h" +#include "teamplay.h" + +extern cvar_t cl_predict_players; +extern cvar_t cl_predict_players2; +extern cvar_t cl_solid_players; + +static struct predicted_player { + int flags; + qboolean active; + qboolean predict; + vec3_t origin; + vec3_t oldo; + vec3_t olda; + player_state_t *oldstate; +} predicted_players[MAX_CLIENTS]; + +//============================================================ + +/* +=============== +CL_AllocDlight + +=============== +*/ +dlight_t *CL_AllocDlight (int key) +{ + int i; + dlight_t *dl; + +// first look for an exact key match + if (key) + { + dl = cl_dlights; + for (i=0 ; ikey == key) + { + memset (dl, 0, sizeof(*dl)); + dl->key = key; + return dl; + } + } + } + +// then look for anything else + dl = cl_dlights; + for (i=0 ; idie < cl.time) + { + memset (dl, 0, sizeof(*dl)); + dl->key = key; + return dl; + } + } + + dl = &cl_dlights[0]; + memset (dl, 0, sizeof(*dl)); + dl->key = key; + return dl; +} + +/* +=============== +CL_NewDlight +=============== +*/ +void CL_NewDlight (int key, float x, float y, float z, float radius, float time, + int type) +{ + dlight_t *dl; + + dl = CL_AllocDlight (key); + dl->origin[0] = x; + dl->origin[1] = y; + dl->origin[2] = z; + dl->radius = radius; + dl->die = cl.time + time; + if (type == 0) { + dl->color[0] = 0.2; + dl->color[1] = 0.1; + dl->color[2] = 0.05; + dl->color[3] = 0.7; + } else if (type == 1) { + dl->color[0] = 0.05; + dl->color[1] = 0.05; + dl->color[2] = 0.3; + dl->color[3] = 0.7; + } else if (type == 2) { + dl->color[0] = 0.5; + dl->color[1] = 0.05; + dl->color[2] = 0.05; + dl->color[3] = 0.7; + } else if (type == 3) { + dl->color[0]=0.5; + dl->color[1] = 0.05; + dl->color[2] = 0.4; + dl->color[3] = 0.7; + } +} + + +/* +=============== +CL_DecayLights + +=============== +*/ +void CL_DecayLights (void) +{ + int i; + dlight_t *dl; + + dl = cl_dlights; + for (i=0 ; idie < cl.time || !dl->radius) + continue; + + dl->radius -= host_frametime*dl->decay; + if (dl->radius < 0) + dl->radius = 0; + } +} + + +/* +========================================================================= + +PACKET ENTITY PARSING / LINKING + +========================================================================= +*/ + +/* +================== +CL_ParseDelta + +Can go from either a baseline or a previous packet_entity +================== +*/ +int bitcounts[32]; /// just for protocol profiling +void CL_ParseDelta (entity_state_t *from, entity_state_t *to, int bits) +{ + int i; + + // set everything to the state we are delta'ing from + *to = *from; + + to->number = bits & 511; + bits &= ~511; + + if (bits & U_MOREBITS) + { // read in the low order bits + i = MSG_ReadByte (); + bits |= i; + } + + // count the bits for net profiling + for (i=0 ; i<16 ; i++) + if (bits&(1<flags = bits; + + if (bits & U_MODEL) + to->modelindex = MSG_ReadByte (); + + if (bits & U_FRAME) + to->frame = MSG_ReadByte (); + + if (bits & U_COLORMAP) + to->colormap = MSG_ReadByte(); + + if (bits & U_SKIN) + to->skinnum = MSG_ReadByte(); + + if (bits & U_EFFECTS) + to->effects = MSG_ReadByte(); + + if (bits & U_ORIGIN1) + to->origin[0] = MSG_ReadCoord (); + + if (bits & U_ANGLE1) + to->angles[0] = MSG_ReadAngle(); + + if (bits & U_ORIGIN2) + to->origin[1] = MSG_ReadCoord (); + + if (bits & U_ANGLE2) + to->angles[1] = MSG_ReadAngle(); + + if (bits & U_ORIGIN3) + to->origin[2] = MSG_ReadCoord (); + + if (bits & U_ANGLE3) + to->angles[2] = MSG_ReadAngle(); + + if (bits & U_SOLID) + { + // FIXME + } +} + + +/* +================= +FlushEntityPacket +================= +*/ +void FlushEntityPacket (void) +{ + int word; + entity_state_t olde, newe; + + Con_DPrintf ("FlushEntityPacket\n"); + + + memset (&olde, 0, sizeof(olde)); + + cl.validsequence = 0; // can't render a frame + cl.frames[cls.netchan.incoming_sequence&UPDATE_MASK].invalid = true; + + // read it all, but ignore it + while (1) + { + word = (unsigned short)MSG_ReadShort (); + if (msg_badread) + { // something didn't parse right... + Host_EndGame ("msg_badread in packetentities"); + return; + } + + if (!word) + break; // done + + CL_ParseDelta (&olde, &newe, word); + } +} + +void CL_InitEntInter(void) +{ + int i; + interpolate_t *ient; + packet_entities_t *pack; + entity_state_t *ent; + + pack = &cl.frames[cl.int_packet].packet_entities; + + for (i=0, ient = cl.int_entities, ent = pack->entities; i < pack->num_entities; i++, ent++, ient++) + { + if (!ient->interpolate) + continue; + + VectorCopy(ient->origin, ent->origin); + VectorCopy(ient->angles, ent->angles); + ient->interpolate = false; + } +} + +/* +================== +CL_ParsePacketEntities + +An svc_packetentities has just been parsed, deal with the +rest of the data stream. +================== +*/ +void CL_ParsePacketEntities (qboolean delta) +{ + int newpacket; + packet_entities_t *oldp, *newp, dummy; + int oldindex, newindex; + int word, newnum, oldnum; + qboolean full; + byte from; + int oldpacket; + + newpacket = cls.netchan.incoming_sequence&UPDATE_MASK; + newp = &cl.frames[newpacket].packet_entities; + cl.frames[newpacket].invalid = false; + + CL_InitEntInter(); + + if (delta) + { + from = MSG_ReadByte (); + + oldpacket = cl.frames[newpacket].delta_sequence; + if (cls.demoplayback2) + from = oldpacket = (cls.netchan.incoming_sequence-1); + + //Con_Printf("old:%d from:%d, new:%d, prev:%d\n", oldpacket, from, newpacket, (cls.netchan.incoming_sequence-1)&255); + + if (cls.netchan.outgoing_sequence - cls.netchan.incoming_sequence >= UPDATE_BACKUP-1) + { // there are no valid frames left, so drop it + FlushEntityPacket (); + return; + } + + if ( (from&UPDATE_MASK) != (oldpacket&UPDATE_MASK) ) + Con_DPrintf ("WARNING: from mismatch\n"); + + if (cls.netchan.outgoing_sequence - oldpacket >= UPDATE_BACKUP-1) + { // we can't use this, it is too old + FlushEntityPacket (); + return; + } + + cl.validsequence = cls.netchan.incoming_sequence; + oldp = &cl.frames[oldpacket&UPDATE_MASK].packet_entities; + full = false; + cl.int_packet = newpacket; + } + else + { // this is a full update that we can start delta compressing from now + oldp = &dummy; + dummy.num_entities = 0; + cl.validsequence = cls.netchan.incoming_sequence; + full = true; + } + + oldindex = 0; + newindex = 0; + newp->num_entities = 0; + + while (1) + { + word = (unsigned short)MSG_ReadShort (); + if (msg_badread) + { // something didn't parse right... + Host_EndGame ("msg_badread in packetentities"); + return; + } + + if (!word) + { + while (oldindex < oldp->num_entities) + { // copy all the rest of the entities from the old packet +//Con_Printf ("copy %i\n", oldp->entities[oldindex].number); + if (newindex >= MAX_PACKET_ENTITIES) + Host_EndGame ("CL_ParsePacketEntities: newindex == MAX_PACKET_ENTITIES"); + newp->entities[newindex] = oldp->entities[oldindex]; + newindex++; + oldindex++; + } + break; + } + newnum = word&511; + oldnum = oldindex >= oldp->num_entities ? 9999 : oldp->entities[oldindex].number; + + while (newnum > oldnum) + { + if (full) + { + Con_Printf ("WARNING: oldcopy on full update"); + FlushEntityPacket (); + return; + } + +//Con_Printf ("copy %i\n", oldnum); + // copy one of the old entities over to the new packet unchanged + if (newindex >= MAX_PACKET_ENTITIES) + Host_EndGame ("CL_ParsePacketEntities: newindex == MAX_PACKET_ENTITIES"); + newp->entities[newindex] = oldp->entities[oldindex]; + newindex++; + oldindex++; + oldnum = oldindex >= oldp->num_entities ? 9999 : oldp->entities[oldindex].number; + } + + if (newnum < oldnum) + { // new from baseline +//Con_Printf ("baseline %i\n", newnum); + if (word & U_REMOVE) + { + if (full) + { + cl.validsequence = 0; + Con_Printf ("WARNING: U_REMOVE on full update\n"); + FlushEntityPacket (); + return; + } + continue; + } + if (newindex >= MAX_PACKET_ENTITIES) + Host_EndGame ("CL_ParsePacketEntities: newindex == MAX_PACKET_ENTITIES"); + CL_ParseDelta (&cl_baselines[newnum], &newp->entities[newindex], word); + newindex++; + continue; + } + + if (newnum == oldnum) + { // delta from previous + if (full) + { + cl.validsequence = 0; + Con_Printf ("WARNING: delta on full update"); + } + if (word & U_REMOVE) + { + oldindex++; + continue; + } +//Con_Printf ("delta %i\n",newnum); + CL_ParseDelta (&oldp->entities[oldindex], &newp->entities[newindex], word); + cl.int_entities[newindex].interpolate = true; + cl.int_entities[newindex].oldindex = oldindex; + + newindex++; + oldindex++; + } + + } + + newp->num_entities = newindex; +} + + +extern int cl_playerindex; +extern int cl_h_playerindex, cl_gib1index, cl_gib2index, cl_gib3index; +extern int cl_rocketindex, cl_grenadeindex; + +/* +=============== +CL_LinkPacketEntities + +=============== +*/ +void CL_LinkPacketEntities (void) +{ + entity_t *ent; + packet_entities_t *pack; + entity_state_t *s1, *s2; + float f; + model_t *model; + vec3_t old_origin; + float autorotate; + int i; + int pnum; + dlight_t *dl; + + pack = &cl.frames[cls.netchan.incoming_sequence&UPDATE_MASK].packet_entities; + + autorotate = anglemod(100*cl.time); + + f = 0; // FIXME: no interpolation right now + + for (pnum=0 ; pnumnum_entities ; pnum++) + { + s1 = &pack->entities[pnum]; + s2 = s1; // FIXME: no interpolation right now + + // control powerup glow for bots + if (s1->modelindex != cl_playerindex || r_powerupglow.value) + { + // spawn light flashes, even ones coming from invisible objects + if ((s1->effects & (EF_BLUE | EF_RED)) == (EF_BLUE | EF_RED)) + CL_NewDlight (s1->number, s1->origin[0], s1->origin[1], s1->origin[2], 200 + (rand()&31), 0.1, 3); + else if (s1->effects & EF_BLUE) + CL_NewDlight (s1->number, s1->origin[0], s1->origin[1], s1->origin[2], 200 + (rand()&31), 0.1, 1); + else if (s1->effects & EF_RED) + CL_NewDlight (s1->number, s1->origin[0], s1->origin[1], s1->origin[2], 200 + (rand()&31), 0.1, 2); + else if (s1->effects & EF_BRIGHTLIGHT) + CL_NewDlight (s1->number, s1->origin[0], s1->origin[1], s1->origin[2] + 16, 400 + (rand()&31), 0.1, 0); + else if (s1->effects & EF_DIMLIGHT) + CL_NewDlight (s1->number, s1->origin[0], s1->origin[1], s1->origin[2], 200 + (rand()&31), 0.1, 0); + } + + if (cl_deadbodyfilter.value && s1->modelindex == cl_playerindex + && ( (i=s1->frame)==49 || i==60 || i==69 || i==84 || i==93 || i==102) ) + continue; + + if (cl_gibfilter.value) + if (s1->modelindex == cl_h_playerindex || s1->modelindex == cl_gib1index + || s1->modelindex == cl_gib2index || s1->modelindex == cl_gib3index) + continue; + + if (cl_rocket2grenade.value && cl_grenadeindex != -1) + if (s1->modelindex == cl_rocketindex) + s1->modelindex = cl_grenadeindex; + + // if set to invisible, skip + if (!s1->modelindex) + continue; + + // create a new entity + if (cl_numvisedicts == MAX_VISEDICTS) + break; // object list is full + + ent = &cl_visedicts[cl_numvisedicts]; + cl_numvisedicts++; + + ent->keynum = s1->number; + ent->model = model = cl.model_precache[s1->modelindex]; + + // set colormap + if (s1->colormap && (s1->colormap < MAX_CLIENTS) + && !strcmp(ent->model->name,"progs/player.mdl") ) + { + ent->colormap = cl.players[s1->colormap-1].translations; + ent->scoreboard = &cl.players[s1->colormap-1]; + } + else + { + ent->colormap = vid.colormap; + ent->scoreboard = NULL; + } + + // set skin + ent->skinnum = s1->skinnum; + + // set frame + ent->frame = s1->frame; + + // rotate binary objects locally + if (model->flags & EF_ROTATE) + { + ent->angles[0] = 0; + ent->angles[1] = autorotate; + ent->angles[2] = 0; + } + else + { + float a1, a2; + + for (i=0 ; i<3 ; i++) + { + a1 = s1->angles[i]; + a2 = s2->angles[i]; + if (a1 - a2 > 180) + a1 -= 360; + if (a1 - a2 < -180) + a1 += 360; + ent->angles[i] = a2 + f * (a1 - a2); + } + } + + // calculate origin + for (i=0 ; i<3 ; i++) + ent->origin[i] = s2->origin[i] + + f * (s1->origin[i] - s2->origin[i]); + + // add automatic particle trails + if (!model->flags) + continue; + + // scan the old entity display list for a matching + for (i=0 ; ikeynum) + { + VectorCopy (cl_oldvisedicts[i].origin, old_origin); + break; + } + } + if (i == cl_oldnumvisedicts) + continue; // not in last message + + for (i=0 ; i<3 ; i++) + if ( abs(old_origin[i] - ent->origin[i]) > 128) + { // no trail if too far + VectorCopy (ent->origin, old_origin); + break; + } + if (model->flags & EF_ROCKET) + { + if (r_rockettrail.value) { + if (r_rockettrail.value == 2) + R_RocketTrail (old_origin, ent->origin, 1); + else + R_RocketTrail (old_origin, ent->origin, 0); + } + + if (r_rocketlight.value) { + dl = CL_AllocDlight (s1->number); + VectorCopy (ent->origin, dl->origin); + dl->radius = 200; + dl->die = cl.time + 0.1; + } + } + else if (model->flags & EF_GRENADE && r_grenadetrail.value) + R_RocketTrail (old_origin, ent->origin, 1); + else if (model->flags & EF_GIB) + R_RocketTrail (old_origin, ent->origin, 2); + else if (model->flags & EF_ZOMGIB) + R_RocketTrail (old_origin, ent->origin, 4); + else if (model->flags & EF_TRACER) + R_RocketTrail (old_origin, ent->origin, 3); + else if (model->flags & EF_TRACER2) + R_RocketTrail (old_origin, ent->origin, 5); + else if (model->flags & EF_TRACER3) + R_RocketTrail (old_origin, ent->origin, 6); + } +} + + +/* +========================================================================= + +PROJECTILE PARSING / LINKING + +========================================================================= +*/ + +typedef struct +{ + int modelindex; + vec3_t origin; + vec3_t angles; + int num; +} projectile_t; + +projectile_t cl_projectiles[MAX_PROJECTILES], cl_oldprojectiles[MAX_PROJECTILES]; +int cl_num_projectiles, cl_num_oldprojectiles; + +extern int cl_spikeindex; + +void CL_ClearProjectiles (void) +{ + static int parsecount = 0; + + if (parsecount == cl.parsecount) + return; + + parsecount = cl.parsecount; + + memset(cl.int_projectiles, 0, sizeof(cl.int_projectiles)); + + cl_num_oldprojectiles = cl_num_projectiles; + memcpy(cl_oldprojectiles, cl_projectiles, sizeof(cl_projectiles)); + cl_num_projectiles = 0; +} + +/* +===================== +CL_ParseProjectiles + +Nails are passed as efficient temporary entities +===================== +*/ +void CL_ParseProjectiles (qboolean nail2) +{ + int i, c, j, num; + byte bits[6]; + projectile_t *pr; + interpolate_t *inter; + + c = MSG_ReadByte (); + for (i=0 ; imodelindex = cl_spikeindex; + pr->origin[0] = ( ( bits[0] + ((bits[1]&15)<<8) ) <<1) - 4096; + pr->origin[1] = ( ( (bits[1]>>4) + (bits[2]<<4) ) <<1) - 4096; + pr->origin[2] = ( ( bits[3] + ((bits[4]&15)<<8) ) <<1) - 4096; + pr->angles[0] = 360*(bits[4]>>4)/16; + pr->angles[1] = 360*bits[5]/256; + pr->num = num; + if (!num) + continue; + + for (j = 0; j < cl_num_oldprojectiles; j++) + if (cl_oldprojectiles[j].num == num) + { + inter->interpolate = true; + inter->oldindex = j; + VectorCopy(pr->origin, inter->origin); + if (!cl_oldprojectiles[j].origin[0]) + Con_Printf("parse:%d, %d, %d\n", j, num, cl_oldprojectiles[j].num); + break; + } + } +} + +/* +============= +CL_LinkProjectiles + +============= +*/ +void CL_LinkProjectiles (void) +{ + int i; + projectile_t *pr; + entity_t *ent; + + for (i=0, pr=cl_projectiles ; ikeynum = 0; + + if (pr->modelindex < 1) + continue; + ent->model = cl.model_precache[pr->modelindex]; + ent->skinnum = 0; + ent->frame = 0; + ent->colormap = vid.colormap; + ent->scoreboard = NULL; + VectorCopy (pr->origin, ent->origin); + VectorCopy (pr->angles, ent->angles); + } +} + +//======================================== + +extern int cl_spikeindex, cl_playerindex, cl_flagindex; + +entity_t *CL_NewTempEntity (void); + +/* +=================== +CL_ParsePlayerinfo +=================== +*/ +extern int parsecountmod; +extern double parsecounttime; + +#define DF_ORIGIN 1 +#define DF_ANGLES (1<<3) +#define DF_EFFECTS (1<<6) +#define DF_SKINNUM (1<<7) +#define DF_DEAD (1<<8) +#define DF_GIB (1<<9) +#define DF_WEAPONFRAME (1<<10) +#define DF_MODEL (1<<11) + +int TranslateFlags(int src) +{ + int dst = 0; + + if (src & DF_EFFECTS) + dst |= PF_EFFECTS; + if (src & DF_SKINNUM) + dst |= PF_SKINNUM; + if (src & DF_DEAD) + dst |= PF_DEAD; + if (src & DF_GIB) + dst |= PF_GIB; + if (src & DF_WEAPONFRAME) + dst |= PF_WEAPONFRAME; + if (src & DF_MODEL) + dst |= PF_MODEL; + + return dst; +} + + +void CL_ParsePlayerinfo (void) +{ + int msec; + int flags; + player_info_t *info; + player_state_t *state, *prevstate, dummy; + int num; + int i; + + num = MSG_ReadByte (); + if (num >= MAX_CLIENTS) + Sys_Error ("CL_ParsePlayerinfo: bad num"); + + info = &cl.players[num]; + + memset(&dummy, 0, sizeof(dummy)); + + state = &cl.frames[parsecountmod].playerstate[num]; + if (info->prevcount > cl.parsecount || !cl.parsecount) { + prevstate = &dummy; + } else { + if (cl.parsecount - info->prevcount >= UPDATE_BACKUP-1) + prevstate = &dummy; + else + prevstate = &cl.frames[info->prevcount&UPDATE_MASK].playerstate[num]; + } + + info->prevcount = cl.parsecount; + + if (cls.demoplayback2) + { + if (cls.findtrack && info->stats[STAT_HEALTH] != 0) + { + extern int autocam; + extern int ideal_track; + + autocam = CAM_TRACK; + Cam_Lock(num); + ideal_track = num; + cls.findtrack = false; + + } + + memcpy(state, prevstate, sizeof(player_state_t)); + flags = MSG_ReadShort (); + state->flags = TranslateFlags(flags); + + Con_Printf("p:%d %d\n", cl.parsecount, num); + state->messagenum = cl.parsecount; + state->command.msec = 0; + + state->frame = MSG_ReadByte (); + + state->state_time = parsecounttime; + state->command.msec = 0; + + for (i=0; i <3; i++) + if (flags & (DF_ORIGIN << i)) + state->origin[i] = MSG_ReadCoord (); + + for (i=0; i <3; i++) + if (flags & (DF_ANGLES << i)) + state->command.angles[i] = MSG_ReadAngle16 (); + + + if (flags & DF_MODEL) + state->modelindex = MSG_ReadByte (); + + if (flags & DF_SKINNUM) + state->skinnum = MSG_ReadByte (); + + if (flags & DF_EFFECTS) + state->effects = MSG_ReadByte (); + + if (flags & DF_WEAPONFRAME) + state->weaponframe = MSG_ReadByte (); + + VectorCopy (state->command.angles, state->viewangles); + return; + } + + flags = state->flags = MSG_ReadShort (); + + state->messagenum = cl.parsecount; + state->origin[0] = MSG_ReadCoord (); + state->origin[1] = MSG_ReadCoord (); + state->origin[2] = MSG_ReadCoord (); + + state->frame = MSG_ReadByte (); + + // the other player's last move was likely some time + // before the packet was sent out, so accurately track + // the exact time it was valid at + if (flags & PF_MSEC) + { + msec = MSG_ReadByte (); + state->state_time = parsecounttime - msec*0.001; + } + else + state->state_time = parsecounttime; + + if (flags & PF_COMMAND) + MSG_ReadDeltaUsercmd (&nullcmd, &state->command); + + for (i=0 ; i<3 ; i++) + { + if (flags & (PF_VELOCITY1<velocity[i] = MSG_ReadShort(); + else + state->velocity[i] = 0; + } + if (flags & PF_MODEL) + state->modelindex = MSG_ReadByte (); + else + state->modelindex = cl_playerindex; + + if (flags & PF_SKINNUM) + state->skinnum = MSG_ReadByte (); + else + state->skinnum = 0; + + if (flags & PF_EFFECTS) + state->effects = MSG_ReadByte (); + else + state->effects = 0; + + if (flags & PF_WEAPONFRAME) + state->weaponframe = MSG_ReadByte (); + else + state->weaponframe = 0; + + VectorCopy (state->command.angles, state->viewangles); + +} + + +/* +================ +CL_AddFlagModels + +Called when the CTF flags are set +================ +*/ +void CL_AddFlagModels (entity_t *ent, int team) +{ + int i; + float f; + vec3_t v_forward, v_right, v_up; + entity_t *newent; + + if (cl_flagindex == -1) + return; + + f = 14; + if (ent->frame >= 29 && ent->frame <= 40) { + if (ent->frame >= 29 && ent->frame <= 34) { //axpain + if (ent->frame == 29) f = f + 2; + else if (ent->frame == 30) f = f + 8; + else if (ent->frame == 31) f = f + 12; + else if (ent->frame == 32) f = f + 11; + else if (ent->frame == 33) f = f + 10; + else if (ent->frame == 34) f = f + 4; + } else if (ent->frame >= 35 && ent->frame <= 40) { // pain + if (ent->frame == 35) f = f + 2; + else if (ent->frame == 36) f = f + 10; + else if (ent->frame == 37) f = f + 10; + else if (ent->frame == 38) f = f + 8; + else if (ent->frame == 39) f = f + 4; + else if (ent->frame == 40) f = f + 2; + } + } else if (ent->frame >= 103 && ent->frame <= 118) { + if (ent->frame >= 103 && ent->frame <= 104) f = f + 6; //nailattack + else if (ent->frame >= 105 && ent->frame <= 106) f = f + 6; //light + else if (ent->frame >= 107 && ent->frame <= 112) f = f + 7; //rocketattack + else if (ent->frame >= 112 && ent->frame <= 118) f = f + 7; //shotattack + } + + newent = CL_NewTempEntity (); + newent->model = cl.model_precache[cl_flagindex]; + newent->skinnum = team; + + AngleVectors (ent->angles, v_forward, v_right, v_up); + v_forward[2] = -v_forward[2]; // reverse z component + for (i=0 ; i<3 ; i++) + newent->origin[i] = ent->origin[i] - f*v_forward[i] + 22*v_right[i]; + newent->origin[2] -= 16; + + VectorCopy (ent->angles, newent->angles) + newent->angles[2] -= 45; +} + +/* +============= +CL_LinkPlayers + +Create visible entities in the correct position +for all current players +============= +*/ +void CL_LinkPlayers (void) +{ + int j; + player_info_t *info; + player_state_t *state; + player_state_t exact; + double playertime; + entity_t *ent; + int msec; + frame_t *frame; + int oldphysent; + vec3_t org; + int i; + + playertime = realtime - cls.latency + 0.02; + if (playertime > realtime) + playertime = realtime; + + frame = &cl.frames[cl.parsecount&UPDATE_MASK]; + + for (j=0, info=cl.players, state=frame->playerstate ; j < MAX_CLIENTS + ; j++, info++, state++) + { + if (state->messagenum != cl.parsecount) + continue; // not present this frame + + // spawn light flashes, even ones coming from invisible objects +#ifdef GLQUAKE + if (!gl_flashblend.value || j != cl.playernum) { +#endif + if (r_powerupglow.value && !(r_powerupglow.value == 2 && j == cl.playernum)) + { + if (j == cl.playernum) { + VectorCopy (cl.simorg, org); + } else + VectorCopy (state->origin, org); + + if ((state->effects & (EF_BLUE | EF_RED)) == (EF_BLUE | EF_RED)) + CL_NewDlight (j+1, org[0], org[1], org[2], 200 + (rand()&31), 0.1, 3); + else if (state->effects & EF_BLUE) + CL_NewDlight (j+1, org[0], org[1], org[2], 200 + (rand()&31), 0.1, 1); + else if (state->effects & EF_RED) + CL_NewDlight (j+1, org[0], org[1], org[2], 200 + (rand()&31), 0.1, 2); + else if (state->effects & EF_BRIGHTLIGHT) + CL_NewDlight (j+1, org[0], org[1], org[2] + 16, 400 + (rand()&31), 0.1, 0); + else if (state->effects & EF_DIMLIGHT) + CL_NewDlight (j+1, org[0], org[1], org[2], 200 + (rand()&31), 0.1, 0); + } +#ifdef GLQUAKE + } +#endif + + // the player object never gets added + if (j == cl.playernum) + continue; + + if (!state->modelindex) + continue; + + if (cl_deadbodyfilter.value && state->modelindex == cl_playerindex + && ( (i=state->frame)==49 || i==60 || i==69 || i==84 || i==93 || i==102) ) + continue; + + if (!Cam_DrawPlayer(j)) + continue; + + // grab an entity to fill in + if (cl_numvisedicts == MAX_VISEDICTS) + break; // object list is full + ent = &cl_visedicts[cl_numvisedicts]; + cl_numvisedicts++; + ent->keynum = 0; + + ent->model = cl.model_precache[state->modelindex]; + ent->skinnum = state->skinnum; + ent->frame = state->frame; + ent->colormap = info->translations; + if (state->modelindex == cl_playerindex) + ent->scoreboard = info; // use custom skin + else + ent->scoreboard = NULL; + + // + // angles + // + ent->angles[PITCH] = -state->viewangles[PITCH]/3; + ent->angles[YAW] = state->viewangles[YAW]; + ent->angles[ROLL] = 0; + ent->angles[ROLL] = V_CalcRoll (ent->angles, state->velocity)*4; + + // only predict half the move to minimize overruns + msec = 500*(playertime - state->state_time); + if (msec <= 0 || (!cl_predict_players.value && !cl_predict_players2.value)) + { + VectorCopy (state->origin, ent->origin); +//Con_DPrintf ("nopredict\n"); + } + else + { + // predict players movement + if (msec > 255) + msec = 255; + state->command.msec = msec; +//Con_DPrintf ("predict: %i\n", msec); + + oldphysent = pmove.numphysent; + CL_SetSolidPlayers (j); + CL_PredictUsercmd (state, &exact, &state->command, false); + pmove.numphysent = oldphysent; + VectorCopy (exact.origin, ent->origin); + } + + if (state->effects & EF_FLAG1) + CL_AddFlagModels (ent, 0); + else if (state->effects & EF_FLAG2) + CL_AddFlagModels (ent, 1); + + } +} + +//====================================================================== + +/* +=============== +CL_SetSolid + +Builds all the pmove physents for the current frame +=============== +*/ +void CL_SetSolidEntities (void) +{ + int i; + frame_t *frame; + packet_entities_t *pak; + entity_state_t *state; + + pmove.physents[0].model = cl.worldmodel; + VectorCopy (vec3_origin, pmove.physents[0].origin); + pmove.physents[0].info = 0; + pmove.numphysent = 1; + + frame = &cl.frames[parsecountmod]; + pak = &frame->packet_entities; + + for (i=0 ; inum_entities ; i++) + { + state = &pak->entities[i]; + + if (!state->modelindex) + continue; + if (!cl.model_precache[state->modelindex]) + continue; + if (pmove.numphysent + 1 == MAX_PHYSENTS) + continue; + + if ( cl.model_precache[state->modelindex]->hulls[1].firstclipnode + || cl.model_precache[state->modelindex]->clipbox ) + { + pmove.physents[pmove.numphysent].model = cl.model_precache[state->modelindex]; + VectorCopy (state->origin, pmove.physents[pmove.numphysent].origin); + pmove.numphysent++; + } + } + +} + +static float timediff; +extern float nextdemotime; + +static float adjustangle(float current, float ideal, float fraction) +{ + float move; + + move = ideal - current; + if (ideal > current) + { + + if (move >= 180) + move = move - 360; + } + else + { + if (move <= -180) + move = move + 360; + } + + move *= fraction; + + return (current + move); +} + +#define ISDEAD(i) ( (i) >=41 && (i) <=102 ) + +void CL_Interpolate(void) +{ + int i, j; + frame_t *frame, *oldframe; + struct predicted_player *pplayer; + player_state_t *state, *oldstate; + float f; + + if (!cl.validsequence || !timediff) + return; + + frame = &cl.frames[cl.parsecount&UPDATE_MASK]; + oldframe = &cl.frames[(cl.oldparsecount)&UPDATE_MASK]; + + f = (realtime - nextdemotime)/(timediff); + if (f > 0.0) + f = 0.0; + if (f < -1.0) + f = -1.0; + + // interpolate entities + for (i=0; i < frame->packet_entities.num_entities; i++) + { + if (!cl.int_entities[i].interpolate) + continue; + + for (j=0;j<3;j++) { + frame->packet_entities.entities[i].origin[j] = cl.int_entities[i].origin[j] + f*(cl.int_entities[i].origin[j] - oldframe->packet_entities.entities[cl.int_entities[i].oldindex].origin[j]); + frame->packet_entities.entities[i].angles[j] = adjustangle(oldframe->packet_entities.entities[cl.int_entities[i].oldindex].angles[j], cl.int_entities[i].angles[j],1.0+f); + } + } + + // interpolate nails + for (i=0; i < cl_num_projectiles; i++) + { + if (!cl.int_projectiles[i].interpolate) + continue; + + if (!cl_oldprojectiles[cl.int_projectiles[i].oldindex].origin[0]) + Con_Printf("%d %d, %d, %d\n", i, cl.int_projectiles[i].oldindex, cl_projectiles[i].num, i >= cl_num_oldprojectiles); + for (j=0;j<3;j++) { + cl_projectiles[i].origin[j] = cl.int_projectiles[i].origin[j] + f*(cl.int_projectiles[i].origin[j] - cl_oldprojectiles[cl.int_projectiles[i].oldindex].origin[j]); + } + } + + // interpolate clients + for (i=0, pplayer = predicted_players, state=frame->playerstate, oldstate=oldframe->playerstate; + i < MAX_CLIENTS; + i++, pplayer++, state++, oldstate++) + { + if (pplayer->predict) + { + for (j=0;j<3;j++) { + state->viewangles[j] = adjustangle(oldstate->command.angles[j], pplayer->olda[j], 1.0+f); + state->origin[j] = pplayer->oldo[j] + f*(pplayer->oldo[j]-oldstate->origin[j]); + } + if (i == cl.playernum) { + VectorCopy(state->viewangles, cl.viewangles); + } + } + } +} +int fixangle; + +void CL_InitInterpolation(float next, float old) +{ + float f; + int i; + struct predicted_player *pplayer; + frame_t *frame, *oldframe; + player_state_t *state, *oldstate; + vec3_t dist; + + if (!cl.validsequence) + return; + + timediff = next-old; + + frame = &cl.frames[cl.parsecount&UPDATE_MASK]; + oldframe = &cl.frames[(cl.oldparsecount)&UPDATE_MASK]; + + if (!timediff) { + return; + } + + f = (realtime - nextdemotime)/(timediff); + if (f > 0.0) + f = 0.0; + if (f < -1.0) + f = -1.0; + + // clients + for (i=0, pplayer = predicted_players, state=frame->playerstate, oldstate=oldframe->playerstate; + i < MAX_CLIENTS; + i++, pplayer++, state++, oldstate++) { + + if (pplayer->predict) + { + VectorCopy(pplayer->oldo, oldstate->origin); + VectorCopy(pplayer->olda, oldstate->command.angles); + } + + pplayer->predict = false; + + if (fixangle & 1 << i) + { + if (i == Cam_TrackNum()) { + VectorCopy(cl.viewangles, state->command.angles); + VectorCopy(cl.viewangles, state->viewangles); + } + + // no angle interpolation + VectorCopy(state->command.angles, oldstate->command.angles); + + fixangle &= ~(1 << i); + //continue; + } + + if (state->messagenum != cl.parsecount) { + continue; // not present this frame + } + + if (oldstate->messagenum != cl.oldparsecount || !oldstate->messagenum) { + continue; // not present last frame + } + + // we dont interpolate ourself if we are spectating + if (i == cl.playernum) { + if (cl.spectator) + continue; + } + + if (!ISDEAD(state->frame) && ISDEAD(oldstate->frame)) + continue; + + VectorSubtract(state->origin, oldstate->origin, dist); + if (Length(dist) > 150) + continue; + + VectorCopy(state->origin, pplayer->oldo); + VectorCopy(state->command.angles, pplayer->olda); + + pplayer->oldstate = oldstate; + pplayer->predict = true; + } + + // entities + for (i=0; i < frame->packet_entities.num_entities; i++) + { + if (!cl.int_entities[i].interpolate) + continue; + + VectorCopy(frame->packet_entities.entities[i].origin, cl.int_entities[i].origin); + VectorCopy(frame->packet_entities.entities[i].angles, cl.int_entities[i].angles); + } + + // nails + for (i=0; i < cl_num_projectiles; i++) { + if (!cl.int_projectiles[i].interpolate) + continue; + + VectorCopy(cl.int_projectiles[i].origin, cl_projectiles[i].origin); + } +} + +void CL_ClearPredict(void) +{ + memset(predicted_players, 0, sizeof(predicted_players)); + fixangle = 0; +} + +/* +=== +Calculate the new position of players, without other player clipping + +We do this to set up real player prediction. +Players are predicted twice, first without clipping other players, +then with clipping against them. +This sets up the first phase. +=== +*/ +void CL_SetUpPlayerPrediction(qboolean dopred) +{ + int j; + player_state_t *state; + player_state_t exact; + double playertime; + int msec; + frame_t *frame; + struct predicted_player *pplayer; + + playertime = realtime - cls.latency + 0.02; + if (playertime > realtime) + playertime = realtime; + + frame = &cl.frames[cl.parsecount&UPDATE_MASK]; + + for (j=0, pplayer = predicted_players, state=frame->playerstate; + j < MAX_CLIENTS; + j++, pplayer++, state++) { + + pplayer->active = false; + + if (state->messagenum != cl.parsecount) + continue; // not present this frame + + if (!state->modelindex) + continue; + + pplayer->active = true; + pplayer->flags = state->flags; + + + // note that the local player is special, since he moves locally + // we use his last predicted postition + if (j == cl.playernum) { + VectorCopy(cl.frames[cls.netchan.outgoing_sequence&UPDATE_MASK].playerstate[cl.playernum].origin, + pplayer->origin); + } else { + // only predict half the move to minimize overruns + msec = 500*(playertime - state->state_time); + if (msec <= 0 || + (!cl_predict_players.value && !cl_predict_players2.value) || + !dopred || cls.demoplayback2) + { + + VectorCopy (state->origin, pplayer->origin); + } + else + { + // predict players movement + if (msec > 255) + msec = 255; + state->command.msec = msec; + //Con_DPrintf ("predict: %i\n", msec); + + CL_PredictUsercmd (state, &exact, &state->command, false); + VectorCopy (exact.origin, pplayer->origin); + } + + } + } +} + +/* +=============== +CL_SetSolid + +Builds all the pmove physents for the current frame +Note that CL_SetUpPlayerPrediction() must be called first! +pmove must be setup with world and solid entity hulls before calling +(via CL_PredictMove) +=============== +*/ +void CL_SetSolidPlayers (int playernum) +{ + int j; + extern vec3_t player_mins; + extern vec3_t player_maxs; + struct predicted_player *pplayer; + physent_t *pent; + + if (!cl_solid_players.value) + return; + + pent = pmove.physents + pmove.numphysent; + + for (j=0, pplayer = predicted_players; j < MAX_CLIENTS; j++, pplayer++) { + + if (!pplayer->active) + continue; // not present this frame + + // the player object never gets added + if (j == playernum) + continue; + + if (pplayer->flags & PF_DEAD) + continue; // dead players aren't solid + + if (pmove.numphysent + 1 == MAX_PHYSENTS) + continue; + + pent->model = 0; + VectorCopy(pplayer->origin, pent->origin); + VectorCopy(player_mins, pent->mins); + VectorCopy(player_maxs, pent->maxs); + pmove.numphysent++; + pent++; + } +} + + +/* +=============== +CL_EmitEntities + +Builds the visedicts array for cl.time + +Made up of: clients, packet_entities, nails, and tents +=============== +*/ +void CL_EmitEntities (void) +{ + static list_index = 0; + + if (cls.state != ca_active) + return; + if (!cl.validsequence) + return; + + // swap visedict lists + cl_oldnumvisedicts = cl_numvisedicts; + cl_oldvisedicts = cl_visedicts_list[list_index]; + list_index = 1 - list_index; + cl_visedicts = cl_visedicts_list[list_index]; + + cl_numvisedicts = 0; + + CL_LinkPlayers (); + CL_LinkPacketEntities (); + CL_LinkProjectiles (); + CL_UpdateTEnts (); +} + diff --git a/source/cl_input.c b/source/cl_input.c index 863c0750..616b2ccc 100644 --- a/source/cl_input.c +++ b/source/cl_input.c @@ -1,643 +1,643 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// cl.input.c -- builds an intended movement command to send to the server - -#include "quakedef.h" - -cvar_t cl_nodelta = {"cl_nodelta","0"}; -cvar_t cl_c2spps = {"cl_c2spps","0"}; // Tonik - -/* -=============================================================================== - -KEY BUTTONS - -Continuous button event tracking is complicated by the fact that two different -input sources (say, mouse button 1 and the control key) can both press the -same button, but the button should only be released when both of the -pressing key have been released. - -When a key event issues a button command (+forward, +attack, etc), it appends -its key number as a parameter to the command so it can be matched up with -the release. - -state bit 0 is the current state of the key -state bit 1 is edge triggered on the up to down transition -state bit 2 is edge triggered on the down to up transition - -=============================================================================== -*/ - - -kbutton_t in_mlook, in_klook; -kbutton_t in_left, in_right, in_forward, in_back; -kbutton_t in_lookup, in_lookdown, in_moveleft, in_moveright; -kbutton_t in_strafe, in_speed, in_use, in_jump, in_attack; -kbutton_t in_up, in_down; - -int in_impulse; - - -void KeyDown (kbutton_t *b) -{ - int k; - char *c; - - c = Cmd_Argv(1); - if (c[0]) - k = atoi(c); - else - k = -1; // typed manually at the console for continuous down - - if (k == b->down[0] || k == b->down[1]) - return; // repeating key - - if (!b->down[0]) - b->down[0] = k; - else if (!b->down[1]) - b->down[1] = k; - else - { - Con_Printf ("Three keys down for a button!\n"); - return; - } - - if (b->state & 1) - return; // still down - b->state |= 1 + 2; // down + impulse down -} - -void KeyUp (kbutton_t *b) -{ - int k; - char *c; - - c = Cmd_Argv(1); - if (c[0]) - k = atoi(c); - else - { // typed manually at the console, assume for unsticking, so clear all - b->down[0] = b->down[1] = 0; - b->state = 4; // impulse up - return; - } - - if (b->down[0] == k) - b->down[0] = 0; - else if (b->down[1] == k) - b->down[1] = 0; - else - return; // key up without coresponding down (menu pass through) - if (b->down[0] || b->down[1]) - return; // some other key is still holding it down - - if (!(b->state & 1)) - return; // still up (this should not happen) - b->state &= ~1; // now up - b->state |= 4; // impulse up -} - -void IN_KLookDown (void) {KeyDown(&in_klook);} -void IN_KLookUp (void) {KeyUp(&in_klook);} -void IN_MLookDown (void) {KeyDown(&in_mlook);} -void IN_MLookUp (void) { -KeyUp(&in_mlook); -if ( !(in_mlook.state&1) && lookspring.value) - V_StartPitchDrift(); -} -void IN_UpDown(void) {KeyDown(&in_up);} -void IN_UpUp(void) {KeyUp(&in_up);} -void IN_DownDown(void) {KeyDown(&in_down);} -void IN_DownUp(void) {KeyUp(&in_down);} -void IN_LeftDown(void) {KeyDown(&in_left);} -void IN_LeftUp(void) {KeyUp(&in_left);} -void IN_RightDown(void) {KeyDown(&in_right);} -void IN_RightUp(void) {KeyUp(&in_right);} -void IN_ForwardDown(void) {KeyDown(&in_forward);} -void IN_ForwardUp(void) {KeyUp(&in_forward);} -void IN_BackDown(void) {KeyDown(&in_back);} -void IN_BackUp(void) {KeyUp(&in_back);} -void IN_LookupDown(void) {KeyDown(&in_lookup);} -void IN_LookupUp(void) {KeyUp(&in_lookup);} -void IN_LookdownDown(void) {KeyDown(&in_lookdown);} -void IN_LookdownUp(void) {KeyUp(&in_lookdown);} -void IN_MoveleftDown(void) {KeyDown(&in_moveleft);} -void IN_MoveleftUp(void) {KeyUp(&in_moveleft);} -void IN_MoverightDown(void) {KeyDown(&in_moveright);} -void IN_MoverightUp(void) {KeyUp(&in_moveright);} - -void IN_SpeedDown(void) {KeyDown(&in_speed);} -void IN_SpeedUp(void) {KeyUp(&in_speed);} -void IN_StrafeDown(void) {KeyDown(&in_strafe);} -void IN_StrafeUp(void) {KeyUp(&in_strafe);} - -void IN_AttackDown(void) {KeyDown(&in_attack);} -void IN_AttackUp(void) {KeyUp(&in_attack);} - -void IN_UseDown (void) {KeyDown(&in_use);} -void IN_UseUp (void) {KeyUp(&in_use);} -void IN_JumpDown (void) {KeyDown(&in_jump);} -void IN_JumpUp (void) {KeyUp(&in_jump);} - -//Tonik void IN_Impulse (void) {in_impulse=Q_atoi(Cmd_Argv(1));} - -// Tonik --> -void IN_Impulse (void) -{ - int best, i, imp, items; - - in_impulse = Q_atoi(Cmd_Argv(1)); - - if (Cmd_Argc() <= 2) - return; - - items = cl.stats[STAT_ITEMS]; - best = 0; - - for (i = Cmd_Argc() - 1; i > 0; i--) - { - imp = Q_atoi(Cmd_Argv(i)); - if (imp < 1 || imp > 8) - continue; - - switch (imp) - { - case 1: - if (items & IT_AXE) - best = 1; - break; - case 2: - if (items & IT_SHOTGUN && cl.stats[STAT_SHELLS] >= 1) - best = 2; - break; - case 3: - if (items & IT_SUPER_SHOTGUN && cl.stats[STAT_SHELLS] >= 2) - best = 3; - break; - case 4: - if (items & IT_NAILGUN && cl.stats[STAT_NAILS] >= 1) - best = 4; - break; - case 5: - if (items & IT_SUPER_NAILGUN && cl.stats[STAT_NAILS] >= 2) - best = 5; - break; - case 6: - if (items & IT_GRENADE_LAUNCHER && cl.stats[STAT_ROCKETS] >= 1) - best = 6; - break; - case 7: - if (items & IT_ROCKET_LAUNCHER && cl.stats[STAT_ROCKETS] >= 1) - best = 7; - break; - case 8: - if (items & IT_LIGHTNING && cl.stats[STAT_CELLS] >= 1) - best = 8; - - } - } - - if (best) - in_impulse = best; -} -// <-- Tonik - -/* -=============== -CL_KeyState - -Returns 0.25 if a key was pressed and released during the frame, -0.5 if it was pressed and held -0 if held then released, and -1.0 if held for the entire time -=============== -*/ -float CL_KeyState (kbutton_t *key) -{ - float val; - qboolean impulsedown, impulseup, down; - - impulsedown = key->state & 2; - impulseup = key->state & 4; - down = key->state & 1; - val = 0; - - if (impulsedown && !impulseup) - if (down) - val = 0.5; // pressed and held this frame - else - val = 0; // I_Error (); - if (impulseup && !impulsedown) - if (down) - val = 0; // I_Error (); - else - val = 0; // released this frame - if (!impulsedown && !impulseup) - if (down) - val = 1.0; // held the entire frame - else - val = 0; // up the entire frame - if (impulsedown && impulseup) - if (down) - val = 0.75; // released and re-pressed this frame - else - val = 0.25; // pressed and released this frame - - key->state &= 1; // clear impulses - - return val; -} - - - - -//========================================================================== - -cvar_t cl_upspeed = {"cl_upspeed","200"}; -cvar_t cl_forwardspeed = {"cl_forwardspeed","200", CVAR_ARCHIVE}; -cvar_t cl_backspeed = {"cl_backspeed","200", CVAR_ARCHIVE}; -cvar_t cl_sidespeed = {"cl_sidespeed","350"}; - -cvar_t cl_movespeedkey = {"cl_movespeedkey","2.0"}; - -cvar_t cl_yawspeed = {"cl_yawspeed","140"}; -cvar_t cl_pitchspeed = {"cl_pitchspeed","150"}; - -cvar_t cl_anglespeedkey = {"cl_anglespeedkey","1.5"}; - - -/* -================ -CL_AdjustAngles - -Moves the local angle positions -================ -*/ -void CL_AdjustAngles (void) -{ - float speed; - float up, down; - - if (in_speed.state & 1) - speed = host_frametime * cl_anglespeedkey.value; - else - speed = host_frametime; - - if (!(in_strafe.state & 1)) - { - cl.viewangles[YAW] -= speed*cl_yawspeed.value*CL_KeyState (&in_right); - cl.viewangles[YAW] += speed*cl_yawspeed.value*CL_KeyState (&in_left); - cl.viewangles[YAW] = anglemod(cl.viewangles[YAW]); - } - if (in_klook.state & 1) - { - V_StopPitchDrift (); - cl.viewangles[PITCH] -= speed*cl_pitchspeed.value * CL_KeyState (&in_forward); - cl.viewangles[PITCH] += speed*cl_pitchspeed.value * CL_KeyState (&in_back); - } - - up = CL_KeyState (&in_lookup); - down = CL_KeyState(&in_lookdown); - - cl.viewangles[PITCH] -= speed*cl_pitchspeed.value * up; - cl.viewangles[PITCH] += speed*cl_pitchspeed.value * down; - - if (up || down) - V_StopPitchDrift (); - - if (cl.viewangles[PITCH] > 80) - cl.viewangles[PITCH] = 80; - if (cl.viewangles[PITCH] < -70) - cl.viewangles[PITCH] = -70; - - if (cl.viewangles[ROLL] > 50) - cl.viewangles[ROLL] = 50; - if (cl.viewangles[ROLL] < -50) - cl.viewangles[ROLL] = -50; - -} - -/* -================ -CL_BaseMove - -Send the intended movement message to the server -================ -*/ -void CL_BaseMove (usercmd_t *cmd) -{ - CL_AdjustAngles (); - - memset (cmd, 0, sizeof(*cmd)); - - VectorCopy (cl.viewangles, cmd->angles); - if (in_strafe.state & 1) - { - cmd->sidemove += cl_sidespeed.value * CL_KeyState (&in_right); - cmd->sidemove -= cl_sidespeed.value * CL_KeyState (&in_left); - } - - cmd->sidemove += cl_sidespeed.value * CL_KeyState (&in_moveright); - cmd->sidemove -= cl_sidespeed.value * CL_KeyState (&in_moveleft); - - cmd->upmove += cl_upspeed.value * CL_KeyState (&in_up); - cmd->upmove -= cl_upspeed.value * CL_KeyState (&in_down); - - if (! (in_klook.state & 1) ) - { - cmd->forwardmove += cl_forwardspeed.value * CL_KeyState (&in_forward); - cmd->forwardmove -= cl_backspeed.value * CL_KeyState (&in_back); - } - -// -// adjust for speed key -// - if (in_speed.state & 1) - { - cmd->forwardmove *= cl_movespeedkey.value; - cmd->sidemove *= cl_movespeedkey.value; - cmd->upmove *= cl_movespeedkey.value; - } -} - -int MakeChar (int i) -{ - i &= ~3; - if (i < -127*4) - i = -127*4; - if (i > 127*4) - i = 127*4; - return i; -} - -/* -============== -CL_FinishMove -============== -*/ -void CL_FinishMove (usercmd_t *cmd) -{ - int i; - int ms; - static double msec_balance = 0; - -// -// always dump the first two messages, because they may contain leftover inputs -// from the last level -// - if (++cl.movemessages <= 2) - return; -// -// figure button bits -// - if ( in_attack.state & 3 ) - cmd->buttons |= 1; - in_attack.state &= ~2; - - if (in_jump.state & 3) - cmd->buttons |= 2; - in_jump.state &= ~2; - - // send milliseconds of time to apply the move -#if 0 - ms = host_frametime * 1000; -#else - msec_balance += real_frametime * 990; - ms = msec_balance; - msec_balance -= ms; -#endif - if (ms > 250) - ms = 100; // time was unreasonable - cmd->msec = ms; - - - VectorCopy (cl.viewangles, cmd->angles); - - cmd->impulse = in_impulse; - in_impulse = 0; - - -// -// chop down so no extra bits are kept that the server wouldn't get -// - cmd->forwardmove = MakeChar (cmd->forwardmove); - cmd->sidemove = MakeChar (cmd->sidemove); - cmd->upmove = MakeChar (cmd->upmove); - - for (i=0 ; i<3 ; i++) - cmd->angles[i] = ((int)(cmd->angles[i]*65536.0/360)&65535) * (360.0/65536.0); -} - -/* -================= -CL_SendCmd -================= -*/ -void CL_SendCmd (void) -{ - sizebuf_t buf; - byte data[128]; - int i; - usercmd_t *cmd, *oldcmd; - int checksumIndex; - int lost; - int seq_hash; - static float pps_balance = 0; - static int dropcount = 0; - qboolean dontdrop; - - if (cls.demoplayback && !cls.demoplayback2) - return; // sendcmds come from the demo - - // save this command off for prediction - i = cls.netchan.outgoing_sequence & UPDATE_MASK; - cmd = &cl.frames[i].cmd; - cl.frames[i].senttime = realtime; - cl.frames[i].receivedtime = -1; // we haven't gotten a reply yet - -// seq_hash = (cls.netchan.outgoing_sequence & 0xffff) ; // ^ QW_CHECK_HASH; - seq_hash = cls.netchan.outgoing_sequence; - - // get basic movement from keyboard - CL_BaseMove (cmd); - - // allow mice or other external controllers to add to the move - IN_Move (cmd); - - // if we are spectator, try autocam - if (cl.spectator) - Cam_Track(cmd); - - CL_FinishMove(cmd); - - Cam_FinishMove(cmd); - - if (cls.demoplayback2) { - cls.netchan.outgoing_sequence++; - return; - } - -// send this and the previous cmds in the message, so -// if the last packet was dropped, it can be recovered - buf.maxsize = 128; - buf.cursize = 0; - buf.data = data; - - MSG_WriteByte (&buf, clc_move); - - // save the position for a checksum byte - checksumIndex = buf.cursize; - MSG_WriteByte (&buf, 0); - - // write our lossage percentage - lost = CL_CalcNet(); - MSG_WriteByte (&buf, (byte)lost); - - dontdrop = false; - - i = (cls.netchan.outgoing_sequence-2) & UPDATE_MASK; - cmd = &cl.frames[i].cmd; -// dontdrop = dontdrop || cmd->impulse; - MSG_WriteDeltaUsercmd (&buf, &nullcmd, cmd); - oldcmd = cmd; - - i = (cls.netchan.outgoing_sequence-1) & UPDATE_MASK; - cmd = &cl.frames[i].cmd; - dontdrop = dontdrop || cmd->impulse; - MSG_WriteDeltaUsercmd (&buf, oldcmd, cmd); - oldcmd = cmd; - - i = (cls.netchan.outgoing_sequence) & UPDATE_MASK; - cmd = &cl.frames[i].cmd; - dontdrop = dontdrop || cmd->impulse; - MSG_WriteDeltaUsercmd (&buf, oldcmd, cmd); - - // calculate a checksum over the move commands - buf.data[checksumIndex] = COM_BlockSequenceCRCByte( - buf.data + checksumIndex + 1, buf.cursize - checksumIndex - 1, - seq_hash); - - // request delta compression of entities - if (cls.netchan.outgoing_sequence - cl.validsequence >= UPDATE_BACKUP-1) - cl.validsequence = 0; - - if (cl.validsequence && !cl_nodelta.value && cls.state == ca_active && - !cls.demorecording) - { - cl.frames[cls.netchan.outgoing_sequence&UPDATE_MASK].delta_sequence = cl.validsequence; - MSG_WriteByte (&buf, clc_delta); - MSG_WriteByte (&buf, cl.validsequence&255); - } - else - cl.frames[cls.netchan.outgoing_sequence&UPDATE_MASK].delta_sequence = -1; - - if (cls.demorecording) - CL_WriteDemoCmd(cmd); - - if (cl_c2spps.value) { - pps_balance += host_frametime; - // never drop more than 2 messages in a row -- that'll cause PL - // and don't drop if one of the last two movemessages have an impulse - if (pps_balance > 0 || dropcount >= 2 || dontdrop) { - float pps; - pps = cl_c2spps.value; - if (pps < 10) pps = 10; - if (pps > 72) pps = 72; - pps_balance -= 1 / pps; - // bound pps_balance. FIXME: is there a better way? - if (pps_balance > 0.1) pps_balance = 0.1; - if (pps_balance < -0.1) pps_balance = -0.1; - dropcount = 0; - } else { - // don't count this message when calculating PL - cl.frames[i].receivedtime = -3; - // drop this message - cls.netchan.outgoing_sequence++; - dropcount++; - return; - } - } else { - pps_balance = 0; - dropcount = 0; - } - -// -// deliver the message -// - Netchan_Transmit (&cls.netchan, buf.cursize, buf.data); -} - - -/* -============ -CL_InitInput -============ -*/ -void CL_InitInput (void) -{ - Cmd_AddCommand ("+moveup",IN_UpDown); - Cmd_AddCommand ("-moveup",IN_UpUp); - Cmd_AddCommand ("+movedown",IN_DownDown); - Cmd_AddCommand ("-movedown",IN_DownUp); - Cmd_AddCommand ("+left",IN_LeftDown); - Cmd_AddCommand ("-left",IN_LeftUp); - Cmd_AddCommand ("+right",IN_RightDown); - Cmd_AddCommand ("-right",IN_RightUp); - Cmd_AddCommand ("+forward",IN_ForwardDown); - Cmd_AddCommand ("-forward",IN_ForwardUp); - Cmd_AddCommand ("+back",IN_BackDown); - Cmd_AddCommand ("-back",IN_BackUp); - Cmd_AddCommand ("+lookup", IN_LookupDown); - Cmd_AddCommand ("-lookup", IN_LookupUp); - Cmd_AddCommand ("+lookdown", IN_LookdownDown); - Cmd_AddCommand ("-lookdown", IN_LookdownUp); - Cmd_AddCommand ("+strafe", IN_StrafeDown); - Cmd_AddCommand ("-strafe", IN_StrafeUp); - Cmd_AddCommand ("+moveleft", IN_MoveleftDown); - Cmd_AddCommand ("-moveleft", IN_MoveleftUp); - Cmd_AddCommand ("+moveright", IN_MoverightDown); - Cmd_AddCommand ("-moveright", IN_MoverightUp); - Cmd_AddCommand ("+speed", IN_SpeedDown); - Cmd_AddCommand ("-speed", IN_SpeedUp); - Cmd_AddCommand ("+attack", IN_AttackDown); - Cmd_AddCommand ("-attack", IN_AttackUp); - Cmd_AddCommand ("+use", IN_UseDown); - Cmd_AddCommand ("-use", IN_UseUp); - Cmd_AddCommand ("+jump", IN_JumpDown); - Cmd_AddCommand ("-jump", IN_JumpUp); - Cmd_AddCommand ("impulse", IN_Impulse); - Cmd_AddCommand ("+klook", IN_KLookDown); - Cmd_AddCommand ("-klook", IN_KLookUp); - Cmd_AddCommand ("+mlook", IN_MLookDown); - Cmd_AddCommand ("-mlook", IN_MLookUp); - - Cvar_RegisterVariable (&cl_nodelta); - Cvar_RegisterVariable (&cl_c2spps); -} - -/* -============ -CL_ClearStates -============ -*/ -void CL_ClearStates (void) -{ -} - +/* +Copyright (C) 1996-1997 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. + +*/ +// cl.input.c -- builds an intended movement command to send to the server + +#include "quakedef.h" + +cvar_t cl_nodelta = {"cl_nodelta","0"}; +cvar_t cl_c2spps = {"cl_c2spps","0"}; // Tonik + +/* +=============================================================================== + +KEY BUTTONS + +Continuous button event tracking is complicated by the fact that two different +input sources (say, mouse button 1 and the control key) can both press the +same button, but the button should only be released when both of the +pressing key have been released. + +When a key event issues a button command (+forward, +attack, etc), it appends +its key number as a parameter to the command so it can be matched up with +the release. + +state bit 0 is the current state of the key +state bit 1 is edge triggered on the up to down transition +state bit 2 is edge triggered on the down to up transition + +=============================================================================== +*/ + + +kbutton_t in_mlook, in_klook; +kbutton_t in_left, in_right, in_forward, in_back; +kbutton_t in_lookup, in_lookdown, in_moveleft, in_moveright; +kbutton_t in_strafe, in_speed, in_use, in_jump, in_attack; +kbutton_t in_up, in_down; + +int in_impulse; + + +void KeyDown (kbutton_t *b) +{ + int k; + char *c; + + c = Cmd_Argv(1); + if (c[0]) + k = atoi(c); + else + k = -1; // typed manually at the console for continuous down + + if (k == b->down[0] || k == b->down[1]) + return; // repeating key + + if (!b->down[0]) + b->down[0] = k; + else if (!b->down[1]) + b->down[1] = k; + else + { + Con_Printf ("Three keys down for a button!\n"); + return; + } + + if (b->state & 1) + return; // still down + b->state |= 1 + 2; // down + impulse down +} + +void KeyUp (kbutton_t *b) +{ + int k; + char *c; + + c = Cmd_Argv(1); + if (c[0]) + k = atoi(c); + else + { // typed manually at the console, assume for unsticking, so clear all + b->down[0] = b->down[1] = 0; + b->state = 4; // impulse up + return; + } + + if (b->down[0] == k) + b->down[0] = 0; + else if (b->down[1] == k) + b->down[1] = 0; + else + return; // key up without coresponding down (menu pass through) + if (b->down[0] || b->down[1]) + return; // some other key is still holding it down + + if (!(b->state & 1)) + return; // still up (this should not happen) + b->state &= ~1; // now up + b->state |= 4; // impulse up +} + +void IN_KLookDown (void) {KeyDown(&in_klook);} +void IN_KLookUp (void) {KeyUp(&in_klook);} +void IN_MLookDown (void) {KeyDown(&in_mlook);} +void IN_MLookUp (void) { +KeyUp(&in_mlook); +if ( !(in_mlook.state&1) && lookspring.value) + V_StartPitchDrift(); +} +void IN_UpDown(void) {KeyDown(&in_up);} +void IN_UpUp(void) {KeyUp(&in_up);} +void IN_DownDown(void) {KeyDown(&in_down);} +void IN_DownUp(void) {KeyUp(&in_down);} +void IN_LeftDown(void) {KeyDown(&in_left);} +void IN_LeftUp(void) {KeyUp(&in_left);} +void IN_RightDown(void) {KeyDown(&in_right);} +void IN_RightUp(void) {KeyUp(&in_right);} +void IN_ForwardDown(void) {KeyDown(&in_forward);} +void IN_ForwardUp(void) {KeyUp(&in_forward);} +void IN_BackDown(void) {KeyDown(&in_back);} +void IN_BackUp(void) {KeyUp(&in_back);} +void IN_LookupDown(void) {KeyDown(&in_lookup);} +void IN_LookupUp(void) {KeyUp(&in_lookup);} +void IN_LookdownDown(void) {KeyDown(&in_lookdown);} +void IN_LookdownUp(void) {KeyUp(&in_lookdown);} +void IN_MoveleftDown(void) {KeyDown(&in_moveleft);} +void IN_MoveleftUp(void) {KeyUp(&in_moveleft);} +void IN_MoverightDown(void) {KeyDown(&in_moveright);} +void IN_MoverightUp(void) {KeyUp(&in_moveright);} + +void IN_SpeedDown(void) {KeyDown(&in_speed);} +void IN_SpeedUp(void) {KeyUp(&in_speed);} +void IN_StrafeDown(void) {KeyDown(&in_strafe);} +void IN_StrafeUp(void) {KeyUp(&in_strafe);} + +void IN_AttackDown(void) {KeyDown(&in_attack);} +void IN_AttackUp(void) {KeyUp(&in_attack);} + +void IN_UseDown (void) {KeyDown(&in_use);} +void IN_UseUp (void) {KeyUp(&in_use);} +void IN_JumpDown (void) {KeyDown(&in_jump);} +void IN_JumpUp (void) {KeyUp(&in_jump);} + +//Tonik void IN_Impulse (void) {in_impulse=Q_atoi(Cmd_Argv(1));} + +// Tonik --> +void IN_Impulse (void) +{ + int best, i, imp, items; + + in_impulse = Q_atoi(Cmd_Argv(1)); + + if (Cmd_Argc() <= 2) + return; + + items = cl.stats[STAT_ITEMS]; + best = 0; + + for (i = Cmd_Argc() - 1; i > 0; i--) + { + imp = Q_atoi(Cmd_Argv(i)); + if (imp < 1 || imp > 8) + continue; + + switch (imp) + { + case 1: + if (items & IT_AXE) + best = 1; + break; + case 2: + if (items & IT_SHOTGUN && cl.stats[STAT_SHELLS] >= 1) + best = 2; + break; + case 3: + if (items & IT_SUPER_SHOTGUN && cl.stats[STAT_SHELLS] >= 2) + best = 3; + break; + case 4: + if (items & IT_NAILGUN && cl.stats[STAT_NAILS] >= 1) + best = 4; + break; + case 5: + if (items & IT_SUPER_NAILGUN && cl.stats[STAT_NAILS] >= 2) + best = 5; + break; + case 6: + if (items & IT_GRENADE_LAUNCHER && cl.stats[STAT_ROCKETS] >= 1) + best = 6; + break; + case 7: + if (items & IT_ROCKET_LAUNCHER && cl.stats[STAT_ROCKETS] >= 1) + best = 7; + break; + case 8: + if (items & IT_LIGHTNING && cl.stats[STAT_CELLS] >= 1) + best = 8; + + } + } + + if (best) + in_impulse = best; +} +// <-- Tonik + +/* +=============== +CL_KeyState + +Returns 0.25 if a key was pressed and released during the frame, +0.5 if it was pressed and held +0 if held then released, and +1.0 if held for the entire time +=============== +*/ +float CL_KeyState (kbutton_t *key) +{ + float val; + qboolean impulsedown, impulseup, down; + + impulsedown = key->state & 2; + impulseup = key->state & 4; + down = key->state & 1; + val = 0; + + if (impulsedown && !impulseup) + if (down) + val = 0.5; // pressed and held this frame + else + val = 0; // I_Error (); + if (impulseup && !impulsedown) + if (down) + val = 0; // I_Error (); + else + val = 0; // released this frame + if (!impulsedown && !impulseup) + if (down) + val = 1.0; // held the entire frame + else + val = 0; // up the entire frame + if (impulsedown && impulseup) + if (down) + val = 0.75; // released and re-pressed this frame + else + val = 0.25; // pressed and released this frame + + key->state &= 1; // clear impulses + + return val; +} + + + + +//========================================================================== + +cvar_t cl_upspeed = {"cl_upspeed","200"}; +cvar_t cl_forwardspeed = {"cl_forwardspeed","200", CVAR_ARCHIVE}; +cvar_t cl_backspeed = {"cl_backspeed","200", CVAR_ARCHIVE}; +cvar_t cl_sidespeed = {"cl_sidespeed","350"}; + +cvar_t cl_movespeedkey = {"cl_movespeedkey","2.0"}; + +cvar_t cl_yawspeed = {"cl_yawspeed","140"}; +cvar_t cl_pitchspeed = {"cl_pitchspeed","150"}; + +cvar_t cl_anglespeedkey = {"cl_anglespeedkey","1.5"}; + + +/* +================ +CL_AdjustAngles + +Moves the local angle positions +================ +*/ +void CL_AdjustAngles (void) +{ + float speed; + float up, down; + + if (in_speed.state & 1) + speed = host_frametime * cl_anglespeedkey.value; + else + speed = host_frametime; + + if (!(in_strafe.state & 1)) + { + cl.viewangles[YAW] -= speed*cl_yawspeed.value*CL_KeyState (&in_right); + cl.viewangles[YAW] += speed*cl_yawspeed.value*CL_KeyState (&in_left); + cl.viewangles[YAW] = anglemod(cl.viewangles[YAW]); + } + if (in_klook.state & 1) + { + V_StopPitchDrift (); + cl.viewangles[PITCH] -= speed*cl_pitchspeed.value * CL_KeyState (&in_forward); + cl.viewangles[PITCH] += speed*cl_pitchspeed.value * CL_KeyState (&in_back); + } + + up = CL_KeyState (&in_lookup); + down = CL_KeyState(&in_lookdown); + + cl.viewangles[PITCH] -= speed*cl_pitchspeed.value * up; + cl.viewangles[PITCH] += speed*cl_pitchspeed.value * down; + + if (up || down) + V_StopPitchDrift (); + + if (cl.viewangles[PITCH] > 80) + cl.viewangles[PITCH] = 80; + if (cl.viewangles[PITCH] < -70) + cl.viewangles[PITCH] = -70; + + if (cl.viewangles[ROLL] > 50) + cl.viewangles[ROLL] = 50; + if (cl.viewangles[ROLL] < -50) + cl.viewangles[ROLL] = -50; + +} + +/* +================ +CL_BaseMove + +Send the intended movement message to the server +================ +*/ +void CL_BaseMove (usercmd_t *cmd) +{ + CL_AdjustAngles (); + + memset (cmd, 0, sizeof(*cmd)); + + VectorCopy (cl.viewangles, cmd->angles); + if (in_strafe.state & 1) + { + cmd->sidemove += cl_sidespeed.value * CL_KeyState (&in_right); + cmd->sidemove -= cl_sidespeed.value * CL_KeyState (&in_left); + } + + cmd->sidemove += cl_sidespeed.value * CL_KeyState (&in_moveright); + cmd->sidemove -= cl_sidespeed.value * CL_KeyState (&in_moveleft); + + cmd->upmove += cl_upspeed.value * CL_KeyState (&in_up); + cmd->upmove -= cl_upspeed.value * CL_KeyState (&in_down); + + if (! (in_klook.state & 1) ) + { + cmd->forwardmove += cl_forwardspeed.value * CL_KeyState (&in_forward); + cmd->forwardmove -= cl_backspeed.value * CL_KeyState (&in_back); + } + +// +// adjust for speed key +// + if (in_speed.state & 1) + { + cmd->forwardmove *= cl_movespeedkey.value; + cmd->sidemove *= cl_movespeedkey.value; + cmd->upmove *= cl_movespeedkey.value; + } +} + +int MakeChar (int i) +{ + i &= ~3; + if (i < -127*4) + i = -127*4; + if (i > 127*4) + i = 127*4; + return i; +} + +/* +============== +CL_FinishMove +============== +*/ +void CL_FinishMove (usercmd_t *cmd) +{ + int i; + int ms; + static double msec_balance = 0; + +// +// always dump the first two messages, because they may contain leftover inputs +// from the last level +// + if (++cl.movemessages <= 2) + return; +// +// figure button bits +// + if ( in_attack.state & 3 ) + cmd->buttons |= 1; + in_attack.state &= ~2; + + if (in_jump.state & 3) + cmd->buttons |= 2; + in_jump.state &= ~2; + + // send milliseconds of time to apply the move +#if 0 + ms = host_frametime * 1000; +#else + msec_balance += real_frametime * 990; + ms = msec_balance; + msec_balance -= ms; +#endif + if (ms > 250) + ms = 100; // time was unreasonable + cmd->msec = ms; + + + VectorCopy (cl.viewangles, cmd->angles); + + cmd->impulse = in_impulse; + in_impulse = 0; + + +// +// chop down so no extra bits are kept that the server wouldn't get +// + cmd->forwardmove = MakeChar (cmd->forwardmove); + cmd->sidemove = MakeChar (cmd->sidemove); + cmd->upmove = MakeChar (cmd->upmove); + + for (i=0 ; i<3 ; i++) + cmd->angles[i] = ((int)(cmd->angles[i]*65536.0/360)&65535) * (360.0/65536.0); +} + +/* +================= +CL_SendCmd +================= +*/ +void CL_SendCmd (void) +{ + sizebuf_t buf; + byte data[128]; + int i; + usercmd_t *cmd, *oldcmd; + int checksumIndex; + int lost; + int seq_hash; + static float pps_balance = 0; + static int dropcount = 0; + qboolean dontdrop; + + if (cls.demoplayback && !cls.demoplayback2) + return; // sendcmds come from the demo + + // save this command off for prediction + i = cls.netchan.outgoing_sequence & UPDATE_MASK; + cmd = &cl.frames[i].cmd; + cl.frames[i].senttime = realtime; + cl.frames[i].receivedtime = -1; // we haven't gotten a reply yet + +// seq_hash = (cls.netchan.outgoing_sequence & 0xffff) ; // ^ QW_CHECK_HASH; + seq_hash = cls.netchan.outgoing_sequence; + + // get basic movement from keyboard + CL_BaseMove (cmd); + + // allow mice or other external controllers to add to the move + IN_Move (cmd); + + // if we are spectator, try autocam + if (cl.spectator) + Cam_Track(cmd); + + CL_FinishMove(cmd); + + Cam_FinishMove(cmd); + + if (cls.demoplayback2) { + cls.netchan.outgoing_sequence++; + return; + } + +// send this and the previous cmds in the message, so +// if the last packet was dropped, it can be recovered + buf.maxsize = 128; + buf.cursize = 0; + buf.data = data; + + MSG_WriteByte (&buf, clc_move); + + // save the position for a checksum byte + checksumIndex = buf.cursize; + MSG_WriteByte (&buf, 0); + + // write our lossage percentage + lost = CL_CalcNet(); + MSG_WriteByte (&buf, (byte)lost); + + dontdrop = false; + + i = (cls.netchan.outgoing_sequence-2) & UPDATE_MASK; + cmd = &cl.frames[i].cmd; +// dontdrop = dontdrop || cmd->impulse; + MSG_WriteDeltaUsercmd (&buf, &nullcmd, cmd); + oldcmd = cmd; + + i = (cls.netchan.outgoing_sequence-1) & UPDATE_MASK; + cmd = &cl.frames[i].cmd; + dontdrop = dontdrop || cmd->impulse; + MSG_WriteDeltaUsercmd (&buf, oldcmd, cmd); + oldcmd = cmd; + + i = (cls.netchan.outgoing_sequence) & UPDATE_MASK; + cmd = &cl.frames[i].cmd; + dontdrop = dontdrop || cmd->impulse; + MSG_WriteDeltaUsercmd (&buf, oldcmd, cmd); + + // calculate a checksum over the move commands + buf.data[checksumIndex] = COM_BlockSequenceCRCByte( + buf.data + checksumIndex + 1, buf.cursize - checksumIndex - 1, + seq_hash); + + // request delta compression of entities + if (cls.netchan.outgoing_sequence - cl.validsequence >= UPDATE_BACKUP-1) + cl.validsequence = 0; + + if (cl.validsequence && !cl_nodelta.value && cls.state == ca_active && + !cls.demorecording) + { + cl.frames[cls.netchan.outgoing_sequence&UPDATE_MASK].delta_sequence = cl.validsequence; + MSG_WriteByte (&buf, clc_delta); + MSG_WriteByte (&buf, cl.validsequence&255); + } + else + cl.frames[cls.netchan.outgoing_sequence&UPDATE_MASK].delta_sequence = -1; + + if (cls.demorecording) + CL_WriteDemoCmd(cmd); + + if (cl_c2spps.value) { + pps_balance += host_frametime; + // never drop more than 2 messages in a row -- that'll cause PL + // and don't drop if one of the last two movemessages have an impulse + if (pps_balance > 0 || dropcount >= 2 || dontdrop) { + float pps; + pps = cl_c2spps.value; + if (pps < 10) pps = 10; + if (pps > 72) pps = 72; + pps_balance -= 1 / pps; + // bound pps_balance. FIXME: is there a better way? + if (pps_balance > 0.1) pps_balance = 0.1; + if (pps_balance < -0.1) pps_balance = -0.1; + dropcount = 0; + } else { + // don't count this message when calculating PL + cl.frames[i].receivedtime = -3; + // drop this message + cls.netchan.outgoing_sequence++; + dropcount++; + return; + } + } else { + pps_balance = 0; + dropcount = 0; + } + +// +// deliver the message +// + Netchan_Transmit (&cls.netchan, buf.cursize, buf.data); +} + + +/* +============ +CL_InitInput +============ +*/ +void CL_InitInput (void) +{ + Cmd_AddCommand ("+moveup",IN_UpDown); + Cmd_AddCommand ("-moveup",IN_UpUp); + Cmd_AddCommand ("+movedown",IN_DownDown); + Cmd_AddCommand ("-movedown",IN_DownUp); + Cmd_AddCommand ("+left",IN_LeftDown); + Cmd_AddCommand ("-left",IN_LeftUp); + Cmd_AddCommand ("+right",IN_RightDown); + Cmd_AddCommand ("-right",IN_RightUp); + Cmd_AddCommand ("+forward",IN_ForwardDown); + Cmd_AddCommand ("-forward",IN_ForwardUp); + Cmd_AddCommand ("+back",IN_BackDown); + Cmd_AddCommand ("-back",IN_BackUp); + Cmd_AddCommand ("+lookup", IN_LookupDown); + Cmd_AddCommand ("-lookup", IN_LookupUp); + Cmd_AddCommand ("+lookdown", IN_LookdownDown); + Cmd_AddCommand ("-lookdown", IN_LookdownUp); + Cmd_AddCommand ("+strafe", IN_StrafeDown); + Cmd_AddCommand ("-strafe", IN_StrafeUp); + Cmd_AddCommand ("+moveleft", IN_MoveleftDown); + Cmd_AddCommand ("-moveleft", IN_MoveleftUp); + Cmd_AddCommand ("+moveright", IN_MoverightDown); + Cmd_AddCommand ("-moveright", IN_MoverightUp); + Cmd_AddCommand ("+speed", IN_SpeedDown); + Cmd_AddCommand ("-speed", IN_SpeedUp); + Cmd_AddCommand ("+attack", IN_AttackDown); + Cmd_AddCommand ("-attack", IN_AttackUp); + Cmd_AddCommand ("+use", IN_UseDown); + Cmd_AddCommand ("-use", IN_UseUp); + Cmd_AddCommand ("+jump", IN_JumpDown); + Cmd_AddCommand ("-jump", IN_JumpUp); + Cmd_AddCommand ("impulse", IN_Impulse); + Cmd_AddCommand ("+klook", IN_KLookDown); + Cmd_AddCommand ("-klook", IN_KLookUp); + Cmd_AddCommand ("+mlook", IN_MLookDown); + Cmd_AddCommand ("-mlook", IN_MLookUp); + + Cvar_RegisterVariable (&cl_nodelta); + Cvar_RegisterVariable (&cl_c2spps); +} + +/* +============ +CL_ClearStates +============ +*/ +void CL_ClearStates (void) +{ +} + diff --git a/source/cl_main.c b/source/cl_main.c index d8209477..b7611869 100644 --- a/source/cl_main.c +++ b/source/cl_main.c @@ -1,1154 +1,1162 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// cl_main.c -- client main loop - -#include "quakedef.h" -#include "winquake.h" -#include "cl_slist.h" -#include "keys.h" -#include "pmove.h" -#include "sbar.h" -#include "sound.h" -#include "version.h" -#include "teamplay.h" -#ifdef _WIN32 -#include "winsock.h" -#else -#include -#endif - - -// we need to declare some mouse variables here, because the menu system -// references them even when on a unix system. - - -cvar_t rcon_password = {"rcon_password", ""}; -cvar_t rcon_address = {"rcon_address", ""}; - -cvar_t cl_timeout = {"cl_timeout", "60"}; - -cvar_t cl_shownet = {"cl_shownet","0"}; // can be 0, 1, or 2 - -cvar_t cl_sbar = {"cl_sbar", "0", CVAR_ARCHIVE}; -cvar_t cl_hudswap = {"cl_hudswap", "0", CVAR_ARCHIVE}; -cvar_t cl_maxfps = {"cl_maxfps", "0", CVAR_ARCHIVE}; - -cvar_t lookspring = {"lookspring","0", CVAR_ARCHIVE}; -cvar_t lookstrafe = {"lookstrafe","0", CVAR_ARCHIVE}; -cvar_t sensitivity = {"sensitivity","3", CVAR_ARCHIVE}; - -cvar_t m_pitch = {"m_pitch","0.022", CVAR_ARCHIVE}; -cvar_t m_yaw = {"m_yaw","0.022"}; -cvar_t m_forward = {"m_forward","1"}; -cvar_t m_side = {"m_side","0.8"}; - -cvar_t entlatency = {"entlatency", "20"}; -cvar_t cl_predict_players = {"cl_predict_players", "1"}; -cvar_t cl_predict_players2 = {"cl_predict_players2", "1"}; -cvar_t cl_solid_players = {"cl_solid_players", "1"}; - -cvar_t localid = {"localid", ""}; - -static qboolean allowremotecmd = true; - -// ZQuake cvars -cvar_t r_rocketlight = {"r_rocketlight", "1"}; -cvar_t r_rockettrail = {"r_rockettrail", "1"}; -cvar_t r_grenadetrail = {"r_grenadetrail", "1"}; -cvar_t r_powerupglow = {"r_powerupglow", "1"}; -cvar_t cl_deadbodyfilter = {"cl_deadbodyfilter", "0"}; -cvar_t cl_explosion = {"cl_explosion", "0"}; -cvar_t cl_gibfilter = {"cl_gibfilter", "0"}; -cvar_t cl_muzzleflash = {"cl_muzzleflash", "1"}; -cvar_t cl_speedjumpfix = {"cl_speedjumpfix", "1"}; -cvar_t cl_demotimescale = {"demotimescale", "1"}; -cvar_t cl_staticsounds = {"cl_staticsounds", "1"}; -cvar_t cl_trueLightning = {"cl_trueLightning", "0"}; -cvar_t default_fov = {"default_fov", "0"}; -cvar_t cl_filterdrawviewmodel = {"cl_filterdrawviewmodel", "0"}; -cvar_t qizmo_dir = {"qizmo_dir", "qizmo"}; - -// -// info mirrors -// -cvar_t password = {"password", "", CVAR_USERINFO}; -cvar_t spectator = {"spectator", "", CVAR_USERINFO}; -cvar_t name = {"name","unnamed", CVAR_ARCHIVE|CVAR_USERINFO}; -cvar_t team = {"team","", CVAR_ARCHIVE|CVAR_USERINFO}; -cvar_t skin = {"skin","", CVAR_ARCHIVE|CVAR_USERINFO}; -cvar_t topcolor = {"topcolor","0", CVAR_ARCHIVE|CVAR_USERINFO}; -cvar_t bottomcolor = {"bottomcolor","0", CVAR_ARCHIVE|CVAR_USERINFO}; -cvar_t rate = {"rate","2500", CVAR_ARCHIVE|CVAR_USERINFO}; -cvar_t noaim = {"noaim","0", CVAR_ARCHIVE|CVAR_USERINFO}; -cvar_t msg = {"msg","1", CVAR_ARCHIVE|CVAR_USERINFO}; - -extern cvar_t cl_hightrack; - - -client_static_t cls; -client_state_t cl; - -entity_state_t cl_baselines[MAX_EDICTS]; -efrag_t cl_efrags[MAX_EFRAGS]; -entity_t cl_static_entities[MAX_STATIC_ENTITIES]; -lightstyle_t cl_lightstyle[MAX_LIGHTSTYLES]; -dlight_t cl_dlights[MAX_DLIGHTS]; - -// refresh list -// this is double buffered so the last frame -// can be scanned for oldorigins of trailing objects -int cl_numvisedicts, cl_oldnumvisedicts; -entity_t *cl_visedicts, *cl_oldvisedicts; -entity_t cl_visedicts_list[2][MAX_VISEDICTS]; - -double connect_time = -1; // for connection retransmits - -quakeparms_t host_parms; - -qboolean host_initialized; // true if into command execution - -double host_frametime; -double realtime; // without any filtering or bounding -double real_frametime; -double noscale_realtime; // without any filtering or bounding -double oldrealtime; // last frame run -int host_framecount; - -int host_hunklevel; - -byte *host_basepal; -byte *host_colormap; - -cvar_t host_speeds = {"host_speeds","0"}; // set for running times -cvar_t show_fps = {"show_fps","0"}; // set for running times -cvar_t developer = {"developer","0"}; - -int fps_count; - -jmp_buf host_abort; - -float server_version = 0; // version of server we connected to - -char emodel_name[] = - { 'e' ^ 0xff, 'm' ^ 0xff, 'o' ^ 0xff, 'd' ^ 0xff, 'e' ^ 0xff, 'l' ^ 0xff, 0 }; -char pmodel_name[] = - { 'p' ^ 0xff, 'm' ^ 0xff, 'o' ^ 0xff, 'd' ^ 0xff, 'e' ^ 0xff, 'l' ^ 0xff, 0 }; -char prespawn_name[] = - { 'p'^0xff, 'r'^0xff, 'e'^0xff, 's'^0xff, 'p'^0xff, 'a'^0xff, 'w'^0xff, 'n'^0xff, - ' '^0xff, '%'^0xff, 'i'^0xff, ' '^0xff, '0'^0xff, ' '^0xff, '%'^0xff, 'i'^0xff, 0 }; -char modellist_name[] = - { 'm'^0xff, 'o'^0xff, 'd'^0xff, 'e'^0xff, 'l'^0xff, 'l'^0xff, 'i'^0xff, 's'^0xff, 't'^0xff, - ' '^0xff, '%'^0xff, 'i'^0xff, ' '^0xff, '%'^0xff, 'i'^0xff, 0 }; -char soundlist_name[] = - { 's'^0xff, 'o'^0xff, 'u'^0xff, 'n'^0xff, 'd'^0xff, 'l'^0xff, 'i'^0xff, 's'^0xff, 't'^0xff, - ' '^0xff, '%'^0xff, 'i'^0xff, ' '^0xff, '%'^0xff, 'i'^0xff, 0 }; - -/* -======================= -CL_SendConnectPacket - -called by CL_Connect_f and CL_CheckResend -====================== -*/ -void CL_SendConnectPacket (void) -{ - netadr_t adr; - char data[2048]; - double t1, t2; -// JACK: Fixed bug where DNS lookups would cause two connects real fast -// Now, adds lookup time to the connect time. -// Should I add it to realtime instead?!?! - - if (cls.state != ca_disconnected) - return; - - t1 = Sys_DoubleTime (); - - if (!NET_StringToAdr (cls.servername, &adr)) - { - Con_Printf ("Bad server address\n"); - connect_time = -1; - return; - } - - if (adr.port == 0) - adr.port = BigShort (27500); - t2 = Sys_DoubleTime (); - - connect_time = realtime+t2-t1; // for retransmit requests - - cls.qport = Cvar_VariableValue("qport"); - -// Con_Printf ("Connecting to %s...\n", cls.servername); - sprintf (data, "\xff\xff\xff\xff" "connect %i %i %i \"%s\"\n", - PROTOCOL_VERSION, cls.qport, cls.challenge, cls.userinfo); - NET_SendPacket (net_clientsocket, strlen(data), data, adr); -} - -/* -================= -CL_CheckForResend - -Resend a connect message if the last one has timed out -================= -*/ -void CL_CheckForResend (void) -{ - netadr_t adr; - char data[2048]; - double t1, t2; - - if (connect_time == -1) - return; - if (cls.state != ca_disconnected) - return; - if (connect_time && realtime - connect_time < 5.0) - return; - - t1 = Sys_DoubleTime (); - if (!NET_StringToAdr (cls.servername, &adr)) - { - Con_Printf ("Bad server address\n"); - connect_time = -1; - return; - } - t2 = Sys_DoubleTime (); - - if (adr.port == 0) - adr.port = BigShort (27500); - - connect_time = realtime+t2-t1; // for retransmit requests - - Con_Printf ("Connecting to %s...\n", cls.servername); - sprintf (data, "\xff\xff\xff\xff" "getchallenge\n"); - NET_SendPacket (net_clientsocket, strlen(data), data, adr); -} - -void CL_BeginServerConnect(void) -{ - connect_time = 0; - CL_CheckForResend(); -} - -/* -================ -CL_Connect_f - -================ -*/ -void CL_Connect_f (void) -{ - char *server; - - if (Cmd_Argc() != 2) - { - Con_Printf ("usage: connect \n"); - return; - } - - server = Cmd_Argv (1); - - CL_Disconnect (); - - Q_strncpyz (cls.servername, server, sizeof(cls.servername)); - CL_BeginServerConnect(); -} - - -/* -===================== -CL_ClearState - -===================== -*/ -void CL_ClearState (void) -{ - int i; - - S_StopAllSounds (true); - - Con_DPrintf ("Clearing memory\n"); - D_FlushCaches (); - Mod_ClearAll (); - Hunk_FreeToLowMark (host_hunklevel); - - CL_ClearTEnts (); - memset (cl_baselines, 0, sizeof(cl_baselines)); - -// wipe the entire cl structure - memset (&cl, 0, sizeof(cl)); - - SZ_Clear (&cls.netchan.message); - -// clear other arrays - memset (cl_efrags, 0, sizeof(cl_efrags)); - memset (cl_dlights, 0, sizeof(cl_dlights)); - memset (cl_lightstyle, 0, sizeof(cl_lightstyle)); - -// -// allocate the efrags and chain together into a free list -// - cl.free_efrags = cl_efrags; - for (i=0 ; i= ca_connected) - { - if (!cls.demoplayback) - Con_Printf ("Dup connect received. Ignored.\n"); - return; - } - Netchan_Setup (&cls.netchan, net_from, cls.qport, net_clientsocket); - MSG_WriteChar (&cls.netchan.message, clc_stringcmd); - MSG_WriteString (&cls.netchan.message, "new"); - cls.state = ca_connected; - Con_Printf ("Connected.\n"); - allowremotecmd = false; // localid required now for remote cmds - return; - } - // remote command from gui front end - if (c == A2C_CLIENT_COMMAND) - { - char cmdtext[2048]; - - Con_Printf ("client command\n"); - - if ((*(unsigned *)net_from.ip != *(unsigned *)net_local_adr.ip - && *(unsigned *)net_from.ip != htonl(INADDR_LOOPBACK)) ) - { - Con_Printf ("Command packet from remote host. Ignored.\n"); - return; - } -#ifdef _WIN32 - ShowWindow (mainwindow, SW_RESTORE); - SetForegroundWindow (mainwindow); -#endif - s = MSG_ReadString (); - - Q_strncpyz (cmdtext, s, sizeof(cmdtext)); - - s = MSG_ReadString (); - - while (*s && isspace(*s)) - s++; - while (*s && isspace(s[strlen(s) - 1])) - s[strlen(s) - 1] = 0; - - if (!allowremotecmd && (!*localid.string || strcmp(localid.string, s))) { - if (!*localid.string) { - Con_Printf("===========================\n"); - Con_Printf("Command packet received from local host, but no " - "localid has been set. You may need to upgrade your server " - "browser.\n"); - Con_Printf("===========================\n"); - return; - } - Con_Printf("===========================\n"); - Con_Printf("Invalid localid on command packet received from local host. " - "\n|%s| != |%s|\n" - "You may need to reload your server browser and QuakeWorld.\n", - s, localid.string); - Con_Printf("===========================\n"); - Cvar_Set(&localid, ""); - return; - } - - Cbuf_AddText (cmdtext); - Cbuf_AddText ("\n"); - allowremotecmd = false; - return; - } - // print command from somewhere - if (c == A2C_PRINT) - { - Con_Printf ("print\n"); - - s = MSG_ReadString (); - Con_Print (s); - return; - } - - // ping from somewhere - if (c == A2A_PING) - { - char data[6]; - - Con_Printf ("ping\n"); - - data[0] = 0xff; - data[1] = 0xff; - data[2] = 0xff; - data[3] = 0xff; - data[4] = A2A_ACK; - data[5] = 0; - - NET_SendPacket (net_clientsocket, 6, &data, net_from); - return; - } - - if (c == S2C_CHALLENGE) { - Con_Printf ("challenge\n"); - - s = MSG_ReadString (); - cls.challenge = atoi(s); - CL_SendConnectPacket (); - return; - } - - if (c == svc_disconnect) { - if (cls.demoplayback) - Host_EndGame ("End of demo"); -// else -// Host_EndGame ("Server disconnected"); - return; - } - - Con_Printf ("unknown: %c\n", c); -} - -/* -================= -CL_ReadPackets -================= -*/ -void CL_InitInterpolation(float cur, float old); -void CL_ReadPackets (void) -{ - extern float nextdemotime, olddemotime; - qboolean change = false; - static float old; - -// while (NET_GetPacket ()) - while (CL_GetMessage()) - { - // - // remote command packet - // - if (*(int *)net_message.data == -1) - { - CL_ConnectionlessPacket (); - continue; - } - - if (net_message.cursize < 8 && !cls.demoplayback2) - { - Con_Printf ("%s: Runt packet\n",NET_AdrToString(net_from)); - continue; - } - - // - // packet from server - // - if (!cls.demoplayback && - !NET_CompareAdr (net_from, cls.netchan.remote_address)) - { - Con_DPrintf ("%s:sequenced packet without connection\n" - ,NET_AdrToString(net_from)); - continue; - } - if (!cls.demoplayback2) { - if (!Netchan_Process(&cls.netchan)) - continue; // wasn't accepted for some reason - } else { - MSG_BeginReading (); - } - - CL_ParseServerMessage (); - -// if (cls.demoplayback && cls.state >= ca_active && !CL_DemoBehind()) -// return; - } - - // - // check timeout - // - if (cls.state >= ca_connected - && realtime - cls.netchan.last_received > cl_timeout.value) - { - Con_Printf ("\nServer connection timed out.\n"); - CL_Disconnect (); - return; - } - -} - -//============================================================================= - -void CL_InitCommands (void); - -/* -================= -CL_Init -================= -*/ -void CL_Init (void) -{ - extern cvar_t baseskin; - extern cvar_t noskins; - - cls.state = ca_disconnected; - - Info_SetValueForKey (cls.userinfo, "name", "unnamed", MAX_INFO_STRING); - Info_SetValueForKey (cls.userinfo, "topcolor", "0", MAX_INFO_STRING); - Info_SetValueForKey (cls.userinfo, "bottomcolor", "0", MAX_INFO_STRING); - Info_SetValueForKey (cls.userinfo, "rate", "2500", MAX_INFO_STRING); - Info_SetValueForKey (cls.userinfo, "msg", "1", MAX_INFO_STRING); - -#ifndef RELEASE_VERSION - Info_SetValueForStarKey (cls.userinfo, "*qwe_ver", QWE_VERSION, MAX_INFO_STRING); -#endif - - CL_InitInput (); - CL_InitTEnts (); - CL_InitPrediction (); - CL_InitCam (); - Pmove_Init (); - -// -// register our commands -// - Cvar_RegisterVariable (&show_fps); - Cvar_RegisterVariable (&host_speeds); - Cvar_RegisterVariable (&developer); - - Cvar_RegisterVariable (&cl_warncmd); - Cvar_RegisterVariable (&cl_upspeed); - Cvar_RegisterVariable (&cl_forwardspeed); - Cvar_RegisterVariable (&cl_backspeed); - Cvar_RegisterVariable (&cl_sidespeed); - Cvar_RegisterVariable (&cl_movespeedkey); - Cvar_RegisterVariable (&cl_yawspeed); - Cvar_RegisterVariable (&cl_pitchspeed); - Cvar_RegisterVariable (&cl_anglespeedkey); - Cvar_RegisterVariable (&cl_shownet); - Cvar_RegisterVariable (&cl_sbar); - Cvar_RegisterVariable (&cl_hudswap); - Cvar_RegisterVariable (&cl_maxfps); - Cvar_RegisterVariable (&cl_timeout); - Cvar_RegisterVariable (&lookspring); - Cvar_RegisterVariable (&lookstrafe); - Cvar_RegisterVariable (&sensitivity); - - Cvar_RegisterVariable (&m_pitch); - Cvar_RegisterVariable (&m_yaw); - Cvar_RegisterVariable (&m_forward); - Cvar_RegisterVariable (&m_side); - - Cvar_RegisterVariable (&rcon_password); - Cvar_RegisterVariable (&rcon_address); - - Cvar_RegisterVariable (&entlatency); - Cvar_RegisterVariable (&cl_predict_players2); - Cvar_RegisterVariable (&cl_predict_players); - Cvar_RegisterVariable (&cl_solid_players); - - Cvar_RegisterVariable (&localid); - - Cvar_RegisterVariable (&baseskin); - Cvar_RegisterVariable (&noskins); - - // ZQuake cvars - Cvar_RegisterVariable (&r_rockettrail); - Cvar_RegisterVariable (&r_grenadetrail); - Cvar_RegisterVariable (&r_powerupglow); - Cvar_RegisterVariable (&r_rocketlight); - Cvar_RegisterVariable (&cl_speedjumpfix); - Cvar_RegisterVariable (&cl_demotimescale); - Cvar_RegisterVariable (&cl_deadbodyfilter); - Cvar_RegisterVariable (&cl_explosion); - Cvar_RegisterVariable (&cl_gibfilter); - Cvar_RegisterVariable (&cl_muzzleflash); - Cvar_RegisterVariable (&cl_staticsounds); - Cvar_RegisterVariable (&cl_trueLightning); - Cvar_RegisterVariable (&default_fov); - Cvar_RegisterVariable (&cl_filterdrawviewmodel); - Cvar_RegisterVariable (&qizmo_dir); - - // - // info mirrors - // - Cvar_RegisterVariable (&name); - Cvar_RegisterVariable (&password); - Cvar_RegisterVariable (&spectator); - Cvar_RegisterVariable (&skin); - Cvar_RegisterVariable (&team); - Cvar_RegisterVariable (&topcolor); - Cvar_RegisterVariable (&bottomcolor); - Cvar_RegisterVariable (&rate); - Cvar_RegisterVariable (&msg); - Cvar_RegisterVariable (&noaim); - - - CL_InitCommands (); - - Cmd_AddCommand ("disconnect", CL_Disconnect_f); - Cmd_AddCommand ("connect", CL_Connect_f); - Cmd_AddCommand ("reconnect", CL_Reconnect_f); - - SList_Init(); - SList_Load(); -} - - -/* -================ -Host_EndGame - -Call this to drop to a console without exiting the qwcl -================ -*/ -void Host_EndGame (char *message, ...) -{ - va_list argptr; - char string[1024]; - - va_start (argptr,message); - vsprintf (string,message,argptr); - va_end (argptr); - Con_Printf ("\n===========================\n"); - Con_Printf ("Host_EndGame: %s\n",string); - Con_Printf ("===========================\n\n"); - - CL_Disconnect (); - - longjmp (host_abort, 1); -} - -/* -================ -Host_Error - -This shuts down the client and exits qwcl -================ -*/ -void Host_Error (char *error, ...) -{ - va_list argptr; - char string[1024]; - static qboolean inerror = false; - - if (inerror) - Sys_Error ("Host_Error: recursively entered"); - inerror = true; - - va_start (argptr,error); - vsprintf (string,error,argptr); - va_end (argptr); - Con_Printf ("Host_Error: %s\n",string); - - CL_Disconnect (); - cls.demonum = -1; - - inerror = false; - -// FIXME - Sys_Error ("Host_Error: %s\n",string); -} - - -/* -=============== -Host_WriteConfiguration - -Writes key bindings and archived cvars to config.cfg -=============== -*/ -void Host_WriteConfiguration (void) -{ - FILE *f; - - if (host_initialized) - { - f = fopen (va("%s/config.cfg",com_gamedir), "w"); - if (!f) - { - Con_Printf ("Couldn't write config.cfg.\n"); - return; - } - - Key_WriteBindings (f); - Cvar_WriteVariables (f); - - fclose (f); - } -} - - -//============================================================================ - -void Host_ConnectLocal () -{ - if (cls.state < ca_connected) - Cmd_ExecuteString ("connect local"); - else - cls.state = ca_connected; -} - - -/* -================== -Host_Frame - -Runs all active servers -================== -*/ -void Host_Frame (double time) -{ - static double time1 = 0; - static double time2 = 0; - static double time3 = 0; - int pass1, pass2, pass3; - float fps; - float scale; - static float old; - static int oldparse; - static double oldrealtime2; - extern float nextdemotime, olddemotime; - - if (setjmp (host_abort) ) - return; // something bad happened, or the server disconnected - - // decide the simulation time - - noscale_realtime += time; - if (!cls.demoplayback) - realtime += time; - else - { - scale = cl_demotimescale.value; - if (scale <= 0) scale = 0; - //if (scale < 0.01) scale = 0.01; - if (scale > 10) scale = 10; - realtime += time*scale; - - } - - if (oldrealtime > realtime) - oldrealtime = 0; - - if (oldrealtime2 > noscale_realtime) - oldrealtime2 = 0; - - if (cl_maxfps.value) - fps = max(30.0, min(cl_maxfps.value, 72.0)); - else - fps = max(30.0, min(rate.value/80.0, 72.0)); - - if (!cls.demoplayback && realtime - oldrealtime < 1.0/fps) - return; // framerate is too high - - host_frametime = realtime - oldrealtime; - real_frametime = noscale_realtime - oldrealtime2; - - if (cls.demoplayback && (cl.paused & 2)) { - realtime = oldrealtime; - noscale_realtime = oldrealtime2; - } - - oldrealtime = realtime; - if (host_frametime > 0.2) - host_frametime = 0.2; - - oldrealtime2 = noscale_realtime; - if (real_frametime > 0.2) - real_frametime = 0.2; - - // get new key events - Sys_SendKeyEvents (); - - // allow mice or other external controllers to add commands - IN_Commands (); - - // process console commands - Cbuf_Execute (); - - // fetch results from server - CL_ReadPackets (); - - - if (cls.demoplayback2) - { - player_state_t *self, *oldself; - self = &cl.frames[cl.parsecount & UPDATE_MASK].playerstate[cl.playernum]; - oldself = &cl.frames[(cls.netchan.outgoing_sequence-1) & UPDATE_MASK].playerstate[cl.playernum]; - self->messagenum = cl.parsecount; - VectorCopy(oldself->origin, self->origin); - VectorCopy(oldself->velocity, self->velocity); - VectorCopy(oldself->viewangles, self->viewangles); - - if (old != nextdemotime) // FIXME: use oldparcecount != cl.parsecount? - { - old = nextdemotime; - CL_InitInterpolation(nextdemotime, olddemotime); - } - - CL_ParseClientdata(); - - cls.netchan.outgoing_sequence = cl.parsecount+1; - CL_Interpolate(); - } - - // process stuffed commands - Cbuf_ExecuteEx (&cbuf_svc); - - // send intentions now - // resend a connection request if necessary - if (cls.state == ca_disconnected) { - CL_CheckForResend (); - } else - CL_SendCmd (); - - if (cls.state >= ca_onserver) // !!! Tonik - { - // Set up prediction for other players - CL_SetUpPlayerPrediction(false); - - // do client side motion prediction - CL_PredictMove (false); - - // Set up prediction for other players - CL_SetUpPlayerPrediction(true); - - // build a refresh entity list - CL_EmitEntities (); - } - - // update video - if (host_speeds.value) - time1 = Sys_DoubleTime (); - - SCR_UpdateScreen (); - - if (host_speeds.value) - time2 = Sys_DoubleTime (); - - // update audio - if (cls.state == ca_active) - { - S_Update (r_origin, vpn, vright, vup); - CL_DecayLights (); - } - else - S_Update (vec3_origin, vec3_origin, vec3_origin, vec3_origin); - - CDAudio_Update(); - - if (host_speeds.value) - { - pass1 = (time1 - time3)*1000; - time3 = Sys_DoubleTime (); - pass2 = (time2 - time1)*1000; - pass3 = (time3 - time2)*1000; - Con_Printf ("%3i tot %3i server %3i gfx %3i snd\n", - pass1+pass2+pass3, pass1, pass2, pass3); - } - - host_framecount++; - fps_count++; -} - -static void simple_crypt(char *buf, int len) -{ - while (len--) - *buf++ ^= 0xff; -} - -void Host_FixupModelNames(void) -{ - simple_crypt(emodel_name, sizeof(emodel_name) - 1); - simple_crypt(pmodel_name, sizeof(pmodel_name) - 1); - simple_crypt(prespawn_name, sizeof(prespawn_name) - 1); - simple_crypt(modellist_name, sizeof(modellist_name) - 1); - simple_crypt(soundlist_name, sizeof(soundlist_name) - 1); -} - -//============================================================================ - -/* -==================== -Host_Init -==================== -*/ -void Host_Init (quakeparms_t *parms) -{ - COM_InitArgv (parms->argc, parms->argv); - - if (COM_CheckParm ("-minmemory")) - parms->memsize = MINIMUM_MEMORY; - - host_parms = *parms; - - if (parms->memsize < MINIMUM_MEMORY) - Sys_Error ("Only %4.1f megs of memory reported, can't execute game", parms->memsize / (float)0x100000); - - Memory_Init (parms->membase, parms->memsize); - Cbuf_Init (); - Cmd_Init (); - Cvar_Init (); - V_Init (); - TP_Init (); - - COM_Init (); - - Host_FixupModelNames(); - - NET_Init (PORT_CLIENT, 0); - - Netchan_Init (); - - W_LoadWadFile ("gfx.wad"); - - Key_Init (); - Con_Init (); - M_Init (); - Mod_Init (); - - Sys_mkdir(va("%s/%s", com_basedir, "qw")); - -// Con_Printf ("Exe: "__TIME__" "__DATE__"\n"); - Con_Printf ("%4.1f megs RAM used.\n",parms->memsize/ (1024*1024.0)); - - R_InitTextures (); - - host_basepal = (byte *)COM_LoadHunkFile ("gfx/palette.lmp"); - if (!host_basepal) - Sys_Error ("Couldn't load gfx/palette.lmp"); - host_colormap = (byte *)COM_LoadHunkFile ("gfx/colormap.lmp"); - if (!host_colormap) - Sys_Error ("Couldn't load gfx/colormap.lmp"); -#ifdef __linux__ - IN_Init (); - CDAudio_Init (); - VID_Init (host_basepal); - Draw_Init (); - SCR_Init (); - R_Init (); - -// S_Init (); // S_Init is now done as part of VID. Sigh. - - cls.state = ca_disconnected; - Sbar_Init (); - CL_Init (); -#else - VID_Init (host_basepal); - Draw_Init (); - SCR_Init (); - R_Init (); -// S_Init (); // S_Init is now done as part of VID. Sigh. -#ifdef GLQUAKE - S_Init(); -#endif - - cls.state = ca_disconnected; - CDAudio_Init (); - Sbar_Init (); - CL_Init (); - IN_Init (); -#endif - - Cbuf_InsertText ("exec quake.rc\n"); -// Cbuf_AddText ("echo Type connect or use GameSpy to connect to a game.\n"); - Cbuf_AddText ("cl_warncmd 1\n"); - - Hunk_AllocName (0, "-HOST_HUNKLEVEL-"); - host_hunklevel = Hunk_LowMark (); - - host_initialized = true; - -#ifdef RELEASE_VERSION - Con_Printf ("\nClient Version %s\n\n", QWE_VERSION); -#else - Con_Printf ("\nClient Version %s (Build %04d)\n\n", QWE_VERSION, build_number()); -#endif - - Con_Printf ("€ QuakeWorld Initialized ‚\n"); -} - - -/* -=============== -Host_Shutdown - -FIXME: this is a callback from Sys_Quit and Sys_Error. It would be better -to run quit through here before the final handoff to the sys code. -=============== -*/ -void Host_Shutdown(void) -{ - static qboolean isdown = false; - - if (isdown) - { - printf ("recursive shutdown\n"); - return; - } - isdown = true; - - Host_WriteConfiguration (); - - SList_Shutdown (); - CDAudio_Shutdown (); - NET_Shutdown (); - S_Shutdown(); - IN_Shutdown (); - if (host_basepal) - VID_Shutdown(); -} +/* +Copyright (C) 1996-1997 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. + +*/ +// cl_main.c -- client main loop + +#include "quakedef.h" +#include "winquake.h" +#include "cl_slist.h" +#include "keys.h" +#include "pmove.h" +#include "sbar.h" +#include "sound.h" +#include "version.h" +#include "teamplay.h" +#ifdef _WIN32 +#include "winsock.h" +#else +#include +#endif + + +// we need to declare some mouse variables here, because the menu system +// references them even when on a unix system. + + +cvar_t rcon_password = {"rcon_password", ""}; +cvar_t rcon_address = {"rcon_address", ""}; + +cvar_t cl_timeout = {"cl_timeout", "60"}; + +cvar_t cl_shownet = {"cl_shownet","0"}; // can be 0, 1, or 2 + +cvar_t cl_sbar = {"cl_sbar", "0", CVAR_ARCHIVE}; +cvar_t cl_hudswap = {"cl_hudswap", "0", CVAR_ARCHIVE}; +cvar_t cl_maxfps = {"cl_maxfps", "0", CVAR_ARCHIVE}; + +cvar_t lookspring = {"lookspring","0", CVAR_ARCHIVE}; +cvar_t lookstrafe = {"lookstrafe","0", CVAR_ARCHIVE}; +cvar_t sensitivity = {"sensitivity","3", CVAR_ARCHIVE}; + +cvar_t m_pitch = {"m_pitch","0.022", CVAR_ARCHIVE}; +cvar_t m_yaw = {"m_yaw","0.022"}; +cvar_t m_forward = {"m_forward","1"}; +cvar_t m_side = {"m_side","0.8"}; + +cvar_t entlatency = {"entlatency", "20"}; +cvar_t cl_predict_players = {"cl_predict_players", "1"}; +cvar_t cl_predict_players2 = {"cl_predict_players2", "1"}; +cvar_t cl_solid_players = {"cl_solid_players", "1"}; + +cvar_t localid = {"localid", ""}; + +static qboolean allowremotecmd = true; + +// ZQuake cvars +cvar_t r_rocketlight = {"r_rocketlight", "1"}; +cvar_t r_rockettrail = {"r_rockettrail", "1"}; +cvar_t r_grenadetrail = {"r_grenadetrail", "1"}; +cvar_t r_powerupglow = {"r_powerupglow", "1"}; +cvar_t cl_deadbodyfilter = {"cl_deadbodyfilter", "0"}; +cvar_t cl_explosion = {"cl_explosion", "0"}; +cvar_t cl_gibfilter = {"cl_gibfilter", "0"}; +cvar_t cl_muzzleflash = {"cl_muzzleflash", "1"}; +cvar_t cl_speedjumpfix = {"cl_speedjumpfix", "1"}; +cvar_t cl_demotimescale = {"demotimescale", "1"}; +cvar_t cl_staticsounds = {"cl_staticsounds", "1"}; +cvar_t cl_trueLightning = {"cl_trueLightning", "0"}; +cvar_t default_fov = {"default_fov", "0"}; +cvar_t cl_filterdrawviewmodel = {"cl_filterdrawviewmodel", "0"}; +cvar_t qizmo_dir = {"qizmo_dir", "qizmo"}; + +// +// info mirrors +// +cvar_t password = {"password", "", CVAR_USERINFO}; +cvar_t spectator = {"spectator", "", CVAR_USERINFO}; +cvar_t name = {"name","unnamed", CVAR_ARCHIVE|CVAR_USERINFO}; +cvar_t team = {"team","", CVAR_ARCHIVE|CVAR_USERINFO}; +cvar_t skin = {"skin","", CVAR_ARCHIVE|CVAR_USERINFO}; +cvar_t topcolor = {"topcolor","0", CVAR_ARCHIVE|CVAR_USERINFO}; +cvar_t bottomcolor = {"bottomcolor","0", CVAR_ARCHIVE|CVAR_USERINFO}; +cvar_t rate = {"rate","2500", CVAR_ARCHIVE|CVAR_USERINFO}; +cvar_t noaim = {"noaim","0", CVAR_ARCHIVE|CVAR_USERINFO}; +cvar_t msg = {"msg","1", CVAR_ARCHIVE|CVAR_USERINFO}; + +extern cvar_t cl_hightrack; + + +client_static_t cls; +client_state_t cl; + +entity_state_t cl_baselines[MAX_EDICTS]; +efrag_t cl_efrags[MAX_EFRAGS]; +entity_t cl_static_entities[MAX_STATIC_ENTITIES]; +lightstyle_t cl_lightstyle[MAX_LIGHTSTYLES]; +dlight_t cl_dlights[MAX_DLIGHTS]; + +// refresh list +// this is double buffered so the last frame +// can be scanned for oldorigins of trailing objects +int cl_numvisedicts, cl_oldnumvisedicts; +entity_t *cl_visedicts, *cl_oldvisedicts; +entity_t cl_visedicts_list[2][MAX_VISEDICTS]; + +double connect_time = -1; // for connection retransmits + +quakeparms_t host_parms; + +qboolean host_initialized; // true if into command execution + +double host_frametime; +double realtime; // without any filtering or bounding +double real_frametime; +double noscale_realtime; // without any filtering or bounding +double oldrealtime; // last frame run +int host_framecount; + +int host_hunklevel; + +byte *host_basepal; +byte *host_colormap; + +cvar_t host_speeds = {"host_speeds","0"}; // set for running times +cvar_t show_fps = {"show_fps","0"}; // set for running times +cvar_t developer = {"developer","0"}; + +int fps_count; + +jmp_buf host_abort; + +float server_version = 0; // version of server we connected to + +char emodel_name[] = + { 'e' ^ 0xff, 'm' ^ 0xff, 'o' ^ 0xff, 'd' ^ 0xff, 'e' ^ 0xff, 'l' ^ 0xff, 0 }; +char pmodel_name[] = + { 'p' ^ 0xff, 'm' ^ 0xff, 'o' ^ 0xff, 'd' ^ 0xff, 'e' ^ 0xff, 'l' ^ 0xff, 0 }; +char prespawn_name[] = + { 'p'^0xff, 'r'^0xff, 'e'^0xff, 's'^0xff, 'p'^0xff, 'a'^0xff, 'w'^0xff, 'n'^0xff, + ' '^0xff, '%'^0xff, 'i'^0xff, ' '^0xff, '0'^0xff, ' '^0xff, '%'^0xff, 'i'^0xff, 0 }; +char modellist_name[] = + { 'm'^0xff, 'o'^0xff, 'd'^0xff, 'e'^0xff, 'l'^0xff, 'l'^0xff, 'i'^0xff, 's'^0xff, 't'^0xff, + ' '^0xff, '%'^0xff, 'i'^0xff, ' '^0xff, '%'^0xff, 'i'^0xff, 0 }; +char soundlist_name[] = + { 's'^0xff, 'o'^0xff, 'u'^0xff, 'n'^0xff, 'd'^0xff, 'l'^0xff, 'i'^0xff, 's'^0xff, 't'^0xff, + ' '^0xff, '%'^0xff, 'i'^0xff, ' '^0xff, '%'^0xff, 'i'^0xff, 0 }; + +/* +======================= +CL_SendConnectPacket + +called by CL_Connect_f and CL_CheckResend +====================== +*/ +void CL_SendConnectPacket (void) +{ + netadr_t adr; + char data[2048]; + double t1, t2; +// JACK: Fixed bug where DNS lookups would cause two connects real fast +// Now, adds lookup time to the connect time. +// Should I add it to realtime instead?!?! + + if (cls.state != ca_disconnected) + return; + + t1 = Sys_DoubleTime (); + + if (!NET_StringToAdr (cls.servername, &adr)) + { + Con_Printf ("Bad server address\n"); + connect_time = -1; + return; + } + + if (adr.port == 0) + adr.port = BigShort (27500); + t2 = Sys_DoubleTime (); + + connect_time = realtime+t2-t1; // for retransmit requests + + cls.qport = Cvar_VariableValue("qport"); + +// Con_Printf ("Connecting to %s...\n", cls.servername); + sprintf (data, "\xff\xff\xff\xff" "connect %i %i %i \"%s\"\n", + PROTOCOL_VERSION, cls.qport, cls.challenge, cls.userinfo); + NET_SendPacket (net_clientsocket, strlen(data), data, adr); +} + +/* +================= +CL_CheckForResend + +Resend a connect message if the last one has timed out +================= +*/ +void CL_CheckForResend (void) +{ + netadr_t adr; + char data[2048]; + double t1, t2; + + if (connect_time == -1) + return; + if (cls.state != ca_disconnected) + return; + if (connect_time && realtime - connect_time < 5.0) + return; + + t1 = Sys_DoubleTime (); + if (!NET_StringToAdr (cls.servername, &adr)) + { + Con_Printf ("Bad server address\n"); + connect_time = -1; + return; + } + t2 = Sys_DoubleTime (); + + if (adr.port == 0) + adr.port = BigShort (27500); + + connect_time = realtime+t2-t1; // for retransmit requests + + Con_Printf ("Connecting to %s...\n", cls.servername); + sprintf (data, "\xff\xff\xff\xff" "getchallenge\n"); + NET_SendPacket (net_clientsocket, strlen(data), data, adr); +} + +void CL_BeginServerConnect(void) +{ + connect_time = 0; + CL_CheckForResend(); +} + +/* +================ +CL_Connect_f + +================ +*/ +void CL_Connect_f (void) +{ + char *server; + + if (Cmd_Argc() != 2) + { + Con_Printf ("usage: connect \n"); + return; + } + + server = Cmd_Argv (1); + + CL_Disconnect (); + + Q_strncpyz (cls.servername, server, sizeof(cls.servername)); + CL_BeginServerConnect(); +} + + +/* +===================== +CL_ClearState + +===================== +*/ + +void CL_ClearState (void) +{ + int i; + + S_StopAllSounds (true); + + Con_DPrintf ("Clearing memory\n"); + D_FlushCaches (); + Mod_ClearAll (); + Hunk_FreeToLowMark (host_hunklevel); + + CL_ClearTEnts (); + memset (cl_baselines, 0, sizeof(cl_baselines)); + +// wipe the entire cl structure + memset (&cl, 0, sizeof(cl)); + + SZ_Clear (&cls.netchan.message); + +// clear other arrays + memset (cl_efrags, 0, sizeof(cl_efrags)); + memset (cl_dlights, 0, sizeof(cl_dlights)); + memset (cl_lightstyle, 0, sizeof(cl_lightstyle)); + +// +// allocate the efrags and chain together into a free list +// + cl.free_efrags = cl_efrags; + for (i=0 ; i= ca_connected) + { + if (!cls.demoplayback) + Con_Printf ("Dup connect received. Ignored.\n"); + return; + } + Netchan_Setup (&cls.netchan, net_from, cls.qport, net_clientsocket); + MSG_WriteChar (&cls.netchan.message, clc_stringcmd); + MSG_WriteString (&cls.netchan.message, "new"); + cls.state = ca_connected; + Con_Printf ("Connected.\n"); + allowremotecmd = false; // localid required now for remote cmds + return; + } + // remote command from gui front end + if (c == A2C_CLIENT_COMMAND) + { + char cmdtext[2048]; + + Con_Printf ("client command\n"); + + if ((*(unsigned *)net_from.ip != *(unsigned *)net_local_adr.ip + && *(unsigned *)net_from.ip != htonl(INADDR_LOOPBACK)) ) + { + Con_Printf ("Command packet from remote host. Ignored.\n"); + return; + } +#ifdef _WIN32 + ShowWindow (mainwindow, SW_RESTORE); + SetForegroundWindow (mainwindow); +#endif + s = MSG_ReadString (); + + Q_strncpyz (cmdtext, s, sizeof(cmdtext)); + + s = MSG_ReadString (); + + while (*s && isspace(*s)) + s++; + while (*s && isspace(s[strlen(s) - 1])) + s[strlen(s) - 1] = 0; + + if (!allowremotecmd && (!*localid.string || strcmp(localid.string, s))) { + if (!*localid.string) { + Con_Printf("===========================\n"); + Con_Printf("Command packet received from local host, but no " + "localid has been set. You may need to upgrade your server " + "browser.\n"); + Con_Printf("===========================\n"); + return; + } + Con_Printf("===========================\n"); + Con_Printf("Invalid localid on command packet received from local host. " + "\n|%s| != |%s|\n" + "You may need to reload your server browser and QuakeWorld.\n", + s, localid.string); + Con_Printf("===========================\n"); + Cvar_Set(&localid, ""); + return; + } + + Cbuf_AddText (cmdtext); + Cbuf_AddText ("\n"); + allowremotecmd = false; + return; + } + // print command from somewhere + if (c == A2C_PRINT) + { + Con_Printf ("print\n"); + + s = MSG_ReadString (); + Con_Print (s); + return; + } + + // ping from somewhere + if (c == A2A_PING) + { + char data[6]; + + Con_Printf ("ping\n"); + + data[0] = 0xff; + data[1] = 0xff; + data[2] = 0xff; + data[3] = 0xff; + data[4] = A2A_ACK; + data[5] = 0; + + NET_SendPacket (net_clientsocket, 6, &data, net_from); + return; + } + + if (c == S2C_CHALLENGE) { + Con_Printf ("challenge\n"); + + s = MSG_ReadString (); + cls.challenge = atoi(s); + CL_SendConnectPacket (); + return; + } + + if (c == svc_disconnect) { + if (cls.demoplayback) + Host_EndGame ("End of demo"); +// else +// Host_EndGame ("Server disconnected"); + return; + } + + Con_Printf ("unknown: %c\n", c); +} + +/* +================= +CL_ReadPackets +================= +*/ +void CL_InitInterpolation(float cur, float old); +void CL_ReadPackets (void) +{ + while (CL_GetMessage()) + { + // + // remote command packet + // + if (net_message.cursize == 1 && net_message.data[0] == A2A_ACK) + { + extern double ping_time; + + Con_Printf("ack:%d\n", (int)((realtime - ping_time)*1000)); + return; + } + + if (*(int *)net_message.data == -1) + { + CL_ConnectionlessPacket (); + continue; + } + + if (net_message.cursize < 8 && !cls.demoplayback2) + { + Con_Printf ("%s: Runt packet\n",NET_AdrToString(net_from)); + continue; + } + + // + // packet from server + // + if (!cls.demoplayback && + !NET_CompareAdr (net_from, cls.netchan.remote_address)) + { + Con_DPrintf ("%s:sequenced packet without connection\n" + ,NET_AdrToString(net_from)); + continue; + } + if (!cls.demoplayback2) { + if (!Netchan_Process(&cls.netchan)) + continue; // wasn't accepted for some reason + } else { + MSG_BeginReading (); + } + + CL_ParseServerMessage (); + +// if (cls.demoplayback && cls.state >= ca_active && !CL_DemoBehind()) +// return; + } + + // + // check timeout + // + if (cls.state >= ca_connected + && realtime - cls.netchan.last_received > cl_timeout.value) + { + Con_Printf ("\nServer connection timed out.\n"); + CL_Disconnect (); + return; + } + +} + +//============================================================================= + +void CL_InitCommands (void); + +/* +================= +CL_Init +================= +*/ +void CL_Init (void) +{ + extern cvar_t baseskin; + extern cvar_t noskins; + + cls.state = ca_disconnected; + + Info_SetValueForKey (cls.userinfo, "name", "unnamed", MAX_INFO_STRING); + Info_SetValueForKey (cls.userinfo, "topcolor", "0", MAX_INFO_STRING); + Info_SetValueForKey (cls.userinfo, "bottomcolor", "0", MAX_INFO_STRING); + Info_SetValueForKey (cls.userinfo, "rate", "2500", MAX_INFO_STRING); + Info_SetValueForKey (cls.userinfo, "msg", "1", MAX_INFO_STRING); + +#ifndef RELEASE_VERSION + Info_SetValueForStarKey (cls.userinfo, "*qwe_ver", QWE_VERSION, MAX_INFO_STRING); +#endif + + CL_InitInput (); + CL_InitTEnts (); + CL_InitPrediction (); + CL_InitCam (); + Pmove_Init (); + +// +// register our commands +// + Cvar_RegisterVariable (&show_fps); + Cvar_RegisterVariable (&host_speeds); + Cvar_RegisterVariable (&developer); + + Cvar_RegisterVariable (&cl_warncmd); + Cvar_RegisterVariable (&cl_upspeed); + Cvar_RegisterVariable (&cl_forwardspeed); + Cvar_RegisterVariable (&cl_backspeed); + Cvar_RegisterVariable (&cl_sidespeed); + Cvar_RegisterVariable (&cl_movespeedkey); + Cvar_RegisterVariable (&cl_yawspeed); + Cvar_RegisterVariable (&cl_pitchspeed); + Cvar_RegisterVariable (&cl_anglespeedkey); + Cvar_RegisterVariable (&cl_shownet); + Cvar_RegisterVariable (&cl_sbar); + Cvar_RegisterVariable (&cl_hudswap); + Cvar_RegisterVariable (&cl_maxfps); + Cvar_RegisterVariable (&cl_timeout); + Cvar_RegisterVariable (&lookspring); + Cvar_RegisterVariable (&lookstrafe); + Cvar_RegisterVariable (&sensitivity); + + Cvar_RegisterVariable (&m_pitch); + Cvar_RegisterVariable (&m_yaw); + Cvar_RegisterVariable (&m_forward); + Cvar_RegisterVariable (&m_side); + + Cvar_RegisterVariable (&rcon_password); + Cvar_RegisterVariable (&rcon_address); + + Cvar_RegisterVariable (&entlatency); + Cvar_RegisterVariable (&cl_predict_players2); + Cvar_RegisterVariable (&cl_predict_players); + Cvar_RegisterVariable (&cl_solid_players); + + Cvar_RegisterVariable (&localid); + + Cvar_RegisterVariable (&baseskin); + Cvar_RegisterVariable (&noskins); + + // ZQuake cvars + Cvar_RegisterVariable (&r_rockettrail); + Cvar_RegisterVariable (&r_grenadetrail); + Cvar_RegisterVariable (&r_powerupglow); + Cvar_RegisterVariable (&r_rocketlight); + Cvar_RegisterVariable (&cl_speedjumpfix); + Cvar_RegisterVariable (&cl_demotimescale); + Cvar_RegisterVariable (&cl_deadbodyfilter); + Cvar_RegisterVariable (&cl_explosion); + Cvar_RegisterVariable (&cl_gibfilter); + Cvar_RegisterVariable (&cl_muzzleflash); + Cvar_RegisterVariable (&cl_staticsounds); + Cvar_RegisterVariable (&cl_trueLightning); + Cvar_RegisterVariable (&default_fov); + Cvar_RegisterVariable (&cl_filterdrawviewmodel); + Cvar_RegisterVariable (&qizmo_dir); + + // + // info mirrors + // + Cvar_RegisterVariable (&name); + Cvar_RegisterVariable (&password); + Cvar_RegisterVariable (&spectator); + Cvar_RegisterVariable (&skin); + Cvar_RegisterVariable (&team); + Cvar_RegisterVariable (&topcolor); + Cvar_RegisterVariable (&bottomcolor); + Cvar_RegisterVariable (&rate); + Cvar_RegisterVariable (&msg); + Cvar_RegisterVariable (&noaim); + + + CL_InitCommands (); + + Cmd_AddCommand ("disconnect", CL_Disconnect_f); + Cmd_AddCommand ("connect", CL_Connect_f); + Cmd_AddCommand ("reconnect", CL_Reconnect_f); + + SList_Init(); + SList_Load(); +} + + +/* +================ +Host_EndGame + +Call this to drop to a console without exiting the qwcl +================ +*/ +void Host_EndGame (char *message, ...) +{ + va_list argptr; + char string[1024]; + + va_start (argptr,message); + vsprintf (string,message,argptr); + va_end (argptr); + Con_Printf ("\n===========================\n"); + Con_Printf ("Host_EndGame: %s\n",string); + Con_Printf ("===========================\n\n"); + + CL_Disconnect (); + + longjmp (host_abort, 1); +} + +/* +================ +Host_Error + +This shuts down the client and exits qwcl +================ +*/ +void Host_Error (char *error, ...) +{ + va_list argptr; + char string[1024]; + static qboolean inerror = false; + + if (inerror) + Sys_Error ("Host_Error: recursively entered"); + inerror = true; + + va_start (argptr,error); + vsprintf (string,error,argptr); + va_end (argptr); + Con_Printf ("Host_Error: %s\n",string); + + CL_Disconnect (); + cls.demonum = -1; + + inerror = false; + +// FIXME + Sys_Error ("Host_Error: %s\n",string); +} + + +/* +=============== +Host_WriteConfiguration + +Writes key bindings and archived cvars to config.cfg +=============== +*/ +void Host_WriteConfiguration (void) +{ + FILE *f; + + if (host_initialized) + { + f = fopen (va("%s/config.cfg",com_gamedir), "w"); + if (!f) + { + Con_Printf ("Couldn't write config.cfg.\n"); + return; + } + + Key_WriteBindings (f); + Cvar_WriteVariables (f); + + fclose (f); + } +} + + +//============================================================================ + +void Host_ConnectLocal () +{ + if (cls.state < ca_connected) + Cmd_ExecuteString ("connect local"); + else + cls.state = ca_connected; +} + + +/* +================== +Host_Frame + +Runs all active servers +================== +*/ +void Host_Frame (double time) +{ + static double time1 = 0; + static double time2 = 0; + static double time3 = 0; + int pass1, pass2, pass3; + float fps; + float scale; + static float old; + static int oldparse; + static double oldrealtime2; + extern float nextdemotime, olddemotime; + + if (setjmp (host_abort) ) + return; // something bad happened, or the server disconnected + + // decide the simulation time + + noscale_realtime += time; + if (!cls.demoplayback) + realtime += time; + else + { + scale = cl_demotimescale.value; + if (scale <= 0) scale = 0; + //if (scale < 0.01) scale = 0.01; + if (scale > 10) scale = 10; + realtime += time*scale; + + } + + if (oldrealtime > realtime) + oldrealtime = 0; + + if (oldrealtime2 > noscale_realtime) + oldrealtime2 = 0; + + if (cl_maxfps.value) + fps = max(30.0, min(cl_maxfps.value, 72.0)); + else + fps = max(30.0, min(rate.value/80.0, 72.0)); + + if (!cls.demoplayback && realtime - oldrealtime < 1.0/fps) + return; // framerate is too high + + host_frametime = realtime - oldrealtime; + real_frametime = noscale_realtime - oldrealtime2; + + if (cls.demoplayback && (cl.paused & 2)) { + realtime = oldrealtime; + noscale_realtime = oldrealtime2; + } + + oldrealtime = realtime; + if (host_frametime > 0.2) + host_frametime = 0.2; + + oldrealtime2 = noscale_realtime; + if (real_frametime > 0.2) + real_frametime = 0.2; + + // get new key events + Sys_SendKeyEvents (); + + // allow mice or other external controllers to add commands + IN_Commands (); + + // process console commands + Cbuf_Execute (); + + // fetch results from server + CL_ReadPackets (); + + + if (cls.demoplayback2) + { + player_state_t *self, *oldself; + + Con_Printf("%d, %d\n", cl.parsecount, cls.netchan.incoming_acknowledged); + + self = &cl.frames[cl.parsecount & UPDATE_MASK].playerstate[cl.playernum]; + oldself = &cl.frames[(cls.netchan.outgoing_sequence-1) & UPDATE_MASK].playerstate[cl.playernum]; + self->messagenum = cl.parsecount; + VectorCopy(oldself->origin, self->origin); + VectorCopy(oldself->velocity, self->velocity); + VectorCopy(oldself->viewangles, self->viewangles); + + if (old != nextdemotime) // FIXME: use oldparcecount != cl.parsecount? + { + old = nextdemotime; + CL_InitInterpolation(nextdemotime, olddemotime); + } + + CL_ParseClientdata(); + + cls.netchan.outgoing_sequence = cl.parsecount+1; + CL_Interpolate(); + } + + // process stuffed commands + Cbuf_ExecuteEx (&cbuf_svc); + + // send intentions now + // resend a connection request if necessary + if (cls.state == ca_disconnected) { + CL_CheckForResend (); + } else + CL_SendCmd (); + + if (cls.state >= ca_onserver) // !!! Tonik + { + // Set up prediction for other players + CL_SetUpPlayerPrediction(false); + + // do client side motion prediction + CL_PredictMove (false); + + // Set up prediction for other players + CL_SetUpPlayerPrediction(true); + + // build a refresh entity list + CL_EmitEntities (); + } + + // update video + if (host_speeds.value) + time1 = Sys_DoubleTime (); + + SCR_UpdateScreen (); + + if (host_speeds.value) + time2 = Sys_DoubleTime (); + + // update audio + if (cls.state == ca_active) + { + S_Update (r_origin, vpn, vright, vup); + CL_DecayLights (); + } + else + S_Update (vec3_origin, vec3_origin, vec3_origin, vec3_origin); + + CDAudio_Update(); + + + if (host_speeds.value) + { + pass1 = (time1 - time3)*1000; + time3 = Sys_DoubleTime (); + pass2 = (time2 - time1)*1000; + pass3 = (time3 - time2)*1000; + Con_Printf ("%3i tot %3i server %3i gfx %3i snd\n", + pass1+pass2+pass3, pass1, pass2, pass3); + } + + host_framecount++; + fps_count++; +} + +static void simple_crypt(char *buf, int len) +{ + while (len--) + *buf++ ^= 0xff; +} + +void Host_FixupModelNames(void) +{ + simple_crypt(emodel_name, sizeof(emodel_name) - 1); + simple_crypt(pmodel_name, sizeof(pmodel_name) - 1); + simple_crypt(prespawn_name, sizeof(prespawn_name) - 1); + simple_crypt(modellist_name, sizeof(modellist_name) - 1); + simple_crypt(soundlist_name, sizeof(soundlist_name) - 1); +} + +//============================================================================ + +/* +==================== +Host_Init +==================== +*/ +void Host_Init (quakeparms_t *parms) +{ + COM_InitArgv (parms->argc, parms->argv); + + if (COM_CheckParm ("-minmemory")) + parms->memsize = MINIMUM_MEMORY; + + host_parms = *parms; + + if (parms->memsize < MINIMUM_MEMORY) + Sys_Error ("Only %4.1f megs of memory reported, can't execute game", parms->memsize / (float)0x100000); + + Memory_Init (parms->membase, parms->memsize); + Cbuf_Init (); + Cmd_Init (); + Cvar_Init (); + V_Init (); + TP_Init (); + + COM_Init (); + + Host_FixupModelNames(); + + NET_Init (PORT_CLIENT, 0); + + Netchan_Init (); + + W_LoadWadFile ("gfx.wad"); + + Key_Init (); + Con_Init (); + M_Init (); + Mod_Init (); + + Sys_mkdir(va("%s/%s", com_basedir, "qw")); + +// Con_Printf ("Exe: "__TIME__" "__DATE__"\n"); + Con_Printf ("%4.1f megs RAM used.\n",parms->memsize/ (1024*1024.0)); + + R_InitTextures (); + + host_basepal = (byte *)COM_LoadHunkFile ("gfx/palette.lmp"); + if (!host_basepal) + Sys_Error ("Couldn't load gfx/palette.lmp"); + host_colormap = (byte *)COM_LoadHunkFile ("gfx/colormap.lmp"); + if (!host_colormap) + Sys_Error ("Couldn't load gfx/colormap.lmp"); +#ifdef __linux__ + IN_Init (); + CDAudio_Init (); + VID_Init (host_basepal); + Draw_Init (); + SCR_Init (); + R_Init (); + +// S_Init (); // S_Init is now done as part of VID. Sigh. + + cls.state = ca_disconnected; + Sbar_Init (); + CL_Init (); +#else + VID_Init (host_basepal); + Draw_Init (); + SCR_Init (); + R_Init (); +// S_Init (); // S_Init is now done as part of VID. Sigh. +#ifdef GLQUAKE + S_Init(); +#endif + + cls.state = ca_disconnected; + CDAudio_Init (); + Sbar_Init (); + CL_Init (); + IN_Init (); +#endif + + Cbuf_InsertText ("exec quake.rc\n"); +// Cbuf_AddText ("echo Type connect or use GameSpy to connect to a game.\n"); + Cbuf_AddText ("cl_warncmd 1\n"); + + Hunk_AllocName (0, "-HOST_HUNKLEVEL-"); + host_hunklevel = Hunk_LowMark (); + + host_initialized = true; + +#ifdef RELEASE_VERSION + Con_Printf ("\nClient Version %s\n\n", QWE_VERSION); +#else + Con_Printf ("\nClient Version %s (Build %04d)\n\n", QWE_VERSION, build_number()); +#endif + + Con_Printf ("€ QuakeWorld Initialized ‚\n"); +} + + +/* +=============== +Host_Shutdown + +FIXME: this is a callback from Sys_Quit and Sys_Error. It would be better +to run quit through here before the final handoff to the sys code. +=============== +*/ +void Host_Shutdown(void) +{ + static qboolean isdown = false; + + if (isdown) + { + printf ("recursive shutdown\n"); + return; + } + isdown = true; + + Host_WriteConfiguration (); + + SList_Shutdown (); + CDAudio_Shutdown (); + NET_Shutdown (); + S_Shutdown(); + IN_Shutdown (); + if (host_basepal) + VID_Shutdown(); +} diff --git a/source/cl_math.s b/source/cl_math.s index 5a209e3e..864c6365 100644 --- a/source/cl_math.s +++ b/source/cl_math.s @@ -1,105 +1,105 @@ -/* - cl_math.s - - x86 assembly language math routines - - Copyright (C) 1996-1997 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: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA -*/ - -#include "asm_i386.h" -#include "quakeasm.h" - - -#ifdef id386 - - .data - - .text - -// TODO: rounding needed? -// stack parameter offset -#define val 4 - -.globl C(Invert24To16) -C(Invert24To16): - - movl val(%esp),%ecx - movl $0x100,%edx // 0x10000000000 as dividend - cmpl %edx,%ecx - jle LOutOfRange - - subl %eax,%eax - divl %ecx - - ret - -LOutOfRange: - movl $0xFFFFFFFF,%eax - ret - -#define in 4 -#define out 8 - - .align 2 -.globl C(TransformVector) -C(TransformVector): - movl in(%esp),%eax - movl out(%esp),%edx - - flds (%eax) // in[0] - fmuls C(vright) // in[0]*vright[0] - flds (%eax) // in[0] | in[0]*vright[0] - fmuls C(vup) // in[0]*vup[0] | in[0]*vright[0] - flds (%eax) // in[0] | in[0]*vup[0] | in[0]*vright[0] - fmuls C(vpn) // in[0]*vpn[0] | in[0]*vup[0] | in[0]*vright[0] - - flds 4(%eax) // in[1] | ... - fmuls C(vright)+4 // in[1]*vright[1] | ... - flds 4(%eax) // in[1] | in[1]*vright[1] | ... - fmuls C(vup)+4 // in[1]*vup[1] | in[1]*vright[1] | ... - flds 4(%eax) // in[1] | in[1]*vup[1] | in[1]*vright[1] | ... - fmuls C(vpn)+4 // in[1]*vpn[1] | in[1]*vup[1] | in[1]*vright[1] | ... - fxch %st(2) // in[1]*vright[1] | in[1]*vup[1] | in[1]*vpn[1] | ... - - faddp %st(0),%st(5) // in[1]*vup[1] | in[1]*vpn[1] | ... - faddp %st(0),%st(3) // in[1]*vpn[1] | ... - faddp %st(0),%st(1) // vpn_accum | vup_accum | vright_accum - - flds 8(%eax) // in[2] | ... - fmuls C(vright)+8 // in[2]*vright[2] | ... - flds 8(%eax) // in[2] | in[2]*vright[2] | ... - fmuls C(vup)+8 // in[2]*vup[2] | in[2]*vright[2] | ... - flds 8(%eax) // in[2] | in[2]*vup[2] | in[2]*vright[2] | ... - fmuls C(vpn)+8 // in[2]*vpn[2] | in[2]*vup[2] | in[2]*vright[2] | ... - fxch %st(2) // in[2]*vright[2] | in[2]*vup[2] | in[2]*vpn[2] | ... - - faddp %st(0),%st(5) // in[2]*vup[2] | in[2]*vpn[2] | ... - faddp %st(0),%st(3) // in[2]*vpn[2] | ... - faddp %st(0),%st(1) // vpn_accum | vup_accum | vright_accum - - fstps 8(%edx) // out[2] - fstps 4(%edx) // out[1] - fstps (%edx) // out[0] - - ret - - -#endif // id386 +/* + cl_math.s + + x86 assembly language math routines + + Copyright (C) 1996-1997 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA +*/ + +#include "asm_i386.h" +#include "quakeasm.h" + + +#ifdef id386 + + .data + + .text + +// TODO: rounding needed? +// stack parameter offset +#define val 4 + +.globl C(Invert24To16) +C(Invert24To16): + + movl val(%esp),%ecx + movl $0x100,%edx // 0x10000000000 as dividend + cmpl %edx,%ecx + jle LOutOfRange + + subl %eax,%eax + divl %ecx + + ret + +LOutOfRange: + movl $0xFFFFFFFF,%eax + ret + +#define in 4 +#define out 8 + + .align 2 +.globl C(TransformVector) +C(TransformVector): + movl in(%esp),%eax + movl out(%esp),%edx + + flds (%eax) // in[0] + fmuls C(vright) // in[0]*vright[0] + flds (%eax) // in[0] | in[0]*vright[0] + fmuls C(vup) // in[0]*vup[0] | in[0]*vright[0] + flds (%eax) // in[0] | in[0]*vup[0] | in[0]*vright[0] + fmuls C(vpn) // in[0]*vpn[0] | in[0]*vup[0] | in[0]*vright[0] + + flds 4(%eax) // in[1] | ... + fmuls C(vright)+4 // in[1]*vright[1] | ... + flds 4(%eax) // in[1] | in[1]*vright[1] | ... + fmuls C(vup)+4 // in[1]*vup[1] | in[1]*vright[1] | ... + flds 4(%eax) // in[1] | in[1]*vup[1] | in[1]*vright[1] | ... + fmuls C(vpn)+4 // in[1]*vpn[1] | in[1]*vup[1] | in[1]*vright[1] | ... + fxch %st(2) // in[1]*vright[1] | in[1]*vup[1] | in[1]*vpn[1] | ... + + faddp %st(0),%st(5) // in[1]*vup[1] | in[1]*vpn[1] | ... + faddp %st(0),%st(3) // in[1]*vpn[1] | ... + faddp %st(0),%st(1) // vpn_accum | vup_accum | vright_accum + + flds 8(%eax) // in[2] | ... + fmuls C(vright)+8 // in[2]*vright[2] | ... + flds 8(%eax) // in[2] | in[2]*vright[2] | ... + fmuls C(vup)+8 // in[2]*vup[2] | in[2]*vright[2] | ... + flds 8(%eax) // in[2] | in[2]*vup[2] | in[2]*vright[2] | ... + fmuls C(vpn)+8 // in[2]*vpn[2] | in[2]*vup[2] | in[2]*vright[2] | ... + fxch %st(2) // in[2]*vright[2] | in[2]*vup[2] | in[2]*vpn[2] | ... + + faddp %st(0),%st(5) // in[2]*vup[2] | in[2]*vpn[2] | ... + faddp %st(0),%st(3) // in[2]*vpn[2] | ... + faddp %st(0),%st(1) // vpn_accum | vup_accum | vright_accum + + fstps 8(%edx) // out[2] + fstps 4(%edx) // out[1] + fstps (%edx) // out[0] + + ret + + +#endif // id386 diff --git a/source/cl_parse.c b/source/cl_parse.c index d19f5a20..f416e4af 100644 --- a/source/cl_parse.c +++ b/source/cl_parse.c @@ -1,1726 +1,1725 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// cl_parse.c -- parse a message received from the server - -#include "quakedef.h" -#include "pmove.h" -#include "sbar.h" -#include "sound.h" -#include "teamplay.h" -#include "version.h" - -char *svc_strings[] = -{ - "svc_bad", - "svc_nop", - "svc_disconnect", - "svc_updatestat", - "svc_version", // [long] server version - "svc_setview", // [short] entity number - "svc_sound", // - "svc_time", // [float] server time - "svc_print", // [string] null terminated string - "svc_stufftext", // [string] stuffed into client's console buffer - // the string should be \n terminated - "svc_setangle", // [vec3] set the view angle to this absolute value - - "svc_serverdata", // [long] version ... - "svc_lightstyle", // [byte] [string] - "svc_updatename", // [byte] [string] - "svc_updatefrags", // [byte] [short] - "svc_clientdata", // - "svc_stopsound", // - "svc_updatecolors", // [byte] [byte] - "svc_particle", // [vec3] - "svc_damage", // [byte] impact [byte] blood [vec3] from - - "svc_spawnstatic", - "OBSOLETE svc_spawnbinary", - "svc_spawnbaseline", - - "svc_temp_entity", // - "svc_setpause", - "svc_signonnum", - "svc_centerprint", - "svc_killedmonster", - "svc_foundsecret", - "svc_spawnstaticsound", - "svc_intermission", - "svc_finale", - - "svc_cdtrack", - "svc_sellscreen", - - "svc_smallkick", - "svc_bigkick", - - "svc_updateping", - "svc_updateentertime", - - "svc_updatestatlong", - "svc_muzzleflash", - "svc_updateuserinfo", - "svc_download", - "svc_playerinfo", - "svc_nails", - "svc_choke", - "svc_modellist", - "svc_soundlist", - "svc_packetentities", - "svc_deltapacketentities", - "svc_maxspeed", - "svc_entgravity", - - "svc_setinfo", - "svc_serverinfo", - "svc_updatepl", - "NEW PROTOCOL", - "NEW PROTOCOL", - "NEW PROTOCOL", - "NEW PROTOCOL", - "NEW PROTOCOL", - "NEW PROTOCOL", - "NEW PROTOCOL", - "NEW PROTOCOL", - "NEW PROTOCOL", - "NEW PROTOCOL", - "NEW PROTOCOL", - "NEW PROTOCOL", - "NEW PROTOCOL" -}; - -int oldparsecountmod; -int parsecountmod; -double parsecounttime; - -int cl_spikeindex, cl_playerindex, cl_flagindex; - -int cl_h_playerindex, cl_gib1index, cl_gib2index, cl_gib3index; // Tonik -int cl_rocketindex, cl_grenadeindex; // Tonik - -extern int stat_packets; -extern int stat_headers; -extern int stat_size; - - -//============================================================================= - -int packet_latency[NET_TIMINGS]; - -int CL_CalcNet (void) -{ - int a, i; - frame_t *frame; - int lost; - int packetcount; - - for (i=cls.netchan.outgoing_sequence-UPDATE_BACKUP+1 - ; i <= cls.netchan.outgoing_sequence - ; i++) - { - frame = &cl.frames[i&UPDATE_MASK]; - if (frame->receivedtime == -1) - packet_latency[i&NET_TIMINGSMASK] = 9999; // dropped - else if (frame->receivedtime == -2) - packet_latency[i&NET_TIMINGSMASK] = 10000; // choked - else if (frame->receivedtime == -3) - packet_latency[i&NET_TIMINGSMASK] = -1; // choked by c2spps - else if (frame->invalid) - packet_latency[i&NET_TIMINGSMASK] = 9998; // invalid delta - else - packet_latency[i&NET_TIMINGSMASK] = (frame->receivedtime - frame->senttime)*20; - } - - lost = 0; - packetcount = 0; - for (a=0 ; achecksum2)); - MSG_WriteString (&cls.netchan.message, va(prespawn_name, cl.servercount, cl.worldmodel->checksum2)); -} - -/* -================= -Sound_NextDownload -================= -*/ -void Sound_NextDownload (void) -{ - char *s; - int i; - - if (cls.downloadnumber == 0) - { - Con_Printf ("Checking sounds...\n"); - cls.downloadnumber = 1; - } - - cls.downloadtype = dl_sound; - for ( - ; cl.sound_name[cls.downloadnumber][0] - ; cls.downloadnumber++) - { - s = cl.sound_name[cls.downloadnumber]; - if (!CL_CheckOrDownloadFile(va("sound/%s",s))) - return; // started a download - } - - for (i=1 ; i - cl_h_playerindex = -1; - cl_gib1index = cl_gib2index = cl_gib3index = -1; - cl_rocketindex = cl_grenadeindex = -1; -// <-- Tonik - MSG_WriteByte (&cls.netchan.message, clc_stringcmd); -// MSG_WriteString (&cls.netchan.message, va("modellist %i 0", cl.servercount)); - MSG_WriteString (&cls.netchan.message, va(modellist_name, cl.servercount, 0)); -} - - -/* -====================== -CL_RequestNextDownload -====================== -*/ -void CL_RequestNextDownload (void) -{ - switch (cls.downloadtype) - { - case dl_single: - break; - case dl_skin: - Skin_NextDownload (); - break; - case dl_model: - Model_NextDownload (); - break; - case dl_sound: - Sound_NextDownload (); - break; - case dl_none: - default: - Con_DPrintf("Unknown download type.\n"); - } -} - -/* -===================== -CL_ParseDownload - -A download message has been received from the server -===================== -*/ -void CL_ParseDownload (void) -{ - int size, percent; - byte name[1024]; - int r; - - - // read the data - size = MSG_ReadShort (); - percent = MSG_ReadByte (); - - if (cls.demoplayback) { - if (size > 0) - msg_readcount += size; - return; // not in demo playback - } - - if (size == -1) - { - Con_Printf ("File not found.\n"); - if (cls.download) - { - Con_Printf ("cls.download shouldn't have been set\n"); - fclose (cls.download); - cls.download = NULL; - } - CL_RequestNextDownload (); - return; - } - - // open the file if not opened yet - if (!cls.download) - { - if (strncmp(cls.downloadtempname,"skins/",6)) - sprintf (name, "%s/%s", com_gamedir, cls.downloadtempname); - else - sprintf (name, "qw/%s", cls.downloadtempname); - - COM_CreatePath (name); - - cls.download = fopen (name, "wb"); - if (!cls.download) - { - msg_readcount += size; - Con_Printf ("Failed to open %s\n", cls.downloadtempname); - CL_RequestNextDownload (); - return; - } - } - - fwrite (net_message.data + msg_readcount, 1, size, cls.download); - msg_readcount += size; - - if (percent != 100) - { -// change display routines by zoid - // request next block -#if 0 - Con_Printf ("."); - if (10*(percent/10) != cls.downloadpercent) - { - cls.downloadpercent = 10*(percent/10); - Con_Printf ("%i%%", cls.downloadpercent); - } -#endif - cls.downloadpercent = percent; - - MSG_WriteByte (&cls.netchan.message, clc_stringcmd); - SZ_Print (&cls.netchan.message, "nextdl"); - } - else - { - char oldn[MAX_OSPATH]; - char newn[MAX_OSPATH]; - -#if 0 - Con_Printf ("100%%\n"); -#endif - - fclose (cls.download); - - // rename the temp file to it's final name - if (strcmp(cls.downloadtempname, cls.downloadname)) { - if (strncmp(cls.downloadtempname,"skins/",6)) { - sprintf (oldn, "%s/%s", com_gamedir, cls.downloadtempname); - sprintf (newn, "%s/%s", com_gamedir, cls.downloadname); - } else { - sprintf (oldn, "qw/%s", cls.downloadtempname); - sprintf (newn, "qw/%s", cls.downloadname); - } - r = rename (oldn, newn); - if (r) - Con_Printf ("failed to rename.\n"); - } - - cls.download = NULL; - cls.downloadpercent = 0; - - // get another file if needed - - CL_RequestNextDownload (); - } -} - -static byte *upload_data; -static int upload_pos; -static int upload_size; - -void CL_NextUpload(void) -{ - byte buffer[1024]; - int r; - int percent; - int size; - - if (!upload_data) - return; - - r = upload_size - upload_pos; - if (r > 768) - r = 768; - memcpy(buffer, upload_data + upload_pos, r); - MSG_WriteByte (&cls.netchan.message, clc_upload); - MSG_WriteShort (&cls.netchan.message, r); - - upload_pos += r; - size = upload_size; - if (!size) - size = 1; - percent = upload_pos*100/size; - MSG_WriteByte (&cls.netchan.message, percent); - SZ_Write (&cls.netchan.message, buffer, r); - -Con_DPrintf ("UPLOAD: %6d: %d written\n", upload_pos - r, r); - - if (upload_pos != upload_size) - return; - - Con_Printf ("Upload completed\n"); - - free(upload_data); - upload_data = 0; - upload_pos = upload_size = 0; -} - -void CL_StartUpload (byte *data, int size) -{ - if (cls.state < ca_onserver) - return; // gotta be connected - - // override - if (upload_data) - free(upload_data); - -Con_DPrintf("Upload starting of %d...\n", size); - - upload_data = Q_Malloc (size); - memcpy(upload_data, data, size); - upload_size = size; - upload_pos = 0; - - CL_NextUpload(); -} - -qboolean CL_IsUploading(void) -{ - if (upload_data) - return true; - return false; -} - -void CL_StopUpload(void) -{ - if (upload_data) - free(upload_data); - upload_data = NULL; -} - -/* -===================================================================== - - SERVER CONNECTING MESSAGES - -===================================================================== -*/ - -/* -================== -CL_ParseServerData -================== -*/ -void CL_ParseServerData (void) -{ - char *str; - FILE *f; - char fn[MAX_OSPATH]; - qboolean cflag = false; - extern char gamedirfile[MAX_OSPATH]; - int protover; - extern cshift_t cshift_empty; - extern float nextdemotime, olddemotime; - - Con_DPrintf ("Serverdata packet received.\n"); -// -// wipe the client_state_t struct -// - CL_ClearState (); - memset (&cshift_empty, 0, sizeof(cshift_empty)); // Tonik - -// parse protocol version number -// allow 2.2 and 2.29 demos to play - protover = MSG_ReadLong (); - if (protover != PROTOCOL_VERSION && - !(cls.demoplayback && (protover == 26 || protover == 27 || protover == 28))) - Host_EndGame ("Server returned version %i, not %i\nYou probably need to upgrade.\nCheck http://www.quakeworld.net/", protover, PROTOCOL_VERSION); - - cl.servercount = MSG_ReadLong (); - - // game directory - str = MSG_ReadString (); - - cl.teamfortress = !Q_strcasecmp(str, "fortress"); - if (cl.teamfortress) { - extern cvar_t v_iyaw_cycle, v_iroll_cycle, v_ipitch_cycle, - v_iyaw_level, v_iroll_level, v_ipitch_level, v_idlescale; - cbuf_current = &cbuf_svc; // hack - Cvar_SetValue (&v_iyaw_cycle, 2); - Cvar_SetValue (&v_iroll_cycle, 0.5); - Cvar_SetValue (&v_ipitch_cycle, 1); - Cvar_SetValue (&v_iyaw_level, 0.3); - Cvar_SetValue (&v_iroll_level, 0.1); - Cvar_SetValue (&v_ipitch_level, 0.3); - Cvar_SetValue (&v_idlescale, 0); - } - - if (stricmp(gamedirfile, str)) { - // save current config - Host_WriteConfiguration (); - cflag = true; - } - - COM_Gamedir(str); - - //ZOID--run the autoexec.cfg in the gamedir - //if it exists - if (cflag) { - int cl_warncmd_val = cl_warncmd.value; - // FIXME: _snprintf is Win32 only - _snprintf(fn, sizeof(fn), "%s/%s", com_gamedir, "config.cfg"); - if ((f = fopen(fn, "r")) != NULL) { - fclose(f); - Cbuf_AddText ("cl_warncmd 0\n"); - Cbuf_AddText ("exec config.cfg\n"); - } - _snprintf(fn, sizeof(fn), "%s/%s", com_gamedir, "frontend.cfg"); - if ((f = fopen(fn, "r")) != NULL) { - fclose(f); - Cbuf_AddText ("cl_warncmd 0\n"); - Cbuf_AddText ("exec frontend.cfg\n"); - } - _snprintf(fn,sizeof(fn), "cl_warncmd %d\n", cl_warncmd_val); - Cbuf_AddText(fn); - } - - if (cls.demoplayback2) { - cls.netchan.last_received = nextdemotime = olddemotime = MSG_ReadFloat(); - cl.playernum = 31; - cl.spectator = true; - } else { - // parse player slot, high bit means spectator - cl.playernum = MSG_ReadByte (); - if (cl.playernum & 128) - { - cl.spectator = true; - cl.playernum &= ~128; - //Con_Printf("pn:%d\n", cl.playernum); - } - } - - // get the full level name - str = MSG_ReadString (); - Q_strncpyz (cl.levelname, str, sizeof(cl.levelname)); - - // get the movevars - movevars.gravity = MSG_ReadFloat(); - movevars.stopspeed = MSG_ReadFloat(); - movevars.maxspeed = MSG_ReadFloat(); - movevars.spectatormaxspeed = MSG_ReadFloat(); - movevars.accelerate = MSG_ReadFloat(); - movevars.airaccelerate = MSG_ReadFloat(); - movevars.wateraccelerate = MSG_ReadFloat(); - movevars.friction = MSG_ReadFloat(); - movevars.waterfriction = MSG_ReadFloat(); - movevars.entgravity = MSG_ReadFloat(); - - // seperate the printfs so the server message can have a color - Con_Printf("\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n\n"); - Con_Printf ("%c%s\n", 2, str); - - // ask for the sound list next - memset(cl.sound_name, 0, sizeof(cl.sound_name)); - MSG_WriteByte (&cls.netchan.message, clc_stringcmd); -// MSG_WriteString (&cls.netchan.message, va("soundlist %i 0", cl.servercount)); - MSG_WriteString (&cls.netchan.message, va(soundlist_name, cl.servercount, 0)); - - // now waiting for downloads, etc - cls.state = ca_onserver; -} - -/* -================== -CL_ParseSoundlist -================== -*/ -void CL_ParseSoundlist (void) -{ - int numsounds; - char *str; - int n; - -// precache sounds -// memset (cl.sound_precache, 0, sizeof(cl.sound_precache)); - - numsounds = MSG_ReadByte(); - - for (;;) { - str = MSG_ReadString (); - if (!str[0]) - break; - numsounds++; - if (numsounds == MAX_SOUNDS) - Host_EndGame ("Server sent too many sound_precache"); - strcpy (cl.sound_name[numsounds], str); - } - - n = MSG_ReadByte(); - - if (n) { - MSG_WriteByte (&cls.netchan.message, clc_stringcmd); -// MSG_WriteString (&cls.netchan.message, va("soundlist %i %i", cl.servercount, n)); - MSG_WriteString (&cls.netchan.message, va(soundlist_name, cl.servercount, n)); - return; - } - - cls.downloadnumber = 0; - cls.downloadtype = dl_sound; - Sound_NextDownload (); -} - -/* -================== -CL_ParseModellist -================== -*/ -void CL_ParseModellist (void) -{ - int nummodels; - char *str; - int n; - -// precache models and note certain default indexes - nummodels = MSG_ReadByte(); - - for (;;) - { - str = MSG_ReadString (); - if (!str[0]) - break; - nummodels++; - if (nummodels==MAX_MODELS) - Host_EndGame ("Server sent too many model_precache"); - strcpy (cl.model_name[nummodels], str); - - if (!strcmp(cl.model_name[nummodels],"progs/spike.mdl")) - cl_spikeindex = nummodels; - if (!strcmp(cl.model_name[nummodels],"progs/player.mdl")) - cl_playerindex = nummodels; - if (!strcmp(cl.model_name[nummodels],"progs/flag.mdl")) - cl_flagindex = nummodels; - -// Tonik --> - else if (!strcmp(cl.model_name[nummodels],"progs/h_player.mdl")) - cl_h_playerindex = nummodels; - else if (!strcmp(cl.model_name[nummodels],"progs/gib1.mdl")) - cl_gib1index = nummodels; - else if (!strcmp(cl.model_name[nummodels],"progs/gib2.mdl")) - cl_gib2index = nummodels; - else if (!strcmp(cl.model_name[nummodels],"progs/gib3.mdl")) - cl_gib3index = nummodels; - else if (!strcmp(cl.model_name[nummodels],"progs/missile.mdl")) - cl_rocketindex = nummodels; - else if (!strcmp(cl.model_name[nummodels],"progs/grenade.mdl")) - cl_grenadeindex = nummodels; -// <-- Tonik - } - - n = MSG_ReadByte(); - - if (n) { - MSG_WriteByte (&cls.netchan.message, clc_stringcmd); -// MSG_WriteString (&cls.netchan.message, va("modellist %i %i", cl.servercount, n)); - MSG_WriteString (&cls.netchan.message, va(modellist_name, cl.servercount, n)); - return; - } - - cls.downloadnumber = 0; - cls.downloadtype = dl_model; - Model_NextDownload (); -} - -/* -================== -CL_ParseBaseline -================== -*/ -void CL_ParseBaseline (entity_state_t *es) -{ - int i; - - es->modelindex = MSG_ReadByte (); - es->frame = MSG_ReadByte (); - es->colormap = MSG_ReadByte(); - es->skinnum = MSG_ReadByte(); - for (i=0 ; i<3 ; i++) - { - es->origin[i] = MSG_ReadCoord (); - es->angles[i] = MSG_ReadAngle (); - } -} - - - -/* -===================== -CL_ParseStatic - -Static entities are non-interactive world objects -like torches -===================== -*/ -void CL_ParseStatic (void) -{ - entity_t *ent; - int i; - entity_state_t es; - - CL_ParseBaseline (&es); - - i = cl.num_statics; - if (i >= MAX_STATIC_ENTITIES) - Host_EndGame ("Too many static entities"); - ent = &cl_static_entities[i]; - cl.num_statics++; - -// copy it to the current state - ent->model = cl.model_precache[es.modelindex]; - ent->frame = es.frame; - ent->colormap = vid.colormap; - ent->skinnum = es.skinnum; - - VectorCopy (es.origin, ent->origin); - VectorCopy (es.angles, ent->angles); - - R_AddEfrags (ent); -} - -/* -=================== -CL_ParseStaticSound -=================== -*/ -void CL_ParseStaticSound (void) -{ - extern cvar_t cl_staticsounds; - vec3_t org; - int sound_num, vol, atten; - int i; - - for (i=0 ; i<3 ; i++) - org[i] = MSG_ReadCoord (); - sound_num = MSG_ReadByte (); - vol = MSG_ReadByte (); - atten = MSG_ReadByte (); - - if (cl_staticsounds.value) - S_StaticSound (cl.sound_precache[sound_num], org, vol, atten); -} - - - -/* -===================================================================== - -ACTION MESSAGES - -===================================================================== -*/ - -/* -================== -CL_ParseStartSoundPacket -================== -*/ -void CL_ParseStartSoundPacket(void) -{ - vec3_t pos; - int channel, ent, tracknum; - int sound_num; - int volume; - float attenuation; - int i; - - channel = MSG_ReadShort(); - - if (channel & SND_VOLUME) - volume = MSG_ReadByte (); - else - volume = DEFAULT_SOUND_PACKET_VOLUME; - - if (channel & SND_ATTENUATION) - attenuation = MSG_ReadByte () / 64.0; - else - attenuation = DEFAULT_SOUND_PACKET_ATTENUATION; - - sound_num = MSG_ReadByte (); - - for (i=0 ; i<3 ; i++) - pos[i] = MSG_ReadCoord (); - - ent = (channel>>3)&1023; - channel &= 7; - - if (ent > MAX_EDICTS) - Host_EndGame ("CL_ParseStartSoundPacket: ent = %i", ent); - - tracknum = Cam_TrackNum(); - if (cl.spectator && tracknum != -1 && ent == tracknum + 1) - ent = cl.playernum + 1; - - S_StartSound (ent, channel, cl.sound_precache[sound_num], pos, volume/255.0, attenuation); - - if (ent == cl.playernum+1) - TP_CheckPickupSound (cl.sound_name[sound_num]); -} - - -/* -================== -CL_ParseClientdata - -Server information pertaining to this client only, sent every frame -================== -*/ -void CL_ParseClientdata (void) -{ - int i; - float latency; - frame_t *frame; - -// calculate simulated time of message - oldparsecountmod = parsecountmod; - - i = cls.netchan.incoming_acknowledged; - if (cls.demoplayback2) - cl.oldparsecount = i - 1; - //else - // cl.oldparsecount = cl.parsecount; - - cl.parsecount = i; - i &= UPDATE_MASK; - parsecountmod = i; - frame = &cl.frames[i]; - if (cls.demoplayback2) - frame->senttime = realtime - host_frametime;//realtime; - parsecounttime = cl.frames[i].senttime; - - frame->receivedtime = realtime; - -// calculate latency - latency = frame->receivedtime - frame->senttime; - - if (latency < 0 || latency > 1.0) - { -// Con_Printf ("Odd latency: %5.2f\n", latency); - } - else - { - // drift the average latency towards the observed latency - if (latency < cls.latency) - cls.latency = latency; - else - cls.latency += 0.001; // drift up, so correction are needed - } -} - -/* -===================== -CL_NewTranslation -===================== -*/ -#ifdef GLQUAKE -void CL_NewTranslation (int slot) -{ - int teamplay, tracknum; - //char s[512]; - player_info_t *player; - static char *team = ""; - - if (slot > MAX_CLIENTS) - Sys_Error ("CL_NewTranslation: slot > MAX_CLIENTS"); - - player = &cl.players[slot]; - if (player->spectator) - return; - -// teamcolor/enemycolor --> - player->topcolor = player->real_topcolor; - player->bottomcolor = player->real_bottomcolor; - - if (cl.spectator) - { - tracknum = Cam_TrackNum(); - if (tracknum != -1) - // team = NULL; - //else - team = cl.players[tracknum].team; - } else - team = cl.players[cl.playernum].team; - - //strcpy (s, cl.players[cl.playernum].team); - teamplay = atoi(Info_ValueForKey(cl.serverinfo, "teamplay")); - - if ( !cl.teamfortress && !(cl.fpd & FPD_NO_FORCE_COLOR) ) { - if (cl_teamtopcolor >= 0 && teamplay && team != NULL && - !strcmp(player->team, team)) - { - player->topcolor = cl_teamtopcolor; - player->bottomcolor = cl_teambottomcolor; - } - - if (cl_enemytopcolor >= 0 && slot != cl.playernum && - (!teamplay || team == NULL || strcmp(player->team, team))) - { - player->topcolor = cl_enemytopcolor; - player->bottomcolor = cl_enemybottomcolor; - } - } -// <-- - - R_TranslatePlayerSkin(slot); -} -#else -void CL_NewTranslation (int slot) -{ - int teamplay, tracknum; - int i, j; - int top, bottom; - byte *dest, *source; - player_info_t *player; - char s[512]; - static char *team = ""; - - if (slot > MAX_CLIENTS) - Sys_Error ("CL_NewTranslation: slot > MAX_CLIENTS"); - - player = &cl.players[slot]; - if (player->spectator) - return; - - strcpy(s, Info_ValueForKey(player->userinfo, "skin")); - COM_StripExtension(s, s); - if (player->skin && !stricmp(s, player->skin->name)) - player->skin = NULL; - -// teamcolor/enemycolor --> - player->topcolor = player->real_topcolor; - player->bottomcolor = player->real_bottomcolor; - - if (cl.spectator) - { - tracknum = Cam_TrackNum(); - if (tracknum != -1) - // team = NULL; - //else - team = cl.players[tracknum].team; - } else { - team = cl.players[cl.playernum].team; - } - - teamplay = atoi(Info_ValueForKey(cl.serverinfo, "teamplay")); - - if ( !cl.teamfortress && !(cl.fpd & FPD_NO_FORCE_COLOR) ) { - if (cl_teamtopcolor >= 0 && teamplay && team != NULL && - !strcmp(player->team, team)) - { - player->topcolor = cl_teamtopcolor; - player->bottomcolor = cl_teambottomcolor; - } - - if (cl_enemytopcolor >= 0 && slot != cl.playernum && - (!teamplay || team == NULL || strcmp(player->team, team))) - { - player->topcolor = cl_enemytopcolor; - player->bottomcolor = cl_enemybottomcolor; - } - } -// <-- - - if (player->_topcolor != player->topcolor || - player->_bottomcolor != player->bottomcolor || !player->skin) - { - player->_topcolor = player->topcolor; - player->_bottomcolor = player->bottomcolor; - - dest = player->translations; - source = vid.colormap; - memcpy (dest, vid.colormap, sizeof(player->translations)); - top = player->topcolor; - if (top > 13 || top < 0) - top = 13; - top *= 16; - bottom = player->bottomcolor; - if (bottom > 13 || bottom < 0) - bottom = 13; - bottom *= 16; - - for (i=0 ; iname, Info_ValueForKey (player->userinfo, "name"), sizeof(player->name)); - player->real_topcolor = atoi(Info_ValueForKey (player->userinfo, "topcolor")); - player->real_bottomcolor = atoi(Info_ValueForKey (player->userinfo, "bottomcolor")); - strcpy (player->team, Info_ValueForKey (player->userinfo, "team")); - - if (Info_ValueForKey (player->userinfo, "*spectator")[0]) - player->spectator = true; - else - player->spectator = false; - - if (cls.state == ca_active) - Skin_Find (player); - - Sbar_Changed (); - if (slot == cl.playernum && (cl_teamtopcolor >= 0 || cl_enemytopcolor >= 0) && - strcmp(player->team, player->_team)) - { - int i; - //strcpy (cl_playerteam, Info_ValueForKey(player->userinfo, "team")); - for (i=0 ; i < MAX_CLIENTS ; i++) - CL_NewTranslation (i); - } - else - CL_NewTranslation (slot); - - strcpy (player->_team, player->team); -} - -/* -============== -CL_UpdateUserinfo -============== -*/ -void CL_UpdateUserinfo (void) -{ - int slot; - player_info_t *player; - - slot = MSG_ReadByte (); - if (slot >= MAX_CLIENTS) - Host_EndGame ("CL_ParseServerMessage: svc_updateuserinfo > MAX_CLIENTS"); - - player = &cl.players[slot]; - player->userid = MSG_ReadLong (); - Q_strncpyz (player->userinfo, MSG_ReadString(), sizeof(player->userinfo)); - - CL_ProcessUserInfo (slot, player); -} - -/* -============== -CL_SetInfo -============== -*/ -void CL_SetInfo (void) -{ - int slot; - player_info_t *player; - char key[MAX_INFO_STRING]; - char value[MAX_INFO_STRING]; - - slot = MSG_ReadByte (); - if (slot >= MAX_CLIENTS) - Host_EndGame ("CL_ParseServerMessage: svc_setinfo > MAX_CLIENTS"); - - player = &cl.players[slot]; - - Q_strncpyz (key, MSG_ReadString(), sizeof(key)); - Q_strncpyz (value, MSG_ReadString(), sizeof(value)); - - Con_DPrintf("SETINFO %s: %s=%s\n", player->name, key, value); - - Info_SetValueForKey (player->userinfo, key, value, MAX_INFO_STRING); - - CL_ProcessUserInfo (slot, player); -} - - -/* -============== -CL_ProcessServerInfo - -Called by CL_FullServerinfo_f and CL_ParseServerInfoChange -============== -*/ -void CL_ProcessServerInfo (void) -{ - char *p; - static int old_teamplay = 0; - static int old_fpd = 0; - int teamplay; // FIXME: make it cl.teamplay? - int i; - - cl.fpd = Q_atof(Info_ValueForKey(cl.serverinfo, "fpd")); - teamplay = Q_atof(Info_ValueForKey(cl.serverinfo, "teamplay")); - - p = Info_ValueForKey(cl.serverinfo, "deathmatch"); - if (*p) - cl.gametype = Q_atof(p) ? GAME_DEATHMATCH : GAME_COOP; - else - cl.gametype = GAME_DEATHMATCH; // assume GAME_DEATHMATCH by default - - if (teamplay != old_teamplay || cl.fpd != old_fpd) { - old_teamplay = teamplay; - old_fpd = cl.fpd; - if (cls.state >= ca_connected) { - for (i = 0; i < MAX_CLIENTS ; i++) - CL_NewTranslation (i); - } - } -} - -/* -============== -CL_ParseServerInfoChange -============== -*/ -void CL_ParseServerInfoChange (void) -{ - char key[MAX_INFO_STRING]; - char value[MAX_INFO_STRING]; - - Q_strncpyz (key, MSG_ReadString(), sizeof(key)); - Q_strncpyz (value, MSG_ReadString(), sizeof(value)); - - Con_DPrintf("SERVERINFO: %s=%s\n", key, value); - - Info_SetValueForKey (cl.serverinfo, key, value, MAX_SERVERINFO_STRING); - - CL_ProcessServerInfo (); -} - - -/* -============== -CL_ParsePrint -============== -*/ - -void CL_ParsePrint (void) -{ - char *s, str[1024]; - char *p; - int len; - int level; - - level = MSG_ReadByte (); - s = MSG_ReadString (); - - //stat_size += 3 + sizeof(s); - //Con_Printf("msg_size:%d\n", stat_size); - - strncat (cl.sprint_buf, s, sizeof(cl.sprint_buf)-1); - - while ( (p=strchr(cl.sprint_buf, '\n')) != NULL ) { - len = p - cl.sprint_buf + 1; - memcpy(str, cl.sprint_buf, len); - str[len] = '\0'; - strcpy (cl.sprint_buf, p+1); - - if (level == PRINT_CHAT) - { - char *p; - int flags; - - flags = TP_CategorizeMessage (str); - TP_CheckVersionRequest (str); - if (cl_nofake.value == 1 || (cl_nofake.value == 2 && flags != 2)) { - for (p = str; *p; p++) - if (*p == 13) - *p = '#'; - } - con_ormask = 128; - S_LocalSound ("misc/talk.wav"); - } - Con_Printf ("%s", str); - con_ormask = 0; - TP_SearchForMsgTriggers (str, level); - } - -} - - -/* -============== -CL_ParseStufftext -============== -*/ -void CL_ParseStufftext (void) -{ - char *s; - - s = MSG_ReadString (); - - Con_DPrintf ("stufftext: %s\n", s); - //if (!strncmp(s, "fullserverinfo", 10)) - // return; - - Cbuf_AddTextEx (&cbuf_svc, s); - - // QW servers send this without the ending \n - if ( !strcmp (s, "cmd snap") || - // QuakeForge servers up to Beta 6 send this without the \n - (!strncmp (s, "r_skyname ", 10) && !strchr (s, '\n')) ) - { - Cbuf_AddTextEx (&cbuf_svc, "\n"); - } -} - - -/* -===================== -CL_SetStat -===================== -*/ - -void CL_SetStat (int stat, int value) -{ - int j; - if (stat < 0 || stat >= MAX_CL_STATS) - Host_EndGame ("CL_SetStat: %i is invalid", stat); - - if (cls.demoplayback2) { - cl.players[cls.lastto].stats[stat]=value; - if ( Cam_TrackNum() != cls.lastto ) - return; - } - - Sbar_Changed (); - - if (stat == STAT_ITEMS) - { // set flash times - Sbar_Changed (); - for (j=0 ; j<32 ; j++) - if ( (value & (1<= MAX_CLIENTS) - { - // a monster firing - num_ent = cl.frames[cls.netchan.incoming_sequence&UPDATE_MASK].packet_entities.num_entities; - for (j = 0; j < num_ent; j++) - { - ent = &cl.frames[cls.netchan.incoming_sequence&UPDATE_MASK].packet_entities.entities[j]; - if (ent->number == i) - { - dl = CL_AllocDlight (-i); - VectorCopy (ent->origin, dl->origin); - AngleVectors (ent->angles, fv, rv, uv); - VectorMA (dl->origin, 18, fv, dl->origin); - dl->radius = 200 + (rand()&31); - dl->minlight = 32; - dl->die = cl.time + 0.1; - dl->color[0] = 0.2; - dl->color[1] = 0.1; - dl->color[2] = 0.05; - dl->color[3] = 0.7; - break; - } - } - return; - } - -#ifdef GLQUAKE - // don't draw our own muzzle flash in gl if flashblending - if (i-1 == cl.playernum && gl_flashblend.value) - return; -#endif - - if (cl_muzzleflash.value == 2 && i-1 == cl.playernum) - return; - - pl = &cl.frames[parsecountmod].playerstate[i-1]; - - dl = CL_AllocDlight (-i); - VectorCopy (pl->origin, dl->origin); - AngleVectors (pl->viewangles, fv, rv, uv); - - VectorMA (dl->origin, 18, fv, dl->origin); - dl->radius = 200 + (rand()&31); - dl->minlight = 32; - dl->die = cl.time + 0.1; - dl->color[0] = 0.2; - dl->color[1] = 0.1; - dl->color[2] = 0.05; - dl->color[3] = 0.7; -} - - -#define SHOWNET(x) if(cl_shownet.value==2)Con_Printf ("%3i:%s\n", msg_readcount-1, x); -/* -===================== -CL_ParseServerMessage -===================== -*/ -int received_framecount; -extern int fixangle; -void CL_ParseServerMessage (void) -{ - int cmd; - int i, j; - - received_framecount = host_framecount; - cl.last_servermessage = realtime; - -// -// if recording demos, copy the message out -// - if (cl_shownet.value == 1) - Con_Printf ("%i ",net_message.cursize); - else if (cl_shownet.value == 2) - Con_Printf ("------------------\n"); - - - CL_ParseClientdata (); - CL_ClearProjectiles(); - -// -// parse the message -// - while (1) - { - if (msg_badread) - { - Host_EndGame ("CL_ParseServerMessage: Bad server message"); - break; - } - - cmd = MSG_ReadByte (); - - if (cmd == -1) - { - msg_readcount++; // so the EOM showner has the right value - SHOWNET("END OF MESSAGE"); - break; - } - - SHOWNET(svc_strings[cmd]); - - // other commands - switch (cmd) - { - default: - Host_EndGame ("CL_ParseServerMessage: Illegible server message"); - break; - - case svc_nop: -// Con_Printf ("svc_nop\n"); - break; - - case svc_disconnect: - if (cls.state == ca_connected) - Host_EndGame ("Server disconnected\n" - "Server version may not be compatible"); - else - Host_EndGame ("Server disconnected"); - break; - - case svc_print: - CL_ParsePrint (); - break; - - case svc_centerprint: - SCR_CenterPrint (MSG_ReadString ()); - break; - - case svc_stufftext: - CL_ParseStufftext (); - break; - - case svc_damage: - V_ParseDamage (); - break; - - case svc_serverdata: - Cbuf_Execute (); // make sure any stuffed commands are done - CL_ParseServerData (); - vid.recalc_refdef = true; // leave full screen intermission - break; - - case svc_setangle: - if (cls.demoplayback2) { - j = MSG_ReadByte(); - fixangle |= 1 << j; - if (j != Cam_TrackNum()) - for (i=0; i<3; i++) - MSG_ReadAngle(); - } - - if (!cls.demoplayback2 || (cls.demoplayback2 && j == Cam_TrackNum())) - { - for (i=0 ; i<3 ; i++) - cl.viewangles[i] = MSG_ReadAngle (); - } -// cl.viewangles[PITCH] = cl.viewangles[ROLL] = 0; - break; - - case svc_lightstyle: - i = MSG_ReadByte (); - if (i >= MAX_LIGHTSTYLES) - Host_EndGame ("svc_lightstyle > MAX_LIGHTSTYLES"); - strncpy (cl_lightstyle[i].map, MSG_ReadString(), MAX_STYLESTRING); - cl_lightstyle[i].length = strlen(cl_lightstyle[i].map); - break; - - case svc_sound: - CL_ParseStartSoundPacket(); - break; - - case svc_stopsound: - i = MSG_ReadShort(); - S_StopSound(i>>3, i&7); - break; - - case svc_updatefrags: - Sbar_Changed (); - i = MSG_ReadByte (); - if (i >= MAX_CLIENTS) - Host_EndGame ("CL_ParseServerMessage: svc_updatefrags > MAX_CLIENTS"); - cl.players[i].frags = MSG_ReadShort (); - break; - - case svc_updateping: - i = MSG_ReadByte (); - if (i >= MAX_CLIENTS) - Host_EndGame ("CL_ParseServerMessage: svc_updateping > MAX_CLIENTS"); - cl.players[i].ping = MSG_ReadShort (); - break; - - case svc_updatepl: - i = MSG_ReadByte (); - if (i >= MAX_CLIENTS) - Host_EndGame ("CL_ParseServerMessage: svc_updatepl > MAX_CLIENTS"); - cl.players[i].pl = MSG_ReadByte (); - break; - - case svc_updateentertime: - // time is sent over as seconds ago - i = MSG_ReadByte (); - if (i >= MAX_CLIENTS) - Host_EndGame ("CL_ParseServerMessage: svc_updateentertime > MAX_CLIENTS"); - cl.players[i].entertime = realtime - MSG_ReadFloat (); - //Con_Printf("enter:%f, real:%f\n", realtime - cl.players[i].entertime, realtime); - break; - - case svc_spawnbaseline: - i = MSG_ReadShort (); - CL_ParseBaseline (&cl_baselines[i]); - break; - case svc_spawnstatic: - CL_ParseStatic (); - break; - case svc_temp_entity: - CL_ParseTEnt (); - break; - - case svc_killedmonster: - cl.stats[STAT_MONSTERS]++; - break; - - case svc_foundsecret: - cl.stats[STAT_SECRETS]++; - break; - - case svc_updatestat: - i = MSG_ReadByte (); - j = MSG_ReadByte (); - //stat_size += 8; - CL_SetStat (i, j); - break; - case svc_updatestatlong: - i = MSG_ReadByte (); - j = MSG_ReadLong (); - //stat_size += 11; - CL_SetStat (i, j); - break; - - case svc_spawnstaticsound: - CL_ParseStaticSound (); - break; - - case svc_cdtrack: - cl.cdtrack = MSG_ReadByte (); - CDAudio_Play ((byte)cl.cdtrack, true); - break; - - case svc_intermission: - cl.intermission = 1; - cl.completed_time = realtime; - vid.recalc_refdef = true; // go to full screen - for (i=0 ; i<3 ; i++) - cl.simorg[i] = MSG_ReadCoord (); - for (i=0 ; i<3 ; i++) - cl.simangles[i] = MSG_ReadAngle (); - VectorCopy (vec3_origin, cl.simvel); - TP_ExecTrigger ("f_mapend"); - break; - - case svc_finale: - cl.intermission = 2; - cl.completed_time = realtime; - vid.recalc_refdef = true; // go to full screen - SCR_CenterPrint (MSG_ReadString ()); - break; - - case svc_sellscreen: - Cmd_ExecuteString ("help"); - break; - - case svc_smallkick: - cl.punchangle = -2; - break; - case svc_bigkick: - cl.punchangle = -4; - break; - - case svc_muzzleflash: - CL_MuzzleFlash (); - break; - - case svc_updateuserinfo: - CL_UpdateUserinfo (); - break; - - case svc_setinfo: - CL_SetInfo (); - break; - - case svc_serverinfo: - CL_ParseServerInfoChange (); - break; - - case svc_download: - CL_ParseDownload (); - break; - - case svc_playerinfo: - CL_ParsePlayerinfo (); - break; - - case svc_nails: - CL_ParseProjectiles (false); - break; - case svc_nails2: - CL_ParseProjectiles (true); - break; - - case svc_chokecount: // some preceding packets were choked - i = MSG_ReadByte (); - for (j=0 ; j + "svc_time", // [float] server time + "svc_print", // [string] null terminated string + "svc_stufftext", // [string] stuffed into client's console buffer + // the string should be \n terminated + "svc_setangle", // [vec3] set the view angle to this absolute value + + "svc_serverdata", // [long] version ... + "svc_lightstyle", // [byte] [string] + "svc_updatename", // [byte] [string] + "svc_updatefrags", // [byte] [short] + "svc_clientdata", // + "svc_stopsound", // + "svc_updatecolors", // [byte] [byte] + "svc_particle", // [vec3] + "svc_damage", // [byte] impact [byte] blood [vec3] from + + "svc_spawnstatic", + "OBSOLETE svc_spawnbinary", + "svc_spawnbaseline", + + "svc_temp_entity", // + "svc_setpause", + "svc_signonnum", + "svc_centerprint", + "svc_killedmonster", + "svc_foundsecret", + "svc_spawnstaticsound", + "svc_intermission", + "svc_finale", + + "svc_cdtrack", + "svc_sellscreen", + + "svc_smallkick", + "svc_bigkick", + + "svc_updateping", + "svc_updateentertime", + + "svc_updatestatlong", + "svc_muzzleflash", + "svc_updateuserinfo", + "svc_download", + "svc_playerinfo", + "svc_nails", + "svc_choke", + "svc_modellist", + "svc_soundlist", + "svc_packetentities", + "svc_deltapacketentities", + "svc_maxspeed", + "svc_entgravity", + + "svc_setinfo", + "svc_serverinfo", + "svc_updatepl", + "NEW PROTOCOL", + "NEW PROTOCOL", + "NEW PROTOCOL", + "NEW PROTOCOL", + "NEW PROTOCOL", + "NEW PROTOCOL", + "NEW PROTOCOL", + "NEW PROTOCOL", + "NEW PROTOCOL", + "NEW PROTOCOL", + "NEW PROTOCOL", + "NEW PROTOCOL", + "NEW PROTOCOL" +}; + +int oldparsecountmod; +int parsecountmod; +double parsecounttime; + +int cl_spikeindex, cl_playerindex, cl_flagindex; + +int cl_h_playerindex, cl_gib1index, cl_gib2index, cl_gib3index; // Tonik +int cl_rocketindex, cl_grenadeindex; // Tonik + +extern int stat_packets; +extern int stat_headers; +extern int stat_size; + + +//============================================================================= + +int packet_latency[NET_TIMINGS]; + +int CL_CalcNet (void) +{ + int a, i; + frame_t *frame; + int lost; + int packetcount; + + for (i=cls.netchan.outgoing_sequence-UPDATE_BACKUP+1 + ; i <= cls.netchan.outgoing_sequence + ; i++) + { + frame = &cl.frames[i&UPDATE_MASK]; + if (frame->receivedtime == -1) + packet_latency[i&NET_TIMINGSMASK] = 9999; // dropped + else if (frame->receivedtime == -2) + packet_latency[i&NET_TIMINGSMASK] = 10000; // choked + else if (frame->receivedtime == -3) + packet_latency[i&NET_TIMINGSMASK] = -1; // choked by c2spps + else if (frame->invalid) + packet_latency[i&NET_TIMINGSMASK] = 9998; // invalid delta + else + packet_latency[i&NET_TIMINGSMASK] = (frame->receivedtime - frame->senttime)*20; + } + + lost = 0; + packetcount = 0; + for (a=0 ; achecksum2)); + MSG_WriteString (&cls.netchan.message, va(prespawn_name, cl.servercount, cl.worldmodel->checksum2)); +} + +/* +================= +Sound_NextDownload +================= +*/ +void Sound_NextDownload (void) +{ + char *s; + int i; + + if (cls.downloadnumber == 0) + { + Con_Printf ("Checking sounds...\n"); + cls.downloadnumber = 1; + } + + cls.downloadtype = dl_sound; + for ( + ; cl.sound_name[cls.downloadnumber][0] + ; cls.downloadnumber++) + { + s = cl.sound_name[cls.downloadnumber]; + if (!CL_CheckOrDownloadFile(va("sound/%s",s))) + return; // started a download + } + + for (i=1 ; i + cl_h_playerindex = -1; + cl_gib1index = cl_gib2index = cl_gib3index = -1; + cl_rocketindex = cl_grenadeindex = -1; +// <-- Tonik + MSG_WriteByte (&cls.netchan.message, clc_stringcmd); +// MSG_WriteString (&cls.netchan.message, va("modellist %i 0", cl.servercount)); + MSG_WriteString (&cls.netchan.message, va(modellist_name, cl.servercount, 0)); +} + + +/* +====================== +CL_RequestNextDownload +====================== +*/ +void CL_RequestNextDownload (void) +{ + switch (cls.downloadtype) + { + case dl_single: + break; + case dl_skin: + Skin_NextDownload (); + break; + case dl_model: + Model_NextDownload (); + break; + case dl_sound: + Sound_NextDownload (); + break; + case dl_none: + default: + Con_DPrintf("Unknown download type.\n"); + } +} + +/* +===================== +CL_ParseDownload + +A download message has been received from the server +===================== +*/ +void CL_ParseDownload (void) +{ + int size, percent; + byte name[1024]; + int r; + + + // read the data + size = MSG_ReadShort (); + percent = MSG_ReadByte (); + + if (cls.demoplayback) { + if (size > 0) + msg_readcount += size; + return; // not in demo playback + } + + if (size == -1) + { + Con_Printf ("File not found.\n"); + if (cls.download) + { + fclose (cls.download); + cls.download = NULL; + } + CL_RequestNextDownload (); + return; + } + + // open the file if not opened yet + if (!cls.download) + { + if (strncmp(cls.downloadtempname,"skins/",6)) + sprintf (name, "%s/%s", com_gamedir, cls.downloadtempname); + else + sprintf (name, "qw/%s", cls.downloadtempname); + + COM_CreatePath (name); + + cls.download = fopen (name, "wb"); + if (!cls.download) + { + msg_readcount += size; + Con_Printf ("Failed to open %s\n", cls.downloadtempname); + CL_RequestNextDownload (); + return; + } + } + + fwrite (net_message.data + msg_readcount, 1, size, cls.download); + msg_readcount += size; + + if (percent != 100) + { +// change display routines by zoid + // request next block +#if 0 + Con_Printf ("."); + if (10*(percent/10) != cls.downloadpercent) + { + cls.downloadpercent = 10*(percent/10); + Con_Printf ("%i%%", cls.downloadpercent); + } +#endif + cls.downloadpercent = percent; + + MSG_WriteByte (&cls.netchan.message, clc_stringcmd); + SZ_Print (&cls.netchan.message, "nextdl"); + } + else + { + char oldn[MAX_OSPATH]; + char newn[MAX_OSPATH]; + +#if 0 + Con_Printf ("100%%\n"); +#endif + + fclose (cls.download); + + // rename the temp file to it's final name + if (strcmp(cls.downloadtempname, cls.downloadname)) { + if (strncmp(cls.downloadtempname,"skins/",6)) { + sprintf (oldn, "%s/%s", com_gamedir, cls.downloadtempname); + sprintf (newn, "%s/%s", com_gamedir, cls.downloadname); + } else { + sprintf (oldn, "qw/%s", cls.downloadtempname); + sprintf (newn, "qw/%s", cls.downloadname); + } + r = rename (oldn, newn); + if (r) + Con_Printf ("failed to rename.\n"); + } + + cls.download = NULL; + cls.downloadpercent = 0; + + // get another file if needed + + CL_RequestNextDownload (); + } +} + +static byte *upload_data; +static int upload_pos; +static int upload_size; + +void CL_NextUpload(void) +{ + byte buffer[1024]; + int r; + int percent; + int size; + + if (!upload_data) + return; + + r = upload_size - upload_pos; + if (r > 768) + r = 768; + memcpy(buffer, upload_data + upload_pos, r); + MSG_WriteByte (&cls.netchan.message, clc_upload); + MSG_WriteShort (&cls.netchan.message, r); + + upload_pos += r; + size = upload_size; + if (!size) + size = 1; + percent = upload_pos*100/size; + MSG_WriteByte (&cls.netchan.message, percent); + SZ_Write (&cls.netchan.message, buffer, r); + +Con_DPrintf ("UPLOAD: %6d: %d written\n", upload_pos - r, r); + + if (upload_pos != upload_size) + return; + + Con_Printf ("Upload completed\n"); + + free(upload_data); + upload_data = 0; + upload_pos = upload_size = 0; +} + +void CL_StartUpload (byte *data, int size) +{ + if (cls.state < ca_onserver) + return; // gotta be connected + + // override + if (upload_data) + free(upload_data); + +Con_DPrintf("Upload starting of %d...\n", size); + + upload_data = Q_Malloc (size); + memcpy(upload_data, data, size); + upload_size = size; + upload_pos = 0; + + CL_NextUpload(); +} + +qboolean CL_IsUploading(void) +{ + if (upload_data) + return true; + return false; +} + +void CL_StopUpload(void) +{ + if (upload_data) + free(upload_data); + upload_data = NULL; +} + +/* +===================================================================== + + SERVER CONNECTING MESSAGES + +===================================================================== +*/ + +/* +================== +CL_ParseServerData +================== +*/ +void CL_ParseServerData (void) +{ + char *str; + FILE *f; + char fn[MAX_OSPATH]; + qboolean cflag = false; + extern char gamedirfile[MAX_OSPATH]; + int protover; + extern cshift_t cshift_empty; + extern float nextdemotime, olddemotime; + + Con_DPrintf ("Serverdata packet received.\n"); +// +// wipe the client_state_t struct +// + CL_ClearState (); + memset (&cshift_empty, 0, sizeof(cshift_empty)); // Tonik + +// parse protocol version number +// allow 2.2 and 2.29 demos to play + protover = MSG_ReadLong (); + if (protover != PROTOCOL_VERSION && + !(cls.demoplayback && (protover == 26 || protover == 27 || protover == 28))) + Host_EndGame ("Server returned version %i, not %i\nYou probably need to upgrade.\nCheck http://www.quakeworld.net/", protover, PROTOCOL_VERSION); + + cl.servercount = MSG_ReadLong (); + + // game directory + str = MSG_ReadString (); + + cl.teamfortress = !Q_strcasecmp(str, "fortress"); + if (cl.teamfortress) { + extern cvar_t v_iyaw_cycle, v_iroll_cycle, v_ipitch_cycle, + v_iyaw_level, v_iroll_level, v_ipitch_level, v_idlescale; + cbuf_current = &cbuf_svc; // hack + Cvar_SetValue (&v_iyaw_cycle, 2); + Cvar_SetValue (&v_iroll_cycle, 0.5); + Cvar_SetValue (&v_ipitch_cycle, 1); + Cvar_SetValue (&v_iyaw_level, 0.3); + Cvar_SetValue (&v_iroll_level, 0.1); + Cvar_SetValue (&v_ipitch_level, 0.3); + Cvar_SetValue (&v_idlescale, 0); + } + + if (stricmp(gamedirfile, str)) { + // save current config + Host_WriteConfiguration (); + cflag = true; + } + + COM_Gamedir(str); + + //ZOID--run the autoexec.cfg in the gamedir + //if it exists + if (cflag) { + int cl_warncmd_val = cl_warncmd.value; + // FIXME: _snprintf is Win32 only + _snprintf(fn, sizeof(fn), "%s/%s", com_gamedir, "config.cfg"); + if ((f = fopen(fn, "r")) != NULL) { + fclose(f); + Cbuf_AddText ("cl_warncmd 0\n"); + Cbuf_AddText ("exec config.cfg\n"); + } + _snprintf(fn, sizeof(fn), "%s/%s", com_gamedir, "frontend.cfg"); + if ((f = fopen(fn, "r")) != NULL) { + fclose(f); + Cbuf_AddText ("cl_warncmd 0\n"); + Cbuf_AddText ("exec frontend.cfg\n"); + } + _snprintf(fn,sizeof(fn), "cl_warncmd %d\n", cl_warncmd_val); + Cbuf_AddText(fn); + } + + if (cls.demoplayback2) { + realtime = cls.netchan.last_received = nextdemotime = olddemotime = cls.basetime = MSG_ReadFloat(); + cl.playernum = 31; + cl.spectator = true; + } else { + // parse player slot, high bit means spectator + cl.playernum = MSG_ReadByte (); + if (cl.playernum & 128) + { + cl.spectator = true; + cl.playernum &= ~128; + //Con_Printf("pn:%d\n", cl.playernum); + } + } + + // get the full level name + str = MSG_ReadString (); + Q_strncpyz (cl.levelname, str, sizeof(cl.levelname)); + + // get the movevars + movevars.gravity = MSG_ReadFloat(); + movevars.stopspeed = MSG_ReadFloat(); + movevars.maxspeed = MSG_ReadFloat(); + movevars.spectatormaxspeed = MSG_ReadFloat(); + movevars.accelerate = MSG_ReadFloat(); + movevars.airaccelerate = MSG_ReadFloat(); + movevars.wateraccelerate = MSG_ReadFloat(); + movevars.friction = MSG_ReadFloat(); + movevars.waterfriction = MSG_ReadFloat(); + movevars.entgravity = MSG_ReadFloat(); + + // seperate the printfs so the server message can have a color + Con_Printf("\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n\n"); + Con_Printf ("%c%s\n", 2, str); + + // ask for the sound list next + memset(cl.sound_name, 0, sizeof(cl.sound_name)); + MSG_WriteByte (&cls.netchan.message, clc_stringcmd); +// MSG_WriteString (&cls.netchan.message, va("soundlist %i 0", cl.servercount)); + MSG_WriteString (&cls.netchan.message, va(soundlist_name, cl.servercount, 0)); + + // now waiting for downloads, etc + cls.state = ca_onserver; +} + +/* +================== +CL_ParseSoundlist +================== +*/ +void CL_ParseSoundlist (void) +{ + int numsounds; + char *str; + int n; + +// precache sounds +// memset (cl.sound_precache, 0, sizeof(cl.sound_precache)); + + numsounds = MSG_ReadByte(); + + for (;;) { + str = MSG_ReadString (); + if (!str[0]) + break; + numsounds++; + if (numsounds == MAX_SOUNDS) + Host_EndGame ("Server sent too many sound_precache"); + strcpy (cl.sound_name[numsounds], str); + } + + n = MSG_ReadByte(); + + if (n) { + MSG_WriteByte (&cls.netchan.message, clc_stringcmd); +// MSG_WriteString (&cls.netchan.message, va("soundlist %i %i", cl.servercount, n)); + MSG_WriteString (&cls.netchan.message, va(soundlist_name, cl.servercount, n)); + return; + } + + cls.downloadnumber = 0; + cls.downloadtype = dl_sound; + Sound_NextDownload (); +} + +/* +================== +CL_ParseModellist +================== +*/ +void CL_ParseModellist (void) +{ + int nummodels; + char *str; + int n; + +// precache models and note certain default indexes + nummodels = MSG_ReadByte(); + + for (;;) + { + str = MSG_ReadString (); + if (!str[0]) + break; + nummodels++; + if (nummodels==MAX_MODELS) + Host_EndGame ("Server sent too many model_precache"); + strcpy (cl.model_name[nummodels], str); + + if (!strcmp(cl.model_name[nummodels],"progs/spike.mdl")) + cl_spikeindex = nummodels; + if (!strcmp(cl.model_name[nummodels],"progs/player.mdl")) + cl_playerindex = nummodels; + if (!strcmp(cl.model_name[nummodels],"progs/flag.mdl")) + cl_flagindex = nummodels; + +// Tonik --> + else if (!strcmp(cl.model_name[nummodels],"progs/h_player.mdl")) + cl_h_playerindex = nummodels; + else if (!strcmp(cl.model_name[nummodels],"progs/gib1.mdl")) + cl_gib1index = nummodels; + else if (!strcmp(cl.model_name[nummodels],"progs/gib2.mdl")) + cl_gib2index = nummodels; + else if (!strcmp(cl.model_name[nummodels],"progs/gib3.mdl")) + cl_gib3index = nummodels; + else if (!strcmp(cl.model_name[nummodels],"progs/missile.mdl")) + cl_rocketindex = nummodels; + else if (!strcmp(cl.model_name[nummodels],"progs/grenade.mdl")) + cl_grenadeindex = nummodels; +// <-- Tonik + } + + n = MSG_ReadByte(); + + if (n) { + MSG_WriteByte (&cls.netchan.message, clc_stringcmd); +// MSG_WriteString (&cls.netchan.message, va("modellist %i %i", cl.servercount, n)); + MSG_WriteString (&cls.netchan.message, va(modellist_name, cl.servercount, n)); + return; + } + + cls.downloadnumber = 0; + cls.downloadtype = dl_model; + Model_NextDownload (); +} + +/* +================== +CL_ParseBaseline +================== +*/ +void CL_ParseBaseline (entity_state_t *es) +{ + int i; + + es->modelindex = MSG_ReadByte (); + es->frame = MSG_ReadByte (); + es->colormap = MSG_ReadByte(); + es->skinnum = MSG_ReadByte(); + for (i=0 ; i<3 ; i++) + { + es->origin[i] = MSG_ReadCoord (); + es->angles[i] = MSG_ReadAngle (); + } +} + + + +/* +===================== +CL_ParseStatic + +Static entities are non-interactive world objects +like torches +===================== +*/ +void CL_ParseStatic (void) +{ + entity_t *ent; + int i; + entity_state_t es; + + CL_ParseBaseline (&es); + + i = cl.num_statics; + if (i >= MAX_STATIC_ENTITIES) + Host_EndGame ("Too many static entities"); + ent = &cl_static_entities[i]; + cl.num_statics++; + +// copy it to the current state + ent->model = cl.model_precache[es.modelindex]; + ent->frame = es.frame; + ent->colormap = vid.colormap; + ent->skinnum = es.skinnum; + + VectorCopy (es.origin, ent->origin); + VectorCopy (es.angles, ent->angles); + + R_AddEfrags (ent); +} + +/* +=================== +CL_ParseStaticSound +=================== +*/ +void CL_ParseStaticSound (void) +{ + extern cvar_t cl_staticsounds; + vec3_t org; + int sound_num, vol, atten; + int i; + + for (i=0 ; i<3 ; i++) + org[i] = MSG_ReadCoord (); + sound_num = MSG_ReadByte (); + vol = MSG_ReadByte (); + atten = MSG_ReadByte (); + + if (cl_staticsounds.value) + S_StaticSound (cl.sound_precache[sound_num], org, vol, atten); +} + + + +/* +===================================================================== + +ACTION MESSAGES + +===================================================================== +*/ + +/* +================== +CL_ParseStartSoundPacket +================== +*/ +void CL_ParseStartSoundPacket(void) +{ + vec3_t pos; + int channel, ent, tracknum; + int sound_num; + int volume; + float attenuation; + int i; + + channel = MSG_ReadShort(); + + if (channel & SND_VOLUME) + volume = MSG_ReadByte (); + else + volume = DEFAULT_SOUND_PACKET_VOLUME; + + if (channel & SND_ATTENUATION) + attenuation = MSG_ReadByte () / 64.0; + else + attenuation = DEFAULT_SOUND_PACKET_ATTENUATION; + + sound_num = MSG_ReadByte (); + + for (i=0 ; i<3 ; i++) + pos[i] = MSG_ReadCoord (); + + ent = (channel>>3)&1023; + channel &= 7; + + if (ent > MAX_EDICTS) + Host_EndGame ("CL_ParseStartSoundPacket: ent = %i", ent); + + tracknum = Cam_TrackNum(); + if (cl.spectator && tracknum != -1 && ent == tracknum + 1) + ent = cl.playernum + 1; + + S_StartSound (ent, channel, cl.sound_precache[sound_num], pos, volume/255.0, attenuation); + + if (ent == cl.playernum+1) + TP_CheckPickupSound (cl.sound_name[sound_num]); +} + + +/* +================== +CL_ParseClientdata + +Server information pertaining to this client only, sent every frame +================== +*/ +void CL_ParseClientdata (void) +{ + int i; + float latency; + frame_t *frame; + +// calculate simulated time of message + oldparsecountmod = parsecountmod; + + i = cls.netchan.incoming_acknowledged; + if (cls.demoplayback2) + cl.oldparsecount = i - 1; + //else + // cl.oldparsecount = cl.parsecount; + + cl.parsecount = i; + i &= UPDATE_MASK; + parsecountmod = i; + frame = &cl.frames[i]; + if (cls.demoplayback2) + frame->senttime = realtime - host_frametime;//realtime; + parsecounttime = cl.frames[i].senttime; + + frame->receivedtime = realtime; + +// calculate latency + latency = frame->receivedtime - frame->senttime; + + if (latency < 0 || latency > 1.0) + { +// Con_Printf ("Odd latency: %5.2f\n", latency); + } + else + { + // drift the average latency towards the observed latency + if (latency < cls.latency) + cls.latency = latency; + else + cls.latency += 0.001; // drift up, so correction are needed + } +} + +/* +===================== +CL_NewTranslation +===================== +*/ +#ifdef GLQUAKE +void CL_NewTranslation (int slot) +{ + int teamplay, tracknum; + //char s[512]; + player_info_t *player; + static char *team = ""; + + if (slot > MAX_CLIENTS) + Sys_Error ("CL_NewTranslation: slot > MAX_CLIENTS"); + + player = &cl.players[slot]; + if (player->spectator) + return; + +// teamcolor/enemycolor --> + player->topcolor = player->real_topcolor; + player->bottomcolor = player->real_bottomcolor; + + if (cl.spectator) + { + tracknum = Cam_TrackNum(); + if (tracknum != -1) + // team = NULL; + //else + team = cl.players[tracknum].team; + } else + team = cl.players[cl.playernum].team; + + //strcpy (s, cl.players[cl.playernum].team); + teamplay = atoi(Info_ValueForKey(cl.serverinfo, "teamplay")); + + if ( !cl.teamfortress && !(cl.fpd & FPD_NO_FORCE_COLOR) ) { + if (cl_teamtopcolor >= 0 && teamplay && team != NULL && + !strcmp(player->team, team)) + { + player->topcolor = cl_teamtopcolor; + player->bottomcolor = cl_teambottomcolor; + } + + if (cl_enemytopcolor >= 0 && slot != cl.playernum && + (!teamplay || team == NULL || strcmp(player->team, team))) + { + player->topcolor = cl_enemytopcolor; + player->bottomcolor = cl_enemybottomcolor; + } + } +// <-- + + R_TranslatePlayerSkin(slot); +} +#else +void CL_NewTranslation (int slot) +{ + int teamplay, tracknum; + int i, j; + int top, bottom; + byte *dest, *source; + player_info_t *player; + char s[512]; + static char *team = ""; + + if (slot > MAX_CLIENTS) + Sys_Error ("CL_NewTranslation: slot > MAX_CLIENTS"); + + player = &cl.players[slot]; + if (player->spectator) + return; + + strcpy(s, Info_ValueForKey(player->userinfo, "skin")); + COM_StripExtension(s, s); + if (player->skin && !stricmp(s, player->skin->name)) + player->skin = NULL; + +// teamcolor/enemycolor --> + player->topcolor = player->real_topcolor; + player->bottomcolor = player->real_bottomcolor; + + if (cl.spectator) + { + tracknum = Cam_TrackNum(); + if (tracknum != -1) + // team = NULL; + //else + team = cl.players[tracknum].team; + } else { + team = cl.players[cl.playernum].team; + } + + teamplay = atoi(Info_ValueForKey(cl.serverinfo, "teamplay")); + + if ( !cl.teamfortress && !(cl.fpd & FPD_NO_FORCE_COLOR) ) { + if (cl_teamtopcolor >= 0 && teamplay && team != NULL && + !strcmp(player->team, team)) + { + player->topcolor = cl_teamtopcolor; + player->bottomcolor = cl_teambottomcolor; + } + + if (cl_enemytopcolor >= 0 && slot != cl.playernum && + (!teamplay || team == NULL || strcmp(player->team, team))) + { + player->topcolor = cl_enemytopcolor; + player->bottomcolor = cl_enemybottomcolor; + } + } +// <-- + + if (player->_topcolor != player->topcolor || + player->_bottomcolor != player->bottomcolor || !player->skin) + { + player->_topcolor = player->topcolor; + player->_bottomcolor = player->bottomcolor; + + dest = player->translations; + source = vid.colormap; + memcpy (dest, vid.colormap, sizeof(player->translations)); + top = player->topcolor; + if (top > 13 || top < 0) + top = 13; + top *= 16; + bottom = player->bottomcolor; + if (bottom > 13 || bottom < 0) + bottom = 13; + bottom *= 16; + + for (i=0 ; iname, Info_ValueForKey (player->userinfo, "name"), sizeof(player->name)); + player->real_topcolor = atoi(Info_ValueForKey (player->userinfo, "topcolor")); + player->real_bottomcolor = atoi(Info_ValueForKey (player->userinfo, "bottomcolor")); + strcpy (player->team, Info_ValueForKey (player->userinfo, "team")); + + if (Info_ValueForKey (player->userinfo, "*spectator")[0]) + player->spectator = true; + else + player->spectator = false; + + if (cls.state == ca_active) + Skin_Find (player); + + Sbar_Changed (); + if (slot == cl.playernum && (cl_teamtopcolor >= 0 || cl_enemytopcolor >= 0) && + strcmp(player->team, player->_team)) + { + int i; + //strcpy (cl_playerteam, Info_ValueForKey(player->userinfo, "team")); + for (i=0 ; i < MAX_CLIENTS ; i++) + CL_NewTranslation (i); + } + else + CL_NewTranslation (slot); + + strcpy (player->_team, player->team); +} + +/* +============== +CL_UpdateUserinfo +============== +*/ +void CL_UpdateUserinfo (void) +{ + int slot; + player_info_t *player; + + slot = MSG_ReadByte (); + if (slot >= MAX_CLIENTS) + Host_EndGame ("CL_ParseServerMessage: svc_updateuserinfo > MAX_CLIENTS"); + + player = &cl.players[slot]; + player->userid = MSG_ReadLong (); + Q_strncpyz (player->userinfo, MSG_ReadString(), sizeof(player->userinfo)); + + CL_ProcessUserInfo (slot, player); +} + +/* +============== +CL_SetInfo +============== +*/ +void CL_SetInfo (void) +{ + int slot; + player_info_t *player; + char key[MAX_INFO_STRING]; + char value[MAX_INFO_STRING]; + + slot = MSG_ReadByte (); + if (slot >= MAX_CLIENTS) + Host_EndGame ("CL_ParseServerMessage: svc_setinfo > MAX_CLIENTS"); + + player = &cl.players[slot]; + + Q_strncpyz (key, MSG_ReadString(), sizeof(key)); + Q_strncpyz (value, MSG_ReadString(), sizeof(value)); + + Con_DPrintf("SETINFO %s: %s=%s\n", player->name, key, value); + + Info_SetValueForKey (player->userinfo, key, value, MAX_INFO_STRING); + + CL_ProcessUserInfo (slot, player); +} + + +/* +============== +CL_ProcessServerInfo + +Called by CL_FullServerinfo_f and CL_ParseServerInfoChange +============== +*/ +void CL_ProcessServerInfo (void) +{ + char *p; + static int old_teamplay = 0; + static int old_fpd = 0; + int teamplay; // FIXME: make it cl.teamplay? + int i; + + cl.fpd = Q_atof(Info_ValueForKey(cl.serverinfo, "fpd")); + teamplay = Q_atof(Info_ValueForKey(cl.serverinfo, "teamplay")); + + p = Info_ValueForKey(cl.serverinfo, "deathmatch"); + if (*p) + cl.gametype = Q_atof(p) ? GAME_DEATHMATCH : GAME_COOP; + else + cl.gametype = GAME_DEATHMATCH; // assume GAME_DEATHMATCH by default + + if (teamplay != old_teamplay || cl.fpd != old_fpd) { + old_teamplay = teamplay; + old_fpd = cl.fpd; + if (cls.state >= ca_connected) { + for (i = 0; i < MAX_CLIENTS ; i++) + CL_NewTranslation (i); + } + } +} + +/* +============== +CL_ParseServerInfoChange +============== +*/ +void CL_ParseServerInfoChange (void) +{ + char key[MAX_INFO_STRING]; + char value[MAX_INFO_STRING]; + + Q_strncpyz (key, MSG_ReadString(), sizeof(key)); + Q_strncpyz (value, MSG_ReadString(), sizeof(value)); + + Con_DPrintf("SERVERINFO: %s=%s\n", key, value); + + Info_SetValueForKey (cl.serverinfo, key, value, MAX_SERVERINFO_STRING); + + CL_ProcessServerInfo (); +} + + +/* +============== +CL_ParsePrint +============== +*/ + +void CL_ParsePrint (void) +{ + char *s, str[1024]; + char *p; + int len; + int level; + + level = MSG_ReadByte (); + s = MSG_ReadString (); + + //stat_size += 3 + sizeof(s); + //Con_Printf("msg_size:%d\n", stat_size); + + strncat (cl.sprint_buf, s, sizeof(cl.sprint_buf)-1); + + while ( (p=strchr(cl.sprint_buf, '\n')) != NULL ) { + len = p - cl.sprint_buf + 1; + memcpy(str, cl.sprint_buf, len); + str[len] = '\0'; + strcpy (cl.sprint_buf, p+1); + + if (level == PRINT_CHAT) + { + char *p; + int flags; + + flags = TP_CategorizeMessage (str); + TP_CheckVersionRequest (str); + if (cl_nofake.value == 1 || (cl_nofake.value == 2 && flags != 2)) { + for (p = str; *p; p++) + if (*p == 13) + *p = '#'; + } + con_ormask = 128; + S_LocalSound ("misc/talk.wav"); + } + Con_Printf ("%s", str); + con_ormask = 0; + TP_SearchForMsgTriggers (str, level); + } + +} + + +/* +============== +CL_ParseStufftext +============== +*/ +void CL_ParseStufftext (void) +{ + char *s; + + s = MSG_ReadString (); + + Con_DPrintf ("stufftext: %s\n", s); + //if (!strncmp(s, "fullserverinfo", 10)) + // return; + + Cbuf_AddTextEx (&cbuf_svc, s); + + // QW servers send this without the ending \n + if ( !strcmp (s, "cmd snap") || + // QuakeForge servers up to Beta 6 send this without the \n + (!strncmp (s, "r_skyname ", 10) && !strchr (s, '\n')) ) + { + Cbuf_AddTextEx (&cbuf_svc, "\n"); + } +} + + +/* +===================== +CL_SetStat +===================== +*/ + +void CL_SetStat (int stat, int value) +{ + int j; + if (stat < 0 || stat >= MAX_CL_STATS) + Host_EndGame ("CL_SetStat: %i is invalid", stat); + + if (cls.demoplayback2) { + cl.players[cls.lastto].stats[stat]=value; + if ( Cam_TrackNum() != cls.lastto ) + return; + } + + Sbar_Changed (); + + if (stat == STAT_ITEMS) + { // set flash times + Sbar_Changed (); + for (j=0 ; j<32 ; j++) + if ( (value & (1<= MAX_CLIENTS) + { + // a monster firing + num_ent = cl.frames[cls.netchan.incoming_sequence&UPDATE_MASK].packet_entities.num_entities; + for (j = 0; j < num_ent; j++) + { + ent = &cl.frames[cls.netchan.incoming_sequence&UPDATE_MASK].packet_entities.entities[j]; + if (ent->number == i) + { + dl = CL_AllocDlight (-i); + VectorCopy (ent->origin, dl->origin); + AngleVectors (ent->angles, fv, rv, uv); + VectorMA (dl->origin, 18, fv, dl->origin); + dl->radius = 200 + (rand()&31); + dl->minlight = 32; + dl->die = cl.time + 0.1; + dl->color[0] = 0.2; + dl->color[1] = 0.1; + dl->color[2] = 0.05; + dl->color[3] = 0.7; + break; + } + } + return; + } + +#ifdef GLQUAKE + // don't draw our own muzzle flash in gl if flashblending + if (i-1 == cl.playernum && gl_flashblend.value) + return; +#endif + + if (cl_muzzleflash.value == 2 && i-1 == cl.playernum) + return; + + pl = &cl.frames[parsecountmod].playerstate[i-1]; + + dl = CL_AllocDlight (-i); + VectorCopy (pl->origin, dl->origin); + AngleVectors (pl->viewangles, fv, rv, uv); + + VectorMA (dl->origin, 18, fv, dl->origin); + dl->radius = 200 + (rand()&31); + dl->minlight = 32; + dl->die = cl.time + 0.1; + dl->color[0] = 0.2; + dl->color[1] = 0.1; + dl->color[2] = 0.05; + dl->color[3] = 0.7; +} + + +#define SHOWNET(x) if(cl_shownet.value==2)Con_Printf ("%3i:%s\n", msg_readcount-1, x); +/* +===================== +CL_ParseServerMessage +===================== +*/ +int received_framecount; +extern int fixangle; +void CL_ParseServerMessage (void) +{ + int cmd; + int i, j; + + received_framecount = host_framecount; + cl.last_servermessage = realtime; + +// +// if recording demos, copy the message out +// + if (cl_shownet.value == 1) + Con_Printf ("%i ",net_message.cursize); + else if (cl_shownet.value == 2) + Con_Printf ("------------------\n"); + + + CL_ParseClientdata (); + CL_ClearProjectiles(); + +// +// parse the message +// + while (1) + { + if (msg_badread) + { + Host_EndGame ("CL_ParseServerMessage: Bad server message"); + break; + } + + cmd = MSG_ReadByte (); + + if (cmd == -1) + { + msg_readcount++; // so the EOM showner has the right value + SHOWNET("END OF MESSAGE"); + break; + } + + SHOWNET(svc_strings[cmd]); + + // other commands + switch (cmd) + { + default: + Host_EndGame ("CL_ParseServerMessage: Illegible server message"); + break; + + case svc_nop: +// Con_Printf ("svc_nop\n"); + break; + + case svc_disconnect: + if (cls.state == ca_connected) + Host_EndGame ("Server disconnected\n" + "Server version may not be compatible"); + else + Host_EndGame ("Server disconnected"); + break; + + case svc_print: + CL_ParsePrint (); + break; + + case svc_centerprint: + SCR_CenterPrint (MSG_ReadString ()); + break; + + case svc_stufftext: + CL_ParseStufftext (); + break; + + case svc_damage: + V_ParseDamage (); + break; + + case svc_serverdata: + Cbuf_Execute (); // make sure any stuffed commands are done + CL_ParseServerData (); + vid.recalc_refdef = true; // leave full screen intermission + break; + + case svc_setangle: + if (cls.demoplayback2) { + j = MSG_ReadByte(); + fixangle |= 1 << j; + if (j != Cam_TrackNum()) + for (i=0; i<3; i++) + MSG_ReadAngle(); + } + + if (!cls.demoplayback2 || (cls.demoplayback2 && j == Cam_TrackNum())) + { + for (i=0 ; i<3 ; i++) + cl.viewangles[i] = MSG_ReadAngle (); + } +// cl.viewangles[PITCH] = cl.viewangles[ROLL] = 0; + break; + + case svc_lightstyle: + i = MSG_ReadByte (); + if (i >= MAX_LIGHTSTYLES) + Host_EndGame ("svc_lightstyle > MAX_LIGHTSTYLES"); + strncpy (cl_lightstyle[i].map, MSG_ReadString(), MAX_STYLESTRING); + cl_lightstyle[i].length = strlen(cl_lightstyle[i].map); + break; + + case svc_sound: + CL_ParseStartSoundPacket(); + break; + + case svc_stopsound: + i = MSG_ReadShort(); + S_StopSound(i>>3, i&7); + break; + + case svc_updatefrags: + Sbar_Changed (); + i = MSG_ReadByte (); + if (i >= MAX_CLIENTS) + Host_EndGame ("CL_ParseServerMessage: svc_updatefrags > MAX_CLIENTS"); + cl.players[i].frags = MSG_ReadShort (); + break; + + case svc_updateping: + i = MSG_ReadByte (); + if (i >= MAX_CLIENTS) + Host_EndGame ("CL_ParseServerMessage: svc_updateping > MAX_CLIENTS"); + cl.players[i].ping = MSG_ReadShort (); + break; + + case svc_updatepl: + i = MSG_ReadByte (); + if (i >= MAX_CLIENTS) + Host_EndGame ("CL_ParseServerMessage: svc_updatepl > MAX_CLIENTS"); + cl.players[i].pl = MSG_ReadByte (); + break; + + case svc_updateentertime: + // time is sent over as seconds ago + i = MSG_ReadByte (); + if (i >= MAX_CLIENTS) + Host_EndGame ("CL_ParseServerMessage: svc_updateentertime > MAX_CLIENTS"); + cl.players[i].entertime = realtime - MSG_ReadFloat (); + //Con_Printf("enter:%f, real:%f\n", realtime - cl.players[i].entertime, realtime); + break; + + case svc_spawnbaseline: + i = MSG_ReadShort (); + CL_ParseBaseline (&cl_baselines[i]); + break; + case svc_spawnstatic: + CL_ParseStatic (); + break; + case svc_temp_entity: + CL_ParseTEnt (); + break; + + case svc_killedmonster: + cl.stats[STAT_MONSTERS]++; + break; + + case svc_foundsecret: + cl.stats[STAT_SECRETS]++; + break; + + case svc_updatestat: + i = MSG_ReadByte (); + j = MSG_ReadByte (); + //stat_size += 8; + CL_SetStat (i, j); + break; + case svc_updatestatlong: + i = MSG_ReadByte (); + j = MSG_ReadLong (); + //stat_size += 11; + CL_SetStat (i, j); + break; + + case svc_spawnstaticsound: + CL_ParseStaticSound (); + break; + + case svc_cdtrack: + cl.cdtrack = MSG_ReadByte (); + CDAudio_Play ((byte)cl.cdtrack, true); + break; + + case svc_intermission: + cl.intermission = 1; + cl.completed_time = realtime; + vid.recalc_refdef = true; // go to full screen + for (i=0 ; i<3 ; i++) + cl.simorg[i] = MSG_ReadCoord (); + for (i=0 ; i<3 ; i++) + cl.simangles[i] = MSG_ReadAngle (); + VectorCopy (vec3_origin, cl.simvel); + TP_ExecTrigger ("f_mapend"); + break; + + case svc_finale: + cl.intermission = 2; + cl.completed_time = realtime; + vid.recalc_refdef = true; // go to full screen + SCR_CenterPrint (MSG_ReadString ()); + break; + + case svc_sellscreen: + Cmd_ExecuteString ("help"); + break; + + case svc_smallkick: + cl.punchangle = -2; + break; + case svc_bigkick: + cl.punchangle = -4; + break; + + case svc_muzzleflash: + CL_MuzzleFlash (); + break; + + case svc_updateuserinfo: + CL_UpdateUserinfo (); + break; + + case svc_setinfo: + CL_SetInfo (); + break; + + case svc_serverinfo: + CL_ParseServerInfoChange (); + break; + + case svc_download: + CL_ParseDownload (); + break; + + case svc_playerinfo: + CL_ParsePlayerinfo (); + break; + + case svc_nails: + CL_ParseProjectiles (false); + break; + case svc_nails2: + CL_ParseProjectiles (true); + break; + + case svc_chokecount: // some preceding packets were choked + i = MSG_ReadByte (); + for (j=0 ; jhulls[1], 0, pmove.origin) == CONTENTS_EMPTY) - return; - - VectorCopy (pmove.origin, base); - for (x=-1 ; x<=1 ; x++) - { - for (y=-1 ; y<=1 ; y++) - { - pmove.origin[0] = base[0] + x * 1.0/8; - pmove.origin[1] = base[1] + y * 1.0/8; - if (PM_HullPointContents (&cl.model_precache[1]->hulls[1], 0, pmove.origin) == CONTENTS_EMPTY) - return; - } - } - Con_DPrintf ("CL_NudgePosition: stuck\n"); -} - - -/* -============== -CL_PredictUsercmd -============== -*/ -void CL_PredictUsercmd (player_state_t *from, player_state_t *to, usercmd_t *u, qboolean spectator) -{ - extern cvar_t cl_speedjumpfix; - - // split up very long moves - if (u->msec > 50) - { - player_state_t temp; - usercmd_t split; - - split = *u; - split.msec /= 2; - - CL_PredictUsercmd (from, &temp, &split, spectator); - CL_PredictUsercmd (&temp, to, &split, spectator); - return; - } - - VectorCopy (from->origin, pmove.origin); -// VectorCopy (from->viewangles, pmove.angles); - VectorCopy (u->angles, pmove.angles); - VectorCopy (from->velocity, pmove.velocity); - - if (cl_speedjumpfix.value) - pmove.jump_msec = from->jump_msec; - pmove.oldbuttons = from->oldbuttons; - pmove.waterjumptime = from->waterjumptime; - pmove.dead = cl.stats[STAT_HEALTH] <= 0; - pmove.spectator = spectator; - - pmove.cmd = *u; - - PlayerMove (); -//for (i=0 ; i<3 ; i++) -//pmove.origin[i] = ((int)(pmove.origin[i]*8))*0.125; - to->waterjumptime = pmove.waterjumptime; - if (cl_speedjumpfix.value) { - to->oldbuttons = pmove.oldbuttons; - to->jump_msec = pmove.jump_msec; - pmove.jump_msec = 0; - } - else - to->oldbuttons = pmove.cmd.buttons; - VectorCopy (pmove.origin, to->origin); - VectorCopy (pmove.angles, to->viewangles); - VectorCopy (pmove.velocity, to->velocity); - to->onground = onground; - - to->weaponframe = from->weaponframe; -} - - -/* -============== -CL_CalcCrouch - -Smooth out stair step ups. -Called before CL_EmitEntities so that the player's lightning model -origin is updated properly -============== -*/ -void CL_CalcCrouch (void) -{ - static float oldz = 0; - static float extracrouch = 0; - static float crouchspeed = 100; - - if (cl.simorg[2] - oldz > 0 // only smooth when moving up - && cl.simorg[2] - oldz < 40) // teleported? - { - if (cl.onground != -1) - { - // if on steep stairs, increase speed - if (cl.simorg[2] - oldz > 20) { - if (crouchspeed < 160) { - extracrouch = cl.simorg[2] - oldz - host_frametime*200 - 15; - if (extracrouch > 5) - extracrouch = 5; - } - crouchspeed = 160; - } - - oldz += host_frametime * crouchspeed; - if (oldz > cl.simorg[2]) - oldz = cl.simorg[2]; - - if (cl.simorg[2] - oldz > 15 + extracrouch) - oldz = cl.simorg[2] - 15 - extracrouch; - extracrouch -= host_frametime*200; - if (extracrouch < 0) - extracrouch = 0; - - cl.crouch = oldz - cl.simorg[2]; - } else { - // in air - oldz = cl.simorg[2]; - cl.crouch += host_frametime * 150; - if (cl.crouch > 0) - cl.crouch = 0; - crouchspeed = 100; - extracrouch = 0; - } - } - else - { - oldz = cl.simorg[2]; - cl.crouch = extracrouch = 0; - crouchspeed = 100; - } -} - - -/* -============== -CL_PredictMove -============== -*/ -void CL_PredictMove (qboolean nopred) -{ - int i; - float f; - frame_t *from, *to = NULL; - int oldphysent; - extern float nextdemotime; - - if (cl_pushlatency.value > 0) - Cvar_Set (&cl_pushlatency, "0"); - - if (cl.paused) - return; - - cl.time = realtime - cls.latency - cl_pushlatency.value*0.001; - if (cl.time > realtime) - cl.time = realtime; - - if (cl.intermission) { - cl.crouch = 0; - return; - } - - if (!cl.validsequence) - return; - - if (cls.netchan.outgoing_sequence - cls.netchan.incoming_sequence >= UPDATE_BACKUP-1) - return; - - VectorCopy (cl.viewangles, cl.simangles); - - // this is the last frame received from the server - from = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK]; - - // we can now render a frame - if (cls.state == ca_onserver) - { // first update is the final signon stage - cls.state = ca_active; -#ifdef _WIN32 - SetWindowText (mainwindow, va("QuakeWorld: %s", cls.servername)); -#endif - TP_ExecTrigger ("f_spawn"); - } - - if (cl_nopred.value || nopred) - { - VectorCopy (from->playerstate[cl.playernum].velocity, cl.simvel); - VectorCopy (from->playerstate[cl.playernum].origin, cl.simorg); - cl.onground = 0; // FIXME - goto out; - } - - // predict forward until cl.time <= to->senttime - oldphysent = pmove.numphysent; - CL_SetSolidPlayers (cl.playernum); - -// to = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK]; - - for (i=1 ; iplayerstate[cl.playernum] - , &to->playerstate[cl.playernum], &to->cmd, cl.spectator); - cl.onground = onground; - if (to->senttime >= cl.time) - break; - from = to; - } - - pmove.numphysent = oldphysent; - - if (i == UPDATE_BACKUP-1 || !to) - return; // net hasn't deliver packets in a long time... - - // now interpolate some fraction of the final frame - if (to->senttime == from->senttime) - f = 0; - else - { - f = (cl.time - from->senttime) / (to->senttime - from->senttime); - - if (f < 0) - f = 0; - if (f > 1) - f = 1; - } - - for (i=0 ; i<3 ; i++) - if ( fabs(from->playerstate[cl.playernum].origin[i] - to->playerstate[cl.playernum].origin[i]) > 128) - { // teleported, so don't lerp - VectorCopy (to->playerstate[cl.playernum].velocity, cl.simvel); - VectorCopy (to->playerstate[cl.playernum].origin, cl.simorg); - goto out; - } - - for (i=0 ; i<3 ; i++) - { - cl.simorg[i] = from->playerstate[cl.playernum].origin[i] - + f*(to->playerstate[cl.playernum].origin[i] - from->playerstate[cl.playernum].origin[i]); - cl.simvel[i] = from->playerstate[cl.playernum].velocity[i] - + f*(to->playerstate[cl.playernum].velocity[i] - from->playerstate[cl.playernum].velocity[i]); - } - -out: - CL_CalcCrouch (); -} - - -/* -============== -CL_InitPrediction -============== -*/ -void CL_InitPrediction (void) -{ - Cvar_RegisterVariable (&cl_pushlatency); - Cvar_RegisterVariable (&cl_nopred); -} - +/* +Copyright (C) 1996-1997 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 "quakedef.h" +#include "winquake.h" +#include "pmove.h" +#include "teamplay.h" + +cvar_t cl_nopred = {"cl_nopred","0"}; +cvar_t cl_pushlatency = {"pushlatency","-999"}; + +extern frame_t *view_frame; + +/* +================= +CL_NudgePosition + +If pmove.origin is in a solid position, +try nudging slightly on all axis to +allow for the cut precision of the net coordinates +================= +*/ +void CL_NudgePosition (void) +{ + vec3_t base; + int x, y; + + if (PM_HullPointContents (&cl.model_precache[1]->hulls[1], 0, pmove.origin) == CONTENTS_EMPTY) + return; + + VectorCopy (pmove.origin, base); + for (x=-1 ; x<=1 ; x++) + { + for (y=-1 ; y<=1 ; y++) + { + pmove.origin[0] = base[0] + x * 1.0/8; + pmove.origin[1] = base[1] + y * 1.0/8; + if (PM_HullPointContents (&cl.model_precache[1]->hulls[1], 0, pmove.origin) == CONTENTS_EMPTY) + return; + } + } + Con_DPrintf ("CL_NudgePosition: stuck\n"); +} + + +/* +============== +CL_PredictUsercmd +============== +*/ +void CL_PredictUsercmd (player_state_t *from, player_state_t *to, usercmd_t *u, qboolean spectator) +{ + extern cvar_t cl_speedjumpfix; + + // split up very long moves + if (u->msec > 50) + { + player_state_t temp; + usercmd_t split; + + split = *u; + split.msec /= 2; + + CL_PredictUsercmd (from, &temp, &split, spectator); + CL_PredictUsercmd (&temp, to, &split, spectator); + return; + } + + VectorCopy (from->origin, pmove.origin); +// VectorCopy (from->viewangles, pmove.angles); + VectorCopy (u->angles, pmove.angles); + VectorCopy (from->velocity, pmove.velocity); + + if (cl_speedjumpfix.value) + pmove.jump_msec = from->jump_msec; + pmove.oldbuttons = from->oldbuttons; + pmove.waterjumptime = from->waterjumptime; + pmove.dead = cl.stats[STAT_HEALTH] <= 0; + pmove.spectator = spectator; + + pmove.cmd = *u; + + PlayerMove (); +//for (i=0 ; i<3 ; i++) +//pmove.origin[i] = ((int)(pmove.origin[i]*8))*0.125; + to->waterjumptime = pmove.waterjumptime; + if (cl_speedjumpfix.value) { + to->oldbuttons = pmove.oldbuttons; + to->jump_msec = pmove.jump_msec; + pmove.jump_msec = 0; + } + else + to->oldbuttons = pmove.cmd.buttons; + VectorCopy (pmove.origin, to->origin); + VectorCopy (pmove.angles, to->viewangles); + VectorCopy (pmove.velocity, to->velocity); + to->onground = onground; + + to->weaponframe = from->weaponframe; +} + + +/* +============== +CL_CalcCrouch + +Smooth out stair step ups. +Called before CL_EmitEntities so that the player's lightning model +origin is updated properly +============== +*/ +void CL_CalcCrouch (void) +{ + static float oldz = 0; + static float extracrouch = 0; + static float crouchspeed = 100; + + if (cl.simorg[2] - oldz > 0 // only smooth when moving up + && cl.simorg[2] - oldz < 40) // teleported? + { + if (cl.onground != -1) + { + // if on steep stairs, increase speed + if (cl.simorg[2] - oldz > 20) { + if (crouchspeed < 160) { + extracrouch = cl.simorg[2] - oldz - host_frametime*200 - 15; + if (extracrouch > 5) + extracrouch = 5; + } + crouchspeed = 160; + } + + oldz += host_frametime * crouchspeed; + if (oldz > cl.simorg[2]) + oldz = cl.simorg[2]; + + if (cl.simorg[2] - oldz > 15 + extracrouch) + oldz = cl.simorg[2] - 15 - extracrouch; + extracrouch -= host_frametime*200; + if (extracrouch < 0) + extracrouch = 0; + + cl.crouch = oldz - cl.simorg[2]; + } else { + // in air + oldz = cl.simorg[2]; + cl.crouch += host_frametime * 150; + if (cl.crouch > 0) + cl.crouch = 0; + crouchspeed = 100; + extracrouch = 0; + } + } + else + { + oldz = cl.simorg[2]; + cl.crouch = extracrouch = 0; + crouchspeed = 100; + } +} + + +/* +============== +CL_PredictMove +============== +*/ +void CL_PredictMove (qboolean nopred) +{ + int i; + float f; + frame_t *from, *to = NULL; + int oldphysent; + extern float nextdemotime; + + if (cl_pushlatency.value > 0) + Cvar_Set (&cl_pushlatency, "0"); + + if (cl.paused) + return; + + cl.time = realtime - cls.latency - cl_pushlatency.value*0.001; + if (cl.time > realtime) + cl.time = realtime; + + if (cl.intermission) { + cl.crouch = 0; + return; + } + + if (!cl.validsequence) + return; + + if (cls.netchan.outgoing_sequence - cls.netchan.incoming_sequence >= UPDATE_BACKUP-1) + return; + + VectorCopy (cl.viewangles, cl.simangles); + + // this is the last frame received from the server + from = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK]; + + // we can now render a frame + if (cls.state == ca_onserver) + { // first update is the final signon stage + cls.state = ca_active; +#ifdef _WIN32 + SetWindowText (mainwindow, va("QuakeWorld: %s", cls.servername)); +#endif + TP_ExecTrigger ("f_spawn"); + } + + if (cl_nopred.value || nopred) + { + VectorCopy (from->playerstate[cl.playernum].velocity, cl.simvel); + VectorCopy (from->playerstate[cl.playernum].origin, cl.simorg); + cl.onground = 0; // FIXME + goto out; + } + + // predict forward until cl.time <= to->senttime + oldphysent = pmove.numphysent; + CL_SetSolidPlayers (cl.playernum); + +// to = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK]; + + for (i=1 ; iplayerstate[cl.playernum] + , &to->playerstate[cl.playernum], &to->cmd, cl.spectator); + cl.onground = onground; + if (to->senttime >= cl.time) + break; + from = to; + } + + pmove.numphysent = oldphysent; + + if (i == UPDATE_BACKUP-1 || !to) + return; // net hasn't deliver packets in a long time... + + // now interpolate some fraction of the final frame + if (to->senttime == from->senttime) + f = 0; + else + { + f = (cl.time - from->senttime) / (to->senttime - from->senttime); + + if (f < 0) + f = 0; + if (f > 1) + f = 1; + } + + for (i=0 ; i<3 ; i++) + if ( fabs(from->playerstate[cl.playernum].origin[i] - to->playerstate[cl.playernum].origin[i]) > 128) + { // teleported, so don't lerp + VectorCopy (to->playerstate[cl.playernum].velocity, cl.simvel); + VectorCopy (to->playerstate[cl.playernum].origin, cl.simorg); + goto out; + } + + for (i=0 ; i<3 ; i++) + { + cl.simorg[i] = from->playerstate[cl.playernum].origin[i] + + f*(to->playerstate[cl.playernum].origin[i] - from->playerstate[cl.playernum].origin[i]); + cl.simvel[i] = from->playerstate[cl.playernum].velocity[i] + + f*(to->playerstate[cl.playernum].velocity[i] - from->playerstate[cl.playernum].velocity[i]); + } + +out: + CL_CalcCrouch (); +} + + +/* +============== +CL_InitPrediction +============== +*/ +void CL_InitPrediction (void) +{ + Cvar_RegisterVariable (&cl_pushlatency); + Cvar_RegisterVariable (&cl_nopred); +} + diff --git a/source/cl_slist.c b/source/cl_slist.c index f31089de..b029a455 100644 --- a/source/cl_slist.c +++ b/source/cl_slist.c @@ -1,249 +1,249 @@ -/* - cl_slist.c - - serverlist addressbook - - Copyright (C) 1999,2000 contributors of the QuakeForge project - Please see the file "AUTHORS" for a list of contributors - - Author: Brian Koropoff - Date: 03 May 2000 - - 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: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - - $Id: cl_slist.c,v 1.1.1.4 2004/10/13 18:54:25 vvd0 Exp $ -*/ - -#include "quakedef.h" -#include "cl_slist.h" - -//Better watch out for buffer overflows -server_entry_t slist[MAX_SERVER_LIST]; - -char *gettokstart (char *str, int req, char delim); -int gettoklen(char *str, int req, char delim); - - -void SList_Init(void) -{ - int i; - for(i=0; i < MAX_SERVER_LIST; i++) { - slist[i].server = NULL; - slist[i].description = NULL; - slist[i].ping = 0; - } -} - - -void SList_Shutdown(void) // I am the liberator of memory. -{ - int i; - FILE *f; - - // FIXME: if the list was deleted, the changes will not be saved - for (i=0; i < MAX_SERVER_LIST; i++) { - if (slist[i].server) - break; - } - if (i < MAX_SERVER_LIST) // the list is not empty - { - if (!(f = fopen(va("%s/servers.txt", com_basedir),"w"))) { - Con_Printf("Couldn't open servers.txt.\n"); - return; - } - SList_Save(f); - fclose(f); - } - for(i=0;i < MAX_SERVER_LIST;i++) { - if (slist[i].server) - free(slist[i].server); - if (slist[i].description) - free(slist[i].description); - } -} - - -void SList_Set (int i, char *addr, char *desc) -{ - if ((unsigned)i >= MAX_SERVER_LIST) - return; - - // Free old strings - if (slist[i].server) - free(slist[i].server); - if (slist[i].description) - free(slist[i].description); - - slist[i].server = Q_Malloc (strlen(addr) + 1); - slist[i].description = Q_Malloc (strlen(desc) + 1); - strcpy (slist[i].server, addr); - strcpy (slist[i].description, desc); -} - - -//NEVER USE THIS UNLESS REALLY NEEDED -void SList_Reset_NoFree (int i) -{ - if ((unsigned)i >= MAX_SERVER_LIST) - return; - - slist[i].server = '\0'; - slist[i].description = '\0'; - slist[i].ping = 0; -} - - -void SList_Reset (int i) -{ - if ((unsigned)i >= MAX_SERVER_LIST) - return; - - if (slist[i].server) - free(slist[i].server); - if (slist[i].description) - free(slist[i].description); - slist[i].server = '\0'; - slist[i].description = '\0'; - slist[i].ping = 0; -} - - -void SList_Switch (int a,int b) -{ - server_entry_t temp; - - if ((unsigned)a >= MAX_SERVER_LIST || (unsigned)b >= MAX_SERVER_LIST) - return; - - memcpy(&temp, &slist[a], sizeof(temp)); - memcpy(&slist[a], &slist[b], sizeof(temp)); - memcpy(&slist[b], &temp, sizeof(temp)); -} - -int SList_Len (void) -{ - int i; - for (i = 0; i < MAX_SERVER_LIST && slist[i].server;i++) - ; - return i; -} - -void SList_Load () // This could get messy -{ - int serv = 0; - char line[256]; /* Long lines get truncated. */ - int c = ' '; /* int so it can be compared to EOF properly*/ - char *start; - int len; - int i; - char *addr; - FILE *f; - - f = fopen (va("%s/servers.txt", com_basedir), "r"); - if (f == NULL) - return; - - while (serv < MAX_SERVER_LIST) { - //First, get a line - i = 0; - c = ' '; - while (c != '\n' && c != EOF) { - c = fgetc(f); - if (i < 255) { - line[i] = c; - i++; - } - } - - line[i - 1] = '\0'; - // Now we can parse it - if ((start = gettokstart(line,1,' ')) != NULL) { - len = gettoklen(line,1,' '); - addr = Q_Malloc (len + 1); - Q_strncpyz (addr, line, len+1); - if ((start = gettokstart(line,2,' '))) { - SList_Set (serv, addr, start); - } - else { - SList_Set (serv, addr, "Unknown"); - } - serv++; - } - - if (c == EOF) - break; - } - - fclose (f); -} - - -void SList_Save (FILE *f) -{ - int i; - - for (i=0; i < MAX_SERVER_LIST; i++) { - if (slist[i].server) - fprintf(f,"%s %s\n", - slist[i].server, - slist[i].description); - } -} - - -char *gettokstart (char *str, int req, char delim) -{ - char *start = str; - - int tok = 1; - - while (*start == delim) { - start++; - } - if (*start == '\0') - return '\0'; - while (tok < req) { //Stop when we get to the requested token - if (*++start == delim) { //Increment pointer and test - while (*start == delim) { //Get to next token - start++; - } - tok++; - } - if (*start == '\0') { - return '\0'; - } - } - return start; -} - -int gettoklen (char *str, int req, char delim) { - char *start = 0; - - int len = 0; - - start = gettokstart(str,req,delim); - if (start == '\0') { - return 0; - } - while (*start != delim && *start != '\0') { - start++; - len++; - } - return len; -} +/* + cl_slist.c + + serverlist addressbook + + Copyright (C) 1999,2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + Author: Brian Koropoff + Date: 03 May 2000 + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id: cl_slist.c,v 1.1.1.5 2004/10/18 17:44:26 vvd0 Exp $ +*/ + +#include "quakedef.h" +#include "cl_slist.h" + +//Better watch out for buffer overflows +server_entry_t slist[MAX_SERVER_LIST]; + +char *gettokstart (char *str, int req, char delim); +int gettoklen(char *str, int req, char delim); + + +void SList_Init(void) +{ + int i; + for(i=0; i < MAX_SERVER_LIST; i++) { + slist[i].server = NULL; + slist[i].description = NULL; + slist[i].ping = 0; + } +} + + +void SList_Shutdown(void) // I am the liberator of memory. +{ + int i; + FILE *f; + + // FIXME: if the list was deleted, the changes will not be saved + for (i=0; i < MAX_SERVER_LIST; i++) { + if (slist[i].server) + break; + } + if (i < MAX_SERVER_LIST) // the list is not empty + { + if (!(f = fopen(va("%s/servers.txt", com_basedir),"w"))) { + Con_Printf("Couldn't open servers.txt.\n"); + return; + } + SList_Save(f); + fclose(f); + } + for(i=0;i < MAX_SERVER_LIST;i++) { + if (slist[i].server) + free(slist[i].server); + if (slist[i].description) + free(slist[i].description); + } +} + + +void SList_Set (int i, char *addr, char *desc) +{ + if ((unsigned)i >= MAX_SERVER_LIST) + return; + + // Free old strings + if (slist[i].server) + free(slist[i].server); + if (slist[i].description) + free(slist[i].description); + + slist[i].server = Q_Malloc (strlen(addr) + 1); + slist[i].description = Q_Malloc (strlen(desc) + 1); + strcpy (slist[i].server, addr); + strcpy (slist[i].description, desc); +} + + +//NEVER USE THIS UNLESS REALLY NEEDED +void SList_Reset_NoFree (int i) +{ + if ((unsigned)i >= MAX_SERVER_LIST) + return; + + slist[i].server = '\0'; + slist[i].description = '\0'; + slist[i].ping = 0; +} + + +void SList_Reset (int i) +{ + if ((unsigned)i >= MAX_SERVER_LIST) + return; + + if (slist[i].server) + free(slist[i].server); + if (slist[i].description) + free(slist[i].description); + slist[i].server = '\0'; + slist[i].description = '\0'; + slist[i].ping = 0; +} + + +void SList_Switch (int a,int b) +{ + server_entry_t temp; + + if ((unsigned)a >= MAX_SERVER_LIST || (unsigned)b >= MAX_SERVER_LIST) + return; + + memcpy(&temp, &slist[a], sizeof(temp)); + memcpy(&slist[a], &slist[b], sizeof(temp)); + memcpy(&slist[b], &temp, sizeof(temp)); +} + +int SList_Len (void) +{ + int i; + for (i = 0; i < MAX_SERVER_LIST && slist[i].server;i++) + ; + return i; +} + +void SList_Load () // This could get messy +{ + int serv = 0; + char line[256]; /* Long lines get truncated. */ + int c = ' '; /* int so it can be compared to EOF properly*/ + char *start; + int len; + int i; + char *addr; + FILE *f; + + f = fopen (va("%s/servers.txt", com_basedir), "r"); + if (f == NULL) + return; + + while (serv < MAX_SERVER_LIST) { + //First, get a line + i = 0; + c = ' '; + while (c != '\n' && c != EOF) { + c = fgetc(f); + if (i < 255) { + line[i] = c; + i++; + } + } + + line[i - 1] = '\0'; + // Now we can parse it + if ((start = gettokstart(line,1,' ')) != NULL) { + len = gettoklen(line,1,' '); + addr = Q_Malloc (len + 1); + Q_strncpyz (addr, line, len+1); + if ((start = gettokstart(line,2,' '))) { + SList_Set (serv, addr, start); + } + else { + SList_Set (serv, addr, "Unknown"); + } + serv++; + } + + if (c == EOF) + break; + } + + fclose (f); +} + + +void SList_Save (FILE *f) +{ + int i; + + for (i=0; i < MAX_SERVER_LIST; i++) { + if (slist[i].server) + fprintf(f,"%s %s\n", + slist[i].server, + slist[i].description); + } +} + + +char *gettokstart (char *str, int req, char delim) +{ + char *start = str; + + int tok = 1; + + while (*start == delim) { + start++; + } + if (*start == '\0') + return '\0'; + while (tok < req) { //Stop when we get to the requested token + if (*++start == delim) { //Increment pointer and test + while (*start == delim) { //Get to next token + start++; + } + tok++; + } + if (*start == '\0') { + return '\0'; + } + } + return start; +} + +int gettoklen (char *str, int req, char delim) { + char *start = 0; + + int len = 0; + + start = gettokstart(str,req,delim); + if (start == '\0') { + return 0; + } + while (*start != delim && *start != '\0') { + start++; + len++; + } + return len; +} diff --git a/source/cl_slist.h b/source/cl_slist.h index 3ca8d14e..25c7b681 100644 --- a/source/cl_slist.h +++ b/source/cl_slist.h @@ -1,53 +1,53 @@ -/* - cl_slist.h - - serverlist addressbook interface - - Copyright (C) 1999,2000 contributors of the QuakeForge project - Please see the file "AUTHORS" for a list of contributors - - Author: Brian Koropoff - Date: 03 May 2000 - - 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: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - - $Id: cl_slist.h,v 1.1.1.3 2004/10/13 18:54:25 vvd0 Exp $ -*/ - -// #include -//#include "common.h" -#define MAX_SERVER_LIST 256 - -typedef struct { - char *server; - char *description; - int ping; -} server_entry_t; - -extern server_entry_t slist[MAX_SERVER_LIST]; - -void SList_Init(void); -void SList_Shutdown(void); -void SList_Set(int i,char *addr,char *desc); -void SList_Reset_NoFree(int i); -void SList_Reset(int i); -void SList_Switch(int a,int b); -int SList_Len(void); -void SList_Load(); -void SList_Save(); +/* + cl_slist.h + + serverlist addressbook interface + + Copyright (C) 1999,2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + Author: Brian Koropoff + Date: 03 May 2000 + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id: cl_slist.h,v 1.1.1.4 2004/10/18 17:44:26 vvd0 Exp $ +*/ + +// #include +//#include "common.h" +#define MAX_SERVER_LIST 256 + +typedef struct { + char *server; + char *description; + int ping; +} server_entry_t; + +extern server_entry_t slist[MAX_SERVER_LIST]; + +void SList_Init(void); +void SList_Shutdown(void); +void SList_Set(int i,char *addr,char *desc); +void SList_Reset_NoFree(int i); +void SList_Reset(int i); +void SList_Switch(int a,int b); +int SList_Len(void); +void SList_Load(); +void SList_Save(); diff --git a/source/cl_tent.c b/source/cl_tent.c index 0e230c2b..dd305351 100644 --- a/source/cl_tent.c +++ b/source/cl_tent.c @@ -1,532 +1,532 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// cl_tent.c -- client side temporary entities - -#include "quakedef.h" -#include "pmove.h" -#include "sound.h" - -#define MAX_BEAMS 8 -typedef struct -{ - int entity; - struct model_s *model; - float endtime; - vec3_t start, end; -} beam_t; - -beam_t cl_beams[MAX_BEAMS]; - -static vec3_t playerbeam_end; - - -#define MAX_EXPLOSIONS 8 -typedef struct -{ - vec3_t origin; - float start; - model_t *model; -} explosion_t; - -explosion_t cl_explosions[MAX_EXPLOSIONS]; - - -sfx_t *cl_sfx_wizhit; -sfx_t *cl_sfx_knighthit; -sfx_t *cl_sfx_tink1; -sfx_t *cl_sfx_ric1; -sfx_t *cl_sfx_ric2; -sfx_t *cl_sfx_ric3; -sfx_t *cl_sfx_r_exp3; - -/* -================= -CL_ParseTEnts -================= -*/ -void CL_InitTEnts (void) -{ - cl_sfx_wizhit = S_PrecacheSound ("wizard/hit.wav"); - cl_sfx_knighthit = S_PrecacheSound ("hknight/hit.wav"); - cl_sfx_tink1 = S_PrecacheSound ("weapons/tink1.wav"); - cl_sfx_ric1 = S_PrecacheSound ("weapons/ric1.wav"); - cl_sfx_ric2 = S_PrecacheSound ("weapons/ric2.wav"); - cl_sfx_ric3 = S_PrecacheSound ("weapons/ric3.wav"); - cl_sfx_r_exp3 = S_PrecacheSound ("weapons/r_exp3.wav"); -} - -/* -================= -CL_ClearTEnts -================= -*/ -void CL_ClearTEnts (void) -{ - memset (&cl_beams, 0, sizeof(cl_beams)); - memset (&cl_explosions, 0, sizeof(cl_explosions)); -} - -/* -================= -CL_AllocExplosion -================= -*/ -explosion_t *CL_AllocExplosion (void) -{ - int i; - float time; - int index; - - for (i=0 ; ientity == ent) - { - b->model = m; - b->endtime = cl.time + 0.2; - VectorCopy (start, b->start); - VectorCopy (end, b->end); - return; - } - -// find a free beam - for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++) - { - if (!b->model || b->endtime < cl.time) - { - b->entity = ent; - b->model = m; - b->endtime = cl.time + 0.2; - VectorCopy (start, b->start); - VectorCopy (end, b->end); - return; - } - } - Con_Printf ("beam list overflow!\n"); -} - -/* -================= -CL_ParseTEnt -================= -*/ -void CL_ParseTEnt (void) -{ - int type; - vec3_t pos; - dlight_t *dl; - int rnd; - explosion_t *ex; - int cnt; - - type = MSG_ReadByte (); - switch (type) - { - case TE_WIZSPIKE: // spike hitting wall - pos[0] = MSG_ReadCoord (); - pos[1] = MSG_ReadCoord (); - pos[2] = MSG_ReadCoord (); - R_RunParticleEffect (pos, vec3_origin, 20, 30); - S_StartSound (-1, 0, cl_sfx_wizhit, pos, 1, 1); - break; - - case TE_KNIGHTSPIKE: // spike hitting wall - pos[0] = MSG_ReadCoord (); - pos[1] = MSG_ReadCoord (); - pos[2] = MSG_ReadCoord (); - R_RunParticleEffect (pos, vec3_origin, 226, 20); - S_StartSound (-1, 0, cl_sfx_knighthit, pos, 1, 1); - break; - - case TE_SPIKE: // spike hitting wall - pos[0] = MSG_ReadCoord (); - pos[1] = MSG_ReadCoord (); - pos[2] = MSG_ReadCoord (); - R_RunParticleEffect (pos, vec3_origin, 0, 10); - - if ( rand() % 5 ) - S_StartSound (-1, 0, cl_sfx_tink1, pos, 1, 1); - else - { - rnd = rand() & 3; - if (rnd == 1) - S_StartSound (-1, 0, cl_sfx_ric1, pos, 1, 1); - else if (rnd == 2) - S_StartSound (-1, 0, cl_sfx_ric2, pos, 1, 1); - else - S_StartSound (-1, 0, cl_sfx_ric3, pos, 1, 1); - } - break; - case TE_SUPERSPIKE: // super spike hitting wall - pos[0] = MSG_ReadCoord (); - pos[1] = MSG_ReadCoord (); - pos[2] = MSG_ReadCoord (); - R_RunParticleEffect (pos, vec3_origin, 0, 20); - - if ( rand() % 5 ) - S_StartSound (-1, 0, cl_sfx_tink1, pos, 1, 1); - else - { - rnd = rand() & 3; - if (rnd == 1) - S_StartSound (-1, 0, cl_sfx_ric1, pos, 1, 1); - else if (rnd == 2) - S_StartSound (-1, 0, cl_sfx_ric2, pos, 1, 1); - else - S_StartSound (-1, 0, cl_sfx_ric3, pos, 1, 1); - } - break; - - case TE_EXPLOSION: // rocket explosion - // particles - - pos[0] = MSG_ReadCoord (); - pos[1] = MSG_ReadCoord (); - pos[2] = MSG_ReadCoord (); - - if (cl_explosion.value == 6) - R_TeleportSplash (pos); - else if (cl_explosion.value == 7) - R_RunParticleEffect (pos, vec3_origin, 73, 20*32); - else if (cl_explosion.value == 8) - R_RunParticleEffect (pos, vec3_origin, 225, 50); - else - { - - // Tonik: explosion modifiers... - if (cl_explosion.value != 1 && cl_explosion.value != 3) - { - R_ParticleExplosion (pos); - } - - // light - if (cl_explosion.value != 2 && cl_explosion.value != 3) - { - dl = CL_AllocDlight (0); - VectorCopy (pos, dl->origin); - dl->radius = 350; - dl->die = cl.time + 0.5; - dl->decay = 300; - dl->color[0] = 0.2; - dl->color[1] = 0.1; - dl->color[2] = 0.05; - dl->color[3] = 0.7; - } - } - - // sound - S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1); - - // sprite - if (cl_explosion.value != 6 && cl_explosion.value != 7 && cl_explosion.value != 8) - { - ex = CL_AllocExplosion (); - VectorCopy (pos, ex->origin); - ex->start = cl.time; - ex->model = Mod_ForName ("progs/s_explod.spr", true); - } - break; - - case TE_TAREXPLOSION: // tarbaby explosion - pos[0] = MSG_ReadCoord (); - pos[1] = MSG_ReadCoord (); - pos[2] = MSG_ReadCoord (); - R_BlobExplosion (pos); - - S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1); - break; - - case TE_LIGHTNING1: // lightning bolts - CL_ParseBeam (Mod_ForName("progs/bolt.mdl", true)); - break; - - case TE_LIGHTNING2: // lightning bolts - CL_ParseBeam (Mod_ForName("progs/bolt2.mdl", true)); - break; - - case TE_LIGHTNING3: // lightning bolts - CL_ParseBeam (Mod_ForName("progs/bolt3.mdl", true)); - break; - - case TE_LAVASPLASH: - pos[0] = MSG_ReadCoord (); - pos[1] = MSG_ReadCoord (); - pos[2] = MSG_ReadCoord (); - R_LavaSplash (pos); - break; - - case TE_TELEPORT: - pos[0] = MSG_ReadCoord (); - pos[1] = MSG_ReadCoord (); - pos[2] = MSG_ReadCoord (); - R_TeleportSplash (pos); - break; - - case TE_GUNSHOT: // bullet hitting wall - cnt = MSG_ReadByte (); - pos[0] = MSG_ReadCoord (); - pos[1] = MSG_ReadCoord (); - pos[2] = MSG_ReadCoord (); - R_RunParticleEffect (pos, vec3_origin, 0, 20*cnt); - break; - - case TE_BLOOD: // bullets hitting body - cnt = MSG_ReadByte (); - pos[0] = MSG_ReadCoord (); - pos[1] = MSG_ReadCoord (); - pos[2] = MSG_ReadCoord (); - R_RunParticleEffect (pos, vec3_origin, 73, 20*cnt); - break; - - case TE_LIGHTNINGBLOOD: // lightning hitting body - pos[0] = MSG_ReadCoord (); - pos[1] = MSG_ReadCoord (); - pos[2] = MSG_ReadCoord (); - R_RunParticleEffect (pos, vec3_origin, 225, 50); - break; - - default: - Host_EndGame ("CL_ParseTEnt: bad type"); - } -} - - -/* -================= -CL_NewTempEntity -================= -*/ -entity_t *CL_NewTempEntity (void) -{ - entity_t *ent; - - if (cl_numvisedicts == MAX_VISEDICTS) - return NULL; - ent = &cl_visedicts[cl_numvisedicts]; - cl_numvisedicts++; - ent->keynum = 0; - - memset (ent, 0, sizeof(*ent)); - - ent->colormap = vid.colormap; - return ent; -} - - -/* -================= -CL_UpdateBeams -================= -*/ -void vectoangles(vec3_t vec, vec3_t ang); - -void CL_UpdateBeams (void) -{ - int i; - beam_t *b; - vec3_t dist, org; - float d; - entity_t *ent; - float yaw, pitch; - float forward; - -// update lightning - for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++) - { - if (!b->model || b->endtime < cl.time) - continue; - - // if coming from the player, update the start position - if (b->entity == cl.playernum+1 || (cl.spectator && b->entity == Cam_TrackNum()+1 && cl_chasecam.value)) // entity 0 is the world - { - VectorCopy (cl.simorg, b->start); - b->start[2] += cl.crouch; - if (cl_trueLightning.value) - { - vec3_t forward, right, up; - vec3_t v, org; - vec3_t ang; - float f, delta; - pmtrace_t trace; - - f = max(0, min(1, cl_trueLightning.value)); - - VectorSubtract (playerbeam_end, cl.simorg, v); - v[2] -= 22; // adjust for view height - vectoangles (v, ang); - - // lerp pitch - ang[0] = -ang[0]; - if (ang[0] < -180) - ang[0] += 360; - ang[0] += (cl.viewangles[0] - ang[0])*f; - - // lerp yaw - delta = cl.viewangles[1] - ang[1]; - if (delta > 180) - delta -= 360; - if (delta < -180) - delta += 360; - ang[1] += delta*f; - ang[2] = 0; - - AngleVectors (ang, forward, right, up); - VectorScale (forward, 600, forward); - VectorCopy (cl.simorg, org); - org[2] += 16; - VectorAdd (org, forward, b->end); - - trace = PM_TraceLine (org, b->end); - if (trace.fraction < 1) - VectorCopy (trace.endpos, b->end); - } - } - - // calculate pitch and yaw - VectorSubtract (b->end, b->start, dist); - - if (dist[1] == 0 && dist[0] == 0) - { - yaw = 0; - if (dist[2] > 0) - pitch = 90; - else - pitch = 270; - } - else - { - yaw = (int) (atan2(dist[1], dist[0]) * 180 / M_PI); - if (yaw < 0) - yaw += 360; - - forward = sqrt (dist[0]*dist[0] + dist[1]*dist[1]); - pitch = (int) (atan2(dist[2], forward) * 180 / M_PI); - if (pitch < 0) - pitch += 360; - } - - // add new entities for the lightning - VectorCopy (b->start, org); - d = VectorNormalize(dist); - while (d > 0) - { - ent = CL_NewTempEntity (); - if (!ent) - return; - VectorCopy (org, ent->origin); - ent->model = b->model; - ent->angles[0] = pitch; - ent->angles[1] = yaw; - ent->angles[2] = rand()%360; - - for (i=0 ; i<3 ; i++) - org[i] += dist[i]*30; - d -= 30; - } - } - -} - -/* -================= -CL_UpdateExplosions -================= -*/ -void CL_UpdateExplosions (void) -{ - int i; - int f; - explosion_t *ex; - entity_t *ent; - - for (i=0, ex=cl_explosions ; i< MAX_EXPLOSIONS ; i++, ex++) - { - if (!ex->model) - continue; - f = 10*(cl.time - ex->start); - if (f >= ex->model->numframes) - { - ex->model = NULL; - continue; - } - - ent = CL_NewTempEntity (); - if (!ent) - return; - VectorCopy (ex->origin, ent->origin); - ent->model = ex->model; - ent->frame = f; - } -} - -/* -================= -CL_UpdateTEnts -================= -*/ -void CL_UpdateTEnts (void) -{ - CL_UpdateBeams (); - CL_UpdateExplosions (); -} +/* +Copyright (C) 1996-1997 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. + +*/ +// cl_tent.c -- client side temporary entities + +#include "quakedef.h" +#include "pmove.h" +#include "sound.h" + +#define MAX_BEAMS 8 +typedef struct +{ + int entity; + struct model_s *model; + float endtime; + vec3_t start, end; +} beam_t; + +beam_t cl_beams[MAX_BEAMS]; + +static vec3_t playerbeam_end; + + +#define MAX_EXPLOSIONS 8 +typedef struct +{ + vec3_t origin; + float start; + model_t *model; +} explosion_t; + +explosion_t cl_explosions[MAX_EXPLOSIONS]; + + +sfx_t *cl_sfx_wizhit; +sfx_t *cl_sfx_knighthit; +sfx_t *cl_sfx_tink1; +sfx_t *cl_sfx_ric1; +sfx_t *cl_sfx_ric2; +sfx_t *cl_sfx_ric3; +sfx_t *cl_sfx_r_exp3; + +/* +================= +CL_ParseTEnts +================= +*/ +void CL_InitTEnts (void) +{ + cl_sfx_wizhit = S_PrecacheSound ("wizard/hit.wav"); + cl_sfx_knighthit = S_PrecacheSound ("hknight/hit.wav"); + cl_sfx_tink1 = S_PrecacheSound ("weapons/tink1.wav"); + cl_sfx_ric1 = S_PrecacheSound ("weapons/ric1.wav"); + cl_sfx_ric2 = S_PrecacheSound ("weapons/ric2.wav"); + cl_sfx_ric3 = S_PrecacheSound ("weapons/ric3.wav"); + cl_sfx_r_exp3 = S_PrecacheSound ("weapons/r_exp3.wav"); +} + +/* +================= +CL_ClearTEnts +================= +*/ +void CL_ClearTEnts (void) +{ + memset (&cl_beams, 0, sizeof(cl_beams)); + memset (&cl_explosions, 0, sizeof(cl_explosions)); +} + +/* +================= +CL_AllocExplosion +================= +*/ +explosion_t *CL_AllocExplosion (void) +{ + int i; + float time; + int index; + + for (i=0 ; ientity == ent) + { + b->model = m; + b->endtime = cl.time + 0.2; + VectorCopy (start, b->start); + VectorCopy (end, b->end); + return; + } + +// find a free beam + for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++) + { + if (!b->model || b->endtime < cl.time) + { + b->entity = ent; + b->model = m; + b->endtime = cl.time + 0.2; + VectorCopy (start, b->start); + VectorCopy (end, b->end); + return; + } + } + Con_Printf ("beam list overflow!\n"); +} + +/* +================= +CL_ParseTEnt +================= +*/ +void CL_ParseTEnt (void) +{ + int type; + vec3_t pos; + dlight_t *dl; + int rnd; + explosion_t *ex; + int cnt; + + type = MSG_ReadByte (); + switch (type) + { + case TE_WIZSPIKE: // spike hitting wall + pos[0] = MSG_ReadCoord (); + pos[1] = MSG_ReadCoord (); + pos[2] = MSG_ReadCoord (); + R_RunParticleEffect (pos, vec3_origin, 20, 30); + S_StartSound (-1, 0, cl_sfx_wizhit, pos, 1, 1); + break; + + case TE_KNIGHTSPIKE: // spike hitting wall + pos[0] = MSG_ReadCoord (); + pos[1] = MSG_ReadCoord (); + pos[2] = MSG_ReadCoord (); + R_RunParticleEffect (pos, vec3_origin, 226, 20); + S_StartSound (-1, 0, cl_sfx_knighthit, pos, 1, 1); + break; + + case TE_SPIKE: // spike hitting wall + pos[0] = MSG_ReadCoord (); + pos[1] = MSG_ReadCoord (); + pos[2] = MSG_ReadCoord (); + R_RunParticleEffect (pos, vec3_origin, 0, 10); + + if ( rand() % 5 ) + S_StartSound (-1, 0, cl_sfx_tink1, pos, 1, 1); + else + { + rnd = rand() & 3; + if (rnd == 1) + S_StartSound (-1, 0, cl_sfx_ric1, pos, 1, 1); + else if (rnd == 2) + S_StartSound (-1, 0, cl_sfx_ric2, pos, 1, 1); + else + S_StartSound (-1, 0, cl_sfx_ric3, pos, 1, 1); + } + break; + case TE_SUPERSPIKE: // super spike hitting wall + pos[0] = MSG_ReadCoord (); + pos[1] = MSG_ReadCoord (); + pos[2] = MSG_ReadCoord (); + R_RunParticleEffect (pos, vec3_origin, 0, 20); + + if ( rand() % 5 ) + S_StartSound (-1, 0, cl_sfx_tink1, pos, 1, 1); + else + { + rnd = rand() & 3; + if (rnd == 1) + S_StartSound (-1, 0, cl_sfx_ric1, pos, 1, 1); + else if (rnd == 2) + S_StartSound (-1, 0, cl_sfx_ric2, pos, 1, 1); + else + S_StartSound (-1, 0, cl_sfx_ric3, pos, 1, 1); + } + break; + + case TE_EXPLOSION: // rocket explosion + // particles + + pos[0] = MSG_ReadCoord (); + pos[1] = MSG_ReadCoord (); + pos[2] = MSG_ReadCoord (); + + if (cl_explosion.value == 6) + R_TeleportSplash (pos); + else if (cl_explosion.value == 7) + R_RunParticleEffect (pos, vec3_origin, 73, 20*32); + else if (cl_explosion.value == 8) + R_RunParticleEffect (pos, vec3_origin, 225, 50); + else + { + + // Tonik: explosion modifiers... + if (cl_explosion.value != 1 && cl_explosion.value != 3) + { + R_ParticleExplosion (pos); + } + + // light + if (cl_explosion.value != 2 && cl_explosion.value != 3) + { + dl = CL_AllocDlight (0); + VectorCopy (pos, dl->origin); + dl->radius = 350; + dl->die = cl.time + 0.5; + dl->decay = 300; + dl->color[0] = 0.2; + dl->color[1] = 0.1; + dl->color[2] = 0.05; + dl->color[3] = 0.7; + } + } + + // sound + S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1); + + // sprite + if (cl_explosion.value != 6 && cl_explosion.value != 7 && cl_explosion.value != 8) + { + ex = CL_AllocExplosion (); + VectorCopy (pos, ex->origin); + ex->start = cl.time; + ex->model = Mod_ForName ("progs/s_explod.spr", true); + } + break; + + case TE_TAREXPLOSION: // tarbaby explosion + pos[0] = MSG_ReadCoord (); + pos[1] = MSG_ReadCoord (); + pos[2] = MSG_ReadCoord (); + R_BlobExplosion (pos); + + S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1); + break; + + case TE_LIGHTNING1: // lightning bolts + CL_ParseBeam (Mod_ForName("progs/bolt.mdl", true)); + break; + + case TE_LIGHTNING2: // lightning bolts + CL_ParseBeam (Mod_ForName("progs/bolt2.mdl", true)); + break; + + case TE_LIGHTNING3: // lightning bolts + CL_ParseBeam (Mod_ForName("progs/bolt3.mdl", true)); + break; + + case TE_LAVASPLASH: + pos[0] = MSG_ReadCoord (); + pos[1] = MSG_ReadCoord (); + pos[2] = MSG_ReadCoord (); + R_LavaSplash (pos); + break; + + case TE_TELEPORT: + pos[0] = MSG_ReadCoord (); + pos[1] = MSG_ReadCoord (); + pos[2] = MSG_ReadCoord (); + R_TeleportSplash (pos); + break; + + case TE_GUNSHOT: // bullet hitting wall + cnt = MSG_ReadByte (); + pos[0] = MSG_ReadCoord (); + pos[1] = MSG_ReadCoord (); + pos[2] = MSG_ReadCoord (); + R_RunParticleEffect (pos, vec3_origin, 0, 20*cnt); + break; + + case TE_BLOOD: // bullets hitting body + cnt = MSG_ReadByte (); + pos[0] = MSG_ReadCoord (); + pos[1] = MSG_ReadCoord (); + pos[2] = MSG_ReadCoord (); + R_RunParticleEffect (pos, vec3_origin, 73, 20*cnt); + break; + + case TE_LIGHTNINGBLOOD: // lightning hitting body + pos[0] = MSG_ReadCoord (); + pos[1] = MSG_ReadCoord (); + pos[2] = MSG_ReadCoord (); + R_RunParticleEffect (pos, vec3_origin, 225, 50); + break; + + default: + Host_EndGame ("CL_ParseTEnt: bad type"); + } +} + + +/* +================= +CL_NewTempEntity +================= +*/ +entity_t *CL_NewTempEntity (void) +{ + entity_t *ent; + + if (cl_numvisedicts == MAX_VISEDICTS) + return NULL; + ent = &cl_visedicts[cl_numvisedicts]; + cl_numvisedicts++; + ent->keynum = 0; + + memset (ent, 0, sizeof(*ent)); + + ent->colormap = vid.colormap; + return ent; +} + + +/* +================= +CL_UpdateBeams +================= +*/ +void vectoangles(vec3_t vec, vec3_t ang); + +void CL_UpdateBeams (void) +{ + int i; + beam_t *b; + vec3_t dist, org; + float d; + entity_t *ent; + float yaw, pitch; + float forward; + +// update lightning + for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++) + { + if (!b->model || b->endtime < cl.time) + continue; + + // if coming from the player, update the start position + if (b->entity == cl.playernum+1 || (cl.spectator && b->entity == Cam_TrackNum()+1 && cl_chasecam.value)) // entity 0 is the world + { + VectorCopy (cl.simorg, b->start); + b->start[2] += cl.crouch; + if (cl_trueLightning.value) + { + vec3_t forward, right, up; + vec3_t v, org; + vec3_t ang; + float f, delta; + pmtrace_t trace; + + f = max(0, min(1, cl_trueLightning.value)); + + VectorSubtract (playerbeam_end, cl.simorg, v); + v[2] -= 22; // adjust for view height + vectoangles (v, ang); + + // lerp pitch + ang[0] = -ang[0]; + if (ang[0] < -180) + ang[0] += 360; + ang[0] += (cl.viewangles[0] - ang[0])*f; + + // lerp yaw + delta = cl.viewangles[1] - ang[1]; + if (delta > 180) + delta -= 360; + if (delta < -180) + delta += 360; + ang[1] += delta*f; + ang[2] = 0; + + AngleVectors (ang, forward, right, up); + VectorScale (forward, 600, forward); + VectorCopy (cl.simorg, org); + org[2] += 16; + VectorAdd (org, forward, b->end); + + trace = PM_TraceLine (org, b->end); + if (trace.fraction < 1) + VectorCopy (trace.endpos, b->end); + } + } + + // calculate pitch and yaw + VectorSubtract (b->end, b->start, dist); + + if (dist[1] == 0 && dist[0] == 0) + { + yaw = 0; + if (dist[2] > 0) + pitch = 90; + else + pitch = 270; + } + else + { + yaw = (int) (atan2(dist[1], dist[0]) * 180 / M_PI); + if (yaw < 0) + yaw += 360; + + forward = sqrt (dist[0]*dist[0] + dist[1]*dist[1]); + pitch = (int) (atan2(dist[2], forward) * 180 / M_PI); + if (pitch < 0) + pitch += 360; + } + + // add new entities for the lightning + VectorCopy (b->start, org); + d = VectorNormalize(dist); + while (d > 0) + { + ent = CL_NewTempEntity (); + if (!ent) + return; + VectorCopy (org, ent->origin); + ent->model = b->model; + ent->angles[0] = pitch; + ent->angles[1] = yaw; + ent->angles[2] = rand()%360; + + for (i=0 ; i<3 ; i++) + org[i] += dist[i]*30; + d -= 30; + } + } + +} + +/* +================= +CL_UpdateExplosions +================= +*/ +void CL_UpdateExplosions (void) +{ + int i; + int f; + explosion_t *ex; + entity_t *ent; + + for (i=0, ex=cl_explosions ; i< MAX_EXPLOSIONS ; i++, ex++) + { + if (!ex->model) + continue; + f = 10*(cl.time - ex->start); + if (f >= ex->model->numframes) + { + ex->model = NULL; + continue; + } + + ent = CL_NewTempEntity (); + if (!ent) + return; + VectorCopy (ex->origin, ent->origin); + ent->model = ex->model; + ent->frame = f; + } +} + +/* +================= +CL_UpdateTEnts +================= +*/ +void CL_UpdateTEnts (void) +{ + CL_UpdateBeams (); + CL_UpdateExplosions (); +} diff --git a/source/client.h b/source/client.h index f633b4d9..ddb13bdb 100644 --- a/source/client.h +++ b/source/client.h @@ -1,569 +1,571 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// client.h - - -typedef struct -{ - char name[16]; - qboolean failedload; // the name isn't a valid skin - cache_user_t cache; -} skin_t; - -// player_state_t is the information needed by a player entity -// to do move prediction and to generate a drawable entity -typedef struct -{ - int messagenum; // all player's won't be updated each frame - - double state_time; // not the same as the packet time, - // because player commands come asyncronously - usercmd_t command; // last command for prediction - - vec3_t origin; - vec3_t viewangles; // only for demos, not from server - vec3_t velocity; - int weaponframe; - - int modelindex; - int frame; - int skinnum; - int effects; - - int flags; // dead, gib, etc - - float waterjumptime; - int onground; // -1 = in air, else pmove entity number - int oldbuttons; - int jump_msec; // fix bunny-hop flickering -} player_state_t; - - -#define MAX_SCOREBOARDNAME 16 -typedef struct player_info_s -{ - int userid; - char userinfo[MAX_INFO_STRING]; - - // scoreboard information - char name[MAX_SCOREBOARDNAME]; - float entertime; - int frags; - int ping; - byte pl; - - // skin information - int topcolor; - int bottomcolor; - - int _topcolor; - int _bottomcolor; - - int real_topcolor; - int real_bottomcolor; - char team[MAX_INFO_STRING]; - char _team[MAX_INFO_STRING]; - - int spectator; - byte translations[VID_GRADES*256]; - skin_t *skin; - int stats[MAX_CL_STATS]; // health, etc - int prevcount; // for delta update from previous -} player_info_t; - - -typedef struct -{ - // generated on client side - usercmd_t cmd; // cmd that generated the frame - double senttime; // time cmd was sent off - int delta_sequence; // sequence number to delta from, -1 = full update - - // received from server - double receivedtime; // time message was received, or -1 - player_state_t playerstate[MAX_CLIENTS]; // message received that reflects performing - // the usercmd - packet_entities_t packet_entities; - qboolean invalid; // true if the packet_entities delta was invalid -} frame_t; - - -typedef struct -{ - int destcolor[3]; - int percent; // 0-256 -} cshift_t; - -#define CSHIFT_CONTENTS 0 -#define CSHIFT_DAMAGE 1 -#define CSHIFT_BONUS 2 -#define CSHIFT_POWERUP 3 -#define NUM_CSHIFTS 4 - - -// -// client_state_t should hold all pieces of the client state -// -#define MAX_DLIGHTS 32 -typedef struct -{ - int key; // so entities can reuse same entry - vec3_t origin; - float radius; - float die; // stop lighting after this time - float decay; // drop this each second - float minlight; // don't add when contributing less - float color[4]; -} dlight_t; - -typedef struct -{ - int length; - char map[MAX_STYLESTRING]; -} lightstyle_t; - - - -#define MAX_EFRAGS 512 - -#define MAX_DEMOS 8 -#define MAX_DEMONAME 16 - -typedef enum { -ca_disconnected, // full screen console with no connection -ca_demostart, // starting up a demo -ca_connected, // netchan_t established, waiting for svc_serverdata -ca_onserver, // processing data lists, donwloading, etc -ca_active // everything is in, so frames can be rendered -} cactive_t; - -typedef enum { - dl_none, - dl_model, - dl_sound, - dl_skin, - dl_single -} dltype_t; // download type - -// -// the client_static_t structure is persistant through an arbitrary number -// of server connections -// -typedef struct -{ -// connection information - cactive_t state; - -// network stuff - netchan_t netchan; - -// private userinfo for sending to masterless servers - char userinfo[MAX_INFO_STRING]; - - char servername[MAX_OSPATH]; // name of server from original connect - - int qport; - - FILE *download; // file transfer from server - char downloadtempname[MAX_OSPATH]; - char downloadname[MAX_OSPATH]; - int downloadnumber; - dltype_t downloadtype; - int downloadpercent; - -// demo loop control - int demonum; // -1 = don't play demos - char demos[MAX_DEMOS][MAX_DEMONAME]; // when not playing - -// demo recording info must be here, because record is started before -// entering a map (and clearing client_state_t) - qboolean demorecording; - qboolean demoplayback; - qboolean demoplayback2; - int lastto; - int lasttype; - qboolean timedemo; - FILE *demofile; - FILE *recordfile; - float td_lastframe; // to meter out one message a frame - int td_startframe; // host_framecount at start - float td_starttime; // realtime at second frame of timedemo - - int challenge; - - float latency; // rolling average - qboolean findtrack; -} client_static_t; - -extern client_static_t cls; - -// -// the client_state_t structure is wiped completely at every -// server signon -// - -typedef struct -{ - qboolean interpolate; - vec3_t origin; - vec3_t angles; - int oldindex; -} interpolate_t; - -#define MAX_PROJECTILES 32 - -typedef struct -{ - int servercount; // server identification for prespawns - - char serverinfo[MAX_SERVERINFO_STRING]; -// some important serverinfo keys are mirrored here: - int gametype; // GAME_COOP or GAME_DEATHMATCH - qboolean teamfortress; // true if gamedir is "fortress" - int fpd; // FAQ proxy flags - - int parsecount; // server message counter - int oldparsecount; // previouse server message used for interpolation - int validsequence; // this is the sequence number of the last good - // packetentity_t we got. If this is 0, we can't - // render a frame yet - int movemessages; // since connecting to this server - // throw out the first couple, so the player - // doesn't accidentally do something the - // first frame - - int spectator; - - double last_ping_request; // while showing scoreboard - double last_servermessage; - -// sentcmds[cl.netchan.outgoing_sequence & UPDATE_MASK] = cmd - frame_t frames[UPDATE_BACKUP]; - -// information for local display - int stats[MAX_CL_STATS]; // health, etc - float item_gettime[32]; // cl.time of aquiring item, for blinking - float faceanimtime; // use anim frame if cl.time < this - - cshift_t cshifts[NUM_CSHIFTS]; // color shifts for damage, powerups and content types - -// the client maintains its own idea of view angles, which are -// sent to the server each frame. And only reset at level change -// and teleport times - vec3_t viewangles; - -// the client simulates or interpolates movement to get these values - double time; // this is the time value that the client - // is rendering at. always <= realtime - vec3_t simorg; - vec3_t simvel; - vec3_t simangles; - -// pitch drifting vars - float pitchvel; - qboolean nodrift; - float driftmove; - double laststop; - - int onground; // -1 when in air -- Tonik - float crouch; // local amount for smoothing stepups - - qboolean paused; // send over by server - - float punchangle; // temporar yview kick from weapon firing - - int intermission; // don't change view angle, full screen, etc - int completed_time; // latched ffrom time at intermission start - -// -// information that is static for the entire time connected to a server -// - char model_name[MAX_MODELS][MAX_QPATH]; - char sound_name[MAX_SOUNDS][MAX_QPATH]; - - struct model_s *model_precache[MAX_MODELS]; - struct sfx_s *sound_precache[MAX_SOUNDS]; - - char levelname[40]; // for display on solo scoreboard - int playernum; - -// refresh related state - struct model_s *worldmodel; // cl_entitites[0].model - struct efrag_s *free_efrags; - int num_entities; // stored bottom up in cl_entities array - int num_statics; // stored top down in cl_entitiers - - int cdtrack; // cd audio - - entity_t viewent; // weapon model - -// all player information - player_info_t players[MAX_CLIENTS]; - - char sprint_buf[1024]; // Tonik - - // interpolation stuff - interpolate_t int_entities[MAX_PACKET_ENTITIES]; - interpolate_t int_projectiles[MAX_PROJECTILES]; - int int_packet; - int int_prevnum[MAX_CLIENTS]; - -} client_state_t; - - -// -// cvars -// -extern cvar_t cl_warncmd; -extern cvar_t cl_upspeed; -extern cvar_t cl_forwardspeed; -extern cvar_t cl_backspeed; -extern cvar_t cl_sidespeed; - -extern cvar_t cl_movespeedkey; - -extern cvar_t cl_yawspeed; -extern cvar_t cl_pitchspeed; - -extern cvar_t cl_anglespeedkey; - -extern cvar_t cl_shownet; -extern cvar_t cl_sbar; -extern cvar_t cl_hudswap; - -extern cvar_t cl_pitchdriftspeed; -extern cvar_t lookspring; -extern cvar_t lookstrafe; -extern cvar_t sensitivity; - -extern cvar_t m_pitch; -extern cvar_t m_yaw; -extern cvar_t m_forward; -extern cvar_t m_side; - -extern cvar_t _windowed_mouse; - -extern cvar_t name; - -// ZQuake cvars -extern cvar_t cl_deadbodyfilter; -extern cvar_t cl_explosion; -extern cvar_t cl_gibfilter; -extern cvar_t cl_muzzleflash; -extern cvar_t cl_trueLightning; -extern cvar_t r_rocketlight; -extern cvar_t r_rockettrail; -extern cvar_t r_grenadetrail; -extern cvar_t r_powerupglow; - -extern cvar_t cl_teamskin; -extern cvar_t cl_enemyskin; - -#define MAX_STATIC_ENTITIES 128 // torches, etc - -extern client_state_t cl; - -// FIXME, allocate dynamically -extern entity_state_t cl_baselines[MAX_EDICTS]; -extern efrag_t cl_efrags[MAX_EFRAGS]; -extern entity_t cl_static_entities[MAX_STATIC_ENTITIES]; -extern lightstyle_t cl_lightstyle[MAX_LIGHTSTYLES]; -extern dlight_t cl_dlights[MAX_DLIGHTS]; - -extern float server_version; // version of server we connected to - -//============================================================================= - - -// -// cl_main -// -dlight_t *CL_AllocDlight (int key); -void CL_DecayLights (void); - -void CL_Init (void); -void Host_WriteConfiguration (void); - -void CL_EstablishConnection (char *host); - -void CL_Disconnect (void); -void CL_Disconnect_f (void); -void CL_NextDemo (void); -qboolean CL_DemoBehind(void); - -void CL_BeginServerConnect(void); - -#define MAX_VISEDICTS 256 -extern int cl_numvisedicts, cl_oldnumvisedicts; -extern entity_t *cl_visedicts, *cl_oldvisedicts; -extern entity_t cl_visedicts_list[2][MAX_VISEDICTS]; - -extern char emodel_name[], pmodel_name[], prespawn_name[], modellist_name[], soundlist_name[]; - -// -// cl_input -// -typedef struct -{ - int down[2]; // key nums holding it down - int state; // low bit is down state -} kbutton_t; - -extern kbutton_t in_mlook, in_klook; -extern kbutton_t in_strafe; -extern kbutton_t in_speed; - -void CL_InitInput (void); -void CL_SendCmd (void); -void CL_SendMove (usercmd_t *cmd); - -void CL_ParseTEnt (void); -void CL_UpdateTEnts (void); - -void CL_ClearState (void); - -void CL_ReadPackets (void); - -int CL_ReadFromServer (void); -void CL_WriteToServer (usercmd_t *cmd); -void CL_BaseMove (usercmd_t *cmd); - - -float CL_KeyState (kbutton_t *key); -char *Key_KeynumToString (int keynum); - -// -// cl_demo.c -// -void CL_StopPlayback (void); -qboolean CL_GetMessage (void); -void CL_WriteDemoCmd (usercmd_t *pcmd); - -void CL_Stop_f (void); -void CL_Record_f (void); -void CL_EasyRecord_f (void); -void CL_ReRecord_f (void); -void CL_PlayDemo_f (void); -void CL_TimeDemo_f (void); - -// -// cl_parse.c -// -#define NET_TIMINGS 256 -#define NET_TIMINGSMASK 255 -extern int packet_latency[NET_TIMINGS]; -int CL_CalcNet (void); -void CL_ParseServerMessage (void); -void CL_NewTranslation (int slot); -qboolean CL_CheckOrDownloadFile (char *filename); -qboolean CL_IsUploading(void); -void CL_NextUpload(void); -void CL_StartUpload (byte *data, int size); -void CL_StopUpload(void); - -// -// view.c -// -void V_StartPitchDrift (void); -void V_StopPitchDrift (void); - -void V_RenderView (void); -void V_UpdatePalette (void); -void V_Register (void); -void V_ParseDamage (void); -void V_SetContentsColor (int contents); -void V_CalcBlend (void); - - -// -// cl_tent -// -void CL_InitTEnts (void); -void CL_ClearTEnts (void); - -// -// cl_ents.c -// -void CL_SetSolidPlayers (int playernum); -void CL_SetUpPlayerPrediction(qboolean dopred); -void CL_EmitEntities (void); -void CL_ClearProjectiles (void); -void CL_ParseProjectiles (qboolean nail2); -void CL_ParsePacketEntities (qboolean delta); -void CL_SetSolidEntities (void); -void CL_ParsePlayerinfo (void); -void CL_Interpolate(void); -void CL_ParseClientdata (void); - -// -// cl_pred.c -// -void CL_InitPrediction (void); -void CL_PredictMove (qboolean nopred); -void CL_PredictUsercmd (player_state_t *from, player_state_t *to, usercmd_t *u, qboolean spectator); - -// -// cl_cam.c -// -#define CAM_NONE 0 -#define CAM_TRACK 1 - -extern int autocam; -extern int spec_track; // player# of who we are tracking - -qboolean Cam_DrawViewModel(void); -qboolean Cam_DrawPlayer(int playernum); -void Cam_Track(usercmd_t *cmd); -void Cam_FinishMove(usercmd_t *cmd); -void Cam_Reset(void); -void CL_InitCam(void); -int Cam_TrackNum(void); -void Cam_Lock(int playernum); - -// -// skin.c -// - -typedef struct -{ - char manufacturer; - char version; - char encoding; - char bits_per_pixel; - unsigned short xmin,ymin,xmax,ymax; - unsigned short hres,vres; - unsigned char palette[48]; - char reserved; - char color_planes; - unsigned short bytes_per_line; - unsigned short palette_type; - char filler[58]; - unsigned char data; // unbounded -} pcx_t; - - -void Skin_Find (player_info_t *sc); -byte *Skin_Cache (skin_t *skin); -void Skin_Skins_f (void); -void Skin_AllSkins_f (void); -void Skin_NextDownload (void); - -#define RSSHOT_WIDTH 320 -#define RSSHOT_HEIGHT 200 +/* +Copyright (C) 1996-1997 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. + +*/ +// client.h + + +typedef struct +{ + char name[16]; + qboolean failedload; // the name isn't a valid skin + cache_user_t cache; +} skin_t; + +// player_state_t is the information needed by a player entity +// to do move prediction and to generate a drawable entity +typedef struct +{ + int messagenum; // all player's won't be updated each frame + + double state_time; // not the same as the packet time, + // because player commands come asyncronously + usercmd_t command; // last command for prediction + + vec3_t origin; + vec3_t viewangles; // only for demos, not from server + vec3_t velocity; + int weaponframe; + + int modelindex; + int frame; + int skinnum; + int effects; + + int flags; // dead, gib, etc + + float waterjumptime; + int onground; // -1 = in air, else pmove entity number + int oldbuttons; + int jump_msec; // fix bunny-hop flickering +} player_state_t; + + +#define MAX_SCOREBOARDNAME 16 +typedef struct player_info_s +{ + int userid; + char userinfo[MAX_INFO_STRING]; + + // scoreboard information + char name[MAX_SCOREBOARDNAME]; + float entertime; + int frags; + int ping; + byte pl; + + // skin information + int topcolor; + int bottomcolor; + + int _topcolor; + int _bottomcolor; + + int real_topcolor; + int real_bottomcolor; + char team[MAX_INFO_STRING]; + char _team[MAX_INFO_STRING]; + + int spectator; + byte translations[VID_GRADES*256]; + skin_t *skin; + int stats[MAX_CL_STATS]; // health, etc + int prevcount; // for delta update from previous +} player_info_t; + + +typedef struct +{ + // generated on client side + usercmd_t cmd; // cmd that generated the frame + double senttime; // time cmd was sent off + int delta_sequence; // sequence number to delta from, -1 = full update + + // received from server + double receivedtime; // time message was received, or -1 + player_state_t playerstate[MAX_CLIENTS]; // message received that reflects performing + // the usercmd + packet_entities_t packet_entities; + qboolean invalid; // true if the packet_entities delta was invalid +} frame_t; + + +typedef struct +{ + int destcolor[3]; + int percent; // 0-256 +} cshift_t; + +#define CSHIFT_CONTENTS 0 +#define CSHIFT_DAMAGE 1 +#define CSHIFT_BONUS 2 +#define CSHIFT_POWERUP 3 +#define NUM_CSHIFTS 4 + + +// +// client_state_t should hold all pieces of the client state +// +#define MAX_DLIGHTS 32 +typedef struct +{ + int key; // so entities can reuse same entry + vec3_t origin; + float radius; + float die; // stop lighting after this time + float decay; // drop this each second + float minlight; // don't add when contributing less + float color[4]; +} dlight_t; + +typedef struct +{ + int length; + char map[MAX_STYLESTRING]; +} lightstyle_t; + + + +#define MAX_EFRAGS 512 + +#define MAX_DEMOS 8 +#define MAX_DEMONAME 16 + +typedef enum { +ca_disconnected, // full screen console with no connection +ca_demostart, // starting up a demo +ca_connected, // netchan_t established, waiting for svc_serverdata +ca_onserver, // processing data lists, donwloading, etc +ca_active // everything is in, so frames can be rendered +} cactive_t; + +typedef enum { + dl_none, + dl_model, + dl_sound, + dl_skin, + dl_single +} dltype_t; // download type + +// +// the client_static_t structure is persistant through an arbitrary number +// of server connections +// +typedef struct +{ +// connection information + cactive_t state; + +// network stuff + netchan_t netchan; + +// private userinfo for sending to masterless servers + char userinfo[MAX_INFO_STRING]; + + char servername[MAX_OSPATH]; // name of server from original connect + + int qport; + + FILE *download; // file transfer from server + char downloadtempname[MAX_OSPATH]; + char downloadname[MAX_OSPATH]; + int downloadnumber; + dltype_t downloadtype; + int downloadpercent; + +// demo loop control + int demonum; // -1 = don't play demos + char demos[MAX_DEMOS][MAX_DEMONAME]; // when not playing + +// demo recording info must be here, because record is started before +// entering a map (and clearing client_state_t) + qboolean demorecording; + qboolean demoplayback; + qboolean demoplayback2; + int lastto; + int lasttype; + int prevtime; + double basetime; + qboolean timedemo; + FILE *demofile; + FILE *recordfile; + float td_lastframe; // to meter out one message a frame + int td_startframe; // host_framecount at start + float td_starttime; // realtime at second frame of timedemo + + int challenge; + + float latency; // rolling average + qboolean findtrack; +} client_static_t; + +extern client_static_t cls; + +// +// the client_state_t structure is wiped completely at every +// server signon +// + +typedef struct +{ + qboolean interpolate; + vec3_t origin; + vec3_t angles; + int oldindex; +} interpolate_t; + +#define MAX_PROJECTILES 32 + +typedef struct +{ + int servercount; // server identification for prespawns + + char serverinfo[MAX_SERVERINFO_STRING]; +// some important serverinfo keys are mirrored here: + int gametype; // GAME_COOP or GAME_DEATHMATCH + qboolean teamfortress; // true if gamedir is "fortress" + int fpd; // FAQ proxy flags + + int parsecount; // server message counter + int oldparsecount; // previouse server message used for interpolation + int validsequence; // this is the sequence number of the last good + // packetentity_t we got. If this is 0, we can't + // render a frame yet + int movemessages; // since connecting to this server + // throw out the first couple, so the player + // doesn't accidentally do something the + // first frame + + int spectator; + + double last_ping_request; // while showing scoreboard + double last_servermessage; + +// sentcmds[cl.netchan.outgoing_sequence & UPDATE_MASK] = cmd + frame_t frames[UPDATE_BACKUP]; + +// information for local display + int stats[MAX_CL_STATS]; // health, etc + float item_gettime[32]; // cl.time of aquiring item, for blinking + float faceanimtime; // use anim frame if cl.time < this + + cshift_t cshifts[NUM_CSHIFTS]; // color shifts for damage, powerups and content types + +// the client maintains its own idea of view angles, which are +// sent to the server each frame. And only reset at level change +// and teleport times + vec3_t viewangles; + +// the client simulates or interpolates movement to get these values + double time; // this is the time value that the client + // is rendering at. always <= realtime + vec3_t simorg; + vec3_t simvel; + vec3_t simangles; + +// pitch drifting vars + float pitchvel; + qboolean nodrift; + float driftmove; + double laststop; + + int onground; // -1 when in air -- Tonik + float crouch; // local amount for smoothing stepups + + qboolean paused; // send over by server + + float punchangle; // temporar yview kick from weapon firing + + int intermission; // don't change view angle, full screen, etc + int completed_time; // latched ffrom time at intermission start + +// +// information that is static for the entire time connected to a server +// + char model_name[MAX_MODELS][MAX_QPATH]; + char sound_name[MAX_SOUNDS][MAX_QPATH]; + + struct model_s *model_precache[MAX_MODELS]; + struct sfx_s *sound_precache[MAX_SOUNDS]; + + char levelname[40]; // for display on solo scoreboard + int playernum; + +// refresh related state + struct model_s *worldmodel; // cl_entitites[0].model + struct efrag_s *free_efrags; + int num_entities; // stored bottom up in cl_entities array + int num_statics; // stored top down in cl_entitiers + + int cdtrack; // cd audio + + entity_t viewent; // weapon model + +// all player information + player_info_t players[MAX_CLIENTS]; + + char sprint_buf[1024]; // Tonik + + // interpolation stuff + interpolate_t int_entities[MAX_PACKET_ENTITIES]; + interpolate_t int_projectiles[MAX_PROJECTILES]; + int int_packet; + int int_prevnum[MAX_CLIENTS]; + +} client_state_t; + + +// +// cvars +// +extern cvar_t cl_warncmd; +extern cvar_t cl_upspeed; +extern cvar_t cl_forwardspeed; +extern cvar_t cl_backspeed; +extern cvar_t cl_sidespeed; + +extern cvar_t cl_movespeedkey; + +extern cvar_t cl_yawspeed; +extern cvar_t cl_pitchspeed; + +extern cvar_t cl_anglespeedkey; + +extern cvar_t cl_shownet; +extern cvar_t cl_sbar; +extern cvar_t cl_hudswap; + +extern cvar_t cl_pitchdriftspeed; +extern cvar_t lookspring; +extern cvar_t lookstrafe; +extern cvar_t sensitivity; + +extern cvar_t m_pitch; +extern cvar_t m_yaw; +extern cvar_t m_forward; +extern cvar_t m_side; + +extern cvar_t _windowed_mouse; + +extern cvar_t name; + +// ZQuake cvars +extern cvar_t cl_deadbodyfilter; +extern cvar_t cl_explosion; +extern cvar_t cl_gibfilter; +extern cvar_t cl_muzzleflash; +extern cvar_t cl_trueLightning; +extern cvar_t r_rocketlight; +extern cvar_t r_rockettrail; +extern cvar_t r_grenadetrail; +extern cvar_t r_powerupglow; + +extern cvar_t cl_teamskin; +extern cvar_t cl_enemyskin; + +#define MAX_STATIC_ENTITIES 128 // torches, etc + +extern client_state_t cl; + +// FIXME, allocate dynamically +extern entity_state_t cl_baselines[MAX_EDICTS]; +extern efrag_t cl_efrags[MAX_EFRAGS]; +extern entity_t cl_static_entities[MAX_STATIC_ENTITIES]; +extern lightstyle_t cl_lightstyle[MAX_LIGHTSTYLES]; +extern dlight_t cl_dlights[MAX_DLIGHTS]; + +extern float server_version; // version of server we connected to + +//============================================================================= + + +// +// cl_main +// +dlight_t *CL_AllocDlight (int key); +void CL_DecayLights (void); + +void CL_Init (void); +void Host_WriteConfiguration (void); + +void CL_EstablishConnection (char *host); + +void CL_Disconnect (void); +void CL_Disconnect_f (void); +void CL_NextDemo (void); +qboolean CL_DemoBehind(void); + +void CL_BeginServerConnect(void); + +#define MAX_VISEDICTS 256 +extern int cl_numvisedicts, cl_oldnumvisedicts; +extern entity_t *cl_visedicts, *cl_oldvisedicts; +extern entity_t cl_visedicts_list[2][MAX_VISEDICTS]; + +extern char emodel_name[], pmodel_name[], prespawn_name[], modellist_name[], soundlist_name[]; + +// +// cl_input +// +typedef struct +{ + int down[2]; // key nums holding it down + int state; // low bit is down state +} kbutton_t; + +extern kbutton_t in_mlook, in_klook; +extern kbutton_t in_strafe; +extern kbutton_t in_speed; + +void CL_InitInput (void); +void CL_SendCmd (void); +void CL_SendMove (usercmd_t *cmd); + +void CL_ParseTEnt (void); +void CL_UpdateTEnts (void); + +void CL_ClearState (void); + +void CL_ReadPackets (void); + +int CL_ReadFromServer (void); +void CL_WriteToServer (usercmd_t *cmd); +void CL_BaseMove (usercmd_t *cmd); + + +float CL_KeyState (kbutton_t *key); +char *Key_KeynumToString (int keynum); + +// +// cl_demo.c +// +void CL_StopPlayback (void); +qboolean CL_GetMessage (void); +void CL_WriteDemoCmd (usercmd_t *pcmd); + +void CL_Stop_f (void); +void CL_Record_f (void); +void CL_EasyRecord_f (void); +void CL_ReRecord_f (void); +void CL_PlayDemo_f (void); +void CL_TimeDemo_f (void); + +// +// cl_parse.c +// +#define NET_TIMINGS 256 +#define NET_TIMINGSMASK 255 +extern int packet_latency[NET_TIMINGS]; +int CL_CalcNet (void); +void CL_ParseServerMessage (void); +void CL_NewTranslation (int slot); +qboolean CL_CheckOrDownloadFile (char *filename); +qboolean CL_IsUploading(void); +void CL_NextUpload(void); +void CL_StartUpload (byte *data, int size); +void CL_StopUpload(void); + +// +// view.c +// +void V_StartPitchDrift (void); +void V_StopPitchDrift (void); + +void V_RenderView (void); +void V_UpdatePalette (void); +void V_Register (void); +void V_ParseDamage (void); +void V_SetContentsColor (int contents); +void V_CalcBlend (void); + + +// +// cl_tent +// +void CL_InitTEnts (void); +void CL_ClearTEnts (void); + +// +// cl_ents.c +// +void CL_SetSolidPlayers (int playernum); +void CL_SetUpPlayerPrediction(qboolean dopred); +void CL_EmitEntities (void); +void CL_ClearProjectiles (void); +void CL_ParseProjectiles (qboolean nail2); +void CL_ParsePacketEntities (qboolean delta); +void CL_SetSolidEntities (void); +void CL_ParsePlayerinfo (void); +void CL_Interpolate(void); +void CL_ParseClientdata (void); + +// +// cl_pred.c +// +void CL_InitPrediction (void); +void CL_PredictMove (qboolean nopred); +void CL_PredictUsercmd (player_state_t *from, player_state_t *to, usercmd_t *u, qboolean spectator); + +// +// cl_cam.c +// +#define CAM_NONE 0 +#define CAM_TRACK 1 + +extern int autocam; +extern int spec_track; // player# of who we are tracking + +qboolean Cam_DrawViewModel(void); +qboolean Cam_DrawPlayer(int playernum); +void Cam_Track(usercmd_t *cmd); +void Cam_FinishMove(usercmd_t *cmd); +void Cam_Reset(void); +void CL_InitCam(void); +int Cam_TrackNum(void); +void Cam_Lock(int playernum); + +// +// skin.c +// + +typedef struct +{ + char manufacturer; + char version; + char encoding; + char bits_per_pixel; + unsigned short xmin,ymin,xmax,ymax; + unsigned short hres,vres; + unsigned char palette[48]; + char reserved; + char color_planes; + unsigned short bytes_per_line; + unsigned short palette_type; + char filler[58]; + unsigned char data; // unbounded +} pcx_t; + + +void Skin_Find (player_info_t *sc); +byte *Skin_Cache (skin_t *skin); +void Skin_Skins_f (void); +void Skin_AllSkins_f (void); +void Skin_NextDownload (void); + +#define RSSHOT_WIDTH 320 +#define RSSHOT_HEIGHT 200 diff --git a/source/cmd.c b/source/cmd.c index c8e626c1..c9f4395a 100644 --- a/source/cmd.c +++ b/source/cmd.c @@ -1,1166 +1,1172 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// cmd.c -- Quake script command processing module - -#include "quakedef.h" - -#ifndef SERVERONLY -void Cmd_ForwardToServer (void); -qboolean CL_CheckServerCommand (void); -#endif - -cvar_t cl_warncmd = {"cl_warncmd", "0"}; - -cbuf_t cbuf_main; -#ifndef SERVERONLY -cbuf_t cbuf_svc; -#endif - -cbuf_t *cbuf_current = NULL; - -//============================================================================= - -/* -============ -Cmd_Wait_f - -Causes execution of the remainder of the command buffer to be delayed until -next frame. This allows commands like: -bind g "impulse 5 ; +attack ; wait ; -attack ; impulse 2" -============ -*/ -void Cmd_Wait_f (void) -{ - if (cbuf_current) - cbuf_current->wait = true; -} - -/* -============================================================================= - - COMMAND BUFFER - -============================================================================= -*/ - - -void Cbuf_AddText (char *text) { Cbuf_AddTextEx (&cbuf_main, text); } -void Cbuf_InsertText (char *text) { Cbuf_InsertTextEx (&cbuf_main, text); } -void Cbuf_Execute () { Cbuf_ExecuteEx (&cbuf_main); } - -/* -============ -Cbuf_Init -============ -*/ -void Cbuf_Init (void) -{ - cbuf_main.text_start = cbuf_main.text_end = MAXCMDBUF / 2; - cbuf_main.wait = false; -#ifndef SERVERONLY - cbuf_svc.text_start = cbuf_svc.text_end = MAXCMDBUF / 2; - cbuf_svc.wait = false; -#endif -} - -/* -============ -Cbuf_AddText - -Adds command text at the end of the buffer -============ -*/ -void Cbuf_AddTextEx (cbuf_t *cbuf, char *text) -{ - int len; - int new_start; - int new_bufsize; - - len = strlen (text); - - if (cbuf->text_end + len <= MAXCMDBUF) - { - memcpy (cbuf->text_buf + cbuf->text_end, text, len); - cbuf->text_end += len; - return; - } - - new_bufsize = cbuf->text_end-cbuf->text_start+len; - if (new_bufsize > MAXCMDBUF) - { - Con_Printf ("Cbuf_AddText: overflow\n"); - return; - } - - // Calculate optimal position of text in buffer - new_start = (MAXCMDBUF - new_bufsize) / 2; - - memcpy (cbuf->text_buf + new_start, cbuf->text_buf + cbuf->text_start, cbuf->text_end-cbuf->text_start); - memcpy (cbuf->text_buf + new_start + cbuf->text_end-cbuf->text_start, text, len); - cbuf->text_start = new_start; - cbuf->text_end = cbuf->text_start + new_bufsize; -} - - -/* -============ -Cbuf_InsertText - -Adds command text immediately after the current command -Adds a \n to the text -============ -*/ -void Cbuf_InsertTextEx (cbuf_t *cbuf, char *text) -{ - int len; - int new_start; - int new_bufsize; - - len = strlen(text); - - if (len < cbuf->text_start) - { - memcpy (cbuf->text_buf + (cbuf->text_start - len - 1), text, len); - cbuf->text_buf[cbuf->text_start-1] = '\n'; - cbuf->text_start -= len + 1; - return; - } - - new_bufsize = cbuf->text_end - cbuf->text_start + len + 1; - if (new_bufsize > MAXCMDBUF) - { - Con_Printf ("Cbuf_InsertText: overflow\n"); - return; - } - - // Calculate optimal position of text in buffer - new_start = (MAXCMDBUF - new_bufsize) / 2; - - memmove (cbuf->text_buf + (new_start + len + 1), cbuf->text_buf + cbuf->text_start, cbuf->text_end-cbuf->text_start); - memcpy (cbuf->text_buf + new_start, text, len); - cbuf->text_buf[new_start + len] = '\n'; - cbuf->text_start = new_start; - cbuf->text_end = cbuf->text_start + new_bufsize; -} - -/* -============ -Cbuf_Execute -============ -*/ -void Cbuf_ExecuteEx (cbuf_t *cbuf) -{ - int i; - char *text; - char line[1024]; - int quotes; - int cursize; - - cbuf_current = cbuf; - - while (cbuf->text_end > cbuf->text_start) - { -// find a \n or ; line break - text = (char *)cbuf->text_buf + cbuf->text_start; - - cursize = cbuf->text_end - cbuf->text_start; - quotes = 0; - for (i=0 ; i 0 && line[i-1] == '\r') - line[i-1] = 0; // remove DOS ending CR - -// delete the text from the command buffer and move remaining commands down -// this is necessary because commands (exec, alias) can insert data at the -// beginning of the text buffer - - if (i == cursize) - { - cbuf->text_start = cbuf->text_end = MAXCMDBUF/2; - } - else - { - i++; - cbuf->text_start += i; - } - -// execute the command line - Cmd_ExecuteString (line); - - if (cbuf->wait) - { // skip out while text still remains in buffer, leaving it - // for next frame - cbuf->wait = false; - break; - } - } - - cbuf_current = NULL; -} - -/* -============================================================================== - - SCRIPT COMMANDS - -============================================================================== -*/ - -/* -=============== -Cmd_StuffCmds_f - -Adds command line parameters as script statements -Commands lead with a +, and continue until a - or another + -quake +prog jctest.qp +cmd amlev1 -quake -nosound +cmd amlev1 -=============== -*/ -void Cmd_StuffCmds_f (void) -{ - int i, j; - int s; - char *text, *build, c; - -// build the combined string to parse from - s = 0; - for (i=1 ; i : execute a script file\n"); - return; - } - - // FIXME: is this safe freeing the hunk here??? - mark = Hunk_LowMark (); - f = (char *)COM_LoadHunkFile (Cmd_Argv(1)); - if (!f) - { - Con_Printf ("couldn't exec %s\n",Cmd_Argv(1)); - return; - } - if (!Cvar_Command () && (cl_warncmd.value || developer.value)) - Con_Printf ("execing %s\n",Cmd_Argv(1)); - -#ifndef SERVERONLY - if (cbuf_current == &cbuf_svc) - Cbuf_AddText (f); - else -#endif - Cbuf_InsertText (f); - Hunk_FreeToLowMark (mark); -} - - -/* -=============== -Cmd_Echo_f - -Just prints the rest of the line to the console -=============== -*/ -void Cmd_Echo_f (void) -{ - int i; - - for (i=1 ; ihash_next) - { - if (!Q_strcasecmp(name, alias->name)) - return alias; - } - return NULL; -} - -char *Cmd_AliasString (char *name) -{ - int key; - cmd_alias_t *alias; - - key = Key (name); - for (alias = cmd_alias_hash[key] ; alias ; alias = alias->hash_next) - { - if (!Q_strcasecmp(name, alias->name)) - return alias->value; - } - return NULL; -} - - -/* -=============== -Cmd_Alias_f - -Creates a new command that executes a command string (possibly ; seperated) -=============== -*/ - -char *CopyString (char *in) -{ - char *out; - - out = Z_Malloc (strlen(in)+1); - strcpy (out, in); - return out; -} - -void Cmd_Alias_f (void) -{ - cmd_alias_t *a; - char cmd[1024]; - int i, c; - int key; - char *s; -// cvar_t *var; - - c = Cmd_Argc(); - if (c == 1) - { - Con_Printf ("Current alias commands:\n"); - for (a = cmd_alias ; a ; a=a->next) - Con_Printf ("%s : %s\n\n", a->name, a->value); - return; - } - - s = Cmd_Argv(1); - if (strlen(s) >= MAX_ALIAS_NAME) - { - Con_Printf ("Alias name is too long\n"); - return; - } - -#if 0 - if ( (var = Cvar_FindVar(s)) != NULL ) { - if (var->flags & CVAR_USER_CREATED) - Cvar_Delete (var->name); - else { -// Con_Printf ("%s is a variable\n"); - return; - } - } -#endif - - key = Key(s); - - // if the alias already exists, reuse it - for (a = cmd_alias_hash[key] ; a ; a=a->hash_next) - { - if (!Q_strcasecmp(a->name, s)) - { - Z_Free (a->value); - break; - } - } - - if (!a) - { - a = Z_Malloc (sizeof(cmd_alias_t)); - a->next = cmd_alias; - cmd_alias = a; - a->hash_next = cmd_alias_hash[key]; - cmd_alias_hash[key] = a; - } - strcpy (a->name, s); - -// copy the rest of the command line - cmd[0] = 0; // start out with a null string - for (i=2 ; i 2) - strcat (cmd, " "); - strcat (cmd, Cmd_Argv(i)); - } - - a->value = CopyString (cmd); -} - - -qboolean Cmd_DeleteAlias (char *name) -{ - cmd_alias_t *a, *prev; - int key; - - key = Key (name); - - prev = NULL; - for (a = cmd_alias_hash[key] ; a ; a = a->hash_next) - { - if (!Q_strcasecmp(a->name, name)) - { - // unlink from hash - if (prev) - prev->hash_next = a->hash_next; - else - cmd_alias_hash[key] = a->hash_next; - break; - } - prev = a; - } - - if (!a) - return false; // not found - - prev = NULL; - for (a = cmd_alias ; a ; a = a->next) - { - if (!Q_strcasecmp(a->name, name)) - { - // unlink from alias list - if (prev) - prev->next = a->next; - else - cmd_alias = a->next; - - // free - Z_Free (a->value); - Z_Free (a); - return true; - } - prev = a; - } - - Sys_Error ("Cmd_DeleteAlias: alias list broken"); - return false; // shut up compiler -} - -void Cmd_UnAlias_f (void) -{ - char *s; - - if (Cmd_Argc() != 2) - { - Con_Printf ("unalias : erase an existing alias\n"); - return; - } - - s = Cmd_Argv(1); - if (strlen(s) >= MAX_ALIAS_NAME) - { - Con_Printf ("Alias name is too long\n"); - return; - } - - if (!Cmd_DeleteAlias(s)) - Con_Printf ("Unknown alias \"%s\"\n", s); -} - -// remove all aliases -void Cmd_UnAliasAll_f (void) -{ - cmd_alias_t *a, *next; - int i; - - for (a=cmd_alias ; a ; a=next) { - next = a->next; - Z_Free (a->value); - Z_Free (a); - } - cmd_alias = NULL; - - // clear hash - for (i=0 ; i<32 ; i++) { - cmd_alias_hash[i] = NULL; - } -} - - -/* -============================================================================= - - COMMAND EXECUTION - -============================================================================= -*/ - -#define MAX_ARGS 80 - -static int cmd_argc; -static char *cmd_argv[MAX_ARGS]; -static char *cmd_null_string = ""; -static char *cmd_args = NULL; - -static cmd_function_t *cmd_hash_array[32]; -static cmd_function_t *cmd_functions; // possible commands to execute - -/* -============ -Cmd_Argc -============ -*/ -int Cmd_Argc (void) -{ - return cmd_argc; -} - -/* -============ -Cmd_Argv -============ -*/ -char *Cmd_Argv (int arg) -{ - if ( arg >= cmd_argc ) - return cmd_null_string; - return cmd_argv[arg]; -} - -/* -============ -Cmd_Args - -Returns a single string containing argv(1) to argv(argc()-1) -============ -*/ -char *Cmd_Args (void) -{ - if (!cmd_args) - return ""; - return cmd_args; -} - - -/* -============ -Cmd_TokenizeString - -Parses the given string into command line tokens. -============ -*/ -void Cmd_TokenizeString (char *text) -{ - int idx; - static char argv_buf[1024]; - - idx = 0; - - cmd_argc = 0; - cmd_args = NULL; - - while (1) - { - // skip whitespace - while (*text == ' ' || *text == '\t' || *text == '\r') - { - text++; - } - - if (*text == '\n') - { // a newline seperates commands in the buffer - text++; - break; - } - - if (!*text) - return; - - if (cmd_argc == 1) - cmd_args = text; - - text = COM_Parse (text); - if (!text) - return; - - if (cmd_argc < MAX_ARGS) - { - cmd_argv[cmd_argc] = argv_buf + idx; - strcpy (cmd_argv[cmd_argc], com_token); - idx += strlen(com_token) + 1; - cmd_argc++; - } - } - -} - - -/* -============ -Cmd_AddCommand -============ -*/ -void Cmd_AddCommand (char *cmd_name, xcommand_t function) -{ - cmd_function_t *cmd; - int key; - - if (host_initialized) // because hunk allocation would get stomped - Sys_Error ("Cmd_AddCommand after host_initialized"); - -// fail if the command is a variable name - if (Cvar_FindVar(cmd_name)) - { - Con_Printf ("Cmd_AddCommand: %s already defined as a var\n", cmd_name); - return; - } - - key = Key (cmd_name); - -// fail if the command already exists - for (cmd=cmd_hash_array[key] ; cmd ; cmd=cmd->hash_next) - { - if (!Q_strcasecmp (cmd_name, cmd->name)) - { - Con_Printf ("Cmd_AddCommand: %s already defined\n", cmd_name); - return; - } - } - - cmd = Hunk_Alloc (sizeof(cmd_function_t)); - cmd->name = cmd_name; - cmd->function = function; - cmd->next = cmd_functions; - cmd_functions = cmd; - cmd->hash_next = cmd_hash_array[key]; - cmd_hash_array[key] = cmd; -} - - -/* -============ -Cmd_Exists -============ -*/ -qboolean Cmd_Exists (char *cmd_name) -{ - int key; - cmd_function_t *cmd; - - key = Key (cmd_name); - for (cmd=cmd_hash_array[key] ; cmd ; cmd=cmd->hash_next) - { - if (!Q_strcasecmp (cmd_name, cmd->name)) - return true; - } - - return false; -} - - -/* -============ -Cmd_FindCommand -============ -*/ -cmd_function_t *Cmd_FindCommand (char *cmd_name) -{ - int key; - cmd_function_t *cmd; - - key = Key (cmd_name); - for (cmd=cmd_hash_array[key] ; cmd ; cmd=cmd->hash_next) - { - if (!Q_strcasecmp (cmd_name, cmd->name)) - return cmd; - } - - return NULL; -} - - -/* -============ -Cmd_CompleteCommand -============ -*/ -char *Cmd_CompleteCommand (char *partial) -{ - cmd_function_t *cmd; - int len; - cmd_alias_t *alias; - - len = strlen(partial); - - if (!len) - return NULL; - -// check for exact match - for (cmd=cmd_functions ; cmd ; cmd=cmd->next) - if (!Q_strcasecmp (partial, cmd->name)) - return cmd->name; - for (alias=cmd_alias ; alias ; alias=alias->next) - if (!Q_strcasecmp (partial, alias->name)) - return alias->name; - -// check for partial match - for (cmd=cmd_functions ; cmd ; cmd=cmd->next) - if (!Q_strncasecmp (partial, cmd->name, len)) - return cmd->name; - for (alias=cmd_alias ; alias ; alias=alias->next) - if (!Q_strncasecmp (partial, alias->name, len)) - return alias->name; - - return NULL; -} - - -void Cmd_CmdList_f (void) -{ - cmd_function_t *cmd; - int i; - - for (cmd=cmd_functions, i=0 ; cmd ; cmd=cmd->next, i++) - Con_Printf("%s\n", cmd->name); - - Con_Printf ("------------\n%d commands\n", i); -} - - -/* -=========== -Cmd_Z_Cmd_f -=========== -_z_cmd -Just executes the rest of the string. -Can be used to do some zqwcl-specific action, e.g. "_z_cmd exec tonik_z.cfg" -*/ -void Cmd_Z_Cmd_f (void) -{ - Cbuf_InsertText (Cmd_Args()); - Cbuf_InsertText ("\n"); -} - - -/* -================ -Cmd_ExpandString - -Expands all $cvar expressions to cvar values -If not SERVERONLY, also expands $macro expressions -Note: dest must point to a 1024 byte buffer -================ -*/ -char *TP_MacroString (char *s); -void Cmd_ExpandString (char *data, char *dest) -{ - unsigned int c; - char buf[255]; - int i, len; - cvar_t *var, *bestvar; - int quotes = 0; - char *str; - int name_length; -#ifndef SERVERONLY - extern int macro_length; -#endif - - len = 0; - - while ( (c = *data) != 0) - { - if (c == '"') - quotes++; - - if (c == '$' && !(quotes&1)) - { - data++; - - // Copy the text after '$' to a temp buffer - i = 0; - buf[0] = 0; - bestvar = NULL; - while ((c = *data) > 32) - { - if (c == '$') - break; - data++; - buf[i++] = c; - buf[i] = 0; - if ( (var = Cvar_FindVar(buf)) != NULL ) - bestvar = var; - } - -#ifndef SERVERONLY - str = TP_MacroString (buf); - name_length = macro_length; - if (bestvar && (!str || (strlen(bestvar->name) > macro_length))) { - str = bestvar->string; - name_length = strlen(bestvar->name); - } -#else - if (bestvar) { - str = bestvar->string; - name_length = strlen(bestvar->name); - } else - str = NULL; -#endif - - if (str) - { - // check buffer size - if (len + strlen(str) >= 1024-1) - break; - - strcpy(&dest[len], str); - len += strlen(str); - i = name_length; - while (buf[i]) - dest[len++] = buf[i++]; - } - else - { - // no matching cvar or macro - dest[len++] = '$'; - if (len + strlen(buf) >= 1024-1) - break; - strcpy (&dest[len], buf); - len += strlen(buf); - } - } - else - { - dest[len] = c; - data++; - len++; - if (len >= 1024-1) - break; - } - }; - - dest[len] = 0; -} - - -/* -============ -Cmd_ExecuteString - -A complete command line has been parsed, so try to execute it -FIXME: lookupnoadd the token to speed search? -============ -*/ -void Cmd_ExecuteString (char *text) -{ - cmd_function_t *cmd; - cmd_alias_t *a; - int key; - static char buf[1024]; - - Cmd_ExpandString (text, buf); - Cmd_TokenizeString (buf); - -// execute the command line - if (!Cmd_Argc()) - return; // no tokens - -#ifndef SERVERONLY - if (cbuf_current == &cbuf_svc) { - if (CL_CheckServerCommand()) - return; - } -#endif - - key = Key (cmd_argv[0]); - -// check functions - for (cmd=cmd_hash_array[key] ; cmd ; cmd=cmd->hash_next) - { - if (!Q_strcasecmp (cmd_argv[0], cmd->name)) - { - if (cmd->function) - cmd->function (); -#ifndef SERVERONLY - else - Cmd_ForwardToServer (); -#endif - return; - } - } - -// check cvars - if (Cvar_Command()) - return; - -// check alias - for (a=cmd_alias_hash[key] ; a ; a=a->hash_next) - { - if (!Q_strcasecmp (cmd_argv[0], a->name)) - { -#ifndef SERVERONLY - if (cbuf_current == &cbuf_svc) { - Cbuf_AddText (a->value); - Cbuf_AddText ("\n"); - } - else -#endif - { - Cbuf_InsertText ("\n"); - Cbuf_InsertText (a->value); - } - return; - } - } - - if (cl_warncmd.value || developer.value) - Con_Printf ("Unknown command \"%s\"\n", Cmd_Argv(0)); -} - - -static qboolean is_numeric (char *c) -{ - return (*c >= '0' && *c <= '9') || - ((*c == '-' || *c == '+') && (c[1] == '.' || (c[1]>='0' && c[1]<='9'))) || - (*c == '.' && (c[1]>='0' && c[1]<='9')); -} -/* -================ -Cmd_If_f -================ -*/ -void Cmd_If_f (void) -{ - int i, c; - char *op; - qboolean result; - char buf[256]; - - c = Cmd_Argc (); - if (c < 5) { - Con_Printf ("usage: if [else ]\n"); - return; - } - - op = Cmd_Argv (2); - if (!strcmp(op, "==") || !strcmp(op, "=") || !strcmp(op, "!=") - || !strcmp(op, "<>")) - { - if (is_numeric(Cmd_Argv(1)) && is_numeric(Cmd_Argv(3))) - result = Q_atof(Cmd_Argv(1)) == Q_atof(Cmd_Argv(3)); - else - result = !strcmp(Cmd_Argv(1), Cmd_Argv(3)); - - if (op[0] != '=') - result = !result; - } - else if (!strcmp(op, ">")) - result = Q_atof(Cmd_Argv(1)) > Q_atof(Cmd_Argv(3)); - else if (!strcmp(op, "<")) - result = Q_atof(Cmd_Argv(1)) < Q_atof(Cmd_Argv(3)); - else if (!strcmp(op, ">=")) - result = Q_atof(Cmd_Argv(1)) >= Q_atof(Cmd_Argv(3)); - else if (!strcmp(op, "<=")) - result = Q_atof(Cmd_Argv(1)) <= Q_atof(Cmd_Argv(3)); - else { - Con_Printf ("unknown operator: %s\n", op); - Con_Printf ("valid operators are ==, =, !=, <>, >, <, >=, <=\n"); - return; - } - - buf[0] = '\0'; - if (result) - { - for (i=4; i < c ; i++) { - if ((i == 4) && !Q_strcasecmp(Cmd_Argv(i), "then")) - continue; - if (!Q_strcasecmp(Cmd_Argv(i), "else")) - break; - if (buf[0]) - strcat (buf, " "); - strcat (buf, Cmd_Argv(i)); - } - } - else - { - for (i=4; i < c ; i++) { - if (!Q_strcasecmp(Cmd_Argv(i), "else")) - break; - } - - if (i == c) - return; - - for (i++ ; i < c ; i++) { - if (buf[0]) - strcat (buf, " "); - strcat (buf, Cmd_Argv(i)); - } - } - - Cbuf_InsertText (buf); -} - - -/* -================ -Cmd_CheckParm - -Returns the position (1 to argc-1) in the command's argument list -where the given parameter apears, or 0 if not present -================ -*/ -int Cmd_CheckParm (char *parm) -{ - int i; - - if (!parm) - Sys_Error ("Cmd_CheckParm: NULL"); - - for (i = 1; i < Cmd_Argc (); i++) - if (! Q_strcasecmp (parm, Cmd_Argv (i))) - return i; - - return 0; -} - - -/* -============ -Cmd_Init -============ -*/ -void Cmd_Init (void) -{ -// -// register our commands -// - Cmd_AddCommand ("stuffcmds",Cmd_StuffCmds_f); - Cmd_AddCommand ("exec",Cmd_Exec_f); - Cmd_AddCommand ("echo",Cmd_Echo_f); - Cmd_AddCommand ("alias",Cmd_Alias_f); - Cmd_AddCommand ("wait", Cmd_Wait_f); - Cmd_AddCommand ("cmdlist", Cmd_CmdList_f); - Cmd_AddCommand ("unaliasall", Cmd_UnAliasAll_f); - Cmd_AddCommand ("unalias", Cmd_UnAlias_f); - Cmd_AddCommand ("if", Cmd_If_f); - Cmd_AddCommand ("_z_cmd", Cmd_Z_Cmd_f); // ZQuake -} +/* +Copyright (C) 1996-1997 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. + +*/ +// cmd.c -- Quake script command processing module + +#include "quakedef.h" + +#ifndef SERVERONLY +void Cmd_ForwardToServer (void); +qboolean CL_CheckServerCommand (void); +#endif + +cvar_t cl_warncmd = {"cl_warncmd", "0"}; + +cbuf_t cbuf_main; +#ifndef SERVERONLY +cbuf_t cbuf_svc; +#endif + +cbuf_t *cbuf_current = NULL; + +//============================================================================= + +/* +============ +Cmd_Wait_f + +Causes execution of the remainder of the command buffer to be delayed until +next frame. This allows commands like: +bind g "impulse 5 ; +attack ; wait ; -attack ; impulse 2" +============ +*/ +void Cmd_Wait_f (void) +{ + if (cbuf_current) + cbuf_current->wait = true; +} + +/* +============================================================================= + + COMMAND BUFFER + +============================================================================= +*/ + + +void Cbuf_AddText (char *text) { Cbuf_AddTextEx (&cbuf_main, text); } +void Cbuf_InsertText (char *text) { Cbuf_InsertTextEx (&cbuf_main, text); } +void Cbuf_Execute () { Cbuf_ExecuteEx (&cbuf_main); } + +/* +============ +Cbuf_Init +============ +*/ +void Cbuf_Init (void) +{ + cbuf_main.text_start = cbuf_main.text_end = MAXCMDBUF / 2; + cbuf_main.wait = false; +#ifndef SERVERONLY + cbuf_svc.text_start = cbuf_svc.text_end = MAXCMDBUF / 2; + cbuf_svc.wait = false; +#endif +} + +/* +============ +Cbuf_AddText + +Adds command text at the end of the buffer +============ +*/ +void Cbuf_AddTextEx (cbuf_t *cbuf, char *text) +{ + int len; + int new_start; + int new_bufsize; + + len = strlen (text); + + if (cbuf->text_end + len <= MAXCMDBUF) + { + memcpy (cbuf->text_buf + cbuf->text_end, text, len); + cbuf->text_end += len; + return; + } + + new_bufsize = cbuf->text_end-cbuf->text_start+len; + if (new_bufsize > MAXCMDBUF) + { + Con_Printf ("Cbuf_AddText: overflow\n"); + return; + } + + // Calculate optimal position of text in buffer + new_start = (MAXCMDBUF - new_bufsize) / 2; + + memcpy (cbuf->text_buf + new_start, cbuf->text_buf + cbuf->text_start, cbuf->text_end-cbuf->text_start); + memcpy (cbuf->text_buf + new_start + cbuf->text_end-cbuf->text_start, text, len); + cbuf->text_start = new_start; + cbuf->text_end = cbuf->text_start + new_bufsize; +} + + +/* +============ +Cbuf_InsertText + +Adds command text immediately after the current command +Adds a \n to the text +============ +*/ +void Cbuf_InsertTextEx (cbuf_t *cbuf, char *text) +{ + int len; + int new_start; + int new_bufsize; + + len = strlen(text); + + if (len < cbuf->text_start) + { + memcpy (cbuf->text_buf + (cbuf->text_start - len - 1), text, len); + cbuf->text_buf[cbuf->text_start-1] = '\n'; + cbuf->text_start -= len + 1; + return; + } + + new_bufsize = cbuf->text_end - cbuf->text_start + len + 1; + if (new_bufsize > MAXCMDBUF) + { + Con_Printf ("Cbuf_InsertText: overflow\n"); + return; + } + + // Calculate optimal position of text in buffer + new_start = (MAXCMDBUF - new_bufsize) / 2; + + memmove (cbuf->text_buf + (new_start + len + 1), cbuf->text_buf + cbuf->text_start, cbuf->text_end-cbuf->text_start); + memcpy (cbuf->text_buf + new_start, text, len); + cbuf->text_buf[new_start + len] = '\n'; + cbuf->text_start = new_start; + cbuf->text_end = cbuf->text_start + new_bufsize; +} + +/* +============ +Cbuf_Execute +============ +*/ +void Cbuf_ExecuteEx (cbuf_t *cbuf) +{ + int i; + char *text; + char line[1024]; + int quotes; + int cursize; + + cbuf_current = cbuf; + + while (cbuf->text_end > cbuf->text_start) + { +// find a \n or ; line break + text = (char *)cbuf->text_buf + cbuf->text_start; + + cursize = cbuf->text_end - cbuf->text_start; + quotes = 0; + for (i=0 ; i 0 && line[i-1] == '\r') + line[i-1] = 0; // remove DOS ending CR + +// delete the text from the command buffer and move remaining commands down +// this is necessary because commands (exec, alias) can insert data at the +// beginning of the text buffer + + if (i == cursize) + { + cbuf->text_start = cbuf->text_end = MAXCMDBUF/2; + } + else + { + i++; + cbuf->text_start += i; + } + +// execute the command line + Cmd_ExecuteString (line); + + if (cbuf->wait) + { // skip out while text still remains in buffer, leaving it + // for next frame + cbuf->wait = false; + break; + } + } + + cbuf_current = NULL; +} + + +/* +============================================================================== + + SCRIPT COMMANDS + +============================================================================== +*/ + +/* +=============== +Cmd_StuffCmds_f + +Adds command line parameters as script statements +Commands lead with a +, and continue until a - or another + +quake +prog jctest.qp +cmd amlev1 +quake -nosound +cmd amlev1 +=============== +*/ +void Cmd_StuffCmds_f (void) +{ + int i, j; + int s; + char *text, *build, c; + +// build the combined string to parse from + s = 0; + for (i=1 ; i : execute a script file\n"); + return; + } + + // FIXME: is this safe freeing the hunk here??? + mark = Hunk_LowMark (); + f = (char *)COM_LoadHunkFile (Cmd_Argv(1)); + if (!f) + { + Con_Printf ("couldn't exec %s\n",Cmd_Argv(1)); + return; + } + if (!Cvar_Command () && (cl_warncmd.value || developer.value)) + Con_Printf ("execing %s\n",Cmd_Argv(1)); + +#ifndef SERVERONLY + if (cbuf_current == &cbuf_svc) + Cbuf_AddText (f); + else +#endif + Cbuf_InsertText (f); + Hunk_FreeToLowMark (mark); +} + + +/* +=============== +Cmd_Echo_f + +Just prints the rest of the line to the console +=============== +*/ +void Cmd_Echo_f (void) +{ + int i; + + for (i=1 ; ihash_next) + { + if (!Q_strcasecmp(name, alias->name)) + return alias; + } + return NULL; +} + +char *Cmd_AliasString (char *name) +{ + int key; + cmd_alias_t *alias; + + key = Key (name); + for (alias = cmd_alias_hash[key] ; alias ; alias = alias->hash_next) + { + if (!Q_strcasecmp(name, alias->name)) + return alias->value; + } + return NULL; +} + + +/* +=============== +Cmd_Alias_f + +Creates a new command that executes a command string (possibly ; seperated) +=============== +*/ + +char *CopyString (char *in) +{ + char *out; + + out = Z_Malloc (strlen(in)+1); + strcpy (out, in); + return out; +} + +void Cmd_Alias_f (void) +{ + cmd_alias_t *a; + char cmd[1024]; + int i, c; + int key; + char *s; +// cvar_t *var; + + c = Cmd_Argc(); + if (c == 1) + { + Con_Printf ("Current alias commands:\n"); + for (a = cmd_alias ; a ; a=a->next) + Con_Printf ("%s : %s\n\n", a->name, a->value); + return; + } + + s = Cmd_Argv(1); + if (strlen(s) >= MAX_ALIAS_NAME) + { + Con_Printf ("Alias name is too long\n"); + return; + } + +#if 0 + if ( (var = Cvar_FindVar(s)) != NULL ) { + if (var->flags & CVAR_USER_CREATED) + Cvar_Delete (var->name); + else { +// Con_Printf ("%s is a variable\n"); + return; + } + } +#endif + + key = Key(s); + + // if the alias already exists, reuse it + for (a = cmd_alias_hash[key] ; a ; a=a->hash_next) + { + if (!Q_strcasecmp(a->name, s)) + { + Z_Free (a->value); + break; + } + } + + if (!a) + { + a = Z_Malloc (sizeof(cmd_alias_t)); + a->next = cmd_alias; + cmd_alias = a; + a->hash_next = cmd_alias_hash[key]; + cmd_alias_hash[key] = a; + } + strcpy (a->name, s); + +// copy the rest of the command line + cmd[0] = 0; // start out with a null string + for (i=2 ; i 2) + strcat (cmd, " "); + strcat (cmd, Cmd_Argv(i)); + } + + a->value = CopyString (cmd); +} + + +qboolean Cmd_DeleteAlias (char *name) +{ + cmd_alias_t *a, *prev; + int key; + + key = Key (name); + + prev = NULL; + for (a = cmd_alias_hash[key] ; a ; a = a->hash_next) + { + if (!Q_strcasecmp(a->name, name)) + { + // unlink from hash + if (prev) + prev->hash_next = a->hash_next; + else + cmd_alias_hash[key] = a->hash_next; + break; + } + prev = a; + } + + if (!a) + return false; // not found + + prev = NULL; + for (a = cmd_alias ; a ; a = a->next) + { + if (!Q_strcasecmp(a->name, name)) + { + // unlink from alias list + if (prev) + prev->next = a->next; + else + cmd_alias = a->next; + + // free + Z_Free (a->value); + Z_Free (a); + return true; + } + prev = a; + } + + Sys_Error ("Cmd_DeleteAlias: alias list broken"); + return false; // shut up compiler +} + +void Cmd_UnAlias_f (void) +{ + char *s; + + if (Cmd_Argc() != 2) + { + Con_Printf ("unalias : erase an existing alias\n"); + return; + } + + s = Cmd_Argv(1); + if (strlen(s) >= MAX_ALIAS_NAME) + { + Con_Printf ("Alias name is too long\n"); + return; + } + + if (!Cmd_DeleteAlias(s)) + Con_Printf ("Unknown alias \"%s\"\n", s); +} + +// remove all aliases +void Cmd_UnAliasAll_f (void) +{ + cmd_alias_t *a, *next; + int i; + + for (a=cmd_alias ; a ; a=next) { + next = a->next; + Z_Free (a->value); + Z_Free (a); + } + cmd_alias = NULL; + + // clear hash + for (i=0 ; i<32 ; i++) { + cmd_alias_hash[i] = NULL; + } +} + + +/* +============================================================================= + + COMMAND EXECUTION + +============================================================================= +*/ + +#define MAX_ARGS 80 + +static int cmd_argc; +static char *cmd_argv[MAX_ARGS]; +static char *cmd_null_string = ""; +static char *cmd_args = NULL; + +static cmd_function_t *cmd_hash_array[32]; +static cmd_function_t *cmd_functions; // possible commands to execute + +/* +============ +Cmd_Argc +============ +*/ +int Cmd_Argc (void) +{ + return cmd_argc; +} + +/* +============ +Cmd_Argv +============ +*/ +char *Cmd_Argv (int arg) +{ + if ( arg >= cmd_argc ) + return cmd_null_string; + return cmd_argv[arg]; +} + +/* +============ +Cmd_Args + +Returns a single string containing argv(1) to argv(argc()-1) +============ +*/ +char *Cmd_Args (void) +{ + if (!cmd_args) + return ""; + return cmd_args; +} + + +/* +============ +Cmd_TokenizeString + +Parses the given string into command line tokens. +============ +*/ +void Cmd_TokenizeString (char *text) +{ + int idx; + static char argv_buf[1024]; + + idx = 0; + + cmd_argc = 0; + cmd_args = NULL; + + while (1) + { + // skip whitespace + while (*text == ' ' || *text == '\t' || *text == '\r') + { + text++; + } + + if (*text == '\n') + { // a newline seperates commands in the buffer + text++; + break; + } + + if (!*text) + return; + + if (cmd_argc == 1) + cmd_args = text; + + text = COM_Parse (text); + if (!text) + return; + + if (cmd_argc < MAX_ARGS) + { + cmd_argv[cmd_argc] = argv_buf + idx; + strcpy (cmd_argv[cmd_argc], com_token); + idx += strlen(com_token) + 1; + cmd_argc++; + } + } + +} + + +/* +============ +Cmd_AddCommand +============ +*/ +void Cmd_AddCommand (char *cmd_name, xcommand_t function) +{ + cmd_function_t *cmd; + int key; + + if (host_initialized) // because hunk allocation would get stomped + Sys_Error ("Cmd_AddCommand after host_initialized"); + +// fail if the command is a variable name + if (Cvar_FindVar(cmd_name)) + { + Con_Printf ("Cmd_AddCommand: %s already defined as a var\n", cmd_name); + return; + } + + key = Key (cmd_name); + +// fail if the command already exists + for (cmd=cmd_hash_array[key] ; cmd ; cmd=cmd->hash_next) + { + if (!Q_strcasecmp (cmd_name, cmd->name)) + { + Con_Printf ("Cmd_AddCommand: %s already defined\n", cmd_name); + return; + } + } + + cmd = Hunk_Alloc (sizeof(cmd_function_t)); + cmd->name = cmd_name; + cmd->function = function; + cmd->next = cmd_functions; + cmd_functions = cmd; + cmd->hash_next = cmd_hash_array[key]; + cmd_hash_array[key] = cmd; +} + + +/* +============ +Cmd_Exists +============ +*/ +qboolean Cmd_Exists (char *cmd_name) +{ + int key; + cmd_function_t *cmd; + + key = Key (cmd_name); + for (cmd=cmd_hash_array[key] ; cmd ; cmd=cmd->hash_next) + { + if (!Q_strcasecmp (cmd_name, cmd->name)) + return true; + } + + return false; +} + + +/* +============ +Cmd_FindCommand +============ +*/ +cmd_function_t *Cmd_FindCommand (char *cmd_name) +{ + int key; + cmd_function_t *cmd; + + key = Key (cmd_name); + for (cmd=cmd_hash_array[key] ; cmd ; cmd=cmd->hash_next) + { + if (!Q_strcasecmp (cmd_name, cmd->name)) + return cmd; + } + + return NULL; +} + + +/* +============ +Cmd_CompleteCommand +============ +*/ +char *Cmd_CompleteCommand (char *partial) +{ + cmd_function_t *cmd; + int len; + cmd_alias_t *alias; + + len = strlen(partial); + + if (!len) + return NULL; + +// check for exact match + for (cmd=cmd_functions ; cmd ; cmd=cmd->next) + if (!Q_strcasecmp (partial, cmd->name)) + return cmd->name; + for (alias=cmd_alias ; alias ; alias=alias->next) + if (!Q_strcasecmp (partial, alias->name)) + return alias->name; + +// check for partial match + for (cmd=cmd_functions ; cmd ; cmd=cmd->next) + if (!Q_strncasecmp (partial, cmd->name, len)) + return cmd->name; + for (alias=cmd_alias ; alias ; alias=alias->next) + if (!Q_strncasecmp (partial, alias->name, len)) + return alias->name; + + return NULL; +} + + +void Cmd_CmdList_f (void) +{ + cmd_function_t *cmd; + int i; + + for (cmd=cmd_functions, i=0 ; cmd ; cmd=cmd->next, i++) + Con_Printf("%s\n", cmd->name); + + Con_Printf ("------------\n%d commands\n", i); +} + + +/* +=========== +Cmd_Z_Cmd_f +=========== +_z_cmd +Just executes the rest of the string. +Can be used to do some zqwcl-specific action, e.g. "_z_cmd exec tonik_z.cfg" +*/ +void Cmd_Z_Cmd_f (void) +{ + Cbuf_InsertText (Cmd_Args()); + Cbuf_InsertText ("\n"); +} + + +/* +================ +Cmd_ExpandString + +Expands all $cvar expressions to cvar values +If not SERVERONLY, also expands $macro expressions +Note: dest must point to a 1024 byte buffer +================ +*/ +char *TP_MacroString (char *s); +void Cmd_ExpandString (char *data, char *dest) +{ + unsigned int c; + char buf[255]; + int i, len; + cvar_t *var, *bestvar; + int quotes = 0; + char *str; + int name_length; +#ifndef SERVERONLY + extern int macro_length; +#endif + + len = 0; + + while ( (c = *data) != 0) + { + if (c == '"') + quotes++; + + if (c == '$' && !(quotes&1)) + { + data++; + + // Copy the text after '$' to a temp buffer + i = 0; + buf[0] = 0; + bestvar = NULL; + while ((c = *data) > 32) + { + if (c == '$') + break; + data++; + buf[i++] = c; + buf[i] = 0; + if ( (var = Cvar_FindVar(buf)) != NULL ) + bestvar = var; + } + +#ifndef SERVERONLY + str = TP_MacroString (buf); + name_length = macro_length; + if (bestvar && (!str || (strlen(bestvar->name) > macro_length))) { + str = bestvar->string; + name_length = strlen(bestvar->name); + } +#else + if (bestvar) { + str = bestvar->string; + name_length = strlen(bestvar->name); + } else + str = NULL; +#endif + + if (str) + { + // check buffer size + if (len + strlen(str) >= 1024-1) + break; + + strcpy(&dest[len], str); + len += strlen(str); + i = name_length; + while (buf[i]) + dest[len++] = buf[i++]; + } + else + { + // no matching cvar or macro + dest[len++] = '$'; + if (len + strlen(buf) >= 1024-1) + break; + strcpy (&dest[len], buf); + len += strlen(buf); + } + } + else + { + dest[len] = c; + data++; + len++; + if (len >= 1024-1) + break; + } + }; + + dest[len] = 0; +} + + +/* +============ +Cmd_ExecuteString + +A complete command line has been parsed, so try to execute it +FIXME: lookupnoadd the token to speed search? +============ +*/ +extern qboolean PR_ConsoleCmd(void); + +void Cmd_ExecuteString (char *text) +{ + cmd_function_t *cmd; + cmd_alias_t *a; + int key; + static char buf[1024]; + + Cmd_ExpandString (text, buf); + Cmd_TokenizeString (buf); + +// execute the command line + if (!Cmd_Argc()) + return; // no tokens + +#ifndef SERVERONLY + if (cbuf_current == &cbuf_svc) { + if (CL_CheckServerCommand()) + return; + } +#endif + + key = Key (cmd_argv[0]); + +// check functions + for (cmd=cmd_hash_array[key] ; cmd ; cmd=cmd->hash_next) + { + if (!Q_strcasecmp (cmd_argv[0], cmd->name)) + { + if (cmd->function) + cmd->function (); +#ifndef SERVERONLY + else + Cmd_ForwardToServer (); +#endif + return; + } + } + +// check cvars + if (Cvar_Command()) + return; + +// check alias + for (a=cmd_alias_hash[key] ; a ; a=a->hash_next) + { + if (!Q_strcasecmp (cmd_argv[0], a->name)) + { +#ifndef SERVERONLY + if (cbuf_current == &cbuf_svc) { + Cbuf_AddText (a->value); + Cbuf_AddText ("\n"); + } + else +#endif + { + Cbuf_InsertText ("\n"); + Cbuf_InsertText (a->value); + } + return; + } + } + +#ifdef SERVERONLY + if (PR_ConsoleCmd()) + return; +#endif + if (cl_warncmd.value || developer.value) + Con_Printf ("Unknown command \"%s\"\n", Cmd_Argv(0)); +} + + +static qboolean is_numeric (char *c) +{ + return (*c >= '0' && *c <= '9') || + ((*c == '-' || *c == '+') && (c[1] == '.' || (c[1]>='0' && c[1]<='9'))) || + (*c == '.' && (c[1]>='0' && c[1]<='9')); +} +/* +================ +Cmd_If_f +================ +*/ +void Cmd_If_f (void) +{ + int i, c; + char *op; + qboolean result; + char buf[256]; + + c = Cmd_Argc (); + if (c < 5) { + Con_Printf ("usage: if [else ]\n"); + return; + } + + op = Cmd_Argv (2); + if (!strcmp(op, "==") || !strcmp(op, "=") || !strcmp(op, "!=") + || !strcmp(op, "<>")) + { + if (is_numeric(Cmd_Argv(1)) && is_numeric(Cmd_Argv(3))) + result = Q_atof(Cmd_Argv(1)) == Q_atof(Cmd_Argv(3)); + else + result = !strcmp(Cmd_Argv(1), Cmd_Argv(3)); + + if (op[0] != '=') + result = !result; + } + else if (!strcmp(op, ">")) + result = Q_atof(Cmd_Argv(1)) > Q_atof(Cmd_Argv(3)); + else if (!strcmp(op, "<")) + result = Q_atof(Cmd_Argv(1)) < Q_atof(Cmd_Argv(3)); + else if (!strcmp(op, ">=")) + result = Q_atof(Cmd_Argv(1)) >= Q_atof(Cmd_Argv(3)); + else if (!strcmp(op, "<=")) + result = Q_atof(Cmd_Argv(1)) <= Q_atof(Cmd_Argv(3)); + else { + Con_Printf ("unknown operator: %s\n", op); + Con_Printf ("valid operators are ==, =, !=, <>, >, <, >=, <=\n"); + return; + } + + buf[0] = '\0'; + if (result) + { + for (i=4; i < c ; i++) { + if ((i == 4) && !Q_strcasecmp(Cmd_Argv(i), "then")) + continue; + if (!Q_strcasecmp(Cmd_Argv(i), "else")) + break; + if (buf[0]) + strcat (buf, " "); + strcat (buf, Cmd_Argv(i)); + } + } + else + { + for (i=4; i < c ; i++) { + if (!Q_strcasecmp(Cmd_Argv(i), "else")) + break; + } + + if (i == c) + return; + + for (i++ ; i < c ; i++) { + if (buf[0]) + strcat (buf, " "); + strcat (buf, Cmd_Argv(i)); + } + } + + Cbuf_InsertText (buf); +} + + +/* +================ +Cmd_CheckParm + +Returns the position (1 to argc-1) in the command's argument list +where the given parameter apears, or 0 if not present +================ +*/ +int Cmd_CheckParm (char *parm) +{ + int i; + + if (!parm) + Sys_Error ("Cmd_CheckParm: NULL"); + + for (i = 1; i < Cmd_Argc (); i++) + if (! Q_strcasecmp (parm, Cmd_Argv (i))) + return i; + + return 0; +} + +/* +============ +Cmd_Init +============ +*/ +void Cmd_Init (void) +{ +// +// register our commands +// + Cmd_AddCommand ("stuffcmds",Cmd_StuffCmds_f); + Cmd_AddCommand ("exec",Cmd_Exec_f); + Cmd_AddCommand ("echo",Cmd_Echo_f); + Cmd_AddCommand ("alias",Cmd_Alias_f); + Cmd_AddCommand ("wait", Cmd_Wait_f); + Cmd_AddCommand ("cmdlist", Cmd_CmdList_f); + Cmd_AddCommand ("unaliasall", Cmd_UnAliasAll_f); + Cmd_AddCommand ("unalias", Cmd_UnAlias_f); + Cmd_AddCommand ("if", Cmd_If_f); + Cmd_AddCommand ("_z_cmd", Cmd_Z_Cmd_f); // ZQuake +} diff --git a/source/cmd.h b/source/cmd.h index abaa3276..7de9f456 100644 --- a/source/cmd.h +++ b/source/cmd.h @@ -1,156 +1,156 @@ -/* -Copyright (C) 1996-1997 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. - -*/ - -// cmd.h -- Command buffer and command execution - -//=========================================================================== - -/* - -Any number of commands can be added in a frame, from several different sources. -Most commands come from either keybindings or console line input, but remote -servers can also send across commands and entire text files can be execed. - -The + command line options are also added to the command buffer. - -The game starts with a Cbuf_AddText ("exec quake.rc\n"); Cbuf_Execute (); - -*/ - -#define MAXCMDBUF 16384 - -typedef struct cbuf_s { - char text_buf[MAXCMDBUF]; - int text_start; - int text_end; - qboolean wait; -} cbuf_t; - -extern cbuf_t cbuf_main; -#ifndef SERVERONLY -extern cbuf_t cbuf_svc; -#endif -extern cbuf_t *cbuf_current; - -void Cbuf_AddTextEx (cbuf_t *cbuf, char *text); -void Cbuf_InsertTextEx (cbuf_t *cbuf, char *text); -void Cbuf_ExecuteEx (cbuf_t *cbuf); - -void Cbuf_Init (void); -// allocates an initial text buffer that will grow as needed - -void Cbuf_AddText (char *text); -// as new commands are generated from the console or keybindings, -// the text is added to the end of the command buffer. - -void Cbuf_InsertText (char *text); -// when a command wants to issue other commands immediately, the text is -// inserted at the beginning of the buffer, before any remaining unexecuted -// commands. - -void Cbuf_Execute (void); -// Pulls off \n terminated lines of text from the command buffer and sends -// them through Cmd_ExecuteString. Stops when the buffer is empty. -// Normally called once per frame, but may be explicitly invoked. -// Do not call inside a command function! - -//=========================================================================== - -/* - -Command execution takes a null terminated string, breaks it into tokens, -then searches for a command or variable that matches the first token. - -*/ - -typedef void (*xcommand_t) (void); - -typedef struct cmd_function_s -{ - struct cmd_function_s *hash_next; - struct cmd_function_s *next; - char *name; - xcommand_t function; -} cmd_function_t; - -void Cmd_Init (void); - -void Cmd_AddCommand (char *cmd_name, xcommand_t function); -// called by the init functions of other parts of the program to -// register commands and functions to call for them. -// The cmd_name is referenced later, so it should not be in temp memory -// if function is NULL, the command will be forwarded to the server -// as a clc_stringcmd instead of executed locally - -qboolean Cmd_Exists (char *cmd_name); -// used by the cvar code to check for cvar / command name overlap - -cmd_function_t *Cmd_FindCommand (char *cmd_name); // for message triggers - -char *Cmd_CompleteCommand (char *partial); -// attempts to match a partial command for automatic command line completion -// returns NULL if nothing fits - -int Cmd_Argc (void); -char *Cmd_Argv (int arg); -char *Cmd_Args (void); -// The functions that execute commands get their parameters with these -// functions. Cmd_Argv () will return an empty string, not a NULL -// if arg > argc, so string operations are always safe. - -int Cmd_CheckParm (char *parm); -// Returns the position (1 to argc-1) in the command's argument list -// where the given parameter apears, or 0 if not present - -void Cmd_ExpandString (char *data, char *dest); -// Expands all $cvar or $macro expressions. -// dest should point to a 1024-byte buffer - -void Cmd_TokenizeString (char *text); -// Takes a null terminated string. Does not need to be /n terminated. -// breaks the string up into arg tokens. - -void Cmd_ExecuteString (char *text); -// Parses a single line of text into arguments and tries to execute it -// as if it was typed at the console - -void Cmd_ForwardToServer (void); -// adds the current command line as a clc_stringcmd to the client message. -// things like godmode, noclip, etc, are commands directed to the server, -// so when they are typed in at the console, they will need to be forwarded. - -void Cmd_StuffCmds_f (void); - - -//=========================================================================== - -#define MAX_ALIAS_NAME 32 - -typedef struct cmd_alias_s -{ - struct cmd_alias_s *hash_next; - struct cmd_alias_s *next; - char name[MAX_ALIAS_NAME]; - char *value; -} cmd_alias_t; - -qboolean Cmd_DeleteAlias (char *name); // return true if successful -cmd_alias_t *Cmd_FindAlias (char *name); // returns NULL on failure -char *Cmd_AliasString (char *name); // returns NULL on failure +/* +Copyright (C) 1996-1997 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. + +*/ + +// cmd.h -- Command buffer and command execution + +//=========================================================================== + +/* + +Any number of commands can be added in a frame, from several different sources. +Most commands come from either keybindings or console line input, but remote +servers can also send across commands and entire text files can be execed. + +The + command line options are also added to the command buffer. + +The game starts with a Cbuf_AddText ("exec quake.rc\n"); Cbuf_Execute (); + +*/ + +#define MAXCMDBUF 16384 + +typedef struct cbuf_s { + char text_buf[MAXCMDBUF]; + int text_start; + int text_end; + qboolean wait; +} cbuf_t; + +extern cbuf_t cbuf_main; +#ifndef SERVERONLY +extern cbuf_t cbuf_svc; +#endif +extern cbuf_t *cbuf_current; + +void Cbuf_AddTextEx (cbuf_t *cbuf, char *text); +void Cbuf_InsertTextEx (cbuf_t *cbuf, char *text); +void Cbuf_ExecuteEx (cbuf_t *cbuf); + +void Cbuf_Init (void); +// allocates an initial text buffer that will grow as needed + +void Cbuf_AddText (char *text); +// as new commands are generated from the console or keybindings, +// the text is added to the end of the command buffer. + +void Cbuf_InsertText (char *text); +// when a command wants to issue other commands immediately, the text is +// inserted at the beginning of the buffer, before any remaining unexecuted +// commands. + +void Cbuf_Execute (void); +// Pulls off \n terminated lines of text from the command buffer and sends +// them through Cmd_ExecuteString. Stops when the buffer is empty. +// Normally called once per frame, but may be explicitly invoked. +// Do not call inside a command function! + +//=========================================================================== + +/* + +Command execution takes a null terminated string, breaks it into tokens, +then searches for a command or variable that matches the first token. + +*/ + +typedef void (*xcommand_t) (void); + +typedef struct cmd_function_s +{ + struct cmd_function_s *hash_next; + struct cmd_function_s *next; + char *name; + xcommand_t function; +} cmd_function_t; + +void Cmd_Init (void); + +void Cmd_AddCommand (char *cmd_name, xcommand_t function); +// called by the init functions of other parts of the program to +// register commands and functions to call for them. +// The cmd_name is referenced later, so it should not be in temp memory +// if function is NULL, the command will be forwarded to the server +// as a clc_stringcmd instead of executed locally + +qboolean Cmd_Exists (char *cmd_name); +// used by the cvar code to check for cvar / command name overlap + +cmd_function_t *Cmd_FindCommand (char *cmd_name); // for message triggers + +char *Cmd_CompleteCommand (char *partial); +// attempts to match a partial command for automatic command line completion +// returns NULL if nothing fits + +int Cmd_Argc (void); +char *Cmd_Argv (int arg); +char *Cmd_Args (void); +// The functions that execute commands get their parameters with these +// functions. Cmd_Argv () will return an empty string, not a NULL +// if arg > argc, so string operations are always safe. + +int Cmd_CheckParm (char *parm); +// Returns the position (1 to argc-1) in the command's argument list +// where the given parameter apears, or 0 if not present + +void Cmd_ExpandString (char *data, char *dest); +// Expands all $cvar or $macro expressions. +// dest should point to a 1024-byte buffer + +void Cmd_TokenizeString (char *text); +// Takes a null terminated string. Does not need to be /n terminated. +// breaks the string up into arg tokens. + +void Cmd_ExecuteString (char *text); +// Parses a single line of text into arguments and tries to execute it +// as if it was typed at the console + +void Cmd_ForwardToServer (void); +// adds the current command line as a clc_stringcmd to the client message. +// things like godmode, noclip, etc, are commands directed to the server, +// so when they are typed in at the console, they will need to be forwarded. + +void Cmd_StuffCmds_f (void); + + +//=========================================================================== + +#define MAX_ALIAS_NAME 32 + +typedef struct cmd_alias_s +{ + struct cmd_alias_s *hash_next; + struct cmd_alias_s *next; + char name[MAX_ALIAS_NAME]; + char *value; +} cmd_alias_t; + +qboolean Cmd_DeleteAlias (char *name); // return true if successful +cmd_alias_t *Cmd_FindAlias (char *name); // returns NULL on failure +char *Cmd_AliasString (char *name); // returns NULL on failure diff --git a/source/common.c b/source/common.c index c2990301..9e95b413 100644 --- a/source/common.c +++ b/source/common.c @@ -1,2080 +1,2121 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// common.c -- misc functions used in client and server - -#include - -#ifdef SERVERONLY -#include "qwsvdef.h" -#else -#include "quakedef.h" -#endif - -#define MAX_NUM_ARGVS 50 -#define NUM_SAFE_ARGVS 6 - -usercmd_t nullcmd; // guarenteed to be zero - -static char *largv[MAX_NUM_ARGVS + NUM_SAFE_ARGVS + 1]; -static char *argvdummy = " "; - -static char *safeargvs[NUM_SAFE_ARGVS] = - {"-stdvid", "-nolan", "-nosound", "-nocdaudio", "-nojoy", "-nomouse"}; - -cvar_t registered = {"registered","0"}; - -qboolean msg_suppress_1 = 0; - -void COM_InitFilesystem (void); -void COM_Path_f (void); - -char gamedirfile[MAX_OSPATH]; - -/* - - -All of Quake's data access is through a hierchal file system, but the contents of the file system can be transparently merged from several sources. - -The "base directory" is the path to the directory holding the quake.exe and all game directories. The sys_* files pass this to host_init in quakeparms_t->basedir. This can be overridden with the "-basedir" command line parm to allow code debugging in a different directory. The base directory is -only used during filesystem initialization. - -The "game directory" is the first tree on the search path and directory that all generated files (savegames, screenshots, demos, config files) will be saved to. This can be overridden with the "-game" command line parameter. The game directory can never be changed while quake is executing. This is a precacution against having a malicious server instruct clients to write files over areas they shouldn't. - -*/ - -//============================================================================ - - -// ClearLink is used for new headnodes -void ClearLink (link_t *l) -{ - l->prev = l->next = l; -} - -void RemoveLink (link_t *l) -{ - l->next->prev = l->prev; - l->prev->next = l->next; -} - -void InsertLinkBefore (link_t *l, link_t *before) -{ - l->next = before; - l->prev = before->prev; - l->prev->next = l; - l->next->prev = l; -} -void InsertLinkAfter (link_t *l, link_t *after) -{ - l->next = after->next; - l->prev = after; - l->prev->next = l; - l->next->prev = l; -} - -/* -============================================================================ - - LIBRARY REPLACEMENT FUNCTIONS - -============================================================================ -*/ - -int Q_atoi (char *str) -{ - int val; - int sign; - int c; - - if (*str == '-') - { - sign = -1; - str++; - } - else - sign = 1; - - val = 0; - -// -// check for hex -// - if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X') ) - { - str += 2; - while (1) - { - c = *str++; - if (c >= '0' && c <= '9') - val = (val<<4) + c - '0'; - else if (c >= 'a' && c <= 'f') - val = (val<<4) + c - 'a' + 10; - else if (c >= 'A' && c <= 'F') - val = (val<<4) + c - 'A' + 10; - else - return val*sign; - } - } - -// -// check for character -// - if (str[0] == '\'') - { - return sign * str[1]; - } - -// -// assume decimal -// - while (1) - { - c = *str++; - if (c <'0' || c > '9') - return val*sign; - val = val*10 + c - '0'; - } - - return 0; -} - - -float Q_atof (char *str) -{ - double val; - int sign; - int c; - int decimal, total; - - if (*str == '-') - { - sign = -1; - str++; - } - else - sign = 1; - - val = 0; - -// -// check for hex -// - if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X') ) - { - str += 2; - while (1) - { - c = *str++; - if (c >= '0' && c <= '9') - val = (val*16) + c - '0'; - else if (c >= 'a' && c <= 'f') - val = (val*16) + c - 'a' + 10; - else if (c >= 'A' && c <= 'F') - val = (val*16) + c - 'A' + 10; - else - return val*sign; - } - } - -// -// check for character -// - if (str[0] == '\'') - { - return sign * str[1]; - } - -// -// assume decimal -// - decimal = -1; - total = 0; - while (1) - { - c = *str++; - if (c == '.') - { - decimal = total; - continue; - } - if (c <'0' || c > '9') - break; - val = val*10 + c - '0'; - total++; - } - - if (decimal == -1) - return val*sign; - while (total > decimal) - { - val /= 10; - total--; - } - - return val*sign; -} - - -void Q_strncpyz (char *dest, char *src, size_t size) -{ - strncpy (dest, src, size-1); - dest[size-1] = 0; -} - - -/* -============================================================================ - - BYTE ORDER FUNCTIONS - -============================================================================ -*/ - -qboolean bigendien; - -short (*BigShort) (short l); -short (*LittleShort) (short l); -int (*BigLong) (int l); -int (*LittleLong) (int l); -float (*BigFloat) (float l); -float (*LittleFloat) (float l); - -short ShortSwap (short l) -{ - byte b1,b2; - - b1 = l&255; - b2 = (l>>8)&255; - - return (b1<<8) + b2; -} - -short ShortNoSwap (short l) -{ - return l; -} - -int LongSwap (int l) -{ - byte b1,b2,b3,b4; - - b1 = l&255; - b2 = (l>>8)&255; - b3 = (l>>16)&255; - b4 = (l>>24)&255; - - return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4; -} - -int LongNoSwap (int l) -{ - return l; -} - -float FloatSwap (float f) -{ - union - { - float f; - byte b[4]; - } dat1, dat2; - - - dat1.f = f; - dat2.b[0] = dat1.b[3]; - dat2.b[1] = dat1.b[2]; - dat2.b[2] = dat1.b[1]; - dat2.b[3] = dat1.b[0]; - return dat2.f; -} - -float FloatNoSwap (float f) -{ - return f; -} - -/* -============================================================================== - - MESSAGE IO FUNCTIONS - -Handles byte ordering and avoids alignment errors -============================================================================== -*/ - -// -// writing functions -// - -void MSG_WriteChar (sizebuf_t *sb, int c) -{ - byte *buf; - -#ifdef PARANOID - if (c < -128 || c > 127) - Sys_Error ("MSG_WriteChar: range error"); -#endif - - buf = SZ_GetSpace (sb, 1); - buf[0] = c; -} - -void MSG_WriteByte (sizebuf_t *sb, int c) -{ - byte *buf; - -#ifdef PARANOID - if (c < 0 || c > 255) - Sys_Error ("MSG_WriteByte: range error"); -#endif - - buf = SZ_GetSpace (sb, 1); - buf[0] = c; -} - -void MSG_WriteShort (sizebuf_t *sb, int c) -{ - byte *buf; - -#ifdef PARANOID - if (c < ((short)0x8000) || c > (short)0x7fff) - Sys_Error ("MSG_WriteShort: range error"); -#endif - - buf = SZ_GetSpace (sb, 2); - buf[0] = c&0xff; - buf[1] = c>>8; -} - -void MSG_WriteLong (sizebuf_t *sb, int c) -{ - byte *buf; - - buf = SZ_GetSpace (sb, 4); - buf[0] = c&0xff; - buf[1] = (c>>8)&0xff; - buf[2] = (c>>16)&0xff; - buf[3] = c>>24; -} - -void MSG_WriteFloat (sizebuf_t *sb, float f) -{ - union - { - float f; - int l; - } dat; - - - dat.f = f; - dat.l = LittleLong (dat.l); - - SZ_Write (sb, &dat.l, 4); -} - -void MSG_WriteString (sizebuf_t *sb, char *s) -{ - if (!s) - SZ_Write (sb, "", 1); - else - SZ_Write (sb, s, strlen(s)+1); -} - -void MSG_WriteCoord (sizebuf_t *sb, float f) -{ - MSG_WriteShort (sb, (int)(f*8)); -} - -void MSG_WriteAngle (sizebuf_t *sb, float f) -{ - MSG_WriteByte (sb, (int)(f*256/360) & 255); -} - -void MSG_WriteAngle16 (sizebuf_t *sb, float f) -{ - MSG_WriteShort (sb, (int)(f*65536/360) & 65535); -} - -void MSG_WriteDeltaUsercmd (sizebuf_t *buf, usercmd_t *from, usercmd_t *cmd) -{ - int bits; - -// -// send the movement message -// - bits = 0; - if (cmd->angles[0] != from->angles[0]) - bits |= CM_ANGLE1; - if (cmd->angles[1] != from->angles[1]) - bits |= CM_ANGLE2; - if (cmd->angles[2] != from->angles[2]) - bits |= CM_ANGLE3; - if (cmd->forwardmove != from->forwardmove) - bits |= CM_FORWARD; - if (cmd->sidemove != from->sidemove) - bits |= CM_SIDE; - if (cmd->upmove != from->upmove) - bits |= CM_UP; - if (cmd->buttons != from->buttons) - bits |= CM_BUTTONS; - if (cmd->impulse != from->impulse) - bits |= CM_IMPULSE; - - MSG_WriteByte (buf, bits); - - if (bits & CM_ANGLE1) - MSG_WriteAngle16 (buf, cmd->angles[0]); - if (bits & CM_ANGLE2) - MSG_WriteAngle16 (buf, cmd->angles[1]); - if (bits & CM_ANGLE3) - MSG_WriteAngle16 (buf, cmd->angles[2]); - - if (bits & CM_FORWARD) - MSG_WriteShort (buf, cmd->forwardmove); - if (bits & CM_SIDE) - MSG_WriteShort (buf, cmd->sidemove); - if (bits & CM_UP) - MSG_WriteShort (buf, cmd->upmove); - - if (bits & CM_BUTTONS) - MSG_WriteByte (buf, cmd->buttons); - if (bits & CM_IMPULSE) - MSG_WriteByte (buf, cmd->impulse); - MSG_WriteByte (buf, cmd->msec); -} - - -// -// reading functions -// -int msg_readcount; -qboolean msg_badread; - -void MSG_BeginReading (void) -{ - msg_readcount = 0; - msg_badread = false; -} - -int MSG_GetReadCount(void) -{ - return msg_readcount; -} - -// returns -1 and sets msg_badread if no more characters are available -int MSG_ReadChar (void) -{ - int c; - - if (msg_readcount+1 > net_message.cursize) - { - msg_badread = true; - return -1; - } - - c = (signed char)net_message.data[msg_readcount]; - msg_readcount++; - - return c; -} - -int MSG_ReadByte (void) -{ - int c; - - if (msg_readcount+1 > net_message.cursize) - { - msg_badread = true; - return -1; - } - - c = (unsigned char)net_message.data[msg_readcount]; - msg_readcount++; - - return c; -} - -int MSG_ReadShort (void) -{ - int c; - - if (msg_readcount+2 > net_message.cursize) - { - msg_badread = true; - return -1; - } - - c = (short)(net_message.data[msg_readcount] - + (net_message.data[msg_readcount+1]<<8)); - - msg_readcount += 2; - - return c; -} - -int MSG_ReadLong (void) -{ - int c; - - if (msg_readcount+4 > net_message.cursize) - { - msg_badread = true; - return -1; - } - - c = net_message.data[msg_readcount] - + (net_message.data[msg_readcount+1]<<8) - + (net_message.data[msg_readcount+2]<<16) - + (net_message.data[msg_readcount+3]<<24); - - msg_readcount += 4; - - return c; -} - -float MSG_ReadFloat (void) -{ - union - { - byte b[4]; - float f; - int l; - } dat; - - dat.b[0] = net_message.data[msg_readcount]; - dat.b[1] = net_message.data[msg_readcount+1]; - dat.b[2] = net_message.data[msg_readcount+2]; - dat.b[3] = net_message.data[msg_readcount+3]; - msg_readcount += 4; - - dat.l = LittleLong (dat.l); - - return dat.f; -} - -char *MSG_ReadString (void) -{ - static char string[2048]; - int l,c; - - l = 0; - do - { - c = MSG_ReadChar (); - if (c == -1 || c == 0) - break; - string[l] = c; - l++; - } while (l < sizeof(string)-1); - - string[l] = 0; - - return string; -} - -char *MSG_ReadStringLine (void) -{ - static char string[2048]; - int l,c; - - l = 0; - do - { - c = MSG_ReadChar (); - if (c == -1 || c == 0 || c == '\n') - break; - string[l] = c; - l++; - } while (l < sizeof(string)-1); - - string[l] = 0; - - return string; -} - -float MSG_ReadCoord (void) -{ - return MSG_ReadShort() * (1.0/8); -} - -float MSG_ReadAngle (void) -{ - return MSG_ReadChar() * (360.0/256); -} - -float MSG_ReadAngle16 (void) -{ - return MSG_ReadShort() * (360.0/65536); -} - -void MSG_ReadDeltaUsercmd (usercmd_t *from, usercmd_t *move) -{ - int bits; - extern int stat_size; - - memcpy (move, from, sizeof(*move)); - - bits = MSG_ReadByte (); - //stat_size +=1; - -// read current angles - if (bits & CM_ANGLE1) { - move->angles[0] = MSG_ReadAngle16 (); - //stat_size += 2; - } - if (bits & CM_ANGLE2) { - move->angles[1] = MSG_ReadAngle16 (); - //stat_size += 2; - } - if (bits & CM_ANGLE3) - { - move->angles[2] = MSG_ReadAngle16 (); - //stat_size += 2; - } - -// read movement - if (bits & CM_FORWARD) - { - move->forwardmove = MSG_ReadShort (); - //stat_size += 2; - } - if (bits & CM_SIDE) - { - move->sidemove = MSG_ReadShort (); - //stat_size += 2; - } - if (bits & CM_UP) - { - move->upmove = MSG_ReadShort (); - //stat_size += 2; - } - -// read buttons - if (bits & CM_BUTTONS) - move->buttons = MSG_ReadByte (); - - if (bits & CM_IMPULSE) - move->impulse = MSG_ReadByte (); - -// read time to run command - move->msec = MSG_ReadByte (); - //stat_size += 1; -} - - -//=========================================================================== - -void SZ_Clear (sizebuf_t *buf) -{ - buf->cursize = 0; - buf->overflowed = false; -} - -void *SZ_GetSpace (sizebuf_t *buf, int length) -{ - void *data; - - if (buf->cursize + length > buf->maxsize) - { - if (!buf->allowoverflow) - Sys_Error ("SZ_GetSpace: overflow without allowoverflow set (%d)", buf->maxsize); - - if (length > buf->maxsize) - Sys_Error ("SZ_GetSpace: %i is > full buffer size", length); - - Sys_Printf ("SZ_GetSpace: overflow\n"); // because Con_Printf may be redirected - SZ_Clear (buf); - buf->overflowed = true; - } - - data = buf->data + buf->cursize; - buf->cursize += length; - - return data; -} - -void SZ_Write (sizebuf_t *buf, void *data, int length) -{ - memcpy (SZ_GetSpace(buf,length),data,length); -} - -void SZ_Print (sizebuf_t *buf, char *data) -{ - int len; - - len = strlen(data)+1; - - if (!buf->cursize || buf->data[buf->cursize-1]) - memcpy ((byte *)SZ_GetSpace(buf, len),data,len); // no trailing 0 - else - memcpy ((byte *)SZ_GetSpace(buf, len-1)-1,data,len); // write over trailing 0 -} - - -//============================================================================ - - -/* -============ -COM_SkipPath -============ -*/ -char *COM_SkipPath (char *pathname) -{ - char *last; - - last = pathname; - while (*pathname) - { - if (*pathname=='/') - last = pathname+1; - pathname++; - } - return last; -} - -/* -============ -COM_StripExtension -============ -*/ -void COM_StripExtension (char *in, char *out) -{ - while (*in && *in != '.') - *out++ = *in++; - *out = 0; -} - -/* -============ -COM_FileExtension -============ -*/ -char *COM_FileExtension (char *in) -{ - static char exten[8]; - int i; - - while (*in && *in != '.') - in++; - if (!*in) - return ""; - in++; - for (i=0 ; i<7 && *in ; i++,in++) - exten[i] = *in; - exten[i] = 0; - return exten; -} - -/* -============ -COM_FileBase -============ -*/ -void COM_FileBase (char *in, char *out) -{ - char *s, *s2; - - s = in + strlen(in) - 1; - - while (s != in && *s != '.') - s--; - - for (s2 = s ; *s2 && *s2 != '/' ; s2--) - ; - - if (s-s2 < 2) - strcpy (out,"?model?"); - else - { - s--; - strncpy (out,s2+1, s-s2); - out[s-s2] = 0; - } -} - - -/* -================== -COM_DefaultExtension - -If path doesn't have a .EXT, append extension -(extension should include the .) -================== -*/ -void COM_DefaultExtension (char *path, char *extension) -{ - char *src; - - src = path + strlen(path) - 1; - - while (*src != '/' && src != path) - { - if (*src == '.') - return; // it has an extension - src--; - } - - strncat (path, extension, MAX_OSPATH); -} - -/* -================== -COM_ForceExtension - -If path doesn't have an extension or has a different extension, -append(!) specified extension -Extension should include the . -================== -*/ -void COM_ForceExtension (char *path, char *extension) -{ - char *src; - - src = path + strlen(path) - strlen(extension); - if (src >= path && !strcmp(src, extension)) - return; - - strncat (path, extension, MAX_OSPATH); -} - -//============================================================================ - -#define MAX_COM_TOKEN 1024 - -char com_token[MAX_COM_TOKEN]; -int com_argc; -char **com_argv; - -/* -============== -COM_Parse - -Parse a token out of a string -============== -*/ -char *COM_Parse (char *data) -{ - unsigned char c; - int len; - - len = 0; - com_token[0] = 0; - - if (!data) - return NULL; - -// skip whitespace -skipwhite: - while ( (c = *data) == ' ' || c == '\t' || c == '\r' || c == '\n') - data++; - - if (c == 0) - return NULL; // end of file; - -// skip // comments - if (c=='/' && data[1] == '/') - { - while (*data && *data != '\n') - data++; - goto skipwhite; - } - - -// handle quoted strings specially - if (c == '\"') - { - data++; - while (1) - { - c = *data++; - if (c=='\"' || !c) - { - com_token[len] = 0; - if (!c) - data--; - return data; - } - com_token[len] = c; - len++; - } - } - -// parse a regular word - do - { - com_token[len] = c; - data++; - len++; - if (len >= MAX_COM_TOKEN-1) - break; - c = *data; - } while (c && c != ' ' && c != '\t' && c != '\n' && c != '\r'); - - com_token[len] = 0; - return data; -} - - -/* -================ -COM_CheckParm - -Returns the position (1 to argc-1) in the program's argument list -where the given parameter appears, or 0 if not present -================ -*/ -int COM_CheckParm (char *parm) -{ - int i; - - for (i=1 ; inext) - { - if (s == com_base_searchpaths) - Con_Printf ("----------\n"); - if (s->pack) - Con_Printf ("%s (%i files)\n", s->pack->filename, s->pack->numfiles); - else - Con_Printf ("%s\n", s->filename); - } -} - -/* -============ -COM_WriteFile - -The filename will be prefixed by the current game directory -============ -*/ -void COM_WriteFile (char *filename, void *data, int len) -{ - FILE *f; - char name[MAX_OSPATH]; - - sprintf (name, "%s/%s", com_gamedir, filename); - - f = fopen (name, "wb"); - if (!f) { - Sys_mkdir(com_gamedir); - f = fopen (name, "wb"); - if (!f) - Sys_Error ("Error opening %s", filename); - } - - Sys_Printf ("COM_WriteFile: %s\n", name); - fwrite (data, 1, len, f); - fclose (f); -} - - -/* -============ -COM_CreatePath - -Only used for CopyFile and download -============ -*/ -void COM_CreatePath (char *path) -{ - char *ofs; - - for (ofs = path+1 ; *ofs ; ofs++) - { - if (*ofs == '/') - { // create the directory - *ofs = 0; - Sys_mkdir (path); - *ofs = '/'; - } - } -} - - -/* -=========== -COM_CopyFile - -Copies a file over from the net to the local cache, creating any directories -needed. This is for the convenience of developers using ISDN from home. -=========== -*/ -void COM_CopyFile (char *netpath, char *cachepath) -{ - FILE *in, *out; - int remaining, count; - char buf[4096]; - - remaining = COM_FileOpenRead (netpath, &in); - COM_CreatePath (cachepath); // create directories up to the cache file - out = fopen(cachepath, "wb"); - if (!out) - Sys_Error ("Error opening %s", cachepath); - - while (remaining) - { - if (remaining < sizeof(buf)) - count = remaining; - else - count = sizeof(buf); - fread (buf, 1, count, in); - fwrite (buf, 1, count, out); - remaining -= count; - } - - fclose (in); - fclose (out); -} - -/* -=========== -COM_FindFile - -Finds the file in the search path. -Sets com_filesize and one of handle or file -=========== -*/ -int file_from_pak; // global indicating file came from pack file ZOID - -int COM_FOpenFile (char *filename, FILE **file) -{ - searchpath_t *search; - char netpath[MAX_OSPATH]; - pack_t *pak; - int i; - int findtime; - - file_from_pak = 0; - -// -// search through the path, one element at a time -// - for (search = com_searchpaths ; search ; search = search->next) - { - // is the element a pak file? - if (search->pack) - { - // look through all the pak file elements - pak = search->pack; - for (i=0 ; inumfiles ; i++) - if (!strcmp (pak->files[i].name, filename)) - { // found it! - if (developer.value) - Sys_Printf ("PackFile: %s : %s\n", pak->filename, filename); - // open a new file on the pakfile - *file = fopen (pak->filename, "rb"); - if (!*file) - Sys_Error ("Couldn't reopen %s", pak->filename); - fseek (*file, pak->files[i].filepos, SEEK_SET); - com_filesize = pak->files[i].filelen; - file_from_pak = 1; - return com_filesize; - } - } - else - { - sprintf (netpath, "%s/%s",search->filename, filename); - - findtime = Sys_FileTime (netpath); - if (findtime == -1) - continue; - - if (developer.value) - Sys_Printf ("FindFile: %s\n",netpath); - - *file = fopen (netpath, "rb"); - return COM_filelength (*file); - } - - } - - Sys_Printf ("FindFile: can't find %s\n", filename); - - *file = NULL; - com_filesize = -1; - return -1; -} - -/* -============ -COM_LoadFile - -Filename are relative to the quake directory. -Always appends a 0 byte to the loaded data. -============ -*/ -cache_user_t *loadcache; -byte *loadbuf; -int loadsize; -byte *COM_LoadFile (char *path, int usehunk) -{ - FILE *h; - byte *buf; - char base[32]; - int len; - - buf = NULL; // quiet compiler warning - -// look for it in the filesystem or pack files - len = com_filesize = COM_FOpenFile (path, &h); - if (!h) - return NULL; - -// extract the filename base name for hunk tag - COM_FileBase (path, base); - - if (usehunk == 1) - buf = Hunk_AllocName (len+1, base); - else if (usehunk == 2) - buf = Hunk_TempAlloc (len+1); - else if (usehunk == 0) - buf = Z_Malloc (len+1); - else if (usehunk == 3) - buf = Cache_Alloc (loadcache, len+1, base); - else if (usehunk == 4) - { - if (len+1 > loadsize) - buf = Hunk_TempAlloc (len+1); - else - buf = loadbuf; - } - else - Sys_Error ("COM_LoadFile: bad usehunk"); - - if (!buf) - Sys_Error ("COM_LoadFile: not enough space for %s", path); - - ((byte *)buf)[len] = 0; -#ifndef SERVERONLY - Draw_BeginDisc (); -#endif - fread (buf, 1, len, h); - fclose (h); -#ifndef SERVERONLY - Draw_EndDisc (); -#endif - - return buf; -} - -byte *COM_LoadHunkFile (char *path) -{ - return COM_LoadFile (path, 1); -} - -byte *COM_LoadTempFile (char *path) -{ - return COM_LoadFile (path, 2); -} - -void COM_LoadCacheFile (char *path, struct cache_user_s *cu) -{ - loadcache = cu; - COM_LoadFile (path, 3); -} - -// uses temp hunk if larger than bufsize -byte *COM_LoadStackFile (char *path, void *buffer, int bufsize) -{ - byte *buf; - - loadbuf = (byte *)buffer; - loadsize = bufsize; - buf = COM_LoadFile (path, 4); - - return buf; -} - -/* -================= -COM_LoadPackFile - -Takes an explicit (not game tree related) path to a pak file. - -Loads the header and directory, adding the files at the beginning -of the list so they override previous pack files. -================= -*/ -pack_t *COM_LoadPackFile (char *packfile) -{ - dpackheader_t header; - int i; - packfile_t *newfiles; - int numpackfiles; - pack_t *pack; - FILE *packhandle; - dpackfile_t info[MAX_FILES_IN_PACK]; - - if (COM_FileOpenRead (packfile, &packhandle) == -1) - return NULL; - - fread (&header, 1, sizeof(header), packhandle); - if (header.id[0] != 'P' || header.id[1] != 'A' - || header.id[2] != 'C' || header.id[3] != 'K') - Sys_Error ("%s is not a packfile", packfile); - header.dirofs = LittleLong (header.dirofs); - header.dirlen = LittleLong (header.dirlen); - - numpackfiles = header.dirlen / sizeof(dpackfile_t); - - if (numpackfiles > MAX_FILES_IN_PACK) - Sys_Error ("%s has %i files", packfile, numpackfiles); - - newfiles = Z_Malloc (numpackfiles * sizeof(packfile_t)); - - fseek (packhandle, header.dirofs, SEEK_SET); - fread (&info, 1, header.dirlen, packhandle); - -// parse the directory - for (i=0 ; ifilename, packfile); - pack->handle = packhandle; - pack->numfiles = numpackfiles; - pack->files = newfiles; - - Con_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles); - return pack; -} - - -/* -================ -COM_AddGameDirectory - -Sets com_gamedir, adds the directory to the head of the path, -then loads and adds pak1.pak pak2.pak ... -================ -*/ -void COM_AddGameDirectory (char *dir) -{ - int i; - searchpath_t *search; - pack_t *pak; - char pakfile[MAX_OSPATH]; - char *p; - - if ((p = strrchr(dir, '/')) != NULL) - strcpy(gamedirfile, ++p); - else - strcpy(gamedirfile, p); - strcpy (com_gamedir, dir); - -// -// add the directory to the search path -// - search = Hunk_Alloc (sizeof(searchpath_t)); - strcpy (search->filename, dir); - search->next = com_searchpaths; - com_searchpaths = search; - -// -// add any pak files in the format pak0.pak pak1.pak, ... -// - for (i=0 ; ; i++) - { - sprintf (pakfile, "%s/pak%i.pak", dir, i); - pak = COM_LoadPackFile (pakfile); - if (!pak) - break; - search = Hunk_Alloc (sizeof(searchpath_t)); - search->pack = pak; - search->next = com_searchpaths; - com_searchpaths = search; - } - -} - -/* -================ -COM_Gamedir - -Sets the gamedir and path to a different directory. -================ -*/ -void COM_Gamedir (char *dir) -{ - searchpath_t *search, *next; - int i; - pack_t *pak; - char pakfile[MAX_OSPATH]; - - if (strstr(dir, "..") || strstr(dir, "/") - || strstr(dir, "\\") || strstr(dir, ":") ) - { - Con_Printf ("Gamedir should be a single filename, not a path\n"); - return; - } - - if (!strcmp(gamedirfile, dir)) - return; // still the same - strcpy (gamedirfile, dir); - - // - // free up any current game dir info - // - while (com_searchpaths != com_base_searchpaths) - { - if (com_searchpaths->pack) - { - fclose (com_searchpaths->pack->handle); - Z_Free (com_searchpaths->pack->files); - Z_Free (com_searchpaths->pack); - } - next = com_searchpaths->next; - Z_Free (com_searchpaths); - com_searchpaths = next; - } - - // - // flush all data, so it will be forced to reload - // - Cache_Flush (); - - if (!strcmp(dir,"id1") || !strcmp(dir, "qw")) - return; - - sprintf (com_gamedir, "%s/%s", com_basedir, dir); - - // - // add the directory to the search path - // - search = Z_Malloc (sizeof(searchpath_t)); - strcpy (search->filename, com_gamedir); - search->next = com_searchpaths; - com_searchpaths = search; - - // - // add any pak files in the format pak0.pak pak1.pak, ... - // - for (i=0 ; ; i++) - { - sprintf (pakfile, "%s/pak%i.pak", com_gamedir, i); - pak = COM_LoadPackFile (pakfile); - if (!pak) - break; - search = Z_Malloc (sizeof(searchpath_t)); - search->pack = pak; - search->next = com_searchpaths; - com_searchpaths = search; - } -} - -/* -================ -COM_InitFilesystem -================ -*/ -void COM_InitFilesystem (void) -{ - int i; - -// -// -basedir -// Overrides the system supplied base directory (under id1) -// - i = COM_CheckParm ("-basedir"); - if (i && i < com_argc-1) - Q_strncpyz (com_basedir, com_argv[i+1], sizeof(com_basedir)); - else - Q_strncpyz (com_basedir, host_parms.basedir, sizeof(com_basedir)); - - i = strlen(com_basedir)-1; - if ((i >= 0) && (com_basedir[i]=='/' || com_basedir[i]=='\\')) - com_basedir[i] = '\0'; - -// -// start up with id1 by default -// - COM_AddGameDirectory (va("%s/id1", com_basedir) ); - COM_AddGameDirectory (va("%s/qw", com_basedir) ); - - // any set gamedirs will be freed up to here - com_base_searchpaths = com_searchpaths; -} - - - -/* -===================================================================== - - INFO STRINGS - -===================================================================== -*/ - -/* -=============== -Info_ValueForKey - -Searches the string for the given -key and returns the associated value, or an empty string. -=============== -*/ -char *Info_ValueForKey (char *s, char *key) -{ - char pkey[512]; - static char value[4][512]; // use two buffers so compares - // work without stomping on each other - static int valueindex; - char *o; - - valueindex = (valueindex + 1) % 4; - if (*s == '\\') - s++; - while (1) - { - o = pkey; - while (*s != '\\') - { - if (!*s) - return ""; - *o++ = *s++; - } - *o = 0; - s++; - - o = value[valueindex]; - - while (*s != '\\' && *s) - { - if (!*s) - return ""; - *o++ = *s++; - } - *o = 0; - - if (!strcmp (key, pkey) ) - return value[valueindex]; - - if (!*s) - return ""; - s++; - } -} - -void Info_RemoveKey (char *s, char *key) -{ - char *start; - char pkey[512]; - char value[512]; - char *o; - - if (strstr (key, "\\")) - { - Con_Printf ("Can't use a key with a \\\n"); - return; - } - - while (1) - { - start = s; - if (*s == '\\') - s++; - o = pkey; - while (*s != '\\') - { - if (!*s) - return; - *o++ = *s++; - } - *o = 0; - s++; - - o = value; - while (*s != '\\' && *s) - { - if (!*s) - return; - *o++ = *s++; - } - *o = 0; - - if (!strcmp (key, pkey) ) - { - strcpy (start, s); // remove this part - return; - } - - if (!*s) - return; - } - -} - -void Info_RemovePrefixedKeys (char *start, char prefix) -{ - char *s; - char pkey[512]; - char value[512]; - char *o; - - s = start; - - while (1) - { - if (*s == '\\') - s++; - o = pkey; - while (*s != '\\') - { - if (!*s) - return; - *o++ = *s++; - } - *o = 0; - s++; - - o = value; - while (*s != '\\' && *s) - { - if (!*s) - return; - *o++ = *s++; - } - *o = 0; - - if (pkey[0] == prefix) - { - Info_RemoveKey (start, pkey); - s = start; - } - - if (!*s) - return; - } - -} - - -void Info_SetValueForStarKey (char *s, char *key, char *value, int maxsize) -{ - char new[1024], *v; - int c; -#ifdef SERVERONLY - extern cvar_t sv_highchars; -#endif - - if (strstr (key, "\\") || strstr (value, "\\") ) - { - Con_Printf ("Can't use keys or values with a \\\n"); - return; - } - - if (strstr (key, "\"") || strstr (value, "\"") ) - { - Con_Printf ("Can't use keys or values with a \"\n"); - return; - } - - if (strlen(key) > 63 || strlen(value) > 63) - { - Con_Printf ("Keys and values must be < 64 characters.\n"); - return; - } - - // this next line is kinda trippy - if (*(v = Info_ValueForKey(s, key))) { - // key exists, make sure we have enough room for new value, if we don't, - // don't change it! - if (strlen(value) - strlen(v) + strlen(s) > maxsize) { - Con_Printf ("Info string length exceeded\n"); - return; - } - } - Info_RemoveKey (s, key); - if (!value || !strlen(value)) - return; - - sprintf (new, "\\%s\\%s", key, value); - - if ((int)(strlen(new) + strlen(s)) > maxsize) - { - Con_Printf ("Info string length exceeded\n"); - return; - } - - // only copy ascii values - s += strlen(s); - v = new; - while (*v) - { - c = (unsigned char)*v++; -#ifndef SERVERONLY - // client only allows highbits on name - if (stricmp(key, "name") != 0) { - c &= 127; - if (c < 32 || c > 127) - continue; - // auto lowercase team - if (stricmp(key, "team") == 0) - c = tolower(c); - } -#else - if (!sv_highchars.value) { - c &= 127; - if (c < 32 || c > 127) - continue; - } -#endif -// c &= 127; // strip high bits - if (c > 13) // && c < 127) - *s++ = c; - } - *s = 0; -} - -void Info_SetValueForKey (char *s, char *key, char *value, int maxsize) -{ - if (key[0] == '*') - { - Con_Printf ("Can't set * keys\n"); - return; - } - - Info_SetValueForStarKey (s, key, value, maxsize); -} - -void Info_Print (char *s) -{ - char key[512]; - char value[512]; - char *o; - int l; - - if (*s == '\\') - s++; - while (*s) - { - o = key; - while (*s && *s != '\\') - *o++ = *s++; - - l = o - key; - if (l < 20) - { - memset (o, ' ', 20-l); - key[20] = 0; - } - else - *o = 0; - Con_Printf ("%s", key); - - if (!*s) - { - Con_Printf ("MISSING VALUE\n"); - return; - } - - o = value; - s++; - while (*s && *s != '\\') - *o++ = *s++; - *o = 0; - - if (*s) - s++; - Con_Printf ("%s\n", value); - } -} - -static byte chktbl[1024 + 4] = { -0x78,0xd2,0x94,0xe3,0x41,0xec,0xd6,0xd5,0xcb,0xfc,0xdb,0x8a,0x4b,0xcc,0x85,0x01, -0x23,0xd2,0xe5,0xf2,0x29,0xa7,0x45,0x94,0x4a,0x62,0xe3,0xa5,0x6f,0x3f,0xe1,0x7a, -0x64,0xed,0x5c,0x99,0x29,0x87,0xa8,0x78,0x59,0x0d,0xaa,0x0f,0x25,0x0a,0x5c,0x58, -0xfb,0x00,0xa7,0xa8,0x8a,0x1d,0x86,0x80,0xc5,0x1f,0xd2,0x28,0x69,0x71,0x58,0xc3, -0x51,0x90,0xe1,0xf8,0x6a,0xf3,0x8f,0xb0,0x68,0xdf,0x95,0x40,0x5c,0xe4,0x24,0x6b, -0x29,0x19,0x71,0x3f,0x42,0x63,0x6c,0x48,0xe7,0xad,0xa8,0x4b,0x91,0x8f,0x42,0x36, -0x34,0xe7,0x32,0x55,0x59,0x2d,0x36,0x38,0x38,0x59,0x9b,0x08,0x16,0x4d,0x8d,0xf8, -0x0a,0xa4,0x52,0x01,0xbb,0x52,0xa9,0xfd,0x40,0x18,0x97,0x37,0xff,0xc9,0x82,0x27, -0xb2,0x64,0x60,0xce,0x00,0xd9,0x04,0xf0,0x9e,0x99,0xbd,0xce,0x8f,0x90,0x4a,0xdd, -0xe1,0xec,0x19,0x14,0xb1,0xfb,0xca,0x1e,0x98,0x0f,0xd4,0xcb,0x80,0xd6,0x05,0x63, -0xfd,0xa0,0x74,0xa6,0x86,0xf6,0x19,0x98,0x76,0x27,0x68,0xf7,0xe9,0x09,0x9a,0xf2, -0x2e,0x42,0xe1,0xbe,0x64,0x48,0x2a,0x74,0x30,0xbb,0x07,0xcc,0x1f,0xd4,0x91,0x9d, -0xac,0x55,0x53,0x25,0xb9,0x64,0xf7,0x58,0x4c,0x34,0x16,0xbc,0xf6,0x12,0x2b,0x65, -0x68,0x25,0x2e,0x29,0x1f,0xbb,0xb9,0xee,0x6d,0x0c,0x8e,0xbb,0xd2,0x5f,0x1d,0x8f, -0xc1,0x39,0xf9,0x8d,0xc0,0x39,0x75,0xcf,0x25,0x17,0xbe,0x96,0xaf,0x98,0x9f,0x5f, -0x65,0x15,0xc4,0x62,0xf8,0x55,0xfc,0xab,0x54,0xcf,0xdc,0x14,0x06,0xc8,0xfc,0x42, -0xd3,0xf0,0xad,0x10,0x08,0xcd,0xd4,0x11,0xbb,0xca,0x67,0xc6,0x48,0x5f,0x9d,0x59, -0xe3,0xe8,0x53,0x67,0x27,0x2d,0x34,0x9e,0x9e,0x24,0x29,0xdb,0x69,0x99,0x86,0xf9, -0x20,0xb5,0xbb,0x5b,0xb0,0xf9,0xc3,0x67,0xad,0x1c,0x9c,0xf7,0xcc,0xef,0xce,0x69, -0xe0,0x26,0x8f,0x79,0xbd,0xca,0x10,0x17,0xda,0xa9,0x88,0x57,0x9b,0x15,0x24,0xba, -0x84,0xd0,0xeb,0x4d,0x14,0xf5,0xfc,0xe6,0x51,0x6c,0x6f,0x64,0x6b,0x73,0xec,0x85, -0xf1,0x6f,0xe1,0x67,0x25,0x10,0x77,0x32,0x9e,0x85,0x6e,0x69,0xb1,0x83,0x00,0xe4, -0x13,0xa4,0x45,0x34,0x3b,0x40,0xff,0x41,0x82,0x89,0x79,0x57,0xfd,0xd2,0x8e,0xe8, -0xfc,0x1d,0x19,0x21,0x12,0x00,0xd7,0x66,0xe5,0xc7,0x10,0x1d,0xcb,0x75,0xe8,0xfa, -0xb6,0xee,0x7b,0x2f,0x1a,0x25,0x24,0xb9,0x9f,0x1d,0x78,0xfb,0x84,0xd0,0x17,0x05, -0x71,0xb3,0xc8,0x18,0xff,0x62,0xee,0xed,0x53,0xab,0x78,0xd3,0x65,0x2d,0xbb,0xc7, -0xc1,0xe7,0x70,0xa2,0x43,0x2c,0x7c,0xc7,0x16,0x04,0xd2,0x45,0xd5,0x6b,0x6c,0x7a, -0x5e,0xa1,0x50,0x2e,0x31,0x5b,0xcc,0xe8,0x65,0x8b,0x16,0x85,0xbf,0x82,0x83,0xfb, -0xde,0x9f,0x36,0x48,0x32,0x79,0xd6,0x9b,0xfb,0x52,0x45,0xbf,0x43,0xf7,0x0b,0x0b, -0x19,0x19,0x31,0xc3,0x85,0xec,0x1d,0x8c,0x20,0xf0,0x3a,0xfa,0x80,0x4d,0x2c,0x7d, -0xac,0x60,0x09,0xc0,0x40,0xee,0xb9,0xeb,0x13,0x5b,0xe8,0x2b,0xb1,0x20,0xf0,0xce, -0x4c,0xbd,0xc6,0x04,0x86,0x70,0xc6,0x33,0xc3,0x15,0x0f,0x65,0x19,0xfd,0xc2,0xd3, - -// map checksum goes here -0x00,0x00,0x00,0x00 -}; - -static byte chkbuf[16 + 60 + 4]; - -static unsigned last_mapchecksum = 0; - -#if 0 -/* -==================== -COM_BlockSequenceCheckByte - -For proxy protecting -==================== -*/ -byte COM_BlockSequenceCheckByte (byte *base, int length, int sequence, unsigned mapchecksum) -{ - int checksum; - byte *p; - - if (last_mapchecksum != mapchecksum) { - last_mapchecksum = mapchecksum; - chktbl[1024] = (mapchecksum & 0xff000000) >> 24; - chktbl[1025] = (mapchecksum & 0x00ff0000) >> 16; - chktbl[1026] = (mapchecksum & 0x0000ff00) >> 8; - chktbl[1027] = (mapchecksum & 0x000000ff); - - Com_BlockFullChecksum (chktbl, sizeof(chktbl), chkbuf); - } - - p = chktbl + (sequence % (sizeof(chktbl) - 8)); - - if (length > 60) - length = 60; - memcpy (chkbuf + 16, base, length); - - length += 16; - - chkbuf[length] = (sequence & 0xff) ^ p[0]; - chkbuf[length+1] = p[1]; - chkbuf[length+2] = ((sequence>>8) & 0xff) ^ p[2]; - chkbuf[length+3] = p[3]; - - length += 4; - - checksum = LittleLong(Com_BlockChecksum (chkbuf, length)); - - checksum &= 0xff; - - return checksum; -} -#endif - -/* -==================== -COM_BlockSequenceCRCByte - -For proxy protecting -==================== -*/ -byte COM_BlockSequenceCRCByte (byte *base, int length, int sequence) -{ - unsigned short crc; - byte *p; - byte chkb[60 + 4]; - - p = chktbl + (sequence % (sizeof(chktbl) - 8)); - - if (length > 60) - length = 60; - memcpy (chkb, base, length); - - chkb[length] = (sequence & 0xff) ^ p[0]; - chkb[length+1] = p[1]; - chkb[length+2] = ((sequence>>8) & 0xff) ^ p[2]; - chkb[length+3] = p[3]; - - length += 4; - - crc = CRC_Block(chkb, length); - - crc &= 0xff; - - return crc; -} - +/* +Copyright (C) 1996-1997 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. + +*/ +// common.c -- misc functions used in client and server + +#include + +#ifdef SERVERONLY +#include "qwsvdef.h" +#else +#include "quakedef.h" +#endif + +#define MAX_NUM_ARGVS 50 +#define NUM_SAFE_ARGVS 6 + +usercmd_t nullcmd; // guarenteed to be zero + +static char *largv[MAX_NUM_ARGVS + NUM_SAFE_ARGVS + 1]; +static char *argvdummy = " "; + +static char *safeargvs[NUM_SAFE_ARGVS] = + {"-stdvid", "-nolan", "-nosound", "-nocdaudio", "-nojoy", "-nomouse"}; + +cvar_t registered = {"registered","0"}; + +qboolean msg_suppress_1 = 0; + +void COM_InitFilesystem (void); +void COM_Path_f (void); + +char gamedirfile[MAX_OSPATH]; + +/* + + +All of Quake's data access is through a hierchal file system, but the contents of the file system can be transparently merged from several sources. + +The "base directory" is the path to the directory holding the quake.exe and all game directories. The sys_* files pass this to host_init in quakeparms_t->basedir. This can be overridden with the "-basedir" command line parm to allow code debugging in a different directory. The base directory is +only used during filesystem initialization. + +The "game directory" is the first tree on the search path and directory that all generated files (savegames, screenshots, demos, config files) will be saved to. This can be overridden with the "-game" command line parameter. The game directory can never be changed while quake is executing. This is a precacution against having a malicious server instruct clients to write files over areas they shouldn't. + +*/ + +//============================================================================ + + +// ClearLink is used for new headnodes +void ClearLink (link_t *l) +{ + l->prev = l->next = l; +} + +void RemoveLink (link_t *l) +{ + l->next->prev = l->prev; + l->prev->next = l->next; +} + +void InsertLinkBefore (link_t *l, link_t *before) +{ + l->next = before; + l->prev = before->prev; + l->prev->next = l; + l->next->prev = l; +} +void InsertLinkAfter (link_t *l, link_t *after) +{ + l->next = after->next; + l->prev = after; + l->prev->next = l; + l->next->prev = l; +} + +/* +============================================================================ + + LIBRARY REPLACEMENT FUNCTIONS + +============================================================================ +*/ + +int Q_atoi (char *str) +{ + int val; + int sign; + int c; + + if (*str == '-') + { + sign = -1; + str++; + } + else + sign = 1; + + val = 0; + +// +// check for hex +// + if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X') ) + { + str += 2; + while (1) + { + c = *str++; + if (c >= '0' && c <= '9') + val = (val<<4) + c - '0'; + else if (c >= 'a' && c <= 'f') + val = (val<<4) + c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + val = (val<<4) + c - 'A' + 10; + else + return val*sign; + } + } + +// +// check for character +// + if (str[0] == '\'') + { + return sign * str[1]; + } + +// +// assume decimal +// + while (1) + { + c = *str++; + if (c <'0' || c > '9') + return val*sign; + val = val*10 + c - '0'; + } + + return 0; +} + + +float Q_atof (char *str) +{ + double val; + int sign; + int c; + int decimal, total; + + if (*str == '-') + { + sign = -1; + str++; + } + else + sign = 1; + + val = 0; + +// +// check for hex +// + if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X') ) + { + str += 2; + while (1) + { + c = *str++; + if (c >= '0' && c <= '9') + val = (val*16) + c - '0'; + else if (c >= 'a' && c <= 'f') + val = (val*16) + c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + val = (val*16) + c - 'A' + 10; + else + return val*sign; + } + } + +// +// check for character +// + if (str[0] == '\'') + { + return sign * str[1]; + } + +// +// assume decimal +// + decimal = -1; + total = 0; + while (1) + { + c = *str++; + if (c == '.') + { + decimal = total; + continue; + } + if (c <'0' || c > '9') + break; + val = val*10 + c - '0'; + total++; + } + + if (decimal == -1) + return val*sign; + while (total > decimal) + { + val /= 10; + total--; + } + + return val*sign; +} + + +void Q_strncpyz (char *dest, char *src, size_t size) +{ + strncpy (dest, src, size-1); + dest[size-1] = 0; +} + + +/* +============================================================================ + + BYTE ORDER FUNCTIONS + +============================================================================ +*/ + +qboolean bigendien; + +short (*BigShort) (short l); +short (*LittleShort) (short l); +int (*BigLong) (int l); +int (*LittleLong) (int l); +float (*BigFloat) (float l); +float (*LittleFloat) (float l); + +short ShortSwap (short l) +{ + byte b1,b2; + + b1 = l&255; + b2 = (l>>8)&255; + + return (b1<<8) + b2; +} + +short ShortNoSwap (short l) +{ + return l; +} + +int LongSwap (int l) +{ + byte b1,b2,b3,b4; + + b1 = l&255; + b2 = (l>>8)&255; + b3 = (l>>16)&255; + b4 = (l>>24)&255; + + return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4; +} + +int LongNoSwap (int l) +{ + return l; +} + +float FloatSwap (float f) +{ + union + { + float f; + byte b[4]; + } dat1, dat2; + + + dat1.f = f; + dat2.b[0] = dat1.b[3]; + dat2.b[1] = dat1.b[2]; + dat2.b[2] = dat1.b[1]; + dat2.b[3] = dat1.b[0]; + return dat2.f; +} + +float FloatNoSwap (float f) +{ + return f; +} + +/* +============================================================================== + + MESSAGE IO FUNCTIONS + +Handles byte ordering and avoids alignment errors +============================================================================== +*/ + +// +// writing functions +// + +void MSG_WriteChar (sizebuf_t *sb, int c) +{ + byte *buf; + +#ifdef PARANOID + if (c < -128 || c > 127) + Sys_Error ("MSG_WriteChar: range error"); +#endif + + buf = SZ_GetSpace (sb, 1); + buf[0] = c; +} + +void MSG_WriteByte (sizebuf_t *sb, int c) +{ + byte *buf; + +#ifdef PARANOID + if (c < 0 || c > 255) + Sys_Error ("MSG_WriteByte: range error"); +#endif + + buf = SZ_GetSpace (sb, 1); + buf[0] = c; +} + +void MSG_WriteShort (sizebuf_t *sb, int c) +{ + byte *buf; + +#ifdef PARANOID + if (c < ((short)0x8000) || c > (short)0x7fff) + Sys_Error ("MSG_WriteShort: range error"); +#endif + + buf = SZ_GetSpace (sb, 2); + buf[0] = c&0xff; + buf[1] = c>>8; +} + +void MSG_WriteLong (sizebuf_t *sb, int c) +{ + byte *buf; + + buf = SZ_GetSpace (sb, 4); + buf[0] = c&0xff; + buf[1] = (c>>8)&0xff; + buf[2] = (c>>16)&0xff; + buf[3] = c>>24; +} + +void MSG_WriteFloat (sizebuf_t *sb, float f) +{ + union + { + float f; + int l; + } dat; + + + dat.f = f; + dat.l = LittleLong (dat.l); + + SZ_Write (sb, &dat.l, 4); +} + +void MSG_WriteString (sizebuf_t *sb, char *s) +{ + if (!s) + SZ_Write (sb, "", 1); + else + SZ_Write (sb, s, strlen(s)+1); +} + +void MSG_WriteCoord (sizebuf_t *sb, float f) +{ + MSG_WriteShort (sb, (int)(f*8)); +} + +void MSG_WriteAngle (sizebuf_t *sb, float f) +{ + MSG_WriteByte (sb, (int)(f*256/360) & 255); +} + +void MSG_WriteAngle16 (sizebuf_t *sb, float f) +{ + MSG_WriteShort (sb, (int)(f*65536/360) & 65535); +} + +void MSG_WriteDeltaUsercmd (sizebuf_t *buf, usercmd_t *from, usercmd_t *cmd) +{ + int bits; + +// +// send the movement message +// + bits = 0; + if (cmd->angles[0] != from->angles[0]) + bits |= CM_ANGLE1; + if (cmd->angles[1] != from->angles[1]) + bits |= CM_ANGLE2; + if (cmd->angles[2] != from->angles[2]) + bits |= CM_ANGLE3; + if (cmd->forwardmove != from->forwardmove) + bits |= CM_FORWARD; + if (cmd->sidemove != from->sidemove) + bits |= CM_SIDE; + if (cmd->upmove != from->upmove) + bits |= CM_UP; + if (cmd->buttons != from->buttons) + bits |= CM_BUTTONS; + if (cmd->impulse != from->impulse) + bits |= CM_IMPULSE; + + MSG_WriteByte (buf, bits); + + if (bits & CM_ANGLE1) + MSG_WriteAngle16 (buf, cmd->angles[0]); + if (bits & CM_ANGLE2) + MSG_WriteAngle16 (buf, cmd->angles[1]); + if (bits & CM_ANGLE3) + MSG_WriteAngle16 (buf, cmd->angles[2]); + + if (bits & CM_FORWARD) + MSG_WriteShort (buf, cmd->forwardmove); + if (bits & CM_SIDE) + MSG_WriteShort (buf, cmd->sidemove); + if (bits & CM_UP) + MSG_WriteShort (buf, cmd->upmove); + + if (bits & CM_BUTTONS) + MSG_WriteByte (buf, cmd->buttons); + if (bits & CM_IMPULSE) + MSG_WriteByte (buf, cmd->impulse); + MSG_WriteByte (buf, cmd->msec); +} + + +// +// reading functions +// +int msg_readcount; +qboolean msg_badread; + +void MSG_BeginReading (void) +{ + msg_readcount = 0; + msg_badread = false; +} + +int MSG_GetReadCount(void) +{ + return msg_readcount; +} + +// returns -1 and sets msg_badread if no more characters are available +int MSG_ReadChar (void) +{ + int c; + + if (msg_readcount+1 > net_message.cursize) + { + msg_badread = true; + return -1; + } + + c = (signed char)net_message.data[msg_readcount]; + msg_readcount++; + + return c; +} + +int MSG_ReadByte (void) +{ + int c; + + if (msg_readcount+1 > net_message.cursize) + { + msg_badread = true; + return -1; + } + + c = (unsigned char)net_message.data[msg_readcount]; + msg_readcount++; + + return c; +} + +int MSG_ReadShort (void) +{ + int c; + + if (msg_readcount+2 > net_message.cursize) + { + msg_badread = true; + return -1; + } + + c = (short)(net_message.data[msg_readcount] + + (net_message.data[msg_readcount+1]<<8)); + + msg_readcount += 2; + + return c; +} + +int MSG_ReadLong (void) +{ + int c; + + if (msg_readcount+4 > net_message.cursize) + { + msg_badread = true; + return -1; + } + + c = net_message.data[msg_readcount] + + (net_message.data[msg_readcount+1]<<8) + + (net_message.data[msg_readcount+2]<<16) + + (net_message.data[msg_readcount+3]<<24); + + msg_readcount += 4; + + return c; +} + +float MSG_ReadFloat (void) +{ + union + { + byte b[4]; + float f; + int l; + } dat; + + dat.b[0] = net_message.data[msg_readcount]; + dat.b[1] = net_message.data[msg_readcount+1]; + dat.b[2] = net_message.data[msg_readcount+2]; + dat.b[3] = net_message.data[msg_readcount+3]; + msg_readcount += 4; + + dat.l = LittleLong (dat.l); + + return dat.f; +} + +char *MSG_ReadString (void) +{ + static char string[2048]; + int l,c; + + l = 0; + do + { + c = MSG_ReadChar (); + if (c == -1 || c == 0) + break; + string[l] = c; + l++; + } while (l < sizeof(string)-1); + + string[l] = 0; + + return string; +} + +char *MSG_ReadStringLine (void) +{ + static char string[2048]; + int l,c; + + l = 0; + do + { + c = MSG_ReadChar (); + if (c == -1 || c == 0 || c == '\n') + break; + string[l] = c; + l++; + } while (l < sizeof(string)-1); + + string[l] = 0; + + return string; +} + +float MSG_ReadCoord (void) +{ + return MSG_ReadShort() * (1.0/8); +} + +float MSG_ReadAngle (void) +{ + return MSG_ReadChar() * (360.0/256); +} + +float MSG_ReadAngle16 (void) +{ + return MSG_ReadShort() * (360.0/65536); +} + +void MSG_ReadDeltaUsercmd (usercmd_t *from, usercmd_t *move) +{ + int bits; + extern int stat_size; + + memcpy (move, from, sizeof(*move)); + + bits = MSG_ReadByte (); + //stat_size +=1; + +// read current angles + if (bits & CM_ANGLE1) { + move->angles[0] = MSG_ReadAngle16 (); + //stat_size += 2; + } + if (bits & CM_ANGLE2) { + move->angles[1] = MSG_ReadAngle16 (); + //stat_size += 2; + } + if (bits & CM_ANGLE3) + { + move->angles[2] = MSG_ReadAngle16 (); + //stat_size += 2; + } + +// read movement + if (bits & CM_FORWARD) + { + move->forwardmove = MSG_ReadShort (); + //stat_size += 2; + } + if (bits & CM_SIDE) + { + move->sidemove = MSG_ReadShort (); + //stat_size += 2; + } + if (bits & CM_UP) + { + move->upmove = MSG_ReadShort (); + //stat_size += 2; + } + +// read buttons + if (bits & CM_BUTTONS) + move->buttons = MSG_ReadByte (); + + if (bits & CM_IMPULSE) + move->impulse = MSG_ReadByte (); + +// read time to run command + move->msec = MSG_ReadByte (); + //stat_size += 1; +} + + +//=========================================================================== + +void SZ_Clear (sizebuf_t *buf) +{ + buf->cursize = 0; + buf->overflowed = false; +} + +void *SZ_GetSpace (sizebuf_t *buf, int length) +{ + void *data; + + if (buf->cursize + length > buf->maxsize) + { + if (!buf->allowoverflow) + Sys_Error ("SZ_GetSpace: overflow without allowoverflow set (%d)", buf->maxsize); + + if (length > buf->maxsize) + Sys_Error ("SZ_GetSpace: %i is > full buffer size", length); + + Sys_Printf ("SZ_GetSpace: overflow\n"); // because Con_Printf may be redirected + SZ_Clear (buf); + buf->overflowed = true; + } + + data = buf->data + buf->cursize; + buf->cursize += length; + + return data; +} + +void SZ_Write (sizebuf_t *buf, void *data, int length) +{ + memcpy (SZ_GetSpace(buf,length),data,length); +} + +void SZ_Print (sizebuf_t *buf, char *data) +{ + int len; + + len = strlen(data)+1; + + if (!buf->cursize || buf->data[buf->cursize-1]) + memcpy ((byte *)SZ_GetSpace(buf, len),data,len); // no trailing 0 + else + memcpy ((byte *)SZ_GetSpace(buf, len-1)-1,data,len); // write over trailing 0 +} + + +//============================================================================ + + +/* +============ +COM_SkipPath +============ +*/ +char *COM_SkipPath (char *pathname) +{ + char *last; + + last = pathname; + while (*pathname) + { + if (*pathname=='/') + last = pathname+1; + pathname++; + } + return last; +} + +/* +============ +COM_StripExtension +============ +*/ +void COM_StripExtension (char *in, char *out) +{ + while (*in && *in != '.') + *out++ = *in++; + *out = 0; +} + +/* +============ +COM_FileExtension +============ +*/ +char *COM_FileExtension (char *in) +{ + static char exten[8]; + int i; + + while (*in && *in != '.') + in++; + if (!*in) + return ""; + in++; + for (i=0 ; i<7 && *in ; i++,in++) + exten[i] = *in; + exten[i] = 0; + return exten; +} + +/* +============ +COM_FileBase +============ +*/ +void COM_FileBase (char *in, char *out) +{ + char *s, *s2; + + s = in + strlen(in) - 1; + + while (s != in && *s != '.') + s--; + + for (s2 = s ; *s2 && *s2 != '/' ; s2--) + ; + + if (s-s2 < 2) + strcpy (out,"?model?"); + else + { + s--; + strncpy (out,s2+1, s-s2); + out[s-s2] = 0; + } +} + + +/* +================== +COM_DefaultExtension + +If path doesn't have a .EXT, append extension +(extension should include the .) +================== +*/ +void COM_DefaultExtension (char *path, char *extension) +{ + char *src; + + src = path + strlen(path) - 1; + + while (*src != '/' && src != path) + { + if (*src == '.') + return; // it has an extension + src--; + } + + strncat (path, extension, MAX_OSPATH); +} + +/* +================== +COM_ForceExtension + +If path doesn't have an extension or has a different extension, +append(!) specified extension +Extension should include the . +================== +*/ +void COM_ForceExtension (char *path, char *extension) +{ + char *src; + + src = path + strlen(path) - strlen(extension); + if (src >= path && !strcmp(src, extension)) + return; + + strncat (path, extension, MAX_OSPATH); +} + +//============================================================================ + +#define MAX_COM_TOKEN 1024 + +char com_token[MAX_COM_TOKEN]; +int com_argc; +char **com_argv; + +/* +============== +COM_Parse + +Parse a token out of a string +============== +*/ +char *COM_Parse (char *data) +{ + unsigned char c; + int len; + + len = 0; + com_token[0] = 0; + + if (!data) + return NULL; + +// skip whitespace +skipwhite: + while ( (c = *data) == ' ' || c == '\t' || c == '\r' || c == '\n') + data++; + + if (c == 0) + return NULL; // end of file; + +// skip // comments + if (c=='/' && data[1] == '/') + { + while (*data && *data != '\n') + data++; + goto skipwhite; + } + + +// handle quoted strings specially + if (c == '\"') + { + data++; + while (1) + { + c = *data++; + if (c=='\"' || !c) + { + com_token[len] = 0; + if (!c) + data--; + return data; + } + com_token[len] = c; + len++; + } + } + +// parse a regular word + do + { + com_token[len] = c; + data++; + len++; + if (len >= MAX_COM_TOKEN-1) + break; + c = *data; + } while (c && c != ' ' && c != '\t' && c != '\n' && c != '\r'); + + com_token[len] = 0; + return data; +} + + +/* +================ +COM_CheckParm + +Returns the position (1 to argc-1) in the program's argument list +where the given parameter appears, or 0 if not present +================ +*/ +int COM_CheckParm (char *parm) +{ + int i; + + for (i=1 ; inext) + { + if (s == com_base_searchpaths) + Con_Printf ("----------\n"); + if (s->pack) + Con_Printf ("%s (%i files)\n", s->pack->filename, s->pack->numfiles); + else + Con_Printf ("%s\n", s->filename); + } +} + +/* +============ +COM_WriteFile + +The filename will be prefixed by the current game directory +============ +*/ +void COM_WriteFile (char *filename, void *data, int len) +{ + FILE *f; + char name[MAX_OSPATH]; + + sprintf (name, "%s/%s", com_gamedir, filename); + + f = fopen (name, "wb"); + if (!f) { + Sys_mkdir(com_gamedir); + f = fopen (name, "wb"); + if (!f) + Sys_Error ("Error opening %s", filename); + } + + Sys_Printf ("COM_WriteFile: %s\n", name); + fwrite (data, 1, len, f); + fclose (f); +} + + +/* +============ +COM_CreatePath + +Only used for CopyFile and download +============ +*/ +void COM_CreatePath (char *path) +{ + char *ofs; + + for (ofs = path+1 ; *ofs ; ofs++) + { + if (*ofs == '/') + { // create the directory + *ofs = 0; + Sys_mkdir (path); + *ofs = '/'; + } + } +} + + +/* +=========== +COM_CopyFile + +Copies a file over from the net to the local cache, creating any directories +needed. This is for the convenience of developers using ISDN from home. +=========== +*/ +void COM_CopyFile (char *netpath, char *cachepath) +{ + FILE *in, *out; + int remaining, count; + char buf[4096]; + + remaining = COM_FileOpenRead (netpath, &in); + COM_CreatePath (cachepath); // create directories up to the cache file + out = fopen(cachepath, "wb"); + if (!out) + Sys_Error ("Error opening %s", cachepath); + + while (remaining) + { + if (remaining < sizeof(buf)) + count = remaining; + else + count = sizeof(buf); + fread (buf, 1, count, in); + fwrite (buf, 1, count, out); + remaining -= count; + } + + fclose (in); + fclose (out); +} + +/* +=========== +COM_FindFile + +Finds the file in the search path. +Sets com_filesize and one of handle or file +=========== +*/ +int file_from_pak; // global indicating file came from pack file ZOID + +int COM_FOpenFile (char *filename, FILE **file) +{ + searchpath_t *search; + char netpath[MAX_OSPATH]; + pack_t *pak; + int i; + int findtime; + + file_from_pak = 0; + +// +// search through the path, one element at a time +// + for (search = com_searchpaths ; search ; search = search->next) + { + // is the element a pak file? + if (search->pack) + { + // look through all the pak file elements + pak = search->pack; + for (i=0 ; inumfiles ; i++) + if (!strcmp (pak->files[i].name, filename)) + { // found it! + if (developer.value) + Sys_Printf ("PackFile: %s : %s\n", pak->filename, filename); + // open a new file on the pakfile + *file = fopen (pak->filename, "rb"); + if (!*file) + Sys_Error ("Couldn't reopen %s", pak->filename); + fseek (*file, pak->files[i].filepos, SEEK_SET); + com_filesize = pak->files[i].filelen; + file_from_pak = 1; + return com_filesize; + } + } + else + { + sprintf (netpath, "%s/%s",search->filename, filename); + + findtime = Sys_FileTime (netpath); + if (findtime == -1) + continue; + + if (developer.value) + Sys_Printf ("FindFile: %s\n",netpath); + + *file = fopen (netpath, "rb"); + return COM_filelength (*file); + } + + } + + Sys_Printf ("FindFile: can't find %s\n", filename); + + *file = NULL; + com_filesize = -1; + return -1; +} + +/* +============ +COM_LoadFile + +Filename are relative to the quake directory. +Always appends a 0 byte to the loaded data. +============ +*/ +cache_user_t *loadcache; +byte *loadbuf; +int loadsize; +void *Hunk_AllocName_f (int size, char *name, qboolean clean); +byte *COM_LoadFile (char *path, int usehunk) +{ + FILE *h; + byte *buf; + char base[32]; + int len; +#ifdef SERVERONLY + extern cvar_t sv_cpserver; + int l, count; +#define READMAX 50000 +#endif + + buf = NULL; // quiet compiler warning + +// look for it in the filesystem or pack files + len = com_filesize = COM_FOpenFile (path, &h); + if (!h) + return NULL; + +// extract the filename base name for hunk tag + COM_FileBase (path, base); + + if (usehunk == 1) + buf = Hunk_AllocName_f (len+1, base, false); + else if (usehunk == 2) + buf = Hunk_TempAlloc (len+1); + else if (usehunk == 0) + buf = Z_Malloc (len+1); + else if (usehunk == 3) + buf = Cache_Alloc (loadcache, len+1, base); + else if (usehunk == 4) + { + if (len+1 > loadsize) + buf = Hunk_TempAlloc (len+1); + else + buf = loadbuf; + } + else + Sys_Error ("COM_LoadFile: bad usehunk"); + + if (!buf) + Sys_Error ("COM_LoadFile: not enough space for %s", path); + + ((byte *)buf)[len] = 0; +#ifndef SERVERONLY + Draw_BeginDisc (); +#endif + +#ifdef SERVERONLY + l = 0; + count = 0; + + while (!feof(h)) { + if (l + 128 > len) { + fread(buf+l, 1, len - l, h); + break; + } + + fread(buf+l, 1, 128, h); + l += 128; + if (l - count > READMAX && (sv_cpserver.value > 0) && (sv_cpserver.value < 100)) { + Sys_Sleep(sv_cpserver.value); + count = l; + } + } + /* + if (len - l > READMAX) + { + fread (buf+l, 1, READMAX, h); + l+=READMAX; + Sys_Sleep(10); + } else { + fread (buf+l, 1, len - l, h); + break; + } + } while (1); + */ +#else + fread (buf, 1, len, h); +#endif + + fclose (h); +#ifndef SERVERONLY + Draw_EndDisc (); +#endif + + return buf; +} + +byte *COM_LoadHunkFile (char *path) +{ + return COM_LoadFile (path, 1); +} + +byte *COM_LoadTempFile (char *path) +{ + return COM_LoadFile (path, 2); +} + +void COM_LoadCacheFile (char *path, struct cache_user_s *cu) +{ + loadcache = cu; + COM_LoadFile (path, 3); +} + +// uses temp hunk if larger than bufsize +byte *COM_LoadStackFile (char *path, void *buffer, int bufsize) +{ + byte *buf; + + loadbuf = (byte *)buffer; + loadsize = bufsize; + buf = COM_LoadFile (path, 4); + + return buf; +} + +/* +================= +COM_LoadPackFile + +Takes an explicit (not game tree related) path to a pak file. + +Loads the header and directory, adding the files at the beginning +of the list so they override previous pack files. +================= +*/ +pack_t *COM_LoadPackFile (char *packfile) +{ + dpackheader_t header; + int i; + packfile_t *newfiles; + int numpackfiles; + pack_t *pack; + FILE *packhandle; + dpackfile_t info[MAX_FILES_IN_PACK]; + + if (COM_FileOpenRead (packfile, &packhandle) == -1) + return NULL; + + fread (&header, 1, sizeof(header), packhandle); + if (header.id[0] != 'P' || header.id[1] != 'A' + || header.id[2] != 'C' || header.id[3] != 'K') + Sys_Error ("%s is not a packfile", packfile); + header.dirofs = LittleLong (header.dirofs); + header.dirlen = LittleLong (header.dirlen); + + numpackfiles = header.dirlen / sizeof(dpackfile_t); + + if (numpackfiles > MAX_FILES_IN_PACK) + Sys_Error ("%s has %i files", packfile, numpackfiles); + + newfiles = Z_Malloc (numpackfiles * sizeof(packfile_t)); + + fseek (packhandle, header.dirofs, SEEK_SET); + fread (&info, 1, header.dirlen, packhandle); + +// parse the directory + for (i=0 ; ifilename, packfile); + pack->handle = packhandle; + pack->numfiles = numpackfiles; + pack->files = newfiles; + + Con_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles); + return pack; +} + + +/* +================ +COM_AddGameDirectory + +Sets com_gamedir, adds the directory to the head of the path, +then loads and adds pak1.pak pak2.pak ... +================ +*/ +void COM_AddGameDirectory (char *dir) +{ + int i; + searchpath_t *search; + pack_t *pak; + char pakfile[MAX_OSPATH]; + char *p; + + if ((p = strrchr(dir, '/')) != NULL) + strcpy(gamedirfile, ++p); + else + strcpy(gamedirfile, p); + strcpy (com_gamedir, dir); + +// +// add the directory to the search path +// + search = Hunk_Alloc (sizeof(searchpath_t)); + strcpy (search->filename, dir); + search->next = com_searchpaths; + com_searchpaths = search; + +// +// add any pak files in the format pak0.pak pak1.pak, ... +// + for (i=0 ; ; i++) + { + sprintf (pakfile, "%s/pak%i.pak", dir, i); + pak = COM_LoadPackFile (pakfile); + if (!pak) + break; + search = Hunk_Alloc (sizeof(searchpath_t)); + search->pack = pak; + search->next = com_searchpaths; + com_searchpaths = search; + } + +} + +/* +================ +COM_Gamedir + +Sets the gamedir and path to a different directory. +================ +*/ +void COM_Gamedir (char *dir) +{ + searchpath_t *search, *next; + int i; + pack_t *pak; + char pakfile[MAX_OSPATH]; + + if (strstr(dir, "..") || strstr(dir, "/") + || strstr(dir, "\\") || strstr(dir, ":") ) + { + Con_Printf ("Gamedir should be a single filename, not a path\n"); + return; + } + + if (!strcmp(gamedirfile, dir)) + return; // still the same + strcpy (gamedirfile, dir); + + // + // free up any current game dir info + // + while (com_searchpaths != com_base_searchpaths) + { + if (com_searchpaths->pack) + { + fclose (com_searchpaths->pack->handle); + Z_Free (com_searchpaths->pack->files); + Z_Free (com_searchpaths->pack); + } + next = com_searchpaths->next; + Z_Free (com_searchpaths); + com_searchpaths = next; + } + + // + // flush all data, so it will be forced to reload + // + Cache_Flush (); + + if (!strcmp(dir,"id1") || !strcmp(dir, "qw")) + return; + + sprintf (com_gamedir, "%s/%s", com_basedir, dir); + + // + // add the directory to the search path + // + search = Z_Malloc (sizeof(searchpath_t)); + strcpy (search->filename, com_gamedir); + search->next = com_searchpaths; + com_searchpaths = search; + + // + // add any pak files in the format pak0.pak pak1.pak, ... + // + for (i=0 ; ; i++) + { + sprintf (pakfile, "%s/pak%i.pak", com_gamedir, i); + pak = COM_LoadPackFile (pakfile); + if (!pak) + break; + search = Z_Malloc (sizeof(searchpath_t)); + search->pack = pak; + search->next = com_searchpaths; + com_searchpaths = search; + } +} + +/* +================ +COM_InitFilesystem +================ +*/ +void COM_InitFilesystem (void) +{ + int i; + +// +// -basedir +// Overrides the system supplied base directory (under id1) +// + i = COM_CheckParm ("-basedir"); + if (i && i < com_argc-1) + Q_strncpyz (com_basedir, com_argv[i+1], sizeof(com_basedir)); + else + Q_strncpyz (com_basedir, host_parms.basedir, sizeof(com_basedir)); + + i = strlen(com_basedir)-1; + if ((i >= 0) && (com_basedir[i]=='/' || com_basedir[i]=='\\')) + com_basedir[i] = '\0'; + +// +// start up with id1 by default +// + COM_AddGameDirectory (va("%s/id1", com_basedir) ); + COM_AddGameDirectory (va("%s/qw", com_basedir) ); + + // any set gamedirs will be freed up to here + com_base_searchpaths = com_searchpaths; +} + + + +/* +===================================================================== + + INFO STRINGS + +===================================================================== +*/ + +/* +=============== +Info_ValueForKey + +Searches the string for the given +key and returns the associated value, or an empty string. +=============== +*/ +char *Info_ValueForKey (char *s, char *key) +{ + char pkey[512]; + static char value[4][512]; // use two buffers so compares + // work without stomping on each other + static int valueindex; + char *o; + + valueindex = (valueindex + 1) % 4; + if (*s == '\\') + s++; + while (1) + { + o = pkey; + while (*s != '\\') + { + if (!*s) + return ""; + *o++ = *s++; + } + *o = 0; + s++; + + o = value[valueindex]; + + while (*s != '\\' && *s) + { + if (!*s) + return ""; + *o++ = *s++; + } + *o = 0; + + if (!strcmp (key, pkey) ) + return value[valueindex]; + + if (!*s) + return ""; + s++; + } +} + +void Info_RemoveKey (char *s, char *key) +{ + char *start; + char pkey[512]; + char value[512]; + char *o; + + if (strstr (key, "\\")) + { + Con_Printf ("Can't use a key with a \\\n"); + return; + } + + while (1) + { + start = s; + if (*s == '\\') + s++; + o = pkey; + while (*s != '\\') + { + if (!*s) + return; + *o++ = *s++; + } + *o = 0; + s++; + + o = value; + while (*s != '\\' && *s) + { + if (!*s) + return; + *o++ = *s++; + } + *o = 0; + + if (!strcmp (key, pkey) ) + { + strcpy (start, s); // remove this part + return; + } + + if (!*s) + return; + } + +} + +void Info_RemovePrefixedKeys (char *start, char prefix) +{ + char *s; + char pkey[512]; + char value[512]; + char *o; + + s = start; + + while (1) + { + if (*s == '\\') + s++; + o = pkey; + while (*s != '\\') + { + if (!*s) + return; + *o++ = *s++; + } + *o = 0; + s++; + + o = value; + while (*s != '\\' && *s) + { + if (!*s) + return; + *o++ = *s++; + } + *o = 0; + + if (pkey[0] == prefix) + { + Info_RemoveKey (start, pkey); + s = start; + } + + if (!*s) + return; + } + +} + + +void Info_SetValueForStarKey (char *s, char *key, char *value, int maxsize) +{ + char new[1024], *v; + int c; +#ifdef SERVERONLY + extern cvar_t sv_highchars; +#endif + + if (strstr (key, "\\") || strstr (value, "\\") ) + { + Con_Printf ("Can't use keys or values with a \\\n"); + return; + } + + if (strstr (key, "\"") || strstr (value, "\"") ) + { + Con_Printf ("Can't use keys or values with a \"\n"); + return; + } + + if (strlen(key) > 63 || strlen(value) > 63) + { + Con_Printf ("Keys and values must be < 64 characters.\n"); + return; + } + + // this next line is kinda trippy + if (*(v = Info_ValueForKey(s, key))) { + // key exists, make sure we have enough room for new value, if we don't, + // don't change it! + if (strlen(value) - strlen(v) + strlen(s) > maxsize) { + Con_Printf ("Info string length exceeded\n"); + return; + } + } + Info_RemoveKey (s, key); + if (!value || !strlen(value)) + return; + + sprintf (new, "\\%s\\%s", key, value); + + if ((int)(strlen(new) + strlen(s)) > maxsize) + { + Con_Printf ("Info string length exceeded\n"); + return; + } + + // only copy ascii values + s += strlen(s); + v = new; + while (*v) + { + c = (unsigned char)*v++; +#ifndef SERVERONLY + // client only allows highbits on name + if (stricmp(key, "name") != 0) { + c &= 127; + if (c < 32 || c > 127) + continue; + // auto lowercase team + if (stricmp(key, "team") == 0) + c = tolower(c); + } +#else + if (!sv_highchars.value) { + c &= 127; + if (c < 32 || c > 127) + continue; + } +#endif +// c &= 127; // strip high bits + if (c > 13) // && c < 127) + *s++ = c; + } + *s = 0; +} + +void Info_SetValueForKey (char *s, char *key, char *value, int maxsize) +{ + if (key[0] == '*') + { + Con_Printf ("Can't set * keys\n"); + return; + } + + Info_SetValueForStarKey (s, key, value, maxsize); +} + +void Info_Print (char *s) +{ + char key[512]; + char value[512]; + char *o; + int l; + + if (*s == '\\') + s++; + while (*s) + { + o = key; + while (*s && *s != '\\') + *o++ = *s++; + + l = o - key; + if (l < 20) + { + memset (o, ' ', 20-l); + key[20] = 0; + } + else + *o = 0; + Con_Printf ("%s", key); + + if (!*s) + { + Con_Printf ("MISSING VALUE\n"); + return; + } + + o = value; + s++; + while (*s && *s != '\\') + *o++ = *s++; + *o = 0; + + if (*s) + s++; + Con_Printf ("%s\n", value); + } +} + +static byte chktbl[1024 + 4] = { +0x78,0xd2,0x94,0xe3,0x41,0xec,0xd6,0xd5,0xcb,0xfc,0xdb,0x8a,0x4b,0xcc,0x85,0x01, +0x23,0xd2,0xe5,0xf2,0x29,0xa7,0x45,0x94,0x4a,0x62,0xe3,0xa5,0x6f,0x3f,0xe1,0x7a, +0x64,0xed,0x5c,0x99,0x29,0x87,0xa8,0x78,0x59,0x0d,0xaa,0x0f,0x25,0x0a,0x5c,0x58, +0xfb,0x00,0xa7,0xa8,0x8a,0x1d,0x86,0x80,0xc5,0x1f,0xd2,0x28,0x69,0x71,0x58,0xc3, +0x51,0x90,0xe1,0xf8,0x6a,0xf3,0x8f,0xb0,0x68,0xdf,0x95,0x40,0x5c,0xe4,0x24,0x6b, +0x29,0x19,0x71,0x3f,0x42,0x63,0x6c,0x48,0xe7,0xad,0xa8,0x4b,0x91,0x8f,0x42,0x36, +0x34,0xe7,0x32,0x55,0x59,0x2d,0x36,0x38,0x38,0x59,0x9b,0x08,0x16,0x4d,0x8d,0xf8, +0x0a,0xa4,0x52,0x01,0xbb,0x52,0xa9,0xfd,0x40,0x18,0x97,0x37,0xff,0xc9,0x82,0x27, +0xb2,0x64,0x60,0xce,0x00,0xd9,0x04,0xf0,0x9e,0x99,0xbd,0xce,0x8f,0x90,0x4a,0xdd, +0xe1,0xec,0x19,0x14,0xb1,0xfb,0xca,0x1e,0x98,0x0f,0xd4,0xcb,0x80,0xd6,0x05,0x63, +0xfd,0xa0,0x74,0xa6,0x86,0xf6,0x19,0x98,0x76,0x27,0x68,0xf7,0xe9,0x09,0x9a,0xf2, +0x2e,0x42,0xe1,0xbe,0x64,0x48,0x2a,0x74,0x30,0xbb,0x07,0xcc,0x1f,0xd4,0x91,0x9d, +0xac,0x55,0x53,0x25,0xb9,0x64,0xf7,0x58,0x4c,0x34,0x16,0xbc,0xf6,0x12,0x2b,0x65, +0x68,0x25,0x2e,0x29,0x1f,0xbb,0xb9,0xee,0x6d,0x0c,0x8e,0xbb,0xd2,0x5f,0x1d,0x8f, +0xc1,0x39,0xf9,0x8d,0xc0,0x39,0x75,0xcf,0x25,0x17,0xbe,0x96,0xaf,0x98,0x9f,0x5f, +0x65,0x15,0xc4,0x62,0xf8,0x55,0xfc,0xab,0x54,0xcf,0xdc,0x14,0x06,0xc8,0xfc,0x42, +0xd3,0xf0,0xad,0x10,0x08,0xcd,0xd4,0x11,0xbb,0xca,0x67,0xc6,0x48,0x5f,0x9d,0x59, +0xe3,0xe8,0x53,0x67,0x27,0x2d,0x34,0x9e,0x9e,0x24,0x29,0xdb,0x69,0x99,0x86,0xf9, +0x20,0xb5,0xbb,0x5b,0xb0,0xf9,0xc3,0x67,0xad,0x1c,0x9c,0xf7,0xcc,0xef,0xce,0x69, +0xe0,0x26,0x8f,0x79,0xbd,0xca,0x10,0x17,0xda,0xa9,0x88,0x57,0x9b,0x15,0x24,0xba, +0x84,0xd0,0xeb,0x4d,0x14,0xf5,0xfc,0xe6,0x51,0x6c,0x6f,0x64,0x6b,0x73,0xec,0x85, +0xf1,0x6f,0xe1,0x67,0x25,0x10,0x77,0x32,0x9e,0x85,0x6e,0x69,0xb1,0x83,0x00,0xe4, +0x13,0xa4,0x45,0x34,0x3b,0x40,0xff,0x41,0x82,0x89,0x79,0x57,0xfd,0xd2,0x8e,0xe8, +0xfc,0x1d,0x19,0x21,0x12,0x00,0xd7,0x66,0xe5,0xc7,0x10,0x1d,0xcb,0x75,0xe8,0xfa, +0xb6,0xee,0x7b,0x2f,0x1a,0x25,0x24,0xb9,0x9f,0x1d,0x78,0xfb,0x84,0xd0,0x17,0x05, +0x71,0xb3,0xc8,0x18,0xff,0x62,0xee,0xed,0x53,0xab,0x78,0xd3,0x65,0x2d,0xbb,0xc7, +0xc1,0xe7,0x70,0xa2,0x43,0x2c,0x7c,0xc7,0x16,0x04,0xd2,0x45,0xd5,0x6b,0x6c,0x7a, +0x5e,0xa1,0x50,0x2e,0x31,0x5b,0xcc,0xe8,0x65,0x8b,0x16,0x85,0xbf,0x82,0x83,0xfb, +0xde,0x9f,0x36,0x48,0x32,0x79,0xd6,0x9b,0xfb,0x52,0x45,0xbf,0x43,0xf7,0x0b,0x0b, +0x19,0x19,0x31,0xc3,0x85,0xec,0x1d,0x8c,0x20,0xf0,0x3a,0xfa,0x80,0x4d,0x2c,0x7d, +0xac,0x60,0x09,0xc0,0x40,0xee,0xb9,0xeb,0x13,0x5b,0xe8,0x2b,0xb1,0x20,0xf0,0xce, +0x4c,0xbd,0xc6,0x04,0x86,0x70,0xc6,0x33,0xc3,0x15,0x0f,0x65,0x19,0xfd,0xc2,0xd3, + +// map checksum goes here +0x00,0x00,0x00,0x00 +}; + +static byte chkbuf[16 + 60 + 4]; + +static unsigned last_mapchecksum = 0; + +#if 0 +/* +==================== +COM_BlockSequenceCheckByte + +For proxy protecting +==================== +*/ +byte COM_BlockSequenceCheckByte (byte *base, int length, int sequence, unsigned mapchecksum) +{ + int checksum; + byte *p; + + if (last_mapchecksum != mapchecksum) { + last_mapchecksum = mapchecksum; + chktbl[1024] = (mapchecksum & 0xff000000) >> 24; + chktbl[1025] = (mapchecksum & 0x00ff0000) >> 16; + chktbl[1026] = (mapchecksum & 0x0000ff00) >> 8; + chktbl[1027] = (mapchecksum & 0x000000ff); + + Com_BlockFullChecksum (chktbl, sizeof(chktbl), chkbuf); + } + + p = chktbl + (sequence % (sizeof(chktbl) - 8)); + + if (length > 60) + length = 60; + memcpy (chkbuf + 16, base, length); + + length += 16; + + chkbuf[length] = (sequence & 0xff) ^ p[0]; + chkbuf[length+1] = p[1]; + chkbuf[length+2] = ((sequence>>8) & 0xff) ^ p[2]; + chkbuf[length+3] = p[3]; + + length += 4; + + checksum = LittleLong(Com_BlockChecksum (chkbuf, length)); + + checksum &= 0xff; + + return checksum; +} +#endif + +/* +==================== +COM_BlockSequenceCRCByte + +For proxy protecting +==================== +*/ +byte COM_BlockSequenceCRCByte (byte *base, int length, int sequence) +{ + unsigned short crc; + byte *p; + byte chkb[60 + 4]; + + p = chktbl + (sequence % (sizeof(chktbl) - 8)); + + if (length > 60) + length = 60; + memcpy (chkb, base, length); + + chkb[length] = (sequence & 0xff) ^ p[0]; + chkb[length+1] = p[1]; + chkb[length+2] = ((sequence>>8) & 0xff) ^ p[2]; + chkb[length+3] = p[3]; + + length += 4; + + crc = CRC_Block(chkb, length); + + crc &= 0xff; + + return crc; +} + diff --git a/source/common.h b/source/common.h index e214e74e..329cc296 100644 --- a/source/common.h +++ b/source/common.h @@ -1,208 +1,208 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// common.h -- general definitions - -typedef unsigned char byte; -#define _DEF_BYTE_ - -// KJB Undefined true and false defined in SciTech's DEBUG.H header -#undef true -#undef false - -typedef enum {false, true} qboolean; - -#define MAX_INFO_STRING 196 -#define MAX_SERVERINFO_STRING 512 -#define MAX_LOCALINFO_STRING 32768 - -//============================================================================ - -typedef struct sizebuf_s -{ - qboolean allowoverflow; // if false, do a Sys_Error - qboolean overflowed; // set to true if the buffer size failed - byte *data; - int maxsize; - int cursize; -} sizebuf_t; - -void SZ_Clear (sizebuf_t *buf); -void *SZ_GetSpace (sizebuf_t *buf, int length); -void SZ_Write (sizebuf_t *buf, void *data, int length); -void SZ_Print (sizebuf_t *buf, char *data); // strcats onto the sizebuf - -//============================================================================ - -typedef struct link_s -{ - struct link_s *prev, *next; -} link_t; - - -void ClearLink (link_t *l); -void RemoveLink (link_t *l); -void InsertLinkBefore (link_t *l, link_t *before); -void InsertLinkAfter (link_t *l, link_t *after); - -// (type *)STRUCT_FROM_LINK(link_t *link, type, member) -// ent = STRUCT_FROM_LINK(link,entity_t,order) -// FIXME: remove this mess! -#define STRUCT_FROM_LINK(l,t,m) ((t *)((byte *)l - (int)&(((t *)0)->m))) - -//============================================================================ - -#ifndef NULL -#define NULL ((void *)0) -#endif - -#define Q_MAXCHAR ((char)0x7f) -#define Q_MAXSHORT ((short)0x7fff) -#define Q_MAXINT ((int)0x7fffffff) -#define Q_MAXLONG ((int)0x7fffffff) -#define Q_MAXFLOAT ((int)0x7fffffff) - -#define Q_MINCHAR ((char)0x80) -#define Q_MINSHORT ((short)0x8000) -#define Q_MININT ((int)0x80000000) -#define Q_MINLONG ((int)0x80000000) -#define Q_MINFLOAT ((int)0x7fffffff) - -//============================================================================ - -extern qboolean bigendien; - -extern short (*BigShort) (short l); -extern short (*LittleShort) (short l); -extern int (*BigLong) (int l); -extern int (*LittleLong) (int l); -extern float (*BigFloat) (float l); -extern float (*LittleFloat) (float l); - -//============================================================================ - -struct usercmd_s; - -extern struct usercmd_s nullcmd; - -void MSG_WriteChar (sizebuf_t *sb, int c); -void MSG_WriteByte (sizebuf_t *sb, int c); -void MSG_WriteShort (sizebuf_t *sb, int c); -void MSG_WriteLong (sizebuf_t *sb, int c); -void MSG_WriteFloat (sizebuf_t *sb, float f); -void MSG_WriteString (sizebuf_t *sb, char *s); -void MSG_WriteCoord (sizebuf_t *sb, float f); -void MSG_WriteAngle (sizebuf_t *sb, float f); -void MSG_WriteAngle16 (sizebuf_t *sb, float f); -void MSG_WriteDeltaUsercmd (sizebuf_t *sb, struct usercmd_s *from, struct usercmd_s *cmd); - -extern int msg_readcount; -extern qboolean msg_badread; // set if a read goes beyond end of message - -void MSG_BeginReading (void); -int MSG_GetReadCount(void); -int MSG_ReadChar (void); -int MSG_ReadByte (void); -int MSG_ReadShort (void); -int MSG_ReadLong (void); -float MSG_ReadFloat (void); -char *MSG_ReadString (void); -char *MSG_ReadStringLine (void); - -float MSG_ReadCoord (void); -float MSG_ReadAngle (void); -float MSG_ReadAngle16 (void); -void MSG_ReadDeltaUsercmd (struct usercmd_s *from, struct usercmd_s *cmd); - -//============================================================================ - -#ifdef _WIN32 - -#define Q_strcasecmp(s1, s2) _stricmp((s1), (s2)) -#define Q_strncasecmp(s1, s2, n) _strnicmp((s1), (s2), (n)) - -#else - -#define Q_strcasecmp(s1, s2) strcasecmp((s1), (s2)) -#define Q_strncasecmp(s1, s2, n) strncasecmp((s1), (s2), (n)) - -#endif - -int Q_atoi (char *str); -float Q_atof (char *str); - -void Q_strncpyz (char *dest, char *src, size_t size); - -//============================================================================ - -extern char com_token[1024]; -extern qboolean com_eof; - -char *COM_Parse (char *data); - - -extern int com_argc; -extern char **com_argv; - -int COM_CheckParm (char *parm); -void COM_AddParm (char *parm); - -void COM_Init (void); -void COM_InitArgv (int argc, char **argv); - -char *COM_SkipPath (char *pathname); -void COM_StripExtension (char *in, char *out); -void COM_FileBase (char *in, char *out); -void COM_DefaultExtension (char *path, char *extension); -void COM_ForceExtension (char *path, char *extension); - -char *va(char *format, ...); -// does a varargs printf into a temp buffer - - -//============================================================================ - -extern int com_filesize; -struct cache_user_s; - -extern char com_gamedir[MAX_OSPATH]; -extern char com_basedir[MAX_OSPATH]; - -void COM_WriteFile (char *filename, void *data, int len); -int COM_FOpenFile (char *filename, FILE **file); -void COM_CloseFile (FILE *h); - -byte *COM_LoadStackFile (char *path, void *buffer, int bufsize); -byte *COM_LoadTempFile (char *path); -byte *COM_LoadHunkFile (char *path); -void COM_LoadCacheFile (char *path, struct cache_user_s *cu); -void COM_CreatePath (char *path); -void COM_Gamedir (char *dir); - -char *Info_ValueForKey (char *s, char *key); -void Info_RemoveKey (char *s, char *key); -void Info_RemovePrefixedKeys (char *start, char prefix); -void Info_SetValueForKey (char *s, char *key, char *value, int maxsize); -void Info_SetValueForStarKey (char *s, char *key, char *value, int maxsize); -void Info_Print (char *s); - -unsigned Com_BlockChecksum (void *buffer, int length); -void Com_BlockFullChecksum (void *buffer, int len, unsigned char *outbuf); -byte COM_BlockSequenceCheckByte (byte *base, int length, int sequence, unsigned mapchecksum); -byte COM_BlockSequenceCRCByte (byte *base, int length, int sequence); +/* +Copyright (C) 1996-1997 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. + +*/ +// common.h -- general definitions + +typedef unsigned char byte; +#define _DEF_BYTE_ + +// KJB Undefined true and false defined in SciTech's DEBUG.H header +#undef true +#undef false + +typedef enum {false, true} qboolean; + +#define MAX_INFO_STRING 196 +#define MAX_SERVERINFO_STRING 512 +#define MAX_LOCALINFO_STRING 32768 + +//============================================================================ + +typedef struct sizebuf_s +{ + qboolean allowoverflow; // if false, do a Sys_Error + qboolean overflowed; // set to true if the buffer size failed + byte *data; + int maxsize; + int cursize; +} sizebuf_t; + +void SZ_Clear (sizebuf_t *buf); +void *SZ_GetSpace (sizebuf_t *buf, int length); +void SZ_Write (sizebuf_t *buf, void *data, int length); +void SZ_Print (sizebuf_t *buf, char *data); // strcats onto the sizebuf + +//============================================================================ + +typedef struct link_s +{ + struct link_s *prev, *next; +} link_t; + + +void ClearLink (link_t *l); +void RemoveLink (link_t *l); +void InsertLinkBefore (link_t *l, link_t *before); +void InsertLinkAfter (link_t *l, link_t *after); + +// (type *)STRUCT_FROM_LINK(link_t *link, type, member) +// ent = STRUCT_FROM_LINK(link,entity_t,order) +// FIXME: remove this mess! +#define STRUCT_FROM_LINK(l,t,m) ((t *)((byte *)l - (int)&(((t *)0)->m))) + +//============================================================================ + +#ifndef NULL +#define NULL ((void *)0) +#endif + +#define Q_MAXCHAR ((char)0x7f) +#define Q_MAXSHORT ((short)0x7fff) +#define Q_MAXINT ((int)0x7fffffff) +#define Q_MAXLONG ((int)0x7fffffff) +#define Q_MAXFLOAT ((int)0x7fffffff) + +#define Q_MINCHAR ((char)0x80) +#define Q_MINSHORT ((short)0x8000) +#define Q_MININT ((int)0x80000000) +#define Q_MINLONG ((int)0x80000000) +#define Q_MINFLOAT ((int)0x7fffffff) + +//============================================================================ + +extern qboolean bigendien; + +extern short (*BigShort) (short l); +extern short (*LittleShort) (short l); +extern int (*BigLong) (int l); +extern int (*LittleLong) (int l); +extern float (*BigFloat) (float l); +extern float (*LittleFloat) (float l); + +//============================================================================ + +struct usercmd_s; + +extern struct usercmd_s nullcmd; + +void MSG_WriteChar (sizebuf_t *sb, int c); +void MSG_WriteByte (sizebuf_t *sb, int c); +void MSG_WriteShort (sizebuf_t *sb, int c); +void MSG_WriteLong (sizebuf_t *sb, int c); +void MSG_WriteFloat (sizebuf_t *sb, float f); +void MSG_WriteString (sizebuf_t *sb, char *s); +void MSG_WriteCoord (sizebuf_t *sb, float f); +void MSG_WriteAngle (sizebuf_t *sb, float f); +void MSG_WriteAngle16 (sizebuf_t *sb, float f); +void MSG_WriteDeltaUsercmd (sizebuf_t *sb, struct usercmd_s *from, struct usercmd_s *cmd); + +extern int msg_readcount; +extern qboolean msg_badread; // set if a read goes beyond end of message + +void MSG_BeginReading (void); +int MSG_GetReadCount(void); +int MSG_ReadChar (void); +int MSG_ReadByte (void); +int MSG_ReadShort (void); +int MSG_ReadLong (void); +float MSG_ReadFloat (void); +char *MSG_ReadString (void); +char *MSG_ReadStringLine (void); + +float MSG_ReadCoord (void); +float MSG_ReadAngle (void); +float MSG_ReadAngle16 (void); +void MSG_ReadDeltaUsercmd (struct usercmd_s *from, struct usercmd_s *cmd); + +//============================================================================ + +#ifdef _WIN32 + +#define Q_strcasecmp(s1, s2) _stricmp((s1), (s2)) +#define Q_strncasecmp(s1, s2, n) _strnicmp((s1), (s2), (n)) + +#else + +#define Q_strcasecmp(s1, s2) strcasecmp((s1), (s2)) +#define Q_strncasecmp(s1, s2, n) strncasecmp((s1), (s2), (n)) + +#endif + +int Q_atoi (char *str); +float Q_atof (char *str); + +void Q_strncpyz (char *dest, char *src, size_t size); + +//============================================================================ + +extern char com_token[1024]; +extern qboolean com_eof; + +char *COM_Parse (char *data); + + +extern int com_argc; +extern char **com_argv; + +int COM_CheckParm (char *parm); +void COM_AddParm (char *parm); + +void COM_Init (void); +void COM_InitArgv (int argc, char **argv); + +char *COM_SkipPath (char *pathname); +void COM_StripExtension (char *in, char *out); +void COM_FileBase (char *in, char *out); +void COM_DefaultExtension (char *path, char *extension); +void COM_ForceExtension (char *path, char *extension); + +char *va(char *format, ...); +// does a varargs printf into a temp buffer + + +//============================================================================ + +extern int com_filesize; +struct cache_user_s; + +extern char com_gamedir[MAX_OSPATH]; +extern char com_basedir[MAX_OSPATH]; + +void COM_WriteFile (char *filename, void *data, int len); +int COM_FOpenFile (char *filename, FILE **file); +void COM_CloseFile (FILE *h); + +byte *COM_LoadStackFile (char *path, void *buffer, int bufsize); +byte *COM_LoadTempFile (char *path); +byte *COM_LoadHunkFile (char *path); +void COM_LoadCacheFile (char *path, struct cache_user_s *cu); +void COM_CreatePath (char *path); +void COM_Gamedir (char *dir); + +char *Info_ValueForKey (char *s, char *key); +void Info_RemoveKey (char *s, char *key); +void Info_RemovePrefixedKeys (char *start, char prefix); +void Info_SetValueForKey (char *s, char *key, char *value, int maxsize); +void Info_SetValueForStarKey (char *s, char *key, char *value, int maxsize); +void Info_Print (char *s); + +unsigned Com_BlockChecksum (void *buffer, int length); +void Com_BlockFullChecksum (void *buffer, int len, unsigned char *outbuf); +byte COM_BlockSequenceCheckByte (byte *base, int length, int sequence, unsigned mapchecksum); +byte COM_BlockSequenceCRCByte (byte *base, int length, int sequence); diff --git a/source/console.c b/source/console.c index f054f472..80cea98f 100644 --- a/source/console.c +++ b/source/console.c @@ -1,723 +1,723 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// console.c - -#include "quakedef.h" -#include "keys.h" - -int con_ormask; -console_t con_main; -console_t con_chat; -console_t *con; // point to either con_main or con_chat - -int con_linewidth; // characters across screen -int con_totallines; // total lines in console scrollback - -float con_cursorspeed = 4; - - -cvar_t con_notifytime = {"con_notifytime","3"}; //seconds - -#define NUM_CON_TIMES 4 -float con_times[NUM_CON_TIMES]; // realtime time the line was generated - // for transparent notify lines - -int con_vislines; -int con_notifylines; // scan lines to clear for notify lines - -qboolean con_debuglog; - -#define MAXCMDLINE 256 -extern char key_lines[32][MAXCMDLINE]; -extern int edit_line; -extern int key_linepos; - - -qboolean con_initialized; - - -void Key_ClearTyping (void) -{ - key_lines[edit_line][1] = 0; // clear any typing - key_linepos = 1; -} - -/* -================ -Con_ToggleConsole_f -================ -*/ -void Con_ToggleConsole_f (void) -{ - Key_ClearTyping (); - - if (key_dest == key_console) - { - if (cls.state == ca_active) - key_dest = key_game; - } - else - key_dest = key_console; - - Con_ClearNotify (); -} - -/* -================ -Con_ToggleChat_f -================ -*/ -void Con_ToggleChat_f (void) -{ - Key_ClearTyping (); - - if (key_dest == key_console) - { - if (cls.state == ca_active) - key_dest = key_game; - } - else - key_dest = key_console; - - Con_ClearNotify (); -} - -/* -================ -Con_Clear_f -================ -*/ -void Con_Clear_f (void) -{ - con_main.numlines = 0; - con_chat.numlines = 0; - memset (con_main.text, ' ', CON_TEXTSIZE); - memset (con_chat.text, ' ', CON_TEXTSIZE); - con_main.display = con_main.current; -} - - -/* -================ -Con_ClearNotify -================ -*/ -void Con_ClearNotify (void) -{ - int i; - - for (i=0 ; i> 3) - 2; - - if (width == con_linewidth) - return; - - if (width < 1) // video hasn't been initialized yet - { - width = 38; - con_linewidth = width; - con_totallines = CON_TEXTSIZE / con_linewidth; - memset (con->text, ' ', CON_TEXTSIZE); - } - else - { - oldwidth = con_linewidth; - con_linewidth = width; - oldtotallines = con_totallines; - con_totallines = CON_TEXTSIZE / con_linewidth; - numlines = oldtotallines; - - if (con_totallines < numlines) - numlines = con_totallines; - - numchars = oldwidth; - - if (con_linewidth < numchars) - numchars = con_linewidth; - - memcpy (tbuf, con->text, CON_TEXTSIZE); - memset (con->text, ' ', CON_TEXTSIZE); - - for (i=0 ; itext[(con_totallines - 1 - i) * con_linewidth + j] = - tbuf[((con->current - i + oldtotallines) % - oldtotallines) * oldwidth + j]; - } - } - - Con_ClearNotify (); - } - - con->current = con_totallines - 1; - con->display = con->current; -} - - -/* -================ -Con_CheckResize - -If the line width has changed, reformat the buffer. -================ -*/ -void Con_CheckResize (void) -{ - Con_Resize (&con_main); - Con_Resize (&con_chat); -} - - -/* -================ -Con_Init -================ -*/ -void Con_Init (void) -{ - con_debuglog = COM_CheckParm("-condebug"); - - con = &con_main; - con_linewidth = -1; - Con_CheckResize (); - - Con_Printf ("Console initialized.\n"); - -// -// register our commands -// - Cvar_RegisterVariable (&con_notifytime); - - Cmd_AddCommand ("toggleconsole", Con_ToggleConsole_f); - Cmd_AddCommand ("togglechat", Con_ToggleChat_f); - Cmd_AddCommand ("messagemode", Con_MessageMode_f); - Cmd_AddCommand ("messagemode2", Con_MessageMode2_f); - Cmd_AddCommand ("clear", Con_Clear_f); - con_initialized = true; -} - - -/* -=============== -Con_Linefeed -=============== -*/ -void Con_Linefeed (void) -{ - con->x = 0; - if (con->display == con->current) - con->display++; - con->current++; - if (con->numlines < con_totallines) - con->numlines++; - memset (&con->text[(con->current%con_totallines)*con_linewidth] - , ' ', con_linewidth); -} - -/* -================ -Con_Print - -Handles cursor positioning, line wrapping, etc -All console printing must go through this in order to be logged to disk -If no console is visible, the notify window will pop up. -================ -*/ -void Con_Print (char *txt) -{ - int y; - int c, l; - static int cr; - int mask; - - if (txt[0] == 1 || txt[0] == 2) - { - mask = 128; // go to colored text - txt++; - } - else - mask = 0; - - - while ( (c = *txt) ) - { - // count word length - for (l=0 ; l< con_linewidth ; l++) - if ( txt[l] <= ' ') - break; - - // word wrap - if (l != con_linewidth && (con->x + l > con_linewidth) ) - con->x = 0; - - txt++; - - if (cr) - { - con->current--; - cr = false; - } - - - if (!con->x) - { - Con_Linefeed (); - // mark time for transparent overlay - if (con->current >= 0) - con_times[con->current % NUM_CON_TIMES] = realtime; - } - - switch (c) - { - case '\n': - con->x = 0; - break; - - case '\r': - con->x = 0; - cr = 1; - break; - - default: // display character and advance - y = con->current % con_totallines; - con->text[y*con_linewidth+con->x] = c | mask | con_ormask; - con->x++; - if (con->x >= con_linewidth) - con->x = 0; - break; - } - - } -} - - -/* -================ -Con_Printf - -Handles cursor positioning, line wrapping, etc -================ -*/ -#define MAXPRINTMSG 4096 -// FIXME: make a buffer size safe vsprintf? -void Con_Printf (char *fmt, ...) -{ - va_list argptr; - char msg[MAXPRINTMSG]; - - va_start (argptr,fmt); - vsprintf (msg,fmt,argptr); - va_end (argptr); - -// also echo to debugging console - Sys_Printf ("%s", msg); // also echo to debugging console - -// log all messages to file - if (con_debuglog) - Sys_DebugLog(va("%s/qconsole.log",com_gamedir), "%s", msg); - - if (!con_initialized) - return; - -// write it to the scrollable buffer - Con_Print (msg); - -#if 0 // Tonik -// update the screen immediately if the console is displayed - if (cls.state != ca_active) - { - static qboolean inupdate; - - // protect against infinite loop if something in SCR_UpdateScreen calls - // Con_Printd - if (!inupdate) - { - inupdate = true; - SCR_UpdateScreen (); - inupdate = false; - } - } -#endif // Tonik -} - -/* -================ -Con_DPrintf - -A Con_Printf that only shows up if the "developer" cvar is set -================ -*/ -void Con_DPrintf (char *fmt, ...) -{ - va_list argptr; - char msg[MAXPRINTMSG]; - - if (!developer.value) - return; // don't confuse non-developers with techie stuff... - - va_start (argptr,fmt); - vsprintf (msg,fmt,argptr); - va_end (argptr); - - Con_Printf ("%s", msg); -} - -/* -============================================================================== - -DRAWING - -============================================================================== -*/ - - -/* -================ -Con_DrawInput - -The input line scrolls horizontally if typing goes beyond the right edge -================ -*/ -void Con_DrawInput (void) -{ - int y; - int i; - char *text; - char temp[MAXCMDLINE]; - - if (key_dest != key_console && cls.state == ca_active) - return; // don't draw anything (always draw if not active) - - text = strcpy (temp, key_lines[edit_line]); - -// fill out remainder with spaces - for (i=strlen(text) ; i < MAXCMDLINE ; i++) - text[i] = ' '; - -// add the cursor frame - if ( (int)(noscale_realtime*con_cursorspeed) & 1 ) - text[key_linepos] = 11; - -// prestep if horizontally scrolling - if (key_linepos >= con_linewidth) - text += 1 + key_linepos - con_linewidth; - -// draw it - y = con_vislines-22; - - for (i=0 ; icurrent-NUM_CON_TIMES+1 ; i<=con->current ; i++) - { - if (i < 0) - continue; - time = con_times[i % NUM_CON_TIMES]; - if (time == 0) - continue; - time = realtime - time; - if (time > con_notifytime.value) - continue; - text = con->text + (i % con_totallines)*con_linewidth; - - clearnotify = 0; - scr_copytop = 1; - - for (x = 0 ; x < con_linewidth ; x++) - Draw_Character ( (x+1)<<3, v, text[x]); - - v += 8; - } - - - if (key_dest == key_message) - { - char temp[MAXCMDLINE+1]; - - clearnotify = 0; - scr_copytop = 1; - - if (chat_team) { - Draw_String (8, v, "say_team:"); - skip = 11; - } else { - Draw_String (8, v, "say:"); - skip = 5; - } - - // FIXME: clean this up - - s = strcpy (temp, chat_buffer); - - // add the cursor frame - if ( (int)(noscale_realtime*con_cursorspeed) & 1 ) { - if (chat_linepos == strlen(s)) - s[chat_linepos+1] = '\0'; - s[chat_linepos] = 11; - } - - // prestep if horizontally scrolling - if (chat_linepos + skip >= (vid.width>>3)) - s += 1 + chat_linepos + skip - (vid.width>>3); - - x = 0; - while (s[x] && x+skip < (vid.width>>3)) - { - Draw_Character ( (x+skip)<<3, v, s[x]); - x++; - } - v += 8; - } - - if (v > con_notifylines) - con_notifylines = v; -} - -/* -================ -Con_DrawConsole - -Draws the console with the solid background -================ -*/ -void Con_DrawConsole (int lines) -{ - int i, j, x, y, n; - int rows; - char *text; - int row; - char dlbar[1024]; - - if (lines <= 0) - return; - -// draw the background - Draw_ConsoleBackground (lines); - -// draw the text - con_vislines = lines; - -// changed to line things up better - rows = (lines-22)>>3; // rows of text to draw - - y = lines - 30; - - row = con->display; - -// draw from the bottom up - if (con->display != con->current) - { - // draw arrows to show the buffer is backscrolled - for (x=0 ; xcurrent - row >= con_totallines) - break; // past scrollback wrap point - - text = con->text + (row % con_totallines)*con_linewidth; - - for (x=0 ; x i) { - y = x - i - 11; - Q_strncpyz (dlbar, text, i+1); - strcat(dlbar, "..."); - } else - strcpy(dlbar, text); - strcat(dlbar, ": "); - i = strlen(dlbar); - dlbar[i++] = '\x80'; - // where's the dot go? - if (cls.downloadpercent == 0) - n = 0; - else - n = y * cls.downloadpercent / 100; - - for (j = 0; j < y; j++) - if (j == n) - dlbar[i++] = '\x83'; - else - dlbar[i++] = '\x81'; - dlbar[i++] = '\x82'; - dlbar[i] = 0; - - sprintf(dlbar + strlen(dlbar), " %02d%%", cls.downloadpercent); - - // draw it - y = con_vislines-22 + 8; - for (i = 0; i < strlen(dlbar); i++) - Draw_Character ( (i+1)<<3, y, dlbar[i]); - } - - -// draw the input prompt, user text, and cursor if desired - Con_DrawInput (); -} - - -/* -================== -Con_NotifyBox -================== -*/ -void Con_NotifyBox (char *text) -{ - double t1, t2; - -// during startup for sound / cd warnings - Con_Printf("\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n"); - - Con_Printf (text); - - Con_Printf ("Press a key.\n"); - Con_Printf("\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n"); - - key_count = -2; // wait for a key down and up - key_dest = key_console; - - do - { - t1 = Sys_DoubleTime (); - SCR_UpdateScreen (); - Sys_SendKeyEvents (); - t2 = Sys_DoubleTime (); - realtime += t2-t1; // make the cursor blink - noscale_realtime += t2-t1; - } while (key_count < 0); - - Con_Printf ("\n"); - key_dest = key_game; - realtime = 0; // put the cursor back to invisible -} - - -/* -================== -Con_SafePrintf - -Okay to call even when the screen can't be updated -================== -*/ -void Con_SafePrintf (char *fmt, ...) -{ - va_list argptr; - char msg[1024]; - int temp; - - va_start (argptr,fmt); - vsprintf (msg,fmt,argptr); - va_end (argptr); - - temp = scr_disabled_for_loading; - scr_disabled_for_loading = true; - Con_Printf ("%s", msg); - scr_disabled_for_loading = temp; -} - +/* +Copyright (C) 1996-1997 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. + +*/ +// console.c + +#include "quakedef.h" +#include "keys.h" + +int con_ormask; +console_t con_main; +console_t con_chat; +console_t *con; // point to either con_main or con_chat + +int con_linewidth; // characters across screen +int con_totallines; // total lines in console scrollback + +float con_cursorspeed = 4; + + +cvar_t con_notifytime = {"con_notifytime","3"}; //seconds + +#define NUM_CON_TIMES 4 +float con_times[NUM_CON_TIMES]; // realtime time the line was generated + // for transparent notify lines + +int con_vislines; +int con_notifylines; // scan lines to clear for notify lines + +qboolean con_debuglog; + +#define MAXCMDLINE 256 +extern char key_lines[32][MAXCMDLINE]; +extern int edit_line; +extern int key_linepos; + + +qboolean con_initialized; + + +void Key_ClearTyping (void) +{ + key_lines[edit_line][1] = 0; // clear any typing + key_linepos = 1; +} + +/* +================ +Con_ToggleConsole_f +================ +*/ +void Con_ToggleConsole_f (void) +{ + Key_ClearTyping (); + + if (key_dest == key_console) + { + if (cls.state == ca_active) + key_dest = key_game; + } + else + key_dest = key_console; + + Con_ClearNotify (); +} + +/* +================ +Con_ToggleChat_f +================ +*/ +void Con_ToggleChat_f (void) +{ + Key_ClearTyping (); + + if (key_dest == key_console) + { + if (cls.state == ca_active) + key_dest = key_game; + } + else + key_dest = key_console; + + Con_ClearNotify (); +} + +/* +================ +Con_Clear_f +================ +*/ +void Con_Clear_f (void) +{ + con_main.numlines = 0; + con_chat.numlines = 0; + memset (con_main.text, ' ', CON_TEXTSIZE); + memset (con_chat.text, ' ', CON_TEXTSIZE); + con_main.display = con_main.current; +} + + +/* +================ +Con_ClearNotify +================ +*/ +void Con_ClearNotify (void) +{ + int i; + + for (i=0 ; i> 3) - 2; + + if (width == con_linewidth) + return; + + if (width < 1) // video hasn't been initialized yet + { + width = 38; + con_linewidth = width; + con_totallines = CON_TEXTSIZE / con_linewidth; + memset (con->text, ' ', CON_TEXTSIZE); + } + else + { + oldwidth = con_linewidth; + con_linewidth = width; + oldtotallines = con_totallines; + con_totallines = CON_TEXTSIZE / con_linewidth; + numlines = oldtotallines; + + if (con_totallines < numlines) + numlines = con_totallines; + + numchars = oldwidth; + + if (con_linewidth < numchars) + numchars = con_linewidth; + + memcpy (tbuf, con->text, CON_TEXTSIZE); + memset (con->text, ' ', CON_TEXTSIZE); + + for (i=0 ; itext[(con_totallines - 1 - i) * con_linewidth + j] = + tbuf[((con->current - i + oldtotallines) % + oldtotallines) * oldwidth + j]; + } + } + + Con_ClearNotify (); + } + + con->current = con_totallines - 1; + con->display = con->current; +} + + +/* +================ +Con_CheckResize + +If the line width has changed, reformat the buffer. +================ +*/ +void Con_CheckResize (void) +{ + Con_Resize (&con_main); + Con_Resize (&con_chat); +} + + +/* +================ +Con_Init +================ +*/ +void Con_Init (void) +{ + con_debuglog = COM_CheckParm("-condebug"); + + con = &con_main; + con_linewidth = -1; + Con_CheckResize (); + + Con_Printf ("Console initialized.\n"); + +// +// register our commands +// + Cvar_RegisterVariable (&con_notifytime); + + Cmd_AddCommand ("toggleconsole", Con_ToggleConsole_f); + Cmd_AddCommand ("togglechat", Con_ToggleChat_f); + Cmd_AddCommand ("messagemode", Con_MessageMode_f); + Cmd_AddCommand ("messagemode2", Con_MessageMode2_f); + Cmd_AddCommand ("clear", Con_Clear_f); + con_initialized = true; +} + + +/* +=============== +Con_Linefeed +=============== +*/ +void Con_Linefeed (void) +{ + con->x = 0; + if (con->display == con->current) + con->display++; + con->current++; + if (con->numlines < con_totallines) + con->numlines++; + memset (&con->text[(con->current%con_totallines)*con_linewidth] + , ' ', con_linewidth); +} + +/* +================ +Con_Print + +Handles cursor positioning, line wrapping, etc +All console printing must go through this in order to be logged to disk +If no console is visible, the notify window will pop up. +================ +*/ +void Con_Print (char *txt) +{ + int y; + int c, l; + static int cr; + int mask; + + if (txt[0] == 1 || txt[0] == 2) + { + mask = 128; // go to colored text + txt++; + } + else + mask = 0; + + + while ( (c = *txt) ) + { + // count word length + for (l=0 ; l< con_linewidth ; l++) + if ( txt[l] <= ' ') + break; + + // word wrap + if (l != con_linewidth && (con->x + l > con_linewidth) ) + con->x = 0; + + txt++; + + if (cr) + { + con->current--; + cr = false; + } + + + if (!con->x) + { + Con_Linefeed (); + // mark time for transparent overlay + if (con->current >= 0) + con_times[con->current % NUM_CON_TIMES] = realtime; + } + + switch (c) + { + case '\n': + con->x = 0; + break; + + case '\r': + con->x = 0; + cr = 1; + break; + + default: // display character and advance + y = con->current % con_totallines; + con->text[y*con_linewidth+con->x] = c | mask | con_ormask; + con->x++; + if (con->x >= con_linewidth) + con->x = 0; + break; + } + + } +} + + +/* +================ +Con_Printf + +Handles cursor positioning, line wrapping, etc +================ +*/ +#define MAXPRINTMSG 4096 +// FIXME: make a buffer size safe vsprintf? +void Con_Printf (char *fmt, ...) +{ + va_list argptr; + char msg[MAXPRINTMSG]; + + va_start (argptr,fmt); + vsprintf (msg,fmt,argptr); + va_end (argptr); + +// also echo to debugging console + Sys_Printf ("%s", msg); // also echo to debugging console + +// log all messages to file + if (con_debuglog) + Sys_DebugLog(va("%s/qconsole.log",com_gamedir), "%s", msg); + + if (!con_initialized) + return; + +// write it to the scrollable buffer + Con_Print (msg); + +#if 0 // Tonik +// update the screen immediately if the console is displayed + if (cls.state != ca_active) + { + static qboolean inupdate; + + // protect against infinite loop if something in SCR_UpdateScreen calls + // Con_Printd + if (!inupdate) + { + inupdate = true; + SCR_UpdateScreen (); + inupdate = false; + } + } +#endif // Tonik +} + +/* +================ +Con_DPrintf + +A Con_Printf that only shows up if the "developer" cvar is set +================ +*/ +void Con_DPrintf (char *fmt, ...) +{ + va_list argptr; + char msg[MAXPRINTMSG]; + + if (!developer.value) + return; // don't confuse non-developers with techie stuff... + + va_start (argptr,fmt); + vsprintf (msg,fmt,argptr); + va_end (argptr); + + Con_Printf ("%s", msg); +} + +/* +============================================================================== + +DRAWING + +============================================================================== +*/ + + +/* +================ +Con_DrawInput + +The input line scrolls horizontally if typing goes beyond the right edge +================ +*/ +void Con_DrawInput (void) +{ + int y; + int i; + char *text; + char temp[MAXCMDLINE]; + + if (key_dest != key_console && cls.state == ca_active) + return; // don't draw anything (always draw if not active) + + text = strcpy (temp, key_lines[edit_line]); + +// fill out remainder with spaces + for (i=strlen(text) ; i < MAXCMDLINE ; i++) + text[i] = ' '; + +// add the cursor frame + if ( (int)(noscale_realtime*con_cursorspeed) & 1 ) + text[key_linepos] = 11; + +// prestep if horizontally scrolling + if (key_linepos >= con_linewidth) + text += 1 + key_linepos - con_linewidth; + +// draw it + y = con_vislines-22; + + for (i=0 ; icurrent-NUM_CON_TIMES+1 ; i<=con->current ; i++) + { + if (i < 0) + continue; + time = con_times[i % NUM_CON_TIMES]; + if (time == 0) + continue; + time = realtime - time; + if (time > con_notifytime.value) + continue; + text = con->text + (i % con_totallines)*con_linewidth; + + clearnotify = 0; + scr_copytop = 1; + + for (x = 0 ; x < con_linewidth ; x++) + Draw_Character ( (x+1)<<3, v, text[x]); + + v += 8; + } + + + if (key_dest == key_message) + { + char temp[MAXCMDLINE+1]; + + clearnotify = 0; + scr_copytop = 1; + + if (chat_team) { + Draw_String (8, v, "say_team:"); + skip = 11; + } else { + Draw_String (8, v, "say:"); + skip = 5; + } + + // FIXME: clean this up + + s = strcpy (temp, chat_buffer); + + // add the cursor frame + if ( (int)(noscale_realtime*con_cursorspeed) & 1 ) { + if (chat_linepos == strlen(s)) + s[chat_linepos+1] = '\0'; + s[chat_linepos] = 11; + } + + // prestep if horizontally scrolling + if (chat_linepos + skip >= (vid.width>>3)) + s += 1 + chat_linepos + skip - (vid.width>>3); + + x = 0; + while (s[x] && x+skip < (vid.width>>3)) + { + Draw_Character ( (x+skip)<<3, v, s[x]); + x++; + } + v += 8; + } + + if (v > con_notifylines) + con_notifylines = v; +} + +/* +================ +Con_DrawConsole + +Draws the console with the solid background +================ +*/ +void Con_DrawConsole (int lines) +{ + int i, j, x, y, n; + int rows; + char *text; + int row; + char dlbar[1024]; + + if (lines <= 0) + return; + +// draw the background + Draw_ConsoleBackground (lines); + +// draw the text + con_vislines = lines; + +// changed to line things up better + rows = (lines-22)>>3; // rows of text to draw + + y = lines - 30; + + row = con->display; + +// draw from the bottom up + if (con->display != con->current) + { + // draw arrows to show the buffer is backscrolled + for (x=0 ; xcurrent - row >= con_totallines) + break; // past scrollback wrap point + + text = con->text + (row % con_totallines)*con_linewidth; + + for (x=0 ; x i) { + y = x - i - 11; + Q_strncpyz (dlbar, text, i+1); + strcat(dlbar, "..."); + } else + strcpy(dlbar, text); + strcat(dlbar, ": "); + i = strlen(dlbar); + dlbar[i++] = '\x80'; + // where's the dot go? + if (cls.downloadpercent == 0) + n = 0; + else + n = y * cls.downloadpercent / 100; + + for (j = 0; j < y; j++) + if (j == n) + dlbar[i++] = '\x83'; + else + dlbar[i++] = '\x81'; + dlbar[i++] = '\x82'; + dlbar[i] = 0; + + sprintf(dlbar + strlen(dlbar), " %02d%%", cls.downloadpercent); + + // draw it + y = con_vislines-22 + 8; + for (i = 0; i < strlen(dlbar); i++) + Draw_Character ( (i+1)<<3, y, dlbar[i]); + } + + +// draw the input prompt, user text, and cursor if desired + Con_DrawInput (); +} + + +/* +================== +Con_NotifyBox +================== +*/ +void Con_NotifyBox (char *text) +{ + double t1, t2; + +// during startup for sound / cd warnings + Con_Printf("\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n"); + + Con_Printf (text); + + Con_Printf ("Press a key.\n"); + Con_Printf("\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n"); + + key_count = -2; // wait for a key down and up + key_dest = key_console; + + do + { + t1 = Sys_DoubleTime (); + SCR_UpdateScreen (); + Sys_SendKeyEvents (); + t2 = Sys_DoubleTime (); + realtime += t2-t1; // make the cursor blink + noscale_realtime += t2-t1; + } while (key_count < 0); + + Con_Printf ("\n"); + key_dest = key_game; + realtime = 0; // put the cursor back to invisible +} + + +/* +================== +Con_SafePrintf + +Okay to call even when the screen can't be updated +================== +*/ +void Con_SafePrintf (char *fmt, ...) +{ + va_list argptr; + char msg[1024]; + int temp; + + va_start (argptr,fmt); + vsprintf (msg,fmt,argptr); + va_end (argptr); + + temp = scr_disabled_for_loading; + scr_disabled_for_loading = true; + Con_Printf ("%s", msg); + scr_disabled_for_loading = temp; +} + diff --git a/source/console.h b/source/console.h index 4552f6f7..cd59565b 100644 --- a/source/console.h +++ b/source/console.h @@ -1,61 +1,61 @@ -/* -Copyright (C) 1996-1997 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. - -*/ - -// -// console -// - -#define CON_TEXTSIZE 16384 -typedef struct -{ - char text[CON_TEXTSIZE]; - int current; // line where next message will be printed - int x; // offset in current line for next print - int display; // bottom of console displays this line - int numlines; // number of non-blank text lines, used for backscroling -} console_t; - -extern console_t con_main; -extern console_t con_chat; -extern console_t *con; // point to either con_main or con_chat - -extern int con_ormask; - -extern int con_totallines; -extern qboolean con_initialized; -extern byte *con_chars; -extern int con_notifylines; // scan lines to clear for notify lines - -void Con_DrawCharacter (int cx, int line, int num); - -void Con_CheckResize (void); -void Con_Init (void); -void Con_DrawConsole (int lines); -void Con_Print (char *txt); -void Con_Printf (char *fmt, ...); -void Con_DPrintf (char *fmt, ...); -void Con_SafePrintf (char *fmt, ...); -void Con_Clear_f (void); -void Con_DrawNotify (void); -void Con_ClearNotify (void); -void Con_ToggleConsole_f (void); - -void Con_NotifyBox (char *text); // during startup for sound / cd warnings - +/* +Copyright (C) 1996-1997 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. + +*/ + +// +// console +// + +#define CON_TEXTSIZE 16384 +typedef struct +{ + char text[CON_TEXTSIZE]; + int current; // line where next message will be printed + int x; // offset in current line for next print + int display; // bottom of console displays this line + int numlines; // number of non-blank text lines, used for backscroling +} console_t; + +extern console_t con_main; +extern console_t con_chat; +extern console_t *con; // point to either con_main or con_chat + +extern int con_ormask; + +extern int con_totallines; +extern qboolean con_initialized; +extern byte *con_chars; +extern int con_notifylines; // scan lines to clear for notify lines + +void Con_DrawCharacter (int cx, int line, int num); + +void Con_CheckResize (void); +void Con_Init (void); +void Con_DrawConsole (int lines); +void Con_Print (char *txt); +void Con_Printf (char *fmt, ...); +void Con_DPrintf (char *fmt, ...); +void Con_SafePrintf (char *fmt, ...); +void Con_Clear_f (void); +void Con_DrawNotify (void); +void Con_ClearNotify (void); +void Con_ToggleConsole_f (void); + +void Con_NotifyBox (char *text); // during startup for sound / cd warnings + diff --git a/source/crc.c b/source/crc.c index 7ff34685..a43712ef 100644 --- a/source/crc.c +++ b/source/crc.c @@ -1,93 +1,93 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -/* crc.c */ - -#include "quakedef.h" -#include "crc.h" - -// this is a 16 bit, non-reflected CRC using the polynomial 0x1021 -// and the initial and final xor values shown below... in other words, the -// CCITT standard CRC used by XMODEM - -#define CRC_INIT_VALUE 0xffff -#define CRC_XOR_VALUE 0x0000 - -static unsigned short crctable[256] = -{ - 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, - 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, - 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, - 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, - 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, - 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, - 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, - 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, - 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, - 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, - 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, - 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, - 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, - 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, - 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, - 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, - 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, - 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, - 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, - 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, - 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, - 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, - 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, - 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, - 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, - 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, - 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, - 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, - 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, - 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, - 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, - 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 -}; - -void CRC_Init(unsigned short *crcvalue) -{ - *crcvalue = CRC_INIT_VALUE; -} - -void CRC_ProcessByte(unsigned short *crcvalue, byte data) -{ - *crcvalue = (*crcvalue << 8) ^ crctable[(*crcvalue >> 8) ^ data]; -} - -unsigned short CRC_Value(unsigned short crcvalue) -{ - return crcvalue ^ CRC_XOR_VALUE; -} - -unsigned short CRC_Block (byte *start, int count) -{ - unsigned short crc; - - CRC_Init (&crc); - while (count--) - crc = (crc << 8) ^ crctable[(crc >> 8) ^ *start++]; - - return crc; -} - +/* +Copyright (C) 1996-1997 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. + +*/ +/* crc.c */ + +#include "quakedef.h" +#include "crc.h" + +// this is a 16 bit, non-reflected CRC using the polynomial 0x1021 +// and the initial and final xor values shown below... in other words, the +// CCITT standard CRC used by XMODEM + +#define CRC_INIT_VALUE 0xffff +#define CRC_XOR_VALUE 0x0000 + +static unsigned short crctable[256] = +{ + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, + 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, + 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, + 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, + 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, + 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, + 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, + 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, + 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, + 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, + 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, + 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, + 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, + 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, + 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, + 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, + 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, + 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, + 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, + 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, + 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, + 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, + 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 +}; + +void CRC_Init(unsigned short *crcvalue) +{ + *crcvalue = CRC_INIT_VALUE; +} + +void CRC_ProcessByte(unsigned short *crcvalue, byte data) +{ + *crcvalue = (*crcvalue << 8) ^ crctable[(*crcvalue >> 8) ^ data]; +} + +unsigned short CRC_Value(unsigned short crcvalue) +{ + return crcvalue ^ CRC_XOR_VALUE; +} + +unsigned short CRC_Block (byte *start, int count) +{ + unsigned short crc; + + CRC_Init (&crc); + while (count--) + crc = (crc << 8) ^ crctable[(crc >> 8) ^ *start++]; + + return crc; +} + diff --git a/source/crc.h b/source/crc.h index d38b37c2..d01fa3c4 100644 --- a/source/crc.h +++ b/source/crc.h @@ -1,25 +1,25 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -/* crc.h */ - -void CRC_Init(unsigned short *crcvalue); -void CRC_ProcessByte(unsigned short *crcvalue, byte data); -unsigned short CRC_Value(unsigned short crcvalue); -unsigned short CRC_Block (byte *start, int count); +/* +Copyright (C) 1996-1997 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. + +*/ +/* crc.h */ + +void CRC_Init(unsigned short *crcvalue); +void CRC_ProcessByte(unsigned short *crcvalue, byte data); +unsigned short CRC_Value(unsigned short crcvalue); +unsigned short CRC_Block (byte *start, int count); diff --git a/source/cvar.c b/source/cvar.c index 3c728e3d..329b3245 100644 --- a/source/cvar.c +++ b/source/cvar.c @@ -1,552 +1,553 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// cvar.c -- dynamic variable tracking - -#ifdef SERVERONLY -#include "qwsvdef.h" -#else -#include "quakedef.h" -#endif - -static cvar_t *cvar_hash[32]; -static cvar_t *cvar_vars; -static char *cvar_null_string = ""; - - -/* -========== -Key -========== -Returns hash key for a string -*/ -static int Key (char *name) -{ - int v; - int c; - - v = 0; - while ( (c = *name++) != 0 ) -// v += *name; - v += c &~ 32; // very lame, but works (case insensitivity) - - return v % 32; -} - -/* -============ -Cvar_FindVar -============ -*/ -cvar_t *Cvar_FindVar (char *var_name) -{ - cvar_t *var; - int key; - - key = Key (var_name); - - for (var=cvar_hash[key] ; var ; var=var->hash_next) - if (!Q_strcasecmp (var_name, var->name)) - return var; - - return NULL; -} - -/* -============ -Cvar_VariableValue -============ -*/ -float Cvar_VariableValue (char *var_name) -{ - cvar_t *var; - - var = Cvar_FindVar (var_name); - if (!var) - return 0; - return Q_atof (var->string); -} - - -/* -============ -Cvar_VariableString -============ -*/ -char *Cvar_VariableString (char *var_name) -{ - cvar_t *var; - - var = Cvar_FindVar (var_name); - if (!var) - return cvar_null_string; - return var->string; -} - - -/* -============ -Cvar_CompleteVariable -============ -*/ -char *Cvar_CompleteVariable (char *partial) -{ - cvar_t *cvar; - int len; - - len = strlen(partial); - - if (!len) - return NULL; - - // check exact match - for (cvar=cvar_vars ; cvar ; cvar=cvar->next) - if (!Q_strcasecmp (partial,cvar->name)) - return cvar->name; - - // check partial match - for (cvar=cvar_vars ; cvar ; cvar=cvar->next) - if (!Q_strncasecmp (partial,cvar->name, len)) - return cvar->name; - - return NULL; -} - - -#ifdef SERVERONLY -void SV_SendServerInfoChange(char *key, char *value); -#else -void TP_FixTeamSets(); -#endif - - -/* -============ -Cvar_Set -============ -*/ -void Cvar_Set (cvar_t *var, char *value) -{ - static qboolean changing = false; - - if (!var) - return; - - if (var->flags & CVAR_ROM) - return; - - if (var->OnChange && !changing) { - changing = true; - if (var->OnChange(var, value)) { - changing = false; - return; - } - changing = false; - } - - Z_Free (var->string); // free the old value string - - var->string = Z_Malloc (strlen(value)+1); - strcpy (var->string, value); - var->value = Q_atof (var->string); - -#if defined(SERVERONLY) - if (var->flags & CVAR_SERVERINFO) - { - if (strcmp(var->string, Info_ValueForKey (svs.info, var->name))) - { - Info_SetValueForKey (svs.info, var->name, var->string, MAX_SERVERINFO_STRING); - SV_SendServerInfoChange(var->name, var->string); - } - } -#else - if (var->flags & CVAR_USERINFO) - { - Info_SetValueForKey (cls.userinfo, var->name, var->string, MAX_INFO_STRING); - if (cls.state >= ca_connected) - { - MSG_WriteByte (&cls.netchan.message, clc_stringcmd); - SZ_Print (&cls.netchan.message, va("setinfo \"%s\" \"%s\"\n", var->name, var->string)); - } - } - if (var == &cl_teamskin || var == &cl_enemyskin) - TP_FixTeamSets(); -#endif -} - -/* -============ -Cvar_SetROM -============ -*/ -void Cvar_SetROM (cvar_t *var, char *value) -{ - int saved_flags; - - if (!var) - return; - - saved_flags = var->flags; - var->flags &= ~CVAR_ROM; - Cvar_Set (var, value); - var->flags = saved_flags; -} - - -/* -============ -Cvar_SetValue -============ -*/ -void Cvar_SetValue (cvar_t *var, float value) -{ - char val[32]; - int i; - - sprintf (val, "%f", value); - for (i=strlen(val)-1 ; i>0 && val[i]=='0' ; i--) - val[i] = 0; - if (val[i] == '.') - val[i] = 0; - Cvar_Set (var, val); -} - - -/* -============ -Cvar_RegisterVariable - -Adds a freestanding variable to the variable list. -============ -*/ -void Cvar_RegisterVariable (cvar_t *variable) -{ - char value[512]; - int key; - -// first check to see if it has already been defined - if (Cvar_FindVar (variable->name)) - { - Con_Printf ("Can't register variable %s, already defined\n", variable->name); - return; - } - -// check for overlap with a command - if (Cmd_Exists (variable->name)) - { - Con_Printf ("Cvar_RegisterVariable: %s is a command\n", variable->name); - return; - } - -// link the variable in - key = Key (variable->name); - variable->hash_next = cvar_hash[key]; - cvar_hash[key] = variable; - variable->next = cvar_vars; - cvar_vars = variable; - -// copy the value off, because future sets will Z_Free it - strcpy (value, variable->string); - variable->string = Z_Malloc (1); - -// set it through the function to be consistent - Cvar_SetROM (variable, value); -} - - -/* -============ -Cvar_Command - -Handles variable inspection and changing from the console -============ -*/ -qboolean Cvar_Command (void) -{ - int i, c; - cvar_t *v; - char string[1024]; - -// check variables - v = Cvar_FindVar (Cmd_Argv(0)); - if (!v) - return false; - -// perform a variable print or set - c = Cmd_Argc(); - if (c == 1) - { - Con_Printf ("\"%s\" is \"%s\"\n", v->name, v->string); - return true; - } - - string[0] = 0; - for (i=1 ; i < c ; i++) - { - if (i > 1) - strcat (string, " "); - strcat (string, Cmd_Argv(i)); - } - - Cvar_Set (v, string); - return true; -} - - -/* -============ -Cvar_WriteVariables - -Writes lines containing "set variable value" for all variables -with the archive flag set to true. -============ -*/ -void Cvar_WriteVariables (FILE *f) -{ - cvar_t *var; - - for (var = cvar_vars ; var ; var = var->next) - if (var->flags & CVAR_ARCHIVE) - fprintf (f, "%s \"%s\"\n", var->name, var->string); -} - - -/* -============= -Cvar_Toggle_f -============= -*/ -void Cvar_Toggle_f (void) -{ - cvar_t *var; - - if (Cmd_Argc() != 2) - { - Con_Printf ("toggle : toggle a cvar on/off\n"); - return; - } - - var = Cvar_FindVar (Cmd_Argv(1)); - if (!var) - { - Con_Printf ("Unknown variable \"%s\"\n", Cmd_Argv(1)); - return; - } - - Cvar_Set (var, var->value ? "0" : "1"); -} - -/* -=============== -Cvar_CvarList_f -=============== -List all cvars -TODO: allow cvar name mask as a parameter, e.g. cvarlist cl_* -*/ -void Cvar_CvarList_f (void) -{ - cvar_t *var; - int i; - - for (var=cvar_vars, i=0 ; var ; var=var->next, i++) - Con_Printf("%c%c%c %s\n", - var->flags & CVAR_ARCHIVE ? '*' : ' ', - var->flags & CVAR_USERINFO ? 'u' : ' ', - var->flags & CVAR_SERVERINFO ? 's' : ' ', - var->name); - - Con_Printf ("------------\n%d variables\n", i); -} - -/* -=========== -Cvar_Create -=========== -*/ -cvar_t *Cvar_Create (char *name, char *string, int cvarflags) -{ - cvar_t *v; - int key; - - v = Cvar_FindVar(name); - if (v) - return v; - v = (cvar_t *) Z_Malloc(sizeof(cvar_t)); - // Cvar doesn't exist, so we create it - v->next = cvar_vars; - cvar_vars = v; - - key = Key (name); - v->hash_next = cvar_hash[key]; - cvar_hash[key] = v; - - v->name = Z_Malloc(strlen(name)+1); - strcpy (v->name, name); - v->string = Z_Malloc (strlen(string)+1); - strcpy (v->string, string); - v->flags = cvarflags; - v->value = Q_atof (v->string); - - return v; -} - -/* -=========== -Cvar_Delete -=========== -returns true if the cvar was found (and deleted) -*/ -qboolean Cvar_Delete (char *name) -{ - cvar_t *var, *prev; - int key; - - key = Key (name); - - prev = NULL; - for (var = cvar_hash[key] ; var ; var=var->hash_next) - { - if (!Q_strcasecmp(var->name, name)) { - // unlink from hash - if (prev) - prev->hash_next = var->next; - else - cvar_hash[key] = var->next; - break; - } - prev = var; - } - - if (!var) - return false; - - prev = NULL; - for (var = cvar_vars ; var ; var=var->next) - { - if (!Q_strcasecmp(var->name, name)) { - // unlink from cvar list - if (prev) - prev->next = var->next; - else - cvar_vars = var->next; - - // free - Z_Free (var->string); - Z_Free (var->name); - Z_Free (var); - return true; - } - prev = var; - } - - Sys_Error ("Cvar list broken"); - return false; // shut up compiler -} - - -void Cvar_Set_f (void) -{ - cvar_t *var; - char *var_name; - - if (Cmd_Argc() != 3) - { - Con_Printf ("usage: set \n"); - return; - } - - var_name = Cmd_Argv (1); - var = Cvar_FindVar (var_name); - - if (var) - { - Cvar_Set (var, Cmd_Argv(2)); - } - else - { - if (Cmd_Exists(var_name)) - { - Con_Printf ("\"%s\" is a command\n", var_name); - return; - } - -#if 0 - // delete alias with the same name if it exists - Cmd_DeleteAlias (var_name); -#endif - - var = Cvar_Create (var_name, Cmd_Argv(2), CVAR_USER_CREATED); - } -} - -void Cvar_Inc_f (void) -{ - int c; - cvar_t *var; - float delta; - - c = Cmd_Argc(); - if (c != 2 && c != 3) { - Con_Printf ("inc [value]\n"); - return; - } - - var = Cvar_FindVar (Cmd_Argv(1)); - if (!var) { - Con_Printf ("Unknown variable \"%s\"\n", Cmd_Argv(1)); - return; - } - - if (c == 3) - delta = atof (Cmd_Argv(2)); - else - delta = 1; - - Cvar_SetValue (var, var->value + delta); -} - -//#define CVAR_DEBUG -#ifdef CVAR_DEBUG -static void Cvar_Hash_Print_f (void) -{ - int i, count; - cvar_t *cvar; - - Con_Printf ("Cvar hash:\n"); - for (i = 0; i<32; i++) - { - count = 0; - for (cvar = cvar_hash[i]; cvar; cvar=cvar->hash_next, count++); - Con_Printf ("%i: %i\n", i, count); - } - -} -#endif - -void Cvar_Init (void) -{ - Cmd_AddCommand ("cvarlist", Cvar_CvarList_f); - Cmd_AddCommand ("toggle", Cvar_Toggle_f); - Cmd_AddCommand ("set", Cvar_Set_f); - Cmd_AddCommand ("inc", Cvar_Inc_f); - -#ifdef CVAR_DEBUG - Cmd_AddCommand ("cvar_hash_print", Cvar_Hash_Print_f); -#endif -} +/* +Copyright (C) 1996-1997 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. + +*/ +// cvar.c -- dynamic variable tracking + +#ifdef SERVERONLY +#include "qwsvdef.h" +#else +#include "quakedef.h" +#endif + +static cvar_t *cvar_hash[32]; +static cvar_t *cvar_vars; +static char *cvar_null_string = ""; + + +/* +========== +Key +========== +Returns hash key for a string +*/ +static int Key (char *name) +{ + int v; + int c; + + v = 0; + while ( (c = *name++) != 0 ) +// v += *name; + v += c &~ 32; // very lame, but works (case insensitivity) + + return v % 32; +} + +/* +============ +Cvar_FindVar +============ +*/ +cvar_t *Cvar_FindVar (char *var_name) +{ + cvar_t *var; + int key; + + key = Key (var_name); + + for (var=cvar_hash[key] ; var ; var=var->hash_next) + if (!Q_strcasecmp (var_name, var->name)) + return var; + + return NULL; +} + +/* +============ +Cvar_VariableValue +============ +*/ +float Cvar_VariableValue (char *var_name) +{ + cvar_t *var; + + var = Cvar_FindVar (var_name); + if (!var) + return 0; + return Q_atof (var->string); +} + + +/* +============ +Cvar_VariableString +============ +*/ +char *Cvar_VariableString (char *var_name) +{ + cvar_t *var; + + var = Cvar_FindVar (var_name); + if (!var) + return cvar_null_string; + return var->string; +} + + +/* +============ +Cvar_CompleteVariable +============ +*/ +char *Cvar_CompleteVariable (char *partial) +{ + cvar_t *cvar; + int len; + + len = strlen(partial); + + if (!len) + return NULL; + + // check exact match + for (cvar=cvar_vars ; cvar ; cvar=cvar->next) + if (!Q_strcasecmp (partial,cvar->name)) + return cvar->name; + + // check partial match + for (cvar=cvar_vars ; cvar ; cvar=cvar->next) + if (!Q_strncasecmp (partial,cvar->name, len)) + return cvar->name; + + return NULL; +} + + +#ifdef SERVERONLY +void SV_SendServerInfoChange(char *key, char *value); +#else +void TP_FixTeamSets(); +#endif + + +/* +============ +Cvar_Set +============ +*/ +void Cvar_Set (cvar_t *var, char *value) +{ + static qboolean changing = false; + + if (!var) + return; + + if (var->flags & CVAR_ROM) + return; + + if (var->OnChange && !changing) { + changing = true; + if (var->OnChange(var, value)) { + changing = false; + return; + } + changing = false; + } + + Z_Free (var->string); // free the old value string + + var->string = Z_Malloc (strlen(value)+1); + strcpy (var->string, value); + var->value = Q_atof (var->string); + +#if defined(SERVERONLY) + if (var->flags & CVAR_SERVERINFO) + { + if (strcmp(var->string, Info_ValueForKey (svs.info, var->name))) + { + Info_SetValueForKey (svs.info, var->name, var->string, MAX_SERVERINFO_STRING); + SV_SendServerInfoChange(var->name, var->string); + } + } + +#else + if (var->flags & CVAR_USERINFO) + { + Info_SetValueForKey (cls.userinfo, var->name, var->string, MAX_INFO_STRING); + if (cls.state >= ca_connected) + { + MSG_WriteByte (&cls.netchan.message, clc_stringcmd); + SZ_Print (&cls.netchan.message, va("setinfo \"%s\" \"%s\"\n", var->name, var->string)); + } + } + if (var == &cl_teamskin || var == &cl_enemyskin) + TP_FixTeamSets(); +#endif +} + +/* +============ +Cvar_SetROM +============ +*/ +void Cvar_SetROM (cvar_t *var, char *value) +{ + int saved_flags; + + if (!var) + return; + + saved_flags = var->flags; + var->flags &= ~CVAR_ROM; + Cvar_Set (var, value); + var->flags = saved_flags; +} + + +/* +============ +Cvar_SetValue +============ +*/ +void Cvar_SetValue (cvar_t *var, float value) +{ + char val[32]; + int i; + + sprintf (val, "%f", value); + for (i=strlen(val)-1 ; i>0 && val[i]=='0' ; i--) + val[i] = 0; + if (val[i] == '.') + val[i] = 0; + Cvar_Set (var, val); +} + + +/* +============ +Cvar_RegisterVariable + +Adds a freestanding variable to the variable list. +============ +*/ +void Cvar_RegisterVariable (cvar_t *variable) +{ + char value[512]; + int key; + +// first check to see if it has already been defined + if (Cvar_FindVar (variable->name)) + { + Con_Printf ("Can't register variable %s, already defined\n", variable->name); + return; + } + +// check for overlap with a command + if (Cmd_Exists (variable->name)) + { + Con_Printf ("Cvar_RegisterVariable: %s is a command\n", variable->name); + return; + } + +// link the variable in + key = Key (variable->name); + variable->hash_next = cvar_hash[key]; + cvar_hash[key] = variable; + variable->next = cvar_vars; + cvar_vars = variable; + +// copy the value off, because future sets will Z_Free it + strcpy (value, variable->string); + variable->string = Z_Malloc (1); + +// set it through the function to be consistent + Cvar_SetROM (variable, value); +} + + +/* +============ +Cvar_Command + +Handles variable inspection and changing from the console +============ +*/ +qboolean Cvar_Command (void) +{ + int i, c; + cvar_t *v; + char string[1024]; + +// check variables + v = Cvar_FindVar (Cmd_Argv(0)); + if (!v) + return false; + +// perform a variable print or set + c = Cmd_Argc(); + if (c == 1) + { + Con_Printf ("\"%s\" is \"%s\"\n", v->name, v->string); + return true; + } + + string[0] = 0; + for (i=1 ; i < c ; i++) + { + if (i > 1) + strcat (string, " "); + strcat (string, Cmd_Argv(i)); + } + + Cvar_Set (v, string); + return true; +} + + +/* +============ +Cvar_WriteVariables + +Writes lines containing "set variable value" for all variables +with the archive flag set to true. +============ +*/ +void Cvar_WriteVariables (FILE *f) +{ + cvar_t *var; + + for (var = cvar_vars ; var ; var = var->next) + if (var->flags & CVAR_ARCHIVE) + fprintf (f, "%s \"%s\"\n", var->name, var->string); +} + + +/* +============= +Cvar_Toggle_f +============= +*/ +void Cvar_Toggle_f (void) +{ + cvar_t *var; + + if (Cmd_Argc() != 2) + { + Con_Printf ("toggle : toggle a cvar on/off\n"); + return; + } + + var = Cvar_FindVar (Cmd_Argv(1)); + if (!var) + { + Con_Printf ("Unknown variable \"%s\"\n", Cmd_Argv(1)); + return; + } + + Cvar_Set (var, var->value ? "0" : "1"); +} + +/* +=============== +Cvar_CvarList_f +=============== +List all cvars +TODO: allow cvar name mask as a parameter, e.g. cvarlist cl_* +*/ +void Cvar_CvarList_f (void) +{ + cvar_t *var; + int i; + + for (var=cvar_vars, i=0 ; var ; var=var->next, i++) + Con_Printf("%c%c%c %s\n", + var->flags & CVAR_ARCHIVE ? '*' : ' ', + var->flags & CVAR_USERINFO ? 'u' : ' ', + var->flags & CVAR_SERVERINFO ? 's' : ' ', + var->name); + + Con_Printf ("------------\n%d variables\n", i); +} + +/* +=========== +Cvar_Create +=========== +*/ +cvar_t *Cvar_Create (char *name, char *string, int cvarflags) +{ + cvar_t *v; + int key; + + v = Cvar_FindVar(name); + if (v) + return v; + v = (cvar_t *) Z_Malloc(sizeof(cvar_t)); + // Cvar doesn't exist, so we create it + v->next = cvar_vars; + cvar_vars = v; + + key = Key (name); + v->hash_next = cvar_hash[key]; + cvar_hash[key] = v; + + v->name = Z_Malloc(strlen(name)+1); + strcpy (v->name, name); + v->string = Z_Malloc (strlen(string)+1); + strcpy (v->string, string); + v->flags = cvarflags; + v->value = Q_atof (v->string); + + return v; +} + +/* +=========== +Cvar_Delete +=========== +returns true if the cvar was found (and deleted) +*/ +qboolean Cvar_Delete (char *name) +{ + cvar_t *var, *prev; + int key; + + key = Key (name); + + prev = NULL; + for (var = cvar_hash[key] ; var ; var=var->hash_next) + { + if (!Q_strcasecmp(var->name, name)) { + // unlink from hash + if (prev) + prev->hash_next = var->next; + else + cvar_hash[key] = var->next; + break; + } + prev = var; + } + + if (!var) + return false; + + prev = NULL; + for (var = cvar_vars ; var ; var=var->next) + { + if (!Q_strcasecmp(var->name, name)) { + // unlink from cvar list + if (prev) + prev->next = var->next; + else + cvar_vars = var->next; + + // free + Z_Free (var->string); + Z_Free (var->name); + Z_Free (var); + return true; + } + prev = var; + } + + Sys_Error ("Cvar list broken"); + return false; // shut up compiler +} + + +void Cvar_Set_f (void) +{ + cvar_t *var; + char *var_name; + + if (Cmd_Argc() != 3) + { + Con_Printf ("usage: set \n"); + return; + } + + var_name = Cmd_Argv (1); + var = Cvar_FindVar (var_name); + + if (var) + { + Cvar_Set (var, Cmd_Argv(2)); + } + else + { + if (Cmd_Exists(var_name)) + { + Con_Printf ("\"%s\" is a command\n", var_name); + return; + } + +#if 0 + // delete alias with the same name if it exists + Cmd_DeleteAlias (var_name); +#endif + + var = Cvar_Create (var_name, Cmd_Argv(2), CVAR_USER_CREATED); + } +} + +void Cvar_Inc_f (void) +{ + int c; + cvar_t *var; + float delta; + + c = Cmd_Argc(); + if (c != 2 && c != 3) { + Con_Printf ("inc [value]\n"); + return; + } + + var = Cvar_FindVar (Cmd_Argv(1)); + if (!var) { + Con_Printf ("Unknown variable \"%s\"\n", Cmd_Argv(1)); + return; + } + + if (c == 3) + delta = atof (Cmd_Argv(2)); + else + delta = 1; + + Cvar_SetValue (var, var->value + delta); +} + +//#define CVAR_DEBUG +#ifdef CVAR_DEBUG +static void Cvar_Hash_Print_f (void) +{ + int i, count; + cvar_t *cvar; + + Con_Printf ("Cvar hash:\n"); + for (i = 0; i<32; i++) + { + count = 0; + for (cvar = cvar_hash[i]; cvar; cvar=cvar->hash_next, count++); + Con_Printf ("%i: %i\n", i, count); + } + +} +#endif + +void Cvar_Init (void) +{ + Cmd_AddCommand ("cvarlist", Cvar_CvarList_f); + Cmd_AddCommand ("toggle", Cvar_Toggle_f); + Cmd_AddCommand ("set", Cvar_Set_f); + Cmd_AddCommand ("inc", Cvar_Inc_f); + +#ifdef CVAR_DEBUG + Cmd_AddCommand ("cvar_hash_print", Cvar_Hash_Print_f); +#endif +} diff --git a/source/cvar.h b/source/cvar.h index 0dfaa32c..95269e06 100644 --- a/source/cvar.h +++ b/source/cvar.h @@ -1,112 +1,112 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// cvar.h - -/* - -cvar_t variables are used to hold scalar or string variables that can be changed or displayed at the console or prog code as well as accessed directly -in C code. - -it is sufficient to initialize a cvar_t with just the first two fields, or -you can add a ,true flag for variables that you want saved to the configuration -file when the game is quit: - -cvar_t r_draworder = {"r_draworder","1"}; -cvar_t scr_screensize = {"screensize","1",CVAR_ARCHIVE}; - -Cvars must be registered before use, or they will have a 0 value instead of the float interpretation of the string. Generally, all cvar_t declarations should be registered in the apropriate init function before any console commands are executed: -Cvar_RegisterVariable (&host_framerate); - - -C code usually just references a cvar in place: -if ( r_draworder.value ) - -It could optionally ask for the value to be looked up for a string name: -if (Cvar_VariableValue ("r_draworder")) - -Interpreted prog code can access cvars with the cvar(name) or -cvar_set (name, value) internal functions: -teamplay = cvar("teamplay"); -cvar_set ("registered", "1"); - -The user can access cvars from the console in two ways: -r_draworder prints the current value -r_draworder 0 sets the current value to 0 -Cvars are restricted from having the same names as commands to keep this -interface from being ambiguous. -*/ - -// cvar flags -#define CVAR_ARCHIVE 1 -#define CVAR_USERINFO 2 // mirrored to userinfo -#define CVAR_SERVERINFO 4 // mirrored to serverinfo -#define CVAR_ROM 64 // read only -#define CVAR_USER_CREATED 128 // created by a set command - -typedef struct cvar_s -{ - char *name; - char *string; - int flags; - qboolean (*OnChange)(struct cvar_s *var, char *value); - float value; - struct cvar_s *hash_next; - struct cvar_s *next; -} cvar_t; - - -void Cvar_RegisterVariable (cvar_t *variable); -// registers a cvar that already has the name, string, and optionally the -// archive elements set. - -void Cvar_Set (cvar_t *var, char *value); -// equivalent to " " typed at the console - -void Cvar_SetROM (cvar_t *var, char *value); -// force a set even if the cvar is read only - -void Cvar_SetValue (cvar_t *var, float value); -// expands value to a string and calls Cvar_Set - -float Cvar_VariableValue (char *var_name); -// returns 0 if not defined or non numeric - -char *Cvar_VariableString (char *var_name); -// returns an empty string if not defined - -char *Cvar_CompleteVariable (char *partial); -// attempts to match a partial variable name for command line completion -// returns NULL if nothing fits - -qboolean Cvar_Command (void); -// called by Cmd_ExecuteString when Cmd_Argv(0) doesn't match a known -// command. Returns true if the command was a variable reference that -// was handled. (print or change) - -void Cvar_WriteVariables (FILE *f); -// Writes lines containing "set variable value" for all variables -// with the archive flag set to true. - -cvar_t *Cvar_FindVar (char *var_name); -qboolean Cvar_Delete (char *name); - -cvar_t *Cvar_Create (char *name, char *string, int cvarflags); - -void Cvar_Init (void); +/* +Copyright (C) 1996-1997 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. + +*/ +// cvar.h + +/* + +cvar_t variables are used to hold scalar or string variables that can be changed or displayed at the console or prog code as well as accessed directly +in C code. + +it is sufficient to initialize a cvar_t with just the first two fields, or +you can add a ,true flag for variables that you want saved to the configuration +file when the game is quit: + +cvar_t r_draworder = {"r_draworder","1"}; +cvar_t scr_screensize = {"screensize","1",CVAR_ARCHIVE}; + +Cvars must be registered before use, or they will have a 0 value instead of the float interpretation of the string. Generally, all cvar_t declarations should be registered in the apropriate init function before any console commands are executed: +Cvar_RegisterVariable (&host_framerate); + + +C code usually just references a cvar in place: +if ( r_draworder.value ) + +It could optionally ask for the value to be looked up for a string name: +if (Cvar_VariableValue ("r_draworder")) + +Interpreted prog code can access cvars with the cvar(name) or +cvar_set (name, value) internal functions: +teamplay = cvar("teamplay"); +cvar_set ("registered", "1"); + +The user can access cvars from the console in two ways: +r_draworder prints the current value +r_draworder 0 sets the current value to 0 +Cvars are restricted from having the same names as commands to keep this +interface from being ambiguous. +*/ + +// cvar flags +#define CVAR_ARCHIVE 1 +#define CVAR_USERINFO 2 // mirrored to userinfo +#define CVAR_SERVERINFO 4 // mirrored to serverinfo +#define CVAR_ROM 64 // read only +#define CVAR_USER_CREATED 128 // created by a set command + +typedef struct cvar_s +{ + char *name; + char *string; + int flags; + qboolean (*OnChange)(struct cvar_s *var, char *value); + float value; + struct cvar_s *hash_next; + struct cvar_s *next; +} cvar_t; + + +void Cvar_RegisterVariable (cvar_t *variable); +// registers a cvar that already has the name, string, and optionally the +// archive elements set. + +void Cvar_Set (cvar_t *var, char *value); +// equivalent to " " typed at the console + +void Cvar_SetROM (cvar_t *var, char *value); +// force a set even if the cvar is read only + +void Cvar_SetValue (cvar_t *var, float value); +// expands value to a string and calls Cvar_Set + +float Cvar_VariableValue (char *var_name); +// returns 0 if not defined or non numeric + +char *Cvar_VariableString (char *var_name); +// returns an empty string if not defined + +char *Cvar_CompleteVariable (char *partial); +// attempts to match a partial variable name for command line completion +// returns NULL if nothing fits + +qboolean Cvar_Command (void); +// called by Cmd_ExecuteString when Cmd_Argv(0) doesn't match a known +// command. Returns true if the command was a variable reference that +// was handled. (print or change) + +void Cvar_WriteVariables (FILE *f); +// Writes lines containing "set variable value" for all variables +// with the archive flag set to true. + +cvar_t *Cvar_FindVar (char *var_name); +qboolean Cvar_Delete (char *name); + +cvar_t *Cvar_Create (char *name, char *string, int cvarflags); + +void Cvar_Init (void); diff --git a/source/d_copy.s b/source/d_copy.s index f553fcb1..5816abb7 100644 --- a/source/d_copy.s +++ b/source/d_copy.s @@ -1,171 +1,171 @@ -/* - d_copy.s - - x86 assembly language screen copying code - - Copyright (C) 1996-1997 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: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA -*/ - -#include "asm_i386.h" -#include "quakeasm.h" -#include "asm_draw.h" - -#ifdef id386 - .data - -LCopyWidth: .long 0 -LBlockSrcStep: .long 0 -LBlockDestStep: .long 0 -LSrcDelta: .long 0 -LDestDelta: .long 0 - -#define bufptr 4+16 - -// copies 16 rows per plane at a pop; idea is that 16*512 = 8k, and since -// no Mode X mode is wider than 360, all the data should fit in the cache for -// the passes for the next 3 planes - - .text - -.globl C(VGA_UpdatePlanarScreen) -C(VGA_UpdatePlanarScreen): - pushl %ebp // preserve caller's stack frame - pushl %edi - pushl %esi // preserve register variables - pushl %ebx - - movl C(VGA_bufferrowbytes),%eax - shll $1,%eax - movl %eax,LBlockSrcStep - movl C(VGA_rowbytes),%eax - shll $1,%eax - movl %eax,LBlockDestStep - - movl $0x3C4,%edx - movb $2,%al - outb %al,%dx // point the SC to the Map Mask - incl %edx - - movl bufptr(%esp),%esi - movl C(VGA_pagebase),%edi - movl C(VGA_height),%ebp - shrl $1,%ebp - - movl C(VGA_width),%ecx - movl C(VGA_bufferrowbytes),%eax - subl %ecx,%eax - movl %eax,LSrcDelta - movl C(VGA_rowbytes),%eax - shll $2,%eax - subl %ecx,%eax - movl %eax,LDestDelta - shrl $4,%ecx - movl %ecx,LCopyWidth - -LRowLoop: - movb $1,%al - -LPlaneLoop: - outb %al,%dx - movb $2,%ah - - pushl %esi - pushl %edi -LRowSetLoop: - movl LCopyWidth,%ecx -LColumnLoop: - movb 12(%esi),%bh - movb 8(%esi),%bl - shll $16,%ebx - movb 4(%esi),%bh - movb (%esi),%bl - movl %ebx,(%edi) - addl $16,%esi - addl $4,%edi - decl %ecx - jnz LColumnLoop - - addl LDestDelta,%edi - addl LSrcDelta,%esi - decb %ah - jnz LRowSetLoop - - popl %edi - popl %esi - incl %esi - - shlb $1,%al - cmpb $16,%al - jnz LPlaneLoop - - subl $4,%esi - addl LBlockSrcStep,%esi - addl LBlockDestStep,%edi - decl %ebp - jnz LRowLoop - - popl %ebx // restore register variables - popl %esi - popl %edi - popl %ebp // restore the caller's stack frame - - ret - - -#define srcptr 4+16 -#define destptr 8+16 -#define width 12+16 -#define height 16+16 -#define srcrowbytes 20+16 -#define destrowbytes 24+16 - -.globl C(VGA_UpdateLinearScreen) -C(VGA_UpdateLinearScreen): - pushl %ebp // preserve caller's stack frame - pushl %edi - pushl %esi // preserve register variables - pushl %ebx - - cld - movl srcptr(%esp),%esi - movl destptr(%esp),%edi - movl width(%esp),%ebx - movl srcrowbytes(%esp),%eax - subl %ebx,%eax - movl destrowbytes(%esp),%edx - subl %ebx,%edx - shrl $2,%ebx - movl height(%esp),%ebp -LLRowLoop: - movl %ebx,%ecx - rep/movsl (%esi),(%edi) - addl %eax,%esi - addl %edx,%edi - decl %ebp - jnz LLRowLoop - - popl %ebx // restore register variables - popl %esi - popl %edi - popl %ebp // restore the caller's stack frame - - ret -#endif // id386 +/* + d_copy.s + + x86 assembly language screen copying code + + Copyright (C) 1996-1997 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA +*/ + +#include "asm_i386.h" +#include "quakeasm.h" +#include "asm_draw.h" + +#ifdef id386 + .data + +LCopyWidth: .long 0 +LBlockSrcStep: .long 0 +LBlockDestStep: .long 0 +LSrcDelta: .long 0 +LDestDelta: .long 0 + +#define bufptr 4+16 + +// copies 16 rows per plane at a pop; idea is that 16*512 = 8k, and since +// no Mode X mode is wider than 360, all the data should fit in the cache for +// the passes for the next 3 planes + + .text + +.globl C(VGA_UpdatePlanarScreen) +C(VGA_UpdatePlanarScreen): + pushl %ebp // preserve caller's stack frame + pushl %edi + pushl %esi // preserve register variables + pushl %ebx + + movl C(VGA_bufferrowbytes),%eax + shll $1,%eax + movl %eax,LBlockSrcStep + movl C(VGA_rowbytes),%eax + shll $1,%eax + movl %eax,LBlockDestStep + + movl $0x3C4,%edx + movb $2,%al + outb %al,%dx // point the SC to the Map Mask + incl %edx + + movl bufptr(%esp),%esi + movl C(VGA_pagebase),%edi + movl C(VGA_height),%ebp + shrl $1,%ebp + + movl C(VGA_width),%ecx + movl C(VGA_bufferrowbytes),%eax + subl %ecx,%eax + movl %eax,LSrcDelta + movl C(VGA_rowbytes),%eax + shll $2,%eax + subl %ecx,%eax + movl %eax,LDestDelta + shrl $4,%ecx + movl %ecx,LCopyWidth + +LRowLoop: + movb $1,%al + +LPlaneLoop: + outb %al,%dx + movb $2,%ah + + pushl %esi + pushl %edi +LRowSetLoop: + movl LCopyWidth,%ecx +LColumnLoop: + movb 12(%esi),%bh + movb 8(%esi),%bl + shll $16,%ebx + movb 4(%esi),%bh + movb (%esi),%bl + movl %ebx,(%edi) + addl $16,%esi + addl $4,%edi + decl %ecx + jnz LColumnLoop + + addl LDestDelta,%edi + addl LSrcDelta,%esi + decb %ah + jnz LRowSetLoop + + popl %edi + popl %esi + incl %esi + + shlb $1,%al + cmpb $16,%al + jnz LPlaneLoop + + subl $4,%esi + addl LBlockSrcStep,%esi + addl LBlockDestStep,%edi + decl %ebp + jnz LRowLoop + + popl %ebx // restore register variables + popl %esi + popl %edi + popl %ebp // restore the caller's stack frame + + ret + + +#define srcptr 4+16 +#define destptr 8+16 +#define width 12+16 +#define height 16+16 +#define srcrowbytes 20+16 +#define destrowbytes 24+16 + +.globl C(VGA_UpdateLinearScreen) +C(VGA_UpdateLinearScreen): + pushl %ebp // preserve caller's stack frame + pushl %edi + pushl %esi // preserve register variables + pushl %ebx + + cld + movl srcptr(%esp),%esi + movl destptr(%esp),%edi + movl width(%esp),%ebx + movl srcrowbytes(%esp),%eax + subl %ebx,%eax + movl destrowbytes(%esp),%edx + subl %ebx,%edx + shrl $2,%ebx + movl height(%esp),%ebp +LLRowLoop: + movl %ebx,%ecx + rep/movsl (%esi),(%edi) + addl %eax,%esi + addl %edx,%edi + decl %ebp + jnz LLRowLoop + + popl %ebx // restore register variables + popl %esi + popl %edi + popl %ebp // restore the caller's stack frame + + ret +#endif // id386 diff --git a/source/d_draw.s b/source/d_draw.s index fe948c42..79ff7d60 100644 --- a/source/d_draw.s +++ b/source/d_draw.s @@ -1,1043 +1,1043 @@ -/* - d_draw.S - - (description) - - Copyright (C) 1996-1997 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: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - - $Id: d_draw.s,v 1.1.1.3 2004/10/13 18:54:29 vvd0 Exp $ -*/ -// d_draw.s -// x86 assembly-language horizontal 8-bpp span-drawing code. - -#include "asm_i386.h" -#include "quakeasm.h" -#include "asm_draw.h" -#include "d_ifacea.h" - -#ifdef id386 - -//---------------------------------------------------------------------- -// 8-bpp horizontal span drawing code for polygons, with no transparency. -// -// Assumes there is at least one span in pspans, and that every span -// contains at least one pixel -//---------------------------------------------------------------------- - - .text - -// out-of-line, rarely-needed clamping code - -LClampHigh0: - movl C(bbextents),%esi - jmp LClampReentry0 -LClampHighOrLow0: - jg LClampHigh0 - xorl %esi,%esi - jmp LClampReentry0 - -LClampHigh1: - movl C(bbextentt),%edx - jmp LClampReentry1 -LClampHighOrLow1: - jg LClampHigh1 - xorl %edx,%edx - jmp LClampReentry1 - -LClampLow2: - movl $2048,%ebp - jmp LClampReentry2 -LClampHigh2: - movl C(bbextents),%ebp - jmp LClampReentry2 - -LClampLow3: - movl $2048,%ecx - jmp LClampReentry3 -LClampHigh3: - movl C(bbextentt),%ecx - jmp LClampReentry3 - -LClampLow4: - movl $2048,%eax - jmp LClampReentry4 -LClampHigh4: - movl C(bbextents),%eax - jmp LClampReentry4 - -LClampLow5: - movl $2048,%ebx - jmp LClampReentry5 -LClampHigh5: - movl C(bbextentt),%ebx - jmp LClampReentry5 - - -#define pspans 4+16 - - .align 4 -.globl C(D_DrawSpans8) -C(D_DrawSpans8): - pushl %ebp // preserve caller's stack frame - pushl %edi - pushl %esi // preserve register variables - pushl %ebx - -// -// set up scaled-by-8 steps, for 8-long segments; also set up cacheblock -// and span list pointers -// -// TODO: any overlap from rearranging? - flds C(d_sdivzstepu) - fmuls fp_8 - movl C(cacheblock),%edx - flds C(d_tdivzstepu) - fmuls fp_8 - movl pspans(%esp),%ebx // point to the first span descriptor - flds C(d_zistepu) - fmuls fp_8 - movl %edx,pbase // pbase = cacheblock - fstps zi8stepu - fstps tdivz8stepu - fstps sdivz8stepu - -LSpanLoop: -// -// set up the initial s/z, t/z, and 1/z on the FP stack, and generate the -// initial s and t values -// -// FIXME: pipeline FILD? - fildl espan_t_v(%ebx) - fildl espan_t_u(%ebx) - - fld %st(1) // dv | du | dv - fmuls C(d_sdivzstepv) // dv*d_sdivzstepv | du | dv - fld %st(1) // du | dv*d_sdivzstepv | du | dv - fmuls C(d_sdivzstepu) // du*d_sdivzstepu | dv*d_sdivzstepv | du | dv - fld %st(2) // du | du*d_sdivzstepu | dv*d_sdivzstepv | du | dv - fmuls C(d_tdivzstepu) // du*d_tdivzstepu | du*d_sdivzstepu | - // dv*d_sdivzstepv | du | dv - fxch %st(1) // du*d_sdivzstepu | du*d_tdivzstepu | - // dv*d_sdivzstepv | du | dv - faddp %st(0),%st(2) // du*d_tdivzstepu | - // du*d_sdivzstepu + dv*d_sdivzstepv | du | dv - fxch %st(1) // du*d_sdivzstepu + dv*d_sdivzstepv | - // du*d_tdivzstepu | du | dv - fld %st(3) // dv | du*d_sdivzstepu + dv*d_sdivzstepv | - // du*d_tdivzstepu | du | dv - fmuls C(d_tdivzstepv) // dv*d_tdivzstepv | - // du*d_sdivzstepu + dv*d_sdivzstepv | - // du*d_tdivzstepu | du | dv - fxch %st(1) // du*d_sdivzstepu + dv*d_sdivzstepv | - // dv*d_tdivzstepv | du*d_tdivzstepu | du | dv - fadds C(d_sdivzorigin) // sdivz = d_sdivzorigin + dv*d_sdivzstepv + - // du*d_sdivzstepu; stays in %st(2) at end - fxch %st(4) // dv | dv*d_tdivzstepv | du*d_tdivzstepu | du | - // s/z - fmuls C(d_zistepv) // dv*d_zistepv | dv*d_tdivzstepv | - // du*d_tdivzstepu | du | s/z - fxch %st(1) // dv*d_tdivzstepv | dv*d_zistepv | - // du*d_tdivzstepu | du | s/z - faddp %st(0),%st(2) // dv*d_zistepv | - // dv*d_tdivzstepv + du*d_tdivzstepu | du | s/z - fxch %st(2) // du | dv*d_tdivzstepv + du*d_tdivzstepu | - // dv*d_zistepv | s/z - fmuls C(d_zistepu) // du*d_zistepu | - // dv*d_tdivzstepv + du*d_tdivzstepu | - // dv*d_zistepv | s/z - fxch %st(1) // dv*d_tdivzstepv + du*d_tdivzstepu | - // du*d_zistepu | dv*d_zistepv | s/z - fadds C(d_tdivzorigin) // tdivz = d_tdivzorigin + dv*d_tdivzstepv + - // du*d_tdivzstepu; stays in %st(1) at end - fxch %st(2) // dv*d_zistepv | du*d_zistepu | t/z | s/z - faddp %st(0),%st(1) // dv*d_zistepv + du*d_zistepu | t/z | s/z - - flds fp_64k // fp_64k | dv*d_zistepv + du*d_zistepu | t/z | s/z - fxch %st(1) // dv*d_zistepv + du*d_zistepu | fp_64k | t/z | s/z - fadds C(d_ziorigin) // zi = d_ziorigin + dv*d_zistepv + - // du*d_zistepu; stays in %st(0) at end - // 1/z | fp_64k | t/z | s/z -// -// calculate and clamp s & t -// - fdivr %st(0),%st(1) // 1/z | z*64k | t/z | s/z - -// -// point %edi to the first pixel in the span -// - movl C(d_viewbuffer),%ecx - movl espan_t_v(%ebx),%eax - movl %ebx,pspantemp // preserve spans pointer - - movl C(tadjust),%edx - movl C(sadjust),%esi - movl C(d_scantable)(,%eax,4),%edi // v * screenwidth - addl %ecx,%edi - movl espan_t_u(%ebx),%ecx - addl %ecx,%edi // pdest = &pdestspan[scans->u]; - movl espan_t_count(%ebx),%ecx - -// -// now start the FDIV for the end of the span -// - cmpl $8,%ecx - ja LSetupNotLast1 - - decl %ecx - jz LCleanup1 // if only one pixel, no need to start an FDIV - movl %ecx,spancountminus1 - -// finish up the s and t calcs - fxch %st(1) // z*64k | 1/z | t/z | s/z - - fld %st(0) // z*64k | z*64k | 1/z | t/z | s/z - fmul %st(4),%st(0) // s | z*64k | 1/z | t/z | s/z - fxch %st(1) // z*64k | s | 1/z | t/z | s/z - fmul %st(3),%st(0) // t | s | 1/z | t/z | s/z - fxch %st(1) // s | t | 1/z | t/z | s/z - fistpl s // 1/z | t | t/z | s/z - fistpl t // 1/z | t/z | s/z - - fildl spancountminus1 - - flds C(d_tdivzstepu) // C(d_tdivzstepu) | spancountminus1 - flds C(d_zistepu) // C(d_zistepu) | C(d_tdivzstepu) | spancountminus1 - fmul %st(2),%st(0) // C(d_zistepu)*scm1 | C(d_tdivzstepu) | scm1 - fxch %st(1) // C(d_tdivzstepu) | C(d_zistepu)*scm1 | scm1 - fmul %st(2),%st(0) // C(d_tdivzstepu)*scm1 | C(d_zistepu)*scm1 | scm1 - fxch %st(2) // scm1 | C(d_zistepu)*scm1 | C(d_tdivzstepu)*scm1 - fmuls C(d_sdivzstepu) // C(d_sdivzstepu)*scm1 | C(d_zistepu)*scm1 | - // C(d_tdivzstepu)*scm1 - fxch %st(1) // C(d_zistepu)*scm1 | C(d_sdivzstepu)*scm1 | - // C(d_tdivzstepu)*scm1 - faddp %st(0),%st(3) // C(d_sdivzstepu)*scm1 | C(d_tdivzstepu)*scm1 - fxch %st(1) // C(d_tdivzstepu)*scm1 | C(d_sdivzstepu)*scm1 - faddp %st(0),%st(3) // C(d_sdivzstepu)*scm1 - faddp %st(0),%st(3) - - flds fp_64k - fdiv %st(1),%st(0) // this is what we've gone to all this trouble to - // overlap - jmp LFDIVInFlight1 - -LCleanup1: -// finish up the s and t calcs - fxch %st(1) // z*64k | 1/z | t/z | s/z - - fld %st(0) // z*64k | z*64k | 1/z | t/z | s/z - fmul %st(4),%st(0) // s | z*64k | 1/z | t/z | s/z - fxch %st(1) // z*64k | s | 1/z | t/z | s/z - fmul %st(3),%st(0) // t | s | 1/z | t/z | s/z - fxch %st(1) // s | t | 1/z | t/z | s/z - fistpl s // 1/z | t | t/z | s/z - fistpl t // 1/z | t/z | s/z - jmp LFDIVInFlight1 - - .align 4 -LSetupNotLast1: -// finish up the s and t calcs - fxch %st(1) // z*64k | 1/z | t/z | s/z - - fld %st(0) // z*64k | z*64k | 1/z | t/z | s/z - fmul %st(4),%st(0) // s | z*64k | 1/z | t/z | s/z - fxch %st(1) // z*64k | s | 1/z | t/z | s/z - fmul %st(3),%st(0) // t | s | 1/z | t/z | s/z - fxch %st(1) // s | t | 1/z | t/z | s/z - fistpl s // 1/z | t | t/z | s/z - fistpl t // 1/z | t/z | s/z - - fadds zi8stepu - fxch %st(2) - fadds sdivz8stepu - fxch %st(2) - flds tdivz8stepu - faddp %st(0),%st(2) - flds fp_64k - fdiv %st(1),%st(0) // z = 1/1/z - // this is what we've gone to all this trouble to - // overlap -LFDIVInFlight1: - - addl s,%esi - addl t,%edx - movl C(bbextents),%ebx - movl C(bbextentt),%ebp - cmpl %ebx,%esi - ja LClampHighOrLow0 -LClampReentry0: - movl %esi,s - movl pbase,%ebx - shll $16,%esi - cmpl %ebp,%edx - movl %esi,sfracf - ja LClampHighOrLow1 -LClampReentry1: - movl %edx,t - movl s,%esi // sfrac = scans->sfrac; - shll $16,%edx - movl t,%eax // tfrac = scans->tfrac; - sarl $16,%esi - movl %edx,tfracf - -// -// calculate the texture starting address -// - sarl $16,%eax - movl C(cachewidth),%edx - imull %edx,%eax // (tfrac >> 16) * cachewidth - addl %ebx,%esi - addl %eax,%esi // psource = pbase + (sfrac >> 16) + - // ((tfrac >> 16) * cachewidth); - -// -// determine whether last span or not -// - cmpl $8,%ecx - jna LLastSegment - -// -// not the last segment; do full 8-wide segment -// -LNotLastSegment: - -// -// advance s/z, t/z, and 1/z, and calculate s & t at end of span and steps to -// get there -// - -// pick up after the FDIV that was left in flight previously - - fld %st(0) // duplicate it - fmul %st(4),%st(0) // s = s/z * z - fxch %st(1) - fmul %st(3),%st(0) // t = t/z * z - fxch %st(1) - fistpl snext - fistpl tnext - movl snext,%eax - movl tnext,%edx - - movb (%esi),%bl // get first source texel - subl $8,%ecx // count off this segments' pixels - movl C(sadjust),%ebp - movl %ecx,counttemp // remember count of remaining pixels - - movl C(tadjust),%ecx - movb %bl,(%edi) // store first dest pixel - - addl %eax,%ebp - addl %edx,%ecx - - movl C(bbextents),%eax - movl C(bbextentt),%edx - - cmpl $2048,%ebp - jl LClampLow2 - cmpl %eax,%ebp - ja LClampHigh2 -LClampReentry2: - - cmpl $2048,%ecx - jl LClampLow3 - cmpl %edx,%ecx - ja LClampHigh3 -LClampReentry3: - - movl %ebp,snext - movl %ecx,tnext - - subl s,%ebp - subl t,%ecx - -// -// set up advancetable -// - movl %ecx,%eax - movl %ebp,%edx - sarl $19,%eax // tstep >>= 16; - jz LZero - sarl $19,%edx // sstep >>= 16; - movl C(cachewidth),%ebx - imull %ebx,%eax - jmp LSetUp1 - -LZero: - sarl $19,%edx // sstep >>= 16; - movl C(cachewidth),%ebx - -LSetUp1: - - addl %edx,%eax // add in sstep - // (tstep >> 16) * cachewidth + (sstep >> 16); - movl tfracf,%edx - movl %eax,advancetable+4 // advance base in t - addl %ebx,%eax // ((tstep >> 16) + 1) * cachewidth + - // (sstep >> 16); - shll $13,%ebp // left-justify sstep fractional part - movl sfracf,%ebx - shll $13,%ecx // left-justify tstep fractional part - movl %eax,advancetable // advance extra in t - - movl %ecx,tstep - addl %ecx,%edx // advance tfrac fractional part by tstep frac - - sbbl %ecx,%ecx // turn tstep carry into -1 (0 if none) - addl %ebp,%ebx // advance sfrac fractional part by sstep frac - adcl advancetable+4(,%ecx,4),%esi // point to next source texel - - addl tstep,%edx - sbbl %ecx,%ecx - movb (%esi),%al - addl %ebp,%ebx - movb %al,1(%edi) - adcl advancetable+4(,%ecx,4),%esi - - addl tstep,%edx - sbbl %ecx,%ecx - addl %ebp,%ebx - movb (%esi),%al - adcl advancetable+4(,%ecx,4),%esi - - addl tstep,%edx - sbbl %ecx,%ecx - movb %al,2(%edi) - addl %ebp,%ebx - movb (%esi),%al - adcl advancetable+4(,%ecx,4),%esi - - addl tstep,%edx - sbbl %ecx,%ecx - movb %al,3(%edi) - addl %ebp,%ebx - movb (%esi),%al - adcl advancetable+4(,%ecx,4),%esi - - -// -// start FDIV for end of next segment in flight, so it can overlap -// - movl counttemp,%ecx - cmpl $8,%ecx // more than one segment after this? - ja LSetupNotLast2 // yes - - decl %ecx - jz LFDIVInFlight2 // if only one pixel, no need to start an FDIV - movl %ecx,spancountminus1 - fildl spancountminus1 - - flds C(d_zistepu) // C(d_zistepu) | spancountminus1 - fmul %st(1),%st(0) // C(d_zistepu)*scm1 | scm1 - flds C(d_tdivzstepu) // C(d_tdivzstepu) | C(d_zistepu)*scm1 | scm1 - fmul %st(2),%st(0) // C(d_tdivzstepu)*scm1 | C(d_zistepu)*scm1 | scm1 - fxch %st(1) // C(d_zistepu)*scm1 | C(d_tdivzstepu)*scm1 | scm1 - faddp %st(0),%st(3) // C(d_tdivzstepu)*scm1 | scm1 - fxch %st(1) // scm1 | C(d_tdivzstepu)*scm1 - fmuls C(d_sdivzstepu) // C(d_sdivzstepu)*scm1 | C(d_tdivzstepu)*scm1 - fxch %st(1) // C(d_tdivzstepu)*scm1 | C(d_sdivzstepu)*scm1 - faddp %st(0),%st(3) // C(d_sdivzstepu)*scm1 - flds fp_64k // 64k | C(d_sdivzstepu)*scm1 - fxch %st(1) // C(d_sdivzstepu)*scm1 | 64k - faddp %st(0),%st(4) // 64k - - fdiv %st(1),%st(0) // this is what we've gone to all this trouble to - // overlap - jmp LFDIVInFlight2 - - .align 4 -LSetupNotLast2: - fadds zi8stepu - fxch %st(2) - fadds sdivz8stepu - fxch %st(2) - flds tdivz8stepu - faddp %st(0),%st(2) - flds fp_64k - fdiv %st(1),%st(0) // z = 1/1/z - // this is what we've gone to all this trouble to - // overlap -LFDIVInFlight2: - movl %ecx,counttemp - - addl tstep,%edx - sbbl %ecx,%ecx - movb %al,4(%edi) - addl %ebp,%ebx - movb (%esi),%al - adcl advancetable+4(,%ecx,4),%esi - - addl tstep,%edx - sbbl %ecx,%ecx - movb %al,5(%edi) - addl %ebp,%ebx - movb (%esi),%al - adcl advancetable+4(,%ecx,4),%esi - - addl tstep,%edx - sbbl %ecx,%ecx - movb %al,6(%edi) - addl %ebp,%ebx - movb (%esi),%al - adcl advancetable+4(,%ecx,4),%esi - - addl $8,%edi - movl %edx,tfracf - movl snext,%edx - movl %ebx,sfracf - movl tnext,%ebx - movl %edx,s - movl %ebx,t - - movl counttemp,%ecx // retrieve count - -// -// determine whether last span or not -// - cmpl $8,%ecx // are there multiple segments remaining? - movb %al,-1(%edi) - ja LNotLastSegment // yes - -// -// last segment of scan -// -LLastSegment: - -// -// advance s/z, t/z, and 1/z, and calculate s & t at end of span and steps to -// get there. The number of pixels left is variable, and we want to land on the -// last pixel, not step one past it, so we can't run into arithmetic problems -// - testl %ecx,%ecx - jz LNoSteps // just draw the last pixel and we're done - -// pick up after the FDIV that was left in flight previously - - - fld %st(0) // duplicate it - fmul %st(4),%st(0) // s = s/z * z - fxch %st(1) - fmul %st(3),%st(0) // t = t/z * z - fxch %st(1) - fistpl snext - fistpl tnext - - movb (%esi),%al // load first texel in segment - movl C(tadjust),%ebx - movb %al,(%edi) // store first pixel in segment - movl C(sadjust),%eax - - addl snext,%eax - addl tnext,%ebx - - movl C(bbextents),%ebp - movl C(bbextentt),%edx - - cmpl $2048,%eax - jl LClampLow4 - cmpl %ebp,%eax - ja LClampHigh4 -LClampReentry4: - movl %eax,snext - - cmpl $2048,%ebx - jl LClampLow5 - cmpl %edx,%ebx - ja LClampHigh5 -LClampReentry5: - - cmpl $1,%ecx // don't bother - je LOnlyOneStep // if two pixels in segment, there's only one step, - // of the segment length - subl s,%eax - subl t,%ebx - - addl %eax,%eax // convert to 15.17 format so multiply by 1.31 - addl %ebx,%ebx // reciprocal yields 16.48 - - imull reciprocal_table-8(,%ecx,4) // sstep = (snext - s) / (spancount-1) - movl %edx,%ebp - - movl %ebx,%eax - imull reciprocal_table-8(,%ecx,4) // tstep = (tnext - t) / (spancount-1) - -LSetEntryvec: -// -// set up advancetable -// - movl entryvec_table(,%ecx,4),%ebx - movl %edx,%eax - movl %ebx,jumptemp // entry point into code for RET later - movl %ebp,%ecx - sarl $16,%edx // tstep >>= 16; - movl C(cachewidth),%ebx - sarl $16,%ecx // sstep >>= 16; - imull %ebx,%edx - - addl %ecx,%edx // add in sstep - // (tstep >> 16) * cachewidth + (sstep >> 16); - movl tfracf,%ecx - movl %edx,advancetable+4 // advance base in t - addl %ebx,%edx // ((tstep >> 16) + 1) * cachewidth + - // (sstep >> 16); - shll $16,%ebp // left-justify sstep fractional part - movl sfracf,%ebx - shll $16,%eax // left-justify tstep fractional part - movl %edx,advancetable // advance extra in t - - movl %eax,tstep - movl %ecx,%edx - addl %eax,%edx - sbbl %ecx,%ecx - addl %ebp,%ebx - adcl advancetable+4(,%ecx,4),%esi - - jmp *jumptemp // jump to the number-of-pixels handler - -//---------------------------------------- - -LNoSteps: - movb (%esi),%al // load first texel in segment - subl $7,%edi // adjust for hardwired offset - jmp LEndSpan - - -LOnlyOneStep: - subl s,%eax - subl t,%ebx - movl %eax,%ebp - movl %ebx,%edx - jmp LSetEntryvec - -//---------------------------------------- - -.globl Entry2_8 -Entry2_8: - subl $6,%edi // adjust for hardwired offsets - movb (%esi),%al - jmp LLEntry2_8 - -//---------------------------------------- - -.globl Entry3_8 -Entry3_8: - subl $5,%edi // adjust for hardwired offsets - addl %eax,%edx - movb (%esi),%al - sbbl %ecx,%ecx - addl %ebp,%ebx - adcl advancetable+4(,%ecx,4),%esi - jmp LLEntry3_8 - -//---------------------------------------- - -.globl Entry4_8 -Entry4_8: - subl $4,%edi // adjust for hardwired offsets - addl %eax,%edx - movb (%esi),%al - sbbl %ecx,%ecx - addl %ebp,%ebx - adcl advancetable+4(,%ecx,4),%esi - addl tstep,%edx - jmp LLEntry4_8 - -//---------------------------------------- - -.globl Entry5_8 -Entry5_8: - subl $3,%edi // adjust for hardwired offsets - addl %eax,%edx - movb (%esi),%al - sbbl %ecx,%ecx - addl %ebp,%ebx - adcl advancetable+4(,%ecx,4),%esi - addl tstep,%edx - jmp LLEntry5_8 - -//---------------------------------------- - -.globl Entry6_8 -Entry6_8: - subl $2,%edi // adjust for hardwired offsets - addl %eax,%edx - movb (%esi),%al - sbbl %ecx,%ecx - addl %ebp,%ebx - adcl advancetable+4(,%ecx,4),%esi - addl tstep,%edx - jmp LLEntry6_8 - -//---------------------------------------- - -.globl Entry7_8 -Entry7_8: - decl %edi // adjust for hardwired offsets - addl %eax,%edx - movb (%esi),%al - sbbl %ecx,%ecx - addl %ebp,%ebx - adcl advancetable+4(,%ecx,4),%esi - addl tstep,%edx - jmp LLEntry7_8 - -//---------------------------------------- - -.globl Entry8_8 -Entry8_8: - addl %eax,%edx - movb (%esi),%al - sbbl %ecx,%ecx - addl %ebp,%ebx - adcl advancetable+4(,%ecx,4),%esi - - addl tstep,%edx - sbbl %ecx,%ecx - movb %al,1(%edi) - addl %ebp,%ebx - movb (%esi),%al - adcl advancetable+4(,%ecx,4),%esi - addl tstep,%edx -LLEntry7_8: - sbbl %ecx,%ecx - movb %al,2(%edi) - addl %ebp,%ebx - movb (%esi),%al - adcl advancetable+4(,%ecx,4),%esi - addl tstep,%edx -LLEntry6_8: - sbbl %ecx,%ecx - movb %al,3(%edi) - addl %ebp,%ebx - movb (%esi),%al - adcl advancetable+4(,%ecx,4),%esi - addl tstep,%edx -LLEntry5_8: - sbbl %ecx,%ecx - movb %al,4(%edi) - addl %ebp,%ebx - movb (%esi),%al - adcl advancetable+4(,%ecx,4),%esi - addl tstep,%edx -LLEntry4_8: - sbbl %ecx,%ecx - movb %al,5(%edi) - addl %ebp,%ebx - movb (%esi),%al - adcl advancetable+4(,%ecx,4),%esi -LLEntry3_8: - movb %al,6(%edi) - movb (%esi),%al -LLEntry2_8: - -LEndSpan: - -// -// clear s/z, t/z, 1/z from FP stack -// - fstp %st(0) - fstp %st(0) - fstp %st(0) - - movl pspantemp,%ebx // restore spans pointer - movl espan_t_pnext(%ebx),%ebx // point to next span - testl %ebx,%ebx // any more spans? - movb %al,7(%edi) - jnz LSpanLoop // more spans - - popl %ebx // restore register variables - popl %esi - popl %edi - popl %ebp // restore the caller's stack frame - ret - -//---------------------------------------------------------------------- -// 8-bpp horizontal span z drawing codefor polygons, with no transparency. -// -// Assumes there is at least one span in pzspans, and that every span -// contains at least one pixel -//---------------------------------------------------------------------- - - .text - -// z-clamp on a non-negative gradient span -LClamp: - movl $0x40000000,%edx - xorl %ebx,%ebx - fstp %st(0) - jmp LZDraw - -// z-clamp on a negative gradient span -LClampNeg: - movl $0x40000000,%edx - xorl %ebx,%ebx - fstp %st(0) - jmp LZDrawNeg - - -#define pzspans 4+16 - -.globl C(D_DrawZSpans) -C(D_DrawZSpans): - pushl %ebp // preserve caller's stack frame - pushl %edi - pushl %esi // preserve register variables - pushl %ebx - - flds C(d_zistepu) - movl C(d_zistepu),%eax - movl pzspans(%esp),%esi - testl %eax,%eax - jz LFNegSpan - - fmuls Float2ToThe31nd - fistpl izistep // note: we are relying on FP exceptions being turned - // off here to avoid range problems - movl izistep,%ebx // remains loaded for all spans - -LFSpanLoop: -// set up the initial 1/z value - fildl espan_t_v(%esi) - fildl espan_t_u(%esi) - movl espan_t_v(%esi),%ecx - movl C(d_pzbuffer),%edi - fmuls C(d_zistepu) - fxch %st(1) - fmuls C(d_zistepv) - fxch %st(1) - fadds C(d_ziorigin) - imull C(d_zrowbytes),%ecx - faddp %st(0),%st(1) - -// clamp if z is nearer than 2 (1/z > 0.5) - fcoms float_point5 - addl %ecx,%edi - movl espan_t_u(%esi),%edx - addl %edx,%edx // word count - movl espan_t_count(%esi),%ecx - addl %edx,%edi // pdest = &pdestspan[scans->u]; - pushl %esi // preserve spans pointer - fnstsw %ax - testb $0x45,%ah - jz LClamp - - fmuls Float2ToThe31nd - fistpl izi // note: we are relying on FP exceptions being turned - // off here to avoid problems when the span is closer - // than 1/(2**31) - movl izi,%edx - -// at this point: -// %ebx = izistep -// %ecx = count -// %edx = izi -// %edi = pdest - -LZDraw: - -// do a single pixel up front, if necessary to dword align the destination - testl $2,%edi - jz LFMiddle - movl %edx,%eax - addl %ebx,%edx - shrl $16,%eax - decl %ecx - movw %ax,(%edi) - addl $2,%edi - -// do middle a pair of aligned dwords at a time -LFMiddle: - pushl %ecx - shrl $1,%ecx // count / 2 - jz LFLast // no aligned dwords to do - shrl $1,%ecx // (count / 2) / 2 - jnc LFMiddleLoop // even number of aligned dwords to do - - movl %edx,%eax - addl %ebx,%edx - shrl $16,%eax - movl %edx,%esi - addl %ebx,%edx - andl $0xFFFF0000,%esi - orl %esi,%eax - movl %eax,(%edi) - addl $4,%edi - andl %ecx,%ecx - jz LFLast - -LFMiddleLoop: - movl %edx,%eax - addl %ebx,%edx - shrl $16,%eax - movl %edx,%esi - addl %ebx,%edx - andl $0xFFFF0000,%esi - orl %esi,%eax - movl %edx,%ebp - movl %eax,(%edi) - addl %ebx,%edx - shrl $16,%ebp - movl %edx,%esi - addl %ebx,%edx - andl $0xFFFF0000,%esi - orl %esi,%ebp - movl %ebp,4(%edi) // FIXME: eliminate register contention - addl $8,%edi - - decl %ecx - jnz LFMiddleLoop - -LFLast: - popl %ecx // retrieve count - popl %esi // retrieve span pointer - -// do the last, unaligned pixel, if there is one - andl $1,%ecx // is there an odd pixel left to do? - jz LFSpanDone // no - shrl $16,%edx - movw %dx,(%edi) // do the final pixel's z - -LFSpanDone: - movl espan_t_pnext(%esi),%esi - testl %esi,%esi - jnz LFSpanLoop - - jmp LFDone - -LFNegSpan: - fmuls FloatMinus2ToThe31nd - fistpl izistep // note: we are relying on FP exceptions being turned - // off here to avoid range problems - movl izistep,%ebx // remains loaded for all spans - -LFNegSpanLoop: -// set up the initial 1/z value - fildl espan_t_v(%esi) - fildl espan_t_u(%esi) - movl espan_t_v(%esi),%ecx - movl C(d_pzbuffer),%edi - fmuls C(d_zistepu) - fxch %st(1) - fmuls C(d_zistepv) - fxch %st(1) - fadds C(d_ziorigin) - imull C(d_zrowbytes),%ecx - faddp %st(0),%st(1) - -// clamp if z is nearer than 2 (1/z > 0.5) - fcoms float_point5 - addl %ecx,%edi - movl espan_t_u(%esi),%edx - addl %edx,%edx // word count - movl espan_t_count(%esi),%ecx - addl %edx,%edi // pdest = &pdestspan[scans->u]; - pushl %esi // preserve spans pointer - fnstsw %ax - testb $0x45,%ah - jz LClampNeg - - fmuls Float2ToThe31nd - fistpl izi // note: we are relying on FP exceptions being turned - // off here to avoid problems when the span is closer - // than 1/(2**31) - movl izi,%edx - -// at this point: -// %ebx = izistep -// %ecx = count -// %edx = izi -// %edi = pdest - -LZDrawNeg: - -// do a single pixel up front, if necessary to dword align the destination - testl $2,%edi - jz LFNegMiddle - movl %edx,%eax - subl %ebx,%edx - shrl $16,%eax - decl %ecx - movw %ax,(%edi) - addl $2,%edi - -// do middle a pair of aligned dwords at a time -LFNegMiddle: - pushl %ecx - shrl $1,%ecx // count / 2 - jz LFNegLast // no aligned dwords to do - shrl $1,%ecx // (count / 2) / 2 - jnc LFNegMiddleLoop // even number of aligned dwords to do - - movl %edx,%eax - subl %ebx,%edx - shrl $16,%eax - movl %edx,%esi - subl %ebx,%edx - andl $0xFFFF0000,%esi - orl %esi,%eax - movl %eax,(%edi) - addl $4,%edi - andl %ecx,%ecx - jz LFNegLast - -LFNegMiddleLoop: - movl %edx,%eax - subl %ebx,%edx - shrl $16,%eax - movl %edx,%esi - subl %ebx,%edx - andl $0xFFFF0000,%esi - orl %esi,%eax - movl %edx,%ebp - movl %eax,(%edi) - subl %ebx,%edx - shrl $16,%ebp - movl %edx,%esi - subl %ebx,%edx - andl $0xFFFF0000,%esi - orl %esi,%ebp - movl %ebp,4(%edi) // FIXME: eliminate register contention - addl $8,%edi - - decl %ecx - jnz LFNegMiddleLoop - -LFNegLast: - popl %ecx // retrieve count - popl %esi // retrieve span pointer - -// do the last, unaligned pixel, if there is one - andl $1,%ecx // is there an odd pixel left to do? - jz LFNegSpanDone // no - shrl $16,%edx - movw %dx,(%edi) // do the final pixel's z - -LFNegSpanDone: - movl espan_t_pnext(%esi),%esi - testl %esi,%esi - jnz LFNegSpanLoop - -LFDone: - popl %ebx // restore register variables - popl %esi - popl %edi - popl %ebp // restore the caller's stack frame - ret - -#endif // id386 +/* + d_draw.S + + (description) + + Copyright (C) 1996-1997 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id: d_draw.s,v 1.1.1.4 2004/10/18 17:44:26 vvd0 Exp $ +*/ +// d_draw.s +// x86 assembly-language horizontal 8-bpp span-drawing code. + +#include "asm_i386.h" +#include "quakeasm.h" +#include "asm_draw.h" +#include "d_ifacea.h" + +#ifdef id386 + +//---------------------------------------------------------------------- +// 8-bpp horizontal span drawing code for polygons, with no transparency. +// +// Assumes there is at least one span in pspans, and that every span +// contains at least one pixel +//---------------------------------------------------------------------- + + .text + +// out-of-line, rarely-needed clamping code + +LClampHigh0: + movl C(bbextents),%esi + jmp LClampReentry0 +LClampHighOrLow0: + jg LClampHigh0 + xorl %esi,%esi + jmp LClampReentry0 + +LClampHigh1: + movl C(bbextentt),%edx + jmp LClampReentry1 +LClampHighOrLow1: + jg LClampHigh1 + xorl %edx,%edx + jmp LClampReentry1 + +LClampLow2: + movl $2048,%ebp + jmp LClampReentry2 +LClampHigh2: + movl C(bbextents),%ebp + jmp LClampReentry2 + +LClampLow3: + movl $2048,%ecx + jmp LClampReentry3 +LClampHigh3: + movl C(bbextentt),%ecx + jmp LClampReentry3 + +LClampLow4: + movl $2048,%eax + jmp LClampReentry4 +LClampHigh4: + movl C(bbextents),%eax + jmp LClampReentry4 + +LClampLow5: + movl $2048,%ebx + jmp LClampReentry5 +LClampHigh5: + movl C(bbextentt),%ebx + jmp LClampReentry5 + + +#define pspans 4+16 + + .align 4 +.globl C(D_DrawSpans8) +C(D_DrawSpans8): + pushl %ebp // preserve caller's stack frame + pushl %edi + pushl %esi // preserve register variables + pushl %ebx + +// +// set up scaled-by-8 steps, for 8-long segments; also set up cacheblock +// and span list pointers +// +// TODO: any overlap from rearranging? + flds C(d_sdivzstepu) + fmuls fp_8 + movl C(cacheblock),%edx + flds C(d_tdivzstepu) + fmuls fp_8 + movl pspans(%esp),%ebx // point to the first span descriptor + flds C(d_zistepu) + fmuls fp_8 + movl %edx,pbase // pbase = cacheblock + fstps zi8stepu + fstps tdivz8stepu + fstps sdivz8stepu + +LSpanLoop: +// +// set up the initial s/z, t/z, and 1/z on the FP stack, and generate the +// initial s and t values +// +// FIXME: pipeline FILD? + fildl espan_t_v(%ebx) + fildl espan_t_u(%ebx) + + fld %st(1) // dv | du | dv + fmuls C(d_sdivzstepv) // dv*d_sdivzstepv | du | dv + fld %st(1) // du | dv*d_sdivzstepv | du | dv + fmuls C(d_sdivzstepu) // du*d_sdivzstepu | dv*d_sdivzstepv | du | dv + fld %st(2) // du | du*d_sdivzstepu | dv*d_sdivzstepv | du | dv + fmuls C(d_tdivzstepu) // du*d_tdivzstepu | du*d_sdivzstepu | + // dv*d_sdivzstepv | du | dv + fxch %st(1) // du*d_sdivzstepu | du*d_tdivzstepu | + // dv*d_sdivzstepv | du | dv + faddp %st(0),%st(2) // du*d_tdivzstepu | + // du*d_sdivzstepu + dv*d_sdivzstepv | du | dv + fxch %st(1) // du*d_sdivzstepu + dv*d_sdivzstepv | + // du*d_tdivzstepu | du | dv + fld %st(3) // dv | du*d_sdivzstepu + dv*d_sdivzstepv | + // du*d_tdivzstepu | du | dv + fmuls C(d_tdivzstepv) // dv*d_tdivzstepv | + // du*d_sdivzstepu + dv*d_sdivzstepv | + // du*d_tdivzstepu | du | dv + fxch %st(1) // du*d_sdivzstepu + dv*d_sdivzstepv | + // dv*d_tdivzstepv | du*d_tdivzstepu | du | dv + fadds C(d_sdivzorigin) // sdivz = d_sdivzorigin + dv*d_sdivzstepv + + // du*d_sdivzstepu; stays in %st(2) at end + fxch %st(4) // dv | dv*d_tdivzstepv | du*d_tdivzstepu | du | + // s/z + fmuls C(d_zistepv) // dv*d_zistepv | dv*d_tdivzstepv | + // du*d_tdivzstepu | du | s/z + fxch %st(1) // dv*d_tdivzstepv | dv*d_zistepv | + // du*d_tdivzstepu | du | s/z + faddp %st(0),%st(2) // dv*d_zistepv | + // dv*d_tdivzstepv + du*d_tdivzstepu | du | s/z + fxch %st(2) // du | dv*d_tdivzstepv + du*d_tdivzstepu | + // dv*d_zistepv | s/z + fmuls C(d_zistepu) // du*d_zistepu | + // dv*d_tdivzstepv + du*d_tdivzstepu | + // dv*d_zistepv | s/z + fxch %st(1) // dv*d_tdivzstepv + du*d_tdivzstepu | + // du*d_zistepu | dv*d_zistepv | s/z + fadds C(d_tdivzorigin) // tdivz = d_tdivzorigin + dv*d_tdivzstepv + + // du*d_tdivzstepu; stays in %st(1) at end + fxch %st(2) // dv*d_zistepv | du*d_zistepu | t/z | s/z + faddp %st(0),%st(1) // dv*d_zistepv + du*d_zistepu | t/z | s/z + + flds fp_64k // fp_64k | dv*d_zistepv + du*d_zistepu | t/z | s/z + fxch %st(1) // dv*d_zistepv + du*d_zistepu | fp_64k | t/z | s/z + fadds C(d_ziorigin) // zi = d_ziorigin + dv*d_zistepv + + // du*d_zistepu; stays in %st(0) at end + // 1/z | fp_64k | t/z | s/z +// +// calculate and clamp s & t +// + fdivr %st(0),%st(1) // 1/z | z*64k | t/z | s/z + +// +// point %edi to the first pixel in the span +// + movl C(d_viewbuffer),%ecx + movl espan_t_v(%ebx),%eax + movl %ebx,pspantemp // preserve spans pointer + + movl C(tadjust),%edx + movl C(sadjust),%esi + movl C(d_scantable)(,%eax,4),%edi // v * screenwidth + addl %ecx,%edi + movl espan_t_u(%ebx),%ecx + addl %ecx,%edi // pdest = &pdestspan[scans->u]; + movl espan_t_count(%ebx),%ecx + +// +// now start the FDIV for the end of the span +// + cmpl $8,%ecx + ja LSetupNotLast1 + + decl %ecx + jz LCleanup1 // if only one pixel, no need to start an FDIV + movl %ecx,spancountminus1 + +// finish up the s and t calcs + fxch %st(1) // z*64k | 1/z | t/z | s/z + + fld %st(0) // z*64k | z*64k | 1/z | t/z | s/z + fmul %st(4),%st(0) // s | z*64k | 1/z | t/z | s/z + fxch %st(1) // z*64k | s | 1/z | t/z | s/z + fmul %st(3),%st(0) // t | s | 1/z | t/z | s/z + fxch %st(1) // s | t | 1/z | t/z | s/z + fistpl s // 1/z | t | t/z | s/z + fistpl t // 1/z | t/z | s/z + + fildl spancountminus1 + + flds C(d_tdivzstepu) // C(d_tdivzstepu) | spancountminus1 + flds C(d_zistepu) // C(d_zistepu) | C(d_tdivzstepu) | spancountminus1 + fmul %st(2),%st(0) // C(d_zistepu)*scm1 | C(d_tdivzstepu) | scm1 + fxch %st(1) // C(d_tdivzstepu) | C(d_zistepu)*scm1 | scm1 + fmul %st(2),%st(0) // C(d_tdivzstepu)*scm1 | C(d_zistepu)*scm1 | scm1 + fxch %st(2) // scm1 | C(d_zistepu)*scm1 | C(d_tdivzstepu)*scm1 + fmuls C(d_sdivzstepu) // C(d_sdivzstepu)*scm1 | C(d_zistepu)*scm1 | + // C(d_tdivzstepu)*scm1 + fxch %st(1) // C(d_zistepu)*scm1 | C(d_sdivzstepu)*scm1 | + // C(d_tdivzstepu)*scm1 + faddp %st(0),%st(3) // C(d_sdivzstepu)*scm1 | C(d_tdivzstepu)*scm1 + fxch %st(1) // C(d_tdivzstepu)*scm1 | C(d_sdivzstepu)*scm1 + faddp %st(0),%st(3) // C(d_sdivzstepu)*scm1 + faddp %st(0),%st(3) + + flds fp_64k + fdiv %st(1),%st(0) // this is what we've gone to all this trouble to + // overlap + jmp LFDIVInFlight1 + +LCleanup1: +// finish up the s and t calcs + fxch %st(1) // z*64k | 1/z | t/z | s/z + + fld %st(0) // z*64k | z*64k | 1/z | t/z | s/z + fmul %st(4),%st(0) // s | z*64k | 1/z | t/z | s/z + fxch %st(1) // z*64k | s | 1/z | t/z | s/z + fmul %st(3),%st(0) // t | s | 1/z | t/z | s/z + fxch %st(1) // s | t | 1/z | t/z | s/z + fistpl s // 1/z | t | t/z | s/z + fistpl t // 1/z | t/z | s/z + jmp LFDIVInFlight1 + + .align 4 +LSetupNotLast1: +// finish up the s and t calcs + fxch %st(1) // z*64k | 1/z | t/z | s/z + + fld %st(0) // z*64k | z*64k | 1/z | t/z | s/z + fmul %st(4),%st(0) // s | z*64k | 1/z | t/z | s/z + fxch %st(1) // z*64k | s | 1/z | t/z | s/z + fmul %st(3),%st(0) // t | s | 1/z | t/z | s/z + fxch %st(1) // s | t | 1/z | t/z | s/z + fistpl s // 1/z | t | t/z | s/z + fistpl t // 1/z | t/z | s/z + + fadds zi8stepu + fxch %st(2) + fadds sdivz8stepu + fxch %st(2) + flds tdivz8stepu + faddp %st(0),%st(2) + flds fp_64k + fdiv %st(1),%st(0) // z = 1/1/z + // this is what we've gone to all this trouble to + // overlap +LFDIVInFlight1: + + addl s,%esi + addl t,%edx + movl C(bbextents),%ebx + movl C(bbextentt),%ebp + cmpl %ebx,%esi + ja LClampHighOrLow0 +LClampReentry0: + movl %esi,s + movl pbase,%ebx + shll $16,%esi + cmpl %ebp,%edx + movl %esi,sfracf + ja LClampHighOrLow1 +LClampReentry1: + movl %edx,t + movl s,%esi // sfrac = scans->sfrac; + shll $16,%edx + movl t,%eax // tfrac = scans->tfrac; + sarl $16,%esi + movl %edx,tfracf + +// +// calculate the texture starting address +// + sarl $16,%eax + movl C(cachewidth),%edx + imull %edx,%eax // (tfrac >> 16) * cachewidth + addl %ebx,%esi + addl %eax,%esi // psource = pbase + (sfrac >> 16) + + // ((tfrac >> 16) * cachewidth); + +// +// determine whether last span or not +// + cmpl $8,%ecx + jna LLastSegment + +// +// not the last segment; do full 8-wide segment +// +LNotLastSegment: + +// +// advance s/z, t/z, and 1/z, and calculate s & t at end of span and steps to +// get there +// + +// pick up after the FDIV that was left in flight previously + + fld %st(0) // duplicate it + fmul %st(4),%st(0) // s = s/z * z + fxch %st(1) + fmul %st(3),%st(0) // t = t/z * z + fxch %st(1) + fistpl snext + fistpl tnext + movl snext,%eax + movl tnext,%edx + + movb (%esi),%bl // get first source texel + subl $8,%ecx // count off this segments' pixels + movl C(sadjust),%ebp + movl %ecx,counttemp // remember count of remaining pixels + + movl C(tadjust),%ecx + movb %bl,(%edi) // store first dest pixel + + addl %eax,%ebp + addl %edx,%ecx + + movl C(bbextents),%eax + movl C(bbextentt),%edx + + cmpl $2048,%ebp + jl LClampLow2 + cmpl %eax,%ebp + ja LClampHigh2 +LClampReentry2: + + cmpl $2048,%ecx + jl LClampLow3 + cmpl %edx,%ecx + ja LClampHigh3 +LClampReentry3: + + movl %ebp,snext + movl %ecx,tnext + + subl s,%ebp + subl t,%ecx + +// +// set up advancetable +// + movl %ecx,%eax + movl %ebp,%edx + sarl $19,%eax // tstep >>= 16; + jz LZero + sarl $19,%edx // sstep >>= 16; + movl C(cachewidth),%ebx + imull %ebx,%eax + jmp LSetUp1 + +LZero: + sarl $19,%edx // sstep >>= 16; + movl C(cachewidth),%ebx + +LSetUp1: + + addl %edx,%eax // add in sstep + // (tstep >> 16) * cachewidth + (sstep >> 16); + movl tfracf,%edx + movl %eax,advancetable+4 // advance base in t + addl %ebx,%eax // ((tstep >> 16) + 1) * cachewidth + + // (sstep >> 16); + shll $13,%ebp // left-justify sstep fractional part + movl sfracf,%ebx + shll $13,%ecx // left-justify tstep fractional part + movl %eax,advancetable // advance extra in t + + movl %ecx,tstep + addl %ecx,%edx // advance tfrac fractional part by tstep frac + + sbbl %ecx,%ecx // turn tstep carry into -1 (0 if none) + addl %ebp,%ebx // advance sfrac fractional part by sstep frac + adcl advancetable+4(,%ecx,4),%esi // point to next source texel + + addl tstep,%edx + sbbl %ecx,%ecx + movb (%esi),%al + addl %ebp,%ebx + movb %al,1(%edi) + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,2(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,3(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + +// +// start FDIV for end of next segment in flight, so it can overlap +// + movl counttemp,%ecx + cmpl $8,%ecx // more than one segment after this? + ja LSetupNotLast2 // yes + + decl %ecx + jz LFDIVInFlight2 // if only one pixel, no need to start an FDIV + movl %ecx,spancountminus1 + fildl spancountminus1 + + flds C(d_zistepu) // C(d_zistepu) | spancountminus1 + fmul %st(1),%st(0) // C(d_zistepu)*scm1 | scm1 + flds C(d_tdivzstepu) // C(d_tdivzstepu) | C(d_zistepu)*scm1 | scm1 + fmul %st(2),%st(0) // C(d_tdivzstepu)*scm1 | C(d_zistepu)*scm1 | scm1 + fxch %st(1) // C(d_zistepu)*scm1 | C(d_tdivzstepu)*scm1 | scm1 + faddp %st(0),%st(3) // C(d_tdivzstepu)*scm1 | scm1 + fxch %st(1) // scm1 | C(d_tdivzstepu)*scm1 + fmuls C(d_sdivzstepu) // C(d_sdivzstepu)*scm1 | C(d_tdivzstepu)*scm1 + fxch %st(1) // C(d_tdivzstepu)*scm1 | C(d_sdivzstepu)*scm1 + faddp %st(0),%st(3) // C(d_sdivzstepu)*scm1 + flds fp_64k // 64k | C(d_sdivzstepu)*scm1 + fxch %st(1) // C(d_sdivzstepu)*scm1 | 64k + faddp %st(0),%st(4) // 64k + + fdiv %st(1),%st(0) // this is what we've gone to all this trouble to + // overlap + jmp LFDIVInFlight2 + + .align 4 +LSetupNotLast2: + fadds zi8stepu + fxch %st(2) + fadds sdivz8stepu + fxch %st(2) + flds tdivz8stepu + faddp %st(0),%st(2) + flds fp_64k + fdiv %st(1),%st(0) // z = 1/1/z + // this is what we've gone to all this trouble to + // overlap +LFDIVInFlight2: + movl %ecx,counttemp + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,4(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,5(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,6(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl $8,%edi + movl %edx,tfracf + movl snext,%edx + movl %ebx,sfracf + movl tnext,%ebx + movl %edx,s + movl %ebx,t + + movl counttemp,%ecx // retrieve count + +// +// determine whether last span or not +// + cmpl $8,%ecx // are there multiple segments remaining? + movb %al,-1(%edi) + ja LNotLastSegment // yes + +// +// last segment of scan +// +LLastSegment: + +// +// advance s/z, t/z, and 1/z, and calculate s & t at end of span and steps to +// get there. The number of pixels left is variable, and we want to land on the +// last pixel, not step one past it, so we can't run into arithmetic problems +// + testl %ecx,%ecx + jz LNoSteps // just draw the last pixel and we're done + +// pick up after the FDIV that was left in flight previously + + + fld %st(0) // duplicate it + fmul %st(4),%st(0) // s = s/z * z + fxch %st(1) + fmul %st(3),%st(0) // t = t/z * z + fxch %st(1) + fistpl snext + fistpl tnext + + movb (%esi),%al // load first texel in segment + movl C(tadjust),%ebx + movb %al,(%edi) // store first pixel in segment + movl C(sadjust),%eax + + addl snext,%eax + addl tnext,%ebx + + movl C(bbextents),%ebp + movl C(bbextentt),%edx + + cmpl $2048,%eax + jl LClampLow4 + cmpl %ebp,%eax + ja LClampHigh4 +LClampReentry4: + movl %eax,snext + + cmpl $2048,%ebx + jl LClampLow5 + cmpl %edx,%ebx + ja LClampHigh5 +LClampReentry5: + + cmpl $1,%ecx // don't bother + je LOnlyOneStep // if two pixels in segment, there's only one step, + // of the segment length + subl s,%eax + subl t,%ebx + + addl %eax,%eax // convert to 15.17 format so multiply by 1.31 + addl %ebx,%ebx // reciprocal yields 16.48 + + imull reciprocal_table-8(,%ecx,4) // sstep = (snext - s) / (spancount-1) + movl %edx,%ebp + + movl %ebx,%eax + imull reciprocal_table-8(,%ecx,4) // tstep = (tnext - t) / (spancount-1) + +LSetEntryvec: +// +// set up advancetable +// + movl entryvec_table(,%ecx,4),%ebx + movl %edx,%eax + movl %ebx,jumptemp // entry point into code for RET later + movl %ebp,%ecx + sarl $16,%edx // tstep >>= 16; + movl C(cachewidth),%ebx + sarl $16,%ecx // sstep >>= 16; + imull %ebx,%edx + + addl %ecx,%edx // add in sstep + // (tstep >> 16) * cachewidth + (sstep >> 16); + movl tfracf,%ecx + movl %edx,advancetable+4 // advance base in t + addl %ebx,%edx // ((tstep >> 16) + 1) * cachewidth + + // (sstep >> 16); + shll $16,%ebp // left-justify sstep fractional part + movl sfracf,%ebx + shll $16,%eax // left-justify tstep fractional part + movl %edx,advancetable // advance extra in t + + movl %eax,tstep + movl %ecx,%edx + addl %eax,%edx + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + + jmp *jumptemp // jump to the number-of-pixels handler + +//---------------------------------------- + +LNoSteps: + movb (%esi),%al // load first texel in segment + subl $7,%edi // adjust for hardwired offset + jmp LEndSpan + + +LOnlyOneStep: + subl s,%eax + subl t,%ebx + movl %eax,%ebp + movl %ebx,%edx + jmp LSetEntryvec + +//---------------------------------------- + +.globl Entry2_8 +Entry2_8: + subl $6,%edi // adjust for hardwired offsets + movb (%esi),%al + jmp LLEntry2_8 + +//---------------------------------------- + +.globl Entry3_8 +Entry3_8: + subl $5,%edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + jmp LLEntry3_8 + +//---------------------------------------- + +.globl Entry4_8 +Entry4_8: + subl $4,%edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx + jmp LLEntry4_8 + +//---------------------------------------- + +.globl Entry5_8 +Entry5_8: + subl $3,%edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx + jmp LLEntry5_8 + +//---------------------------------------- + +.globl Entry6_8 +Entry6_8: + subl $2,%edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx + jmp LLEntry6_8 + +//---------------------------------------- + +.globl Entry7_8 +Entry7_8: + decl %edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx + jmp LLEntry7_8 + +//---------------------------------------- + +.globl Entry8_8 +Entry8_8: + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,1(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx +LLEntry7_8: + sbbl %ecx,%ecx + movb %al,2(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx +LLEntry6_8: + sbbl %ecx,%ecx + movb %al,3(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx +LLEntry5_8: + sbbl %ecx,%ecx + movb %al,4(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx +LLEntry4_8: + sbbl %ecx,%ecx + movb %al,5(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi +LLEntry3_8: + movb %al,6(%edi) + movb (%esi),%al +LLEntry2_8: + +LEndSpan: + +// +// clear s/z, t/z, 1/z from FP stack +// + fstp %st(0) + fstp %st(0) + fstp %st(0) + + movl pspantemp,%ebx // restore spans pointer + movl espan_t_pnext(%ebx),%ebx // point to next span + testl %ebx,%ebx // any more spans? + movb %al,7(%edi) + jnz LSpanLoop // more spans + + popl %ebx // restore register variables + popl %esi + popl %edi + popl %ebp // restore the caller's stack frame + ret + +//---------------------------------------------------------------------- +// 8-bpp horizontal span z drawing codefor polygons, with no transparency. +// +// Assumes there is at least one span in pzspans, and that every span +// contains at least one pixel +//---------------------------------------------------------------------- + + .text + +// z-clamp on a non-negative gradient span +LClamp: + movl $0x40000000,%edx + xorl %ebx,%ebx + fstp %st(0) + jmp LZDraw + +// z-clamp on a negative gradient span +LClampNeg: + movl $0x40000000,%edx + xorl %ebx,%ebx + fstp %st(0) + jmp LZDrawNeg + + +#define pzspans 4+16 + +.globl C(D_DrawZSpans) +C(D_DrawZSpans): + pushl %ebp // preserve caller's stack frame + pushl %edi + pushl %esi // preserve register variables + pushl %ebx + + flds C(d_zistepu) + movl C(d_zistepu),%eax + movl pzspans(%esp),%esi + testl %eax,%eax + jz LFNegSpan + + fmuls Float2ToThe31nd + fistpl izistep // note: we are relying on FP exceptions being turned + // off here to avoid range problems + movl izistep,%ebx // remains loaded for all spans + +LFSpanLoop: +// set up the initial 1/z value + fildl espan_t_v(%esi) + fildl espan_t_u(%esi) + movl espan_t_v(%esi),%ecx + movl C(d_pzbuffer),%edi + fmuls C(d_zistepu) + fxch %st(1) + fmuls C(d_zistepv) + fxch %st(1) + fadds C(d_ziorigin) + imull C(d_zrowbytes),%ecx + faddp %st(0),%st(1) + +// clamp if z is nearer than 2 (1/z > 0.5) + fcoms float_point5 + addl %ecx,%edi + movl espan_t_u(%esi),%edx + addl %edx,%edx // word count + movl espan_t_count(%esi),%ecx + addl %edx,%edi // pdest = &pdestspan[scans->u]; + pushl %esi // preserve spans pointer + fnstsw %ax + testb $0x45,%ah + jz LClamp + + fmuls Float2ToThe31nd + fistpl izi // note: we are relying on FP exceptions being turned + // off here to avoid problems when the span is closer + // than 1/(2**31) + movl izi,%edx + +// at this point: +// %ebx = izistep +// %ecx = count +// %edx = izi +// %edi = pdest + +LZDraw: + +// do a single pixel up front, if necessary to dword align the destination + testl $2,%edi + jz LFMiddle + movl %edx,%eax + addl %ebx,%edx + shrl $16,%eax + decl %ecx + movw %ax,(%edi) + addl $2,%edi + +// do middle a pair of aligned dwords at a time +LFMiddle: + pushl %ecx + shrl $1,%ecx // count / 2 + jz LFLast // no aligned dwords to do + shrl $1,%ecx // (count / 2) / 2 + jnc LFMiddleLoop // even number of aligned dwords to do + + movl %edx,%eax + addl %ebx,%edx + shrl $16,%eax + movl %edx,%esi + addl %ebx,%edx + andl $0xFFFF0000,%esi + orl %esi,%eax + movl %eax,(%edi) + addl $4,%edi + andl %ecx,%ecx + jz LFLast + +LFMiddleLoop: + movl %edx,%eax + addl %ebx,%edx + shrl $16,%eax + movl %edx,%esi + addl %ebx,%edx + andl $0xFFFF0000,%esi + orl %esi,%eax + movl %edx,%ebp + movl %eax,(%edi) + addl %ebx,%edx + shrl $16,%ebp + movl %edx,%esi + addl %ebx,%edx + andl $0xFFFF0000,%esi + orl %esi,%ebp + movl %ebp,4(%edi) // FIXME: eliminate register contention + addl $8,%edi + + decl %ecx + jnz LFMiddleLoop + +LFLast: + popl %ecx // retrieve count + popl %esi // retrieve span pointer + +// do the last, unaligned pixel, if there is one + andl $1,%ecx // is there an odd pixel left to do? + jz LFSpanDone // no + shrl $16,%edx + movw %dx,(%edi) // do the final pixel's z + +LFSpanDone: + movl espan_t_pnext(%esi),%esi + testl %esi,%esi + jnz LFSpanLoop + + jmp LFDone + +LFNegSpan: + fmuls FloatMinus2ToThe31nd + fistpl izistep // note: we are relying on FP exceptions being turned + // off here to avoid range problems + movl izistep,%ebx // remains loaded for all spans + +LFNegSpanLoop: +// set up the initial 1/z value + fildl espan_t_v(%esi) + fildl espan_t_u(%esi) + movl espan_t_v(%esi),%ecx + movl C(d_pzbuffer),%edi + fmuls C(d_zistepu) + fxch %st(1) + fmuls C(d_zistepv) + fxch %st(1) + fadds C(d_ziorigin) + imull C(d_zrowbytes),%ecx + faddp %st(0),%st(1) + +// clamp if z is nearer than 2 (1/z > 0.5) + fcoms float_point5 + addl %ecx,%edi + movl espan_t_u(%esi),%edx + addl %edx,%edx // word count + movl espan_t_count(%esi),%ecx + addl %edx,%edi // pdest = &pdestspan[scans->u]; + pushl %esi // preserve spans pointer + fnstsw %ax + testb $0x45,%ah + jz LClampNeg + + fmuls Float2ToThe31nd + fistpl izi // note: we are relying on FP exceptions being turned + // off here to avoid problems when the span is closer + // than 1/(2**31) + movl izi,%edx + +// at this point: +// %ebx = izistep +// %ecx = count +// %edx = izi +// %edi = pdest + +LZDrawNeg: + +// do a single pixel up front, if necessary to dword align the destination + testl $2,%edi + jz LFNegMiddle + movl %edx,%eax + subl %ebx,%edx + shrl $16,%eax + decl %ecx + movw %ax,(%edi) + addl $2,%edi + +// do middle a pair of aligned dwords at a time +LFNegMiddle: + pushl %ecx + shrl $1,%ecx // count / 2 + jz LFNegLast // no aligned dwords to do + shrl $1,%ecx // (count / 2) / 2 + jnc LFNegMiddleLoop // even number of aligned dwords to do + + movl %edx,%eax + subl %ebx,%edx + shrl $16,%eax + movl %edx,%esi + subl %ebx,%edx + andl $0xFFFF0000,%esi + orl %esi,%eax + movl %eax,(%edi) + addl $4,%edi + andl %ecx,%ecx + jz LFNegLast + +LFNegMiddleLoop: + movl %edx,%eax + subl %ebx,%edx + shrl $16,%eax + movl %edx,%esi + subl %ebx,%edx + andl $0xFFFF0000,%esi + orl %esi,%eax + movl %edx,%ebp + movl %eax,(%edi) + subl %ebx,%edx + shrl $16,%ebp + movl %edx,%esi + subl %ebx,%edx + andl $0xFFFF0000,%esi + orl %esi,%ebp + movl %ebp,4(%edi) // FIXME: eliminate register contention + addl $8,%edi + + decl %ecx + jnz LFNegMiddleLoop + +LFNegLast: + popl %ecx // retrieve count + popl %esi // retrieve span pointer + +// do the last, unaligned pixel, if there is one + andl $1,%ecx // is there an odd pixel left to do? + jz LFNegSpanDone // no + shrl $16,%edx + movw %dx,(%edi) // do the final pixel's z + +LFNegSpanDone: + movl espan_t_pnext(%esi),%esi + testl %esi,%esi + jnz LFNegSpanLoop + +LFDone: + popl %ebx // restore register variables + popl %esi + popl %edi + popl %ebp // restore the caller's stack frame + ret + +#endif // id386 diff --git a/source/d_draw16.s b/source/d_draw16.s index dec75913..027e9e3e 100644 --- a/source/d_draw16.s +++ b/source/d_draw16.s @@ -1,980 +1,980 @@ -/* - d_draw16.S - - (description) - - Copyright (C) 1996-1997 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: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - - $Id: d_draw16.s,v 1.1.1.3 2004/10/13 18:54:29 vvd0 Exp $ -*/ -// d_draw16.s -// x86 assembly-language horizontal 8-bpp span-drawing code, with 16-pixel -// subdivision. - -#include "asm_i386.h" -#include "quakeasm.h" -#include "asm_draw.h" -#include "d_ifacea.h" - -#ifdef id386 - -//---------------------------------------------------------------------- -// 8-bpp horizontal span drawing code for polygons, with no transparency and -// 16-pixel subdivision. -// -// Assumes there is at least one span in pspans, and that every span -// contains at least one pixel -//---------------------------------------------------------------------- - - .data - - .text - -// out-of-line, rarely-needed clamping code - -LClampHigh0: - movl C(bbextents),%esi - jmp LClampReentry0 -LClampHighOrLow0: - jg LClampHigh0 - xorl %esi,%esi - jmp LClampReentry0 - -LClampHigh1: - movl C(bbextentt),%edx - jmp LClampReentry1 -LClampHighOrLow1: - jg LClampHigh1 - xorl %edx,%edx - jmp LClampReentry1 - -LClampLow2: - movl $4096,%ebp - jmp LClampReentry2 -LClampHigh2: - movl C(bbextents),%ebp - jmp LClampReentry2 - -LClampLow3: - movl $4096,%ecx - jmp LClampReentry3 -LClampHigh3: - movl C(bbextentt),%ecx - jmp LClampReentry3 - -LClampLow4: - movl $4096,%eax - jmp LClampReentry4 -LClampHigh4: - movl C(bbextents),%eax - jmp LClampReentry4 - -LClampLow5: - movl $4096,%ebx - jmp LClampReentry5 -LClampHigh5: - movl C(bbextentt),%ebx - jmp LClampReentry5 - - -#define pspans 4+16 - - .align 4 -.globl C(D_DrawSpans16) -C(D_DrawSpans16): - pushl %ebp // preserve caller's stack frame - pushl %edi - pushl %esi // preserve register variables - pushl %ebx - -// -// set up scaled-by-16 steps, for 16-long segments; also set up cacheblock -// and span list pointers -// -// TODO: any overlap from rearranging? - flds C(d_sdivzstepu) - fmuls fp_16 - movl C(cacheblock),%edx - flds C(d_tdivzstepu) - fmuls fp_16 - movl pspans(%esp),%ebx // point to the first span descriptor - flds C(d_zistepu) - fmuls fp_16 - movl %edx,pbase // pbase = cacheblock - fstps zi16stepu - fstps tdivz16stepu - fstps sdivz16stepu - -LSpanLoop: -// -// set up the initial s/z, t/z, and 1/z on the FP stack, and generate the -// initial s and t values -// -// FIXME: pipeline FILD? - fildl espan_t_v(%ebx) - fildl espan_t_u(%ebx) - - fld %st(1) // dv | du | dv - fmuls C(d_sdivzstepv) // dv*d_sdivzstepv | du | dv - fld %st(1) // du | dv*d_sdivzstepv | du | dv - fmuls C(d_sdivzstepu) // du*d_sdivzstepu | dv*d_sdivzstepv | du | dv - fld %st(2) // du | du*d_sdivzstepu | dv*d_sdivzstepv | du | dv - fmuls C(d_tdivzstepu) // du*d_tdivzstepu | du*d_sdivzstepu | - // dv*d_sdivzstepv | du | dv - fxch %st(1) // du*d_sdivzstepu | du*d_tdivzstepu | - // dv*d_sdivzstepv | du | dv - faddp %st(0),%st(2) // du*d_tdivzstepu | - // du*d_sdivzstepu + dv*d_sdivzstepv | du | dv - fxch %st(1) // du*d_sdivzstepu + dv*d_sdivzstepv | - // du*d_tdivzstepu | du | dv - fld %st(3) // dv | du*d_sdivzstepu + dv*d_sdivzstepv | - // du*d_tdivzstepu | du | dv - fmuls C(d_tdivzstepv) // dv*d_tdivzstepv | - // du*d_sdivzstepu + dv*d_sdivzstepv | - // du*d_tdivzstepu | du | dv - fxch %st(1) // du*d_sdivzstepu + dv*d_sdivzstepv | - // dv*d_tdivzstepv | du*d_tdivzstepu | du | dv - fadds C(d_sdivzorigin) // sdivz = d_sdivzorigin + dv*d_sdivzstepv + - // du*d_sdivzstepu; stays in %st(2) at end - fxch %st(4) // dv | dv*d_tdivzstepv | du*d_tdivzstepu | du | - // s/z - fmuls C(d_zistepv) // dv*d_zistepv | dv*d_tdivzstepv | - // du*d_tdivzstepu | du | s/z - fxch %st(1) // dv*d_tdivzstepv | dv*d_zistepv | - // du*d_tdivzstepu | du | s/z - faddp %st(0),%st(2) // dv*d_zistepv | - // dv*d_tdivzstepv + du*d_tdivzstepu | du | s/z - fxch %st(2) // du | dv*d_tdivzstepv + du*d_tdivzstepu | - // dv*d_zistepv | s/z - fmuls C(d_zistepu) // du*d_zistepu | - // dv*d_tdivzstepv + du*d_tdivzstepu | - // dv*d_zistepv | s/z - fxch %st(1) // dv*d_tdivzstepv + du*d_tdivzstepu | - // du*d_zistepu | dv*d_zistepv | s/z - fadds C(d_tdivzorigin) // tdivz = d_tdivzorigin + dv*d_tdivzstepv + - // du*d_tdivzstepu; stays in %st(1) at end - fxch %st(2) // dv*d_zistepv | du*d_zistepu | t/z | s/z - faddp %st(0),%st(1) // dv*d_zistepv + du*d_zistepu | t/z | s/z - - flds fp_64k // fp_64k | dv*d_zistepv + du*d_zistepu | t/z | s/z - fxch %st(1) // dv*d_zistepv + du*d_zistepu | fp_64k | t/z | s/z - fadds C(d_ziorigin) // zi = d_ziorigin + dv*d_zistepv + - // du*d_zistepu; stays in %st(0) at end - // 1/z | fp_64k | t/z | s/z -// -// calculate and clamp s & t -// - fdivr %st(0),%st(1) // 1/z | z*64k | t/z | s/z - -// -// point %edi to the first pixel in the span -// - movl C(d_viewbuffer),%ecx - movl espan_t_v(%ebx),%eax - movl %ebx,pspantemp // preserve spans pointer - - movl C(tadjust),%edx - movl C(sadjust),%esi - movl C(d_scantable)(,%eax,4),%edi // v * screenwidth - addl %ecx,%edi - movl espan_t_u(%ebx),%ecx - addl %ecx,%edi // pdest = &pdestspan[scans->u]; - movl espan_t_count(%ebx),%ecx - -// -// now start the FDIV for the end of the span -// - cmpl $16,%ecx - ja LSetupNotLast1 - - decl %ecx - jz LCleanup1 // if only one pixel, no need to start an FDIV - movl %ecx,spancountminus1 - -// finish up the s and t calcs - fxch %st(1) // z*64k | 1/z | t/z | s/z - - fld %st(0) // z*64k | z*64k | 1/z | t/z | s/z - fmul %st(4),%st(0) // s | z*64k | 1/z | t/z | s/z - fxch %st(1) // z*64k | s | 1/z | t/z | s/z - fmul %st(3),%st(0) // t | s | 1/z | t/z | s/z - fxch %st(1) // s | t | 1/z | t/z | s/z - fistpl s // 1/z | t | t/z | s/z - fistpl t // 1/z | t/z | s/z - - fildl spancountminus1 - - flds C(d_tdivzstepu) // C(d_tdivzstepu) | spancountminus1 - flds C(d_zistepu) // C(d_zistepu) | C(d_tdivzstepu) | spancountminus1 - fmul %st(2),%st(0) // C(d_zistepu)*scm1 | C(d_tdivzstepu) | scm1 - fxch %st(1) // C(d_tdivzstepu) | C(d_zistepu)*scm1 | scm1 - fmul %st(2),%st(0) // C(d_tdivzstepu)*scm1 | C(d_zistepu)*scm1 | scm1 - fxch %st(2) // scm1 | C(d_zistepu)*scm1 | C(d_tdivzstepu)*scm1 - fmuls C(d_sdivzstepu) // C(d_sdivzstepu)*scm1 | C(d_zistepu)*scm1 | - // C(d_tdivzstepu)*scm1 - fxch %st(1) // C(d_zistepu)*scm1 | C(d_sdivzstepu)*scm1 | - // C(d_tdivzstepu)*scm1 - faddp %st(0),%st(3) // C(d_sdivzstepu)*scm1 | C(d_tdivzstepu)*scm1 - fxch %st(1) // C(d_tdivzstepu)*scm1 | C(d_sdivzstepu)*scm1 - faddp %st(0),%st(3) // C(d_sdivzstepu)*scm1 - faddp %st(0),%st(3) - - flds fp_64k - fdiv %st(1),%st(0) // this is what we've gone to all this trouble to - // overlap - jmp LFDIVInFlight1 - -LCleanup1: -// finish up the s and t calcs - fxch %st(1) // z*64k | 1/z | t/z | s/z - - fld %st(0) // z*64k | z*64k | 1/z | t/z | s/z - fmul %st(4),%st(0) // s | z*64k | 1/z | t/z | s/z - fxch %st(1) // z*64k | s | 1/z | t/z | s/z - fmul %st(3),%st(0) // t | s | 1/z | t/z | s/z - fxch %st(1) // s | t | 1/z | t/z | s/z - fistpl s // 1/z | t | t/z | s/z - fistpl t // 1/z | t/z | s/z - jmp LFDIVInFlight1 - - .align 4 -LSetupNotLast1: -// finish up the s and t calcs - fxch %st(1) // z*64k | 1/z | t/z | s/z - - fld %st(0) // z*64k | z*64k | 1/z | t/z | s/z - fmul %st(4),%st(0) // s | z*64k | 1/z | t/z | s/z - fxch %st(1) // z*64k | s | 1/z | t/z | s/z - fmul %st(3),%st(0) // t | s | 1/z | t/z | s/z - fxch %st(1) // s | t | 1/z | t/z | s/z - fistpl s // 1/z | t | t/z | s/z - fistpl t // 1/z | t/z | s/z - - fadds zi16stepu - fxch %st(2) - fadds sdivz16stepu - fxch %st(2) - flds tdivz16stepu - faddp %st(0),%st(2) - flds fp_64k - fdiv %st(1),%st(0) // z = 1/1/z - // this is what we've gone to all this trouble to - // overlap -LFDIVInFlight1: - - addl s,%esi - addl t,%edx - movl C(bbextents),%ebx - movl C(bbextentt),%ebp - cmpl %ebx,%esi - ja LClampHighOrLow0 -LClampReentry0: - movl %esi,s - movl pbase,%ebx - shll $16,%esi - cmpl %ebp,%edx - movl %esi,sfracf - ja LClampHighOrLow1 -LClampReentry1: - movl %edx,t - movl s,%esi // sfrac = scans->sfrac; - shll $16,%edx - movl t,%eax // tfrac = scans->tfrac; - sarl $16,%esi - movl %edx,tfracf - -// -// calculate the texture starting address -// - sarl $16,%eax - movl C(cachewidth),%edx - imull %edx,%eax // (tfrac >> 16) * cachewidth - addl %ebx,%esi - addl %eax,%esi // psource = pbase + (sfrac >> 16) + - // ((tfrac >> 16) * cachewidth); -// -// determine whether last span or not -// - cmpl $16,%ecx - jna LLastSegment - -// -// not the last segment; do full 16-wide segment -// -LNotLastSegment: - -// -// advance s/z, t/z, and 1/z, and calculate s & t at end of span and steps to -// get there -// - -// pick up after the FDIV that was left in flight previously - - fld %st(0) // duplicate it - fmul %st(4),%st(0) // s = s/z * z - fxch %st(1) - fmul %st(3),%st(0) // t = t/z * z - fxch %st(1) - fistpl snext - fistpl tnext - movl snext,%eax - movl tnext,%edx - - movb (%esi),%bl // get first source texel - subl $16,%ecx // count off this segments' pixels - movl C(sadjust),%ebp - movl %ecx,counttemp // remember count of remaining pixels - - movl C(tadjust),%ecx - movb %bl,(%edi) // store first dest pixel - - addl %eax,%ebp - addl %edx,%ecx - - movl C(bbextents),%eax - movl C(bbextentt),%edx - - cmpl $4096,%ebp - jl LClampLow2 - cmpl %eax,%ebp - ja LClampHigh2 -LClampReentry2: - - cmpl $4096,%ecx - jl LClampLow3 - cmpl %edx,%ecx - ja LClampHigh3 -LClampReentry3: - - movl %ebp,snext - movl %ecx,tnext - - subl s,%ebp - subl t,%ecx - -// -// set up advancetable -// - movl %ecx,%eax - movl %ebp,%edx - sarl $20,%eax // tstep >>= 16; - jz LZero - sarl $20,%edx // sstep >>= 16; - movl C(cachewidth),%ebx - imull %ebx,%eax - jmp LSetUp1 - -LZero: - sarl $20,%edx // sstep >>= 16; - movl C(cachewidth),%ebx - -LSetUp1: - - addl %edx,%eax // add in sstep - // (tstep >> 16) * cachewidth + (sstep >> 16); - movl tfracf,%edx - movl %eax,advancetable+4 // advance base in t - addl %ebx,%eax // ((tstep >> 16) + 1) * cachewidth + - // (sstep >> 16); - shll $12,%ebp // left-justify sstep fractional part - movl sfracf,%ebx - shll $12,%ecx // left-justify tstep fractional part - movl %eax,advancetable // advance extra in t - - movl %ecx,tstep - addl %ecx,%edx // advance tfrac fractional part by tstep frac - - sbbl %ecx,%ecx // turn tstep carry into -1 (0 if none) - addl %ebp,%ebx // advance sfrac fractional part by sstep frac - adcl advancetable+4(,%ecx,4),%esi // point to next source texel - - addl tstep,%edx - sbbl %ecx,%ecx - movb (%esi),%al - addl %ebp,%ebx - movb %al,1(%edi) - adcl advancetable+4(,%ecx,4),%esi - - addl tstep,%edx - sbbl %ecx,%ecx - addl %ebp,%ebx - movb (%esi),%al - adcl advancetable+4(,%ecx,4),%esi - - addl tstep,%edx - sbbl %ecx,%ecx - movb %al,2(%edi) - addl %ebp,%ebx - movb (%esi),%al - adcl advancetable+4(,%ecx,4),%esi - - addl tstep,%edx - sbbl %ecx,%ecx - movb %al,3(%edi) - addl %ebp,%ebx - movb (%esi),%al - adcl advancetable+4(,%ecx,4),%esi - - addl tstep,%edx - sbbl %ecx,%ecx - movb %al,4(%edi) - addl %ebp,%ebx - movb (%esi),%al - adcl advancetable+4(,%ecx,4),%esi - - addl tstep,%edx - sbbl %ecx,%ecx - movb %al,5(%edi) - addl %ebp,%ebx - movb (%esi),%al - adcl advancetable+4(,%ecx,4),%esi - - addl tstep,%edx - sbbl %ecx,%ecx - movb %al,6(%edi) - addl %ebp,%ebx - movb (%esi),%al - adcl advancetable+4(,%ecx,4),%esi - - addl tstep,%edx - sbbl %ecx,%ecx - movb %al,7(%edi) - addl %ebp,%ebx - movb (%esi),%al - adcl advancetable+4(,%ecx,4),%esi - - -// -// start FDIV for end of next segment in flight, so it can overlap -// - movl counttemp,%ecx - cmpl $16,%ecx // more than one segment after this? - ja LSetupNotLast2 // yes - - decl %ecx - jz LFDIVInFlight2 // if only one pixel, no need to start an FDIV - movl %ecx,spancountminus1 - fildl spancountminus1 - - flds C(d_zistepu) // C(d_zistepu) | spancountminus1 - fmul %st(1),%st(0) // C(d_zistepu)*scm1 | scm1 - flds C(d_tdivzstepu) // C(d_tdivzstepu) | C(d_zistepu)*scm1 | scm1 - fmul %st(2),%st(0) // C(d_tdivzstepu)*scm1 | C(d_zistepu)*scm1 | scm1 - fxch %st(1) // C(d_zistepu)*scm1 | C(d_tdivzstepu)*scm1 | scm1 - faddp %st(0),%st(3) // C(d_tdivzstepu)*scm1 | scm1 - fxch %st(1) // scm1 | C(d_tdivzstepu)*scm1 - fmuls C(d_sdivzstepu) // C(d_sdivzstepu)*scm1 | C(d_tdivzstepu)*scm1 - fxch %st(1) // C(d_tdivzstepu)*scm1 | C(d_sdivzstepu)*scm1 - faddp %st(0),%st(3) // C(d_sdivzstepu)*scm1 - flds fp_64k // 64k | C(d_sdivzstepu)*scm1 - fxch %st(1) // C(d_sdivzstepu)*scm1 | 64k - faddp %st(0),%st(4) // 64k - - fdiv %st(1),%st(0) // this is what we've gone to all this trouble to - // overlap - jmp LFDIVInFlight2 - - .align 4 -LSetupNotLast2: - fadds zi16stepu - fxch %st(2) - fadds sdivz16stepu - fxch %st(2) - flds tdivz16stepu - faddp %st(0),%st(2) - flds fp_64k - fdiv %st(1),%st(0) // z = 1/1/z - // this is what we've gone to all this trouble to - // overlap -LFDIVInFlight2: - movl %ecx,counttemp - - addl tstep,%edx - sbbl %ecx,%ecx - movb %al,8(%edi) - addl %ebp,%ebx - movb (%esi),%al - adcl advancetable+4(,%ecx,4),%esi - - addl tstep,%edx - sbbl %ecx,%ecx - movb %al,9(%edi) - addl %ebp,%ebx - movb (%esi),%al - adcl advancetable+4(,%ecx,4),%esi - - addl tstep,%edx - sbbl %ecx,%ecx - movb %al,10(%edi) - addl %ebp,%ebx - movb (%esi),%al - adcl advancetable+4(,%ecx,4),%esi - - addl tstep,%edx - sbbl %ecx,%ecx - movb %al,11(%edi) - addl %ebp,%ebx - movb (%esi),%al - adcl advancetable+4(,%ecx,4),%esi - - addl tstep,%edx - sbbl %ecx,%ecx - movb %al,12(%edi) - addl %ebp,%ebx - movb (%esi),%al - adcl advancetable+4(,%ecx,4),%esi - - addl tstep,%edx - sbbl %ecx,%ecx - movb %al,13(%edi) - addl %ebp,%ebx - movb (%esi),%al - adcl advancetable+4(,%ecx,4),%esi - - addl tstep,%edx - sbbl %ecx,%ecx - movb %al,14(%edi) - addl %ebp,%ebx - movb (%esi),%al - adcl advancetable+4(,%ecx,4),%esi - - addl $16,%edi - movl %edx,tfracf - movl snext,%edx - movl %ebx,sfracf - movl tnext,%ebx - movl %edx,s - movl %ebx,t - - movl counttemp,%ecx // retrieve count - -// -// determine whether last span or not -// - cmpl $16,%ecx // are there multiple segments remaining? - movb %al,-1(%edi) - ja LNotLastSegment // yes - -// -// last segment of scan -// -LLastSegment: - -// -// advance s/z, t/z, and 1/z, and calculate s & t at end of span and steps to -// get there. The number of pixels left is variable, and we want to land on the -// last pixel, not step one past it, so we can't run into arithmetic problems -// - testl %ecx,%ecx - jz LNoSteps // just draw the last pixel and we're done - -// pick up after the FDIV that was left in flight previously - - - fld %st(0) // duplicate it - fmul %st(4),%st(0) // s = s/z * z - fxch %st(1) - fmul %st(3),%st(0) // t = t/z * z - fxch %st(1) - fistpl snext - fistpl tnext - - movb (%esi),%al // load first texel in segment - movl C(tadjust),%ebx - movb %al,(%edi) // store first pixel in segment - movl C(sadjust),%eax - - addl snext,%eax - addl tnext,%ebx - - movl C(bbextents),%ebp - movl C(bbextentt),%edx - - cmpl $4096,%eax - jl LClampLow4 - cmpl %ebp,%eax - ja LClampHigh4 -LClampReentry4: - movl %eax,snext - - cmpl $4096,%ebx - jl LClampLow5 - cmpl %edx,%ebx - ja LClampHigh5 -LClampReentry5: - - cmpl $1,%ecx // don't bother - je LOnlyOneStep // if two pixels in segment, there's only one step, - // of the segment length - subl s,%eax - subl t,%ebx - - addl %eax,%eax // convert to 15.17 format so multiply by 1.31 - addl %ebx,%ebx // reciprocal yields 16.48 - - imull reciprocal_table_16-8(,%ecx,4) // sstep = (snext - s) / - // (spancount-1) - movl %edx,%ebp - - movl %ebx,%eax - imull reciprocal_table_16-8(,%ecx,4) // tstep = (tnext - t) / - // (spancount-1) -LSetEntryvec: -// -// set up advancetable -// - movl entryvec_table_16(,%ecx,4),%ebx - movl %edx,%eax - movl %ebx,jumptemp // entry point into code for RET later - movl %ebp,%ecx - sarl $16,%edx // tstep >>= 16; - movl C(cachewidth),%ebx - sarl $16,%ecx // sstep >>= 16; - imull %ebx,%edx - - addl %ecx,%edx // add in sstep - // (tstep >> 16) * cachewidth + (sstep >> 16); - movl tfracf,%ecx - movl %edx,advancetable+4 // advance base in t - addl %ebx,%edx // ((tstep >> 16) + 1) * cachewidth + - // (sstep >> 16); - shll $16,%ebp // left-justify sstep fractional part - movl sfracf,%ebx - shll $16,%eax // left-justify tstep fractional part - movl %edx,advancetable // advance extra in t - - movl %eax,tstep - movl %ecx,%edx - addl %eax,%edx - sbbl %ecx,%ecx - addl %ebp,%ebx - adcl advancetable+4(,%ecx,4),%esi - - jmp *jumptemp // jump to the number-of-pixels handler - -//---------------------------------------- - -LNoSteps: - movb (%esi),%al // load first texel in segment - subl $15,%edi // adjust for hardwired offset - jmp LEndSpan - - -LOnlyOneStep: - subl s,%eax - subl t,%ebx - movl %eax,%ebp - movl %ebx,%edx - jmp LSetEntryvec - -//---------------------------------------- - -.globl Entry2_16, Entry3_16, Entry4_16, Entry5_16 -.globl Entry6_16, Entry7_16, Entry8_16, Entry9_16 -.globl Entry10_16, Entry11_16, Entry12_16, Entry13_16 -.globl Entry14_16, Entry15_16, Entry16_16 - -Entry2_16: - subl $14,%edi // adjust for hardwired offsets - movb (%esi),%al - jmp LEntry2_16 - -//---------------------------------------- - -Entry3_16: - subl $13,%edi // adjust for hardwired offsets - addl %eax,%edx - movb (%esi),%al - sbbl %ecx,%ecx - addl %ebp,%ebx - adcl advancetable+4(,%ecx,4),%esi - jmp LEntry3_16 - -//---------------------------------------- - -Entry4_16: - subl $12,%edi // adjust for hardwired offsets - addl %eax,%edx - movb (%esi),%al - sbbl %ecx,%ecx - addl %ebp,%ebx - adcl advancetable+4(,%ecx,4),%esi - addl tstep,%edx - jmp LEntry4_16 - -//---------------------------------------- - -Entry5_16: - subl $11,%edi // adjust for hardwired offsets - addl %eax,%edx - movb (%esi),%al - sbbl %ecx,%ecx - addl %ebp,%ebx - adcl advancetable+4(,%ecx,4),%esi - addl tstep,%edx - jmp LEntry5_16 - -//---------------------------------------- - -Entry6_16: - subl $10,%edi // adjust for hardwired offsets - addl %eax,%edx - movb (%esi),%al - sbbl %ecx,%ecx - addl %ebp,%ebx - adcl advancetable+4(,%ecx,4),%esi - addl tstep,%edx - jmp LEntry6_16 - -//---------------------------------------- - -Entry7_16: - subl $9,%edi // adjust for hardwired offsets - addl %eax,%edx - movb (%esi),%al - sbbl %ecx,%ecx - addl %ebp,%ebx - adcl advancetable+4(,%ecx,4),%esi - addl tstep,%edx - jmp LEntry7_16 - -//---------------------------------------- - -Entry8_16: - subl $8,%edi // adjust for hardwired offsets - addl %eax,%edx - movb (%esi),%al - sbbl %ecx,%ecx - addl %ebp,%ebx - adcl advancetable+4(,%ecx,4),%esi - addl tstep,%edx - jmp LEntry8_16 - -//---------------------------------------- - -Entry9_16: - subl $7,%edi // adjust for hardwired offsets - addl %eax,%edx - movb (%esi),%al - sbbl %ecx,%ecx - addl %ebp,%ebx - adcl advancetable+4(,%ecx,4),%esi - addl tstep,%edx - jmp LEntry9_16 - -//---------------------------------------- - -Entry10_16: - subl $6,%edi // adjust for hardwired offsets - addl %eax,%edx - movb (%esi),%al - sbbl %ecx,%ecx - addl %ebp,%ebx - adcl advancetable+4(,%ecx,4),%esi - addl tstep,%edx - jmp LEntry10_16 - -//---------------------------------------- - -Entry11_16: - subl $5,%edi // adjust for hardwired offsets - addl %eax,%edx - movb (%esi),%al - sbbl %ecx,%ecx - addl %ebp,%ebx - adcl advancetable+4(,%ecx,4),%esi - addl tstep,%edx - jmp LEntry11_16 - -//---------------------------------------- - -Entry12_16: - subl $4,%edi // adjust for hardwired offsets - addl %eax,%edx - movb (%esi),%al - sbbl %ecx,%ecx - addl %ebp,%ebx - adcl advancetable+4(,%ecx,4),%esi - addl tstep,%edx - jmp LEntry12_16 - -//---------------------------------------- - -Entry13_16: - subl $3,%edi // adjust for hardwired offsets - addl %eax,%edx - movb (%esi),%al - sbbl %ecx,%ecx - addl %ebp,%ebx - adcl advancetable+4(,%ecx,4),%esi - addl tstep,%edx - jmp LEntry13_16 - -//---------------------------------------- - -Entry14_16: - subl $2,%edi // adjust for hardwired offsets - addl %eax,%edx - movb (%esi),%al - sbbl %ecx,%ecx - addl %ebp,%ebx - adcl advancetable+4(,%ecx,4),%esi - addl tstep,%edx - jmp LEntry14_16 - -//---------------------------------------- - -Entry15_16: - decl %edi // adjust for hardwired offsets - addl %eax,%edx - movb (%esi),%al - sbbl %ecx,%ecx - addl %ebp,%ebx - adcl advancetable+4(,%ecx,4),%esi - addl tstep,%edx - jmp LEntry15_16 - -//---------------------------------------- - -Entry16_16: - addl %eax,%edx - movb (%esi),%al - sbbl %ecx,%ecx - addl %ebp,%ebx - adcl advancetable+4(,%ecx,4),%esi - - addl tstep,%edx - sbbl %ecx,%ecx - movb %al,1(%edi) - addl %ebp,%ebx - movb (%esi),%al - adcl advancetable+4(,%ecx,4),%esi - addl tstep,%edx -LEntry15_16: - sbbl %ecx,%ecx - movb %al,2(%edi) - addl %ebp,%ebx - movb (%esi),%al - adcl advancetable+4(,%ecx,4),%esi - addl tstep,%edx -LEntry14_16: - sbbl %ecx,%ecx - movb %al,3(%edi) - addl %ebp,%ebx - movb (%esi),%al - adcl advancetable+4(,%ecx,4),%esi - addl tstep,%edx -LEntry13_16: - sbbl %ecx,%ecx - movb %al,4(%edi) - addl %ebp,%ebx - movb (%esi),%al - adcl advancetable+4(,%ecx,4),%esi - addl tstep,%edx -LEntry12_16: - sbbl %ecx,%ecx - movb %al,5(%edi) - addl %ebp,%ebx - movb (%esi),%al - adcl advancetable+4(,%ecx,4),%esi - addl tstep,%edx -LEntry11_16: - sbbl %ecx,%ecx - movb %al,6(%edi) - addl %ebp,%ebx - movb (%esi),%al - adcl advancetable+4(,%ecx,4),%esi - addl tstep,%edx -LEntry10_16: - sbbl %ecx,%ecx - movb %al,7(%edi) - addl %ebp,%ebx - movb (%esi),%al - adcl advancetable+4(,%ecx,4),%esi - addl tstep,%edx -LEntry9_16: - sbbl %ecx,%ecx - movb %al,8(%edi) - addl %ebp,%ebx - movb (%esi),%al - adcl advancetable+4(,%ecx,4),%esi - addl tstep,%edx -LEntry8_16: - sbbl %ecx,%ecx - movb %al,9(%edi) - addl %ebp,%ebx - movb (%esi),%al - adcl advancetable+4(,%ecx,4),%esi - addl tstep,%edx -LEntry7_16: - sbbl %ecx,%ecx - movb %al,10(%edi) - addl %ebp,%ebx - movb (%esi),%al - adcl advancetable+4(,%ecx,4),%esi - addl tstep,%edx -LEntry6_16: - sbbl %ecx,%ecx - movb %al,11(%edi) - addl %ebp,%ebx - movb (%esi),%al - adcl advancetable+4(,%ecx,4),%esi - addl tstep,%edx -LEntry5_16: - sbbl %ecx,%ecx - movb %al,12(%edi) - addl %ebp,%ebx - movb (%esi),%al - adcl advancetable+4(,%ecx,4),%esi - addl tstep,%edx -LEntry4_16: - sbbl %ecx,%ecx - movb %al,13(%edi) - addl %ebp,%ebx - movb (%esi),%al - adcl advancetable+4(,%ecx,4),%esi -LEntry3_16: - movb %al,14(%edi) - movb (%esi),%al -LEntry2_16: - -LEndSpan: - -// -// clear s/z, t/z, 1/z from FP stack -// - fstp %st(0) - fstp %st(0) - fstp %st(0) - - movl pspantemp,%ebx // restore spans pointer - movl espan_t_pnext(%ebx),%ebx // point to next span - testl %ebx,%ebx // any more spans? - movb %al,15(%edi) - jnz LSpanLoop // more spans - - popl %ebx // restore register variables - popl %esi - popl %edi - popl %ebp // restore the caller's stack frame - ret - -#endif // id386 +/* + d_draw16.S + + (description) + + Copyright (C) 1996-1997 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id: d_draw16.s,v 1.1.1.4 2004/10/18 17:44:26 vvd0 Exp $ +*/ +// d_draw16.s +// x86 assembly-language horizontal 8-bpp span-drawing code, with 16-pixel +// subdivision. + +#include "asm_i386.h" +#include "quakeasm.h" +#include "asm_draw.h" +#include "d_ifacea.h" + +#ifdef id386 + +//---------------------------------------------------------------------- +// 8-bpp horizontal span drawing code for polygons, with no transparency and +// 16-pixel subdivision. +// +// Assumes there is at least one span in pspans, and that every span +// contains at least one pixel +//---------------------------------------------------------------------- + + .data + + .text + +// out-of-line, rarely-needed clamping code + +LClampHigh0: + movl C(bbextents),%esi + jmp LClampReentry0 +LClampHighOrLow0: + jg LClampHigh0 + xorl %esi,%esi + jmp LClampReentry0 + +LClampHigh1: + movl C(bbextentt),%edx + jmp LClampReentry1 +LClampHighOrLow1: + jg LClampHigh1 + xorl %edx,%edx + jmp LClampReentry1 + +LClampLow2: + movl $4096,%ebp + jmp LClampReentry2 +LClampHigh2: + movl C(bbextents),%ebp + jmp LClampReentry2 + +LClampLow3: + movl $4096,%ecx + jmp LClampReentry3 +LClampHigh3: + movl C(bbextentt),%ecx + jmp LClampReentry3 + +LClampLow4: + movl $4096,%eax + jmp LClampReentry4 +LClampHigh4: + movl C(bbextents),%eax + jmp LClampReentry4 + +LClampLow5: + movl $4096,%ebx + jmp LClampReentry5 +LClampHigh5: + movl C(bbextentt),%ebx + jmp LClampReentry5 + + +#define pspans 4+16 + + .align 4 +.globl C(D_DrawSpans16) +C(D_DrawSpans16): + pushl %ebp // preserve caller's stack frame + pushl %edi + pushl %esi // preserve register variables + pushl %ebx + +// +// set up scaled-by-16 steps, for 16-long segments; also set up cacheblock +// and span list pointers +// +// TODO: any overlap from rearranging? + flds C(d_sdivzstepu) + fmuls fp_16 + movl C(cacheblock),%edx + flds C(d_tdivzstepu) + fmuls fp_16 + movl pspans(%esp),%ebx // point to the first span descriptor + flds C(d_zistepu) + fmuls fp_16 + movl %edx,pbase // pbase = cacheblock + fstps zi16stepu + fstps tdivz16stepu + fstps sdivz16stepu + +LSpanLoop: +// +// set up the initial s/z, t/z, and 1/z on the FP stack, and generate the +// initial s and t values +// +// FIXME: pipeline FILD? + fildl espan_t_v(%ebx) + fildl espan_t_u(%ebx) + + fld %st(1) // dv | du | dv + fmuls C(d_sdivzstepv) // dv*d_sdivzstepv | du | dv + fld %st(1) // du | dv*d_sdivzstepv | du | dv + fmuls C(d_sdivzstepu) // du*d_sdivzstepu | dv*d_sdivzstepv | du | dv + fld %st(2) // du | du*d_sdivzstepu | dv*d_sdivzstepv | du | dv + fmuls C(d_tdivzstepu) // du*d_tdivzstepu | du*d_sdivzstepu | + // dv*d_sdivzstepv | du | dv + fxch %st(1) // du*d_sdivzstepu | du*d_tdivzstepu | + // dv*d_sdivzstepv | du | dv + faddp %st(0),%st(2) // du*d_tdivzstepu | + // du*d_sdivzstepu + dv*d_sdivzstepv | du | dv + fxch %st(1) // du*d_sdivzstepu + dv*d_sdivzstepv | + // du*d_tdivzstepu | du | dv + fld %st(3) // dv | du*d_sdivzstepu + dv*d_sdivzstepv | + // du*d_tdivzstepu | du | dv + fmuls C(d_tdivzstepv) // dv*d_tdivzstepv | + // du*d_sdivzstepu + dv*d_sdivzstepv | + // du*d_tdivzstepu | du | dv + fxch %st(1) // du*d_sdivzstepu + dv*d_sdivzstepv | + // dv*d_tdivzstepv | du*d_tdivzstepu | du | dv + fadds C(d_sdivzorigin) // sdivz = d_sdivzorigin + dv*d_sdivzstepv + + // du*d_sdivzstepu; stays in %st(2) at end + fxch %st(4) // dv | dv*d_tdivzstepv | du*d_tdivzstepu | du | + // s/z + fmuls C(d_zistepv) // dv*d_zistepv | dv*d_tdivzstepv | + // du*d_tdivzstepu | du | s/z + fxch %st(1) // dv*d_tdivzstepv | dv*d_zistepv | + // du*d_tdivzstepu | du | s/z + faddp %st(0),%st(2) // dv*d_zistepv | + // dv*d_tdivzstepv + du*d_tdivzstepu | du | s/z + fxch %st(2) // du | dv*d_tdivzstepv + du*d_tdivzstepu | + // dv*d_zistepv | s/z + fmuls C(d_zistepu) // du*d_zistepu | + // dv*d_tdivzstepv + du*d_tdivzstepu | + // dv*d_zistepv | s/z + fxch %st(1) // dv*d_tdivzstepv + du*d_tdivzstepu | + // du*d_zistepu | dv*d_zistepv | s/z + fadds C(d_tdivzorigin) // tdivz = d_tdivzorigin + dv*d_tdivzstepv + + // du*d_tdivzstepu; stays in %st(1) at end + fxch %st(2) // dv*d_zistepv | du*d_zistepu | t/z | s/z + faddp %st(0),%st(1) // dv*d_zistepv + du*d_zistepu | t/z | s/z + + flds fp_64k // fp_64k | dv*d_zistepv + du*d_zistepu | t/z | s/z + fxch %st(1) // dv*d_zistepv + du*d_zistepu | fp_64k | t/z | s/z + fadds C(d_ziorigin) // zi = d_ziorigin + dv*d_zistepv + + // du*d_zistepu; stays in %st(0) at end + // 1/z | fp_64k | t/z | s/z +// +// calculate and clamp s & t +// + fdivr %st(0),%st(1) // 1/z | z*64k | t/z | s/z + +// +// point %edi to the first pixel in the span +// + movl C(d_viewbuffer),%ecx + movl espan_t_v(%ebx),%eax + movl %ebx,pspantemp // preserve spans pointer + + movl C(tadjust),%edx + movl C(sadjust),%esi + movl C(d_scantable)(,%eax,4),%edi // v * screenwidth + addl %ecx,%edi + movl espan_t_u(%ebx),%ecx + addl %ecx,%edi // pdest = &pdestspan[scans->u]; + movl espan_t_count(%ebx),%ecx + +// +// now start the FDIV for the end of the span +// + cmpl $16,%ecx + ja LSetupNotLast1 + + decl %ecx + jz LCleanup1 // if only one pixel, no need to start an FDIV + movl %ecx,spancountminus1 + +// finish up the s and t calcs + fxch %st(1) // z*64k | 1/z | t/z | s/z + + fld %st(0) // z*64k | z*64k | 1/z | t/z | s/z + fmul %st(4),%st(0) // s | z*64k | 1/z | t/z | s/z + fxch %st(1) // z*64k | s | 1/z | t/z | s/z + fmul %st(3),%st(0) // t | s | 1/z | t/z | s/z + fxch %st(1) // s | t | 1/z | t/z | s/z + fistpl s // 1/z | t | t/z | s/z + fistpl t // 1/z | t/z | s/z + + fildl spancountminus1 + + flds C(d_tdivzstepu) // C(d_tdivzstepu) | spancountminus1 + flds C(d_zistepu) // C(d_zistepu) | C(d_tdivzstepu) | spancountminus1 + fmul %st(2),%st(0) // C(d_zistepu)*scm1 | C(d_tdivzstepu) | scm1 + fxch %st(1) // C(d_tdivzstepu) | C(d_zistepu)*scm1 | scm1 + fmul %st(2),%st(0) // C(d_tdivzstepu)*scm1 | C(d_zistepu)*scm1 | scm1 + fxch %st(2) // scm1 | C(d_zistepu)*scm1 | C(d_tdivzstepu)*scm1 + fmuls C(d_sdivzstepu) // C(d_sdivzstepu)*scm1 | C(d_zistepu)*scm1 | + // C(d_tdivzstepu)*scm1 + fxch %st(1) // C(d_zistepu)*scm1 | C(d_sdivzstepu)*scm1 | + // C(d_tdivzstepu)*scm1 + faddp %st(0),%st(3) // C(d_sdivzstepu)*scm1 | C(d_tdivzstepu)*scm1 + fxch %st(1) // C(d_tdivzstepu)*scm1 | C(d_sdivzstepu)*scm1 + faddp %st(0),%st(3) // C(d_sdivzstepu)*scm1 + faddp %st(0),%st(3) + + flds fp_64k + fdiv %st(1),%st(0) // this is what we've gone to all this trouble to + // overlap + jmp LFDIVInFlight1 + +LCleanup1: +// finish up the s and t calcs + fxch %st(1) // z*64k | 1/z | t/z | s/z + + fld %st(0) // z*64k | z*64k | 1/z | t/z | s/z + fmul %st(4),%st(0) // s | z*64k | 1/z | t/z | s/z + fxch %st(1) // z*64k | s | 1/z | t/z | s/z + fmul %st(3),%st(0) // t | s | 1/z | t/z | s/z + fxch %st(1) // s | t | 1/z | t/z | s/z + fistpl s // 1/z | t | t/z | s/z + fistpl t // 1/z | t/z | s/z + jmp LFDIVInFlight1 + + .align 4 +LSetupNotLast1: +// finish up the s and t calcs + fxch %st(1) // z*64k | 1/z | t/z | s/z + + fld %st(0) // z*64k | z*64k | 1/z | t/z | s/z + fmul %st(4),%st(0) // s | z*64k | 1/z | t/z | s/z + fxch %st(1) // z*64k | s | 1/z | t/z | s/z + fmul %st(3),%st(0) // t | s | 1/z | t/z | s/z + fxch %st(1) // s | t | 1/z | t/z | s/z + fistpl s // 1/z | t | t/z | s/z + fistpl t // 1/z | t/z | s/z + + fadds zi16stepu + fxch %st(2) + fadds sdivz16stepu + fxch %st(2) + flds tdivz16stepu + faddp %st(0),%st(2) + flds fp_64k + fdiv %st(1),%st(0) // z = 1/1/z + // this is what we've gone to all this trouble to + // overlap +LFDIVInFlight1: + + addl s,%esi + addl t,%edx + movl C(bbextents),%ebx + movl C(bbextentt),%ebp + cmpl %ebx,%esi + ja LClampHighOrLow0 +LClampReentry0: + movl %esi,s + movl pbase,%ebx + shll $16,%esi + cmpl %ebp,%edx + movl %esi,sfracf + ja LClampHighOrLow1 +LClampReentry1: + movl %edx,t + movl s,%esi // sfrac = scans->sfrac; + shll $16,%edx + movl t,%eax // tfrac = scans->tfrac; + sarl $16,%esi + movl %edx,tfracf + +// +// calculate the texture starting address +// + sarl $16,%eax + movl C(cachewidth),%edx + imull %edx,%eax // (tfrac >> 16) * cachewidth + addl %ebx,%esi + addl %eax,%esi // psource = pbase + (sfrac >> 16) + + // ((tfrac >> 16) * cachewidth); +// +// determine whether last span or not +// + cmpl $16,%ecx + jna LLastSegment + +// +// not the last segment; do full 16-wide segment +// +LNotLastSegment: + +// +// advance s/z, t/z, and 1/z, and calculate s & t at end of span and steps to +// get there +// + +// pick up after the FDIV that was left in flight previously + + fld %st(0) // duplicate it + fmul %st(4),%st(0) // s = s/z * z + fxch %st(1) + fmul %st(3),%st(0) // t = t/z * z + fxch %st(1) + fistpl snext + fistpl tnext + movl snext,%eax + movl tnext,%edx + + movb (%esi),%bl // get first source texel + subl $16,%ecx // count off this segments' pixels + movl C(sadjust),%ebp + movl %ecx,counttemp // remember count of remaining pixels + + movl C(tadjust),%ecx + movb %bl,(%edi) // store first dest pixel + + addl %eax,%ebp + addl %edx,%ecx + + movl C(bbextents),%eax + movl C(bbextentt),%edx + + cmpl $4096,%ebp + jl LClampLow2 + cmpl %eax,%ebp + ja LClampHigh2 +LClampReentry2: + + cmpl $4096,%ecx + jl LClampLow3 + cmpl %edx,%ecx + ja LClampHigh3 +LClampReentry3: + + movl %ebp,snext + movl %ecx,tnext + + subl s,%ebp + subl t,%ecx + +// +// set up advancetable +// + movl %ecx,%eax + movl %ebp,%edx + sarl $20,%eax // tstep >>= 16; + jz LZero + sarl $20,%edx // sstep >>= 16; + movl C(cachewidth),%ebx + imull %ebx,%eax + jmp LSetUp1 + +LZero: + sarl $20,%edx // sstep >>= 16; + movl C(cachewidth),%ebx + +LSetUp1: + + addl %edx,%eax // add in sstep + // (tstep >> 16) * cachewidth + (sstep >> 16); + movl tfracf,%edx + movl %eax,advancetable+4 // advance base in t + addl %ebx,%eax // ((tstep >> 16) + 1) * cachewidth + + // (sstep >> 16); + shll $12,%ebp // left-justify sstep fractional part + movl sfracf,%ebx + shll $12,%ecx // left-justify tstep fractional part + movl %eax,advancetable // advance extra in t + + movl %ecx,tstep + addl %ecx,%edx // advance tfrac fractional part by tstep frac + + sbbl %ecx,%ecx // turn tstep carry into -1 (0 if none) + addl %ebp,%ebx // advance sfrac fractional part by sstep frac + adcl advancetable+4(,%ecx,4),%esi // point to next source texel + + addl tstep,%edx + sbbl %ecx,%ecx + movb (%esi),%al + addl %ebp,%ebx + movb %al,1(%edi) + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,2(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,3(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,4(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,5(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,6(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,7(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + +// +// start FDIV for end of next segment in flight, so it can overlap +// + movl counttemp,%ecx + cmpl $16,%ecx // more than one segment after this? + ja LSetupNotLast2 // yes + + decl %ecx + jz LFDIVInFlight2 // if only one pixel, no need to start an FDIV + movl %ecx,spancountminus1 + fildl spancountminus1 + + flds C(d_zistepu) // C(d_zistepu) | spancountminus1 + fmul %st(1),%st(0) // C(d_zistepu)*scm1 | scm1 + flds C(d_tdivzstepu) // C(d_tdivzstepu) | C(d_zistepu)*scm1 | scm1 + fmul %st(2),%st(0) // C(d_tdivzstepu)*scm1 | C(d_zistepu)*scm1 | scm1 + fxch %st(1) // C(d_zistepu)*scm1 | C(d_tdivzstepu)*scm1 | scm1 + faddp %st(0),%st(3) // C(d_tdivzstepu)*scm1 | scm1 + fxch %st(1) // scm1 | C(d_tdivzstepu)*scm1 + fmuls C(d_sdivzstepu) // C(d_sdivzstepu)*scm1 | C(d_tdivzstepu)*scm1 + fxch %st(1) // C(d_tdivzstepu)*scm1 | C(d_sdivzstepu)*scm1 + faddp %st(0),%st(3) // C(d_sdivzstepu)*scm1 + flds fp_64k // 64k | C(d_sdivzstepu)*scm1 + fxch %st(1) // C(d_sdivzstepu)*scm1 | 64k + faddp %st(0),%st(4) // 64k + + fdiv %st(1),%st(0) // this is what we've gone to all this trouble to + // overlap + jmp LFDIVInFlight2 + + .align 4 +LSetupNotLast2: + fadds zi16stepu + fxch %st(2) + fadds sdivz16stepu + fxch %st(2) + flds tdivz16stepu + faddp %st(0),%st(2) + flds fp_64k + fdiv %st(1),%st(0) // z = 1/1/z + // this is what we've gone to all this trouble to + // overlap +LFDIVInFlight2: + movl %ecx,counttemp + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,8(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,9(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,10(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,11(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,12(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,13(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,14(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl $16,%edi + movl %edx,tfracf + movl snext,%edx + movl %ebx,sfracf + movl tnext,%ebx + movl %edx,s + movl %ebx,t + + movl counttemp,%ecx // retrieve count + +// +// determine whether last span or not +// + cmpl $16,%ecx // are there multiple segments remaining? + movb %al,-1(%edi) + ja LNotLastSegment // yes + +// +// last segment of scan +// +LLastSegment: + +// +// advance s/z, t/z, and 1/z, and calculate s & t at end of span and steps to +// get there. The number of pixels left is variable, and we want to land on the +// last pixel, not step one past it, so we can't run into arithmetic problems +// + testl %ecx,%ecx + jz LNoSteps // just draw the last pixel and we're done + +// pick up after the FDIV that was left in flight previously + + + fld %st(0) // duplicate it + fmul %st(4),%st(0) // s = s/z * z + fxch %st(1) + fmul %st(3),%st(0) // t = t/z * z + fxch %st(1) + fistpl snext + fistpl tnext + + movb (%esi),%al // load first texel in segment + movl C(tadjust),%ebx + movb %al,(%edi) // store first pixel in segment + movl C(sadjust),%eax + + addl snext,%eax + addl tnext,%ebx + + movl C(bbextents),%ebp + movl C(bbextentt),%edx + + cmpl $4096,%eax + jl LClampLow4 + cmpl %ebp,%eax + ja LClampHigh4 +LClampReentry4: + movl %eax,snext + + cmpl $4096,%ebx + jl LClampLow5 + cmpl %edx,%ebx + ja LClampHigh5 +LClampReentry5: + + cmpl $1,%ecx // don't bother + je LOnlyOneStep // if two pixels in segment, there's only one step, + // of the segment length + subl s,%eax + subl t,%ebx + + addl %eax,%eax // convert to 15.17 format so multiply by 1.31 + addl %ebx,%ebx // reciprocal yields 16.48 + + imull reciprocal_table_16-8(,%ecx,4) // sstep = (snext - s) / + // (spancount-1) + movl %edx,%ebp + + movl %ebx,%eax + imull reciprocal_table_16-8(,%ecx,4) // tstep = (tnext - t) / + // (spancount-1) +LSetEntryvec: +// +// set up advancetable +// + movl entryvec_table_16(,%ecx,4),%ebx + movl %edx,%eax + movl %ebx,jumptemp // entry point into code for RET later + movl %ebp,%ecx + sarl $16,%edx // tstep >>= 16; + movl C(cachewidth),%ebx + sarl $16,%ecx // sstep >>= 16; + imull %ebx,%edx + + addl %ecx,%edx // add in sstep + // (tstep >> 16) * cachewidth + (sstep >> 16); + movl tfracf,%ecx + movl %edx,advancetable+4 // advance base in t + addl %ebx,%edx // ((tstep >> 16) + 1) * cachewidth + + // (sstep >> 16); + shll $16,%ebp // left-justify sstep fractional part + movl sfracf,%ebx + shll $16,%eax // left-justify tstep fractional part + movl %edx,advancetable // advance extra in t + + movl %eax,tstep + movl %ecx,%edx + addl %eax,%edx + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + + jmp *jumptemp // jump to the number-of-pixels handler + +//---------------------------------------- + +LNoSteps: + movb (%esi),%al // load first texel in segment + subl $15,%edi // adjust for hardwired offset + jmp LEndSpan + + +LOnlyOneStep: + subl s,%eax + subl t,%ebx + movl %eax,%ebp + movl %ebx,%edx + jmp LSetEntryvec + +//---------------------------------------- + +.globl Entry2_16, Entry3_16, Entry4_16, Entry5_16 +.globl Entry6_16, Entry7_16, Entry8_16, Entry9_16 +.globl Entry10_16, Entry11_16, Entry12_16, Entry13_16 +.globl Entry14_16, Entry15_16, Entry16_16 + +Entry2_16: + subl $14,%edi // adjust for hardwired offsets + movb (%esi),%al + jmp LEntry2_16 + +//---------------------------------------- + +Entry3_16: + subl $13,%edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + jmp LEntry3_16 + +//---------------------------------------- + +Entry4_16: + subl $12,%edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx + jmp LEntry4_16 + +//---------------------------------------- + +Entry5_16: + subl $11,%edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx + jmp LEntry5_16 + +//---------------------------------------- + +Entry6_16: + subl $10,%edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx + jmp LEntry6_16 + +//---------------------------------------- + +Entry7_16: + subl $9,%edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx + jmp LEntry7_16 + +//---------------------------------------- + +Entry8_16: + subl $8,%edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx + jmp LEntry8_16 + +//---------------------------------------- + +Entry9_16: + subl $7,%edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx + jmp LEntry9_16 + +//---------------------------------------- + +Entry10_16: + subl $6,%edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx + jmp LEntry10_16 + +//---------------------------------------- + +Entry11_16: + subl $5,%edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx + jmp LEntry11_16 + +//---------------------------------------- + +Entry12_16: + subl $4,%edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx + jmp LEntry12_16 + +//---------------------------------------- + +Entry13_16: + subl $3,%edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx + jmp LEntry13_16 + +//---------------------------------------- + +Entry14_16: + subl $2,%edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx + jmp LEntry14_16 + +//---------------------------------------- + +Entry15_16: + decl %edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx + jmp LEntry15_16 + +//---------------------------------------- + +Entry16_16: + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,1(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx +LEntry15_16: + sbbl %ecx,%ecx + movb %al,2(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx +LEntry14_16: + sbbl %ecx,%ecx + movb %al,3(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx +LEntry13_16: + sbbl %ecx,%ecx + movb %al,4(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx +LEntry12_16: + sbbl %ecx,%ecx + movb %al,5(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx +LEntry11_16: + sbbl %ecx,%ecx + movb %al,6(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx +LEntry10_16: + sbbl %ecx,%ecx + movb %al,7(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx +LEntry9_16: + sbbl %ecx,%ecx + movb %al,8(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx +LEntry8_16: + sbbl %ecx,%ecx + movb %al,9(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx +LEntry7_16: + sbbl %ecx,%ecx + movb %al,10(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx +LEntry6_16: + sbbl %ecx,%ecx + movb %al,11(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx +LEntry5_16: + sbbl %ecx,%ecx + movb %al,12(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx +LEntry4_16: + sbbl %ecx,%ecx + movb %al,13(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi +LEntry3_16: + movb %al,14(%edi) + movb (%esi),%al +LEntry2_16: + +LEndSpan: + +// +// clear s/z, t/z, 1/z from FP stack +// + fstp %st(0) + fstp %st(0) + fstp %st(0) + + movl pspantemp,%ebx // restore spans pointer + movl espan_t_pnext(%ebx),%ebx // point to next span + testl %ebx,%ebx // any more spans? + movb %al,15(%edi) + jnz LSpanLoop // more spans + + popl %ebx // restore register variables + popl %esi + popl %edi + popl %ebp // restore the caller's stack frame + ret + +#endif // id386 diff --git a/source/d_edge.c b/source/d_edge.c index 1a8c97b6..ca35b71f 100644 --- a/source/d_edge.c +++ b/source/d_edge.c @@ -1,341 +1,341 @@ -/* -Copyright (C) 1996-1997 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_edge.c - -#include "quakedef.h" -#include "d_local.h" - -static int miplevel; - -float scale_for_mip; -int screenwidth; -int ubasestep, errorterm, erroradjustup, erroradjustdown; -int vstartscan; - -// FIXME: should go away -extern void R_RotateBmodel (void); -extern void R_TransformFrustum (void); - -vec3_t transformed_modelorg; - -/* -============== -D_DrawPoly - -============== -*/ -void D_DrawPoly (void) -{ -// this driver takes spans, not polygons -} - - -/* -============= -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_DrawSolidSurface -============== -*/ - -// FIXME: clean this up - -void D_DrawSolidSurface (surf_t *surf, int color) -{ - espan_t *span; - byte *pdest; - int u, u2, pix; - - pix = (color<<24) | (color<<16) | (color<<8) | color; - for (span=surf->spans ; span ; span=span->pnext) - { - pdest = (byte *)d_viewbuffer + screenwidth*span->v; - u = span->u; - u2 = span->u + span->count - 1; - ((byte *)pdest)[u] = pix; - - if (u2 - u < 8) - { - for (u++ ; u <= u2 ; u++) - ((byte *)pdest)[u] = pix; - } - else - { - for (u++ ; u & 3 ; u++) - ((byte *)pdest)[u] = pix; - - u2 -= 4; - for ( ; u <= u2 ; u+=4) - *(int *)((byte *)pdest + u) = pix; - u2 += 4; - for ( ; u <= u2 ; u++) - ((byte *)pdest)[u] = pix; - } - } -} - - -/* -============== -D_CalcGradients -============== -*/ -void D_CalcGradients (msurface_t *pface) -{ - mplane_t *pplane; - float mipscale; - vec3_t p_temp1; - vec3_t p_saxis, p_taxis; - float t; - - pplane = pface->plane; - - 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 = ((fixed16_t)(DotProduct (p_temp1, p_saxis) * 0x10000 + 0.5)) - - ((pface->texturemins[0] << 16) >> miplevel) - + pface->texinfo->vecs[0][3]*t; - tadjust = ((fixed16_t)(DotProduct (p_temp1, p_taxis) * 0x10000 + 0.5)) - - ((pface->texturemins[1] << 16) >> miplevel) - + pface->texinfo->vecs[1][3]*t; - -// -// -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_DrawSurfaces -============== -*/ -void D_DrawSurfaces (void) -{ - surf_t *s; - msurface_t *pface; - surfcache_t *pcurrentcache; - vec3_t world_transformed_modelorg; - vec3_t local_modelorg; - - currententity = &r_worldentity; - TransformVector (modelorg, transformed_modelorg); - VectorCopy (transformed_modelorg, world_transformed_modelorg); - -// TODO: could preset a lot of this at mode set time - if (r_drawflat.value) - { - for (s = &surfaces[1] ; sspans) - continue; - - d_zistepu = s->d_zistepu; - d_zistepv = s->d_zistepv; - d_ziorigin = s->d_ziorigin; - -#ifdef __alpha__ - D_DrawSolidSurface (s, (int)((long)s->data & 0xFF)); -#else - D_DrawSolidSurface (s, (int)s->data & 0xFF); -#endif - D_DrawZSpans (s->spans); - } - } - else - { - for (s = &surfaces[1] ; sspans) - continue; - - r_drawnpolycount++; - - d_zistepu = s->d_zistepu; - d_zistepv = s->d_zistepv; - d_ziorigin = s->d_ziorigin; - - if (s->flags & SURF_DRAWSKY) - { - extern cvar_t r_fastsky; - extern cvar_t r_skycolor; - - if (r_fastsky.value) - D_DrawSolidSurface (s, (int)r_skycolor.value & 0xFF); - else { - if (!r_skymade) - R_MakeSky (); - D_DrawSkyScans8 (s->spans); - } - - D_DrawZSpans (s->spans); - } - else if (s->flags & SURF_DRAWBACKGROUND) - { - // 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_DrawSolidSurface (s, (int)r_clearcolor.value & 0xFF); - D_DrawZSpans (s->spans); - } - else if (s->flags & SURF_DRAWTURB) - { - pface = s->data; - miplevel = 0; - cacheblock = (pixel_t *) - ((byte *)pface->texinfo->texture + - pface->texinfo->texture->offsets[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); - - Turbulent8 (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 - // - currententity = &r_worldentity; - VectorCopy (world_transformed_modelorg, - transformed_modelorg); - VectorCopy (base_vpn, vpn); - VectorCopy (base_vup, vup); - VectorCopy (base_vright, vright); - VectorCopy (base_modelorg, modelorg); - R_TransformFrustum (); - } - } - else - { - 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 - } - - pface = s->data; - 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_drawspans) (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); - VectorCopy (base_modelorg, modelorg); - R_TransformFrustum (); - currententity = &r_worldentity; - } - } - } - } -} - +/* +Copyright (C) 1996-1997 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_edge.c + +#include "quakedef.h" +#include "d_local.h" + +static int miplevel; + +float scale_for_mip; +int screenwidth; +int ubasestep, errorterm, erroradjustup, erroradjustdown; +int vstartscan; + +// FIXME: should go away +extern void R_RotateBmodel (void); +extern void R_TransformFrustum (void); + +vec3_t transformed_modelorg; + +/* +============== +D_DrawPoly + +============== +*/ +void D_DrawPoly (void) +{ +// this driver takes spans, not polygons +} + + +/* +============= +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_DrawSolidSurface +============== +*/ + +// FIXME: clean this up + +void D_DrawSolidSurface (surf_t *surf, int color) +{ + espan_t *span; + byte *pdest; + int u, u2, pix; + + pix = (color<<24) | (color<<16) | (color<<8) | color; + for (span=surf->spans ; span ; span=span->pnext) + { + pdest = (byte *)d_viewbuffer + screenwidth*span->v; + u = span->u; + u2 = span->u + span->count - 1; + ((byte *)pdest)[u] = pix; + + if (u2 - u < 8) + { + for (u++ ; u <= u2 ; u++) + ((byte *)pdest)[u] = pix; + } + else + { + for (u++ ; u & 3 ; u++) + ((byte *)pdest)[u] = pix; + + u2 -= 4; + for ( ; u <= u2 ; u+=4) + *(int *)((byte *)pdest + u) = pix; + u2 += 4; + for ( ; u <= u2 ; u++) + ((byte *)pdest)[u] = pix; + } + } +} + + +/* +============== +D_CalcGradients +============== +*/ +void D_CalcGradients (msurface_t *pface) +{ + mplane_t *pplane; + float mipscale; + vec3_t p_temp1; + vec3_t p_saxis, p_taxis; + float t; + + pplane = pface->plane; + + 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 = ((fixed16_t)(DotProduct (p_temp1, p_saxis) * 0x10000 + 0.5)) - + ((pface->texturemins[0] << 16) >> miplevel) + + pface->texinfo->vecs[0][3]*t; + tadjust = ((fixed16_t)(DotProduct (p_temp1, p_taxis) * 0x10000 + 0.5)) - + ((pface->texturemins[1] << 16) >> miplevel) + + pface->texinfo->vecs[1][3]*t; + +// +// -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_DrawSurfaces +============== +*/ +void D_DrawSurfaces (void) +{ + surf_t *s; + msurface_t *pface; + surfcache_t *pcurrentcache; + vec3_t world_transformed_modelorg; + vec3_t local_modelorg; + + currententity = &r_worldentity; + TransformVector (modelorg, transformed_modelorg); + VectorCopy (transformed_modelorg, world_transformed_modelorg); + +// TODO: could preset a lot of this at mode set time + if (r_drawflat.value) + { + for (s = &surfaces[1] ; sspans) + continue; + + d_zistepu = s->d_zistepu; + d_zistepv = s->d_zistepv; + d_ziorigin = s->d_ziorigin; + +#ifdef __alpha__ + D_DrawSolidSurface (s, (int)((long)s->data & 0xFF)); +#else + D_DrawSolidSurface (s, (int)s->data & 0xFF); +#endif + D_DrawZSpans (s->spans); + } + } + else + { + for (s = &surfaces[1] ; sspans) + continue; + + r_drawnpolycount++; + + d_zistepu = s->d_zistepu; + d_zistepv = s->d_zistepv; + d_ziorigin = s->d_ziorigin; + + if (s->flags & SURF_DRAWSKY) + { + extern cvar_t r_fastsky; + extern cvar_t r_skycolor; + + if (r_fastsky.value) + D_DrawSolidSurface (s, (int)r_skycolor.value & 0xFF); + else { + if (!r_skymade) + R_MakeSky (); + D_DrawSkyScans8 (s->spans); + } + + D_DrawZSpans (s->spans); + } + else if (s->flags & SURF_DRAWBACKGROUND) + { + // 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_DrawSolidSurface (s, (int)r_clearcolor.value & 0xFF); + D_DrawZSpans (s->spans); + } + else if (s->flags & SURF_DRAWTURB) + { + pface = s->data; + miplevel = 0; + cacheblock = (pixel_t *) + ((byte *)pface->texinfo->texture + + pface->texinfo->texture->offsets[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); + + Turbulent8 (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 + // + currententity = &r_worldentity; + VectorCopy (world_transformed_modelorg, + transformed_modelorg); + VectorCopy (base_vpn, vpn); + VectorCopy (base_vup, vup); + VectorCopy (base_vright, vright); + VectorCopy (base_modelorg, modelorg); + R_TransformFrustum (); + } + } + else + { + 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 + } + + pface = s->data; + 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_drawspans) (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); + VectorCopy (base_modelorg, modelorg); + R_TransformFrustum (); + currententity = &r_worldentity; + } + } + } + } +} + diff --git a/source/d_fill.c b/source/d_fill.c index b1ebb5f1..e6c14733 100644 --- a/source/d_fill.c +++ b/source/d_fill.c @@ -1,88 +1,88 @@ -/* -Copyright (C) 1996-1997 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_clear: clears a specified rectangle to the specified color - -#include "quakedef.h" - - -/* -================ -D_FillRect -================ -*/ -void D_FillRect (vrect_t *rect, int color) -{ - int rx, ry, rwidth, rheight; - unsigned char *dest; - unsigned *ldest; - - rx = rect->x; - ry = rect->y; - rwidth = rect->width; - rheight = rect->height; - - if (rx < 0) - { - rwidth += rx; - rx = 0; - } - if (ry < 0) - { - rheight += ry; - ry = 0; - } - if (rx+rwidth > vid.width) - rwidth = vid.width - rx; - if (ry+rheight > vid.height) - rheight = vid.height - rx; - - if (rwidth < 1 || rheight < 1) - return; - - dest = ((byte *)vid.buffer + ry*vid.rowbytes + rx); - - if (((rwidth & 0x03) == 0) && (((long)dest & 0x03) == 0)) - { - // faster aligned dword clear - ldest = (unsigned *)dest; - color += color << 16; - - rwidth >>= 2; - color += color << 8; - - for (ry=0 ; ryx; + ry = rect->y; + rwidth = rect->width; + rheight = rect->height; + + if (rx < 0) + { + rwidth += rx; + rx = 0; + } + if (ry < 0) + { + rheight += ry; + ry = 0; + } + if (rx+rwidth > vid.width) + rwidth = vid.width - rx; + if (ry+rheight > vid.height) + rheight = vid.height - rx; + + if (rwidth < 1 || rheight < 1) + return; + + dest = ((byte *)vid.buffer + ry*vid.rowbytes + rx); + + if (((rwidth & 0x03) == 0) && (((long)dest & 0x03) == 0)) + { + // faster aligned dword clear + ldest = (unsigned *)dest; + color += color << 16; + + rwidth >>= 2; + color += color << 8; + + for (ry=0 ; ry 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] * d_mipscale.value; - -#if id386 - if (d_subdiv16.value) - d_drawspans = D_DrawSpans16; - else - d_drawspans = D_DrawSpans8; -#else - d_drawspans = D_DrawSpans8; -#endif - - d_aflatcolor = 0; -} - - -/* -=============== -D_UpdateRects -=============== -*/ -void D_UpdateRects (vrect_t *prect) -{ - -// the software driver draws these directly to the vid buffer - - UNUSED(prect); -} - +/* +Copyright (C) 1996-1997 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_init.c: rasterization driver initialization + +#include "quakedef.h" +#include "d_local.h" + +#define NUM_MIPS 4 + +cvar_t d_subdiv16 = {"d_subdiv16", "1"}; +cvar_t d_mipcap = {"d_mipcap", "0"}; +cvar_t d_mipscale = {"d_mipscale", "1"}; + +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; + +void (*d_drawspans) (espan_t *pspan); + + +/* +=============== +D_Init +=============== +*/ +void D_Init (void) +{ + + r_skydirect = 1; + + Cvar_RegisterVariable (&d_subdiv16); + Cvar_RegisterVariable (&d_mipcap); + Cvar_RegisterVariable (&d_mipscale); + + r_drawpolys = false; + r_worldpolysbacktofront = false; + r_recursiveaffinetriangles = true; + r_pixbytes = 1; + r_aliasuvscale = 1.0; +} + + +/* +=============== +D_CopyRects +=============== +*/ +void D_CopyRects (vrect_t *prects, int transparent) +{ + +// this function is only required if the CPU doesn't have direct access to the +// back buffer, and there's some driver interface function that the driver +// doesn't support and requires Quake to do in software (such as drawing the +// console); Quake will then draw into wherever the driver points vid.buffer +// and will call this function before swapping buffers + + UNUSED(prects); + UNUSED(transparent); +} + + +/* +=============== +D_EnableBackBufferAccess +=============== +*/ +void D_EnableBackBufferAccess (void) +{ + + VID_LockBuffer (); +} + + +/* +=============== +D_TurnZOn +=============== +*/ +void D_TurnZOn (void) +{ +// not needed for software version +} + + +/* +=============== +D_DisableBackBufferAccess +=============== +*/ +void D_DisableBackBufferAccess (void) +{ + VID_UnlockBuffer (); +} + + +/* +=============== +D_SetupFrame +=============== +*/ +void D_SetupFrame (void) +{ + int i; + + if (r_dowarp) + d_viewbuffer = r_warpbuffer; + else + d_viewbuffer = (void *)(byte *)vid.buffer; + + if (r_dowarp) + screenwidth = WARP_WIDTH; + else + screenwidth = vid.rowbytes; + + d_roverwrapped = false; + d_initial_rover = sc_rover; + + d_minmip = d_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] * d_mipscale.value; + +#if id386 + if (d_subdiv16.value) + d_drawspans = D_DrawSpans16; + else + d_drawspans = D_DrawSpans8; +#else + d_drawspans = D_DrawSpans8; +#endif + + d_aflatcolor = 0; +} + + +/* +=============== +D_UpdateRects +=============== +*/ +void D_UpdateRects (vrect_t *prect) +{ + +// the software driver draws these directly to the vid buffer + + UNUSED(prect); +} + diff --git a/source/d_local.h b/source/d_local.h index 4636c459..af924899 100644 --- a/source/d_local.h +++ b/source/d_local.h @@ -1,111 +1,111 @@ -/* -Copyright (C) 1996-1997 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_local.h: private rasterization driver defs - -#include "r_shared.h" - -// -// TODO: fine-tune this; it's based on providing some overage even if there -// is a 2k-wide scan, with subdivision every 8, for 256 spans of 12 bytes each -// -#define SCANBUFFERPAD 0x1000 - -#define R_SKY_SMASK 0x007F0000 -#define R_SKY_TMASK 0x007F0000 - -#define DS_SPAN_LIST_END -128 - -#define SURFCACHE_SIZE_AT_320X200 600*1024 - -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; - struct texture_s *texture; // checked for animating textures - byte data[4]; // width*height elements -} surfcache_t; - -// !!! if this is changed, it must be changed in asm_draw.h too !!! -typedef struct sspan_s -{ - int u, v, count; -} sspan_t; - -extern cvar_t d_subdiv16; - -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; - -fixed16_t sadjust, tadjust; -fixed16_t bbextents, bbextentt; - - -void D_DrawSpans8 (espan_t *pspans); -void D_DrawSpans16 (espan_t *pspans); -void D_DrawZSpans (espan_t *pspans); -void Turbulent8 (espan_t *pspan); -void D_SpriteDrawSpans (sspan_t *pspan); - -void D_DrawSkyScans8 (espan_t *pspan); -void D_DrawSkyScans16 (espan_t *pspan); - -void R_ShowSubDiv (void); -void (*prealspandrawer)(void); -surfcache_t *D_CacheSurface (msurface_t *surface, int miplevel); - -extern int D_MipLevelForScale (float scale); - -#if id386 -extern void D_PolysetAff8Start (void); -extern void D_PolysetAff8End (void); -#endif - -extern short *d_pzbuffer; -extern unsigned int d_zrowbytes, d_zwidth; - -extern int *d_pscantable; -extern int d_scantable[MAXHEIGHT]; - -extern int d_vrectx, d_vrecty, d_vrectright_particle, d_vrectbottom_particle; - -extern int d_y_aspect_shift, d_pix_min, d_pix_max, d_pix_shift; - -extern pixel_t *d_viewbuffer; - -extern short *zspantable[MAXHEIGHT]; - -extern int d_minmip; -extern float d_scalemip[3]; - -extern void (*d_drawspans) (espan_t *pspan); - +/* +Copyright (C) 1996-1997 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_local.h: private rasterization driver defs + +#include "r_shared.h" + +// +// TODO: fine-tune this; it's based on providing some overage even if there +// is a 2k-wide scan, with subdivision every 8, for 256 spans of 12 bytes each +// +#define SCANBUFFERPAD 0x1000 + +#define R_SKY_SMASK 0x007F0000 +#define R_SKY_TMASK 0x007F0000 + +#define DS_SPAN_LIST_END -128 + +#define SURFCACHE_SIZE_AT_320X200 600*1024 + +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; + struct texture_s *texture; // checked for animating textures + byte data[4]; // width*height elements +} surfcache_t; + +// !!! if this is changed, it must be changed in asm_draw.h too !!! +typedef struct sspan_s +{ + int u, v, count; +} sspan_t; + +extern cvar_t d_subdiv16; + +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; + +fixed16_t sadjust, tadjust; +fixed16_t bbextents, bbextentt; + + +void D_DrawSpans8 (espan_t *pspans); +void D_DrawSpans16 (espan_t *pspans); +void D_DrawZSpans (espan_t *pspans); +void Turbulent8 (espan_t *pspan); +void D_SpriteDrawSpans (sspan_t *pspan); + +void D_DrawSkyScans8 (espan_t *pspan); +void D_DrawSkyScans16 (espan_t *pspan); + +void R_ShowSubDiv (void); +void (*prealspandrawer)(void); +surfcache_t *D_CacheSurface (msurface_t *surface, int miplevel); + +extern int D_MipLevelForScale (float scale); + +#if id386 +extern void D_PolysetAff8Start (void); +extern void D_PolysetAff8End (void); +#endif + +extern short *d_pzbuffer; +extern unsigned int d_zrowbytes, d_zwidth; + +extern int *d_pscantable; +extern int d_scantable[MAXHEIGHT]; + +extern int d_vrectx, d_vrecty, d_vrectright_particle, d_vrectbottom_particle; + +extern int d_y_aspect_shift, d_pix_min, d_pix_max, d_pix_shift; + +extern pixel_t *d_viewbuffer; + +extern short *zspantable[MAXHEIGHT]; + +extern int d_minmip; +extern float d_scalemip[3]; + +extern void (*d_drawspans) (espan_t *pspan); + diff --git a/source/d_modech.c b/source/d_modech.c index f1162b4b..75c188e3 100644 --- a/source/d_modech.c +++ b/source/d_modech.c @@ -1,107 +1,107 @@ -/* -Copyright (C) 1996-1997 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_modech.c: called when mode has just changed - -#include "quakedef.h" -#include "d_local.h" - -int d_vrectx, d_vrecty, d_vrectright_particle, d_vrectbottom_particle; - -int d_y_aspect_shift, d_pix_min, d_pix_max, d_pix_shift; - -int d_scantable[MAXHEIGHT]; -short *zspantable[MAXHEIGHT]; - -/* -================ -D_Patch -================ -*/ -void D_Patch (void) -{ -#if id386 - - static qboolean protectset8 = false; - - if (!protectset8) - { - Sys_MakeCodeWriteable ((int)D_PolysetAff8Start, - (int)D_PolysetAff8End - (int)D_PolysetAff8Start); - protectset8 = true; - } - -#endif // id386 -} - - -/* -================ -D_ViewChanged -================ -*/ -void D_ViewChanged (void) -{ - int rowbytes; - - if (r_dowarp) - rowbytes = WARP_WIDTH; - else - rowbytes = vid.rowbytes; - - 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; - - if (pixelAspect > 1.4) - d_y_aspect_shift = 1; - else - d_y_aspect_shift = 0; - - 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 << d_y_aspect_shift); - - { - int i; - - for (i=0 ; i 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; + + if (pixelAspect > 1.4) + d_y_aspect_shift = 1; + else + d_y_aspect_shift = 0; + + 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 << d_y_aspect_shift); + + { + int i; + + for (i=0 ; iorg, 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; - } - - pz = d_pzbuffer + (d_zwidth * v) + u; - pdest = d_viewbuffer + d_scantable[v] + u; - izi = (int)(zi * 0x8000); - - pix = izi >> d_pix_shift; - - if (pix < d_pix_min) - pix = d_pix_min; - else if (pix > d_pix_max) - pix = d_pix_max; - - switch (pix) - { - case 1: - count = 1 << d_y_aspect_shift; - - for ( ; count ; count--, pz += d_zwidth, pdest += screenwidth) - { - if (pz[0] <= izi) - { - pz[0] = izi; - pdest[0] = pparticle->color; - } - } - break; - - case 2: - count = 2 << d_y_aspect_shift; - - for ( ; count ; count--, pz += d_zwidth, pdest += screenwidth) - { - if (pz[0] <= izi) - { - pz[0] = izi; - pdest[0] = pparticle->color; - } - - if (pz[1] <= izi) - { - pz[1] = izi; - pdest[1] = pparticle->color; - } - } - break; - - case 3: - count = 3 << d_y_aspect_shift; - - for ( ; count ; count--, pz += d_zwidth, pdest += screenwidth) - { - if (pz[0] <= izi) - { - pz[0] = izi; - pdest[0] = pparticle->color; - } - - if (pz[1] <= izi) - { - pz[1] = izi; - pdest[1] = pparticle->color; - } - - if (pz[2] <= izi) - { - pz[2] = izi; - pdest[2] = pparticle->color; - } - } - break; - - case 4: - count = 4 << d_y_aspect_shift; - - for ( ; count ; count--, pz += d_zwidth, pdest += screenwidth) - { - if (pz[0] <= izi) - { - pz[0] = izi; - pdest[0] = pparticle->color; - } - - if (pz[1] <= izi) - { - pz[1] = izi; - pdest[1] = pparticle->color; - } - - if (pz[2] <= izi) - { - pz[2] = izi; - pdest[2] = pparticle->color; - } - - if (pz[3] <= izi) - { - pz[3] = izi; - pdest[3] = pparticle->color; - } - } - break; - - default: - count = pix << d_y_aspect_shift; - - for ( ; count ; count--, pz += d_zwidth, pdest += screenwidth) - { - for (i=0 ; icolor; - } - } - } - break; - } -} - -#endif // !id386 - +/* +Copyright (C) 1996-1997 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_part.c: software driver module for drawing particles + +#include "quakedef.h" +#include "d_local.h" + + +/* +============== +D_EndParticles +============== +*/ +void D_EndParticles (void) +{ +// not used by software driver +} + + +/* +============== +D_StartParticles +============== +*/ +void D_StartParticles (void) +{ +// not used by software driver +} + + +#if !id386 + +/* +============== +D_DrawParticle +============== +*/ +void D_DrawParticle (particle_t *pparticle) +{ + vec3_t local, transformed; + float zi; + byte *pdest; + short *pz; + int i, izi, pix, count, u, v; + +// transform point + VectorSubtract (pparticle->org, 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; + } + + pz = d_pzbuffer + (d_zwidth * v) + u; + pdest = d_viewbuffer + d_scantable[v] + u; + izi = (int)(zi * 0x8000); + + pix = izi >> d_pix_shift; + + if (pix < d_pix_min) + pix = d_pix_min; + else if (pix > d_pix_max) + pix = d_pix_max; + + switch (pix) + { + case 1: + count = 1 << d_y_aspect_shift; + + for ( ; count ; count--, pz += d_zwidth, pdest += screenwidth) + { + if (pz[0] <= izi) + { + pz[0] = izi; + pdest[0] = pparticle->color; + } + } + break; + + case 2: + count = 2 << d_y_aspect_shift; + + for ( ; count ; count--, pz += d_zwidth, pdest += screenwidth) + { + if (pz[0] <= izi) + { + pz[0] = izi; + pdest[0] = pparticle->color; + } + + if (pz[1] <= izi) + { + pz[1] = izi; + pdest[1] = pparticle->color; + } + } + break; + + case 3: + count = 3 << d_y_aspect_shift; + + for ( ; count ; count--, pz += d_zwidth, pdest += screenwidth) + { + if (pz[0] <= izi) + { + pz[0] = izi; + pdest[0] = pparticle->color; + } + + if (pz[1] <= izi) + { + pz[1] = izi; + pdest[1] = pparticle->color; + } + + if (pz[2] <= izi) + { + pz[2] = izi; + pdest[2] = pparticle->color; + } + } + break; + + case 4: + count = 4 << d_y_aspect_shift; + + for ( ; count ; count--, pz += d_zwidth, pdest += screenwidth) + { + if (pz[0] <= izi) + { + pz[0] = izi; + pdest[0] = pparticle->color; + } + + if (pz[1] <= izi) + { + pz[1] = izi; + pdest[1] = pparticle->color; + } + + if (pz[2] <= izi) + { + pz[2] = izi; + pdest[2] = pparticle->color; + } + + if (pz[3] <= izi) + { + pz[3] = izi; + pdest[3] = pparticle->color; + } + } + break; + + default: + count = pix << d_y_aspect_shift; + + for ( ; count ; count--, pz += d_zwidth, pdest += screenwidth) + { + for (i=0 ; icolor; + } + } + } + break; + } +} + +#endif // !id386 + diff --git a/source/d_parta.s b/source/d_parta.s index a84b27cd..bf3b5b9a 100644 --- a/source/d_parta.s +++ b/source/d_parta.s @@ -1,483 +1,483 @@ -/* - d_parta.S - - (description) - - Copyright (C) 1996-1997 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: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - - $Id: d_parta.s,v 1.1.1.3 2004/10/13 18:54:30 vvd0 Exp $ -*/ -// d_parta.s -// x86 assembly-language 8-bpp particle-drawing code. - -#include "asm_i386.h" -#include "quakeasm.h" -#include "d_ifacea.h" -#include "asm_draw.h" - -#ifdef id386 - -//---------------------------------------------------------------------- -// 8-bpp particle drawing code. -//---------------------------------------------------------------------- - -//FIXME: comments, full optimization - -//---------------------------------------------------------------------- -// 8-bpp particle queueing code. -//---------------------------------------------------------------------- - - .text - -#define P 12+4 - - .align 4 -.globl C(D_DrawParticle) -C(D_DrawParticle): - pushl %ebp // preserve caller's stack frame - pushl %edi // preserve register variables - pushl %ebx - - movl P(%esp),%edi - -// FIXME: better FP overlap in general here - -// transform point -// VectorSubtract (p->org, r_origin, local); - flds C(r_origin) - fsubrs pt_org(%edi) - flds pt_org+4(%edi) - fsubs C(r_origin)+4 - flds pt_org+8(%edi) - fsubs C(r_origin)+8 - fxch %st(2) // local[0] | local[1] | local[2] - -// transformed[2] = DotProduct(local, r_ppn); - flds C(r_ppn) // r_ppn[0] | local[0] | local[1] | local[2] - fmul %st(1),%st(0) // dot0 | local[0] | local[1] | local[2] - flds C(r_ppn)+4 // r_ppn[1] | dot0 | local[0] | local[1] | local[2] - fmul %st(3),%st(0) // dot1 | dot0 | local[0] | local[1] | local[2] - flds C(r_ppn)+8 // r_ppn[2] | dot1 | dot0 | local[0] | - // local[1] | local[2] - fmul %st(5),%st(0) // dot2 | dot1 | dot0 | local[0] | local[1] | local[2] - fxch %st(2) // dot0 | dot1 | dot2 | local[0] | local[1] | local[2] - faddp %st(0),%st(1) // dot0 + dot1 | dot2 | local[0] | local[1] | - // local[2] - faddp %st(0),%st(1) // z | local[0] | local[1] | local[2] - fld %st(0) // z | z | local[0] | local[1] | - // local[2] - fdivrs float_1 // 1/z | z | local[0] | local[1] | local[2] - fxch %st(1) // z | 1/z | local[0] | local[1] | local[2] - -// if (transformed[2] < PARTICLE_Z_CLIP) -// return; - fcomps float_particle_z_clip // 1/z | local[0] | local[1] | local[2] - fxch %st(3) // local[2] | local[0] | local[1] | 1/z - - flds C(r_pup) // r_pup[0] | local[2] | local[0] | local[1] | 1/z - fmul %st(2),%st(0) // dot0 | local[2] | local[0] | local[1] | 1/z - flds C(r_pup)+4 // r_pup[1] | dot0 | local[2] | local[0] | - // local[1] | 1/z - - fnstsw %ax - testb $1,%ah - jnz LPop6AndDone - -// transformed[1] = DotProduct(local, r_pup); - fmul %st(4),%st(0) // dot1 | dot0 | local[2] | local[0] | local[1] | 1/z - flds C(r_pup)+8 // r_pup[2] | dot1 | dot0 | local[2] | - // local[0] | local[1] | 1/z - fmul %st(3),%st(0) // dot2 | dot1 | dot0 | local[2] | local[0] | - // local[1] | 1/z - fxch %st(2) // dot0 | dot1 | dot2 | local[2] | local[0] | - // local[1] | 1/z - faddp %st(0),%st(1) // dot0 + dot1 | dot2 | local[2] | local[0] | - // local[1] | 1/z - faddp %st(0),%st(1) // y | local[2] | local[0] | local[1] | 1/z - fxch %st(3) // local[1] | local[2] | local[0] | y | 1/z - -// transformed[0] = DotProduct(local, r_pright); - fmuls C(r_pright)+4 // dot1 | local[2] | local[0] | y | 1/z - fxch %st(2) // local[0] | local[2] | dot1 | y | 1/z - fmuls C(r_pright) // dot0 | local[2] | dot1 | y | 1/z - fxch %st(1) // local[2] | dot0 | dot1 | y | 1/z - fmuls C(r_pright)+8 // dot2 | dot0 | dot1 | y | 1/z - fxch %st(2) // dot1 | dot0 | dot2 | y | 1/z - faddp %st(0),%st(1) // dot1 + dot0 | dot2 | y | 1/z - - faddp %st(0),%st(1) // x | y | 1/z - fxch %st(1) // y | x | 1/z - -// project the point - fmul %st(2),%st(0) // y/z | x | 1/z - fxch %st(1) // x | y/z | 1/z - fmul %st(2),%st(0) // x/z | y/z | 1/z - fxch %st(1) // y/z | x/z | 1/z - fsubrs C(ycenter) // v | x/z | 1/z - fxch %st(1) // x/z | v | 1/z - fadds C(xcenter) // u | v | 1/z -// FIXME: preadjust xcenter and ycenter - fxch %st(1) // v | u | 1/z - fadds float_point5 // v | u | 1/z - fxch %st(1) // u | v | 1/z - fadds float_point5 // u | v | 1/z - fxch %st(2) // 1/z | v | u - fmuls DP_32768 // 1/z * 0x8000 | v | u - fxch %st(2) // u | v | 1/z * 0x8000 - -// FIXME: use Terje's fp->int trick here? -// FIXME: check we're getting proper rounding here - fistpl DP_u // v | 1/z * 0x8000 - fistpl DP_v // 1/z * 0x8000 - - movl DP_u,%eax - movl DP_v,%edx - -// if ((v > d_vrectbottom_particle) || -// (u > d_vrectright_particle) || -// (v < d_vrecty) || -// (u < d_vrectx)) -// { -// continue; -// } - - movl C(d_vrectbottom_particle),%ebx - movl C(d_vrectright_particle),%ecx - cmpl %ebx,%edx - jg LPop1AndDone - cmpl %ecx,%eax - jg LPop1AndDone - movl C(d_vrecty),%ebx - movl C(d_vrectx),%ecx - cmpl %ebx,%edx - jl LPop1AndDone - - cmpl %ecx,%eax - jl LPop1AndDone - - flds pt_color(%edi) // color | 1/z * 0x8000 -// FIXME: use Terje's fast fp->int trick? - fistpl DP_Color // 1/z * 0x8000 - - movl C(d_viewbuffer),%ebx - - addl %eax,%ebx - movl C(d_scantable)(,%edx,4),%edi // point to the pixel - - imull C(d_zrowbytes),%edx // point to the z pixel - - leal (%edx,%eax,2),%edx - movl C(d_pzbuffer),%eax - - fistpl izi - - addl %ebx,%edi - addl %eax,%edx - -// pix = izi >> d_pix_shift; - - movl izi,%eax - movl C(d_pix_shift),%ecx - shrl %cl,%eax - movl izi,%ebp - -// if (pix < d_pix_min) -// pix = d_pix_min; -// else if (pix > d_pix_max) -// pix = d_pix_max; - - movl C(d_pix_min),%ebx - movl C(d_pix_max),%ecx - cmpl %ebx,%eax - jnl LTestPixMax - movl %ebx,%eax - jmp LTestDone - -LTestPixMax: - cmpl %ecx,%eax - jng LTestDone - movl %ecx,%eax -LTestDone: - - movb DP_Color,%ch - - movl C(d_y_aspect_shift),%ebx - testl %ebx,%ebx - jnz LDefault - - cmpl $4,%eax - ja LDefault - - jmp DP_EntryTable-4(,%eax,4) - -// 1x1 -.globl DP_1x1 -DP_1x1: - cmpw %bp,(%edx) // just one pixel to do - jg LDone - movw %bp,(%edx) - movb %ch,(%edi) - jmp LDone - -// 2x2 -.globl DP_2x2 -DP_2x2: - pushl %esi - movl C(screenwidth),%ebx - movl C(d_zrowbytes),%esi - - cmpw %bp,(%edx) - jg L2x2_1 - movw %bp,(%edx) - movb %ch,(%edi) -L2x2_1: - cmpw %bp,2(%edx) - jg L2x2_2 - movw %bp,2(%edx) - movb %ch,1(%edi) -L2x2_2: - cmpw %bp,(%edx,%esi,1) - jg L2x2_3 - movw %bp,(%edx,%esi,1) - movb %ch,(%edi,%ebx,1) -L2x2_3: - cmpw %bp,2(%edx,%esi,1) - jg L2x2_4 - movw %bp,2(%edx,%esi,1) - movb %ch,1(%edi,%ebx,1) -L2x2_4: - - popl %esi - jmp LDone - -// 3x3 -.globl DP_3x3 -DP_3x3: - pushl %esi - movl C(screenwidth),%ebx - movl C(d_zrowbytes),%esi - - cmpw %bp,(%edx) - jg L3x3_1 - movw %bp,(%edx) - movb %ch,(%edi) -L3x3_1: - cmpw %bp,2(%edx) - jg L3x3_2 - movw %bp,2(%edx) - movb %ch,1(%edi) -L3x3_2: - cmpw %bp,4(%edx) - jg L3x3_3 - movw %bp,4(%edx) - movb %ch,2(%edi) -L3x3_3: - - cmpw %bp,(%edx,%esi,1) - jg L3x3_4 - movw %bp,(%edx,%esi,1) - movb %ch,(%edi,%ebx,1) -L3x3_4: - cmpw %bp,2(%edx,%esi,1) - jg L3x3_5 - movw %bp,2(%edx,%esi,1) - movb %ch,1(%edi,%ebx,1) -L3x3_5: - cmpw %bp,4(%edx,%esi,1) - jg L3x3_6 - movw %bp,4(%edx,%esi,1) - movb %ch,2(%edi,%ebx,1) -L3x3_6: - - cmpw %bp,(%edx,%esi,2) - jg L3x3_7 - movw %bp,(%edx,%esi,2) - movb %ch,(%edi,%ebx,2) -L3x3_7: - cmpw %bp,2(%edx,%esi,2) - jg L3x3_8 - movw %bp,2(%edx,%esi,2) - movb %ch,1(%edi,%ebx,2) -L3x3_8: - cmpw %bp,4(%edx,%esi,2) - jg L3x3_9 - movw %bp,4(%edx,%esi,2) - movb %ch,2(%edi,%ebx,2) -L3x3_9: - - popl %esi - jmp LDone - - -// 4x4 -.globl DP_4x4 -DP_4x4: - pushl %esi - movl C(screenwidth),%ebx - movl C(d_zrowbytes),%esi - - cmpw %bp,(%edx) - jg L4x4_1 - movw %bp,(%edx) - movb %ch,(%edi) -L4x4_1: - cmpw %bp,2(%edx) - jg L4x4_2 - movw %bp,2(%edx) - movb %ch,1(%edi) -L4x4_2: - cmpw %bp,4(%edx) - jg L4x4_3 - movw %bp,4(%edx) - movb %ch,2(%edi) -L4x4_3: - cmpw %bp,6(%edx) - jg L4x4_4 - movw %bp,6(%edx) - movb %ch,3(%edi) -L4x4_4: - - cmpw %bp,(%edx,%esi,1) - jg L4x4_5 - movw %bp,(%edx,%esi,1) - movb %ch,(%edi,%ebx,1) -L4x4_5: - cmpw %bp,2(%edx,%esi,1) - jg L4x4_6 - movw %bp,2(%edx,%esi,1) - movb %ch,1(%edi,%ebx,1) -L4x4_6: - cmpw %bp,4(%edx,%esi,1) - jg L4x4_7 - movw %bp,4(%edx,%esi,1) - movb %ch,2(%edi,%ebx,1) -L4x4_7: - cmpw %bp,6(%edx,%esi,1) - jg L4x4_8 - movw %bp,6(%edx,%esi,1) - movb %ch,3(%edi,%ebx,1) -L4x4_8: - - leal (%edx,%esi,2),%edx - leal (%edi,%ebx,2),%edi - - cmpw %bp,(%edx) - jg L4x4_9 - movw %bp,(%edx) - movb %ch,(%edi) -L4x4_9: - cmpw %bp,2(%edx) - jg L4x4_10 - movw %bp,2(%edx) - movb %ch,1(%edi) -L4x4_10: - cmpw %bp,4(%edx) - jg L4x4_11 - movw %bp,4(%edx) - movb %ch,2(%edi) -L4x4_11: - cmpw %bp,6(%edx) - jg L4x4_12 - movw %bp,6(%edx) - movb %ch,3(%edi) -L4x4_12: - - cmpw %bp,(%edx,%esi,1) - jg L4x4_13 - movw %bp,(%edx,%esi,1) - movb %ch,(%edi,%ebx,1) -L4x4_13: - cmpw %bp,2(%edx,%esi,1) - jg L4x4_14 - movw %bp,2(%edx,%esi,1) - movb %ch,1(%edi,%ebx,1) -L4x4_14: - cmpw %bp,4(%edx,%esi,1) - jg L4x4_15 - movw %bp,4(%edx,%esi,1) - movb %ch,2(%edi,%ebx,1) -L4x4_15: - cmpw %bp,6(%edx,%esi,1) - jg L4x4_16 - movw %bp,6(%edx,%esi,1) - movb %ch,3(%edi,%ebx,1) -L4x4_16: - - popl %esi - jmp LDone - -// default case, handling any size particle -LDefault: - -// count = pix << d_y_aspect_shift; - - movl %eax,%ebx - movl %eax,DP_Pix - movb C(d_y_aspect_shift),%cl - shll %cl,%ebx - -// for ( ; count ; count--, pz += d_zwidth, pdest += screenwidth) -// { -// for (i=0 ; iorg, r_origin, local); + flds C(r_origin) + fsubrs pt_org(%edi) + flds pt_org+4(%edi) + fsubs C(r_origin)+4 + flds pt_org+8(%edi) + fsubs C(r_origin)+8 + fxch %st(2) // local[0] | local[1] | local[2] + +// transformed[2] = DotProduct(local, r_ppn); + flds C(r_ppn) // r_ppn[0] | local[0] | local[1] | local[2] + fmul %st(1),%st(0) // dot0 | local[0] | local[1] | local[2] + flds C(r_ppn)+4 // r_ppn[1] | dot0 | local[0] | local[1] | local[2] + fmul %st(3),%st(0) // dot1 | dot0 | local[0] | local[1] | local[2] + flds C(r_ppn)+8 // r_ppn[2] | dot1 | dot0 | local[0] | + // local[1] | local[2] + fmul %st(5),%st(0) // dot2 | dot1 | dot0 | local[0] | local[1] | local[2] + fxch %st(2) // dot0 | dot1 | dot2 | local[0] | local[1] | local[2] + faddp %st(0),%st(1) // dot0 + dot1 | dot2 | local[0] | local[1] | + // local[2] + faddp %st(0),%st(1) // z | local[0] | local[1] | local[2] + fld %st(0) // z | z | local[0] | local[1] | + // local[2] + fdivrs float_1 // 1/z | z | local[0] | local[1] | local[2] + fxch %st(1) // z | 1/z | local[0] | local[1] | local[2] + +// if (transformed[2] < PARTICLE_Z_CLIP) +// return; + fcomps float_particle_z_clip // 1/z | local[0] | local[1] | local[2] + fxch %st(3) // local[2] | local[0] | local[1] | 1/z + + flds C(r_pup) // r_pup[0] | local[2] | local[0] | local[1] | 1/z + fmul %st(2),%st(0) // dot0 | local[2] | local[0] | local[1] | 1/z + flds C(r_pup)+4 // r_pup[1] | dot0 | local[2] | local[0] | + // local[1] | 1/z + + fnstsw %ax + testb $1,%ah + jnz LPop6AndDone + +// transformed[1] = DotProduct(local, r_pup); + fmul %st(4),%st(0) // dot1 | dot0 | local[2] | local[0] | local[1] | 1/z + flds C(r_pup)+8 // r_pup[2] | dot1 | dot0 | local[2] | + // local[0] | local[1] | 1/z + fmul %st(3),%st(0) // dot2 | dot1 | dot0 | local[2] | local[0] | + // local[1] | 1/z + fxch %st(2) // dot0 | dot1 | dot2 | local[2] | local[0] | + // local[1] | 1/z + faddp %st(0),%st(1) // dot0 + dot1 | dot2 | local[2] | local[0] | + // local[1] | 1/z + faddp %st(0),%st(1) // y | local[2] | local[0] | local[1] | 1/z + fxch %st(3) // local[1] | local[2] | local[0] | y | 1/z + +// transformed[0] = DotProduct(local, r_pright); + fmuls C(r_pright)+4 // dot1 | local[2] | local[0] | y | 1/z + fxch %st(2) // local[0] | local[2] | dot1 | y | 1/z + fmuls C(r_pright) // dot0 | local[2] | dot1 | y | 1/z + fxch %st(1) // local[2] | dot0 | dot1 | y | 1/z + fmuls C(r_pright)+8 // dot2 | dot0 | dot1 | y | 1/z + fxch %st(2) // dot1 | dot0 | dot2 | y | 1/z + faddp %st(0),%st(1) // dot1 + dot0 | dot2 | y | 1/z + + faddp %st(0),%st(1) // x | y | 1/z + fxch %st(1) // y | x | 1/z + +// project the point + fmul %st(2),%st(0) // y/z | x | 1/z + fxch %st(1) // x | y/z | 1/z + fmul %st(2),%st(0) // x/z | y/z | 1/z + fxch %st(1) // y/z | x/z | 1/z + fsubrs C(ycenter) // v | x/z | 1/z + fxch %st(1) // x/z | v | 1/z + fadds C(xcenter) // u | v | 1/z +// FIXME: preadjust xcenter and ycenter + fxch %st(1) // v | u | 1/z + fadds float_point5 // v | u | 1/z + fxch %st(1) // u | v | 1/z + fadds float_point5 // u | v | 1/z + fxch %st(2) // 1/z | v | u + fmuls DP_32768 // 1/z * 0x8000 | v | u + fxch %st(2) // u | v | 1/z * 0x8000 + +// FIXME: use Terje's fp->int trick here? +// FIXME: check we're getting proper rounding here + fistpl DP_u // v | 1/z * 0x8000 + fistpl DP_v // 1/z * 0x8000 + + movl DP_u,%eax + movl DP_v,%edx + +// if ((v > d_vrectbottom_particle) || +// (u > d_vrectright_particle) || +// (v < d_vrecty) || +// (u < d_vrectx)) +// { +// continue; +// } + + movl C(d_vrectbottom_particle),%ebx + movl C(d_vrectright_particle),%ecx + cmpl %ebx,%edx + jg LPop1AndDone + cmpl %ecx,%eax + jg LPop1AndDone + movl C(d_vrecty),%ebx + movl C(d_vrectx),%ecx + cmpl %ebx,%edx + jl LPop1AndDone + + cmpl %ecx,%eax + jl LPop1AndDone + + flds pt_color(%edi) // color | 1/z * 0x8000 +// FIXME: use Terje's fast fp->int trick? + fistpl DP_Color // 1/z * 0x8000 + + movl C(d_viewbuffer),%ebx + + addl %eax,%ebx + movl C(d_scantable)(,%edx,4),%edi // point to the pixel + + imull C(d_zrowbytes),%edx // point to the z pixel + + leal (%edx,%eax,2),%edx + movl C(d_pzbuffer),%eax + + fistpl izi + + addl %ebx,%edi + addl %eax,%edx + +// pix = izi >> d_pix_shift; + + movl izi,%eax + movl C(d_pix_shift),%ecx + shrl %cl,%eax + movl izi,%ebp + +// if (pix < d_pix_min) +// pix = d_pix_min; +// else if (pix > d_pix_max) +// pix = d_pix_max; + + movl C(d_pix_min),%ebx + movl C(d_pix_max),%ecx + cmpl %ebx,%eax + jnl LTestPixMax + movl %ebx,%eax + jmp LTestDone + +LTestPixMax: + cmpl %ecx,%eax + jng LTestDone + movl %ecx,%eax +LTestDone: + + movb DP_Color,%ch + + movl C(d_y_aspect_shift),%ebx + testl %ebx,%ebx + jnz LDefault + + cmpl $4,%eax + ja LDefault + + jmp DP_EntryTable-4(,%eax,4) + +// 1x1 +.globl DP_1x1 +DP_1x1: + cmpw %bp,(%edx) // just one pixel to do + jg LDone + movw %bp,(%edx) + movb %ch,(%edi) + jmp LDone + +// 2x2 +.globl DP_2x2 +DP_2x2: + pushl %esi + movl C(screenwidth),%ebx + movl C(d_zrowbytes),%esi + + cmpw %bp,(%edx) + jg L2x2_1 + movw %bp,(%edx) + movb %ch,(%edi) +L2x2_1: + cmpw %bp,2(%edx) + jg L2x2_2 + movw %bp,2(%edx) + movb %ch,1(%edi) +L2x2_2: + cmpw %bp,(%edx,%esi,1) + jg L2x2_3 + movw %bp,(%edx,%esi,1) + movb %ch,(%edi,%ebx,1) +L2x2_3: + cmpw %bp,2(%edx,%esi,1) + jg L2x2_4 + movw %bp,2(%edx,%esi,1) + movb %ch,1(%edi,%ebx,1) +L2x2_4: + + popl %esi + jmp LDone + +// 3x3 +.globl DP_3x3 +DP_3x3: + pushl %esi + movl C(screenwidth),%ebx + movl C(d_zrowbytes),%esi + + cmpw %bp,(%edx) + jg L3x3_1 + movw %bp,(%edx) + movb %ch,(%edi) +L3x3_1: + cmpw %bp,2(%edx) + jg L3x3_2 + movw %bp,2(%edx) + movb %ch,1(%edi) +L3x3_2: + cmpw %bp,4(%edx) + jg L3x3_3 + movw %bp,4(%edx) + movb %ch,2(%edi) +L3x3_3: + + cmpw %bp,(%edx,%esi,1) + jg L3x3_4 + movw %bp,(%edx,%esi,1) + movb %ch,(%edi,%ebx,1) +L3x3_4: + cmpw %bp,2(%edx,%esi,1) + jg L3x3_5 + movw %bp,2(%edx,%esi,1) + movb %ch,1(%edi,%ebx,1) +L3x3_5: + cmpw %bp,4(%edx,%esi,1) + jg L3x3_6 + movw %bp,4(%edx,%esi,1) + movb %ch,2(%edi,%ebx,1) +L3x3_6: + + cmpw %bp,(%edx,%esi,2) + jg L3x3_7 + movw %bp,(%edx,%esi,2) + movb %ch,(%edi,%ebx,2) +L3x3_7: + cmpw %bp,2(%edx,%esi,2) + jg L3x3_8 + movw %bp,2(%edx,%esi,2) + movb %ch,1(%edi,%ebx,2) +L3x3_8: + cmpw %bp,4(%edx,%esi,2) + jg L3x3_9 + movw %bp,4(%edx,%esi,2) + movb %ch,2(%edi,%ebx,2) +L3x3_9: + + popl %esi + jmp LDone + + +// 4x4 +.globl DP_4x4 +DP_4x4: + pushl %esi + movl C(screenwidth),%ebx + movl C(d_zrowbytes),%esi + + cmpw %bp,(%edx) + jg L4x4_1 + movw %bp,(%edx) + movb %ch,(%edi) +L4x4_1: + cmpw %bp,2(%edx) + jg L4x4_2 + movw %bp,2(%edx) + movb %ch,1(%edi) +L4x4_2: + cmpw %bp,4(%edx) + jg L4x4_3 + movw %bp,4(%edx) + movb %ch,2(%edi) +L4x4_3: + cmpw %bp,6(%edx) + jg L4x4_4 + movw %bp,6(%edx) + movb %ch,3(%edi) +L4x4_4: + + cmpw %bp,(%edx,%esi,1) + jg L4x4_5 + movw %bp,(%edx,%esi,1) + movb %ch,(%edi,%ebx,1) +L4x4_5: + cmpw %bp,2(%edx,%esi,1) + jg L4x4_6 + movw %bp,2(%edx,%esi,1) + movb %ch,1(%edi,%ebx,1) +L4x4_6: + cmpw %bp,4(%edx,%esi,1) + jg L4x4_7 + movw %bp,4(%edx,%esi,1) + movb %ch,2(%edi,%ebx,1) +L4x4_7: + cmpw %bp,6(%edx,%esi,1) + jg L4x4_8 + movw %bp,6(%edx,%esi,1) + movb %ch,3(%edi,%ebx,1) +L4x4_8: + + leal (%edx,%esi,2),%edx + leal (%edi,%ebx,2),%edi + + cmpw %bp,(%edx) + jg L4x4_9 + movw %bp,(%edx) + movb %ch,(%edi) +L4x4_9: + cmpw %bp,2(%edx) + jg L4x4_10 + movw %bp,2(%edx) + movb %ch,1(%edi) +L4x4_10: + cmpw %bp,4(%edx) + jg L4x4_11 + movw %bp,4(%edx) + movb %ch,2(%edi) +L4x4_11: + cmpw %bp,6(%edx) + jg L4x4_12 + movw %bp,6(%edx) + movb %ch,3(%edi) +L4x4_12: + + cmpw %bp,(%edx,%esi,1) + jg L4x4_13 + movw %bp,(%edx,%esi,1) + movb %ch,(%edi,%ebx,1) +L4x4_13: + cmpw %bp,2(%edx,%esi,1) + jg L4x4_14 + movw %bp,2(%edx,%esi,1) + movb %ch,1(%edi,%ebx,1) +L4x4_14: + cmpw %bp,4(%edx,%esi,1) + jg L4x4_15 + movw %bp,4(%edx,%esi,1) + movb %ch,2(%edi,%ebx,1) +L4x4_15: + cmpw %bp,6(%edx,%esi,1) + jg L4x4_16 + movw %bp,6(%edx,%esi,1) + movb %ch,3(%edi,%ebx,1) +L4x4_16: + + popl %esi + jmp LDone + +// default case, handling any size particle +LDefault: + +// count = pix << d_y_aspect_shift; + + movl %eax,%ebx + movl %eax,DP_Pix + movb C(d_y_aspect_shift),%cl + shll %cl,%ebx + +// for ( ; count ; count--, pz += d_zwidth, pdest += screenwidth) +// { +// for (i=0 ; i> 16) + -// (r_sstepx >> 16); - - movl C(r_sstepx),%eax - movl C(r_tstepx),%edx - shll $16,%eax - shll $16,%edx - movl %eax,C(a_sstepxfrac) - movl %edx,C(a_tstepxfrac) - - movl C(r_sstepx),%ecx - movl C(r_tstepx),%eax - sarl $16,%ecx - sarl $16,%eax - imull skinwidth(%esp) - addl %ecx,%eax - movl %eax,C(a_ststepxwhole) - - ret - - -//---------------------------------------------------------------------- -// recursive subdivision affine triangle drawing code -// -// not C-callable because of stdcall return -//---------------------------------------------------------------------- - -#define lp1 4+16 -#define lp2 8+16 -#define lp3 12+16 - -.globl C(D_PolysetRecursiveTriangle) -C(D_PolysetRecursiveTriangle): - pushl %ebp // preserve caller stack frame pointer - pushl %esi // preserve register variables - pushl %edi - pushl %ebx - -// int *temp; -// int d; -// int new[6]; -// int i; -// int z; -// short *zbuf; - movl lp2(%esp),%esi - movl lp1(%esp),%ebx - movl lp3(%esp),%edi - -// d = lp2[0] - lp1[0]; -// if (d < -1 || d > 1) -// goto split; - movl 0(%esi),%eax - - movl 0(%ebx),%edx - movl 4(%esi),%ebp - - subl %edx,%eax - movl 4(%ebx),%ecx - - subl %ecx,%ebp - incl %eax - - cmpl $2,%eax - ja LSplit - -// d = lp2[1] - lp1[1]; -// if (d < -1 || d > 1) -// goto split; - movl 0(%edi),%eax - incl %ebp - - cmpl $2,%ebp - ja LSplit - -// d = lp3[0] - lp2[0]; -// if (d < -1 || d > 1) -// goto split2; - movl 0(%esi),%edx - movl 4(%edi),%ebp - - subl %edx,%eax - movl 4(%esi),%ecx - - subl %ecx,%ebp - incl %eax - - cmpl $2,%eax - ja LSplit2 - -// d = lp3[1] - lp2[1]; -// if (d < -1 || d > 1) -// goto split2; - movl 0(%ebx),%eax - incl %ebp - - cmpl $2,%ebp - ja LSplit2 - -// d = lp1[0] - lp3[0]; -// if (d < -1 || d > 1) -// goto split3; - movl 0(%edi),%edx - movl 4(%ebx),%ebp - - subl %edx,%eax - movl 4(%edi),%ecx - - subl %ecx,%ebp - incl %eax - - incl %ebp - movl %ebx,%edx - - cmpl $2,%eax - ja LSplit3 - -// d = lp1[1] - lp3[1]; -// if (d < -1 || d > 1) -// { -//split3: -// temp = lp1; -// lp3 = lp2; -// lp1 = lp3; -// lp2 = temp; -// goto split; -// } -// -// return; // entire tri is filled -// - cmpl $2,%ebp - jna LDone - -LSplit3: - movl %edi,%ebx - movl %esi,%edi - movl %edx,%esi - jmp LSplit - -//split2: -LSplit2: - -// temp = lp1; -// lp1 = lp2; -// lp2 = lp3; -// lp3 = temp; - movl %ebx,%eax - movl %esi,%ebx - movl %edi,%esi - movl %eax,%edi - -//split: -LSplit: - - subl $24,%esp // allocate space for a new vertex - -//// split this edge -// new[0] = (lp1[0] + lp2[0]) >> 1; -// new[1] = (lp1[1] + lp2[1]) >> 1; -// new[2] = (lp1[2] + lp2[2]) >> 1; -// new[3] = (lp1[3] + lp2[3]) >> 1; -// new[5] = (lp1[5] + lp2[5]) >> 1; - movl 8(%ebx),%eax - - movl 8(%esi),%edx - movl 12(%ebx),%ecx - - addl %edx,%eax - movl 12(%esi),%edx - - sarl $1,%eax - addl %edx,%ecx - - movl %eax,8(%esp) - movl 20(%ebx),%eax - - sarl $1,%ecx - movl 20(%esi),%edx - - movl %ecx,12(%esp) - addl %edx,%eax - - movl 0(%ebx),%ecx - movl 0(%esi),%edx - - sarl $1,%eax - addl %ecx,%edx - - movl %eax,20(%esp) - movl 4(%ebx),%eax - - sarl $1,%edx - movl 4(%esi),%ebp - - movl %edx,0(%esp) - addl %eax,%ebp - - sarl $1,%ebp - movl %ebp,4(%esp) - -//// draw the point if splitting a leading edge -// if (lp2[1] > lp1[1]) -// goto nodraw; - cmpl %eax,4(%esi) - jg LNoDraw - -// if ((lp2[1] == lp1[1]) && (lp2[0] < lp1[0])) -// goto nodraw; - movl 0(%esi),%edx - jnz LDraw - - cmpl %ecx,%edx - jl LNoDraw - -LDraw: - -// z = new[5] >> 16; - movl 20(%esp),%edx - movl 4(%esp),%ecx - - sarl $16,%edx - movl 0(%esp),%ebp - -// zbuf = zspantable[new[1]] + new[0]; - movl C(zspantable)(,%ecx,4),%eax - -// if (z >= *zbuf) -// { - cmpw (%eax,%ebp,2),%dx - jnge LNoDraw - -// int pix; -// -// *zbuf = z; - movw %dx,(%eax,%ebp,2) - -// pix = d_pcolormap[skintable[new[3]>>16][new[2]>>16]]; - movl 12(%esp),%eax - - sarl $16,%eax - movl 8(%esp),%edx - - sarl $16,%edx - subl %ecx,%ecx - - movl C(skintable)(,%eax,4),%eax - movl 4(%esp),%ebp - - movb (%eax,%edx,),%cl - movl C(d_pcolormap),%edx - - movb (%edx,%ecx,),%dl - movl 0(%esp),%ecx - -// d_viewbuffer[d_scantable[new[1]] + new[0]] = pix; - movl C(d_scantable)(,%ebp,4),%eax - addl %eax,%ecx - movl C(d_viewbuffer),%eax - movb %dl,(%eax,%ecx,1) - -// } -// -//nodraw: -LNoDraw: - -//// recursively continue -// D_PolysetRecursiveTriangle (lp3, lp1, new); - pushl %esp - pushl %ebx - pushl %edi - call C(D_PolysetRecursiveTriangle) - -// D_PolysetRecursiveTriangle (lp3, new, lp2); - movl %esp,%ebx - pushl %esi - pushl %ebx - pushl %edi - call C(D_PolysetRecursiveTriangle) - addl $24,%esp - -LDone: - popl %ebx // restore register variables - popl %edi - popl %esi - popl %ebp // restore caller stack frame pointer - ret $12 - - -//---------------------------------------------------------------------- -// 8-bpp horizontal span drawing code for affine polygons, with smooth -// shading and no transparency -//---------------------------------------------------------------------- - -#define pspans 4+8 - -.globl C(D_PolysetAff8Start) -C(D_PolysetAff8Start): - -.globl C(D_PolysetDrawSpans8) -C(D_PolysetDrawSpans8): - pushl %esi // preserve register variables - pushl %ebx - - movl pspans(%esp),%esi // point to the first span descriptor - movl C(r_zistepx),%ecx - - pushl %ebp // preserve caller's stack frame - pushl %edi - - rorl $16,%ecx // put high 16 bits of 1/z step in low word - movl spanpackage_t_count(%esi),%edx - - movl %ecx,lzistepx - -LSpanLoop: - -// lcount = d_aspancount - pspanpackage->count; -// -// errorterm += erroradjustup; -// if (errorterm >= 0) -// { -// d_aspancount += d_countextrastep; -// errorterm -= erroradjustdown; -// } -// else -// { -// d_aspancount += ubasestep; -// } - movl C(d_aspancount),%eax - subl %edx,%eax - - movl C(erroradjustup),%edx - movl C(errorterm),%ebx - addl %edx,%ebx - js LNoTurnover - - movl C(erroradjustdown),%edx - movl C(d_countextrastep),%edi - subl %edx,%ebx - movl C(d_aspancount),%ebp - movl %ebx,C(errorterm) - addl %edi,%ebp - movl %ebp,C(d_aspancount) - jmp LRightEdgeStepped - -LNoTurnover: - movl C(d_aspancount),%edi - movl C(ubasestep),%edx - movl %ebx,C(errorterm) - addl %edx,%edi - movl %edi,C(d_aspancount) - -LRightEdgeStepped: - cmpl $1,%eax - - jl LNextSpan - jz LExactlyOneLong - -// -// set up advancetable -// - movl C(a_ststepxwhole),%ecx - movl C(r_affinetridesc)+atd_skinwidth,%edx - - movl %ecx,advancetable+4 // advance base in t - addl %edx,%ecx - - movl %ecx,advancetable // advance extra in t - movl C(a_tstepxfrac),%ecx - - movw C(r_lstepx),%cx - movl %eax,%edx // count - - movl %ecx,tstep - addl $7,%edx - - shrl $3,%edx // count of full and partial loops - movl spanpackage_t_sfrac(%esi),%ebx - - movw %dx,%bx - movl spanpackage_t_pz(%esi),%ecx - - negl %eax - - movl spanpackage_t_pdest(%esi),%edi - andl $7,%eax // 0->0, 1->7, 2->6, ... , 7->1 - - subl %eax,%edi // compensate for hardwired offsets - subl %eax,%ecx - - subl %eax,%ecx - movl spanpackage_t_tfrac(%esi),%edx - - movw spanpackage_t_light(%esi),%dx - movl spanpackage_t_zi(%esi),%ebp - - rorl $16,%ebp // put high 16 bits of 1/z in low word - pushl %esi - - movl spanpackage_t_ptex(%esi),%esi - jmp aff8entryvec_table(,%eax,4) - -// %bx = count of full and partial loops -// %ebx high word = sfrac -// %ecx = pz -// %dx = light -// %edx high word = tfrac -// %esi = ptex -// %edi = pdest -// %ebp = 1/z -// tstep low word = C(r_lstepx) -// tstep high word = C(a_tstepxfrac) -// C(a_sstepxfrac) low word = 0 -// C(a_sstepxfrac) high word = C(a_sstepxfrac) - -LDrawLoop: - -// FIXME: do we need to clamp light? We may need at least a buffer bit to -// keep it from poking into tfrac and causing problems - -LDraw8: - cmpw (%ecx),%bp - jl Lp1 - xorl %eax,%eax - movb %dh,%ah - movb (%esi),%al - movw %bp,(%ecx) - movb 0x12345678(%eax),%al -LPatch8: - movb %al,(%edi) -Lp1: - addl tstep,%edx - sbbl %eax,%eax - addl lzistepx,%ebp - adcl $0,%ebp - addl C(a_sstepxfrac),%ebx - adcl advancetable+4(,%eax,4),%esi - -LDraw7: - cmpw 2(%ecx),%bp - jl Lp2 - xorl %eax,%eax - movb %dh,%ah - movb (%esi),%al - movw %bp,2(%ecx) - movb 0x12345678(%eax),%al -LPatch7: - movb %al,1(%edi) -Lp2: - addl tstep,%edx - sbbl %eax,%eax - addl lzistepx,%ebp - adcl $0,%ebp - addl C(a_sstepxfrac),%ebx - adcl advancetable+4(,%eax,4),%esi - -LDraw6: - cmpw 4(%ecx),%bp - jl Lp3 - xorl %eax,%eax - movb %dh,%ah - movb (%esi),%al - movw %bp,4(%ecx) - movb 0x12345678(%eax),%al -LPatch6: - movb %al,2(%edi) -Lp3: - addl tstep,%edx - sbbl %eax,%eax - addl lzistepx,%ebp - adcl $0,%ebp - addl C(a_sstepxfrac),%ebx - adcl advancetable+4(,%eax,4),%esi - -LDraw5: - cmpw 6(%ecx),%bp - jl Lp4 - xorl %eax,%eax - movb %dh,%ah - movb (%esi),%al - movw %bp,6(%ecx) - movb 0x12345678(%eax),%al -LPatch5: - movb %al,3(%edi) -Lp4: - addl tstep,%edx - sbbl %eax,%eax - addl lzistepx,%ebp - adcl $0,%ebp - addl C(a_sstepxfrac),%ebx - adcl advancetable+4(,%eax,4),%esi - -LDraw4: - cmpw 8(%ecx),%bp - jl Lp5 - xorl %eax,%eax - movb %dh,%ah - movb (%esi),%al - movw %bp,8(%ecx) - movb 0x12345678(%eax),%al -LPatch4: - movb %al,4(%edi) -Lp5: - addl tstep,%edx - sbbl %eax,%eax - addl lzistepx,%ebp - adcl $0,%ebp - addl C(a_sstepxfrac),%ebx - adcl advancetable+4(,%eax,4),%esi - -LDraw3: - cmpw 10(%ecx),%bp - jl Lp6 - xorl %eax,%eax - movb %dh,%ah - movb (%esi),%al - movw %bp,10(%ecx) - movb 0x12345678(%eax),%al -LPatch3: - movb %al,5(%edi) -Lp6: - addl tstep,%edx - sbbl %eax,%eax - addl lzistepx,%ebp - adcl $0,%ebp - addl C(a_sstepxfrac),%ebx - adcl advancetable+4(,%eax,4),%esi - -LDraw2: - cmpw 12(%ecx),%bp - jl Lp7 - xorl %eax,%eax - movb %dh,%ah - movb (%esi),%al - movw %bp,12(%ecx) - movb 0x12345678(%eax),%al -LPatch2: - movb %al,6(%edi) -Lp7: - addl tstep,%edx - sbbl %eax,%eax - addl lzistepx,%ebp - adcl $0,%ebp - addl C(a_sstepxfrac),%ebx - adcl advancetable+4(,%eax,4),%esi - -LDraw1: - cmpw 14(%ecx),%bp - jl Lp8 - xorl %eax,%eax - movb %dh,%ah - movb (%esi),%al - movw %bp,14(%ecx) - movb 0x12345678(%eax),%al -LPatch1: - movb %al,7(%edi) -Lp8: - addl tstep,%edx - sbbl %eax,%eax - addl lzistepx,%ebp - adcl $0,%ebp - addl C(a_sstepxfrac),%ebx - adcl advancetable+4(,%eax,4),%esi - - addl $8,%edi - addl $16,%ecx - - decw %bx - jnz LDrawLoop - - popl %esi // restore spans pointer -LNextSpan: - addl $(spanpackage_t_size),%esi // point to next span -LNextSpanESISet: - movl spanpackage_t_count(%esi),%edx - cmpl $-999999,%edx // any more spans? - jnz LSpanLoop // yes - - popl %edi - popl %ebp // restore the caller's stack frame - popl %ebx // restore register variables - popl %esi - ret - - -// draw a one-long span - -LExactlyOneLong: - - movl spanpackage_t_pz(%esi),%ecx - movl spanpackage_t_zi(%esi),%ebp - - rorl $16,%ebp // put high 16 bits of 1/z in low word - movl spanpackage_t_ptex(%esi),%ebx - - cmpw (%ecx),%bp - jl LNextSpan - xorl %eax,%eax - movl spanpackage_t_pdest(%esi),%edi - movb spanpackage_t_light+1(%esi),%ah - addl $(spanpackage_t_size),%esi // point to next span - movb (%ebx),%al - movw %bp,(%ecx) - movb 0x12345678(%eax),%al -LPatch9: - movb %al,(%edi) - - jmp LNextSpanESISet - -.globl C(D_PolysetAff8End) -C(D_PolysetAff8End): - - -#define pcolormap 4 - -.globl C(D_Aff8Patch) -C(D_Aff8Patch): - movl pcolormap(%esp),%eax - movl %eax,LPatch1-4 - movl %eax,LPatch2-4 - movl %eax,LPatch3-4 - movl %eax,LPatch4-4 - movl %eax,LPatch5-4 - movl %eax,LPatch6-4 - movl %eax,LPatch7-4 - movl %eax,LPatch8-4 - movl %eax,LPatch9-4 - - ret - - -//---------------------------------------------------------------------- -// Alias model polygon dispatching code, combined with subdivided affine -// triangle drawing code -//---------------------------------------------------------------------- - -.globl C(D_PolysetDraw) -C(D_PolysetDraw): - -// spanpackage_t spans[DPS_MAXSPANS + 1 + -// ((CACHE_SIZE - 1) / sizeof(spanpackage_t)) + 1]; -// // one extra because of cache line pretouching -// -// a_spans = (spanpackage_t *) -// (((long)&spans[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1)); - subl $(SPAN_SIZE),%esp - movl %esp,%eax - addl $(CACHE_SIZE - 1),%eax - andl $(~(CACHE_SIZE - 1)),%eax - movl %eax,C(a_spans) - -// if (r_affinetridesc.drawtype) -// D_DrawSubdiv (); -// else -// D_DrawNonSubdiv (); - movl C(r_affinetridesc)+atd_drawtype,%eax - testl %eax,%eax - jz C(D_DrawNonSubdiv) - - pushl %ebp // preserve caller stack frame pointer - -// lnumtriangles = r_affinetridesc.numtriangles; - movl C(r_affinetridesc)+atd_numtriangles,%ebp - - pushl %esi // preserve register variables - shll $4,%ebp - - pushl %ebx -// ptri = r_affinetridesc.ptriangles; - movl C(r_affinetridesc)+atd_ptriangles,%ebx - - pushl %edi - -// mtriangle_t *ptri; -// finalvert_t *pfv, *index0, *index1, *index2; -// int i; -// int lnumtriangles; -// int s0, s1, s2; - -// pfv = r_affinetridesc.pfinalverts; - movl C(r_affinetridesc)+atd_pfinalverts,%edi - -// for (i=0 ; iv[1]-index1->v[1]) * -// (index0->v[0]-index2->v[0]) - -// (index0->v[0]-index1->v[0])*(index0->v[1]-index2->v[1])) >= 0) -// { -// continue; -// } -// -// d_pcolormap = &((byte *)acolormap)[index0->v[4] & 0xFF00]; - fildl fv_v+4(%ecx) // i0v1 - fildl fv_v+4(%esi) // i1v1 | i0v1 - fildl fv_v+0(%ecx) // i0v0 | i1v1 | i0v1 - fildl fv_v+0(%edx) // i2v0 | i0v0 | i1v1 | i0v1 - fxch %st(2) // i1v1 | i0v0 | i2v0 | i0v1 - fsubr %st(3),%st(0) // i0v1-i1v1 | i0v0 | i2v0 | i0v1 - fildl fv_v+0(%esi) // i1v0 | i0v1-i1v1 | i0v0 | i2v0 | i0v1 - fxch %st(2) // i0v0 | i0v1-i1v1 | i1v0 | i2v0 | i0v1 - fsub %st(0),%st(3) // i0v0 | i0v1-i1v1 | i1v0 | i0v0-i2v0 | i0v1 - fildl fv_v+4(%edx) // i2v1 | i0v0 | i0v1-i1v1 | i1v0 | i0v0-i2v0| i0v1 - fxch %st(1) // i0v0 | i2v1 | i0v1-i1v1 | i1v0 | i0v0-i2v0| i0v1 - fsubp %st(0),%st(3) // i2v1 | i0v1-i1v1 | i0v0-i1v0 | i0v0-i2v0 | i0v1 - fxch %st(1) // i0v1-i1v1 | i2v1 | i0v0-i1v0 | i0v0-i2v0 | i0v1 - fmulp %st(0),%st(3) // i2v1 | i0v0-i1v0 | i0v1-i1v1*i0v0-i2v0 | i0v1 - fsubrp %st(0),%st(3) // i0v0-i1v0 | i0v1-i1v1*i0v0-i2v0 | i0v1-i2v1 - movl fv_v+16(%ecx),%eax - andl $0xFF00,%eax - fmulp %st(0),%st(2) // i0v1-i1v1*i0v0-i2v0 | i0v0-i1v0*i0v1-i2v1 - addl C(acolormap),%eax - fsubp %st(0),%st(1) // (i0v1-i1v1)*(i0v0-i2v0)-(i0v0-i1v0)*(i0v1-i2v1) - movl %eax,C(d_pcolormap) - fstps Ltemp - movl Ltemp,%eax - subl $0x80000001,%eax - jc Lskip - -// if (ptri[i].facesfront) -// { -// D_PolysetRecursiveTriangle(index0->v, index1->v, index2->v); - movl mtri_facesfront-16(%ebx,%ebp,),%eax - testl %eax,%eax - jz Lfacesback - - pushl %edx - pushl %esi - pushl %ecx - call C(D_PolysetRecursiveTriangle) - - subl $16,%ebp - jnz Llooptop - jmp Ldone2 - -// } -// else -// { -Lfacesback: - -// s0 = index0->v[2]; -// s1 = index1->v[2]; -// s2 = index2->v[2]; - movl fv_v+8(%ecx),%eax - pushl %eax - movl fv_v+8(%esi),%eax - pushl %eax - movl fv_v+8(%edx),%eax - pushl %eax - pushl %ecx - pushl %edx - -// if (index0->flags & ALIAS_ONSEAM) -// index0->v[2] += r_affinetridesc.seamfixupX16; - movl C(r_affinetridesc)+atd_seamfixupX16,%eax - testl $(ALIAS_ONSEAM),fv_flags(%ecx) - jz Lp11 - addl %eax,fv_v+8(%ecx) -Lp11: - -// if (index1->flags & ALIAS_ONSEAM) -// index1->v[2] += r_affinetridesc.seamfixupX16; - testl $(ALIAS_ONSEAM),fv_flags(%esi) - jz Lp12 - addl %eax,fv_v+8(%esi) -Lp12: - -// if (index2->flags & ALIAS_ONSEAM) -// index2->v[2] += r_affinetridesc.seamfixupX16; - testl $(ALIAS_ONSEAM),fv_flags(%edx) - jz Lp13 - addl %eax,fv_v+8(%edx) -Lp13: - -// D_PolysetRecursiveTriangle(index0->v, index1->v, index2->v); - pushl %edx - pushl %esi - pushl %ecx - call C(D_PolysetRecursiveTriangle) - -// index0->v[2] = s0; -// index1->v[2] = s1; -// index2->v[2] = s2; - popl %edx - popl %ecx - popl %eax - movl %eax,fv_v+8(%edx) - popl %eax - movl %eax,fv_v+8(%esi) - popl %eax - movl %eax,fv_v+8(%ecx) - -// } -// } -Lskip: - subl $16,%ebp - jnz Llooptop - -Ldone2: - popl %edi // restore the caller's stack frame - popl %ebx - popl %esi // restore register variables - popl %ebp - - addl $(SPAN_SIZE),%esp - - ret - - -//---------------------------------------------------------------------- -// Alias model triangle left-edge scanning code -//---------------------------------------------------------------------- - -#define height 4+16 - -.globl C(D_PolysetScanLeftEdge) -C(D_PolysetScanLeftEdge): - pushl %ebp // preserve caller stack frame pointer - pushl %esi // preserve register variables - pushl %edi - pushl %ebx - - movl height(%esp),%eax - movl C(d_sfrac),%ecx - andl $0xFFFF,%eax - movl C(d_ptex),%ebx - orl %eax,%ecx - movl C(d_pedgespanpackage),%esi - movl C(d_tfrac),%edx - movl C(d_light),%edi - movl C(d_zi),%ebp - -// %eax: scratch -// %ebx: d_ptex -// %ecx: d_sfrac in high word, count in low word -// %edx: d_tfrac -// %esi: d_pedgespanpackage, errorterm, scratch alternately -// %edi: d_light -// %ebp: d_zi - -// do -// { - -LScanLoop: - -// d_pedgespanpackage->ptex = ptex; -// d_pedgespanpackage->pdest = d_pdest; -// d_pedgespanpackage->pz = d_pz; -// d_pedgespanpackage->count = d_aspancount; -// d_pedgespanpackage->light = d_light; -// d_pedgespanpackage->zi = d_zi; -// d_pedgespanpackage->sfrac = d_sfrac << 16; -// d_pedgespanpackage->tfrac = d_tfrac << 16; - movl %ebx,spanpackage_t_ptex(%esi) - movl C(d_pdest),%eax - movl %eax,spanpackage_t_pdest(%esi) - movl C(d_pz),%eax - movl %eax,spanpackage_t_pz(%esi) - movl C(d_aspancount),%eax - movl %eax,spanpackage_t_count(%esi) - movl %edi,spanpackage_t_light(%esi) - movl %ebp,spanpackage_t_zi(%esi) - movl %ecx,spanpackage_t_sfrac(%esi) - movl %edx,spanpackage_t_tfrac(%esi) - -// pretouch the next cache line - movb spanpackage_t_size(%esi),%al - -// d_pedgespanpackage++; - addl $(spanpackage_t_size),%esi - movl C(erroradjustup),%eax - movl %esi,C(d_pedgespanpackage) - -// errorterm += erroradjustup; - movl C(errorterm),%esi - addl %eax,%esi - movl C(d_pdest),%eax - -// if (errorterm >= 0) -// { - js LNoLeftEdgeTurnover - -// errorterm -= erroradjustdown; -// d_pdest += d_pdestextrastep; - subl C(erroradjustdown),%esi - addl C(d_pdestextrastep),%eax - movl %esi,C(errorterm) - movl %eax,C(d_pdest) - -// 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; - movl C(d_pz),%eax - movl C(d_aspancount),%esi - addl C(d_pzextrastep),%eax - addl C(d_sfracextrastep),%ecx - adcl C(d_ptexextrastep),%ebx - addl C(d_countextrastep),%esi - movl %eax,C(d_pz) - movl C(d_tfracextrastep),%eax - movl %esi,C(d_aspancount) - addl %eax,%edx - -// if (d_tfrac & 0x10000) -// { - jnc LSkip1 - -// d_ptex += r_affinetridesc.skinwidth; -// d_tfrac &= 0xFFFF; - addl C(r_affinetridesc)+atd_skinwidth,%ebx - -// } - -LSkip1: - -// d_light += d_lightextrastep; -// d_zi += d_ziextrastep; - addl C(d_lightextrastep),%edi - addl C(d_ziextrastep),%ebp - -// } - movl C(d_pedgespanpackage),%esi - decl %ecx - testl $0xFFFF,%ecx - jnz LScanLoop - - popl %ebx - popl %edi - popl %esi - popl %ebp - ret - -// else -// { - -LNoLeftEdgeTurnover: - movl %esi,C(errorterm) - -// d_pdest += d_pdestbasestep; - addl C(d_pdestbasestep),%eax - movl %eax,C(d_pdest) - -// d_pz += d_pzbasestep; -// d_aspancount += ubasestep; -// d_ptex += d_ptexbasestep; -// d_sfrac += d_sfracbasestep; -// d_ptex += d_sfrac >> 16; -// d_sfrac &= 0xFFFF; - movl C(d_pz),%eax - movl C(d_aspancount),%esi - addl C(d_pzbasestep),%eax - addl C(d_sfracbasestep),%ecx - adcl C(d_ptexbasestep),%ebx - addl C(ubasestep),%esi - movl %eax,C(d_pz) - movl %esi,C(d_aspancount) - -// d_tfrac += d_tfracbasestep; - movl C(d_tfracbasestep),%esi - addl %esi,%edx - -// if (d_tfrac & 0x10000) -// { - jnc LSkip2 - -// d_ptex += r_affinetridesc.skinwidth; -// d_tfrac &= 0xFFFF; - addl C(r_affinetridesc)+atd_skinwidth,%ebx - -// } - -LSkip2: - -// d_light += d_lightbasestep; -// d_zi += d_zibasestep; - addl C(d_lightbasestep),%edi - addl C(d_zibasestep),%ebp - -// } -// } while (--height); - movl C(d_pedgespanpackage),%esi - decl %ecx - testl $0xFFFF,%ecx - jnz LScanLoop - - popl %ebx - popl %edi - popl %esi - popl %ebp - ret - - -//---------------------------------------------------------------------- -// Alias model vertex drawing code -//---------------------------------------------------------------------- - -#define fv 4+8 -#define numverts 8+8 - -.globl C(D_PolysetDrawFinalVerts) -C(D_PolysetDrawFinalVerts): - pushl %ebp // preserve caller stack frame pointer - pushl %ebx - -// int i, z; -// short *zbuf; - - movl numverts(%esp),%ecx - movl fv(%esp),%ebx - - pushl %esi // preserve register variables - pushl %edi - -LFVLoop: - -// for (i=0 ; iv[0] < r_refdef.vrectright) && -// (fv->v[1] < r_refdef.vrectbottom)) -// { - movl fv_v+0(%ebx),%eax - movl C(r_refdef)+rd_vrectright,%edx - cmpl %edx,%eax - jge LNextVert - movl fv_v+4(%ebx),%esi - movl C(r_refdef)+rd_vrectbottom,%edx - cmpl %edx,%esi - jge LNextVert - -// zbuf = zspantable[fv->v[1]] + fv->v[0]; - movl C(zspantable)(,%esi,4),%edi - -// z = fv->v[5]>>16; - movl fv_v+20(%ebx),%edx - shrl $16,%edx - -// if (z >= *zbuf) -// { -// int pix; - cmpw (%edi,%eax,2),%dx - jl LNextVert - -// *zbuf = z; - movw %dx,(%edi,%eax,2) - -// pix = skintable[fv->v[3]>>16][fv->v[2]>>16]; - movl fv_v+12(%ebx),%edi - shrl $16,%edi - movl C(skintable)(,%edi,4),%edi - movl fv_v+8(%ebx),%edx - shrl $16,%edx - movb (%edi,%edx),%dl - -// pix = ((byte *)acolormap)[pix + (fv->v[4] & 0xFF00)]; - movl fv_v+16(%ebx),%edi - andl $0xFF00,%edi - andl $0x00FF,%edx - addl %edx,%edi - movl C(acolormap),%edx - movb (%edx,%edi,1),%dl - -// d_viewbuffer[d_scantable[fv->v[1]] + fv->v[0]] = pix; - movl C(d_scantable)(,%esi,4),%edi - movl C(d_viewbuffer),%esi - addl %eax,%edi - movb %dl,(%esi,%edi) - -// } -// } -// } -LNextVert: - addl $(fv_size),%ebx - decl %ecx - jnz LFVLoop - - popl %edi - popl %esi - popl %ebx - popl %ebp - ret - - -//---------------------------------------------------------------------- -// Alias model non-subdivided polygon dispatching code -// -// not C-callable because of stack buffer cleanup -//---------------------------------------------------------------------- - -.globl C(D_DrawNonSubdiv) -C(D_DrawNonSubdiv): - pushl %ebp // preserve caller stack frame pointer - movl C(r_affinetridesc)+atd_numtriangles,%ebp - pushl %ebx - shll $(mtri_shift),%ebp - pushl %esi // preserve register variables - movl C(r_affinetridesc)+atd_ptriangles,%esi - pushl %edi - -// mtriangle_t *ptri; -// finalvert_t *pfv, *index0, *index1, *index2; -// int i; -// int lnumtriangles; - -// pfv = r_affinetridesc.pfinalverts; -// ptri = r_affinetridesc.ptriangles; -// lnumtriangles = r_affinetridesc.numtriangles; - -LNDLoop: - -// for (i=0 ; ivertindex[0]; -// index1 = pfv + ptri->vertindex[1]; -// index2 = pfv + ptri->vertindex[2]; - movl C(r_affinetridesc)+atd_pfinalverts,%edi - movl mtri_vertindex+0-mtri_size(%esi,%ebp,1),%ecx - shll $(fv_shift),%ecx - movl mtri_vertindex+4-mtri_size(%esi,%ebp,1),%edx - shll $(fv_shift),%edx - movl mtri_vertindex+8-mtri_size(%esi,%ebp,1),%ebx - shll $(fv_shift),%ebx - addl %edi,%ecx - addl %edi,%edx - addl %edi,%ebx - -// d_xdenom = (index0->v[1]-index1->v[1]) * -// (index0->v[0]-index2->v[0]) - -// (index0->v[0]-index1->v[0])*(index0->v[1]-index2->v[1]); - movl fv_v+4(%ecx),%eax - movl fv_v+0(%ecx),%esi - subl fv_v+4(%edx),%eax - subl fv_v+0(%ebx),%esi - imull %esi,%eax - movl fv_v+0(%ecx),%esi - movl fv_v+4(%ecx),%edi - subl fv_v+0(%edx),%esi - subl fv_v+4(%ebx),%edi - imull %esi,%edi - subl %edi,%eax - -// if (d_xdenom >= 0) -// { -// continue; - jns LNextTri - -// } - - movl %eax,C(d_xdenom) - fildl C(d_xdenom) - -// r_p0[0] = index0->v[0]; // u -// r_p0[1] = index0->v[1]; // v -// r_p0[2] = index0->v[2]; // s -// r_p0[3] = index0->v[3]; // t -// r_p0[4] = index0->v[4]; // light -// r_p0[5] = index0->v[5]; // iz - movl fv_v+0(%ecx),%eax - movl fv_v+4(%ecx),%esi - movl %eax,C(r_p0)+0 - movl %esi,C(r_p0)+4 - movl fv_v+8(%ecx),%eax - movl fv_v+12(%ecx),%esi - movl %eax,C(r_p0)+8 - movl %esi,C(r_p0)+12 - movl fv_v+16(%ecx),%eax - movl fv_v+20(%ecx),%esi - movl %eax,C(r_p0)+16 - movl %esi,C(r_p0)+20 - - fdivrs float_1 - -// r_p1[0] = index1->v[0]; -// r_p1[1] = index1->v[1]; -// r_p1[2] = index1->v[2]; -// r_p1[3] = index1->v[3]; -// r_p1[4] = index1->v[4]; -// r_p1[5] = index1->v[5]; - movl fv_v+0(%edx),%eax - movl fv_v+4(%edx),%esi - movl %eax,C(r_p1)+0 - movl %esi,C(r_p1)+4 - movl fv_v+8(%edx),%eax - movl fv_v+12(%edx),%esi - movl %eax,C(r_p1)+8 - movl %esi,C(r_p1)+12 - movl fv_v+16(%edx),%eax - movl fv_v+20(%edx),%esi - movl %eax,C(r_p1)+16 - movl %esi,C(r_p1)+20 - -// r_p2[0] = index2->v[0]; -// r_p2[1] = index2->v[1]; -// r_p2[2] = index2->v[2]; -// r_p2[3] = index2->v[3]; -// r_p2[4] = index2->v[4]; -// r_p2[5] = index2->v[5]; - movl fv_v+0(%ebx),%eax - movl fv_v+4(%ebx),%esi - movl %eax,C(r_p2)+0 - movl %esi,C(r_p2)+4 - movl fv_v+8(%ebx),%eax - movl fv_v+12(%ebx),%esi - movl %eax,C(r_p2)+8 - movl %esi,C(r_p2)+12 - movl fv_v+16(%ebx),%eax - movl fv_v+20(%ebx),%esi - movl %eax,C(r_p2)+16 - movl C(r_affinetridesc)+atd_ptriangles,%edi - movl %esi,C(r_p2)+20 - movl mtri_facesfront-mtri_size(%edi,%ebp,1),%eax - -// if (!ptri->facesfront) -// { - testl %eax,%eax - jnz LFacesFront - -// if (index0->flags & ALIAS_ONSEAM) -// r_p0[2] += r_affinetridesc.seamfixupX16; - movl fv_flags(%ecx),%eax - movl fv_flags(%edx),%esi - movl fv_flags(%ebx),%edi - testl $(ALIAS_ONSEAM),%eax - movl C(r_affinetridesc)+atd_seamfixupX16,%eax - jz LOnseamDone0 - addl %eax,C(r_p0)+8 -LOnseamDone0: - -// if (index1->flags & ALIAS_ONSEAM) -// r_p1[2] += r_affinetridesc.seamfixupX16; - testl $(ALIAS_ONSEAM),%esi - jz LOnseamDone1 - addl %eax,C(r_p1)+8 -LOnseamDone1: - -// if (index2->flags & ALIAS_ONSEAM) -// r_p2[2] += r_affinetridesc.seamfixupX16; - testl $(ALIAS_ONSEAM),%edi - jz LOnseamDone2 - addl %eax,C(r_p2)+8 -LOnseamDone2: - -// } - -LFacesFront: - - fstps C(d_xdenom) - -// D_PolysetSetEdgeTable (); -// D_RasterizeAliasPolySmooth (); - call C(D_PolysetSetEdgeTable) - call C(D_RasterizeAliasPolySmooth) - -LNextTri: - movl C(r_affinetridesc)+atd_ptriangles,%esi - subl $16,%ebp - jnz LNDLoop -// } - - popl %edi - popl %esi - popl %ebx - popl %ebp - - addl $(SPAN_SIZE),%esp - - ret - - -#endif // id386 - +/* + d_polysa.S + + (description) + + Copyright (C) 1996-1997 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id: d_polysa.s,v 1.1.1.4 2004/10/18 17:44:27 vvd0 Exp $ +*/ +// d_polysa.s +// x86 assembly-language polygon model drawing code + +#include "asm_i386.h" +#include "quakeasm.h" +#include "asm_draw.h" +#include "d_ifacea.h" + +#ifdef id386 + +// !!! if this is changed, it must be changed in d_polyse.c too !!! +#define DPS_MAXSPANS MAXHEIGHT+1 + // 1 extra for spanpackage that marks end + +//#define SPAN_SIZE (((DPS_MAXSPANS + 1 + ((CACHE_SIZE - 1) / spanpackage_t_size)) + 1) * spanpackage_t_size) +#define SPAN_SIZE (1024+1+1+1)*32 + + + .data + + .align 4 +p10_minus_p20: .single 0 +p01_minus_p21: .single 0 +temp0: .single 0 +temp1: .single 0 +Ltemp: .single 0 + +aff8entryvec_table: .long LDraw8, LDraw7, LDraw6, LDraw5 + .long LDraw4, LDraw3, LDraw2, LDraw1 + +lzistepx: .long 0 + + + .text + +#ifndef NeXT + .extern C(D_PolysetSetEdgeTable) + .extern C(D_RasterizeAliasPolySmooth) +#endif + +//---------------------------------------------------------------------- +// affine triangle gradient calculation code +//---------------------------------------------------------------------- + +#define skinwidth 4+0 + +.globl C(D_PolysetCalcGradients) +C(D_PolysetCalcGradients): + +// 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 / (p10_minus_p20 * p01_minus_p21 - +// p00_minus_p20 * p11_minus_p21); +// +// ystepdenominv = -xstepdenominv; + + fildl C(r_p0)+0 // r_p0[0] + fildl C(r_p2)+0 // r_p2[0] | r_p0[0] + fildl C(r_p0)+4 // r_p0[1] | r_p2[0] | r_p0[0] + fildl C(r_p2)+4 // r_p2[1] | r_p0[1] | r_p2[0] | r_p0[0] + fildl C(r_p1)+0 // r_p1[0] | r_p2[1] | r_p0[1] | r_p2[0] | r_p0[0] + fildl C(r_p1)+4 // r_p1[1] | r_p1[0] | r_p2[1] | r_p0[1] | + // r_p2[0] | r_p0[0] + fxch %st(3) // r_p0[1] | r_p1[0] | r_p2[1] | r_p1[1] | + // r_p2[0] | r_p0[0] + fsub %st(2),%st(0) // p01_minus_p21 | r_p1[0] | r_p2[1] | r_p1[1] | + // r_p2[0] | r_p0[0] + fxch %st(1) // r_p1[0] | p01_minus_p21 | r_p2[1] | r_p1[1] | + // r_p2[0] | r_p0[0] + fsub %st(4),%st(0) // p10_minus_p20 | p01_minus_p21 | r_p2[1] | + // r_p1[1] | r_p2[0] | r_p0[0] + fxch %st(5) // r_p0[0] | p01_minus_p21 | r_p2[1] | + // r_p1[1] | r_p2[0] | p10_minus_p20 + fsubp %st(0),%st(4) // p01_minus_p21 | r_p2[1] | r_p1[1] | + // p00_minus_p20 | p10_minus_p20 + fxch %st(2) // r_p1[1] | r_p2[1] | p01_minus_p21 | + // p00_minus_p20 | p10_minus_p20 + fsubp %st(0),%st(1) // p11_minus_p21 | p01_minus_p21 | + // p00_minus_p20 | p10_minus_p20 + fxch %st(1) // p01_minus_p21 | p11_minus_p21 | + // p00_minus_p20 | p10_minus_p20 + flds C(d_xdenom) // d_xdenom | p01_minus_p21 | p11_minus_p21 | + // p00_minus_p20 | p10_minus_p20 + fxch %st(4) // p10_minus_p20 | p01_minus_p21 | p11_minus_p21 | + // p00_minus_p20 | d_xdenom + fstps p10_minus_p20 // p01_minus_p21 | p11_minus_p21 | + // p00_minus_p20 | d_xdenom + fstps p01_minus_p21 // p11_minus_p21 | p00_minus_p20 | xstepdenominv + fxch %st(2) // xstepdenominv | p00_minus_p20 | p11_minus_p21 + +//// 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]; + + fildl C(r_p2)+16 // r_p2[4] | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fildl C(r_p0)+16 // r_p0[4] | r_p2[4] | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + fildl C(r_p1)+16 // r_p1[4] | r_p0[4] | r_p2[4] | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + fxch %st(2) // r_p2[4] | r_p0[4] | r_p1[4] | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + fld %st(0) // r_p2[4] | r_p2[4] | r_p0[4] | r_p1[4] | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fsubrp %st(0),%st(2) // r_p2[4] | t0 | r_p1[4] | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + fsubrp %st(0),%st(2) // t0 | t1 | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + +// 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); + + fld %st(0) // t0 | t0 | t1 | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fmul %st(5),%st(0) // t0*p11_minus_p21 | t0 | t1 | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + fxch %st(2) // t1 | t0 | t0*p11_minus_p21 | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + fld %st(0) // t1 | t1 | t0 | t0*p11_minus_p21 | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fmuls p01_minus_p21 // t1*p01_minus_p21 | t1 | t0 | t0*p11_minus_p21 | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fxch %st(2) // t0 | t1 | t1*p01_minus_p21 | t0*p11_minus_p21 | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fmuls p10_minus_p20 // t0*p10_minus_p20 | t1 | t1*p01_minus_p21 | + // t0*p11_minus_p21 | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + fxch %st(1) // t1 | t0*p10_minus_p20 | t1*p01_minus_p21 | + // t0*p11_minus_p21 | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + fmul %st(5),%st(0) // t1*p00_minus_p20 | t0*p10_minus_p20 | + // t1*p01_minus_p21 | t0*p11_minus_p21 | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fxch %st(2) // t1*p01_minus_p21 | t0*p10_minus_p20 | + // t1*p00_minus_p20 | t0*p11_minus_p21 | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fsubp %st(0),%st(3) // t0*p10_minus_p20 | t1*p00_minus_p20 | + // t1*p01_minus_p21 - t0*p11_minus_p21 | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fsubrp %st(0),%st(1) // t1*p00_minus_p20 - t0*p10_minus_p20 | + // t1*p01_minus_p21 - t0*p11_minus_p21 | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fld %st(2) // xstepdenominv | + // t1*p00_minus_p20 - t0*p10_minus_p20 | + // t1*p01_minus_p21 - t0*p11_minus_p21 | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fmuls float_minus_1 // ystepdenominv | + // t1*p00_minus_p20 - t0*p10_minus_p20 | + // t1*p01_minus_p21 - t0*p11_minus_p21 | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fxch %st(2) // t1*p01_minus_p21 - t0*p11_minus_p21 | + // t1*p00_minus_p20 - t0*p10_minus_p20 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fmul %st(3),%st(0) // (t1*p01_minus_p21 - t0*p11_minus_p21)* + // xstepdenominv | + // t1*p00_minus_p20 - t0*p10_minus_p20 | + // | ystepdenominv | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + fxch %st(1) // t1*p00_minus_p20 - t0*p10_minus_p20 | + // (t1*p01_minus_p21 - t0*p11_minus_p21)* + // xstepdenominv | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fmul %st(2),%st(0) // (t1*p00_minus_p20 - t0*p10_minus_p20)* + // ystepdenominv | + // (t1*p01_minus_p21 - t0*p11_minus_p21)* + // xstepdenominv | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fldcw ceil_cw + fistpl C(r_lstepy) // r_lstepx | ystepdenominv | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + fistpl C(r_lstepx) // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fldcw single_cw + +// t0 = r_p0[2] - r_p2[2]; +// t1 = r_p1[2] - r_p2[2]; + + fildl C(r_p2)+8 // r_p2[2] | ystepdenominv | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + fildl C(r_p0)+8 // r_p0[2] | r_p2[2] | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fildl C(r_p1)+8 // r_p1[2] | r_p0[2] | r_p2[2] | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fxch %st(2) // r_p2[2] | r_p0[2] | r_p1[2] | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fld %st(0) // r_p2[2] | r_p2[2] | r_p0[2] | r_p1[2] | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fsubrp %st(0),%st(2) // r_p2[2] | t0 | r_p1[2] | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fsubrp %st(0),%st(2) // t0 | t1 | ystepdenominv | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + +// r_sstepx = (int)((t1 * p01_minus_p21 - t0 * p11_minus_p21) * +// xstepdenominv); +// r_sstepy = (int)((t1 * p00_minus_p20 - t0 * p10_minus_p20) * +// ystepdenominv); + + fld %st(0) // t0 | t0 | t1 | ystepdenominv | xstepdenominv + fmul %st(6),%st(0) // t0*p11_minus_p21 | t0 | t1 | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fxch %st(2) // t1 | t0 | t0*p11_minus_p21 | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fld %st(0) // t1 | t1 | t0 | t0*p11_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fmuls p01_minus_p21 // t1*p01_minus_p21 | t1 | t0 | t0*p11_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fxch %st(2) // t0 | t1 | t1*p01_minus_p21 | t0*p11_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fmuls p10_minus_p20 // t0*p10_minus_p20 | t1 | t1*p01_minus_p21 | + // t0*p11_minus_p21 | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fxch %st(1) // t1 | t0*p10_minus_p20 | t1*p01_minus_p21 | + // t0*p11_minus_p21 | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fmul %st(6),%st(0) // t1*p00_minus_p20 | t0*p10_minus_p20 | + // t1*p01_minus_p21 | t0*p11_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fxch %st(2) // t1*p01_minus_p21 | t0*p10_minus_p20 | + // t1*p00_minus_p20 | t0*p11_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fsubp %st(0),%st(3) // t0*p10_minus_p20 | t1*p00_minus_p20 | + // t1*p01_minus_p21 - t0*p11_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fsubrp %st(0),%st(1) // t1*p00_minus_p20 - t0*p10_minus_p20 | + // t1*p01_minus_p21 - t0*p11_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fmul %st(2),%st(0) // (t1*p00_minus_p20 - t0*p10_minus_p20)* + // ystepdenominv | + // t1*p01_minus_p21 - t0*p11_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fxch %st(1) // t1*p01_minus_p21 - t0*p11_minus_p21 | + // (t1*p00_minus_p20 - t0*p10_minus_p20)* + // ystepdenominv | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fmul %st(3),%st(0) // (t1*p01_minus_p21 - t0*p11_minus_p21)* + // xstepdenominv | + // (t1*p00_minus_p20 - t0*p10_minus_p20)* + // ystepdenominv | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fxch %st(1) // (t1*p00_minus_p20 - t0*p10_minus_p20)* + // ystepdenominv | + // (t1*p01_minus_p21 - t0*p11_minus_p21)* + // xstepdenominv | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fistpl C(r_sstepy) // r_sstepx | ystepdenominv | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + fistpl C(r_sstepx) // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + +// t0 = r_p0[3] - r_p2[3]; +// t1 = r_p1[3] - r_p2[3]; + + fildl C(r_p2)+12 // r_p2[3] | ystepdenominv | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + fildl C(r_p0)+12 // r_p0[3] | r_p2[3] | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fildl C(r_p1)+12 // r_p1[3] | r_p0[3] | r_p2[3] | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fxch %st(2) // r_p2[3] | r_p0[3] | r_p1[3] | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fld %st(0) // r_p2[3] | r_p2[3] | r_p0[3] | r_p1[3] | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fsubrp %st(0),%st(2) // r_p2[3] | t0 | r_p1[3] | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fsubrp %st(0),%st(2) // t0 | t1 | ystepdenominv | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + +// r_tstepx = (int)((t1 * p01_minus_p21 - t0 * p11_minus_p21) * +// xstepdenominv); +// r_tstepy = (int)((t1 * p00_minus_p20 - t0 * p10_minus_p20) * +// ystepdenominv); + + fld %st(0) // t0 | t0 | t1 | ystepdenominv | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + fmul %st(6),%st(0) // t0*p11_minus_p21 | t0 | t1 | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fxch %st(2) // t1 | t0 | t0*p11_minus_p21 | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fld %st(0) // t1 | t1 | t0 | t0*p11_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fmuls p01_minus_p21 // t1*p01_minus_p21 | t1 | t0 | t0*p11_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fxch %st(2) // t0 | t1 | t1*p01_minus_p21 | t0*p11_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fmuls p10_minus_p20 // t0*p10_minus_p20 | t1 | t1*p01_minus_p21 | + // t0*p11_minus_p21 | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fxch %st(1) // t1 | t0*p10_minus_p20 | t1*p01_minus_p21 | + // t0*p11_minus_p21 | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fmul %st(6),%st(0) // t1*p00_minus_p20 | t0*p10_minus_p20 | + // t1*p01_minus_p21 | t0*p11_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fxch %st(2) // t1*p01_minus_p21 | t0*p10_minus_p20 | + // t1*p00_minus_p20 | t0*p11_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fsubp %st(0),%st(3) // t0*p10_minus_p20 | t1*p00_minus_p20 | + // t1*p01_minus_p21 - t0*p11_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fsubrp %st(0),%st(1) // t1*p00_minus_p20 - t0*p10_minus_p20 | + // t1*p01_minus_p21 - t0*p11_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fmul %st(2),%st(0) // (t1*p00_minus_p20 - t0*p10_minus_p20)* + // ystepdenominv | + // t1*p01_minus_p21 - t0*p11_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fxch %st(1) // t1*p01_minus_p21 - t0*p11_minus_p21 | + // (t1*p00_minus_p20 - t0*p10_minus_p20)* + // ystepdenominv | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fmul %st(3),%st(0) // (t1*p01_minus_p21 - t0*p11_minus_p21)* + // xstepdenominv | + // (t1*p00_minus_p20 - t0*p10_minus_p20)* + // ystepdenominv | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fxch %st(1) // (t1*p00_minus_p20 - t0*p10_minus_p20)* + // ystepdenominv | + // (t1*p01_minus_p21 - t0*p11_minus_p21)* + // xstepdenominv | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fistpl C(r_tstepy) // r_tstepx | ystepdenominv | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + fistpl C(r_tstepx) // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + +// t0 = r_p0[5] - r_p2[5]; +// t1 = r_p1[5] - r_p2[5]; + + fildl C(r_p2)+20 // r_p2[5] | ystepdenominv | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + fildl C(r_p0)+20 // r_p0[5] | r_p2[5] | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fildl C(r_p1)+20 // r_p1[5] | r_p0[5] | r_p2[5] | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fxch %st(2) // r_p2[5] | r_p0[5] | r_p1[5] | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fld %st(0) // r_p2[5] | r_p2[5] | r_p0[5] | r_p1[5] | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fsubrp %st(0),%st(2) // r_p2[5] | t0 | r_p1[5] | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fsubrp %st(0),%st(2) // t0 | t1 | ystepdenominv | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + +// r_zistepx = (int)((t1 * p01_minus_p21 - t0 * p11_minus_p21) * +// xstepdenominv); +// r_zistepy = (int)((t1 * p00_minus_p20 - t0 * p10_minus_p20) * +// ystepdenominv); + + fld %st(0) // t0 | t0 | t1 | ystepdenominv | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + fmulp %st(0),%st(6) // t0 | t1 | ystepdenominv | xstepdenominv | + // p00_minus_p20 | t0*p11_minus_p21 + fxch %st(1) // t1 | t0 | ystepdenominv | xstepdenominv | + // p00_minus_p20 | t0*p11_minus_p21 + fld %st(0) // t1 | t1 | t0 | ystepdenominv | xstepdenominv | + // p00_minus_p20 | t0*p11_minus_p21 + fmuls p01_minus_p21 // t1*p01_minus_p21 | t1 | t0 | ystepdenominv | + // xstepdenominv | p00_minus_p20 | + // t0*p11_minus_p21 + fxch %st(2) // t0 | t1 | t1*p01_minus_p21 | ystepdenominv | + // xstepdenominv | p00_minus_p20 | + // t0*p11_minus_p21 + fmuls p10_minus_p20 // t0*p10_minus_p20 | t1 | t1*p01_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // t0*p11_minus_p21 + fxch %st(1) // t1 | t0*p10_minus_p20 | t1*p01_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // t0*p11_minus_p21 + fmulp %st(0),%st(5) // t0*p10_minus_p20 | t1*p01_minus_p21 | + // ystepdenominv | xstepdenominv | + // t1*p00_minus_p20 | t0*p11_minus_p21 + fxch %st(5) // t0*p11_minus_p21 | t1*p01_minus_p21 | + // ystepdenominv | xstepdenominv | + // t1*p00_minus_p20 | t0*p10_minus_p20 + fsubrp %st(0),%st(1) // t1*p01_minus_p21 - t0*p11_minus_p21 | + // ystepdenominv | xstepdenominv | + // t1*p00_minus_p20 | t0*p10_minus_p20 + fxch %st(3) // t1*p00_minus_p20 | ystepdenominv | + // xstepdenominv | + // t1*p01_minus_p21 - t0*p11_minus_p21 | + // t0*p10_minus_p20 + fsubp %st(0),%st(4) // ystepdenominv | xstepdenominv | + // t1*p01_minus_p21 - t0*p11_minus_p21 | + // t1*p00_minus_p20 - t0*p10_minus_p20 + fxch %st(1) // xstepdenominv | ystepdenominv | + // t1*p01_minus_p21 - t0*p11_minus_p21 | + // t1*p00_minus_p20 - t0*p10_minus_p20 + fmulp %st(0),%st(2) // ystepdenominv | + // (t1*p01_minus_p21 - t0*p11_minus_p21) * + // xstepdenominv | + // t1*p00_minus_p20 - t0*p10_minus_p20 + fmulp %st(0),%st(2) // (t1*p01_minus_p21 - t0*p11_minus_p21) * + // xstepdenominv | + // (t1*p00_minus_p20 - t0*p10_minus_p20) * + // ystepdenominv + fistpl C(r_zistepx) // (t1*p00_minus_p20 - t0*p10_minus_p20) * + // ystepdenominv + fistpl C(r_zistepy) + +// a_sstepxfrac = r_sstepx << 16; +// a_tstepxfrac = r_tstepx << 16; +// +// a_ststepxwhole = r_affinetridesc.skinwidth * (r_tstepx >> 16) + +// (r_sstepx >> 16); + + movl C(r_sstepx),%eax + movl C(r_tstepx),%edx + shll $16,%eax + shll $16,%edx + movl %eax,C(a_sstepxfrac) + movl %edx,C(a_tstepxfrac) + + movl C(r_sstepx),%ecx + movl C(r_tstepx),%eax + sarl $16,%ecx + sarl $16,%eax + imull skinwidth(%esp) + addl %ecx,%eax + movl %eax,C(a_ststepxwhole) + + ret + + +//---------------------------------------------------------------------- +// recursive subdivision affine triangle drawing code +// +// not C-callable because of stdcall return +//---------------------------------------------------------------------- + +#define lp1 4+16 +#define lp2 8+16 +#define lp3 12+16 + +.globl C(D_PolysetRecursiveTriangle) +C(D_PolysetRecursiveTriangle): + pushl %ebp // preserve caller stack frame pointer + pushl %esi // preserve register variables + pushl %edi + pushl %ebx + +// int *temp; +// int d; +// int new[6]; +// int i; +// int z; +// short *zbuf; + movl lp2(%esp),%esi + movl lp1(%esp),%ebx + movl lp3(%esp),%edi + +// d = lp2[0] - lp1[0]; +// if (d < -1 || d > 1) +// goto split; + movl 0(%esi),%eax + + movl 0(%ebx),%edx + movl 4(%esi),%ebp + + subl %edx,%eax + movl 4(%ebx),%ecx + + subl %ecx,%ebp + incl %eax + + cmpl $2,%eax + ja LSplit + +// d = lp2[1] - lp1[1]; +// if (d < -1 || d > 1) +// goto split; + movl 0(%edi),%eax + incl %ebp + + cmpl $2,%ebp + ja LSplit + +// d = lp3[0] - lp2[0]; +// if (d < -1 || d > 1) +// goto split2; + movl 0(%esi),%edx + movl 4(%edi),%ebp + + subl %edx,%eax + movl 4(%esi),%ecx + + subl %ecx,%ebp + incl %eax + + cmpl $2,%eax + ja LSplit2 + +// d = lp3[1] - lp2[1]; +// if (d < -1 || d > 1) +// goto split2; + movl 0(%ebx),%eax + incl %ebp + + cmpl $2,%ebp + ja LSplit2 + +// d = lp1[0] - lp3[0]; +// if (d < -1 || d > 1) +// goto split3; + movl 0(%edi),%edx + movl 4(%ebx),%ebp + + subl %edx,%eax + movl 4(%edi),%ecx + + subl %ecx,%ebp + incl %eax + + incl %ebp + movl %ebx,%edx + + cmpl $2,%eax + ja LSplit3 + +// d = lp1[1] - lp3[1]; +// if (d < -1 || d > 1) +// { +//split3: +// temp = lp1; +// lp3 = lp2; +// lp1 = lp3; +// lp2 = temp; +// goto split; +// } +// +// return; // entire tri is filled +// + cmpl $2,%ebp + jna LDone + +LSplit3: + movl %edi,%ebx + movl %esi,%edi + movl %edx,%esi + jmp LSplit + +//split2: +LSplit2: + +// temp = lp1; +// lp1 = lp2; +// lp2 = lp3; +// lp3 = temp; + movl %ebx,%eax + movl %esi,%ebx + movl %edi,%esi + movl %eax,%edi + +//split: +LSplit: + + subl $24,%esp // allocate space for a new vertex + +//// split this edge +// new[0] = (lp1[0] + lp2[0]) >> 1; +// new[1] = (lp1[1] + lp2[1]) >> 1; +// new[2] = (lp1[2] + lp2[2]) >> 1; +// new[3] = (lp1[3] + lp2[3]) >> 1; +// new[5] = (lp1[5] + lp2[5]) >> 1; + movl 8(%ebx),%eax + + movl 8(%esi),%edx + movl 12(%ebx),%ecx + + addl %edx,%eax + movl 12(%esi),%edx + + sarl $1,%eax + addl %edx,%ecx + + movl %eax,8(%esp) + movl 20(%ebx),%eax + + sarl $1,%ecx + movl 20(%esi),%edx + + movl %ecx,12(%esp) + addl %edx,%eax + + movl 0(%ebx),%ecx + movl 0(%esi),%edx + + sarl $1,%eax + addl %ecx,%edx + + movl %eax,20(%esp) + movl 4(%ebx),%eax + + sarl $1,%edx + movl 4(%esi),%ebp + + movl %edx,0(%esp) + addl %eax,%ebp + + sarl $1,%ebp + movl %ebp,4(%esp) + +//// draw the point if splitting a leading edge +// if (lp2[1] > lp1[1]) +// goto nodraw; + cmpl %eax,4(%esi) + jg LNoDraw + +// if ((lp2[1] == lp1[1]) && (lp2[0] < lp1[0])) +// goto nodraw; + movl 0(%esi),%edx + jnz LDraw + + cmpl %ecx,%edx + jl LNoDraw + +LDraw: + +// z = new[5] >> 16; + movl 20(%esp),%edx + movl 4(%esp),%ecx + + sarl $16,%edx + movl 0(%esp),%ebp + +// zbuf = zspantable[new[1]] + new[0]; + movl C(zspantable)(,%ecx,4),%eax + +// if (z >= *zbuf) +// { + cmpw (%eax,%ebp,2),%dx + jnge LNoDraw + +// int pix; +// +// *zbuf = z; + movw %dx,(%eax,%ebp,2) + +// pix = d_pcolormap[skintable[new[3]>>16][new[2]>>16]]; + movl 12(%esp),%eax + + sarl $16,%eax + movl 8(%esp),%edx + + sarl $16,%edx + subl %ecx,%ecx + + movl C(skintable)(,%eax,4),%eax + movl 4(%esp),%ebp + + movb (%eax,%edx,),%cl + movl C(d_pcolormap),%edx + + movb (%edx,%ecx,),%dl + movl 0(%esp),%ecx + +// d_viewbuffer[d_scantable[new[1]] + new[0]] = pix; + movl C(d_scantable)(,%ebp,4),%eax + addl %eax,%ecx + movl C(d_viewbuffer),%eax + movb %dl,(%eax,%ecx,1) + +// } +// +//nodraw: +LNoDraw: + +//// recursively continue +// D_PolysetRecursiveTriangle (lp3, lp1, new); + pushl %esp + pushl %ebx + pushl %edi + call C(D_PolysetRecursiveTriangle) + +// D_PolysetRecursiveTriangle (lp3, new, lp2); + movl %esp,%ebx + pushl %esi + pushl %ebx + pushl %edi + call C(D_PolysetRecursiveTriangle) + addl $24,%esp + +LDone: + popl %ebx // restore register variables + popl %edi + popl %esi + popl %ebp // restore caller stack frame pointer + ret $12 + + +//---------------------------------------------------------------------- +// 8-bpp horizontal span drawing code for affine polygons, with smooth +// shading and no transparency +//---------------------------------------------------------------------- + +#define pspans 4+8 + +.globl C(D_PolysetAff8Start) +C(D_PolysetAff8Start): + +.globl C(D_PolysetDrawSpans8) +C(D_PolysetDrawSpans8): + pushl %esi // preserve register variables + pushl %ebx + + movl pspans(%esp),%esi // point to the first span descriptor + movl C(r_zistepx),%ecx + + pushl %ebp // preserve caller's stack frame + pushl %edi + + rorl $16,%ecx // put high 16 bits of 1/z step in low word + movl spanpackage_t_count(%esi),%edx + + movl %ecx,lzistepx + +LSpanLoop: + +// lcount = d_aspancount - pspanpackage->count; +// +// errorterm += erroradjustup; +// if (errorterm >= 0) +// { +// d_aspancount += d_countextrastep; +// errorterm -= erroradjustdown; +// } +// else +// { +// d_aspancount += ubasestep; +// } + movl C(d_aspancount),%eax + subl %edx,%eax + + movl C(erroradjustup),%edx + movl C(errorterm),%ebx + addl %edx,%ebx + js LNoTurnover + + movl C(erroradjustdown),%edx + movl C(d_countextrastep),%edi + subl %edx,%ebx + movl C(d_aspancount),%ebp + movl %ebx,C(errorterm) + addl %edi,%ebp + movl %ebp,C(d_aspancount) + jmp LRightEdgeStepped + +LNoTurnover: + movl C(d_aspancount),%edi + movl C(ubasestep),%edx + movl %ebx,C(errorterm) + addl %edx,%edi + movl %edi,C(d_aspancount) + +LRightEdgeStepped: + cmpl $1,%eax + + jl LNextSpan + jz LExactlyOneLong + +// +// set up advancetable +// + movl C(a_ststepxwhole),%ecx + movl C(r_affinetridesc)+atd_skinwidth,%edx + + movl %ecx,advancetable+4 // advance base in t + addl %edx,%ecx + + movl %ecx,advancetable // advance extra in t + movl C(a_tstepxfrac),%ecx + + movw C(r_lstepx),%cx + movl %eax,%edx // count + + movl %ecx,tstep + addl $7,%edx + + shrl $3,%edx // count of full and partial loops + movl spanpackage_t_sfrac(%esi),%ebx + + movw %dx,%bx + movl spanpackage_t_pz(%esi),%ecx + + negl %eax + + movl spanpackage_t_pdest(%esi),%edi + andl $7,%eax // 0->0, 1->7, 2->6, ... , 7->1 + + subl %eax,%edi // compensate for hardwired offsets + subl %eax,%ecx + + subl %eax,%ecx + movl spanpackage_t_tfrac(%esi),%edx + + movw spanpackage_t_light(%esi),%dx + movl spanpackage_t_zi(%esi),%ebp + + rorl $16,%ebp // put high 16 bits of 1/z in low word + pushl %esi + + movl spanpackage_t_ptex(%esi),%esi + jmp aff8entryvec_table(,%eax,4) + +// %bx = count of full and partial loops +// %ebx high word = sfrac +// %ecx = pz +// %dx = light +// %edx high word = tfrac +// %esi = ptex +// %edi = pdest +// %ebp = 1/z +// tstep low word = C(r_lstepx) +// tstep high word = C(a_tstepxfrac) +// C(a_sstepxfrac) low word = 0 +// C(a_sstepxfrac) high word = C(a_sstepxfrac) + +LDrawLoop: + +// FIXME: do we need to clamp light? We may need at least a buffer bit to +// keep it from poking into tfrac and causing problems + +LDraw8: + cmpw (%ecx),%bp + jl Lp1 + xorl %eax,%eax + movb %dh,%ah + movb (%esi),%al + movw %bp,(%ecx) + movb 0x12345678(%eax),%al +LPatch8: + movb %al,(%edi) +Lp1: + addl tstep,%edx + sbbl %eax,%eax + addl lzistepx,%ebp + adcl $0,%ebp + addl C(a_sstepxfrac),%ebx + adcl advancetable+4(,%eax,4),%esi + +LDraw7: + cmpw 2(%ecx),%bp + jl Lp2 + xorl %eax,%eax + movb %dh,%ah + movb (%esi),%al + movw %bp,2(%ecx) + movb 0x12345678(%eax),%al +LPatch7: + movb %al,1(%edi) +Lp2: + addl tstep,%edx + sbbl %eax,%eax + addl lzistepx,%ebp + adcl $0,%ebp + addl C(a_sstepxfrac),%ebx + adcl advancetable+4(,%eax,4),%esi + +LDraw6: + cmpw 4(%ecx),%bp + jl Lp3 + xorl %eax,%eax + movb %dh,%ah + movb (%esi),%al + movw %bp,4(%ecx) + movb 0x12345678(%eax),%al +LPatch6: + movb %al,2(%edi) +Lp3: + addl tstep,%edx + sbbl %eax,%eax + addl lzistepx,%ebp + adcl $0,%ebp + addl C(a_sstepxfrac),%ebx + adcl advancetable+4(,%eax,4),%esi + +LDraw5: + cmpw 6(%ecx),%bp + jl Lp4 + xorl %eax,%eax + movb %dh,%ah + movb (%esi),%al + movw %bp,6(%ecx) + movb 0x12345678(%eax),%al +LPatch5: + movb %al,3(%edi) +Lp4: + addl tstep,%edx + sbbl %eax,%eax + addl lzistepx,%ebp + adcl $0,%ebp + addl C(a_sstepxfrac),%ebx + adcl advancetable+4(,%eax,4),%esi + +LDraw4: + cmpw 8(%ecx),%bp + jl Lp5 + xorl %eax,%eax + movb %dh,%ah + movb (%esi),%al + movw %bp,8(%ecx) + movb 0x12345678(%eax),%al +LPatch4: + movb %al,4(%edi) +Lp5: + addl tstep,%edx + sbbl %eax,%eax + addl lzistepx,%ebp + adcl $0,%ebp + addl C(a_sstepxfrac),%ebx + adcl advancetable+4(,%eax,4),%esi + +LDraw3: + cmpw 10(%ecx),%bp + jl Lp6 + xorl %eax,%eax + movb %dh,%ah + movb (%esi),%al + movw %bp,10(%ecx) + movb 0x12345678(%eax),%al +LPatch3: + movb %al,5(%edi) +Lp6: + addl tstep,%edx + sbbl %eax,%eax + addl lzistepx,%ebp + adcl $0,%ebp + addl C(a_sstepxfrac),%ebx + adcl advancetable+4(,%eax,4),%esi + +LDraw2: + cmpw 12(%ecx),%bp + jl Lp7 + xorl %eax,%eax + movb %dh,%ah + movb (%esi),%al + movw %bp,12(%ecx) + movb 0x12345678(%eax),%al +LPatch2: + movb %al,6(%edi) +Lp7: + addl tstep,%edx + sbbl %eax,%eax + addl lzistepx,%ebp + adcl $0,%ebp + addl C(a_sstepxfrac),%ebx + adcl advancetable+4(,%eax,4),%esi + +LDraw1: + cmpw 14(%ecx),%bp + jl Lp8 + xorl %eax,%eax + movb %dh,%ah + movb (%esi),%al + movw %bp,14(%ecx) + movb 0x12345678(%eax),%al +LPatch1: + movb %al,7(%edi) +Lp8: + addl tstep,%edx + sbbl %eax,%eax + addl lzistepx,%ebp + adcl $0,%ebp + addl C(a_sstepxfrac),%ebx + adcl advancetable+4(,%eax,4),%esi + + addl $8,%edi + addl $16,%ecx + + decw %bx + jnz LDrawLoop + + popl %esi // restore spans pointer +LNextSpan: + addl $(spanpackage_t_size),%esi // point to next span +LNextSpanESISet: + movl spanpackage_t_count(%esi),%edx + cmpl $-999999,%edx // any more spans? + jnz LSpanLoop // yes + + popl %edi + popl %ebp // restore the caller's stack frame + popl %ebx // restore register variables + popl %esi + ret + + +// draw a one-long span + +LExactlyOneLong: + + movl spanpackage_t_pz(%esi),%ecx + movl spanpackage_t_zi(%esi),%ebp + + rorl $16,%ebp // put high 16 bits of 1/z in low word + movl spanpackage_t_ptex(%esi),%ebx + + cmpw (%ecx),%bp + jl LNextSpan + xorl %eax,%eax + movl spanpackage_t_pdest(%esi),%edi + movb spanpackage_t_light+1(%esi),%ah + addl $(spanpackage_t_size),%esi // point to next span + movb (%ebx),%al + movw %bp,(%ecx) + movb 0x12345678(%eax),%al +LPatch9: + movb %al,(%edi) + + jmp LNextSpanESISet + +.globl C(D_PolysetAff8End) +C(D_PolysetAff8End): + + +#define pcolormap 4 + +.globl C(D_Aff8Patch) +C(D_Aff8Patch): + movl pcolormap(%esp),%eax + movl %eax,LPatch1-4 + movl %eax,LPatch2-4 + movl %eax,LPatch3-4 + movl %eax,LPatch4-4 + movl %eax,LPatch5-4 + movl %eax,LPatch6-4 + movl %eax,LPatch7-4 + movl %eax,LPatch8-4 + movl %eax,LPatch9-4 + + ret + + +//---------------------------------------------------------------------- +// Alias model polygon dispatching code, combined with subdivided affine +// triangle drawing code +//---------------------------------------------------------------------- + +.globl C(D_PolysetDraw) +C(D_PolysetDraw): + +// spanpackage_t spans[DPS_MAXSPANS + 1 + +// ((CACHE_SIZE - 1) / sizeof(spanpackage_t)) + 1]; +// // one extra because of cache line pretouching +// +// a_spans = (spanpackage_t *) +// (((long)&spans[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1)); + subl $(SPAN_SIZE),%esp + movl %esp,%eax + addl $(CACHE_SIZE - 1),%eax + andl $(~(CACHE_SIZE - 1)),%eax + movl %eax,C(a_spans) + +// if (r_affinetridesc.drawtype) +// D_DrawSubdiv (); +// else +// D_DrawNonSubdiv (); + movl C(r_affinetridesc)+atd_drawtype,%eax + testl %eax,%eax + jz C(D_DrawNonSubdiv) + + pushl %ebp // preserve caller stack frame pointer + +// lnumtriangles = r_affinetridesc.numtriangles; + movl C(r_affinetridesc)+atd_numtriangles,%ebp + + pushl %esi // preserve register variables + shll $4,%ebp + + pushl %ebx +// ptri = r_affinetridesc.ptriangles; + movl C(r_affinetridesc)+atd_ptriangles,%ebx + + pushl %edi + +// mtriangle_t *ptri; +// finalvert_t *pfv, *index0, *index1, *index2; +// int i; +// int lnumtriangles; +// int s0, s1, s2; + +// pfv = r_affinetridesc.pfinalverts; + movl C(r_affinetridesc)+atd_pfinalverts,%edi + +// for (i=0 ; iv[1]-index1->v[1]) * +// (index0->v[0]-index2->v[0]) - +// (index0->v[0]-index1->v[0])*(index0->v[1]-index2->v[1])) >= 0) +// { +// continue; +// } +// +// d_pcolormap = &((byte *)acolormap)[index0->v[4] & 0xFF00]; + fildl fv_v+4(%ecx) // i0v1 + fildl fv_v+4(%esi) // i1v1 | i0v1 + fildl fv_v+0(%ecx) // i0v0 | i1v1 | i0v1 + fildl fv_v+0(%edx) // i2v0 | i0v0 | i1v1 | i0v1 + fxch %st(2) // i1v1 | i0v0 | i2v0 | i0v1 + fsubr %st(3),%st(0) // i0v1-i1v1 | i0v0 | i2v0 | i0v1 + fildl fv_v+0(%esi) // i1v0 | i0v1-i1v1 | i0v0 | i2v0 | i0v1 + fxch %st(2) // i0v0 | i0v1-i1v1 | i1v0 | i2v0 | i0v1 + fsub %st(0),%st(3) // i0v0 | i0v1-i1v1 | i1v0 | i0v0-i2v0 | i0v1 + fildl fv_v+4(%edx) // i2v1 | i0v0 | i0v1-i1v1 | i1v0 | i0v0-i2v0| i0v1 + fxch %st(1) // i0v0 | i2v1 | i0v1-i1v1 | i1v0 | i0v0-i2v0| i0v1 + fsubp %st(0),%st(3) // i2v1 | i0v1-i1v1 | i0v0-i1v0 | i0v0-i2v0 | i0v1 + fxch %st(1) // i0v1-i1v1 | i2v1 | i0v0-i1v0 | i0v0-i2v0 | i0v1 + fmulp %st(0),%st(3) // i2v1 | i0v0-i1v0 | i0v1-i1v1*i0v0-i2v0 | i0v1 + fsubrp %st(0),%st(3) // i0v0-i1v0 | i0v1-i1v1*i0v0-i2v0 | i0v1-i2v1 + movl fv_v+16(%ecx),%eax + andl $0xFF00,%eax + fmulp %st(0),%st(2) // i0v1-i1v1*i0v0-i2v0 | i0v0-i1v0*i0v1-i2v1 + addl C(acolormap),%eax + fsubp %st(0),%st(1) // (i0v1-i1v1)*(i0v0-i2v0)-(i0v0-i1v0)*(i0v1-i2v1) + movl %eax,C(d_pcolormap) + fstps Ltemp + movl Ltemp,%eax + subl $0x80000001,%eax + jc Lskip + +// if (ptri[i].facesfront) +// { +// D_PolysetRecursiveTriangle(index0->v, index1->v, index2->v); + movl mtri_facesfront-16(%ebx,%ebp,),%eax + testl %eax,%eax + jz Lfacesback + + pushl %edx + pushl %esi + pushl %ecx + call C(D_PolysetRecursiveTriangle) + + subl $16,%ebp + jnz Llooptop + jmp Ldone2 + +// } +// else +// { +Lfacesback: + +// s0 = index0->v[2]; +// s1 = index1->v[2]; +// s2 = index2->v[2]; + movl fv_v+8(%ecx),%eax + pushl %eax + movl fv_v+8(%esi),%eax + pushl %eax + movl fv_v+8(%edx),%eax + pushl %eax + pushl %ecx + pushl %edx + +// if (index0->flags & ALIAS_ONSEAM) +// index0->v[2] += r_affinetridesc.seamfixupX16; + movl C(r_affinetridesc)+atd_seamfixupX16,%eax + testl $(ALIAS_ONSEAM),fv_flags(%ecx) + jz Lp11 + addl %eax,fv_v+8(%ecx) +Lp11: + +// if (index1->flags & ALIAS_ONSEAM) +// index1->v[2] += r_affinetridesc.seamfixupX16; + testl $(ALIAS_ONSEAM),fv_flags(%esi) + jz Lp12 + addl %eax,fv_v+8(%esi) +Lp12: + +// if (index2->flags & ALIAS_ONSEAM) +// index2->v[2] += r_affinetridesc.seamfixupX16; + testl $(ALIAS_ONSEAM),fv_flags(%edx) + jz Lp13 + addl %eax,fv_v+8(%edx) +Lp13: + +// D_PolysetRecursiveTriangle(index0->v, index1->v, index2->v); + pushl %edx + pushl %esi + pushl %ecx + call C(D_PolysetRecursiveTriangle) + +// index0->v[2] = s0; +// index1->v[2] = s1; +// index2->v[2] = s2; + popl %edx + popl %ecx + popl %eax + movl %eax,fv_v+8(%edx) + popl %eax + movl %eax,fv_v+8(%esi) + popl %eax + movl %eax,fv_v+8(%ecx) + +// } +// } +Lskip: + subl $16,%ebp + jnz Llooptop + +Ldone2: + popl %edi // restore the caller's stack frame + popl %ebx + popl %esi // restore register variables + popl %ebp + + addl $(SPAN_SIZE),%esp + + ret + + +//---------------------------------------------------------------------- +// Alias model triangle left-edge scanning code +//---------------------------------------------------------------------- + +#define height 4+16 + +.globl C(D_PolysetScanLeftEdge) +C(D_PolysetScanLeftEdge): + pushl %ebp // preserve caller stack frame pointer + pushl %esi // preserve register variables + pushl %edi + pushl %ebx + + movl height(%esp),%eax + movl C(d_sfrac),%ecx + andl $0xFFFF,%eax + movl C(d_ptex),%ebx + orl %eax,%ecx + movl C(d_pedgespanpackage),%esi + movl C(d_tfrac),%edx + movl C(d_light),%edi + movl C(d_zi),%ebp + +// %eax: scratch +// %ebx: d_ptex +// %ecx: d_sfrac in high word, count in low word +// %edx: d_tfrac +// %esi: d_pedgespanpackage, errorterm, scratch alternately +// %edi: d_light +// %ebp: d_zi + +// do +// { + +LScanLoop: + +// d_pedgespanpackage->ptex = ptex; +// d_pedgespanpackage->pdest = d_pdest; +// d_pedgespanpackage->pz = d_pz; +// d_pedgespanpackage->count = d_aspancount; +// d_pedgespanpackage->light = d_light; +// d_pedgespanpackage->zi = d_zi; +// d_pedgespanpackage->sfrac = d_sfrac << 16; +// d_pedgespanpackage->tfrac = d_tfrac << 16; + movl %ebx,spanpackage_t_ptex(%esi) + movl C(d_pdest),%eax + movl %eax,spanpackage_t_pdest(%esi) + movl C(d_pz),%eax + movl %eax,spanpackage_t_pz(%esi) + movl C(d_aspancount),%eax + movl %eax,spanpackage_t_count(%esi) + movl %edi,spanpackage_t_light(%esi) + movl %ebp,spanpackage_t_zi(%esi) + movl %ecx,spanpackage_t_sfrac(%esi) + movl %edx,spanpackage_t_tfrac(%esi) + +// pretouch the next cache line + movb spanpackage_t_size(%esi),%al + +// d_pedgespanpackage++; + addl $(spanpackage_t_size),%esi + movl C(erroradjustup),%eax + movl %esi,C(d_pedgespanpackage) + +// errorterm += erroradjustup; + movl C(errorterm),%esi + addl %eax,%esi + movl C(d_pdest),%eax + +// if (errorterm >= 0) +// { + js LNoLeftEdgeTurnover + +// errorterm -= erroradjustdown; +// d_pdest += d_pdestextrastep; + subl C(erroradjustdown),%esi + addl C(d_pdestextrastep),%eax + movl %esi,C(errorterm) + movl %eax,C(d_pdest) + +// 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; + movl C(d_pz),%eax + movl C(d_aspancount),%esi + addl C(d_pzextrastep),%eax + addl C(d_sfracextrastep),%ecx + adcl C(d_ptexextrastep),%ebx + addl C(d_countextrastep),%esi + movl %eax,C(d_pz) + movl C(d_tfracextrastep),%eax + movl %esi,C(d_aspancount) + addl %eax,%edx + +// if (d_tfrac & 0x10000) +// { + jnc LSkip1 + +// d_ptex += r_affinetridesc.skinwidth; +// d_tfrac &= 0xFFFF; + addl C(r_affinetridesc)+atd_skinwidth,%ebx + +// } + +LSkip1: + +// d_light += d_lightextrastep; +// d_zi += d_ziextrastep; + addl C(d_lightextrastep),%edi + addl C(d_ziextrastep),%ebp + +// } + movl C(d_pedgespanpackage),%esi + decl %ecx + testl $0xFFFF,%ecx + jnz LScanLoop + + popl %ebx + popl %edi + popl %esi + popl %ebp + ret + +// else +// { + +LNoLeftEdgeTurnover: + movl %esi,C(errorterm) + +// d_pdest += d_pdestbasestep; + addl C(d_pdestbasestep),%eax + movl %eax,C(d_pdest) + +// d_pz += d_pzbasestep; +// d_aspancount += ubasestep; +// d_ptex += d_ptexbasestep; +// d_sfrac += d_sfracbasestep; +// d_ptex += d_sfrac >> 16; +// d_sfrac &= 0xFFFF; + movl C(d_pz),%eax + movl C(d_aspancount),%esi + addl C(d_pzbasestep),%eax + addl C(d_sfracbasestep),%ecx + adcl C(d_ptexbasestep),%ebx + addl C(ubasestep),%esi + movl %eax,C(d_pz) + movl %esi,C(d_aspancount) + +// d_tfrac += d_tfracbasestep; + movl C(d_tfracbasestep),%esi + addl %esi,%edx + +// if (d_tfrac & 0x10000) +// { + jnc LSkip2 + +// d_ptex += r_affinetridesc.skinwidth; +// d_tfrac &= 0xFFFF; + addl C(r_affinetridesc)+atd_skinwidth,%ebx + +// } + +LSkip2: + +// d_light += d_lightbasestep; +// d_zi += d_zibasestep; + addl C(d_lightbasestep),%edi + addl C(d_zibasestep),%ebp + +// } +// } while (--height); + movl C(d_pedgespanpackage),%esi + decl %ecx + testl $0xFFFF,%ecx + jnz LScanLoop + + popl %ebx + popl %edi + popl %esi + popl %ebp + ret + + +//---------------------------------------------------------------------- +// Alias model vertex drawing code +//---------------------------------------------------------------------- + +#define fv 4+8 +#define numverts 8+8 + +.globl C(D_PolysetDrawFinalVerts) +C(D_PolysetDrawFinalVerts): + pushl %ebp // preserve caller stack frame pointer + pushl %ebx + +// int i, z; +// short *zbuf; + + movl numverts(%esp),%ecx + movl fv(%esp),%ebx + + pushl %esi // preserve register variables + pushl %edi + +LFVLoop: + +// for (i=0 ; iv[0] < r_refdef.vrectright) && +// (fv->v[1] < r_refdef.vrectbottom)) +// { + movl fv_v+0(%ebx),%eax + movl C(r_refdef)+rd_vrectright,%edx + cmpl %edx,%eax + jge LNextVert + movl fv_v+4(%ebx),%esi + movl C(r_refdef)+rd_vrectbottom,%edx + cmpl %edx,%esi + jge LNextVert + +// zbuf = zspantable[fv->v[1]] + fv->v[0]; + movl C(zspantable)(,%esi,4),%edi + +// z = fv->v[5]>>16; + movl fv_v+20(%ebx),%edx + shrl $16,%edx + +// if (z >= *zbuf) +// { +// int pix; + cmpw (%edi,%eax,2),%dx + jl LNextVert + +// *zbuf = z; + movw %dx,(%edi,%eax,2) + +// pix = skintable[fv->v[3]>>16][fv->v[2]>>16]; + movl fv_v+12(%ebx),%edi + shrl $16,%edi + movl C(skintable)(,%edi,4),%edi + movl fv_v+8(%ebx),%edx + shrl $16,%edx + movb (%edi,%edx),%dl + +// pix = ((byte *)acolormap)[pix + (fv->v[4] & 0xFF00)]; + movl fv_v+16(%ebx),%edi + andl $0xFF00,%edi + andl $0x00FF,%edx + addl %edx,%edi + movl C(acolormap),%edx + movb (%edx,%edi,1),%dl + +// d_viewbuffer[d_scantable[fv->v[1]] + fv->v[0]] = pix; + movl C(d_scantable)(,%esi,4),%edi + movl C(d_viewbuffer),%esi + addl %eax,%edi + movb %dl,(%esi,%edi) + +// } +// } +// } +LNextVert: + addl $(fv_size),%ebx + decl %ecx + jnz LFVLoop + + popl %edi + popl %esi + popl %ebx + popl %ebp + ret + + +//---------------------------------------------------------------------- +// Alias model non-subdivided polygon dispatching code +// +// not C-callable because of stack buffer cleanup +//---------------------------------------------------------------------- + +.globl C(D_DrawNonSubdiv) +C(D_DrawNonSubdiv): + pushl %ebp // preserve caller stack frame pointer + movl C(r_affinetridesc)+atd_numtriangles,%ebp + pushl %ebx + shll $(mtri_shift),%ebp + pushl %esi // preserve register variables + movl C(r_affinetridesc)+atd_ptriangles,%esi + pushl %edi + +// mtriangle_t *ptri; +// finalvert_t *pfv, *index0, *index1, *index2; +// int i; +// int lnumtriangles; + +// pfv = r_affinetridesc.pfinalverts; +// ptri = r_affinetridesc.ptriangles; +// lnumtriangles = r_affinetridesc.numtriangles; + +LNDLoop: + +// for (i=0 ; ivertindex[0]; +// index1 = pfv + ptri->vertindex[1]; +// index2 = pfv + ptri->vertindex[2]; + movl C(r_affinetridesc)+atd_pfinalverts,%edi + movl mtri_vertindex+0-mtri_size(%esi,%ebp,1),%ecx + shll $(fv_shift),%ecx + movl mtri_vertindex+4-mtri_size(%esi,%ebp,1),%edx + shll $(fv_shift),%edx + movl mtri_vertindex+8-mtri_size(%esi,%ebp,1),%ebx + shll $(fv_shift),%ebx + addl %edi,%ecx + addl %edi,%edx + addl %edi,%ebx + +// d_xdenom = (index0->v[1]-index1->v[1]) * +// (index0->v[0]-index2->v[0]) - +// (index0->v[0]-index1->v[0])*(index0->v[1]-index2->v[1]); + movl fv_v+4(%ecx),%eax + movl fv_v+0(%ecx),%esi + subl fv_v+4(%edx),%eax + subl fv_v+0(%ebx),%esi + imull %esi,%eax + movl fv_v+0(%ecx),%esi + movl fv_v+4(%ecx),%edi + subl fv_v+0(%edx),%esi + subl fv_v+4(%ebx),%edi + imull %esi,%edi + subl %edi,%eax + +// if (d_xdenom >= 0) +// { +// continue; + jns LNextTri + +// } + + movl %eax,C(d_xdenom) + fildl C(d_xdenom) + +// r_p0[0] = index0->v[0]; // u +// r_p0[1] = index0->v[1]; // v +// r_p0[2] = index0->v[2]; // s +// r_p0[3] = index0->v[3]; // t +// r_p0[4] = index0->v[4]; // light +// r_p0[5] = index0->v[5]; // iz + movl fv_v+0(%ecx),%eax + movl fv_v+4(%ecx),%esi + movl %eax,C(r_p0)+0 + movl %esi,C(r_p0)+4 + movl fv_v+8(%ecx),%eax + movl fv_v+12(%ecx),%esi + movl %eax,C(r_p0)+8 + movl %esi,C(r_p0)+12 + movl fv_v+16(%ecx),%eax + movl fv_v+20(%ecx),%esi + movl %eax,C(r_p0)+16 + movl %esi,C(r_p0)+20 + + fdivrs float_1 + +// r_p1[0] = index1->v[0]; +// r_p1[1] = index1->v[1]; +// r_p1[2] = index1->v[2]; +// r_p1[3] = index1->v[3]; +// r_p1[4] = index1->v[4]; +// r_p1[5] = index1->v[5]; + movl fv_v+0(%edx),%eax + movl fv_v+4(%edx),%esi + movl %eax,C(r_p1)+0 + movl %esi,C(r_p1)+4 + movl fv_v+8(%edx),%eax + movl fv_v+12(%edx),%esi + movl %eax,C(r_p1)+8 + movl %esi,C(r_p1)+12 + movl fv_v+16(%edx),%eax + movl fv_v+20(%edx),%esi + movl %eax,C(r_p1)+16 + movl %esi,C(r_p1)+20 + +// r_p2[0] = index2->v[0]; +// r_p2[1] = index2->v[1]; +// r_p2[2] = index2->v[2]; +// r_p2[3] = index2->v[3]; +// r_p2[4] = index2->v[4]; +// r_p2[5] = index2->v[5]; + movl fv_v+0(%ebx),%eax + movl fv_v+4(%ebx),%esi + movl %eax,C(r_p2)+0 + movl %esi,C(r_p2)+4 + movl fv_v+8(%ebx),%eax + movl fv_v+12(%ebx),%esi + movl %eax,C(r_p2)+8 + movl %esi,C(r_p2)+12 + movl fv_v+16(%ebx),%eax + movl fv_v+20(%ebx),%esi + movl %eax,C(r_p2)+16 + movl C(r_affinetridesc)+atd_ptriangles,%edi + movl %esi,C(r_p2)+20 + movl mtri_facesfront-mtri_size(%edi,%ebp,1),%eax + +// if (!ptri->facesfront) +// { + testl %eax,%eax + jnz LFacesFront + +// if (index0->flags & ALIAS_ONSEAM) +// r_p0[2] += r_affinetridesc.seamfixupX16; + movl fv_flags(%ecx),%eax + movl fv_flags(%edx),%esi + movl fv_flags(%ebx),%edi + testl $(ALIAS_ONSEAM),%eax + movl C(r_affinetridesc)+atd_seamfixupX16,%eax + jz LOnseamDone0 + addl %eax,C(r_p0)+8 +LOnseamDone0: + +// if (index1->flags & ALIAS_ONSEAM) +// r_p1[2] += r_affinetridesc.seamfixupX16; + testl $(ALIAS_ONSEAM),%esi + jz LOnseamDone1 + addl %eax,C(r_p1)+8 +LOnseamDone1: + +// if (index2->flags & ALIAS_ONSEAM) +// r_p2[2] += r_affinetridesc.seamfixupX16; + testl $(ALIAS_ONSEAM),%edi + jz LOnseamDone2 + addl %eax,C(r_p2)+8 +LOnseamDone2: + +// } + +LFacesFront: + + fstps C(d_xdenom) + +// D_PolysetSetEdgeTable (); +// D_RasterizeAliasPolySmooth (); + call C(D_PolysetSetEdgeTable) + call C(D_RasterizeAliasPolySmooth) + +LNextTri: + movl C(r_affinetridesc)+atd_ptriangles,%esi + subl $16,%ebp + jnz LNDLoop +// } + + popl %edi + popl %esi + popl %ebx + popl %ebp + + addl $(SPAN_SIZE),%esp + + ret + + +#endif // id386 + diff --git a/source/d_polyse.c b/source/d_polyse.c index 5ed3ce93..19239685 100644 --- a/source/d_polyse.c +++ b/source/d_polyse.c @@ -1,1064 +1,1064 @@ -/* -Copyright (C) 1996-1997 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 "quakedef.h" -#include "r_local.h" -#include "d_local.h" - -// TODO: put in span spilling to shrink list size -// !!! if this is changed, it must be changed in d_polysa.s too !!! -#define DPS_MAXSPANS MAXHEIGHT+1 - // 1 extra for spanpackage that marks end - -// !!! if this is changed, it must be changed in asm_draw.h too !!! -typedef struct { - void *pdest; - short *pz; - int count; - byte *ptex; - int sfrac, tfrac, light, zi; -} spanpackage_t; - -typedef struct { - int isflattop; - int numleftedges; - int *pleftedgevert0; - int *pleftedgevert1; - int *pleftedgevert2; - int numrightedges; - int *prightedgevert0; - int *prightedgevert1; - int *prightedgevert2; -} edgetable; - -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; - -spanpackage_t *a_spans; -spanpackage_t *d_pedgespanpackage; -static int ystart; -byte *d_pdest, *d_ptex; -short *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_PolysetDrawSpans8 (spanpackage_t *pspanpackage); -void D_PolysetCalcGradients (int skinwidth); -void D_DrawSubdiv (void); -void D_DrawNonSubdiv (void); -void D_PolysetRecursiveTriangle (int *p1, int *p2, int *p3); -void D_PolysetSetEdgeTable (void); -void D_RasterizeAliasPolySmooth (void); -void D_PolysetScanLeftEdge (int height); - -#if !id386 - -/* -================ -D_PolysetDraw -================ -*/ -void D_PolysetDraw (void) -{ - spanpackage_t spans[DPS_MAXSPANS + 1 + - ((CACHE_SIZE - 1) / sizeof(spanpackage_t)) + 1]; - // one extra because of cache line pretouching - - a_spans = (spanpackage_t *) - (((long)&spans[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1)); - - if (r_affinetridesc.drawtype) - { - D_DrawSubdiv (); - } - else - { - D_DrawNonSubdiv (); - } -} - - -/* -================ -D_PolysetDrawFinalVerts -================ -*/ -void D_PolysetDrawFinalVerts (finalvert_t *fv, int numverts) -{ - int i, z; - short *zbuf; - - for (i=0 ; iv[0] < r_refdef.vrectright) && - (fv->v[1] < r_refdef.vrectbottom)) - { - z = fv->v[5]>>16; - zbuf = zspantable[fv->v[1]] + fv->v[0]; - if (z >= *zbuf) - { - int pix; - - *zbuf = z; - pix = skintable[fv->v[3]>>16][fv->v[2]>>16]; - pix = ((byte *)acolormap)[pix + (fv->v[4] & 0xFF00) ]; - d_viewbuffer[d_scantable[fv->v[1]] + fv->v[0]] = pix; - } - } - } -} - - -/* -================ -D_DrawSubdiv -================ -*/ -void D_DrawSubdiv (void) -{ - mtriangle_t *ptri; - finalvert_t *pfv, *index0, *index1, *index2; - int i; - int lnumtriangles; - - pfv = r_affinetridesc.pfinalverts; - ptri = r_affinetridesc.ptriangles; - lnumtriangles = r_affinetridesc.numtriangles; - - for (i=0 ; iv[1]-index1->v[1]) * - (index0->v[0]-index2->v[0]) - - (index0->v[0]-index1->v[0]) * - (index0->v[1]-index2->v[1])) >= 0) - { - continue; - } - - d_pcolormap = &((byte *)acolormap)[index0->v[4] & 0xFF00]; - - if (ptri[i].facesfront) - { - D_PolysetRecursiveTriangle(index0->v, index1->v, index2->v); - } - else - { - int s0, s1, s2; - - s0 = index0->v[2]; - s1 = index1->v[2]; - s2 = index2->v[2]; - - if (index0->flags & ALIAS_ONSEAM) - index0->v[2] += r_affinetridesc.seamfixupX16; - if (index1->flags & ALIAS_ONSEAM) - index1->v[2] += r_affinetridesc.seamfixupX16; - if (index2->flags & ALIAS_ONSEAM) - index2->v[2] += r_affinetridesc.seamfixupX16; - - D_PolysetRecursiveTriangle(index0->v, index1->v, index2->v); - - index0->v[2] = s0; - index1->v[2] = s1; - index2->v[2] = s2; - } - } -} - - -/* -================ -D_DrawNonSubdiv -================ -*/ -void D_DrawNonSubdiv (void) -{ - mtriangle_t *ptri; - finalvert_t *pfv, *index0, *index1, *index2; - int i; - int lnumtriangles; - - pfv = r_affinetridesc.pfinalverts; - ptri = r_affinetridesc.ptriangles; - lnumtriangles = r_affinetridesc.numtriangles; - - for (i=0 ; ivertindex[0]; - index1 = pfv + ptri->vertindex[1]; - index2 = pfv + ptri->vertindex[2]; - - d_xdenom = (index0->v[1]-index1->v[1]) * - (index0->v[0]-index2->v[0]) - - (index0->v[0]-index1->v[0])*(index0->v[1]-index2->v[1]); - - if (d_xdenom >= 0) - { - continue; - } - - r_p0[0] = index0->v[0]; // u - r_p0[1] = index0->v[1]; // v - r_p0[2] = index0->v[2]; // s - r_p0[3] = index0->v[3]; // t - r_p0[4] = index0->v[4]; // light - r_p0[5] = index0->v[5]; // iz - - r_p1[0] = index1->v[0]; - r_p1[1] = index1->v[1]; - r_p1[2] = index1->v[2]; - r_p1[3] = index1->v[3]; - r_p1[4] = index1->v[4]; - r_p1[5] = index1->v[5]; - - r_p2[0] = index2->v[0]; - r_p2[1] = index2->v[1]; - r_p2[2] = index2->v[2]; - r_p2[3] = index2->v[3]; - r_p2[4] = index2->v[4]; - r_p2[5] = index2->v[5]; - - if (!ptri->facesfront) - { - if (index0->flags & ALIAS_ONSEAM) - r_p0[2] += r_affinetridesc.seamfixupX16; - if (index1->flags & ALIAS_ONSEAM) - r_p1[2] += r_affinetridesc.seamfixupX16; - if (index2->flags & ALIAS_ONSEAM) - r_p2[2] += r_affinetridesc.seamfixupX16; - } - - D_PolysetSetEdgeTable (); - D_RasterizeAliasPolySmooth (); - } -} - - -/* -================ -D_PolysetRecursiveTriangle -================ -*/ -void D_PolysetRecursiveTriangle (int *lp1, int *lp2, int *lp3) -{ - int *temp; - int d; - int new[6]; - int z; - short *zbuf; - - d = lp2[0] - lp1[0]; - if (d < -1 || d > 1) - goto split; - d = lp2[1] - lp1[1]; - if (d < -1 || d > 1) - goto split; - - d = lp3[0] - lp2[0]; - if (d < -1 || d > 1) - goto split2; - d = lp3[1] - lp2[1]; - if (d < -1 || d > 1) - goto split2; - - d = lp1[0] - lp3[0]; - if (d < -1 || d > 1) - goto split3; - d = lp1[1] - lp3[1]; - if (d < -1 || d > 1) - { -split3: - temp = lp1; - lp1 = lp3; - lp3 = lp2; - lp2 = temp; - - goto split; - } - - return; // entire tri is filled - -split2: - temp = lp1; - lp1 = lp2; - lp2 = lp3; - lp3 = temp; - -split: -// split this edge - new[0] = (lp1[0] + lp2[0]) >> 1; - new[1] = (lp1[1] + lp2[1]) >> 1; - new[2] = (lp1[2] + lp2[2]) >> 1; - new[3] = (lp1[3] + lp2[3]) >> 1; - new[5] = (lp1[5] + lp2[5]) >> 1; - -// draw the point if splitting a leading edge - if (lp2[1] > lp1[1]) - goto nodraw; - if ((lp2[1] == lp1[1]) && (lp2[0] < lp1[0])) - goto nodraw; - - - z = new[5]>>16; - zbuf = zspantable[new[1]] + new[0]; - if (z >= *zbuf) - { - int pix; - - *zbuf = z; - pix = d_pcolormap[skintable[new[3]>>16][new[2]>>16]]; - d_viewbuffer[d_scantable[new[1]] + new[0]] = pix; - } - -nodraw: -// recursively continue - D_PolysetRecursiveTriangle (lp3, lp1, new); - D_PolysetRecursiveTriangle (lp3, new, lp2); -} - -#endif // !id386 - - -/* -================ -D_PolysetUpdateTables -================ -*/ -void D_PolysetUpdateTables (void) -{ - int i; - byte *s; - - if (r_affinetridesc.skinwidth != skinwidth || - r_affinetridesc.pskin != skinstart) - { - skinwidth = r_affinetridesc.skinwidth; - skinstart = r_affinetridesc.pskin; - s = skinstart; - for (i=0 ; ipdest = 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); -} - -#endif // !id386 - - -/* -=================== -D_PolysetSetUpForLineScan -==================== -*/ -void D_PolysetSetUpForLineScan(fixed8_t startvertu, fixed8_t startvertv, - fixed8_t endvertu, fixed8_t endvertv) -{ - double dm, dn; - 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 - { - dm = (double)tm; - dn = (double)tn; - - FloorDivMod (dm, dn, &ubasestep, &erroradjustup); - - erroradjustdown = dn; - } -} - - -#if !id386 - -/* -================ -D_PolysetCalcGradients -================ -*/ -void D_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); - -#if id386 - a_sstepxfrac = r_sstepx << 16; - a_tstepxfrac = r_tstepx << 16; -#else - a_sstepxfrac = r_sstepx & 0xFFFF; - a_tstepxfrac = r_tstepx & 0xFFFF; -#endif - - a_ststepxwhole = skinwidth * (r_tstepx >> 16) + (r_sstepx >> 16); -} - -#endif // !id386 - - -byte gelmap[256]; -void InitGel (byte *palette) -{ - int i; - int r; - - for (i=0 ; i<256 ; i++) - { -// r = (palette[i*3]>>4); - r = (palette[i*3] + palette[i*3+1] + palette[i*3+2])/(16*3); - gelmap[i] = /* 64 */ 0 + r; - } -} - - -#if !id386 - -/* -================ -D_PolysetDrawSpans8 -================ -*/ -void D_PolysetDrawSpans8 (spanpackage_t *pspanpackage) -{ - int lcount; - byte *lpdest; - byte *lptex; - int lsfrac, ltfrac; - int llight; - int lzi; - short *lpz; - - do - { - 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) - { - *lpdest = ((byte *)acolormap)[*lptex + (llight & 0xFF00)]; -// gel mapping *lpdest = gelmap[*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); -} -#endif // !id386 - - -/* -================ -D_PolysetFillSpans8 -================ -*/ -void D_PolysetFillSpans8 (spanpackage_t *pspanpackage) -{ - int color; - -// FIXME: do z buffering - - color = d_aflatcolor++; - - while (1) - { - int lcount; - byte *lpdest; - - lcount = pspanpackage->count; - - if (lcount == -1) - return; - - if (lcount) - { - lpdest = pspanpackage->pdest; - - do - { - *lpdest++ = color; - } while (--lcount); - } - - pspanpackage++; - } -} - -/* -================ -D_RasterizeAliasPolySmooth -================ -*/ -void D_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 -// - D_PolysetCalcGradients (r_affinetridesc.skinwidth); - -// -// rasterize the polygon -// - -// -// scan out the top (and possibly only) part of the left edge -// - D_PolysetSetUpForLineScan(plefttop[0], plefttop[1], - pleftbottom[0], pleftbottom[1]); - - 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; -#if id386 - d_sfrac = (plefttop[2] & 0xFFFF) << 16; - d_tfrac = (plefttop[3] & 0xFFFF) << 16; - d_pzbasestep = (d_zwidth + ubasestep) << 1; - d_pzextrastep = d_pzbasestep + 2; -#else - d_sfrac = plefttop[2] & 0xFFFF; - d_tfrac = plefttop[3] & 0xFFFF; - d_pzbasestep = d_zwidth + ubasestep; - d_pzextrastep = d_pzbasestep + 1; -#endif - d_light = plefttop[4]; - d_zi = plefttop[5]; - - d_pdestbasestep = screenwidth + ubasestep; - d_pdestextrastep = d_pdestbasestep + 1; - d_pdest = (byte *)d_viewbuffer + - ystart * screenwidth + plefttop[0]; - d_pz = d_pzbuffer + ystart * d_zwidth + plefttop[0]; - -// 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; -#if id386 - d_sfracbasestep = (r_sstepy + r_sstepx * ubasestep) << 16; - d_tfracbasestep = (r_tstepy + r_tstepx * ubasestep) << 16; -#else - d_sfracbasestep = (r_sstepy + r_sstepx * ubasestep) & 0xFFFF; - d_tfracbasestep = (r_tstepy + r_tstepx * ubasestep) & 0xFFFF; -#endif - 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; -#if id386 - d_sfracextrastep = (r_sstepy + r_sstepx*d_countextrastep) << 16; - d_tfracextrastep = (r_tstepy + r_tstepx*d_countextrastep) << 16; -#else - d_sfracextrastep = (r_sstepy + r_sstepx*d_countextrastep) & 0xFFFF; - d_tfracextrastep = (r_tstepy + r_tstepx*d_countextrastep) & 0xFFFF; -#endif - d_lightextrastep = d_lightbasestep + working_lstepx; - d_ziextrastep = d_zibasestep + r_zistepx; - - D_PolysetScanLeftEdge (initialleftheight); - -// -// scan out the bottom part of the left edge, if it exists -// - if (pedgetable->numleftedges == 2) - { - int height; - - plefttop = pleftbottom; - pleftbottom = pedgetable->pleftedgevert2; - - D_PolysetSetUpForLineScan(plefttop[0], plefttop[1], - pleftbottom[0], pleftbottom[1]); - - 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_pdestbasestep = screenwidth + ubasestep; - d_pdestextrastep = d_pdestbasestep + 1; - d_pdest = (byte *)d_viewbuffer + ystart * screenwidth + plefttop[0]; -#if id386 - d_pzbasestep = (d_zwidth + ubasestep) << 1; - d_pzextrastep = d_pzbasestep + 2; -#else - d_pzbasestep = d_zwidth + ubasestep; - d_pzextrastep = d_pzbasestep + 1; -#endif - d_pz = d_pzbuffer + ystart * d_zwidth + plefttop[0]; - - 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; -#if id386 - d_sfracbasestep = (r_sstepy + r_sstepx * ubasestep) << 16; - d_tfracbasestep = (r_tstepy + r_tstepx * ubasestep) << 16; -#else - d_sfracbasestep = (r_sstepy + r_sstepx * ubasestep) & 0xFFFF; - d_tfracbasestep = (r_tstepy + r_tstepx * ubasestep) & 0xFFFF; -#endif - 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; -#if id386 - d_sfracextrastep = ((r_sstepy+r_sstepx*d_countextrastep) & 0xFFFF)<<16; - d_tfracextrastep = ((r_tstepy+r_tstepx*d_countextrastep) & 0xFFFF)<<16; -#else - d_sfracextrastep = (r_sstepy+r_sstepx*d_countextrastep) & 0xFFFF; - d_tfracextrastep = (r_tstepy+r_tstepx*d_countextrastep) & 0xFFFF; -#endif - d_lightextrastep = d_lightbasestep + working_lstepx; - d_ziextrastep = d_zibasestep + r_zistepx; - - D_PolysetScanLeftEdge (height); - } - -// scan out the top (and possibly only) part of the right edge, updating the -// count field - d_pedgespanpackage = a_spans; - - D_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_PolysetDrawSpans8 (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]; - - D_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_PolysetDrawSpans8 (pstart); - } -} - - -/* -================ -D_PolysetSetEdgeTable -================ -*/ -void D_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]; -} - - -#if 0 - -void D_PolysetRecursiveDrawLine (int *lp1, int *lp2) -{ - int d; - int new[6]; - int ofs; - - d = lp2[0] - lp1[0]; - if (d < -1 || d > 1) - goto split; - d = lp2[1] - lp1[1]; - if (d < -1 || d > 1) - goto split; - - return; // line is completed - -split: -// split this edge - new[0] = (lp1[0] + lp2[0]) >> 1; - new[1] = (lp1[1] + lp2[1]) >> 1; - new[5] = (lp1[5] + lp2[5]) >> 1; - new[2] = (lp1[2] + lp2[2]) >> 1; - new[3] = (lp1[3] + lp2[3]) >> 1; - new[4] = (lp1[4] + lp2[4]) >> 1; - -// draw the point - ofs = d_scantable[new[1]] + new[0]; - if (new[5] > d_pzbuffer[ofs]) - { - int pix; - - d_pzbuffer[ofs] = new[5]; - pix = skintable[new[3]>>16][new[2]>>16]; -// pix = ((byte *)acolormap)[pix + (new[4] & 0xFF00)]; - d_viewbuffer[ofs] = pix; - } - -// recursively continue - D_PolysetRecursiveDrawLine (lp1, new); - D_PolysetRecursiveDrawLine (new, lp2); -} - -void D_PolysetRecursiveTriangle2 (int *lp1, int *lp2, int *lp3) -{ - int d; - int new[4]; - - d = lp2[0] - lp1[0]; - if (d < -1 || d > 1) - goto split; - d = lp2[1] - lp1[1]; - if (d < -1 || d > 1) - goto split; - return; - -split: -// split this edge - new[0] = (lp1[0] + lp2[0]) >> 1; - new[1] = (lp1[1] + lp2[1]) >> 1; - new[5] = (lp1[5] + lp2[5]) >> 1; - new[2] = (lp1[2] + lp2[2]) >> 1; - new[3] = (lp1[3] + lp2[3]) >> 1; - new[4] = (lp1[4] + lp2[4]) >> 1; - - D_PolysetRecursiveDrawLine (new, lp3); - -// recursively continue - D_PolysetRecursiveTriangle (lp1, new, lp3); - D_PolysetRecursiveTriangle (new, lp2, lp3); -} - -#endif - +/* +Copyright (C) 1996-1997 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 "quakedef.h" +#include "r_local.h" +#include "d_local.h" + +// TODO: put in span spilling to shrink list size +// !!! if this is changed, it must be changed in d_polysa.s too !!! +#define DPS_MAXSPANS MAXHEIGHT+1 + // 1 extra for spanpackage that marks end + +// !!! if this is changed, it must be changed in asm_draw.h too !!! +typedef struct { + void *pdest; + short *pz; + int count; + byte *ptex; + int sfrac, tfrac, light, zi; +} spanpackage_t; + +typedef struct { + int isflattop; + int numleftedges; + int *pleftedgevert0; + int *pleftedgevert1; + int *pleftedgevert2; + int numrightedges; + int *prightedgevert0; + int *prightedgevert1; + int *prightedgevert2; +} edgetable; + +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; + +spanpackage_t *a_spans; +spanpackage_t *d_pedgespanpackage; +static int ystart; +byte *d_pdest, *d_ptex; +short *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_PolysetDrawSpans8 (spanpackage_t *pspanpackage); +void D_PolysetCalcGradients (int skinwidth); +void D_DrawSubdiv (void); +void D_DrawNonSubdiv (void); +void D_PolysetRecursiveTriangle (int *p1, int *p2, int *p3); +void D_PolysetSetEdgeTable (void); +void D_RasterizeAliasPolySmooth (void); +void D_PolysetScanLeftEdge (int height); + +#if !id386 + +/* +================ +D_PolysetDraw +================ +*/ +void D_PolysetDraw (void) +{ + spanpackage_t spans[DPS_MAXSPANS + 1 + + ((CACHE_SIZE - 1) / sizeof(spanpackage_t)) + 1]; + // one extra because of cache line pretouching + + a_spans = (spanpackage_t *) + (((long)&spans[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1)); + + if (r_affinetridesc.drawtype) + { + D_DrawSubdiv (); + } + else + { + D_DrawNonSubdiv (); + } +} + + +/* +================ +D_PolysetDrawFinalVerts +================ +*/ +void D_PolysetDrawFinalVerts (finalvert_t *fv, int numverts) +{ + int i, z; + short *zbuf; + + for (i=0 ; iv[0] < r_refdef.vrectright) && + (fv->v[1] < r_refdef.vrectbottom)) + { + z = fv->v[5]>>16; + zbuf = zspantable[fv->v[1]] + fv->v[0]; + if (z >= *zbuf) + { + int pix; + + *zbuf = z; + pix = skintable[fv->v[3]>>16][fv->v[2]>>16]; + pix = ((byte *)acolormap)[pix + (fv->v[4] & 0xFF00) ]; + d_viewbuffer[d_scantable[fv->v[1]] + fv->v[0]] = pix; + } + } + } +} + + +/* +================ +D_DrawSubdiv +================ +*/ +void D_DrawSubdiv (void) +{ + mtriangle_t *ptri; + finalvert_t *pfv, *index0, *index1, *index2; + int i; + int lnumtriangles; + + pfv = r_affinetridesc.pfinalverts; + ptri = r_affinetridesc.ptriangles; + lnumtriangles = r_affinetridesc.numtriangles; + + for (i=0 ; iv[1]-index1->v[1]) * + (index0->v[0]-index2->v[0]) - + (index0->v[0]-index1->v[0]) * + (index0->v[1]-index2->v[1])) >= 0) + { + continue; + } + + d_pcolormap = &((byte *)acolormap)[index0->v[4] & 0xFF00]; + + if (ptri[i].facesfront) + { + D_PolysetRecursiveTriangle(index0->v, index1->v, index2->v); + } + else + { + int s0, s1, s2; + + s0 = index0->v[2]; + s1 = index1->v[2]; + s2 = index2->v[2]; + + if (index0->flags & ALIAS_ONSEAM) + index0->v[2] += r_affinetridesc.seamfixupX16; + if (index1->flags & ALIAS_ONSEAM) + index1->v[2] += r_affinetridesc.seamfixupX16; + if (index2->flags & ALIAS_ONSEAM) + index2->v[2] += r_affinetridesc.seamfixupX16; + + D_PolysetRecursiveTriangle(index0->v, index1->v, index2->v); + + index0->v[2] = s0; + index1->v[2] = s1; + index2->v[2] = s2; + } + } +} + + +/* +================ +D_DrawNonSubdiv +================ +*/ +void D_DrawNonSubdiv (void) +{ + mtriangle_t *ptri; + finalvert_t *pfv, *index0, *index1, *index2; + int i; + int lnumtriangles; + + pfv = r_affinetridesc.pfinalverts; + ptri = r_affinetridesc.ptriangles; + lnumtriangles = r_affinetridesc.numtriangles; + + for (i=0 ; ivertindex[0]; + index1 = pfv + ptri->vertindex[1]; + index2 = pfv + ptri->vertindex[2]; + + d_xdenom = (index0->v[1]-index1->v[1]) * + (index0->v[0]-index2->v[0]) - + (index0->v[0]-index1->v[0])*(index0->v[1]-index2->v[1]); + + if (d_xdenom >= 0) + { + continue; + } + + r_p0[0] = index0->v[0]; // u + r_p0[1] = index0->v[1]; // v + r_p0[2] = index0->v[2]; // s + r_p0[3] = index0->v[3]; // t + r_p0[4] = index0->v[4]; // light + r_p0[5] = index0->v[5]; // iz + + r_p1[0] = index1->v[0]; + r_p1[1] = index1->v[1]; + r_p1[2] = index1->v[2]; + r_p1[3] = index1->v[3]; + r_p1[4] = index1->v[4]; + r_p1[5] = index1->v[5]; + + r_p2[0] = index2->v[0]; + r_p2[1] = index2->v[1]; + r_p2[2] = index2->v[2]; + r_p2[3] = index2->v[3]; + r_p2[4] = index2->v[4]; + r_p2[5] = index2->v[5]; + + if (!ptri->facesfront) + { + if (index0->flags & ALIAS_ONSEAM) + r_p0[2] += r_affinetridesc.seamfixupX16; + if (index1->flags & ALIAS_ONSEAM) + r_p1[2] += r_affinetridesc.seamfixupX16; + if (index2->flags & ALIAS_ONSEAM) + r_p2[2] += r_affinetridesc.seamfixupX16; + } + + D_PolysetSetEdgeTable (); + D_RasterizeAliasPolySmooth (); + } +} + + +/* +================ +D_PolysetRecursiveTriangle +================ +*/ +void D_PolysetRecursiveTriangle (int *lp1, int *lp2, int *lp3) +{ + int *temp; + int d; + int new[6]; + int z; + short *zbuf; + + d = lp2[0] - lp1[0]; + if (d < -1 || d > 1) + goto split; + d = lp2[1] - lp1[1]; + if (d < -1 || d > 1) + goto split; + + d = lp3[0] - lp2[0]; + if (d < -1 || d > 1) + goto split2; + d = lp3[1] - lp2[1]; + if (d < -1 || d > 1) + goto split2; + + d = lp1[0] - lp3[0]; + if (d < -1 || d > 1) + goto split3; + d = lp1[1] - lp3[1]; + if (d < -1 || d > 1) + { +split3: + temp = lp1; + lp1 = lp3; + lp3 = lp2; + lp2 = temp; + + goto split; + } + + return; // entire tri is filled + +split2: + temp = lp1; + lp1 = lp2; + lp2 = lp3; + lp3 = temp; + +split: +// split this edge + new[0] = (lp1[0] + lp2[0]) >> 1; + new[1] = (lp1[1] + lp2[1]) >> 1; + new[2] = (lp1[2] + lp2[2]) >> 1; + new[3] = (lp1[3] + lp2[3]) >> 1; + new[5] = (lp1[5] + lp2[5]) >> 1; + +// draw the point if splitting a leading edge + if (lp2[1] > lp1[1]) + goto nodraw; + if ((lp2[1] == lp1[1]) && (lp2[0] < lp1[0])) + goto nodraw; + + + z = new[5]>>16; + zbuf = zspantable[new[1]] + new[0]; + if (z >= *zbuf) + { + int pix; + + *zbuf = z; + pix = d_pcolormap[skintable[new[3]>>16][new[2]>>16]]; + d_viewbuffer[d_scantable[new[1]] + new[0]] = pix; + } + +nodraw: +// recursively continue + D_PolysetRecursiveTriangle (lp3, lp1, new); + D_PolysetRecursiveTriangle (lp3, new, lp2); +} + +#endif // !id386 + + +/* +================ +D_PolysetUpdateTables +================ +*/ +void D_PolysetUpdateTables (void) +{ + int i; + byte *s; + + if (r_affinetridesc.skinwidth != skinwidth || + r_affinetridesc.pskin != skinstart) + { + skinwidth = r_affinetridesc.skinwidth; + skinstart = r_affinetridesc.pskin; + s = skinstart; + for (i=0 ; ipdest = 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); +} + +#endif // !id386 + + +/* +=================== +D_PolysetSetUpForLineScan +==================== +*/ +void D_PolysetSetUpForLineScan(fixed8_t startvertu, fixed8_t startvertv, + fixed8_t endvertu, fixed8_t endvertv) +{ + double dm, dn; + 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 + { + dm = (double)tm; + dn = (double)tn; + + FloorDivMod (dm, dn, &ubasestep, &erroradjustup); + + erroradjustdown = dn; + } +} + + +#if !id386 + +/* +================ +D_PolysetCalcGradients +================ +*/ +void D_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); + +#if id386 + a_sstepxfrac = r_sstepx << 16; + a_tstepxfrac = r_tstepx << 16; +#else + a_sstepxfrac = r_sstepx & 0xFFFF; + a_tstepxfrac = r_tstepx & 0xFFFF; +#endif + + a_ststepxwhole = skinwidth * (r_tstepx >> 16) + (r_sstepx >> 16); +} + +#endif // !id386 + + +byte gelmap[256]; +void InitGel (byte *palette) +{ + int i; + int r; + + for (i=0 ; i<256 ; i++) + { +// r = (palette[i*3]>>4); + r = (palette[i*3] + palette[i*3+1] + palette[i*3+2])/(16*3); + gelmap[i] = /* 64 */ 0 + r; + } +} + + +#if !id386 + +/* +================ +D_PolysetDrawSpans8 +================ +*/ +void D_PolysetDrawSpans8 (spanpackage_t *pspanpackage) +{ + int lcount; + byte *lpdest; + byte *lptex; + int lsfrac, ltfrac; + int llight; + int lzi; + short *lpz; + + do + { + 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) + { + *lpdest = ((byte *)acolormap)[*lptex + (llight & 0xFF00)]; +// gel mapping *lpdest = gelmap[*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); +} +#endif // !id386 + + +/* +================ +D_PolysetFillSpans8 +================ +*/ +void D_PolysetFillSpans8 (spanpackage_t *pspanpackage) +{ + int color; + +// FIXME: do z buffering + + color = d_aflatcolor++; + + while (1) + { + int lcount; + byte *lpdest; + + lcount = pspanpackage->count; + + if (lcount == -1) + return; + + if (lcount) + { + lpdest = pspanpackage->pdest; + + do + { + *lpdest++ = color; + } while (--lcount); + } + + pspanpackage++; + } +} + +/* +================ +D_RasterizeAliasPolySmooth +================ +*/ +void D_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 +// + D_PolysetCalcGradients (r_affinetridesc.skinwidth); + +// +// rasterize the polygon +// + +// +// scan out the top (and possibly only) part of the left edge +// + D_PolysetSetUpForLineScan(plefttop[0], plefttop[1], + pleftbottom[0], pleftbottom[1]); + + 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; +#if id386 + d_sfrac = (plefttop[2] & 0xFFFF) << 16; + d_tfrac = (plefttop[3] & 0xFFFF) << 16; + d_pzbasestep = (d_zwidth + ubasestep) << 1; + d_pzextrastep = d_pzbasestep + 2; +#else + d_sfrac = plefttop[2] & 0xFFFF; + d_tfrac = plefttop[3] & 0xFFFF; + d_pzbasestep = d_zwidth + ubasestep; + d_pzextrastep = d_pzbasestep + 1; +#endif + d_light = plefttop[4]; + d_zi = plefttop[5]; + + d_pdestbasestep = screenwidth + ubasestep; + d_pdestextrastep = d_pdestbasestep + 1; + d_pdest = (byte *)d_viewbuffer + + ystart * screenwidth + plefttop[0]; + d_pz = d_pzbuffer + ystart * d_zwidth + plefttop[0]; + +// 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; +#if id386 + d_sfracbasestep = (r_sstepy + r_sstepx * ubasestep) << 16; + d_tfracbasestep = (r_tstepy + r_tstepx * ubasestep) << 16; +#else + d_sfracbasestep = (r_sstepy + r_sstepx * ubasestep) & 0xFFFF; + d_tfracbasestep = (r_tstepy + r_tstepx * ubasestep) & 0xFFFF; +#endif + 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; +#if id386 + d_sfracextrastep = (r_sstepy + r_sstepx*d_countextrastep) << 16; + d_tfracextrastep = (r_tstepy + r_tstepx*d_countextrastep) << 16; +#else + d_sfracextrastep = (r_sstepy + r_sstepx*d_countextrastep) & 0xFFFF; + d_tfracextrastep = (r_tstepy + r_tstepx*d_countextrastep) & 0xFFFF; +#endif + d_lightextrastep = d_lightbasestep + working_lstepx; + d_ziextrastep = d_zibasestep + r_zistepx; + + D_PolysetScanLeftEdge (initialleftheight); + +// +// scan out the bottom part of the left edge, if it exists +// + if (pedgetable->numleftedges == 2) + { + int height; + + plefttop = pleftbottom; + pleftbottom = pedgetable->pleftedgevert2; + + D_PolysetSetUpForLineScan(plefttop[0], plefttop[1], + pleftbottom[0], pleftbottom[1]); + + 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_pdestbasestep = screenwidth + ubasestep; + d_pdestextrastep = d_pdestbasestep + 1; + d_pdest = (byte *)d_viewbuffer + ystart * screenwidth + plefttop[0]; +#if id386 + d_pzbasestep = (d_zwidth + ubasestep) << 1; + d_pzextrastep = d_pzbasestep + 2; +#else + d_pzbasestep = d_zwidth + ubasestep; + d_pzextrastep = d_pzbasestep + 1; +#endif + d_pz = d_pzbuffer + ystart * d_zwidth + plefttop[0]; + + 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; +#if id386 + d_sfracbasestep = (r_sstepy + r_sstepx * ubasestep) << 16; + d_tfracbasestep = (r_tstepy + r_tstepx * ubasestep) << 16; +#else + d_sfracbasestep = (r_sstepy + r_sstepx * ubasestep) & 0xFFFF; + d_tfracbasestep = (r_tstepy + r_tstepx * ubasestep) & 0xFFFF; +#endif + 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; +#if id386 + d_sfracextrastep = ((r_sstepy+r_sstepx*d_countextrastep) & 0xFFFF)<<16; + d_tfracextrastep = ((r_tstepy+r_tstepx*d_countextrastep) & 0xFFFF)<<16; +#else + d_sfracextrastep = (r_sstepy+r_sstepx*d_countextrastep) & 0xFFFF; + d_tfracextrastep = (r_tstepy+r_tstepx*d_countextrastep) & 0xFFFF; +#endif + d_lightextrastep = d_lightbasestep + working_lstepx; + d_ziextrastep = d_zibasestep + r_zistepx; + + D_PolysetScanLeftEdge (height); + } + +// scan out the top (and possibly only) part of the right edge, updating the +// count field + d_pedgespanpackage = a_spans; + + D_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_PolysetDrawSpans8 (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]; + + D_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_PolysetDrawSpans8 (pstart); + } +} + + +/* +================ +D_PolysetSetEdgeTable +================ +*/ +void D_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]; +} + + +#if 0 + +void D_PolysetRecursiveDrawLine (int *lp1, int *lp2) +{ + int d; + int new[6]; + int ofs; + + d = lp2[0] - lp1[0]; + if (d < -1 || d > 1) + goto split; + d = lp2[1] - lp1[1]; + if (d < -1 || d > 1) + goto split; + + return; // line is completed + +split: +// split this edge + new[0] = (lp1[0] + lp2[0]) >> 1; + new[1] = (lp1[1] + lp2[1]) >> 1; + new[5] = (lp1[5] + lp2[5]) >> 1; + new[2] = (lp1[2] + lp2[2]) >> 1; + new[3] = (lp1[3] + lp2[3]) >> 1; + new[4] = (lp1[4] + lp2[4]) >> 1; + +// draw the point + ofs = d_scantable[new[1]] + new[0]; + if (new[5] > d_pzbuffer[ofs]) + { + int pix; + + d_pzbuffer[ofs] = new[5]; + pix = skintable[new[3]>>16][new[2]>>16]; +// pix = ((byte *)acolormap)[pix + (new[4] & 0xFF00)]; + d_viewbuffer[ofs] = pix; + } + +// recursively continue + D_PolysetRecursiveDrawLine (lp1, new); + D_PolysetRecursiveDrawLine (new, lp2); +} + +void D_PolysetRecursiveTriangle2 (int *lp1, int *lp2, int *lp3) +{ + int d; + int new[4]; + + d = lp2[0] - lp1[0]; + if (d < -1 || d > 1) + goto split; + d = lp2[1] - lp1[1]; + if (d < -1 || d > 1) + goto split; + return; + +split: +// split this edge + new[0] = (lp1[0] + lp2[0]) >> 1; + new[1] = (lp1[1] + lp2[1]) >> 1; + new[5] = (lp1[5] + lp2[5]) >> 1; + new[2] = (lp1[2] + lp2[2]) >> 1; + new[3] = (lp1[3] + lp2[3]) >> 1; + new[4] = (lp1[4] + lp2[4]) >> 1; + + D_PolysetRecursiveDrawLine (new, lp3); + +// recursively continue + D_PolysetRecursiveTriangle (lp1, new, lp3); + D_PolysetRecursiveTriangle (new, lp2, lp3); +} + +#endif + diff --git a/source/d_scan.c b/source/d_scan.c index c1690cd2..292bb560 100644 --- a/source/d_scan.c +++ b/source/d_scan.c @@ -1,446 +1,446 @@ -/* -Copyright (C) 1996-1997 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 "quakedef.h" -#include "r_local.h" -#include "d_local.h" - -unsigned char *r_turb_pbase, *r_turb_pdest; -fixed16_t r_turb_s, r_turb_t, r_turb_sstep, r_turb_tstep; -int *r_turb_turb; -int r_turb_spancount; - -void D_DrawTurbulent8Span (void); - - -/* -============= -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; - byte *dest; - int *turb; - int *col; - byte **row; - byte *rowptr[1024]; - int column[1280]; - float wratio, hratio; - - w = r_refdef.vrect.width; - h = r_refdef.vrect.height; - - wratio = w / (float)scr_vrect.width; - hratio = h / (float)scr_vrect.height; - - 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); -} - -#endif // !id386 - -/* -============= -Turbulent8 -============= -*/ -void Turbulent8 (espan_t *pspan) -{ - int count; - fixed16_t snext, tnext; - float sdivz, tdivz, zi, z, du, dv, spancountminus1; - float sdivz16stepu, tdivz16stepu, zi16stepu; - - r_turb_turb = sintable + ((int)(cl.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 - { - r_turb_pdest = (unsigned char *)((byte *)d_viewbuffer + - (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); -} - - -#if !id386 - -/* -============= -D_DrawSpans8 -============= -*/ -void D_DrawSpans8 (espan_t *pspan) -{ - int count, spancount; - unsigned char *pbase, *pdest; - fixed16_t s, t, snext, tnext, sstep, tstep; - float sdivz, tdivz, zi, z, du, dv, 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 - { - pdest = (unsigned char *)((byte *)d_viewbuffer + - (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); -} - -#endif - - -#if !id386 - -/* -============= -D_DrawZSpans -============= -*/ -void D_DrawZSpans (espan_t *pspan) -{ - int count, doublecount, izistep; - int izi; - short *pdest; - unsigned ltemp; - double zi; - float du, dv; - -// 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 - { - 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); - - if ((long)pdest & 0x02) - { - *pdest++ = (short)(izi >> 16); - izi += izistep; - count--; - } - - if ((doublecount = count >> 1) > 0) - { - do - { - ltemp = izi >> 16; - izi += izistep; - ltemp |= izi & 0xFFFF0000; - izi += izistep; - *(int *)pdest = ltemp; - pdest += 2; - } while (--doublecount > 0); - } - - if (count & 1) - *pdest = (short)(izi >> 16); - - } while ((pspan = pspan->pnext) != NULL); -} - -#endif +/* +Copyright (C) 1996-1997 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 "quakedef.h" +#include "r_local.h" +#include "d_local.h" + +unsigned char *r_turb_pbase, *r_turb_pdest; +fixed16_t r_turb_s, r_turb_t, r_turb_sstep, r_turb_tstep; +int *r_turb_turb; +int r_turb_spancount; + +void D_DrawTurbulent8Span (void); + + +/* +============= +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; + byte *dest; + int *turb; + int *col; + byte **row; + byte *rowptr[1024]; + int column[1280]; + float wratio, hratio; + + w = r_refdef.vrect.width; + h = r_refdef.vrect.height; + + wratio = w / (float)scr_vrect.width; + hratio = h / (float)scr_vrect.height; + + 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); +} + +#endif // !id386 + +/* +============= +Turbulent8 +============= +*/ +void Turbulent8 (espan_t *pspan) +{ + int count; + fixed16_t snext, tnext; + float sdivz, tdivz, zi, z, du, dv, spancountminus1; + float sdivz16stepu, tdivz16stepu, zi16stepu; + + r_turb_turb = sintable + ((int)(cl.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 + { + r_turb_pdest = (unsigned char *)((byte *)d_viewbuffer + + (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); +} + + +#if !id386 + +/* +============= +D_DrawSpans8 +============= +*/ +void D_DrawSpans8 (espan_t *pspan) +{ + int count, spancount; + unsigned char *pbase, *pdest; + fixed16_t s, t, snext, tnext, sstep, tstep; + float sdivz, tdivz, zi, z, du, dv, 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 + { + pdest = (unsigned char *)((byte *)d_viewbuffer + + (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); +} + +#endif + + +#if !id386 + +/* +============= +D_DrawZSpans +============= +*/ +void D_DrawZSpans (espan_t *pspan) +{ + int count, doublecount, izistep; + int izi; + short *pdest; + unsigned ltemp; + double zi; + float du, dv; + +// 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 + { + 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); + + if ((long)pdest & 0x02) + { + *pdest++ = (short)(izi >> 16); + izi += izistep; + count--; + } + + if ((doublecount = count >> 1) > 0) + { + do + { + ltemp = izi >> 16; + izi += izistep; + ltemp |= izi & 0xFFFF0000; + izi += izistep; + *(int *)pdest = ltemp; + pdest += 2; + } while (--doublecount > 0); + } + + if (count & 1) + *pdest = (short)(izi >> 16); + + } while ((pspan = pspan->pnext) != NULL); +} + +#endif diff --git a/source/d_scana.s b/source/d_scana.s index a8ba34ef..ccc6e51b 100644 --- a/source/d_scana.s +++ b/source/d_scana.s @@ -1,95 +1,95 @@ -/* - d_scana.S - - (description) - - Copyright (C) 1996-1997 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: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - - $Id: d_scana.s,v 1.1.1.3 2004/10/13 18:54:31 vvd0 Exp $ -*/ -// d_scana.s -// x86 assembly-language turbulent texture mapping code - -#include "asm_i386.h" -#include "quakeasm.h" -#include "asm_draw.h" -#include "d_ifacea.h" - -#ifdef id386 - - .data - - .text - -//---------------------------------------------------------------------- -// turbulent texture mapping code -//---------------------------------------------------------------------- - - .align 4 -.globl C(D_DrawTurbulent8Span) -C(D_DrawTurbulent8Span): - pushl %ebp // preserve caller's stack frame pointer - pushl %esi // preserve register variables - pushl %edi - pushl %ebx - - movl C(r_turb_s),%esi - movl C(r_turb_t),%ecx - movl C(r_turb_pdest),%edi - movl C(r_turb_spancount),%ebx - -Llp: - movl %ecx,%eax - movl %esi,%edx - sarl $16,%eax - movl C(r_turb_turb),%ebp - sarl $16,%edx - andl $(CYCLE-1),%eax - andl $(CYCLE-1),%edx - movl (%ebp,%eax,4),%eax - movl (%ebp,%edx,4),%edx - addl %esi,%eax - sarl $16,%eax - addl %ecx,%edx - sarl $16,%edx - andl $(TURB_TEX_SIZE-1),%eax - andl $(TURB_TEX_SIZE-1),%edx - shll $6,%edx - movl C(r_turb_pbase),%ebp - addl %eax,%edx - incl %edi - addl C(r_turb_sstep),%esi - addl C(r_turb_tstep),%ecx - movb (%ebp,%edx,1),%dl - decl %ebx - movb %dl,-1(%edi) - jnz Llp - - movl %edi,C(r_turb_pdest) - - popl %ebx // restore register variables - popl %edi - popl %esi - popl %ebp // restore caller's stack frame pointer - ret - -#endif // id386 - +/* + d_scana.S + + (description) + + Copyright (C) 1996-1997 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id: d_scana.s,v 1.1.1.4 2004/10/18 17:44:27 vvd0 Exp $ +*/ +// d_scana.s +// x86 assembly-language turbulent texture mapping code + +#include "asm_i386.h" +#include "quakeasm.h" +#include "asm_draw.h" +#include "d_ifacea.h" + +#ifdef id386 + + .data + + .text + +//---------------------------------------------------------------------- +// turbulent texture mapping code +//---------------------------------------------------------------------- + + .align 4 +.globl C(D_DrawTurbulent8Span) +C(D_DrawTurbulent8Span): + pushl %ebp // preserve caller's stack frame pointer + pushl %esi // preserve register variables + pushl %edi + pushl %ebx + + movl C(r_turb_s),%esi + movl C(r_turb_t),%ecx + movl C(r_turb_pdest),%edi + movl C(r_turb_spancount),%ebx + +Llp: + movl %ecx,%eax + movl %esi,%edx + sarl $16,%eax + movl C(r_turb_turb),%ebp + sarl $16,%edx + andl $(CYCLE-1),%eax + andl $(CYCLE-1),%edx + movl (%ebp,%eax,4),%eax + movl (%ebp,%edx,4),%edx + addl %esi,%eax + sarl $16,%eax + addl %ecx,%edx + sarl $16,%edx + andl $(TURB_TEX_SIZE-1),%eax + andl $(TURB_TEX_SIZE-1),%edx + shll $6,%edx + movl C(r_turb_pbase),%ebp + addl %eax,%edx + incl %edi + addl C(r_turb_sstep),%esi + addl C(r_turb_tstep),%ecx + movb (%ebp,%edx,1),%dl + decl %ebx + movb %dl,-1(%edi) + jnz Llp + + movl %edi,C(r_turb_pdest) + + popl %ebx // restore register variables + popl %edi + popl %esi + popl %ebp // restore caller's stack frame pointer + ret + +#endif // id386 + diff --git a/source/d_sky.c b/source/d_sky.c index 9cf93f8c..508826fd 100644 --- a/source/d_sky.c +++ b/source/d_sky.c @@ -1,132 +1,132 @@ -/* -Copyright (C) 1996-1997 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_sky.c - -#include "quakedef.h" -#include "r_local.h" -#include "d_local.h" - -#define SKY_SPAN_SHIFT 5 -#define SKY_SPAN_MAX (1 << SKY_SPAN_SHIFT) - - -/* -================= -D_Sky_uv_To_st -================= -*/ -void D_Sky_uv_To_st (int u, int v, fixed16_t *s, fixed16_t *t) -{ - float wu, wv; - vec3_t end; - - wu = (u - xcenter) / xscale; - wv = (ycenter - v) / yscale; - - end[0] = vpn[0] + wu*vright[0] + wv*vup[0]; - end[1] = vpn[1] + wu*vright[1] + wv*vup[1]; - end[2] = vpn[2] + wu*vright[2] + wv*vup[2]; - end[2] *= 3; - VectorNormalize (end); - - *s = (int)((skyshift + 6*(SKYSIZE/2-1)*end[0]) * 0x10000); - *t = (int)((skyshift + 6*(SKYSIZE/2-1)*end[1]) * 0x10000); -} - - -/* -================= -D_DrawSkyScans8 -================= -*/ -void D_DrawSkyScans8 (espan_t *pspan) -{ - int count, spancount, u, v; - unsigned char *pdest; - fixed16_t s, t, snext, tnext, sstep, tstep; - int spancountminus1; - - sstep = 0; // keep compiler happy - tstep = 0; // ditto - - do - { - pdest = (unsigned char *)((byte *)d_viewbuffer + - (screenwidth * pspan->v) + pspan->u); - - count = pspan->count; - - // calculate the initial s & t - u = pspan->u; - v = pspan->v; - D_Sky_uv_To_st (u, v, &s, &t); - - do - { - if (count >= SKY_SPAN_MAX) - spancount = SKY_SPAN_MAX; - else - spancount = count; - - count -= spancount; - - if (count) - { - u += spancount; - - // calculate s and t at far end of span, - // calculate s and t steps across span by shifting - D_Sky_uv_To_st (u, v, &snext, &tnext); - - sstep = (snext - s) >> SKY_SPAN_SHIFT; - tstep = (tnext - t) >> SKY_SPAN_SHIFT; - } - else - { - // calculate s and t at last pixel in span, - // calculate s and t steps across span by division - spancountminus1 = (float)(spancount - 1); - - if (spancountminus1 > 0) - { - u += spancountminus1; - D_Sky_uv_To_st (u, v, &snext, &tnext); - - sstep = (snext - s) / spancountminus1; - tstep = (tnext - t) / spancountminus1; - } - } - - do - { - *pdest++ = r_skysource[((t & R_SKY_TMASK) >> 8) + - ((s & R_SKY_SMASK) >> 16)]; - s += sstep; - t += tstep; - } while (--spancount > 0); - - s = snext; - t = tnext; - - } while (count > 0); - - } while ((pspan = pspan->pnext) != NULL); -} - +/* +Copyright (C) 1996-1997 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_sky.c + +#include "quakedef.h" +#include "r_local.h" +#include "d_local.h" + +#define SKY_SPAN_SHIFT 5 +#define SKY_SPAN_MAX (1 << SKY_SPAN_SHIFT) + + +/* +================= +D_Sky_uv_To_st +================= +*/ +void D_Sky_uv_To_st (int u, int v, fixed16_t *s, fixed16_t *t) +{ + float wu, wv; + vec3_t end; + + wu = (u - xcenter) / xscale; + wv = (ycenter - v) / yscale; + + end[0] = vpn[0] + wu*vright[0] + wv*vup[0]; + end[1] = vpn[1] + wu*vright[1] + wv*vup[1]; + end[2] = vpn[2] + wu*vright[2] + wv*vup[2]; + end[2] *= 3; + VectorNormalize (end); + + *s = (int)((skyshift + 6*(SKYSIZE/2-1)*end[0]) * 0x10000); + *t = (int)((skyshift + 6*(SKYSIZE/2-1)*end[1]) * 0x10000); +} + + +/* +================= +D_DrawSkyScans8 +================= +*/ +void D_DrawSkyScans8 (espan_t *pspan) +{ + int count, spancount, u, v; + unsigned char *pdest; + fixed16_t s, t, snext, tnext, sstep, tstep; + int spancountminus1; + + sstep = 0; // keep compiler happy + tstep = 0; // ditto + + do + { + pdest = (unsigned char *)((byte *)d_viewbuffer + + (screenwidth * pspan->v) + pspan->u); + + count = pspan->count; + + // calculate the initial s & t + u = pspan->u; + v = pspan->v; + D_Sky_uv_To_st (u, v, &s, &t); + + do + { + if (count >= SKY_SPAN_MAX) + spancount = SKY_SPAN_MAX; + else + spancount = count; + + count -= spancount; + + if (count) + { + u += spancount; + + // calculate s and t at far end of span, + // calculate s and t steps across span by shifting + D_Sky_uv_To_st (u, v, &snext, &tnext); + + sstep = (snext - s) >> SKY_SPAN_SHIFT; + tstep = (tnext - t) >> SKY_SPAN_SHIFT; + } + else + { + // calculate s and t at last pixel in span, + // calculate s and t steps across span by division + spancountminus1 = (float)(spancount - 1); + + if (spancountminus1 > 0) + { + u += spancountminus1; + D_Sky_uv_To_st (u, v, &snext, &tnext); + + sstep = (snext - s) / spancountminus1; + tstep = (tnext - t) / spancountminus1; + } + } + + do + { + *pdest++ = r_skysource[((t & R_SKY_TMASK) >> 8) + + ((s & R_SKY_SMASK) >> 16)]; + s += sstep; + t += tstep; + } while (--spancount > 0); + + s = snext; + t = tnext; + + } while (count > 0); + + } while ((pspan = pspan->pnext) != NULL); +} + diff --git a/source/d_spr8.s b/source/d_spr8.s index f5800013..cab401db 100644 --- a/source/d_spr8.s +++ b/source/d_spr8.s @@ -1,906 +1,906 @@ -/* - d_spr8.S - - (description) - - Copyright (C) 1996-1997 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: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - - $Id: d_spr8.s,v 1.1.1.3 2004/10/13 18:54:32 vvd0 Exp $ -*/ -// d_spr8.s -// x86 assembly-language horizontal 8-bpp transparent span-drawing code. - -#include "asm_i386.h" -#include "quakeasm.h" -#include "asm_draw.h" - -#ifdef id386 - -//---------------------------------------------------------------------- -// 8-bpp horizontal span drawing code for polygons, with transparency. -//---------------------------------------------------------------------- - - .text - -// out-of-line, rarely-needed clamping code - -LClampHigh0: - movl C(bbextents),%esi - jmp LClampReentry0 -LClampHighOrLow0: - jg LClampHigh0 - xorl %esi,%esi - jmp LClampReentry0 - -LClampHigh1: - movl C(bbextentt),%edx - jmp LClampReentry1 -LClampHighOrLow1: - jg LClampHigh1 - xorl %edx,%edx - jmp LClampReentry1 - -LClampLow2: - movl $2048,%ebp - jmp LClampReentry2 -LClampHigh2: - movl C(bbextents),%ebp - jmp LClampReentry2 - -LClampLow3: - movl $2048,%ecx - jmp LClampReentry3 -LClampHigh3: - movl C(bbextentt),%ecx - jmp LClampReentry3 - -LClampLow4: - movl $2048,%eax - jmp LClampReentry4 -LClampHigh4: - movl C(bbextents),%eax - jmp LClampReentry4 - -LClampLow5: - movl $2048,%ebx - jmp LClampReentry5 -LClampHigh5: - movl C(bbextentt),%ebx - jmp LClampReentry5 - - -#define pspans 4+16 - - .align 4 -.globl C(D_SpriteDrawSpans) -C(D_SpriteDrawSpans): - pushl %ebp // preserve caller's stack frame - pushl %edi - pushl %esi // preserve register variables - pushl %ebx - -// -// set up scaled-by-8 steps, for 8-long segments; also set up cacheblock -// and span list pointers, and 1/z step in 0.32 fixed-point -// -// FIXME: any overlap from rearranging? - flds C(d_sdivzstepu) - fmuls fp_8 - movl C(cacheblock),%edx - flds C(d_tdivzstepu) - fmuls fp_8 - movl pspans(%esp),%ebx // point to the first span descriptor - flds C(d_zistepu) - fmuls fp_8 - movl %edx,pbase // pbase = cacheblock - flds C(d_zistepu) - fmuls fp_64kx64k - fxch %st(3) - fstps sdivz8stepu - fstps zi8stepu - fstps tdivz8stepu - fistpl izistep - movl izistep,%eax - rorl $16,%eax // put upper 16 bits in low word - movl sspan_t_count(%ebx),%ecx - movl %eax,izistep - - cmpl $0,%ecx - jle LNextSpan - -LSpanLoop: - -// -// set up the initial s/z, t/z, and 1/z on the FP stack, and generate the -// initial s and t values -// -// FIXME: pipeline FILD? - fildl sspan_t_v(%ebx) - fildl sspan_t_u(%ebx) - - fld %st(1) // dv | du | dv - fmuls C(d_sdivzstepv) // dv*d_sdivzstepv | du | dv - fld %st(1) // du | dv*d_sdivzstepv | du | dv - fmuls C(d_sdivzstepu) // du*d_sdivzstepu | dv*d_sdivzstepv | du | dv - fld %st(2) // du | du*d_sdivzstepu | dv*d_sdivzstepv | du | dv - fmuls C(d_tdivzstepu) // du*d_tdivzstepu | du*d_sdivzstepu | - // dv*d_sdivzstepv | du | dv - fxch %st(1) // du*d_sdivzstepu | du*d_tdivzstepu | - // dv*d_sdivzstepv | du | dv - faddp %st(0),%st(2) // du*d_tdivzstepu | - // du*d_sdivzstepu + dv*d_sdivzstepv | du | dv - fxch %st(1) // du*d_sdivzstepu + dv*d_sdivzstepv | - // du*d_tdivzstepu | du | dv - fld %st(3) // dv | du*d_sdivzstepu + dv*d_sdivzstepv | - // du*d_tdivzstepu | du | dv - fmuls C(d_tdivzstepv) // dv*d_tdivzstepv | - // du*d_sdivzstepu + dv*d_sdivzstepv | - // du*d_tdivzstepu | du | dv - fxch %st(1) // du*d_sdivzstepu + dv*d_sdivzstepv | - // dv*d_tdivzstepv | du*d_tdivzstepu | du | dv - fadds C(d_sdivzorigin) // sdivz = d_sdivzorigin + dv*d_sdivzstepv + - // du*d_sdivzstepu; stays in %st(2) at end - fxch %st(4) // dv | dv*d_tdivzstepv | du*d_tdivzstepu | du | - // s/z - fmuls C(d_zistepv) // dv*d_zistepv | dv*d_tdivzstepv | - // du*d_tdivzstepu | du | s/z - fxch %st(1) // dv*d_tdivzstepv | dv*d_zistepv | - // du*d_tdivzstepu | du | s/z - faddp %st(0),%st(2) // dv*d_zistepv | - // dv*d_tdivzstepv + du*d_tdivzstepu | du | s/z - fxch %st(2) // du | dv*d_tdivzstepv + du*d_tdivzstepu | - // dv*d_zistepv | s/z - fmuls C(d_zistepu) // du*d_zistepu | - // dv*d_tdivzstepv + du*d_tdivzstepu | - // dv*d_zistepv | s/z - fxch %st(1) // dv*d_tdivzstepv + du*d_tdivzstepu | - // du*d_zistepu | dv*d_zistepv | s/z - fadds C(d_tdivzorigin) // tdivz = d_tdivzorigin + dv*d_tdivzstepv + - // du*d_tdivzstepu; stays in %st(1) at end - fxch %st(2) // dv*d_zistepv | du*d_zistepu | t/z | s/z - faddp %st(0),%st(1) // dv*d_zistepv + du*d_zistepu | t/z | s/z - - flds fp_64k // fp_64k | dv*d_zistepv + du*d_zistepu | t/z | s/z - fxch %st(1) // dv*d_zistepv + du*d_zistepu | fp_64k | t/z | s/z - fadds C(d_ziorigin) // zi = d_ziorigin + dv*d_zistepv + - // du*d_zistepu; stays in %st(0) at end - // 1/z | fp_64k | t/z | s/z - - fld %st(0) // FIXME: get rid of stall on FMUL? - fmuls fp_64kx64k - fxch %st(1) - -// -// calculate and clamp s & t -// - fdivr %st(0),%st(2) // 1/z | z*64k | t/z | s/z - fxch %st(1) - - fistpl izi // 0.32 fixed-point 1/z - movl izi,%ebp - -// -// set pz to point to the first z-buffer pixel in the span -// - rorl $16,%ebp // put upper 16 bits in low word - movl sspan_t_v(%ebx),%eax - movl %ebp,izi - movl sspan_t_u(%ebx),%ebp - imull C(d_zrowbytes) - shll $1,%ebp // a word per pixel - addl C(d_pzbuffer),%eax - addl %ebp,%eax - movl %eax,pz - -// -// point %edi to the first pixel in the span -// - movl C(d_viewbuffer),%ebp - movl sspan_t_v(%ebx),%eax - pushl %ebx // preserve spans pointer - movl C(tadjust),%edx - movl C(sadjust),%esi - movl C(d_scantable)(,%eax,4),%edi // v * screenwidth - addl %ebp,%edi - movl sspan_t_u(%ebx),%ebp - addl %ebp,%edi // pdest = &pdestspan[scans->u]; - -// -// now start the FDIV for the end of the span -// - cmpl $8,%ecx - ja LSetupNotLast1 - - decl %ecx - jz LCleanup1 // if only one pixel, no need to start an FDIV - movl %ecx,spancountminus1 - -// finish up the s and t calcs - fxch %st(1) // z*64k | 1/z | t/z | s/z - - fld %st(0) // z*64k | z*64k | 1/z | t/z | s/z - fmul %st(4),%st(0) // s | z*64k | 1/z | t/z | s/z - fxch %st(1) // z*64k | s | 1/z | t/z | s/z - fmul %st(3),%st(0) // t | s | 1/z | t/z | s/z - fxch %st(1) // s | t | 1/z | t/z | s/z - fistpl s // 1/z | t | t/z | s/z - fistpl t // 1/z | t/z | s/z - - fildl spancountminus1 - - flds C(d_tdivzstepu) // _d_tdivzstepu | spancountminus1 - flds C(d_zistepu) // _d_zistepu | _d_tdivzstepu | spancountminus1 - fmul %st(2),%st(0) // _d_zistepu*scm1 | _d_tdivzstepu | scm1 - fxch %st(1) // _d_tdivzstepu | _d_zistepu*scm1 | scm1 - fmul %st(2),%st(0) // _d_tdivzstepu*scm1 | _d_zistepu*scm1 | scm1 - fxch %st(2) // scm1 | _d_zistepu*scm1 | _d_tdivzstepu*scm1 - fmuls C(d_sdivzstepu) // _d_sdivzstepu*scm1 | _d_zistepu*scm1 | - // _d_tdivzstepu*scm1 - fxch %st(1) // _d_zistepu*scm1 | _d_sdivzstepu*scm1 | - // _d_tdivzstepu*scm1 - faddp %st(0),%st(3) // _d_sdivzstepu*scm1 | _d_tdivzstepu*scm1 - fxch %st(1) // _d_tdivzstepu*scm1 | _d_sdivzstepu*scm1 - faddp %st(0),%st(3) // _d_sdivzstepu*scm1 - faddp %st(0),%st(3) - - flds fp_64k - fdiv %st(1),%st(0) // this is what we've gone to all this trouble to - // overlap - jmp LFDIVInFlight1 - -LCleanup1: -// finish up the s and t calcs - fxch %st(1) // z*64k | 1/z | t/z | s/z - - fld %st(0) // z*64k | z*64k | 1/z | t/z | s/z - fmul %st(4),%st(0) // s | z*64k | 1/z | t/z | s/z - fxch %st(1) // z*64k | s | 1/z | t/z | s/z - fmul %st(3),%st(0) // t | s | 1/z | t/z | s/z - fxch %st(1) // s | t | 1/z | t/z | s/z - fistpl s // 1/z | t | t/z | s/z - fistpl t // 1/z | t/z | s/z - jmp LFDIVInFlight1 - - .align 4 -LSetupNotLast1: -// finish up the s and t calcs - fxch %st(1) // z*64k | 1/z | t/z | s/z - - fld %st(0) // z*64k | z*64k | 1/z | t/z | s/z - fmul %st(4),%st(0) // s | z*64k | 1/z | t/z | s/z - fxch %st(1) // z*64k | s | 1/z | t/z | s/z - fmul %st(3),%st(0) // t | s | 1/z | t/z | s/z - fxch %st(1) // s | t | 1/z | t/z | s/z - fistpl s // 1/z | t | t/z | s/z - fistpl t // 1/z | t/z | s/z - - fadds zi8stepu - fxch %st(2) - fadds sdivz8stepu - fxch %st(2) - flds tdivz8stepu - faddp %st(0),%st(2) - flds fp_64k - fdiv %st(1),%st(0) // z = 1/1/z - // this is what we've gone to all this trouble to - // overlap -LFDIVInFlight1: - - addl s,%esi - addl t,%edx - movl C(bbextents),%ebx - movl C(bbextentt),%ebp - cmpl %ebx,%esi - ja LClampHighOrLow0 -LClampReentry0: - movl %esi,s - movl pbase,%ebx - shll $16,%esi - cmpl %ebp,%edx - movl %esi,sfracf - ja LClampHighOrLow1 -LClampReentry1: - movl %edx,t - movl s,%esi // sfrac = scans->sfrac; - shll $16,%edx - movl t,%eax // tfrac = scans->tfrac; - sarl $16,%esi - movl %edx,tfracf - -// -// calculate the texture starting address -// - sarl $16,%eax - addl %ebx,%esi - imull C(cachewidth),%eax // (tfrac >> 16) * cachewidth - addl %eax,%esi // psource = pbase + (sfrac >> 16) + - // ((tfrac >> 16) * cachewidth); - -// -// determine whether last span or not -// - cmpl $8,%ecx - jna LLastSegment - -// -// not the last segment; do full 8-wide segment -// -LNotLastSegment: - -// -// advance s/z, t/z, and 1/z, and calculate s & t at end of span and steps to -// get there -// - -// pick up after the FDIV that was left in flight previously - - fld %st(0) // duplicate it - fmul %st(4),%st(0) // s = s/z * z - fxch %st(1) - fmul %st(3),%st(0) // t = t/z * z - fxch %st(1) - fistpl snext - fistpl tnext - movl snext,%eax - movl tnext,%edx - - subl $8,%ecx // count off this segments' pixels - movl C(sadjust),%ebp - pushl %ecx // remember count of remaining pixels - movl C(tadjust),%ecx - - addl %eax,%ebp - addl %edx,%ecx - - movl C(bbextents),%eax - movl C(bbextentt),%edx - - cmpl $2048,%ebp - jl LClampLow2 - cmpl %eax,%ebp - ja LClampHigh2 -LClampReentry2: - - cmpl $2048,%ecx - jl LClampLow3 - cmpl %edx,%ecx - ja LClampHigh3 -LClampReentry3: - - movl %ebp,snext - movl %ecx,tnext - - subl s,%ebp - subl t,%ecx - -// -// set up advancetable -// - movl %ecx,%eax - movl %ebp,%edx - sarl $19,%edx // sstep >>= 16; - movl C(cachewidth),%ebx - sarl $19,%eax // tstep >>= 16; - jz LIsZero - imull %ebx,%eax // (tstep >> 16) * cachewidth; -LIsZero: - addl %edx,%eax // add in sstep - // (tstep >> 16) * cachewidth + (sstep >> 16); - movl tfracf,%edx - movl %eax,advancetable+4 // advance base in t - addl %ebx,%eax // ((tstep >> 16) + 1) * cachewidth + - // (sstep >> 16); - shll $13,%ebp // left-justify sstep fractional part - movl %ebp,sstep - movl sfracf,%ebx - shll $13,%ecx // left-justify tstep fractional part - movl %eax,advancetable // advance extra in t - movl %ecx,tstep - - movl pz,%ecx - movl izi,%ebp - - cmpw (%ecx),%bp - jl Lp1 - movb (%esi),%al // get first source texel - cmpb $(TRANSPARENT_COLOR),%al - jz Lp1 - movw %bp,(%ecx) - movb %al,(%edi) // store first dest pixel -Lp1: - addl izistep,%ebp - adcl $0,%ebp - addl tstep,%edx // advance tfrac fractional part by tstep frac - - sbbl %eax,%eax // turn tstep carry into -1 (0 if none) - addl sstep,%ebx // advance sfrac fractional part by sstep frac - adcl advancetable+4(,%eax,4),%esi // point to next source texel - - cmpw 2(%ecx),%bp - jl Lp2 - movb (%esi),%al - cmpb $(TRANSPARENT_COLOR),%al - jz Lp2 - movw %bp,2(%ecx) - movb %al,1(%edi) -Lp2: - addl izistep,%ebp - adcl $0,%ebp - addl tstep,%edx - sbbl %eax,%eax - addl sstep,%ebx - adcl advancetable+4(,%eax,4),%esi - - cmpw 4(%ecx),%bp - jl Lp3 - movb (%esi),%al - cmpb $(TRANSPARENT_COLOR),%al - jz Lp3 - movw %bp,4(%ecx) - movb %al,2(%edi) -Lp3: - addl izistep,%ebp - adcl $0,%ebp - addl tstep,%edx - sbbl %eax,%eax - addl sstep,%ebx - adcl advancetable+4(,%eax,4),%esi - - cmpw 6(%ecx),%bp - jl Lp4 - movb (%esi),%al - cmpb $(TRANSPARENT_COLOR),%al - jz Lp4 - movw %bp,6(%ecx) - movb %al,3(%edi) -Lp4: - addl izistep,%ebp - adcl $0,%ebp - addl tstep,%edx - sbbl %eax,%eax - addl sstep,%ebx - adcl advancetable+4(,%eax,4),%esi - - cmpw 8(%ecx),%bp - jl Lp5 - movb (%esi),%al - cmpb $(TRANSPARENT_COLOR),%al - jz Lp5 - movw %bp,8(%ecx) - movb %al,4(%edi) -Lp5: - addl izistep,%ebp - adcl $0,%ebp - addl tstep,%edx - sbbl %eax,%eax - addl sstep,%ebx - adcl advancetable+4(,%eax,4),%esi - -// -// start FDIV for end of next segment in flight, so it can overlap -// - popl %eax - cmpl $8,%eax // more than one segment after this? - ja LSetupNotLast2 // yes - - decl %eax - jz LFDIVInFlight2 // if only one pixel, no need to start an FDIV - movl %eax,spancountminus1 - fildl spancountminus1 - - flds C(d_zistepu) // _d_zistepu | spancountminus1 - fmul %st(1),%st(0) // _d_zistepu*scm1 | scm1 - flds C(d_tdivzstepu) // _d_tdivzstepu | _d_zistepu*scm1 | scm1 - fmul %st(2),%st(0) // _d_tdivzstepu*scm1 | _d_zistepu*scm1 | scm1 - fxch %st(1) // _d_zistepu*scm1 | _d_tdivzstepu*scm1 | scm1 - faddp %st(0),%st(3) // _d_tdivzstepu*scm1 | scm1 - fxch %st(1) // scm1 | _d_tdivzstepu*scm1 - fmuls C(d_sdivzstepu) // _d_sdivzstepu*scm1 | _d_tdivzstepu*scm1 - fxch %st(1) // _d_tdivzstepu*scm1 | _d_sdivzstepu*scm1 - faddp %st(0),%st(3) // _d_sdivzstepu*scm1 - flds fp_64k // 64k | _d_sdivzstepu*scm1 - fxch %st(1) // _d_sdivzstepu*scm1 | 64k - faddp %st(0),%st(4) // 64k - - fdiv %st(1),%st(0) // this is what we've gone to all this trouble to - // overlap - jmp LFDIVInFlight2 - - .align 4 -LSetupNotLast2: - fadds zi8stepu - fxch %st(2) - fadds sdivz8stepu - fxch %st(2) - flds tdivz8stepu - faddp %st(0),%st(2) - flds fp_64k - fdiv %st(1),%st(0) // z = 1/1/z - // this is what we've gone to all this trouble to - // overlap -LFDIVInFlight2: - pushl %eax - - cmpw 10(%ecx),%bp - jl Lp6 - movb (%esi),%al - cmpb $(TRANSPARENT_COLOR),%al - jz Lp6 - movw %bp,10(%ecx) - movb %al,5(%edi) -Lp6: - addl izistep,%ebp - adcl $0,%ebp - addl tstep,%edx - sbbl %eax,%eax - addl sstep,%ebx - adcl advancetable+4(,%eax,4),%esi - - cmpw 12(%ecx),%bp - jl Lp7 - movb (%esi),%al - cmpb $(TRANSPARENT_COLOR),%al - jz Lp7 - movw %bp,12(%ecx) - movb %al,6(%edi) -Lp7: - addl izistep,%ebp - adcl $0,%ebp - addl tstep,%edx - sbbl %eax,%eax - addl sstep,%ebx - adcl advancetable+4(,%eax,4),%esi - - cmpw 14(%ecx),%bp - jl Lp8 - movb (%esi),%al - cmpb $(TRANSPARENT_COLOR),%al - jz Lp8 - movw %bp,14(%ecx) - movb %al,7(%edi) -Lp8: - addl izistep,%ebp - adcl $0,%ebp - addl tstep,%edx - sbbl %eax,%eax - addl sstep,%ebx - adcl advancetable+4(,%eax,4),%esi - - addl $8,%edi - addl $16,%ecx - movl %edx,tfracf - movl snext,%edx - movl %ebx,sfracf - movl tnext,%ebx - movl %edx,s - movl %ebx,t - - movl %ecx,pz - movl %ebp,izi - - popl %ecx // retrieve count - -// -// determine whether last span or not -// - cmpl $8,%ecx // are there multiple segments remaining? - ja LNotLastSegment // yes - -// -// last segment of scan -// -LLastSegment: - -// -// advance s/z, t/z, and 1/z, and calculate s & t at end of span and steps to -// get there. The number of pixels left is variable, and we want to land on the -// last pixel, not step one past it, so we can't run into arithmetic problems -// - testl %ecx,%ecx - jz LNoSteps // just draw the last pixel and we're done - -// pick up after the FDIV that was left in flight previously - - - fld %st(0) // duplicate it - fmul %st(4),%st(0) // s = s/z * z - fxch %st(1) - fmul %st(3),%st(0) // t = t/z * z - fxch %st(1) - fistpl snext - fistpl tnext - - movl C(tadjust),%ebx - movl C(sadjust),%eax - - addl snext,%eax - addl tnext,%ebx - - movl C(bbextents),%ebp - movl C(bbextentt),%edx - - cmpl $2048,%eax - jl LClampLow4 - cmpl %ebp,%eax - ja LClampHigh4 -LClampReentry4: - movl %eax,snext - - cmpl $2048,%ebx - jl LClampLow5 - cmpl %edx,%ebx - ja LClampHigh5 -LClampReentry5: - - cmpl $1,%ecx // don't bother - je LOnlyOneStep // if two pixels in segment, there's only one step, - // of the segment length - subl s,%eax - subl t,%ebx - - addl %eax,%eax // convert to 15.17 format so multiply by 1.31 - addl %ebx,%ebx // reciprocal yields 16.48 - imull reciprocal_table-8(,%ecx,4) // sstep = (snext - s) / (spancount-1) - movl %edx,%ebp - - movl %ebx,%eax - imull reciprocal_table-8(,%ecx,4) // tstep = (tnext - t) / (spancount-1) - -LSetEntryvec: -// -// set up advancetable -// - movl spr8entryvec_table(,%ecx,4),%ebx - movl %edx,%eax - pushl %ebx // entry point into code for RET later - movl %ebp,%ecx - sarl $16,%ecx // sstep >>= 16; - movl C(cachewidth),%ebx - sarl $16,%edx // tstep >>= 16; - jz LIsZeroLast - imull %ebx,%edx // (tstep >> 16) * cachewidth; -LIsZeroLast: - addl %ecx,%edx // add in sstep - // (tstep >> 16) * cachewidth + (sstep >> 16); - movl tfracf,%ecx - movl %edx,advancetable+4 // advance base in t - addl %ebx,%edx // ((tstep >> 16) + 1) * cachewidth + - // (sstep >> 16); - shll $16,%ebp // left-justify sstep fractional part - movl sfracf,%ebx - shll $16,%eax // left-justify tstep fractional part - movl %edx,advancetable // advance extra in t - - movl %eax,tstep - movl %ebp,sstep - movl %ecx,%edx - - movl pz,%ecx - movl izi,%ebp - - ret // jump to the number-of-pixels handler - -//---------------------------------------- - -LNoSteps: - movl pz,%ecx - subl $7,%edi // adjust for hardwired offset - subl $14,%ecx - jmp LEndSpan - - -LOnlyOneStep: - subl s,%eax - subl t,%ebx - movl %eax,%ebp - movl %ebx,%edx - jmp LSetEntryvec - -//---------------------------------------- - -.globl Spr8Entry2_8 -Spr8Entry2_8: - subl $6,%edi // adjust for hardwired offsets - subl $12,%ecx - movb (%esi),%al - jmp LLEntry2_8 - -//---------------------------------------- - -.globl Spr8Entry3_8 -Spr8Entry3_8: - subl $5,%edi // adjust for hardwired offsets - subl $10,%ecx - jmp LLEntry3_8 - -//---------------------------------------- - -.globl Spr8Entry4_8 -Spr8Entry4_8: - subl $4,%edi // adjust for hardwired offsets - subl $8,%ecx - jmp LLEntry4_8 - -//---------------------------------------- - -.globl Spr8Entry5_8 -Spr8Entry5_8: - subl $3,%edi // adjust for hardwired offsets - subl $6,%ecx - jmp LLEntry5_8 - -//---------------------------------------- - -.globl Spr8Entry6_8 -Spr8Entry6_8: - subl $2,%edi // adjust for hardwired offsets - subl $4,%ecx - jmp LLEntry6_8 - -//---------------------------------------- - -.globl Spr8Entry7_8 -Spr8Entry7_8: - decl %edi // adjust for hardwired offsets - subl $2,%ecx - jmp LLEntry7_8 - -//---------------------------------------- - -.globl Spr8Entry8_8 -Spr8Entry8_8: - cmpw (%ecx),%bp - jl Lp9 - movb (%esi),%al - cmpb $(TRANSPARENT_COLOR),%al - jz Lp9 - movw %bp,(%ecx) - movb %al,(%edi) -Lp9: - addl izistep,%ebp - adcl $0,%ebp - addl tstep,%edx - sbbl %eax,%eax - addl sstep,%ebx - adcl advancetable+4(,%eax,4),%esi -LLEntry7_8: - cmpw 2(%ecx),%bp - jl Lp10 - movb (%esi),%al - cmpb $(TRANSPARENT_COLOR),%al - jz Lp10 - movw %bp,2(%ecx) - movb %al,1(%edi) -Lp10: - addl izistep,%ebp - adcl $0,%ebp - addl tstep,%edx - sbbl %eax,%eax - addl sstep,%ebx - adcl advancetable+4(,%eax,4),%esi -LLEntry6_8: - cmpw 4(%ecx),%bp - jl Lp11 - movb (%esi),%al - cmpb $(TRANSPARENT_COLOR),%al - jz Lp11 - movw %bp,4(%ecx) - movb %al,2(%edi) -Lp11: - addl izistep,%ebp - adcl $0,%ebp - addl tstep,%edx - sbbl %eax,%eax - addl sstep,%ebx - adcl advancetable+4(,%eax,4),%esi -LLEntry5_8: - cmpw 6(%ecx),%bp - jl Lp12 - movb (%esi),%al - cmpb $(TRANSPARENT_COLOR),%al - jz Lp12 - movw %bp,6(%ecx) - movb %al,3(%edi) -Lp12: - addl izistep,%ebp - adcl $0,%ebp - addl tstep,%edx - sbbl %eax,%eax - addl sstep,%ebx - adcl advancetable+4(,%eax,4),%esi -LLEntry4_8: - cmpw 8(%ecx),%bp - jl Lp13 - movb (%esi),%al - cmpb $(TRANSPARENT_COLOR),%al - jz Lp13 - movw %bp,8(%ecx) - movb %al,4(%edi) -Lp13: - addl izistep,%ebp - adcl $0,%ebp - addl tstep,%edx - sbbl %eax,%eax - addl sstep,%ebx - adcl advancetable+4(,%eax,4),%esi -LLEntry3_8: - cmpw 10(%ecx),%bp - jl Lp14 - movb (%esi),%al - cmpb $(TRANSPARENT_COLOR),%al - jz Lp14 - movw %bp,10(%ecx) - movb %al,5(%edi) -Lp14: - addl izistep,%ebp - adcl $0,%ebp - addl tstep,%edx - sbbl %eax,%eax - addl sstep,%ebx - adcl advancetable+4(,%eax,4),%esi -LLEntry2_8: - cmpw 12(%ecx),%bp - jl Lp15 - movb (%esi),%al - cmpb $(TRANSPARENT_COLOR),%al - jz Lp15 - movw %bp,12(%ecx) - movb %al,6(%edi) -Lp15: - addl izistep,%ebp - adcl $0,%ebp - addl tstep,%edx - sbbl %eax,%eax - addl sstep,%ebx - adcl advancetable+4(,%eax,4),%esi - -LEndSpan: - cmpw 14(%ecx),%bp - jl Lp16 - movb (%esi),%al // load first texel in segment - cmpb $(TRANSPARENT_COLOR),%al - jz Lp16 - movw %bp,14(%ecx) - movb %al,7(%edi) -Lp16: - -// -// clear s/z, t/z, 1/z from FP stack -// - fstp %st(0) - fstp %st(0) - fstp %st(0) - - popl %ebx // restore spans pointer -LNextSpan: - addl $(sspan_t_size),%ebx // point to next span - movl sspan_t_count(%ebx),%ecx - cmpl $0,%ecx // any more spans? - jg LSpanLoop // yes - jz LNextSpan // yes, but this one's empty - - popl %ebx // restore register variables - popl %esi - popl %edi - popl %ebp // restore the caller's stack frame - ret - -#endif // id386 +/* + d_spr8.S + + (description) + + Copyright (C) 1996-1997 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id: d_spr8.s,v 1.1.1.4 2004/10/18 17:44:27 vvd0 Exp $ +*/ +// d_spr8.s +// x86 assembly-language horizontal 8-bpp transparent span-drawing code. + +#include "asm_i386.h" +#include "quakeasm.h" +#include "asm_draw.h" + +#ifdef id386 + +//---------------------------------------------------------------------- +// 8-bpp horizontal span drawing code for polygons, with transparency. +//---------------------------------------------------------------------- + + .text + +// out-of-line, rarely-needed clamping code + +LClampHigh0: + movl C(bbextents),%esi + jmp LClampReentry0 +LClampHighOrLow0: + jg LClampHigh0 + xorl %esi,%esi + jmp LClampReentry0 + +LClampHigh1: + movl C(bbextentt),%edx + jmp LClampReentry1 +LClampHighOrLow1: + jg LClampHigh1 + xorl %edx,%edx + jmp LClampReentry1 + +LClampLow2: + movl $2048,%ebp + jmp LClampReentry2 +LClampHigh2: + movl C(bbextents),%ebp + jmp LClampReentry2 + +LClampLow3: + movl $2048,%ecx + jmp LClampReentry3 +LClampHigh3: + movl C(bbextentt),%ecx + jmp LClampReentry3 + +LClampLow4: + movl $2048,%eax + jmp LClampReentry4 +LClampHigh4: + movl C(bbextents),%eax + jmp LClampReentry4 + +LClampLow5: + movl $2048,%ebx + jmp LClampReentry5 +LClampHigh5: + movl C(bbextentt),%ebx + jmp LClampReentry5 + + +#define pspans 4+16 + + .align 4 +.globl C(D_SpriteDrawSpans) +C(D_SpriteDrawSpans): + pushl %ebp // preserve caller's stack frame + pushl %edi + pushl %esi // preserve register variables + pushl %ebx + +// +// set up scaled-by-8 steps, for 8-long segments; also set up cacheblock +// and span list pointers, and 1/z step in 0.32 fixed-point +// +// FIXME: any overlap from rearranging? + flds C(d_sdivzstepu) + fmuls fp_8 + movl C(cacheblock),%edx + flds C(d_tdivzstepu) + fmuls fp_8 + movl pspans(%esp),%ebx // point to the first span descriptor + flds C(d_zistepu) + fmuls fp_8 + movl %edx,pbase // pbase = cacheblock + flds C(d_zistepu) + fmuls fp_64kx64k + fxch %st(3) + fstps sdivz8stepu + fstps zi8stepu + fstps tdivz8stepu + fistpl izistep + movl izistep,%eax + rorl $16,%eax // put upper 16 bits in low word + movl sspan_t_count(%ebx),%ecx + movl %eax,izistep + + cmpl $0,%ecx + jle LNextSpan + +LSpanLoop: + +// +// set up the initial s/z, t/z, and 1/z on the FP stack, and generate the +// initial s and t values +// +// FIXME: pipeline FILD? + fildl sspan_t_v(%ebx) + fildl sspan_t_u(%ebx) + + fld %st(1) // dv | du | dv + fmuls C(d_sdivzstepv) // dv*d_sdivzstepv | du | dv + fld %st(1) // du | dv*d_sdivzstepv | du | dv + fmuls C(d_sdivzstepu) // du*d_sdivzstepu | dv*d_sdivzstepv | du | dv + fld %st(2) // du | du*d_sdivzstepu | dv*d_sdivzstepv | du | dv + fmuls C(d_tdivzstepu) // du*d_tdivzstepu | du*d_sdivzstepu | + // dv*d_sdivzstepv | du | dv + fxch %st(1) // du*d_sdivzstepu | du*d_tdivzstepu | + // dv*d_sdivzstepv | du | dv + faddp %st(0),%st(2) // du*d_tdivzstepu | + // du*d_sdivzstepu + dv*d_sdivzstepv | du | dv + fxch %st(1) // du*d_sdivzstepu + dv*d_sdivzstepv | + // du*d_tdivzstepu | du | dv + fld %st(3) // dv | du*d_sdivzstepu + dv*d_sdivzstepv | + // du*d_tdivzstepu | du | dv + fmuls C(d_tdivzstepv) // dv*d_tdivzstepv | + // du*d_sdivzstepu + dv*d_sdivzstepv | + // du*d_tdivzstepu | du | dv + fxch %st(1) // du*d_sdivzstepu + dv*d_sdivzstepv | + // dv*d_tdivzstepv | du*d_tdivzstepu | du | dv + fadds C(d_sdivzorigin) // sdivz = d_sdivzorigin + dv*d_sdivzstepv + + // du*d_sdivzstepu; stays in %st(2) at end + fxch %st(4) // dv | dv*d_tdivzstepv | du*d_tdivzstepu | du | + // s/z + fmuls C(d_zistepv) // dv*d_zistepv | dv*d_tdivzstepv | + // du*d_tdivzstepu | du | s/z + fxch %st(1) // dv*d_tdivzstepv | dv*d_zistepv | + // du*d_tdivzstepu | du | s/z + faddp %st(0),%st(2) // dv*d_zistepv | + // dv*d_tdivzstepv + du*d_tdivzstepu | du | s/z + fxch %st(2) // du | dv*d_tdivzstepv + du*d_tdivzstepu | + // dv*d_zistepv | s/z + fmuls C(d_zistepu) // du*d_zistepu | + // dv*d_tdivzstepv + du*d_tdivzstepu | + // dv*d_zistepv | s/z + fxch %st(1) // dv*d_tdivzstepv + du*d_tdivzstepu | + // du*d_zistepu | dv*d_zistepv | s/z + fadds C(d_tdivzorigin) // tdivz = d_tdivzorigin + dv*d_tdivzstepv + + // du*d_tdivzstepu; stays in %st(1) at end + fxch %st(2) // dv*d_zistepv | du*d_zistepu | t/z | s/z + faddp %st(0),%st(1) // dv*d_zistepv + du*d_zistepu | t/z | s/z + + flds fp_64k // fp_64k | dv*d_zistepv + du*d_zistepu | t/z | s/z + fxch %st(1) // dv*d_zistepv + du*d_zistepu | fp_64k | t/z | s/z + fadds C(d_ziorigin) // zi = d_ziorigin + dv*d_zistepv + + // du*d_zistepu; stays in %st(0) at end + // 1/z | fp_64k | t/z | s/z + + fld %st(0) // FIXME: get rid of stall on FMUL? + fmuls fp_64kx64k + fxch %st(1) + +// +// calculate and clamp s & t +// + fdivr %st(0),%st(2) // 1/z | z*64k | t/z | s/z + fxch %st(1) + + fistpl izi // 0.32 fixed-point 1/z + movl izi,%ebp + +// +// set pz to point to the first z-buffer pixel in the span +// + rorl $16,%ebp // put upper 16 bits in low word + movl sspan_t_v(%ebx),%eax + movl %ebp,izi + movl sspan_t_u(%ebx),%ebp + imull C(d_zrowbytes) + shll $1,%ebp // a word per pixel + addl C(d_pzbuffer),%eax + addl %ebp,%eax + movl %eax,pz + +// +// point %edi to the first pixel in the span +// + movl C(d_viewbuffer),%ebp + movl sspan_t_v(%ebx),%eax + pushl %ebx // preserve spans pointer + movl C(tadjust),%edx + movl C(sadjust),%esi + movl C(d_scantable)(,%eax,4),%edi // v * screenwidth + addl %ebp,%edi + movl sspan_t_u(%ebx),%ebp + addl %ebp,%edi // pdest = &pdestspan[scans->u]; + +// +// now start the FDIV for the end of the span +// + cmpl $8,%ecx + ja LSetupNotLast1 + + decl %ecx + jz LCleanup1 // if only one pixel, no need to start an FDIV + movl %ecx,spancountminus1 + +// finish up the s and t calcs + fxch %st(1) // z*64k | 1/z | t/z | s/z + + fld %st(0) // z*64k | z*64k | 1/z | t/z | s/z + fmul %st(4),%st(0) // s | z*64k | 1/z | t/z | s/z + fxch %st(1) // z*64k | s | 1/z | t/z | s/z + fmul %st(3),%st(0) // t | s | 1/z | t/z | s/z + fxch %st(1) // s | t | 1/z | t/z | s/z + fistpl s // 1/z | t | t/z | s/z + fistpl t // 1/z | t/z | s/z + + fildl spancountminus1 + + flds C(d_tdivzstepu) // _d_tdivzstepu | spancountminus1 + flds C(d_zistepu) // _d_zistepu | _d_tdivzstepu | spancountminus1 + fmul %st(2),%st(0) // _d_zistepu*scm1 | _d_tdivzstepu | scm1 + fxch %st(1) // _d_tdivzstepu | _d_zistepu*scm1 | scm1 + fmul %st(2),%st(0) // _d_tdivzstepu*scm1 | _d_zistepu*scm1 | scm1 + fxch %st(2) // scm1 | _d_zistepu*scm1 | _d_tdivzstepu*scm1 + fmuls C(d_sdivzstepu) // _d_sdivzstepu*scm1 | _d_zistepu*scm1 | + // _d_tdivzstepu*scm1 + fxch %st(1) // _d_zistepu*scm1 | _d_sdivzstepu*scm1 | + // _d_tdivzstepu*scm1 + faddp %st(0),%st(3) // _d_sdivzstepu*scm1 | _d_tdivzstepu*scm1 + fxch %st(1) // _d_tdivzstepu*scm1 | _d_sdivzstepu*scm1 + faddp %st(0),%st(3) // _d_sdivzstepu*scm1 + faddp %st(0),%st(3) + + flds fp_64k + fdiv %st(1),%st(0) // this is what we've gone to all this trouble to + // overlap + jmp LFDIVInFlight1 + +LCleanup1: +// finish up the s and t calcs + fxch %st(1) // z*64k | 1/z | t/z | s/z + + fld %st(0) // z*64k | z*64k | 1/z | t/z | s/z + fmul %st(4),%st(0) // s | z*64k | 1/z | t/z | s/z + fxch %st(1) // z*64k | s | 1/z | t/z | s/z + fmul %st(3),%st(0) // t | s | 1/z | t/z | s/z + fxch %st(1) // s | t | 1/z | t/z | s/z + fistpl s // 1/z | t | t/z | s/z + fistpl t // 1/z | t/z | s/z + jmp LFDIVInFlight1 + + .align 4 +LSetupNotLast1: +// finish up the s and t calcs + fxch %st(1) // z*64k | 1/z | t/z | s/z + + fld %st(0) // z*64k | z*64k | 1/z | t/z | s/z + fmul %st(4),%st(0) // s | z*64k | 1/z | t/z | s/z + fxch %st(1) // z*64k | s | 1/z | t/z | s/z + fmul %st(3),%st(0) // t | s | 1/z | t/z | s/z + fxch %st(1) // s | t | 1/z | t/z | s/z + fistpl s // 1/z | t | t/z | s/z + fistpl t // 1/z | t/z | s/z + + fadds zi8stepu + fxch %st(2) + fadds sdivz8stepu + fxch %st(2) + flds tdivz8stepu + faddp %st(0),%st(2) + flds fp_64k + fdiv %st(1),%st(0) // z = 1/1/z + // this is what we've gone to all this trouble to + // overlap +LFDIVInFlight1: + + addl s,%esi + addl t,%edx + movl C(bbextents),%ebx + movl C(bbextentt),%ebp + cmpl %ebx,%esi + ja LClampHighOrLow0 +LClampReentry0: + movl %esi,s + movl pbase,%ebx + shll $16,%esi + cmpl %ebp,%edx + movl %esi,sfracf + ja LClampHighOrLow1 +LClampReentry1: + movl %edx,t + movl s,%esi // sfrac = scans->sfrac; + shll $16,%edx + movl t,%eax // tfrac = scans->tfrac; + sarl $16,%esi + movl %edx,tfracf + +// +// calculate the texture starting address +// + sarl $16,%eax + addl %ebx,%esi + imull C(cachewidth),%eax // (tfrac >> 16) * cachewidth + addl %eax,%esi // psource = pbase + (sfrac >> 16) + + // ((tfrac >> 16) * cachewidth); + +// +// determine whether last span or not +// + cmpl $8,%ecx + jna LLastSegment + +// +// not the last segment; do full 8-wide segment +// +LNotLastSegment: + +// +// advance s/z, t/z, and 1/z, and calculate s & t at end of span and steps to +// get there +// + +// pick up after the FDIV that was left in flight previously + + fld %st(0) // duplicate it + fmul %st(4),%st(0) // s = s/z * z + fxch %st(1) + fmul %st(3),%st(0) // t = t/z * z + fxch %st(1) + fistpl snext + fistpl tnext + movl snext,%eax + movl tnext,%edx + + subl $8,%ecx // count off this segments' pixels + movl C(sadjust),%ebp + pushl %ecx // remember count of remaining pixels + movl C(tadjust),%ecx + + addl %eax,%ebp + addl %edx,%ecx + + movl C(bbextents),%eax + movl C(bbextentt),%edx + + cmpl $2048,%ebp + jl LClampLow2 + cmpl %eax,%ebp + ja LClampHigh2 +LClampReentry2: + + cmpl $2048,%ecx + jl LClampLow3 + cmpl %edx,%ecx + ja LClampHigh3 +LClampReentry3: + + movl %ebp,snext + movl %ecx,tnext + + subl s,%ebp + subl t,%ecx + +// +// set up advancetable +// + movl %ecx,%eax + movl %ebp,%edx + sarl $19,%edx // sstep >>= 16; + movl C(cachewidth),%ebx + sarl $19,%eax // tstep >>= 16; + jz LIsZero + imull %ebx,%eax // (tstep >> 16) * cachewidth; +LIsZero: + addl %edx,%eax // add in sstep + // (tstep >> 16) * cachewidth + (sstep >> 16); + movl tfracf,%edx + movl %eax,advancetable+4 // advance base in t + addl %ebx,%eax // ((tstep >> 16) + 1) * cachewidth + + // (sstep >> 16); + shll $13,%ebp // left-justify sstep fractional part + movl %ebp,sstep + movl sfracf,%ebx + shll $13,%ecx // left-justify tstep fractional part + movl %eax,advancetable // advance extra in t + movl %ecx,tstep + + movl pz,%ecx + movl izi,%ebp + + cmpw (%ecx),%bp + jl Lp1 + movb (%esi),%al // get first source texel + cmpb $(TRANSPARENT_COLOR),%al + jz Lp1 + movw %bp,(%ecx) + movb %al,(%edi) // store first dest pixel +Lp1: + addl izistep,%ebp + adcl $0,%ebp + addl tstep,%edx // advance tfrac fractional part by tstep frac + + sbbl %eax,%eax // turn tstep carry into -1 (0 if none) + addl sstep,%ebx // advance sfrac fractional part by sstep frac + adcl advancetable+4(,%eax,4),%esi // point to next source texel + + cmpw 2(%ecx),%bp + jl Lp2 + movb (%esi),%al + cmpb $(TRANSPARENT_COLOR),%al + jz Lp2 + movw %bp,2(%ecx) + movb %al,1(%edi) +Lp2: + addl izistep,%ebp + adcl $0,%ebp + addl tstep,%edx + sbbl %eax,%eax + addl sstep,%ebx + adcl advancetable+4(,%eax,4),%esi + + cmpw 4(%ecx),%bp + jl Lp3 + movb (%esi),%al + cmpb $(TRANSPARENT_COLOR),%al + jz Lp3 + movw %bp,4(%ecx) + movb %al,2(%edi) +Lp3: + addl izistep,%ebp + adcl $0,%ebp + addl tstep,%edx + sbbl %eax,%eax + addl sstep,%ebx + adcl advancetable+4(,%eax,4),%esi + + cmpw 6(%ecx),%bp + jl Lp4 + movb (%esi),%al + cmpb $(TRANSPARENT_COLOR),%al + jz Lp4 + movw %bp,6(%ecx) + movb %al,3(%edi) +Lp4: + addl izistep,%ebp + adcl $0,%ebp + addl tstep,%edx + sbbl %eax,%eax + addl sstep,%ebx + adcl advancetable+4(,%eax,4),%esi + + cmpw 8(%ecx),%bp + jl Lp5 + movb (%esi),%al + cmpb $(TRANSPARENT_COLOR),%al + jz Lp5 + movw %bp,8(%ecx) + movb %al,4(%edi) +Lp5: + addl izistep,%ebp + adcl $0,%ebp + addl tstep,%edx + sbbl %eax,%eax + addl sstep,%ebx + adcl advancetable+4(,%eax,4),%esi + +// +// start FDIV for end of next segment in flight, so it can overlap +// + popl %eax + cmpl $8,%eax // more than one segment after this? + ja LSetupNotLast2 // yes + + decl %eax + jz LFDIVInFlight2 // if only one pixel, no need to start an FDIV + movl %eax,spancountminus1 + fildl spancountminus1 + + flds C(d_zistepu) // _d_zistepu | spancountminus1 + fmul %st(1),%st(0) // _d_zistepu*scm1 | scm1 + flds C(d_tdivzstepu) // _d_tdivzstepu | _d_zistepu*scm1 | scm1 + fmul %st(2),%st(0) // _d_tdivzstepu*scm1 | _d_zistepu*scm1 | scm1 + fxch %st(1) // _d_zistepu*scm1 | _d_tdivzstepu*scm1 | scm1 + faddp %st(0),%st(3) // _d_tdivzstepu*scm1 | scm1 + fxch %st(1) // scm1 | _d_tdivzstepu*scm1 + fmuls C(d_sdivzstepu) // _d_sdivzstepu*scm1 | _d_tdivzstepu*scm1 + fxch %st(1) // _d_tdivzstepu*scm1 | _d_sdivzstepu*scm1 + faddp %st(0),%st(3) // _d_sdivzstepu*scm1 + flds fp_64k // 64k | _d_sdivzstepu*scm1 + fxch %st(1) // _d_sdivzstepu*scm1 | 64k + faddp %st(0),%st(4) // 64k + + fdiv %st(1),%st(0) // this is what we've gone to all this trouble to + // overlap + jmp LFDIVInFlight2 + + .align 4 +LSetupNotLast2: + fadds zi8stepu + fxch %st(2) + fadds sdivz8stepu + fxch %st(2) + flds tdivz8stepu + faddp %st(0),%st(2) + flds fp_64k + fdiv %st(1),%st(0) // z = 1/1/z + // this is what we've gone to all this trouble to + // overlap +LFDIVInFlight2: + pushl %eax + + cmpw 10(%ecx),%bp + jl Lp6 + movb (%esi),%al + cmpb $(TRANSPARENT_COLOR),%al + jz Lp6 + movw %bp,10(%ecx) + movb %al,5(%edi) +Lp6: + addl izistep,%ebp + adcl $0,%ebp + addl tstep,%edx + sbbl %eax,%eax + addl sstep,%ebx + adcl advancetable+4(,%eax,4),%esi + + cmpw 12(%ecx),%bp + jl Lp7 + movb (%esi),%al + cmpb $(TRANSPARENT_COLOR),%al + jz Lp7 + movw %bp,12(%ecx) + movb %al,6(%edi) +Lp7: + addl izistep,%ebp + adcl $0,%ebp + addl tstep,%edx + sbbl %eax,%eax + addl sstep,%ebx + adcl advancetable+4(,%eax,4),%esi + + cmpw 14(%ecx),%bp + jl Lp8 + movb (%esi),%al + cmpb $(TRANSPARENT_COLOR),%al + jz Lp8 + movw %bp,14(%ecx) + movb %al,7(%edi) +Lp8: + addl izistep,%ebp + adcl $0,%ebp + addl tstep,%edx + sbbl %eax,%eax + addl sstep,%ebx + adcl advancetable+4(,%eax,4),%esi + + addl $8,%edi + addl $16,%ecx + movl %edx,tfracf + movl snext,%edx + movl %ebx,sfracf + movl tnext,%ebx + movl %edx,s + movl %ebx,t + + movl %ecx,pz + movl %ebp,izi + + popl %ecx // retrieve count + +// +// determine whether last span or not +// + cmpl $8,%ecx // are there multiple segments remaining? + ja LNotLastSegment // yes + +// +// last segment of scan +// +LLastSegment: + +// +// advance s/z, t/z, and 1/z, and calculate s & t at end of span and steps to +// get there. The number of pixels left is variable, and we want to land on the +// last pixel, not step one past it, so we can't run into arithmetic problems +// + testl %ecx,%ecx + jz LNoSteps // just draw the last pixel and we're done + +// pick up after the FDIV that was left in flight previously + + + fld %st(0) // duplicate it + fmul %st(4),%st(0) // s = s/z * z + fxch %st(1) + fmul %st(3),%st(0) // t = t/z * z + fxch %st(1) + fistpl snext + fistpl tnext + + movl C(tadjust),%ebx + movl C(sadjust),%eax + + addl snext,%eax + addl tnext,%ebx + + movl C(bbextents),%ebp + movl C(bbextentt),%edx + + cmpl $2048,%eax + jl LClampLow4 + cmpl %ebp,%eax + ja LClampHigh4 +LClampReentry4: + movl %eax,snext + + cmpl $2048,%ebx + jl LClampLow5 + cmpl %edx,%ebx + ja LClampHigh5 +LClampReentry5: + + cmpl $1,%ecx // don't bother + je LOnlyOneStep // if two pixels in segment, there's only one step, + // of the segment length + subl s,%eax + subl t,%ebx + + addl %eax,%eax // convert to 15.17 format so multiply by 1.31 + addl %ebx,%ebx // reciprocal yields 16.48 + imull reciprocal_table-8(,%ecx,4) // sstep = (snext - s) / (spancount-1) + movl %edx,%ebp + + movl %ebx,%eax + imull reciprocal_table-8(,%ecx,4) // tstep = (tnext - t) / (spancount-1) + +LSetEntryvec: +// +// set up advancetable +// + movl spr8entryvec_table(,%ecx,4),%ebx + movl %edx,%eax + pushl %ebx // entry point into code for RET later + movl %ebp,%ecx + sarl $16,%ecx // sstep >>= 16; + movl C(cachewidth),%ebx + sarl $16,%edx // tstep >>= 16; + jz LIsZeroLast + imull %ebx,%edx // (tstep >> 16) * cachewidth; +LIsZeroLast: + addl %ecx,%edx // add in sstep + // (tstep >> 16) * cachewidth + (sstep >> 16); + movl tfracf,%ecx + movl %edx,advancetable+4 // advance base in t + addl %ebx,%edx // ((tstep >> 16) + 1) * cachewidth + + // (sstep >> 16); + shll $16,%ebp // left-justify sstep fractional part + movl sfracf,%ebx + shll $16,%eax // left-justify tstep fractional part + movl %edx,advancetable // advance extra in t + + movl %eax,tstep + movl %ebp,sstep + movl %ecx,%edx + + movl pz,%ecx + movl izi,%ebp + + ret // jump to the number-of-pixels handler + +//---------------------------------------- + +LNoSteps: + movl pz,%ecx + subl $7,%edi // adjust for hardwired offset + subl $14,%ecx + jmp LEndSpan + + +LOnlyOneStep: + subl s,%eax + subl t,%ebx + movl %eax,%ebp + movl %ebx,%edx + jmp LSetEntryvec + +//---------------------------------------- + +.globl Spr8Entry2_8 +Spr8Entry2_8: + subl $6,%edi // adjust for hardwired offsets + subl $12,%ecx + movb (%esi),%al + jmp LLEntry2_8 + +//---------------------------------------- + +.globl Spr8Entry3_8 +Spr8Entry3_8: + subl $5,%edi // adjust for hardwired offsets + subl $10,%ecx + jmp LLEntry3_8 + +//---------------------------------------- + +.globl Spr8Entry4_8 +Spr8Entry4_8: + subl $4,%edi // adjust for hardwired offsets + subl $8,%ecx + jmp LLEntry4_8 + +//---------------------------------------- + +.globl Spr8Entry5_8 +Spr8Entry5_8: + subl $3,%edi // adjust for hardwired offsets + subl $6,%ecx + jmp LLEntry5_8 + +//---------------------------------------- + +.globl Spr8Entry6_8 +Spr8Entry6_8: + subl $2,%edi // adjust for hardwired offsets + subl $4,%ecx + jmp LLEntry6_8 + +//---------------------------------------- + +.globl Spr8Entry7_8 +Spr8Entry7_8: + decl %edi // adjust for hardwired offsets + subl $2,%ecx + jmp LLEntry7_8 + +//---------------------------------------- + +.globl Spr8Entry8_8 +Spr8Entry8_8: + cmpw (%ecx),%bp + jl Lp9 + movb (%esi),%al + cmpb $(TRANSPARENT_COLOR),%al + jz Lp9 + movw %bp,(%ecx) + movb %al,(%edi) +Lp9: + addl izistep,%ebp + adcl $0,%ebp + addl tstep,%edx + sbbl %eax,%eax + addl sstep,%ebx + adcl advancetable+4(,%eax,4),%esi +LLEntry7_8: + cmpw 2(%ecx),%bp + jl Lp10 + movb (%esi),%al + cmpb $(TRANSPARENT_COLOR),%al + jz Lp10 + movw %bp,2(%ecx) + movb %al,1(%edi) +Lp10: + addl izistep,%ebp + adcl $0,%ebp + addl tstep,%edx + sbbl %eax,%eax + addl sstep,%ebx + adcl advancetable+4(,%eax,4),%esi +LLEntry6_8: + cmpw 4(%ecx),%bp + jl Lp11 + movb (%esi),%al + cmpb $(TRANSPARENT_COLOR),%al + jz Lp11 + movw %bp,4(%ecx) + movb %al,2(%edi) +Lp11: + addl izistep,%ebp + adcl $0,%ebp + addl tstep,%edx + sbbl %eax,%eax + addl sstep,%ebx + adcl advancetable+4(,%eax,4),%esi +LLEntry5_8: + cmpw 6(%ecx),%bp + jl Lp12 + movb (%esi),%al + cmpb $(TRANSPARENT_COLOR),%al + jz Lp12 + movw %bp,6(%ecx) + movb %al,3(%edi) +Lp12: + addl izistep,%ebp + adcl $0,%ebp + addl tstep,%edx + sbbl %eax,%eax + addl sstep,%ebx + adcl advancetable+4(,%eax,4),%esi +LLEntry4_8: + cmpw 8(%ecx),%bp + jl Lp13 + movb (%esi),%al + cmpb $(TRANSPARENT_COLOR),%al + jz Lp13 + movw %bp,8(%ecx) + movb %al,4(%edi) +Lp13: + addl izistep,%ebp + adcl $0,%ebp + addl tstep,%edx + sbbl %eax,%eax + addl sstep,%ebx + adcl advancetable+4(,%eax,4),%esi +LLEntry3_8: + cmpw 10(%ecx),%bp + jl Lp14 + movb (%esi),%al + cmpb $(TRANSPARENT_COLOR),%al + jz Lp14 + movw %bp,10(%ecx) + movb %al,5(%edi) +Lp14: + addl izistep,%ebp + adcl $0,%ebp + addl tstep,%edx + sbbl %eax,%eax + addl sstep,%ebx + adcl advancetable+4(,%eax,4),%esi +LLEntry2_8: + cmpw 12(%ecx),%bp + jl Lp15 + movb (%esi),%al + cmpb $(TRANSPARENT_COLOR),%al + jz Lp15 + movw %bp,12(%ecx) + movb %al,6(%edi) +Lp15: + addl izistep,%ebp + adcl $0,%ebp + addl tstep,%edx + sbbl %eax,%eax + addl sstep,%ebx + adcl advancetable+4(,%eax,4),%esi + +LEndSpan: + cmpw 14(%ecx),%bp + jl Lp16 + movb (%esi),%al // load first texel in segment + cmpb $(TRANSPARENT_COLOR),%al + jz Lp16 + movw %bp,14(%ecx) + movb %al,7(%edi) +Lp16: + +// +// clear s/z, t/z, 1/z from FP stack +// + fstp %st(0) + fstp %st(0) + fstp %st(0) + + popl %ebx // restore spans pointer +LNextSpan: + addl $(sspan_t_size),%ebx // point to next span + movl sspan_t_count(%ebx),%ecx + cmpl $0,%ecx // any more spans? + jg LSpanLoop // yes + jz LNextSpan // yes, but this one's empty + + popl %ebx // restore register variables + popl %esi + popl %edi + popl %ebp // restore the caller's stack frame + ret + +#endif // id386 diff --git a/source/d_sprite.c b/source/d_sprite.c index 7adff967..2f02ad2c 100644 --- a/source/d_sprite.c +++ b/source/d_sprite.c @@ -1,442 +1,442 @@ -/* -Copyright (C) 1996-1997 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_sprite.c: software top-level rasterization driver module for drawing -// sprites - -#include "quakedef.h" -#include "d_local.h" - -static int sprite_height; -static int minindex, maxindex; -static sspan_t *sprite_spans; - -#if !id386 - -/* -===================== -D_SpriteDrawSpans -===================== -*/ -void D_SpriteDrawSpans (sspan_t *pspan) -{ - int count, spancount, izistep; - int izi; - byte *pbase, *pdest; - fixed16_t s, t, snext, tnext, sstep, tstep; - float sdivz, tdivz, zi, z, du, dv, spancountminus1; - float sdivz8stepu, tdivz8stepu, zi8stepu; - byte btemp; - short *pz; - - sstep = 0; // keep compiler happy - tstep = 0; // ditto - - pbase = cacheblock; - - sdivz8stepu = d_sdivzstepu * 8; - tdivz8stepu = d_tdivzstepu * 8; - zi8stepu = d_zistepu * 8; - -// we count on FP exceptions being turned off to avoid range problems - izistep = (int)(d_zistepu * 0x8000 * 0x10000); - - do - { - pdest = (byte *)d_viewbuffer + (screenwidth * pspan->v) + pspan->u; - pz = d_pzbuffer + (d_zwidth * pspan->v) + pspan->u; - - count = pspan->count; - - if (count <= 0) - goto NextSpan; - - // 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 - izi = (int)(zi * 0x8000 * 0x10000); - - 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 - { - btemp = *(pbase + (s >> 16) + (t >> 16) * cachewidth); - if (btemp != 255) - { - if (*pz <= (izi >> 16)) - { - *pz = izi >> 16; - *pdest = btemp; - } - } - - izi += izistep; - pdest++; - pz++; - s += sstep; - t += tstep; - } while (--spancount > 0); - - s = snext; - t = tnext; - - } while (count > 0); - -NextSpan: - pspan++; - - } while (pspan->count != DS_SPAN_LIST_END); -} - -#endif - - -/* -===================== -D_SpriteScanLeftEdge -===================== -*/ -void D_SpriteScanLeftEdge (void) -{ - int i, v, itop, ibottom, lmaxindex; - emitpoint_t *pvert, *pnext; - sspan_t *pspan; - float du, dv, vtop, vbottom, slope; - fixed16_t u, u_step; - - pspan = sprite_spans; - i = minindex; - if (i == 0) - i = r_spritedesc.nump; - - lmaxindex = maxindex; - if (lmaxindex == 0) - lmaxindex = r_spritedesc.nump; - - vtop = ceil (r_spritedesc.pverts[i].v); - - do - { - pvert = &r_spritedesc.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_spritedesc.nump; - - } while (i != lmaxindex); -} - - -/* -===================== -D_SpriteScanRightEdge -===================== -*/ -void D_SpriteScanRightEdge (void) -{ - int i, v, itop, ibottom; - emitpoint_t *pvert, *pnext; - sspan_t *pspan; - float du, dv, vtop, vbottom, slope, uvert, unext, vvert, vnext; - fixed16_t u, u_step; - - pspan = sprite_spans; - i = minindex; - - vvert = r_spritedesc.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 - { - pvert = &r_spritedesc.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_spritedesc.nump) - i = 0; - - } while (i != maxindex); - - pspan->count = DS_SPAN_LIST_END; // mark the end of the span list -} - - -/* -===================== -D_SpriteCalculateGradients -===================== -*/ -void D_SpriteCalculateGradients (void) -{ - vec3_t p_normal, p_saxis, p_taxis, p_temp1; - float distinv; - - TransformVector (r_spritedesc.vpn, p_normal); - TransformVector (r_spritedesc.vright, p_saxis); - TransformVector (r_spritedesc.vup, p_taxis); - VectorInverse (p_taxis); - - distinv = 1.0 / (-DotProduct (modelorg, r_spritedesc.vpn)); - - d_sdivzstepu = p_saxis[0] * xscaleinv; - d_tdivzstepu = p_taxis[0] * xscaleinv; - - d_sdivzstepv = -p_saxis[1] * yscaleinv; - d_tdivzstepv = -p_taxis[1] * yscaleinv; - - d_zistepu = p_normal[0] * xscaleinv * distinv; - d_zistepv = -p_normal[1] * yscaleinv * distinv; - - d_sdivzorigin = p_saxis[2] - xcenter * d_sdivzstepu - - ycenter * d_sdivzstepv; - d_tdivzorigin = p_taxis[2] - xcenter * d_tdivzstepu - - ycenter * d_tdivzstepv; - d_ziorigin = p_normal[2] * distinv - xcenter * d_zistepu - - ycenter * d_zistepv; - - TransformVector (modelorg, p_temp1); - - sadjust = ((fixed16_t)(DotProduct (p_temp1, p_saxis) * 0x10000 + 0.5)) - - (-(cachewidth >> 1) << 16); - tadjust = ((fixed16_t)(DotProduct (p_temp1, p_taxis) * 0x10000 + 0.5)) - - (-(sprite_height >> 1) << 16); - -// -1 (-epsilon) so we never wander off the edge of the texture - bbextents = (cachewidth << 16) - 1; - bbextentt = (sprite_height << 16) - 1; -} - - -/* -===================== -D_DrawSprite -===================== -*/ -void D_DrawSprite (void) -{ - int i, nump; - float ymin, ymax; - emitpoint_t *pverts; - sspan_t spans[MAXHEIGHT+1]; - - sprite_spans = 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_spritedesc.pverts; - - for (i=0 ; iv < ymin) - { - ymin = pverts->v; - minindex = i; - } - - if (pverts->v > ymax) - { - ymax = pverts->v; - maxindex = i; - } - - pverts++; - } - - ymin = ceil (ymin); - ymax = ceil (ymax); - - if (ymin >= ymax) - return; // doesn't cross any scans at all - - cachewidth = r_spritedesc.pspriteframe->width; - sprite_height = r_spritedesc.pspriteframe->height; - cacheblock = (byte *)&r_spritedesc.pspriteframe->pixels[0]; - -// copy the first vertex to the last vertex, so we don't have to deal with -// wrapping - nump = r_spritedesc.nump; - pverts = r_spritedesc.pverts; - pverts[nump] = pverts[0]; - - D_SpriteCalculateGradients (); - D_SpriteScanLeftEdge (); - D_SpriteScanRightEdge (); - D_SpriteDrawSpans (sprite_spans); -} - +/* +Copyright (C) 1996-1997 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_sprite.c: software top-level rasterization driver module for drawing +// sprites + +#include "quakedef.h" +#include "d_local.h" + +static int sprite_height; +static int minindex, maxindex; +static sspan_t *sprite_spans; + +#if !id386 + +/* +===================== +D_SpriteDrawSpans +===================== +*/ +void D_SpriteDrawSpans (sspan_t *pspan) +{ + int count, spancount, izistep; + int izi; + byte *pbase, *pdest; + fixed16_t s, t, snext, tnext, sstep, tstep; + float sdivz, tdivz, zi, z, du, dv, spancountminus1; + float sdivz8stepu, tdivz8stepu, zi8stepu; + byte btemp; + short *pz; + + sstep = 0; // keep compiler happy + tstep = 0; // ditto + + pbase = cacheblock; + + sdivz8stepu = d_sdivzstepu * 8; + tdivz8stepu = d_tdivzstepu * 8; + zi8stepu = d_zistepu * 8; + +// we count on FP exceptions being turned off to avoid range problems + izistep = (int)(d_zistepu * 0x8000 * 0x10000); + + do + { + pdest = (byte *)d_viewbuffer + (screenwidth * pspan->v) + pspan->u; + pz = d_pzbuffer + (d_zwidth * pspan->v) + pspan->u; + + count = pspan->count; + + if (count <= 0) + goto NextSpan; + + // 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 + izi = (int)(zi * 0x8000 * 0x10000); + + 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 + { + btemp = *(pbase + (s >> 16) + (t >> 16) * cachewidth); + if (btemp != 255) + { + if (*pz <= (izi >> 16)) + { + *pz = izi >> 16; + *pdest = btemp; + } + } + + izi += izistep; + pdest++; + pz++; + s += sstep; + t += tstep; + } while (--spancount > 0); + + s = snext; + t = tnext; + + } while (count > 0); + +NextSpan: + pspan++; + + } while (pspan->count != DS_SPAN_LIST_END); +} + +#endif + + +/* +===================== +D_SpriteScanLeftEdge +===================== +*/ +void D_SpriteScanLeftEdge (void) +{ + int i, v, itop, ibottom, lmaxindex; + emitpoint_t *pvert, *pnext; + sspan_t *pspan; + float du, dv, vtop, vbottom, slope; + fixed16_t u, u_step; + + pspan = sprite_spans; + i = minindex; + if (i == 0) + i = r_spritedesc.nump; + + lmaxindex = maxindex; + if (lmaxindex == 0) + lmaxindex = r_spritedesc.nump; + + vtop = ceil (r_spritedesc.pverts[i].v); + + do + { + pvert = &r_spritedesc.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_spritedesc.nump; + + } while (i != lmaxindex); +} + + +/* +===================== +D_SpriteScanRightEdge +===================== +*/ +void D_SpriteScanRightEdge (void) +{ + int i, v, itop, ibottom; + emitpoint_t *pvert, *pnext; + sspan_t *pspan; + float du, dv, vtop, vbottom, slope, uvert, unext, vvert, vnext; + fixed16_t u, u_step; + + pspan = sprite_spans; + i = minindex; + + vvert = r_spritedesc.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 + { + pvert = &r_spritedesc.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_spritedesc.nump) + i = 0; + + } while (i != maxindex); + + pspan->count = DS_SPAN_LIST_END; // mark the end of the span list +} + + +/* +===================== +D_SpriteCalculateGradients +===================== +*/ +void D_SpriteCalculateGradients (void) +{ + vec3_t p_normal, p_saxis, p_taxis, p_temp1; + float distinv; + + TransformVector (r_spritedesc.vpn, p_normal); + TransformVector (r_spritedesc.vright, p_saxis); + TransformVector (r_spritedesc.vup, p_taxis); + VectorInverse (p_taxis); + + distinv = 1.0 / (-DotProduct (modelorg, r_spritedesc.vpn)); + + d_sdivzstepu = p_saxis[0] * xscaleinv; + d_tdivzstepu = p_taxis[0] * xscaleinv; + + d_sdivzstepv = -p_saxis[1] * yscaleinv; + d_tdivzstepv = -p_taxis[1] * yscaleinv; + + d_zistepu = p_normal[0] * xscaleinv * distinv; + d_zistepv = -p_normal[1] * yscaleinv * distinv; + + d_sdivzorigin = p_saxis[2] - xcenter * d_sdivzstepu - + ycenter * d_sdivzstepv; + d_tdivzorigin = p_taxis[2] - xcenter * d_tdivzstepu - + ycenter * d_tdivzstepv; + d_ziorigin = p_normal[2] * distinv - xcenter * d_zistepu - + ycenter * d_zistepv; + + TransformVector (modelorg, p_temp1); + + sadjust = ((fixed16_t)(DotProduct (p_temp1, p_saxis) * 0x10000 + 0.5)) - + (-(cachewidth >> 1) << 16); + tadjust = ((fixed16_t)(DotProduct (p_temp1, p_taxis) * 0x10000 + 0.5)) - + (-(sprite_height >> 1) << 16); + +// -1 (-epsilon) so we never wander off the edge of the texture + bbextents = (cachewidth << 16) - 1; + bbextentt = (sprite_height << 16) - 1; +} + + +/* +===================== +D_DrawSprite +===================== +*/ +void D_DrawSprite (void) +{ + int i, nump; + float ymin, ymax; + emitpoint_t *pverts; + sspan_t spans[MAXHEIGHT+1]; + + sprite_spans = 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_spritedesc.pverts; + + for (i=0 ; iv < ymin) + { + ymin = pverts->v; + minindex = i; + } + + if (pverts->v > ymax) + { + ymax = pverts->v; + maxindex = i; + } + + pverts++; + } + + ymin = ceil (ymin); + ymax = ceil (ymax); + + if (ymin >= ymax) + return; // doesn't cross any scans at all + + cachewidth = r_spritedesc.pspriteframe->width; + sprite_height = r_spritedesc.pspriteframe->height; + cacheblock = (byte *)&r_spritedesc.pspriteframe->pixels[0]; + +// copy the first vertex to the last vertex, so we don't have to deal with +// wrapping + nump = r_spritedesc.nump; + pverts = r_spritedesc.pverts; + pverts[nump] = pverts[0]; + + D_SpriteCalculateGradients (); + D_SpriteScanLeftEdge (); + D_SpriteScanRightEdge (); + D_SpriteDrawSpans (sprite_spans); +} + diff --git a/source/d_surf.c b/source/d_surf.c index 94b71e5d..91aca3b8 100644 --- a/source/d_surf.c +++ b/source/d_surf.c @@ -1,347 +1,347 @@ -/* -Copyright (C) 1996-1997 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_surf.c: rasterization driver surface heap manager - -#include "quakedef.h" -#include "d_local.h" -#include "r_local.h" - -float surfscale; -qboolean r_cache_thrash; // set if surface cache is thrashing - -int sc_size; -surfcache_t *sc_rover, *sc_base; - -#define GUARDSIZE 4 - - -int D_SurfaceCacheForRes (int width, int height) -{ - int size, pix; - - if (COM_CheckParm ("-surfcachesize")) - { - size = Q_atoi(com_argv[COM_CheckParm("-surfcachesize")+1]) * 1024; - return size; - } - - size = SURFCACHE_SIZE_AT_320X200; - - pix = width*height; - if (pix > 64000) - size += (pix-64000)*3; - - - return size; -} - -void D_CheckCacheGuard (void) -{ - byte *s; - int i; - - s = (byte *)sc_base + sc_size; - for (i=0 ; inext = NULL; - sc_base->owner = NULL; - sc_base->size = sc_size; - - D_ClearCacheGuard (); -} - - -/* -================== -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)) - Sys_Error ("D_SCAlloc: bad cache width %d\n", width); - - if ((size <= 0) || (size > 0x10000)) - Sys_Error ("D_SCAlloc: bad cache size %d\n", size); - -#ifdef __alpha__ - size = (int)((long)&((surfcache_t *)0)->data[size]); -#else - size = (int)&((surfcache_t *)0)->data[size]; -#endif - size = (size + 3) & ~3; - if (size > sc_size) - Sys_Error ("D_SCAlloc: %i > cache size",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) - Sys_Error ("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; - } - -D_CheckCacheGuard (); // DEBUG - return new; -} - - -/* -================= -D_SCDump -================= -*/ -void D_SCDump (void) -{ - surfcache_t *test; - - for (test = sc_base ; test ; test = test->next) - { - if (test == sc_rover) - Sys_Printf ("ROVER:\n"); - printf ("%p : %i bytes %i width\n",test, test->size, test->width); - } -} - -//============================================================================= - -// 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.texture = R_TextureAnimation (surface->texinfo->texture); - r_drawsurf.lightadj[0] = d_lightstylevalue[surface->styles[0]]; - r_drawsurf.lightadj[1] = d_lightstylevalue[surface->styles[1]]; - r_drawsurf.lightadj[2] = d_lightstylevalue[surface->styles[2]]; - r_drawsurf.lightadj[3] = d_lightstylevalue[surface->styles[3]]; - -// -// see if the cache holds apropriate data -// - cache = surface->cachespots[miplevel]; - - if (cache && !cache->dlight /*&& surface->dlightframe != r_framecount*/ - && cache->texture == r_drawsurf.texture - && 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] ) - { - if (surface->dlightframe == r_framecount) - // surface was not lit but is (possibly) going to - r_drawsurf.dlightonly = true; - else - return cache; - } else - r_drawsurf.dlightonly = false; - -// -// 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->texture = r_drawsurf.texture; - 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++; - - if (!R_DrawSurface ()) - cache->dlight = 0; - - return surface->cachespots[miplevel]; -} - - +/* +Copyright (C) 1996-1997 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_surf.c: rasterization driver surface heap manager + +#include "quakedef.h" +#include "d_local.h" +#include "r_local.h" + +float surfscale; +qboolean r_cache_thrash; // set if surface cache is thrashing + +int sc_size; +surfcache_t *sc_rover, *sc_base; + +#define GUARDSIZE 4 + + +int D_SurfaceCacheForRes (int width, int height) +{ + int size, pix; + + if (COM_CheckParm ("-surfcachesize")) + { + size = Q_atoi(com_argv[COM_CheckParm("-surfcachesize")+1]) * 1024; + return size; + } + + size = SURFCACHE_SIZE_AT_320X200; + + pix = width*height; + if (pix > 64000) + size += (pix-64000)*3; + + + return size; +} + +void D_CheckCacheGuard (void) +{ + byte *s; + int i; + + s = (byte *)sc_base + sc_size; + for (i=0 ; inext = NULL; + sc_base->owner = NULL; + sc_base->size = sc_size; + + D_ClearCacheGuard (); +} + + +/* +================== +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)) + Sys_Error ("D_SCAlloc: bad cache width %d\n", width); + + if ((size <= 0) || (size > 0x10000)) + Sys_Error ("D_SCAlloc: bad cache size %d\n", size); + +#ifdef __alpha__ + size = (int)((long)&((surfcache_t *)0)->data[size]); +#else + size = (int)&((surfcache_t *)0)->data[size]; +#endif + size = (size + 3) & ~3; + if (size > sc_size) + Sys_Error ("D_SCAlloc: %i > cache size",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) + Sys_Error ("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; + } + +D_CheckCacheGuard (); // DEBUG + return new; +} + + +/* +================= +D_SCDump +================= +*/ +void D_SCDump (void) +{ + surfcache_t *test; + + for (test = sc_base ; test ; test = test->next) + { + if (test == sc_rover) + Sys_Printf ("ROVER:\n"); + printf ("%p : %i bytes %i width\n",test, test->size, test->width); + } +} + +//============================================================================= + +// 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.texture = R_TextureAnimation (surface->texinfo->texture); + r_drawsurf.lightadj[0] = d_lightstylevalue[surface->styles[0]]; + r_drawsurf.lightadj[1] = d_lightstylevalue[surface->styles[1]]; + r_drawsurf.lightadj[2] = d_lightstylevalue[surface->styles[2]]; + r_drawsurf.lightadj[3] = d_lightstylevalue[surface->styles[3]]; + +// +// see if the cache holds apropriate data +// + cache = surface->cachespots[miplevel]; + + if (cache && !cache->dlight /*&& surface->dlightframe != r_framecount*/ + && cache->texture == r_drawsurf.texture + && 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] ) + { + if (surface->dlightframe == r_framecount) + // surface was not lit but is (possibly) going to + r_drawsurf.dlightonly = true; + else + return cache; + } else + r_drawsurf.dlightonly = false; + +// +// 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->texture = r_drawsurf.texture; + 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++; + + if (!R_DrawSurface ()) + cache->dlight = 0; + + return surface->cachespots[miplevel]; +} + + diff --git a/source/d_vars.c b/source/d_vars.c index 38dbf33e..9c9805d6 100644 --- a/source/d_vars.c +++ b/source/d_vars.c @@ -1,50 +1,50 @@ -/* -Copyright (C) 1996-1997 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_vars.c: global refresh variables - -#if !id386 - -#include "quakedef.h" - -// 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; - -fixed16_t sadjust, tadjust, bbextents, bbextentt; - -pixel_t *cacheblock; -int cachewidth; -pixel_t *d_viewbuffer; -short *d_pzbuffer; -unsigned int d_zrowbytes; -unsigned int d_zwidth; - -#endif // !id386 - +/* +Copyright (C) 1996-1997 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_vars.c: global refresh variables + +#if !id386 + +#include "quakedef.h" + +// 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; + +fixed16_t sadjust, tadjust, bbextents, bbextentt; + +pixel_t *cacheblock; +int cachewidth; +pixel_t *d_viewbuffer; +short *d_pzbuffer; +unsigned int d_zrowbytes; +unsigned int d_zwidth; + +#endif // !id386 + diff --git a/source/d_varsa.s b/source/d_varsa.s index 51bc3c98..661067b4 100644 --- a/source/d_varsa.s +++ b/source/d_varsa.s @@ -1,218 +1,218 @@ -/* - d_varsa.S - - (description) - - Copyright (C) 1996-1997 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: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - - $Id: d_varsa.s,v 1.1.1.3 2004/10/13 18:54:32 vvd0 Exp $ -*/ - -#include "asm_i386.h" -#include "quakeasm.h" -#include "asm_draw.h" -#include "d_ifacea.h" - -#ifdef id386 - - .data - -//------------------------------------------------------- -// global refresh variables -//------------------------------------------------------- - -// FIXME: put all refresh variables into one contiguous block. Make into one -// big structure, like cl or sv? - - .align 4 -.globl C(d_sdivzstepu) -.globl C(d_tdivzstepu) -.globl C(d_zistepu) -.globl C(d_sdivzstepv) -.globl C(d_tdivzstepv) -.globl C(d_zistepv) -.globl C(d_sdivzorigin) -.globl C(d_tdivzorigin) -.globl C(d_ziorigin) -C(d_sdivzstepu): .single 0 -C(d_tdivzstepu): .single 0 -C(d_zistepu): .single 0 -C(d_sdivzstepv): .single 0 -C(d_tdivzstepv): .single 0 -C(d_zistepv): .single 0 -C(d_sdivzorigin): .single 0 -C(d_tdivzorigin): .single 0 -C(d_ziorigin): .single 0 - -.globl C(sadjust) -.globl C(tadjust) -.globl C(bbextents) -.globl C(bbextentt) -C(sadjust): .long 0 -C(tadjust): .long 0 -C(bbextents): .long 0 -C(bbextentt): .long 0 - -.globl C(cacheblock) -.globl C(d_viewbuffer) -.globl C(cachewidth) -.globl C(d_pzbuffer) -.globl C(d_zrowbytes) -.globl C(d_zwidth) -C(cacheblock): .long 0 -C(cachewidth): .long 0 -C(d_viewbuffer): .long 0 -C(d_pzbuffer): .long 0 -C(d_zrowbytes): .long 0 -C(d_zwidth): .long 0 - - -//------------------------------------------------------- -// ASM-only variables -//------------------------------------------------------- -.globl izi -izi: .long 0 - -.globl pbase, s, t, sfracf, tfracf, snext, tnext -.globl spancountminus1, zi16stepu, sdivz16stepu, tdivz16stepu -.globl zi8stepu, sdivz8stepu, tdivz8stepu, pz -s: .long 0 -t: .long 0 -snext: .long 0 -tnext: .long 0 -sfracf: .long 0 -tfracf: .long 0 -pbase: .long 0 -zi8stepu: .long 0 -sdivz8stepu: .long 0 -tdivz8stepu: .long 0 -zi16stepu: .long 0 -sdivz16stepu: .long 0 -tdivz16stepu: .long 0 -spancountminus1: .long 0 -pz: .long 0 - -.globl izistep -izistep: .long 0 - -//------------------------------------------------------- -// local variables for d_draw16.s -//------------------------------------------------------- - -.globl reciprocal_table_16, entryvec_table_16 -// 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, and 1/15 in 0.32 form -reciprocal_table_16: .long 0x40000000, 0x2aaaaaaa, 0x20000000 - .long 0x19999999, 0x15555555, 0x12492492 - .long 0x10000000, 0xe38e38e, 0xccccccc, 0xba2e8ba - .long 0xaaaaaaa, 0x9d89d89, 0x9249249, 0x8888888 - -#ifndef NeXT - .extern Entry2_16 - .extern Entry3_16 - .extern Entry4_16 - .extern Entry5_16 - .extern Entry6_16 - .extern Entry7_16 - .extern Entry8_16 - .extern Entry9_16 - .extern Entry10_16 - .extern Entry11_16 - .extern Entry12_16 - .extern Entry13_16 - .extern Entry14_16 - .extern Entry15_16 - .extern Entry16_16 -#endif - -entryvec_table_16: .long 0, Entry2_16, Entry3_16, Entry4_16 - .long Entry5_16, Entry6_16, Entry7_16, Entry8_16 - .long Entry9_16, Entry10_16, Entry11_16, Entry12_16 - .long Entry13_16, Entry14_16, Entry15_16, Entry16_16 - -//------------------------------------------------------- -// local variables for d_parta.s -//------------------------------------------------------- -.globl DP_Count, DP_u, DP_v, DP_32768, DP_Color, DP_Pix, DP_EntryTable -DP_Count: .long 0 -DP_u: .long 0 -DP_v: .long 0 -DP_32768: .single 32768.0 -DP_Color: .long 0 -DP_Pix: .long 0 - - -#ifndef NeXT - .extern DP_1x1 - .extern DP_2x2 - .extern DP_3x3 - .extern DP_4x4 -#endif - -DP_EntryTable: .long DP_1x1, DP_2x2, DP_3x3, DP_4x4 - -// -// advancetable is 8 bytes, but points to the middle of that range so negative -// offsets will work -// -.globl advancetable, sstep, tstep, pspantemp, counttemp, jumptemp -advancetable: .long 0, 0 -sstep: .long 0 -tstep: .long 0 - -pspantemp: .long 0 -counttemp: .long 0 -jumptemp: .long 0 - -// 1/2, 1/3, 1/4, 1/5, 1/6, and 1/7 in 0.32 form -.globl reciprocal_table, entryvec_table -reciprocal_table: .long 0x40000000, 0x2aaaaaaa, 0x20000000 - .long 0x19999999, 0x15555555, 0x12492492 - -#ifndef NeXT - .extern Entry2_8 - .extern Entry3_8 - .extern Entry4_8 - .extern Entry5_8 - .extern Entry6_8 - .extern Entry7_8 - .extern Entry8_8 -#endif - -entryvec_table: .long 0, Entry2_8, Entry3_8, Entry4_8 - .long Entry5_8, Entry6_8, Entry7_8, Entry8_8 - -#ifndef NeXT - .extern Spr8Entry2_8 - .extern Spr8Entry3_8 - .extern Spr8Entry4_8 - .extern Spr8Entry5_8 - .extern Spr8Entry6_8 - .extern Spr8Entry7_8 - .extern Spr8Entry8_8 -#endif - -.globl spr8entryvec_table -spr8entryvec_table: .long 0, Spr8Entry2_8, Spr8Entry3_8, Spr8Entry4_8 - .long Spr8Entry5_8, Spr8Entry6_8, Spr8Entry7_8, Spr8Entry8_8 - -#endif // id386 - +/* + d_varsa.S + + (description) + + Copyright (C) 1996-1997 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id: d_varsa.s,v 1.1.1.4 2004/10/18 17:44:28 vvd0 Exp $ +*/ + +#include "asm_i386.h" +#include "quakeasm.h" +#include "asm_draw.h" +#include "d_ifacea.h" + +#ifdef id386 + + .data + +//------------------------------------------------------- +// global refresh variables +//------------------------------------------------------- + +// FIXME: put all refresh variables into one contiguous block. Make into one +// big structure, like cl or sv? + + .align 4 +.globl C(d_sdivzstepu) +.globl C(d_tdivzstepu) +.globl C(d_zistepu) +.globl C(d_sdivzstepv) +.globl C(d_tdivzstepv) +.globl C(d_zistepv) +.globl C(d_sdivzorigin) +.globl C(d_tdivzorigin) +.globl C(d_ziorigin) +C(d_sdivzstepu): .single 0 +C(d_tdivzstepu): .single 0 +C(d_zistepu): .single 0 +C(d_sdivzstepv): .single 0 +C(d_tdivzstepv): .single 0 +C(d_zistepv): .single 0 +C(d_sdivzorigin): .single 0 +C(d_tdivzorigin): .single 0 +C(d_ziorigin): .single 0 + +.globl C(sadjust) +.globl C(tadjust) +.globl C(bbextents) +.globl C(bbextentt) +C(sadjust): .long 0 +C(tadjust): .long 0 +C(bbextents): .long 0 +C(bbextentt): .long 0 + +.globl C(cacheblock) +.globl C(d_viewbuffer) +.globl C(cachewidth) +.globl C(d_pzbuffer) +.globl C(d_zrowbytes) +.globl C(d_zwidth) +C(cacheblock): .long 0 +C(cachewidth): .long 0 +C(d_viewbuffer): .long 0 +C(d_pzbuffer): .long 0 +C(d_zrowbytes): .long 0 +C(d_zwidth): .long 0 + + +//------------------------------------------------------- +// ASM-only variables +//------------------------------------------------------- +.globl izi +izi: .long 0 + +.globl pbase, s, t, sfracf, tfracf, snext, tnext +.globl spancountminus1, zi16stepu, sdivz16stepu, tdivz16stepu +.globl zi8stepu, sdivz8stepu, tdivz8stepu, pz +s: .long 0 +t: .long 0 +snext: .long 0 +tnext: .long 0 +sfracf: .long 0 +tfracf: .long 0 +pbase: .long 0 +zi8stepu: .long 0 +sdivz8stepu: .long 0 +tdivz8stepu: .long 0 +zi16stepu: .long 0 +sdivz16stepu: .long 0 +tdivz16stepu: .long 0 +spancountminus1: .long 0 +pz: .long 0 + +.globl izistep +izistep: .long 0 + +//------------------------------------------------------- +// local variables for d_draw16.s +//------------------------------------------------------- + +.globl reciprocal_table_16, entryvec_table_16 +// 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, and 1/15 in 0.32 form +reciprocal_table_16: .long 0x40000000, 0x2aaaaaaa, 0x20000000 + .long 0x19999999, 0x15555555, 0x12492492 + .long 0x10000000, 0xe38e38e, 0xccccccc, 0xba2e8ba + .long 0xaaaaaaa, 0x9d89d89, 0x9249249, 0x8888888 + +#ifndef NeXT + .extern Entry2_16 + .extern Entry3_16 + .extern Entry4_16 + .extern Entry5_16 + .extern Entry6_16 + .extern Entry7_16 + .extern Entry8_16 + .extern Entry9_16 + .extern Entry10_16 + .extern Entry11_16 + .extern Entry12_16 + .extern Entry13_16 + .extern Entry14_16 + .extern Entry15_16 + .extern Entry16_16 +#endif + +entryvec_table_16: .long 0, Entry2_16, Entry3_16, Entry4_16 + .long Entry5_16, Entry6_16, Entry7_16, Entry8_16 + .long Entry9_16, Entry10_16, Entry11_16, Entry12_16 + .long Entry13_16, Entry14_16, Entry15_16, Entry16_16 + +//------------------------------------------------------- +// local variables for d_parta.s +//------------------------------------------------------- +.globl DP_Count, DP_u, DP_v, DP_32768, DP_Color, DP_Pix, DP_EntryTable +DP_Count: .long 0 +DP_u: .long 0 +DP_v: .long 0 +DP_32768: .single 32768.0 +DP_Color: .long 0 +DP_Pix: .long 0 + + +#ifndef NeXT + .extern DP_1x1 + .extern DP_2x2 + .extern DP_3x3 + .extern DP_4x4 +#endif + +DP_EntryTable: .long DP_1x1, DP_2x2, DP_3x3, DP_4x4 + +// +// advancetable is 8 bytes, but points to the middle of that range so negative +// offsets will work +// +.globl advancetable, sstep, tstep, pspantemp, counttemp, jumptemp +advancetable: .long 0, 0 +sstep: .long 0 +tstep: .long 0 + +pspantemp: .long 0 +counttemp: .long 0 +jumptemp: .long 0 + +// 1/2, 1/3, 1/4, 1/5, 1/6, and 1/7 in 0.32 form +.globl reciprocal_table, entryvec_table +reciprocal_table: .long 0x40000000, 0x2aaaaaaa, 0x20000000 + .long 0x19999999, 0x15555555, 0x12492492 + +#ifndef NeXT + .extern Entry2_8 + .extern Entry3_8 + .extern Entry4_8 + .extern Entry5_8 + .extern Entry6_8 + .extern Entry7_8 + .extern Entry8_8 +#endif + +entryvec_table: .long 0, Entry2_8, Entry3_8, Entry4_8 + .long Entry5_8, Entry6_8, Entry7_8, Entry8_8 + +#ifndef NeXT + .extern Spr8Entry2_8 + .extern Spr8Entry3_8 + .extern Spr8Entry4_8 + .extern Spr8Entry5_8 + .extern Spr8Entry6_8 + .extern Spr8Entry7_8 + .extern Spr8Entry8_8 +#endif + +.globl spr8entryvec_table +spr8entryvec_table: .long 0, Spr8Entry2_8, Spr8Entry3_8, Spr8Entry4_8 + .long Spr8Entry5_8, Spr8Entry6_8, Spr8Entry7_8, Spr8Entry8_8 + +#endif // id386 + diff --git a/source/d_zpoint.c b/source/d_zpoint.c index 4c38acf7..909f2501 100644 --- a/source/d_zpoint.c +++ b/source/d_zpoint.c @@ -1,47 +1,47 @@ -/* -Copyright (C) 1996-1997 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_zpoint.c: software driver module for drawing z-buffered points - -#include "quakedef.h" -#include "d_local.h" - - -/* -===================== -D_DrawZPoint -===================== -*/ -void D_DrawZPoint (void) -{ - byte *pdest; - short *pz; - int izi; - - pz = d_pzbuffer + (d_zwidth * r_zpointdesc.v) + r_zpointdesc.u; - pdest = d_viewbuffer + d_scantable[r_zpointdesc.v] + r_zpointdesc.u; - izi = (int)(r_zpointdesc.zi * 0x8000); - - if (*pz <= izi) - { - *pz = izi; - *pdest = r_zpointdesc.color; - } -} - +/* +Copyright (C) 1996-1997 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_zpoint.c: software driver module for drawing z-buffered points + +#include "quakedef.h" +#include "d_local.h" + + +/* +===================== +D_DrawZPoint +===================== +*/ +void D_DrawZPoint (void) +{ + byte *pdest; + short *pz; + int izi; + + pz = d_pzbuffer + (d_zwidth * r_zpointdesc.v) + r_zpointdesc.u; + pdest = d_viewbuffer + d_scantable[r_zpointdesc.v] + r_zpointdesc.u; + izi = (int)(r_zpointdesc.zi * 0x8000); + + if (*pz <= izi) + { + *pz = izi; + *pdest = r_zpointdesc.color; + } +} + diff --git a/source/draw.c b/source/draw.c index ec87e5e6..11335a26 100644 --- a/source/draw.c +++ b/source/draw.c @@ -1,1026 +1,1026 @@ -/* -Copyright (C) 1996-1997 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 -- this is the only file outside the refresh that touches the -// vid buffer - -#include "quakedef.h" -#include "sound.h" -#include "version.h" - -typedef struct { - vrect_t rect; - int width; - int height; - byte *ptexbytes; - int rowbytes; -} rectdesc_t; - -static rectdesc_t r_rectdesc; - -byte *draw_chars; // 8*8 graphic characters -qpic_t *draw_disc; -qpic_t *draw_backtile; - -//============================================================================= -/* Support Routines */ - -typedef struct cachepic_s -{ - char name[MAX_QPATH]; - cache_user_t cache; -} cachepic_t; - -#define MAX_CACHED_PICS 128 -cachepic_t menu_cachepics[MAX_CACHED_PICS]; -int menu_numcachepics; - - -qpic_t *Draw_PicFromWad (char *name) -{ - return W_GetLumpName (name); -} - -/* -================ -Draw_CachePic -================ -*/ -qpic_t *Draw_CachePic (char *path) -{ - cachepic_t *pic; - int i; - qpic_t *dat; - - for (pic=menu_cachepics, i=0 ; iname)) - break; - - if (i == menu_numcachepics) - { - if (menu_numcachepics == MAX_CACHED_PICS) - Sys_Error ("menu_numcachepics == MAX_CACHED_PICS"); - menu_numcachepics++; - strcpy (pic->name, path); - } - - dat = Cache_Check (&pic->cache); - - if (dat) - return dat; - -// -// load the pic from disk -// - COM_LoadCacheFile (path, &pic->cache); - - dat = (qpic_t *)pic->cache.data; - if (!dat) - { - Sys_Error ("Draw_CachePic: failed to load %s", path); - } - - SwapPic (dat); - - return dat; -} - - - -/* -=============== -Draw_Init -=============== -*/ -void Draw_Init (void) -{ - draw_chars = (byte *) Draw_PicFromWad ("conchars"); - draw_disc = Draw_PicFromWad ("disc"); - draw_backtile = Draw_PicFromWad ("backtile"); - - r_rectdesc.width = draw_backtile->width; - r_rectdesc.height = draw_backtile->height; - r_rectdesc.ptexbytes = draw_backtile->data; - r_rectdesc.rowbytes = draw_backtile->width; -} - - - -/* -================ -Draw_Character - -Draws one 8*8 graphics character with 0 being transparent. -It can be clipped to the top of the screen to allow the console to be -smoothly scrolled off. -================ -*/ -void Draw_Character (int x, int y, int num) -{ - byte *dest; - byte *source; - unsigned short *pusdest; - int drawline; - int row, col; - - num &= 255; - - if (y <= -8) - return; // totally off screen - - if (y > vid.height - 8 || x < 0 || x > vid.width - 8) - return; - if (num < 0 || num > 255) - return; - - row = num>>4; - col = num&15; - source = draw_chars + (row<<10) + (col<<3); - - if (y < 0) - { // clipped - drawline = 8 + y; - source -= 128*y; - y = 0; - } - else - drawline = 8; - - - if (r_pixbytes == 1) - { - dest = vid.conbuffer + y*vid.conrowbytes + x; - - while (drawline--) - { - if (source[0]) - dest[0] = source[0]; - if (source[1]) - dest[1] = source[1]; - if (source[2]) - dest[2] = source[2]; - if (source[3]) - dest[3] = source[3]; - if (source[4]) - dest[4] = source[4]; - if (source[5]) - dest[5] = source[5]; - if (source[6]) - dest[6] = source[6]; - if (source[7]) - dest[7] = source[7]; - source += 128; - dest += vid.conrowbytes; - } - } - else - { - // FIXME: pre-expand to native format? - pusdest = (unsigned short *) - ((byte *)vid.conbuffer + y*vid.conrowbytes + (x<<1)); - - while (drawline--) - { - if (source[0]) - pusdest[0] = d_8to16table[source[0]]; - if (source[1]) - pusdest[1] = d_8to16table[source[1]]; - if (source[2]) - pusdest[2] = d_8to16table[source[2]]; - if (source[3]) - pusdest[3] = d_8to16table[source[3]]; - if (source[4]) - pusdest[4] = d_8to16table[source[4]]; - if (source[5]) - pusdest[5] = d_8to16table[source[5]]; - if (source[6]) - pusdest[6] = d_8to16table[source[6]]; - if (source[7]) - pusdest[7] = d_8to16table[source[7]]; - - source += 128; - pusdest += (vid.conrowbytes >> 1); - } - } -} - -/* -================ -Draw_String -================ -*/ -void Draw_String (int x, int y, char *str) -{ - while (*str) - { - Draw_Character (x, y, *str); - str++; - x += 8; - } -} - -/* -================ -Draw_Alt_String -================ -*/ -void Draw_Alt_String (int x, int y, char *str) -{ - while (*str) - { - Draw_Character (x, y, (*str) | 0x80); - str++; - x += 8; - } -} - -void Draw_Pixel(int x, int y, byte color) -{ - byte *dest; - unsigned short *pusdest; - - if (r_pixbytes == 1) - { - dest = vid.conbuffer + y*vid.conrowbytes + x; - *dest = color; - } - else - { - // FIXME: pre-expand to native format? - pusdest = (unsigned short *) - ((byte *)vid.conbuffer + y*vid.conrowbytes + (x<<1)); - *pusdest = d_8to16table[color]; - } -} - -void Draw_Crosshair(void) -{ - int x, y; - extern cvar_t crosshair, cl_crossx, cl_crossy, crosshaircolor; - extern vrect_t scr_vrect; - byte c = (byte)crosshaircolor.value; - - if (crosshair.value == 2) { - x = scr_vrect.x + scr_vrect.width/2 + cl_crossx.value; - y = scr_vrect.y + scr_vrect.height/2 + cl_crossy.value; - Draw_Pixel(x - 1, y, c); - Draw_Pixel(x - 3, y, c); - Draw_Pixel(x + 1, y, c); - Draw_Pixel(x + 3, y, c); - Draw_Pixel(x, y - 1, c); - Draw_Pixel(x, y - 3, c); - Draw_Pixel(x, y + 1, c); - Draw_Pixel(x, y + 3, c); -// Tonik --> - } else if (crosshair.value == 3) { - x = scr_vrect.x + scr_vrect.width/2 + cl_crossx.value; - y = scr_vrect.y + scr_vrect.height/2 + cl_crossy.value; - Draw_Pixel(x, y, c); - Draw_Pixel(x - 1, y, c); - Draw_Pixel(x + 1, y, c); - Draw_Pixel(x, y - 1, c); - Draw_Pixel(x, y + 1, c); -// <-- Tonik - } else if (crosshair.value) - Draw_Character ( - scr_vrect.x + scr_vrect.width/2-4 + cl_crossx.value, - scr_vrect.y + scr_vrect.height/2-4 + cl_crossy.value, - '+'); -} - -/* -================ -Draw_DebugChar - -Draws a single character directly to the upper right corner of the screen. -This is for debugging lockups by drawing different chars in different parts -of the code. -================ -*/ -void Draw_DebugChar (char num) -{ - byte *dest; - byte *source; - int drawline; - extern byte *draw_chars; - int row, col; - - if (!vid.direct) - return; // don't have direct FB access, so no debugchars... - - drawline = 8; - - row = num>>4; - col = num&15; - source = draw_chars + (row<<10) + (col<<3); - - dest = vid.direct + 312; - - while (drawline--) - { - dest[0] = source[0]; - dest[1] = source[1]; - dest[2] = source[2]; - dest[3] = source[3]; - dest[4] = source[4]; - dest[5] = source[5]; - dest[6] = source[6]; - dest[7] = source[7]; - source += 128; - dest += 320; - } -} - -/* -============= -Draw_Pic -============= -*/ -void Draw_Pic (int x, int y, qpic_t *pic) -{ - byte *dest, *source; - unsigned short *pusdest; - int v, u; - - if ((x < 0) || - (x + pic->width > vid.width) || - (y < 0) || - (y + pic->height > vid.height)) - { - Sys_Error ("Draw_Pic: bad coordinates"); - } - - source = pic->data; - - if (r_pixbytes == 1) - { - dest = vid.buffer + y * vid.rowbytes + x; - - for (v=0 ; vheight ; v++) - { - memcpy (dest, source, pic->width); - dest += vid.rowbytes; - source += pic->width; - } - } - else - { - // FIXME: pretranslate at load time? - pusdest = (unsigned short *)vid.buffer + y * (vid.rowbytes >> 1) + x; - - for (v=0 ; vheight ; v++) - { - for (u=0 ; uwidth ; u++) - { - pusdest[u] = d_8to16table[source[u]]; - } - - pusdest += vid.rowbytes >> 1; - source += pic->width; - } - } -} - - -/* -============= -Draw_SubPic -============= -*/ -void Draw_SubPic(int x, int y, qpic_t *pic, int srcx, int srcy, int width, int height) -{ - byte *dest, *source; - unsigned short *pusdest; - int v, u; - - if ((x < 0) || - (x + width > vid.width) || - (y < 0) || - (y + height > vid.height)) - { - Sys_Error ("Draw_Pic: bad coordinates"); - } - - source = pic->data + srcy * pic->width + srcx; - - if (r_pixbytes == 1) - { - dest = vid.buffer + y * vid.rowbytes + x; - - for (v=0 ; vwidth; - } - } - else - { - // FIXME: pretranslate at load time? - pusdest = (unsigned short *)vid.buffer + y * (vid.rowbytes >> 1) + x; - - for (v=0 ; v> 1; - source += pic->width; - } - } -} - - -/* -============= -Draw_TransPic -============= -*/ -void Draw_TransPic (int x, int y, qpic_t *pic) -{ - byte *dest, *source, tbyte; - unsigned short *pusdest; - int v, u; - - if (x < 0 || (unsigned)(x + pic->width) > vid.width || y < 0 || - (unsigned)(y + pic->height) > vid.height) - { - Sys_Error ("Draw_TransPic: bad coordinates"); - } - - source = pic->data; - - if (r_pixbytes == 1) - { - dest = vid.buffer + y * vid.rowbytes + x; - - if (pic->width & 7) - { // general - for (v=0 ; vheight ; v++) - { - for (u=0 ; uwidth ; u++) - if ( (tbyte=source[u]) != TRANSPARENT_COLOR) - dest[u] = tbyte; - - dest += vid.rowbytes; - source += pic->width; - } - } - else - { // unwound - for (v=0 ; vheight ; v++) - { - for (u=0 ; uwidth ; u+=8) - { - if ( (tbyte=source[u]) != TRANSPARENT_COLOR) - dest[u] = tbyte; - if ( (tbyte=source[u+1]) != TRANSPARENT_COLOR) - dest[u+1] = tbyte; - if ( (tbyte=source[u+2]) != TRANSPARENT_COLOR) - dest[u+2] = tbyte; - if ( (tbyte=source[u+3]) != TRANSPARENT_COLOR) - dest[u+3] = tbyte; - if ( (tbyte=source[u+4]) != TRANSPARENT_COLOR) - dest[u+4] = tbyte; - if ( (tbyte=source[u+5]) != TRANSPARENT_COLOR) - dest[u+5] = tbyte; - if ( (tbyte=source[u+6]) != TRANSPARENT_COLOR) - dest[u+6] = tbyte; - if ( (tbyte=source[u+7]) != TRANSPARENT_COLOR) - dest[u+7] = tbyte; - } - dest += vid.rowbytes; - source += pic->width; - } - } - } - else - { - // FIXME: pretranslate at load time? - pusdest = (unsigned short *)vid.buffer + y * (vid.rowbytes >> 1) + x; - - for (v=0 ; vheight ; v++) - { - for (u=0 ; uwidth ; u++) - { - tbyte = source[u]; - - if (tbyte != TRANSPARENT_COLOR) - { - pusdest[u] = d_8to16table[tbyte]; - } - } - - pusdest += vid.rowbytes >> 1; - source += pic->width; - } - } -} - - -/* -============= -Draw_TransPicTranslate -============= -*/ -void Draw_TransPicTranslate (int x, int y, qpic_t *pic, byte *translation) -{ - byte *dest, *source, tbyte; - unsigned short *pusdest; - int v, u; - - if (x < 0 || (unsigned)(x + pic->width) > vid.width || y < 0 || - (unsigned)(y + pic->height) > vid.height) - { - Sys_Error ("Draw_TransPic: bad coordinates"); - } - - source = pic->data; - - if (r_pixbytes == 1) - { - dest = vid.buffer + y * vid.rowbytes + x; - - if (pic->width & 7) - { // general - for (v=0 ; vheight ; v++) - { - for (u=0 ; uwidth ; u++) - if ( (tbyte=source[u]) != TRANSPARENT_COLOR) - dest[u] = translation[tbyte]; - - dest += vid.rowbytes; - source += pic->width; - } - } - else - { // unwound - for (v=0 ; vheight ; v++) - { - for (u=0 ; uwidth ; u+=8) - { - if ( (tbyte=source[u]) != TRANSPARENT_COLOR) - dest[u] = translation[tbyte]; - if ( (tbyte=source[u+1]) != TRANSPARENT_COLOR) - dest[u+1] = translation[tbyte]; - if ( (tbyte=source[u+2]) != TRANSPARENT_COLOR) - dest[u+2] = translation[tbyte]; - if ( (tbyte=source[u+3]) != TRANSPARENT_COLOR) - dest[u+3] = translation[tbyte]; - if ( (tbyte=source[u+4]) != TRANSPARENT_COLOR) - dest[u+4] = translation[tbyte]; - if ( (tbyte=source[u+5]) != TRANSPARENT_COLOR) - dest[u+5] = translation[tbyte]; - if ( (tbyte=source[u+6]) != TRANSPARENT_COLOR) - dest[u+6] = translation[tbyte]; - if ( (tbyte=source[u+7]) != TRANSPARENT_COLOR) - dest[u+7] = translation[tbyte]; - } - dest += vid.rowbytes; - source += pic->width; - } - } - } - else - { - // FIXME: pretranslate at load time? - pusdest = (unsigned short *)vid.buffer + y * (vid.rowbytes >> 1) + x; - - for (v=0 ; vheight ; v++) - { - for (u=0 ; uwidth ; u++) - { - tbyte = source[u]; - - if (tbyte != TRANSPARENT_COLOR) - { - pusdest[u] = d_8to16table[tbyte]; - } - } - - pusdest += vid.rowbytes >> 1; - source += pic->width; - } - } -} - - -void Draw_CharToConback (int num, byte *dest) -{ - int row, col; - byte *source; - int drawline; - int x; - - row = num>>4; - col = num&15; - source = draw_chars + (row<<10) + (col<<3); - - drawline = 8; - - while (drawline--) - { - for (x=0 ; x<8 ; x++) - if (source[x]) - dest[x] = 0x60 + source[x]; - source += 128; - dest += 320; - } - -} - -/* -================ -Draw_ConsoleBackground - -================ -*/ -void Draw_ConsoleBackground (int lines) -{ - int x, y, v; - byte *src, *dest; - unsigned short *pusdest; - int f, fstep; - qpic_t *conback; - char ver[100]; - static char saveback[320*8]; - - conback = Draw_CachePic ("gfx/conback.lmp"); - -// hack the version number directly into the pic - - if (cls.download) { - strcpy (ver, QWE_VERSION); - dest = conback->data + 320 + 320*186 - 11 - 8*strlen(ver); - } else { - sprintf (ver, "QWExtended client %s", QWE_VERSION); - dest = conback->data + 320 - (strlen(ver)*8 + 11) + 320*186; - } - - memcpy(saveback, conback->data + 320*186, 320*8); - for (x=0 ; xdata + v*320; - if (vid.conwidth == 320) - memcpy (dest, src, vid.conwidth); - else - { - f = 0; - fstep = 320*0x10000/vid.conwidth; - for (x=0 ; x>16]; - f += fstep; - dest[x+1] = src[f>>16]; - f += fstep; - dest[x+2] = src[f>>16]; - f += fstep; - dest[x+3] = src[f>>16]; - f += fstep; - } - } - } - } - else - { - pusdest = (unsigned short *)vid.conbuffer; - - for (y=0 ; y> 1)) - { - // FIXME: pre-expand to native format? - // FIXME: does the endian switching go away in production? - v = (vid.conheight - lines + y)*200/vid.conheight; - src = conback->data + v*320; - f = 0; - fstep = 320*0x10000/vid.conwidth; - for (x=0 ; x>16]]; - f += fstep; - pusdest[x+1] = d_8to16table[src[f>>16]]; - f += fstep; - pusdest[x+2] = d_8to16table[src[f>>16]]; - f += fstep; - pusdest[x+3] = d_8to16table[src[f>>16]]; - f += fstep; - } - } - } - // put it back - memcpy(conback->data + 320*186, saveback, 320*8); -} - - -/* -============== -R_DrawRect8 -============== -*/ -void R_DrawRect8 (vrect_t *prect, int rowbytes, byte *psrc, - int transparent) -{ - byte t; - int i, j, srcdelta, destdelta; - byte *pdest; - - pdest = vid.buffer + (prect->y * vid.rowbytes) + prect->x; - - srcdelta = rowbytes - prect->width; - destdelta = vid.rowbytes - prect->width; - - if (transparent) - { - for (i=0 ; iheight ; i++) - { - for (j=0 ; jwidth ; j++) - { - t = *psrc; - if (t != TRANSPARENT_COLOR) - { - *pdest = t; - } - - psrc++; - pdest++; - } - - psrc += srcdelta; - pdest += destdelta; - } - } - else - { - for (i=0 ; iheight ; i++) - { - memcpy (pdest, psrc, prect->width); - psrc += rowbytes; - pdest += vid.rowbytes; - } - } -} - - -/* -============== -R_DrawRect16 -============== -*/ -void R_DrawRect16 (vrect_t *prect, int rowbytes, byte *psrc, - int transparent) -{ - byte t; - int i, j, srcdelta, destdelta; - unsigned short *pdest; - -// FIXME: would it be better to pre-expand native-format versions? - - pdest = (unsigned short *)vid.buffer + - (prect->y * (vid.rowbytes >> 1)) + prect->x; - - srcdelta = rowbytes - prect->width; - destdelta = (vid.rowbytes >> 1) - prect->width; - - if (transparent) - { - for (i=0 ; iheight ; i++) - { - for (j=0 ; jwidth ; j++) - { - t = *psrc; - if (t != TRANSPARENT_COLOR) - { - *pdest = d_8to16table[t]; - } - - psrc++; - pdest++; - } - - psrc += srcdelta; - pdest += destdelta; - } - } - else - { - for (i=0 ; iheight ; i++) - { - for (j=0 ; jwidth ; j++) - { - *pdest = d_8to16table[*psrc]; - psrc++; - pdest++; - } - - psrc += srcdelta; - pdest += destdelta; - } - } -} - - -/* -============= -Draw_TileClear - -This repeats a 64*64 tile graphic to fill the screen around a sized down -refresh window. -============= -*/ -void Draw_TileClear (int x, int y, int w, int h) -{ - int width, height, tileoffsetx, tileoffsety; - byte *psrc; - vrect_t vr; - - r_rectdesc.rect.x = x; - r_rectdesc.rect.y = y; - r_rectdesc.rect.width = w; - r_rectdesc.rect.height = h; - - vr.y = r_rectdesc.rect.y; - height = r_rectdesc.rect.height; - - tileoffsety = vr.y % r_rectdesc.height; - - while (height > 0) - { - vr.x = r_rectdesc.rect.x; - width = r_rectdesc.rect.width; - - if (tileoffsety != 0) - vr.height = r_rectdesc.height - tileoffsety; - else - vr.height = r_rectdesc.height; - - if (vr.height > height) - vr.height = height; - - tileoffsetx = vr.x % r_rectdesc.width; - - while (width > 0) - { - if (tileoffsetx != 0) - vr.width = r_rectdesc.width - tileoffsetx; - else - vr.width = r_rectdesc.width; - - if (vr.width > width) - vr.width = width; - - psrc = r_rectdesc.ptexbytes + - (tileoffsety * r_rectdesc.rowbytes) + tileoffsetx; - - if (r_pixbytes == 1) - { - R_DrawRect8 (&vr, r_rectdesc.rowbytes, psrc, 0); - } - else - { - R_DrawRect16 (&vr, r_rectdesc.rowbytes, psrc, 0); - } - - vr.x += vr.width; - width -= vr.width; - tileoffsetx = 0; // only the left tile can be left-clipped - } - - vr.y += vr.height; - height -= vr.height; - tileoffsety = 0; // only the top tile can be top-clipped - } -} - - -/* -============= -Draw_Fill - -Fills a box of pixels with a single color -============= -*/ -void Draw_Fill (int x, int y, int w, int h, int c) -{ - byte *dest; - unsigned short *pusdest; - unsigned uc; - int u, v; - - if (x < 0 || x + w > vid.width || - y < 0 || y + h > vid.height) { - Con_Printf("Bad Draw_Fill(%d, %d, %d, %d, %c)\n", - x, y, w, h, c); - return; - } - - if (r_pixbytes == 1) - { - dest = vid.buffer + y*vid.rowbytes + x; - for (v=0 ; v> 1) + x; - for (v=0 ; v> 1)) - for (u=0 ; udata, 24, 24); -} - - -/* -================ -Draw_EndDisc - -Erases the disc icon. -Call after completing any disc IO -================ -*/ -void Draw_EndDisc (void) -{ - - D_EndDirectRect (vid.width - 24, 0, 24, 24); -} - +/* +Copyright (C) 1996-1997 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 -- this is the only file outside the refresh that touches the +// vid buffer + +#include "quakedef.h" +#include "sound.h" +#include "version.h" + +typedef struct { + vrect_t rect; + int width; + int height; + byte *ptexbytes; + int rowbytes; +} rectdesc_t; + +static rectdesc_t r_rectdesc; + +byte *draw_chars; // 8*8 graphic characters +qpic_t *draw_disc; +qpic_t *draw_backtile; + +//============================================================================= +/* Support Routines */ + +typedef struct cachepic_s +{ + char name[MAX_QPATH]; + cache_user_t cache; +} cachepic_t; + +#define MAX_CACHED_PICS 128 +cachepic_t menu_cachepics[MAX_CACHED_PICS]; +int menu_numcachepics; + + +qpic_t *Draw_PicFromWad (char *name) +{ + return W_GetLumpName (name); +} + +/* +================ +Draw_CachePic +================ +*/ +qpic_t *Draw_CachePic (char *path) +{ + cachepic_t *pic; + int i; + qpic_t *dat; + + for (pic=menu_cachepics, i=0 ; iname)) + break; + + if (i == menu_numcachepics) + { + if (menu_numcachepics == MAX_CACHED_PICS) + Sys_Error ("menu_numcachepics == MAX_CACHED_PICS"); + menu_numcachepics++; + strcpy (pic->name, path); + } + + dat = Cache_Check (&pic->cache); + + if (dat) + return dat; + +// +// load the pic from disk +// + COM_LoadCacheFile (path, &pic->cache); + + dat = (qpic_t *)pic->cache.data; + if (!dat) + { + Sys_Error ("Draw_CachePic: failed to load %s", path); + } + + SwapPic (dat); + + return dat; +} + + + +/* +=============== +Draw_Init +=============== +*/ +void Draw_Init (void) +{ + draw_chars = (byte *) Draw_PicFromWad ("conchars"); + draw_disc = Draw_PicFromWad ("disc"); + draw_backtile = Draw_PicFromWad ("backtile"); + + r_rectdesc.width = draw_backtile->width; + r_rectdesc.height = draw_backtile->height; + r_rectdesc.ptexbytes = draw_backtile->data; + r_rectdesc.rowbytes = draw_backtile->width; +} + + + +/* +================ +Draw_Character + +Draws one 8*8 graphics character with 0 being transparent. +It can be clipped to the top of the screen to allow the console to be +smoothly scrolled off. +================ +*/ +void Draw_Character (int x, int y, int num) +{ + byte *dest; + byte *source; + unsigned short *pusdest; + int drawline; + int row, col; + + num &= 255; + + if (y <= -8) + return; // totally off screen + + if (y > vid.height - 8 || x < 0 || x > vid.width - 8) + return; + if (num < 0 || num > 255) + return; + + row = num>>4; + col = num&15; + source = draw_chars + (row<<10) + (col<<3); + + if (y < 0) + { // clipped + drawline = 8 + y; + source -= 128*y; + y = 0; + } + else + drawline = 8; + + + if (r_pixbytes == 1) + { + dest = vid.conbuffer + y*vid.conrowbytes + x; + + while (drawline--) + { + if (source[0]) + dest[0] = source[0]; + if (source[1]) + dest[1] = source[1]; + if (source[2]) + dest[2] = source[2]; + if (source[3]) + dest[3] = source[3]; + if (source[4]) + dest[4] = source[4]; + if (source[5]) + dest[5] = source[5]; + if (source[6]) + dest[6] = source[6]; + if (source[7]) + dest[7] = source[7]; + source += 128; + dest += vid.conrowbytes; + } + } + else + { + // FIXME: pre-expand to native format? + pusdest = (unsigned short *) + ((byte *)vid.conbuffer + y*vid.conrowbytes + (x<<1)); + + while (drawline--) + { + if (source[0]) + pusdest[0] = d_8to16table[source[0]]; + if (source[1]) + pusdest[1] = d_8to16table[source[1]]; + if (source[2]) + pusdest[2] = d_8to16table[source[2]]; + if (source[3]) + pusdest[3] = d_8to16table[source[3]]; + if (source[4]) + pusdest[4] = d_8to16table[source[4]]; + if (source[5]) + pusdest[5] = d_8to16table[source[5]]; + if (source[6]) + pusdest[6] = d_8to16table[source[6]]; + if (source[7]) + pusdest[7] = d_8to16table[source[7]]; + + source += 128; + pusdest += (vid.conrowbytes >> 1); + } + } +} + +/* +================ +Draw_String +================ +*/ +void Draw_String (int x, int y, char *str) +{ + while (*str) + { + Draw_Character (x, y, *str); + str++; + x += 8; + } +} + +/* +================ +Draw_Alt_String +================ +*/ +void Draw_Alt_String (int x, int y, char *str) +{ + while (*str) + { + Draw_Character (x, y, (*str) | 0x80); + str++; + x += 8; + } +} + +void Draw_Pixel(int x, int y, byte color) +{ + byte *dest; + unsigned short *pusdest; + + if (r_pixbytes == 1) + { + dest = vid.conbuffer + y*vid.conrowbytes + x; + *dest = color; + } + else + { + // FIXME: pre-expand to native format? + pusdest = (unsigned short *) + ((byte *)vid.conbuffer + y*vid.conrowbytes + (x<<1)); + *pusdest = d_8to16table[color]; + } +} + +void Draw_Crosshair(void) +{ + int x, y; + extern cvar_t crosshair, cl_crossx, cl_crossy, crosshaircolor; + extern vrect_t scr_vrect; + byte c = (byte)crosshaircolor.value; + + if (crosshair.value == 2) { + x = scr_vrect.x + scr_vrect.width/2 + cl_crossx.value; + y = scr_vrect.y + scr_vrect.height/2 + cl_crossy.value; + Draw_Pixel(x - 1, y, c); + Draw_Pixel(x - 3, y, c); + Draw_Pixel(x + 1, y, c); + Draw_Pixel(x + 3, y, c); + Draw_Pixel(x, y - 1, c); + Draw_Pixel(x, y - 3, c); + Draw_Pixel(x, y + 1, c); + Draw_Pixel(x, y + 3, c); +// Tonik --> + } else if (crosshair.value == 3) { + x = scr_vrect.x + scr_vrect.width/2 + cl_crossx.value; + y = scr_vrect.y + scr_vrect.height/2 + cl_crossy.value; + Draw_Pixel(x, y, c); + Draw_Pixel(x - 1, y, c); + Draw_Pixel(x + 1, y, c); + Draw_Pixel(x, y - 1, c); + Draw_Pixel(x, y + 1, c); +// <-- Tonik + } else if (crosshair.value) + Draw_Character ( + scr_vrect.x + scr_vrect.width/2-4 + cl_crossx.value, + scr_vrect.y + scr_vrect.height/2-4 + cl_crossy.value, + '+'); +} + +/* +================ +Draw_DebugChar + +Draws a single character directly to the upper right corner of the screen. +This is for debugging lockups by drawing different chars in different parts +of the code. +================ +*/ +void Draw_DebugChar (char num) +{ + byte *dest; + byte *source; + int drawline; + extern byte *draw_chars; + int row, col; + + if (!vid.direct) + return; // don't have direct FB access, so no debugchars... + + drawline = 8; + + row = num>>4; + col = num&15; + source = draw_chars + (row<<10) + (col<<3); + + dest = vid.direct + 312; + + while (drawline--) + { + dest[0] = source[0]; + dest[1] = source[1]; + dest[2] = source[2]; + dest[3] = source[3]; + dest[4] = source[4]; + dest[5] = source[5]; + dest[6] = source[6]; + dest[7] = source[7]; + source += 128; + dest += 320; + } +} + +/* +============= +Draw_Pic +============= +*/ +void Draw_Pic (int x, int y, qpic_t *pic) +{ + byte *dest, *source; + unsigned short *pusdest; + int v, u; + + if ((x < 0) || + (x + pic->width > vid.width) || + (y < 0) || + (y + pic->height > vid.height)) + { + Sys_Error ("Draw_Pic: bad coordinates"); + } + + source = pic->data; + + if (r_pixbytes == 1) + { + dest = vid.buffer + y * vid.rowbytes + x; + + for (v=0 ; vheight ; v++) + { + memcpy (dest, source, pic->width); + dest += vid.rowbytes; + source += pic->width; + } + } + else + { + // FIXME: pretranslate at load time? + pusdest = (unsigned short *)vid.buffer + y * (vid.rowbytes >> 1) + x; + + for (v=0 ; vheight ; v++) + { + for (u=0 ; uwidth ; u++) + { + pusdest[u] = d_8to16table[source[u]]; + } + + pusdest += vid.rowbytes >> 1; + source += pic->width; + } + } +} + + +/* +============= +Draw_SubPic +============= +*/ +void Draw_SubPic(int x, int y, qpic_t *pic, int srcx, int srcy, int width, int height) +{ + byte *dest, *source; + unsigned short *pusdest; + int v, u; + + if ((x < 0) || + (x + width > vid.width) || + (y < 0) || + (y + height > vid.height)) + { + Sys_Error ("Draw_Pic: bad coordinates"); + } + + source = pic->data + srcy * pic->width + srcx; + + if (r_pixbytes == 1) + { + dest = vid.buffer + y * vid.rowbytes + x; + + for (v=0 ; vwidth; + } + } + else + { + // FIXME: pretranslate at load time? + pusdest = (unsigned short *)vid.buffer + y * (vid.rowbytes >> 1) + x; + + for (v=0 ; v> 1; + source += pic->width; + } + } +} + + +/* +============= +Draw_TransPic +============= +*/ +void Draw_TransPic (int x, int y, qpic_t *pic) +{ + byte *dest, *source, tbyte; + unsigned short *pusdest; + int v, u; + + if (x < 0 || (unsigned)(x + pic->width) > vid.width || y < 0 || + (unsigned)(y + pic->height) > vid.height) + { + Sys_Error ("Draw_TransPic: bad coordinates"); + } + + source = pic->data; + + if (r_pixbytes == 1) + { + dest = vid.buffer + y * vid.rowbytes + x; + + if (pic->width & 7) + { // general + for (v=0 ; vheight ; v++) + { + for (u=0 ; uwidth ; u++) + if ( (tbyte=source[u]) != TRANSPARENT_COLOR) + dest[u] = tbyte; + + dest += vid.rowbytes; + source += pic->width; + } + } + else + { // unwound + for (v=0 ; vheight ; v++) + { + for (u=0 ; uwidth ; u+=8) + { + if ( (tbyte=source[u]) != TRANSPARENT_COLOR) + dest[u] = tbyte; + if ( (tbyte=source[u+1]) != TRANSPARENT_COLOR) + dest[u+1] = tbyte; + if ( (tbyte=source[u+2]) != TRANSPARENT_COLOR) + dest[u+2] = tbyte; + if ( (tbyte=source[u+3]) != TRANSPARENT_COLOR) + dest[u+3] = tbyte; + if ( (tbyte=source[u+4]) != TRANSPARENT_COLOR) + dest[u+4] = tbyte; + if ( (tbyte=source[u+5]) != TRANSPARENT_COLOR) + dest[u+5] = tbyte; + if ( (tbyte=source[u+6]) != TRANSPARENT_COLOR) + dest[u+6] = tbyte; + if ( (tbyte=source[u+7]) != TRANSPARENT_COLOR) + dest[u+7] = tbyte; + } + dest += vid.rowbytes; + source += pic->width; + } + } + } + else + { + // FIXME: pretranslate at load time? + pusdest = (unsigned short *)vid.buffer + y * (vid.rowbytes >> 1) + x; + + for (v=0 ; vheight ; v++) + { + for (u=0 ; uwidth ; u++) + { + tbyte = source[u]; + + if (tbyte != TRANSPARENT_COLOR) + { + pusdest[u] = d_8to16table[tbyte]; + } + } + + pusdest += vid.rowbytes >> 1; + source += pic->width; + } + } +} + + +/* +============= +Draw_TransPicTranslate +============= +*/ +void Draw_TransPicTranslate (int x, int y, qpic_t *pic, byte *translation) +{ + byte *dest, *source, tbyte; + unsigned short *pusdest; + int v, u; + + if (x < 0 || (unsigned)(x + pic->width) > vid.width || y < 0 || + (unsigned)(y + pic->height) > vid.height) + { + Sys_Error ("Draw_TransPic: bad coordinates"); + } + + source = pic->data; + + if (r_pixbytes == 1) + { + dest = vid.buffer + y * vid.rowbytes + x; + + if (pic->width & 7) + { // general + for (v=0 ; vheight ; v++) + { + for (u=0 ; uwidth ; u++) + if ( (tbyte=source[u]) != TRANSPARENT_COLOR) + dest[u] = translation[tbyte]; + + dest += vid.rowbytes; + source += pic->width; + } + } + else + { // unwound + for (v=0 ; vheight ; v++) + { + for (u=0 ; uwidth ; u+=8) + { + if ( (tbyte=source[u]) != TRANSPARENT_COLOR) + dest[u] = translation[tbyte]; + if ( (tbyte=source[u+1]) != TRANSPARENT_COLOR) + dest[u+1] = translation[tbyte]; + if ( (tbyte=source[u+2]) != TRANSPARENT_COLOR) + dest[u+2] = translation[tbyte]; + if ( (tbyte=source[u+3]) != TRANSPARENT_COLOR) + dest[u+3] = translation[tbyte]; + if ( (tbyte=source[u+4]) != TRANSPARENT_COLOR) + dest[u+4] = translation[tbyte]; + if ( (tbyte=source[u+5]) != TRANSPARENT_COLOR) + dest[u+5] = translation[tbyte]; + if ( (tbyte=source[u+6]) != TRANSPARENT_COLOR) + dest[u+6] = translation[tbyte]; + if ( (tbyte=source[u+7]) != TRANSPARENT_COLOR) + dest[u+7] = translation[tbyte]; + } + dest += vid.rowbytes; + source += pic->width; + } + } + } + else + { + // FIXME: pretranslate at load time? + pusdest = (unsigned short *)vid.buffer + y * (vid.rowbytes >> 1) + x; + + for (v=0 ; vheight ; v++) + { + for (u=0 ; uwidth ; u++) + { + tbyte = source[u]; + + if (tbyte != TRANSPARENT_COLOR) + { + pusdest[u] = d_8to16table[tbyte]; + } + } + + pusdest += vid.rowbytes >> 1; + source += pic->width; + } + } +} + + +void Draw_CharToConback (int num, byte *dest) +{ + int row, col; + byte *source; + int drawline; + int x; + + row = num>>4; + col = num&15; + source = draw_chars + (row<<10) + (col<<3); + + drawline = 8; + + while (drawline--) + { + for (x=0 ; x<8 ; x++) + if (source[x]) + dest[x] = 0x60 + source[x]; + source += 128; + dest += 320; + } + +} + +/* +================ +Draw_ConsoleBackground + +================ +*/ +void Draw_ConsoleBackground (int lines) +{ + int x, y, v; + byte *src, *dest; + unsigned short *pusdest; + int f, fstep; + qpic_t *conback; + char ver[100]; + static char saveback[320*8]; + + conback = Draw_CachePic ("gfx/conback.lmp"); + +// hack the version number directly into the pic + + if (cls.download) { + strcpy (ver, QWE_VERSION); + dest = conback->data + 320 + 320*186 - 11 - 8*strlen(ver); + } else { + sprintf (ver, "QWExtended client %s", QWE_VERSION); + dest = conback->data + 320 - (strlen(ver)*8 + 11) + 320*186; + } + + memcpy(saveback, conback->data + 320*186, 320*8); + for (x=0 ; xdata + v*320; + if (vid.conwidth == 320) + memcpy (dest, src, vid.conwidth); + else + { + f = 0; + fstep = 320*0x10000/vid.conwidth; + for (x=0 ; x>16]; + f += fstep; + dest[x+1] = src[f>>16]; + f += fstep; + dest[x+2] = src[f>>16]; + f += fstep; + dest[x+3] = src[f>>16]; + f += fstep; + } + } + } + } + else + { + pusdest = (unsigned short *)vid.conbuffer; + + for (y=0 ; y> 1)) + { + // FIXME: pre-expand to native format? + // FIXME: does the endian switching go away in production? + v = (vid.conheight - lines + y)*200/vid.conheight; + src = conback->data + v*320; + f = 0; + fstep = 320*0x10000/vid.conwidth; + for (x=0 ; x>16]]; + f += fstep; + pusdest[x+1] = d_8to16table[src[f>>16]]; + f += fstep; + pusdest[x+2] = d_8to16table[src[f>>16]]; + f += fstep; + pusdest[x+3] = d_8to16table[src[f>>16]]; + f += fstep; + } + } + } + // put it back + memcpy(conback->data + 320*186, saveback, 320*8); +} + + +/* +============== +R_DrawRect8 +============== +*/ +void R_DrawRect8 (vrect_t *prect, int rowbytes, byte *psrc, + int transparent) +{ + byte t; + int i, j, srcdelta, destdelta; + byte *pdest; + + pdest = vid.buffer + (prect->y * vid.rowbytes) + prect->x; + + srcdelta = rowbytes - prect->width; + destdelta = vid.rowbytes - prect->width; + + if (transparent) + { + for (i=0 ; iheight ; i++) + { + for (j=0 ; jwidth ; j++) + { + t = *psrc; + if (t != TRANSPARENT_COLOR) + { + *pdest = t; + } + + psrc++; + pdest++; + } + + psrc += srcdelta; + pdest += destdelta; + } + } + else + { + for (i=0 ; iheight ; i++) + { + memcpy (pdest, psrc, prect->width); + psrc += rowbytes; + pdest += vid.rowbytes; + } + } +} + + +/* +============== +R_DrawRect16 +============== +*/ +void R_DrawRect16 (vrect_t *prect, int rowbytes, byte *psrc, + int transparent) +{ + byte t; + int i, j, srcdelta, destdelta; + unsigned short *pdest; + +// FIXME: would it be better to pre-expand native-format versions? + + pdest = (unsigned short *)vid.buffer + + (prect->y * (vid.rowbytes >> 1)) + prect->x; + + srcdelta = rowbytes - prect->width; + destdelta = (vid.rowbytes >> 1) - prect->width; + + if (transparent) + { + for (i=0 ; iheight ; i++) + { + for (j=0 ; jwidth ; j++) + { + t = *psrc; + if (t != TRANSPARENT_COLOR) + { + *pdest = d_8to16table[t]; + } + + psrc++; + pdest++; + } + + psrc += srcdelta; + pdest += destdelta; + } + } + else + { + for (i=0 ; iheight ; i++) + { + for (j=0 ; jwidth ; j++) + { + *pdest = d_8to16table[*psrc]; + psrc++; + pdest++; + } + + psrc += srcdelta; + pdest += destdelta; + } + } +} + + +/* +============= +Draw_TileClear + +This repeats a 64*64 tile graphic to fill the screen around a sized down +refresh window. +============= +*/ +void Draw_TileClear (int x, int y, int w, int h) +{ + int width, height, tileoffsetx, tileoffsety; + byte *psrc; + vrect_t vr; + + r_rectdesc.rect.x = x; + r_rectdesc.rect.y = y; + r_rectdesc.rect.width = w; + r_rectdesc.rect.height = h; + + vr.y = r_rectdesc.rect.y; + height = r_rectdesc.rect.height; + + tileoffsety = vr.y % r_rectdesc.height; + + while (height > 0) + { + vr.x = r_rectdesc.rect.x; + width = r_rectdesc.rect.width; + + if (tileoffsety != 0) + vr.height = r_rectdesc.height - tileoffsety; + else + vr.height = r_rectdesc.height; + + if (vr.height > height) + vr.height = height; + + tileoffsetx = vr.x % r_rectdesc.width; + + while (width > 0) + { + if (tileoffsetx != 0) + vr.width = r_rectdesc.width - tileoffsetx; + else + vr.width = r_rectdesc.width; + + if (vr.width > width) + vr.width = width; + + psrc = r_rectdesc.ptexbytes + + (tileoffsety * r_rectdesc.rowbytes) + tileoffsetx; + + if (r_pixbytes == 1) + { + R_DrawRect8 (&vr, r_rectdesc.rowbytes, psrc, 0); + } + else + { + R_DrawRect16 (&vr, r_rectdesc.rowbytes, psrc, 0); + } + + vr.x += vr.width; + width -= vr.width; + tileoffsetx = 0; // only the left tile can be left-clipped + } + + vr.y += vr.height; + height -= vr.height; + tileoffsety = 0; // only the top tile can be top-clipped + } +} + + +/* +============= +Draw_Fill + +Fills a box of pixels with a single color +============= +*/ +void Draw_Fill (int x, int y, int w, int h, int c) +{ + byte *dest; + unsigned short *pusdest; + unsigned uc; + int u, v; + + if (x < 0 || x + w > vid.width || + y < 0 || y + h > vid.height) { + Con_Printf("Bad Draw_Fill(%d, %d, %d, %d, %c)\n", + x, y, w, h, c); + return; + } + + if (r_pixbytes == 1) + { + dest = vid.buffer + y*vid.rowbytes + x; + for (v=0 ; v> 1) + x; + for (v=0 ; v> 1)) + for (u=0 ; udata, 24, 24); +} + + +/* +================ +Draw_EndDisc + +Erases the disc icon. +Call after completing any disc IO +================ +*/ +void Draw_EndDisc (void) +{ + + D_EndDirectRect (vid.width - 24, 0, 24, 24); +} + diff --git a/source/draw.h b/source/draw.h index f7f2a774..f926863c 100644 --- a/source/draw.h +++ b/source/draw.h @@ -1,45 +1,45 @@ -/* -Copyright (C) 1996-1997 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.h -- these are the only functions outside the refresh allowed -// to touch the vid buffer - -#include "wad.h" - -extern qpic_t *draw_disc; // also used on sbar - -void Draw_Init (void); -void Draw_Character (int x, int y, int num); -void Draw_DebugChar (char num); -void Draw_SubPic(int x, int y, qpic_t *pic, int srcx, int srcy, int width, int height); -void Draw_Pic (int x, int y, qpic_t *pic); -void Draw_TransPic (int x, int y, qpic_t *pic); -void Draw_TransPicTranslate (int x, int y, qpic_t *pic, byte *translation); -void Draw_ConsoleBackground (int lines); -void Draw_BeginDisc (void); -void Draw_EndDisc (void); -void Draw_TileClear (int x, int y, int w, int h); -void Draw_Fill (int x, int y, int w, int h, int c); -void Draw_FadeScreen (void); -void Draw_String (int x, int y, char *str); -void Draw_Alt_String (int x, int y, char *str); -qpic_t *Draw_PicFromWad (char *name); -qpic_t *Draw_CachePic (char *path); -void Draw_Crosshair(void); +/* +Copyright (C) 1996-1997 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.h -- these are the only functions outside the refresh allowed +// to touch the vid buffer + +#include "wad.h" + +extern qpic_t *draw_disc; // also used on sbar + +void Draw_Init (void); +void Draw_Character (int x, int y, int num); +void Draw_DebugChar (char num); +void Draw_SubPic(int x, int y, qpic_t *pic, int srcx, int srcy, int width, int height); +void Draw_Pic (int x, int y, qpic_t *pic); +void Draw_TransPic (int x, int y, qpic_t *pic); +void Draw_TransPicTranslate (int x, int y, qpic_t *pic, byte *translation); +void Draw_ConsoleBackground (int lines); +void Draw_BeginDisc (void); +void Draw_EndDisc (void); +void Draw_TileClear (int x, int y, int w, int h); +void Draw_Fill (int x, int y, int w, int h, int c); +void Draw_FadeScreen (void); +void Draw_String (int x, int y, char *str); +void Draw_Alt_String (int x, int y, char *str); +qpic_t *Draw_PicFromWad (char *name); +qpic_t *Draw_CachePic (char *path); +void Draw_Crosshair(void); diff --git a/source/gl_draw.c b/source/gl_draw.c index db9a77c6..b2c2b365 100644 --- a/source/gl_draw.c +++ b/source/gl_draw.c @@ -1,1505 +1,1505 @@ -/* -Copyright (C) 1996-1997 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 "quakedef.h" -#include "version.h" - -extern unsigned char d_15to8table[65536]; -extern unsigned d_8to24table2[256]; -extern cvar_t crosshair, cl_crossx, cl_crossy, crosshaircolor; - -cvar_t gl_nobind = {"gl_nobind", "0"}; -cvar_t gl_max_size = {"gl_max_size", "1024"}; -cvar_t gl_picmip = {"gl_picmip", "0"}; -cvar_t gl_conalpha = {"gl_conalpha", "0.8"}; - -byte *draw_chars; // 8*8 graphic characters -qpic_t *draw_disc; -qpic_t *draw_backtile; - -int translate_texture; -int char_texture; -int cs2_texture; // crosshair 2 texture -int cs3_texture; // crosshair 3 texture - -static byte cs2_data[64] = { - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, - 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff -}; - -static byte cs3_data[64] = { - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff -}; - - -typedef struct -{ - int texnum; - float sl, tl, sh, th; -} glpic_t; - -int GL_LoadPicTexture (qpic_t *pic, byte *data); - -byte conback_buffer[sizeof(qpic_t) + sizeof(glpic_t)]; -qpic_t *conback = (qpic_t *)&conback_buffer; - -int gl_lightmap_format = 4; -int gl_solid_format = 3; -int gl_alpha_format = 4; - -int gl_filter_min = GL_LINEAR_MIPMAP_NEAREST; -int gl_filter_max = GL_LINEAR; - - -int texels; - -typedef struct -{ - int texnum; - char identifier[64]; - int width, height; - qboolean mipmap; -} gltexture_t; - -gltexture_t gltextures[MAX_GLTEXTURES]; -int numgltextures; - -void GL_Bind (int texnum) -{ - if (gl_nobind.value) - texnum = char_texture; - if (currenttexture == texnum) - return; - currenttexture = texnum; -#ifdef _WIN32 - bindTexFunc (GL_TEXTURE_2D, texnum); -#else - glBindTexture (GL_TEXTURE_2D, texnum); -#endif -} - - -/* -============================================================================= - - scrap allocation - - Allocate all the little status bar objects into a single texture - to crutch up stupid hardware / drivers - -============================================================================= -*/ - -// some cards have low quality of alpha pics, so load the pics -// without transparent pixels into a different scrap block. -// scrap 0 is solid pics, 1 is transparent -#define MAX_SCRAPS 2 -#define BLOCK_WIDTH 256 -#define BLOCK_HEIGHT 256 - -int scrap_allocated[MAX_SCRAPS][BLOCK_WIDTH]; -byte scrap_texels[MAX_SCRAPS][BLOCK_WIDTH*BLOCK_HEIGHT*4]; -int scrap_dirty = 0; // bit mask -int scrap_texnum; - -// returns false if allocation failed -qboolean Scrap_AllocBlock (int scrapnum, int w, int h, int *x, int *y) -{ - int i, j; - int best, best2; - - best = BLOCK_HEIGHT; - - for (i=0 ; i= best) - break; - if (scrap_allocated[scrapnum][i+j] > best2) - best2 = scrap_allocated[scrapnum][i+j]; - } - if (j == w) - { // this is a valid spot - *x = i; - *y = best = best2; - } - } - - if (best + h > BLOCK_HEIGHT) - return false; - - for (i=0 ; idata; - - // load little ones into the scrap - if (p->width < 64 && p->height < 64) - { - int x, y; - int i, j, k; - int texnum; - - texnum = memchr(p->data, 255, p->width*p->height) != NULL; - if (!Scrap_AllocBlock (texnum, p->width, p->height, &x, &y)) { - GL_LoadPicTexture (p, p->data); - return p; - } - k = 0; - for (i=0 ; iheight ; i++) - for (j=0 ; jwidth ; j++, k++) - scrap_texels[texnum][(y+i)*BLOCK_WIDTH + x + j] = p->data[k]; - texnum += scrap_texnum; - gl->texnum = texnum; - gl->sl = (x+0.01)/(float)BLOCK_WIDTH; - gl->sh = (x+p->width-0.01)/(float)BLOCK_WIDTH; - gl->tl = (y+0.01)/(float)BLOCK_WIDTH; - gl->th = (y+p->height-0.01)/(float)BLOCK_WIDTH; - - pic_count++; - pic_texels += p->width*p->height; - } - else - GL_LoadPicTexture (p, p->data); - - return p; -} - - -/* -================ -Draw_CachePic -================ -*/ -qpic_t *Draw_CachePic (char *path) -{ - cachepic_t *pic; - int i; - qpic_t *dat; - - for (pic=menu_cachepics, i=0 ; iname)) - return &pic->pic; - - if (menu_numcachepics == MAX_CACHED_PICS) - Sys_Error ("menu_numcachepics == MAX_CACHED_PICS"); - menu_numcachepics++; - strcpy (pic->name, path); - -// -// load the pic from disk -// - dat = (qpic_t *)COM_LoadTempFile (path); - if (!dat) - Sys_Error ("Draw_CachePic: failed to load %s", path); - SwapPic (dat); - - // HACK HACK HACK --- we need to keep the bytes for - // the translatable player picture just for the menu - // configuration dialog - if (!strcmp (path, "gfx/menuplyr.lmp")) - memcpy (menuplyr_pixels, dat->data, dat->width*dat->height); - - pic->pic.width = dat->width; - pic->pic.height = dat->height; - - GL_LoadPicTexture (&pic->pic, dat->data); - - return &pic->pic; -} - - -void Draw_CharToConback (int num, byte *dest) -{ - int row, col; - byte *source; - int drawline; - int x; - - row = num>>4; - col = num&15; - source = draw_chars + (row<<10) + (col<<3); - - drawline = 8; - - while (drawline--) - { - for (x=0 ; x<8 ; x++) - if (source[x] != 255) - dest[x] = 0x60 + source[x]; - source += 128; - dest += 320; - } - -} - -typedef struct -{ - char *name; - int minimize, maximize; -} glmode_t; - -glmode_t modes[] = { - {"GL_NEAREST", GL_NEAREST, GL_NEAREST}, - {"GL_LINEAR", GL_LINEAR, GL_LINEAR}, - {"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST}, - {"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR}, - {"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST}, - {"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR} -}; - -/* -=============== -Draw_TextureMode_f -=============== -*/ -void Draw_TextureMode_f (void) -{ - int i; - gltexture_t *glt; - - if (Cmd_Argc() == 1) - { - for (i=0 ; i< 6 ; i++) - if (gl_filter_min == modes[i].minimize) - { - Con_Printf ("%s\n", modes[i].name); - return; - } - Con_Printf ("current filter is unknown???\n"); - return; - } - - for (i=0 ; i< 6 ; i++) - { - if (!Q_strcasecmp (modes[i].name, Cmd_Argv(1) ) ) - break; - } - if (i == 6) - { - Con_Printf ("bad filter name\n"); - return; - } - - gl_filter_min = modes[i].minimize; - gl_filter_max = modes[i].maximize; - - // change all the existing mipmap texture objects - for (i=0, glt=gltextures ; imipmap) - { - GL_Bind (glt->texnum); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); - } - } -} - - -void Draw_LoadCharset (void) -{ - int i; - char buf[128*256]; - char *src, *dest; - - draw_chars = W_GetLumpName ("conchars"); - for (i=0 ; i<256*64 ; i++) - if (draw_chars[i] == 0) - draw_chars[i] = 255; // proper transparent color - - // Convert the 128*128 conchars texture to 128*256 leaving - // empty space between rows so that chars don't stumble on - // each other because of texture smoothing. - // This hack costs us 64K of GL texture memory - memset (buf, 255, sizeof(buf)); - src = draw_chars; - dest = buf; - for (i=0 ; i<16 ; i++) { - memcpy (dest, src, 128*8); - src += 128*8; - dest += 128*8*2; - } - - char_texture = GL_LoadTexture ("charset", 128, 256, buf, false, true, false); -} - - -/* -=============== -Draw_Init -=============== -*/ -void Draw_Init (void) -{ - qpic_t *cb; - byte *dest; - int x; - char ver[40]; - glpic_t *gl; - int start; - byte *ncdata; - - Cvar_RegisterVariable (&gl_nobind); - Cvar_RegisterVariable (&gl_max_size); - Cvar_RegisterVariable (&gl_picmip); - Cvar_RegisterVariable (&gl_conalpha); - - // 3dfx can only handle 256 wide textures - if (!Q_strncasecmp ((char *)gl_renderer, "3dfx",4) || - !Q_strncasecmp ((char *)gl_renderer, "Mesa",4)) - Cvar_Set (&gl_max_size, "256"); - - Cmd_AddCommand ("gl_texturemode", &Draw_TextureMode_f); - - // load the console background and the charset - // by hand, because we need to write the version - // string into the background before turning - // it into a texture - - Draw_LoadCharset (); - - cs2_texture = GL_LoadTexture ("", 8, 8, cs2_data, false, true, false); - glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - - cs3_texture = GL_LoadTexture ("", 8, 8, cs3_data, false, true, false); - glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - - start = Hunk_LowMark (); - - cb = (qpic_t *)COM_LoadHunkFile ("gfx/conback.lmp"); - if (!cb) - Sys_Error ("Couldn't load gfx/conback.lmp"); - SwapPic (cb); - -// sprintf (ver, "%4.2f", QW_VERSION); - strcpy (ver, QWE_VERSION); - dest = cb->data + 320 + 320*186 - 11 - 8*strlen(ver); - for (x=0 ; xwidth = vid.conwidth; - conback->height = vid.conheight; - - // scale console to vid size - dest = ncdata = Hunk_AllocName(vid.conwidth * vid.conheight, "conback"); - - for (y=0 ; ydata + cb->width * (y*cb->height/vid.conheight); - if (vid.conwidth == cb->width) - memcpy (dest, src, vid.conwidth); - else - { - f = 0; - fstep = cb->width*0x10000/vid.conwidth; - for (x=0 ; x>16]; - f += fstep; - dest[x+1] = src[f>>16]; - f += fstep; - dest[x+2] = src[f>>16]; - f += fstep; - dest[x+3] = src[f>>16]; - f += fstep; - } - } - } -#else - conback->width = cb->width; - conback->height = cb->height; - ncdata = cb->data; -#endif - - gl = (glpic_t *)conback->data; - gl->texnum = GL_LoadTexture ("conback", conback->width, conback->height, ncdata, false, false, false); - gl->sl = 0; - gl->sh = 1; - gl->tl = 0; - gl->th = 1; - conback->width = vid.conwidth; - conback->height = vid.conheight; - - // free loaded console - Hunk_FreeToLowMark (start); - - // save a texture slot for translated picture - translate_texture = texture_extension_number++; - - // save slots for scraps - scrap_texnum = texture_extension_number; - texture_extension_number += MAX_SCRAPS; - - // - // get the other pics we need - // - draw_disc = Draw_PicFromWad ("disc"); - draw_backtile = Draw_PicFromWad ("backtile"); -} - - - -/* -================ -Draw_Character - -Draws one 8*8 graphics character with 0 being transparent. -It can be clipped to the top of the screen to allow the console to be -smoothly scrolled off. -================ -*/ -void Draw_Character (int x, int y, int num) -{ - int row, col; - float frow, fcol; - - if (num == 32) - return; // space - - num &= 255; - - if (y <= -8) - return; // totally off screen - - row = num>>4; - col = num&15; - - frow = row*0.0625; - fcol = col*0.0625; - - GL_Bind (char_texture); - - glBegin (GL_QUADS); - glTexCoord2f (fcol, frow); - glVertex2f (x, y); - glTexCoord2f (fcol + (1.0/16), frow); - glVertex2f (x+8, y); - glTexCoord2f (fcol + (1.0/16), frow + (1.0/32)); - glVertex2f (x+8, y+8); - glTexCoord2f (fcol, frow + (1.0/32)); - glVertex2f (x, y+8); - glEnd (); -} - -/* -================ -Draw_String -================ -*/ -void Draw_String (int x, int y, char *str) -{ - while (*str) - { - Draw_Character (x, y, *str); - str++; - x += 8; - } -} - -/* -================ -Draw_Alt_String -================ -*/ -void Draw_Alt_String (int x, int y, char *str) -{ - while (*str) - { - Draw_Character (x, y, (*str) | 0x80); - str++; - x += 8; - } -} - -void Draw_Crosshair (void) -{ - int x, y; - int ofs1, ofs2; - extern vrect_t scr_vrect; - unsigned char *pColor; - - if (crosshair.value == 2 || crosshair.value == 3) { - x = scr_vrect.x + scr_vrect.width/2 + cl_crossx.value; - y = scr_vrect.y + scr_vrect.height/2 + cl_crossy.value; - - glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - pColor = (unsigned char *) &d_8to24table[(byte) crosshaircolor.value]; - glColor4ubv ( pColor ); - if (crosshair.value == 2) - GL_Bind (cs2_texture); - else - GL_Bind (cs3_texture); - - if (vid.width == 320) { - ofs1 = 3;//3.5; - ofs2 = 5;//4.5; - } else { - ofs1 = 7; - ofs2 = 9; - } - glBegin (GL_QUADS); - glTexCoord2f (0, 0); - glVertex2f (x - ofs1, y - ofs1); - glTexCoord2f (1, 0); - glVertex2f (x + ofs2, y - ofs1); - glTexCoord2f (1, 1); - glVertex2f (x + ofs2, y + ofs2); - glTexCoord2f (0, 1); - glVertex2f (x - ofs1, y + ofs2); - glEnd (); - - glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); - } else if (crosshair.value) - Draw_Character (scr_vrect.x + scr_vrect.width/2-4 + cl_crossx.value, - scr_vrect.y + scr_vrect.height/2-4 + cl_crossy.value, - '+'); -} - -/* -================ -Draw_DebugChar - -Draws a single character directly to the upper right corner of the screen. -This is for debugging lockups by drawing different chars in different parts -of the code. -================ -*/ -void Draw_DebugChar (char num) -{ -} - -/* -============= -Draw_Pic -============= -*/ -void Draw_Pic (int x, int y, qpic_t *pic) -{ - glpic_t *gl; - - if (scrap_dirty) - Scrap_Upload (); - gl = (glpic_t *)pic->data; - glColor4f (1,1,1,1); - GL_Bind (gl->texnum); - glBegin (GL_QUADS); - glTexCoord2f (gl->sl, gl->tl); - glVertex2f (x, y); - glTexCoord2f (gl->sh, gl->tl); - glVertex2f (x+pic->width, y); - glTexCoord2f (gl->sh, gl->th); - glVertex2f (x+pic->width, y+pic->height); - glTexCoord2f (gl->sl, gl->th); - glVertex2f (x, y+pic->height); - glEnd (); -} - -/* -============= -Draw_AlphaPic -============= -*/ -void Draw_AlphaPic (int x, int y, qpic_t *pic, float alpha) -{ - glpic_t *gl; - - if (scrap_dirty) - Scrap_Upload (); - gl = (glpic_t *)pic->data; - glDisable(GL_ALPHA_TEST); - glEnable (GL_BLEND); -// glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glCullFace(GL_FRONT); - glColor4f (1,1,1,alpha); - GL_Bind (gl->texnum); - glBegin (GL_QUADS); - glTexCoord2f (gl->sl, gl->tl); - glVertex2f (x, y); - glTexCoord2f (gl->sh, gl->tl); - glVertex2f (x+pic->width, y); - glTexCoord2f (gl->sh, gl->th); - glVertex2f (x+pic->width, y+pic->height); - glTexCoord2f (gl->sl, gl->th); - glVertex2f (x, y+pic->height); - glEnd (); - glColor4f (1,1,1,1); - glEnable(GL_ALPHA_TEST); - glDisable (GL_BLEND); -} - -void Draw_SubPic(int x, int y, qpic_t *pic, int srcx, int srcy, int width, int height) -{ - glpic_t *gl; - float newsl, newtl, newsh, newth; - float oldglwidth, oldglheight; - - if (scrap_dirty) - Scrap_Upload (); - gl = (glpic_t *)pic->data; - - oldglwidth = gl->sh - gl->sl; - oldglheight = gl->th - gl->tl; - - newsl = gl->sl + (srcx*oldglwidth)/pic->width; - newsh = newsl + (width*oldglwidth)/pic->width; - - newtl = gl->tl + (srcy*oldglheight)/pic->height; - newth = newtl + (height*oldglheight)/pic->height; - - glColor4f (1,1,1,1); - GL_Bind (gl->texnum); - glBegin (GL_QUADS); - glTexCoord2f (newsl, newtl); - glVertex2f (x, y); - glTexCoord2f (newsh, newtl); - glVertex2f (x+width, y); - glTexCoord2f (newsh, newth); - glVertex2f (x+width, y+height); - glTexCoord2f (newsl, newth); - glVertex2f (x, y+height); - glEnd (); -} - -/* -============= -Draw_TransPic -============= -*/ -void Draw_TransPic (int x, int y, qpic_t *pic) -{ - - if (x < 0 || (unsigned)(x + pic->width) > vid.width || y < 0 || - (unsigned)(y + pic->height) > vid.height) - { - Sys_Error ("Draw_TransPic: bad coordinates"); - } - - Draw_Pic (x, y, pic); -} - - -/* -============= -Draw_TransPicTranslate - -Only used for the player color selection menu -============= -*/ -void Draw_TransPicTranslate (int x, int y, qpic_t *pic, byte *translation) -{ - int v, u, c; - unsigned trans[64*64], *dest; - byte *src; - int p; - - GL_Bind (translate_texture); - - c = pic->width * pic->height; - - dest = trans; - for (v=0 ; v<64 ; v++, dest += 64) - { - src = &menuplyr_pixels[ ((v*pic->height)>>6) *pic->width]; - for (u=0 ; u<64 ; u++) - { - p = src[(u*pic->width)>>6]; - if (p == 255) - dest[u] = p; - else - dest[u] = d_8to24table[translation[p]]; - } - } - - glTexImage2D (GL_TEXTURE_2D, 0, gl_alpha_format, 64, 64, 0, GL_RGBA, GL_UNSIGNED_BYTE, trans); - - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - glColor3f (1,1,1); - glBegin (GL_QUADS); - glTexCoord2f (0, 0); - glVertex2f (x, y); - glTexCoord2f (1, 0); - glVertex2f (x+pic->width, y); - glTexCoord2f (1, 1); - glVertex2f (x+pic->width, y+pic->height); - glTexCoord2f (0, 1); - glVertex2f (x, y+pic->height); - glEnd (); -} - - -/* -================ -Draw_ConsoleBackground - -================ -*/ -void Draw_ConsoleBackground (int lines) -{ - char ver[80]; - int x, y, i; - - if (lines == vid.height) - Draw_Pic(0, lines - vid.height, conback); - else - Draw_AlphaPic (0, lines - vid.height, conback, gl_conalpha.value); - - y = lines-14; - if (!cls.download) { -#ifdef __linux__ - sprintf (ver, "LinuxGL (%4.2f) QuakeWorld", LINUX_VERSION); -#else -// sprintf (ver, "GL (%4.2f) QuakeWorld", GLQUAKE_VERSION); - sprintf (ver, "GL (%4.2f) QWExtended client", GLQUAKE_VERSION); -#endif - x = vid.conwidth - (strlen(ver)*8 + 11) - (vid.conwidth*8/320)*7; - for (i=0 ; idata); - glBegin (GL_QUADS); - glTexCoord2f (x/64.0, y/64.0); - glVertex2f (x, y); - glTexCoord2f ( (x+w)/64.0, y/64.0); - glVertex2f (x+w, y); - glTexCoord2f ( (x+w)/64.0, (y+h)/64.0); - glVertex2f (x+w, y+h); - glTexCoord2f ( x/64.0, (y+h)/64.0 ); - glVertex2f (x, y+h); - glEnd (); -} - - -/* -============= -Draw_Fill - -Fills a box of pixels with a single color -============= -*/ -void Draw_Fill (int x, int y, int w, int h, int c) -{ - glDisable (GL_TEXTURE_2D); - glColor3f (host_basepal[c*3]/255.0, - host_basepal[c*3+1]/255.0, - host_basepal[c*3+2]/255.0); - - glBegin (GL_QUADS); - - glVertex2f (x,y); - glVertex2f (x+w, y); - glVertex2f (x+w, y+h); - glVertex2f (x, y+h); - - glEnd (); - glColor3f (1,1,1); - glEnable (GL_TEXTURE_2D); -} -//============================================================================= - -/* -================ -Draw_FadeScreen - -================ -*/ -void Draw_FadeScreen (void) -{ - glEnable (GL_BLEND); - glDisable (GL_TEXTURE_2D); - glColor4f (0, 0, 0, 0.7); - glBegin (GL_QUADS); - - glVertex2f (0,0); - glVertex2f (vid.width, 0); - glVertex2f (vid.width, vid.height); - glVertex2f (0, vid.height); - - glEnd (); - glColor4f (1,1,1,1); - glEnable (GL_TEXTURE_2D); - glDisable (GL_BLEND); - - Sbar_Changed(); -} - -//============================================================================= - -/* -================ -Draw_BeginDisc - -Draws the little blue disc in the corner of the screen. -Call before beginning any disc IO. -================ -*/ -void Draw_BeginDisc (void) -{ - if (!draw_disc) - return; - glDrawBuffer (GL_FRONT); - Draw_Pic (vid.width - 24, 0, draw_disc); - glDrawBuffer (GL_BACK); -} - - -/* -================ -Draw_EndDisc - -Erases the disc icon. -Call after completing any disc IO -================ -*/ -void Draw_EndDisc (void) -{ -} - -/* -================ -GL_Set2D - -Setup as if the screen was 320*200 -================ -*/ -void GL_Set2D (void) -{ - glViewport (glx, gly, glwidth, glheight); - - glMatrixMode(GL_PROJECTION); - glLoadIdentity (); - glOrtho (0, vid.width, vid.height, 0, -99999, 99999); - - glMatrixMode(GL_MODELVIEW); - glLoadIdentity (); - - glDisable (GL_DEPTH_TEST); - glDisable (GL_CULL_FACE); - glDisable (GL_BLEND); - glEnable (GL_ALPHA_TEST); -// glDisable (GL_ALPHA_TEST); - - glColor4f (1,1,1,1); -} - -//==================================================================== - -/* -================ -GL_FindTexture -================ -*/ -int GL_FindTexture (char *identifier) -{ - int i; - gltexture_t *glt; - - for (i=0, glt=gltextures ; iidentifier)) - return gltextures[i].texnum; - } - - return -1; -} - -/* -================ -GL_ResampleTexture -================ -*/ -void GL_ResampleTexture (unsigned *in, int inwidth, int inheight, unsigned *out, int outwidth, int outheight) -{ - int i, j; - unsigned *inrow; - unsigned frac, fracstep; - - fracstep = inwidth*0x10000/outwidth; - for (i=0 ; i> 1; - for (j=0 ; j>16]; - frac += fracstep; - out[j+1] = inrow[frac>>16]; - frac += fracstep; - out[j+2] = inrow[frac>>16]; - frac += fracstep; - out[j+3] = inrow[frac>>16]; - frac += fracstep; - } - } -} - -/* -================ -GL_Resample8BitTexture -- JACK -================ -*/ -void GL_Resample8BitTexture (unsigned char *in, int inwidth, int inheight, unsigned char *out, int outwidth, int outheight) -{ - int i, j; - unsigned char *inrow; - unsigned frac, fracstep; - - fracstep = inwidth*0x10000/outwidth; - for (i=0 ; i> 1; - for (j=0 ; j>16]; - frac += fracstep; - out[j+1] = inrow[frac>>16]; - frac += fracstep; - out[j+2] = inrow[frac>>16]; - frac += fracstep; - out[j+3] = inrow[frac>>16]; - frac += fracstep; - } - } -} - -/* -================ -GL_MipMap - -Operates in place, quartering the size of the texture -================ -*/ -void GL_MipMap (byte *in, int width, int height) -{ - int i, j; - byte *out; - - width <<=2; - height >>= 1; - out = in; - for (i=0 ; i>2; - out[1] = (in[1] + in[5] + in[width+1] + in[width+5])>>2; - out[2] = (in[2] + in[6] + in[width+2] + in[width+6])>>2; - out[3] = (in[3] + in[7] + in[width+3] + in[width+7])>>2; - } - } -} - -/* -================ -GL_MipMap8Bit - -Mipping for 8 bit textures -================ -*/ -void GL_MipMap8Bit (byte *in, int width, int height) -{ - int i, j; - byte *out; - unsigned short r,g,b; - byte *at1, *at2, *at3, *at4; - - height >>= 1; - out = in; - for (i=0 ; i>=5; - g = (at1[1]+at2[1]+at3[1]+at4[1]); g>>=5; - b = (at1[2]+at2[2]+at3[2]+at4[2]); b>>=5; - - out[0] = d_15to8table[(r<<0) + (g<<5) + (b<<10)]; - } -} - -/* -=============== -GL_Upload32 -=============== -*/ -void GL_Upload32 (unsigned *data, int width, int height, qboolean mipmap, qboolean alpha) -{ - int samples; -static unsigned scaled[1024*512]; // [512*256]; - int scaled_width, scaled_height; - - for (scaled_width = 1 ; scaled_width < width ; scaled_width<<=1) - ; - for (scaled_height = 1 ; scaled_height < height ; scaled_height<<=1) - ; - - if (mipmap) { - scaled_width >>= (int)gl_picmip.value; - scaled_height >>= (int)gl_picmip.value; - } - - if (scaled_width > gl_max_size.value) - scaled_width = gl_max_size.value; - if (scaled_height > gl_max_size.value) - scaled_height = gl_max_size.value; - if (scaled_width < 1) - scaled_width = 1; - if (scaled_height < 1) - scaled_height = 1; - - if (scaled_width * scaled_height > sizeof(scaled)/4) - Sys_Error ("GL_LoadTexture: too big"); - - samples = alpha ? gl_alpha_format : gl_solid_format; - -#if 0 - if (mipmap) - gluBuild2DMipmaps (GL_TEXTURE_2D, samples, width, height, GL_RGBA, GL_UNSIGNED_BYTE, trans); - else if (scaled_width == width && scaled_height == height) - glTexImage2D (GL_TEXTURE_2D, 0, samples, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, trans); - else - { - gluScaleImage (GL_RGBA, width, height, GL_UNSIGNED_BYTE, trans, - scaled_width, scaled_height, GL_UNSIGNED_BYTE, scaled); - glTexImage2D (GL_TEXTURE_2D, 0, samples, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled); - } -#else -texels += scaled_width * scaled_height; - - if (scaled_width == width && scaled_height == height) - { - if (!mipmap) - { - glTexImage2D (GL_TEXTURE_2D, 0, samples, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); - goto done; - } - memcpy (scaled, data, width*height*4); - } - else - GL_ResampleTexture (data, width, height, scaled, scaled_width, scaled_height); - - glTexImage2D (GL_TEXTURE_2D, 0, samples, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled); - if (mipmap) - { - int miplevel; - - miplevel = 0; - while (scaled_width > 1 || scaled_height > 1) - { - GL_MipMap ((byte *)scaled, scaled_width, scaled_height); - scaled_width >>= 1; - scaled_height >>= 1; - if (scaled_width < 1) - scaled_width = 1; - if (scaled_height < 1) - scaled_height = 1; - miplevel++; - glTexImage2D (GL_TEXTURE_2D, miplevel, samples, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled); - } - } -done: ; -#endif - - - if (mipmap) - { - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); - } - else - { - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); - } -} - -void GL_Upload8_EXT (byte *data, int width, int height, qboolean mipmap, qboolean alpha) -{ - int i, s; - qboolean noalpha; - int samples; - static unsigned char scaled[1024*512]; // [512*256]; - int scaled_width, scaled_height; - - s = width*height; - // if there are no transparent pixels, make it a 3 component - // texture even if it was specified as otherwise - if (alpha) - { - noalpha = true; - for (i=0 ; i>= (int)gl_picmip.value; - scaled_height >>= (int)gl_picmip.value; - } - - if (scaled_width > gl_max_size.value) - scaled_width = gl_max_size.value; - if (scaled_height > gl_max_size.value) - scaled_height = gl_max_size.value; - if (scaled_width < 1) - scaled_width = 1; - if (scaled_height < 1) - scaled_height = 1; - - if (scaled_width * scaled_height > sizeof(scaled)) - Sys_Error ("GL_LoadTexture: too big"); - - samples = 1; // alpha ? gl_alpha_format : gl_solid_format; - - texels += scaled_width * scaled_height; - - if (scaled_width == width && scaled_height == height) - { - if (!mipmap) - { - glTexImage2D (GL_TEXTURE_2D, 0, GL_COLOR_INDEX8_EXT, scaled_width, scaled_height, 0, GL_COLOR_INDEX , GL_UNSIGNED_BYTE, data); - goto done; - } - memcpy (scaled, data, width*height); - } - else - GL_Resample8BitTexture (data, width, height, scaled, scaled_width, scaled_height); - - glTexImage2D (GL_TEXTURE_2D, 0, GL_COLOR_INDEX8_EXT, scaled_width, scaled_height, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, scaled); - if (mipmap) - { - int miplevel; - - miplevel = 0; - while (scaled_width > 1 || scaled_height > 1) - { - GL_MipMap8Bit ((byte *)scaled, scaled_width, scaled_height); - scaled_width >>= 1; - scaled_height >>= 1; - if (scaled_width < 1) - scaled_width = 1; - if (scaled_height < 1) - scaled_height = 1; - miplevel++; - glTexImage2D (GL_TEXTURE_2D, miplevel, GL_COLOR_INDEX8_EXT, scaled_width, scaled_height, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, scaled); - } - } -done: ; - - if (mipmap) - { - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); - } - else - { - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); - } -} - -extern qboolean VID_Is8bit(); - -/* -=============== -GL_Upload8 -=============== -*/ -void GL_Upload8 (byte *data, int width, int height, qboolean mipmap, qboolean alpha, qboolean brighten) -{ -static unsigned trans[640*480]; // FIXME, temporary - int i, s; - qboolean noalpha; - int p; - unsigned *table; - - if (brighten) - table = d_8to24table2; - else - table = d_8to24table; - - s = width*height; - - if (alpha == 2) - { - // this is a fullbright mask, so make all non-fullbright - // colors transparent - for (i=0 ; iidentifier) - && width == glt->width && height == glt->height) - return gltextures[i].texnum; - } - } - else - glt = &gltextures[numgltextures]; - - if (numgltextures == MAX_GLTEXTURES) - Sys_Error ("GL_LoadTexture: numgltextures == MAX_GLTEXTURES"); - numgltextures++; - - strcpy (glt->identifier, identifier); - glt->texnum = texture_extension_number; - glt->width = width; - glt->height = height; - glt->mipmap = mipmap; - - GL_Bind (texture_extension_number); - - GL_Upload8 (data, width, height, mipmap, alpha, brighten); - - texture_extension_number++; - - return texture_extension_number-1; -} - -/* -================ -GL_LoadPicTexture - -NOTE: qpic_t must point to a buffer that will fit both -qpic_t and appended glpic_t - -FIXME: this is messy -Merge qpic_t and glpic_t into one struct (dpic_t)? -================ -*/ -int GL_LoadPicTexture (qpic_t *pic, byte *data) -{ - glpic_t *gl; - int glwidth, glheight; - int i; - - gl = (glpic_t *)pic->data; - - for (glwidth = 1 ; glwidth < pic->width ; glwidth<<=1) - ; - for (glheight = 1 ; glheight < pic->height ; glheight<<=1) - ; - - if (glwidth == pic->width && glheight == pic->height) - { - gl->texnum = GL_LoadTexture ("", glwidth, glheight, data, - false, true, false); - gl->sl = 0; - gl->sh = 1; - gl->tl = 0; - gl->th = 1; - } - else - { - byte *src, *dest; - byte *buf; - - buf = Q_Malloc (glwidth*glheight); - - memset (buf, 0, glwidth*glheight); - src = data; - dest = buf; - for (i=0 ; iheight ; i++) { - memcpy (dest, src, pic->width); - src += pic->width; - dest += glwidth; - } - - gl->texnum = GL_LoadTexture ("", glwidth, glheight, buf, - false, true, false); - gl->sl = 0; - gl->sh = (float)pic->width / glwidth; - gl->tl = 0; - gl->th = (float)pic->height / glheight; - - free (buf); - } - - return gl->texnum; -} - -/****************************************/ - -static GLenum oldtarget = TEXTURE0_ARB; - -void GL_SelectTexture (GLenum target) -{ - if (!gl_mtexable) - return; -#ifndef __linux__ // no multitexture under Linux yet - qglActiveTexture (target); -#endif - if (target == oldtarget) - return; - cnttextures[oldtarget-TEXTURE0_ARB] = currenttexture; - currenttexture = cnttextures[target-TEXTURE0_ARB]; - oldtarget = target; -} +/* +Copyright (C) 1996-1997 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 "quakedef.h" +#include "version.h" + +extern unsigned char d_15to8table[65536]; +extern unsigned d_8to24table2[256]; +extern cvar_t crosshair, cl_crossx, cl_crossy, crosshaircolor; + +cvar_t gl_nobind = {"gl_nobind", "0"}; +cvar_t gl_max_size = {"gl_max_size", "1024"}; +cvar_t gl_picmip = {"gl_picmip", "0"}; +cvar_t gl_conalpha = {"gl_conalpha", "0.8"}; + +byte *draw_chars; // 8*8 graphic characters +qpic_t *draw_disc; +qpic_t *draw_backtile; + +int translate_texture; +int char_texture; +int cs2_texture; // crosshair 2 texture +int cs3_texture; // crosshair 3 texture + +static byte cs2_data[64] = { + 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, + 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, + 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff +}; + +static byte cs3_data[64] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff +}; + + +typedef struct +{ + int texnum; + float sl, tl, sh, th; +} glpic_t; + +int GL_LoadPicTexture (qpic_t *pic, byte *data); + +byte conback_buffer[sizeof(qpic_t) + sizeof(glpic_t)]; +qpic_t *conback = (qpic_t *)&conback_buffer; + +int gl_lightmap_format = 4; +int gl_solid_format = 3; +int gl_alpha_format = 4; + +int gl_filter_min = GL_LINEAR_MIPMAP_NEAREST; +int gl_filter_max = GL_LINEAR; + + +int texels; + +typedef struct +{ + int texnum; + char identifier[64]; + int width, height; + qboolean mipmap; +} gltexture_t; + +gltexture_t gltextures[MAX_GLTEXTURES]; +int numgltextures; + +void GL_Bind (int texnum) +{ + if (gl_nobind.value) + texnum = char_texture; + if (currenttexture == texnum) + return; + currenttexture = texnum; +#ifdef _WIN32 + bindTexFunc (GL_TEXTURE_2D, texnum); +#else + glBindTexture (GL_TEXTURE_2D, texnum); +#endif +} + + +/* +============================================================================= + + scrap allocation + + Allocate all the little status bar objects into a single texture + to crutch up stupid hardware / drivers + +============================================================================= +*/ + +// some cards have low quality of alpha pics, so load the pics +// without transparent pixels into a different scrap block. +// scrap 0 is solid pics, 1 is transparent +#define MAX_SCRAPS 2 +#define BLOCK_WIDTH 256 +#define BLOCK_HEIGHT 256 + +int scrap_allocated[MAX_SCRAPS][BLOCK_WIDTH]; +byte scrap_texels[MAX_SCRAPS][BLOCK_WIDTH*BLOCK_HEIGHT*4]; +int scrap_dirty = 0; // bit mask +int scrap_texnum; + +// returns false if allocation failed +qboolean Scrap_AllocBlock (int scrapnum, int w, int h, int *x, int *y) +{ + int i, j; + int best, best2; + + best = BLOCK_HEIGHT; + + for (i=0 ; i= best) + break; + if (scrap_allocated[scrapnum][i+j] > best2) + best2 = scrap_allocated[scrapnum][i+j]; + } + if (j == w) + { // this is a valid spot + *x = i; + *y = best = best2; + } + } + + if (best + h > BLOCK_HEIGHT) + return false; + + for (i=0 ; idata; + + // load little ones into the scrap + if (p->width < 64 && p->height < 64) + { + int x, y; + int i, j, k; + int texnum; + + texnum = memchr(p->data, 255, p->width*p->height) != NULL; + if (!Scrap_AllocBlock (texnum, p->width, p->height, &x, &y)) { + GL_LoadPicTexture (p, p->data); + return p; + } + k = 0; + for (i=0 ; iheight ; i++) + for (j=0 ; jwidth ; j++, k++) + scrap_texels[texnum][(y+i)*BLOCK_WIDTH + x + j] = p->data[k]; + texnum += scrap_texnum; + gl->texnum = texnum; + gl->sl = (x+0.01)/(float)BLOCK_WIDTH; + gl->sh = (x+p->width-0.01)/(float)BLOCK_WIDTH; + gl->tl = (y+0.01)/(float)BLOCK_WIDTH; + gl->th = (y+p->height-0.01)/(float)BLOCK_WIDTH; + + pic_count++; + pic_texels += p->width*p->height; + } + else + GL_LoadPicTexture (p, p->data); + + return p; +} + + +/* +================ +Draw_CachePic +================ +*/ +qpic_t *Draw_CachePic (char *path) +{ + cachepic_t *pic; + int i; + qpic_t *dat; + + for (pic=menu_cachepics, i=0 ; iname)) + return &pic->pic; + + if (menu_numcachepics == MAX_CACHED_PICS) + Sys_Error ("menu_numcachepics == MAX_CACHED_PICS"); + menu_numcachepics++; + strcpy (pic->name, path); + +// +// load the pic from disk +// + dat = (qpic_t *)COM_LoadTempFile (path); + if (!dat) + Sys_Error ("Draw_CachePic: failed to load %s", path); + SwapPic (dat); + + // HACK HACK HACK --- we need to keep the bytes for + // the translatable player picture just for the menu + // configuration dialog + if (!strcmp (path, "gfx/menuplyr.lmp")) + memcpy (menuplyr_pixels, dat->data, dat->width*dat->height); + + pic->pic.width = dat->width; + pic->pic.height = dat->height; + + GL_LoadPicTexture (&pic->pic, dat->data); + + return &pic->pic; +} + + +void Draw_CharToConback (int num, byte *dest) +{ + int row, col; + byte *source; + int drawline; + int x; + + row = num>>4; + col = num&15; + source = draw_chars + (row<<10) + (col<<3); + + drawline = 8; + + while (drawline--) + { + for (x=0 ; x<8 ; x++) + if (source[x] != 255) + dest[x] = 0x60 + source[x]; + source += 128; + dest += 320; + } + +} + +typedef struct +{ + char *name; + int minimize, maximize; +} glmode_t; + +glmode_t modes[] = { + {"GL_NEAREST", GL_NEAREST, GL_NEAREST}, + {"GL_LINEAR", GL_LINEAR, GL_LINEAR}, + {"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST}, + {"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR}, + {"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST}, + {"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR} +}; + +/* +=============== +Draw_TextureMode_f +=============== +*/ +void Draw_TextureMode_f (void) +{ + int i; + gltexture_t *glt; + + if (Cmd_Argc() == 1) + { + for (i=0 ; i< 6 ; i++) + if (gl_filter_min == modes[i].minimize) + { + Con_Printf ("%s\n", modes[i].name); + return; + } + Con_Printf ("current filter is unknown???\n"); + return; + } + + for (i=0 ; i< 6 ; i++) + { + if (!Q_strcasecmp (modes[i].name, Cmd_Argv(1) ) ) + break; + } + if (i == 6) + { + Con_Printf ("bad filter name\n"); + return; + } + + gl_filter_min = modes[i].minimize; + gl_filter_max = modes[i].maximize; + + // change all the existing mipmap texture objects + for (i=0, glt=gltextures ; imipmap) + { + GL_Bind (glt->texnum); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); + } + } +} + + +void Draw_LoadCharset (void) +{ + int i; + char buf[128*256]; + char *src, *dest; + + draw_chars = W_GetLumpName ("conchars"); + for (i=0 ; i<256*64 ; i++) + if (draw_chars[i] == 0) + draw_chars[i] = 255; // proper transparent color + + // Convert the 128*128 conchars texture to 128*256 leaving + // empty space between rows so that chars don't stumble on + // each other because of texture smoothing. + // This hack costs us 64K of GL texture memory + memset (buf, 255, sizeof(buf)); + src = draw_chars; + dest = buf; + for (i=0 ; i<16 ; i++) { + memcpy (dest, src, 128*8); + src += 128*8; + dest += 128*8*2; + } + + char_texture = GL_LoadTexture ("charset", 128, 256, buf, false, true, false); +} + + +/* +=============== +Draw_Init +=============== +*/ +void Draw_Init (void) +{ + qpic_t *cb; + byte *dest; + int x; + char ver[40]; + glpic_t *gl; + int start; + byte *ncdata; + + Cvar_RegisterVariable (&gl_nobind); + Cvar_RegisterVariable (&gl_max_size); + Cvar_RegisterVariable (&gl_picmip); + Cvar_RegisterVariable (&gl_conalpha); + + // 3dfx can only handle 256 wide textures + if (!Q_strncasecmp ((char *)gl_renderer, "3dfx",4) || + !Q_strncasecmp ((char *)gl_renderer, "Mesa",4)) + Cvar_Set (&gl_max_size, "256"); + + Cmd_AddCommand ("gl_texturemode", &Draw_TextureMode_f); + + // load the console background and the charset + // by hand, because we need to write the version + // string into the background before turning + // it into a texture + + Draw_LoadCharset (); + + cs2_texture = GL_LoadTexture ("", 8, 8, cs2_data, false, true, false); + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + cs3_texture = GL_LoadTexture ("", 8, 8, cs3_data, false, true, false); + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + start = Hunk_LowMark (); + + cb = (qpic_t *)COM_LoadHunkFile ("gfx/conback.lmp"); + if (!cb) + Sys_Error ("Couldn't load gfx/conback.lmp"); + SwapPic (cb); + +// sprintf (ver, "%4.2f", QW_VERSION); + strcpy (ver, QWE_VERSION); + dest = cb->data + 320 + 320*186 - 11 - 8*strlen(ver); + for (x=0 ; xwidth = vid.conwidth; + conback->height = vid.conheight; + + // scale console to vid size + dest = ncdata = Hunk_AllocName(vid.conwidth * vid.conheight, "conback"); + + for (y=0 ; ydata + cb->width * (y*cb->height/vid.conheight); + if (vid.conwidth == cb->width) + memcpy (dest, src, vid.conwidth); + else + { + f = 0; + fstep = cb->width*0x10000/vid.conwidth; + for (x=0 ; x>16]; + f += fstep; + dest[x+1] = src[f>>16]; + f += fstep; + dest[x+2] = src[f>>16]; + f += fstep; + dest[x+3] = src[f>>16]; + f += fstep; + } + } + } +#else + conback->width = cb->width; + conback->height = cb->height; + ncdata = cb->data; +#endif + + gl = (glpic_t *)conback->data; + gl->texnum = GL_LoadTexture ("conback", conback->width, conback->height, ncdata, false, false, false); + gl->sl = 0; + gl->sh = 1; + gl->tl = 0; + gl->th = 1; + conback->width = vid.conwidth; + conback->height = vid.conheight; + + // free loaded console + Hunk_FreeToLowMark (start); + + // save a texture slot for translated picture + translate_texture = texture_extension_number++; + + // save slots for scraps + scrap_texnum = texture_extension_number; + texture_extension_number += MAX_SCRAPS; + + // + // get the other pics we need + // + draw_disc = Draw_PicFromWad ("disc"); + draw_backtile = Draw_PicFromWad ("backtile"); +} + + + +/* +================ +Draw_Character + +Draws one 8*8 graphics character with 0 being transparent. +It can be clipped to the top of the screen to allow the console to be +smoothly scrolled off. +================ +*/ +void Draw_Character (int x, int y, int num) +{ + int row, col; + float frow, fcol; + + if (num == 32) + return; // space + + num &= 255; + + if (y <= -8) + return; // totally off screen + + row = num>>4; + col = num&15; + + frow = row*0.0625; + fcol = col*0.0625; + + GL_Bind (char_texture); + + glBegin (GL_QUADS); + glTexCoord2f (fcol, frow); + glVertex2f (x, y); + glTexCoord2f (fcol + (1.0/16), frow); + glVertex2f (x+8, y); + glTexCoord2f (fcol + (1.0/16), frow + (1.0/32)); + glVertex2f (x+8, y+8); + glTexCoord2f (fcol, frow + (1.0/32)); + glVertex2f (x, y+8); + glEnd (); +} + +/* +================ +Draw_String +================ +*/ +void Draw_String (int x, int y, char *str) +{ + while (*str) + { + Draw_Character (x, y, *str); + str++; + x += 8; + } +} + +/* +================ +Draw_Alt_String +================ +*/ +void Draw_Alt_String (int x, int y, char *str) +{ + while (*str) + { + Draw_Character (x, y, (*str) | 0x80); + str++; + x += 8; + } +} + +void Draw_Crosshair (void) +{ + int x, y; + int ofs1, ofs2; + extern vrect_t scr_vrect; + unsigned char *pColor; + + if (crosshair.value == 2 || crosshair.value == 3) { + x = scr_vrect.x + scr_vrect.width/2 + cl_crossx.value; + y = scr_vrect.y + scr_vrect.height/2 + cl_crossy.value; + + glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + pColor = (unsigned char *) &d_8to24table[(byte) crosshaircolor.value]; + glColor4ubv ( pColor ); + if (crosshair.value == 2) + GL_Bind (cs2_texture); + else + GL_Bind (cs3_texture); + + if (vid.width == 320) { + ofs1 = 3;//3.5; + ofs2 = 5;//4.5; + } else { + ofs1 = 7; + ofs2 = 9; + } + glBegin (GL_QUADS); + glTexCoord2f (0, 0); + glVertex2f (x - ofs1, y - ofs1); + glTexCoord2f (1, 0); + glVertex2f (x + ofs2, y - ofs1); + glTexCoord2f (1, 1); + glVertex2f (x + ofs2, y + ofs2); + glTexCoord2f (0, 1); + glVertex2f (x - ofs1, y + ofs2); + glEnd (); + + glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + } else if (crosshair.value) + Draw_Character (scr_vrect.x + scr_vrect.width/2-4 + cl_crossx.value, + scr_vrect.y + scr_vrect.height/2-4 + cl_crossy.value, + '+'); +} + +/* +================ +Draw_DebugChar + +Draws a single character directly to the upper right corner of the screen. +This is for debugging lockups by drawing different chars in different parts +of the code. +================ +*/ +void Draw_DebugChar (char num) +{ +} + +/* +============= +Draw_Pic +============= +*/ +void Draw_Pic (int x, int y, qpic_t *pic) +{ + glpic_t *gl; + + if (scrap_dirty) + Scrap_Upload (); + gl = (glpic_t *)pic->data; + glColor4f (1,1,1,1); + GL_Bind (gl->texnum); + glBegin (GL_QUADS); + glTexCoord2f (gl->sl, gl->tl); + glVertex2f (x, y); + glTexCoord2f (gl->sh, gl->tl); + glVertex2f (x+pic->width, y); + glTexCoord2f (gl->sh, gl->th); + glVertex2f (x+pic->width, y+pic->height); + glTexCoord2f (gl->sl, gl->th); + glVertex2f (x, y+pic->height); + glEnd (); +} + +/* +============= +Draw_AlphaPic +============= +*/ +void Draw_AlphaPic (int x, int y, qpic_t *pic, float alpha) +{ + glpic_t *gl; + + if (scrap_dirty) + Scrap_Upload (); + gl = (glpic_t *)pic->data; + glDisable(GL_ALPHA_TEST); + glEnable (GL_BLEND); +// glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glCullFace(GL_FRONT); + glColor4f (1,1,1,alpha); + GL_Bind (gl->texnum); + glBegin (GL_QUADS); + glTexCoord2f (gl->sl, gl->tl); + glVertex2f (x, y); + glTexCoord2f (gl->sh, gl->tl); + glVertex2f (x+pic->width, y); + glTexCoord2f (gl->sh, gl->th); + glVertex2f (x+pic->width, y+pic->height); + glTexCoord2f (gl->sl, gl->th); + glVertex2f (x, y+pic->height); + glEnd (); + glColor4f (1,1,1,1); + glEnable(GL_ALPHA_TEST); + glDisable (GL_BLEND); +} + +void Draw_SubPic(int x, int y, qpic_t *pic, int srcx, int srcy, int width, int height) +{ + glpic_t *gl; + float newsl, newtl, newsh, newth; + float oldglwidth, oldglheight; + + if (scrap_dirty) + Scrap_Upload (); + gl = (glpic_t *)pic->data; + + oldglwidth = gl->sh - gl->sl; + oldglheight = gl->th - gl->tl; + + newsl = gl->sl + (srcx*oldglwidth)/pic->width; + newsh = newsl + (width*oldglwidth)/pic->width; + + newtl = gl->tl + (srcy*oldglheight)/pic->height; + newth = newtl + (height*oldglheight)/pic->height; + + glColor4f (1,1,1,1); + GL_Bind (gl->texnum); + glBegin (GL_QUADS); + glTexCoord2f (newsl, newtl); + glVertex2f (x, y); + glTexCoord2f (newsh, newtl); + glVertex2f (x+width, y); + glTexCoord2f (newsh, newth); + glVertex2f (x+width, y+height); + glTexCoord2f (newsl, newth); + glVertex2f (x, y+height); + glEnd (); +} + +/* +============= +Draw_TransPic +============= +*/ +void Draw_TransPic (int x, int y, qpic_t *pic) +{ + + if (x < 0 || (unsigned)(x + pic->width) > vid.width || y < 0 || + (unsigned)(y + pic->height) > vid.height) + { + Sys_Error ("Draw_TransPic: bad coordinates"); + } + + Draw_Pic (x, y, pic); +} + + +/* +============= +Draw_TransPicTranslate + +Only used for the player color selection menu +============= +*/ +void Draw_TransPicTranslate (int x, int y, qpic_t *pic, byte *translation) +{ + int v, u, c; + unsigned trans[64*64], *dest; + byte *src; + int p; + + GL_Bind (translate_texture); + + c = pic->width * pic->height; + + dest = trans; + for (v=0 ; v<64 ; v++, dest += 64) + { + src = &menuplyr_pixels[ ((v*pic->height)>>6) *pic->width]; + for (u=0 ; u<64 ; u++) + { + p = src[(u*pic->width)>>6]; + if (p == 255) + dest[u] = p; + else + dest[u] = d_8to24table[translation[p]]; + } + } + + glTexImage2D (GL_TEXTURE_2D, 0, gl_alpha_format, 64, 64, 0, GL_RGBA, GL_UNSIGNED_BYTE, trans); + + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glColor3f (1,1,1); + glBegin (GL_QUADS); + glTexCoord2f (0, 0); + glVertex2f (x, y); + glTexCoord2f (1, 0); + glVertex2f (x+pic->width, y); + glTexCoord2f (1, 1); + glVertex2f (x+pic->width, y+pic->height); + glTexCoord2f (0, 1); + glVertex2f (x, y+pic->height); + glEnd (); +} + + +/* +================ +Draw_ConsoleBackground + +================ +*/ +void Draw_ConsoleBackground (int lines) +{ + char ver[80]; + int x, y, i; + + if (lines == vid.height) + Draw_Pic(0, lines - vid.height, conback); + else + Draw_AlphaPic (0, lines - vid.height, conback, gl_conalpha.value); + + y = lines-14; + if (!cls.download) { +#ifdef __linux__ + sprintf (ver, "LinuxGL (%4.2f) QuakeWorld", LINUX_VERSION); +#else +// sprintf (ver, "GL (%4.2f) QuakeWorld", GLQUAKE_VERSION); + sprintf (ver, "GL (%4.2f) QWExtended client", GLQUAKE_VERSION); +#endif + x = vid.conwidth - (strlen(ver)*8 + 11) - (vid.conwidth*8/320)*7; + for (i=0 ; idata); + glBegin (GL_QUADS); + glTexCoord2f (x/64.0, y/64.0); + glVertex2f (x, y); + glTexCoord2f ( (x+w)/64.0, y/64.0); + glVertex2f (x+w, y); + glTexCoord2f ( (x+w)/64.0, (y+h)/64.0); + glVertex2f (x+w, y+h); + glTexCoord2f ( x/64.0, (y+h)/64.0 ); + glVertex2f (x, y+h); + glEnd (); +} + + +/* +============= +Draw_Fill + +Fills a box of pixels with a single color +============= +*/ +void Draw_Fill (int x, int y, int w, int h, int c) +{ + glDisable (GL_TEXTURE_2D); + glColor3f (host_basepal[c*3]/255.0, + host_basepal[c*3+1]/255.0, + host_basepal[c*3+2]/255.0); + + glBegin (GL_QUADS); + + glVertex2f (x,y); + glVertex2f (x+w, y); + glVertex2f (x+w, y+h); + glVertex2f (x, y+h); + + glEnd (); + glColor3f (1,1,1); + glEnable (GL_TEXTURE_2D); +} +//============================================================================= + +/* +================ +Draw_FadeScreen + +================ +*/ +void Draw_FadeScreen (void) +{ + glEnable (GL_BLEND); + glDisable (GL_TEXTURE_2D); + glColor4f (0, 0, 0, 0.7); + glBegin (GL_QUADS); + + glVertex2f (0,0); + glVertex2f (vid.width, 0); + glVertex2f (vid.width, vid.height); + glVertex2f (0, vid.height); + + glEnd (); + glColor4f (1,1,1,1); + glEnable (GL_TEXTURE_2D); + glDisable (GL_BLEND); + + Sbar_Changed(); +} + +//============================================================================= + +/* +================ +Draw_BeginDisc + +Draws the little blue disc in the corner of the screen. +Call before beginning any disc IO. +================ +*/ +void Draw_BeginDisc (void) +{ + if (!draw_disc) + return; + glDrawBuffer (GL_FRONT); + Draw_Pic (vid.width - 24, 0, draw_disc); + glDrawBuffer (GL_BACK); +} + + +/* +================ +Draw_EndDisc + +Erases the disc icon. +Call after completing any disc IO +================ +*/ +void Draw_EndDisc (void) +{ +} + +/* +================ +GL_Set2D + +Setup as if the screen was 320*200 +================ +*/ +void GL_Set2D (void) +{ + glViewport (glx, gly, glwidth, glheight); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity (); + glOrtho (0, vid.width, vid.height, 0, -99999, 99999); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity (); + + glDisable (GL_DEPTH_TEST); + glDisable (GL_CULL_FACE); + glDisable (GL_BLEND); + glEnable (GL_ALPHA_TEST); +// glDisable (GL_ALPHA_TEST); + + glColor4f (1,1,1,1); +} + +//==================================================================== + +/* +================ +GL_FindTexture +================ +*/ +int GL_FindTexture (char *identifier) +{ + int i; + gltexture_t *glt; + + for (i=0, glt=gltextures ; iidentifier)) + return gltextures[i].texnum; + } + + return -1; +} + +/* +================ +GL_ResampleTexture +================ +*/ +void GL_ResampleTexture (unsigned *in, int inwidth, int inheight, unsigned *out, int outwidth, int outheight) +{ + int i, j; + unsigned *inrow; + unsigned frac, fracstep; + + fracstep = inwidth*0x10000/outwidth; + for (i=0 ; i> 1; + for (j=0 ; j>16]; + frac += fracstep; + out[j+1] = inrow[frac>>16]; + frac += fracstep; + out[j+2] = inrow[frac>>16]; + frac += fracstep; + out[j+3] = inrow[frac>>16]; + frac += fracstep; + } + } +} + +/* +================ +GL_Resample8BitTexture -- JACK +================ +*/ +void GL_Resample8BitTexture (unsigned char *in, int inwidth, int inheight, unsigned char *out, int outwidth, int outheight) +{ + int i, j; + unsigned char *inrow; + unsigned frac, fracstep; + + fracstep = inwidth*0x10000/outwidth; + for (i=0 ; i> 1; + for (j=0 ; j>16]; + frac += fracstep; + out[j+1] = inrow[frac>>16]; + frac += fracstep; + out[j+2] = inrow[frac>>16]; + frac += fracstep; + out[j+3] = inrow[frac>>16]; + frac += fracstep; + } + } +} + +/* +================ +GL_MipMap + +Operates in place, quartering the size of the texture +================ +*/ +void GL_MipMap (byte *in, int width, int height) +{ + int i, j; + byte *out; + + width <<=2; + height >>= 1; + out = in; + for (i=0 ; i>2; + out[1] = (in[1] + in[5] + in[width+1] + in[width+5])>>2; + out[2] = (in[2] + in[6] + in[width+2] + in[width+6])>>2; + out[3] = (in[3] + in[7] + in[width+3] + in[width+7])>>2; + } + } +} + +/* +================ +GL_MipMap8Bit + +Mipping for 8 bit textures +================ +*/ +void GL_MipMap8Bit (byte *in, int width, int height) +{ + int i, j; + byte *out; + unsigned short r,g,b; + byte *at1, *at2, *at3, *at4; + + height >>= 1; + out = in; + for (i=0 ; i>=5; + g = (at1[1]+at2[1]+at3[1]+at4[1]); g>>=5; + b = (at1[2]+at2[2]+at3[2]+at4[2]); b>>=5; + + out[0] = d_15to8table[(r<<0) + (g<<5) + (b<<10)]; + } +} + +/* +=============== +GL_Upload32 +=============== +*/ +void GL_Upload32 (unsigned *data, int width, int height, qboolean mipmap, qboolean alpha) +{ + int samples; +static unsigned scaled[1024*512]; // [512*256]; + int scaled_width, scaled_height; + + for (scaled_width = 1 ; scaled_width < width ; scaled_width<<=1) + ; + for (scaled_height = 1 ; scaled_height < height ; scaled_height<<=1) + ; + + if (mipmap) { + scaled_width >>= (int)gl_picmip.value; + scaled_height >>= (int)gl_picmip.value; + } + + if (scaled_width > gl_max_size.value) + scaled_width = gl_max_size.value; + if (scaled_height > gl_max_size.value) + scaled_height = gl_max_size.value; + if (scaled_width < 1) + scaled_width = 1; + if (scaled_height < 1) + scaled_height = 1; + + if (scaled_width * scaled_height > sizeof(scaled)/4) + Sys_Error ("GL_LoadTexture: too big"); + + samples = alpha ? gl_alpha_format : gl_solid_format; + +#if 0 + if (mipmap) + gluBuild2DMipmaps (GL_TEXTURE_2D, samples, width, height, GL_RGBA, GL_UNSIGNED_BYTE, trans); + else if (scaled_width == width && scaled_height == height) + glTexImage2D (GL_TEXTURE_2D, 0, samples, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, trans); + else + { + gluScaleImage (GL_RGBA, width, height, GL_UNSIGNED_BYTE, trans, + scaled_width, scaled_height, GL_UNSIGNED_BYTE, scaled); + glTexImage2D (GL_TEXTURE_2D, 0, samples, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled); + } +#else +texels += scaled_width * scaled_height; + + if (scaled_width == width && scaled_height == height) + { + if (!mipmap) + { + glTexImage2D (GL_TEXTURE_2D, 0, samples, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); + goto done; + } + memcpy (scaled, data, width*height*4); + } + else + GL_ResampleTexture (data, width, height, scaled, scaled_width, scaled_height); + + glTexImage2D (GL_TEXTURE_2D, 0, samples, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled); + if (mipmap) + { + int miplevel; + + miplevel = 0; + while (scaled_width > 1 || scaled_height > 1) + { + GL_MipMap ((byte *)scaled, scaled_width, scaled_height); + scaled_width >>= 1; + scaled_height >>= 1; + if (scaled_width < 1) + scaled_width = 1; + if (scaled_height < 1) + scaled_height = 1; + miplevel++; + glTexImage2D (GL_TEXTURE_2D, miplevel, samples, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled); + } + } +done: ; +#endif + + + if (mipmap) + { + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); + } + else + { + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); + } +} + +void GL_Upload8_EXT (byte *data, int width, int height, qboolean mipmap, qboolean alpha) +{ + int i, s; + qboolean noalpha; + int samples; + static unsigned char scaled[1024*512]; // [512*256]; + int scaled_width, scaled_height; + + s = width*height; + // if there are no transparent pixels, make it a 3 component + // texture even if it was specified as otherwise + if (alpha) + { + noalpha = true; + for (i=0 ; i>= (int)gl_picmip.value; + scaled_height >>= (int)gl_picmip.value; + } + + if (scaled_width > gl_max_size.value) + scaled_width = gl_max_size.value; + if (scaled_height > gl_max_size.value) + scaled_height = gl_max_size.value; + if (scaled_width < 1) + scaled_width = 1; + if (scaled_height < 1) + scaled_height = 1; + + if (scaled_width * scaled_height > sizeof(scaled)) + Sys_Error ("GL_LoadTexture: too big"); + + samples = 1; // alpha ? gl_alpha_format : gl_solid_format; + + texels += scaled_width * scaled_height; + + if (scaled_width == width && scaled_height == height) + { + if (!mipmap) + { + glTexImage2D (GL_TEXTURE_2D, 0, GL_COLOR_INDEX8_EXT, scaled_width, scaled_height, 0, GL_COLOR_INDEX , GL_UNSIGNED_BYTE, data); + goto done; + } + memcpy (scaled, data, width*height); + } + else + GL_Resample8BitTexture (data, width, height, scaled, scaled_width, scaled_height); + + glTexImage2D (GL_TEXTURE_2D, 0, GL_COLOR_INDEX8_EXT, scaled_width, scaled_height, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, scaled); + if (mipmap) + { + int miplevel; + + miplevel = 0; + while (scaled_width > 1 || scaled_height > 1) + { + GL_MipMap8Bit ((byte *)scaled, scaled_width, scaled_height); + scaled_width >>= 1; + scaled_height >>= 1; + if (scaled_width < 1) + scaled_width = 1; + if (scaled_height < 1) + scaled_height = 1; + miplevel++; + glTexImage2D (GL_TEXTURE_2D, miplevel, GL_COLOR_INDEX8_EXT, scaled_width, scaled_height, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, scaled); + } + } +done: ; + + if (mipmap) + { + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); + } + else + { + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); + } +} + +extern qboolean VID_Is8bit(); + +/* +=============== +GL_Upload8 +=============== +*/ +void GL_Upload8 (byte *data, int width, int height, qboolean mipmap, qboolean alpha, qboolean brighten) +{ +static unsigned trans[640*480]; // FIXME, temporary + int i, s; + qboolean noalpha; + int p; + unsigned *table; + + if (brighten) + table = d_8to24table2; + else + table = d_8to24table; + + s = width*height; + + if (alpha == 2) + { + // this is a fullbright mask, so make all non-fullbright + // colors transparent + for (i=0 ; iidentifier) + && width == glt->width && height == glt->height) + return gltextures[i].texnum; + } + } + else + glt = &gltextures[numgltextures]; + + if (numgltextures == MAX_GLTEXTURES) + Sys_Error ("GL_LoadTexture: numgltextures == MAX_GLTEXTURES"); + numgltextures++; + + strcpy (glt->identifier, identifier); + glt->texnum = texture_extension_number; + glt->width = width; + glt->height = height; + glt->mipmap = mipmap; + + GL_Bind (texture_extension_number); + + GL_Upload8 (data, width, height, mipmap, alpha, brighten); + + texture_extension_number++; + + return texture_extension_number-1; +} + +/* +================ +GL_LoadPicTexture + +NOTE: qpic_t must point to a buffer that will fit both +qpic_t and appended glpic_t + +FIXME: this is messy +Merge qpic_t and glpic_t into one struct (dpic_t)? +================ +*/ +int GL_LoadPicTexture (qpic_t *pic, byte *data) +{ + glpic_t *gl; + int glwidth, glheight; + int i; + + gl = (glpic_t *)pic->data; + + for (glwidth = 1 ; glwidth < pic->width ; glwidth<<=1) + ; + for (glheight = 1 ; glheight < pic->height ; glheight<<=1) + ; + + if (glwidth == pic->width && glheight == pic->height) + { + gl->texnum = GL_LoadTexture ("", glwidth, glheight, data, + false, true, false); + gl->sl = 0; + gl->sh = 1; + gl->tl = 0; + gl->th = 1; + } + else + { + byte *src, *dest; + byte *buf; + + buf = Q_Malloc (glwidth*glheight); + + memset (buf, 0, glwidth*glheight); + src = data; + dest = buf; + for (i=0 ; iheight ; i++) { + memcpy (dest, src, pic->width); + src += pic->width; + dest += glwidth; + } + + gl->texnum = GL_LoadTexture ("", glwidth, glheight, buf, + false, true, false); + gl->sl = 0; + gl->sh = (float)pic->width / glwidth; + gl->tl = 0; + gl->th = (float)pic->height / glheight; + + free (buf); + } + + return gl->texnum; +} + +/****************************************/ + +static GLenum oldtarget = TEXTURE0_ARB; + +void GL_SelectTexture (GLenum target) +{ + if (!gl_mtexable) + return; +#ifndef __linux__ // no multitexture under Linux yet + qglActiveTexture (target); +#endif + if (target == oldtarget) + return; + cnttextures[oldtarget-TEXTURE0_ARB] = currenttexture; + currenttexture = cnttextures[target-TEXTURE0_ARB]; + oldtarget = target; +} diff --git a/source/gl_mesh.c b/source/gl_mesh.c index 8f99014a..518e145e 100644 --- a/source/gl_mesh.c +++ b/source/gl_mesh.c @@ -1,366 +1,366 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// gl_mesh.c: triangle model functions - -#include "quakedef.h" - -/* -================================================================= - -ALIAS MODEL DISPLAY LIST GENERATION - -================================================================= -*/ - -model_t *aliasmodel; -aliashdr_t *paliashdr; - -qboolean used[8192]; - -// the command list holds counts and s/t values that are valid for -// every frame -int commands[8192]; -int numcommands; - -// all frames will have their vertexes rearranged and expanded -// so they are in the order expected by the command list -int vertexorder[8192]; -int numorder; - -int allverts, alltris; - -int stripverts[128]; -int striptris[128]; -int stripcount; - -/* -================ -StripLength -================ -*/ -int StripLength (int starttri, int startv) -{ - int m1, m2; - int j; - mtriangle_t *last, *check; - int k; - - used[starttri] = 2; - - last = &triangles[starttri]; - - stripverts[0] = last->vertindex[(startv)%3]; - stripverts[1] = last->vertindex[(startv+1)%3]; - stripverts[2] = last->vertindex[(startv+2)%3]; - - striptris[0] = starttri; - stripcount = 1; - - m1 = last->vertindex[(startv+2)%3]; - m2 = last->vertindex[(startv+1)%3]; - - // look for a matching triangle -nexttri: - for (j=starttri+1, check=&triangles[starttri+1] ; jnumtris ; j++, check++) - { - if (check->facesfront != last->facesfront) - continue; - for (k=0 ; k<3 ; k++) - { - if (check->vertindex[k] != m1) - continue; - if (check->vertindex[ (k+1)%3 ] != m2) - continue; - - // this is the next part of the fan - - // if we can't use this triangle, this tristrip is done - if (used[j]) - goto done; - - // the new edge - if (stripcount & 1) - m2 = check->vertindex[ (k+2)%3 ]; - else - m1 = check->vertindex[ (k+2)%3 ]; - - stripverts[stripcount+2] = check->vertindex[ (k+2)%3 ]; - striptris[stripcount] = j; - stripcount++; - - used[j] = 2; - goto nexttri; - } - } -done: - - // clear the temp used flags - for (j=starttri+1 ; jnumtris ; j++) - if (used[j] == 2) - used[j] = 0; - - return stripcount; -} - -/* -=========== -FanLength -=========== -*/ -int FanLength (int starttri, int startv) -{ - int m1, m2; - int j; - mtriangle_t *last, *check; - int k; - - used[starttri] = 2; - - last = &triangles[starttri]; - - stripverts[0] = last->vertindex[(startv)%3]; - stripverts[1] = last->vertindex[(startv+1)%3]; - stripverts[2] = last->vertindex[(startv+2)%3]; - - striptris[0] = starttri; - stripcount = 1; - - m1 = last->vertindex[(startv+0)%3]; - m2 = last->vertindex[(startv+2)%3]; - - - // look for a matching triangle -nexttri: - for (j=starttri+1, check=&triangles[starttri+1] ; jnumtris ; j++, check++) - { - if (check->facesfront != last->facesfront) - continue; - for (k=0 ; k<3 ; k++) - { - if (check->vertindex[k] != m1) - continue; - if (check->vertindex[ (k+1)%3 ] != m2) - continue; - - // this is the next part of the fan - - // if we can't use this triangle, this tristrip is done - if (used[j]) - goto done; - - // the new edge - m2 = check->vertindex[ (k+2)%3 ]; - - stripverts[stripcount+2] = m2; - striptris[stripcount] = j; - stripcount++; - - used[j] = 2; - goto nexttri; - } - } -done: - - // clear the temp used flags - for (j=starttri+1 ; jnumtris ; j++) - if (used[j] == 2) - used[j] = 0; - - return stripcount; -} - - -/* -================ -BuildTris - -Generate a list of trifans or strips -for the model, which holds for all frames -================ -*/ -void BuildTris (void) -{ - int i, j, k; - int startv; - float s, t; - int len, bestlen, besttype; - int bestverts[1024]; - int besttris[1024]; - int type; - - // - // build tristrips - // - numorder = 0; - numcommands = 0; - memset (used, 0, sizeof(used)); - for (i=0 ; inumtris ; i++) - { - // pick an unused triangle and start the trifan - if (used[i]) - continue; - - bestlen = 0; - for (type = 0 ; type < 2 ; type++) -// type = 1; - { - for (startv =0 ; startv < 3 ; startv++) - { - if (type == 1) - len = StripLength (i, startv); - else - len = FanLength (i, startv); - if (len > bestlen) - { - besttype = type; - bestlen = len; - for (j=0 ; jskinwidth / 2; // on back side - s = (s + 0.5) / pheader->skinwidth; - t = (t + 0.5) / pheader->skinheight; - - *(float *)&commands[numcommands++] = s; - *(float *)&commands[numcommands++] = t; - } - } - - commands[numcommands++] = 0; // end of list marker - - Con_DPrintf ("%3i tri %3i vert %3i cmd\n", pheader->numtris, numorder, numcommands); - - allverts += numorder; - alltris += pheader->numtris; -} - - -/* -================ -GL_MakeAliasModelDisplayLists -================ -*/ -void GL_MakeAliasModelDisplayLists (model_t *m, aliashdr_t *hdr) -{ - int i, j; - int *cmds; - trivertx_t *verts; -#if 0 - char cache[MAX_QPATH], fullpath[MAX_OSPATH]; - FILE *f; -#endif - - aliasmodel = m; - paliashdr = hdr; // (aliashdr_t *)Mod_Extradata (m); - -#if 0 - // - // look for a cached version - // - strcpy (cache, "glquake/"); - COM_StripExtension (m->name+strlen("progs/"), cache+strlen("glquake/")); - strcat (cache, ".ms2"); - - COM_FOpenFile (cache, &f); - if (f) - { - fread (&numcommands, 4, 1, f); - fread (&numorder, 4, 1, f); - fread (&commands, numcommands * sizeof(commands[0]), 1, f); - fread (&vertexorder, numorder * sizeof(vertexorder[0]), 1, f); - fclose (f); - } - else - { - // - // build it from scratch - // - Con_Printf ("meshing %s...\n",m->name); - - BuildTris (); // trifans or lists - - // - // save out the cached version - // - sprintf (fullpath, "%s/%s", com_gamedir, cache); - f = fopen (fullpath, "wb"); - if (!f) { - char gldir[MAX_OSPATH]; - - sprintf (gldir, "%s/glquake", com_gamedir); - Sys_mkdir (gldir); - f = fopen (fullpath, "wb"); - } - - if (f) - { - fwrite (&numcommands, 4, 1, f); - fwrite (&numorder, 4, 1, f); - fwrite (&commands, numcommands * sizeof(commands[0]), 1, f); - fwrite (&vertexorder, numorder * sizeof(vertexorder[0]), 1, f); - fclose (f); - } - } -#else - // Tonik: don't cache anything, because it's seems just as fast - // (if not faster) to rebuild the tris instead of loading them from disk - BuildTris (); // trifans or lists -#endif - - // save the data out - - paliashdr->poseverts = numorder; - - cmds = Hunk_Alloc (numcommands * 4); - paliashdr->commands = (byte *)cmds - (byte *)paliashdr; - memcpy (cmds, commands, numcommands * 4); - - verts = Hunk_Alloc (paliashdr->numposes * paliashdr->poseverts - * sizeof(trivertx_t) ); - paliashdr->posedata = (byte *)verts - (byte *)paliashdr; - for (i=0 ; inumposes ; i++) - for (j=0 ; jvertindex[(startv)%3]; + stripverts[1] = last->vertindex[(startv+1)%3]; + stripverts[2] = last->vertindex[(startv+2)%3]; + + striptris[0] = starttri; + stripcount = 1; + + m1 = last->vertindex[(startv+2)%3]; + m2 = last->vertindex[(startv+1)%3]; + + // look for a matching triangle +nexttri: + for (j=starttri+1, check=&triangles[starttri+1] ; jnumtris ; j++, check++) + { + if (check->facesfront != last->facesfront) + continue; + for (k=0 ; k<3 ; k++) + { + if (check->vertindex[k] != m1) + continue; + if (check->vertindex[ (k+1)%3 ] != m2) + continue; + + // this is the next part of the fan + + // if we can't use this triangle, this tristrip is done + if (used[j]) + goto done; + + // the new edge + if (stripcount & 1) + m2 = check->vertindex[ (k+2)%3 ]; + else + m1 = check->vertindex[ (k+2)%3 ]; + + stripverts[stripcount+2] = check->vertindex[ (k+2)%3 ]; + striptris[stripcount] = j; + stripcount++; + + used[j] = 2; + goto nexttri; + } + } +done: + + // clear the temp used flags + for (j=starttri+1 ; jnumtris ; j++) + if (used[j] == 2) + used[j] = 0; + + return stripcount; +} + +/* +=========== +FanLength +=========== +*/ +int FanLength (int starttri, int startv) +{ + int m1, m2; + int j; + mtriangle_t *last, *check; + int k; + + used[starttri] = 2; + + last = &triangles[starttri]; + + stripverts[0] = last->vertindex[(startv)%3]; + stripverts[1] = last->vertindex[(startv+1)%3]; + stripverts[2] = last->vertindex[(startv+2)%3]; + + striptris[0] = starttri; + stripcount = 1; + + m1 = last->vertindex[(startv+0)%3]; + m2 = last->vertindex[(startv+2)%3]; + + + // look for a matching triangle +nexttri: + for (j=starttri+1, check=&triangles[starttri+1] ; jnumtris ; j++, check++) + { + if (check->facesfront != last->facesfront) + continue; + for (k=0 ; k<3 ; k++) + { + if (check->vertindex[k] != m1) + continue; + if (check->vertindex[ (k+1)%3 ] != m2) + continue; + + // this is the next part of the fan + + // if we can't use this triangle, this tristrip is done + if (used[j]) + goto done; + + // the new edge + m2 = check->vertindex[ (k+2)%3 ]; + + stripverts[stripcount+2] = m2; + striptris[stripcount] = j; + stripcount++; + + used[j] = 2; + goto nexttri; + } + } +done: + + // clear the temp used flags + for (j=starttri+1 ; jnumtris ; j++) + if (used[j] == 2) + used[j] = 0; + + return stripcount; +} + + +/* +================ +BuildTris + +Generate a list of trifans or strips +for the model, which holds for all frames +================ +*/ +void BuildTris (void) +{ + int i, j, k; + int startv; + float s, t; + int len, bestlen, besttype; + int bestverts[1024]; + int besttris[1024]; + int type; + + // + // build tristrips + // + numorder = 0; + numcommands = 0; + memset (used, 0, sizeof(used)); + for (i=0 ; inumtris ; i++) + { + // pick an unused triangle and start the trifan + if (used[i]) + continue; + + bestlen = 0; + for (type = 0 ; type < 2 ; type++) +// type = 1; + { + for (startv =0 ; startv < 3 ; startv++) + { + if (type == 1) + len = StripLength (i, startv); + else + len = FanLength (i, startv); + if (len > bestlen) + { + besttype = type; + bestlen = len; + for (j=0 ; jskinwidth / 2; // on back side + s = (s + 0.5) / pheader->skinwidth; + t = (t + 0.5) / pheader->skinheight; + + *(float *)&commands[numcommands++] = s; + *(float *)&commands[numcommands++] = t; + } + } + + commands[numcommands++] = 0; // end of list marker + + Con_DPrintf ("%3i tri %3i vert %3i cmd\n", pheader->numtris, numorder, numcommands); + + allverts += numorder; + alltris += pheader->numtris; +} + + +/* +================ +GL_MakeAliasModelDisplayLists +================ +*/ +void GL_MakeAliasModelDisplayLists (model_t *m, aliashdr_t *hdr) +{ + int i, j; + int *cmds; + trivertx_t *verts; +#if 0 + char cache[MAX_QPATH], fullpath[MAX_OSPATH]; + FILE *f; +#endif + + aliasmodel = m; + paliashdr = hdr; // (aliashdr_t *)Mod_Extradata (m); + +#if 0 + // + // look for a cached version + // + strcpy (cache, "glquake/"); + COM_StripExtension (m->name+strlen("progs/"), cache+strlen("glquake/")); + strcat (cache, ".ms2"); + + COM_FOpenFile (cache, &f); + if (f) + { + fread (&numcommands, 4, 1, f); + fread (&numorder, 4, 1, f); + fread (&commands, numcommands * sizeof(commands[0]), 1, f); + fread (&vertexorder, numorder * sizeof(vertexorder[0]), 1, f); + fclose (f); + } + else + { + // + // build it from scratch + // + Con_Printf ("meshing %s...\n",m->name); + + BuildTris (); // trifans or lists + + // + // save out the cached version + // + sprintf (fullpath, "%s/%s", com_gamedir, cache); + f = fopen (fullpath, "wb"); + if (!f) { + char gldir[MAX_OSPATH]; + + sprintf (gldir, "%s/glquake", com_gamedir); + Sys_mkdir (gldir); + f = fopen (fullpath, "wb"); + } + + if (f) + { + fwrite (&numcommands, 4, 1, f); + fwrite (&numorder, 4, 1, f); + fwrite (&commands, numcommands * sizeof(commands[0]), 1, f); + fwrite (&vertexorder, numorder * sizeof(vertexorder[0]), 1, f); + fclose (f); + } + } +#else + // Tonik: don't cache anything, because it's seems just as fast + // (if not faster) to rebuild the tris instead of loading them from disk + BuildTris (); // trifans or lists +#endif + + // save the data out + + paliashdr->poseverts = numorder; + + cmds = Hunk_Alloc (numcommands * 4); + paliashdr->commands = (byte *)cmds - (byte *)paliashdr; + memcpy (cmds, commands, numcommands * 4); + + verts = Hunk_Alloc (paliashdr->numposes * paliashdr->poseverts + * sizeof(trivertx_t) ); + paliashdr->posedata = (byte *)verts - (byte *)paliashdr; + for (i=0 ; inumposes ; i++) + for (j=0 ; jcache); - if (r) - return r; - - Mod_LoadModel (mod, true); - - if (!mod->cache.data) - Sys_Error ("Mod_Extradata: caching failed"); - return mod->cache.data; -} - -/* -=============== -Mod_PointInLeaf -=============== -*/ -mleaf_t *Mod_PointInLeaf (vec3_t p, model_t *model) -{ - mnode_t *node; - float d; - mplane_t *plane; - - if (!model || !model->nodes) - Sys_Error ("Mod_PointInLeaf: bad model"); - - node = model->nodes; - while (1) - { - if (node->contents < 0) - 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->numleafs+7)>>3; - out = decompressed; - -#if 0 - memcpy (out, in, row); -#else - 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); -#endif - - return decompressed; -} - -byte *Mod_LeafPVS (mleaf_t *leaf, model_t *model) -{ - if (leaf == model->leafs) - return mod_novis; - return Mod_DecompressVis (leaf->compressed_vis, model); -} - -/* -=================== -Mod_ClearAll -=================== -*/ -void Mod_ClearAll (void) -{ - int i; - model_t *mod; - - for (i=0 , mod=mod_known ; itype != mod_alias) - mod->needload = true; -} - -/* -================== -Mod_FindName - -================== -*/ -model_t *Mod_FindName (char *name) -{ - int i; - model_t *mod; - - if (!name[0]) - Sys_Error ("Mod_ForName: NULL name"); - -// -// search the currently loaded models -// - for (i=0 , mod=mod_known ; iname, name) ) - break; - - if (i == mod_numknown) - { - if (mod_numknown == MAX_MOD_KNOWN) - Sys_Error ("mod_numknown == MAX_MOD_KNOWN"); - strcpy (mod->name, name); - mod->needload = true; - mod_numknown++; - } - - return mod; -} - -/* -================== -Mod_TouchModel - -================== -*/ -void Mod_TouchModel (char *name) -{ - model_t *mod; - - mod = Mod_FindName (name); - - if (!mod->needload) - { - if (mod->type == mod_alias) - Cache_Check (&mod->cache); - } -} - -/* -================== -Mod_LoadModel - -Loads a model into the cache -================== -*/ -model_t *Mod_LoadModel (model_t *mod, qboolean crash) -{ - void *d; - unsigned *buf; - byte stackbuf[1024]; // avoid dirtying the cache heap - - if (!mod->needload) - { - if (mod->type == mod_alias) - { - d = Cache_Check (&mod->cache); - if (d) - return mod; - } - else - return mod; // not cached at all - } - -// -// because the world is so huge, load it one piece at a time -// - if (!crash) - { - - } - -// -// load the file -// - buf = (unsigned *)COM_LoadStackFile (mod->name, stackbuf, sizeof(stackbuf)); - if (!buf) - { - if (crash) - Sys_Error ("Mod_NumForName: %s not found", mod->name); - return NULL; - } - -// -// allocate a new model -// - COM_FileBase (mod->name, loadname); - - loadmodel = mod; - -// -// fill it in -// - -// call the apropriate loader - mod->needload = false; - - switch (LittleLong(*(unsigned *)buf)) - { - case IDPOLYHEADER: - Mod_LoadAliasModel (mod, buf); - break; - - case IDSPRITEHEADER: - Mod_LoadSpriteModel (mod, buf); - break; - - default: - Mod_LoadBrushModel (mod, buf); - break; - } - - return mod; -} - -/* -================== -Mod_ForName - -Loads in a model for the given name -================== -*/ -model_t *Mod_ForName (char *name, qboolean crash) -{ - model_t *mod; - - mod = Mod_FindName (name); - - return Mod_LoadModel (mod, crash); -} - - -static qboolean HasFullbrights (byte *pixels, int size) -{ - int i; - - for (i = 0; i < size; i++) - if (pixels[i] >= 224) - return true; - - return false; -} - - -/* -=============================================================================== - - BRUSHMODEL LOADING - -=============================================================================== -*/ - -byte *mod_base; - -/* -================= -Mod_LoadTextures -================= -*/ -void Mod_LoadTextures (lump_t *l) -{ - int i, j, pixels, num, max, altmax; - miptex_t *mt; - texture_t *tx, *tx2; - texture_t *anims[10]; - texture_t *altanims[10]; - dmiptexlump_t *m; - - if (!l->filelen) - { - loadmodel->textures = NULL; - return; - } - m = (dmiptexlump_t *)(mod_base + l->fileofs); - - m->nummiptex = LittleLong (m->nummiptex); - - loadmodel->numtextures = m->nummiptex; - loadmodel->textures = Hunk_AllocName (m->nummiptex * sizeof(*loadmodel->textures) , loadname); - - for (i=0 ; inummiptex ; i++) - { - m->dataofs[i] = LittleLong(m->dataofs[i]); - if (m->dataofs[i] == -1) - continue; - mt = (miptex_t *)((byte *)m + m->dataofs[i]); - mt->width = LittleLong (mt->width); - mt->height = LittleLong (mt->height); - for (j=0 ; joffsets[j] = LittleLong (mt->offsets[j]); - - if ( (mt->width & 15) || (mt->height & 15) ) - Sys_Error ("Texture %s is not 16 aligned", mt->name); -// pixels = mt->width*mt->height/64*85; - pixels = mt->width*mt->height; // Tonik: we don't need - // precalculated mip textures in GL - tx = Hunk_AllocName (sizeof(texture_t) + pixels, loadname); - loadmodel->textures[i] = tx; - - memcpy (tx->name, mt->name, sizeof(tx->name)); - tx->width = mt->width; - tx->height = mt->height; - for (j=0 ; joffsets[j] = mt->offsets[j] + sizeof(texture_t) - sizeof(miptex_t); - // the pixels immediately follow the structures - memcpy (tx+1, mt+1, pixels); - - // HACK HACK HACK - if (!strcmp(mt->name, "shot1sid") && mt->width==32 && mt->height==32 - && CRC_Block((byte*)(mt+1), mt->width*mt->height) == 65393) - { // This texture in b_shell1.bsp has some of the first 32 pixels painted white. - // They are invisible in software, but look really ugly in GL. So we just copy - // 32 pixels from the bottom to make it look nice. - memcpy (tx+1, (byte *)(tx+1) + 32*31, 32); - } - - if (!strncmp(mt->name,"sky",3)) - R_InitSky (tx); - else - { - texture_mode = GL_LINEAR_MIPMAP_NEAREST; //_LINEAR; - if (mt->name[0] == '*') // we don't brighten turb textures - tx->gl_texturenum = GL_LoadTexture (mt->name, tx->width, tx->height, (byte *)(tx+1), true, false, false); - else { - tx->gl_texturenum = GL_LoadTexture (mt->name, tx->width, tx->height, (byte *)(tx+1), true, false, true); - if (HasFullbrights((byte *)(tx+1), tx->width*tx->height)) { - tx->fb_texturenum = GL_LoadTexture (va("@fb_%s", mt->name), tx->width, tx->height, (byte *)(tx+1), - true, 2 /* Fullbright mask */, false); - } - } - texture_mode = GL_LINEAR; - } - } - -// -// sequence the animations -// - for (i=0 ; inummiptex ; i++) - { - tx = loadmodel->textures[i]; - if (!tx || tx->name[0] != '+') - continue; - if (tx->anim_next) - continue; // already sequenced - - // find the number of frames in the animation - memset (anims, 0, sizeof(anims)); - memset (altanims, 0, sizeof(altanims)); - - max = tx->name[1]; - altmax = 0; - if (max >= 'a' && max <= 'z') - max -= 'a' - 'A'; - if (max >= '0' && max <= '9') - { - max -= '0'; - altmax = 0; - anims[max] = tx; - max++; - } - else if (max >= 'A' && max <= 'J') - { - altmax = max - 'A'; - max = 0; - altanims[altmax] = tx; - altmax++; - } - else - Sys_Error ("Bad animating texture %s", tx->name); - - for (j=i+1 ; jnummiptex ; j++) - { - tx2 = loadmodel->textures[j]; - if (!tx2 || tx2->name[0] != '+') - continue; - if (strcmp (tx2->name+2, tx->name+2)) - continue; - - num = tx2->name[1]; - if (num >= 'a' && num <= 'z') - num -= 'a' - 'A'; - if (num >= '0' && num <= '9') - { - num -= '0'; - anims[num] = tx2; - if (num+1 > max) - max = num + 1; - } - else if (num >= 'A' && num <= 'J') - { - num = num - 'A'; - altanims[num] = tx2; - if (num+1 > altmax) - altmax = num+1; - } - else - Sys_Error ("Bad animating texture %s", tx->name); - } - -#define ANIM_CYCLE 2 - // link them all together - for (j=0 ; jname); - tx2->anim_total = max * ANIM_CYCLE; - tx2->anim_min = j * ANIM_CYCLE; - tx2->anim_max = (j+1) * ANIM_CYCLE; - tx2->anim_next = anims[ (j+1)%max ]; - if (altmax) - tx2->alternate_anims = altanims[0]; - } - for (j=0 ; jname); - tx2->anim_total = altmax * ANIM_CYCLE; - tx2->anim_min = j * ANIM_CYCLE; - tx2->anim_max = (j+1) * ANIM_CYCLE; - tx2->anim_next = altanims[ (j+1)%altmax ]; - if (max) - tx2->alternate_anims = anims[0]; - } - } -} - -/* -================= -Mod_LoadLighting -================= -*/ -void Mod_LoadLighting (lump_t *l) -{ - if (!l->filelen) - { - loadmodel->lightdata = NULL; - return; - } - loadmodel->lightdata = Hunk_AllocName ( l->filelen, loadname); - memcpy (loadmodel->lightdata, mod_base + l->fileofs, l->filelen); -} - - -/* -================= -Mod_LoadVisibility -================= -*/ -void Mod_LoadVisibility (lump_t *l) -{ - if (!l->filelen) - { - loadmodel->visdata = NULL; - return; - } - loadmodel->visdata = Hunk_AllocName ( l->filelen, loadname); - memcpy (loadmodel->visdata, mod_base + l->fileofs, l->filelen); -} - - -/* -================= -Mod_LoadEntities -================= -*/ -void Mod_LoadEntities (lump_t *l) -{ - if (!l->filelen) - { - loadmodel->entities = NULL; - return; - } - loadmodel->entities = Hunk_AllocName ( l->filelen, loadname); - memcpy (loadmodel->entities, mod_base + l->fileofs, l->filelen); -} - - -/* -================= -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)) - Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); - count = l->filelen / sizeof(*in); - out = Hunk_AllocName ( count*sizeof(*out), loadname); - - 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)) - Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); - count = l->filelen / sizeof(*in); - out = Hunk_AllocName ( count*sizeof(*out), loadname); - - 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]); - } - for (j=0 ; jheadnode[j] = LittleLong (in->headnode[j]); - out->visleafs = LittleLong (in->visleafs); - 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)) - Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); - count = l->filelen / sizeof(*in); - out = Hunk_AllocName ( (count + 1) * sizeof(*out), loadname); - - 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; - int i, j, count; - int miptex; - float len1, len2; - - in = (void *)(mod_base + l->fileofs); - if (l->filelen % sizeof(*in)) - Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); - count = l->filelen / sizeof(*in); - out = Hunk_AllocName ( count*sizeof(*out), loadname); - - loadmodel->texinfo = out; - loadmodel->numtexinfo = count; - - for ( i=0 ; ivecs[0][j] = LittleFloat (in->vecs[0][j]); - len1 = Length (out->vecs[0]); - len2 = Length (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; -#if 0 - if (len1 + len2 < 0.001) - out->mipadjust = 1; // don't crash - else - out->mipadjust = 1 / floor( (len1+len2)/2 + 0.1 ); -#endif - - miptex = LittleLong (in->miptex); - out->flags = LittleLong (in->flags); - - if (!loadmodel->textures) - { - out->texture = r_notexture_mip; // checkerboard texture - out->flags = 0; - } - else - { - if (miptex >= loadmodel->numtextures) - Sys_Error ("miptex >= loadmodel->numtextures"); - out->texture = loadmodel->textures[miptex]; - if (!out->texture) - { - out->texture = r_notexture_mip; // texture not found - out->flags = 0; - } - } - } -} - -/* -================ -CalcSurfaceExtents - -Fills in s->texturemins[] and s->extents[] -================ -*/ -void CalcSurfaceExtents (msurface_t *s) -{ - float mins[2], maxs[2], val; - int i,j, e; - mvertex_t *v; - 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++) - { - 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 ( !(tex->flags & TEX_SPECIAL) && s->extents[i] > 512 /* 256 */ ) - Sys_Error ("Bad surface extents"); - } -} - - -/* -================= -Mod_LoadFaces -================= -*/ -void Mod_LoadFaces (lump_t *l) -{ - dface_t *in; - msurface_t *out; - int i, count, surfnum; - int planenum, side; - - in = (void *)(mod_base + l->fileofs); - if (l->filelen % sizeof(*in)) - Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); - count = l->filelen / sizeof(*in); - out = Hunk_AllocName ( count*sizeof(*out), loadname); - - loadmodel->surfaces = out; - loadmodel->numsurfaces = count; - - for ( surfnum=0 ; surfnumfirstedge = LittleLong(in->firstedge); - out->numedges = LittleShort(in->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 - - for (i=0 ; istyles[i] = in->styles[i]; - i = LittleLong(in->lightofs); - if (i == -1) - out->samples = NULL; - else - out->samples = loadmodel->lightdata + i; - - // set the drawing flags flag - - if (!strncmp(out->texinfo->texture->name,"sky",3)) // sky - { - out->flags |= (SURF_DRAWSKY | SURF_DRAWTILED); -#ifndef QUAKE2 - GL_SubdivideSurface (out); // cut up polygon for warps -#endif - continue; - } - - if (!strncmp(out->texinfo->texture->name,"*",1)) // turbulent - { - out->flags |= (SURF_DRAWTURB | SURF_DRAWTILED); - for (i=0 ; i<2 ; i++) - { - out->extents[i] = 16384; - out->texturemins[i] = -8192; - } - GL_SubdivideSurface (out); // cut up polygon for warps - continue; - } - - } -} - - -/* -================= -Mod_SetParent -================= -*/ -void Mod_SetParent (mnode_t *node, mnode_t *parent) -{ - node->parent = parent; - if (node->contents < 0) - return; - Mod_SetParent (node->children[0], node); - Mod_SetParent (node->children[1], node); -} - -/* -================= -Mod_LoadNodes -================= -*/ -void Mod_LoadNodes (lump_t *l) -{ - int i, j, count, p; - dnode_t *in; - mnode_t *out; - - in = (void *)(mod_base + l->fileofs); - if (l->filelen % sizeof(*in)) - Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); - count = l->filelen / sizeof(*in); - out = Hunk_AllocName ( count*sizeof(*out), loadname); - - 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); - - for (j=0 ; j<2 ; j++) - { - p = LittleShort (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, p; - - in = (void *)(mod_base + l->fileofs); - if (l->filelen % sizeof(*in)) - Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); - count = l->filelen / sizeof(*in); - out = Hunk_AllocName ( count*sizeof(*out), loadname); - - loadmodel->leafs = out; - loadmodel->numleafs = count; - for ( i=0 ; iminmaxs[j] = LittleShort (in->mins[j]); - out->minmaxs[3+j] = LittleShort (in->maxs[j]); - } - - p = LittleLong(in->contents); - out->contents = p; - - out->firstmarksurface = loadmodel->marksurfaces + - LittleShort(in->firstmarksurface); - out->nummarksurfaces = LittleShort(in->nummarksurfaces); - - p = LittleLong(in->visofs); - if (p == -1) - out->compressed_vis = NULL; - else - out->compressed_vis = loadmodel->visdata + p; - out->efrags = NULL; - - for (j=0 ; j<4 ; j++) - out->ambient_sound_level[j] = in->ambient_level[j]; - } -} - -/* -================= -Mod_LoadClipnodes -================= -*/ -void Mod_LoadClipnodes (lump_t *l) -{ - dclipnode_t *in, *out; - int i, count; - hull_t *hull; - - in = (void *)(mod_base + l->fileofs); - if (l->filelen % sizeof(*in)) - Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); - count = l->filelen / sizeof(*in); - out = Hunk_AllocName ( count*sizeof(*out), loadname); - - loadmodel->clipnodes = out; - loadmodel->numclipnodes = count; - - hull = &loadmodel->hulls[1]; - hull->clipnodes = out; - hull->firstclipnode = 0; - hull->lastclipnode = count-1; - hull->planes = loadmodel->planes; - hull->clip_mins[0] = -16; - hull->clip_mins[1] = -16; - hull->clip_mins[2] = -24; - hull->clip_maxs[0] = 16; - hull->clip_maxs[1] = 16; - hull->clip_maxs[2] = 32; - - hull = &loadmodel->hulls[2]; - hull->clipnodes = out; - hull->firstclipnode = 0; - hull->lastclipnode = count-1; - hull->planes = loadmodel->planes; - hull->clip_mins[0] = -32; - hull->clip_mins[1] = -32; - hull->clip_mins[2] = -24; - hull->clip_maxs[0] = 32; - hull->clip_maxs[1] = 32; - hull->clip_maxs[2] = 64; - - for (i=0 ; iplanenum = LittleLong(in->planenum); - out->children[0] = LittleShort(in->children[0]); - out->children[1] = LittleShort(in->children[1]); - } -} - -/* -================= -Mod_MakeHull0 - -Deplicate the drawing hull structure as a clipping hull -================= -*/ -void Mod_MakeHull0 (void) -{ - mnode_t *in, *child; - dclipnode_t *out; - int i, j, count; - hull_t *hull; - - hull = &loadmodel->hulls[0]; - - in = loadmodel->nodes; - count = loadmodel->numnodes; - out = Hunk_AllocName ( count*sizeof(*out), loadname); - - hull->clipnodes = out; - hull->firstclipnode = 0; - hull->lastclipnode = count-1; - hull->planes = loadmodel->planes; - - for (i=0 ; iplanenum = in->plane - loadmodel->planes; - for (j=0 ; j<2 ; j++) - { - child = in->children[j]; - if (child->contents < 0) - out->children[j] = child->contents; - else - out->children[j] = child - loadmodel->nodes; - } - } -} - -/* -================= -Mod_LoadMarksurfaces -================= -*/ -void Mod_LoadMarksurfaces (lump_t *l) -{ - int i, j, count; - short *in; - msurface_t **out; - - in = (void *)(mod_base + l->fileofs); - if (l->filelen % sizeof(*in)) - Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); - count = l->filelen / sizeof(*in); - out = Hunk_AllocName ( count*sizeof(*out), loadname); - - loadmodel->marksurfaces = out; - loadmodel->nummarksurfaces = count; - - for ( i=0 ; i= loadmodel->numsurfaces) - Sys_Error ("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)) - Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); - count = l->filelen / sizeof(*in); - out = Hunk_AllocName ( count*sizeof(*out), loadname); - - loadmodel->surfedges = out; - loadmodel->numsurfedges = count; - - for ( i=0 ; ifileofs); - if (l->filelen % sizeof(*in)) - Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); - count = l->filelen / sizeof(*in); - out = Hunk_AllocName ( count*2*sizeof(*out), loadname); - - 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; - } -} - -/* -================= -RadiusFromBounds -================= -*/ -float RadiusFromBounds (vec3_t mins, vec3_t maxs) -{ - int i; - vec3_t corner; - - for (i=0 ; i<3 ; i++) - { - corner[i] = fabs(mins[i]) > fabs(maxs[i]) ? fabs(mins[i]) : fabs(maxs[i]); - } - - return Length (corner); -} - -/* -================= -Mod_LoadBrushModel -================= -*/ -void Mod_LoadBrushModel (model_t *mod, void *buffer) -{ - int i, j; - dheader_t *header; - dmodel_t *bm; - - loadmodel->type = mod_brush; - - header = (dheader_t *)buffer; - - i = LittleLong (header->version); - if (i != BSPVERSION) - Sys_Error ("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 ; ichecksum = 0; - mod->checksum2 = 0; - - for (i = 0; i < HEADER_LUMPS; i++) { - if (i == LUMP_ENTITIES) - continue; - mod->checksum ^= Com_BlockChecksum(mod_base + header->lumps[i].fileofs, - header->lumps[i].filelen); - - if (i == LUMP_VISIBILITY || i == LUMP_LEAFS || i == LUMP_NODES) - continue; - mod->checksum2 ^= Com_BlockChecksum(mod_base + header->lumps[i].fileofs, - header->lumps[i].filelen); - } - - -// load into heap - - Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]); - Mod_LoadEdges (&header->lumps[LUMP_EDGES]); - Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]); - Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]); - 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_MARKSURFACES]); - Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]); - Mod_LoadLeafs (&header->lumps[LUMP_LEAFS]); - Mod_LoadNodes (&header->lumps[LUMP_NODES]); - Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES]); - Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]); - Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]); - - Mod_MakeHull0 (); - - mod->numframes = 2; // regular and alternate animation - -// -// set up the submodels (FIXME: this is confusing) -// - for (i=0 ; inumsubmodels ; i++) - { - bm = &mod->submodels[i]; - - mod->hulls[0].firstclipnode = bm->headnode[0]; - for (j=1 ; jhulls[j].firstclipnode = bm->headnode[j]; - mod->hulls[j].lastclipnode = mod->numclipnodes-1; - } - - mod->firstmodelsurface = bm->firstface; - mod->nummodelsurfaces = bm->numfaces; - - VectorCopy (bm->maxs, mod->maxs); - VectorCopy (bm->mins, mod->mins); - - mod->radius = RadiusFromBounds (mod->mins, mod->maxs); - - mod->numleafs = bm->visleafs; - - if (i < mod->numsubmodels-1) - { // duplicate the basic information - char name[10]; - - sprintf (name, "*%i", i+1); - loadmodel = Mod_FindName (name); - *loadmodel = *mod; - strcpy (loadmodel->name, name); - mod = loadmodel; - } - } -} - -/* -============================================================================== - -ALIAS MODELS - -============================================================================== -*/ - -aliashdr_t *pheader; - -stvert_t stverts[MAXALIASVERTS]; -mtriangle_t triangles[MAXALIASTRIS]; - -// a pose is a single set of vertexes. a frame may be -// an animating sequence of poses -trivertx_t *poseverts[MAXALIASFRAMES]; -int posenum; - -byte player_8bit_texels[320*200]; - -/* -================= -Mod_LoadAliasFrame -================= -*/ -void * Mod_LoadAliasFrame (void * pin, maliasframedesc_t *frame) -{ - trivertx_t *pinframe; - int i; - daliasframe_t *pdaliasframe; - - pdaliasframe = (daliasframe_t *)pin; - - strcpy (frame->name, pdaliasframe->name); - frame->firstpose = posenum; - frame->numposes = 1; - - for (i=0 ; i<3 ; i++) - { - // these are byte values, so we don't have to worry about - // endianness - frame->bboxmin.v[i] = pdaliasframe->bboxmin.v[i]; - frame->bboxmin.v[i] = pdaliasframe->bboxmax.v[i]; - } - - pinframe = (trivertx_t *)(pdaliasframe + 1); - - poseverts[posenum] = pinframe; - posenum++; - - pinframe += pheader->numverts; - - return (void *)pinframe; -} - - -/* -================= -Mod_LoadAliasGroup -================= -*/ -void * Mod_LoadAliasGroup (void * pin, maliasframedesc_t *frame) -{ - daliasgroup_t *pingroup; - int i, numframes; - daliasinterval_t *pin_intervals; - void *ptemp; - - pingroup = (daliasgroup_t *)pin; - - numframes = LittleLong (pingroup->numframes); - - frame->firstpose = posenum; - frame->numposes = numframes; - - for (i=0 ; i<3 ; i++) - { - // these are byte values, so we don't have to worry about endianness - frame->bboxmin.v[i] = pingroup->bboxmin.v[i]; - frame->bboxmin.v[i] = pingroup->bboxmax.v[i]; - } - - pin_intervals = (daliasinterval_t *)(pingroup + 1); - - frame->interval = LittleFloat (pin_intervals->interval); - - pin_intervals += numframes; - - ptemp = (void *)pin_intervals; - - for (i=0 ; inumverts; - } - - return ptemp; -} - -//========================================================= - -/* -================= -Mod_FloodFillSkin - -Fill background pixels so mipmapping doesn't have haloes - Ed -================= -*/ - -typedef struct -{ - short x, y; -} floodfill_t; - -extern unsigned d_8to24table[]; - -// must be a power of 2 -#define FLOODFILL_FIFO_SIZE 0x1000 -#define FLOODFILL_FIFO_MASK (FLOODFILL_FIFO_SIZE - 1) - -#define FLOODFILL_STEP( off, dx, dy ) \ -{ \ - if (pos[off] == fillcolor) \ - { \ - pos[off] = 255; \ - fifo[inpt].x = x + (dx), fifo[inpt].y = y + (dy); \ - inpt = (inpt + 1) & FLOODFILL_FIFO_MASK; \ - } \ - else if (pos[off] != 255) fdc = pos[off]; \ -} - -void Mod_FloodFillSkin( byte *skin, int skinwidth, int skinheight ) -{ - byte fillcolor = *skin; // assume this is the pixel to fill - floodfill_t fifo[FLOODFILL_FIFO_SIZE]; - int inpt = 0, outpt = 0; - int filledcolor = -1; - int i; - - if (filledcolor == -1) - { - filledcolor = 0; - // attempt to find opaque black - for (i = 0; i < 256; ++i) - if (d_8to24table[i] == (255 << 0)) // alpha 1.0 - { - filledcolor = i; - break; - } - } - - // can't fill to filled color or to transparent color (used as visited marker) - if ((fillcolor == filledcolor) || (fillcolor == 255)) - { - //printf( "not filling skin from %d to %d\n", fillcolor, filledcolor ); - return; - } - - fifo[inpt].x = 0, fifo[inpt].y = 0; - inpt = (inpt + 1) & FLOODFILL_FIFO_MASK; - - while (outpt != inpt) - { - int x = fifo[outpt].x, y = fifo[outpt].y; - int fdc = filledcolor; - byte *pos = &skin[x + skinwidth * y]; - - outpt = (outpt + 1) & FLOODFILL_FIFO_MASK; - - if (x > 0) FLOODFILL_STEP( -1, -1, 0 ); - if (x < skinwidth - 1) FLOODFILL_STEP( 1, 1, 0 ); - if (y > 0) FLOODFILL_STEP( -skinwidth, 0, -1 ); - if (y < skinheight - 1) FLOODFILL_STEP( skinwidth, 0, 1 ); - skin[x + skinwidth * y] = fdc; - } -} - -/* -=============== -Mod_LoadAllSkins -=============== -*/ -void *Mod_LoadAllSkins (int numskins, daliasskintype_t *pskintype) -{ - int i, j, k; - char name[32]; - int s; - byte *skin; - daliasskingroup_t *pinskingroup; - int groupskins; - daliasskininterval_t *pinskinintervals; - - skin = (byte *)(pskintype + 1); - - if (numskins < 1 || numskins > MAX_SKINS) - Sys_Error ("Mod_LoadAliasModel: Invalid # of skins: %d\n", numskins); - - s = pheader->skinwidth * pheader->skinheight; - - for (i=0 ; itype == ALIAS_SKIN_SINGLE) { - Mod_FloodFillSkin (skin, pheader->skinwidth, pheader->skinheight); - - // save 8 bit texels for the player model to remap - if (!strcmp(loadmodel->name,"progs/player.mdl")) - { - if (s > sizeof(player_8bit_texels)) - Sys_Error ("Player skin too large"); - memcpy (player_8bit_texels, (byte *)(pskintype + 1), s); - } - _snprintf (name, sizeof(name)-1, "%s_%i", loadmodel->name, i); - pheader->gl_texturenum[i][0] = - pheader->gl_texturenum[i][1] = - pheader->gl_texturenum[i][2] = - pheader->gl_texturenum[i][3] = - GL_LoadTexture (name, pheader->skinwidth, pheader->skinheight, - (byte *)(pskintype + 1), true, false, false); - - if (HasFullbrights((byte *)(pskintype + 1), pheader->skinwidth*pheader->skinheight)) - pheader->fb_texturenum[i][0] = pheader->fb_texturenum[i][1] = - pheader->fb_texturenum[i][2] = pheader->fb_texturenum[i][3] = - GL_LoadTexture (va("@fb_%s", name), pheader->skinwidth, - pheader->skinheight, (byte *)(pskintype + 1), true, 2, false); - else - pheader->fb_texturenum[i][0] = pheader->fb_texturenum[i][1] = - pheader->fb_texturenum[i][2] = pheader->fb_texturenum[i][3] = 0; - - pskintype = (daliasskintype_t *)((byte *)(pskintype+1) + s); - } else { - // animating skin group. yuck. - pskintype++; - pinskingroup = (daliasskingroup_t *)pskintype; - groupskins = LittleLong (pinskingroup->numskins); - pinskinintervals = (daliasskininterval_t *)(pinskingroup + 1); - - pskintype = (void *)(pinskinintervals + groupskins); - - for (j=0 ; jskinwidth, pheader->skinheight); - _snprintf (name, sizeof(name)-1, "%s_%i_%i", loadmodel->name, i, j); - pheader->gl_texturenum[i][j&3] = - GL_LoadTexture (name, pheader->skinwidth, - pheader->skinheight, (byte *)(pskintype), true, false, false); - - if (HasFullbrights((byte *)(pskintype), pheader->skinwidth*pheader->skinheight)) - pheader->fb_texturenum[i][j&3] = - GL_LoadTexture (va("@fb_%s", name), pheader->skinwidth, - pheader->skinheight, (byte *)(pskintype), true, 2, false); - else - pheader->fb_texturenum[i][j&3] = 0; - - pskintype = (daliasskintype_t *)((byte *)(pskintype) + s); - } - k = j; - for (/* */; j < 4; j++) - pheader->gl_texturenum[i][j&3] = - pheader->gl_texturenum[i][j - k]; - } - } - - return (void *)pskintype; -} - - -//========================================================================= - -/* -================= -Mod_LoadAliasModel -================= -*/ -void Mod_LoadAliasModel (model_t *mod, void *buffer) -{ - int i, j; - mdl_t *pinmodel; - stvert_t *pinstverts; - dtriangle_t *pintriangles; - int version, numframes; - int size; - daliasframetype_t *pframetype; - daliasskintype_t *pskintype; - int start, end, total; - -// some models are special - if(!strcmp(mod->name, "progs/player.mdl")) - mod->modhint = MOD_PLAYER; - else if(!strcmp(mod->name, "progs/eyes.mdl")) - mod->modhint = MOD_EYES; - else if (!strcmp(mod->name, "progs/flame.mdl") || - !strcmp(mod->name, "progs/flame2.mdl")) - mod->modhint = MOD_FLAME; - else if (!strcmp(mod->name, "progs/bolt.mdl") || - !strcmp(mod->name, "progs/bolt2.mdl") || - !strcmp(mod->name, "progs/bolt3.mdl")) - mod->modhint = MOD_THUNDERBOLT; - - if (mod->modhint == MOD_PLAYER || mod->modhint == MOD_EYES) { - unsigned short crc; - byte *p; - int len; - char st[40]; - - CRC_Init(&crc); - for (len = com_filesize, p = buffer; len; len--, p++) - CRC_ProcessByte(&crc, *p); - - sprintf(st, "%d", (int) crc); - Info_SetValueForKey (cls.userinfo, - mod->modhint == MOD_PLAYER ? pmodel_name : emodel_name, - st, MAX_INFO_STRING); - - if (cls.state >= ca_connected) { - MSG_WriteByte (&cls.netchan.message, clc_stringcmd); - sprintf(st, "setinfo %s %d", - mod->modhint == MOD_PLAYER ? pmodel_name : emodel_name, - (int)crc); - SZ_Print (&cls.netchan.message, st); - } - } - - start = Hunk_LowMark (); - - pinmodel = (mdl_t *)buffer; - - version = LittleLong (pinmodel->version); - if (version != ALIAS_VERSION) - Sys_Error ("%s has wrong version number (%i should be %i)", - mod->name, version, ALIAS_VERSION); - -// -// allocate space for a working header, plus all the data except the frames, -// skin and group info -// - size = sizeof (aliashdr_t) - + (LittleLong (pinmodel->numframes) - 1) * - sizeof (pheader->frames[0]); - pheader = Hunk_AllocName (size, loadname); - - mod->flags = LittleLong (pinmodel->flags); - -// -// endian-adjust and copy the data, starting with the alias model header -// - pheader->boundingradius = LittleFloat (pinmodel->boundingradius); - pheader->numskins = LittleLong (pinmodel->numskins); - pheader->skinwidth = LittleLong (pinmodel->skinwidth); - pheader->skinheight = LittleLong (pinmodel->skinheight); - - if (pheader->skinheight > MAX_LBM_HEIGHT) - Sys_Error ("model %s has a skin taller than %d", mod->name, - MAX_LBM_HEIGHT); - - pheader->numverts = LittleLong (pinmodel->numverts); - - if (pheader->numverts <= 0) - Sys_Error ("model %s has no vertices", mod->name); - - if (pheader->numverts > MAXALIASVERTS) - Sys_Error ("model %s has too many vertices", mod->name); - - pheader->numtris = LittleLong (pinmodel->numtris); - - if (pheader->numtris <= 0) - Sys_Error ("model %s has no triangles", mod->name); - - pheader->numframes = LittleLong (pinmodel->numframes); - numframes = pheader->numframes; - if (numframes < 1) - Sys_Error ("Mod_LoadAliasModel: Invalid # of frames: %d\n", numframes); - - pheader->size = LittleFloat (pinmodel->size) * ALIAS_BASE_SIZE_RATIO; - mod->synctype = LittleLong (pinmodel->synctype); - mod->numframes = pheader->numframes; - - for (i=0 ; i<3 ; i++) - { - pheader->scale[i] = LittleFloat (pinmodel->scale[i]); - pheader->scale_origin[i] = LittleFloat (pinmodel->scale_origin[i]); - pheader->eyeposition[i] = LittleFloat (pinmodel->eyeposition[i]); - } - - -// -// load the skins -// - pskintype = (daliasskintype_t *)&pinmodel[1]; - pskintype = Mod_LoadAllSkins (pheader->numskins, pskintype); - -// -// load base s and t vertices -// - pinstverts = (stvert_t *)pskintype; - - for (i=0 ; inumverts ; i++) - { - stverts[i].onseam = LittleLong (pinstverts[i].onseam); - stverts[i].s = LittleLong (pinstverts[i].s); - stverts[i].t = LittleLong (pinstverts[i].t); - } - -// -// load triangle lists -// - pintriangles = (dtriangle_t *)&pinstverts[pheader->numverts]; - - for (i=0 ; inumtris ; i++) - { - triangles[i].facesfront = LittleLong (pintriangles[i].facesfront); - - for (j=0 ; j<3 ; j++) - { - triangles[i].vertindex[j] = - LittleLong (pintriangles[i].vertindex[j]); - } - } - -// -// load the frames -// - posenum = 0; - pframetype = (daliasframetype_t *)&pintriangles[pheader->numtris]; - - for (i=0 ; itype); - - if (frametype == ALIAS_SINGLE) - { - pframetype = (daliasframetype_t *) - Mod_LoadAliasFrame (pframetype + 1, &pheader->frames[i]); - } - else - { - pframetype = (daliasframetype_t *) - Mod_LoadAliasGroup (pframetype + 1, &pheader->frames[i]); - } - } - - pheader->numposes = posenum; - - mod->type = mod_alias; - -// FIXME: do this right - mod->mins[0] = mod->mins[1] = mod->mins[2] = -16; - mod->maxs[0] = mod->maxs[1] = mod->maxs[2] = 16; - - // - // build the draw lists - // - GL_MakeAliasModelDisplayLists (mod, pheader); - -// -// move the complete, relocatable alias model to the cache -// - end = Hunk_LowMark (); - total = end - start; - - Cache_Alloc (&mod->cache, total, loadname); - if (!mod->cache.data) - return; - memcpy (mod->cache.data, pheader, total); - - Hunk_FreeToLowMark (start); -} - -//============================================================================= - -/* -================= -Mod_LoadSpriteFrame -================= -*/ -void * Mod_LoadSpriteFrame (void * pin, mspriteframe_t **ppframe, int framenum) -{ - dspriteframe_t *pinframe; - mspriteframe_t *pspriteframe; - int width, height, size, origin[2]; - char name[64]; - - pinframe = (dspriteframe_t *)pin; - - width = LittleLong (pinframe->width); - height = LittleLong (pinframe->height); - size = width * height; - - pspriteframe = Hunk_AllocName (sizeof (mspriteframe_t),loadname); - - memset (pspriteframe, 0, sizeof (mspriteframe_t)); - - *ppframe = pspriteframe; - - pspriteframe->width = width; - pspriteframe->height = height; - origin[0] = LittleLong (pinframe->origin[0]); - origin[1] = LittleLong (pinframe->origin[1]); - - pspriteframe->up = origin[1]; - pspriteframe->down = origin[1] - height; - pspriteframe->left = origin[0]; - pspriteframe->right = width + origin[0]; - - sprintf (name, "%s_%i", loadmodel->name, framenum); - pspriteframe->gl_texturenum = GL_LoadTexture (name, width, height, (byte *)(pinframe + 1), true, true, false); - - return (void *)((byte *)pinframe + sizeof (dspriteframe_t) + size); -} - - -/* -================= -Mod_LoadSpriteGroup -================= -*/ -void * Mod_LoadSpriteGroup (void * pin, mspriteframe_t **ppframe, int framenum) -{ - dspritegroup_t *pingroup; - mspritegroup_t *pspritegroup; - int i, numframes; - dspriteinterval_t *pin_intervals; - float *poutintervals; - void *ptemp; - - pingroup = (dspritegroup_t *)pin; - - numframes = LittleLong (pingroup->numframes); - - pspritegroup = Hunk_AllocName (sizeof (mspritegroup_t) + - (numframes - 1) * sizeof (pspritegroup->frames[0]), loadname); - - pspritegroup->numframes = numframes; - - *ppframe = (mspriteframe_t *)pspritegroup; - - pin_intervals = (dspriteinterval_t *)(pingroup + 1); - - poutintervals = Hunk_AllocName (numframes * sizeof (float), loadname); - - pspritegroup->intervals = poutintervals; - - for (i=0 ; iinterval); - if (*poutintervals <= 0.0) - Sys_Error ("Mod_LoadSpriteGroup: interval<=0"); - - poutintervals++; - pin_intervals++; - } - - ptemp = (void *)pin_intervals; - - for (i=0 ; iframes[i], framenum * 100 + i); - } - - return ptemp; -} - - -/* -================= -Mod_LoadSpriteModel -================= -*/ -void Mod_LoadSpriteModel (model_t *mod, void *buffer) -{ - int i; - int version; - dsprite_t *pin; - msprite_t *psprite; - int numframes; - int size; - dspriteframetype_t *pframetype; - - pin = (dsprite_t *)buffer; - - version = LittleLong (pin->version); - if (version != SPRITE_VERSION) - Sys_Error ("%s has wrong version number " - "(%i should be %i)", mod->name, version, SPRITE_VERSION); - - numframes = LittleLong (pin->numframes); - - size = sizeof (msprite_t) + (numframes - 1) * sizeof (psprite->frames); - - psprite = Hunk_AllocName (size, loadname); - - mod->cache.data = psprite; - - psprite->type = LittleLong (pin->type); - psprite->maxwidth = LittleLong (pin->width); - psprite->maxheight = LittleLong (pin->height); - psprite->beamlength = LittleFloat (pin->beamlength); - mod->synctype = LittleLong (pin->synctype); - psprite->numframes = numframes; - - mod->mins[0] = mod->mins[1] = -psprite->maxwidth/2; - mod->maxs[0] = mod->maxs[1] = psprite->maxwidth/2; - mod->mins[2] = -psprite->maxheight/2; - mod->maxs[2] = psprite->maxheight/2; - -// -// load the frames -// - if (numframes < 1) - Sys_Error ("Mod_LoadSpriteModel: Invalid # of frames: %d\n", numframes); - - mod->numframes = numframes; - - pframetype = (dspriteframetype_t *)(pin + 1); - - for (i=0 ; itype); - psprite->frames[i].type = frametype; - - if (frametype == SPR_SINGLE) - { - pframetype = (dspriteframetype_t *) - Mod_LoadSpriteFrame (pframetype + 1, - &psprite->frames[i].frameptr, i); - } - else - { - pframetype = (dspriteframetype_t *) - Mod_LoadSpriteGroup (pframetype + 1, - &psprite->frames[i].frameptr, i); - } - } - - mod->type = mod_sprite; -} - -//============================================================================= - -/* -================ -Mod_Print -================ -*/ -void Mod_Print (void) -{ - int i; - model_t *mod; - - Con_Printf ("Cached models:\n"); - for (i=0, mod=mod_known ; i < mod_numknown ; i++, mod++) - { - Con_Printf ("%8p : %s\n",mod->cache.data, mod->name); - } -} - - +/* +Copyright (C) 1996-1997 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 "quakedef.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 512 +model_t mod_known[MAX_MOD_KNOWN]; +int mod_numknown; + +cvar_t gl_subdivide_size = {"gl_subdivide_size", "128", CVAR_ARCHIVE}; + +/* +=============== +Mod_Init +=============== +*/ +void Mod_Init (void) +{ + Cvar_RegisterVariable (&gl_subdivide_size); + memset (mod_novis, 0xff, sizeof(mod_novis)); +} + +/* +=============== +Mod_Init + +Caches the data if needed +=============== +*/ +void *Mod_Extradata (model_t *mod) +{ + void *r; + + r = Cache_Check (&mod->cache); + if (r) + return r; + + Mod_LoadModel (mod, true); + + if (!mod->cache.data) + Sys_Error ("Mod_Extradata: caching failed"); + return mod->cache.data; +} + +/* +=============== +Mod_PointInLeaf +=============== +*/ +mleaf_t *Mod_PointInLeaf (vec3_t p, model_t *model) +{ + mnode_t *node; + float d; + mplane_t *plane; + + if (!model || !model->nodes) + Sys_Error ("Mod_PointInLeaf: bad model"); + + node = model->nodes; + while (1) + { + if (node->contents < 0) + 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->numleafs+7)>>3; + out = decompressed; + +#if 0 + memcpy (out, in, row); +#else + 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); +#endif + + return decompressed; +} + +byte *Mod_LeafPVS (mleaf_t *leaf, model_t *model) +{ + if (leaf == model->leafs) + return mod_novis; + return Mod_DecompressVis (leaf->compressed_vis, model); +} + +/* +=================== +Mod_ClearAll +=================== +*/ +void Mod_ClearAll (void) +{ + int i; + model_t *mod; + + for (i=0 , mod=mod_known ; itype != mod_alias) + mod->needload = true; +} + +/* +================== +Mod_FindName + +================== +*/ +model_t *Mod_FindName (char *name) +{ + int i; + model_t *mod; + + if (!name[0]) + Sys_Error ("Mod_ForName: NULL name"); + +// +// search the currently loaded models +// + for (i=0 , mod=mod_known ; iname, name) ) + break; + + if (i == mod_numknown) + { + if (mod_numknown == MAX_MOD_KNOWN) + Sys_Error ("mod_numknown == MAX_MOD_KNOWN"); + strcpy (mod->name, name); + mod->needload = true; + mod_numknown++; + } + + return mod; +} + +/* +================== +Mod_TouchModel + +================== +*/ +void Mod_TouchModel (char *name) +{ + model_t *mod; + + mod = Mod_FindName (name); + + if (!mod->needload) + { + if (mod->type == mod_alias) + Cache_Check (&mod->cache); + } +} + +/* +================== +Mod_LoadModel + +Loads a model into the cache +================== +*/ +model_t *Mod_LoadModel (model_t *mod, qboolean crash) +{ + void *d; + unsigned *buf; + byte stackbuf[1024]; // avoid dirtying the cache heap + + if (!mod->needload) + { + if (mod->type == mod_alias) + { + d = Cache_Check (&mod->cache); + if (d) + return mod; + } + else + return mod; // not cached at all + } + +// +// because the world is so huge, load it one piece at a time +// + if (!crash) + { + + } + +// +// load the file +// + buf = (unsigned *)COM_LoadStackFile (mod->name, stackbuf, sizeof(stackbuf)); + if (!buf) + { + if (crash) + Sys_Error ("Mod_NumForName: %s not found", mod->name); + return NULL; + } + +// +// allocate a new model +// + COM_FileBase (mod->name, loadname); + + loadmodel = mod; + +// +// fill it in +// + +// call the apropriate loader + mod->needload = false; + + switch (LittleLong(*(unsigned *)buf)) + { + case IDPOLYHEADER: + Mod_LoadAliasModel (mod, buf); + break; + + case IDSPRITEHEADER: + Mod_LoadSpriteModel (mod, buf); + break; + + default: + Mod_LoadBrushModel (mod, buf); + break; + } + + return mod; +} + +/* +================== +Mod_ForName + +Loads in a model for the given name +================== +*/ +model_t *Mod_ForName (char *name, qboolean crash) +{ + model_t *mod; + + mod = Mod_FindName (name); + + return Mod_LoadModel (mod, crash); +} + + +static qboolean HasFullbrights (byte *pixels, int size) +{ + int i; + + for (i = 0; i < size; i++) + if (pixels[i] >= 224) + return true; + + return false; +} + + +/* +=============================================================================== + + BRUSHMODEL LOADING + +=============================================================================== +*/ + +byte *mod_base; + +/* +================= +Mod_LoadTextures +================= +*/ +void Mod_LoadTextures (lump_t *l) +{ + int i, j, pixels, num, max, altmax; + miptex_t *mt; + texture_t *tx, *tx2; + texture_t *anims[10]; + texture_t *altanims[10]; + dmiptexlump_t *m; + + if (!l->filelen) + { + loadmodel->textures = NULL; + return; + } + m = (dmiptexlump_t *)(mod_base + l->fileofs); + + m->nummiptex = LittleLong (m->nummiptex); + + loadmodel->numtextures = m->nummiptex; + loadmodel->textures = Hunk_AllocName (m->nummiptex * sizeof(*loadmodel->textures) , loadname); + + for (i=0 ; inummiptex ; i++) + { + m->dataofs[i] = LittleLong(m->dataofs[i]); + if (m->dataofs[i] == -1) + continue; + mt = (miptex_t *)((byte *)m + m->dataofs[i]); + mt->width = LittleLong (mt->width); + mt->height = LittleLong (mt->height); + for (j=0 ; joffsets[j] = LittleLong (mt->offsets[j]); + + if ( (mt->width & 15) || (mt->height & 15) ) + Sys_Error ("Texture %s is not 16 aligned", mt->name); +// pixels = mt->width*mt->height/64*85; + pixels = mt->width*mt->height; // Tonik: we don't need + // precalculated mip textures in GL + tx = Hunk_AllocName (sizeof(texture_t) + pixels, loadname); + loadmodel->textures[i] = tx; + + memcpy (tx->name, mt->name, sizeof(tx->name)); + tx->width = mt->width; + tx->height = mt->height; + for (j=0 ; joffsets[j] = mt->offsets[j] + sizeof(texture_t) - sizeof(miptex_t); + // the pixels immediately follow the structures + memcpy (tx+1, mt+1, pixels); + + // HACK HACK HACK + if (!strcmp(mt->name, "shot1sid") && mt->width==32 && mt->height==32 + && CRC_Block((byte*)(mt+1), mt->width*mt->height) == 65393) + { // This texture in b_shell1.bsp has some of the first 32 pixels painted white. + // They are invisible in software, but look really ugly in GL. So we just copy + // 32 pixels from the bottom to make it look nice. + memcpy (tx+1, (byte *)(tx+1) + 32*31, 32); + } + + if (!strncmp(mt->name,"sky",3)) + R_InitSky (tx); + else + { + texture_mode = GL_LINEAR_MIPMAP_NEAREST; //_LINEAR; + if (mt->name[0] == '*') // we don't brighten turb textures + tx->gl_texturenum = GL_LoadTexture (mt->name, tx->width, tx->height, (byte *)(tx+1), true, false, false); + else { + tx->gl_texturenum = GL_LoadTexture (mt->name, tx->width, tx->height, (byte *)(tx+1), true, false, true); + if (HasFullbrights((byte *)(tx+1), tx->width*tx->height)) { + tx->fb_texturenum = GL_LoadTexture (va("@fb_%s", mt->name), tx->width, tx->height, (byte *)(tx+1), + true, 2 /* Fullbright mask */, false); + } + } + texture_mode = GL_LINEAR; + } + } + +// +// sequence the animations +// + for (i=0 ; inummiptex ; i++) + { + tx = loadmodel->textures[i]; + if (!tx || tx->name[0] != '+') + continue; + if (tx->anim_next) + continue; // already sequenced + + // find the number of frames in the animation + memset (anims, 0, sizeof(anims)); + memset (altanims, 0, sizeof(altanims)); + + max = tx->name[1]; + altmax = 0; + if (max >= 'a' && max <= 'z') + max -= 'a' - 'A'; + if (max >= '0' && max <= '9') + { + max -= '0'; + altmax = 0; + anims[max] = tx; + max++; + } + else if (max >= 'A' && max <= 'J') + { + altmax = max - 'A'; + max = 0; + altanims[altmax] = tx; + altmax++; + } + else + Sys_Error ("Bad animating texture %s", tx->name); + + for (j=i+1 ; jnummiptex ; j++) + { + tx2 = loadmodel->textures[j]; + if (!tx2 || tx2->name[0] != '+') + continue; + if (strcmp (tx2->name+2, tx->name+2)) + continue; + + num = tx2->name[1]; + if (num >= 'a' && num <= 'z') + num -= 'a' - 'A'; + if (num >= '0' && num <= '9') + { + num -= '0'; + anims[num] = tx2; + if (num+1 > max) + max = num + 1; + } + else if (num >= 'A' && num <= 'J') + { + num = num - 'A'; + altanims[num] = tx2; + if (num+1 > altmax) + altmax = num+1; + } + else + Sys_Error ("Bad animating texture %s", tx->name); + } + +#define ANIM_CYCLE 2 + // link them all together + for (j=0 ; jname); + tx2->anim_total = max * ANIM_CYCLE; + tx2->anim_min = j * ANIM_CYCLE; + tx2->anim_max = (j+1) * ANIM_CYCLE; + tx2->anim_next = anims[ (j+1)%max ]; + if (altmax) + tx2->alternate_anims = altanims[0]; + } + for (j=0 ; jname); + tx2->anim_total = altmax * ANIM_CYCLE; + tx2->anim_min = j * ANIM_CYCLE; + tx2->anim_max = (j+1) * ANIM_CYCLE; + tx2->anim_next = altanims[ (j+1)%altmax ]; + if (max) + tx2->alternate_anims = anims[0]; + } + } +} + +/* +================= +Mod_LoadLighting +================= +*/ +void Mod_LoadLighting (lump_t *l) +{ + if (!l->filelen) + { + loadmodel->lightdata = NULL; + return; + } + loadmodel->lightdata = Hunk_AllocName ( l->filelen, loadname); + memcpy (loadmodel->lightdata, mod_base + l->fileofs, l->filelen); +} + + +/* +================= +Mod_LoadVisibility +================= +*/ +void Mod_LoadVisibility (lump_t *l) +{ + if (!l->filelen) + { + loadmodel->visdata = NULL; + return; + } + loadmodel->visdata = Hunk_AllocName ( l->filelen, loadname); + memcpy (loadmodel->visdata, mod_base + l->fileofs, l->filelen); +} + + +/* +================= +Mod_LoadEntities +================= +*/ +void Mod_LoadEntities (lump_t *l) +{ + if (!l->filelen) + { + loadmodel->entities = NULL; + return; + } + loadmodel->entities = Hunk_AllocName ( l->filelen, loadname); + memcpy (loadmodel->entities, mod_base + l->fileofs, l->filelen); +} + + +/* +================= +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)) + Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + 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)) + Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + 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]); + } + for (j=0 ; jheadnode[j] = LittleLong (in->headnode[j]); + out->visleafs = LittleLong (in->visleafs); + 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)) + Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( (count + 1) * sizeof(*out), loadname); + + 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; + int i, j, count; + int miptex; + float len1, len2; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->texinfo = out; + loadmodel->numtexinfo = count; + + for ( i=0 ; ivecs[0][j] = LittleFloat (in->vecs[0][j]); + len1 = Length (out->vecs[0]); + len2 = Length (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; +#if 0 + if (len1 + len2 < 0.001) + out->mipadjust = 1; // don't crash + else + out->mipadjust = 1 / floor( (len1+len2)/2 + 0.1 ); +#endif + + miptex = LittleLong (in->miptex); + out->flags = LittleLong (in->flags); + + if (!loadmodel->textures) + { + out->texture = r_notexture_mip; // checkerboard texture + out->flags = 0; + } + else + { + if (miptex >= loadmodel->numtextures) + Sys_Error ("miptex >= loadmodel->numtextures"); + out->texture = loadmodel->textures[miptex]; + if (!out->texture) + { + out->texture = r_notexture_mip; // texture not found + out->flags = 0; + } + } + } +} + +/* +================ +CalcSurfaceExtents + +Fills in s->texturemins[] and s->extents[] +================ +*/ +void CalcSurfaceExtents (msurface_t *s) +{ + float mins[2], maxs[2], val; + int i,j, e; + mvertex_t *v; + 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++) + { + 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 ( !(tex->flags & TEX_SPECIAL) && s->extents[i] > 512 /* 256 */ ) + Sys_Error ("Bad surface extents"); + } +} + + +/* +================= +Mod_LoadFaces +================= +*/ +void Mod_LoadFaces (lump_t *l) +{ + dface_t *in; + msurface_t *out; + int i, count, surfnum; + int planenum, side; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->surfaces = out; + loadmodel->numsurfaces = count; + + for ( surfnum=0 ; surfnumfirstedge = LittleLong(in->firstedge); + out->numedges = LittleShort(in->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 + + for (i=0 ; istyles[i] = in->styles[i]; + i = LittleLong(in->lightofs); + if (i == -1) + out->samples = NULL; + else + out->samples = loadmodel->lightdata + i; + + // set the drawing flags flag + + if (!strncmp(out->texinfo->texture->name,"sky",3)) // sky + { + out->flags |= (SURF_DRAWSKY | SURF_DRAWTILED); +#ifndef QUAKE2 + GL_SubdivideSurface (out); // cut up polygon for warps +#endif + continue; + } + + if (!strncmp(out->texinfo->texture->name,"*",1)) // turbulent + { + out->flags |= (SURF_DRAWTURB | SURF_DRAWTILED); + for (i=0 ; i<2 ; i++) + { + out->extents[i] = 16384; + out->texturemins[i] = -8192; + } + GL_SubdivideSurface (out); // cut up polygon for warps + continue; + } + + } +} + + +/* +================= +Mod_SetParent +================= +*/ +void Mod_SetParent (mnode_t *node, mnode_t *parent) +{ + node->parent = parent; + if (node->contents < 0) + return; + Mod_SetParent (node->children[0], node); + Mod_SetParent (node->children[1], node); +} + +/* +================= +Mod_LoadNodes +================= +*/ +void Mod_LoadNodes (lump_t *l) +{ + int i, j, count, p; + dnode_t *in; + mnode_t *out; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + 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); + + for (j=0 ; j<2 ; j++) + { + p = LittleShort (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, p; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->leafs = out; + loadmodel->numleafs = count; + for ( i=0 ; iminmaxs[j] = LittleShort (in->mins[j]); + out->minmaxs[3+j] = LittleShort (in->maxs[j]); + } + + p = LittleLong(in->contents); + out->contents = p; + + out->firstmarksurface = loadmodel->marksurfaces + + LittleShort(in->firstmarksurface); + out->nummarksurfaces = LittleShort(in->nummarksurfaces); + + p = LittleLong(in->visofs); + if (p == -1) + out->compressed_vis = NULL; + else + out->compressed_vis = loadmodel->visdata + p; + out->efrags = NULL; + + for (j=0 ; j<4 ; j++) + out->ambient_sound_level[j] = in->ambient_level[j]; + } +} + +/* +================= +Mod_LoadClipnodes +================= +*/ +void Mod_LoadClipnodes (lump_t *l) +{ + dclipnode_t *in, *out; + int i, count; + hull_t *hull; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->clipnodes = out; + loadmodel->numclipnodes = count; + + hull = &loadmodel->hulls[1]; + hull->clipnodes = out; + hull->firstclipnode = 0; + hull->lastclipnode = count-1; + hull->planes = loadmodel->planes; + hull->clip_mins[0] = -16; + hull->clip_mins[1] = -16; + hull->clip_mins[2] = -24; + hull->clip_maxs[0] = 16; + hull->clip_maxs[1] = 16; + hull->clip_maxs[2] = 32; + + hull = &loadmodel->hulls[2]; + hull->clipnodes = out; + hull->firstclipnode = 0; + hull->lastclipnode = count-1; + hull->planes = loadmodel->planes; + hull->clip_mins[0] = -32; + hull->clip_mins[1] = -32; + hull->clip_mins[2] = -24; + hull->clip_maxs[0] = 32; + hull->clip_maxs[1] = 32; + hull->clip_maxs[2] = 64; + + for (i=0 ; iplanenum = LittleLong(in->planenum); + out->children[0] = LittleShort(in->children[0]); + out->children[1] = LittleShort(in->children[1]); + } +} + +/* +================= +Mod_MakeHull0 + +Deplicate the drawing hull structure as a clipping hull +================= +*/ +void Mod_MakeHull0 (void) +{ + mnode_t *in, *child; + dclipnode_t *out; + int i, j, count; + hull_t *hull; + + hull = &loadmodel->hulls[0]; + + in = loadmodel->nodes; + count = loadmodel->numnodes; + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + hull->clipnodes = out; + hull->firstclipnode = 0; + hull->lastclipnode = count-1; + hull->planes = loadmodel->planes; + + for (i=0 ; iplanenum = in->plane - loadmodel->planes; + for (j=0 ; j<2 ; j++) + { + child = in->children[j]; + if (child->contents < 0) + out->children[j] = child->contents; + else + out->children[j] = child - loadmodel->nodes; + } + } +} + +/* +================= +Mod_LoadMarksurfaces +================= +*/ +void Mod_LoadMarksurfaces (lump_t *l) +{ + int i, j, count; + short *in; + msurface_t **out; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->marksurfaces = out; + loadmodel->nummarksurfaces = count; + + for ( i=0 ; i= loadmodel->numsurfaces) + Sys_Error ("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)) + Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->surfedges = out; + loadmodel->numsurfedges = count; + + for ( i=0 ; ifileofs); + if (l->filelen % sizeof(*in)) + Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*2*sizeof(*out), loadname); + + 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; + } +} + +/* +================= +RadiusFromBounds +================= +*/ +float RadiusFromBounds (vec3_t mins, vec3_t maxs) +{ + int i; + vec3_t corner; + + for (i=0 ; i<3 ; i++) + { + corner[i] = fabs(mins[i]) > fabs(maxs[i]) ? fabs(mins[i]) : fabs(maxs[i]); + } + + return Length (corner); +} + +/* +================= +Mod_LoadBrushModel +================= +*/ +void Mod_LoadBrushModel (model_t *mod, void *buffer) +{ + int i, j; + dheader_t *header; + dmodel_t *bm; + + loadmodel->type = mod_brush; + + header = (dheader_t *)buffer; + + i = LittleLong (header->version); + if (i != BSPVERSION) + Sys_Error ("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 ; ichecksum = 0; + mod->checksum2 = 0; + + for (i = 0; i < HEADER_LUMPS; i++) { + if (i == LUMP_ENTITIES) + continue; + mod->checksum ^= Com_BlockChecksum(mod_base + header->lumps[i].fileofs, + header->lumps[i].filelen); + + if (i == LUMP_VISIBILITY || i == LUMP_LEAFS || i == LUMP_NODES) + continue; + mod->checksum2 ^= Com_BlockChecksum(mod_base + header->lumps[i].fileofs, + header->lumps[i].filelen); + } + + +// load into heap + + Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]); + Mod_LoadEdges (&header->lumps[LUMP_EDGES]); + Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]); + Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]); + 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_MARKSURFACES]); + Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]); + Mod_LoadLeafs (&header->lumps[LUMP_LEAFS]); + Mod_LoadNodes (&header->lumps[LUMP_NODES]); + Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES]); + Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]); + Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]); + + Mod_MakeHull0 (); + + mod->numframes = 2; // regular and alternate animation + +// +// set up the submodels (FIXME: this is confusing) +// + for (i=0 ; inumsubmodels ; i++) + { + bm = &mod->submodels[i]; + + mod->hulls[0].firstclipnode = bm->headnode[0]; + for (j=1 ; jhulls[j].firstclipnode = bm->headnode[j]; + mod->hulls[j].lastclipnode = mod->numclipnodes-1; + } + + mod->firstmodelsurface = bm->firstface; + mod->nummodelsurfaces = bm->numfaces; + + VectorCopy (bm->maxs, mod->maxs); + VectorCopy (bm->mins, mod->mins); + + mod->radius = RadiusFromBounds (mod->mins, mod->maxs); + + mod->numleafs = bm->visleafs; + + if (i < mod->numsubmodels-1) + { // duplicate the basic information + char name[10]; + + sprintf (name, "*%i", i+1); + loadmodel = Mod_FindName (name); + *loadmodel = *mod; + strcpy (loadmodel->name, name); + mod = loadmodel; + } + } +} + +/* +============================================================================== + +ALIAS MODELS + +============================================================================== +*/ + +aliashdr_t *pheader; + +stvert_t stverts[MAXALIASVERTS]; +mtriangle_t triangles[MAXALIASTRIS]; + +// a pose is a single set of vertexes. a frame may be +// an animating sequence of poses +trivertx_t *poseverts[MAXALIASFRAMES]; +int posenum; + +byte player_8bit_texels[320*200]; + +/* +================= +Mod_LoadAliasFrame +================= +*/ +void * Mod_LoadAliasFrame (void * pin, maliasframedesc_t *frame) +{ + trivertx_t *pinframe; + int i; + daliasframe_t *pdaliasframe; + + pdaliasframe = (daliasframe_t *)pin; + + strcpy (frame->name, pdaliasframe->name); + frame->firstpose = posenum; + frame->numposes = 1; + + for (i=0 ; i<3 ; i++) + { + // these are byte values, so we don't have to worry about + // endianness + frame->bboxmin.v[i] = pdaliasframe->bboxmin.v[i]; + frame->bboxmin.v[i] = pdaliasframe->bboxmax.v[i]; + } + + pinframe = (trivertx_t *)(pdaliasframe + 1); + + poseverts[posenum] = pinframe; + posenum++; + + pinframe += pheader->numverts; + + return (void *)pinframe; +} + + +/* +================= +Mod_LoadAliasGroup +================= +*/ +void * Mod_LoadAliasGroup (void * pin, maliasframedesc_t *frame) +{ + daliasgroup_t *pingroup; + int i, numframes; + daliasinterval_t *pin_intervals; + void *ptemp; + + pingroup = (daliasgroup_t *)pin; + + numframes = LittleLong (pingroup->numframes); + + frame->firstpose = posenum; + frame->numposes = numframes; + + for (i=0 ; i<3 ; i++) + { + // these are byte values, so we don't have to worry about endianness + frame->bboxmin.v[i] = pingroup->bboxmin.v[i]; + frame->bboxmin.v[i] = pingroup->bboxmax.v[i]; + } + + pin_intervals = (daliasinterval_t *)(pingroup + 1); + + frame->interval = LittleFloat (pin_intervals->interval); + + pin_intervals += numframes; + + ptemp = (void *)pin_intervals; + + for (i=0 ; inumverts; + } + + return ptemp; +} + +//========================================================= + +/* +================= +Mod_FloodFillSkin + +Fill background pixels so mipmapping doesn't have haloes - Ed +================= +*/ + +typedef struct +{ + short x, y; +} floodfill_t; + +extern unsigned d_8to24table[]; + +// must be a power of 2 +#define FLOODFILL_FIFO_SIZE 0x1000 +#define FLOODFILL_FIFO_MASK (FLOODFILL_FIFO_SIZE - 1) + +#define FLOODFILL_STEP( off, dx, dy ) \ +{ \ + if (pos[off] == fillcolor) \ + { \ + pos[off] = 255; \ + fifo[inpt].x = x + (dx), fifo[inpt].y = y + (dy); \ + inpt = (inpt + 1) & FLOODFILL_FIFO_MASK; \ + } \ + else if (pos[off] != 255) fdc = pos[off]; \ +} + +void Mod_FloodFillSkin( byte *skin, int skinwidth, int skinheight ) +{ + byte fillcolor = *skin; // assume this is the pixel to fill + floodfill_t fifo[FLOODFILL_FIFO_SIZE]; + int inpt = 0, outpt = 0; + int filledcolor = -1; + int i; + + if (filledcolor == -1) + { + filledcolor = 0; + // attempt to find opaque black + for (i = 0; i < 256; ++i) + if (d_8to24table[i] == (255 << 0)) // alpha 1.0 + { + filledcolor = i; + break; + } + } + + // can't fill to filled color or to transparent color (used as visited marker) + if ((fillcolor == filledcolor) || (fillcolor == 255)) + { + //printf( "not filling skin from %d to %d\n", fillcolor, filledcolor ); + return; + } + + fifo[inpt].x = 0, fifo[inpt].y = 0; + inpt = (inpt + 1) & FLOODFILL_FIFO_MASK; + + while (outpt != inpt) + { + int x = fifo[outpt].x, y = fifo[outpt].y; + int fdc = filledcolor; + byte *pos = &skin[x + skinwidth * y]; + + outpt = (outpt + 1) & FLOODFILL_FIFO_MASK; + + if (x > 0) FLOODFILL_STEP( -1, -1, 0 ); + if (x < skinwidth - 1) FLOODFILL_STEP( 1, 1, 0 ); + if (y > 0) FLOODFILL_STEP( -skinwidth, 0, -1 ); + if (y < skinheight - 1) FLOODFILL_STEP( skinwidth, 0, 1 ); + skin[x + skinwidth * y] = fdc; + } +} + +/* +=============== +Mod_LoadAllSkins +=============== +*/ +void *Mod_LoadAllSkins (int numskins, daliasskintype_t *pskintype) +{ + int i, j, k; + char name[32]; + int s; + byte *skin; + daliasskingroup_t *pinskingroup; + int groupskins; + daliasskininterval_t *pinskinintervals; + + skin = (byte *)(pskintype + 1); + + if (numskins < 1 || numskins > MAX_SKINS) + Sys_Error ("Mod_LoadAliasModel: Invalid # of skins: %d\n", numskins); + + s = pheader->skinwidth * pheader->skinheight; + + for (i=0 ; itype == ALIAS_SKIN_SINGLE) { + Mod_FloodFillSkin (skin, pheader->skinwidth, pheader->skinheight); + + // save 8 bit texels for the player model to remap + if (!strcmp(loadmodel->name,"progs/player.mdl")) + { + if (s > sizeof(player_8bit_texels)) + Sys_Error ("Player skin too large"); + memcpy (player_8bit_texels, (byte *)(pskintype + 1), s); + } + _snprintf (name, sizeof(name)-1, "%s_%i", loadmodel->name, i); + pheader->gl_texturenum[i][0] = + pheader->gl_texturenum[i][1] = + pheader->gl_texturenum[i][2] = + pheader->gl_texturenum[i][3] = + GL_LoadTexture (name, pheader->skinwidth, pheader->skinheight, + (byte *)(pskintype + 1), true, false, false); + + if (HasFullbrights((byte *)(pskintype + 1), pheader->skinwidth*pheader->skinheight)) + pheader->fb_texturenum[i][0] = pheader->fb_texturenum[i][1] = + pheader->fb_texturenum[i][2] = pheader->fb_texturenum[i][3] = + GL_LoadTexture (va("@fb_%s", name), pheader->skinwidth, + pheader->skinheight, (byte *)(pskintype + 1), true, 2, false); + else + pheader->fb_texturenum[i][0] = pheader->fb_texturenum[i][1] = + pheader->fb_texturenum[i][2] = pheader->fb_texturenum[i][3] = 0; + + pskintype = (daliasskintype_t *)((byte *)(pskintype+1) + s); + } else { + // animating skin group. yuck. + pskintype++; + pinskingroup = (daliasskingroup_t *)pskintype; + groupskins = LittleLong (pinskingroup->numskins); + pinskinintervals = (daliasskininterval_t *)(pinskingroup + 1); + + pskintype = (void *)(pinskinintervals + groupskins); + + for (j=0 ; jskinwidth, pheader->skinheight); + _snprintf (name, sizeof(name)-1, "%s_%i_%i", loadmodel->name, i, j); + pheader->gl_texturenum[i][j&3] = + GL_LoadTexture (name, pheader->skinwidth, + pheader->skinheight, (byte *)(pskintype), true, false, false); + + if (HasFullbrights((byte *)(pskintype), pheader->skinwidth*pheader->skinheight)) + pheader->fb_texturenum[i][j&3] = + GL_LoadTexture (va("@fb_%s", name), pheader->skinwidth, + pheader->skinheight, (byte *)(pskintype), true, 2, false); + else + pheader->fb_texturenum[i][j&3] = 0; + + pskintype = (daliasskintype_t *)((byte *)(pskintype) + s); + } + k = j; + for (/* */; j < 4; j++) + pheader->gl_texturenum[i][j&3] = + pheader->gl_texturenum[i][j - k]; + } + } + + return (void *)pskintype; +} + + +//========================================================================= + +/* +================= +Mod_LoadAliasModel +================= +*/ +void Mod_LoadAliasModel (model_t *mod, void *buffer) +{ + int i, j; + mdl_t *pinmodel; + stvert_t *pinstverts; + dtriangle_t *pintriangles; + int version, numframes; + int size; + daliasframetype_t *pframetype; + daliasskintype_t *pskintype; + int start, end, total; + +// some models are special + if(!strcmp(mod->name, "progs/player.mdl")) + mod->modhint = MOD_PLAYER; + else if(!strcmp(mod->name, "progs/eyes.mdl")) + mod->modhint = MOD_EYES; + else if (!strcmp(mod->name, "progs/flame.mdl") || + !strcmp(mod->name, "progs/flame2.mdl")) + mod->modhint = MOD_FLAME; + else if (!strcmp(mod->name, "progs/bolt.mdl") || + !strcmp(mod->name, "progs/bolt2.mdl") || + !strcmp(mod->name, "progs/bolt3.mdl")) + mod->modhint = MOD_THUNDERBOLT; + + if (mod->modhint == MOD_PLAYER || mod->modhint == MOD_EYES) { + unsigned short crc; + byte *p; + int len; + char st[40]; + + CRC_Init(&crc); + for (len = com_filesize, p = buffer; len; len--, p++) + CRC_ProcessByte(&crc, *p); + + sprintf(st, "%d", (int) crc); + Info_SetValueForKey (cls.userinfo, + mod->modhint == MOD_PLAYER ? pmodel_name : emodel_name, + st, MAX_INFO_STRING); + + if (cls.state >= ca_connected) { + MSG_WriteByte (&cls.netchan.message, clc_stringcmd); + sprintf(st, "setinfo %s %d", + mod->modhint == MOD_PLAYER ? pmodel_name : emodel_name, + (int)crc); + SZ_Print (&cls.netchan.message, st); + } + } + + start = Hunk_LowMark (); + + pinmodel = (mdl_t *)buffer; + + version = LittleLong (pinmodel->version); + if (version != ALIAS_VERSION) + Sys_Error ("%s has wrong version number (%i should be %i)", + mod->name, version, ALIAS_VERSION); + +// +// allocate space for a working header, plus all the data except the frames, +// skin and group info +// + size = sizeof (aliashdr_t) + + (LittleLong (pinmodel->numframes) - 1) * + sizeof (pheader->frames[0]); + pheader = Hunk_AllocName (size, loadname); + + mod->flags = LittleLong (pinmodel->flags); + +// +// endian-adjust and copy the data, starting with the alias model header +// + pheader->boundingradius = LittleFloat (pinmodel->boundingradius); + pheader->numskins = LittleLong (pinmodel->numskins); + pheader->skinwidth = LittleLong (pinmodel->skinwidth); + pheader->skinheight = LittleLong (pinmodel->skinheight); + + if (pheader->skinheight > MAX_LBM_HEIGHT) + Sys_Error ("model %s has a skin taller than %d", mod->name, + MAX_LBM_HEIGHT); + + pheader->numverts = LittleLong (pinmodel->numverts); + + if (pheader->numverts <= 0) + Sys_Error ("model %s has no vertices", mod->name); + + if (pheader->numverts > MAXALIASVERTS) + Sys_Error ("model %s has too many vertices", mod->name); + + pheader->numtris = LittleLong (pinmodel->numtris); + + if (pheader->numtris <= 0) + Sys_Error ("model %s has no triangles", mod->name); + + pheader->numframes = LittleLong (pinmodel->numframes); + numframes = pheader->numframes; + if (numframes < 1) + Sys_Error ("Mod_LoadAliasModel: Invalid # of frames: %d\n", numframes); + + pheader->size = LittleFloat (pinmodel->size) * ALIAS_BASE_SIZE_RATIO; + mod->synctype = LittleLong (pinmodel->synctype); + mod->numframes = pheader->numframes; + + for (i=0 ; i<3 ; i++) + { + pheader->scale[i] = LittleFloat (pinmodel->scale[i]); + pheader->scale_origin[i] = LittleFloat (pinmodel->scale_origin[i]); + pheader->eyeposition[i] = LittleFloat (pinmodel->eyeposition[i]); + } + + +// +// load the skins +// + pskintype = (daliasskintype_t *)&pinmodel[1]; + pskintype = Mod_LoadAllSkins (pheader->numskins, pskintype); + +// +// load base s and t vertices +// + pinstverts = (stvert_t *)pskintype; + + for (i=0 ; inumverts ; i++) + { + stverts[i].onseam = LittleLong (pinstverts[i].onseam); + stverts[i].s = LittleLong (pinstverts[i].s); + stverts[i].t = LittleLong (pinstverts[i].t); + } + +// +// load triangle lists +// + pintriangles = (dtriangle_t *)&pinstverts[pheader->numverts]; + + for (i=0 ; inumtris ; i++) + { + triangles[i].facesfront = LittleLong (pintriangles[i].facesfront); + + for (j=0 ; j<3 ; j++) + { + triangles[i].vertindex[j] = + LittleLong (pintriangles[i].vertindex[j]); + } + } + +// +// load the frames +// + posenum = 0; + pframetype = (daliasframetype_t *)&pintriangles[pheader->numtris]; + + for (i=0 ; itype); + + if (frametype == ALIAS_SINGLE) + { + pframetype = (daliasframetype_t *) + Mod_LoadAliasFrame (pframetype + 1, &pheader->frames[i]); + } + else + { + pframetype = (daliasframetype_t *) + Mod_LoadAliasGroup (pframetype + 1, &pheader->frames[i]); + } + } + + pheader->numposes = posenum; + + mod->type = mod_alias; + +// FIXME: do this right + mod->mins[0] = mod->mins[1] = mod->mins[2] = -16; + mod->maxs[0] = mod->maxs[1] = mod->maxs[2] = 16; + + // + // build the draw lists + // + GL_MakeAliasModelDisplayLists (mod, pheader); + +// +// move the complete, relocatable alias model to the cache +// + end = Hunk_LowMark (); + total = end - start; + + Cache_Alloc (&mod->cache, total, loadname); + if (!mod->cache.data) + return; + memcpy (mod->cache.data, pheader, total); + + Hunk_FreeToLowMark (start); +} + +//============================================================================= + +/* +================= +Mod_LoadSpriteFrame +================= +*/ +void * Mod_LoadSpriteFrame (void * pin, mspriteframe_t **ppframe, int framenum) +{ + dspriteframe_t *pinframe; + mspriteframe_t *pspriteframe; + int width, height, size, origin[2]; + char name[64]; + + pinframe = (dspriteframe_t *)pin; + + width = LittleLong (pinframe->width); + height = LittleLong (pinframe->height); + size = width * height; + + pspriteframe = Hunk_AllocName (sizeof (mspriteframe_t),loadname); + + memset (pspriteframe, 0, sizeof (mspriteframe_t)); + + *ppframe = pspriteframe; + + pspriteframe->width = width; + pspriteframe->height = height; + origin[0] = LittleLong (pinframe->origin[0]); + origin[1] = LittleLong (pinframe->origin[1]); + + pspriteframe->up = origin[1]; + pspriteframe->down = origin[1] - height; + pspriteframe->left = origin[0]; + pspriteframe->right = width + origin[0]; + + sprintf (name, "%s_%i", loadmodel->name, framenum); + pspriteframe->gl_texturenum = GL_LoadTexture (name, width, height, (byte *)(pinframe + 1), true, true, false); + + return (void *)((byte *)pinframe + sizeof (dspriteframe_t) + size); +} + + +/* +================= +Mod_LoadSpriteGroup +================= +*/ +void * Mod_LoadSpriteGroup (void * pin, mspriteframe_t **ppframe, int framenum) +{ + dspritegroup_t *pingroup; + mspritegroup_t *pspritegroup; + int i, numframes; + dspriteinterval_t *pin_intervals; + float *poutintervals; + void *ptemp; + + pingroup = (dspritegroup_t *)pin; + + numframes = LittleLong (pingroup->numframes); + + pspritegroup = Hunk_AllocName (sizeof (mspritegroup_t) + + (numframes - 1) * sizeof (pspritegroup->frames[0]), loadname); + + pspritegroup->numframes = numframes; + + *ppframe = (mspriteframe_t *)pspritegroup; + + pin_intervals = (dspriteinterval_t *)(pingroup + 1); + + poutintervals = Hunk_AllocName (numframes * sizeof (float), loadname); + + pspritegroup->intervals = poutintervals; + + for (i=0 ; iinterval); + if (*poutintervals <= 0.0) + Sys_Error ("Mod_LoadSpriteGroup: interval<=0"); + + poutintervals++; + pin_intervals++; + } + + ptemp = (void *)pin_intervals; + + for (i=0 ; iframes[i], framenum * 100 + i); + } + + return ptemp; +} + + +/* +================= +Mod_LoadSpriteModel +================= +*/ +void Mod_LoadSpriteModel (model_t *mod, void *buffer) +{ + int i; + int version; + dsprite_t *pin; + msprite_t *psprite; + int numframes; + int size; + dspriteframetype_t *pframetype; + + pin = (dsprite_t *)buffer; + + version = LittleLong (pin->version); + if (version != SPRITE_VERSION) + Sys_Error ("%s has wrong version number " + "(%i should be %i)", mod->name, version, SPRITE_VERSION); + + numframes = LittleLong (pin->numframes); + + size = sizeof (msprite_t) + (numframes - 1) * sizeof (psprite->frames); + + psprite = Hunk_AllocName (size, loadname); + + mod->cache.data = psprite; + + psprite->type = LittleLong (pin->type); + psprite->maxwidth = LittleLong (pin->width); + psprite->maxheight = LittleLong (pin->height); + psprite->beamlength = LittleFloat (pin->beamlength); + mod->synctype = LittleLong (pin->synctype); + psprite->numframes = numframes; + + mod->mins[0] = mod->mins[1] = -psprite->maxwidth/2; + mod->maxs[0] = mod->maxs[1] = psprite->maxwidth/2; + mod->mins[2] = -psprite->maxheight/2; + mod->maxs[2] = psprite->maxheight/2; + +// +// load the frames +// + if (numframes < 1) + Sys_Error ("Mod_LoadSpriteModel: Invalid # of frames: %d\n", numframes); + + mod->numframes = numframes; + + pframetype = (dspriteframetype_t *)(pin + 1); + + for (i=0 ; itype); + psprite->frames[i].type = frametype; + + if (frametype == SPR_SINGLE) + { + pframetype = (dspriteframetype_t *) + Mod_LoadSpriteFrame (pframetype + 1, + &psprite->frames[i].frameptr, i); + } + else + { + pframetype = (dspriteframetype_t *) + Mod_LoadSpriteGroup (pframetype + 1, + &psprite->frames[i].frameptr, i); + } + } + + mod->type = mod_sprite; +} + +//============================================================================= + +/* +================ +Mod_Print +================ +*/ +void Mod_Print (void) +{ + int i; + model_t *mod; + + Con_Printf ("Cached models:\n"); + for (i=0, mod=mod_known ; i < mod_numknown ; i++, mod++) + { + Con_Printf ("%8p : %s\n",mod->cache.data, mod->name); + } +} + + diff --git a/source/gl_model.h b/source/gl_model.h index 0f4dffd5..00e4a267 100644 --- a/source/gl_model.h +++ b/source/gl_model.h @@ -1,445 +1,445 @@ -/* -Copyright (C) 1996-1997 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 __MODEL__ -#define __MODEL__ - -#include "modelgen.h" -#include "spritegn.h" -#include "bspfile.h" - -/* - -d*_t structures are on-disk representations -m*_t structures are in-memory - -*/ - -// entity effects - -#define EF_BRIGHTFIELD 1 -#define EF_MUZZLEFLASH 2 -#define EF_BRIGHTLIGHT 4 -#define EF_DIMLIGHT 8 -#define EF_FLAG1 16 -#define EF_FLAG2 32 -#define EF_BLUE 64 -#define EF_RED 128 - -/* -============================================================================== - -BRUSH MODELS - -============================================================================== -*/ - - -// -// in memory representation -// -// !!! if this is changed, it must be changed in asm_draw.h too !!! -typedef struct -{ - vec3_t position; -} mvertex_t; - -#define SIDE_FRONT 0 -#define SIDE_BACK 1 -#define SIDE_ON 2 - - -// plane_t structure -// !!! if this is changed, it must be changed in asm_i386.h too !!! -typedef struct mplane_s -{ - vec3_t normal; - float dist; - byte type; // for texture axis selection and fast side tests - byte signbits; // signx + signy<<1 + signz<<1 - byte pad[2]; -} mplane_t; - -typedef struct texture_s -{ - char name[16]; - unsigned width, height; - int gl_texturenum; - int fb_texturenum; // index of fullbright mask or 0 - struct msurface_s *texturechain; // for gl_texsort drawing - int anim_total; // total tenths in sequence ( 0 = no) - int anim_min, anim_max; // time for this frame min <=time< max - struct texture_s *anim_next; // in the animation sequence - struct texture_s *alternate_anims; // bmodels in frmae 1 use these - unsigned offsets[MIPLEVELS]; // four mip maps stored -} texture_t; - - -#define SURF_PLANEBACK 2 -#define SURF_DRAWSKY 4 -#define SURF_DRAWSPRITE 8 -#define SURF_DRAWTURB 0x10 -#define SURF_DRAWTILED 0x20 -#define SURF_DRAWBACKGROUND 0x40 -#define SURF_UNDERWATER 0x80 -#define SURF_DONTWARP 0x100 - -// !!! if this is changed, it must be changed in asm_draw.h too !!! -typedef struct -{ - unsigned short v[2]; - unsigned int cachededgeoffset; -} medge_t; - -typedef struct -{ - float vecs[2][4]; - float mipadjust; - texture_t *texture; - int flags; -} mtexinfo_t; - -#define VERTEXSIZE 7 - -typedef struct glpoly_s -{ - struct glpoly_s *next; - struct glpoly_s *chain; - struct glpoly_s *fb_chain; - int numverts; - int flags; // for SURF_UNDERWATER - float verts[4][VERTEXSIZE]; // variable sized (xyz s1t1 s2t2) -} glpoly_t; - -typedef struct msurface_s -{ - int visframe; // should be drawn when node is crossed - - mplane_t *plane; - int flags; - - int firstedge; // look up in model->surfedges[], negative numbers - int numedges; // are backwards edges - - short texturemins[2]; - short extents[2]; - - int light_s, light_t; // gl lightmap coordinates - - glpoly_t *polys; // multiple if warped - struct msurface_s *texturechain; - - mtexinfo_t *texinfo; - -// lighting info - int dlightframe; - int dlightbits; - - int lightmaptexturenum; - byte styles[MAXLIGHTMAPS]; - int cached_light[MAXLIGHTMAPS]; // values currently used in lightmap - qboolean cached_dlight; // true if dynamic light in cache - byte *samples; // [numstyles*surfsize] -} msurface_t; - -typedef struct mnode_s -{ -// common with leaf - int contents; // 0, to differentiate from leafs - int visframe; // node needs to be traversed if current - - float 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 a negative contents number - int visframe; // node needs to be traversed if current - - float minmaxs[6]; // for bounding box culling - - struct mnode_s *parent; - -// leaf specific - byte *compressed_vis; - efrag_t *efrags; - - msurface_t **firstmarksurface; - int nummarksurfaces; - int key; // BSP sequence number for leaf's contents - byte ambient_sound_level[NUM_AMBIENTS]; -} mleaf_t; - -// !!! if this is changed, it must be changed in asm_i386.h too !!! -typedef struct -{ - dclipnode_t *clipnodes; - mplane_t *planes; - int firstclipnode; - int lastclipnode; - vec3_t clip_mins; - vec3_t clip_maxs; -} hull_t; - -/* -============================================================================== - -SPRITE MODELS - -============================================================================== -*/ - - -// FIXME: shorten these? -typedef struct mspriteframe_s -{ - int width; - int height; - float up, down, left, right; - int gl_texturenum; -} mspriteframe_t; - -typedef struct -{ - int numframes; - float *intervals; - mspriteframe_t *frames[1]; -} mspritegroup_t; - -typedef struct -{ - spriteframetype_t type; - mspriteframe_t *frameptr; -} mspriteframedesc_t; - -typedef struct -{ - int type; - int maxwidth; - int maxheight; - int numframes; - float beamlength; // remove? - void *cachespot; // remove? - mspriteframedesc_t frames[1]; -} msprite_t; - - -/* -============================================================================== - -ALIAS MODELS - -Alias models are position independent, so the cache manager can move them. -============================================================================== -*/ - -typedef struct -{ - int firstpose; - int numposes; - float interval; - trivertx_t bboxmin; - trivertx_t bboxmax; - int frame; - char name[16]; -} maliasframedesc_t; - -typedef struct -{ - trivertx_t bboxmin; - trivertx_t bboxmax; - int frame; -} maliasgroupframedesc_t; - -typedef struct -{ - int numframes; - int intervals; - maliasgroupframedesc_t frames[1]; -} maliasgroup_t; - -// !!! if this is changed, it must be changed in asm_draw.h too !!! -typedef struct mtriangle_s { - int facesfront; - int vertindex[3]; -} mtriangle_t; - - -#define MAX_SKINS 32 -typedef struct { - int ident; - int version; - vec3_t scale; - vec3_t scale_origin; - float boundingradius; - vec3_t eyeposition; - int numskins; - int skinwidth; - int skinheight; - int numverts; - int numtris; - int numframes; - synctype_t synctype; - int flags; - float size; - - int numposes; - int poseverts; - int posedata; // numposes*poseverts trivert_t - int commands; // gl command list with embedded s/t - int gl_texturenum[MAX_SKINS][4]; - int fb_texturenum[MAX_SKINS][4]; - maliasframedesc_t frames[1]; // variable sized -} aliashdr_t; - -#define MAXALIASVERTS 1024 -#define MAXALIASFRAMES 256 -#define MAXALIASTRIS 2048 -extern aliashdr_t *pheader; -extern stvert_t stverts[MAXALIASVERTS]; -extern mtriangle_t triangles[MAXALIASTRIS]; -extern trivertx_t *poseverts[MAXALIASFRAMES]; - -//=================================================================== - -// -// Whole model -// - -typedef enum {mod_brush, mod_sprite, mod_alias} modtype_t; - -// some models are special -typedef enum {MOD_NORMAL, MOD_PLAYER, MOD_EYES, MOD_FLAME, MOD_THUNDERBOLT} modhint_t; - -#define EF_ROCKET 1 // leave a trail -#define EF_GRENADE 2 // leave a trail -#define EF_GIB 4 // leave a trail -#define EF_ROTATE 8 // rotate (bonus items) -#define EF_TRACER 16 // green split trail -#define EF_ZOMGIB 32 // small blood trail -#define EF_TRACER2 64 // orange split trail + rotate -#define EF_TRACER3 128 // purple trail - -typedef struct model_s -{ - char name[MAX_QPATH]; - qboolean needload; // bmodels and sprites don't cache normally - - modhint_t modhint; - - modtype_t type; - int numframes; - synctype_t synctype; - - int flags; - -// -// volume occupied by the model graphics -// - vec3_t mins, maxs; - float radius; - -// -// solid volume for clipping -// - 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; - mnode_t *nodes; - - int numtexinfo; - mtexinfo_t *texinfo; - - int numsurfaces; - msurface_t *surfaces; - - int numsurfedges; - int *surfedges; - - int numclipnodes; - dclipnode_t *clipnodes; - - int nummarksurfaces; - msurface_t **marksurfaces; - - hull_t hulls[MAX_MAP_HULLS]; - - int numtextures; - texture_t **textures; - - byte *visdata; - byte *lightdata; - char *entities; - - unsigned checksum; - unsigned checksum2; - -// -// additional model data -// - cache_user_t cache; // only access through Mod_Extradata - -} 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_LeafPVS (mleaf_t *leaf, model_t *model); - -#endif // __MODEL__ +/* +Copyright (C) 1996-1997 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 __MODEL__ +#define __MODEL__ + +#include "modelgen.h" +#include "spritegn.h" +#include "bspfile.h" + +/* + +d*_t structures are on-disk representations +m*_t structures are in-memory + +*/ + +// entity effects + +#define EF_BRIGHTFIELD 1 +#define EF_MUZZLEFLASH 2 +#define EF_BRIGHTLIGHT 4 +#define EF_DIMLIGHT 8 +#define EF_FLAG1 16 +#define EF_FLAG2 32 +#define EF_BLUE 64 +#define EF_RED 128 + +/* +============================================================================== + +BRUSH MODELS + +============================================================================== +*/ + + +// +// in memory representation +// +// !!! if this is changed, it must be changed in asm_draw.h too !!! +typedef struct +{ + vec3_t position; +} mvertex_t; + +#define SIDE_FRONT 0 +#define SIDE_BACK 1 +#define SIDE_ON 2 + + +// plane_t structure +// !!! if this is changed, it must be changed in asm_i386.h too !!! +typedef struct mplane_s +{ + vec3_t normal; + float dist; + byte type; // for texture axis selection and fast side tests + byte signbits; // signx + signy<<1 + signz<<1 + byte pad[2]; +} mplane_t; + +typedef struct texture_s +{ + char name[16]; + unsigned width, height; + int gl_texturenum; + int fb_texturenum; // index of fullbright mask or 0 + struct msurface_s *texturechain; // for gl_texsort drawing + int anim_total; // total tenths in sequence ( 0 = no) + int anim_min, anim_max; // time for this frame min <=time< max + struct texture_s *anim_next; // in the animation sequence + struct texture_s *alternate_anims; // bmodels in frmae 1 use these + unsigned offsets[MIPLEVELS]; // four mip maps stored +} texture_t; + + +#define SURF_PLANEBACK 2 +#define SURF_DRAWSKY 4 +#define SURF_DRAWSPRITE 8 +#define SURF_DRAWTURB 0x10 +#define SURF_DRAWTILED 0x20 +#define SURF_DRAWBACKGROUND 0x40 +#define SURF_UNDERWATER 0x80 +#define SURF_DONTWARP 0x100 + +// !!! if this is changed, it must be changed in asm_draw.h too !!! +typedef struct +{ + unsigned short v[2]; + unsigned int cachededgeoffset; +} medge_t; + +typedef struct +{ + float vecs[2][4]; + float mipadjust; + texture_t *texture; + int flags; +} mtexinfo_t; + +#define VERTEXSIZE 7 + +typedef struct glpoly_s +{ + struct glpoly_s *next; + struct glpoly_s *chain; + struct glpoly_s *fb_chain; + int numverts; + int flags; // for SURF_UNDERWATER + float verts[4][VERTEXSIZE]; // variable sized (xyz s1t1 s2t2) +} glpoly_t; + +typedef struct msurface_s +{ + int visframe; // should be drawn when node is crossed + + mplane_t *plane; + int flags; + + int firstedge; // look up in model->surfedges[], negative numbers + int numedges; // are backwards edges + + short texturemins[2]; + short extents[2]; + + int light_s, light_t; // gl lightmap coordinates + + glpoly_t *polys; // multiple if warped + struct msurface_s *texturechain; + + mtexinfo_t *texinfo; + +// lighting info + int dlightframe; + int dlightbits; + + int lightmaptexturenum; + byte styles[MAXLIGHTMAPS]; + int cached_light[MAXLIGHTMAPS]; // values currently used in lightmap + qboolean cached_dlight; // true if dynamic light in cache + byte *samples; // [numstyles*surfsize] +} msurface_t; + +typedef struct mnode_s +{ +// common with leaf + int contents; // 0, to differentiate from leafs + int visframe; // node needs to be traversed if current + + float 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 a negative contents number + int visframe; // node needs to be traversed if current + + float minmaxs[6]; // for bounding box culling + + struct mnode_s *parent; + +// leaf specific + byte *compressed_vis; + efrag_t *efrags; + + msurface_t **firstmarksurface; + int nummarksurfaces; + int key; // BSP sequence number for leaf's contents + byte ambient_sound_level[NUM_AMBIENTS]; +} mleaf_t; + +// !!! if this is changed, it must be changed in asm_i386.h too !!! +typedef struct +{ + dclipnode_t *clipnodes; + mplane_t *planes; + int firstclipnode; + int lastclipnode; + vec3_t clip_mins; + vec3_t clip_maxs; +} hull_t; + +/* +============================================================================== + +SPRITE MODELS + +============================================================================== +*/ + + +// FIXME: shorten these? +typedef struct mspriteframe_s +{ + int width; + int height; + float up, down, left, right; + int gl_texturenum; +} mspriteframe_t; + +typedef struct +{ + int numframes; + float *intervals; + mspriteframe_t *frames[1]; +} mspritegroup_t; + +typedef struct +{ + spriteframetype_t type; + mspriteframe_t *frameptr; +} mspriteframedesc_t; + +typedef struct +{ + int type; + int maxwidth; + int maxheight; + int numframes; + float beamlength; // remove? + void *cachespot; // remove? + mspriteframedesc_t frames[1]; +} msprite_t; + + +/* +============================================================================== + +ALIAS MODELS + +Alias models are position independent, so the cache manager can move them. +============================================================================== +*/ + +typedef struct +{ + int firstpose; + int numposes; + float interval; + trivertx_t bboxmin; + trivertx_t bboxmax; + int frame; + char name[16]; +} maliasframedesc_t; + +typedef struct +{ + trivertx_t bboxmin; + trivertx_t bboxmax; + int frame; +} maliasgroupframedesc_t; + +typedef struct +{ + int numframes; + int intervals; + maliasgroupframedesc_t frames[1]; +} maliasgroup_t; + +// !!! if this is changed, it must be changed in asm_draw.h too !!! +typedef struct mtriangle_s { + int facesfront; + int vertindex[3]; +} mtriangle_t; + + +#define MAX_SKINS 32 +typedef struct { + int ident; + int version; + vec3_t scale; + vec3_t scale_origin; + float boundingradius; + vec3_t eyeposition; + int numskins; + int skinwidth; + int skinheight; + int numverts; + int numtris; + int numframes; + synctype_t synctype; + int flags; + float size; + + int numposes; + int poseverts; + int posedata; // numposes*poseverts trivert_t + int commands; // gl command list with embedded s/t + int gl_texturenum[MAX_SKINS][4]; + int fb_texturenum[MAX_SKINS][4]; + maliasframedesc_t frames[1]; // variable sized +} aliashdr_t; + +#define MAXALIASVERTS 1024 +#define MAXALIASFRAMES 256 +#define MAXALIASTRIS 2048 +extern aliashdr_t *pheader; +extern stvert_t stverts[MAXALIASVERTS]; +extern mtriangle_t triangles[MAXALIASTRIS]; +extern trivertx_t *poseverts[MAXALIASFRAMES]; + +//=================================================================== + +// +// Whole model +// + +typedef enum {mod_brush, mod_sprite, mod_alias} modtype_t; + +// some models are special +typedef enum {MOD_NORMAL, MOD_PLAYER, MOD_EYES, MOD_FLAME, MOD_THUNDERBOLT} modhint_t; + +#define EF_ROCKET 1 // leave a trail +#define EF_GRENADE 2 // leave a trail +#define EF_GIB 4 // leave a trail +#define EF_ROTATE 8 // rotate (bonus items) +#define EF_TRACER 16 // green split trail +#define EF_ZOMGIB 32 // small blood trail +#define EF_TRACER2 64 // orange split trail + rotate +#define EF_TRACER3 128 // purple trail + +typedef struct model_s +{ + char name[MAX_QPATH]; + qboolean needload; // bmodels and sprites don't cache normally + + modhint_t modhint; + + modtype_t type; + int numframes; + synctype_t synctype; + + int flags; + +// +// volume occupied by the model graphics +// + vec3_t mins, maxs; + float radius; + +// +// solid volume for clipping +// + 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; + mnode_t *nodes; + + int numtexinfo; + mtexinfo_t *texinfo; + + int numsurfaces; + msurface_t *surfaces; + + int numsurfedges; + int *surfedges; + + int numclipnodes; + dclipnode_t *clipnodes; + + int nummarksurfaces; + msurface_t **marksurfaces; + + hull_t hulls[MAX_MAP_HULLS]; + + int numtextures; + texture_t **textures; + + byte *visdata; + byte *lightdata; + char *entities; + + unsigned checksum; + unsigned checksum2; + +// +// additional model data +// + cache_user_t cache; // only access through Mod_Extradata + +} 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_LeafPVS (mleaf_t *leaf, model_t *model); + +#endif // __MODEL__ diff --git a/source/gl_ngraph.c b/source/gl_ngraph.c index 134cd564..9ee1900e 100644 --- a/source/gl_ngraph.c +++ b/source/gl_ngraph.c @@ -1,141 +1,141 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// gl_ngraph.c - -#include "quakedef.h" - -extern byte *draw_chars; // 8*8 graphic characters - -int netgraphtexture; // netgraph texture - -#define NET_GRAPHHEIGHT 32 - -static byte ngraph_texels[NET_GRAPHHEIGHT][NET_TIMINGS]; - -static void R_LineGraph (int x, int h) -{ - int i; - int s; - int color; - - s = NET_GRAPHHEIGHT; - - if (h == 10000) - color = 0x6f; // yellow - else if (h == 9999) - color = 0x4f; // red - else if (h == 9998) - color = 0xd0; // blue - else - color = 0xfe; // white - - if (h>s) - h = s; - - for (i=0 ; i>4; - col = num&15; - source = draw_chars + (row<<10) + (col<<3); - - for (drawline = 8; drawline; drawline--, y++) - { - for (nx=0 ; nx<8 ; nx++) - if (source[nx] != 255) - ngraph_texels[y][nx+x] = 0x60 + source[nx]; - source += 128; - } -} - - -/* -============== -R_NetGraph -============== -*/ -void R_NetGraph (void) -{ - int a, x, i, y; - int lost; - char st[80]; - unsigned ngraph_pixels[NET_GRAPHHEIGHT][NET_TIMINGS]; - - x = 0; - lost = CL_CalcNet(); - for (a=0 ; a>1); - y = vid.height - sb_lines - 24 - NET_GRAPHHEIGHT - 1; - - M_DrawTextBox (x, y, NET_TIMINGS/8, NET_GRAPHHEIGHT/8 + 1); - y += 8; - - sprintf(st, "%3i%% packet loss", lost); - Draw_String(8, y, st); - y += 8; - - GL_Bind(netgraphtexture); - - glTexImage2D (GL_TEXTURE_2D, 0, gl_alpha_format, - NET_TIMINGS, NET_GRAPHHEIGHT, 0, GL_RGBA, - GL_UNSIGNED_BYTE, ngraph_pixels); - - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - x = 8; - glColor3f (1,1,1); - glBegin (GL_QUADS); - glTexCoord2f (0, 0); - glVertex2f (x, y); - glTexCoord2f (1, 0); - glVertex2f (x+NET_TIMINGS, y); - glTexCoord2f (1, 1); - glVertex2f (x+NET_TIMINGS, y+NET_GRAPHHEIGHT); - glTexCoord2f (0, 1); - glVertex2f (x, y+NET_GRAPHHEIGHT); - glEnd (); -} - +/* +Copyright (C) 1996-1997 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. + +*/ +// gl_ngraph.c + +#include "quakedef.h" + +extern byte *draw_chars; // 8*8 graphic characters + +int netgraphtexture; // netgraph texture + +#define NET_GRAPHHEIGHT 32 + +static byte ngraph_texels[NET_GRAPHHEIGHT][NET_TIMINGS]; + +static void R_LineGraph (int x, int h) +{ + int i; + int s; + int color; + + s = NET_GRAPHHEIGHT; + + if (h == 10000) + color = 0x6f; // yellow + else if (h == 9999) + color = 0x4f; // red + else if (h == 9998) + color = 0xd0; // blue + else + color = 0xfe; // white + + if (h>s) + h = s; + + for (i=0 ; i>4; + col = num&15; + source = draw_chars + (row<<10) + (col<<3); + + for (drawline = 8; drawline; drawline--, y++) + { + for (nx=0 ; nx<8 ; nx++) + if (source[nx] != 255) + ngraph_texels[y][nx+x] = 0x60 + source[nx]; + source += 128; + } +} + + +/* +============== +R_NetGraph +============== +*/ +void R_NetGraph (void) +{ + int a, x, i, y; + int lost; + char st[80]; + unsigned ngraph_pixels[NET_GRAPHHEIGHT][NET_TIMINGS]; + + x = 0; + lost = CL_CalcNet(); + for (a=0 ; a>1); + y = vid.height - sb_lines - 24 - NET_GRAPHHEIGHT - 1; + + M_DrawTextBox (x, y, NET_TIMINGS/8, NET_GRAPHHEIGHT/8 + 1); + y += 8; + + sprintf(st, "%3i%% packet loss", lost); + Draw_String(8, y, st); + y += 8; + + GL_Bind(netgraphtexture); + + glTexImage2D (GL_TEXTURE_2D, 0, gl_alpha_format, + NET_TIMINGS, NET_GRAPHHEIGHT, 0, GL_RGBA, + GL_UNSIGNED_BYTE, ngraph_pixels); + + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + x = 8; + glColor3f (1,1,1); + glBegin (GL_QUADS); + glTexCoord2f (0, 0); + glVertex2f (x, y); + glTexCoord2f (1, 0); + glVertex2f (x+NET_TIMINGS, y); + glTexCoord2f (1, 1); + glVertex2f (x+NET_TIMINGS, y+NET_GRAPHHEIGHT); + glTexCoord2f (0, 1); + glVertex2f (x, y+NET_GRAPHHEIGHT); + glEnd (); +} + diff --git a/source/gl_refrag.c b/source/gl_refrag.c index 9d31a225..136659f7 100644 --- a/source/gl_refrag.c +++ b/source/gl_refrag.c @@ -1,230 +1,230 @@ -/* -Copyright (C) 1996-1997 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_efrag.c - -#include "quakedef.h" - -mnode_t *r_pefragtopnode; - - -//=========================================================================== - -/* -=============================================================================== - - ENTITY FRAGMENT FUNCTIONS - -=============================================================================== -*/ - -efrag_t **lastlink; - -vec3_t r_emins, r_emaxs; - -entity_t *r_addent; - - -/* -================ -R_RemoveEfrags - -Call when removing an object from the world or moving it to another position -================ -*/ -void R_RemoveEfrags (entity_t *ent) -{ - efrag_t *ef, *old, *walk, **prev; - - ef = ent->efrag; - - while (ef) - { - prev = &ef->leaf->efrags; - while (1) - { - walk = *prev; - if (!walk) - break; - if (walk == ef) - { // remove this fragment - *prev = ef->leafnext; - break; - } - else - prev = &walk->leafnext; - } - - old = ef; - ef = ef->entnext; - - // put it on the free list - old->entnext = cl.free_efrags; - cl.free_efrags = old; - } - - ent->efrag = NULL; -} - -/* -=================== -R_SplitEntityOnNode -=================== -*/ -void R_SplitEntityOnNode (mnode_t *node) -{ - efrag_t *ef; - mplane_t *splitplane; - mleaf_t *leaf; - int sides; - - if (node->contents == CONTENTS_SOLID) - { - return; - } - -// add an efrag if the node is a leaf - - if ( node->contents < 0) - { - if (!r_pefragtopnode) - r_pefragtopnode = node; - - leaf = (mleaf_t *)node; - -// grab an efrag off the free list - ef = cl.free_efrags; - if (!ef) - { - Con_Printf ("Too many efrags!\n"); - return; // no free fragments... - } - cl.free_efrags = cl.free_efrags->entnext; - - ef->entity = r_addent; - -// add the entity link - *lastlink = ef; - lastlink = &ef->entnext; - ef->entnext = NULL; - -// set the leaf links - ef->leaf = leaf; - ef->leafnext = leaf->efrags; - leaf->efrags = ef; - - return; - } - -// NODE_MIXED - - splitplane = node->plane; - sides = BOX_ON_PLANE_SIDE(r_emins, r_emaxs, splitplane); - - if (sides == 3) - { - // split on this plane - // if this is the first splitter of this bmodel, remember it - if (!r_pefragtopnode) - r_pefragtopnode = node; - } - -// recurse down the contacted sides - if (sides & 1) - R_SplitEntityOnNode (node->children[0]); - - if (sides & 2) - R_SplitEntityOnNode (node->children[1]); -} - - - -/* -=========== -R_AddEfrags -=========== -*/ -void R_AddEfrags (entity_t *ent) -{ - model_t *entmodel; - int i; - - if (!ent->model) - return; - - r_addent = ent; - - lastlink = &ent->efrag; - r_pefragtopnode = NULL; - - entmodel = ent->model; - - for (i=0 ; i<3 ; i++) - { - r_emins[i] = ent->origin[i] + entmodel->mins[i]; - r_emaxs[i] = ent->origin[i] + entmodel->maxs[i]; - } - - R_SplitEntityOnNode (cl.worldmodel->nodes); - - ent->topnode = r_pefragtopnode; -} - - -/* -================ -R_StoreEfrags - -// FIXME: a lot of this goes away with edge-based -================ -*/ -void R_StoreEfrags (efrag_t **ppefrag) -{ - entity_t *pent; - model_t *model; - efrag_t *pefrag; - - for (pefrag = *ppefrag ; pefrag ; pefrag = pefrag->leafnext) - { - pent = pefrag->entity; - model = pent->model; - - if (model->modhint == MOD_FLAME && !r_drawflame.value) - continue; - - switch (model->type) - { - case mod_alias: - case mod_brush: - case mod_sprite: - if ((pent->visframe != r_framecount) && - (cl_numvisedicts < MAX_VISEDICTS)) - { - cl_visedicts[cl_numvisedicts++] = *pent; - - // mark that we've recorded this entity for this frame - pent->visframe = r_framecount; - } - break; - - default: - Sys_Error ("R_StoreEfrags: Bad entity type %d\n", model->type); - } - } -} +/* +Copyright (C) 1996-1997 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_efrag.c + +#include "quakedef.h" + +mnode_t *r_pefragtopnode; + + +//=========================================================================== + +/* +=============================================================================== + + ENTITY FRAGMENT FUNCTIONS + +=============================================================================== +*/ + +efrag_t **lastlink; + +vec3_t r_emins, r_emaxs; + +entity_t *r_addent; + + +/* +================ +R_RemoveEfrags + +Call when removing an object from the world or moving it to another position +================ +*/ +void R_RemoveEfrags (entity_t *ent) +{ + efrag_t *ef, *old, *walk, **prev; + + ef = ent->efrag; + + while (ef) + { + prev = &ef->leaf->efrags; + while (1) + { + walk = *prev; + if (!walk) + break; + if (walk == ef) + { // remove this fragment + *prev = ef->leafnext; + break; + } + else + prev = &walk->leafnext; + } + + old = ef; + ef = ef->entnext; + + // put it on the free list + old->entnext = cl.free_efrags; + cl.free_efrags = old; + } + + ent->efrag = NULL; +} + +/* +=================== +R_SplitEntityOnNode +=================== +*/ +void R_SplitEntityOnNode (mnode_t *node) +{ + efrag_t *ef; + mplane_t *splitplane; + mleaf_t *leaf; + int sides; + + if (node->contents == CONTENTS_SOLID) + { + return; + } + +// add an efrag if the node is a leaf + + if ( node->contents < 0) + { + if (!r_pefragtopnode) + r_pefragtopnode = node; + + leaf = (mleaf_t *)node; + +// grab an efrag off the free list + ef = cl.free_efrags; + if (!ef) + { + Con_Printf ("Too many efrags!\n"); + return; // no free fragments... + } + cl.free_efrags = cl.free_efrags->entnext; + + ef->entity = r_addent; + +// add the entity link + *lastlink = ef; + lastlink = &ef->entnext; + ef->entnext = NULL; + +// set the leaf links + ef->leaf = leaf; + ef->leafnext = leaf->efrags; + leaf->efrags = ef; + + return; + } + +// NODE_MIXED + + splitplane = node->plane; + sides = BOX_ON_PLANE_SIDE(r_emins, r_emaxs, splitplane); + + if (sides == 3) + { + // split on this plane + // if this is the first splitter of this bmodel, remember it + if (!r_pefragtopnode) + r_pefragtopnode = node; + } + +// recurse down the contacted sides + if (sides & 1) + R_SplitEntityOnNode (node->children[0]); + + if (sides & 2) + R_SplitEntityOnNode (node->children[1]); +} + + + +/* +=========== +R_AddEfrags +=========== +*/ +void R_AddEfrags (entity_t *ent) +{ + model_t *entmodel; + int i; + + if (!ent->model) + return; + + r_addent = ent; + + lastlink = &ent->efrag; + r_pefragtopnode = NULL; + + entmodel = ent->model; + + for (i=0 ; i<3 ; i++) + { + r_emins[i] = ent->origin[i] + entmodel->mins[i]; + r_emaxs[i] = ent->origin[i] + entmodel->maxs[i]; + } + + R_SplitEntityOnNode (cl.worldmodel->nodes); + + ent->topnode = r_pefragtopnode; +} + + +/* +================ +R_StoreEfrags + +// FIXME: a lot of this goes away with edge-based +================ +*/ +void R_StoreEfrags (efrag_t **ppefrag) +{ + entity_t *pent; + model_t *model; + efrag_t *pefrag; + + for (pefrag = *ppefrag ; pefrag ; pefrag = pefrag->leafnext) + { + pent = pefrag->entity; + model = pent->model; + + if (model->modhint == MOD_FLAME && !r_drawflame.value) + continue; + + switch (model->type) + { + case mod_alias: + case mod_brush: + case mod_sprite: + if ((pent->visframe != r_framecount) && + (cl_numvisedicts < MAX_VISEDICTS)) + { + cl_visedicts[cl_numvisedicts++] = *pent; + + // mark that we've recorded this entity for this frame + pent->visframe = r_framecount; + } + break; + + default: + Sys_Error ("R_StoreEfrags: Bad entity type %d\n", model->type); + } + } +} diff --git a/source/gl_rlight.c b/source/gl_rlight.c index cd56410a..98531db3 100644 --- a/source/gl_rlight.c +++ b/source/gl_rlight.c @@ -1,384 +1,384 @@ -/* -Copyright (C) 1996-1997 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 "quakedef.h" - -int r_dlightframecount; - - -/* -================== -R_AnimateLight -================== -*/ -void R_AnimateLight (void) -{ - int i,j,k; - -// -// light animations -// 'm' is normal light, 'a' is no light, 'z' is double bright - i = (int)(cl.time*10); - for (j=0 ; j=0 ; i--) - { - a = i/16.0 * M_PI*2; - *bub_sin++ = sin(a); - *bub_cos++ = cos(a); - } -} - -void R_RenderDlight (dlight_t *light) -{ - int i, j; -// float a; - vec3_t v; - float rad; - float *bub_sin, *bub_cos; - vec3_t v_right, v_up; - float length; - - bub_sin = bubble_sintable; - bub_cos = bubble_costable; - rad = light->radius * 0.35; - - VectorSubtract (light->origin, r_origin, v); - length = VectorNormalize (v); - if (length < rad) - { // view is inside the dlight - V_AddLightBlend (1, 0.5, 0, light->radius * 0.0003); - return; - } - - glBegin (GL_TRIANGLE_FAN); -// glColor3f (0.2,0.1,0.0); -// glColor3f (0.2,0.1,0.05); // changed dimlight effect - glColor4f (light->color[0], light->color[1], light->color[2], - light->color[3]); - - v_right[0] = v[1]; - v_right[1] = -v[0]; - v_right[2] = 0; - VectorNormalize (v_right); - CrossProduct (v_right, v, v_up); - - if (length - rad > 8) - VectorScale (v, rad, v); - else { - // make sure the light bubble will not be clipped by - // near z clipping plane - VectorScale (v, length-8, v); - } - VectorSubtract (light->origin, v, v); - - glVertex3fv (v); - glColor3f (0,0,0); - for (i=16 ; i>=0 ; i--) - { -// a = i/16.0 * M_PI*2; - for (j=0 ; j<3 ; j++) - v[j] = light->origin[j] + (v_right[j]*(*bub_cos) + - + v_up[j]*(*bub_sin)) * rad; - bub_sin++; - bub_cos++; - glVertex3fv (v); - } - glEnd (); -} - -/* -============= -R_RenderDlights -============= -*/ -void R_RenderDlights (void) -{ - int i; - dlight_t *l; - - if (!gl_flashblend.value) - return; - - r_dlightframecount = r_framecount + 1; // because the count hasn't - // advanced yet for this frame - glDepthMask (0); - glDisable (GL_TEXTURE_2D); - glShadeModel (GL_SMOOTH); - glEnable (GL_BLEND); - glBlendFunc (GL_ONE, GL_ONE); - - l = cl_dlights; - for (i=0 ; idie < cl.time || !l->radius || !l->color[3]) - continue; - R_RenderDlight (l); - } - - glColor3f (1,1,1); - glDisable (GL_BLEND); - glEnable (GL_TEXTURE_2D); - glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glDepthMask (1); -} - - -/* -============================================================================= - -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 < 0) - return; - - splitplane = node->plane; - dist = DotProduct (light->origin, splitplane->normal) - splitplane->dist; - - if (dist > light->radius) - { - R_MarkLights (light, bit, node->children[0]); - return; - } - if (dist < -light->radius) - { - R_MarkLights (light, bit, node->children[1]); - return; - } - -// mark the polygons - surf = cl.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 (void) -{ - int i; - dlight_t *l; - - if (gl_flashblend.value) - return; - - r_dlightframecount = r_framecount + 1; // because the count hasn't - // advanced yet for this frame - l = cl_dlights; - - for (i=0 ; idie < cl.time || !l->radius) - continue; - R_MarkLights ( l, 1<nodes ); - } -} - - -/* -============================================================================= - -LIGHT SAMPLING - -============================================================================= -*/ - -mplane_t *lightplane; -vec3_t lightspot; - -int RecursiveLightPoint (mnode_t *node, vec3_t start, vec3_t end) -{ - int r; - float front, back, frac; - int side; - mplane_t *plane; - vec3_t mid; - msurface_t *surf; - int s, t, ds, dt; - int i; - mtexinfo_t *tex; - byte *lightmap; - unsigned scale; - int maps; - - if (node->contents < 0) - 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; - -// 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 anuthing - -// check for impact on this node - VectorCopy (mid, lightspot); - lightplane = plane; - - surf = cl.worldmodel->surfaces + node->firstsurface; - for (i=0 ; inumsurfaces ; i++, surf++) - { - if (surf->flags & SURF_DRAWTILED) - 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; - r = 0; - if (lightmap) - { - - lightmap += dt * ((surf->extents[0]>>4)+1) + ds; - - for (maps = 0 ; maps < MAXLIGHTMAPS && surf->styles[maps] != 255 ; - maps++) - { - scale = d_lightstylevalue[surf->styles[maps]]; - r += *lightmap * scale; - lightmap += ((surf->extents[0]>>4)+1) * - ((surf->extents[1]>>4)+1); - } - - r >>= 8; - } - - return r; - } - -// go down back side - return RecursiveLightPoint (node->children[!side], mid, end); -} - -int R_LightPoint (vec3_t p) -{ - vec3_t end; - int r; - - if (!cl.worldmodel->lightdata) - return 255; - - end[0] = p[0]; - end[1] = p[1]; - end[2] = p[2] - 2048; - - r = RecursiveLightPoint (cl.worldmodel->nodes, p, end); - - if (r == -1) - r = 0; - - return r; -} - +/* +Copyright (C) 1996-1997 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 "quakedef.h" + +int r_dlightframecount; + + +/* +================== +R_AnimateLight +================== +*/ +void R_AnimateLight (void) +{ + int i,j,k; + +// +// light animations +// 'm' is normal light, 'a' is no light, 'z' is double bright + i = (int)(cl.time*10); + for (j=0 ; j=0 ; i--) + { + a = i/16.0 * M_PI*2; + *bub_sin++ = sin(a); + *bub_cos++ = cos(a); + } +} + +void R_RenderDlight (dlight_t *light) +{ + int i, j; +// float a; + vec3_t v; + float rad; + float *bub_sin, *bub_cos; + vec3_t v_right, v_up; + float length; + + bub_sin = bubble_sintable; + bub_cos = bubble_costable; + rad = light->radius * 0.35; + + VectorSubtract (light->origin, r_origin, v); + length = VectorNormalize (v); + if (length < rad) + { // view is inside the dlight + V_AddLightBlend (1, 0.5, 0, light->radius * 0.0003); + return; + } + + glBegin (GL_TRIANGLE_FAN); +// glColor3f (0.2,0.1,0.0); +// glColor3f (0.2,0.1,0.05); // changed dimlight effect + glColor4f (light->color[0], light->color[1], light->color[2], + light->color[3]); + + v_right[0] = v[1]; + v_right[1] = -v[0]; + v_right[2] = 0; + VectorNormalize (v_right); + CrossProduct (v_right, v, v_up); + + if (length - rad > 8) + VectorScale (v, rad, v); + else { + // make sure the light bubble will not be clipped by + // near z clipping plane + VectorScale (v, length-8, v); + } + VectorSubtract (light->origin, v, v); + + glVertex3fv (v); + glColor3f (0,0,0); + for (i=16 ; i>=0 ; i--) + { +// a = i/16.0 * M_PI*2; + for (j=0 ; j<3 ; j++) + v[j] = light->origin[j] + (v_right[j]*(*bub_cos) + + + v_up[j]*(*bub_sin)) * rad; + bub_sin++; + bub_cos++; + glVertex3fv (v); + } + glEnd (); +} + +/* +============= +R_RenderDlights +============= +*/ +void R_RenderDlights (void) +{ + int i; + dlight_t *l; + + if (!gl_flashblend.value) + return; + + r_dlightframecount = r_framecount + 1; // because the count hasn't + // advanced yet for this frame + glDepthMask (0); + glDisable (GL_TEXTURE_2D); + glShadeModel (GL_SMOOTH); + glEnable (GL_BLEND); + glBlendFunc (GL_ONE, GL_ONE); + + l = cl_dlights; + for (i=0 ; idie < cl.time || !l->radius || !l->color[3]) + continue; + R_RenderDlight (l); + } + + glColor3f (1,1,1); + glDisable (GL_BLEND); + glEnable (GL_TEXTURE_2D); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDepthMask (1); +} + + +/* +============================================================================= + +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 < 0) + return; + + splitplane = node->plane; + dist = DotProduct (light->origin, splitplane->normal) - splitplane->dist; + + if (dist > light->radius) + { + R_MarkLights (light, bit, node->children[0]); + return; + } + if (dist < -light->radius) + { + R_MarkLights (light, bit, node->children[1]); + return; + } + +// mark the polygons + surf = cl.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 (void) +{ + int i; + dlight_t *l; + + if (gl_flashblend.value) + return; + + r_dlightframecount = r_framecount + 1; // because the count hasn't + // advanced yet for this frame + l = cl_dlights; + + for (i=0 ; idie < cl.time || !l->radius) + continue; + R_MarkLights ( l, 1<nodes ); + } +} + + +/* +============================================================================= + +LIGHT SAMPLING + +============================================================================= +*/ + +mplane_t *lightplane; +vec3_t lightspot; + +int RecursiveLightPoint (mnode_t *node, vec3_t start, vec3_t end) +{ + int r; + float front, back, frac; + int side; + mplane_t *plane; + vec3_t mid; + msurface_t *surf; + int s, t, ds, dt; + int i; + mtexinfo_t *tex; + byte *lightmap; + unsigned scale; + int maps; + + if (node->contents < 0) + 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; + +// 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 anuthing + +// check for impact on this node + VectorCopy (mid, lightspot); + lightplane = plane; + + surf = cl.worldmodel->surfaces + node->firstsurface; + for (i=0 ; inumsurfaces ; i++, surf++) + { + if (surf->flags & SURF_DRAWTILED) + 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; + r = 0; + if (lightmap) + { + + lightmap += dt * ((surf->extents[0]>>4)+1) + ds; + + for (maps = 0 ; maps < MAXLIGHTMAPS && surf->styles[maps] != 255 ; + maps++) + { + scale = d_lightstylevalue[surf->styles[maps]]; + r += *lightmap * scale; + lightmap += ((surf->extents[0]>>4)+1) * + ((surf->extents[1]>>4)+1); + } + + r >>= 8; + } + + return r; + } + +// go down back side + return RecursiveLightPoint (node->children[!side], mid, end); +} + +int R_LightPoint (vec3_t p) +{ + vec3_t end; + int r; + + if (!cl.worldmodel->lightdata) + return 255; + + end[0] = p[0]; + end[1] = p[1]; + end[2] = p[2] - 2048; + + r = RecursiveLightPoint (cl.worldmodel->nodes, p, end); + + if (r == -1) + r = 0; + + return r; +} + diff --git a/source/gl_rmain.c b/source/gl_rmain.c index 4d817f44..c0a6f872 100644 --- a/source/gl_rmain.c +++ b/source/gl_rmain.c @@ -1,1239 +1,1243 @@ -/* -Copyright (C) 1996-1997 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 "quakedef.h" -#include "sound.h" - -entity_t r_worldentity; - -qboolean r_cache_thrash; // compatability - -vec3_t modelorg, r_entorigin; -entity_t *currententity; - -int r_visframecount; // bumped when going to a new PVS -int r_framecount; // used for dlight push checking - -mplane_t frustum[4]; - -int c_brush_polys, c_alias_polys; - -qboolean envmap; // true during envmap command capture - -int currenttexture = -1; // to avoid unnecessary texture sets - -int cnttextures[2] = {-1, -1}; // cached - -int particletexture; // little dot for particles -int playertextures; // up to 16 color translated skins - -int mirrortexturenum; // quake texturenum, not gltexturenum -qboolean mirror; -mplane_t *mirror_plane; - -// -// view origin -// -vec3_t vup; -vec3_t vpn; -vec3_t vright; -vec3_t r_origin; - -float r_world_matrix[16]; -float r_base_world_matrix[16]; - -// -// screen size info -// -refdef_t r_refdef; - -mleaf_t *r_viewleaf, *r_oldviewleaf; -mleaf_t *r_viewleaf2, *r_oldviewleaf2; // for watervis hack - -texture_t *r_notexture_mip; - -int d_lightstylevalue[256]; // 8.8 fraction of base light value - - -void R_MarkLeaves (void); - -cvar_t r_norefresh = {"r_norefresh","0"}; -cvar_t r_drawentities = {"r_drawentities","1"}; -cvar_t r_drawviewmodel = {"r_drawviewmodel","1"}; -cvar_t r_drawflame = {"r_drawflame","1"}; -cvar_t r_speeds = {"r_speeds","0"}; -cvar_t r_fullbright = {"r_fullbright","0"}; -cvar_t r_lightmap = {"r_lightmap","0"}; -cvar_t r_shadows = {"r_shadows","0"}; -cvar_t r_mirroralpha = {"r_mirroralpha","1"}; -cvar_t r_wateralpha = {"r_wateralpha","1"}; -cvar_t r_dynamic = {"r_dynamic","1"}; -cvar_t r_novis = {"r_novis","0"}; -cvar_t r_netgraph = {"r_netgraph","0"}; -cvar_t r_watervishack = {"r_watervishack", "1"}; -cvar_t r_fullbrightSkins = {"r_fullbrightSkins", "0"}; - -cvar_t gl_clear = {"gl_clear","0"}; -cvar_t gl_cull = {"gl_cull","1"}; -cvar_t gl_texsort = {"gl_texsort","1"}; -cvar_t gl_smoothmodels = {"gl_smoothmodels","1"}; -cvar_t gl_affinemodels = {"gl_affinemodels","0"}; -cvar_t gl_polyblend = {"gl_polyblend","1"}; -cvar_t gl_flashblend = {"gl_flashblend","1"}; -cvar_t gl_playermip = {"gl_playermip","0"}; -cvar_t gl_nocolors = {"gl_nocolors","0"}; -cvar_t gl_keeptjunctions = {"gl_keeptjunctions","1"}; -cvar_t gl_reporttjunctions = {"gl_reporttjunctions","0"}; -cvar_t gl_finish = {"gl_finish","0"}; -cvar_t gl_fb_depthhack = {"gl_fb_depthhack","1"}; -cvar_t gl_fb_bmodels = {"gl_fb_bmodels","1"}; -cvar_t gl_fb_models = {"gl_fb_models","1"}; - -extern cvar_t gl_ztrick; -extern cvar_t scr_fov; - -#ifndef _WIN32 -qboolean vid_hwgamma_enabled = false; // dummy -#endif - -/* -================= -R_CullBox - -Returns true if the box is completely outside the frustom -================= -*/ -qboolean R_CullBox (vec3_t mins, vec3_t maxs) -{ - int i; - - for (i=0 ; i<4 ; i++) - if (BoxOnPlaneSide (mins, maxs, &frustum[i]) == 2) - return true; - return false; -} - - -void R_RotateForEntity (entity_t *e) -{ - glTranslatef (e->origin[0], e->origin[1], e->origin[2]); - - glRotatef (e->angles[1], 0, 0, 1); - glRotatef (-e->angles[0], 0, 1, 0); - //ZOID: fixed z angle - glRotatef (e->angles[2], 1, 0, 0); -} - -/* -============================================================= - - SPRITE MODELS - -============================================================= -*/ - -/* -================ -R_GetSpriteFrame -================ -*/ -mspriteframe_t *R_GetSpriteFrame (entity_t *currententity) -{ - msprite_t *psprite; - mspritegroup_t *pspritegroup; - mspriteframe_t *pspriteframe; - int i, numframes, frame; - float *pintervals, fullinterval, targettime, time; - - psprite = currententity->model->cache.data; - frame = currententity->frame; - - if ((frame >= psprite->numframes) || (frame < 0)) - { - Con_Printf ("R_DrawSprite: no such frame %d\n", frame); - frame = 0; - } - - if (psprite->frames[frame].type == SPR_SINGLE) - { - pspriteframe = psprite->frames[frame].frameptr; - } - else - { - pspritegroup = (mspritegroup_t *)psprite->frames[frame].frameptr; - pintervals = pspritegroup->intervals; - numframes = pspritegroup->numframes; - fullinterval = pintervals[numframes-1]; - - time = cl.time + currententity->syncbase; - - // when loading in Mod_LoadSpriteGroup, we guaranteed all interval values - // are positive, so we don't have to worry about division by 0 - targettime = time - ((int)(time / fullinterval)) * fullinterval; - - for (i=0 ; i<(numframes-1) ; i++) - { - if (pintervals[i] > targettime) - break; - } - - pspriteframe = pspritegroup->frames[i]; - } - - return pspriteframe; -} - - -/* -================= -R_DrawSpriteModel - -================= -*/ -void R_DrawSpriteModel (entity_t *e) -{ - vec3_t point; - mspriteframe_t *frame; - float *up, *right; - vec3_t v_forward, v_right, v_up; - msprite_t *psprite; - - // don't even bother culling, because it's just a single - // polygon without a surface cache - frame = R_GetSpriteFrame (e); - psprite = currententity->model->cache.data; - - if (psprite->type == SPR_ORIENTED) - { // bullet marks on walls - AngleVectors (currententity->angles, v_forward, v_right, v_up); - up = v_up; - right = v_right; - } - else - { // normal sprite - up = vup; - right = vright; - } - - glColor3f (1,1,1); - - GL_DisableMultitexture(); - - GL_Bind(frame->gl_texturenum); - - glEnable (GL_ALPHA_TEST); - glBegin (GL_QUADS); - - glEnable (GL_ALPHA_TEST); - glBegin (GL_QUADS); - - glTexCoord2f (0, 1); - VectorMA (e->origin, frame->down, up, point); - VectorMA (point, frame->left, right, point); - glVertex3fv (point); - - glTexCoord2f (0, 0); - VectorMA (e->origin, frame->up, up, point); - VectorMA (point, frame->left, right, point); - glVertex3fv (point); - - glTexCoord2f (1, 0); - VectorMA (e->origin, frame->up, up, point); - VectorMA (point, frame->right, right, point); - glVertex3fv (point); - - glTexCoord2f (1, 1); - VectorMA (e->origin, frame->down, up, point); - VectorMA (point, frame->right, right, point); - glVertex3fv (point); - - glEnd (); - - glDisable (GL_ALPHA_TEST); -} - -/* -============================================================= - - ALIAS MODELS - -============================================================= -*/ - - -#define NUMVERTEXNORMALS 162 - -float r_avertexnormals[NUMVERTEXNORMALS][3] = { -#include "anorms.h" -}; - -vec3_t shadevector; -float shadelight, ambientlight; - -// precalculated dot products for quantized angles -#define SHADEDOT_QUANT 16 -float r_avertexnormal_dots[SHADEDOT_QUANT][256] = -#include "anorm_dots.h" -; - -float *shadedots = r_avertexnormal_dots[0]; - -int lastposenum; - -/* -============= -GL_DrawAliasFrame -============= -*/ -void GL_DrawAliasFrame (aliashdr_t *paliashdr, int posenum) -{ - float l; - trivertx_t *verts; - int *order; - int count; - -lastposenum = posenum; - - verts = (trivertx_t *)((byte *)paliashdr + paliashdr->posedata); - verts += posenum * paliashdr->poseverts; - order = (int *)((byte *)paliashdr + paliashdr->commands); - - while (1) - { - // get the vertex count and primitive type - count = *order++; - if (!count) - break; // done - if (count < 0) - { - count = -count; - glBegin (GL_TRIANGLE_FAN); - } - else - glBegin (GL_TRIANGLE_STRIP); - - do - { - // texture coordinates come from the draw list - glTexCoord2f (((float *)order)[0], ((float *)order)[1]); - order += 2; - - // normals and vertexes come from the frame list - l = (shadedots[verts->lightnormalindex] * shadelight + ambientlight) / 256; - if (l > 1) - l = 1; - glColor3f (l, l, l); - glVertex3f (verts->v[0], verts->v[1], verts->v[2]); - verts++; - } while (--count); - - glEnd (); - } -} - - -/* -============= -GL_DrawAliasShadow -============= -*/ -extern vec3_t lightspot; - -void GL_DrawAliasShadow (aliashdr_t *paliashdr, int posenum) -{ - trivertx_t *verts; - int *order; - vec3_t point; - float height, lheight; - int count; - - lheight = currententity->origin[2] - lightspot[2]; - - height = 0; - verts = (trivertx_t *)((byte *)paliashdr + paliashdr->posedata); - verts += posenum * paliashdr->poseverts; - order = (int *)((byte *)paliashdr + paliashdr->commands); - - height = -lheight + 1.0; - - while (1) - { - // get the vertex count and primitive type - count = *order++; - if (!count) - break; // done - if (count < 0) - { - count = -count; - glBegin (GL_TRIANGLE_FAN); - } - else - glBegin (GL_TRIANGLE_STRIP); - - do - { - // texture coordinates come from the draw list - // (skipped for shadows) glTexCoord2fv ((float *)order); - order += 2; - - // normals and vertexes come from the frame list - point[0] = verts->v[0] * paliashdr->scale[0] + paliashdr->scale_origin[0]; - point[1] = verts->v[1] * paliashdr->scale[1] + paliashdr->scale_origin[1]; - point[2] = verts->v[2] * paliashdr->scale[2] + paliashdr->scale_origin[2]; - - point[0] -= shadevector[0]*(point[2]+lheight); - point[1] -= shadevector[1]*(point[2]+lheight); - point[2] = height; -// height -= 0.001; - glVertex3fv (point); - - verts++; - } while (--count); - - glEnd (); - } -} - - - -/* -================= -R_SetupAliasFrame - -================= -*/ -void R_SetupAliasFrame (int frame, aliashdr_t *paliashdr) -{ - int pose, numposes; - float interval; - - if ((frame >= paliashdr->numframes) || (frame < 0)) - { - Con_DPrintf ("R_AliasSetupFrame: no such frame %d\n", frame); - frame = 0; - } - - pose = paliashdr->frames[frame].firstpose; - numposes = paliashdr->frames[frame].numposes; - - if (numposes > 1) - { - interval = paliashdr->frames[frame].interval; - pose += (int)(cl.time / interval) % numposes; - } - - GL_DrawAliasFrame (paliashdr, pose); -} - - - -/* -================= -R_DrawAliasModel - -================= -*/ -void R_DrawAliasModel (entity_t *ent) -{ - int i; - int lnum; - vec3_t dist; - float add; - model_t *clmodel; - vec3_t mins, maxs; - aliashdr_t *paliashdr; - float an; - int anim, skinnum; - int texture; - qboolean full_light; - - clmodel = ent->model; - - VectorAdd (ent->origin, clmodel->mins, mins); - VectorAdd (ent->origin, clmodel->maxs, maxs); - - if (R_CullBox (mins, maxs)) - return; - - VectorCopy (ent->origin, r_entorigin); - VectorSubtract (r_origin, r_entorigin, modelorg); - - // - // get lighting information - // - -// make thunderbolt and torches full light - if (clmodel->modhint == MOD_THUNDERBOLT) { - ambientlight = 210; - shadelight = 0; - full_light = true; - } else if (clmodel->modhint == MOD_FLAME) { - ambientlight = 255; - shadelight = 0; - full_light = true; - } - else if (clmodel->modhint == MOD_PLAYER && r_fullbrightSkins.value - && !cl.teamfortress) { - ambientlight = shadelight = 128; - full_light = true; - } - else - { - // normal lighting - - full_light = false; - ambientlight = shadelight = R_LightPoint (ent->origin); - - for (lnum=0 ; lnum= cl.time) - { - VectorSubtract (ent->origin, - cl_dlights[lnum].origin, - dist); - add = cl_dlights[lnum].radius - Length(dist); - - if (add > 0) - ambientlight += add; - } - } - - // clamp lighting so it doesn't overbright as much - if (ambientlight > 128) - ambientlight = 128; - if (ambientlight + shadelight > 192) - shadelight = 192 - ambientlight; - - // always give the gun some light - if (ent == &cl.viewent && ambientlight < 24) - ambientlight = shadelight = 24; - - // never allow players to go totally black - if (clmodel->modhint == MOD_PLAYER) { - if (ambientlight < 8) - ambientlight = shadelight = 8; - } - } - - shadedots = r_avertexnormal_dots[((int)(ent->angles[1] * (SHADEDOT_QUANT / 360.0))) & (SHADEDOT_QUANT - 1)]; - - an = ent->angles[1]/180*M_PI; - shadevector[0] = cos(-an); - shadevector[1] = sin(-an); - shadevector[2] = 1; - VectorNormalize (shadevector); - - // - // locate the proper data - // - paliashdr = (aliashdr_t *)Mod_Extradata (ent->model); - - c_alias_polys += paliashdr->numtris; - - // - // draw all the triangles - // - - GL_DisableMultitexture(); - - glPushMatrix (); - R_RotateForEntity (ent); - - if (clmodel->modhint == MOD_EYES) { - glTranslatef (paliashdr->scale_origin[0], paliashdr->scale_origin[1], paliashdr->scale_origin[2] - (22 + 8)); - // double size of eyes, since they are really hard to see in gl - glScalef (paliashdr->scale[0]*2, paliashdr->scale[1]*2, paliashdr->scale[2]*2); - } else { - glTranslatef (paliashdr->scale_origin[0], paliashdr->scale_origin[1], paliashdr->scale_origin[2]); - glScalef (paliashdr->scale[0], paliashdr->scale[1], paliashdr->scale[2]); - } - - anim = (int)(cl.time*10) & 3; - skinnum = ent->skinnum; - if ((skinnum >= paliashdr->numskins) || (skinnum < 0)) { - Con_DPrintf ("R_DrawAliasModel: no such skin # %d\n", skinnum); - skinnum = 0; - } - - texture = paliashdr->gl_texturenum[skinnum][anim]; - - // we can't dynamically colormap textures, so they are cached - // separately for the players. Heads are just uncolored. - if (ent->scoreboard && !gl_nocolors.value) - { - i = ent->scoreboard - cl.players; - if (!ent->scoreboard->skin) { - Skin_Find(ent->scoreboard); - R_TranslatePlayerSkin(i); - } - if (i >= 0 && iframe, paliashdr); - - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); - - if (!full_light && gl_fb_models.value) { - int fb_texture = paliashdr->fb_texturenum[skinnum][anim]; - if (fb_texture) { - glEnable (GL_BLEND); - GL_Bind (fb_texture); - R_SetupAliasFrame (ent->frame, paliashdr); - glDisable (GL_BLEND); - } - } - - glShadeModel (GL_FLAT); - if (gl_affinemodels.value) - glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); - - glPopMatrix (); - - if (r_shadows.value && !full_light && ent != &cl.viewent) - { - glPushMatrix (); - - glTranslatef (ent->origin[0], ent->origin[1], ent->origin[2]); - glRotatef (ent->angles[1], 0, 0, 1); - //FIXME glRotatef (-ent->angles[0], 0, 1, 0); - //FIXME glRotatef (ent->angles[2], 1, 0, 0); - - glDisable (GL_TEXTURE_2D); - glEnable (GL_BLEND); - glColor4f (0,0,0,0.5); - GL_DrawAliasShadow (paliashdr, lastposenum); - glEnable (GL_TEXTURE_2D); - glDisable (GL_BLEND); - glColor4f (1,1,1,1); - glPopMatrix (); - } - -} - -//================================================================================== - -/* -============= -R_DrawEntitiesOnList -============= -*/ -void R_DrawEntitiesOnList (void) -{ - int i; - - if (!r_drawentities.value) - return; - - // draw sprites seperately, because of alpha blending - for (i=0 ; imodel->type) - { - case mod_alias: - R_DrawAliasModel (currententity); - break; - - case mod_brush: - R_DrawBrushModel (currententity); - break; - - default: - break; - } - } - - for (i=0 ; imodel->type) - { - case mod_sprite: - R_DrawSpriteModel (currententity); - break; - - default : - break; - } - } -} - -/* -============= -R_DrawViewModel -============= -*/ -void R_DrawViewModel (void) -{ - if (!r_drawviewmodel.value || - (r_drawviewmodel.value == 2 && scr_fov.value > 90) || - !Cam_DrawViewModel()) - return; - - if (envmap) - return; - - if (!r_drawentities.value) - return; - - if (cl.stats[STAT_ITEMS] & IT_INVISIBILITY) - return; - - if (cl.stats[STAT_HEALTH] <= 0) - return; - - currententity = &cl.viewent; - if (!currententity->model) - return; - - // hack the depth range to prevent view model from poking into walls - glDepthRange (gldepthmin, gldepthmin + 0.3*(gldepthmax-gldepthmin)); - R_DrawAliasModel (currententity); - glDepthRange (gldepthmin, gldepthmax); -} - - -/* -============ -R_PolyBlend -============ -*/ -void R_PolyBlend (void) -{ - if (vid_hwgamma_enabled) - return; - if (!v_blend[3]) - return; - - glDisable (GL_ALPHA_TEST); - glEnable (GL_BLEND); - glDisable (GL_TEXTURE_2D); - - glColor4fv (v_blend); - - glBegin (GL_QUADS); - glVertex2f (r_refdef.vrect.x, r_refdef.vrect.y); - glVertex2f (r_refdef.vrect.x + r_refdef.vrect.width, r_refdef.vrect.y); - glVertex2f (r_refdef.vrect.x + r_refdef.vrect.width, r_refdef.vrect.y + r_refdef.vrect.height); - glVertex2f (r_refdef.vrect.x, r_refdef.vrect.y + r_refdef.vrect.height); - glEnd (); - - glDisable (GL_BLEND); - glEnable (GL_TEXTURE_2D); - glEnable (GL_ALPHA_TEST); -} - -/* -================ -R_BrightenScreen -================ -*/ -void R_BrightenScreen (void) -{ - extern float vid_gamma; - float f; - - if (vid_hwgamma_enabled) - return; - if (gl_contrast.value <= 1.0) - return; - - f = gl_contrast.value; - if (f > 3) - f = 3; - - f = pow (f, vid_gamma); - - glDisable (GL_TEXTURE_2D); - glEnable (GL_BLEND); - glBlendFunc (GL_DST_COLOR, GL_ONE); - glBegin (GL_QUADS); - while (f > 1) - { - if (f >= 2) - glColor3f (1, 1, 1); - else - glColor3f (f - 1, f - 1, f - 1); - glVertex2f (0, 0); - glVertex2f (vid.width, 0); - glVertex2f (vid.width, vid.height); - glVertex2f (0, vid.height); - f *= 0.5; - } - glEnd (); - glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glEnable (GL_TEXTURE_2D); - glDisable (GL_BLEND); -} - -int SignbitsForPlane (mplane_t *out) -{ - int bits, j; - - // for fast box on planeside test - - bits = 0; - for (j=0 ; j<3 ; j++) - { - if (out->normal[j] < 0) - bits |= 1<contents <= CONTENTS_WATER && - r_viewleaf->contents >= CONTENTS_LAVA) { - // Test the point 10 units above. 10 seems to be enough - // for fov values up to 140 - testorigin[2] += 10; - testleaf = Mod_PointInLeaf (testorigin, cl.worldmodel); - if (testleaf->contents == CONTENTS_EMPTY) - r_viewleaf2 = testleaf; - } - else if (r_viewleaf->contents == CONTENTS_EMPTY) { - testorigin[2] -= 10; - testleaf = Mod_PointInLeaf (testorigin, cl.worldmodel); - if (testleaf->contents <= CONTENTS_WATER && - testleaf->contents >= CONTENTS_LAVA) - r_viewleaf2 = testleaf; - } - } else - r_viewleaf2 = r_oldviewleaf2 = NULL; - - V_SetContentsColor (r_viewleaf->contents); - V_CalcBlend (); - - r_cache_thrash = false; - - c_brush_polys = 0; - c_alias_polys = 0; - -} - - -void MYgluPerspective( GLdouble fovy, GLdouble aspect, - GLdouble zNear, GLdouble zFar ) -{ - GLdouble xmin, xmax, ymin, ymax; - - ymax = zNear * tan( fovy * M_PI / 360.0 ); - ymin = -ymax; - - xmin = ymin * aspect; - xmax = ymax * aspect; - - glFrustum( xmin, xmax, ymin, ymax, zNear, zFar ); -} - - -/* -============= -R_SetupGL -============= -*/ -void R_SetupGL (void) -{ - float screenaspect; - extern int glwidth, glheight; - int x, x2, y2, y, w, h; - - // - // set up viewpoint - // - glMatrixMode(GL_PROJECTION); - glLoadIdentity (); - x = r_refdef.vrect.x * glwidth/vid.width; - x2 = (r_refdef.vrect.x + r_refdef.vrect.width) * glwidth/vid.width; - y = (vid.height-r_refdef.vrect.y) * glheight/vid.height; - y2 = (vid.height - (r_refdef.vrect.y + r_refdef.vrect.height)) * glheight/vid.height; - - // fudge around because of frac screen scale - if (x > 0) - x--; - if (x2 < glwidth) - x2++; - if (y2 < 0) - y2--; - if (y < glheight) - y++; - - w = x2 - x; - h = y - y2; - - if (envmap) - { - x = y2 = 0; - w = h = 256; - } - - glViewport (glx + x, gly + y2, w, h); - screenaspect = (float)r_refdef.vrect.width/r_refdef.vrect.height; -// yfov = 2*atan((float)r_refdef.vrect.height/r_refdef.vrect.width)*180/M_PI; -// yfov = (2.0 * tan (scr_fov.value/360*M_PI)) / screenaspect; -// yfov = 2*atan((float)r_refdef.vrect.height/r_refdef.vrect.width)*(scr_fov.value*2)/M_PI; -// MYgluPerspective (yfov, screenaspect, 4, 4096); - MYgluPerspective (r_refdef.fov_y, screenaspect, 4, 4096); - - if (mirror) - { - if (mirror_plane->normal[2]) - glScalef (1, -1, 1); - else - glScalef (-1, 1, 1); - glCullFace(GL_BACK); - } - else - glCullFace(GL_FRONT); - - glMatrixMode(GL_MODELVIEW); - glLoadIdentity (); - - glRotatef (-90, 1, 0, 0); // put Z going up - glRotatef (90, 0, 0, 1); // put Z going up - glRotatef (-r_refdef.viewangles[2], 1, 0, 0); - glRotatef (-r_refdef.viewangles[0], 0, 1, 0); - glRotatef (-r_refdef.viewangles[1], 0, 0, 1); - glTranslatef (-r_refdef.vieworg[0], -r_refdef.vieworg[1], -r_refdef.vieworg[2]); - - glGetFloatv (GL_MODELVIEW_MATRIX, r_world_matrix); - - // - // set drawing parms - // - if (gl_cull.value) - glEnable(GL_CULL_FACE); - else - glDisable(GL_CULL_FACE); - - glDisable(GL_BLEND); - glDisable(GL_ALPHA_TEST); - glEnable(GL_DEPTH_TEST); -} - -/* -================ -R_RenderScene - -r_refdef must be set before the first call -================ -*/ -void R_RenderScene (void) -{ - R_SetupFrame (); - - R_SetFrustum (); - - R_SetupGL (); - - R_MarkLeaves (); // done here so we know if we're in water - - R_DrawWorld (); // adds static entities to the list - - S_ExtraUpdate (); // don't let sound get messed up if going slow - - R_DrawEntitiesOnList (); - - GL_DisableMultitexture(); - - R_RenderDlights (); - - R_DrawParticles (); - -#ifdef GLTEST - Test_Draw (); -#endif - -} - - -/* -============= -R_Clear -============= -*/ -int gl_ztrickframe = 0; -void R_Clear (void) -{ - static qboolean cleartogray; - qboolean clear = false; - - if (gl_clear.value) { - clear = true; - if (cleartogray) { - glClearColor (1, 0, 0, 0); - cleartogray = false; - } - } - else if (!vid_hwgamma_enabled && gl_contrast.value > 1) { - clear = true; - if (!cleartogray) { - glClearColor (0.1, 0.1, 0.1, 0); - cleartogray = true; - } - } - - if (r_mirroralpha.value != 1.0) - { - if (clear) - glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - else - glClear (GL_DEPTH_BUFFER_BIT); - gldepthmin = 0; - gldepthmax = 0.5; - glDepthFunc (GL_LEQUAL); - } - else if (gl_ztrick.value) - { - if (clear) - glClear (GL_COLOR_BUFFER_BIT); - - gl_ztrickframe = !gl_ztrickframe; - if (gl_ztrickframe) - { - gldepthmin = 0; - gldepthmax = 0.49999; - glDepthFunc (GL_LEQUAL); - } - else - { - gldepthmin = 1; - gldepthmax = 0.5; - glDepthFunc (GL_GEQUAL); - } - } - else - { - if (clear) - glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - else - glClear (GL_DEPTH_BUFFER_BIT); - gldepthmin = 0; - gldepthmax = 1; - glDepthFunc (GL_LEQUAL); - } - - glDepthRange (gldepthmin, gldepthmax); -} - -#if 0 //!!! FIXME, Zoid, mirror is disabled for now -/* -============= -R_Mirror -============= -*/ -void R_Mirror (void) -{ - float d; - msurface_t *s; - entity_t *ent; - - if (!mirror) - return; - - memcpy (r_base_world_matrix, r_world_matrix, sizeof(r_base_world_matrix)); - - d = DotProduct (r_refdef.vieworg, mirror_plane->normal) - mirror_plane->dist; - VectorMA (r_refdef.vieworg, -2*d, mirror_plane->normal, r_refdef.vieworg); - - d = DotProduct (vpn, mirror_plane->normal); - VectorMA (vpn, -2*d, mirror_plane->normal, vpn); - - r_refdef.viewangles[0] = -asin (vpn[2])/M_PI*180; - r_refdef.viewangles[1] = atan2 (vpn[1], vpn[0])/M_PI*180; - r_refdef.viewangles[2] = -r_refdef.viewangles[2]; - - ent = &cl_entities[cl.viewentity]; - if (cl_numvisedicts < MAX_VISEDICTS) - { - cl_visedicts[cl_numvisedicts] = ent; - cl_numvisedicts++; - } - - gldepthmin = 0.5; - gldepthmax = 1; - glDepthRange (gldepthmin, gldepthmax); - glDepthFunc (GL_LEQUAL); - - R_RenderScene (); - R_DrawWaterSurfaces (); - - - gldepthmin = 0; - gldepthmax = 0.5; - glDepthRange (gldepthmin, gldepthmax); - glDepthFunc (GL_LEQUAL); - - // blend on top - glEnable (GL_BLEND); - glMatrixMode(GL_PROJECTION); - if (mirror_plane->normal[2]) - glScalef (1,-1,1); - else - glScalef (-1,1,1); - glCullFace(GL_FRONT); - glMatrixMode(GL_MODELVIEW); - - glLoadMatrixf (r_base_world_matrix); - - glColor4f (1,1,1,r_mirroralpha.value); - s = cl.worldmodel->textures[mirrortexturenum]->texturechain; - for ( ; s ; s=s->texturechain) - R_RenderBrushPoly (s); - cl.worldmodel->textures[mirrortexturenum]->texturechain = NULL; - glDisable (GL_BLEND); - glColor4f (1,1,1,1); -} -#endif - -/* -================ -R_RenderView - -r_refdef must be set before the first call -================ -*/ -void R_RenderView (void) -{ - double time1 = 0, time2; - - if (r_norefresh.value) - return; - - if (!r_worldentity.model || !cl.worldmodel) - Sys_Error ("R_RenderView: NULL worldmodel"); - - if (r_speeds.value) - { - glFinish (); - time1 = Sys_DoubleTime (); - c_brush_polys = 0; - c_alias_polys = 0; - } - - mirror = false; - - if (gl_finish.value) - glFinish (); - - R_Clear (); - - // render normal view - R_RenderScene (); - R_DrawViewModel (); - R_DrawWaterSurfaces (); - - // render mirror view -// R_Mirror (); - - if (r_speeds.value) - { -// glFinish (); - time2 = Sys_DoubleTime (); - Con_Printf ("%3i ms %4i wpoly %4i epoly\n", (int)((time2-time1)*1000), c_brush_polys, c_alias_polys); - } -} +/* +Copyright (C) 1996-1997 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 "quakedef.h" +#include "sound.h" + +entity_t r_worldentity; + +qboolean r_cache_thrash; // compatability + +vec3_t modelorg, r_entorigin; +entity_t *currententity; + +int r_visframecount; // bumped when going to a new PVS +int r_framecount; // used for dlight push checking + +mplane_t frustum[4]; + +int c_brush_polys, c_alias_polys; + +qboolean envmap; // true during envmap command capture + +int currenttexture = -1; // to avoid unnecessary texture sets + +int cnttextures[2] = {-1, -1}; // cached + +int particletexture; // little dot for particles +int playertextures; // up to 16 color translated skins + +int mirrortexturenum; // quake texturenum, not gltexturenum +qboolean mirror; +mplane_t *mirror_plane; + +// +// view origin +// +vec3_t vup; +vec3_t vpn; +vec3_t vright; +vec3_t r_origin; + +float r_world_matrix[16]; +float r_base_world_matrix[16]; + +// +// screen size info +// +refdef_t r_refdef; + +mleaf_t *r_viewleaf, *r_oldviewleaf; +mleaf_t *r_viewleaf2, *r_oldviewleaf2; // for watervis hack + +texture_t *r_notexture_mip; + +int d_lightstylevalue[256]; // 8.8 fraction of base light value + + +void R_MarkLeaves (void); + +cvar_t r_norefresh = {"r_norefresh","0"}; +cvar_t r_drawentities = {"r_drawentities","1"}; +cvar_t r_drawviewmodel = {"r_drawviewmodel","1"}; +cvar_t r_drawflame = {"r_drawflame","1"}; +cvar_t r_speeds = {"r_speeds","0"}; +cvar_t r_fullbright = {"r_fullbright","0"}; +cvar_t r_lightmap = {"r_lightmap","0"}; +cvar_t r_shadows = {"r_shadows","0"}; +cvar_t r_mirroralpha = {"r_mirroralpha","1"}; +cvar_t r_wateralpha = {"r_wateralpha","1"}; +cvar_t r_dynamic = {"r_dynamic","1"}; +cvar_t r_novis = {"r_novis","0"}; +cvar_t r_netgraph = {"r_netgraph","0"}; +cvar_t r_watervishack = {"r_watervishack", "1"}; +cvar_t r_fullbrightSkins = {"r_fullbrightSkins", "0"}; + +cvar_t gl_clear = {"gl_clear","0"}; +cvar_t gl_cull = {"gl_cull","1"}; +cvar_t gl_texsort = {"gl_texsort","1"}; +cvar_t gl_smoothmodels = {"gl_smoothmodels","1"}; +cvar_t gl_affinemodels = {"gl_affinemodels","0"}; +cvar_t gl_polyblend = {"gl_polyblend","1"}; +cvar_t gl_flashblend = {"gl_flashblend","1"}; +cvar_t gl_playermip = {"gl_playermip","0"}; +cvar_t gl_nocolors = {"gl_nocolors","0"}; +cvar_t gl_keeptjunctions = {"gl_keeptjunctions","1"}; +cvar_t gl_reporttjunctions = {"gl_reporttjunctions","0"}; +cvar_t gl_finish = {"gl_finish","0"}; +cvar_t gl_fb_depthhack = {"gl_fb_depthhack","1"}; +cvar_t gl_fb_bmodels = {"gl_fb_bmodels","1"}; +cvar_t gl_fb_models = {"gl_fb_models","1"}; + +extern cvar_t gl_ztrick; +extern cvar_t scr_fov; + +#ifndef _WIN32 +qboolean vid_hwgamma_enabled = false; // dummy +#endif + +/* +================= +R_CullBox + +Returns true if the box is completely outside the frustom +================= +*/ +qboolean R_CullBox (vec3_t mins, vec3_t maxs) +{ + int i; + + for (i=0 ; i<4 ; i++) + if (BoxOnPlaneSide (mins, maxs, &frustum[i]) == 2) + return true; + return false; +} + + +void R_RotateForEntity (entity_t *e) +{ + glTranslatef (e->origin[0], e->origin[1], e->origin[2]); + + glRotatef (e->angles[1], 0, 0, 1); + glRotatef (-e->angles[0], 0, 1, 0); + //ZOID: fixed z angle + glRotatef (e->angles[2], 1, 0, 0); +} + +/* +============================================================= + + SPRITE MODELS + +============================================================= +*/ + +/* +================ +R_GetSpriteFrame +================ +*/ +mspriteframe_t *R_GetSpriteFrame (entity_t *currententity) +{ + msprite_t *psprite; + mspritegroup_t *pspritegroup; + mspriteframe_t *pspriteframe; + int i, numframes, frame; + float *pintervals, fullinterval, targettime, time; + + psprite = currententity->model->cache.data; + frame = currententity->frame; + + if ((frame >= psprite->numframes) || (frame < 0)) + { + Con_Printf ("R_DrawSprite: no such frame %d\n", frame); + frame = 0; + } + + if (psprite->frames[frame].type == SPR_SINGLE) + { + pspriteframe = psprite->frames[frame].frameptr; + } + else + { + pspritegroup = (mspritegroup_t *)psprite->frames[frame].frameptr; + pintervals = pspritegroup->intervals; + numframes = pspritegroup->numframes; + fullinterval = pintervals[numframes-1]; + + time = cl.time + currententity->syncbase; + + // when loading in Mod_LoadSpriteGroup, we guaranteed all interval values + // are positive, so we don't have to worry about division by 0 + targettime = time - ((int)(time / fullinterval)) * fullinterval; + + for (i=0 ; i<(numframes-1) ; i++) + { + if (pintervals[i] > targettime) + break; + } + + pspriteframe = pspritegroup->frames[i]; + } + + return pspriteframe; +} + + +/* +================= +R_DrawSpriteModel + +================= +*/ +void R_DrawSpriteModel (entity_t *e) +{ + vec3_t point; + mspriteframe_t *frame; + float *up, *right; + vec3_t v_forward, v_right, v_up; + msprite_t *psprite; + + // don't even bother culling, because it's just a single + // polygon without a surface cache + frame = R_GetSpriteFrame (e); + psprite = currententity->model->cache.data; + + if (psprite->type == SPR_ORIENTED) + { // bullet marks on walls + AngleVectors (currententity->angles, v_forward, v_right, v_up); + up = v_up; + right = v_right; + } + else + { // normal sprite + up = vup; + right = vright; + } + + glColor3f (1,1,1); + + GL_DisableMultitexture(); + + GL_Bind(frame->gl_texturenum); + + glEnable (GL_ALPHA_TEST); + glBegin (GL_QUADS); + + glEnable (GL_ALPHA_TEST); + glBegin (GL_QUADS); + + glTexCoord2f (0, 1); + VectorMA (e->origin, frame->down, up, point); + VectorMA (point, frame->left, right, point); + glVertex3fv (point); + + glTexCoord2f (0, 0); + VectorMA (e->origin, frame->up, up, point); + VectorMA (point, frame->left, right, point); + glVertex3fv (point); + + glTexCoord2f (1, 0); + VectorMA (e->origin, frame->up, up, point); + VectorMA (point, frame->right, right, point); + glVertex3fv (point); + + glTexCoord2f (1, 1); + VectorMA (e->origin, frame->down, up, point); + VectorMA (point, frame->right, right, point); + glVertex3fv (point); + + glEnd (); + + glDisable (GL_ALPHA_TEST); +} + +/* +============================================================= + + ALIAS MODELS + +============================================================= +*/ + + +#define NUMVERTEXNORMALS 162 + +float r_avertexnormals[NUMVERTEXNORMALS][3] = { +#include "anorms.h" +}; + +vec3_t shadevector; +float shadelight, ambientlight; + +// precalculated dot products for quantized angles +#define SHADEDOT_QUANT 16 +float r_avertexnormal_dots[SHADEDOT_QUANT][256] = +#include "anorm_dots.h" +; + +float *shadedots = r_avertexnormal_dots[0]; + +int lastposenum; + +/* +============= +GL_DrawAliasFrame +============= +*/ +void GL_DrawAliasFrame (aliashdr_t *paliashdr, int posenum) +{ + float l; + trivertx_t *verts; + int *order; + int count; + +lastposenum = posenum; + + verts = (trivertx_t *)((byte *)paliashdr + paliashdr->posedata); + verts += posenum * paliashdr->poseverts; + order = (int *)((byte *)paliashdr + paliashdr->commands); + + while (1) + { + // get the vertex count and primitive type + count = *order++; + if (!count) + break; // done + if (count < 0) + { + count = -count; + glBegin (GL_TRIANGLE_FAN); + } + else + glBegin (GL_TRIANGLE_STRIP); + + do + { + // texture coordinates come from the draw list + glTexCoord2f (((float *)order)[0], ((float *)order)[1]); + order += 2; + + // normals and vertexes come from the frame list + l = (shadedots[verts->lightnormalindex] * shadelight + ambientlight) / 256; + if (l > 1) + l = 1; + glColor3f (l, l, l); + glVertex3f (verts->v[0], verts->v[1], verts->v[2]); + verts++; + } while (--count); + + glEnd (); + } +} + + +/* +============= +GL_DrawAliasShadow +============= +*/ +extern vec3_t lightspot; + +void GL_DrawAliasShadow (aliashdr_t *paliashdr, int posenum) +{ + trivertx_t *verts; + int *order; + vec3_t point; + float height, lheight; + int count; + + lheight = currententity->origin[2] - lightspot[2]; + + height = 0; + verts = (trivertx_t *)((byte *)paliashdr + paliashdr->posedata); + verts += posenum * paliashdr->poseverts; + order = (int *)((byte *)paliashdr + paliashdr->commands); + + height = -lheight + 1.0; + + while (1) + { + // get the vertex count and primitive type + count = *order++; + if (!count) + break; // done + if (count < 0) + { + count = -count; + glBegin (GL_TRIANGLE_FAN); + } + else + glBegin (GL_TRIANGLE_STRIP); + + do + { + // texture coordinates come from the draw list + // (skipped for shadows) glTexCoord2fv ((float *)order); + order += 2; + + // normals and vertexes come from the frame list + point[0] = verts->v[0] * paliashdr->scale[0] + paliashdr->scale_origin[0]; + point[1] = verts->v[1] * paliashdr->scale[1] + paliashdr->scale_origin[1]; + point[2] = verts->v[2] * paliashdr->scale[2] + paliashdr->scale_origin[2]; + + point[0] -= shadevector[0]*(point[2]+lheight); + point[1] -= shadevector[1]*(point[2]+lheight); + point[2] = height; +// height -= 0.001; + glVertex3fv (point); + + verts++; + } while (--count); + + glEnd (); + } +} + + + +/* +================= +R_SetupAliasFrame + +================= +*/ +void R_SetupAliasFrame (int frame, aliashdr_t *paliashdr) +{ + int pose, numposes; + float interval; + + if ((frame >= paliashdr->numframes) || (frame < 0)) + { + Con_DPrintf ("R_AliasSetupFrame: no such frame %d\n", frame); + frame = 0; + } + + pose = paliashdr->frames[frame].firstpose; + numposes = paliashdr->frames[frame].numposes; + + if (numposes > 1) + { + interval = paliashdr->frames[frame].interval; + pose += (int)(cl.time / interval) % numposes; + } + + GL_DrawAliasFrame (paliashdr, pose); +} + + + +/* +================= +R_DrawAliasModel + +================= +*/ +void R_DrawAliasModel (entity_t *ent) +{ + int i; + int lnum; + vec3_t dist; + float add; + model_t *clmodel; + vec3_t mins, maxs; + aliashdr_t *paliashdr; + float an; + int anim, skinnum; + int texture; + qboolean full_light; + + clmodel = ent->model; + + VectorAdd (ent->origin, clmodel->mins, mins); + VectorAdd (ent->origin, clmodel->maxs, maxs); + + if (R_CullBox (mins, maxs)) + return; + + VectorCopy (ent->origin, r_entorigin); + VectorSubtract (r_origin, r_entorigin, modelorg); + + // + // get lighting information + // + +// make thunderbolt and torches full light + if (clmodel->modhint == MOD_THUNDERBOLT) { + ambientlight = 210; + shadelight = 0; + full_light = true; + } else if (clmodel->modhint == MOD_FLAME) { + ambientlight = 255; + shadelight = 0; + full_light = true; + } + else if (clmodel->modhint == MOD_PLAYER && r_fullbrightSkins.value + && !cl.teamfortress) { + ambientlight = shadelight = 128; + full_light = true; + } + else + { + // normal lighting + + full_light = false; + ambientlight = shadelight = R_LightPoint (ent->origin); + + for (lnum=0 ; lnum= cl.time) + { + VectorSubtract (ent->origin, + cl_dlights[lnum].origin, + dist); + add = cl_dlights[lnum].radius - Length(dist); + + if (add > 0) + ambientlight += add; + } + } + + // clamp lighting so it doesn't overbright as much + if (ambientlight > 128) + ambientlight = 128; + if (ambientlight + shadelight > 192) + shadelight = 192 - ambientlight; + + // always give the gun some light + if (ent == &cl.viewent && ambientlight < 24) + ambientlight = shadelight = 24; + + // never allow players to go totally black + if (clmodel->modhint == MOD_PLAYER) { + if (ambientlight < 8) + ambientlight = shadelight = 8; + } + } + + shadedots = r_avertexnormal_dots[((int)(ent->angles[1] * (SHADEDOT_QUANT / 360.0))) & (SHADEDOT_QUANT - 1)]; + + an = ent->angles[1]/180*M_PI; + shadevector[0] = cos(-an); + shadevector[1] = sin(-an); + shadevector[2] = 1; + VectorNormalize (shadevector); + + // + // locate the proper data + // + paliashdr = (aliashdr_t *)Mod_Extradata (ent->model); + + c_alias_polys += paliashdr->numtris; + + // + // draw all the triangles + // + + GL_DisableMultitexture(); + + glPushMatrix (); + R_RotateForEntity (ent); + + if (clmodel->modhint == MOD_EYES) { + glTranslatef (paliashdr->scale_origin[0], paliashdr->scale_origin[1], paliashdr->scale_origin[2] - (22 + 8)); + // double size of eyes, since they are really hard to see in gl + glScalef (paliashdr->scale[0]*2, paliashdr->scale[1]*2, paliashdr->scale[2]*2); + } else { + glTranslatef (paliashdr->scale_origin[0], paliashdr->scale_origin[1], paliashdr->scale_origin[2]); + glScalef (paliashdr->scale[0], paliashdr->scale[1], paliashdr->scale[2]); + } + + anim = (int)(cl.time*10) & 3; + skinnum = ent->skinnum; + if ((skinnum >= paliashdr->numskins) || (skinnum < 0)) { + Con_DPrintf ("R_DrawAliasModel: no such skin # %d\n", skinnum); + skinnum = 0; + } + + texture = paliashdr->gl_texturenum[skinnum][anim]; + + // we can't dynamically colormap textures, so they are cached + // separately for the players. Heads are just uncolored. + if (ent->scoreboard && !gl_nocolors.value) + { + i = ent->scoreboard - cl.players; + if (!ent->scoreboard->skin) { + Skin_Find(ent->scoreboard); + R_TranslatePlayerSkin(i); + } + if (i >= 0 && iframe, paliashdr); + + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + + if (!full_light && gl_fb_models.value) { + int fb_texture = paliashdr->fb_texturenum[skinnum][anim]; + if (fb_texture) { + glEnable (GL_BLEND); + GL_Bind (fb_texture); + R_SetupAliasFrame (ent->frame, paliashdr); + glDisable (GL_BLEND); + } + } + + glShadeModel (GL_FLAT); + if (gl_affinemodels.value) + glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); + + glPopMatrix (); + + if (r_shadows.value && !full_light && ent != &cl.viewent) + { + glPushMatrix (); + + glTranslatef (ent->origin[0], ent->origin[1], ent->origin[2]); + glRotatef (ent->angles[1], 0, 0, 1); + //FIXME glRotatef (-ent->angles[0], 0, 1, 0); + //FIXME glRotatef (ent->angles[2], 1, 0, 0); + + glDisable (GL_TEXTURE_2D); + glEnable (GL_BLEND); + glColor4f (0,0,0,0.5); + GL_DrawAliasShadow (paliashdr, lastposenum); + glEnable (GL_TEXTURE_2D); + glDisable (GL_BLEND); + glColor4f (1,1,1,1); + glPopMatrix (); + } + +} + +//================================================================================== + +/* +============= +R_DrawEntitiesOnList +============= +*/ +void R_DrawEntitiesOnList (void) +{ + int i; + + if (!r_drawentities.value) + return; + + // draw sprites seperately, because of alpha blending + for (i=0 ; imodel->type) + { + case mod_alias: + R_DrawAliasModel (currententity); + break; + + case mod_brush: + R_DrawBrushModel (currententity); + break; + + default: + break; + } + } + + for (i=0 ; imodel->type) + { + case mod_sprite: + R_DrawSpriteModel (currententity); + break; + + default : + break; + } + } +} + +/* +============= +R_DrawViewModel +============= +*/ +void R_DrawViewModel (void) +{ + if (!r_drawviewmodel.value || + (r_drawviewmodel.value == 2 && scr_fov.value > 90) || + !Cam_DrawViewModel()) + return; + + if (envmap) + return; + + if (!r_drawentities.value) + return; + + if (cl.stats[STAT_ITEMS] & IT_INVISIBILITY) + return; + + if (cl.stats[STAT_HEALTH] <= 0) + return; + + currententity = &cl.viewent; + if (!currententity->model) + return; + + // hack the depth range to prevent view model from poking into walls + glDepthRange (gldepthmin, gldepthmin + 0.3*(gldepthmax-gldepthmin)); + R_DrawAliasModel (currententity); + glDepthRange (gldepthmin, gldepthmax); +} + + +/* +============ +R_PolyBlend +============ +*/ +void R_PolyBlend (void) +{ + if (vid_hwgamma_enabled) + return; + if (!v_blend[3]) + return; + + glDisable (GL_ALPHA_TEST); + glEnable (GL_BLEND); + glDisable (GL_TEXTURE_2D); + + glColor4fv (v_blend); + + glBegin (GL_QUADS); + glVertex2f (r_refdef.vrect.x, r_refdef.vrect.y); + glVertex2f (r_refdef.vrect.x + r_refdef.vrect.width, r_refdef.vrect.y); + glVertex2f (r_refdef.vrect.x + r_refdef.vrect.width, r_refdef.vrect.y + r_refdef.vrect.height); + glVertex2f (r_refdef.vrect.x, r_refdef.vrect.y + r_refdef.vrect.height); + glEnd (); + + glDisable (GL_BLEND); + glEnable (GL_TEXTURE_2D); + glEnable (GL_ALPHA_TEST); +} + +/* +================ +R_BrightenScreen +================ +*/ +void R_BrightenScreen (void) +{ +#ifdef WIN32 + extern float vid_gamma; +#endif + float f; + + if (vid_hwgamma_enabled) + return; + if (gl_contrast.value <= 1.0) + return; + + f = gl_contrast.value; + if (f > 3) + f = 3; + +#ifdef WIN32 + f = pow (f, vid_gamma); +#endif + + glDisable (GL_TEXTURE_2D); + glEnable (GL_BLEND); + glBlendFunc (GL_DST_COLOR, GL_ONE); + glBegin (GL_QUADS); + while (f > 1) + { + if (f >= 2) + glColor3f (1, 1, 1); + else + glColor3f (f - 1, f - 1, f - 1); + glVertex2f (0, 0); + glVertex2f (vid.width, 0); + glVertex2f (vid.width, vid.height); + glVertex2f (0, vid.height); + f *= 0.5; + } + glEnd (); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable (GL_TEXTURE_2D); + glDisable (GL_BLEND); +} + +int SignbitsForPlane (mplane_t *out) +{ + int bits, j; + + // for fast box on planeside test + + bits = 0; + for (j=0 ; j<3 ; j++) + { + if (out->normal[j] < 0) + bits |= 1<contents <= CONTENTS_WATER && + r_viewleaf->contents >= CONTENTS_LAVA) { + // Test the point 10 units above. 10 seems to be enough + // for fov values up to 140 + testorigin[2] += 10; + testleaf = Mod_PointInLeaf (testorigin, cl.worldmodel); + if (testleaf->contents == CONTENTS_EMPTY) + r_viewleaf2 = testleaf; + } + else if (r_viewleaf->contents == CONTENTS_EMPTY) { + testorigin[2] -= 10; + testleaf = Mod_PointInLeaf (testorigin, cl.worldmodel); + if (testleaf->contents <= CONTENTS_WATER && + testleaf->contents >= CONTENTS_LAVA) + r_viewleaf2 = testleaf; + } + } else + r_viewleaf2 = r_oldviewleaf2 = NULL; + + V_SetContentsColor (r_viewleaf->contents); + V_CalcBlend (); + + r_cache_thrash = false; + + c_brush_polys = 0; + c_alias_polys = 0; + +} + + +void MYgluPerspective( GLdouble fovy, GLdouble aspect, + GLdouble zNear, GLdouble zFar ) +{ + GLdouble xmin, xmax, ymin, ymax; + + ymax = zNear * tan( fovy * M_PI / 360.0 ); + ymin = -ymax; + + xmin = ymin * aspect; + xmax = ymax * aspect; + + glFrustum( xmin, xmax, ymin, ymax, zNear, zFar ); +} + + +/* +============= +R_SetupGL +============= +*/ +void R_SetupGL (void) +{ + float screenaspect; + extern int glwidth, glheight; + int x, x2, y2, y, w, h; + + // + // set up viewpoint + // + glMatrixMode(GL_PROJECTION); + glLoadIdentity (); + x = r_refdef.vrect.x * glwidth/vid.width; + x2 = (r_refdef.vrect.x + r_refdef.vrect.width) * glwidth/vid.width; + y = (vid.height-r_refdef.vrect.y) * glheight/vid.height; + y2 = (vid.height - (r_refdef.vrect.y + r_refdef.vrect.height)) * glheight/vid.height; + + // fudge around because of frac screen scale + if (x > 0) + x--; + if (x2 < glwidth) + x2++; + if (y2 < 0) + y2--; + if (y < glheight) + y++; + + w = x2 - x; + h = y - y2; + + if (envmap) + { + x = y2 = 0; + w = h = 256; + } + + glViewport (glx + x, gly + y2, w, h); + screenaspect = (float)r_refdef.vrect.width/r_refdef.vrect.height; +// yfov = 2*atan((float)r_refdef.vrect.height/r_refdef.vrect.width)*180/M_PI; +// yfov = (2.0 * tan (scr_fov.value/360*M_PI)) / screenaspect; +// yfov = 2*atan((float)r_refdef.vrect.height/r_refdef.vrect.width)*(scr_fov.value*2)/M_PI; +// MYgluPerspective (yfov, screenaspect, 4, 4096); + MYgluPerspective (r_refdef.fov_y, screenaspect, 4, 4096); + + if (mirror) + { + if (mirror_plane->normal[2]) + glScalef (1, -1, 1); + else + glScalef (-1, 1, 1); + glCullFace(GL_BACK); + } + else + glCullFace(GL_FRONT); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity (); + + glRotatef (-90, 1, 0, 0); // put Z going up + glRotatef (90, 0, 0, 1); // put Z going up + glRotatef (-r_refdef.viewangles[2], 1, 0, 0); + glRotatef (-r_refdef.viewangles[0], 0, 1, 0); + glRotatef (-r_refdef.viewangles[1], 0, 0, 1); + glTranslatef (-r_refdef.vieworg[0], -r_refdef.vieworg[1], -r_refdef.vieworg[2]); + + glGetFloatv (GL_MODELVIEW_MATRIX, r_world_matrix); + + // + // set drawing parms + // + if (gl_cull.value) + glEnable(GL_CULL_FACE); + else + glDisable(GL_CULL_FACE); + + glDisable(GL_BLEND); + glDisable(GL_ALPHA_TEST); + glEnable(GL_DEPTH_TEST); +} + +/* +================ +R_RenderScene + +r_refdef must be set before the first call +================ +*/ +void R_RenderScene (void) +{ + R_SetupFrame (); + + R_SetFrustum (); + + R_SetupGL (); + + R_MarkLeaves (); // done here so we know if we're in water + + R_DrawWorld (); // adds static entities to the list + + S_ExtraUpdate (); // don't let sound get messed up if going slow + + R_DrawEntitiesOnList (); + + GL_DisableMultitexture(); + + R_RenderDlights (); + + R_DrawParticles (); + +#ifdef GLTEST + Test_Draw (); +#endif + +} + + +/* +============= +R_Clear +============= +*/ +int gl_ztrickframe = 0; +void R_Clear (void) +{ + static qboolean cleartogray; + qboolean clear = false; + + if (gl_clear.value) { + clear = true; + if (cleartogray) { + glClearColor (1, 0, 0, 0); + cleartogray = false; + } + } + else if (!vid_hwgamma_enabled && gl_contrast.value > 1) { + clear = true; + if (!cleartogray) { + glClearColor (0.1, 0.1, 0.1, 0); + cleartogray = true; + } + } + + if (r_mirroralpha.value != 1.0) + { + if (clear) + glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + else + glClear (GL_DEPTH_BUFFER_BIT); + gldepthmin = 0; + gldepthmax = 0.5; + glDepthFunc (GL_LEQUAL); + } + else if (gl_ztrick.value) + { + if (clear) + glClear (GL_COLOR_BUFFER_BIT); + + gl_ztrickframe = !gl_ztrickframe; + if (gl_ztrickframe) + { + gldepthmin = 0; + gldepthmax = 0.49999; + glDepthFunc (GL_LEQUAL); + } + else + { + gldepthmin = 1; + gldepthmax = 0.5; + glDepthFunc (GL_GEQUAL); + } + } + else + { + if (clear) + glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + else + glClear (GL_DEPTH_BUFFER_BIT); + gldepthmin = 0; + gldepthmax = 1; + glDepthFunc (GL_LEQUAL); + } + + glDepthRange (gldepthmin, gldepthmax); +} + +#if 0 //!!! FIXME, Zoid, mirror is disabled for now +/* +============= +R_Mirror +============= +*/ +void R_Mirror (void) +{ + float d; + msurface_t *s; + entity_t *ent; + + if (!mirror) + return; + + memcpy (r_base_world_matrix, r_world_matrix, sizeof(r_base_world_matrix)); + + d = DotProduct (r_refdef.vieworg, mirror_plane->normal) - mirror_plane->dist; + VectorMA (r_refdef.vieworg, -2*d, mirror_plane->normal, r_refdef.vieworg); + + d = DotProduct (vpn, mirror_plane->normal); + VectorMA (vpn, -2*d, mirror_plane->normal, vpn); + + r_refdef.viewangles[0] = -asin (vpn[2])/M_PI*180; + r_refdef.viewangles[1] = atan2 (vpn[1], vpn[0])/M_PI*180; + r_refdef.viewangles[2] = -r_refdef.viewangles[2]; + + ent = &cl_entities[cl.viewentity]; + if (cl_numvisedicts < MAX_VISEDICTS) + { + cl_visedicts[cl_numvisedicts] = ent; + cl_numvisedicts++; + } + + gldepthmin = 0.5; + gldepthmax = 1; + glDepthRange (gldepthmin, gldepthmax); + glDepthFunc (GL_LEQUAL); + + R_RenderScene (); + R_DrawWaterSurfaces (); + + + gldepthmin = 0; + gldepthmax = 0.5; + glDepthRange (gldepthmin, gldepthmax); + glDepthFunc (GL_LEQUAL); + + // blend on top + glEnable (GL_BLEND); + glMatrixMode(GL_PROJECTION); + if (mirror_plane->normal[2]) + glScalef (1,-1,1); + else + glScalef (-1,1,1); + glCullFace(GL_FRONT); + glMatrixMode(GL_MODELVIEW); + + glLoadMatrixf (r_base_world_matrix); + + glColor4f (1,1,1,r_mirroralpha.value); + s = cl.worldmodel->textures[mirrortexturenum]->texturechain; + for ( ; s ; s=s->texturechain) + R_RenderBrushPoly (s); + cl.worldmodel->textures[mirrortexturenum]->texturechain = NULL; + glDisable (GL_BLEND); + glColor4f (1,1,1,1); +} +#endif + +/* +================ +R_RenderView + +r_refdef must be set before the first call +================ +*/ +void R_RenderView (void) +{ + double time1 = 0, time2; + + if (r_norefresh.value) + return; + + if (!r_worldentity.model || !cl.worldmodel) + Sys_Error ("R_RenderView: NULL worldmodel"); + + if (r_speeds.value) + { + glFinish (); + time1 = Sys_DoubleTime (); + c_brush_polys = 0; + c_alias_polys = 0; + } + + mirror = false; + + if (gl_finish.value) + glFinish (); + + R_Clear (); + + // render normal view + R_RenderScene (); + R_DrawViewModel (); + R_DrawWaterSurfaces (); + + // render mirror view +// R_Mirror (); + + if (r_speeds.value) + { +// glFinish (); + time2 = Sys_DoubleTime (); + Con_Printf ("%3i ms %4i wpoly %4i epoly\n", (int)((time2-time1)*1000), c_brush_polys, c_alias_polys); + } +} diff --git a/source/gl_rmisc.c b/source/gl_rmisc.c index 25c80c91..0a2f1c95 100644 --- a/source/gl_rmisc.c +++ b/source/gl_rmisc.c @@ -1,488 +1,488 @@ -/* -Copyright (C) 1996-1997 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 - -#include "quakedef.h" - -extern void R_InitBubble(); - -/* -================== -R_InitTextures -================== -*/ -void R_InitTextures (void) -{ - int x,y, m; - byte *dest; - -// create a simple checkerboard texture for the default - r_notexture_mip = Hunk_AllocName (sizeof(texture_t) + 16*16+8*8+4*4+2*2, "notexture"); - - r_notexture_mip->width = r_notexture_mip->height = 16; - r_notexture_mip->offsets[0] = sizeof(texture_t); - r_notexture_mip->offsets[1] = r_notexture_mip->offsets[0] + 16*16; - r_notexture_mip->offsets[2] = r_notexture_mip->offsets[1] + 8*8; - r_notexture_mip->offsets[3] = r_notexture_mip->offsets[2] + 4*4; - - for (m=0 ; m<4 ; m++) - { - dest = (byte *)r_notexture_mip + r_notexture_mip->offsets[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; - } - } -} - -byte dottexture[8][8] = -{ - {0,1,1,0,0,0,0,0}, - {1,1,1,1,0,0,0,0}, - {1,1,1,1,0,0,0,0}, - {0,1,1,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}, -}; -void R_InitParticleTexture (void) -{ - int x,y; - byte data[8][8][4]; - - // - // particle texture - // - particletexture = texture_extension_number++; - GL_Bind(particletexture); - - for (x=0 ; x<8 ; x++) - { - for (y=0 ; y<8 ; y++) - { - data[y][x][0] = 255; - data[y][x][1] = 255; - data[y][x][2] = 255; - data[y][x][3] = dottexture[x][y]*255; - } - } - glTexImage2D (GL_TEXTURE_2D, 0, gl_alpha_format, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); - - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); -} - -/* -=============== -R_Envmap_f - -Grab six views for environment mapping tests -=============== -*/ -void R_Envmap_f (void) -{ - byte buffer[256*256*4]; - - glDrawBuffer (GL_FRONT); - glReadBuffer (GL_FRONT); - envmap = true; - - r_refdef.vrect.x = 0; - r_refdef.vrect.y = 0; - r_refdef.vrect.width = 256; - r_refdef.vrect.height = 256; - - r_refdef.viewangles[0] = 0; - r_refdef.viewangles[1] = 0; - r_refdef.viewangles[2] = 0; - GL_BeginRendering (&glx, &gly, &glwidth, &glheight); - R_RenderView (); - glReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer); - COM_WriteFile ("env0.rgb", buffer, sizeof(buffer)); - - r_refdef.viewangles[1] = 90; - GL_BeginRendering (&glx, &gly, &glwidth, &glheight); - R_RenderView (); - glReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer); - COM_WriteFile ("env1.rgb", buffer, sizeof(buffer)); - - r_refdef.viewangles[1] = 180; - GL_BeginRendering (&glx, &gly, &glwidth, &glheight); - R_RenderView (); - glReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer); - COM_WriteFile ("env2.rgb", buffer, sizeof(buffer)); - - r_refdef.viewangles[1] = 270; - GL_BeginRendering (&glx, &gly, &glwidth, &glheight); - R_RenderView (); - glReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer); - COM_WriteFile ("env3.rgb", buffer, sizeof(buffer)); - - r_refdef.viewangles[0] = -90; - r_refdef.viewangles[1] = 0; - GL_BeginRendering (&glx, &gly, &glwidth, &glheight); - R_RenderView (); - glReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer); - COM_WriteFile ("env4.rgb", buffer, sizeof(buffer)); - - r_refdef.viewangles[0] = 90; - r_refdef.viewangles[1] = 0; - GL_BeginRendering (&glx, &gly, &glwidth, &glheight); - R_RenderView (); - glReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer); - COM_WriteFile ("env5.rgb", buffer, sizeof(buffer)); - - envmap = false; - glDrawBuffer (GL_BACK); - glReadBuffer (GL_BACK); - GL_EndRendering (); -} - -/* -=============== -R_Init -=============== -*/ -void R_Init (void) -{ - Cmd_AddCommand ("timerefresh", R_TimeRefresh_f); - Cmd_AddCommand ("envmap", R_Envmap_f); - - Cvar_RegisterVariable (&r_watervishack); - if (gl_vendor && strstr(gl_vendor, "3Dfx")) - Cvar_SetValue (&r_watervishack, 0); - - Cvar_RegisterVariable (&r_norefresh); - Cvar_RegisterVariable (&r_lightmap); - Cvar_RegisterVariable (&r_fullbright); - Cvar_RegisterVariable (&r_drawentities); - Cvar_RegisterVariable (&r_drawviewmodel); - Cvar_RegisterVariable (&r_drawflame); - Cvar_RegisterVariable (&r_shadows); - Cvar_RegisterVariable (&r_mirroralpha); - Cvar_RegisterVariable (&r_wateralpha); - Cvar_RegisterVariable (&r_dynamic); - Cvar_RegisterVariable (&r_novis); - Cvar_RegisterVariable (&r_speeds); - Cvar_RegisterVariable (&r_netgraph); - Cvar_RegisterVariable (&r_fullbrightSkins); - - Cvar_RegisterVariable (&gl_clear); - Cvar_RegisterVariable (&gl_texsort); - - if (gl_mtexable) - Cvar_SetValue (&gl_texsort, 0.0); - - Cvar_RegisterVariable (&gl_cull); - Cvar_RegisterVariable (&gl_smoothmodels); - Cvar_RegisterVariable (&gl_affinemodels); - Cvar_RegisterVariable (&gl_polyblend); - Cvar_RegisterVariable (&gl_flashblend); - Cvar_RegisterVariable (&gl_playermip); - Cvar_RegisterVariable (&gl_nocolors); - Cvar_RegisterVariable (&gl_finish); - Cvar_RegisterVariable (&gl_fb_depthhack); - Cvar_RegisterVariable (&gl_fb_bmodels); - Cvar_RegisterVariable (&gl_fb_models); - - Cvar_RegisterVariable (&gl_keeptjunctions); - Cvar_RegisterVariable (&gl_reporttjunctions); - - R_InitBubble(); - - R_InitParticles (); - R_InitParticleTexture (); - -#ifdef GLTEST - Test_Init (); -#endif - - netgraphtexture = texture_extension_number; - texture_extension_number++; - - playertextures = texture_extension_number; - texture_extension_number += MAX_CLIENTS; -} - -/* -=============== -R_TranslatePlayerSkin - -Translates a skin texture by the per-player color lookup -=============== -*/ -void R_TranslatePlayerSkin (int playernum) -{ - int top, bottom; - byte translate[256]; - unsigned translate32[256]; - int i, j; - byte *original; - unsigned pixels[512*256], *out; - int scaled_width, scaled_height; - int inwidth, inheight; - int tinwidth, tinheight; - byte *inrow; - unsigned frac, fracstep; - player_info_t *player; - extern byte player_8bit_texels[320*200]; - char s[512]; - - GL_DisableMultitexture(); - - player = &cl.players[playernum]; - if (!player->name[0]) - return; - - strcpy(s, Info_ValueForKey(player->userinfo, "skin")); - COM_StripExtension(s, s); - if (player->skin && !stricmp(s, player->skin->name)) - player->skin = NULL; - - if (player->_topcolor != player->topcolor || - player->_bottomcolor != player->bottomcolor || !player->skin) { - player->_topcolor = player->topcolor; - player->_bottomcolor = player->bottomcolor; - - top = player->topcolor; - bottom = player->bottomcolor; - top = (top < 0) ? 0 : ((top > 13) ? 13 : top); - bottom = (bottom < 0) ? 0 : ((bottom > 13) ? 13 : bottom); - top *= 16; - bottom *= 16; - - for (i=0 ; i<256 ; i++) - translate[i] = i; - - for (i=0 ; i<16 ; i++) - { - if (top < 128) // the artists made some backwards ranges. sigh. - translate[TOP_RANGE+i] = top+i; - else - translate[TOP_RANGE+i] = top+15-i; - - if (bottom < 128) - translate[BOTTOM_RANGE+i] = bottom+i; - else - translate[BOTTOM_RANGE+i] = bottom+15-i; - } - - // - // locate the original skin pixels - // - // real model width - tinwidth = 296; - tinheight = 194; - - if (!player->skin) - Skin_Find(player); - if ((original = Skin_Cache(player->skin)) != NULL) { - //skin data width - inwidth = 320; - inheight = 200; - } else { - original = player_8bit_texels; - inwidth = 296; - inheight = 194; - } - - - // because this happens during gameplay, do it fast - // instead of sending it through gl_upload 8 - GL_Bind(playertextures + playernum); - - #if 0 - s = 320*200; - byte translated[320*200]; - - for (i=0 ; iskinwidth, paliashdr->skinheight, - false, false, true, false); - #endif - - scaled_width = gl_max_size.value < 512 ? gl_max_size.value : 512; - scaled_height = gl_max_size.value < 256 ? gl_max_size.value : 256; - // allow users to crunch sizes down even more if they want - scaled_width >>= (int)gl_playermip.value; - scaled_height >>= (int)gl_playermip.value; - if (scaled_width < 1) - scaled_width = 1; - if (scaled_height < 1) - scaled_height = 1; - - if (VID_Is8bit()) { // 8bit texture upload - byte *out2; - - out2 = (byte *)pixels; - memset(pixels, 0, sizeof(pixels)); - fracstep = tinwidth*0x10000/scaled_width; - for (i=0 ; i> 1; - for (j=0 ; j>16]]; - frac += fracstep; - out2[j+1] = translate[inrow[frac>>16]]; - frac += fracstep; - out2[j+2] = translate[inrow[frac>>16]]; - frac += fracstep; - out2[j+3] = translate[inrow[frac>>16]]; - frac += fracstep; - } - } - - GL_Upload8_EXT ((byte *)pixels, scaled_width, scaled_height, false, false); - return; - } - - for (i=0 ; i<256 ; i++) - translate32[i] = d_8to24table[translate[i]]; - - out = pixels; - memset(pixels, 0, sizeof(pixels)); - fracstep = tinwidth*0x10000/scaled_width; - for (i=0 ; i> 1; - for (j=0 ; j>16]]; - frac += fracstep; - out[j+1] = translate32[inrow[frac>>16]]; - frac += fracstep; - out[j+2] = translate32[inrow[frac>>16]]; - frac += fracstep; - out[j+3] = translate32[inrow[frac>>16]]; - frac += fracstep; - } - } - - glTexImage2D (GL_TEXTURE_2D, 0, gl_solid_format, - scaled_width, scaled_height, 0, GL_RGBA, - GL_UNSIGNED_BYTE, pixels); - - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - } -} - -/* -=============== -R_NewMap -=============== -*/ -void R_NewMap (void) -{ - int i; - - for (i=0 ; i<256 ; i++) - d_lightstylevalue[i] = 264; // normal light value - - memset (&r_worldentity, 0, sizeof(r_worldentity)); - r_worldentity.model = cl.worldmodel; - -// clear out efrags in case the level hasn't been reloaded -// FIXME: is this one short? - for (i=0 ; inumleafs ; i++) - cl.worldmodel->leafs[i].efrags = NULL; - - r_viewleaf = NULL; - R_ClearParticles (); - - GL_BuildLightmaps (); - - // identify sky texture - skytexturenum = -1; - mirrortexturenum = -1; - for (i=0 ; inumtextures ; i++) - { - if (!cl.worldmodel->textures[i]) - continue; - if (!strncmp(cl.worldmodel->textures[i]->name,"sky",3) ) - skytexturenum = i; - if (!strncmp(cl.worldmodel->textures[i]->name,"window02_1",10) ) - mirrortexturenum = i; - cl.worldmodel->textures[i]->texturechain = NULL; - } -#ifdef QUAKE2 - R_LoadSkys (); -#endif -} - - -/* -==================== -R_TimeRefresh_f - -For program optimization -==================== -*/ -void R_TimeRefresh_f (void) -{ - int i; - float start, stop, time; - - if (cls.state != ca_active) - return; - - glDrawBuffer (GL_FRONT); - glFinish (); - - start = Sys_DoubleTime (); - for (i=0 ; i<128 ; i++) - { - r_refdef.viewangles[1] = i/128.0*360.0; - R_RenderView (); - } - - glFinish (); - stop = Sys_DoubleTime (); - time = stop-start; - Con_Printf ("%f seconds (%f fps)\n", time, 128/time); - - glDrawBuffer (GL_BACK); - GL_EndRendering (); -} - -void D_FlushCaches (void) -{ -} - - +/* +Copyright (C) 1996-1997 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 + +#include "quakedef.h" + +extern void R_InitBubble(); + +/* +================== +R_InitTextures +================== +*/ +void R_InitTextures (void) +{ + int x,y, m; + byte *dest; + +// create a simple checkerboard texture for the default + r_notexture_mip = Hunk_AllocName (sizeof(texture_t) + 16*16+8*8+4*4+2*2, "notexture"); + + r_notexture_mip->width = r_notexture_mip->height = 16; + r_notexture_mip->offsets[0] = sizeof(texture_t); + r_notexture_mip->offsets[1] = r_notexture_mip->offsets[0] + 16*16; + r_notexture_mip->offsets[2] = r_notexture_mip->offsets[1] + 8*8; + r_notexture_mip->offsets[3] = r_notexture_mip->offsets[2] + 4*4; + + for (m=0 ; m<4 ; m++) + { + dest = (byte *)r_notexture_mip + r_notexture_mip->offsets[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; + } + } +} + +byte dottexture[8][8] = +{ + {0,1,1,0,0,0,0,0}, + {1,1,1,1,0,0,0,0}, + {1,1,1,1,0,0,0,0}, + {0,1,1,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}, +}; +void R_InitParticleTexture (void) +{ + int x,y; + byte data[8][8][4]; + + // + // particle texture + // + particletexture = texture_extension_number++; + GL_Bind(particletexture); + + for (x=0 ; x<8 ; x++) + { + for (y=0 ; y<8 ; y++) + { + data[y][x][0] = 255; + data[y][x][1] = 255; + data[y][x][2] = 255; + data[y][x][3] = dottexture[x][y]*255; + } + } + glTexImage2D (GL_TEXTURE_2D, 0, gl_alpha_format, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); + + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); +} + +/* +=============== +R_Envmap_f + +Grab six views for environment mapping tests +=============== +*/ +void R_Envmap_f (void) +{ + byte buffer[256*256*4]; + + glDrawBuffer (GL_FRONT); + glReadBuffer (GL_FRONT); + envmap = true; + + r_refdef.vrect.x = 0; + r_refdef.vrect.y = 0; + r_refdef.vrect.width = 256; + r_refdef.vrect.height = 256; + + r_refdef.viewangles[0] = 0; + r_refdef.viewangles[1] = 0; + r_refdef.viewangles[2] = 0; + GL_BeginRendering (&glx, &gly, &glwidth, &glheight); + R_RenderView (); + glReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer); + COM_WriteFile ("env0.rgb", buffer, sizeof(buffer)); + + r_refdef.viewangles[1] = 90; + GL_BeginRendering (&glx, &gly, &glwidth, &glheight); + R_RenderView (); + glReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer); + COM_WriteFile ("env1.rgb", buffer, sizeof(buffer)); + + r_refdef.viewangles[1] = 180; + GL_BeginRendering (&glx, &gly, &glwidth, &glheight); + R_RenderView (); + glReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer); + COM_WriteFile ("env2.rgb", buffer, sizeof(buffer)); + + r_refdef.viewangles[1] = 270; + GL_BeginRendering (&glx, &gly, &glwidth, &glheight); + R_RenderView (); + glReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer); + COM_WriteFile ("env3.rgb", buffer, sizeof(buffer)); + + r_refdef.viewangles[0] = -90; + r_refdef.viewangles[1] = 0; + GL_BeginRendering (&glx, &gly, &glwidth, &glheight); + R_RenderView (); + glReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer); + COM_WriteFile ("env4.rgb", buffer, sizeof(buffer)); + + r_refdef.viewangles[0] = 90; + r_refdef.viewangles[1] = 0; + GL_BeginRendering (&glx, &gly, &glwidth, &glheight); + R_RenderView (); + glReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer); + COM_WriteFile ("env5.rgb", buffer, sizeof(buffer)); + + envmap = false; + glDrawBuffer (GL_BACK); + glReadBuffer (GL_BACK); + GL_EndRendering (); +} + +/* +=============== +R_Init +=============== +*/ +void R_Init (void) +{ + Cmd_AddCommand ("timerefresh", R_TimeRefresh_f); + Cmd_AddCommand ("envmap", R_Envmap_f); + + Cvar_RegisterVariable (&r_watervishack); + if (gl_vendor && strstr(gl_vendor, "3Dfx")) + Cvar_SetValue (&r_watervishack, 0); + + Cvar_RegisterVariable (&r_norefresh); + Cvar_RegisterVariable (&r_lightmap); + Cvar_RegisterVariable (&r_fullbright); + Cvar_RegisterVariable (&r_drawentities); + Cvar_RegisterVariable (&r_drawviewmodel); + Cvar_RegisterVariable (&r_drawflame); + Cvar_RegisterVariable (&r_shadows); + Cvar_RegisterVariable (&r_mirroralpha); + Cvar_RegisterVariable (&r_wateralpha); + Cvar_RegisterVariable (&r_dynamic); + Cvar_RegisterVariable (&r_novis); + Cvar_RegisterVariable (&r_speeds); + Cvar_RegisterVariable (&r_netgraph); + Cvar_RegisterVariable (&r_fullbrightSkins); + + Cvar_RegisterVariable (&gl_clear); + Cvar_RegisterVariable (&gl_texsort); + + if (gl_mtexable) + Cvar_SetValue (&gl_texsort, 0.0); + + Cvar_RegisterVariable (&gl_cull); + Cvar_RegisterVariable (&gl_smoothmodels); + Cvar_RegisterVariable (&gl_affinemodels); + Cvar_RegisterVariable (&gl_polyblend); + Cvar_RegisterVariable (&gl_flashblend); + Cvar_RegisterVariable (&gl_playermip); + Cvar_RegisterVariable (&gl_nocolors); + Cvar_RegisterVariable (&gl_finish); + Cvar_RegisterVariable (&gl_fb_depthhack); + Cvar_RegisterVariable (&gl_fb_bmodels); + Cvar_RegisterVariable (&gl_fb_models); + + Cvar_RegisterVariable (&gl_keeptjunctions); + Cvar_RegisterVariable (&gl_reporttjunctions); + + R_InitBubble(); + + R_InitParticles (); + R_InitParticleTexture (); + +#ifdef GLTEST + Test_Init (); +#endif + + netgraphtexture = texture_extension_number; + texture_extension_number++; + + playertextures = texture_extension_number; + texture_extension_number += MAX_CLIENTS; +} + +/* +=============== +R_TranslatePlayerSkin + +Translates a skin texture by the per-player color lookup +=============== +*/ +void R_TranslatePlayerSkin (int playernum) +{ + int top, bottom; + byte translate[256]; + unsigned translate32[256]; + int i, j; + byte *original; + unsigned pixels[512*256], *out; + int scaled_width, scaled_height; + int inwidth, inheight; + int tinwidth, tinheight; + byte *inrow; + unsigned frac, fracstep; + player_info_t *player; + extern byte player_8bit_texels[320*200]; + char s[512]; + + GL_DisableMultitexture(); + + player = &cl.players[playernum]; + if (!player->name[0]) + return; + + strcpy(s, Info_ValueForKey(player->userinfo, "skin")); + COM_StripExtension(s, s); + if (player->skin && !stricmp(s, player->skin->name)) + player->skin = NULL; + + if (player->_topcolor != player->topcolor || + player->_bottomcolor != player->bottomcolor || !player->skin) { + player->_topcolor = player->topcolor; + player->_bottomcolor = player->bottomcolor; + + top = player->topcolor; + bottom = player->bottomcolor; + top = (top < 0) ? 0 : ((top > 13) ? 13 : top); + bottom = (bottom < 0) ? 0 : ((bottom > 13) ? 13 : bottom); + top *= 16; + bottom *= 16; + + for (i=0 ; i<256 ; i++) + translate[i] = i; + + for (i=0 ; i<16 ; i++) + { + if (top < 128) // the artists made some backwards ranges. sigh. + translate[TOP_RANGE+i] = top+i; + else + translate[TOP_RANGE+i] = top+15-i; + + if (bottom < 128) + translate[BOTTOM_RANGE+i] = bottom+i; + else + translate[BOTTOM_RANGE+i] = bottom+15-i; + } + + // + // locate the original skin pixels + // + // real model width + tinwidth = 296; + tinheight = 194; + + if (!player->skin) + Skin_Find(player); + if ((original = Skin_Cache(player->skin)) != NULL) { + //skin data width + inwidth = 320; + inheight = 200; + } else { + original = player_8bit_texels; + inwidth = 296; + inheight = 194; + } + + + // because this happens during gameplay, do it fast + // instead of sending it through gl_upload 8 + GL_Bind(playertextures + playernum); + + #if 0 + s = 320*200; + byte translated[320*200]; + + for (i=0 ; iskinwidth, paliashdr->skinheight, + false, false, true, false); + #endif + + scaled_width = gl_max_size.value < 512 ? gl_max_size.value : 512; + scaled_height = gl_max_size.value < 256 ? gl_max_size.value : 256; + // allow users to crunch sizes down even more if they want + scaled_width >>= (int)gl_playermip.value; + scaled_height >>= (int)gl_playermip.value; + if (scaled_width < 1) + scaled_width = 1; + if (scaled_height < 1) + scaled_height = 1; + + if (VID_Is8bit()) { // 8bit texture upload + byte *out2; + + out2 = (byte *)pixels; + memset(pixels, 0, sizeof(pixels)); + fracstep = tinwidth*0x10000/scaled_width; + for (i=0 ; i> 1; + for (j=0 ; j>16]]; + frac += fracstep; + out2[j+1] = translate[inrow[frac>>16]]; + frac += fracstep; + out2[j+2] = translate[inrow[frac>>16]]; + frac += fracstep; + out2[j+3] = translate[inrow[frac>>16]]; + frac += fracstep; + } + } + + GL_Upload8_EXT ((byte *)pixels, scaled_width, scaled_height, false, false); + return; + } + + for (i=0 ; i<256 ; i++) + translate32[i] = d_8to24table[translate[i]]; + + out = pixels; + memset(pixels, 0, sizeof(pixels)); + fracstep = tinwidth*0x10000/scaled_width; + for (i=0 ; i> 1; + for (j=0 ; j>16]]; + frac += fracstep; + out[j+1] = translate32[inrow[frac>>16]]; + frac += fracstep; + out[j+2] = translate32[inrow[frac>>16]]; + frac += fracstep; + out[j+3] = translate32[inrow[frac>>16]]; + frac += fracstep; + } + } + + glTexImage2D (GL_TEXTURE_2D, 0, gl_solid_format, + scaled_width, scaled_height, 0, GL_RGBA, + GL_UNSIGNED_BYTE, pixels); + + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } +} + +/* +=============== +R_NewMap +=============== +*/ +void R_NewMap (void) +{ + int i; + + for (i=0 ; i<256 ; i++) + d_lightstylevalue[i] = 264; // normal light value + + memset (&r_worldentity, 0, sizeof(r_worldentity)); + r_worldentity.model = cl.worldmodel; + +// clear out efrags in case the level hasn't been reloaded +// FIXME: is this one short? + for (i=0 ; inumleafs ; i++) + cl.worldmodel->leafs[i].efrags = NULL; + + r_viewleaf = NULL; + R_ClearParticles (); + + GL_BuildLightmaps (); + + // identify sky texture + skytexturenum = -1; + mirrortexturenum = -1; + for (i=0 ; inumtextures ; i++) + { + if (!cl.worldmodel->textures[i]) + continue; + if (!strncmp(cl.worldmodel->textures[i]->name,"sky",3) ) + skytexturenum = i; + if (!strncmp(cl.worldmodel->textures[i]->name,"window02_1",10) ) + mirrortexturenum = i; + cl.worldmodel->textures[i]->texturechain = NULL; + } +#ifdef QUAKE2 + R_LoadSkys (); +#endif +} + + +/* +==================== +R_TimeRefresh_f + +For program optimization +==================== +*/ +void R_TimeRefresh_f (void) +{ + int i; + float start, stop, time; + + if (cls.state != ca_active) + return; + + glDrawBuffer (GL_FRONT); + glFinish (); + + start = Sys_DoubleTime (); + for (i=0 ; i<128 ; i++) + { + r_refdef.viewangles[1] = i/128.0*360.0; + R_RenderView (); + } + + glFinish (); + stop = Sys_DoubleTime (); + time = stop-start; + Con_Printf ("%f seconds (%f fps)\n", time, 128/time); + + glDrawBuffer (GL_BACK); + GL_EndRendering (); +} + +void D_FlushCaches (void) +{ +} + + diff --git a/source/gl_rsurf.c b/source/gl_rsurf.c index bd047d72..7df5c0bd 100644 --- a/source/gl_rsurf.c +++ b/source/gl_rsurf.c @@ -1,1737 +1,1826 @@ -/* -Copyright (C) 1996-1997 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 "quakedef.h" - -int skytexturenum; - -#ifndef GL_RGBA4 -#define GL_RGBA4 0 -#endif - -float wateralpha; // 1 if watervis is disabled by server, - // otherwise equal to r_wateralpha.value - -int lightmap_bytes; // 1, 2, or 4 - -int lightmap_textures; - -unsigned blocklights[18*18]; - -#define BLOCK_WIDTH 128 -#define BLOCK_HEIGHT 128 - -#define MAX_LIGHTMAPS 64 -int active_lightmaps; - -typedef struct glRect_s { - unsigned char l,t,w,h; -} glRect_t; - -glpoly_t *lightmap_polys[MAX_LIGHTMAPS]; -qboolean lightmap_modified[MAX_LIGHTMAPS]; -glRect_t lightmap_rectchange[MAX_LIGHTMAPS]; - -int allocated[MAX_LIGHTMAPS][BLOCK_WIDTH]; - -// the lightmap texture data needs to be kept in -// main memory so texsubimage can update properly -byte lightmaps[4*MAX_LIGHTMAPS*BLOCK_WIDTH*BLOCK_HEIGHT]; - -// For gl_texsort 0 -msurface_t *skychain = NULL; -msurface_t *waterchain = NULL; - -void R_RenderDynamicLightmaps (msurface_t *fa); - -glpoly_t *fullbright_polys[MAX_GLTEXTURES]; -qboolean drawfullbrights = false; - -void DrawGLPoly (glpoly_t *p); - -void R_RenderFullbrights (void) -{ - int i; - glpoly_t *p; - - if (!drawfullbrights || !gl_fb_bmodels.value) - return; - - GL_DisableMultitexture (); - - glDepthMask (0); // don't bother writing Z - if (gl_fb_depthhack.value) - { - float depthdelta; - extern cvar_t gl_ztrick; - extern int gl_ztrickframe; - - if (gl_ztrick.value) - depthdelta = gl_ztrickframe ? - 1.0/16384 : 1.0/16384; - else - depthdelta = -1.0/8192; - - // hack depth range to prevent flickering of fullbrights - glDepthRange (gldepthmin + depthdelta, gldepthmax + depthdelta); - } - - glEnable (GL_BLEND); -// glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); -// glColor4f (1, 1, 1, 1); - - for (i=1 ; ifb_chain) { - DrawGLPoly (p); - } - fullbright_polys[i] = NULL; - } - - - glDisable (GL_BLEND); - glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); - - glDepthMask (1); - if (gl_fb_depthhack.value) - glDepthRange (gldepthmin, gldepthmax); - - drawfullbrights = false; -} - - -//============================================================= -// Dynamic lights - -typedef struct dlightinfo_s { - int local[2]; - int rad; - int minlight; // rad - minlight -} dlightinfo_t; - -static dlightinfo_t dlightlist[MAX_DLIGHTS]; -static int numdlights; - -/* -=============== -R_BuildDLightList -=============== -*/ -void R_BuildDLightList (msurface_t *surf) -{ - int lnum; - float dist; - vec3_t impact; - int i; - int smax, tmax; - mtexinfo_t *tex; - int irad, iminlight; - int local[2]; - int tdmin, sdmin, distmin; - dlightinfo_t *light; - - numdlights = 0; - - smax = (surf->extents[0]>>4)+1; - tmax = (surf->extents[1]>>4)+1; - tex = surf->texinfo; - - for (lnum=0 ; lnumdlightbits & (1<plane->normal) - - surf->plane->dist; - irad = (cl_dlights[lnum].radius - fabs(dist)) * 256; - iminlight = cl_dlights[lnum].minlight * 256; - if (irad < iminlight) - continue; - - iminlight = irad - iminlight; - - for (i=0 ; i<3 ; i++) { - impact[i] = cl_dlights[lnum].origin[i] - - surf->plane->normal[i]*dist; - } - - local[0] = DotProduct (impact, tex->vecs[0]) + - tex->vecs[0][3] - surf->texturemins[0]; - local[1] = DotProduct (impact, tex->vecs[1]) + - tex->vecs[1][3] - surf->texturemins[1]; - - // check if this dlight will touch the surface - if (local[1] > 0) { - tdmin = local[1] - (tmax<<4); - if (tdmin < 0) - tdmin = 0; - } else - tdmin = -local[1]; - - if (local[0] > 0) { - sdmin = local[0] - (smax<<4); - if (sdmin < 0) - sdmin = 0; - } else - sdmin = -local[0]; - - if (sdmin > tdmin) - distmin = (sdmin<<8) + (tdmin<<7); - else - distmin = (tdmin<<8) + (sdmin<<7); - - if (distmin < iminlight) - { - // save dlight info - light = &dlightlist[numdlights]; - light->minlight = iminlight; - light->rad = irad; - light->local[0] = local[0]; - light->local[1] = local[1]; - numdlights++; - } - } -} - -/* -=============== -R_AddDynamicLights - -NOTE: R_BuildDLightList must be called first! -=============== -*/ -void R_AddDynamicLights (msurface_t *surf) -{ - int i; - int smax, tmax; - int s, t; - int sd, td; - int _sd, _td; - int local[2]; - int irad, idist, iminlight; - dlightinfo_t *light; - unsigned *dest; - - smax = (surf->extents[0]>>4)+1; - tmax = (surf->extents[1]>>4)+1; - - for (i=0,light=dlightlist ; irad; - iminlight = light->minlight; - local[0] = light->local[0]; -// local[1] = light->local[1]; - - _td = light->local[1]; - dest = blocklights; - for (t = 0 ; t td) - idist = (sd<<8) + (td<<7); - else - idist = (td<<8) + (sd<<7); - if (idist < iminlight) - *dest += irad - idist; - dest++; - } - } - } -} - - -/* -=============== -R_BuildLightMap - -Combine and scale multiple lightmaps into the 8.8 format in blocklights -=============== -*/ -void R_BuildLightMap (msurface_t *surf, byte *dest, int stride) -{ - int smax, tmax; - int t; - int i, j, size; - byte *lightmap; - unsigned scale; - int maps; - unsigned *bl; - - surf->cached_dlight = !!numdlights; - - smax = (surf->extents[0]>>4)+1; - tmax = (surf->extents[1]>>4)+1; - size = smax*tmax; - lightmap = surf->samples; - -// set to full bright if no light data - if (/* r_fullbright.value || */ !cl.worldmodel->lightdata) - { - for (i=0 ; istyles[maps] != 255 ; - maps++) - { - scale = d_lightstylevalue[surf->styles[maps]]; - surf->cached_light[maps] = scale; // 8.8 fraction - for (i=0 ; i>= 7; - t = (t >> 8) + (t >> 9); - if (t > 255) - t = 255; - dest[3] = 255-t; - dest += 4; - } - } - break; - case GL_ALPHA: - case GL_LUMINANCE: - case GL_INTENSITY: - bl = blocklights; - for (i=0 ; i>= 7; - t = (t >> 8) + (t >> 9); - if (t > 255) - t = 255; - dest[j] = 255-t; - } - } - break; - default: - Sys_Error ("Bad lightmap format"); - } -} - - -/* -=============== -R_TextureAnimation - -Returns the proper texture for a given time and base texture -=============== -*/ -texture_t *R_TextureAnimation (texture_t *base) -{ - int relative; - int count; - - if (currententity->frame) - { - if (base->alternate_anims) - base = base->alternate_anims; - } - - if (!base->anim_total) - return base; - - relative = (int)(cl.time*10) % base->anim_total; - - count = 0; - while (base->anim_min > relative || base->anim_max <= relative) - { - base = base->anim_next; - if (!base) - Sys_Error ("R_TextureAnimation: broken cycle"); - if (++count > 100) - Sys_Error ("R_TextureAnimation: infinite cycle"); - } - - return base; -} - - -/* -============================================================= - - BRUSH MODELS - -============================================================= -*/ - - -extern int solidskytexture; -extern int alphaskytexture; -extern float speedscale; // for top sky and bottom sky - -#ifdef _WIN32 -lpMTexFUNC qglMultiTexCoord2f = NULL; -lpSelTexFUNC qglActiveTexture = NULL; -#endif - -qboolean mtexenabled = false; - -void GL_SelectTexture (GLenum target); - -void GL_DisableMultitexture (void) -{ - if (mtexenabled) { - glDisable (GL_TEXTURE_2D); - GL_SelectTexture (TEXTURE0_ARB); - mtexenabled = false; - } -} - -void GL_EnableMultitexture (void) -{ - if (gl_mtexable) { - GL_SelectTexture (TEXTURE1_ARB); - glEnable (GL_TEXTURE_2D); - mtexenabled = true; - } -} - -#ifndef _WIN32 -/* -================ -R_DrawSequentialPoly - -Systems that have fast state and texture changes can -just do everything as it passes with no need to sort -================ -*/ - -//TODO: make this work! -void R_DrawSequentialPoly (msurface_t *s) -{ - glpoly_t *p; - float *v; - int i; - texture_t *t; - - // - // normal lightmaped poly - // - if (!(s->flags & (SURF_DRAWSKY|SURF_DRAWTURB)) - { - p = s->polys; - - t = R_TextureAnimation (s->texinfo->texture); - GL_Bind (t->gl_texturenum); - glBegin (GL_POLYGON); - v = p->verts[0]; - for (i=0 ; inumverts ; i++, v+= VERTEXSIZE) - { - glTexCoord2f (v[3], v[4]); - glVertex3fv (v); - } - glEnd (); - - GL_Bind (lightmap_textures + s->lightmaptexturenum); - glEnable (GL_BLEND); - glBegin (GL_POLYGON); - v = p->verts[0]; - for (i=0 ; inumverts ; i++, v+= VERTEXSIZE) - { - glTexCoord2f (v[5], v[6]); - glVertex3fv (v); - } - glEnd (); - - glDisable (GL_BLEND); - - return; - } - - // - // subdivided water surface warp - // - if (s->flags & SURF_DRAWTURB) - { - GL_Bind (s->texinfo->texture->gl_texturenum); - EmitWaterPolys (s); - return; - } - - // - // subdivided sky warp - // - if (s->flags & SURF_DRAWSKY) - { - GL_Bind (solidskytexture); - speedscale = realtime*8; - speedscale -= (int)speedscale; - - EmitSkyPolys (s); - - glEnable (GL_BLEND); - glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - GL_Bind (alphaskytexture); - speedscale = realtime*16; - speedscale -= (int)speedscale; - EmitSkyPolys (s); - if (gl_lightmap_format == GL_LUMINANCE) - glBlendFunc (GL_ZERO, GL_ONE_MINUS_SRC_COLOR); - - glDisable (GL_BLEND); - } -} -#else -/* -================ -R_DrawSequentialPoly - -Systems that have fast state and texture changes can -just do everything as it passes with no need to sort -================ -*/ -void R_DrawSequentialPoly (msurface_t *s) -{ - glpoly_t *p; - float *v; - int i; - texture_t *t; - glRect_t *theRect; - - // - // normal lightmaped poly - // - if (!(s->flags & (SURF_DRAWSKY|SURF_DRAWTURB))) - { - R_RenderDynamicLightmaps (s); - if (gl_mtexable) { - p = s->polys; - - t = R_TextureAnimation (s->texinfo->texture); - // Binds world to texture env 0 - GL_SelectTexture(TEXTURE0_ARB); - GL_Bind (t->gl_texturenum); - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); - // Binds lightmap to texenv 1 - GL_EnableMultitexture(); // Same as SelectTexture (TEXTURE1) - GL_Bind (lightmap_textures + s->lightmaptexturenum); - i = s->lightmaptexturenum; - if (lightmap_modified[i]) - { - lightmap_modified[i] = false; - theRect = &lightmap_rectchange[i]; - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, theRect->t, - BLOCK_WIDTH, theRect->h, gl_lightmap_format, GL_UNSIGNED_BYTE, - lightmaps+(i* BLOCK_HEIGHT + theRect->t) *BLOCK_WIDTH*lightmap_bytes); - theRect->l = BLOCK_WIDTH; - theRect->t = BLOCK_HEIGHT; - theRect->h = 0; - theRect->w = 0; - } - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND); - glBegin(GL_POLYGON); - v = p->verts[0]; - for (i=0 ; inumverts ; i++, v+= VERTEXSIZE) - { - qglMultiTexCoord2f (TEXTURE0_ARB, v[3], v[4]); - qglMultiTexCoord2f (TEXTURE1_ARB, v[5], v[6]); - glVertex3fv (v); - } - glEnd (); - } else { - p = s->polys; - - t = R_TextureAnimation (s->texinfo->texture); - GL_Bind (t->gl_texturenum); - glBegin (GL_POLYGON); - v = p->verts[0]; - for (i=0 ; inumverts ; i++, v+= VERTEXSIZE) - { - glTexCoord2f (v[3], v[4]); - glVertex3fv (v); - } - glEnd (); - - GL_Bind (lightmap_textures + s->lightmaptexturenum); - glEnable (GL_BLEND); - glBegin (GL_POLYGON); - v = p->verts[0]; - for (i=0 ; inumverts ; i++, v+= VERTEXSIZE) - { - glTexCoord2f (v[5], v[6]); - glVertex3fv (v); - } - glEnd (); - - glDisable (GL_BLEND); - } - - if (t->fb_texturenum) { - s->polys->fb_chain = fullbright_polys[t->fb_texturenum]; - fullbright_polys[t->fb_texturenum] = s->polys; - drawfullbrights = true; - } - - return; - } - - // - // subdivided water surface warp - // - if (s->flags & SURF_DRAWTURB) - { - GL_DisableMultitexture(); - GL_Bind (s->texinfo->texture->gl_texturenum); - EmitWaterPolys (s); - return; - } - - // - // subdivided sky warp - // - if (s->flags & SURF_DRAWSKY) - { - GL_DisableMultitexture(); - GL_Bind (solidskytexture); - speedscale = realtime*8; - speedscale -= (int)speedscale & ~127; - - EmitSkyPolys (s); - - glEnable (GL_BLEND); - GL_Bind (alphaskytexture); - speedscale = realtime*16; - speedscale -= (int)speedscale & ~127; - EmitSkyPolys (s); - - glDisable (GL_BLEND); - return; - } -} -#endif - - -/* -================ -DrawGLPoly -================ -*/ -void DrawGLPoly (glpoly_t *p) -{ - int i; - float *v; - - glBegin (GL_POLYGON); - v = p->verts[0]; - for (i=0 ; inumverts ; i++, v+= VERTEXSIZE) - { - glTexCoord2f (v[3], v[4]); - glVertex3fv (v); - } - glEnd (); -} - - -/* -================ -R_BlendLightmaps -================ -*/ -void R_BlendLightmaps (void) -{ - int i, j; - glpoly_t *p; - float *v; - glRect_t *theRect; - -#if 0 - if (r_fullbright.value) - return; -#endif - if (!gl_texsort.value) - return; - - glDepthMask (0); // don't bother writing Z - - if (gl_lightmap_format == GL_LUMINANCE) - glBlendFunc (GL_ZERO, GL_ONE_MINUS_SRC_COLOR); - else if (gl_lightmap_format == GL_INTENSITY) - { - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - glColor4f (0,0,0,1); - glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - } - - if (!r_lightmap.value) - { - glEnable (GL_BLEND); - } - - for (i=0 ; il = 0; -// theRect->t = 0; -// theRect->w = BLOCK_WIDTH; -// theRect->h = BLOCK_HEIGHT; -// glTexImage2D (GL_TEXTURE_2D, 0, lightmap_bytes -// , BLOCK_WIDTH, BLOCK_HEIGHT, 0, -// gl_lightmap_format, GL_UNSIGNED_BYTE, lightmaps+i*BLOCK_WIDTH*BLOCK_HEIGHT*lightmap_bytes); -// glTexImage2D (GL_TEXTURE_2D, 0, lightmap_bytes -// , BLOCK_WIDTH, theRect->h, 0, -// gl_lightmap_format, GL_UNSIGNED_BYTE, lightmaps+(i*BLOCK_HEIGHT+theRect->t)*BLOCK_WIDTH*lightmap_bytes); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, theRect->t, - BLOCK_WIDTH, theRect->h, gl_lightmap_format, GL_UNSIGNED_BYTE, - lightmaps+(i* BLOCK_HEIGHT + theRect->t) *BLOCK_WIDTH*lightmap_bytes); - theRect->l = BLOCK_WIDTH; - theRect->t = BLOCK_HEIGHT; - theRect->h = 0; - theRect->w = 0; - } - for ( ; p ; p=p->chain) - { - glBegin (GL_POLYGON); - v = p->verts[0]; - for (j=0 ; jnumverts ; j++, v+= VERTEXSIZE) - { - glTexCoord2f (v[5], v[6]); - glVertex3fv (v); - } - glEnd (); - } - } - - glDisable (GL_BLEND); - if (gl_lightmap_format == GL_LUMINANCE) - glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - else if (gl_lightmap_format == GL_INTENSITY) - { - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); - glColor4f (1,1,1,1); - } - - glDepthMask (1); // back to normal Z buffering -} - -/* -================ -R_RenderBrushPoly -================ -*/ -void R_RenderBrushPoly (msurface_t *fa) -{ - texture_t *t; - byte *base; - int maps; - glRect_t *theRect; - int smax, tmax; - qboolean lightstyle_modified = false; - - c_brush_polys++; - - if (fa->flags & SURF_DRAWSKY) - { // warp texture, no lightmaps - EmitBothSkyLayers (fa); - return; - } - - t = R_TextureAnimation (fa->texinfo->texture); - GL_Bind (t->gl_texturenum); - - if (fa->flags & SURF_DRAWTURB) - { // warp texture, no lightmaps - EmitWaterPolys (fa); - return; - } - - DrawGLPoly (fa->polys); - - // add the poly to the proper lightmap chain - fa->polys->chain = lightmap_polys[fa->lightmaptexturenum]; - lightmap_polys[fa->lightmaptexturenum] = fa->polys; - - if (t->fb_texturenum) { - fa->polys->fb_chain = fullbright_polys[t->fb_texturenum]; - fullbright_polys[t->fb_texturenum] = fa->polys; - drawfullbrights = true; - } - - if (!r_dynamic.value) - return; - - // check for lightmap modification - for (maps=0 ; mapsstyles[maps] != 255 ; maps++) - if (d_lightstylevalue[fa->styles[maps]] != fa->cached_light[maps]) { - lightstyle_modified = true; - break; - } - - if (fa->dlightframe == r_framecount) - R_BuildDLightList (fa); - else - numdlights = 0; - - if (numdlights == 0 && !fa->cached_dlight && !lightstyle_modified) - return; - - lightmap_modified[fa->lightmaptexturenum] = true; - theRect = &lightmap_rectchange[fa->lightmaptexturenum]; - if (fa->light_t < theRect->t) { - if (theRect->h) - theRect->h += theRect->t - fa->light_t; - theRect->t = fa->light_t; - } - if (fa->light_s < theRect->l) { - if (theRect->w) - theRect->w += theRect->l - fa->light_s; - theRect->l = fa->light_s; - } - smax = (fa->extents[0]>>4)+1; - tmax = (fa->extents[1]>>4)+1; - if ((theRect->w + theRect->l) < (fa->light_s + smax)) - theRect->w = (fa->light_s-theRect->l)+smax; - if ((theRect->h + theRect->t) < (fa->light_t + tmax)) - theRect->h = (fa->light_t-theRect->t)+tmax; - base = lightmaps + fa->lightmaptexturenum*lightmap_bytes*BLOCK_WIDTH*BLOCK_HEIGHT; - base += fa->light_t * BLOCK_WIDTH * lightmap_bytes + fa->light_s * lightmap_bytes; - R_BuildLightMap (fa, base, BLOCK_WIDTH*lightmap_bytes); -} - -/* -================ -R_RenderDynamicLightmaps -Multitexture -================ -*/ -void R_RenderDynamicLightmaps (msurface_t *fa) -{ - byte *base; - int maps; - glRect_t *theRect; - int smax, tmax; - qboolean lightstyle_modified = false; - - c_brush_polys++; - - if (fa->flags & ( SURF_DRAWSKY | SURF_DRAWTURB) ) - return; - - fa->polys->chain = lightmap_polys[fa->lightmaptexturenum]; - lightmap_polys[fa->lightmaptexturenum] = fa->polys; - - if (!r_dynamic.value) - return; - - // check for lightmap modification - for (maps=0 ; mapsstyles[maps] != 255 ; maps++) - if (d_lightstylevalue[fa->styles[maps]] != fa->cached_light[maps]) { - lightstyle_modified = true; - break; - } - - if (fa->dlightframe == r_framecount) - R_BuildDLightList (fa); - else - numdlights = 0; - - if (numdlights == 0 && !fa->cached_dlight && !lightstyle_modified) - return; - - lightmap_modified[fa->lightmaptexturenum] = true; - theRect = &lightmap_rectchange[fa->lightmaptexturenum]; - if (fa->light_t < theRect->t) { - if (theRect->h) - theRect->h += theRect->t - fa->light_t; - theRect->t = fa->light_t; - } - if (fa->light_s < theRect->l) { - if (theRect->w) - theRect->w += theRect->l - fa->light_s; - theRect->l = fa->light_s; - } - smax = (fa->extents[0]>>4)+1; - tmax = (fa->extents[1]>>4)+1; - if ((theRect->w + theRect->l) < (fa->light_s + smax)) - theRect->w = (fa->light_s-theRect->l)+smax; - if ((theRect->h + theRect->t) < (fa->light_t + tmax)) - theRect->h = (fa->light_t-theRect->t)+tmax; - base = lightmaps + fa->lightmaptexturenum*lightmap_bytes*BLOCK_WIDTH*BLOCK_HEIGHT; - base += fa->light_t * BLOCK_WIDTH * lightmap_bytes + fa->light_s * lightmap_bytes; - R_BuildLightMap (fa, base, BLOCK_WIDTH*lightmap_bytes); -} - -/* -================ -R_MirrorChain -================ -*/ -void R_MirrorChain (msurface_t *s) -{ - if (mirror) - return; - mirror = true; - mirror_plane = s->plane; -} - - -#if 0 -/* -================ -R_DrawWaterSurfaces -================ -*/ -void R_DrawWaterSurfaces (void) -{ - int i; - msurface_t *s; - texture_t *t; - - if (wateralpha == 1.0) - return; - - // - // go back to the world matrix - // - glLoadMatrixf (r_world_matrix); - - glEnable (GL_BLEND); - glColor4f (1,1,1,wateralpha); - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - - for (i=0 ; inumtextures ; i++) - { - t = cl.worldmodel->textures[i]; - if (!t) - continue; - s = t->texturechain; - if (!s) - continue; - if ( !(s->flags & SURF_DRAWTURB) ) - continue; - - // set modulate mode explicitly - GL_Bind (t->gl_texturenum); - - for ( ; s ; s=s->texturechain) - R_RenderBrushPoly (s); - - t->texturechain = NULL; - } - - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); - - glColor4f (1,1,1,1); - glDisable (GL_BLEND); -} -#else -/* -================ -R_DrawWaterSurfaces -================ -*/ -void R_DrawWaterSurfaces (void) -{ - int i; - msurface_t *s; - texture_t *t; - - if (wateralpha == 1.0 && gl_texsort.value) - return; - - // - // go back to the world matrix - // - - glLoadMatrixf (r_world_matrix); - - if (wateralpha < 1.0) { - glEnable (GL_BLEND); - glColor4f (1, 1, 1, wateralpha); - glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - } - - if (!gl_texsort.value) { - if (!waterchain) - return; - - for (s=waterchain ; s ; s=s->texturechain) { - GL_Bind (s->texinfo->texture->gl_texturenum); - EmitWaterPolys (s); - } - - waterchain = NULL; - } else { - - for (i=0 ; inumtextures ; i++) - { - t = cl.worldmodel->textures[i]; - if (!t) - continue; - s = t->texturechain; - if (!s) - continue; - if ( !(s->flags & SURF_DRAWTURB ) ) - continue; - - // set modulate mode explicitly - - GL_Bind (t->gl_texturenum); - - for ( ; s ; s=s->texturechain) - EmitWaterPolys (s); - - t->texturechain = NULL; - } - - } - - if (wateralpha < 1.0) { - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); - - glColor4f (1,1,1,1); - glDisable (GL_BLEND); - } - -} - -#endif - -/* -================ -DrawTextureChains -================ -*/ -void DrawTextureChains (void) -{ - int i; - msurface_t *s; - texture_t *t; - - if (!gl_texsort.value) { - GL_DisableMultitexture(); - - if (skychain) { - R_DrawSkyChain(skychain); - skychain = NULL; - } - - return; - } - - for (i=0 ; inumtextures ; i++) - { - t = cl.worldmodel->textures[i]; - if (!t) - continue; - s = t->texturechain; - if (!s) - continue; - if (i == skytexturenum) - R_DrawSkyChain (s); - else if (i == mirrortexturenum && r_mirroralpha.value != 1.0) - { - R_MirrorChain (s); - continue; - } - else - { - if ((s->flags & SURF_DRAWTURB) && wateralpha != 1.0) - continue; // draw translucent water later - for ( ; s ; s=s->texturechain) - R_RenderBrushPoly (s); - } - - t->texturechain = NULL; - } -} - -/* -================= -R_DrawBrushModel -================= -*/ -void R_DrawBrushModel (entity_t *e) -{ - int i; - int k; - vec3_t mins, maxs; - msurface_t *psurf; - float dot; - mplane_t *pplane; - model_t *clmodel; - qboolean rotated; - - currententity = e; - currenttexture = -1; - - clmodel = e->model; - - if (e->angles[0] || e->angles[1] || e->angles[2]) - { - rotated = true; - for (i=0 ; i<3 ; i++) - { - mins[i] = e->origin[i] - clmodel->radius; - maxs[i] = e->origin[i] + clmodel->radius; - } - } - else - { - rotated = false; - VectorAdd (e->origin, clmodel->mins, mins); - VectorAdd (e->origin, clmodel->maxs, maxs); - } - - if (R_CullBox (mins, maxs)) - return; - - glColor3f (1,1,1); - memset (lightmap_polys, 0, sizeof(lightmap_polys)); -// if (gl_fb_bmodels.value) -// memset (fullbright_polys, 0, sizeof(fullbright_polys)); - - VectorSubtract (r_refdef.vieworg, e->origin, modelorg); - if (rotated) - { - vec3_t temp; - vec3_t forward, right, up; - - VectorCopy (modelorg, temp); - AngleVectors (e->angles, forward, right, up); - modelorg[0] = DotProduct (temp, forward); - modelorg[1] = -DotProduct (temp, right); - modelorg[2] = DotProduct (temp, up); - } - - psurf = &clmodel->surfaces[clmodel->firstmodelsurface]; - -// calculate dynamic lighting for bmodel if it's not an -// instanced model - if (clmodel->firstmodelsurface != 0 && !gl_flashblend.value) - { - for (k=0 ; knodes + clmodel->hulls[0].firstclipnode); - } - } - - glPushMatrix (); -e->angles[0] = -e->angles[0]; // stupid quake bug - R_RotateForEntity (e); -e->angles[0] = -e->angles[0]; // stupid quake bug - - // - // draw texture - // - for (i=0 ; inummodelsurfaces ; i++, psurf++) - { - // find which side of the node we are on - pplane = psurf->plane; - - 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))) - { - if (gl_texsort.value) - R_RenderBrushPoly (psurf); - else - R_DrawSequentialPoly (psurf); - } - } - - R_BlendLightmaps (); - - R_RenderFullbrights (); - - glPopMatrix (); -} - -/* -============================================================= - - WORLD MODEL - -============================================================= -*/ - -/* -================ -R_RecursiveWorldNode -================ -*/ -void R_RecursiveWorldNode (mnode_t *node) -{ - int c, side; - mplane_t *plane; - msurface_t *surf, **mark; - mleaf_t *pleaf; - double dot; - - if (node->contents == CONTENTS_SOLID) - return; // solid - - if (node->visframe != r_visframecount) - return; - if (R_CullBox (node->minmaxs, node->minmaxs+3)) - return; - -// if a leaf node, draw stuff - if (node->contents < 0) - { - pleaf = (mleaf_t *)node; - - mark = pleaf->firstmarksurface; - c = pleaf->nummarksurfaces; - - if (c) - { - do - { - (*mark)->visframe = r_framecount; - mark++; - } while (--c); - } - - // deal with model fragments in this leaf - if (pleaf->efrags) - R_StoreEfrags (&pleaf->efrags); - - return; - } - -// 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]); - -// draw stuff - c = node->numsurfaces; - - if (c) - { - surf = cl.worldmodel->surfaces + node->firstsurface; - - if (dot < 0 -BACKFACE_EPSILON) - side = SURF_PLANEBACK; - else if (dot > BACKFACE_EPSILON) - side = 0; - { - for ( ; c ; c--, surf++) - { - if (surf->visframe != r_framecount) - continue; - - if ((dot < 0) ^ !!(surf->flags & SURF_PLANEBACK)) - continue; // wrong side - - // if sorting by texture, just store it out - if (gl_texsort.value) - { - if (!mirror - || surf->texinfo->texture != cl.worldmodel->textures[mirrortexturenum]) - { - surf->texturechain = surf->texinfo->texture->texturechain; - surf->texinfo->texture->texturechain = surf; - } - } else if (surf->flags & SURF_DRAWSKY) { - surf->texturechain = skychain; - skychain = surf; - } else if (surf->flags & SURF_DRAWTURB) { - surf->texturechain = waterchain; - waterchain = surf; - } else - R_DrawSequentialPoly (surf); - - } - } - - } - -// recurse down the back side - R_RecursiveWorldNode (node->children[!side]); -} - - - -/* -============= -R_DrawWorld -============= -*/ -void R_DrawWorld (void) -{ - entity_t ent; - - memset (&ent, 0, sizeof(ent)); - ent.model = cl.worldmodel; - - VectorCopy (r_refdef.vieworg, modelorg); - - currententity = &ent; - currenttexture = -1; - - glColor3f (1,1,1); - memset (lightmap_polys, 0, sizeof(lightmap_polys)); - if (gl_fb_bmodels.value) - memset (fullbright_polys, 0, sizeof(fullbright_polys)); - -#ifdef QUAKE2 - R_ClearSkyBox (); -#endif - - R_RecursiveWorldNode (cl.worldmodel->nodes); - - DrawTextureChains (); - - R_BlendLightmaps (); - - R_RenderFullbrights (); - -#ifdef QUAKE2 - R_DrawSkyBox (); -#endif -} - - -/* -=============== -R_MarkLeaves -=============== -*/ -void R_MarkLeaves (void) -{ - byte *vis; - mnode_t *node; - int i; - byte solid[MAX_MAP_LEAFS/8]; - - if (!r_novis.value && r_oldviewleaf == r_viewleaf - && r_oldviewleaf2 == r_viewleaf2) // watervis hack - return; - - if (mirror) - return; - - r_visframecount++; - r_oldviewleaf = r_viewleaf; - - if (r_novis.value) - { - vis = solid; - memset (solid, 0xff, (cl.worldmodel->numleafs+7)>>3); - } - else - { - vis = Mod_LeafPVS (r_viewleaf, cl.worldmodel); - - if (r_viewleaf2) { - int i, count; - unsigned *src, *dest; - - // merge visibility data for two leafs - count = (cl.worldmodel->numleafs+7)>>3; - memcpy (solid, vis, count); - src = (unsigned *) Mod_LeafPVS (r_viewleaf2, cl.worldmodel); - dest = (unsigned *) solid; - count = (count + 3)>>2; - for (i=0 ; inumleafs ; i++) - { - if (vis[i>>3] & (1<<(i&7))) - { - node = (mnode_t *)&cl.worldmodel->leafs[i+1]; - do - { - if (node->visframe == r_visframecount) - break; - node->visframe = r_visframecount; - node = node->parent; - } while (node); - } - } -} - - - -/* -============================================================================= - - LIGHTMAP ALLOCATION - -============================================================================= -*/ - -// returns a texture number and the position inside it -int AllocBlock (int w, int h, int *x, int *y) -{ - int i, j; - int best, best2; - int texnum; - - for (texnum=0 ; texnum= best) - break; - if (allocated[texnum][i+j] > best2) - best2 = allocated[texnum][i+j]; - } - if (j == w) - { // this is a valid spot - *x = i; - *y = best = best2; - } - } - - if (best + h > BLOCK_HEIGHT) - continue; - - for (i=0 ; iedges; - lnumverts = fa->numedges; - vertpage = 0; - - // - // draw texture - // - poly = Hunk_Alloc (sizeof(glpoly_t) + (lnumverts-4) * VERTEXSIZE*sizeof(float)); - poly->next = fa->polys; - poly->flags = fa->flags; - fa->polys = poly; - poly->numverts = lnumverts; - - for (i=0 ; isurfedges[fa->firstedge + i]; - - if (lindex > 0) - { - r_pedge = &pedges[lindex]; - vec = r_pcurrentvertbase[r_pedge->v[0]].position; - } - else - { - r_pedge = &pedges[-lindex]; - vec = r_pcurrentvertbase[r_pedge->v[1]].position; - } - s = DotProduct (vec, fa->texinfo->vecs[0]) + fa->texinfo->vecs[0][3]; - s /= fa->texinfo->texture->width; - - t = DotProduct (vec, fa->texinfo->vecs[1]) + fa->texinfo->vecs[1][3]; - t /= fa->texinfo->texture->height; - - VectorCopy (vec, poly->verts[i]); - poly->verts[i][3] = s; - poly->verts[i][4] = t; - - // - // lightmap texture coordinates - // - s = DotProduct (vec, fa->texinfo->vecs[0]) + fa->texinfo->vecs[0][3]; - s -= fa->texturemins[0]; - s += fa->light_s*16; - s += 8; - s /= BLOCK_WIDTH*16; //fa->texinfo->texture->width; - - t = DotProduct (vec, fa->texinfo->vecs[1]) + fa->texinfo->vecs[1][3]; - t -= fa->texturemins[1]; - t += fa->light_t*16; - t += 8; - t /= BLOCK_HEIGHT*16; //fa->texinfo->texture->height; - - poly->verts[i][5] = s; - poly->verts[i][6] = t; - } - - // - // remove co-linear points - Ed - // - if (!gl_keeptjunctions.value) - { - for (i=0 ; iverts[(i + lnumverts - 1) % lnumverts]; - this = poly->verts[i]; - next = poly->verts[(i + 1) % lnumverts]; - - VectorSubtract (this, prev, v1); - VectorNormalize (v1); - VectorSubtract (next, prev, v2 ); - VectorNormalize (v2); - - // skip co-linear points - #define COLINEAR_EPSILON 0.001 - if ((fabs(v1[0] - v2[0]) <= COLINEAR_EPSILON) && - (fabs(v1[1] - v2[1]) <= COLINEAR_EPSILON) && - (fabs(v1[2] - v2[2]) <= COLINEAR_EPSILON)) - { - int j; - for (j = i + 1; j < lnumverts; ++j) - { - int k; - for (k = 0; k < VERTEXSIZE; ++k) - poly->verts[j - 1][k] = poly->verts[j][k]; - } - lnumverts--; - nColinElim++; - // retry next vertex next time, which is now current vertex - i--; - } - } - } - poly->numverts = lnumverts; - -} - -/* -======================== -GL_CreateSurfaceLightmap -======================== -*/ -void GL_CreateSurfaceLightmap (msurface_t *surf) -{ - int smax, tmax; - byte *base; - - if (surf->flags & (SURF_DRAWSKY|SURF_DRAWTURB)) - return; - - smax = (surf->extents[0]>>4)+1; - tmax = (surf->extents[1]>>4)+1; - - surf->lightmaptexturenum = AllocBlock (smax, tmax, &surf->light_s, &surf->light_t); - base = lightmaps + surf->lightmaptexturenum*lightmap_bytes*BLOCK_WIDTH*BLOCK_HEIGHT; - base += (surf->light_t * BLOCK_WIDTH + surf->light_s) * lightmap_bytes; - numdlights = 0; - R_BuildLightMap (surf, base, BLOCK_WIDTH*lightmap_bytes); -} - - -/* -================== -GL_BuildLightmaps - -Builds the lightmap texture -with all the surfaces from all brush models -================== -*/ -void GL_BuildLightmaps (void) -{ - int i, j; - model_t *m; - - memset (allocated, 0, sizeof(allocated)); - - r_framecount = 1; // no dlightcache - - if (!lightmap_textures) - { - lightmap_textures = texture_extension_number; - texture_extension_number += MAX_LIGHTMAPS; - } - - gl_lightmap_format = GL_LUMINANCE; - if (COM_CheckParm ("-lm_1")) - gl_lightmap_format = GL_LUMINANCE; - if (COM_CheckParm ("-lm_a")) - gl_lightmap_format = GL_ALPHA; - if (COM_CheckParm ("-lm_i")) - gl_lightmap_format = GL_INTENSITY; - if (COM_CheckParm ("-lm_2")) - gl_lightmap_format = GL_RGBA4; - if (COM_CheckParm ("-lm_4")) - gl_lightmap_format = GL_RGBA; - - switch (gl_lightmap_format) - { - case GL_RGBA: - lightmap_bytes = 4; - break; - case GL_RGBA4: - lightmap_bytes = 2; - break; - case GL_LUMINANCE: - case GL_INTENSITY: - case GL_ALPHA: - lightmap_bytes = 1; - break; - } - - for (j=1 ; jname[0] == '*') - continue; - r_pcurrentvertbase = m->vertexes; - currentmodel = m; - for (i=0 ; inumsurfaces ; i++) - { - GL_CreateSurfaceLightmap (m->surfaces + i); - if ( m->surfaces[i].flags & SURF_DRAWTURB ) - continue; -#ifndef QUAKE2 - if ( m->surfaces[i].flags & SURF_DRAWSKY ) - continue; -#endif - BuildSurfaceDisplayList (m->surfaces + i); - } - } - - if (!gl_texsort.value) - GL_SelectTexture (TEXTURE1_ARB); - - // - // upload all lightmaps that were filled - // - for (i=0 ; ifb_chain) { + DrawGLPoly (p); + } + fullbright_polys[i] = NULL; + } + + + glDisable (GL_BLEND); + glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + + glDepthMask (1); + if (gl_fb_depthhack.value) + glDepthRange (gldepthmin, gldepthmax); + + drawfullbrights = false; +} + + +//============================================================= +// Dynamic lights + +typedef struct dlightinfo_s { + int local[2]; + int rad; + int minlight; // rad - minlight +} dlightinfo_t; + +static dlightinfo_t dlightlist[MAX_DLIGHTS]; +static int numdlights; + +/* +=============== +R_BuildDLightList +=============== +*/ +void R_BuildDLightList (msurface_t *surf) +{ + int lnum; + float dist; + vec3_t impact; + int i; + int smax, tmax; + mtexinfo_t *tex; + int irad, iminlight; + int local[2]; + int tdmin, sdmin, distmin; + dlightinfo_t *light; + + numdlights = 0; + + smax = (surf->extents[0]>>4)+1; + tmax = (surf->extents[1]>>4)+1; + tex = surf->texinfo; + + for (lnum=0 ; lnumdlightbits & (1<plane->normal) - + surf->plane->dist; + irad = (cl_dlights[lnum].radius - fabs(dist)) * 256; + iminlight = cl_dlights[lnum].minlight * 256; + if (irad < iminlight) + continue; + + iminlight = irad - iminlight; + + for (i=0 ; i<3 ; i++) { + impact[i] = cl_dlights[lnum].origin[i] - + surf->plane->normal[i]*dist; + } + + local[0] = DotProduct (impact, tex->vecs[0]) + + tex->vecs[0][3] - surf->texturemins[0]; + local[1] = DotProduct (impact, tex->vecs[1]) + + tex->vecs[1][3] - surf->texturemins[1]; + + // check if this dlight will touch the surface + if (local[1] > 0) { + tdmin = local[1] - (tmax<<4); + if (tdmin < 0) + tdmin = 0; + } else + tdmin = -local[1]; + + if (local[0] > 0) { + sdmin = local[0] - (smax<<4); + if (sdmin < 0) + sdmin = 0; + } else + sdmin = -local[0]; + + if (sdmin > tdmin) + distmin = (sdmin<<8) + (tdmin<<7); + else + distmin = (tdmin<<8) + (sdmin<<7); + + if (distmin < iminlight) + { + // save dlight info + light = &dlightlist[numdlights]; + light->minlight = iminlight; + light->rad = irad; + light->local[0] = local[0]; + light->local[1] = local[1]; + numdlights++; + } + } +} + +/* +=============== +R_AddDynamicLights + +NOTE: R_BuildDLightList must be called first! +=============== +*/ +void R_AddDynamicLights (msurface_t *surf) +{ + int i; + int smax, tmax; + int s, t; + int sd, td; + int _sd, _td; + int local[2]; + int irad, idist, iminlight; + dlightinfo_t *light; + unsigned *dest; + + smax = (surf->extents[0]>>4)+1; + tmax = (surf->extents[1]>>4)+1; + + for (i=0,light=dlightlist ; irad; + iminlight = light->minlight; + local[0] = light->local[0]; +// local[1] = light->local[1]; + + _td = light->local[1]; + dest = blocklights; + for (t = 0 ; t td) + idist = (sd<<8) + (td<<7); + else + idist = (td<<8) + (sd<<7); + if (idist < iminlight) + *dest += irad - idist; + dest++; + } + } + } +} + + +/* +=============== +R_BuildLightMap + +Combine and scale multiple lightmaps into the 8.8 format in blocklights +=============== +*/ +void R_BuildLightMap (msurface_t *surf, byte *dest, int stride) +{ + int smax, tmax; + int t; + int i, j, size; + byte *lightmap; + unsigned scale; + int maps; + unsigned *bl; + + surf->cached_dlight = !!numdlights; + + smax = (surf->extents[0]>>4)+1; + tmax = (surf->extents[1]>>4)+1; + size = smax*tmax; + lightmap = surf->samples; + +// set to full bright if no light data + if (/* r_fullbright.value || */ !cl.worldmodel->lightdata) + { + for (i=0 ; istyles[maps] != 255 ; + maps++) + { + scale = d_lightstylevalue[surf->styles[maps]]; + surf->cached_light[maps] = scale; // 8.8 fraction + for (i=0 ; i>= 7; + t = (t >> 8) + (t >> 9); + if (t > 255) + t = 255; + dest[3] = 255-t; + dest += 4; + } + } + break; + case GL_ALPHA: + case GL_LUMINANCE: + case GL_INTENSITY: + bl = blocklights; + for (i=0 ; i>= 7; + t = (t >> 8) + (t >> 9); + if (t > 255) + t = 255; + dest[j] = 255-t; + } + } + break; + default: + Sys_Error ("Bad lightmap format"); + } +} + + +/* +=============== +R_TextureAnimation + +Returns the proper texture for a given time and base texture +=============== +*/ +texture_t *R_TextureAnimation (texture_t *base) +{ + int relative; + int count; + + if (currententity->frame) + { + if (base->alternate_anims) + base = base->alternate_anims; + } + + if (!base->anim_total) + return base; + + relative = (int)(cl.time*10) % base->anim_total; + + count = 0; + while (base->anim_min > relative || base->anim_max <= relative) + { + base = base->anim_next; + if (!base) + Sys_Error ("R_TextureAnimation: broken cycle"); + if (++count > 100) + Sys_Error ("R_TextureAnimation: infinite cycle"); + } + + return base; +} + + +/* +============================================================= + + BRUSH MODELS + +============================================================= +*/ + + +extern int solidskytexture; +extern int alphaskytexture; +extern float speedscale; // for top sky and bottom sky + +#ifdef _WIN32 +lpMTexFUNC qglMultiTexCoord2f = NULL; +lpSelTexFUNC qglActiveTexture = NULL; +#endif + +qboolean mtexenabled = false; + +void GL_SelectTexture (GLenum target); + +void GL_DisableMultitexture (void) +{ + if (mtexenabled) { + glDisable (GL_TEXTURE_2D); + GL_SelectTexture (TEXTURE0_ARB); + mtexenabled = false; + } +} + +void GL_EnableMultitexture (void) +{ + if (gl_mtexable) { + GL_SelectTexture (TEXTURE1_ARB); + glEnable (GL_TEXTURE_2D); + mtexenabled = true; + } +} + +#ifndef _WIN32 +void DrawGLWaterPoly (glpoly_t *p); +void DrawGLWaterPolyLightmap (glpoly_t *p); +/* +================ +R_DrawSequentialPoly + +Systems that have fast state and texture changes can +just do everything as it passes with no need to sort +================ +*/ + +//TODO: make this work! +void R_DrawSequentialPoly (msurface_t *s) +{ + glpoly_t *p; + float *v; + int i; + texture_t *t; + + // + // normal lightmaped poly + // +#if 0 + if (!(s->flags & (SURF_DRAWSKY|SURF_DRAWTURB)) + { + p = s->polys; + + t = R_TextureAnimation (s->texinfo->texture); + GL_Bind (t->gl_texturenum); + glBegin (GL_POLYGON); + v = p->verts[0]; + for (i=0 ; inumverts ; i++, v+= VERTEXSIZE) + { + glTexCoord2f (v[3], v[4]); + glVertex3fv (v); + } + glEnd (); + + GL_Bind (lightmap_textures + s->lightmaptexturenum); + glEnable (GL_BLEND); + glBegin (GL_POLYGON); + v = p->verts[0]; + for (i=0 ; inumverts ; i++, v+= VERTEXSIZE) + { + glTexCoord2f (v[5], v[6]); + glVertex3fv (v); + } + glEnd (); + + glDisable (GL_BLEND); + + return; + } +#endif + // + // subdivided water surface warp + // + if (s->flags & SURF_DRAWTURB) + { + GL_Bind (s->texinfo->texture->gl_texturenum); + EmitWaterPolys (s); + return; + } + + // subdivided sky warp + // + if (s->flags & SURF_DRAWSKY) + { + GL_Bind (solidskytexture); + speedscale = realtime*8; + speedscale -= (int)speedscale; + EmitSkyPolys (s); + glEnable (GL_BLEND); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + GL_Bind (alphaskytexture); + speedscale = realtime*16; + speedscale -= (int)speedscale; + EmitSkyPolys (s); + if (gl_lightmap_format == GL_LUMINANCE) + glBlendFunc (GL_ZERO, GL_ONE_MINUS_SRC_COLOR); + glDisable (GL_BLEND); + } + // + // subdivided sky warp + // + + p = s->polys; + + t = R_TextureAnimation (s->texinfo->texture); + GL_Bind (t->gl_texturenum); + DrawGLWaterPoly (p); + + GL_Bind (lightmap_textures + s->lightmaptexturenum); + glEnable (GL_BLEND); + DrawGLWaterPolyLightmap (p); + glDisable (GL_BLEND); + +#if 0 + if (s->flags & SURF_DRAWSKY) + { + GL_Bind (solidskytexture); + speedscale = realtime*8; + speedscale -= (int)speedscale; + + EmitSkyPolys (s); + + glEnable (GL_BLEND); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + GL_Bind (alphaskytexture); + speedscale = realtime*16; + speedscale -= (int)speedscale; + EmitSkyPolys (s); + if (gl_lightmap_format == GL_LUMINANCE) + glBlendFunc (GL_ZERO, GL_ONE_MINUS_SRC_COLOR); + + glDisable (GL_BLEND); + } +#endif +} + +/* +================ +DrawGLWaterPoly + +Warp the vertex coordinates +================ +*/ +void DrawGLWaterPoly (glpoly_t *p) +{ + int i; + float *v; + vec3_t nv; + + GL_DisableMultitexture(); + + glBegin (GL_TRIANGLE_FAN); + v = p->verts[0]; + for (i=0 ; inumverts ; i++, v+= VERTEXSIZE) + { + glTexCoord2f (v[3], v[4]); + + nv[0] = v[0] + 8*sin(v[1]*0.05+realtime)*sin(v[2]*0.05+realtime); + nv[1] = v[1] + 8*sin(v[0]*0.05+realtime)*sin(v[2]*0.05+realtime); + nv[2] = v[2]; + + glVertex3fv (nv); + } + glEnd (); +} + +void DrawGLWaterPolyLightmap (glpoly_t *p) +{ + int i; + float *v; + vec3_t nv; + + GL_DisableMultitexture(); + + glBegin (GL_TRIANGLE_FAN); + v = p->verts[0]; + for (i=0 ; inumverts ; i++, v+= VERTEXSIZE) + { + glTexCoord2f (v[5], v[6]); + + nv[0] = v[0] + 8*sin(v[1]*0.05+realtime)*sin(v[2]*0.05+realtime); + nv[1] = v[1] + 8*sin(v[0]*0.05+realtime)*sin(v[2]*0.05+realtime); + nv[2] = v[2]; + + glVertex3fv (nv); + } + glEnd (); +} + +#else +/* +================ +R_DrawSequentialPoly + +Systems that have fast state and texture changes can +just do everything as it passes with no need to sort +================ +*/ +void R_DrawSequentialPoly (msurface_t *s) +{ + glpoly_t *p; + float *v; + int i; + texture_t *t; + glRect_t *theRect; + + // + // normal lightmaped poly + // + if (!(s->flags & (SURF_DRAWSKY|SURF_DRAWTURB))) + { + R_RenderDynamicLightmaps (s); + if (gl_mtexable) { + p = s->polys; + + t = R_TextureAnimation (s->texinfo->texture); + // Binds world to texture env 0 + GL_SelectTexture(TEXTURE0_ARB); + GL_Bind (t->gl_texturenum); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + // Binds lightmap to texenv 1 + GL_EnableMultitexture(); // Same as SelectTexture (TEXTURE1) + GL_Bind (lightmap_textures + s->lightmaptexturenum); + i = s->lightmaptexturenum; + if (lightmap_modified[i]) + { + lightmap_modified[i] = false; + theRect = &lightmap_rectchange[i]; + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, theRect->t, + BLOCK_WIDTH, theRect->h, gl_lightmap_format, GL_UNSIGNED_BYTE, + lightmaps+(i* BLOCK_HEIGHT + theRect->t) *BLOCK_WIDTH*lightmap_bytes); + theRect->l = BLOCK_WIDTH; + theRect->t = BLOCK_HEIGHT; + theRect->h = 0; + theRect->w = 0; + } + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND); + glBegin(GL_POLYGON); + v = p->verts[0]; + for (i=0 ; inumverts ; i++, v+= VERTEXSIZE) + { + qglMultiTexCoord2f (TEXTURE0_ARB, v[3], v[4]); + qglMultiTexCoord2f (TEXTURE1_ARB, v[5], v[6]); + glVertex3fv (v); + } + glEnd (); + } else { + p = s->polys; + + t = R_TextureAnimation (s->texinfo->texture); + GL_Bind (t->gl_texturenum); + glBegin (GL_POLYGON); + v = p->verts[0]; + for (i=0 ; inumverts ; i++, v+= VERTEXSIZE) + { + glTexCoord2f (v[3], v[4]); + glVertex3fv (v); + } + glEnd (); + + GL_Bind (lightmap_textures + s->lightmaptexturenum); + glEnable (GL_BLEND); + glBegin (GL_POLYGON); + v = p->verts[0]; + for (i=0 ; inumverts ; i++, v+= VERTEXSIZE) + { + glTexCoord2f (v[5], v[6]); + glVertex3fv (v); + } + glEnd (); + + glDisable (GL_BLEND); + } + + if (t->fb_texturenum) { + s->polys->fb_chain = fullbright_polys[t->fb_texturenum]; + fullbright_polys[t->fb_texturenum] = s->polys; + drawfullbrights = true; + } + + return; + } + + // + // subdivided water surface warp + // + if (s->flags & SURF_DRAWTURB) + { + GL_DisableMultitexture(); + GL_Bind (s->texinfo->texture->gl_texturenum); + EmitWaterPolys (s); + return; + } + + // + // subdivided sky warp + // + if (s->flags & SURF_DRAWSKY) + { + GL_DisableMultitexture(); + GL_Bind (solidskytexture); + speedscale = realtime*8; + speedscale -= (int)speedscale & ~127; + + EmitSkyPolys (s); + + glEnable (GL_BLEND); + GL_Bind (alphaskytexture); + speedscale = realtime*16; + speedscale -= (int)speedscale & ~127; + EmitSkyPolys (s); + + glDisable (GL_BLEND); + return; + } +} +#endif + + +/* +================ +DrawGLPoly +================ +*/ +void DrawGLPoly (glpoly_t *p) +{ + int i; + float *v; + + glBegin (GL_POLYGON); + v = p->verts[0]; + for (i=0 ; inumverts ; i++, v+= VERTEXSIZE) + { + glTexCoord2f (v[3], v[4]); + glVertex3fv (v); + } + glEnd (); +} + + +/* +================ +R_BlendLightmaps +================ +*/ +void R_BlendLightmaps (void) +{ + int i, j; + glpoly_t *p; + float *v; + glRect_t *theRect; + +#if 0 + if (r_fullbright.value) + return; +#endif + if (!gl_texsort.value) + return; + + glDepthMask (0); // don't bother writing Z + + if (gl_lightmap_format == GL_LUMINANCE) + glBlendFunc (GL_ZERO, GL_ONE_MINUS_SRC_COLOR); + else if (gl_lightmap_format == GL_INTENSITY) + { + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glColor4f (0,0,0,1); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + + if (!r_lightmap.value) + { + glEnable (GL_BLEND); + } + + for (i=0 ; il = 0; +// theRect->t = 0; +// theRect->w = BLOCK_WIDTH; +// theRect->h = BLOCK_HEIGHT; +// glTexImage2D (GL_TEXTURE_2D, 0, lightmap_bytes +// , BLOCK_WIDTH, BLOCK_HEIGHT, 0, +// gl_lightmap_format, GL_UNSIGNED_BYTE, lightmaps+i*BLOCK_WIDTH*BLOCK_HEIGHT*lightmap_bytes); +// glTexImage2D (GL_TEXTURE_2D, 0, lightmap_bytes +// , BLOCK_WIDTH, theRect->h, 0, +// gl_lightmap_format, GL_UNSIGNED_BYTE, lightmaps+(i*BLOCK_HEIGHT+theRect->t)*BLOCK_WIDTH*lightmap_bytes); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, theRect->t, + BLOCK_WIDTH, theRect->h, gl_lightmap_format, GL_UNSIGNED_BYTE, + lightmaps+(i* BLOCK_HEIGHT + theRect->t) *BLOCK_WIDTH*lightmap_bytes); + theRect->l = BLOCK_WIDTH; + theRect->t = BLOCK_HEIGHT; + theRect->h = 0; + theRect->w = 0; + } + for ( ; p ; p=p->chain) + { + glBegin (GL_POLYGON); + v = p->verts[0]; + for (j=0 ; jnumverts ; j++, v+= VERTEXSIZE) + { + glTexCoord2f (v[5], v[6]); + glVertex3fv (v); + } + glEnd (); + } + } + + glDisable (GL_BLEND); + if (gl_lightmap_format == GL_LUMINANCE) + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + else if (gl_lightmap_format == GL_INTENSITY) + { + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glColor4f (1,1,1,1); + } + + glDepthMask (1); // back to normal Z buffering +} + +/* +================ +R_RenderBrushPoly +================ +*/ +void R_RenderBrushPoly (msurface_t *fa) +{ + texture_t *t; + byte *base; + int maps; + glRect_t *theRect; + int smax, tmax; + qboolean lightstyle_modified = false; + + c_brush_polys++; + + if (fa->flags & SURF_DRAWSKY) + { // warp texture, no lightmaps + EmitBothSkyLayers (fa); + return; + } + + t = R_TextureAnimation (fa->texinfo->texture); + GL_Bind (t->gl_texturenum); + + if (fa->flags & SURF_DRAWTURB) + { // warp texture, no lightmaps + EmitWaterPolys (fa); + return; + } + + DrawGLPoly (fa->polys); + + // add the poly to the proper lightmap chain + fa->polys->chain = lightmap_polys[fa->lightmaptexturenum]; + lightmap_polys[fa->lightmaptexturenum] = fa->polys; + + if (t->fb_texturenum) { + fa->polys->fb_chain = fullbright_polys[t->fb_texturenum]; + fullbright_polys[t->fb_texturenum] = fa->polys; + drawfullbrights = true; + } + + if (!r_dynamic.value) + return; + + // check for lightmap modification + for (maps=0 ; mapsstyles[maps] != 255 ; maps++) + if (d_lightstylevalue[fa->styles[maps]] != fa->cached_light[maps]) { + lightstyle_modified = true; + break; + } + + if (fa->dlightframe == r_framecount) + R_BuildDLightList (fa); + else + numdlights = 0; + + if (numdlights == 0 && !fa->cached_dlight && !lightstyle_modified) + return; + + lightmap_modified[fa->lightmaptexturenum] = true; + theRect = &lightmap_rectchange[fa->lightmaptexturenum]; + if (fa->light_t < theRect->t) { + if (theRect->h) + theRect->h += theRect->t - fa->light_t; + theRect->t = fa->light_t; + } + if (fa->light_s < theRect->l) { + if (theRect->w) + theRect->w += theRect->l - fa->light_s; + theRect->l = fa->light_s; + } + smax = (fa->extents[0]>>4)+1; + tmax = (fa->extents[1]>>4)+1; + if ((theRect->w + theRect->l) < (fa->light_s + smax)) + theRect->w = (fa->light_s-theRect->l)+smax; + if ((theRect->h + theRect->t) < (fa->light_t + tmax)) + theRect->h = (fa->light_t-theRect->t)+tmax; + base = lightmaps + fa->lightmaptexturenum*lightmap_bytes*BLOCK_WIDTH*BLOCK_HEIGHT; + base += fa->light_t * BLOCK_WIDTH * lightmap_bytes + fa->light_s * lightmap_bytes; + R_BuildLightMap (fa, base, BLOCK_WIDTH*lightmap_bytes); +} + +/* +================ +R_RenderDynamicLightmaps +Multitexture +================ +*/ +void R_RenderDynamicLightmaps (msurface_t *fa) +{ + byte *base; + int maps; + glRect_t *theRect; + int smax, tmax; + qboolean lightstyle_modified = false; + + c_brush_polys++; + + if (fa->flags & ( SURF_DRAWSKY | SURF_DRAWTURB) ) + return; + + fa->polys->chain = lightmap_polys[fa->lightmaptexturenum]; + lightmap_polys[fa->lightmaptexturenum] = fa->polys; + + if (!r_dynamic.value) + return; + + // check for lightmap modification + for (maps=0 ; mapsstyles[maps] != 255 ; maps++) + if (d_lightstylevalue[fa->styles[maps]] != fa->cached_light[maps]) { + lightstyle_modified = true; + break; + } + + if (fa->dlightframe == r_framecount) + R_BuildDLightList (fa); + else + numdlights = 0; + + if (numdlights == 0 && !fa->cached_dlight && !lightstyle_modified) + return; + + lightmap_modified[fa->lightmaptexturenum] = true; + theRect = &lightmap_rectchange[fa->lightmaptexturenum]; + if (fa->light_t < theRect->t) { + if (theRect->h) + theRect->h += theRect->t - fa->light_t; + theRect->t = fa->light_t; + } + if (fa->light_s < theRect->l) { + if (theRect->w) + theRect->w += theRect->l - fa->light_s; + theRect->l = fa->light_s; + } + smax = (fa->extents[0]>>4)+1; + tmax = (fa->extents[1]>>4)+1; + if ((theRect->w + theRect->l) < (fa->light_s + smax)) + theRect->w = (fa->light_s-theRect->l)+smax; + if ((theRect->h + theRect->t) < (fa->light_t + tmax)) + theRect->h = (fa->light_t-theRect->t)+tmax; + base = lightmaps + fa->lightmaptexturenum*lightmap_bytes*BLOCK_WIDTH*BLOCK_HEIGHT; + base += fa->light_t * BLOCK_WIDTH * lightmap_bytes + fa->light_s * lightmap_bytes; + R_BuildLightMap (fa, base, BLOCK_WIDTH*lightmap_bytes); +} + +/* +================ +R_MirrorChain +================ +*/ +void R_MirrorChain (msurface_t *s) +{ + if (mirror) + return; + mirror = true; + mirror_plane = s->plane; +} + + +#if 0 +/* +================ +R_DrawWaterSurfaces +================ +*/ +void R_DrawWaterSurfaces (void) +{ + int i; + msurface_t *s; + texture_t *t; + + if (wateralpha == 1.0) + return; + + // + // go back to the world matrix + // + glLoadMatrixf (r_world_matrix); + + glEnable (GL_BLEND); + glColor4f (1,1,1,wateralpha); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + for (i=0 ; inumtextures ; i++) + { + t = cl.worldmodel->textures[i]; + if (!t) + continue; + s = t->texturechain; + if (!s) + continue; + if ( !(s->flags & SURF_DRAWTURB) ) + continue; + + // set modulate mode explicitly + GL_Bind (t->gl_texturenum); + + for ( ; s ; s=s->texturechain) + R_RenderBrushPoly (s); + + t->texturechain = NULL; + } + + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + + glColor4f (1,1,1,1); + glDisable (GL_BLEND); +} +#else +/* +================ +R_DrawWaterSurfaces +================ +*/ +void R_DrawWaterSurfaces (void) +{ + int i; + msurface_t *s; + texture_t *t; + + if (wateralpha == 1.0 && gl_texsort.value) + return; + + // + // go back to the world matrix + // + + glLoadMatrixf (r_world_matrix); + + if (wateralpha < 1.0) { + glEnable (GL_BLEND); + glColor4f (1, 1, 1, wateralpha); + glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + } + + if (!gl_texsort.value) { + if (!waterchain) + return; + + for (s=waterchain ; s ; s=s->texturechain) { + GL_Bind (s->texinfo->texture->gl_texturenum); + EmitWaterPolys (s); + } + + waterchain = NULL; + } else { + + for (i=0 ; inumtextures ; i++) + { + t = cl.worldmodel->textures[i]; + if (!t) + continue; + s = t->texturechain; + if (!s) + continue; + if ( !(s->flags & SURF_DRAWTURB ) ) + continue; + + // set modulate mode explicitly + + GL_Bind (t->gl_texturenum); + + for ( ; s ; s=s->texturechain) + EmitWaterPolys (s); + + t->texturechain = NULL; + } + + } + + if (wateralpha < 1.0) { + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + + glColor4f (1,1,1,1); + glDisable (GL_BLEND); + } + +} + +#endif + +/* +================ +DrawTextureChains +================ +*/ +void DrawTextureChains (void) +{ + int i; + msurface_t *s; + texture_t *t; + + if (!gl_texsort.value) { + GL_DisableMultitexture(); + + if (skychain) { + R_DrawSkyChain(skychain); + skychain = NULL; + } + + return; + } + + for (i=0 ; inumtextures ; i++) + { + t = cl.worldmodel->textures[i]; + if (!t) + continue; + s = t->texturechain; + if (!s) + continue; + if (i == skytexturenum) + R_DrawSkyChain (s); + else if (i == mirrortexturenum && r_mirroralpha.value != 1.0) + { + R_MirrorChain (s); + continue; + } + else + { + if ((s->flags & SURF_DRAWTURB) && wateralpha != 1.0) + continue; // draw translucent water later + for ( ; s ; s=s->texturechain) + R_RenderBrushPoly (s); + } + + t->texturechain = NULL; + } +} + +/* +================= +R_DrawBrushModel +================= +*/ +void R_DrawBrushModel (entity_t *e) +{ + int i; + int k; + vec3_t mins, maxs; + msurface_t *psurf; + float dot; + mplane_t *pplane; + model_t *clmodel; + qboolean rotated; + + currententity = e; + currenttexture = -1; + + clmodel = e->model; + + if (e->angles[0] || e->angles[1] || e->angles[2]) + { + rotated = true; + for (i=0 ; i<3 ; i++) + { + mins[i] = e->origin[i] - clmodel->radius; + maxs[i] = e->origin[i] + clmodel->radius; + } + } + else + { + rotated = false; + VectorAdd (e->origin, clmodel->mins, mins); + VectorAdd (e->origin, clmodel->maxs, maxs); + } + + if (R_CullBox (mins, maxs)) + return; + + glColor3f (1,1,1); + memset (lightmap_polys, 0, sizeof(lightmap_polys)); +// if (gl_fb_bmodels.value) +// memset (fullbright_polys, 0, sizeof(fullbright_polys)); + + VectorSubtract (r_refdef.vieworg, e->origin, modelorg); + if (rotated) + { + vec3_t temp; + vec3_t forward, right, up; + + VectorCopy (modelorg, temp); + AngleVectors (e->angles, forward, right, up); + modelorg[0] = DotProduct (temp, forward); + modelorg[1] = -DotProduct (temp, right); + modelorg[2] = DotProduct (temp, up); + } + + psurf = &clmodel->surfaces[clmodel->firstmodelsurface]; + +// calculate dynamic lighting for bmodel if it's not an +// instanced model + if (clmodel->firstmodelsurface != 0 && !gl_flashblend.value) + { + for (k=0 ; knodes + clmodel->hulls[0].firstclipnode); + } + } + + glPushMatrix (); +e->angles[0] = -e->angles[0]; // stupid quake bug + R_RotateForEntity (e); +e->angles[0] = -e->angles[0]; // stupid quake bug + + // + // draw texture + // + for (i=0 ; inummodelsurfaces ; i++, psurf++) + { + // find which side of the node we are on + pplane = psurf->plane; + + 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))) + { + if (gl_texsort.value) + R_RenderBrushPoly (psurf); + else + R_DrawSequentialPoly (psurf); + } + } + + R_BlendLightmaps (); + + R_RenderFullbrights (); + + glPopMatrix (); +} + +/* +============================================================= + + WORLD MODEL + +============================================================= +*/ + +/* +================ +R_RecursiveWorldNode +================ +*/ +void R_RecursiveWorldNode (mnode_t *node) +{ + int c, side; + mplane_t *plane; + msurface_t *surf, **mark; + mleaf_t *pleaf; + double dot; + + if (node->contents == CONTENTS_SOLID) + return; // solid + + if (node->visframe != r_visframecount) + return; + if (R_CullBox (node->minmaxs, node->minmaxs+3)) + return; + +// if a leaf node, draw stuff + if (node->contents < 0) + { + pleaf = (mleaf_t *)node; + + mark = pleaf->firstmarksurface; + c = pleaf->nummarksurfaces; + + if (c) + { + do + { + (*mark)->visframe = r_framecount; + mark++; + } while (--c); + } + + // deal with model fragments in this leaf + if (pleaf->efrags) + R_StoreEfrags (&pleaf->efrags); + + return; + } + +// 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]); + +// draw stuff + c = node->numsurfaces; + + if (c) + { + surf = cl.worldmodel->surfaces + node->firstsurface; + + if (dot < 0 -BACKFACE_EPSILON) + side = SURF_PLANEBACK; + else if (dot > BACKFACE_EPSILON) + side = 0; + { + for ( ; c ; c--, surf++) + { + if (surf->visframe != r_framecount) + continue; + + if ((dot < 0) ^ !!(surf->flags & SURF_PLANEBACK)) + continue; // wrong side + + // if sorting by texture, just store it out + if (gl_texsort.value) + { + if (!mirror + || surf->texinfo->texture != cl.worldmodel->textures[mirrortexturenum]) + { + surf->texturechain = surf->texinfo->texture->texturechain; + surf->texinfo->texture->texturechain = surf; + } + } else if (surf->flags & SURF_DRAWSKY) { + surf->texturechain = skychain; + skychain = surf; + } else if (surf->flags & SURF_DRAWTURB) { + surf->texturechain = waterchain; + waterchain = surf; + } else + R_DrawSequentialPoly (surf); + + } + } + + } + +// recurse down the back side + R_RecursiveWorldNode (node->children[!side]); +} + + + +/* +============= +R_DrawWorld +============= +*/ +void R_DrawWorld (void) +{ + entity_t ent; + + memset (&ent, 0, sizeof(ent)); + ent.model = cl.worldmodel; + + VectorCopy (r_refdef.vieworg, modelorg); + + currententity = &ent; + currenttexture = -1; + + glColor3f (1,1,1); + memset (lightmap_polys, 0, sizeof(lightmap_polys)); + if (gl_fb_bmodels.value) + memset (fullbright_polys, 0, sizeof(fullbright_polys)); + +#ifdef QUAKE2 + R_ClearSkyBox (); +#endif + + R_RecursiveWorldNode (cl.worldmodel->nodes); + + DrawTextureChains (); + + R_BlendLightmaps (); + + R_RenderFullbrights (); + +#ifdef QUAKE2 + R_DrawSkyBox (); +#endif +} + + +/* +=============== +R_MarkLeaves +=============== +*/ +void R_MarkLeaves (void) +{ + byte *vis; + mnode_t *node; + int i; + byte solid[MAX_MAP_LEAFS/8]; + + if (!r_novis.value && r_oldviewleaf == r_viewleaf + && r_oldviewleaf2 == r_viewleaf2) // watervis hack + return; + + if (mirror) + return; + + r_visframecount++; + r_oldviewleaf = r_viewleaf; + + if (r_novis.value) + { + vis = solid; + memset (solid, 0xff, (cl.worldmodel->numleafs+7)>>3); + } + else + { + vis = Mod_LeafPVS (r_viewleaf, cl.worldmodel); + + if (r_viewleaf2) { + int i, count; + unsigned *src, *dest; + + // merge visibility data for two leafs + count = (cl.worldmodel->numleafs+7)>>3; + memcpy (solid, vis, count); + src = (unsigned *) Mod_LeafPVS (r_viewleaf2, cl.worldmodel); + dest = (unsigned *) solid; + count = (count + 3)>>2; + for (i=0 ; inumleafs ; i++) + { + if (vis[i>>3] & (1<<(i&7))) + { + node = (mnode_t *)&cl.worldmodel->leafs[i+1]; + do + { + if (node->visframe == r_visframecount) + break; + node->visframe = r_visframecount; + node = node->parent; + } while (node); + } + } +} + + + +/* +============================================================================= + + LIGHTMAP ALLOCATION + +============================================================================= +*/ + +// returns a texture number and the position inside it +int AllocBlock (int w, int h, int *x, int *y) +{ + int i, j; + int best, best2; + int texnum; + + for (texnum=0 ; texnum= best) + break; + if (allocated[texnum][i+j] > best2) + best2 = allocated[texnum][i+j]; + } + if (j == w) + { // this is a valid spot + *x = i; + *y = best = best2; + } + } + + if (best + h > BLOCK_HEIGHT) + continue; + + for (i=0 ; iedges; + lnumverts = fa->numedges; + vertpage = 0; + + // + // draw texture + // + poly = Hunk_Alloc (sizeof(glpoly_t) + (lnumverts-4) * VERTEXSIZE*sizeof(float)); + poly->next = fa->polys; + poly->flags = fa->flags; + fa->polys = poly; + poly->numverts = lnumverts; + + for (i=0 ; isurfedges[fa->firstedge + i]; + + if (lindex > 0) + { + r_pedge = &pedges[lindex]; + vec = r_pcurrentvertbase[r_pedge->v[0]].position; + } + else + { + r_pedge = &pedges[-lindex]; + vec = r_pcurrentvertbase[r_pedge->v[1]].position; + } + s = DotProduct (vec, fa->texinfo->vecs[0]) + fa->texinfo->vecs[0][3]; + s /= fa->texinfo->texture->width; + + t = DotProduct (vec, fa->texinfo->vecs[1]) + fa->texinfo->vecs[1][3]; + t /= fa->texinfo->texture->height; + + VectorCopy (vec, poly->verts[i]); + poly->verts[i][3] = s; + poly->verts[i][4] = t; + + // + // lightmap texture coordinates + // + s = DotProduct (vec, fa->texinfo->vecs[0]) + fa->texinfo->vecs[0][3]; + s -= fa->texturemins[0]; + s += fa->light_s*16; + s += 8; + s /= BLOCK_WIDTH*16; //fa->texinfo->texture->width; + + t = DotProduct (vec, fa->texinfo->vecs[1]) + fa->texinfo->vecs[1][3]; + t -= fa->texturemins[1]; + t += fa->light_t*16; + t += 8; + t /= BLOCK_HEIGHT*16; //fa->texinfo->texture->height; + + poly->verts[i][5] = s; + poly->verts[i][6] = t; + } + + // + // remove co-linear points - Ed + // + if (!gl_keeptjunctions.value) + { + for (i=0 ; iverts[(i + lnumverts - 1) % lnumverts]; + this = poly->verts[i]; + next = poly->verts[(i + 1) % lnumverts]; + + VectorSubtract (this, prev, v1); + VectorNormalize (v1); + VectorSubtract (next, prev, v2 ); + VectorNormalize (v2); + + // skip co-linear points + #define COLINEAR_EPSILON 0.001 + if ((fabs(v1[0] - v2[0]) <= COLINEAR_EPSILON) && + (fabs(v1[1] - v2[1]) <= COLINEAR_EPSILON) && + (fabs(v1[2] - v2[2]) <= COLINEAR_EPSILON)) + { + int j; + for (j = i + 1; j < lnumverts; ++j) + { + int k; + for (k = 0; k < VERTEXSIZE; ++k) + poly->verts[j - 1][k] = poly->verts[j][k]; + } + lnumverts--; + nColinElim++; + // retry next vertex next time, which is now current vertex + i--; + } + } + } + poly->numverts = lnumverts; + +} + +/* +======================== +GL_CreateSurfaceLightmap +======================== +*/ +void GL_CreateSurfaceLightmap (msurface_t *surf) +{ + int smax, tmax; + byte *base; + + if (surf->flags & (SURF_DRAWSKY|SURF_DRAWTURB)) + return; + + smax = (surf->extents[0]>>4)+1; + tmax = (surf->extents[1]>>4)+1; + + surf->lightmaptexturenum = AllocBlock (smax, tmax, &surf->light_s, &surf->light_t); + base = lightmaps + surf->lightmaptexturenum*lightmap_bytes*BLOCK_WIDTH*BLOCK_HEIGHT; + base += (surf->light_t * BLOCK_WIDTH + surf->light_s) * lightmap_bytes; + numdlights = 0; + R_BuildLightMap (surf, base, BLOCK_WIDTH*lightmap_bytes); +} + + +/* +================== +GL_BuildLightmaps + +Builds the lightmap texture +with all the surfaces from all brush models +================== +*/ +void GL_BuildLightmaps (void) +{ + int i, j; + model_t *m; + + memset (allocated, 0, sizeof(allocated)); + + r_framecount = 1; // no dlightcache + + if (!lightmap_textures) + { + lightmap_textures = texture_extension_number; + texture_extension_number += MAX_LIGHTMAPS; + } + + gl_lightmap_format = GL_LUMINANCE; + if (COM_CheckParm ("-lm_1")) + gl_lightmap_format = GL_LUMINANCE; + if (COM_CheckParm ("-lm_a")) + gl_lightmap_format = GL_ALPHA; + if (COM_CheckParm ("-lm_i")) + gl_lightmap_format = GL_INTENSITY; + if (COM_CheckParm ("-lm_2")) + gl_lightmap_format = GL_RGBA4; + if (COM_CheckParm ("-lm_4")) + gl_lightmap_format = GL_RGBA; + + switch (gl_lightmap_format) + { + case GL_RGBA: + lightmap_bytes = 4; + break; + case GL_RGBA4: + lightmap_bytes = 2; + break; + case GL_LUMINANCE: + case GL_INTENSITY: + case GL_ALPHA: + lightmap_bytes = 1; + break; + } + + for (j=1 ; jname[0] == '*') + continue; + r_pcurrentvertbase = m->vertexes; + currentmodel = m; + for (i=0 ; inumsurfaces ; i++) + { + GL_CreateSurfaceLightmap (m->surfaces + i); + if ( m->surfaces[i].flags & SURF_DRAWTURB ) + continue; +#ifndef QUAKE2 + if ( m->surfaces[i].flags & SURF_DRAWSKY ) + continue; +#endif + BuildSurfaceDisplayList (m->surfaces + i); + } + } + + if (!gl_texsort.value) + GL_SelectTexture (TEXTURE1_ARB); + + // + // upload all lightmaps that were filled + // + for (i=0 ; i - -/* - -background clear -rendering -turtle/net/ram icons -sbar -centerprint / slow centerprint -notify lines -intermission / finale overlay -loading plaque -console -menu - -required background clears -required update regions - - -syncronous draw mode or async -One off screen buffer, with updates either copied or xblited -Need to double buffer? - - -async draw will require the refresh area to be cleared, because it will be -xblited, but sync draw can just ignore it. - -sync -draw - -CenterPrint () -SlowPrint () -Screen_Update (); -Con_Printf (); - -net -turn off messages option - -the refresh is always rendered, unless the console is full screen - - -console is: - notify lines - half - full - - -*/ - - -int glx, gly, glwidth, glheight; - -// only the refresh window will be updated unless these variables are flagged -int scr_copytop; -int scr_copyeverything; - -float scr_con_current; -float scr_conlines; // lines of console to display - -float oldscreensize, oldfov; -cvar_t scr_viewsize = {"viewsize","100",CVAR_ARCHIVE}; -cvar_t scr_fov = {"fov","90",CVAR_ARCHIVE}; // 10 - 170 -cvar_t scr_consize = {"scr_consize","0.5"}; -cvar_t scr_conspeed = {"scr_conspeed","1000"}; -cvar_t scr_centertime = {"scr_centertime","2"}; -cvar_t scr_showram = {"showram","1"}; -cvar_t scr_showturtle = {"showturtle","0"}; -cvar_t scr_showpause = {"showpause","1"}; -cvar_t scr_printspeed = {"scr_printspeed","8"}; -cvar_t scr_allowsnap = {"scr_allowsnap", "1"}; -cvar_t gl_triplebuffer = {"gl_triplebuffer", "1",CVAR_ARCHIVE}; -extern cvar_t crosshair; - -// Tonik: -cvar_t scr_clock = {"cl_clock","0"}; -cvar_t scr_clock_x = {"cl_clock_x","0"}; -cvar_t scr_clock_y = {"cl_clock_y","-1"}; -cvar_t show_speed = {"show_speed","0"}; - -extern cvar_t show_fps; - -qboolean scr_initialized; // ready to draw - -qpic_t *scr_ram; -qpic_t *scr_net; -qpic_t *scr_turtle; - -int scr_fullupdate; - -int clearconsole; -int clearnotify; - -viddef_t vid; // global video state - -vrect_t scr_vrect; - -qboolean scr_disabled_for_loading; -qboolean scr_drawloading; -float scr_disabled_time; - -qboolean block_drawing; - -void SCR_ScreenShot_f (void); -void SCR_RSShot_f (void); - -/* -=============================================================================== - -CENTER PRINTING - -=============================================================================== -*/ - -char scr_centerstring[1024]; -float scr_centertime_start; // for slow victory printing -float scr_centertime_off; -int scr_center_lines; -int scr_erase_lines; -int scr_erase_center; - -/* -============== -SCR_CenterPrint - -Called for important messages that should stay in the center of the screen -for a few moments -============== -*/ -void SCR_CenterPrint (char *str) -{ - Q_strncpyz (scr_centerstring, str, sizeof(scr_centerstring)); - scr_centertime_off = scr_centertime.value; - scr_centertime_start = cl.time; - -// count the number of lines for centering - scr_center_lines = 1; - while (*str) - { - if (*str == '\n') - scr_center_lines++; - str++; - } -} - - -void SCR_DrawCenterString (void) -{ - char *start; - int l; - int j; - int x, y; - int remaining; - -// the finale prints the characters one at a time - if (cl.intermission) - remaining = scr_printspeed.value * (cl.time - scr_centertime_start); - else - remaining = 9999; - - scr_erase_center = 0; - start = scr_centerstring; - - if (scr_center_lines <= 4) - y = vid.height*0.35; - else - y = 48; - - do - { - // scan the width of the line - for (l=0 ; l<40 ; l++) - if (start[l] == '\n' || !start[l]) - break; - x = (vid.width - l*8)/2; - for (j=0 ; j scr_erase_lines) - scr_erase_lines = scr_center_lines; - - scr_centertime_off -= host_frametime; - - if (scr_centertime_off <= 0 && !cl.intermission) - return; - if (key_dest != key_game) - return; - - SCR_DrawCenterString (); -} - -//============================================================================= - -/* -==================== -CalcFov -==================== -*/ -float CalcFov (float fov_x, float width, float height) -{ - float a; - float x; - - if (fov_x < 1 || fov_x > 179) - Sys_Error ("Bad fov: %f", fov_x); - - x = width/tan(fov_x/360*M_PI); - - a = atan (height/x); - - a = a*360/M_PI; - - return a; -} - -/* -================= -SCR_CalcRefdef - -Must be called whenever vid changes -Internal use only -================= -*/ -static void SCR_CalcRefdef (void) -{ - float size; - int h; - qboolean full = false; - - - scr_fullupdate = 0; // force a background redraw - vid.recalc_refdef = 0; - -// force the status bar to redraw - Sbar_Changed (); - -//======================================== - -// bound viewsize - if (scr_viewsize.value < 30) - Cvar_Set (&scr_viewsize,"30"); - if (scr_viewsize.value > 120) - Cvar_Set (&scr_viewsize,"120"); - -// bound field of view - if (scr_fov.value < 10) - Cvar_Set (&scr_fov,"10"); - if (scr_fov.value > 170) - Cvar_Set (&scr_fov,"170"); - -// intermission is always full screen - if (cl.intermission) - size = 120; - else - size = scr_viewsize.value; - - if (size >= 120) - sb_lines = 0; // no status bar at all - else if (size >= 110) - sb_lines = 24; // no inventory - else - sb_lines = 24+16+8; - - if (scr_viewsize.value >= 100.0) { - full = true; - size = 100.0; - } else - size = scr_viewsize.value; - if (cl.intermission) - { - full = true; - size = 100.0; - sb_lines = 0; - } - size /= 100.0; - - if (!cl_sbar.value && full) - h = vid.height; - else - h = vid.height - sb_lines; - - r_refdef.vrect.width = vid.width * size; - if (r_refdef.vrect.width < 96) - { - size = 96.0 / r_refdef.vrect.width; - r_refdef.vrect.width = 96; // min for icons - } - - r_refdef.vrect.height = vid.height * size; - if (cl_sbar.value || !full) { - if (r_refdef.vrect.height > vid.height - sb_lines) - r_refdef.vrect.height = vid.height - sb_lines; - } else if (r_refdef.vrect.height > vid.height) - r_refdef.vrect.height = vid.height; - r_refdef.vrect.x = (vid.width - r_refdef.vrect.width)/2; - if (full) - r_refdef.vrect.y = 0; - else - r_refdef.vrect.y = (h - r_refdef.vrect.height)/2; - - r_refdef.fov_x = scr_fov.value; - r_refdef.fov_y = CalcFov (r_refdef.fov_x, r_refdef.vrect.width, r_refdef.vrect.height); - - scr_vrect = r_refdef.vrect; -} - - -/* -================= -SCR_SizeUp_f - -Keybinding command -================= -*/ -void SCR_SizeUp_f (void) -{ - Cvar_SetValue (&scr_viewsize,scr_viewsize.value+10); - vid.recalc_refdef = 1; -} - - -/* -================= -SCR_SizeDown_f - -Keybinding command -================= -*/ -void SCR_SizeDown_f (void) -{ - Cvar_SetValue (&scr_viewsize,scr_viewsize.value-10); - vid.recalc_refdef = 1; -} - -//============================================================================ - -/* -================== -SCR_Init -================== -*/ -void SCR_Init (void) -{ - Cvar_RegisterVariable (&scr_fov); - Cvar_RegisterVariable (&scr_viewsize); - Cvar_RegisterVariable (&scr_consize); - Cvar_RegisterVariable (&scr_conspeed); - Cvar_RegisterVariable (&scr_showram); - Cvar_RegisterVariable (&scr_showturtle); - Cvar_RegisterVariable (&scr_showpause); - Cvar_RegisterVariable (&scr_centertime); - Cvar_RegisterVariable (&scr_printspeed); - Cvar_RegisterVariable (&scr_allowsnap); - Cvar_RegisterVariable (&gl_triplebuffer); - -// Tonik: - Cvar_RegisterVariable (&scr_clock_x); - Cvar_RegisterVariable (&scr_clock_y); - Cvar_RegisterVariable (&scr_clock); - Cvar_RegisterVariable (&show_speed); -// -// register our commands -// - Cmd_AddCommand ("screenshot",SCR_ScreenShot_f); - Cmd_AddCommand ("sizeup",SCR_SizeUp_f); - Cmd_AddCommand ("sizedown",SCR_SizeDown_f); - - scr_ram = Draw_PicFromWad ("ram"); - scr_net = Draw_PicFromWad ("net"); - scr_turtle = Draw_PicFromWad ("turtle"); - - scr_initialized = true; -} - - - -/* -============== -SCR_DrawRam -============== -*/ -void SCR_DrawRam (void) -{ - if (!scr_showram.value) - return; - - if (!r_cache_thrash) - return; - - Draw_Pic (scr_vrect.x+32, scr_vrect.y, scr_ram); -} - -/* -============== -SCR_DrawTurtle -============== -*/ -void SCR_DrawTurtle (void) -{ - static int count; - - if (!scr_showturtle.value) - return; - - if (host_frametime < 0.1) - { - count = 0; - return; - } - - count++; - if (count < 3) - return; - - Draw_Pic (scr_vrect.x, scr_vrect.y, scr_turtle); -} - -/* -============== -SCR_DrawNet -============== -*/ -void SCR_DrawNet (void) -{ - if (cls.netchan.outgoing_sequence - cls.netchan.incoming_acknowledged < UPDATE_BACKUP-1) - return; - if (cls.demoplayback) - return; - - Draw_Pic (scr_vrect.x+64, scr_vrect.y, scr_net); -} - -void SCR_DrawFPS (void) -{ - extern cvar_t show_fps; - static double lastframetime; - double t; - extern int fps_count; - static lastfps; - int x, y; - char st[80]; - - if (!show_fps.value) - return; - - t = Sys_DoubleTime(); - if ((t - lastframetime) >= 1.0) { - lastfps = fps_count; - fps_count = 0; - lastframetime = t; - } - - sprintf(st, "%3d FPS", lastfps); - x = vid.width - strlen(st) * 8 - 8; - y = vid.height - sb_lines - 8; -// Draw_TileClear(x, y, strlen(st) * 8, 8); - Draw_String(x, y, st); -} - - -void SCR_DrawSpeed (void) -{ - int x, y; - char st[80]; - vec3_t vel; - float speed, vspeed; - static float maxspeed = 0; - static float display_speed = -1; - static double lastrealtime = 0; - - if (!show_speed.value) - return; - - if (lastrealtime > realtime) - { - lastrealtime = 0; - display_speed = -1; - maxspeed = 0; - } - -// VectorCopy (cl.simvel, vel); - VectorCopy (cl.frames[(cls.netchan.incoming_sequence)&UPDATE_MASK].playerstate[cl.playernum].velocity, vel); - vspeed = vel[2]; - vel[2] = 0; - speed = Length(vel); - - if (speed > maxspeed) - maxspeed = speed; - - if (display_speed >= 0) - { - sprintf(st, "%3d", (int)display_speed); - x = vid.width - strlen(st) * 8 - 8; - y = 8; - // Draw_TileClear(x, y, strlen(st) * 8, 8); - Draw_String(x, y, st); - } - - if (realtime - lastrealtime >= 0.1) - { - lastrealtime = realtime; - display_speed = maxspeed; - maxspeed = 0; - } -} - -void SCR_DrawClock (void) -{ - char str[80]; - int hours, minutes, seconds; - int tens_hours, tens_minutes, tens_seconds; - - if (!scr_clock.value || cls.demoplayback || cl.intermission) - return; - - if (scr_clock.value == 2) - { - time_t t; - struct tm *ptm; - str[0] = 0; - t = time(NULL); - if (t != -1) { - ptm = localtime (&t); - if (ptm) - strftime(str, sizeof(str)-1, "%H:%M:%S", ptm); - } - } - else - { - float time; - time = cl.time; - tens_hours = fmod (time / 36000, 10); - hours = fmod (time / 3600, 10); - tens_minutes = fmod (time / 600, 6); - minutes = fmod (time / 60, 10); - tens_seconds = fmod (time / 10, 6); - seconds = fmod (time, 10); - sprintf (str, "%i%i:%i%i:%i%i", tens_hours, hours, tens_minutes, minutes, - tens_seconds, seconds); - } - - if (scr_clock_y.value < 0) - Draw_String (8 * scr_clock_x.value, vid.height - sb_lines + 8*scr_clock_y.value, str); - else - Draw_String (8 * scr_clock_x.value, 8*scr_clock_y.value, str); -} - - -/* -============== -DrawPause -============== -*/ -void SCR_DrawPause (void) -{ - qpic_t *pic; - - if (!scr_showpause.value) // turn off for screenshots - return; - - if (!cl.paused) - return; - - pic = Draw_CachePic ("gfx/pause.lmp"); - Draw_Pic ( (vid.width - pic->width)/2, - (vid.height - 48 - pic->height)/2, pic); -} - - - -/* -============== -SCR_DrawLoading -============== -*/ -void SCR_DrawLoading (void) -{ - qpic_t *pic; - - if (!scr_drawloading) - return; - - pic = Draw_CachePic ("gfx/loading.lmp"); - Draw_Pic ( (vid.width - pic->width)/2, - (vid.height - 48 - pic->height)/2, pic); -} - - - -//============================================================================= - - -/* -================== -SCR_SetUpToDrawConsole -================== -*/ -void SCR_SetUpToDrawConsole (void) -{ - Con_CheckResize (); - - if (scr_drawloading) - return; // never a console with loading plaque - -// decide on the height of the console - if (cls.state != ca_active) - { - scr_conlines = vid.height; // full screen - scr_con_current = scr_conlines; - } - else if (key_dest == key_console) { - scr_conlines = vid.height * scr_consize.value; - if (scr_conlines < 30) - scr_conlines = 30; - if (scr_conlines > vid.height - 10) - scr_conlines = vid.height - 10; - } - else - scr_conlines = 0; // none visible - - if (scr_conlines < scr_con_current) - { - scr_con_current -= scr_conspeed.value*real_frametime*vid.height/320; - if (scr_conlines > scr_con_current) - scr_con_current = scr_conlines; - - } - else if (scr_conlines > scr_con_current) - { - scr_con_current += scr_conspeed.value*real_frametime*vid.height/320; - if (scr_conlines < scr_con_current) - scr_con_current = scr_conlines; - } - - if (clearconsole++ < vid.numpages) - { - Sbar_Changed (); - } - else if (clearnotify++ < vid.numpages) - { - } - else - con_notifylines = 0; -} - -/* -================== -SCR_DrawConsole -================== -*/ -void SCR_DrawConsole (void) -{ - if (scr_con_current) - { - scr_copyeverything = 1; - Con_DrawConsole (scr_con_current); - clearconsole = 0; - } - else - { - if (key_dest == key_game || key_dest == key_message) - Con_DrawNotify (); // only draw notify in game - } -} - - -/* -============================================================================== - - SCREEN SHOTS - -============================================================================== -*/ - -typedef struct _TargaHeader { - unsigned char id_length, colormap_type, image_type; - unsigned short colormap_index, colormap_length; - unsigned char colormap_size; - unsigned short x_origin, y_origin, width, height; - unsigned char pixel_size, attributes; -} TargaHeader; - - -/* -================== -SCR_ScreenShot_f -================== -*/ -void SCR_ScreenShot_f (void) -{ - byte *buffer; - char pcxname[MAX_OSPATH]; - char checkname[MAX_OSPATH]; - int i, c, temp; - - if (Cmd_Argc() == 2) { - Q_strncpyz (pcxname, Cmd_Argv(1), sizeof(pcxname)); - COM_ForceExtension (pcxname, ".tga"); - } - else - { - // - // find a file name to save it to - // - strcpy(pcxname,"quake00.tga"); - - for (i=0 ; i<=99 ; i++) - { - pcxname[5] = i/10 + '0'; - pcxname[6] = i%10 + '0'; - sprintf (checkname, "%s/%s", com_gamedir, pcxname); - if (Sys_FileTime(checkname) == -1) - break; // file doesn't exist - } - if (i==100) - { - Con_Printf ("SCR_ScreenShot_f: Couldn't create a PCX file\n"); - return; - } - } - - buffer = Q_Malloc (glwidth*glheight*3 + 18); - memset (buffer, 0, 18); - buffer[2] = 2; // uncompressed type - buffer[12] = glwidth&255; - buffer[13] = glwidth>>8; - buffer[14] = glheight&255; - buffer[15] = glheight>>8; - buffer[16] = 24; // pixel size - - glReadPixels (glx, gly, glwidth, glheight, GL_RGB, GL_UNSIGNED_BYTE, buffer+18 ); - - // swap rgb to bgr - c = 18+glwidth*glheight*3; - for (i=18 ; imanufacturer = 0x0a; // PCX id - pcx->version = 5; // 256 color - pcx->encoding = 1; // uncompressed - pcx->bits_per_pixel = 8; // 256 color - pcx->xmin = 0; - pcx->ymin = 0; - pcx->xmax = LittleShort((short)(width-1)); - pcx->ymax = LittleShort((short)(height-1)); - pcx->hres = LittleShort((short)width); - pcx->vres = LittleShort((short)height); - memset (pcx->palette,0,sizeof(pcx->palette)); - pcx->color_planes = 1; // chunky image - pcx->bytes_per_line = LittleShort((short)width); - pcx->palette_type = LittleShort(2); // not a grey scale - memset (pcx->filler,0,sizeof(pcx->filler)); - -// pack the image - pack = &pcx->data; - - data += rowbytes * (height - 1); - - for (i=0 ; i>4; - col = num&15; - source = draw_chars + (row<<10) + (col<<3); - - drawline = 8; - - while (drawline--) - { - for (x=0 ; x<8 ; x++) - if (source[x]) - dest[x] = source[x]; - else - dest[x] = 98; - source += 128; - dest -= width; - } - -} - -void SCR_DrawStringToSnap (const char *s, byte *buf, int x, int y, int width) -{ - byte *dest; - const unsigned char *p; - - dest = buf + ((y * width) + x); - - p = (const unsigned char *)s; - while (*p) { - SCR_DrawCharToSnap(*p++, dest, width); - dest += 8; - } -} - - -/* -================== -SCR_RSShot_f -================== -*/ -void SCR_RSShot_f (void) -{ - int x, y; - unsigned char *src, *dest; - char pcxname[80]; - unsigned char *newbuf; - int w, h; - int dx, dy, dex, dey, nx; - int r, b, g; - int count; - float fracw, frach; - char st[80]; - time_t now; - - if (CL_IsUploading()) - return; // already one pending - - if (cls.state < ca_onserver) - return; // gotta be connected - - Con_Printf ("Remote screen shot requested.\n"); - -// -// save the pcx file -// - newbuf = Q_Malloc (glheight * glwidth * 3); - - glReadPixels (glx, gly, glwidth, glheight, GL_RGB, GL_UNSIGNED_BYTE, newbuf); - - w = (vid.width < RSSHOT_WIDTH) ? glwidth : RSSHOT_WIDTH; - h = (vid.height < RSSHOT_HEIGHT) ? glheight : RSSHOT_HEIGHT; - - fracw = (float)glwidth / (float)w; - frach = (float)glheight / (float)h; - - for (y = 0; y < h; y++) { - dest = newbuf + (w*3 * y); - - for (x = 0; x < w; x++) { - r = g = b = 0; - - dx = x * fracw; - dex = (x + 1) * fracw; - if (dex == dx) dex++; // at least one - dy = y * frach; - dey = (y + 1) * frach; - if (dey == dy) dey++; // at least one - - count = 0; - for (/* */; dy < dey; dy++) { - src = newbuf + (glwidth * 3 * dy) + dx * 3; - for (nx = dx; nx < dex; nx++) { - r += *src++; - g += *src++; - b += *src++; - count++; - } - } - r /= count; - g /= count; - b /= count; - *dest++ = r; - *dest++ = b; - *dest++ = g; - } - } - - // convert to eight bit - for (y = 0; y < h; y++) { - src = newbuf + (w * 3 * y); - dest = newbuf + (w * y); - - for (x = 0; x < w; x++) { - *dest++ = MipColor(src[0], src[1], src[2]); - src += 3; - } - } - - time(&now); - strcpy(st, ctime(&now)); - st[strlen(st) - 1] = 0; - SCR_DrawStringToSnap (st, newbuf, w - strlen(st)*8, h - 1, w); - - Q_strncpyz (st, cls.servername, sizeof(st)); - SCR_DrawStringToSnap (st, newbuf, w - strlen(st)*8, h - 11, w); - - Q_strncpyz (st, name.string, sizeof(st)); - SCR_DrawStringToSnap (st, newbuf, w - strlen(st)*8, h - 21, w); - - WritePCXfile (pcxname, newbuf, w, h, w, host_basepal, true); - - free(newbuf); - - Con_Printf ("Wrote %s\n", pcxname); -} - - - - -//============================================================================= - - -//============================================================================= - -char *scr_notifystring; -qboolean scr_drawdialog; - -void SCR_DrawNotifyString (void) -{ - char *start; - int l; - int j; - int x, y; - - start = scr_notifystring; - - y = vid.height*0.35; - - do - { - // scan the width of the line - for (l=0 ; l<40 ; l++) - if (start[l] == '\n' || !start[l]) - break; - x = (vid.width - l*8)/2; - for (j=0 ; j 0) { - // left - Draw_TileClear (0, 0, r_refdef.vrect.x, vid.height - sb_lines); - // right - Draw_TileClear (r_refdef.vrect.x + r_refdef.vrect.width, 0, - vid.width - r_refdef.vrect.x + r_refdef.vrect.width, - vid.height - sb_lines); - } - if (r_refdef.vrect.y > 0) { - // top - Draw_TileClear (r_refdef.vrect.x, 0, - r_refdef.vrect.x + r_refdef.vrect.width, - r_refdef.vrect.y); - } - if (r_refdef.vrect.y + r_refdef.vrect.height < vid.height - sb_lines) { - // bottom - Draw_TileClear (r_refdef.vrect.x, - r_refdef.vrect.y + r_refdef.vrect.height, - r_refdef.vrect.width, - vid.height - sb_lines - - (r_refdef.vrect.height + r_refdef.vrect.y)); - } -} - -float oldsbar = 0; - -/* -================== -SCR_UpdateScreen - -This is called every frame, and can also be called explicitly to flush -text to the screen. - -WARNING: be very careful calling this from elsewhere, because the refresh -needs almost the entire 256k of stack space! -================== -*/ -void SCR_UpdateScreen (void) -{ - if (block_drawing) - return; - - vid.numpages = 2 + gl_triplebuffer.value; - - scr_copytop = 0; - scr_copyeverything = 0; - - if (scr_disabled_for_loading) - { - if (realtime - scr_disabled_time > 60) - { - scr_disabled_for_loading = false; - Con_Printf ("load failed.\n"); - } - else - return; - } - - if (!scr_initialized || !con_initialized) - return; // not initialized yet - - - if (oldsbar != cl_sbar.value) { - oldsbar = cl_sbar.value; - vid.recalc_refdef = true; - } - - // - // determine size of refresh window - // - if (oldfov != scr_fov.value) - { - oldfov = scr_fov.value; - vid.recalc_refdef = true; - } - - if (oldscreensize != scr_viewsize.value) - { - oldscreensize = scr_viewsize.value; - vid.recalc_refdef = true; - } - - if (vid.recalc_refdef) - SCR_CalcRefdef (); - - if (gl_contrast.value > 1 && !vid_hwgamma_enabled) { - // scr_fullupdate = true; - Sbar_Changed (); - } - -// -// do 3D refresh drawing, and then update the screen -// - GL_BeginRendering (&glx, &gly, &glwidth, &glheight); - - SCR_SetUpToDrawConsole (); - - V_RenderView (); - - GL_Set2D (); - - R_PolyBlend (); - - // - // draw any areas not covered by the refresh - // - SCR_TileClear (); - - if (r_netgraph.value) - R_NetGraph (); - - if (scr_drawdialog) - { - Sbar_Draw (); - Draw_FadeScreen (); - SCR_DrawNotifyString (); - scr_copyeverything = true; - } - else if (scr_drawloading) - { - SCR_DrawLoading (); - Sbar_Draw (); - } - else if (cl.intermission == 1 && key_dest == key_game) - { - Sbar_IntermissionOverlay (); - } - else if (cl.intermission == 2 && key_dest == key_game) - { - Sbar_FinaleOverlay (); - SCR_CheckDrawCenterString (); - } - else - { - if (crosshair.value) - Draw_Crosshair(); - - SCR_DrawRam (); - SCR_DrawNet (); - SCR_DrawFPS (); - SCR_DrawTurtle (); - SCR_DrawPause (); - SCR_CheckDrawCenterString (); - SCR_DrawSpeed (); - SCR_DrawClock (); - Sbar_Draw (); - SCR_DrawConsole (); - M_Draw (); - } - - R_BrightenScreen (); - - V_UpdatePalette (); - - GL_EndRendering (); -} +/* +Copyright (C) 1996-1997 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. + +*/ + +// screen.c -- master for refresh, status bar, console, chat, notify, etc + +#include "quakedef.h" +#include "sound.h" +#include "sbar.h" +#include "keys.h" +#include + +/* + +background clear +rendering +turtle/net/ram icons +sbar +centerprint / slow centerprint +notify lines +intermission / finale overlay +loading plaque +console +menu + +required background clears +required update regions + + +syncronous draw mode or async +One off screen buffer, with updates either copied or xblited +Need to double buffer? + + +async draw will require the refresh area to be cleared, because it will be +xblited, but sync draw can just ignore it. + +sync +draw + +CenterPrint () +SlowPrint () +Screen_Update (); +Con_Printf (); + +net +turn off messages option + +the refresh is always rendered, unless the console is full screen + + +console is: + notify lines + half + full + + +*/ + + +int glx, gly, glwidth, glheight; + +// only the refresh window will be updated unless these variables are flagged +int scr_copytop; +int scr_copyeverything; + +float scr_con_current; +float scr_conlines; // lines of console to display + +float oldscreensize, oldfov; +cvar_t scr_viewsize = {"viewsize","100",CVAR_ARCHIVE}; +cvar_t scr_fov = {"fov","90",CVAR_ARCHIVE}; // 10 - 170 +cvar_t scr_consize = {"scr_consize","0.5"}; +cvar_t scr_conspeed = {"scr_conspeed","1000"}; +cvar_t scr_centertime = {"scr_centertime","2"}; +cvar_t scr_showram = {"showram","1"}; +cvar_t scr_showturtle = {"showturtle","0"}; +cvar_t scr_showpause = {"showpause","1"}; +cvar_t scr_printspeed = {"scr_printspeed","8"}; +cvar_t scr_allowsnap = {"scr_allowsnap", "1"}; +cvar_t gl_triplebuffer = {"gl_triplebuffer", "1",CVAR_ARCHIVE}; +extern cvar_t crosshair; + +// Tonik: +cvar_t scr_clock = {"cl_clock","0"}; +cvar_t scr_clock_x = {"cl_clock_x","0"}; +cvar_t scr_clock_y = {"cl_clock_y","-1"}; +cvar_t show_speed = {"show_speed","0"}; + +extern cvar_t show_fps; + +qboolean scr_initialized; // ready to draw + +qpic_t *scr_ram; +qpic_t *scr_net; +qpic_t *scr_turtle; + +int scr_fullupdate; + +int clearconsole; +int clearnotify; + +viddef_t vid; // global video state + +vrect_t scr_vrect; + +qboolean scr_disabled_for_loading; +qboolean scr_drawloading; +float scr_disabled_time; + +qboolean block_drawing; + +void SCR_ScreenShot_f (void); +void SCR_RSShot_f (void); + +/* +=============================================================================== + +CENTER PRINTING + +=============================================================================== +*/ + +char scr_centerstring[1024]; +float scr_centertime_start; // for slow victory printing +float scr_centertime_off; +int scr_center_lines; +int scr_erase_lines; +int scr_erase_center; + +/* +============== +SCR_CenterPrint + +Called for important messages that should stay in the center of the screen +for a few moments +============== +*/ +void SCR_CenterPrint (char *str) +{ + Q_strncpyz (scr_centerstring, str, sizeof(scr_centerstring)); + scr_centertime_off = scr_centertime.value; + scr_centertime_start = cl.time; + +// count the number of lines for centering + scr_center_lines = 1; + while (*str) + { + if (*str == '\n') + scr_center_lines++; + str++; + } +} + + +void SCR_DrawCenterString (void) +{ + char *start; + int l; + int j; + int x, y; + int remaining; + +// the finale prints the characters one at a time + if (cl.intermission) + remaining = scr_printspeed.value * (cl.time - scr_centertime_start); + else + remaining = 9999; + + scr_erase_center = 0; + start = scr_centerstring; + + if (scr_center_lines <= 4) + y = vid.height*0.35; + else + y = 48; + + do + { + // scan the width of the line + for (l=0 ; l<40 ; l++) + if (start[l] == '\n' || !start[l]) + break; + x = (vid.width - l*8)/2; + for (j=0 ; j scr_erase_lines) + scr_erase_lines = scr_center_lines; + + scr_centertime_off -= host_frametime; + + if (scr_centertime_off <= 0 && !cl.intermission) + return; + if (key_dest != key_game) + return; + + SCR_DrawCenterString (); +} + +//============================================================================= + +/* +==================== +CalcFov +==================== +*/ +float CalcFov (float fov_x, float width, float height) +{ + float a; + float x; + + if (fov_x < 1 || fov_x > 179) + Sys_Error ("Bad fov: %f", fov_x); + + x = width/tan(fov_x/360*M_PI); + + a = atan (height/x); + + a = a*360/M_PI; + + return a; +} + +/* +================= +SCR_CalcRefdef + +Must be called whenever vid changes +Internal use only +================= +*/ +static void SCR_CalcRefdef (void) +{ + float size; + int h; + qboolean full = false; + + + scr_fullupdate = 0; // force a background redraw + vid.recalc_refdef = 0; + +// force the status bar to redraw + Sbar_Changed (); + +//======================================== + +// bound viewsize + if (scr_viewsize.value < 30) + Cvar_Set (&scr_viewsize,"30"); + if (scr_viewsize.value > 120) + Cvar_Set (&scr_viewsize,"120"); + +// bound field of view + if (scr_fov.value < 10) + Cvar_Set (&scr_fov,"10"); + if (scr_fov.value > 170) + Cvar_Set (&scr_fov,"170"); + +// intermission is always full screen + if (cl.intermission) + size = 120; + else + size = scr_viewsize.value; + + if (size >= 120) + sb_lines = 0; // no status bar at all + else if (size >= 110) + sb_lines = 24; // no inventory + else + sb_lines = 24+16+8; + + if (scr_viewsize.value >= 100.0) { + full = true; + size = 100.0; + } else + size = scr_viewsize.value; + if (cl.intermission) + { + full = true; + size = 100.0; + sb_lines = 0; + } + size /= 100.0; + + if (!cl_sbar.value && full) + h = vid.height; + else + h = vid.height - sb_lines; + + r_refdef.vrect.width = vid.width * size; + if (r_refdef.vrect.width < 96) + { + size = 96.0 / r_refdef.vrect.width; + r_refdef.vrect.width = 96; // min for icons + } + + r_refdef.vrect.height = vid.height * size; + if (cl_sbar.value || !full) { + if (r_refdef.vrect.height > vid.height - sb_lines) + r_refdef.vrect.height = vid.height - sb_lines; + } else if (r_refdef.vrect.height > vid.height) + r_refdef.vrect.height = vid.height; + r_refdef.vrect.x = (vid.width - r_refdef.vrect.width)/2; + if (full) + r_refdef.vrect.y = 0; + else + r_refdef.vrect.y = (h - r_refdef.vrect.height)/2; + + r_refdef.fov_x = scr_fov.value; + r_refdef.fov_y = CalcFov (r_refdef.fov_x, r_refdef.vrect.width, r_refdef.vrect.height); + + scr_vrect = r_refdef.vrect; +} + + +/* +================= +SCR_SizeUp_f + +Keybinding command +================= +*/ +void SCR_SizeUp_f (void) +{ + Cvar_SetValue (&scr_viewsize,scr_viewsize.value+10); + vid.recalc_refdef = 1; +} + + +/* +================= +SCR_SizeDown_f + +Keybinding command +================= +*/ +void SCR_SizeDown_f (void) +{ + Cvar_SetValue (&scr_viewsize,scr_viewsize.value-10); + vid.recalc_refdef = 1; +} + +//============================================================================ + +/* +================== +SCR_Init +================== +*/ +void SCR_Init (void) +{ + Cvar_RegisterVariable (&scr_fov); + Cvar_RegisterVariable (&scr_viewsize); + Cvar_RegisterVariable (&scr_consize); + Cvar_RegisterVariable (&scr_conspeed); + Cvar_RegisterVariable (&scr_showram); + Cvar_RegisterVariable (&scr_showturtle); + Cvar_RegisterVariable (&scr_showpause); + Cvar_RegisterVariable (&scr_centertime); + Cvar_RegisterVariable (&scr_printspeed); + Cvar_RegisterVariable (&scr_allowsnap); + Cvar_RegisterVariable (&gl_triplebuffer); + +// Tonik: + Cvar_RegisterVariable (&scr_clock_x); + Cvar_RegisterVariable (&scr_clock_y); + Cvar_RegisterVariable (&scr_clock); + Cvar_RegisterVariable (&show_speed); +// +// register our commands +// + Cmd_AddCommand ("screenshot",SCR_ScreenShot_f); + Cmd_AddCommand ("sizeup",SCR_SizeUp_f); + Cmd_AddCommand ("sizedown",SCR_SizeDown_f); + + scr_ram = Draw_PicFromWad ("ram"); + scr_net = Draw_PicFromWad ("net"); + scr_turtle = Draw_PicFromWad ("turtle"); + + scr_initialized = true; +} + + + +/* +============== +SCR_DrawRam +============== +*/ +void SCR_DrawRam (void) +{ + if (!scr_showram.value) + return; + + if (!r_cache_thrash) + return; + + Draw_Pic (scr_vrect.x+32, scr_vrect.y, scr_ram); +} + +/* +============== +SCR_DrawTurtle +============== +*/ +void SCR_DrawTurtle (void) +{ + static int count; + + if (!scr_showturtle.value) + return; + + if (host_frametime < 0.1) + { + count = 0; + return; + } + + count++; + if (count < 3) + return; + + Draw_Pic (scr_vrect.x, scr_vrect.y, scr_turtle); +} + +/* +============== +SCR_DrawNet +============== +*/ +void SCR_DrawNet (void) +{ + if (cls.netchan.outgoing_sequence - cls.netchan.incoming_acknowledged < UPDATE_BACKUP-1) + return; + if (cls.demoplayback) + return; + + Draw_Pic (scr_vrect.x+64, scr_vrect.y, scr_net); +} + +void SCR_DrawFPS (void) +{ + extern cvar_t show_fps; + static double lastframetime; + double t; + extern int fps_count; + static lastfps; + int x, y; + char st[80]; + + if (!show_fps.value) + return; + + t = Sys_DoubleTime(); + if ((t - lastframetime) >= 1.0) { + lastfps = fps_count; + fps_count = 0; + lastframetime = t; + } + + sprintf(st, "%3d FPS", lastfps); + x = vid.width - strlen(st) * 8 - 8; + y = vid.height - sb_lines - 8; +// Draw_TileClear(x, y, strlen(st) * 8, 8); + Draw_String(x, y, st); +} + + +void SCR_DrawSpeed (void) +{ + int x, y; + char st[80]; + vec3_t vel; + float speed, vspeed; + static float maxspeed = 0; + static float display_speed = -1; + static double lastrealtime = 0; + + if (!show_speed.value) + return; + + if (lastrealtime > realtime) + { + lastrealtime = 0; + display_speed = -1; + maxspeed = 0; + } + +// VectorCopy (cl.simvel, vel); + VectorCopy (cl.frames[(cls.netchan.incoming_sequence)&UPDATE_MASK].playerstate[cl.playernum].velocity, vel); + vspeed = vel[2]; + vel[2] = 0; + speed = Length(vel); + + if (speed > maxspeed) + maxspeed = speed; + + if (display_speed >= 0) + { + sprintf(st, "%3d", (int)display_speed); + x = vid.width - strlen(st) * 8 - 8; + y = 8; + // Draw_TileClear(x, y, strlen(st) * 8, 8); + Draw_String(x, y, st); + } + + if (realtime - lastrealtime >= 0.1) + { + lastrealtime = realtime; + display_speed = maxspeed; + maxspeed = 0; + } +} + +void SCR_DrawClock (void) +{ + char str[80]; + int hours, minutes, seconds; + int tens_hours, tens_minutes, tens_seconds; + + if (!scr_clock.value || cls.demoplayback || cl.intermission) + return; + + if (scr_clock.value == 2) + { + time_t t; + struct tm *ptm; + str[0] = 0; + t = time(NULL); + if (t != -1) { + ptm = localtime (&t); + if (ptm) + strftime(str, sizeof(str)-1, "%H:%M:%S", ptm); + } + } + else + { + float time; + time = cl.time; + tens_hours = fmod (time / 36000, 10); + hours = fmod (time / 3600, 10); + tens_minutes = fmod (time / 600, 6); + minutes = fmod (time / 60, 10); + tens_seconds = fmod (time / 10, 6); + seconds = fmod (time, 10); + sprintf (str, "%i%i:%i%i:%i%i", tens_hours, hours, tens_minutes, minutes, + tens_seconds, seconds); + } + + if (scr_clock_y.value < 0) + Draw_String (8 * scr_clock_x.value, vid.height - sb_lines + 8*scr_clock_y.value, str); + else + Draw_String (8 * scr_clock_x.value, 8*scr_clock_y.value, str); +} + + +/* +============== +DrawPause +============== +*/ +void SCR_DrawPause (void) +{ + qpic_t *pic; + + if (!scr_showpause.value) // turn off for screenshots + return; + + if (!cl.paused) + return; + + pic = Draw_CachePic ("gfx/pause.lmp"); + Draw_Pic ( (vid.width - pic->width)/2, + (vid.height - 48 - pic->height)/2, pic); +} + + + +/* +============== +SCR_DrawLoading +============== +*/ +void SCR_DrawLoading (void) +{ + qpic_t *pic; + + if (!scr_drawloading) + return; + + pic = Draw_CachePic ("gfx/loading.lmp"); + Draw_Pic ( (vid.width - pic->width)/2, + (vid.height - 48 - pic->height)/2, pic); +} + + + +//============================================================================= + + +/* +================== +SCR_SetUpToDrawConsole +================== +*/ +void SCR_SetUpToDrawConsole (void) +{ + Con_CheckResize (); + + if (scr_drawloading) + return; // never a console with loading plaque + +// decide on the height of the console + if (cls.state != ca_active) + { + scr_conlines = vid.height; // full screen + scr_con_current = scr_conlines; + } + else if (key_dest == key_console) { + scr_conlines = vid.height * scr_consize.value; + if (scr_conlines < 30) + scr_conlines = 30; + if (scr_conlines > vid.height - 10) + scr_conlines = vid.height - 10; + } + else + scr_conlines = 0; // none visible + + if (scr_conlines < scr_con_current) + { + scr_con_current -= scr_conspeed.value*real_frametime*vid.height/320; + if (scr_conlines > scr_con_current) + scr_con_current = scr_conlines; + + } + else if (scr_conlines > scr_con_current) + { + scr_con_current += scr_conspeed.value*real_frametime*vid.height/320; + if (scr_conlines < scr_con_current) + scr_con_current = scr_conlines; + } + + if (clearconsole++ < vid.numpages) + { + Sbar_Changed (); + } + else if (clearnotify++ < vid.numpages) + { + } + else + con_notifylines = 0; +} + +/* +================== +SCR_DrawConsole +================== +*/ +void SCR_DrawConsole (void) +{ + if (scr_con_current) + { + scr_copyeverything = 1; + Con_DrawConsole (scr_con_current); + clearconsole = 0; + } + else + { + if (key_dest == key_game || key_dest == key_message) + Con_DrawNotify (); // only draw notify in game + } +} + + +/* +============================================================================== + + SCREEN SHOTS + +============================================================================== +*/ + +typedef struct _TargaHeader { + unsigned char id_length, colormap_type, image_type; + unsigned short colormap_index, colormap_length; + unsigned char colormap_size; + unsigned short x_origin, y_origin, width, height; + unsigned char pixel_size, attributes; +} TargaHeader; + + +/* +================== +SCR_ScreenShot_f +================== +*/ +void SCR_ScreenShot_f (void) +{ + byte *buffer; + char pcxname[MAX_OSPATH]; + char checkname[MAX_OSPATH]; + int i, c, temp; + + if (Cmd_Argc() == 2) { + Q_strncpyz (pcxname, Cmd_Argv(1), sizeof(pcxname)); + COM_ForceExtension (pcxname, ".tga"); + } + else + { + // + // find a file name to save it to + // + strcpy(pcxname,"quake00.tga"); + + for (i=0 ; i<=99 ; i++) + { + pcxname[5] = i/10 + '0'; + pcxname[6] = i%10 + '0'; + sprintf (checkname, "%s/%s", com_gamedir, pcxname); + if (Sys_FileTime(checkname) == -1) + break; // file doesn't exist + } + if (i==100) + { + Con_Printf ("SCR_ScreenShot_f: Couldn't create a PCX file\n"); + return; + } + } + + buffer = Q_Malloc (glwidth*glheight*3 + 18); + memset (buffer, 0, 18); + buffer[2] = 2; // uncompressed type + buffer[12] = glwidth&255; + buffer[13] = glwidth>>8; + buffer[14] = glheight&255; + buffer[15] = glheight>>8; + buffer[16] = 24; // pixel size + + glReadPixels (glx, gly, glwidth, glheight, GL_RGB, GL_UNSIGNED_BYTE, buffer+18 ); + + // swap rgb to bgr + c = 18+glwidth*glheight*3; + for (i=18 ; imanufacturer = 0x0a; // PCX id + pcx->version = 5; // 256 color + pcx->encoding = 1; // uncompressed + pcx->bits_per_pixel = 8; // 256 color + pcx->xmin = 0; + pcx->ymin = 0; + pcx->xmax = LittleShort((short)(width-1)); + pcx->ymax = LittleShort((short)(height-1)); + pcx->hres = LittleShort((short)width); + pcx->vres = LittleShort((short)height); + memset (pcx->palette,0,sizeof(pcx->palette)); + pcx->color_planes = 1; // chunky image + pcx->bytes_per_line = LittleShort((short)width); + pcx->palette_type = LittleShort(2); // not a grey scale + memset (pcx->filler,0,sizeof(pcx->filler)); + +// pack the image + pack = &pcx->data; + + data += rowbytes * (height - 1); + + for (i=0 ; i>4; + col = num&15; + source = draw_chars + (row<<10) + (col<<3); + + drawline = 8; + + while (drawline--) + { + for (x=0 ; x<8 ; x++) + if (source[x]) + dest[x] = source[x]; + else + dest[x] = 98; + source += 128; + dest -= width; + } + +} + +void SCR_DrawStringToSnap (const char *s, byte *buf, int x, int y, int width) +{ + byte *dest; + const unsigned char *p; + + dest = buf + ((y * width) + x); + + p = (const unsigned char *)s; + while (*p) { + SCR_DrawCharToSnap(*p++, dest, width); + dest += 8; + } +} + + +/* +================== +SCR_RSShot_f +================== +*/ +void SCR_RSShot_f (void) +{ + int x, y; + unsigned char *src, *dest; + char pcxname[80]; + unsigned char *newbuf; + int w, h; + int dx, dy, dex, dey, nx; + int r, b, g; + int count; + float fracw, frach; + char st[80]; + time_t now; + + if (CL_IsUploading()) + return; // already one pending + + if (cls.state < ca_onserver) + return; // gotta be connected + + Con_Printf ("Remote screen shot requested.\n"); + +// +// save the pcx file +// + newbuf = Q_Malloc (glheight * glwidth * 3); + + glReadPixels (glx, gly, glwidth, glheight, GL_RGB, GL_UNSIGNED_BYTE, newbuf); + + w = (vid.width < RSSHOT_WIDTH) ? glwidth : RSSHOT_WIDTH; + h = (vid.height < RSSHOT_HEIGHT) ? glheight : RSSHOT_HEIGHT; + + fracw = (float)glwidth / (float)w; + frach = (float)glheight / (float)h; + + for (y = 0; y < h; y++) { + dest = newbuf + (w*3 * y); + + for (x = 0; x < w; x++) { + r = g = b = 0; + + dx = x * fracw; + dex = (x + 1) * fracw; + if (dex == dx) dex++; // at least one + dy = y * frach; + dey = (y + 1) * frach; + if (dey == dy) dey++; // at least one + + count = 0; + for (/* */; dy < dey; dy++) { + src = newbuf + (glwidth * 3 * dy) + dx * 3; + for (nx = dx; nx < dex; nx++) { + r += *src++; + g += *src++; + b += *src++; + count++; + } + } + r /= count; + g /= count; + b /= count; + *dest++ = r; + *dest++ = b; + *dest++ = g; + } + } + + // convert to eight bit + for (y = 0; y < h; y++) { + src = newbuf + (w * 3 * y); + dest = newbuf + (w * y); + + for (x = 0; x < w; x++) { + *dest++ = MipColor(src[0], src[1], src[2]); + src += 3; + } + } + + time(&now); + strcpy(st, ctime(&now)); + st[strlen(st) - 1] = 0; + SCR_DrawStringToSnap (st, newbuf, w - strlen(st)*8, h - 1, w); + + Q_strncpyz (st, cls.servername, sizeof(st)); + SCR_DrawStringToSnap (st, newbuf, w - strlen(st)*8, h - 11, w); + + Q_strncpyz (st, name.string, sizeof(st)); + SCR_DrawStringToSnap (st, newbuf, w - strlen(st)*8, h - 21, w); + + WritePCXfile (pcxname, newbuf, w, h, w, host_basepal, true); + + free(newbuf); + + Con_Printf ("Wrote %s\n", pcxname); +} + + + + +//============================================================================= + + +//============================================================================= + +char *scr_notifystring; +qboolean scr_drawdialog; + +void SCR_DrawNotifyString (void) +{ + char *start; + int l; + int j; + int x, y; + + start = scr_notifystring; + + y = vid.height*0.35; + + do + { + // scan the width of the line + for (l=0 ; l<40 ; l++) + if (start[l] == '\n' || !start[l]) + break; + x = (vid.width - l*8)/2; + for (j=0 ; j 0) { + // left + Draw_TileClear (0, 0, r_refdef.vrect.x, vid.height - sb_lines); + // right + Draw_TileClear (r_refdef.vrect.x + r_refdef.vrect.width, 0, + vid.width - r_refdef.vrect.x + r_refdef.vrect.width, + vid.height - sb_lines); + } + if (r_refdef.vrect.y > 0) { + // top + Draw_TileClear (r_refdef.vrect.x, 0, + r_refdef.vrect.x + r_refdef.vrect.width, + r_refdef.vrect.y); + } + if (r_refdef.vrect.y + r_refdef.vrect.height < vid.height - sb_lines) { + // bottom + Draw_TileClear (r_refdef.vrect.x, + r_refdef.vrect.y + r_refdef.vrect.height, + r_refdef.vrect.width, + vid.height - sb_lines - + (r_refdef.vrect.height + r_refdef.vrect.y)); + } +} + +float oldsbar = 0; + +/* +================== +SCR_UpdateScreen + +This is called every frame, and can also be called explicitly to flush +text to the screen. + +WARNING: be very careful calling this from elsewhere, because the refresh +needs almost the entire 256k of stack space! +================== +*/ +void SCR_UpdateScreen (void) +{ + if (block_drawing) + return; + + vid.numpages = 2 + gl_triplebuffer.value; + + scr_copytop = 0; + scr_copyeverything = 0; + + if (scr_disabled_for_loading) + { + if (realtime - scr_disabled_time > 60) + { + scr_disabled_for_loading = false; + Con_Printf ("load failed.\n"); + } + else + return; + } + + if (!scr_initialized || !con_initialized) + return; // not initialized yet + + + if (oldsbar != cl_sbar.value) { + oldsbar = cl_sbar.value; + vid.recalc_refdef = true; + } + + // + // determine size of refresh window + // + if (oldfov != scr_fov.value) + { + oldfov = scr_fov.value; + vid.recalc_refdef = true; + } + + if (oldscreensize != scr_viewsize.value) + { + oldscreensize = scr_viewsize.value; + vid.recalc_refdef = true; + } + + if (vid.recalc_refdef) + SCR_CalcRefdef (); +#ifdef WIN32 + if (gl_contrast.value > 1 && !vid_hwgamma_enabled) { + // scr_fullupdate = true; + Sbar_Changed (); + } +#else + if (gl_contrast.value > 1) { + scr_fullupdate = true; + Sbar_Changed (); + } +#endif + +// +// do 3D refresh drawing, and then update the screen +// + GL_BeginRendering (&glx, &gly, &glwidth, &glheight); + + SCR_SetUpToDrawConsole (); + + V_RenderView (); + + GL_Set2D (); + + R_PolyBlend (); + + // + // draw any areas not covered by the refresh + // + SCR_TileClear (); + + if (r_netgraph.value) + R_NetGraph (); + + if (scr_drawdialog) + { + Sbar_Draw (); + Draw_FadeScreen (); + SCR_DrawNotifyString (); + scr_copyeverything = true; + } + else if (scr_drawloading) + { + SCR_DrawLoading (); + Sbar_Draw (); + } + else if (cl.intermission == 1 && key_dest == key_game) + { + Sbar_IntermissionOverlay (); + } + else if (cl.intermission == 2 && key_dest == key_game) + { + Sbar_FinaleOverlay (); + SCR_CheckDrawCenterString (); + } + else + { + if (crosshair.value) + Draw_Crosshair(); + + SCR_DrawRam (); + SCR_DrawNet (); + SCR_DrawFPS (); + SCR_DrawTurtle (); + SCR_DrawPause (); + SCR_CheckDrawCenterString (); + SCR_DrawSpeed (); + SCR_DrawClock (); + Sbar_Draw (); + SCR_DrawConsole (); + M_Draw (); + } + + R_BrightenScreen (); + + V_UpdatePalette (); + + GL_EndRendering (); +} diff --git a/source/gl_test.c b/source/gl_test.c index c646a7ba..a3b10276 100644 --- a/source/gl_test.c +++ b/source/gl_test.c @@ -1,182 +1,182 @@ -/* -Copyright (C) 1996-1997 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 "quakedef.h" - -#ifdef GLTEST - -typedef struct -{ - plane_t *plane; - vec3_t origin; - vec3_t normal; - vec3_t up; - vec3_t right; - vec3_t reflect; - float length; -} puff_t; - -#define MAX_PUFFS 64 - -puff_t puffs[MAX_PUFFS]; - - -void Test_Init (void) -{ -} - - - -plane_t junk; -plane_t *HitPlane (vec3_t start, vec3_t end) -{ - trace_t trace; - -// fill in a default trace - memset (&trace, 0, sizeof(trace_t)); - trace.fraction = 1; - trace.allsolid = true; - VectorCopy (end, trace.endpos); - - SV_RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, start, end, &trace); - - junk = trace.plane; - return &junk; -} - -void Test_Spawn (vec3_t origin) -{ - int i; - puff_t *p; - vec3_t temp; - vec3_t normal; - vec3_t incoming; - plane_t *plane; - float d; - - for (i=0,p=puffs ; ilength <= 0) - break; - } - if (i == MAX_PUFFS) - return; - - VectorSubtract (r_refdef.vieworg, origin, incoming); - VectorSubtract (origin, incoming, temp); - plane = HitPlane (r_refdef.vieworg, temp); - - VectorNormalize (incoming); - d = DotProduct (incoming, plane->normal); - VectorSubtract (vec3_origin, incoming, p->reflect); - VectorMA (p->reflect, d*2, plane->normal, p->reflect); - - VectorCopy (origin, p->origin); - VectorCopy (plane->normal, p->normal); - - CrossProduct (incoming, p->normal, p->up); - - CrossProduct (p->up, p->normal, p->right); - - p->length = 8; -} - -void DrawPuff (puff_t *p) -{ - vec3_t pts[2][3]; - int i, j; - float s, d; - - for (i=0 ; i<2 ; i++) - { - if (i == 1) - { - s = 6; - d = p->length; - } - else - { - s = 2; - d = 0; - } - - for (j=0 ; j<3 ; j++) - { - pts[i][0][j] = p->origin[j] + p->up[j]*s + p->reflect[j]*d; - pts[i][1][j] = p->origin[j] + p->right[j]*s + p->reflect[j]*d; - pts[i][2][j] = p->origin[j] + -p->right[j]*s + p->reflect[j]*d; - } - } - - glColor3f (1, 0, 0); - -#if 0 - glBegin (GL_LINES); - glVertex3fv (p->origin); - glVertex3f (p->origin[0] + p->length*p->reflect[0], - p->origin[1] + p->length*p->reflect[1], - p->origin[2] + p->length*p->reflect[2]); - - glVertex3fv (pts[0][0]); - glVertex3fv (pts[1][0]); - - glVertex3fv (pts[0][1]); - glVertex3fv (pts[1][1]); - - glVertex3fv (pts[0][2]); - glVertex3fv (pts[1][2]); - - glEnd (); -#endif - - glBegin (GL_QUADS); - for (i=0 ; i<3 ; i++) - { - j = (i+1)%3; - glVertex3fv (pts[0][j]); - glVertex3fv (pts[1][j]); - glVertex3fv (pts[1][i]); - glVertex3fv (pts[0][i]); - } - glEnd (); - - glBegin (GL_TRIANGLES); - glVertex3fv (pts[1][0]); - glVertex3fv (pts[1][1]); - glVertex3fv (pts[1][2]); - glEnd (); - - p->length -= host_frametime*2; -} - - -void Test_Draw (void) -{ - int i; - puff_t *p; - - for (i=0, p=puffs ; ilength > 0) - DrawPuff (p); - } -} - -#endif +/* +Copyright (C) 1996-1997 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 "quakedef.h" + +#ifdef GLTEST + +typedef struct +{ + plane_t *plane; + vec3_t origin; + vec3_t normal; + vec3_t up; + vec3_t right; + vec3_t reflect; + float length; +} puff_t; + +#define MAX_PUFFS 64 + +puff_t puffs[MAX_PUFFS]; + + +void Test_Init (void) +{ +} + + + +plane_t junk; +plane_t *HitPlane (vec3_t start, vec3_t end) +{ + trace_t trace; + +// fill in a default trace + memset (&trace, 0, sizeof(trace_t)); + trace.fraction = 1; + trace.allsolid = true; + VectorCopy (end, trace.endpos); + + SV_RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, start, end, &trace); + + junk = trace.plane; + return &junk; +} + +void Test_Spawn (vec3_t origin) +{ + int i; + puff_t *p; + vec3_t temp; + vec3_t normal; + vec3_t incoming; + plane_t *plane; + float d; + + for (i=0,p=puffs ; ilength <= 0) + break; + } + if (i == MAX_PUFFS) + return; + + VectorSubtract (r_refdef.vieworg, origin, incoming); + VectorSubtract (origin, incoming, temp); + plane = HitPlane (r_refdef.vieworg, temp); + + VectorNormalize (incoming); + d = DotProduct (incoming, plane->normal); + VectorSubtract (vec3_origin, incoming, p->reflect); + VectorMA (p->reflect, d*2, plane->normal, p->reflect); + + VectorCopy (origin, p->origin); + VectorCopy (plane->normal, p->normal); + + CrossProduct (incoming, p->normal, p->up); + + CrossProduct (p->up, p->normal, p->right); + + p->length = 8; +} + +void DrawPuff (puff_t *p) +{ + vec3_t pts[2][3]; + int i, j; + float s, d; + + for (i=0 ; i<2 ; i++) + { + if (i == 1) + { + s = 6; + d = p->length; + } + else + { + s = 2; + d = 0; + } + + for (j=0 ; j<3 ; j++) + { + pts[i][0][j] = p->origin[j] + p->up[j]*s + p->reflect[j]*d; + pts[i][1][j] = p->origin[j] + p->right[j]*s + p->reflect[j]*d; + pts[i][2][j] = p->origin[j] + -p->right[j]*s + p->reflect[j]*d; + } + } + + glColor3f (1, 0, 0); + +#if 0 + glBegin (GL_LINES); + glVertex3fv (p->origin); + glVertex3f (p->origin[0] + p->length*p->reflect[0], + p->origin[1] + p->length*p->reflect[1], + p->origin[2] + p->length*p->reflect[2]); + + glVertex3fv (pts[0][0]); + glVertex3fv (pts[1][0]); + + glVertex3fv (pts[0][1]); + glVertex3fv (pts[1][1]); + + glVertex3fv (pts[0][2]); + glVertex3fv (pts[1][2]); + + glEnd (); +#endif + + glBegin (GL_QUADS); + for (i=0 ; i<3 ; i++) + { + j = (i+1)%3; + glVertex3fv (pts[0][j]); + glVertex3fv (pts[1][j]); + glVertex3fv (pts[1][i]); + glVertex3fv (pts[0][i]); + } + glEnd (); + + glBegin (GL_TRIANGLES); + glVertex3fv (pts[1][0]); + glVertex3fv (pts[1][1]); + glVertex3fv (pts[1][2]); + glEnd (); + + p->length -= host_frametime*2; +} + + +void Test_Draw (void) +{ + int i; + puff_t *p; + + for (i=0, p=puffs ; ilength > 0) + DrawPuff (p); + } +} + +#endif diff --git a/source/gl_vidlinux.c b/source/gl_vidlinux.c index be9e8d18..2973f218 100644 --- a/source/gl_vidlinux.c +++ b/source/gl_vidlinux.c @@ -1,813 +1,815 @@ -/* -Copyright (C) 1996-1997 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 -#include -#include -#include -#include -#include - -#include - -#include "vga.h" -#include "vgakeyboard.h" -#include "vgamouse.h" - -#include "quakedef.h" -#include "GL/fxmesa.h" - -#define WARP_WIDTH 320 -#define WARP_HEIGHT 200 - -static fxMesaContext fc = NULL; -#define stringify(m) { #m, m } - -unsigned short d_8to16table[256]; -unsigned d_8to24table[256]; -unsigned char d_15to8table[65536]; - -int num_shades=32; - -struct -{ - char *name; - int num; -} mice[] = -{ - stringify(MOUSE_MICROSOFT), - stringify(MOUSE_MOUSESYSTEMS), - stringify(MOUSE_MMSERIES), - stringify(MOUSE_LOGITECH), - stringify(MOUSE_BUSMOUSE), - stringify(MOUSE_PS2), -}; - -static unsigned char scantokey[128]; - -int num_mice = sizeof (mice) / sizeof(mice[0]); - -int d_con_indirect = 0; - -int svgalib_inited=0; -int UseMouse = 1; -int UseKeyboard = 1; - -int mouserate = MOUSE_DEFAULTSAMPLERATE; - -cvar_t _windowed_mouse = {"_windowed_mouse","0",CVAR_ARCHIVE}; - -cvar_t vid_mode = {"vid_mode","5"}; -cvar_t vid_redrawfull = {"vid_redrawfull","0"}; -cvar_t vid_waitforrefresh = {"vid_waitforrefresh","0",CVAR_ARCHIVE}; - -char *framebuffer_ptr; - -cvar_t mouse_button_commands[3] = -{ - {"mouse1","+attack"}, - {"mouse2","+strafe"}, - {"mouse3","+forward"}, -}; - -int mouse_buttons; -int mouse_buttonstate; -int mouse_oldbuttonstate; -float mouse_x, mouse_y; -float old_mouse_x, old_mouse_y; -int mx, my; - -cvar_t m_filter = {"m_filter","1"}; - -int scr_width, scr_height; - -/*-----------------------------------------------------------------------*/ - -//int texture_mode = GL_NEAREST; -//int texture_mode = GL_NEAREST_MIPMAP_NEAREST; -//int texture_mode = GL_NEAREST_MIPMAP_LINEAR; -int texture_mode = GL_LINEAR; -//int texture_mode = GL_LINEAR_MIPMAP_NEAREST; -//int texture_mode = GL_LINEAR_MIPMAP_LINEAR; - -int texture_extension_number = 1; - -float gldepthmin, gldepthmax; - -cvar_t gl_ztrick = {"gl_ztrick","1"}; - -const char *gl_vendor; -const char *gl_renderer; -const char *gl_version; -const char *gl_extensions; - -qboolean is8bit = false; -qboolean isPermedia = false; -qboolean gl_mtexable = false; - -/*-----------------------------------------------------------------------*/ -void D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height) -{ -} - -void D_EndDirectRect (int x, int y, int width, int height) -{ -} - -int matchmouse(int mouse, char *name) -{ - int i; - for (i=0 ; i> 2)+4; - b = ((i & 0x7C00) >> 7)+4; - pal = (unsigned char *)d_8to24table; - for (v=0,k=0,bestdist=10000.0; v<256; v++,pal+=4) { - r1 = (int)r - (int)pal[0]; - g1 = (int)g - (int)pal[1]; - b1 = (int)b - (int)pal[2]; - dist = sqrt(((r1*r1)+(g1*g1)+(b1*b1))); - if (dist < bestdist) { - k=v; - bestdist = dist; - } - } - d_15to8table[i]=k; - } - sprintf(s, "%s/glquake", com_gamedir); - Sys_mkdir (s); - sprintf(s, "%s/glquake/15to8.pal", com_gamedir); - if ((f = fopen(s, "wb")) != NULL) { - fwrite(d_15to8table, 1<<15, 1, f); - fclose(f); - } - } -} - -/* -=============== -GL_Init -=============== -*/ -void GL_Init (void) -{ - gl_vendor = glGetString (GL_VENDOR); - Con_Printf ("GL_VENDOR: %s\n", gl_vendor); - gl_renderer = glGetString (GL_RENDERER); - Con_Printf ("GL_RENDERER: %s\n", gl_renderer); - - gl_version = glGetString (GL_VERSION); - Con_Printf ("GL_VERSION: %s\n", gl_version); - gl_extensions = glGetString (GL_EXTENSIONS); - Con_Printf ("GL_EXTENSIONS: %s\n", gl_extensions); - -// Con_Printf ("%s %s\n", gl_renderer, gl_version); - - glClearColor (1,0,0,0); - glCullFace(GL_FRONT); - glEnable(GL_TEXTURE_2D); - - glEnable(GL_ALPHA_TEST); - glAlphaFunc(GL_GREATER, 0.666); - - glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); - glShadeModel (GL_FLAT); - - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - - glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - -// glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); -} - -/* -================= -GL_BeginRendering - -================= -*/ -void GL_BeginRendering (int *x, int *y, int *width, int *height) -{ - extern cvar_t gl_clear; - - *x = *y = 0; - *width = scr_width; - *height = scr_height; - -// if (!wglMakeCurrent( maindc, baseRC )) -// Sys_Error ("wglMakeCurrent failed"); - -// glViewport (*x, *y, *width, *height); -} - - -void GL_EndRendering (void) -{ - glFlush(); - fxMesaSwapBuffers(); -} - -void Init_KBD(void) -{ - int i; - - if (COM_CheckParm("-nokbd")) UseKeyboard = 0; - - if (UseKeyboard) - { - for (i=0 ; i<128 ; i++) - scantokey[i] = ' '; - - scantokey[42] = K_SHIFT; - scantokey[54] = K_SHIFT; - scantokey[72] = K_UPARROW; - scantokey[103] = K_UPARROW; - scantokey[80] = K_DOWNARROW; - scantokey[108] = K_DOWNARROW; - scantokey[75] = K_LEFTARROW; - scantokey[105] = K_LEFTARROW; - scantokey[77] = K_RIGHTARROW; - scantokey[106] = K_RIGHTARROW; - scantokey[29] = K_CTRL; - scantokey[97] = K_CTRL; - scantokey[56] = K_ALT; - scantokey[100] = K_ALT; -// scantokey[58] = JK_CAPS; -// scantokey[69] = JK_NUM_LOCK; - scantokey[71] = K_HOME; - scantokey[73] = K_PGUP; - scantokey[79] = K_END; - scantokey[81] = K_PGDN; - scantokey[82] = K_INS; - scantokey[83] = K_DEL; - scantokey[1 ] = K_ESCAPE; - scantokey[28] = K_ENTER; - scantokey[15] = K_TAB; - scantokey[14] = K_BACKSPACE; - scantokey[119] = K_PAUSE; - scantokey[57] = ' '; - - scantokey[102] = K_HOME; - scantokey[104] = K_PGUP; - scantokey[107] = K_END; - scantokey[109] = K_PGDN; - scantokey[110] = K_INS; - scantokey[111] = K_DEL; - - scantokey[2] = '1'; - scantokey[3] = '2'; - scantokey[4] = '3'; - scantokey[5] = '4'; - scantokey[6] = '5'; - scantokey[7] = '6'; - scantokey[8] = '7'; - scantokey[9] = '8'; - scantokey[10] = '9'; - scantokey[11] = '0'; - scantokey[12] = '-'; - scantokey[13] = '='; - scantokey[41] = '`'; - scantokey[26] = '['; - scantokey[27] = ']'; - scantokey[39] = ';'; - scantokey[40] = '\''; - scantokey[51] = ','; - scantokey[52] = '.'; - scantokey[53] = '/'; - scantokey[43] = '\\'; - - scantokey[59] = K_F1; - scantokey[60] = K_F2; - scantokey[61] = K_F3; - scantokey[62] = K_F4; - scantokey[63] = K_F5; - scantokey[64] = K_F6; - scantokey[65] = K_F7; - scantokey[66] = K_F8; - scantokey[67] = K_F9; - scantokey[68] = K_F10; - scantokey[87] = K_F11; - scantokey[88] = K_F12; - scantokey[30] = 'a'; - scantokey[48] = 'b'; - scantokey[46] = 'c'; - scantokey[32] = 'd'; - scantokey[18] = 'e'; - scantokey[33] = 'f'; - scantokey[34] = 'g'; - scantokey[35] = 'h'; - scantokey[23] = 'i'; - scantokey[36] = 'j'; - scantokey[37] = 'k'; - scantokey[38] = 'l'; - scantokey[50] = 'm'; - scantokey[49] = 'n'; - scantokey[24] = 'o'; - scantokey[25] = 'p'; - scantokey[16] = 'q'; - scantokey[19] = 'r'; - scantokey[31] = 's'; - scantokey[20] = 't'; - scantokey[22] = 'u'; - scantokey[47] = 'v'; - scantokey[17] = 'w'; - scantokey[45] = 'x'; - scantokey[21] = 'y'; - scantokey[44] = 'z'; - - scantokey[78] = '+'; - scantokey[74] = '-'; - - if (keyboard_init()) - Sys_Error("keyboard_init() failed"); - keyboard_seteventhandler(keyhandler); - } -} - -#define NUM_RESOLUTIONS 3 - -static resolutions[NUM_RESOLUTIONS][3]={ - { 512, 384, GR_RESOLUTION_512x384 }, - { 640, 400, GR_RESOLUTION_640x400 }, - { 640, 480, GR_RESOLUTION_640x480 } -}; - -int findres(int *width, int *height) -{ - int i; - - for(i=0;i height) - vid.conheight = height; - if (vid.conwidth > width) - vid.conwidth = width; - vid.width = vid.conwidth; - vid.height = vid.conheight; - - vid.aspect = ((float)vid.height / (float)vid.width) * - (320.0 / 240.0); - vid.numpages = 2; - - InitSig(); // trap evil signals - - GL_Init(); - - sprintf (gldir, "%s/glquake", com_gamedir); - Sys_mkdir (gldir); - - VID_SetPalette(palette); - - // Check for 3DFX Extensions and initialize them. - VID_Init8bitPalette(); - - Con_SafePrintf ("Video mode %dx%d initialized.\n", width, height); - - vid.recalc_refdef = 1; // force a surface cache flush -} - -void Sys_SendKeyEvents(void) -{ - if (UseKeyboard) - while (keyboard_update()); -} - -void Force_CenterView_f (void) -{ - cl.viewangles[PITCH] = 0; -} - - -void mousehandler(int buttonstate, int dx, int dy) -{ - mouse_buttonstate = buttonstate; - mx += dx; - my += dy; -} - -void IN_Init(void) -{ - - int mtype; - char *mousedev; - int mouserate; - - if (UseMouse) - { - - Cvar_RegisterVariable (&mouse_button_commands[0]); - Cvar_RegisterVariable (&mouse_button_commands[1]); - Cvar_RegisterVariable (&mouse_button_commands[2]); - Cmd_AddCommand ("force_centerview", Force_CenterView_f); - - mouse_buttons = 3; - - mtype = vga_getmousetype(); - - mousedev = "/dev/mouse"; - if (getenv("MOUSEDEV")) mousedev = getenv("MOUSEDEV"); - if (COM_CheckParm("-mdev")) - mousedev = com_argv[COM_CheckParm("-mdev")+1]; - - mouserate = 1200; - if (getenv("MOUSERATE")) mouserate = atoi(getenv("MOUSERATE")); - if (COM_CheckParm("-mrate")) - mouserate = atoi(com_argv[COM_CheckParm("-mrate")+1]); - - if (mouse_init(mousedev, mtype, mouserate)) - { - Con_Printf("No mouse found\n"); - UseMouse = 0; - } - else - mouse_seteventhandler(mousehandler); - - } - -} - -void IN_Shutdown(void) -{ - if (UseMouse) - mouse_close(); -} - -/* -=========== -IN_Commands -=========== -*/ -void IN_Commands (void) -{ - if (UseMouse) - { - // poll mouse values - while (mouse_update()) - ; - - // perform button actions - if ((mouse_buttonstate & MOUSE_LEFTBUTTON) && - !(mouse_oldbuttonstate & MOUSE_LEFTBUTTON)) - Key_Event (K_MOUSE1, true); - else if (!(mouse_buttonstate & MOUSE_LEFTBUTTON) && - (mouse_oldbuttonstate & MOUSE_LEFTBUTTON)) - Key_Event (K_MOUSE1, false); - - if ((mouse_buttonstate & MOUSE_RIGHTBUTTON) && - !(mouse_oldbuttonstate & MOUSE_RIGHTBUTTON)) - Key_Event (K_MOUSE2, true); - else if (!(mouse_buttonstate & MOUSE_RIGHTBUTTON) && - (mouse_oldbuttonstate & MOUSE_RIGHTBUTTON)) - Key_Event (K_MOUSE2, false); - - if ((mouse_buttonstate & MOUSE_MIDDLEBUTTON) && - !(mouse_oldbuttonstate & MOUSE_MIDDLEBUTTON)) - Key_Event (K_MOUSE3, true); - else if (!(mouse_buttonstate & MOUSE_MIDDLEBUTTON) && - (mouse_oldbuttonstate & MOUSE_MIDDLEBUTTON)) - Key_Event (K_MOUSE3, false); - - mouse_oldbuttonstate = mouse_buttonstate; - } -} - -/* -=========== -IN_Move -=========== -*/ -void IN_MouseMove (usercmd_t *cmd) -{ - if (!UseMouse) - return; - - // poll mouse values - while (mouse_update()) - ; - - if (m_filter.value) - { - mouse_x = (mx + old_mouse_x) * 0.5; - mouse_y = (my + old_mouse_y) * 0.5; - } - else - { - mouse_x = mx; - mouse_y = my; - } - old_mouse_x = mx; - old_mouse_y = my; - mx = my = 0; // clear for next update - - mouse_x *= sensitivity.value; - mouse_y *= sensitivity.value; - -// add mouse X/Y movement to cmd - if ( (in_strafe.state & 1) || (lookstrafe.value && (in_mlook.state & 1) )) - cmd->sidemove += m_side.value * mouse_x; - else - cl.viewangles[YAW] -= m_yaw.value * mouse_x; - - if (in_mlook.state & 1) - V_StopPitchDrift (); - - if ( (in_mlook.state & 1) && !(in_strafe.state & 1)) - { - cl.viewangles[PITCH] += m_pitch.value * mouse_y; - if (cl.viewangles[PITCH] > 80) - cl.viewangles[PITCH] = 80; - if (cl.viewangles[PITCH] < -70) - cl.viewangles[PITCH] = -70; - } - else - { - cmd->forwardmove -= m_forward.value * mouse_y; - } -} - -void IN_Move (usercmd_t *cmd) -{ - IN_MouseMove(cmd); -} - -void VID_UnlockBuffer() {} -void VID_LockBuffer() {} - +/* +Copyright (C) 1996-1997 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 +#include +#include +#include +#include +#include + +#include + +#include "vga.h" +#include "vgakeyboard.h" +#include "vgamouse.h" + +#include "quakedef.h" +#include "GL/fxmesa.h" +#include "keys.h" + +#define WARP_WIDTH 320 +#define WARP_HEIGHT 200 + +static fxMesaContext fc = NULL; +#define stringify(m) { #m, m } + +unsigned short d_8to16table[256]; +unsigned d_8to24table[256]; +unsigned d_8to24table2[256]; +unsigned char d_15to8table[65536]; + +int num_shades=32; + +struct +{ + char *name; + int num; +} mice[] = +{ + stringify(MOUSE_MICROSOFT), + stringify(MOUSE_MOUSESYSTEMS), + stringify(MOUSE_MMSERIES), + stringify(MOUSE_LOGITECH), + stringify(MOUSE_BUSMOUSE), + stringify(MOUSE_PS2), +}; + +static unsigned char scantokey[128]; + +int num_mice = sizeof (mice) / sizeof(mice[0]); + +int d_con_indirect = 0; + +int svgalib_inited=0; +int UseMouse = 1; +int UseKeyboard = 1; + +int mouserate = MOUSE_DEFAULTSAMPLERATE; + +cvar_t _windowed_mouse = {"_windowed_mouse","0",CVAR_ARCHIVE}; + +cvar_t vid_mode = {"vid_mode","5"}; +cvar_t vid_redrawfull = {"vid_redrawfull","0"}; +cvar_t vid_waitforrefresh = {"vid_waitforrefresh","0",CVAR_ARCHIVE}; + +char *framebuffer_ptr; + +cvar_t mouse_button_commands[3] = +{ + {"mouse1","+attack"}, + {"mouse2","+strafe"}, + {"mouse3","+forward"}, +}; + +int mouse_buttons; +int mouse_buttonstate; +int mouse_oldbuttonstate; +float mouse_x, mouse_y; +float old_mouse_x, old_mouse_y; +int mx, my; + +cvar_t m_filter = {"m_filter","1"}; + +int scr_width, scr_height; + +/*-----------------------------------------------------------------------*/ + +//int texture_mode = GL_NEAREST; +//int texture_mode = GL_NEAREST_MIPMAP_NEAREST; +//int texture_mode = GL_NEAREST_MIPMAP_LINEAR; +int texture_mode = GL_LINEAR; +//int texture_mode = GL_LINEAR_MIPMAP_NEAREST; +//int texture_mode = GL_LINEAR_MIPMAP_LINEAR; + +int texture_extension_number = 1; + +float gldepthmin, gldepthmax; + +cvar_t gl_ztrick = {"gl_ztrick","1"}; + +const char *gl_vendor; +const char *gl_renderer; +const char *gl_version; +const char *gl_extensions; + +qboolean is8bit = false; +qboolean isPermedia = false; +qboolean gl_mtexable = false; + +/*-----------------------------------------------------------------------*/ +void D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height) +{ +} + +void D_EndDirectRect (int x, int y, int width, int height) +{ +} + +int matchmouse(int mouse, char *name) +{ + int i; + for (i=0 ; i> 2)+4; + b = ((i & 0x7C00) >> 7)+4; + pal = (unsigned char *)d_8to24table; + for (v=0,k=0,bestdist=10000.0; v<256; v++,pal+=4) { + r1 = (int)r - (int)pal[0]; + g1 = (int)g - (int)pal[1]; + b1 = (int)b - (int)pal[2]; + dist = sqrt(((r1*r1)+(g1*g1)+(b1*b1))); + if (dist < bestdist) { + k=v; + bestdist = dist; + } + } + d_15to8table[i]=k; + } + sprintf(s, "%s/glquake", com_gamedir); + Sys_mkdir (s); + sprintf(s, "%s/glquake/15to8.pal", com_gamedir); + if ((f = fopen(s, "wb")) != NULL) { + fwrite(d_15to8table, 1<<15, 1, f); + fclose(f); + } + } +} + +/* +=============== +GL_Init +=============== +*/ +void GL_Init (void) +{ + gl_vendor = glGetString (GL_VENDOR); + Con_Printf ("GL_VENDOR: %s\n", gl_vendor); + gl_renderer = glGetString (GL_RENDERER); + Con_Printf ("GL_RENDERER: %s\n", gl_renderer); + + gl_version = glGetString (GL_VERSION); + Con_Printf ("GL_VERSION: %s\n", gl_version); + gl_extensions = glGetString (GL_EXTENSIONS); + Con_Printf ("GL_EXTENSIONS: %s\n", gl_extensions); + +// Con_Printf ("%s %s\n", gl_renderer, gl_version); + + glClearColor (1,0,0,0); + glCullFace(GL_FRONT); + glEnable(GL_TEXTURE_2D); + + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_GREATER, 0.666); + + glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); + glShadeModel (GL_FLAT); + + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + +// glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); +} + +/* +================= +GL_BeginRendering + +================= +*/ +void GL_BeginRendering (int *x, int *y, int *width, int *height) +{ + extern cvar_t gl_clear; + + *x = *y = 0; + *width = scr_width; + *height = scr_height; + +// if (!wglMakeCurrent( maindc, baseRC )) +// Sys_Error ("wglMakeCurrent failed"); + +// glViewport (*x, *y, *width, *height); +} + + +void GL_EndRendering (void) +{ + glFlush(); + fxMesaSwapBuffers(); +} + +void Init_KBD(void) +{ + int i; + + if (COM_CheckParm("-nokbd")) UseKeyboard = 0; + + if (UseKeyboard) + { + for (i=0 ; i<128 ; i++) + scantokey[i] = ' '; + + scantokey[42] = K_SHIFT; + scantokey[54] = K_SHIFT; + scantokey[72] = K_UPARROW; + scantokey[103] = K_UPARROW; + scantokey[80] = K_DOWNARROW; + scantokey[108] = K_DOWNARROW; + scantokey[75] = K_LEFTARROW; + scantokey[105] = K_LEFTARROW; + scantokey[77] = K_RIGHTARROW; + scantokey[106] = K_RIGHTARROW; + scantokey[29] = K_CTRL; + scantokey[97] = K_CTRL; + scantokey[56] = K_ALT; + scantokey[100] = K_ALT; +// scantokey[58] = JK_CAPS; +// scantokey[69] = JK_NUM_LOCK; + scantokey[71] = K_HOME; + scantokey[73] = K_PGUP; + scantokey[79] = K_END; + scantokey[81] = K_PGDN; + scantokey[82] = K_INS; + scantokey[83] = K_DEL; + scantokey[1 ] = K_ESCAPE; + scantokey[28] = K_ENTER; + scantokey[15] = K_TAB; + scantokey[14] = K_BACKSPACE; + scantokey[119] = K_PAUSE; + scantokey[57] = ' '; + + scantokey[102] = K_HOME; + scantokey[104] = K_PGUP; + scantokey[107] = K_END; + scantokey[109] = K_PGDN; + scantokey[110] = K_INS; + scantokey[111] = K_DEL; + + scantokey[2] = '1'; + scantokey[3] = '2'; + scantokey[4] = '3'; + scantokey[5] = '4'; + scantokey[6] = '5'; + scantokey[7] = '6'; + scantokey[8] = '7'; + scantokey[9] = '8'; + scantokey[10] = '9'; + scantokey[11] = '0'; + scantokey[12] = '-'; + scantokey[13] = '='; + scantokey[41] = '`'; + scantokey[26] = '['; + scantokey[27] = ']'; + scantokey[39] = ';'; + scantokey[40] = '\''; + scantokey[51] = ','; + scantokey[52] = '.'; + scantokey[53] = '/'; + scantokey[43] = '\\'; + + scantokey[59] = K_F1; + scantokey[60] = K_F2; + scantokey[61] = K_F3; + scantokey[62] = K_F4; + scantokey[63] = K_F5; + scantokey[64] = K_F6; + scantokey[65] = K_F7; + scantokey[66] = K_F8; + scantokey[67] = K_F9; + scantokey[68] = K_F10; + scantokey[87] = K_F11; + scantokey[88] = K_F12; + scantokey[30] = 'a'; + scantokey[48] = 'b'; + scantokey[46] = 'c'; + scantokey[32] = 'd'; + scantokey[18] = 'e'; + scantokey[33] = 'f'; + scantokey[34] = 'g'; + scantokey[35] = 'h'; + scantokey[23] = 'i'; + scantokey[36] = 'j'; + scantokey[37] = 'k'; + scantokey[38] = 'l'; + scantokey[50] = 'm'; + scantokey[49] = 'n'; + scantokey[24] = 'o'; + scantokey[25] = 'p'; + scantokey[16] = 'q'; + scantokey[19] = 'r'; + scantokey[31] = 's'; + scantokey[20] = 't'; + scantokey[22] = 'u'; + scantokey[47] = 'v'; + scantokey[17] = 'w'; + scantokey[45] = 'x'; + scantokey[21] = 'y'; + scantokey[44] = 'z'; + + scantokey[78] = '+'; + scantokey[74] = '-'; + + if (keyboard_init()) + Sys_Error("keyboard_init() failed"); + keyboard_seteventhandler(keyhandler); + } +} + +#define NUM_RESOLUTIONS 3 + +static resolutions[NUM_RESOLUTIONS][3]={ + { 512, 384, GR_RESOLUTION_512x384 }, + { 640, 400, GR_RESOLUTION_640x400 }, + { 640, 480, GR_RESOLUTION_640x480 } +}; + +int findres(int *width, int *height) +{ + int i; + + for(i=0;i height) + vid.conheight = height; + if (vid.conwidth > width) + vid.conwidth = width; + vid.width = vid.conwidth; + vid.height = vid.conheight; + + vid.aspect = ((float)vid.height / (float)vid.width) * + (320.0 / 240.0); + vid.numpages = 2; + + InitSig(); // trap evil signals + + GL_Init(); + + sprintf (gldir, "%s/glquake", com_gamedir); + Sys_mkdir (gldir); + + VID_SetPalette(palette); + + // Check for 3DFX Extensions and initialize them. + VID_Init8bitPalette(); + + Con_SafePrintf ("Video mode %dx%d initialized.\n", width, height); + + vid.recalc_refdef = 1; // force a surface cache flush +} + +void Sys_SendKeyEvents(void) +{ + if (UseKeyboard) + while (keyboard_update()); +} + +void Force_CenterView_f (void) +{ + cl.viewangles[PITCH] = 0; +} + + +void mousehandler(int buttonstate, int dx, int dy) +{ + mouse_buttonstate = buttonstate; + mx += dx; + my += dy; +} + +void IN_Init(void) +{ + + int mtype; + char *mousedev; + int mouserate; + + if (UseMouse) + { + + Cvar_RegisterVariable (&mouse_button_commands[0]); + Cvar_RegisterVariable (&mouse_button_commands[1]); + Cvar_RegisterVariable (&mouse_button_commands[2]); + Cmd_AddCommand ("force_centerview", Force_CenterView_f); + + mouse_buttons = 3; + + mtype = vga_getmousetype(); + + mousedev = "/dev/mouse"; + if (getenv("MOUSEDEV")) mousedev = getenv("MOUSEDEV"); + if (COM_CheckParm("-mdev")) + mousedev = com_argv[COM_CheckParm("-mdev")+1]; + + mouserate = 1200; + if (getenv("MOUSERATE")) mouserate = atoi(getenv("MOUSERATE")); + if (COM_CheckParm("-mrate")) + mouserate = atoi(com_argv[COM_CheckParm("-mrate")+1]); + + if (mouse_init(mousedev, mtype, mouserate)) + { + Con_Printf("No mouse found\n"); + UseMouse = 0; + } + else + mouse_seteventhandler(mousehandler); + + } + +} + +void IN_Shutdown(void) +{ + if (UseMouse) + mouse_close(); +} + +/* +=========== +IN_Commands +=========== +*/ +void IN_Commands (void) +{ + if (UseMouse) + { + // poll mouse values + while (mouse_update()) + ; + + // perform button actions + if ((mouse_buttonstate & MOUSE_LEFTBUTTON) && + !(mouse_oldbuttonstate & MOUSE_LEFTBUTTON)) + Key_Event (K_MOUSE1, true); + else if (!(mouse_buttonstate & MOUSE_LEFTBUTTON) && + (mouse_oldbuttonstate & MOUSE_LEFTBUTTON)) + Key_Event (K_MOUSE1, false); + + if ((mouse_buttonstate & MOUSE_RIGHTBUTTON) && + !(mouse_oldbuttonstate & MOUSE_RIGHTBUTTON)) + Key_Event (K_MOUSE2, true); + else if (!(mouse_buttonstate & MOUSE_RIGHTBUTTON) && + (mouse_oldbuttonstate & MOUSE_RIGHTBUTTON)) + Key_Event (K_MOUSE2, false); + + if ((mouse_buttonstate & MOUSE_MIDDLEBUTTON) && + !(mouse_oldbuttonstate & MOUSE_MIDDLEBUTTON)) + Key_Event (K_MOUSE3, true); + else if (!(mouse_buttonstate & MOUSE_MIDDLEBUTTON) && + (mouse_oldbuttonstate & MOUSE_MIDDLEBUTTON)) + Key_Event (K_MOUSE3, false); + + mouse_oldbuttonstate = mouse_buttonstate; + } +} + +/* +=========== +IN_Move +=========== +*/ +void IN_MouseMove (usercmd_t *cmd) +{ + if (!UseMouse) + return; + + // poll mouse values + while (mouse_update()) + ; + + if (m_filter.value) + { + mouse_x = (mx + old_mouse_x) * 0.5; + mouse_y = (my + old_mouse_y) * 0.5; + } + else + { + mouse_x = mx; + mouse_y = my; + } + old_mouse_x = mx; + old_mouse_y = my; + mx = my = 0; // clear for next update + + mouse_x *= sensitivity.value; + mouse_y *= sensitivity.value; + +// add mouse X/Y movement to cmd + if ( (in_strafe.state & 1) || (lookstrafe.value && (in_mlook.state & 1) )) + cmd->sidemove += m_side.value * mouse_x; + else + cl.viewangles[YAW] -= m_yaw.value * mouse_x; + + if (in_mlook.state & 1) + V_StopPitchDrift (); + + if ( (in_mlook.state & 1) && !(in_strafe.state & 1)) + { + cl.viewangles[PITCH] += m_pitch.value * mouse_y; + if (cl.viewangles[PITCH] > 80) + cl.viewangles[PITCH] = 80; + if (cl.viewangles[PITCH] < -70) + cl.viewangles[PITCH] = -70; + } + else + { + cmd->forwardmove -= m_forward.value * mouse_y; + } +} + +void IN_Move (usercmd_t *cmd) +{ + IN_MouseMove(cmd); +} + +void VID_UnlockBuffer() {} +void VID_LockBuffer() {} + diff --git a/source/gl_vidlinux_svga.c b/source/gl_vidlinux_svga.c index 1dd16ff3..583b8575 100644 --- a/source/gl_vidlinux_svga.c +++ b/source/gl_vidlinux_svga.c @@ -1,852 +1,852 @@ -/* -Copyright (C) 1996-1997 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 -#include -#include -#include -#include -#include - -#include - -/*#include "vga.h" */ -#include "vgakeyboard.h" -#include "vgamouse.h" - -#include "quakedef.h" -#include "GL/fxmesa.h" - -#define WARP_WIDTH 320 -#define WARP_HEIGHT 200 - -static fxMesaContext fc = NULL; -#define stringify(m) { #m, m } - -unsigned short d_8to16table[256]; -unsigned d_8to24table[256]; -unsigned char d_15to8table[65536]; - -int num_shades=32; - -struct -{ - char *name; - int num; -} mice[] = -{ - stringify(MOUSE_MICROSOFT), - stringify(MOUSE_MOUSESYSTEMS), - stringify(MOUSE_MMSERIES), - stringify(MOUSE_LOGITECH), - stringify(MOUSE_BUSMOUSE), - stringify(MOUSE_PS2), -}; - -static unsigned char scantokey[128]; - -int num_mice = sizeof (mice) / sizeof(mice[0]); - -int d_con_indirect = 0; - -int svgalib_inited=0; -int UseMouse = 1; -int UseKeyboard = 1; - -int mouserate = MOUSE_DEFAULTSAMPLERATE; - -cvar_t vid_mode = {"vid_mode","5"}; -cvar_t vid_redrawfull = {"vid_redrawfull","0"}; -cvar_t vid_waitforrefresh = {"vid_waitforrefresh","0",CVAR_ARCHIVE}; - -char *framebuffer_ptr; - -cvar_t mouse_button_commands[3] = -{ - {"mouse1","+attack"}, - {"mouse2","+strafe"}, - {"mouse3","+forward"}, -}; - -int mouse_buttons; -int mouse_buttonstate; -int mouse_oldbuttonstate; -float mouse_x, mouse_y; -float old_mouse_x, old_mouse_y; -int mx, my; - -cvar_t _windowed_mouse = {"_windowed_mouse", "1",CVAR_ARCHIVE}; -cvar_t m_filter = {"m_filter","0"}; - -int scr_width, scr_height; - -/*-----------------------------------------------------------------------*/ - -//int texture_mode = GL_NEAREST; -//int texture_mode = GL_NEAREST_MIPMAP_NEAREST; -//int texture_mode = GL_NEAREST_MIPMAP_LINEAR; -int texture_mode = GL_LINEAR; -//int texture_mode = GL_LINEAR_MIPMAP_NEAREST; -//int texture_mode = GL_LINEAR_MIPMAP_LINEAR; - -int texture_extension_number = 1; - -float gldepthmin, gldepthmax; - -cvar_t gl_ztrick = {"gl_ztrick","1"}; - -const char *gl_vendor; -const char *gl_renderer; -const char *gl_version; -const char *gl_extensions; - -qboolean is8bit = false; -qboolean isPermedia = false; -qboolean gl_mtexable = false; - -/*-----------------------------------------------------------------------*/ -void D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height) -{ -} - -void D_EndDirectRect (int x, int y, int width, int height) -{ -} - -/* -================= -VID_Gamma_f - -Keybinding command -================= -*/ -void VID_Gamma_f (void) -{ - float gamma, f, inf; - unsigned char palette[768]; - int i; - - if (Cmd_Argc () == 2) - { - gamma = Q_atof (Cmd_Argv(1)); - - for (i=0 ; i<768 ; i++) - { - f = pow ( (host_basepal[i]+1)/256.0 , gamma ); - inf = f*255 + 0.5; - if (inf < 0) - inf = 0; - if (inf > 255) - inf = 255; - palette[i] = inf; - } - - VID_SetPalette (palette); - - vid.recalc_refdef = 1; // force a surface cache flush - } -} - -int matchmouse(int mouse, char *name) -{ - int i; - for (i=0 ; i> 2)+4; - b = ((i & 0x7C00) >> 7)+4; - pal = (unsigned char *)d_8to24table; - for (v=0,k=0,bestdist=10000.0; v<256; v++,pal+=4) { - r1 = (int)r - (int)pal[0]; - g1 = (int)g - (int)pal[1]; - b1 = (int)b - (int)pal[2]; - dist = sqrt(((r1*r1)+(g1*g1)+(b1*b1))); - if (dist < bestdist) { - k=v; - bestdist = dist; - } - } - d_15to8table[i]=k; - } - sprintf(s, "%s/glquake", com_gamedir); - Sys_mkdir (s); - sprintf(s, "%s/glquake/15to8.pal", com_gamedir); - if ((f = fopen(s, "wb")) != NULL) { - fwrite(d_15to8table, 1<<15, 1, f); - fclose(f); - } - } -} - -/* -=============== -GL_Init -=============== -*/ -void GL_Init (void) -{ - gl_vendor = glGetString (GL_VENDOR); - Con_Printf ("GL_VENDOR: %s\n", gl_vendor); - gl_renderer = glGetString (GL_RENDERER); - Con_Printf ("GL_RENDERER: %s\n", gl_renderer); - - gl_version = glGetString (GL_VERSION); - Con_Printf ("GL_VERSION: %s\n", gl_version); - gl_extensions = glGetString (GL_EXTENSIONS); - Con_Printf ("GL_EXTENSIONS: %s\n", gl_extensions); - -// Con_Printf ("%s %s\n", gl_renderer, gl_version); - - glClearColor (1,0,0,0); - glCullFace(GL_FRONT); - glEnable(GL_TEXTURE_2D); - - glEnable(GL_ALPHA_TEST); - glAlphaFunc(GL_GREATER, 0.666); - - glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); - glShadeModel (GL_FLAT); - - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - - glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - -// glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); -} - -/* -================= -GL_BeginRendering - -================= -*/ -void GL_BeginRendering (int *x, int *y, int *width, int *height) -{ - extern cvar_t gl_clear; - - *x = *y = 0; - *width = scr_width; - *height = scr_height; - -// if (!wglMakeCurrent( maindc, baseRC )) -// Sys_Error ("wglMakeCurrent failed"); - -// glViewport (*x, *y, *width, *height); -} - - -void GL_EndRendering (void) -{ - glFlush(); - fxMesaSwapBuffers(); -} - -void Init_KBD(void) -{ - int i; - - if (COM_CheckParm("-nokbd")) UseKeyboard = 0; - - if (UseKeyboard) - { - for (i=0 ; i<128 ; i++) - scantokey[i] = ' '; - - scantokey[ 1] = K_ESCAPE; - scantokey[ 2] = '1'; - scantokey[ 3] = '2'; - scantokey[ 4] = '3'; - scantokey[ 5] = '4'; - scantokey[ 6] = '5'; - scantokey[ 7] = '6'; - scantokey[ 8] = '7'; - scantokey[ 9] = '8'; - scantokey[ 10] = '9'; - scantokey[ 11] = '0'; - scantokey[ 12] = '-'; - scantokey[ 13] = '='; - scantokey[ 14] = K_BACKSPACE; - scantokey[ 15] = K_TAB; - scantokey[ 16] = 'q'; - scantokey[ 17] = 'w'; - scantokey[ 18] = 'e'; - scantokey[ 19] = 'r'; - scantokey[ 20] = 't'; - scantokey[ 21] = 'y'; - scantokey[ 22] = 'u'; - scantokey[ 23] = 'i'; - scantokey[ 24] = 'o'; - scantokey[ 25] = 'p'; - scantokey[ 26] = '['; - scantokey[ 27] = ']'; - scantokey[ 28] = K_ENTER; - scantokey[ 29] = K_CTRL; //left - scantokey[ 30] = 'a'; - scantokey[ 31] = 's'; - scantokey[ 32] = 'd'; - scantokey[ 33] = 'f'; - scantokey[ 34] = 'g'; - scantokey[ 35] = 'h'; - scantokey[ 36] = 'j'; - scantokey[ 37] = 'k'; - scantokey[ 38] = 'l'; - scantokey[ 39] = ';'; - scantokey[ 40] = '\''; - scantokey[ 41] = '`'; - scantokey[ 42] = K_SHIFT; //left - scantokey[ 43] = '\\'; - scantokey[ 44] = 'z'; - scantokey[ 45] = 'x'; - scantokey[ 46] = 'c'; - scantokey[ 47] = 'v'; - scantokey[ 48] = 'b'; - scantokey[ 49] = 'n'; - scantokey[ 50] = 'm'; - scantokey[ 51] = ','; - scantokey[ 52] = '.'; - scantokey[ 53] = '/'; - scantokey[ 54] = K_SHIFT; //right - scantokey[ 55] = '*'; //keypad - scantokey[ 56] = K_ALT; //left - scantokey[ 57] = ' '; - // 58 caps lock - scantokey[ 59] = K_F1; - scantokey[ 60] = K_F2; - scantokey[ 61] = K_F3; - scantokey[ 62] = K_F4; - scantokey[ 63] = K_F5; - scantokey[ 64] = K_F6; - scantokey[ 65] = K_F7; - scantokey[ 66] = K_F8; - scantokey[ 67] = K_F9; - scantokey[ 68] = K_F10; - // 69 numlock - // 70 scrollock - scantokey[ 71] = K_HOME; - scantokey[ 72] = K_UPARROW; - scantokey[ 73] = K_PGUP; - scantokey[ 74] = '-'; - scantokey[ 75] = K_LEFTARROW; - scantokey[ 76] = '5'; - scantokey[ 77] = K_RIGHTARROW; - scantokey[ 79] = K_END; - scantokey[ 78] = '+'; - scantokey[ 80] = K_DOWNARROW; - scantokey[ 81] = K_PGDN; - scantokey[ 82] = K_INS; - scantokey[ 83] = K_DEL; - // 84 to 86 not used - scantokey[ 87] = K_F11; - scantokey[ 88] = K_F12; - // 89 to 95 not used - scantokey[ 96] = K_ENTER; //keypad enter - scantokey[ 97] = K_CTRL; //right - scantokey[ 98] = '/'; - scantokey[ 99] = K_F12; // print screen, bind to screenshot by default - scantokey[100] = K_ALT; // right - - - scantokey[101] = K_PAUSE; // break - scantokey[102] = K_HOME; - scantokey[103] = K_UPARROW; - scantokey[104] = K_PGUP; - scantokey[105] = K_LEFTARROW; - scantokey[106] = K_RIGHTARROW; - scantokey[107] = K_END; - scantokey[108] = K_DOWNARROW; - scantokey[109] = K_PGDN; - scantokey[110] = K_INS; - scantokey[111] = K_DEL; - - scantokey[119] = K_PAUSE; - - if (keyboard_init()) - Sys_Error("keyboard_init() failed"); - keyboard_seteventhandler(keyhandler); - } -} - -#define NUM_RESOLUTIONS 3 - -static resolutions[NUM_RESOLUTIONS][3]={ - { 512, 384, GR_RESOLUTION_512x384 }, - { 640, 400, GR_RESOLUTION_640x400 }, - { 640, 480, GR_RESOLUTION_640x480 } -}; - -int findres(int *width, int *height) -{ - int i; - - for(i=0;i height) - vid.conheight = height; - if (vid.conwidth > width) - vid.conwidth = width; - vid.width = vid.conwidth; - vid.height = vid.conheight; - - vid.aspect = ((float)vid.height / (float)vid.width) * - (320.0 / 240.0); - vid.numpages = 2; - - InitSig(); // trap evil signals - - GL_Init(); - - sprintf (gldir, "%s/glquake", com_gamedir); - Sys_mkdir (gldir); - - VID_SetPalette(palette); - - // Check for 3DFX Extensions and initialize them. - VID_Init8bitPalette(); - - Con_SafePrintf ("Video mode %dx%d initialized.\n", width, height); - - vid.recalc_refdef = 1; // force a surface cache flush -} - -void Sys_SendKeyEvents(void) -{ - if (UseKeyboard) - while (keyboard_update()); -} - -void Force_CenterView_f (void) -{ - cl.viewangles[PITCH] = 0; -} - - -void mousehandler(int buttonstate, int dx, int dy) -{ - mouse_buttonstate = buttonstate; - mx += dx; - my += dy; -} - -void IN_Init(void) -{ - - int mtype; - char *mousedev; - int mouserate; - - if (UseMouse) - { - - Cvar_RegisterVariable (&mouse_button_commands[0]); - Cvar_RegisterVariable (&mouse_button_commands[1]); - Cvar_RegisterVariable (&mouse_button_commands[2]); - Cmd_AddCommand ("force_centerview", Force_CenterView_f); - - mouse_buttons = 3; - - mtype = vga_getmousetype(); - - mousedev = "/dev/mouse"; - if (getenv("MOUSEDEV")) mousedev = getenv("MOUSEDEV"); - if (COM_CheckParm("-mdev")) - mousedev = com_argv[COM_CheckParm("-mdev")+1]; - - mouserate = 1200; - if (getenv("MOUSERATE")) mouserate = atoi(getenv("MOUSERATE")); - if (COM_CheckParm("-mrate")) - mouserate = atoi(com_argv[COM_CheckParm("-mrate")+1]); - - if (mouse_init(mousedev, mtype, mouserate)) - { - Con_Printf("No mouse found\n"); - UseMouse = 0; - } - else - mouse_seteventhandler(mousehandler); - - } - -} - -void IN_Shutdown(void) -{ - if (UseMouse) - mouse_close(); -} - -/* -=========== -IN_Commands -=========== -*/ -void IN_Commands (void) -{ - if (UseMouse) - { - // poll mouse values - while (mouse_update()) - ; - - // perform button actions - if ((mouse_buttonstate & MOUSE_LEFTBUTTON) && - !(mouse_oldbuttonstate & MOUSE_LEFTBUTTON)) - Key_Event (K_MOUSE1, true); - else if (!(mouse_buttonstate & MOUSE_LEFTBUTTON) && - (mouse_oldbuttonstate & MOUSE_LEFTBUTTON)) - Key_Event (K_MOUSE1, false); - - if ((mouse_buttonstate & MOUSE_RIGHTBUTTON) && - !(mouse_oldbuttonstate & MOUSE_RIGHTBUTTON)) - Key_Event (K_MOUSE2, true); - else if (!(mouse_buttonstate & MOUSE_RIGHTBUTTON) && - (mouse_oldbuttonstate & MOUSE_RIGHTBUTTON)) - Key_Event (K_MOUSE2, false); - - if ((mouse_buttonstate & MOUSE_MIDDLEBUTTON) && - !(mouse_oldbuttonstate & MOUSE_MIDDLEBUTTON)) - Key_Event (K_MOUSE3, true); - else if (!(mouse_buttonstate & MOUSE_MIDDLEBUTTON) && - (mouse_oldbuttonstate & MOUSE_MIDDLEBUTTON)) - Key_Event (K_MOUSE3, false); - - mouse_oldbuttonstate = mouse_buttonstate; - } -} - -/* -=========== -IN_Move -=========== -*/ -void IN_MouseMove (usercmd_t *cmd) -{ - if (!UseMouse) - return; - - // poll mouse values - while (mouse_update()) - ; - - if (m_filter.value) - { - mouse_x = (mx + old_mouse_x) * 0.5; - mouse_y = (my + old_mouse_y) * 0.5; - } - else - { - mouse_x = mx; - mouse_y = my; - } - old_mouse_x = mx; - old_mouse_y = my; - mx = my = 0; // clear for next update - - mouse_x *= sensitivity.value; - mouse_y *= sensitivity.value; - -// add mouse X/Y movement to cmd - if ( (in_strafe.state & 1) || (lookstrafe.value && (in_mlook.state & 1) )) - cmd->sidemove += m_side.value * mouse_x; - else - cl.viewangles[YAW] -= m_yaw.value * mouse_x; - - if (in_mlook.state & 1) - V_StopPitchDrift (); - - if ( (in_mlook.state & 1) && !(in_strafe.state & 1)) - { - cl.viewangles[PITCH] += m_pitch.value * mouse_y; - if (cl.viewangles[PITCH] > 80) - cl.viewangles[PITCH] = 80; - if (cl.viewangles[PITCH] < -70) - cl.viewangles[PITCH] = -70; - } - else - { - cmd->forwardmove -= m_forward.value * mouse_y; - } -} - -void IN_Move (usercmd_t *cmd) -{ - IN_MouseMove(cmd); -} - - -void VID_LockBuffer (void) {} -void VID_UnlockBuffer (void) {} - +/* +Copyright (C) 1996-1997 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 +#include +#include +#include +#include +#include + +#include + +/*#include "vga.h" */ +#include "vgakeyboard.h" +#include "vgamouse.h" + +#include "quakedef.h" +#include "GL/fxmesa.h" + +#define WARP_WIDTH 320 +#define WARP_HEIGHT 200 + +static fxMesaContext fc = NULL; +#define stringify(m) { #m, m } + +unsigned short d_8to16table[256]; +unsigned d_8to24table[256]; +unsigned char d_15to8table[65536]; + +int num_shades=32; + +struct +{ + char *name; + int num; +} mice[] = +{ + stringify(MOUSE_MICROSOFT), + stringify(MOUSE_MOUSESYSTEMS), + stringify(MOUSE_MMSERIES), + stringify(MOUSE_LOGITECH), + stringify(MOUSE_BUSMOUSE), + stringify(MOUSE_PS2), +}; + +static unsigned char scantokey[128]; + +int num_mice = sizeof (mice) / sizeof(mice[0]); + +int d_con_indirect = 0; + +int svgalib_inited=0; +int UseMouse = 1; +int UseKeyboard = 1; + +int mouserate = MOUSE_DEFAULTSAMPLERATE; + +cvar_t vid_mode = {"vid_mode","5"}; +cvar_t vid_redrawfull = {"vid_redrawfull","0"}; +cvar_t vid_waitforrefresh = {"vid_waitforrefresh","0",CVAR_ARCHIVE}; + +char *framebuffer_ptr; + +cvar_t mouse_button_commands[3] = +{ + {"mouse1","+attack"}, + {"mouse2","+strafe"}, + {"mouse3","+forward"}, +}; + +int mouse_buttons; +int mouse_buttonstate; +int mouse_oldbuttonstate; +float mouse_x, mouse_y; +float old_mouse_x, old_mouse_y; +int mx, my; + +cvar_t _windowed_mouse = {"_windowed_mouse", "1",CVAR_ARCHIVE}; +cvar_t m_filter = {"m_filter","0"}; + +int scr_width, scr_height; + +/*-----------------------------------------------------------------------*/ + +//int texture_mode = GL_NEAREST; +//int texture_mode = GL_NEAREST_MIPMAP_NEAREST; +//int texture_mode = GL_NEAREST_MIPMAP_LINEAR; +int texture_mode = GL_LINEAR; +//int texture_mode = GL_LINEAR_MIPMAP_NEAREST; +//int texture_mode = GL_LINEAR_MIPMAP_LINEAR; + +int texture_extension_number = 1; + +float gldepthmin, gldepthmax; + +cvar_t gl_ztrick = {"gl_ztrick","1"}; + +const char *gl_vendor; +const char *gl_renderer; +const char *gl_version; +const char *gl_extensions; + +qboolean is8bit = false; +qboolean isPermedia = false; +qboolean gl_mtexable = false; + +/*-----------------------------------------------------------------------*/ +void D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height) +{ +} + +void D_EndDirectRect (int x, int y, int width, int height) +{ +} + +/* +================= +VID_Gamma_f + +Keybinding command +================= +*/ +void VID_Gamma_f (void) +{ + float gamma, f, inf; + unsigned char palette[768]; + int i; + + if (Cmd_Argc () == 2) + { + gamma = Q_atof (Cmd_Argv(1)); + + for (i=0 ; i<768 ; i++) + { + f = pow ( (host_basepal[i]+1)/256.0 , gamma ); + inf = f*255 + 0.5; + if (inf < 0) + inf = 0; + if (inf > 255) + inf = 255; + palette[i] = inf; + } + + VID_SetPalette (palette); + + vid.recalc_refdef = 1; // force a surface cache flush + } +} + +int matchmouse(int mouse, char *name) +{ + int i; + for (i=0 ; i> 2)+4; + b = ((i & 0x7C00) >> 7)+4; + pal = (unsigned char *)d_8to24table; + for (v=0,k=0,bestdist=10000.0; v<256; v++,pal+=4) { + r1 = (int)r - (int)pal[0]; + g1 = (int)g - (int)pal[1]; + b1 = (int)b - (int)pal[2]; + dist = sqrt(((r1*r1)+(g1*g1)+(b1*b1))); + if (dist < bestdist) { + k=v; + bestdist = dist; + } + } + d_15to8table[i]=k; + } + sprintf(s, "%s/glquake", com_gamedir); + Sys_mkdir (s); + sprintf(s, "%s/glquake/15to8.pal", com_gamedir); + if ((f = fopen(s, "wb")) != NULL) { + fwrite(d_15to8table, 1<<15, 1, f); + fclose(f); + } + } +} + +/* +=============== +GL_Init +=============== +*/ +void GL_Init (void) +{ + gl_vendor = glGetString (GL_VENDOR); + Con_Printf ("GL_VENDOR: %s\n", gl_vendor); + gl_renderer = glGetString (GL_RENDERER); + Con_Printf ("GL_RENDERER: %s\n", gl_renderer); + + gl_version = glGetString (GL_VERSION); + Con_Printf ("GL_VERSION: %s\n", gl_version); + gl_extensions = glGetString (GL_EXTENSIONS); + Con_Printf ("GL_EXTENSIONS: %s\n", gl_extensions); + +// Con_Printf ("%s %s\n", gl_renderer, gl_version); + + glClearColor (1,0,0,0); + glCullFace(GL_FRONT); + glEnable(GL_TEXTURE_2D); + + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_GREATER, 0.666); + + glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); + glShadeModel (GL_FLAT); + + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + +// glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); +} + +/* +================= +GL_BeginRendering + +================= +*/ +void GL_BeginRendering (int *x, int *y, int *width, int *height) +{ + extern cvar_t gl_clear; + + *x = *y = 0; + *width = scr_width; + *height = scr_height; + +// if (!wglMakeCurrent( maindc, baseRC )) +// Sys_Error ("wglMakeCurrent failed"); + +// glViewport (*x, *y, *width, *height); +} + + +void GL_EndRendering (void) +{ + glFlush(); + fxMesaSwapBuffers(); +} + +void Init_KBD(void) +{ + int i; + + if (COM_CheckParm("-nokbd")) UseKeyboard = 0; + + if (UseKeyboard) + { + for (i=0 ; i<128 ; i++) + scantokey[i] = ' '; + + scantokey[ 1] = K_ESCAPE; + scantokey[ 2] = '1'; + scantokey[ 3] = '2'; + scantokey[ 4] = '3'; + scantokey[ 5] = '4'; + scantokey[ 6] = '5'; + scantokey[ 7] = '6'; + scantokey[ 8] = '7'; + scantokey[ 9] = '8'; + scantokey[ 10] = '9'; + scantokey[ 11] = '0'; + scantokey[ 12] = '-'; + scantokey[ 13] = '='; + scantokey[ 14] = K_BACKSPACE; + scantokey[ 15] = K_TAB; + scantokey[ 16] = 'q'; + scantokey[ 17] = 'w'; + scantokey[ 18] = 'e'; + scantokey[ 19] = 'r'; + scantokey[ 20] = 't'; + scantokey[ 21] = 'y'; + scantokey[ 22] = 'u'; + scantokey[ 23] = 'i'; + scantokey[ 24] = 'o'; + scantokey[ 25] = 'p'; + scantokey[ 26] = '['; + scantokey[ 27] = ']'; + scantokey[ 28] = K_ENTER; + scantokey[ 29] = K_CTRL; //left + scantokey[ 30] = 'a'; + scantokey[ 31] = 's'; + scantokey[ 32] = 'd'; + scantokey[ 33] = 'f'; + scantokey[ 34] = 'g'; + scantokey[ 35] = 'h'; + scantokey[ 36] = 'j'; + scantokey[ 37] = 'k'; + scantokey[ 38] = 'l'; + scantokey[ 39] = ';'; + scantokey[ 40] = '\''; + scantokey[ 41] = '`'; + scantokey[ 42] = K_SHIFT; //left + scantokey[ 43] = '\\'; + scantokey[ 44] = 'z'; + scantokey[ 45] = 'x'; + scantokey[ 46] = 'c'; + scantokey[ 47] = 'v'; + scantokey[ 48] = 'b'; + scantokey[ 49] = 'n'; + scantokey[ 50] = 'm'; + scantokey[ 51] = ','; + scantokey[ 52] = '.'; + scantokey[ 53] = '/'; + scantokey[ 54] = K_SHIFT; //right + scantokey[ 55] = '*'; //keypad + scantokey[ 56] = K_ALT; //left + scantokey[ 57] = ' '; + // 58 caps lock + scantokey[ 59] = K_F1; + scantokey[ 60] = K_F2; + scantokey[ 61] = K_F3; + scantokey[ 62] = K_F4; + scantokey[ 63] = K_F5; + scantokey[ 64] = K_F6; + scantokey[ 65] = K_F7; + scantokey[ 66] = K_F8; + scantokey[ 67] = K_F9; + scantokey[ 68] = K_F10; + // 69 numlock + // 70 scrollock + scantokey[ 71] = K_HOME; + scantokey[ 72] = K_UPARROW; + scantokey[ 73] = K_PGUP; + scantokey[ 74] = '-'; + scantokey[ 75] = K_LEFTARROW; + scantokey[ 76] = '5'; + scantokey[ 77] = K_RIGHTARROW; + scantokey[ 79] = K_END; + scantokey[ 78] = '+'; + scantokey[ 80] = K_DOWNARROW; + scantokey[ 81] = K_PGDN; + scantokey[ 82] = K_INS; + scantokey[ 83] = K_DEL; + // 84 to 86 not used + scantokey[ 87] = K_F11; + scantokey[ 88] = K_F12; + // 89 to 95 not used + scantokey[ 96] = K_ENTER; //keypad enter + scantokey[ 97] = K_CTRL; //right + scantokey[ 98] = '/'; + scantokey[ 99] = K_F12; // print screen, bind to screenshot by default + scantokey[100] = K_ALT; // right + + + scantokey[101] = K_PAUSE; // break + scantokey[102] = K_HOME; + scantokey[103] = K_UPARROW; + scantokey[104] = K_PGUP; + scantokey[105] = K_LEFTARROW; + scantokey[106] = K_RIGHTARROW; + scantokey[107] = K_END; + scantokey[108] = K_DOWNARROW; + scantokey[109] = K_PGDN; + scantokey[110] = K_INS; + scantokey[111] = K_DEL; + + scantokey[119] = K_PAUSE; + + if (keyboard_init()) + Sys_Error("keyboard_init() failed"); + keyboard_seteventhandler(keyhandler); + } +} + +#define NUM_RESOLUTIONS 3 + +static resolutions[NUM_RESOLUTIONS][3]={ + { 512, 384, GR_RESOLUTION_512x384 }, + { 640, 400, GR_RESOLUTION_640x400 }, + { 640, 480, GR_RESOLUTION_640x480 } +}; + +int findres(int *width, int *height) +{ + int i; + + for(i=0;i height) + vid.conheight = height; + if (vid.conwidth > width) + vid.conwidth = width; + vid.width = vid.conwidth; + vid.height = vid.conheight; + + vid.aspect = ((float)vid.height / (float)vid.width) * + (320.0 / 240.0); + vid.numpages = 2; + + InitSig(); // trap evil signals + + GL_Init(); + + sprintf (gldir, "%s/glquake", com_gamedir); + Sys_mkdir (gldir); + + VID_SetPalette(palette); + + // Check for 3DFX Extensions and initialize them. + VID_Init8bitPalette(); + + Con_SafePrintf ("Video mode %dx%d initialized.\n", width, height); + + vid.recalc_refdef = 1; // force a surface cache flush +} + +void Sys_SendKeyEvents(void) +{ + if (UseKeyboard) + while (keyboard_update()); +} + +void Force_CenterView_f (void) +{ + cl.viewangles[PITCH] = 0; +} + + +void mousehandler(int buttonstate, int dx, int dy) +{ + mouse_buttonstate = buttonstate; + mx += dx; + my += dy; +} + +void IN_Init(void) +{ + + int mtype; + char *mousedev; + int mouserate; + + if (UseMouse) + { + + Cvar_RegisterVariable (&mouse_button_commands[0]); + Cvar_RegisterVariable (&mouse_button_commands[1]); + Cvar_RegisterVariable (&mouse_button_commands[2]); + Cmd_AddCommand ("force_centerview", Force_CenterView_f); + + mouse_buttons = 3; + + mtype = vga_getmousetype(); + + mousedev = "/dev/mouse"; + if (getenv("MOUSEDEV")) mousedev = getenv("MOUSEDEV"); + if (COM_CheckParm("-mdev")) + mousedev = com_argv[COM_CheckParm("-mdev")+1]; + + mouserate = 1200; + if (getenv("MOUSERATE")) mouserate = atoi(getenv("MOUSERATE")); + if (COM_CheckParm("-mrate")) + mouserate = atoi(com_argv[COM_CheckParm("-mrate")+1]); + + if (mouse_init(mousedev, mtype, mouserate)) + { + Con_Printf("No mouse found\n"); + UseMouse = 0; + } + else + mouse_seteventhandler(mousehandler); + + } + +} + +void IN_Shutdown(void) +{ + if (UseMouse) + mouse_close(); +} + +/* +=========== +IN_Commands +=========== +*/ +void IN_Commands (void) +{ + if (UseMouse) + { + // poll mouse values + while (mouse_update()) + ; + + // perform button actions + if ((mouse_buttonstate & MOUSE_LEFTBUTTON) && + !(mouse_oldbuttonstate & MOUSE_LEFTBUTTON)) + Key_Event (K_MOUSE1, true); + else if (!(mouse_buttonstate & MOUSE_LEFTBUTTON) && + (mouse_oldbuttonstate & MOUSE_LEFTBUTTON)) + Key_Event (K_MOUSE1, false); + + if ((mouse_buttonstate & MOUSE_RIGHTBUTTON) && + !(mouse_oldbuttonstate & MOUSE_RIGHTBUTTON)) + Key_Event (K_MOUSE2, true); + else if (!(mouse_buttonstate & MOUSE_RIGHTBUTTON) && + (mouse_oldbuttonstate & MOUSE_RIGHTBUTTON)) + Key_Event (K_MOUSE2, false); + + if ((mouse_buttonstate & MOUSE_MIDDLEBUTTON) && + !(mouse_oldbuttonstate & MOUSE_MIDDLEBUTTON)) + Key_Event (K_MOUSE3, true); + else if (!(mouse_buttonstate & MOUSE_MIDDLEBUTTON) && + (mouse_oldbuttonstate & MOUSE_MIDDLEBUTTON)) + Key_Event (K_MOUSE3, false); + + mouse_oldbuttonstate = mouse_buttonstate; + } +} + +/* +=========== +IN_Move +=========== +*/ +void IN_MouseMove (usercmd_t *cmd) +{ + if (!UseMouse) + return; + + // poll mouse values + while (mouse_update()) + ; + + if (m_filter.value) + { + mouse_x = (mx + old_mouse_x) * 0.5; + mouse_y = (my + old_mouse_y) * 0.5; + } + else + { + mouse_x = mx; + mouse_y = my; + } + old_mouse_x = mx; + old_mouse_y = my; + mx = my = 0; // clear for next update + + mouse_x *= sensitivity.value; + mouse_y *= sensitivity.value; + +// add mouse X/Y movement to cmd + if ( (in_strafe.state & 1) || (lookstrafe.value && (in_mlook.state & 1) )) + cmd->sidemove += m_side.value * mouse_x; + else + cl.viewangles[YAW] -= m_yaw.value * mouse_x; + + if (in_mlook.state & 1) + V_StopPitchDrift (); + + if ( (in_mlook.state & 1) && !(in_strafe.state & 1)) + { + cl.viewangles[PITCH] += m_pitch.value * mouse_y; + if (cl.viewangles[PITCH] > 80) + cl.viewangles[PITCH] = 80; + if (cl.viewangles[PITCH] < -70) + cl.viewangles[PITCH] = -70; + } + else + { + cmd->forwardmove -= m_forward.value * mouse_y; + } +} + +void IN_Move (usercmd_t *cmd) +{ + IN_MouseMove(cmd); +} + + +void VID_LockBuffer (void) {} +void VID_UnlockBuffer (void) {} + diff --git a/source/gl_vidlinux_x11.c b/source/gl_vidlinux_x11.c index c53b32c4..631b6af8 100644 --- a/source/gl_vidlinux_x11.c +++ b/source/gl_vidlinux_x11.c @@ -1,867 +1,867 @@ -/* -Copyright (C) 1996-1997 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 -#include -#include -#include -#include -#include -#include - -//#include -#include -#include -#include -#include - -#include "GL/gl.h" -#include "GL/glx.h" - -#include "quakedef.h" - -#define WARP_WIDTH 320 -#define WARP_HEIGHT 200 - -static Display *dpy = NULL; -static Window win; -static GLXContext ctx = NULL; - -unsigned short d_8to16table[256]; -unsigned int d_8to24table[256]; -unsigned char d_15to8table[65536]; - -static qboolean usedga = false; - -#define stringify(m) { #m, m } - -cvar_t vid_mode = {"vid_mode","0"}; - -cvar_t mouse_button_commands[3] = -{ - {"mouse1","+attack"}, - {"mouse2","+strafe"}, - {"mouse3","+forward"}, -}; - -static int mouse_buttons=3; -static int mouse_buttonstate; -static int mouse_oldbuttonstate; -static float mouse_x, mouse_y; -static float p_mouse_x, p_mouse_y; -static float old_mouse_x, old_mouse_y; - -cvar_t _windowed_mouse = {"_windowed_mouse","1",CVAR_ARCHIVE}; -cvar_t m_filter = {"m_filter","0"}; -static float old_windowed_mouse; - -static int scr_width, scr_height; - -#define KEY_MASK (KeyPressMask | KeyReleaseMask) -#define MOUSE_MASK (ButtonPressMask | ButtonReleaseMask | \ - PointerMotionMask | ButtonMotionMask) - -/*-----------------------------------------------------------------------*/ - -//int texture_mode = GL_NEAREST; -//int texture_mode = GL_NEAREST_MIPMAP_NEAREST; -//int texture_mode = GL_NEAREST_MIPMAP_LINEAR; -int texture_mode = GL_LINEAR; -//int texture_mode = GL_LINEAR_MIPMAP_NEAREST; -//int texture_mode = GL_LINEAR_MIPMAP_LINEAR; - -int texture_extension_number = 1; - -float gldepthmin, gldepthmax; - -cvar_t gl_ztrick = {"gl_ztrick","1"}; - -const char *gl_vendor; -const char *gl_renderer; -const char *gl_version; -const char *gl_extensions; - -qboolean is8bit = false; -qboolean isPermedia = false; -qboolean gl_mtexable = false; - -/*-----------------------------------------------------------------------*/ -void D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height) -{ -} - -void D_EndDirectRect (int x, int y, int width, int height) -{ -} - -/* -================= -VID_Gamma_f - -Keybinding command -================= -*/ -void VID_Gamma_f (void) -{ - float gamma, f, inf; - unsigned char palette[768]; - int i; - - if (Cmd_Argc () == 2) - { - gamma = Q_atof (Cmd_Argv(1)); - - for (i=0 ; i<768 ; i++) - { - f = pow ( (host_basepal[i]+1)/256.0 , gamma ); - inf = f*255 + 0.5; - if (inf < 0) - inf = 0; - if (inf > 255) - inf = 255; - palette[i] = inf; - } - - VID_SetPalette (palette); - - vid.recalc_refdef = 1; // force a surface cache flush - } -} - -void VID_Shutdown(void) -{ - if (!ctx) - return; - - XUngrabPointer(dpy,CurrentTime); - XUngrabKeyboard(dpy,CurrentTime); - - glXDestroyContext(dpy,ctx); - -#ifdef USE_DGA - if (usedga) - XF86DGADirectVideo(dpy,DefaultScreen(dpy),0); -#endif -} - -int XLateKey(XKeyEvent *ev) -{ - - int key; - char buf[64]; - KeySym keysym; - - key = 0; - - XLookupString(ev, buf, sizeof buf, &keysym, 0); - - switch(keysym) - { - case XK_KP_Page_Up: - case XK_Page_Up: key = K_PGUP; break; - - case XK_KP_Page_Down: - case XK_Page_Down: key = K_PGDN; break; - - case XK_KP_Home: - case XK_Home: key = K_HOME; break; - - case XK_KP_End: - case XK_End: key = K_END; break; - - case XK_KP_Left: - case XK_Left: key = K_LEFTARROW; break; - - case XK_KP_Right: - case XK_Right: key = K_RIGHTARROW; break; - - case XK_KP_Down: - case XK_Down: key = K_DOWNARROW; break; - - case XK_KP_Up: - case XK_Up: key = K_UPARROW; break; - - case XK_Escape: key = K_ESCAPE; break; - - case XK_KP_Enter: - case XK_Return: key = K_ENTER; break; - - case XK_Tab: key = K_TAB; break; - - case XK_F1: key = K_F1; break; - - case XK_F2: key = K_F2; break; - - case XK_F3: key = K_F3; break; - - case XK_F4: key = K_F4; break; - - case XK_F5: key = K_F5; break; - - case XK_F6: key = K_F6; break; - - case XK_F7: key = K_F7; break; - - case XK_F8: key = K_F8; break; - - case XK_F9: key = K_F9; break; - - case XK_F10: key = K_F10; break; - - case XK_F11: key = K_F11; break; - - case XK_F12: key = K_F12; break; - - case XK_BackSpace: key = K_BACKSPACE; break; - - case XK_KP_Delete: - case XK_Delete: key = K_DEL; break; - - case XK_Pause: key = K_PAUSE; break; - - case XK_Shift_L: - case XK_Shift_R: key = K_SHIFT; break; - - case XK_Execute: - case XK_Control_L: - case XK_Control_R: key = K_CTRL; break; - - case XK_Alt_L: - case XK_Meta_L: - case XK_Alt_R: - case XK_Meta_R: key = K_ALT; break; - - case XK_KP_Begin: key = K_AUX30; break; - - case XK_Insert: - case XK_KP_Insert: key = K_INS; break; - - case XK_KP_Multiply: key = '*'; break; - case XK_KP_Add: key = '+'; break; - case XK_KP_Subtract: key = '-'; break; - case XK_KP_Divide: key = '/'; break; - -#if 0 - case 0x021: key = '1';break;/* [!] */ - case 0x040: key = '2';break;/* [@] */ - case 0x023: key = '3';break;/* [#] */ - case 0x024: key = '4';break;/* [$] */ - case 0x025: key = '5';break;/* [%] */ - case 0x05e: key = '6';break;/* [^] */ - case 0x026: key = '7';break;/* [&] */ - case 0x02a: key = '8';break;/* [*] */ - case 0x028: key = '9';;break;/* [(] */ - case 0x029: key = '0';break;/* [)] */ - case 0x05f: key = '-';break;/* [_] */ - case 0x02b: key = '=';break;/* [+] */ - case 0x07c: key = '\'';break;/* [|] */ - case 0x07d: key = '[';break;/* [}] */ - case 0x07b: key = ']';break;/* [{] */ - case 0x022: key = '\'';break;/* ["] */ - case 0x03a: key = ';';break;/* [:] */ - case 0x03f: key = '/';break;/* [?] */ - case 0x03e: key = '.';break;/* [>] */ - case 0x03c: key = ',';break;/* [<] */ -#endif - - default: - key = *(unsigned char*)buf; - if (key >= 'A' && key <= 'Z') - key = key - 'A' + 'a'; -// fprintf(stdout, "case 0x0%x: key = ___;break;/* [%c] */\n", keysym); - break; - } - - return key; -} - -struct -{ - int key; - int down; -} keyq[64]; -int keyq_head=0; -int keyq_tail=0; - -int config_notify=0; -int config_notify_width; -int config_notify_height; - -qboolean Keyboard_Update(void) -{ - XEvent x_event; - - if(!XCheckMaskEvent(dpy,KEY_MASK,&x_event)) - return false; - - switch(x_event.type) { - case KeyPress: - keyq[keyq_head].key = XLateKey(&x_event.xkey); - keyq[keyq_head].down = true; - keyq_head = (keyq_head + 1) & 63; - break; - case KeyRelease: - keyq[keyq_head].key = XLateKey(&x_event.xkey); - keyq[keyq_head].down = false; - keyq_head = (keyq_head + 1) & 63; - break; - } - - return true; -} - -qboolean Mouse_Update(void) -{ - XEvent x_event; - int b; - - if(!XCheckMaskEvent(dpy,MOUSE_MASK,&x_event)) - return false; - - switch(x_event.type) { - case MotionNotify: - if (usedga) { - mouse_x += x_event.xmotion.x_root; - mouse_y += x_event.xmotion.y_root; - } else if (_windowed_mouse.value) { - mouse_x += (float) ((int)x_event.xmotion.x - (int)(scr_width/2)); - mouse_y += (float) ((int)x_event.xmotion.y - (int)(scr_height/2)); - - /* move the mouse to the window center again */ - XSelectInput(dpy,win, (KEY_MASK | MOUSE_MASK) & ~PointerMotionMask); - XWarpPointer(dpy,None,win,0,0,0,0, (scr_width/2),(scr_height/2)); - XSelectInput(dpy,win, KEY_MASK | MOUSE_MASK); - } else { - mouse_x = (float) (x_event.xmotion.x-p_mouse_x); - mouse_y = (float) (x_event.xmotion.y-p_mouse_y); - p_mouse_x=x_event.xmotion.x; - p_mouse_y=x_event.xmotion.y; - } - break; - - case ButtonPress: - b=-1; - if (x_event.xbutton.button == 1) - b = 0; - else if (x_event.xbutton.button == 2) - b = 2; - else if (x_event.xbutton.button == 3) - b = 1; - if (b>=0) - mouse_buttonstate |= 1<=0) - mouse_buttonstate &= ~(1<> 2)+4; - b = ((i & 0x7C00) >> 7)+4; - pal = (unsigned char *)d_8to24table; - for (v=0,k=0,bestdist=10000.0; v<256; v++,pal+=4) { - r1 = (int)r - (int)pal[0]; - g1 = (int)g - (int)pal[1]; - b1 = (int)b - (int)pal[2]; - dist = sqrt(((r1*r1)+(g1*g1)+(b1*b1))); - if (dist < bestdist) { - k=v; - bestdist = dist; - } - } - d_15to8table[i]=k; - } - sprintf(s, "%s/glquake", com_gamedir); - Sys_mkdir (s); - sprintf(s, "%s/glquake/15to8.pal", com_gamedir); - if ((f = fopen(s, "wb")) != NULL) { - fwrite(d_15to8table, 1<<15, 1, f); - fclose(f); - } - } -} - -/* -=============== -GL_Init -=============== -*/ -void GL_Init (void) -{ - gl_vendor = glGetString (GL_VENDOR); - Con_Printf ("GL_VENDOR: %s\n", gl_vendor); - gl_renderer = glGetString (GL_RENDERER); - Con_Printf ("GL_RENDERER: %s\n", gl_renderer); - - gl_version = glGetString (GL_VERSION); - Con_Printf ("GL_VERSION: %s\n", gl_version); - gl_extensions = glGetString (GL_EXTENSIONS); - Con_Printf ("GL_EXTENSIONS: %s\n", gl_extensions); - -// Con_Printf ("%s %s\n", gl_renderer, gl_version); - - glClearColor (1,0,0,0); - glCullFace(GL_FRONT); - glEnable(GL_TEXTURE_2D); - - glEnable(GL_ALPHA_TEST); - glAlphaFunc(GL_GREATER, 0.666); - - glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); - glShadeModel (GL_FLAT); - - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - - glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - -// glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); -} - -/* -================= -GL_BeginRendering - -================= -*/ -void GL_BeginRendering (int *x, int *y, int *width, int *height) -{ - extern cvar_t gl_clear; - - *x = *y = 0; - *width = scr_width; - *height = scr_height; - -// if (!wglMakeCurrent( maindc, baseRC )) -// Sys_Error ("wglMakeCurrent failed"); - -// glViewport (*x, *y, *width, *height); -} - - -void GL_EndRendering (void) -{ - glFlush(); - glXSwapBuffers(dpy,win); -} - -qboolean VID_Is8bit(void) -{ - return is8bit; -} - -#ifdef GL_EXT_SHARED -void VID_Init8bitPalette() -{ - // Check for 8bit Extensions and initialize them. - int i; - char thePalette[256*3]; - char *oldPalette, *newPalette; - - if (strstr(gl_extensions, "GL_EXT_shared_texture_palette") == NULL) - return; - - Con_SafePrintf("8-bit GL extensions enabled.\n"); - glEnable( GL_SHARED_TEXTURE_PALETTE_EXT ); - oldPalette = (char *) d_8to24table; //d_8to24table3dfx; - newPalette = thePalette; - for (i=0;i<256;i++) { - *newPalette++ = *oldPalette++; - *newPalette++ = *oldPalette++; - *newPalette++ = *oldPalette++; - oldPalette++; - } - glColorTableEXT(GL_SHARED_TEXTURE_PALETTE_EXT, GL_RGB, 256, GL_RGB, GL_UNSIGNED_BYTE, (void *) thePalette); - is8bit = true; -} - -#else -extern void gl3DfxSetPaletteEXT(GLuint *pal); - -void VID_Init8bitPalette(void) -{ - // Check for 8bit Extensions and initialize them. - int i; - GLubyte table[256][4]; - char *oldpal; - - if (strstr(gl_extensions, "3DFX_set_global_palette") == NULL) - return; - - Con_SafePrintf("8-bit GL extensions enabled.\n"); - glEnable( GL_SHARED_TEXTURE_PALETTE_EXT ); - oldpal = (char *) d_8to24table; //d_8to24table3dfx; - for (i=0;i<256;i++) { - table[i][2] = *oldpal++; - table[i][1] = *oldpal++; - table[i][0] = *oldpal++; - table[i][3] = 255; - oldpal++; - } - gl3DfxSetPaletteEXT((GLuint *)table); - is8bit = true; -} -#endif - -void VID_Init(unsigned char *palette) -{ - int i; - char gldir[MAX_OSPATH]; - int width = 640, height = 480; - int attrib[] = { - GLX_RGBA, - GLX_RED_SIZE, 1, - GLX_GREEN_SIZE, 1, - GLX_BLUE_SIZE, 1, - GLX_DOUBLEBUFFER, - GLX_DEPTH_SIZE, 1, - None }; - int scrnum; - XSetWindowAttributes attr; - unsigned long mask; - Window root; - XVisualInfo *visinfo; - - S_Init(); - - Cvar_RegisterVariable (&vid_mode); - Cvar_RegisterVariable (&gl_ztrick); - - vid.maxwarpwidth = WARP_WIDTH; - vid.maxwarpheight = WARP_HEIGHT; - vid.colormap = host_colormap; - vid.fullbright = 256 - LittleLong (*((int *)vid.colormap + 2048)); - -// interpret command-line params - -// set vid parameters - - if ((i = COM_CheckParm("-width")) != 0) - width = atoi(com_argv[i+1]); - if ((i = COM_CheckParm("-height")) != 0) - height = atoi(com_argv[i+1]); - - if ((i = COM_CheckParm("-conwidth")) != 0) - vid.conwidth = Q_atoi(com_argv[i+1]); - else - vid.conwidth = 640; - - vid.conwidth &= 0xfff8; // make it a multiple of eight - - if (vid.conwidth < 320) - vid.conwidth = 320; - - // pick a conheight that matches with correct aspect - vid.conheight = vid.conwidth*3 / 4; - - if ((i = COM_CheckParm("-conheight")) != 0) - vid.conheight = Q_atoi(com_argv[i+1]); - if (vid.conheight < 200) - vid.conheight = 200; - - if (!(dpy = XOpenDisplay(NULL))) { - fprintf(stderr, "Error couldn't open the X display\n"); - exit(1); - } - - scrnum = DefaultScreen(dpy); - root = RootWindow(dpy, scrnum); - - visinfo=glXChooseVisual(dpy,scrnum,attrib); - if (!visinfo) { - fprintf(stderr, "Error couldn't get an RGB, Double-buffered, Depth visual\n"); - exit(1); - } - - /* window attributes */ - attr.background_pixel=0; - attr.border_pixel=0; - attr.colormap=XCreateColormap(dpy,root,visinfo->visual,AllocNone); - attr.event_mask=KEY_MASK|MOUSE_MASK|VisibilityChangeMask; - mask=CWBackPixel|CWBorderPixel|CWColormap|CWEventMask; - - win=XCreateWindow(dpy,root,0,0,width,height, - 0,visinfo->depth,InputOutput, - visinfo->visual,mask,&attr); - XMapWindow(dpy,win); - - XMoveWindow(dpy,win,0,0); - - XFlush(dpy); - - if (COM_CheckParm("-window")) - putenv("MESA_GLX_FX=window"); - else - putenv("MESA_GLX_FX=fullscreen"); - - ctx = glXCreateContext(dpy,visinfo,NULL,True); - - if (!ctx) { - fprintf(stderr, "Unable to create glX context.\n"); - exit(1); - } - - glXMakeCurrent(dpy,win,ctx); - - scr_width = width; - scr_height = height; - - if (vid.conheight > height) - vid.conheight = height; - if (vid.conwidth > width) - vid.conwidth = width; - vid.width = vid.conwidth; - vid.height = vid.conheight; - - vid.aspect = ((float)vid.height / (float)vid.width) * - (320.0 / 240.0); - vid.numpages = 2; - - InitSig(); // trap evil signals - - GL_Init(); - - sprintf (gldir, "%s/glquake", com_gamedir); - Sys_mkdir (gldir); - - VID_SetPalette(palette); - - // Check for 3DFX Extensions and initialize them. - VID_Init8bitPalette(); - - Con_SafePrintf ("Video mode %dx%d initialized.\n", width, height); - - vid.recalc_refdef = 1; // force a surface cache flush -} - -void Sys_SendKeyEvents(void) -{ - if (dpy) - { - while (Keyboard_Update()) - ; - - while (keyq_head != keyq_tail) - { - Key_Event(keyq[keyq_tail].key, keyq[keyq_tail].down); - keyq_tail = (keyq_tail + 1) & 63; - } - } -} - -void Force_CenterView_f (void) -{ - cl.viewangles[PITCH] = 0; -} - - -void IN_Init(void) -{ - Cvar_RegisterVariable (&_windowed_mouse); - Cvar_RegisterVariable (&m_filter); - Cvar_RegisterVariable (&mouse_button_commands[0]); - Cvar_RegisterVariable (&mouse_button_commands[1]); - Cvar_RegisterVariable (&mouse_button_commands[2]); - Cmd_AddCommand ("force_centerview", Force_CenterView_f); -} - -void IN_Shutdown(void) -{ -} - -/* -=========== -IN_Commands -=========== -*/ -void IN_Commands (void) -{ - int i; - - for (i=0 ; isidemove += m_side.value * mouse_x; - else - cl.viewangles[YAW] -= m_yaw.value * mouse_x; - if (in_mlook.state & 1) - V_StopPitchDrift (); - - if ( (in_mlook.state & 1) && !(in_strafe.state & 1)) { - cl.viewangles[PITCH] += m_pitch.value * mouse_y; - if (cl.viewangles[PITCH] > 80) - cl.viewangles[PITCH] = 80; - if (cl.viewangles[PITCH] < -70) - cl.viewangles[PITCH] = -70; - } else { - cmd->forwardmove -= m_forward.value * mouse_y; - } - mouse_x = mouse_y = 0.0; -} - - -void VID_LockBuffer (void) {} -void VID_UnlockBuffer (void) {} - +/* +Copyright (C) 1996-1997 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 +#include +#include +#include +#include +#include +#include + +//#include +#include +#include +#include +#include + +#include "GL/gl.h" +#include "GL/glx.h" + +#include "quakedef.h" + +#define WARP_WIDTH 320 +#define WARP_HEIGHT 200 + +static Display *dpy = NULL; +static Window win; +static GLXContext ctx = NULL; + +unsigned short d_8to16table[256]; +unsigned int d_8to24table[256]; +unsigned char d_15to8table[65536]; + +static qboolean usedga = false; + +#define stringify(m) { #m, m } + +cvar_t vid_mode = {"vid_mode","0"}; + +cvar_t mouse_button_commands[3] = +{ + {"mouse1","+attack"}, + {"mouse2","+strafe"}, + {"mouse3","+forward"}, +}; + +static int mouse_buttons=3; +static int mouse_buttonstate; +static int mouse_oldbuttonstate; +static float mouse_x, mouse_y; +static float p_mouse_x, p_mouse_y; +static float old_mouse_x, old_mouse_y; + +cvar_t _windowed_mouse = {"_windowed_mouse","1",CVAR_ARCHIVE}; +cvar_t m_filter = {"m_filter","0"}; +static float old_windowed_mouse; + +static int scr_width, scr_height; + +#define KEY_MASK (KeyPressMask | KeyReleaseMask) +#define MOUSE_MASK (ButtonPressMask | ButtonReleaseMask | \ + PointerMotionMask | ButtonMotionMask) + +/*-----------------------------------------------------------------------*/ + +//int texture_mode = GL_NEAREST; +//int texture_mode = GL_NEAREST_MIPMAP_NEAREST; +//int texture_mode = GL_NEAREST_MIPMAP_LINEAR; +int texture_mode = GL_LINEAR; +//int texture_mode = GL_LINEAR_MIPMAP_NEAREST; +//int texture_mode = GL_LINEAR_MIPMAP_LINEAR; + +int texture_extension_number = 1; + +float gldepthmin, gldepthmax; + +cvar_t gl_ztrick = {"gl_ztrick","1"}; + +const char *gl_vendor; +const char *gl_renderer; +const char *gl_version; +const char *gl_extensions; + +qboolean is8bit = false; +qboolean isPermedia = false; +qboolean gl_mtexable = false; + +/*-----------------------------------------------------------------------*/ +void D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height) +{ +} + +void D_EndDirectRect (int x, int y, int width, int height) +{ +} + +/* +================= +VID_Gamma_f + +Keybinding command +================= +*/ +void VID_Gamma_f (void) +{ + float gamma, f, inf; + unsigned char palette[768]; + int i; + + if (Cmd_Argc () == 2) + { + gamma = Q_atof (Cmd_Argv(1)); + + for (i=0 ; i<768 ; i++) + { + f = pow ( (host_basepal[i]+1)/256.0 , gamma ); + inf = f*255 + 0.5; + if (inf < 0) + inf = 0; + if (inf > 255) + inf = 255; + palette[i] = inf; + } + + VID_SetPalette (palette); + + vid.recalc_refdef = 1; // force a surface cache flush + } +} + +void VID_Shutdown(void) +{ + if (!ctx) + return; + + XUngrabPointer(dpy,CurrentTime); + XUngrabKeyboard(dpy,CurrentTime); + + glXDestroyContext(dpy,ctx); + +#ifdef USE_DGA + if (usedga) + XF86DGADirectVideo(dpy,DefaultScreen(dpy),0); +#endif +} + +int XLateKey(XKeyEvent *ev) +{ + + int key; + char buf[64]; + KeySym keysym; + + key = 0; + + XLookupString(ev, buf, sizeof buf, &keysym, 0); + + switch(keysym) + { + case XK_KP_Page_Up: + case XK_Page_Up: key = K_PGUP; break; + + case XK_KP_Page_Down: + case XK_Page_Down: key = K_PGDN; break; + + case XK_KP_Home: + case XK_Home: key = K_HOME; break; + + case XK_KP_End: + case XK_End: key = K_END; break; + + case XK_KP_Left: + case XK_Left: key = K_LEFTARROW; break; + + case XK_KP_Right: + case XK_Right: key = K_RIGHTARROW; break; + + case XK_KP_Down: + case XK_Down: key = K_DOWNARROW; break; + + case XK_KP_Up: + case XK_Up: key = K_UPARROW; break; + + case XK_Escape: key = K_ESCAPE; break; + + case XK_KP_Enter: + case XK_Return: key = K_ENTER; break; + + case XK_Tab: key = K_TAB; break; + + case XK_F1: key = K_F1; break; + + case XK_F2: key = K_F2; break; + + case XK_F3: key = K_F3; break; + + case XK_F4: key = K_F4; break; + + case XK_F5: key = K_F5; break; + + case XK_F6: key = K_F6; break; + + case XK_F7: key = K_F7; break; + + case XK_F8: key = K_F8; break; + + case XK_F9: key = K_F9; break; + + case XK_F10: key = K_F10; break; + + case XK_F11: key = K_F11; break; + + case XK_F12: key = K_F12; break; + + case XK_BackSpace: key = K_BACKSPACE; break; + + case XK_KP_Delete: + case XK_Delete: key = K_DEL; break; + + case XK_Pause: key = K_PAUSE; break; + + case XK_Shift_L: + case XK_Shift_R: key = K_SHIFT; break; + + case XK_Execute: + case XK_Control_L: + case XK_Control_R: key = K_CTRL; break; + + case XK_Alt_L: + case XK_Meta_L: + case XK_Alt_R: + case XK_Meta_R: key = K_ALT; break; + + case XK_KP_Begin: key = K_AUX30; break; + + case XK_Insert: + case XK_KP_Insert: key = K_INS; break; + + case XK_KP_Multiply: key = '*'; break; + case XK_KP_Add: key = '+'; break; + case XK_KP_Subtract: key = '-'; break; + case XK_KP_Divide: key = '/'; break; + +#if 0 + case 0x021: key = '1';break;/* [!] */ + case 0x040: key = '2';break;/* [@] */ + case 0x023: key = '3';break;/* [#] */ + case 0x024: key = '4';break;/* [$] */ + case 0x025: key = '5';break;/* [%] */ + case 0x05e: key = '6';break;/* [^] */ + case 0x026: key = '7';break;/* [&] */ + case 0x02a: key = '8';break;/* [*] */ + case 0x028: key = '9';;break;/* [(] */ + case 0x029: key = '0';break;/* [)] */ + case 0x05f: key = '-';break;/* [_] */ + case 0x02b: key = '=';break;/* [+] */ + case 0x07c: key = '\'';break;/* [|] */ + case 0x07d: key = '[';break;/* [}] */ + case 0x07b: key = ']';break;/* [{] */ + case 0x022: key = '\'';break;/* ["] */ + case 0x03a: key = ';';break;/* [:] */ + case 0x03f: key = '/';break;/* [?] */ + case 0x03e: key = '.';break;/* [>] */ + case 0x03c: key = ',';break;/* [<] */ +#endif + + default: + key = *(unsigned char*)buf; + if (key >= 'A' && key <= 'Z') + key = key - 'A' + 'a'; +// fprintf(stdout, "case 0x0%x: key = ___;break;/* [%c] */\n", keysym); + break; + } + + return key; +} + +struct +{ + int key; + int down; +} keyq[64]; +int keyq_head=0; +int keyq_tail=0; + +int config_notify=0; +int config_notify_width; +int config_notify_height; + +qboolean Keyboard_Update(void) +{ + XEvent x_event; + + if(!XCheckMaskEvent(dpy,KEY_MASK,&x_event)) + return false; + + switch(x_event.type) { + case KeyPress: + keyq[keyq_head].key = XLateKey(&x_event.xkey); + keyq[keyq_head].down = true; + keyq_head = (keyq_head + 1) & 63; + break; + case KeyRelease: + keyq[keyq_head].key = XLateKey(&x_event.xkey); + keyq[keyq_head].down = false; + keyq_head = (keyq_head + 1) & 63; + break; + } + + return true; +} + +qboolean Mouse_Update(void) +{ + XEvent x_event; + int b; + + if(!XCheckMaskEvent(dpy,MOUSE_MASK,&x_event)) + return false; + + switch(x_event.type) { + case MotionNotify: + if (usedga) { + mouse_x += x_event.xmotion.x_root; + mouse_y += x_event.xmotion.y_root; + } else if (_windowed_mouse.value) { + mouse_x += (float) ((int)x_event.xmotion.x - (int)(scr_width/2)); + mouse_y += (float) ((int)x_event.xmotion.y - (int)(scr_height/2)); + + /* move the mouse to the window center again */ + XSelectInput(dpy,win, (KEY_MASK | MOUSE_MASK) & ~PointerMotionMask); + XWarpPointer(dpy,None,win,0,0,0,0, (scr_width/2),(scr_height/2)); + XSelectInput(dpy,win, KEY_MASK | MOUSE_MASK); + } else { + mouse_x = (float) (x_event.xmotion.x-p_mouse_x); + mouse_y = (float) (x_event.xmotion.y-p_mouse_y); + p_mouse_x=x_event.xmotion.x; + p_mouse_y=x_event.xmotion.y; + } + break; + + case ButtonPress: + b=-1; + if (x_event.xbutton.button == 1) + b = 0; + else if (x_event.xbutton.button == 2) + b = 2; + else if (x_event.xbutton.button == 3) + b = 1; + if (b>=0) + mouse_buttonstate |= 1<=0) + mouse_buttonstate &= ~(1<> 2)+4; + b = ((i & 0x7C00) >> 7)+4; + pal = (unsigned char *)d_8to24table; + for (v=0,k=0,bestdist=10000.0; v<256; v++,pal+=4) { + r1 = (int)r - (int)pal[0]; + g1 = (int)g - (int)pal[1]; + b1 = (int)b - (int)pal[2]; + dist = sqrt(((r1*r1)+(g1*g1)+(b1*b1))); + if (dist < bestdist) { + k=v; + bestdist = dist; + } + } + d_15to8table[i]=k; + } + sprintf(s, "%s/glquake", com_gamedir); + Sys_mkdir (s); + sprintf(s, "%s/glquake/15to8.pal", com_gamedir); + if ((f = fopen(s, "wb")) != NULL) { + fwrite(d_15to8table, 1<<15, 1, f); + fclose(f); + } + } +} + +/* +=============== +GL_Init +=============== +*/ +void GL_Init (void) +{ + gl_vendor = glGetString (GL_VENDOR); + Con_Printf ("GL_VENDOR: %s\n", gl_vendor); + gl_renderer = glGetString (GL_RENDERER); + Con_Printf ("GL_RENDERER: %s\n", gl_renderer); + + gl_version = glGetString (GL_VERSION); + Con_Printf ("GL_VERSION: %s\n", gl_version); + gl_extensions = glGetString (GL_EXTENSIONS); + Con_Printf ("GL_EXTENSIONS: %s\n", gl_extensions); + +// Con_Printf ("%s %s\n", gl_renderer, gl_version); + + glClearColor (1,0,0,0); + glCullFace(GL_FRONT); + glEnable(GL_TEXTURE_2D); + + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_GREATER, 0.666); + + glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); + glShadeModel (GL_FLAT); + + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + +// glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); +} + +/* +================= +GL_BeginRendering + +================= +*/ +void GL_BeginRendering (int *x, int *y, int *width, int *height) +{ + extern cvar_t gl_clear; + + *x = *y = 0; + *width = scr_width; + *height = scr_height; + +// if (!wglMakeCurrent( maindc, baseRC )) +// Sys_Error ("wglMakeCurrent failed"); + +// glViewport (*x, *y, *width, *height); +} + + +void GL_EndRendering (void) +{ + glFlush(); + glXSwapBuffers(dpy,win); +} + +qboolean VID_Is8bit(void) +{ + return is8bit; +} + +#ifdef GL_EXT_SHARED +void VID_Init8bitPalette() +{ + // Check for 8bit Extensions and initialize them. + int i; + char thePalette[256*3]; + char *oldPalette, *newPalette; + + if (strstr(gl_extensions, "GL_EXT_shared_texture_palette") == NULL) + return; + + Con_SafePrintf("8-bit GL extensions enabled.\n"); + glEnable( GL_SHARED_TEXTURE_PALETTE_EXT ); + oldPalette = (char *) d_8to24table; //d_8to24table3dfx; + newPalette = thePalette; + for (i=0;i<256;i++) { + *newPalette++ = *oldPalette++; + *newPalette++ = *oldPalette++; + *newPalette++ = *oldPalette++; + oldPalette++; + } + glColorTableEXT(GL_SHARED_TEXTURE_PALETTE_EXT, GL_RGB, 256, GL_RGB, GL_UNSIGNED_BYTE, (void *) thePalette); + is8bit = true; +} + +#else +extern void gl3DfxSetPaletteEXT(GLuint *pal); + +void VID_Init8bitPalette(void) +{ + // Check for 8bit Extensions and initialize them. + int i; + GLubyte table[256][4]; + char *oldpal; + + if (strstr(gl_extensions, "3DFX_set_global_palette") == NULL) + return; + + Con_SafePrintf("8-bit GL extensions enabled.\n"); + glEnable( GL_SHARED_TEXTURE_PALETTE_EXT ); + oldpal = (char *) d_8to24table; //d_8to24table3dfx; + for (i=0;i<256;i++) { + table[i][2] = *oldpal++; + table[i][1] = *oldpal++; + table[i][0] = *oldpal++; + table[i][3] = 255; + oldpal++; + } + gl3DfxSetPaletteEXT((GLuint *)table); + is8bit = true; +} +#endif + +void VID_Init(unsigned char *palette) +{ + int i; + char gldir[MAX_OSPATH]; + int width = 640, height = 480; + int attrib[] = { + GLX_RGBA, + GLX_RED_SIZE, 1, + GLX_GREEN_SIZE, 1, + GLX_BLUE_SIZE, 1, + GLX_DOUBLEBUFFER, + GLX_DEPTH_SIZE, 1, + None }; + int scrnum; + XSetWindowAttributes attr; + unsigned long mask; + Window root; + XVisualInfo *visinfo; + + S_Init(); + + Cvar_RegisterVariable (&vid_mode); + Cvar_RegisterVariable (&gl_ztrick); + + vid.maxwarpwidth = WARP_WIDTH; + vid.maxwarpheight = WARP_HEIGHT; + vid.colormap = host_colormap; + vid.fullbright = 256 - LittleLong (*((int *)vid.colormap + 2048)); + +// interpret command-line params + +// set vid parameters + + if ((i = COM_CheckParm("-width")) != 0) + width = atoi(com_argv[i+1]); + if ((i = COM_CheckParm("-height")) != 0) + height = atoi(com_argv[i+1]); + + if ((i = COM_CheckParm("-conwidth")) != 0) + vid.conwidth = Q_atoi(com_argv[i+1]); + else + vid.conwidth = 640; + + vid.conwidth &= 0xfff8; // make it a multiple of eight + + if (vid.conwidth < 320) + vid.conwidth = 320; + + // pick a conheight that matches with correct aspect + vid.conheight = vid.conwidth*3 / 4; + + if ((i = COM_CheckParm("-conheight")) != 0) + vid.conheight = Q_atoi(com_argv[i+1]); + if (vid.conheight < 200) + vid.conheight = 200; + + if (!(dpy = XOpenDisplay(NULL))) { + fprintf(stderr, "Error couldn't open the X display\n"); + exit(1); + } + + scrnum = DefaultScreen(dpy); + root = RootWindow(dpy, scrnum); + + visinfo=glXChooseVisual(dpy,scrnum,attrib); + if (!visinfo) { + fprintf(stderr, "Error couldn't get an RGB, Double-buffered, Depth visual\n"); + exit(1); + } + + /* window attributes */ + attr.background_pixel=0; + attr.border_pixel=0; + attr.colormap=XCreateColormap(dpy,root,visinfo->visual,AllocNone); + attr.event_mask=KEY_MASK|MOUSE_MASK|VisibilityChangeMask; + mask=CWBackPixel|CWBorderPixel|CWColormap|CWEventMask; + + win=XCreateWindow(dpy,root,0,0,width,height, + 0,visinfo->depth,InputOutput, + visinfo->visual,mask,&attr); + XMapWindow(dpy,win); + + XMoveWindow(dpy,win,0,0); + + XFlush(dpy); + + if (COM_CheckParm("-window")) + putenv("MESA_GLX_FX=window"); + else + putenv("MESA_GLX_FX=fullscreen"); + + ctx = glXCreateContext(dpy,visinfo,NULL,True); + + if (!ctx) { + fprintf(stderr, "Unable to create glX context.\n"); + exit(1); + } + + glXMakeCurrent(dpy,win,ctx); + + scr_width = width; + scr_height = height; + + if (vid.conheight > height) + vid.conheight = height; + if (vid.conwidth > width) + vid.conwidth = width; + vid.width = vid.conwidth; + vid.height = vid.conheight; + + vid.aspect = ((float)vid.height / (float)vid.width) * + (320.0 / 240.0); + vid.numpages = 2; + + InitSig(); // trap evil signals + + GL_Init(); + + sprintf (gldir, "%s/glquake", com_gamedir); + Sys_mkdir (gldir); + + VID_SetPalette(palette); + + // Check for 3DFX Extensions and initialize them. + VID_Init8bitPalette(); + + Con_SafePrintf ("Video mode %dx%d initialized.\n", width, height); + + vid.recalc_refdef = 1; // force a surface cache flush +} + +void Sys_SendKeyEvents(void) +{ + if (dpy) + { + while (Keyboard_Update()) + ; + + while (keyq_head != keyq_tail) + { + Key_Event(keyq[keyq_tail].key, keyq[keyq_tail].down); + keyq_tail = (keyq_tail + 1) & 63; + } + } +} + +void Force_CenterView_f (void) +{ + cl.viewangles[PITCH] = 0; +} + + +void IN_Init(void) +{ + Cvar_RegisterVariable (&_windowed_mouse); + Cvar_RegisterVariable (&m_filter); + Cvar_RegisterVariable (&mouse_button_commands[0]); + Cvar_RegisterVariable (&mouse_button_commands[1]); + Cvar_RegisterVariable (&mouse_button_commands[2]); + Cmd_AddCommand ("force_centerview", Force_CenterView_f); +} + +void IN_Shutdown(void) +{ +} + +/* +=========== +IN_Commands +=========== +*/ +void IN_Commands (void) +{ + int i; + + for (i=0 ; isidemove += m_side.value * mouse_x; + else + cl.viewangles[YAW] -= m_yaw.value * mouse_x; + if (in_mlook.state & 1) + V_StopPitchDrift (); + + if ( (in_mlook.state & 1) && !(in_strafe.state & 1)) { + cl.viewangles[PITCH] += m_pitch.value * mouse_y; + if (cl.viewangles[PITCH] > 80) + cl.viewangles[PITCH] = 80; + if (cl.viewangles[PITCH] < -70) + cl.viewangles[PITCH] = -70; + } else { + cmd->forwardmove -= m_forward.value * mouse_y; + } + mouse_x = mouse_y = 0.0; +} + + +void VID_LockBuffer (void) {} +void VID_UnlockBuffer (void) {} + diff --git a/source/gl_vidlinuxglx.c b/source/gl_vidlinuxglx.c index cd8eb6f6..713830a1 100644 --- a/source/gl_vidlinuxglx.c +++ b/source/gl_vidlinuxglx.c @@ -1,786 +1,788 @@ -/* -Copyright (C) 1996-1997 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 -#include -#include -#include -#include -#include - -#include "quakedef.h" - -#include - -#include -#include - -#ifdef USE_DGA -#include -#endif - - -#define WARP_WIDTH 320 -#define WARP_HEIGHT 200 - -static Display *dpy = NULL; -static Window win; -static GLXContext ctx = NULL; - -static float old_windowed_mouse = 0; - -#define KEY_MASK (KeyPressMask | KeyReleaseMask) -#define MOUSE_MASK (ButtonPressMask | ButtonReleaseMask | \ - PointerMotionMask) - -#define X_MASK (KEY_MASK | MOUSE_MASK | VisibilityChangeMask) - -unsigned short d_8to16table[256]; -unsigned d_8to24table[256]; -unsigned char d_15to8table[65536]; - -cvar_t _windowed_mouse = {"_windowed_mouse","0",CVAR_ARCHIVE}; -cvar_t vid_mode = {"vid_mode","0"}; - -static float mouse_x, mouse_y; -static float old_mouse_x, old_mouse_y; - -cvar_t m_filter = {"m_filter", "0"}; - -static int scr_width, scr_height; - -/*-----------------------------------------------------------------------*/ - -//int texture_mode = GL_NEAREST; -//int texture_mode = GL_NEAREST_MIPMAP_NEAREST; -//int texture_mode = GL_NEAREST_MIPMAP_LINEAR; -int texture_mode = GL_LINEAR; -//int texture_mode = GL_LINEAR_MIPMAP_NEAREST; -//int texture_mode = GL_LINEAR_MIPMAP_LINEAR; - -int texture_extension_number = 1; - -float gldepthmin, gldepthmax; - -cvar_t gl_ztrick = {"gl_ztrick","1"}; - -const char *gl_vendor; -const char *gl_renderer; -const char *gl_version; -const char *gl_extensions; - -qboolean is8bit = false; -qboolean isPermedia = false; -qboolean gl_mtexable = false; - -/*-----------------------------------------------------------------------*/ -void D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height) -{ -} - -void D_EndDirectRect (int x, int y, int width, int height) -{ -} - -static int XLateKey(XKeyEvent *ev) -{ - - int key; - char buf[64]; - KeySym keysym; - - key = 0; - - XLookupString(ev, buf, sizeof buf, &keysym, 0); - - switch(keysym) - { - case XK_KP_Page_Up: - case XK_Page_Up: key = K_PGUP; break; - - case XK_KP_Page_Down: - case XK_Page_Down: key = K_PGDN; break; - - case XK_KP_Home: - case XK_Home: key = K_HOME; break; - - case XK_KP_End: - case XK_End: key = K_END; break; - - case XK_KP_Left: - case XK_Left: key = K_LEFTARROW; break; - - case XK_KP_Right: - case XK_Right: key = K_RIGHTARROW; break; - - case XK_KP_Down: - case XK_Down: key = K_DOWNARROW; break; - - case XK_KP_Up: - case XK_Up: key = K_UPARROW; break; - - case XK_Escape: key = K_ESCAPE; break; - - case XK_KP_Enter: - case XK_Return: key = K_ENTER; break; - - case XK_Tab: key = K_TAB; break; - - case XK_F1: key = K_F1; break; - - case XK_F2: key = K_F2; break; - - case XK_F3: key = K_F3; break; - - case XK_F4: key = K_F4; break; - - case XK_F5: key = K_F5; break; - - case XK_F6: key = K_F6; break; - - case XK_F7: key = K_F7; break; - - case XK_F8: key = K_F8; break; - - case XK_F9: key = K_F9; break; - - case XK_F10: key = K_F10; break; - - case XK_F11: key = K_F11; break; - - case XK_F12: key = K_F12; break; - - case XK_BackSpace: key = K_BACKSPACE; break; - - case XK_KP_Delete: - case XK_Delete: key = K_DEL; break; - - case XK_Pause: key = K_PAUSE; break; - - case XK_Shift_L: - case XK_Shift_R: key = K_SHIFT; break; - - case XK_Execute: - case XK_Control_L: - case XK_Control_R: key = K_CTRL; break; - - case XK_Alt_L: - case XK_Meta_L: - case XK_Alt_R: - case XK_Meta_R: key = K_ALT; break; - - case XK_KP_Begin: key = '5'; break; - - case XK_KP_Insert: - case XK_Insert:key = K_INS; break; - - case XK_KP_Multiply: key = '*'; break; - case XK_KP_Add: key = '+'; break; - case XK_KP_Subtract: key = '-'; break; - case XK_KP_Divide: key = '/'; break; - -#if 0 - case 0x021: key = '1';break;/* [!] */ - case 0x040: key = '2';break;/* [@] */ - case 0x023: key = '3';break;/* [#] */ - case 0x024: key = '4';break;/* [$] */ - case 0x025: key = '5';break;/* [%] */ - case 0x05e: key = '6';break;/* [^] */ - case 0x026: key = '7';break;/* [&] */ - case 0x02a: key = '8';break;/* [*] */ - case 0x028: key = '9';;break;/* [(] */ - case 0x029: key = '0';break;/* [)] */ - case 0x05f: key = '-';break;/* [_] */ - case 0x02b: key = '=';break;/* [+] */ - case 0x07c: key = '\'';break;/* [|] */ - case 0x07d: key = '[';break;/* [}] */ - case 0x07b: key = ']';break;/* [{] */ - case 0x022: key = '\'';break;/* ["] */ - case 0x03a: key = ';';break;/* [:] */ - case 0x03f: key = '/';break;/* [?] */ - case 0x03e: key = '.';break;/* [>] */ - case 0x03c: key = ',';break;/* [<] */ -#endif - - default: - key = *(unsigned char*)buf; - if (key >= 'A' && key <= 'Z') - key = key - 'A' + 'a'; - break; - } - - return key; -} - -static void install_grabs(void) -{ - XGrabPointer(dpy, win, - True, - 0, - GrabModeAsync, GrabModeAsync, - win, - None, - CurrentTime); - -#ifdef USE_DGA - XF86DGADirectVideo(dpy, DefaultScreen(dpy), XF86DGADirectMouse); - dgamouse = 1; -#else - XWarpPointer(dpy, None, win, - 0, 0, 0, 0, - vid.width / 2, vid.height / 2); -#endif - - XGrabKeyboard(dpy, win, - False, - GrabModeAsync, GrabModeAsync, - CurrentTime); - -// XSync(dpy, True); -} - -static void uninstall_grabs(void) -{ -#ifdef USE_DGA - XF86DGADirectVideo(dpy, DefaultScreen(dpy), 0); - dgamouse = 0; -#endif - - XUngrabPointer(dpy, CurrentTime); - XUngrabKeyboard(dpy, CurrentTime); - -// XSync(dpy, True); -} - -static void GetEvent(void) -{ - XEvent event; - int b; - - if (!dpy) - return; - - XNextEvent(dpy, &event); - - switch (event.type) { - case KeyPress: - case KeyRelease: - Key_Event(XLateKey(&event.xkey), event.type == KeyPress); - break; - - case MotionNotify: -#ifdef USE_DGA - if (dgamouse && _windowed_mouse.value) { - mouse_x = event.xmotion.x_root; - mouse_y = event.xmotion.y_root; - } else -#endif - { - if (_windowed_mouse.value) { - mouse_x = (float) ((int)event.xmotion.x - (int)(vid.width/2)); - mouse_y = (float) ((int)event.xmotion.y - (int)(vid.height/2)); - - /* move the mouse to the window center again */ - XSelectInput(dpy, win, X_MASK & ~PointerMotionMask); - XWarpPointer(dpy, None, win, 0, 0, 0, 0, - (vid.width/2), (vid.height/2)); - XSelectInput(dpy, win, X_MASK); - } - } - break; - - case ButtonPress: - b=-1; - if (event.xbutton.button == 1) - b = 0; - else if (event.xbutton.button == 2) - b = 2; - else if (event.xbutton.button == 3) - b = 1; - if (b>=0) - Key_Event(K_MOUSE1 + b, true); - break; - - case ButtonRelease: - b=-1; - if (event.xbutton.button == 1) - b = 0; - else if (event.xbutton.button == 2) - b = 2; - else if (event.xbutton.button == 3) - b = 1; - if (b>=0) - Key_Event(K_MOUSE1 + b, false); - break; - } - - if (old_windowed_mouse != _windowed_mouse.value) { - old_windowed_mouse = _windowed_mouse.value; - - if (!_windowed_mouse.value) { - /* ungrab the pointer */ - uninstall_grabs(); - } else { - /* grab the pointer */ - install_grabs(); - } - } -} - - -void VID_Shutdown(void) -{ - if (!ctx) - return; - - glXDestroyContext(dpy, ctx); -} - -void signal_handler(int sig) -{ - printf("Received signal %d, exiting...\n", sig); - Sys_Quit(); - exit(0); -} - -void InitSig(void) -{ - signal(SIGHUP, signal_handler); - signal(SIGINT, signal_handler); - signal(SIGQUIT, signal_handler); - signal(SIGILL, signal_handler); - signal(SIGTRAP, signal_handler); - signal(SIGIOT, signal_handler); - signal(SIGBUS, signal_handler); - signal(SIGFPE, signal_handler); - signal(SIGSEGV, signal_handler); - signal(SIGTERM, signal_handler); -} - -void VID_ShiftPalette(unsigned char *p) -{ -// VID_SetPalette(p); -} - -void VID_SetPalette (unsigned char *palette) -{ - byte *pal; - unsigned r,g,b; - unsigned v; - int r1,g1,b1; - int k; - unsigned short i; - unsigned *table; - FILE *f; - char s[255]; - float dist, bestdist; - static qboolean palflag = false; - -// -// 8 8 8 encoding -// - Con_Printf("Converting 8to24\n"); - - pal = palette; - table = d_8to24table; - for (i=0 ; i<256 ; i++) - { - r = pal[0]; - g = pal[1]; - b = pal[2]; - pal += 3; - -// v = (255<<24) + (r<<16) + (g<<8) + (b<<0); -// v = (255<<0) + (r<<8) + (g<<16) + (b<<24); - v = (255<<24) + (r<<0) + (g<<8) + (b<<16); - *table++ = v; - } - d_8to24table[255] &= 0xffffff; // 255 is transparent - - // JACK: 3D distance calcs - k is last closest, l is the distance. - // FIXME: Precalculate this and cache to disk. - if (palflag) - return; - palflag = true; - - COM_FOpenFile("glquake/15to8.pal", &f); - if (f) { - fread(d_15to8table, 1<<15, 1, f); - fclose(f); - } else { - for (i=0; i < (1<<15); i++) { - /* Maps - 000000000000000 - 000000000011111 = Red = 0x1F - 000001111100000 = Blue = 0x03E0 - 111110000000000 = Grn = 0x7C00 - */ - r = ((i & 0x1F) << 3)+4; - g = ((i & 0x03E0) >> 2)+4; - b = ((i & 0x7C00) >> 7)+4; - pal = (unsigned char *)d_8to24table; - for (v=0,k=0,bestdist=10000.0; v<256; v++,pal+=4) { - r1 = (int)r - (int)pal[0]; - g1 = (int)g - (int)pal[1]; - b1 = (int)b - (int)pal[2]; - dist = sqrt(((r1*r1)+(g1*g1)+(b1*b1))); - if (dist < bestdist) { - k=v; - bestdist = dist; - } - } - d_15to8table[i]=k; - } - sprintf(s, "%s/glquake", com_gamedir); - Sys_mkdir (s); - sprintf(s, "%s/glquake/15to8.pal", com_gamedir); - if ((f = fopen(s, "wb")) != NULL) { - fwrite(d_15to8table, 1<<15, 1, f); - fclose(f); - } - } -} - -/* -=============== -GL_Init -=============== -*/ -void GL_Init (void) -{ - gl_vendor = glGetString (GL_VENDOR); - Con_Printf ("GL_VENDOR: %s\n", gl_vendor); - gl_renderer = glGetString (GL_RENDERER); - Con_Printf ("GL_RENDERER: %s\n", gl_renderer); - - gl_version = glGetString (GL_VERSION); - Con_Printf ("GL_VERSION: %s\n", gl_version); - gl_extensions = glGetString (GL_EXTENSIONS); - Con_Printf ("GL_EXTENSIONS: %s\n", gl_extensions); - -// Con_Printf ("%s %s\n", gl_renderer, gl_version); - - glClearColor (1,0,0,0); - glCullFace(GL_FRONT); - glEnable(GL_TEXTURE_2D); - - glEnable(GL_ALPHA_TEST); - glAlphaFunc(GL_GREATER, 0.666); - - glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); - glShadeModel (GL_FLAT); - - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - - glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - -// glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); -} - -/* -================= -GL_BeginRendering - -================= -*/ -void GL_BeginRendering (int *x, int *y, int *width, int *height) -{ - extern cvar_t gl_clear; - - *x = *y = 0; - *width = scr_width; - *height = scr_height; - -// if (!wglMakeCurrent( maindc, baseRC )) -// Sys_Error ("wglMakeCurrent failed"); - -// glViewport (*x, *y, *width, *height); -} - - -void GL_EndRendering (void) -{ - glFlush(); - glXSwapBuffers(dpy, win); -} - -qboolean VID_Is8bit(void) -{ - return is8bit; -} - -#ifdef GL_EXT_SHARED -void VID_Init8bitPalette() -{ - // Check for 8bit Extensions and initialize them. - int i; - char thePalette[256*3]; - char *oldPalette, *newPalette; - - if (strstr(gl_extensions, "GL_EXT_shared_texture_palette") == NULL) - return; - - Con_SafePrintf("8-bit GL extensions enabled.\n"); - glEnable( GL_SHARED_TEXTURE_PALETTE_EXT ); - oldPalette = (char *) d_8to24table; //d_8to24table3dfx; - newPalette = thePalette; - for (i=0;i<256;i++) { - *newPalette++ = *oldPalette++; - *newPalette++ = *oldPalette++; - *newPalette++ = *oldPalette++; - oldPalette++; - } - glColorTableEXT(GL_SHARED_TEXTURE_PALETTE_EXT, GL_RGB, 256, GL_RGB, GL_UNSIGNED_BYTE, (void *) thePalette); - is8bit = true; -} - -#else -extern void gl3DfxSetPaletteEXT(GLuint *pal); - -void VID_Init8bitPalette(void) -{ - // Check for 8bit Extensions and initialize them. - int i; - GLubyte table[256][4]; - char *oldpal; - - if (strstr(gl_extensions, "3DFX_set_global_palette") == NULL) - return; - - Con_SafePrintf("8-bit GL extensions enabled.\n"); - glEnable( GL_SHARED_TEXTURE_PALETTE_EXT ); - oldpal = (char *) d_8to24table; //d_8to24table3dfx; - for (i=0;i<256;i++) { - table[i][2] = *oldpal++; - table[i][1] = *oldpal++; - table[i][0] = *oldpal++; - table[i][3] = 255; - oldpal++; - } - gl3DfxSetPaletteEXT((GLuint *)table); - is8bit = true; -} -#endif - -void VID_Init(unsigned char *palette) -{ - int i; - int attrib[] = { - GLX_RGBA, - GLX_RED_SIZE, 1, - GLX_GREEN_SIZE, 1, - GLX_BLUE_SIZE, 1, - GLX_DOUBLEBUFFER, - GLX_DEPTH_SIZE, 1, - None - }; - char gldir[MAX_OSPATH]; - int width = 640, height = 480; - int scrnum; - XSetWindowAttributes attr; - unsigned long mask; - Window root; - XVisualInfo *visinfo; - - S_Init(); - - Cvar_RegisterVariable (&vid_mode); - Cvar_RegisterVariable (&gl_ztrick); - Cvar_RegisterVariable (&_windowed_mouse); - - vid.maxwarpwidth = WARP_WIDTH; - vid.maxwarpheight = WARP_HEIGHT; - vid.colormap = host_colormap; - vid.fullbright = 256 - LittleLong (*((int *)vid.colormap + 2048)); - -// interpret command-line params - -// set vid parameters - if ((i = COM_CheckParm("-width")) != 0) - width = atoi(com_argv[i+1]); - if ((i = COM_CheckParm("-height")) != 0) - height = atoi(com_argv[i+1]); - - if ((i = COM_CheckParm("-conwidth")) != 0) - vid.conwidth = Q_atoi(com_argv[i+1]); - else - vid.conwidth = 640; - - vid.conwidth &= 0xfff8; // make it a multiple of eight - - if (vid.conwidth < 320) - vid.conwidth = 320; - - // pick a conheight that matches with correct aspect - vid.conheight = vid.conwidth*3 / 4; - - if ((i = COM_CheckParm("-conheight")) != 0) - vid.conheight = Q_atoi(com_argv[i+1]); - if (vid.conheight < 200) - vid.conheight = 200; - - if (!(dpy = XOpenDisplay(NULL))) { - fprintf(stderr, "Error couldn't open the X display\n"); - exit(1); - } - - scrnum = DefaultScreen(dpy); - root = RootWindow(dpy, scrnum); - - visinfo = glXChooseVisual(dpy, scrnum, attrib); - if (!visinfo) { - fprintf(stderr, "qkHack: Error couldn't get an RGB, Double-buffered, Depth visual\n"); - exit(1); - } - /* window attributes */ - attr.background_pixel = 0; - attr.border_pixel = 0; - attr.colormap = XCreateColormap(dpy, root, visinfo->visual, AllocNone); - attr.event_mask = X_MASK; - mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask; - - win = XCreateWindow(dpy, root, 0, 0, width, height, - 0, visinfo->depth, InputOutput, - visinfo->visual, mask, &attr); - XMapWindow(dpy, win); - - XMoveWindow(dpy, win, 0, 0); - - XFlush(dpy); - - ctx = glXCreateContext(dpy, visinfo, NULL, True); - - glXMakeCurrent(dpy, win, ctx); - - scr_width = width; - scr_height = height; - - if (vid.conheight > height) - vid.conheight = height; - if (vid.conwidth > width) - vid.conwidth = width; - vid.width = vid.conwidth; - vid.height = vid.conheight; - - vid.aspect = ((float)vid.height / (float)vid.width) * (320.0 / 240.0); - vid.numpages = 2; - - InitSig(); // trap evil signals - - GL_Init(); - - sprintf (gldir, "%s/glquake", com_gamedir); - Sys_mkdir (gldir); - - VID_SetPalette(palette); - - // Check for 3DFX Extensions and initialize them. - VID_Init8bitPalette(); - - Con_SafePrintf ("Video mode %dx%d initialized.\n", width, height); - - vid.recalc_refdef = 1; // force a surface cache flush -} - -void Sys_SendKeyEvents(void) -{ - if (dpy) { - while (XPending(dpy)) - GetEvent(); - } -} - -void Force_CenterView_f (void) -{ - cl.viewangles[PITCH] = 0; -} - -void IN_Init(void) -{ -} - -void IN_Shutdown(void) -{ -} - -/* -=========== -IN_Commands -=========== -*/ -void IN_Commands (void) -{ -} - -/* -=========== -IN_Move -=========== -*/ -void IN_MouseMove (usercmd_t *cmd) -{ - if (m_filter.value) - { - mouse_x = (mouse_x + old_mouse_x) * 0.5; - mouse_y = (mouse_y + old_mouse_y) * 0.5; - } - old_mouse_x = mouse_x; - old_mouse_y = mouse_y; - - mouse_x *= sensitivity.value; - mouse_y *= sensitivity.value; - -// add mouse X/Y movement to cmd - if ( (in_strafe.state & 1) || (lookstrafe.value && (in_mlook.state & 1) )) - cmd->sidemove += m_side.value * mouse_x; - else - cl.viewangles[YAW] -= m_yaw.value * mouse_x; - - if (in_mlook.state & 1) - V_StopPitchDrift (); - - if ( (in_mlook.state & 1) && !(in_strafe.state & 1)) - { - cl.viewangles[PITCH] += m_pitch.value * mouse_y; - if (cl.viewangles[PITCH] > 80) - cl.viewangles[PITCH] = 80; - if (cl.viewangles[PITCH] < -70) - cl.viewangles[PITCH] = -70; - } - else - { - cmd->forwardmove -= m_forward.value * mouse_y; - } - mouse_x = mouse_y = 0.0; -} - -void IN_Move (usercmd_t *cmd) -{ - IN_MouseMove(cmd); -} - - -void VID_UnlockBuffer() {} -void VID_LockBuffer() {} - +/* +Copyright (C) 1996-1997 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 +#include +#include +#include +#include +#include + +#include "quakedef.h" + +#include + +#include +#include +#include "keys.h" + +#ifdef USE_DGA +#include +#endif + + +#define WARP_WIDTH 320 +#define WARP_HEIGHT 200 + +static Display *dpy = NULL; +static Window win; +static GLXContext ctx = NULL; + +static float old_windowed_mouse = 0; + +#define KEY_MASK (KeyPressMask | KeyReleaseMask) +#define MOUSE_MASK (ButtonPressMask | ButtonReleaseMask | \ + PointerMotionMask) + +#define X_MASK (KEY_MASK | MOUSE_MASK | VisibilityChangeMask) + +unsigned short d_8to16table[256]; +unsigned d_8to24table[256]; +unsigned d_8to24table2[256]; +unsigned char d_15to8table[65536]; + +cvar_t _windowed_mouse = {"_windowed_mouse","0",CVAR_ARCHIVE}; +cvar_t vid_mode = {"vid_mode","0"}; + +static float mouse_x, mouse_y; +static float old_mouse_x, old_mouse_y; + +cvar_t m_filter = {"m_filter", "0"}; + +static int scr_width, scr_height; + +/*-----------------------------------------------------------------------*/ + +//int texture_mode = GL_NEAREST; +//int texture_mode = GL_NEAREST_MIPMAP_NEAREST; +//int texture_mode = GL_NEAREST_MIPMAP_LINEAR; +int texture_mode = GL_LINEAR; +//int texture_mode = GL_LINEAR_MIPMAP_NEAREST; +//int texture_mode = GL_LINEAR_MIPMAP_LINEAR; + +int texture_extension_number = 1; + +float gldepthmin, gldepthmax; + +cvar_t gl_ztrick = {"gl_ztrick","1"}; + +const char *gl_vendor; +const char *gl_renderer; +const char *gl_version; +const char *gl_extensions; + +qboolean is8bit = false; +qboolean isPermedia = false; +qboolean gl_mtexable = false; + +/*-----------------------------------------------------------------------*/ +void D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height) +{ +} + +void D_EndDirectRect (int x, int y, int width, int height) +{ +} + +static int XLateKey(XKeyEvent *ev) +{ + + int key; + char buf[64]; + KeySym keysym; + + key = 0; + + XLookupString(ev, buf, sizeof buf, &keysym, 0); + + switch(keysym) + { + case XK_KP_Page_Up: + case XK_Page_Up: key = K_PGUP; break; + + case XK_KP_Page_Down: + case XK_Page_Down: key = K_PGDN; break; + + case XK_KP_Home: + case XK_Home: key = K_HOME; break; + + case XK_KP_End: + case XK_End: key = K_END; break; + + case XK_KP_Left: + case XK_Left: key = K_LEFTARROW; break; + + case XK_KP_Right: + case XK_Right: key = K_RIGHTARROW; break; + + case XK_KP_Down: + case XK_Down: key = K_DOWNARROW; break; + + case XK_KP_Up: + case XK_Up: key = K_UPARROW; break; + + case XK_Escape: key = K_ESCAPE; break; + + case XK_KP_Enter: + case XK_Return: key = K_ENTER; break; + + case XK_Tab: key = K_TAB; break; + + case XK_F1: key = K_F1; break; + + case XK_F2: key = K_F2; break; + + case XK_F3: key = K_F3; break; + + case XK_F4: key = K_F4; break; + + case XK_F5: key = K_F5; break; + + case XK_F6: key = K_F6; break; + + case XK_F7: key = K_F7; break; + + case XK_F8: key = K_F8; break; + + case XK_F9: key = K_F9; break; + + case XK_F10: key = K_F10; break; + + case XK_F11: key = K_F11; break; + + case XK_F12: key = K_F12; break; + + case XK_BackSpace: key = K_BACKSPACE; break; + + case XK_KP_Delete: + case XK_Delete: key = K_DEL; break; + + case XK_Pause: key = K_PAUSE; break; + + case XK_Shift_L: + case XK_Shift_R: key = K_SHIFT; break; + + case XK_Execute: + case XK_Control_L: + case XK_Control_R: key = K_CTRL; break; + + case XK_Alt_L: + case XK_Meta_L: + case XK_Alt_R: + case XK_Meta_R: key = K_ALT; break; + + case XK_KP_Begin: key = '5'; break; + + case XK_KP_Insert: + case XK_Insert:key = K_INS; break; + + case XK_KP_Multiply: key = '*'; break; + case XK_KP_Add: key = '+'; break; + case XK_KP_Subtract: key = '-'; break; + case XK_KP_Divide: key = '/'; break; + +#if 0 + case 0x021: key = '1';break;/* [!] */ + case 0x040: key = '2';break;/* [@] */ + case 0x023: key = '3';break;/* [#] */ + case 0x024: key = '4';break;/* [$] */ + case 0x025: key = '5';break;/* [%] */ + case 0x05e: key = '6';break;/* [^] */ + case 0x026: key = '7';break;/* [&] */ + case 0x02a: key = '8';break;/* [*] */ + case 0x028: key = '9';;break;/* [(] */ + case 0x029: key = '0';break;/* [)] */ + case 0x05f: key = '-';break;/* [_] */ + case 0x02b: key = '=';break;/* [+] */ + case 0x07c: key = '\'';break;/* [|] */ + case 0x07d: key = '[';break;/* [}] */ + case 0x07b: key = ']';break;/* [{] */ + case 0x022: key = '\'';break;/* ["] */ + case 0x03a: key = ';';break;/* [:] */ + case 0x03f: key = '/';break;/* [?] */ + case 0x03e: key = '.';break;/* [>] */ + case 0x03c: key = ',';break;/* [<] */ +#endif + + default: + key = *(unsigned char*)buf; + if (key >= 'A' && key <= 'Z') + key = key - 'A' + 'a'; + break; + } + + return key; +} + +static void install_grabs(void) +{ + XGrabPointer(dpy, win, + True, + 0, + GrabModeAsync, GrabModeAsync, + win, + None, + CurrentTime); + +#ifdef USE_DGA + XF86DGADirectVideo(dpy, DefaultScreen(dpy), XF86DGADirectMouse); + dgamouse = 1; +#else + XWarpPointer(dpy, None, win, + 0, 0, 0, 0, + vid.width / 2, vid.height / 2); +#endif + + XGrabKeyboard(dpy, win, + False, + GrabModeAsync, GrabModeAsync, + CurrentTime); + +// XSync(dpy, True); +} + +static void uninstall_grabs(void) +{ +#ifdef USE_DGA + XF86DGADirectVideo(dpy, DefaultScreen(dpy), 0); + dgamouse = 0; +#endif + + XUngrabPointer(dpy, CurrentTime); + XUngrabKeyboard(dpy, CurrentTime); + +// XSync(dpy, True); +} + +static void GetEvent(void) +{ + XEvent event; + int b; + + if (!dpy) + return; + + XNextEvent(dpy, &event); + + switch (event.type) { + case KeyPress: + case KeyRelease: + Key_Event(XLateKey(&event.xkey), event.type == KeyPress); + break; + + case MotionNotify: +#ifdef USE_DGA + if (dgamouse && _windowed_mouse.value) { + mouse_x = event.xmotion.x_root; + mouse_y = event.xmotion.y_root; + } else +#endif + { + if (_windowed_mouse.value) { + mouse_x = (float) ((int)event.xmotion.x - (int)(vid.width/2)); + mouse_y = (float) ((int)event.xmotion.y - (int)(vid.height/2)); + + /* move the mouse to the window center again */ + XSelectInput(dpy, win, X_MASK & ~PointerMotionMask); + XWarpPointer(dpy, None, win, 0, 0, 0, 0, + (vid.width/2), (vid.height/2)); + XSelectInput(dpy, win, X_MASK); + } + } + break; + + case ButtonPress: + b=-1; + if (event.xbutton.button == 1) + b = 0; + else if (event.xbutton.button == 2) + b = 2; + else if (event.xbutton.button == 3) + b = 1; + if (b>=0) + Key_Event(K_MOUSE1 + b, true); + break; + + case ButtonRelease: + b=-1; + if (event.xbutton.button == 1) + b = 0; + else if (event.xbutton.button == 2) + b = 2; + else if (event.xbutton.button == 3) + b = 1; + if (b>=0) + Key_Event(K_MOUSE1 + b, false); + break; + } + + if (old_windowed_mouse != _windowed_mouse.value) { + old_windowed_mouse = _windowed_mouse.value; + + if (!_windowed_mouse.value) { + /* ungrab the pointer */ + uninstall_grabs(); + } else { + /* grab the pointer */ + install_grabs(); + } + } +} + + +void VID_Shutdown(void) +{ + if (!ctx) + return; + + glXDestroyContext(dpy, ctx); +} + +void signal_handler(int sig) +{ + printf("Received signal %d, exiting...\n", sig); + Sys_Quit(); + exit(0); +} + +void InitSig(void) +{ + signal(SIGHUP, signal_handler); + signal(SIGINT, signal_handler); + signal(SIGQUIT, signal_handler); + signal(SIGILL, signal_handler); + signal(SIGTRAP, signal_handler); + signal(SIGIOT, signal_handler); + signal(SIGBUS, signal_handler); + signal(SIGFPE, signal_handler); + signal(SIGSEGV, signal_handler); + signal(SIGTERM, signal_handler); +} + +void VID_ShiftPalette(unsigned char *p) +{ +// VID_SetPalette(p); +} + +void VID_SetPalette (unsigned char *palette) +{ + byte *pal; + unsigned r,g,b; + unsigned v; + int r1,g1,b1; + int k; + unsigned short i; + unsigned *table; + FILE *f; + char s[255]; + float dist, bestdist; + static qboolean palflag = false; + +// +// 8 8 8 encoding +// + Con_Printf("Converting 8to24\n"); + + pal = palette; + table = d_8to24table; + for (i=0 ; i<256 ; i++) + { + r = pal[0]; + g = pal[1]; + b = pal[2]; + pal += 3; + +// v = (255<<24) + (r<<16) + (g<<8) + (b<<0); +// v = (255<<0) + (r<<8) + (g<<16) + (b<<24); + v = (255<<24) + (r<<0) + (g<<8) + (b<<16); + *table++ = v; + } + d_8to24table[255] &= 0xffffff; // 255 is transparent + + // JACK: 3D distance calcs - k is last closest, l is the distance. + // FIXME: Precalculate this and cache to disk. + if (palflag) + return; + palflag = true; + + COM_FOpenFile("glquake/15to8.pal", &f); + if (f) { + fread(d_15to8table, 1<<15, 1, f); + fclose(f); + } else { + for (i=0; i < (1<<15); i++) { + /* Maps + 000000000000000 + 000000000011111 = Red = 0x1F + 000001111100000 = Blue = 0x03E0 + 111110000000000 = Grn = 0x7C00 + */ + r = ((i & 0x1F) << 3)+4; + g = ((i & 0x03E0) >> 2)+4; + b = ((i & 0x7C00) >> 7)+4; + pal = (unsigned char *)d_8to24table; + for (v=0,k=0,bestdist=10000.0; v<256; v++,pal+=4) { + r1 = (int)r - (int)pal[0]; + g1 = (int)g - (int)pal[1]; + b1 = (int)b - (int)pal[2]; + dist = sqrt(((r1*r1)+(g1*g1)+(b1*b1))); + if (dist < bestdist) { + k=v; + bestdist = dist; + } + } + d_15to8table[i]=k; + } + sprintf(s, "%s/glquake", com_gamedir); + Sys_mkdir (s); + sprintf(s, "%s/glquake/15to8.pal", com_gamedir); + if ((f = fopen(s, "wb")) != NULL) { + fwrite(d_15to8table, 1<<15, 1, f); + fclose(f); + } + } +} + +/* +=============== +GL_Init +=============== +*/ +void GL_Init (void) +{ + gl_vendor = glGetString (GL_VENDOR); + Con_Printf ("GL_VENDOR: %s\n", gl_vendor); + gl_renderer = glGetString (GL_RENDERER); + Con_Printf ("GL_RENDERER: %s\n", gl_renderer); + + gl_version = glGetString (GL_VERSION); + Con_Printf ("GL_VERSION: %s\n", gl_version); + gl_extensions = glGetString (GL_EXTENSIONS); + Con_Printf ("GL_EXTENSIONS: %s\n", gl_extensions); + +// Con_Printf ("%s %s\n", gl_renderer, gl_version); + + glClearColor (1,0,0,0); + glCullFace(GL_FRONT); + glEnable(GL_TEXTURE_2D); + + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_GREATER, 0.666); + + glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); + glShadeModel (GL_FLAT); + + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + +// glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); +} + +/* +================= +GL_BeginRendering + +================= +*/ +void GL_BeginRendering (int *x, int *y, int *width, int *height) +{ + extern cvar_t gl_clear; + + *x = *y = 0; + *width = scr_width; + *height = scr_height; + +// if (!wglMakeCurrent( maindc, baseRC )) +// Sys_Error ("wglMakeCurrent failed"); + +// glViewport (*x, *y, *width, *height); +} + + +void GL_EndRendering (void) +{ + glFlush(); + glXSwapBuffers(dpy, win); +} + +qboolean VID_Is8bit(void) +{ + return is8bit; +} +#define GL_EXT_SHARED +#ifdef GL_EXT_SHARED +void VID_Init8bitPalette() +{ + // Check for 8bit Extensions and initialize them. + int i; + char thePalette[256*3]; + char *oldPalette, *newPalette; + + if (strstr(gl_extensions, "GL_EXT_shared_texture_palette") == NULL) + return; + + Con_SafePrintf("8-bit GL extensions enabled.\n"); + glEnable( GL_SHARED_TEXTURE_PALETTE_EXT ); + oldPalette = (char *) d_8to24table; //d_8to24table3dfx; + newPalette = thePalette; + for (i=0;i<256;i++) { + *newPalette++ = *oldPalette++; + *newPalette++ = *oldPalette++; + *newPalette++ = *oldPalette++; + oldPalette++; + } + glColorTableEXT(GL_SHARED_TEXTURE_PALETTE_EXT, GL_RGB, 256, GL_RGB, GL_UNSIGNED_BYTE, (void *) thePalette); + is8bit = true; +} + +#else +extern void gl3DfxSetPaletteEXT(GLuint *pal); + +void VID_Init8bitPalette(void) +{ + // Check for 8bit Extensions and initialize them. + int i; + GLubyte table[256][4]; + char *oldpal; + + if (strstr(gl_extensions, "3DFX_set_global_palette") == NULL) + return; + + Con_SafePrintf("8-bit GL extensions enabled.\n"); + glEnable( GL_SHARED_TEXTURE_PALETTE_EXT ); + oldpal = (char *) d_8to24table; //d_8to24table3dfx; + for (i=0;i<256;i++) { + table[i][2] = *oldpal++; + table[i][1] = *oldpal++; + table[i][0] = *oldpal++; + table[i][3] = 255; + oldpal++; + } + gl3DfxSetPaletteEXT((GLuint *)table); + is8bit = true; +} +#endif + +void VID_Init(unsigned char *palette) +{ + int i; + int attrib[] = { + GLX_RGBA, + GLX_RED_SIZE, 1, + GLX_GREEN_SIZE, 1, + GLX_BLUE_SIZE, 1, + GLX_DOUBLEBUFFER, + GLX_DEPTH_SIZE, 1, + None + }; + char gldir[MAX_OSPATH]; + int width = 640, height = 480; + int scrnum; + XSetWindowAttributes attr; + unsigned long mask; + Window root; + XVisualInfo *visinfo; + + S_Init(); + + Cvar_RegisterVariable (&vid_mode); + Cvar_RegisterVariable (&gl_ztrick); + Cvar_RegisterVariable (&_windowed_mouse); + + vid.maxwarpwidth = WARP_WIDTH; + vid.maxwarpheight = WARP_HEIGHT; + vid.colormap = host_colormap; + vid.fullbright = 256 - LittleLong (*((int *)vid.colormap + 2048)); + +// interpret command-line params + +// set vid parameters + if ((i = COM_CheckParm("-width")) != 0) + width = atoi(com_argv[i+1]); + if ((i = COM_CheckParm("-height")) != 0) + height = atoi(com_argv[i+1]); + + if ((i = COM_CheckParm("-conwidth")) != 0) + vid.conwidth = Q_atoi(com_argv[i+1]); + else + vid.conwidth = 640; + + vid.conwidth &= 0xfff8; // make it a multiple of eight + + if (vid.conwidth < 320) + vid.conwidth = 320; + + // pick a conheight that matches with correct aspect + vid.conheight = vid.conwidth*3 / 4; + + if ((i = COM_CheckParm("-conheight")) != 0) + vid.conheight = Q_atoi(com_argv[i+1]); + if (vid.conheight < 200) + vid.conheight = 200; + + if (!(dpy = XOpenDisplay(NULL))) { + fprintf(stderr, "Error couldn't open the X display\n"); + exit(1); + } + + scrnum = DefaultScreen(dpy); + root = RootWindow(dpy, scrnum); + + visinfo = glXChooseVisual(dpy, scrnum, attrib); + if (!visinfo) { + fprintf(stderr, "qkHack: Error couldn't get an RGB, Double-buffered, Depth visual\n"); + exit(1); + } + /* window attributes */ + attr.background_pixel = 0; + attr.border_pixel = 0; + attr.colormap = XCreateColormap(dpy, root, visinfo->visual, AllocNone); + attr.event_mask = X_MASK; + mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask; + + win = XCreateWindow(dpy, root, 0, 0, width, height, + 0, visinfo->depth, InputOutput, + visinfo->visual, mask, &attr); + XMapWindow(dpy, win); + + XMoveWindow(dpy, win, 0, 0); + + XFlush(dpy); + + ctx = glXCreateContext(dpy, visinfo, NULL, True); + + glXMakeCurrent(dpy, win, ctx); + + scr_width = width; + scr_height = height; + + if (vid.conheight > height) + vid.conheight = height; + if (vid.conwidth > width) + vid.conwidth = width; + vid.width = vid.conwidth; + vid.height = vid.conheight; + + vid.aspect = ((float)vid.height / (float)vid.width) * (320.0 / 240.0); + vid.numpages = 2; + + InitSig(); // trap evil signals + + GL_Init(); + + sprintf (gldir, "%s/glquake", com_gamedir); + Sys_mkdir (gldir); + + VID_SetPalette(palette); + + // Check for 3DFX Extensions and initialize them. + VID_Init8bitPalette(); + + Con_SafePrintf ("Video mode %dx%d initialized.\n", width, height); + + vid.recalc_refdef = 1; // force a surface cache flush +} + +void Sys_SendKeyEvents(void) +{ + if (dpy) { + while (XPending(dpy)) + GetEvent(); + } +} + +void Force_CenterView_f (void) +{ + cl.viewangles[PITCH] = 0; +} + +void IN_Init(void) +{ +} + +void IN_Shutdown(void) +{ +} + +/* +=========== +IN_Commands +=========== +*/ +void IN_Commands (void) +{ +} + +/* +=========== +IN_Move +=========== +*/ +void IN_MouseMove (usercmd_t *cmd) +{ + if (m_filter.value) + { + mouse_x = (mouse_x + old_mouse_x) * 0.5; + mouse_y = (mouse_y + old_mouse_y) * 0.5; + } + old_mouse_x = mouse_x; + old_mouse_y = mouse_y; + + mouse_x *= sensitivity.value; + mouse_y *= sensitivity.value; + +// add mouse X/Y movement to cmd + if ( (in_strafe.state & 1) || (lookstrafe.value && (in_mlook.state & 1) )) + cmd->sidemove += m_side.value * mouse_x; + else + cl.viewangles[YAW] -= m_yaw.value * mouse_x; + + if (in_mlook.state & 1) + V_StopPitchDrift (); + + if ( (in_mlook.state & 1) && !(in_strafe.state & 1)) + { + cl.viewangles[PITCH] += m_pitch.value * mouse_y; + if (cl.viewangles[PITCH] > 80) + cl.viewangles[PITCH] = 80; + if (cl.viewangles[PITCH] < -70) + cl.viewangles[PITCH] = -70; + } + else + { + cmd->forwardmove -= m_forward.value * mouse_y; + } + mouse_x = mouse_y = 0.0; +} + +void IN_Move (usercmd_t *cmd) +{ + IN_MouseMove(cmd); +} + + +void VID_UnlockBuffer() {} +void VID_LockBuffer() {} + diff --git a/source/gl_warp.c b/source/gl_warp.c index bde654f9..572ece0c 100644 --- a/source/gl_warp.c +++ b/source/gl_warp.c @@ -1,1087 +1,1087 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// gl_warp.c -- sky and water polygons - -#include "quakedef.h" - -extern model_t *loadmodel; - -int skytexturenum; - -int solidskytexture; -int alphaskytexture; -float speedscale; // for top sky and bottom sky - -msurface_t *warpface; - -extern cvar_t gl_subdivide_size; - -void BoundPoly (int numverts, float *verts, vec3_t mins, vec3_t maxs) -{ - int i, j; - float *v; - - mins[0] = mins[1] = mins[2] = 9999; - maxs[0] = maxs[1] = maxs[2] = -9999; - v = verts; - for (i=0 ; i maxs[j]) - maxs[j] = *v; - } -} - -void SubdividePolygon (int numverts, float *verts) -{ - int i, j, k; - vec3_t mins, maxs; - float m; - float *v; - vec3_t front[64], back[64]; - int f, b; - float dist[64]; - float frac; - glpoly_t *poly; - float s, t; - - if (numverts > 60) - Sys_Error ("numverts = %i", numverts); - - BoundPoly (numverts, verts, mins, maxs); - - for (i=0 ; i<3 ; i++) - { - m = (mins[i] + maxs[i]) * 0.5; - m = gl_subdivide_size.value * floor (m/gl_subdivide_size.value + 0.5); - if (maxs[i] - m < 8) - continue; - if (m - mins[i] < 8) - continue; - - // cut it - v = verts + i; - for (j=0 ; j= 0) - { - VectorCopy (v, front[f]); - f++; - } - if (dist[j] <= 0) - { - VectorCopy (v, back[b]); - b++; - } - if (dist[j] == 0 || dist[j+1] == 0) - continue; - if ( (dist[j] > 0) != (dist[j+1] > 0) ) - { - // clip point - frac = dist[j] / (dist[j] - dist[j+1]); - for (k=0 ; k<3 ; k++) - front[f][k] = back[b][k] = v[k] + frac*(v[3+k] - v[k]); - f++; - b++; - } - } - - SubdividePolygon (f, front[0]); - SubdividePolygon (b, back[0]); - return; - } - - poly = Hunk_Alloc (sizeof(glpoly_t) + (numverts-4) * VERTEXSIZE*sizeof(float)); - poly->next = warpface->polys; - warpface->polys = poly; - poly->numverts = numverts; - for (i=0 ; iverts[i]); - s = DotProduct (verts, warpface->texinfo->vecs[0]); - t = DotProduct (verts, warpface->texinfo->vecs[1]); - poly->verts[i][3] = s; - poly->verts[i][4] = t; - } -} - -/* -================ -GL_SubdivideSurface - -Breaks a polygon up along axial 64 unit -boundaries so that turbulent and sky warps -can be done reasonably. -================ -*/ -void GL_SubdivideSurface (msurface_t *fa) -{ - vec3_t verts[64]; - int numverts; - int i; - int lindex; - float *vec; - - warpface = fa; - - // - // convert edges back to a normal polygon - // - numverts = 0; - for (i=0 ; inumedges ; i++) - { - lindex = loadmodel->surfedges[fa->firstedge + i]; - - if (lindex > 0) - vec = loadmodel->vertexes[loadmodel->edges[lindex].v[0]].position; - else - vec = loadmodel->vertexes[loadmodel->edges[-lindex].v[1]].position; - VectorCopy (vec, verts[numverts]); - numverts++; - } - - SubdividePolygon (numverts, verts[0]); -} - -//========================================================= - - - -// speed up sin calculations - Ed -float turbsin[] = -{ - #include "gl_warp_sin.h" -}; -#define TURBSCALE (256.0 / (2 * M_PI)) - -/* -============= -EmitWaterPolys - -Does a water warp on the pre-fragmented glpoly_t chain -============= -*/ -void EmitWaterPolys (msurface_t *fa) -{ - glpoly_t *p; - float *v; - int i; - float s, t, os, ot; - - - for (p=fa->polys ; p ; p=p->next) - { - glBegin (GL_POLYGON); - for (i=0,v=p->verts[0] ; inumverts ; i++, v+=VERTEXSIZE) - { - os = v[3]; - ot = v[4]; - - s = os + turbsin[(int)((ot*0.125+realtime) * TURBSCALE) & 255]; - s *= (1.0/64); - - t = ot + turbsin[(int)((os*0.125+realtime) * TURBSCALE) & 255]; - t *= (1.0/64); - - glTexCoord2f (s, t); - glVertex3fv (v); - } - glEnd (); - } -} - - - - -/* -============= -EmitSkyPolys -============= -*/ -void EmitSkyPolys (msurface_t *fa) -{ - glpoly_t *p; - float *v; - int i; - float s, t; - vec3_t dir; - float length; - - for (p=fa->polys ; p ; p=p->next) - { - glBegin (GL_POLYGON); - for (i=0,v=p->verts[0] ; inumverts ; i++, v+=VERTEXSIZE) - { - VectorSubtract (v, r_origin, dir); - dir[2] *= 3; // flatten the sphere - - length = dir[0]*dir[0] + dir[1]*dir[1] + dir[2]*dir[2]; - length = sqrt (length); - length = 6*63/length; - - dir[0] *= length; - dir[1] *= length; - - s = (speedscale + dir[0]) * (1.0/128); - t = (speedscale + dir[1]) * (1.0/128); - - glTexCoord2f (s, t); - glVertex3fv (v); - } - glEnd (); - } -} - -/* -=============== -EmitBothSkyLayers - -Does a sky warp on the pre-fragmented glpoly_t chain -This will be called for brushmodels, the world -will have them chained together. -=============== -*/ -void EmitBothSkyLayers (msurface_t *fa) -{ - GL_DisableMultitexture(); - - GL_Bind (solidskytexture); - speedscale = realtime*8; - speedscale -= (int)speedscale & ~127 ; - - EmitSkyPolys (fa); - - glEnable (GL_BLEND); - GL_Bind (alphaskytexture); - speedscale = realtime*16; - speedscale -= (int)speedscale & ~127 ; - - EmitSkyPolys (fa); - - glDisable (GL_BLEND); -} - -#ifndef QUAKE2 -/* -================= -R_DrawSkyChain -================= -*/ -void R_DrawSkyChain (msurface_t *s) -{ - msurface_t *fa; - - GL_DisableMultitexture(); - - // used when gl_texsort is on - GL_Bind(solidskytexture); - speedscale = realtime*8; - speedscale -= (int)speedscale & ~127 ; - - for (fa=s ; fa ; fa=fa->texturechain) - EmitSkyPolys (fa); - - glEnable (GL_BLEND); - GL_Bind (alphaskytexture); - speedscale = realtime*16; - speedscale -= (int)speedscale & ~127 ; - - for (fa=s ; fa ; fa=fa->texturechain) - EmitSkyPolys (fa); - - glDisable (GL_BLEND); -} - -#endif - -/* -================================================================= - - Quake 2 environment sky - -================================================================= -*/ - -#ifdef QUAKE2 - - -#define SKY_TEX 2000 - -/* -================================================================= - - PCX Loading - -================================================================= -*/ - -typedef struct -{ - char manufacturer; - char version; - char encoding; - char bits_per_pixel; - unsigned short xmin,ymin,xmax,ymax; - unsigned short hres,vres; - unsigned char palette[48]; - char reserved; - char color_planes; - unsigned short bytes_per_line; - unsigned short palette_type; - char filler[58]; - unsigned data; // unbounded -} pcx_t; - -byte *pcx_rgb; - -/* -============ -LoadPCX -============ -*/ -void LoadPCX (FILE *f) -{ - pcx_t *pcx, pcxbuf; - byte palette[768]; - byte *pix; - int x, y; - int dataByte, runLength; - int count; - -// -// parse the PCX file -// - fread (&pcxbuf, 1, sizeof(pcxbuf), f); - - pcx = &pcxbuf; - - if (pcx->manufacturer != 0x0a - || pcx->version != 5 - || pcx->encoding != 1 - || pcx->bits_per_pixel != 8 - || pcx->xmax >= 320 - || pcx->ymax >= 256) - { - Con_Printf ("Bad pcx file\n"); - return; - } - - // seek to palette - fseek (f, -768, SEEK_END); - fread (palette, 1, 768, f); - - fseek (f, sizeof(pcxbuf) - 4, SEEK_SET); - - count = (pcx->xmax+1) * (pcx->ymax+1); - pcx_rgb = Q_Malloc ( count * 4); - - for (y=0 ; y<=pcx->ymax ; y++) - { - pix = pcx_rgb + 4*y*(pcx->xmax+1); - for (x=0 ; x<=pcx->ymax ; ) - { - dataByte = fgetc(f); - - if((dataByte & 0xC0) == 0xC0) - { - runLength = dataByte & 0x3F; - dataByte = fgetc(f); - } - else - runLength = 1; - - while(runLength-- > 0) - { - pix[0] = palette[dataByte*3]; - pix[1] = palette[dataByte*3+1]; - pix[2] = palette[dataByte*3+2]; - pix[3] = 255; - pix += 4; - x++; - } - } - } -} - -/* -========================================================= - -TARGA LOADING - -========================================================= -*/ - -typedef struct _TargaHeader { - unsigned char id_length, colormap_type, image_type; - unsigned short colormap_index, colormap_length; - unsigned char colormap_size; - unsigned short x_origin, y_origin, width, height; - unsigned char pixel_size, attributes; -} TargaHeader; - - -TargaHeader targa_header; -byte *targa_rgba; - -int fgetLittleShort (FILE *f) -{ - byte b1, b2; - - b1 = fgetc(f); - b2 = fgetc(f); - - return (short)(b1 + b2*256); -} - -int fgetLittleLong (FILE *f) -{ - byte b1, b2, b3, b4; - - b1 = fgetc(f); - b2 = fgetc(f); - b3 = fgetc(f); - b4 = fgetc(f); - - return b1 + (b2<<8) + (b3<<16) + (b4<<24); -} - - -/* -============= -LoadTGA -============= -*/ -void LoadTGA (FILE *fin) -{ - int columns, rows, numPixels; - byte *pixbuf; - int row, column; - - targa_header.id_length = fgetc(fin); - targa_header.colormap_type = fgetc(fin); - targa_header.image_type = fgetc(fin); - - targa_header.colormap_index = fgetLittleShort(fin); - targa_header.colormap_length = fgetLittleShort(fin); - targa_header.colormap_size = fgetc(fin); - targa_header.x_origin = fgetLittleShort(fin); - targa_header.y_origin = fgetLittleShort(fin); - targa_header.width = fgetLittleShort(fin); - targa_header.height = fgetLittleShort(fin); - targa_header.pixel_size = fgetc(fin); - targa_header.attributes = fgetc(fin); - - if (targa_header.image_type!=2 - && targa_header.image_type!=10) - Sys_Error ("LoadTGA: Only type 2 and 10 targa RGB images supported\n"); - - if (targa_header.colormap_type !=0 - || (targa_header.pixel_size!=32 && targa_header.pixel_size!=24)) - Sys_Error ("Texture_LoadTGA: Only 32 or 24 bit images supported (no colormaps)\n"); - - columns = targa_header.width; - rows = targa_header.height; - numPixels = columns * rows; - - targa_rgba = Q_Malloc (numPixels*4); - - if (targa_header.id_length != 0) - fseek(fin, targa_header.id_length, SEEK_CUR); // skip TARGA image comment - - if (targa_header.image_type==2) { // Uncompressed, RGB images - for(row=rows-1; row>=0; row--) { - pixbuf = targa_rgba + row*columns*4; - for(column=0; column=0; row--) { - pixbuf = targa_rgba + row*columns*4; - for(column=0; column0) - row--; - else - goto breakOut; - pixbuf = targa_rgba + row*columns*4; - } - } - } - else { // non run-length packet - for(j=0;j0) - row--; - else - goto breakOut; - pixbuf = targa_rgba + row*columns*4; - } - } - } - } - breakOut:; - } - } - - fclose(fin); -} - -/* -================== -R_LoadSkys -================== -*/ -char *suf[6] = {"rt", "bk", "lf", "ft", "up", "dn"}; -void R_LoadSkys (void) -{ - int i; - FILE *f; - char name[64]; - - for (i=0 ; i<6 ; i++) - { - GL_Bind (SKY_TEX + i); - sprintf (name, "gfx/env/bkgtst%s.tga", suf[i]); - COM_FOpenFile (name, &f); - if (!f) - { - Con_Printf ("Couldn't load %s\n", name); - continue; - } - LoadTGA (f); -// LoadPCX (f); - - glTexImage2D (GL_TEXTURE_2D, 0, gl_solid_format, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, targa_rgba); -// glTexImage2D (GL_TEXTURE_2D, 0, gl_solid_format, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, pcx_rgb); - - free (targa_rgba); -// free (pcx_rgb); - - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - } -} - - -vec3_t skyclip[6] = { - {1,1,0}, - {1,-1,0}, - {0,-1,1}, - {0,1,1}, - {1,0,1}, - {-1,0,1} -}; -int c_sky; - -// 1 = s, 2 = t, 3 = 2048 -int st_to_vec[6][3] = -{ - {3,-1,2}, - {-3,1,2}, - - {1,3,2}, - {-1,-3,2}, - - {-2,-1,3}, // 0 degrees yaw, look straight up - {2,-1,-3} // look straight down - -// {-1,2,3}, -// {1,2,-3} -}; - -// s = [0]/[2], t = [1]/[2] -int vec_to_st[6][3] = -{ - {-2,3,1}, - {2,3,-1}, - - {1,3,2}, - {-1,3,-2}, - - {-2,-1,3}, - {-2,1,-3} - -// {-1,2,3}, -// {1,2,-3} -}; - -float skymins[2][6], skymaxs[2][6]; - -void DrawSkyPolygon (int nump, vec3_t vecs) -{ - int i,j; - vec3_t v, av; - float s, t, dv; - int axis; - float *vp; - - c_sky++; -#if 0 -glBegin (GL_POLYGON); -for (i=0 ; i av[1] && av[0] > av[2]) - { - if (v[0] < 0) - axis = 1; - else - axis = 0; - } - else if (av[1] > av[2] && av[1] > av[0]) - { - if (v[1] < 0) - axis = 3; - else - axis = 2; - } - else - { - if (v[2] < 0) - axis = 5; - else - axis = 4; - } - - // project new texture coords - for (i=0 ; i 0) - dv = vecs[j - 1]; - else - dv = -vecs[-j - 1]; - - j = vec_to_st[axis][0]; - if (j < 0) - s = -vecs[-j -1] / dv; - else - s = vecs[j-1] / dv; - j = vec_to_st[axis][1]; - if (j < 0) - t = -vecs[-j -1] / dv; - else - t = vecs[j-1] / dv; - - if (s < skymins[0][axis]) - skymins[0][axis] = s; - if (t < skymins[1][axis]) - skymins[1][axis] = t; - if (s > skymaxs[0][axis]) - skymaxs[0][axis] = s; - if (t > skymaxs[1][axis]) - skymaxs[1][axis] = t; - } -} - -#define MAX_CLIP_VERTS 64 -void ClipSkyPolygon (int nump, vec3_t vecs, int stage) -{ - float *norm; - float *v; - qboolean front, back; - float d, e; - float dists[MAX_CLIP_VERTS]; - int sides[MAX_CLIP_VERTS]; - vec3_t newv[2][MAX_CLIP_VERTS]; - int newc[2]; - int i, j; - - if (nump > MAX_CLIP_VERTS-2) - Sys_Error ("ClipSkyPolygon: MAX_CLIP_VERTS"); - if (stage == 6) - { // fully clipped, so draw it - DrawSkyPolygon (nump, vecs); - return; - } - - front = back = false; - norm = skyclip[stage]; - for (i=0, v = vecs ; i ON_EPSILON) - { - front = true; - sides[i] = SIDE_FRONT; - } - else if (d < ON_EPSILON) - { - back = true; - sides[i] = SIDE_BACK; - } - else - sides[i] = SIDE_ON; - dists[i] = d; - } - - if (!front || !back) - { // not clipped - ClipSkyPolygon (nump, vecs, stage+1); - return; - } - - // clip it - sides[i] = sides[0]; - dists[i] = dists[0]; - VectorCopy (vecs, (vecs+(i*3)) ); - newc[0] = newc[1] = 0; - - for (i=0, v = vecs ; itexturechain) - { - for (p=fa->polys ; p ; p=p->next) - { - for (i=0 ; inumverts ; i++) - { - VectorSubtract (p->verts[i], r_origin, verts[i]); - } - ClipSkyPolygon (p->numverts, verts[0], 0); - } - } -} - - -/* -============== -R_ClearSkyBox -============== -*/ -void R_ClearSkyBox (void) -{ - int i; - - for (i=0 ; i<6 ; i++) - { - skymins[0][i] = skymins[1][i] = 9999; - skymaxs[0][i] = skymaxs[1][i] = -9999; - } -} - - -void MakeSkyVec (float s, float t, int axis) -{ - vec3_t v, b; - int j, k; - - b[0] = s*2048; - b[1] = t*2048; - b[2] = 2048; - - for (j=0 ; j<3 ; j++) - { - k = st_to_vec[axis][j]; - if (k < 0) - v[j] = -b[-k - 1]; - else - v[j] = b[k - 1]; - v[j] += r_origin[j]; - } - - // avoid bilerp seam - s = (s+1)*0.5; - t = (t+1)*0.5; - - if (s < 1.0/512) - s = 1.0/512; - else if (s > 511.0/512) - s = 511.0/512; - if (t < 1.0/512) - t = 1.0/512; - else if (t > 511.0/512) - t = 511.0/512; - - t = 1.0 - t; - glTexCoord2f (s, t); - glVertex3fv (v); -} - -/* -============== -R_DrawSkyBox -============== -*/ -int skytexorder[6] = {0,2,1,3,4,5}; -void R_DrawSkyBox (void) -{ - int i, j, k; - vec3_t v; - float s, t; - -#if 0 -glEnable (GL_BLEND); -glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); -glColor4f (1,1,1,0.5); -glDisable (GL_DEPTH_TEST); -#endif - for (i=0 ; i<6 ; i++) - { - if (skymins[0][i] >= skymaxs[0][i] - || skymins[1][i] >= skymaxs[1][i]) - continue; - - GL_Bind (SKY_TEX+skytexorder[i]); -#if 0 -skymins[0][i] = -1; -skymins[1][i] = -1; -skymaxs[0][i] = 1; -skymaxs[1][i] = 1; -#endif - glBegin (GL_QUADS); - MakeSkyVec (skymins[0][i], skymins[1][i], i); - MakeSkyVec (skymins[0][i], skymaxs[1][i], i); - MakeSkyVec (skymaxs[0][i], skymaxs[1][i], i); - MakeSkyVec (skymaxs[0][i], skymins[1][i], i); - glEnd (); - } -#if 0 -glDisable (GL_BLEND); -glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); -glColor4f (1,1,1,0.5); -glEnable (GL_DEPTH_TEST); -#endif -} - - -#endif - -//=============================================================== - -/* -============= -R_InitSky - -A sky texture is 256*128, with the right side being a masked overlay -============== -*/ -void R_InitSky (texture_t *mt) -{ - int i, j, p; - byte *src; - unsigned trans[128*128]; - unsigned transpix; - int r, g, b; - unsigned *rgba; - extern int skytexturenum; - - src = (byte *)mt + mt->offsets[0]; - - // make an average value for the back to avoid - // a fringe on the top level - - r = g = b = 0; - for (i=0 ; i<128 ; i++) - for (j=0 ; j<128 ; j++) - { - p = src[i*256 + j + 128]; - rgba = &d_8to24table[p]; - trans[(i*128) + j] = *rgba; - r += ((byte *)rgba)[0]; - g += ((byte *)rgba)[1]; - b += ((byte *)rgba)[2]; - } - - ((byte *)&transpix)[0] = r/(128*128); - ((byte *)&transpix)[1] = g/(128*128); - ((byte *)&transpix)[2] = b/(128*128); - ((byte *)&transpix)[3] = 0; - - - if (!solidskytexture) - solidskytexture = texture_extension_number++; - GL_Bind (solidskytexture ); - glTexImage2D (GL_TEXTURE_2D, 0, gl_solid_format, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, trans); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - - for (i=0 ; i<128 ; i++) - for (j=0 ; j<128 ; j++) - { - p = src[i*256 + j]; - if (p == 0) - trans[(i*128) + j] = transpix; - else - trans[(i*128) + j] = d_8to24table[p]; - } - - if (!alphaskytexture) - alphaskytexture = texture_extension_number++; - GL_Bind(alphaskytexture); - glTexImage2D (GL_TEXTURE_2D, 0, gl_alpha_format, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, trans); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); -} - +/* +Copyright (C) 1996-1997 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. + +*/ +// gl_warp.c -- sky and water polygons + +#include "quakedef.h" + +extern model_t *loadmodel; + +int skytexturenum; + +int solidskytexture; +int alphaskytexture; +float speedscale; // for top sky and bottom sky + +msurface_t *warpface; + +extern cvar_t gl_subdivide_size; + +void BoundPoly (int numverts, float *verts, vec3_t mins, vec3_t maxs) +{ + int i, j; + float *v; + + mins[0] = mins[1] = mins[2] = 9999; + maxs[0] = maxs[1] = maxs[2] = -9999; + v = verts; + for (i=0 ; i maxs[j]) + maxs[j] = *v; + } +} + +void SubdividePolygon (int numverts, float *verts) +{ + int i, j, k; + vec3_t mins, maxs; + float m; + float *v; + vec3_t front[64], back[64]; + int f, b; + float dist[64]; + float frac; + glpoly_t *poly; + float s, t; + + if (numverts > 60) + Sys_Error ("numverts = %i", numverts); + + BoundPoly (numverts, verts, mins, maxs); + + for (i=0 ; i<3 ; i++) + { + m = (mins[i] + maxs[i]) * 0.5; + m = gl_subdivide_size.value * floor (m/gl_subdivide_size.value + 0.5); + if (maxs[i] - m < 8) + continue; + if (m - mins[i] < 8) + continue; + + // cut it + v = verts + i; + for (j=0 ; j= 0) + { + VectorCopy (v, front[f]); + f++; + } + if (dist[j] <= 0) + { + VectorCopy (v, back[b]); + b++; + } + if (dist[j] == 0 || dist[j+1] == 0) + continue; + if ( (dist[j] > 0) != (dist[j+1] > 0) ) + { + // clip point + frac = dist[j] / (dist[j] - dist[j+1]); + for (k=0 ; k<3 ; k++) + front[f][k] = back[b][k] = v[k] + frac*(v[3+k] - v[k]); + f++; + b++; + } + } + + SubdividePolygon (f, front[0]); + SubdividePolygon (b, back[0]); + return; + } + + poly = Hunk_Alloc (sizeof(glpoly_t) + (numverts-4) * VERTEXSIZE*sizeof(float)); + poly->next = warpface->polys; + warpface->polys = poly; + poly->numverts = numverts; + for (i=0 ; iverts[i]); + s = DotProduct (verts, warpface->texinfo->vecs[0]); + t = DotProduct (verts, warpface->texinfo->vecs[1]); + poly->verts[i][3] = s; + poly->verts[i][4] = t; + } +} + +/* +================ +GL_SubdivideSurface + +Breaks a polygon up along axial 64 unit +boundaries so that turbulent and sky warps +can be done reasonably. +================ +*/ +void GL_SubdivideSurface (msurface_t *fa) +{ + vec3_t verts[64]; + int numverts; + int i; + int lindex; + float *vec; + + warpface = fa; + + // + // convert edges back to a normal polygon + // + numverts = 0; + for (i=0 ; inumedges ; i++) + { + lindex = loadmodel->surfedges[fa->firstedge + i]; + + if (lindex > 0) + vec = loadmodel->vertexes[loadmodel->edges[lindex].v[0]].position; + else + vec = loadmodel->vertexes[loadmodel->edges[-lindex].v[1]].position; + VectorCopy (vec, verts[numverts]); + numverts++; + } + + SubdividePolygon (numverts, verts[0]); +} + +//========================================================= + + + +// speed up sin calculations - Ed +float turbsin[] = +{ + #include "gl_warp_sin.h" +}; +#define TURBSCALE (256.0 / (2 * M_PI)) + +/* +============= +EmitWaterPolys + +Does a water warp on the pre-fragmented glpoly_t chain +============= +*/ +void EmitWaterPolys (msurface_t *fa) +{ + glpoly_t *p; + float *v; + int i; + float s, t, os, ot; + + + for (p=fa->polys ; p ; p=p->next) + { + glBegin (GL_POLYGON); + for (i=0,v=p->verts[0] ; inumverts ; i++, v+=VERTEXSIZE) + { + os = v[3]; + ot = v[4]; + + s = os + turbsin[(int)((ot*0.125+realtime) * TURBSCALE) & 255]; + s *= (1.0/64); + + t = ot + turbsin[(int)((os*0.125+realtime) * TURBSCALE) & 255]; + t *= (1.0/64); + + glTexCoord2f (s, t); + glVertex3fv (v); + } + glEnd (); + } +} + + + + +/* +============= +EmitSkyPolys +============= +*/ +void EmitSkyPolys (msurface_t *fa) +{ + glpoly_t *p; + float *v; + int i; + float s, t; + vec3_t dir; + float length; + + for (p=fa->polys ; p ; p=p->next) + { + glBegin (GL_POLYGON); + for (i=0,v=p->verts[0] ; inumverts ; i++, v+=VERTEXSIZE) + { + VectorSubtract (v, r_origin, dir); + dir[2] *= 3; // flatten the sphere + + length = dir[0]*dir[0] + dir[1]*dir[1] + dir[2]*dir[2]; + length = sqrt (length); + length = 6*63/length; + + dir[0] *= length; + dir[1] *= length; + + s = (speedscale + dir[0]) * (1.0/128); + t = (speedscale + dir[1]) * (1.0/128); + + glTexCoord2f (s, t); + glVertex3fv (v); + } + glEnd (); + } +} + +/* +=============== +EmitBothSkyLayers + +Does a sky warp on the pre-fragmented glpoly_t chain +This will be called for brushmodels, the world +will have them chained together. +=============== +*/ +void EmitBothSkyLayers (msurface_t *fa) +{ + GL_DisableMultitexture(); + + GL_Bind (solidskytexture); + speedscale = realtime*8; + speedscale -= (int)speedscale & ~127 ; + + EmitSkyPolys (fa); + + glEnable (GL_BLEND); + GL_Bind (alphaskytexture); + speedscale = realtime*16; + speedscale -= (int)speedscale & ~127 ; + + EmitSkyPolys (fa); + + glDisable (GL_BLEND); +} + +#ifndef QUAKE2 +/* +================= +R_DrawSkyChain +================= +*/ +void R_DrawSkyChain (msurface_t *s) +{ + msurface_t *fa; + + GL_DisableMultitexture(); + + // used when gl_texsort is on + GL_Bind(solidskytexture); + speedscale = realtime*8; + speedscale -= (int)speedscale & ~127 ; + + for (fa=s ; fa ; fa=fa->texturechain) + EmitSkyPolys (fa); + + glEnable (GL_BLEND); + GL_Bind (alphaskytexture); + speedscale = realtime*16; + speedscale -= (int)speedscale & ~127 ; + + for (fa=s ; fa ; fa=fa->texturechain) + EmitSkyPolys (fa); + + glDisable (GL_BLEND); +} + +#endif + +/* +================================================================= + + Quake 2 environment sky + +================================================================= +*/ + +#ifdef QUAKE2 + + +#define SKY_TEX 2000 + +/* +================================================================= + + PCX Loading + +================================================================= +*/ + +typedef struct +{ + char manufacturer; + char version; + char encoding; + char bits_per_pixel; + unsigned short xmin,ymin,xmax,ymax; + unsigned short hres,vres; + unsigned char palette[48]; + char reserved; + char color_planes; + unsigned short bytes_per_line; + unsigned short palette_type; + char filler[58]; + unsigned data; // unbounded +} pcx_t; + +byte *pcx_rgb; + +/* +============ +LoadPCX +============ +*/ +void LoadPCX (FILE *f) +{ + pcx_t *pcx, pcxbuf; + byte palette[768]; + byte *pix; + int x, y; + int dataByte, runLength; + int count; + +// +// parse the PCX file +// + fread (&pcxbuf, 1, sizeof(pcxbuf), f); + + pcx = &pcxbuf; + + if (pcx->manufacturer != 0x0a + || pcx->version != 5 + || pcx->encoding != 1 + || pcx->bits_per_pixel != 8 + || pcx->xmax >= 320 + || pcx->ymax >= 256) + { + Con_Printf ("Bad pcx file\n"); + return; + } + + // seek to palette + fseek (f, -768, SEEK_END); + fread (palette, 1, 768, f); + + fseek (f, sizeof(pcxbuf) - 4, SEEK_SET); + + count = (pcx->xmax+1) * (pcx->ymax+1); + pcx_rgb = Q_Malloc ( count * 4); + + for (y=0 ; y<=pcx->ymax ; y++) + { + pix = pcx_rgb + 4*y*(pcx->xmax+1); + for (x=0 ; x<=pcx->ymax ; ) + { + dataByte = fgetc(f); + + if((dataByte & 0xC0) == 0xC0) + { + runLength = dataByte & 0x3F; + dataByte = fgetc(f); + } + else + runLength = 1; + + while(runLength-- > 0) + { + pix[0] = palette[dataByte*3]; + pix[1] = palette[dataByte*3+1]; + pix[2] = palette[dataByte*3+2]; + pix[3] = 255; + pix += 4; + x++; + } + } + } +} + +/* +========================================================= + +TARGA LOADING + +========================================================= +*/ + +typedef struct _TargaHeader { + unsigned char id_length, colormap_type, image_type; + unsigned short colormap_index, colormap_length; + unsigned char colormap_size; + unsigned short x_origin, y_origin, width, height; + unsigned char pixel_size, attributes; +} TargaHeader; + + +TargaHeader targa_header; +byte *targa_rgba; + +int fgetLittleShort (FILE *f) +{ + byte b1, b2; + + b1 = fgetc(f); + b2 = fgetc(f); + + return (short)(b1 + b2*256); +} + +int fgetLittleLong (FILE *f) +{ + byte b1, b2, b3, b4; + + b1 = fgetc(f); + b2 = fgetc(f); + b3 = fgetc(f); + b4 = fgetc(f); + + return b1 + (b2<<8) + (b3<<16) + (b4<<24); +} + + +/* +============= +LoadTGA +============= +*/ +void LoadTGA (FILE *fin) +{ + int columns, rows, numPixels; + byte *pixbuf; + int row, column; + + targa_header.id_length = fgetc(fin); + targa_header.colormap_type = fgetc(fin); + targa_header.image_type = fgetc(fin); + + targa_header.colormap_index = fgetLittleShort(fin); + targa_header.colormap_length = fgetLittleShort(fin); + targa_header.colormap_size = fgetc(fin); + targa_header.x_origin = fgetLittleShort(fin); + targa_header.y_origin = fgetLittleShort(fin); + targa_header.width = fgetLittleShort(fin); + targa_header.height = fgetLittleShort(fin); + targa_header.pixel_size = fgetc(fin); + targa_header.attributes = fgetc(fin); + + if (targa_header.image_type!=2 + && targa_header.image_type!=10) + Sys_Error ("LoadTGA: Only type 2 and 10 targa RGB images supported\n"); + + if (targa_header.colormap_type !=0 + || (targa_header.pixel_size!=32 && targa_header.pixel_size!=24)) + Sys_Error ("Texture_LoadTGA: Only 32 or 24 bit images supported (no colormaps)\n"); + + columns = targa_header.width; + rows = targa_header.height; + numPixels = columns * rows; + + targa_rgba = Q_Malloc (numPixels*4); + + if (targa_header.id_length != 0) + fseek(fin, targa_header.id_length, SEEK_CUR); // skip TARGA image comment + + if (targa_header.image_type==2) { // Uncompressed, RGB images + for(row=rows-1; row>=0; row--) { + pixbuf = targa_rgba + row*columns*4; + for(column=0; column=0; row--) { + pixbuf = targa_rgba + row*columns*4; + for(column=0; column0) + row--; + else + goto breakOut; + pixbuf = targa_rgba + row*columns*4; + } + } + } + else { // non run-length packet + for(j=0;j0) + row--; + else + goto breakOut; + pixbuf = targa_rgba + row*columns*4; + } + } + } + } + breakOut:; + } + } + + fclose(fin); +} + +/* +================== +R_LoadSkys +================== +*/ +char *suf[6] = {"rt", "bk", "lf", "ft", "up", "dn"}; +void R_LoadSkys (void) +{ + int i; + FILE *f; + char name[64]; + + for (i=0 ; i<6 ; i++) + { + GL_Bind (SKY_TEX + i); + sprintf (name, "gfx/env/bkgtst%s.tga", suf[i]); + COM_FOpenFile (name, &f); + if (!f) + { + Con_Printf ("Couldn't load %s\n", name); + continue; + } + LoadTGA (f); +// LoadPCX (f); + + glTexImage2D (GL_TEXTURE_2D, 0, gl_solid_format, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, targa_rgba); +// glTexImage2D (GL_TEXTURE_2D, 0, gl_solid_format, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, pcx_rgb); + + free (targa_rgba); +// free (pcx_rgb); + + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } +} + + +vec3_t skyclip[6] = { + {1,1,0}, + {1,-1,0}, + {0,-1,1}, + {0,1,1}, + {1,0,1}, + {-1,0,1} +}; +int c_sky; + +// 1 = s, 2 = t, 3 = 2048 +int st_to_vec[6][3] = +{ + {3,-1,2}, + {-3,1,2}, + + {1,3,2}, + {-1,-3,2}, + + {-2,-1,3}, // 0 degrees yaw, look straight up + {2,-1,-3} // look straight down + +// {-1,2,3}, +// {1,2,-3} +}; + +// s = [0]/[2], t = [1]/[2] +int vec_to_st[6][3] = +{ + {-2,3,1}, + {2,3,-1}, + + {1,3,2}, + {-1,3,-2}, + + {-2,-1,3}, + {-2,1,-3} + +// {-1,2,3}, +// {1,2,-3} +}; + +float skymins[2][6], skymaxs[2][6]; + +void DrawSkyPolygon (int nump, vec3_t vecs) +{ + int i,j; + vec3_t v, av; + float s, t, dv; + int axis; + float *vp; + + c_sky++; +#if 0 +glBegin (GL_POLYGON); +for (i=0 ; i av[1] && av[0] > av[2]) + { + if (v[0] < 0) + axis = 1; + else + axis = 0; + } + else if (av[1] > av[2] && av[1] > av[0]) + { + if (v[1] < 0) + axis = 3; + else + axis = 2; + } + else + { + if (v[2] < 0) + axis = 5; + else + axis = 4; + } + + // project new texture coords + for (i=0 ; i 0) + dv = vecs[j - 1]; + else + dv = -vecs[-j - 1]; + + j = vec_to_st[axis][0]; + if (j < 0) + s = -vecs[-j -1] / dv; + else + s = vecs[j-1] / dv; + j = vec_to_st[axis][1]; + if (j < 0) + t = -vecs[-j -1] / dv; + else + t = vecs[j-1] / dv; + + if (s < skymins[0][axis]) + skymins[0][axis] = s; + if (t < skymins[1][axis]) + skymins[1][axis] = t; + if (s > skymaxs[0][axis]) + skymaxs[0][axis] = s; + if (t > skymaxs[1][axis]) + skymaxs[1][axis] = t; + } +} + +#define MAX_CLIP_VERTS 64 +void ClipSkyPolygon (int nump, vec3_t vecs, int stage) +{ + float *norm; + float *v; + qboolean front, back; + float d, e; + float dists[MAX_CLIP_VERTS]; + int sides[MAX_CLIP_VERTS]; + vec3_t newv[2][MAX_CLIP_VERTS]; + int newc[2]; + int i, j; + + if (nump > MAX_CLIP_VERTS-2) + Sys_Error ("ClipSkyPolygon: MAX_CLIP_VERTS"); + if (stage == 6) + { // fully clipped, so draw it + DrawSkyPolygon (nump, vecs); + return; + } + + front = back = false; + norm = skyclip[stage]; + for (i=0, v = vecs ; i ON_EPSILON) + { + front = true; + sides[i] = SIDE_FRONT; + } + else if (d < ON_EPSILON) + { + back = true; + sides[i] = SIDE_BACK; + } + else + sides[i] = SIDE_ON; + dists[i] = d; + } + + if (!front || !back) + { // not clipped + ClipSkyPolygon (nump, vecs, stage+1); + return; + } + + // clip it + sides[i] = sides[0]; + dists[i] = dists[0]; + VectorCopy (vecs, (vecs+(i*3)) ); + newc[0] = newc[1] = 0; + + for (i=0, v = vecs ; itexturechain) + { + for (p=fa->polys ; p ; p=p->next) + { + for (i=0 ; inumverts ; i++) + { + VectorSubtract (p->verts[i], r_origin, verts[i]); + } + ClipSkyPolygon (p->numverts, verts[0], 0); + } + } +} + + +/* +============== +R_ClearSkyBox +============== +*/ +void R_ClearSkyBox (void) +{ + int i; + + for (i=0 ; i<6 ; i++) + { + skymins[0][i] = skymins[1][i] = 9999; + skymaxs[0][i] = skymaxs[1][i] = -9999; + } +} + + +void MakeSkyVec (float s, float t, int axis) +{ + vec3_t v, b; + int j, k; + + b[0] = s*2048; + b[1] = t*2048; + b[2] = 2048; + + for (j=0 ; j<3 ; j++) + { + k = st_to_vec[axis][j]; + if (k < 0) + v[j] = -b[-k - 1]; + else + v[j] = b[k - 1]; + v[j] += r_origin[j]; + } + + // avoid bilerp seam + s = (s+1)*0.5; + t = (t+1)*0.5; + + if (s < 1.0/512) + s = 1.0/512; + else if (s > 511.0/512) + s = 511.0/512; + if (t < 1.0/512) + t = 1.0/512; + else if (t > 511.0/512) + t = 511.0/512; + + t = 1.0 - t; + glTexCoord2f (s, t); + glVertex3fv (v); +} + +/* +============== +R_DrawSkyBox +============== +*/ +int skytexorder[6] = {0,2,1,3,4,5}; +void R_DrawSkyBox (void) +{ + int i, j, k; + vec3_t v; + float s, t; + +#if 0 +glEnable (GL_BLEND); +glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); +glColor4f (1,1,1,0.5); +glDisable (GL_DEPTH_TEST); +#endif + for (i=0 ; i<6 ; i++) + { + if (skymins[0][i] >= skymaxs[0][i] + || skymins[1][i] >= skymaxs[1][i]) + continue; + + GL_Bind (SKY_TEX+skytexorder[i]); +#if 0 +skymins[0][i] = -1; +skymins[1][i] = -1; +skymaxs[0][i] = 1; +skymaxs[1][i] = 1; +#endif + glBegin (GL_QUADS); + MakeSkyVec (skymins[0][i], skymins[1][i], i); + MakeSkyVec (skymins[0][i], skymaxs[1][i], i); + MakeSkyVec (skymaxs[0][i], skymaxs[1][i], i); + MakeSkyVec (skymaxs[0][i], skymins[1][i], i); + glEnd (); + } +#if 0 +glDisable (GL_BLEND); +glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); +glColor4f (1,1,1,0.5); +glEnable (GL_DEPTH_TEST); +#endif +} + + +#endif + +//=============================================================== + +/* +============= +R_InitSky + +A sky texture is 256*128, with the right side being a masked overlay +============== +*/ +void R_InitSky (texture_t *mt) +{ + int i, j, p; + byte *src; + unsigned trans[128*128]; + unsigned transpix; + int r, g, b; + unsigned *rgba; + extern int skytexturenum; + + src = (byte *)mt + mt->offsets[0]; + + // make an average value for the back to avoid + // a fringe on the top level + + r = g = b = 0; + for (i=0 ; i<128 ; i++) + for (j=0 ; j<128 ; j++) + { + p = src[i*256 + j + 128]; + rgba = &d_8to24table[p]; + trans[(i*128) + j] = *rgba; + r += ((byte *)rgba)[0]; + g += ((byte *)rgba)[1]; + b += ((byte *)rgba)[2]; + } + + ((byte *)&transpix)[0] = r/(128*128); + ((byte *)&transpix)[1] = g/(128*128); + ((byte *)&transpix)[2] = b/(128*128); + ((byte *)&transpix)[3] = 0; + + + if (!solidskytexture) + solidskytexture = texture_extension_number++; + GL_Bind (solidskytexture ); + glTexImage2D (GL_TEXTURE_2D, 0, gl_solid_format, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, trans); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + + for (i=0 ; i<128 ; i++) + for (j=0 ; j<128 ; j++) + { + p = src[i*256 + j]; + if (p == 0) + trans[(i*128) + j] = transpix; + else + trans[(i*128) + j] = d_8to24table[p]; + } + + if (!alphaskytexture) + alphaskytexture = texture_extension_number++; + GL_Bind(alphaskytexture); + glTexImage2D (GL_TEXTURE_2D, 0, gl_alpha_format, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, trans); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); +} + diff --git a/source/gl_warp_sin.h b/source/gl_warp_sin.h index 4d9cb3d5..22976a73 100644 --- a/source/gl_warp_sin.h +++ b/source/gl_warp_sin.h @@ -1,51 +1,51 @@ -/* -Copyright (C) 1996-1997 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. - -*/ - 0, 0.19633, 0.392541, 0.588517, 0.784137, 0.979285, 1.17384, 1.3677, - 1.56072, 1.75281, 1.94384, 2.1337, 2.32228, 2.50945, 2.69512, 2.87916, - 3.06147, 3.24193, 3.42044, 3.59689, 3.77117, 3.94319, 4.11282, 4.27998, - 4.44456, 4.60647, 4.76559, 4.92185, 5.07515, 5.22538, 5.37247, 5.51632, - 5.65685, 5.79398, 5.92761, 6.05767, 6.18408, 6.30677, 6.42566, 6.54068, - 6.65176, 6.75883, 6.86183, 6.9607, 7.05537, 7.14579, 7.23191, 7.31368, - 7.39104, 7.46394, 7.53235, 7.59623, 7.65552, 7.71021, 7.76025, 7.80562, - 7.84628, 7.88222, 7.91341, 7.93984, 7.96148, 7.97832, 7.99036, 7.99759, - 8, 7.99759, 7.99036, 7.97832, 7.96148, 7.93984, 7.91341, 7.88222, - 7.84628, 7.80562, 7.76025, 7.71021, 7.65552, 7.59623, 7.53235, 7.46394, - 7.39104, 7.31368, 7.23191, 7.14579, 7.05537, 6.9607, 6.86183, 6.75883, - 6.65176, 6.54068, 6.42566, 6.30677, 6.18408, 6.05767, 5.92761, 5.79398, - 5.65685, 5.51632, 5.37247, 5.22538, 5.07515, 4.92185, 4.76559, 4.60647, - 4.44456, 4.27998, 4.11282, 3.94319, 3.77117, 3.59689, 3.42044, 3.24193, - 3.06147, 2.87916, 2.69512, 2.50945, 2.32228, 2.1337, 1.94384, 1.75281, - 1.56072, 1.3677, 1.17384, 0.979285, 0.784137, 0.588517, 0.392541, 0.19633, - 9.79717e-16, -0.19633, -0.392541, -0.588517, -0.784137, -0.979285, -1.17384, -1.3677, - -1.56072, -1.75281, -1.94384, -2.1337, -2.32228, -2.50945, -2.69512, -2.87916, - -3.06147, -3.24193, -3.42044, -3.59689, -3.77117, -3.94319, -4.11282, -4.27998, - -4.44456, -4.60647, -4.76559, -4.92185, -5.07515, -5.22538, -5.37247, -5.51632, - -5.65685, -5.79398, -5.92761, -6.05767, -6.18408, -6.30677, -6.42566, -6.54068, - -6.65176, -6.75883, -6.86183, -6.9607, -7.05537, -7.14579, -7.23191, -7.31368, - -7.39104, -7.46394, -7.53235, -7.59623, -7.65552, -7.71021, -7.76025, -7.80562, - -7.84628, -7.88222, -7.91341, -7.93984, -7.96148, -7.97832, -7.99036, -7.99759, - -8, -7.99759, -7.99036, -7.97832, -7.96148, -7.93984, -7.91341, -7.88222, - -7.84628, -7.80562, -7.76025, -7.71021, -7.65552, -7.59623, -7.53235, -7.46394, - -7.39104, -7.31368, -7.23191, -7.14579, -7.05537, -6.9607, -6.86183, -6.75883, - -6.65176, -6.54068, -6.42566, -6.30677, -6.18408, -6.05767, -5.92761, -5.79398, - -5.65685, -5.51632, -5.37247, -5.22538, -5.07515, -4.92185, -4.76559, -4.60647, - -4.44456, -4.27998, -4.11282, -3.94319, -3.77117, -3.59689, -3.42044, -3.24193, - -3.06147, -2.87916, -2.69512, -2.50945, -2.32228, -2.1337, -1.94384, -1.75281, - -1.56072, -1.3677, -1.17384, -0.979285, -0.784137, -0.588517, -0.392541, -0.19633, +/* +Copyright (C) 1996-1997 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. + +*/ + 0, 0.19633, 0.392541, 0.588517, 0.784137, 0.979285, 1.17384, 1.3677, + 1.56072, 1.75281, 1.94384, 2.1337, 2.32228, 2.50945, 2.69512, 2.87916, + 3.06147, 3.24193, 3.42044, 3.59689, 3.77117, 3.94319, 4.11282, 4.27998, + 4.44456, 4.60647, 4.76559, 4.92185, 5.07515, 5.22538, 5.37247, 5.51632, + 5.65685, 5.79398, 5.92761, 6.05767, 6.18408, 6.30677, 6.42566, 6.54068, + 6.65176, 6.75883, 6.86183, 6.9607, 7.05537, 7.14579, 7.23191, 7.31368, + 7.39104, 7.46394, 7.53235, 7.59623, 7.65552, 7.71021, 7.76025, 7.80562, + 7.84628, 7.88222, 7.91341, 7.93984, 7.96148, 7.97832, 7.99036, 7.99759, + 8, 7.99759, 7.99036, 7.97832, 7.96148, 7.93984, 7.91341, 7.88222, + 7.84628, 7.80562, 7.76025, 7.71021, 7.65552, 7.59623, 7.53235, 7.46394, + 7.39104, 7.31368, 7.23191, 7.14579, 7.05537, 6.9607, 6.86183, 6.75883, + 6.65176, 6.54068, 6.42566, 6.30677, 6.18408, 6.05767, 5.92761, 5.79398, + 5.65685, 5.51632, 5.37247, 5.22538, 5.07515, 4.92185, 4.76559, 4.60647, + 4.44456, 4.27998, 4.11282, 3.94319, 3.77117, 3.59689, 3.42044, 3.24193, + 3.06147, 2.87916, 2.69512, 2.50945, 2.32228, 2.1337, 1.94384, 1.75281, + 1.56072, 1.3677, 1.17384, 0.979285, 0.784137, 0.588517, 0.392541, 0.19633, + 9.79717e-16, -0.19633, -0.392541, -0.588517, -0.784137, -0.979285, -1.17384, -1.3677, + -1.56072, -1.75281, -1.94384, -2.1337, -2.32228, -2.50945, -2.69512, -2.87916, + -3.06147, -3.24193, -3.42044, -3.59689, -3.77117, -3.94319, -4.11282, -4.27998, + -4.44456, -4.60647, -4.76559, -4.92185, -5.07515, -5.22538, -5.37247, -5.51632, + -5.65685, -5.79398, -5.92761, -6.05767, -6.18408, -6.30677, -6.42566, -6.54068, + -6.65176, -6.75883, -6.86183, -6.9607, -7.05537, -7.14579, -7.23191, -7.31368, + -7.39104, -7.46394, -7.53235, -7.59623, -7.65552, -7.71021, -7.76025, -7.80562, + -7.84628, -7.88222, -7.91341, -7.93984, -7.96148, -7.97832, -7.99036, -7.99759, + -8, -7.99759, -7.99036, -7.97832, -7.96148, -7.93984, -7.91341, -7.88222, + -7.84628, -7.80562, -7.76025, -7.71021, -7.65552, -7.59623, -7.53235, -7.46394, + -7.39104, -7.31368, -7.23191, -7.14579, -7.05537, -6.9607, -6.86183, -6.75883, + -6.65176, -6.54068, -6.42566, -6.30677, -6.18408, -6.05767, -5.92761, -5.79398, + -5.65685, -5.51632, -5.37247, -5.22538, -5.07515, -4.92185, -4.76559, -4.60647, + -4.44456, -4.27998, -4.11282, -3.94319, -3.77117, -3.59689, -3.42044, -3.24193, + -3.06147, -2.87916, -2.69512, -2.50945, -2.32228, -2.1337, -1.94384, -1.75281, + -1.56072, -1.3677, -1.17384, -0.979285, -0.784137, -0.588517, -0.392541, -0.19633, diff --git a/source/glquake.h b/source/glquake.h index 3641c1d3..273b7167 100644 --- a/source/glquake.h +++ b/source/glquake.h @@ -1,286 +1,286 @@ -/* -Copyright (C) 1996-1997 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. - -*/ - -// disable data conversion warnings - -#pragma warning(disable : 4244) // MIPS -#pragma warning(disable : 4136) // X86 -#pragma warning(disable : 4051) // ALPHA - -#ifdef _WIN32 -#include -#endif - -#include -#include - -void GL_BeginRendering (int *x, int *y, int *width, int *height); -void GL_EndRendering (void); - - -// Function prototypes for the Texture Object Extension routines -typedef GLboolean (APIENTRY *ARETEXRESFUNCPTR)(GLsizei, const GLuint *, - const GLboolean *); -typedef void (APIENTRY *BINDTEXFUNCPTR)(GLenum, GLuint); -typedef void (APIENTRY *DELTEXFUNCPTR)(GLsizei, const GLuint *); -typedef void (APIENTRY *GENTEXFUNCPTR)(GLsizei, GLuint *); -typedef GLboolean (APIENTRY *ISTEXFUNCPTR)(GLuint); -typedef void (APIENTRY *PRIORTEXFUNCPTR)(GLsizei, const GLuint *, - const GLclampf *); -typedef void (APIENTRY *TEXSUBIMAGEPTR)(int, int, int, int, int, int, int, int, void *); - -extern BINDTEXFUNCPTR bindTexFunc; -extern DELTEXFUNCPTR delTexFunc; -extern TEXSUBIMAGEPTR TexSubImage2DFunc; - -extern int texture_extension_number; -extern int texture_mode; - -extern float gldepthmin, gldepthmax; - -#define MAX_GLTEXTURES 1024 - -void GL_Upload32 (unsigned *data, int width, int height, qboolean mipmap, qboolean alpha); -void GL_Upload8 (byte *data, int width, int height, qboolean mipmap, qboolean alpha, qboolean brighten); -void GL_Upload8_EXT (byte *data, int width, int height, qboolean mipmap, qboolean alpha); -int GL_LoadTexture (char *identifier, int width, int height, byte *data, qboolean mipmap, qboolean alpha, qboolean brighten); -int GL_FindTexture (char *identifier); - -typedef struct -{ - float x, y, z; - float s, t; - float r, g, b; -} glvert_t; - -extern glvert_t glv; - -extern int glx, gly, glwidth, glheight; - -#ifdef _WIN32 -extern PROC glArrayElementEXT; -extern PROC glColorPointerEXT; -extern PROC glTexturePointerEXT; -extern PROC glVertexPointerEXT; -#endif - -// r_local.h -- private refresh defs - -#define ALIAS_BASE_SIZE_RATIO (1.0 / 11.0) - // normalizing factor so player model works out to about - // 1 pixel per triangle -#define MAX_LBM_HEIGHT 480 - -#define TILE_SIZE 128 // size of textures generated by R_GenTiledSurf - -#define SKYSHIFT 7 -#define SKYSIZE (1 << SKYSHIFT) -#define SKYMASK (SKYSIZE - 1) - -#define BACKFACE_EPSILON 0.01 - - -void R_TimeRefresh_f (void); -void R_ReadPointFile_f (void); -texture_t *R_TextureAnimation (texture_t *base); - - -typedef enum { - pt_static, pt_grav, pt_slowgrav, pt_fire, pt_explode, pt_explode2, pt_blob, pt_blob2 -} ptype_t; - -// !!! if this is changed, it must be changed in d_ifacea.h too !!! -typedef struct particle_s -{ -// driver-usable fields - vec3_t org; - float color; -// drivers never touch the following fields - struct particle_s *next; - vec3_t vel; - float ramp; - float die; - ptype_t type; -} particle_t; - - -//==================================================== - - -extern entity_t r_worldentity; -extern qboolean r_cache_thrash; // compatability -extern vec3_t modelorg, r_entorigin; -extern entity_t *currententity; -extern int r_visframecount; -extern int r_framecount; -extern mplane_t frustum[4]; -extern int c_brush_polys, c_alias_polys; - - -// -// view origin -// -extern vec3_t vup; -extern vec3_t vpn; -extern vec3_t vright; -extern vec3_t r_origin; - -// -// screen size info -// -extern refdef_t r_refdef; -extern mleaf_t *r_viewleaf, *r_oldviewleaf; -extern mleaf_t *r_viewleaf2, *r_oldviewleaf2; // for watervis hack -extern texture_t *r_notexture_mip; -extern int d_lightstylevalue[256]; // 8.8 fraction of base light value - -extern qboolean envmap; -extern int currenttexture; -extern int cnttextures[2]; -extern int particletexture; -extern int netgraphtexture; // netgraph texture -extern int playertextures; - -extern int skytexturenum; // index in cl.loadmodel, not gl texture object - -extern cvar_t r_norefresh; -extern cvar_t r_drawentities; -extern cvar_t r_drawworld; -extern cvar_t r_drawviewmodel; -extern cvar_t r_drawflame; -extern cvar_t r_speeds; -extern cvar_t r_fullbright; -extern cvar_t r_lightmap; -extern cvar_t r_shadows; -extern cvar_t r_mirroralpha; -extern cvar_t r_wateralpha; -extern cvar_t r_dynamic; -extern cvar_t r_novis; -extern cvar_t r_netgraph; -extern cvar_t r_watervishack; -extern cvar_t r_fullbrightSkins; - -extern cvar_t gl_clear; -extern cvar_t gl_cull; -extern cvar_t gl_poly; -extern cvar_t gl_texsort; -extern cvar_t gl_smoothmodels; -extern cvar_t gl_affinemodels; -extern cvar_t gl_polyblend; -extern cvar_t gl_keeptjunctions; -extern cvar_t gl_reporttjunctions; -extern cvar_t gl_flashblend; -extern cvar_t gl_nocolors; -extern cvar_t gl_finish; -extern cvar_t gl_fb_depthhack; -extern cvar_t gl_fb_bmodels; -extern cvar_t gl_fb_models; -extern cvar_t gl_contrast; -extern cvar_t gl_gamma; - -extern int gl_lightmap_format; -extern int gl_solid_format; -extern int gl_alpha_format; - -extern cvar_t gl_max_size; -extern cvar_t gl_playermip; - -extern int mirrortexturenum; // quake texturenum, not gltexturenum -extern qboolean mirror; -extern mplane_t *mirror_plane; - -extern float r_world_matrix[16]; - -extern const char *gl_vendor; -extern const char *gl_renderer; -extern const char *gl_version; -extern const char *gl_extensions; - -void R_TranslatePlayerSkin (int playernum); -void GL_Bind (int texnum); - -// Multitexture -#define TEXTURE0_ARB 0x84C0 -#define TEXTURE1_ARB 0x84C1 - -#ifdef _WIN32 -typedef void (APIENTRY *lpMTexFUNC) (GLenum, GLfloat, GLfloat); -typedef void (APIENTRY *lpSelTexFUNC) (GLenum); -extern lpMTexFUNC qglMultiTexCoord2f; -extern lpSelTexFUNC qglActiveTexture; -#endif - -extern qboolean gl_mtexable; - -void GL_DisableMultitexture(void); -void GL_EnableMultitexture(void); - -// -// gl_warp.c -// -void GL_SubdivideSurface (msurface_t *fa); -void EmitBothSkyLayers (msurface_t *fa); -void EmitWaterPolys (msurface_t *fa); -void EmitSkyPolys (msurface_t *fa); -void R_DrawSkyChain (msurface_t *s); - -// -// gl_draw.c -// -void GL_Set2D (void); - -// -// gl_rmain.c -// -qboolean R_CullBox (vec3_t mins, vec3_t maxs); -void R_RotateForEntity (entity_t *e); -void R_PolyBlend (void); -void R_BrightenScreen (void); - -// -// gl_rlight.c -// -void R_MarkLights (dlight_t *light, int bit, mnode_t *node); -void R_AnimateLight (void); -void R_RenderDlights (void); -int R_LightPoint (vec3_t p); - -// -// gl_refrag.c -// -void R_StoreEfrags (efrag_t **ppefrag); - -// -// gl_mesh.c -// -void GL_MakeAliasModelDisplayLists (model_t *m, aliashdr_t *hdr); - -// -// gl_rsurf.c -// -void R_DrawBrushModel (entity_t *e); -void R_DrawWorld (void); -void GL_BuildLightmaps (void); - -// -// gl_ngraph.c -// -void R_NetGraph (void); - +/* +Copyright (C) 1996-1997 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. + +*/ + +// disable data conversion warnings + +#pragma warning(disable : 4244) // MIPS +#pragma warning(disable : 4136) // X86 +#pragma warning(disable : 4051) // ALPHA + +#ifdef _WIN32 +#include +#endif + +#include +#include + +void GL_BeginRendering (int *x, int *y, int *width, int *height); +void GL_EndRendering (void); + + +// Function prototypes for the Texture Object Extension routines +typedef GLboolean (APIENTRY *ARETEXRESFUNCPTR)(GLsizei, const GLuint *, + const GLboolean *); +typedef void (APIENTRY *BINDTEXFUNCPTR)(GLenum, GLuint); +typedef void (APIENTRY *DELTEXFUNCPTR)(GLsizei, const GLuint *); +typedef void (APIENTRY *GENTEXFUNCPTR)(GLsizei, GLuint *); +typedef GLboolean (APIENTRY *ISTEXFUNCPTR)(GLuint); +typedef void (APIENTRY *PRIORTEXFUNCPTR)(GLsizei, const GLuint *, + const GLclampf *); +typedef void (APIENTRY *TEXSUBIMAGEPTR)(int, int, int, int, int, int, int, int, void *); + +extern BINDTEXFUNCPTR bindTexFunc; +extern DELTEXFUNCPTR delTexFunc; +extern TEXSUBIMAGEPTR TexSubImage2DFunc; + +extern int texture_extension_number; +extern int texture_mode; + +extern float gldepthmin, gldepthmax; + +#define MAX_GLTEXTURES 1024 + +void GL_Upload32 (unsigned *data, int width, int height, qboolean mipmap, qboolean alpha); +void GL_Upload8 (byte *data, int width, int height, qboolean mipmap, qboolean alpha, qboolean brighten); +void GL_Upload8_EXT (byte *data, int width, int height, qboolean mipmap, qboolean alpha); +int GL_LoadTexture (char *identifier, int width, int height, byte *data, qboolean mipmap, qboolean alpha, qboolean brighten); +int GL_FindTexture (char *identifier); + +typedef struct +{ + float x, y, z; + float s, t; + float r, g, b; +} glvert_t; + +extern glvert_t glv; + +extern int glx, gly, glwidth, glheight; + +#ifdef _WIN32 +extern PROC glArrayElementEXT; +extern PROC glColorPointerEXT; +extern PROC glTexturePointerEXT; +extern PROC glVertexPointerEXT; +#endif + +// r_local.h -- private refresh defs + +#define ALIAS_BASE_SIZE_RATIO (1.0 / 11.0) + // normalizing factor so player model works out to about + // 1 pixel per triangle +#define MAX_LBM_HEIGHT 480 + +#define TILE_SIZE 128 // size of textures generated by R_GenTiledSurf + +#define SKYSHIFT 7 +#define SKYSIZE (1 << SKYSHIFT) +#define SKYMASK (SKYSIZE - 1) + +#define BACKFACE_EPSILON 0.01 + + +void R_TimeRefresh_f (void); +void R_ReadPointFile_f (void); +texture_t *R_TextureAnimation (texture_t *base); + + +typedef enum { + pt_static, pt_grav, pt_slowgrav, pt_fire, pt_explode, pt_explode2, pt_blob, pt_blob2 +} ptype_t; + +// !!! if this is changed, it must be changed in d_ifacea.h too !!! +typedef struct particle_s +{ +// driver-usable fields + vec3_t org; + float color; +// drivers never touch the following fields + struct particle_s *next; + vec3_t vel; + float ramp; + float die; + ptype_t type; +} particle_t; + + +//==================================================== + + +extern entity_t r_worldentity; +extern qboolean r_cache_thrash; // compatability +extern vec3_t modelorg, r_entorigin; +extern entity_t *currententity; +extern int r_visframecount; +extern int r_framecount; +extern mplane_t frustum[4]; +extern int c_brush_polys, c_alias_polys; + + +// +// view origin +// +extern vec3_t vup; +extern vec3_t vpn; +extern vec3_t vright; +extern vec3_t r_origin; + +// +// screen size info +// +extern refdef_t r_refdef; +extern mleaf_t *r_viewleaf, *r_oldviewleaf; +extern mleaf_t *r_viewleaf2, *r_oldviewleaf2; // for watervis hack +extern texture_t *r_notexture_mip; +extern int d_lightstylevalue[256]; // 8.8 fraction of base light value + +extern qboolean envmap; +extern int currenttexture; +extern int cnttextures[2]; +extern int particletexture; +extern int netgraphtexture; // netgraph texture +extern int playertextures; + +extern int skytexturenum; // index in cl.loadmodel, not gl texture object + +extern cvar_t r_norefresh; +extern cvar_t r_drawentities; +extern cvar_t r_drawworld; +extern cvar_t r_drawviewmodel; +extern cvar_t r_drawflame; +extern cvar_t r_speeds; +extern cvar_t r_fullbright; +extern cvar_t r_lightmap; +extern cvar_t r_shadows; +extern cvar_t r_mirroralpha; +extern cvar_t r_wateralpha; +extern cvar_t r_dynamic; +extern cvar_t r_novis; +extern cvar_t r_netgraph; +extern cvar_t r_watervishack; +extern cvar_t r_fullbrightSkins; + +extern cvar_t gl_clear; +extern cvar_t gl_cull; +extern cvar_t gl_poly; +extern cvar_t gl_texsort; +extern cvar_t gl_smoothmodels; +extern cvar_t gl_affinemodels; +extern cvar_t gl_polyblend; +extern cvar_t gl_keeptjunctions; +extern cvar_t gl_reporttjunctions; +extern cvar_t gl_flashblend; +extern cvar_t gl_nocolors; +extern cvar_t gl_finish; +extern cvar_t gl_fb_depthhack; +extern cvar_t gl_fb_bmodels; +extern cvar_t gl_fb_models; +extern cvar_t gl_contrast; +extern cvar_t gl_gamma; + +extern int gl_lightmap_format; +extern int gl_solid_format; +extern int gl_alpha_format; + +extern cvar_t gl_max_size; +extern cvar_t gl_playermip; + +extern int mirrortexturenum; // quake texturenum, not gltexturenum +extern qboolean mirror; +extern mplane_t *mirror_plane; + +extern float r_world_matrix[16]; + +extern const char *gl_vendor; +extern const char *gl_renderer; +extern const char *gl_version; +extern const char *gl_extensions; + +void R_TranslatePlayerSkin (int playernum); +void GL_Bind (int texnum); + +// Multitexture +#define TEXTURE0_ARB 0x84C0 +#define TEXTURE1_ARB 0x84C1 + +#ifdef _WIN32 +typedef void (APIENTRY *lpMTexFUNC) (GLenum, GLfloat, GLfloat); +typedef void (APIENTRY *lpSelTexFUNC) (GLenum); +extern lpMTexFUNC qglMultiTexCoord2f; +extern lpSelTexFUNC qglActiveTexture; +#endif + +extern qboolean gl_mtexable; + +void GL_DisableMultitexture(void); +void GL_EnableMultitexture(void); + +// +// gl_warp.c +// +void GL_SubdivideSurface (msurface_t *fa); +void EmitBothSkyLayers (msurface_t *fa); +void EmitWaterPolys (msurface_t *fa); +void EmitSkyPolys (msurface_t *fa); +void R_DrawSkyChain (msurface_t *s); + +// +// gl_draw.c +// +void GL_Set2D (void); + +// +// gl_rmain.c +// +qboolean R_CullBox (vec3_t mins, vec3_t maxs); +void R_RotateForEntity (entity_t *e); +void R_PolyBlend (void); +void R_BrightenScreen (void); + +// +// gl_rlight.c +// +void R_MarkLights (dlight_t *light, int bit, mnode_t *node); +void R_AnimateLight (void); +void R_RenderDlights (void); +int R_LightPoint (vec3_t p); + +// +// gl_refrag.c +// +void R_StoreEfrags (efrag_t **ppefrag); + +// +// gl_mesh.c +// +void GL_MakeAliasModelDisplayLists (model_t *m, aliashdr_t *hdr); + +// +// gl_rsurf.c +// +void R_DrawBrushModel (entity_t *e); +void R_DrawWorld (void); +void GL_BuildLightmaps (void); + +// +// gl_ngraph.c +// +void R_NetGraph (void); + diff --git a/source/in_null.c b/source/in_null.c index c03927b6..33b09b7c 100644 --- a/source/in_null.c +++ b/source/in_null.c @@ -1,29 +1,29 @@ -// in_null.c -- for systems without a mouse - -#include "quakedef.h" - -void IN_Init (void) -{ -} - -void IN_Shutdown (void) -{ -} - -void IN_Commands (void) -{ -} - -void IN_Move (usercmd_t *cmd) -{ -} - -/* -=========== -IN_ModeChanged -=========== -*/ -void IN_ModeChanged (void) -{ -} - +// in_null.c -- for systems without a mouse + +#include "quakedef.h" + +void IN_Init (void) +{ +} + +void IN_Shutdown (void) +{ +} + +void IN_Commands (void) +{ +} + +void IN_Move (usercmd_t *cmd) +{ +} + +/* +=========== +IN_ModeChanged +=========== +*/ +void IN_ModeChanged (void) +{ +} + diff --git a/source/in_win.c b/source/in_win.c index 6b496b82..58a56222 100644 --- a/source/in_win.c +++ b/source/in_win.c @@ -1,1315 +1,1315 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// in_win.c -- windows 95 mouse and joystick code -// 02/21/97 JCB Added extended DirectInput code to support external controllers. - -#include -#include "quakedef.h" -#include "winquake.h" -#include "keys.h" - -#define DINPUT_BUFFERSIZE 16 -#define iDirectInputCreate(a,b,c,d) pDirectInputCreate(a,b,c,d) - -HRESULT (WINAPI *pDirectInputCreate)(HINSTANCE hinst, DWORD dwVersion, - LPDIRECTINPUT * lplpDirectInput, LPUNKNOWN punkOuter); - -// mouse variables -cvar_t m_filter = {"m_filter","0"}; - -// compatibility with old Quake -- setting to 0 disables KP_* codes -cvar_t cl_keypad = {"cl_keypad","1"}; - -int mouse_buttons; -int mouse_oldbuttonstate; -POINT current_pos; -int mouse_x, mouse_y, old_mouse_x, old_mouse_y, mx_accum, my_accum; - -static qboolean restore_spi; -static int originalmouseparms[3], newmouseparms[3] = {0, 0, 1}; -qboolean mouseinitialized; -static qboolean mouseparmsvalid, mouseactivatetoggle; -static qboolean mouseshowtoggle = 1; -static qboolean dinput_acquired; -static unsigned int mstate_di; -unsigned int uiWheelMessage; - -qboolean mouseactive; - -// joystick defines and variables -// where should defines be moved? -#define JOY_ABSOLUTE_AXIS 0x00000000 // control like a joystick -#define JOY_RELATIVE_AXIS 0x00000010 // control like a mouse, spinner, trackball -#define JOY_MAX_AXES 6 // X, Y, Z, R, U, V -#define JOY_AXIS_X 0 -#define JOY_AXIS_Y 1 -#define JOY_AXIS_Z 2 -#define JOY_AXIS_R 3 -#define JOY_AXIS_U 4 -#define JOY_AXIS_V 5 - -enum _ControlList -{ - AxisNada = 0, AxisForward, AxisLook, AxisSide, AxisTurn -}; - -DWORD dwAxisFlags[JOY_MAX_AXES] = -{ - JOY_RETURNX, JOY_RETURNY, JOY_RETURNZ, JOY_RETURNR, JOY_RETURNU, JOY_RETURNV -}; - -DWORD dwAxisMap[JOY_MAX_AXES]; -DWORD dwControlMap[JOY_MAX_AXES]; -PDWORD pdwRawValue[JOY_MAX_AXES]; - -// none of these cvars are saved over a session -// this means that advanced controller configuration needs to be executed -// each time. this avoids any problems with getting back to a default usage -// or when changing from one controller to another. this way at least something -// works. -cvar_t in_joystick = {"joystick","0",CVAR_ARCHIVE}; -cvar_t joy_name = {"joyname", "joystick"}; -cvar_t joy_advanced = {"joyadvanced", "0"}; -cvar_t joy_advaxisx = {"joyadvaxisx", "0"}; -cvar_t joy_advaxisy = {"joyadvaxisy", "0"}; -cvar_t joy_advaxisz = {"joyadvaxisz", "0"}; -cvar_t joy_advaxisr = {"joyadvaxisr", "0"}; -cvar_t joy_advaxisu = {"joyadvaxisu", "0"}; -cvar_t joy_advaxisv = {"joyadvaxisv", "0"}; -cvar_t joy_forwardthreshold = {"joyforwardthreshold", "0.15"}; -cvar_t joy_sidethreshold = {"joysidethreshold", "0.15"}; -cvar_t joy_pitchthreshold = {"joypitchthreshold", "0.15"}; -cvar_t joy_yawthreshold = {"joyyawthreshold", "0.15"}; -cvar_t joy_forwardsensitivity = {"joyforwardsensitivity", "-1.0"}; -cvar_t joy_sidesensitivity = {"joysidesensitivity", "-1.0"}; -cvar_t joy_pitchsensitivity = {"joypitchsensitivity", "1.0"}; -cvar_t joy_yawsensitivity = {"joyyawsensitivity", "-1.0"}; -cvar_t joy_wwhack1 = {"joywwhack1", "0.0"}; -cvar_t joy_wwhack2 = {"joywwhack2", "0.0"}; - -qboolean joy_avail, joy_advancedinit, joy_haspov; -DWORD joy_oldbuttonstate, joy_oldpovstate; - -int joy_id; -DWORD joy_flags; -DWORD joy_numbuttons; - -static LPDIRECTINPUT g_pdi; -static LPDIRECTINPUTDEVICE g_pMouse; - -static JOYINFOEX ji; - -static HINSTANCE hInstDI; - -static qboolean dinput; - -typedef struct MYDATA { - LONG lX; // X axis goes here - LONG lY; // Y axis goes here - LONG lZ; // Z axis goes here - BYTE bButtonA; // One button goes here - BYTE bButtonB; // Another button goes here - BYTE bButtonC; // Another button goes here - BYTE bButtonD; // Another button goes here -} MYDATA; - -static DIOBJECTDATAFORMAT rgodf[] = { - { &GUID_XAxis, FIELD_OFFSET(MYDATA, lX), DIDFT_AXIS | DIDFT_ANYINSTANCE, 0,}, - { &GUID_YAxis, FIELD_OFFSET(MYDATA, lY), DIDFT_AXIS | DIDFT_ANYINSTANCE, 0,}, - { &GUID_ZAxis, FIELD_OFFSET(MYDATA, lZ), 0x80000000 | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0,}, - { 0, FIELD_OFFSET(MYDATA, bButtonA), DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,}, - { 0, FIELD_OFFSET(MYDATA, bButtonB), DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,}, - { 0, FIELD_OFFSET(MYDATA, bButtonC), 0x80000000 | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,}, - { 0, FIELD_OFFSET(MYDATA, bButtonD), 0x80000000 | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,}, -}; - -#define NUM_OBJECTS (sizeof(rgodf) / sizeof(rgodf[0])) - -static DIDATAFORMAT df = { - sizeof(DIDATAFORMAT), // this structure - sizeof(DIOBJECTDATAFORMAT), // size of object data format - DIDF_RELAXIS, // absolute axis coordinates - sizeof(MYDATA), // device data size - NUM_OBJECTS, // number of objects - rgodf, // and here they are -}; - -// forward-referenced functions -void IN_StartupJoystick (void); -void Joy_AdvancedUpdate_f (void); -void IN_JoyMove (usercmd_t *cmd); - - -/* -=========== -Force_CenterView_f -=========== -*/ -void Force_CenterView_f (void) -{ - cl.viewangles[PITCH] = 0; -} - - -/* -=========== -IN_UpdateClipCursor -=========== -*/ -void IN_UpdateClipCursor (void) -{ - - if (mouseinitialized && mouseactive && !dinput) - { - ClipCursor (&window_rect); - } -} - - -/* -=========== -IN_ShowMouse -=========== -*/ -void IN_ShowMouse (void) -{ - - if (!mouseshowtoggle) - { - ShowCursor (TRUE); - mouseshowtoggle = 1; - } -} - - -/* -=========== -IN_HideMouse -=========== -*/ -void IN_HideMouse (void) -{ - - if (mouseshowtoggle) - { - ShowCursor (FALSE); - mouseshowtoggle = 0; - } -} - - -/* -=========== -IN_ActivateMouse -=========== -*/ -void IN_ActivateMouse (void) -{ - - mouseactivatetoggle = true; - - if (mouseinitialized) - { - if (dinput) - { - if (g_pMouse) - { - if (!dinput_acquired) - { - IDirectInputDevice_Acquire(g_pMouse); - dinput_acquired = true; - } - } - else - { - return; - } - } - else - { - if (mouseparmsvalid) - restore_spi = SystemParametersInfo (SPI_SETMOUSE, 0, newmouseparms, 0); - - SetCursorPos (window_center_x, window_center_y); - SetCapture (mainwindow); - ClipCursor (&window_rect); - } - - mouseactive = true; - } -} - - -/* -=========== -IN_SetQuakeMouseState -=========== -*/ -void IN_SetQuakeMouseState (void) -{ - if (mouseactivatetoggle) - IN_ActivateMouse (); -} - - -/* -=========== -IN_DeactivateMouse -=========== -*/ -void IN_DeactivateMouse (void) -{ - - mouseactivatetoggle = false; - - if (mouseinitialized) - { - if (dinput) - { - if (g_pMouse) - { - if (dinput_acquired) - { - IDirectInputDevice_Unacquire(g_pMouse); - dinput_acquired = false; - } - } - } - else - { - if (restore_spi) - SystemParametersInfo (SPI_SETMOUSE, 0, originalmouseparms, 0); - - ClipCursor (NULL); - ReleaseCapture (); - } - - mouseactive = false; - } -} - - -/* -=========== -IN_RestoreOriginalMouseState -=========== -*/ -void IN_RestoreOriginalMouseState (void) -{ - if (mouseactivatetoggle) - { - IN_DeactivateMouse (); - mouseactivatetoggle = true; - } - -// try to redraw the cursor so it gets reinitialized, because sometimes it -// has garbage after the mode switch - ShowCursor (TRUE); - ShowCursor (FALSE); -} - - -/* -=========== -IN_InitDInput -=========== -*/ -qboolean IN_InitDInput (void) -{ - HRESULT hr; - DIPROPDWORD dipdw = { - { - sizeof(DIPROPDWORD), // diph.dwSize - sizeof(DIPROPHEADER), // diph.dwHeaderSize - 0, // diph.dwObj - DIPH_DEVICE, // diph.dwHow - }, - DINPUT_BUFFERSIZE, // dwData - }; - - if (!hInstDI) - { - hInstDI = LoadLibrary("dinput.dll"); - - if (hInstDI == NULL) - { - Con_SafePrintf ("Couldn't load dinput.dll\n"); - return false; - } - } - - if (!pDirectInputCreate) - { - pDirectInputCreate = (void *)GetProcAddress(hInstDI,"DirectInputCreateA"); - - if (!pDirectInputCreate) - { - Con_SafePrintf ("Couldn't get DI proc addr\n"); - return false; - } - } - -// register with DirectInput and get an IDirectInput to play with. - hr = iDirectInputCreate(global_hInstance, DIRECTINPUT_VERSION, &g_pdi, NULL); - - if (FAILED(hr)) - { - return false; - } - -// obtain an interface to the system mouse device. - hr = IDirectInput_CreateDevice(g_pdi, &GUID_SysMouse, &g_pMouse, NULL); - - if (FAILED(hr)) - { - Con_SafePrintf ("Couldn't open DI mouse device\n"); - return false; - } - -// set the data format to "mouse format". - hr = IDirectInputDevice_SetDataFormat(g_pMouse, &df); - - if (FAILED(hr)) - { - Con_SafePrintf ("Couldn't set DI mouse format\n"); - return false; - } - -// set the cooperativity level. - hr = IDirectInputDevice_SetCooperativeLevel(g_pMouse, mainwindow, - DISCL_EXCLUSIVE | DISCL_FOREGROUND); - - if (FAILED(hr)) - { - Con_SafePrintf ("Couldn't set DI coop level\n"); - return false; - } - - -// set the buffer size to DINPUT_BUFFERSIZE elements. -// the buffer size is a DWORD property associated with the device - hr = IDirectInputDevice_SetProperty(g_pMouse, DIPROP_BUFFERSIZE, &dipdw.diph); - - if (FAILED(hr)) - { - Con_SafePrintf ("Couldn't set DI buffersize\n"); - return false; - } - - return true; -} - - -/* -=========== -IN_StartupMouse -=========== -*/ -void IN_StartupMouse (void) -{ -// HDC hdc; - - if ( COM_CheckParm ("-nomouse") ) - return; - - mouseinitialized = true; - - if (COM_CheckParm ("-dinput")) - { - dinput = IN_InitDInput (); - - if (dinput) - { - Con_SafePrintf ("DirectInput initialized\n"); - } - else - { - Con_SafePrintf ("DirectInput not initialized\n"); - } - } - - if (!dinput) - { - mouseparmsvalid = SystemParametersInfo (SPI_GETMOUSE, 0, originalmouseparms, 0); - - if (mouseparmsvalid) - { - if ( COM_CheckParm ("-noforcemspd") ) - newmouseparms[2] = originalmouseparms[2]; - - if ( COM_CheckParm ("-noforcemaccel") ) - { - newmouseparms[0] = originalmouseparms[0]; - newmouseparms[1] = originalmouseparms[1]; - } - - if ( COM_CheckParm ("-noforcemparms") ) - { - newmouseparms[0] = originalmouseparms[0]; - newmouseparms[1] = originalmouseparms[1]; - newmouseparms[2] = originalmouseparms[2]; - } - } - } - - mouse_buttons = 3; - -// if a fullscreen video mode was set before the mouse was initialized, -// set the mouse state appropriately - if (mouseactivatetoggle) - IN_ActivateMouse (); -} - - -/* -=========== -IN_Init -=========== -*/ -void IN_Init (void) -{ - // mouse variables - Cvar_RegisterVariable (&m_filter); - - // keyboard variables - Cvar_RegisterVariable (&cl_keypad); - - // joystick variables - Cvar_RegisterVariable (&in_joystick); - Cvar_RegisterVariable (&joy_name); - Cvar_RegisterVariable (&joy_advanced); - Cvar_RegisterVariable (&joy_advaxisx); - Cvar_RegisterVariable (&joy_advaxisy); - Cvar_RegisterVariable (&joy_advaxisz); - Cvar_RegisterVariable (&joy_advaxisr); - Cvar_RegisterVariable (&joy_advaxisu); - Cvar_RegisterVariable (&joy_advaxisv); - Cvar_RegisterVariable (&joy_forwardthreshold); - Cvar_RegisterVariable (&joy_sidethreshold); - Cvar_RegisterVariable (&joy_pitchthreshold); - Cvar_RegisterVariable (&joy_yawthreshold); - Cvar_RegisterVariable (&joy_forwardsensitivity); - Cvar_RegisterVariable (&joy_sidesensitivity); - Cvar_RegisterVariable (&joy_pitchsensitivity); - Cvar_RegisterVariable (&joy_yawsensitivity); - Cvar_RegisterVariable (&joy_wwhack1); - Cvar_RegisterVariable (&joy_wwhack2); - - Cmd_AddCommand ("force_centerview", Force_CenterView_f); - Cmd_AddCommand ("joyadvancedupdate", Joy_AdvancedUpdate_f); - - uiWheelMessage = RegisterWindowMessage ( "MSWHEEL_ROLLMSG" ); - - IN_StartupMouse (); - IN_StartupJoystick (); -} - -/* -=========== -IN_Shutdown -=========== -*/ -void IN_Shutdown (void) -{ - - IN_DeactivateMouse (); - IN_ShowMouse (); - - if (g_pMouse) - { - IDirectInputDevice_Release(g_pMouse); - g_pMouse = NULL; - } - - if (g_pdi) - { - IDirectInput_Release(g_pdi); - g_pdi = NULL; - } -} - - -/* -=========== -IN_MouseEvent -=========== -*/ -void IN_MouseEvent (int mstate) -{ - int i; - - if (mouseactive && !dinput) - { - // perform button actions - for (i=0 ; isidemove += m_side.value * mouse_x; - else - cl.viewangles[YAW] -= m_yaw.value * mouse_x; - - if (in_mlook.state & 1) - V_StopPitchDrift (); - - if ( (in_mlook.state & 1) && !(in_strafe.state & 1)) - { - cl.viewangles[PITCH] += m_pitch.value * mouse_y; - if (cl.viewangles[PITCH] > 80) - cl.viewangles[PITCH] = 80; - if (cl.viewangles[PITCH] < -70) - cl.viewangles[PITCH] = -70; - } - else - { - cmd->forwardmove -= m_forward.value * mouse_y; - } - -// if the mouse has moved, force it to the center, so there's room to move - if (mx || my) - { - SetCursorPos (window_center_x, window_center_y); - } -} - - -/* -=========== -IN_Move -=========== -*/ -void IN_Move (usercmd_t *cmd) -{ - - if (ActiveApp && !Minimized) - { - IN_MouseMove (cmd); - IN_JoyMove (cmd); - } -} - - -/* -=========== -IN_Accumulate -=========== -*/ -void IN_Accumulate (void) -{ -// int mx, my; -// HDC hdc; - - if (mouseactive) - { - GetCursorPos (¤t_pos); - - mx_accum += current_pos.x - window_center_x; - my_accum += current_pos.y - window_center_y; - - // force the mouse to the center, so there's room to move - SetCursorPos (window_center_x, window_center_y); - } -} - - -/* -=================== -IN_ClearStates -=================== -*/ -void IN_ClearStates (void) -{ - - if (mouseactive) - { - mx_accum = 0; - my_accum = 0; - mouse_oldbuttonstate = 0; - } -} - - -/* -=============== -IN_StartupJoystick -=============== -*/ -void IN_StartupJoystick (void) -{ - int /*i,*/ numdevs; - JOYCAPS jc; - MMRESULT mmr; - - // assume no joystick - joy_avail = false; - - // abort startup if user requests no joystick - if ( COM_CheckParm ("-nojoy") ) - return; - - // verify joystick driver is present - if ((numdevs = joyGetNumDevs ()) == 0) - { - Con_Printf ("\njoystick not found -- driver not present\n\n"); - return; - } - - // cycle through the joystick ids for the first valid one - for (joy_id=0 ; joy_id 14000.0) - fTemp = 14000.0; - // restore direction information - fAxisValue = (fAxisValue > 0.0) ? fTemp : -fTemp; - } - } - - // convert range from -32768..32767 to -1..1 - fAxisValue /= 32768.0; - - switch (dwAxisMap[i]) - { - case AxisForward: - if ((joy_advanced.value == 0.0) && (in_mlook.state & 1)) - { - // user wants forward control to become look control - if (fabs(fAxisValue) > joy_pitchthreshold.value) - { - // if mouse invert is on, invert the joystick pitch value - // only absolute control support here (joy_advanced is false) - if (m_pitch.value < 0.0) - { - cl.viewangles[PITCH] -= (fAxisValue * joy_pitchsensitivity.value) * aspeed * cl_pitchspeed.value; - } - else - { - cl.viewangles[PITCH] += (fAxisValue * joy_pitchsensitivity.value) * aspeed * cl_pitchspeed.value; - } - V_StopPitchDrift(); - } - else - { - // no pitch movement - // disable pitch return-to-center unless requested by user - // *** this code can be removed when the lookspring bug is fixed - // *** the bug always has the lookspring feature on - if(lookspring.value == 0.0) - V_StopPitchDrift(); - } - } - else - { - // user wants forward control to be forward control - if (fabs(fAxisValue) > joy_forwardthreshold.value) - { - cmd->forwardmove += (fAxisValue * joy_forwardsensitivity.value) * speed * cl_forwardspeed.value; - } - } - break; - - case AxisSide: - if (fabs(fAxisValue) > joy_sidethreshold.value) - { - cmd->sidemove += (fAxisValue * joy_sidesensitivity.value) * speed * cl_sidespeed.value; - } - break; - - case AxisTurn: - if ((in_strafe.state & 1) || (lookstrafe.value && (in_mlook.state & 1))) - { - // user wants turn control to become side control - if (fabs(fAxisValue) > joy_sidethreshold.value) - { - cmd->sidemove -= (fAxisValue * joy_sidesensitivity.value) * speed * cl_sidespeed.value; - } - } - else - { - // user wants turn control to be turn control - if (fabs(fAxisValue) > joy_yawthreshold.value) - { - if(dwControlMap[i] == JOY_ABSOLUTE_AXIS) - { - cl.viewangles[YAW] += (fAxisValue * joy_yawsensitivity.value) * aspeed * cl_yawspeed.value; - } - else - { - cl.viewangles[YAW] += (fAxisValue * joy_yawsensitivity.value) * speed * 180.0; - } - - } - } - break; - - case AxisLook: - if (in_mlook.state & 1) - { - if (fabs(fAxisValue) > joy_pitchthreshold.value) - { - // pitch movement detected and pitch movement desired by user - if(dwControlMap[i] == JOY_ABSOLUTE_AXIS) - { - cl.viewangles[PITCH] += (fAxisValue * joy_pitchsensitivity.value) * aspeed * cl_pitchspeed.value; - } - else - { - cl.viewangles[PITCH] += (fAxisValue * joy_pitchsensitivity.value) * speed * 180.0; - } - V_StopPitchDrift(); - } - else - { - // no pitch movement - // disable pitch return-to-center unless requested by user - // *** this code can be removed when the lookspring bug is fixed - // *** the bug always has the lookspring feature on - if(lookspring.value == 0.0) - V_StopPitchDrift(); - } - } - break; - - default: - break; - } - } - - // bounds check pitch - if (cl.viewangles[PITCH] > 80.0) - cl.viewangles[PITCH] = 80.0; - if (cl.viewangles[PITCH] < -70.0) - cl.viewangles[PITCH] = -70.0; -} - -//========================================================================== - -static byte scantokey[128] = -{ -// 0 1 2 3 4 5 6 7 -// 8 9 A B C D E F - 0 , K_ESCAPE,'1', '2', '3', '4', '5', '6', - '7', '8', '9', '0', '-', '=', K_BACKSPACE, 9, // 0 - 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', - 'o', 'p', '[', ']', K_ENTER,K_CTRL, 'a', 's', // 1 - 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', - '\'', '`', K_SHIFT,'\\', 'z', 'x', 'c', 'v', // 2 - 'b', 'n', 'm', ',', '.', '/', K_SHIFT,KP_STAR, - K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3 - K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE,K_SCRLCK,K_HOME, - K_UPARROW,K_PGUP,KP_MINUS,K_LEFTARROW,KP_5,K_RIGHTARROW,KP_PLUS,K_END, // 4 - K_DOWNARROW,K_PGDN,K_INS,K_DEL, 0, 0, 0, K_F11, - K_F12, 0, 0, 0, 0, 0, 0, 0, // 5 - 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 -}; - - -/* -======= -IN_MapKey - -Map from windows to quake keynums -======= -*/ -int IN_MapKey (int key) -{ - int extended; - extern cvar_t cl_keypad; - - extended = (key >> 24) & 1; - - key = (key>>16)&255; - if (key > 127) - return 0; - - key = scantokey[key]; - - if (cl_keypad.value) { - if (extended) { - switch (key) { - case K_ENTER: return KP_ENTER; - case '/': return KP_SLASH; - case K_PAUSE: return KP_NUMLOCK; - }; - } else { - switch (key) { - case K_HOME: return KP_HOME; - case K_UPARROW: return KP_UPARROW; - case K_PGUP: return KP_PGUP; - case K_LEFTARROW: return KP_LEFTARROW; - case K_RIGHTARROW: return KP_RIGHTARROW; - case K_END: return KP_END; - case K_DOWNARROW: return KP_DOWNARROW; - case K_PGDN: return KP_PGDN; - case K_INS: return KP_INS; - case K_DEL: return KP_DEL; - } - } - } else { - // cl_keypad 0, compatibility mode - switch (key) { - case KP_STAR: return '*'; - case KP_MINUS: return '-'; - case KP_5: return '5'; - case KP_PLUS: return '+'; - } - } - - return key; -} +/* +Copyright (C) 1996-1997 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. + +*/ +// in_win.c -- windows 95 mouse and joystick code +// 02/21/97 JCB Added extended DirectInput code to support external controllers. + +#include +#include "quakedef.h" +#include "winquake.h" +#include "keys.h" + +#define DINPUT_BUFFERSIZE 16 +#define iDirectInputCreate(a,b,c,d) pDirectInputCreate(a,b,c,d) + +HRESULT (WINAPI *pDirectInputCreate)(HINSTANCE hinst, DWORD dwVersion, + LPDIRECTINPUT * lplpDirectInput, LPUNKNOWN punkOuter); + +// mouse variables +cvar_t m_filter = {"m_filter","0"}; + +// compatibility with old Quake -- setting to 0 disables KP_* codes +cvar_t cl_keypad = {"cl_keypad","1"}; + +int mouse_buttons; +int mouse_oldbuttonstate; +POINT current_pos; +int mouse_x, mouse_y, old_mouse_x, old_mouse_y, mx_accum, my_accum; + +static qboolean restore_spi; +static int originalmouseparms[3], newmouseparms[3] = {0, 0, 1}; +qboolean mouseinitialized; +static qboolean mouseparmsvalid, mouseactivatetoggle; +static qboolean mouseshowtoggle = 1; +static qboolean dinput_acquired; +static unsigned int mstate_di; +unsigned int uiWheelMessage; + +qboolean mouseactive; + +// joystick defines and variables +// where should defines be moved? +#define JOY_ABSOLUTE_AXIS 0x00000000 // control like a joystick +#define JOY_RELATIVE_AXIS 0x00000010 // control like a mouse, spinner, trackball +#define JOY_MAX_AXES 6 // X, Y, Z, R, U, V +#define JOY_AXIS_X 0 +#define JOY_AXIS_Y 1 +#define JOY_AXIS_Z 2 +#define JOY_AXIS_R 3 +#define JOY_AXIS_U 4 +#define JOY_AXIS_V 5 + +enum _ControlList +{ + AxisNada = 0, AxisForward, AxisLook, AxisSide, AxisTurn +}; + +DWORD dwAxisFlags[JOY_MAX_AXES] = +{ + JOY_RETURNX, JOY_RETURNY, JOY_RETURNZ, JOY_RETURNR, JOY_RETURNU, JOY_RETURNV +}; + +DWORD dwAxisMap[JOY_MAX_AXES]; +DWORD dwControlMap[JOY_MAX_AXES]; +PDWORD pdwRawValue[JOY_MAX_AXES]; + +// none of these cvars are saved over a session +// this means that advanced controller configuration needs to be executed +// each time. this avoids any problems with getting back to a default usage +// or when changing from one controller to another. this way at least something +// works. +cvar_t in_joystick = {"joystick","0",CVAR_ARCHIVE}; +cvar_t joy_name = {"joyname", "joystick"}; +cvar_t joy_advanced = {"joyadvanced", "0"}; +cvar_t joy_advaxisx = {"joyadvaxisx", "0"}; +cvar_t joy_advaxisy = {"joyadvaxisy", "0"}; +cvar_t joy_advaxisz = {"joyadvaxisz", "0"}; +cvar_t joy_advaxisr = {"joyadvaxisr", "0"}; +cvar_t joy_advaxisu = {"joyadvaxisu", "0"}; +cvar_t joy_advaxisv = {"joyadvaxisv", "0"}; +cvar_t joy_forwardthreshold = {"joyforwardthreshold", "0.15"}; +cvar_t joy_sidethreshold = {"joysidethreshold", "0.15"}; +cvar_t joy_pitchthreshold = {"joypitchthreshold", "0.15"}; +cvar_t joy_yawthreshold = {"joyyawthreshold", "0.15"}; +cvar_t joy_forwardsensitivity = {"joyforwardsensitivity", "-1.0"}; +cvar_t joy_sidesensitivity = {"joysidesensitivity", "-1.0"}; +cvar_t joy_pitchsensitivity = {"joypitchsensitivity", "1.0"}; +cvar_t joy_yawsensitivity = {"joyyawsensitivity", "-1.0"}; +cvar_t joy_wwhack1 = {"joywwhack1", "0.0"}; +cvar_t joy_wwhack2 = {"joywwhack2", "0.0"}; + +qboolean joy_avail, joy_advancedinit, joy_haspov; +DWORD joy_oldbuttonstate, joy_oldpovstate; + +int joy_id; +DWORD joy_flags; +DWORD joy_numbuttons; + +static LPDIRECTINPUT g_pdi; +static LPDIRECTINPUTDEVICE g_pMouse; + +static JOYINFOEX ji; + +static HINSTANCE hInstDI; + +static qboolean dinput; + +typedef struct MYDATA { + LONG lX; // X axis goes here + LONG lY; // Y axis goes here + LONG lZ; // Z axis goes here + BYTE bButtonA; // One button goes here + BYTE bButtonB; // Another button goes here + BYTE bButtonC; // Another button goes here + BYTE bButtonD; // Another button goes here +} MYDATA; + +static DIOBJECTDATAFORMAT rgodf[] = { + { &GUID_XAxis, FIELD_OFFSET(MYDATA, lX), DIDFT_AXIS | DIDFT_ANYINSTANCE, 0,}, + { &GUID_YAxis, FIELD_OFFSET(MYDATA, lY), DIDFT_AXIS | DIDFT_ANYINSTANCE, 0,}, + { &GUID_ZAxis, FIELD_OFFSET(MYDATA, lZ), 0x80000000 | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0,}, + { 0, FIELD_OFFSET(MYDATA, bButtonA), DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,}, + { 0, FIELD_OFFSET(MYDATA, bButtonB), DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,}, + { 0, FIELD_OFFSET(MYDATA, bButtonC), 0x80000000 | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,}, + { 0, FIELD_OFFSET(MYDATA, bButtonD), 0x80000000 | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,}, +}; + +#define NUM_OBJECTS (sizeof(rgodf) / sizeof(rgodf[0])) + +static DIDATAFORMAT df = { + sizeof(DIDATAFORMAT), // this structure + sizeof(DIOBJECTDATAFORMAT), // size of object data format + DIDF_RELAXIS, // absolute axis coordinates + sizeof(MYDATA), // device data size + NUM_OBJECTS, // number of objects + rgodf, // and here they are +}; + +// forward-referenced functions +void IN_StartupJoystick (void); +void Joy_AdvancedUpdate_f (void); +void IN_JoyMove (usercmd_t *cmd); + + +/* +=========== +Force_CenterView_f +=========== +*/ +void Force_CenterView_f (void) +{ + cl.viewangles[PITCH] = 0; +} + + +/* +=========== +IN_UpdateClipCursor +=========== +*/ +void IN_UpdateClipCursor (void) +{ + + if (mouseinitialized && mouseactive && !dinput) + { + ClipCursor (&window_rect); + } +} + + +/* +=========== +IN_ShowMouse +=========== +*/ +void IN_ShowMouse (void) +{ + + if (!mouseshowtoggle) + { + ShowCursor (TRUE); + mouseshowtoggle = 1; + } +} + + +/* +=========== +IN_HideMouse +=========== +*/ +void IN_HideMouse (void) +{ + + if (mouseshowtoggle) + { + ShowCursor (FALSE); + mouseshowtoggle = 0; + } +} + + +/* +=========== +IN_ActivateMouse +=========== +*/ +void IN_ActivateMouse (void) +{ + + mouseactivatetoggle = true; + + if (mouseinitialized) + { + if (dinput) + { + if (g_pMouse) + { + if (!dinput_acquired) + { + IDirectInputDevice_Acquire(g_pMouse); + dinput_acquired = true; + } + } + else + { + return; + } + } + else + { + if (mouseparmsvalid) + restore_spi = SystemParametersInfo (SPI_SETMOUSE, 0, newmouseparms, 0); + + SetCursorPos (window_center_x, window_center_y); + SetCapture (mainwindow); + ClipCursor (&window_rect); + } + + mouseactive = true; + } +} + + +/* +=========== +IN_SetQuakeMouseState +=========== +*/ +void IN_SetQuakeMouseState (void) +{ + if (mouseactivatetoggle) + IN_ActivateMouse (); +} + + +/* +=========== +IN_DeactivateMouse +=========== +*/ +void IN_DeactivateMouse (void) +{ + + mouseactivatetoggle = false; + + if (mouseinitialized) + { + if (dinput) + { + if (g_pMouse) + { + if (dinput_acquired) + { + IDirectInputDevice_Unacquire(g_pMouse); + dinput_acquired = false; + } + } + } + else + { + if (restore_spi) + SystemParametersInfo (SPI_SETMOUSE, 0, originalmouseparms, 0); + + ClipCursor (NULL); + ReleaseCapture (); + } + + mouseactive = false; + } +} + + +/* +=========== +IN_RestoreOriginalMouseState +=========== +*/ +void IN_RestoreOriginalMouseState (void) +{ + if (mouseactivatetoggle) + { + IN_DeactivateMouse (); + mouseactivatetoggle = true; + } + +// try to redraw the cursor so it gets reinitialized, because sometimes it +// has garbage after the mode switch + ShowCursor (TRUE); + ShowCursor (FALSE); +} + + +/* +=========== +IN_InitDInput +=========== +*/ +qboolean IN_InitDInput (void) +{ + HRESULT hr; + DIPROPDWORD dipdw = { + { + sizeof(DIPROPDWORD), // diph.dwSize + sizeof(DIPROPHEADER), // diph.dwHeaderSize + 0, // diph.dwObj + DIPH_DEVICE, // diph.dwHow + }, + DINPUT_BUFFERSIZE, // dwData + }; + + if (!hInstDI) + { + hInstDI = LoadLibrary("dinput.dll"); + + if (hInstDI == NULL) + { + Con_SafePrintf ("Couldn't load dinput.dll\n"); + return false; + } + } + + if (!pDirectInputCreate) + { + pDirectInputCreate = (void *)GetProcAddress(hInstDI,"DirectInputCreateA"); + + if (!pDirectInputCreate) + { + Con_SafePrintf ("Couldn't get DI proc addr\n"); + return false; + } + } + +// register with DirectInput and get an IDirectInput to play with. + hr = iDirectInputCreate(global_hInstance, DIRECTINPUT_VERSION, &g_pdi, NULL); + + if (FAILED(hr)) + { + return false; + } + +// obtain an interface to the system mouse device. + hr = IDirectInput_CreateDevice(g_pdi, &GUID_SysMouse, &g_pMouse, NULL); + + if (FAILED(hr)) + { + Con_SafePrintf ("Couldn't open DI mouse device\n"); + return false; + } + +// set the data format to "mouse format". + hr = IDirectInputDevice_SetDataFormat(g_pMouse, &df); + + if (FAILED(hr)) + { + Con_SafePrintf ("Couldn't set DI mouse format\n"); + return false; + } + +// set the cooperativity level. + hr = IDirectInputDevice_SetCooperativeLevel(g_pMouse, mainwindow, + DISCL_EXCLUSIVE | DISCL_FOREGROUND); + + if (FAILED(hr)) + { + Con_SafePrintf ("Couldn't set DI coop level\n"); + return false; + } + + +// set the buffer size to DINPUT_BUFFERSIZE elements. +// the buffer size is a DWORD property associated with the device + hr = IDirectInputDevice_SetProperty(g_pMouse, DIPROP_BUFFERSIZE, &dipdw.diph); + + if (FAILED(hr)) + { + Con_SafePrintf ("Couldn't set DI buffersize\n"); + return false; + } + + return true; +} + + +/* +=========== +IN_StartupMouse +=========== +*/ +void IN_StartupMouse (void) +{ +// HDC hdc; + + if ( COM_CheckParm ("-nomouse") ) + return; + + mouseinitialized = true; + + if (COM_CheckParm ("-dinput")) + { + dinput = IN_InitDInput (); + + if (dinput) + { + Con_SafePrintf ("DirectInput initialized\n"); + } + else + { + Con_SafePrintf ("DirectInput not initialized\n"); + } + } + + if (!dinput) + { + mouseparmsvalid = SystemParametersInfo (SPI_GETMOUSE, 0, originalmouseparms, 0); + + if (mouseparmsvalid) + { + if ( COM_CheckParm ("-noforcemspd") ) + newmouseparms[2] = originalmouseparms[2]; + + if ( COM_CheckParm ("-noforcemaccel") ) + { + newmouseparms[0] = originalmouseparms[0]; + newmouseparms[1] = originalmouseparms[1]; + } + + if ( COM_CheckParm ("-noforcemparms") ) + { + newmouseparms[0] = originalmouseparms[0]; + newmouseparms[1] = originalmouseparms[1]; + newmouseparms[2] = originalmouseparms[2]; + } + } + } + + mouse_buttons = 3; + +// if a fullscreen video mode was set before the mouse was initialized, +// set the mouse state appropriately + if (mouseactivatetoggle) + IN_ActivateMouse (); +} + + +/* +=========== +IN_Init +=========== +*/ +void IN_Init (void) +{ + // mouse variables + Cvar_RegisterVariable (&m_filter); + + // keyboard variables + Cvar_RegisterVariable (&cl_keypad); + + // joystick variables + Cvar_RegisterVariable (&in_joystick); + Cvar_RegisterVariable (&joy_name); + Cvar_RegisterVariable (&joy_advanced); + Cvar_RegisterVariable (&joy_advaxisx); + Cvar_RegisterVariable (&joy_advaxisy); + Cvar_RegisterVariable (&joy_advaxisz); + Cvar_RegisterVariable (&joy_advaxisr); + Cvar_RegisterVariable (&joy_advaxisu); + Cvar_RegisterVariable (&joy_advaxisv); + Cvar_RegisterVariable (&joy_forwardthreshold); + Cvar_RegisterVariable (&joy_sidethreshold); + Cvar_RegisterVariable (&joy_pitchthreshold); + Cvar_RegisterVariable (&joy_yawthreshold); + Cvar_RegisterVariable (&joy_forwardsensitivity); + Cvar_RegisterVariable (&joy_sidesensitivity); + Cvar_RegisterVariable (&joy_pitchsensitivity); + Cvar_RegisterVariable (&joy_yawsensitivity); + Cvar_RegisterVariable (&joy_wwhack1); + Cvar_RegisterVariable (&joy_wwhack2); + + Cmd_AddCommand ("force_centerview", Force_CenterView_f); + Cmd_AddCommand ("joyadvancedupdate", Joy_AdvancedUpdate_f); + + uiWheelMessage = RegisterWindowMessage ( "MSWHEEL_ROLLMSG" ); + + IN_StartupMouse (); + IN_StartupJoystick (); +} + +/* +=========== +IN_Shutdown +=========== +*/ +void IN_Shutdown (void) +{ + + IN_DeactivateMouse (); + IN_ShowMouse (); + + if (g_pMouse) + { + IDirectInputDevice_Release(g_pMouse); + g_pMouse = NULL; + } + + if (g_pdi) + { + IDirectInput_Release(g_pdi); + g_pdi = NULL; + } +} + + +/* +=========== +IN_MouseEvent +=========== +*/ +void IN_MouseEvent (int mstate) +{ + int i; + + if (mouseactive && !dinput) + { + // perform button actions + for (i=0 ; isidemove += m_side.value * mouse_x; + else + cl.viewangles[YAW] -= m_yaw.value * mouse_x; + + if (in_mlook.state & 1) + V_StopPitchDrift (); + + if ( (in_mlook.state & 1) && !(in_strafe.state & 1)) + { + cl.viewangles[PITCH] += m_pitch.value * mouse_y; + if (cl.viewangles[PITCH] > 80) + cl.viewangles[PITCH] = 80; + if (cl.viewangles[PITCH] < -70) + cl.viewangles[PITCH] = -70; + } + else + { + cmd->forwardmove -= m_forward.value * mouse_y; + } + +// if the mouse has moved, force it to the center, so there's room to move + if (mx || my) + { + SetCursorPos (window_center_x, window_center_y); + } +} + + +/* +=========== +IN_Move +=========== +*/ +void IN_Move (usercmd_t *cmd) +{ + + if (ActiveApp && !Minimized) + { + IN_MouseMove (cmd); + IN_JoyMove (cmd); + } +} + + +/* +=========== +IN_Accumulate +=========== +*/ +void IN_Accumulate (void) +{ +// int mx, my; +// HDC hdc; + + if (mouseactive) + { + GetCursorPos (¤t_pos); + + mx_accum += current_pos.x - window_center_x; + my_accum += current_pos.y - window_center_y; + + // force the mouse to the center, so there's room to move + SetCursorPos (window_center_x, window_center_y); + } +} + + +/* +=================== +IN_ClearStates +=================== +*/ +void IN_ClearStates (void) +{ + + if (mouseactive) + { + mx_accum = 0; + my_accum = 0; + mouse_oldbuttonstate = 0; + } +} + + +/* +=============== +IN_StartupJoystick +=============== +*/ +void IN_StartupJoystick (void) +{ + int /*i,*/ numdevs; + JOYCAPS jc; + MMRESULT mmr; + + // assume no joystick + joy_avail = false; + + // abort startup if user requests no joystick + if ( COM_CheckParm ("-nojoy") ) + return; + + // verify joystick driver is present + if ((numdevs = joyGetNumDevs ()) == 0) + { + Con_Printf ("\njoystick not found -- driver not present\n\n"); + return; + } + + // cycle through the joystick ids for the first valid one + for (joy_id=0 ; joy_id 14000.0) + fTemp = 14000.0; + // restore direction information + fAxisValue = (fAxisValue > 0.0) ? fTemp : -fTemp; + } + } + + // convert range from -32768..32767 to -1..1 + fAxisValue /= 32768.0; + + switch (dwAxisMap[i]) + { + case AxisForward: + if ((joy_advanced.value == 0.0) && (in_mlook.state & 1)) + { + // user wants forward control to become look control + if (fabs(fAxisValue) > joy_pitchthreshold.value) + { + // if mouse invert is on, invert the joystick pitch value + // only absolute control support here (joy_advanced is false) + if (m_pitch.value < 0.0) + { + cl.viewangles[PITCH] -= (fAxisValue * joy_pitchsensitivity.value) * aspeed * cl_pitchspeed.value; + } + else + { + cl.viewangles[PITCH] += (fAxisValue * joy_pitchsensitivity.value) * aspeed * cl_pitchspeed.value; + } + V_StopPitchDrift(); + } + else + { + // no pitch movement + // disable pitch return-to-center unless requested by user + // *** this code can be removed when the lookspring bug is fixed + // *** the bug always has the lookspring feature on + if(lookspring.value == 0.0) + V_StopPitchDrift(); + } + } + else + { + // user wants forward control to be forward control + if (fabs(fAxisValue) > joy_forwardthreshold.value) + { + cmd->forwardmove += (fAxisValue * joy_forwardsensitivity.value) * speed * cl_forwardspeed.value; + } + } + break; + + case AxisSide: + if (fabs(fAxisValue) > joy_sidethreshold.value) + { + cmd->sidemove += (fAxisValue * joy_sidesensitivity.value) * speed * cl_sidespeed.value; + } + break; + + case AxisTurn: + if ((in_strafe.state & 1) || (lookstrafe.value && (in_mlook.state & 1))) + { + // user wants turn control to become side control + if (fabs(fAxisValue) > joy_sidethreshold.value) + { + cmd->sidemove -= (fAxisValue * joy_sidesensitivity.value) * speed * cl_sidespeed.value; + } + } + else + { + // user wants turn control to be turn control + if (fabs(fAxisValue) > joy_yawthreshold.value) + { + if(dwControlMap[i] == JOY_ABSOLUTE_AXIS) + { + cl.viewangles[YAW] += (fAxisValue * joy_yawsensitivity.value) * aspeed * cl_yawspeed.value; + } + else + { + cl.viewangles[YAW] += (fAxisValue * joy_yawsensitivity.value) * speed * 180.0; + } + + } + } + break; + + case AxisLook: + if (in_mlook.state & 1) + { + if (fabs(fAxisValue) > joy_pitchthreshold.value) + { + // pitch movement detected and pitch movement desired by user + if(dwControlMap[i] == JOY_ABSOLUTE_AXIS) + { + cl.viewangles[PITCH] += (fAxisValue * joy_pitchsensitivity.value) * aspeed * cl_pitchspeed.value; + } + else + { + cl.viewangles[PITCH] += (fAxisValue * joy_pitchsensitivity.value) * speed * 180.0; + } + V_StopPitchDrift(); + } + else + { + // no pitch movement + // disable pitch return-to-center unless requested by user + // *** this code can be removed when the lookspring bug is fixed + // *** the bug always has the lookspring feature on + if(lookspring.value == 0.0) + V_StopPitchDrift(); + } + } + break; + + default: + break; + } + } + + // bounds check pitch + if (cl.viewangles[PITCH] > 80.0) + cl.viewangles[PITCH] = 80.0; + if (cl.viewangles[PITCH] < -70.0) + cl.viewangles[PITCH] = -70.0; +} + +//========================================================================== + +static byte scantokey[128] = +{ +// 0 1 2 3 4 5 6 7 +// 8 9 A B C D E F + 0 , K_ESCAPE,'1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '-', '=', K_BACKSPACE, 9, // 0 + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', + 'o', 'p', '[', ']', K_ENTER,K_CTRL, 'a', 's', // 1 + 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', + '\'', '`', K_SHIFT,'\\', 'z', 'x', 'c', 'v', // 2 + 'b', 'n', 'm', ',', '.', '/', K_SHIFT,KP_STAR, + K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3 + K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE,K_SCRLCK,K_HOME, + K_UPARROW,K_PGUP,KP_MINUS,K_LEFTARROW,KP_5,K_RIGHTARROW,KP_PLUS,K_END, // 4 + K_DOWNARROW,K_PGDN,K_INS,K_DEL, 0, 0, 0, K_F11, + K_F12, 0, 0, 0, 0, 0, 0, 0, // 5 + 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 +}; + + +/* +======= +IN_MapKey + +Map from windows to quake keynums +======= +*/ +int IN_MapKey (int key) +{ + int extended; + extern cvar_t cl_keypad; + + extended = (key >> 24) & 1; + + key = (key>>16)&255; + if (key > 127) + return 0; + + key = scantokey[key]; + + if (cl_keypad.value) { + if (extended) { + switch (key) { + case K_ENTER: return KP_ENTER; + case '/': return KP_SLASH; + case K_PAUSE: return KP_NUMLOCK; + }; + } else { + switch (key) { + case K_HOME: return KP_HOME; + case K_UPARROW: return KP_UPARROW; + case K_PGUP: return KP_PGUP; + case K_LEFTARROW: return KP_LEFTARROW; + case K_RIGHTARROW: return KP_RIGHTARROW; + case K_END: return KP_END; + case K_DOWNARROW: return KP_DOWNARROW; + case K_PGDN: return KP_PGDN; + case K_INS: return KP_INS; + case K_DEL: return KP_DEL; + } + } + } else { + // cl_keypad 0, compatibility mode + switch (key) { + case KP_STAR: return '*'; + case KP_MINUS: return '-'; + case KP_5: return '5'; + case KP_PLUS: return '+'; + } + } + + return key; +} diff --git a/source/input.h b/source/input.h index dc7e6cf8..4a5a1296 100644 --- a/source/input.h +++ b/source/input.h @@ -1,34 +1,34 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// input.h -- external (non-keyboard) input devices - -void IN_Init (void); - -void IN_Shutdown (void); - -void IN_Commands (void); -// oportunity for devices to stick commands on the script buffer - -void IN_Move (usercmd_t *cmd); -// add additional movement on top of the keyboard move cmd - -void IN_ModeChanged (void); -// called whenever screen dimensions change - +/* +Copyright (C) 1996-1997 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. + +*/ +// input.h -- external (non-keyboard) input devices + +void IN_Init (void); + +void IN_Shutdown (void); + +void IN_Commands (void); +// oportunity for devices to stick commands on the script buffer + +void IN_Move (usercmd_t *cmd); +// add additional movement on top of the keyboard move cmd + +void IN_ModeChanged (void); +// called whenever screen dimensions change + diff --git a/source/keys.c b/source/keys.c index b3794eef..e5aa0251 100644 --- a/source/keys.c +++ b/source/keys.c @@ -1,1019 +1,1019 @@ -/* -Copyright (C) 1996-1997 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 "quakedef.h" -#include "keys.h" -#ifdef _WINDOWS -#include -#endif -/* - -key up events are sent even if in console mode - -*/ - -cvar_t cl_chatmode = {"cl_chatmode", "2"}; - -#define MAXCMDLINE 256 -char key_lines[32][MAXCMDLINE]; -int key_linepos; -int key_lastpress; - -int edit_line=0; -int history_line=0; - -keydest_t key_dest; - -int key_count; // incremented every key event - -char *keybindings[256]; -qboolean consolekeys[256]; // if true, can't be rebound while in console -qboolean menubound[256]; // if true, can't be rebound while in menu -int keyshift[256]; // key to map to if shift held down in console -int key_repeats[256]; // if > 1, it is autorepeating -qboolean keydown[256]; - -typedef struct -{ - char *name; - int keynum; -} keyname_t; - -keyname_t keynames[] = -{ - {"TAB", K_TAB}, - {"ENTER", K_ENTER}, - {"ESCAPE", K_ESCAPE}, - {"SPACE", K_SPACE}, - {"BACKSPACE", K_BACKSPACE}, - - {"CAPSLOCK",K_CAPSLOCK}, - {"PRINTSCR", K_PRINTSCR}, - {"SCRLCK", K_SCRLCK}, - {"SCROLLOCK", K_SCRLCK}, // FIXME - {"PAUSE", K_PAUSE}, - - {"UPARROW", K_UPARROW}, - {"DOWNARROW", K_DOWNARROW}, - {"LEFTARROW", K_LEFTARROW}, - {"RIGHTARROW", K_RIGHTARROW}, - - {"ALT", K_ALT}, - {"CTRL", K_CTRL}, - {"SHIFT", K_SHIFT}, - - // Keypad stuff.. - - {"NUMLOCK", KP_NUMLOCK}, - {"KP_NUMLCK", KP_NUMLOCK}, - {"KP_NUMLOCK", KP_NUMLOCK}, - {"KP_SLASH", KP_SLASH}, - {"KP_DIVIDE", KP_SLASH}, - {"KP_STAR", KP_STAR}, - {"KP_MULTIPLY", KP_STAR}, - - {"KP_MINUS", KP_MINUS}, - - {"KP_HOME", KP_HOME}, - {"KP_7", KP_HOME}, - {"KP_UPARROW", KP_UPARROW}, - {"KP_8", KP_UPARROW}, - {"KP_PGUP", KP_PGUP}, - {"KP_9", KP_PGUP}, - {"KP_PLUS", KP_PLUS}, - - {"KP_LEFTARROW", KP_LEFTARROW}, - {"KP_4", KP_LEFTARROW}, - {"KP_5", KP_5}, - {"KP_RIGHTARROW", KP_RIGHTARROW}, - {"KP_6", KP_RIGHTARROW}, - - {"KP_END", KP_END}, - {"KP_1", KP_END}, - {"KP_DOWNARROW", KP_DOWNARROW}, - {"KP_2", KP_DOWNARROW}, - {"KP_PGDN", KP_PGDN}, - {"KP_3", KP_PGDN}, - - {"KP_INS", KP_INS}, - {"KP_0", KP_INS}, - {"KP_DEL", KP_DEL}, - {"KP_ENTER", KP_ENTER}, - - {"F1", K_F1}, - {"F2", K_F2}, - {"F3", K_F3}, - {"F4", K_F4}, - {"F5", K_F5}, - {"F6", K_F6}, - {"F7", K_F7}, - {"F8", K_F8}, - {"F9", K_F9}, - {"F10", K_F10}, - {"F11", K_F11}, - {"F12", K_F12}, - - {"INS", K_INS}, - {"DEL", K_DEL}, - {"PGDN", K_PGDN}, - {"PGUP", K_PGUP}, - {"HOME", K_HOME}, - {"END", K_END}, - - {"MOUSE1", K_MOUSE1}, - {"MOUSE2", K_MOUSE2}, - {"MOUSE3", K_MOUSE3}, - - {"JOY1", K_JOY1}, - {"JOY2", K_JOY2}, - {"JOY3", K_JOY3}, - {"JOY4", K_JOY4}, - - {"AUX1", K_AUX1}, - {"AUX2", K_AUX2}, - {"AUX3", K_AUX3}, - {"AUX4", K_AUX4}, - {"AUX5", K_AUX5}, - {"AUX6", K_AUX6}, - {"AUX7", K_AUX7}, - {"AUX8", K_AUX8}, - {"AUX9", K_AUX9}, - {"AUX10", K_AUX10}, - {"AUX11", K_AUX11}, - {"AUX12", K_AUX12}, - {"AUX13", K_AUX13}, - {"AUX14", K_AUX14}, - {"AUX15", K_AUX15}, - {"AUX16", K_AUX16}, - {"AUX17", K_AUX17}, - {"AUX18", K_AUX18}, - {"AUX19", K_AUX19}, - {"AUX20", K_AUX20}, - {"AUX21", K_AUX21}, - {"AUX22", K_AUX22}, - {"AUX23", K_AUX23}, - {"AUX24", K_AUX24}, - {"AUX25", K_AUX25}, - {"AUX26", K_AUX26}, - {"AUX27", K_AUX27}, - {"AUX28", K_AUX28}, - {"AUX29", K_AUX29}, - {"AUX30", K_AUX30}, - {"AUX31", K_AUX31}, - {"AUX32", K_AUX32}, - - {"PAUSE", K_PAUSE}, - - {"MWHEELUP", K_MWHEELUP}, - {"MWHEELDOWN", K_MWHEELDOWN}, - - {"SEMICOLON", ';'}, // because a raw semicolon seperates commands - - {NULL,0} -}; - -/* -============================================================================== - - LINE TYPING INTO THE CONSOLE - -============================================================================== -*/ - -qboolean CheckForCommand (void) -{ - char command[128]; - char *cmd, *s; - int i; - - s = key_lines[edit_line]+1; - - for (i=0 ; i<127 ; i++) - if (s[i] <= ' ') - break; - else - command[i] = s[i]; - command[i] = 0; - - cmd = Cmd_CompleteCommand (command); - if (!cmd || Q_strcasecmp (cmd, command)) - cmd = Cvar_CompleteVariable (command); - if (!cmd || Q_strcasecmp (cmd, command) ) - return false; // just a chat message - return true; -} - -void CompleteCommand (void) -{ - char *cmd, *s; - - s = key_lines[edit_line]+1; - if (*s == '\\' || *s == '/') - s++; - - cmd = Cmd_CompleteCommand (s); - if (!cmd) - cmd = Cvar_CompleteVariable (s); - if (cmd) - { - key_lines[edit_line][1] = '/'; - strcpy (key_lines[edit_line]+2, cmd); - key_linepos = strlen(cmd)+2; - key_lines[edit_line][key_linepos] = ' '; - key_linepos++; - key_lines[edit_line][key_linepos] = 0; - return; - } -} - -/* -==================== -Key_Console - -Interactive line editing and console scrollback -==================== -*/ -void Key_Console (int key) -{ - int i; - - switch (key) { - case K_ENTER: - // backslash text are commands - if (key_lines[edit_line][1] == '/' && key_lines[edit_line][2] == '/') - goto no_lf; - else if (key_lines[edit_line][1] == '\\' || key_lines[edit_line][1] == '/') - Cbuf_AddText (key_lines[edit_line]+2); // skip the ]/ - else if (cl_chatmode.value != 1 && CheckForCommand()) - Cbuf_AddText (key_lines[edit_line]+1); // valid command - else if ((cls.state >= ca_connected && cl_chatmode.value == 2) || cl_chatmode.value == 1) - { - if (cls.state < ca_connected) // can happen if cl_chatmode is 1 - goto no_lf; // drop the whole line - - // convert to a chat message - Cbuf_AddText ("say "); - Cbuf_AddText (key_lines[edit_line]+1); - } - else - Cbuf_AddText (key_lines[edit_line]+1); // skip the ] - - Cbuf_AddText ("\n"); -no_lf: - Con_Printf ("%s\n",key_lines[edit_line]); - edit_line = (edit_line + 1) & 31; - history_line = edit_line; - key_lines[edit_line][0] = ']'; - key_lines[edit_line][1] = 0; - key_linepos = 1; - if (cls.state == ca_disconnected) - SCR_UpdateScreen (); // force an update, because the command - // may take some time - return; - - case K_TAB: - // command completion - CompleteCommand (); - return; - - case K_BACKSPACE: - if (key_linepos > 1) - { - strcpy(key_lines[edit_line] + key_linepos - 1, key_lines[edit_line] + key_linepos); - key_linepos--; - } - return; - - case K_DEL: - if (key_linepos < strlen(key_lines[edit_line])) - strcpy(key_lines[edit_line] + key_linepos, key_lines[edit_line] + key_linepos + 1); - return; - - case K_RIGHTARROW: - if (keydown[K_CTRL]) { - // word right - i = strlen(key_lines[edit_line]); - while (key_linepos < i && key_lines[edit_line][key_linepos] != ' ') - key_linepos++; - while (key_linepos < i && key_lines[edit_line][key_linepos] == ' ') - key_linepos++; - return; - } - if (key_linepos < strlen(key_lines[edit_line])) - key_linepos++; - return; - - case K_LEFTARROW: - if (keydown[K_CTRL]) { - // word left - while (key_linepos > 1 && key_lines[edit_line][key_linepos-1] == ' ') - key_linepos--; - while (key_linepos > 1 && key_lines[edit_line][key_linepos-1] != ' ') - key_linepos--; - return; - } - if (key_linepos > 1) - key_linepos--; - return; - - case K_UPARROW: - do { - history_line = (history_line - 1) & 31; - } while (history_line != edit_line - && !key_lines[history_line][1]); - if (history_line == edit_line) - history_line = (edit_line+1)&31; - strcpy(key_lines[edit_line], key_lines[history_line]); - key_linepos = strlen(key_lines[edit_line]); - return; - - case K_DOWNARROW: - if (history_line == edit_line) return; - do { - history_line = (history_line + 1) & 31; - } while (history_line != edit_line - && !key_lines[history_line][1]); - - if (history_line == edit_line) { - key_lines[edit_line][0] = ']'; - key_lines[edit_line][1] = 0; - key_linepos = 1; - } else { - strcpy(key_lines[edit_line], key_lines[history_line]); - key_linepos = strlen(key_lines[edit_line]); - } - return; - - case K_PGUP: - case K_MWHEELUP: - if (keydown[K_CTRL] && key == K_PGUP) - con->display -= ((int)scr_conlines-22)>>3; - else - con->display -= 2; - if (con->display - con->current + con->numlines < 0) - con->display = con->current - con->numlines; - return; - - case K_MWHEELDOWN: - case K_PGDN: - if (keydown[K_CTRL] && key == K_PGDN) - con->display += ((int)scr_conlines-22)>>3; - else - con->display += 2; - if (con->display - con->current > 0) - con->display = con->current; - return; - - case K_HOME: - if (keydown[K_CTRL]) - con->display = con->current - con->numlines; - else - key_linepos = 1; - return; - - case K_END: - if (keydown[K_CTRL]) - con->display = con->current; - else - key_linepos = strlen(key_lines[edit_line]); - return; - } -#ifdef _WIN32 - if ((key=='V' || key=='v') && keydown[K_CTRL]) - { - HANDLE th; - char *clipText; - - if (OpenClipboard(NULL)) { - th = GetClipboardData(CF_TEXT); - if (th) { - clipText = GlobalLock(th); - if (clipText) { - for (i=0; clipText[i]; i++) - if (clipText[i]=='\n' || clipText[i]=='\r' || clipText[i]=='\b') - break; - if (i + strlen(key_lines[edit_line]) > MAXCMDLINE-1) - i = MAXCMDLINE-1 - strlen(key_lines[edit_line]); - if (i > 0) - { // insert the string - memmove (key_lines[edit_line] + key_linepos + i, - key_lines[edit_line] + key_linepos, strlen(key_lines[edit_line]) - key_linepos + 1); - memcpy (key_lines[edit_line] + key_linepos, clipText, i); - key_linepos += i; - } - } - GlobalUnlock(th); - } - CloseClipboard(); - } - return; - } -#endif - - if (key < 32 || key > 127) - return; // non printable - - if (keydown[K_CTRL]) { - if (key >= '0' && key <= '9') - key = key - '0' + 0x12; // yellow number - else switch (key) - { - case '[': key = 0x10; break; - case ']': key = 0x11; break; - case 'g': key = 0x86; break; - case 'r': key = 0x87; break; - case 'y': key = 0x88; break; - case 'b': key = 0x89; break; - case '(': key = 0x80; break; - case '=': key = 0x81; break; - case ')': key = 0x82; break; - case 'a': key = 0x83; break; - case '<': key = 0x1d; break; - case '-': key = 0x1e; break; - case '>': key = 0x1f; break; - case ',': key = 0x1c; break; - case '.': key = 0x9c; break; - //case '?': key = 0x8b; break; // filled red block - } - } - - if (keydown[K_ALT]) - key |= 128; // red char - - i = strlen(key_lines[edit_line]); - if (i >= MAXCMDLINE-1) - return; - - // This also moves the ending \0 - memmove (key_lines[edit_line]+key_linepos+1, key_lines[edit_line]+key_linepos, i-key_linepos+1); - key_lines[edit_line][key_linepos] = key; - key_linepos++; -} - -//============================================================================ - -qboolean chat_team; -char chat_buffer[MAXCMDLINE]; -int chat_linepos = 0; - -void Key_Message (int key) -{ - int len; - - switch (key) - { - case K_ENTER: - if (chat_team) - Cbuf_AddText ("say_team \""); - else - Cbuf_AddText ("say \""); - Cbuf_AddText(chat_buffer); - Cbuf_AddText("\"\n"); - - key_dest = key_game; - chat_linepos = 0; - chat_buffer[0] = 0; - return; - - case K_ESCAPE: - key_dest = key_game; - chat_buffer[0] = 0; - chat_linepos = 0; - return; - - case K_HOME: - chat_linepos = 0; - return; - - case K_END: - chat_linepos = strlen(chat_buffer); - return; - - case K_LEFTARROW: - if (chat_linepos > 0) - chat_linepos--; - return; - - case K_RIGHTARROW: - if (chat_linepos < strlen(chat_buffer)) - chat_linepos++; - return; - - case K_BACKSPACE: - if (chat_linepos > 0) { - strcpy(chat_buffer + chat_linepos - 1, chat_buffer + chat_linepos); - chat_linepos--; - } - return; - - case K_DEL: - if (chat_buffer[chat_linepos]) - strcpy(chat_buffer + chat_linepos, chat_buffer + chat_linepos + 1); - return; - } - -#ifdef _WIN32 - // FIXME: make a separate function to use it here and in Key_Console - if ((key=='V' || key=='v') && keydown[K_CTRL]) - { - HANDLE th; - char *clipText; - int i; - - if (OpenClipboard(NULL)) { - th = GetClipboardData(CF_TEXT); - if (th) { - clipText = GlobalLock(th); - if (clipText) { - for (i=0; clipText[i]; i++) - if (clipText[i]=='\n' || clipText[i]=='\r' || clipText[i]=='\b') - break; - if (i + strlen(chat_buffer) > sizeof(chat_buffer)-1) - i = sizeof(chat_buffer)-1 - strlen(chat_buffer); - if (i > 0) - { // insert the string - memmove (chat_buffer + chat_linepos + i, - chat_buffer + chat_linepos, strlen(chat_buffer) - chat_linepos + 1); - memcpy (chat_buffer + chat_linepos, clipText, i); - chat_linepos += i; - } - } - GlobalUnlock(th); - } - CloseClipboard(); - } - return; - } -#endif - - if (key < 32 || key > 127) - return; // non printable - - len = strlen(chat_buffer); - - if (len >= sizeof(chat_buffer)-1) - return; // all full - - // This also moves the ending \0 - memmove (chat_buffer+chat_linepos+1, chat_buffer+chat_linepos, - len-chat_linepos+1); - chat_buffer[chat_linepos++] = key; -} - -//============================================================================ - - -/* -=================== -Key_StringToKeynum - -Returns a key number to be used to index keybindings[] by looking at -the given string. Single ascii characters return themselves, while -the K_* names are matched up. -=================== -*/ -int Key_StringToKeynum (char *str) -{ - keyname_t *kn; - - if (!str || !str[0]) - return -1; - if (!str[1]) - return str[0]; - - for (kn=keynames ; kn->name ; kn++) - { - if (!Q_strcasecmp(str,kn->name)) - return kn->keynum; - } - return -1; -} - -/* -=================== -Key_KeynumToString - -Returns a string (either a single ascii char, or a K_* name) for the -given keynum. -FIXME: handle quote special (general escape sequence?) -=================== -*/ -char *Key_KeynumToString (int keynum) -{ - keyname_t *kn; - static char tinystr[2]; - - if (keynum == -1) - return ""; - if (keynum > 32 && keynum < 127) - { // printable ascii - tinystr[0] = keynum; - tinystr[1] = 0; - return tinystr; - } - - for (kn=keynames ; kn->name ; kn++) - if (keynum == kn->keynum) - return kn->name; - - return ""; -} - - -/* -=================== -Key_SetBinding -=================== -*/ -void Key_SetBinding (int keynum, char *binding) -{ - char *new; - int l; - - if (keynum == -1) - return; - -// free old bindings - if (keybindings[keynum]) - { - Z_Free (keybindings[keynum]); - keybindings[keynum] = NULL; - } - -// allocate memory for new binding - l = strlen (binding); - new = Z_Malloc (l+1); - strcpy (new, binding); - new[l] = 0; - keybindings[keynum] = new; -} - -/* -=================== -Key_Unbind_f -=================== -*/ -void Key_Unbind_f (void) -{ - int b; - - if (Cmd_Argc() != 2) - { - Con_Printf ("unbind : remove commands from a key\n"); - return; - } - - b = Key_StringToKeynum (Cmd_Argv(1)); - if (b==-1) - { - Con_Printf ("\"%s\" isn't a valid key\n", Cmd_Argv(1)); - return; - } - - Key_SetBinding (b, ""); -} - -void Key_Unbindall_f (void) -{ - int i; - - for (i=0 ; i<256 ; i++) - if (keybindings[i]) - Key_SetBinding (i, ""); -} - - -/* -=================== -Key_Bind_f -=================== -*/ -void Key_Bind_f (void) -{ - int i, c, b; - char cmd[1024]; - - c = Cmd_Argc(); - - if (c < 2) - { - Con_Printf ("bind [command] : attach a command to a key\n"); - return; - } - b = Key_StringToKeynum (Cmd_Argv(1)); - if (b==-1) - { - Con_Printf ("\"%s\" isn't a valid key\n", Cmd_Argv(1)); - return; - } - - if (c == 2) - { - if (keybindings[b]) - Con_Printf ("\"%s\" = \"%s\"\n", Cmd_Argv(1), keybindings[b] ); - else - Con_Printf ("\"%s\" is not bound\n", Cmd_Argv(1) ); - return; - } - -// copy the rest of the command line - cmd[0] = 0; // start out with a null string - for (i=2 ; i< c ; i++) - { - strcat (cmd, Cmd_Argv(i)); - if (i != (c-1)) - strcat (cmd, " "); - } - - Key_SetBinding (b, cmd); -} - -/* -============ -Key_WriteBindings - -Writes lines containing "bind key value" -============ -*/ -void Key_WriteBindings (FILE *f) -{ - int i; - - for (i=0 ; i<256 ; i++) - if (keybindings[i]) - fprintf (f, "bind %s \"%s\"\n", Key_KeynumToString(i), keybindings[i]); -} - - -/* -=================== -Key_Init -=================== -*/ -void Key_Init (void) -{ - int i; - - for (i=0 ; i<32 ; i++) - { - key_lines[i][0] = ']'; - key_lines[i][1] = 0; - } - key_linepos = 1; - -// -// init ascii characters in console mode -// - for (i=32 ; i<128 ; i++) - consolekeys[i] = true; - consolekeys[K_ENTER] = true; - consolekeys[K_TAB] = true; - consolekeys[K_LEFTARROW] = true; - consolekeys[K_RIGHTARROW] = true; - consolekeys[K_UPARROW] = true; - consolekeys[K_DOWNARROW] = true; - consolekeys[K_BACKSPACE] = true; - consolekeys[K_INS] = true; - consolekeys[K_DEL] = true; - consolekeys[K_HOME] = true; - consolekeys[K_END] = true; - consolekeys[K_PGUP] = true; - consolekeys[K_PGDN] = true; - consolekeys[K_ALT] = true; - consolekeys[K_CTRL] = true; - consolekeys[K_SHIFT] = true; - consolekeys[K_MWHEELUP] = true; - consolekeys[K_MWHEELDOWN] = true; - consolekeys['`'] = false; - consolekeys['~'] = false; - - for (i=0 ; i<256 ; i++) - keyshift[i] = i; - for (i='a' ; i<='z' ; i++) - keyshift[i] = i - 'a' + 'A'; - keyshift['1'] = '!'; - keyshift['2'] = '@'; - keyshift['3'] = '#'; - keyshift['4'] = '$'; - keyshift['5'] = '%'; - keyshift['6'] = '^'; - keyshift['7'] = '&'; - keyshift['8'] = '*'; - keyshift['9'] = '('; - keyshift['0'] = ')'; - keyshift['-'] = '_'; - keyshift['='] = '+'; - keyshift[','] = '<'; - keyshift['.'] = '>'; - keyshift['/'] = '?'; - keyshift[';'] = ':'; - keyshift['\''] = '"'; - keyshift['['] = '{'; - keyshift[']'] = '}'; - keyshift['`'] = '~'; - keyshift['\\'] = '|'; - - menubound[K_ESCAPE] = true; - for (i=0 ; i<12 ; i++) - menubound[K_F1+i] = true; - -// -// register our functions -// - Cmd_AddCommand ("bind",Key_Bind_f); - Cmd_AddCommand ("unbind",Key_Unbind_f); - Cmd_AddCommand ("unbindall",Key_Unbindall_f); - - Cvar_RegisterVariable (&cl_chatmode); -} - -/* -=================== -Key_Event - -Called by the system between frames for both key up and key down events -Should NOT be called during an interrupt! -=================== -*/ -void Key_Event (int key, qboolean down) -{ - char *kb; - char cmd[1024]; - -// Con_Printf ("%i : %i\n", key, down); //@@@ - - keydown[key] = down; - - if (!down) - key_repeats[key] = 0; - - key_lastpress = key; - key_count++; - if (key_count <= 0) - { - return; // just catching keys for Con_NotifyBox - } - -// update auto-repeat status - if (down) - { - key_repeats[key]++; - if (key_repeats[key] > 1) - { - if ((key != K_BACKSPACE && key != K_DEL - && key != K_LEFTARROW && key != K_RIGHTARROW - && key != K_UPARROW && key != K_DOWNARROW - && key != K_PGUP && key != K_PGDN && (key < 32 || key > 126 || key == '`')) - || (key_dest == key_game && cls.state == ca_active)) - return; // ignore most autorepeats - } - - if (key >= 200 && !keybindings[key]) - Con_Printf ("%s is unbound, hit F4 to set.\n", Key_KeynumToString (key) ); - } - -// -// handle escape specialy, so the user can never unbind it -// - if (key == K_ESCAPE) - { - if (!down) - return; - switch (key_dest) - { - case key_message: - Key_Message (key); - break; - case key_menu: - M_Keydown (key); - break; - case key_game: - case key_console: - M_ToggleMenu_f (); - break; - default: - Sys_Error ("Bad key_dest"); - } - return; - } - -// -// key up events only generate commands if the game key binding is -// a button command (leading + sign). These will occur even in console mode, -// to keep the character from continuing an action started before a console -// switch. Button commands include the kenum as a parameter, so multiple -// downs can be matched with ups -// - if (!down) - { - kb = keybindings[key]; - if (kb && kb[0] == '+') - { - sprintf (cmd, "-%s %i\n", kb+1, key); - Cbuf_AddText (cmd); - } - if (keyshift[key] != key) - { - kb = keybindings[keyshift[key]]; - if (kb && kb[0] == '+') - { - sprintf (cmd, "-%s %i\n", kb+1, key); - Cbuf_AddText (cmd); - } - } - return; - } - -// -// during demo playback, most keys bring up the main menu -// - if (cls.demoplayback && !cls.demoplayback2 && down && consolekeys[key] && key_dest == key_game - && key != K_ALT && key != K_CTRL && key != K_SHIFT - && key != K_INS && key != K_DEL && key != K_HOME - && key != K_END && key != K_TAB) - { - M_ToggleMenu_f (); - return; - } - -// -// if not a consolekey, send to the interpreter no matter what mode is -// - if ( (key_dest == key_menu && menubound[key]) - || ((key_dest == key_console || key_dest == key_message) - && !consolekeys[key]) - || (key_dest == key_game && ( cls.state == ca_active || !consolekeys[key] ) ) ) - { - kb = keybindings[key]; - if (kb) - { - if (kb[0] == '+') - { // button commands add keynum as a parm - sprintf (cmd, "%s %i\n", kb, key); - Cbuf_AddText (cmd); - } - else - { - Cbuf_AddText (kb); - Cbuf_AddText ("\n"); - } - } - return; - } - - if (!down) - return; // other systems only care about key down events - - if (keydown[K_SHIFT]) - key = keyshift[key]; - - switch (key_dest) - { - case key_message: - Key_Message (key); - break; - case key_menu: - M_Keydown (key); - break; - - case key_game: - case key_console: - Key_Console (key); - break; - default: - Sys_Error ("Bad key_dest"); - } -} - -/* -=================== -Key_ClearStates -=================== -*/ -void Key_ClearStates (void) -{ - int i; - - for (i=0 ; i<256 ; i++) - { - keydown[i] = false; - key_repeats[i] = false; - } -} - +/* +Copyright (C) 1996-1997 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 "quakedef.h" +#include "keys.h" +#ifdef _WINDOWS +#include +#endif +/* + +key up events are sent even if in console mode + +*/ + +cvar_t cl_chatmode = {"cl_chatmode", "2"}; + +#define MAXCMDLINE 256 +char key_lines[32][MAXCMDLINE]; +int key_linepos; +int key_lastpress; + +int edit_line=0; +int history_line=0; + +keydest_t key_dest; + +int key_count; // incremented every key event + +char *keybindings[256]; +qboolean consolekeys[256]; // if true, can't be rebound while in console +qboolean menubound[256]; // if true, can't be rebound while in menu +int keyshift[256]; // key to map to if shift held down in console +int key_repeats[256]; // if > 1, it is autorepeating +qboolean keydown[256]; + +typedef struct +{ + char *name; + int keynum; +} keyname_t; + +keyname_t keynames[] = +{ + {"TAB", K_TAB}, + {"ENTER", K_ENTER}, + {"ESCAPE", K_ESCAPE}, + {"SPACE", K_SPACE}, + {"BACKSPACE", K_BACKSPACE}, + + {"CAPSLOCK",K_CAPSLOCK}, + {"PRINTSCR", K_PRINTSCR}, + {"SCRLCK", K_SCRLCK}, + {"SCROLLOCK", K_SCRLCK}, // FIXME + {"PAUSE", K_PAUSE}, + + {"UPARROW", K_UPARROW}, + {"DOWNARROW", K_DOWNARROW}, + {"LEFTARROW", K_LEFTARROW}, + {"RIGHTARROW", K_RIGHTARROW}, + + {"ALT", K_ALT}, + {"CTRL", K_CTRL}, + {"SHIFT", K_SHIFT}, + + // Keypad stuff.. + + {"NUMLOCK", KP_NUMLOCK}, + {"KP_NUMLCK", KP_NUMLOCK}, + {"KP_NUMLOCK", KP_NUMLOCK}, + {"KP_SLASH", KP_SLASH}, + {"KP_DIVIDE", KP_SLASH}, + {"KP_STAR", KP_STAR}, + {"KP_MULTIPLY", KP_STAR}, + + {"KP_MINUS", KP_MINUS}, + + {"KP_HOME", KP_HOME}, + {"KP_7", KP_HOME}, + {"KP_UPARROW", KP_UPARROW}, + {"KP_8", KP_UPARROW}, + {"KP_PGUP", KP_PGUP}, + {"KP_9", KP_PGUP}, + {"KP_PLUS", KP_PLUS}, + + {"KP_LEFTARROW", KP_LEFTARROW}, + {"KP_4", KP_LEFTARROW}, + {"KP_5", KP_5}, + {"KP_RIGHTARROW", KP_RIGHTARROW}, + {"KP_6", KP_RIGHTARROW}, + + {"KP_END", KP_END}, + {"KP_1", KP_END}, + {"KP_DOWNARROW", KP_DOWNARROW}, + {"KP_2", KP_DOWNARROW}, + {"KP_PGDN", KP_PGDN}, + {"KP_3", KP_PGDN}, + + {"KP_INS", KP_INS}, + {"KP_0", KP_INS}, + {"KP_DEL", KP_DEL}, + {"KP_ENTER", KP_ENTER}, + + {"F1", K_F1}, + {"F2", K_F2}, + {"F3", K_F3}, + {"F4", K_F4}, + {"F5", K_F5}, + {"F6", K_F6}, + {"F7", K_F7}, + {"F8", K_F8}, + {"F9", K_F9}, + {"F10", K_F10}, + {"F11", K_F11}, + {"F12", K_F12}, + + {"INS", K_INS}, + {"DEL", K_DEL}, + {"PGDN", K_PGDN}, + {"PGUP", K_PGUP}, + {"HOME", K_HOME}, + {"END", K_END}, + + {"MOUSE1", K_MOUSE1}, + {"MOUSE2", K_MOUSE2}, + {"MOUSE3", K_MOUSE3}, + + {"JOY1", K_JOY1}, + {"JOY2", K_JOY2}, + {"JOY3", K_JOY3}, + {"JOY4", K_JOY4}, + + {"AUX1", K_AUX1}, + {"AUX2", K_AUX2}, + {"AUX3", K_AUX3}, + {"AUX4", K_AUX4}, + {"AUX5", K_AUX5}, + {"AUX6", K_AUX6}, + {"AUX7", K_AUX7}, + {"AUX8", K_AUX8}, + {"AUX9", K_AUX9}, + {"AUX10", K_AUX10}, + {"AUX11", K_AUX11}, + {"AUX12", K_AUX12}, + {"AUX13", K_AUX13}, + {"AUX14", K_AUX14}, + {"AUX15", K_AUX15}, + {"AUX16", K_AUX16}, + {"AUX17", K_AUX17}, + {"AUX18", K_AUX18}, + {"AUX19", K_AUX19}, + {"AUX20", K_AUX20}, + {"AUX21", K_AUX21}, + {"AUX22", K_AUX22}, + {"AUX23", K_AUX23}, + {"AUX24", K_AUX24}, + {"AUX25", K_AUX25}, + {"AUX26", K_AUX26}, + {"AUX27", K_AUX27}, + {"AUX28", K_AUX28}, + {"AUX29", K_AUX29}, + {"AUX30", K_AUX30}, + {"AUX31", K_AUX31}, + {"AUX32", K_AUX32}, + + {"PAUSE", K_PAUSE}, + + {"MWHEELUP", K_MWHEELUP}, + {"MWHEELDOWN", K_MWHEELDOWN}, + + {"SEMICOLON", ';'}, // because a raw semicolon seperates commands + + {NULL,0} +}; + +/* +============================================================================== + + LINE TYPING INTO THE CONSOLE + +============================================================================== +*/ + +qboolean CheckForCommand (void) +{ + char command[128]; + char *cmd, *s; + int i; + + s = key_lines[edit_line]+1; + + for (i=0 ; i<127 ; i++) + if (s[i] <= ' ') + break; + else + command[i] = s[i]; + command[i] = 0; + + cmd = Cmd_CompleteCommand (command); + if (!cmd || Q_strcasecmp (cmd, command)) + cmd = Cvar_CompleteVariable (command); + if (!cmd || Q_strcasecmp (cmd, command) ) + return false; // just a chat message + return true; +} + +void CompleteCommand (void) +{ + char *cmd, *s; + + s = key_lines[edit_line]+1; + if (*s == '\\' || *s == '/') + s++; + + cmd = Cmd_CompleteCommand (s); + if (!cmd) + cmd = Cvar_CompleteVariable (s); + if (cmd) + { + key_lines[edit_line][1] = '/'; + strcpy (key_lines[edit_line]+2, cmd); + key_linepos = strlen(cmd)+2; + key_lines[edit_line][key_linepos] = ' '; + key_linepos++; + key_lines[edit_line][key_linepos] = 0; + return; + } +} + +/* +==================== +Key_Console + +Interactive line editing and console scrollback +==================== +*/ +void Key_Console (int key) +{ + int i; + + switch (key) { + case K_ENTER: + // backslash text are commands + if (key_lines[edit_line][1] == '/' && key_lines[edit_line][2] == '/') + goto no_lf; + else if (key_lines[edit_line][1] == '\\' || key_lines[edit_line][1] == '/') + Cbuf_AddText (key_lines[edit_line]+2); // skip the ]/ + else if (cl_chatmode.value != 1 && CheckForCommand()) + Cbuf_AddText (key_lines[edit_line]+1); // valid command + else if ((cls.state >= ca_connected && cl_chatmode.value == 2) || cl_chatmode.value == 1) + { + if (cls.state < ca_connected) // can happen if cl_chatmode is 1 + goto no_lf; // drop the whole line + + // convert to a chat message + Cbuf_AddText ("say "); + Cbuf_AddText (key_lines[edit_line]+1); + } + else + Cbuf_AddText (key_lines[edit_line]+1); // skip the ] + + Cbuf_AddText ("\n"); +no_lf: + Con_Printf ("%s\n",key_lines[edit_line]); + edit_line = (edit_line + 1) & 31; + history_line = edit_line; + key_lines[edit_line][0] = ']'; + key_lines[edit_line][1] = 0; + key_linepos = 1; + if (cls.state == ca_disconnected) + SCR_UpdateScreen (); // force an update, because the command + // may take some time + return; + + case K_TAB: + // command completion + CompleteCommand (); + return; + + case K_BACKSPACE: + if (key_linepos > 1) + { + strcpy(key_lines[edit_line] + key_linepos - 1, key_lines[edit_line] + key_linepos); + key_linepos--; + } + return; + + case K_DEL: + if (key_linepos < strlen(key_lines[edit_line])) + strcpy(key_lines[edit_line] + key_linepos, key_lines[edit_line] + key_linepos + 1); + return; + + case K_RIGHTARROW: + if (keydown[K_CTRL]) { + // word right + i = strlen(key_lines[edit_line]); + while (key_linepos < i && key_lines[edit_line][key_linepos] != ' ') + key_linepos++; + while (key_linepos < i && key_lines[edit_line][key_linepos] == ' ') + key_linepos++; + return; + } + if (key_linepos < strlen(key_lines[edit_line])) + key_linepos++; + return; + + case K_LEFTARROW: + if (keydown[K_CTRL]) { + // word left + while (key_linepos > 1 && key_lines[edit_line][key_linepos-1] == ' ') + key_linepos--; + while (key_linepos > 1 && key_lines[edit_line][key_linepos-1] != ' ') + key_linepos--; + return; + } + if (key_linepos > 1) + key_linepos--; + return; + + case K_UPARROW: + do { + history_line = (history_line - 1) & 31; + } while (history_line != edit_line + && !key_lines[history_line][1]); + if (history_line == edit_line) + history_line = (edit_line+1)&31; + strcpy(key_lines[edit_line], key_lines[history_line]); + key_linepos = strlen(key_lines[edit_line]); + return; + + case K_DOWNARROW: + if (history_line == edit_line) return; + do { + history_line = (history_line + 1) & 31; + } while (history_line != edit_line + && !key_lines[history_line][1]); + + if (history_line == edit_line) { + key_lines[edit_line][0] = ']'; + key_lines[edit_line][1] = 0; + key_linepos = 1; + } else { + strcpy(key_lines[edit_line], key_lines[history_line]); + key_linepos = strlen(key_lines[edit_line]); + } + return; + + case K_PGUP: + case K_MWHEELUP: + if (keydown[K_CTRL] && key == K_PGUP) + con->display -= ((int)scr_conlines-22)>>3; + else + con->display -= 2; + if (con->display - con->current + con->numlines < 0) + con->display = con->current - con->numlines; + return; + + case K_MWHEELDOWN: + case K_PGDN: + if (keydown[K_CTRL] && key == K_PGDN) + con->display += ((int)scr_conlines-22)>>3; + else + con->display += 2; + if (con->display - con->current > 0) + con->display = con->current; + return; + + case K_HOME: + if (keydown[K_CTRL]) + con->display = con->current - con->numlines; + else + key_linepos = 1; + return; + + case K_END: + if (keydown[K_CTRL]) + con->display = con->current; + else + key_linepos = strlen(key_lines[edit_line]); + return; + } +#ifdef _WIN32 + if ((key=='V' || key=='v') && keydown[K_CTRL]) + { + HANDLE th; + char *clipText; + + if (OpenClipboard(NULL)) { + th = GetClipboardData(CF_TEXT); + if (th) { + clipText = GlobalLock(th); + if (clipText) { + for (i=0; clipText[i]; i++) + if (clipText[i]=='\n' || clipText[i]=='\r' || clipText[i]=='\b') + break; + if (i + strlen(key_lines[edit_line]) > MAXCMDLINE-1) + i = MAXCMDLINE-1 - strlen(key_lines[edit_line]); + if (i > 0) + { // insert the string + memmove (key_lines[edit_line] + key_linepos + i, + key_lines[edit_line] + key_linepos, strlen(key_lines[edit_line]) - key_linepos + 1); + memcpy (key_lines[edit_line] + key_linepos, clipText, i); + key_linepos += i; + } + } + GlobalUnlock(th); + } + CloseClipboard(); + } + return; + } +#endif + + if (key < 32 || key > 127) + return; // non printable + + if (keydown[K_CTRL]) { + if (key >= '0' && key <= '9') + key = key - '0' + 0x12; // yellow number + else switch (key) + { + case '[': key = 0x10; break; + case ']': key = 0x11; break; + case 'g': key = 0x86; break; + case 'r': key = 0x87; break; + case 'y': key = 0x88; break; + case 'b': key = 0x89; break; + case '(': key = 0x80; break; + case '=': key = 0x81; break; + case ')': key = 0x82; break; + case 'a': key = 0x83; break; + case '<': key = 0x1d; break; + case '-': key = 0x1e; break; + case '>': key = 0x1f; break; + case ',': key = 0x1c; break; + case '.': key = 0x9c; break; + //case '?': key = 0x8b; break; // filled red block + } + } + + if (keydown[K_ALT]) + key |= 128; // red char + + i = strlen(key_lines[edit_line]); + if (i >= MAXCMDLINE-1) + return; + + // This also moves the ending \0 + memmove (key_lines[edit_line]+key_linepos+1, key_lines[edit_line]+key_linepos, i-key_linepos+1); + key_lines[edit_line][key_linepos] = key; + key_linepos++; +} + +//============================================================================ + +qboolean chat_team; +char chat_buffer[MAXCMDLINE]; +int chat_linepos = 0; + +void Key_Message (int key) +{ + int len; + + switch (key) + { + case K_ENTER: + if (chat_team) + Cbuf_AddText ("say_team \""); + else + Cbuf_AddText ("say \""); + Cbuf_AddText(chat_buffer); + Cbuf_AddText("\"\n"); + + key_dest = key_game; + chat_linepos = 0; + chat_buffer[0] = 0; + return; + + case K_ESCAPE: + key_dest = key_game; + chat_buffer[0] = 0; + chat_linepos = 0; + return; + + case K_HOME: + chat_linepos = 0; + return; + + case K_END: + chat_linepos = strlen(chat_buffer); + return; + + case K_LEFTARROW: + if (chat_linepos > 0) + chat_linepos--; + return; + + case K_RIGHTARROW: + if (chat_linepos < strlen(chat_buffer)) + chat_linepos++; + return; + + case K_BACKSPACE: + if (chat_linepos > 0) { + strcpy(chat_buffer + chat_linepos - 1, chat_buffer + chat_linepos); + chat_linepos--; + } + return; + + case K_DEL: + if (chat_buffer[chat_linepos]) + strcpy(chat_buffer + chat_linepos, chat_buffer + chat_linepos + 1); + return; + } + +#ifdef _WIN32 + // FIXME: make a separate function to use it here and in Key_Console + if ((key=='V' || key=='v') && keydown[K_CTRL]) + { + HANDLE th; + char *clipText; + int i; + + if (OpenClipboard(NULL)) { + th = GetClipboardData(CF_TEXT); + if (th) { + clipText = GlobalLock(th); + if (clipText) { + for (i=0; clipText[i]; i++) + if (clipText[i]=='\n' || clipText[i]=='\r' || clipText[i]=='\b') + break; + if (i + strlen(chat_buffer) > sizeof(chat_buffer)-1) + i = sizeof(chat_buffer)-1 - strlen(chat_buffer); + if (i > 0) + { // insert the string + memmove (chat_buffer + chat_linepos + i, + chat_buffer + chat_linepos, strlen(chat_buffer) - chat_linepos + 1); + memcpy (chat_buffer + chat_linepos, clipText, i); + chat_linepos += i; + } + } + GlobalUnlock(th); + } + CloseClipboard(); + } + return; + } +#endif + + if (key < 32 || key > 127) + return; // non printable + + len = strlen(chat_buffer); + + if (len >= sizeof(chat_buffer)-1) + return; // all full + + // This also moves the ending \0 + memmove (chat_buffer+chat_linepos+1, chat_buffer+chat_linepos, + len-chat_linepos+1); + chat_buffer[chat_linepos++] = key; +} + +//============================================================================ + + +/* +=================== +Key_StringToKeynum + +Returns a key number to be used to index keybindings[] by looking at +the given string. Single ascii characters return themselves, while +the K_* names are matched up. +=================== +*/ +int Key_StringToKeynum (char *str) +{ + keyname_t *kn; + + if (!str || !str[0]) + return -1; + if (!str[1]) + return str[0]; + + for (kn=keynames ; kn->name ; kn++) + { + if (!Q_strcasecmp(str,kn->name)) + return kn->keynum; + } + return -1; +} + +/* +=================== +Key_KeynumToString + +Returns a string (either a single ascii char, or a K_* name) for the +given keynum. +FIXME: handle quote special (general escape sequence?) +=================== +*/ +char *Key_KeynumToString (int keynum) +{ + keyname_t *kn; + static char tinystr[2]; + + if (keynum == -1) + return ""; + if (keynum > 32 && keynum < 127) + { // printable ascii + tinystr[0] = keynum; + tinystr[1] = 0; + return tinystr; + } + + for (kn=keynames ; kn->name ; kn++) + if (keynum == kn->keynum) + return kn->name; + + return ""; +} + + +/* +=================== +Key_SetBinding +=================== +*/ +void Key_SetBinding (int keynum, char *binding) +{ + char *new; + int l; + + if (keynum == -1) + return; + +// free old bindings + if (keybindings[keynum]) + { + Z_Free (keybindings[keynum]); + keybindings[keynum] = NULL; + } + +// allocate memory for new binding + l = strlen (binding); + new = Z_Malloc (l+1); + strcpy (new, binding); + new[l] = 0; + keybindings[keynum] = new; +} + +/* +=================== +Key_Unbind_f +=================== +*/ +void Key_Unbind_f (void) +{ + int b; + + if (Cmd_Argc() != 2) + { + Con_Printf ("unbind : remove commands from a key\n"); + return; + } + + b = Key_StringToKeynum (Cmd_Argv(1)); + if (b==-1) + { + Con_Printf ("\"%s\" isn't a valid key\n", Cmd_Argv(1)); + return; + } + + Key_SetBinding (b, ""); +} + +void Key_Unbindall_f (void) +{ + int i; + + for (i=0 ; i<256 ; i++) + if (keybindings[i]) + Key_SetBinding (i, ""); +} + + +/* +=================== +Key_Bind_f +=================== +*/ +void Key_Bind_f (void) +{ + int i, c, b; + char cmd[1024]; + + c = Cmd_Argc(); + + if (c < 2) + { + Con_Printf ("bind [command] : attach a command to a key\n"); + return; + } + b = Key_StringToKeynum (Cmd_Argv(1)); + if (b==-1) + { + Con_Printf ("\"%s\" isn't a valid key\n", Cmd_Argv(1)); + return; + } + + if (c == 2) + { + if (keybindings[b]) + Con_Printf ("\"%s\" = \"%s\"\n", Cmd_Argv(1), keybindings[b] ); + else + Con_Printf ("\"%s\" is not bound\n", Cmd_Argv(1) ); + return; + } + +// copy the rest of the command line + cmd[0] = 0; // start out with a null string + for (i=2 ; i< c ; i++) + { + strcat (cmd, Cmd_Argv(i)); + if (i != (c-1)) + strcat (cmd, " "); + } + + Key_SetBinding (b, cmd); +} + +/* +============ +Key_WriteBindings + +Writes lines containing "bind key value" +============ +*/ +void Key_WriteBindings (FILE *f) +{ + int i; + + for (i=0 ; i<256 ; i++) + if (keybindings[i]) + fprintf (f, "bind %s \"%s\"\n", Key_KeynumToString(i), keybindings[i]); +} + + +/* +=================== +Key_Init +=================== +*/ +void Key_Init (void) +{ + int i; + + for (i=0 ; i<32 ; i++) + { + key_lines[i][0] = ']'; + key_lines[i][1] = 0; + } + key_linepos = 1; + +// +// init ascii characters in console mode +// + for (i=32 ; i<128 ; i++) + consolekeys[i] = true; + consolekeys[K_ENTER] = true; + consolekeys[K_TAB] = true; + consolekeys[K_LEFTARROW] = true; + consolekeys[K_RIGHTARROW] = true; + consolekeys[K_UPARROW] = true; + consolekeys[K_DOWNARROW] = true; + consolekeys[K_BACKSPACE] = true; + consolekeys[K_INS] = true; + consolekeys[K_DEL] = true; + consolekeys[K_HOME] = true; + consolekeys[K_END] = true; + consolekeys[K_PGUP] = true; + consolekeys[K_PGDN] = true; + consolekeys[K_ALT] = true; + consolekeys[K_CTRL] = true; + consolekeys[K_SHIFT] = true; + consolekeys[K_MWHEELUP] = true; + consolekeys[K_MWHEELDOWN] = true; + consolekeys['`'] = false; + consolekeys['~'] = false; + + for (i=0 ; i<256 ; i++) + keyshift[i] = i; + for (i='a' ; i<='z' ; i++) + keyshift[i] = i - 'a' + 'A'; + keyshift['1'] = '!'; + keyshift['2'] = '@'; + keyshift['3'] = '#'; + keyshift['4'] = '$'; + keyshift['5'] = '%'; + keyshift['6'] = '^'; + keyshift['7'] = '&'; + keyshift['8'] = '*'; + keyshift['9'] = '('; + keyshift['0'] = ')'; + keyshift['-'] = '_'; + keyshift['='] = '+'; + keyshift[','] = '<'; + keyshift['.'] = '>'; + keyshift['/'] = '?'; + keyshift[';'] = ':'; + keyshift['\''] = '"'; + keyshift['['] = '{'; + keyshift[']'] = '}'; + keyshift['`'] = '~'; + keyshift['\\'] = '|'; + + menubound[K_ESCAPE] = true; + for (i=0 ; i<12 ; i++) + menubound[K_F1+i] = true; + +// +// register our functions +// + Cmd_AddCommand ("bind",Key_Bind_f); + Cmd_AddCommand ("unbind",Key_Unbind_f); + Cmd_AddCommand ("unbindall",Key_Unbindall_f); + + Cvar_RegisterVariable (&cl_chatmode); +} + +/* +=================== +Key_Event + +Called by the system between frames for both key up and key down events +Should NOT be called during an interrupt! +=================== +*/ +void Key_Event (int key, qboolean down) +{ + char *kb; + char cmd[1024]; + +// Con_Printf ("%i : %i\n", key, down); //@@@ + + keydown[key] = down; + + if (!down) + key_repeats[key] = 0; + + key_lastpress = key; + key_count++; + if (key_count <= 0) + { + return; // just catching keys for Con_NotifyBox + } + +// update auto-repeat status + if (down) + { + key_repeats[key]++; + if (key_repeats[key] > 1) + { + if ((key != K_BACKSPACE && key != K_DEL + && key != K_LEFTARROW && key != K_RIGHTARROW + && key != K_UPARROW && key != K_DOWNARROW + && key != K_PGUP && key != K_PGDN && (key < 32 || key > 126 || key == '`')) + || (key_dest == key_game && cls.state == ca_active)) + return; // ignore most autorepeats + } + + if (key >= 200 && !keybindings[key]) + Con_Printf ("%s is unbound, hit F4 to set.\n", Key_KeynumToString (key) ); + } + +// +// handle escape specialy, so the user can never unbind it +// + if (key == K_ESCAPE) + { + if (!down) + return; + switch (key_dest) + { + case key_message: + Key_Message (key); + break; + case key_menu: + M_Keydown (key); + break; + case key_game: + case key_console: + M_ToggleMenu_f (); + break; + default: + Sys_Error ("Bad key_dest"); + } + return; + } + +// +// key up events only generate commands if the game key binding is +// a button command (leading + sign). These will occur even in console mode, +// to keep the character from continuing an action started before a console +// switch. Button commands include the kenum as a parameter, so multiple +// downs can be matched with ups +// + if (!down) + { + kb = keybindings[key]; + if (kb && kb[0] == '+') + { + sprintf (cmd, "-%s %i\n", kb+1, key); + Cbuf_AddText (cmd); + } + if (keyshift[key] != key) + { + kb = keybindings[keyshift[key]]; + if (kb && kb[0] == '+') + { + sprintf (cmd, "-%s %i\n", kb+1, key); + Cbuf_AddText (cmd); + } + } + return; + } + +// +// during demo playback, most keys bring up the main menu +// + if (cls.demoplayback && !cls.demoplayback2 && down && consolekeys[key] && key_dest == key_game + && key != K_ALT && key != K_CTRL && key != K_SHIFT + && key != K_INS && key != K_DEL && key != K_HOME + && key != K_END && key != K_TAB) + { + M_ToggleMenu_f (); + return; + } + +// +// if not a consolekey, send to the interpreter no matter what mode is +// + if ( (key_dest == key_menu && menubound[key]) + || ((key_dest == key_console || key_dest == key_message) + && !consolekeys[key]) + || (key_dest == key_game && ( cls.state == ca_active || !consolekeys[key] ) ) ) + { + kb = keybindings[key]; + if (kb) + { + if (kb[0] == '+') + { // button commands add keynum as a parm + sprintf (cmd, "%s %i\n", kb, key); + Cbuf_AddText (cmd); + } + else + { + Cbuf_AddText (kb); + Cbuf_AddText ("\n"); + } + } + return; + } + + if (!down) + return; // other systems only care about key down events + + if (keydown[K_SHIFT]) + key = keyshift[key]; + + switch (key_dest) + { + case key_message: + Key_Message (key); + break; + case key_menu: + M_Keydown (key); + break; + + case key_game: + case key_console: + Key_Console (key); + break; + default: + Sys_Error ("Bad key_dest"); + } +} + +/* +=================== +Key_ClearStates +=================== +*/ +void Key_ClearStates (void) +{ + int i; + + for (i=0 ; i<256 ; i++) + { + keydown[i] = false; + key_repeats[i] = false; + } +} + diff --git a/source/keys.h b/source/keys.h index f9f94c7c..0ed58914 100644 --- a/source/keys.h +++ b/source/keys.h @@ -1,168 +1,168 @@ -/* -Copyright (C) 1996-1997 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. - -*/ - -// these are the key numbers that should be passed to Key_Event - -typedef enum { - K_TAB = 9, - K_ENTER = 13, - K_ESCAPE = 27, - K_SPACE = 32, - -// normal keys should be passed as lowercased ascii - - K_BACKSPACE = 127, - - K_CAPSLOCK, - K_PRINTSCR, - K_SCRLCK, - K_PAUSE, - - K_UPARROW, - K_DOWNARROW, - K_LEFTARROW, - K_RIGHTARROW, - - K_ALT, - K_CTRL, - K_SHIFT, - K_F1, - K_F2, - K_F3, - K_F4, - K_F5, - K_F6, - K_F7, - K_F8, - K_F9, - K_F10, - K_F11, - K_F12, - K_INS, - K_DEL, - K_PGDN, - K_PGUP, - K_HOME, - K_END, - -// -// Keypad stuff.. -// - - KP_NUMLOCK, - KP_SLASH, - KP_STAR, - - KP_HOME, - KP_UPARROW, - KP_PGUP, - KP_MINUS, - - KP_LEFTARROW, - KP_5, - KP_RIGHTARROW, - KP_PLUS, - - KP_END, - KP_DOWNARROW, - KP_PGDN, - - KP_INS, - KP_DEL, - KP_ENTER, - -// -// mouse buttons generate virtual keys -// - K_MOUSE1 = 200, - K_MOUSE2, - K_MOUSE3, - -// -// joystick buttons -// - K_JOY1, - K_JOY2, - K_JOY3, - K_JOY4, - -// -// aux keys are for multi-buttoned joysticks to generate so they can use -// the normal binding process -// - K_AUX1, - K_AUX2, - K_AUX3, - K_AUX4, - K_AUX5, - K_AUX6, - K_AUX7, - K_AUX8, - K_AUX9, - K_AUX10, - K_AUX11, - K_AUX12, - K_AUX13, - K_AUX14, - K_AUX15, - K_AUX16, - K_AUX17, - K_AUX18, - K_AUX19, - K_AUX20, - K_AUX21, - K_AUX22, - K_AUX23, - K_AUX24, - K_AUX25, - K_AUX26, - K_AUX27, - K_AUX28, - K_AUX29, - K_AUX30, - K_AUX31, - K_AUX32, - -// JACK: Intellimouse(c) Mouse Wheel Support - - K_MWHEELUP, - K_MWHEELDOWN -} keynum_t; - - -typedef enum {key_game, key_console, key_message, key_menu} keydest_t; - -extern keydest_t key_dest; -extern char *keybindings[256]; -extern int key_repeats[256]; -extern qboolean keydown[256]; -extern int key_count; // incremented every key event -extern int key_lastpress; - -extern char chat_buffer[]; -extern int chat_linepos; -extern qboolean chat_team; - -void Key_Event (int key, qboolean down); -void Key_Init (void); -void Key_WriteBindings (FILE *f); -void Key_SetBinding (int keynum, char *binding); -void Key_ClearStates (void); - +/* +Copyright (C) 1996-1997 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. + +*/ + +// these are the key numbers that should be passed to Key_Event + +typedef enum { + K_TAB = 9, + K_ENTER = 13, + K_ESCAPE = 27, + K_SPACE = 32, + +// normal keys should be passed as lowercased ascii + + K_BACKSPACE = 127, + + K_CAPSLOCK, + K_PRINTSCR, + K_SCRLCK, + K_PAUSE, + + K_UPARROW, + K_DOWNARROW, + K_LEFTARROW, + K_RIGHTARROW, + + K_ALT, + K_CTRL, + K_SHIFT, + K_F1, + K_F2, + K_F3, + K_F4, + K_F5, + K_F6, + K_F7, + K_F8, + K_F9, + K_F10, + K_F11, + K_F12, + K_INS, + K_DEL, + K_PGDN, + K_PGUP, + K_HOME, + K_END, + +// +// Keypad stuff.. +// + + KP_NUMLOCK, + KP_SLASH, + KP_STAR, + + KP_HOME, + KP_UPARROW, + KP_PGUP, + KP_MINUS, + + KP_LEFTARROW, + KP_5, + KP_RIGHTARROW, + KP_PLUS, + + KP_END, + KP_DOWNARROW, + KP_PGDN, + + KP_INS, + KP_DEL, + KP_ENTER, + +// +// mouse buttons generate virtual keys +// + K_MOUSE1 = 200, + K_MOUSE2, + K_MOUSE3, + +// +// joystick buttons +// + K_JOY1, + K_JOY2, + K_JOY3, + K_JOY4, + +// +// aux keys are for multi-buttoned joysticks to generate so they can use +// the normal binding process +// + K_AUX1, + K_AUX2, + K_AUX3, + K_AUX4, + K_AUX5, + K_AUX6, + K_AUX7, + K_AUX8, + K_AUX9, + K_AUX10, + K_AUX11, + K_AUX12, + K_AUX13, + K_AUX14, + K_AUX15, + K_AUX16, + K_AUX17, + K_AUX18, + K_AUX19, + K_AUX20, + K_AUX21, + K_AUX22, + K_AUX23, + K_AUX24, + K_AUX25, + K_AUX26, + K_AUX27, + K_AUX28, + K_AUX29, + K_AUX30, + K_AUX31, + K_AUX32, + +// JACK: Intellimouse(c) Mouse Wheel Support + + K_MWHEELUP, + K_MWHEELDOWN +} keynum_t; + + +typedef enum {key_game, key_console, key_message, key_menu} keydest_t; + +extern keydest_t key_dest; +extern char *keybindings[256]; +extern int key_repeats[256]; +extern qboolean keydown[256]; +extern int key_count; // incremented every key event +extern int key_lastpress; + +extern char chat_buffer[]; +extern int chat_linepos; +extern qboolean chat_team; + +void Key_Event (int key, qboolean down); +void Key_Init (void); +void Key_WriteBindings (FILE *f); +void Key_SetBinding (int keynum, char *binding); +void Key_ClearStates (void); + diff --git a/source/math.s b/source/math.s index 420fc83f..e7887f26 100644 --- a/source/math.s +++ b/source/math.s @@ -1,357 +1,357 @@ -/* - math.s - - x86 assembly language math routines - - Copyright (C) 1996-1997 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: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - - $Id: math.s,v 1.1.1.4 2004/10/13 18:54:38 vvd0 Exp $ -*/ - -#include "asm_i386.h" -#include "quakeasm.h" - - -#ifdef id386 - - .data - - .align 4 -Ljmptab: .long Lcase0, Lcase1, Lcase2, Lcase3 - .long Lcase4, Lcase5, Lcase6, Lcase7 - - .text - - .extern C(BOPS_Error) - - -#define EMINS 4+4 -#define EMAXS 4+8 -#define P 4+12 - - .align 2 -.globl C(BoxOnPlaneSide) -C(BoxOnPlaneSide): - pushl %ebx - - movl P(%esp),%edx - movl EMINS(%esp),%ecx - xorl %eax,%eax - movl EMAXS(%esp),%ebx - movb pl_signbits(%edx),%al - cmpb $8,%al - jge Lerror - flds pl_normal(%edx) // p->normal[0] - fld %st(0) // p->normal[0] | p->normal[0] - jmp *Ljmptab(,%eax,4) - - -//dist1= p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; -//dist2= p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; -Lcase0: - fmuls (%ebx) // p->normal[0]*emaxs[0] | p->normal[0] - flds pl_normal+4(%edx) // p->normal[1] | p->normal[0]*emaxs[0] | - // p->normal[0] - fxch %st(2) // p->normal[0] | p->normal[0]*emaxs[0] | - // p->normal[1] - fmuls (%ecx) // p->normal[0]*emins[0] | - // p->normal[0]*emaxs[0] | p->normal[1] - fxch %st(2) // p->normal[1] | p->normal[0]*emaxs[0] | - // p->normal[0]*emins[0] - fld %st(0) // p->normal[1] | p->normal[1] | - // p->normal[0]*emaxs[0] | - // p->normal[0]*emins[0] - fmuls 4(%ebx) // p->normal[1]*emaxs[1] | p->normal[1] | - // p->normal[0]*emaxs[0] | - // p->normal[0]*emins[0] - flds pl_normal+8(%edx) // p->normal[2] | p->normal[1]*emaxs[1] | - // p->normal[1] | p->normal[0]*emaxs[0] | - // p->normal[0]*emins[0] - fxch %st(2) // p->normal[1] | p->normal[1]*emaxs[1] | - // p->normal[2] | p->normal[0]*emaxs[0] | - // p->normal[0]*emins[0] - fmuls 4(%ecx) // p->normal[1]*emins[1] | - // p->normal[1]*emaxs[1] | - // p->normal[2] | p->normal[0]*emaxs[0] | - // p->normal[0]*emins[0] - fxch %st(2) // p->normal[2] | p->normal[1]*emaxs[1] | - // p->normal[1]*emins[1] | - // p->normal[0]*emaxs[0] | - // p->normal[0]*emins[0] - fld %st(0) // p->normal[2] | p->normal[2] | - // p->normal[1]*emaxs[1] | - // p->normal[1]*emins[1] | - // p->normal[0]*emaxs[0] | - // p->normal[0]*emins[0] - fmuls 8(%ebx) // p->normal[2]*emaxs[2] | - // p->normal[2] | - // p->normal[1]*emaxs[1] | - // p->normal[1]*emins[1] | - // p->normal[0]*emaxs[0] | - // p->normal[0]*emins[0] - fxch %st(5) // p->normal[0]*emins[0] | - // p->normal[2] | - // p->normal[1]*emaxs[1] | - // p->normal[1]*emins[1] | - // p->normal[0]*emaxs[0] | - // p->normal[2]*emaxs[2] - faddp %st(0),%st(3) //p->normal[2] | - // p->normal[1]*emaxs[1] | - // p->normal[1]*emins[1]+p->normal[0]*emins[0]| - // p->normal[0]*emaxs[0] | - // p->normal[2]*emaxs[2] - fmuls 8(%ecx) //p->normal[2]*emins[2] | - // p->normal[1]*emaxs[1] | - // p->normal[1]*emins[1]+p->normal[0]*emins[0]| - // p->normal[0]*emaxs[0] | - // p->normal[2]*emaxs[2] - fxch %st(1) //p->normal[1]*emaxs[1] | - // p->normal[2]*emins[2] | - // p->normal[1]*emins[1]+p->normal[0]*emins[0]| - // p->normal[0]*emaxs[0] | - // p->normal[2]*emaxs[2] - faddp %st(0),%st(3) //p->normal[2]*emins[2] | - // p->normal[1]*emins[1]+p->normal[0]*emins[0]| - // p->normal[0]*emaxs[0]+p->normal[1]*emaxs[1]| - // p->normal[2]*emaxs[2] - fxch %st(3) //p->normal[2]*emaxs[2] + - // p->normal[1]*emins[1]+p->normal[0]*emins[0]| - // p->normal[0]*emaxs[0]+p->normal[1]*emaxs[1]| - // p->normal[2]*emins[2] - faddp %st(0),%st(2) //p->normal[1]*emins[1]+p->normal[0]*emins[0]| - // dist1 | p->normal[2]*emins[2] - - jmp LSetSides - -//dist1= p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; -//dist2= p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; -Lcase1: - fmuls (%ecx) // emins[0] - flds pl_normal+4(%edx) - fxch %st(2) - fmuls (%ebx) // emaxs[0] - fxch %st(2) - fld %st(0) - fmuls 4(%ebx) // emaxs[1] - flds pl_normal+8(%edx) - fxch %st(2) - fmuls 4(%ecx) // emins[1] - fxch %st(2) - fld %st(0) - fmuls 8(%ebx) // emaxs[2] - fxch %st(5) - faddp %st(0),%st(3) - fmuls 8(%ecx) // emins[2] - fxch %st(1) - faddp %st(0),%st(3) - fxch %st(3) - faddp %st(0),%st(2) - - jmp LSetSides - -//dist1= p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; -//dist2= p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; -Lcase2: - fmuls (%ebx) // emaxs[0] - flds pl_normal+4(%edx) - fxch %st(2) - fmuls (%ecx) // emins[0] - fxch %st(2) - fld %st(0) - fmuls 4(%ecx) // emins[1] - flds pl_normal+8(%edx) - fxch %st(2) - fmuls 4(%ebx) // emaxs[1] - fxch %st(2) - fld %st(0) - fmuls 8(%ebx) // emaxs[2] - fxch %st(5) - faddp %st(0),%st(3) - fmuls 8(%ecx) // emins[2] - fxch %st(1) - faddp %st(0),%st(3) - fxch %st(3) - faddp %st(0),%st(2) - - jmp LSetSides - -//dist1= p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; -//dist2= p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; -Lcase3: - fmuls (%ecx) // emins[0] - flds pl_normal+4(%edx) - fxch %st(2) - fmuls (%ebx) // emaxs[0] - fxch %st(2) - fld %st(0) - fmuls 4(%ecx) // emins[1] - flds pl_normal+8(%edx) - fxch %st(2) - fmuls 4(%ebx) // emaxs[1] - fxch %st(2) - fld %st(0) - fmuls 8(%ebx) // emaxs[2] - fxch %st(5) - faddp %st(0),%st(3) - fmuls 8(%ecx) // emins[2] - fxch %st(1) - faddp %st(0),%st(3) - fxch %st(3) - faddp %st(0),%st(2) - - jmp LSetSides - -//dist1= p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; -//dist2= p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; -Lcase4: - fmuls (%ebx) // emaxs[0] - flds pl_normal+4(%edx) - fxch %st(2) - fmuls (%ecx) // emins[0] - fxch %st(2) - fld %st(0) - fmuls 4(%ebx) // emaxs[1] - flds pl_normal+8(%edx) - fxch %st(2) - fmuls 4(%ecx) // emins[1] - fxch %st(2) - fld %st(0) - fmuls 8(%ecx) // emins[2] - fxch %st(5) - faddp %st(0),%st(3) - fmuls 8(%ebx) // emaxs[2] - fxch %st(1) - faddp %st(0),%st(3) - fxch %st(3) - faddp %st(0),%st(2) - - jmp LSetSides - -//dist1= p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; -//dist2= p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; -Lcase5: - fmuls (%ecx) // emins[0] - flds pl_normal+4(%edx) - fxch %st(2) - fmuls (%ebx) // emaxs[0] - fxch %st(2) - fld %st(0) - fmuls 4(%ebx) // emaxs[1] - flds pl_normal+8(%edx) - fxch %st(2) - fmuls 4(%ecx) // emins[1] - fxch %st(2) - fld %st(0) - fmuls 8(%ecx) // emins[2] - fxch %st(5) - faddp %st(0),%st(3) - fmuls 8(%ebx) // emaxs[2] - fxch %st(1) - faddp %st(0),%st(3) - fxch %st(3) - faddp %st(0),%st(2) - - jmp LSetSides - -//dist1= p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; -//dist2= p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; -Lcase6: - fmuls (%ebx) // emaxs[0] - flds pl_normal+4(%edx) - fxch %st(2) - fmuls (%ecx) // emins[0] - fxch %st(2) - fld %st(0) - fmuls 4(%ecx) // emins[1] - flds pl_normal+8(%edx) - fxch %st(2) - fmuls 4(%ebx) // emaxs[1] - fxch %st(2) - fld %st(0) - fmuls 8(%ecx) // emins[2] - fxch %st(5) - faddp %st(0),%st(3) - fmuls 8(%ebx) // emaxs[2] - fxch %st(1) - faddp %st(0),%st(3) - fxch %st(3) - faddp %st(0),%st(2) - - jmp LSetSides - -//dist1= p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; -//dist2= p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; -Lcase7: - fmuls (%ecx) // emins[0] - flds pl_normal+4(%edx) - fxch %st(2) - fmuls (%ebx) // emaxs[0] - fxch %st(2) - fld %st(0) - fmuls 4(%ecx) // emins[1] - flds pl_normal+8(%edx) - fxch %st(2) - fmuls 4(%ebx) // emaxs[1] - fxch %st(2) - fld %st(0) - fmuls 8(%ecx) // emins[2] - fxch %st(5) - faddp %st(0),%st(3) - fmuls 8(%ebx) // emaxs[2] - fxch %st(1) - faddp %st(0),%st(3) - fxch %st(3) - faddp %st(0),%st(2) - -LSetSides: - -// sides = 0; -// if (dist1 >= p->dist) -// sides = 1; -// if (dist2 < p->dist) -// sides |= 2; - - faddp %st(0),%st(2) // dist1 | dist2 - fcomps pl_dist(%edx) - xorl %ecx,%ecx - fnstsw %ax - fcomps pl_dist(%edx) - andb $1,%ah - xorb $1,%ah - addb %ah,%cl - - fnstsw %ax - andb $1,%ah - addb %ah,%ah - addb %ah,%cl - -// return sides; - - popl %ebx - movl %ecx,%eax // return status - - ret - - -Lerror: - call C(BOPS_Error) - -#endif // id386 +/* + math.s + + x86 assembly language math routines + + Copyright (C) 1996-1997 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id: math.s,v 1.1.1.5 2004/10/18 17:44:32 vvd0 Exp $ +*/ + +#include "asm_i386.h" +#include "quakeasm.h" + + +#ifdef id386 + + .data + + .align 4 +Ljmptab: .long Lcase0, Lcase1, Lcase2, Lcase3 + .long Lcase4, Lcase5, Lcase6, Lcase7 + + .text + + .extern C(BOPS_Error) + + +#define EMINS 4+4 +#define EMAXS 4+8 +#define P 4+12 + + .align 2 +.globl C(BoxOnPlaneSide) +C(BoxOnPlaneSide): + pushl %ebx + + movl P(%esp),%edx + movl EMINS(%esp),%ecx + xorl %eax,%eax + movl EMAXS(%esp),%ebx + movb pl_signbits(%edx),%al + cmpb $8,%al + jge Lerror + flds pl_normal(%edx) // p->normal[0] + fld %st(0) // p->normal[0] | p->normal[0] + jmp *Ljmptab(,%eax,4) + + +//dist1= p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; +//dist2= p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; +Lcase0: + fmuls (%ebx) // p->normal[0]*emaxs[0] | p->normal[0] + flds pl_normal+4(%edx) // p->normal[1] | p->normal[0]*emaxs[0] | + // p->normal[0] + fxch %st(2) // p->normal[0] | p->normal[0]*emaxs[0] | + // p->normal[1] + fmuls (%ecx) // p->normal[0]*emins[0] | + // p->normal[0]*emaxs[0] | p->normal[1] + fxch %st(2) // p->normal[1] | p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fld %st(0) // p->normal[1] | p->normal[1] | + // p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fmuls 4(%ebx) // p->normal[1]*emaxs[1] | p->normal[1] | + // p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + flds pl_normal+8(%edx) // p->normal[2] | p->normal[1]*emaxs[1] | + // p->normal[1] | p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fxch %st(2) // p->normal[1] | p->normal[1]*emaxs[1] | + // p->normal[2] | p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fmuls 4(%ecx) // p->normal[1]*emins[1] | + // p->normal[1]*emaxs[1] | + // p->normal[2] | p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fxch %st(2) // p->normal[2] | p->normal[1]*emaxs[1] | + // p->normal[1]*emins[1] | + // p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fld %st(0) // p->normal[2] | p->normal[2] | + // p->normal[1]*emaxs[1] | + // p->normal[1]*emins[1] | + // p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fmuls 8(%ebx) // p->normal[2]*emaxs[2] | + // p->normal[2] | + // p->normal[1]*emaxs[1] | + // p->normal[1]*emins[1] | + // p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fxch %st(5) // p->normal[0]*emins[0] | + // p->normal[2] | + // p->normal[1]*emaxs[1] | + // p->normal[1]*emins[1] | + // p->normal[0]*emaxs[0] | + // p->normal[2]*emaxs[2] + faddp %st(0),%st(3) //p->normal[2] | + // p->normal[1]*emaxs[1] | + // p->normal[1]*emins[1]+p->normal[0]*emins[0]| + // p->normal[0]*emaxs[0] | + // p->normal[2]*emaxs[2] + fmuls 8(%ecx) //p->normal[2]*emins[2] | + // p->normal[1]*emaxs[1] | + // p->normal[1]*emins[1]+p->normal[0]*emins[0]| + // p->normal[0]*emaxs[0] | + // p->normal[2]*emaxs[2] + fxch %st(1) //p->normal[1]*emaxs[1] | + // p->normal[2]*emins[2] | + // p->normal[1]*emins[1]+p->normal[0]*emins[0]| + // p->normal[0]*emaxs[0] | + // p->normal[2]*emaxs[2] + faddp %st(0),%st(3) //p->normal[2]*emins[2] | + // p->normal[1]*emins[1]+p->normal[0]*emins[0]| + // p->normal[0]*emaxs[0]+p->normal[1]*emaxs[1]| + // p->normal[2]*emaxs[2] + fxch %st(3) //p->normal[2]*emaxs[2] + + // p->normal[1]*emins[1]+p->normal[0]*emins[0]| + // p->normal[0]*emaxs[0]+p->normal[1]*emaxs[1]| + // p->normal[2]*emins[2] + faddp %st(0),%st(2) //p->normal[1]*emins[1]+p->normal[0]*emins[0]| + // dist1 | p->normal[2]*emins[2] + + jmp LSetSides + +//dist1= p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; +//dist2= p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; +Lcase1: + fmuls (%ecx) // emins[0] + flds pl_normal+4(%edx) + fxch %st(2) + fmuls (%ebx) // emaxs[0] + fxch %st(2) + fld %st(0) + fmuls 4(%ebx) // emaxs[1] + flds pl_normal+8(%edx) + fxch %st(2) + fmuls 4(%ecx) // emins[1] + fxch %st(2) + fld %st(0) + fmuls 8(%ebx) // emaxs[2] + fxch %st(5) + faddp %st(0),%st(3) + fmuls 8(%ecx) // emins[2] + fxch %st(1) + faddp %st(0),%st(3) + fxch %st(3) + faddp %st(0),%st(2) + + jmp LSetSides + +//dist1= p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; +//dist2= p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; +Lcase2: + fmuls (%ebx) // emaxs[0] + flds pl_normal+4(%edx) + fxch %st(2) + fmuls (%ecx) // emins[0] + fxch %st(2) + fld %st(0) + fmuls 4(%ecx) // emins[1] + flds pl_normal+8(%edx) + fxch %st(2) + fmuls 4(%ebx) // emaxs[1] + fxch %st(2) + fld %st(0) + fmuls 8(%ebx) // emaxs[2] + fxch %st(5) + faddp %st(0),%st(3) + fmuls 8(%ecx) // emins[2] + fxch %st(1) + faddp %st(0),%st(3) + fxch %st(3) + faddp %st(0),%st(2) + + jmp LSetSides + +//dist1= p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; +//dist2= p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; +Lcase3: + fmuls (%ecx) // emins[0] + flds pl_normal+4(%edx) + fxch %st(2) + fmuls (%ebx) // emaxs[0] + fxch %st(2) + fld %st(0) + fmuls 4(%ecx) // emins[1] + flds pl_normal+8(%edx) + fxch %st(2) + fmuls 4(%ebx) // emaxs[1] + fxch %st(2) + fld %st(0) + fmuls 8(%ebx) // emaxs[2] + fxch %st(5) + faddp %st(0),%st(3) + fmuls 8(%ecx) // emins[2] + fxch %st(1) + faddp %st(0),%st(3) + fxch %st(3) + faddp %st(0),%st(2) + + jmp LSetSides + +//dist1= p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; +//dist2= p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; +Lcase4: + fmuls (%ebx) // emaxs[0] + flds pl_normal+4(%edx) + fxch %st(2) + fmuls (%ecx) // emins[0] + fxch %st(2) + fld %st(0) + fmuls 4(%ebx) // emaxs[1] + flds pl_normal+8(%edx) + fxch %st(2) + fmuls 4(%ecx) // emins[1] + fxch %st(2) + fld %st(0) + fmuls 8(%ecx) // emins[2] + fxch %st(5) + faddp %st(0),%st(3) + fmuls 8(%ebx) // emaxs[2] + fxch %st(1) + faddp %st(0),%st(3) + fxch %st(3) + faddp %st(0),%st(2) + + jmp LSetSides + +//dist1= p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; +//dist2= p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; +Lcase5: + fmuls (%ecx) // emins[0] + flds pl_normal+4(%edx) + fxch %st(2) + fmuls (%ebx) // emaxs[0] + fxch %st(2) + fld %st(0) + fmuls 4(%ebx) // emaxs[1] + flds pl_normal+8(%edx) + fxch %st(2) + fmuls 4(%ecx) // emins[1] + fxch %st(2) + fld %st(0) + fmuls 8(%ecx) // emins[2] + fxch %st(5) + faddp %st(0),%st(3) + fmuls 8(%ebx) // emaxs[2] + fxch %st(1) + faddp %st(0),%st(3) + fxch %st(3) + faddp %st(0),%st(2) + + jmp LSetSides + +//dist1= p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; +//dist2= p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; +Lcase6: + fmuls (%ebx) // emaxs[0] + flds pl_normal+4(%edx) + fxch %st(2) + fmuls (%ecx) // emins[0] + fxch %st(2) + fld %st(0) + fmuls 4(%ecx) // emins[1] + flds pl_normal+8(%edx) + fxch %st(2) + fmuls 4(%ebx) // emaxs[1] + fxch %st(2) + fld %st(0) + fmuls 8(%ecx) // emins[2] + fxch %st(5) + faddp %st(0),%st(3) + fmuls 8(%ebx) // emaxs[2] + fxch %st(1) + faddp %st(0),%st(3) + fxch %st(3) + faddp %st(0),%st(2) + + jmp LSetSides + +//dist1= p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; +//dist2= p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; +Lcase7: + fmuls (%ecx) // emins[0] + flds pl_normal+4(%edx) + fxch %st(2) + fmuls (%ebx) // emaxs[0] + fxch %st(2) + fld %st(0) + fmuls 4(%ecx) // emins[1] + flds pl_normal+8(%edx) + fxch %st(2) + fmuls 4(%ebx) // emaxs[1] + fxch %st(2) + fld %st(0) + fmuls 8(%ecx) // emins[2] + fxch %st(5) + faddp %st(0),%st(3) + fmuls 8(%ebx) // emaxs[2] + fxch %st(1) + faddp %st(0),%st(3) + fxch %st(3) + faddp %st(0),%st(2) + +LSetSides: + +// sides = 0; +// if (dist1 >= p->dist) +// sides = 1; +// if (dist2 < p->dist) +// sides |= 2; + + faddp %st(0),%st(2) // dist1 | dist2 + fcomps pl_dist(%edx) + xorl %ecx,%ecx + fnstsw %ax + fcomps pl_dist(%edx) + andb $1,%ah + xorb $1,%ah + addb %ah,%cl + + fnstsw %ax + andb $1,%ah + addb %ah,%ah + addb %ah,%cl + +// return sides; + + popl %ebx + movl %ecx,%eax // return status + + ret + + +Lerror: + call C(BOPS_Error) + +#endif // id386 diff --git a/source/mathlib.c b/source/mathlib.c index 383021ec..cebdaf53 100644 --- a/source/mathlib.c +++ b/source/mathlib.c @@ -1,583 +1,583 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// mathlib.c -- math primitives - -#include -#include "quakedef.h" - -void Sys_Error (char *error, ...); - -vec3_t vec3_origin = {0,0,0}; -int nanmask = 255<<23; - -/*-----------------------------------------------------------------*/ - -#define DEG2RAD( a ) ( a * M_PI ) / 180.0F - -void ProjectPointOnPlane( vec3_t dst, const vec3_t p, const vec3_t normal ) -{ - float d; - vec3_t n; - float inv_denom; - - inv_denom = 1.0F / DotProduct( normal, normal ); - - d = DotProduct( normal, p ) * inv_denom; - - n[0] = normal[0] * inv_denom; - n[1] = normal[1] * inv_denom; - n[2] = normal[2] * inv_denom; - - dst[0] = p[0] - d * n[0]; - dst[1] = p[1] - d * n[1]; - dst[2] = p[2] - d * n[2]; -} - -/* -** assumes "src" is normalized -*/ -void PerpendicularVector( vec3_t dst, const vec3_t src ) -{ - int pos; - int i; - float minelem = 1.0F; - vec3_t tempvec; - - /* - ** find the smallest magnitude axially aligned vector - */ - for ( pos = 0, i = 0; i < 3; i++ ) - { - if ( fabs( src[i] ) < minelem ) - { - pos = i; - minelem = fabs( src[i] ); - } - } - tempvec[0] = tempvec[1] = tempvec[2] = 0.0F; - tempvec[pos] = 1.0F; - - /* - ** project the point onto the plane defined by src - */ - ProjectPointOnPlane( dst, tempvec, src ); - - /* - ** normalize the result - */ - VectorNormalize( dst ); -} - -#ifdef _WIN32 -#pragma optimize( "", off ) -#endif - - -void RotatePointAroundVector( vec3_t dst, const vec3_t dir, const vec3_t point, float degrees ) -{ - float m[3][3]; - float im[3][3]; - float zrot[3][3]; - float tmpmat[3][3]; - float rot[3][3]; - int i; - vec3_t vr, vup, vf; - - vf[0] = dir[0]; - vf[1] = dir[1]; - vf[2] = dir[2]; - - PerpendicularVector( vr, dir ); - CrossProduct( vr, vf, vup ); - - m[0][0] = vr[0]; - m[1][0] = vr[1]; - m[2][0] = vr[2]; - - m[0][1] = vup[0]; - m[1][1] = vup[1]; - m[2][1] = vup[2]; - - m[0][2] = vf[0]; - m[1][2] = vf[1]; - m[2][2] = vf[2]; - - memcpy( im, m, sizeof( im ) ); - - im[0][1] = m[1][0]; - im[0][2] = m[2][0]; - im[1][0] = m[0][1]; - im[1][2] = m[2][1]; - im[2][0] = m[0][2]; - im[2][1] = m[1][2]; - - memset( zrot, 0, sizeof( zrot ) ); - zrot[0][0] = zrot[1][1] = zrot[2][2] = 1.0F; - - zrot[0][0] = cos( DEG2RAD( degrees ) ); - zrot[0][1] = sin( DEG2RAD( degrees ) ); - zrot[1][0] = -sin( DEG2RAD( degrees ) ); - zrot[1][1] = cos( DEG2RAD( degrees ) ); - - R_ConcatRotations( m, zrot, tmpmat ); - R_ConcatRotations( tmpmat, im, rot ); - - for ( i = 0; i < 3; i++ ) - { - dst[i] = rot[i][0] * point[0] + rot[i][1] * point[1] + rot[i][2] * point[2]; - } -} - -#ifdef _WIN32 -#pragma optimize( "", on ) -#endif - -/*-----------------------------------------------------------------*/ - -float anglemod(float a) -{ -#if 0 - if (a >= 0) - a -= 360*(int)(a/360); - else - a += 360*( 1 + (int)(-a/360) ); -#endif - a = (360.0/65536) * ((int)(a*(65536/360.0)) & 65535); - return a; -} - -/* -================== -BOPS_Error - -Split out like this for ASM to call. -================== -*/ -void BOPS_Error (void) -{ - Sys_Error ("BoxOnPlaneSide: Bad signbits"); -} - -#if !id386 - -/* -================== -BoxOnPlaneSide - -Returns 1, 2, or 1 + 2 -================== -*/ -int BoxOnPlaneSide (vec3_t emins, vec3_t emaxs, mplane_t *p) -{ - float dist1, dist2; - int sides; - -#if 0 // this is done by the BOX_ON_PLANE_SIDE macro before calling this - // function -// fast axial cases - if (p->type < 3) - { - if (p->dist <= emins[p->type]) - return 1; - if (p->dist >= emaxs[p->type]) - return 2; - return 3; - } -#endif - -// general case - switch (p->signbits) - { - case 0: -dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; -dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; - break; - case 1: -dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; -dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; - break; - case 2: -dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; -dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; - break; - case 3: -dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; -dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; - break; - case 4: -dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; -dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; - break; - case 5: -dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; -dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; - break; - case 6: -dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; -dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; - break; - case 7: -dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; -dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; - break; - default: - dist1 = dist2 = 0; // shut up compiler - BOPS_Error (); - break; - } - -#if 0 - int i; - vec3_t corners[2]; - - for (i=0 ; i<3 ; i++) - { - if (plane->normal[i] < 0) - { - corners[0][i] = emins[i]; - corners[1][i] = emaxs[i]; - } - else - { - corners[1][i] = emins[i]; - corners[0][i] = emaxs[i]; - } - } - dist = DotProduct (plane->normal, corners[0]) - plane->dist; - dist2 = DotProduct (plane->normal, corners[1]) - plane->dist; - sides = 0; - if (dist1 >= 0) - sides = 1; - if (dist2 < 0) - sides |= 2; - -#endif - - sides = 0; - if (dist1 >= p->dist) - sides = 1; - if (dist2 < p->dist) - sides |= 2; - -#ifdef PARANOID -if (sides == 0) - Sys_Error ("BoxOnPlaneSide: sides==0"); -#endif - - return sides; -} - -#endif - - -void AngleVectors (vec3_t angles, vec3_t forward, vec3_t right, vec3_t up) -{ - float angle; - float sr, sp, sy, cr, cp, cy; - - angle = angles[YAW] * (M_PI*2 / 360); - sy = sin(angle); - cy = cos(angle); - angle = angles[PITCH] * (M_PI*2 / 360); - sp = sin(angle); - cp = cos(angle); - angle = angles[ROLL] * (M_PI*2 / 360); - sr = sin(angle); - cr = cos(angle); - - forward[0] = cp*cy; - forward[1] = cp*sy; - forward[2] = -sp; - right[0] = (-1*sr*sp*cy+-1*cr*-sy); - right[1] = (-1*sr*sp*sy+-1*cr*cy); - right[2] = -1*sr*cp; - up[0] = (cr*sp*cy+-sr*-sy); - up[1] = (cr*sp*sy+-sr*cy); - up[2] = cr*cp; -} - -int VectorCompare (vec3_t v1, vec3_t v2) -{ - int i; - - for (i=0 ; i<3 ; i++) - if (v1[i] != v2[i]) - return 0; - - return 1; -} - -void VectorMA (vec3_t veca, float scale, vec3_t vecb, vec3_t vecc) -{ - vecc[0] = veca[0] + scale*vecb[0]; - vecc[1] = veca[1] + scale*vecb[1]; - vecc[2] = veca[2] + scale*vecb[2]; -} - - -vec_t _DotProduct (vec3_t v1, vec3_t v2) -{ - return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2]; -} - -void _VectorSubtract (vec3_t veca, vec3_t vecb, vec3_t out) -{ - out[0] = veca[0]-vecb[0]; - out[1] = veca[1]-vecb[1]; - out[2] = veca[2]-vecb[2]; -} - -void _VectorAdd (vec3_t veca, vec3_t vecb, vec3_t out) -{ - out[0] = veca[0]+vecb[0]; - out[1] = veca[1]+vecb[1]; - out[2] = veca[2]+vecb[2]; -} - -void _VectorCopy (vec3_t in, vec3_t out) -{ - out[0] = in[0]; - out[1] = in[1]; - out[2] = in[2]; -} - -void CrossProduct (vec3_t v1, vec3_t v2, vec3_t cross) -{ - cross[0] = v1[1]*v2[2] - v1[2]*v2[1]; - cross[1] = v1[2]*v2[0] - v1[0]*v2[2]; - cross[2] = v1[0]*v2[1] - v1[1]*v2[0]; -} - -double sqrt(double x); - -vec_t Length(vec3_t v) -{ - int i; - float length; - - length = 0; - for (i=0 ; i< 3 ; i++) - length += v[i]*v[i]; - length = sqrt (length); // FIXME - - return length; -} - -float VectorNormalize (vec3_t v) -{ - float length, ilength; - - length = v[0]*v[0] + v[1]*v[1] + v[2]*v[2]; - length = sqrt (length); // FIXME - - if (length) - { - ilength = 1/length; - v[0] *= ilength; - v[1] *= ilength; - v[2] *= ilength; - } - - return length; - -} - -void VectorInverse (vec3_t v) -{ - v[0] = -v[0]; - v[1] = -v[1]; - v[2] = -v[2]; -} - -void VectorScale (vec3_t in, vec_t scale, vec3_t out) -{ - out[0] = in[0]*scale; - out[1] = in[1]*scale; - out[2] = in[2]*scale; -} - - -int Q_log2(int val) -{ - int answer=0; - while ((val>>=1) != 0) - answer++; - return answer; -} - - -/* -================ -R_ConcatRotations -================ -*/ -void R_ConcatRotations (float in1[3][3], float in2[3][3], float out[3][3]) -{ - out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + - in1[0][2] * in2[2][0]; - out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + - in1[0][2] * in2[2][1]; - out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] + - in1[0][2] * in2[2][2]; - out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] + - in1[1][2] * in2[2][0]; - out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] + - in1[1][2] * in2[2][1]; - out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] + - in1[1][2] * in2[2][2]; - out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] + - in1[2][2] * in2[2][0]; - out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] + - in1[2][2] * in2[2][1]; - out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] + - in1[2][2] * in2[2][2]; -} - - -/* -================ -R_ConcatTransforms -================ -*/ -void R_ConcatTransforms (float in1[3][4], float in2[3][4], float out[3][4]) -{ - out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + - in1[0][2] * in2[2][0]; - out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + - in1[0][2] * in2[2][1]; - out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] + - in1[0][2] * in2[2][2]; - out[0][3] = in1[0][0] * in2[0][3] + in1[0][1] * in2[1][3] + - in1[0][2] * in2[2][3] + in1[0][3]; - out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] + - in1[1][2] * in2[2][0]; - out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] + - in1[1][2] * in2[2][1]; - out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] + - in1[1][2] * in2[2][2]; - out[1][3] = in1[1][0] * in2[0][3] + in1[1][1] * in2[1][3] + - in1[1][2] * in2[2][3] + in1[1][3]; - out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] + - in1[2][2] * in2[2][0]; - out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] + - in1[2][2] * in2[2][1]; - out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] + - in1[2][2] * in2[2][2]; - out[2][3] = in1[2][0] * in2[0][3] + in1[2][1] * in2[1][3] + - in1[2][2] * in2[2][3] + in1[2][3]; -} - - -/* -=================== -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. -==================== -*/ - -void FloorDivMod (double numer, double denom, int *quotient, - int *rem) -{ - int q, r; - double x; - -#ifndef PARANOID - if (denom <= 0.0) - Sys_Error ("FloorDivMod: bad denominator %d\n", denom); - -// if ((floor(numer) != numer) || (floor(denom) != denom)) -// Sys_Error ("FloorDivMod: non-integer numer or denom %f %f\n", -// numer, denom); -#endif - - 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; -} - - -/* -=================== -GreatestCommonDivisor -==================== -*/ -int GreatestCommonDivisor (int i1, int i2) -{ - if (i1 > i2) - { - if (i2 == 0) - return (i1); - return GreatestCommonDivisor (i2, i1 % i2); - } - else - { - if (i1 == 0) - return (i2); - return GreatestCommonDivisor (i1, i2 % i1); - } -} - - -#if !id386 - -// TODO: move to nonintel.c - -/* -=================== -Invert24To16 - -Inverts an 8.24 value to a 16.16 value -==================== -*/ - -fixed16_t Invert24To16(fixed16_t val) -{ - if (val < 256) - return (0xFFFFFFFF); - - return (fixed16_t) - (((double)0x10000 * (double)0x1000000 / (double)val) + 0.5); -} - -#endif +/* +Copyright (C) 1996-1997 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. + +*/ +// mathlib.c -- math primitives + +#include +#include "quakedef.h" + +void Sys_Error (char *error, ...); + +vec3_t vec3_origin = {0,0,0}; +int nanmask = 255<<23; + +/*-----------------------------------------------------------------*/ + +#define DEG2RAD( a ) ( a * M_PI ) / 180.0F + +void ProjectPointOnPlane( vec3_t dst, const vec3_t p, const vec3_t normal ) +{ + float d; + vec3_t n; + float inv_denom; + + inv_denom = 1.0F / DotProduct( normal, normal ); + + d = DotProduct( normal, p ) * inv_denom; + + n[0] = normal[0] * inv_denom; + n[1] = normal[1] * inv_denom; + n[2] = normal[2] * inv_denom; + + dst[0] = p[0] - d * n[0]; + dst[1] = p[1] - d * n[1]; + dst[2] = p[2] - d * n[2]; +} + +/* +** assumes "src" is normalized +*/ +void PerpendicularVector( vec3_t dst, const vec3_t src ) +{ + int pos; + int i; + float minelem = 1.0F; + vec3_t tempvec; + + /* + ** find the smallest magnitude axially aligned vector + */ + for ( pos = 0, i = 0; i < 3; i++ ) + { + if ( fabs( src[i] ) < minelem ) + { + pos = i; + minelem = fabs( src[i] ); + } + } + tempvec[0] = tempvec[1] = tempvec[2] = 0.0F; + tempvec[pos] = 1.0F; + + /* + ** project the point onto the plane defined by src + */ + ProjectPointOnPlane( dst, tempvec, src ); + + /* + ** normalize the result + */ + VectorNormalize( dst ); +} + +#ifdef _WIN32 +#pragma optimize( "", off ) +#endif + + +void RotatePointAroundVector( vec3_t dst, const vec3_t dir, const vec3_t point, float degrees ) +{ + float m[3][3]; + float im[3][3]; + float zrot[3][3]; + float tmpmat[3][3]; + float rot[3][3]; + int i; + vec3_t vr, vup, vf; + + vf[0] = dir[0]; + vf[1] = dir[1]; + vf[2] = dir[2]; + + PerpendicularVector( vr, dir ); + CrossProduct( vr, vf, vup ); + + m[0][0] = vr[0]; + m[1][0] = vr[1]; + m[2][0] = vr[2]; + + m[0][1] = vup[0]; + m[1][1] = vup[1]; + m[2][1] = vup[2]; + + m[0][2] = vf[0]; + m[1][2] = vf[1]; + m[2][2] = vf[2]; + + memcpy( im, m, sizeof( im ) ); + + im[0][1] = m[1][0]; + im[0][2] = m[2][0]; + im[1][0] = m[0][1]; + im[1][2] = m[2][1]; + im[2][0] = m[0][2]; + im[2][1] = m[1][2]; + + memset( zrot, 0, sizeof( zrot ) ); + zrot[0][0] = zrot[1][1] = zrot[2][2] = 1.0F; + + zrot[0][0] = cos( DEG2RAD( degrees ) ); + zrot[0][1] = sin( DEG2RAD( degrees ) ); + zrot[1][0] = -sin( DEG2RAD( degrees ) ); + zrot[1][1] = cos( DEG2RAD( degrees ) ); + + R_ConcatRotations( m, zrot, tmpmat ); + R_ConcatRotations( tmpmat, im, rot ); + + for ( i = 0; i < 3; i++ ) + { + dst[i] = rot[i][0] * point[0] + rot[i][1] * point[1] + rot[i][2] * point[2]; + } +} + +#ifdef _WIN32 +#pragma optimize( "", on ) +#endif + +/*-----------------------------------------------------------------*/ + +float anglemod(float a) +{ +#if 0 + if (a >= 0) + a -= 360*(int)(a/360); + else + a += 360*( 1 + (int)(-a/360) ); +#endif + a = (360.0/65536) * ((int)(a*(65536/360.0)) & 65535); + return a; +} + +/* +================== +BOPS_Error + +Split out like this for ASM to call. +================== +*/ +void BOPS_Error (void) +{ + Sys_Error ("BoxOnPlaneSide: Bad signbits"); +} + +#if !id386 + +/* +================== +BoxOnPlaneSide + +Returns 1, 2, or 1 + 2 +================== +*/ +int BoxOnPlaneSide (vec3_t emins, vec3_t emaxs, mplane_t *p) +{ + float dist1, dist2; + int sides; + +#if 0 // this is done by the BOX_ON_PLANE_SIDE macro before calling this + // function +// fast axial cases + if (p->type < 3) + { + if (p->dist <= emins[p->type]) + return 1; + if (p->dist >= emaxs[p->type]) + return 2; + return 3; + } +#endif + +// general case + switch (p->signbits) + { + case 0: +dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; +dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; + break; + case 1: +dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; +dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; + break; + case 2: +dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; +dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; + break; + case 3: +dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; +dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; + break; + case 4: +dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; +dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; + break; + case 5: +dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; +dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; + break; + case 6: +dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; +dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; + break; + case 7: +dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; +dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; + break; + default: + dist1 = dist2 = 0; // shut up compiler + BOPS_Error (); + break; + } + +#if 0 + int i; + vec3_t corners[2]; + + for (i=0 ; i<3 ; i++) + { + if (plane->normal[i] < 0) + { + corners[0][i] = emins[i]; + corners[1][i] = emaxs[i]; + } + else + { + corners[1][i] = emins[i]; + corners[0][i] = emaxs[i]; + } + } + dist = DotProduct (plane->normal, corners[0]) - plane->dist; + dist2 = DotProduct (plane->normal, corners[1]) - plane->dist; + sides = 0; + if (dist1 >= 0) + sides = 1; + if (dist2 < 0) + sides |= 2; + +#endif + + sides = 0; + if (dist1 >= p->dist) + sides = 1; + if (dist2 < p->dist) + sides |= 2; + +#ifdef PARANOID +if (sides == 0) + Sys_Error ("BoxOnPlaneSide: sides==0"); +#endif + + return sides; +} + +#endif + + +void AngleVectors (vec3_t angles, vec3_t forward, vec3_t right, vec3_t up) +{ + float angle; + float sr, sp, sy, cr, cp, cy; + + angle = angles[YAW] * (M_PI*2 / 360); + sy = sin(angle); + cy = cos(angle); + angle = angles[PITCH] * (M_PI*2 / 360); + sp = sin(angle); + cp = cos(angle); + angle = angles[ROLL] * (M_PI*2 / 360); + sr = sin(angle); + cr = cos(angle); + + forward[0] = cp*cy; + forward[1] = cp*sy; + forward[2] = -sp; + right[0] = (-1*sr*sp*cy+-1*cr*-sy); + right[1] = (-1*sr*sp*sy+-1*cr*cy); + right[2] = -1*sr*cp; + up[0] = (cr*sp*cy+-sr*-sy); + up[1] = (cr*sp*sy+-sr*cy); + up[2] = cr*cp; +} + +int VectorCompare (vec3_t v1, vec3_t v2) +{ + int i; + + for (i=0 ; i<3 ; i++) + if (v1[i] != v2[i]) + return 0; + + return 1; +} + +void VectorMA (vec3_t veca, float scale, vec3_t vecb, vec3_t vecc) +{ + vecc[0] = veca[0] + scale*vecb[0]; + vecc[1] = veca[1] + scale*vecb[1]; + vecc[2] = veca[2] + scale*vecb[2]; +} + + +vec_t _DotProduct (vec3_t v1, vec3_t v2) +{ + return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2]; +} + +void _VectorSubtract (vec3_t veca, vec3_t vecb, vec3_t out) +{ + out[0] = veca[0]-vecb[0]; + out[1] = veca[1]-vecb[1]; + out[2] = veca[2]-vecb[2]; +} + +void _VectorAdd (vec3_t veca, vec3_t vecb, vec3_t out) +{ + out[0] = veca[0]+vecb[0]; + out[1] = veca[1]+vecb[1]; + out[2] = veca[2]+vecb[2]; +} + +void _VectorCopy (vec3_t in, vec3_t out) +{ + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; +} + +void CrossProduct (vec3_t v1, vec3_t v2, vec3_t cross) +{ + cross[0] = v1[1]*v2[2] - v1[2]*v2[1]; + cross[1] = v1[2]*v2[0] - v1[0]*v2[2]; + cross[2] = v1[0]*v2[1] - v1[1]*v2[0]; +} + +double sqrt(double x); + +vec_t Length(vec3_t v) +{ + int i; + float length; + + length = 0; + for (i=0 ; i< 3 ; i++) + length += v[i]*v[i]; + length = sqrt (length); // FIXME + + return length; +} + +float VectorNormalize (vec3_t v) +{ + float length, ilength; + + length = v[0]*v[0] + v[1]*v[1] + v[2]*v[2]; + length = sqrt (length); // FIXME + + if (length) + { + ilength = 1/length; + v[0] *= ilength; + v[1] *= ilength; + v[2] *= ilength; + } + + return length; + +} + +void VectorInverse (vec3_t v) +{ + v[0] = -v[0]; + v[1] = -v[1]; + v[2] = -v[2]; +} + +void VectorScale (vec3_t in, vec_t scale, vec3_t out) +{ + out[0] = in[0]*scale; + out[1] = in[1]*scale; + out[2] = in[2]*scale; +} + + +int Q_log2(int val) +{ + int answer=0; + while ((val>>=1) != 0) + answer++; + return answer; +} + + +/* +================ +R_ConcatRotations +================ +*/ +void R_ConcatRotations (float in1[3][3], float in2[3][3], float out[3][3]) +{ + out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + + in1[0][2] * in2[2][0]; + out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + + in1[0][2] * in2[2][1]; + out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] + + in1[0][2] * in2[2][2]; + out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] + + in1[1][2] * in2[2][0]; + out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] + + in1[1][2] * in2[2][1]; + out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] + + in1[1][2] * in2[2][2]; + out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] + + in1[2][2] * in2[2][0]; + out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] + + in1[2][2] * in2[2][1]; + out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] + + in1[2][2] * in2[2][2]; +} + + +/* +================ +R_ConcatTransforms +================ +*/ +void R_ConcatTransforms (float in1[3][4], float in2[3][4], float out[3][4]) +{ + out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + + in1[0][2] * in2[2][0]; + out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + + in1[0][2] * in2[2][1]; + out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] + + in1[0][2] * in2[2][2]; + out[0][3] = in1[0][0] * in2[0][3] + in1[0][1] * in2[1][3] + + in1[0][2] * in2[2][3] + in1[0][3]; + out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] + + in1[1][2] * in2[2][0]; + out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] + + in1[1][2] * in2[2][1]; + out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] + + in1[1][2] * in2[2][2]; + out[1][3] = in1[1][0] * in2[0][3] + in1[1][1] * in2[1][3] + + in1[1][2] * in2[2][3] + in1[1][3]; + out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] + + in1[2][2] * in2[2][0]; + out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] + + in1[2][2] * in2[2][1]; + out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] + + in1[2][2] * in2[2][2]; + out[2][3] = in1[2][0] * in2[0][3] + in1[2][1] * in2[1][3] + + in1[2][2] * in2[2][3] + in1[2][3]; +} + + +/* +=================== +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. +==================== +*/ + +void FloorDivMod (double numer, double denom, int *quotient, + int *rem) +{ + int q, r; + double x; + +#ifndef PARANOID + if (denom <= 0.0) + Sys_Error ("FloorDivMod: bad denominator %d\n", denom); + +// if ((floor(numer) != numer) || (floor(denom) != denom)) +// Sys_Error ("FloorDivMod: non-integer numer or denom %f %f\n", +// numer, denom); +#endif + + 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; +} + + +/* +=================== +GreatestCommonDivisor +==================== +*/ +int GreatestCommonDivisor (int i1, int i2) +{ + if (i1 > i2) + { + if (i2 == 0) + return (i1); + return GreatestCommonDivisor (i2, i1 % i2); + } + else + { + if (i1 == 0) + return (i2); + return GreatestCommonDivisor (i1, i2 % i1); + } +} + + +#if !id386 + +// TODO: move to nonintel.c + +/* +=================== +Invert24To16 + +Inverts an 8.24 value to a 16.16 value +==================== +*/ + +fixed16_t Invert24To16(fixed16_t val) +{ + if (val < 256) + return (0xFFFFFFFF); + + return (fixed16_t) + (((double)0x10000 * (double)0x1000000 / (double)val) + 0.5); +} + +#endif diff --git a/source/mathlib.h b/source/mathlib.h index 47ad4478..d00702e4 100644 --- a/source/mathlib.h +++ b/source/mathlib.h @@ -1,96 +1,96 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// mathlib.h - - -#define PITCH 0 // up / down -#define YAW 1 // left / right -#define ROLL 2 // fall over - -typedef float vec_t; -typedef vec_t vec3_t[3]; -typedef vec_t vec5_t[5]; - -typedef int fixed4_t; -typedef int fixed8_t; -typedef int fixed16_t; - -#ifndef M_PI -#define M_PI 3.14159265358979323846 // matches value in gcc v2 math.h -#endif - -struct mplane_s; - -extern vec3_t vec3_origin; -extern int nanmask; - -#define IS_NAN(x) (((*(int *)&x)&nanmask)==nanmask) - -#define DotProduct(x,y) (x[0]*y[0]+x[1]*y[1]+x[2]*y[2]) -#define VectorSubtract(a,b,c) {c[0]=a[0]-b[0];c[1]=a[1]-b[1];c[2]=a[2]-b[2];} -#define VectorAdd(a,b,c) {c[0]=a[0]+b[0];c[1]=a[1]+b[1];c[2]=a[2]+b[2];} -#define VectorCopy(a,b) {b[0]=a[0];b[1]=a[1];b[2]=a[2];} - -void VectorMA (vec3_t veca, float scale, vec3_t vecb, vec3_t vecc); - -vec_t _DotProduct (vec3_t v1, vec3_t v2); -void _VectorSubtract (vec3_t veca, vec3_t vecb, vec3_t out); -void _VectorAdd (vec3_t veca, vec3_t vecb, vec3_t out); -void _VectorCopy (vec3_t in, vec3_t out); - -int VectorCompare (vec3_t v1, vec3_t v2); -vec_t Length (vec3_t v); -void CrossProduct (vec3_t v1, vec3_t v2, vec3_t cross); -float VectorNormalize (vec3_t v); // returns vector length -void VectorInverse (vec3_t v); -void VectorScale (vec3_t in, vec_t scale, vec3_t out); -int Q_log2(int val); - -void R_ConcatRotations (float in1[3][3], float in2[3][3], float out[3][3]); -void R_ConcatTransforms (float in1[3][4], float in2[3][4], float out[3][4]); - -void FloorDivMod (double numer, double denom, int *quotient, - int *rem); -fixed16_t Invert24To16(fixed16_t val); -fixed16_t Mul16_30(fixed16_t multiplier, fixed16_t multiplicand); -int GreatestCommonDivisor (int i1, int i2); - -void AngleVectors (vec3_t angles, vec3_t forward, vec3_t right, vec3_t up); -int BoxOnPlaneSide (vec3_t emins, vec3_t emaxs, struct mplane_s *plane); -float anglemod(float a); - -void RotatePointAroundVector( vec3_t dst, const vec3_t dir, const vec3_t point, float degrees ); - - -#define BOX_ON_PLANE_SIDE(emins, emaxs, p) \ - (((p)->type < 3)? \ - ( \ - ((p)->dist <= (emins)[(p)->type])? \ - 1 \ - : \ - ( \ - ((p)->dist >= (emaxs)[(p)->type])?\ - 2 \ - : \ - 3 \ - ) \ - ) \ - : \ - BoxOnPlaneSide( (emins), (emaxs), (p))) +/* +Copyright (C) 1996-1997 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. + +*/ +// mathlib.h + + +#define PITCH 0 // up / down +#define YAW 1 // left / right +#define ROLL 2 // fall over + +typedef float vec_t; +typedef vec_t vec3_t[3]; +typedef vec_t vec5_t[5]; + +typedef int fixed4_t; +typedef int fixed8_t; +typedef int fixed16_t; + +#ifndef M_PI +#define M_PI 3.14159265358979323846 // matches value in gcc v2 math.h +#endif + +struct mplane_s; + +extern vec3_t vec3_origin; +extern int nanmask; + +#define IS_NAN(x) (((*(int *)&x)&nanmask)==nanmask) + +#define DotProduct(x,y) (x[0]*y[0]+x[1]*y[1]+x[2]*y[2]) +#define VectorSubtract(a,b,c) {c[0]=a[0]-b[0];c[1]=a[1]-b[1];c[2]=a[2]-b[2];} +#define VectorAdd(a,b,c) {c[0]=a[0]+b[0];c[1]=a[1]+b[1];c[2]=a[2]+b[2];} +#define VectorCopy(a,b) {b[0]=a[0];b[1]=a[1];b[2]=a[2];} + +void VectorMA (vec3_t veca, float scale, vec3_t vecb, vec3_t vecc); + +vec_t _DotProduct (vec3_t v1, vec3_t v2); +void _VectorSubtract (vec3_t veca, vec3_t vecb, vec3_t out); +void _VectorAdd (vec3_t veca, vec3_t vecb, vec3_t out); +void _VectorCopy (vec3_t in, vec3_t out); + +int VectorCompare (vec3_t v1, vec3_t v2); +vec_t Length (vec3_t v); +void CrossProduct (vec3_t v1, vec3_t v2, vec3_t cross); +float VectorNormalize (vec3_t v); // returns vector length +void VectorInverse (vec3_t v); +void VectorScale (vec3_t in, vec_t scale, vec3_t out); +int Q_log2(int val); + +void R_ConcatRotations (float in1[3][3], float in2[3][3], float out[3][3]); +void R_ConcatTransforms (float in1[3][4], float in2[3][4], float out[3][4]); + +void FloorDivMod (double numer, double denom, int *quotient, + int *rem); +fixed16_t Invert24To16(fixed16_t val); +fixed16_t Mul16_30(fixed16_t multiplier, fixed16_t multiplicand); +int GreatestCommonDivisor (int i1, int i2); + +void AngleVectors (vec3_t angles, vec3_t forward, vec3_t right, vec3_t up); +int BoxOnPlaneSide (vec3_t emins, vec3_t emaxs, struct mplane_s *plane); +float anglemod(float a); + +void RotatePointAroundVector( vec3_t dst, const vec3_t dir, const vec3_t point, float degrees ); + + +#define BOX_ON_PLANE_SIDE(emins, emaxs, p) \ + (((p)->type < 3)? \ + ( \ + ((p)->dist <= (emins)[(p)->type])? \ + 1 \ + : \ + ( \ + ((p)->dist >= (emaxs)[(p)->type])?\ + 2 \ + : \ + 3 \ + ) \ + ) \ + : \ + BoxOnPlaneSide( (emins), (emaxs), (p))) diff --git a/source/mdfour.c b/source/mdfour.c index f54fbb89..1bc3bbf4 100644 --- a/source/mdfour.c +++ b/source/mdfour.c @@ -1,224 +1,224 @@ -/* - mdfour.c - - An implementation of MD4 designed for use in the samba SMB - authentication protocol - - Copyright (C) 1997-1998 Andrew Tridgell - - 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: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - - $Id: mdfour.c,v 1.1.1.3 2004/10/13 18:54:38 vvd0 Exp $ -*/ - -#include /* XoXus: needed for memset call */ -#include "mdfour.h" - -/* NOTE: This code makes no attempt to be fast! - - It assumes that a int is at least 32 bits long -*/ - -static struct mdfour *m; - -#define F(X,Y,Z) (((X)&(Y)) | ((~(X))&(Z))) -#define G(X,Y,Z) (((X)&(Y)) | ((X)&(Z)) | ((Y)&(Z))) -#define H(X,Y,Z) ((X)^(Y)^(Z)) -#ifdef LARGE_INT32 -#define lshift(x,s) ((((x)<<(s))&0xFFFFFFFF) | (((x)>>(32-(s)))&0xFFFFFFFF)) -#else -#define lshift(x,s) (((x)<<(s)) | ((x)>>(32-(s)))) -#endif - -#define ROUND1(a,b,c,d,k,s) a = lshift(a + F(b,c,d) + X[k], s) -#define ROUND2(a,b,c,d,k,s) a = lshift(a + G(b,c,d) + X[k] + 0x5A827999,s) -#define ROUND3(a,b,c,d,k,s) a = lshift(a + H(b,c,d) + X[k] + 0x6ED9EBA1,s) - -/* this applies md4 to 64 byte chunks */ -static void mdfour64(uint32 *M) -{ - int j; - uint32 AA, BB, CC, DD; - uint32 X[16]; - uint32 A,B,C,D; - - for (j=0;j<16;j++) - X[j] = M[j]; - - A = m->A; B = m->B; C = m->C; D = m->D; - AA = A; BB = B; CC = C; DD = D; - - ROUND1(A,B,C,D, 0, 3); ROUND1(D,A,B,C, 1, 7); - ROUND1(C,D,A,B, 2, 11); ROUND1(B,C,D,A, 3, 19); - ROUND1(A,B,C,D, 4, 3); ROUND1(D,A,B,C, 5, 7); - ROUND1(C,D,A,B, 6, 11); ROUND1(B,C,D,A, 7, 19); - ROUND1(A,B,C,D, 8, 3); ROUND1(D,A,B,C, 9, 7); - ROUND1(C,D,A,B, 10, 11); ROUND1(B,C,D,A, 11, 19); - ROUND1(A,B,C,D, 12, 3); ROUND1(D,A,B,C, 13, 7); - ROUND1(C,D,A,B, 14, 11); ROUND1(B,C,D,A, 15, 19); - - ROUND2(A,B,C,D, 0, 3); ROUND2(D,A,B,C, 4, 5); - ROUND2(C,D,A,B, 8, 9); ROUND2(B,C,D,A, 12, 13); - ROUND2(A,B,C,D, 1, 3); ROUND2(D,A,B,C, 5, 5); - ROUND2(C,D,A,B, 9, 9); ROUND2(B,C,D,A, 13, 13); - ROUND2(A,B,C,D, 2, 3); ROUND2(D,A,B,C, 6, 5); - ROUND2(C,D,A,B, 10, 9); ROUND2(B,C,D,A, 14, 13); - ROUND2(A,B,C,D, 3, 3); ROUND2(D,A,B,C, 7, 5); - ROUND2(C,D,A,B, 11, 9); ROUND2(B,C,D,A, 15, 13); - - ROUND3(A,B,C,D, 0, 3); ROUND3(D,A,B,C, 8, 9); - ROUND3(C,D,A,B, 4, 11); ROUND3(B,C,D,A, 12, 15); - ROUND3(A,B,C,D, 2, 3); ROUND3(D,A,B,C, 10, 9); - ROUND3(C,D,A,B, 6, 11); ROUND3(B,C,D,A, 14, 15); - ROUND3(A,B,C,D, 1, 3); ROUND3(D,A,B,C, 9, 9); - ROUND3(C,D,A,B, 5, 11); ROUND3(B,C,D,A, 13, 15); - ROUND3(A,B,C,D, 3, 3); ROUND3(D,A,B,C, 11, 9); - ROUND3(C,D,A,B, 7, 11); ROUND3(B,C,D,A, 15, 15); - - A += AA; B += BB; C += CC; D += DD; - -#ifdef LARGE_INT32 - A &= 0xFFFFFFFF; B &= 0xFFFFFFFF; - C &= 0xFFFFFFFF; D &= 0xFFFFFFFF; -#endif - - for (j=0;j<16;j++) - X[j] = 0; - - m->A = A; m->B = B; m->C = C; m->D = D; -} - -static void copy64(uint32 *M, unsigned char *in) -{ - int i; - - for (i=0;i<16;i++) - M[i] = (in[i*4+3]<<24) | (in[i*4+2]<<16) | - (in[i*4+1]<<8) | (in[i*4+0]<<0); -} - -static void copy4(unsigned char *out,uint32 x) -{ - out[0] = x&0xFF; - out[1] = (x>>8)&0xFF; - out[2] = (x>>16)&0xFF; - out[3] = (x>>24)&0xFF; -} - -void mdfour_begin(struct mdfour *md) -{ - md->A = 0x67452301; - md->B = 0xefcdab89; - md->C = 0x98badcfe; - md->D = 0x10325476; - md->totalN = 0; -} - - -static void mdfour_tail(unsigned char *in, int n) -{ - unsigned char buf[128]; - uint32 M[16]; - uint32 b; - - m->totalN += n; - - b = m->totalN * 8; - - memset(buf, 0, 128); - if (n) memcpy(buf, in, n); - buf[n] = 0x80; - - if (n <= 55) { - copy4(buf+56, b); - copy64(M, buf); - mdfour64(M); - } else { - copy4(buf+120, b); - copy64(M, buf); - mdfour64(M); - copy64(M, buf+64); - mdfour64(M); - } -} - -void mdfour_update(struct mdfour *md, unsigned char *in, int n) -{ - uint32 M[16]; - - if (n == 0) mdfour_tail(in, n); - - m = md; - - while (n >= 64) { - copy64(M, in); - mdfour64(M); - in += 64; - n -= 64; - m->totalN += 64; - } - - mdfour_tail(in, n); -} - - -void mdfour_result(struct mdfour *md, unsigned char *out) -{ - m = md; - - copy4(out, m->A); - copy4(out+4, m->B); - copy4(out+8, m->C); - copy4(out+12, m->D); -} - - -void mdfour(unsigned char *out, unsigned char *in, int n) -{ - struct mdfour md; - mdfour_begin(&md); - mdfour_update(&md, in, n); - mdfour_result(&md, out); -} - -/////////////////////////////////////////////////////////////// -// MD4-based checksum utility functions -// -// Copyright (C) 2000 Jeff Teunissen -// -// Author: Jeff Teunissen -// Date: 01 Jan 2000 - -unsigned Com_BlockChecksum (void *buffer, int length) -{ - int digest[4]; - unsigned val; - - mdfour ( (unsigned char *) digest, (unsigned char *) buffer, length ); - - val = digest[0] ^ digest[1] ^ digest[2] ^ digest[3]; - - return val; -} - -void Com_BlockFullChecksum (void *buffer, int len, unsigned char *outbuf) -{ - mdfour ( outbuf, (unsigned char *) buffer, len ); -} - +/* + mdfour.c + + An implementation of MD4 designed for use in the samba SMB + authentication protocol + + Copyright (C) 1997-1998 Andrew Tridgell + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id: mdfour.c,v 1.1.1.4 2004/10/18 17:44:32 vvd0 Exp $ +*/ + +#include /* XoXus: needed for memset call */ +#include "mdfour.h" + +/* NOTE: This code makes no attempt to be fast! + + It assumes that a int is at least 32 bits long +*/ + +static struct mdfour *m; + +#define F(X,Y,Z) (((X)&(Y)) | ((~(X))&(Z))) +#define G(X,Y,Z) (((X)&(Y)) | ((X)&(Z)) | ((Y)&(Z))) +#define H(X,Y,Z) ((X)^(Y)^(Z)) +#ifdef LARGE_INT32 +#define lshift(x,s) ((((x)<<(s))&0xFFFFFFFF) | (((x)>>(32-(s)))&0xFFFFFFFF)) +#else +#define lshift(x,s) (((x)<<(s)) | ((x)>>(32-(s)))) +#endif + +#define ROUND1(a,b,c,d,k,s) a = lshift(a + F(b,c,d) + X[k], s) +#define ROUND2(a,b,c,d,k,s) a = lshift(a + G(b,c,d) + X[k] + 0x5A827999,s) +#define ROUND3(a,b,c,d,k,s) a = lshift(a + H(b,c,d) + X[k] + 0x6ED9EBA1,s) + +/* this applies md4 to 64 byte chunks */ +static void mdfour64(uint32 *M) +{ + int j; + uint32 AA, BB, CC, DD; + uint32 X[16]; + uint32 A,B,C,D; + + for (j=0;j<16;j++) + X[j] = M[j]; + + A = m->A; B = m->B; C = m->C; D = m->D; + AA = A; BB = B; CC = C; DD = D; + + ROUND1(A,B,C,D, 0, 3); ROUND1(D,A,B,C, 1, 7); + ROUND1(C,D,A,B, 2, 11); ROUND1(B,C,D,A, 3, 19); + ROUND1(A,B,C,D, 4, 3); ROUND1(D,A,B,C, 5, 7); + ROUND1(C,D,A,B, 6, 11); ROUND1(B,C,D,A, 7, 19); + ROUND1(A,B,C,D, 8, 3); ROUND1(D,A,B,C, 9, 7); + ROUND1(C,D,A,B, 10, 11); ROUND1(B,C,D,A, 11, 19); + ROUND1(A,B,C,D, 12, 3); ROUND1(D,A,B,C, 13, 7); + ROUND1(C,D,A,B, 14, 11); ROUND1(B,C,D,A, 15, 19); + + ROUND2(A,B,C,D, 0, 3); ROUND2(D,A,B,C, 4, 5); + ROUND2(C,D,A,B, 8, 9); ROUND2(B,C,D,A, 12, 13); + ROUND2(A,B,C,D, 1, 3); ROUND2(D,A,B,C, 5, 5); + ROUND2(C,D,A,B, 9, 9); ROUND2(B,C,D,A, 13, 13); + ROUND2(A,B,C,D, 2, 3); ROUND2(D,A,B,C, 6, 5); + ROUND2(C,D,A,B, 10, 9); ROUND2(B,C,D,A, 14, 13); + ROUND2(A,B,C,D, 3, 3); ROUND2(D,A,B,C, 7, 5); + ROUND2(C,D,A,B, 11, 9); ROUND2(B,C,D,A, 15, 13); + + ROUND3(A,B,C,D, 0, 3); ROUND3(D,A,B,C, 8, 9); + ROUND3(C,D,A,B, 4, 11); ROUND3(B,C,D,A, 12, 15); + ROUND3(A,B,C,D, 2, 3); ROUND3(D,A,B,C, 10, 9); + ROUND3(C,D,A,B, 6, 11); ROUND3(B,C,D,A, 14, 15); + ROUND3(A,B,C,D, 1, 3); ROUND3(D,A,B,C, 9, 9); + ROUND3(C,D,A,B, 5, 11); ROUND3(B,C,D,A, 13, 15); + ROUND3(A,B,C,D, 3, 3); ROUND3(D,A,B,C, 11, 9); + ROUND3(C,D,A,B, 7, 11); ROUND3(B,C,D,A, 15, 15); + + A += AA; B += BB; C += CC; D += DD; + +#ifdef LARGE_INT32 + A &= 0xFFFFFFFF; B &= 0xFFFFFFFF; + C &= 0xFFFFFFFF; D &= 0xFFFFFFFF; +#endif + + for (j=0;j<16;j++) + X[j] = 0; + + m->A = A; m->B = B; m->C = C; m->D = D; +} + +static void copy64(uint32 *M, unsigned char *in) +{ + int i; + + for (i=0;i<16;i++) + M[i] = (in[i*4+3]<<24) | (in[i*4+2]<<16) | + (in[i*4+1]<<8) | (in[i*4+0]<<0); +} + +static void copy4(unsigned char *out,uint32 x) +{ + out[0] = x&0xFF; + out[1] = (x>>8)&0xFF; + out[2] = (x>>16)&0xFF; + out[3] = (x>>24)&0xFF; +} + +void mdfour_begin(struct mdfour *md) +{ + md->A = 0x67452301; + md->B = 0xefcdab89; + md->C = 0x98badcfe; + md->D = 0x10325476; + md->totalN = 0; +} + + +static void mdfour_tail(unsigned char *in, int n) +{ + unsigned char buf[128]; + uint32 M[16]; + uint32 b; + + m->totalN += n; + + b = m->totalN * 8; + + memset(buf, 0, 128); + if (n) memcpy(buf, in, n); + buf[n] = 0x80; + + if (n <= 55) { + copy4(buf+56, b); + copy64(M, buf); + mdfour64(M); + } else { + copy4(buf+120, b); + copy64(M, buf); + mdfour64(M); + copy64(M, buf+64); + mdfour64(M); + } +} + +void mdfour_update(struct mdfour *md, unsigned char *in, int n) +{ + uint32 M[16]; + + if (n == 0) mdfour_tail(in, n); + + m = md; + + while (n >= 64) { + copy64(M, in); + mdfour64(M); + in += 64; + n -= 64; + m->totalN += 64; + } + + mdfour_tail(in, n); +} + + +void mdfour_result(struct mdfour *md, unsigned char *out) +{ + m = md; + + copy4(out, m->A); + copy4(out+4, m->B); + copy4(out+8, m->C); + copy4(out+12, m->D); +} + + +void mdfour(unsigned char *out, unsigned char *in, int n) +{ + struct mdfour md; + mdfour_begin(&md); + mdfour_update(&md, in, n); + mdfour_result(&md, out); +} + +/////////////////////////////////////////////////////////////// +// MD4-based checksum utility functions +// +// Copyright (C) 2000 Jeff Teunissen +// +// Author: Jeff Teunissen +// Date: 01 Jan 2000 + +unsigned Com_BlockChecksum (void *buffer, int length) +{ + int digest[4]; + unsigned val; + + mdfour ( (unsigned char *) digest, (unsigned char *) buffer, length ); + + val = digest[0] ^ digest[1] ^ digest[2] ^ digest[3]; + + return val; +} + +void Com_BlockFullChecksum (void *buffer, int len, unsigned char *outbuf) +{ + mdfour ( outbuf, (unsigned char *) buffer, len ); +} + diff --git a/source/mdfour.h b/source/mdfour.h index a84c496a..69ca6f78 100644 --- a/source/mdfour.h +++ b/source/mdfour.h @@ -1,54 +1,54 @@ -/* - mdfour.h - - an implementation of MD4 designed for use in the SMB authentication - protocol - - Copyright (C) Andrew Tridgell 1997-1998 - - 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: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA -*/ - -#ifndef _MDFOUR_H -#define _MDFOUR_H - -#ifndef int32 -#define int32 int -#endif - -#if SIZEOF_INT > 4 -#define LARGE_INT32 -#endif - -#ifndef uint32 -#define uint32 unsigned int32 -#endif - -struct mdfour { - uint32 A, B, C, D; - uint32 totalN; -}; - -void mdfour_begin(struct mdfour *md); // old: MD4Init -void mdfour_update(struct mdfour *md, unsigned char *in, int n); //old: MD4Update -void mdfour_result(struct mdfour *md, unsigned char *out); // old: MD4Final -void mdfour(unsigned char *out, unsigned char *in, int n); - -#endif // _MDFOUR_H - +/* + mdfour.h + + an implementation of MD4 designed for use in the SMB authentication + protocol + + Copyright (C) Andrew Tridgell 1997-1998 + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA +*/ + +#ifndef _MDFOUR_H +#define _MDFOUR_H + +#ifndef int32 +#define int32 int +#endif + +#if SIZEOF_INT > 4 +#define LARGE_INT32 +#endif + +#ifndef uint32 +#define uint32 unsigned int32 +#endif + +struct mdfour { + uint32 A, B, C, D; + uint32 totalN; +}; + +void mdfour_begin(struct mdfour *md); // old: MD4Init +void mdfour_update(struct mdfour *md, unsigned char *in, int n); //old: MD4Update +void mdfour_result(struct mdfour *md, unsigned char *out); // old: MD4Final +void mdfour(unsigned char *out, unsigned char *in, int n); + +#endif // _MDFOUR_H + diff --git a/source/menu.c b/source/menu.c index 9143ebda..4a8c704a 100644 --- a/source/menu.c +++ b/source/menu.c @@ -1,2508 +1,2549 @@ -/* -Copyright (C) 1996-1997 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 "quakedef.h" -#include "winquake.h" -#include "cl_slist.h" -#include "keys.h" -#include "sound.h" -#include "version.h" - -void (*vid_menudrawfn)(void); -void (*vid_menukeyfn)(int key); - -enum {m_none, m_main, m_singleplayer, m_load, m_save, m_multiplayer, - m_setup, m_net, m_options, m_video, m_keys, m_help, m_quit, - m_serialconfig, m_modemconfig, m_lanconfig, m_gameoptions, - m_search, m_slist, m_sedit, m_fps, m_demos} m_state; - -void M_Menu_Main_f (void); - void M_Menu_SinglePlayer_f (void); - void M_Menu_Load_f (void); - void M_Menu_Save_f (void); - void M_Menu_MultiPlayer_f (void); - void M_Menu_ServerList_f (void); - void M_Menu_SEdit_f (void); - void M_Menu_Setup_f (void); - void M_Menu_Demos_f (void); - void M_Menu_GameOptions_f (void); - void M_Menu_Options_f (void); - void M_Menu_Keys_f (void); - void M_Menu_Fps_f (void); - void M_Menu_Video_f (void); - void M_Menu_Help_f (void); - void M_Menu_Quit_f (void); - -void M_Main_Draw (void); - void M_SinglePlayer_Draw (void); - void M_Load_Draw (void); - void M_Save_Draw (void); - void M_MultiPlayer_Draw (void); - void M_ServerList_Draw (void); - void M_SEdit_Draw (void); - void M_Setup_Draw (void); - void M_Demos_Draw (void); - void M_GameOptions_Draw (void); - void M_Options_Draw (void); - void M_Keys_Draw (void); - void M_Fps_Draw (void); - void M_Video_Draw (void); - void M_Help_Draw (void); - void M_Quit_Draw (void); - -void M_Main_Key (int key); - void M_SinglePlayer_Key (int key); - void M_Load_Key (int key); - void M_Save_Key (int key); - void M_MultiPlayer_Key (int key); - void M_ServerList_Key (int key); - void M_SEdit_Key (int key); - void M_Setup_Key (int key); - void M_Demos_Key (int key); - void M_GameOptions_Key (int key); - void M_Options_Key (int key); - void M_Keys_Key (int key); - void M_Fps_Key (int key); - void M_Video_Key (int key); - void M_Help_Key (int key); - void M_Quit_Key (int key); - - -qboolean m_entersound; // play after drawing a frame, so caching - // won't disrupt the sound -qboolean m_recursiveDraw; - -int m_return_state; -qboolean m_return_onerror; -char m_return_reason [32]; - - -//============================================================================= -/* Support Routines */ - -cvar_t scr_centerMenu = {"scr_centerMenu","1"}; -int m_yofs = 0; - -/* -================ -M_DrawCharacter - -Draws one solid graphics character -================ -*/ -void M_DrawCharacter (int cx, int line, int num) -{ - Draw_Character (cx + ((vid.width - 320)>>1), line + m_yofs, num); -} - -void M_Print (int cx, int cy, char *str) -{ - while (*str) - { - M_DrawCharacter (cx, cy, (*str) | 128); - str++; - cx += 8; - } -} - -void M_PrintWhite (int cx, int cy, char *str) -{ - while (*str) - { - M_DrawCharacter (cx, cy, *str); - str++; - cx += 8; - } -} - -void M_DrawTransPic (int x, int y, qpic_t *pic) -{ - Draw_TransPic (x + ((vid.width - 320)>>1), y + m_yofs, pic); -} - -void M_DrawPic (int x, int y, qpic_t *pic) -{ - Draw_Pic (x + ((vid.width - 320)>>1), y + m_yofs, pic); -} - -byte identityTable[256]; -byte translationTable[256]; - -void M_BuildTranslationTable(int top, int bottom) -{ - int j; - byte *dest, *source; - - for (j = 0; j < 256; j++) - identityTable[j] = j; - dest = translationTable; - source = identityTable; - memcpy (dest, source, 256); - - if (top < 128) // the artists made some backwards ranges. sigh. - memcpy (dest + TOP_RANGE, source + top, 16); - else - for (j=0 ; j<16 ; j++) - dest[TOP_RANGE+j] = source[top+15-j]; - - if (bottom < 128) - memcpy (dest + BOTTOM_RANGE, source + bottom, 16); - else - for (j=0 ; j<16 ; j++) - dest[BOTTOM_RANGE+j] = source[bottom+15-j]; -} - - -void M_DrawTransPicTranslate (int x, int y, qpic_t *pic) -{ - Draw_TransPicTranslate (x + ((vid.width - 320)>>1), y + m_yofs, pic, translationTable); -} - - -void M_DrawTextBox (int x, int y, int width, int lines) -{ - qpic_t *p; - int cx, cy; - int n; - - // draw left side - cx = x; - cy = y; - p = Draw_CachePic ("gfx/box_tl.lmp"); - M_DrawTransPic (cx, cy, p); - p = Draw_CachePic ("gfx/box_ml.lmp"); - for (n = 0; n < lines; n++) - { - cy += 8; - M_DrawTransPic (cx, cy, p); - } - p = Draw_CachePic ("gfx/box_bl.lmp"); - M_DrawTransPic (cx, cy+8, p); - - // draw middle - cx += 8; - while (width > 0) - { - cy = y; - p = Draw_CachePic ("gfx/box_tm.lmp"); - M_DrawTransPic (cx, cy, p); - p = Draw_CachePic ("gfx/box_mm.lmp"); - for (n = 0; n < lines; n++) - { - cy += 8; - if (n == 1) - p = Draw_CachePic ("gfx/box_mm2.lmp"); - M_DrawTransPic (cx, cy, p); - } - p = Draw_CachePic ("gfx/box_bm.lmp"); - M_DrawTransPic (cx, cy+8, p); - width -= 2; - cx += 16; - } - - // draw right side - cy = y; - p = Draw_CachePic ("gfx/box_tr.lmp"); - M_DrawTransPic (cx, cy, p); - p = Draw_CachePic ("gfx/box_mr.lmp"); - for (n = 0; n < lines; n++) - { - cy += 8; - M_DrawTransPic (cx, cy, p); - } - p = Draw_CachePic ("gfx/box_br.lmp"); - M_DrawTransPic (cx, cy+8, p); -} - -//============================================================================= - -int m_save_demonum; - -/* -================ -M_ToggleMenu_f -================ -*/ -void M_ToggleMenu_f (void) -{ - m_entersound = true; - - if (key_dest == key_menu) - { - if (m_state != m_main) - { - M_Menu_Main_f (); - return; - } - key_dest = key_game; - m_state = m_none; - return; - } -/* if (key_dest == key_console) - { - Con_ToggleConsole_f (); - }*/ - else - { - // hide console - scr_conlines = 0; - scr_con_current = 0; - M_Menu_Main_f (); - } -} - - -//============================================================================= -/* MAIN MENU */ - -int m_main_cursor; -#define MAIN_ITEMS 5 - - -void M_Menu_Main_f (void) -{ - if (key_dest != key_menu) - { - m_save_demonum = cls.demonum; - cls.demonum = -1; - } - key_dest = key_menu; - m_state = m_main; - m_entersound = true; -} - - -void M_Main_Draw (void) -{ - int f; - qpic_t *p; - - M_DrawTransPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") ); - p = Draw_CachePic ("gfx/ttl_main.lmp"); - M_DrawPic ( (320-p->width)/2, 4, p); - M_DrawTransPic (72, 32, Draw_CachePic ("gfx/mainmenu.lmp") ); - - f = (int)(realtime * 10)%6; - - M_DrawTransPic (54, 32 + m_main_cursor * 20,Draw_CachePic( va("gfx/menudot%i.lmp", f+1 ) ) ); -} - - -void M_Main_Key (int key) -{ - switch (key) - { - case K_ESCAPE: - key_dest = key_game; - m_state = m_none; - cls.demonum = m_save_demonum; - if (cls.demonum != -1 && !cls.demoplayback && cls.state == ca_disconnected) - CL_NextDemo (); - break; - - case K_UPARROW: - S_LocalSound ("misc/menu1.wav"); - if (--m_main_cursor < 0) - m_main_cursor = MAIN_ITEMS - 1; - break; - - case K_DOWNARROW: - S_LocalSound ("misc/menu1.wav"); - if (++m_main_cursor >= MAIN_ITEMS) - m_main_cursor = 0; - break; - - case K_HOME: - case K_PGUP: - S_LocalSound ("misc/menu1.wav"); - m_main_cursor = 0; - break; - - case K_END: - case K_PGDN: - S_LocalSound ("misc/menu1.wav"); - m_main_cursor = MAIN_ITEMS - 1; - break; - - case K_ENTER: - m_entersound = true; - - switch (m_main_cursor) - { - case 0: - M_Menu_SinglePlayer_f (); - break; - - case 1: - M_Menu_MultiPlayer_f (); - break; - - case 2: - M_Menu_Options_f (); - break; - - case 3: - M_Menu_Help_f (); - break; - - case 4: - M_Menu_Quit_f (); - break; - } - } -} - - -//============================================================================= -/* OPTIONS MENU */ - -#define OPTIONS_ITEMS 18 - -#define SLIDER_RANGE 10 - -int options_cursor; - -#ifdef GLQUAKE -#define gamma gl_gamma -#define contrast gl_contrast -#else -#define gamma v_gamma -#define contrast v_contrast -#endif - -void M_Menu_Options_f (void) -{ - key_dest = key_menu; - m_state = m_options; - m_entersound = true; -} - - -void M_AdjustSliders (int dir) -{ - S_LocalSound ("misc/menu3.wav"); - - switch (options_cursor) - { - case 3: // screen size - scr_viewsize.value += dir * 10; - if (scr_viewsize.value < 30) - scr_viewsize.value = 30; - if (scr_viewsize.value > 120) - scr_viewsize.value = 120; - Cvar_SetValue (&scr_viewsize, scr_viewsize.value); - break; - case 4: // gamma - gamma.value -= dir * 0.05; - if (gamma.value < 0.5) - gamma.value = 0.5; - if (gamma.value > 1) - gamma.value = 1; - Cvar_SetValue (&gamma, gamma.value); - break; - case 5: // contrast - contrast.value += dir * 0.1; - if (contrast.value < 1) - contrast.value = 1; - if (contrast.value > 2) - contrast.value = 2; - Cvar_SetValue (&contrast, contrast.value); - break; - case 6: // mouse speed - sensitivity.value += dir * 0.5; - if (sensitivity.value < 1) - sensitivity.value = 1; - if (sensitivity.value > 11) - sensitivity.value = 11; - Cvar_SetValue (&sensitivity, sensitivity.value); - break; - case 7: // music volume -#ifdef _WIN32 - bgmvolume.value += dir * 1.0; -#else - bgmvolume.value += dir * 0.1; -#endif - if (bgmvolume.value < 0) - bgmvolume.value = 0; - if (bgmvolume.value > 1) - bgmvolume.value = 1; - Cvar_SetValue (&bgmvolume, bgmvolume.value); - break; - case 8: // sfx volume - volume.value += dir * 0.1; - if (volume.value < 0) - volume.value = 0; - if (volume.value > 1) - volume.value = 1; - Cvar_SetValue (&volume, volume.value); - break; - - case 9: // always run - if (cl_forwardspeed.value > 200) - { - Cvar_SetValue (&cl_forwardspeed, 200); - Cvar_SetValue (&cl_backspeed, 200); - } - else - { - Cvar_SetValue (&cl_forwardspeed, 400); - Cvar_SetValue (&cl_backspeed, 400); - } - break; - - case 10: // invert mouse - Cvar_SetValue (&m_pitch, -m_pitch.value); - break; - - case 11: // lookspring - Cvar_SetValue (&lookspring, !lookspring.value); - break; - - case 12: // lookstrafe - Cvar_SetValue (&lookstrafe, !lookstrafe.value); - break; - - case 13: - Cvar_SetValue (&cl_sbar, !cl_sbar.value); - break; - - case 14: - Cvar_SetValue (&cl_hudswap, !cl_hudswap.value); - break; - - case 17: // _windowed_mouse - Cvar_SetValue (&_windowed_mouse, !_windowed_mouse.value); - break; - } -} - - -void M_DrawSlider (int x, int y, float range) -{ - int i; - - if (range < 0) - range = 0; - if (range > 1) - range = 1; - M_DrawCharacter (x-8, y, 128); - for (i=0 ; iwidth)/2, 4, p); - - M_Print (16, 32, " Customize controls"); - M_Print (16, 40, " Go to console"); - M_Print (16, 48, " Reset to defaults"); - - M_Print (16, 56, " Screen size"); - r = (scr_viewsize.value - 30) / (120 - 30); - M_DrawSlider (220, 56, r); - - M_Print (16, 64, " Gamma"); - r = (1.0 - gamma.value) / 0.5; - M_DrawSlider (220, 64, r); - - M_Print (16, 72, " Contrast"); - r = contrast.value - 1.0; - M_DrawSlider (220, 72, r); - - M_Print (16, 80, " Mouse Speed"); - r = (sensitivity.value - 1)/10; - M_DrawSlider (220, 80, r); - - M_Print (16, 88, " CD Music Volume"); - r = bgmvolume.value; - M_DrawSlider (220, 88, r); - - M_Print (16, 96, " Sound Volume"); - r = volume.value; - M_DrawSlider (220, 96, r); - - M_Print (16, 104, " Always Run"); - M_DrawCheckbox (220, 104, cl_forwardspeed.value > 200); - - M_Print (16, 112, " Invert Mouse"); - M_DrawCheckbox (220, 112, m_pitch.value < 0); - - M_Print (16, 120, " Lookspring"); - M_DrawCheckbox (220, 120, lookspring.value); - - M_Print (16, 128, " Lookstrafe"); - M_DrawCheckbox (220, 128, lookstrafe.value); - - M_Print (16, 136, " Use old status bar"); - M_DrawCheckbox (220, 136, cl_sbar.value); - - M_Print (16, 144, " HUD on left side"); - M_DrawCheckbox (220, 144, cl_hudswap.value); - - M_Print (16, 152, " FPS settings"); - - if (vid_menudrawfn) - M_Print (16, 160, " Video Modes"); - -#ifdef _WIN32 - if (modestate == MS_WINDOWED) - { -#endif - M_Print (16, 168, " Use Mouse"); - M_DrawCheckbox (220, 168, _windowed_mouse.value); -#ifdef _WIN32 - } -#endif - -// cursor - M_DrawCharacter (200, 32 + options_cursor*8, 12+((int)(realtime*4)&1)); -} - - -void M_Options_Key (int k) -{ - switch (k) - { - case K_ESCAPE: - M_Menu_Main_f (); - break; - - case K_ENTER: - m_entersound = true; - switch (options_cursor) - { - case 0: - M_Menu_Keys_f (); - break; - case 1: - m_state = m_none; - key_dest = key_console; -// Con_ToggleConsole_f (); - break; - case 2: - Cbuf_AddText ("exec default.cfg\n"); - break; - case 15: - M_Menu_Fps_f (); - break; - case 16: - if (vid_menudrawfn) - M_Menu_Video_f (); - break; - default: - M_AdjustSliders (1); - break; - } - return; - - case K_UPARROW: - S_LocalSound ("misc/menu1.wav"); - options_cursor--; - if (options_cursor < 0) - options_cursor = OPTIONS_ITEMS-1; - break; - - case K_DOWNARROW: - S_LocalSound ("misc/menu1.wav"); - options_cursor++; - if (options_cursor >= OPTIONS_ITEMS) - options_cursor = 0; - break; - - case K_HOME: - case K_PGUP: - S_LocalSound ("misc/menu1.wav"); - options_cursor = 0; - break; - - case K_END: - case K_PGDN: - S_LocalSound ("misc/menu1.wav"); - options_cursor = OPTIONS_ITEMS-1; - break; - - case K_LEFTARROW: - M_AdjustSliders (-1); - break; - - case K_RIGHTARROW: - M_AdjustSliders (1); - break; - } - - if (options_cursor == 16 && vid_menudrawfn == NULL) - { - if (k == K_UPARROW || k == K_END || k == K_PGDN) - options_cursor = 15; - else - options_cursor = 0; - } - - if ((options_cursor == 17) -#ifdef _WIN32 - && (modestate != MS_WINDOWED) -#endif - ) - { - if (k == K_UPARROW || k == K_END || k == K_PGDN) - options_cursor = 16; - else - options_cursor = 0; - } -} - - -//============================================================================= -/* KEYS MENU */ - -char *bindnames[][2] = -{ -{"+attack", "attack"}, -{"+jump", "jump"}, -{"+forward", "walk forward"}, -{"+back", "backpedal"}, -{"+moveleft", "move left"}, -{"+moveright", "move right"}, -{"+moveup", "swim up"}, -{"+movedown", "swim down"}, -{"impulse 10", "change weapon"}, -{"+speed", "run"}, -{"+strafe", "sidestep"}, -{"+left", "turn left"}, -{"+right", "turn right"}, -{"+lookup", "look up"}, -{"+lookdown", "look down"}, -{"centerview", "center view"}, -{"+mlook", "mouse look"}, -}; - -#define NUMCOMMANDS (sizeof(bindnames)/sizeof(bindnames[0])) - -int keys_cursor; -int bind_grab; - -void M_Menu_Keys_f (void) -{ - key_dest = key_menu; - m_state = m_keys; - m_entersound = true; -} - - -void M_FindKeysForCommand (char *command, int *twokeys) -{ - int count; - int j; - int l; - char *b; - - twokeys[0] = twokeys[1] = -1; - l = strlen(command); - count = 0; - - for (j=0 ; j<256 ; j++) - { - b = keybindings[j]; - if (!b) - continue; - if (!strncmp (b, command, l) ) - { - twokeys[count] = j; - count++; - if (count == 2) - break; - } - } -} - -void M_UnbindCommand (char *command) -{ - int j; - int l; - char *b; - - l = strlen(command); - - for (j=0 ; j<256 ; j++) - { - b = keybindings[j]; - if (!b) - continue; - if (!strncmp (b, command, l) ) - Key_SetBinding (j, ""); - } -} - - -void M_Keys_Draw (void) -{ - int i, l; - int keys[2]; - char *name; - int x, y; - qpic_t *p; - - p = Draw_CachePic ("gfx/ttl_cstm.lmp"); - M_DrawPic ( (320-p->width)/2, 4, p); - - if (bind_grab) - M_Print (12, 32, "Press a key or button for this action"); - else - M_Print (18, 32, "Enter to change, del to clear"); - -// search for known bindings - for (i=0 ; i= NUMCOMMANDS) - keys_cursor = 0; - break; - - case K_HOME: - case K_PGUP: - S_LocalSound ("misc/menu1.wav"); - keys_cursor = 0; - break; - - case K_END: - case K_PGDN: - S_LocalSound ("misc/menu1.wav"); - keys_cursor = NUMCOMMANDS - 1; - break; - - case K_ENTER: // go into bind mode - M_FindKeysForCommand (bindnames[keys_cursor][0], keys); - S_LocalSound ("misc/menu2.wav"); - if (keys[1] != -1) - M_UnbindCommand (bindnames[keys_cursor][0]); - bind_grab = true; - break; - - case K_DEL: // delete bindings - S_LocalSound ("misc/menu2.wav"); - M_UnbindCommand (bindnames[keys_cursor][0]); - break; - } -} - - -//============================================================================= -/* FPS SETTINGS MENU */ - -#ifdef GLQUAKE -#define FPS_ITEMS 11 -#else -#define FPS_ITEMS 12 -#endif -int fps_cursor = 0; - -extern cvar_t v_bonusflash; -extern cvar_t cl_rocket2grenade; -extern cvar_t v_damagecshift; -extern cvar_t r_fastsky; -extern cvar_t r_drawflame; - -void M_Menu_Fps_f (void) -{ - key_dest = key_menu; - m_state = m_fps; - m_entersound = true; -} - -void M_Fps_Draw (void) -{ - qpic_t *p; - - M_DrawTransPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") ); - p = Draw_CachePic ("gfx/ttl_cstm.lmp"); - M_DrawPic ( (320-p->width)/2, 4, p); - - M_Print (16, 32, " Explosions"); - M_Print (220, 32, cl_explosion.value==0 ? "normal" : - cl_explosion.value==1 ? "type 1" : cl_explosion.value==2 ? "type 2" : - cl_explosion.value==3 ? "type 3" : ""); - - M_Print (16, 40, " Muzzleflashes"); - M_Print (220, 40, cl_muzzleflash.value==2 ? "own off" : - cl_muzzleflash.value ? "on" : "off"); - - M_Print (16, 48, " Gib filter"); - M_DrawCheckbox (220, 48, cl_gibfilter.value); - - M_Print (16, 56, " Dead bodies filter"); - M_DrawCheckbox (220, 56, cl_deadbodyfilter.value); - - M_Print (16, 64, " Rocket model"); - M_Print (220, 64, cl_rocket2grenade.value ? "grenade" : "normal"); - - M_Print (16, 72, " Rocket trail"); - M_Print (220, 72, r_rockettrail.value==2 ? "grenade" : - r_rockettrail.value ? "normal" : "off"); - - M_Print (16, 80, " Rocket light"); - M_DrawCheckbox (220, 80, r_rocketlight.value); - - M_Print (16, 88, " Damage filter"); - M_DrawCheckbox (220, 88, v_damagecshift.value == 0); - - M_Print (16, 96, " Pickup flashes"); - M_DrawCheckbox (220, 96, v_bonusflash.value); - - M_Print (16, 104, " Powerup glow"); - M_Print (220, 104, r_powerupglow.value==2 ? "own off" : - r_powerupglow.value ? "on" : "off"); - - M_Print (16, 112, " Draw torches"); - M_DrawCheckbox (220, 112, r_drawflame.value); - -#ifndef GLQUAKE - M_Print (16, 120, " Fast sky"); - M_DrawCheckbox (220, 120, r_fastsky.value); -#endif - -// cursor - M_DrawCharacter (200, 32 + fps_cursor*8, 12+((int)(realtime*4)&1)); -} - -void M_Fps_Key (int k) -{ - int i; - - switch (k) - { - case K_ESCAPE: - M_Menu_Options_f (); - break; - - case K_UPARROW: - S_LocalSound ("misc/menu1.wav"); - fps_cursor--; - if (fps_cursor < 0) - fps_cursor = FPS_ITEMS - 1; - break; - - case K_DOWNARROW: - S_LocalSound ("misc/menu1.wav"); - fps_cursor++; - if (fps_cursor >= FPS_ITEMS) - fps_cursor = 0; - break; - - case K_HOME: - case K_PGUP: - S_LocalSound ("misc/menu1.wav"); - fps_cursor = 0; - break; - - case K_END: - case K_PGDN: - S_LocalSound ("misc/menu1.wav"); - fps_cursor = FPS_ITEMS - 1; - break; - - case K_RIGHTARROW: - case K_ENTER: - S_LocalSound ("misc/menu2.wav"); - switch (fps_cursor) { - case 0: - i = cl_explosion.value + 1; - if (i > 3 || i < 0) - i = 0; - Cvar_SetValue (&cl_explosion, i); - break; - case 1: - Cvar_SetValue (&cl_muzzleflash, cl_muzzleflash.value==2 ? 1 : - cl_muzzleflash.value ? 0 : 2); - break; - case 2: - Cvar_SetValue (&cl_gibfilter, !cl_gibfilter.value); - break; - case 3: - Cvar_SetValue (&cl_deadbodyfilter, !cl_deadbodyfilter.value); - break; - case 4: - Cvar_SetValue (&cl_rocket2grenade, !cl_rocket2grenade.value); - break; - case 5: - i = r_rockettrail.value + 1; - if (i < 0 || i > 2) - i = 0; - Cvar_SetValue (&r_rockettrail, i); - break; - case 6: - Cvar_SetValue (&r_rocketlight, !r_rocketlight.value); - break; - case 7: - Cvar_SetValue (&v_damagecshift, !v_damagecshift.value); - break; - case 8: - Cvar_SetValue (&v_bonusflash, !v_bonusflash.value); - break; - case 9: - i = r_powerupglow.value + 1; - if (i < 0 || i > 2) - i = 0; - Cvar_SetValue (&r_powerupglow, i); - break; - case 10: - Cvar_SetValue (&r_drawflame, !r_drawflame.value); - break; -#ifndef GLQUAKE - case 11: - Cvar_SetValue (&r_fastsky, !r_fastsky.value); - break; -#endif - } - break; - - } -} - - -//============================================================================= -/* VIDEO MENU */ - -void M_Menu_Video_f (void) -{ - key_dest = key_menu; - m_state = m_video; - m_entersound = true; -} - - -void M_Video_Draw (void) -{ - (*vid_menudrawfn) (); -} - - -void M_Video_Key (int key) -{ - (*vid_menukeyfn) (key); -} - -//============================================================================= -/* HELP MENU */ - -int help_page; -#define NUM_HELP_PAGES 6 - - -void M_Menu_Help_f (void) -{ - key_dest = key_menu; - m_state = m_help; - m_entersound = true; - help_page = 0; -} - - - -void M_Help_Draw (void) -{ - M_DrawPic (0, 0, Draw_CachePic ( va("gfx/help%i.lmp", help_page)) ); -} - - -void M_Help_Key (int key) -{ - switch (key) - { - case K_ESCAPE: - M_Menu_Main_f (); - break; - - case K_UPARROW: - case K_RIGHTARROW: - m_entersound = true; - if (++help_page >= NUM_HELP_PAGES) - help_page = 0; - break; - - case K_DOWNARROW: - case K_LEFTARROW: - m_entersound = true; - if (--help_page < 0) - help_page = NUM_HELP_PAGES-1; - break; - } - -} - -//============================================================================= -/* QUIT MENU */ - -int msgNumber; -int m_quit_prevstate; -qboolean wasInMenus; - -void M_Menu_Quit_f (void) -{ - if (m_state == m_quit) - return; - wasInMenus = (key_dest == key_menu); - key_dest = key_menu; - m_quit_prevstate = m_state; - m_state = m_quit; - m_entersound = true; - msgNumber = rand()&7; -} - - -void M_Quit_Key (int key) -{ - switch (key) - { - case K_ESCAPE: - case 'n': - case 'N': - if (wasInMenus) - { - m_state = m_quit_prevstate; - m_entersound = true; - } - else - { - key_dest = key_game; - m_state = m_none; - } - break; - - case 'Y': - case 'y': - key_dest = key_console; - CL_Disconnect (); - Sys_Quit (); - break; - - default: - break; - } - -} - -//============================================================================= -/* SINGLE PLAYER MENU */ - -void M_Menu_SinglePlayer_f (void) -{ - m_state = m_singleplayer; -} - -void M_SinglePlayer_Draw (void) -{ - qpic_t *p; - - M_DrawTransPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") ); - p = Draw_CachePic ("gfx/ttl_sgl.lmp"); - M_DrawPic ( (320-p->width)/2, 4, p); -// M_DrawTransPic (72, 32, Draw_CachePic ("gfx/sp_menu.lmp") ); - - M_DrawTextBox (60, 10*8, 23, 4); - M_PrintWhite (88, 12*8, "This client is for"); - M_PrintWhite (88, 13*8, "Internet play only"); -} - -void M_SinglePlayer_Key (key) -{ - if (key == K_ESCAPE || key == K_ENTER) - m_state = m_main; -} - - - -//============================================================================= -/* LOAD/SAVE MENU */ - -/* -int load_cursor; // 0 < load_cursor < MAX_SAVEGAMES - -#define MAX_SAVEGAMES 12 -char m_filenames[MAX_SAVEGAMES][SAVEGAME_COMMENT_LENGTH+1]; -int loadable[MAX_SAVEGAMES]; - -void M_ScanSaves (void) -{ - int i, j; - char name[MAX_OSPATH]; - FILE *f; - int version; - - for (i=0 ; iwidth)/2, 4, p); - -/* for (i=0 ; i< MAX_SAVEGAMES; i++) - M_Print (16, 32 + 8*i, m_filenames[i]); - -// line cursor - M_DrawCharacter (8, 32 + load_cursor*8, 12+((int)(realtime*4)&1)); -*/ - /* - - M_DrawTextBox (60, 10*8, 23, 4); - M_PrintWhite (80, 12*8, "Savegames are not yet"); - M_PrintWhite (88, 13*8, "supported by ZQuake"); - */ -} - - -void M_Save_Draw (void) -{ -// int i; - qpic_t *p; - - p = Draw_CachePic ("gfx/p_save.lmp"); - M_DrawPic ( (320-p->width)/2, 4, p); - -/* for (i=0 ; iwidth)/2, 4, p); - M_Print (80, 40, "favorite servers"); - M_Print (80, 48, "player setup"); - M_Print (80, 56, "demos"); - -// cursor - M_DrawCharacter (64, 40 + m_multiplayer_cursor*8, 12+((int)(realtime*4)&1)); -} - - -void M_MultiPlayer_Key (int key) -{ - switch (key) - { - case K_ESCAPE: - M_Menu_Main_f (); - break; - - case K_DOWNARROW: - S_LocalSound ("misc/menu1.wav"); - if (++m_multiplayer_cursor >= MULTIPLAYER_ITEMS) - m_multiplayer_cursor = 0; - break; - - case K_UPARROW: - S_LocalSound ("misc/menu1.wav"); - if (--m_multiplayer_cursor < 0) - m_multiplayer_cursor = MULTIPLAYER_ITEMS - 1; - break; - - case K_HOME: - case K_PGUP: - S_LocalSound ("misc/menu1.wav"); - m_multiplayer_cursor = 0; - break; - - case K_END: - case K_PGDN: - S_LocalSound ("misc/menu1.wav"); - m_multiplayer_cursor = MULTIPLAYER_ITEMS - 1; - break; - - case K_ENTER: - m_entersound = true; - switch (m_multiplayer_cursor) - { - case 0: - M_Menu_ServerList_f (); - break; - - case 1: - M_Menu_Setup_f (); - break; - - case 2: - M_Menu_Demos_f (); - break; - - } - } -} - - -//============================================================================= -/* DEMOS MENU */ - -#define MAX_DEMO_NAME 64 -#define MAX_DEMO_FILES 1000 -#define MAXLINES 19 // maximum number of files visible on screen - -typedef struct direntry_s { - int type; // 0=file, 1=dir, 2="..", 3=message - char name[MAX_DEMO_NAME]; - int size; -} direntry_t; - -direntry_t dir[MAX_DEMO_FILES] = {0}; -int numfiles; -char demodir[MAX_QPATH] = "/qw"; -char prevdir[MAX_QPATH] = ""; - -int demo_cursor = 0; -int demo_base = 0; - -static void ReadDir (void) -{ -#ifdef _WIN32 // FIXME - HANDLE h; - WIN32_FIND_DATA fd; - int i; - - numfiles = 0; - demo_base = 0; - demo_cursor = 0; - - if (demodir[0]) { - strcpy (dir[0].name, ".."); - dir[0].type = 2; - numfiles = 1; - } - - h = FindFirstFile (va("%s%s/*.*", com_basedir, demodir), &fd); - if (h == INVALID_HANDLE_VALUE) { - strcpy (dir[numfiles].name, "Error reading directory\n"); - dir[numfiles].type = 3; - numfiles++; - return; - } - - do { - int type, size; - int pos; - char name[MAX_DEMO_NAME]; - - if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - if (!strcmp(fd.cFileName, ".") || !strcmp(fd.cFileName, "..")) - continue; - type = 1; - size = 0; - } - else - { - i = strlen(fd.cFileName); - if (i < 5 || (Q_strcasecmp(fd.cFileName+i-4, ".qwd") - && Q_strcasecmp(fd.cFileName+i-4, ".qwz") && Q_strcasecmp(fd.cFileName+i-4, ".mvd"))) - continue; - type = 0; - size = fd.nFileSizeLow; - } - - Q_strncpyz (name, fd.cFileName, MAX_DEMO_NAME); - - // inclusion sort - for (i=0 ; i dir[i].type) - break; - if (strcmp (name, dir[i].name) < 0) - break; - } - pos = i; - numfiles++; - for (i=numfiles-1 ; i>pos ; i--) - dir[i] = dir[i-1]; - strcpy (dir[i].name, name); - dir[i].type = type; - dir[i].size = size; - if (numfiles == MAX_DEMO_FILES) - break; - } while ( FindNextFile(h, &fd) ); - FindClose (h); - - // TODO: position demo cursor - if (prevdir) { - for (i=0 ; i= MAXLINES) { - demo_base += demo_cursor - (MAXLINES-1); - demo_cursor = MAXLINES-1; - } - *prevdir = '\0'; - } - } - } - - if (!numfiles) { - strcpy (dir[0].name, "[ no files ]"); - dir[0].type = 3; - numfiles = 1; - } -#endif // _WIN32 -} - -void M_Menu_Demos_f (void) -{ -#ifdef _WIN32 - m_entersound = true; - m_state = m_demos; - key_dest = key_menu; - ReadDir (); -#endif -} - -static char *toyellow (char *s) -{ - static char buf[20]; - - Q_strncpyz (buf, s, sizeof(buf)); - for (s=buf ; *s ; s++) - if (*s >= '0' && *s <= '9') - *s = *s - '0' + 18; - return buf; -} - -void M_Demos_Draw (void) -{ - int i; - int y; - direntry_t *d; - char str[29]; - - M_Print (140, 8, "DEMOS"); - M_Print (16, 16, demodir); - M_Print (8, 24, "\x1d\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1f \x1d\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1f"); - - d = dir + demo_base; - for (i=0, y=32 ; iname, sizeof(str)); - if (d->type) - M_PrintWhite (24, y, str); - else - M_Print (24, y, str); - - if (d->type == 1) - M_PrintWhite (240, y, " folder"); - else if (d->type == 2) - M_PrintWhite (240, y, " up "); - else if (d->type == 0) - M_Print (240, y, toyellow(va("%7ik", d->size>>10))); - } - - M_DrawCharacter (8, 32 + demo_cursor*8, 12+((int)(realtime*4)&1)); -} - -void M_Demos_Key (int k) -{ - switch (k) - { - case K_ESCAPE: - M_Menu_MultiPlayer_f (); - break; - - case K_UPARROW: - S_LocalSound ("misc/menu1.wav"); - if (demo_cursor > 0) - demo_cursor--; - else if (demo_base > 0) - demo_base--; - break; - - case K_DOWNARROW: - S_LocalSound ("misc/menu1.wav"); - if (demo_cursor+demo_base < numfiles-1) - { - if (demo_cursor < MAXLINES-1) - demo_cursor++; - else - demo_base++; - } - break; - - case K_HOME: - S_LocalSound ("misc/menu1.wav"); - demo_cursor = 0; - demo_base = 0; - break; - - case K_END: - S_LocalSound ("misc/menu1.wav"); - if (numfiles > MAXLINES) { - demo_cursor = MAXLINES-1; - demo_base = numfiles - demo_cursor - 1; - } else { - demo_base = 0; - demo_cursor = numfiles-1; - } - break; - - case K_PGUP: - S_LocalSound ("misc/menu1.wav"); - demo_cursor -= MAXLINES-1; - if (demo_cursor < 0) { - demo_base += demo_cursor; - if (demo_base < 0) - demo_base = 0; - demo_cursor = 0; - } - break; - - case K_PGDN: - S_LocalSound ("misc/menu1.wav"); - demo_cursor += MAXLINES-1; - if (demo_base + demo_cursor >= numfiles) - demo_cursor = numfiles - demo_base - 1; - if (demo_cursor >= MAXLINES) { - demo_base += demo_cursor - (MAXLINES-1); - demo_cursor = MAXLINES-1; - if (demo_base + demo_cursor >= numfiles) - demo_base = numfiles - demo_cursor - 1; - } - break; - - case K_ENTER: - if (!numfiles || dir[demo_base + demo_cursor].type == 3) - break; - - if (dir[demo_base + demo_cursor].type) { - if (dir[demo_base + demo_cursor].type == 2) - { - char *p; - if ( (p = strrchr(demodir, '/')) != NULL) - { - strcpy (prevdir, p+1); - *p = '\0'; - } - } - else - { - strncat (demodir, "/", sizeof(demodir)-1); - strncat (demodir, dir[demo_base + demo_cursor].name, sizeof(demodir)-1); - } - demo_cursor = 0; - ReadDir (); - } - else - { - key_dest = key_game; - m_state = m_none; - Cbuf_AddText (va("playdemo \"..%s/%s\"\n", demodir, dir[demo_cursor+demo_base].name)); - } - break; - } -} - - -//============================================================================= -/* GAME OPTIONS MENU */ - - -//============================================================================= -/* SETUP MENU */ - -int setup_cursor = 0; -int setup_cursor_table[] = {40, 56, 80, 104, 140}; - -char setup_name[16]; -char setup_team[16]; -int setup_oldtop; -int setup_oldbottom; -int setup_top; -int setup_bottom; - -extern cvar_t name, team; -extern cvar_t topcolor, bottomcolor; - -#define NUM_SETUP_CMDS 5 - -void M_Menu_Setup_f (void) -{ - key_dest = key_menu; - m_state = m_setup; - m_entersound = true; - Q_strncpyz (setup_name, name.string, sizeof(setup_name)); - Q_strncpyz (setup_team, team.string, sizeof(setup_team)); - setup_top = setup_oldtop = (int)topcolor.value; - setup_bottom = setup_oldbottom = (int)bottomcolor.value; -} - - -void M_Setup_Draw (void) -{ - qpic_t *p; - - M_DrawTransPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") ); - p = Draw_CachePic ("gfx/p_multi.lmp"); - M_DrawPic ( (320-p->width)/2, 4, p); - - M_Print (64, 40, "Your name"); - M_DrawTextBox (160, 32, 16, 1); - M_PrintWhite (168, 40, setup_name); - - M_Print (64, 56, "Your team"); - M_DrawTextBox (160, 48, 16, 1); - M_PrintWhite (168, 56, setup_team); - - M_Print (64, 80, "Shirt color"); - M_Print (64, 104, "Pants color"); - - M_DrawTextBox (64, 140-8, 14, 1); - M_Print (72, 140, "Accept Changes"); - - p = Draw_CachePic ("gfx/bigbox.lmp"); - M_DrawTransPic (160, 64, p); - p = Draw_CachePic ("gfx/menuplyr.lmp"); - M_BuildTranslationTable(setup_top*16, setup_bottom*16); - M_DrawTransPicTranslate (172, 72, p); - - M_DrawCharacter (56, setup_cursor_table [setup_cursor], 12+((int)(realtime*4)&1)); - - if (setup_cursor == 0) - M_DrawCharacter (168 + 8*strlen(setup_name), setup_cursor_table [setup_cursor], 10+((int)(realtime*4)&1)); - - if (setup_cursor == 1) - M_DrawCharacter (168 + 8*strlen(setup_team), setup_cursor_table [setup_cursor], 10+((int)(realtime*4)&1)); -} - - -void M_Setup_Key (int k) -{ - int l; - - switch (k) - { - case K_ESCAPE: - M_Menu_MultiPlayer_f (); - break; - - case K_UPARROW: - S_LocalSound ("misc/menu1.wav"); - setup_cursor--; - if (setup_cursor < 0) - setup_cursor = NUM_SETUP_CMDS-1; - break; - - case K_DOWNARROW: - S_LocalSound ("misc/menu1.wav"); - setup_cursor++; - if (setup_cursor >= NUM_SETUP_CMDS) - setup_cursor = 0; - break; - - case K_HOME: - S_LocalSound ("misc/menu1.wav"); - setup_cursor = 0; - break; - - case K_END: - S_LocalSound ("misc/menu1.wav"); - setup_cursor = NUM_SETUP_CMDS-1; - break; - - case K_LEFTARROW: - if (setup_cursor < 2) - return; - S_LocalSound ("misc/menu3.wav"); - if (setup_cursor == 2) - setup_top = setup_top - 1; - if (setup_cursor == 3) - setup_bottom = setup_bottom - 1; - break; - case K_RIGHTARROW: - if (setup_cursor < 2) - return; -//forward: - S_LocalSound ("misc/menu3.wav"); - if (setup_cursor == 2) - setup_top = setup_top + 1; - if (setup_cursor == 3) - setup_bottom = setup_bottom + 1; - break; - - case K_ENTER: -// if (setup_cursor == 0 || setup_cursor == 1) -// return; - -// if (setup_cursor == 2 || setup_cursor == 3) -// goto forward; - - // setup_cursor == 4 (OK) - Cvar_Set (&name, setup_name); - Cvar_Set (&team, setup_team); - Cvar_Set (&topcolor, va("%i", setup_top)); - Cvar_Set (&bottomcolor, va("%i", setup_bottom)); - m_entersound = true; - M_Menu_MultiPlayer_f (); - break; - - case K_BACKSPACE: - if (setup_cursor == 0) - { - if (strlen(setup_name)) - setup_name[strlen(setup_name)-1] = 0; - } - - if (setup_cursor == 1) - { - if (strlen(setup_team)) - setup_team[strlen(setup_team)-1] = 0; - } - break; - - default: - if (k < 32 || k > 127) - break; - if (setup_cursor == 0) - { - l = strlen(setup_name); - if (l < 15) - { - setup_name[l+1] = 0; - setup_name[l] = k; - } - } - if (setup_cursor == 1) - { - l = strlen(setup_team); - if (l < 15) - { - setup_team[l+1] = 0; - setup_team[l] = k; - } - } - } - - if (setup_top > 13) - setup_top = 0; - if (setup_top < 0) - setup_top = 13; - if (setup_bottom > 13) - setup_bottom = 0; - if (setup_bottom < 0) - setup_bottom = 13; -} - - -// SLIST --> - -#define MENU_X 50 -#define MENU_Y 30 -#define STAT_X 50 -#define STAT_Y 122 - -int m_multip_cursor=0; -int m_multip_mins; -int m_multip_maxs; -int m_multip_horiz; -int m_multip_state; - -void M_Menu_ServerList_f (void) { - key_dest = key_menu; - m_entersound = true; -// m_state = m_multiplayer; - m_state = m_slist; -// m_multip_cursor = 0; - m_multip_mins = 0; - m_multip_maxs = 10; - m_multip_horiz = 0; - m_multip_state = 0; -} - -void M_ServerList_Draw (void) { - int serv; - int line = 1; - qpic_t *p; - //int f; - - M_DrawTransPic(16,4,Draw_CachePic("gfx/qplaque.lmp")); - p = Draw_CachePic("gfx/p_multi.lmp"); - M_DrawPic((320-p->width)/2,4,p); - - if (!(slist[0].server)) { - M_DrawTextBox(60,80,23,4); - M_PrintWhite(110,12*8,"No server list"); - M_PrintWhite(140,13*8,"found."); - return; - } - M_DrawTextBox(STAT_X,STAT_Y,23,4); - //M_DrawTextBox(STAT_X+96,STAT_Y+38,12,3); - M_DrawTextBox(STAT_X,STAT_Y+38,23,3); - M_DrawTextBox(MENU_X,MENU_Y,23,(m_multip_maxs - m_multip_mins)+1); - for (serv = m_multip_mins; serv <= m_multip_maxs; serv++) { - if (slist[serv].server) { - M_Print(MENU_X+18,line*8+MENU_Y, - va("%1.21s", - strlen(slist[serv].description) <= m_multip_horiz ? "" : slist[serv].description+m_multip_horiz)); - line++; - } - } - M_PrintWhite(STAT_X+18,STAT_Y+16,"IP/Hostname:"); - M_Print(STAT_X+18,STAT_Y+24,slist[m_multip_cursor].server); - M_DrawCharacter(MENU_X+8,(m_multip_cursor - m_multip_mins + 1) * 8+MENU_Y, - 12+((int)(realtime*4)&1)); -} - -void M_ServerList_Key (key) -{ -// server_entry_t *pt; - if (!(slist[0].server) && key != K_ESCAPE && key != K_INS) - return; - switch(key) - { - case K_ESCAPE: - M_Menu_MultiPlayer_f(); - break; - - case K_UPARROW: - S_LocalSound("misc/menu1.wav"); - if (m_multip_cursor > 0) - { - if (keydown[K_CTRL]) - { - SList_Switch(m_multip_cursor,m_multip_cursor-1); - m_multip_cursor--; - } - else - m_multip_cursor--; - } - break; - - case K_DOWNARROW: - S_LocalSound("misc/menu1.wav"); - if (keydown[K_CTRL]) - { - if (m_multip_cursor != SList_Len() - 1) { - SList_Switch(m_multip_cursor,m_multip_cursor+1); - m_multip_cursor++; - } - } - else if (m_multip_cursor < (MAX_SERVER_LIST-1) && slist[m_multip_cursor+1].server) { - m_multip_cursor++; - } - break; - - case K_HOME: - S_LocalSound("misc/menu1.wav"); - m_multip_cursor = 0; - break; - - case K_END: - S_LocalSound("misc/menu1.wav"); - m_multip_cursor = SList_Len() - 1; - break; - - case K_PGUP: - S_LocalSound("misc/menu1.wav"); - m_multip_cursor -= (m_multip_maxs - m_multip_mins); - if (m_multip_cursor < 0) - m_multip_cursor = 0; - break; - - case K_PGDN: - S_LocalSound("misc/menu1.wav"); - m_multip_cursor += (m_multip_maxs - m_multip_mins); - if (m_multip_cursor >= MAX_SERVER_LIST) - m_multip_cursor = MAX_SERVER_LIST - 1; - while (!(slist[m_multip_cursor].server)) - m_multip_cursor--; - break; - - case K_RIGHTARROW: - S_LocalSound("misc/menu1.wav"); - if (m_multip_horiz < 256) - m_multip_horiz++; - break; - - case K_LEFTARROW: - S_LocalSound("misc/menu1.wav"); - if (m_multip_horiz > 0 ) - m_multip_horiz--; - break; - - case K_ENTER: - if (keydown[K_CTRL]) - { - M_Menu_SEdit_f(); - break; - } - m_state = m_main; - M_ToggleMenu_f(); - CL_Disconnect(); - Q_strncpyz (cls.servername, slist[m_multip_cursor].server, sizeof(cls.servername)); - CL_BeginServerConnect(); - break; - - case 'e': - case 'E': - M_Menu_SEdit_f(); - break; - - case K_INS: - S_LocalSound("misc/menu2.wav"); - if (SList_Len() < (MAX_SERVER_LIST-1)) { - memmove(&slist[m_multip_cursor+1], - &slist[m_multip_cursor], - (SList_Len() - m_multip_cursor)*sizeof(slist[0])); - SList_Reset_NoFree(m_multip_cursor); - SList_Set(m_multip_cursor,"127.0.0.1",""); - } - break; - - case K_DEL: - S_LocalSound("misc/menu2.wav"); - if (SList_Len() > 0) { - free(slist[m_multip_cursor].server); - free(slist[m_multip_cursor].description); - if (SList_Len()-1 == m_multip_cursor) { - SList_Reset_NoFree(m_multip_cursor); - m_multip_cursor = !m_multip_cursor ? 0 : m_multip_cursor-1; - - } - else { - memmove(&slist[m_multip_cursor], - &slist[m_multip_cursor+1], - (SList_Len()-m_multip_cursor-1) * sizeof(slist[0])); - SList_Reset_NoFree(SList_Len()-1); - } - } - break; - default: - break; - } - if (m_multip_cursor < m_multip_mins) { - m_multip_maxs -= (m_multip_mins - m_multip_cursor); - m_multip_mins = m_multip_cursor; - } - if (m_multip_cursor > m_multip_maxs) { - m_multip_mins += (m_multip_cursor - m_multip_maxs); - m_multip_maxs = m_multip_cursor; - } -} -#define SERV_X 60 -#define SERV_Y 64 -#define DESC_X 60 -#define DESC_Y 40 -#define SERV_L 22 -#define DESC_L 22 - -char serv[256]; -char desc[256]; -int serv_max; -int serv_min; -int desc_max; -int desc_min; -int sedit_state; - -void M_Menu_SEdit_f (void) { - key_dest = key_menu; - m_entersound = true; - m_state = m_sedit; - sedit_state = 0; - Q_strncpyz (serv, slist[m_multip_cursor].server, sizeof(serv)); - Q_strncpyz (desc, slist[m_multip_cursor].description, sizeof(desc)); - serv_max = strlen(serv) > SERV_L ? strlen(serv) : SERV_L; - serv_min = serv_max - (SERV_L); - desc_max = strlen(desc) > DESC_L ? strlen(desc) : DESC_L; - desc_min = desc_max - (DESC_L); -} - -void M_SEdit_Draw (void) { - qpic_t *p; - - M_DrawTransPic(16,4,Draw_CachePic("gfx/qplaque.lmp")); - p = Draw_CachePic("gfx/p_multi.lmp"); - M_DrawPic((320-p->width)/2,4,p); - - M_DrawTextBox(SERV_X,SERV_Y,23,1); - M_DrawTextBox(DESC_X,DESC_Y,23,1); - M_PrintWhite(SERV_X,SERV_Y-4,"Hostname/IP:"); - M_PrintWhite(DESC_X,DESC_Y-4,"Description:"); - M_Print(SERV_X+9,SERV_Y+8,va("%1.22s",serv+serv_min)); - M_Print(DESC_X+9,DESC_Y+8,va("%1.22s",desc+desc_min)); - if (sedit_state == 0) - M_DrawCharacter(SERV_X+9+8*(strlen(serv)-serv_min), - SERV_Y+8,10+((int)(realtime*4)&1)); - if (sedit_state == 1) - M_DrawCharacter(DESC_X+9+8*(strlen(desc)-desc_min), - DESC_Y+8,10+((int)(realtime*4)&1)); -} - -void M_SEdit_Key (int key) { - int l; - switch (key) { - case K_ESCAPE: - M_Menu_ServerList_f (); - break; - case K_ENTER: - SList_Set(m_multip_cursor,serv,desc); - M_Menu_ServerList_f (); - break; - case K_UPARROW: - S_LocalSound("misc/menu1.wav"); - sedit_state = sedit_state == 0 ? 1 : 0; - break; - case K_DOWNARROW: - S_LocalSound("misc/menu1.wav"); - sedit_state = sedit_state == 1 ? 0 : 1; - break; - case K_BACKSPACE: - switch (sedit_state) { - case 0: - if ((l = strlen(serv))) - serv[--l] = 0; - if (strlen(serv)-6 < serv_min && serv_min) { - serv_min--; - serv_max--; - } - break; - case 1: - if ((l = strlen(desc))) - desc[--l] = 0; - if (strlen(desc)-6 < desc_min && desc_min) { - desc_min--; - desc_max--; - } - break; - default: - break; - } - break; - default: - if (key < 32 || key > 127) - break; - switch(sedit_state) { - case 0: - l = strlen(serv); - if (l < 254) { - serv[l+1] = 0; - serv[l] = key; - } - if (strlen(serv) > serv_max) { - serv_min++; - serv_max++; - } - break; - case 1: - l = strlen(desc); - if (l < 254) { - desc[l+1] = 0; - desc[l] = key; - } - if (strlen(desc) > desc_max) { - desc_min++; - desc_max++; - } - break; - } - break; - } -} - -// <-- SLIST - -void M_Quit_Draw (void) -{ -#define VSTR(x) #x -#define VSTR2(x) VSTR(x) - char *cmsg[] = { -// 0123456789012345678901234567890123456789 - "0 QuakeWorld", - "1 version " VSTR2(QW_VERSION) " by id Software", - "0Programming", - "1 John Carmack Michael Abrash", - "1 John Cash Christian Antkow", - "0Additional Programming", - "1 Dave 'Zoid' Kirsch", - "1 Jack 'morbid' Mathews", - "0Id Software is not responsible for", - "0providing technical support for", - "0QUAKEWORLD(tm). (c)1996 Id Software,", - "0Inc. All Rights Reserved.", - "0QUAKEWORLD(tm) is a trademark of Id", - "0Software, Inc.", - "1NOTICE: THE COPYRIGHT AND TRADEMARK", - "1NOTICES APPEARING IN YOUR COPY OF", - "1QUAKE(r) ARE NOT MODIFIED BY THE USE", - "1OF QUAKEWORLD(tm) AND REMAIN IN FULL", - "1FORCE.", - "0NIN(r) is a registered trademark", - "0licensed to Nothing Interactive, Inc.", - "0All rights reserved. Press y to exit", - NULL }; - char **p; - int y; - -/* if (wasInMenus) - { - m_state = m_quit_prevstate; - m_recursiveDraw = true; - M_Draw (); - m_state = m_quit; - }*/ - - M_DrawTextBox (0, 0, 38, 23); - y = 12; - for (p = cmsg; *p; p++, y += 8) { - if (**p == '0') - M_PrintWhite (16, y, *p + 1); - else - M_Print (16, y, *p + 1); - } -} - - - -//============================================================================= -/* Menu Subsystem */ - - -void M_Init (void) -{ - Cvar_RegisterVariable (&scr_centerMenu); - - Cmd_AddCommand ("togglemenu", M_ToggleMenu_f); - - Cmd_AddCommand ("menu_main", M_Menu_Main_f); - Cmd_AddCommand ("menu_multiplayer", M_Menu_MultiPlayer_f); - Cmd_AddCommand ("menu_setup", M_Menu_Setup_f); - Cmd_AddCommand ("menu_demos", M_Menu_Demos_f); - Cmd_AddCommand ("menu_options", M_Menu_Options_f); - Cmd_AddCommand ("menu_keys", M_Menu_Keys_f); - Cmd_AddCommand ("menu_fps", M_Menu_Fps_f); - Cmd_AddCommand ("menu_video", M_Menu_Video_f); - Cmd_AddCommand ("help", M_Menu_Help_f); - Cmd_AddCommand ("menu_help", M_Menu_Help_f); - Cmd_AddCommand ("menu_quit", M_Menu_Quit_f); -} - - -void M_Draw (void) -{ - if (m_state == m_none || key_dest != key_menu) - return; - - if (!m_recursiveDraw) - { - scr_copyeverything = 1; - - if (scr_con_current) - { - Draw_ConsoleBackground (scr_con_current); - VID_UnlockBuffer (); - S_ExtraUpdate (); - VID_LockBuffer (); - } - else - Draw_FadeScreen (); - - scr_fullupdate = 0; - } - else - { - m_recursiveDraw = false; - } - - if (scr_centerMenu.value) - m_yofs = (vid.height - 200) / 2; - - switch (m_state) - { - case m_none: - break; - - case m_main: - M_Main_Draw (); - break; - - case m_singleplayer: - M_SinglePlayer_Draw (); - break; - - case m_load: - M_Load_Draw (); - break; - - case m_save: - M_Save_Draw (); - break; - - case m_multiplayer: - M_MultiPlayer_Draw (); - break; - - case m_setup: - M_Setup_Draw (); - break; - - case m_net: -// M_Net_Draw (); - break; - - case m_options: - M_Options_Draw (); - break; - - case m_keys: - M_Keys_Draw (); - break; - - case m_fps: - M_Fps_Draw (); - break; - - case m_video: - M_Video_Draw (); - break; - - case m_help: - M_Help_Draw (); - break; - - case m_quit: - M_Quit_Draw (); - break; - - case m_serialconfig: -// M_SerialConfig_Draw (); - break; - - case m_modemconfig: -// M_ModemConfig_Draw (); - break; - - case m_lanconfig: -// M_LanConfig_Draw (); - break; - - case m_search: -// M_Search_Draw (); - break; - - case m_slist: - M_ServerList_Draw (); - break; - - case m_sedit: - M_SEdit_Draw (); - break; - - case m_demos: - M_Demos_Draw (); - } - - m_yofs = 0; - - if (m_entersound) - { - S_LocalSound ("misc/menu2.wav"); - m_entersound = false; - } - - VID_UnlockBuffer (); - S_ExtraUpdate (); - VID_LockBuffer (); -} - - -void M_Keydown (int key) -{ - switch (m_state) - { - case m_none: - return; - - case m_main: - M_Main_Key (key); - return; - - case m_singleplayer: - M_SinglePlayer_Key (key); - return; - - case m_load: - M_Load_Key (key); - return; - - case m_save: - M_Save_Key (key); - return; - - case m_multiplayer: - M_MultiPlayer_Key (key); - return; - - case m_setup: - M_Setup_Key (key); - return; - - case m_net: -// M_Net_Key (key); - return; - - case m_options: - M_Options_Key (key); - return; - - case m_keys: - M_Keys_Key (key); - return; - - case m_fps: - M_Fps_Key (key); - return; - - case m_video: - M_Video_Key (key); - return; - - case m_help: - M_Help_Key (key); - return; - - case m_quit: - M_Quit_Key (key); - return; - - case m_serialconfig: -// M_SerialConfig_Key (key); - return; - - case m_modemconfig: -// M_ModemConfig_Key (key); - return; - - case m_lanconfig: -// M_LanConfig_Key (key); - return; - - case m_search: -// M_Search_Key (key); - break; - - case m_slist: - M_ServerList_Key (key); - return; - - case m_sedit: - M_SEdit_Key (key); - break; - - case m_demos: - M_Demos_Key (key); - break; - } -} - - +/* +Copyright (C) 1996-1997 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. + +*/ +#if defined(__linux__) || defined(sun) || defined(__GNUC__) +#include +#include +#endif + +#include "quakedef.h" +#include "winquake.h" +#include "cl_slist.h" +#include "keys.h" +#include "sound.h" +#include "version.h" + +void (*vid_menudrawfn)(void); +void (*vid_menukeyfn)(int key); + +enum {m_none, m_main, m_singleplayer, m_load, m_save, m_multiplayer, + m_setup, m_net, m_options, m_video, m_keys, m_help, m_quit, + m_serialconfig, m_modemconfig, m_lanconfig, m_gameoptions, + m_search, m_slist, m_sedit, m_fps, m_demos} m_state; + +void M_Menu_Main_f (void); + void M_Menu_SinglePlayer_f (void); + void M_Menu_Load_f (void); + void M_Menu_Save_f (void); + void M_Menu_MultiPlayer_f (void); + void M_Menu_ServerList_f (void); + void M_Menu_SEdit_f (void); + void M_Menu_Setup_f (void); + void M_Menu_Demos_f (void); + void M_Menu_GameOptions_f (void); + void M_Menu_Options_f (void); + void M_Menu_Keys_f (void); + void M_Menu_Fps_f (void); + void M_Menu_Video_f (void); + void M_Menu_Help_f (void); + void M_Menu_Quit_f (void); + +void M_Main_Draw (void); + void M_SinglePlayer_Draw (void); + void M_Load_Draw (void); + void M_Save_Draw (void); + void M_MultiPlayer_Draw (void); + void M_ServerList_Draw (void); + void M_SEdit_Draw (void); + void M_Setup_Draw (void); + void M_Demos_Draw (void); + void M_GameOptions_Draw (void); + void M_Options_Draw (void); + void M_Keys_Draw (void); + void M_Fps_Draw (void); + void M_Video_Draw (void); + void M_Help_Draw (void); + void M_Quit_Draw (void); + +void M_Main_Key (int key); + void M_SinglePlayer_Key (int key); + void M_Load_Key (int key); + void M_Save_Key (int key); + void M_MultiPlayer_Key (int key); + void M_ServerList_Key (int key); + void M_SEdit_Key (int key); + void M_Setup_Key (int key); + void M_Demos_Key (int key); + void M_GameOptions_Key (int key); + void M_Options_Key (int key); + void M_Keys_Key (int key); + void M_Fps_Key (int key); + void M_Video_Key (int key); + void M_Help_Key (int key); + void M_Quit_Key (int key); + + +qboolean m_entersound; // play after drawing a frame, so caching + // won't disrupt the sound +qboolean m_recursiveDraw; + +int m_return_state; +qboolean m_return_onerror; +char m_return_reason [32]; + + +//============================================================================= +/* Support Routines */ + +cvar_t scr_centerMenu = {"scr_centerMenu","1"}; +int m_yofs = 0; + +/* +================ +M_DrawCharacter + +Draws one solid graphics character +================ +*/ +void M_DrawCharacter (int cx, int line, int num) +{ + Draw_Character (cx + ((vid.width - 320)>>1), line + m_yofs, num); +} + +void M_Print (int cx, int cy, char *str) +{ + while (*str) + { + M_DrawCharacter (cx, cy, (*str) | 128); + str++; + cx += 8; + } +} + +void M_PrintWhite (int cx, int cy, char *str) +{ + while (*str) + { + M_DrawCharacter (cx, cy, *str); + str++; + cx += 8; + } +} + +void M_DrawTransPic (int x, int y, qpic_t *pic) +{ + Draw_TransPic (x + ((vid.width - 320)>>1), y + m_yofs, pic); +} + +void M_DrawPic (int x, int y, qpic_t *pic) +{ + Draw_Pic (x + ((vid.width - 320)>>1), y + m_yofs, pic); +} + +byte identityTable[256]; +byte translationTable[256]; + +void M_BuildTranslationTable(int top, int bottom) +{ + int j; + byte *dest, *source; + + for (j = 0; j < 256; j++) + identityTable[j] = j; + dest = translationTable; + source = identityTable; + memcpy (dest, source, 256); + + if (top < 128) // the artists made some backwards ranges. sigh. + memcpy (dest + TOP_RANGE, source + top, 16); + else + for (j=0 ; j<16 ; j++) + dest[TOP_RANGE+j] = source[top+15-j]; + + if (bottom < 128) + memcpy (dest + BOTTOM_RANGE, source + bottom, 16); + else + for (j=0 ; j<16 ; j++) + dest[BOTTOM_RANGE+j] = source[bottom+15-j]; +} + + +void M_DrawTransPicTranslate (int x, int y, qpic_t *pic) +{ + Draw_TransPicTranslate (x + ((vid.width - 320)>>1), y + m_yofs, pic, translationTable); +} + + +void M_DrawTextBox (int x, int y, int width, int lines) +{ + qpic_t *p; + int cx, cy; + int n; + + // draw left side + cx = x; + cy = y; + p = Draw_CachePic ("gfx/box_tl.lmp"); + M_DrawTransPic (cx, cy, p); + p = Draw_CachePic ("gfx/box_ml.lmp"); + for (n = 0; n < lines; n++) + { + cy += 8; + M_DrawTransPic (cx, cy, p); + } + p = Draw_CachePic ("gfx/box_bl.lmp"); + M_DrawTransPic (cx, cy+8, p); + + // draw middle + cx += 8; + while (width > 0) + { + cy = y; + p = Draw_CachePic ("gfx/box_tm.lmp"); + M_DrawTransPic (cx, cy, p); + p = Draw_CachePic ("gfx/box_mm.lmp"); + for (n = 0; n < lines; n++) + { + cy += 8; + if (n == 1) + p = Draw_CachePic ("gfx/box_mm2.lmp"); + M_DrawTransPic (cx, cy, p); + } + p = Draw_CachePic ("gfx/box_bm.lmp"); + M_DrawTransPic (cx, cy+8, p); + width -= 2; + cx += 16; + } + + // draw right side + cy = y; + p = Draw_CachePic ("gfx/box_tr.lmp"); + M_DrawTransPic (cx, cy, p); + p = Draw_CachePic ("gfx/box_mr.lmp"); + for (n = 0; n < lines; n++) + { + cy += 8; + M_DrawTransPic (cx, cy, p); + } + p = Draw_CachePic ("gfx/box_br.lmp"); + M_DrawTransPic (cx, cy+8, p); +} + +//============================================================================= + +int m_save_demonum; + +/* +================ +M_ToggleMenu_f +================ +*/ +void M_ToggleMenu_f (void) +{ + m_entersound = true; + + if (key_dest == key_menu) + { + if (m_state != m_main) + { + M_Menu_Main_f (); + return; + } + key_dest = key_game; + m_state = m_none; + return; + } +/* if (key_dest == key_console) + { + Con_ToggleConsole_f (); + }*/ + else + { + // hide console + scr_conlines = 0; + scr_con_current = 0; + M_Menu_Main_f (); + } +} + + +//============================================================================= +/* MAIN MENU */ + +int m_main_cursor; +#define MAIN_ITEMS 5 + + +void M_Menu_Main_f (void) +{ + if (key_dest != key_menu) + { + m_save_demonum = cls.demonum; + cls.demonum = -1; + } + key_dest = key_menu; + m_state = m_main; + m_entersound = true; +} + + +void M_Main_Draw (void) +{ + int f; + qpic_t *p; + + M_DrawTransPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") ); + p = Draw_CachePic ("gfx/ttl_main.lmp"); + M_DrawPic ( (320-p->width)/2, 4, p); + M_DrawTransPic (72, 32, Draw_CachePic ("gfx/mainmenu.lmp") ); + + f = (int)(realtime * 10)%6; + + M_DrawTransPic (54, 32 + m_main_cursor * 20,Draw_CachePic( va("gfx/menudot%i.lmp", f+1 ) ) ); +} + + +void M_Main_Key (int key) +{ + switch (key) + { + case K_ESCAPE: + key_dest = key_game; + m_state = m_none; + cls.demonum = m_save_demonum; + if (cls.demonum != -1 && !cls.demoplayback && cls.state == ca_disconnected) + CL_NextDemo (); + break; + + case K_UPARROW: + S_LocalSound ("misc/menu1.wav"); + if (--m_main_cursor < 0) + m_main_cursor = MAIN_ITEMS - 1; + break; + + case K_DOWNARROW: + S_LocalSound ("misc/menu1.wav"); + if (++m_main_cursor >= MAIN_ITEMS) + m_main_cursor = 0; + break; + + case K_HOME: + case K_PGUP: + S_LocalSound ("misc/menu1.wav"); + m_main_cursor = 0; + break; + + case K_END: + case K_PGDN: + S_LocalSound ("misc/menu1.wav"); + m_main_cursor = MAIN_ITEMS - 1; + break; + + case K_ENTER: + m_entersound = true; + + switch (m_main_cursor) + { + case 0: + M_Menu_SinglePlayer_f (); + break; + + case 1: + M_Menu_MultiPlayer_f (); + break; + + case 2: + M_Menu_Options_f (); + break; + + case 3: + M_Menu_Help_f (); + break; + + case 4: + M_Menu_Quit_f (); + break; + } + } +} + + +//============================================================================= +/* OPTIONS MENU */ + +#define OPTIONS_ITEMS 18 + +#define SLIDER_RANGE 10 + +int options_cursor; + +#ifdef GLQUAKE +#define gamma gl_gamma +#define contrast gl_contrast +#else +#define gamma v_gamma +#define contrast v_contrast +#endif + +void M_Menu_Options_f (void) +{ + key_dest = key_menu; + m_state = m_options; + m_entersound = true; +} + + +void M_AdjustSliders (int dir) +{ + S_LocalSound ("misc/menu3.wav"); + + switch (options_cursor) + { + case 3: // screen size + scr_viewsize.value += dir * 10; + if (scr_viewsize.value < 30) + scr_viewsize.value = 30; + if (scr_viewsize.value > 120) + scr_viewsize.value = 120; + Cvar_SetValue (&scr_viewsize, scr_viewsize.value); + break; + case 4: // gamma + gamma.value -= dir * 0.05; + if (gamma.value < 0.5) + gamma.value = 0.5; + if (gamma.value > 1) + gamma.value = 1; + Cvar_SetValue (&gamma, gamma.value); + break; + case 5: // contrast + contrast.value += dir * 0.1; + if (contrast.value < 1) + contrast.value = 1; + if (contrast.value > 2) + contrast.value = 2; + Cvar_SetValue (&contrast, contrast.value); + break; + case 6: // mouse speed + sensitivity.value += dir * 0.5; + if (sensitivity.value < 1) + sensitivity.value = 1; + if (sensitivity.value > 11) + sensitivity.value = 11; + Cvar_SetValue (&sensitivity, sensitivity.value); + break; + case 7: // music volume +#ifdef _WIN32 + bgmvolume.value += dir * 1.0; +#else + bgmvolume.value += dir * 0.1; +#endif + if (bgmvolume.value < 0) + bgmvolume.value = 0; + if (bgmvolume.value > 1) + bgmvolume.value = 1; + Cvar_SetValue (&bgmvolume, bgmvolume.value); + break; + case 8: // sfx volume + volume.value += dir * 0.1; + if (volume.value < 0) + volume.value = 0; + if (volume.value > 1) + volume.value = 1; + Cvar_SetValue (&volume, volume.value); + break; + + case 9: // always run + if (cl_forwardspeed.value > 200) + { + Cvar_SetValue (&cl_forwardspeed, 200); + Cvar_SetValue (&cl_backspeed, 200); + } + else + { + Cvar_SetValue (&cl_forwardspeed, 400); + Cvar_SetValue (&cl_backspeed, 400); + } + break; + + case 10: // invert mouse + Cvar_SetValue (&m_pitch, -m_pitch.value); + break; + + case 11: // lookspring + Cvar_SetValue (&lookspring, !lookspring.value); + break; + + case 12: // lookstrafe + Cvar_SetValue (&lookstrafe, !lookstrafe.value); + break; + + case 13: + Cvar_SetValue (&cl_sbar, !cl_sbar.value); + break; + + case 14: + Cvar_SetValue (&cl_hudswap, !cl_hudswap.value); + break; + + case 17: // _windowed_mouse + Cvar_SetValue (&_windowed_mouse, !_windowed_mouse.value); + break; + } +} + + +void M_DrawSlider (int x, int y, float range) +{ + int i; + + if (range < 0) + range = 0; + if (range > 1) + range = 1; + M_DrawCharacter (x-8, y, 128); + for (i=0 ; iwidth)/2, 4, p); + + M_Print (16, 32, " Customize controls"); + M_Print (16, 40, " Go to console"); + M_Print (16, 48, " Reset to defaults"); + + M_Print (16, 56, " Screen size"); + r = (scr_viewsize.value - 30) / (120 - 30); + M_DrawSlider (220, 56, r); + + M_Print (16, 64, " Gamma"); + r = (1.0 - gamma.value) / 0.5; + M_DrawSlider (220, 64, r); + + M_Print (16, 72, " Contrast"); + r = contrast.value - 1.0; + M_DrawSlider (220, 72, r); + + M_Print (16, 80, " Mouse Speed"); + r = (sensitivity.value - 1)/10; + M_DrawSlider (220, 80, r); + + M_Print (16, 88, " CD Music Volume"); + r = bgmvolume.value; + M_DrawSlider (220, 88, r); + + M_Print (16, 96, " Sound Volume"); + r = volume.value; + M_DrawSlider (220, 96, r); + + M_Print (16, 104, " Always Run"); + M_DrawCheckbox (220, 104, cl_forwardspeed.value > 200); + + M_Print (16, 112, " Invert Mouse"); + M_DrawCheckbox (220, 112, m_pitch.value < 0); + + M_Print (16, 120, " Lookspring"); + M_DrawCheckbox (220, 120, lookspring.value); + + M_Print (16, 128, " Lookstrafe"); + M_DrawCheckbox (220, 128, lookstrafe.value); + + M_Print (16, 136, " Use old status bar"); + M_DrawCheckbox (220, 136, cl_sbar.value); + + M_Print (16, 144, " HUD on left side"); + M_DrawCheckbox (220, 144, cl_hudswap.value); + + M_Print (16, 152, " FPS settings"); + + if (vid_menudrawfn) + M_Print (16, 160, " Video Modes"); + +#ifdef _WIN32 + if (modestate == MS_WINDOWED) + { +#endif + M_Print (16, 168, " Use Mouse"); + M_DrawCheckbox (220, 168, _windowed_mouse.value); +#ifdef _WIN32 + } +#endif + +// cursor + M_DrawCharacter (200, 32 + options_cursor*8, 12+((int)(realtime*4)&1)); +} + + +void M_Options_Key (int k) +{ + switch (k) + { + case K_ESCAPE: + M_Menu_Main_f (); + break; + + case K_ENTER: + m_entersound = true; + switch (options_cursor) + { + case 0: + M_Menu_Keys_f (); + break; + case 1: + m_state = m_none; + key_dest = key_console; +// Con_ToggleConsole_f (); + break; + case 2: + Cbuf_AddText ("exec default.cfg\n"); + break; + case 15: + M_Menu_Fps_f (); + break; + case 16: + if (vid_menudrawfn) + M_Menu_Video_f (); + break; + default: + M_AdjustSliders (1); + break; + } + return; + + case K_UPARROW: + S_LocalSound ("misc/menu1.wav"); + options_cursor--; + if (options_cursor < 0) + options_cursor = OPTIONS_ITEMS-1; + break; + + case K_DOWNARROW: + S_LocalSound ("misc/menu1.wav"); + options_cursor++; + if (options_cursor >= OPTIONS_ITEMS) + options_cursor = 0; + break; + + case K_HOME: + case K_PGUP: + S_LocalSound ("misc/menu1.wav"); + options_cursor = 0; + break; + + case K_END: + case K_PGDN: + S_LocalSound ("misc/menu1.wav"); + options_cursor = OPTIONS_ITEMS-1; + break; + + case K_LEFTARROW: + M_AdjustSliders (-1); + break; + + case K_RIGHTARROW: + M_AdjustSliders (1); + break; + } + + if (options_cursor == 16 && vid_menudrawfn == NULL) + { + if (k == K_UPARROW || k == K_END || k == K_PGDN) + options_cursor = 15; + else + options_cursor = 0; + } + + if ((options_cursor == 17) +#ifdef _WIN32 + && (modestate != MS_WINDOWED) +#endif + ) + { + if (k == K_UPARROW || k == K_END || k == K_PGDN) + options_cursor = 16; + else + options_cursor = 0; + } +} + + +//============================================================================= +/* KEYS MENU */ + +char *bindnames[][2] = +{ +{"+attack", "attack"}, +{"+jump", "jump"}, +{"+forward", "walk forward"}, +{"+back", "backpedal"}, +{"+moveleft", "move left"}, +{"+moveright", "move right"}, +{"+moveup", "swim up"}, +{"+movedown", "swim down"}, +{"impulse 10", "change weapon"}, +{"+speed", "run"}, +{"+strafe", "sidestep"}, +{"+left", "turn left"}, +{"+right", "turn right"}, +{"+lookup", "look up"}, +{"+lookdown", "look down"}, +{"centerview", "center view"}, +{"+mlook", "mouse look"}, +}; + +#define NUMCOMMANDS (sizeof(bindnames)/sizeof(bindnames[0])) + +int keys_cursor; +int bind_grab; + +void M_Menu_Keys_f (void) +{ + key_dest = key_menu; + m_state = m_keys; + m_entersound = true; +} + + +void M_FindKeysForCommand (char *command, int *twokeys) +{ + int count; + int j; + int l; + char *b; + + twokeys[0] = twokeys[1] = -1; + l = strlen(command); + count = 0; + + for (j=0 ; j<256 ; j++) + { + b = keybindings[j]; + if (!b) + continue; + if (!strncmp (b, command, l) ) + { + twokeys[count] = j; + count++; + if (count == 2) + break; + } + } +} + +void M_UnbindCommand (char *command) +{ + int j; + int l; + char *b; + + l = strlen(command); + + for (j=0 ; j<256 ; j++) + { + b = keybindings[j]; + if (!b) + continue; + if (!strncmp (b, command, l) ) + Key_SetBinding (j, ""); + } +} + + +void M_Keys_Draw (void) +{ + int i, l; + int keys[2]; + char *name; + int x, y; + qpic_t *p; + + p = Draw_CachePic ("gfx/ttl_cstm.lmp"); + M_DrawPic ( (320-p->width)/2, 4, p); + + if (bind_grab) + M_Print (12, 32, "Press a key or button for this action"); + else + M_Print (18, 32, "Enter to change, del to clear"); + +// search for known bindings + for (i=0 ; i= NUMCOMMANDS) + keys_cursor = 0; + break; + + case K_HOME: + case K_PGUP: + S_LocalSound ("misc/menu1.wav"); + keys_cursor = 0; + break; + + case K_END: + case K_PGDN: + S_LocalSound ("misc/menu1.wav"); + keys_cursor = NUMCOMMANDS - 1; + break; + + case K_ENTER: // go into bind mode + M_FindKeysForCommand (bindnames[keys_cursor][0], keys); + S_LocalSound ("misc/menu2.wav"); + if (keys[1] != -1) + M_UnbindCommand (bindnames[keys_cursor][0]); + bind_grab = true; + break; + + case K_DEL: // delete bindings + S_LocalSound ("misc/menu2.wav"); + M_UnbindCommand (bindnames[keys_cursor][0]); + break; + } +} + + +//============================================================================= +/* FPS SETTINGS MENU */ + +#ifdef GLQUAKE +#define FPS_ITEMS 11 +#else +#define FPS_ITEMS 12 +#endif +int fps_cursor = 0; + +extern cvar_t v_bonusflash; +extern cvar_t cl_rocket2grenade; +extern cvar_t v_damagecshift; +extern cvar_t r_fastsky; +extern cvar_t r_drawflame; + +void M_Menu_Fps_f (void) +{ + key_dest = key_menu; + m_state = m_fps; + m_entersound = true; +} + +void M_Fps_Draw (void) +{ + qpic_t *p; + + M_DrawTransPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") ); + p = Draw_CachePic ("gfx/ttl_cstm.lmp"); + M_DrawPic ( (320-p->width)/2, 4, p); + + M_Print (16, 32, " Explosions"); + M_Print (220, 32, cl_explosion.value==0 ? "normal" : + cl_explosion.value==1 ? "type 1" : cl_explosion.value==2 ? "type 2" : + cl_explosion.value==3 ? "type 3" : ""); + + M_Print (16, 40, " Muzzleflashes"); + M_Print (220, 40, cl_muzzleflash.value==2 ? "own off" : + cl_muzzleflash.value ? "on" : "off"); + + M_Print (16, 48, " Gib filter"); + M_DrawCheckbox (220, 48, cl_gibfilter.value); + + M_Print (16, 56, " Dead bodies filter"); + M_DrawCheckbox (220, 56, cl_deadbodyfilter.value); + + M_Print (16, 64, " Rocket model"); + M_Print (220, 64, cl_rocket2grenade.value ? "grenade" : "normal"); + + M_Print (16, 72, " Rocket trail"); + M_Print (220, 72, r_rockettrail.value==2 ? "grenade" : + r_rockettrail.value ? "normal" : "off"); + + M_Print (16, 80, " Rocket light"); + M_DrawCheckbox (220, 80, r_rocketlight.value); + + M_Print (16, 88, " Damage filter"); + M_DrawCheckbox (220, 88, v_damagecshift.value == 0); + + M_Print (16, 96, " Pickup flashes"); + M_DrawCheckbox (220, 96, v_bonusflash.value); + + M_Print (16, 104, " Powerup glow"); + M_Print (220, 104, r_powerupglow.value==2 ? "own off" : + r_powerupglow.value ? "on" : "off"); + + M_Print (16, 112, " Draw torches"); + M_DrawCheckbox (220, 112, r_drawflame.value); + +#ifndef GLQUAKE + M_Print (16, 120, " Fast sky"); + M_DrawCheckbox (220, 120, r_fastsky.value); +#endif + +// cursor + M_DrawCharacter (200, 32 + fps_cursor*8, 12+((int)(realtime*4)&1)); +} + +void M_Fps_Key (int k) +{ + int i; + + switch (k) + { + case K_ESCAPE: + M_Menu_Options_f (); + break; + + case K_UPARROW: + S_LocalSound ("misc/menu1.wav"); + fps_cursor--; + if (fps_cursor < 0) + fps_cursor = FPS_ITEMS - 1; + break; + + case K_DOWNARROW: + S_LocalSound ("misc/menu1.wav"); + fps_cursor++; + if (fps_cursor >= FPS_ITEMS) + fps_cursor = 0; + break; + + case K_HOME: + case K_PGUP: + S_LocalSound ("misc/menu1.wav"); + fps_cursor = 0; + break; + + case K_END: + case K_PGDN: + S_LocalSound ("misc/menu1.wav"); + fps_cursor = FPS_ITEMS - 1; + break; + + case K_RIGHTARROW: + case K_ENTER: + S_LocalSound ("misc/menu2.wav"); + switch (fps_cursor) { + case 0: + i = cl_explosion.value + 1; + if (i > 3 || i < 0) + i = 0; + Cvar_SetValue (&cl_explosion, i); + break; + case 1: + Cvar_SetValue (&cl_muzzleflash, cl_muzzleflash.value==2 ? 1 : + cl_muzzleflash.value ? 0 : 2); + break; + case 2: + Cvar_SetValue (&cl_gibfilter, !cl_gibfilter.value); + break; + case 3: + Cvar_SetValue (&cl_deadbodyfilter, !cl_deadbodyfilter.value); + break; + case 4: + Cvar_SetValue (&cl_rocket2grenade, !cl_rocket2grenade.value); + break; + case 5: + i = r_rockettrail.value + 1; + if (i < 0 || i > 2) + i = 0; + Cvar_SetValue (&r_rockettrail, i); + break; + case 6: + Cvar_SetValue (&r_rocketlight, !r_rocketlight.value); + break; + case 7: + Cvar_SetValue (&v_damagecshift, !v_damagecshift.value); + break; + case 8: + Cvar_SetValue (&v_bonusflash, !v_bonusflash.value); + break; + case 9: + i = r_powerupglow.value + 1; + if (i < 0 || i > 2) + i = 0; + Cvar_SetValue (&r_powerupglow, i); + break; + case 10: + Cvar_SetValue (&r_drawflame, !r_drawflame.value); + break; +#ifndef GLQUAKE + case 11: + Cvar_SetValue (&r_fastsky, !r_fastsky.value); + break; +#endif + } + break; + + } +} + + +//============================================================================= +/* VIDEO MENU */ + +void M_Menu_Video_f (void) +{ + key_dest = key_menu; + m_state = m_video; + m_entersound = true; +} + + +void M_Video_Draw (void) +{ + (*vid_menudrawfn) (); +} + + +void M_Video_Key (int key) +{ + (*vid_menukeyfn) (key); +} + +//============================================================================= +/* HELP MENU */ + +int help_page; +#define NUM_HELP_PAGES 6 + + +void M_Menu_Help_f (void) +{ + key_dest = key_menu; + m_state = m_help; + m_entersound = true; + help_page = 0; +} + + + +void M_Help_Draw (void) +{ + M_DrawPic (0, 0, Draw_CachePic ( va("gfx/help%i.lmp", help_page)) ); +} + + +void M_Help_Key (int key) +{ + switch (key) + { + case K_ESCAPE: + M_Menu_Main_f (); + break; + + case K_UPARROW: + case K_RIGHTARROW: + m_entersound = true; + if (++help_page >= NUM_HELP_PAGES) + help_page = 0; + break; + + case K_DOWNARROW: + case K_LEFTARROW: + m_entersound = true; + if (--help_page < 0) + help_page = NUM_HELP_PAGES-1; + break; + } + +} + +//============================================================================= +/* QUIT MENU */ + +int msgNumber; +int m_quit_prevstate; +qboolean wasInMenus; + +void M_Menu_Quit_f (void) +{ + if (m_state == m_quit) + return; + wasInMenus = (key_dest == key_menu); + key_dest = key_menu; + m_quit_prevstate = m_state; + m_state = m_quit; + m_entersound = true; + msgNumber = rand()&7; +} + + +void M_Quit_Key (int key) +{ + switch (key) + { + case K_ESCAPE: + case 'n': + case 'N': + if (wasInMenus) + { + m_state = m_quit_prevstate; + m_entersound = true; + } + else + { + key_dest = key_game; + m_state = m_none; + } + break; + + case 'Y': + case 'y': + key_dest = key_console; + CL_Disconnect (); + Sys_Quit (); + break; + + default: + break; + } + +} + +//============================================================================= +/* SINGLE PLAYER MENU */ + +void M_Menu_SinglePlayer_f (void) +{ + m_state = m_singleplayer; +} + +void M_SinglePlayer_Draw (void) +{ + qpic_t *p; + + M_DrawTransPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") ); + p = Draw_CachePic ("gfx/ttl_sgl.lmp"); + M_DrawPic ( (320-p->width)/2, 4, p); +// M_DrawTransPic (72, 32, Draw_CachePic ("gfx/sp_menu.lmp") ); + + M_DrawTextBox (60, 10*8, 23, 4); + M_PrintWhite (88, 12*8, "This client is for"); + M_PrintWhite (88, 13*8, "Internet play only"); +} + +void M_SinglePlayer_Key (key) +{ + if (key == K_ESCAPE || key == K_ENTER) + m_state = m_main; +} + + + +//============================================================================= +/* LOAD/SAVE MENU */ + +/* +int load_cursor; // 0 < load_cursor < MAX_SAVEGAMES + +#define MAX_SAVEGAMES 12 +char m_filenames[MAX_SAVEGAMES][SAVEGAME_COMMENT_LENGTH+1]; +int loadable[MAX_SAVEGAMES]; + +void M_ScanSaves (void) +{ + int i, j; + char name[MAX_OSPATH]; + FILE *f; + int version; + + for (i=0 ; iwidth)/2, 4, p); + +/* for (i=0 ; i< MAX_SAVEGAMES; i++) + M_Print (16, 32 + 8*i, m_filenames[i]); + +// line cursor + M_DrawCharacter (8, 32 + load_cursor*8, 12+((int)(realtime*4)&1)); +*/ + /* + + M_DrawTextBox (60, 10*8, 23, 4); + M_PrintWhite (80, 12*8, "Savegames are not yet"); + M_PrintWhite (88, 13*8, "supported by ZQuake"); + */ +} + + +void M_Save_Draw (void) +{ +// int i; + qpic_t *p; + + p = Draw_CachePic ("gfx/p_save.lmp"); + M_DrawPic ( (320-p->width)/2, 4, p); + +/* for (i=0 ; iwidth)/2, 4, p); + M_Print (80, 40, "favorite servers"); + M_Print (80, 48, "player setup"); + M_Print (80, 56, "demos"); + +// cursor + M_DrawCharacter (64, 40 + m_multiplayer_cursor*8, 12+((int)(realtime*4)&1)); +} + + +void M_MultiPlayer_Key (int key) +{ + switch (key) + { + case K_ESCAPE: + M_Menu_Main_f (); + break; + + case K_DOWNARROW: + S_LocalSound ("misc/menu1.wav"); + if (++m_multiplayer_cursor >= MULTIPLAYER_ITEMS) + m_multiplayer_cursor = 0; + break; + + case K_UPARROW: + S_LocalSound ("misc/menu1.wav"); + if (--m_multiplayer_cursor < 0) + m_multiplayer_cursor = MULTIPLAYER_ITEMS - 1; + break; + + case K_HOME: + case K_PGUP: + S_LocalSound ("misc/menu1.wav"); + m_multiplayer_cursor = 0; + break; + + case K_END: + case K_PGDN: + S_LocalSound ("misc/menu1.wav"); + m_multiplayer_cursor = MULTIPLAYER_ITEMS - 1; + break; + + case K_ENTER: + m_entersound = true; + switch (m_multiplayer_cursor) + { + case 0: + M_Menu_ServerList_f (); + break; + + case 1: + M_Menu_Setup_f (); + break; + + case 2: + M_Menu_Demos_f (); + break; + + } + } +} + + +//============================================================================= +/* DEMOS MENU */ + +#define MAX_DEMO_NAME 64 +#define MAX_DEMO_FILES 1000 +#define MAXLINES 19 // maximum number of files visible on screen + +typedef struct direntry_s { + int type; // 0=file, 1=dir, 2="..", 3=message + char name[MAX_DEMO_NAME]; + int size; +} direntry_t; + +direntry_t dir[MAX_DEMO_FILES] = {0}; +int numfiles; +char demodir[MAX_QPATH] = "/qw"; +char prevdir[MAX_QPATH] = ""; + +int demo_cursor = 0; +int demo_base = 0; + +static void ReadDir (void) +{ +#ifdef _WIN32 // FIXME + HANDLE h; + WIN32_FIND_DATA fd; +#else + DIR *Dir; + struct dirent *oneentry; + struct stat info; + char path[MAX_OSPATH]; +#endif + int i; + char *fname; + + numfiles = 0; + demo_base = 0; + demo_cursor = 0; + + if (demodir[0]) { + strcpy (dir[0].name, ".."); + dir[0].type = 2; + numfiles = 1; + } + +#ifdef WIN32 + h = FindFirstFile (va("%s%s/*.*", com_basedir, demodir), &fd); + if (h == INVALID_HANDLE_VALUE) { +#else + strcpy(path, va("%s%s", com_basedir, demodir)); + Dir = opendir(va("%s", path)); + if (!Dir) { +#endif + strcpy (dir[numfiles].name, "Error reading directory\n"); + dir[numfiles].type = 3; + numfiles++; + return; + } + + do { + int type, size; + int pos; + char name[MAX_DEMO_NAME]; + +#ifndef WIN32 + oneentry=readdir(Dir); + if(!oneentry) + break; + + fname = oneentry->d_name; + if (oneentry->d_type == DT_DIR || oneentry->d_type == DT_LNK) + { + if (!strcmp(fname, ".") || !strcmp(fname, "..")) + continue; + type = 1; + size = 0; + } else { + if (stat (va("%s/%s", path, name) ,&info) == -1) + size = 0; + else size = info.st_size; + +#else + if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + if (!strcmp(fd.cFileName, ".") || !strcmp(fd.cFileName, "..")) + continue; + type = 1; + size = 0; + fname = fd.cFileName; + } else { + fname = fd.cFileName; + size = fd.nFileSizeLow; +#endif + i = strlen(fname); + if (i < 5 || (Q_strcasecmp(fname+i-4, ".qwd") + && Q_strcasecmp(fname+i-4, ".qwz") && Q_strcasecmp(fname+i-4, ".mvd"))) + continue; + type = 0; + } + + Q_strncpyz (name, fname, MAX_DEMO_NAME); + + // inclusion sort + for (i=0 ; i dir[i].type) + break; + if (strcmp (name, dir[i].name) < 0) + break; + } + pos = i; + numfiles++; + for (i=numfiles-1 ; i>pos ; i--) + dir[i] = dir[i-1]; + strcpy (dir[i].name, name); + dir[i].type = type; + dir[i].size = size; + if (numfiles == MAX_DEMO_FILES) + break; +#ifdef WIN32 + } while ( FindNextFile(h, &fd) ); + FindClose (h); +#else + } while (1); +#endif + + // TODO: position demo cursor + if (prevdir) { + for (i=0 ; i= MAXLINES) { + demo_base += demo_cursor - (MAXLINES-1); + demo_cursor = MAXLINES-1; + } + *prevdir = '\0'; + } + } + } + + if (!numfiles) { + strcpy (dir[0].name, "[ no files ]"); + dir[0].type = 3; + numfiles = 1; + } +//#endif // _WIN32 +} + +void M_Menu_Demos_f (void) +{ +//#ifdef _WIN32 + m_entersound = true; + m_state = m_demos; + key_dest = key_menu; + ReadDir (); +//#endif +} + +static char *toyellow (char *s) +{ + static char buf[20]; + + Q_strncpyz (buf, s, sizeof(buf)); + for (s=buf ; *s ; s++) + if (*s >= '0' && *s <= '9') + *s = *s - '0' + 18; + return buf; +} + +void M_Demos_Draw (void) +{ + int i; + int y; + direntry_t *d; + char str[29]; + + M_Print (140, 8, "DEMOS"); + M_Print (16, 16, demodir); + M_Print (8, 24, "\x1d\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1f \x1d\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1f"); + + d = dir + demo_base; + for (i=0, y=32 ; iname, sizeof(str)); + if (d->type) + M_PrintWhite (24, y, str); + else + M_Print (24, y, str); + + if (d->type == 1) + M_PrintWhite (240, y, " folder"); + else if (d->type == 2) + M_PrintWhite (240, y, " up "); + else if (d->type == 0) + M_Print (240, y, toyellow(va("%7ik", d->size>>10))); + } + + M_DrawCharacter (8, 32 + demo_cursor*8, 12+((int)(realtime*4)&1)); +} + +void M_Demos_Key (int k) +{ + switch (k) + { + case K_ESCAPE: + M_Menu_MultiPlayer_f (); + break; + + case K_UPARROW: + S_LocalSound ("misc/menu1.wav"); + if (demo_cursor > 0) + demo_cursor--; + else if (demo_base > 0) + demo_base--; + break; + + case K_DOWNARROW: + S_LocalSound ("misc/menu1.wav"); + if (demo_cursor+demo_base < numfiles-1) + { + if (demo_cursor < MAXLINES-1) + demo_cursor++; + else + demo_base++; + } + break; + + case K_HOME: + S_LocalSound ("misc/menu1.wav"); + demo_cursor = 0; + demo_base = 0; + break; + + case K_END: + S_LocalSound ("misc/menu1.wav"); + if (numfiles > MAXLINES) { + demo_cursor = MAXLINES-1; + demo_base = numfiles - demo_cursor - 1; + } else { + demo_base = 0; + demo_cursor = numfiles-1; + } + break; + + case K_PGUP: + S_LocalSound ("misc/menu1.wav"); + demo_cursor -= MAXLINES-1; + if (demo_cursor < 0) { + demo_base += demo_cursor; + if (demo_base < 0) + demo_base = 0; + demo_cursor = 0; + } + break; + + case K_PGDN: + S_LocalSound ("misc/menu1.wav"); + demo_cursor += MAXLINES-1; + if (demo_base + demo_cursor >= numfiles) + demo_cursor = numfiles - demo_base - 1; + if (demo_cursor >= MAXLINES) { + demo_base += demo_cursor - (MAXLINES-1); + demo_cursor = MAXLINES-1; + if (demo_base + demo_cursor >= numfiles) + demo_base = numfiles - demo_cursor - 1; + } + break; + + case K_ENTER: + if (!numfiles || dir[demo_base + demo_cursor].type == 3) + break; + + if (dir[demo_base + demo_cursor].type) { + if (dir[demo_base + demo_cursor].type == 2) + { + char *p; + if ( (p = strrchr(demodir, '/')) != NULL) + { + strcpy (prevdir, p+1); + *p = '\0'; + } + } + else + { + strncat (demodir, "/", sizeof(demodir)-1); + strncat (demodir, dir[demo_base + demo_cursor].name, sizeof(demodir)-1); + } + demo_cursor = 0; + ReadDir (); + } + else + { + key_dest = key_game; + m_state = m_none; + Cbuf_AddText (va("playdemo \"..%s/%s\"\n", demodir, dir[demo_cursor+demo_base].name)); + } + break; + } +} + + +//============================================================================= +/* GAME OPTIONS MENU */ + + +//============================================================================= +/* SETUP MENU */ + +int setup_cursor = 0; +int setup_cursor_table[] = {40, 56, 80, 104, 140}; + +char setup_name[16]; +char setup_team[16]; +int setup_oldtop; +int setup_oldbottom; +int setup_top; +int setup_bottom; + +extern cvar_t name, team; +extern cvar_t topcolor, bottomcolor; + +#define NUM_SETUP_CMDS 5 + +void M_Menu_Setup_f (void) +{ + key_dest = key_menu; + m_state = m_setup; + m_entersound = true; + Q_strncpyz (setup_name, name.string, sizeof(setup_name)); + Q_strncpyz (setup_team, team.string, sizeof(setup_team)); + setup_top = setup_oldtop = (int)topcolor.value; + setup_bottom = setup_oldbottom = (int)bottomcolor.value; +} + + +void M_Setup_Draw (void) +{ + qpic_t *p; + + M_DrawTransPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") ); + p = Draw_CachePic ("gfx/p_multi.lmp"); + M_DrawPic ( (320-p->width)/2, 4, p); + + M_Print (64, 40, "Your name"); + M_DrawTextBox (160, 32, 16, 1); + M_PrintWhite (168, 40, setup_name); + + M_Print (64, 56, "Your team"); + M_DrawTextBox (160, 48, 16, 1); + M_PrintWhite (168, 56, setup_team); + + M_Print (64, 80, "Shirt color"); + M_Print (64, 104, "Pants color"); + + M_DrawTextBox (64, 140-8, 14, 1); + M_Print (72, 140, "Accept Changes"); + + p = Draw_CachePic ("gfx/bigbox.lmp"); + M_DrawTransPic (160, 64, p); + p = Draw_CachePic ("gfx/menuplyr.lmp"); + M_BuildTranslationTable(setup_top*16, setup_bottom*16); + M_DrawTransPicTranslate (172, 72, p); + + M_DrawCharacter (56, setup_cursor_table [setup_cursor], 12+((int)(realtime*4)&1)); + + if (setup_cursor == 0) + M_DrawCharacter (168 + 8*strlen(setup_name), setup_cursor_table [setup_cursor], 10+((int)(realtime*4)&1)); + + if (setup_cursor == 1) + M_DrawCharacter (168 + 8*strlen(setup_team), setup_cursor_table [setup_cursor], 10+((int)(realtime*4)&1)); +} + + +void M_Setup_Key (int k) +{ + int l; + + switch (k) + { + case K_ESCAPE: + M_Menu_MultiPlayer_f (); + break; + + case K_UPARROW: + S_LocalSound ("misc/menu1.wav"); + setup_cursor--; + if (setup_cursor < 0) + setup_cursor = NUM_SETUP_CMDS-1; + break; + + case K_DOWNARROW: + S_LocalSound ("misc/menu1.wav"); + setup_cursor++; + if (setup_cursor >= NUM_SETUP_CMDS) + setup_cursor = 0; + break; + + case K_HOME: + S_LocalSound ("misc/menu1.wav"); + setup_cursor = 0; + break; + + case K_END: + S_LocalSound ("misc/menu1.wav"); + setup_cursor = NUM_SETUP_CMDS-1; + break; + + case K_LEFTARROW: + if (setup_cursor < 2) + return; + S_LocalSound ("misc/menu3.wav"); + if (setup_cursor == 2) + setup_top = setup_top - 1; + if (setup_cursor == 3) + setup_bottom = setup_bottom - 1; + break; + case K_RIGHTARROW: + if (setup_cursor < 2) + return; +//forward: + S_LocalSound ("misc/menu3.wav"); + if (setup_cursor == 2) + setup_top = setup_top + 1; + if (setup_cursor == 3) + setup_bottom = setup_bottom + 1; + break; + + case K_ENTER: +// if (setup_cursor == 0 || setup_cursor == 1) +// return; + +// if (setup_cursor == 2 || setup_cursor == 3) +// goto forward; + + // setup_cursor == 4 (OK) + Cvar_Set (&name, setup_name); + Cvar_Set (&team, setup_team); + Cvar_Set (&topcolor, va("%i", setup_top)); + Cvar_Set (&bottomcolor, va("%i", setup_bottom)); + m_entersound = true; + M_Menu_MultiPlayer_f (); + break; + + case K_BACKSPACE: + if (setup_cursor == 0) + { + if (strlen(setup_name)) + setup_name[strlen(setup_name)-1] = 0; + } + + if (setup_cursor == 1) + { + if (strlen(setup_team)) + setup_team[strlen(setup_team)-1] = 0; + } + break; + + default: + if (k < 32 || k > 127) + break; + if (setup_cursor == 0) + { + l = strlen(setup_name); + if (l < 15) + { + setup_name[l+1] = 0; + setup_name[l] = k; + } + } + if (setup_cursor == 1) + { + l = strlen(setup_team); + if (l < 15) + { + setup_team[l+1] = 0; + setup_team[l] = k; + } + } + } + + if (setup_top > 13) + setup_top = 0; + if (setup_top < 0) + setup_top = 13; + if (setup_bottom > 13) + setup_bottom = 0; + if (setup_bottom < 0) + setup_bottom = 13; +} + + +// SLIST --> + +#define MENU_X 50 +#define MENU_Y 30 +#define STAT_X 50 +#define STAT_Y 122 + +int m_multip_cursor=0; +int m_multip_mins; +int m_multip_maxs; +int m_multip_horiz; +int m_multip_state; + +void M_Menu_ServerList_f (void) { + key_dest = key_menu; + m_entersound = true; +// m_state = m_multiplayer; + m_state = m_slist; +// m_multip_cursor = 0; + m_multip_mins = 0; + m_multip_maxs = 10; + m_multip_horiz = 0; + m_multip_state = 0; +} + +void M_ServerList_Draw (void) { + int serv; + int line = 1; + qpic_t *p; + //int f; + + M_DrawTransPic(16,4,Draw_CachePic("gfx/qplaque.lmp")); + p = Draw_CachePic("gfx/p_multi.lmp"); + M_DrawPic((320-p->width)/2,4,p); + + if (!(slist[0].server)) { + M_DrawTextBox(60,80,23,4); + M_PrintWhite(110,12*8,"No server list"); + M_PrintWhite(140,13*8,"found."); + return; + } + M_DrawTextBox(STAT_X,STAT_Y,23,4); + //M_DrawTextBox(STAT_X+96,STAT_Y+38,12,3); + M_DrawTextBox(STAT_X,STAT_Y+38,23,3); + M_DrawTextBox(MENU_X,MENU_Y,23,(m_multip_maxs - m_multip_mins)+1); + for (serv = m_multip_mins; serv <= m_multip_maxs; serv++) { + if (slist[serv].server) { + M_Print(MENU_X+18,line*8+MENU_Y, + va("%1.21s", + strlen(slist[serv].description) <= m_multip_horiz ? "" : slist[serv].description+m_multip_horiz)); + line++; + } + } + M_PrintWhite(STAT_X+18,STAT_Y+16,"IP/Hostname:"); + M_Print(STAT_X+18,STAT_Y+24,slist[m_multip_cursor].server); + M_DrawCharacter(MENU_X+8,(m_multip_cursor - m_multip_mins + 1) * 8+MENU_Y, + 12+((int)(realtime*4)&1)); +} + +void M_ServerList_Key (key) +{ +// server_entry_t *pt; + if (!(slist[0].server) && key != K_ESCAPE && key != K_INS) + return; + switch(key) + { + case K_ESCAPE: + M_Menu_MultiPlayer_f(); + break; + + case K_UPARROW: + S_LocalSound("misc/menu1.wav"); + if (m_multip_cursor > 0) + { + if (keydown[K_CTRL]) + { + SList_Switch(m_multip_cursor,m_multip_cursor-1); + m_multip_cursor--; + } + else + m_multip_cursor--; + } + break; + + case K_DOWNARROW: + S_LocalSound("misc/menu1.wav"); + if (keydown[K_CTRL]) + { + if (m_multip_cursor != SList_Len() - 1) { + SList_Switch(m_multip_cursor,m_multip_cursor+1); + m_multip_cursor++; + } + } + else if (m_multip_cursor < (MAX_SERVER_LIST-1) && slist[m_multip_cursor+1].server) { + m_multip_cursor++; + } + break; + + case K_HOME: + S_LocalSound("misc/menu1.wav"); + m_multip_cursor = 0; + break; + + case K_END: + S_LocalSound("misc/menu1.wav"); + m_multip_cursor = SList_Len() - 1; + break; + + case K_PGUP: + S_LocalSound("misc/menu1.wav"); + m_multip_cursor -= (m_multip_maxs - m_multip_mins); + if (m_multip_cursor < 0) + m_multip_cursor = 0; + break; + + case K_PGDN: + S_LocalSound("misc/menu1.wav"); + m_multip_cursor += (m_multip_maxs - m_multip_mins); + if (m_multip_cursor >= MAX_SERVER_LIST) + m_multip_cursor = MAX_SERVER_LIST - 1; + while (!(slist[m_multip_cursor].server)) + m_multip_cursor--; + break; + + case K_RIGHTARROW: + S_LocalSound("misc/menu1.wav"); + if (m_multip_horiz < 256) + m_multip_horiz++; + break; + + case K_LEFTARROW: + S_LocalSound("misc/menu1.wav"); + if (m_multip_horiz > 0 ) + m_multip_horiz--; + break; + + case K_ENTER: + if (keydown[K_CTRL]) + { + M_Menu_SEdit_f(); + break; + } + m_state = m_main; + M_ToggleMenu_f(); + CL_Disconnect(); + Q_strncpyz (cls.servername, slist[m_multip_cursor].server, sizeof(cls.servername)); + CL_BeginServerConnect(); + break; + + case 'e': + case 'E': + M_Menu_SEdit_f(); + break; + + case K_INS: + S_LocalSound("misc/menu2.wav"); + if (SList_Len() < (MAX_SERVER_LIST-1)) { + memmove(&slist[m_multip_cursor+1], + &slist[m_multip_cursor], + (SList_Len() - m_multip_cursor)*sizeof(slist[0])); + SList_Reset_NoFree(m_multip_cursor); + SList_Set(m_multip_cursor,"127.0.0.1",""); + } + break; + + case K_DEL: + S_LocalSound("misc/menu2.wav"); + if (SList_Len() > 0) { + free(slist[m_multip_cursor].server); + free(slist[m_multip_cursor].description); + if (SList_Len()-1 == m_multip_cursor) { + SList_Reset_NoFree(m_multip_cursor); + m_multip_cursor = !m_multip_cursor ? 0 : m_multip_cursor-1; + + } + else { + memmove(&slist[m_multip_cursor], + &slist[m_multip_cursor+1], + (SList_Len()-m_multip_cursor-1) * sizeof(slist[0])); + SList_Reset_NoFree(SList_Len()-1); + } + } + break; + default: + break; + } + if (m_multip_cursor < m_multip_mins) { + m_multip_maxs -= (m_multip_mins - m_multip_cursor); + m_multip_mins = m_multip_cursor; + } + if (m_multip_cursor > m_multip_maxs) { + m_multip_mins += (m_multip_cursor - m_multip_maxs); + m_multip_maxs = m_multip_cursor; + } +} +#define SERV_X 60 +#define SERV_Y 64 +#define DESC_X 60 +#define DESC_Y 40 +#define SERV_L 22 +#define DESC_L 22 + +char serv[256]; +char desc[256]; +int serv_max; +int serv_min; +int desc_max; +int desc_min; +int sedit_state; + +void M_Menu_SEdit_f (void) { + key_dest = key_menu; + m_entersound = true; + m_state = m_sedit; + sedit_state = 0; + Q_strncpyz (serv, slist[m_multip_cursor].server, sizeof(serv)); + Q_strncpyz (desc, slist[m_multip_cursor].description, sizeof(desc)); + serv_max = strlen(serv) > SERV_L ? strlen(serv) : SERV_L; + serv_min = serv_max - (SERV_L); + desc_max = strlen(desc) > DESC_L ? strlen(desc) : DESC_L; + desc_min = desc_max - (DESC_L); +} + +void M_SEdit_Draw (void) { + qpic_t *p; + + M_DrawTransPic(16,4,Draw_CachePic("gfx/qplaque.lmp")); + p = Draw_CachePic("gfx/p_multi.lmp"); + M_DrawPic((320-p->width)/2,4,p); + + M_DrawTextBox(SERV_X,SERV_Y,23,1); + M_DrawTextBox(DESC_X,DESC_Y,23,1); + M_PrintWhite(SERV_X,SERV_Y-4,"Hostname/IP:"); + M_PrintWhite(DESC_X,DESC_Y-4,"Description:"); + M_Print(SERV_X+9,SERV_Y+8,va("%1.22s",serv+serv_min)); + M_Print(DESC_X+9,DESC_Y+8,va("%1.22s",desc+desc_min)); + if (sedit_state == 0) + M_DrawCharacter(SERV_X+9+8*(strlen(serv)-serv_min), + SERV_Y+8,10+((int)(realtime*4)&1)); + if (sedit_state == 1) + M_DrawCharacter(DESC_X+9+8*(strlen(desc)-desc_min), + DESC_Y+8,10+((int)(realtime*4)&1)); +} + +void M_SEdit_Key (int key) { + int l; + switch (key) { + case K_ESCAPE: + M_Menu_ServerList_f (); + break; + case K_ENTER: + SList_Set(m_multip_cursor,serv,desc); + M_Menu_ServerList_f (); + break; + case K_UPARROW: + S_LocalSound("misc/menu1.wav"); + sedit_state = sedit_state == 0 ? 1 : 0; + break; + case K_DOWNARROW: + S_LocalSound("misc/menu1.wav"); + sedit_state = sedit_state == 1 ? 0 : 1; + break; + case K_BACKSPACE: + switch (sedit_state) { + case 0: + if ((l = strlen(serv))) + serv[--l] = 0; + if (strlen(serv)-6 < serv_min && serv_min) { + serv_min--; + serv_max--; + } + break; + case 1: + if ((l = strlen(desc))) + desc[--l] = 0; + if (strlen(desc)-6 < desc_min && desc_min) { + desc_min--; + desc_max--; + } + break; + default: + break; + } + break; + default: + if (key < 32 || key > 127) + break; + switch(sedit_state) { + case 0: + l = strlen(serv); + if (l < 254) { + serv[l+1] = 0; + serv[l] = key; + } + if (strlen(serv) > serv_max) { + serv_min++; + serv_max++; + } + break; + case 1: + l = strlen(desc); + if (l < 254) { + desc[l+1] = 0; + desc[l] = key; + } + if (strlen(desc) > desc_max) { + desc_min++; + desc_max++; + } + break; + } + break; + } +} + +// <-- SLIST + +void M_Quit_Draw (void) +{ +#define VSTR(x) #x +#define VSTR2(x) VSTR(x) + char *cmsg[] = { +// 0123456789012345678901234567890123456789 + "0 QuakeWorld", + "1 version " VSTR2(QW_VERSION) " by id Software", + "0Programming", + "1 John Carmack Michael Abrash", + "1 John Cash Christian Antkow", + "0Additional Programming", + "1 Dave 'Zoid' Kirsch", + "1 Jack 'morbid' Mathews", + "0Id Software is not responsible for", + "0providing technical support for", + "0QUAKEWORLD(tm). (c)1996 Id Software,", + "0Inc. All Rights Reserved.", + "0QUAKEWORLD(tm) is a trademark of Id", + "0Software, Inc.", + "1NOTICE: THE COPYRIGHT AND TRADEMARK", + "1NOTICES APPEARING IN YOUR COPY OF", + "1QUAKE(r) ARE NOT MODIFIED BY THE USE", + "1OF QUAKEWORLD(tm) AND REMAIN IN FULL", + "1FORCE.", + "0NIN(r) is a registered trademark", + "0licensed to Nothing Interactive, Inc.", + "0All rights reserved. Press y to exit", + NULL }; + char **p; + int y; + +/* if (wasInMenus) + { + m_state = m_quit_prevstate; + m_recursiveDraw = true; + M_Draw (); + m_state = m_quit; + }*/ + + M_DrawTextBox (0, 0, 38, 23); + y = 12; + for (p = cmsg; *p; p++, y += 8) { + if (**p == '0') + M_PrintWhite (16, y, *p + 1); + else + M_Print (16, y, *p + 1); + } +} + + + +//============================================================================= +/* Menu Subsystem */ + + +void M_Init (void) +{ + Cvar_RegisterVariable (&scr_centerMenu); + + Cmd_AddCommand ("togglemenu", M_ToggleMenu_f); + + Cmd_AddCommand ("menu_main", M_Menu_Main_f); + Cmd_AddCommand ("menu_multiplayer", M_Menu_MultiPlayer_f); + Cmd_AddCommand ("menu_setup", M_Menu_Setup_f); + Cmd_AddCommand ("menu_demos", M_Menu_Demos_f); + Cmd_AddCommand ("menu_options", M_Menu_Options_f); + Cmd_AddCommand ("menu_keys", M_Menu_Keys_f); + Cmd_AddCommand ("menu_fps", M_Menu_Fps_f); + Cmd_AddCommand ("menu_video", M_Menu_Video_f); + Cmd_AddCommand ("help", M_Menu_Help_f); + Cmd_AddCommand ("menu_help", M_Menu_Help_f); + Cmd_AddCommand ("menu_quit", M_Menu_Quit_f); +} + + +void M_Draw (void) +{ + if (m_state == m_none || key_dest != key_menu) + return; + + if (!m_recursiveDraw) + { + scr_copyeverything = 1; + + if (scr_con_current) + { + Draw_ConsoleBackground (scr_con_current); + VID_UnlockBuffer (); + S_ExtraUpdate (); + VID_LockBuffer (); + } + else + Draw_FadeScreen (); + + scr_fullupdate = 0; + } + else + { + m_recursiveDraw = false; + } + + if (scr_centerMenu.value) + m_yofs = (vid.height - 200) / 2; + + switch (m_state) + { + case m_none: + break; + + case m_main: + M_Main_Draw (); + break; + + case m_singleplayer: + M_SinglePlayer_Draw (); + break; + + case m_load: + M_Load_Draw (); + break; + + case m_save: + M_Save_Draw (); + break; + + case m_multiplayer: + M_MultiPlayer_Draw (); + break; + + case m_setup: + M_Setup_Draw (); + break; + + case m_net: +// M_Net_Draw (); + break; + + case m_options: + M_Options_Draw (); + break; + + case m_keys: + M_Keys_Draw (); + break; + + case m_fps: + M_Fps_Draw (); + break; + + case m_video: + M_Video_Draw (); + break; + + case m_help: + M_Help_Draw (); + break; + + case m_quit: + M_Quit_Draw (); + break; + + case m_serialconfig: +// M_SerialConfig_Draw (); + break; + + case m_modemconfig: +// M_ModemConfig_Draw (); + break; + + case m_lanconfig: +// M_LanConfig_Draw (); + break; + + case m_search: +// M_Search_Draw (); + break; + + case m_slist: + M_ServerList_Draw (); + break; + + case m_sedit: + M_SEdit_Draw (); + break; + + case m_demos: + M_Demos_Draw (); + } + + m_yofs = 0; + + if (m_entersound) + { + S_LocalSound ("misc/menu2.wav"); + m_entersound = false; + } + + VID_UnlockBuffer (); + S_ExtraUpdate (); + VID_LockBuffer (); +} + + +void M_Keydown (int key) +{ + switch (m_state) + { + case m_none: + return; + + case m_main: + M_Main_Key (key); + return; + + case m_singleplayer: + M_SinglePlayer_Key (key); + return; + + case m_load: + M_Load_Key (key); + return; + + case m_save: + M_Save_Key (key); + return; + + case m_multiplayer: + M_MultiPlayer_Key (key); + return; + + case m_setup: + M_Setup_Key (key); + return; + + case m_net: +// M_Net_Key (key); + return; + + case m_options: + M_Options_Key (key); + return; + + case m_keys: + M_Keys_Key (key); + return; + + case m_fps: + M_Fps_Key (key); + return; + + case m_video: + M_Video_Key (key); + return; + + case m_help: + M_Help_Key (key); + return; + + case m_quit: + M_Quit_Key (key); + return; + + case m_serialconfig: +// M_SerialConfig_Key (key); + return; + + case m_modemconfig: +// M_ModemConfig_Key (key); + return; + + case m_lanconfig: +// M_LanConfig_Key (key); + return; + + case m_search: +// M_Search_Key (key); + break; + + case m_slist: + M_ServerList_Key (key); + return; + + case m_sedit: + M_SEdit_Key (key); + break; + + case m_demos: + M_Demos_Key (key); + break; + } +} + + diff --git a/source/menu.h b/source/menu.h index 6b5bc591..8a30d099 100644 --- a/source/menu.h +++ b/source/menu.h @@ -1,41 +1,41 @@ -/* -Copyright (C) 1996-1997 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. - -*/ - -// -// the net drivers should just set the apropriate bits in m_activenet, -// instead of having the menu code look through their internal tables -// -#define MNET_IPX 1 -#define MNET_TCP 2 - -extern int m_activenet; - -// -// menus -// -void M_Init (void); -void M_Keydown (int key); -void M_Draw (void); -void M_ToggleMenu_f (void); -qpic_t *M_CachePic (char *path); -void M_DrawTextBox (int x, int y, int width, int lines); -void M_Menu_Quit_f (void); - - +/* +Copyright (C) 1996-1997 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. + +*/ + +// +// the net drivers should just set the apropriate bits in m_activenet, +// instead of having the menu code look through their internal tables +// +#define MNET_IPX 1 +#define MNET_TCP 2 + +extern int m_activenet; + +// +// menus +// +void M_Init (void); +void M_Keydown (int key); +void M_Draw (void); +void M_ToggleMenu_f (void); +qpic_t *M_CachePic (char *path); +void M_DrawTextBox (int x, int y, int width, int lines); +void M_Menu_Quit_f (void); + + diff --git a/source/model.c b/source/model.c index 5d167d8a..279de47f 100644 --- a/source/model.c +++ b/source/model.c @@ -1,1904 +1,1904 @@ -/* -Copyright (C) 1996-1997 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 "quakedef.h" -#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; - -/* -=============== -Mod_Init -=============== -*/ -void Mod_Init (void) -{ - memset (mod_novis, 0xff, sizeof(mod_novis)); -} - -/* -=============== -Mod_Init - -Caches the data if needed -=============== -*/ -void *Mod_Extradata (model_t *mod) -{ - void *r; - - r = Cache_Check (&mod->cache); - if (r) - return r; - - Mod_LoadModel (mod, true); - - if (!mod->cache.data) - Sys_Error ("Mod_Extradata: caching failed"); - return mod->cache.data; -} - -/* -=============== -Mod_PointInLeaf -=============== -*/ -mleaf_t *Mod_PointInLeaf (vec3_t p, model_t *model) -{ - mnode_t *node; - float d; - mplane_t *plane; - - if (!model || !model->nodes) - Sys_Error ("Mod_PointInLeaf: bad model"); - - node = model->nodes; - while (1) - { - if (node->contents < 0) - 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->numleafs+7)>>3; - out = decompressed; - -#if 0 - memcpy (out, in, row); -#else - 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); -#endif - - return decompressed; -} - -byte *Mod_LeafPVS (mleaf_t *leaf, model_t *model) -{ - if (leaf == model->leafs) - return mod_novis; - return Mod_DecompressVis (leaf->compressed_vis, model); -} - -/* -=================== -Mod_ClearAll -=================== -*/ -void Mod_ClearAll (void) -{ - int i; - model_t *mod; - - for (i=0 , mod=mod_known ; itype != mod_alias) - mod->needload = true; -} - -/* -================== -Mod_FindName - -================== -*/ -model_t *Mod_FindName (char *name) -{ - int i; - model_t *mod; - - if (!name[0]) - Sys_Error ("Mod_ForName: NULL name"); - -// -// search the currently loaded models -// - for (i=0 , mod=mod_known ; iname, name) ) - break; - - if (i == mod_numknown) - { - if (mod_numknown == MAX_MOD_KNOWN) - Sys_Error ("mod_numknown == MAX_MOD_KNOWN"); - strcpy (mod->name, name); - mod->needload = true; - mod_numknown++; - } - - return mod; -} - -/* -================== -Mod_TouchModel - -================== -*/ -void Mod_TouchModel (char *name) -{ - model_t *mod; - - mod = Mod_FindName (name); - - if (!mod->needload) - { - if (mod->type == mod_alias) - Cache_Check (&mod->cache); - } -} - -/* -================== -Mod_LoadModel - -Loads a model into the cache -================== -*/ -model_t *Mod_LoadModel (model_t *mod, qboolean crash) -{ - void *d; - unsigned *buf; - byte stackbuf[1024]; // avoid dirtying the cache heap - - if (!mod->needload) - { - if (mod->type == mod_alias) - { - d = Cache_Check (&mod->cache); - if (d) - return mod; - } - else - return mod; // not cached at all - } - -// -// because the world is so huge, load it one piece at a time -// - if (!crash) - { - - } - -// -// load the file -// - buf = (unsigned *)COM_LoadStackFile (mod->name, stackbuf, sizeof(stackbuf)); - if (!buf) - { - if (crash) - Sys_Error ("Mod_NumForName: %s not found", mod->name); - return NULL; - } - -// -// allocate a new model -// - COM_FileBase (mod->name, loadname); - - loadmodel = mod; - -// -// fill it in -// - -// call the apropriate loader - mod->needload = false; - mod->modhint = 0; - - switch (LittleLong(*(unsigned *)buf)) - { - case IDPOLYHEADER: - Mod_LoadAliasModel (mod, buf); - break; - - case IDSPRITEHEADER: - Mod_LoadSpriteModel (mod, buf); - break; - - default: - Mod_LoadBrushModel (mod, buf); - break; - } - - return mod; -} - -/* -================== -Mod_ForName - -Loads in a model for the given name -================== -*/ -model_t *Mod_ForName (char *name, qboolean crash) -{ - model_t *mod; - - mod = Mod_FindName (name); - - return Mod_LoadModel (mod, crash); -} - - -/* -=============================================================================== - - BRUSHMODEL LOADING - -=============================================================================== -*/ - -byte *mod_base; - - -/* -================= -Mod_LoadTextures -================= -*/ -void Mod_LoadTextures (lump_t *l) -{ - int i, j, pixels, num, max, altmax; - miptex_t *mt; - texture_t *tx, *tx2; - texture_t *anims[10]; - texture_t *altanims[10]; - dmiptexlump_t *m; - - if (!l->filelen) - { - loadmodel->textures = NULL; - return; - } - m = (dmiptexlump_t *)(mod_base + l->fileofs); - - m->nummiptex = LittleLong (m->nummiptex); - - loadmodel->numtextures = m->nummiptex; - loadmodel->textures = Hunk_AllocName (m->nummiptex * sizeof(*loadmodel->textures) , loadname); - - for (i=0 ; inummiptex ; i++) - { - m->dataofs[i] = LittleLong(m->dataofs[i]); - if (m->dataofs[i] == -1) - continue; - mt = (miptex_t *)((byte *)m + m->dataofs[i]); - mt->width = LittleLong (mt->width); - mt->height = LittleLong (mt->height); - for (j=0 ; joffsets[j] = LittleLong (mt->offsets[j]); - - if ( (mt->width & 15) || (mt->height & 15) ) - Sys_Error ("Texture %s is not 16 aligned", mt->name); - pixels = mt->width*mt->height/64*85; - tx = Hunk_AllocName (sizeof(texture_t) +pixels, loadname ); - loadmodel->textures[i] = tx; - - memcpy (tx->name, mt->name, sizeof(tx->name)); - tx->width = mt->width; - tx->height = mt->height; - for (j=0 ; joffsets[j] = mt->offsets[j] + sizeof(texture_t) - sizeof(miptex_t); - // the pixels immediately follow the structures - memcpy ( tx+1, mt+1, pixels); - - if (!strncmp(mt->name,"sky",3)) - R_InitSky (tx); - } - -// -// sequence the animations -// - for (i=0 ; inummiptex ; i++) - { - tx = loadmodel->textures[i]; - if (!tx || tx->name[0] != '+') - continue; - if (tx->anim_next) - continue; // already sequenced - - // find the number of frames in the animation - memset (anims, 0, sizeof(anims)); - memset (altanims, 0, sizeof(altanims)); - - max = tx->name[1]; - altmax = 0; - if (max >= 'a' && max <= 'z') - max -= 'a' - 'A'; - if (max >= '0' && max <= '9') - { - max -= '0'; - altmax = 0; - anims[max] = tx; - max++; - } - else if (max >= 'A' && max <= 'J') - { - altmax = max - 'A'; - max = 0; - altanims[altmax] = tx; - altmax++; - } - else - Sys_Error ("Bad animating texture %s", tx->name); - - for (j=i+1 ; jnummiptex ; j++) - { - tx2 = loadmodel->textures[j]; - if (!tx2 || tx2->name[0] != '+') - continue; - if (strcmp (tx2->name+2, tx->name+2)) - continue; - - num = tx2->name[1]; - if (num >= 'a' && num <= 'z') - num -= 'a' - 'A'; - if (num >= '0' && num <= '9') - { - num -= '0'; - anims[num] = tx2; - if (num+1 > max) - max = num + 1; - } - else if (num >= 'A' && num <= 'J') - { - num = num - 'A'; - altanims[num] = tx2; - if (num+1 > altmax) - altmax = num+1; - } - else - Sys_Error ("Bad animating texture %s", tx->name); - } - -#define ANIM_CYCLE 2 - // link them all together - for (j=0 ; jname); - tx2->anim_total = max * ANIM_CYCLE; - tx2->anim_min = j * ANIM_CYCLE; - tx2->anim_max = (j+1) * ANIM_CYCLE; - tx2->anim_next = anims[ (j+1)%max ]; - if (altmax) - tx2->alternate_anims = altanims[0]; - } - for (j=0 ; jname); - tx2->anim_total = altmax * ANIM_CYCLE; - tx2->anim_min = j * ANIM_CYCLE; - tx2->anim_max = (j+1) * ANIM_CYCLE; - tx2->anim_next = altanims[ (j+1)%altmax ]; - if (max) - tx2->alternate_anims = anims[0]; - } - } -} - -/* -================= -Mod_LoadLighting -================= -*/ -void Mod_LoadLighting (lump_t *l) -{ - if (!l->filelen) - { - loadmodel->lightdata = NULL; - return; - } - loadmodel->lightdata = Hunk_AllocName ( l->filelen, loadname); - memcpy (loadmodel->lightdata, mod_base + l->fileofs, l->filelen); -} - - -/* -================= -Mod_LoadVisibility -================= -*/ -void Mod_LoadVisibility (lump_t *l) -{ - if (!l->filelen) - { - loadmodel->visdata = NULL; - return; - } - loadmodel->visdata = Hunk_AllocName ( l->filelen, loadname); - memcpy (loadmodel->visdata, mod_base + l->fileofs, l->filelen); -} - - -/* -================= -Mod_LoadEntities -================= -*/ -void Mod_LoadEntities (lump_t *l) -{ - if (!l->filelen) - { - loadmodel->entities = NULL; - return; - } - loadmodel->entities = Hunk_AllocName ( l->filelen, loadname); - memcpy (loadmodel->entities, mod_base + l->fileofs, l->filelen); -} - - -/* -================= -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)) - Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); - count = l->filelen / sizeof(*in); - out = Hunk_AllocName ( count*sizeof(*out), loadname); - - 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)) - Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); - count = l->filelen / sizeof(*in); - out = Hunk_AllocName ( count*sizeof(*out), loadname); - - 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]); - } - for (j=0 ; jheadnode[j] = LittleLong (in->headnode[j]); - out->visleafs = LittleLong (in->visleafs); - 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)) - Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); - count = l->filelen / sizeof(*in); - out = Hunk_AllocName ( (count + 1) * sizeof(*out), loadname); - - 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; - int i, j, count; - int miptex; - float len1, len2; - - in = (void *)(mod_base + l->fileofs); - if (l->filelen % sizeof(*in)) - Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); - count = l->filelen / sizeof(*in); - out = Hunk_AllocName ( count*sizeof(*out), loadname); - - loadmodel->texinfo = out; - loadmodel->numtexinfo = count; - - for ( i=0 ; ivecs[0][j] = LittleFloat (in->vecs[0][j]); - len1 = Length (out->vecs[0]); - len2 = Length (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; -#if 0 - if (len1 + len2 < 0.001) - out->mipadjust = 1; // don't crash - else - out->mipadjust = 1 / floor( (len1+len2)/2 + 0.1 ); -#endif - - miptex = LittleLong (in->miptex); - out->flags = LittleLong (in->flags); - - if (!loadmodel->textures) - { - out->texture = r_notexture_mip; // checkerboard texture - out->flags = 0; - } - else - { - if (miptex >= loadmodel->numtextures) - Sys_Error ("miptex >= loadmodel->numtextures"); - out->texture = loadmodel->textures[miptex]; - if (!out->texture) - { - out->texture = r_notexture_mip; // texture not found - out->flags = 0; - } - } - } -} - -/* -================ -CalcSurfaceExtents - -Fills in s->texturemins[] and s->extents[] -================ -*/ -void CalcSurfaceExtents (msurface_t *s) -{ - float mins[2], maxs[2], val; - int i,j, e; - mvertex_t *v; - 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++) - { - 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 ( !(tex->flags & TEX_SPECIAL) && s->extents[i] > 256) - Sys_Error ("Bad surface extents"); - } -} - - -/* -================= -Mod_LoadFaces -================= -*/ -void Mod_LoadFaces (lump_t *l) -{ - dface_t *in; - msurface_t *out; - int i, count, surfnum; - int planenum, side; - - in = (void *)(mod_base + l->fileofs); - if (l->filelen % sizeof(*in)) - Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); - count = l->filelen / sizeof(*in); - out = Hunk_AllocName ( count*sizeof(*out), loadname); - - loadmodel->surfaces = out; - loadmodel->numsurfaces = count; - - for ( surfnum=0 ; surfnumfirstedge = LittleLong(in->firstedge); - out->numedges = LittleShort(in->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 - - for (i=0 ; istyles[i] = in->styles[i]; - i = LittleLong(in->lightofs); - if (i == -1) - out->samples = NULL; - else - out->samples = loadmodel->lightdata + i; - - // set the drawing flags flag - - if (!strncmp(out->texinfo->texture->name,"sky",3)) // sky - { - out->flags |= (SURF_DRAWSKY | SURF_DRAWTILED); - continue; - } - - if (!strncmp(out->texinfo->texture->name,"*",1)) // turbulent - { - out->flags |= (SURF_DRAWTURB | SURF_DRAWTILED); - for (i=0 ; i<2 ; i++) - { - out->extents[i] = 16384; - out->texturemins[i] = -8192; - } - continue; - } - } -} - - -/* -================= -Mod_SetParent -================= -*/ -void Mod_SetParent (mnode_t *node, mnode_t *parent) -{ - node->parent = parent; - if (node->contents < 0) - return; - Mod_SetParent (node->children[0], node); - Mod_SetParent (node->children[1], node); -} - -/* -================= -Mod_LoadNodes -================= -*/ -void Mod_LoadNodes (lump_t *l) -{ - int i, j, count, p; - dnode_t *in; - mnode_t *out; - - in = (void *)(mod_base + l->fileofs); - if (l->filelen % sizeof(*in)) - Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); - count = l->filelen / sizeof(*in); - out = Hunk_AllocName ( count*sizeof(*out), loadname); - - 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); - - for (j=0 ; j<2 ; j++) - { - p = LittleShort (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, p; - - in = (void *)(mod_base + l->fileofs); - if (l->filelen % sizeof(*in)) - Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); - count = l->filelen / sizeof(*in); - out = Hunk_AllocName ( count*sizeof(*out), loadname); - - loadmodel->leafs = out; - loadmodel->numleafs = count; - - for ( i=0 ; iminmaxs[j] = LittleShort (in->mins[j]); - out->minmaxs[3+j] = LittleShort (in->maxs[j]); - } - - p = LittleLong(in->contents); - out->contents = p; - - out->firstmarksurface = loadmodel->marksurfaces + - LittleShort(in->firstmarksurface); - out->nummarksurfaces = LittleShort(in->nummarksurfaces); - - p = LittleLong(in->visofs); - if (p == -1) - out->compressed_vis = NULL; - else - out->compressed_vis = loadmodel->visdata + p; - out->efrags = NULL; - - for (j=0 ; j<4 ; j++) - out->ambient_sound_level[j] = in->ambient_level[j]; - } -} - -/* -================= -Mod_LoadClipnodes -================= -*/ -void Mod_LoadClipnodes (lump_t *l) -{ - dclipnode_t *in, *out; - int i, count; - hull_t *hull; - - in = (void *)(mod_base + l->fileofs); - if (l->filelen % sizeof(*in)) - Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); - count = l->filelen / sizeof(*in); - out = Hunk_AllocName ( count*sizeof(*out), loadname); - - loadmodel->clipnodes = out; - loadmodel->numclipnodes = count; - - hull = &loadmodel->hulls[1]; - hull->clipnodes = out; - hull->firstclipnode = 0; - hull->lastclipnode = count-1; - hull->planes = loadmodel->planes; - hull->clip_mins[0] = -16; - hull->clip_mins[1] = -16; - hull->clip_mins[2] = -24; - hull->clip_maxs[0] = 16; - hull->clip_maxs[1] = 16; - hull->clip_maxs[2] = 32; - - hull = &loadmodel->hulls[2]; - hull->clipnodes = out; - hull->firstclipnode = 0; - hull->lastclipnode = count-1; - hull->planes = loadmodel->planes; - hull->clip_mins[0] = -32; - hull->clip_mins[1] = -32; - hull->clip_mins[2] = -24; - hull->clip_maxs[0] = 32; - hull->clip_maxs[1] = 32; - hull->clip_maxs[2] = 64; - - for (i=0 ; iplanenum = LittleLong(in->planenum); - out->children[0] = LittleShort(in->children[0]); - out->children[1] = LittleShort(in->children[1]); - } -} - -/* -================= -Mod_MakeHull0 - -Deplicate the drawing hull structure as a clipping hull -================= -*/ -void Mod_MakeHull0 (void) -{ - mnode_t *in, *child; - dclipnode_t *out; - int i, j, count; - hull_t *hull; - - hull = &loadmodel->hulls[0]; - - in = loadmodel->nodes; - count = loadmodel->numnodes; - out = Hunk_AllocName ( count*sizeof(*out), loadname); - - hull->clipnodes = out; - hull->firstclipnode = 0; - hull->lastclipnode = count-1; - hull->planes = loadmodel->planes; - - for (i=0 ; iplanenum = in->plane - loadmodel->planes; - for (j=0 ; j<2 ; j++) - { - child = in->children[j]; - if (child->contents < 0) - out->children[j] = child->contents; - else - out->children[j] = child - loadmodel->nodes; - } - } -} - -/* -================= -Mod_LoadMarksurfaces -================= -*/ -void Mod_LoadMarksurfaces (lump_t *l) -{ - int i, j, count; - short *in; - msurface_t **out; - - in = (void *)(mod_base + l->fileofs); - if (l->filelen % sizeof(*in)) - Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); - count = l->filelen / sizeof(*in); - out = Hunk_AllocName ( count*sizeof(*out), loadname); - - loadmodel->marksurfaces = out; - loadmodel->nummarksurfaces = count; - - for ( i=0 ; i= loadmodel->numsurfaces) - Sys_Error ("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)) - Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); - count = l->filelen / sizeof(*in); - out = Hunk_AllocName ( count*sizeof(*out), loadname); - - loadmodel->surfedges = out; - loadmodel->numsurfedges = count; - - for ( i=0 ; ifileofs); - if (l->filelen % sizeof(*in)) - Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); - count = l->filelen / sizeof(*in); - out = Hunk_AllocName ( count*2*sizeof(*out), loadname); - - 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; - } -} - -/* -================= -RadiusFromBounds -================= -*/ -float RadiusFromBounds (vec3_t mins, vec3_t maxs) -{ - int i; - vec3_t corner; - - for (i=0 ; i<3 ; i++) - { - corner[i] = fabs(mins[i]) > fabs(maxs[i]) ? fabs(mins[i]) : fabs(maxs[i]); - } - - return Length (corner); -} - -/* -================= -Mod_LoadBrushModel -================= -*/ -void Mod_LoadBrushModel (model_t *mod, void *buffer) -{ - int i, j; - dheader_t *header; - dmodel_t *bm; - - loadmodel->type = mod_brush; - - header = (dheader_t *)buffer; - - i = LittleLong (header->version); - if (i != BSPVERSION) - Sys_Error ("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 ; ichecksum = 0; - mod->checksum2 = 0; - -// checksum all of the map, except for entities - for (i = 0; i < HEADER_LUMPS; i++) { - if (i == LUMP_ENTITIES) - continue; - mod->checksum ^= Com_BlockChecksum(mod_base + header->lumps[i].fileofs, - header->lumps[i].filelen); - - if (i == LUMP_VISIBILITY || i == LUMP_LEAFS || i == LUMP_NODES) - continue; - mod->checksum2 ^= Com_BlockChecksum(mod_base + header->lumps[i].fileofs, - header->lumps[i].filelen); - } - -// load into heap - - Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]); - Mod_LoadEdges (&header->lumps[LUMP_EDGES]); - Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]); - Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]); - 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_MARKSURFACES]); - Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]); - Mod_LoadLeafs (&header->lumps[LUMP_LEAFS]); - Mod_LoadNodes (&header->lumps[LUMP_NODES]); - Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES]); - Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]); - Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]); - - Mod_MakeHull0 (); - - mod->numframes = 2; // regular and alternate animation - -// -// set up the submodels (FIXME: this is confusing) -// - for (i=0 ; inumsubmodels ; i++) - { - bm = &mod->submodels[i]; - - mod->hulls[0].firstclipnode = bm->headnode[0]; - for (j=1 ; jhulls[j].firstclipnode = bm->headnode[j]; - mod->hulls[j].lastclipnode = mod->numclipnodes-1; - } - - mod->firstmodelsurface = bm->firstface; - mod->nummodelsurfaces = bm->numfaces; - mod->radius = RadiusFromBounds (mod->mins, mod->maxs); - - VectorCopy (bm->maxs, mod->maxs); - VectorCopy (bm->mins, mod->mins); - - mod->numleafs = bm->visleafs; - - if (i < mod->numsubmodels-1) - { // duplicate the basic information - char name[10]; - - sprintf (name, "*%i", i+1); - loadmodel = Mod_FindName (name); - *loadmodel = *mod; - strcpy (loadmodel->name, name); - mod = loadmodel; - } - } -} - -/* -============================================================================== - -ALIAS MODELS - -============================================================================== -*/ - -/* -================= -Mod_LoadAliasFrame -================= -*/ -void * Mod_LoadAliasFrame (void * pin, int *pframeindex, int numv, - trivertx_t *pbboxmin, trivertx_t *pbboxmax, aliashdr_t *pheader, char *name) -{ - trivertx_t *pframe, *pinframe; - int i, j; - daliasframe_t *pdaliasframe; - - pdaliasframe = (daliasframe_t *)pin; - - strcpy (name, pdaliasframe->name); - - for (i=0 ; i<3 ; i++) - { - // these are byte values, so we don't have to worry about - // endianness - pbboxmin->v[i] = pdaliasframe->bboxmin.v[i]; - pbboxmax->v[i] = pdaliasframe->bboxmax.v[i]; - } - - pinframe = (trivertx_t *)(pdaliasframe + 1); - pframe = Hunk_AllocName (numv * sizeof(*pframe), loadname); - - *pframeindex = (byte *)pframe - (byte *)pheader; - - for (j=0 ; jnumframes); - - paliasgroup = Hunk_AllocName (sizeof (maliasgroup_t) + - (numframes - 1) * sizeof (paliasgroup->frames[0]), loadname); - - paliasgroup->numframes = numframes; - - for (i=0 ; i<3 ; i++) - { - // these are byte values, so we don't have to worry about endianness - pbboxmin->v[i] = pingroup->bboxmin.v[i]; - pbboxmax->v[i] = pingroup->bboxmax.v[i]; - } - - *pframeindex = (byte *)paliasgroup - (byte *)pheader; - - pin_intervals = (daliasinterval_t *)(pingroup + 1); - - poutintervals = Hunk_AllocName (numframes * sizeof (float), loadname); - - paliasgroup->intervals = (byte *)poutintervals - (byte *)pheader; - - for (i=0 ; iinterval); - if (*poutintervals <= 0.0) - Sys_Error ("Mod_LoadAliasGroup: interval<=0"); - - poutintervals++; - pin_intervals++; - } - - ptemp = (void *)pin_intervals; - - for (i=0 ; iframes[i].frame, - numv, - &paliasgroup->frames[i].bboxmin, - &paliasgroup->frames[i].bboxmax, - pheader, name); - } - - return ptemp; -} - - -/* -================= -Mod_LoadAliasSkin -================= -*/ -void * Mod_LoadAliasSkin (void * pin, int *pskinindex, int skinsize, - aliashdr_t *pheader) -{ - int i; - byte *pskin, *pinskin; - unsigned short *pusskin; - - pskin = Hunk_AllocName (skinsize * r_pixbytes, loadname); - pinskin = (byte *)pin; - *pskinindex = (byte *)pskin - (byte *)pheader; - - if (r_pixbytes == 1) - { - memcpy (pskin, pinskin, skinsize); - } - else if (r_pixbytes == 2) - { - pusskin = (unsigned short *)pskin; - - for (i=0 ; inumskins); - - paliasskingroup = Hunk_AllocName (sizeof (maliasskingroup_t) + - (numskins - 1) * sizeof (paliasskingroup->skindescs[0]), - loadname); - - paliasskingroup->numskins = numskins; - - *pskinindex = (byte *)paliasskingroup - (byte *)pheader; - - pinskinintervals = (daliasskininterval_t *)(pinskingroup + 1); - - poutskinintervals = Hunk_AllocName (numskins * sizeof (float),loadname); - - paliasskingroup->intervals = (byte *)poutskinintervals - (byte *)pheader; - - for (i=0 ; iinterval); - if (*poutskinintervals <= 0) - Sys_Error ("Mod_LoadAliasSkinGroup: interval<=0"); - - poutskinintervals++; - pinskinintervals++; - } - - ptemp = (void *)pinskinintervals; - - for (i=0 ; iskindescs[i].skin, skinsize, pheader); - } - - return ptemp; -} - - -/* -================= -Mod_LoadAliasModel -================= -*/ -void Mod_LoadAliasModel (model_t *mod, void *buffer) -{ - int i; - mdl_t *pmodel, *pinmodel; - stvert_t *pstverts, *pinstverts; - aliashdr_t *pheader; - mtriangle_t *ptri; - dtriangle_t *pintriangles; - int version, numframes, numskins; - int size; - daliasframetype_t *pframetype; - daliasskintype_t *pskintype; - maliasskindesc_t *pskindesc; - int skinsize; - int start, end, total; - -// some models are special - if(!strcmp(mod->name, "progs/player.mdl")) - mod->modhint = MOD_PLAYER; - else if(!strcmp(mod->name, "progs/eyes.mdl")) - mod->modhint = MOD_EYES; - else if (!strcmp(mod->name, "progs/flame.mdl") || - !strcmp(mod->name, "progs/flame2.mdl")) - mod->modhint = MOD_FLAME; -/* else if (!strcmp(mod->name, "progs/bolt.mdl") || - !strcmp(mod->name, "progs/bolt2.mdl") || - !strcmp(mod->name, "progs/bolt3.mdl")) - mod->modhint = MOD_THUNDERBOLT; */ - - if (mod->modhint == MOD_PLAYER || mod->modhint == MOD_EYES) { - unsigned short crc; - byte *p; - int len; - char st[40]; - - CRC_Init(&crc); - for (len = com_filesize, p = buffer; len; len--, p++) - CRC_ProcessByte(&crc, *p); - - sprintf(st, "%d", (int) crc); - Info_SetValueForKey (cls.userinfo, - mod->modhint == MOD_PLAYER ? pmodel_name : emodel_name, - st, MAX_INFO_STRING); - - if (cls.state >= ca_connected) { - MSG_WriteByte (&cls.netchan.message, clc_stringcmd); - sprintf(st, "setinfo %s %d", - mod->modhint == MOD_PLAYER ? pmodel_name : emodel_name, - (int)crc); - SZ_Print (&cls.netchan.message, st); - } - } - - start = Hunk_LowMark (); - - pinmodel = (mdl_t *)buffer; - - version = LittleLong (pinmodel->version); - if (version != ALIAS_VERSION) - Sys_Error ("%s has wrong version number (%i should be %i)", - mod->name, version, ALIAS_VERSION); - -// -// allocate space for a working header, plus all the data except the frames, -// skin and group info -// - size = sizeof (aliashdr_t) + (LittleLong (pinmodel->numframes) - 1) * - sizeof (pheader->frames[0]) + - sizeof (mdl_t) + - LittleLong (pinmodel->numverts) * sizeof (stvert_t) + - LittleLong (pinmodel->numtris) * sizeof (mtriangle_t); - - pheader = Hunk_AllocName (size, loadname); - pmodel = (mdl_t *) ((byte *)&pheader[1] + - (LittleLong (pinmodel->numframes) - 1) * - sizeof (pheader->frames[0])); - -// mod->cache.data = pheader; - mod->flags = LittleLong (pinmodel->flags); - -// -// endian-adjust and copy the data, starting with the alias model header -// - pmodel->boundingradius = LittleFloat (pinmodel->boundingradius); - pmodel->numskins = LittleLong (pinmodel->numskins); - pmodel->skinwidth = LittleLong (pinmodel->skinwidth); - pmodel->skinheight = LittleLong (pinmodel->skinheight); - - if (pmodel->skinheight > MAX_LBM_HEIGHT) - Sys_Error ("model %s has a skin taller than %d", mod->name, - MAX_LBM_HEIGHT); - - pmodel->numverts = LittleLong (pinmodel->numverts); - - if (pmodel->numverts <= 0) - Sys_Error ("model %s has no vertices", mod->name); - - if (pmodel->numverts > MAXALIASVERTS) - Sys_Error ("model %s has too many vertices", mod->name); - - pmodel->numtris = LittleLong (pinmodel->numtris); - - if (pmodel->numtris <= 0) - Sys_Error ("model %s has no triangles", mod->name); - - pmodel->numframes = LittleLong (pinmodel->numframes); - pmodel->size = LittleFloat (pinmodel->size) * ALIAS_BASE_SIZE_RATIO; - mod->synctype = LittleLong (pinmodel->synctype); - mod->numframes = pmodel->numframes; - - for (i=0 ; i<3 ; i++) - { - pmodel->scale[i] = LittleFloat (pinmodel->scale[i]); - pmodel->scale_origin[i] = LittleFloat (pinmodel->scale_origin[i]); - pmodel->eyeposition[i] = LittleFloat (pinmodel->eyeposition[i]); - } - - numskins = pmodel->numskins; - numframes = pmodel->numframes; - - if (pmodel->skinwidth & 0x03) - Sys_Error ("Mod_LoadAliasModel: skinwidth not multiple of 4"); - - pheader->model = (byte *)pmodel - (byte *)pheader; - -// -// load the skins -// - skinsize = pmodel->skinheight * pmodel->skinwidth; - - if (numskins < 1) - Sys_Error ("Mod_LoadAliasModel: Invalid # of skins: %d\n", numskins); - - pskintype = (daliasskintype_t *)&pinmodel[1]; - - pskindesc = Hunk_AllocName (numskins * sizeof (maliasskindesc_t), - loadname); - - pheader->skindesc = (byte *)pskindesc - (byte *)pheader; - - for (i=0 ; itype); - pskindesc[i].type = skintype; - - if (skintype == ALIAS_SKIN_SINGLE) - { - pskintype = (daliasskintype_t *) - Mod_LoadAliasSkin (pskintype + 1, - &pskindesc[i].skin, - skinsize, pheader); - } - else - { - pskintype = (daliasskintype_t *) - Mod_LoadAliasSkinGroup (pskintype + 1, - &pskindesc[i].skin, - skinsize, pheader); - } - } - -// -// set base s and t vertices -// - pstverts = (stvert_t *)&pmodel[1]; - pinstverts = (stvert_t *)pskintype; - - pheader->stverts = (byte *)pstverts - (byte *)pheader; - - for (i=0 ; inumverts ; i++) - { - pstverts[i].onseam = LittleLong (pinstverts[i].onseam); - // put s and t in 16.16 format - pstverts[i].s = LittleLong (pinstverts[i].s) << 16; - pstverts[i].t = LittleLong (pinstverts[i].t) << 16; - } - -// -// set up the triangles -// - ptri = (mtriangle_t *)&pstverts[pmodel->numverts]; - pintriangles = (dtriangle_t *)&pinstverts[pmodel->numverts]; - - pheader->triangles = (byte *)ptri - (byte *)pheader; - - for (i=0 ; inumtris ; i++) - { - int j; - - ptri[i].facesfront = LittleLong (pintriangles[i].facesfront); - - for (j=0 ; j<3 ; j++) - { - ptri[i].vertindex[j] = - LittleLong (pintriangles[i].vertindex[j]); - } - } - -// -// load the frames -// - if (numframes < 1) - Sys_Error ("Mod_LoadAliasModel: Invalid # of frames: %d\n", numframes); - - pframetype = (daliasframetype_t *)&pintriangles[pmodel->numtris]; - - for (i=0 ; itype); - pheader->frames[i].type = frametype; - - - if (frametype == ALIAS_SINGLE) - { - pframetype = (daliasframetype_t *) - Mod_LoadAliasFrame (pframetype + 1, - &pheader->frames[i].frame, - pmodel->numverts, - &pheader->frames[i].bboxmin, - &pheader->frames[i].bboxmax, - pheader, pheader->frames[i].name); - } - else - { - pframetype = (daliasframetype_t *) - Mod_LoadAliasGroup (pframetype + 1, - &pheader->frames[i].frame, - pmodel->numverts, - &pheader->frames[i].bboxmin, - &pheader->frames[i].bboxmax, - pheader, pheader->frames[i].name); - } - } - - mod->type = mod_alias; - -// FIXME: do this right - mod->mins[0] = mod->mins[1] = mod->mins[2] = -16; - mod->maxs[0] = mod->maxs[1] = mod->maxs[2] = 16; - -// -// move the complete, relocatable alias model to the cache -// - end = Hunk_LowMark (); - total = end - start; - - Cache_Alloc (&mod->cache, total, loadname); - if (!mod->cache.data) - return; - memcpy (mod->cache.data, pheader, total); - - Hunk_FreeToLowMark (start); -} - -//============================================================================= - -/* -================= -Mod_LoadSpriteFrame -================= -*/ -void * Mod_LoadSpriteFrame (void * pin, mspriteframe_t **ppframe) -{ - dspriteframe_t *pinframe; - mspriteframe_t *pspriteframe; - int i, width, height, size, origin[2]; - unsigned short *ppixout; - byte *ppixin; - - pinframe = (dspriteframe_t *)pin; - - width = LittleLong (pinframe->width); - height = LittleLong (pinframe->height); - size = width * height; - - pspriteframe = Hunk_AllocName (sizeof (mspriteframe_t) + size*r_pixbytes, - loadname); - - memset (pspriteframe, 0, sizeof (mspriteframe_t) + size); - *ppframe = pspriteframe; - - pspriteframe->width = width; - pspriteframe->height = height; - origin[0] = LittleLong (pinframe->origin[0]); - origin[1] = LittleLong (pinframe->origin[1]); - - pspriteframe->up = origin[1]; - pspriteframe->down = origin[1] - height; - pspriteframe->left = origin[0]; - pspriteframe->right = width + origin[0]; - - if (r_pixbytes == 1) - { - memcpy (&pspriteframe->pixels[0], (byte *)(pinframe + 1), size); - } - else if (r_pixbytes == 2) - { - ppixin = (byte *)(pinframe + 1); - ppixout = (unsigned short *)&pspriteframe->pixels[0]; - - for (i=0 ; inumframes); - - pspritegroup = Hunk_AllocName (sizeof (mspritegroup_t) + - (numframes - 1) * sizeof (pspritegroup->frames[0]), loadname); - - pspritegroup->numframes = numframes; - - *ppframe = (mspriteframe_t *)pspritegroup; - - pin_intervals = (dspriteinterval_t *)(pingroup + 1); - - poutintervals = Hunk_AllocName (numframes * sizeof (float), loadname); - - pspritegroup->intervals = poutintervals; - - for (i=0 ; iinterval); - if (*poutintervals <= 0.0) - Sys_Error ("Mod_LoadSpriteGroup: interval<=0"); - - poutintervals++; - pin_intervals++; - } - - ptemp = (void *)pin_intervals; - - for (i=0 ; iframes[i]); - } - - return ptemp; -} - - -/* -================= -Mod_LoadSpriteModel -================= -*/ -void Mod_LoadSpriteModel (model_t *mod, void *buffer) -{ - int i; - int version; - dsprite_t *pin; - msprite_t *psprite; - int numframes; - int size; - dspriteframetype_t *pframetype; - - pin = (dsprite_t *)buffer; - - version = LittleLong (pin->version); - if (version != SPRITE_VERSION) - Sys_Error ("%s has wrong version number " - "(%i should be %i)", mod->name, version, SPRITE_VERSION); - - numframes = LittleLong (pin->numframes); - - size = sizeof (msprite_t) + (numframes - 1) * sizeof (psprite->frames); - - psprite = Hunk_AllocName (size, loadname); - - mod->cache.data = psprite; - - psprite->type = LittleLong (pin->type); - psprite->maxwidth = LittleLong (pin->width); - psprite->maxheight = LittleLong (pin->height); - psprite->beamlength = LittleFloat (pin->beamlength); - mod->synctype = LittleLong (pin->synctype); - psprite->numframes = numframes; - - mod->mins[0] = mod->mins[1] = -psprite->maxwidth/2; - mod->maxs[0] = mod->maxs[1] = psprite->maxwidth/2; - mod->mins[2] = -psprite->maxheight/2; - mod->maxs[2] = psprite->maxheight/2; - -// -// load the frames -// - if (numframes < 1) - Sys_Error ("Mod_LoadSpriteModel: Invalid # of frames: %d\n", numframes); - - mod->numframes = numframes; - - pframetype = (dspriteframetype_t *)(pin + 1); - - for (i=0 ; itype); - psprite->frames[i].type = frametype; - - if (frametype == SPR_SINGLE) - { - pframetype = (dspriteframetype_t *) - Mod_LoadSpriteFrame (pframetype + 1, - &psprite->frames[i].frameptr); - } - else - { - pframetype = (dspriteframetype_t *) - Mod_LoadSpriteGroup (pframetype + 1, - &psprite->frames[i].frameptr); - } - } - - mod->type = mod_sprite; -} - -//============================================================================= - -/* -================ -Mod_Print -================ -*/ -void Mod_Print (void) -{ - int i; - model_t *mod; - - Con_Printf ("Cached models:\n"); - for (i=0, mod=mod_known ; i < mod_numknown ; i++, mod++) - { - Con_Printf ("%8p : %s\n",mod->cache.data, mod->name); - } -} - - +/* +Copyright (C) 1996-1997 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 "quakedef.h" +#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; + +/* +=============== +Mod_Init +=============== +*/ +void Mod_Init (void) +{ + memset (mod_novis, 0xff, sizeof(mod_novis)); +} + +/* +=============== +Mod_Init + +Caches the data if needed +=============== +*/ +void *Mod_Extradata (model_t *mod) +{ + void *r; + + r = Cache_Check (&mod->cache); + if (r) + return r; + + Mod_LoadModel (mod, true); + + if (!mod->cache.data) + Sys_Error ("Mod_Extradata: caching failed"); + return mod->cache.data; +} + +/* +=============== +Mod_PointInLeaf +=============== +*/ +mleaf_t *Mod_PointInLeaf (vec3_t p, model_t *model) +{ + mnode_t *node; + float d; + mplane_t *plane; + + if (!model || !model->nodes) + Sys_Error ("Mod_PointInLeaf: bad model"); + + node = model->nodes; + while (1) + { + if (node->contents < 0) + 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->numleafs+7)>>3; + out = decompressed; + +#if 0 + memcpy (out, in, row); +#else + 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); +#endif + + return decompressed; +} + +byte *Mod_LeafPVS (mleaf_t *leaf, model_t *model) +{ + if (leaf == model->leafs) + return mod_novis; + return Mod_DecompressVis (leaf->compressed_vis, model); +} + +/* +=================== +Mod_ClearAll +=================== +*/ +void Mod_ClearAll (void) +{ + int i; + model_t *mod; + + for (i=0 , mod=mod_known ; itype != mod_alias) + mod->needload = true; +} + +/* +================== +Mod_FindName + +================== +*/ +model_t *Mod_FindName (char *name) +{ + int i; + model_t *mod; + + if (!name[0]) + Sys_Error ("Mod_ForName: NULL name"); + +// +// search the currently loaded models +// + for (i=0 , mod=mod_known ; iname, name) ) + break; + + if (i == mod_numknown) + { + if (mod_numknown == MAX_MOD_KNOWN) + Sys_Error ("mod_numknown == MAX_MOD_KNOWN"); + strcpy (mod->name, name); + mod->needload = true; + mod_numknown++; + } + + return mod; +} + +/* +================== +Mod_TouchModel + +================== +*/ +void Mod_TouchModel (char *name) +{ + model_t *mod; + + mod = Mod_FindName (name); + + if (!mod->needload) + { + if (mod->type == mod_alias) + Cache_Check (&mod->cache); + } +} + +/* +================== +Mod_LoadModel + +Loads a model into the cache +================== +*/ +model_t *Mod_LoadModel (model_t *mod, qboolean crash) +{ + void *d; + unsigned *buf; + byte stackbuf[1024]; // avoid dirtying the cache heap + + if (!mod->needload) + { + if (mod->type == mod_alias) + { + d = Cache_Check (&mod->cache); + if (d) + return mod; + } + else + return mod; // not cached at all + } + +// +// because the world is so huge, load it one piece at a time +// + if (!crash) + { + + } + +// +// load the file +// + buf = (unsigned *)COM_LoadStackFile (mod->name, stackbuf, sizeof(stackbuf)); + if (!buf) + { + if (crash) + Sys_Error ("Mod_NumForName: %s not found", mod->name); + return NULL; + } + +// +// allocate a new model +// + COM_FileBase (mod->name, loadname); + + loadmodel = mod; + +// +// fill it in +// + +// call the apropriate loader + mod->needload = false; + mod->modhint = 0; + + switch (LittleLong(*(unsigned *)buf)) + { + case IDPOLYHEADER: + Mod_LoadAliasModel (mod, buf); + break; + + case IDSPRITEHEADER: + Mod_LoadSpriteModel (mod, buf); + break; + + default: + Mod_LoadBrushModel (mod, buf); + break; + } + + return mod; +} + +/* +================== +Mod_ForName + +Loads in a model for the given name +================== +*/ +model_t *Mod_ForName (char *name, qboolean crash) +{ + model_t *mod; + + mod = Mod_FindName (name); + + return Mod_LoadModel (mod, crash); +} + + +/* +=============================================================================== + + BRUSHMODEL LOADING + +=============================================================================== +*/ + +byte *mod_base; + + +/* +================= +Mod_LoadTextures +================= +*/ +void Mod_LoadTextures (lump_t *l) +{ + int i, j, pixels, num, max, altmax; + miptex_t *mt; + texture_t *tx, *tx2; + texture_t *anims[10]; + texture_t *altanims[10]; + dmiptexlump_t *m; + + if (!l->filelen) + { + loadmodel->textures = NULL; + return; + } + m = (dmiptexlump_t *)(mod_base + l->fileofs); + + m->nummiptex = LittleLong (m->nummiptex); + + loadmodel->numtextures = m->nummiptex; + loadmodel->textures = Hunk_AllocName (m->nummiptex * sizeof(*loadmodel->textures) , loadname); + + for (i=0 ; inummiptex ; i++) + { + m->dataofs[i] = LittleLong(m->dataofs[i]); + if (m->dataofs[i] == -1) + continue; + mt = (miptex_t *)((byte *)m + m->dataofs[i]); + mt->width = LittleLong (mt->width); + mt->height = LittleLong (mt->height); + for (j=0 ; joffsets[j] = LittleLong (mt->offsets[j]); + + if ( (mt->width & 15) || (mt->height & 15) ) + Sys_Error ("Texture %s is not 16 aligned", mt->name); + pixels = mt->width*mt->height/64*85; + tx = Hunk_AllocName (sizeof(texture_t) +pixels, loadname ); + loadmodel->textures[i] = tx; + + memcpy (tx->name, mt->name, sizeof(tx->name)); + tx->width = mt->width; + tx->height = mt->height; + for (j=0 ; joffsets[j] = mt->offsets[j] + sizeof(texture_t) - sizeof(miptex_t); + // the pixels immediately follow the structures + memcpy ( tx+1, mt+1, pixels); + + if (!strncmp(mt->name,"sky",3)) + R_InitSky (tx); + } + +// +// sequence the animations +// + for (i=0 ; inummiptex ; i++) + { + tx = loadmodel->textures[i]; + if (!tx || tx->name[0] != '+') + continue; + if (tx->anim_next) + continue; // already sequenced + + // find the number of frames in the animation + memset (anims, 0, sizeof(anims)); + memset (altanims, 0, sizeof(altanims)); + + max = tx->name[1]; + altmax = 0; + if (max >= 'a' && max <= 'z') + max -= 'a' - 'A'; + if (max >= '0' && max <= '9') + { + max -= '0'; + altmax = 0; + anims[max] = tx; + max++; + } + else if (max >= 'A' && max <= 'J') + { + altmax = max - 'A'; + max = 0; + altanims[altmax] = tx; + altmax++; + } + else + Sys_Error ("Bad animating texture %s", tx->name); + + for (j=i+1 ; jnummiptex ; j++) + { + tx2 = loadmodel->textures[j]; + if (!tx2 || tx2->name[0] != '+') + continue; + if (strcmp (tx2->name+2, tx->name+2)) + continue; + + num = tx2->name[1]; + if (num >= 'a' && num <= 'z') + num -= 'a' - 'A'; + if (num >= '0' && num <= '9') + { + num -= '0'; + anims[num] = tx2; + if (num+1 > max) + max = num + 1; + } + else if (num >= 'A' && num <= 'J') + { + num = num - 'A'; + altanims[num] = tx2; + if (num+1 > altmax) + altmax = num+1; + } + else + Sys_Error ("Bad animating texture %s", tx->name); + } + +#define ANIM_CYCLE 2 + // link them all together + for (j=0 ; jname); + tx2->anim_total = max * ANIM_CYCLE; + tx2->anim_min = j * ANIM_CYCLE; + tx2->anim_max = (j+1) * ANIM_CYCLE; + tx2->anim_next = anims[ (j+1)%max ]; + if (altmax) + tx2->alternate_anims = altanims[0]; + } + for (j=0 ; jname); + tx2->anim_total = altmax * ANIM_CYCLE; + tx2->anim_min = j * ANIM_CYCLE; + tx2->anim_max = (j+1) * ANIM_CYCLE; + tx2->anim_next = altanims[ (j+1)%altmax ]; + if (max) + tx2->alternate_anims = anims[0]; + } + } +} + +/* +================= +Mod_LoadLighting +================= +*/ +void Mod_LoadLighting (lump_t *l) +{ + if (!l->filelen) + { + loadmodel->lightdata = NULL; + return; + } + loadmodel->lightdata = Hunk_AllocName ( l->filelen, loadname); + memcpy (loadmodel->lightdata, mod_base + l->fileofs, l->filelen); +} + + +/* +================= +Mod_LoadVisibility +================= +*/ +void Mod_LoadVisibility (lump_t *l) +{ + if (!l->filelen) + { + loadmodel->visdata = NULL; + return; + } + loadmodel->visdata = Hunk_AllocName ( l->filelen, loadname); + memcpy (loadmodel->visdata, mod_base + l->fileofs, l->filelen); +} + + +/* +================= +Mod_LoadEntities +================= +*/ +void Mod_LoadEntities (lump_t *l) +{ + if (!l->filelen) + { + loadmodel->entities = NULL; + return; + } + loadmodel->entities = Hunk_AllocName ( l->filelen, loadname); + memcpy (loadmodel->entities, mod_base + l->fileofs, l->filelen); +} + + +/* +================= +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)) + Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + 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)) + Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + 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]); + } + for (j=0 ; jheadnode[j] = LittleLong (in->headnode[j]); + out->visleafs = LittleLong (in->visleafs); + 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)) + Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( (count + 1) * sizeof(*out), loadname); + + 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; + int i, j, count; + int miptex; + float len1, len2; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->texinfo = out; + loadmodel->numtexinfo = count; + + for ( i=0 ; ivecs[0][j] = LittleFloat (in->vecs[0][j]); + len1 = Length (out->vecs[0]); + len2 = Length (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; +#if 0 + if (len1 + len2 < 0.001) + out->mipadjust = 1; // don't crash + else + out->mipadjust = 1 / floor( (len1+len2)/2 + 0.1 ); +#endif + + miptex = LittleLong (in->miptex); + out->flags = LittleLong (in->flags); + + if (!loadmodel->textures) + { + out->texture = r_notexture_mip; // checkerboard texture + out->flags = 0; + } + else + { + if (miptex >= loadmodel->numtextures) + Sys_Error ("miptex >= loadmodel->numtextures"); + out->texture = loadmodel->textures[miptex]; + if (!out->texture) + { + out->texture = r_notexture_mip; // texture not found + out->flags = 0; + } + } + } +} + +/* +================ +CalcSurfaceExtents + +Fills in s->texturemins[] and s->extents[] +================ +*/ +void CalcSurfaceExtents (msurface_t *s) +{ + float mins[2], maxs[2], val; + int i,j, e; + mvertex_t *v; + 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++) + { + 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 ( !(tex->flags & TEX_SPECIAL) && s->extents[i] > 256) + Sys_Error ("Bad surface extents"); + } +} + + +/* +================= +Mod_LoadFaces +================= +*/ +void Mod_LoadFaces (lump_t *l) +{ + dface_t *in; + msurface_t *out; + int i, count, surfnum; + int planenum, side; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->surfaces = out; + loadmodel->numsurfaces = count; + + for ( surfnum=0 ; surfnumfirstedge = LittleLong(in->firstedge); + out->numedges = LittleShort(in->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 + + for (i=0 ; istyles[i] = in->styles[i]; + i = LittleLong(in->lightofs); + if (i == -1) + out->samples = NULL; + else + out->samples = loadmodel->lightdata + i; + + // set the drawing flags flag + + if (!strncmp(out->texinfo->texture->name,"sky",3)) // sky + { + out->flags |= (SURF_DRAWSKY | SURF_DRAWTILED); + continue; + } + + if (!strncmp(out->texinfo->texture->name,"*",1)) // turbulent + { + out->flags |= (SURF_DRAWTURB | SURF_DRAWTILED); + for (i=0 ; i<2 ; i++) + { + out->extents[i] = 16384; + out->texturemins[i] = -8192; + } + continue; + } + } +} + + +/* +================= +Mod_SetParent +================= +*/ +void Mod_SetParent (mnode_t *node, mnode_t *parent) +{ + node->parent = parent; + if (node->contents < 0) + return; + Mod_SetParent (node->children[0], node); + Mod_SetParent (node->children[1], node); +} + +/* +================= +Mod_LoadNodes +================= +*/ +void Mod_LoadNodes (lump_t *l) +{ + int i, j, count, p; + dnode_t *in; + mnode_t *out; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + 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); + + for (j=0 ; j<2 ; j++) + { + p = LittleShort (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, p; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->leafs = out; + loadmodel->numleafs = count; + + for ( i=0 ; iminmaxs[j] = LittleShort (in->mins[j]); + out->minmaxs[3+j] = LittleShort (in->maxs[j]); + } + + p = LittleLong(in->contents); + out->contents = p; + + out->firstmarksurface = loadmodel->marksurfaces + + LittleShort(in->firstmarksurface); + out->nummarksurfaces = LittleShort(in->nummarksurfaces); + + p = LittleLong(in->visofs); + if (p == -1) + out->compressed_vis = NULL; + else + out->compressed_vis = loadmodel->visdata + p; + out->efrags = NULL; + + for (j=0 ; j<4 ; j++) + out->ambient_sound_level[j] = in->ambient_level[j]; + } +} + +/* +================= +Mod_LoadClipnodes +================= +*/ +void Mod_LoadClipnodes (lump_t *l) +{ + dclipnode_t *in, *out; + int i, count; + hull_t *hull; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->clipnodes = out; + loadmodel->numclipnodes = count; + + hull = &loadmodel->hulls[1]; + hull->clipnodes = out; + hull->firstclipnode = 0; + hull->lastclipnode = count-1; + hull->planes = loadmodel->planes; + hull->clip_mins[0] = -16; + hull->clip_mins[1] = -16; + hull->clip_mins[2] = -24; + hull->clip_maxs[0] = 16; + hull->clip_maxs[1] = 16; + hull->clip_maxs[2] = 32; + + hull = &loadmodel->hulls[2]; + hull->clipnodes = out; + hull->firstclipnode = 0; + hull->lastclipnode = count-1; + hull->planes = loadmodel->planes; + hull->clip_mins[0] = -32; + hull->clip_mins[1] = -32; + hull->clip_mins[2] = -24; + hull->clip_maxs[0] = 32; + hull->clip_maxs[1] = 32; + hull->clip_maxs[2] = 64; + + for (i=0 ; iplanenum = LittleLong(in->planenum); + out->children[0] = LittleShort(in->children[0]); + out->children[1] = LittleShort(in->children[1]); + } +} + +/* +================= +Mod_MakeHull0 + +Deplicate the drawing hull structure as a clipping hull +================= +*/ +void Mod_MakeHull0 (void) +{ + mnode_t *in, *child; + dclipnode_t *out; + int i, j, count; + hull_t *hull; + + hull = &loadmodel->hulls[0]; + + in = loadmodel->nodes; + count = loadmodel->numnodes; + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + hull->clipnodes = out; + hull->firstclipnode = 0; + hull->lastclipnode = count-1; + hull->planes = loadmodel->planes; + + for (i=0 ; iplanenum = in->plane - loadmodel->planes; + for (j=0 ; j<2 ; j++) + { + child = in->children[j]; + if (child->contents < 0) + out->children[j] = child->contents; + else + out->children[j] = child - loadmodel->nodes; + } + } +} + +/* +================= +Mod_LoadMarksurfaces +================= +*/ +void Mod_LoadMarksurfaces (lump_t *l) +{ + int i, j, count; + short *in; + msurface_t **out; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->marksurfaces = out; + loadmodel->nummarksurfaces = count; + + for ( i=0 ; i= loadmodel->numsurfaces) + Sys_Error ("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)) + Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->surfedges = out; + loadmodel->numsurfedges = count; + + for ( i=0 ; ifileofs); + if (l->filelen % sizeof(*in)) + Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*2*sizeof(*out), loadname); + + 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; + } +} + +/* +================= +RadiusFromBounds +================= +*/ +float RadiusFromBounds (vec3_t mins, vec3_t maxs) +{ + int i; + vec3_t corner; + + for (i=0 ; i<3 ; i++) + { + corner[i] = fabs(mins[i]) > fabs(maxs[i]) ? fabs(mins[i]) : fabs(maxs[i]); + } + + return Length (corner); +} + +/* +================= +Mod_LoadBrushModel +================= +*/ +void Mod_LoadBrushModel (model_t *mod, void *buffer) +{ + int i, j; + dheader_t *header; + dmodel_t *bm; + + loadmodel->type = mod_brush; + + header = (dheader_t *)buffer; + + i = LittleLong (header->version); + if (i != BSPVERSION) + Sys_Error ("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 ; ichecksum = 0; + mod->checksum2 = 0; + +// checksum all of the map, except for entities + for (i = 0; i < HEADER_LUMPS; i++) { + if (i == LUMP_ENTITIES) + continue; + mod->checksum ^= Com_BlockChecksum(mod_base + header->lumps[i].fileofs, + header->lumps[i].filelen); + + if (i == LUMP_VISIBILITY || i == LUMP_LEAFS || i == LUMP_NODES) + continue; + mod->checksum2 ^= Com_BlockChecksum(mod_base + header->lumps[i].fileofs, + header->lumps[i].filelen); + } + +// load into heap + + Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]); + Mod_LoadEdges (&header->lumps[LUMP_EDGES]); + Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]); + Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]); + 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_MARKSURFACES]); + Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]); + Mod_LoadLeafs (&header->lumps[LUMP_LEAFS]); + Mod_LoadNodes (&header->lumps[LUMP_NODES]); + Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES]); + Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]); + Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]); + + Mod_MakeHull0 (); + + mod->numframes = 2; // regular and alternate animation + +// +// set up the submodels (FIXME: this is confusing) +// + for (i=0 ; inumsubmodels ; i++) + { + bm = &mod->submodels[i]; + + mod->hulls[0].firstclipnode = bm->headnode[0]; + for (j=1 ; jhulls[j].firstclipnode = bm->headnode[j]; + mod->hulls[j].lastclipnode = mod->numclipnodes-1; + } + + mod->firstmodelsurface = bm->firstface; + mod->nummodelsurfaces = bm->numfaces; + mod->radius = RadiusFromBounds (mod->mins, mod->maxs); + + VectorCopy (bm->maxs, mod->maxs); + VectorCopy (bm->mins, mod->mins); + + mod->numleafs = bm->visleafs; + + if (i < mod->numsubmodels-1) + { // duplicate the basic information + char name[10]; + + sprintf (name, "*%i", i+1); + loadmodel = Mod_FindName (name); + *loadmodel = *mod; + strcpy (loadmodel->name, name); + mod = loadmodel; + } + } +} + +/* +============================================================================== + +ALIAS MODELS + +============================================================================== +*/ + +/* +================= +Mod_LoadAliasFrame +================= +*/ +void * Mod_LoadAliasFrame (void * pin, int *pframeindex, int numv, + trivertx_t *pbboxmin, trivertx_t *pbboxmax, aliashdr_t *pheader, char *name) +{ + trivertx_t *pframe, *pinframe; + int i, j; + daliasframe_t *pdaliasframe; + + pdaliasframe = (daliasframe_t *)pin; + + strcpy (name, pdaliasframe->name); + + for (i=0 ; i<3 ; i++) + { + // these are byte values, so we don't have to worry about + // endianness + pbboxmin->v[i] = pdaliasframe->bboxmin.v[i]; + pbboxmax->v[i] = pdaliasframe->bboxmax.v[i]; + } + + pinframe = (trivertx_t *)(pdaliasframe + 1); + pframe = Hunk_AllocName (numv * sizeof(*pframe), loadname); + + *pframeindex = (byte *)pframe - (byte *)pheader; + + for (j=0 ; jnumframes); + + paliasgroup = Hunk_AllocName (sizeof (maliasgroup_t) + + (numframes - 1) * sizeof (paliasgroup->frames[0]), loadname); + + paliasgroup->numframes = numframes; + + for (i=0 ; i<3 ; i++) + { + // these are byte values, so we don't have to worry about endianness + pbboxmin->v[i] = pingroup->bboxmin.v[i]; + pbboxmax->v[i] = pingroup->bboxmax.v[i]; + } + + *pframeindex = (byte *)paliasgroup - (byte *)pheader; + + pin_intervals = (daliasinterval_t *)(pingroup + 1); + + poutintervals = Hunk_AllocName (numframes * sizeof (float), loadname); + + paliasgroup->intervals = (byte *)poutintervals - (byte *)pheader; + + for (i=0 ; iinterval); + if (*poutintervals <= 0.0) + Sys_Error ("Mod_LoadAliasGroup: interval<=0"); + + poutintervals++; + pin_intervals++; + } + + ptemp = (void *)pin_intervals; + + for (i=0 ; iframes[i].frame, + numv, + &paliasgroup->frames[i].bboxmin, + &paliasgroup->frames[i].bboxmax, + pheader, name); + } + + return ptemp; +} + + +/* +================= +Mod_LoadAliasSkin +================= +*/ +void * Mod_LoadAliasSkin (void * pin, int *pskinindex, int skinsize, + aliashdr_t *pheader) +{ + int i; + byte *pskin, *pinskin; + unsigned short *pusskin; + + pskin = Hunk_AllocName (skinsize * r_pixbytes, loadname); + pinskin = (byte *)pin; + *pskinindex = (byte *)pskin - (byte *)pheader; + + if (r_pixbytes == 1) + { + memcpy (pskin, pinskin, skinsize); + } + else if (r_pixbytes == 2) + { + pusskin = (unsigned short *)pskin; + + for (i=0 ; inumskins); + + paliasskingroup = Hunk_AllocName (sizeof (maliasskingroup_t) + + (numskins - 1) * sizeof (paliasskingroup->skindescs[0]), + loadname); + + paliasskingroup->numskins = numskins; + + *pskinindex = (byte *)paliasskingroup - (byte *)pheader; + + pinskinintervals = (daliasskininterval_t *)(pinskingroup + 1); + + poutskinintervals = Hunk_AllocName (numskins * sizeof (float),loadname); + + paliasskingroup->intervals = (byte *)poutskinintervals - (byte *)pheader; + + for (i=0 ; iinterval); + if (*poutskinintervals <= 0) + Sys_Error ("Mod_LoadAliasSkinGroup: interval<=0"); + + poutskinintervals++; + pinskinintervals++; + } + + ptemp = (void *)pinskinintervals; + + for (i=0 ; iskindescs[i].skin, skinsize, pheader); + } + + return ptemp; +} + + +/* +================= +Mod_LoadAliasModel +================= +*/ +void Mod_LoadAliasModel (model_t *mod, void *buffer) +{ + int i; + mdl_t *pmodel, *pinmodel; + stvert_t *pstverts, *pinstverts; + aliashdr_t *pheader; + mtriangle_t *ptri; + dtriangle_t *pintriangles; + int version, numframes, numskins; + int size; + daliasframetype_t *pframetype; + daliasskintype_t *pskintype; + maliasskindesc_t *pskindesc; + int skinsize; + int start, end, total; + +// some models are special + if(!strcmp(mod->name, "progs/player.mdl")) + mod->modhint = MOD_PLAYER; + else if(!strcmp(mod->name, "progs/eyes.mdl")) + mod->modhint = MOD_EYES; + else if (!strcmp(mod->name, "progs/flame.mdl") || + !strcmp(mod->name, "progs/flame2.mdl")) + mod->modhint = MOD_FLAME; +/* else if (!strcmp(mod->name, "progs/bolt.mdl") || + !strcmp(mod->name, "progs/bolt2.mdl") || + !strcmp(mod->name, "progs/bolt3.mdl")) + mod->modhint = MOD_THUNDERBOLT; */ + + if (mod->modhint == MOD_PLAYER || mod->modhint == MOD_EYES) { + unsigned short crc; + byte *p; + int len; + char st[40]; + + CRC_Init(&crc); + for (len = com_filesize, p = buffer; len; len--, p++) + CRC_ProcessByte(&crc, *p); + + sprintf(st, "%d", (int) crc); + Info_SetValueForKey (cls.userinfo, + mod->modhint == MOD_PLAYER ? pmodel_name : emodel_name, + st, MAX_INFO_STRING); + + if (cls.state >= ca_connected) { + MSG_WriteByte (&cls.netchan.message, clc_stringcmd); + sprintf(st, "setinfo %s %d", + mod->modhint == MOD_PLAYER ? pmodel_name : emodel_name, + (int)crc); + SZ_Print (&cls.netchan.message, st); + } + } + + start = Hunk_LowMark (); + + pinmodel = (mdl_t *)buffer; + + version = LittleLong (pinmodel->version); + if (version != ALIAS_VERSION) + Sys_Error ("%s has wrong version number (%i should be %i)", + mod->name, version, ALIAS_VERSION); + +// +// allocate space for a working header, plus all the data except the frames, +// skin and group info +// + size = sizeof (aliashdr_t) + (LittleLong (pinmodel->numframes) - 1) * + sizeof (pheader->frames[0]) + + sizeof (mdl_t) + + LittleLong (pinmodel->numverts) * sizeof (stvert_t) + + LittleLong (pinmodel->numtris) * sizeof (mtriangle_t); + + pheader = Hunk_AllocName (size, loadname); + pmodel = (mdl_t *) ((byte *)&pheader[1] + + (LittleLong (pinmodel->numframes) - 1) * + sizeof (pheader->frames[0])); + +// mod->cache.data = pheader; + mod->flags = LittleLong (pinmodel->flags); + +// +// endian-adjust and copy the data, starting with the alias model header +// + pmodel->boundingradius = LittleFloat (pinmodel->boundingradius); + pmodel->numskins = LittleLong (pinmodel->numskins); + pmodel->skinwidth = LittleLong (pinmodel->skinwidth); + pmodel->skinheight = LittleLong (pinmodel->skinheight); + + if (pmodel->skinheight > MAX_LBM_HEIGHT) + Sys_Error ("model %s has a skin taller than %d", mod->name, + MAX_LBM_HEIGHT); + + pmodel->numverts = LittleLong (pinmodel->numverts); + + if (pmodel->numverts <= 0) + Sys_Error ("model %s has no vertices", mod->name); + + if (pmodel->numverts > MAXALIASVERTS) + Sys_Error ("model %s has too many vertices", mod->name); + + pmodel->numtris = LittleLong (pinmodel->numtris); + + if (pmodel->numtris <= 0) + Sys_Error ("model %s has no triangles", mod->name); + + pmodel->numframes = LittleLong (pinmodel->numframes); + pmodel->size = LittleFloat (pinmodel->size) * ALIAS_BASE_SIZE_RATIO; + mod->synctype = LittleLong (pinmodel->synctype); + mod->numframes = pmodel->numframes; + + for (i=0 ; i<3 ; i++) + { + pmodel->scale[i] = LittleFloat (pinmodel->scale[i]); + pmodel->scale_origin[i] = LittleFloat (pinmodel->scale_origin[i]); + pmodel->eyeposition[i] = LittleFloat (pinmodel->eyeposition[i]); + } + + numskins = pmodel->numskins; + numframes = pmodel->numframes; + + if (pmodel->skinwidth & 0x03) + Sys_Error ("Mod_LoadAliasModel: skinwidth not multiple of 4"); + + pheader->model = (byte *)pmodel - (byte *)pheader; + +// +// load the skins +// + skinsize = pmodel->skinheight * pmodel->skinwidth; + + if (numskins < 1) + Sys_Error ("Mod_LoadAliasModel: Invalid # of skins: %d\n", numskins); + + pskintype = (daliasskintype_t *)&pinmodel[1]; + + pskindesc = Hunk_AllocName (numskins * sizeof (maliasskindesc_t), + loadname); + + pheader->skindesc = (byte *)pskindesc - (byte *)pheader; + + for (i=0 ; itype); + pskindesc[i].type = skintype; + + if (skintype == ALIAS_SKIN_SINGLE) + { + pskintype = (daliasskintype_t *) + Mod_LoadAliasSkin (pskintype + 1, + &pskindesc[i].skin, + skinsize, pheader); + } + else + { + pskintype = (daliasskintype_t *) + Mod_LoadAliasSkinGroup (pskintype + 1, + &pskindesc[i].skin, + skinsize, pheader); + } + } + +// +// set base s and t vertices +// + pstverts = (stvert_t *)&pmodel[1]; + pinstverts = (stvert_t *)pskintype; + + pheader->stverts = (byte *)pstverts - (byte *)pheader; + + for (i=0 ; inumverts ; i++) + { + pstverts[i].onseam = LittleLong (pinstverts[i].onseam); + // put s and t in 16.16 format + pstverts[i].s = LittleLong (pinstverts[i].s) << 16; + pstverts[i].t = LittleLong (pinstverts[i].t) << 16; + } + +// +// set up the triangles +// + ptri = (mtriangle_t *)&pstverts[pmodel->numverts]; + pintriangles = (dtriangle_t *)&pinstverts[pmodel->numverts]; + + pheader->triangles = (byte *)ptri - (byte *)pheader; + + for (i=0 ; inumtris ; i++) + { + int j; + + ptri[i].facesfront = LittleLong (pintriangles[i].facesfront); + + for (j=0 ; j<3 ; j++) + { + ptri[i].vertindex[j] = + LittleLong (pintriangles[i].vertindex[j]); + } + } + +// +// load the frames +// + if (numframes < 1) + Sys_Error ("Mod_LoadAliasModel: Invalid # of frames: %d\n", numframes); + + pframetype = (daliasframetype_t *)&pintriangles[pmodel->numtris]; + + for (i=0 ; itype); + pheader->frames[i].type = frametype; + + + if (frametype == ALIAS_SINGLE) + { + pframetype = (daliasframetype_t *) + Mod_LoadAliasFrame (pframetype + 1, + &pheader->frames[i].frame, + pmodel->numverts, + &pheader->frames[i].bboxmin, + &pheader->frames[i].bboxmax, + pheader, pheader->frames[i].name); + } + else + { + pframetype = (daliasframetype_t *) + Mod_LoadAliasGroup (pframetype + 1, + &pheader->frames[i].frame, + pmodel->numverts, + &pheader->frames[i].bboxmin, + &pheader->frames[i].bboxmax, + pheader, pheader->frames[i].name); + } + } + + mod->type = mod_alias; + +// FIXME: do this right + mod->mins[0] = mod->mins[1] = mod->mins[2] = -16; + mod->maxs[0] = mod->maxs[1] = mod->maxs[2] = 16; + +// +// move the complete, relocatable alias model to the cache +// + end = Hunk_LowMark (); + total = end - start; + + Cache_Alloc (&mod->cache, total, loadname); + if (!mod->cache.data) + return; + memcpy (mod->cache.data, pheader, total); + + Hunk_FreeToLowMark (start); +} + +//============================================================================= + +/* +================= +Mod_LoadSpriteFrame +================= +*/ +void * Mod_LoadSpriteFrame (void * pin, mspriteframe_t **ppframe) +{ + dspriteframe_t *pinframe; + mspriteframe_t *pspriteframe; + int i, width, height, size, origin[2]; + unsigned short *ppixout; + byte *ppixin; + + pinframe = (dspriteframe_t *)pin; + + width = LittleLong (pinframe->width); + height = LittleLong (pinframe->height); + size = width * height; + + pspriteframe = Hunk_AllocName (sizeof (mspriteframe_t) + size*r_pixbytes, + loadname); + + memset (pspriteframe, 0, sizeof (mspriteframe_t) + size); + *ppframe = pspriteframe; + + pspriteframe->width = width; + pspriteframe->height = height; + origin[0] = LittleLong (pinframe->origin[0]); + origin[1] = LittleLong (pinframe->origin[1]); + + pspriteframe->up = origin[1]; + pspriteframe->down = origin[1] - height; + pspriteframe->left = origin[0]; + pspriteframe->right = width + origin[0]; + + if (r_pixbytes == 1) + { + memcpy (&pspriteframe->pixels[0], (byte *)(pinframe + 1), size); + } + else if (r_pixbytes == 2) + { + ppixin = (byte *)(pinframe + 1); + ppixout = (unsigned short *)&pspriteframe->pixels[0]; + + for (i=0 ; inumframes); + + pspritegroup = Hunk_AllocName (sizeof (mspritegroup_t) + + (numframes - 1) * sizeof (pspritegroup->frames[0]), loadname); + + pspritegroup->numframes = numframes; + + *ppframe = (mspriteframe_t *)pspritegroup; + + pin_intervals = (dspriteinterval_t *)(pingroup + 1); + + poutintervals = Hunk_AllocName (numframes * sizeof (float), loadname); + + pspritegroup->intervals = poutintervals; + + for (i=0 ; iinterval); + if (*poutintervals <= 0.0) + Sys_Error ("Mod_LoadSpriteGroup: interval<=0"); + + poutintervals++; + pin_intervals++; + } + + ptemp = (void *)pin_intervals; + + for (i=0 ; iframes[i]); + } + + return ptemp; +} + + +/* +================= +Mod_LoadSpriteModel +================= +*/ +void Mod_LoadSpriteModel (model_t *mod, void *buffer) +{ + int i; + int version; + dsprite_t *pin; + msprite_t *psprite; + int numframes; + int size; + dspriteframetype_t *pframetype; + + pin = (dsprite_t *)buffer; + + version = LittleLong (pin->version); + if (version != SPRITE_VERSION) + Sys_Error ("%s has wrong version number " + "(%i should be %i)", mod->name, version, SPRITE_VERSION); + + numframes = LittleLong (pin->numframes); + + size = sizeof (msprite_t) + (numframes - 1) * sizeof (psprite->frames); + + psprite = Hunk_AllocName (size, loadname); + + mod->cache.data = psprite; + + psprite->type = LittleLong (pin->type); + psprite->maxwidth = LittleLong (pin->width); + psprite->maxheight = LittleLong (pin->height); + psprite->beamlength = LittleFloat (pin->beamlength); + mod->synctype = LittleLong (pin->synctype); + psprite->numframes = numframes; + + mod->mins[0] = mod->mins[1] = -psprite->maxwidth/2; + mod->maxs[0] = mod->maxs[1] = psprite->maxwidth/2; + mod->mins[2] = -psprite->maxheight/2; + mod->maxs[2] = psprite->maxheight/2; + +// +// load the frames +// + if (numframes < 1) + Sys_Error ("Mod_LoadSpriteModel: Invalid # of frames: %d\n", numframes); + + mod->numframes = numframes; + + pframetype = (dspriteframetype_t *)(pin + 1); + + for (i=0 ; itype); + psprite->frames[i].type = frametype; + + if (frametype == SPR_SINGLE) + { + pframetype = (dspriteframetype_t *) + Mod_LoadSpriteFrame (pframetype + 1, + &psprite->frames[i].frameptr); + } + else + { + pframetype = (dspriteframetype_t *) + Mod_LoadSpriteGroup (pframetype + 1, + &psprite->frames[i].frameptr); + } + } + + mod->type = mod_sprite; +} + +//============================================================================= + +/* +================ +Mod_Print +================ +*/ +void Mod_Print (void) +{ + int i; + model_t *mod; + + Con_Printf ("Cached models:\n"); + for (i=0, mod=mod_known ; i < mod_numknown ; i++, mod++) + { + Con_Printf ("%8p : %s\n",mod->cache.data, mod->name); + } +} + + diff --git a/source/model.h b/source/model.h index 8649c2ce..71a9e816 100644 --- a/source/model.h +++ b/source/model.h @@ -1,408 +1,408 @@ -/* -Copyright (C) 1996-1997 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 __MODEL__ -#define __MODEL__ - -#include "modelgen.h" -#include "spritegn.h" -#include "bspfile.h" - -/* - -d*_t structures are on-disk representations -m*_t structures are in-memory - -*/ - -// entity effects - -#define EF_BRIGHTFIELD 1 -#define EF_MUZZLEFLASH 2 -#define EF_BRIGHTLIGHT 4 -#define EF_DIMLIGHT 8 -#define EF_FLAG1 16 -#define EF_FLAG2 32 -#define EF_BLUE 64 -#define EF_RED 128 - -/* -============================================================================== - -BRUSH MODELS - -============================================================================== -*/ - - -// -// in memory representation -// -// !!! if this is changed, it must be changed in asm_draw.h too !!! -typedef struct -{ - vec3_t position; -} mvertex_t; - -#define SIDE_FRONT 0 -#define SIDE_BACK 1 -#define SIDE_ON 2 - - -// plane_t structure -// !!! if this is changed, it must be changed in asm_i386.h too !!! -typedef struct mplane_s -{ - vec3_t normal; - float dist; - byte type; // for texture axis selection and fast side tests - byte signbits; // signx + signy<<1 + signz<<1 - byte pad[2]; -} mplane_t; - -typedef struct texture_s -{ - char name[16]; - unsigned width, height; - int anim_total; // total tenths in sequence ( 0 = no) - int anim_min, anim_max; // time for this frame min <=time< max - struct texture_s *anim_next; // in the animation sequence - struct texture_s *alternate_anims; // bmodels in frmae 1 use these - unsigned offsets[MIPLEVELS]; // four mip maps stored -} texture_t; - - -#define SURF_PLANEBACK 2 -#define SURF_DRAWSKY 4 -#define SURF_DRAWSPRITE 8 -#define SURF_DRAWTURB 0x10 -#define SURF_DRAWTILED 0x20 -#define SURF_DRAWBACKGROUND 0x40 - -// !!! if this is changed, it must be changed in asm_draw.h too !!! -typedef struct -{ - unsigned short v[2]; - unsigned int cachededgeoffset; -} medge_t; - -typedef struct -{ - float vecs[2][4]; - float mipadjust; - texture_t *texture; - int flags; -} mtexinfo_t; - -typedef struct msurface_s -{ - int visframe; // should be drawn when node is crossed - - int dlightframe; - int dlightbits; - - mplane_t *plane; - int flags; - - int firstedge; // look up in model->surfedges[], 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] -} msurface_t; - -typedef struct mnode_s -{ -// common with leaf - int contents; // 0, 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 a negative contents number - int visframe; // node needs to be traversed if current - - short minmaxs[6]; // for bounding box culling - - struct mnode_s *parent; - -// leaf specific - byte *compressed_vis; - struct efrag_s *efrags; - - msurface_t **firstmarksurface; - int nummarksurfaces; - int key; // BSP sequence number for leaf's contents - byte ambient_sound_level[NUM_AMBIENTS]; -} mleaf_t; - -// !!! if this is changed, it must be changed in asm_i386.h too !!! -typedef struct -{ - dclipnode_t *clipnodes; - mplane_t *planes; - int firstclipnode; - int lastclipnode; - vec3_t clip_mins; - vec3_t clip_maxs; -} hull_t; - -/* -============================================================================== - -SPRITE MODELS - -============================================================================== -*/ - - -// FIXME: shorten these? -typedef struct mspriteframe_s -{ - int width; - int height; - void *pcachespot; // remove? - float up, down, left, right; - byte pixels[4]; -} mspriteframe_t; - -typedef struct -{ - int numframes; - float *intervals; - mspriteframe_t *frames[1]; -} mspritegroup_t; - -typedef struct -{ - spriteframetype_t type; - mspriteframe_t *frameptr; -} mspriteframedesc_t; - -typedef struct -{ - int type; - int maxwidth; - int maxheight; - int numframes; - float beamlength; // remove? - void *cachespot; // remove? - mspriteframedesc_t frames[1]; -} msprite_t; - - -/* -============================================================================== - -ALIAS MODELS - -Alias models are position independent, so the cache manager can move them. -============================================================================== -*/ - -typedef struct -{ - aliasframetype_t type; - trivertx_t bboxmin; - trivertx_t bboxmax; - int frame; - char name[16]; -} maliasframedesc_t; - -typedef struct -{ - aliasskintype_t type; - void *pcachespot; - int skin; -} maliasskindesc_t; - -typedef struct -{ - trivertx_t bboxmin; - trivertx_t bboxmax; - int frame; -} maliasgroupframedesc_t; - -typedef struct -{ - int numframes; - int intervals; - maliasgroupframedesc_t frames[1]; -} maliasgroup_t; - -typedef struct -{ - int numskins; - int intervals; - maliasskindesc_t skindescs[1]; -} maliasskingroup_t; - -// !!! if this is changed, it must be changed in asm_draw.h too !!! -typedef struct mtriangle_s { - int facesfront; - int vertindex[3]; -} mtriangle_t; - -typedef struct { - int model; - int stverts; - int skindesc; - int triangles; - maliasframedesc_t frames[1]; -} aliashdr_t; - -//=================================================================== - -// -// Whole model -// - -typedef enum {mod_brush, mod_sprite, mod_alias} modtype_t; - -// some models are special -typedef enum {MOD_NORMAL, MOD_PLAYER, MOD_EYES, MOD_FLAME, MOD_THUNDERBOLT} modhint_t; - -#define EF_ROCKET 1 // leave a trail -#define EF_GRENADE 2 // leave a trail -#define EF_GIB 4 // leave a trail -#define EF_ROTATE 8 // rotate (bonus items) -#define EF_TRACER 16 // green split trail -#define EF_ZOMGIB 32 // small blood trail -#define EF_TRACER2 64 // orange split trail + rotate -#define EF_TRACER3 128 // purple trail - -typedef struct model_s -{ - char name[MAX_QPATH]; - qboolean needload; // bmodels and sprites don't cache normally - - modhint_t modhint; - - modtype_t type; - int numframes; - synctype_t synctype; - - int flags; - -// -// volume occupied by the model graphics -// - vec3_t mins, maxs; - float radius; - -// -// 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; - mnode_t *nodes; - - int numtexinfo; - mtexinfo_t *texinfo; - - int numsurfaces; - msurface_t *surfaces; - - int numsurfedges; - int *surfedges; - - int numclipnodes; - dclipnode_t *clipnodes; - - int nummarksurfaces; - msurface_t **marksurfaces; - - hull_t hulls[MAX_MAP_HULLS]; - - int numtextures; - texture_t **textures; - - byte *visdata; - byte *lightdata; - char *entities; - - unsigned checksum; // for world models only - unsigned checksum2; // for world models only - -// -// additional model data -// - cache_user_t cache; // only access through Mod_Extradata - -} 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_LeafPVS (mleaf_t *leaf, model_t *model); - -#endif // __MODEL__ +/* +Copyright (C) 1996-1997 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 __MODEL__ +#define __MODEL__ + +#include "modelgen.h" +#include "spritegn.h" +#include "bspfile.h" + +/* + +d*_t structures are on-disk representations +m*_t structures are in-memory + +*/ + +// entity effects + +#define EF_BRIGHTFIELD 1 +#define EF_MUZZLEFLASH 2 +#define EF_BRIGHTLIGHT 4 +#define EF_DIMLIGHT 8 +#define EF_FLAG1 16 +#define EF_FLAG2 32 +#define EF_BLUE 64 +#define EF_RED 128 + +/* +============================================================================== + +BRUSH MODELS + +============================================================================== +*/ + + +// +// in memory representation +// +// !!! if this is changed, it must be changed in asm_draw.h too !!! +typedef struct +{ + vec3_t position; +} mvertex_t; + +#define SIDE_FRONT 0 +#define SIDE_BACK 1 +#define SIDE_ON 2 + + +// plane_t structure +// !!! if this is changed, it must be changed in asm_i386.h too !!! +typedef struct mplane_s +{ + vec3_t normal; + float dist; + byte type; // for texture axis selection and fast side tests + byte signbits; // signx + signy<<1 + signz<<1 + byte pad[2]; +} mplane_t; + +typedef struct texture_s +{ + char name[16]; + unsigned width, height; + int anim_total; // total tenths in sequence ( 0 = no) + int anim_min, anim_max; // time for this frame min <=time< max + struct texture_s *anim_next; // in the animation sequence + struct texture_s *alternate_anims; // bmodels in frmae 1 use these + unsigned offsets[MIPLEVELS]; // four mip maps stored +} texture_t; + + +#define SURF_PLANEBACK 2 +#define SURF_DRAWSKY 4 +#define SURF_DRAWSPRITE 8 +#define SURF_DRAWTURB 0x10 +#define SURF_DRAWTILED 0x20 +#define SURF_DRAWBACKGROUND 0x40 + +// !!! if this is changed, it must be changed in asm_draw.h too !!! +typedef struct +{ + unsigned short v[2]; + unsigned int cachededgeoffset; +} medge_t; + +typedef struct +{ + float vecs[2][4]; + float mipadjust; + texture_t *texture; + int flags; +} mtexinfo_t; + +typedef struct msurface_s +{ + int visframe; // should be drawn when node is crossed + + int dlightframe; + int dlightbits; + + mplane_t *plane; + int flags; + + int firstedge; // look up in model->surfedges[], 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] +} msurface_t; + +typedef struct mnode_s +{ +// common with leaf + int contents; // 0, 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 a negative contents number + int visframe; // node needs to be traversed if current + + short minmaxs[6]; // for bounding box culling + + struct mnode_s *parent; + +// leaf specific + byte *compressed_vis; + struct efrag_s *efrags; + + msurface_t **firstmarksurface; + int nummarksurfaces; + int key; // BSP sequence number for leaf's contents + byte ambient_sound_level[NUM_AMBIENTS]; +} mleaf_t; + +// !!! if this is changed, it must be changed in asm_i386.h too !!! +typedef struct +{ + dclipnode_t *clipnodes; + mplane_t *planes; + int firstclipnode; + int lastclipnode; + vec3_t clip_mins; + vec3_t clip_maxs; +} hull_t; + +/* +============================================================================== + +SPRITE MODELS + +============================================================================== +*/ + + +// FIXME: shorten these? +typedef struct mspriteframe_s +{ + int width; + int height; + void *pcachespot; // remove? + float up, down, left, right; + byte pixels[4]; +} mspriteframe_t; + +typedef struct +{ + int numframes; + float *intervals; + mspriteframe_t *frames[1]; +} mspritegroup_t; + +typedef struct +{ + spriteframetype_t type; + mspriteframe_t *frameptr; +} mspriteframedesc_t; + +typedef struct +{ + int type; + int maxwidth; + int maxheight; + int numframes; + float beamlength; // remove? + void *cachespot; // remove? + mspriteframedesc_t frames[1]; +} msprite_t; + + +/* +============================================================================== + +ALIAS MODELS + +Alias models are position independent, so the cache manager can move them. +============================================================================== +*/ + +typedef struct +{ + aliasframetype_t type; + trivertx_t bboxmin; + trivertx_t bboxmax; + int frame; + char name[16]; +} maliasframedesc_t; + +typedef struct +{ + aliasskintype_t type; + void *pcachespot; + int skin; +} maliasskindesc_t; + +typedef struct +{ + trivertx_t bboxmin; + trivertx_t bboxmax; + int frame; +} maliasgroupframedesc_t; + +typedef struct +{ + int numframes; + int intervals; + maliasgroupframedesc_t frames[1]; +} maliasgroup_t; + +typedef struct +{ + int numskins; + int intervals; + maliasskindesc_t skindescs[1]; +} maliasskingroup_t; + +// !!! if this is changed, it must be changed in asm_draw.h too !!! +typedef struct mtriangle_s { + int facesfront; + int vertindex[3]; +} mtriangle_t; + +typedef struct { + int model; + int stverts; + int skindesc; + int triangles; + maliasframedesc_t frames[1]; +} aliashdr_t; + +//=================================================================== + +// +// Whole model +// + +typedef enum {mod_brush, mod_sprite, mod_alias} modtype_t; + +// some models are special +typedef enum {MOD_NORMAL, MOD_PLAYER, MOD_EYES, MOD_FLAME, MOD_THUNDERBOLT} modhint_t; + +#define EF_ROCKET 1 // leave a trail +#define EF_GRENADE 2 // leave a trail +#define EF_GIB 4 // leave a trail +#define EF_ROTATE 8 // rotate (bonus items) +#define EF_TRACER 16 // green split trail +#define EF_ZOMGIB 32 // small blood trail +#define EF_TRACER2 64 // orange split trail + rotate +#define EF_TRACER3 128 // purple trail + +typedef struct model_s +{ + char name[MAX_QPATH]; + qboolean needload; // bmodels and sprites don't cache normally + + modhint_t modhint; + + modtype_t type; + int numframes; + synctype_t synctype; + + int flags; + +// +// volume occupied by the model graphics +// + vec3_t mins, maxs; + float radius; + +// +// 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; + mnode_t *nodes; + + int numtexinfo; + mtexinfo_t *texinfo; + + int numsurfaces; + msurface_t *surfaces; + + int numsurfedges; + int *surfedges; + + int numclipnodes; + dclipnode_t *clipnodes; + + int nummarksurfaces; + msurface_t **marksurfaces; + + hull_t hulls[MAX_MAP_HULLS]; + + int numtextures; + texture_t **textures; + + byte *visdata; + byte *lightdata; + char *entities; + + unsigned checksum; // for world models only + unsigned checksum2; // for world models only + +// +// additional model data +// + cache_user_t cache; // only access through Mod_Extradata + +} 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_LeafPVS (mleaf_t *leaf, model_t *model); + +#endif // __MODEL__ diff --git a/source/modelgen.h b/source/modelgen.h index 76cb956f..be9ad92f 100644 --- a/source/modelgen.h +++ b/source/modelgen.h @@ -1,134 +1,134 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// -// modelgen.h: header file for model generation program -// - -// ********************************************************* -// * This file must be identical in the modelgen directory * -// * and in the Quake directory, because it's used to * -// * pass data from one to the other via model files. * -// ********************************************************* - -#ifdef INCLUDELIBS - -#include -#include -#include -#include - -#include "cmdlib.h" -#include "scriplib.h" -#include "trilib.h" -#include "lbmlib.h" -#include "mathlib.h" - -#endif - -#define ALIAS_VERSION 6 - -#define ALIAS_ONSEAM 0x0020 - -// must match definition in spritegn.h -#ifndef SYNCTYPE_T -#define SYNCTYPE_T -typedef enum {ST_SYNC=0, ST_RAND } synctype_t; -#endif - -typedef enum { ALIAS_SINGLE=0, ALIAS_GROUP } aliasframetype_t; - -typedef enum { ALIAS_SKIN_SINGLE=0, ALIAS_SKIN_GROUP } aliasskintype_t; - -typedef struct { - int ident; - int version; - vec3_t scale; - vec3_t scale_origin; - float boundingradius; - vec3_t eyeposition; - int numskins; - int skinwidth; - int skinheight; - int numverts; - int numtris; - int numframes; - synctype_t synctype; - int flags; - float size; -} mdl_t; - -// TODO: could be shorts - -typedef struct { - int onseam; - int s; - int t; -} stvert_t; - -typedef struct dtriangle_s { - int facesfront; - int vertindex[3]; -} dtriangle_t; - -#define DT_FACES_FRONT 0x0010 - -// This mirrors trivert_t in trilib.h, is present so Quake knows how to -// load this data - -typedef struct { - byte v[3]; - byte lightnormalindex; -} trivertx_t; - -typedef struct { - trivertx_t bboxmin; // lightnormal isn't used - trivertx_t bboxmax; // lightnormal isn't used - char name[16]; // frame name from grabbing -} daliasframe_t; - -typedef struct { - int numframes; - trivertx_t bboxmin; // lightnormal isn't used - trivertx_t bboxmax; // lightnormal isn't used -} daliasgroup_t; - -typedef struct { - int numskins; -} daliasskingroup_t; - -typedef struct { - float interval; -} daliasinterval_t; - -typedef struct { - float interval; -} daliasskininterval_t; - -typedef struct { - aliasframetype_t type; -} daliasframetype_t; - -typedef struct { - aliasskintype_t type; -} daliasskintype_t; - -#define IDPOLYHEADER (('O'<<24)+('P'<<16)+('D'<<8)+'I') - // little-endian "IDPO" - +/* +Copyright (C) 1996-1997 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. + +*/ +// +// modelgen.h: header file for model generation program +// + +// ********************************************************* +// * This file must be identical in the modelgen directory * +// * and in the Quake directory, because it's used to * +// * pass data from one to the other via model files. * +// ********************************************************* + +#ifdef INCLUDELIBS + +#include +#include +#include +#include + +#include "cmdlib.h" +#include "scriplib.h" +#include "trilib.h" +#include "lbmlib.h" +#include "mathlib.h" + +#endif + +#define ALIAS_VERSION 6 + +#define ALIAS_ONSEAM 0x0020 + +// must match definition in spritegn.h +#ifndef SYNCTYPE_T +#define SYNCTYPE_T +typedef enum {ST_SYNC=0, ST_RAND } synctype_t; +#endif + +typedef enum { ALIAS_SINGLE=0, ALIAS_GROUP } aliasframetype_t; + +typedef enum { ALIAS_SKIN_SINGLE=0, ALIAS_SKIN_GROUP } aliasskintype_t; + +typedef struct { + int ident; + int version; + vec3_t scale; + vec3_t scale_origin; + float boundingradius; + vec3_t eyeposition; + int numskins; + int skinwidth; + int skinheight; + int numverts; + int numtris; + int numframes; + synctype_t synctype; + int flags; + float size; +} mdl_t; + +// TODO: could be shorts + +typedef struct { + int onseam; + int s; + int t; +} stvert_t; + +typedef struct dtriangle_s { + int facesfront; + int vertindex[3]; +} dtriangle_t; + +#define DT_FACES_FRONT 0x0010 + +// This mirrors trivert_t in trilib.h, is present so Quake knows how to +// load this data + +typedef struct { + byte v[3]; + byte lightnormalindex; +} trivertx_t; + +typedef struct { + trivertx_t bboxmin; // lightnormal isn't used + trivertx_t bboxmax; // lightnormal isn't used + char name[16]; // frame name from grabbing +} daliasframe_t; + +typedef struct { + int numframes; + trivertx_t bboxmin; // lightnormal isn't used + trivertx_t bboxmax; // lightnormal isn't used +} daliasgroup_t; + +typedef struct { + int numskins; +} daliasskingroup_t; + +typedef struct { + float interval; +} daliasinterval_t; + +typedef struct { + float interval; +} daliasskininterval_t; + +typedef struct { + aliasframetype_t type; +} daliasframetype_t; + +typedef struct { + aliasskintype_t type; +} daliasskintype_t; + +#define IDPOLYHEADER (('O'<<24)+('P'<<16)+('D'<<8)+'I') + // little-endian "IDPO" + diff --git a/source/net.h b/source/net.h index ce8ed926..0b32206f 100644 --- a/source/net.h +++ b/source/net.h @@ -1,113 +1,113 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// net.h -- quake's interface to the networking layer - -#define PORT_ANY -1 - -typedef struct -{ - byte ip[4]; - unsigned short port; - unsigned short pad; -} netadr_t; - -extern netadr_t net_local_adr; -extern netadr_t net_from; // address of who sent the packet -extern sizebuf_t net_message; - -extern cvar_t hostname; - -extern int net_clientsocket; -extern int net_serversocket; - -void NET_Init (int clientport, int serverport); -void NET_Shutdown (void); -qboolean NET_GetPacket (int net_socket); -void NET_SendPacket (int net_socket, int length, void *data, netadr_t to); - -qboolean NET_CompareAdr (netadr_t a, netadr_t b); -qboolean NET_CompareBaseAdr (netadr_t a, netadr_t b); -char *NET_AdrToString (netadr_t a); -char *NET_BaseAdrToString (netadr_t a); -qboolean NET_StringToAdr (char *s, netadr_t *a); - -//============================================================================ - -#define OLD_AVG 0.99 // total = oldtotal*OLD_AVG + new*(1-OLD_AVG) - -#define MAX_LATENT 32 - -typedef struct -{ - qboolean fatal_error; - - float last_received; // for timeouts - -// the statistics are cleared at each client begin, because -// the server connecting process gives a bogus picture of the data - float frame_latency; // rolling average - float frame_rate; - - int drop_count; // dropped packets, cleared each level - int good_count; // cleared each level - - int net_socket; // Tonik - netadr_t remote_address; - int qport; - -// bandwidth estimator - double cleartime; // if realtime > nc->cleartime, free to go - double rate; // seconds / byte - -// sequencing variables - int incoming_sequence; - int incoming_acknowledged; - int incoming_reliable_acknowledged; // single bit - - int incoming_reliable_sequence; // single bit, maintained local - - int outgoing_sequence; - int reliable_sequence; // single bit - int last_reliable_sequence; // sequence number of last send - -// reliable staging and holding areas - sizebuf_t message; // writing buffer to send to server - byte message_buf[MAX_MSGLEN]; - - int reliable_length; - byte reliable_buf[MAX_MSGLEN]; // unacked reliable message - -// time and size data to calculate bandwidth - int outgoing_size[MAX_LATENT]; - double outgoing_time[MAX_LATENT]; -} netchan_t; - -extern int net_drop; // packets dropped before this one - -void Netchan_Init (void); -void Netchan_Transmit (netchan_t *chan, int length, byte *data); -void Netchan_OutOfBand (int net_socket, netadr_t adr, int length, byte *data); -void Netchan_OutOfBandPrint (int net_socket, netadr_t adr, char *format, ...); -qboolean Netchan_Process (netchan_t *chan); -void Netchan_Setup (netchan_t *chan, netadr_t adr, int qport, int net_socket); - -qboolean Netchan_CanPacket (netchan_t *chan); -qboolean Netchan_CanReliable (netchan_t *chan); - +/* +Copyright (C) 1996-1997 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. + +*/ +// net.h -- quake's interface to the networking layer + +#define PORT_ANY -1 + +typedef struct +{ + byte ip[4]; + unsigned short port; + unsigned short pad; +} netadr_t; + +extern netadr_t net_local_adr; +extern netadr_t net_from; // address of who sent the packet +extern sizebuf_t net_message; + +extern cvar_t hostname; + +extern int net_clientsocket; +extern int net_serversocket; + +void NET_Init (int clientport, int serverport); +void NET_Shutdown (void); +qboolean NET_GetPacket (int net_socket); +void NET_SendPacket (int net_socket, int length, void *data, netadr_t to); + +qboolean NET_CompareAdr (netadr_t a, netadr_t b); +qboolean NET_CompareBaseAdr (netadr_t a, netadr_t b); +char *NET_AdrToString (netadr_t a); +char *NET_BaseAdrToString (netadr_t a); +qboolean NET_StringToAdr (char *s, netadr_t *a); + +//============================================================================ + +#define OLD_AVG 0.99 // total = oldtotal*OLD_AVG + new*(1-OLD_AVG) + +#define MAX_LATENT 32 + +typedef struct +{ + qboolean fatal_error; + + float last_received; // for timeouts + +// the statistics are cleared at each client begin, because +// the server connecting process gives a bogus picture of the data + float frame_latency; // rolling average + float frame_rate; + + int drop_count; // dropped packets, cleared each level + int good_count; // cleared each level + + int net_socket; // Tonik + netadr_t remote_address; + int qport; + +// bandwidth estimator + double cleartime; // if realtime > nc->cleartime, free to go + double rate; // seconds / byte + +// sequencing variables + int incoming_sequence; + int incoming_acknowledged; + int incoming_reliable_acknowledged; // single bit + + int incoming_reliable_sequence; // single bit, maintained local + + int outgoing_sequence; + int reliable_sequence; // single bit + int last_reliable_sequence; // sequence number of last send + +// reliable staging and holding areas + sizebuf_t message; // writing buffer to send to server + byte message_buf[MAX_MSGLEN]; + + int reliable_length; + byte reliable_buf[MAX_MSGLEN]; // unacked reliable message + +// time and size data to calculate bandwidth + int outgoing_size[MAX_LATENT]; + double outgoing_time[MAX_LATENT]; +} netchan_t; + +extern int net_drop; // packets dropped before this one + +void Netchan_Init (void); +void Netchan_Transmit (netchan_t *chan, int length, byte *data); +void Netchan_OutOfBand (int net_socket, netadr_t adr, int length, byte *data); +void Netchan_OutOfBandPrint (int net_socket, netadr_t adr, char *format, ...); +qboolean Netchan_Process (netchan_t *chan); +void Netchan_Setup (netchan_t *chan, netadr_t adr, int qport, int net_socket); + +qboolean Netchan_CanPacket (netchan_t *chan); +qboolean Netchan_CanReliable (netchan_t *chan); + diff --git a/source/net_chan.c b/source/net_chan.c index 7919f64e..2d284708 100644 --- a/source/net_chan.c +++ b/source/net_chan.c @@ -1,454 +1,454 @@ -/* -Copyright (C) 1996-1997 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 "quakedef.h" - -#ifdef _WIN32 -#include "winquake.h" -#endif - -#define PACKET_HEADER 8 - -/* - -packet header -------------- -31 sequence -1 does this message contain a reliable payload -31 acknowledge sequence -1 acknowledge receipt of even/odd message -16 qport - -The remote connection never knows if it missed a reliable message, the -local side detects that it has been dropped by seeing a sequence acknowledge -higher thatn the last reliable sequence, but without the correct evon/odd -bit for the reliable set. - -If the sender notices that a reliable message has been dropped, it will be -retransmitted. It will not be retransmitted again until a message after -the retransmit has been acknowledged and the reliable still failed to get there. - -if the sequence number is -1, the packet should be handled without a netcon - -The reliable message can be added to at any time by doing -MSG_Write* (&netchan->message, ). - -If the message buffer is overflowed, either by a single message, or by -multiple frames worth piling up while the last reliable transmit goes -unacknowledged, the netchan signals a fatal error. - -Reliable messages are always placed first in a packet, then the unreliable -message is included if there is sufficient room. - -To the receiver, there is no distinction between the reliable and unreliable -parts of the message, they are just processed out as a single larger message. - -Illogical packet sequence numbers cause the packet to be dropped, but do -not kill the connection. This, combined with the tight window of valid -reliable acknowledgement numbers provides protection against malicious -address spoofing. - -The qport field is a workaround for bad address translating routers that -sometimes remap the client's source port on a packet during gameplay. - -If the base part of the net address matches and the qport matches, then the -channel matches even if the IP port differs. The IP port should be updated -to the new value before sending out any replies. - - -*/ - -int net_drop; -cvar_t showpackets = {"showpackets", "0"}; -cvar_t showdrop = {"showdrop", "0"}; -cvar_t qport = {"qport", "0"}; - -/* -=============== -Netchan_Init - -=============== -*/ -void Netchan_Init (void) -{ - int port; - - // pick a port value that should be nice and random -#ifdef _WIN32 - port = ((int)(timeGetTime()*1000) * time(NULL)) & 0xffff; -#else - port = ((int)(getpid()+getuid()*1000) * time(NULL)) & 0xffff; -#endif - - Cvar_RegisterVariable (&showpackets); - Cvar_RegisterVariable (&showdrop); - Cvar_RegisterVariable (&qport); - Cvar_SetValue(&qport, port); -} - -/* -=============== -Netchan_OutOfBand - -Sends an out-of-band datagram -================ -*/ -void Netchan_OutOfBand (int net_socket, netadr_t adr, int length, byte *data) -{ - sizebuf_t send; - byte send_buf[MAX_MSGLEN + PACKET_HEADER]; - -// write the packet header - send.data = send_buf; - send.maxsize = sizeof(send_buf); - send.cursize = 0; - - MSG_WriteLong (&send, -1); // -1 sequence means out of band - SZ_Write (&send, data, length); - -// send the datagram - //zoid, no input in demo playback mode -#ifndef SERVERONLY - if (!cls.demoplayback) -#endif - NET_SendPacket (net_socket, send.cursize, send.data, adr); -} - -/* -=============== -Netchan_OutOfBandPrint - -Sends a text message in an out-of-band datagram -================ -*/ -void Netchan_OutOfBandPrint (int net_socket, netadr_t adr, char *format, ...) -{ - va_list argptr; - static char string[8192]; // ??? why static? - - va_start (argptr, format); - vsprintf (string, format,argptr); - va_end (argptr); - - - Netchan_OutOfBand (net_socket, adr, strlen(string), (byte *)string); -} - - -/* -============== -Netchan_Setup - -called to open a channel to a remote system -============== -*/ -void Netchan_Setup (netchan_t *chan, netadr_t adr, int qport, int net_socket) -{ - memset (chan, 0, sizeof(*chan)); - - chan->net_socket = net_socket; - - chan->remote_address = adr; - chan->last_received = realtime; - - chan->message.data = chan->message_buf; - chan->message.allowoverflow = true; - chan->message.maxsize = sizeof(chan->message_buf); - - chan->qport = qport; - - chan->rate = 1.0/2500; -} - - -/* -=============== -Netchan_CanPacket - -Returns true if the bandwidth choke isn't active -================ -*/ -#define MAX_BACKUP 200 -qboolean Netchan_CanPacket (netchan_t *chan) -{ - if (chan->cleartime < realtime + MAX_BACKUP*chan->rate) - return true; - return false; -} - - -/* -=============== -Netchan_CanReliable - -Returns true if the bandwidth choke isn't -================ -*/ -qboolean Netchan_CanReliable (netchan_t *chan) -{ - if (chan->reliable_length) - return false; // waiting for ack - return Netchan_CanPacket (chan); -} - -#ifdef SERVERONLY -qboolean ServerPaused(void); -#endif - -/* -=============== -Netchan_Transmit - -tries to send an unreliable message to a connection, and handles the -transmition / retransmition of the reliable messages. - -A 0 length will still generate a packet and deal with the reliable messages. -================ -*/ -void Netchan_Transmit (netchan_t *chan, int length, byte *data) -{ - sizebuf_t send; - byte send_buf[MAX_MSGLEN + PACKET_HEADER]; - qboolean send_reliable; - unsigned w1, w2; - int i; - -// check for message overflow - if (chan->message.overflowed) - { - chan->fatal_error = true; - Con_Printf ("%s:Outgoing message overflow\n" - , NET_AdrToString (chan->remote_address)); - return; - } - -// if the remote side dropped the last reliable message, resend it - send_reliable = false; - - if (chan->incoming_acknowledged > chan->last_reliable_sequence - && chan->incoming_reliable_acknowledged != chan->reliable_sequence) - send_reliable = true; - -// if the reliable transmit buffer is empty, copy the current message out - if (!chan->reliable_length && chan->message.cursize) - { - memcpy (chan->reliable_buf, chan->message_buf, chan->message.cursize); - chan->reliable_length = chan->message.cursize; - chan->message.cursize = 0; - chan->reliable_sequence ^= 1; - send_reliable = true; - } - -// write the packet header - send.data = send_buf; - send.maxsize = sizeof(send_buf); - send.cursize = 0; - - w1 = chan->outgoing_sequence | (send_reliable<<31); - w2 = chan->incoming_sequence | (chan->incoming_reliable_sequence<<31); - - chan->outgoing_sequence++; - - MSG_WriteLong (&send, w1); - MSG_WriteLong (&send, w2); - - // send the qport if we are a client -#ifndef SERVERONLY - if (chan->net_socket == net_clientsocket) - MSG_WriteShort (&send, cls.qport); -#endif - -// copy the reliable message to the packet first - if (send_reliable) - { - SZ_Write (&send, chan->reliable_buf, chan->reliable_length); - chan->last_reliable_sequence = chan->outgoing_sequence; - } - -// add the unreliable part if space is available - if (send.maxsize - send.cursize >= length) - SZ_Write (&send, data, length); - -// send the datagram - i = chan->outgoing_sequence & (MAX_LATENT-1); - chan->outgoing_size[i] = send.cursize; - chan->outgoing_time[i] = realtime; - - //zoid, no input in demo playback mode -#ifndef SERVERONLY - if (!cls.demoplayback) -#endif - NET_SendPacket (chan->net_socket, send.cursize, send.data, chan->remote_address); - - if (chan->cleartime < realtime) - chan->cleartime = realtime + send.cursize*chan->rate; - else - chan->cleartime += send.cursize*chan->rate; -#ifdef SERVERONLY - if (ServerPaused()) - chan->cleartime = realtime; -#endif - - if (showpackets.value) - Con_Printf ("--> s=%i(%i) a=%i(%i) %i\n" - , chan->outgoing_sequence - , send_reliable - , chan->incoming_sequence - , chan->incoming_reliable_sequence - , send.cursize); - -} - -/* -================= -Netchan_Process - -called when the current net_message is from remote_address -modifies net_message so that it points to the packet payload -================= -*/ -qboolean Netchan_Process (netchan_t *chan) -{ - unsigned sequence, sequence_ack; - unsigned reliable_ack, reliable_message; -#if defined(SERVERONLY) - int qport; -#endif - - if ( -#ifndef SERVERONLY - !cls.demoplayback && -#endif - !NET_CompareAdr (net_from, chan->remote_address)) - return false; - -// get sequence numbers - MSG_BeginReading (); - sequence = MSG_ReadLong (); - sequence_ack = MSG_ReadLong (); - - // read the qport if we are a server -#ifdef SERVERONLY - qport = MSG_ReadShort (); -#endif - - reliable_message = sequence >> 31; - reliable_ack = sequence_ack >> 31; - - sequence &= ~(1<<31); - sequence_ack &= ~(1<<31); - - if (showpackets.value) - Con_Printf ("<-- s=%i(%i) a=%i(%i) %i\n" - , sequence - , reliable_message - , sequence_ack - , reliable_ack - , net_message.cursize); - -// get a rate estimation -#if 0 - if (chan->outgoing_sequence - sequence_ack < MAX_LATENT) - { - int i; - double time, rate; - - i = sequence_ack & (MAX_LATENT - 1); - time = realtime - chan->outgoing_time[i]; - time -= 0.1; // subtract 100 ms - if (time <= 0) - { // gotta be a digital link for <100 ms ping - if (chan->rate > 1.0/5000) - chan->rate = 1.0/5000; - } - else - { - if (chan->outgoing_size[i] < 512) - { // only deal with small messages - rate = chan->outgoing_size[i]/time; - if (rate > 5000) - rate = 5000; - rate = 1.0/rate; - if (chan->rate > rate) - chan->rate = rate; - } - } - } -#endif - -// -// discard stale or duplicated packets -// - if (sequence <= (unsigned)chan->incoming_sequence) - { - if (showdrop.value) - Con_Printf ("%s:Out of order packet %i at %i\n" - , NET_AdrToString (chan->remote_address) - , sequence - , chan->incoming_sequence); - return false; - } - -// -// dropped packets don't keep the message from being used -// - net_drop = sequence - (chan->incoming_sequence+1); - if (net_drop > 0) - { - chan->drop_count += 1; - - if (showdrop.value) - Con_Printf ("%s:Dropped %i packets at %i\n" - , NET_AdrToString (chan->remote_address) - , sequence-(chan->incoming_sequence+1) - , sequence); - } - -// -// if the current outgoing reliable message has been acknowledged -// clear the buffer to make way for the next -// - if (reliable_ack == (unsigned)chan->reliable_sequence) - chan->reliable_length = 0; // it has been received - -// -// if this message contains a reliable message, bump incoming_reliable_sequence -// - chan->incoming_sequence = sequence; - chan->incoming_acknowledged = sequence_ack; - chan->incoming_reliable_acknowledged = reliable_ack; - if (reliable_message) - chan->incoming_reliable_sequence ^= 1; - -// -// the message can now be read from the current message pointer -// update statistics counters -// - chan->frame_latency = chan->frame_latency*OLD_AVG - + (chan->outgoing_sequence-sequence_ack)*(1.0-OLD_AVG); - chan->frame_rate = chan->frame_rate*OLD_AVG - + (realtime-chan->last_received)*(1.0-OLD_AVG); - chan->good_count += 1; - - chan->last_received = realtime; - - return true; -} - +/* +Copyright (C) 1996-1997 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 "quakedef.h" + +#ifdef _WIN32 +#include "winquake.h" +#endif + +#define PACKET_HEADER 8 + +/* + +packet header +------------- +31 sequence +1 does this message contain a reliable payload +31 acknowledge sequence +1 acknowledge receipt of even/odd message +16 qport + +The remote connection never knows if it missed a reliable message, the +local side detects that it has been dropped by seeing a sequence acknowledge +higher thatn the last reliable sequence, but without the correct evon/odd +bit for the reliable set. + +If the sender notices that a reliable message has been dropped, it will be +retransmitted. It will not be retransmitted again until a message after +the retransmit has been acknowledged and the reliable still failed to get there. + +if the sequence number is -1, the packet should be handled without a netcon + +The reliable message can be added to at any time by doing +MSG_Write* (&netchan->message, ). + +If the message buffer is overflowed, either by a single message, or by +multiple frames worth piling up while the last reliable transmit goes +unacknowledged, the netchan signals a fatal error. + +Reliable messages are always placed first in a packet, then the unreliable +message is included if there is sufficient room. + +To the receiver, there is no distinction between the reliable and unreliable +parts of the message, they are just processed out as a single larger message. + +Illogical packet sequence numbers cause the packet to be dropped, but do +not kill the connection. This, combined with the tight window of valid +reliable acknowledgement numbers provides protection against malicious +address spoofing. + +The qport field is a workaround for bad address translating routers that +sometimes remap the client's source port on a packet during gameplay. + +If the base part of the net address matches and the qport matches, then the +channel matches even if the IP port differs. The IP port should be updated +to the new value before sending out any replies. + + +*/ + +int net_drop; +cvar_t showpackets = {"showpackets", "0"}; +cvar_t showdrop = {"showdrop", "0"}; +cvar_t qport = {"qport", "0"}; + +/* +=============== +Netchan_Init + +=============== +*/ +void Netchan_Init (void) +{ + int port; + + // pick a port value that should be nice and random +#ifdef _WIN32 + port = ((int)(timeGetTime()*1000) * time(NULL)) & 0xffff; +#else + port = ((int)(getpid()+getuid()*1000) * time(NULL)) & 0xffff; +#endif + + Cvar_RegisterVariable (&showpackets); + Cvar_RegisterVariable (&showdrop); + Cvar_RegisterVariable (&qport); + Cvar_SetValue(&qport, port); +} + +/* +=============== +Netchan_OutOfBand + +Sends an out-of-band datagram +================ +*/ +void Netchan_OutOfBand (int net_socket, netadr_t adr, int length, byte *data) +{ + sizebuf_t send; + byte send_buf[MAX_MSGLEN + PACKET_HEADER]; + +// write the packet header + send.data = send_buf; + send.maxsize = sizeof(send_buf); + send.cursize = 0; + + MSG_WriteLong (&send, -1); // -1 sequence means out of band + SZ_Write (&send, data, length); + +// send the datagram + //zoid, no input in demo playback mode +#ifndef SERVERONLY + if (!cls.demoplayback) +#endif + NET_SendPacket (net_socket, send.cursize, send.data, adr); +} + +/* +=============== +Netchan_OutOfBandPrint + +Sends a text message in an out-of-band datagram +================ +*/ +void Netchan_OutOfBandPrint (int net_socket, netadr_t adr, char *format, ...) +{ + va_list argptr; + static char string[8192]; // ??? why static? + + va_start (argptr, format); + vsprintf (string, format,argptr); + va_end (argptr); + + + Netchan_OutOfBand (net_socket, adr, strlen(string), (byte *)string); +} + + +/* +============== +Netchan_Setup + +called to open a channel to a remote system +============== +*/ +void Netchan_Setup (netchan_t *chan, netadr_t adr, int qport, int net_socket) +{ + memset (chan, 0, sizeof(*chan)); + + chan->net_socket = net_socket; + + chan->remote_address = adr; + chan->last_received = realtime; + + chan->message.data = chan->message_buf; + chan->message.allowoverflow = true; + chan->message.maxsize = sizeof(chan->message_buf); + + chan->qport = qport; + + chan->rate = 1.0/2500; +} + + +/* +=============== +Netchan_CanPacket + +Returns true if the bandwidth choke isn't active +================ +*/ +#define MAX_BACKUP 200 +qboolean Netchan_CanPacket (netchan_t *chan) +{ + if (chan->cleartime < realtime + MAX_BACKUP*chan->rate) + return true; + return false; +} + + +/* +=============== +Netchan_CanReliable + +Returns true if the bandwidth choke isn't +================ +*/ +qboolean Netchan_CanReliable (netchan_t *chan) +{ + if (chan->reliable_length) + return false; // waiting for ack + return Netchan_CanPacket (chan); +} + +#ifdef SERVERONLY +qboolean ServerPaused(void); +#endif + +/* +=============== +Netchan_Transmit + +tries to send an unreliable message to a connection, and handles the +transmition / retransmition of the reliable messages. + +A 0 length will still generate a packet and deal with the reliable messages. +================ +*/ +void Netchan_Transmit (netchan_t *chan, int length, byte *data) +{ + sizebuf_t send; + byte send_buf[MAX_MSGLEN + PACKET_HEADER]; + qboolean send_reliable; + unsigned w1, w2; + int i; + +// check for message overflow + if (chan->message.overflowed) + { + chan->fatal_error = true; + Con_Printf ("%s:Outgoing message overflow\n" + , NET_AdrToString (chan->remote_address)); + return; + } + +// if the remote side dropped the last reliable message, resend it + send_reliable = false; + + if (chan->incoming_acknowledged > chan->last_reliable_sequence + && chan->incoming_reliable_acknowledged != chan->reliable_sequence) + send_reliable = true; + +// if the reliable transmit buffer is empty, copy the current message out + if (!chan->reliable_length && chan->message.cursize) + { + memcpy (chan->reliable_buf, chan->message_buf, chan->message.cursize); + chan->reliable_length = chan->message.cursize; + chan->message.cursize = 0; + chan->reliable_sequence ^= 1; + send_reliable = true; + } + +// write the packet header + send.data = send_buf; + send.maxsize = sizeof(send_buf); + send.cursize = 0; + + w1 = chan->outgoing_sequence | (send_reliable<<31); + w2 = chan->incoming_sequence | (chan->incoming_reliable_sequence<<31); + + chan->outgoing_sequence++; + + MSG_WriteLong (&send, w1); + MSG_WriteLong (&send, w2); + + // send the qport if we are a client +#ifndef SERVERONLY + if (chan->net_socket == net_clientsocket) + MSG_WriteShort (&send, cls.qport); +#endif + +// copy the reliable message to the packet first + if (send_reliable) + { + SZ_Write (&send, chan->reliable_buf, chan->reliable_length); + chan->last_reliable_sequence = chan->outgoing_sequence; + } + +// add the unreliable part if space is available + if (send.maxsize - send.cursize >= length) + SZ_Write (&send, data, length); + +// send the datagram + i = chan->outgoing_sequence & (MAX_LATENT-1); + chan->outgoing_size[i] = send.cursize; + chan->outgoing_time[i] = realtime; + + //zoid, no input in demo playback mode +#ifndef SERVERONLY + if (!cls.demoplayback) +#endif + NET_SendPacket (chan->net_socket, send.cursize, send.data, chan->remote_address); + + if (chan->cleartime < realtime) + chan->cleartime = realtime + send.cursize*chan->rate; + else + chan->cleartime += send.cursize*chan->rate; +#ifdef SERVERONLY + if (ServerPaused()) + chan->cleartime = realtime; +#endif + + if (showpackets.value) + Con_Printf ("--> s=%i(%i) a=%i(%i) %i\n" + , chan->outgoing_sequence + , send_reliable + , chan->incoming_sequence + , chan->incoming_reliable_sequence + , send.cursize); + +} + +/* +================= +Netchan_Process + +called when the current net_message is from remote_address +modifies net_message so that it points to the packet payload +================= +*/ +qboolean Netchan_Process (netchan_t *chan) +{ + unsigned sequence, sequence_ack; + unsigned reliable_ack, reliable_message; +#if defined(SERVERONLY) + int qport; +#endif + + if ( +#ifndef SERVERONLY + !cls.demoplayback && +#endif + !NET_CompareAdr (net_from, chan->remote_address)) + return false; + +// get sequence numbers + MSG_BeginReading (); + sequence = MSG_ReadLong (); + sequence_ack = MSG_ReadLong (); + + // read the qport if we are a server +#ifdef SERVERONLY + qport = MSG_ReadShort (); +#endif + + reliable_message = sequence >> 31; + reliable_ack = sequence_ack >> 31; + + sequence &= ~(1<<31); + sequence_ack &= ~(1<<31); + + if (showpackets.value) + Con_Printf ("<-- s=%i(%i) a=%i(%i) %i\n" + , sequence + , reliable_message + , sequence_ack + , reliable_ack + , net_message.cursize); + +// get a rate estimation +#if 0 + if (chan->outgoing_sequence - sequence_ack < MAX_LATENT) + { + int i; + double time, rate; + + i = sequence_ack & (MAX_LATENT - 1); + time = realtime - chan->outgoing_time[i]; + time -= 0.1; // subtract 100 ms + if (time <= 0) + { // gotta be a digital link for <100 ms ping + if (chan->rate > 1.0/5000) + chan->rate = 1.0/5000; + } + else + { + if (chan->outgoing_size[i] < 512) + { // only deal with small messages + rate = chan->outgoing_size[i]/time; + if (rate > 5000) + rate = 5000; + rate = 1.0/rate; + if (chan->rate > rate) + chan->rate = rate; + } + } + } +#endif + +// +// discard stale or duplicated packets +// + if (sequence <= (unsigned)chan->incoming_sequence) + { + if (showdrop.value) + Con_Printf ("%s:Out of order packet %i at %i\n" + , NET_AdrToString (chan->remote_address) + , sequence + , chan->incoming_sequence); + return false; + } + +// +// dropped packets don't keep the message from being used +// + net_drop = sequence - (chan->incoming_sequence+1); + if (net_drop > 0) + { + chan->drop_count += 1; + + if (showdrop.value) + Con_Printf ("%s:Dropped %i packets at %i\n" + , NET_AdrToString (chan->remote_address) + , sequence-(chan->incoming_sequence+1) + , sequence); + } + +// +// if the current outgoing reliable message has been acknowledged +// clear the buffer to make way for the next +// + if (reliable_ack == (unsigned)chan->reliable_sequence) + chan->reliable_length = 0; // it has been received + +// +// if this message contains a reliable message, bump incoming_reliable_sequence +// + chan->incoming_sequence = sequence; + chan->incoming_acknowledged = sequence_ack; + chan->incoming_reliable_acknowledged = reliable_ack; + if (reliable_message) + chan->incoming_reliable_sequence ^= 1; + +// +// the message can now be read from the current message pointer +// update statistics counters +// + chan->frame_latency = chan->frame_latency*OLD_AVG + + (chan->outgoing_sequence-sequence_ack)*(1.0-OLD_AVG); + chan->frame_rate = chan->frame_rate*OLD_AVG + + (realtime-chan->last_received)*(1.0-OLD_AVG); + chan->good_count += 1; + + chan->last_received = realtime; + + return true; +} + diff --git a/source/net_udp.c b/source/net_udp.c index ca951b45..15ff1b4c 100644 --- a/source/net_udp.c +++ b/source/net_udp.c @@ -1,293 +1,293 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// net_main.c - -#include "quakedef.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(sun) -#include -#endif - -#ifdef sun -#include -#endif - -#ifdef NeXT -#include -#endif - -netadr_t net_local_adr; - -netadr_t net_from; -sizebuf_t net_message; -int net_socket; // non blocking, for receives -int net_send_socket; // blocking, for sends - -int net_clientsocket; // @@@ dummy -int net_serversocket; // @@@ dummy - -#define MAX_UDP_PACKET 8192 -byte net_message_buffer[MAX_UDP_PACKET]; - -int gethostname (char *, int); -int close (int); - -//============================================================================= - -void NetadrToSockadr (netadr_t *a, struct sockaddr_in *s) -{ - memset (s, 0, sizeof(*s)); - s->sin_family = AF_INET; - - *(int *)&s->sin_addr = *(int *)&a->ip; - s->sin_port = a->port; -} - -void SockadrToNetadr (struct sockaddr_in *s, netadr_t *a) -{ - *(int *)&a->ip = *(int *)&s->sin_addr; - a->port = s->sin_port; -} - -qboolean NET_CompareBaseAdr (netadr_t a, netadr_t b) -{ - if (a.ip[0] == b.ip[0] && a.ip[1] == b.ip[1] && a.ip[2] == b.ip[2] && a.ip[3] == b.ip[3]) - return true; - return false; -} - - -qboolean NET_CompareAdr (netadr_t a, netadr_t b) -{ - if (a.ip[0] == b.ip[0] && a.ip[1] == b.ip[1] && a.ip[2] == b.ip[2] && a.ip[3] == b.ip[3] && a.port == b.port) - return true; - return false; -} - -char *NET_AdrToString (netadr_t a) -{ - static char s[64]; - - sprintf (s, "%i.%i.%i.%i:%i", a.ip[0], a.ip[1], a.ip[2], a.ip[3], ntohs(a.port)); - - return s; -} - -char *NET_BaseAdrToString (netadr_t a) -{ - static char s[64]; - - sprintf (s, "%i.%i.%i.%i", a.ip[0], a.ip[1], a.ip[2], a.ip[3]); - - return s; -} - -/* -============= -NET_StringToAdr - -idnewt -idnewt:28000 -192.246.40.70 -192.246.40.70:28000 -============= -*/ -qboolean NET_StringToAdr (char *s, netadr_t *a) -{ - struct hostent *h; - struct sockaddr_in sadr; - char *colon; - char copy[128]; - - - memset (&sadr, 0, sizeof(sadr)); - sadr.sin_family = AF_INET; - - sadr.sin_port = 0; - - strcpy (copy, s); - // strip off a trailing :port if present - for (colon = copy ; *colon ; colon++) - if (*colon == ':') - { - *colon = 0; - sadr.sin_port = htons(atoi(colon+1)); - } - - if (copy[0] >= '0' && copy[0] <= '9') - { - *(int *)&sadr.sin_addr = inet_addr(copy); - } - else - { - if (! (h = gethostbyname(copy)) ) - return 0; - *(int *)&sadr.sin_addr = *(int *)h->h_addr_list[0]; - } - - SockadrToNetadr (&sadr, a); - - return true; -} - - -//============================================================================= - -qboolean NET_GetPacket (int dummy) -{ - int ret; - struct sockaddr_in from; - int fromlen; - - fromlen = sizeof(from); - ret = recvfrom (net_socket, net_message_buffer, sizeof(net_message_buffer), 0, (struct sockaddr *)&from, &fromlen); - if (ret == -1) { - if (errno == EWOULDBLOCK) - return false; - if (errno == ECONNREFUSED) - return false; - Sys_Printf ("NET_GetPacket: %s\n", strerror(errno)); - return false; - } - - net_message.cursize = ret; - SockadrToNetadr (&from, &net_from); - - return ret; -} - -//============================================================================= - -void NET_SendPacket (int dummy, int length, void *data, netadr_t to) -{ - int ret; - struct sockaddr_in addr; - - NetadrToSockadr (&to, &addr); - - ret = sendto (net_socket, data, length, 0, (struct sockaddr *)&addr, sizeof(addr) ); - if (ret == -1) { - if (errno == EWOULDBLOCK) - return; - if (errno == ECONNREFUSED) - return; - Sys_Printf ("NET_SendPacket: %s\n", strerror(errno)); - } -} - -//============================================================================= - -int UDP_OpenSocket (int port) -{ - int newsocket; - struct sockaddr_in address; - qboolean _true = true; - int i; - - if ((newsocket = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) - Sys_Error ("UDP_OpenSocket: socket:", strerror(errno)); - if (ioctl (newsocket, FIONBIO, (char *)&_true) == -1) - Sys_Error ("UDP_OpenSocket: ioctl FIONBIO:", strerror(errno)); - address.sin_family = AF_INET; -//ZOID -- check for interface binding option - if ((i = COM_CheckParm("-ip")) != 0 && i < com_argc) { - address.sin_addr.s_addr = inet_addr(com_argv[i+1]); - Con_Printf("Binding to IP Interface Address of %s\n", - inet_ntoa(address.sin_addr)); - } else - address.sin_addr.s_addr = INADDR_ANY; - if (port == PORT_ANY) - address.sin_port = 0; - else - address.sin_port = htons((short)port); - if( bind (newsocket, (void *)&address, sizeof(address)) == -1) - Sys_Error ("UDP_OpenSocket: bind: %s", strerror(errno)); - - return newsocket; -} - -void NET_GetLocalAddress (void) -{ - char buff[MAXHOSTNAMELEN]; - struct sockaddr_in address; - int namelen; - - gethostname(buff, MAXHOSTNAMELEN); - buff[MAXHOSTNAMELEN-1] = 0; - - NET_StringToAdr (buff, &net_local_adr); - - namelen = sizeof(address); - if (getsockname (net_socket, (struct sockaddr *)&address, &namelen) == -1) - Sys_Error ("NET_Init: getsockname:", strerror(errno)); - net_local_adr.port = address.sin_port; - - Con_Printf("IP address %s\n", NET_AdrToString (net_local_adr) ); -} - -/* -==================== -NET_Init -==================== -*/ -void NET_Init (int port, int dummy) -{ - if (dummy) - port = dummy; - - // - // open the single socket to be used for all communications - // - net_socket = UDP_OpenSocket (port); - - // - // init the message buffer - // - net_message.maxsize = sizeof(net_message_buffer); - net_message.data = net_message_buffer; - - // - // determine my name & address - // - NET_GetLocalAddress (); - - Con_Printf("UDP Initialized\n"); -} - -/* -==================== -NET_Shutdown -==================== -*/ -void NET_Shutdown (void) -{ - close (net_socket); -} - +/* +Copyright (C) 1996-1997 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. + +*/ +// net_main.c + +#include "quakedef.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(sun) +#include +#endif + +#ifdef sun +#include +#endif + +#ifdef NeXT +#include +#endif + +netadr_t net_local_adr; + +netadr_t net_from; +sizebuf_t net_message; +int net_socket; // non blocking, for receives +int net_send_socket; // blocking, for sends + +int net_clientsocket; // @@@ dummy +int net_serversocket; // @@@ dummy + +#define MAX_UDP_PACKET 8192 +byte net_message_buffer[MAX_UDP_PACKET]; + +int gethostname (char *, int); +int close (int); + +//============================================================================= + +void NetadrToSockadr (netadr_t *a, struct sockaddr_in *s) +{ + memset (s, 0, sizeof(*s)); + s->sin_family = AF_INET; + + *(int *)&s->sin_addr = *(int *)&a->ip; + s->sin_port = a->port; +} + +void SockadrToNetadr (struct sockaddr_in *s, netadr_t *a) +{ + *(int *)&a->ip = *(int *)&s->sin_addr; + a->port = s->sin_port; +} + +qboolean NET_CompareBaseAdr (netadr_t a, netadr_t b) +{ + if (a.ip[0] == b.ip[0] && a.ip[1] == b.ip[1] && a.ip[2] == b.ip[2] && a.ip[3] == b.ip[3]) + return true; + return false; +} + + +qboolean NET_CompareAdr (netadr_t a, netadr_t b) +{ + if (a.ip[0] == b.ip[0] && a.ip[1] == b.ip[1] && a.ip[2] == b.ip[2] && a.ip[3] == b.ip[3] && a.port == b.port) + return true; + return false; +} + +char *NET_AdrToString (netadr_t a) +{ + static char s[64]; + + sprintf (s, "%i.%i.%i.%i:%i", a.ip[0], a.ip[1], a.ip[2], a.ip[3], ntohs(a.port)); + + return s; +} + +char *NET_BaseAdrToString (netadr_t a) +{ + static char s[64]; + + sprintf (s, "%i.%i.%i.%i", a.ip[0], a.ip[1], a.ip[2], a.ip[3]); + + return s; +} + +/* +============= +NET_StringToAdr + +idnewt +idnewt:28000 +192.246.40.70 +192.246.40.70:28000 +============= +*/ +qboolean NET_StringToAdr (char *s, netadr_t *a) +{ + struct hostent *h; + struct sockaddr_in sadr; + char *colon; + char copy[128]; + + + memset (&sadr, 0, sizeof(sadr)); + sadr.sin_family = AF_INET; + + sadr.sin_port = 0; + + strcpy (copy, s); + // strip off a trailing :port if present + for (colon = copy ; *colon ; colon++) + if (*colon == ':') + { + *colon = 0; + sadr.sin_port = htons(atoi(colon+1)); + } + + if (copy[0] >= '0' && copy[0] <= '9') + { + *(int *)&sadr.sin_addr = inet_addr(copy); + } + else + { + if (! (h = gethostbyname(copy)) ) + return 0; + *(int *)&sadr.sin_addr = *(int *)h->h_addr_list[0]; + } + + SockadrToNetadr (&sadr, a); + + return true; +} + + +//============================================================================= + +qboolean NET_GetPacket (int dummy) +{ + int ret; + struct sockaddr_in from; + int fromlen; + + fromlen = sizeof(from); + ret = recvfrom (net_socket, net_message_buffer, sizeof(net_message_buffer), 0, (struct sockaddr *)&from, &fromlen); + if (ret == -1) { + if (errno == EWOULDBLOCK) + return false; + if (errno == ECONNREFUSED) + return false; + Sys_Printf ("NET_GetPacket: %s\n", strerror(errno)); + return false; + } + + net_message.cursize = ret; + SockadrToNetadr (&from, &net_from); + + return ret; +} + +//============================================================================= + +void NET_SendPacket (int dummy, int length, void *data, netadr_t to) +{ + int ret; + struct sockaddr_in addr; + + NetadrToSockadr (&to, &addr); + + ret = sendto (net_socket, data, length, 0, (struct sockaddr *)&addr, sizeof(addr) ); + if (ret == -1) { + if (errno == EWOULDBLOCK) + return; + if (errno == ECONNREFUSED) + return; + Sys_Printf ("NET_SendPacket: %s\n", strerror(errno)); + } +} + +//============================================================================= + +int UDP_OpenSocket (int port) +{ + int newsocket; + struct sockaddr_in address; + qboolean _true = true; + int i; + + if ((newsocket = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) + Sys_Error ("UDP_OpenSocket: socket:", strerror(errno)); + if (ioctl (newsocket, FIONBIO, (char *)&_true) == -1) + Sys_Error ("UDP_OpenSocket: ioctl FIONBIO:", strerror(errno)); + address.sin_family = AF_INET; +//ZOID -- check for interface binding option + if ((i = COM_CheckParm("-ip")) != 0 && i < com_argc) { + address.sin_addr.s_addr = inet_addr(com_argv[i+1]); + Con_Printf("Binding to IP Interface Address of %s\n", + inet_ntoa(address.sin_addr)); + } else + address.sin_addr.s_addr = INADDR_ANY; + if (port == PORT_ANY) + address.sin_port = 0; + else + address.sin_port = htons((short)port); + if( bind (newsocket, (void *)&address, sizeof(address)) == -1) + Sys_Error ("UDP_OpenSocket: bind: %s", strerror(errno)); + + return newsocket; +} + +void NET_GetLocalAddress (void) +{ + char buff[MAXHOSTNAMELEN]; + struct sockaddr_in address; + int namelen; + + gethostname(buff, MAXHOSTNAMELEN); + buff[MAXHOSTNAMELEN-1] = 0; + + NET_StringToAdr (buff, &net_local_adr); + + namelen = sizeof(address); + if (getsockname (net_socket, (struct sockaddr *)&address, &namelen) == -1) + Sys_Error ("NET_Init: getsockname:", strerror(errno)); + net_local_adr.port = address.sin_port; + + Con_Printf("IP address %s\n", NET_AdrToString (net_local_adr) ); +} + +/* +==================== +NET_Init +==================== +*/ +void NET_Init (int port, int dummy) +{ + if (dummy) + port = dummy; + + // + // open the single socket to be used for all communications + // + net_socket = UDP_OpenSocket (port); + + // + // init the message buffer + // + net_message.maxsize = sizeof(net_message_buffer); + net_message.data = net_message_buffer; + + // + // determine my name & address + // + NET_GetLocalAddress (); + + Con_Printf("UDP Initialized\n"); +} + +/* +==================== +NET_Shutdown +==================== +*/ +void NET_Shutdown (void) +{ + close (net_socket); +} + diff --git a/source/net_wins.c b/source/net_wins.c index fc74b32e..3496b228 100644 --- a/source/net_wins.c +++ b/source/net_wins.c @@ -1,372 +1,372 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// net_wins.c - -#include "quakedef.h" -#include "winquake.h" - -netadr_t net_local_adr; - -netadr_t net_from; -sizebuf_t net_message; -int net_clientsocket; -int net_serversocket; - -#define MAX_UDP_PACKET (MAX_MSGLEN*2) // one more than msg + header -byte net_message_buffer[MAX_UDP_PACKET]; - -WSADATA winsockdata; - -// Tonik --> -#define PORT_LOOPBACK 65535 -int loop_c2s_messageLength; -char loop_c2s_message[MAX_UDP_PACKET]; -int loop_s2c_messageLength; -char loop_s2c_message[MAX_UDP_PACKET]; -// <-- Tonik - -//============================================================================= - -void NetadrToSockadr (netadr_t *a, struct sockaddr_in *s) -{ - memset (s, 0, sizeof(*s)); - s->sin_family = AF_INET; - - *(int *)&s->sin_addr = *(int *)&a->ip; - s->sin_port = a->port; -} - -void SockadrToNetadr (struct sockaddr_in *s, netadr_t *a) -{ - *(int *)&a->ip = *(int *)&s->sin_addr; - a->port = s->sin_port; -} - -qboolean NET_CompareBaseAdr (netadr_t a, netadr_t b) -{ - if (a.ip[0] == b.ip[0] && a.ip[1] == b.ip[1] && a.ip[2] == b.ip[2] && a.ip[3] == b.ip[3]) - return true; - return false; -} - -qboolean NET_CompareAdr (netadr_t a, netadr_t b) -{ - if (a.ip[0] == b.ip[0] && a.ip[1] == b.ip[1] && a.ip[2] == b.ip[2] && a.ip[3] == b.ip[3] && a.port == b.port) - return true; - return false; -} - -char *NET_AdrToString (netadr_t a) -{ - static char s[64]; - - - sprintf (s, "%i.%i.%i.%i:%i", a.ip[0], a.ip[1], a.ip[2], a.ip[3], ntohs(a.port)); - - return s; -} - -char *NET_BaseAdrToString (netadr_t a) -{ - static char s[64]; - - sprintf (s, "%i.%i.%i.%i", a.ip[0], a.ip[1], a.ip[2], a.ip[3]); - - return s; -} - -/* -============= -NET_StringToAdr - -idnewt -idnewt:28000 -192.246.40.70 -192.246.40.70:28000 -============= -*/ -qboolean NET_StringToAdr (char *s, netadr_t *a) -{ - struct hostent *h; - struct sockaddr_in sadr; - char *colon; - char copy[128]; - - memset (&sadr, 0, sizeof(sadr)); - sadr.sin_family = AF_INET; - - sadr.sin_port = 0; - - strcpy (copy, s); - // strip off a trailing :port if present - for (colon = copy ; *colon ; colon++) - if (*colon == ':') - { - *colon = 0; - sadr.sin_port = htons((short)atoi(colon+1)); - } - - if (copy[0] >= '0' && copy[0] <= '9') - { - *(int *)&sadr.sin_addr = inet_addr(copy); - } - else - { - if ((h = gethostbyname(copy)) == 0) - return 0; - *(int *)&sadr.sin_addr = *(int *)h->h_addr_list[0]; - } - - SockadrToNetadr (&sadr, a); - - return true; -} - - -//============================================================================= - -qboolean NET_GetPacket (int net_socket) -{ - int ret; - struct sockaddr_in from; - int fromlen; - -// Tonik --> - if (net_socket == net_clientsocket && loop_s2c_messageLength > 0) - { - memcpy (net_message_buffer, loop_s2c_message, loop_s2c_messageLength); - net_message.cursize = loop_s2c_messageLength; - loop_s2c_messageLength = 0; - memset (&from, 0, sizeof(from)); - from.sin_port = PORT_LOOPBACK; - SockadrToNetadr (&from, &net_from); - return net_message.cursize; - } - - if (net_socket == net_serversocket && loop_c2s_messageLength > 0) - { -// Con_DPrintf ("NET_GetPacket: c2s\n"); - memcpy (net_message_buffer, loop_c2s_message, loop_c2s_messageLength); - net_message.cursize = loop_c2s_messageLength; - loop_c2s_messageLength = 0; - memset (&from, 0, sizeof(from)); - from.sin_port = PORT_LOOPBACK; - SockadrToNetadr (&from, &net_from); - return net_message.cursize; - } -// <-- Tonik - - fromlen = sizeof(from); - ret = recvfrom (net_socket, (char *)net_message_buffer, sizeof(net_message_buffer), 0, (struct sockaddr *)&from, &fromlen); - SockadrToNetadr (&from, &net_from); - - if (ret == -1) - { - int errno = WSAGetLastError(); - - if (errno == WSAEWOULDBLOCK) - return false; - if (errno == WSAEMSGSIZE) { - Con_Printf ("Warning: Oversize packet from %s\n", - NET_AdrToString (net_from)); - return false; - } - if (errno == 10054) { - Con_DPrintf ("NET_GetPacket: Error 10054 from %s\n", NET_AdrToString (net_from)); - return false; - } - - Sys_Error ("NET_GetPacket: %s", strerror(errno)); - } - - net_message.cursize = ret; - if (ret == sizeof(net_message_buffer) ) - { - Con_Printf ("Oversize packet from %s\n", NET_AdrToString (net_from)); - return false; - } - - return ret; -} - -//============================================================================= - -void NET_SendPacket (int net_socket, int length, void *data, netadr_t to) -{ - int ret; - struct sockaddr_in addr; - -// Tonik --> - if (*(int *)&to.ip == 0 && to.port == 65535) // Loopback - { - if (net_socket == net_clientsocket) - { -// if (loop_c2s_messageLength) -// Con_Printf ("Warning: NET_SendPacket: loop_c2s: NET_SendPacket without NET_GetPacket\n"); - memcpy (loop_c2s_message, data, length); - loop_c2s_messageLength = length; - return; - } - else if (net_socket == net_serversocket) - { -// if (loop_s2c_messageLength) -// Con_Printf ("Warning: NET_SendPacket: loop_s2c: NET_SendPacket without NET_GetPacket\n"); - memcpy (loop_s2c_message, data, length); - loop_s2c_messageLength = length; - return; - } - Sys_Error("NET_SendPacket: loopback: unknown socket"); - return; - } -// <-- Tonik - - NetadrToSockadr (&to, &addr); - - ret = sendto (net_socket, data, length, 0, (struct sockaddr *)&addr, sizeof(addr) ); - if (ret == -1) - { - int err = WSAGetLastError(); - -// wouldblock is silent - if (err == WSAEWOULDBLOCK) - return; - -#ifndef SERVERONLY - if (err == WSAEADDRNOTAVAIL) - Con_DPrintf("NET_SendPacket Warning: %i\n", err); - else -#endif - Con_Printf ("NET_SendPacket ERROR: %i\n", errno); - } -} - -//============================================================================= - -int UDP_OpenSocket (int port, qboolean crash) -{ - int newsocket; - struct sockaddr_in address; - unsigned long _true = true; - int i; - - if ((newsocket = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) - Sys_Error ("UDP_OpenSocket: socket:", strerror(errno)); - - if (ioctlsocket (newsocket, FIONBIO, &_true) == -1) - Sys_Error ("UDP_OpenSocket: ioctl FIONBIO:", strerror(errno)); - - address.sin_family = AF_INET; -//ZOID -- check for interface binding option - if ((i = COM_CheckParm("-ip")) != 0 && i < com_argc) { - address.sin_addr.s_addr = inet_addr(com_argv[i+1]); - Con_Printf("Binding to IP Interface Address of %s\n", - inet_ntoa(address.sin_addr)); - } else - address.sin_addr.s_addr = INADDR_ANY; - - if (port == PORT_ANY) - address.sin_port = 0; - else - address.sin_port = htons((short)port); - if( bind (newsocket, (void *)&address, sizeof(address)) == -1) - { - Sys_Error ("UDP_OpenSocket: bind: %s", strerror(errno)); - } - - return newsocket; -} - -void NET_GetLocalAddress (int net_socket) // FIXME -{ - char buff[512]; - struct sockaddr_in address; - int namelen; - - gethostname(buff, 512); - buff[512-1] = 0; - - NET_StringToAdr (buff, &net_local_adr); - - namelen = sizeof(address); - if (getsockname (net_socket, (struct sockaddr *)&address, &namelen) == -1) - Sys_Error ("NET_Init: getsockname:", strerror(errno)); - net_local_adr.port = address.sin_port; - - Con_Printf("IP address %s\n", NET_AdrToString (net_local_adr) ); -} - -/* -==================== -NET_Init -==================== -*/ -int __serverport; // so we can open it later -void NET_Init (int clientport, int serverport) -{ - WORD wVersionRequested; - int r; - - wVersionRequested = MAKEWORD(1, 1); - - r = WSAStartup (MAKEWORD(1, 1), &winsockdata); - - if (r) - Sys_Error ("Winsock initialization failed."); - - // - // open the single socket to be used for all communications - // -// net_socket = UDP_OpenSocket (port); - if (clientport) - net_clientsocket = UDP_OpenSocket (clientport, true); - - if (serverport) - net_serversocket = UDP_OpenSocket (serverport, false); - // - // init the message buffer - // - net_message.maxsize = sizeof(net_message_buffer); - net_message.data = net_message_buffer; - - // - // determine my name & address - // - if (clientport) - NET_GetLocalAddress (net_clientsocket); - else if (serverport) - NET_GetLocalAddress (net_serversocket); - - Con_Printf("UDP Initialized\n"); -} - -/* -==================== -NET_Shutdown -==================== -*/ -void NET_Shutdown (void) -{ - if (net_clientsocket) - closesocket (net_clientsocket); - if (net_serversocket) - closesocket (net_serversocket); - WSACleanup (); -} - +/* +Copyright (C) 1996-1997 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. + +*/ +// net_wins.c + +#include "quakedef.h" +#include "winquake.h" + +netadr_t net_local_adr; + +netadr_t net_from; +sizebuf_t net_message; +int net_clientsocket; +int net_serversocket; + +#define MAX_UDP_PACKET (MAX_MSGLEN*2) // one more than msg + header +byte net_message_buffer[MAX_UDP_PACKET]; + +WSADATA winsockdata; + +// Tonik --> +#define PORT_LOOPBACK 65535 +int loop_c2s_messageLength; +char loop_c2s_message[MAX_UDP_PACKET]; +int loop_s2c_messageLength; +char loop_s2c_message[MAX_UDP_PACKET]; +// <-- Tonik + +//============================================================================= + +void NetadrToSockadr (netadr_t *a, struct sockaddr_in *s) +{ + memset (s, 0, sizeof(*s)); + s->sin_family = AF_INET; + + *(int *)&s->sin_addr = *(int *)&a->ip; + s->sin_port = a->port; +} + +void SockadrToNetadr (struct sockaddr_in *s, netadr_t *a) +{ + *(int *)&a->ip = *(int *)&s->sin_addr; + a->port = s->sin_port; +} + +qboolean NET_CompareBaseAdr (netadr_t a, netadr_t b) +{ + if (a.ip[0] == b.ip[0] && a.ip[1] == b.ip[1] && a.ip[2] == b.ip[2] && a.ip[3] == b.ip[3]) + return true; + return false; +} + +qboolean NET_CompareAdr (netadr_t a, netadr_t b) +{ + if (a.ip[0] == b.ip[0] && a.ip[1] == b.ip[1] && a.ip[2] == b.ip[2] && a.ip[3] == b.ip[3] && a.port == b.port) + return true; + return false; +} + +char *NET_AdrToString (netadr_t a) +{ + static char s[64]; + + + sprintf (s, "%i.%i.%i.%i:%i", a.ip[0], a.ip[1], a.ip[2], a.ip[3], ntohs(a.port)); + + return s; +} + +char *NET_BaseAdrToString (netadr_t a) +{ + static char s[64]; + + sprintf (s, "%i.%i.%i.%i", a.ip[0], a.ip[1], a.ip[2], a.ip[3]); + + return s; +} + +/* +============= +NET_StringToAdr + +idnewt +idnewt:28000 +192.246.40.70 +192.246.40.70:28000 +============= +*/ +qboolean NET_StringToAdr (char *s, netadr_t *a) +{ + struct hostent *h; + struct sockaddr_in sadr; + char *colon; + char copy[128]; + + memset (&sadr, 0, sizeof(sadr)); + sadr.sin_family = AF_INET; + + sadr.sin_port = 0; + + strcpy (copy, s); + // strip off a trailing :port if present + for (colon = copy ; *colon ; colon++) + if (*colon == ':') + { + *colon = 0; + sadr.sin_port = htons((short)atoi(colon+1)); + } + + if (copy[0] >= '0' && copy[0] <= '9') + { + *(int *)&sadr.sin_addr = inet_addr(copy); + } + else + { + if ((h = gethostbyname(copy)) == 0) + return 0; + *(int *)&sadr.sin_addr = *(int *)h->h_addr_list[0]; + } + + SockadrToNetadr (&sadr, a); + + return true; +} + + +//============================================================================= + +qboolean NET_GetPacket (int net_socket) +{ + int ret; + struct sockaddr_in from; + int fromlen; + +// Tonik --> + if (net_socket == net_clientsocket && loop_s2c_messageLength > 0) + { + memcpy (net_message_buffer, loop_s2c_message, loop_s2c_messageLength); + net_message.cursize = loop_s2c_messageLength; + loop_s2c_messageLength = 0; + memset (&from, 0, sizeof(from)); + from.sin_port = PORT_LOOPBACK; + SockadrToNetadr (&from, &net_from); + return net_message.cursize; + } + + if (net_socket == net_serversocket && loop_c2s_messageLength > 0) + { +// Con_DPrintf ("NET_GetPacket: c2s\n"); + memcpy (net_message_buffer, loop_c2s_message, loop_c2s_messageLength); + net_message.cursize = loop_c2s_messageLength; + loop_c2s_messageLength = 0; + memset (&from, 0, sizeof(from)); + from.sin_port = PORT_LOOPBACK; + SockadrToNetadr (&from, &net_from); + return net_message.cursize; + } +// <-- Tonik + + fromlen = sizeof(from); + ret = recvfrom (net_socket, (char *)net_message_buffer, sizeof(net_message_buffer), 0, (struct sockaddr *)&from, &fromlen); + SockadrToNetadr (&from, &net_from); + + if (ret == -1) + { + int errno = WSAGetLastError(); + + if (errno == WSAEWOULDBLOCK) + return false; + if (errno == WSAEMSGSIZE) { + Con_Printf ("Warning: Oversize packet from %s\n", + NET_AdrToString (net_from)); + return false; + } + if (errno == 10054) { + Con_DPrintf ("NET_GetPacket: Error 10054 from %s\n", NET_AdrToString (net_from)); + return false; + } + + Sys_Error ("NET_GetPacket: %s", strerror(errno)); + } + + net_message.cursize = ret; + if (ret == sizeof(net_message_buffer) ) + { + Con_Printf ("Oversize packet from %s\n", NET_AdrToString (net_from)); + return false; + } + + return ret; +} + +//============================================================================= + +void NET_SendPacket (int net_socket, int length, void *data, netadr_t to) +{ + int ret; + struct sockaddr_in addr; + +// Tonik --> + if (*(int *)&to.ip == 0 && to.port == 65535) // Loopback + { + if (net_socket == net_clientsocket) + { +// if (loop_c2s_messageLength) +// Con_Printf ("Warning: NET_SendPacket: loop_c2s: NET_SendPacket without NET_GetPacket\n"); + memcpy (loop_c2s_message, data, length); + loop_c2s_messageLength = length; + return; + } + else if (net_socket == net_serversocket) + { +// if (loop_s2c_messageLength) +// Con_Printf ("Warning: NET_SendPacket: loop_s2c: NET_SendPacket without NET_GetPacket\n"); + memcpy (loop_s2c_message, data, length); + loop_s2c_messageLength = length; + return; + } + Sys_Error("NET_SendPacket: loopback: unknown socket"); + return; + } +// <-- Tonik + + NetadrToSockadr (&to, &addr); + + ret = sendto (net_socket, data, length, 0, (struct sockaddr *)&addr, sizeof(addr) ); + if (ret == -1) + { + int err = WSAGetLastError(); + +// wouldblock is silent + if (err == WSAEWOULDBLOCK) + return; + +#ifndef SERVERONLY + if (err == WSAEADDRNOTAVAIL) + Con_DPrintf("NET_SendPacket Warning: %i\n", err); + else +#endif + Con_Printf ("NET_SendPacket ERROR: %i\n", errno); + } +} + +//============================================================================= + +int UDP_OpenSocket (int port, qboolean crash) +{ + int newsocket; + struct sockaddr_in address; + unsigned long _true = true; + int i; + + if ((newsocket = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) + Sys_Error ("UDP_OpenSocket: socket:", strerror(errno)); + + if (ioctlsocket (newsocket, FIONBIO, &_true) == -1) + Sys_Error ("UDP_OpenSocket: ioctl FIONBIO:", strerror(errno)); + + address.sin_family = AF_INET; +//ZOID -- check for interface binding option + if ((i = COM_CheckParm("-ip")) != 0 && i < com_argc) { + address.sin_addr.s_addr = inet_addr(com_argv[i+1]); + Con_Printf("Binding to IP Interface Address of %s\n", + inet_ntoa(address.sin_addr)); + } else + address.sin_addr.s_addr = INADDR_ANY; + + if (port == PORT_ANY) + address.sin_port = 0; + else + address.sin_port = htons((short)port); + if( bind (newsocket, (void *)&address, sizeof(address)) == -1) + { + Sys_Error ("UDP_OpenSocket: bind: %s", strerror(errno)); + } + + return newsocket; +} + +void NET_GetLocalAddress (int net_socket) // FIXME +{ + char buff[512]; + struct sockaddr_in address; + int namelen; + + gethostname(buff, 512); + buff[512-1] = 0; + + NET_StringToAdr (buff, &net_local_adr); + + namelen = sizeof(address); + if (getsockname (net_socket, (struct sockaddr *)&address, &namelen) == -1) + Sys_Error ("NET_Init: getsockname:", strerror(errno)); + net_local_adr.port = address.sin_port; + + Con_Printf("IP address %s\n", NET_AdrToString (net_local_adr) ); +} + +/* +==================== +NET_Init +==================== +*/ +int __serverport; // so we can open it later +void NET_Init (int clientport, int serverport) +{ + WORD wVersionRequested; + int r; + + wVersionRequested = MAKEWORD(1, 1); + + r = WSAStartup (MAKEWORD(1, 1), &winsockdata); + + if (r) + Sys_Error ("Winsock initialization failed."); + + // + // open the single socket to be used for all communications + // +// net_socket = UDP_OpenSocket (port); + if (clientport) + net_clientsocket = UDP_OpenSocket (clientport, true); + + if (serverport) + net_serversocket = UDP_OpenSocket (serverport, false); + // + // init the message buffer + // + net_message.maxsize = sizeof(net_message_buffer); + net_message.data = net_message_buffer; + + // + // determine my name & address + // + if (clientport) + NET_GetLocalAddress (net_clientsocket); + else if (serverport) + NET_GetLocalAddress (net_serversocket); + + Con_Printf("UDP Initialized\n"); +} + +/* +==================== +NET_Shutdown +==================== +*/ +void NET_Shutdown (void) +{ + if (net_clientsocket) + closesocket (net_clientsocket); + if (net_serversocket) + closesocket (net_serversocket); + WSACleanup (); +} + diff --git a/source/nonintel.c b/source/nonintel.c index a19c386c..9163ee52 100644 --- a/source/nonintel.c +++ b/source/nonintel.c @@ -1,62 +1,62 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// -// nonintel.c: code for non-Intel processors only -// - -#include "quakedef.h" - -#if !id386 - -/* -================ -R_Surf8Patch -================ -*/ -void R_Surf8Patch () -{ - // we only patch code on Intel -} - - -/* -================ -R_Surf16Patch -================ -*/ -void R_Surf16Patch () -{ - // we only patch code on Intel -} - - -/* -================ -R_SurfacePatch -================ -*/ -void R_SurfacePatch (void) -{ - // we only patch code on Intel -} - - -#endif // !id386 - +/* +Copyright (C) 1996-1997 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. + +*/ +// +// nonintel.c: code for non-Intel processors only +// + +#include "quakedef.h" + +#if !id386 + +/* +================ +R_Surf8Patch +================ +*/ +void R_Surf8Patch () +{ + // we only patch code on Intel +} + + +/* +================ +R_Surf16Patch +================ +*/ +void R_Surf16Patch () +{ + // we only patch code on Intel +} + + +/* +================ +R_SurfacePatch +================ +*/ +void R_SurfacePatch (void) +{ + // we only patch code on Intel +} + + +#endif // !id386 + diff --git a/source/pmove.c b/source/pmove.c index 18c4dee1..33e3d5f6 100644 --- a/source/pmove.c +++ b/source/pmove.c @@ -1,976 +1,976 @@ -/* -Copyright (C) 1996-1997 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 "quakedef.h" -#include "pmove.h" - -#ifndef SERVERONLY -cvar_t pm_jumpfixtime = {"pm_jumpfixtime","0.05"}; -#endif -cvar_t pm_jumpfix = {"pm_jumpfix","1"}; -cvar_t pm_slidefix = {"pm_slidefix","0"}; // FIXME: remove? -cvar_t pm_ktphysics = {"pm_ktphysics", "0"}; // set this when - // playing on a server running Kombat Teams 2.10 or later - -movevars_t movevars; - -playermove_t pmove; - -int onground; -int waterlevel; -int watertype; - -float frametime; - -vec3_t forward, right, up; - -vec3_t player_mins = {-16, -16, -24}; -vec3_t player_maxs = {16, 16, 32}; - -// #define PM_GRAVITY 800 -// #define PM_STOPSPEED 100 -// #define PM_MAXSPEED 320 -// #define PM_SPECTATORMAXSPEED 500 -// #define PM_ACCELERATE 10 -// #define PM_AIRACCELERATE 0.7 -// #define PM_WATERACCELERATE 10 -// #define PM_FRICTION 6 -// #define PM_WATERFRICTION 1 - -void PM_InitBoxHull (void); - -void Pmove_Init (void) -{ -#ifndef SERVERONLY - Cvar_RegisterVariable (&pm_jumpfixtime); -#endif - -#if defined(SERVERONLY) - Cvar_RegisterVariable (&pm_jumpfix); -#else - pm_jumpfix.value = 1; -#endif - - Cvar_RegisterVariable (&pm_slidefix); - Cvar_RegisterVariable (&pm_ktphysics); - PM_InitBoxHull (); -} - -#define STEPSIZE 18 - - -#define BUTTON_JUMP 2 - - -/* -================== -PM_ClipVelocity - -Slide off of the impacting object -returns the blocked flags (1 = floor, 2 = step / wall) -================== -*/ -#define STOP_EPSILON 0.1 - -int PM_ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce) -{ - float backoff; - float change; - int i, blocked; - - blocked = 0; - if (normal[2] > 0) - blocked |= 1; // floor - if (!normal[2]) - blocked |= 2; // step - - backoff = DotProduct (in, normal) * overbounce; - - for (i=0 ; i<3 ; i++) - { - change = normal[i]*backoff; - out[i] = in[i] - change; - if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON) - out[i] = 0; - } - - return blocked; -} - - -/* -============ -PM_FlyMove - -The basic solid body movement clip that slides along multiple planes -============ -*/ -#define MAX_CLIP_PLANES 5 - -int PM_FlyMove (void) -{ - int bumpcount, numbumps; - vec3_t dir; - float d; - int numplanes; - vec3_t planes[MAX_CLIP_PLANES]; - vec3_t primal_velocity, original_velocity; - int i, j; - pmtrace_t trace; - vec3_t end; - float time_left; - int blocked; - - numbumps = 4; - - blocked = 0; - VectorCopy (pmove.velocity, original_velocity); - VectorCopy (pmove.velocity, primal_velocity); - numplanes = 0; - - time_left = frametime; - - for (bumpcount=0 ; bumpcount 0) - { // actually covered some distance - VectorCopy (trace.endpos, pmove.origin); - numplanes = 0; - } - - if (trace.fraction == 1) - break; // moved the entire distance - - // save entity for contact - pmove.touchindex[pmove.numtouch] = trace.ent; - pmove.numtouch++; - - if (trace.plane.normal[2] > 0.7) - { - blocked |= 1; // floor - } - if (!trace.plane.normal[2]) - { - blocked |= 2; // step - } - - time_left -= time_left * trace.fraction; - - // cliped to another plane - if (numplanes >= MAX_CLIP_PLANES) - { // this shouldn't really happen - VectorCopy (vec3_origin, pmove.velocity); - break; - } - - VectorCopy (trace.plane.normal, planes[numplanes]); - numplanes++; - -// -// modify original_velocity so it parallels all of the clip planes -// - for (i=0 ; i= updist) - { -usedown: - VectorCopy (down, pmove.origin); - VectorCopy (downvel, pmove.velocity); - } else // copy z value from slide move - pmove.velocity[2] = downvel[2]; - -// if at a dead stop, retry the move with nudges to get around lips - -} - - - -/* -================== -PM_Friction - -Handles both ground friction and water friction -================== -*/ -void PM_Friction (void) -{ - float *vel; - float speed, newspeed, control; - float friction; - float drop; - vec3_t start, stop; - pmtrace_t trace; - - if (pmove.waterjumptime) - return; - - vel = pmove.velocity; - - speed = sqrt(vel[0]*vel[0] +vel[1]*vel[1] + vel[2]*vel[2]); - if (speed < 1) - { - vel[0] = 0; - vel[1] = 0; - return; - } - - friction = movevars.friction; - -// if the leading edge is over a dropoff, increase friction - if (onground != -1) { - start[0] = stop[0] = pmove.origin[0] + vel[0]/speed*16; - start[1] = stop[1] = pmove.origin[1] + vel[1]/speed*16; - start[2] = pmove.origin[2] + player_mins[2]; - stop[2] = start[2] - 34; - - trace = PM_PlayerMove (start, stop); - - if (trace.fraction == 1) { - friction *= 2; - } - } - - drop = 0; - - if (waterlevel >= 2) // apply water friction - drop += speed*movevars.waterfriction*waterlevel*frametime; - else if (onground != -1) // apply ground friction - { - control = speed < movevars.stopspeed ? movevars.stopspeed : speed; - drop += control*friction*frametime; - } - - -// scale the velocity - newspeed = speed - drop; - if (newspeed < 0) - newspeed = 0; - newspeed /= speed; - - vel[0] = vel[0] * newspeed; - vel[1] = vel[1] * newspeed; - vel[2] = vel[2] * newspeed; -} - - -/* -============== -PM_Accelerate -============== -*/ -void PM_Accelerate (vec3_t wishdir, float wishspeed, float accel) -{ - int i; - float addspeed, accelspeed, currentspeed; - - if (pmove.dead) - return; - if (pmove.waterjumptime) - return; - - currentspeed = DotProduct (pmove.velocity, wishdir); - addspeed = wishspeed - currentspeed; - if (addspeed <= 0) - return; - accelspeed = accel*frametime*wishspeed; - if (accelspeed > addspeed) - accelspeed = addspeed; - - for (i=0 ; i<3 ; i++) - pmove.velocity[i] += accelspeed*wishdir[i]; -} - -void PM_AirAccelerate (vec3_t wishdir, float wishspeed, float accel) -{ - int i; - float addspeed, accelspeed, currentspeed, wishspd = wishspeed; - - if (pmove.dead) - return; - if (pmove.waterjumptime) - return; - - if (wishspd > 30) - wishspd = 30; - currentspeed = DotProduct (pmove.velocity, wishdir); - addspeed = wishspd - currentspeed; - if (addspeed <= 0) - return; - accelspeed = accel * wishspeed * frametime; - if (accelspeed > addspeed) - accelspeed = addspeed; - - for (i=0 ; i<3 ; i++) - pmove.velocity[i] += accelspeed*wishdir[i]; -} - - - -/* -=================== -PM_WaterMove - -=================== -*/ -void PM_WaterMove (void) -{ - int i; - vec3_t wishvel; - float wishspeed; - vec3_t wishdir; - vec3_t start, dest; - pmtrace_t trace; - -// -// user intentions -// - for (i=0 ; i<3 ; i++) - wishvel[i] = forward[i]*pmove.cmd.forwardmove + right[i]*pmove.cmd.sidemove; - - if (!pmove.cmd.forwardmove && !pmove.cmd.sidemove && !pmove.cmd.upmove) - wishvel[2] -= 60; // drift towards bottom - else - wishvel[2] += pmove.cmd.upmove; - - VectorCopy (wishvel, wishdir); - wishspeed = VectorNormalize(wishdir); - - if (wishspeed > movevars.maxspeed) - { - VectorScale (wishvel, movevars.maxspeed/wishspeed, wishvel); - wishspeed = movevars.maxspeed; - } - wishspeed *= 0.7; - -// -// water acceleration -// -// if (pmove.waterjumptime) -// Con_Printf ("wm->%f, %f, %f\n", pmove.velocity[0], pmove.velocity[1], pmove.velocity[2]); - PM_Accelerate (wishdir, wishspeed, movevars.wateraccelerate); - -// assume it is a stair or a slope, so press down from stepheight above - VectorMA (pmove.origin, frametime, pmove.velocity, dest); - VectorCopy (dest, start); - start[2] += STEPSIZE + 1; - trace = PM_PlayerMove (start, dest); - if (!trace.startsolid && !trace.allsolid) // FIXME: check steep slope? - { // walked up the step - VectorCopy (trace.endpos, pmove.origin); - return; - } - - PM_FlyMove (); -// if (pmove.waterjumptime) -// Con_Printf ("<-wm%f, %f, %f\n", pmove.velocity[0], pmove.velocity[1], pmove.velocity[2]); -} - - -/* -=================== -PM_AirMove - -=================== -*/ -void PM_AirMove (void) -{ - int i; - vec3_t wishvel; - float fmove, smove; - vec3_t wishdir; - float wishspeed; - vec3_t original; - - fmove = pmove.cmd.forwardmove; - smove = pmove.cmd.sidemove; - - forward[2] = 0; - right[2] = 0; - VectorNormalize (forward); - VectorNormalize (right); - - for (i=0 ; i<2 ; i++) - wishvel[i] = forward[i]*fmove + right[i]*smove; - wishvel[2] = 0; - - VectorCopy (wishvel, wishdir); - wishspeed = VectorNormalize(wishdir); - -// -// clamp to server defined max speed -// - if (wishspeed > movevars.maxspeed) - { - VectorScale (wishvel, movevars.maxspeed/wishspeed, wishvel); - wishspeed = movevars.maxspeed; - } - -// if (pmove.waterjumptime) -// Con_Printf ("am->%f, %f, %f\n", pmove.velocity[0], pmove.velocity[1], pmove.velocity[2]); - - if ( onground != -1) - { - if (pmove.velocity[2] > 0 || !pm_slidefix.value) - pmove.velocity[2] = 0; - PM_Accelerate (wishdir, wishspeed, movevars.accelerate); - pmove.velocity[2] -= movevars.entgravity * movevars.gravity * frametime; - PM_GroundMove (); - } - else - { // not on ground, so little effect on velocity -#if 0 - PM_AirAccelerate (wishdir, wishspeed, movevars.airaccelerate); -#else - PM_AirAccelerate (wishdir, wishspeed, movevars.accelerate); -#endif - - // add gravity - pmove.velocity[2] -= movevars.entgravity * movevars.gravity * frametime; - - i = PM_FlyMove(); - if (!i && pm_jumpfix.value) - { - // the move didn't get blocked - PM_CategorizePosition (); - if (onground != -1) // but we're on ground now - { - // This is a hack to fix the jumping bug - VectorCopy (pmove.origin, original); - // Calculate correct velocity - if ( ! PM_FlyMove() ) - { - // This shouldn't probably happen (?) - if (pmove.velocity[2] < 0) - pmove.velocity[2] = 0; - } - VectorCopy (original, pmove.origin); - } - } - } - -//Con_Printf("airmove:vec: %4.2f %4.2f %4.2f\n", -// pmove.velocity[0], -// pmove.velocity[1], -// pmove.velocity[2]); -// - -// if (pmove.waterjumptime) -// Con_Printf ("<-am%f, %f, %f\n", pmove.velocity[0], pmove.velocity[1], pmove.velocity[2]); -} - - - -/* -============= -PM_CategorizePosition -============= -*/ -void PM_CategorizePosition (void) -{ - vec3_t point; - int cont; - pmtrace_t tr; - -// if the player hull point one unit down is solid, the player -// is on ground - -// see if standing on something solid - point[0] = pmove.origin[0]; - point[1] = pmove.origin[1]; - point[2] = pmove.origin[2] - 1; - if (pmove.velocity[2] > 180) - { - onground = -1; - } - else - { - tr = PM_PlayerMove (pmove.origin, point); - if ( tr.plane.normal[2] < 0.7) - onground = -1; // too steep - else - onground = tr.ent; - if (onground != -1) - { - pmove.waterjumptime = 0; - if (!tr.startsolid && !tr.allsolid) - VectorCopy (tr.endpos, pmove.origin); - } - - // standing on an entity other than the world - if (tr.ent > 0) - { - pmove.touchindex[pmove.numtouch] = tr.ent; - pmove.numtouch++; - } - } - -// -// get waterlevel -// - waterlevel = 0; - watertype = CONTENTS_EMPTY; - - point[2] = pmove.origin[2] + player_mins[2] + 1; - cont = PM_PointContents (point); - - if (cont <= CONTENTS_WATER) - { - watertype = cont; - waterlevel = 1; - point[2] = pmove.origin[2] + (player_mins[2] + player_maxs[2])*0.5; - cont = PM_PointContents (point); - if (cont <= CONTENTS_WATER) - { - waterlevel = 2; - point[2] = pmove.origin[2] + 22; - cont = PM_PointContents (point); - if (cont <= CONTENTS_WATER) - waterlevel = 3; - } - } -} - - -/* -============= -JumpButton -============= -*/ -void JumpButton (void) -{ - if (pmove.dead) - { - pmove.oldbuttons |= BUTTON_JUMP; // don't jump again until released - return; - } - - if (pmove.waterjumptime) - { - pmove.waterjumptime -= frametime; - if (pmove.waterjumptime < 0) - pmove.waterjumptime = 0; - return; - } - - if (waterlevel >= 2) - { // swimming, not jumping - onground = -1; - - if (watertype == CONTENTS_WATER) - pmove.velocity[2] = 100; - else if (watertype == CONTENTS_SLIME) - pmove.velocity[2] = 80; - else - pmove.velocity[2] = 50; - return; - } - - if (onground == -1) - return; // in air, so no effect - -#ifdef SERVERONLY - if (pmove.oldbuttons & BUTTON_JUMP) - return; // don't pogo stick -#else - if (pmove.oldbuttons & BUTTON_JUMP && !pmove.jump_msec) - return; // don't pogo stick -#endif - -// When connected to a Kombat Teams server, "fix" the jumping bug -// the same way qc code does to minimize prediction errors - if (pm_ktphysics.value) - if (pmove.velocity[2] < 0) - pmove.velocity[2] = 0; - - onground = -1; - pmove.velocity[2] += 270; - - pmove.oldbuttons |= BUTTON_JUMP; // don't jump again until released - -#ifndef SERVERONLY - pmove.jump_msec = pmove.cmd.msec; -#endif -} - -/* -============= -CheckWaterJump -============= -*/ -void CheckWaterJump (void) -{ - vec3_t spot; - int cont; - vec3_t flatforward; - - if (pmove.waterjumptime) - return; - - // ZOID, don't hop out if we just jumped in - if (pmove.velocity[2] < -180) - return; // only hop out if we are moving up - - // see if near an edge - flatforward[0] = forward[0]; - flatforward[1] = forward[1]; - flatforward[2] = 0; - VectorNormalize (flatforward); - - VectorMA (pmove.origin, 24, flatforward, spot); - spot[2] += 8; - cont = PM_PointContents (spot); - if (cont != CONTENTS_SOLID) - return; - spot[2] += 24; - cont = PM_PointContents (spot); - if (cont != CONTENTS_EMPTY) - return; - // jump out of water - VectorScale (flatforward, 50, pmove.velocity); - pmove.velocity[2] = 310; - pmove.waterjumptime = 2; // safety net - pmove.oldbuttons |= BUTTON_JUMP; // don't jump again until released -} - -/* -================= -NudgePosition - -If pmove.origin is in a solid position, -try nudging slightly on all axis to -allow for the cut precision of the net coordinates -================= -*/ -void NudgePosition (void) -{ - vec3_t base; - int x, y, z; - int i; - static int sign[3] = {0, -1, 1}; - - VectorCopy (pmove.origin, base); - - for (i=0 ; i<3 ; i++) - pmove.origin[i] = ((int)(pmove.origin[i]*8)) * 0.125; -// pmove.origin[2] += 0.124; - -// if (pmove.dead) -// return; // might be a squished point, so don'y bother -// if (PM_TestPlayerPosition (pmove.origin) ) -// return; - - for (z=0 ; z<=2 ; z++) - { - for (x=0 ; x<=2 ; x++) - { - for (y=0 ; y<=2 ; y++) - { - pmove.origin[0] = base[0] + (sign[x] * 1.0/8); - pmove.origin[1] = base[1] + (sign[y] * 1.0/8); - pmove.origin[2] = base[2] + (sign[z] * 1.0/8); - if (PM_TestPlayerPosition (pmove.origin)) - return; - } - } - } - VectorCopy (base, pmove.origin); -// Con_DPrintf ("NudgePosition: stuck\n"); -} - -/* -=============== -SpectatorMove -=============== -*/ -void SpectatorMove (void) -{ - float speed, drop, friction, control, newspeed; - float currentspeed, addspeed, accelspeed; - int i; - vec3_t wishvel; - float fmove, smove; - vec3_t wishdir; - float wishspeed; -#ifndef SERVERONLY - extern float server_version; // version of server we connected to -#endif - - // friction - - speed = Length (pmove.velocity); - if (speed < 1) - { - VectorCopy (vec3_origin, pmove.velocity) - } - else - { - drop = 0; - - friction = movevars.friction*1.5; // extra friction - control = speed < movevars.stopspeed ? movevars.stopspeed : speed; - drop += control*friction*frametime; - - // scale the velocity - newspeed = speed - drop; - if (newspeed < 0) - newspeed = 0; - newspeed /= speed; - - VectorScale (pmove.velocity, newspeed, pmove.velocity); - } - - // accelerate - fmove = pmove.cmd.forwardmove; - smove = pmove.cmd.sidemove; - - VectorNormalize (forward); - VectorNormalize (right); - - for (i=0 ; i<3 ; i++) - wishvel[i] = forward[i]*fmove + right[i]*smove; - wishvel[2] += pmove.cmd.upmove; - - VectorCopy (wishvel, wishdir); - wishspeed = VectorNormalize(wishdir); - - // - // clamp to server defined max speed - // - if (wishspeed > movevars.spectatormaxspeed) - { - VectorScale (wishvel, movevars.spectatormaxspeed/wishspeed, wishvel); - wishspeed = movevars.spectatormaxspeed; - } - - currentspeed = DotProduct(pmove.velocity, wishdir); - addspeed = wishspeed - currentspeed; - if (addspeed <= 0) - return; - accelspeed = movevars.accelerate*frametime*wishspeed; - if (accelspeed > addspeed) - accelspeed = addspeed; - - for (i=0 ; i<3 ; i++) - pmove.velocity[i] += accelspeed*wishdir[i]; - - - // move - VectorMA (pmove.origin, frametime, pmove.velocity, pmove.origin); -} - -/* -============= -PlayerMove - -Returns with origin, angles, and velocity modified in place. - -Numtouch and touchindex[] will be set if any of the physents -were contacted during the move. -============= -*/ -void PlayerMove (void) -{ - frametime = pmove.cmd.msec * 0.001; - pmove.numtouch = 0; - - AngleVectors (pmove.angles, forward, right, up); - - if (pmove.spectator) - { - SpectatorMove (); - return; - } - - NudgePosition (); - - // take angles directly from command - VectorCopy (pmove.cmd.angles, pmove.angles); - - // set onground, watertype, and waterlevel - PM_CategorizePosition (); - - if (waterlevel == 2) - CheckWaterJump (); - - if (pmove.velocity[2] < 0) - pmove.waterjumptime = 0; - -#ifndef SERVERONLY - if (pmove.jump_msec) { - pmove.jump_msec += pmove.cmd.msec; - if (pmove.jump_msec > pm_jumpfixtime.value*1000) - pmove.jump_msec = 0; - } -#endif - - if (pmove.cmd.buttons & BUTTON_JUMP) - JumpButton (); - else - pmove.oldbuttons &= ~BUTTON_JUMP; - - PM_Friction (); - - if (waterlevel >= 2) - PM_WaterMove (); - else - PM_AirMove (); - - // set onground, watertype, and waterlevel for final spot - PM_CategorizePosition (); -} - +/* +Copyright (C) 1996-1997 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 "quakedef.h" +#include "pmove.h" + +#ifndef SERVERONLY +cvar_t pm_jumpfixtime = {"pm_jumpfixtime","0.05"}; +#endif +cvar_t pm_jumpfix = {"pm_jumpfix","1"}; +cvar_t pm_slidefix = {"pm_slidefix","0"}; // FIXME: remove? +cvar_t pm_ktphysics = {"pm_ktphysics", "0"}; // set this when + // playing on a server running Kombat Teams 2.10 or later + +movevars_t movevars; + +playermove_t pmove; + +int onground; +int waterlevel; +int watertype; + +float frametime; + +vec3_t forward, right, up; + +vec3_t player_mins = {-16, -16, -24}; +vec3_t player_maxs = {16, 16, 32}; + +// #define PM_GRAVITY 800 +// #define PM_STOPSPEED 100 +// #define PM_MAXSPEED 320 +// #define PM_SPECTATORMAXSPEED 500 +// #define PM_ACCELERATE 10 +// #define PM_AIRACCELERATE 0.7 +// #define PM_WATERACCELERATE 10 +// #define PM_FRICTION 6 +// #define PM_WATERFRICTION 1 + +void PM_InitBoxHull (void); + +void Pmove_Init (void) +{ +#ifndef SERVERONLY + Cvar_RegisterVariable (&pm_jumpfixtime); +#endif + +#if defined(SERVERONLY) + Cvar_RegisterVariable (&pm_jumpfix); +#else + pm_jumpfix.value = 1; +#endif + + Cvar_RegisterVariable (&pm_slidefix); + Cvar_RegisterVariable (&pm_ktphysics); + PM_InitBoxHull (); +} + +#define STEPSIZE 18 + + +#define BUTTON_JUMP 2 + + +/* +================== +PM_ClipVelocity + +Slide off of the impacting object +returns the blocked flags (1 = floor, 2 = step / wall) +================== +*/ +#define STOP_EPSILON 0.1 + +int PM_ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce) +{ + float backoff; + float change; + int i, blocked; + + blocked = 0; + if (normal[2] > 0) + blocked |= 1; // floor + if (!normal[2]) + blocked |= 2; // step + + backoff = DotProduct (in, normal) * overbounce; + + for (i=0 ; i<3 ; i++) + { + change = normal[i]*backoff; + out[i] = in[i] - change; + if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON) + out[i] = 0; + } + + return blocked; +} + + +/* +============ +PM_FlyMove + +The basic solid body movement clip that slides along multiple planes +============ +*/ +#define MAX_CLIP_PLANES 5 + +int PM_FlyMove (void) +{ + int bumpcount, numbumps; + vec3_t dir; + float d; + int numplanes; + vec3_t planes[MAX_CLIP_PLANES]; + vec3_t primal_velocity, original_velocity; + int i, j; + pmtrace_t trace; + vec3_t end; + float time_left; + int blocked; + + numbumps = 4; + + blocked = 0; + VectorCopy (pmove.velocity, original_velocity); + VectorCopy (pmove.velocity, primal_velocity); + numplanes = 0; + + time_left = frametime; + + for (bumpcount=0 ; bumpcount 0) + { // actually covered some distance + VectorCopy (trace.endpos, pmove.origin); + numplanes = 0; + } + + if (trace.fraction == 1) + break; // moved the entire distance + + // save entity for contact + pmove.touchindex[pmove.numtouch] = trace.ent; + pmove.numtouch++; + + if (trace.plane.normal[2] > 0.7) + { + blocked |= 1; // floor + } + if (!trace.plane.normal[2]) + { + blocked |= 2; // step + } + + time_left -= time_left * trace.fraction; + + // cliped to another plane + if (numplanes >= MAX_CLIP_PLANES) + { // this shouldn't really happen + VectorCopy (vec3_origin, pmove.velocity); + break; + } + + VectorCopy (trace.plane.normal, planes[numplanes]); + numplanes++; + +// +// modify original_velocity so it parallels all of the clip planes +// + for (i=0 ; i= updist) + { +usedown: + VectorCopy (down, pmove.origin); + VectorCopy (downvel, pmove.velocity); + } else // copy z value from slide move + pmove.velocity[2] = downvel[2]; + +// if at a dead stop, retry the move with nudges to get around lips + +} + + + +/* +================== +PM_Friction + +Handles both ground friction and water friction +================== +*/ +void PM_Friction (void) +{ + float *vel; + float speed, newspeed, control; + float friction; + float drop; + vec3_t start, stop; + pmtrace_t trace; + + if (pmove.waterjumptime) + return; + + vel = pmove.velocity; + + speed = sqrt(vel[0]*vel[0] +vel[1]*vel[1] + vel[2]*vel[2]); + if (speed < 1) + { + vel[0] = 0; + vel[1] = 0; + return; + } + + friction = movevars.friction; + +// if the leading edge is over a dropoff, increase friction + if (onground != -1) { + start[0] = stop[0] = pmove.origin[0] + vel[0]/speed*16; + start[1] = stop[1] = pmove.origin[1] + vel[1]/speed*16; + start[2] = pmove.origin[2] + player_mins[2]; + stop[2] = start[2] - 34; + + trace = PM_PlayerMove (start, stop); + + if (trace.fraction == 1) { + friction *= 2; + } + } + + drop = 0; + + if (waterlevel >= 2) // apply water friction + drop += speed*movevars.waterfriction*waterlevel*frametime; + else if (onground != -1) // apply ground friction + { + control = speed < movevars.stopspeed ? movevars.stopspeed : speed; + drop += control*friction*frametime; + } + + +// scale the velocity + newspeed = speed - drop; + if (newspeed < 0) + newspeed = 0; + newspeed /= speed; + + vel[0] = vel[0] * newspeed; + vel[1] = vel[1] * newspeed; + vel[2] = vel[2] * newspeed; +} + + +/* +============== +PM_Accelerate +============== +*/ +void PM_Accelerate (vec3_t wishdir, float wishspeed, float accel) +{ + int i; + float addspeed, accelspeed, currentspeed; + + if (pmove.dead) + return; + if (pmove.waterjumptime) + return; + + currentspeed = DotProduct (pmove.velocity, wishdir); + addspeed = wishspeed - currentspeed; + if (addspeed <= 0) + return; + accelspeed = accel*frametime*wishspeed; + if (accelspeed > addspeed) + accelspeed = addspeed; + + for (i=0 ; i<3 ; i++) + pmove.velocity[i] += accelspeed*wishdir[i]; +} + +void PM_AirAccelerate (vec3_t wishdir, float wishspeed, float accel) +{ + int i; + float addspeed, accelspeed, currentspeed, wishspd = wishspeed; + + if (pmove.dead) + return; + if (pmove.waterjumptime) + return; + + if (wishspd > 30) + wishspd = 30; + currentspeed = DotProduct (pmove.velocity, wishdir); + addspeed = wishspd - currentspeed; + if (addspeed <= 0) + return; + accelspeed = accel * wishspeed * frametime; + if (accelspeed > addspeed) + accelspeed = addspeed; + + for (i=0 ; i<3 ; i++) + pmove.velocity[i] += accelspeed*wishdir[i]; +} + + + +/* +=================== +PM_WaterMove + +=================== +*/ +void PM_WaterMove (void) +{ + int i; + vec3_t wishvel; + float wishspeed; + vec3_t wishdir; + vec3_t start, dest; + pmtrace_t trace; + +// +// user intentions +// + for (i=0 ; i<3 ; i++) + wishvel[i] = forward[i]*pmove.cmd.forwardmove + right[i]*pmove.cmd.sidemove; + + if (!pmove.cmd.forwardmove && !pmove.cmd.sidemove && !pmove.cmd.upmove) + wishvel[2] -= 60; // drift towards bottom + else + wishvel[2] += pmove.cmd.upmove; + + VectorCopy (wishvel, wishdir); + wishspeed = VectorNormalize(wishdir); + + if (wishspeed > movevars.maxspeed) + { + VectorScale (wishvel, movevars.maxspeed/wishspeed, wishvel); + wishspeed = movevars.maxspeed; + } + wishspeed *= 0.7; + +// +// water acceleration +// +// if (pmove.waterjumptime) +// Con_Printf ("wm->%f, %f, %f\n", pmove.velocity[0], pmove.velocity[1], pmove.velocity[2]); + PM_Accelerate (wishdir, wishspeed, movevars.wateraccelerate); + +// assume it is a stair or a slope, so press down from stepheight above + VectorMA (pmove.origin, frametime, pmove.velocity, dest); + VectorCopy (dest, start); + start[2] += STEPSIZE + 1; + trace = PM_PlayerMove (start, dest); + if (!trace.startsolid && !trace.allsolid) // FIXME: check steep slope? + { // walked up the step + VectorCopy (trace.endpos, pmove.origin); + return; + } + + PM_FlyMove (); +// if (pmove.waterjumptime) +// Con_Printf ("<-wm%f, %f, %f\n", pmove.velocity[0], pmove.velocity[1], pmove.velocity[2]); +} + + +/* +=================== +PM_AirMove + +=================== +*/ +void PM_AirMove (void) +{ + int i; + vec3_t wishvel; + float fmove, smove; + vec3_t wishdir; + float wishspeed; + vec3_t original; + + fmove = pmove.cmd.forwardmove; + smove = pmove.cmd.sidemove; + + forward[2] = 0; + right[2] = 0; + VectorNormalize (forward); + VectorNormalize (right); + + for (i=0 ; i<2 ; i++) + wishvel[i] = forward[i]*fmove + right[i]*smove; + wishvel[2] = 0; + + VectorCopy (wishvel, wishdir); + wishspeed = VectorNormalize(wishdir); + +// +// clamp to server defined max speed +// + if (wishspeed > movevars.maxspeed) + { + VectorScale (wishvel, movevars.maxspeed/wishspeed, wishvel); + wishspeed = movevars.maxspeed; + } + +// if (pmove.waterjumptime) +// Con_Printf ("am->%f, %f, %f\n", pmove.velocity[0], pmove.velocity[1], pmove.velocity[2]); + + if ( onground != -1) + { + if (pmove.velocity[2] > 0 || !pm_slidefix.value) + pmove.velocity[2] = 0; + PM_Accelerate (wishdir, wishspeed, movevars.accelerate); + pmove.velocity[2] -= movevars.entgravity * movevars.gravity * frametime; + PM_GroundMove (); + } + else + { // not on ground, so little effect on velocity +#if 0 + PM_AirAccelerate (wishdir, wishspeed, movevars.airaccelerate); +#else + PM_AirAccelerate (wishdir, wishspeed, movevars.accelerate); +#endif + + // add gravity + pmove.velocity[2] -= movevars.entgravity * movevars.gravity * frametime; + + i = PM_FlyMove(); + if (!i && pm_jumpfix.value) + { + // the move didn't get blocked + PM_CategorizePosition (); + if (onground != -1) // but we're on ground now + { + // This is a hack to fix the jumping bug + VectorCopy (pmove.origin, original); + // Calculate correct velocity + if ( ! PM_FlyMove() ) + { + // This shouldn't probably happen (?) + if (pmove.velocity[2] < 0) + pmove.velocity[2] = 0; + } + VectorCopy (original, pmove.origin); + } + } + } + +//Con_Printf("airmove:vec: %4.2f %4.2f %4.2f\n", +// pmove.velocity[0], +// pmove.velocity[1], +// pmove.velocity[2]); +// + +// if (pmove.waterjumptime) +// Con_Printf ("<-am%f, %f, %f\n", pmove.velocity[0], pmove.velocity[1], pmove.velocity[2]); +} + + + +/* +============= +PM_CategorizePosition +============= +*/ +void PM_CategorizePosition (void) +{ + vec3_t point; + int cont; + pmtrace_t tr; + +// if the player hull point one unit down is solid, the player +// is on ground + +// see if standing on something solid + point[0] = pmove.origin[0]; + point[1] = pmove.origin[1]; + point[2] = pmove.origin[2] - 1; + if (pmove.velocity[2] > 180) + { + onground = -1; + } + else + { + tr = PM_PlayerMove (pmove.origin, point); + if ( tr.plane.normal[2] < 0.7) + onground = -1; // too steep + else + onground = tr.ent; + if (onground != -1) + { + pmove.waterjumptime = 0; + if (!tr.startsolid && !tr.allsolid) + VectorCopy (tr.endpos, pmove.origin); + } + + // standing on an entity other than the world + if (tr.ent > 0) + { + pmove.touchindex[pmove.numtouch] = tr.ent; + pmove.numtouch++; + } + } + +// +// get waterlevel +// + waterlevel = 0; + watertype = CONTENTS_EMPTY; + + point[2] = pmove.origin[2] + player_mins[2] + 1; + cont = PM_PointContents (point); + + if (cont <= CONTENTS_WATER) + { + watertype = cont; + waterlevel = 1; + point[2] = pmove.origin[2] + (player_mins[2] + player_maxs[2])*0.5; + cont = PM_PointContents (point); + if (cont <= CONTENTS_WATER) + { + waterlevel = 2; + point[2] = pmove.origin[2] + 22; + cont = PM_PointContents (point); + if (cont <= CONTENTS_WATER) + waterlevel = 3; + } + } +} + + +/* +============= +JumpButton +============= +*/ +void JumpButton (void) +{ + if (pmove.dead) + { + pmove.oldbuttons |= BUTTON_JUMP; // don't jump again until released + return; + } + + if (pmove.waterjumptime) + { + pmove.waterjumptime -= frametime; + if (pmove.waterjumptime < 0) + pmove.waterjumptime = 0; + return; + } + + if (waterlevel >= 2) + { // swimming, not jumping + onground = -1; + + if (watertype == CONTENTS_WATER) + pmove.velocity[2] = 100; + else if (watertype == CONTENTS_SLIME) + pmove.velocity[2] = 80; + else + pmove.velocity[2] = 50; + return; + } + + if (onground == -1) + return; // in air, so no effect + +#ifdef SERVERONLY + if (pmove.oldbuttons & BUTTON_JUMP) + return; // don't pogo stick +#else + if (pmove.oldbuttons & BUTTON_JUMP && !pmove.jump_msec) + return; // don't pogo stick +#endif + +// When connected to a Kombat Teams server, "fix" the jumping bug +// the same way qc code does to minimize prediction errors + if (pm_ktphysics.value) + if (pmove.velocity[2] < 0) + pmove.velocity[2] = 0; + + onground = -1; + pmove.velocity[2] += 270; + + pmove.oldbuttons |= BUTTON_JUMP; // don't jump again until released + +#ifndef SERVERONLY + pmove.jump_msec = pmove.cmd.msec; +#endif +} + +/* +============= +CheckWaterJump +============= +*/ +void CheckWaterJump (void) +{ + vec3_t spot; + int cont; + vec3_t flatforward; + + if (pmove.waterjumptime) + return; + + // ZOID, don't hop out if we just jumped in + if (pmove.velocity[2] < -180) + return; // only hop out if we are moving up + + // see if near an edge + flatforward[0] = forward[0]; + flatforward[1] = forward[1]; + flatforward[2] = 0; + VectorNormalize (flatforward); + + VectorMA (pmove.origin, 24, flatforward, spot); + spot[2] += 8; + cont = PM_PointContents (spot); + if (cont != CONTENTS_SOLID) + return; + spot[2] += 24; + cont = PM_PointContents (spot); + if (cont != CONTENTS_EMPTY) + return; + // jump out of water + VectorScale (flatforward, 50, pmove.velocity); + pmove.velocity[2] = 310; + pmove.waterjumptime = 2; // safety net + pmove.oldbuttons |= BUTTON_JUMP; // don't jump again until released +} + +/* +================= +NudgePosition + +If pmove.origin is in a solid position, +try nudging slightly on all axis to +allow for the cut precision of the net coordinates +================= +*/ +void NudgePosition (void) +{ + vec3_t base; + int x, y, z; + int i; + static int sign[3] = {0, -1, 1}; + + VectorCopy (pmove.origin, base); + + for (i=0 ; i<3 ; i++) + pmove.origin[i] = ((int)(pmove.origin[i]*8)) * 0.125; +// pmove.origin[2] += 0.124; + +// if (pmove.dead) +// return; // might be a squished point, so don'y bother +// if (PM_TestPlayerPosition (pmove.origin) ) +// return; + + for (z=0 ; z<=2 ; z++) + { + for (x=0 ; x<=2 ; x++) + { + for (y=0 ; y<=2 ; y++) + { + pmove.origin[0] = base[0] + (sign[x] * 1.0/8); + pmove.origin[1] = base[1] + (sign[y] * 1.0/8); + pmove.origin[2] = base[2] + (sign[z] * 1.0/8); + if (PM_TestPlayerPosition (pmove.origin)) + return; + } + } + } + VectorCopy (base, pmove.origin); +// Con_DPrintf ("NudgePosition: stuck\n"); +} + +/* +=============== +SpectatorMove +=============== +*/ +void SpectatorMove (void) +{ + float speed, drop, friction, control, newspeed; + float currentspeed, addspeed, accelspeed; + int i; + vec3_t wishvel; + float fmove, smove; + vec3_t wishdir; + float wishspeed; +#ifndef SERVERONLY + extern float server_version; // version of server we connected to +#endif + + // friction + + speed = Length (pmove.velocity); + if (speed < 1) + { + VectorCopy (vec3_origin, pmove.velocity) + } + else + { + drop = 0; + + friction = movevars.friction*1.5; // extra friction + control = speed < movevars.stopspeed ? movevars.stopspeed : speed; + drop += control*friction*frametime; + + // scale the velocity + newspeed = speed - drop; + if (newspeed < 0) + newspeed = 0; + newspeed /= speed; + + VectorScale (pmove.velocity, newspeed, pmove.velocity); + } + + // accelerate + fmove = pmove.cmd.forwardmove; + smove = pmove.cmd.sidemove; + + VectorNormalize (forward); + VectorNormalize (right); + + for (i=0 ; i<3 ; i++) + wishvel[i] = forward[i]*fmove + right[i]*smove; + wishvel[2] += pmove.cmd.upmove; + + VectorCopy (wishvel, wishdir); + wishspeed = VectorNormalize(wishdir); + + // + // clamp to server defined max speed + // + if (wishspeed > movevars.spectatormaxspeed) + { + VectorScale (wishvel, movevars.spectatormaxspeed/wishspeed, wishvel); + wishspeed = movevars.spectatormaxspeed; + } + + currentspeed = DotProduct(pmove.velocity, wishdir); + addspeed = wishspeed - currentspeed; + if (addspeed <= 0) + return; + accelspeed = movevars.accelerate*frametime*wishspeed; + if (accelspeed > addspeed) + accelspeed = addspeed; + + for (i=0 ; i<3 ; i++) + pmove.velocity[i] += accelspeed*wishdir[i]; + + + // move + VectorMA (pmove.origin, frametime, pmove.velocity, pmove.origin); +} + +/* +============= +PlayerMove + +Returns with origin, angles, and velocity modified in place. + +Numtouch and touchindex[] will be set if any of the physents +were contacted during the move. +============= +*/ +void PlayerMove (void) +{ + frametime = pmove.cmd.msec * 0.001; + pmove.numtouch = 0; + + AngleVectors (pmove.angles, forward, right, up); + + if (pmove.spectator) + { + SpectatorMove (); + return; + } + + NudgePosition (); + + // take angles directly from command + VectorCopy (pmove.cmd.angles, pmove.angles); + + // set onground, watertype, and waterlevel + PM_CategorizePosition (); + + if (waterlevel == 2) + CheckWaterJump (); + + if (pmove.velocity[2] < 0) + pmove.waterjumptime = 0; + +#ifndef SERVERONLY + if (pmove.jump_msec) { + pmove.jump_msec += pmove.cmd.msec; + if (pmove.jump_msec > pm_jumpfixtime.value*1000) + pmove.jump_msec = 0; + } +#endif + + if (pmove.cmd.buttons & BUTTON_JUMP) + JumpButton (); + else + pmove.oldbuttons &= ~BUTTON_JUMP; + + PM_Friction (); + + if (waterlevel >= 2) + PM_WaterMove (); + else + PM_AirMove (); + + // set onground, watertype, and waterlevel for final spot + PM_CategorizePosition (); +} + diff --git a/source/pmove.h b/source/pmove.h index c6afbae7..e66fffef 100644 --- a/source/pmove.h +++ b/source/pmove.h @@ -1,105 +1,105 @@ -/* -Copyright (C) 1996-1997 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. - -*/ - -typedef struct -{ - vec3_t normal; - float dist; -} pmplane_t; - -typedef struct -{ - qboolean allsolid; // if true, plane is not valid - qboolean startsolid; // if true, the initial point was in a solid area - qboolean inopen, inwater; - float fraction; // time completed, 1.0 = didn't hit anything - vec3_t endpos; // final position - pmplane_t plane; // surface normal at impact - int ent; // entity the surface is on -} pmtrace_t; - - -#define MAX_PHYSENTS 64 //32 -typedef struct -{ - vec3_t origin; - model_t *model; // only for bsp models - vec3_t mins, maxs; // only for non-bsp models - int info; // for client or server to identify -} physent_t; - - -typedef struct -{ - int sequence; // just for debugging prints - - // player state - vec3_t origin; - vec3_t angles; - vec3_t velocity; - int oldbuttons; -#ifndef SERVERONLY - int jump_msec; // msec since last jump -#endif - float waterjumptime; - qboolean dead; - int spectator; - - // world state - int numphysent; - physent_t physents[MAX_PHYSENTS]; // 0 should be the world - - // input - usercmd_t cmd; - - // results - int numtouch; - int touchindex[MAX_PHYSENTS]; -} playermove_t; - -typedef struct { - float gravity; - float stopspeed; - float maxspeed; - float spectatormaxspeed; - float accelerate; - float airaccelerate; - float wateraccelerate; - float friction; - float waterfriction; - float entgravity; -} movevars_t; - - -extern movevars_t movevars; -extern playermove_t pmove; -extern int onground; -extern int waterlevel; -extern int watertype; - -void PlayerMove (void); -void Pmove_Init (void); - -int PM_HullPointContents (hull_t *hull, int num, vec3_t p); -int PM_PointContents (vec3_t point); -void PM_CategorizePosition (void); -qboolean PM_TestPlayerPosition (vec3_t point); -pmtrace_t PM_PlayerMove (vec3_t start, vec3_t end); -pmtrace_t PM_TraceLine (vec3_t start, vec3_t end); +/* +Copyright (C) 1996-1997 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. + +*/ + +typedef struct +{ + vec3_t normal; + float dist; +} pmplane_t; + +typedef struct +{ + qboolean allsolid; // if true, plane is not valid + qboolean startsolid; // if true, the initial point was in a solid area + qboolean inopen, inwater; + float fraction; // time completed, 1.0 = didn't hit anything + vec3_t endpos; // final position + pmplane_t plane; // surface normal at impact + int ent; // entity the surface is on +} pmtrace_t; + + +#define MAX_PHYSENTS 64 //32 +typedef struct +{ + vec3_t origin; + model_t *model; // only for bsp models + vec3_t mins, maxs; // only for non-bsp models + int info; // for client or server to identify +} physent_t; + + +typedef struct +{ + int sequence; // just for debugging prints + + // player state + vec3_t origin; + vec3_t angles; + vec3_t velocity; + int oldbuttons; +#ifndef SERVERONLY + int jump_msec; // msec since last jump +#endif + float waterjumptime; + qboolean dead; + int spectator; + + // world state + int numphysent; + physent_t physents[MAX_PHYSENTS]; // 0 should be the world + + // input + usercmd_t cmd; + + // results + int numtouch; + int touchindex[MAX_PHYSENTS]; +} playermove_t; + +typedef struct { + float gravity; + float stopspeed; + float maxspeed; + float spectatormaxspeed; + float accelerate; + float airaccelerate; + float wateraccelerate; + float friction; + float waterfriction; + float entgravity; +} movevars_t; + + +extern movevars_t movevars; +extern playermove_t pmove; +extern int onground; +extern int waterlevel; +extern int watertype; + +void PlayerMove (void); +void Pmove_Init (void); + +int PM_HullPointContents (hull_t *hull, int num, vec3_t p); +int PM_PointContents (vec3_t point); +void PM_CategorizePosition (void); +qboolean PM_TestPlayerPosition (vec3_t point); +pmtrace_t PM_PlayerMove (vec3_t start, vec3_t end); +pmtrace_t PM_TraceLine (vec3_t start, vec3_t end); diff --git a/source/pmovetst.c b/source/pmovetst.c index 47eeb81d..c2e2cfce 100644 --- a/source/pmovetst.c +++ b/source/pmovetst.c @@ -1,482 +1,482 @@ -/* -Copyright (C) 1996-1997 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 "quakedef.h" -#include "pmove.h" - -static hull_t box_hull; -static dclipnode_t box_clipnodes[6]; -static mplane_t box_planes[6]; - -extern vec3_t player_mins; -extern vec3_t player_maxs; - -/* -=================== -PM_InitBoxHull - -Set up the planes and clipnodes so that the six floats of a bounding box -can just be stored out and get a proper hull_t structure. -=================== -*/ -void PM_InitBoxHull (void) -{ - int i; - int side; - - box_hull.clipnodes = box_clipnodes; - box_hull.planes = box_planes; - box_hull.firstclipnode = 0; - box_hull.lastclipnode = 5; - - for (i=0 ; i<6 ; i++) - { - box_clipnodes[i].planenum = i; - - side = i&1; - - box_clipnodes[i].children[side] = CONTENTS_EMPTY; - if (i != 5) - box_clipnodes[i].children[side^1] = i + 1; - else - box_clipnodes[i].children[side^1] = CONTENTS_SOLID; - - box_planes[i].type = i>>1; - box_planes[i].normal[i>>1] = 1; - } - -} - - -/* -=================== -PM_HullForBox - -To keep everything totally uniform, bounding boxes are turned into small -BSP trees instead of being compared directly. -=================== -*/ -hull_t *PM_HullForBox (vec3_t mins, vec3_t maxs) -{ - box_planes[0].dist = maxs[0]; - box_planes[1].dist = mins[0]; - box_planes[2].dist = maxs[1]; - box_planes[3].dist = mins[1]; - box_planes[4].dist = maxs[2]; - box_planes[5].dist = mins[2]; - - return &box_hull; -} - - -/* -================== -PM_HullPointContents - -================== -*/ -int PM_HullPointContents (hull_t *hull, int num, vec3_t p) -{ - float d; - dclipnode_t *node; - mplane_t *plane; - - while (num >= 0) - { - if (num < hull->firstclipnode || num > hull->lastclipnode) - Sys_Error ("PM_HullPointContents: bad node number"); - - node = hull->clipnodes + num; - plane = hull->planes + node->planenum; - - if (plane->type < 3) - d = p[plane->type] - plane->dist; - else - d = DotProduct (plane->normal, p) - plane->dist; - if (d < 0) - num = node->children[1]; - else - num = node->children[0]; - } - - return num; -} - -/* -================== -PM_PointContents - -================== -*/ -int PM_PointContents (vec3_t p) -{ - float d; - dclipnode_t *node; - mplane_t *plane; - hull_t *hull; - int num; - - hull = &pmove.physents[0].model->hulls[0]; - - num = hull->firstclipnode; - - while (num >= 0) - { - if (num < hull->firstclipnode || num > hull->lastclipnode) - Sys_Error ("PM_HullPointContents: bad node number"); - - node = hull->clipnodes + num; - plane = hull->planes + node->planenum; - - if (plane->type < 3) - d = p[plane->type] - plane->dist; - else - d = DotProduct (plane->normal, p) - plane->dist; - if (d < 0) - num = node->children[1]; - else - num = node->children[0]; - } - - return num; -} - -/* -=============================================================================== - -LINE TESTING IN HULLS - -=============================================================================== -*/ - -// 1/32 epsilon to keep floating point happy -#define DIST_EPSILON (0.03125) - -/* -================== -PM_RecursiveHullCheck - -================== -*/ -qboolean PM_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, pmtrace_t *trace) -{ - dclipnode_t *node; - mplane_t *plane; - float t1, t2; - float frac; - int i; - vec3_t mid; - int side; - float midf; - -// check for empty - if (num < 0) - { - if (num != CONTENTS_SOLID) - { - trace->allsolid = false; - if (num == CONTENTS_EMPTY) - trace->inopen = true; - else - trace->inwater = true; - } - else - trace->startsolid = true; - return true; // empty - } - - if (num < hull->firstclipnode || num > hull->lastclipnode) - Sys_Error ("PM_RecursiveHullCheck: bad node number"); - -// -// find the point distances -// - node = hull->clipnodes + num; - plane = hull->planes + node->planenum; - - if (plane->type < 3) - { - t1 = p1[plane->type] - plane->dist; - t2 = p2[plane->type] - plane->dist; - } - else - { - t1 = DotProduct (plane->normal, p1) - plane->dist; - t2 = DotProduct (plane->normal, p2) - plane->dist; - } - -#if 1 - if (t1 >= 0 && t2 >= 0) - return PM_RecursiveHullCheck (hull, node->children[0], p1f, p2f, p1, p2, trace); - if (t1 < 0 && t2 < 0) - return PM_RecursiveHullCheck (hull, node->children[1], p1f, p2f, p1, p2, trace); -#else - if ( (t1 >= DIST_EPSILON && t2 >= DIST_EPSILON) || (t2 > t1 && t1 >= 0) ) - return PM_RecursiveHullCheck (hull, node->children[0], p1f, p2f, p1, p2, trace); - if ( (t1 <= -DIST_EPSILON && t2 <= -DIST_EPSILON) || (t2 < t1 && t1 <= 0) ) - return PM_RecursiveHullCheck (hull, node->children[1], p1f, p2f, p1, p2, trace); -#endif - -// put the crosspoint DIST_EPSILON pixels on the near side - if (t1 < 0) - frac = (t1 + DIST_EPSILON)/(t1-t2); - else - frac = (t1 - DIST_EPSILON)/(t1-t2); - if (frac < 0) - frac = 0; - if (frac > 1) - frac = 1; - - midf = p1f + (p2f - p1f)*frac; - for (i=0 ; i<3 ; i++) - mid[i] = p1[i] + frac*(p2[i] - p1[i]); - - side = (t1 < 0); - -// move up to the node - if (!PM_RecursiveHullCheck (hull, node->children[side], p1f, midf, p1, mid, trace) ) - return false; - -#ifdef PARANOID - if (PM_HullPointContents (pm_hullmodel, mid, node->children[side]) - == CONTENTS_SOLID) - { - Con_Printf ("mid PointInHullSolid\n"); - return false; - } -#endif - - if (PM_HullPointContents (hull, node->children[side^1], mid) - != CONTENTS_SOLID) -// go past the node - return PM_RecursiveHullCheck (hull, node->children[side^1], midf, p2f, mid, p2, trace); - - if (trace->allsolid) - return false; // never got out of the solid area - -//================== -// the other side of the node is solid, this is the impact point -//================== - if (!side) - { - VectorCopy (plane->normal, trace->plane.normal); - trace->plane.dist = plane->dist; - } - else - { - VectorSubtract (vec3_origin, plane->normal, trace->plane.normal); - trace->plane.dist = -plane->dist; - } - - while (PM_HullPointContents (hull, hull->firstclipnode, mid) - == CONTENTS_SOLID) - { // shouldn't really happen, but does occasionally - frac -= 0.1; - if (frac < 0) - { - trace->fraction = midf; - VectorCopy (mid, trace->endpos); - Con_DPrintf ("backup past 0\n"); - return false; - } - midf = p1f + (p2f - p1f)*frac; - for (i=0 ; i<3 ; i++) - mid[i] = p1[i] + frac*(p2[i] - p1[i]); - } - - trace->fraction = midf; - VectorCopy (mid, trace->endpos); - - return false; -} - - -/* -================ -PM_TestPlayerPosition - -Returns false if the given player position is not valid (in solid) -================ -*/ -qboolean PM_TestPlayerPosition (vec3_t pos) -{ - int i; - physent_t *pe; - vec3_t mins, maxs, test; - hull_t *hull; - - for (i=0 ; i< pmove.numphysent ; i++) - { - pe = &pmove.physents[i]; - // get the clipping hull - if (pe->model) - hull = &pmove.physents[i].model->hulls[1]; - else - { - VectorSubtract (pe->mins, player_maxs, mins); - VectorSubtract (pe->maxs, player_mins, maxs); - hull = PM_HullForBox (mins, maxs); - } - - VectorSubtract (pos, pe->origin, test); - - if (PM_HullPointContents (hull, hull->firstclipnode, test) == CONTENTS_SOLID) - return false; - } - - return true; -} - -/* -================ -PM_PlayerMove -================ -*/ -pmtrace_t PM_PlayerMove (vec3_t start, vec3_t end) -{ - pmtrace_t trace, total; - vec3_t offset; - vec3_t start_l, end_l; - hull_t *hull; - int i; - physent_t *pe; - vec3_t mins, maxs; - -// fill in a default trace - memset (&total, 0, sizeof(pmtrace_t)); - total.fraction = 1; - total.ent = -1; - VectorCopy (end, total.endpos); - - for (i=0 ; i< pmove.numphysent ; i++) - { - pe = &pmove.physents[i]; - // get the clipping hull - if (pe->model) - hull = &pmove.physents[i].model->hulls[1]; - else - { - VectorSubtract (pe->mins, player_maxs, mins); - VectorSubtract (pe->maxs, player_mins, maxs); - hull = PM_HullForBox (mins, maxs); - } - - // PM_HullForEntity (ent, mins, maxs, offset); - VectorCopy (pe->origin, offset); - - VectorSubtract (start, offset, start_l); - VectorSubtract (end, offset, end_l); - - // fill in a default trace - memset (&trace, 0, sizeof(pmtrace_t)); - trace.fraction = 1; - trace.allsolid = true; -// trace.startsolid = true; - VectorCopy (end, trace.endpos); - - // trace a line through the apropriate clipping hull - PM_RecursiveHullCheck (hull, hull->firstclipnode, 0, 1, start_l, end_l, &trace); - - if (trace.allsolid) - trace.startsolid = true; - if (trace.startsolid) - trace.fraction = 0; - - // did we clip the move? - if (trace.fraction < total.fraction) - { - // fix trace up by the offset - VectorAdd (trace.endpos, offset, trace.endpos); - total = trace; - total.ent = i; - } - - } - - return total; -} - - -/* -================ -PM_TraceLine - -FIXME: merge with PM_PlayerMove (PM_Move?) -================ -*/ -pmtrace_t PM_TraceLine (vec3_t start, vec3_t end) -{ - pmtrace_t trace, total; - vec3_t offset; - vec3_t start_l, end_l; - hull_t *hull; - int i; - physent_t *pe; - -// fill in a default trace - memset (&total, 0, sizeof(pmtrace_t)); - total.fraction = 1; - total.ent = -1; - VectorCopy (end, total.endpos); - - for (i=0 ; i< pmove.numphysent ; i++) - { - pe = &pmove.physents[i]; - // get the clipping hull - if (pe->model) - hull = &pmove.physents[i].model->hulls[0]; - else - hull = PM_HullForBox (pe->mins, pe->maxs); - - // PM_HullForEntity (ent, mins, maxs, offset); - VectorCopy (pe->origin, offset); - - VectorSubtract (start, offset, start_l); - VectorSubtract (end, offset, end_l); - - // fill in a default trace - memset (&trace, 0, sizeof(pmtrace_t)); - trace.fraction = 1; - trace.allsolid = true; -// trace.startsolid = true; - VectorCopy (end, trace.endpos); - - // trace a line through the apropriate clipping hull - PM_RecursiveHullCheck (hull, hull->firstclipnode, 0, 1, start_l, end_l, &trace); - - if (trace.allsolid) - trace.startsolid = true; - if (trace.startsolid) - trace.fraction = 0; - - // did we clip the move? - if (trace.fraction < total.fraction) - { - // fix trace up by the offset - VectorAdd (trace.endpos, offset, trace.endpos); - total = trace; - total.ent = i; - } - - } - - return total; -} - - +/* +Copyright (C) 1996-1997 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 "quakedef.h" +#include "pmove.h" + +static hull_t box_hull; +static dclipnode_t box_clipnodes[6]; +static mplane_t box_planes[6]; + +extern vec3_t player_mins; +extern vec3_t player_maxs; + +/* +=================== +PM_InitBoxHull + +Set up the planes and clipnodes so that the six floats of a bounding box +can just be stored out and get a proper hull_t structure. +=================== +*/ +void PM_InitBoxHull (void) +{ + int i; + int side; + + box_hull.clipnodes = box_clipnodes; + box_hull.planes = box_planes; + box_hull.firstclipnode = 0; + box_hull.lastclipnode = 5; + + for (i=0 ; i<6 ; i++) + { + box_clipnodes[i].planenum = i; + + side = i&1; + + box_clipnodes[i].children[side] = CONTENTS_EMPTY; + if (i != 5) + box_clipnodes[i].children[side^1] = i + 1; + else + box_clipnodes[i].children[side^1] = CONTENTS_SOLID; + + box_planes[i].type = i>>1; + box_planes[i].normal[i>>1] = 1; + } + +} + + +/* +=================== +PM_HullForBox + +To keep everything totally uniform, bounding boxes are turned into small +BSP trees instead of being compared directly. +=================== +*/ +hull_t *PM_HullForBox (vec3_t mins, vec3_t maxs) +{ + box_planes[0].dist = maxs[0]; + box_planes[1].dist = mins[0]; + box_planes[2].dist = maxs[1]; + box_planes[3].dist = mins[1]; + box_planes[4].dist = maxs[2]; + box_planes[5].dist = mins[2]; + + return &box_hull; +} + + +/* +================== +PM_HullPointContents + +================== +*/ +int PM_HullPointContents (hull_t *hull, int num, vec3_t p) +{ + float d; + dclipnode_t *node; + mplane_t *plane; + + while (num >= 0) + { + if (num < hull->firstclipnode || num > hull->lastclipnode) + Sys_Error ("PM_HullPointContents: bad node number"); + + node = hull->clipnodes + num; + plane = hull->planes + node->planenum; + + if (plane->type < 3) + d = p[plane->type] - plane->dist; + else + d = DotProduct (plane->normal, p) - plane->dist; + if (d < 0) + num = node->children[1]; + else + num = node->children[0]; + } + + return num; +} + +/* +================== +PM_PointContents + +================== +*/ +int PM_PointContents (vec3_t p) +{ + float d; + dclipnode_t *node; + mplane_t *plane; + hull_t *hull; + int num; + + hull = &pmove.physents[0].model->hulls[0]; + + num = hull->firstclipnode; + + while (num >= 0) + { + if (num < hull->firstclipnode || num > hull->lastclipnode) + Sys_Error ("PM_HullPointContents: bad node number"); + + node = hull->clipnodes + num; + plane = hull->planes + node->planenum; + + if (plane->type < 3) + d = p[plane->type] - plane->dist; + else + d = DotProduct (plane->normal, p) - plane->dist; + if (d < 0) + num = node->children[1]; + else + num = node->children[0]; + } + + return num; +} + +/* +=============================================================================== + +LINE TESTING IN HULLS + +=============================================================================== +*/ + +// 1/32 epsilon to keep floating point happy +#define DIST_EPSILON (0.03125) + +/* +================== +PM_RecursiveHullCheck + +================== +*/ +qboolean PM_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, pmtrace_t *trace) +{ + dclipnode_t *node; + mplane_t *plane; + float t1, t2; + float frac; + int i; + vec3_t mid; + int side; + float midf; + +// check for empty + if (num < 0) + { + if (num != CONTENTS_SOLID) + { + trace->allsolid = false; + if (num == CONTENTS_EMPTY) + trace->inopen = true; + else + trace->inwater = true; + } + else + trace->startsolid = true; + return true; // empty + } + + if (num < hull->firstclipnode || num > hull->lastclipnode) + Sys_Error ("PM_RecursiveHullCheck: bad node number"); + +// +// find the point distances +// + node = hull->clipnodes + num; + plane = hull->planes + node->planenum; + + if (plane->type < 3) + { + t1 = p1[plane->type] - plane->dist; + t2 = p2[plane->type] - plane->dist; + } + else + { + t1 = DotProduct (plane->normal, p1) - plane->dist; + t2 = DotProduct (plane->normal, p2) - plane->dist; + } + +#if 1 + if (t1 >= 0 && t2 >= 0) + return PM_RecursiveHullCheck (hull, node->children[0], p1f, p2f, p1, p2, trace); + if (t1 < 0 && t2 < 0) + return PM_RecursiveHullCheck (hull, node->children[1], p1f, p2f, p1, p2, trace); +#else + if ( (t1 >= DIST_EPSILON && t2 >= DIST_EPSILON) || (t2 > t1 && t1 >= 0) ) + return PM_RecursiveHullCheck (hull, node->children[0], p1f, p2f, p1, p2, trace); + if ( (t1 <= -DIST_EPSILON && t2 <= -DIST_EPSILON) || (t2 < t1 && t1 <= 0) ) + return PM_RecursiveHullCheck (hull, node->children[1], p1f, p2f, p1, p2, trace); +#endif + +// put the crosspoint DIST_EPSILON pixels on the near side + if (t1 < 0) + frac = (t1 + DIST_EPSILON)/(t1-t2); + else + frac = (t1 - DIST_EPSILON)/(t1-t2); + if (frac < 0) + frac = 0; + if (frac > 1) + frac = 1; + + midf = p1f + (p2f - p1f)*frac; + for (i=0 ; i<3 ; i++) + mid[i] = p1[i] + frac*(p2[i] - p1[i]); + + side = (t1 < 0); + +// move up to the node + if (!PM_RecursiveHullCheck (hull, node->children[side], p1f, midf, p1, mid, trace) ) + return false; + +#ifdef PARANOID + if (PM_HullPointContents (pm_hullmodel, mid, node->children[side]) + == CONTENTS_SOLID) + { + Con_Printf ("mid PointInHullSolid\n"); + return false; + } +#endif + + if (PM_HullPointContents (hull, node->children[side^1], mid) + != CONTENTS_SOLID) +// go past the node + return PM_RecursiveHullCheck (hull, node->children[side^1], midf, p2f, mid, p2, trace); + + if (trace->allsolid) + return false; // never got out of the solid area + +//================== +// the other side of the node is solid, this is the impact point +//================== + if (!side) + { + VectorCopy (plane->normal, trace->plane.normal); + trace->plane.dist = plane->dist; + } + else + { + VectorSubtract (vec3_origin, plane->normal, trace->plane.normal); + trace->plane.dist = -plane->dist; + } + + while (PM_HullPointContents (hull, hull->firstclipnode, mid) + == CONTENTS_SOLID) + { // shouldn't really happen, but does occasionally + frac -= 0.1; + if (frac < 0) + { + trace->fraction = midf; + VectorCopy (mid, trace->endpos); + Con_DPrintf ("backup past 0\n"); + return false; + } + midf = p1f + (p2f - p1f)*frac; + for (i=0 ; i<3 ; i++) + mid[i] = p1[i] + frac*(p2[i] - p1[i]); + } + + trace->fraction = midf; + VectorCopy (mid, trace->endpos); + + return false; +} + + +/* +================ +PM_TestPlayerPosition + +Returns false if the given player position is not valid (in solid) +================ +*/ +qboolean PM_TestPlayerPosition (vec3_t pos) +{ + int i; + physent_t *pe; + vec3_t mins, maxs, test; + hull_t *hull; + + for (i=0 ; i< pmove.numphysent ; i++) + { + pe = &pmove.physents[i]; + // get the clipping hull + if (pe->model) + hull = &pmove.physents[i].model->hulls[1]; + else + { + VectorSubtract (pe->mins, player_maxs, mins); + VectorSubtract (pe->maxs, player_mins, maxs); + hull = PM_HullForBox (mins, maxs); + } + + VectorSubtract (pos, pe->origin, test); + + if (PM_HullPointContents (hull, hull->firstclipnode, test) == CONTENTS_SOLID) + return false; + } + + return true; +} + +/* +================ +PM_PlayerMove +================ +*/ +pmtrace_t PM_PlayerMove (vec3_t start, vec3_t end) +{ + pmtrace_t trace, total; + vec3_t offset; + vec3_t start_l, end_l; + hull_t *hull; + int i; + physent_t *pe; + vec3_t mins, maxs; + +// fill in a default trace + memset (&total, 0, sizeof(pmtrace_t)); + total.fraction = 1; + total.ent = -1; + VectorCopy (end, total.endpos); + + for (i=0 ; i< pmove.numphysent ; i++) + { + pe = &pmove.physents[i]; + // get the clipping hull + if (pe->model) + hull = &pmove.physents[i].model->hulls[1]; + else + { + VectorSubtract (pe->mins, player_maxs, mins); + VectorSubtract (pe->maxs, player_mins, maxs); + hull = PM_HullForBox (mins, maxs); + } + + // PM_HullForEntity (ent, mins, maxs, offset); + VectorCopy (pe->origin, offset); + + VectorSubtract (start, offset, start_l); + VectorSubtract (end, offset, end_l); + + // fill in a default trace + memset (&trace, 0, sizeof(pmtrace_t)); + trace.fraction = 1; + trace.allsolid = true; +// trace.startsolid = true; + VectorCopy (end, trace.endpos); + + // trace a line through the apropriate clipping hull + PM_RecursiveHullCheck (hull, hull->firstclipnode, 0, 1, start_l, end_l, &trace); + + if (trace.allsolid) + trace.startsolid = true; + if (trace.startsolid) + trace.fraction = 0; + + // did we clip the move? + if (trace.fraction < total.fraction) + { + // fix trace up by the offset + VectorAdd (trace.endpos, offset, trace.endpos); + total = trace; + total.ent = i; + } + + } + + return total; +} + + +/* +================ +PM_TraceLine + +FIXME: merge with PM_PlayerMove (PM_Move?) +================ +*/ +pmtrace_t PM_TraceLine (vec3_t start, vec3_t end) +{ + pmtrace_t trace, total; + vec3_t offset; + vec3_t start_l, end_l; + hull_t *hull; + int i; + physent_t *pe; + +// fill in a default trace + memset (&total, 0, sizeof(pmtrace_t)); + total.fraction = 1; + total.ent = -1; + VectorCopy (end, total.endpos); + + for (i=0 ; i< pmove.numphysent ; i++) + { + pe = &pmove.physents[i]; + // get the clipping hull + if (pe->model) + hull = &pmove.physents[i].model->hulls[0]; + else + hull = PM_HullForBox (pe->mins, pe->maxs); + + // PM_HullForEntity (ent, mins, maxs, offset); + VectorCopy (pe->origin, offset); + + VectorSubtract (start, offset, start_l); + VectorSubtract (end, offset, end_l); + + // fill in a default trace + memset (&trace, 0, sizeof(pmtrace_t)); + trace.fraction = 1; + trace.allsolid = true; +// trace.startsolid = true; + VectorCopy (end, trace.endpos); + + // trace a line through the apropriate clipping hull + PM_RecursiveHullCheck (hull, hull->firstclipnode, 0, 1, start_l, end_l, &trace); + + if (trace.allsolid) + trace.startsolid = true; + if (trace.startsolid) + trace.fraction = 0; + + // did we clip the move? + if (trace.fraction < total.fraction) + { + // fix trace up by the offset + VectorAdd (trace.endpos, offset, trace.endpos); + total = trace; + total.ent = i; + } + + } + + return total; +} + + diff --git a/source/pr_cmds.c b/source/pr_cmds.c index dd1cdb79..eae8dcea 100644 --- a/source/pr_cmds.c +++ b/source/pr_cmds.c @@ -1,1873 +1,2337 @@ -/* -Copyright (C) 1996-1997 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 "qwsvdef.h" - -#define RETURN_EDICT(e) (((int *)pr_globals)[OFS_RETURN] = EDICT_TO_PROG(e)) -#define RETURN_STRING(s) (((int *)pr_globals)[OFS_RETURN] = PR_SetString(s)) - -/* -=============================================================================== - - BUILT-IN FUNCTIONS - -=============================================================================== -*/ - -char *PF_VarString (int first) -{ - int i; - static char out[256]; - - out[0] = 0; - for (i=first ; is_name) ,s); - ed = PROG_TO_EDICT(pr_global_struct->self); - ED_Print (ed); - - SV_Error ("Program error"); -} - -/* -================= -PF_objerror - -Dumps out self, then an error message. The program is aborted and self is -removed, but the level can continue. - -objerror(value) -================= -*/ -void PF_objerror (void) -{ - char *s; - edict_t *ed; - - s = PF_VarString(0); - Con_Printf ("======OBJECT ERROR in %s:\n%s\n", PR_GetString(pr_xfunction->s_name),s); - ed = PROG_TO_EDICT(pr_global_struct->self); - ED_Print (ed); - ED_Free (ed); - - SV_Error ("Program error"); -} - - - -/* -============== -PF_makevectors - -Writes new values for v_forward, v_up, and v_right based on angles -makevectors(vector) -============== -*/ -void PF_makevectors (void) -{ - AngleVectors (G_VECTOR(OFS_PARM0), pr_global_struct->v_forward, pr_global_struct->v_right, pr_global_struct->v_up); -} - -/* -================= -PF_setorigin - -This is the only valid way to move an object without using the physics of the world (setting velocity and waiting). Directly changing origin will not set internal links correctly, so clipping would be messed up. This should be called when an object is spawned, and then only if it is teleported. - -setorigin (entity, origin) -================= -*/ -void PF_setorigin (void) -{ - edict_t *e; - float *org; - - e = G_EDICT(OFS_PARM0); - org = G_VECTOR(OFS_PARM1); - VectorCopy (org, e->v.origin); - SV_LinkEdict (e, false); -} - - -/* -================= -PF_setsize - -the size box is rotated by the current angle - -setsize (entity, minvector, maxvector) -================= -*/ -void PF_setsize (void) -{ - edict_t *e; - float *min, *max; - - e = G_EDICT(OFS_PARM0); - min = G_VECTOR(OFS_PARM1); - max = G_VECTOR(OFS_PARM2); - VectorCopy (min, e->v.mins); - VectorCopy (max, e->v.maxs); - VectorSubtract (max, min, e->v.size); - SV_LinkEdict (e, false); -} - - -/* -================= -PF_setmodel - -setmodel(entity, model) -Also sets size, mins, and maxs for inline bmodels -================= -*/ -void PF_setmodel (void) -{ - edict_t *e; - char *m, **check; - int i; - model_t *mod; - - e = G_EDICT(OFS_PARM0); - m = G_STRING(OFS_PARM1); - -// check to see if model was properly precached - for (i=0, check = sv.model_precache ; *check ; i++, check++) - if (!strcmp(*check, m)) - break; - - if (!*check) - PR_RunError ("no precache: %s\n", m); - - e->v.model = PR_SetString(m); - e->v.modelindex = i; - -// if it is an inline model, get the size information for it - if (m[0] == '*') - { - mod = Mod_ForName (m, true); - VectorCopy (mod->mins, e->v.mins); - VectorCopy (mod->maxs, e->v.maxs); - VectorSubtract (mod->maxs, mod->mins, e->v.size); - SV_LinkEdict (e, false); - } - -} - -/* -================= -PF_bprint - -broadcast print to everyone on server - -bprint(value) -================= -*/ -void PF_bprint (void) -{ - char *s; - int level; - - level = G_FLOAT(OFS_PARM0); - - s = PF_VarString(1); - SV_BroadcastPrintf (level, "%s", s); -} - -/* -================= -PF_sprint - -single print to a specific client - -sprint(clientent, value) -================= -*/ - -void PF_sprint (void) -{ - char *s; - client_t *client; - int entnum; - int level; - - entnum = G_EDICTNUM(OFS_PARM0); - level = G_FLOAT(OFS_PARM1); - - s = PF_VarString(2); - - if (entnum < 1 || entnum > MAX_CLIENTS) - { - Con_Printf ("tried to sprint to a non-client\n"); - return; - } - - client = &svs.clients[entnum-1]; - - SV_ClientPrintf (client, level, "%s", s); -} - - -/* -================= -PF_centerprint - -single print to a specific client - -centerprint(clientent, value) -================= -*/ -void PF_centerprint (void) -{ - char *s; - int entnum; - client_t *cl; - - entnum = G_EDICTNUM(OFS_PARM0); - s = PF_VarString(1); - - if (entnum < 1 || entnum > MAX_CLIENTS) - { - Con_Printf ("tried to sprint to a non-client\n"); - return; - } - - cl = &svs.clients[entnum-1]; - - ClientReliableWrite_Begin (cl, svc_centerprint, 2 + strlen(s)); - ClientReliableWrite_String (cl, s); - - if (sv.demorecording) { - DemoReliableWrite_Begin (dem_single, entnum - 1, 2 + strlen(s)); - MSG_WriteByte (demo.buf, svc_centerprint); - MSG_WriteString (demo.buf, s); - } -} - - -/* -================= -PF_normalize - -vector normalize(vector) -================= -*/ -void PF_normalize (void) -{ - float *value1; - vec3_t newvalue; - float new; - - value1 = G_VECTOR(OFS_PARM0); - - new = value1[0] * value1[0] + value1[1] * value1[1] + value1[2]*value1[2]; - new = sqrt(new); - - if (new == 0) - newvalue[0] = newvalue[1] = newvalue[2] = 0; - else - { - new = 1/new; - newvalue[0] = value1[0] * new; - newvalue[1] = value1[1] * new; - newvalue[2] = value1[2] * new; - } - - VectorCopy (newvalue, G_VECTOR(OFS_RETURN)); -} - -/* -================= -PF_vlen - -scalar vlen(vector) -================= -*/ -void PF_vlen (void) -{ - float *value1; - float new; - - value1 = G_VECTOR(OFS_PARM0); - - new = value1[0] * value1[0] + value1[1] * value1[1] + value1[2]*value1[2]; - new = sqrt(new); - - G_FLOAT(OFS_RETURN) = new; -} - -/* -================= -PF_vectoyaw - -float vectoyaw(vector) -================= -*/ -void PF_vectoyaw (void) -{ - float *value1; - float yaw; - - value1 = G_VECTOR(OFS_PARM0); - - if (value1[1] == 0 && value1[0] == 0) - yaw = 0; - else - { - yaw = (int) (atan2(value1[1], value1[0]) * 180 / M_PI); - if (yaw < 0) - yaw += 360; - } - - G_FLOAT(OFS_RETURN) = yaw; -} - - -/* -================= -PF_vectoangles - -vector vectoangles(vector) -================= -*/ -void PF_vectoangles (void) -{ - float *value1; - float forward; - float yaw, pitch; - - value1 = G_VECTOR(OFS_PARM0); - - if (value1[1] == 0 && value1[0] == 0) - { - yaw = 0; - if (value1[2] > 0) - pitch = 90; - else - pitch = 270; - } - else - { - yaw = (int) (atan2(value1[1], value1[0]) * 180 / M_PI); - if (yaw < 0) - yaw += 360; - - forward = sqrt (value1[0]*value1[0] + value1[1]*value1[1]); - pitch = (int) (atan2(value1[2], forward) * 180 / M_PI); - if (pitch < 0) - pitch += 360; - } - - G_FLOAT(OFS_RETURN+0) = pitch; - G_FLOAT(OFS_RETURN+1) = yaw; - G_FLOAT(OFS_RETURN+2) = 0; -} - -/* -================= -PF_Random - -Returns a number from 0<= num < 1 - -random() -================= -*/ -void PF_random (void) -{ - float num; - - num = (rand ()&0x7fff) / ((float)0x7fff); - - G_FLOAT(OFS_RETURN) = num; -} - - -/* -================= -PF_ambientsound - -================= -*/ -void PF_ambientsound (void) -{ - char **check; - char *samp; - float *pos; - float vol, attenuation; - int i, soundnum; - - pos = G_VECTOR (OFS_PARM0); - samp = G_STRING(OFS_PARM1); - vol = G_FLOAT(OFS_PARM2); - attenuation = G_FLOAT(OFS_PARM3); - -// check to see if samp was properly precached - for (soundnum=0, check = sv.sound_precache ; *check ; check++, soundnum++) - if (!strcmp(*check,samp)) - break; - - if (!*check) - { - Con_Printf ("no precache: %s\n", samp); - return; - } - -// add an svc_spawnambient command to the level signon packet - - MSG_WriteByte (&sv.signon,svc_spawnstaticsound); - for (i=0 ; i<3 ; i++) - MSG_WriteCoord(&sv.signon, pos[i]); - - MSG_WriteByte (&sv.signon, soundnum); - - MSG_WriteByte (&sv.signon, vol*255); - MSG_WriteByte (&sv.signon, attenuation*64); - -} - -/* -================= -PF_sound - -Each entity can have eight independant sound sources, like voice, -weapon, feet, etc. - -Channel 0 is an auto-allocate channel, the others override anything -already running on that entity/channel pair. - -An attenuation of 0 will play full volume everywhere in the level. -Larger attenuations will drop off. - -================= -*/ -void PF_sound (void) -{ - char *sample; - int channel; - edict_t *entity; - int volume; - float attenuation; - - entity = G_EDICT(OFS_PARM0); - channel = G_FLOAT(OFS_PARM1); - sample = G_STRING(OFS_PARM2); - volume = G_FLOAT(OFS_PARM3) * 255; - attenuation = G_FLOAT(OFS_PARM4); - - SV_StartSound (entity, channel, sample, volume, attenuation); -} - -/* -================= -PF_break - -break() -================= -*/ -void PF_break (void) -{ -Con_Printf ("break statement\n"); -*(int *)-4 = 0; // dump to debugger -// PR_RunError ("break statement"); -} - -/* -================= -PF_traceline - -Used for use tracing and shot targeting -Traces are blocked by bbox and exact bsp entityes, and also slide box entities -if the tryents flag is set. - -traceline (vector1, vector2, tryents) -================= -*/ -void PF_traceline (void) -{ - float *v1, *v2; - trace_t trace; - int nomonsters; - edict_t *ent; - - v1 = G_VECTOR(OFS_PARM0); - v2 = G_VECTOR(OFS_PARM1); - nomonsters = G_FLOAT(OFS_PARM2); - ent = G_EDICT(OFS_PARM3); - - trace = SV_Move (v1, vec3_origin, vec3_origin, v2, nomonsters, ent); - - pr_global_struct->trace_allsolid = trace.allsolid; - pr_global_struct->trace_startsolid = trace.startsolid; - pr_global_struct->trace_fraction = trace.fraction; - pr_global_struct->trace_inwater = trace.inwater; - pr_global_struct->trace_inopen = trace.inopen; - VectorCopy (trace.endpos, pr_global_struct->trace_endpos); - VectorCopy (trace.plane.normal, pr_global_struct->trace_plane_normal); - pr_global_struct->trace_plane_dist = trace.plane.dist; - if (trace.ent) - pr_global_struct->trace_ent = EDICT_TO_PROG(trace.ent); - else - pr_global_struct->trace_ent = EDICT_TO_PROG(sv.edicts); -} - -/* -================= -PF_checkpos - -Returns true if the given entity can move to the given position from it's -current position by walking or rolling. -FIXME: make work... -scalar checkpos (entity, vector) -================= -*/ -void PF_checkpos (void) -{ -} - -//============================================================================ - -byte checkpvs[MAX_MAP_LEAFS/8]; - -int PF_newcheckclient (int check) -{ - int i; - byte *pvs; - edict_t *ent; - mleaf_t *leaf; - vec3_t org; - -// cycle to the next one - - if (check < 1) - check = 1; - if (check > MAX_CLIENTS) - check = MAX_CLIENTS; - - if (check == MAX_CLIENTS) - i = 1; - else - i = check + 1; - - for ( ; ; i++) - { - if (i == MAX_CLIENTS+1) - i = 1; - - ent = EDICT_NUM(i); - - if (i == check) - break; // didn't find anything else - - if (ent->free) - continue; - if (ent->v.health <= 0) - continue; - if ((int)ent->v.flags & FL_NOTARGET) - continue; - - // anything that is a client, or has a client as an enemy - break; - } - -// get the PVS for the entity - VectorAdd (ent->v.origin, ent->v.view_ofs, org); - leaf = Mod_PointInLeaf (org, sv.worldmodel); - pvs = Mod_LeafPVS (leaf, sv.worldmodel); - memcpy (checkpvs, pvs, (sv.worldmodel->numleafs+7)>>3 ); - - return i; -} - -/* -================= -PF_checkclient - -Returns a client (or object that has a client enemy) that would be a -valid target. - -If there are more than one valid options, they are cycled each frame - -If (self.origin + self.viewofs) is not in the PVS of the current target, -it is not returned at all. - -name checkclient () -================= -*/ -#define MAX_CHECK 16 -int c_invis, c_notvis; -void PF_checkclient (void) -{ - edict_t *ent, *self; - mleaf_t *leaf; - int l; - vec3_t view; - -// find a new check if on a new frame - if (sv.time - sv.lastchecktime >= 0.1) - { - sv.lastcheck = PF_newcheckclient (sv.lastcheck); - sv.lastchecktime = sv.time; - } - -// return check if it might be visible - ent = EDICT_NUM(sv.lastcheck); - if (ent->free || ent->v.health <= 0) - { - RETURN_EDICT(sv.edicts); - return; - } - -// if current entity can't possibly see the check entity, return 0 - self = PROG_TO_EDICT(pr_global_struct->self); - VectorAdd (self->v.origin, self->v.view_ofs, view); - leaf = Mod_PointInLeaf (view, sv.worldmodel); - l = (leaf - sv.worldmodel->leafs) - 1; - if ( (l<0) || !(checkpvs[l>>3] & (1<<(l&7)) ) ) - { -c_notvis++; - RETURN_EDICT(sv.edicts); - return; - } - -// might be able to see it -c_invis++; - RETURN_EDICT(ent); -} - -//============================================================================ - - -/* -================= -PF_stuffcmd - -Sends text over to the client's execution buffer - -stuffcmd (clientent, value) -================= -*/ -void PF_stuffcmd (void) -{ - int entnum; - char *str; - client_t *cl; - char *buf; - int i; - - entnum = G_EDICTNUM(OFS_PARM0); - if (entnum < 1 || entnum > MAX_CLIENTS) - PR_RunError ("Parm 0 not a client"); - str = G_STRING(OFS_PARM1); - - cl = &svs.clients[entnum-1]; - - buf = cl->stufftext_buf; - if (strlen(buf) + strlen(str) >= MAX_STUFFTEXT) - PR_RunError ("stufftext buffer overflow"); - strcat (buf, str); - - for (i = strlen(buf); i >= 0; i--) - { - if (buf[i] == '\n') - { - if (!strcmp(buf, "disconnect\n")) - { - // so long and thanks for all the fish - cl->drop = true; - buf[0] = 0; - return; - } - ClientReliableWrite_Begin (cl, svc_stufftext, 2+strlen(buf)); - ClientReliableWrite_String (cl, buf); - if (sv.demorecording) { - DemoReliableWrite_Begin ( dem_single, cl - svs.clients, 2+strlen(buf)); - MSG_WriteByte(demo.buf, svc_stufftext); - MSG_WriteString(demo.buf, buf); - } - buf[0] = 0; - } - } -} - -/* -================= -PF_localcmd - -Sends text over to the client's execution buffer - -localcmd (string) -================= -*/ -void PF_localcmd (void) -{ - char *str; - - str = G_STRING(OFS_PARM0); - Cbuf_AddText (str); -} - -void PF_executecmd (void) -{ - Cbuf_Execute(); -} - -char pr_string_temp[128]; - -/* -================= -PF_tokanize - -tokanize string - -void tokanize(string) -================= -*/ - -void PF_tokanize (void) -{ - char *str; - - str = G_STRING(OFS_PARM0); - Cmd_TokenizeString(str); -} - -/* -================= -PF_argc - -returns number of tokens (must be executed after PF_Tokanize!) - -float argc(void) -================= -*/ - -void PF_argc (void) -{ - G_FLOAT(OFS_RETURN) = (float) Cmd_Argc(); -} - -/* -================= -PF_argv - -returns token requested by user (must be executed after PF_Tokanize!) - -string argc(float) -================= -*/ - -void PF_argv (void) -{ - int num; - - num = (int) G_FLOAT(OFS_PARM0); - - if (num < 0 ) num = 0; - if (num > Cmd_Argc()-1) num = Cmd_Argc()-1; - - sprintf (pr_string_temp, "%s", Cmd_Argv(num)); - G_INT(OFS_RETURN) = PR_SetString(pr_string_temp); -} - - -/* -================= -PF_cvar - -float cvar (string) -================= -*/ -void PF_cvar (void) -{ - char *str; - - str = G_STRING(OFS_PARM0); - - G_FLOAT(OFS_RETURN) = Cvar_VariableValue (str); -} - -/* -================= -PF_cvar_set - -float cvar (string) -================= -*/ -void PF_cvar_set (void) -{ - char *var_name, *val; - cvar_t *var; - - var_name = G_STRING(OFS_PARM0); - val = G_STRING(OFS_PARM1); - - var = Cvar_FindVar(var_name); - if (!var) - { - Con_Printf ("PF_cvar_set: variable %s not found\n", var_name); - return; - } - - Cvar_Set (var, val); -} - -/* -================= -PF_findradius - -Returns a chain of entities that have origins within a spherical area - -findradius (origin, radius) -================= -*/ -void PF_findradius (void) -{ - edict_t *ent, *chain; - float rad; - float *org; - vec3_t eorg; - int i, j; - - chain = (edict_t *)sv.edicts; - - org = G_VECTOR(OFS_PARM0); - rad = G_FLOAT(OFS_PARM1); - - ent = NEXT_EDICT(sv.edicts); - for (i=1 ; ifree) - continue; - if (ent->v.solid == SOLID_NOT) - continue; - for (j=0 ; j<3 ; j++) - eorg[j] = org[j] - (ent->v.origin[j] + (ent->v.mins[j] + ent->v.maxs[j])*0.5); - if (Length(eorg) > rad) - continue; - - ent->v.chain = EDICT_TO_PROG(chain); - chain = ent; - } - - RETURN_EDICT(chain); -} - - -/* -========= -PF_dprint -========= -*/ -void PF_dprint (void) -{ - Con_Printf ("%s",PF_VarString(0)); -} - -char pr_string_temp[128]; - -void PF_ftos (void) -{ - float v; - v = G_FLOAT(OFS_PARM0); - - if (v == (int)v) - sprintf (pr_string_temp, "%d",(int)v); - else - sprintf (pr_string_temp, "%5.1f",v); - G_INT(OFS_RETURN) = PR_SetString(pr_string_temp); -} - -void PF_fabs (void) -{ - float v; - v = G_FLOAT(OFS_PARM0); - G_FLOAT(OFS_RETURN) = fabs(v); -} - -void PF_vtos (void) -{ - sprintf (pr_string_temp, "'%5.1f %5.1f %5.1f'", G_VECTOR(OFS_PARM0)[0], G_VECTOR(OFS_PARM0)[1], G_VECTOR(OFS_PARM0)[2]); - G_INT(OFS_RETURN) = PR_SetString(pr_string_temp); -} - -void PF_Spawn (void) -{ - edict_t *ed; - ed = ED_Alloc(); - RETURN_EDICT(ed); -} - -void PF_Remove (void) -{ - edict_t *ed; - - ed = G_EDICT(OFS_PARM0); - ED_Free (ed); -} - - -// entity (entity start, .string field, string match) find = #5; -void PF_Find (void) -{ - int e; - int f; - char *s, *t; - edict_t *ed; - - e = G_EDICTNUM(OFS_PARM0); - f = G_INT(OFS_PARM1); - s = G_STRING(OFS_PARM2); - if (!s) - PR_RunError ("PF_Find: bad search string"); - - for (e++ ; e < sv.num_edicts ; e++) - { - ed = EDICT_NUM(e); - if (ed->free) - continue; - t = E_STRING(ed,f); - if (!t) - continue; - if (!strcmp(t,s)) - { - RETURN_EDICT(ed); - return; - } - } - - RETURN_EDICT(sv.edicts); -} - -void PR_CheckEmptyString (char *s) -{ - if (s[0] <= ' ') - PR_RunError ("Bad string"); -} - -void PF_precache_file (void) -{ // precache_file is only used to copy files with qcc, it does nothing - G_INT(OFS_RETURN) = G_INT(OFS_PARM0); -} - -void PF_precache_sound (void) -{ - char *s; - int i; - - if (sv.state != ss_loading) - PR_RunError ("PF_Precache_*: Precache can only be done in spawn functions"); - - s = G_STRING(OFS_PARM0); - G_INT(OFS_RETURN) = G_INT(OFS_PARM0); - PR_CheckEmptyString (s); - - for (i=0 ; iself); - yaw = G_FLOAT(OFS_PARM0); - dist = G_FLOAT(OFS_PARM1); - - if ( !( (int)ent->v.flags & (FL_ONGROUND|FL_FLY|FL_SWIM) ) ) - { - G_FLOAT(OFS_RETURN) = 0; - return; - } - - yaw = yaw*M_PI*2 / 360; - - move[0] = cos(yaw)*dist; - move[1] = sin(yaw)*dist; - move[2] = 0; - -// save program state, because SV_movestep may call other progs - oldf = pr_xfunction; - oldself = pr_global_struct->self; - - G_FLOAT(OFS_RETURN) = SV_movestep(ent, move, true); - - -// restore program state - pr_xfunction = oldf; - pr_global_struct->self = oldself; -} - -/* -=============== -PF_droptofloor - -void() droptofloor -=============== -*/ -void PF_droptofloor (void) -{ - edict_t *ent; - vec3_t end; - trace_t trace; - - ent = PROG_TO_EDICT(pr_global_struct->self); - - VectorCopy (ent->v.origin, end); - end[2] -= 256; - - trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, false, ent); - - if (trace.fraction == 1 || trace.allsolid) - G_FLOAT(OFS_RETURN) = 0; - else - { - VectorCopy (trace.endpos, ent->v.origin); - SV_LinkEdict (ent, false); - ent->v.flags = (int)ent->v.flags | FL_ONGROUND; - ent->v.groundentity = EDICT_TO_PROG(trace.ent); - G_FLOAT(OFS_RETURN) = 1; - } -} - -/* -=============== -PF_lightstyle - -void(float style, string value) lightstyle -=============== -*/ -void PF_lightstyle (void) -{ - int style; - char *val; - client_t *client; - int j; - - style = G_FLOAT(OFS_PARM0); - val = G_STRING(OFS_PARM1); - -// change the string in sv - sv.lightstyles[style] = val; - -// send message to all clients on this server - if (sv.state != ss_active) - return; - - for (j=0, client = svs.clients ; jstate == cs_spawned ) - { - ClientReliableWrite_Begin (client, svc_lightstyle, strlen(val)+3); - ClientReliableWrite_Char (client, style); - ClientReliableWrite_String (client, val); - } - if (sv.demorecording) - { - DemoReliableWrite_Begin( dem_all, 0, strlen(val)+3); - MSG_WriteByte(demo.buf, svc_lightstyle); - MSG_WriteChar(demo.buf, style); - MSG_WriteString(demo.buf, val); - } -} - -void PF_rint (void) -{ - float f; - f = G_FLOAT(OFS_PARM0); - if (f > 0) - G_FLOAT(OFS_RETURN) = (int)(f + 0.5); - else - G_FLOAT(OFS_RETURN) = (int)(f - 0.5); -} -void PF_floor (void) -{ - G_FLOAT(OFS_RETURN) = floor(G_FLOAT(OFS_PARM0)); -} -void PF_ceil (void) -{ - G_FLOAT(OFS_RETURN) = ceil(G_FLOAT(OFS_PARM0)); -} - - -/* -============= -PF_checkbottom -============= -*/ -void PF_checkbottom (void) -{ - edict_t *ent; - - ent = G_EDICT(OFS_PARM0); - - G_FLOAT(OFS_RETURN) = SV_CheckBottom (ent); -} - -/* -============= -PF_pointcontents -============= -*/ -void PF_pointcontents (void) -{ - float *v; - - v = G_VECTOR(OFS_PARM0); - - G_FLOAT(OFS_RETURN) = SV_PointContents (v); -} - -/* -============= -PF_nextent - -entity nextent(entity) -============= -*/ -void PF_nextent (void) -{ - int i; - edict_t *ent; - - i = G_EDICTNUM(OFS_PARM0); - while (1) - { - i++; - if (i == sv.num_edicts) - { - RETURN_EDICT(sv.edicts); - return; - } - ent = EDICT_NUM(i); - if (!ent->free) - { - RETURN_EDICT(ent); - return; - } - } -} - -/* -============= -PF_aim - -Pick a vector for the player to shoot along -vector aim(entity, missilespeed) -============= -*/ -//cvar_t sv_aim = {"sv_aim", "0.93"}; -cvar_t sv_aim = {"sv_aim", "2"}; -void PF_aim (void) -{ - edict_t *ent, *check, *bestent; - vec3_t start, dir, end, bestdir; - int i, j; - trace_t tr; - float dist, bestdist; - float speed; - char *noaim; - - ent = G_EDICT(OFS_PARM0); - speed = G_FLOAT(OFS_PARM1); - - VectorCopy (ent->v.origin, start); - start[2] += 20; - -// noaim option - i = NUM_FOR_EDICT(ent); - if (i>0 && i 0) - { - VectorCopy (pr_global_struct->v_forward, G_VECTOR(OFS_RETURN)); - return; - } - } - -// try sending a trace straight - VectorCopy (pr_global_struct->v_forward, dir); - VectorMA (start, 2048, dir, end); - tr = SV_Move (start, vec3_origin, vec3_origin, end, false, ent); - if (tr.ent && tr.ent->v.takedamage == DAMAGE_AIM - && (!teamplay.value || ent->v.team <=0 || ent->v.team != tr.ent->v.team) ) - { - VectorCopy (pr_global_struct->v_forward, G_VECTOR(OFS_RETURN)); - return; - } - - -// try all possible entities - VectorCopy (dir, bestdir); - bestdist = sv_aim.value; - bestent = NULL; - - check = NEXT_EDICT(sv.edicts); - for (i=1 ; iv.takedamage != DAMAGE_AIM) - continue; - if (check == ent) - continue; - if (teamplay.value && ent->v.team > 0 && ent->v.team == check->v.team) - continue; // don't aim at teammate - for (j=0 ; j<3 ; j++) - end[j] = check->v.origin[j] - + 0.5*(check->v.mins[j] + check->v.maxs[j]); - VectorSubtract (end, start, dir); - VectorNormalize (dir); - dist = DotProduct (dir, pr_global_struct->v_forward); - if (dist < bestdist) - continue; // to far to turn - tr = SV_Move (start, vec3_origin, vec3_origin, end, false, ent); - if (tr.ent == check) - { // can shoot at this one - bestdist = dist; - bestent = check; - } - } - - if (bestent) - { - VectorSubtract (bestent->v.origin, ent->v.origin, dir); - dist = DotProduct (dir, pr_global_struct->v_forward); - VectorScale (pr_global_struct->v_forward, dist, end); - end[2] = dir[2]; - VectorNormalize (end); - VectorCopy (end, G_VECTOR(OFS_RETURN)); - } - else - { - VectorCopy (bestdir, G_VECTOR(OFS_RETURN)); - } -} - -/* -============== -PF_changeyaw - -This was a major timewaster in progs, so it was converted to C -============== -*/ -void PF_changeyaw (void) -{ - edict_t *ent; - float ideal, current, move, speed; - - ent = PROG_TO_EDICT(pr_global_struct->self); - current = anglemod( ent->v.angles[1] ); - ideal = ent->v.ideal_yaw; - speed = ent->v.yaw_speed; - - if (current == ideal) - return; - move = ideal - current; - if (ideal > current) - { - if (move >= 180) - move = move - 360; - } - else - { - if (move <= -180) - move = move + 360; - } - if (move > 0) - { - if (move > speed) - move = speed; - } - else - { - if (move < -speed) - move = -speed; - } - - ent->v.angles[1] = anglemod (current + move); -} - -/* -=============================================================================== - -MESSAGE WRITING - -=============================================================================== -*/ - -#define MSG_BROADCAST 0 // unreliable to all -#define MSG_ONE 1 // reliable to one (msg_entity) -#define MSG_ALL 2 // reliable to all -#define MSG_INIT 3 // write to the init string -#define MSG_MULTICAST 4 // for multicast() - -sizebuf_t *WriteDest (void) -{ - int dest; -// int entnum; -// edict_t *ent; - - dest = G_FLOAT(OFS_PARM0); - switch (dest) - { - case MSG_BROADCAST: - return &sv.datagram; - - case MSG_ONE: - SV_Error("Shouldn't be at MSG_ONE"); -#if 0 - ent = PROG_TO_EDICT(pr_global_struct->msg_entity); - entnum = NUM_FOR_EDICT(ent); - if (entnum < 1 || entnum > MAX_CLIENTS) - PR_RunError ("WriteDest: not a client"); - return &svs.clients[entnum-1].netchan.message; -#endif - - case MSG_ALL: - return &sv.reliable_datagram; - - case MSG_INIT: - if (sv.state != ss_loading) - PR_RunError ("PF_Write_*: MSG_INIT can only be written in spawn functions"); - return &sv.signon; - - case MSG_MULTICAST: - return &sv.multicast; - - default: - PR_RunError ("WriteDest: bad destination"); - break; - } - - return NULL; -} - -static client_t *Write_GetClient(void) -{ - int entnum; - edict_t *ent; - - ent = PROG_TO_EDICT(pr_global_struct->msg_entity); - entnum = NUM_FOR_EDICT(ent); - if (entnum < 1 || entnum > MAX_CLIENTS) - PR_RunError ("WriteDest: not a client"); - return &svs.clients[entnum-1]; -} - - -void PF_WriteByte (void) -{ - if (G_FLOAT(OFS_PARM0) == MSG_ONE) { - client_t *cl = Write_GetClient(); - ClientReliableCheckBlock(cl, 1); - ClientReliableWrite_Byte(cl, G_FLOAT(OFS_PARM1)); - if (sv.demorecording) - { - DemoReliableWrite_Begin(dem_single, cl - svs.clients, 1); - MSG_WriteByte(demo.buf, G_FLOAT(OFS_PARM1)); - } - } else - MSG_WriteByte (WriteDest(), G_FLOAT(OFS_PARM1)); -} - -void PF_WriteChar (void) -{ - if (G_FLOAT(OFS_PARM0) == MSG_ONE) { - client_t *cl = Write_GetClient(); - ClientReliableCheckBlock(cl, 1); - ClientReliableWrite_Char(cl, G_FLOAT(OFS_PARM1)); - if (sv.demorecording) - { - DemoReliableWrite_Begin(dem_single, cl - svs.clients, 1); - MSG_WriteByte(demo.buf, G_FLOAT(OFS_PARM1)); - } - } else - MSG_WriteChar (WriteDest(), G_FLOAT(OFS_PARM1)); -} - -void PF_WriteShort (void) -{ - if (G_FLOAT(OFS_PARM0) == MSG_ONE) { - client_t *cl = Write_GetClient(); - ClientReliableCheckBlock(cl, 2); - ClientReliableWrite_Short(cl, G_FLOAT(OFS_PARM1)); - if (sv.demorecording) - { - DemoReliableWrite_Begin(dem_single, cl - svs.clients, 2); - MSG_WriteShort(demo.buf, G_FLOAT(OFS_PARM1)); - } - } else - MSG_WriteShort (WriteDest(), G_FLOAT(OFS_PARM1)); -} - -void PF_WriteLong (void) -{ - if (G_FLOAT(OFS_PARM0) == MSG_ONE) { - client_t *cl = Write_GetClient(); - ClientReliableCheckBlock(cl, 4); - ClientReliableWrite_Long(cl, G_FLOAT(OFS_PARM1)); - if (sv.demorecording) - { - DemoReliableWrite_Begin(dem_single, cl - svs.clients, 4); - MSG_WriteLong(demo.buf, G_FLOAT(OFS_PARM1)); - } - } else - MSG_WriteLong (WriteDest(), G_FLOAT(OFS_PARM1)); -} - -void PF_WriteAngle (void) -{ - if (G_FLOAT(OFS_PARM0) == MSG_ONE) { - client_t *cl = Write_GetClient(); - ClientReliableCheckBlock(cl, 1); - ClientReliableWrite_Angle(cl, G_FLOAT(OFS_PARM1)); - if (sv.demorecording) - { - DemoReliableWrite_Begin(dem_single, cl - svs.clients, 1); - MSG_WriteByte(demo.buf, G_FLOAT(OFS_PARM1)); - } - } else - MSG_WriteAngle (WriteDest(), G_FLOAT(OFS_PARM1)); -} - -void PF_WriteCoord (void) -{ - if (G_FLOAT(OFS_PARM0) == MSG_ONE) { - client_t *cl = Write_GetClient(); - ClientReliableCheckBlock(cl, 2); - ClientReliableWrite_Coord(cl, G_FLOAT(OFS_PARM1)); - if (sv.demorecording) - { - DemoReliableWrite_Begin(dem_single, cl - svs.clients, 2); - MSG_WriteCoord(demo.buf, G_FLOAT(OFS_PARM1)); - } - } else - MSG_WriteCoord (WriteDest(), G_FLOAT(OFS_PARM1)); -} - -void PF_WriteString (void) -{ - if (G_FLOAT(OFS_PARM0) == MSG_ONE) { - client_t *cl = Write_GetClient(); - ClientReliableCheckBlock(cl, 1+strlen(G_STRING(OFS_PARM1))); - ClientReliableWrite_String(cl, G_STRING(OFS_PARM1)); - if (sv.demorecording) - { - DemoReliableWrite_Begin(dem_single, cl - svs.clients, 1 + strlen(G_STRING(OFS_PARM1))); - MSG_WriteString(demo.buf, G_STRING(OFS_PARM1)); - } - } else - MSG_WriteString (WriteDest(), G_STRING(OFS_PARM1)); -} - - -void PF_WriteEntity (void) -{ - if (G_FLOAT(OFS_PARM0) == MSG_ONE) { - client_t *cl = Write_GetClient(); - ClientReliableCheckBlock(cl, 2); - ClientReliableWrite_Short(cl, G_EDICTNUM(OFS_PARM1)); - if (sv.demorecording) - { - DemoReliableWrite_Begin(dem_single, cl - svs.clients, 2); - MSG_WriteShort(demo.buf, G_EDICTNUM(OFS_PARM1)); - } - } else - MSG_WriteShort (WriteDest(), G_EDICTNUM(OFS_PARM1)); -} - -//============================================================================= - -int SV_ModelIndex (char *name); - -void PF_makestatic (void) -{ - edict_t *ent; - int i; - - ent = G_EDICT(OFS_PARM0); - - MSG_WriteByte (&sv.signon,svc_spawnstatic); - - MSG_WriteByte (&sv.signon, SV_ModelIndex(PR_GetString(ent->v.model))); - - MSG_WriteByte (&sv.signon, ent->v.frame); - MSG_WriteByte (&sv.signon, ent->v.colormap); - MSG_WriteByte (&sv.signon, ent->v.skin); - for (i=0 ; i<3 ; i++) - { - MSG_WriteCoord(&sv.signon, ent->v.origin[i]); - MSG_WriteAngle(&sv.signon, ent->v.angles[i]); - } - -// throw the entity away now - ED_Free (ent); -} - -//============================================================================= - -/* -============== -PF_setspawnparms -============== -*/ -void PF_setspawnparms (void) -{ - edict_t *ent; - int i; - client_t *client; - - ent = G_EDICT(OFS_PARM0); - i = NUM_FOR_EDICT(ent); - if (i < 1 || i > MAX_CLIENTS) - PR_RunError ("Entity is not a client"); - - // copy spawn parms out of the client_t - client = svs.clients + (i-1); - - for (i=0 ; i< NUM_SPAWN_PARMS ; i++) - (&pr_global_struct->parm1)[i] = client->spawn_parms[i]; -} - -/* -============== -PF_changelevel -============== -*/ -void PF_changelevel (void) -{ - char *s; - static int last_spawncount; - -// make sure we don't issue two changelevels - if (svs.spawncount == last_spawncount) - return; - last_spawncount = svs.spawncount; - - s = G_STRING(OFS_PARM0); - Cbuf_AddText (va("map %s\n",s)); -} - - -/* -============== -PF_logfrag - -logfrag (killer, killee) -============== -*/ -void PF_logfrag (void) -{ - edict_t *ent1, *ent2; - int e1, e2; - char *s; - - ent1 = G_EDICT(OFS_PARM0); - ent2 = G_EDICT(OFS_PARM1); - - e1 = NUM_FOR_EDICT(ent1); - e2 = NUM_FOR_EDICT(ent2); - - if (e1 < 1 || e1 > MAX_CLIENTS - || e2 < 1 || e2 > MAX_CLIENTS) - return; - - s = va("\\%s\\%s\\\n",svs.clients[e1-1].name, svs.clients[e2-1].name); - - SZ_Print (&svs.log[svs.logsequence&1], s); - if (sv_fraglogfile) { - fprintf (sv_fraglogfile, s); - fflush (sv_fraglogfile); - } -} - - -/* -============== -PF_infokey - -string(entity e, string key) infokey -============== -*/ -void PF_infokey (void) -{ - edict_t *e; - int e1; - char *value; - char *key; - static char ov[256]; - - e = G_EDICT(OFS_PARM0); - e1 = NUM_FOR_EDICT(e); - key = G_STRING(OFS_PARM1); - - if (e1 == 0) { - if ((value = Info_ValueForKey (svs.info, key)) == NULL || - !*value) - value = Info_ValueForKey(localinfo, key); - } else if (e1 <= MAX_CLIENTS) { - if (!strcmp(key, "ip")) - value = strcpy(ov, NET_BaseAdrToString (svs.clients[e1-1].netchan.remote_address)); - else if (!strcmp(key, "ping")) { - int ping = SV_CalcPing (&svs.clients[e1-1]); - sprintf(ov, "%d", ping); - value = ov; - } else - value = Info_ValueForKey (svs.clients[e1-1].userinfo, key); - } else - value = ""; - - RETURN_STRING(value); -} - -/* -============== -PF_stof - -float(string s) stof -============== -*/ -void PF_stof (void) -{ - char *s; - - s = G_STRING(OFS_PARM0); - - G_FLOAT(OFS_RETURN) = atof(s); -} - - -/* -============== -PF_multicast - -void(vector where, float set) multicast -============== -*/ -void PF_multicast (void) -{ - float *o; - int to; - - o = G_VECTOR(OFS_PARM0); - to = G_FLOAT(OFS_PARM1); - - SV_Multicast (o, to); -} - - -void PF_Fixme (void) -{ - PR_RunError ("unimplemented bulitin"); -} - - - -builtin_t pr_builtin[] = -{ - PF_Fixme, -PF_makevectors, // void(entity e) makevectors = #1; -PF_setorigin, // void(entity e, vector o) setorigin = #2; -PF_setmodel, // void(entity e, string m) setmodel = #3; -PF_setsize, // void(entity e, vector min, vector max) setsize = #4; -PF_Fixme, // void(entity e, vector min, vector max) setabssize = #5; -PF_break, // void() break = #6; -PF_random, // float() random = #7; -PF_sound, // void(entity e, float chan, string samp) sound = #8; -PF_normalize, // vector(vector v) normalize = #9; -PF_error, // void(string e) error = #10; -PF_objerror, // void(string e) objerror = #11; -PF_vlen, // float(vector v) vlen = #12; -PF_vectoyaw, // float(vector v) vectoyaw = #13; -PF_Spawn, // entity() spawn = #14; -PF_Remove, // void(entity e) remove = #15; -PF_traceline, // float(vector v1, vector v2, float tryents) traceline = #16; -PF_checkclient, // entity() clientlist = #17; -PF_Find, // entity(entity start, .string fld, string match) find = #18; -PF_precache_sound, // void(string s) precache_sound = #19; -PF_precache_model, // void(string s) precache_model = #20; -PF_stuffcmd, // void(entity client, string s)stuffcmd = #21; -PF_findradius, // entity(vector org, float rad) findradius = #22; -PF_bprint, // void(string s) bprint = #23; -PF_sprint, // void(entity client, string s) sprint = #24; -PF_dprint, // void(string s) dprint = #25; -PF_ftos, // void(string s) ftos = #26; -PF_vtos, // void(string s) vtos = #27; -PF_coredump, -PF_traceon, -PF_traceoff, -PF_eprint, // void(entity e) debug print an entire entity -PF_walkmove, // float(float yaw, float dist) walkmove -PF_Fixme, // float(float yaw, float dist) walkmove -PF_droptofloor, -PF_lightstyle, -PF_rint, -PF_floor, -PF_ceil, -PF_Fixme, -PF_checkbottom, -PF_pointcontents, -PF_Fixme, -PF_fabs, -PF_aim, -PF_cvar, -PF_localcmd, -PF_nextent, -PF_Fixme, -PF_changeyaw, -PF_Fixme, -PF_vectoangles, - -PF_WriteByte, -PF_WriteChar, -PF_WriteShort, -PF_WriteLong, -PF_WriteCoord, -PF_WriteAngle, -PF_WriteString, -PF_WriteEntity, - -PF_Fixme, -PF_Fixme, -PF_Fixme, -PF_Fixme, -PF_Fixme, -PF_Fixme, -PF_Fixme, - -SV_MoveToGoal, -PF_precache_file, -PF_makestatic, - -PF_changelevel, -PF_Fixme, - -PF_cvar_set, -PF_centerprint, - -PF_ambientsound, - -PF_precache_model, -PF_precache_sound, // precache_sound2 is different only for qcc -PF_precache_file, - -PF_setspawnparms, - -PF_logfrag, - -PF_infokey, -PF_stof, -PF_multicast, -PF_executecmd, // #83 -PF_tokanize, -PF_argc, -PF_argv // #86 -}; -// SET QWE_FUNCS IN VERSION.H IF NUMBER OF FUNCTIONS CHANGES - -builtin_t *pr_builtins = pr_builtin; -int pr_numbuiltins = sizeof(pr_builtin)/sizeof(pr_builtin[0]); - +/* +Copyright (C) 1996-1997 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 "qwsvdef.h" + +#define RETURN_EDICT(e) (((int *)pr_globals)[OFS_RETURN] = EDICT_TO_PROG(e)) +#define RETURN_STRING(s) (((int *)pr_globals)[OFS_RETURN] = PR_SetString(s)) + +/* +=============================================================================== + + BUILT-IN FUNCTIONS + +=============================================================================== +*/ + +char *PF_VarString (int first) +{ + int i; + static char out[2048]; + + out[0] = 0; + for (i=first ; is_name) ,s); + ed = PROG_TO_EDICT(pr_global_struct->self); + ED_Print (ed); + + SV_Error ("Program error"); +} + +/* +================= +PF_objerror + +Dumps out self, then an error message. The program is aborted and self is +removed, but the level can continue. + +objerror(value) +================= +*/ +void PF_objerror (void) +{ + char *s; + edict_t *ed; + + s = PF_VarString(0); + Con_Printf ("======OBJECT ERROR in %s:\n%s\n", PR_GetString(pr_xfunction->s_name),s); + ed = PROG_TO_EDICT(pr_global_struct->self); + ED_Print (ed); + ED_Free (ed); + + SV_Error ("Program error"); +} + + + +/* +============== +PF_makevectors + +Writes new values for v_forward, v_up, and v_right based on angles +makevectors(vector) +============== +*/ +void PF_makevectors (void) +{ + AngleVectors (G_VECTOR(OFS_PARM0), pr_global_struct->v_forward, pr_global_struct->v_right, pr_global_struct->v_up); +} + +/* +================= +PF_setorigin + +This is the only valid way to move an object without using the physics of the world (setting velocity and waiting). Directly changing origin will not set internal links correctly, so clipping would be messed up. This should be called when an object is spawned, and then only if it is teleported. + +setorigin (entity, origin) +================= +*/ +void PF_setorigin (void) +{ + edict_t *e; + float *org; + + e = G_EDICT(OFS_PARM0); + org = G_VECTOR(OFS_PARM1); + VectorCopy (org, e->v.origin); + SV_LinkEdict (e, false); +} + + +/* +================= +PF_setsize + +the size box is rotated by the current angle + +setsize (entity, minvector, maxvector) +================= +*/ +void PF_setsize (void) +{ + edict_t *e; + float *min, *max; + + e = G_EDICT(OFS_PARM0); + min = G_VECTOR(OFS_PARM1); + max = G_VECTOR(OFS_PARM2); + VectorCopy (min, e->v.mins); + VectorCopy (max, e->v.maxs); + VectorSubtract (max, min, e->v.size); + SV_LinkEdict (e, false); +} + + +/* +================= +PF_setmodel + +setmodel(entity, model) +Also sets size, mins, and maxs for inline bmodels +================= +*/ +void PF_setmodel (void) +{ + edict_t *e; + char *m, **check; + int i; + model_t *mod; + + e = G_EDICT(OFS_PARM0); + m = G_STRING(OFS_PARM1); + +// check to see if model was properly precached + for (i=0, check = sv.model_precache ; *check ; i++, check++) + if (!strcmp(*check, m)) + break; + + if (!*check) + PR_RunError ("no precache: %s\n", m); + + e->v.model = PR_SetString(m); + e->v.modelindex = i; + +// if it is an inline model, get the size information for it + if (m[0] == '*') + { + mod = Mod_ForName (m, true); + VectorCopy (mod->mins, e->v.mins); + VectorCopy (mod->maxs, e->v.maxs); + VectorSubtract (mod->maxs, mod->mins, e->v.size); + SV_LinkEdict (e, false); + } + +} + +/* +================= +PF_bprint + +broadcast print to everyone on server + +bprint(value) +================= +*/ +void PF_bprint (void) +{ + char *s; + int level; + + level = G_FLOAT(OFS_PARM0); + + s = PF_VarString(1); + SV_BroadcastPrintf (level, "%s", s); +} + +/* +================= +PF_sprint + +single print to a specific client + +sprint(clientent, value) +================= +*/ + +void PF_sprint (void) +{ + char *s; + client_t *client; + int entnum; + int level; + + entnum = G_EDICTNUM(OFS_PARM0); + level = G_FLOAT(OFS_PARM1); + + s = PF_VarString(2); + + if (entnum < 1 || entnum > MAX_CLIENTS) + { + Con_Printf ("tried to sprint to a non-client\n"); + return; + } + + client = &svs.clients[entnum-1]; + + SV_ClientPrintf (client, level, "%s", s); +} + + +/* +================= +PF_centerprint + +single print to a specific client + +centerprint(clientent, value) +================= +*/ +void PF_centerprint (void) +{ + char *s; + int entnum; + client_t *cl; + + entnum = G_EDICTNUM(OFS_PARM0); + s = PF_VarString(1); + + if (entnum < 1 || entnum > MAX_CLIENTS) + { + Con_Printf ("tried to sprint to a non-client\n"); + return; + } + + cl = &svs.clients[entnum-1]; + + ClientReliableWrite_Begin (cl, svc_centerprint, 2 + strlen(s)); + ClientReliableWrite_String (cl, s); + + if (sv.demorecording) { + DemoWrite_Begin (dem_single, entnum - 1, 2 + strlen(s)); + MSG_WriteByte ((sizebuf_t*)demo.dbuf, svc_centerprint); + MSG_WriteString ((sizebuf_t*)demo.dbuf, s); + } +} + + +/* +================= +PF_normalize + +vector normalize(vector) +================= +*/ +void PF_normalize (void) +{ + float *value1; + vec3_t newvalue; + float new; + + value1 = G_VECTOR(OFS_PARM0); + + new = value1[0] * value1[0] + value1[1] * value1[1] + value1[2]*value1[2]; + new = sqrt(new); + + if (new == 0) + newvalue[0] = newvalue[1] = newvalue[2] = 0; + else + { + new = 1/new; + newvalue[0] = value1[0] * new; + newvalue[1] = value1[1] * new; + newvalue[2] = value1[2] * new; + } + + VectorCopy (newvalue, G_VECTOR(OFS_RETURN)); +} + +/* +================= +PF_vlen + +scalar vlen(vector) +================= +*/ +void PF_vlen (void) +{ + float *value1; + float new; + + value1 = G_VECTOR(OFS_PARM0); + + new = value1[0] * value1[0] + value1[1] * value1[1] + value1[2]*value1[2]; + new = sqrt(new); + + G_FLOAT(OFS_RETURN) = new; +} + +/* +================= +PF_vectoyaw + +float vectoyaw(vector) +================= +*/ +void PF_vectoyaw (void) +{ + float *value1; + float yaw; + + value1 = G_VECTOR(OFS_PARM0); + + if (value1[1] == 0 && value1[0] == 0) + yaw = 0; + else + { + yaw = (int) (atan2(value1[1], value1[0]) * 180 / M_PI); + if (yaw < 0) + yaw += 360; + } + + G_FLOAT(OFS_RETURN) = yaw; +} + + +/* +================= +PF_vectoangles + +vector vectoangles(vector) +================= +*/ +void PF_vectoangles (void) +{ + float *value1; + float forward; + float yaw, pitch; + + value1 = G_VECTOR(OFS_PARM0); + + if (value1[1] == 0 && value1[0] == 0) + { + yaw = 0; + if (value1[2] > 0) + pitch = 90; + else + pitch = 270; + } + else + { + yaw = (int) (atan2(value1[1], value1[0]) * 180 / M_PI); + if (yaw < 0) + yaw += 360; + + forward = sqrt (value1[0]*value1[0] + value1[1]*value1[1]); + pitch = (int) (atan2(value1[2], forward) * 180 / M_PI); + if (pitch < 0) + pitch += 360; + } + + G_FLOAT(OFS_RETURN+0) = pitch; + G_FLOAT(OFS_RETURN+1) = yaw; + G_FLOAT(OFS_RETURN+2) = 0; +} + +/* +================= +PF_Random + +Returns a number from 0<= num < 1 + +random() +================= +*/ +void PF_random (void) +{ + float num; + + num = (rand ()&0x7fff) / ((float)0x7fff); + + G_FLOAT(OFS_RETURN) = num; +} + + +/* +================= +PF_ambientsound + +================= +*/ +void PF_ambientsound (void) +{ + char **check; + char *samp; + float *pos; + float vol, attenuation; + int i, soundnum; + + pos = G_VECTOR (OFS_PARM0); + samp = G_STRING(OFS_PARM1); + vol = G_FLOAT(OFS_PARM2); + attenuation = G_FLOAT(OFS_PARM3); + +// check to see if samp was properly precached + for (soundnum=0, check = sv.sound_precache ; *check ; check++, soundnum++) + if (!strcmp(*check,samp)) + break; + + if (!*check) + { + Con_Printf ("no precache: %s\n", samp); + return; + } + +// add an svc_spawnambient command to the level signon packet + + MSG_WriteByte (&sv.signon,svc_spawnstaticsound); + for (i=0 ; i<3 ; i++) + MSG_WriteCoord(&sv.signon, pos[i]); + + MSG_WriteByte (&sv.signon, soundnum); + + MSG_WriteByte (&sv.signon, vol*255); + MSG_WriteByte (&sv.signon, attenuation*64); + +} + +/* +================= +PF_sound + +Each entity can have eight independant sound sources, like voice, +weapon, feet, etc. + +Channel 0 is an auto-allocate channel, the others override anything +already running on that entity/channel pair. + +An attenuation of 0 will play full volume everywhere in the level. +Larger attenuations will drop off. + +================= +*/ +void PF_sound (void) +{ + char *sample; + int channel; + edict_t *entity; + int volume; + float attenuation; + + entity = G_EDICT(OFS_PARM0); + channel = G_FLOAT(OFS_PARM1); + sample = G_STRING(OFS_PARM2); + volume = G_FLOAT(OFS_PARM3) * 255; + attenuation = G_FLOAT(OFS_PARM4); + + SV_StartSound (entity, channel, sample, volume, attenuation); +} + +/* +================= +PF_break + +break() +================= +*/ +void PF_break (void) +{ +Con_Printf ("break statement\n"); +*(int *)-4 = 0; // dump to debugger +// PR_RunError ("break statement"); +} + +/* +================= +PF_traceline + +Used for use tracing and shot targeting +Traces are blocked by bbox and exact bsp entityes, and also slide box entities +if the tryents flag is set. + +traceline (vector1, vector2, tryents) +================= +*/ +void PF_traceline (void) +{ + float *v1, *v2; + trace_t trace; + int nomonsters; + edict_t *ent; + + v1 = G_VECTOR(OFS_PARM0); + v2 = G_VECTOR(OFS_PARM1); + nomonsters = G_FLOAT(OFS_PARM2); + ent = G_EDICT(OFS_PARM3); + + trace = SV_Move (v1, vec3_origin, vec3_origin, v2, nomonsters, ent); + + pr_global_struct->trace_allsolid = trace.allsolid; + pr_global_struct->trace_startsolid = trace.startsolid; + pr_global_struct->trace_fraction = trace.fraction; + pr_global_struct->trace_inwater = trace.inwater; + pr_global_struct->trace_inopen = trace.inopen; + VectorCopy (trace.endpos, pr_global_struct->trace_endpos); + VectorCopy (trace.plane.normal, pr_global_struct->trace_plane_normal); + pr_global_struct->trace_plane_dist = trace.plane.dist; + if (trace.ent) + pr_global_struct->trace_ent = EDICT_TO_PROG(trace.ent); + else + pr_global_struct->trace_ent = EDICT_TO_PROG(sv.edicts); +} + +/* +================= +PF_checkpos + +Returns true if the given entity can move to the given position from it's +current position by walking or rolling. +FIXME: make work... +scalar checkpos (entity, vector) +================= +*/ +void PF_checkpos (void) +{ +} + +//============================================================================ + +byte checkpvs[MAX_MAP_LEAFS/8]; + +int PF_newcheckclient (int check) +{ + int i; + byte *pvs; + edict_t *ent; + mleaf_t *leaf; + vec3_t org; + +// cycle to the next one + + if (check < 1) + check = 1; + if (check > MAX_CLIENTS) + check = MAX_CLIENTS; + + if (check == MAX_CLIENTS) + i = 1; + else + i = check + 1; + + for ( ; ; i++) + { + if (i == MAX_CLIENTS+1) + i = 1; + + ent = EDICT_NUM(i); + + if (i == check) + break; // didn't find anything else + + if (ent->free) + continue; + if (ent->v.health <= 0) + continue; + if ((int)ent->v.flags & FL_NOTARGET) + continue; + + // anything that is a client, or has a client as an enemy + break; + } + +// get the PVS for the entity + VectorAdd (ent->v.origin, ent->v.view_ofs, org); + leaf = Mod_PointInLeaf (org, sv.worldmodel); + pvs = Mod_LeafPVS (leaf, sv.worldmodel); + memcpy (checkpvs, pvs, (sv.worldmodel->numleafs+7)>>3 ); + + return i; +} + +/* +================= +PF_checkclient + +Returns a client (or object that has a client enemy) that would be a +valid target. + +If there are more than one valid options, they are cycled each frame + +If (self.origin + self.viewofs) is not in the PVS of the current target, +it is not returned at all. + +name checkclient () +================= +*/ +#define MAX_CHECK 16 +int c_invis, c_notvis; +void PF_checkclient (void) +{ + edict_t *ent, *self; + mleaf_t *leaf; + int l; + vec3_t view; + +// find a new check if on a new frame + if (sv.time - sv.lastchecktime >= 0.1) + { + sv.lastcheck = PF_newcheckclient (sv.lastcheck); + sv.lastchecktime = sv.time; + } + +// return check if it might be visible + ent = EDICT_NUM(sv.lastcheck); + if (ent->free || ent->v.health <= 0) + { + RETURN_EDICT(sv.edicts); + return; + } + +// if current entity can't possibly see the check entity, return 0 + self = PROG_TO_EDICT(pr_global_struct->self); + VectorAdd (self->v.origin, self->v.view_ofs, view); + leaf = Mod_PointInLeaf (view, sv.worldmodel); + l = (leaf - sv.worldmodel->leafs) - 1; + if ( (l<0) || !(checkpvs[l>>3] & (1<<(l&7)) ) ) + { +c_notvis++; + RETURN_EDICT(sv.edicts); + return; + } + +// might be able to see it +c_invis++; + RETURN_EDICT(ent); +} + +//============================================================================ + + +/* +================= +PF_stuffcmd + +Sends text over to the client's execution buffer + +stuffcmd (clientent, value) +================= +*/ +void PF_stuffcmd (void) +{ + int entnum; + char *str; + client_t *cl; + char *buf; + int i; + + entnum = G_EDICTNUM(OFS_PARM0); + if (entnum < 1 || entnum > MAX_CLIENTS) + PR_RunError ("Parm 0 not a client"); + str = G_STRING(OFS_PARM1); + + cl = &svs.clients[entnum-1]; + + buf = cl->stufftext_buf; + if (strlen(buf) + strlen(str) >= MAX_STUFFTEXT) + PR_RunError ("stufftext buffer overflow"); + strcat (buf, str); + + for (i = strlen(buf); i >= 0; i--) + { + if (buf[i] == '\n') + { + if (!strcmp(buf, "disconnect\n")) + { + // so long and thanks for all the fish + cl->drop = true; + buf[0] = 0; + return; + } + ClientReliableWrite_Begin (cl, svc_stufftext, 2+strlen(buf)); + ClientReliableWrite_String (cl, buf); + if (sv.demorecording) { + DemoWrite_Begin ( dem_single, cl - svs.clients, 2+strlen(buf)); + MSG_WriteByte((sizebuf_t*)demo.dbuf, svc_stufftext); + MSG_WriteString((sizebuf_t*)demo.dbuf, buf); + } + buf[0] = 0; + } + } +} + +/* +================= +PF_localcmd + +Sends text over to the client's execution buffer + +localcmd (string) +================= +*/ +void PF_localcmd (void) +{ + char *str; + + str = G_STRING(OFS_PARM0); + Cbuf_AddText (str); +} + +void PF_executecmd (void) +{ + int old_other, old_self; // mod_consolecmd will be executed, so we need to store this + + old_self = pr_global_struct->self; + old_other = pr_global_struct->other; + + Cbuf_Execute(); + + pr_global_struct->self = old_self; + pr_global_struct->other = old_other; +} + +#define MAX_PR_STRING_SIZE 2048 + +int pr_string_index = 0; +char pr_string_buf[8][MAX_PR_STRING_SIZE]; +char *pr_string_temp = pr_string_buf[0]; + +void PF_SetTempString(void) +{ + pr_string_temp = pr_string_buf[pr_string_index++&7]; +} + + +/* +================= +PF_tokanize + +tokanize string + +void tokanize(string) +================= +*/ + +void PF_tokanize (void) +{ + char *str; + + str = G_STRING(OFS_PARM0); + Cmd_TokenizeString(str); +} + +/* +================= +PF_argc + +returns number of tokens (must be executed after PF_Tokanize!) + +float argc(void) +================= +*/ + +void PF_argc (void) +{ + G_FLOAT(OFS_RETURN) = (float) Cmd_Argc(); +} + +/* +================= +PF_argv + +returns token requested by user (must be executed after PF_Tokanize!) + +string argc(float) +================= +*/ + +void PF_argv (void) +{ + int num; + + num = (int) G_FLOAT(OFS_PARM0); + + if (num < 0 ) num = 0; + if (num > Cmd_Argc()-1) num = Cmd_Argc()-1; + + sprintf (pr_string_temp, "%s", Cmd_Argv(num)); + G_INT(OFS_RETURN) = PR_SetString(pr_string_temp); + PF_SetTempString(); +} + +/* +================= +PF_teamfield + +string teamfield(.string field) +================= +*/ + +void PF_teamfield (void) +{ + pr_teamfield = G_INT(OFS_PARM0); +} + +/* +================= +PF_substr + +string substr(string str, float start, float len) +================= +*/ + +void PF_substr (void) +{ + char *s; + int start, len, l; + + s = G_STRING(OFS_PARM0); + start = (int) G_FLOAT(OFS_PARM1); + len = (int) G_FLOAT(OFS_PARM2); + l = strlen(s); + + if (start >= l || !len || !*s) { + G_INT(OFS_RETURN) = PR_SetTmpString(""); + return; + } + + s += start; + l -= start; + + if ( len > l + 1) + len = l+1; + + strncpy(pr_string_temp, s, len); + pr_string_temp[len] = 0; + + G_INT(OFS_RETURN) = PR_SetString(pr_string_temp); + + PF_SetTempString(); +} + +/* +================= +PF_strcat + +string strcat(string str1, string str2) +================= +*/ + +void PF_strcat (void) +{ + + strcpy(pr_string_temp, PF_VarString(0)); + G_INT(OFS_RETURN) = PR_SetString(pr_string_temp); + + PF_SetTempString(); +} + +/* +================= +PF_strlen + +float strlen(string str) +================= +*/ + +void PF_strlen (void) +{ + G_FLOAT(OFS_RETURN) = (float) strlen(G_STRING(OFS_PARM0)); +} + +/* +================= +PF_str2byte + +float str2byte (string str) +================= +*/ + +void PF_str2byte (void) +{ + G_FLOAT(OFS_RETURN) = (float) *G_STRING(OFS_PARM0); +} + +/* +================= +PF_str2short + +float str2short (string str) +================= +*/ + +void PF_str2short (void) +{ + G_FLOAT(OFS_RETURN) = (float) LittleShort(*(short*)G_STRING(OFS_PARM0)); +} + +/* +================= +PF_newstr + +string newstr (string str [, float size]) +================= +*/ + +void PF_newstr (void) +{ + char *s; + int i, size; + + s = G_STRING(OFS_PARM0); + + for (i = 0; i < MAX_PRSTR; i++) + { + if (pr_newstrtbl[i] == NULL) + break; + } + + if (i == MAX_PRSTR) + PR_RunError("PF_newstr: MAX_PRSTR"); + + size = strlen(s) + 1; + if (pr_argc == 2 && (int) G_FLOAT(OFS_PARM1) > size) + size = (int) G_FLOAT(OFS_PARM1); + + pr_newstrtbl[i] = (char*) Z_Malloc(size); + strcpy(pr_newstrtbl[i], s); + + G_INT(OFS_RETURN) = -(i+MAX_PRSTR); +} + +/* +================= +PF_frestr + +void freestr (string str) +================= +*/ + +void PF_freestr (void) +{ + int num; + + num = G_INT(OFS_PARM0); + if (num > - MAX_PRSTR) + PR_RunError("freestr: Bad pointer"); + + num = - (num + MAX_PRSTR); + Z_Free(pr_newstrtbl[num]); + pr_newstrtbl[num] = NULL; +} + +void PF_clear_strtbl(void) +{ + int i; + + for (i = 0; i < MAX_PRSTR; i++) + { + if (pr_newstrtbl[i] != NULL) { + Z_Free(pr_newstrtbl[i]); + pr_newstrtbl[i] = NULL; + } + } +} + +/* +================= +PF_readcmd + +string readmcmd (string str) +================= +*/ + +void PF_readcmd (void) +{ + char *s; + static char output[8000]; + extern char outputbuf[]; + extern redirect_t sv_redirected; + redirect_t old; + + s = G_STRING(OFS_PARM0); + + Cbuf_Execute(); + Cbuf_AddText (s); + + old = sv_redirected; + if (old != RD_NONE) + SV_EndRedirect(); + + SV_BeginRedirect(RD_MOD); + Cbuf_Execute(); + Q_strncpyz(output, outputbuf, 8000); + SV_EndRedirect(); + + if (old != RD_NONE) + SV_BeginRedirect(old); + + + G_INT(OFS_RETURN) = PR_SetString(output); +} + +/* +================= +PF_redirectcmd + +void redirectcmd (entity to, string str) +================= +*/ + +void PF_redirectcmd (void) +{ + char *s; + int entnum; + extern redirect_t sv_redirected; + + if (sv_redirected) + return; + + entnum = G_EDICTNUM(OFS_PARM0); + if (entnum < 1 || entnum > MAX_CLIENTS) + PR_RunError ("Parm 0 not a client"); + + s = G_STRING(OFS_PARM1); + + Cbuf_AddText (s); + + SV_BeginRedirect(RD_MOD + entnum); + Cbuf_Execute(); + SV_EndRedirect(); +} +dfunction_t *ED_FindFunction (char *name); +void PF_calltimeofday (void) +{ + date_t date; + dfunction_t *f; + + if ((f = ED_FindFunction ("timeofday")) != NULL) { + + Sys_TimeOfDay(&date); + + G_FLOAT(OFS_PARM0) = (float)date.sec; + G_FLOAT(OFS_PARM1) = (float)date.min; + G_FLOAT(OFS_PARM2) = (float)date.hour; + G_FLOAT(OFS_PARM3) = (float)date.day; + G_FLOAT(OFS_PARM4) = (float)date.mon; + G_FLOAT(OFS_PARM5) = (float)date.year; + G_INT(OFS_PARM6) = PR_SetTmpString(date.str); + + PR_ExecuteProgram((func_t)(f - pr_functions)); + } + +} + + +/* +================= +PF_strcpy + +void strcpy(string dst, string src) +FIXME: check for null pointers first? +================= +*/ + +void PF_strcpy (void) +{ + strcpy(G_STRING(OFS_PARM0), G_STRING(OFS_PARM1)); +} + +/* +================= +PF_strncpy + +void strcpy(string dst, string src, float count) +FIXME: check for null pointers first? +================= +*/ + +void PF_strncpy (void) +{ + strncpy(G_STRING(OFS_PARM0), G_STRING(OFS_PARM1), (int) G_FLOAT(OFS_PARM2)); +} + + +/* +================= +PF_strstr + +string strstr(string str, string sub) +================= +*/ + +void PF_strstr (void) +{ + char *str, *sub, *p; + + str = G_STRING(OFS_PARM0); + sub = G_STRING(OFS_PARM1); + + if ((p = strstr(str, sub)) == NULL) + { + G_INT(OFS_RETURN) = 0; + return; + } + + RETURN_STRING(p); +} + +/* +==================== +SV_CleanName_Init + +sets chararcter table to translate quake texts to more friendly texts +==================== +*/ + +static char chartbl[256]; + +void PR_CleanLogText_Init () +{ + int i; + + for (i = 0; i < 256; i++) + chartbl[i] = (i&127) < 32 ? ' ' : i&127; + + chartbl[13] = 13; + chartbl[10] = 10; + // special cases + + // numbers + for (i = 18; i < 28; i++) + chartbl[i] = chartbl[i + 128] = i + 30; + + // brackets + chartbl[29] = chartbl[29 + 128] = chartbl[128] = '('; + chartbl[31] = chartbl[31 + 128] = chartbl[130] = ')'; + chartbl[16] = chartbl[16 + 128]= '['; + chartbl[17] = chartbl[17 + 128] = ']'; + + // hash + for (i = 1; i < 5; i++) + chartbl[i] = chartbl[i + 128] = '#'; + for (i = 6; i < 10; i++) + chartbl[i] = chartbl[i + 128] = '#'; + + chartbl[11] = chartbl[11 + 128] = '#'; + + // dot + chartbl[5] = chartbl[14] = chartbl[15] = chartbl[28] = chartbl[46] = '.'; + chartbl[5 + 128] = chartbl[14 + 128] = chartbl[15 + 128] = chartbl[28 + 128] = chartbl[46 + 128] = '.'; + + // left arrow + chartbl[127] = '>'; + + // right arrow + chartbl[141] = '<'; + + // '=' + chartbl[30] = chartbl[129] = chartbl[30 + 128] = '='; + + // whitespaces + chartbl[12] = chartbl[12 + 128] = chartbl[138] = ' '; + + chartbl[33] = chartbl[33 + 128]= '!'; +} + +void PR_CleanText(unsigned char *text) +{ + while (*text) + *text = chartbl[*text++]; +} + +/* +================ +PF_log + +void log(string name, float console, string text) +================= +*/ + +void PF_log(void) +{ + char name[MAX_OSPATH], *text; + FILE *file; + + sprintf(name,"%s/%s.log",com_gamedir, G_STRING(OFS_PARM0)); + text = PF_VarString(2); + PR_CleanText(text); + + if ((file = fopen(name, "a")) == NULL) + { + Sys_Printf("coldn't open log file %s\n", name); + } else { + fprintf (file, text); + fflush (file); + fclose(file); + } + + if (G_FLOAT(OFS_PARM1)) + Sys_Printf("%s", text); + +} + +/* +================= +PF_cvar + +float cvar (string) +================= +*/ +void PF_cvar (void) +{ + char *str; + + str = G_STRING(OFS_PARM0); + + G_FLOAT(OFS_RETURN) = Cvar_VariableValue (str); +} + +/* +================= +PF_cvar_set + +float cvar (string) +================= +*/ +void PF_cvar_set (void) +{ + char *var_name, *val; + cvar_t *var; + + var_name = G_STRING(OFS_PARM0); + val = G_STRING(OFS_PARM1); + + var = Cvar_FindVar(var_name); + if (!var) + { + Con_Printf ("PF_cvar_set: variable %s not found\n", var_name); + return; + } + + Cvar_Set (var, val); +} + +/* +================= +PF_findradius + +Returns a chain of entities that have origins within a spherical area + +findradius (origin, radius) +================= +*/ +void PF_findradius (void) +{ + edict_t *ent, *chain; + float rad; + float *org; + vec3_t eorg; + int i, j; + + chain = (edict_t *)sv.edicts; + + org = G_VECTOR(OFS_PARM0); + rad = G_FLOAT(OFS_PARM1); + + ent = NEXT_EDICT(sv.edicts); + for (i=1 ; ifree) + continue; + if (ent->v.solid == SOLID_NOT) + continue; + for (j=0 ; j<3 ; j++) + eorg[j] = org[j] - (ent->v.origin[j] + (ent->v.mins[j] + ent->v.maxs[j])*0.5); + if (Length(eorg) > rad) + continue; + + ent->v.chain = EDICT_TO_PROG(chain); + chain = ent; + } + + RETURN_EDICT(chain); +} + + +/* +========= +PF_dprint +========= +*/ +void PF_dprint (void) +{ + Con_Printf ("%s",PF_VarString(0)); +} + +/* +========= +PF_conprint +========= +*/ +void PF_conprint (void) +{ + Sys_Printf ("%s",PF_VarString(0)); +} + +//char pr_string_temp[128]; + +void PF_ftos (void) +{ + float v; + v = G_FLOAT(OFS_PARM0); + + if (v == (int)v) + sprintf (pr_string_temp, "%d",(int)v); + else + sprintf (pr_string_temp, "%5.1f",v); + G_INT(OFS_RETURN) = PR_SetString(pr_string_temp); + PF_SetTempString(); +} + +void PF_fabs (void) +{ + float v; + v = G_FLOAT(OFS_PARM0); + G_FLOAT(OFS_RETURN) = fabs(v); +} + +void PF_vtos (void) +{ + sprintf (pr_string_temp, "'%5.1f %5.1f %5.1f'", G_VECTOR(OFS_PARM0)[0], G_VECTOR(OFS_PARM0)[1], G_VECTOR(OFS_PARM0)[2]); + G_INT(OFS_RETURN) = PR_SetString(pr_string_temp); + + PF_SetTempString(); +} + +void PF_Spawn (void) +{ + edict_t *ed; + ed = ED_Alloc(); + RETURN_EDICT(ed); +} + +void PF_Remove (void) +{ + edict_t *ed; + + ed = G_EDICT(OFS_PARM0); + ED_Free (ed); +} + + +// entity (entity start, .string field, string match) find = #5; +void PF_Find (void) +{ + int e; + int f; + char *s, *t; + edict_t *ed; + + e = G_EDICTNUM(OFS_PARM0); + f = G_INT(OFS_PARM1); + s = G_STRING(OFS_PARM2); + if (!s) + PR_RunError ("PF_Find: bad search string"); + + for (e++ ; e < sv.num_edicts ; e++) + { + ed = EDICT_NUM(e); + if (ed->free) + continue; + t = E_STRING(ed,f); + if (!t) + continue; + if (!strcmp(t,s)) + { + RETURN_EDICT(ed); + return; + } + } + + RETURN_EDICT(sv.edicts); +} + +void PR_CheckEmptyString (char *s) +{ + if (s[0] <= ' ') + PR_RunError ("Bad string"); +} + +void PF_precache_file (void) +{ // precache_file is only used to copy files with qcc, it does nothing + G_INT(OFS_RETURN) = G_INT(OFS_PARM0); +} + +void PF_precache_sound (void) +{ + char *s; + int i; + + if (sv.state != ss_loading) + PR_RunError ("PF_Precache_*: Precache can only be done in spawn functions"); + + s = G_STRING(OFS_PARM0); + G_INT(OFS_RETURN) = G_INT(OFS_PARM0); + PR_CheckEmptyString (s); + + for (i=0 ; iself); + yaw = G_FLOAT(OFS_PARM0); + dist = G_FLOAT(OFS_PARM1); + + if ( !( (int)ent->v.flags & (FL_ONGROUND|FL_FLY|FL_SWIM) ) ) + { + G_FLOAT(OFS_RETURN) = 0; + return; + } + + yaw = yaw*M_PI*2 / 360; + + move[0] = cos(yaw)*dist; + move[1] = sin(yaw)*dist; + move[2] = 0; + +// save program state, because SV_movestep may call other progs + oldf = pr_xfunction; + oldself = pr_global_struct->self; + + G_FLOAT(OFS_RETURN) = SV_movestep(ent, move, true); + + +// restore program state + pr_xfunction = oldf; + pr_global_struct->self = oldself; +} + +/* +=============== +PF_droptofloor + +void() droptofloor +=============== +*/ +void PF_droptofloor (void) +{ + edict_t *ent; + vec3_t end; + trace_t trace; + + ent = PROG_TO_EDICT(pr_global_struct->self); + + VectorCopy (ent->v.origin, end); + end[2] -= 256; + + trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, false, ent); + + if (trace.fraction == 1 || trace.allsolid) + G_FLOAT(OFS_RETURN) = 0; + else + { + VectorCopy (trace.endpos, ent->v.origin); + SV_LinkEdict (ent, false); + ent->v.flags = (int)ent->v.flags | FL_ONGROUND; + ent->v.groundentity = EDICT_TO_PROG(trace.ent); + G_FLOAT(OFS_RETURN) = 1; + } +} + +/* +=============== +PF_lightstyle + +void(float style, string value) lightstyle +=============== +*/ +void PF_lightstyle (void) +{ + int style; + char *val; + client_t *client; + int j; + + style = G_FLOAT(OFS_PARM0); + val = G_STRING(OFS_PARM1); + +// change the string in sv + sv.lightstyles[style] = val; + +// send message to all clients on this server + if (sv.state != ss_active) + return; + + for (j=0, client = svs.clients ; jstate == cs_spawned ) + { + ClientReliableWrite_Begin (client, svc_lightstyle, strlen(val)+3); + ClientReliableWrite_Char (client, style); + ClientReliableWrite_String (client, val); + } + if (sv.demorecording) + { + DemoWrite_Begin( dem_all, 0, strlen(val)+3); + MSG_WriteByte((sizebuf_t*)demo.dbuf, svc_lightstyle); + MSG_WriteChar((sizebuf_t*)demo.dbuf, style); + MSG_WriteString((sizebuf_t*)demo.dbuf, val); + } +} + +void PF_rint (void) +{ + float f; + f = G_FLOAT(OFS_PARM0); + if (f > 0) + G_FLOAT(OFS_RETURN) = (int)(f + 0.5); + else + G_FLOAT(OFS_RETURN) = (int)(f - 0.5); +} +void PF_floor (void) +{ + G_FLOAT(OFS_RETURN) = floor(G_FLOAT(OFS_PARM0)); +} +void PF_ceil (void) +{ + G_FLOAT(OFS_RETURN) = ceil(G_FLOAT(OFS_PARM0)); +} + + +/* +============= +PF_checkbottom +============= +*/ +void PF_checkbottom (void) +{ + edict_t *ent; + + ent = G_EDICT(OFS_PARM0); + + G_FLOAT(OFS_RETURN) = SV_CheckBottom (ent); +} + +/* +============= +PF_pointcontents +============= +*/ +void PF_pointcontents (void) +{ + float *v; + + v = G_VECTOR(OFS_PARM0); + + G_FLOAT(OFS_RETURN) = SV_PointContents (v); +} + +/* +============= +PF_nextent + +entity nextent(entity) +============= +*/ +void PF_nextent (void) +{ + int i; + edict_t *ent; + + i = G_EDICTNUM(OFS_PARM0); + while (1) + { + i++; + if (i == sv.num_edicts) + { + RETURN_EDICT(sv.edicts); + return; + } + ent = EDICT_NUM(i); + if (!ent->free) + { + RETURN_EDICT(ent); + return; + } + } +} + +/* +============= +PF_aim + +Pick a vector for the player to shoot along +vector aim(entity, missilespeed) +============= +*/ +//cvar_t sv_aim = {"sv_aim", "0.93"}; +cvar_t sv_aim = {"sv_aim", "2"}; +void PF_aim (void) +{ + edict_t *ent, *check, *bestent; + vec3_t start, dir, end, bestdir; + int i, j; + trace_t tr; + float dist, bestdist; + float speed; + char *noaim; + + ent = G_EDICT(OFS_PARM0); + speed = G_FLOAT(OFS_PARM1); + + VectorCopy (ent->v.origin, start); + start[2] += 20; + +// noaim option + i = NUM_FOR_EDICT(ent); + if (i>0 && i 0) + { + VectorCopy (pr_global_struct->v_forward, G_VECTOR(OFS_RETURN)); + return; + } + } + +// try sending a trace straight + VectorCopy (pr_global_struct->v_forward, dir); + VectorMA (start, 2048, dir, end); + tr = SV_Move (start, vec3_origin, vec3_origin, end, false, ent); + if (tr.ent && tr.ent->v.takedamage == DAMAGE_AIM + && (!teamplay.value || ent->v.team <=0 || ent->v.team != tr.ent->v.team) ) + { + VectorCopy (pr_global_struct->v_forward, G_VECTOR(OFS_RETURN)); + return; + } + + +// try all possible entities + VectorCopy (dir, bestdir); + bestdist = sv_aim.value; + bestent = NULL; + + check = NEXT_EDICT(sv.edicts); + for (i=1 ; iv.takedamage != DAMAGE_AIM) + continue; + if (check == ent) + continue; + if (teamplay.value && ent->v.team > 0 && ent->v.team == check->v.team) + continue; // don't aim at teammate + for (j=0 ; j<3 ; j++) + end[j] = check->v.origin[j] + + 0.5*(check->v.mins[j] + check->v.maxs[j]); + VectorSubtract (end, start, dir); + VectorNormalize (dir); + dist = DotProduct (dir, pr_global_struct->v_forward); + if (dist < bestdist) + continue; // to far to turn + tr = SV_Move (start, vec3_origin, vec3_origin, end, false, ent); + if (tr.ent == check) + { // can shoot at this one + bestdist = dist; + bestent = check; + } + } + + if (bestent) + { + VectorSubtract (bestent->v.origin, ent->v.origin, dir); + dist = DotProduct (dir, pr_global_struct->v_forward); + VectorScale (pr_global_struct->v_forward, dist, end); + end[2] = dir[2]; + VectorNormalize (end); + VectorCopy (end, G_VECTOR(OFS_RETURN)); + } + else + { + VectorCopy (bestdir, G_VECTOR(OFS_RETURN)); + } +} + +/* +============== +PF_changeyaw + +This was a major timewaster in progs, so it was converted to C +============== +*/ +void PF_changeyaw (void) +{ + edict_t *ent; + float ideal, current, move, speed; + + ent = PROG_TO_EDICT(pr_global_struct->self); + current = anglemod( ent->v.angles[1] ); + ideal = ent->v.ideal_yaw; + speed = ent->v.yaw_speed; + + if (current == ideal) + return; + move = ideal - current; + if (ideal > current) + { + if (move >= 180) + move = move - 360; + } + else + { + if (move <= -180) + move = move + 360; + } + if (move > 0) + { + if (move > speed) + move = speed; + } + else + { + if (move < -speed) + move = -speed; + } + + ent->v.angles[1] = anglemod (current + move); +} + +/* +=============================================================================== + +MESSAGE WRITING + +=============================================================================== +*/ + +#define MSG_BROADCAST 0 // unreliable to all +#define MSG_ONE 1 // reliable to one (msg_entity) +#define MSG_ALL 2 // reliable to all +#define MSG_INIT 3 // write to the init string +#define MSG_MULTICAST 4 // for multicast() + +sizebuf_t *WriteDest (void) +{ + int dest; +// int entnum; +// edict_t *ent; + + dest = G_FLOAT(OFS_PARM0); + switch (dest) + { + case MSG_BROADCAST: + return &sv.datagram; + + case MSG_ONE: + SV_Error("Shouldn't be at MSG_ONE"); +#if 0 + ent = PROG_TO_EDICT(pr_global_struct->msg_entity); + entnum = NUM_FOR_EDICT(ent); + if (entnum < 1 || entnum > MAX_CLIENTS) + PR_RunError ("WriteDest: not a client"); + return &svs.clients[entnum-1].netchan.message; +#endif + + case MSG_ALL: + return &sv.reliable_datagram; + + case MSG_INIT: + if (sv.state != ss_loading) + PR_RunError ("PF_Write_*: MSG_INIT can only be written in spawn functions"); + return &sv.signon; + + case MSG_MULTICAST: + return &sv.multicast; + + default: + PR_RunError ("WriteDest: bad destination"); + break; + } + + return NULL; +} + +static client_t *Write_GetClient(void) +{ + int entnum; + edict_t *ent; + + ent = PROG_TO_EDICT(pr_global_struct->msg_entity); + entnum = NUM_FOR_EDICT(ent); + if (entnum < 1 || entnum > MAX_CLIENTS) + PR_RunError ("WriteDest: not a client"); + return &svs.clients[entnum-1]; +} + + +void PF_WriteByte (void) +{ + if (G_FLOAT(OFS_PARM0) == MSG_ONE) { + client_t *cl = Write_GetClient(); + ClientReliableCheckBlock(cl, 1); + ClientReliableWrite_Byte(cl, G_FLOAT(OFS_PARM1)); + if (sv.demorecording) + { + DemoWrite_Begin(dem_single, cl - svs.clients, 1); + MSG_WriteByte((sizebuf_t*)demo.dbuf, G_FLOAT(OFS_PARM1)); + } + } else + MSG_WriteByte (WriteDest(), G_FLOAT(OFS_PARM1)); +} + +void PF_WriteChar (void) +{ + if (G_FLOAT(OFS_PARM0) == MSG_ONE) { + client_t *cl = Write_GetClient(); + ClientReliableCheckBlock(cl, 1); + ClientReliableWrite_Char(cl, G_FLOAT(OFS_PARM1)); + if (sv.demorecording) + { + DemoWrite_Begin(dem_single, cl - svs.clients, 1); + MSG_WriteByte((sizebuf_t*)demo.dbuf, G_FLOAT(OFS_PARM1)); + } + } else + MSG_WriteChar (WriteDest(), G_FLOAT(OFS_PARM1)); +} + +void PF_WriteShort (void) +{ + if (G_FLOAT(OFS_PARM0) == MSG_ONE) { + client_t *cl = Write_GetClient(); + ClientReliableCheckBlock(cl, 2); + ClientReliableWrite_Short(cl, G_FLOAT(OFS_PARM1)); + if (sv.demorecording) + { + DemoWrite_Begin(dem_single, cl - svs.clients, 2); + MSG_WriteShort((sizebuf_t*)demo.dbuf, G_FLOAT(OFS_PARM1)); + } + } else + MSG_WriteShort (WriteDest(), G_FLOAT(OFS_PARM1)); +} + +void PF_WriteLong (void) +{ + if (G_FLOAT(OFS_PARM0) == MSG_ONE) { + client_t *cl = Write_GetClient(); + ClientReliableCheckBlock(cl, 4); + ClientReliableWrite_Long(cl, G_FLOAT(OFS_PARM1)); + if (sv.demorecording) + { + DemoWrite_Begin(dem_single, cl - svs.clients, 4); + MSG_WriteLong((sizebuf_t*)demo.dbuf, G_FLOAT(OFS_PARM1)); + } + } else + MSG_WriteLong (WriteDest(), G_FLOAT(OFS_PARM1)); +} + +void PF_WriteAngle (void) +{ + if (G_FLOAT(OFS_PARM0) == MSG_ONE) { + client_t *cl = Write_GetClient(); + ClientReliableCheckBlock(cl, 1); + ClientReliableWrite_Angle(cl, G_FLOAT(OFS_PARM1)); + if (sv.demorecording) + { + DemoWrite_Begin(dem_single, cl - svs.clients, 1); + MSG_WriteByte((sizebuf_t*)demo.dbuf, G_FLOAT(OFS_PARM1)); + } + } else + MSG_WriteAngle (WriteDest(), G_FLOAT(OFS_PARM1)); +} + +void PF_WriteCoord (void) +{ + if (G_FLOAT(OFS_PARM0) == MSG_ONE) { + client_t *cl = Write_GetClient(); + ClientReliableCheckBlock(cl, 2); + ClientReliableWrite_Coord(cl, G_FLOAT(OFS_PARM1)); + if (sv.demorecording) + { + DemoWrite_Begin(dem_single, cl - svs.clients, 2); + MSG_WriteCoord((sizebuf_t*)demo.dbuf, G_FLOAT(OFS_PARM1)); + } + } else + MSG_WriteCoord (WriteDest(), G_FLOAT(OFS_PARM1)); +} + +void PF_WriteString (void) +{ + if (G_FLOAT(OFS_PARM0) == MSG_ONE) { + client_t *cl = Write_GetClient(); + ClientReliableCheckBlock(cl, 1+strlen(G_STRING(OFS_PARM1))); + ClientReliableWrite_String(cl, G_STRING(OFS_PARM1)); + if (sv.demorecording) + { + DemoWrite_Begin(dem_single, cl - svs.clients, 1 + strlen(G_STRING(OFS_PARM1))); + MSG_WriteString((sizebuf_t*)demo.dbuf, G_STRING(OFS_PARM1)); + } + } else + MSG_WriteString (WriteDest(), G_STRING(OFS_PARM1)); +} + + +void PF_WriteEntity (void) +{ + if (G_FLOAT(OFS_PARM0) == MSG_ONE) { + client_t *cl = Write_GetClient(); + ClientReliableCheckBlock(cl, 2); + ClientReliableWrite_Short(cl, G_EDICTNUM(OFS_PARM1)); + if (sv.demorecording) + { + DemoWrite_Begin(dem_single, cl - svs.clients, 2); + MSG_WriteShort((sizebuf_t*)demo.dbuf, G_EDICTNUM(OFS_PARM1)); + } + } else + MSG_WriteShort (WriteDest(), G_EDICTNUM(OFS_PARM1)); +} + +//============================================================================= + +int SV_ModelIndex (char *name); + +void PF_makestatic (void) +{ + edict_t *ent; + int i; + + ent = G_EDICT(OFS_PARM0); + + MSG_WriteByte (&sv.signon,svc_spawnstatic); + + MSG_WriteByte (&sv.signon, SV_ModelIndex(PR_GetString(ent->v.model))); + + MSG_WriteByte (&sv.signon, ent->v.frame); + MSG_WriteByte (&sv.signon, ent->v.colormap); + MSG_WriteByte (&sv.signon, ent->v.skin); + for (i=0 ; i<3 ; i++) + { + MSG_WriteCoord(&sv.signon, ent->v.origin[i]); + MSG_WriteAngle(&sv.signon, ent->v.angles[i]); + } + +// throw the entity away now + ED_Free (ent); +} + +//============================================================================= + +/* +============== +PF_setspawnparms +============== +*/ +void PF_setspawnparms (void) +{ + edict_t *ent; + int i; + client_t *client; + + ent = G_EDICT(OFS_PARM0); + i = NUM_FOR_EDICT(ent); + if (i < 1 || i > MAX_CLIENTS) + PR_RunError ("Entity is not a client"); + + // copy spawn parms out of the client_t + client = svs.clients + (i-1); + + for (i=0 ; i< NUM_SPAWN_PARMS ; i++) + (&pr_global_struct->parm1)[i] = client->spawn_parms[i]; +} + +/* +============== +PF_changelevel +============== +*/ +void PF_changelevel (void) +{ + char *s; + static int last_spawncount; + +// make sure we don't issue two changelevels + if (svs.spawncount == last_spawncount) + return; + last_spawncount = svs.spawncount; + + s = G_STRING(OFS_PARM0); + Cbuf_AddText (va("map %s\n",s)); +} + + +/* +============== +PF_logfrag + +logfrag (killer, killee) +============== +*/ +void PF_logfrag (void) +{ + edict_t *ent1, *ent2; + int e1, e2; + char *s; + + ent1 = G_EDICT(OFS_PARM0); + ent2 = G_EDICT(OFS_PARM1); + + e1 = NUM_FOR_EDICT(ent1); + e2 = NUM_FOR_EDICT(ent2); + + if (e1 < 1 || e1 > MAX_CLIENTS + || e2 < 1 || e2 > MAX_CLIENTS) + return; + + s = va("\\%s\\%s\\\n",svs.clients[e1-1].name, svs.clients[e2-1].name); + + SZ_Print (&svs.log[svs.logsequence&1], s); + if (sv_fraglogfile) { + fprintf (sv_fraglogfile, s); + fflush (sv_fraglogfile); + } +} + + +/* +============== +PF_infokey + +string(entity e, string key) infokey +============== +*/ +void PF_infokey (void) +{ + edict_t *e; + int e1; + char *value; + char *key; + static char ov[256]; + + e = G_EDICT(OFS_PARM0); + e1 = NUM_FOR_EDICT(e); + key = G_STRING(OFS_PARM1); + + if (e1 == 0) { + if ((value = Info_ValueForKey (svs.info, key)) == NULL || + !*value) + value = Info_ValueForKey(localinfo, key); + } else if (e1 <= MAX_CLIENTS) { + if (!strcmp(key, "ip")) + value = strcpy(ov, NET_BaseAdrToString (svs.clients[e1-1].netchan.remote_address)); + else if (!strcmp(key, "realip")) + value = strcpy(ov, NET_BaseAdrToString (svs.clients[e1-1].realip)); + else if (!strcmp(key, "ping")) { + int ping = SV_CalcPing (&svs.clients[e1-1]); + sprintf(ov, "%d", ping); + value = ov; + } else if (!strcmp(key, "login")) + value = svs.clients[e1-1].login; + else + value = Info_ValueForKey (svs.clients[e1-1].userinfo, key); + } else + value = ""; + + strcpy(pr_string_temp, value); + RETURN_STRING(pr_string_temp); + PF_SetTempString(); +} + +/* +============== +PF_stof + +float(string s) stof +============== +*/ +void PF_stof (void) +{ + char *s; + + s = G_STRING(OFS_PARM0); + + G_FLOAT(OFS_RETURN) = atof(s); +} + + +/* +============== +PF_multicast + +void(vector where, float set) multicast +============== +*/ +void PF_multicast (void) +{ + float *o; + int to; + + o = G_VECTOR(OFS_PARM0); + to = G_FLOAT(OFS_PARM1); + + SV_Multicast (o, to); +} + + +void PF_Fixme (void) +{ + PR_RunError ("unimplemented bulitin"); +} + + + +builtin_t pr_builtin[] = +{ + PF_Fixme, +PF_makevectors, // void(entity e) makevectors = #1; +PF_setorigin, // void(entity e, vector o) setorigin = #2; +PF_setmodel, // void(entity e, string m) setmodel = #3; +PF_setsize, // void(entity e, vector min, vector max) setsize = #4; +PF_Fixme, // void(entity e, vector min, vector max) setabssize = #5; +PF_break, // void() break = #6; +PF_random, // float() random = #7; +PF_sound, // void(entity e, float chan, string samp) sound = #8; +PF_normalize, // vector(vector v) normalize = #9; +PF_error, // void(string e) error = #10; +PF_objerror, // void(string e) objerror = #11; +PF_vlen, // float(vector v) vlen = #12; +PF_vectoyaw, // float(vector v) vectoyaw = #13; +PF_Spawn, // entity() spawn = #14; +PF_Remove, // void(entity e) remove = #15; +PF_traceline, // float(vector v1, vector v2, float tryents) traceline = #16; +PF_checkclient, // entity() clientlist = #17; +PF_Find, // entity(entity start, .string fld, string match) find = #18; +PF_precache_sound, // void(string s) precache_sound = #19; +PF_precache_model, // void(string s) precache_model = #20; +PF_stuffcmd, // void(entity client, string s)stuffcmd = #21; +PF_findradius, // entity(vector org, float rad) findradius = #22; +PF_bprint, // void(string s) bprint = #23; +PF_sprint, // void(entity client, string s) sprint = #24; +PF_dprint, // void(string s) dprint = #25; +PF_ftos, // void(string s) ftos = #26; +PF_vtos, // void(string s) vtos = #27; +PF_coredump, +PF_traceon, +PF_traceoff, +PF_eprint, // void(entity e) debug print an entire entity +PF_walkmove, // float(float yaw, float dist) walkmove +PF_Fixme, // float(float yaw, float dist) walkmove +PF_droptofloor, +PF_lightstyle, +PF_rint, +PF_floor, +PF_ceil, +PF_Fixme, +PF_checkbottom, +PF_pointcontents, +PF_Fixme, +PF_fabs, +PF_aim, +PF_cvar, +PF_localcmd, +PF_nextent, +PF_Fixme, +PF_changeyaw, +PF_Fixme, +PF_vectoangles, + +PF_WriteByte, +PF_WriteChar, +PF_WriteShort, +PF_WriteLong, +PF_WriteCoord, +PF_WriteAngle, +PF_WriteString, +PF_WriteEntity, + +PF_Fixme, +PF_Fixme, +PF_Fixme, +PF_Fixme, +PF_Fixme, +PF_Fixme, +PF_Fixme, + +SV_MoveToGoal, +PF_precache_file, +PF_makestatic, + +PF_changelevel, +PF_Fixme, + +PF_cvar_set, +PF_centerprint, + +PF_ambientsound, + +PF_precache_model, +PF_precache_sound, // precache_sound2 is different only for qcc +PF_precache_file, + +PF_setspawnparms, + +PF_logfrag, + +PF_infokey, +PF_stof, +PF_multicast, +PF_executecmd, // #83 +PF_tokanize, +PF_argc, +PF_argv, +PF_teamfield, +PF_substr, +PF_strcat, +PF_strlen, +PF_str2byte, +PF_str2short, +PF_newstr, +PF_freestr, +PF_conprint, +PF_readcmd, +PF_strcpy, +PF_strstr, +PF_strncpy, +PF_log, +PF_redirectcmd, +PF_calltimeofday, //102 +}; + +builtin_t *pr_builtins = pr_builtin; +int pr_numbuiltins = sizeof(pr_builtin)/sizeof(pr_builtin[0]); + diff --git a/source/pr_comp.h b/source/pr_comp.h index fb092516..b237ed5f 100644 --- a/source/pr_comp.h +++ b/source/pr_comp.h @@ -1,180 +1,180 @@ -/* -Copyright (C) 1996-1997 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. - -*/ - -// this file is shared by quake and qcc - -typedef int func_t; -typedef int string_t; - -typedef enum {ev_void, ev_string, ev_float, ev_vector, ev_entity, ev_field, ev_function, ev_pointer} etype_t; - - -#define OFS_NULL 0 -#define OFS_RETURN 1 -#define OFS_PARM0 4 // leave 3 ofs for each parm to hold vectors -#define OFS_PARM1 7 -#define OFS_PARM2 10 -#define OFS_PARM3 13 -#define OFS_PARM4 16 -#define OFS_PARM5 19 -#define OFS_PARM6 22 -#define OFS_PARM7 25 -#define RESERVED_OFS 28 - - -enum { - OP_DONE, - OP_MUL_F, - OP_MUL_V, - OP_MUL_FV, - OP_MUL_VF, - OP_DIV_F, - OP_ADD_F, - OP_ADD_V, - OP_SUB_F, - OP_SUB_V, - - OP_EQ_F, - OP_EQ_V, - OP_EQ_S, - OP_EQ_E, - OP_EQ_FNC, - - OP_NE_F, - OP_NE_V, - OP_NE_S, - OP_NE_E, - OP_NE_FNC, - - OP_LE, - OP_GE, - OP_LT, - OP_GT, - - OP_LOAD_F, - OP_LOAD_V, - OP_LOAD_S, - OP_LOAD_ENT, - OP_LOAD_FLD, - OP_LOAD_FNC, - - OP_ADDRESS, - - OP_STORE_F, - OP_STORE_V, - OP_STORE_S, - OP_STORE_ENT, - OP_STORE_FLD, - OP_STORE_FNC, - - OP_STOREP_F, - OP_STOREP_V, - OP_STOREP_S, - OP_STOREP_ENT, - OP_STOREP_FLD, - OP_STOREP_FNC, - - OP_RETURN, - OP_NOT_F, - OP_NOT_V, - OP_NOT_S, - OP_NOT_ENT, - OP_NOT_FNC, - OP_IF, - OP_IFNOT, - OP_CALL0, - OP_CALL1, - OP_CALL2, - OP_CALL3, - OP_CALL4, - OP_CALL5, - OP_CALL6, - OP_CALL7, - OP_CALL8, - OP_STATE, - OP_GOTO, - OP_AND, - OP_OR, - - OP_BITAND, - OP_BITOR -}; - - -typedef struct statement_s -{ - unsigned short op; - short a,b,c; -} dstatement_t; - -typedef struct -{ - unsigned short type; // if DEF_SAVEGLOBGAL bit is set - // the variable needs to be saved in savegames - unsigned short ofs; - int s_name; -} ddef_t; -#define DEF_SAVEGLOBAL (1<<15) - -#define MAX_PARMS 8 - -typedef struct -{ - int first_statement; // negative numbers are builtins - int parm_start; - int locals; // total ints of parms + locals - - int profile; // runtime - - int s_name; - int s_file; // source file defined in - - int numparms; - byte parm_size[MAX_PARMS]; -} dfunction_t; - - -#define PROG_VERSION 6 -typedef struct -{ - int version; - int crc; // check of header file - - int ofs_statements; - int numstatements; // statement 0 is an error - - int ofs_globaldefs; - int numglobaldefs; - - int ofs_fielddefs; - int numfielddefs; - - int ofs_functions; - int numfunctions; // function 0 is an empty - - int ofs_strings; - int numstrings; // first string is a null string - - int ofs_globals; - int numglobals; - - int entityfields; -} dprograms_t; - +/* +Copyright (C) 1996-1997 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. + +*/ + +// this file is shared by quake and qcc + +typedef int func_t; +typedef int string_t; + +typedef enum {ev_void, ev_string, ev_float, ev_vector, ev_entity, ev_field, ev_function, ev_pointer} etype_t; + + +#define OFS_NULL 0 +#define OFS_RETURN 1 +#define OFS_PARM0 4 // leave 3 ofs for each parm to hold vectors +#define OFS_PARM1 7 +#define OFS_PARM2 10 +#define OFS_PARM3 13 +#define OFS_PARM4 16 +#define OFS_PARM5 19 +#define OFS_PARM6 22 +#define OFS_PARM7 25 +#define RESERVED_OFS 28 + + +enum { + OP_DONE, + OP_MUL_F, + OP_MUL_V, + OP_MUL_FV, + OP_MUL_VF, + OP_DIV_F, + OP_ADD_F, + OP_ADD_V, + OP_SUB_F, + OP_SUB_V, + + OP_EQ_F, + OP_EQ_V, + OP_EQ_S, + OP_EQ_E, + OP_EQ_FNC, + + OP_NE_F, + OP_NE_V, + OP_NE_S, + OP_NE_E, + OP_NE_FNC, + + OP_LE, + OP_GE, + OP_LT, + OP_GT, + + OP_LOAD_F, + OP_LOAD_V, + OP_LOAD_S, + OP_LOAD_ENT, + OP_LOAD_FLD, + OP_LOAD_FNC, + + OP_ADDRESS, + + OP_STORE_F, + OP_STORE_V, + OP_STORE_S, + OP_STORE_ENT, + OP_STORE_FLD, + OP_STORE_FNC, + + OP_STOREP_F, + OP_STOREP_V, + OP_STOREP_S, + OP_STOREP_ENT, + OP_STOREP_FLD, + OP_STOREP_FNC, + + OP_RETURN, + OP_NOT_F, + OP_NOT_V, + OP_NOT_S, + OP_NOT_ENT, + OP_NOT_FNC, + OP_IF, + OP_IFNOT, + OP_CALL0, + OP_CALL1, + OP_CALL2, + OP_CALL3, + OP_CALL4, + OP_CALL5, + OP_CALL6, + OP_CALL7, + OP_CALL8, + OP_STATE, + OP_GOTO, + OP_AND, + OP_OR, + + OP_BITAND, + OP_BITOR +}; + + +typedef struct statement_s +{ + unsigned short op; + short a,b,c; +} dstatement_t; + +typedef struct +{ + unsigned short type; // if DEF_SAVEGLOBGAL bit is set + // the variable needs to be saved in savegames + unsigned short ofs; + int s_name; +} ddef_t; +#define DEF_SAVEGLOBAL (1<<15) + +#define MAX_PARMS 8 + +typedef struct +{ + int first_statement; // negative numbers are builtins + int parm_start; + int locals; // total ints of parms + locals + + int profile; // runtime + + int s_name; + int s_file; // source file defined in + + int numparms; + byte parm_size[MAX_PARMS]; +} dfunction_t; + + +#define PROG_VERSION 6 +typedef struct +{ + int version; + int crc; // check of header file + + int ofs_statements; + int numstatements; // statement 0 is an error + + int ofs_globaldefs; + int numglobaldefs; + + int ofs_fielddefs; + int numfielddefs; + + int ofs_functions; + int numfunctions; // function 0 is an empty + + int ofs_strings; + int numstrings; // first string is a null string + + int ofs_globals; + int numglobals; + + int entityfields; +} dprograms_t; + diff --git a/source/pr_edict.c b/source/pr_edict.c index a51f9886..c0af5cf5 100644 --- a/source/pr_edict.c +++ b/source/pr_edict.c @@ -1,1100 +1,1149 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// sv_edict.c -- entity dictionary - -#include "qwsvdef.h" - -dprograms_t *progs; -dfunction_t *pr_functions; -char *pr_strings; -ddef_t *pr_fielddefs; -ddef_t *pr_globaldefs; -dstatement_t *pr_statements; -globalvars_t *pr_global_struct; -float *pr_globals; // same as pr_global_struct -int pr_edict_size; // in bytes - -int type_size[8] = {1,sizeof(void *)/4,1,3,1,1,sizeof(void *)/4,sizeof(void *)/4}; - -ddef_t *ED_FieldAtOfs (int ofs); -qboolean ED_ParseEpair (void *base, ddef_t *key, char *s); - -#define MAX_FIELD_LEN 64 -#define GEFV_CACHESIZE 2 - -typedef struct { - ddef_t *pcache; - char field[MAX_FIELD_LEN]; -} gefv_cache; - -static gefv_cache gefvCache[GEFV_CACHESIZE] = {{NULL, ""}, {NULL, ""}}; - -func_t SpectatorConnect; -func_t SpectatorThink; -func_t SpectatorDisconnect; -func_t ChatMessage; - - -/* -================= -ED_ClearEdict - -Sets everything to NULL -================= -*/ -void ED_ClearEdict (edict_t *e) -{ - memset (&e->v, 0, progs->entityfields * 4); - e->free = false; -} - -/* -================= -ED_Alloc - -Either finds a free edict, or allocates a new one. -Try to avoid reusing an entity that was recently freed, because it -can cause the client to think the entity morphed into something else -instead of being removed and recreated, which can cause interpolated -angles and bad trails. -================= -*/ -edict_t *ED_Alloc (void) -{ - int i; - edict_t *e; - - for ( i=MAX_CLIENTS+1 ; ifree && ( e->freetime < 2 || sv.time - e->freetime > 0.5 ) ) - { - ED_ClearEdict (e); - return e; - } - } - - if (i == MAX_EDICTS) - { - Con_Printf ("WARNING: ED_Alloc: no free edicts\n"); - i--; // step on whatever is the last edict - e = EDICT_NUM(i); - SV_UnlinkEdict(e); - } - else - sv.num_edicts++; - e = EDICT_NUM(i); - ED_ClearEdict (e); - - return e; -} - -/* -================= -ED_Free - -Marks the edict as free -FIXME: walk all entities and NULL out references to this entity -================= -*/ -void ED_Free (edict_t *ed) -{ - SV_UnlinkEdict (ed); // unlink from world bsp - - ed->free = true; - ed->v.model = 0; - ed->v.takedamage = 0; - ed->v.modelindex = 0; - ed->v.colormap = 0; - ed->v.skin = 0; - ed->v.frame = 0; - VectorCopy (vec3_origin, ed->v.origin); - VectorCopy (vec3_origin, ed->v.angles); - ed->v.nextthink = -1; - ed->v.solid = 0; - - ed->freetime = sv.time; -} - -//=========================================================================== - -/* -============ -ED_GlobalAtOfs -============ -*/ -ddef_t *ED_GlobalAtOfs (int ofs) -{ - ddef_t *def; - int i; - - for (i=0 ; inumglobaldefs ; i++) - { - def = &pr_globaldefs[i]; - if (def->ofs == ofs) - return def; - } - return NULL; -} - -/* -============ -ED_FieldAtOfs -============ -*/ -ddef_t *ED_FieldAtOfs (int ofs) -{ - ddef_t *def; - int i; - - for (i=0 ; inumfielddefs ; i++) - { - def = &pr_fielddefs[i]; - if (def->ofs == ofs) - return def; - } - return NULL; -} - -/* -============ -ED_FindField -============ -*/ -ddef_t *ED_FindField (char *name) -{ - ddef_t *def; - int i; - - for (i=0 ; inumfielddefs ; i++) - { - def = &pr_fielddefs[i]; - if (!strcmp(PR_GetString(def->s_name),name) ) - return def; - } - return NULL; -} - - -/* -============ -ED_FindGlobal -============ -*/ -ddef_t *ED_FindGlobal (char *name) -{ - ddef_t *def; - int i; - - for (i=0 ; inumglobaldefs ; i++) - { - def = &pr_globaldefs[i]; - if (!strcmp(PR_GetString(def->s_name),name) ) - return def; - } - return NULL; -} - - -/* -============ -ED_FindFunction -============ -*/ -dfunction_t *ED_FindFunction (char *name) -{ - dfunction_t *func; - int i; - - for (i=0 ; inumfunctions ; i++) - { - func = &pr_functions[i]; - if (!strcmp(PR_GetString(func->s_name),name) ) - return func; - } - return NULL; -} - -eval_t *GetEdictFieldValue(edict_t *ed, char *field) -{ - ddef_t *def = NULL; - int i; - static int rep = 0; - - for (i=0 ; iv + def->ofs*4); -} - -/* -============ -PR_ValueString - -Returns a string describing *data in a type specific manner -============= -*/ -char *PR_ValueString (etype_t type, eval_t *val) -{ - static char line[256]; - ddef_t *def; - dfunction_t *f; - - type &= ~DEF_SAVEGLOBAL; - - switch (type) - { - case ev_string: - sprintf (line, "%s", PR_GetString(val->string)); - break; - case ev_entity: - sprintf (line, "entity %i", NUM_FOR_EDICT(PROG_TO_EDICT(val->edict)) ); - break; - case ev_function: - f = pr_functions + val->function; - sprintf (line, "%s()", PR_GetString(f->s_name)); - break; - case ev_field: - def = ED_FieldAtOfs ( val->_int ); - sprintf (line, ".%s", PR_GetString(def->s_name)); - break; - case ev_void: - sprintf (line, "void"); - break; - case ev_float: - sprintf (line, "%5.1f", val->_float); - break; - case ev_vector: - sprintf (line, "'%5.1f %5.1f %5.1f'", val->vector[0], val->vector[1], val->vector[2]); - break; - case ev_pointer: - sprintf (line, "pointer"); - break; - default: - sprintf (line, "bad type %i", type); - break; - } - - return line; -} - -/* -============ -PR_UglyValueString - -Returns a string describing *data in a type specific manner -Easier to parse than PR_ValueString -============= -*/ -char *PR_UglyValueString (etype_t type, eval_t *val) -{ - static char line[256]; - ddef_t *def; - dfunction_t *f; - - type &= ~DEF_SAVEGLOBAL; - - switch (type) - { - case ev_string: - sprintf (line, "%s", PR_GetString(val->string)); - break; - case ev_entity: - sprintf (line, "%i", NUM_FOR_EDICT(PROG_TO_EDICT(val->edict))); - break; - case ev_function: - f = pr_functions + val->function; - sprintf (line, "%s", PR_GetString(f->s_name)); - break; - case ev_field: - def = ED_FieldAtOfs ( val->_int ); - sprintf (line, "%s", PR_GetString(def->s_name)); - break; - case ev_void: - sprintf (line, "void"); - break; - case ev_float: - sprintf (line, "%f", val->_float); - break; - case ev_vector: - sprintf (line, "%f %f %f", val->vector[0], val->vector[1], val->vector[2]); - break; - default: - sprintf (line, "bad type %i", type); - break; - } - - return line; -} - -/* -============ -PR_GlobalString - -Returns a string with a description and the contents of a global, -padded to 20 field width -============ -*/ -char *PR_GlobalString (int ofs) -{ - char *s; - int i; - ddef_t *def; - void *val; - static char line[128]; - - val = (void *)&pr_globals[ofs]; - def = ED_GlobalAtOfs(ofs); - if (!def) - sprintf (line,"%i(???)", ofs); - else - { - s = PR_ValueString (def->type, val); - sprintf (line,"%i(%s)%s", ofs, PR_GetString(def->s_name), s); - } - - i = strlen(line); - for ( ; i<20 ; i++) - strcat (line," "); - strcat (line," "); - - return line; -} - -char *PR_GlobalStringNoContents (int ofs) -{ - int i; - ddef_t *def; - static char line[128]; - - def = ED_GlobalAtOfs(ofs); - if (!def) - sprintf (line,"%i(???)", ofs); - else - sprintf (line,"%i(%s)", ofs, PR_GetString(def->s_name)); - - i = strlen(line); - for ( ; i<20 ; i++) - strcat (line," "); - strcat (line," "); - - return line; -} - - -/* -============= -ED_Print - -For debugging -============= -*/ -void ED_Print (edict_t *ed) -{ - int l; - ddef_t *d; - int *v; - int i, j; - char *name; - int type; - - if (ed->free) - { - Con_Printf ("FREE\n"); - return; - } - - for (i=1 ; inumfielddefs ; i++) - { - d = &pr_fielddefs[i]; - name = PR_GetString(d->s_name); - if (name[strlen(name)-2] == '_') - continue; // skip _x, _y, _z vars - - v = (int *)((char *)&ed->v + d->ofs*4); - - // if the value is still all 0, skip the field - type = d->type & ~DEF_SAVEGLOBAL; - - for (j=0 ; jtype, (eval_t *)v)); - } -} - -/* -============= -ED_Write - -For savegames -============= -*/ -void ED_Write (FILE *f, edict_t *ed) -{ - ddef_t *d; - int *v; - int i, j; - char *name; - int type; - - fprintf (f, "{\n"); - - if (ed->free) - { - fprintf (f, "}\n"); - return; - } - - for (i=1 ; inumfielddefs ; i++) - { - d = &pr_fielddefs[i]; - name = PR_GetString(d->s_name); - if (name[strlen(name)-2] == '_') - continue; // skip _x, _y, _z vars - - v = (int *)((char *)&ed->v + d->ofs*4); - - // if the value is still all 0, skip the field - type = d->type & ~DEF_SAVEGLOBAL; - for (j=0 ; jtype, (eval_t *)v)); - } - - fprintf (f, "}\n"); -} - -void ED_PrintNum (int ent) -{ - ED_Print (EDICT_NUM(ent)); -} - -/* -============= -ED_PrintEdicts - -For debugging, prints all the entities in the current server -============= -*/ -void ED_PrintEdicts (void) -{ - int i; - - Con_Printf ("%i entities\n", sv.num_edicts); - for (i=0 ; ifree) - continue; - active++; - if (ent->v.solid) - solid++; - if (ent->v.model) - models++; - if (ent->v.movetype == MOVETYPE_STEP) - step++; - } - - Con_Printf ("num_edicts:%3i\n", sv.num_edicts); - Con_Printf ("active :%3i\n", active); - Con_Printf ("view :%3i\n", models); - Con_Printf ("touch :%3i\n", solid); - Con_Printf ("step :%3i\n", step); - -} - -/* -============================================================================== - - ARCHIVING GLOBALS - -FIXME: need to tag constants, doesn't really work -============================================================================== -*/ - -/* -============= -ED_WriteGlobals -============= -*/ -void ED_WriteGlobals (FILE *f) -{ - ddef_t *def; - int i; - char *name; - int type; - - fprintf (f,"{\n"); - for (i=0 ; inumglobaldefs ; i++) - { - def = &pr_globaldefs[i]; - type = def->type; - if ( !(def->type & DEF_SAVEGLOBAL) ) - continue; - type &= ~DEF_SAVEGLOBAL; - - if (type != ev_string - && type != ev_float - && type != ev_entity) - continue; - - name = PR_GetString(def->s_name); - fprintf (f,"\"%s\" ", name); - fprintf (f,"\"%s\"\n", PR_UglyValueString(type, (eval_t *)&pr_globals[def->ofs])); - } - fprintf (f,"}\n"); -} - -/* -============= -ED_ParseGlobals -============= -*/ -void ED_ParseGlobals (char *data) -{ - char keyname[64]; - ddef_t *key; - - while (1) - { - // parse key - data = COM_Parse (data); - if (com_token[0] == '}') - break; - if (!data) - SV_Error ("ED_ParseEntity: EOF without closing brace"); - - strcpy (keyname, com_token); - - // parse value - data = COM_Parse (data); - if (!data) - SV_Error ("ED_ParseEntity: EOF without closing brace"); - - if (com_token[0] == '}') - SV_Error ("ED_ParseEntity: closing brace without data"); - - key = ED_FindGlobal (keyname); - if (!key) - { - Con_Printf ("%s is not a global\n", keyname); - continue; - } - - if (!ED_ParseEpair ((void *)pr_globals, key, com_token)) - SV_Error ("ED_ParseGlobals: parse error"); - } -} - -//============================================================================ - - -/* -============= -ED_NewString -============= -*/ -char *ED_NewString (char *string) -{ - char *new, *new_p; - int i,l; - - l = strlen(string) + 1; - new = Hunk_Alloc (l); - new_p = new; - - for (i=0 ; i< l ; i++) - { - if (string[i] == '\\' && i < l-1) - { - i++; - if (string[i] == 'n') - *new_p++ = '\n'; - else - *new_p++ = '\\'; - } - else - *new_p++ = string[i]; - } - - return new; -} - - -/* -============= -ED_ParseEval - -Can parse either fields or globals -returns false if error -============= -*/ -qboolean ED_ParseEpair (void *base, ddef_t *key, char *s) -{ - int i; - char string[128]; - ddef_t *def; - char *v, *w; - void *d; - dfunction_t *func; - - d = (void *)((int *)base + key->ofs); - - switch (key->type & ~DEF_SAVEGLOBAL) - { - case ev_string: - *(string_t *)d = PR_SetString(ED_NewString (s)); - break; - - case ev_float: - *(float *)d = atof (s); - break; - - case ev_vector: - strcpy (string, s); - v = string; - w = string; - for (i=0 ; i<3 ; i++) - { - while (*v && *v != ' ') - v++; - *v = 0; - ((float *)d)[i] = atof (w); - w = v = v+1; - } - break; - - case ev_entity: - *(int *)d = EDICT_TO_PROG(EDICT_NUM(atoi (s))); - break; - - case ev_field: - def = ED_FindField (s); - if (!def) - { - Con_Printf ("Can't find field %s\n", s); - return false; - } - *(int *)d = G_INT(def->ofs); - break; - - case ev_function: - func = ED_FindFunction (s); - if (!func) - { - Con_Printf ("Can't find function %s\n", s); - return false; - } - *(func_t *)d = func - pr_functions; - break; - - default: - break; - } - return true; -} - -/* -==================== -ED_ParseEdict - -Parses an edict out of the given string, returning the new position -ed should be a properly initialized empty edict. -Used for initial level load and for savegames. -==================== -*/ -char *ED_ParseEdict (char *data, edict_t *ent) -{ - ddef_t *key; - qboolean anglehack; - qboolean init; - char keyname[256]; - - init = false; - -// clear it - if (ent != sv.edicts) // hack - memset (&ent->v, 0, progs->entityfields * 4); - -// go through all the dictionary pairs - while (1) - { - // parse key - data = COM_Parse (data); - if (com_token[0] == '}') - break; - if (!data) - SV_Error ("ED_ParseEntity: EOF without closing brace"); - -// anglehack is to allow QuakeEd to write single scalar angles -// and allow them to be turned into vectors. (FIXME...) -if (!strcmp(com_token, "angle")) -{ - strcpy (com_token, "angles"); - anglehack = true; -} -else - anglehack = false; - -// FIXME: change light to _light to get rid of this hack -if (!strcmp(com_token, "light")) - strcpy (com_token, "light_lev"); // hack for single light def - - strcpy (keyname, com_token); - - // parse value - data = COM_Parse (data); - if (!data) - SV_Error ("ED_ParseEntity: EOF without closing brace"); - - if (com_token[0] == '}') - SV_Error ("ED_ParseEntity: closing brace without data"); - - init = true; - -// keynames with a leading underscore are used for utility comments, -// and are immediately discarded by quake - if (keyname[0] == '_') - continue; - - key = ED_FindField (keyname); - if (!key) - { - Con_Printf ("%s is not a field\n", keyname); - continue; - } - -if (anglehack) -{ -char temp[32]; -strcpy (temp, com_token); -sprintf (com_token, "0 %s 0", temp); -} - - if (!ED_ParseEpair ((void *)&ent->v, key, com_token)) - SV_Error ("ED_ParseEdict: parse error"); - } - - if (!init) - ent->free = true; - - return data; -} - - -/* -================ -ED_LoadFromFile - -The entities are directly placed in the array, rather than allocated with -ED_Alloc, because otherwise an error loading the map would have entity -number references out of order. - -Creates a server's entity / program execution context by -parsing textual entity definitions out of an ent file. - -Used for both fresh maps and savegame loads. A fresh map would also need -to call ED_CallSpawnFunctions () to let the objects initialize themselves. -================ -*/ -void ED_LoadFromFile (char *data) -{ - edict_t *ent; - int inhibit; - dfunction_t *func; - - ent = NULL; - inhibit = 0; - pr_global_struct->time = sv.time; - -// parse ents - while (1) - { -// parse the opening brace - data = COM_Parse (data); - if (!data) - break; - if (com_token[0] != '{') - SV_Error ("ED_LoadFromFile: found %s when expecting {",com_token); - - if (!ent) - ent = EDICT_NUM(0); - else - ent = ED_Alloc (); - data = ED_ParseEdict (data, ent); - -// remove things from different skill levels or deathmatch - if (deathmatch.value) - { - if (((int)ent->v.spawnflags & SPAWNFLAG_NOT_DEATHMATCH)) - { - ED_Free (ent); - inhibit++; - continue; - } - } - else if ((current_skill == 0 && ((int)ent->v.spawnflags & SPAWNFLAG_NOT_EASY)) - || (current_skill == 1 && ((int)ent->v.spawnflags & SPAWNFLAG_NOT_MEDIUM)) - || (current_skill >= 2 && ((int)ent->v.spawnflags & SPAWNFLAG_NOT_HARD)) ) - { - ED_Free (ent); - inhibit++; - continue; - } - -// -// immediately call spawn function -// - if (!ent->v.classname) - { - Con_Printf ("No classname for:\n"); - ED_Print (ent); - ED_Free (ent); - continue; - } - - // look for the spawn function - func = ED_FindFunction ( PR_GetString(ent->v.classname) ); - - if (!func) - { - Con_Printf ("No spawn function for:\n"); - ED_Print (ent); - ED_Free (ent); - continue; - } - - pr_global_struct->self = EDICT_TO_PROG(ent); - PR_ExecuteProgram (func - pr_functions); - SV_FlushSignon(); - } - - Con_DPrintf ("%i entities inhibited\n", inhibit); -} - - -/* -=============== -PR_LoadProgs -=============== -*/ - -void PR_LoadProgs (void) -{ - int i; - char num[32]; - dfunction_t *f; - -// flush the non-C variable lookup cache - for (i=0 ; iversion != PROG_VERSION) - SV_Error ("progs.dat has wrong version number (%i should be %i)", progs->version, PROG_VERSION); - if (progs->crc != PROGHEADER_CRC) - SV_Error ("You must have the progs.dat from QuakeWorld installed"); - - pr_functions = (dfunction_t *)((byte *)progs + progs->ofs_functions); - pr_strings = (char *)progs + progs->ofs_strings; - pr_globaldefs = (ddef_t *)((byte *)progs + progs->ofs_globaldefs); - pr_fielddefs = (ddef_t *)((byte *)progs + progs->ofs_fielddefs); - pr_statements = (dstatement_t *)((byte *)progs + progs->ofs_statements); - - num_prstr = 0; - - pr_global_struct = (globalvars_t *)((byte *)progs + progs->ofs_globals); - pr_globals = (float *)pr_global_struct; - - pr_edict_size = progs->entityfields * 4 + sizeof (edict_t) - sizeof(entvars_t); - -// byte swap the lumps - for (i=0 ; inumstatements ; i++) - { - pr_statements[i].op = LittleShort(pr_statements[i].op); - pr_statements[i].a = LittleShort(pr_statements[i].a); - pr_statements[i].b = LittleShort(pr_statements[i].b); - pr_statements[i].c = LittleShort(pr_statements[i].c); - } - - for (i=0 ; inumfunctions; i++) - { - pr_functions[i].first_statement = LittleLong (pr_functions[i].first_statement); - pr_functions[i].parm_start = LittleLong (pr_functions[i].parm_start); - pr_functions[i].s_name = LittleLong (pr_functions[i].s_name); - pr_functions[i].s_file = LittleLong (pr_functions[i].s_file); - pr_functions[i].numparms = LittleLong (pr_functions[i].numparms); - pr_functions[i].locals = LittleLong (pr_functions[i].locals); - } - - for (i=0 ; inumglobaldefs ; i++) - { - pr_globaldefs[i].type = LittleShort (pr_globaldefs[i].type); - pr_globaldefs[i].ofs = LittleShort (pr_globaldefs[i].ofs); - pr_globaldefs[i].s_name = LittleLong (pr_globaldefs[i].s_name); - } - - for (i=0 ; inumfielddefs ; i++) - { - pr_fielddefs[i].type = LittleShort (pr_fielddefs[i].type); - if (pr_fielddefs[i].type & DEF_SAVEGLOBAL) - SV_Error ("PR_LoadProgs: pr_fielddefs[i].type & DEF_SAVEGLOBAL"); - pr_fielddefs[i].ofs = LittleShort (pr_fielddefs[i].ofs); - pr_fielddefs[i].s_name = LittleLong (pr_fielddefs[i].s_name); - } - - for (i=0 ; inumglobals ; i++) - ((int *)pr_globals)[i] = LittleLong (((int *)pr_globals)[i]); - - // Zoid, find the spectator functions - ChatMessage = SpectatorConnect = SpectatorThink = SpectatorDisconnect = 0; - - if ((f = ED_FindFunction ("SpectatorConnect")) != NULL) - SpectatorConnect = (func_t)(f - pr_functions); - if ((f = ED_FindFunction ("SpectatorThink")) != NULL) - SpectatorThink = (func_t)(f - pr_functions); - if ((f = ED_FindFunction ("SpectatorDisconnect")) != NULL) - SpectatorDisconnect = (func_t)(f - pr_functions); - if ((f = ED_FindFunction ("ChatMessage")) != NULL) - ChatMessage = (func_t)(f - pr_functions); -} - - -/* -=============== -PR_Init -=============== -*/ -void PR_Init (void) -{ - Cmd_AddCommand ("edict", ED_PrintEdict_f); - Cmd_AddCommand ("edicts", ED_PrintEdicts); - Cmd_AddCommand ("edictcount", ED_Count); - Cmd_AddCommand ("profile", PR_Profile_f); -} - - - -edict_t *EDICT_NUM(int n) -{ - if (n < 0 || n >= MAX_EDICTS) - SV_Error ("EDICT_NUM: bad number %i", n); - return (edict_t *)((byte *)sv.edicts+ (n)*pr_edict_size); -} - -int NUM_FOR_EDICT(edict_t *e) -{ - int b; - - b = (byte *)e - (byte *)sv.edicts; - b = b / pr_edict_size; - - if (b < 0 || b >= sv.num_edicts) - SV_Error ("NUM_FOR_EDICT: bad pointer"); - return b; -} - - +/* +Copyright (C) 1996-1997 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. + +*/ +// sv_edict.c -- entity dictionary + +#include "qwsvdef.h" + +dprograms_t *progs; +dfunction_t *pr_functions; +char *pr_strings; +ddef_t *pr_fielddefs; +ddef_t *pr_globaldefs; +dstatement_t *pr_statements; +globalvars_t *pr_global_struct; +float *pr_globals; // same as pr_global_struct +int pr_edict_size; // in bytes +int pr_teamfield = 0; // field for team storage + +int type_size[8] = {1,sizeof(void *)/4,1,3,1,1,sizeof(void *)/4,sizeof(void *)/4}; + +ddef_t *ED_FieldAtOfs (int ofs); +qboolean ED_ParseEpair (void *base, ddef_t *key, char *s); + +#define MAX_FIELD_LEN 64 +#define GEFV_CACHESIZE 2 + +typedef struct { + ddef_t *pcache; + char field[MAX_FIELD_LEN]; +} gefv_cache; + +static gefv_cache gefvCache[GEFV_CACHESIZE] = {{NULL, ""}, {NULL, ""}}; + +func_t SpectatorConnect; +func_t SpectatorThink; +func_t SpectatorDisconnect; +func_t ChatMessage; +func_t UserInfo_Changed; +func_t mod_ConsoleCmd; +func_t mod_UserCmd; + + +/* +================= +ED_ClearEdict + +Sets everything to NULL +================= +*/ +void ED_ClearEdict (edict_t *e) +{ + memset (&e->v, 0, progs->entityfields * 4); + e->free = false; +} + +/* +================= +ED_Alloc + +Either finds a free edict, or allocates a new one. +Try to avoid reusing an entity that was recently freed, because it +can cause the client to think the entity morphed into something else +instead of being removed and recreated, which can cause interpolated +angles and bad trails. +================= +*/ +edict_t *ED_Alloc (void) +{ + int i; + edict_t *e; + + for ( i=MAX_CLIENTS+1 ; ifree && ( e->freetime < 2 || sv.time - e->freetime > 0.5 ) ) + { + ED_ClearEdict (e); + return e; + } + } + + if (i == MAX_EDICTS) + { + Con_Printf ("WARNING: ED_Alloc: no free edicts\n"); + i--; // step on whatever is the last edict + e = EDICT_NUM(i); + SV_UnlinkEdict(e); + } + else + sv.num_edicts++; + e = EDICT_NUM(i); + ED_ClearEdict (e); + + return e; +} + +/* +================= +ED_Free + +Marks the edict as free +FIXME: walk all entities and NULL out references to this entity +================= +*/ +void ED_Free (edict_t *ed) +{ + SV_UnlinkEdict (ed); // unlink from world bsp + + ed->free = true; + ed->v.model = 0; + ed->v.takedamage = 0; + ed->v.modelindex = 0; + ed->v.colormap = 0; + ed->v.skin = 0; + ed->v.frame = 0; + ed->v.health = 0; + ed->v.classname = 0; + VectorCopy (vec3_origin, ed->v.origin); + VectorCopy (vec3_origin, ed->v.angles); + ed->v.nextthink = -1; + ed->v.solid = 0; + + ed->freetime = sv.time; +} + +//=========================================================================== + +/* +============ +ED_GlobalAtOfs +============ +*/ +ddef_t *ED_GlobalAtOfs (int ofs) +{ + ddef_t *def; + int i; + + for (i=0 ; inumglobaldefs ; i++) + { + def = &pr_globaldefs[i]; + if (def->ofs == ofs) + return def; + } + return NULL; +} + +/* +============ +ED_FieldAtOfs +============ +*/ +ddef_t *ED_FieldAtOfs (int ofs) +{ + ddef_t *def; + int i; + + for (i=0 ; inumfielddefs ; i++) + { + def = &pr_fielddefs[i]; + if (def->ofs == ofs) + return def; + } + return NULL; +} + +/* +============ +ED_FindField +============ +*/ +ddef_t *ED_FindField (char *name) +{ + ddef_t *def; + int i; + + for (i=0 ; inumfielddefs ; i++) + { + def = &pr_fielddefs[i]; + if (!strcmp(PR_GetString(def->s_name),name) ) + return def; + } + return NULL; +} + + +/* +============ +ED_FindGlobal +============ +*/ +ddef_t *ED_FindGlobal (char *name) +{ + ddef_t *def; + int i; + + for (i=0 ; inumglobaldefs ; i++) + { + def = &pr_globaldefs[i]; + if (!strcmp(PR_GetString(def->s_name),name) ) + return def; + } + return NULL; +} + + +/* +============ +ED_FindFunction +============ +*/ +dfunction_t *ED_FindFunction (char *name) +{ + dfunction_t *func; + int i; + + for (i=0 ; inumfunctions ; i++) + { + func = &pr_functions[i]; + if (!strcmp(PR_GetString(func->s_name),name) ) + return func; + } + return NULL; +} + +eval_t *GetEdictFieldValue(edict_t *ed, char *field) +{ + ddef_t *def = NULL; + int i; + static int rep = 0; + + for (i=0 ; iv + def->ofs*4); +} + +/* +============ +PR_ValueString + +Returns a string describing *data in a type specific manner +============= +*/ +char *PR_ValueString (etype_t type, eval_t *val) +{ + static char line[256]; + ddef_t *def; + dfunction_t *f; + + type &= ~DEF_SAVEGLOBAL; + + switch (type) + { + case ev_string: + sprintf (line, "%s", PR_GetString(val->string)); + break; + case ev_entity: + sprintf (line, "entity %i", NUM_FOR_EDICT(PROG_TO_EDICT(val->edict)) ); + break; + case ev_function: + f = pr_functions + val->function; + sprintf (line, "%s()", PR_GetString(f->s_name)); + break; + case ev_field: + def = ED_FieldAtOfs ( val->_int ); + sprintf (line, ".%s", PR_GetString(def->s_name)); + break; + case ev_void: + sprintf (line, "void"); + break; + case ev_float: + sprintf (line, "%5.1f", val->_float); + break; + case ev_vector: + sprintf (line, "'%5.1f %5.1f %5.1f'", val->vector[0], val->vector[1], val->vector[2]); + break; + case ev_pointer: + sprintf (line, "pointer"); + break; + default: + sprintf (line, "bad type %i", type); + break; + } + + return line; +} + +/* +============ +PR_UglyValueString + +Returns a string describing *data in a type specific manner +Easier to parse than PR_ValueString +============= +*/ +char *PR_UglyValueString (etype_t type, eval_t *val) +{ + static char line[256]; + ddef_t *def; + dfunction_t *f; + + type &= ~DEF_SAVEGLOBAL; + + switch (type) + { + case ev_string: + sprintf (line, "%s", PR_GetString(val->string)); + break; + case ev_entity: + sprintf (line, "%i", NUM_FOR_EDICT(PROG_TO_EDICT(val->edict))); + break; + case ev_function: + f = pr_functions + val->function; + sprintf (line, "%s", PR_GetString(f->s_name)); + break; + case ev_field: + def = ED_FieldAtOfs ( val->_int ); + sprintf (line, "%s", PR_GetString(def->s_name)); + break; + case ev_void: + sprintf (line, "void"); + break; + case ev_float: + sprintf (line, "%f", val->_float); + break; + case ev_vector: + sprintf (line, "%f %f %f", val->vector[0], val->vector[1], val->vector[2]); + break; + default: + sprintf (line, "bad type %i", type); + break; + } + + return line; +} + +/* +============ +PR_GlobalString + +Returns a string with a description and the contents of a global, +padded to 20 field width +============ +*/ +char *PR_GlobalString (int ofs) +{ + char *s; + int i; + ddef_t *def; + void *val; + static char line[128]; + + val = (void *)&pr_globals[ofs]; + def = ED_GlobalAtOfs(ofs); + if (!def) + sprintf (line,"%i(???)", ofs); + else + { + s = PR_ValueString (def->type, val); + sprintf (line,"%i(%s)%s", ofs, PR_GetString(def->s_name), s); + } + + i = strlen(line); + for ( ; i<20 ; i++) + strcat (line," "); + strcat (line," "); + + return line; +} + +char *PR_GlobalStringNoContents (int ofs) +{ + int i; + ddef_t *def; + static char line[128]; + + def = ED_GlobalAtOfs(ofs); + if (!def) + sprintf (line,"%i(???)", ofs); + else + sprintf (line,"%i(%s)", ofs, PR_GetString(def->s_name)); + + i = strlen(line); + for ( ; i<20 ; i++) + strcat (line," "); + strcat (line," "); + + return line; +} + + +/* +============= +ED_Print + +For debugging +============= +*/ +void ED_Print (edict_t *ed) +{ + int l; + ddef_t *d; + int *v; + int i, j; + char *name; + int type; + + if (ed->free) + { + Con_Printf ("FREE\n"); + return; + } + + for (i=1 ; inumfielddefs ; i++) + { + d = &pr_fielddefs[i]; + name = PR_GetString(d->s_name); + if (name[strlen(name)-2] == '_') + continue; // skip _x, _y, _z vars + + v = (int *)((char *)&ed->v + d->ofs*4); + + // if the value is still all 0, skip the field + type = d->type & ~DEF_SAVEGLOBAL; + + for (j=0 ; jtype, (eval_t *)v)); + } +} + +/* +============= +ED_Write + +For savegames +============= +*/ +void ED_Write (FILE *f, edict_t *ed) +{ + ddef_t *d; + int *v; + int i, j; + char *name; + int type; + + fprintf (f, "{\n"); + + if (ed->free) + { + fprintf (f, "}\n"); + return; + } + + for (i=1 ; inumfielddefs ; i++) + { + d = &pr_fielddefs[i]; + name = PR_GetString(d->s_name); + if (name[strlen(name)-2] == '_') + continue; // skip _x, _y, _z vars + + v = (int *)((char *)&ed->v + d->ofs*4); + + // if the value is still all 0, skip the field + type = d->type & ~DEF_SAVEGLOBAL; + for (j=0 ; jtype, (eval_t *)v)); + } + + fprintf (f, "}\n"); +} + +void ED_PrintNum (int ent) +{ + ED_Print (EDICT_NUM(ent)); +} + +/* +============= +ED_PrintEdicts + +For debugging, prints all the entities in the current server +============= +*/ +void ED_PrintEdicts (void) +{ + int i; + + Con_Printf ("%i entities\n", sv.num_edicts); + for (i=0 ; ifree) + continue; + active++; + if (ent->v.solid) + solid++; + if (ent->v.model) + models++; + if (ent->v.movetype == MOVETYPE_STEP) + step++; + } + + Con_Printf ("num_edicts:%3i\n", sv.num_edicts); + Con_Printf ("active :%3i\n", active); + Con_Printf ("view :%3i\n", models); + Con_Printf ("touch :%3i\n", solid); + Con_Printf ("step :%3i\n", step); + +} + +/* +============================================================================== + + ARCHIVING GLOBALS + +FIXME: need to tag constants, doesn't really work +============================================================================== +*/ + +/* +============= +ED_WriteGlobals +============= +*/ +void ED_WriteGlobals (FILE *f) +{ + ddef_t *def; + int i; + char *name; + int type; + + fprintf (f,"{\n"); + for (i=0 ; inumglobaldefs ; i++) + { + def = &pr_globaldefs[i]; + type = def->type; + if ( !(def->type & DEF_SAVEGLOBAL) ) + continue; + type &= ~DEF_SAVEGLOBAL; + + if (type != ev_string + && type != ev_float + && type != ev_entity) + continue; + + name = PR_GetString(def->s_name); + fprintf (f,"\"%s\" ", name); + fprintf (f,"\"%s\"\n", PR_UglyValueString(type, (eval_t *)&pr_globals[def->ofs])); + } + fprintf (f,"}\n"); +} + +/* +============= +ED_ParseGlobals +============= +*/ +void ED_ParseGlobals (char *data) +{ + char keyname[64]; + ddef_t *key; + + while (1) + { + // parse key + data = COM_Parse (data); + if (com_token[0] == '}') + break; + if (!data) + SV_Error ("ED_ParseEntity: EOF without closing brace"); + + strcpy (keyname, com_token); + + // parse value + data = COM_Parse (data); + if (!data) + SV_Error ("ED_ParseEntity: EOF without closing brace"); + + if (com_token[0] == '}') + SV_Error ("ED_ParseEntity: closing brace without data"); + + key = ED_FindGlobal (keyname); + if (!key) + { + Con_Printf ("%s is not a global\n", keyname); + continue; + } + + if (!ED_ParseEpair ((void *)pr_globals, key, com_token)) + SV_Error ("ED_ParseGlobals: parse error"); + } +} + +//============================================================================ + + +/* +============= +ED_NewString +============= +*/ +char *ED_NewString (char *string) +{ + char *new, *new_p; + int i,l; + + l = strlen(string) + 1; + new = Hunk_Alloc (l); + new_p = new; + + for (i=0 ; i< l ; i++) + { + if (string[i] == '\\' && i < l-1) + { + i++; + if (string[i] == 'n') + *new_p++ = '\n'; + else + *new_p++ = '\\'; + } + else + *new_p++ = string[i]; + } + + return new; +} + + +/* +============= +ED_ParseEval + +Can parse either fields or globals +returns false if error +============= +*/ +qboolean ED_ParseEpair (void *base, ddef_t *key, char *s) +{ + int i; + char string[128]; + ddef_t *def; + char *v, *w; + void *d; + dfunction_t *func; + + d = (void *)((int *)base + key->ofs); + + switch (key->type & ~DEF_SAVEGLOBAL) + { + case ev_string: + *(string_t *)d = PR_SetString(ED_NewString (s)); + break; + + case ev_float: + *(float *)d = atof (s); + break; + + case ev_vector: + strcpy (string, s); + v = string; + w = string; + for (i=0 ; i<3 ; i++) + { + while (*v && *v != ' ') + v++; + *v = 0; + ((float *)d)[i] = atof (w); + w = v = v+1; + } + break; + + case ev_entity: + *(int *)d = EDICT_TO_PROG(EDICT_NUM(atoi (s))); + break; + + case ev_field: + def = ED_FindField (s); + if (!def) + { + Con_Printf ("Can't find field %s\n", s); + return false; + } + *(int *)d = G_INT(def->ofs); + break; + + case ev_function: + func = ED_FindFunction (s); + if (!func) + { + Con_Printf ("Can't find function %s\n", s); + return false; + } + *(func_t *)d = func - pr_functions; + break; + + default: + break; + } + return true; +} + +/* +==================== +ED_ParseEdict + +Parses an edict out of the given string, returning the new position +ed should be a properly initialized empty edict. +Used for initial level load and for savegames. +==================== +*/ +char *ED_ParseEdict (char *data, edict_t *ent) +{ + ddef_t *key; + qboolean anglehack; + qboolean init; + char keyname[256]; + + init = false; + +// clear it + if (ent != sv.edicts) // hack + memset (&ent->v, 0, progs->entityfields * 4); + +// go through all the dictionary pairs + while (1) + { + // parse key + data = COM_Parse (data); + if (com_token[0] == '}') + break; + if (!data) + SV_Error ("ED_ParseEntity: EOF without closing brace"); + +// anglehack is to allow QuakeEd to write single scalar angles +// and allow them to be turned into vectors. (FIXME...) +if (!strcmp(com_token, "angle")) +{ + strcpy (com_token, "angles"); + anglehack = true; +} +else + anglehack = false; + +// FIXME: change light to _light to get rid of this hack +if (!strcmp(com_token, "light")) + strcpy (com_token, "light_lev"); // hack for single light def + + strcpy (keyname, com_token); + + // parse value + data = COM_Parse (data); + if (!data) + SV_Error ("ED_ParseEntity: EOF without closing brace"); + + if (com_token[0] == '}') + SV_Error ("ED_ParseEntity: closing brace without data"); + + init = true; + +// keynames with a leading underscore are used for utility comments, +// and are immediately discarded by quake + if (keyname[0] == '_') + continue; + + key = ED_FindField (keyname); + if (!key) + { + Con_Printf ("%s is not a field\n", keyname); + continue; + } + +if (anglehack) +{ +char temp[32]; +strcpy (temp, com_token); +sprintf (com_token, "0 %s 0", temp); +} + + if (!ED_ParseEpair ((void *)&ent->v, key, com_token)) + SV_Error ("ED_ParseEdict: parse error"); + } + + if (!init) + ent->free = true; + + return data; +} + + +/* +================ +ED_LoadFromFile + +The entities are directly placed in the array, rather than allocated with +ED_Alloc, because otherwise an error loading the map would have entity +number references out of order. + +Creates a server's entity / program execution context by +parsing textual entity definitions out of an ent file. + +Used for both fresh maps and savegame loads. A fresh map would also need +to call ED_CallSpawnFunctions () to let the objects initialize themselves. +================ +*/ +void ED_LoadFromFile (char *data) +{ + edict_t *ent; + int inhibit; + dfunction_t *func; + + ent = NULL; + inhibit = 0; + pr_global_struct->time = sv.time; + +// parse ents + while (1) + { +// parse the opening brace + data = COM_Parse (data); + if (!data) + break; + if (com_token[0] != '{') + SV_Error ("ED_LoadFromFile: found %s when expecting {",com_token); + + if (!ent) + ent = EDICT_NUM(0); + else + ent = ED_Alloc (); + data = ED_ParseEdict (data, ent); + +// remove things from different skill levels or deathmatch + if (deathmatch.value) + { + if (((int)ent->v.spawnflags & SPAWNFLAG_NOT_DEATHMATCH)) + { + ED_Free (ent); + inhibit++; + continue; + } + } + else if ((current_skill == 0 && ((int)ent->v.spawnflags & SPAWNFLAG_NOT_EASY)) + || (current_skill == 1 && ((int)ent->v.spawnflags & SPAWNFLAG_NOT_MEDIUM)) + || (current_skill >= 2 && ((int)ent->v.spawnflags & SPAWNFLAG_NOT_HARD)) ) + { + ED_Free (ent); + inhibit++; + continue; + } + +// +// immediately call spawn function +// + if (!ent->v.classname) + { + Con_Printf ("No classname for:\n"); + ED_Print (ent); + ED_Free (ent); + continue; + } + + // look for the spawn function + func = ED_FindFunction ( PR_GetString(ent->v.classname) ); + + if (!func) + { + Con_Printf ("No spawn function for:\n"); + ED_Print (ent); + ED_Free (ent); + continue; + } + + pr_global_struct->self = EDICT_TO_PROG(ent); + PR_ExecuteProgram (func - pr_functions); + SV_FlushSignon(); + } + + Con_DPrintf ("%i entities inhibited\n", inhibit); +} +extern redirect_t sv_redirected; +qboolean PR_ConsoleCmd(void) +{ + if (mod_ConsoleCmd) + { + if (sv_redirected != RD_MOD) { + pr_global_struct->time = sv.time; + pr_global_struct->self = 0; + } + PR_ExecuteProgram (mod_ConsoleCmd); + return (int) G_FLOAT(OFS_RETURN); + } + + return false; +} + +qboolean PR_UserCmd(void) +{ + if (mod_UserCmd) + { + pr_global_struct->time = sv.time; + pr_global_struct->self = EDICT_TO_PROG(sv_player); + + PR_ExecuteProgram (mod_UserCmd); + return (int) G_FLOAT(OFS_RETURN); + } + + return false; +} + + +/* +=============== +PR_LoadProgs +=============== +*/ +void PF_clear_strtbl(void); + +void PR_LoadProgs (void) +{ + int i; + char num[32]; + dfunction_t *f; + +// flush the non-C variable lookup cache + for (i=0 ; iversion != PROG_VERSION) + SV_Error ("progs.dat has wrong version number (%i should be %i)", progs->version, PROG_VERSION); + if (progs->crc != PROGHEADER_CRC) + SV_Error ("You must have the progs.dat from QuakeWorld installed"); + + pr_functions = (dfunction_t *)((byte *)progs + progs->ofs_functions); + pr_strings = (char *)progs + progs->ofs_strings; + pr_globaldefs = (ddef_t *)((byte *)progs + progs->ofs_globaldefs); + pr_fielddefs = (ddef_t *)((byte *)progs + progs->ofs_fielddefs); + pr_statements = (dstatement_t *)((byte *)progs + progs->ofs_statements); + + num_prstr = 0; + + pr_global_struct = (globalvars_t *)((byte *)progs + progs->ofs_globals); + pr_globals = (float *)pr_global_struct; + + pr_edict_size = progs->entityfields * 4 + sizeof (edict_t) - sizeof(entvars_t); + +// byte swap the lumps + for (i=0 ; inumstatements ; i++) + { + pr_statements[i].op = LittleShort(pr_statements[i].op); + pr_statements[i].a = LittleShort(pr_statements[i].a); + pr_statements[i].b = LittleShort(pr_statements[i].b); + pr_statements[i].c = LittleShort(pr_statements[i].c); + } + + for (i=0 ; inumfunctions; i++) + { + pr_functions[i].first_statement = LittleLong (pr_functions[i].first_statement); + pr_functions[i].parm_start = LittleLong (pr_functions[i].parm_start); + pr_functions[i].s_name = LittleLong (pr_functions[i].s_name); + pr_functions[i].s_file = LittleLong (pr_functions[i].s_file); + pr_functions[i].numparms = LittleLong (pr_functions[i].numparms); + pr_functions[i].locals = LittleLong (pr_functions[i].locals); + } + + for (i=0 ; inumglobaldefs ; i++) + { + pr_globaldefs[i].type = LittleShort (pr_globaldefs[i].type); + pr_globaldefs[i].ofs = LittleShort (pr_globaldefs[i].ofs); + pr_globaldefs[i].s_name = LittleLong (pr_globaldefs[i].s_name); + } + + for (i=0 ; inumfielddefs ; i++) + { + pr_fielddefs[i].type = LittleShort (pr_fielddefs[i].type); + if (pr_fielddefs[i].type & DEF_SAVEGLOBAL) + SV_Error ("PR_LoadProgs: pr_fielddefs[i].type & DEF_SAVEGLOBAL"); + pr_fielddefs[i].ofs = LittleShort (pr_fielddefs[i].ofs); + pr_fielddefs[i].s_name = LittleLong (pr_fielddefs[i].s_name); + } + + for (i=0 ; inumglobals ; i++) + ((int *)pr_globals)[i] = LittleLong (((int *)pr_globals)[i]); + + // Zoid, find the spectator functions + mod_UserCmd = mod_ConsoleCmd = UserInfo_Changed = ChatMessage = SpectatorConnect = SpectatorThink = SpectatorDisconnect = 0; + + if ((f = ED_FindFunction ("SpectatorConnect")) != NULL) + SpectatorConnect = (func_t)(f - pr_functions); + if ((f = ED_FindFunction ("SpectatorThink")) != NULL) + SpectatorThink = (func_t)(f - pr_functions); + if ((f = ED_FindFunction ("SpectatorDisconnect")) != NULL) + SpectatorDisconnect = (func_t)(f - pr_functions); + if ((f = ED_FindFunction ("ChatMessage")) != NULL) + ChatMessage = (func_t)(f - pr_functions); + if ((f = ED_FindFunction ("UserInfo_Changed")) != NULL) + UserInfo_Changed = (func_t)(f - pr_functions); + if ((f = ED_FindFunction ("ConsoleCmd")) != NULL) + mod_ConsoleCmd = (func_t)(f - pr_functions); + if ((f = ED_FindFunction ("UserCmd")) != NULL) + mod_UserCmd = (func_t)(f - pr_functions); +} + + +/* +=============== +PR_Init +=============== +*/ +void PR_CleanLogText_Init(); +void PR_Init (void) +{ + Cmd_AddCommand ("edict", ED_PrintEdict_f); + Cmd_AddCommand ("edicts", ED_PrintEdicts); + Cmd_AddCommand ("edictcount", ED_Count); + Cmd_AddCommand ("profile", PR_Profile_f); + + memset(pr_newstrtbl, 0, sizeof(pr_newstrtbl)); + PR_CleanLogText_Init(); +} + + + +edict_t *EDICT_NUM(int n) +{ + if (n < 0 || n >= MAX_EDICTS) + SV_Error ("EDICT_NUM: bad number %i", n); + return (edict_t *)((byte *)sv.edicts+ (n)*pr_edict_size); +} + +int NUM_FOR_EDICT(edict_t *e) +{ + int b; + + b = (byte *)e - (byte *)sv.edicts; + b = b / pr_edict_size; + + if (b < 0 || b >= sv.num_edicts) + SV_Error ("NUM_FOR_EDICT: bad pointer"); + return b; +} + + diff --git a/source/pr_exec.c b/source/pr_exec.c index 341cd62e..f1c0cdcf 100644 --- a/source/pr_exec.c +++ b/source/pr_exec.c @@ -1,699 +1,728 @@ -/* -Copyright (C) 1996-1997 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 "qwsvdef.h" - - -/* - -*/ - -typedef struct -{ - int s; - dfunction_t *f; -} prstack_t; - -#define MAX_STACK_DEPTH 32 -prstack_t pr_stack[MAX_STACK_DEPTH]; -int pr_depth; - -#define LOCALSTACK_SIZE 2048 -int localstack[LOCALSTACK_SIZE]; -int localstack_used; - - -qboolean pr_trace; -dfunction_t *pr_xfunction; -int pr_xstatement; - - -int pr_argc; - -char *pr_opnames[] = -{ -"DONE", - -"MUL_F", -"MUL_V", -"MUL_FV", -"MUL_VF", - -"DIV", - -"ADD_F", -"ADD_V", - -"SUB_F", -"SUB_V", - -"EQ_F", -"EQ_V", -"EQ_S", -"EQ_E", -"EQ_FNC", - -"NE_F", -"NE_V", -"NE_S", -"NE_E", -"NE_FNC", - -"LE", -"GE", -"LT", -"GT", - -"INDIRECT", -"INDIRECT", -"INDIRECT", -"INDIRECT", -"INDIRECT", -"INDIRECT", - -"ADDRESS", - -"STORE_F", -"STORE_V", -"STORE_S", -"STORE_ENT", -"STORE_FLD", -"STORE_FNC", - -"STOREP_F", -"STOREP_V", -"STOREP_S", -"STOREP_ENT", -"STOREP_FLD", -"STOREP_FNC", - -"RETURN", - -"NOT_F", -"NOT_V", -"NOT_S", -"NOT_ENT", -"NOT_FNC", - -"IF", -"IFNOT", - -"CALL0", -"CALL1", -"CALL2", -"CALL3", -"CALL4", -"CALL5", -"CALL6", -"CALL7", -"CALL8", - -"STATE", - -"GOTO", - -"AND", -"OR", - -"BITAND", -"BITOR" -}; - -char *PR_GlobalString (int ofs); -char *PR_GlobalStringNoContents (int ofs); - - -//============================================================================= - -/* -================= -PR_PrintStatement -================= -*/ -void PR_PrintStatement (dstatement_t *s) -{ - int i; - - if ( (unsigned)s->op < sizeof(pr_opnames)/sizeof(pr_opnames[0])) - { - Con_Printf ("%s ", pr_opnames[s->op]); - i = strlen(pr_opnames[s->op]); - for ( ; i<10 ; i++) - Con_Printf (" "); - } - - if (s->op == OP_IF || s->op == OP_IFNOT) - Con_Printf ("%sbranch %i",PR_GlobalString(s->a),s->b); - else if (s->op == OP_GOTO) - { - Con_Printf ("branch %i",s->a); - } - else if ( (unsigned)(s->op - OP_STORE_F) < 6) - { - Con_Printf ("%s",PR_GlobalString(s->a)); - Con_Printf ("%s", PR_GlobalStringNoContents(s->b)); - } - else - { - if (s->a) - Con_Printf ("%s",PR_GlobalString(s->a)); - if (s->b) - Con_Printf ("%s",PR_GlobalString(s->b)); - if (s->c) - Con_Printf ("%s", PR_GlobalStringNoContents(s->c)); - } - Con_Printf ("\n"); -} - -/* -============ -PR_StackTrace -============ -*/ -void PR_StackTrace (void) -{ - dfunction_t *f; - int i; - - if (pr_depth == 0) - { - Con_Printf ("\n"); - return; - } - - pr_stack[pr_depth].f = pr_xfunction; - for (i=pr_depth ; i>=0 ; i--) - { - f = pr_stack[i].f; - - if (!f) - { - Con_Printf ("\n"); - } - else - Con_Printf ("%12s : %s\n", PR_GetString(f->s_file), PR_GetString(f->s_name)); - } -} - - -/* -============ -PR_Profile_f - -============ -*/ -void PR_Profile_f (void) -{ - dfunction_t *f, *best; - int max; - int num; - int i; - - num = 0; - do - { - max = 0; - best = NULL; - for (i=0 ; inumfunctions ; i++) - { - f = &pr_functions[i]; - if (f->profile > max) - { - max = f->profile; - best = f; - } - } - if (best) - { - if (num < 10) - Con_Printf ("%7i %s\n", best->profile, PR_GetString(best->s_name)); - num++; - best->profile = 0; - } - } while (best); -} - - -/* -============ -PR_RunError - -Aborts the currently executing function -============ -*/ -void PR_RunError (char *error, ...) -{ - va_list argptr; - char string[1024]; - - va_start (argptr,error); - vsprintf (string,error,argptr); - va_end (argptr); - - PR_PrintStatement (pr_statements + pr_xstatement); - PR_StackTrace (); - Con_Printf ("%s\n", string); - - pr_depth = 0; // dump the stack so SV_Error can shutdown functions - - SV_Error ("Program error"); -} - -/* -============================================================================ -PR_ExecuteProgram - -The interpretation main loop -============================================================================ -*/ - -/* -==================== -PR_EnterFunction - -Returns the new program statement counter -==================== -*/ -int PR_EnterFunction (dfunction_t *f) -{ - int i, j, c, o; - - pr_stack[pr_depth].s = pr_xstatement; - pr_stack[pr_depth].f = pr_xfunction; - pr_depth++; - if (pr_depth >= MAX_STACK_DEPTH) - PR_RunError ("stack overflow"); - -// save off any locals that the new function steps on - c = f->locals; - if (localstack_used + c > LOCALSTACK_SIZE) - PR_RunError ("PR_ExecuteProgram: locals stack overflow\n"); - - for (i=0 ; i < c ; i++) - localstack[localstack_used+i] = ((int *)pr_globals)[f->parm_start + i]; - localstack_used += c; - -// copy parameters - o = f->parm_start; - for (i=0 ; inumparms ; i++) - { - for (j=0 ; jparm_size[i] ; j++) - { - ((int *)pr_globals)[o] = ((int *)pr_globals)[OFS_PARM0+i*3+j]; - o++; - } - } - - pr_xfunction = f; - return f->first_statement - 1; // offset the s++ -} - -/* -==================== -PR_LeaveFunction -==================== -*/ -int PR_LeaveFunction (void) -{ - int i, c; - - if (pr_depth <= 0) - SV_Error ("prog stack underflow"); - -// restore locals from the stack - c = pr_xfunction->locals; - localstack_used -= c; - if (localstack_used < 0) - PR_RunError ("PR_ExecuteProgram: locals stack underflow\n"); - - for (i=0 ; i < c ; i++) - ((int *)pr_globals)[pr_xfunction->parm_start + i] = localstack[localstack_used+i]; - -// up stack - pr_depth--; - pr_xfunction = pr_stack[pr_depth].f; - return pr_stack[pr_depth].s; -} - - -/* -==================== -PR_ExecuteProgram -==================== -*/ -void PR_ExecuteProgram (func_t fnum) -{ - eval_t *a, *b, *c; - int s; - dstatement_t *st; - dfunction_t *f, *newf; - int runaway; - int i; - edict_t *ed; - int exitdepth; - eval_t *ptr; - - if (!fnum || fnum >= progs->numfunctions) - { - if (pr_global_struct->self) - ED_Print (PROG_TO_EDICT(pr_global_struct->self)); - SV_Error ("PR_ExecuteProgram: NULL function"); - } - - f = &pr_functions[fnum]; - - runaway = 100000; - pr_trace = false; - -// make a stack frame - exitdepth = pr_depth; - - s = PR_EnterFunction (f); - -while (1) -{ - s++; // next statement - - st = &pr_statements[s]; - a = (eval_t *)&pr_globals[st->a]; - b = (eval_t *)&pr_globals[st->b]; - c = (eval_t *)&pr_globals[st->c]; - - if (--runaway == 0) - PR_RunError ("runaway loop error"); - - pr_xfunction->profile++; - pr_xstatement = s; - - if (pr_trace) - PR_PrintStatement (st); - - switch (st->op) - { - case OP_ADD_F: - c->_float = a->_float + b->_float; - break; - case OP_ADD_V: - c->vector[0] = a->vector[0] + b->vector[0]; - c->vector[1] = a->vector[1] + b->vector[1]; - c->vector[2] = a->vector[2] + b->vector[2]; - break; - - case OP_SUB_F: - c->_float = a->_float - b->_float; - break; - case OP_SUB_V: - c->vector[0] = a->vector[0] - b->vector[0]; - c->vector[1] = a->vector[1] - b->vector[1]; - c->vector[2] = a->vector[2] - b->vector[2]; - break; - - case OP_MUL_F: - c->_float = a->_float * b->_float; - break; - case OP_MUL_V: - c->_float = a->vector[0]*b->vector[0] - + a->vector[1]*b->vector[1] - + a->vector[2]*b->vector[2]; - break; - case OP_MUL_FV: - c->vector[0] = a->_float * b->vector[0]; - c->vector[1] = a->_float * b->vector[1]; - c->vector[2] = a->_float * b->vector[2]; - break; - case OP_MUL_VF: - c->vector[0] = b->_float * a->vector[0]; - c->vector[1] = b->_float * a->vector[1]; - c->vector[2] = b->_float * a->vector[2]; - break; - - case OP_DIV_F: - c->_float = a->_float / b->_float; - break; - - case OP_BITAND: - c->_float = (int)a->_float & (int)b->_float; - break; - - case OP_BITOR: - c->_float = (int)a->_float | (int)b->_float; - break; - - - case OP_GE: - c->_float = a->_float >= b->_float; - break; - case OP_LE: - c->_float = a->_float <= b->_float; - break; - case OP_GT: - c->_float = a->_float > b->_float; - break; - case OP_LT: - c->_float = a->_float < b->_float; - break; - case OP_AND: - c->_float = a->_float && b->_float; - break; - case OP_OR: - c->_float = a->_float || b->_float; - break; - - case OP_NOT_F: - c->_float = !a->_float; - break; - case OP_NOT_V: - c->_float = !a->vector[0] && !a->vector[1] && !a->vector[2]; - break; - case OP_NOT_S: - c->_float = !a->string || !*PR_GetString(a->string); - break; - case OP_NOT_FNC: - c->_float = !a->function; - break; - case OP_NOT_ENT: - c->_float = (PROG_TO_EDICT(a->edict) == sv.edicts); - break; - - case OP_EQ_F: - c->_float = a->_float == b->_float; - break; - case OP_EQ_V: - c->_float = (a->vector[0] == b->vector[0]) && - (a->vector[1] == b->vector[1]) && - (a->vector[2] == b->vector[2]); - break; - case OP_EQ_S: - c->_float = !strcmp(PR_GetString(a->string), PR_GetString(b->string)); - break; - case OP_EQ_E: - c->_float = a->_int == b->_int; - break; - case OP_EQ_FNC: - c->_float = a->function == b->function; - break; - - - case OP_NE_F: - c->_float = a->_float != b->_float; - break; - case OP_NE_V: - c->_float = (a->vector[0] != b->vector[0]) || - (a->vector[1] != b->vector[1]) || - (a->vector[2] != b->vector[2]); - break; - case OP_NE_S: - c->_float = strcmp(PR_GetString(a->string), PR_GetString(b->string)); - break; - case OP_NE_E: - c->_float = a->_int != b->_int; - break; - case OP_NE_FNC: - c->_float = a->function != b->function; - break; - -//================== - case OP_STORE_F: - case OP_STORE_ENT: - case OP_STORE_FLD: // integers - case OP_STORE_S: - case OP_STORE_FNC: // pointers - b->_int = a->_int; - break; - case OP_STORE_V: - b->vector[0] = a->vector[0]; - b->vector[1] = a->vector[1]; - b->vector[2] = a->vector[2]; - break; - - case OP_STOREP_F: - case OP_STOREP_ENT: - case OP_STOREP_FLD: // integers - case OP_STOREP_S: - case OP_STOREP_FNC: // pointers - ptr = (eval_t *)((byte *)sv.edicts + b->_int); - ptr->_int = a->_int; - break; - case OP_STOREP_V: - ptr = (eval_t *)((byte *)sv.edicts + b->_int); - ptr->vector[0] = a->vector[0]; - ptr->vector[1] = a->vector[1]; - ptr->vector[2] = a->vector[2]; - break; - - case OP_ADDRESS: - ed = PROG_TO_EDICT(a->edict); -#ifdef PARANOID - NUM_FOR_EDICT(ed); // make sure it's in range -#endif - if (ed == (edict_t *)sv.edicts && sv.state == ss_active) - PR_RunError ("assignment to world entity"); - c->_int = (byte *)((int *)&ed->v + b->_int) - (byte *)sv.edicts; - break; - - case OP_LOAD_F: - case OP_LOAD_FLD: - case OP_LOAD_ENT: - case OP_LOAD_S: - case OP_LOAD_FNC: - ed = PROG_TO_EDICT(a->edict); -#ifdef PARANOID - NUM_FOR_EDICT(ed); // make sure it's in range -#endif - a = (eval_t *)((int *)&ed->v + b->_int); - c->_int = a->_int; - break; - - case OP_LOAD_V: - ed = PROG_TO_EDICT(a->edict); -#ifdef PARANOID - NUM_FOR_EDICT(ed); // make sure it's in range -#endif - a = (eval_t *)((int *)&ed->v + b->_int); - c->vector[0] = a->vector[0]; - c->vector[1] = a->vector[1]; - c->vector[2] = a->vector[2]; - break; - -//================== - - case OP_IFNOT: - if (!a->_int) - s += st->b - 1; // offset the s++ - break; - - case OP_IF: - if (a->_int) - s += st->b - 1; // offset the s++ - break; - - case OP_GOTO: - s += st->a - 1; // offset the s++ - break; - - case OP_CALL0: - case OP_CALL1: - case OP_CALL2: - case OP_CALL3: - case OP_CALL4: - case OP_CALL5: - case OP_CALL6: - case OP_CALL7: - case OP_CALL8: - pr_argc = st->op - OP_CALL0; - if (!a->function) - PR_RunError ("NULL function"); - - newf = &pr_functions[a->function]; - - if (newf->first_statement < 0) - { // negative statements are built in functions - i = -newf->first_statement; - if (i >= pr_numbuiltins) - PR_RunError ("Bad builtin call number"); - pr_builtins[i] (); - break; - } - - s = PR_EnterFunction (newf); - break; - - case OP_DONE: - case OP_RETURN: - pr_globals[OFS_RETURN] = pr_globals[st->a]; - pr_globals[OFS_RETURN+1] = pr_globals[st->a+1]; - pr_globals[OFS_RETURN+2] = pr_globals[st->a+2]; - - s = PR_LeaveFunction (); - if (pr_depth == exitdepth) - return; // all done - break; - - case OP_STATE: - ed = PROG_TO_EDICT(pr_global_struct->self); - ed->v.nextthink = pr_global_struct->time + 0.1; - if (a->_float != ed->v.frame) - { - ed->v.frame = a->_float; - } - ed->v.think = b->function; - break; - - default: - PR_RunError ("Bad opcode %i", st->op); - } -} - -} - -/*----------------------*/ - -char *pr_strtbl[MAX_PRSTR]; -int num_prstr; - -char *PR_GetString(int num) -{ - if (num < 0) { -//Con_DPrintf("GET:%d == %s\n", num, pr_strtbl[-num]); - return pr_strtbl[-num]; - } - return pr_strings + num; -} - -int PR_SetString(char *s) -{ - int i; - - if (s - pr_strings < 0) { - for (i = 0; i <= num_prstr; i++) - if (pr_strtbl[i] == s) - break; - if (i < num_prstr) - return -i; - if (num_prstr == MAX_PRSTR - 1) - Sys_Error("MAX_PRSTR"); - num_prstr++; - pr_strtbl[num_prstr] = s; -//Con_DPrintf("SET:%d == %s\n", -num_prstr, s); - return -num_prstr; - } - return (int)(s - pr_strings); -} - +/* +Copyright (C) 1996-1997 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 "qwsvdef.h" + + +/* + +*/ + +typedef struct +{ + int s; + dfunction_t *f; +} prstack_t; + +#define MAX_STACK_DEPTH 32 +prstack_t pr_stack[MAX_STACK_DEPTH]; +int pr_depth; + +#define LOCALSTACK_SIZE 2048 +int localstack[LOCALSTACK_SIZE]; +int localstack_used; + + +qboolean pr_trace; +dfunction_t *pr_xfunction; +int pr_xstatement; + + +int pr_argc; + +char *pr_opnames[] = +{ +"DONE", + +"MUL_F", +"MUL_V", +"MUL_FV", +"MUL_VF", + +"DIV", + +"ADD_F", +"ADD_V", + +"SUB_F", +"SUB_V", + +"EQ_F", +"EQ_V", +"EQ_S", +"EQ_E", +"EQ_FNC", + +"NE_F", +"NE_V", +"NE_S", +"NE_E", +"NE_FNC", + +"LE", +"GE", +"LT", +"GT", + +"INDIRECT", +"INDIRECT", +"INDIRECT", +"INDIRECT", +"INDIRECT", +"INDIRECT", + +"ADDRESS", + +"STORE_F", +"STORE_V", +"STORE_S", +"STORE_ENT", +"STORE_FLD", +"STORE_FNC", + +"STOREP_F", +"STOREP_V", +"STOREP_S", +"STOREP_ENT", +"STOREP_FLD", +"STOREP_FNC", + +"RETURN", + +"NOT_F", +"NOT_V", +"NOT_S", +"NOT_ENT", +"NOT_FNC", + +"IF", +"IFNOT", + +"CALL0", +"CALL1", +"CALL2", +"CALL3", +"CALL4", +"CALL5", +"CALL6", +"CALL7", +"CALL8", + +"STATE", + +"GOTO", + +"AND", +"OR", + +"BITAND", +"BITOR" +}; + +char *PR_GlobalString (int ofs); +char *PR_GlobalStringNoContents (int ofs); + + +//============================================================================= + +/* +================= +PR_PrintStatement +================= +*/ +void PR_PrintStatement (dstatement_t *s) +{ + int i; + + if ( (unsigned)s->op < sizeof(pr_opnames)/sizeof(pr_opnames[0])) + { + + Con_Printf ("%s ", pr_opnames[s->op]); + + i = strlen(pr_opnames[s->op]); + for ( ; i<10 ; i++) + Con_Printf (" "); + } + + if (s->op == OP_IF || s->op == OP_IFNOT) + Con_Printf ("%sbranch %i",PR_GlobalString(s->a),s->b); + else if (s->op == OP_GOTO) + { + Con_Printf ("branch %i",s->a); + } + else if ( (unsigned)(s->op - OP_STORE_F) < 6) + { + Con_Printf ("%s",PR_GlobalString(s->a)); + Con_Printf ("%s", PR_GlobalStringNoContents(s->b)); + } + else + { + if (s->a) + Con_Printf ("%s",PR_GlobalString(s->a)); + if (s->b) + Con_Printf ("%s",PR_GlobalString(s->b)); + if (s->c) + Con_Printf ("%s", PR_GlobalStringNoContents(s->c)); + } + Con_Printf ("\n"); +} + +/* +============ +PR_StackTrace +============ +*/ +void PR_StackTrace (void) +{ + dfunction_t *f; + int i; + + if (pr_depth == 0) + { + Con_Printf ("\n"); + return; + } + + pr_stack[pr_depth].f = pr_xfunction; + for (i=pr_depth ; i>=0 ; i--) + { + f = pr_stack[i].f; + + if (!f) + { + Con_Printf ("\n"); + } + else + Con_Printf ("%12s : %s\n", PR_GetString(f->s_file), PR_GetString(f->s_name)); + } +} + + +/* +============ +PR_Profile_f + +============ +*/ +void PR_Profile_f (void) +{ + dfunction_t *f, *best; + int max; + int num; + int i; + + num = 0; + do + { + max = 0; + best = NULL; + for (i=0 ; inumfunctions ; i++) + { + f = &pr_functions[i]; + if (f->profile > max) + { + max = f->profile; + best = f; + } + } + if (best) + { + if (num < 10) + Con_Printf ("%7i %s\n", best->profile, PR_GetString(best->s_name)); + num++; + best->profile = 0; + } + } while (best); +} + + +/* +============ +PR_RunError + +Aborts the currently executing function +============ +*/ +void PR_RunError (char *error, ...) +{ + va_list argptr; + char string[1024]; + + va_start (argptr,error); + vsprintf (string,error,argptr); + va_end (argptr); + + + sv_error = true; + + PR_PrintStatement (pr_statements + pr_xstatement); + PR_StackTrace (); + Con_Printf ("%s\n", string); + + pr_depth = 0; // dump the stack so SV_Error can shutdown functions + + SV_Error ("Program error"); +} + +/* +============================================================================ +PR_ExecuteProgram + +The interpretation main loop +============================================================================ +*/ + +/* +==================== +PR_EnterFunction + +Returns the new program statement counter +==================== +*/ +int PR_EnterFunction (dfunction_t *f) +{ + int i, j, c, o; + + pr_stack[pr_depth].s = pr_xstatement; + pr_stack[pr_depth].f = pr_xfunction; + pr_depth++; + if (pr_depth >= MAX_STACK_DEPTH) + PR_RunError ("stack overflow"); + +// save off any locals that the new function steps on + c = f->locals; + if (localstack_used + c > LOCALSTACK_SIZE) + PR_RunError ("PR_ExecuteProgram: locals stack overflow\n"); + + for (i=0 ; i < c ; i++) + localstack[localstack_used+i] = ((int *)pr_globals)[f->parm_start + i]; + localstack_used += c; + +// copy parameters + o = f->parm_start; + for (i=0 ; inumparms ; i++) + { + for (j=0 ; jparm_size[i] ; j++) + { + ((int *)pr_globals)[o] = ((int *)pr_globals)[OFS_PARM0+i*3+j]; + o++; + } + } + + pr_xfunction = f; + return f->first_statement - 1; // offset the s++ +} + +/* +==================== +PR_LeaveFunction +==================== +*/ +int PR_LeaveFunction (void) +{ + int i, c; + + if (pr_depth <= 0) + SV_Error ("prog stack underflow"); + +// restore locals from the stack + c = pr_xfunction->locals; + localstack_used -= c; + if (localstack_used < 0) + PR_RunError ("PR_ExecuteProgram: locals stack underflow\n"); + + for (i=0 ; i < c ; i++) + ((int *)pr_globals)[pr_xfunction->parm_start + i] = localstack[localstack_used+i]; + +// up stack + pr_depth--; + pr_xfunction = pr_stack[pr_depth].f; + return pr_stack[pr_depth].s; +} + + +/* +==================== +PR_ExecuteProgram +==================== +*/ +void PR_ExecuteProgram (func_t fnum) +{ + eval_t *a, *b, *c; + int s; + dstatement_t *st; + dfunction_t *f, *newf; + int runaway; + int i; + edict_t *ed; + int exitdepth; + eval_t *ptr; + + if (!fnum || fnum >= progs->numfunctions) + { + if (pr_global_struct->self) + ED_Print (PROG_TO_EDICT(pr_global_struct->self)); + SV_Error ("PR_ExecuteProgram: NULL function"); + } + + f = &pr_functions[fnum]; + + runaway = 100000; + pr_trace = false; + +// make a stack frame + exitdepth = pr_depth; + + s = PR_EnterFunction (f); + +while (1) +{ + s++; // next statement + + st = &pr_statements[s]; + a = (eval_t *)&pr_globals[st->a]; + b = (eval_t *)&pr_globals[st->b]; + c = (eval_t *)&pr_globals[st->c]; + + if (--runaway == 0) + PR_RunError ("runaway loop error"); + + pr_xfunction->profile++; + pr_xstatement = s; + + if (pr_trace) + PR_PrintStatement (st); + + switch (st->op) + { + case OP_ADD_F: + c->_float = a->_float + b->_float; + break; + case OP_ADD_V: + c->vector[0] = a->vector[0] + b->vector[0]; + c->vector[1] = a->vector[1] + b->vector[1]; + c->vector[2] = a->vector[2] + b->vector[2]; + break; + + case OP_SUB_F: + c->_float = a->_float - b->_float; + break; + case OP_SUB_V: + c->vector[0] = a->vector[0] - b->vector[0]; + c->vector[1] = a->vector[1] - b->vector[1]; + c->vector[2] = a->vector[2] - b->vector[2]; + break; + + case OP_MUL_F: + c->_float = a->_float * b->_float; + break; + case OP_MUL_V: + c->_float = a->vector[0]*b->vector[0] + + a->vector[1]*b->vector[1] + + a->vector[2]*b->vector[2]; + break; + case OP_MUL_FV: + c->vector[0] = a->_float * b->vector[0]; + c->vector[1] = a->_float * b->vector[1]; + c->vector[2] = a->_float * b->vector[2]; + break; + case OP_MUL_VF: + c->vector[0] = b->_float * a->vector[0]; + c->vector[1] = b->_float * a->vector[1]; + c->vector[2] = b->_float * a->vector[2]; + break; + + case OP_DIV_F: + c->_float = a->_float / b->_float; + break; + + case OP_BITAND: + c->_float = (int)a->_float & (int)b->_float; + break; + + case OP_BITOR: + c->_float = (int)a->_float | (int)b->_float; + break; + + + case OP_GE: + c->_float = a->_float >= b->_float; + break; + case OP_LE: + c->_float = a->_float <= b->_float; + break; + case OP_GT: + c->_float = a->_float > b->_float; + break; + case OP_LT: + c->_float = a->_float < b->_float; + break; + case OP_AND: + c->_float = a->_float && b->_float; + break; + case OP_OR: + c->_float = a->_float || b->_float; + break; + + case OP_NOT_F: + c->_float = !a->_float; + break; + case OP_NOT_V: + c->_float = !a->vector[0] && !a->vector[1] && !a->vector[2]; + break; + case OP_NOT_S: + c->_float = !a->string || !*PR_GetString(a->string); + break; + case OP_NOT_FNC: + c->_float = !a->function; + break; + case OP_NOT_ENT: + c->_float = (PROG_TO_EDICT(a->edict) == sv.edicts); + break; + + case OP_EQ_F: + c->_float = a->_float == b->_float; + break; + case OP_EQ_V: + c->_float = (a->vector[0] == b->vector[0]) && + (a->vector[1] == b->vector[1]) && + (a->vector[2] == b->vector[2]); + break; + case OP_EQ_S: + c->_float = !strcmp(PR_GetString(a->string), PR_GetString(b->string)); + break; + case OP_EQ_E: + c->_float = a->_int == b->_int; + break; + case OP_EQ_FNC: + c->_float = a->function == b->function; + break; + + + case OP_NE_F: + c->_float = a->_float != b->_float; + break; + case OP_NE_V: + c->_float = (a->vector[0] != b->vector[0]) || + (a->vector[1] != b->vector[1]) || + (a->vector[2] != b->vector[2]); + break; + case OP_NE_S: + c->_float = strcmp(PR_GetString(a->string), PR_GetString(b->string)); + break; + case OP_NE_E: + c->_float = a->_int != b->_int; + break; + case OP_NE_FNC: + c->_float = a->function != b->function; + break; + +//================== + case OP_STORE_F: + case OP_STORE_ENT: + case OP_STORE_FLD: // integers + case OP_STORE_S: + case OP_STORE_FNC: // pointers + b->_int = a->_int; + break; + case OP_STORE_V: + b->vector[0] = a->vector[0]; + b->vector[1] = a->vector[1]; + b->vector[2] = a->vector[2]; + break; + + case OP_STOREP_F: + case OP_STOREP_ENT: + case OP_STOREP_FLD: // integers + case OP_STOREP_S: + case OP_STOREP_FNC: // pointers + ptr = (eval_t *)((byte *)sv.edicts + b->_int); + ptr->_int = a->_int; + break; + case OP_STOREP_V: + ptr = (eval_t *)((byte *)sv.edicts + b->_int); + ptr->vector[0] = a->vector[0]; + ptr->vector[1] = a->vector[1]; + ptr->vector[2] = a->vector[2]; + break; + + case OP_ADDRESS: + ed = PROG_TO_EDICT(a->edict); +#ifdef PARANOID + NUM_FOR_EDICT(ed); // make sure it's in range +#endif + if (ed == (edict_t *)sv.edicts && sv.state == ss_active) + PR_RunError ("assignment to world entity"); + c->_int = (byte *)((int *)&ed->v + b->_int) - (byte *)sv.edicts; + break; + + case OP_LOAD_F: + case OP_LOAD_FLD: + case OP_LOAD_ENT: + case OP_LOAD_S: + case OP_LOAD_FNC: + ed = PROG_TO_EDICT(a->edict); +#ifdef PARANOID + NUM_FOR_EDICT(ed); // make sure it's in range +#endif + a = (eval_t *)((int *)&ed->v + b->_int); + c->_int = a->_int; + break; + + case OP_LOAD_V: + ed = PROG_TO_EDICT(a->edict); +#ifdef PARANOID + NUM_FOR_EDICT(ed); // make sure it's in range +#endif + a = (eval_t *)((int *)&ed->v + b->_int); + c->vector[0] = a->vector[0]; + c->vector[1] = a->vector[1]; + c->vector[2] = a->vector[2]; + break; + +//================== + + case OP_IFNOT: + if (!a->_int) + s += st->b - 1; // offset the s++ + break; + + case OP_IF: + if (a->_int) + s += st->b - 1; // offset the s++ + break; + + case OP_GOTO: + s += st->a - 1; // offset the s++ + break; + + case OP_CALL0: + case OP_CALL1: + case OP_CALL2: + case OP_CALL3: + case OP_CALL4: + case OP_CALL5: + case OP_CALL6: + case OP_CALL7: + case OP_CALL8: + pr_argc = st->op - OP_CALL0; + if (!a->function) + PR_RunError ("NULL function"); + + newf = &pr_functions[a->function]; + + if (newf->first_statement < 0) + { // negative statements are built in functions + i = -newf->first_statement; + if (i >= pr_numbuiltins) + PR_RunError ("Bad builtin call number"); + pr_builtins[i] (); + break; + } + + s = PR_EnterFunction (newf); + + break; + + case OP_DONE: + case OP_RETURN: + pr_globals[OFS_RETURN] = pr_globals[st->a]; + pr_globals[OFS_RETURN+1] = pr_globals[st->a+1]; + pr_globals[OFS_RETURN+2] = pr_globals[st->a+2]; + + s = PR_LeaveFunction (); + if (pr_depth == exitdepth) + return; // all done + break; + + case OP_STATE: + ed = PROG_TO_EDICT(pr_global_struct->self); + ed->v.nextthink = pr_global_struct->time + 0.1; + if (a->_float != ed->v.frame) + { + ed->v.frame = a->_float; + } + ed->v.think = b->function; + break; + + default: + PR_RunError ("Bad opcode %i", st->op); + } +} + +} + +/*----------------------*/ + +char *pr_newstrtbl[MAX_PRSTR]; +char *pr_strtbl[MAX_PRSTR]; +int num_prstr; + +char *PR_GetString(int num) +{ + if (num < 0) { +//Con_DPrintf("GET:%d == %s\n", num, pr_strtbl[-num]); + if (num <= -MAX_PRSTR) + return pr_newstrtbl[-(num + MAX_PRSTR)]; + + return pr_strtbl[-num]; + } + return pr_strings + num; +} + +int PR_SetString(char *s) +{ + int i; + + if (s - pr_strings < 0) { + for (i = 0; i <= num_prstr; i++) + if (pr_strtbl[i] == s) + break; + if (i < num_prstr) + return -i; + if (num_prstr == MAX_PRSTR - 1) + Sys_Error("MAX_PRSTR"); + num_prstr++; + pr_strtbl[num_prstr] = s; +//Con_DPrintf("SET:%d == %s\n", -num_prstr, s); + return -num_prstr; + } + return (int)(s - pr_strings); +} + +/* +============== +PR_SetTmpString + +temp strings are used for qc function parameters +many calls to function could cause strtbl overflow +============== +*/ + +int PR_SetTmpString(char *s) +{ + static int index; + static char tmp[8][2048]; + + index = (index + 1)&7; + + strcpy(tmp[index], s); + return PR_SetString(tmp[index]); +} diff --git a/source/progdefs.h b/source/progdefs.h index 3dcc2c44..7563a43b 100644 --- a/source/progdefs.h +++ b/source/progdefs.h @@ -1,159 +1,159 @@ -/* -Copyright (C) 1996-1997 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. - -*/ - -/* file generated by qcc, do not modify */ - -typedef struct -{ int pad[28]; - int self; - int other; - int world; - float time; - float frametime; - int newmis; - float force_retouch; - string_t mapname; - float serverflags; - float total_secrets; - float total_monsters; - float found_secrets; - float killed_monsters; - float parm1; - float parm2; - float parm3; - float parm4; - float parm5; - float parm6; - float parm7; - float parm8; - float parm9; - float parm10; - float parm11; - float parm12; - float parm13; - float parm14; - float parm15; - float parm16; - vec3_t v_forward; - vec3_t v_up; - vec3_t v_right; - float trace_allsolid; - float trace_startsolid; - float trace_fraction; - vec3_t trace_endpos; - vec3_t trace_plane_normal; - float trace_plane_dist; - int trace_ent; - float trace_inopen; - float trace_inwater; - int msg_entity; - func_t main; - func_t StartFrame; - func_t PlayerPreThink; - func_t PlayerPostThink; - func_t ClientKill; - func_t ClientConnect; - func_t PutClientInServer; - func_t ClientDisconnect; - func_t SetNewParms; - func_t SetChangeParms; -} globalvars_t; - -typedef struct -{ - float modelindex; - vec3_t absmin; - vec3_t absmax; - float ltime; - float lastruntime; - float movetype; - float solid; - vec3_t origin; - vec3_t oldorigin; - vec3_t velocity; - vec3_t angles; - vec3_t avelocity; - string_t classname; - string_t model; - float frame; - float skin; - float effects; - vec3_t mins; - vec3_t maxs; - vec3_t size; - func_t touch; - func_t use; - func_t think; - func_t blocked; - float nextthink; - int groundentity; - float health; - float frags; - float weapon; - string_t weaponmodel; - float weaponframe; - float currentammo; - float ammo_shells; - float ammo_nails; - float ammo_rockets; - float ammo_cells; - float items; - float takedamage; - int chain; - float deadflag; - vec3_t view_ofs; - float button0; - float button1; - float button2; - float impulse; - float fixangle; - vec3_t v_angle; - string_t netname; - int enemy; - float flags; - float colormap; - float team; - float max_health; - float teleport_time; - float armortype; - float armorvalue; - float waterlevel; - float watertype; - float ideal_yaw; - float yaw_speed; - int aiment; - int goalentity; - float spawnflags; - string_t target; - string_t targetname; - float dmg_take; - float dmg_save; - int dmg_inflictor; - int owner; - vec3_t movedir; - string_t message; - float sounds; - string_t noise; - string_t noise1; - string_t noise2; - string_t noise3; -} entvars_t; - -#define PROGHEADER_CRC 54730 +/* +Copyright (C) 1996-1997 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. + +*/ + +/* file generated by qcc, do not modify */ + +typedef struct +{ int pad[28]; + int self; + int other; + int world; + float time; + float frametime; + int newmis; + float force_retouch; + string_t mapname; + float serverflags; + float total_secrets; + float total_monsters; + float found_secrets; + float killed_monsters; + float parm1; + float parm2; + float parm3; + float parm4; + float parm5; + float parm6; + float parm7; + float parm8; + float parm9; + float parm10; + float parm11; + float parm12; + float parm13; + float parm14; + float parm15; + float parm16; + vec3_t v_forward; + vec3_t v_up; + vec3_t v_right; + float trace_allsolid; + float trace_startsolid; + float trace_fraction; + vec3_t trace_endpos; + vec3_t trace_plane_normal; + float trace_plane_dist; + int trace_ent; + float trace_inopen; + float trace_inwater; + int msg_entity; + func_t main; + func_t StartFrame; + func_t PlayerPreThink; + func_t PlayerPostThink; + func_t ClientKill; + func_t ClientConnect; + func_t PutClientInServer; + func_t ClientDisconnect; + func_t SetNewParms; + func_t SetChangeParms; +} globalvars_t; + +typedef struct +{ + float modelindex; + vec3_t absmin; + vec3_t absmax; + float ltime; + float lastruntime; + float movetype; + float solid; + vec3_t origin; + vec3_t oldorigin; + vec3_t velocity; + vec3_t angles; + vec3_t avelocity; + string_t classname; + string_t model; + float frame; + float skin; + float effects; + vec3_t mins; + vec3_t maxs; + vec3_t size; + func_t touch; + func_t use; + func_t think; + func_t blocked; + float nextthink; + int groundentity; + float health; + float frags; + float weapon; + string_t weaponmodel; + float weaponframe; + float currentammo; + float ammo_shells; + float ammo_nails; + float ammo_rockets; + float ammo_cells; + float items; + float takedamage; + int chain; + float deadflag; + vec3_t view_ofs; + float button0; + float button1; + float button2; + float impulse; + float fixangle; + vec3_t v_angle; + string_t netname; + int enemy; + float flags; + float colormap; + float team; + float max_health; + float teleport_time; + float armortype; + float armorvalue; + float waterlevel; + float watertype; + float ideal_yaw; + float yaw_speed; + int aiment; + int goalentity; + float spawnflags; + string_t target; + string_t targetname; + float dmg_take; + float dmg_save; + int dmg_inflictor; + int owner; + vec3_t movedir; + string_t message; + float sounds; + string_t noise; + string_t noise1; + string_t noise2; + string_t noise3; +} entvars_t; + +#define PROGHEADER_CRC 54730 diff --git a/source/progs.h b/source/progs.h index cc7c85b6..f2981e7e 100644 --- a/source/progs.h +++ b/source/progs.h @@ -1,147 +1,150 @@ -/* -Copyright (C) 1996-1997 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 "pr_comp.h" // defs shared with qcc -#include "progdefs.h" // generated by program cdefs - -typedef union eval_s -{ - string_t string; - float _float; - float vector[3]; - func_t function; - int _int; - int edict; -} eval_t; - -#define MAX_ENT_LEAFS 16 -typedef struct edict_s -{ - qboolean free; - link_t area; // linked to a division node or leaf - - int num_leafs; - short leafnums[MAX_ENT_LEAFS]; - - entity_state_t baseline; - - float freetime; // sv.time when the object was freed - entvars_t v; // C exported fields from progs -// other fields from progs come immediately after -} edict_t; -#define EDICT_FROM_AREA(l) STRUCT_FROM_LINK(l,edict_t,area) - -//============================================================================ - -extern dprograms_t *progs; -extern dfunction_t *pr_functions; -extern char *pr_strings; -extern ddef_t *pr_globaldefs; -extern ddef_t *pr_fielddefs; -extern dstatement_t *pr_statements; -extern globalvars_t *pr_global_struct; -extern float *pr_globals; // same as pr_global_struct - -extern int pr_edict_size; // in bytes - -//============================================================================ - -void PR_Init (void); - -void PR_ExecuteProgram (func_t fnum); -void PR_LoadProgs (void); - -void PR_Profile_f (void); - -edict_t *ED_Alloc (void); -void ED_Free (edict_t *ed); - -char *ED_NewString (char *string); -// returns a copy of the string allocated from the server's string heap - -void ED_Print (edict_t *ed); -void ED_Write (FILE *f, edict_t *ed); -char *ED_ParseEdict (char *data, edict_t *ent); - -void ED_WriteGlobals (FILE *f); -void ED_ParseGlobals (char *data); - -void ED_LoadFromFile (char *data); - -//define EDICT_NUM(n) ((edict_t *)(sv.edicts+ (n)*pr_edict_size)) -//define NUM_FOR_EDICT(e) (((byte *)(e) - sv.edicts)/pr_edict_size) - -edict_t *EDICT_NUM(int n); -int NUM_FOR_EDICT(edict_t *e); - -#define NEXT_EDICT(e) ((edict_t *)( (byte *)e + pr_edict_size)) - -#define EDICT_TO_PROG(e) ((byte *)e - (byte *)sv.edicts) -#define PROG_TO_EDICT(e) ((edict_t *)((byte *)sv.edicts + e)) - -//============================================================================ - -#define G_FLOAT(o) (pr_globals[o]) -#define G_INT(o) (*(int *)&pr_globals[o]) -#define G_EDICT(o) ((edict_t *)((byte *)sv.edicts+ *(int *)&pr_globals[o])) -#define G_EDICTNUM(o) NUM_FOR_EDICT(G_EDICT(o)) -#define G_VECTOR(o) (&pr_globals[o]) -#define G_STRING(o) (PR_GetString(*(string_t *)&pr_globals[o])) -#define G_FUNCTION(o) (*(func_t *)&pr_globals[o]) - -#define E_FLOAT(e,o) (((float*)&e->v)[o]) -#define E_INT(e,o) (*(int *)&((float*)&e->v)[o]) -#define E_VECTOR(e,o) (&((float*)&e->v)[o]) -#define E_STRING(e,o) (PR_GetString(*(string_t *)&((float*)&e->v)[o])) - -extern int type_size[8]; - -typedef void (*builtin_t) (void); -extern builtin_t *pr_builtins; -extern int pr_numbuiltins; - -extern int pr_argc; - -extern qboolean pr_trace; -extern dfunction_t *pr_xfunction; -extern int pr_xstatement; - -extern func_t SpectatorConnect; -extern func_t SpectatorThink; -extern func_t SpectatorDisconnect; - -void PR_RunError (char *error, ...); - -void ED_PrintEdicts (void); -void ED_PrintNum (int ent); - -eval_t *GetEdictFieldValue(edict_t *ed, char *field); - -// -// PR STrings stuff -// -#define MAX_PRSTR 1024 - -extern char *pr_strtbl[MAX_PRSTR]; -extern int num_prstr; - -char *PR_GetString(int num); -int PR_SetString(char *s); - +/* +Copyright (C) 1996-1997 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 "pr_comp.h" // defs shared with qcc +#include "progdefs.h" // generated by program cdefs + +typedef union eval_s +{ + string_t string; + float _float; + float vector[3]; + func_t function; + int _int; + int edict; +} eval_t; + +#define MAX_ENT_LEAFS 16 +typedef struct edict_s +{ + qboolean free; + link_t area; // linked to a division node or leaf + + int num_leafs; + short leafnums[MAX_ENT_LEAFS]; + + entity_state_t baseline; + + float freetime; // sv.time when the object was freed + entvars_t v; // C exported fields from progs +// other fields from progs come immediately after +} edict_t; +#define EDICT_FROM_AREA(l) STRUCT_FROM_LINK(l,edict_t,area) + +//============================================================================ + +extern dprograms_t *progs; +extern dfunction_t *pr_functions; +extern char *pr_strings; +extern ddef_t *pr_globaldefs; +extern ddef_t *pr_fielddefs; +extern dstatement_t *pr_statements; +extern globalvars_t *pr_global_struct; +extern float *pr_globals; // same as pr_global_struct + +extern int pr_edict_size; // in bytes +extern int pr_teamfield; + +//============================================================================ + +void PR_Init (void); + +void PR_ExecuteProgram (func_t fnum); +void PR_LoadProgs (void); + +void PR_Profile_f (void); + +edict_t *ED_Alloc (void); +void ED_Free (edict_t *ed); + +char *ED_NewString (char *string); +// returns a copy of the string allocated from the server's string heap + +void ED_Print (edict_t *ed); +void ED_Write (FILE *f, edict_t *ed); +char *ED_ParseEdict (char *data, edict_t *ent); + +void ED_WriteGlobals (FILE *f); +void ED_ParseGlobals (char *data); + +void ED_LoadFromFile (char *data); + +//define EDICT_NUM(n) ((edict_t *)(sv.edicts+ (n)*pr_edict_size)) +//define NUM_FOR_EDICT(e) (((byte *)(e) - sv.edicts)/pr_edict_size) + +edict_t *EDICT_NUM(int n); +int NUM_FOR_EDICT(edict_t *e); + +#define NEXT_EDICT(e) ((edict_t *)( (byte *)e + pr_edict_size)) + +#define EDICT_TO_PROG(e) ((byte *)e - (byte *)sv.edicts) +#define PROG_TO_EDICT(e) ((edict_t *)((byte *)sv.edicts + e)) + +//============================================================================ + +#define G_FLOAT(o) (pr_globals[o]) +#define G_INT(o) (*(int *)&pr_globals[o]) +#define G_EDICT(o) ((edict_t *)((byte *)sv.edicts+ *(int *)&pr_globals[o])) +#define G_EDICTNUM(o) NUM_FOR_EDICT(G_EDICT(o)) +#define G_VECTOR(o) (&pr_globals[o]) +#define G_STRING(o) (PR_GetString(*(string_t *)&pr_globals[o])) +#define G_FUNCTION(o) (*(func_t *)&pr_globals[o]) + +#define E_FLOAT(e,o) (((float*)&e->v)[o]) +#define E_INT(e,o) (*(int *)&((float*)&e->v)[o]) +#define E_VECTOR(e,o) (&((float*)&e->v)[o]) +#define E_STRING(e,o) (PR_GetString(*(string_t *)&((float*)&e->v)[o])) + +extern int type_size[8]; + +typedef void (*builtin_t) (void); +extern builtin_t *pr_builtins; +extern int pr_numbuiltins; + +extern int pr_argc; + +extern qboolean pr_trace; +extern dfunction_t *pr_xfunction; +extern int pr_xstatement; + +extern func_t SpectatorConnect; +extern func_t SpectatorThink; +extern func_t SpectatorDisconnect; + +void PR_RunError (char *error, ...); + +void ED_PrintEdicts (void); +void ED_PrintNum (int ent); + +eval_t *GetEdictFieldValue(edict_t *ed, char *field); + +// +// PR STrings stuff +// +#define MAX_PRSTR 1024 + +extern char *pr_strtbl[MAX_PRSTR]; +extern char *pr_newstrtbl[MAX_PRSTR]; +extern int num_prstr; + +char *PR_GetString(int num); +int PR_SetString(char *s); +int PR_SetTmpString(char *s); + diff --git a/source/protocol.h b/source/protocol.h index 42879a65..82c35056 100644 --- a/source/protocol.h +++ b/source/protocol.h @@ -1,300 +1,312 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// protocol.h -- communications protocols - -#define PROTOCOL_VERSION 28 - -#define QW_CHECK_HASH 0x5157 - -//========================================= - -#define PORT_CLIENT 27001 -#define PORT_MASTER 27000 -#define PORT_SERVER 27500 - -//========================================= - -// out of band message id bytes - -// M = master, S = server, C = client, A = any -// the second character will always be \n if the message isn't a single -// byte long (?? not true anymore?) - -#define S2C_CHALLENGE 'c' -#define S2C_CONNECTION 'j' -#define A2A_PING 'k' // respond with an A2A_ACK -#define A2A_ACK 'l' // general acknowledgement without info -#define A2A_NACK 'm' // [+ comment] general failure -#define A2A_ECHO 'e' // for echoing -#define A2C_PRINT 'n' // print a message on client - -#define S2M_HEARTBEAT 'a' // + serverinfo + userlist + fraglist -#define A2C_CLIENT_COMMAND 'B' // + command line -#define S2M_SHUTDOWN 'C' - - -//================== -// note that there are some defs.qc that mirror to these numbers -// also related to svc_strings[] in cl_parse -//================== - -// -// server to client -// -#define svc_bad 0 -#define svc_nop 1 -#define svc_disconnect 2 -#define svc_updatestat 3 // [byte] [byte] -//define svc_version 4 // [long] server version -#define svc_setview 5 // [short] entity number -#define svc_sound 6 // -//define svc_time 7 // [float] server time -#define svc_print 8 // [byte] id [string] null terminated string -#define svc_stufftext 9 // [string] stuffed into client's console buffer - // the string should be \n terminated -#define svc_setangle 10 // [angle3] set the view angle to this absolute value - -#define svc_serverdata 11 // [long] protocol ... -#define svc_lightstyle 12 // [byte] [string] -//define svc_updatename 13 // [byte] [string] -#define svc_updatefrags 14 // [byte] [short] -//define svc_clientdata 15 // -#define svc_stopsound 16 // -//define svc_updatecolors 17 // [byte] [byte] [byte] -//define svc_particle 18 // [vec3] -#define svc_damage 19 - -#define svc_spawnstatic 20 -// svc_spawnbinary 21 -#define svc_spawnbaseline 22 - -#define svc_temp_entity 23 // variable -#define svc_setpause 24 // [byte] on / off -// svc_signonnum 25 // [byte] used for the signon sequence - -#define svc_centerprint 26 // [string] to put in center of the screen - -#define svc_killedmonster 27 -#define svc_foundsecret 28 - -#define svc_spawnstaticsound 29 // [coord3] [byte] samp [byte] vol [byte] aten - -#define svc_intermission 30 // [vec3_t] origin [vec3_t] angle -#define svc_finale 31 // [string] text - -#define svc_cdtrack 32 // [byte] track -#define svc_sellscreen 33 - -#define svc_smallkick 34 // set client punchangle to 2 -#define svc_bigkick 35 // set client punchangle to 4 - -#define svc_updateping 36 // [byte] [short] -#define svc_updateentertime 37 // [byte] [float] - -#define svc_updatestatlong 38 // [byte] [long] - -#define svc_muzzleflash 39 // [short] entity - -#define svc_updateuserinfo 40 // [byte] slot [long] uid - // [string] userinfo - -#define svc_download 41 // [short] size [size bytes] -#define svc_playerinfo 42 // variable -#define svc_nails 43 // [byte] num [48 bits] xyzpy 12 12 12 4 8 -#define svc_chokecount 44 // [byte] packets choked -#define svc_modellist 45 // [strings] -#define svc_soundlist 46 // [strings] -#define svc_packetentities 47 // [...] -#define svc_deltapacketentities 48 // [...] -#define svc_maxspeed 49 // maxspeed change, for prediction -#define svc_entgravity 50 // gravity change, for prediction -#define svc_setinfo 51 // setinfo on a client -#define svc_serverinfo 52 // serverinfo -#define svc_updatepl 53 // [byte] [byte] - -#define svc_nails2 54 // for interpolation, stores edict num - - - -//============================================== - -// -// client to server -// -#define clc_bad 0 -#define clc_nop 1 -//define clc_doublemove 2 -#define clc_move 3 // [[usercmd_t] -#define clc_stringcmd 4 // [string] message -#define clc_delta 5 // [byte] sequence number, requests delta compression of message -#define clc_tmove 6 // teleport request, spectator only -#define clc_upload 7 // teleport request, spectator only - - -//============================================== - -// -// demo recording -// - -#define dem_cmd 0 -#define dem_read 1 -#define dem_set 2 -#define dem_multiple 3 -#define dem_single 4 -#define dem_stats 5 -#define dem_all 6 - -//============================================== - -// playerinfo flags from server -// playerinfo always sends: playernum, flags, origin[] and framenumber - -#define PF_MSEC (1<<0) -#define PF_COMMAND (1<<1) -#define PF_VELOCITY1 (1<<2) -#define PF_VELOCITY2 (1<<3) -#define PF_VELOCITY3 (1<<4) -#define PF_MODEL (1<<5) -#define PF_SKINNUM (1<<6) -#define PF_EFFECTS (1<<7) -#define PF_WEAPONFRAME (1<<8) // only sent for view player -#define PF_DEAD (1<<9) // don't block movement any more -#define PF_GIB (1<<10) // offset the view height differently -#define PF_NOGRAV (1<<11) // don't apply gravity for prediction - -//============================================== - -// if the high bit of the client to server byte is set, the low bits are -// client move cmd bits -// ms and angle2 are always sent, the others are optional -#define CM_ANGLE1 (1<<0) -#define CM_ANGLE3 (1<<1) -#define CM_FORWARD (1<<2) -#define CM_SIDE (1<<3) -#define CM_UP (1<<4) -#define CM_BUTTONS (1<<5) -#define CM_IMPULSE (1<<6) -#define CM_ANGLE2 (1<<7) - -//============================================== - -// the first 16 bits of a packetentities update holds 9 bits -// of entity number and 7 bits of flags -#define U_ORIGIN1 (1<<9) -#define U_ORIGIN2 (1<<10) -#define U_ORIGIN3 (1<<11) -#define U_ANGLE2 (1<<12) -#define U_FRAME (1<<13) -#define U_REMOVE (1<<14) // REMOVE this entity, don't add it -#define U_MOREBITS (1<<15) - -// if MOREBITS is set, these additional flags are read in next -#define U_ANGLE1 (1<<0) -#define U_ANGLE3 (1<<1) -#define U_MODEL (1<<2) -#define U_COLORMAP (1<<3) -#define U_SKIN (1<<4) -#define U_EFFECTS (1<<5) -#define U_SOLID (1<<6) // the entity should be solid for prediction - -//============================================== - -// a sound with no channel is a local only sound -// the sound field has bits 0-2: channel, 3-12: entity -#define SND_VOLUME (1<<15) // a byte -#define SND_ATTENUATION (1<<14) // a byte - -#define DEFAULT_SOUND_PACKET_VOLUME 255 -#define DEFAULT_SOUND_PACKET_ATTENUATION 1.0 - -// svc_print messages have an id, so messages can be filtered -#define PRINT_LOW 0 -#define PRINT_MEDIUM 1 -#define PRINT_HIGH 2 -#define PRINT_CHAT 3 // also go to chat buffer - -// -// temp entity events -// -#define TE_SPIKE 0 -#define TE_SUPERSPIKE 1 -#define TE_GUNSHOT 2 -#define TE_EXPLOSION 3 -#define TE_TAREXPLOSION 4 -#define TE_LIGHTNING1 5 -#define TE_LIGHTNING2 6 -#define TE_WIZSPIKE 7 -#define TE_KNIGHTSPIKE 8 -#define TE_LIGHTNING3 9 -#define TE_LAVASPLASH 10 -#define TE_TELEPORT 11 -#define TE_BLOOD 12 -#define TE_LIGHTNINGBLOOD 13 - - -/* -========================================================== - - ELEMENTS COMMUNICATED ACROSS THE NET - -========================================================== -*/ - -#define MAX_CLIENTS 32 - -#define UPDATE_BACKUP 64 // copies of entity_state_t to keep buffered - // must be power of two -#define UPDATE_MASK (UPDATE_BACKUP-1) - -// entity_state_t is the information conveyed from the server -// in an update message -typedef struct -{ - int number; // edict index - - int flags; // nolerp, etc - vec3_t origin; - vec3_t angles; - int modelindex; - int frame; - int colormap; - int skinnum; - int effects; -} entity_state_t; - - -#define MAX_PACKET_ENTITIES 196 // doesn't count nails -#define OLD_MAX_PACKET_ENTITIES 64 -typedef struct -{ - int num_entities; - entity_state_t entities[MAX_PACKET_ENTITIES]; -} packet_entities_t; - -typedef struct usercmd_s -{ - byte msec; - vec3_t angles; - short forwardmove, sidemove, upmove; - byte buttons; - byte impulse; -} usercmd_t; - +/* +Copyright (C) 1996-1997 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. + +*/ +// protocol.h -- communications protocols + +#define PROTOCOL_VERSION 28 + +#define QW_CHECK_HASH 0x5157 + +//========================================= + +#define PORT_CLIENT 27001 +#define PORT_MASTER 27000 +#define PORT_SERVER 27500 + +//========================================= + +// out of band message id bytes + +// M = master, S = server, C = client, A = any +// the second character will always be \n if the message isn't a single +// byte long (?? not true anymore?) + +#define S2C_CHALLENGE 'c' +#define S2C_CONNECTION 'j' +#define A2A_PING 'k' // respond with an A2A_ACK +#define A2A_ACK 'l' // general acknowledgement without info +#define A2A_NACK 'm' // [+ comment] general failure +#define A2A_ECHO 'e' // for echoing +#define A2C_PRINT 'n' // print a message on client + +#define S2M_HEARTBEAT 'a' // + serverinfo + userlist + fraglist +#define A2C_CLIENT_COMMAND 'B' // + command line +#define S2M_SHUTDOWN 'C' + + +//================== +// note that there are some defs.qc that mirror to these numbers +// also related to svc_strings[] in cl_parse +//================== + +// +// server to client +// +#define svc_bad 0 +#define svc_nop 1 +#define svc_disconnect 2 +#define svc_updatestat 3 // [byte] [byte] +//define svc_version 4 // [long] server version +#define svc_setview 5 // [short] entity number +#define svc_sound 6 // +//define svc_time 7 // [float] server time +#define svc_print 8 // [byte] id [string] null terminated string +#define svc_stufftext 9 // [string] stuffed into client's console buffer + // the string should be \n terminated +#define svc_setangle 10 // [angle3] set the view angle to this absolute value + +#define svc_serverdata 11 // [long] protocol ... +#define svc_lightstyle 12 // [byte] [string] +//define svc_updatename 13 // [byte] [string] +#define svc_updatefrags 14 // [byte] [short] +//define svc_clientdata 15 // +#define svc_stopsound 16 // +//define svc_updatecolors 17 // [byte] [byte] [byte] +//define svc_particle 18 // [vec3] +#define svc_damage 19 + +#define svc_spawnstatic 20 +// svc_spawnbinary 21 +#define svc_spawnbaseline 22 + +#define svc_temp_entity 23 // variable +#define svc_setpause 24 // [byte] on / off +// svc_signonnum 25 // [byte] used for the signon sequence + +#define svc_centerprint 26 // [string] to put in center of the screen + +#define svc_killedmonster 27 +#define svc_foundsecret 28 + +#define svc_spawnstaticsound 29 // [coord3] [byte] samp [byte] vol [byte] aten + +#define svc_intermission 30 // [vec3_t] origin [vec3_t] angle +#define svc_finale 31 // [string] text + +#define svc_cdtrack 32 // [byte] track +#define svc_sellscreen 33 + +#define svc_smallkick 34 // set client punchangle to 2 +#define svc_bigkick 35 // set client punchangle to 4 + +#define svc_updateping 36 // [byte] [short] +#define svc_updateentertime 37 // [byte] [float] + +#define svc_updatestatlong 38 // [byte] [long] + +#define svc_muzzleflash 39 // [short] entity + +#define svc_updateuserinfo 40 // [byte] slot [long] uid + // [string] userinfo + +#define svc_download 41 // [short] size [size bytes] +#define svc_playerinfo 42 // variable +#define svc_nails 43 // [byte] num [48 bits] xyzpy 12 12 12 4 8 +#define svc_chokecount 44 // [byte] packets choked +#define svc_modellist 45 // [strings] +#define svc_soundlist 46 // [strings] +#define svc_packetentities 47 // [...] +#define svc_deltapacketentities 48 // [...] +#define svc_maxspeed 49 // maxspeed change, for prediction +#define svc_entgravity 50 // gravity change, for prediction +#define svc_setinfo 51 // setinfo on a client +#define svc_serverinfo 52 // serverinfo +#define svc_updatepl 53 // [byte] [byte] + +#define svc_nails2 54 // for interpolation, stores edict num + + + +//============================================== + +// +// client to server +// +#define clc_bad 0 +#define clc_nop 1 +//define clc_doublemove 2 +#define clc_move 3 // [[usercmd_t] +#define clc_stringcmd 4 // [string] message +#define clc_delta 5 // [byte] sequence number, requests delta compression of message +#define clc_tmove 6 // teleport request, spectator only +#define clc_upload 7 // teleport request, spectator only + + +//============================================== + +// +// demo recording +// + +#define dem_cmd 0 +#define dem_read 1 +#define dem_set 2 +#define dem_multiple 3 +#define dem_single 4 +#define dem_stats 5 +#define dem_all 6 + +//============================================== + +// playerinfo flags from server +// playerinfo always sends: playernum, flags, origin[] and framenumber + +#define PF_MSEC (1<<0) +#define PF_COMMAND (1<<1) +#define PF_VELOCITY1 (1<<2) +#define PF_VELOCITY2 (1<<3) +#define PF_VELOCITY3 (1<<4) +#define PF_MODEL (1<<5) +#define PF_SKINNUM (1<<6) +#define PF_EFFECTS (1<<7) +#define PF_WEAPONFRAME (1<<8) // only sent for view player +#define PF_DEAD (1<<9) // don't block movement any more +#define PF_GIB (1<<10) // offset the view height differently +#define PF_NOGRAV (1<<11) // don't apply gravity for prediction + +//============================================== + +// if the high bit of the client to server byte is set, the low bits are +// client move cmd bits +// ms and angle2 are always sent, the others are optional +#define CM_ANGLE1 (1<<0) +#define CM_ANGLE3 (1<<1) +#define CM_FORWARD (1<<2) +#define CM_SIDE (1<<3) +#define CM_UP (1<<4) +#define CM_BUTTONS (1<<5) +#define CM_IMPULSE (1<<6) +#define CM_ANGLE2 (1<<7) + +//============================================== + +// the first 16 bits of a packetentities update holds 9 bits +// of entity number and 7 bits of flags +#define U_ORIGIN1 (1<<9) +#define U_ORIGIN2 (1<<10) +#define U_ORIGIN3 (1<<11) +#define U_ANGLE2 (1<<12) +#define U_FRAME (1<<13) +#define U_REMOVE (1<<14) // REMOVE this entity, don't add it +#define U_MOREBITS (1<<15) + +// if MOREBITS is set, these additional flags are read in next +#define U_ANGLE1 (1<<0) +#define U_ANGLE3 (1<<1) +#define U_MODEL (1<<2) +#define U_COLORMAP (1<<3) +#define U_SKIN (1<<4) +#define U_EFFECTS (1<<5) +#define U_SOLID (1<<6) // the entity should be solid for prediction + +//============================================== + +// a sound with no channel is a local only sound +// the sound field has bits 0-2: channel, 3-12: entity +#define SND_VOLUME (1<<15) // a byte +#define SND_ATTENUATION (1<<14) // a byte + +#define DEFAULT_SOUND_PACKET_VOLUME 255 +#define DEFAULT_SOUND_PACKET_ATTENUATION 1.0 + +// svc_print messages have an id, so messages can be filtered +#define PRINT_LOW 0 +#define PRINT_MEDIUM 1 +#define PRINT_HIGH 2 +#define PRINT_CHAT 3 // also go to chat buffer + +// +// temp entity events +// +#define TE_SPIKE 0 +#define TE_SUPERSPIKE 1 +#define TE_GUNSHOT 2 +#define TE_EXPLOSION 3 +#define TE_TAREXPLOSION 4 +#define TE_LIGHTNING1 5 +#define TE_LIGHTNING2 6 +#define TE_WIZSPIKE 7 +#define TE_KNIGHTSPIKE 8 +#define TE_LIGHTNING3 9 +#define TE_LAVASPLASH 10 +#define TE_TELEPORT 11 +#define TE_BLOOD 12 +#define TE_LIGHTNINGBLOOD 13 + + +/* +========================================================== + + ELEMENTS COMMUNICATED ACROSS THE NET + +========================================================== +*/ + +#define MAX_CLIENTS 32 + +#define UPDATE_BACKUP 64 // copies of entity_state_t to keep buffered + // must be power of two +#define UPDATE_MASK (UPDATE_BACKUP-1) + +// entity_state_t is the information conveyed from the server +// in an update message +typedef struct +{ + int number; // edict index + + int flags; // nolerp, etc + vec3_t origin; + vec3_t angles; + int modelindex; + int frame; + int colormap; + int skinnum; + int effects; +} entity_state_t; + +#ifdef SERVERONLY + +#define MAX_PACKET_ENTITIES 64 // doesn't count nails +#define MAX_DEMO_PACKET_ENTITIES 196 +typedef struct +{ + int num_entities; + entity_state_t *entities; +} packet_entities_t; + +#else + +#define MAX_PACKET_ENTITIES 196 // doesn't count nails +typedef struct +{ + int num_entities; + entity_state_t entities[MAX_PACKET_ENTITIES]; +} packet_entities_t; + +#endif + +typedef struct usercmd_s +{ + byte msec; + vec3_t angles; + short forwardmove, sidemove, upmove; + byte buttons; + byte impulse; +} usercmd_t; + diff --git a/source/quakeasm.h b/source/quakeasm.h index dbad50db..c55fed46 100644 --- a/source/quakeasm.h +++ b/source/quakeasm.h @@ -1,271 +1,271 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// -// quakeasm.h: general asm header file -// - -#ifdef _WIN32 -#define __i386__ 1 -#endif - -#ifdef __i386__ -#define id386 1 -#else -#define id386 0 -#endif - -#ifndef SERVERONLY - -// !!! must be kept the same as in d_iface.h !!! -#define TRANSPARENT_COLOR 255 - -#ifndef NeXT -#ifndef GLQUAKE - .extern C(d_zistepu) - .extern C(d_pzbuffer) - .extern C(d_zistepv) - .extern C(d_zrowbytes) - .extern C(d_ziorigin) - .extern C(r_turb_s) - .extern C(r_turb_t) - .extern C(r_turb_pdest) - .extern C(r_turb_spancount) - .extern C(r_turb_turb) - .extern C(r_turb_pbase) - .extern C(r_turb_sstep) - .extern C(r_turb_tstep) - .extern C(r_bmodelactive) - .extern C(d_sdivzstepu) - .extern C(d_tdivzstepu) - .extern C(d_sdivzstepv) - .extern C(d_tdivzstepv) - .extern C(d_sdivzorigin) - .extern C(d_tdivzorigin) - .extern C(sadjust) - .extern C(tadjust) - .extern C(bbextents) - .extern C(bbextentt) - .extern C(cacheblock) - .extern C(d_viewbuffer) - .extern C(cachewidth) - .extern C(d_pzbuffer) - .extern C(d_zrowbytes) - .extern C(d_zwidth) - .extern C(d_scantable) - .extern C(r_lightptr) - .extern C(r_numvblocks) - .extern C(prowdestbase) - .extern C(pbasesource) - .extern C(r_lightwidth) - .extern C(lightright) - .extern C(lightrightstep) - .extern C(lightdeltastep) - .extern C(lightdelta) - .extern C(lightright) - .extern C(lightdelta) - .extern C(sourcetstep) - .extern C(surfrowbytes) - .extern C(lightrightstep) - .extern C(lightdeltastep) - .extern C(r_sourcemax) - .extern C(r_stepback) - .extern C(colormap) - .extern C(blocksize) - .extern C(sourcesstep) - .extern C(lightleft) - .extern C(blockdivshift) - .extern C(blockdivmask) - .extern C(lightleftstep) - .extern C(r_origin) - .extern C(r_ppn) - .extern C(r_pup) - .extern C(r_pright) - .extern C(ycenter) - .extern C(xcenter) - .extern C(d_vrectbottom_particle) - .extern C(d_vrectright_particle) - .extern C(d_vrecty) - .extern C(d_vrectx) - .extern C(d_pix_shift) - .extern C(d_pix_min) - .extern C(d_pix_max) - .extern C(d_y_aspect_shift) - .extern C(screenwidth) - .extern C(r_leftclipped) - .extern C(r_leftenter) - .extern C(r_rightclipped) - .extern C(r_rightenter) - .extern C(modelorg) - .extern C(xscale) - .extern C(r_refdef) - .extern C(yscale) - .extern C(r_leftexit) - .extern C(r_rightexit) - .extern C(r_lastvertvalid) - .extern C(cacheoffset) - .extern C(newedges) - .extern C(removeedges) - .extern C(r_pedge) - .extern C(r_framecount) - .extern C(r_u1) - .extern C(r_emitted) - .extern C(edge_p) - .extern C(surface_p) - .extern C(surfaces) - .extern C(r_lzi1) - .extern C(r_v1) - .extern C(r_ceilv1) - .extern C(r_nearzi) - .extern C(r_nearzionly) - .extern C(edge_aftertail) - .extern C(edge_tail) - .extern C(current_iv) - .extern C(edge_head_u_shift20) - .extern C(span_p) - .extern C(edge_head) - .extern C(fv) - .extern C(edge_tail_u_shift20) - .extern C(r_apverts) - .extern C(r_anumverts) - .extern C(aliastransform) - .extern C(r_avertexnormals) - .extern C(r_plightvec) - .extern C(r_ambientlight) - .extern C(r_shadelight) - .extern C(aliasxcenter) - .extern C(aliasycenter) - .extern C(a_sstepxfrac) - .extern C(r_affinetridesc) - .extern C(acolormap) - .extern C(d_pcolormap) - .extern C(r_affinetridesc) - .extern C(d_sfrac) - .extern C(d_ptex) - .extern C(d_pedgespanpackage) - .extern C(d_tfrac) - .extern C(d_light) - .extern C(d_zi) - .extern C(d_pdest) - .extern C(d_pz) - .extern C(d_aspancount) - .extern C(erroradjustup) - .extern C(errorterm) - .extern C(d_xdenom) - .extern C(r_p0) - .extern C(r_p1) - .extern C(r_p2) - .extern C(a_tstepxfrac) - .extern C(r_sstepx) - .extern C(r_tstepx) - .extern C(a_ststepxwhole) - .extern C(zspantable) - .extern C(skintable) - .extern C(r_zistepx) - .extern C(erroradjustdown) - .extern C(d_countextrastep) - .extern C(ubasestep) - .extern C(a_ststepxwhole) - .extern C(a_tstepxfrac) - .extern C(r_lstepx) - .extern C(a_spans) - .extern C(erroradjustdown) - .extern C(d_pdestextrastep) - .extern C(d_pzextrastep) - .extern C(d_sfracextrastep) - .extern C(d_ptexextrastep) - .extern C(d_countextrastep) - .extern C(d_tfracextrastep) - .extern C(d_lightextrastep) - .extern C(d_ziextrastep) - .extern C(d_pdestbasestep) - .extern C(d_pzbasestep) - .extern C(d_sfracbasestep) - .extern C(d_ptexbasestep) - .extern C(ubasestep) - .extern C(d_tfracbasestep) - .extern C(d_lightbasestep) - .extern C(d_zibasestep) - .extern C(zspantable) - .extern C(r_lstepy) - .extern C(r_sstepy) - .extern C(r_tstepy) - .extern C(r_zistepy) - .extern C(D_PolysetSetEdgeTable) - .extern C(D_RasterizeAliasPolySmooth) - - .extern float_point5 - .extern Float2ToThe31nd - .extern izistep - .extern izi - .extern FloatMinus2ToThe31nd - .extern float_1 - .extern float_particle_z_clip - .extern float_minus_1 - .extern float_0 - .extern fp_16 - .extern fp_64k - .extern fp_1m - .extern fp_1m_minus_1 - .extern fp_8 - .extern entryvec_table - .extern advancetable - .extern sstep - .extern tstep - .extern pspantemp - .extern counttemp - .extern jumptemp - .extern reciprocal_table - .extern DP_Count - .extern DP_u - .extern DP_v - .extern DP_32768 - .extern DP_Color - .extern DP_Pix - .extern DP_EntryTable - .extern pbase - .extern s - .extern t - .extern sfracf - .extern tfracf - .extern snext - .extern tnext - .extern spancountminus1 - .extern zi16stepu - .extern sdivz16stepu - .extern tdivz16stepu - .extern zi8stepu - .extern sdivz8stepu - .extern tdivz8stepu - .extern reciprocal_table_16 - .extern entryvec_table_16 - .extern ceil_cw - .extern single_cw - .extern fp_64kx64k - .extern pz - .extern spr8entryvec_table -#endif - - .extern C(vright) - .extern C(vup) - .extern C(vpn) - -#endif - -#endif // SERVERONLY +/* +Copyright (C) 1996-1997 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. + +*/ +// +// quakeasm.h: general asm header file +// + +#ifdef _WIN32 +#define __i386__ 1 +#endif + +#ifdef __i386__ +#define id386 1 +#else +#define id386 0 +#endif + +#ifndef SERVERONLY + +// !!! must be kept the same as in d_iface.h !!! +#define TRANSPARENT_COLOR 255 + +#ifndef NeXT +#ifndef GLQUAKE + .extern C(d_zistepu) + .extern C(d_pzbuffer) + .extern C(d_zistepv) + .extern C(d_zrowbytes) + .extern C(d_ziorigin) + .extern C(r_turb_s) + .extern C(r_turb_t) + .extern C(r_turb_pdest) + .extern C(r_turb_spancount) + .extern C(r_turb_turb) + .extern C(r_turb_pbase) + .extern C(r_turb_sstep) + .extern C(r_turb_tstep) + .extern C(r_bmodelactive) + .extern C(d_sdivzstepu) + .extern C(d_tdivzstepu) + .extern C(d_sdivzstepv) + .extern C(d_tdivzstepv) + .extern C(d_sdivzorigin) + .extern C(d_tdivzorigin) + .extern C(sadjust) + .extern C(tadjust) + .extern C(bbextents) + .extern C(bbextentt) + .extern C(cacheblock) + .extern C(d_viewbuffer) + .extern C(cachewidth) + .extern C(d_pzbuffer) + .extern C(d_zrowbytes) + .extern C(d_zwidth) + .extern C(d_scantable) + .extern C(r_lightptr) + .extern C(r_numvblocks) + .extern C(prowdestbase) + .extern C(pbasesource) + .extern C(r_lightwidth) + .extern C(lightright) + .extern C(lightrightstep) + .extern C(lightdeltastep) + .extern C(lightdelta) + .extern C(lightright) + .extern C(lightdelta) + .extern C(sourcetstep) + .extern C(surfrowbytes) + .extern C(lightrightstep) + .extern C(lightdeltastep) + .extern C(r_sourcemax) + .extern C(r_stepback) + .extern C(colormap) + .extern C(blocksize) + .extern C(sourcesstep) + .extern C(lightleft) + .extern C(blockdivshift) + .extern C(blockdivmask) + .extern C(lightleftstep) + .extern C(r_origin) + .extern C(r_ppn) + .extern C(r_pup) + .extern C(r_pright) + .extern C(ycenter) + .extern C(xcenter) + .extern C(d_vrectbottom_particle) + .extern C(d_vrectright_particle) + .extern C(d_vrecty) + .extern C(d_vrectx) + .extern C(d_pix_shift) + .extern C(d_pix_min) + .extern C(d_pix_max) + .extern C(d_y_aspect_shift) + .extern C(screenwidth) + .extern C(r_leftclipped) + .extern C(r_leftenter) + .extern C(r_rightclipped) + .extern C(r_rightenter) + .extern C(modelorg) + .extern C(xscale) + .extern C(r_refdef) + .extern C(yscale) + .extern C(r_leftexit) + .extern C(r_rightexit) + .extern C(r_lastvertvalid) + .extern C(cacheoffset) + .extern C(newedges) + .extern C(removeedges) + .extern C(r_pedge) + .extern C(r_framecount) + .extern C(r_u1) + .extern C(r_emitted) + .extern C(edge_p) + .extern C(surface_p) + .extern C(surfaces) + .extern C(r_lzi1) + .extern C(r_v1) + .extern C(r_ceilv1) + .extern C(r_nearzi) + .extern C(r_nearzionly) + .extern C(edge_aftertail) + .extern C(edge_tail) + .extern C(current_iv) + .extern C(edge_head_u_shift20) + .extern C(span_p) + .extern C(edge_head) + .extern C(fv) + .extern C(edge_tail_u_shift20) + .extern C(r_apverts) + .extern C(r_anumverts) + .extern C(aliastransform) + .extern C(r_avertexnormals) + .extern C(r_plightvec) + .extern C(r_ambientlight) + .extern C(r_shadelight) + .extern C(aliasxcenter) + .extern C(aliasycenter) + .extern C(a_sstepxfrac) + .extern C(r_affinetridesc) + .extern C(acolormap) + .extern C(d_pcolormap) + .extern C(r_affinetridesc) + .extern C(d_sfrac) + .extern C(d_ptex) + .extern C(d_pedgespanpackage) + .extern C(d_tfrac) + .extern C(d_light) + .extern C(d_zi) + .extern C(d_pdest) + .extern C(d_pz) + .extern C(d_aspancount) + .extern C(erroradjustup) + .extern C(errorterm) + .extern C(d_xdenom) + .extern C(r_p0) + .extern C(r_p1) + .extern C(r_p2) + .extern C(a_tstepxfrac) + .extern C(r_sstepx) + .extern C(r_tstepx) + .extern C(a_ststepxwhole) + .extern C(zspantable) + .extern C(skintable) + .extern C(r_zistepx) + .extern C(erroradjustdown) + .extern C(d_countextrastep) + .extern C(ubasestep) + .extern C(a_ststepxwhole) + .extern C(a_tstepxfrac) + .extern C(r_lstepx) + .extern C(a_spans) + .extern C(erroradjustdown) + .extern C(d_pdestextrastep) + .extern C(d_pzextrastep) + .extern C(d_sfracextrastep) + .extern C(d_ptexextrastep) + .extern C(d_countextrastep) + .extern C(d_tfracextrastep) + .extern C(d_lightextrastep) + .extern C(d_ziextrastep) + .extern C(d_pdestbasestep) + .extern C(d_pzbasestep) + .extern C(d_sfracbasestep) + .extern C(d_ptexbasestep) + .extern C(ubasestep) + .extern C(d_tfracbasestep) + .extern C(d_lightbasestep) + .extern C(d_zibasestep) + .extern C(zspantable) + .extern C(r_lstepy) + .extern C(r_sstepy) + .extern C(r_tstepy) + .extern C(r_zistepy) + .extern C(D_PolysetSetEdgeTable) + .extern C(D_RasterizeAliasPolySmooth) + + .extern float_point5 + .extern Float2ToThe31nd + .extern izistep + .extern izi + .extern FloatMinus2ToThe31nd + .extern float_1 + .extern float_particle_z_clip + .extern float_minus_1 + .extern float_0 + .extern fp_16 + .extern fp_64k + .extern fp_1m + .extern fp_1m_minus_1 + .extern fp_8 + .extern entryvec_table + .extern advancetable + .extern sstep + .extern tstep + .extern pspantemp + .extern counttemp + .extern jumptemp + .extern reciprocal_table + .extern DP_Count + .extern DP_u + .extern DP_v + .extern DP_32768 + .extern DP_Color + .extern DP_Pix + .extern DP_EntryTable + .extern pbase + .extern s + .extern t + .extern sfracf + .extern tfracf + .extern snext + .extern tnext + .extern spancountminus1 + .extern zi16stepu + .extern sdivz16stepu + .extern tdivz16stepu + .extern zi8stepu + .extern sdivz8stepu + .extern tdivz8stepu + .extern reciprocal_table_16 + .extern entryvec_table_16 + .extern ceil_cw + .extern single_cw + .extern fp_64kx64k + .extern pz + .extern spr8entryvec_table +#endif + + .extern C(vright) + .extern C(vup) + .extern C(vpn) + +#endif + +#endif // SERVERONLY diff --git a/source/quakedef.h b/source/quakedef.h index 4d633578..e19356f3 100644 --- a/source/quakedef.h +++ b/source/quakedef.h @@ -1,140 +1,140 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// quakedef.h -- primary header for client - -#define QUAKE_GAME // as opposed to utilities - -//define PARANOID // speed sapping error checking - -#ifdef _WIN32 -#pragma warning( disable : 4244 4127 4201 4214 4514 4305 4115 4018) -#endif - - -#include -#include -#include -#include -#include -#include -#include - -#include "bothdefs.h" - -#include "common.h" -//#include "bspfile.h" -#include "vid.h" -#include "sys.h" -#include "zone.h" -#include "mathlib.h" -//#include "wad.h" -#include "draw.h" -#include "cvar.h" -#include "screen.h" -#include "net.h" -#include "protocol.h" -#include "cmd.h" -//#include "sbar.h" -//#include "sound.h" -#include "render.h" -#include "client.h" - -#ifdef GLQUAKE -#include "gl_model.h" -#else -#include "model.h" -#include "d_iface.h" -#endif - -#include "input.h" -//#include "keys.h" -#include "console.h" -#include "view.h" -#include "menu.h" -#include "crc.h" -#include "cdaudio.h" -//#include "pmove.h" - -#ifdef GLQUAKE -#include "glquake.h" -#endif - -#ifndef max -#define max(a,b) ((a) > (b) ? (a) : (b)) -#define min(a,b) ((a) < (b) ? (a) : (b)) -#endif - -//============================================================================= - -// the host system specifies the base of the directory tree, the -// command line parms passed to the program, and the amount of memory -// available for the program to use - -typedef struct -{ - char *basedir; - int argc; - char **argv; - void *membase; - int memsize; -} quakeparms_t; - - -//============================================================================= - -#define MAX_NUM_ARGVS 50 - - -// -// host -// -extern quakeparms_t host_parms; - -extern cvar_t sys_ticrate; -extern cvar_t sys_nostdout; -extern cvar_t developer; - -extern cvar_t password; - -extern qboolean host_initialized; // true if into command execution -extern double host_frametime; -extern byte *host_basepal; -extern byte *host_colormap; -extern double real_frametime; -extern double noscale_realtime; -extern int host_framecount; // incremented every frame, never reset -extern double realtime; // not bounded in any way, changed at - // start of every frame, never reset - -void Host_ServerFrame (void); -void Host_InitCommands (void); -void Host_Init (quakeparms_t *parms); -void Host_Shutdown(void); -void Host_Error (char *error, ...); -void Host_EndGame (char *message, ...); -//qboolean Host_SimulationTime(float time); -void Host_Frame (double time); -void Host_Quit_f (void); -void Host_ClientCommands (char *fmt, ...); -void Host_ShutdownServer (qboolean crash); - -extern qboolean msg_suppress_1; // suppresses resolution and cache size console output - // an fullscreen DIB focus gain/loss - +/* +Copyright (C) 1996-1997 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. + +*/ +// quakedef.h -- primary header for client + +#define QUAKE_GAME // as opposed to utilities + +//define PARANOID // speed sapping error checking + +#ifdef _WIN32 +#pragma warning( disable : 4244 4127 4201 4214 4514 4305 4115 4018) +#endif + + +#include +#include +#include +#include +#include +#include +#include + +#include "bothdefs.h" + +#include "common.h" +//#include "bspfile.h" +#include "vid.h" +#include "sys.h" +#include "zone.h" +#include "mathlib.h" +//#include "wad.h" +#include "draw.h" +#include "cvar.h" +#include "screen.h" +#include "net.h" +#include "protocol.h" +#include "cmd.h" +//#include "sbar.h" +//#include "sound.h" +#include "render.h" +#include "client.h" + +#ifdef GLQUAKE +#include "gl_model.h" +#else +#include "model.h" +#include "d_iface.h" +#endif + +#include "input.h" +//#include "keys.h" +#include "console.h" +#include "view.h" +#include "menu.h" +#include "crc.h" +#include "cdaudio.h" +//#include "pmove.h" + +#ifdef GLQUAKE +#include "glquake.h" +#endif + +#ifndef max +#define max(a,b) ((a) > (b) ? (a) : (b)) +#define min(a,b) ((a) < (b) ? (a) : (b)) +#endif + +//============================================================================= + +// the host system specifies the base of the directory tree, the +// command line parms passed to the program, and the amount of memory +// available for the program to use + +typedef struct +{ + char *basedir; + int argc; + char **argv; + void *membase; + int memsize; +} quakeparms_t; + + +//============================================================================= + +#define MAX_NUM_ARGVS 50 + + +// +// host +// +extern quakeparms_t host_parms; + +extern cvar_t sys_ticrate; +extern cvar_t sys_nostdout; +extern cvar_t developer; + +extern cvar_t password; + +extern qboolean host_initialized; // true if into command execution +extern double host_frametime; +extern byte *host_basepal; +extern byte *host_colormap; +extern double real_frametime; +extern double noscale_realtime; +extern int host_framecount; // incremented every frame, never reset +extern double realtime; // not bounded in any way, changed at + // start of every frame, never reset + +void Host_ServerFrame (void); +void Host_InitCommands (void); +void Host_Init (quakeparms_t *parms); +void Host_Shutdown(void); +void Host_Error (char *error, ...); +void Host_EndGame (char *message, ...); +//qboolean Host_SimulationTime(float time); +void Host_Frame (double time); +void Host_Quit_f (void); +void Host_ClientCommands (char *fmt, ...); +void Host_ShutdownServer (qboolean crash); + +extern qboolean msg_suppress_1; // suppresses resolution and cache size console output + // an fullscreen DIB focus gain/loss + diff --git a/source/qwsv.dsp b/source/qwsv.dsp index 988bbc71..b48150ef 100644 --- a/source/qwsv.dsp +++ b/source/qwsv.dsp @@ -156,6 +156,10 @@ SOURCE=.\sv_init.c # End Source File # Begin Source File +SOURCE=.\sv_login.c +# End Source File +# Begin Source File + SOURCE=.\sv_main.c # End Source File # Begin Source File @@ -389,7 +393,7 @@ InputName=math cl /nologo /EP > $(OUTDIR)\$(InputName).spp $(InputPath) gas2masm < $(OUTDIR)\$(InputName).spp >$(OUTDIR)\$(InputName).asm ml /nologo /c /Cp /coff /Fo$(OUTDIR)\$(InputName).obj /Zm /Zi\ - $(OUTDIR)\$(InputName).asm + $(OUTDIR)\$(InputName).asm del $(OUTDIR)\$(InputName).spp # End Custom Build @@ -416,7 +420,7 @@ InputName=worlda cl /nologo /EP > $(OUTDIR)\$(InputName).spp $(InputPath) gas2masm < $(OUTDIR)\$(InputName).spp >$(OUTDIR)\$(InputName).asm ml /nologo /c /Cp /coff /Fo$(OUTDIR)\$(InputName).obj /Zm /Zi\ - $(OUTDIR)\$(InputName).asm + $(OUTDIR)\$(InputName).asm del $(OUTDIR)\$(InputName).spp # End Custom Build diff --git a/source/qwsvdef.h b/source/qwsvdef.h index 6665a393..24d60aca 100644 --- a/source/qwsvdef.h +++ b/source/qwsvdef.h @@ -1,103 +1,114 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// qwsvdef.h -- primary header for server - -#define QUAKE_GAME // as opposed to utilities - -//define PARANOID // speed sapping error checking - -#ifdef _WIN32 -#pragma warning( disable : 4244 4127 4201 4214 4514 4305 4115 4018) -#endif - -#include -#include -#include -#include -#include -#include -#include - -#include "bothdefs.h" - -#include "common.h" -#include "bspfile.h" -#include "sys.h" -#include "zone.h" -#include "mathlib.h" - -#include "cvar.h" -#include "net.h" -#include "protocol.h" -#include "cmd.h" - -#ifdef GLQUAKE -//FIXME: we don't need vid.h and render.h here -#include "vid.h" -#include "render.h" -#include "gl_model.h" -#else -#include "model.h" -#endif - -#include "crc.h" - -#include "server.h" -#include "world.h" -#include "pmove.h" - -//============================================================================= - -// the host system specifies the base of the directory tree, the -// command line parms passed to the program, and the amount of memory -// available for the program to use - -typedef struct -{ - char *basedir; - int argc; - char **argv; - void *membase; - int memsize; -} quakeparms_t; - - -//============================================================================= - -// -// host -// -extern quakeparms_t host_parms; - -extern cvar_t sys_nostdout; -extern cvar_t developer; - -extern qboolean host_initialized; // true if into command execution -//extern double host_frametime; -extern double realtime; // not bounded in any way, changed at - // start of every frame, never reset -extern double sv_frametime; - -void SV_Error (char *error, ...); -void SV_Init (quakeparms_t *parms); - -void Con_Printf (char *fmt, ...); -void Con_DPrintf (char *fmt, ...); - +/* +Copyright (C) 1996-1997 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. + +*/ +// qwsvdef.h -- primary header for server + +#define QUAKE_GAME // as opposed to utilities + +//define PARANOID // speed sapping error checking + +#ifdef _WIN32 +#pragma warning( disable : 4244 4127 4201 4214 4514 4305 4115 4018) +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "bothdefs.h" + +#include "common.h" +#include "bspfile.h" +#include "sys.h" +#include "zone.h" +#include "mathlib.h" + +#include "cvar.h" +#include "net.h" +#include "protocol.h" +#include "cmd.h" + +#ifdef GLQUAKE +//FIXME: we don't need vid.h and render.h here +#include "vid.h" +#include "render.h" +#include "gl_model.h" +#else +#include "model.h" +#endif + +#include "crc.h" + +#include "server.h" +#include "world.h" +#include "pmove.h" + +//============================================================================= + +// the host system specifies the base of the directory tree, the +// command line parms passed to the program, and the amount of memory +// available for the program to use + +typedef struct +{ + char *basedir; + int argc; + char **argv; + void *membase; + int memsize; +} quakeparms_t; + + +//============================================================================= + +typedef struct +{ + int sec; + int min; + int hour; + int day; + int mon; + int year; + char str[128]; +} date_t; + +// +// host +// +extern quakeparms_t host_parms; + +extern cvar_t sys_nostdout; +extern cvar_t developer; + +extern qboolean host_initialized; // true if into command execution +//extern double host_frametime; +extern double realtime; // not bounded in any way, changed at + // start of every frame, never reset +extern float sv_frametime; + +void SV_Error (char *error, ...); +void SV_Init (quakeparms_t *parms); + +void Con_Printf (char *fmt, ...); +void Con_DPrintf (char *fmt, ...); + diff --git a/source/r_aclip.c b/source/r_aclip.c index 2d8dbbdf..61fef0cd 100644 --- a/source/r_aclip.c +++ b/source/r_aclip.c @@ -1,350 +1,350 @@ -/* -Copyright (C) 1996-1997 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 "quakedef.h" -#include "r_local.h" -#include "d_local.h" - -static finalvert_t fv[2][8]; -static auxvert_t av[8]; - -void R_AliasProjectFinalVert (finalvert_t *fv, auxvert_t *av); -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; - auxvert_t *pav0, *pav1, avout; - - pav0 = &av[pfv0 - &fv[0][0]]; - pav1 = &av[pfv1 - &fv[0][0]]; - - if (pfv0->v[1] >= pfv1->v[1]) - { - scale = (ALIAS_Z_CLIP_PLANE - pav0->fv[2]) / - (pav1->fv[2] - pav0->fv[2]); - - avout.fv[0] = pav0->fv[0] + (pav1->fv[0] - pav0->fv[0]) * scale; - avout.fv[1] = pav0->fv[1] + (pav1->fv[1] - pav0->fv[1]) * scale; - avout.fv[2] = ALIAS_Z_CLIP_PLANE; - - out->v[2] = pfv0->v[2] + (pfv1->v[2] - pfv0->v[2]) * scale; - out->v[3] = pfv0->v[3] + (pfv1->v[3] - pfv0->v[3]) * scale; - out->v[4] = pfv0->v[4] + (pfv1->v[4] - pfv0->v[4]) * scale; - } - else - { - scale = (ALIAS_Z_CLIP_PLANE - pav1->fv[2]) / - (pav0->fv[2] - pav1->fv[2]); - - avout.fv[0] = pav1->fv[0] + (pav0->fv[0] - pav1->fv[0]) * scale; - avout.fv[1] = pav1->fv[1] + (pav0->fv[1] - pav1->fv[1]) * scale; - avout.fv[2] = ALIAS_Z_CLIP_PLANE; - - out->v[2] = pfv1->v[2] + (pfv0->v[2] - pfv1->v[2]) * scale; - out->v[3] = pfv1->v[3] + (pfv0->v[3] - pfv1->v[3]) * scale; - out->v[4] = pfv1->v[4] + (pfv0->v[4] - pfv1->v[4]) * scale; - } - - R_AliasProjectFinalVert (out, &avout); - - if (out->v[0] < r_refdef.aliasvrect.x) - out->flags |= ALIAS_LEFT_CLIP; - if (out->v[1] < r_refdef.aliasvrect.y) - out->flags |= ALIAS_TOP_CLIP; - if (out->v[0] > r_refdef.aliasvrectright) - out->flags |= ALIAS_RIGHT_CLIP; - if (out->v[1] > r_refdef.aliasvrectbottom) - out->flags |= ALIAS_BOTTOM_CLIP; -} - - -#if !id386 - -void R_Alias_clip_left (finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out) -{ - float scale; - int i; - - if (pfv0->v[1] >= pfv1->v[1]) - { - scale = (float)(r_refdef.aliasvrect.x - pfv0->v[0]) / - (pfv1->v[0] - pfv0->v[0]); - for (i=0 ; i<6 ; i++) - out->v[i] = pfv0->v[i] + (pfv1->v[i] - pfv0->v[i])*scale + 0.5; - } - else - { - scale = (float)(r_refdef.aliasvrect.x - pfv1->v[0]) / - (pfv0->v[0] - pfv1->v[0]); - for (i=0 ; i<6 ; i++) - out->v[i] = pfv1->v[i] + (pfv0->v[i] - pfv1->v[i])*scale + 0.5; - } -} - - -void R_Alias_clip_right (finalvert_t *pfv0, finalvert_t *pfv1, - finalvert_t *out) -{ - float scale; - int i; - - if (pfv0->v[1] >= pfv1->v[1]) - { - scale = (float)(r_refdef.aliasvrectright - pfv0->v[0]) / - (pfv1->v[0] - pfv0->v[0]); - for (i=0 ; i<6 ; i++) - out->v[i] = pfv0->v[i] + (pfv1->v[i] - pfv0->v[i])*scale + 0.5; - } - else - { - scale = (float)(r_refdef.aliasvrectright - pfv1->v[0]) / - (pfv0->v[0] - pfv1->v[0]); - for (i=0 ; i<6 ; i++) - out->v[i] = pfv1->v[i] + (pfv0->v[i] - pfv1->v[i])*scale + 0.5; - } -} - - -void R_Alias_clip_top (finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out) -{ - float scale; - int i; - - if (pfv0->v[1] >= pfv1->v[1]) - { - scale = (float)(r_refdef.aliasvrect.y - pfv0->v[1]) / - (pfv1->v[1] - pfv0->v[1]); - for (i=0 ; i<6 ; i++) - out->v[i] = pfv0->v[i] + (pfv1->v[i] - pfv0->v[i])*scale + 0.5; - } - else - { - scale = (float)(r_refdef.aliasvrect.y - pfv1->v[1]) / - (pfv0->v[1] - pfv1->v[1]); - for (i=0 ; i<6 ; i++) - out->v[i] = pfv1->v[i] + (pfv0->v[i] - pfv1->v[i])*scale + 0.5; - } -} - - -void R_Alias_clip_bottom (finalvert_t *pfv0, finalvert_t *pfv1, - finalvert_t *out) -{ - float scale; - int i; - - if (pfv0->v[1] >= pfv1->v[1]) - { - scale = (float)(r_refdef.aliasvrectbottom - pfv0->v[1]) / - (pfv1->v[1] - pfv0->v[1]); - - for (i=0 ; i<6 ; i++) - out->v[i] = pfv0->v[i] + (pfv1->v[i] - pfv0->v[i])*scale + 0.5; - } - else - { - scale = (float)(r_refdef.aliasvrectbottom - pfv1->v[1]) / - (pfv0->v[1] - pfv1->v[1]); - - for (i=0 ; i<6 ; i++) - out->v[i] = pfv1->v[i] + (pfv0->v[i] - pfv1->v[i])*scale + 0.5; - } -} - -#endif - - -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; - int flags, oldflags; - - j = count-1; - k = 0; - for (i=0 ; i r_refdef.aliasvrectright) - out[k].flags |= ALIAS_RIGHT_CLIP; - if (out[k].v[1] > r_refdef.aliasvrectbottom) - out[k].flags |= ALIAS_BOTTOM_CLIP; - k++; - } - if (!flags) - { - out[k] = in[i]; - k++; - } - } - - return k; -} - - -/* -================ -R_AliasClipTriangle -================ -*/ -void R_AliasClipTriangle (mtriangle_t *ptri) -{ - int i, k, pingpong; - mtriangle_t mtri; - unsigned clipflags; - -// copy vertexes and fix seam texture coordinates - if (ptri->facesfront) - { - fv[0][0] = pfinalverts[ptri->vertindex[0]]; - fv[0][1] = pfinalverts[ptri->vertindex[1]]; - fv[0][2] = pfinalverts[ptri->vertindex[2]]; - } - else - { - for (i=0 ; i<3 ; i++) - { - fv[0][i] = pfinalverts[ptri->vertindex[i]]; - - if (!ptri->facesfront && (fv[0][i].flags & ALIAS_ONSEAM) ) - fv[0][i].v[2] += r_affinetridesc.seamfixupX16; - } - } - -// clip - clipflags = fv[0][0].flags | fv[0][1].flags | fv[0][2].flags; - - if (clipflags & ALIAS_Z_CLIP) - { - for (i=0 ; i<3 ; i++) - av[i] = pauxverts[ptri->vertindex[i]]; - - 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].v[0] = r_refdef.aliasvrectright; - - if (fv[pingpong][i].v[1] < r_refdef.aliasvrect.y) - fv[pingpong][i].v[1] = r_refdef.aliasvrect.y; - else if (fv[pingpong][i].v[1] > r_refdef.aliasvrectbottom) - fv[pingpong][i].v[1] = r_refdef.aliasvrectbottom; - - fv[pingpong][i].flags = 0; - } - -// draw triangles - mtri.facesfront = ptri->facesfront; - r_affinetridesc.ptriangles = &mtri; - r_affinetridesc.pfinalverts = fv[pingpong]; - -// FIXME: do all at once as trifan? - mtri.vertindex[0] = 0; - for (i=1 ; iv[1] >= pfv1->v[1]) + { + scale = (ALIAS_Z_CLIP_PLANE - pav0->fv[2]) / + (pav1->fv[2] - pav0->fv[2]); + + avout.fv[0] = pav0->fv[0] + (pav1->fv[0] - pav0->fv[0]) * scale; + avout.fv[1] = pav0->fv[1] + (pav1->fv[1] - pav0->fv[1]) * scale; + avout.fv[2] = ALIAS_Z_CLIP_PLANE; + + out->v[2] = pfv0->v[2] + (pfv1->v[2] - pfv0->v[2]) * scale; + out->v[3] = pfv0->v[3] + (pfv1->v[3] - pfv0->v[3]) * scale; + out->v[4] = pfv0->v[4] + (pfv1->v[4] - pfv0->v[4]) * scale; + } + else + { + scale = (ALIAS_Z_CLIP_PLANE - pav1->fv[2]) / + (pav0->fv[2] - pav1->fv[2]); + + avout.fv[0] = pav1->fv[0] + (pav0->fv[0] - pav1->fv[0]) * scale; + avout.fv[1] = pav1->fv[1] + (pav0->fv[1] - pav1->fv[1]) * scale; + avout.fv[2] = ALIAS_Z_CLIP_PLANE; + + out->v[2] = pfv1->v[2] + (pfv0->v[2] - pfv1->v[2]) * scale; + out->v[3] = pfv1->v[3] + (pfv0->v[3] - pfv1->v[3]) * scale; + out->v[4] = pfv1->v[4] + (pfv0->v[4] - pfv1->v[4]) * scale; + } + + R_AliasProjectFinalVert (out, &avout); + + if (out->v[0] < r_refdef.aliasvrect.x) + out->flags |= ALIAS_LEFT_CLIP; + if (out->v[1] < r_refdef.aliasvrect.y) + out->flags |= ALIAS_TOP_CLIP; + if (out->v[0] > r_refdef.aliasvrectright) + out->flags |= ALIAS_RIGHT_CLIP; + if (out->v[1] > r_refdef.aliasvrectbottom) + out->flags |= ALIAS_BOTTOM_CLIP; +} + + +#if !id386 + +void R_Alias_clip_left (finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out) +{ + float scale; + int i; + + if (pfv0->v[1] >= pfv1->v[1]) + { + scale = (float)(r_refdef.aliasvrect.x - pfv0->v[0]) / + (pfv1->v[0] - pfv0->v[0]); + for (i=0 ; i<6 ; i++) + out->v[i] = pfv0->v[i] + (pfv1->v[i] - pfv0->v[i])*scale + 0.5; + } + else + { + scale = (float)(r_refdef.aliasvrect.x - pfv1->v[0]) / + (pfv0->v[0] - pfv1->v[0]); + for (i=0 ; i<6 ; i++) + out->v[i] = pfv1->v[i] + (pfv0->v[i] - pfv1->v[i])*scale + 0.5; + } +} + + +void R_Alias_clip_right (finalvert_t *pfv0, finalvert_t *pfv1, + finalvert_t *out) +{ + float scale; + int i; + + if (pfv0->v[1] >= pfv1->v[1]) + { + scale = (float)(r_refdef.aliasvrectright - pfv0->v[0]) / + (pfv1->v[0] - pfv0->v[0]); + for (i=0 ; i<6 ; i++) + out->v[i] = pfv0->v[i] + (pfv1->v[i] - pfv0->v[i])*scale + 0.5; + } + else + { + scale = (float)(r_refdef.aliasvrectright - pfv1->v[0]) / + (pfv0->v[0] - pfv1->v[0]); + for (i=0 ; i<6 ; i++) + out->v[i] = pfv1->v[i] + (pfv0->v[i] - pfv1->v[i])*scale + 0.5; + } +} + + +void R_Alias_clip_top (finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out) +{ + float scale; + int i; + + if (pfv0->v[1] >= pfv1->v[1]) + { + scale = (float)(r_refdef.aliasvrect.y - pfv0->v[1]) / + (pfv1->v[1] - pfv0->v[1]); + for (i=0 ; i<6 ; i++) + out->v[i] = pfv0->v[i] + (pfv1->v[i] - pfv0->v[i])*scale + 0.5; + } + else + { + scale = (float)(r_refdef.aliasvrect.y - pfv1->v[1]) / + (pfv0->v[1] - pfv1->v[1]); + for (i=0 ; i<6 ; i++) + out->v[i] = pfv1->v[i] + (pfv0->v[i] - pfv1->v[i])*scale + 0.5; + } +} + + +void R_Alias_clip_bottom (finalvert_t *pfv0, finalvert_t *pfv1, + finalvert_t *out) +{ + float scale; + int i; + + if (pfv0->v[1] >= pfv1->v[1]) + { + scale = (float)(r_refdef.aliasvrectbottom - pfv0->v[1]) / + (pfv1->v[1] - pfv0->v[1]); + + for (i=0 ; i<6 ; i++) + out->v[i] = pfv0->v[i] + (pfv1->v[i] - pfv0->v[i])*scale + 0.5; + } + else + { + scale = (float)(r_refdef.aliasvrectbottom - pfv1->v[1]) / + (pfv0->v[1] - pfv1->v[1]); + + for (i=0 ; i<6 ; i++) + out->v[i] = pfv1->v[i] + (pfv0->v[i] - pfv1->v[i])*scale + 0.5; + } +} + +#endif + + +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; + int flags, oldflags; + + j = count-1; + k = 0; + for (i=0 ; i r_refdef.aliasvrectright) + out[k].flags |= ALIAS_RIGHT_CLIP; + if (out[k].v[1] > r_refdef.aliasvrectbottom) + out[k].flags |= ALIAS_BOTTOM_CLIP; + k++; + } + if (!flags) + { + out[k] = in[i]; + k++; + } + } + + return k; +} + + +/* +================ +R_AliasClipTriangle +================ +*/ +void R_AliasClipTriangle (mtriangle_t *ptri) +{ + int i, k, pingpong; + mtriangle_t mtri; + unsigned clipflags; + +// copy vertexes and fix seam texture coordinates + if (ptri->facesfront) + { + fv[0][0] = pfinalverts[ptri->vertindex[0]]; + fv[0][1] = pfinalverts[ptri->vertindex[1]]; + fv[0][2] = pfinalverts[ptri->vertindex[2]]; + } + else + { + for (i=0 ; i<3 ; i++) + { + fv[0][i] = pfinalverts[ptri->vertindex[i]]; + + if (!ptri->facesfront && (fv[0][i].flags & ALIAS_ONSEAM) ) + fv[0][i].v[2] += r_affinetridesc.seamfixupX16; + } + } + +// clip + clipflags = fv[0][0].flags | fv[0][1].flags | fv[0][2].flags; + + if (clipflags & ALIAS_Z_CLIP) + { + for (i=0 ; i<3 ; i++) + av[i] = pauxverts[ptri->vertindex[i]]; + + 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].v[0] = r_refdef.aliasvrectright; + + if (fv[pingpong][i].v[1] < r_refdef.aliasvrect.y) + fv[pingpong][i].v[1] = r_refdef.aliasvrect.y; + else if (fv[pingpong][i].v[1] > r_refdef.aliasvrectbottom) + fv[pingpong][i].v[1] = r_refdef.aliasvrectbottom; + + fv[pingpong][i].flags = 0; + } + +// draw triangles + mtri.facesfront = ptri->facesfront; + r_affinetridesc.ptriangles = &mtri; + r_affinetridesc.pfinalverts = fv[pingpong]; + +// FIXME: do all at once as trifan? + mtri.vertindex[0] = 0; + for (i=1 ; itrivial_accept = 0; - pmodel = currententity->model; - pahdr = Mod_Extradata (pmodel); - pmdl = (mdl_t *)((byte *)pahdr + pahdr->model); - - R_AliasSetUpTransform (0); - -// construct the base bounding box for this frame - frame = currententity->frame; -// TODO: don't repeat this check when drawing? - if ((frame >= pmdl->numframes) || (frame < 0)) - { - Con_DPrintf ("No such frame %d %s\n", frame, - pmodel->name); - frame = 0; - } - - pframedesc = &pahdr->frames[frame]; - -// x worldspace coordinates - basepts[0][0] = basepts[1][0] = basepts[2][0] = basepts[3][0] = - (float)pframedesc->bboxmin.v[0]; - basepts[4][0] = basepts[5][0] = basepts[6][0] = basepts[7][0] = - (float)pframedesc->bboxmax.v[0]; - -// y worldspace coordinates - basepts[0][1] = basepts[3][1] = basepts[5][1] = basepts[6][1] = - (float)pframedesc->bboxmin.v[1]; - basepts[1][1] = basepts[2][1] = basepts[4][1] = basepts[7][1] = - (float)pframedesc->bboxmax.v[1]; - -// z worldspace coordinates - basepts[0][2] = basepts[1][2] = basepts[4][2] = basepts[5][2] = - (float)pframedesc->bboxmin.v[2]; - basepts[2][2] = basepts[3][2] = basepts[6][2] = basepts[7][2] = - (float)pframedesc->bboxmax.v[2]; - - zclipped = false; - zfullyclipped = true; - - minz = 9999; - for (i=0; i<8 ; i++) - { - R_AliasTransformVector (&basepts[i][0], &viewaux[i].fv[0]); - - if (viewaux[i].fv[2] < ALIAS_Z_CLIP_PLANE) - { - // we must clip points that are closer than the near clip plane - viewpts[i].flags = ALIAS_Z_CLIP; - zclipped = true; - } - else - { - if (viewaux[i].fv[2] < minz) - minz = viewaux[i].fv[2]; - viewpts[i].flags = 0; - zfullyclipped = false; - } - } - - - if (zfullyclipped) - { - return false; // everything was near-z-clipped - } - - numv = 8; - - if (zclipped) - { - // organize points by edges, use edges to get new points (possible trivial - // reject) - for (i=0 ; i<12 ; i++) - { - // edge endpoints - pv0 = &viewpts[aedges[i].index0]; - pv1 = &viewpts[aedges[i].index1]; - pa0 = &viewaux[aedges[i].index0]; - pa1 = &viewaux[aedges[i].index1]; - - // if one end is clipped and the other isn't, make a new point - if (pv0->flags ^ pv1->flags) - { - frac = (ALIAS_Z_CLIP_PLANE - pa0->fv[2]) / - (pa1->fv[2] - pa0->fv[2]); - viewaux[numv].fv[0] = pa0->fv[0] + - (pa1->fv[0] - pa0->fv[0]) * frac; - viewaux[numv].fv[1] = pa0->fv[1] + - (pa1->fv[1] - pa0->fv[1]) * frac; - viewaux[numv].fv[2] = ALIAS_Z_CLIP_PLANE; - viewpts[numv].flags = 0; - numv++; - } - } - } - -// project the vertices that remain after clipping - anyclip = 0; - allclip = ALIAS_XY_CLIP_MASK; - -// TODO: probably should do this loop in ASM, especially if we use floats - for (i=0 ; i r_refdef.fvrectright) - flags |= ALIAS_RIGHT_CLIP; - if (v1 > r_refdef.fvrectbottom) - flags |= ALIAS_BOTTOM_CLIP; - - anyclip |= flags; - allclip &= flags; - } - - if (allclip) - return false; // trivial reject off one side - - currententity->trivial_accept = !anyclip & !zclipped; - - if (currententity->trivial_accept) - { - if (minz > (r_aliastransition + (pmdl->size * r_resfudge))) - { - currententity->trivial_accept |= 2; - } - } - - return true; -} - - -/* -================ -R_AliasTransformVector -================ -*/ -void R_AliasTransformVector (vec3_t in, vec3_t out) -{ - out[0] = DotProduct(in, aliastransform[0]) + aliastransform[0][3]; - out[1] = DotProduct(in, aliastransform[1]) + aliastransform[1][3]; - out[2] = DotProduct(in, aliastransform[2]) + aliastransform[2][3]; -} - - -/* -================ -R_AliasPreparePoints - -General clipped case -================ -*/ -void R_AliasPreparePoints (void) -{ - int i; - stvert_t *pstverts; - finalvert_t *fv; - auxvert_t *av; - mtriangle_t *ptri; - finalvert_t *pfv[3]; - - pstverts = (stvert_t *)((byte *)paliashdr + paliashdr->stverts); - r_anumverts = pmdl->numverts; - fv = pfinalverts; - av = pauxverts; - - for (i=0 ; ifv[2] < ALIAS_Z_CLIP_PLANE) - fv->flags |= ALIAS_Z_CLIP; - else - { - R_AliasProjectFinalVert (fv, av); - - if (fv->v[0] < r_refdef.aliasvrect.x) - fv->flags |= ALIAS_LEFT_CLIP; - if (fv->v[1] < r_refdef.aliasvrect.y) - fv->flags |= ALIAS_TOP_CLIP; - if (fv->v[0] > r_refdef.aliasvrectright) - fv->flags |= ALIAS_RIGHT_CLIP; - if (fv->v[1] > r_refdef.aliasvrectbottom) - fv->flags |= ALIAS_BOTTOM_CLIP; - } - } - -// -// clip and draw all triangles -// - r_affinetridesc.numtriangles = 1; - - ptri = (mtriangle_t *)((byte *)paliashdr + paliashdr->triangles); - for (i=0 ; inumtris ; i++, ptri++) - { - pfv[0] = &pfinalverts[ptri->vertindex[0]]; - pfv[1] = &pfinalverts[ptri->vertindex[1]]; - pfv[2] = &pfinalverts[ptri->vertindex[2]]; - - if ( pfv[0]->flags & pfv[1]->flags & pfv[2]->flags & (ALIAS_XY_CLIP_MASK | ALIAS_Z_CLIP) ) - continue; // completely clipped - - if ( ! ( (pfv[0]->flags | pfv[1]->flags | pfv[2]->flags) & - (ALIAS_XY_CLIP_MASK | ALIAS_Z_CLIP) ) ) - { // totally unclipped - r_affinetridesc.pfinalverts = pfinalverts; - r_affinetridesc.ptriangles = ptri; - D_PolysetDraw (); - } - else - { // partially clipped - R_AliasClipTriangle (ptri); - } - } -} - - -/* -================ -R_AliasSetUpTransform -================ -*/ -void R_AliasSetUpTransform (int trivial_accept) -{ - int i; - float rotationmatrix[3][4], t2matrix[3][4]; - static float tmatrix[3][4]; - 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, alias_forward, alias_right, alias_up); - - tmatrix[0][0] = pmdl->scale[0]; - tmatrix[1][1] = pmdl->scale[1]; - tmatrix[2][2] = pmdl->scale[2]; - - tmatrix[0][3] = pmdl->scale_origin[0]; - tmatrix[1][3] = pmdl->scale_origin[1]; - tmatrix[2][3] = pmdl->scale_origin[2]; - -// TODO: can do this with simple matrix rearrangement - - for (i=0 ; i<3 ; i++) - { - t2matrix[i][0] = alias_forward[i]; - t2matrix[i][1] = -alias_right[i]; - t2matrix[i][2] = alias_up[i]; - } - - t2matrix[0][3] = -modelorg[0]; - t2matrix[1][3] = -modelorg[1]; - t2matrix[2][3] = -modelorg[2]; - -// FIXME: can do more efficiently than full concatenation - 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; - - R_ConcatTransforms (viewmatrix, rotationmatrix, aliastransform); - -// do the scaling up of x and y to screen coordinates as part of the transform -// for the unclipped case (it would mess up clipping in the clipped case). -// Also scale down z, so 1/z is scaled 31 bits for free, and scale down x and y -// correspondingly so the projected x and y come out right -// FIXME: make this work for clipped case too? - if (trivial_accept) - { - for (i=0 ; i<4 ; i++) - { - aliastransform[0][i] *= aliasxscale * - (1.0 / ((float)0x8000 * 0x10000)); - aliastransform[1][i] *= aliasyscale * - (1.0 / ((float)0x8000 * 0x10000)); - aliastransform[2][i] *= 1.0 / ((float)0x8000 * 0x10000); - - } - } -} - - -/* -================ -R_AliasTransformFinalVert -================ -*/ -void R_AliasTransformFinalVert (finalvert_t *fv, auxvert_t *av, - trivertx_t *pverts, stvert_t *pstverts) -{ - int temp; - float lightcos, *plightnormal; - - av->fv[0] = DotProduct(pverts->v, aliastransform[0]) + - aliastransform[0][3]; - av->fv[1] = DotProduct(pverts->v, aliastransform[1]) + - aliastransform[1][3]; - av->fv[2] = DotProduct(pverts->v, aliastransform[2]) + - aliastransform[2][3]; - - fv->v[2] = pstverts->s; - fv->v[3] = pstverts->t; - - fv->flags = pstverts->onseam; - -// lighting - plightnormal = r_avertexnormals[pverts->lightnormalindex]; - 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->v[4] = temp; -} - - -#if !id386 - -/* -================ -R_AliasTransformAndProjectFinalVerts -================ -*/ -void R_AliasTransformAndProjectFinalVerts (finalvert_t *fv, stvert_t *pstverts) -{ - int i, temp; - float lightcos, *plightnormal, zi; - trivertx_t *pverts; - - pverts = r_apverts; - - for (i=0 ; iv, aliastransform[2]) + - aliastransform[2][3]); - - // x, y, and z are scaled down by 1/2**31 in the transform, so 1/z is - // scaled up by 1/2**31, and the scaling cancels out for x and y in the - // projection - fv->v[5] = zi; - - fv->v[0] = ((DotProduct(pverts->v, aliastransform[0]) + - aliastransform[0][3]) * zi) + aliasxcenter; - fv->v[1] = ((DotProduct(pverts->v, aliastransform[1]) + - aliastransform[1][3]) * zi) + aliasycenter; - - fv->v[2] = pstverts->s; - fv->v[3] = pstverts->t; - fv->flags = pstverts->onseam; - - // lighting - plightnormal = r_avertexnormals[pverts->lightnormalindex]; - 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->v[4] = temp; - } -} - -#endif - - -/* -================ -R_AliasProjectFinalVert -================ -*/ -void R_AliasProjectFinalVert (finalvert_t *fv, auxvert_t *av) -{ - float zi; - -// project points - zi = 1.0 / av->fv[2]; - - fv->v[5] = zi * ziscale; - - fv->v[0] = (av->fv[0] * aliasxscale * zi) + aliasxcenter; - fv->v[1] = (av->fv[1] * aliasyscale * zi) + aliasycenter; -} - - -/* -================ -R_AliasPrepareUnclippedPoints -================ -*/ -void R_AliasPrepareUnclippedPoints (void) -{ - stvert_t *pstverts; - finalvert_t *fv; - - pstverts = (stvert_t *)((byte *)paliashdr + paliashdr->stverts); - r_anumverts = pmdl->numverts; -// FIXME: just use pfinalverts directly? - fv = pfinalverts; - - R_AliasTransformAndProjectFinalVerts (fv, pstverts); - - if (r_affinetridesc.drawtype) - D_PolysetDrawFinalVerts (fv, r_anumverts); - - r_affinetridesc.pfinalverts = pfinalverts; - r_affinetridesc.ptriangles = (mtriangle_t *) - ((byte *)paliashdr + paliashdr->triangles); - r_affinetridesc.numtriangles = pmdl->numtris; - - D_PolysetDraw (); -} - -/* -=============== -R_AliasSetupSkin -=============== -*/ -void R_AliasSetupSkin (void) -{ - int skinnum; - int i, numskins; - maliasskingroup_t *paliasskingroup; - float *pskinintervals, fullskininterval; - float skintargettime, skintime; - - skinnum = currententity->skinnum; - if ((skinnum >= pmdl->numskins) || (skinnum < 0)) - { - Con_DPrintf ("R_AliasSetupSkin: no such skin # %d\n", skinnum); - skinnum = 0; - } - - pskindesc = ((maliasskindesc_t *) - ((byte *)paliashdr + paliashdr->skindesc)) + skinnum; - a_skinwidth = pmdl->skinwidth; - - if (pskindesc->type == ALIAS_SKIN_GROUP) - { - paliasskingroup = (maliasskingroup_t *)((byte *)paliashdr + - pskindesc->skin); - pskinintervals = (float *) - ((byte *)paliashdr + paliasskingroup->intervals); - numskins = paliasskingroup->numskins; - fullskininterval = pskinintervals[numskins-1]; - - skintime = cl.time + currententity->syncbase; - - // when loading in Mod_LoadAliasSkinGroup, we guaranteed all interval - // values are positive, so we don't have to worry about division by 0 - skintargettime = skintime - - ((int)(skintime / fullskininterval)) * fullskininterval; - - for (i=0 ; i<(numskins-1) ; i++) - { - if (pskinintervals[i] > skintargettime) - break; - } - - pskindesc = &paliasskingroup->skindescs[i]; - } - - r_affinetridesc.pskindesc = pskindesc; - r_affinetridesc.pskin = (void *)((byte *)paliashdr + pskindesc->skin); - r_affinetridesc.skinwidth = a_skinwidth; - r_affinetridesc.seamfixupX16 = (a_skinwidth >> 1) << 16; - r_affinetridesc.skinheight = pmdl->skinheight; - - if (currententity->scoreboard) - { - byte *base; - - if (!currententity->scoreboard->skin) - Skin_Find (currententity->scoreboard); - base = Skin_Cache (currententity->scoreboard->skin); - if (base) - { - r_affinetridesc.pskin = base; - r_affinetridesc.skinwidth = 320; - r_affinetridesc.skinheight = 200; - } - } -} - -/* -================ -R_AliasSetupLighting -================ -*/ -void R_AliasSetupLighting (alight_t *plighting) -{ - -// guarantee that no vertex will ever be lit below LIGHT_MIN, so we don't have -// to clamp off the bottom - r_ambientlight = plighting->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 = plighting->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 (plighting->plightvec, alias_forward); - r_plightvec[1] = -DotProduct (plighting->plightvec, alias_right); - r_plightvec[2] = DotProduct (plighting->plightvec, alias_up); -} - -/* -================= -R_AliasSetupFrame - -set r_apverts -================= -*/ -void R_AliasSetupFrame (void) -{ - int frame; - int i, numframes; - maliasgroup_t *paliasgroup; - float *pintervals, fullinterval, targettime, time; - - frame = currententity->frame; - if ((frame >= pmdl->numframes) || (frame < 0)) - { - Con_DPrintf ("R_AliasSetupFrame: no such frame %d\n", frame); - frame = 0; - } - - if (paliashdr->frames[frame].type == ALIAS_SINGLE) - { - r_apverts = (trivertx_t *) - ((byte *)paliashdr + paliashdr->frames[frame].frame); - return; - } - - paliasgroup = (maliasgroup_t *) - ((byte *)paliashdr + paliashdr->frames[frame].frame); - pintervals = (float *)((byte *)paliashdr + paliasgroup->intervals); - numframes = paliasgroup->numframes; - fullinterval = pintervals[numframes-1]; - - time = cl.time + currententity->syncbase; - -// -// when loading in Mod_LoadAliasGroup, we guaranteed all interval values -// are positive, so we don't have to worry about division by 0 -// - targettime = time - ((int)(time / fullinterval)) * fullinterval; - - for (i=0 ; i<(numframes-1) ; i++) - { - if (pintervals[i] > targettime) - break; - } - - r_apverts = (trivertx_t *) - ((byte *)paliashdr + paliasgroup->frames[i].frame); -} - - -/* -================ -R_AliasDrawModel -================ -*/ -void R_AliasDrawModel (alight_t *plighting) -{ - finalvert_t finalverts[MAXALIASVERTS + - ((CACHE_SIZE - 1) / sizeof(finalvert_t)) + 1]; - auxvert_t auxverts[MAXALIASVERTS]; - - r_amodels_drawn++; - -// cache align - pfinalverts = (finalvert_t *) - (((long)&finalverts[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1)); - pauxverts = &auxverts[0]; - - paliashdr = (aliashdr_t *)Mod_Extradata (currententity->model); - pmdl = (mdl_t *)((byte *)paliashdr + paliashdr->model); - - R_AliasSetupSkin (); - R_AliasSetUpTransform (currententity->trivial_accept); - R_AliasSetupLighting (plighting); - R_AliasSetupFrame (); - - if (!currententity->colormap) - Sys_Error ("R_AliasDrawModel: !currententity->colormap"); - - r_affinetridesc.drawtype = (currententity->trivial_accept == 3) && - r_recursiveaffinetriangles; - - if (r_affinetridesc.drawtype) - { - D_PolysetUpdateTables (); // FIXME: precalc... - } - else - { -#if id386 - D_Aff8Patch (currententity->colormap); -#endif - } - - acolormap = currententity->colormap; - - if (currententity != &cl.viewent) - ziscale = (float)0x8000 * (float)0x10000; - else - ziscale = (float)0x8000 * (float)0x10000 * 3.0; - - if (currententity->trivial_accept) - R_AliasPrepareUnclippedPoints (); - else - R_AliasPreparePoints (); -} - +/* +Copyright (C) 1996-1997 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_alias.c: routines for setting up to draw alias models + +#include "quakedef.h" +#include "r_local.h" +#include "d_local.h" // FIXME: shouldn't be needed (is needed for patch + // right now, but that should move) + +#define LIGHT_MIN 5 // lowest light value we'll allow, to avoid the + // need for inner-loop light clamping + +mtriangle_t *ptriangles; +affinetridesc_t r_affinetridesc; + +void * acolormap; // FIXME: should go away + +trivertx_t *r_apverts; + +// TODO: these probably will go away with optimized rasterization +mdl_t *pmdl; +vec3_t r_plightvec; +int r_ambientlight; +float r_shadelight; +aliashdr_t *paliashdr; +finalvert_t *pfinalverts; +auxvert_t *pauxverts; +static float ziscale; +static model_t *pmodel; + +static vec3_t alias_forward, alias_right, alias_up; + +static maliasskindesc_t *pskindesc; + +int r_amodels_drawn; +int a_skinwidth; +int r_anumverts; + +float aliastransform[3][4]; + +typedef struct { + int index0; + int index1; +} aedge_t; + +static aedge_t aedges[12] = { +{0, 1}, {1, 2}, {2, 3}, {3, 0}, +{4, 5}, {5, 6}, {6, 7}, {7, 4}, +{0, 5}, {1, 4}, {2, 7}, {3, 6} +}; + +#define NUMVERTEXNORMALS 162 + +float r_avertexnormals[NUMVERTEXNORMALS][3] = { +#include "anorms.h" +}; + +void R_AliasTransformAndProjectFinalVerts (finalvert_t *fv, + stvert_t *pstverts); +void R_AliasSetUpTransform (int trivial_accept); +void R_AliasTransformVector (vec3_t in, vec3_t out); +void R_AliasTransformFinalVert (finalvert_t *fv, auxvert_t *av, + trivertx_t *pverts, stvert_t *pstverts); +void R_AliasProjectFinalVert (finalvert_t *fv, auxvert_t *av); + + +/* +================ +R_AliasCheckBBox +================ +*/ +qboolean R_AliasCheckBBox (void) +{ + int i, flags, frame, numv; + aliashdr_t *pahdr; + float zi, basepts[8][3], v0, v1, frac; + finalvert_t *pv0, *pv1, viewpts[16]; + auxvert_t *pa0, *pa1, viewaux[16]; + maliasframedesc_t *pframedesc; + qboolean zclipped, zfullyclipped; + unsigned anyclip, allclip; + int minz; + +// expand, rotate, and translate points into worldspace + + currententity->trivial_accept = 0; + pmodel = currententity->model; + pahdr = Mod_Extradata (pmodel); + pmdl = (mdl_t *)((byte *)pahdr + pahdr->model); + + R_AliasSetUpTransform (0); + +// construct the base bounding box for this frame + frame = currententity->frame; +// TODO: don't repeat this check when drawing? + if ((frame >= pmdl->numframes) || (frame < 0)) + { + Con_DPrintf ("No such frame %d %s\n", frame, + pmodel->name); + frame = 0; + } + + pframedesc = &pahdr->frames[frame]; + +// x worldspace coordinates + basepts[0][0] = basepts[1][0] = basepts[2][0] = basepts[3][0] = + (float)pframedesc->bboxmin.v[0]; + basepts[4][0] = basepts[5][0] = basepts[6][0] = basepts[7][0] = + (float)pframedesc->bboxmax.v[0]; + +// y worldspace coordinates + basepts[0][1] = basepts[3][1] = basepts[5][1] = basepts[6][1] = + (float)pframedesc->bboxmin.v[1]; + basepts[1][1] = basepts[2][1] = basepts[4][1] = basepts[7][1] = + (float)pframedesc->bboxmax.v[1]; + +// z worldspace coordinates + basepts[0][2] = basepts[1][2] = basepts[4][2] = basepts[5][2] = + (float)pframedesc->bboxmin.v[2]; + basepts[2][2] = basepts[3][2] = basepts[6][2] = basepts[7][2] = + (float)pframedesc->bboxmax.v[2]; + + zclipped = false; + zfullyclipped = true; + + minz = 9999; + for (i=0; i<8 ; i++) + { + R_AliasTransformVector (&basepts[i][0], &viewaux[i].fv[0]); + + if (viewaux[i].fv[2] < ALIAS_Z_CLIP_PLANE) + { + // we must clip points that are closer than the near clip plane + viewpts[i].flags = ALIAS_Z_CLIP; + zclipped = true; + } + else + { + if (viewaux[i].fv[2] < minz) + minz = viewaux[i].fv[2]; + viewpts[i].flags = 0; + zfullyclipped = false; + } + } + + + if (zfullyclipped) + { + return false; // everything was near-z-clipped + } + + numv = 8; + + if (zclipped) + { + // organize points by edges, use edges to get new points (possible trivial + // reject) + for (i=0 ; i<12 ; i++) + { + // edge endpoints + pv0 = &viewpts[aedges[i].index0]; + pv1 = &viewpts[aedges[i].index1]; + pa0 = &viewaux[aedges[i].index0]; + pa1 = &viewaux[aedges[i].index1]; + + // if one end is clipped and the other isn't, make a new point + if (pv0->flags ^ pv1->flags) + { + frac = (ALIAS_Z_CLIP_PLANE - pa0->fv[2]) / + (pa1->fv[2] - pa0->fv[2]); + viewaux[numv].fv[0] = pa0->fv[0] + + (pa1->fv[0] - pa0->fv[0]) * frac; + viewaux[numv].fv[1] = pa0->fv[1] + + (pa1->fv[1] - pa0->fv[1]) * frac; + viewaux[numv].fv[2] = ALIAS_Z_CLIP_PLANE; + viewpts[numv].flags = 0; + numv++; + } + } + } + +// project the vertices that remain after clipping + anyclip = 0; + allclip = ALIAS_XY_CLIP_MASK; + +// TODO: probably should do this loop in ASM, especially if we use floats + for (i=0 ; i r_refdef.fvrectright) + flags |= ALIAS_RIGHT_CLIP; + if (v1 > r_refdef.fvrectbottom) + flags |= ALIAS_BOTTOM_CLIP; + + anyclip |= flags; + allclip &= flags; + } + + if (allclip) + return false; // trivial reject off one side + + currententity->trivial_accept = !anyclip & !zclipped; + + if (currententity->trivial_accept) + { + if (minz > (r_aliastransition + (pmdl->size * r_resfudge))) + { + currententity->trivial_accept |= 2; + } + } + + return true; +} + + +/* +================ +R_AliasTransformVector +================ +*/ +void R_AliasTransformVector (vec3_t in, vec3_t out) +{ + out[0] = DotProduct(in, aliastransform[0]) + aliastransform[0][3]; + out[1] = DotProduct(in, aliastransform[1]) + aliastransform[1][3]; + out[2] = DotProduct(in, aliastransform[2]) + aliastransform[2][3]; +} + + +/* +================ +R_AliasPreparePoints + +General clipped case +================ +*/ +void R_AliasPreparePoints (void) +{ + int i; + stvert_t *pstverts; + finalvert_t *fv; + auxvert_t *av; + mtriangle_t *ptri; + finalvert_t *pfv[3]; + + pstverts = (stvert_t *)((byte *)paliashdr + paliashdr->stverts); + r_anumverts = pmdl->numverts; + fv = pfinalverts; + av = pauxverts; + + for (i=0 ; ifv[2] < ALIAS_Z_CLIP_PLANE) + fv->flags |= ALIAS_Z_CLIP; + else + { + R_AliasProjectFinalVert (fv, av); + + if (fv->v[0] < r_refdef.aliasvrect.x) + fv->flags |= ALIAS_LEFT_CLIP; + if (fv->v[1] < r_refdef.aliasvrect.y) + fv->flags |= ALIAS_TOP_CLIP; + if (fv->v[0] > r_refdef.aliasvrectright) + fv->flags |= ALIAS_RIGHT_CLIP; + if (fv->v[1] > r_refdef.aliasvrectbottom) + fv->flags |= ALIAS_BOTTOM_CLIP; + } + } + +// +// clip and draw all triangles +// + r_affinetridesc.numtriangles = 1; + + ptri = (mtriangle_t *)((byte *)paliashdr + paliashdr->triangles); + for (i=0 ; inumtris ; i++, ptri++) + { + pfv[0] = &pfinalverts[ptri->vertindex[0]]; + pfv[1] = &pfinalverts[ptri->vertindex[1]]; + pfv[2] = &pfinalverts[ptri->vertindex[2]]; + + if ( pfv[0]->flags & pfv[1]->flags & pfv[2]->flags & (ALIAS_XY_CLIP_MASK | ALIAS_Z_CLIP) ) + continue; // completely clipped + + if ( ! ( (pfv[0]->flags | pfv[1]->flags | pfv[2]->flags) & + (ALIAS_XY_CLIP_MASK | ALIAS_Z_CLIP) ) ) + { // totally unclipped + r_affinetridesc.pfinalverts = pfinalverts; + r_affinetridesc.ptriangles = ptri; + D_PolysetDraw (); + } + else + { // partially clipped + R_AliasClipTriangle (ptri); + } + } +} + + +/* +================ +R_AliasSetUpTransform +================ +*/ +void R_AliasSetUpTransform (int trivial_accept) +{ + int i; + float rotationmatrix[3][4], t2matrix[3][4]; + static float tmatrix[3][4]; + 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, alias_forward, alias_right, alias_up); + + tmatrix[0][0] = pmdl->scale[0]; + tmatrix[1][1] = pmdl->scale[1]; + tmatrix[2][2] = pmdl->scale[2]; + + tmatrix[0][3] = pmdl->scale_origin[0]; + tmatrix[1][3] = pmdl->scale_origin[1]; + tmatrix[2][3] = pmdl->scale_origin[2]; + +// TODO: can do this with simple matrix rearrangement + + for (i=0 ; i<3 ; i++) + { + t2matrix[i][0] = alias_forward[i]; + t2matrix[i][1] = -alias_right[i]; + t2matrix[i][2] = alias_up[i]; + } + + t2matrix[0][3] = -modelorg[0]; + t2matrix[1][3] = -modelorg[1]; + t2matrix[2][3] = -modelorg[2]; + +// FIXME: can do more efficiently than full concatenation + 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; + + R_ConcatTransforms (viewmatrix, rotationmatrix, aliastransform); + +// do the scaling up of x and y to screen coordinates as part of the transform +// for the unclipped case (it would mess up clipping in the clipped case). +// Also scale down z, so 1/z is scaled 31 bits for free, and scale down x and y +// correspondingly so the projected x and y come out right +// FIXME: make this work for clipped case too? + if (trivial_accept) + { + for (i=0 ; i<4 ; i++) + { + aliastransform[0][i] *= aliasxscale * + (1.0 / ((float)0x8000 * 0x10000)); + aliastransform[1][i] *= aliasyscale * + (1.0 / ((float)0x8000 * 0x10000)); + aliastransform[2][i] *= 1.0 / ((float)0x8000 * 0x10000); + + } + } +} + + +/* +================ +R_AliasTransformFinalVert +================ +*/ +void R_AliasTransformFinalVert (finalvert_t *fv, auxvert_t *av, + trivertx_t *pverts, stvert_t *pstverts) +{ + int temp; + float lightcos, *plightnormal; + + av->fv[0] = DotProduct(pverts->v, aliastransform[0]) + + aliastransform[0][3]; + av->fv[1] = DotProduct(pverts->v, aliastransform[1]) + + aliastransform[1][3]; + av->fv[2] = DotProduct(pverts->v, aliastransform[2]) + + aliastransform[2][3]; + + fv->v[2] = pstverts->s; + fv->v[3] = pstverts->t; + + fv->flags = pstverts->onseam; + +// lighting + plightnormal = r_avertexnormals[pverts->lightnormalindex]; + 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->v[4] = temp; +} + + +#if !id386 + +/* +================ +R_AliasTransformAndProjectFinalVerts +================ +*/ +void R_AliasTransformAndProjectFinalVerts (finalvert_t *fv, stvert_t *pstverts) +{ + int i, temp; + float lightcos, *plightnormal, zi; + trivertx_t *pverts; + + pverts = r_apverts; + + for (i=0 ; iv, aliastransform[2]) + + aliastransform[2][3]); + + // x, y, and z are scaled down by 1/2**31 in the transform, so 1/z is + // scaled up by 1/2**31, and the scaling cancels out for x and y in the + // projection + fv->v[5] = zi; + + fv->v[0] = ((DotProduct(pverts->v, aliastransform[0]) + + aliastransform[0][3]) * zi) + aliasxcenter; + fv->v[1] = ((DotProduct(pverts->v, aliastransform[1]) + + aliastransform[1][3]) * zi) + aliasycenter; + + fv->v[2] = pstverts->s; + fv->v[3] = pstverts->t; + fv->flags = pstverts->onseam; + + // lighting + plightnormal = r_avertexnormals[pverts->lightnormalindex]; + 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->v[4] = temp; + } +} + +#endif + + +/* +================ +R_AliasProjectFinalVert +================ +*/ +void R_AliasProjectFinalVert (finalvert_t *fv, auxvert_t *av) +{ + float zi; + +// project points + zi = 1.0 / av->fv[2]; + + fv->v[5] = zi * ziscale; + + fv->v[0] = (av->fv[0] * aliasxscale * zi) + aliasxcenter; + fv->v[1] = (av->fv[1] * aliasyscale * zi) + aliasycenter; +} + + +/* +================ +R_AliasPrepareUnclippedPoints +================ +*/ +void R_AliasPrepareUnclippedPoints (void) +{ + stvert_t *pstverts; + finalvert_t *fv; + + pstverts = (stvert_t *)((byte *)paliashdr + paliashdr->stverts); + r_anumverts = pmdl->numverts; +// FIXME: just use pfinalverts directly? + fv = pfinalverts; + + R_AliasTransformAndProjectFinalVerts (fv, pstverts); + + if (r_affinetridesc.drawtype) + D_PolysetDrawFinalVerts (fv, r_anumverts); + + r_affinetridesc.pfinalverts = pfinalverts; + r_affinetridesc.ptriangles = (mtriangle_t *) + ((byte *)paliashdr + paliashdr->triangles); + r_affinetridesc.numtriangles = pmdl->numtris; + + D_PolysetDraw (); +} + +/* +=============== +R_AliasSetupSkin +=============== +*/ +void R_AliasSetupSkin (void) +{ + int skinnum; + int i, numskins; + maliasskingroup_t *paliasskingroup; + float *pskinintervals, fullskininterval; + float skintargettime, skintime; + + skinnum = currententity->skinnum; + if ((skinnum >= pmdl->numskins) || (skinnum < 0)) + { + Con_DPrintf ("R_AliasSetupSkin: no such skin # %d\n", skinnum); + skinnum = 0; + } + + pskindesc = ((maliasskindesc_t *) + ((byte *)paliashdr + paliashdr->skindesc)) + skinnum; + a_skinwidth = pmdl->skinwidth; + + if (pskindesc->type == ALIAS_SKIN_GROUP) + { + paliasskingroup = (maliasskingroup_t *)((byte *)paliashdr + + pskindesc->skin); + pskinintervals = (float *) + ((byte *)paliashdr + paliasskingroup->intervals); + numskins = paliasskingroup->numskins; + fullskininterval = pskinintervals[numskins-1]; + + skintime = cl.time + currententity->syncbase; + + // when loading in Mod_LoadAliasSkinGroup, we guaranteed all interval + // values are positive, so we don't have to worry about division by 0 + skintargettime = skintime - + ((int)(skintime / fullskininterval)) * fullskininterval; + + for (i=0 ; i<(numskins-1) ; i++) + { + if (pskinintervals[i] > skintargettime) + break; + } + + pskindesc = &paliasskingroup->skindescs[i]; + } + + r_affinetridesc.pskindesc = pskindesc; + r_affinetridesc.pskin = (void *)((byte *)paliashdr + pskindesc->skin); + r_affinetridesc.skinwidth = a_skinwidth; + r_affinetridesc.seamfixupX16 = (a_skinwidth >> 1) << 16; + r_affinetridesc.skinheight = pmdl->skinheight; + + if (currententity->scoreboard) + { + byte *base; + + if (!currententity->scoreboard->skin) + Skin_Find (currententity->scoreboard); + base = Skin_Cache (currententity->scoreboard->skin); + if (base) + { + r_affinetridesc.pskin = base; + r_affinetridesc.skinwidth = 320; + r_affinetridesc.skinheight = 200; + } + } +} + +/* +================ +R_AliasSetupLighting +================ +*/ +void R_AliasSetupLighting (alight_t *plighting) +{ + +// guarantee that no vertex will ever be lit below LIGHT_MIN, so we don't have +// to clamp off the bottom + r_ambientlight = plighting->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 = plighting->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 (plighting->plightvec, alias_forward); + r_plightvec[1] = -DotProduct (plighting->plightvec, alias_right); + r_plightvec[2] = DotProduct (plighting->plightvec, alias_up); +} + +/* +================= +R_AliasSetupFrame + +set r_apverts +================= +*/ +void R_AliasSetupFrame (void) +{ + int frame; + int i, numframes; + maliasgroup_t *paliasgroup; + float *pintervals, fullinterval, targettime, time; + + frame = currententity->frame; + if ((frame >= pmdl->numframes) || (frame < 0)) + { + Con_DPrintf ("R_AliasSetupFrame: no such frame %d\n", frame); + frame = 0; + } + + if (paliashdr->frames[frame].type == ALIAS_SINGLE) + { + r_apverts = (trivertx_t *) + ((byte *)paliashdr + paliashdr->frames[frame].frame); + return; + } + + paliasgroup = (maliasgroup_t *) + ((byte *)paliashdr + paliashdr->frames[frame].frame); + pintervals = (float *)((byte *)paliashdr + paliasgroup->intervals); + numframes = paliasgroup->numframes; + fullinterval = pintervals[numframes-1]; + + time = cl.time + currententity->syncbase; + +// +// when loading in Mod_LoadAliasGroup, we guaranteed all interval values +// are positive, so we don't have to worry about division by 0 +// + targettime = time - ((int)(time / fullinterval)) * fullinterval; + + for (i=0 ; i<(numframes-1) ; i++) + { + if (pintervals[i] > targettime) + break; + } + + r_apverts = (trivertx_t *) + ((byte *)paliashdr + paliasgroup->frames[i].frame); +} + + +/* +================ +R_AliasDrawModel +================ +*/ +void R_AliasDrawModel (alight_t *plighting) +{ + finalvert_t finalverts[MAXALIASVERTS + + ((CACHE_SIZE - 1) / sizeof(finalvert_t)) + 1]; + auxvert_t auxverts[MAXALIASVERTS]; + + r_amodels_drawn++; + +// cache align + pfinalverts = (finalvert_t *) + (((long)&finalverts[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1)); + pauxverts = &auxverts[0]; + + paliashdr = (aliashdr_t *)Mod_Extradata (currententity->model); + pmdl = (mdl_t *)((byte *)paliashdr + paliashdr->model); + + R_AliasSetupSkin (); + R_AliasSetUpTransform (currententity->trivial_accept); + R_AliasSetupLighting (plighting); + R_AliasSetupFrame (); + + if (!currententity->colormap) + Sys_Error ("R_AliasDrawModel: !currententity->colormap"); + + r_affinetridesc.drawtype = (currententity->trivial_accept == 3) && + r_recursiveaffinetriangles; + + if (r_affinetridesc.drawtype) + { + D_PolysetUpdateTables (); // FIXME: precalc... + } + else + { +#if id386 + D_Aff8Patch (currententity->colormap); +#endif + } + + acolormap = currententity->colormap; + + if (currententity != &cl.viewent) + ziscale = (float)0x8000 * (float)0x10000; + else + ziscale = (float)0x8000 * (float)0x10000 * 3.0; + + if (currententity->trivial_accept) + R_AliasPrepareUnclippedPoints (); + else + R_AliasPreparePoints (); +} + diff --git a/source/r_aliasa.s b/source/r_aliasa.s index 13081992..ed2a4f26 100644 --- a/source/r_aliasa.s +++ b/source/r_aliasa.s @@ -1,243 +1,243 @@ -/* - r_aliasa.S - - (description) - - Copyright (C) 1996-1997 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: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - - $Id: r_aliasa.s,v 1.1.1.3 2004/10/13 18:54:44 vvd0 Exp $ -*/ -// r_aliasa.s -// x86 assembly-language Alias model transform and project code. - -#include "asm_i386.h" -#include "quakeasm.h" -#include "asm_draw.h" -#include "d_ifacea.h" - -#ifdef id386 - - .data - -Lfloat_1: .single 1.0 -Ltemp: .long 0 -Lcoords: .long 0, 0, 0 - - .text - -#define fv 12+4 -#define pstverts 12+8 - -.globl C(R_AliasTransformAndProjectFinalVerts) -C(R_AliasTransformAndProjectFinalVerts): - pushl %ebp // preserve caller's stack frame - pushl %edi - pushl %esi // preserve register variables - -// int i, temp; -// float lightcos, *plightnormal, zi; -// trivertx_t *pverts; - -// pverts = r_apverts; - movl C(r_apverts),%esi - -// for (i=0 ; iv, aliastransform[2]) + -// aliastransform[2][3]); - movb (%esi),%dl - movb %dl,Lcoords - fildl Lcoords // v[0] - movb 1(%esi),%dl - movb %dl,Lcoords+4 - fildl Lcoords+4 // v[1] | v[0] - movb 2(%esi),%dl - movb %dl,Lcoords+8 - fildl Lcoords+8 // v[2] | v[1] | v[0] - - fld %st(2) // v[0] | v[2] | v[1] | v[0] - fmuls C(aliastransform)+32 // accum | v[2] | v[1] | v[0] - fld %st(2) // v[1] | accum | v[2] | v[1] | v[0] - fmuls C(aliastransform)+36 // accum2 | accum | v[2] | v[1] | v[0] - fxch %st(1) // accum | accum2 | v[2] | v[1] | v[0] - fadds C(aliastransform)+44 // accum | accum2 | v[2] | v[1] | v[0] - fld %st(2) // v[2] | accum | accum2 | v[2] | v[1] | v[0] - fmuls C(aliastransform)+40 // accum3 | accum | accum2 | v[2] | v[1] | - // v[0] - fxch %st(1) // accum | accum3 | accum2 | v[2] | v[1] | v[0] - faddp %st(0),%st(2) // accum3 | accum | v[2] | v[1] | v[0] - movb tv_lightnormalindex(%esi),%dl - movl stv_s(%ebp),%eax - movl %eax,fv_v+8(%edi) - faddp %st(0),%st(1) // z | v[2] | v[1] | v[0] - - movl stv_t(%ebp),%eax - movl %eax,fv_v+12(%edi) - -// // lighting -// plightnormal = r_avertexnormals[pverts->lightnormalindex]; - - fdivrs Lfloat_1 // zi | v[2] | v[1] | v[0] - -// fv->v[2] = pstverts->s; -// fv->v[3] = pstverts->t; -// fv->flags = pstverts->onseam; - movl stv_onseam(%ebp),%eax - movl %eax,fv_flags(%edi) - - movl fv_size(%edi),%eax - movl stv_size(%ebp),%eax - movl 4(%esi),%eax - - leal (%edx,%edx,2),%eax // index*3 - - fxch %st(3) // v[0] | v[2] | v[1] | zi - -// lightcos = DotProduct (plightnormal, r_plightvec); - flds C(r_avertexnormals)(,%eax,4) - fmuls C(r_plightvec) - flds C(r_avertexnormals)+4(,%eax,4) - fmuls C(r_plightvec)+4 - flds C(r_avertexnormals)+8(,%eax,4) - fmuls C(r_plightvec)+8 - fxch %st(1) - faddp %st(0),%st(2) - fld %st(2) // v[0] | laccum | laccum2 | v[0] | v[2] | - // v[1] | zi - fmuls C(aliastransform)+0 // xaccum | laccum | laccum2 | v[0] | v[2] | - // v[1] | zi - fxch %st(2) // laccum2 | laccum | xaccum | v[0] | v[2] | - // v[1] | zi - faddp %st(0),%st(1) // laccum | xaccum | v[0] | v[2] | v[1] | zi - -// temp = r_ambientlight; -// if (lightcos < 0) -// { - fsts Ltemp - movl C(r_ambientlight),%eax - movb Ltemp+3,%dl - testb $0x80,%dl - jz Lsavelight // no need to clamp if only ambient lit, because - // r_ambientlight is preclamped - -// temp += (int)(r_shadelight * lightcos); - fmuls C(r_shadelight) -// FIXME: fast float->int conversion? - fistpl Ltemp - addl Ltemp,%eax - -// // 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; - jns Lp1 - subl %eax,%eax - -// } - -Lp1: - -// fv->v[4] = temp; -// -// // x, y, and z are scaled down by 1/2**31 in the transform, so 1/z is -// // scaled up by 1/2**31, and the scaling cancels out for x and y in the -// // projection -// fv->v[0] = ((DotProduct(pverts->v, aliastransform[0]) + -// aliastransform[0][3]) * zi) + aliasxcenter; -// fv->v[1] = ((DotProduct(pverts->v, aliastransform[1]) + -// aliastransform[1][3]) * zi) + aliasycenter; -// fv->v[5] = zi; - fxch %st(1) // v[0] | xaccum | v[2] | v[1] | zi - fmuls C(aliastransform)+16 // yaccum | xaccum | v[2] | v[1] | zi - fxch %st(3) // v[1] | xaccum | v[2] | yaccum | zi - fld %st(0) // v[1] | v[1] | xaccum | v[2] | yaccum | zi - fmuls C(aliastransform)+4 // xaccum2 | v[1] | xaccum | v[2] | yaccum |zi - fxch %st(1) // v[1] | xaccum2 | xaccum | v[2] | yaccum |zi - movl %eax,fv_v+16(%edi) - fmuls C(aliastransform)+20 // yaccum2 | xaccum2 | xaccum | v[2] | yaccum| - // zi - fxch %st(2) // xaccum | xaccum2 | yaccum2 | v[2] | yaccum| - // zi - fadds C(aliastransform)+12 // xaccum | xaccum2 | yaccum2 | v[2] | yaccum| - // zi - fxch %st(4) // yaccum | xaccum2 | yaccum2 | v[2] | xaccum| - // zi - fadds C(aliastransform)+28 // yaccum | xaccum2 | yaccum2 | v[2] | xaccum| - // zi - fxch %st(3) // v[2] | xaccum2 | yaccum2 | yaccum | xaccum| - // zi - fld %st(0) // v[2] | v[2] | xaccum2 | yaccum2 | yaccum | - // xaccum | zi - fmuls C(aliastransform)+8 // xaccum3 | v[2] | xaccum2 | yaccum2 |yaccum| - // xaccum | zi - fxch %st(1) // v[2] | xaccum3 | xaccum2 | yaccum2 |yaccum| - // xaccum | zi - fmuls C(aliastransform)+24 // yaccum3 | xaccum3 | xaccum2 | yaccum2 | - // yaccum | xaccum | zi - fxch %st(5) // xaccum | xaccum3 | xaccum2 | yaccum2 | - // yaccum | yaccum3 | zi - faddp %st(0),%st(2) // xaccum3 | xaccum | yaccum2 | yaccum | - // yaccum3 | zi - fxch %st(3) // yaccum | xaccum | yaccum2 | xaccum3 | - // yaccum3 | zi - faddp %st(0),%st(2) // xaccum | yaccum | xaccum3 | yaccum3 | zi - addl $(tv_size),%esi - faddp %st(0),%st(2) // yaccum | x | yaccum3 | zi - faddp %st(0),%st(2) // x | y | zi - addl $(stv_size),%ebp - fmul %st(2),%st(0) // x/z | y | zi - fxch %st(1) // y | x/z | zi - fmul %st(2),%st(0) // y/z | x/z | zi - fxch %st(1) // x/z | y/z | zi - fadds C(aliasxcenter) // u | y/z | zi - fxch %st(1) // y/z | u | zi - fadds C(aliasycenter) // v | u | zi - fxch %st(2) // zi | u | v -// FIXME: fast float->int conversion? - fistpl fv_v+20(%edi) // u | v - fistpl fv_v+0(%edi) // v - fistpl fv_v+4(%edi) - -// } - - addl $(fv_size),%edi - decl %ecx - jnz Lloop - - popl %esi // restore register variables - popl %edi - popl %ebp // restore the caller's stack frame - ret - -Lsavelight: - fstp %st(0) - jmp Lp1 - -#endif // id386 - +/* + r_aliasa.S + + (description) + + Copyright (C) 1996-1997 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id: r_aliasa.s,v 1.1.1.4 2004/10/18 17:44:35 vvd0 Exp $ +*/ +// r_aliasa.s +// x86 assembly-language Alias model transform and project code. + +#include "asm_i386.h" +#include "quakeasm.h" +#include "asm_draw.h" +#include "d_ifacea.h" + +#ifdef id386 + + .data + +Lfloat_1: .single 1.0 +Ltemp: .long 0 +Lcoords: .long 0, 0, 0 + + .text + +#define fv 12+4 +#define pstverts 12+8 + +.globl C(R_AliasTransformAndProjectFinalVerts) +C(R_AliasTransformAndProjectFinalVerts): + pushl %ebp // preserve caller's stack frame + pushl %edi + pushl %esi // preserve register variables + +// int i, temp; +// float lightcos, *plightnormal, zi; +// trivertx_t *pverts; + +// pverts = r_apverts; + movl C(r_apverts),%esi + +// for (i=0 ; iv, aliastransform[2]) + +// aliastransform[2][3]); + movb (%esi),%dl + movb %dl,Lcoords + fildl Lcoords // v[0] + movb 1(%esi),%dl + movb %dl,Lcoords+4 + fildl Lcoords+4 // v[1] | v[0] + movb 2(%esi),%dl + movb %dl,Lcoords+8 + fildl Lcoords+8 // v[2] | v[1] | v[0] + + fld %st(2) // v[0] | v[2] | v[1] | v[0] + fmuls C(aliastransform)+32 // accum | v[2] | v[1] | v[0] + fld %st(2) // v[1] | accum | v[2] | v[1] | v[0] + fmuls C(aliastransform)+36 // accum2 | accum | v[2] | v[1] | v[0] + fxch %st(1) // accum | accum2 | v[2] | v[1] | v[0] + fadds C(aliastransform)+44 // accum | accum2 | v[2] | v[1] | v[0] + fld %st(2) // v[2] | accum | accum2 | v[2] | v[1] | v[0] + fmuls C(aliastransform)+40 // accum3 | accum | accum2 | v[2] | v[1] | + // v[0] + fxch %st(1) // accum | accum3 | accum2 | v[2] | v[1] | v[0] + faddp %st(0),%st(2) // accum3 | accum | v[2] | v[1] | v[0] + movb tv_lightnormalindex(%esi),%dl + movl stv_s(%ebp),%eax + movl %eax,fv_v+8(%edi) + faddp %st(0),%st(1) // z | v[2] | v[1] | v[0] + + movl stv_t(%ebp),%eax + movl %eax,fv_v+12(%edi) + +// // lighting +// plightnormal = r_avertexnormals[pverts->lightnormalindex]; + + fdivrs Lfloat_1 // zi | v[2] | v[1] | v[0] + +// fv->v[2] = pstverts->s; +// fv->v[3] = pstverts->t; +// fv->flags = pstverts->onseam; + movl stv_onseam(%ebp),%eax + movl %eax,fv_flags(%edi) + + movl fv_size(%edi),%eax + movl stv_size(%ebp),%eax + movl 4(%esi),%eax + + leal (%edx,%edx,2),%eax // index*3 + + fxch %st(3) // v[0] | v[2] | v[1] | zi + +// lightcos = DotProduct (plightnormal, r_plightvec); + flds C(r_avertexnormals)(,%eax,4) + fmuls C(r_plightvec) + flds C(r_avertexnormals)+4(,%eax,4) + fmuls C(r_plightvec)+4 + flds C(r_avertexnormals)+8(,%eax,4) + fmuls C(r_plightvec)+8 + fxch %st(1) + faddp %st(0),%st(2) + fld %st(2) // v[0] | laccum | laccum2 | v[0] | v[2] | + // v[1] | zi + fmuls C(aliastransform)+0 // xaccum | laccum | laccum2 | v[0] | v[2] | + // v[1] | zi + fxch %st(2) // laccum2 | laccum | xaccum | v[0] | v[2] | + // v[1] | zi + faddp %st(0),%st(1) // laccum | xaccum | v[0] | v[2] | v[1] | zi + +// temp = r_ambientlight; +// if (lightcos < 0) +// { + fsts Ltemp + movl C(r_ambientlight),%eax + movb Ltemp+3,%dl + testb $0x80,%dl + jz Lsavelight // no need to clamp if only ambient lit, because + // r_ambientlight is preclamped + +// temp += (int)(r_shadelight * lightcos); + fmuls C(r_shadelight) +// FIXME: fast float->int conversion? + fistpl Ltemp + addl Ltemp,%eax + +// // 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; + jns Lp1 + subl %eax,%eax + +// } + +Lp1: + +// fv->v[4] = temp; +// +// // x, y, and z are scaled down by 1/2**31 in the transform, so 1/z is +// // scaled up by 1/2**31, and the scaling cancels out for x and y in the +// // projection +// fv->v[0] = ((DotProduct(pverts->v, aliastransform[0]) + +// aliastransform[0][3]) * zi) + aliasxcenter; +// fv->v[1] = ((DotProduct(pverts->v, aliastransform[1]) + +// aliastransform[1][3]) * zi) + aliasycenter; +// fv->v[5] = zi; + fxch %st(1) // v[0] | xaccum | v[2] | v[1] | zi + fmuls C(aliastransform)+16 // yaccum | xaccum | v[2] | v[1] | zi + fxch %st(3) // v[1] | xaccum | v[2] | yaccum | zi + fld %st(0) // v[1] | v[1] | xaccum | v[2] | yaccum | zi + fmuls C(aliastransform)+4 // xaccum2 | v[1] | xaccum | v[2] | yaccum |zi + fxch %st(1) // v[1] | xaccum2 | xaccum | v[2] | yaccum |zi + movl %eax,fv_v+16(%edi) + fmuls C(aliastransform)+20 // yaccum2 | xaccum2 | xaccum | v[2] | yaccum| + // zi + fxch %st(2) // xaccum | xaccum2 | yaccum2 | v[2] | yaccum| + // zi + fadds C(aliastransform)+12 // xaccum | xaccum2 | yaccum2 | v[2] | yaccum| + // zi + fxch %st(4) // yaccum | xaccum2 | yaccum2 | v[2] | xaccum| + // zi + fadds C(aliastransform)+28 // yaccum | xaccum2 | yaccum2 | v[2] | xaccum| + // zi + fxch %st(3) // v[2] | xaccum2 | yaccum2 | yaccum | xaccum| + // zi + fld %st(0) // v[2] | v[2] | xaccum2 | yaccum2 | yaccum | + // xaccum | zi + fmuls C(aliastransform)+8 // xaccum3 | v[2] | xaccum2 | yaccum2 |yaccum| + // xaccum | zi + fxch %st(1) // v[2] | xaccum3 | xaccum2 | yaccum2 |yaccum| + // xaccum | zi + fmuls C(aliastransform)+24 // yaccum3 | xaccum3 | xaccum2 | yaccum2 | + // yaccum | xaccum | zi + fxch %st(5) // xaccum | xaccum3 | xaccum2 | yaccum2 | + // yaccum | yaccum3 | zi + faddp %st(0),%st(2) // xaccum3 | xaccum | yaccum2 | yaccum | + // yaccum3 | zi + fxch %st(3) // yaccum | xaccum | yaccum2 | xaccum3 | + // yaccum3 | zi + faddp %st(0),%st(2) // xaccum | yaccum | xaccum3 | yaccum3 | zi + addl $(tv_size),%esi + faddp %st(0),%st(2) // yaccum | x | yaccum3 | zi + faddp %st(0),%st(2) // x | y | zi + addl $(stv_size),%ebp + fmul %st(2),%st(0) // x/z | y | zi + fxch %st(1) // y | x/z | zi + fmul %st(2),%st(0) // y/z | x/z | zi + fxch %st(1) // x/z | y/z | zi + fadds C(aliasxcenter) // u | y/z | zi + fxch %st(1) // y/z | u | zi + fadds C(aliasycenter) // v | u | zi + fxch %st(2) // zi | u | v +// FIXME: fast float->int conversion? + fistpl fv_v+20(%edi) // u | v + fistpl fv_v+0(%edi) // v + fistpl fv_v+4(%edi) + +// } + + addl $(fv_size),%edi + decl %ecx + jnz Lloop + + popl %esi // restore register variables + popl %edi + popl %ebp // restore the caller's stack frame + ret + +Lsavelight: + fstp %st(0) + jmp Lp1 + +#endif // id386 + diff --git a/source/r_bsp.c b/source/r_bsp.c index 3c9338c5..9592f9ab 100644 --- a/source/r_bsp.c +++ b/source/r_bsp.c @@ -1,674 +1,674 @@ -/* -Copyright (C) 1996-1997 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 "quakedef.h" -#include "r_local.h" - -// -// current entity info -// -qboolean insubmodel; -entity_t *currententity; -vec3_t modelorg, base_modelorg; - // modelorg is the viewpoint relative to - // the currently rendering entity -vec3_t r_entorigin; // the currently rendering entity in world - // coordinates - -float entity_rotation[3][3]; - -vec3_t r_worldmodelorg; - -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 *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 -================ -*/ -void R_RecursiveClipBPoly (bedge_t *pedges, mnode_t *pnode, msurface_t *psurf) -{ - bedge_t *psideedges[2], *pnextedge, *ptedge; - int i, side, lastside; - float dist, frac, lastdist; - mplane_t *splitplane, tplane; - mvertex_t *pvert, *plastvert, *ptvert; - mnode_t *pn; - - 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) - { - 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)) - { - Con_Printf ("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)) - { - Con_Printf ("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 < 0) - { - if (pn->contents != CONTENTS_SOLID) - { - r_currentbkey = ((mleaf_t *)pn)->key; - R_RenderBmodelFace (psideedges[i], psurf); - } - } - else - { - R_RecursiveClipBPoly (psideedges[i], pnode->children[i], - psurf); - } - } - } - } -} - - -/* -================ -R_DrawSolidClippedSubmodelPolygons -================ -*/ -void R_DrawSolidClippedSubmodelPolygons (model_t *pmodel) -{ - int i, j, lindex; - vec_t dot; - msurface_t *psurf; - int numsurfaces; - mplane_t *pplane; - mvertex_t bverts[MAX_BMODEL_VERTS]; - bedge_t bedges[MAX_BMODEL_EDGES], *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))) - { - // 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; - - if (psurf->numedges > 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 - - R_RecursiveClipBPoly (pbedge, currententity->topnode, psurf); - } - else - { - Sys_Error ("no edges in bmodel"); - } - } - } -} - - -/* -================ -R_DrawSubmodelPolygons -================ -*/ -void R_DrawSubmodelPolygons (model_t *pmodel, int clipflags) -{ - int i; - vec_t dot; - msurface_t *psurf; - int numsurfaces; - mplane_t *pplane; - -// 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 *)currententity->topnode)->key; - - // FIXME: use bounding-box-based frustum clipping info? - R_RenderFace (psurf, clipflags); - } - } -} - - -/* -================ -R_RecursiveWorldNode -================ -*/ -void R_RecursiveWorldNode (mnode_t *node, int clipflags) -{ - int i, c, side, *pindex; - vec3_t acceptpt, rejectpt; - mplane_t *plane; - msurface_t *surf, **mark; - mleaf_t *pleaf; - double d, dot; - - 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) - { - for (i=0 ; i<4 ; i++) - { - 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 < 0) - { - pleaf = (mleaf_t *)node; - - mark = pleaf->firstmarksurface; - c = pleaf->nummarksurfaces; - - if (c) - { - do - { - (*mark)->visframe = r_framecount; - mark++; - } while (--c); - } - - // deal with model fragments in this leaf - if (pleaf->efrags) - { - R_StoreEfrags (&pleaf->efrags); - } - - pleaf->key = r_currentkey; - r_currentkey++; // all bmodels in a leaf share the same key - } - else - { - // 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) - { - surf = cl.worldmodel->surfaces + node->firstsurface; - - if (dot < -BACKFACE_EPSILON) - { - do - { - if ((surf->flags & SURF_PLANEBACK) && - (surf->visframe == r_framecount)) - { - if (r_drawpolys) - { - if (r_worldpolysbacktofront) - { - if (numbtofpolys < MAX_BTOFPOLYS) - { - pbtofpolys[numbtofpolys].clipflags = - clipflags; - pbtofpolys[numbtofpolys].psurf = surf; - numbtofpolys++; - } - } - else - { - R_RenderPoly (surf, clipflags); - } - } - else - { - R_RenderFace (surf, clipflags); - } - } - - surf++; - } while (--c); - } - else if (dot > BACKFACE_EPSILON) - { - do - { - if (!(surf->flags & SURF_PLANEBACK) && - (surf->visframe == r_framecount)) - { - if (r_drawpolys) - { - if (r_worldpolysbacktofront) - { - if (numbtofpolys < MAX_BTOFPOLYS) - { - pbtofpolys[numbtofpolys].clipflags = - clipflags; - pbtofpolys[numbtofpolys].psurf = surf; - numbtofpolys++; - } - } - else - { - R_RenderPoly (surf, clipflags); - } - } - else - { - 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) -{ - int i; - model_t *clmodel; - btofpoly_t btofpolys[MAX_BTOFPOLYS]; - - pbtofpolys = btofpolys; - - currententity = &r_worldentity; - VectorCopy (r_origin, modelorg); - clmodel = currententity->model; - r_pcurrentvertbase = clmodel->vertexes; - - R_RecursiveWorldNode (clmodel->nodes, 15); - -// if the driver wants the polygons back to front, play the visible ones back -// in that order - if (r_worldpolysbacktofront) - { - for (i=numbtofpolys-1 ; i>=0 ; i--) - { - R_RenderPoly (btofpolys[i].psurf, btofpolys[i].clipflags); - } - } -} - - +/* +Copyright (C) 1996-1997 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 "quakedef.h" +#include "r_local.h" + +// +// current entity info +// +qboolean insubmodel; +entity_t *currententity; +vec3_t modelorg, base_modelorg; + // modelorg is the viewpoint relative to + // the currently rendering entity +vec3_t r_entorigin; // the currently rendering entity in world + // coordinates + +float entity_rotation[3][3]; + +vec3_t r_worldmodelorg; + +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 *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 +================ +*/ +void R_RecursiveClipBPoly (bedge_t *pedges, mnode_t *pnode, msurface_t *psurf) +{ + bedge_t *psideedges[2], *pnextedge, *ptedge; + int i, side, lastside; + float dist, frac, lastdist; + mplane_t *splitplane, tplane; + mvertex_t *pvert, *plastvert, *ptvert; + mnode_t *pn; + + 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) + { + 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)) + { + Con_Printf ("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)) + { + Con_Printf ("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 < 0) + { + if (pn->contents != CONTENTS_SOLID) + { + r_currentbkey = ((mleaf_t *)pn)->key; + R_RenderBmodelFace (psideedges[i], psurf); + } + } + else + { + R_RecursiveClipBPoly (psideedges[i], pnode->children[i], + psurf); + } + } + } + } +} + + +/* +================ +R_DrawSolidClippedSubmodelPolygons +================ +*/ +void R_DrawSolidClippedSubmodelPolygons (model_t *pmodel) +{ + int i, j, lindex; + vec_t dot; + msurface_t *psurf; + int numsurfaces; + mplane_t *pplane; + mvertex_t bverts[MAX_BMODEL_VERTS]; + bedge_t bedges[MAX_BMODEL_EDGES], *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))) + { + // 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; + + if (psurf->numedges > 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 + + R_RecursiveClipBPoly (pbedge, currententity->topnode, psurf); + } + else + { + Sys_Error ("no edges in bmodel"); + } + } + } +} + + +/* +================ +R_DrawSubmodelPolygons +================ +*/ +void R_DrawSubmodelPolygons (model_t *pmodel, int clipflags) +{ + int i; + vec_t dot; + msurface_t *psurf; + int numsurfaces; + mplane_t *pplane; + +// 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 *)currententity->topnode)->key; + + // FIXME: use bounding-box-based frustum clipping info? + R_RenderFace (psurf, clipflags); + } + } +} + + +/* +================ +R_RecursiveWorldNode +================ +*/ +void R_RecursiveWorldNode (mnode_t *node, int clipflags) +{ + int i, c, side, *pindex; + vec3_t acceptpt, rejectpt; + mplane_t *plane; + msurface_t *surf, **mark; + mleaf_t *pleaf; + double d, dot; + + 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) + { + for (i=0 ; i<4 ; i++) + { + 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 < 0) + { + pleaf = (mleaf_t *)node; + + mark = pleaf->firstmarksurface; + c = pleaf->nummarksurfaces; + + if (c) + { + do + { + (*mark)->visframe = r_framecount; + mark++; + } while (--c); + } + + // deal with model fragments in this leaf + if (pleaf->efrags) + { + R_StoreEfrags (&pleaf->efrags); + } + + pleaf->key = r_currentkey; + r_currentkey++; // all bmodels in a leaf share the same key + } + else + { + // 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) + { + surf = cl.worldmodel->surfaces + node->firstsurface; + + if (dot < -BACKFACE_EPSILON) + { + do + { + if ((surf->flags & SURF_PLANEBACK) && + (surf->visframe == r_framecount)) + { + if (r_drawpolys) + { + if (r_worldpolysbacktofront) + { + if (numbtofpolys < MAX_BTOFPOLYS) + { + pbtofpolys[numbtofpolys].clipflags = + clipflags; + pbtofpolys[numbtofpolys].psurf = surf; + numbtofpolys++; + } + } + else + { + R_RenderPoly (surf, clipflags); + } + } + else + { + R_RenderFace (surf, clipflags); + } + } + + surf++; + } while (--c); + } + else if (dot > BACKFACE_EPSILON) + { + do + { + if (!(surf->flags & SURF_PLANEBACK) && + (surf->visframe == r_framecount)) + { + if (r_drawpolys) + { + if (r_worldpolysbacktofront) + { + if (numbtofpolys < MAX_BTOFPOLYS) + { + pbtofpolys[numbtofpolys].clipflags = + clipflags; + pbtofpolys[numbtofpolys].psurf = surf; + numbtofpolys++; + } + } + else + { + R_RenderPoly (surf, clipflags); + } + } + else + { + 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) +{ + int i; + model_t *clmodel; + btofpoly_t btofpolys[MAX_BTOFPOLYS]; + + pbtofpolys = btofpolys; + + currententity = &r_worldentity; + VectorCopy (r_origin, modelorg); + clmodel = currententity->model; + r_pcurrentvertbase = clmodel->vertexes; + + R_RecursiveWorldNode (clmodel->nodes, 15); + +// if the driver wants the polygons back to front, play the visible ones back +// in that order + if (r_worldpolysbacktofront) + { + for (i=numbtofpolys-1 ; i>=0 ; i--) + { + R_RenderPoly (btofpolys[i].psurf, btofpolys[i].clipflags); + } + } +} + + diff --git a/source/r_draw.c b/source/r_draw.c index a94f82ed..81262f74 100644 --- a/source/r_draw.c +++ b/source/r_draw.c @@ -1,908 +1,908 @@ -/* -Copyright (C) 1996-1997 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_draw.c - -#include "quakedef.h" -#include "r_local.h" -#include "d_local.h" // FIXME: shouldn't need to include this - -#define MAXLEFTCLIPEDGES 100 - -// !!! if these are changed, they must be changed in asm_draw.h too !!! -#define FULLY_CLIPPED_CACHED 0x80000000 -#define FRAMECOUNT_MASK 0x7FFFFFFF - -unsigned int cacheoffset; - -int c_faceclip; // number of faces clipped - -zpointdesc_t r_zpointdesc; - -polydesc_t r_polydesc; - - - -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[1280]; -int intsintable[1280]; - -mvertex_t r_leftenter, r_leftexit; -mvertex_t r_rightenter, r_rightexit; - -typedef struct -{ - float u,v; - int ceilv; -} evert_t; - -int r_emitted; -float r_nearzi; -float r_u1, r_v1, r_lzi1; -int r_ceilv1; - -qboolean r_lastvertvalid; - - -#if !id386 - -/* -================ -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*0x100000; - edge->u = u*0x100000 + 0xFFFFF; - -// we need to do this to avoid stepping off the edges if a very nearly -// horizontal edge is less than epsilon above a scan, and numeric error causes -// it to incorrectly extend to the scan, and the extension of the line goes off -// the edge of the screen -// FIXME: is this actually needed? - if (edge->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) -{ - float d0, d1, f; - mvertex_t clipvert; - - if (clip) - { - do - { - 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); -} - -#endif // !id386 - - -/* -================ -R_EmitCachedEdge -================ -*/ -void R_EmitCachedEdge (void) -{ - edge_t *pedge_t; - - pedge_t = (edge_t *)((unsigned long)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, lindex; - unsigned mask; - mplane_t *pplane; - float distinv; - vec3_t p_normal; - medge_t *pedges, tedge; - clipplane_t *pclip; - -// 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 = currententity->model->edges; - r_lastvertvalid = false; - - for (i=0 ; inumedges ; i++) - { - lindex = currententity->model->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 ((((unsigned long)edge_p - (unsigned long)r_edges) > - r_pedge->cachededgeoffset) && - (((edge_t *)((unsigned long)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 ((((unsigned long)edge_p - (unsigned long)r_edges) > - r_pedge->cachededgeoffset) && - (((edge_t *)((unsigned long)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->data = (void *)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; - -//JDC VectorCopy (r_worldmodelorg, surface_p->modelorg); - 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; - -// 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->data = (void *)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; - -//JDC VectorCopy (r_worldmodelorg, surface_p->modelorg); - surface_p++; -} - - -/* -================ -R_RenderPoly -================ -*/ -void R_RenderPoly (msurface_t *fa, int clipflags) -{ - int i, lindex, lnumverts, s_axis, t_axis; - float dist, lastdist, lzi, scale, u, v, frac; - unsigned mask; - vec3_t local, transformed; - clipplane_t *pclip; - medge_t *pedges; - mplane_t *pplane; - mvertex_t verts[2][100]; //FIXME: do real number - polyvert_t pverts[100]; //FIXME: do real number, safely - int vertpage, newverts, newpage, lastvert; - qboolean visible; - -// FIXME: clean this up and make it faster -// FIXME: guard against running out of vertices - - s_axis = t_axis = 0; // keep compiler happy - -// 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]; - } - } - -// reconstruct the polygon -// FIXME: these should be precalculated and loaded off disk - pedges = currententity->model->edges; - lnumverts = fa->numedges; - vertpage = 0; - - for (i=0 ; imodel->surfedges[fa->firstedge + i]; - - if (lindex > 0) - { - r_pedge = &pedges[lindex]; - verts[0][i] = r_pcurrentvertbase[r_pedge->v[0]]; - } - else - { - r_pedge = &pedges[-lindex]; - verts[0][i] = r_pcurrentvertbase[r_pedge->v[1]]; - } - } - -// clip the polygon, done if not visible - while (pclip) - { - lastvert = lnumverts - 1; - lastdist = DotProduct (verts[vertpage][lastvert].position, - pclip->normal) - pclip->dist; - - visible = false; - newverts = 0; - newpage = vertpage ^ 1; - - for (i=0 ; inormal) - - pclip->dist; - - if ((lastdist > 0) != (dist > 0)) - { - frac = dist / (dist - lastdist); - verts[newpage][newverts].position[0] = - verts[vertpage][i].position[0] + - ((verts[vertpage][lastvert].position[0] - - verts[vertpage][i].position[0]) * frac); - verts[newpage][newverts].position[1] = - verts[vertpage][i].position[1] + - ((verts[vertpage][lastvert].position[1] - - verts[vertpage][i].position[1]) * frac); - verts[newpage][newverts].position[2] = - verts[vertpage][i].position[2] + - ((verts[vertpage][lastvert].position[2] - - verts[vertpage][i].position[2]) * frac); - newverts++; - } - - if (dist >= 0) - { - verts[newpage][newverts] = verts[vertpage][i]; - newverts++; - visible = true; - } - - lastvert = i; - lastdist = dist; - } - - if (!visible || (newverts < 3)) - return; - - lnumverts = newverts; - vertpage ^= 1; - pclip = pclip->next; - } - -// transform and project, remembering the z values at the vertices and -// r_nearzi, and extract the s and t coordinates at the vertices - pplane = fa->plane; - switch (pplane->type) - { - case PLANE_X: - case PLANE_ANYX: - s_axis = 1; - t_axis = 2; - break; - case PLANE_Y: - case PLANE_ANYY: - s_axis = 0; - t_axis = 2; - break; - case PLANE_Z: - case PLANE_ANYZ: - s_axis = 0; - t_axis = 1; - break; - } - - r_nearzi = 0; - - for (i=0 ; i r_nearzi) // for mipmap finding - r_nearzi = lzi; - - // FIXME: build x/yscale into transform? - scale = xscale * lzi; - u = (xcenter + scale*transformed[0]); - if (u < r_refdef.fvrectx_adj) - u = r_refdef.fvrectx_adj; - if (u > r_refdef.fvrectright_adj) - u = r_refdef.fvrectright_adj; - - scale = yscale * lzi; - v = (ycenter - scale*transformed[1]); - if (v < r_refdef.fvrecty_adj) - v = r_refdef.fvrecty_adj; - if (v > r_refdef.fvrectbottom_adj) - v = r_refdef.fvrectbottom_adj; - - pverts[i].u = u; - pverts[i].v = v; - pverts[i].zi = lzi; - pverts[i].s = verts[vertpage][i].position[s_axis]; - pverts[i].t = verts[vertpage][i].position[t_axis]; - } - -// build the polygon descriptor, including fa, r_nearzi, and u, v, s, t, and z -// for each vertex - r_polydesc.numverts = lnumverts; - r_polydesc.nearzi = r_nearzi; - r_polydesc.pcurrentface = fa; - r_polydesc.pverts = pverts; - -// draw the polygon - D_DrawPoly (); -} - - -/* -================ -R_ZDrawSubmodelPolys -================ -*/ -void R_ZDrawSubmodelPolys (model_t *pmodel) -{ - int i, numsurfaces; - msurface_t *psurf; - float dot; - mplane_t *pplane; - - 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))) - { - // FIXME: use bounding-box-based frustum clipping info? - R_RenderPoly (psurf, 15); - } - } -} - +/* +Copyright (C) 1996-1997 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_draw.c + +#include "quakedef.h" +#include "r_local.h" +#include "d_local.h" // FIXME: shouldn't need to include this + +#define MAXLEFTCLIPEDGES 100 + +// !!! if these are changed, they must be changed in asm_draw.h too !!! +#define FULLY_CLIPPED_CACHED 0x80000000 +#define FRAMECOUNT_MASK 0x7FFFFFFF + +unsigned int cacheoffset; + +int c_faceclip; // number of faces clipped + +zpointdesc_t r_zpointdesc; + +polydesc_t r_polydesc; + + + +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[1280]; +int intsintable[1280]; + +mvertex_t r_leftenter, r_leftexit; +mvertex_t r_rightenter, r_rightexit; + +typedef struct +{ + float u,v; + int ceilv; +} evert_t; + +int r_emitted; +float r_nearzi; +float r_u1, r_v1, r_lzi1; +int r_ceilv1; + +qboolean r_lastvertvalid; + + +#if !id386 + +/* +================ +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*0x100000; + edge->u = u*0x100000 + 0xFFFFF; + +// we need to do this to avoid stepping off the edges if a very nearly +// horizontal edge is less than epsilon above a scan, and numeric error causes +// it to incorrectly extend to the scan, and the extension of the line goes off +// the edge of the screen +// FIXME: is this actually needed? + if (edge->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) +{ + float d0, d1, f; + mvertex_t clipvert; + + if (clip) + { + do + { + 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); +} + +#endif // !id386 + + +/* +================ +R_EmitCachedEdge +================ +*/ +void R_EmitCachedEdge (void) +{ + edge_t *pedge_t; + + pedge_t = (edge_t *)((unsigned long)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, lindex; + unsigned mask; + mplane_t *pplane; + float distinv; + vec3_t p_normal; + medge_t *pedges, tedge; + clipplane_t *pclip; + +// 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 = currententity->model->edges; + r_lastvertvalid = false; + + for (i=0 ; inumedges ; i++) + { + lindex = currententity->model->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 ((((unsigned long)edge_p - (unsigned long)r_edges) > + r_pedge->cachededgeoffset) && + (((edge_t *)((unsigned long)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 ((((unsigned long)edge_p - (unsigned long)r_edges) > + r_pedge->cachededgeoffset) && + (((edge_t *)((unsigned long)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->data = (void *)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; + +//JDC VectorCopy (r_worldmodelorg, surface_p->modelorg); + 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; + +// 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->data = (void *)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; + +//JDC VectorCopy (r_worldmodelorg, surface_p->modelorg); + surface_p++; +} + + +/* +================ +R_RenderPoly +================ +*/ +void R_RenderPoly (msurface_t *fa, int clipflags) +{ + int i, lindex, lnumverts, s_axis, t_axis; + float dist, lastdist, lzi, scale, u, v, frac; + unsigned mask; + vec3_t local, transformed; + clipplane_t *pclip; + medge_t *pedges; + mplane_t *pplane; + mvertex_t verts[2][100]; //FIXME: do real number + polyvert_t pverts[100]; //FIXME: do real number, safely + int vertpage, newverts, newpage, lastvert; + qboolean visible; + +// FIXME: clean this up and make it faster +// FIXME: guard against running out of vertices + + s_axis = t_axis = 0; // keep compiler happy + +// 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]; + } + } + +// reconstruct the polygon +// FIXME: these should be precalculated and loaded off disk + pedges = currententity->model->edges; + lnumverts = fa->numedges; + vertpage = 0; + + for (i=0 ; imodel->surfedges[fa->firstedge + i]; + + if (lindex > 0) + { + r_pedge = &pedges[lindex]; + verts[0][i] = r_pcurrentvertbase[r_pedge->v[0]]; + } + else + { + r_pedge = &pedges[-lindex]; + verts[0][i] = r_pcurrentvertbase[r_pedge->v[1]]; + } + } + +// clip the polygon, done if not visible + while (pclip) + { + lastvert = lnumverts - 1; + lastdist = DotProduct (verts[vertpage][lastvert].position, + pclip->normal) - pclip->dist; + + visible = false; + newverts = 0; + newpage = vertpage ^ 1; + + for (i=0 ; inormal) - + pclip->dist; + + if ((lastdist > 0) != (dist > 0)) + { + frac = dist / (dist - lastdist); + verts[newpage][newverts].position[0] = + verts[vertpage][i].position[0] + + ((verts[vertpage][lastvert].position[0] - + verts[vertpage][i].position[0]) * frac); + verts[newpage][newverts].position[1] = + verts[vertpage][i].position[1] + + ((verts[vertpage][lastvert].position[1] - + verts[vertpage][i].position[1]) * frac); + verts[newpage][newverts].position[2] = + verts[vertpage][i].position[2] + + ((verts[vertpage][lastvert].position[2] - + verts[vertpage][i].position[2]) * frac); + newverts++; + } + + if (dist >= 0) + { + verts[newpage][newverts] = verts[vertpage][i]; + newverts++; + visible = true; + } + + lastvert = i; + lastdist = dist; + } + + if (!visible || (newverts < 3)) + return; + + lnumverts = newverts; + vertpage ^= 1; + pclip = pclip->next; + } + +// transform and project, remembering the z values at the vertices and +// r_nearzi, and extract the s and t coordinates at the vertices + pplane = fa->plane; + switch (pplane->type) + { + case PLANE_X: + case PLANE_ANYX: + s_axis = 1; + t_axis = 2; + break; + case PLANE_Y: + case PLANE_ANYY: + s_axis = 0; + t_axis = 2; + break; + case PLANE_Z: + case PLANE_ANYZ: + s_axis = 0; + t_axis = 1; + break; + } + + r_nearzi = 0; + + for (i=0 ; i r_nearzi) // for mipmap finding + r_nearzi = lzi; + + // FIXME: build x/yscale into transform? + scale = xscale * lzi; + u = (xcenter + scale*transformed[0]); + if (u < r_refdef.fvrectx_adj) + u = r_refdef.fvrectx_adj; + if (u > r_refdef.fvrectright_adj) + u = r_refdef.fvrectright_adj; + + scale = yscale * lzi; + v = (ycenter - scale*transformed[1]); + if (v < r_refdef.fvrecty_adj) + v = r_refdef.fvrecty_adj; + if (v > r_refdef.fvrectbottom_adj) + v = r_refdef.fvrectbottom_adj; + + pverts[i].u = u; + pverts[i].v = v; + pverts[i].zi = lzi; + pverts[i].s = verts[vertpage][i].position[s_axis]; + pverts[i].t = verts[vertpage][i].position[t_axis]; + } + +// build the polygon descriptor, including fa, r_nearzi, and u, v, s, t, and z +// for each vertex + r_polydesc.numverts = lnumverts; + r_polydesc.nearzi = r_nearzi; + r_polydesc.pcurrentface = fa; + r_polydesc.pverts = pverts; + +// draw the polygon + D_DrawPoly (); +} + + +/* +================ +R_ZDrawSubmodelPolys +================ +*/ +void R_ZDrawSubmodelPolys (model_t *pmodel) +{ + int i, numsurfaces; + msurface_t *psurf; + float dot; + mplane_t *pplane; + + 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))) + { + // FIXME: use bounding-box-based frustum clipping info? + R_RenderPoly (psurf, 15); + } + } +} + diff --git a/source/r_drawa.s b/source/r_drawa.s index e9c77c11..69a4da97 100644 --- a/source/r_drawa.s +++ b/source/r_drawa.s @@ -1,844 +1,844 @@ -/* - r_drawa.S - - (description) - - Copyright (C) 1996-1997 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: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - - $Id: r_drawa.s,v 1.1.1.3 2004/10/13 18:54:45 vvd0 Exp $ -*/ -// r_drawa.s -// x86 assembly-language edge clipping and emission code - -#include "asm_i386.h" -#include "quakeasm.h" -#include "asm_draw.h" -#include "d_ifacea.h" - -#ifdef id386 - -// !!! if these are changed, they must be changed in r_draw.c too !!! -#define FULLY_CLIPPED_CACHED 0x80000000 -#define FRAMECOUNT_MASK 0x7FFFFFFF - - .data - -Ld0: .single 0.0 -Ld1: .single 0.0 -Lstack: .long 0 -Lfp_near_clip: .single NEAR_CLIP -Lceilv0: .long 0 -Lv: .long 0 -Lu0: .long 0 -Lv0: .long 0 -Lzi0: .long 0 - - .text - -//---------------------------------------------------------------------- -// edge clipping code -//---------------------------------------------------------------------- - -#define pv0 4+12 -#define pv1 8+12 -#define clip 12+12 - - .align 4 -.globl C(R_ClipEdge) -C(R_ClipEdge): - pushl %esi // preserve register variables - pushl %edi - pushl %ebx - movl %esp,Lstack // for clearing the stack later - -// float d0, d1, f; -// mvertex_t clipvert; - - movl clip(%esp),%ebx - movl pv0(%esp),%esi - movl pv1(%esp),%edx - -// if (clip) -// { - testl %ebx,%ebx - jz Lemit - -// do -// { - -Lcliploop: - -// d0 = DotProduct (pv0->position, clip->normal) - clip->dist; -// d1 = DotProduct (pv1->position, clip->normal) - clip->dist; - flds mv_position+0(%esi) - fmuls cp_normal+0(%ebx) - flds mv_position+4(%esi) - fmuls cp_normal+4(%ebx) - flds mv_position+8(%esi) - fmuls cp_normal+8(%ebx) - fxch %st(1) - faddp %st(0),%st(2) // d0mul2 | d0add0 - - flds mv_position+0(%edx) - fmuls cp_normal+0(%ebx) - flds mv_position+4(%edx) - fmuls cp_normal+4(%ebx) - flds mv_position+8(%edx) - fmuls cp_normal+8(%ebx) - fxch %st(1) - faddp %st(0),%st(2) // d1mul2 | d1add0 | d0mul2 | d0add0 - fxch %st(3) // d0add0 | d1add0 | d0mul2 | d1mul2 - - faddp %st(0),%st(2) // d1add0 | dot0 | d1mul2 - faddp %st(0),%st(2) // dot0 | dot1 - - fsubs cp_dist(%ebx) // d0 | dot1 - fxch %st(1) // dot1 | d0 - fsubs cp_dist(%ebx) // d1 | d0 - fxch %st(1) - fstps Ld0 - fstps Ld1 - -// if (d0 >= 0) -// { - movl Ld0,%eax - movl Ld1,%ecx - orl %eax,%ecx - js Lp2 - -// both points are unclipped - -Lcontinue: - -// -// R_ClipEdge (&clipvert, pv1, clip->next); -// return; -// } -// } while ((clip = clip->next) != NULL); - movl cp_next(%ebx),%ebx - testl %ebx,%ebx - jnz Lcliploop - -// } - -//// add the edge -// R_EmitEdge (pv0, pv1); -Lemit: - -// -// set integer rounding to ceil mode, set to single precision -// -// FIXME: do away with by manually extracting integers from floats? -// FIXME: set less often - fldcw ceil_cw - -// 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) -// { - cmpl $0,C(r_lastvertvalid) - jz LCalcFirst - -// u0 = r_u1; -// v0 = r_v1; -// lzi0 = r_lzi1; -// ceilv0 = r_ceilv1; - movl C(r_lzi1),%eax - movl C(r_u1),%ecx - movl %eax,Lzi0 - movl %ecx,Lu0 - movl C(r_v1),%ecx - movl C(r_ceilv1),%eax - movl %ecx,Lv0 - movl %eax,Lceilv0 - jmp LCalcSecond - -// } - -LCalcFirst: - -// else -// { -// world = &pv0->position[0]; - - call LTransformAndProject // v0 | lzi0 | u0 - - fsts Lv0 - fxch %st(2) // u0 | lzi0 | v0 - fstps Lu0 // lzi0 | v0 - fstps Lzi0 // v0 - -// ceilv0 = (int)(v0 - 2000) + 2000; // ceil(v0); - fistpl Lceilv0 - -// } - -LCalcSecond: - -// world = &pv1->position[0]; - movl %edx,%esi - - call LTransformAndProject // v1 | lzi1 | u1 - - flds Lu0 // u0 | v1 | lzi1 | u1 - fxch %st(3) // u1 | v1 | lzi1 | u0 - flds Lzi0 // lzi0 | u1 | v1 | lzi1 | u0 - fxch %st(3) // lzi1 | u1 | v1 | lzi0 | u0 - flds Lv0 // v0 | lzi1 | u1 | v1 | lzi0 | u0 - fxch %st(3) // v1 | lzi1 | u1 | v0 | lzi0 | u0 - -// r_ceilv1 = (int)(r_v1 - 2000) + 2000; // ceil(r_v1); - fistl C(r_ceilv1) - - fldcw single_cw // put back normal floating-point state - - fsts C(r_v1) - fxch %st(4) // lzi0 | lzi1 | u1 | v0 | v1 | u0 - -// if (r_lzi1 > lzi0) -// lzi0 = r_lzi1; - fcom %st(1) - fnstsw %ax - testb $1,%ah - jz LP0 - fstp %st(0) - fld %st(0) -LP0: - - fxch %st(1) // lzi1 | lzi0 | u1 | v0 | v1 | u0 - fstps C(r_lzi1) // lzi0 | u1 | v0 | v1 | u0 - fxch %st(1) - fsts C(r_u1) - fxch %st(1) - -// if (lzi0 > r_nearzi) // for mipmap finding -// r_nearzi = lzi0; - fcoms C(r_nearzi) - fnstsw %ax - testb $0x45,%ah - jnz LP1 - fsts C(r_nearzi) -LP1: - -// // for right edges, all we want is the effect on 1/z -// if (r_nearzionly) -// return; - movl C(r_nearzionly),%eax - testl %eax,%eax - jz LP2 -LPop5AndDone: - movl C(cacheoffset),%eax - movl C(r_framecount),%edx - cmpl $0x7FFFFFFF,%eax - jz LDoPop - andl $(FRAMECOUNT_MASK),%edx - orl $(FULLY_CLIPPED_CACHED),%edx - movl %edx,C(cacheoffset) - -LDoPop: - fstp %st(0) // u1 | v0 | v1 | u0 - fstp %st(0) // v0 | v1 | u0 - fstp %st(0) // v1 | u0 - fstp %st(0) // u0 - fstp %st(0) - jmp Ldone - -LP2: - -// // create the edge -// if (ceilv0 == r_ceilv1) -// return; // horizontal edge - movl Lceilv0,%ebx - movl C(edge_p),%edi - movl C(r_ceilv1),%ecx - movl %edi,%edx - movl C(r_pedge),%esi - addl $(et_size),%edx - cmpl %ecx,%ebx - jz LPop5AndDone - - movl C(r_pedge),%eax - movl %eax,et_owner(%edi) - -// side = ceilv0 > r_ceilv1; -// -// edge->nearzi = lzi0; - fstps et_nearzi(%edi) // u1 | v0 | v1 | u0 - -// if (side == 1) -// { - jc LSide0 - -LSide1: - -// // leading edge (go from p2 to p1) - -// u_step = ((u0 - r_u1) / (v0 - r_v1)); - fsubrp %st(0),%st(3) // v0 | v1 | u0-u1 - fsub %st(1),%st(0) // v0-v1 | v1 | u0-u1 - fdivrp %st(0),%st(2) // v1 | ustep - -// r_emitted = 1; - movl $1,C(r_emitted) - -// edge = edge_p++; - movl %edx,C(edge_p) - -// pretouch next edge - movl (%edx),%eax - -// v2 = ceilv0 - 1; -// v = r_ceilv1; - movl %ecx,%eax - leal -1(%ebx),%ecx - movl %eax,%ebx - -// edge->surfs[0] = 0; -// edge->surfs[1] = surface_p - surfaces; - movl C(surface_p),%eax - movl C(surfaces),%esi - subl %edx,%edx - subl %esi,%eax - shrl $(SURF_T_SHIFT),%eax - movl %edx,et_surfs(%edi) - movl %eax,et_surfs+2(%edi) - - subl %esi,%esi - -// u = r_u1 + ((float)v - r_v1) * u_step; - movl %ebx,Lv - fildl Lv // v | v1 | ustep - fsubp %st(0),%st(1) // v-v1 | ustep - fmul %st(1),%st(0) // (v-v1)*ustep | ustep - fadds C(r_u1) // u | ustep - - jmp LSideDone - -// } - -LSide0: - -// else -// { -// // trailing edge (go from p1 to p2) - -// u_step = ((r_u1 - u0) / (r_v1 - v0)); - fsub %st(3),%st(0) // u1-u0 | v0 | v1 | u0 - fxch %st(2) // v1 | v0 | u1-u0 | u0 - fsub %st(1),%st(0) // v1-v0 | v0 | u1-u0 | u0 - fdivrp %st(0),%st(2) // v0 | ustep | u0 - -// r_emitted = 1; - movl $1,C(r_emitted) - -// edge = edge_p++; - movl %edx,C(edge_p) - -// pretouch next edge - movl (%edx),%eax - -// v = ceilv0; -// v2 = r_ceilv1 - 1; - decl %ecx - -// edge->surfs[0] = surface_p - surfaces; -// edge->surfs[1] = 0; - movl C(surface_p),%eax - movl C(surfaces),%esi - subl %edx,%edx - subl %esi,%eax - shrl $(SURF_T_SHIFT),%eax - movl %edx,et_surfs+2(%edi) - movl %eax,et_surfs(%edi) - - movl $1,%esi - -// u = u0 + ((float)v - v0) * u_step; - movl %ebx,Lv - fildl Lv // v | v0 | ustep | u0 - fsubp %st(0),%st(1) // v-v0 | ustep | u0 - fmul %st(1),%st(0) // (v-v0)*ustep | ustep | u0 - faddp %st(0),%st(2) // ustep | u - fxch %st(1) // u | ustep - -// } - -LSideDone: - -// edge->u_step = u_step*0x100000; -// edge->u = u*0x100000 + 0xFFFFF; - - fmuls fp_1m // u*0x100000 | ustep - fxch %st(1) // ustep | u*0x100000 - fmuls fp_1m // ustep*0x100000 | u*0x100000 - fxch %st(1) // u*0x100000 | ustep*0x100000 - fadds fp_1m_minus_1 // u*0x100000 + 0xFFFFF | ustep*0x100000 - fxch %st(1) // ustep*0x100000 | u*0x100000 + 0xFFFFF - fistpl et_u_step(%edi) // u*0x100000 + 0xFFFFF - fistpl et_u(%edi) - -// // we need to do this to avoid stepping off the edges if a very nearly -// // horizontal edge is less than epsilon above a scan, and numeric error -// // causes it to incorrectly extend to the scan, and the extension of the -// // line goes off the edge of the screen -// // FIXME: is this actually needed? -// if (edge->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; - movl et_u(%edi),%eax - movl C(r_refdef)+rd_vrect_x_adj_shift20,%edx - cmpl %edx,%eax - jl LP4 - movl C(r_refdef)+rd_vrectright_adj_shift20,%edx - cmpl %edx,%eax - jng LP5 -LP4: - movl %edx,et_u(%edi) - movl %edx,%eax -LP5: - -// // sort the edge in normally -// u_check = edge->u; -// -// if (edge->surfs[0]) -// u_check++; // sort trailers after leaders - addl %esi,%eax - -// if (!newedges[v] || newedges[v]->u >= u_check) -// { - movl C(newedges)(,%ebx,4),%esi - testl %esi,%esi - jz LDoFirst - cmpl %eax,et_u(%esi) - jl LNotFirst -LDoFirst: - -// edge->next = newedges[v]; -// newedges[v] = edge; - movl %esi,et_next(%edi) - movl %edi,C(newedges)(,%ebx,4) - - jmp LSetRemove - -// } - -LNotFirst: - -// else -// { -// pcheck = newedges[v]; -// -// while (pcheck->next && pcheck->next->u < u_check) -// pcheck = pcheck->next; -LFindInsertLoop: - movl %esi,%edx - movl et_next(%esi),%esi - testl %esi,%esi - jz LInsertFound - cmpl %eax,et_u(%esi) - jl LFindInsertLoop - -LInsertFound: - -// edge->next = pcheck->next; -// pcheck->next = edge; - movl %esi,et_next(%edi) - movl %edi,et_next(%edx) - -// } - -LSetRemove: - -// edge->nextremove = removeedges[v2]; -// removeedges[v2] = edge; - movl C(removeedges)(,%ecx,4),%eax - movl %edi,C(removeedges)(,%ecx,4) - movl %eax,et_nextremove(%edi) - -Ldone: - movl Lstack,%esp // clear temporary variables from stack - - popl %ebx // restore register variables - popl %edi - popl %esi - ret - -// at least one point is clipped - -Lp2: - testl %eax,%eax - jns Lp1 - -// else -// { -// // point 0 is clipped - -// if (d1 < 0) -// { - movl Ld1,%eax - testl %eax,%eax - jns Lp3 - -// // both points are clipped -// // we do cache fully clipped edges -// if (!leftclipped) - movl C(r_leftclipped),%eax - movl C(r_pedge),%ecx - testl %eax,%eax - jnz Ldone - -// r_pedge->framecount = r_framecount; - movl C(r_framecount),%eax - andl $(FRAMECOUNT_MASK),%eax - orl $(FULLY_CLIPPED_CACHED),%eax - movl %eax,C(cacheoffset) - -// return; - jmp Ldone - -// } - -Lp1: - -// // point 0 is unclipped -// if (d1 >= 0) -// { -// // both points are unclipped -// continue; - -// // only point 1 is clipped - -// f = d0 / (d0 - d1); - flds Ld0 - flds Ld1 - fsubr %st(1),%st(0) - -// // we don't cache partially clipped edges - movl $0x7FFFFFFF,C(cacheoffset) - - fdivrp %st(0),%st(1) - - subl $(mv_size),%esp // allocate space for clipvert - -// 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]); - flds mv_position+8(%edx) - fsubs mv_position+8(%esi) - flds mv_position+4(%edx) - fsubs mv_position+4(%esi) - flds mv_position+0(%edx) - fsubs mv_position+0(%esi) // 0 | 1 | 2 - -// replace pv1 with the clip point - movl %esp,%edx - movl cp_leftedge(%ebx),%eax - testb %al,%al - - fmul %st(3),%st(0) - fxch %st(1) // 1 | 0 | 2 - fmul %st(3),%st(0) - fxch %st(2) // 2 | 0 | 1 - fmulp %st(0),%st(3) // 0 | 1 | 2 - fadds mv_position+0(%esi) - fxch %st(1) // 1 | 0 | 2 - fadds mv_position+4(%esi) - fxch %st(2) // 2 | 0 | 1 - fadds mv_position+8(%esi) - fxch %st(1) // 0 | 2 | 1 - fstps mv_position+0(%esp) // 2 | 1 - fstps mv_position+8(%esp) // 1 - fstps mv_position+4(%esp) - -// if (clip->leftedge) -// { - jz Ltestright - -// r_leftclipped = true; -// r_leftexit = clipvert; - movl $1,C(r_leftclipped) - movl mv_position+0(%esp),%eax - movl %eax,C(r_leftexit)+mv_position+0 - movl mv_position+4(%esp),%eax - movl %eax,C(r_leftexit)+mv_position+4 - movl mv_position+8(%esp),%eax - movl %eax,C(r_leftexit)+mv_position+8 - - jmp Lcontinue - -// } - -Ltestright: -// else if (clip->rightedge) -// { - testb %ah,%ah - jz Lcontinue - -// r_rightclipped = true; -// r_rightexit = clipvert; - movl $1,C(r_rightclipped) - movl mv_position+0(%esp),%eax - movl %eax,C(r_rightexit)+mv_position+0 - movl mv_position+4(%esp),%eax - movl %eax,C(r_rightexit)+mv_position+4 - movl mv_position+8(%esp),%eax - movl %eax,C(r_rightexit)+mv_position+8 - -// } -// -// R_ClipEdge (pv0, &clipvert, clip->next); -// return; -// } - jmp Lcontinue - -// } - -Lp3: - -// // only point 0 is clipped -// r_lastvertvalid = false; - - movl $0,C(r_lastvertvalid) - -// f = d0 / (d0 - d1); - flds Ld0 - flds Ld1 - fsubr %st(1),%st(0) - -// // we don't cache partially clipped edges - movl $0x7FFFFFFF,C(cacheoffset) - - fdivrp %st(0),%st(1) - - subl $(mv_size),%esp // allocate space for clipvert - -// 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]); - flds mv_position+8(%edx) - fsubs mv_position+8(%esi) - flds mv_position+4(%edx) - fsubs mv_position+4(%esi) - flds mv_position+0(%edx) - fsubs mv_position+0(%esi) // 0 | 1 | 2 - - movl cp_leftedge(%ebx),%eax - testb %al,%al - - fmul %st(3),%st(0) - fxch %st(1) // 1 | 0 | 2 - fmul %st(3),%st(0) - fxch %st(2) // 2 | 0 | 1 - fmulp %st(0),%st(3) // 0 | 1 | 2 - fadds mv_position+0(%esi) - fxch %st(1) // 1 | 0 | 2 - fadds mv_position+4(%esi) - fxch %st(2) // 2 | 0 | 1 - fadds mv_position+8(%esi) - fxch %st(1) // 0 | 2 | 1 - fstps mv_position+0(%esp) // 2 | 1 - fstps mv_position+8(%esp) // 1 - fstps mv_position+4(%esp) - -// replace pv0 with the clip point - movl %esp,%esi - -// if (clip->leftedge) -// { - jz Ltestright2 - -// r_leftclipped = true; -// r_leftenter = clipvert; - movl $1,C(r_leftclipped) - movl mv_position+0(%esp),%eax - movl %eax,C(r_leftenter)+mv_position+0 - movl mv_position+4(%esp),%eax - movl %eax,C(r_leftenter)+mv_position+4 - movl mv_position+8(%esp),%eax - movl %eax,C(r_leftenter)+mv_position+8 - - jmp Lcontinue - -// } - -Ltestright2: -// else if (clip->rightedge) -// { - testb %ah,%ah - jz Lcontinue - -// r_rightclipped = true; -// r_rightenter = clipvert; - movl $1,C(r_rightclipped) - movl mv_position+0(%esp),%eax - movl %eax,C(r_rightenter)+mv_position+0 - movl mv_position+4(%esp),%eax - movl %eax,C(r_rightenter)+mv_position+4 - movl mv_position+8(%esp),%eax - movl %eax,C(r_rightenter)+mv_position+8 - -// } - jmp Lcontinue - -// %esi = vec3_t point to transform and project -// %edx preserved -LTransformAndProject: - -// // transform and project -// VectorSubtract (world, modelorg, local); - flds mv_position+0(%esi) - fsubs C(modelorg)+0 - flds mv_position+4(%esi) - fsubs C(modelorg)+4 - flds mv_position+8(%esi) - fsubs C(modelorg)+8 - fxch %st(2) // local[0] | local[1] | local[2] - -// TransformVector (local, transformed); -// -// if (transformed[2] < NEAR_CLIP) -// transformed[2] = NEAR_CLIP; -// -// lzi0 = 1.0 / transformed[2]; - fld %st(0) // local[0] | local[0] | local[1] | local[2] - fmuls C(vpn)+0 // zm0 | local[0] | local[1] | local[2] - fld %st(1) // local[0] | zm0 | local[0] | local[1] | - // local[2] - fmuls C(vright)+0 // xm0 | zm0 | local[0] | local[1] | local[2] - fxch %st(2) // local[0] | zm0 | xm0 | local[1] | local[2] - fmuls C(vup)+0 // ym0 | zm0 | xm0 | local[1] | local[2] - fld %st(3) // local[1] | ym0 | zm0 | xm0 | local[1] | - // local[2] - fmuls C(vpn)+4 // zm1 | ym0 | zm0 | xm0 | local[1] | - // local[2] - fld %st(4) // local[1] | zm1 | ym0 | zm0 | xm0 | - // local[1] | local[2] - fmuls C(vright)+4 // xm1 | zm1 | ym0 | zm0 | xm0 | - // local[1] | local[2] - fxch %st(5) // local[1] | zm1 | ym0 | zm0 | xm0 | - // xm1 | local[2] - fmuls C(vup)+4 // ym1 | zm1 | ym0 | zm0 | xm0 | - // xm1 | local[2] - fxch %st(1) // zm1 | ym1 | ym0 | zm0 | xm0 | - // xm1 | local[2] - faddp %st(0),%st(3) // ym1 | ym0 | zm2 | xm0 | xm1 | local[2] - fxch %st(3) // xm0 | ym0 | zm2 | ym1 | xm1 | local[2] - faddp %st(0),%st(4) // ym0 | zm2 | ym1 | xm2 | local[2] - faddp %st(0),%st(2) // zm2 | ym2 | xm2 | local[2] - fld %st(3) // local[2] | zm2 | ym2 | xm2 | local[2] - fmuls C(vpn)+8 // zm3 | zm2 | ym2 | xm2 | local[2] - fld %st(4) // local[2] | zm3 | zm2 | ym2 | xm2 | local[2] - fmuls C(vright)+8 // xm3 | zm3 | zm2 | ym2 | xm2 | local[2] - fxch %st(5) // local[2] | zm3 | zm2 | ym2 | xm2 | xm3 - fmuls C(vup)+8 // ym3 | zm3 | zm2 | ym2 | xm2 | xm3 - fxch %st(1) // zm3 | ym3 | zm2 | ym2 | xm2 | xm3 - faddp %st(0),%st(2) // ym3 | zm4 | ym2 | xm2 | xm3 - fxch %st(4) // xm3 | zm4 | ym2 | xm2 | ym3 - faddp %st(0),%st(3) // zm4 | ym2 | xm4 | ym3 - fxch %st(1) // ym2 | zm4 | xm4 | ym3 - faddp %st(0),%st(3) // zm4 | xm4 | ym4 - - fcoms Lfp_near_clip - fnstsw %ax - testb $1,%ah - jz LNoClip - fstp %st(0) - flds Lfp_near_clip - -LNoClip: - - fdivrs float_1 // lzi0 | x | y - fxch %st(1) // x | lzi0 | y - -// // FIXME: build x/yscale into transform? -// scale = xscale * lzi0; -// u0 = (xcenter + scale*transformed[0]); - flds C(xscale) // xscale | x | lzi0 | y - fmul %st(2),%st(0) // scale | x | lzi0 | y - fmulp %st(0),%st(1) // scale*x | lzi0 | y - fadds C(xcenter) // u0 | lzi0 | y - -// if (u0 < r_refdef.fvrectx_adj) -// u0 = r_refdef.fvrectx_adj; -// if (u0 > r_refdef.fvrectright_adj) -// u0 = r_refdef.fvrectright_adj; -// FIXME: use integer compares of floats? - fcoms C(r_refdef)+rd_fvrectx_adj - fnstsw %ax - testb $1,%ah - jz LClampP0 - fstp %st(0) - flds C(r_refdef)+rd_fvrectx_adj -LClampP0: - fcoms C(r_refdef)+rd_fvrectright_adj - fnstsw %ax - testb $0x45,%ah - jnz LClampP1 - fstp %st(0) - flds C(r_refdef)+rd_fvrectright_adj -LClampP1: - - fld %st(1) // lzi0 | u0 | lzi0 | y - -// scale = yscale * lzi0; -// v0 = (ycenter - scale*transformed[1]); - fmuls C(yscale) // scale | u0 | lzi0 | y - fmulp %st(0),%st(3) // u0 | lzi0 | scale*y - fxch %st(2) // scale*y | lzi0 | u0 - fsubrs C(ycenter) // v0 | lzi0 | u0 - -// if (v0 < r_refdef.fvrecty_adj) -// v0 = r_refdef.fvrecty_adj; -// if (v0 > r_refdef.fvrectbottom_adj) -// v0 = r_refdef.fvrectbottom_adj; -// FIXME: use integer compares of floats? - fcoms C(r_refdef)+rd_fvrecty_adj - fnstsw %ax - testb $1,%ah - jz LClampP2 - fstp %st(0) - flds C(r_refdef)+rd_fvrecty_adj -LClampP2: - fcoms C(r_refdef)+rd_fvrectbottom_adj - fnstsw %ax - testb $0x45,%ah - jnz LClampP3 - fstp %st(0) - flds C(r_refdef)+rd_fvrectbottom_adj -LClampP3: - ret - -#endif // id386 - +/* + r_drawa.S + + (description) + + Copyright (C) 1996-1997 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id: r_drawa.s,v 1.1.1.4 2004/10/18 17:44:37 vvd0 Exp $ +*/ +// r_drawa.s +// x86 assembly-language edge clipping and emission code + +#include "asm_i386.h" +#include "quakeasm.h" +#include "asm_draw.h" +#include "d_ifacea.h" + +#ifdef id386 + +// !!! if these are changed, they must be changed in r_draw.c too !!! +#define FULLY_CLIPPED_CACHED 0x80000000 +#define FRAMECOUNT_MASK 0x7FFFFFFF + + .data + +Ld0: .single 0.0 +Ld1: .single 0.0 +Lstack: .long 0 +Lfp_near_clip: .single NEAR_CLIP +Lceilv0: .long 0 +Lv: .long 0 +Lu0: .long 0 +Lv0: .long 0 +Lzi0: .long 0 + + .text + +//---------------------------------------------------------------------- +// edge clipping code +//---------------------------------------------------------------------- + +#define pv0 4+12 +#define pv1 8+12 +#define clip 12+12 + + .align 4 +.globl C(R_ClipEdge) +C(R_ClipEdge): + pushl %esi // preserve register variables + pushl %edi + pushl %ebx + movl %esp,Lstack // for clearing the stack later + +// float d0, d1, f; +// mvertex_t clipvert; + + movl clip(%esp),%ebx + movl pv0(%esp),%esi + movl pv1(%esp),%edx + +// if (clip) +// { + testl %ebx,%ebx + jz Lemit + +// do +// { + +Lcliploop: + +// d0 = DotProduct (pv0->position, clip->normal) - clip->dist; +// d1 = DotProduct (pv1->position, clip->normal) - clip->dist; + flds mv_position+0(%esi) + fmuls cp_normal+0(%ebx) + flds mv_position+4(%esi) + fmuls cp_normal+4(%ebx) + flds mv_position+8(%esi) + fmuls cp_normal+8(%ebx) + fxch %st(1) + faddp %st(0),%st(2) // d0mul2 | d0add0 + + flds mv_position+0(%edx) + fmuls cp_normal+0(%ebx) + flds mv_position+4(%edx) + fmuls cp_normal+4(%ebx) + flds mv_position+8(%edx) + fmuls cp_normal+8(%ebx) + fxch %st(1) + faddp %st(0),%st(2) // d1mul2 | d1add0 | d0mul2 | d0add0 + fxch %st(3) // d0add0 | d1add0 | d0mul2 | d1mul2 + + faddp %st(0),%st(2) // d1add0 | dot0 | d1mul2 + faddp %st(0),%st(2) // dot0 | dot1 + + fsubs cp_dist(%ebx) // d0 | dot1 + fxch %st(1) // dot1 | d0 + fsubs cp_dist(%ebx) // d1 | d0 + fxch %st(1) + fstps Ld0 + fstps Ld1 + +// if (d0 >= 0) +// { + movl Ld0,%eax + movl Ld1,%ecx + orl %eax,%ecx + js Lp2 + +// both points are unclipped + +Lcontinue: + +// +// R_ClipEdge (&clipvert, pv1, clip->next); +// return; +// } +// } while ((clip = clip->next) != NULL); + movl cp_next(%ebx),%ebx + testl %ebx,%ebx + jnz Lcliploop + +// } + +//// add the edge +// R_EmitEdge (pv0, pv1); +Lemit: + +// +// set integer rounding to ceil mode, set to single precision +// +// FIXME: do away with by manually extracting integers from floats? +// FIXME: set less often + fldcw ceil_cw + +// 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) +// { + cmpl $0,C(r_lastvertvalid) + jz LCalcFirst + +// u0 = r_u1; +// v0 = r_v1; +// lzi0 = r_lzi1; +// ceilv0 = r_ceilv1; + movl C(r_lzi1),%eax + movl C(r_u1),%ecx + movl %eax,Lzi0 + movl %ecx,Lu0 + movl C(r_v1),%ecx + movl C(r_ceilv1),%eax + movl %ecx,Lv0 + movl %eax,Lceilv0 + jmp LCalcSecond + +// } + +LCalcFirst: + +// else +// { +// world = &pv0->position[0]; + + call LTransformAndProject // v0 | lzi0 | u0 + + fsts Lv0 + fxch %st(2) // u0 | lzi0 | v0 + fstps Lu0 // lzi0 | v0 + fstps Lzi0 // v0 + +// ceilv0 = (int)(v0 - 2000) + 2000; // ceil(v0); + fistpl Lceilv0 + +// } + +LCalcSecond: + +// world = &pv1->position[0]; + movl %edx,%esi + + call LTransformAndProject // v1 | lzi1 | u1 + + flds Lu0 // u0 | v1 | lzi1 | u1 + fxch %st(3) // u1 | v1 | lzi1 | u0 + flds Lzi0 // lzi0 | u1 | v1 | lzi1 | u0 + fxch %st(3) // lzi1 | u1 | v1 | lzi0 | u0 + flds Lv0 // v0 | lzi1 | u1 | v1 | lzi0 | u0 + fxch %st(3) // v1 | lzi1 | u1 | v0 | lzi0 | u0 + +// r_ceilv1 = (int)(r_v1 - 2000) + 2000; // ceil(r_v1); + fistl C(r_ceilv1) + + fldcw single_cw // put back normal floating-point state + + fsts C(r_v1) + fxch %st(4) // lzi0 | lzi1 | u1 | v0 | v1 | u0 + +// if (r_lzi1 > lzi0) +// lzi0 = r_lzi1; + fcom %st(1) + fnstsw %ax + testb $1,%ah + jz LP0 + fstp %st(0) + fld %st(0) +LP0: + + fxch %st(1) // lzi1 | lzi0 | u1 | v0 | v1 | u0 + fstps C(r_lzi1) // lzi0 | u1 | v0 | v1 | u0 + fxch %st(1) + fsts C(r_u1) + fxch %st(1) + +// if (lzi0 > r_nearzi) // for mipmap finding +// r_nearzi = lzi0; + fcoms C(r_nearzi) + fnstsw %ax + testb $0x45,%ah + jnz LP1 + fsts C(r_nearzi) +LP1: + +// // for right edges, all we want is the effect on 1/z +// if (r_nearzionly) +// return; + movl C(r_nearzionly),%eax + testl %eax,%eax + jz LP2 +LPop5AndDone: + movl C(cacheoffset),%eax + movl C(r_framecount),%edx + cmpl $0x7FFFFFFF,%eax + jz LDoPop + andl $(FRAMECOUNT_MASK),%edx + orl $(FULLY_CLIPPED_CACHED),%edx + movl %edx,C(cacheoffset) + +LDoPop: + fstp %st(0) // u1 | v0 | v1 | u0 + fstp %st(0) // v0 | v1 | u0 + fstp %st(0) // v1 | u0 + fstp %st(0) // u0 + fstp %st(0) + jmp Ldone + +LP2: + +// // create the edge +// if (ceilv0 == r_ceilv1) +// return; // horizontal edge + movl Lceilv0,%ebx + movl C(edge_p),%edi + movl C(r_ceilv1),%ecx + movl %edi,%edx + movl C(r_pedge),%esi + addl $(et_size),%edx + cmpl %ecx,%ebx + jz LPop5AndDone + + movl C(r_pedge),%eax + movl %eax,et_owner(%edi) + +// side = ceilv0 > r_ceilv1; +// +// edge->nearzi = lzi0; + fstps et_nearzi(%edi) // u1 | v0 | v1 | u0 + +// if (side == 1) +// { + jc LSide0 + +LSide1: + +// // leading edge (go from p2 to p1) + +// u_step = ((u0 - r_u1) / (v0 - r_v1)); + fsubrp %st(0),%st(3) // v0 | v1 | u0-u1 + fsub %st(1),%st(0) // v0-v1 | v1 | u0-u1 + fdivrp %st(0),%st(2) // v1 | ustep + +// r_emitted = 1; + movl $1,C(r_emitted) + +// edge = edge_p++; + movl %edx,C(edge_p) + +// pretouch next edge + movl (%edx),%eax + +// v2 = ceilv0 - 1; +// v = r_ceilv1; + movl %ecx,%eax + leal -1(%ebx),%ecx + movl %eax,%ebx + +// edge->surfs[0] = 0; +// edge->surfs[1] = surface_p - surfaces; + movl C(surface_p),%eax + movl C(surfaces),%esi + subl %edx,%edx + subl %esi,%eax + shrl $(SURF_T_SHIFT),%eax + movl %edx,et_surfs(%edi) + movl %eax,et_surfs+2(%edi) + + subl %esi,%esi + +// u = r_u1 + ((float)v - r_v1) * u_step; + movl %ebx,Lv + fildl Lv // v | v1 | ustep + fsubp %st(0),%st(1) // v-v1 | ustep + fmul %st(1),%st(0) // (v-v1)*ustep | ustep + fadds C(r_u1) // u | ustep + + jmp LSideDone + +// } + +LSide0: + +// else +// { +// // trailing edge (go from p1 to p2) + +// u_step = ((r_u1 - u0) / (r_v1 - v0)); + fsub %st(3),%st(0) // u1-u0 | v0 | v1 | u0 + fxch %st(2) // v1 | v0 | u1-u0 | u0 + fsub %st(1),%st(0) // v1-v0 | v0 | u1-u0 | u0 + fdivrp %st(0),%st(2) // v0 | ustep | u0 + +// r_emitted = 1; + movl $1,C(r_emitted) + +// edge = edge_p++; + movl %edx,C(edge_p) + +// pretouch next edge + movl (%edx),%eax + +// v = ceilv0; +// v2 = r_ceilv1 - 1; + decl %ecx + +// edge->surfs[0] = surface_p - surfaces; +// edge->surfs[1] = 0; + movl C(surface_p),%eax + movl C(surfaces),%esi + subl %edx,%edx + subl %esi,%eax + shrl $(SURF_T_SHIFT),%eax + movl %edx,et_surfs+2(%edi) + movl %eax,et_surfs(%edi) + + movl $1,%esi + +// u = u0 + ((float)v - v0) * u_step; + movl %ebx,Lv + fildl Lv // v | v0 | ustep | u0 + fsubp %st(0),%st(1) // v-v0 | ustep | u0 + fmul %st(1),%st(0) // (v-v0)*ustep | ustep | u0 + faddp %st(0),%st(2) // ustep | u + fxch %st(1) // u | ustep + +// } + +LSideDone: + +// edge->u_step = u_step*0x100000; +// edge->u = u*0x100000 + 0xFFFFF; + + fmuls fp_1m // u*0x100000 | ustep + fxch %st(1) // ustep | u*0x100000 + fmuls fp_1m // ustep*0x100000 | u*0x100000 + fxch %st(1) // u*0x100000 | ustep*0x100000 + fadds fp_1m_minus_1 // u*0x100000 + 0xFFFFF | ustep*0x100000 + fxch %st(1) // ustep*0x100000 | u*0x100000 + 0xFFFFF + fistpl et_u_step(%edi) // u*0x100000 + 0xFFFFF + fistpl et_u(%edi) + +// // we need to do this to avoid stepping off the edges if a very nearly +// // horizontal edge is less than epsilon above a scan, and numeric error +// // causes it to incorrectly extend to the scan, and the extension of the +// // line goes off the edge of the screen +// // FIXME: is this actually needed? +// if (edge->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; + movl et_u(%edi),%eax + movl C(r_refdef)+rd_vrect_x_adj_shift20,%edx + cmpl %edx,%eax + jl LP4 + movl C(r_refdef)+rd_vrectright_adj_shift20,%edx + cmpl %edx,%eax + jng LP5 +LP4: + movl %edx,et_u(%edi) + movl %edx,%eax +LP5: + +// // sort the edge in normally +// u_check = edge->u; +// +// if (edge->surfs[0]) +// u_check++; // sort trailers after leaders + addl %esi,%eax + +// if (!newedges[v] || newedges[v]->u >= u_check) +// { + movl C(newedges)(,%ebx,4),%esi + testl %esi,%esi + jz LDoFirst + cmpl %eax,et_u(%esi) + jl LNotFirst +LDoFirst: + +// edge->next = newedges[v]; +// newedges[v] = edge; + movl %esi,et_next(%edi) + movl %edi,C(newedges)(,%ebx,4) + + jmp LSetRemove + +// } + +LNotFirst: + +// else +// { +// pcheck = newedges[v]; +// +// while (pcheck->next && pcheck->next->u < u_check) +// pcheck = pcheck->next; +LFindInsertLoop: + movl %esi,%edx + movl et_next(%esi),%esi + testl %esi,%esi + jz LInsertFound + cmpl %eax,et_u(%esi) + jl LFindInsertLoop + +LInsertFound: + +// edge->next = pcheck->next; +// pcheck->next = edge; + movl %esi,et_next(%edi) + movl %edi,et_next(%edx) + +// } + +LSetRemove: + +// edge->nextremove = removeedges[v2]; +// removeedges[v2] = edge; + movl C(removeedges)(,%ecx,4),%eax + movl %edi,C(removeedges)(,%ecx,4) + movl %eax,et_nextremove(%edi) + +Ldone: + movl Lstack,%esp // clear temporary variables from stack + + popl %ebx // restore register variables + popl %edi + popl %esi + ret + +// at least one point is clipped + +Lp2: + testl %eax,%eax + jns Lp1 + +// else +// { +// // point 0 is clipped + +// if (d1 < 0) +// { + movl Ld1,%eax + testl %eax,%eax + jns Lp3 + +// // both points are clipped +// // we do cache fully clipped edges +// if (!leftclipped) + movl C(r_leftclipped),%eax + movl C(r_pedge),%ecx + testl %eax,%eax + jnz Ldone + +// r_pedge->framecount = r_framecount; + movl C(r_framecount),%eax + andl $(FRAMECOUNT_MASK),%eax + orl $(FULLY_CLIPPED_CACHED),%eax + movl %eax,C(cacheoffset) + +// return; + jmp Ldone + +// } + +Lp1: + +// // point 0 is unclipped +// if (d1 >= 0) +// { +// // both points are unclipped +// continue; + +// // only point 1 is clipped + +// f = d0 / (d0 - d1); + flds Ld0 + flds Ld1 + fsubr %st(1),%st(0) + +// // we don't cache partially clipped edges + movl $0x7FFFFFFF,C(cacheoffset) + + fdivrp %st(0),%st(1) + + subl $(mv_size),%esp // allocate space for clipvert + +// 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]); + flds mv_position+8(%edx) + fsubs mv_position+8(%esi) + flds mv_position+4(%edx) + fsubs mv_position+4(%esi) + flds mv_position+0(%edx) + fsubs mv_position+0(%esi) // 0 | 1 | 2 + +// replace pv1 with the clip point + movl %esp,%edx + movl cp_leftedge(%ebx),%eax + testb %al,%al + + fmul %st(3),%st(0) + fxch %st(1) // 1 | 0 | 2 + fmul %st(3),%st(0) + fxch %st(2) // 2 | 0 | 1 + fmulp %st(0),%st(3) // 0 | 1 | 2 + fadds mv_position+0(%esi) + fxch %st(1) // 1 | 0 | 2 + fadds mv_position+4(%esi) + fxch %st(2) // 2 | 0 | 1 + fadds mv_position+8(%esi) + fxch %st(1) // 0 | 2 | 1 + fstps mv_position+0(%esp) // 2 | 1 + fstps mv_position+8(%esp) // 1 + fstps mv_position+4(%esp) + +// if (clip->leftedge) +// { + jz Ltestright + +// r_leftclipped = true; +// r_leftexit = clipvert; + movl $1,C(r_leftclipped) + movl mv_position+0(%esp),%eax + movl %eax,C(r_leftexit)+mv_position+0 + movl mv_position+4(%esp),%eax + movl %eax,C(r_leftexit)+mv_position+4 + movl mv_position+8(%esp),%eax + movl %eax,C(r_leftexit)+mv_position+8 + + jmp Lcontinue + +// } + +Ltestright: +// else if (clip->rightedge) +// { + testb %ah,%ah + jz Lcontinue + +// r_rightclipped = true; +// r_rightexit = clipvert; + movl $1,C(r_rightclipped) + movl mv_position+0(%esp),%eax + movl %eax,C(r_rightexit)+mv_position+0 + movl mv_position+4(%esp),%eax + movl %eax,C(r_rightexit)+mv_position+4 + movl mv_position+8(%esp),%eax + movl %eax,C(r_rightexit)+mv_position+8 + +// } +// +// R_ClipEdge (pv0, &clipvert, clip->next); +// return; +// } + jmp Lcontinue + +// } + +Lp3: + +// // only point 0 is clipped +// r_lastvertvalid = false; + + movl $0,C(r_lastvertvalid) + +// f = d0 / (d0 - d1); + flds Ld0 + flds Ld1 + fsubr %st(1),%st(0) + +// // we don't cache partially clipped edges + movl $0x7FFFFFFF,C(cacheoffset) + + fdivrp %st(0),%st(1) + + subl $(mv_size),%esp // allocate space for clipvert + +// 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]); + flds mv_position+8(%edx) + fsubs mv_position+8(%esi) + flds mv_position+4(%edx) + fsubs mv_position+4(%esi) + flds mv_position+0(%edx) + fsubs mv_position+0(%esi) // 0 | 1 | 2 + + movl cp_leftedge(%ebx),%eax + testb %al,%al + + fmul %st(3),%st(0) + fxch %st(1) // 1 | 0 | 2 + fmul %st(3),%st(0) + fxch %st(2) // 2 | 0 | 1 + fmulp %st(0),%st(3) // 0 | 1 | 2 + fadds mv_position+0(%esi) + fxch %st(1) // 1 | 0 | 2 + fadds mv_position+4(%esi) + fxch %st(2) // 2 | 0 | 1 + fadds mv_position+8(%esi) + fxch %st(1) // 0 | 2 | 1 + fstps mv_position+0(%esp) // 2 | 1 + fstps mv_position+8(%esp) // 1 + fstps mv_position+4(%esp) + +// replace pv0 with the clip point + movl %esp,%esi + +// if (clip->leftedge) +// { + jz Ltestright2 + +// r_leftclipped = true; +// r_leftenter = clipvert; + movl $1,C(r_leftclipped) + movl mv_position+0(%esp),%eax + movl %eax,C(r_leftenter)+mv_position+0 + movl mv_position+4(%esp),%eax + movl %eax,C(r_leftenter)+mv_position+4 + movl mv_position+8(%esp),%eax + movl %eax,C(r_leftenter)+mv_position+8 + + jmp Lcontinue + +// } + +Ltestright2: +// else if (clip->rightedge) +// { + testb %ah,%ah + jz Lcontinue + +// r_rightclipped = true; +// r_rightenter = clipvert; + movl $1,C(r_rightclipped) + movl mv_position+0(%esp),%eax + movl %eax,C(r_rightenter)+mv_position+0 + movl mv_position+4(%esp),%eax + movl %eax,C(r_rightenter)+mv_position+4 + movl mv_position+8(%esp),%eax + movl %eax,C(r_rightenter)+mv_position+8 + +// } + jmp Lcontinue + +// %esi = vec3_t point to transform and project +// %edx preserved +LTransformAndProject: + +// // transform and project +// VectorSubtract (world, modelorg, local); + flds mv_position+0(%esi) + fsubs C(modelorg)+0 + flds mv_position+4(%esi) + fsubs C(modelorg)+4 + flds mv_position+8(%esi) + fsubs C(modelorg)+8 + fxch %st(2) // local[0] | local[1] | local[2] + +// TransformVector (local, transformed); +// +// if (transformed[2] < NEAR_CLIP) +// transformed[2] = NEAR_CLIP; +// +// lzi0 = 1.0 / transformed[2]; + fld %st(0) // local[0] | local[0] | local[1] | local[2] + fmuls C(vpn)+0 // zm0 | local[0] | local[1] | local[2] + fld %st(1) // local[0] | zm0 | local[0] | local[1] | + // local[2] + fmuls C(vright)+0 // xm0 | zm0 | local[0] | local[1] | local[2] + fxch %st(2) // local[0] | zm0 | xm0 | local[1] | local[2] + fmuls C(vup)+0 // ym0 | zm0 | xm0 | local[1] | local[2] + fld %st(3) // local[1] | ym0 | zm0 | xm0 | local[1] | + // local[2] + fmuls C(vpn)+4 // zm1 | ym0 | zm0 | xm0 | local[1] | + // local[2] + fld %st(4) // local[1] | zm1 | ym0 | zm0 | xm0 | + // local[1] | local[2] + fmuls C(vright)+4 // xm1 | zm1 | ym0 | zm0 | xm0 | + // local[1] | local[2] + fxch %st(5) // local[1] | zm1 | ym0 | zm0 | xm0 | + // xm1 | local[2] + fmuls C(vup)+4 // ym1 | zm1 | ym0 | zm0 | xm0 | + // xm1 | local[2] + fxch %st(1) // zm1 | ym1 | ym0 | zm0 | xm0 | + // xm1 | local[2] + faddp %st(0),%st(3) // ym1 | ym0 | zm2 | xm0 | xm1 | local[2] + fxch %st(3) // xm0 | ym0 | zm2 | ym1 | xm1 | local[2] + faddp %st(0),%st(4) // ym0 | zm2 | ym1 | xm2 | local[2] + faddp %st(0),%st(2) // zm2 | ym2 | xm2 | local[2] + fld %st(3) // local[2] | zm2 | ym2 | xm2 | local[2] + fmuls C(vpn)+8 // zm3 | zm2 | ym2 | xm2 | local[2] + fld %st(4) // local[2] | zm3 | zm2 | ym2 | xm2 | local[2] + fmuls C(vright)+8 // xm3 | zm3 | zm2 | ym2 | xm2 | local[2] + fxch %st(5) // local[2] | zm3 | zm2 | ym2 | xm2 | xm3 + fmuls C(vup)+8 // ym3 | zm3 | zm2 | ym2 | xm2 | xm3 + fxch %st(1) // zm3 | ym3 | zm2 | ym2 | xm2 | xm3 + faddp %st(0),%st(2) // ym3 | zm4 | ym2 | xm2 | xm3 + fxch %st(4) // xm3 | zm4 | ym2 | xm2 | ym3 + faddp %st(0),%st(3) // zm4 | ym2 | xm4 | ym3 + fxch %st(1) // ym2 | zm4 | xm4 | ym3 + faddp %st(0),%st(3) // zm4 | xm4 | ym4 + + fcoms Lfp_near_clip + fnstsw %ax + testb $1,%ah + jz LNoClip + fstp %st(0) + flds Lfp_near_clip + +LNoClip: + + fdivrs float_1 // lzi0 | x | y + fxch %st(1) // x | lzi0 | y + +// // FIXME: build x/yscale into transform? +// scale = xscale * lzi0; +// u0 = (xcenter + scale*transformed[0]); + flds C(xscale) // xscale | x | lzi0 | y + fmul %st(2),%st(0) // scale | x | lzi0 | y + fmulp %st(0),%st(1) // scale*x | lzi0 | y + fadds C(xcenter) // u0 | lzi0 | y + +// if (u0 < r_refdef.fvrectx_adj) +// u0 = r_refdef.fvrectx_adj; +// if (u0 > r_refdef.fvrectright_adj) +// u0 = r_refdef.fvrectright_adj; +// FIXME: use integer compares of floats? + fcoms C(r_refdef)+rd_fvrectx_adj + fnstsw %ax + testb $1,%ah + jz LClampP0 + fstp %st(0) + flds C(r_refdef)+rd_fvrectx_adj +LClampP0: + fcoms C(r_refdef)+rd_fvrectright_adj + fnstsw %ax + testb $0x45,%ah + jnz LClampP1 + fstp %st(0) + flds C(r_refdef)+rd_fvrectright_adj +LClampP1: + + fld %st(1) // lzi0 | u0 | lzi0 | y + +// scale = yscale * lzi0; +// v0 = (ycenter - scale*transformed[1]); + fmuls C(yscale) // scale | u0 | lzi0 | y + fmulp %st(0),%st(3) // u0 | lzi0 | scale*y + fxch %st(2) // scale*y | lzi0 | u0 + fsubrs C(ycenter) // v0 | lzi0 | u0 + +// if (v0 < r_refdef.fvrecty_adj) +// v0 = r_refdef.fvrecty_adj; +// if (v0 > r_refdef.fvrectbottom_adj) +// v0 = r_refdef.fvrectbottom_adj; +// FIXME: use integer compares of floats? + fcoms C(r_refdef)+rd_fvrecty_adj + fnstsw %ax + testb $1,%ah + jz LClampP2 + fstp %st(0) + flds C(r_refdef)+rd_fvrecty_adj +LClampP2: + fcoms C(r_refdef)+rd_fvrectbottom_adj + fnstsw %ax + testb $0x45,%ah + jnz LClampP3 + fstp %st(0) + flds C(r_refdef)+rd_fvrectbottom_adj +LClampP3: + ret + +#endif // id386 + diff --git a/source/r_edge.c b/source/r_edge.c index 423b9aaa..0ae6e29c 100644 --- a/source/r_edge.c +++ b/source/r_edge.c @@ -1,771 +1,771 @@ -/* -Copyright (C) 1996-1997 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_edge.c - -#include "quakedef.h" -#include "r_local.h" -#include "sound.h" - -#if 0 -// FIXME -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 - -this breaks spans at every edge, even hidden ones (bad) - -have a sentinal at both ends? -#endif - - -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[MAXHEIGHT]; -edge_t *removeedges[MAXHEIGHT]; - -espan_t *span_p, *max_span_p; - -int r_currentkey; - -extern int screenwidth; - -int current_iv; - -int 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; - -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); - - -//============================================================================= - - -/* -============== -R_DrawCulledPolys -============== -*/ -void R_DrawCulledPolys (void) -{ - surf_t *s; - msurface_t *pface; - - currententity = &r_worldentity; - - if (r_worldpolysbacktofront) - { - for (s=surface_p-1 ; s>&surfaces[1] ; s--) - { - if (!s->spans) - continue; - - if (!(s->flags & SURF_DRAWBACKGROUND)) - { - pface = (msurface_t *)s->data; - R_RenderPoly (pface, 15); - } - } - } - else - { - for (s = &surfaces[1] ; sspans) - continue; - - if (!(s->flags & SURF_DRAWBACKGROUND)) - { - pface = (msurface_t *)s->data; - R_RenderPoly (pface, 15); - } - } - } -} - - -/* -============== -R_BeginEdgeFrame -============== -*/ -void R_BeginEdgeFrame (void) -{ - int v; - - 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 (r_draworder.value) - { - pdrawfunc = R_GenerateSpansBackward; - surfaces[1].key = 0; - r_currentkey = 1; - } - else - { - pdrawfunc = R_GenerateSpans; - surfaces[1].key = 0x7FFFFFFF; - r_currentkey = 0; - } - -// FIXME: set with memset - for (v=r_refdef.vrect.y ; vnext; -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); -} - -#endif // !id386 - - -#if !id386 - -/* -============== -R_RemoveEdges -============== -*/ -void R_RemoveEdges (edge_t *pedge) -{ - - do - { - pedge->next->prev = pedge->prev; - pedge->prev->next = pedge->next; - } while ((pedge = pedge->nextremove) != NULL); -} - -#endif // !id386 - - -#if !id386 - -/* -============== -R_StepActiveU -============== -*/ -void R_StepActiveU (edge_t *pedge) -{ - edge_t *pnext_edge, *pwedge; - - while (1) - { -nextedge: - pedge->u += pedge->u_step; - if (pedge->u < pedge->prev->u) - goto pushback; - pedge = pedge->next; - - pedge->u += pedge->u_step; - if (pedge->u < pedge->prev->u) - goto pushback; - pedge = pedge->next; - - pedge->u += pedge->u_step; - if (pedge->u < pedge->prev->u) - goto pushback; - pedge = pedge->next; - - pedge->u += pedge->u_step; - if (pedge->u < pedge->prev->u) - goto pushback; - pedge = pedge->next; - - goto nextedge; - -pushback: - if (pedge == &edge_aftertail) - return; - - // 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->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; - } -} - -#endif // !id386 - - -/* -============== -R_CleanupSpan -============== -*/ -void R_CleanupSpan () -{ - surf_t *surf; - int 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; - int 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 >> 20; - - 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; - int iu; - -// 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->insubmodel) - r_bmodelactive--; - - if (surf == surfaces[1].next) - { - // emit a span (current top going away) - iu = edge->u >> 20; - 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; - } -} - - -#if !id386 - -/* -============== -R_LeadingEdge -============== -*/ -void R_LeadingEdge (edge_t *edge) -{ - espan_t *span; - surf_t *surf, *surf2; - int iu; - double fu, newzi, testzi, newzitop, newzibottom; - - if (edge->surfs[1]) - { - // 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) - { - if (surf->insubmodel) - r_bmodelactive++; - - 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 - 0xFFFFF) * (1.0 / 0x100000); - newzi = surf->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 - 0xFFFFF) * (1.0 / 0x100000); - newzi = surf->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 >> 20; - - 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; - - r_bmodelactive = 0; - -// 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 (); -} - -#endif // !id386 - - -/* -============== -R_GenerateSpansBackward -============== -*/ -void R_GenerateSpansBackward (void) -{ - edge_t *edge; - - r_bmodelactive = 0; - -// 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) -{ - int iv, bottom; - byte basespans[MAXSPANS*sizeof(espan_t)+CACHE_SIZE]; - espan_t *basespan_p; - surf_t *s; - - basespan_p = (espan_t *) - ((long)(basespans + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1)); - max_span_p = &basespan_p[MAXSPANS - r_refdef.vrect.width]; - - 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 << 20; - edge_head_u_shift20 = edge_head.u >> 20; - 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 << 20) + 0xFFFFF; - edge_tail_u_shift20 = edge_tail.u >> 20; - 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 = 2000 << 24; // 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) - { - VID_UnlockBuffer (); - S_ExtraUpdate (); // don't let sound get messed up if going slow - VID_LockBuffer (); - - if (r_drawculledpolys) - R_DrawCulledPolys (); - else - 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 - if (r_drawculledpolys) - R_DrawCulledPolys (); - else - D_DrawSurfaces (); -} - - +/* +Copyright (C) 1996-1997 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_edge.c + +#include "quakedef.h" +#include "r_local.h" +#include "sound.h" + +#if 0 +// FIXME +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 + +this breaks spans at every edge, even hidden ones (bad) + +have a sentinal at both ends? +#endif + + +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[MAXHEIGHT]; +edge_t *removeedges[MAXHEIGHT]; + +espan_t *span_p, *max_span_p; + +int r_currentkey; + +extern int screenwidth; + +int current_iv; + +int 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; + +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); + + +//============================================================================= + + +/* +============== +R_DrawCulledPolys +============== +*/ +void R_DrawCulledPolys (void) +{ + surf_t *s; + msurface_t *pface; + + currententity = &r_worldentity; + + if (r_worldpolysbacktofront) + { + for (s=surface_p-1 ; s>&surfaces[1] ; s--) + { + if (!s->spans) + continue; + + if (!(s->flags & SURF_DRAWBACKGROUND)) + { + pface = (msurface_t *)s->data; + R_RenderPoly (pface, 15); + } + } + } + else + { + for (s = &surfaces[1] ; sspans) + continue; + + if (!(s->flags & SURF_DRAWBACKGROUND)) + { + pface = (msurface_t *)s->data; + R_RenderPoly (pface, 15); + } + } + } +} + + +/* +============== +R_BeginEdgeFrame +============== +*/ +void R_BeginEdgeFrame (void) +{ + int v; + + 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 (r_draworder.value) + { + pdrawfunc = R_GenerateSpansBackward; + surfaces[1].key = 0; + r_currentkey = 1; + } + else + { + pdrawfunc = R_GenerateSpans; + surfaces[1].key = 0x7FFFFFFF; + r_currentkey = 0; + } + +// FIXME: set with memset + for (v=r_refdef.vrect.y ; vnext; +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); +} + +#endif // !id386 + + +#if !id386 + +/* +============== +R_RemoveEdges +============== +*/ +void R_RemoveEdges (edge_t *pedge) +{ + + do + { + pedge->next->prev = pedge->prev; + pedge->prev->next = pedge->next; + } while ((pedge = pedge->nextremove) != NULL); +} + +#endif // !id386 + + +#if !id386 + +/* +============== +R_StepActiveU +============== +*/ +void R_StepActiveU (edge_t *pedge) +{ + edge_t *pnext_edge, *pwedge; + + while (1) + { +nextedge: + pedge->u += pedge->u_step; + if (pedge->u < pedge->prev->u) + goto pushback; + pedge = pedge->next; + + pedge->u += pedge->u_step; + if (pedge->u < pedge->prev->u) + goto pushback; + pedge = pedge->next; + + pedge->u += pedge->u_step; + if (pedge->u < pedge->prev->u) + goto pushback; + pedge = pedge->next; + + pedge->u += pedge->u_step; + if (pedge->u < pedge->prev->u) + goto pushback; + pedge = pedge->next; + + goto nextedge; + +pushback: + if (pedge == &edge_aftertail) + return; + + // 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->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; + } +} + +#endif // !id386 + + +/* +============== +R_CleanupSpan +============== +*/ +void R_CleanupSpan () +{ + surf_t *surf; + int 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; + int 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 >> 20; + + 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; + int iu; + +// 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->insubmodel) + r_bmodelactive--; + + if (surf == surfaces[1].next) + { + // emit a span (current top going away) + iu = edge->u >> 20; + 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; + } +} + + +#if !id386 + +/* +============== +R_LeadingEdge +============== +*/ +void R_LeadingEdge (edge_t *edge) +{ + espan_t *span; + surf_t *surf, *surf2; + int iu; + double fu, newzi, testzi, newzitop, newzibottom; + + if (edge->surfs[1]) + { + // 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) + { + if (surf->insubmodel) + r_bmodelactive++; + + 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 - 0xFFFFF) * (1.0 / 0x100000); + newzi = surf->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 - 0xFFFFF) * (1.0 / 0x100000); + newzi = surf->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 >> 20; + + 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; + + r_bmodelactive = 0; + +// 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 (); +} + +#endif // !id386 + + +/* +============== +R_GenerateSpansBackward +============== +*/ +void R_GenerateSpansBackward (void) +{ + edge_t *edge; + + r_bmodelactive = 0; + +// 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) +{ + int iv, bottom; + byte basespans[MAXSPANS*sizeof(espan_t)+CACHE_SIZE]; + espan_t *basespan_p; + surf_t *s; + + basespan_p = (espan_t *) + ((long)(basespans + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1)); + max_span_p = &basespan_p[MAXSPANS - r_refdef.vrect.width]; + + 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 << 20; + edge_head_u_shift20 = edge_head.u >> 20; + 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 << 20) + 0xFFFFF; + edge_tail_u_shift20 = edge_tail.u >> 20; + 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 = 2000 << 24; // 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) + { + VID_UnlockBuffer (); + S_ExtraUpdate (); // don't let sound get messed up if going slow + VID_LockBuffer (); + + if (r_drawculledpolys) + R_DrawCulledPolys (); + else + 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 + if (r_drawculledpolys) + R_DrawCulledPolys (); + else + D_DrawSurfaces (); +} + + diff --git a/source/r_edgea.s b/source/r_edgea.s index 7689cdad..43045ca5 100644 --- a/source/r_edgea.s +++ b/source/r_edgea.s @@ -1,756 +1,756 @@ -/* - r_edgea.S - - (description) - - Copyright (C) 1996-1997 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: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - - $Id: r_edgea.s,v 1.1.1.3 2004/10/13 18:54:45 vvd0 Exp $ -*/ -// r_edgea.s -// x86 assembly-language edge-processing code. - -#include "asm_i386.h" -#include "quakeasm.h" -#include "asm_draw.h" - -#ifdef id386 - - .data -Ltemp: .long 0 -float_1_div_0100000h: .long 0x35800000 // 1.0/(float)0x100000 -float_point_999: .single 0.999 -float_1_point_001: .single 1.001 - - .text - -//-------------------------------------------------------------------- - -#define edgestoadd 4+8 // note odd stack offsets because of interleaving -#define edgelist 8+12 // with pushes - -.globl C(R_EdgeCodeStart) -C(R_EdgeCodeStart): - -.globl C(R_InsertNewEdges) -C(R_InsertNewEdges): - pushl %edi - pushl %esi // preserve register variables - movl edgestoadd(%esp),%edx - pushl %ebx - movl edgelist(%esp),%ecx - -LDoNextEdge: - movl et_u(%edx),%eax - movl %edx,%edi - -LContinueSearch: - movl et_u(%ecx),%ebx - movl et_next(%ecx),%esi - cmpl %ebx,%eax - jle LAddedge - movl et_u(%esi),%ebx - movl et_next(%esi),%ecx - cmpl %ebx,%eax - jle LAddedge2 - movl et_u(%ecx),%ebx - movl et_next(%ecx),%esi - cmpl %ebx,%eax - jle LAddedge - movl et_u(%esi),%ebx - movl et_next(%esi),%ecx - cmpl %ebx,%eax - jg LContinueSearch - -LAddedge2: - movl et_next(%edx),%edx - movl et_prev(%esi),%ebx - movl %esi,et_next(%edi) - movl %ebx,et_prev(%edi) - movl %edi,et_next(%ebx) - movl %edi,et_prev(%esi) - movl %esi,%ecx - - cmpl $0,%edx - jnz LDoNextEdge - jmp LDone - - .align 4 -LAddedge: - movl et_next(%edx),%edx - movl et_prev(%ecx),%ebx - movl %ecx,et_next(%edi) - movl %ebx,et_prev(%edi) - movl %edi,et_next(%ebx) - movl %edi,et_prev(%ecx) - - cmpl $0,%edx - jnz LDoNextEdge - -LDone: - popl %ebx // restore register variables - popl %esi - popl %edi - - ret - -//-------------------------------------------------------------------- - -#define predge 4+4 - -.globl C(R_RemoveEdges) -C(R_RemoveEdges): - pushl %ebx - movl predge(%esp),%eax - -Lre_loop: - movl et_next(%eax),%ecx - movl et_nextremove(%eax),%ebx - movl et_prev(%eax),%edx - testl %ebx,%ebx - movl %edx,et_prev(%ecx) - jz Lre_done - movl %ecx,et_next(%edx) - - movl et_next(%ebx),%ecx - movl et_prev(%ebx),%edx - movl et_nextremove(%ebx),%eax - movl %edx,et_prev(%ecx) - testl %eax,%eax - movl %ecx,et_next(%edx) - jnz Lre_loop - - popl %ebx - ret - -Lre_done: - movl %ecx,et_next(%edx) - popl %ebx - - ret - -//-------------------------------------------------------------------- - -#define pedgelist 4+4 // note odd stack offset because of interleaving - // with pushes - -.globl C(R_StepActiveU) -C(R_StepActiveU): - pushl %edi - movl pedgelist(%esp),%edx - pushl %esi // preserve register variables - pushl %ebx - - movl et_prev(%edx),%esi - -LNewEdge: - movl et_u(%esi),%edi - -LNextEdge: - movl et_u(%edx),%eax - movl et_u_step(%edx),%ebx - addl %ebx,%eax - movl et_next(%edx),%esi - movl %eax,et_u(%edx) - cmpl %edi,%eax - jl LPushBack - - movl et_u(%esi),%edi - movl et_u_step(%esi),%ebx - addl %ebx,%edi - movl et_next(%esi),%edx - movl %edi,et_u(%esi) - cmpl %eax,%edi - jl LPushBack2 - - movl et_u(%edx),%eax - movl et_u_step(%edx),%ebx - addl %ebx,%eax - movl et_next(%edx),%esi - movl %eax,et_u(%edx) - cmpl %edi,%eax - jl LPushBack - - movl et_u(%esi),%edi - movl et_u_step(%esi),%ebx - addl %ebx,%edi - movl et_next(%esi),%edx - movl %edi,et_u(%esi) - cmpl %eax,%edi - jnl LNextEdge - -LPushBack2: - movl %edx,%ebx - movl %edi,%eax - movl %esi,%edx - movl %ebx,%esi - -LPushBack: -// push it back to keep it sorted - movl et_prev(%edx),%ecx - movl et_next(%edx),%ebx - -// done if the -1 in edge_aftertail triggered this - cmpl $(C(edge_aftertail)),%edx - jz LUDone - -// pull the edge out of the edge list - movl et_prev(%ecx),%edi - movl %ecx,et_prev(%esi) - movl %ebx,et_next(%ecx) - -// find out where the edge goes in the edge list -LPushBackLoop: - movl et_prev(%edi),%ecx - movl et_u(%edi),%ebx - cmpl %ebx,%eax - jnl LPushBackFound - - movl et_prev(%ecx),%edi - movl et_u(%ecx),%ebx - cmpl %ebx,%eax - jl LPushBackLoop - - movl %ecx,%edi - -// put the edge back into the edge list -LPushBackFound: - movl et_next(%edi),%ebx - movl %edi,et_prev(%edx) - movl %ebx,et_next(%edx) - movl %edx,et_next(%edi) - movl %edx,et_prev(%ebx) - - movl %esi,%edx - movl et_prev(%esi),%esi - - cmpl $(C(edge_tail)),%edx - jnz LNewEdge - -LUDone: - popl %ebx // restore register variables - popl %esi - popl %edi - - ret - -//-------------------------------------------------------------------- - -#define surf 4 // note this is loaded before any pushes - - .align 4 -TrailingEdge: - movl st_spanstate(%esi),%eax // check for edge inversion - decl %eax - jnz LInverted - - movl %eax,st_spanstate(%esi) - movl st_insubmodel(%esi),%ecx - movl 0x12345678,%edx // surfaces[1].st_next -LPatch0: - movl C(r_bmodelactive),%eax - subl %ecx,%eax - cmpl %esi,%edx - movl %eax,C(r_bmodelactive) - jnz LNoEmit // surface isn't on top, just remove - -// emit a span (current top going away) - movl et_u(%ebx),%eax - shrl $20,%eax // iu = integral pixel u - movl st_last_u(%esi),%edx - movl st_next(%esi),%ecx - cmpl %edx,%eax - jle LNoEmit2 // iu <= surf->last_u, so nothing to emit - - movl %eax,st_last_u(%ecx) // surf->next->last_u = iu; - subl %edx,%eax - movl %edx,espan_t_u(%ebp) // span->u = surf->last_u; - - movl %eax,espan_t_count(%ebp) // span->count = iu - span->u; - movl C(current_iv),%eax - movl %eax,espan_t_v(%ebp) // span->v = current_iv; - movl st_spans(%esi),%eax - movl %eax,espan_t_pnext(%ebp) // span->pnext = surf->spans; - movl %ebp,st_spans(%esi) // surf->spans = span; - addl $(espan_t_size),%ebp - - movl st_next(%esi),%edx // remove the surface from the surface - movl st_prev(%esi),%esi // stack - - movl %edx,st_next(%esi) - movl %esi,st_prev(%edx) - ret - -LNoEmit2: - movl %eax,st_last_u(%ecx) // surf->next->last_u = iu; - movl st_next(%esi),%edx // remove the surface from the surface - movl st_prev(%esi),%esi // stack - - movl %edx,st_next(%esi) - movl %esi,st_prev(%edx) - ret - -LNoEmit: - movl st_next(%esi),%edx // remove the surface from the surface - movl st_prev(%esi),%esi // stack - - movl %edx,st_next(%esi) - movl %esi,st_prev(%edx) - ret - -LInverted: - movl %eax,st_spanstate(%esi) - ret - -//-------------------------------------------------------------------- - -// trailing edge only -Lgs_trailing: - pushl $Lgs_nextedge - jmp TrailingEdge - - -.globl C(R_GenerateSpans) -C(R_GenerateSpans): - pushl %ebp // preserve caller's stack frame - pushl %edi - pushl %esi // preserve register variables - pushl %ebx - -// clear active surfaces to just the background surface - movl C(surfaces),%eax - movl C(edge_head_u_shift20),%edx - addl $(st_size),%eax -// %ebp = span_p throughout - movl C(span_p),%ebp - - movl $0,C(r_bmodelactive) - - movl %eax,st_next(%eax) - movl %eax,st_prev(%eax) - movl %edx,st_last_u(%eax) - movl C(edge_head)+et_next,%ebx // edge=edge_head.next - -// generate spans - cmpl $(C(edge_tail)),%ebx // done if empty list - jz Lgs_lastspan - -Lgs_edgeloop: - - movl et_surfs(%ebx),%edi - movl C(surfaces),%eax - movl %edi,%esi - andl $0xFFFF0000,%edi - andl $0xFFFF,%esi - jz Lgs_leading // not a trailing edge - -// it has a left surface, so a surface is going away for this span - shll $(SURF_T_SHIFT),%esi - addl %eax,%esi - testl %edi,%edi - jz Lgs_trailing - -// both leading and trailing - call TrailingEdge - movl C(surfaces),%eax - -// --------------------------------------------------------------- -// handle a leading edge -// --------------------------------------------------------------- - -Lgs_leading: - shrl $16-SURF_T_SHIFT,%edi - movl C(surfaces),%eax - addl %eax,%edi - movl 0x12345678,%esi // surf2 = surfaces[1].next; -LPatch2: - movl st_spanstate(%edi),%edx - movl st_insubmodel(%edi),%eax - testl %eax,%eax - jnz Lbmodel_leading - -// handle a leading non-bmodel edge - -// 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) - testl %edx,%edx - jnz Lxl_done - - -// if (surf->key < surf2->key) -// goto newtop; - incl %edx - movl st_key(%edi),%eax - movl %edx,st_spanstate(%edi) - movl st_key(%esi),%ecx - cmpl %ecx,%eax - jl Lnewtop - -// main sorting loop to search through surface stack until insertion point -// found. Always terminates because background surface is sentinel -// do -// { -// surf2 = surf2->next; -// } while (surf->key >= surf2->key); -Lsortloopnb: - movl st_next(%esi),%esi - movl st_key(%esi),%ecx - cmpl %ecx,%eax - jge Lsortloopnb - - jmp LInsertAndExit - - -// handle a leading bmodel edge - .align 4 -Lbmodel_leading: - -// 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) - testl %edx,%edx - jnz Lxl_done - - movl C(r_bmodelactive),%ecx - incl %edx - incl %ecx - movl %edx,st_spanstate(%edi) - movl %ecx,C(r_bmodelactive) - -// if (surf->key < surf2->key) -// goto newtop; - movl st_key(%edi),%eax - movl st_key(%esi),%ecx - cmpl %ecx,%eax - jl Lnewtop - -// if ((surf->key == surf2->key) && surf->insubmodel) -// { - jz Lzcheck_for_newtop - -// main sorting loop to search through surface stack until insertion point -// found. Always terminates because background surface is sentinel -// do -// { -// surf2 = surf2->next; -// } while (surf->key > surf2->key); -Lsortloop: - movl st_next(%esi),%esi - movl st_key(%esi),%ecx - cmpl %ecx,%eax - jg Lsortloop - - jne LInsertAndExit - -// Do 1/z sorting to see if we've arrived in the right position - movl et_u(%ebx),%eax - subl $0xFFFFF,%eax - movl %eax,Ltemp - fildl Ltemp - - fmuls float_1_div_0100000h // fu = (float)(edge->u - 0xFFFFF) * - // (1.0 / 0x100000); - - fld %st(0) // fu | fu - fmuls st_d_zistepu(%edi) // fu*surf->d_zistepu | fu - flds C(fv) // fv | fu*surf->d_zistepu | fu - fmuls st_d_zistepv(%edi) // fv*surf->d_zistepv | fu*surf->d_zistepu | fu - fxch %st(1) // fu*surf->d_zistepu | fv*surf->d_zistepv | fu - fadds st_d_ziorigin(%edi) // fu*surf->d_zistepu + surf->d_ziorigin | - // fv*surf->d_zistepv | fu - - flds st_d_zistepu(%esi) // surf2->d_zistepu | - // fu*surf->d_zistepu + surf->d_ziorigin | - // fv*surf->d_zistepv | fu - fmul %st(3),%st(0) // fu*surf2->d_zistepu | - // fu*surf->d_zistepu + surf->d_ziorigin | - // fv*surf->d_zistepv | fu - fxch %st(1) // fu*surf->d_zistepu + surf->d_ziorigin | - // fu*surf2->d_zistepu | - // fv*surf->d_zistepv | fu - faddp %st(0),%st(2) // fu*surf2->d_zistepu | newzi | fu - - flds C(fv) // fv | fu*surf2->d_zistepu | newzi | fu - fmuls st_d_zistepv(%esi) // fv*surf2->d_zistepv | - // fu*surf2->d_zistepu | newzi | fu - fld %st(2) // newzi | fv*surf2->d_zistepv | - // fu*surf2->d_zistepu | newzi | fu - fmuls float_point_999 // newzibottom | fv*surf2->d_zistepv | - // fu*surf2->d_zistepu | newzi | fu - - fxch %st(2) // fu*surf2->d_zistepu | fv*surf2->d_zistepv | - // newzibottom | newzi | fu - fadds st_d_ziorigin(%esi) // fu*surf2->d_zistepu + surf2->d_ziorigin | - // fv*surf2->d_zistepv | newzibottom | newzi | - // fu - faddp %st(0),%st(1) // testzi | newzibottom | newzi | fu - fxch %st(1) // newzibottom | testzi | newzi | fu - -// if (newzibottom >= testzi) -// goto Lgotposition; - - fcomp %st(1) // testzi | newzi | fu - - fxch %st(1) // newzi | testzi | fu - fmuls float_1_point_001 // newzitop | testzi | fu - fxch %st(1) // testzi | newzitop | fu - - fnstsw %ax - testb $0x01,%ah - jz Lgotposition_fpop3 - -// if (newzitop >= testzi) -// { - - fcomp %st(1) // newzitop | fu - fnstsw %ax - testb $0x45,%ah - jz Lsortloop_fpop2 - -// if (surf->d_zistepu >= surf2->d_zistepu) -// goto newtop; - - flds st_d_zistepu(%edi) // surf->d_zistepu | newzitop| fu - fcomps st_d_zistepu(%esi) // newzitop | fu - fnstsw %ax - testb $0x01,%ah - jz Lgotposition_fpop2 - - fstp %st(0) // clear the FPstack - fstp %st(0) - movl st_key(%edi),%eax - jmp Lsortloop - - -Lgotposition_fpop3: - fstp %st(0) -Lgotposition_fpop2: - fstp %st(0) - fstp %st(0) - jmp LInsertAndExit - - -// emit a span (obscures current top) - -Lnewtop_fpop3: - fstp %st(0) -Lnewtop_fpop2: - fstp %st(0) - fstp %st(0) - movl st_key(%edi),%eax // reload the sorting key - -Lnewtop: - movl et_u(%ebx),%eax - movl st_last_u(%esi),%edx - shrl $20,%eax // iu = integral pixel u - movl %eax,st_last_u(%edi) // surf->last_u = iu; - cmpl %edx,%eax - jle LInsertAndExit // iu <= surf->last_u, so nothing to emit - - subl %edx,%eax - movl %edx,espan_t_u(%ebp) // span->u = surf->last_u; - - movl %eax,espan_t_count(%ebp) // span->count = iu - span->u; - movl C(current_iv),%eax - movl %eax,espan_t_v(%ebp) // span->v = current_iv; - movl st_spans(%esi),%eax - movl %eax,espan_t_pnext(%ebp) // span->pnext = surf->spans; - movl %ebp,st_spans(%esi) // surf->spans = span; - addl $(espan_t_size),%ebp - -LInsertAndExit: -// insert before surf2 - movl %esi,st_next(%edi) // surf->next = surf2; - movl st_prev(%esi),%eax - movl %eax,st_prev(%edi) // surf->prev = surf2->prev; - movl %edi,st_prev(%esi) // surf2->prev = surf; - movl %edi,st_next(%eax) // surf2->prev->next = surf; - -// --------------------------------------------------------------- -// leading edge done -// --------------------------------------------------------------- - -// --------------------------------------------------------------- -// see if there are any more edges -// --------------------------------------------------------------- - -Lgs_nextedge: - movl et_next(%ebx),%ebx - cmpl $(C(edge_tail)),%ebx - jnz Lgs_edgeloop - -// clean up at the right edge -Lgs_lastspan: - -// 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 - movl 0x12345678,%esi // surfaces[1].st_next -LPatch3: - movl C(edge_tail_u_shift20),%eax - xorl %ecx,%ecx - movl st_last_u(%esi),%edx - subl %edx,%eax - jle Lgs_resetspanstate - - movl %edx,espan_t_u(%ebp) - movl %eax,espan_t_count(%ebp) - movl C(current_iv),%eax - movl %eax,espan_t_v(%ebp) - movl st_spans(%esi),%eax - movl %eax,espan_t_pnext(%ebp) - movl %ebp,st_spans(%esi) - addl $(espan_t_size),%ebp - -// reset spanstate for all surfaces in the surface stack -Lgs_resetspanstate: - movl %ecx,st_spanstate(%esi) - movl st_next(%esi),%esi - cmpl $0x12345678,%esi // &surfaces[1] -LPatch4: - jnz Lgs_resetspanstate - -// store the final span_p - movl %ebp,C(span_p) - - popl %ebx // restore register variables - popl %esi - popl %edi - popl %ebp // restore the caller's stack frame - ret - - -// --------------------------------------------------------------- -// 1/z sorting for bmodels in the same leaf -// --------------------------------------------------------------- - .align 4 -Lxl_done: - incl %edx - movl %edx,st_spanstate(%edi) - - jmp Lgs_nextedge - - - .align 4 -Lzcheck_for_newtop: - movl et_u(%ebx),%eax - subl $0xFFFFF,%eax - movl %eax,Ltemp - fildl Ltemp - - fmuls float_1_div_0100000h // fu = (float)(edge->u - 0xFFFFF) * - // (1.0 / 0x100000); - - fld %st(0) // fu | fu - fmuls st_d_zistepu(%edi) // fu*surf->d_zistepu | fu - flds C(fv) // fv | fu*surf->d_zistepu | fu - fmuls st_d_zistepv(%edi) // fv*surf->d_zistepv | fu*surf->d_zistepu | fu - fxch %st(1) // fu*surf->d_zistepu | fv*surf->d_zistepv | fu - fadds st_d_ziorigin(%edi) // fu*surf->d_zistepu + surf->d_ziorigin | - // fv*surf->d_zistepv | fu - - flds st_d_zistepu(%esi) // surf2->d_zistepu | - // fu*surf->d_zistepu + surf->d_ziorigin | - // fv*surf->d_zistepv | fu - fmul %st(3),%st(0) // fu*surf2->d_zistepu | - // fu*surf->d_zistepu + surf->d_ziorigin | - // fv*surf->d_zistepv | fu - fxch %st(1) // fu*surf->d_zistepu + surf->d_ziorigin | - // fu*surf2->d_zistepu | - // fv*surf->d_zistepv | fu - faddp %st(0),%st(2) // fu*surf2->d_zistepu | newzi | fu - - flds C(fv) // fv | fu*surf2->d_zistepu | newzi | fu - fmuls st_d_zistepv(%esi) // fv*surf2->d_zistepv | - // fu*surf2->d_zistepu | newzi | fu - fld %st(2) // newzi | fv*surf2->d_zistepv | - // fu*surf2->d_zistepu | newzi | fu - fmuls float_point_999 // newzibottom | fv*surf2->d_zistepv | - // fu*surf2->d_zistepu | newzi | fu - - fxch %st(2) // fu*surf2->d_zistepu | fv*surf2->d_zistepv | - // newzibottom | newzi | fu - fadds st_d_ziorigin(%esi) // fu*surf2->d_zistepu + surf2->d_ziorigin | - // fv*surf2->d_zistepv | newzibottom | newzi | - // fu - faddp %st(0),%st(1) // testzi | newzibottom | newzi | fu - fxch %st(1) // newzibottom | testzi | newzi | fu - -// if (newzibottom >= testzi) -// goto newtop; - - fcomp %st(1) // testzi | newzi | fu - - fxch %st(1) // newzi | testzi | fu - fmuls float_1_point_001 // newzitop | testzi | fu - fxch %st(1) // testzi | newzitop | fu - - fnstsw %ax - testb $0x01,%ah - jz Lnewtop_fpop3 - -// if (newzitop >= testzi) -// { - - fcomp %st(1) // newzitop | fu - fnstsw %ax - testb $0x45,%ah - jz Lsortloop_fpop2 - -// if (surf->d_zistepu >= surf2->d_zistepu) -// goto newtop; - - flds st_d_zistepu(%edi) // surf->d_zistepu | newzitop | fu - fcomps st_d_zistepu(%esi) // newzitop | fu - fnstsw %ax - testb $0x01,%ah - jz Lnewtop_fpop2 - -Lsortloop_fpop2: - fstp %st(0) // clear the FP stack - fstp %st(0) - movl st_key(%edi),%eax - jmp Lsortloop - - -.globl C(R_EdgeCodeEnd) -C(R_EdgeCodeEnd): - - -//---------------------------------------------------------------------- -// Surface array address code patching routine -//---------------------------------------------------------------------- - - .align 4 -.globl C(R_SurfacePatch) -C(R_SurfacePatch): - - movl C(surfaces),%eax - addl $(st_size),%eax - movl %eax,LPatch4-4 - - addl $(st_next),%eax - movl %eax,LPatch0-4 - movl %eax,LPatch2-4 - movl %eax,LPatch3-4 - - ret - -#endif // id386 - +/* + r_edgea.S + + (description) + + Copyright (C) 1996-1997 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id: r_edgea.s,v 1.1.1.4 2004/10/18 17:44:37 vvd0 Exp $ +*/ +// r_edgea.s +// x86 assembly-language edge-processing code. + +#include "asm_i386.h" +#include "quakeasm.h" +#include "asm_draw.h" + +#ifdef id386 + + .data +Ltemp: .long 0 +float_1_div_0100000h: .long 0x35800000 // 1.0/(float)0x100000 +float_point_999: .single 0.999 +float_1_point_001: .single 1.001 + + .text + +//-------------------------------------------------------------------- + +#define edgestoadd 4+8 // note odd stack offsets because of interleaving +#define edgelist 8+12 // with pushes + +.globl C(R_EdgeCodeStart) +C(R_EdgeCodeStart): + +.globl C(R_InsertNewEdges) +C(R_InsertNewEdges): + pushl %edi + pushl %esi // preserve register variables + movl edgestoadd(%esp),%edx + pushl %ebx + movl edgelist(%esp),%ecx + +LDoNextEdge: + movl et_u(%edx),%eax + movl %edx,%edi + +LContinueSearch: + movl et_u(%ecx),%ebx + movl et_next(%ecx),%esi + cmpl %ebx,%eax + jle LAddedge + movl et_u(%esi),%ebx + movl et_next(%esi),%ecx + cmpl %ebx,%eax + jle LAddedge2 + movl et_u(%ecx),%ebx + movl et_next(%ecx),%esi + cmpl %ebx,%eax + jle LAddedge + movl et_u(%esi),%ebx + movl et_next(%esi),%ecx + cmpl %ebx,%eax + jg LContinueSearch + +LAddedge2: + movl et_next(%edx),%edx + movl et_prev(%esi),%ebx + movl %esi,et_next(%edi) + movl %ebx,et_prev(%edi) + movl %edi,et_next(%ebx) + movl %edi,et_prev(%esi) + movl %esi,%ecx + + cmpl $0,%edx + jnz LDoNextEdge + jmp LDone + + .align 4 +LAddedge: + movl et_next(%edx),%edx + movl et_prev(%ecx),%ebx + movl %ecx,et_next(%edi) + movl %ebx,et_prev(%edi) + movl %edi,et_next(%ebx) + movl %edi,et_prev(%ecx) + + cmpl $0,%edx + jnz LDoNextEdge + +LDone: + popl %ebx // restore register variables + popl %esi + popl %edi + + ret + +//-------------------------------------------------------------------- + +#define predge 4+4 + +.globl C(R_RemoveEdges) +C(R_RemoveEdges): + pushl %ebx + movl predge(%esp),%eax + +Lre_loop: + movl et_next(%eax),%ecx + movl et_nextremove(%eax),%ebx + movl et_prev(%eax),%edx + testl %ebx,%ebx + movl %edx,et_prev(%ecx) + jz Lre_done + movl %ecx,et_next(%edx) + + movl et_next(%ebx),%ecx + movl et_prev(%ebx),%edx + movl et_nextremove(%ebx),%eax + movl %edx,et_prev(%ecx) + testl %eax,%eax + movl %ecx,et_next(%edx) + jnz Lre_loop + + popl %ebx + ret + +Lre_done: + movl %ecx,et_next(%edx) + popl %ebx + + ret + +//-------------------------------------------------------------------- + +#define pedgelist 4+4 // note odd stack offset because of interleaving + // with pushes + +.globl C(R_StepActiveU) +C(R_StepActiveU): + pushl %edi + movl pedgelist(%esp),%edx + pushl %esi // preserve register variables + pushl %ebx + + movl et_prev(%edx),%esi + +LNewEdge: + movl et_u(%esi),%edi + +LNextEdge: + movl et_u(%edx),%eax + movl et_u_step(%edx),%ebx + addl %ebx,%eax + movl et_next(%edx),%esi + movl %eax,et_u(%edx) + cmpl %edi,%eax + jl LPushBack + + movl et_u(%esi),%edi + movl et_u_step(%esi),%ebx + addl %ebx,%edi + movl et_next(%esi),%edx + movl %edi,et_u(%esi) + cmpl %eax,%edi + jl LPushBack2 + + movl et_u(%edx),%eax + movl et_u_step(%edx),%ebx + addl %ebx,%eax + movl et_next(%edx),%esi + movl %eax,et_u(%edx) + cmpl %edi,%eax + jl LPushBack + + movl et_u(%esi),%edi + movl et_u_step(%esi),%ebx + addl %ebx,%edi + movl et_next(%esi),%edx + movl %edi,et_u(%esi) + cmpl %eax,%edi + jnl LNextEdge + +LPushBack2: + movl %edx,%ebx + movl %edi,%eax + movl %esi,%edx + movl %ebx,%esi + +LPushBack: +// push it back to keep it sorted + movl et_prev(%edx),%ecx + movl et_next(%edx),%ebx + +// done if the -1 in edge_aftertail triggered this + cmpl $(C(edge_aftertail)),%edx + jz LUDone + +// pull the edge out of the edge list + movl et_prev(%ecx),%edi + movl %ecx,et_prev(%esi) + movl %ebx,et_next(%ecx) + +// find out where the edge goes in the edge list +LPushBackLoop: + movl et_prev(%edi),%ecx + movl et_u(%edi),%ebx + cmpl %ebx,%eax + jnl LPushBackFound + + movl et_prev(%ecx),%edi + movl et_u(%ecx),%ebx + cmpl %ebx,%eax + jl LPushBackLoop + + movl %ecx,%edi + +// put the edge back into the edge list +LPushBackFound: + movl et_next(%edi),%ebx + movl %edi,et_prev(%edx) + movl %ebx,et_next(%edx) + movl %edx,et_next(%edi) + movl %edx,et_prev(%ebx) + + movl %esi,%edx + movl et_prev(%esi),%esi + + cmpl $(C(edge_tail)),%edx + jnz LNewEdge + +LUDone: + popl %ebx // restore register variables + popl %esi + popl %edi + + ret + +//-------------------------------------------------------------------- + +#define surf 4 // note this is loaded before any pushes + + .align 4 +TrailingEdge: + movl st_spanstate(%esi),%eax // check for edge inversion + decl %eax + jnz LInverted + + movl %eax,st_spanstate(%esi) + movl st_insubmodel(%esi),%ecx + movl 0x12345678,%edx // surfaces[1].st_next +LPatch0: + movl C(r_bmodelactive),%eax + subl %ecx,%eax + cmpl %esi,%edx + movl %eax,C(r_bmodelactive) + jnz LNoEmit // surface isn't on top, just remove + +// emit a span (current top going away) + movl et_u(%ebx),%eax + shrl $20,%eax // iu = integral pixel u + movl st_last_u(%esi),%edx + movl st_next(%esi),%ecx + cmpl %edx,%eax + jle LNoEmit2 // iu <= surf->last_u, so nothing to emit + + movl %eax,st_last_u(%ecx) // surf->next->last_u = iu; + subl %edx,%eax + movl %edx,espan_t_u(%ebp) // span->u = surf->last_u; + + movl %eax,espan_t_count(%ebp) // span->count = iu - span->u; + movl C(current_iv),%eax + movl %eax,espan_t_v(%ebp) // span->v = current_iv; + movl st_spans(%esi),%eax + movl %eax,espan_t_pnext(%ebp) // span->pnext = surf->spans; + movl %ebp,st_spans(%esi) // surf->spans = span; + addl $(espan_t_size),%ebp + + movl st_next(%esi),%edx // remove the surface from the surface + movl st_prev(%esi),%esi // stack + + movl %edx,st_next(%esi) + movl %esi,st_prev(%edx) + ret + +LNoEmit2: + movl %eax,st_last_u(%ecx) // surf->next->last_u = iu; + movl st_next(%esi),%edx // remove the surface from the surface + movl st_prev(%esi),%esi // stack + + movl %edx,st_next(%esi) + movl %esi,st_prev(%edx) + ret + +LNoEmit: + movl st_next(%esi),%edx // remove the surface from the surface + movl st_prev(%esi),%esi // stack + + movl %edx,st_next(%esi) + movl %esi,st_prev(%edx) + ret + +LInverted: + movl %eax,st_spanstate(%esi) + ret + +//-------------------------------------------------------------------- + +// trailing edge only +Lgs_trailing: + pushl $Lgs_nextedge + jmp TrailingEdge + + +.globl C(R_GenerateSpans) +C(R_GenerateSpans): + pushl %ebp // preserve caller's stack frame + pushl %edi + pushl %esi // preserve register variables + pushl %ebx + +// clear active surfaces to just the background surface + movl C(surfaces),%eax + movl C(edge_head_u_shift20),%edx + addl $(st_size),%eax +// %ebp = span_p throughout + movl C(span_p),%ebp + + movl $0,C(r_bmodelactive) + + movl %eax,st_next(%eax) + movl %eax,st_prev(%eax) + movl %edx,st_last_u(%eax) + movl C(edge_head)+et_next,%ebx // edge=edge_head.next + +// generate spans + cmpl $(C(edge_tail)),%ebx // done if empty list + jz Lgs_lastspan + +Lgs_edgeloop: + + movl et_surfs(%ebx),%edi + movl C(surfaces),%eax + movl %edi,%esi + andl $0xFFFF0000,%edi + andl $0xFFFF,%esi + jz Lgs_leading // not a trailing edge + +// it has a left surface, so a surface is going away for this span + shll $(SURF_T_SHIFT),%esi + addl %eax,%esi + testl %edi,%edi + jz Lgs_trailing + +// both leading and trailing + call TrailingEdge + movl C(surfaces),%eax + +// --------------------------------------------------------------- +// handle a leading edge +// --------------------------------------------------------------- + +Lgs_leading: + shrl $16-SURF_T_SHIFT,%edi + movl C(surfaces),%eax + addl %eax,%edi + movl 0x12345678,%esi // surf2 = surfaces[1].next; +LPatch2: + movl st_spanstate(%edi),%edx + movl st_insubmodel(%edi),%eax + testl %eax,%eax + jnz Lbmodel_leading + +// handle a leading non-bmodel edge + +// 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) + testl %edx,%edx + jnz Lxl_done + + +// if (surf->key < surf2->key) +// goto newtop; + incl %edx + movl st_key(%edi),%eax + movl %edx,st_spanstate(%edi) + movl st_key(%esi),%ecx + cmpl %ecx,%eax + jl Lnewtop + +// main sorting loop to search through surface stack until insertion point +// found. Always terminates because background surface is sentinel +// do +// { +// surf2 = surf2->next; +// } while (surf->key >= surf2->key); +Lsortloopnb: + movl st_next(%esi),%esi + movl st_key(%esi),%ecx + cmpl %ecx,%eax + jge Lsortloopnb + + jmp LInsertAndExit + + +// handle a leading bmodel edge + .align 4 +Lbmodel_leading: + +// 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) + testl %edx,%edx + jnz Lxl_done + + movl C(r_bmodelactive),%ecx + incl %edx + incl %ecx + movl %edx,st_spanstate(%edi) + movl %ecx,C(r_bmodelactive) + +// if (surf->key < surf2->key) +// goto newtop; + movl st_key(%edi),%eax + movl st_key(%esi),%ecx + cmpl %ecx,%eax + jl Lnewtop + +// if ((surf->key == surf2->key) && surf->insubmodel) +// { + jz Lzcheck_for_newtop + +// main sorting loop to search through surface stack until insertion point +// found. Always terminates because background surface is sentinel +// do +// { +// surf2 = surf2->next; +// } while (surf->key > surf2->key); +Lsortloop: + movl st_next(%esi),%esi + movl st_key(%esi),%ecx + cmpl %ecx,%eax + jg Lsortloop + + jne LInsertAndExit + +// Do 1/z sorting to see if we've arrived in the right position + movl et_u(%ebx),%eax + subl $0xFFFFF,%eax + movl %eax,Ltemp + fildl Ltemp + + fmuls float_1_div_0100000h // fu = (float)(edge->u - 0xFFFFF) * + // (1.0 / 0x100000); + + fld %st(0) // fu | fu + fmuls st_d_zistepu(%edi) // fu*surf->d_zistepu | fu + flds C(fv) // fv | fu*surf->d_zistepu | fu + fmuls st_d_zistepv(%edi) // fv*surf->d_zistepv | fu*surf->d_zistepu | fu + fxch %st(1) // fu*surf->d_zistepu | fv*surf->d_zistepv | fu + fadds st_d_ziorigin(%edi) // fu*surf->d_zistepu + surf->d_ziorigin | + // fv*surf->d_zistepv | fu + + flds st_d_zistepu(%esi) // surf2->d_zistepu | + // fu*surf->d_zistepu + surf->d_ziorigin | + // fv*surf->d_zistepv | fu + fmul %st(3),%st(0) // fu*surf2->d_zistepu | + // fu*surf->d_zistepu + surf->d_ziorigin | + // fv*surf->d_zistepv | fu + fxch %st(1) // fu*surf->d_zistepu + surf->d_ziorigin | + // fu*surf2->d_zistepu | + // fv*surf->d_zistepv | fu + faddp %st(0),%st(2) // fu*surf2->d_zistepu | newzi | fu + + flds C(fv) // fv | fu*surf2->d_zistepu | newzi | fu + fmuls st_d_zistepv(%esi) // fv*surf2->d_zistepv | + // fu*surf2->d_zistepu | newzi | fu + fld %st(2) // newzi | fv*surf2->d_zistepv | + // fu*surf2->d_zistepu | newzi | fu + fmuls float_point_999 // newzibottom | fv*surf2->d_zistepv | + // fu*surf2->d_zistepu | newzi | fu + + fxch %st(2) // fu*surf2->d_zistepu | fv*surf2->d_zistepv | + // newzibottom | newzi | fu + fadds st_d_ziorigin(%esi) // fu*surf2->d_zistepu + surf2->d_ziorigin | + // fv*surf2->d_zistepv | newzibottom | newzi | + // fu + faddp %st(0),%st(1) // testzi | newzibottom | newzi | fu + fxch %st(1) // newzibottom | testzi | newzi | fu + +// if (newzibottom >= testzi) +// goto Lgotposition; + + fcomp %st(1) // testzi | newzi | fu + + fxch %st(1) // newzi | testzi | fu + fmuls float_1_point_001 // newzitop | testzi | fu + fxch %st(1) // testzi | newzitop | fu + + fnstsw %ax + testb $0x01,%ah + jz Lgotposition_fpop3 + +// if (newzitop >= testzi) +// { + + fcomp %st(1) // newzitop | fu + fnstsw %ax + testb $0x45,%ah + jz Lsortloop_fpop2 + +// if (surf->d_zistepu >= surf2->d_zistepu) +// goto newtop; + + flds st_d_zistepu(%edi) // surf->d_zistepu | newzitop| fu + fcomps st_d_zistepu(%esi) // newzitop | fu + fnstsw %ax + testb $0x01,%ah + jz Lgotposition_fpop2 + + fstp %st(0) // clear the FPstack + fstp %st(0) + movl st_key(%edi),%eax + jmp Lsortloop + + +Lgotposition_fpop3: + fstp %st(0) +Lgotposition_fpop2: + fstp %st(0) + fstp %st(0) + jmp LInsertAndExit + + +// emit a span (obscures current top) + +Lnewtop_fpop3: + fstp %st(0) +Lnewtop_fpop2: + fstp %st(0) + fstp %st(0) + movl st_key(%edi),%eax // reload the sorting key + +Lnewtop: + movl et_u(%ebx),%eax + movl st_last_u(%esi),%edx + shrl $20,%eax // iu = integral pixel u + movl %eax,st_last_u(%edi) // surf->last_u = iu; + cmpl %edx,%eax + jle LInsertAndExit // iu <= surf->last_u, so nothing to emit + + subl %edx,%eax + movl %edx,espan_t_u(%ebp) // span->u = surf->last_u; + + movl %eax,espan_t_count(%ebp) // span->count = iu - span->u; + movl C(current_iv),%eax + movl %eax,espan_t_v(%ebp) // span->v = current_iv; + movl st_spans(%esi),%eax + movl %eax,espan_t_pnext(%ebp) // span->pnext = surf->spans; + movl %ebp,st_spans(%esi) // surf->spans = span; + addl $(espan_t_size),%ebp + +LInsertAndExit: +// insert before surf2 + movl %esi,st_next(%edi) // surf->next = surf2; + movl st_prev(%esi),%eax + movl %eax,st_prev(%edi) // surf->prev = surf2->prev; + movl %edi,st_prev(%esi) // surf2->prev = surf; + movl %edi,st_next(%eax) // surf2->prev->next = surf; + +// --------------------------------------------------------------- +// leading edge done +// --------------------------------------------------------------- + +// --------------------------------------------------------------- +// see if there are any more edges +// --------------------------------------------------------------- + +Lgs_nextedge: + movl et_next(%ebx),%ebx + cmpl $(C(edge_tail)),%ebx + jnz Lgs_edgeloop + +// clean up at the right edge +Lgs_lastspan: + +// 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 + movl 0x12345678,%esi // surfaces[1].st_next +LPatch3: + movl C(edge_tail_u_shift20),%eax + xorl %ecx,%ecx + movl st_last_u(%esi),%edx + subl %edx,%eax + jle Lgs_resetspanstate + + movl %edx,espan_t_u(%ebp) + movl %eax,espan_t_count(%ebp) + movl C(current_iv),%eax + movl %eax,espan_t_v(%ebp) + movl st_spans(%esi),%eax + movl %eax,espan_t_pnext(%ebp) + movl %ebp,st_spans(%esi) + addl $(espan_t_size),%ebp + +// reset spanstate for all surfaces in the surface stack +Lgs_resetspanstate: + movl %ecx,st_spanstate(%esi) + movl st_next(%esi),%esi + cmpl $0x12345678,%esi // &surfaces[1] +LPatch4: + jnz Lgs_resetspanstate + +// store the final span_p + movl %ebp,C(span_p) + + popl %ebx // restore register variables + popl %esi + popl %edi + popl %ebp // restore the caller's stack frame + ret + + +// --------------------------------------------------------------- +// 1/z sorting for bmodels in the same leaf +// --------------------------------------------------------------- + .align 4 +Lxl_done: + incl %edx + movl %edx,st_spanstate(%edi) + + jmp Lgs_nextedge + + + .align 4 +Lzcheck_for_newtop: + movl et_u(%ebx),%eax + subl $0xFFFFF,%eax + movl %eax,Ltemp + fildl Ltemp + + fmuls float_1_div_0100000h // fu = (float)(edge->u - 0xFFFFF) * + // (1.0 / 0x100000); + + fld %st(0) // fu | fu + fmuls st_d_zistepu(%edi) // fu*surf->d_zistepu | fu + flds C(fv) // fv | fu*surf->d_zistepu | fu + fmuls st_d_zistepv(%edi) // fv*surf->d_zistepv | fu*surf->d_zistepu | fu + fxch %st(1) // fu*surf->d_zistepu | fv*surf->d_zistepv | fu + fadds st_d_ziorigin(%edi) // fu*surf->d_zistepu + surf->d_ziorigin | + // fv*surf->d_zistepv | fu + + flds st_d_zistepu(%esi) // surf2->d_zistepu | + // fu*surf->d_zistepu + surf->d_ziorigin | + // fv*surf->d_zistepv | fu + fmul %st(3),%st(0) // fu*surf2->d_zistepu | + // fu*surf->d_zistepu + surf->d_ziorigin | + // fv*surf->d_zistepv | fu + fxch %st(1) // fu*surf->d_zistepu + surf->d_ziorigin | + // fu*surf2->d_zistepu | + // fv*surf->d_zistepv | fu + faddp %st(0),%st(2) // fu*surf2->d_zistepu | newzi | fu + + flds C(fv) // fv | fu*surf2->d_zistepu | newzi | fu + fmuls st_d_zistepv(%esi) // fv*surf2->d_zistepv | + // fu*surf2->d_zistepu | newzi | fu + fld %st(2) // newzi | fv*surf2->d_zistepv | + // fu*surf2->d_zistepu | newzi | fu + fmuls float_point_999 // newzibottom | fv*surf2->d_zistepv | + // fu*surf2->d_zistepu | newzi | fu + + fxch %st(2) // fu*surf2->d_zistepu | fv*surf2->d_zistepv | + // newzibottom | newzi | fu + fadds st_d_ziorigin(%esi) // fu*surf2->d_zistepu + surf2->d_ziorigin | + // fv*surf2->d_zistepv | newzibottom | newzi | + // fu + faddp %st(0),%st(1) // testzi | newzibottom | newzi | fu + fxch %st(1) // newzibottom | testzi | newzi | fu + +// if (newzibottom >= testzi) +// goto newtop; + + fcomp %st(1) // testzi | newzi | fu + + fxch %st(1) // newzi | testzi | fu + fmuls float_1_point_001 // newzitop | testzi | fu + fxch %st(1) // testzi | newzitop | fu + + fnstsw %ax + testb $0x01,%ah + jz Lnewtop_fpop3 + +// if (newzitop >= testzi) +// { + + fcomp %st(1) // newzitop | fu + fnstsw %ax + testb $0x45,%ah + jz Lsortloop_fpop2 + +// if (surf->d_zistepu >= surf2->d_zistepu) +// goto newtop; + + flds st_d_zistepu(%edi) // surf->d_zistepu | newzitop | fu + fcomps st_d_zistepu(%esi) // newzitop | fu + fnstsw %ax + testb $0x01,%ah + jz Lnewtop_fpop2 + +Lsortloop_fpop2: + fstp %st(0) // clear the FP stack + fstp %st(0) + movl st_key(%edi),%eax + jmp Lsortloop + + +.globl C(R_EdgeCodeEnd) +C(R_EdgeCodeEnd): + + +//---------------------------------------------------------------------- +// Surface array address code patching routine +//---------------------------------------------------------------------- + + .align 4 +.globl C(R_SurfacePatch) +C(R_SurfacePatch): + + movl C(surfaces),%eax + addl $(st_size),%eax + movl %eax,LPatch4-4 + + addl $(st_next),%eax + movl %eax,LPatch0-4 + movl %eax,LPatch2-4 + movl %eax,LPatch3-4 + + ret + +#endif // id386 + diff --git a/source/r_efrag.c b/source/r_efrag.c index 94ca48a8..44382198 100644 --- a/source/r_efrag.c +++ b/source/r_efrag.c @@ -1,275 +1,275 @@ -/* -Copyright (C) 1996-1997 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_efrag.c - -#include "quakedef.h" -#include "r_local.h" - -mnode_t *r_pefragtopnode; - - -//=========================================================================== - -/* -=============================================================================== - - ENTITY FRAGMENT FUNCTIONS - -=============================================================================== -*/ - -efrag_t **lastlink; - -vec3_t r_emins, r_emaxs; - -entity_t *r_addent; - - -/* -================ -R_RemoveEfrags - -Call when removing an object from the world or moving it to another position -================ -*/ -void R_RemoveEfrags (entity_t *ent) -{ - efrag_t *ef, *old, *walk, **prev; - - ef = ent->efrag; - - while (ef) - { - prev = &ef->leaf->efrags; - while (1) - { - walk = *prev; - if (!walk) - break; - if (walk == ef) - { // remove this fragment - *prev = ef->leafnext; - break; - } - else - prev = &walk->leafnext; - } - - old = ef; - ef = ef->entnext; - - // put it on the free list - old->entnext = cl.free_efrags; - cl.free_efrags = old; - } - - ent->efrag = NULL; -} - -/* -=================== -R_SplitEntityOnNode -=================== -*/ -void R_SplitEntityOnNode (mnode_t *node) -{ - efrag_t *ef; - mplane_t *splitplane; - mleaf_t *leaf; - int sides; - - if (node->contents == CONTENTS_SOLID) - { - return; - } - -// add an efrag if the node is a leaf - - if ( node->contents < 0) - { - if (!r_pefragtopnode) - r_pefragtopnode = node; - - leaf = (mleaf_t *)node; - -// grab an efrag off the free list - ef = cl.free_efrags; - if (!ef) - { - Con_Printf ("Too many efrags!\n"); - return; // no free fragments... - } - cl.free_efrags = cl.free_efrags->entnext; - - ef->entity = r_addent; - -// add the entity link - *lastlink = ef; - lastlink = &ef->entnext; - ef->entnext = NULL; - -// set the leaf links - ef->leaf = leaf; - ef->leafnext = leaf->efrags; - leaf->efrags = ef; - - return; - } - -// NODE_MIXED - - splitplane = node->plane; - sides = BOX_ON_PLANE_SIDE(r_emins, r_emaxs, splitplane); - - if (sides == 3) - { - // split on this plane - // if this is the first splitter of this bmodel, remember it - if (!r_pefragtopnode) - r_pefragtopnode = node; - } - -// recurse down the contacted sides - if (sides & 1) - R_SplitEntityOnNode (node->children[0]); - - if (sides & 2) - R_SplitEntityOnNode (node->children[1]); -} - - -/* -=================== -R_SplitEntityOnNode2 -=================== -*/ -void R_SplitEntityOnNode2 (mnode_t *node) -{ - mplane_t *splitplane; - int sides; - - if (node->visframe != r_visframecount) - return; - - if (node->contents < 0) - { - if (node->contents != CONTENTS_SOLID) - r_pefragtopnode = node; // we've reached a non-solid leaf, so it's - // visible and not BSP clipped - return; - } - - splitplane = node->plane; - sides = BOX_ON_PLANE_SIDE(r_emins, r_emaxs, splitplane); - - if (sides == 3) - { - // remember first splitter - r_pefragtopnode = node; - return; - } - -// not split yet; recurse down the contacted side - if (sides & 1) - R_SplitEntityOnNode2 (node->children[0]); - else - R_SplitEntityOnNode2 (node->children[1]); -} - - -/* -=========== -R_AddEfrags -=========== -*/ -void R_AddEfrags (entity_t *ent) -{ - model_t *entmodel; - int i; - - if (!ent->model) - return; - - if (ent == &r_worldentity) - return; // never add the world - - r_addent = ent; - - lastlink = &ent->efrag; - r_pefragtopnode = NULL; - - entmodel = ent->model; - - for (i=0 ; i<3 ; i++) - { - r_emins[i] = ent->origin[i] + entmodel->mins[i]; - r_emaxs[i] = ent->origin[i] + entmodel->maxs[i]; - } - - R_SplitEntityOnNode (cl.worldmodel->nodes); - - ent->topnode = r_pefragtopnode; -} - - -/* -================ -R_StoreEfrags - -// FIXME: a lot of this goes away with edge-based -================ -*/ -void R_StoreEfrags (efrag_t **ppefrag) -{ - entity_t *pent; - model_t *model; - efrag_t *pefrag; - extern cvar_t r_drawflame; - - for (pefrag = *ppefrag ; pefrag ; pefrag = pefrag->leafnext) - { - pent = pefrag->entity; - model = pent->model; - - if (model->modhint == MOD_FLAME && !r_drawflame.value) - continue; - - switch (model->type) - { - case mod_alias: - case mod_brush: - case mod_sprite: - if ((pent->visframe != r_framecount) && - (cl_numvisedicts < MAX_VISEDICTS)) - { - cl_visedicts[cl_numvisedicts++] = *pent; - - // mark that we've recorded this entity for this frame - pent->visframe = r_framecount; - } - break; - - default: - Sys_Error ("R_StoreEfrags: Bad entity type %d\n", model->type); - } - } -} - - +/* +Copyright (C) 1996-1997 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_efrag.c + +#include "quakedef.h" +#include "r_local.h" + +mnode_t *r_pefragtopnode; + + +//=========================================================================== + +/* +=============================================================================== + + ENTITY FRAGMENT FUNCTIONS + +=============================================================================== +*/ + +efrag_t **lastlink; + +vec3_t r_emins, r_emaxs; + +entity_t *r_addent; + + +/* +================ +R_RemoveEfrags + +Call when removing an object from the world or moving it to another position +================ +*/ +void R_RemoveEfrags (entity_t *ent) +{ + efrag_t *ef, *old, *walk, **prev; + + ef = ent->efrag; + + while (ef) + { + prev = &ef->leaf->efrags; + while (1) + { + walk = *prev; + if (!walk) + break; + if (walk == ef) + { // remove this fragment + *prev = ef->leafnext; + break; + } + else + prev = &walk->leafnext; + } + + old = ef; + ef = ef->entnext; + + // put it on the free list + old->entnext = cl.free_efrags; + cl.free_efrags = old; + } + + ent->efrag = NULL; +} + +/* +=================== +R_SplitEntityOnNode +=================== +*/ +void R_SplitEntityOnNode (mnode_t *node) +{ + efrag_t *ef; + mplane_t *splitplane; + mleaf_t *leaf; + int sides; + + if (node->contents == CONTENTS_SOLID) + { + return; + } + +// add an efrag if the node is a leaf + + if ( node->contents < 0) + { + if (!r_pefragtopnode) + r_pefragtopnode = node; + + leaf = (mleaf_t *)node; + +// grab an efrag off the free list + ef = cl.free_efrags; + if (!ef) + { + Con_Printf ("Too many efrags!\n"); + return; // no free fragments... + } + cl.free_efrags = cl.free_efrags->entnext; + + ef->entity = r_addent; + +// add the entity link + *lastlink = ef; + lastlink = &ef->entnext; + ef->entnext = NULL; + +// set the leaf links + ef->leaf = leaf; + ef->leafnext = leaf->efrags; + leaf->efrags = ef; + + return; + } + +// NODE_MIXED + + splitplane = node->plane; + sides = BOX_ON_PLANE_SIDE(r_emins, r_emaxs, splitplane); + + if (sides == 3) + { + // split on this plane + // if this is the first splitter of this bmodel, remember it + if (!r_pefragtopnode) + r_pefragtopnode = node; + } + +// recurse down the contacted sides + if (sides & 1) + R_SplitEntityOnNode (node->children[0]); + + if (sides & 2) + R_SplitEntityOnNode (node->children[1]); +} + + +/* +=================== +R_SplitEntityOnNode2 +=================== +*/ +void R_SplitEntityOnNode2 (mnode_t *node) +{ + mplane_t *splitplane; + int sides; + + if (node->visframe != r_visframecount) + return; + + if (node->contents < 0) + { + if (node->contents != CONTENTS_SOLID) + r_pefragtopnode = node; // we've reached a non-solid leaf, so it's + // visible and not BSP clipped + return; + } + + splitplane = node->plane; + sides = BOX_ON_PLANE_SIDE(r_emins, r_emaxs, splitplane); + + if (sides == 3) + { + // remember first splitter + r_pefragtopnode = node; + return; + } + +// not split yet; recurse down the contacted side + if (sides & 1) + R_SplitEntityOnNode2 (node->children[0]); + else + R_SplitEntityOnNode2 (node->children[1]); +} + + +/* +=========== +R_AddEfrags +=========== +*/ +void R_AddEfrags (entity_t *ent) +{ + model_t *entmodel; + int i; + + if (!ent->model) + return; + + if (ent == &r_worldentity) + return; // never add the world + + r_addent = ent; + + lastlink = &ent->efrag; + r_pefragtopnode = NULL; + + entmodel = ent->model; + + for (i=0 ; i<3 ; i++) + { + r_emins[i] = ent->origin[i] + entmodel->mins[i]; + r_emaxs[i] = ent->origin[i] + entmodel->maxs[i]; + } + + R_SplitEntityOnNode (cl.worldmodel->nodes); + + ent->topnode = r_pefragtopnode; +} + + +/* +================ +R_StoreEfrags + +// FIXME: a lot of this goes away with edge-based +================ +*/ +void R_StoreEfrags (efrag_t **ppefrag) +{ + entity_t *pent; + model_t *model; + efrag_t *pefrag; + extern cvar_t r_drawflame; + + for (pefrag = *ppefrag ; pefrag ; pefrag = pefrag->leafnext) + { + pent = pefrag->entity; + model = pent->model; + + if (model->modhint == MOD_FLAME && !r_drawflame.value) + continue; + + switch (model->type) + { + case mod_alias: + case mod_brush: + case mod_sprite: + if ((pent->visframe != r_framecount) && + (cl_numvisedicts < MAX_VISEDICTS)) + { + cl_visedicts[cl_numvisedicts++] = *pent; + + // mark that we've recorded this entity for this frame + pent->visframe = r_framecount; + } + break; + + default: + Sys_Error ("R_StoreEfrags: Bad entity type %d\n", model->type); + } + } +} + + diff --git a/source/r_light.c b/source/r_light.c index 14350e81..f735d50d 100644 --- a/source/r_light.c +++ b/source/r_light.c @@ -1,260 +1,260 @@ -/* -Copyright (C) 1996-1997 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 "quakedef.h" -#include "r_local.h" - -int r_dlightframecount; - - -/* -================== -R_AnimateLight -================== -*/ -void R_AnimateLight (void) -{ - int i,j,k; - -// -// light animations -// 'm' is normal light, 'a' is no light, 'z' is double bright - i = (int)(cl.time*10); - for (j=0 ; jcontents < 0) - return; - - splitplane = node->plane; - dist = DotProduct (light->origin, splitplane->normal) - splitplane->dist; - - if (dist > light->radius) - { - R_MarkLights (light, bit, node->children[0]); - return; - } - if (dist < -light->radius) - { - R_MarkLights (light, bit, node->children[1]); - return; - } - -// mark the polygons - surf = cl.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 (void) -{ - int i; - dlight_t *l; - - r_dlightframecount = r_framecount + 1; // because the count hasn't - // advanced yet for this frame - l = cl_dlights; - - for (i=0 ; idie < cl.time || !l->radius) - continue; - R_MarkLights ( l, 1<nodes ); - } -} - - -/* -============================================================================= - -LIGHT SAMPLING - -============================================================================= -*/ - -int RecursiveLightPoint (mnode_t *node, vec3_t start, vec3_t end) -{ - int r; - float front, back, frac; - int side; - mplane_t *plane; - vec3_t mid; - msurface_t *surf; - int s, t, ds, dt; - int i; - mtexinfo_t *tex; - byte *lightmap; - unsigned scale; - int maps; - - if (node->contents < 0) - 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; - -// 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 anuthing - -// check for impact on this node - - surf = cl.worldmodel->surfaces + node->firstsurface; - for (i=0 ; inumsurfaces ; i++, surf++) - { - if (surf->flags & SURF_DRAWTILED) - 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; - r = 0; - if (lightmap) - { - - lightmap += dt * ((surf->extents[0]>>4)+1) + ds; - - for (maps = 0 ; maps < MAXLIGHTMAPS && surf->styles[maps] != 255 ; - maps++) - { - scale = d_lightstylevalue[surf->styles[maps]]; - r += *lightmap * scale; - lightmap += ((surf->extents[0]>>4)+1) * - ((surf->extents[1]>>4)+1); - } - - r >>= 8; - } - - return r; - } - -// go down back side - return RecursiveLightPoint (node->children[!side], mid, end); -} - -int R_LightPoint (vec3_t p) -{ - vec3_t end; - int r; - - if (!cl.worldmodel->lightdata) - return 255; - - end[0] = p[0]; - end[1] = p[1]; - end[2] = p[2] - 2048; - - r = RecursiveLightPoint (cl.worldmodel->nodes, p, end); - - if (r == -1) - r = 0; - - if (r < r_refdef.ambientlight) - r = r_refdef.ambientlight; - - return r; -} - +/* +Copyright (C) 1996-1997 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 "quakedef.h" +#include "r_local.h" + +int r_dlightframecount; + + +/* +================== +R_AnimateLight +================== +*/ +void R_AnimateLight (void) +{ + int i,j,k; + +// +// light animations +// 'm' is normal light, 'a' is no light, 'z' is double bright + i = (int)(cl.time*10); + for (j=0 ; jcontents < 0) + return; + + splitplane = node->plane; + dist = DotProduct (light->origin, splitplane->normal) - splitplane->dist; + + if (dist > light->radius) + { + R_MarkLights (light, bit, node->children[0]); + return; + } + if (dist < -light->radius) + { + R_MarkLights (light, bit, node->children[1]); + return; + } + +// mark the polygons + surf = cl.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 (void) +{ + int i; + dlight_t *l; + + r_dlightframecount = r_framecount + 1; // because the count hasn't + // advanced yet for this frame + l = cl_dlights; + + for (i=0 ; idie < cl.time || !l->radius) + continue; + R_MarkLights ( l, 1<nodes ); + } +} + + +/* +============================================================================= + +LIGHT SAMPLING + +============================================================================= +*/ + +int RecursiveLightPoint (mnode_t *node, vec3_t start, vec3_t end) +{ + int r; + float front, back, frac; + int side; + mplane_t *plane; + vec3_t mid; + msurface_t *surf; + int s, t, ds, dt; + int i; + mtexinfo_t *tex; + byte *lightmap; + unsigned scale; + int maps; + + if (node->contents < 0) + 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; + +// 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 anuthing + +// check for impact on this node + + surf = cl.worldmodel->surfaces + node->firstsurface; + for (i=0 ; inumsurfaces ; i++, surf++) + { + if (surf->flags & SURF_DRAWTILED) + 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; + r = 0; + if (lightmap) + { + + lightmap += dt * ((surf->extents[0]>>4)+1) + ds; + + for (maps = 0 ; maps < MAXLIGHTMAPS && surf->styles[maps] != 255 ; + maps++) + { + scale = d_lightstylevalue[surf->styles[maps]]; + r += *lightmap * scale; + lightmap += ((surf->extents[0]>>4)+1) * + ((surf->extents[1]>>4)+1); + } + + r >>= 8; + } + + return r; + } + +// go down back side + return RecursiveLightPoint (node->children[!side], mid, end); +} + +int R_LightPoint (vec3_t p) +{ + vec3_t end; + int r; + + if (!cl.worldmodel->lightdata) + return 255; + + end[0] = p[0]; + end[1] = p[1]; + end[2] = p[2] - 2048; + + r = RecursiveLightPoint (cl.worldmodel->nodes, p, end); + + if (r == -1) + r = 0; + + if (r < r_refdef.ambientlight) + r = r_refdef.ambientlight; + + return r; +} + diff --git a/source/r_local.h b/source/r_local.h index 9787d300..223e3cec 100644 --- a/source/r_local.h +++ b/source/r_local.h @@ -1,317 +1,317 @@ -/* -Copyright (C) 1996-1997 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_local.h -- private refresh defs - -#ifndef GLQUAKE - -#include "r_shared.h" - -#define ALIAS_BASE_SIZE_RATIO (1.0 / 11.0) - // normalizing factor so player model works out to about - // 1 pixel per triangle - -#define BMODEL_FULLY_CLIPPED 0x10 // value returned by R_BmodelCheckBBox () - // if bbox is trivially rejected - -//=========================================================================== -// viewmodel lighting - -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 { - float fv[3]; // viewspace x, y -} auxvert_t; - -//=========================================================================== - -extern cvar_t r_draworder; -extern cvar_t r_speeds; -extern cvar_t r_timegraph; -extern cvar_t r_graphheight; -extern cvar_t r_clearcolor; -extern cvar_t r_waterwarp; -extern cvar_t r_fullbright; -extern cvar_t r_drawentities; -extern cvar_t r_aliasstats; -extern cvar_t r_dspeeds; -extern cvar_t r_drawflat; -extern cvar_t r_ambient; -extern cvar_t r_reportsurfout; -extern cvar_t r_maxsurfs; -extern cvar_t r_numsurfs; -extern cvar_t r_reportedgeout; -extern cvar_t r_maxedges; -extern cvar_t r_numedges; - -#define XCENTERING (1.0 / 2.0) -#define YCENTERING (1.0 / 2.0) - -#define CLIP_EPSILON 0.001 - -#define BACKFACE_EPSILON 0.01 - -//=========================================================================== - -#define DIST_NOT_SET 98765 - -// !!! if this is changed, it must be changed in asm_draw.h too !!! -typedef struct clipplane_s -{ - vec3_t normal; - float dist; - struct clipplane_s *next; - byte leftedge; - byte rightedge; - byte reserved[2]; -} clipplane_t; - -extern clipplane_t view_clipplanes[4]; - -//============================================================================= - -void R_RenderWorld (void); - -//============================================================================= - -extern mplane_t screenedge[4]; - -extern vec3_t r_origin; - -extern vec3_t r_entorigin; - -extern float screenAspect; -extern float verticalFieldOfView; -extern float xOrigin, yOrigin; - -extern int r_visframecount; - -//============================================================================= - -extern int vstartscan; - - -void R_ClearPolyList (void); -void R_DrawPolyList (void); - -// -// current entity info -// -extern qboolean insubmodel; -extern vec3_t r_worldmodelorg; - - -void R_DrawSprite (void); -void R_RenderFace (msurface_t *fa, int clipflags); -void R_RenderPoly (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_SetSkyFrame (void); -void R_DrawSurfaceBlock16 (void); -void R_DrawSurfaceBlock8 (void); -texture_t *R_TextureAnimation (texture_t *base); - -#if id386 - -void R_DrawSurfaceBlock8_mip0 (void); -void R_DrawSurfaceBlock8_mip1 (void); -void R_DrawSurfaceBlock8_mip2 (void); -void R_DrawSurfaceBlock8_mip3 (void); - -#endif - -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); -void R_DrawSolidClippedSubmodelPolygons (model_t *pmodel); - -void R_AddPolygonEdges (emitpoint_t *pverts, int numverts, int miplevel); -surf_t *R_GetSurf (void); -void R_AliasDrawModel (alight_t *plighting); -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); - -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 model_t *cl_worldmodel; - -extern int *pfrustum_indexes[4]; - -// !!! if this is changed, it must be changed in asm_draw.h too !!! -#define NEAR_CLIP 0.01 - -extern int ubasestep, errorterm, erroradjustup, erroradjustdown; -extern int vstartscan; - -extern fixed16_t sadjust, tadjust; -extern fixed16_t bbextents, bbextentt; - -#define MAXBVERTINDEXES 1000 // new clipped vertices when clipping bmodels - // to the world BSP -extern mvertex_t *r_ptverts, *r_ptvertsmax; - -extern vec3_t sbaseaxis[3], tbaseaxis[3]; -extern float entity_rotation[3][3]; - -extern int reinit_surfcache; - -extern int r_currentkey; -extern int r_currentbkey; - -typedef struct btofpoly_s { - int clipflags; - msurface_t *psurf; -} btofpoly_t; - -#define MAX_BTOFPOLYS 5000 // FIXME: tune this - -extern int numbtofpolys; -extern btofpoly_t *pbtofpolys; - -void R_InitTurb (void); -void R_ZDrawSubmodelPolys (model_t *clmodel); - -//========================================================= -// Alias models -//========================================================= - -#define MAXALIASVERTS 2000 // TODO: tune this -#define ALIAS_Z_CLIP_PLANE 5 - -extern int numverts; -extern int a_skinwidth; -extern mtriangle_t *ptriangles; -extern int numtriangles; -extern aliashdr_t *paliashdr; -extern mdl_t *pmdl; -extern float leftclip, topclip, rightclip, bottomclip; -extern int r_acliptype; -extern finalvert_t *pfinalverts; -extern auxvert_t *pauxverts; - -qboolean R_AliasCheckBBox (void); - -//========================================================= -// turbulence stuff - -#define AMP 8*0x10000 -#define AMP2 3 -#define SPEED 20 - -//========================================================= -// particle stuff - -void R_DrawParticles (void); -void R_InitParticles (void); -void R_ClearParticles (void); -void R_ReadPointFile_f (void); -void R_SurfacePatch (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[MAXHEIGHT]; -extern edge_t *removeedges[MAXHEIGHT]; - -extern int screenwidth; - -// FIXME: make stack vars when debugging done -extern edge_t edge_head; -extern edge_t edge_tail; -extern edge_t edge_aftertail; -extern int r_bmodelactive; -extern vrect_t *pconupdate; - -extern float aliasxscale, aliasyscale, aliasxcenter, aliasycenter; -extern float r_aliastransition, r_resfudge; - -extern int r_outofsurfaces; -extern int r_outofedges; - -extern mvertex_t *r_pcurrentvertbase; -extern int r_maxvalidedgeoffset; - -void R_AliasClipTriangle (mtriangle_t *ptri); - -extern float r_time1; -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 cshift_t cshift_water; -extern qboolean r_dowarpold, r_viewchanged; - -extern mleaf_t *r_viewleaf, *r_oldviewleaf; - -extern vec3_t r_emins, r_emaxs; -extern mnode_t *r_pefragtopnode; -extern int r_clipflags; -extern int r_dlightframecount; -extern qboolean r_fov_greater_than_90; - -void R_StoreEfrags (efrag_t **ppefrag); -void R_TimeRefresh_f (void); -void R_TimeGraph (void); -void R_PrintAliasStats (void); -void R_PrintTimes (void); -void R_PrintDSpeeds (void); -void R_AnimateLight (void); -int R_LightPoint (vec3_t p); -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); -void R_MarkLights (dlight_t *light, int bit, mnode_t *node); - +/* +Copyright (C) 1996-1997 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_local.h -- private refresh defs + +#ifndef GLQUAKE + +#include "r_shared.h" + +#define ALIAS_BASE_SIZE_RATIO (1.0 / 11.0) + // normalizing factor so player model works out to about + // 1 pixel per triangle + +#define BMODEL_FULLY_CLIPPED 0x10 // value returned by R_BmodelCheckBBox () + // if bbox is trivially rejected + +//=========================================================================== +// viewmodel lighting + +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 { + float fv[3]; // viewspace x, y +} auxvert_t; + +//=========================================================================== + +extern cvar_t r_draworder; +extern cvar_t r_speeds; +extern cvar_t r_timegraph; +extern cvar_t r_graphheight; +extern cvar_t r_clearcolor; +extern cvar_t r_waterwarp; +extern cvar_t r_fullbright; +extern cvar_t r_drawentities; +extern cvar_t r_aliasstats; +extern cvar_t r_dspeeds; +extern cvar_t r_drawflat; +extern cvar_t r_ambient; +extern cvar_t r_reportsurfout; +extern cvar_t r_maxsurfs; +extern cvar_t r_numsurfs; +extern cvar_t r_reportedgeout; +extern cvar_t r_maxedges; +extern cvar_t r_numedges; + +#define XCENTERING (1.0 / 2.0) +#define YCENTERING (1.0 / 2.0) + +#define CLIP_EPSILON 0.001 + +#define BACKFACE_EPSILON 0.01 + +//=========================================================================== + +#define DIST_NOT_SET 98765 + +// !!! if this is changed, it must be changed in asm_draw.h too !!! +typedef struct clipplane_s +{ + vec3_t normal; + float dist; + struct clipplane_s *next; + byte leftedge; + byte rightedge; + byte reserved[2]; +} clipplane_t; + +extern clipplane_t view_clipplanes[4]; + +//============================================================================= + +void R_RenderWorld (void); + +//============================================================================= + +extern mplane_t screenedge[4]; + +extern vec3_t r_origin; + +extern vec3_t r_entorigin; + +extern float screenAspect; +extern float verticalFieldOfView; +extern float xOrigin, yOrigin; + +extern int r_visframecount; + +//============================================================================= + +extern int vstartscan; + + +void R_ClearPolyList (void); +void R_DrawPolyList (void); + +// +// current entity info +// +extern qboolean insubmodel; +extern vec3_t r_worldmodelorg; + + +void R_DrawSprite (void); +void R_RenderFace (msurface_t *fa, int clipflags); +void R_RenderPoly (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_SetSkyFrame (void); +void R_DrawSurfaceBlock16 (void); +void R_DrawSurfaceBlock8 (void); +texture_t *R_TextureAnimation (texture_t *base); + +#if id386 + +void R_DrawSurfaceBlock8_mip0 (void); +void R_DrawSurfaceBlock8_mip1 (void); +void R_DrawSurfaceBlock8_mip2 (void); +void R_DrawSurfaceBlock8_mip3 (void); + +#endif + +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); +void R_DrawSolidClippedSubmodelPolygons (model_t *pmodel); + +void R_AddPolygonEdges (emitpoint_t *pverts, int numverts, int miplevel); +surf_t *R_GetSurf (void); +void R_AliasDrawModel (alight_t *plighting); +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); + +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 model_t *cl_worldmodel; + +extern int *pfrustum_indexes[4]; + +// !!! if this is changed, it must be changed in asm_draw.h too !!! +#define NEAR_CLIP 0.01 + +extern int ubasestep, errorterm, erroradjustup, erroradjustdown; +extern int vstartscan; + +extern fixed16_t sadjust, tadjust; +extern fixed16_t bbextents, bbextentt; + +#define MAXBVERTINDEXES 1000 // new clipped vertices when clipping bmodels + // to the world BSP +extern mvertex_t *r_ptverts, *r_ptvertsmax; + +extern vec3_t sbaseaxis[3], tbaseaxis[3]; +extern float entity_rotation[3][3]; + +extern int reinit_surfcache; + +extern int r_currentkey; +extern int r_currentbkey; + +typedef struct btofpoly_s { + int clipflags; + msurface_t *psurf; +} btofpoly_t; + +#define MAX_BTOFPOLYS 5000 // FIXME: tune this + +extern int numbtofpolys; +extern btofpoly_t *pbtofpolys; + +void R_InitTurb (void); +void R_ZDrawSubmodelPolys (model_t *clmodel); + +//========================================================= +// Alias models +//========================================================= + +#define MAXALIASVERTS 2000 // TODO: tune this +#define ALIAS_Z_CLIP_PLANE 5 + +extern int numverts; +extern int a_skinwidth; +extern mtriangle_t *ptriangles; +extern int numtriangles; +extern aliashdr_t *paliashdr; +extern mdl_t *pmdl; +extern float leftclip, topclip, rightclip, bottomclip; +extern int r_acliptype; +extern finalvert_t *pfinalverts; +extern auxvert_t *pauxverts; + +qboolean R_AliasCheckBBox (void); + +//========================================================= +// turbulence stuff + +#define AMP 8*0x10000 +#define AMP2 3 +#define SPEED 20 + +//========================================================= +// particle stuff + +void R_DrawParticles (void); +void R_InitParticles (void); +void R_ClearParticles (void); +void R_ReadPointFile_f (void); +void R_SurfacePatch (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[MAXHEIGHT]; +extern edge_t *removeedges[MAXHEIGHT]; + +extern int screenwidth; + +// FIXME: make stack vars when debugging done +extern edge_t edge_head; +extern edge_t edge_tail; +extern edge_t edge_aftertail; +extern int r_bmodelactive; +extern vrect_t *pconupdate; + +extern float aliasxscale, aliasyscale, aliasxcenter, aliasycenter; +extern float r_aliastransition, r_resfudge; + +extern int r_outofsurfaces; +extern int r_outofedges; + +extern mvertex_t *r_pcurrentvertbase; +extern int r_maxvalidedgeoffset; + +void R_AliasClipTriangle (mtriangle_t *ptri); + +extern float r_time1; +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 cshift_t cshift_water; +extern qboolean r_dowarpold, r_viewchanged; + +extern mleaf_t *r_viewleaf, *r_oldviewleaf; + +extern vec3_t r_emins, r_emaxs; +extern mnode_t *r_pefragtopnode; +extern int r_clipflags; +extern int r_dlightframecount; +extern qboolean r_fov_greater_than_90; + +void R_StoreEfrags (efrag_t **ppefrag); +void R_TimeRefresh_f (void); +void R_TimeGraph (void); +void R_PrintAliasStats (void); +void R_PrintTimes (void); +void R_PrintDSpeeds (void); +void R_AnimateLight (void); +int R_LightPoint (vec3_t p); +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); +void R_MarkLights (dlight_t *light, int bit, mnode_t *node); + #endif //GLQUAKE \ No newline at end of file diff --git a/source/r_main.c b/source/r_main.c index 20111036..b1cacd68 100644 --- a/source/r_main.c +++ b/source/r_main.c @@ -1,1117 +1,1117 @@ -/* -Copyright (C) 1996-1997 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 "quakedef.h" -#include "r_local.h" -#include "sound.h" - -//define PASSAGES - -void *colormap; -vec3_t viewlightvec; -alight_t r_viewlighting = {128, 192, viewlightvec}; -float r_time1; -int r_numallocatededges; -qboolean r_drawpolys; -qboolean r_drawculledpolys; -qboolean r_worldpolysbacktofront; -qboolean r_recursiveaffinetriangles = true; -int r_pixbytes = 1; -float r_aliasuvscale = 1.0; -int r_outofsurfaces; -int r_outofedges; - -qboolean r_dowarp, r_dowarpold, r_viewchanged; - -int numbtofpolys; -btofpoly_t *pbtofpolys; -mvertex_t *r_pcurrentvertbase; - -int c_surf; -int r_maxsurfsseen, r_maxedgesseen, r_cnumsurfs; -qboolean r_surfsonstack; -int r_clipflags; - -byte *r_warpbuffer; - -byte *r_stack_start; - -qboolean r_fov_greater_than_90; - -entity_t r_worldentity; - -// -// view origin -// -vec3_t vup, base_vup; -vec3_t vpn, base_vpn; -vec3_t vright, base_vright; -vec3_t r_origin; - -// -// screen size info -// -refdef_t r_refdef; -float xcenter, ycenter; -float xscale, yscale; -float xscaleinv, yscaleinv; -float xscaleshrink, yscaleshrink; -float aliasxscale, aliasyscale, aliasxcenter, aliasycenter; - -int screenwidth; - -float pixelAspect; -float screenAspect; -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]; - -int reinit_surfcache = 1; // if 1, surface cache is currently empty and - // must be reinitialized for current cache size - -mleaf_t *r_viewleaf, *r_oldviewleaf; - -texture_t *r_notexture_mip; - -float r_aliastransition, r_resfudge; - -int d_lightstylevalue[256]; // 8.8 fraction of base light value - -float dp_time1, dp_time2, db_time1, db_time2, rw_time1, rw_time2; -float se_time1, se_time2, de_time1, de_time2, dv_time1, dv_time2; - -void R_MarkLeaves (void); - -cvar_t r_draworder = {"r_draworder","0"}; -cvar_t r_speeds = {"r_speeds","0"}; -cvar_t r_timegraph = {"r_timegraph","0"}; -cvar_t r_netgraph = {"r_netgraph","0"}; -cvar_t r_zgraph = {"r_zgraph","0"}; -cvar_t r_graphheight = {"r_graphheight","15"}; -cvar_t r_clearcolor = {"r_clearcolor","2"}; -cvar_t r_skycolor = {"r_skycolor","4"}; -cvar_t r_fastsky = {"r_fastsky","0"}; -cvar_t r_waterwarp = {"r_waterwarp","1"}; -cvar_t r_fullbright = {"r_fullbright","0"}; -cvar_t r_drawentities = {"r_drawentities","1"}; -cvar_t r_drawviewmodel = {"r_drawviewmodel","2"}; -cvar_t r_drawflame = {"r_drawflame","1"}; -cvar_t r_aliasstats = {"r_polymodelstats","0"}; -cvar_t r_dspeeds = {"r_dspeeds","0"}; -cvar_t r_drawflat = {"r_drawflat", "0"}; -cvar_t r_ambient = {"r_ambient", "0"}; -cvar_t r_reportsurfout = {"r_reportsurfout", "0"}; -cvar_t r_maxsurfs = {"r_maxsurfs", "0"}; -cvar_t r_numsurfs = {"r_numsurfs", "0"}; -cvar_t r_reportedgeout = {"r_reportedgeout", "0"}; -cvar_t r_maxedges = {"r_maxedges", "0"}; -cvar_t r_numedges = {"r_numedges", "0"}; -cvar_t r_aliastransbase = {"r_aliastransbase", "200"}; -cvar_t r_aliastransadj = {"r_aliastransadj", "100"}; -cvar_t r_fullbrightSkins = {"r_fullbrightSkins","0"}; - -extern cvar_t scr_fov; - -void CreatePassages (void); -void SetVisibilityByPassages (void); - -void R_NetGraph (void); -void R_ZGraph (void); - -/* -================== -R_InitTextures -================== -*/ -void R_InitTextures (void) -{ - int x,y, m; - byte *dest; - -// create a simple checkerboard texture for the default - r_notexture_mip = Hunk_AllocName (sizeof(texture_t) + 16*16+8*8+4*4+2*2, "notexture"); - - r_notexture_mip->width = r_notexture_mip->height = 16; - r_notexture_mip->offsets[0] = sizeof(texture_t); - r_notexture_mip->offsets[1] = r_notexture_mip->offsets[0] + 16*16; - r_notexture_mip->offsets[2] = r_notexture_mip->offsets[1] + 8*8; - r_notexture_mip->offsets[3] = r_notexture_mip->offsets[2] + 4*4; - - for (m=0 ; m<4 ; m++) - { - dest = (byte *)r_notexture_mip + r_notexture_mip->offsets[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_Init -=============== -*/ -void R_Init (void) -{ - int dummy; - -// get stack position so we can guess if we are going to overflow - r_stack_start = (byte *)&dummy; - - R_InitTurb (); - - Cmd_AddCommand ("timerefresh", R_TimeRefresh_f); - - Cvar_RegisterVariable (&r_draworder); - Cvar_RegisterVariable (&r_speeds); - Cvar_RegisterVariable (&r_timegraph); - Cvar_RegisterVariable (&r_netgraph); - Cvar_RegisterVariable (&r_zgraph); - Cvar_RegisterVariable (&r_graphheight); - Cvar_RegisterVariable (&r_drawflat); - Cvar_RegisterVariable (&r_ambient); - Cvar_RegisterVariable (&r_clearcolor); - Cvar_RegisterVariable (&r_skycolor); - Cvar_RegisterVariable (&r_fastsky); - Cvar_RegisterVariable (&r_waterwarp); - Cvar_RegisterVariable (&r_fullbright); - Cvar_RegisterVariable (&r_drawentities); - Cvar_RegisterVariable (&r_drawviewmodel); - Cvar_RegisterVariable (&r_drawflame); - Cvar_RegisterVariable (&r_aliasstats); - Cvar_RegisterVariable (&r_dspeeds); - Cvar_RegisterVariable (&r_reportsurfout); - Cvar_RegisterVariable (&r_maxsurfs); - Cvar_RegisterVariable (&r_numsurfs); - Cvar_RegisterVariable (&r_reportedgeout); - Cvar_RegisterVariable (&r_maxedges); - Cvar_RegisterVariable (&r_numedges); - Cvar_RegisterVariable (&r_aliastransbase); - Cvar_RegisterVariable (&r_aliastransadj); - Cvar_RegisterVariable (&r_fullbrightSkins); - - Cvar_SetValue (&r_maxedges, (float)NUMSTACKEDGES); - Cvar_SetValue (&r_maxsurfs, (float)NUMSTACKSURFACES); - - 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_InitParticles (); - -// TODO: collect 386-specific code in one place -#if id386 - Sys_MakeCodeWriteable ((long)R_EdgeCodeStart, - (long)R_EdgeCodeEnd - (long)R_EdgeCodeStart); -#endif // id386 - - D_Init (); -} - -/* -=============== -R_NewMap -=============== -*/ -void R_NewMap (void) -{ - int i; - - memset (&r_worldentity, 0, sizeof(r_worldentity)); - r_worldentity.model = cl.worldmodel; - -// clear out efrags in case the level hasn't been reloaded -// FIXME: is this one short? - for (i=0 ; inumleafs ; i++) - cl.worldmodel->leafs[i].efrags = NULL; - - r_viewleaf = NULL; - R_ClearParticles (); - - r_cnumsurfs = r_maxsurfs.value; - - if (r_cnumsurfs <= MINSURFACES) - r_cnumsurfs = MINSURFACES; - - if (r_cnumsurfs > NUMSTACKSURFACES) - { - surfaces = Hunk_AllocName (r_cnumsurfs * sizeof(surf_t), "surfaces"); - 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--; - R_SurfacePatch (); - } - else - { - r_surfsonstack = true; - } - - r_maxedgesseen = 0; - r_maxsurfsseen = 0; - - r_numallocatededges = r_maxedges.value; - - if (r_numallocatededges < MINEDGES) - r_numallocatededges = MINEDGES; - - if (r_numallocatededges <= NUMSTACKEDGES) - { - auxedges = NULL; - } - else - { - auxedges = Hunk_AllocName (r_numallocatededges * sizeof(edge_t), - "edges"); - } - - r_dowarpold = false; - r_viewchanged = false; -} - - -/* -=============== -R_SetVrect -=============== -*/ -void R_SetVrect (vrect_t *pvrectin, vrect_t *pvrect, int lineadj) -{ - int h; - float size; - qboolean full = false; - - if (scr_viewsize.value >= 100.0) { - size = 100.0; - full = true; - } else - size = scr_viewsize.value; - - if (cl.intermission) - { - full = true; - size = 100.0; - lineadj = 0; - } - size /= 100.0; - - if (!cl_sbar.value && full) - h = pvrectin->height; - else - h = pvrectin->height - lineadj; - -// h = (!cl_sbar.value && size==1.0) ? pvrectin->height : (pvrectin->height - lineadj); -// h = pvrectin->height - lineadj; - if (full) - pvrect->width = pvrectin->width; - else - pvrect->width = pvrectin->width * size; - if (pvrect->width < 96) - { - size = 96.0 / pvrectin->width; - pvrect->width = 96; // min for icons - } - pvrect->width &= ~7; - pvrect->height = pvrectin->height * size; - if (cl_sbar.value || !full) { - if (pvrect->height > pvrectin->height - lineadj) - pvrect->height = pvrectin->height - lineadj; - } else - if (pvrect->height > pvrectin->height) - pvrect->height = pvrectin->height; - - pvrect->height &= ~1; - - pvrect->x = (pvrectin->width - pvrect->width)/2; - if (full) - pvrect->y = 0; - else - pvrect->y = (h - pvrect->height)/2; -} - - -/* -=============== -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 *pvrect, int lineadj, float aspect) -{ - int i; - float res_scale; - - r_viewchanged = true; - - R_SetVrect (pvrect, &r_refdef.vrect, lineadj); - - r_refdef.horizontalFieldOfView = 2.0 * tan (r_refdef.fov_x/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<<20) + (1<<19) - 1; - r_refdef.fvrecty = (float)r_refdef.vrect.y; - r_refdef.fvrecty_adj = (float)r_refdef.vrect.y - 0.5; - r_refdef.vrectright = r_refdef.vrect.x + r_refdef.vrect.width; - r_refdef.vrectright_adj_shift20 = (r_refdef.vrectright<<20) + (1<<19) - 1; - r_refdef.fvrectright = (float)r_refdef.vrectright; - r_refdef.fvrectright_adj = (float)r_refdef.vrectright - 0.5; - r_refdef.vrectrightedge = (float)r_refdef.vrectright - 0.99; - r_refdef.vrectbottom = r_refdef.vrect.y + r_refdef.vrect.height; - r_refdef.fvrectbottom = (float)r_refdef.vrectbottom; - r_refdef.fvrectbottom_adj = (float)r_refdef.vrectbottom - 0.5; - - r_refdef.aliasvrect.x = (int)(r_refdef.vrect.x * r_aliasuvscale); - r_refdef.aliasvrect.y = (int)(r_refdef.vrect.y * r_aliasuvscale); - r_refdef.aliasvrect.width = (int)(r_refdef.vrect.width * r_aliasuvscale); - r_refdef.aliasvrect.height = (int)(r_refdef.vrect.height * r_aliasuvscale); - r_refdef.aliasvrectright = r_refdef.aliasvrect.x + - r_refdef.aliasvrect.width; - r_refdef.aliasvrectbottom = r_refdef.aliasvrect.y + - r_refdef.aliasvrect.height; - - pixelAspect = aspect; - xOrigin = r_refdef.xOrigin; - yOrigin = r_refdef.yOrigin; - - screenAspect = r_refdef.vrect.width*pixelAspect / - r_refdef.vrect.height; -// 320*200 1.0 pixelAspect = 1.6 screenAspect -// 320*240 1.0 pixelAspect = 1.3333 screenAspect -// proper 320*200 pixelAspect = 0.8333333 - - verticalFieldOfView = r_refdef.horizontalFieldOfView / screenAspect; - -// values for perspective projection -// if math were exact, the values would range from 0.5 to to range+0.5 -// hopefully they wll be in the 0.000001 to range+.999999 and truncate -// the polygon rasterization will never render in the first row or column -// but will definately render in the [range] row and column, so adjust the -// buffer origin to get an exact edge to edge fill - xcenter = ((float)r_refdef.vrect.width * XCENTERING) + - r_refdef.vrect.x - 0.5; - aliasxcenter = xcenter * r_aliasuvscale; - ycenter = ((float)r_refdef.vrect.height * YCENTERING) + - r_refdef.vrect.y - 0.5; - aliasycenter = ycenter * r_aliasuvscale; - - xscale = r_refdef.vrect.width / r_refdef.horizontalFieldOfView; - aliasxscale = xscale * r_aliasuvscale; - xscaleinv = 1.0 / xscale; - yscale = xscale * pixelAspect; - aliasyscale = yscale * r_aliasuvscale; - yscaleinv = 1.0 / yscale; - xscaleshrink = (r_refdef.vrect.width-6)/r_refdef.horizontalFieldOfView; - yscaleshrink = xscaleshrink*pixelAspect; - -// left side clip - screenedge[0].normal[0] = -1.0 / (xOrigin*r_refdef.horizontalFieldOfView); - screenedge[0].normal[1] = 0; - screenedge[0].normal[2] = 1; - screenedge[0].type = PLANE_ANYZ; - -// right side clip - screenedge[1].normal[0] = - 1.0 / ((1.0-xOrigin)*r_refdef.horizontalFieldOfView); - screenedge[1].normal[1] = 0; - screenedge[1].normal[2] = 1; - screenedge[1].type = PLANE_ANYZ; - -// top side clip - screenedge[2].normal[0] = 0; - screenedge[2].normal[1] = -1.0 / (yOrigin*verticalFieldOfView); - screenedge[2].normal[2] = 1; - screenedge[2].type = PLANE_ANYZ; - -// bottom side clip - screenedge[3].normal[0] = 0; - screenedge[3].normal[1] = 1.0 / ((1.0-yOrigin)*verticalFieldOfView); - screenedge[3].normal[2] = 1; - screenedge[3].type = PLANE_ANYZ; - - for (i=0 ; i<4 ; i++) - VectorNormalize (screenedge[i].normal); - - res_scale = sqrt ((double)(r_refdef.vrect.width * r_refdef.vrect.height) / - (320.0 * 152.0)) * - (2.0 / r_refdef.horizontalFieldOfView); - r_aliastransition = r_aliastransbase.value * res_scale; - r_resfudge = r_aliastransadj.value * res_scale; - - if (scr_fov.value <= 90.0) - r_fov_greater_than_90 = false; - else - r_fov_greater_than_90 = true; - -// TODO: collect 386-specific code in one place -#if id386 - if (r_pixbytes == 1) - { - Sys_MakeCodeWriteable ((long)R_Surf8Start, - (long)R_Surf8End - (long)R_Surf8Start); - colormap = vid.colormap; - R_Surf8Patch (); - } - else - { - Sys_MakeCodeWriteable ((long)R_Surf16Start, - (long)R_Surf16End - (long)R_Surf16Start); - colormap = vid.colormap16; - R_Surf16Patch (); - } -#endif // id386 - - D_ViewChanged (); -} - - -/* -=============== -R_MarkLeaves -=============== -*/ -void R_MarkLeaves (void) -{ - byte *vis; - mnode_t *node; - int i; - - if (r_oldviewleaf == r_viewleaf) - return; - - r_visframecount++; - r_oldviewleaf = r_viewleaf; - - vis = Mod_LeafPVS (r_viewleaf, cl.worldmodel); - - for (i=0 ; inumleafs ; i++) - { - if (vis[i>>3] & (1<<(i&7))) - { - node = (mnode_t *)&cl.worldmodel->leafs[i+1]; - do - { - if (node->visframe == r_visframecount) - break; - node->visframe = r_visframecount; - node = node->parent; - } while (node); - } - } -} - - -/* -============= -R_DrawEntitiesOnList -============= -*/ -void R_DrawEntitiesOnList (void) -{ - int i, j; - int lnum; - alight_t lighting; -// FIXME: remove and do real lighting - float lightvec[3] = {-1, 0, 0}; - vec3_t dist; - float add; - - if (!r_drawentities.value) - return; - - for (i=0 ; imodel->type) - { - case mod_sprite: - VectorCopy (currententity->origin, r_entorigin); - VectorSubtract (r_origin, r_entorigin, modelorg); - R_DrawSprite (); - break; - - case mod_alias: - VectorCopy (currententity->origin, r_entorigin); - VectorSubtract (r_origin, r_entorigin, modelorg); - - // see if the bounding box lets us trivially reject, also sets - // trivial accept status - if (R_AliasCheckBBox ()) - { - j = R_LightPoint (currententity->origin); - - lighting.ambientlight = j; - lighting.shadelight = j; - - lighting.plightvec = lightvec; - - for (lnum=0 ; lnum= cl.time) - { - VectorSubtract (currententity->origin, - cl_dlights[lnum].origin, - dist); - add = cl_dlights[lnum].radius - Length(dist); - - if (add > 0) - lighting.ambientlight += add; - } - } - - // 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; - - if (r_fullbrightSkins.value && currententity->model->modhint == MOD_PLAYER - && !cl.teamfortress) - lighting.ambientlight = lighting.shadelight = 128; - - R_AliasDrawModel (&lighting); - } - - break; - - default: - break; - } - } -} - -/* -============= -R_DrawViewModel -============= -*/ -void R_DrawViewModel (void) -{ -// FIXME: remove and do real lighting - float lightvec[3] = {-1, 0, 0}; - int j; - int lnum; - vec3_t dist; - float add; - dlight_t *dl; - - if (!r_drawviewmodel.value || (r_fov_greater_than_90 && r_drawviewmodel.value == 2) - || !Cam_DrawViewModel()) - return; - - if (cl.stats[STAT_ITEMS] & IT_INVISIBILITY) - return; - - if (cl.stats[STAT_HEALTH] <= 0) - return; - - currententity = &cl.viewent; - if (!currententity->model) - return; - - VectorCopy (currententity->origin, r_entorigin); - VectorSubtract (r_origin, r_entorigin, modelorg); - - VectorCopy (vup, viewlightvec); - VectorInverse (viewlightvec); - - j = R_LightPoint (currententity->origin); - - if (j < 24) - j = 24; // always give some light on gun - r_viewlighting.ambientlight = j; - r_viewlighting.shadelight = j; - -// add dynamic lights - for (lnum=0 ; lnumradius) - continue; - if (!dl->radius) - continue; - if (dl->die < cl.time) - continue; - - VectorSubtract (currententity->origin, dl->origin, dist); - add = dl->radius - Length(dist); - if (add > 0) - r_viewlighting.ambientlight += add; - } - -// clamp lighting so it doesn't overbright as much - if (r_viewlighting.ambientlight > 128) - r_viewlighting.ambientlight = 128; - if (r_viewlighting.ambientlight + r_viewlighting.shadelight > 192) - r_viewlighting.shadelight = 192 - r_viewlighting.ambientlight; - - r_viewlighting.plightvec = lightvec; - - R_AliasDrawModel (&r_viewlighting); -} - - -/* -============= -R_BmodelCheckBBox -============= -*/ -int R_BmodelCheckBBox (model_t *clmodel, float *minmaxs) -{ - int i, *pindex, clipflags; - vec3_t acceptpt, rejectpt; - double d; - - clipflags = 0; - - if (currententity->angles[0] || currententity->angles[1] - || currententity->angles[2]) - { - for (i=0 ; i<4 ; i++) - { - d = DotProduct (currententity->origin, view_clipplanes[i].normal); - d -= view_clipplanes[i].dist; - - if (d <= -clmodel->radius) - return BMODEL_FULLY_CLIPPED; - - if (d <= clmodel->radius) - clipflags |= (1<model->type) - { - case mod_brush: - - clmodel = currententity->model; - - // see if the bounding box lets us trivially reject, also sets - // trivial accept status - for (j=0 ; j<3 ; j++) - { - minmaxs[j] = currententity->origin[j] + - clmodel->mins[j]; - minmaxs[3+j] = currententity->origin[j] + - clmodel->maxs[j]; - } - - clipflags = R_BmodelCheckBBox (clmodel, minmaxs); - - if (clipflags != BMODEL_FULLY_CLIPPED) - { - VectorCopy (currententity->origin, r_entorigin); - VectorSubtract (r_origin, r_entorigin, modelorg); - // FIXME: is this needed? - VectorCopy (modelorg, r_worldmodelorg); - - r_pcurrentvertbase = clmodel->vertexes; - - // FIXME: stop transforming twice - R_RotateBmodel (); - - // calculate dynamic lighting for bmodel if it's not an - // instanced model - if (clmodel->firstmodelsurface != 0) - { - for (k=0 ; knodes + clmodel->hulls[0].firstclipnode); - } - } - - // if the driver wants polygons, deliver those. Z-buffering is on - // at this point, so no clipping to the world tree is needed, just - // frustum clipping - if (r_drawpolys | r_drawculledpolys) - { - R_ZDrawSubmodelPolys (clmodel); - } - else - { - r_pefragtopnode = NULL; - - for (j=0 ; j<3 ; j++) - { - r_emins[j] = minmaxs[j]; - r_emaxs[j] = minmaxs[3+j]; - } - - R_SplitEntityOnNode2 (cl.worldmodel->nodes); - - if (r_pefragtopnode) - { - currententity->topnode = r_pefragtopnode; - - if (r_pefragtopnode->contents >= 0) - { - // not a leaf; has to be clipped to the world BSP - r_clipflags = clipflags; - R_DrawSolidClippedSubmodelPolygons (clmodel); - } - 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 (clmodel, clipflags); - } - - currententity->topnode = NULL; - } - } - - // 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 (base_modelorg, modelorg); - VectorCopy (oldorigin, modelorg); - R_TransformFrustum (); - } - - break; - - default: - break; - } - } - - insubmodel = false; -} - - -/* -================ -R_EdgeDrawing -================ -*/ -void R_EdgeDrawing (void) -{ - edge_t ledges[NUMSTACKEDGES + - ((CACHE_SIZE - 1) / sizeof(edge_t)) + 1]; - surf_t lsurfs[NUMSTACKSURFACES + - ((CACHE_SIZE - 1) / sizeof(surf_t)) + 1]; - - if (auxedges) - { - r_edges = auxedges; - } - else - { - r_edges = (edge_t *) - (((long)&ledges[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1)); - } - - if (r_surfsonstack) - { - surfaces = (surf_t *) - (((long)&lsurfs[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1)); - 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_SurfacePatch (); - } - - R_BeginEdgeFrame (); - - if (r_dspeeds.value) - { - rw_time1 = Sys_DoubleTime (); - } - - R_RenderWorld (); - - if (r_drawculledpolys) - R_ScanEdges (); - -// only the world can be drawn back to front with no z reads or compares, just -// z writes, so have the driver turn z compares on now - D_TurnZOn (); - - if (r_dspeeds.value) - { - rw_time2 = Sys_DoubleTime (); - db_time1 = rw_time2; - } - - R_DrawBEntitiesOnList (); - - if (r_dspeeds.value) - { - db_time2 = Sys_DoubleTime (); - se_time1 = db_time2; - } - - if (!r_dspeeds.value) - { - VID_UnlockBuffer (); - S_ExtraUpdate (); // don't let sound get messed up if going slow - VID_LockBuffer (); - } - - if (!(r_drawpolys | r_drawculledpolys)) - R_ScanEdges (); -} - - -/* -================ -R_RenderView - -r_refdef must be set before the first call -================ -*/ -void R_RenderView_ (void) -{ - byte warpbuffer[WARP_WIDTH * WARP_HEIGHT]; - - r_warpbuffer = warpbuffer; - - if (r_timegraph.value || r_speeds.value || r_dspeeds.value) - r_time1 = Sys_DoubleTime (); - - R_SetupFrame (); - -#ifdef PASSAGES -SetVisibilityByPassages (); -#else - R_MarkLeaves (); // done here so we know if we're in water -#endif - -// make FDIV fast. This reduces timing precision after we've been running for a -// while, so we don't do it globally. This also sets chop mode, and we do it -// here so that setup stuff like the refresh area calculations match what's -// done in screen.c - Sys_LowFPPrecision (); - - if (!r_worldentity.model || !cl.worldmodel) - Sys_Error ("R_RenderView: NULL worldmodel"); - - if (!r_dspeeds.value) - { - VID_UnlockBuffer (); - S_ExtraUpdate (); // don't let sound get messed up if going slow - VID_LockBuffer (); - } - - R_EdgeDrawing (); - - if (!r_dspeeds.value) - { - VID_UnlockBuffer (); - S_ExtraUpdate (); // don't let sound get messed up if going slow - VID_LockBuffer (); - } - - if (r_dspeeds.value) - { - se_time2 = Sys_DoubleTime (); - de_time1 = se_time2; - } - - R_DrawEntitiesOnList (); - - if (r_dspeeds.value) - { - de_time2 = Sys_DoubleTime (); - dv_time1 = de_time2; - } - - R_DrawViewModel (); - - if (r_dspeeds.value) - { - dv_time2 = Sys_DoubleTime (); - dp_time1 = Sys_DoubleTime (); - } - - R_DrawParticles (); - - if (r_dspeeds.value) - dp_time2 = Sys_DoubleTime (); - - if (r_dowarp) - D_WarpScreen (); - - V_SetContentsColor (r_viewleaf->contents); - - if (r_timegraph.value) - R_TimeGraph (); - - if (r_netgraph.value) - R_NetGraph (); - - if (r_zgraph.value) - R_ZGraph (); - - if (r_aliasstats.value) - R_PrintAliasStats (); - - if (r_speeds.value) - R_PrintTimes (); - - if (r_dspeeds.value) - R_PrintDSpeeds (); - - if (r_reportsurfout.value && r_outofsurfaces) - Con_Printf ("Short %d surfaces\n", r_outofsurfaces); - - if (r_reportedgeout.value && r_outofedges) - Con_Printf ("Short roughly %d edges\n", r_outofedges * 2 / 3); - -// back to high floating-point precision - Sys_HighFPPrecision (); -} - -void R_RenderView (void) -{ - int dummy; - int delta; - - delta = (byte *)&dummy - r_stack_start; - if (delta < -10000 || delta > 10000) - Sys_Error ("R_RenderView: called without enough stack"); - - if ( Hunk_LowMark() & 3 ) - Sys_Error ("Hunk is missaligned"); - - if ( (long)(&dummy) & 3 ) - Sys_Error ("Stack is missaligned"); - - if ( (long)(&r_warpbuffer) & 3 ) - Sys_Error ("Globals are missaligned"); - - R_RenderView_ (); -} - -/* -================ -R_InitTurb -================ -*/ -void R_InitTurb (void) -{ - int i; - - for (i=0 ; i<1280 ; i++) - { - sintable[i] = AMP + sin(i*3.14159*2/CYCLE)*AMP; - intsintable[i] = AMP2 + sin(i*3.14159*2/CYCLE)*AMP2; // AMP2, not 20 - } -} - +/* +Copyright (C) 1996-1997 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 "quakedef.h" +#include "r_local.h" +#include "sound.h" + +//define PASSAGES + +void *colormap; +vec3_t viewlightvec; +alight_t r_viewlighting = {128, 192, viewlightvec}; +float r_time1; +int r_numallocatededges; +qboolean r_drawpolys; +qboolean r_drawculledpolys; +qboolean r_worldpolysbacktofront; +qboolean r_recursiveaffinetriangles = true; +int r_pixbytes = 1; +float r_aliasuvscale = 1.0; +int r_outofsurfaces; +int r_outofedges; + +qboolean r_dowarp, r_dowarpold, r_viewchanged; + +int numbtofpolys; +btofpoly_t *pbtofpolys; +mvertex_t *r_pcurrentvertbase; + +int c_surf; +int r_maxsurfsseen, r_maxedgesseen, r_cnumsurfs; +qboolean r_surfsonstack; +int r_clipflags; + +byte *r_warpbuffer; + +byte *r_stack_start; + +qboolean r_fov_greater_than_90; + +entity_t r_worldentity; + +// +// view origin +// +vec3_t vup, base_vup; +vec3_t vpn, base_vpn; +vec3_t vright, base_vright; +vec3_t r_origin; + +// +// screen size info +// +refdef_t r_refdef; +float xcenter, ycenter; +float xscale, yscale; +float xscaleinv, yscaleinv; +float xscaleshrink, yscaleshrink; +float aliasxscale, aliasyscale, aliasxcenter, aliasycenter; + +int screenwidth; + +float pixelAspect; +float screenAspect; +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]; + +int reinit_surfcache = 1; // if 1, surface cache is currently empty and + // must be reinitialized for current cache size + +mleaf_t *r_viewleaf, *r_oldviewleaf; + +texture_t *r_notexture_mip; + +float r_aliastransition, r_resfudge; + +int d_lightstylevalue[256]; // 8.8 fraction of base light value + +float dp_time1, dp_time2, db_time1, db_time2, rw_time1, rw_time2; +float se_time1, se_time2, de_time1, de_time2, dv_time1, dv_time2; + +void R_MarkLeaves (void); + +cvar_t r_draworder = {"r_draworder","0"}; +cvar_t r_speeds = {"r_speeds","0"}; +cvar_t r_timegraph = {"r_timegraph","0"}; +cvar_t r_netgraph = {"r_netgraph","0"}; +cvar_t r_zgraph = {"r_zgraph","0"}; +cvar_t r_graphheight = {"r_graphheight","15"}; +cvar_t r_clearcolor = {"r_clearcolor","2"}; +cvar_t r_skycolor = {"r_skycolor","4"}; +cvar_t r_fastsky = {"r_fastsky","0"}; +cvar_t r_waterwarp = {"r_waterwarp","1"}; +cvar_t r_fullbright = {"r_fullbright","0"}; +cvar_t r_drawentities = {"r_drawentities","1"}; +cvar_t r_drawviewmodel = {"r_drawviewmodel","2"}; +cvar_t r_drawflame = {"r_drawflame","1"}; +cvar_t r_aliasstats = {"r_polymodelstats","0"}; +cvar_t r_dspeeds = {"r_dspeeds","0"}; +cvar_t r_drawflat = {"r_drawflat", "0"}; +cvar_t r_ambient = {"r_ambient", "0"}; +cvar_t r_reportsurfout = {"r_reportsurfout", "0"}; +cvar_t r_maxsurfs = {"r_maxsurfs", "0"}; +cvar_t r_numsurfs = {"r_numsurfs", "0"}; +cvar_t r_reportedgeout = {"r_reportedgeout", "0"}; +cvar_t r_maxedges = {"r_maxedges", "0"}; +cvar_t r_numedges = {"r_numedges", "0"}; +cvar_t r_aliastransbase = {"r_aliastransbase", "200"}; +cvar_t r_aliastransadj = {"r_aliastransadj", "100"}; +cvar_t r_fullbrightSkins = {"r_fullbrightSkins","0"}; + +extern cvar_t scr_fov; + +void CreatePassages (void); +void SetVisibilityByPassages (void); + +void R_NetGraph (void); +void R_ZGraph (void); + +/* +================== +R_InitTextures +================== +*/ +void R_InitTextures (void) +{ + int x,y, m; + byte *dest; + +// create a simple checkerboard texture for the default + r_notexture_mip = Hunk_AllocName (sizeof(texture_t) + 16*16+8*8+4*4+2*2, "notexture"); + + r_notexture_mip->width = r_notexture_mip->height = 16; + r_notexture_mip->offsets[0] = sizeof(texture_t); + r_notexture_mip->offsets[1] = r_notexture_mip->offsets[0] + 16*16; + r_notexture_mip->offsets[2] = r_notexture_mip->offsets[1] + 8*8; + r_notexture_mip->offsets[3] = r_notexture_mip->offsets[2] + 4*4; + + for (m=0 ; m<4 ; m++) + { + dest = (byte *)r_notexture_mip + r_notexture_mip->offsets[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_Init +=============== +*/ +void R_Init (void) +{ + int dummy; + +// get stack position so we can guess if we are going to overflow + r_stack_start = (byte *)&dummy; + + R_InitTurb (); + + Cmd_AddCommand ("timerefresh", R_TimeRefresh_f); + + Cvar_RegisterVariable (&r_draworder); + Cvar_RegisterVariable (&r_speeds); + Cvar_RegisterVariable (&r_timegraph); + Cvar_RegisterVariable (&r_netgraph); + Cvar_RegisterVariable (&r_zgraph); + Cvar_RegisterVariable (&r_graphheight); + Cvar_RegisterVariable (&r_drawflat); + Cvar_RegisterVariable (&r_ambient); + Cvar_RegisterVariable (&r_clearcolor); + Cvar_RegisterVariable (&r_skycolor); + Cvar_RegisterVariable (&r_fastsky); + Cvar_RegisterVariable (&r_waterwarp); + Cvar_RegisterVariable (&r_fullbright); + Cvar_RegisterVariable (&r_drawentities); + Cvar_RegisterVariable (&r_drawviewmodel); + Cvar_RegisterVariable (&r_drawflame); + Cvar_RegisterVariable (&r_aliasstats); + Cvar_RegisterVariable (&r_dspeeds); + Cvar_RegisterVariable (&r_reportsurfout); + Cvar_RegisterVariable (&r_maxsurfs); + Cvar_RegisterVariable (&r_numsurfs); + Cvar_RegisterVariable (&r_reportedgeout); + Cvar_RegisterVariable (&r_maxedges); + Cvar_RegisterVariable (&r_numedges); + Cvar_RegisterVariable (&r_aliastransbase); + Cvar_RegisterVariable (&r_aliastransadj); + Cvar_RegisterVariable (&r_fullbrightSkins); + + Cvar_SetValue (&r_maxedges, (float)NUMSTACKEDGES); + Cvar_SetValue (&r_maxsurfs, (float)NUMSTACKSURFACES); + + 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_InitParticles (); + +// TODO: collect 386-specific code in one place +#if id386 + Sys_MakeCodeWriteable ((long)R_EdgeCodeStart, + (long)R_EdgeCodeEnd - (long)R_EdgeCodeStart); +#endif // id386 + + D_Init (); +} + +/* +=============== +R_NewMap +=============== +*/ +void R_NewMap (void) +{ + int i; + + memset (&r_worldentity, 0, sizeof(r_worldentity)); + r_worldentity.model = cl.worldmodel; + +// clear out efrags in case the level hasn't been reloaded +// FIXME: is this one short? + for (i=0 ; inumleafs ; i++) + cl.worldmodel->leafs[i].efrags = NULL; + + r_viewleaf = NULL; + R_ClearParticles (); + + r_cnumsurfs = r_maxsurfs.value; + + if (r_cnumsurfs <= MINSURFACES) + r_cnumsurfs = MINSURFACES; + + if (r_cnumsurfs > NUMSTACKSURFACES) + { + surfaces = Hunk_AllocName (r_cnumsurfs * sizeof(surf_t), "surfaces"); + 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--; + R_SurfacePatch (); + } + else + { + r_surfsonstack = true; + } + + r_maxedgesseen = 0; + r_maxsurfsseen = 0; + + r_numallocatededges = r_maxedges.value; + + if (r_numallocatededges < MINEDGES) + r_numallocatededges = MINEDGES; + + if (r_numallocatededges <= NUMSTACKEDGES) + { + auxedges = NULL; + } + else + { + auxedges = Hunk_AllocName (r_numallocatededges * sizeof(edge_t), + "edges"); + } + + r_dowarpold = false; + r_viewchanged = false; +} + + +/* +=============== +R_SetVrect +=============== +*/ +void R_SetVrect (vrect_t *pvrectin, vrect_t *pvrect, int lineadj) +{ + int h; + float size; + qboolean full = false; + + if (scr_viewsize.value >= 100.0) { + size = 100.0; + full = true; + } else + size = scr_viewsize.value; + + if (cl.intermission) + { + full = true; + size = 100.0; + lineadj = 0; + } + size /= 100.0; + + if (!cl_sbar.value && full) + h = pvrectin->height; + else + h = pvrectin->height - lineadj; + +// h = (!cl_sbar.value && size==1.0) ? pvrectin->height : (pvrectin->height - lineadj); +// h = pvrectin->height - lineadj; + if (full) + pvrect->width = pvrectin->width; + else + pvrect->width = pvrectin->width * size; + if (pvrect->width < 96) + { + size = 96.0 / pvrectin->width; + pvrect->width = 96; // min for icons + } + pvrect->width &= ~7; + pvrect->height = pvrectin->height * size; + if (cl_sbar.value || !full) { + if (pvrect->height > pvrectin->height - lineadj) + pvrect->height = pvrectin->height - lineadj; + } else + if (pvrect->height > pvrectin->height) + pvrect->height = pvrectin->height; + + pvrect->height &= ~1; + + pvrect->x = (pvrectin->width - pvrect->width)/2; + if (full) + pvrect->y = 0; + else + pvrect->y = (h - pvrect->height)/2; +} + + +/* +=============== +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 *pvrect, int lineadj, float aspect) +{ + int i; + float res_scale; + + r_viewchanged = true; + + R_SetVrect (pvrect, &r_refdef.vrect, lineadj); + + r_refdef.horizontalFieldOfView = 2.0 * tan (r_refdef.fov_x/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<<20) + (1<<19) - 1; + r_refdef.fvrecty = (float)r_refdef.vrect.y; + r_refdef.fvrecty_adj = (float)r_refdef.vrect.y - 0.5; + r_refdef.vrectright = r_refdef.vrect.x + r_refdef.vrect.width; + r_refdef.vrectright_adj_shift20 = (r_refdef.vrectright<<20) + (1<<19) - 1; + r_refdef.fvrectright = (float)r_refdef.vrectright; + r_refdef.fvrectright_adj = (float)r_refdef.vrectright - 0.5; + r_refdef.vrectrightedge = (float)r_refdef.vrectright - 0.99; + r_refdef.vrectbottom = r_refdef.vrect.y + r_refdef.vrect.height; + r_refdef.fvrectbottom = (float)r_refdef.vrectbottom; + r_refdef.fvrectbottom_adj = (float)r_refdef.vrectbottom - 0.5; + + r_refdef.aliasvrect.x = (int)(r_refdef.vrect.x * r_aliasuvscale); + r_refdef.aliasvrect.y = (int)(r_refdef.vrect.y * r_aliasuvscale); + r_refdef.aliasvrect.width = (int)(r_refdef.vrect.width * r_aliasuvscale); + r_refdef.aliasvrect.height = (int)(r_refdef.vrect.height * r_aliasuvscale); + r_refdef.aliasvrectright = r_refdef.aliasvrect.x + + r_refdef.aliasvrect.width; + r_refdef.aliasvrectbottom = r_refdef.aliasvrect.y + + r_refdef.aliasvrect.height; + + pixelAspect = aspect; + xOrigin = r_refdef.xOrigin; + yOrigin = r_refdef.yOrigin; + + screenAspect = r_refdef.vrect.width*pixelAspect / + r_refdef.vrect.height; +// 320*200 1.0 pixelAspect = 1.6 screenAspect +// 320*240 1.0 pixelAspect = 1.3333 screenAspect +// proper 320*200 pixelAspect = 0.8333333 + + verticalFieldOfView = r_refdef.horizontalFieldOfView / screenAspect; + +// values for perspective projection +// if math were exact, the values would range from 0.5 to to range+0.5 +// hopefully they wll be in the 0.000001 to range+.999999 and truncate +// the polygon rasterization will never render in the first row or column +// but will definately render in the [range] row and column, so adjust the +// buffer origin to get an exact edge to edge fill + xcenter = ((float)r_refdef.vrect.width * XCENTERING) + + r_refdef.vrect.x - 0.5; + aliasxcenter = xcenter * r_aliasuvscale; + ycenter = ((float)r_refdef.vrect.height * YCENTERING) + + r_refdef.vrect.y - 0.5; + aliasycenter = ycenter * r_aliasuvscale; + + xscale = r_refdef.vrect.width / r_refdef.horizontalFieldOfView; + aliasxscale = xscale * r_aliasuvscale; + xscaleinv = 1.0 / xscale; + yscale = xscale * pixelAspect; + aliasyscale = yscale * r_aliasuvscale; + yscaleinv = 1.0 / yscale; + xscaleshrink = (r_refdef.vrect.width-6)/r_refdef.horizontalFieldOfView; + yscaleshrink = xscaleshrink*pixelAspect; + +// left side clip + screenedge[0].normal[0] = -1.0 / (xOrigin*r_refdef.horizontalFieldOfView); + screenedge[0].normal[1] = 0; + screenedge[0].normal[2] = 1; + screenedge[0].type = PLANE_ANYZ; + +// right side clip + screenedge[1].normal[0] = + 1.0 / ((1.0-xOrigin)*r_refdef.horizontalFieldOfView); + screenedge[1].normal[1] = 0; + screenedge[1].normal[2] = 1; + screenedge[1].type = PLANE_ANYZ; + +// top side clip + screenedge[2].normal[0] = 0; + screenedge[2].normal[1] = -1.0 / (yOrigin*verticalFieldOfView); + screenedge[2].normal[2] = 1; + screenedge[2].type = PLANE_ANYZ; + +// bottom side clip + screenedge[3].normal[0] = 0; + screenedge[3].normal[1] = 1.0 / ((1.0-yOrigin)*verticalFieldOfView); + screenedge[3].normal[2] = 1; + screenedge[3].type = PLANE_ANYZ; + + for (i=0 ; i<4 ; i++) + VectorNormalize (screenedge[i].normal); + + res_scale = sqrt ((double)(r_refdef.vrect.width * r_refdef.vrect.height) / + (320.0 * 152.0)) * + (2.0 / r_refdef.horizontalFieldOfView); + r_aliastransition = r_aliastransbase.value * res_scale; + r_resfudge = r_aliastransadj.value * res_scale; + + if (scr_fov.value <= 90.0) + r_fov_greater_than_90 = false; + else + r_fov_greater_than_90 = true; + +// TODO: collect 386-specific code in one place +#if id386 + if (r_pixbytes == 1) + { + Sys_MakeCodeWriteable ((long)R_Surf8Start, + (long)R_Surf8End - (long)R_Surf8Start); + colormap = vid.colormap; + R_Surf8Patch (); + } + else + { + Sys_MakeCodeWriteable ((long)R_Surf16Start, + (long)R_Surf16End - (long)R_Surf16Start); + colormap = vid.colormap16; + R_Surf16Patch (); + } +#endif // id386 + + D_ViewChanged (); +} + + +/* +=============== +R_MarkLeaves +=============== +*/ +void R_MarkLeaves (void) +{ + byte *vis; + mnode_t *node; + int i; + + if (r_oldviewleaf == r_viewleaf) + return; + + r_visframecount++; + r_oldviewleaf = r_viewleaf; + + vis = Mod_LeafPVS (r_viewleaf, cl.worldmodel); + + for (i=0 ; inumleafs ; i++) + { + if (vis[i>>3] & (1<<(i&7))) + { + node = (mnode_t *)&cl.worldmodel->leafs[i+1]; + do + { + if (node->visframe == r_visframecount) + break; + node->visframe = r_visframecount; + node = node->parent; + } while (node); + } + } +} + + +/* +============= +R_DrawEntitiesOnList +============= +*/ +void R_DrawEntitiesOnList (void) +{ + int i, j; + int lnum; + alight_t lighting; +// FIXME: remove and do real lighting + float lightvec[3] = {-1, 0, 0}; + vec3_t dist; + float add; + + if (!r_drawentities.value) + return; + + for (i=0 ; imodel->type) + { + case mod_sprite: + VectorCopy (currententity->origin, r_entorigin); + VectorSubtract (r_origin, r_entorigin, modelorg); + R_DrawSprite (); + break; + + case mod_alias: + VectorCopy (currententity->origin, r_entorigin); + VectorSubtract (r_origin, r_entorigin, modelorg); + + // see if the bounding box lets us trivially reject, also sets + // trivial accept status + if (R_AliasCheckBBox ()) + { + j = R_LightPoint (currententity->origin); + + lighting.ambientlight = j; + lighting.shadelight = j; + + lighting.plightvec = lightvec; + + for (lnum=0 ; lnum= cl.time) + { + VectorSubtract (currententity->origin, + cl_dlights[lnum].origin, + dist); + add = cl_dlights[lnum].radius - Length(dist); + + if (add > 0) + lighting.ambientlight += add; + } + } + + // 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; + + if (r_fullbrightSkins.value && currententity->model->modhint == MOD_PLAYER + && !cl.teamfortress) + lighting.ambientlight = lighting.shadelight = 128; + + R_AliasDrawModel (&lighting); + } + + break; + + default: + break; + } + } +} + +/* +============= +R_DrawViewModel +============= +*/ +void R_DrawViewModel (void) +{ +// FIXME: remove and do real lighting + float lightvec[3] = {-1, 0, 0}; + int j; + int lnum; + vec3_t dist; + float add; + dlight_t *dl; + + if (!r_drawviewmodel.value || (r_fov_greater_than_90 && r_drawviewmodel.value == 2) + || !Cam_DrawViewModel()) + return; + + if (cl.stats[STAT_ITEMS] & IT_INVISIBILITY) + return; + + if (cl.stats[STAT_HEALTH] <= 0) + return; + + currententity = &cl.viewent; + if (!currententity->model) + return; + + VectorCopy (currententity->origin, r_entorigin); + VectorSubtract (r_origin, r_entorigin, modelorg); + + VectorCopy (vup, viewlightvec); + VectorInverse (viewlightvec); + + j = R_LightPoint (currententity->origin); + + if (j < 24) + j = 24; // always give some light on gun + r_viewlighting.ambientlight = j; + r_viewlighting.shadelight = j; + +// add dynamic lights + for (lnum=0 ; lnumradius) + continue; + if (!dl->radius) + continue; + if (dl->die < cl.time) + continue; + + VectorSubtract (currententity->origin, dl->origin, dist); + add = dl->radius - Length(dist); + if (add > 0) + r_viewlighting.ambientlight += add; + } + +// clamp lighting so it doesn't overbright as much + if (r_viewlighting.ambientlight > 128) + r_viewlighting.ambientlight = 128; + if (r_viewlighting.ambientlight + r_viewlighting.shadelight > 192) + r_viewlighting.shadelight = 192 - r_viewlighting.ambientlight; + + r_viewlighting.plightvec = lightvec; + + R_AliasDrawModel (&r_viewlighting); +} + + +/* +============= +R_BmodelCheckBBox +============= +*/ +int R_BmodelCheckBBox (model_t *clmodel, float *minmaxs) +{ + int i, *pindex, clipflags; + vec3_t acceptpt, rejectpt; + double d; + + clipflags = 0; + + if (currententity->angles[0] || currententity->angles[1] + || currententity->angles[2]) + { + for (i=0 ; i<4 ; i++) + { + d = DotProduct (currententity->origin, view_clipplanes[i].normal); + d -= view_clipplanes[i].dist; + + if (d <= -clmodel->radius) + return BMODEL_FULLY_CLIPPED; + + if (d <= clmodel->radius) + clipflags |= (1<model->type) + { + case mod_brush: + + clmodel = currententity->model; + + // see if the bounding box lets us trivially reject, also sets + // trivial accept status + for (j=0 ; j<3 ; j++) + { + minmaxs[j] = currententity->origin[j] + + clmodel->mins[j]; + minmaxs[3+j] = currententity->origin[j] + + clmodel->maxs[j]; + } + + clipflags = R_BmodelCheckBBox (clmodel, minmaxs); + + if (clipflags != BMODEL_FULLY_CLIPPED) + { + VectorCopy (currententity->origin, r_entorigin); + VectorSubtract (r_origin, r_entorigin, modelorg); + // FIXME: is this needed? + VectorCopy (modelorg, r_worldmodelorg); + + r_pcurrentvertbase = clmodel->vertexes; + + // FIXME: stop transforming twice + R_RotateBmodel (); + + // calculate dynamic lighting for bmodel if it's not an + // instanced model + if (clmodel->firstmodelsurface != 0) + { + for (k=0 ; knodes + clmodel->hulls[0].firstclipnode); + } + } + + // if the driver wants polygons, deliver those. Z-buffering is on + // at this point, so no clipping to the world tree is needed, just + // frustum clipping + if (r_drawpolys | r_drawculledpolys) + { + R_ZDrawSubmodelPolys (clmodel); + } + else + { + r_pefragtopnode = NULL; + + for (j=0 ; j<3 ; j++) + { + r_emins[j] = minmaxs[j]; + r_emaxs[j] = minmaxs[3+j]; + } + + R_SplitEntityOnNode2 (cl.worldmodel->nodes); + + if (r_pefragtopnode) + { + currententity->topnode = r_pefragtopnode; + + if (r_pefragtopnode->contents >= 0) + { + // not a leaf; has to be clipped to the world BSP + r_clipflags = clipflags; + R_DrawSolidClippedSubmodelPolygons (clmodel); + } + 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 (clmodel, clipflags); + } + + currententity->topnode = NULL; + } + } + + // 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 (base_modelorg, modelorg); + VectorCopy (oldorigin, modelorg); + R_TransformFrustum (); + } + + break; + + default: + break; + } + } + + insubmodel = false; +} + + +/* +================ +R_EdgeDrawing +================ +*/ +void R_EdgeDrawing (void) +{ + edge_t ledges[NUMSTACKEDGES + + ((CACHE_SIZE - 1) / sizeof(edge_t)) + 1]; + surf_t lsurfs[NUMSTACKSURFACES + + ((CACHE_SIZE - 1) / sizeof(surf_t)) + 1]; + + if (auxedges) + { + r_edges = auxedges; + } + else + { + r_edges = (edge_t *) + (((long)&ledges[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1)); + } + + if (r_surfsonstack) + { + surfaces = (surf_t *) + (((long)&lsurfs[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1)); + 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_SurfacePatch (); + } + + R_BeginEdgeFrame (); + + if (r_dspeeds.value) + { + rw_time1 = Sys_DoubleTime (); + } + + R_RenderWorld (); + + if (r_drawculledpolys) + R_ScanEdges (); + +// only the world can be drawn back to front with no z reads or compares, just +// z writes, so have the driver turn z compares on now + D_TurnZOn (); + + if (r_dspeeds.value) + { + rw_time2 = Sys_DoubleTime (); + db_time1 = rw_time2; + } + + R_DrawBEntitiesOnList (); + + if (r_dspeeds.value) + { + db_time2 = Sys_DoubleTime (); + se_time1 = db_time2; + } + + if (!r_dspeeds.value) + { + VID_UnlockBuffer (); + S_ExtraUpdate (); // don't let sound get messed up if going slow + VID_LockBuffer (); + } + + if (!(r_drawpolys | r_drawculledpolys)) + R_ScanEdges (); +} + + +/* +================ +R_RenderView + +r_refdef must be set before the first call +================ +*/ +void R_RenderView_ (void) +{ + byte warpbuffer[WARP_WIDTH * WARP_HEIGHT]; + + r_warpbuffer = warpbuffer; + + if (r_timegraph.value || r_speeds.value || r_dspeeds.value) + r_time1 = Sys_DoubleTime (); + + R_SetupFrame (); + +#ifdef PASSAGES +SetVisibilityByPassages (); +#else + R_MarkLeaves (); // done here so we know if we're in water +#endif + +// make FDIV fast. This reduces timing precision after we've been running for a +// while, so we don't do it globally. This also sets chop mode, and we do it +// here so that setup stuff like the refresh area calculations match what's +// done in screen.c + Sys_LowFPPrecision (); + + if (!r_worldentity.model || !cl.worldmodel) + Sys_Error ("R_RenderView: NULL worldmodel"); + + if (!r_dspeeds.value) + { + VID_UnlockBuffer (); + S_ExtraUpdate (); // don't let sound get messed up if going slow + VID_LockBuffer (); + } + + R_EdgeDrawing (); + + if (!r_dspeeds.value) + { + VID_UnlockBuffer (); + S_ExtraUpdate (); // don't let sound get messed up if going slow + VID_LockBuffer (); + } + + if (r_dspeeds.value) + { + se_time2 = Sys_DoubleTime (); + de_time1 = se_time2; + } + + R_DrawEntitiesOnList (); + + if (r_dspeeds.value) + { + de_time2 = Sys_DoubleTime (); + dv_time1 = de_time2; + } + + R_DrawViewModel (); + + if (r_dspeeds.value) + { + dv_time2 = Sys_DoubleTime (); + dp_time1 = Sys_DoubleTime (); + } + + R_DrawParticles (); + + if (r_dspeeds.value) + dp_time2 = Sys_DoubleTime (); + + if (r_dowarp) + D_WarpScreen (); + + V_SetContentsColor (r_viewleaf->contents); + + if (r_timegraph.value) + R_TimeGraph (); + + if (r_netgraph.value) + R_NetGraph (); + + if (r_zgraph.value) + R_ZGraph (); + + if (r_aliasstats.value) + R_PrintAliasStats (); + + if (r_speeds.value) + R_PrintTimes (); + + if (r_dspeeds.value) + R_PrintDSpeeds (); + + if (r_reportsurfout.value && r_outofsurfaces) + Con_Printf ("Short %d surfaces\n", r_outofsurfaces); + + if (r_reportedgeout.value && r_outofedges) + Con_Printf ("Short roughly %d edges\n", r_outofedges * 2 / 3); + +// back to high floating-point precision + Sys_HighFPPrecision (); +} + +void R_RenderView (void) +{ + int dummy; + int delta; + + delta = (byte *)&dummy - r_stack_start; + if (delta < -10000 || delta > 10000) + Sys_Error ("R_RenderView: called without enough stack"); + + if ( Hunk_LowMark() & 3 ) + Sys_Error ("Hunk is missaligned"); + + if ( (long)(&dummy) & 3 ) + Sys_Error ("Stack is missaligned"); + + if ( (long)(&r_warpbuffer) & 3 ) + Sys_Error ("Globals are missaligned"); + + R_RenderView_ (); +} + +/* +================ +R_InitTurb +================ +*/ +void R_InitTurb (void) +{ + int i; + + for (i=0 ; i<1280 ; i++) + { + sintable[i] = AMP + sin(i*3.14159*2/CYCLE)*AMP; + intsintable[i] = AMP2 + sin(i*3.14159*2/CYCLE)*AMP2; // AMP2, not 20 + } +} + diff --git a/source/r_misc.c b/source/r_misc.c index d1cd9657..37741acb 100644 --- a/source/r_misc.c +++ b/source/r_misc.c @@ -1,599 +1,599 @@ -/* -Copyright (C) 1996-1997 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 - -#include "quakedef.h" -#include "r_local.h" - - -/* -=============== -R_CheckVariables -=============== -*/ -void R_CheckVariables (void) -{ -#if 0 - static float oldbright; - - if (r_fullbright.value != oldbright) - { - oldbright = r_fullbright.value; - D_FlushCaches (); // so all lighting changes - } -#endif -} - - -/* -============ -Show - -Debugging use -============ -*/ -void Show (void) -{ - vrect_t vr; - - vr.x = vr.y = 0; - vr.width = vid.width; - vr.height = vid.height; - vr.pnext = NULL; - VID_Update (&vr); -} - -/* -==================== -R_TimeRefresh_f - -For program optimization -==================== -*/ -void R_TimeRefresh_f (void) -{ - int i; - float start, stop, time; - int startangle; - vrect_t vr; - - if (cls.state != ca_active) - return; - - startangle = r_refdef.viewangles[1]; - - start = Sys_DoubleTime (); - for (i=0 ; i<128 ; i++) - { - r_refdef.viewangles[1] = i/128.0*360.0; - - VID_LockBuffer (); - - R_RenderView (); - - VID_UnlockBuffer (); - - vr.x = r_refdef.vrect.x; - vr.y = r_refdef.vrect.y; - vr.width = r_refdef.vrect.width; - vr.height = r_refdef.vrect.height; - vr.pnext = NULL; - VID_Update (&vr); - } - stop = Sys_DoubleTime (); - time = stop-start; - Con_Printf ("%f seconds (%f fps)\n", time, 128/time); - - r_refdef.viewangles[1] = startangle; -} - -/* -================ -R_LineGraph - -Only called by R_DisplayTime -================ -*/ -void R_LineGraph (int x, int y, int h) -{ - int i; - byte *dest; - int s; - int color; - -// FIXME: should be disabled on no-buffer adapters, or should be in the driver - -// x += r_refdef.vrect.x; -// y += r_refdef.vrect.y; - - dest = vid.buffer + vid.rowbytes*y + x; - - s = r_graphheight.value; - - if (h == 10000) - color = 0x6f; // yellow - else if (h == 9999) - color = 0x4f; // red - else if (h == 9998) - color = 0xd0; // blue - else - color = 0xff; // pink - - if (h>s) - h = s; - - for (i=0 ; i>1); - y = vid.height - sb_lines - 24 - (int)r_graphheight.value*2 - 2; - - M_DrawTextBox (x, y, (w+7)/8, ((int)r_graphheight.value*2+7)/8 + 1); - y2 = y + 8; - y = vid.height - sb_lines - 8 - 2; - - x = 8; - lost = CL_CalcNet(); - for (a=NET_TIMINGS-w ; anormal); - *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_SetupFrame -=============== -*/ -void R_SetupFrame (void) -{ - int edgecount; - vrect_t vrect; - float w, h; - -// don't allow cheats in multiplayer -r_draworder.value = 0; -r_fullbright.value = 0; -r_ambient.value = 0; -r_drawflat.value = 0; - - if (r_numsurfs.value) - { - if ((surface_p - surfaces) > r_maxsurfsseen) - r_maxsurfsseen = surface_p - surfaces; - - Con_Printf ("Used %d of %d surfs; %d max\n", surface_p - surfaces, - surf_max - surfaces, r_maxsurfsseen); - } - - if (r_numedges.value) - { - edgecount = edge_p - r_edges; - - if (edgecount > r_maxedgesseen) - r_maxedgesseen = edgecount; - - Con_Printf ("Used %d of %d edges; %d max\n", edgecount, - r_numallocatededges, r_maxedgesseen); - } - - r_refdef.ambientlight = r_ambient.value; - - if (r_refdef.ambientlight < 0) - r_refdef.ambientlight = 0; - -// if (!sv.active) - r_draworder.value = 0; // don't let cheaters look behind walls - - R_CheckVariables (); - - R_AnimateLight (); - - r_framecount++; - - numbtofpolys = 0; - -// debugging -#if 0 -r_refdef.vieworg[0]= 80; -r_refdef.vieworg[1]= 64; -r_refdef.vieworg[2]= 40; -r_refdef.viewangles[0]= 0; -r_refdef.viewangles[1]= 46.763641357; -r_refdef.viewangles[2]= 0; -#endif - -// 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 - r_oldviewleaf = r_viewleaf; - r_viewleaf = Mod_PointInLeaf (r_origin, cl.worldmodel); - - r_dowarpold = r_dowarp; - r_dowarp = r_waterwarp.value && (r_viewleaf->contents <= CONTENTS_WATER); - - if ((r_dowarp != r_dowarpold) || r_viewchanged) - { - if (r_dowarp) - { - if ((vid.width <= vid.maxwarpwidth) && - (vid.height <= vid.maxwarpheight)) - { - vrect.x = 0; - vrect.y = 0; - vrect.width = vid.width; - vrect.height = vid.height; - - R_ViewChanged (&vrect, sb_lines, vid.aspect); - } - else - { - w = vid.width; - h = vid.height; - - if (w > vid.maxwarpwidth) - { - h *= (float)vid.maxwarpwidth / w; - w = vid.maxwarpwidth; - } - - if (h > vid.maxwarpheight) - { - h = vid.maxwarpheight; - w *= (float)vid.maxwarpheight / h; - } - - vrect.x = 0; - vrect.y = 0; - vrect.width = (int)w; - vrect.height = (int)h; - - R_ViewChanged (&vrect, - (int)((float)sb_lines * (h/(float)vid.height)), - vid.aspect * (h / w) * - ((float)vid.width / (float)vid.height)); - } - } - else - { - vrect.x = 0; - vrect.y = 0; - vrect.width = vid.width; - vrect.height = vid.height; - - R_ViewChanged (&vrect, sb_lines, vid.aspect); - } - - r_viewchanged = false; - } - -// start off with just the four screen edge clip planes - R_TransformFrustum (); - -// save base values - VectorCopy (vpn, base_vpn); - VectorCopy (vright, base_vright); - VectorCopy (vup, base_vup); - VectorCopy (modelorg, base_modelorg); - - R_SetSkyFrame (); - - R_SetUpFrustumIndexes (); - - r_cache_thrash = false; - -// 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_SetupFrame (); -} - +/* +Copyright (C) 1996-1997 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 + +#include "quakedef.h" +#include "r_local.h" + + +/* +=============== +R_CheckVariables +=============== +*/ +void R_CheckVariables (void) +{ +#if 0 + static float oldbright; + + if (r_fullbright.value != oldbright) + { + oldbright = r_fullbright.value; + D_FlushCaches (); // so all lighting changes + } +#endif +} + + +/* +============ +Show + +Debugging use +============ +*/ +void Show (void) +{ + vrect_t vr; + + vr.x = vr.y = 0; + vr.width = vid.width; + vr.height = vid.height; + vr.pnext = NULL; + VID_Update (&vr); +} + +/* +==================== +R_TimeRefresh_f + +For program optimization +==================== +*/ +void R_TimeRefresh_f (void) +{ + int i; + float start, stop, time; + int startangle; + vrect_t vr; + + if (cls.state != ca_active) + return; + + startangle = r_refdef.viewangles[1]; + + start = Sys_DoubleTime (); + for (i=0 ; i<128 ; i++) + { + r_refdef.viewangles[1] = i/128.0*360.0; + + VID_LockBuffer (); + + R_RenderView (); + + VID_UnlockBuffer (); + + vr.x = r_refdef.vrect.x; + vr.y = r_refdef.vrect.y; + vr.width = r_refdef.vrect.width; + vr.height = r_refdef.vrect.height; + vr.pnext = NULL; + VID_Update (&vr); + } + stop = Sys_DoubleTime (); + time = stop-start; + Con_Printf ("%f seconds (%f fps)\n", time, 128/time); + + r_refdef.viewangles[1] = startangle; +} + +/* +================ +R_LineGraph + +Only called by R_DisplayTime +================ +*/ +void R_LineGraph (int x, int y, int h) +{ + int i; + byte *dest; + int s; + int color; + +// FIXME: should be disabled on no-buffer adapters, or should be in the driver + +// x += r_refdef.vrect.x; +// y += r_refdef.vrect.y; + + dest = vid.buffer + vid.rowbytes*y + x; + + s = r_graphheight.value; + + if (h == 10000) + color = 0x6f; // yellow + else if (h == 9999) + color = 0x4f; // red + else if (h == 9998) + color = 0xd0; // blue + else + color = 0xff; // pink + + if (h>s) + h = s; + + for (i=0 ; i>1); + y = vid.height - sb_lines - 24 - (int)r_graphheight.value*2 - 2; + + M_DrawTextBox (x, y, (w+7)/8, ((int)r_graphheight.value*2+7)/8 + 1); + y2 = y + 8; + y = vid.height - sb_lines - 8 - 2; + + x = 8; + lost = CL_CalcNet(); + for (a=NET_TIMINGS-w ; anormal); + *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_SetupFrame +=============== +*/ +void R_SetupFrame (void) +{ + int edgecount; + vrect_t vrect; + float w, h; + +// don't allow cheats in multiplayer +r_draworder.value = 0; +r_fullbright.value = 0; +r_ambient.value = 0; +r_drawflat.value = 0; + + if (r_numsurfs.value) + { + if ((surface_p - surfaces) > r_maxsurfsseen) + r_maxsurfsseen = surface_p - surfaces; + + Con_Printf ("Used %d of %d surfs; %d max\n", surface_p - surfaces, + surf_max - surfaces, r_maxsurfsseen); + } + + if (r_numedges.value) + { + edgecount = edge_p - r_edges; + + if (edgecount > r_maxedgesseen) + r_maxedgesseen = edgecount; + + Con_Printf ("Used %d of %d edges; %d max\n", edgecount, + r_numallocatededges, r_maxedgesseen); + } + + r_refdef.ambientlight = r_ambient.value; + + if (r_refdef.ambientlight < 0) + r_refdef.ambientlight = 0; + +// if (!sv.active) + r_draworder.value = 0; // don't let cheaters look behind walls + + R_CheckVariables (); + + R_AnimateLight (); + + r_framecount++; + + numbtofpolys = 0; + +// debugging +#if 0 +r_refdef.vieworg[0]= 80; +r_refdef.vieworg[1]= 64; +r_refdef.vieworg[2]= 40; +r_refdef.viewangles[0]= 0; +r_refdef.viewangles[1]= 46.763641357; +r_refdef.viewangles[2]= 0; +#endif + +// 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 + r_oldviewleaf = r_viewleaf; + r_viewleaf = Mod_PointInLeaf (r_origin, cl.worldmodel); + + r_dowarpold = r_dowarp; + r_dowarp = r_waterwarp.value && (r_viewleaf->contents <= CONTENTS_WATER); + + if ((r_dowarp != r_dowarpold) || r_viewchanged) + { + if (r_dowarp) + { + if ((vid.width <= vid.maxwarpwidth) && + (vid.height <= vid.maxwarpheight)) + { + vrect.x = 0; + vrect.y = 0; + vrect.width = vid.width; + vrect.height = vid.height; + + R_ViewChanged (&vrect, sb_lines, vid.aspect); + } + else + { + w = vid.width; + h = vid.height; + + if (w > vid.maxwarpwidth) + { + h *= (float)vid.maxwarpwidth / w; + w = vid.maxwarpwidth; + } + + if (h > vid.maxwarpheight) + { + h = vid.maxwarpheight; + w *= (float)vid.maxwarpheight / h; + } + + vrect.x = 0; + vrect.y = 0; + vrect.width = (int)w; + vrect.height = (int)h; + + R_ViewChanged (&vrect, + (int)((float)sb_lines * (h/(float)vid.height)), + vid.aspect * (h / w) * + ((float)vid.width / (float)vid.height)); + } + } + else + { + vrect.x = 0; + vrect.y = 0; + vrect.width = vid.width; + vrect.height = vid.height; + + R_ViewChanged (&vrect, sb_lines, vid.aspect); + } + + r_viewchanged = false; + } + +// start off with just the four screen edge clip planes + R_TransformFrustum (); + +// save base values + VectorCopy (vpn, base_vpn); + VectorCopy (vright, base_vright); + VectorCopy (vup, base_vup); + VectorCopy (modelorg, base_modelorg); + + R_SetSkyFrame (); + + R_SetUpFrustumIndexes (); + + r_cache_thrash = false; + +// 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_SetupFrame (); +} + diff --git a/source/r_part.c b/source/r_part.c index a0868265..17da2810 100644 --- a/source/r_part.c +++ b/source/r_part.c @@ -1,617 +1,617 @@ -/* -Copyright (C) 1996-1997 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 "quakedef.h" -#include "r_local.h" - -#define MAX_PARTICLES 2048 // default max # of particles at one - // time -#define ABSOLUTE_MIN_PARTICLES 512 // no fewer than this no matter what's - // on the command line - -int ramp1[8] = {0x6f, 0x6d, 0x6b, 0x69, 0x67, 0x65, 0x63, 0x61}; -int ramp2[8] = {0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x68, 0x66}; -int ramp3[8] = {0x6d, 0x6b, 6, 5, 4, 3}; - -particle_t *active_particles, *free_particles; - -particle_t *particles; -int r_numparticles; - -vec3_t r_pright, r_pup, r_ppn; - - -/* -=============== -R_InitParticles -=============== -*/ -void R_InitParticles (void) -{ - int i; - - i = COM_CheckParm ("-particles"); - - if (i) - { - r_numparticles = (int)(Q_atoi(com_argv[i+1])); - if (r_numparticles < ABSOLUTE_MIN_PARTICLES) - r_numparticles = ABSOLUTE_MIN_PARTICLES; - } - else - { - r_numparticles = MAX_PARTICLES; - } - - particles = (particle_t *) - Hunk_AllocName (r_numparticles * sizeof(particle_t), "particles"); -} - - -/* -=============== -R_ClearParticles -=============== -*/ -void R_ClearParticles (void) -{ - int i; - - free_particles = &particles[0]; - active_particles = NULL; - - for (i=0 ;inext; - p->next = active_particles; - active_particles = p; - - p->die = 99999; - p->color = (-c)&15; - p->type = pt_static; - VectorCopy (vec3_origin, p->vel); - VectorCopy (org, p->org); - } - - fclose (f); - Con_Printf ("%i points read\n", c); -} - -/* -=============== -R_ParticleExplosion - -=============== -*/ -void R_ParticleExplosion (vec3_t org) -{ - int i, j; - particle_t *p; - - for (i=0 ; i<1024 ; i++) - { - if (!free_particles) - return; - p = free_particles; - free_particles = p->next; - p->next = active_particles; - active_particles = p; - - p->die = cl.time + 5; - p->color = ramp1[0]; - p->ramp = rand()&3; - if (i & 1) - { - p->type = pt_explode; - for (j=0 ; j<3 ; j++) - { - p->org[j] = org[j] + ((rand()%32)-16); - p->vel[j] = (rand()%512)-256; - } - } - else - { - p->type = pt_explode2; - for (j=0 ; j<3 ; j++) - { - p->org[j] = org[j] + ((rand()%32)-16); - p->vel[j] = (rand()%512)-256; - } - } - } -} - -/* -=============== -R_BlobExplosion - -=============== -*/ -void R_BlobExplosion (vec3_t org) -{ - int i, j; - particle_t *p; - - for (i=0 ; i<1024 ; i++) - { - if (!free_particles) - return; - p = free_particles; - free_particles = p->next; - p->next = active_particles; - active_particles = p; - - p->die = cl.time + 1 + (rand()&8)*0.05; - - if (i & 1) - { - p->type = pt_blob; - p->color = 66 + rand()%6; - for (j=0 ; j<3 ; j++) - { - p->org[j] = org[j] + ((rand()%32)-16); - p->vel[j] = (rand()%512)-256; - } - } - else - { - p->type = pt_blob2; - p->color = 150 + rand()%6; - for (j=0 ; j<3 ; j++) - { - p->org[j] = org[j] + ((rand()%32)-16); - p->vel[j] = (rand()%512)-256; - } - } - } -} - -/* -=============== -R_RunParticleEffect - -=============== -*/ -void R_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count) -{ - int i, j; - particle_t *p; - int scale; - - if (count > 130) - scale = 3; - else if (count > 20) - scale = 2; - else - scale = 1; - - for (i=0 ; inext; - p->next = active_particles; - active_particles = p; - - p->die = cl.time + 0.1*(rand()%5); - p->color = (color&~7) + (rand()&7); - p->type = pt_grav; - for (j=0 ; j<3 ; j++) - { - p->org[j] = org[j] + scale*((rand()&15)-8); - p->vel[j] = dir[j]*15;// + (rand()%300)-150; - } - } -} - - -/* -=============== -R_LavaSplash - -=============== -*/ -void R_LavaSplash (vec3_t org) -{ - int i, j, k; - particle_t *p; - float vel; - vec3_t dir; - - for (i=-16 ; i<16 ; i++) - for (j=-16 ; j<16 ; j++) - for (k=0 ; k<1 ; k++) - { - if (!free_particles) - return; - p = free_particles; - free_particles = p->next; - p->next = active_particles; - active_particles = p; - - p->die = cl.time + 2 + (rand()&31) * 0.02; - p->color = 224 + (rand()&7); - p->type = pt_grav; - - dir[0] = j*8 + (rand()&7); - dir[1] = i*8 + (rand()&7); - dir[2] = 256; - - p->org[0] = org[0] + dir[0]; - p->org[1] = org[1] + dir[1]; - p->org[2] = org[2] + (rand()&63); - - VectorNormalize (dir); - vel = 50 + (rand()&63); - VectorScale (dir, vel, p->vel); - } -} - -/* -=============== -R_TeleportSplash - -=============== -*/ -void R_TeleportSplash (vec3_t org) -{ - int i, j, k; - particle_t *p; - float vel; - vec3_t dir; - - for (i=-16 ; i<16 ; i+=4) - for (j=-16 ; j<16 ; j+=4) - for (k=-24 ; k<32 ; k+=4) - { - if (!free_particles) - return; - p = free_particles; - free_particles = p->next; - p->next = active_particles; - active_particles = p; - - p->die = cl.time + 0.2 + (rand()&7) * 0.02; - p->color = 7 + (rand()&7); - p->type = pt_grav; - - dir[0] = j*8; - dir[1] = i*8; - dir[2] = k*8; - - p->org[0] = org[0] + i + (rand()&3); - p->org[1] = org[1] + j + (rand()&3); - p->org[2] = org[2] + k + (rand()&3); - - VectorNormalize (dir); - vel = 50 + (rand()&63); - VectorScale (dir, vel, p->vel); - } -} - -void R_RocketTrail (vec3_t start, vec3_t end, int type) -{ - vec3_t vec; - float len; - int j; - particle_t *p; - - VectorSubtract (end, start, vec); - len = VectorNormalize (vec); - VectorScale (vec, 3, vec); - while (len > 0) - { - len -= 3; - - if (!free_particles) - return; - p = free_particles; - free_particles = p->next; - p->next = active_particles; - active_particles = p; - - VectorCopy (vec3_origin, p->vel); - p->die = cl.time + 2; - - if (type == 4) - { // slight blood - p->type = pt_slowgrav; - p->color = 67 + (rand()&3); - for (j=0 ; j<3 ; j++) - p->org[j] = start[j] + ((rand()%6)-3); - len -= 3; - } - else if (type == 2) - { // blood - p->type = pt_slowgrav; - p->color = 67 + (rand()&3); - for (j=0 ; j<3 ; j++) - p->org[j] = start[j] + ((rand()%6)-3); - } - else if (type == 6) - { // voor trail - p->color = 9*16 + 8 + (rand()&3); - p->type = pt_static; - p->die = cl.time + 0.3; - for (j=0 ; j<3 ; j++) - p->org[j] = start[j] + ((rand()&15)-8); - } - else if (type == 1) - { // smoke smoke - p->ramp = (rand()&3) + 2; - p->color = ramp3[(int)p->ramp]; - p->type = pt_fire; - for (j=0 ; j<3 ; j++) - p->org[j] = start[j] + ((rand()%6)-3); - } - else if (type == 0) - { // rocket trail - p->ramp = (rand()&3); - p->color = ramp3[(int)p->ramp]; - p->type = pt_fire; - for (j=0 ; j<3 ; j++) - p->org[j] = start[j] + ((rand()%6)-3); - } - else if (type == 3 || type == 5) - { // tracer - static int tracercount; - - p->die = cl.time + 0.5; - p->type = pt_static; - if (type == 3) - p->color = 52 + ((tracercount&4)<<1); - else - p->color = 230 + ((tracercount&4)<<1); - - tracercount++; - - VectorCopy (start, p->org); - if (tracercount & 1) - { - p->vel[0] = 30*vec[1]; - p->vel[1] = 30*-vec[0]; - } - else - { - p->vel[0] = 30*-vec[1]; - p->vel[1] = 30*vec[0]; - } - - } - - - VectorAdd (start, vec, start); - } -} - - -/* -=============== -R_DrawParticles -=============== -*/ -void R_DrawParticles (void) -{ - particle_t *p, *kill; - float grav; - int i; - float time2, time3; - float time1; - float dvel; - float frametime; -#ifdef GLQUAKE - unsigned char *at; - unsigned char theAlpha; - vec3_t up, right; - float scale; - qboolean alphaTestEnabled; - - GL_Bind(particletexture); - alphaTestEnabled = glIsEnabled(GL_ALPHA_TEST); - - if (alphaTestEnabled) - glDisable(GL_ALPHA_TEST); - glEnable (GL_BLEND); - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - glBegin (GL_TRIANGLES); - - VectorScale (vup, 1.5, up); - VectorScale (vright, 1.5, right); -#else - D_StartParticles (); - - VectorScale (vright, xscaleshrink, r_pright); - VectorScale (vup, yscaleshrink, r_pup); - VectorCopy (vpn, r_ppn); -#endif - - frametime = host_frametime; - time3 = frametime * 15; - time2 = frametime * 10; // 15; - time1 = frametime * 5; - grav = frametime * 800 * 0.05; - dvel = 4*frametime; - - for ( ;; ) - { - kill = active_particles; - if (kill && kill->die < cl.time) - { - active_particles = kill->next; - kill->next = free_particles; - free_particles = kill; - continue; - } - break; - } - - for (p=active_particles ; p ; p=p->next) - { - for ( ;; ) - { - kill = p->next; - if (kill && kill->die < cl.time) - { - p->next = kill->next; - kill->next = free_particles; - free_particles = kill; - continue; - } - break; - } - -#ifdef GLQUAKE - // hack a scale up to keep particles from disapearing - scale = (p->org[0] - r_origin[0])*vpn[0] + (p->org[1] - r_origin[1])*vpn[1] - + (p->org[2] - r_origin[2])*vpn[2]; - if (scale < 20) - scale = 1; - else - scale = 1 + scale * 0.004; - at = (byte *)&d_8to24table[(int)p->color]; - if (p->type==pt_fire) - theAlpha = 255*(6-p->ramp)/6; -// theAlpha = 192; -// else if (p->type==pt_explode || p->type==pt_explode2) -// theAlpha = 255*(8-p->ramp)/8; - else - theAlpha = 255; - glColor4ub (*at, *(at+1), *(at+2), theAlpha); -// glColor3ubv (at); -// glColor3ubv ((byte *)&d_8to24table[(int)p->color]); - glTexCoord2f (0,0); - glVertex3fv (p->org); - glTexCoord2f (1,0); - glVertex3f (p->org[0] + up[0]*scale, p->org[1] + up[1]*scale, p->org[2] + up[2]*scale); - glTexCoord2f (0,1); - glVertex3f (p->org[0] + right[0]*scale, p->org[1] + right[1]*scale, p->org[2] + right[2]*scale); - -#else - D_DrawParticle (p); -#endif - - p->org[0] += p->vel[0]*frametime; - p->org[1] += p->vel[1]*frametime; - p->org[2] += p->vel[2]*frametime; - - switch (p->type) - { - case pt_static: - break; - case pt_fire: - p->ramp += time1; - if (p->ramp >= 6) - p->die = -1; - else - p->color = ramp3[(int)p->ramp]; - p->vel[2] += grav; - break; - - case pt_explode: - p->ramp += time2; - if (p->ramp >=8) - p->die = -1; - else - p->color = ramp1[(int)p->ramp]; - for (i=0 ; i<3 ; i++) - p->vel[i] += p->vel[i]*dvel; - p->vel[2] -= grav; - break; - - case pt_explode2: - p->ramp += time3; - if (p->ramp >=8) - p->die = -1; - else - p->color = ramp2[(int)p->ramp]; - for (i=0 ; i<3 ; i++) - p->vel[i] -= p->vel[i]*frametime; - p->vel[2] -= grav; - break; - - case pt_blob: - for (i=0 ; i<3 ; i++) - p->vel[i] += p->vel[i]*dvel; - p->vel[2] -= grav; - break; - - case pt_blob2: - for (i=0 ; i<2 ; i++) - p->vel[i] -= p->vel[i]*dvel; - p->vel[2] -= grav; - break; - - case pt_slowgrav: - case pt_grav: - p->vel[2] -= grav; - break; - } - } - -#ifdef GLQUAKE - glEnd (); - glDisable (GL_BLEND); - if (alphaTestEnabled) - glEnable(GL_ALPHA_TEST); - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); -#else - D_EndParticles (); -#endif -} - +/* +Copyright (C) 1996-1997 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 "quakedef.h" +#include "r_local.h" + +#define MAX_PARTICLES 2048 // default max # of particles at one + // time +#define ABSOLUTE_MIN_PARTICLES 512 // no fewer than this no matter what's + // on the command line + +int ramp1[8] = {0x6f, 0x6d, 0x6b, 0x69, 0x67, 0x65, 0x63, 0x61}; +int ramp2[8] = {0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x68, 0x66}; +int ramp3[8] = {0x6d, 0x6b, 6, 5, 4, 3}; + +particle_t *active_particles, *free_particles; + +particle_t *particles; +int r_numparticles; + +vec3_t r_pright, r_pup, r_ppn; + + +/* +=============== +R_InitParticles +=============== +*/ +void R_InitParticles (void) +{ + int i; + + i = COM_CheckParm ("-particles"); + + if (i) + { + r_numparticles = (int)(Q_atoi(com_argv[i+1])); + if (r_numparticles < ABSOLUTE_MIN_PARTICLES) + r_numparticles = ABSOLUTE_MIN_PARTICLES; + } + else + { + r_numparticles = MAX_PARTICLES; + } + + particles = (particle_t *) + Hunk_AllocName (r_numparticles * sizeof(particle_t), "particles"); +} + + +/* +=============== +R_ClearParticles +=============== +*/ +void R_ClearParticles (void) +{ + int i; + + free_particles = &particles[0]; + active_particles = NULL; + + for (i=0 ;inext; + p->next = active_particles; + active_particles = p; + + p->die = 99999; + p->color = (-c)&15; + p->type = pt_static; + VectorCopy (vec3_origin, p->vel); + VectorCopy (org, p->org); + } + + fclose (f); + Con_Printf ("%i points read\n", c); +} + +/* +=============== +R_ParticleExplosion + +=============== +*/ +void R_ParticleExplosion (vec3_t org) +{ + int i, j; + particle_t *p; + + for (i=0 ; i<1024 ; i++) + { + if (!free_particles) + return; + p = free_particles; + free_particles = p->next; + p->next = active_particles; + active_particles = p; + + p->die = cl.time + 5; + p->color = ramp1[0]; + p->ramp = rand()&3; + if (i & 1) + { + p->type = pt_explode; + for (j=0 ; j<3 ; j++) + { + p->org[j] = org[j] + ((rand()%32)-16); + p->vel[j] = (rand()%512)-256; + } + } + else + { + p->type = pt_explode2; + for (j=0 ; j<3 ; j++) + { + p->org[j] = org[j] + ((rand()%32)-16); + p->vel[j] = (rand()%512)-256; + } + } + } +} + +/* +=============== +R_BlobExplosion + +=============== +*/ +void R_BlobExplosion (vec3_t org) +{ + int i, j; + particle_t *p; + + for (i=0 ; i<1024 ; i++) + { + if (!free_particles) + return; + p = free_particles; + free_particles = p->next; + p->next = active_particles; + active_particles = p; + + p->die = cl.time + 1 + (rand()&8)*0.05; + + if (i & 1) + { + p->type = pt_blob; + p->color = 66 + rand()%6; + for (j=0 ; j<3 ; j++) + { + p->org[j] = org[j] + ((rand()%32)-16); + p->vel[j] = (rand()%512)-256; + } + } + else + { + p->type = pt_blob2; + p->color = 150 + rand()%6; + for (j=0 ; j<3 ; j++) + { + p->org[j] = org[j] + ((rand()%32)-16); + p->vel[j] = (rand()%512)-256; + } + } + } +} + +/* +=============== +R_RunParticleEffect + +=============== +*/ +void R_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count) +{ + int i, j; + particle_t *p; + int scale; + + if (count > 130) + scale = 3; + else if (count > 20) + scale = 2; + else + scale = 1; + + for (i=0 ; inext; + p->next = active_particles; + active_particles = p; + + p->die = cl.time + 0.1*(rand()%5); + p->color = (color&~7) + (rand()&7); + p->type = pt_grav; + for (j=0 ; j<3 ; j++) + { + p->org[j] = org[j] + scale*((rand()&15)-8); + p->vel[j] = dir[j]*15;// + (rand()%300)-150; + } + } +} + + +/* +=============== +R_LavaSplash + +=============== +*/ +void R_LavaSplash (vec3_t org) +{ + int i, j, k; + particle_t *p; + float vel; + vec3_t dir; + + for (i=-16 ; i<16 ; i++) + for (j=-16 ; j<16 ; j++) + for (k=0 ; k<1 ; k++) + { + if (!free_particles) + return; + p = free_particles; + free_particles = p->next; + p->next = active_particles; + active_particles = p; + + p->die = cl.time + 2 + (rand()&31) * 0.02; + p->color = 224 + (rand()&7); + p->type = pt_grav; + + dir[0] = j*8 + (rand()&7); + dir[1] = i*8 + (rand()&7); + dir[2] = 256; + + p->org[0] = org[0] + dir[0]; + p->org[1] = org[1] + dir[1]; + p->org[2] = org[2] + (rand()&63); + + VectorNormalize (dir); + vel = 50 + (rand()&63); + VectorScale (dir, vel, p->vel); + } +} + +/* +=============== +R_TeleportSplash + +=============== +*/ +void R_TeleportSplash (vec3_t org) +{ + int i, j, k; + particle_t *p; + float vel; + vec3_t dir; + + for (i=-16 ; i<16 ; i+=4) + for (j=-16 ; j<16 ; j+=4) + for (k=-24 ; k<32 ; k+=4) + { + if (!free_particles) + return; + p = free_particles; + free_particles = p->next; + p->next = active_particles; + active_particles = p; + + p->die = cl.time + 0.2 + (rand()&7) * 0.02; + p->color = 7 + (rand()&7); + p->type = pt_grav; + + dir[0] = j*8; + dir[1] = i*8; + dir[2] = k*8; + + p->org[0] = org[0] + i + (rand()&3); + p->org[1] = org[1] + j + (rand()&3); + p->org[2] = org[2] + k + (rand()&3); + + VectorNormalize (dir); + vel = 50 + (rand()&63); + VectorScale (dir, vel, p->vel); + } +} + +void R_RocketTrail (vec3_t start, vec3_t end, int type) +{ + vec3_t vec; + float len; + int j; + particle_t *p; + + VectorSubtract (end, start, vec); + len = VectorNormalize (vec); + VectorScale (vec, 3, vec); + while (len > 0) + { + len -= 3; + + if (!free_particles) + return; + p = free_particles; + free_particles = p->next; + p->next = active_particles; + active_particles = p; + + VectorCopy (vec3_origin, p->vel); + p->die = cl.time + 2; + + if (type == 4) + { // slight blood + p->type = pt_slowgrav; + p->color = 67 + (rand()&3); + for (j=0 ; j<3 ; j++) + p->org[j] = start[j] + ((rand()%6)-3); + len -= 3; + } + else if (type == 2) + { // blood + p->type = pt_slowgrav; + p->color = 67 + (rand()&3); + for (j=0 ; j<3 ; j++) + p->org[j] = start[j] + ((rand()%6)-3); + } + else if (type == 6) + { // voor trail + p->color = 9*16 + 8 + (rand()&3); + p->type = pt_static; + p->die = cl.time + 0.3; + for (j=0 ; j<3 ; j++) + p->org[j] = start[j] + ((rand()&15)-8); + } + else if (type == 1) + { // smoke smoke + p->ramp = (rand()&3) + 2; + p->color = ramp3[(int)p->ramp]; + p->type = pt_fire; + for (j=0 ; j<3 ; j++) + p->org[j] = start[j] + ((rand()%6)-3); + } + else if (type == 0) + { // rocket trail + p->ramp = (rand()&3); + p->color = ramp3[(int)p->ramp]; + p->type = pt_fire; + for (j=0 ; j<3 ; j++) + p->org[j] = start[j] + ((rand()%6)-3); + } + else if (type == 3 || type == 5) + { // tracer + static int tracercount; + + p->die = cl.time + 0.5; + p->type = pt_static; + if (type == 3) + p->color = 52 + ((tracercount&4)<<1); + else + p->color = 230 + ((tracercount&4)<<1); + + tracercount++; + + VectorCopy (start, p->org); + if (tracercount & 1) + { + p->vel[0] = 30*vec[1]; + p->vel[1] = 30*-vec[0]; + } + else + { + p->vel[0] = 30*-vec[1]; + p->vel[1] = 30*vec[0]; + } + + } + + + VectorAdd (start, vec, start); + } +} + + +/* +=============== +R_DrawParticles +=============== +*/ +void R_DrawParticles (void) +{ + particle_t *p, *kill; + float grav; + int i; + float time2, time3; + float time1; + float dvel; + float frametime; +#ifdef GLQUAKE + unsigned char *at; + unsigned char theAlpha; + vec3_t up, right; + float scale; + qboolean alphaTestEnabled; + + GL_Bind(particletexture); + alphaTestEnabled = glIsEnabled(GL_ALPHA_TEST); + + if (alphaTestEnabled) + glDisable(GL_ALPHA_TEST); + glEnable (GL_BLEND); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glBegin (GL_TRIANGLES); + + VectorScale (vup, 1.5, up); + VectorScale (vright, 1.5, right); +#else + D_StartParticles (); + + VectorScale (vright, xscaleshrink, r_pright); + VectorScale (vup, yscaleshrink, r_pup); + VectorCopy (vpn, r_ppn); +#endif + + frametime = host_frametime; + time3 = frametime * 15; + time2 = frametime * 10; // 15; + time1 = frametime * 5; + grav = frametime * 800 * 0.05; + dvel = 4*frametime; + + for ( ;; ) + { + kill = active_particles; + if (kill && kill->die < cl.time) + { + active_particles = kill->next; + kill->next = free_particles; + free_particles = kill; + continue; + } + break; + } + + for (p=active_particles ; p ; p=p->next) + { + for ( ;; ) + { + kill = p->next; + if (kill && kill->die < cl.time) + { + p->next = kill->next; + kill->next = free_particles; + free_particles = kill; + continue; + } + break; + } + +#ifdef GLQUAKE + // hack a scale up to keep particles from disapearing + scale = (p->org[0] - r_origin[0])*vpn[0] + (p->org[1] - r_origin[1])*vpn[1] + + (p->org[2] - r_origin[2])*vpn[2]; + if (scale < 20) + scale = 1; + else + scale = 1 + scale * 0.004; + at = (byte *)&d_8to24table[(int)p->color]; + if (p->type==pt_fire) + theAlpha = 255*(6-p->ramp)/6; +// theAlpha = 192; +// else if (p->type==pt_explode || p->type==pt_explode2) +// theAlpha = 255*(8-p->ramp)/8; + else + theAlpha = 255; + glColor4ub (*at, *(at+1), *(at+2), theAlpha); +// glColor3ubv (at); +// glColor3ubv ((byte *)&d_8to24table[(int)p->color]); + glTexCoord2f (0,0); + glVertex3fv (p->org); + glTexCoord2f (1,0); + glVertex3f (p->org[0] + up[0]*scale, p->org[1] + up[1]*scale, p->org[2] + up[2]*scale); + glTexCoord2f (0,1); + glVertex3f (p->org[0] + right[0]*scale, p->org[1] + right[1]*scale, p->org[2] + right[2]*scale); + +#else + D_DrawParticle (p); +#endif + + p->org[0] += p->vel[0]*frametime; + p->org[1] += p->vel[1]*frametime; + p->org[2] += p->vel[2]*frametime; + + switch (p->type) + { + case pt_static: + break; + case pt_fire: + p->ramp += time1; + if (p->ramp >= 6) + p->die = -1; + else + p->color = ramp3[(int)p->ramp]; + p->vel[2] += grav; + break; + + case pt_explode: + p->ramp += time2; + if (p->ramp >=8) + p->die = -1; + else + p->color = ramp1[(int)p->ramp]; + for (i=0 ; i<3 ; i++) + p->vel[i] += p->vel[i]*dvel; + p->vel[2] -= grav; + break; + + case pt_explode2: + p->ramp += time3; + if (p->ramp >=8) + p->die = -1; + else + p->color = ramp2[(int)p->ramp]; + for (i=0 ; i<3 ; i++) + p->vel[i] -= p->vel[i]*frametime; + p->vel[2] -= grav; + break; + + case pt_blob: + for (i=0 ; i<3 ; i++) + p->vel[i] += p->vel[i]*dvel; + p->vel[2] -= grav; + break; + + case pt_blob2: + for (i=0 ; i<2 ; i++) + p->vel[i] -= p->vel[i]*dvel; + p->vel[2] -= grav; + break; + + case pt_slowgrav: + case pt_grav: + p->vel[2] -= grav; + break; + } + } + +#ifdef GLQUAKE + glEnd (); + glDisable (GL_BLEND); + if (alphaTestEnabled) + glEnable(GL_ALPHA_TEST); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); +#else + D_EndParticles (); +#endif +} + diff --git a/source/r_shared.h b/source/r_shared.h index 53b2ea29..3234cb22 100644 --- a/source/r_shared.h +++ b/source/r_shared.h @@ -1,154 +1,154 @@ -/* -Copyright (C) 1996-1997 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 GLQUAKE -// r_shared.h: general refresh-related stuff shared between the refresh and the -// driver - -// FIXME: clean up and move into d_iface.h - -#ifndef _R_SHARED_H_ -#define _R_SHARED_H_ - -#define MAXVERTS 16 // max points in a surface polygon -#define MAXWORKINGVERTS (MAXVERTS+4) // max points in an intermediate - // polygon (while processing) -// !!! if this is changed, it must be changed in d_ifacea.h too !!! -#define MAXHEIGHT 1024 -#define MAXWIDTH 1280 - -#define INFINITE_DISTANCE 0x10000 // distance that's always guaranteed to - // be farther away than anything in - // the scene - -//=================================================================== - -extern void R_DrawLine (polyvert_t *polyvert0, polyvert_t *polyvert1); - -extern int cachewidth; -extern pixel_t *cacheblock; -extern int screenwidth; - -extern float pixelAspect; - -extern int r_drawnpolycount; - -extern cvar_t r_clearcolor; - -extern int sintable[1280]; -extern int intsintable[1280]; - -extern vec3_t vup, base_vup; -extern vec3_t vpn, base_vpn; -extern vec3_t vright, base_vright; -extern entity_t *currententity; - -#define NUMSTACKEDGES 2000 -#define MINEDGES NUMSTACKEDGES -#define NUMSTACKSURFACES 1000 -#define MINSURFACES NUMSTACKSURFACES -#define MAXSPANS 3000 - -// !!! if this is changed, it must be changed in asm_draw.h too !!! -typedef struct espan_s -{ - int u, v, count; - struct espan_s *pnext; -} espan_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) - int 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 - void *data; // associated data like msurface_t - 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; - -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 vec3_t modelorg, base_modelorg; - -extern float xcenter, ycenter; -extern float xscale, yscale; -extern float xscaleinv, yscaleinv; -extern float xscaleshrink, yscaleshrink; - -extern int d_lightstylevalue[256]; // 8.8 frac of base light value - -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 r_skymade; -extern void R_MakeSky (void); - -extern int ubasestep, errorterm, erroradjustup, erroradjustdown; - -// 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 -// !!! if this is changed, it must be changed in d_ifacea.h too !!! -#define ALIAS_ONSEAM 0x0020 // also defined in modelgen.h; - // must be kept in sync -#define ALIAS_XY_CLIP_MASK 0x000F - -// !!! if this is changed, it must be changed in asm_draw.h too !!! -typedef struct edge_s -{ - fixed16_t u; - fixed16_t u_step; - struct edge_s *prev, *next; - unsigned short surfs[2]; - struct edge_s *nextremove; - float nearzi; - medge_t *owner; -} edge_t; - -#endif // _R_SHARED_H_ - -#endif // GLQUAKE +/* +Copyright (C) 1996-1997 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 GLQUAKE +// r_shared.h: general refresh-related stuff shared between the refresh and the +// driver + +// FIXME: clean up and move into d_iface.h + +#ifndef _R_SHARED_H_ +#define _R_SHARED_H_ + +#define MAXVERTS 16 // max points in a surface polygon +#define MAXWORKINGVERTS (MAXVERTS+4) // max points in an intermediate + // polygon (while processing) +// !!! if this is changed, it must be changed in d_ifacea.h too !!! +#define MAXHEIGHT 1024 +#define MAXWIDTH 1280 + +#define INFINITE_DISTANCE 0x10000 // distance that's always guaranteed to + // be farther away than anything in + // the scene + +//=================================================================== + +extern void R_DrawLine (polyvert_t *polyvert0, polyvert_t *polyvert1); + +extern int cachewidth; +extern pixel_t *cacheblock; +extern int screenwidth; + +extern float pixelAspect; + +extern int r_drawnpolycount; + +extern cvar_t r_clearcolor; + +extern int sintable[1280]; +extern int intsintable[1280]; + +extern vec3_t vup, base_vup; +extern vec3_t vpn, base_vpn; +extern vec3_t vright, base_vright; +extern entity_t *currententity; + +#define NUMSTACKEDGES 2000 +#define MINEDGES NUMSTACKEDGES +#define NUMSTACKSURFACES 1000 +#define MINSURFACES NUMSTACKSURFACES +#define MAXSPANS 3000 + +// !!! if this is changed, it must be changed in asm_draw.h too !!! +typedef struct espan_s +{ + int u, v, count; + struct espan_s *pnext; +} espan_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) + int 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 + void *data; // associated data like msurface_t + 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; + +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 vec3_t modelorg, base_modelorg; + +extern float xcenter, ycenter; +extern float xscale, yscale; +extern float xscaleinv, yscaleinv; +extern float xscaleshrink, yscaleshrink; + +extern int d_lightstylevalue[256]; // 8.8 frac of base light value + +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 r_skymade; +extern void R_MakeSky (void); + +extern int ubasestep, errorterm, erroradjustup, erroradjustdown; + +// 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 +// !!! if this is changed, it must be changed in d_ifacea.h too !!! +#define ALIAS_ONSEAM 0x0020 // also defined in modelgen.h; + // must be kept in sync +#define ALIAS_XY_CLIP_MASK 0x000F + +// !!! if this is changed, it must be changed in asm_draw.h too !!! +typedef struct edge_s +{ + fixed16_t u; + fixed16_t u_step; + struct edge_s *prev, *next; + unsigned short surfs[2]; + struct edge_s *nextremove; + float nearzi; + medge_t *owner; +} edge_t; + +#endif // _R_SHARED_H_ + +#endif // GLQUAKE diff --git a/source/r_sky.c b/source/r_sky.c index b35774fd..1c516d19 100644 --- a/source/r_sky.c +++ b/source/r_sky.c @@ -1,280 +1,280 @@ -/* -Copyright (C) 1996-1997 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_sky.c - -#include "quakedef.h" -#include "r_local.h" -#include "d_local.h" - - -int iskyspeed = 8; -int iskyspeed2 = 2; -float skyspeed, skyspeed2; - -float skyshift; - -byte *r_skysource; - -int r_skymade; -int r_skydirect; // not used? - - -// TODO: clean up these routines - -byte bottomsky[128*131]; -byte bottommask[128*131]; -byte newsky[128*256]; // newsky and topsky both pack in here, 128 bytes - // of newsky on the left of each scan, 128 bytes - // of topsky on the right, because the low-level - // drawers need 256-byte scan widths - - -/* -============= -R_InitSky - -A sky texture is 256*128, with the right side being a masked overlay -============== -*/ -void R_InitSky (texture_t *mt) -{ - int i, j; - byte *src; - - src = (byte *)mt + mt->offsets[0]; - - for (i=0 ; i<128 ; i++) - { - for (j=0 ; j<128 ; j++) - { - newsky[(i*256) + j + 128] = src[i*256 + j + 128]; - } - } - - for (i=0 ; i<128 ; i++) - { - for (j=0 ; j<131 ; j++) - { - if (src[i*256 + (j & 0x7F)]) - { - bottomsky[(i*131) + j] = src[i*256 + (j & 0x7F)]; - bottommask[(i*131) + j] = 0; - } - else - { - bottomsky[(i*131) + j] = 0; - bottommask[(i*131) + j] = 0xff; - } - } - } - - r_skysource = newsky; -} - - -/* -================= -R_MakeSky -================= -*/ -void R_MakeSky (void) -{ - int x, y; - int ofs, baseofs; - int xshift, yshift; - unsigned *pnewsky; - static int xlast = -1, ylast = -1; - - xshift = skyshift; - yshift = skyshift; - - if ((xshift == xlast) && (yshift == ylast)) - return; - - xlast = xshift; - ylast = yshift; - - pnewsky = (unsigned *)&newsky[0]; - - for (y=0 ; yoffsets[0]; + + for (i=0 ; i<128 ; i++) + { + for (j=0 ; j<128 ; j++) + { + newsky[(i*256) + j + 128] = src[i*256 + j + 128]; + } + } + + for (i=0 ; i<128 ; i++) + { + for (j=0 ; j<131 ; j++) + { + if (src[i*256 + (j & 0x7F)]) + { + bottomsky[(i*131) + j] = src[i*256 + (j & 0x7F)]; + bottommask[(i*131) + j] = 0; + } + else + { + bottomsky[(i*131) + j] = 0; + bottommask[(i*131) + j] = 0xff; + } + } + } + + r_skysource = newsky; +} + + +/* +================= +R_MakeSky +================= +*/ +void R_MakeSky (void) +{ + int x, y; + int ofs, baseofs; + int xshift, yshift; + unsigned *pnewsky; + static int xlast = -1, ylast = -1; + + xshift = skyshift; + yshift = skyshift; + + if ((xshift == xlast) && (yshift == ylast)) + return; + + xlast = xshift; + ylast = yshift; + + pnewsky = (unsigned *)&newsky[0]; + + for (y=0 ; ydist; - pclipnormal = pclipplane->normal; - -// calc dists - if (clip_current) - { - in = clip_verts[1][0]; - outstep = clip_verts[0][0]; - clip_current = 0; - } - else - { - in = clip_verts[0][0]; - outstep = 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 (float); - 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 (float); - - 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 (float); - outcount++; - } - - return outcount; -} - - -/* -================ -R_SetupAndDrawSprite -================ -*/ -void R_SetupAndDrawSprite () -{ - int i, nump; - float dot, scale, *pv; - vec5_t *pverts; - vec3_t left, up, right, down, transformed, local; - emitpoint_t outverts[MAXWORKINGVERTS+1], *pout; - - dot = DotProduct (r_spritedesc.vpn, modelorg); - -// backface cull - if (dot >= 0) - return; - -// build the sprite poster in worldspace - VectorScale (r_spritedesc.vright, r_spritedesc.pspriteframe->right, right); - VectorScale (r_spritedesc.vup, r_spritedesc.pspriteframe->up, up); - VectorScale (r_spritedesc.vright, r_spritedesc.pspriteframe->left, left); - VectorScale (r_spritedesc.vup, r_spritedesc.pspriteframe->down, down); - - pverts = 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] = sprite_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] = sprite_width; - pverts[2][4] = sprite_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] = sprite_height; - -// clip to the frustum in worldspace - nump = 4; - clip_current = 0; - - for (i=0 ; i<4 ; i++) - { - nump = R_ClipSpriteFace (nump, &view_clipplanes[i]); - if (nump < 3) - return; - if (nump >= MAXWORKINGVERTS) - Sys_Error("R_SetupAndDrawSprite: too many points"); - } - -// transform vertices into viewspace and project - pv = &clip_verts[clip_current][0][0]; - r_spritedesc.nearzi = -999999; - - for (i=0 ; izi = 1.0 / transformed[2]; - if (pout->zi > r_spritedesc.nearzi) - r_spritedesc.nearzi = pout->zi; - - 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 (pv); - } - -// draw it - r_spritedesc.nump = nump; - r_spritedesc.pverts = outverts; - D_DrawSprite (); -} - - -/* -================ -R_GetSpriteframe -================ -*/ -mspriteframe_t *R_GetSpriteframe (msprite_t *psprite) -{ - mspritegroup_t *pspritegroup; - mspriteframe_t *pspriteframe; - int i, numframes, frame; - float *pintervals, fullinterval, targettime, time; - - frame = currententity->frame; - - if ((frame >= psprite->numframes) || (frame < 0)) - { - Con_Printf ("R_DrawSprite: no such frame %d\n", frame); - frame = 0; - } - - if (psprite->frames[frame].type == SPR_SINGLE) - { - pspriteframe = psprite->frames[frame].frameptr; - } - else - { - pspritegroup = (mspritegroup_t *)psprite->frames[frame].frameptr; - pintervals = pspritegroup->intervals; - numframes = pspritegroup->numframes; - fullinterval = pintervals[numframes-1]; - - time = cl.time + currententity->syncbase; - - // when loading in Mod_LoadSpriteGroup, we guaranteed all interval values - // are positive, so we don't have to worry about division by 0 - targettime = time - ((int)(time / fullinterval)) * fullinterval; - - for (i=0 ; i<(numframes-1) ; i++) - { - if (pintervals[i] > targettime) - break; - } - - pspriteframe = pspritegroup->frames[i]; - } - - return pspriteframe; -} - - -/* -================ -R_DrawSprite -================ -*/ -void R_DrawSprite (void) -{ - int i; - msprite_t *psprite; - vec3_t tvec; - float dot, angle, sr, cr; - - psprite = currententity->model->cache.data; - - r_spritedesc.pspriteframe = R_GetSpriteframe (psprite); - - sprite_width = r_spritedesc.pspriteframe->width; - sprite_height = r_spritedesc.pspriteframe->height; - -// TODO: make this caller-selectable - if (psprite->type == SPR_FACING_UPRIGHT) - { - // generate the sprite's axes, with vup straight up in worldspace, and - // r_spritedesc.vright perpendicular to modelorg. - // This will not work if the view direction is very close to straight up or - // down, because the cross product will be between two nearly parallel - // vectors and starts to approach an undefined state, so we don't draw if - // the two vectors are less than 1 degree apart - tvec[0] = -modelorg[0]; - tvec[1] = -modelorg[1]; - tvec[2] = -modelorg[2]; - VectorNormalize (tvec); - dot = tvec[2]; // same as DotProduct (tvec, r_spritedesc.vup) because - // r_spritedesc.vup is 0, 0, 1 - if ((dot > 0.999848) || (dot < -0.999848)) // cos(1 degree) = 0.999848 - return; - r_spritedesc.vup[0] = 0; - r_spritedesc.vup[1] = 0; - r_spritedesc.vup[2] = 1; - r_spritedesc.vright[0] = tvec[1]; - // CrossProduct(r_spritedesc.vup, -modelorg, - r_spritedesc.vright[1] = -tvec[0]; - // r_spritedesc.vright) - r_spritedesc.vright[2] = 0; - VectorNormalize (r_spritedesc.vright); - r_spritedesc.vpn[0] = -r_spritedesc.vright[1]; - r_spritedesc.vpn[1] = r_spritedesc.vright[0]; - r_spritedesc.vpn[2] = 0; - // CrossProduct (r_spritedesc.vright, r_spritedesc.vup, - // r_spritedesc.vpn) - } - else if (psprite->type == SPR_VP_PARALLEL) - { - // generate the sprite's axes, completely parallel to the viewplane. There - // are no problem situations, because the sprite is always in the same - // position relative to the viewer - for (i=0 ; i<3 ; i++) - { - r_spritedesc.vup[i] = vup[i]; - r_spritedesc.vright[i] = vright[i]; - r_spritedesc.vpn[i] = vpn[i]; - } - } - else if (psprite->type == SPR_VP_PARALLEL_UPRIGHT) - { - // generate the sprite's axes, with vup straight up in worldspace, and - // r_spritedesc.vright parallel to the viewplane. - // This will not work if the view direction is very close to straight up or - // down, because the cross product will be between two nearly parallel - // vectors and starts to approach an undefined state, so we don't draw if - // the two vectors are less than 1 degree apart - dot = vpn[2]; // same as DotProduct (vpn, r_spritedesc.vup) because - // r_spritedesc.vup is 0, 0, 1 - if ((dot > 0.999848) || (dot < -0.999848)) // cos(1 degree) = 0.999848 - return; - r_spritedesc.vup[0] = 0; - r_spritedesc.vup[1] = 0; - r_spritedesc.vup[2] = 1; - r_spritedesc.vright[0] = vpn[1]; - // CrossProduct (r_spritedesc.vup, vpn, - r_spritedesc.vright[1] = -vpn[0]; // r_spritedesc.vright) - r_spritedesc.vright[2] = 0; - VectorNormalize (r_spritedesc.vright); - r_spritedesc.vpn[0] = -r_spritedesc.vright[1]; - r_spritedesc.vpn[1] = r_spritedesc.vright[0]; - r_spritedesc.vpn[2] = 0; - // CrossProduct (r_spritedesc.vright, r_spritedesc.vup, - // r_spritedesc.vpn) - } - else if (psprite->type == SPR_ORIENTED) - { - // generate the sprite's axes, according to the sprite's world orientation - AngleVectors (currententity->angles, r_spritedesc.vpn, - r_spritedesc.vright, r_spritedesc.vup); - } - else if (psprite->type == SPR_VP_PARALLEL_ORIENTED) - { - // generate the sprite's axes, parallel to the viewplane, but rotated in - // that plane around the center according to the sprite entity's roll - // angle. So vpn stays the same, but vright and vup rotate - angle = currententity->angles[ROLL] * (M_PI*2 / 360); - sr = sin(angle); - cr = cos(angle); - - for (i=0 ; i<3 ; i++) - { - r_spritedesc.vpn[i] = vpn[i]; - r_spritedesc.vright[i] = vright[i] * cr + vup[i] * sr; - r_spritedesc.vup[i] = vright[i] * -sr + vup[i] * cr; - } - } - else - { - Sys_Error ("R_DrawSprite: Bad sprite type %d", psprite->type); - } - - R_RotateSprite (psprite->beamlength); - - R_SetupAndDrawSprite (); -} - +/* +Copyright (C) 1996-1997 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 "quakedef.h" +#include "r_local.h" + +static int clip_current; +static vec5_t clip_verts[2][MAXWORKINGVERTS]; +static int sprite_width, sprite_height; + +spritedesc_t r_spritedesc; + + +/* +================ +R_RotateSprite +================ +*/ +void R_RotateSprite (float beamlength) +{ + vec3_t vec; + + if (beamlength == 0.0) + return; + + VectorScale (r_spritedesc.vpn, -beamlength, vec); + VectorAdd (r_entorigin, vec, r_entorigin); + VectorSubtract (modelorg, vec, modelorg); +} + + +/* +============= +R_ClipSpriteFace + +Clips the winding at clip_verts[clip_current] and changes clip_current +Throws out the back side +============== +*/ +int R_ClipSpriteFace (int nump, clipplane_t *pclipplane) +{ + int i, outcount; + float dists[MAXWORKINGVERTS+1]; + float frac, clipdist, *pclipnormal; + float *in, *instep, *outstep, *vert2; + + clipdist = pclipplane->dist; + pclipnormal = pclipplane->normal; + +// calc dists + if (clip_current) + { + in = clip_verts[1][0]; + outstep = clip_verts[0][0]; + clip_current = 0; + } + else + { + in = clip_verts[0][0]; + outstep = 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 (float); + 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 (float); + + 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 (float); + outcount++; + } + + return outcount; +} + + +/* +================ +R_SetupAndDrawSprite +================ +*/ +void R_SetupAndDrawSprite () +{ + int i, nump; + float dot, scale, *pv; + vec5_t *pverts; + vec3_t left, up, right, down, transformed, local; + emitpoint_t outverts[MAXWORKINGVERTS+1], *pout; + + dot = DotProduct (r_spritedesc.vpn, modelorg); + +// backface cull + if (dot >= 0) + return; + +// build the sprite poster in worldspace + VectorScale (r_spritedesc.vright, r_spritedesc.pspriteframe->right, right); + VectorScale (r_spritedesc.vup, r_spritedesc.pspriteframe->up, up); + VectorScale (r_spritedesc.vright, r_spritedesc.pspriteframe->left, left); + VectorScale (r_spritedesc.vup, r_spritedesc.pspriteframe->down, down); + + pverts = 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] = sprite_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] = sprite_width; + pverts[2][4] = sprite_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] = sprite_height; + +// clip to the frustum in worldspace + nump = 4; + clip_current = 0; + + for (i=0 ; i<4 ; i++) + { + nump = R_ClipSpriteFace (nump, &view_clipplanes[i]); + if (nump < 3) + return; + if (nump >= MAXWORKINGVERTS) + Sys_Error("R_SetupAndDrawSprite: too many points"); + } + +// transform vertices into viewspace and project + pv = &clip_verts[clip_current][0][0]; + r_spritedesc.nearzi = -999999; + + for (i=0 ; izi = 1.0 / transformed[2]; + if (pout->zi > r_spritedesc.nearzi) + r_spritedesc.nearzi = pout->zi; + + 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 (pv); + } + +// draw it + r_spritedesc.nump = nump; + r_spritedesc.pverts = outverts; + D_DrawSprite (); +} + + +/* +================ +R_GetSpriteframe +================ +*/ +mspriteframe_t *R_GetSpriteframe (msprite_t *psprite) +{ + mspritegroup_t *pspritegroup; + mspriteframe_t *pspriteframe; + int i, numframes, frame; + float *pintervals, fullinterval, targettime, time; + + frame = currententity->frame; + + if ((frame >= psprite->numframes) || (frame < 0)) + { + Con_Printf ("R_DrawSprite: no such frame %d\n", frame); + frame = 0; + } + + if (psprite->frames[frame].type == SPR_SINGLE) + { + pspriteframe = psprite->frames[frame].frameptr; + } + else + { + pspritegroup = (mspritegroup_t *)psprite->frames[frame].frameptr; + pintervals = pspritegroup->intervals; + numframes = pspritegroup->numframes; + fullinterval = pintervals[numframes-1]; + + time = cl.time + currententity->syncbase; + + // when loading in Mod_LoadSpriteGroup, we guaranteed all interval values + // are positive, so we don't have to worry about division by 0 + targettime = time - ((int)(time / fullinterval)) * fullinterval; + + for (i=0 ; i<(numframes-1) ; i++) + { + if (pintervals[i] > targettime) + break; + } + + pspriteframe = pspritegroup->frames[i]; + } + + return pspriteframe; +} + + +/* +================ +R_DrawSprite +================ +*/ +void R_DrawSprite (void) +{ + int i; + msprite_t *psprite; + vec3_t tvec; + float dot, angle, sr, cr; + + psprite = currententity->model->cache.data; + + r_spritedesc.pspriteframe = R_GetSpriteframe (psprite); + + sprite_width = r_spritedesc.pspriteframe->width; + sprite_height = r_spritedesc.pspriteframe->height; + +// TODO: make this caller-selectable + if (psprite->type == SPR_FACING_UPRIGHT) + { + // generate the sprite's axes, with vup straight up in worldspace, and + // r_spritedesc.vright perpendicular to modelorg. + // This will not work if the view direction is very close to straight up or + // down, because the cross product will be between two nearly parallel + // vectors and starts to approach an undefined state, so we don't draw if + // the two vectors are less than 1 degree apart + tvec[0] = -modelorg[0]; + tvec[1] = -modelorg[1]; + tvec[2] = -modelorg[2]; + VectorNormalize (tvec); + dot = tvec[2]; // same as DotProduct (tvec, r_spritedesc.vup) because + // r_spritedesc.vup is 0, 0, 1 + if ((dot > 0.999848) || (dot < -0.999848)) // cos(1 degree) = 0.999848 + return; + r_spritedesc.vup[0] = 0; + r_spritedesc.vup[1] = 0; + r_spritedesc.vup[2] = 1; + r_spritedesc.vright[0] = tvec[1]; + // CrossProduct(r_spritedesc.vup, -modelorg, + r_spritedesc.vright[1] = -tvec[0]; + // r_spritedesc.vright) + r_spritedesc.vright[2] = 0; + VectorNormalize (r_spritedesc.vright); + r_spritedesc.vpn[0] = -r_spritedesc.vright[1]; + r_spritedesc.vpn[1] = r_spritedesc.vright[0]; + r_spritedesc.vpn[2] = 0; + // CrossProduct (r_spritedesc.vright, r_spritedesc.vup, + // r_spritedesc.vpn) + } + else if (psprite->type == SPR_VP_PARALLEL) + { + // generate the sprite's axes, completely parallel to the viewplane. There + // are no problem situations, because the sprite is always in the same + // position relative to the viewer + for (i=0 ; i<3 ; i++) + { + r_spritedesc.vup[i] = vup[i]; + r_spritedesc.vright[i] = vright[i]; + r_spritedesc.vpn[i] = vpn[i]; + } + } + else if (psprite->type == SPR_VP_PARALLEL_UPRIGHT) + { + // generate the sprite's axes, with vup straight up in worldspace, and + // r_spritedesc.vright parallel to the viewplane. + // This will not work if the view direction is very close to straight up or + // down, because the cross product will be between two nearly parallel + // vectors and starts to approach an undefined state, so we don't draw if + // the two vectors are less than 1 degree apart + dot = vpn[2]; // same as DotProduct (vpn, r_spritedesc.vup) because + // r_spritedesc.vup is 0, 0, 1 + if ((dot > 0.999848) || (dot < -0.999848)) // cos(1 degree) = 0.999848 + return; + r_spritedesc.vup[0] = 0; + r_spritedesc.vup[1] = 0; + r_spritedesc.vup[2] = 1; + r_spritedesc.vright[0] = vpn[1]; + // CrossProduct (r_spritedesc.vup, vpn, + r_spritedesc.vright[1] = -vpn[0]; // r_spritedesc.vright) + r_spritedesc.vright[2] = 0; + VectorNormalize (r_spritedesc.vright); + r_spritedesc.vpn[0] = -r_spritedesc.vright[1]; + r_spritedesc.vpn[1] = r_spritedesc.vright[0]; + r_spritedesc.vpn[2] = 0; + // CrossProduct (r_spritedesc.vright, r_spritedesc.vup, + // r_spritedesc.vpn) + } + else if (psprite->type == SPR_ORIENTED) + { + // generate the sprite's axes, according to the sprite's world orientation + AngleVectors (currententity->angles, r_spritedesc.vpn, + r_spritedesc.vright, r_spritedesc.vup); + } + else if (psprite->type == SPR_VP_PARALLEL_ORIENTED) + { + // generate the sprite's axes, parallel to the viewplane, but rotated in + // that plane around the center according to the sprite entity's roll + // angle. So vpn stays the same, but vright and vup rotate + angle = currententity->angles[ROLL] * (M_PI*2 / 360); + sr = sin(angle); + cr = cos(angle); + + for (i=0 ; i<3 ; i++) + { + r_spritedesc.vpn[i] = vpn[i]; + r_spritedesc.vright[i] = vright[i] * cr + vup[i] * sr; + r_spritedesc.vup[i] = vright[i] * -sr + vup[i] * cr; + } + } + else + { + Sys_Error ("R_DrawSprite: Bad sprite type %d", psprite->type); + } + + R_RotateSprite (psprite->beamlength); + + R_SetupAndDrawSprite (); +} + diff --git a/source/r_surf.c b/source/r_surf.c index 73dac7e7..6a8fb123 100644 --- a/source/r_surf.c +++ b/source/r_surf.c @@ -1,811 +1,811 @@ -/* -Copyright (C) 1996-1997 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 "quakedef.h" -#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 -}; - - - -unsigned blocklights[18*18]; - -#if 0 -/* -=============== -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; - - surf = r_drawsurf.surf; - smax = (surf->extents[0]>>4)+1; - tmax = (surf->extents[1]>>4)+1; - tex = surf->texinfo; - - for (lnum=0 ; lnumdlightbits & (1<plane->normal) - - surf->plane->dist; - rad -= fabs(dist); - minlight = cl_dlights[lnum].minlight; - if (rad < minlight) - continue; - minlight = rad - minlight; - - for (i=0 ; i<3 ; i++) - { - impact[i] = cl_dlights[lnum].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); - if (dist < minlight) - blocklights[t*smax + s] += (rad - dist)*256; - } - } - } -} -#endif - - -typedef struct dlightinfo_s { - int local[2]; - int rad; - int minlight; // rad - minlight -} dlightinfo_t; - -static dlightinfo_t dlightlist[MAX_DLIGHTS]; -static int numdlights; - -void R_BuildDLightList (void) -{ - msurface_t *surf; - int lnum; - float dist; - vec3_t impact; - int i; - int smax, tmax; - mtexinfo_t *tex; - int irad, iminlight; - int local[2]; - int tdmin, sdmin, distmin; - dlightinfo_t *light; - - numdlights = 0; - - surf = r_drawsurf.surf; - smax = (surf->extents[0]>>4)+1; - tmax = (surf->extents[1]>>4)+1; - tex = surf->texinfo; - - for (lnum=0 ; lnumdlightbits & (1<plane->normal) - - surf->plane->dist; - irad = (cl_dlights[lnum].radius - fabs(dist)) * 256; - iminlight = cl_dlights[lnum].minlight * 256; - if (irad < iminlight) - continue; - - iminlight = irad - iminlight; - - for (i=0 ; i<3 ; i++) { - impact[i] = cl_dlights[lnum].origin[i] - - surf->plane->normal[i]*dist; - } - - local[0] = DotProduct (impact, tex->vecs[0]) + - tex->vecs[0][3] - surf->texturemins[0]; - local[1] = DotProduct (impact, tex->vecs[1]) + - tex->vecs[1][3] - surf->texturemins[1]; - - // check if this dlight will touch the surface - if (local[1] > 0) { - tdmin = local[1] - (tmax<<4); - if (tdmin < 0) - tdmin = 0; - } else - tdmin = -local[1]; - - if (local[0] > 0) { - sdmin = local[0] - (smax<<4); - if (sdmin < 0) - sdmin = 0; - } else - sdmin = -local[0]; - - if (sdmin > tdmin) - distmin = (sdmin<<8) + (tdmin<<7); - else - distmin = (tdmin<<8) + (sdmin<<7); - - if (distmin < iminlight) - { - // save dlight info - light = &dlightlist[numdlights]; - light->minlight = iminlight; - light->rad = irad; - light->local[0] = local[0]; - light->local[1] = local[1]; - numdlights++; - } - } -} - -void R_AddDynamicLights (void) -{ - int i; - int smax, tmax; - int s, t; - int sd, td; - int _sd, _td; - int local[2]; - int irad, idist, iminlight; - dlightinfo_t *light; - unsigned *dest; - - smax = (r_drawsurf.surf->extents[0]>>4)+1; - tmax = (r_drawsurf.surf->extents[1]>>4)+1; - - for (i=0,light=dlightlist ; irad; - iminlight = light->minlight; - local[0] = light->local[0]; -// local[1] = light->local[1]; - - _td = light->local[1]; - dest = blocklights; - for (t = 0 ; t td) - idist = (sd<<8) + (td<<7); - else - idist = (td<<8) + (sd<<7); - if (idist < iminlight) - *dest += irad - idist; - dest++; - } - } - } -} - -/* -=============== -R_BuildLightMap - -Combine and scale multiple lightmaps into the 8.8 format in blocklights -=============== -*/ -void R_BuildLightMap (void) -{ - int smax, tmax; - int t; - int i, size; - byte *lightmap; - unsigned scale; - int maps; - msurface_t *surf; - - surf = r_drawsurf.surf; - - smax = (surf->extents[0]>>4)+1; - tmax = (surf->extents[1]>>4)+1; - size = smax*tmax; - lightmap = surf->samples; - - if (/* r_fullbright.value || */ !cl.worldmodel->lightdata) - { - for (i=0 ; istyles[maps] != 255 ; - maps++) - { - scale = r_drawsurf.lightadj[maps]; // 8.8 fraction - for (i=0 ; i> (8 - VID_CBITS); - - if (t < (1 << 6)) - t = (1 << 6); - - blocklights[i] = t; - } -} - - -/* -=============== -R_TextureAnimation - -Returns the proper texture for a given time and base texture -=============== -*/ -texture_t *R_TextureAnimation (texture_t *base) -{ - int relative; - int count; - - if (currententity->frame) - { - if (base->alternate_anims) - base = base->alternate_anims; - } - - if (!base->anim_total) - return base; - - relative = (int)(cl.time*10) % base->anim_total; - - count = 0; - while (base->anim_min > relative || base->anim_max <= relative) - { - base = base->anim_next; - if (!base) - Sys_Error ("R_TextureAnimation: broken cycle"); - if (++count > 100) - Sys_Error ("R_TextureAnimation: infinite cycle"); - } - - return base; -} - - -/* -=============== -R_DrawSurface -=============== -Returns false if it wasn't hit by a dynamic light -*/ -qboolean R_DrawSurface (void) -{ - unsigned char *basetptr; - int smax, tmax, twidth; - int u; - int soffset, basetoffset, texwidth; - int horzblockstep; - unsigned char *pcolumndest; - void (*pblockdrawer)(void); - texture_t *mt; - -// build a list of dlights that touch this surface - if (r_drawsurf.surf->dlightframe == r_framecount) - R_BuildDLightList (); - else - numdlights = 0; - - if (!numdlights) { - if (r_drawsurf.dlightonly) - return false; // not hit by a dlight, so no need to redraw it - } - -// calculate the lightings - R_BuildLightMap (); - - surfrowbytes = r_drawsurf.rowbytes; - - mt = r_drawsurf.texture; - - r_source = (byte *)mt + mt->offsets[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; - -//============================== - - if (r_pixbytes == 1) - { - pblockdrawer = surfmiptable[r_drawsurf.surfmip]; - // TODO: only needs to be set when there is a display settings change - horzblockstep = blocksize; - } - else - { - pblockdrawer = R_DrawSurfaceBlock16; - // TODO: only needs to be set when there is a display settings change - horzblockstep = blocksize << 1; - } - - 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; - } - return (numdlights != 0); -} - - -//============================================================================= - -#if !id386 - -/* -================ -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_DrawSurfaceBlock16 - -FIXME: make this work -================ -*/ -void R_DrawSurfaceBlock16 (void) -{ - int k; - unsigned char *psource; - int lighttemp, lightstep, light; - unsigned short *prowdest; - - prowdest = (unsigned short *)prowdestbase; - - for (k=0 ; k> blockdivshift; - - light = lightleft; - pdest = prowdest; - - for (b=0; b> 16) & 63; - t = (((i << 16) + turb[j & (CYCLE-1)]) >> 16) & 63; - *pd++ = *(pbasetex + (t<<6) + s); - } - } -} - - -/* -================ -R_GenTurbTile16 -================ -*/ -void R_GenTurbTile16 (pixel_t *pbasetex, void *pdest) -{ - int *turb; - int i, j, s, t; - unsigned short *pd; - - turb = sintable + ((int)(cl.time*SPEED)&(CYCLE-1)); - pd = (unsigned short *)pdest; - - for (i=0 ; i> 16) & 63; - t = (((i << 16) + turb[j & (CYCLE-1)]) >> 16) & 63; - *pd++ = d_8to16table[*(pbasetex + (t<<6) + s)]; - } - } -} - - -/* -================ -R_GenTile -================ -*/ -void R_GenTile (msurface_t *psurf, void *pdest) -{ - if (psurf->flags & SURF_DRAWTURB) - { - if (r_pixbytes == 1) - { - R_GenTurbTile ((pixel_t *) - ((byte *)psurf->texinfo->texture + psurf->texinfo->texture->offsets[0]), pdest); - } - else - { - R_GenTurbTile16 ((pixel_t *) - ((byte *)psurf->texinfo->texture + psurf->texinfo->texture->offsets[0]), pdest); - } - } - else if (psurf->flags & SURF_DRAWSKY) - { - if (r_pixbytes == 1) - { - R_GenSkyTile (pdest); - } - else - { - R_GenSkyTile16 (pdest); - } - } - else - { - Sys_Error ("Unknown tile type"); - } -} - +/* +Copyright (C) 1996-1997 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 "quakedef.h" +#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 +}; + + + +unsigned blocklights[18*18]; + +#if 0 +/* +=============== +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; + + surf = r_drawsurf.surf; + smax = (surf->extents[0]>>4)+1; + tmax = (surf->extents[1]>>4)+1; + tex = surf->texinfo; + + for (lnum=0 ; lnumdlightbits & (1<plane->normal) - + surf->plane->dist; + rad -= fabs(dist); + minlight = cl_dlights[lnum].minlight; + if (rad < minlight) + continue; + minlight = rad - minlight; + + for (i=0 ; i<3 ; i++) + { + impact[i] = cl_dlights[lnum].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); + if (dist < minlight) + blocklights[t*smax + s] += (rad - dist)*256; + } + } + } +} +#endif + + +typedef struct dlightinfo_s { + int local[2]; + int rad; + int minlight; // rad - minlight +} dlightinfo_t; + +static dlightinfo_t dlightlist[MAX_DLIGHTS]; +static int numdlights; + +void R_BuildDLightList (void) +{ + msurface_t *surf; + int lnum; + float dist; + vec3_t impact; + int i; + int smax, tmax; + mtexinfo_t *tex; + int irad, iminlight; + int local[2]; + int tdmin, sdmin, distmin; + dlightinfo_t *light; + + numdlights = 0; + + surf = r_drawsurf.surf; + smax = (surf->extents[0]>>4)+1; + tmax = (surf->extents[1]>>4)+1; + tex = surf->texinfo; + + for (lnum=0 ; lnumdlightbits & (1<plane->normal) - + surf->plane->dist; + irad = (cl_dlights[lnum].radius - fabs(dist)) * 256; + iminlight = cl_dlights[lnum].minlight * 256; + if (irad < iminlight) + continue; + + iminlight = irad - iminlight; + + for (i=0 ; i<3 ; i++) { + impact[i] = cl_dlights[lnum].origin[i] - + surf->plane->normal[i]*dist; + } + + local[0] = DotProduct (impact, tex->vecs[0]) + + tex->vecs[0][3] - surf->texturemins[0]; + local[1] = DotProduct (impact, tex->vecs[1]) + + tex->vecs[1][3] - surf->texturemins[1]; + + // check if this dlight will touch the surface + if (local[1] > 0) { + tdmin = local[1] - (tmax<<4); + if (tdmin < 0) + tdmin = 0; + } else + tdmin = -local[1]; + + if (local[0] > 0) { + sdmin = local[0] - (smax<<4); + if (sdmin < 0) + sdmin = 0; + } else + sdmin = -local[0]; + + if (sdmin > tdmin) + distmin = (sdmin<<8) + (tdmin<<7); + else + distmin = (tdmin<<8) + (sdmin<<7); + + if (distmin < iminlight) + { + // save dlight info + light = &dlightlist[numdlights]; + light->minlight = iminlight; + light->rad = irad; + light->local[0] = local[0]; + light->local[1] = local[1]; + numdlights++; + } + } +} + +void R_AddDynamicLights (void) +{ + int i; + int smax, tmax; + int s, t; + int sd, td; + int _sd, _td; + int local[2]; + int irad, idist, iminlight; + dlightinfo_t *light; + unsigned *dest; + + smax = (r_drawsurf.surf->extents[0]>>4)+1; + tmax = (r_drawsurf.surf->extents[1]>>4)+1; + + for (i=0,light=dlightlist ; irad; + iminlight = light->minlight; + local[0] = light->local[0]; +// local[1] = light->local[1]; + + _td = light->local[1]; + dest = blocklights; + for (t = 0 ; t td) + idist = (sd<<8) + (td<<7); + else + idist = (td<<8) + (sd<<7); + if (idist < iminlight) + *dest += irad - idist; + dest++; + } + } + } +} + +/* +=============== +R_BuildLightMap + +Combine and scale multiple lightmaps into the 8.8 format in blocklights +=============== +*/ +void R_BuildLightMap (void) +{ + int smax, tmax; + int t; + int i, size; + byte *lightmap; + unsigned scale; + int maps; + msurface_t *surf; + + surf = r_drawsurf.surf; + + smax = (surf->extents[0]>>4)+1; + tmax = (surf->extents[1]>>4)+1; + size = smax*tmax; + lightmap = surf->samples; + + if (/* r_fullbright.value || */ !cl.worldmodel->lightdata) + { + for (i=0 ; istyles[maps] != 255 ; + maps++) + { + scale = r_drawsurf.lightadj[maps]; // 8.8 fraction + for (i=0 ; i> (8 - VID_CBITS); + + if (t < (1 << 6)) + t = (1 << 6); + + blocklights[i] = t; + } +} + + +/* +=============== +R_TextureAnimation + +Returns the proper texture for a given time and base texture +=============== +*/ +texture_t *R_TextureAnimation (texture_t *base) +{ + int relative; + int count; + + if (currententity->frame) + { + if (base->alternate_anims) + base = base->alternate_anims; + } + + if (!base->anim_total) + return base; + + relative = (int)(cl.time*10) % base->anim_total; + + count = 0; + while (base->anim_min > relative || base->anim_max <= relative) + { + base = base->anim_next; + if (!base) + Sys_Error ("R_TextureAnimation: broken cycle"); + if (++count > 100) + Sys_Error ("R_TextureAnimation: infinite cycle"); + } + + return base; +} + + +/* +=============== +R_DrawSurface +=============== +Returns false if it wasn't hit by a dynamic light +*/ +qboolean R_DrawSurface (void) +{ + unsigned char *basetptr; + int smax, tmax, twidth; + int u; + int soffset, basetoffset, texwidth; + int horzblockstep; + unsigned char *pcolumndest; + void (*pblockdrawer)(void); + texture_t *mt; + +// build a list of dlights that touch this surface + if (r_drawsurf.surf->dlightframe == r_framecount) + R_BuildDLightList (); + else + numdlights = 0; + + if (!numdlights) { + if (r_drawsurf.dlightonly) + return false; // not hit by a dlight, so no need to redraw it + } + +// calculate the lightings + R_BuildLightMap (); + + surfrowbytes = r_drawsurf.rowbytes; + + mt = r_drawsurf.texture; + + r_source = (byte *)mt + mt->offsets[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; + +//============================== + + if (r_pixbytes == 1) + { + pblockdrawer = surfmiptable[r_drawsurf.surfmip]; + // TODO: only needs to be set when there is a display settings change + horzblockstep = blocksize; + } + else + { + pblockdrawer = R_DrawSurfaceBlock16; + // TODO: only needs to be set when there is a display settings change + horzblockstep = blocksize << 1; + } + + 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; + } + return (numdlights != 0); +} + + +//============================================================================= + +#if !id386 + +/* +================ +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_DrawSurfaceBlock16 + +FIXME: make this work +================ +*/ +void R_DrawSurfaceBlock16 (void) +{ + int k; + unsigned char *psource; + int lighttemp, lightstep, light; + unsigned short *prowdest; + + prowdest = (unsigned short *)prowdestbase; + + for (k=0 ; k> blockdivshift; + + light = lightleft; + pdest = prowdest; + + for (b=0; b> 16) & 63; + t = (((i << 16) + turb[j & (CYCLE-1)]) >> 16) & 63; + *pd++ = *(pbasetex + (t<<6) + s); + } + } +} + + +/* +================ +R_GenTurbTile16 +================ +*/ +void R_GenTurbTile16 (pixel_t *pbasetex, void *pdest) +{ + int *turb; + int i, j, s, t; + unsigned short *pd; + + turb = sintable + ((int)(cl.time*SPEED)&(CYCLE-1)); + pd = (unsigned short *)pdest; + + for (i=0 ; i> 16) & 63; + t = (((i << 16) + turb[j & (CYCLE-1)]) >> 16) & 63; + *pd++ = d_8to16table[*(pbasetex + (t<<6) + s)]; + } + } +} + + +/* +================ +R_GenTile +================ +*/ +void R_GenTile (msurface_t *psurf, void *pdest) +{ + if (psurf->flags & SURF_DRAWTURB) + { + if (r_pixbytes == 1) + { + R_GenTurbTile ((pixel_t *) + ((byte *)psurf->texinfo->texture + psurf->texinfo->texture->offsets[0]), pdest); + } + else + { + R_GenTurbTile16 ((pixel_t *) + ((byte *)psurf->texinfo->texture + psurf->texinfo->texture->offsets[0]), pdest); + } + } + else if (psurf->flags & SURF_DRAWSKY) + { + if (r_pixbytes == 1) + { + R_GenSkyTile (pdest); + } + else + { + R_GenSkyTile16 (pdest); + } + } + else + { + Sys_Error ("Unknown tile type"); + } +} + diff --git a/source/r_vars.c b/source/r_vars.c index 90fe7f55..eb41290e 100644 --- a/source/r_vars.c +++ b/source/r_vars.c @@ -1,39 +1,39 @@ -/* -Copyright (C) 1996-1997 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_vars.c: global refresh variables - -#include "quakedef.h" - -#if !id386 - -// 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 - -int r_bmodelactive; - -#endif // !id386 - +/* +Copyright (C) 1996-1997 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_vars.c: global refresh variables + +#include "quakedef.h" + +#if !id386 + +// 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 + +int r_bmodelactive; + +#endif // !id386 + diff --git a/source/r_varsa.s b/source/r_varsa.s index 45ca4554..e987c1de 100644 --- a/source/r_varsa.s +++ b/source/r_varsa.s @@ -1,69 +1,69 @@ -/* - r_varsa.S - - (description) - - Copyright (C) 1996-1997 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: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - - $Id: r_varsa.s,v 1.1.1.3 2004/10/13 18:54:48 vvd0 Exp $ -*/ - -#include "asm_i386.h" -#include "quakeasm.h" -#include "asm_draw.h" -#include "d_ifacea.h" - -#ifdef id386 - - .data - -//------------------------------------------------------- -// ASM-only variables -//------------------------------------------------------- -.globl float_1, float_particle_z_clip, float_point5 -.globl float_minus_1, float_0 -float_0: .single 0.0 -float_1: .single 1.0 -float_minus_1: .single -1.0 -float_particle_z_clip: .single PARTICLE_Z_CLIP -float_point5: .single 0.5 - -.globl fp_16, fp_64k, fp_1m, fp_64kx64k -.globl fp_1m_minus_1 -.globl fp_8 -fp_1m: .single 1048576.0 -fp_1m_minus_1: .single 1048575.0 -fp_64k: .single 65536.0 -fp_8: .single 8.0 -fp_16: .single 16.0 -fp_64kx64k: .long 0x4f000000 // (float)0x8000*0x10000 - - -.globl FloatZero, Float2ToThe31nd, FloatMinus2ToThe31nd -FloatZero: .long 0 -Float2ToThe31nd: .long 0x4f000000 -FloatMinus2ToThe31nd: .long 0xcf000000 - -.globl C(r_bmodelactive) -C(r_bmodelactive): .long 0 - -#endif // id386 - +/* + r_varsa.S + + (description) + + Copyright (C) 1996-1997 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id: r_varsa.s,v 1.1.1.4 2004/10/18 17:44:38 vvd0 Exp $ +*/ + +#include "asm_i386.h" +#include "quakeasm.h" +#include "asm_draw.h" +#include "d_ifacea.h" + +#ifdef id386 + + .data + +//------------------------------------------------------- +// ASM-only variables +//------------------------------------------------------- +.globl float_1, float_particle_z_clip, float_point5 +.globl float_minus_1, float_0 +float_0: .single 0.0 +float_1: .single 1.0 +float_minus_1: .single -1.0 +float_particle_z_clip: .single PARTICLE_Z_CLIP +float_point5: .single 0.5 + +.globl fp_16, fp_64k, fp_1m, fp_64kx64k +.globl fp_1m_minus_1 +.globl fp_8 +fp_1m: .single 1048576.0 +fp_1m_minus_1: .single 1048575.0 +fp_64k: .single 65536.0 +fp_8: .single 8.0 +fp_16: .single 16.0 +fp_64kx64k: .long 0x4f000000 // (float)0x8000*0x10000 + + +.globl FloatZero, Float2ToThe31nd, FloatMinus2ToThe31nd +FloatZero: .long 0 +Float2ToThe31nd: .long 0x4f000000 +FloatMinus2ToThe31nd: .long 0xcf000000 + +.globl C(r_bmodelactive) +C(r_bmodelactive): .long 0 + +#endif // id386 + diff --git a/source/render.h b/source/render.h index 568c4f5d..94dbee71 100644 --- a/source/render.h +++ b/source/render.h @@ -1,153 +1,153 @@ -/* -Copyright (C) 1996-1997 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. - -*/ - -// refresh.h -- public interface to refresh functions - -#define TOP_RANGE 16 // soldier uniform colors -#define BOTTOM_RANGE 96 - -//============================================================================= - -typedef struct efrag_s -{ - struct mleaf_s *leaf; - struct efrag_s *leafnext; - struct entity_s *entity; - struct efrag_s *entnext; -} efrag_t; - - -typedef struct entity_s -{ - int keynum; // for matching entities in different frames - vec3_t origin; - vec3_t angles; - struct model_s *model; // NULL = no model - int frame; - byte *colormap; - int skinnum; // for Alias models - - struct player_info_s *scoreboard; // identify player - - float syncbase; - - struct efrag_s *efrag; // linked list of efrags (FIXME) - int visframe; // last frame this entity was - // found in an active leaf - // only used for static objects - - int dlightframe; // dynamic lighting - int dlightbits; - -// FIXME: could turn these into a union - int trivial_accept; - struct mnode_s *topnode; // for bmodels, first world node - // that splits bmodel, or NULL if - // not split -} entity_t; - -// !!! if this is changed, it must be changed in asm_draw.h too !!! -typedef struct -{ - vrect_t vrect; // subwindow in video for refresh - // FIXME: not need vrect next field here? - vrect_t aliasvrect; // scaled Alias version - int vrectright, vrectbottom; // right & bottom screen coords - int 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 - int vrect_x_adj_shift20; // (vrect.x + 0.5 - epsilon) << 20 - int 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; - - float fov_x, fov_y; - - int ambientlight; -} refdef_t; - - -// -// refresh -// -extern int reinit_surfcache; - - -extern refdef_t r_refdef; -extern vec3_t r_origin, vpn, vright, vup; - -extern struct texture_s *r_notexture_mip; - -extern entity_t r_worldentity; - -void R_Init (void); -void R_InitTextures (void); -void R_InitEfrags (void); -void R_RenderView (void); // must set r_refdef first -void R_ViewChanged (vrect_t *pvrect, int lineadj, float aspect); - // called whenever r_refdef or vid change -void R_InitSky (struct texture_s *mt); // called at level load - -void R_AddEfrags (entity_t *ent); -void R_RemoveEfrags (entity_t *ent); - -void R_NewMap (void); - - -void R_ParseParticleEffect (void); -void R_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count); -void R_RocketTrail (vec3_t start, vec3_t end, int type); - -void R_EntityParticles (entity_t *ent); -void R_BlobExplosion (vec3_t org); -void R_ParticleExplosion (vec3_t org); -void R_LavaSplash (vec3_t org); -void R_TeleportSplash (vec3_t org); - -void R_PushDlights (void); -void R_InitParticles (void); -void R_ClearParticles (void); -void R_DrawParticles (void); -void R_DrawWaterSurfaces (void); - - -// -// surface cache related -// -extern int reinit_surfcache; // if 1, surface cache is currently empty and -extern qboolean r_cache_thrash; // set if thrashing the surface cache - -int D_SurfaceCacheForRes (int width, int height); -void D_FlushCaches (void); -void D_DeleteSurfaceCache (void); -void D_InitCaches (void *buffer, int size); -void R_SetVrect (vrect_t *pvrect, vrect_t *pvrectin, int lineadj); - +/* +Copyright (C) 1996-1997 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. + +*/ + +// refresh.h -- public interface to refresh functions + +#define TOP_RANGE 16 // soldier uniform colors +#define BOTTOM_RANGE 96 + +//============================================================================= + +typedef struct efrag_s +{ + struct mleaf_s *leaf; + struct efrag_s *leafnext; + struct entity_s *entity; + struct efrag_s *entnext; +} efrag_t; + + +typedef struct entity_s +{ + int keynum; // for matching entities in different frames + vec3_t origin; + vec3_t angles; + struct model_s *model; // NULL = no model + int frame; + byte *colormap; + int skinnum; // for Alias models + + struct player_info_s *scoreboard; // identify player + + float syncbase; + + struct efrag_s *efrag; // linked list of efrags (FIXME) + int visframe; // last frame this entity was + // found in an active leaf + // only used for static objects + + int dlightframe; // dynamic lighting + int dlightbits; + +// FIXME: could turn these into a union + int trivial_accept; + struct mnode_s *topnode; // for bmodels, first world node + // that splits bmodel, or NULL if + // not split +} entity_t; + +// !!! if this is changed, it must be changed in asm_draw.h too !!! +typedef struct +{ + vrect_t vrect; // subwindow in video for refresh + // FIXME: not need vrect next field here? + vrect_t aliasvrect; // scaled Alias version + int vrectright, vrectbottom; // right & bottom screen coords + int 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 + int vrect_x_adj_shift20; // (vrect.x + 0.5 - epsilon) << 20 + int 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; + + float fov_x, fov_y; + + int ambientlight; +} refdef_t; + + +// +// refresh +// +extern int reinit_surfcache; + + +extern refdef_t r_refdef; +extern vec3_t r_origin, vpn, vright, vup; + +extern struct texture_s *r_notexture_mip; + +extern entity_t r_worldentity; + +void R_Init (void); +void R_InitTextures (void); +void R_InitEfrags (void); +void R_RenderView (void); // must set r_refdef first +void R_ViewChanged (vrect_t *pvrect, int lineadj, float aspect); + // called whenever r_refdef or vid change +void R_InitSky (struct texture_s *mt); // called at level load + +void R_AddEfrags (entity_t *ent); +void R_RemoveEfrags (entity_t *ent); + +void R_NewMap (void); + + +void R_ParseParticleEffect (void); +void R_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count); +void R_RocketTrail (vec3_t start, vec3_t end, int type); + +void R_EntityParticles (entity_t *ent); +void R_BlobExplosion (vec3_t org); +void R_ParticleExplosion (vec3_t org); +void R_LavaSplash (vec3_t org); +void R_TeleportSplash (vec3_t org); + +void R_PushDlights (void); +void R_InitParticles (void); +void R_ClearParticles (void); +void R_DrawParticles (void); +void R_DrawWaterSurfaces (void); + + +// +// surface cache related +// +extern int reinit_surfcache; // if 1, surface cache is currently empty and +extern qboolean r_cache_thrash; // set if thrashing the surface cache + +int D_SurfaceCacheForRes (int width, int height); +void D_FlushCaches (void); +void D_DeleteSurfaceCache (void); +void D_InitCaches (void *buffer, int size); +void R_SetVrect (vrect_t *pvrect, vrect_t *pvrectin, int lineadj); + diff --git a/source/resource.h b/source/resource.h index f25557f5..68493e58 100644 --- a/source/resource.h +++ b/source/resource.h @@ -1,23 +1,23 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Developer Studio generated include file. -// Used by winquake.rc -// -#define IDS_STRING1 1 -#define IDI_ICON2 1 -#define IDD_DIALOG1 108 -#define IDD_PROGRESS 109 -#define IDB_QWBITMAP 112 -#define IDB_ZQBITMAP 115 -#define IDB_BITMAP1 117 -#define IDC_PROGRESS 1000 - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 118 -#define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1001 -#define _APS_NEXT_SYMED_VALUE 101 -#endif -#endif +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by winquake.rc +// +#define IDS_STRING1 1 +#define IDI_ICON2 1 +#define IDD_DIALOG1 108 +#define IDD_PROGRESS 109 +#define IDB_QWBITMAP 112 +#define IDB_ZQBITMAP 115 +#define IDB_BITMAP1 117 +#define IDC_PROGRESS 1000 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 118 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/source/savegame.c b/source/savegame.c index f588a974..50f7cd5e 100644 --- a/source/savegame.c +++ b/source/savegame.c @@ -1,296 +1,296 @@ -/* -=============================================================================== - -LOAD / SAVE GAME - -=============================================================================== -*/ - -#include "quakedef.h" -#include "server.h" -#include "world.h" - -extern cvar_t maxclients; - -#define SAVEGAME_COMMENT_LENGTH 39 -#define SAVEGAME_VERSION 5 - -/* -=============== -Host_SavegameComment - -Writes a SAVEGAME_COMMENT_LENGTH character comment describing the current -=============== -*/ -void Host_SavegameComment (char *text) -{ - int i; - char kills[20]; - - for (i=0 ; i : save a game\n"); - return; - } - - if (strstr(Cmd_Argv(1), "..")) - { - Con_Printf ("Relative pathnames are not allowed.\n"); - return; - } - -/* for (i=0 ; i < svs.maxclients ; i++) - { - if (svs.clients[i].active && (svs.clients[i].edict->v.health <= 0) ) - { - Con_Printf ("Can't savegame with a dead player\n"); - return; - } - } */ - // FIXME - if (cl.stats[STAT_HEALTH] <= 0) - { - Con_Printf ("Can't savegame with a dead player\n"); - return; - } - - sprintf (name, "%s/%s", com_gamedir, Cmd_Argv(1)); - COM_DefaultExtension (name, ".sav"); - - Con_Printf ("Saving game to %s...\n", name); - f = fopen (name, "w"); - if (!f) - { - Con_Printf ("ERROR: couldn't open.\n"); - return; - } - - fprintf (f, "%i\n", SAVEGAME_VERSION); - Host_SavegameComment (comment); - fprintf (f, "%s\n", comment); - for (i=0 ; ispawn_parms[i]); - fprintf (f, "%d\n", current_skill); - fprintf (f, "%s\n", sv.name); - fprintf (f, "%f\n",sv.time); - -// write the light styles - - for (i=0 ; i : load a game\n"); - return; - } - - cls.demonum = -1; // stop demo loop in case this fails - - sprintf (name, "%s/%s", com_gamedir, Cmd_Argv(1)); - COM_DefaultExtension (name, ".sav"); - -// we can't call SCR_BeginLoadingPlaque, because too much stack space has -// been used. The menu calls it before stuffing loadgame command -// SCR_BeginLoadingPlaque (); - - Con_Printf ("Loading game from %s...\n", name); - f = fopen (name, "r"); - if (!f) - { - Con_Printf ("ERROR: couldn't open.\n"); - return; - } - - fscanf (f, "%i\n", &version); - if (version != SAVEGAME_VERSION) - { - fclose (f); - Con_Printf ("Savegame is version %i, not %i\n", version, SAVEGAME_VERSION); - return; - } - fscanf (f, "%s\n", str); - for (i=0 ; iv, 0, progs->entityfields * 4); - ent->free = false; - ED_ParseEdict (start, ent); - - // link it into the bsp tree - if (!ent->free) - SV_LinkEdict (ent, false); - } - - entnum++; - } - - sv.num_edicts = entnum; - sv.time = time; - - fclose (f); - - for (i=0 ; ispawn_parms[i] = spawn_parms[i]; - -/* if (cls.state != ca_dedicated) - { - CL_EstablishConnection ("local"); - Host_Reconnect_f (); - }*/ - if (cls.state > ca_connected) - cls.state = ca_connected; - Cbuf_AddText ("connect local\n"); -} - +/* +=============================================================================== + +LOAD / SAVE GAME + +=============================================================================== +*/ + +#include "quakedef.h" +#include "server.h" +#include "world.h" + +extern cvar_t maxclients; + +#define SAVEGAME_COMMENT_LENGTH 39 +#define SAVEGAME_VERSION 5 + +/* +=============== +Host_SavegameComment + +Writes a SAVEGAME_COMMENT_LENGTH character comment describing the current +=============== +*/ +void Host_SavegameComment (char *text) +{ + int i; + char kills[20]; + + for (i=0 ; i : save a game\n"); + return; + } + + if (strstr(Cmd_Argv(1), "..")) + { + Con_Printf ("Relative pathnames are not allowed.\n"); + return; + } + +/* for (i=0 ; i < svs.maxclients ; i++) + { + if (svs.clients[i].active && (svs.clients[i].edict->v.health <= 0) ) + { + Con_Printf ("Can't savegame with a dead player\n"); + return; + } + } */ + // FIXME + if (cl.stats[STAT_HEALTH] <= 0) + { + Con_Printf ("Can't savegame with a dead player\n"); + return; + } + + sprintf (name, "%s/%s", com_gamedir, Cmd_Argv(1)); + COM_DefaultExtension (name, ".sav"); + + Con_Printf ("Saving game to %s...\n", name); + f = fopen (name, "w"); + if (!f) + { + Con_Printf ("ERROR: couldn't open.\n"); + return; + } + + fprintf (f, "%i\n", SAVEGAME_VERSION); + Host_SavegameComment (comment); + fprintf (f, "%s\n", comment); + for (i=0 ; ispawn_parms[i]); + fprintf (f, "%d\n", current_skill); + fprintf (f, "%s\n", sv.name); + fprintf (f, "%f\n",sv.time); + +// write the light styles + + for (i=0 ; i : load a game\n"); + return; + } + + cls.demonum = -1; // stop demo loop in case this fails + + sprintf (name, "%s/%s", com_gamedir, Cmd_Argv(1)); + COM_DefaultExtension (name, ".sav"); + +// we can't call SCR_BeginLoadingPlaque, because too much stack space has +// been used. The menu calls it before stuffing loadgame command +// SCR_BeginLoadingPlaque (); + + Con_Printf ("Loading game from %s...\n", name); + f = fopen (name, "r"); + if (!f) + { + Con_Printf ("ERROR: couldn't open.\n"); + return; + } + + fscanf (f, "%i\n", &version); + if (version != SAVEGAME_VERSION) + { + fclose (f); + Con_Printf ("Savegame is version %i, not %i\n", version, SAVEGAME_VERSION); + return; + } + fscanf (f, "%s\n", str); + for (i=0 ; iv, 0, progs->entityfields * 4); + ent->free = false; + ED_ParseEdict (start, ent); + + // link it into the bsp tree + if (!ent->free) + SV_LinkEdict (ent, false); + } + + entnum++; + } + + sv.num_edicts = entnum; + sv.time = time; + + fclose (f); + + for (i=0 ; ispawn_parms[i] = spawn_parms[i]; + +/* if (cls.state != ca_dedicated) + { + CL_EstablishConnection ("local"); + Host_Reconnect_f (); + }*/ + if (cls.state > ca_connected) + cls.state = ca_connected; + Cbuf_AddText ("connect local\n"); +} + diff --git a/source/sbar.c b/source/sbar.c index 71d7c94c..4475f289 100644 --- a/source/sbar.c +++ b/source/sbar.c @@ -1,1403 +1,1403 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// sbar.c -- status bar code - -#include "quakedef.h" -#include "sbar.h" - - - -int sb_updates; // if >= vid.numpages, no update needed - -#define STAT_MINUS 10 // num frame for '-' stats digit -qpic_t *sb_nums[2][11]; -qpic_t *sb_colon, *sb_slash; -qpic_t *sb_ibar; -qpic_t *sb_sbar; -qpic_t *sb_scorebar; - -qpic_t *sb_weapons[7][8]; // 0 is active, 1 is owned, 2-5 are flashes -qpic_t *sb_ammo[4]; -qpic_t *sb_sigil[4]; -qpic_t *sb_armor[3]; -qpic_t *sb_items[32]; - -qpic_t *sb_faces[7][2]; // 0 is gibbed, 1 is dead, 2-6 are alive - // 0 is static, 1 is temporary animation -qpic_t *sb_face_invis; -qpic_t *sb_face_quad; -qpic_t *sb_face_invuln; -qpic_t *sb_face_invis_invuln; - -qboolean sb_showscores; -qboolean sb_showteamscores; - -int sb_lines; // scan lines to draw - -void Sbar_DeathmatchOverlay (int start); -void Sbar_TeamOverlay (void); -void Sbar_MiniDeathmatchOverlay (void); - -static qboolean largegame = false; - -int sbar_xofs; - -cvar_t scr_centerSbar = {"scr_centerSbar","0"}; -cvar_t scr_centerScores = {"scr_centerScores","1"}; - -/* -=============== -Sbar_ShowTeamScores - -Tab key down -=============== -*/ -void Sbar_ShowTeamScores (void) -{ - if (sb_showteamscores) - return; - - sb_showteamscores = true; - sb_updates = 0; -} - -/* -=============== -Sbar_DontShowTeamScores - -Tab key up -=============== -*/ -void Sbar_DontShowTeamScores (void) -{ - sb_showteamscores = false; - sb_updates = 0; -} - -/* -=============== -Sbar_ShowScores - -Tab key down -=============== -*/ -void Sbar_ShowScores (void) -{ - if (sb_showscores) - return; - - sb_showscores = true; - sb_updates = 0; -} - -/* -=============== -Sbar_DontShowScores - -Tab key up -=============== -*/ -void Sbar_DontShowScores (void) -{ - sb_showscores = false; - sb_updates = 0; -} - -/* -=============== -Sbar_Changed -=============== -*/ -void Sbar_Changed (void) -{ - sb_updates = 0; // update next frame -} - -/* -=============== -Sbar_Init -=============== -*/ -void Sbar_Init (void) -{ - int i; - - for (i=0 ; i<10 ; i++) - { - sb_nums[0][i] = Draw_PicFromWad (va("num_%i",i)); - sb_nums[1][i] = Draw_PicFromWad (va("anum_%i",i)); - } - - sb_nums[0][10] = Draw_PicFromWad ("num_minus"); - sb_nums[1][10] = Draw_PicFromWad ("anum_minus"); - - sb_colon = Draw_PicFromWad ("num_colon"); - sb_slash = Draw_PicFromWad ("num_slash"); - - sb_weapons[0][0] = Draw_PicFromWad ("inv_shotgun"); - sb_weapons[0][1] = Draw_PicFromWad ("inv_sshotgun"); - sb_weapons[0][2] = Draw_PicFromWad ("inv_nailgun"); - sb_weapons[0][3] = Draw_PicFromWad ("inv_snailgun"); - sb_weapons[0][4] = Draw_PicFromWad ("inv_rlaunch"); - sb_weapons[0][5] = Draw_PicFromWad ("inv_srlaunch"); - sb_weapons[0][6] = Draw_PicFromWad ("inv_lightng"); - - sb_weapons[1][0] = Draw_PicFromWad ("inv2_shotgun"); - sb_weapons[1][1] = Draw_PicFromWad ("inv2_sshotgun"); - sb_weapons[1][2] = Draw_PicFromWad ("inv2_nailgun"); - sb_weapons[1][3] = Draw_PicFromWad ("inv2_snailgun"); - sb_weapons[1][4] = Draw_PicFromWad ("inv2_rlaunch"); - sb_weapons[1][5] = Draw_PicFromWad ("inv2_srlaunch"); - sb_weapons[1][6] = Draw_PicFromWad ("inv2_lightng"); - - for (i=0 ; i<5 ; i++) - { - sb_weapons[2+i][0] = Draw_PicFromWad (va("inva%i_shotgun",i+1)); - sb_weapons[2+i][1] = Draw_PicFromWad (va("inva%i_sshotgun",i+1)); - sb_weapons[2+i][2] = Draw_PicFromWad (va("inva%i_nailgun",i+1)); - sb_weapons[2+i][3] = Draw_PicFromWad (va("inva%i_snailgun",i+1)); - sb_weapons[2+i][4] = Draw_PicFromWad (va("inva%i_rlaunch",i+1)); - sb_weapons[2+i][5] = Draw_PicFromWad (va("inva%i_srlaunch",i+1)); - sb_weapons[2+i][6] = Draw_PicFromWad (va("inva%i_lightng",i+1)); - } - - sb_ammo[0] = Draw_PicFromWad ("sb_shells"); - sb_ammo[1] = Draw_PicFromWad ("sb_nails"); - sb_ammo[2] = Draw_PicFromWad ("sb_rocket"); - sb_ammo[3] = Draw_PicFromWad ("sb_cells"); - - sb_armor[0] = Draw_PicFromWad ("sb_armor1"); - sb_armor[1] = Draw_PicFromWad ("sb_armor2"); - sb_armor[2] = Draw_PicFromWad ("sb_armor3"); - - sb_items[0] = Draw_PicFromWad ("sb_key1"); - sb_items[1] = Draw_PicFromWad ("sb_key2"); - sb_items[2] = Draw_PicFromWad ("sb_invis"); - sb_items[3] = Draw_PicFromWad ("sb_invuln"); - sb_items[4] = Draw_PicFromWad ("sb_suit"); - sb_items[5] = Draw_PicFromWad ("sb_quad"); - - sb_sigil[0] = Draw_PicFromWad ("sb_sigil1"); - sb_sigil[1] = Draw_PicFromWad ("sb_sigil2"); - sb_sigil[2] = Draw_PicFromWad ("sb_sigil3"); - sb_sigil[3] = Draw_PicFromWad ("sb_sigil4"); - - sb_faces[4][0] = Draw_PicFromWad ("face1"); - sb_faces[4][1] = Draw_PicFromWad ("face_p1"); - sb_faces[3][0] = Draw_PicFromWad ("face2"); - sb_faces[3][1] = Draw_PicFromWad ("face_p2"); - sb_faces[2][0] = Draw_PicFromWad ("face3"); - sb_faces[2][1] = Draw_PicFromWad ("face_p3"); - sb_faces[1][0] = Draw_PicFromWad ("face4"); - sb_faces[1][1] = Draw_PicFromWad ("face_p4"); - sb_faces[0][0] = Draw_PicFromWad ("face5"); - sb_faces[0][1] = Draw_PicFromWad ("face_p5"); - - sb_face_invis = Draw_PicFromWad ("face_invis"); - sb_face_invuln = Draw_PicFromWad ("face_invul2"); - sb_face_invis_invuln = Draw_PicFromWad ("face_inv2"); - sb_face_quad = Draw_PicFromWad ("face_quad"); - - Cvar_RegisterVariable (&scr_centerScores); - Cvar_RegisterVariable (&scr_centerSbar); - - Cmd_AddCommand ("+showscores", Sbar_ShowScores); - Cmd_AddCommand ("-showscores", Sbar_DontShowScores); - - Cmd_AddCommand ("+showteamscores", Sbar_ShowTeamScores); - Cmd_AddCommand ("-showteamscores", Sbar_DontShowTeamScores); - - sb_sbar = Draw_PicFromWad ("sbar"); - sb_ibar = Draw_PicFromWad ("ibar"); - sb_scorebar = Draw_PicFromWad ("scorebar"); -} - - -//============================================================================= - -// drawing routines are relative to the status bar location - -/* -============= -Sbar_DrawPic -============= -*/ -void Sbar_DrawPic (int x, int y, qpic_t *pic) -{ - Draw_Pic (x + sbar_xofs, y + (vid.height-SBAR_HEIGHT), pic); -} - -/* -============= -Sbar_DrawSubPic -============= -JACK: Draws a portion of the picture in the status bar. -*/ - -void Sbar_DrawSubPic(int x, int y, qpic_t *pic, int srcx, int srcy, int width, int height) -{ - Draw_SubPic (x, y+(vid.height-SBAR_HEIGHT), pic, srcx, srcy, width, height); -} - - -/* -============= -Sbar_DrawTransPic -============= -*/ -void Sbar_DrawTransPic (int x, int y, qpic_t *pic) -{ - Draw_TransPic (x + sbar_xofs, y + (vid.height-SBAR_HEIGHT), pic); -} - -/* -================ -Sbar_DrawCharacter - -Draws one solid graphics character -================ -*/ -void Sbar_DrawCharacter (int x, int y, int num) -{ - Draw_Character (x + 4 + sbar_xofs, y + vid.height-SBAR_HEIGHT, num); -} - -/* -================ -Sbar_DrawString -================ -*/ -void Sbar_DrawString (int x, int y, char *str) -{ - Draw_String (x + sbar_xofs, y+ vid.height-SBAR_HEIGHT, str); -} - -/* -============= -Sbar_itoa -============= -*/ -int Sbar_itoa (int num, char *buf) -{ - char *str; - int pow10; - int dig; - - str = buf; - - if (num < 0) - { - *str++ = '-'; - num = -num; - } - - for (pow10 = 10 ; num >= pow10 ; pow10 *= 10) - ; - - do - { - pow10 /= 10; - dig = num/pow10; - *str++ = '0'+dig; - num -= dig*pow10; - } while (pow10 != 1); - - *str = 0; - - return str-buf; -} - - -/* -============= -Sbar_DrawNum -============= -*/ -void Sbar_DrawNum (int x, int y, int num, int digits, int color) -{ - char str[12]; - char *ptr; - int l, frame; - - l = Sbar_itoa (num, str); - ptr = str; - if (l > digits) - ptr += (l-digits); - if (l < digits) - x += (digits-l)*24; - - while (*ptr) - { - if (*ptr == '-') - frame = STAT_MINUS; - else - frame = *ptr -'0'; - - Sbar_DrawTransPic (x,y,sb_nums[color][frame]); - x += 24; - ptr++; - } -} - -//============================================================================= - -int fragsort[MAX_CLIENTS]; -int scoreboardlines; -typedef struct { - char team[16+1]; - int frags; - int players; - int plow, phigh, ptotal; -} team_t; -team_t teams[MAX_CLIENTS]; -int teamsort[MAX_CLIENTS]; -int scoreboardteams; - -/* -=============== -Sbar_SortFrags -=============== -*/ -void Sbar_SortFrags (qboolean includespec) -{ - int i, j, k; - -// sort by frags - scoreboardlines = 0; - for (i=0 ; iname[0]) - continue; - if (s->spectator) - continue; - - // find his team in the list - Q_strncpyz (t, Info_ValueForKey(s->userinfo, "team"), sizeof(t)); - if (!t[0]) - continue; // not on team - for (j = 0; j < scoreboardteams; j++) - if (!strcmp(teams[j].team, t)) { - teams[j].frags += s->frags; - teams[j].players++; - goto addpinginfo; - } - if (j == scoreboardteams) { // must add him - j = scoreboardteams++; - strcpy(teams[j].team, t); - teams[j].frags = s->frags; - teams[j].players = 1; -addpinginfo: - if (teams[j].plow > s->ping) - teams[j].plow = s->ping; - if (teams[j].phigh < s->ping) - teams[j].phigh = s->ping; - teams[j].ptotal += s->ping; - } - } - - // sort - for (i = 0; i < scoreboardteams; i++) - teamsort[i] = i; - - // good 'ol bubble sort - for (i = 0; i < scoreboardteams - 1; i++) - for (j = i + 1; j < scoreboardteams; j++) - if (teams[teamsort[i]].frags < teams[teamsort[j]].frags) { - k = teamsort[i]; - teamsort[i] = teamsort[j]; - teamsort[j] = k; - } -} - -int Sbar_ColorForMap (int m) -{ - m = (m < 0) ? 0 : ((m > 13) ? 13 : m); - - m *= 16; - return m < 128 ? m + 8 : m + 8; -} - - -/* -=============== -Sbar_SoloScoreboard -=============== -*/ -void Sbar_SoloScoreboard (void) -{ - char str[80]; - double _time; - int minutes, seconds, tens, units; - - Sbar_DrawPic (0, 0, sb_scorebar); - - if (cl.gametype == GAME_COOP) - { - sprintf(str, "Monsters:%3i /%3i", cl.stats[STAT_MONSTERS], cl.stats[STAT_TOTALMONSTERS]); - Sbar_DrawString (8, 4, str); - - sprintf(str, "Secrets :%3i /%3i", cl.stats[STAT_SECRETS], cl.stats[STAT_TOTALSECRETS]); - Sbar_DrawString (8, 12, str); - } - - // time - _time = cl.time; - if (cl.gametype == GAME_COOP) - _time -= cl.players[cl.playernum].entertime; - minutes = _time / 60; - seconds = _time - 60*minutes; - tens = seconds / 10; - units = seconds - 10*tens; - sprintf (str,"Time :%3i:%i%i", minutes, tens, units); - Sbar_DrawString (184, 4, str); - - if (cl.gametype == GAME_COOP) - { - // draw level name - int l = strlen (cl.levelname); - if (l < 22 && !strstr(cl.levelname, "\n")) - Sbar_DrawString (232 - l*4, 12, cl.levelname); - } -} - -//============================================================================= - -/* -=============== -Sbar_DrawInventory -=============== -*/ -void Sbar_DrawInventory (void) -{ - int i; - char num[6]; - float time; - int flashon; - qboolean headsup; - qboolean hudswap; - - headsup = !(cl_sbar.value || scr_viewsize.value<100); - hudswap = cl_hudswap.value; // Get that nasty float out :) - - if (!headsup) - Sbar_DrawPic (0, -24, sb_ibar); -// weapons - for (i=0 ; i<7 ; i++) - { - if (cl.stats[STAT_ITEMS] & (IT_SHOTGUN<= 10) - { - if ( cl.stats[STAT_ACTIVEWEAPON] == (IT_SHOTGUN<200) - Sbar_DrawSubPic ((hudswap) ? 0 : (vid.width-24),-68-(7-i)*16 , sb_weapons[flashon][i],0,0,24,16); - - } else - Sbar_DrawPic (i*24, -16, sb_weapons[flashon][i]); -// Sbar_DrawSubPic (0,0,20,20,i*24, -16, sb_weapons[flashon][i]); - - if (flashon > 1) - sb_updates = 0; // force update to remove flash - } - } - -// ammo counts - for (i=0 ; i<4 ; i++) - { - sprintf (num, "%3i",cl.stats[STAT_SHELLS+i] ); - if (headsup) { -// Sbar_DrawSubPic(3, -24, sb_ibar, 3, 0, 42,11); - Sbar_DrawSubPic((hudswap) ? 0 : (vid.width-42), -24 - (4-i)*11, sb_ibar, 3+(i*48), 0, 42, 11); - if (num[0] != ' ') - Draw_Character ( (hudswap) ? 7: (vid.width - 35), vid.height-SBAR_HEIGHT-24 - (4-i)*11, 18 + num[0] - '0'); - if (num[1] != ' ') - Draw_Character ( (hudswap) ? 15: (vid.width - 27), vid.height-SBAR_HEIGHT-24 - (4-i)*11, 18 + num[1] - '0'); - if (num[2] != ' ') - Draw_Character ( (hudswap) ? 23: (vid.width - 19), vid.height-SBAR_HEIGHT-24 - (4-i)*11, 18 + num[2] - '0'); - } else { - if (num[0] != ' ') - Sbar_DrawCharacter ( (6*i+1)*8 - 2, -24, 18 + num[0] - '0'); - if (num[1] != ' ') - Sbar_DrawCharacter ( (6*i+2)*8 - 2, -24, 18 + num[1] - '0'); - if (num[2] != ' ') - Sbar_DrawCharacter ( (6*i+3)*8 - 2, -24, 18 + num[2] - '0'); - } - } - - flashon = 0; -// items - for (i=0 ; i<6 ; i++) - if (cl.stats[STAT_ITEMS] & (1<<(17+i))) - { - time = cl.item_gettime[17+i]; - if (time && time > cl.time - 2 && flashon ) - { // flash frame - sb_updates = 0; - } - else - Sbar_DrawPic (192 + i*16, -16, sb_items[i]); - if (time && time > cl.time - 2) - sb_updates = 0; - } - -// sigils - for (i=0 ; i<4 ; i++) - if (cl.stats[STAT_ITEMS] & (1<<(28+i))) - { - time = cl.item_gettime[28+i]; - if (time && time > cl.time - 2 && flashon ) - { // flash frame - sb_updates = 0; - } - else - Sbar_DrawPic (320-32 + i*8, -16, sb_sigil[i]); - if (time && time > cl.time - 2) - sb_updates = 0; - } -} - -//============================================================================= - -/* -=============== -Sbar_DrawFrags -=============== -*/ -void Sbar_DrawFrags (void) -{ - int i, k, l; - int top, bottom; - int x, y, f; - char num[12]; - player_info_t *s; - - if (cl.gametype == GAME_COOP) - return; - - Sbar_SortFrags (false); - -// draw the text - l = scoreboardlines <= 4 ? scoreboardlines : 4; - - x = 23; -// xofs = (vid.width - 320)>>1; - y = vid.height - SBAR_HEIGHT - 23; - - for (i=0 ; iname[0]) - continue; - if (s->spectator) - continue; - - // draw background - top = s->topcolor; - bottom = s->bottomcolor; - top = (top < 0) ? 0 : ((top > 13) ? 13 : top); - bottom = (bottom < 0) ? 0 : ((bottom > 13) ? 13 : bottom); - - top = Sbar_ColorForMap (top); - bottom = Sbar_ColorForMap (bottom); - - Draw_Fill (sbar_xofs + x*8 + 10, y, 28, 4, top); - Draw_Fill (sbar_xofs + x*8 + 10, y+4, 28, 3, bottom); - - // draw number - f = s->frags; - sprintf (num, "%3i",f); - - Sbar_DrawCharacter ((x+1)*8 , -24, num[0]); - Sbar_DrawCharacter ((x+2)*8 , -24, num[1]); - Sbar_DrawCharacter ((x+3)*8 , -24, num[2]); - - if (k == cl.playernum) - { - Sbar_DrawCharacter (x*8+2, -24, 16); - Sbar_DrawCharacter ((x+4)*8-4, -24, 17); - } - x+=4; - } -} - -//============================================================================= - - -/* -=============== -Sbar_DrawFace -=============== -*/ -void Sbar_DrawFace (void) -{ - int f, anim; - - if ( (cl.stats[STAT_ITEMS] & (IT_INVISIBILITY | IT_INVULNERABILITY) ) - == (IT_INVISIBILITY | IT_INVULNERABILITY) ) - { - Sbar_DrawPic (112, 0, sb_face_invis_invuln); - return; - } - if (cl.stats[STAT_ITEMS] & IT_QUAD) - { - Sbar_DrawPic (112, 0, sb_face_quad ); - return; - } - if (cl.stats[STAT_ITEMS] & IT_INVISIBILITY) - { - Sbar_DrawPic (112, 0, sb_face_invis ); - return; - } - if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY) - { - Sbar_DrawPic (112, 0, sb_face_invuln); - return; - } - - if (cl.stats[STAT_HEALTH] >= 100) - f = 4; - else - f = cl.stats[STAT_HEALTH] / 20; - - if (cl.time <= cl.faceanimtime) - { - anim = 1; - sb_updates = 0; // make sure the anim gets drawn over - } - else - anim = 0; - Sbar_DrawPic (112, 0, sb_faces[f][anim]); -} - -/* -============= -Sbar_DrawNormal -============= -*/ -void Sbar_DrawNormal (void) -{ - if (cl_sbar.value || scr_viewsize.value<100) - Sbar_DrawPic (0, 0, sb_sbar); - -// armor - if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY) - { - Sbar_DrawNum (24, 0, 666, 3, 1); - Sbar_DrawPic (0, 0, draw_disc); - } - else - { - Sbar_DrawNum (24, 0, cl.stats[STAT_ARMOR], 3 - , cl.stats[STAT_ARMOR] <= 25); - if (cl.stats[STAT_ITEMS] & IT_ARMOR3) - Sbar_DrawPic (0, 0, sb_armor[2]); - else if (cl.stats[STAT_ITEMS] & IT_ARMOR2) - Sbar_DrawPic (0, 0, sb_armor[1]); - else if (cl.stats[STAT_ITEMS] & IT_ARMOR1) - Sbar_DrawPic (0, 0, sb_armor[0]); - } - -// face - Sbar_DrawFace (); - -// health - Sbar_DrawNum (136, 0, cl.stats[STAT_HEALTH], 3 - , cl.stats[STAT_HEALTH] <= 25); - -// ammo icon - if (cl.stats[STAT_ITEMS] & IT_SHELLS) - Sbar_DrawPic (224, 0, sb_ammo[0]); - else if (cl.stats[STAT_ITEMS] & IT_NAILS) - Sbar_DrawPic (224, 0, sb_ammo[1]); - else if (cl.stats[STAT_ITEMS] & IT_ROCKETS) - Sbar_DrawPic (224, 0, sb_ammo[2]); - else if (cl.stats[STAT_ITEMS] & IT_CELLS) - Sbar_DrawPic (224, 0, sb_ammo[3]); - - Sbar_DrawNum (248, 0, cl.stats[STAT_AMMO], 3 - , cl.stats[STAT_AMMO] <= 10); -} - -/* -=============== -Sbar_Draw -=============== -*/ -void Sbar_Draw (void) -{ - qboolean headsup; - char st[512]; - - headsup = !(cl_sbar.value || scr_viewsize.value<100); - if ((sb_updates >= vid.numpages) && !headsup) - return; - - if (scr_con_current == vid.height) - return; // console is full screen - - scr_copyeverything = 1; -// scr_fullupdate = 0; - - sb_updates++; - - if (scr_centerSbar.value) - sbar_xofs = (vid.width - 320)>>1; - else - sbar_xofs = 0; - -// top line - if (sb_lines > 24) - { - if (!cl.spectator || autocam == CAM_TRACK) - Sbar_DrawInventory (); - if (!headsup || vid.width<512) - Sbar_DrawFrags (); - } - -// main area - if (sb_lines > 0) - { - if (cl.spectator) { - if (autocam != CAM_TRACK) { - Sbar_DrawPic (0, 0, sb_scorebar); - Sbar_DrawString (160-7*8,4, "SPECTATOR MODE"); - Sbar_DrawString(160-14*8+4, 12, "Press [ATTACK] for AutoCamera"); - } else { - if (sb_showscores || cl.stats[STAT_HEALTH] <= 0) - Sbar_SoloScoreboard (); - else - Sbar_DrawNormal (); - -// Sbar_DrawString (160-14*8+4,4, "SPECTATOR MODE - TRACK CAMERA"); - sprintf(st, "Tracking %-.13s, [JUMP] for next", - cl.players[spec_track].name); - Sbar_DrawString(0, -8, st); - } - } else if (sb_showscores || cl.stats[STAT_HEALTH] <= 0) - Sbar_SoloScoreboard (); - else - Sbar_DrawNormal (); - } - -// main screen deathmatch rankings - // if we're dead show team scores in team games - if (cl.stats[STAT_HEALTH] <= 0 && !cl.spectator) - if (atoi(Info_ValueForKey(cl.serverinfo, "teamplay")) > 0 && - !sb_showscores) - Sbar_TeamOverlay(); - else - Sbar_DeathmatchOverlay (0); - else if (sb_showscores) - Sbar_DeathmatchOverlay (0); - else if (sb_showteamscores) - Sbar_TeamOverlay(); - -#ifdef GLQUAKE - if (sb_showscores || sb_showteamscores || - cl.stats[STAT_HEALTH] <= 0) - sb_updates = 0; - - // clear unused areas in GL - if (vid.width > 320 && !headsup) { - // left - if (scr_centerSbar.value) - Draw_TileClear (0, vid.height - sb_lines, sbar_xofs, sb_lines); - // right - Draw_TileClear (320 + sbar_xofs, vid.height - sb_lines, vid.width - (320 + sbar_xofs), sb_lines); - } - if (!headsup && cl.spectator && (autocam != CAM_TRACK) && (sb_lines > SBAR_HEIGHT)) - Draw_TileClear (sbar_xofs, vid.height - sb_lines, 320, sb_lines - SBAR_HEIGHT); -#endif - - if (sb_lines > 0 && cl.gametype == GAME_DEATHMATCH - && !scr_centerSbar.value) - Sbar_MiniDeathmatchOverlay (); -} - -//============================================================================= - -/* -================== -Sbar_IntermissionNumber - -================== -*/ -void Sbar_IntermissionNumber (int x, int y, int num, int digits, int color) -{ - char str[12]; - char *ptr; - int l, frame; - - l = Sbar_itoa (num, str); - ptr = str; - if (l > digits) - ptr += (l-digits); - if (l < digits) - x += (digits-l)*24; - - while (*ptr) - { - if (*ptr == '-') - frame = STAT_MINUS; - else - frame = *ptr -'0'; - - Draw_TransPic (x,y,sb_nums[color][frame]); - x += 24; - ptr++; - } -} - -/* -================== -Sbar_TeamOverlay - -team frags -added by Zoid -================== -*/ -void Sbar_TeamOverlay (void) -{ - qpic_t *pic; - int i, k, l; - int x, y; - int xofs; - char num[12]; - int teamplay; - char team[4+1]; - team_t *tm; - int plow, phigh, pavg; - -// request new ping times every two second - teamplay = atoi(Info_ValueForKey(cl.serverinfo, "teamplay")); - - - if (!teamplay) { - Sbar_DeathmatchOverlay(0); - return; - } - - scr_copyeverything = 1; - scr_fullupdate = 0; - - if (scr_centerScores.value) - xofs = (vid.width - 320)>>1; - else - xofs = 0; - - pic = Draw_CachePic ("gfx/ranking.lmp"); - Draw_Pic (xofs + 160 - pic->width/2, 0, pic); - - y = 24; - x = xofs + 36; - Draw_String(x, y, "low/avg/high team total players"); - y += 8; -// Draw_String(x, y, "------------ ---- ----- -------"); - Draw_String(x, y, "\x1d\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1f \x1d\x1e\x1e\x1f \x1d\x1e\x1e\x1e\x1f \x1d\x1e\x1e\x1e\x1e\x1e\x1f"); - y += 8; - -// sort the teams - Sbar_SortTeams(); - -// draw the text - l = scoreboardlines; - - for (i=0 ; i < scoreboardteams && y <= vid.height-10 ; i++) - { - k = teamsort[i]; - tm = teams + k; - - // draw pings - plow = tm->plow; - if (plow < 0 || plow > 999) - plow = 999; - phigh = tm->phigh; - if (phigh < 0 || phigh > 999) - phigh = 999; - if (!tm->players) - pavg = 999; - else - pavg = tm->ptotal / tm->players; - if (pavg < 0 || pavg > 999) - pavg = 999; - - sprintf (num, "%3i/%3i/%3i", plow, pavg, phigh); - Draw_String ( x, y, num); - - // draw team - Q_strncpyz (team, tm->team, sizeof(team)); - Draw_String (x + 104, y, team); - - // draw total - sprintf (num, "%5i", tm->frags); - Draw_String (x + 104 + 40, y, num); - - // draw players - sprintf (num, "%5i", tm->players); - Draw_String (x + 104 + 88, y, num); - - if (!strncmp(Info_ValueForKey(cl.players[cl.playernum].userinfo, - "team"), tm->team, 16)) { - Draw_Character ( x + 104 - 8, y, 16); - Draw_Character ( x + 104 + 32, y, 17); - } - - y += 8; - } - y += 8; - Sbar_DeathmatchOverlay(y); -} - -/* -================== -Sbar_DeathmatchOverlay - -ping time frags name -================== -*/ -void Sbar_DeathmatchOverlay (int start) -{ - qpic_t *pic; - int i, k, l; - int top, bottom; - int x, y, f; - int xofs; - char num[12]; - player_info_t *s; - int total; - int minutes; - int p; - int teamplay; - char team[5]; - int skip = 10; - - if (largegame) - skip = 8; - -// request new ping times every two second - if (realtime - cl.last_ping_request > 2) - { - cl.last_ping_request = realtime; - MSG_WriteByte (&cls.netchan.message, clc_stringcmd); - SZ_Print (&cls.netchan.message, "pings"); - } - - teamplay = atoi(Info_ValueForKey(cl.serverinfo, "teamplay")); - - scr_copyeverything = 1; - scr_fullupdate = 0; - - if (scr_centerScores.value) - xofs = (vid.width - 320)>>1; - else - xofs = 0; - - if (!start) { - pic = Draw_CachePic ("gfx/ranking.lmp"); - Draw_Pic (xofs + 160 - pic->width/2, 0, pic); - } - -// scores - Sbar_SortFrags (true); - -// draw the text - l = scoreboardlines; - - if (start) - y = start; - else - y = 24; - if (teamplay) - { - x = xofs + 4; -// 0 40 64 104 152 192 - Draw_String ( x , y, "ping pl time frags team name"); - y += 8; -// Draw_String ( x , y, "---- -- ---- ----- ---- ----------------"); - Draw_String ( x , y, "\x1d\x1e\x1e\x1f \x1d\x1f \x1d\x1e\x1e\x1f \x1d\x1e\x1e\x1e\x1f \x1d\x1e\x1e\x1f \x1d\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1f"); - y += 8; - } - else - { - x = xofs + 16; -// 0 40 64 104 152 - Draw_String ( x , y, "ping pl time frags name"); - y += 8; -// Draw_String ( x , y, "---- -- ---- ----- ----------------"); - Draw_String ( x , y, "\x1d\x1e\x1e\x1f \x1d\x1f \x1d\x1e\x1e\x1f \x1d\x1e\x1e\x1e\x1f \x1d\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1f"); - y += 8; - } - - for (i=0 ; iname[0]) - continue; - - // draw ping - p = s->ping; - if (p < 0 || p > 999) - p = 999; - sprintf (num, "%4i", p); - Draw_String ( x, y, num); - - // draw pl - p = s->pl; - sprintf (num, "%3i", p); - if (p > 25) - Draw_Alt_String ( x+32, y, num); - else - Draw_String ( x+32, y, num); - - if (s->spectator) - { - Draw_String (x+40, y, "(spectator)"); - // draw name - if (teamplay) - Draw_String (x+152+40, y, s->name); - else - Draw_String (x+152, y, s->name); - y += skip; - continue; - } - - - // draw time - if (cl.intermission) - total = cl.completed_time - s->entertime; - else - total = realtime - s->entertime; - minutes = (int)total/60; - sprintf (num, "%4i", minutes); - Draw_String ( x+64 , y, num); - - // draw background - top = s->topcolor; - bottom = s->bottomcolor; - top = Sbar_ColorForMap (top); - bottom = Sbar_ColorForMap (bottom); - - if (largegame) - Draw_Fill ( x+104, y+1, 40, 3, top); - else - Draw_Fill ( x+104, y, 40, 4, top); - Draw_Fill ( x+104, y+4, 40, 4, bottom); - - // draw number - f = s->frags; - sprintf (num, "%3i",f); - - Draw_Character ( x+112 , y, num[0]); - Draw_Character ( x+120 , y, num[1]); - Draw_Character ( x+128 , y, num[2]); - - if (k == cl.playernum) - { - Draw_Character ( x + 104, y, 16); - Draw_Character ( x + 136, y, 17); - } - - // team - if (teamplay) - { - Q_strncpyz (team, Info_ValueForKey(s->userinfo, "team"), sizeof(team)); - Draw_String (x+152, y, team); - } - - // draw name - if (teamplay) - Draw_String (x+152+40, y, s->name); - else - Draw_String (x+152, y, s->name); - - y += skip; - } - - if (y >= vid.height-10) // we ran over the screen size, squish - largegame = true; -} - -/* -================== -Sbar_MiniDeathmatchOverlay - -frags name -frags team name -displayed to right of status bar if there's room -================== -*/ -void Sbar_MiniDeathmatchOverlay (void) -{ - int i, k; - int top, bottom; - int x, y, f; - char num[12]; - player_info_t *s; - int teamplay; - char team[4+1]; - int numlines; - char name[16+1]; - team_t *tm; - - if (vid.width < 512 || !sb_lines) - return; // not enuff room - - teamplay = atoi(Info_ValueForKey(cl.serverinfo, "teamplay")); - - scr_copyeverything = 1; - scr_fullupdate = 0; - -// scores - Sbar_SortFrags (false); - if (vid.width >= 640) - Sbar_SortTeams(); - - if (!scoreboardlines) - return; // no one there? - -// draw the text - y = vid.height - sb_lines - 1; - numlines = sb_lines/8; - if (numlines < 3) - return; // not enough room - - // find us - for (i=0 ; i < scoreboardlines; i++) - if (fragsort[i] == cl.playernum) - break; - - if (i == scoreboardlines) // we're not there, we are probably a spectator, just display top - i = 0; - else // figure out start - i = i - numlines/2; - - if (i > scoreboardlines - numlines) - i = scoreboardlines - numlines; - if (i < 0) - i = 0; - - x = 324; - - for (/* */ ; i < scoreboardlines && y < vid.height - 8 + 1; i++) - { - k = fragsort[i]; - s = &cl.players[k]; - if (!s->name[0]) - continue; - - // draw ping - top = s->topcolor; - bottom = s->bottomcolor; - top = Sbar_ColorForMap (top); - bottom = Sbar_ColorForMap (bottom); - - Draw_Fill ( x, y+1, 40, 3, top); - Draw_Fill ( x, y+4, 40, 4, bottom); - - // draw number - f = s->frags; - sprintf (num, "%3i",f); - - Draw_Character ( x+8 , y, num[0]); - Draw_Character ( x+16, y, num[1]); - Draw_Character ( x+24, y, num[2]); - - if (k == cl.playernum) - { - Draw_Character ( x, y, 16); - Draw_Character ( x + 32, y, 17); - } - - // team - if (teamplay) - { - Q_strncpyz (team, Info_ValueForKey(s->userinfo, "team"), sizeof(team)); - Draw_String (x+48, y, team); - } - - // draw name - Q_strncpyz (name, s->name, sizeof(name)); - if (teamplay) - Draw_String (x+48+40, y, name); - else - Draw_String (x+48, y, name); - y += 8; - } - - // draw teams if room - if (vid.width < 640 || !teamplay) - return; - - // draw seperator - x += 208; - for (y = vid.height - sb_lines; y < vid.height - 6; y += 2) - Draw_Character(x, y, 14); - - x += 16; - - y = vid.height - sb_lines; - for (i=0 ; i < scoreboardteams && y <= vid.height; i++) - { - k = teamsort[i]; - tm = teams + k; - - // draw pings - Q_strncpyz (team, tm->team, sizeof(team)); - Draw_String (x, y, team); - - // draw total - sprintf (num, "%5i", tm->frags); - Draw_String (x + 40, y, num); - - if (!strncmp(Info_ValueForKey(cl.players[cl.playernum].userinfo, - "team"), tm->team, 16)) { - Draw_Character ( x - 8, y, 16); - Draw_Character ( x + 32, y, 17); - } - - y += 8; - } - -} - - -/* -================== -Sbar_IntermissionOverlay - -================== -*/ -void Sbar_IntermissionOverlay (void) -{ - qpic_t *pic; - int dig; - int num; - int xofs; - - scr_copyeverything = 1; - scr_fullupdate = 0; - - if (cl.gametype == GAME_DEATHMATCH) - { - if (atoi(Info_ValueForKey(cl.serverinfo, "teamplay")) > 0 && !sb_showscores) - Sbar_TeamOverlay (); - else - Sbar_DeathmatchOverlay (0); - return; - } - - xofs = (vid.width - 320)>>1; - - pic = Draw_CachePic ("gfx/complete.lmp"); - Draw_Pic (xofs + 64, 24, pic); - - pic = Draw_CachePic ("gfx/inter.lmp"); - Draw_TransPic (xofs, 56, pic); - - // time - dig = (cl.completed_time - cl.players[cl.playernum].entertime) / 60; - Sbar_IntermissionNumber (xofs + 160, 64, dig, 3, 0); - num = (cl.completed_time - cl.players[cl.playernum].entertime) - dig*60; - Draw_TransPic (xofs + 234, 64, sb_colon); - Draw_TransPic (xofs + 246, 64, sb_nums[0][num/10]); - Draw_TransPic (xofs + 266, 64, sb_nums[0][num%10]); - - Sbar_IntermissionNumber (xofs + 160, 104, cl.stats[STAT_SECRETS], 3, 0); - Draw_TransPic (xofs + 232, 104, sb_slash); - Sbar_IntermissionNumber (xofs + 240, 104, cl.stats[STAT_TOTALSECRETS], 3, 0); - - Sbar_IntermissionNumber (xofs + 160, 144, cl.stats[STAT_MONSTERS], 3, 0); - Draw_TransPic (xofs + 232, 144, sb_slash); - Sbar_IntermissionNumber (xofs + 240, 144, cl.stats[STAT_TOTALMONSTERS], 3, 0); -} - - -/* -================== -Sbar_FinaleOverlay - -================== -*/ -void Sbar_FinaleOverlay (void) -{ - qpic_t *pic; - - scr_copyeverything = 1; - - pic = Draw_CachePic ("gfx/finale.lmp"); - Draw_TransPic ( (vid.width-pic->width)/2, 16, pic); -} - - +/* +Copyright (C) 1996-1997 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. + +*/ +// sbar.c -- status bar code + +#include "quakedef.h" +#include "sbar.h" + + + +int sb_updates; // if >= vid.numpages, no update needed + +#define STAT_MINUS 10 // num frame for '-' stats digit +qpic_t *sb_nums[2][11]; +qpic_t *sb_colon, *sb_slash; +qpic_t *sb_ibar; +qpic_t *sb_sbar; +qpic_t *sb_scorebar; + +qpic_t *sb_weapons[7][8]; // 0 is active, 1 is owned, 2-5 are flashes +qpic_t *sb_ammo[4]; +qpic_t *sb_sigil[4]; +qpic_t *sb_armor[3]; +qpic_t *sb_items[32]; + +qpic_t *sb_faces[7][2]; // 0 is gibbed, 1 is dead, 2-6 are alive + // 0 is static, 1 is temporary animation +qpic_t *sb_face_invis; +qpic_t *sb_face_quad; +qpic_t *sb_face_invuln; +qpic_t *sb_face_invis_invuln; + +qboolean sb_showscores; +qboolean sb_showteamscores; + +int sb_lines; // scan lines to draw + +void Sbar_DeathmatchOverlay (int start); +void Sbar_TeamOverlay (void); +void Sbar_MiniDeathmatchOverlay (void); + +static qboolean largegame = false; + +int sbar_xofs; + +cvar_t scr_centerSbar = {"scr_centerSbar","0"}; +cvar_t scr_centerScores = {"scr_centerScores","1"}; + +/* +=============== +Sbar_ShowTeamScores + +Tab key down +=============== +*/ +void Sbar_ShowTeamScores (void) +{ + if (sb_showteamscores) + return; + + sb_showteamscores = true; + sb_updates = 0; +} + +/* +=============== +Sbar_DontShowTeamScores + +Tab key up +=============== +*/ +void Sbar_DontShowTeamScores (void) +{ + sb_showteamscores = false; + sb_updates = 0; +} + +/* +=============== +Sbar_ShowScores + +Tab key down +=============== +*/ +void Sbar_ShowScores (void) +{ + if (sb_showscores) + return; + + sb_showscores = true; + sb_updates = 0; +} + +/* +=============== +Sbar_DontShowScores + +Tab key up +=============== +*/ +void Sbar_DontShowScores (void) +{ + sb_showscores = false; + sb_updates = 0; +} + +/* +=============== +Sbar_Changed +=============== +*/ +void Sbar_Changed (void) +{ + sb_updates = 0; // update next frame +} + +/* +=============== +Sbar_Init +=============== +*/ +void Sbar_Init (void) +{ + int i; + + for (i=0 ; i<10 ; i++) + { + sb_nums[0][i] = Draw_PicFromWad (va("num_%i",i)); + sb_nums[1][i] = Draw_PicFromWad (va("anum_%i",i)); + } + + sb_nums[0][10] = Draw_PicFromWad ("num_minus"); + sb_nums[1][10] = Draw_PicFromWad ("anum_minus"); + + sb_colon = Draw_PicFromWad ("num_colon"); + sb_slash = Draw_PicFromWad ("num_slash"); + + sb_weapons[0][0] = Draw_PicFromWad ("inv_shotgun"); + sb_weapons[0][1] = Draw_PicFromWad ("inv_sshotgun"); + sb_weapons[0][2] = Draw_PicFromWad ("inv_nailgun"); + sb_weapons[0][3] = Draw_PicFromWad ("inv_snailgun"); + sb_weapons[0][4] = Draw_PicFromWad ("inv_rlaunch"); + sb_weapons[0][5] = Draw_PicFromWad ("inv_srlaunch"); + sb_weapons[0][6] = Draw_PicFromWad ("inv_lightng"); + + sb_weapons[1][0] = Draw_PicFromWad ("inv2_shotgun"); + sb_weapons[1][1] = Draw_PicFromWad ("inv2_sshotgun"); + sb_weapons[1][2] = Draw_PicFromWad ("inv2_nailgun"); + sb_weapons[1][3] = Draw_PicFromWad ("inv2_snailgun"); + sb_weapons[1][4] = Draw_PicFromWad ("inv2_rlaunch"); + sb_weapons[1][5] = Draw_PicFromWad ("inv2_srlaunch"); + sb_weapons[1][6] = Draw_PicFromWad ("inv2_lightng"); + + for (i=0 ; i<5 ; i++) + { + sb_weapons[2+i][0] = Draw_PicFromWad (va("inva%i_shotgun",i+1)); + sb_weapons[2+i][1] = Draw_PicFromWad (va("inva%i_sshotgun",i+1)); + sb_weapons[2+i][2] = Draw_PicFromWad (va("inva%i_nailgun",i+1)); + sb_weapons[2+i][3] = Draw_PicFromWad (va("inva%i_snailgun",i+1)); + sb_weapons[2+i][4] = Draw_PicFromWad (va("inva%i_rlaunch",i+1)); + sb_weapons[2+i][5] = Draw_PicFromWad (va("inva%i_srlaunch",i+1)); + sb_weapons[2+i][6] = Draw_PicFromWad (va("inva%i_lightng",i+1)); + } + + sb_ammo[0] = Draw_PicFromWad ("sb_shells"); + sb_ammo[1] = Draw_PicFromWad ("sb_nails"); + sb_ammo[2] = Draw_PicFromWad ("sb_rocket"); + sb_ammo[3] = Draw_PicFromWad ("sb_cells"); + + sb_armor[0] = Draw_PicFromWad ("sb_armor1"); + sb_armor[1] = Draw_PicFromWad ("sb_armor2"); + sb_armor[2] = Draw_PicFromWad ("sb_armor3"); + + sb_items[0] = Draw_PicFromWad ("sb_key1"); + sb_items[1] = Draw_PicFromWad ("sb_key2"); + sb_items[2] = Draw_PicFromWad ("sb_invis"); + sb_items[3] = Draw_PicFromWad ("sb_invuln"); + sb_items[4] = Draw_PicFromWad ("sb_suit"); + sb_items[5] = Draw_PicFromWad ("sb_quad"); + + sb_sigil[0] = Draw_PicFromWad ("sb_sigil1"); + sb_sigil[1] = Draw_PicFromWad ("sb_sigil2"); + sb_sigil[2] = Draw_PicFromWad ("sb_sigil3"); + sb_sigil[3] = Draw_PicFromWad ("sb_sigil4"); + + sb_faces[4][0] = Draw_PicFromWad ("face1"); + sb_faces[4][1] = Draw_PicFromWad ("face_p1"); + sb_faces[3][0] = Draw_PicFromWad ("face2"); + sb_faces[3][1] = Draw_PicFromWad ("face_p2"); + sb_faces[2][0] = Draw_PicFromWad ("face3"); + sb_faces[2][1] = Draw_PicFromWad ("face_p3"); + sb_faces[1][0] = Draw_PicFromWad ("face4"); + sb_faces[1][1] = Draw_PicFromWad ("face_p4"); + sb_faces[0][0] = Draw_PicFromWad ("face5"); + sb_faces[0][1] = Draw_PicFromWad ("face_p5"); + + sb_face_invis = Draw_PicFromWad ("face_invis"); + sb_face_invuln = Draw_PicFromWad ("face_invul2"); + sb_face_invis_invuln = Draw_PicFromWad ("face_inv2"); + sb_face_quad = Draw_PicFromWad ("face_quad"); + + Cvar_RegisterVariable (&scr_centerScores); + Cvar_RegisterVariable (&scr_centerSbar); + + Cmd_AddCommand ("+showscores", Sbar_ShowScores); + Cmd_AddCommand ("-showscores", Sbar_DontShowScores); + + Cmd_AddCommand ("+showteamscores", Sbar_ShowTeamScores); + Cmd_AddCommand ("-showteamscores", Sbar_DontShowTeamScores); + + sb_sbar = Draw_PicFromWad ("sbar"); + sb_ibar = Draw_PicFromWad ("ibar"); + sb_scorebar = Draw_PicFromWad ("scorebar"); +} + + +//============================================================================= + +// drawing routines are relative to the status bar location + +/* +============= +Sbar_DrawPic +============= +*/ +void Sbar_DrawPic (int x, int y, qpic_t *pic) +{ + Draw_Pic (x + sbar_xofs, y + (vid.height-SBAR_HEIGHT), pic); +} + +/* +============= +Sbar_DrawSubPic +============= +JACK: Draws a portion of the picture in the status bar. +*/ + +void Sbar_DrawSubPic(int x, int y, qpic_t *pic, int srcx, int srcy, int width, int height) +{ + Draw_SubPic (x, y+(vid.height-SBAR_HEIGHT), pic, srcx, srcy, width, height); +} + + +/* +============= +Sbar_DrawTransPic +============= +*/ +void Sbar_DrawTransPic (int x, int y, qpic_t *pic) +{ + Draw_TransPic (x + sbar_xofs, y + (vid.height-SBAR_HEIGHT), pic); +} + +/* +================ +Sbar_DrawCharacter + +Draws one solid graphics character +================ +*/ +void Sbar_DrawCharacter (int x, int y, int num) +{ + Draw_Character (x + 4 + sbar_xofs, y + vid.height-SBAR_HEIGHT, num); +} + +/* +================ +Sbar_DrawString +================ +*/ +void Sbar_DrawString (int x, int y, char *str) +{ + Draw_String (x + sbar_xofs, y+ vid.height-SBAR_HEIGHT, str); +} + +/* +============= +Sbar_itoa +============= +*/ +int Sbar_itoa (int num, char *buf) +{ + char *str; + int pow10; + int dig; + + str = buf; + + if (num < 0) + { + *str++ = '-'; + num = -num; + } + + for (pow10 = 10 ; num >= pow10 ; pow10 *= 10) + ; + + do + { + pow10 /= 10; + dig = num/pow10; + *str++ = '0'+dig; + num -= dig*pow10; + } while (pow10 != 1); + + *str = 0; + + return str-buf; +} + + +/* +============= +Sbar_DrawNum +============= +*/ +void Sbar_DrawNum (int x, int y, int num, int digits, int color) +{ + char str[12]; + char *ptr; + int l, frame; + + l = Sbar_itoa (num, str); + ptr = str; + if (l > digits) + ptr += (l-digits); + if (l < digits) + x += (digits-l)*24; + + while (*ptr) + { + if (*ptr == '-') + frame = STAT_MINUS; + else + frame = *ptr -'0'; + + Sbar_DrawTransPic (x,y,sb_nums[color][frame]); + x += 24; + ptr++; + } +} + +//============================================================================= + +int fragsort[MAX_CLIENTS]; +int scoreboardlines; +typedef struct { + char team[16+1]; + int frags; + int players; + int plow, phigh, ptotal; +} team_t; +team_t teams[MAX_CLIENTS]; +int teamsort[MAX_CLIENTS]; +int scoreboardteams; + +/* +=============== +Sbar_SortFrags +=============== +*/ +void Sbar_SortFrags (qboolean includespec) +{ + int i, j, k; + +// sort by frags + scoreboardlines = 0; + for (i=0 ; iname[0]) + continue; + if (s->spectator) + continue; + + // find his team in the list + Q_strncpyz (t, Info_ValueForKey(s->userinfo, "team"), sizeof(t)); + if (!t[0]) + continue; // not on team + for (j = 0; j < scoreboardteams; j++) + if (!strcmp(teams[j].team, t)) { + teams[j].frags += s->frags; + teams[j].players++; + goto addpinginfo; + } + if (j == scoreboardteams) { // must add him + j = scoreboardteams++; + strcpy(teams[j].team, t); + teams[j].frags = s->frags; + teams[j].players = 1; +addpinginfo: + if (teams[j].plow > s->ping) + teams[j].plow = s->ping; + if (teams[j].phigh < s->ping) + teams[j].phigh = s->ping; + teams[j].ptotal += s->ping; + } + } + + // sort + for (i = 0; i < scoreboardteams; i++) + teamsort[i] = i; + + // good 'ol bubble sort + for (i = 0; i < scoreboardteams - 1; i++) + for (j = i + 1; j < scoreboardteams; j++) + if (teams[teamsort[i]].frags < teams[teamsort[j]].frags) { + k = teamsort[i]; + teamsort[i] = teamsort[j]; + teamsort[j] = k; + } +} + +int Sbar_ColorForMap (int m) +{ + m = (m < 0) ? 0 : ((m > 13) ? 13 : m); + + m *= 16; + return m < 128 ? m + 8 : m + 8; +} + + +/* +=============== +Sbar_SoloScoreboard +=============== +*/ +void Sbar_SoloScoreboard (void) +{ + char str[80]; + double _time; + int minutes, seconds, tens, units; + + Sbar_DrawPic (0, 0, sb_scorebar); + + if (cl.gametype == GAME_COOP) + { + sprintf(str, "Monsters:%3i /%3i", cl.stats[STAT_MONSTERS], cl.stats[STAT_TOTALMONSTERS]); + Sbar_DrawString (8, 4, str); + + sprintf(str, "Secrets :%3i /%3i", cl.stats[STAT_SECRETS], cl.stats[STAT_TOTALSECRETS]); + Sbar_DrawString (8, 12, str); + } + + // time + _time = cl.time; + if (cl.gametype == GAME_COOP) + _time -= cl.players[cl.playernum].entertime; + minutes = _time / 60; + seconds = _time - 60*minutes; + tens = seconds / 10; + units = seconds - 10*tens; + sprintf (str,"Time :%3i:%i%i", minutes, tens, units); + Sbar_DrawString (184, 4, str); + + if (cl.gametype == GAME_COOP) + { + // draw level name + int l = strlen (cl.levelname); + if (l < 22 && !strstr(cl.levelname, "\n")) + Sbar_DrawString (232 - l*4, 12, cl.levelname); + } +} + +//============================================================================= + +/* +=============== +Sbar_DrawInventory +=============== +*/ +void Sbar_DrawInventory (void) +{ + int i; + char num[6]; + float time; + int flashon; + qboolean headsup; + qboolean hudswap; + + headsup = !(cl_sbar.value || scr_viewsize.value<100); + hudswap = cl_hudswap.value; // Get that nasty float out :) + + if (!headsup) + Sbar_DrawPic (0, -24, sb_ibar); +// weapons + for (i=0 ; i<7 ; i++) + { + if (cl.stats[STAT_ITEMS] & (IT_SHOTGUN<= 10) + { + if ( cl.stats[STAT_ACTIVEWEAPON] == (IT_SHOTGUN<200) + Sbar_DrawSubPic ((hudswap) ? 0 : (vid.width-24),-68-(7-i)*16 , sb_weapons[flashon][i],0,0,24,16); + + } else + Sbar_DrawPic (i*24, -16, sb_weapons[flashon][i]); +// Sbar_DrawSubPic (0,0,20,20,i*24, -16, sb_weapons[flashon][i]); + + if (flashon > 1) + sb_updates = 0; // force update to remove flash + } + } + +// ammo counts + for (i=0 ; i<4 ; i++) + { + sprintf (num, "%3i",cl.stats[STAT_SHELLS+i] ); + if (headsup) { +// Sbar_DrawSubPic(3, -24, sb_ibar, 3, 0, 42,11); + Sbar_DrawSubPic((hudswap) ? 0 : (vid.width-42), -24 - (4-i)*11, sb_ibar, 3+(i*48), 0, 42, 11); + if (num[0] != ' ') + Draw_Character ( (hudswap) ? 7: (vid.width - 35), vid.height-SBAR_HEIGHT-24 - (4-i)*11, 18 + num[0] - '0'); + if (num[1] != ' ') + Draw_Character ( (hudswap) ? 15: (vid.width - 27), vid.height-SBAR_HEIGHT-24 - (4-i)*11, 18 + num[1] - '0'); + if (num[2] != ' ') + Draw_Character ( (hudswap) ? 23: (vid.width - 19), vid.height-SBAR_HEIGHT-24 - (4-i)*11, 18 + num[2] - '0'); + } else { + if (num[0] != ' ') + Sbar_DrawCharacter ( (6*i+1)*8 - 2, -24, 18 + num[0] - '0'); + if (num[1] != ' ') + Sbar_DrawCharacter ( (6*i+2)*8 - 2, -24, 18 + num[1] - '0'); + if (num[2] != ' ') + Sbar_DrawCharacter ( (6*i+3)*8 - 2, -24, 18 + num[2] - '0'); + } + } + + flashon = 0; +// items + for (i=0 ; i<6 ; i++) + if (cl.stats[STAT_ITEMS] & (1<<(17+i))) + { + time = cl.item_gettime[17+i]; + if (time && time > cl.time - 2 && flashon ) + { // flash frame + sb_updates = 0; + } + else + Sbar_DrawPic (192 + i*16, -16, sb_items[i]); + if (time && time > cl.time - 2) + sb_updates = 0; + } + +// sigils + for (i=0 ; i<4 ; i++) + if (cl.stats[STAT_ITEMS] & (1<<(28+i))) + { + time = cl.item_gettime[28+i]; + if (time && time > cl.time - 2 && flashon ) + { // flash frame + sb_updates = 0; + } + else + Sbar_DrawPic (320-32 + i*8, -16, sb_sigil[i]); + if (time && time > cl.time - 2) + sb_updates = 0; + } +} + +//============================================================================= + +/* +=============== +Sbar_DrawFrags +=============== +*/ +void Sbar_DrawFrags (void) +{ + int i, k, l; + int top, bottom; + int x, y, f; + char num[12]; + player_info_t *s; + + if (cl.gametype == GAME_COOP) + return; + + Sbar_SortFrags (false); + +// draw the text + l = scoreboardlines <= 4 ? scoreboardlines : 4; + + x = 23; +// xofs = (vid.width - 320)>>1; + y = vid.height - SBAR_HEIGHT - 23; + + for (i=0 ; iname[0]) + continue; + if (s->spectator) + continue; + + // draw background + top = s->topcolor; + bottom = s->bottomcolor; + top = (top < 0) ? 0 : ((top > 13) ? 13 : top); + bottom = (bottom < 0) ? 0 : ((bottom > 13) ? 13 : bottom); + + top = Sbar_ColorForMap (top); + bottom = Sbar_ColorForMap (bottom); + + Draw_Fill (sbar_xofs + x*8 + 10, y, 28, 4, top); + Draw_Fill (sbar_xofs + x*8 + 10, y+4, 28, 3, bottom); + + // draw number + f = s->frags; + sprintf (num, "%3i",f); + + Sbar_DrawCharacter ((x+1)*8 , -24, num[0]); + Sbar_DrawCharacter ((x+2)*8 , -24, num[1]); + Sbar_DrawCharacter ((x+3)*8 , -24, num[2]); + + if (k == cl.playernum) + { + Sbar_DrawCharacter (x*8+2, -24, 16); + Sbar_DrawCharacter ((x+4)*8-4, -24, 17); + } + x+=4; + } +} + +//============================================================================= + + +/* +=============== +Sbar_DrawFace +=============== +*/ +void Sbar_DrawFace (void) +{ + int f, anim; + + if ( (cl.stats[STAT_ITEMS] & (IT_INVISIBILITY | IT_INVULNERABILITY) ) + == (IT_INVISIBILITY | IT_INVULNERABILITY) ) + { + Sbar_DrawPic (112, 0, sb_face_invis_invuln); + return; + } + if (cl.stats[STAT_ITEMS] & IT_QUAD) + { + Sbar_DrawPic (112, 0, sb_face_quad ); + return; + } + if (cl.stats[STAT_ITEMS] & IT_INVISIBILITY) + { + Sbar_DrawPic (112, 0, sb_face_invis ); + return; + } + if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY) + { + Sbar_DrawPic (112, 0, sb_face_invuln); + return; + } + + if (cl.stats[STAT_HEALTH] >= 100) + f = 4; + else + f = cl.stats[STAT_HEALTH] / 20; + + if (cl.time <= cl.faceanimtime) + { + anim = 1; + sb_updates = 0; // make sure the anim gets drawn over + } + else + anim = 0; + Sbar_DrawPic (112, 0, sb_faces[f][anim]); +} + +/* +============= +Sbar_DrawNormal +============= +*/ +void Sbar_DrawNormal (void) +{ + if (cl_sbar.value || scr_viewsize.value<100) + Sbar_DrawPic (0, 0, sb_sbar); + +// armor + if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY) + { + Sbar_DrawNum (24, 0, 666, 3, 1); + Sbar_DrawPic (0, 0, draw_disc); + } + else + { + Sbar_DrawNum (24, 0, cl.stats[STAT_ARMOR], 3 + , cl.stats[STAT_ARMOR] <= 25); + if (cl.stats[STAT_ITEMS] & IT_ARMOR3) + Sbar_DrawPic (0, 0, sb_armor[2]); + else if (cl.stats[STAT_ITEMS] & IT_ARMOR2) + Sbar_DrawPic (0, 0, sb_armor[1]); + else if (cl.stats[STAT_ITEMS] & IT_ARMOR1) + Sbar_DrawPic (0, 0, sb_armor[0]); + } + +// face + Sbar_DrawFace (); + +// health + Sbar_DrawNum (136, 0, cl.stats[STAT_HEALTH], 3 + , cl.stats[STAT_HEALTH] <= 25); + +// ammo icon + if (cl.stats[STAT_ITEMS] & IT_SHELLS) + Sbar_DrawPic (224, 0, sb_ammo[0]); + else if (cl.stats[STAT_ITEMS] & IT_NAILS) + Sbar_DrawPic (224, 0, sb_ammo[1]); + else if (cl.stats[STAT_ITEMS] & IT_ROCKETS) + Sbar_DrawPic (224, 0, sb_ammo[2]); + else if (cl.stats[STAT_ITEMS] & IT_CELLS) + Sbar_DrawPic (224, 0, sb_ammo[3]); + + Sbar_DrawNum (248, 0, cl.stats[STAT_AMMO], 3 + , cl.stats[STAT_AMMO] <= 10); +} + +/* +=============== +Sbar_Draw +=============== +*/ +void Sbar_Draw (void) +{ + qboolean headsup; + char st[512]; + + headsup = !(cl_sbar.value || scr_viewsize.value<100); + if ((sb_updates >= vid.numpages) && !headsup) + return; + + if (scr_con_current == vid.height) + return; // console is full screen + + scr_copyeverything = 1; +// scr_fullupdate = 0; + + sb_updates++; + + if (scr_centerSbar.value) + sbar_xofs = (vid.width - 320)>>1; + else + sbar_xofs = 0; + +// top line + if (sb_lines > 24) + { + if (!cl.spectator || autocam == CAM_TRACK) + Sbar_DrawInventory (); + if (!headsup || vid.width<512) + Sbar_DrawFrags (); + } + +// main area + if (sb_lines > 0) + { + if (cl.spectator) { + if (autocam != CAM_TRACK) { + Sbar_DrawPic (0, 0, sb_scorebar); + Sbar_DrawString (160-7*8,4, "SPECTATOR MODE"); + Sbar_DrawString(160-14*8+4, 12, "Press [ATTACK] for AutoCamera"); + } else { + if (sb_showscores || cl.stats[STAT_HEALTH] <= 0) + Sbar_SoloScoreboard (); + else + Sbar_DrawNormal (); + +// Sbar_DrawString (160-14*8+4,4, "SPECTATOR MODE - TRACK CAMERA"); + sprintf(st, "Tracking %-.13s, [JUMP] for next", + cl.players[spec_track].name); + Sbar_DrawString(0, -8, st); + } + } else if (sb_showscores || cl.stats[STAT_HEALTH] <= 0) + Sbar_SoloScoreboard (); + else + Sbar_DrawNormal (); + } + +// main screen deathmatch rankings + // if we're dead show team scores in team games + if (cl.stats[STAT_HEALTH] <= 0 && !cl.spectator) + if (atoi(Info_ValueForKey(cl.serverinfo, "teamplay")) > 0 && + !sb_showscores) + Sbar_TeamOverlay(); + else + Sbar_DeathmatchOverlay (0); + else if (sb_showscores) + Sbar_DeathmatchOverlay (0); + else if (sb_showteamscores) + Sbar_TeamOverlay(); + +#ifdef GLQUAKE + if (sb_showscores || sb_showteamscores || + cl.stats[STAT_HEALTH] <= 0) + sb_updates = 0; + + // clear unused areas in GL + if (vid.width > 320 && !headsup) { + // left + if (scr_centerSbar.value) + Draw_TileClear (0, vid.height - sb_lines, sbar_xofs, sb_lines); + // right + Draw_TileClear (320 + sbar_xofs, vid.height - sb_lines, vid.width - (320 + sbar_xofs), sb_lines); + } + if (!headsup && cl.spectator && (autocam != CAM_TRACK) && (sb_lines > SBAR_HEIGHT)) + Draw_TileClear (sbar_xofs, vid.height - sb_lines, 320, sb_lines - SBAR_HEIGHT); +#endif + + if (sb_lines > 0 && cl.gametype == GAME_DEATHMATCH + && !scr_centerSbar.value) + Sbar_MiniDeathmatchOverlay (); +} + +//============================================================================= + +/* +================== +Sbar_IntermissionNumber + +================== +*/ +void Sbar_IntermissionNumber (int x, int y, int num, int digits, int color) +{ + char str[12]; + char *ptr; + int l, frame; + + l = Sbar_itoa (num, str); + ptr = str; + if (l > digits) + ptr += (l-digits); + if (l < digits) + x += (digits-l)*24; + + while (*ptr) + { + if (*ptr == '-') + frame = STAT_MINUS; + else + frame = *ptr -'0'; + + Draw_TransPic (x,y,sb_nums[color][frame]); + x += 24; + ptr++; + } +} + +/* +================== +Sbar_TeamOverlay + +team frags +added by Zoid +================== +*/ +void Sbar_TeamOverlay (void) +{ + qpic_t *pic; + int i, k, l; + int x, y; + int xofs; + char num[12]; + int teamplay; + char team[4+1]; + team_t *tm; + int plow, phigh, pavg; + +// request new ping times every two second + teamplay = atoi(Info_ValueForKey(cl.serverinfo, "teamplay")); + + + if (!teamplay) { + Sbar_DeathmatchOverlay(0); + return; + } + + scr_copyeverything = 1; + scr_fullupdate = 0; + + if (scr_centerScores.value) + xofs = (vid.width - 320)>>1; + else + xofs = 0; + + pic = Draw_CachePic ("gfx/ranking.lmp"); + Draw_Pic (xofs + 160 - pic->width/2, 0, pic); + + y = 24; + x = xofs + 36; + Draw_String(x, y, "low/avg/high team total players"); + y += 8; +// Draw_String(x, y, "------------ ---- ----- -------"); + Draw_String(x, y, "\x1d\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1f \x1d\x1e\x1e\x1f \x1d\x1e\x1e\x1e\x1f \x1d\x1e\x1e\x1e\x1e\x1e\x1f"); + y += 8; + +// sort the teams + Sbar_SortTeams(); + +// draw the text + l = scoreboardlines; + + for (i=0 ; i < scoreboardteams && y <= vid.height-10 ; i++) + { + k = teamsort[i]; + tm = teams + k; + + // draw pings + plow = tm->plow; + if (plow < 0 || plow > 999) + plow = 999; + phigh = tm->phigh; + if (phigh < 0 || phigh > 999) + phigh = 999; + if (!tm->players) + pavg = 999; + else + pavg = tm->ptotal / tm->players; + if (pavg < 0 || pavg > 999) + pavg = 999; + + sprintf (num, "%3i/%3i/%3i", plow, pavg, phigh); + Draw_String ( x, y, num); + + // draw team + Q_strncpyz (team, tm->team, sizeof(team)); + Draw_String (x + 104, y, team); + + // draw total + sprintf (num, "%5i", tm->frags); + Draw_String (x + 104 + 40, y, num); + + // draw players + sprintf (num, "%5i", tm->players); + Draw_String (x + 104 + 88, y, num); + + if (!strncmp(Info_ValueForKey(cl.players[cl.playernum].userinfo, + "team"), tm->team, 16)) { + Draw_Character ( x + 104 - 8, y, 16); + Draw_Character ( x + 104 + 32, y, 17); + } + + y += 8; + } + y += 8; + Sbar_DeathmatchOverlay(y); +} + +/* +================== +Sbar_DeathmatchOverlay + +ping time frags name +================== +*/ +void Sbar_DeathmatchOverlay (int start) +{ + qpic_t *pic; + int i, k, l; + int top, bottom; + int x, y, f; + int xofs; + char num[12]; + player_info_t *s; + int total; + int minutes; + int p; + int teamplay; + char team[5]; + int skip = 10; + + if (largegame) + skip = 8; + +// request new ping times every two second + if (realtime - cl.last_ping_request > 2) + { + cl.last_ping_request = realtime; + MSG_WriteByte (&cls.netchan.message, clc_stringcmd); + SZ_Print (&cls.netchan.message, "pings"); + } + + teamplay = atoi(Info_ValueForKey(cl.serverinfo, "teamplay")); + + scr_copyeverything = 1; + scr_fullupdate = 0; + + if (scr_centerScores.value) + xofs = (vid.width - 320)>>1; + else + xofs = 0; + + if (!start) { + pic = Draw_CachePic ("gfx/ranking.lmp"); + Draw_Pic (xofs + 160 - pic->width/2, 0, pic); + } + +// scores + Sbar_SortFrags (true); + +// draw the text + l = scoreboardlines; + + if (start) + y = start; + else + y = 24; + if (teamplay) + { + x = xofs + 4; +// 0 40 64 104 152 192 + Draw_String ( x , y, "ping pl time frags team name"); + y += 8; +// Draw_String ( x , y, "---- -- ---- ----- ---- ----------------"); + Draw_String ( x , y, "\x1d\x1e\x1e\x1f \x1d\x1f \x1d\x1e\x1e\x1f \x1d\x1e\x1e\x1e\x1f \x1d\x1e\x1e\x1f \x1d\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1f"); + y += 8; + } + else + { + x = xofs + 16; +// 0 40 64 104 152 + Draw_String ( x , y, "ping pl time frags name"); + y += 8; +// Draw_String ( x , y, "---- -- ---- ----- ----------------"); + Draw_String ( x , y, "\x1d\x1e\x1e\x1f \x1d\x1f \x1d\x1e\x1e\x1f \x1d\x1e\x1e\x1e\x1f \x1d\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1f"); + y += 8; + } + + for (i=0 ; iname[0]) + continue; + + // draw ping + p = s->ping; + if (p < 0 || p > 999) + p = 999; + sprintf (num, "%4i", p); + Draw_String ( x, y, num); + + // draw pl + p = s->pl; + sprintf (num, "%3i", p); + if (p > 25) + Draw_Alt_String ( x+32, y, num); + else + Draw_String ( x+32, y, num); + + if (s->spectator) + { + Draw_String (x+40, y, "(spectator)"); + // draw name + if (teamplay) + Draw_String (x+152+40, y, s->name); + else + Draw_String (x+152, y, s->name); + y += skip; + continue; + } + + + // draw time + if (cl.intermission) + total = cl.completed_time - s->entertime; + else + total = realtime - s->entertime; + minutes = (int)total/60; + sprintf (num, "%4i", minutes); + Draw_String ( x+64 , y, num); + + // draw background + top = s->topcolor; + bottom = s->bottomcolor; + top = Sbar_ColorForMap (top); + bottom = Sbar_ColorForMap (bottom); + + if (largegame) + Draw_Fill ( x+104, y+1, 40, 3, top); + else + Draw_Fill ( x+104, y, 40, 4, top); + Draw_Fill ( x+104, y+4, 40, 4, bottom); + + // draw number + f = s->frags; + sprintf (num, "%3i",f); + + Draw_Character ( x+112 , y, num[0]); + Draw_Character ( x+120 , y, num[1]); + Draw_Character ( x+128 , y, num[2]); + + if (k == cl.playernum) + { + Draw_Character ( x + 104, y, 16); + Draw_Character ( x + 136, y, 17); + } + + // team + if (teamplay) + { + Q_strncpyz (team, Info_ValueForKey(s->userinfo, "team"), sizeof(team)); + Draw_String (x+152, y, team); + } + + // draw name + if (teamplay) + Draw_String (x+152+40, y, s->name); + else + Draw_String (x+152, y, s->name); + + y += skip; + } + + if (y >= vid.height-10) // we ran over the screen size, squish + largegame = true; +} + +/* +================== +Sbar_MiniDeathmatchOverlay + +frags name +frags team name +displayed to right of status bar if there's room +================== +*/ +void Sbar_MiniDeathmatchOverlay (void) +{ + int i, k; + int top, bottom; + int x, y, f; + char num[12]; + player_info_t *s; + int teamplay; + char team[4+1]; + int numlines; + char name[16+1]; + team_t *tm; + + if (vid.width < 512 || !sb_lines) + return; // not enuff room + + teamplay = atoi(Info_ValueForKey(cl.serverinfo, "teamplay")); + + scr_copyeverything = 1; + scr_fullupdate = 0; + +// scores + Sbar_SortFrags (false); + if (vid.width >= 640) + Sbar_SortTeams(); + + if (!scoreboardlines) + return; // no one there? + +// draw the text + y = vid.height - sb_lines - 1; + numlines = sb_lines/8; + if (numlines < 3) + return; // not enough room + + // find us + for (i=0 ; i < scoreboardlines; i++) + if (fragsort[i] == cl.playernum) + break; + + if (i == scoreboardlines) // we're not there, we are probably a spectator, just display top + i = 0; + else // figure out start + i = i - numlines/2; + + if (i > scoreboardlines - numlines) + i = scoreboardlines - numlines; + if (i < 0) + i = 0; + + x = 324; + + for (/* */ ; i < scoreboardlines && y < vid.height - 8 + 1; i++) + { + k = fragsort[i]; + s = &cl.players[k]; + if (!s->name[0]) + continue; + + // draw ping + top = s->topcolor; + bottom = s->bottomcolor; + top = Sbar_ColorForMap (top); + bottom = Sbar_ColorForMap (bottom); + + Draw_Fill ( x, y+1, 40, 3, top); + Draw_Fill ( x, y+4, 40, 4, bottom); + + // draw number + f = s->frags; + sprintf (num, "%3i",f); + + Draw_Character ( x+8 , y, num[0]); + Draw_Character ( x+16, y, num[1]); + Draw_Character ( x+24, y, num[2]); + + if (k == cl.playernum) + { + Draw_Character ( x, y, 16); + Draw_Character ( x + 32, y, 17); + } + + // team + if (teamplay) + { + Q_strncpyz (team, Info_ValueForKey(s->userinfo, "team"), sizeof(team)); + Draw_String (x+48, y, team); + } + + // draw name + Q_strncpyz (name, s->name, sizeof(name)); + if (teamplay) + Draw_String (x+48+40, y, name); + else + Draw_String (x+48, y, name); + y += 8; + } + + // draw teams if room + if (vid.width < 640 || !teamplay) + return; + + // draw seperator + x += 208; + for (y = vid.height - sb_lines; y < vid.height - 6; y += 2) + Draw_Character(x, y, 14); + + x += 16; + + y = vid.height - sb_lines; + for (i=0 ; i < scoreboardteams && y <= vid.height; i++) + { + k = teamsort[i]; + tm = teams + k; + + // draw pings + Q_strncpyz (team, tm->team, sizeof(team)); + Draw_String (x, y, team); + + // draw total + sprintf (num, "%5i", tm->frags); + Draw_String (x + 40, y, num); + + if (!strncmp(Info_ValueForKey(cl.players[cl.playernum].userinfo, + "team"), tm->team, 16)) { + Draw_Character ( x - 8, y, 16); + Draw_Character ( x + 32, y, 17); + } + + y += 8; + } + +} + + +/* +================== +Sbar_IntermissionOverlay + +================== +*/ +void Sbar_IntermissionOverlay (void) +{ + qpic_t *pic; + int dig; + int num; + int xofs; + + scr_copyeverything = 1; + scr_fullupdate = 0; + + if (cl.gametype == GAME_DEATHMATCH) + { + if (atoi(Info_ValueForKey(cl.serverinfo, "teamplay")) > 0 && !sb_showscores) + Sbar_TeamOverlay (); + else + Sbar_DeathmatchOverlay (0); + return; + } + + xofs = (vid.width - 320)>>1; + + pic = Draw_CachePic ("gfx/complete.lmp"); + Draw_Pic (xofs + 64, 24, pic); + + pic = Draw_CachePic ("gfx/inter.lmp"); + Draw_TransPic (xofs, 56, pic); + + // time + dig = (cl.completed_time - cl.players[cl.playernum].entertime) / 60; + Sbar_IntermissionNumber (xofs + 160, 64, dig, 3, 0); + num = (cl.completed_time - cl.players[cl.playernum].entertime) - dig*60; + Draw_TransPic (xofs + 234, 64, sb_colon); + Draw_TransPic (xofs + 246, 64, sb_nums[0][num/10]); + Draw_TransPic (xofs + 266, 64, sb_nums[0][num%10]); + + Sbar_IntermissionNumber (xofs + 160, 104, cl.stats[STAT_SECRETS], 3, 0); + Draw_TransPic (xofs + 232, 104, sb_slash); + Sbar_IntermissionNumber (xofs + 240, 104, cl.stats[STAT_TOTALSECRETS], 3, 0); + + Sbar_IntermissionNumber (xofs + 160, 144, cl.stats[STAT_MONSTERS], 3, 0); + Draw_TransPic (xofs + 232, 144, sb_slash); + Sbar_IntermissionNumber (xofs + 240, 144, cl.stats[STAT_TOTALMONSTERS], 3, 0); +} + + +/* +================== +Sbar_FinaleOverlay + +================== +*/ +void Sbar_FinaleOverlay (void) +{ + qpic_t *pic; + + scr_copyeverything = 1; + + pic = Draw_CachePic ("gfx/finale.lmp"); + Draw_TransPic ( (vid.width-pic->width)/2, 16, pic); +} + + diff --git a/source/sbar.h b/source/sbar.h index 6a1d8395..286b3b6a 100644 --- a/source/sbar.h +++ b/source/sbar.h @@ -1,39 +1,39 @@ -/* -Copyright (C) 1996-1997 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. - -*/ - -// the status bar is only redrawn if something has changed, but if anything -// does, the entire thing will be redrawn for the next vid.numpages frames. - -#define SBAR_HEIGHT 24 - -extern int sb_lines; // scan lines to draw - -void Sbar_Init (void); - -void Sbar_Changed (void); -// call whenever any of the client stats represented on the sbar changes - -void Sbar_Draw (void); -// called every frame by screen - -void Sbar_IntermissionOverlay (void); -// called each frame after the level has been completed - -void Sbar_FinaleOverlay (void); +/* +Copyright (C) 1996-1997 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. + +*/ + +// the status bar is only redrawn if something has changed, but if anything +// does, the entire thing will be redrawn for the next vid.numpages frames. + +#define SBAR_HEIGHT 24 + +extern int sb_lines; // scan lines to draw + +void Sbar_Init (void); + +void Sbar_Changed (void); +// call whenever any of the client stats represented on the sbar changes + +void Sbar_Draw (void); +// called every frame by screen + +void Sbar_IntermissionOverlay (void); +// called each frame after the level has been completed + +void Sbar_FinaleOverlay (void); diff --git a/source/screen.c b/source/screen.c index 28cf7ba3..60fbea12 100644 --- a/source/screen.c +++ b/source/screen.c @@ -1,1290 +1,1290 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// screen.c -- master for refresh, status bar, console, chat, notify, etc - -#include "quakedef.h" -#include "r_local.h" -#include "keys.h" -#include "sbar.h" -#include "sound.h" - -#include - -/* - -background clear -rendering -turtle/net/ram icons -sbar -centerprint / slow centerprint -notify lines -intermission / finale overlay -loading plaque -console -menu - -required background clears -required update regions - - -syncronous draw mode or async -One off screen buffer, with updates either copied or xblited -Need to double buffer? - - -async draw will require the refresh area to be cleared, because it will be -xblited, but sync draw can just ignore it. - -sync -draw - -CenterPrint () -SlowPrint () -Screen_Update (); -Con_Printf (); - -net -turn off messages option - -the refresh is always rendered, unless the console is full screen - - -console is: - notify lines - half - full - - -*/ - - -// only the refresh window will be updated unless these variables are flagged -int scr_copytop; -int scr_copyeverything; - -float scr_con_current; -float scr_conlines; // lines of console to display - -float oldscreensize, oldfov; -float oldsbar; -cvar_t scr_viewsize = {"viewsize","100",CVAR_ARCHIVE}; -cvar_t scr_fov = {"fov","90",CVAR_ARCHIVE}; // 10 - 170 -cvar_t scr_consize = {"scr_consize","0.5"}; -cvar_t scr_conspeed = {"scr_conspeed","1000"}; -cvar_t scr_centertime = {"scr_centertime","2"}; -cvar_t scr_showram = {"showram","1"}; -cvar_t scr_showturtle = {"showturtle","0"}; -cvar_t scr_showpause = {"showpause","1"}; -cvar_t scr_printspeed = {"scr_printspeed","8"}; -cvar_t scr_allowsnap = {"scr_allowsnap","1"}; - -// Tonik: -cvar_t scr_clock = {"cl_clock","0"}; -cvar_t scr_clock_x = {"cl_clock_x","0"}; -cvar_t scr_clock_y = {"cl_clock_y","-1"}; -cvar_t show_speed = {"show_speed","0"}; - -extern cvar_t show_fps; - -qboolean scr_initialized; // ready to draw - -qpic_t *scr_ram; -qpic_t *scr_net; -qpic_t *scr_turtle; - -int scr_fullupdate; - -int clearconsole; -int clearnotify; - -viddef_t vid; // global video state - -vrect_t *pconupdate; -vrect_t scr_vrect; - -qboolean scr_disabled_for_loading; - -qboolean scr_skipupdate; - -qboolean block_drawing; - -void SCR_ScreenShot_f (void); -void SCR_RSShot_f (void); - -/* -=============================================================================== - -CENTER PRINTING - -=============================================================================== -*/ - -char scr_centerstring[1024]; -float scr_centertime_start; // for slow victory printing -float scr_centertime_off; -int scr_center_lines; -int scr_erase_lines; -int scr_erase_center; - -/* -============== -SCR_CenterPrint - -Called for important messages that should stay in the center of the screen -for a few moments -============== -*/ -void SCR_CenterPrint (char *str) -{ - Q_strncpyz (scr_centerstring, str, sizeof(scr_centerstring)); - scr_centertime_off = scr_centertime.value; - scr_centertime_start = cl.time; - -// count the number of lines for centering - scr_center_lines = 1; - while (*str) - { - if (*str == '\n') - scr_center_lines++; - str++; - } -} - -void SCR_EraseCenterString (void) -{ - int y; - - if (scr_erase_center++ > vid.numpages) - { - scr_erase_lines = 0; - return; - } - - if (scr_center_lines <= 4) - y = vid.height*0.35; - else - y = 48; - - scr_copytop = 1; - Draw_TileClear (0, y, vid.width, min(8*scr_erase_lines, vid.height - y - 1)); -} - -void SCR_DrawCenterString (void) -{ - char *start; - int l; - int j; - int x, y; - int remaining; - -// the finale prints the characters one at a time - if (cl.intermission) - remaining = scr_printspeed.value * (cl.time - scr_centertime_start); - else - remaining = 9999; - - scr_erase_center = 0; - start = scr_centerstring; - - if (scr_center_lines <= 4) - y = vid.height*0.35; - else - y = 48; - - do - { - // scan the width of the line - for (l=0 ; l<40 ; l++) - if (start[l] == '\n' || !start[l]) - break; - x = (vid.width - l*8)/2; - for (j=0 ; j scr_erase_lines) - scr_erase_lines = scr_center_lines; - - scr_centertime_off -= host_frametime; - - if (scr_centertime_off <= 0 && !cl.intermission) - return; - if (key_dest != key_game) - return; - - SCR_DrawCenterString (); -} - -//============================================================================= - -/* -==================== -CalcFov -==================== -*/ -float CalcFov (float fov_x, float width, float height) -{ - float a; - float x; - - if (fov_x < 1 || fov_x > 179) - Sys_Error ("Bad fov: %f", fov_x); - - x = width/tan(fov_x/360*M_PI); - - a = atan (height/x); - - a = a*360/M_PI; - - return a; -} - -/* -================= -SCR_CalcRefdef - -Must be called whenever vid changes -Internal use only -================= -*/ -static void SCR_CalcRefdef (void) -{ - vrect_t vrect; - float size; - - scr_fullupdate = 0; // force a background redraw - vid.recalc_refdef = 0; - -// force the status bar to redraw - Sbar_Changed (); - -//======================================== - -// bound viewsize - if (scr_viewsize.value < 30) - Cvar_Set (&scr_viewsize,"30"); - if (scr_viewsize.value > 120) - Cvar_Set (&scr_viewsize,"120"); - -// bound field of view - if (scr_fov.value < 10) - Cvar_Set (&scr_fov,"10"); - if (scr_fov.value > 170) - Cvar_Set (&scr_fov,"170"); - - r_refdef.fov_x = scr_fov.value; - r_refdef.fov_y = CalcFov (r_refdef.fov_x, r_refdef.vrect.width, r_refdef.vrect.height); - -// intermission is always full screen - if (cl.intermission) - size = 120; - else - size = scr_viewsize.value; - - if (size >= 120) - sb_lines = 0; // no status bar at all - else if (size >= 110) - sb_lines = 24; // no inventory - else - sb_lines = 24+16+8; - -// these calculations mirror those in R_Init() for r_refdef, but take no -// account of water warping - vrect.x = 0; - vrect.y = 0; - vrect.width = vid.width; - vrect.height = vid.height; - - R_SetVrect (&vrect, &scr_vrect, sb_lines); - -// guard against going from one mode to another that's less than half the -// vertical resolution - if (scr_con_current > vid.height) - scr_con_current = vid.height; - -// notify the refresh of the change - R_ViewChanged (&vrect, sb_lines, vid.aspect); -} - - -/* -================= -SCR_SizeUp_f - -Keybinding command -================= -*/ -void SCR_SizeUp_f (void) -{ - if (scr_viewsize.value < 120) { - Cvar_SetValue (&scr_viewsize, scr_viewsize.value+10); - vid.recalc_refdef = 1; - } -} - - -/* -================= -SCR_SizeDown_f - -Keybinding command -================= -*/ -void SCR_SizeDown_f (void) -{ - Cvar_SetValue (&scr_viewsize, scr_viewsize.value-10); - vid.recalc_refdef = 1; -} - -//============================================================================ - -/* -================== -SCR_Init -================== -*/ -void SCR_Init (void) -{ - Cvar_RegisterVariable (&scr_fov); - Cvar_RegisterVariable (&scr_viewsize); - Cvar_RegisterVariable (&scr_consize); - Cvar_RegisterVariable (&scr_conspeed); - Cvar_RegisterVariable (&scr_showram); - Cvar_RegisterVariable (&scr_showturtle); - Cvar_RegisterVariable (&scr_showpause); - Cvar_RegisterVariable (&scr_centertime); - Cvar_RegisterVariable (&scr_printspeed); - Cvar_RegisterVariable (&scr_allowsnap); - -// Tonik: - Cvar_RegisterVariable (&scr_clock_x); - Cvar_RegisterVariable (&scr_clock_y); - Cvar_RegisterVariable (&scr_clock); - Cvar_RegisterVariable (&show_speed); - -// -// register our commands -// - Cmd_AddCommand ("screenshot",SCR_ScreenShot_f); - Cmd_AddCommand ("sizeup",SCR_SizeUp_f); - Cmd_AddCommand ("sizedown",SCR_SizeDown_f); - - scr_ram = Draw_PicFromWad ("ram"); - scr_net = Draw_PicFromWad ("net"); - scr_turtle = Draw_PicFromWad ("turtle"); - - scr_initialized = true; -} - - - -/* -============== -SCR_DrawRam -============== -*/ -void SCR_DrawRam (void) -{ - if (!scr_showram.value) - return; - - if (!r_cache_thrash) - return; - - Draw_Pic (scr_vrect.x+32, scr_vrect.y, scr_ram); -} - -/* -============== -SCR_DrawTurtle -============== -*/ -void SCR_DrawTurtle (void) -{ - static int count; - - if (!scr_showturtle.value) - return; - - if (host_frametime < 0.1) - { - count = 0; - return; - } - - count++; - if (count < 3) - return; - - Draw_Pic (scr_vrect.x, scr_vrect.y, scr_turtle); -} - -/* -============== -SCR_DrawNet -============== -*/ -void SCR_DrawNet (void) -{ - if (cls.netchan.outgoing_sequence - cls.netchan.incoming_acknowledged < UPDATE_BACKUP-1) - return; - if (cls.demoplayback) - return; - - Draw_Pic (scr_vrect.x+64, scr_vrect.y, scr_net); -} - -void SCR_DrawFPS (void) -{ - static double lastframetime; - double t; - extern int fps_count; - static lastfps; - int x, y; - char st[80]; - - if (!show_fps.value) - return; - - t = Sys_DoubleTime(); - if ((t - lastframetime) >= 1.0) { - lastfps = fps_count; - fps_count = 0; - lastframetime = t; - } - - sprintf(st, "%3d FPS", lastfps); - x = vid.width - strlen(st) * 8 - 8; - y = vid.height - sb_lines - 8; -// Draw_TileClear(x, y, strlen(st) * 8, 8); - Draw_String(x, y, st); -} - - -void SCR_DrawSpeed (void) -{ - int x, y; - char st[80]; - vec3_t vel; - float speed, vspeed; - static float maxspeed = 0; - static float display_speed = -1; - static double lastrealtime = 0; - - if (!show_speed.value) - return; - - if (lastrealtime > realtime) - { - lastrealtime = 0; - display_speed = -1; - maxspeed = 0; - } - -// VectorCopy (cl.simvel, vel); - VectorCopy (cl.frames[(cls.netchan.incoming_sequence)&UPDATE_MASK].playerstate[cl.playernum].velocity, vel); - vspeed = vel[2]; - vel[2] = 0; - speed = Length(vel); - - if (speed > maxspeed) - maxspeed = speed; - - if (display_speed >= 0) - { - sprintf(st, "%3d", (int)display_speed); - x = vid.width - strlen(st) * 8 - 8; - y = 8; - // Draw_TileClear(x, y, strlen(st) * 8, 8); - Draw_String(x, y, st); - } - - if (realtime - lastrealtime >= 0.1) - { - lastrealtime = realtime; - display_speed = maxspeed; - maxspeed = 0; - } -} - -void SCR_DrawClock (void) -{ - char str[80]; - int hours, minutes, seconds; - int tens_hours, tens_minutes, tens_seconds; - - if (!scr_clock.value || cls.demoplayback || cl.intermission) - return; - - if (scr_clock.value == 2) - { - time_t t; - struct tm *ptm; - str[0] = 0; - t = time(NULL); - if (t != -1) { - ptm = localtime (&t); - if (ptm) - strftime(str, sizeof(str)-1, "%H:%M:%S", ptm); - } - } - else - { - float time; - time = cl.time; - tens_hours = fmod (time / 36000, 10); - hours = fmod (time / 3600, 10); - tens_minutes = fmod (time / 600, 6); - minutes = fmod (time / 60, 10); - tens_seconds = fmod (time / 10, 6); - seconds = fmod (time, 10); - sprintf (str, "%i%i:%i%i:%i%i", tens_hours, hours, tens_minutes, minutes, - tens_seconds, seconds); - } - - if (scr_clock_y.value < 0) - Draw_String (8 * scr_clock_x.value, vid.height - sb_lines + 8*scr_clock_y.value, str); - else - Draw_String (8 * scr_clock_x.value, 8*scr_clock_y.value, str); -} - - -/* -============== -DrawPause -============== -*/ -void SCR_DrawPause (void) -{ - qpic_t *pic; - - if (!scr_showpause.value) // turn off for screenshots - return; - - if (!cl.paused) - return; - - pic = Draw_CachePic ("gfx/pause.lmp"); - Draw_Pic ( (vid.width - pic->width)/2, - (vid.height - 48 - pic->height)/2, pic); -} - - -//============================================================================= - - -/* -================== -SCR_SetUpToDrawConsole -================== -*/ -void SCR_SetUpToDrawConsole (void) -{ - Con_CheckResize (); - -// decide on the height of the console - if (cls.state != ca_active) - { - scr_conlines = vid.height; // full screen - scr_con_current = scr_conlines; - } - else if (key_dest == key_console) { - scr_conlines = vid.height * scr_consize.value; - if (scr_conlines < 30) - scr_conlines = 30; - if (scr_conlines > vid.height - 10) - scr_conlines = vid.height - 10; - } - else - scr_conlines = 0; // none visible - - if (scr_conlines < scr_con_current) - { - scr_con_current -= scr_conspeed.value*real_frametime*vid.height/320; - if (scr_conlines > scr_con_current) - scr_con_current = scr_conlines; - - } - else if (scr_conlines > scr_con_current) - { - scr_con_current += scr_conspeed.value*real_frametime*vid.height/320; - if (scr_conlines < scr_con_current) - scr_con_current = scr_conlines; - } - - if (clearconsole++ < vid.numpages) - { - scr_copytop = 1; - Draw_TileClear (0,(int)scr_con_current,vid.width, vid.height - (int)scr_con_current); - Sbar_Changed (); - } - else if (clearnotify++ < vid.numpages) - { - scr_copytop = 1; - Draw_TileClear (0,0,vid.width, con_notifylines); - } - else - con_notifylines = 0; -} - -/* -================== -SCR_DrawConsole -================== -*/ -void SCR_DrawConsole (void) -{ - if (scr_con_current) - { - scr_copyeverything = 1; - Con_DrawConsole (scr_con_current); - clearconsole = 0; - } - else - { - if (key_dest == key_game || key_dest == key_message) - Con_DrawNotify (); // only draw notify in game - } -} - - -/* -============================================================================== - - SCREEN SHOTS - -============================================================================== -*/ - - -/* -============== -WritePCXfile -============== -*/ -void WritePCXfile (char *filename, byte *data, int width, int height, - int rowbytes, byte *palette, qboolean upload) -{ - int i, j, length; - pcx_t *pcx; - byte *pack; - - pcx = Hunk_TempAlloc (width*height*2+1000); - if (pcx == NULL) - { - Con_Printf("SCR_ScreenShot_f: not enough memory\n"); - return; - } - - pcx->manufacturer = 0x0a; // PCX id - pcx->version = 5; // 256 color - pcx->encoding = 1; // uncompressed - pcx->bits_per_pixel = 8; // 256 color - pcx->xmin = 0; - pcx->ymin = 0; - pcx->xmax = LittleShort((short)(width-1)); - pcx->ymax = LittleShort((short)(height-1)); - pcx->hres = LittleShort((short)width); - pcx->vres = LittleShort((short)height); - memset (pcx->palette,0,sizeof(pcx->palette)); - pcx->color_planes = 1; // chunky image - pcx->bytes_per_line = LittleShort((short)width); - pcx->palette_type = LittleShort(2); // not a grey scale - memset (pcx->filler,0,sizeof(pcx->filler)); - -// pack the image - pack = &pcx->data; - - for (i=0 ; i>4; - col = num&15; - source = draw_chars + (row<<10) + (col<<3); - - drawline = 8; - - while (drawline--) - { - for (x=0 ; x<8 ; x++) - if (source[x]) - dest[x] = source[x]; - else - dest[x] = 98; - source += 128; - dest += width; - } - -} - -void SCR_DrawStringToSnap (const char *s, byte *buf, int x, int y, int width) -{ - byte *dest; - const unsigned char *p; - - dest = buf + ((y * width) + x); - - p = (const unsigned char *)s; - while (*p) { - SCR_DrawCharToSnap(*p++, dest, width); - dest += 8; - } -} - - -/* -================== -SCR_RSShot_f -================== -*/ -void SCR_RSShot_f (void) -{ -// int i, x, y; - int x, y; - unsigned char *src, *dest; - char pcxname[80]; -// char checkname[MAX_OSPATH]; - unsigned char *newbuf; - int w, h; - int dx, dy, dex, dey, nx; - int r, b, g; - int count; - float fracw, frach; - char st[80]; - time_t now; - - if (CL_IsUploading()) - return; // already one pending - - if (cls.state < ca_onserver) - return; // gotta be connected - - if (!scr_allowsnap.value) { - MSG_WriteByte (&cls.netchan.message, clc_stringcmd); - SZ_Print (&cls.netchan.message, "snap\n"); - Con_Printf("Refusing remote screen shot request.\n"); - return; - } - - Con_Printf("Remote screen shot requested.\n"); - -#if 0 -// -// find a file name to save it to -// - strcpy(pcxname,"mquake00.pcx"); - - for (i=0 ; i<=99 ; i++) - { - pcxname[6] = i/10 + '0'; - pcxname[7] = i%10 + '0'; - sprintf (checkname, "%s/%s", com_gamedir, pcxname); - if (Sys_FileTime(checkname) == -1) - break; // file doesn't exist - } - if (i==100) - { - Con_Printf ("SCR_ScreenShot_f: Couldn't create a PCX"); - return; - } -#endif - -// -// save the pcx file -// - D_EnableBackBufferAccess (); // enable direct drawing of console to back - // buffer - - w = (vid.width < RSSHOT_WIDTH) ? vid.width : RSSHOT_WIDTH; - h = (vid.height < RSSHOT_HEIGHT) ? vid.height : RSSHOT_HEIGHT; - - fracw = (float)vid.width / (float)w; - frach = (float)vid.height / (float)h; - - newbuf = Q_Malloc (w*h); - - for (y = 0; y < h; y++) { - dest = newbuf + (w * y); - - for (x = 0; x < w; x++) { - r = g = b = 0; - - dx = x * fracw; - dex = (x + 1) * fracw; - if (dex == dx) dex++; // at least one - dy = y * frach; - dey = (y + 1) * frach; - if (dey == dy) dey++; // at least one - - count = 0; - for (/* */; dy < dey; dy++) { - src = vid.buffer + (vid.rowbytes * dy) + dx; - for (nx = dx; nx < dex; nx++) { - r += host_basepal[*src * 3]; - g += host_basepal[*src * 3+1]; - b += host_basepal[*src * 3+2]; - src++; - count++; - } - } - r /= count; - g /= count; - b /= count; - *dest++ = MipColor(r, g, b); - } - } - - time(&now); - strcpy(st, ctime(&now)); - st[strlen(st) - 1] = 0; // remove the trailing \n - SCR_DrawStringToSnap (st, newbuf, w - strlen(st)*8, 0, w); - - Q_strncpyz (st, cls.servername, sizeof(st)); - SCR_DrawStringToSnap (st, newbuf, w - strlen(st)*8, 10, w); - - Q_strncpyz (st, name.string, sizeof(st)); - SCR_DrawStringToSnap (st, newbuf, w - strlen(st)*8, 20, w); - - WritePCXfile (pcxname, newbuf, w, h, w, host_basepal, true); - - free(newbuf); - - D_DisableBackBufferAccess (); // for adapters that can't stay mapped in - // for linear writes all the time - -// Con_Printf ("Wrote %s\n", pcxname); - Con_Printf ("Sending shot to server...\n"); -} - - -//============================================================================= - -char *scr_notifystring; -qboolean scr_drawdialog; - -void SCR_DrawNotifyString (void) -{ - char *start; - int l; - int j; - int x, y; - - start = scr_notifystring; - - y = vid.height*0.35; - - do - { - // scan the width of the line - for (l=0 ; l<40 ; l++) - if (start[l] == '\n' || !start[l]) - break; - x = (vid.width - l*8)/2; - for (j=0 ; j + +/* + +background clear +rendering +turtle/net/ram icons +sbar +centerprint / slow centerprint +notify lines +intermission / finale overlay +loading plaque +console +menu + +required background clears +required update regions + + +syncronous draw mode or async +One off screen buffer, with updates either copied or xblited +Need to double buffer? + + +async draw will require the refresh area to be cleared, because it will be +xblited, but sync draw can just ignore it. + +sync +draw + +CenterPrint () +SlowPrint () +Screen_Update (); +Con_Printf (); + +net +turn off messages option + +the refresh is always rendered, unless the console is full screen + + +console is: + notify lines + half + full + + +*/ + + +// only the refresh window will be updated unless these variables are flagged +int scr_copytop; +int scr_copyeverything; + +float scr_con_current; +float scr_conlines; // lines of console to display + +float oldscreensize, oldfov; +float oldsbar; +cvar_t scr_viewsize = {"viewsize","100",CVAR_ARCHIVE}; +cvar_t scr_fov = {"fov","90",CVAR_ARCHIVE}; // 10 - 170 +cvar_t scr_consize = {"scr_consize","0.5"}; +cvar_t scr_conspeed = {"scr_conspeed","1000"}; +cvar_t scr_centertime = {"scr_centertime","2"}; +cvar_t scr_showram = {"showram","1"}; +cvar_t scr_showturtle = {"showturtle","0"}; +cvar_t scr_showpause = {"showpause","1"}; +cvar_t scr_printspeed = {"scr_printspeed","8"}; +cvar_t scr_allowsnap = {"scr_allowsnap","1"}; + +// Tonik: +cvar_t scr_clock = {"cl_clock","0"}; +cvar_t scr_clock_x = {"cl_clock_x","0"}; +cvar_t scr_clock_y = {"cl_clock_y","-1"}; +cvar_t show_speed = {"show_speed","0"}; + +extern cvar_t show_fps; + +qboolean scr_initialized; // ready to draw + +qpic_t *scr_ram; +qpic_t *scr_net; +qpic_t *scr_turtle; + +int scr_fullupdate; + +int clearconsole; +int clearnotify; + +viddef_t vid; // global video state + +vrect_t *pconupdate; +vrect_t scr_vrect; + +qboolean scr_disabled_for_loading; + +qboolean scr_skipupdate; + +qboolean block_drawing; + +void SCR_ScreenShot_f (void); +void SCR_RSShot_f (void); + +/* +=============================================================================== + +CENTER PRINTING + +=============================================================================== +*/ + +char scr_centerstring[1024]; +float scr_centertime_start; // for slow victory printing +float scr_centertime_off; +int scr_center_lines; +int scr_erase_lines; +int scr_erase_center; + +/* +============== +SCR_CenterPrint + +Called for important messages that should stay in the center of the screen +for a few moments +============== +*/ +void SCR_CenterPrint (char *str) +{ + Q_strncpyz (scr_centerstring, str, sizeof(scr_centerstring)); + scr_centertime_off = scr_centertime.value; + scr_centertime_start = cl.time; + +// count the number of lines for centering + scr_center_lines = 1; + while (*str) + { + if (*str == '\n') + scr_center_lines++; + str++; + } +} + +void SCR_EraseCenterString (void) +{ + int y; + + if (scr_erase_center++ > vid.numpages) + { + scr_erase_lines = 0; + return; + } + + if (scr_center_lines <= 4) + y = vid.height*0.35; + else + y = 48; + + scr_copytop = 1; + Draw_TileClear (0, y, vid.width, min(8*scr_erase_lines, vid.height - y - 1)); +} + +void SCR_DrawCenterString (void) +{ + char *start; + int l; + int j; + int x, y; + int remaining; + +// the finale prints the characters one at a time + if (cl.intermission) + remaining = scr_printspeed.value * (cl.time - scr_centertime_start); + else + remaining = 9999; + + scr_erase_center = 0; + start = scr_centerstring; + + if (scr_center_lines <= 4) + y = vid.height*0.35; + else + y = 48; + + do + { + // scan the width of the line + for (l=0 ; l<40 ; l++) + if (start[l] == '\n' || !start[l]) + break; + x = (vid.width - l*8)/2; + for (j=0 ; j scr_erase_lines) + scr_erase_lines = scr_center_lines; + + scr_centertime_off -= host_frametime; + + if (scr_centertime_off <= 0 && !cl.intermission) + return; + if (key_dest != key_game) + return; + + SCR_DrawCenterString (); +} + +//============================================================================= + +/* +==================== +CalcFov +==================== +*/ +float CalcFov (float fov_x, float width, float height) +{ + float a; + float x; + + if (fov_x < 1 || fov_x > 179) + Sys_Error ("Bad fov: %f", fov_x); + + x = width/tan(fov_x/360*M_PI); + + a = atan (height/x); + + a = a*360/M_PI; + + return a; +} + +/* +================= +SCR_CalcRefdef + +Must be called whenever vid changes +Internal use only +================= +*/ +static void SCR_CalcRefdef (void) +{ + vrect_t vrect; + float size; + + scr_fullupdate = 0; // force a background redraw + vid.recalc_refdef = 0; + +// force the status bar to redraw + Sbar_Changed (); + +//======================================== + +// bound viewsize + if (scr_viewsize.value < 30) + Cvar_Set (&scr_viewsize,"30"); + if (scr_viewsize.value > 120) + Cvar_Set (&scr_viewsize,"120"); + +// bound field of view + if (scr_fov.value < 10) + Cvar_Set (&scr_fov,"10"); + if (scr_fov.value > 170) + Cvar_Set (&scr_fov,"170"); + + r_refdef.fov_x = scr_fov.value; + r_refdef.fov_y = CalcFov (r_refdef.fov_x, r_refdef.vrect.width, r_refdef.vrect.height); + +// intermission is always full screen + if (cl.intermission) + size = 120; + else + size = scr_viewsize.value; + + if (size >= 120) + sb_lines = 0; // no status bar at all + else if (size >= 110) + sb_lines = 24; // no inventory + else + sb_lines = 24+16+8; + +// these calculations mirror those in R_Init() for r_refdef, but take no +// account of water warping + vrect.x = 0; + vrect.y = 0; + vrect.width = vid.width; + vrect.height = vid.height; + + R_SetVrect (&vrect, &scr_vrect, sb_lines); + +// guard against going from one mode to another that's less than half the +// vertical resolution + if (scr_con_current > vid.height) + scr_con_current = vid.height; + +// notify the refresh of the change + R_ViewChanged (&vrect, sb_lines, vid.aspect); +} + + +/* +================= +SCR_SizeUp_f + +Keybinding command +================= +*/ +void SCR_SizeUp_f (void) +{ + if (scr_viewsize.value < 120) { + Cvar_SetValue (&scr_viewsize, scr_viewsize.value+10); + vid.recalc_refdef = 1; + } +} + + +/* +================= +SCR_SizeDown_f + +Keybinding command +================= +*/ +void SCR_SizeDown_f (void) +{ + Cvar_SetValue (&scr_viewsize, scr_viewsize.value-10); + vid.recalc_refdef = 1; +} + +//============================================================================ + +/* +================== +SCR_Init +================== +*/ +void SCR_Init (void) +{ + Cvar_RegisterVariable (&scr_fov); + Cvar_RegisterVariable (&scr_viewsize); + Cvar_RegisterVariable (&scr_consize); + Cvar_RegisterVariable (&scr_conspeed); + Cvar_RegisterVariable (&scr_showram); + Cvar_RegisterVariable (&scr_showturtle); + Cvar_RegisterVariable (&scr_showpause); + Cvar_RegisterVariable (&scr_centertime); + Cvar_RegisterVariable (&scr_printspeed); + Cvar_RegisterVariable (&scr_allowsnap); + +// Tonik: + Cvar_RegisterVariable (&scr_clock_x); + Cvar_RegisterVariable (&scr_clock_y); + Cvar_RegisterVariable (&scr_clock); + Cvar_RegisterVariable (&show_speed); + +// +// register our commands +// + Cmd_AddCommand ("screenshot",SCR_ScreenShot_f); + Cmd_AddCommand ("sizeup",SCR_SizeUp_f); + Cmd_AddCommand ("sizedown",SCR_SizeDown_f); + + scr_ram = Draw_PicFromWad ("ram"); + scr_net = Draw_PicFromWad ("net"); + scr_turtle = Draw_PicFromWad ("turtle"); + + scr_initialized = true; +} + + + +/* +============== +SCR_DrawRam +============== +*/ +void SCR_DrawRam (void) +{ + if (!scr_showram.value) + return; + + if (!r_cache_thrash) + return; + + Draw_Pic (scr_vrect.x+32, scr_vrect.y, scr_ram); +} + +/* +============== +SCR_DrawTurtle +============== +*/ +void SCR_DrawTurtle (void) +{ + static int count; + + if (!scr_showturtle.value) + return; + + if (host_frametime < 0.1) + { + count = 0; + return; + } + + count++; + if (count < 3) + return; + + Draw_Pic (scr_vrect.x, scr_vrect.y, scr_turtle); +} + +/* +============== +SCR_DrawNet +============== +*/ +void SCR_DrawNet (void) +{ + if (cls.netchan.outgoing_sequence - cls.netchan.incoming_acknowledged < UPDATE_BACKUP-1) + return; + if (cls.demoplayback) + return; + + Draw_Pic (scr_vrect.x+64, scr_vrect.y, scr_net); +} + +void SCR_DrawFPS (void) +{ + static double lastframetime; + double t; + extern int fps_count; + static lastfps; + int x, y; + char st[80]; + + if (!show_fps.value) + return; + + t = Sys_DoubleTime(); + if ((t - lastframetime) >= 1.0) { + lastfps = fps_count; + fps_count = 0; + lastframetime = t; + } + + sprintf(st, "%3d FPS", lastfps); + x = vid.width - strlen(st) * 8 - 8; + y = vid.height - sb_lines - 8; +// Draw_TileClear(x, y, strlen(st) * 8, 8); + Draw_String(x, y, st); +} + + +void SCR_DrawSpeed (void) +{ + int x, y; + char st[80]; + vec3_t vel; + float speed, vspeed; + static float maxspeed = 0; + static float display_speed = -1; + static double lastrealtime = 0; + + if (!show_speed.value) + return; + + if (lastrealtime > realtime) + { + lastrealtime = 0; + display_speed = -1; + maxspeed = 0; + } + +// VectorCopy (cl.simvel, vel); + VectorCopy (cl.frames[(cls.netchan.incoming_sequence)&UPDATE_MASK].playerstate[cl.playernum].velocity, vel); + vspeed = vel[2]; + vel[2] = 0; + speed = Length(vel); + + if (speed > maxspeed) + maxspeed = speed; + + if (display_speed >= 0) + { + sprintf(st, "%3d", (int)display_speed); + x = vid.width - strlen(st) * 8 - 8; + y = 8; + // Draw_TileClear(x, y, strlen(st) * 8, 8); + Draw_String(x, y, st); + } + + if (realtime - lastrealtime >= 0.1) + { + lastrealtime = realtime; + display_speed = maxspeed; + maxspeed = 0; + } +} + +void SCR_DrawClock (void) +{ + char str[80]; + int hours, minutes, seconds; + int tens_hours, tens_minutes, tens_seconds; + + if (!scr_clock.value || cls.demoplayback || cl.intermission) + return; + + if (scr_clock.value == 2) + { + time_t t; + struct tm *ptm; + str[0] = 0; + t = time(NULL); + if (t != -1) { + ptm = localtime (&t); + if (ptm) + strftime(str, sizeof(str)-1, "%H:%M:%S", ptm); + } + } + else + { + float time; + time = cl.time; + tens_hours = fmod (time / 36000, 10); + hours = fmod (time / 3600, 10); + tens_minutes = fmod (time / 600, 6); + minutes = fmod (time / 60, 10); + tens_seconds = fmod (time / 10, 6); + seconds = fmod (time, 10); + sprintf (str, "%i%i:%i%i:%i%i", tens_hours, hours, tens_minutes, minutes, + tens_seconds, seconds); + } + + if (scr_clock_y.value < 0) + Draw_String (8 * scr_clock_x.value, vid.height - sb_lines + 8*scr_clock_y.value, str); + else + Draw_String (8 * scr_clock_x.value, 8*scr_clock_y.value, str); +} + + +/* +============== +DrawPause +============== +*/ +void SCR_DrawPause (void) +{ + qpic_t *pic; + + if (!scr_showpause.value) // turn off for screenshots + return; + + if (!cl.paused) + return; + + pic = Draw_CachePic ("gfx/pause.lmp"); + Draw_Pic ( (vid.width - pic->width)/2, + (vid.height - 48 - pic->height)/2, pic); +} + + +//============================================================================= + + +/* +================== +SCR_SetUpToDrawConsole +================== +*/ +void SCR_SetUpToDrawConsole (void) +{ + Con_CheckResize (); + +// decide on the height of the console + if (cls.state != ca_active) + { + scr_conlines = vid.height; // full screen + scr_con_current = scr_conlines; + } + else if (key_dest == key_console) { + scr_conlines = vid.height * scr_consize.value; + if (scr_conlines < 30) + scr_conlines = 30; + if (scr_conlines > vid.height - 10) + scr_conlines = vid.height - 10; + } + else + scr_conlines = 0; // none visible + + if (scr_conlines < scr_con_current) + { + scr_con_current -= scr_conspeed.value*real_frametime*vid.height/320; + if (scr_conlines > scr_con_current) + scr_con_current = scr_conlines; + + } + else if (scr_conlines > scr_con_current) + { + scr_con_current += scr_conspeed.value*real_frametime*vid.height/320; + if (scr_conlines < scr_con_current) + scr_con_current = scr_conlines; + } + + if (clearconsole++ < vid.numpages) + { + scr_copytop = 1; + Draw_TileClear (0,(int)scr_con_current,vid.width, vid.height - (int)scr_con_current); + Sbar_Changed (); + } + else if (clearnotify++ < vid.numpages) + { + scr_copytop = 1; + Draw_TileClear (0,0,vid.width, con_notifylines); + } + else + con_notifylines = 0; +} + +/* +================== +SCR_DrawConsole +================== +*/ +void SCR_DrawConsole (void) +{ + if (scr_con_current) + { + scr_copyeverything = 1; + Con_DrawConsole (scr_con_current); + clearconsole = 0; + } + else + { + if (key_dest == key_game || key_dest == key_message) + Con_DrawNotify (); // only draw notify in game + } +} + + +/* +============================================================================== + + SCREEN SHOTS + +============================================================================== +*/ + + +/* +============== +WritePCXfile +============== +*/ +void WritePCXfile (char *filename, byte *data, int width, int height, + int rowbytes, byte *palette, qboolean upload) +{ + int i, j, length; + pcx_t *pcx; + byte *pack; + + pcx = Hunk_TempAlloc (width*height*2+1000); + if (pcx == NULL) + { + Con_Printf("SCR_ScreenShot_f: not enough memory\n"); + return; + } + + pcx->manufacturer = 0x0a; // PCX id + pcx->version = 5; // 256 color + pcx->encoding = 1; // uncompressed + pcx->bits_per_pixel = 8; // 256 color + pcx->xmin = 0; + pcx->ymin = 0; + pcx->xmax = LittleShort((short)(width-1)); + pcx->ymax = LittleShort((short)(height-1)); + pcx->hres = LittleShort((short)width); + pcx->vres = LittleShort((short)height); + memset (pcx->palette,0,sizeof(pcx->palette)); + pcx->color_planes = 1; // chunky image + pcx->bytes_per_line = LittleShort((short)width); + pcx->palette_type = LittleShort(2); // not a grey scale + memset (pcx->filler,0,sizeof(pcx->filler)); + +// pack the image + pack = &pcx->data; + + for (i=0 ; i>4; + col = num&15; + source = draw_chars + (row<<10) + (col<<3); + + drawline = 8; + + while (drawline--) + { + for (x=0 ; x<8 ; x++) + if (source[x]) + dest[x] = source[x]; + else + dest[x] = 98; + source += 128; + dest += width; + } + +} + +void SCR_DrawStringToSnap (const char *s, byte *buf, int x, int y, int width) +{ + byte *dest; + const unsigned char *p; + + dest = buf + ((y * width) + x); + + p = (const unsigned char *)s; + while (*p) { + SCR_DrawCharToSnap(*p++, dest, width); + dest += 8; + } +} + + +/* +================== +SCR_RSShot_f +================== +*/ +void SCR_RSShot_f (void) +{ +// int i, x, y; + int x, y; + unsigned char *src, *dest; + char pcxname[80]; +// char checkname[MAX_OSPATH]; + unsigned char *newbuf; + int w, h; + int dx, dy, dex, dey, nx; + int r, b, g; + int count; + float fracw, frach; + char st[80]; + time_t now; + + if (CL_IsUploading()) + return; // already one pending + + if (cls.state < ca_onserver) + return; // gotta be connected + + if (!scr_allowsnap.value) { + MSG_WriteByte (&cls.netchan.message, clc_stringcmd); + SZ_Print (&cls.netchan.message, "snap\n"); + Con_Printf("Refusing remote screen shot request.\n"); + return; + } + + Con_Printf("Remote screen shot requested.\n"); + +#if 0 +// +// find a file name to save it to +// + strcpy(pcxname,"mquake00.pcx"); + + for (i=0 ; i<=99 ; i++) + { + pcxname[6] = i/10 + '0'; + pcxname[7] = i%10 + '0'; + sprintf (checkname, "%s/%s", com_gamedir, pcxname); + if (Sys_FileTime(checkname) == -1) + break; // file doesn't exist + } + if (i==100) + { + Con_Printf ("SCR_ScreenShot_f: Couldn't create a PCX"); + return; + } +#endif + +// +// save the pcx file +// + D_EnableBackBufferAccess (); // enable direct drawing of console to back + // buffer + + w = (vid.width < RSSHOT_WIDTH) ? vid.width : RSSHOT_WIDTH; + h = (vid.height < RSSHOT_HEIGHT) ? vid.height : RSSHOT_HEIGHT; + + fracw = (float)vid.width / (float)w; + frach = (float)vid.height / (float)h; + + newbuf = Q_Malloc (w*h); + + for (y = 0; y < h; y++) { + dest = newbuf + (w * y); + + for (x = 0; x < w; x++) { + r = g = b = 0; + + dx = x * fracw; + dex = (x + 1) * fracw; + if (dex == dx) dex++; // at least one + dy = y * frach; + dey = (y + 1) * frach; + if (dey == dy) dey++; // at least one + + count = 0; + for (/* */; dy < dey; dy++) { + src = vid.buffer + (vid.rowbytes * dy) + dx; + for (nx = dx; nx < dex; nx++) { + r += host_basepal[*src * 3]; + g += host_basepal[*src * 3+1]; + b += host_basepal[*src * 3+2]; + src++; + count++; + } + } + r /= count; + g /= count; + b /= count; + *dest++ = MipColor(r, g, b); + } + } + + time(&now); + strcpy(st, ctime(&now)); + st[strlen(st) - 1] = 0; // remove the trailing \n + SCR_DrawStringToSnap (st, newbuf, w - strlen(st)*8, 0, w); + + Q_strncpyz (st, cls.servername, sizeof(st)); + SCR_DrawStringToSnap (st, newbuf, w - strlen(st)*8, 10, w); + + Q_strncpyz (st, name.string, sizeof(st)); + SCR_DrawStringToSnap (st, newbuf, w - strlen(st)*8, 20, w); + + WritePCXfile (pcxname, newbuf, w, h, w, host_basepal, true); + + free(newbuf); + + D_DisableBackBufferAccess (); // for adapters that can't stay mapped in + // for linear writes all the time + +// Con_Printf ("Wrote %s\n", pcxname); + Con_Printf ("Sending shot to server...\n"); +} + + +//============================================================================= + +char *scr_notifystring; +qboolean scr_drawdialog; + +void SCR_DrawNotifyString (void) +{ + char *start; + int l; + int j; + int x, y; + + start = scr_notifystring; + + y = vid.height*0.35; + + do + { + // scan the width of the line + for (l=0 ; l<40 ; l++) + if (start[l] == '\n' || !start[l]) + break; + x = (vid.width - l*8)/2; + for (j=0 ; j.bsp, for model_precache[0] - struct model_s *worldmodel; - char *model_precache[MAX_MODELS]; // NULL terminated - char *sound_precache[MAX_SOUNDS]; // NULL terminated - char *lightstyles[MAX_LIGHTSTYLES]; - struct model_s *models[MAX_MODELS]; - - int num_edicts; // increases towards MAX_EDICTS - edict_t *edicts; // can NOT be array indexed, because - // edict_t is variable sized, but can - // be used to reference the world ent - - byte *pvs, *phs; // fully expanded and decompressed - - // added to every client's unreliable buffer each frame, then cleared - sizebuf_t datagram; - byte datagram_buf[MAX_DATAGRAM]; - - // added to every client's reliable buffer each frame, then cleared - sizebuf_t reliable_datagram; - byte reliable_datagram_buf[MAX_MSGLEN]; - - // the multicast buffer is used to send a message to a set of clients - sizebuf_t multicast; - byte multicast_buf[MAX_MSGLEN]; - - // the master buffer is used for building log packets - sizebuf_t master; - byte master_buf[MAX_DATAGRAM]; - - // the signon buffer will be sent to each client as they connect - // includes the entity baselines, the static entities, etc - // large levels will have >MAX_DATAGRAM sized signons, so - // multiple signon messages are kept - sizebuf_t signon; - int num_signon_buffers; - int signon_buffer_size[MAX_SIGNON_BUFFERS]; - byte signon_buffers[MAX_SIGNON_BUFFERS][MAX_DATAGRAM]; - - qboolean demorecording; -} server_t; - -typedef struct -{ - vec3_t origin; - vec3_t angles; - int weaponframe; - int skinnum; - int model; - int effects; -} demoinfo_t; - -#define NUM_SPAWN_PARMS 16 - -typedef enum -{ - cs_free, // can be reused for a new connection - cs_zombie, // client has been disconnected, but don't reuse - // connection for a couple seconds - cs_connected, // has been assigned to a client_t, but not in game yet - cs_spawned // client is fully in game -} sv_client_state_t; // FIXME - -typedef struct -{ - // received from client - - // reply - double senttime; - float ping_time; - packet_entities_t entities; -} client_frame_t; - -#define MAX_BACK_BUFFERS 16 -#define MAX_STUFFTEXT 256 - -typedef struct client_s -{ - sv_client_state_t state; - - int spectator; // non-interactive - int multipov; // for multipov demo recording - int POVs; - int teamPOVs; - - qboolean sendinfo; // at end of frame, send info to all - // this prevents malicious multiple broadcasts - float lastnametime; // time of last name change - int lastnamecount; // time of last name change - unsigned checksum; // checksum for calcs - qboolean drop; // lose this guy next opportunity - int lossage; // loss percentage - - int userid; // identifying number - char userinfo[MAX_INFO_STRING]; // infostring - - usercmd_t lastcmd; // for filling in big drops and partial predictions - double localtime; // of last message - int oldbuttons; - - float maxspeed; // localized maxspeed - float entgravity; // localized ent gravity - - edict_t *edict; // EDICT_NUM(clientnum+1) - char name[32]; // for printing to other people - char team[32]; - // extracted from userinfo - int messagelevel; // for filtering printed messages - - // the datagram is written to after every frame, but only cleared - // when it is sent out to the client. overflow is tolerated. - sizebuf_t datagram; - byte datagram_buf[MAX_DATAGRAM]; - - // back buffers for client reliable data - sizebuf_t backbuf; - int num_backbuf; - int backbuf_size[MAX_BACK_BUFFERS]; - byte backbuf_data[MAX_BACK_BUFFERS][MAX_MSGLEN]; - - byte stufftext_buf[MAX_STUFFTEXT]; - - double connection_started; // or time of disconnect for zombies - qboolean send_message; // set on frames a datagram arived on - -// spawn parms are carried from level to level - float spawn_parms[NUM_SPAWN_PARMS]; - -// client known data for deltas - int old_frags; - - int stats[MAX_CL_STATS]; - - - client_frame_t frames[UPDATE_BACKUP]; // updates can be deltad from here - - FILE *download; // file being downloaded - int downloadsize; // total bytes - int downloadcount; // bytes sent - - int spec_track; // entnum of player tracking - - double whensaid[10]; // JACK: For floodprots - int whensaidhead; // Head value for floodprots - double lockedtill; - - qboolean upgradewarn; // did we warn him? - - FILE *upload; - char uploadfn[MAX_QPATH]; - netadr_t snap_from; - qboolean remote_snap; - - -//===== NETWORK ============ - int chokecount; - int delta_sequence; // -1 = no compression - netchan_t netchan; -} client_t; - -// a client can leave the server in one of four ways: -// dropping properly by quiting or disconnecting -// timing out if no valid messages are received for timeout.value seconds -// getting kicked off by the server operator -// a program error, like an overflowed reliable buffer - -typedef struct -{ - demoinfo_t info; - float sec; - int parsecount; - qboolean fixangle; - vec3_t angle; - float cmdtime; - int flags; - int frame; -} demo_client_t; - -typedef struct -{ - qboolean allowoverflow; // if false, do a Sys_Error - qboolean overflowed; // set to true if the buffer size failed - byte *data; - int maxsize; - int cursize; - int size, *msgsize; - int curto, curtype; -} demobuf_t; - -typedef struct -{ - demo_client_t clients[MAX_CLIENTS]; - double time; - demobuf_t buf; - -} demo_frame_t; - -#define DEMO_FRAMES 64 -#define DEMO_FRAMES_MASK (DEMO_FRAMES - 1) - -typedef struct -{ - FILE *file; - - union { - sizebuf_t *buf; - demobuf_t *dbuf; - }; - sizebuf_t datagram; - byte datagram_data[MAX_DATAGRAM]; - int lastto; - int lasttype; - double time, pingtime; - int stats[MAX_CLIENTS][MAX_CL_STATS]; // ouch! - client_t recorder; - qboolean fixangle[MAX_CLIENTS]; - float fixangletime[MAX_CLIENTS]; - vec3_t angles[MAX_CLIENTS]; - char name[MAX_QPATH]; - int parsecount; - int lastwritten; - demo_frame_t frames[DEMO_FRAMES]; - demoinfo_t info[MAX_CLIENTS]; - int size; - qboolean disk; - void *dest; - byte buffer[40*MAX_MSGLEN]; - int bufsize; -} demo_t; - -//============================================================================= - - -#define STATFRAMES 100 -typedef struct -{ - double active; - double idle; - double demo; - int count; - int packets; - - double latched_active; - double latched_idle; - double latched_demo; - int latched_packets; -} svstats_t; - -// MAX_CHALLENGES is made large to prevent a denial -// of service attack that could cycle all of them -// out before legitimate users connected -#define MAX_CHALLENGES 1024 - -typedef struct -{ - netadr_t adr; - int challenge; - int time; -} challenge_t; - -typedef struct -{ - int spawncount; // number of servers spawned since start, - // used to check late spawns - client_t clients[MAX_CLIENTS]; - int serverflags; // episode completion information - - double last_heartbeat; - int heartbeat_sequence; - svstats_t stats; - - char info[MAX_SERVERINFO_STRING]; - - // log messages are used so that fraglog processes can get stats - int logsequence; // the message currently being filled - double logtime; // time of last swap - sizebuf_t log[2]; - byte log_buf[2][MAX_DATAGRAM]; - - challenge_t challenges[MAX_CHALLENGES]; // to prevent invalid IPs from connecting - byte *demomem; - int demomemsize; -} server_static_t; - -//============================================================================= - -// edict->movetype values -#define MOVETYPE_NONE 0 // never moves -#define MOVETYPE_ANGLENOCLIP 1 -#define MOVETYPE_ANGLECLIP 2 -#define MOVETYPE_WALK 3 // gravity -#define MOVETYPE_STEP 4 // gravity, special edge handling -#define MOVETYPE_FLY 5 -#define MOVETYPE_TOSS 6 // gravity -#define MOVETYPE_PUSH 7 // no clip to world, push and crush -#define MOVETYPE_NOCLIP 8 -#define MOVETYPE_FLYMISSILE 9 // extra size to monsters -#define MOVETYPE_BOUNCE 10 - -// edict->solid values -#define SOLID_NOT 0 // no interaction with other objects -#define SOLID_TRIGGER 1 // touch on edge, but not blocking -#define SOLID_BBOX 2 // touch on edge, block -#define SOLID_SLIDEBOX 3 // touch on edge, but not an onground -#define SOLID_BSP 4 // bsp clip, touch on edge, block - -// edict->deadflag values -#define DEAD_NO 0 -#define DEAD_DYING 1 -#define DEAD_DEAD 2 - -#define DAMAGE_NO 0 -#define DAMAGE_YES 1 -#define DAMAGE_AIM 2 - -// edict->flags -#define FL_FLY 1 -#define FL_SWIM 2 -#define FL_GLIMPSE 4 -#define FL_CLIENT 8 -#define FL_INWATER 16 -#define FL_MONSTER 32 -#define FL_GODMODE 64 -#define FL_NOTARGET 128 -#define FL_ITEM 256 -#define FL_ONGROUND 512 -#define FL_PARTIALGROUND 1024 // not all corners are valid -#define FL_WATERJUMP 2048 // player jumping out of water - -// entity effects - -//define EF_BRIGHTFIELD 1 -//define EF_MUZZLEFLASH 2 -#define EF_BRIGHTLIGHT 4 -#define EF_DIMLIGHT 8 - - -#define SPAWNFLAG_NOT_EASY 256 -#define SPAWNFLAG_NOT_MEDIUM 512 -#define SPAWNFLAG_NOT_HARD 1024 -#define SPAWNFLAG_NOT_DEATHMATCH 2048 - -#define MULTICAST_ALL 0 -#define MULTICAST_PHS 1 -#define MULTICAST_PVS 2 - -#define MULTICAST_ALL_R 3 -#define MULTICAST_PHS_R 4 -#define MULTICAST_PVS_R 5 - -//============================================================================ - -extern cvar_t sv_mintic, sv_maxtic; -extern cvar_t sv_maxspeed; - -extern netadr_t master_adr[MAX_MASTERS]; // address of the master server - -extern int current_skill; - -extern cvar_t spawn; -extern cvar_t teamplay; -extern cvar_t serverdemo; -extern cvar_t deathmatch; -extern cvar_t fraglimit; -extern cvar_t timelimit; -extern cvar_t skill; -extern cvar_t coop; - -extern server_static_t svs; // persistant server info -extern server_t sv; // local server -extern demo_t demo; // server demo struct - -extern client_t *host_client; - -extern edict_t *sv_player; - -extern char localmodels[MAX_MODELS][5]; // inline model names for precache - -extern char localinfo[MAX_LOCALINFO_STRING+1]; - -extern int host_hunklevel; -extern FILE *sv_logfile; -extern FILE *sv_fraglogfile; - -//=========================================================== - -// -// sv_main.c -// -void SV_Shutdown (void); -void SV_ShutdownServer (void); -void SV_Frame (double time); -void SV_FinalMessage (char *message); -void SV_DropClient (client_t *drop); -void SV_InitLocal (void); - -int SV_CalcPing (client_t *cl); -void SV_FullClientUpdate (client_t *client, sizebuf_t *buf); -void SV_FullClientUpdateToClient (client_t *client, client_t *cl); - -qboolean SV_CheckBottom (edict_t *ent); -qboolean SV_movestep (edict_t *ent, vec3_t move, qboolean relink); - -void SV_WriteClientdataToMessage (client_t *client, sizebuf_t *msg); - -void SV_MoveToGoal (void); - -void SV_Physics_Client (edict_t *ent); - -void SV_ExecuteUserCommand (char *s); -void SV_InitOperatorCommands (void); - -void SV_SendServerinfo (client_t *client); -void SV_ExtractFromUserinfo (client_t *cl); -int SV_BoundRate (int rate); - -void Master_Heartbeat (void); -void Master_Packet (void); - - -// -// sv_init.c -// -int SV_ModelIndex (char *name); -void SV_FlushSignon (void); -void SV_SaveSpawnparms (void); -void SV_SpawnServer (char *server); - - -// -// sv_phys.c -// -void SV_ProgStartFrame (void); -void SV_Physics (void); -void SV_CheckVelocity (edict_t *ent); -void SV_AddGravity (edict_t *ent, float scale); -qboolean SV_RunThink (edict_t *ent); -void SV_Physics_Toss (edict_t *ent); -void SV_RunNewmis (void); -void SV_Impact (edict_t *e1, edict_t *e2); -void SV_SetMoveVars(void); - -// -// sv_send.c -// -typedef enum {RD_NONE, RD_CLIENT, RD_PACKET} redirect_t; -void SV_BeginRedirect (redirect_t rd); -void SV_EndRedirect (void); - -void SV_Multicast (vec3_t origin, int to); -void SV_StartSound (edict_t *entity, int channel, char *sample, int volume, - float attenuation); -void SV_ClientPrintf (client_t *cl, int level, char *fmt, ...); -void SV_ClientPrintf2 (client_t *cl, int level, char *fmt, ...); -void SV_BroadcastPrintf (int level, char *fmt, ...); -void SV_BroadcastCommand (char *fmt, ...); -void SV_SendClientMessages (void); -void SV_SendDemoMessage(void); -void SV_SendMessagesToAll (void); -void SV_FindModelNumbers (void); - - -// -// sv_user.c -// -void SV_ExecuteClientMessage (client_t *cl); -void SV_UserInit (void); -void SV_TogglePause (const char *msg); - - -// -// sv_ccmds.c -// -void SV_Status_f (void); -void SV_SendServerInfoChange (char *key, char *value); - -// -// sv_ents.c -// -void SV_WriteEntitiesToClient (client_t *client, sizebuf_t *msg, qboolean recorder); - -// -// sv_nchan.c -// -void ClientReliableCheckBlock(client_t *cl, int maxsize); -void ClientReliable_FinishWrite(client_t *cl); -void ClientReliableWrite_Begin(client_t *cl, int c, int maxsize); -void ClientReliableWrite_Angle(client_t *cl, float f); -void ClientReliableWrite_Angle16(client_t *cl, float f); -void ClientReliableWrite_Byte(client_t *cl, int c); -void ClientReliableWrite_Char(client_t *cl, int c); -void ClientReliableWrite_Float(client_t *cl, float f); -void ClientReliableWrite_Coord(client_t *cl, float f); -void ClientReliableWrite_Long(client_t *cl, int c); -void ClientReliableWrite_Short(client_t *cl, int c); -void ClientReliableWrite_String(client_t *cl, char *s); -void ClientReliableWrite_SZ(client_t *cl, void *data, int len); - -// -// sv_demo.c -// -void SV_DemoPings (void); -void SV_DemoWriteToDisk(int type, int to, float time); -void DemoReliableWrite_Begin(int type, int to, int size); -void SV_Stop (int reason); -void SV_Stop_f (void); -void SV_DemoWritePackets (int num); -void Demo_Init (void); +/* +Copyright (C) 1996-1997 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. + +*/ +// server.h + +#include "progs.h" + +//#define NEWWAY + +#define MAX_MASTERS 8 // max recipients for heartbeat packets + +#define MAX_SIGNON_BUFFERS 8 + +typedef enum { + ss_dead, // no map loaded + ss_loading, // spawning level edicts + ss_active // actively running +} server_state_t; +// some qc commands are only valid before the server has finished +// initializing (precache commands, static sounds / objects, etc) + +typedef struct +{ +// qboolean active; // false when server is going down + server_state_t state; // precache commands are only valid during load + + double time; + double gametime; + + int lastcheck; // used by PF_checkclient + double lastchecktime; // for monster ai + + qboolean paused; // are we paused? + + //check player/eyes models for hacks + unsigned model_player_checksum; + unsigned eyes_player_checksum; + + char name[64]; // map name + char modelname[MAX_QPATH]; // maps/.bsp, for model_precache[0] + struct model_s *worldmodel; + char *model_precache[MAX_MODELS]; // NULL terminated + char *sound_precache[MAX_SOUNDS]; // NULL terminated + char *lightstyles[MAX_LIGHTSTYLES]; + struct model_s *models[MAX_MODELS]; + + int num_edicts; // increases towards MAX_EDICTS + edict_t *edicts; // can NOT be array indexed, because + // edict_t is variable sized, but can + // be used to reference the world ent + + byte *pvs, *phs; // fully expanded and decompressed + + // added to every client's unreliable buffer each frame, then cleared + sizebuf_t datagram; + byte datagram_buf[MAX_DATAGRAM]; + + // added to every client's reliable buffer each frame, then cleared + sizebuf_t reliable_datagram; + byte reliable_datagram_buf[MAX_MSGLEN]; + + // the multicast buffer is used to send a message to a set of clients + sizebuf_t multicast; + byte multicast_buf[MAX_MSGLEN]; + + // the master buffer is used for building log packets + sizebuf_t master; + byte master_buf[MAX_DATAGRAM]; + + // the signon buffer will be sent to each client as they connect + // includes the entity baselines, the static entities, etc + // large levels will have >MAX_DATAGRAM sized signons, so + // multiple signon messages are kept + sizebuf_t signon; + int num_signon_buffers; + int signon_buffer_size[MAX_SIGNON_BUFFERS]; + byte signon_buffers[MAX_SIGNON_BUFFERS][MAX_DATAGRAM]; + + qboolean demorecording; +} server_t; + +typedef struct +{ + vec3_t origin; + vec3_t angles; + int weaponframe; + int skinnum; + int model; + int effects; +} demoinfo_t; + +#define NUM_SPAWN_PARMS 16 + +typedef enum +{ + cs_free, // can be reused for a new connection + cs_zombie, // client has been disconnected, but don't reuse + // connection for a couple seconds + cs_preconnected,// has been assigned, but login/realip not settled yet + cs_connected, // has been assigned to a client_t, but not in game yet + cs_spawned // client is fully in game +} sv_client_state_t; // FIXME + +typedef struct +{ + // received from client + + // reply + double senttime; + float ping_time; + packet_entities_t entities; +} client_frame_t; + +#define MAX_BACK_BUFFERS 4 +#define MAX_STUFFTEXT 256 + +typedef struct client_s +{ + sv_client_state_t state; + + int spectator; // non-interactive + int vip; + + qboolean sendinfo; // at end of frame, send info to all + // this prevents malicious multiple broadcasts + float lastnametime; // time of last name change + int lastnamecount; // time of last name change + unsigned checksum; // checksum for calcs + qboolean drop; // lose this guy next opportunity + int lossage; // loss percentage + + int userid; // identifying number + char userinfo[MAX_INFO_STRING]; // infostring + + usercmd_t lastcmd; // for filling in big drops and partial predictions + double localtime; // of last message + int oldbuttons; + + float maxspeed; // localized maxspeed + float entgravity; // localized ent gravity + + edict_t *edict; // EDICT_NUM(clientnum+1) + char name[32]; // for printing to other people + char team[32]; + // extracted from userinfo + int messagelevel; // for filtering printed messages + + // the datagram is written to after every frame, but only cleared + // when it is sent out to the client. overflow is tolerated. + sizebuf_t datagram; + byte datagram_buf[MAX_DATAGRAM]; + + // back buffers for client reliable data + sizebuf_t backbuf; + int num_backbuf; + int backbuf_size[MAX_BACK_BUFFERS]; + byte backbuf_data[MAX_BACK_BUFFERS][MAX_MSGLEN]; + + byte stufftext_buf[MAX_STUFFTEXT]; + + double connection_started; // or time of disconnect for zombies + qboolean send_message; // set on frames a datagram arived on + +// spawn parms are carried from level to level + float spawn_parms[NUM_SPAWN_PARMS]; + +// client known data for deltas + int old_frags; + + int stats[MAX_CL_STATS]; + + + client_frame_t frames[UPDATE_BACKUP]; // updates can be deltad from here + + FILE *download; // file being downloaded + int downloadsize; // total bytes + int downloadcount; // bytes sent + + int spec_track; // entnum of player tracking + + double whensaid[10]; // JACK: For floodprots + int whensaidhead; // Head value for floodprots + double lockedtill; + + qboolean upgradewarn; // did we warn him? + + FILE *upload; + char uploadfn[MAX_QPATH]; + netadr_t snap_from; + qboolean remote_snap; + + char login[16]; + int logged; + + +//===== NETWORK ============ + int chokecount; + int delta_sequence; // -1 = no compression + netchan_t netchan; + netadr_t realip; // client's ip, not latest proxy's + int realip_num; // random value + int realip_count; + qboolean rip_vip; +} client_t; + +// a client can leave the server in one of four ways: +// dropping properly by quiting or disconnecting +// timing out if no valid messages are received for timeout.value seconds +// getting kicked off by the server operator +// a program error, like an overflowed reliable buffer + +typedef struct +{ + demoinfo_t info; + float sec; + int parsecount; + qboolean fixangle; + vec3_t angle; + float cmdtime; + int flags; + int frame; +} demo_client_t; + +typedef struct { + byte type; + byte full; + int to; + int size; + byte data[1]; //gcc doesn't allow [] (?) +} header_t; + +typedef struct +{ + qboolean allowoverflow; // if false, do a Sys_Error + qboolean overflowed; // set to true if the buffer size failed + byte *data; + int maxsize; + int cursize; + int bufsize; + header_t *h; +} demobuf_t; + +typedef struct +{ + demo_client_t clients[MAX_CLIENTS]; + double time; + demobuf_t buf; + +} demo_frame_t; + +typedef struct { + byte *data; + int start, end, last; + int maxsize; +} dbuffer_t; + +#define DEMO_FRAMES 64 +#define DEMO_FRAMES_MASK (DEMO_FRAMES - 1) + +typedef struct +{ + FILE *file; + + demobuf_t *dbuf; + dbuffer_t dbuffer; + sizebuf_t datagram; + byte datagram_data[MAX_DATAGRAM]; + int lastto; + int lasttype; + double time, pingtime; + int stats[MAX_CLIENTS][MAX_CL_STATS]; // ouch! + client_t recorder; + qboolean fixangle[MAX_CLIENTS]; + float fixangletime[MAX_CLIENTS]; + vec3_t angles[MAX_CLIENTS]; + char name[MAX_OSPATH], namelong[MAX_OSPATH]; + int parsecount; + int lastwritten; + demo_frame_t frames[DEMO_FRAMES]; + demoinfo_t info[MAX_CLIENTS]; + int size; + qboolean disk; + void *dest; + byte *mfile; + byte buffer[20*MAX_MSGLEN]; + int bufsize; +} demo_t; + +//============================================================================= + + +#define STATFRAMES 100 +typedef struct +{ + double active; + double idle; + double demo; + int count; + int packets; + + double latched_active; + double latched_idle; + double latched_demo; + int latched_packets; +} svstats_t; + +// MAX_CHALLENGES is made large to prevent a denial +// of service attack that could cycle all of them +// out before legitimate users connected +#define MAX_CHALLENGES 1024 + +typedef struct +{ + netadr_t adr; + int challenge; + int time; +} challenge_t; + +typedef struct +{ + int spawncount; // number of servers spawned since start, + // used to check late spawns + client_t clients[MAX_CLIENTS]; + int serverflags; // episode completion information + + double last_heartbeat; + int heartbeat_sequence; + svstats_t stats; + + char info[MAX_SERVERINFO_STRING]; + + // log messages are used so that fraglog processes can get stats + int logsequence; // the message currently being filled + double logtime; // time of last swap + sizebuf_t log[2]; + byte log_buf[2][MAX_DATAGRAM]; + + challenge_t challenges[MAX_CHALLENGES]; // to prevent invalid IPs from connecting + byte *demomem; + int demomemsize; +} server_static_t; + +//============================================================================= + +// edict->movetype values +#define MOVETYPE_NONE 0 // never moves +#define MOVETYPE_ANGLENOCLIP 1 +#define MOVETYPE_ANGLECLIP 2 +#define MOVETYPE_WALK 3 // gravity +#define MOVETYPE_STEP 4 // gravity, special edge handling +#define MOVETYPE_FLY 5 +#define MOVETYPE_TOSS 6 // gravity +#define MOVETYPE_PUSH 7 // no clip to world, push and crush +#define MOVETYPE_NOCLIP 8 +#define MOVETYPE_FLYMISSILE 9 // extra size to monsters +#define MOVETYPE_BOUNCE 10 + +// edict->solid values +#define SOLID_NOT 0 // no interaction with other objects +#define SOLID_TRIGGER 1 // touch on edge, but not blocking +#define SOLID_BBOX 2 // touch on edge, block +#define SOLID_SLIDEBOX 3 // touch on edge, but not an onground +#define SOLID_BSP 4 // bsp clip, touch on edge, block + +// edict->deadflag values +#define DEAD_NO 0 +#define DEAD_DYING 1 +#define DEAD_DEAD 2 + +#define DAMAGE_NO 0 +#define DAMAGE_YES 1 +#define DAMAGE_AIM 2 + +// edict->flags +#define FL_FLY 1 +#define FL_SWIM 2 +#define FL_GLIMPSE 4 +#define FL_CLIENT 8 +#define FL_INWATER 16 +#define FL_MONSTER 32 +#define FL_GODMODE 64 +#define FL_NOTARGET 128 +#define FL_ITEM 256 +#define FL_ONGROUND 512 +#define FL_PARTIALGROUND 1024 // not all corners are valid +#define FL_WATERJUMP 2048 // player jumping out of water + +// entity effects + +//define EF_BRIGHTFIELD 1 +//define EF_MUZZLEFLASH 2 +#define EF_BRIGHTLIGHT 4 +#define EF_DIMLIGHT 8 + + +#define SPAWNFLAG_NOT_EASY 256 +#define SPAWNFLAG_NOT_MEDIUM 512 +#define SPAWNFLAG_NOT_HARD 1024 +#define SPAWNFLAG_NOT_DEATHMATCH 2048 + +#define MULTICAST_ALL 0 +#define MULTICAST_PHS 1 +#define MULTICAST_PVS 2 + +#define MULTICAST_ALL_R 3 +#define MULTICAST_PHS_R 4 +#define MULTICAST_PVS_R 5 + +//============================================================================ + +extern cvar_t sv_mintic, sv_maxtic, sv_ticrate; +extern cvar_t sv_maxspeed; + +extern netadr_t master_adr[MAX_MASTERS]; // address of the master server + +extern int current_skill; + +extern cvar_t spawn; +extern cvar_t teamplay; +extern cvar_t serverdemo; +extern cvar_t deathmatch; +extern cvar_t fraglimit; +extern cvar_t timelimit; +extern cvar_t skill; +extern cvar_t coop; + +extern server_static_t svs; // persistant server info +extern server_t sv; // local server +extern demo_t demo; // server demo struct +extern entity_state_t cl_entities[MAX_CLIENTS][UPDATE_BACKUP+1][MAX_PACKET_ENTITIES]; // client entities + +extern client_t *host_client; + +extern edict_t *sv_player; + +extern char localmodels[MAX_MODELS][5]; // inline model names for precache + +extern char localinfo[MAX_LOCALINFO_STRING+1]; + +extern int host_hunklevel; +extern FILE *sv_logfile; +extern FILE *sv_fraglogfile; +extern FILE *sv_errorlogfile; +extern FILE *sv_rconlogfile; + +extern qboolean sv_error; + +//=========================================================== + +// +// sv_main.c +// + +typedef struct +{ + unsigned mask; + unsigned compare; + int level; +} ipfilter_t; + +void SV_Shutdown (void); +void SV_ShutdownServer (void); +void SV_Frame (double time); +void SV_FinalMessage (char *message); +void SV_DropClient (client_t *drop); +void SV_InitLocal (void); + +int SV_CalcPing (client_t *cl); +void SV_FullClientUpdate (client_t *client, sizebuf_t *buf); +void SV_FullClientUpdateToClient (client_t *client, client_t *cl); + +qboolean SV_CheckBottom (edict_t *ent); +qboolean SV_movestep (edict_t *ent, vec3_t move, qboolean relink); + +void SV_WriteClientdataToMessage (client_t *client, sizebuf_t *msg); + +void SV_MoveToGoal (void); + +void SV_Physics_Client (edict_t *ent); + +void SV_ExecuteUserCommand (char *s); +void SV_InitOperatorCommands (void); + +void SV_SendServerinfo (client_t *client); +void SV_ExtractFromUserinfo (client_t *cl, qboolean namechanged); +int SV_BoundRate (int rate); + +void Master_Heartbeat (void); +void Master_Packet (void); + + +// +// sv_init.c +// +int SV_ModelIndex (char *name); +void SV_FlushSignon (void); +void SV_SaveSpawnparms (void); +void SV_SpawnServer (char *server); + + +// +// sv_phys.c +// +void SV_ProgStartFrame (void); +void SV_Physics (void); +void SV_CheckVelocity (edict_t *ent); +void SV_AddGravity (edict_t *ent, float scale); +qboolean SV_RunThink (edict_t *ent); +void SV_Physics_Toss (edict_t *ent); +void SV_RunNewmis (void); +void SV_Impact (edict_t *e1, edict_t *e2); +void SV_SetMoveVars(void); + +// +// sv_send.c +// +typedef enum {RD_NONE, RD_CLIENT, RD_PACKET, RD_MOD} redirect_t; +void SV_BeginRedirect (redirect_t rd); +void SV_EndRedirect (void); + +void SV_Multicast (vec3_t origin, int to); +void SV_StartSound (edict_t *entity, int channel, char *sample, int volume, + float attenuation); +void SV_ClientPrintf (client_t *cl, int level, char *fmt, ...); +void SV_ClientPrintf2 (client_t *cl, int level, char *fmt, ...); +void SV_BroadcastPrintf (int level, char *fmt, ...); +void SV_BroadcastCommand (char *fmt, ...); +void SV_SendClientMessages (void); +void SV_SendDemoMessage(void); +void SV_SendMessagesToAll (void); +void SV_FindModelNumbers (void); + + +// +// sv_user.c +// +void SV_ExecuteClientMessage (client_t *cl); +void SV_UserInit (void); +void SV_TogglePause (const char *msg); + + +// +// sv_ccmds.c +// +void SV_Status_f (void); +void SV_SendServerInfoChange (char *key, char *value); + +// +// sv_ents.c +// +void SV_WriteEntitiesToClient (client_t *client, sizebuf_t *msg, qboolean recorder); + +// +// sv_nchan.c +// +void ClientReliableCheckBlock(client_t *cl, int maxsize); +void ClientReliable_FinishWrite(client_t *cl); +void ClientReliableWrite_Begin(client_t *cl, int c, int maxsize); +void ClientReliableWrite_Angle(client_t *cl, float f); +void ClientReliableWrite_Angle16(client_t *cl, float f); +void ClientReliableWrite_Byte(client_t *cl, int c); +void ClientReliableWrite_Char(client_t *cl, int c); +void ClientReliableWrite_Float(client_t *cl, float f); +void ClientReliableWrite_Coord(client_t *cl, float f); +void ClientReliableWrite_Long(client_t *cl, int c); +void ClientReliableWrite_Short(client_t *cl, int c); +void ClientReliableWrite_String(client_t *cl, char *s); +void ClientReliableWrite_SZ(client_t *cl, void *data, int len); + +// +// sv_demo.c +// +void SV_DemoPings (void); +void SV_DemoWriteToDisk(int type, int to, float time); +void DemoWrite_Begin(byte type, int to, int size); +void SV_Stop (int reason); +void SV_Stop_f (void); +void SV_DemoWritePackets (int num); +void Demo_Init (void); +char *SV_DemoNum(int num); + + +// +// sv_login.c +// + +void SV_LoadAccounts(void); +void SV_CreateAccount_f(void); +void SV_RemoveAccount_f(void); +void SV_ListAccount_f (void); +int checklogin(char *log, char *pass, int num, int use); +void Login_Init (void); +qboolean SV_Login(client_t *cl); +void SV_Logout(client_t *cl); +void SV_ParseLogin(client_t *cl); +void SV_LoginCheckTimeOut(client_t *cl); + diff --git a/source/skin.c b/source/skin.c index aed6ee9b..d3796f77 100644 --- a/source/skin.c +++ b/source/skin.c @@ -1,336 +1,336 @@ -/* -Copyright (C) 1996-1997 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 "quakedef.h" -#include "teamplay.h" - -cvar_t baseskin = {"baseskin", "base"}; -cvar_t noskins = {"noskins", "0"}; - -char allskins[128]; -#define MAX_CACHED_SKINS 128 -skin_t skins[MAX_CACHED_SKINS]; -int numskins; - -/* -================ -Skin_Find - - Determines the best skin for the given scoreboard - slot, and sets scoreboard->skin - -================ -*/ -void Skin_Find (player_info_t *sc) -{ - skin_t *skin; - int i, tracknum; - char name[128], *s; - static char *team = ""; - - if (allskins[0]) - strcpy (name, allskins); - else - { - s = Info_ValueForKey (sc->userinfo, "skin"); - if (s && s[0]) - strcpy (name, s); - else - strcpy (name, baseskin.string); - } - - // ZQuake: check teamskin/enemyskin - // FIXME: does this work? - - if (cl.spectator) - { - tracknum = Cam_TrackNum(); - if (tracknum != -1) - //team = NULL; - //else - team = cl.players[tracknum].team; - } else - team = cl.players[cl.playernum].team; - - if ( !cl.teamfortress && !(cl.fpd & FPD_NO_FORCE_SKIN) ) - { - int teamplay; - - teamplay = atoi(Info_ValueForKey(cl.serverinfo, "teamplay")); - - if (cl_teamskin.string[0] && teamplay && team != NULL && - !strcmp(sc->team, team)) - { - Q_strncpyz (name, cl_teamskin.string, sizeof(name)); - } - - if (cl_enemyskin.string[0] && (!teamplay || team == NULL || - strcmp(sc->team, team))) - { - Q_strncpyz (name, cl_enemyskin.string, sizeof(name)); - } - } - - if (strstr (name, "..") || *name == '.') - strcpy (name, "base"); - - COM_StripExtension (name, name); - - for (i=0 ; iskin = &skins[i]; - Skin_Cache (sc->skin); - return; - } - } - - if (numskins == MAX_CACHED_SKINS) - { // ran out of spots, so flush everything - Skin_Skins_f (); - return; - } - - skin = &skins[numskins]; - sc->skin = skin; - numskins++; - - memset (skin, 0, sizeof(*skin)); - Q_strncpyz (skin->name, name, sizeof(skin->name)); -} - - -/* -========== -Skin_Cache - -Returns a pointer to the skin bitmap, or NULL to use the default -========== -*/ -byte *Skin_Cache (skin_t *skin) -{ - char name[1024]; - byte *raw; - byte *out, *pix; - pcx_t *pcx; - int x, y; - int dataByte; - int runLength; - - if (cls.downloadtype == dl_skin) - return NULL; // use base until downloaded - - if (noskins.value==1) // JACK: So NOSKINS > 1 will show skins, but - return NULL; // not download new ones. - - if (skin->failedload) - return NULL; - - out = Cache_Check (&skin->cache); - if (out) - return out; - -// -// load the pic from disk -// - sprintf (name, "skins/%s.pcx", skin->name); - raw = COM_LoadTempFile (name); - if (!raw) - { - Con_Printf ("Couldn't load skin %s\n", name); - sprintf (name, "skins/%s.pcx", baseskin.string); - raw = COM_LoadTempFile (name); - if (!raw) - { - skin->failedload = true; - return NULL; - } - } - -// -// parse the PCX file -// - pcx = (pcx_t *)raw; - raw = &pcx->data; - - if (pcx->manufacturer != 0x0a - || pcx->version != 5 - || pcx->encoding != 1 - || pcx->bits_per_pixel != 8 - || pcx->xmax >= 320 - || pcx->ymax >= 200) - { - skin->failedload = true; - Con_Printf ("Bad skin %s\n", name); - return NULL; - } - - out = Cache_Alloc (&skin->cache, 320*200, skin->name); - if (!out) - Sys_Error ("Skin_Cache: couldn't allocate"); - - pix = out; - memset (out, 0, 320*200); - - for (y=0 ; yymax ; y++, pix += 320) - { - for (x=0 ; x<=pcx->xmax ; ) - { - if (raw - (byte*)pcx > com_filesize) - { - Cache_Free (&skin->cache); - skin->failedload = true; - Con_Printf ("Skin %s was malformed. You should delete it.\n", name); - return NULL; - } - dataByte = *raw++; - - if((dataByte & 0xC0) == 0xC0) - { - runLength = dataByte & 0x3F; - if (raw - (byte*)pcx > com_filesize) - { - Cache_Free (&skin->cache); - skin->failedload = true; - Con_Printf ("Skin %s was malformed. You should delete it.\n", name); - return NULL; - } - dataByte = *raw++; - } - else - runLength = 1; - - // skin sanity check - if (runLength + x > pcx->xmax + 2) { - Cache_Free (&skin->cache); - skin->failedload = true; - Con_Printf ("Skin %s was malformed. You should delete it.\n", name); - return NULL; - } - while(runLength-- > 0) - pix[x++] = dataByte; - } - - } - - if ( raw - (byte *)pcx > com_filesize) - { - Cache_Free (&skin->cache); - skin->failedload = true; - Con_Printf ("Skin %s was malformed. You should delete it.\n", name); - return NULL; - } - - skin->failedload = false; - - return out; -} - - -/* -================= -Skin_NextDownload -================= -*/ -void Skin_NextDownload (void) -{ - player_info_t *sc; - int i; - - if (cls.downloadnumber == 0) - Con_Printf ("Checking skins...\n"); - cls.downloadtype = dl_skin; - - for ( - ; cls.downloadnumber != MAX_CLIENTS - ; cls.downloadnumber++) - { - sc = &cl.players[cls.downloadnumber]; - if (!sc->name[0]) - continue; - Skin_Find (sc); - if (noskins.value) - continue; - if (!CL_CheckOrDownloadFile(va("skins/%s.pcx", sc->skin->name))) - return; // started a download - } - - cls.downloadtype = dl_none; - - // now load them in for real - for (i=0 ; iname[0]) - continue; - Skin_Cache (sc->skin); -#ifdef GLQUAKE - sc->skin = NULL; -#endif - } - -// Tonik: only download when connecting - if (cls.state == ca_onserver) - { // get next signon phase - MSG_WriteByte (&cls.netchan.message, clc_stringcmd); - MSG_WriteString (&cls.netchan.message, - va("begin %i", cl.servercount)); - Cache_Report (); // print remaining memory - } -} - - -/* -========== -Skin_Skins_f - -Refind all skins, downloading if needed. -========== -*/ -void Skin_Skins_f (void) -{ - int i; - - for (i=0 ; iskin + +================ +*/ +void Skin_Find (player_info_t *sc) +{ + skin_t *skin; + int i, tracknum; + char name[128], *s; + static char *team = ""; + + if (allskins[0]) + strcpy (name, allskins); + else + { + s = Info_ValueForKey (sc->userinfo, "skin"); + if (s && s[0]) + strcpy (name, s); + else + strcpy (name, baseskin.string); + } + + // ZQuake: check teamskin/enemyskin + // FIXME: does this work? + + if (cl.spectator) + { + tracknum = Cam_TrackNum(); + if (tracknum != -1) + //team = NULL; + //else + team = cl.players[tracknum].team; + } else + team = cl.players[cl.playernum].team; + + if ( !cl.teamfortress && !(cl.fpd & FPD_NO_FORCE_SKIN) ) + { + int teamplay; + + teamplay = atoi(Info_ValueForKey(cl.serverinfo, "teamplay")); + + if (cl_teamskin.string[0] && teamplay && team != NULL && + !strcmp(sc->team, team)) + { + Q_strncpyz (name, cl_teamskin.string, sizeof(name)); + } + + if (cl_enemyskin.string[0] && (!teamplay || team == NULL || + strcmp(sc->team, team))) + { + Q_strncpyz (name, cl_enemyskin.string, sizeof(name)); + } + } + + if (strstr (name, "..") || *name == '.') + strcpy (name, "base"); + + COM_StripExtension (name, name); + + for (i=0 ; iskin = &skins[i]; + Skin_Cache (sc->skin); + return; + } + } + + if (numskins == MAX_CACHED_SKINS) + { // ran out of spots, so flush everything + Skin_Skins_f (); + return; + } + + skin = &skins[numskins]; + sc->skin = skin; + numskins++; + + memset (skin, 0, sizeof(*skin)); + Q_strncpyz (skin->name, name, sizeof(skin->name)); +} + + +/* +========== +Skin_Cache + +Returns a pointer to the skin bitmap, or NULL to use the default +========== +*/ +byte *Skin_Cache (skin_t *skin) +{ + char name[1024]; + byte *raw; + byte *out, *pix; + pcx_t *pcx; + int x, y; + int dataByte; + int runLength; + + if (cls.downloadtype == dl_skin) + return NULL; // use base until downloaded + + if (noskins.value==1) // JACK: So NOSKINS > 1 will show skins, but + return NULL; // not download new ones. + + if (skin->failedload) + return NULL; + + out = Cache_Check (&skin->cache); + if (out) + return out; + +// +// load the pic from disk +// + sprintf (name, "skins/%s.pcx", skin->name); + raw = COM_LoadTempFile (name); + if (!raw) + { + Con_Printf ("Couldn't load skin %s\n", name); + sprintf (name, "skins/%s.pcx", baseskin.string); + raw = COM_LoadTempFile (name); + if (!raw) + { + skin->failedload = true; + return NULL; + } + } + +// +// parse the PCX file +// + pcx = (pcx_t *)raw; + raw = &pcx->data; + + if (pcx->manufacturer != 0x0a + || pcx->version != 5 + || pcx->encoding != 1 + || pcx->bits_per_pixel != 8 + || pcx->xmax >= 320 + || pcx->ymax >= 200) + { + skin->failedload = true; + Con_Printf ("Bad skin %s\n", name); + return NULL; + } + + out = Cache_Alloc (&skin->cache, 320*200, skin->name); + if (!out) + Sys_Error ("Skin_Cache: couldn't allocate"); + + pix = out; + memset (out, 0, 320*200); + + for (y=0 ; yymax ; y++, pix += 320) + { + for (x=0 ; x<=pcx->xmax ; ) + { + if (raw - (byte*)pcx > com_filesize) + { + Cache_Free (&skin->cache); + skin->failedload = true; + Con_Printf ("Skin %s was malformed. You should delete it.\n", name); + return NULL; + } + dataByte = *raw++; + + if((dataByte & 0xC0) == 0xC0) + { + runLength = dataByte & 0x3F; + if (raw - (byte*)pcx > com_filesize) + { + Cache_Free (&skin->cache); + skin->failedload = true; + Con_Printf ("Skin %s was malformed. You should delete it.\n", name); + return NULL; + } + dataByte = *raw++; + } + else + runLength = 1; + + // skin sanity check + if (runLength + x > pcx->xmax + 2) { + Cache_Free (&skin->cache); + skin->failedload = true; + Con_Printf ("Skin %s was malformed. You should delete it.\n", name); + return NULL; + } + while(runLength-- > 0) + pix[x++] = dataByte; + } + + } + + if ( raw - (byte *)pcx > com_filesize) + { + Cache_Free (&skin->cache); + skin->failedload = true; + Con_Printf ("Skin %s was malformed. You should delete it.\n", name); + return NULL; + } + + skin->failedload = false; + + return out; +} + + +/* +================= +Skin_NextDownload +================= +*/ +void Skin_NextDownload (void) +{ + player_info_t *sc; + int i; + + if (cls.downloadnumber == 0) + Con_Printf ("Checking skins...\n"); + cls.downloadtype = dl_skin; + + for ( + ; cls.downloadnumber != MAX_CLIENTS + ; cls.downloadnumber++) + { + sc = &cl.players[cls.downloadnumber]; + if (!sc->name[0]) + continue; + Skin_Find (sc); + if (noskins.value) + continue; + if (!CL_CheckOrDownloadFile(va("skins/%s.pcx", sc->skin->name))) + return; // started a download + } + + cls.downloadtype = dl_none; + + // now load them in for real + for (i=0 ; iname[0]) + continue; + Skin_Cache (sc->skin); +#ifdef GLQUAKE + sc->skin = NULL; +#endif + } + +// Tonik: only download when connecting + if (cls.state == ca_onserver) + { // get next signon phase + MSG_WriteByte (&cls.netchan.message, clc_stringcmd); + MSG_WriteString (&cls.netchan.message, + va("begin %i", cl.servercount)); + Cache_Report (); // print remaining memory + } +} + + +/* +========== +Skin_Skins_f + +Refind all skins, downloading if needed. +========== +*/ +void Skin_Skins_f (void) +{ + int i; + + for (i=0 ; ichannels - 1); - Con_Printf("%5d samples\n", shm->samples); - Con_Printf("%5d samplepos\n", shm->samplepos); - Con_Printf("%5d samplebits\n", shm->samplebits); - Con_Printf("%5d submission_chunk\n", shm->submission_chunk); - Con_Printf("%5d speed\n", shm->speed); - Con_Printf("0x%x dma buffer\n", shm->buffer); - Con_Printf("%5d total_channels\n", total_channels); -} - - -/* -================ -S_Startup -================ -*/ - -void S_Startup (void) -{ - int rc; - - if (!snd_initialized) - return; - - if (!fakedma) - { - rc = SNDDMA_Init(); - - if (!rc) - { -#ifndef _WIN32 - Con_Printf("S_Startup: SNDDMA_Init failed.\n"); -#endif - sound_started = 0; - return; - } - } - - sound_started = 1; -} - - -/* -================ -S_Init -================ -*/ -void S_Init (void) -{ -// Con_Printf("\nSound Initialization\n"); - - Cvar_RegisterVariable(&nosound); - Cvar_RegisterVariable(&volume); - Cvar_RegisterVariable(&precache); - Cvar_RegisterVariable(&loadas8bit); - Cvar_RegisterVariable(&bgmvolume); - Cvar_RegisterVariable(&bgmbuffer); - Cvar_RegisterVariable(&ambient_level); - Cvar_RegisterVariable(&ambient_fade); - Cvar_RegisterVariable(&snd_noextraupdate); - Cvar_RegisterVariable(&snd_show); - Cvar_RegisterVariable(&_snd_mixahead); - - if (COM_CheckParm("-nosound")) - return; - - if (COM_CheckParm("-simsound")) - fakedma = true; - - Cmd_AddCommand("play", S_Play_f); - Cmd_AddCommand("playvol", S_PlayVol_f); - Cmd_AddCommand("stopsound", S_StopAllSounds_f); - Cmd_AddCommand("soundlist", S_SoundList_f); - Cmd_AddCommand("soundinfo", S_SoundInfo_f); - - if (host_parms.memsize < 0x800000) - { - Cvar_Set (&loadas8bit, "1"); - Con_Printf ("loading all sounds as 8bit\n"); - } - - - - snd_initialized = true; - - S_Startup (); - - SND_InitScaletable (); - - known_sfx = Hunk_AllocName (MAX_SFX*sizeof(sfx_t), "sfx_t"); - num_sfx = 0; - -// create a piece of DMA memory - - if (fakedma) - { - shm = (void *) Hunk_AllocName(sizeof(*shm), "shm"); - shm->splitbuffer = 0; - shm->samplebits = 16; - shm->speed = 22050; - shm->channels = 2; - shm->samples = 32768; - shm->samplepos = 0; - shm->soundalive = true; - shm->gamealive = true; - shm->submission_chunk = 1; - shm->buffer = Hunk_AllocName(1<<16, "shmbuf"); - } - -// Con_Printf ("Sound sampling rate: %i\n", shm->speed); - - // provides a tick sound until washed clean - -// if (shm->buffer) -// shm->buffer[4] = shm->buffer[5] = 0x7f; // force a pop for debugging - - ambient_sfx[AMBIENT_WATER] = S_PrecacheSound ("ambience/water1.wav"); - ambient_sfx[AMBIENT_SKY] = S_PrecacheSound ("ambience/wind2.wav"); - - S_StopAllSounds (true); -} - - -// ======================================================================= -// Shutdown sound engine -// ======================================================================= - -void S_Shutdown (void) -{ - - if (!sound_started) - return; - - if (shm) - shm->gamealive = 0; - - shm = 0; - sound_started = 0; - - if (!fakedma) - { - SNDDMA_Shutdown(); - } -} - - -// ======================================================================= -// Load a sound -// ======================================================================= - -/* -================== -S_FindName - -================== -*/ -sfx_t *S_FindName (char *name) -{ - int i; - sfx_t *sfx; - - if (!name) - Sys_Error ("S_FindName: NULL\n"); - - if (strlen(name) >= MAX_QPATH) - Sys_Error ("Sound name too long: %s", name); - -// see if already loaded - for (i=0 ; i < num_sfx ; i++) - if (!strcmp(known_sfx[i].name, name)) - { - return &known_sfx[i]; - } - - if (num_sfx == MAX_SFX) - Sys_Error ("S_FindName: out of sfx_t"); - - sfx = &known_sfx[i]; - strcpy (sfx->name, name); - - num_sfx++; - - return sfx; -} - - -/* -================== -S_TouchSound - -================== -*/ -void S_TouchSound (char *name) -{ - sfx_t *sfx; - - if (!sound_started) - return; - - sfx = S_FindName (name); - Cache_Check (&sfx->cache); -} - -/* -================== -S_PrecacheSound - -================== -*/ -sfx_t *S_PrecacheSound (char *name) -{ - sfx_t *sfx; - - if (!sound_started || nosound.value) - return NULL; - - sfx = S_FindName (name); - -// cache it in - if (precache.value) - S_LoadSound (sfx); - - return sfx; -} - - -//============================================================================= - -/* -================= -SND_PickChannel -================= -*/ -channel_t *SND_PickChannel (int entnum, int entchannel) -{ - int ch_idx; - int first_to_die; - int life_left; - -// Check for replacement sound, or find the best one to replace - first_to_die = -1; - life_left = 0x7fffffff; - for (ch_idx=NUM_AMBIENTS ; ch_idx < NUM_AMBIENTS + MAX_DYNAMIC_CHANNELS ; ch_idx++) - { - if (entchannel != 0 // channel 0 never overrides - && channels[ch_idx].entnum == entnum - && (channels[ch_idx].entchannel == entchannel || entchannel == -1) ) - { // always override sound from same entity - first_to_die = ch_idx; - break; - } - - // don't let monster sounds override player sounds - if (channels[ch_idx].entnum == cl.viewentity && entnum != cl.viewentity && channels[ch_idx].sfx) - continue; - - if (channels[ch_idx].end - paintedtime < life_left) - { - life_left = channels[ch_idx].end - paintedtime; - first_to_die = ch_idx; - } - } - - if (first_to_die == -1) - return NULL; - - if (channels[first_to_die].sfx) - channels[first_to_die].sfx = NULL; - - return &channels[first_to_die]; -} - -/* -================= -SND_Spatialize -================= -*/ -void SND_Spatialize (channel_t *ch) -{ - vec_t dot; - vec_t dist; - vec_t lscale, rscale, scale; - vec3_t source_vec; - sfx_t *snd; - -// anything coming from the view entity will always be full volume - if (ch->entnum == cl.viewentity) - { - ch->leftvol = ch->master_vol; - ch->rightvol = ch->master_vol; - return; - } - -// calculate stereo seperation and distance attenuation - - snd = ch->sfx; - VectorSubtract(ch->origin, listener_origin, source_vec); - - dist = VectorNormalize(source_vec) * ch->dist_mult; - - dot = DotProduct(listener_right, source_vec); - - if (shm->channels == 1) - { - rscale = 1.0; - lscale = 1.0; - } - else - { - rscale = 1.0 + dot; - lscale = 1.0 - dot; - } - -// add in distance effect - scale = (1.0 - dist) * rscale; - ch->rightvol = (int) (ch->master_vol * scale); - if (ch->rightvol < 0) - ch->rightvol = 0; - - scale = (1.0 - dist) * lscale; - ch->leftvol = (int) (ch->master_vol * scale); - if (ch->leftvol < 0) - ch->leftvol = 0; -} - - -// ======================================================================= -// Start a sound effect -// ======================================================================= - -void S_StartSound (int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float fvol, float attenuation) -{ - channel_t *target_chan, *check; - sfxcache_t *sc; - int vol; - int ch_idx; - int skip; - - if (!sound_started) - return; - - if (!sfx) - return; - - if (nosound.value) - return; - - vol = fvol*255; - -// pick a channel to play on - target_chan = SND_PickChannel(entnum, entchannel); - if (!target_chan) - return; - -// spatialize - memset (target_chan, 0, sizeof(*target_chan)); - VectorCopy(origin, target_chan->origin); - target_chan->dist_mult = attenuation / sound_nominal_clip_dist; - target_chan->master_vol = vol; - target_chan->entnum = entnum; - target_chan->entchannel = entchannel; - SND_Spatialize(target_chan); - - if (!target_chan->leftvol && !target_chan->rightvol) - return; // not audible at all - -// new channel - sc = S_LoadSound (sfx); - if (!sc) - { - target_chan->sfx = NULL; - Con_Printf("couldnt load\n"); - return; // couldn't load the sound's data - } - - target_chan->sfx = sfx; - target_chan->pos = 0.0; - target_chan->end = paintedtime + sc->length; - -// if an identical sound has also been started this frame, offset the pos -// a bit to keep it from just making the first one louder - check = &channels[NUM_AMBIENTS]; - for (ch_idx=NUM_AMBIENTS ; ch_idx < NUM_AMBIENTS + MAX_DYNAMIC_CHANNELS ; ch_idx++, check++) - { - if (check == target_chan) - continue; - if (check->sfx == sfx && !check->pos) - { - skip = rand () % (int)(0.1*shm->speed); - if (skip >= target_chan->end) - skip = target_chan->end - 1; - target_chan->pos += skip; - target_chan->end -= skip; - break; - } - - } -} - -void S_StopSound (int entnum, int entchannel) -{ - int i; - - for (i=0 ; ibuffer && !pDSBuf)) -#else - if (!sound_started || !shm || !shm->buffer) -#endif - return; - - if (shm->samplebits == 8) - clear = 0x80; - else - clear = 0; - -#ifdef _WIN32 - if (pDSBuf) - { - DWORD dwSize; - DWORD *pData; - int reps; - HRESULT hresult; - - reps = 0; - - while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, &pData, &dwSize, NULL, NULL, 0)) != DS_OK) - { - if (hresult != DSERR_BUFFERLOST) - { - Con_Printf ("S_ClearBuffer: DS::Lock Sound Buffer Failed\n"); - S_Shutdown (); - return; - } - - if (++reps > 10000) - { - Con_Printf ("S_ClearBuffer: DS: couldn't restore buffer\n"); - S_Shutdown (); - return; - } - } - - memset(pData, clear, shm->samples * shm->samplebits/8); - - pDSBuf->lpVtbl->Unlock(pDSBuf, pData, dwSize, NULL, 0); - - } - else -#endif - { - memset(shm->buffer, clear, shm->samples * shm->samplebits/8); - } -} - - -/* -================= -S_StaticSound -================= -*/ -void S_StaticSound (sfx_t *sfx, vec3_t origin, float vol, float attenuation) -{ - channel_t *ss; - sfxcache_t *sc; - - if (!sfx) - return; - - if (total_channels == MAX_CHANNELS) - { - Con_Printf ("total_channels == MAX_CHANNELS\n"); - return; - } - - ss = &channels[total_channels]; - total_channels++; - - sc = S_LoadSound (sfx); - if (!sc) - return; - - if (sc->loopstart == -1) - { - Con_Printf ("Sound %s not looped\n", sfx->name); - return; - } - - ss->sfx = sfx; - VectorCopy (origin, ss->origin); - ss->master_vol = vol; - ss->dist_mult = (attenuation/64) / sound_nominal_clip_dist; - ss->end = paintedtime + sc->length; - - SND_Spatialize (ss); -} - - -//============================================================================= - -/* -=================== -S_UpdateAmbientSounds -=================== -*/ -void S_UpdateAmbientSounds (void) -{ - mleaf_t *l; - float vol; - int ambient_channel; - channel_t *chan; - - if (!snd_ambient) - return; - -// calc ambient sound levels - if (!cl.worldmodel) - return; - - l = Mod_PointInLeaf (listener_origin, cl.worldmodel); - if (!l || !ambient_level.value) - { - for (ambient_channel = 0 ; ambient_channel< NUM_AMBIENTS ; ambient_channel++) - channels[ambient_channel].sfx = NULL; - return; - } - - for (ambient_channel = 0 ; ambient_channel< NUM_AMBIENTS ; ambient_channel++) - { - chan = &channels[ambient_channel]; - chan->sfx = ambient_sfx[ambient_channel]; - - vol = ambient_level.value * l->ambient_sound_level[ambient_channel]; - if (vol < 8) - vol = 0; - - // don't adjust volume too fast - if (chan->master_vol < vol) - { - chan->master_vol += host_frametime * ambient_fade.value; - if (chan->master_vol > vol) - chan->master_vol = vol; - } - else if (chan->master_vol > vol) - { - chan->master_vol -= host_frametime * ambient_fade.value; - if (chan->master_vol < vol) - chan->master_vol = vol; - } - - chan->leftvol = chan->rightvol = chan->master_vol; - } -} - - -/* -============ -S_Update - -Called once each time through the main loop -============ -*/ -void S_Update (vec3_t origin, vec3_t forward, vec3_t right, vec3_t up) -{ - int i, j; - int total; - channel_t *ch; - channel_t *combine; - - if (!sound_started || (snd_blocked > 0)) - return; - - VectorCopy(origin, listener_origin); - VectorCopy(forward, listener_forward); - VectorCopy(right, listener_right); - VectorCopy(up, listener_up); - -// update general area ambient sound sources - S_UpdateAmbientSounds (); - - combine = NULL; - -// update spatialization for static and dynamic sounds - ch = channels+NUM_AMBIENTS; - for (i=NUM_AMBIENTS ; isfx) - continue; - SND_Spatialize(ch); // respatialize channel - if (!ch->leftvol && !ch->rightvol) - continue; - - // try to combine static sounds with a previous channel of the same - // sound effect so we don't mix five torches every frame - - if (i >= MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS) - { - // see if it can just use the last one - if (combine && combine->sfx == ch->sfx) - { - combine->leftvol += ch->leftvol; - combine->rightvol += ch->rightvol; - ch->leftvol = ch->rightvol = 0; - continue; - } - // search for one - combine = channels+MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS; - for (j=MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS ; jsfx == ch->sfx) - break; - - if (j == total_channels) - { - combine = NULL; - } - else - { - if (combine != ch) - { - combine->leftvol += ch->leftvol; - combine->rightvol += ch->rightvol; - ch->leftvol = ch->rightvol = 0; - } - continue; - } - } - - - } - -// -// debugging output -// - if (snd_show.value) - { - total = 0; - ch = channels; - for (i=0 ; isfx && (ch->leftvol || ch->rightvol) ) - { - //Con_Printf ("%3i %3i %s\n", ch->leftvol, ch->rightvol, ch->sfx->name); - total++; - } - - Con_Printf ("----(%i)----\n", total); - } - -// mix some sound - S_Update_(); -} - -void GetSoundtime (void) -{ - int samplepos; - static int buffers; - static int oldsamplepos; - int fullsamples; - - fullsamples = shm->samples / shm->channels; - -// it is possible to miscount buffers if it has wrapped twice between -// calls to S_Update. Oh well. - samplepos = SNDDMA_GetDMAPos(); - - if (samplepos < oldsamplepos) - { - buffers++; // buffer wrapped - - if (paintedtime > 0x40000000) - { // time to chop things off to avoid 32 bit limits - buffers = 0; - paintedtime = fullsamples; - S_StopAllSounds (true); - } - } - oldsamplepos = samplepos; - - soundtime = buffers*fullsamples + samplepos/shm->channels; -} - - -void S_ExtraUpdate (void) -{ - extern void IN_Accumulate (void); - -#ifdef _WIN32 - IN_Accumulate (); -#endif - - if (snd_noextraupdate.value) - return; // don't pollute timings - S_Update_(); -} - - - -void S_Update_ (void) -{ - unsigned endtime; - int samps; - - if (!sound_started || (snd_blocked > 0)) - return; - -// Updates DMA time - GetSoundtime(); - -// check to make sure that we haven't overshot - if (paintedtime < soundtime) - { - //Con_Printf ("S_Update_ : overflow\n"); - paintedtime = soundtime; - } - -// mix ahead of current position - endtime = soundtime + _snd_mixahead.value * shm->speed; - samps = shm->samples >> (shm->channels-1); - if (endtime - soundtime > samps) - endtime = soundtime + samps; - -#ifdef _WIN32 -// if the buffer was lost or stopped, restore it and/or restart it - { - DWORD dwStatus; - - if (pDSBuf) - { - if (pDSBuf->lpVtbl->GetStatus (pDSBuf, &dwStatus) != DD_OK) - Con_Printf ("Couldn't get sound buffer status\n"); - - if (dwStatus & DSBSTATUS_BUFFERLOST) - pDSBuf->lpVtbl->Restore (pDSBuf); - - if (!(dwStatus & DSBSTATUS_PLAYING)) - pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING); - } - } -#endif - - S_PaintChannels (endtime); - - SNDDMA_Submit (); -} - -/* -=============================================================================== - -console functions - -=============================================================================== -*/ - -void S_Play_f (void) -{ - static int hash=345; - int i; - char name[256]; - sfx_t *sfx; - - for (i=1; i < Cmd_Argc(); i++) - { - strcpy(name, Cmd_Argv(i)); - COM_DefaultExtension (name, ".wav"); - sfx = S_PrecacheSound(name); - S_StartSound(hash++, 0, sfx, listener_origin, 1.0, 0.0); - } -} - -void S_PlayVol_f (void) -{ - static int hash=543; - int i; - float vol; - char name[256]; - sfx_t *sfx; - - for (i=1; i < Cmd_Argc(); i+=2) - { - strcpy(name, Cmd_Argv(i)); - COM_DefaultExtension (name, ".wav"); - sfx = S_PrecacheSound(name); - vol = Q_atof(Cmd_Argv(i+1)); - S_StartSound(hash++, 0, sfx, listener_origin, vol, 0.0); - } -} - -void S_SoundList_f (void) -{ - int i; - sfx_t *sfx; - sfxcache_t *sc; - int size, total; - - total = 0; - for (sfx=known_sfx, i=0 ; icache); - if (!sc) - continue; - size = sc->length*sc->width*(sc->stereo+1); - total += size; - if (sc->loopstart >= 0) - Con_Printf ("L"); - else - Con_Printf (" "); - Con_Printf("(%2db) %6i : %s\n",sc->width*8, size, sfx->name); - } - Con_Printf ("Total resident: %i\n", total); -} - - -void S_LocalSound (char *sound) -{ - sfx_t *sfx; - - if (nosound.value) - return; - if (!sound_started) - return; - - sfx = S_PrecacheSound (sound); - if (!sfx) - { - Con_Printf ("S_LocalSound: can't cache %s\n", sound); - return; - } - S_StartSound (cl.viewentity, -1, sfx, vec3_origin, 1, 0); -} - - -void S_ClearPrecache (void) -{ -} - - -void S_BeginPrecaching (void) -{ -} - - -void S_EndPrecaching (void) -{ -} - +/* +Copyright (C) 1996-1997 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. + +*/ +// snd_dma.c -- main control for any streaming sound output device + +#include "quakedef.h" +#include "sound.h" + +#ifdef _WIN32 +#include "winquake.h" +#endif + +void S_Play_f (void); +void S_PlayVol_f (void); +void S_SoundList_f (void); +void S_Update_ (); +void S_StopAllSounds (qboolean clear); +void S_StopAllSounds_f (void); + +// QuakeWorld hack... +#define viewentity playernum+1 + +// ======================================================================= +// Internal sound data & structures +// ======================================================================= + +channel_t channels[MAX_CHANNELS]; +int total_channels; + +int snd_blocked = 0; +static qboolean snd_ambient = 1; +qboolean snd_initialized = false; + +// pointer should go away +volatile dma_t *shm = 0; +volatile dma_t sn; + +vec3_t listener_origin; +vec3_t listener_forward; +vec3_t listener_right; +vec3_t listener_up; +vec_t sound_nominal_clip_dist=1000.0; + +int soundtime; // sample PAIRS +int paintedtime; // sample PAIRS + + +#define MAX_SFX 512 +sfx_t *known_sfx; // hunk allocated [MAX_SFX] +int num_sfx; + +sfx_t *ambient_sfx[NUM_AMBIENTS]; + +int desired_speed = 11025; +int desired_bits = 16; + +int sound_started=0; + +cvar_t bgmvolume = {"bgmvolume", "1", CVAR_ARCHIVE}; +cvar_t volume = {"volume", "0.7", CVAR_ARCHIVE}; + +cvar_t nosound = {"nosound", "0"}; +cvar_t precache = {"precache", "1"}; +cvar_t loadas8bit = {"loadas8bit", "0"}; +cvar_t bgmbuffer = {"bgmbuffer", "4096"}; +cvar_t ambient_level = {"ambient_level", "0.3"}; +cvar_t ambient_fade = {"ambient_fade", "100"}; +cvar_t snd_noextraupdate = {"snd_noextraupdate", "0"}; +cvar_t snd_show = {"snd_show", "0"}; +cvar_t _snd_mixahead = {"_snd_mixahead", "0.1", CVAR_ARCHIVE}; + + +// ==================================================================== +// User-setable variables +// ==================================================================== + + +// +// Fake dma is a synchronous faking of the DMA progress used for +// isolating performance in the renderer. The fakedma_updates is +// number of times S_Update() is called per second. +// + +qboolean fakedma = false; +int fakedma_updates = 15; + + +void S_AmbientOff (void) +{ + snd_ambient = false; +} + + +void S_AmbientOn (void) +{ + snd_ambient = true; +} + + +void S_SoundInfo_f (void) +{ + if (!sound_started || !shm) + { + Con_Printf ("sound system not started\n"); + return; + } + + Con_Printf("%5d stereo\n", shm->channels - 1); + Con_Printf("%5d samples\n", shm->samples); + Con_Printf("%5d samplepos\n", shm->samplepos); + Con_Printf("%5d samplebits\n", shm->samplebits); + Con_Printf("%5d submission_chunk\n", shm->submission_chunk); + Con_Printf("%5d speed\n", shm->speed); + Con_Printf("0x%x dma buffer\n", shm->buffer); + Con_Printf("%5d total_channels\n", total_channels); +} + + +/* +================ +S_Startup +================ +*/ + +void S_Startup (void) +{ + int rc; + + if (!snd_initialized) + return; + + if (!fakedma) + { + rc = SNDDMA_Init(); + + if (!rc) + { +#ifndef _WIN32 + Con_Printf("S_Startup: SNDDMA_Init failed.\n"); +#endif + sound_started = 0; + return; + } + } + + sound_started = 1; +} + + +/* +================ +S_Init +================ +*/ +void S_Init (void) +{ +// Con_Printf("\nSound Initialization\n"); + + Cvar_RegisterVariable(&nosound); + Cvar_RegisterVariable(&volume); + Cvar_RegisterVariable(&precache); + Cvar_RegisterVariable(&loadas8bit); + Cvar_RegisterVariable(&bgmvolume); + Cvar_RegisterVariable(&bgmbuffer); + Cvar_RegisterVariable(&ambient_level); + Cvar_RegisterVariable(&ambient_fade); + Cvar_RegisterVariable(&snd_noextraupdate); + Cvar_RegisterVariable(&snd_show); + Cvar_RegisterVariable(&_snd_mixahead); + + if (COM_CheckParm("-nosound")) + return; + + if (COM_CheckParm("-simsound")) + fakedma = true; + + Cmd_AddCommand("play", S_Play_f); + Cmd_AddCommand("playvol", S_PlayVol_f); + Cmd_AddCommand("stopsound", S_StopAllSounds_f); + Cmd_AddCommand("soundlist", S_SoundList_f); + Cmd_AddCommand("soundinfo", S_SoundInfo_f); + + if (host_parms.memsize < 0x800000) + { + Cvar_Set (&loadas8bit, "1"); + Con_Printf ("loading all sounds as 8bit\n"); + } + + + + snd_initialized = true; + + S_Startup (); + + SND_InitScaletable (); + + known_sfx = Hunk_AllocName (MAX_SFX*sizeof(sfx_t), "sfx_t"); + num_sfx = 0; + +// create a piece of DMA memory + + if (fakedma) + { + shm = (void *) Hunk_AllocName(sizeof(*shm), "shm"); + shm->splitbuffer = 0; + shm->samplebits = 16; + shm->speed = 22050; + shm->channels = 2; + shm->samples = 32768; + shm->samplepos = 0; + shm->soundalive = true; + shm->gamealive = true; + shm->submission_chunk = 1; + shm->buffer = Hunk_AllocName(1<<16, "shmbuf"); + } + +// Con_Printf ("Sound sampling rate: %i\n", shm->speed); + + // provides a tick sound until washed clean + +// if (shm->buffer) +// shm->buffer[4] = shm->buffer[5] = 0x7f; // force a pop for debugging + + ambient_sfx[AMBIENT_WATER] = S_PrecacheSound ("ambience/water1.wav"); + ambient_sfx[AMBIENT_SKY] = S_PrecacheSound ("ambience/wind2.wav"); + + S_StopAllSounds (true); +} + + +// ======================================================================= +// Shutdown sound engine +// ======================================================================= + +void S_Shutdown (void) +{ + + if (!sound_started) + return; + + if (shm) + shm->gamealive = 0; + + shm = 0; + sound_started = 0; + + if (!fakedma) + { + SNDDMA_Shutdown(); + } +} + + +// ======================================================================= +// Load a sound +// ======================================================================= + +/* +================== +S_FindName + +================== +*/ +sfx_t *S_FindName (char *name) +{ + int i; + sfx_t *sfx; + + if (!name) + Sys_Error ("S_FindName: NULL\n"); + + if (strlen(name) >= MAX_QPATH) + Sys_Error ("Sound name too long: %s", name); + +// see if already loaded + for (i=0 ; i < num_sfx ; i++) + if (!strcmp(known_sfx[i].name, name)) + { + return &known_sfx[i]; + } + + if (num_sfx == MAX_SFX) + Sys_Error ("S_FindName: out of sfx_t"); + + sfx = &known_sfx[i]; + strcpy (sfx->name, name); + + num_sfx++; + + return sfx; +} + + +/* +================== +S_TouchSound + +================== +*/ +void S_TouchSound (char *name) +{ + sfx_t *sfx; + + if (!sound_started) + return; + + sfx = S_FindName (name); + Cache_Check (&sfx->cache); +} + +/* +================== +S_PrecacheSound + +================== +*/ +sfx_t *S_PrecacheSound (char *name) +{ + sfx_t *sfx; + + if (!sound_started || nosound.value) + return NULL; + + sfx = S_FindName (name); + +// cache it in + if (precache.value) + S_LoadSound (sfx); + + return sfx; +} + + +//============================================================================= + +/* +================= +SND_PickChannel +================= +*/ +channel_t *SND_PickChannel (int entnum, int entchannel) +{ + int ch_idx; + int first_to_die; + int life_left; + +// Check for replacement sound, or find the best one to replace + first_to_die = -1; + life_left = 0x7fffffff; + for (ch_idx=NUM_AMBIENTS ; ch_idx < NUM_AMBIENTS + MAX_DYNAMIC_CHANNELS ; ch_idx++) + { + if (entchannel != 0 // channel 0 never overrides + && channels[ch_idx].entnum == entnum + && (channels[ch_idx].entchannel == entchannel || entchannel == -1) ) + { // always override sound from same entity + first_to_die = ch_idx; + break; + } + + // don't let monster sounds override player sounds + if (channels[ch_idx].entnum == cl.viewentity && entnum != cl.viewentity && channels[ch_idx].sfx) + continue; + + if (channels[ch_idx].end - paintedtime < life_left) + { + life_left = channels[ch_idx].end - paintedtime; + first_to_die = ch_idx; + } + } + + if (first_to_die == -1) + return NULL; + + if (channels[first_to_die].sfx) + channels[first_to_die].sfx = NULL; + + return &channels[first_to_die]; +} + +/* +================= +SND_Spatialize +================= +*/ +void SND_Spatialize (channel_t *ch) +{ + vec_t dot; + vec_t dist; + vec_t lscale, rscale, scale; + vec3_t source_vec; + sfx_t *snd; + +// anything coming from the view entity will always be full volume + if (ch->entnum == cl.viewentity) + { + ch->leftvol = ch->master_vol; + ch->rightvol = ch->master_vol; + return; + } + +// calculate stereo seperation and distance attenuation + + snd = ch->sfx; + VectorSubtract(ch->origin, listener_origin, source_vec); + + dist = VectorNormalize(source_vec) * ch->dist_mult; + + dot = DotProduct(listener_right, source_vec); + + if (shm->channels == 1) + { + rscale = 1.0; + lscale = 1.0; + } + else + { + rscale = 1.0 + dot; + lscale = 1.0 - dot; + } + +// add in distance effect + scale = (1.0 - dist) * rscale; + ch->rightvol = (int) (ch->master_vol * scale); + if (ch->rightvol < 0) + ch->rightvol = 0; + + scale = (1.0 - dist) * lscale; + ch->leftvol = (int) (ch->master_vol * scale); + if (ch->leftvol < 0) + ch->leftvol = 0; +} + + +// ======================================================================= +// Start a sound effect +// ======================================================================= + +void S_StartSound (int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float fvol, float attenuation) +{ + channel_t *target_chan, *check; + sfxcache_t *sc; + int vol; + int ch_idx; + int skip; + + if (!sound_started) + return; + + if (!sfx) + return; + + if (nosound.value) + return; + + vol = fvol*255; + +// pick a channel to play on + target_chan = SND_PickChannel(entnum, entchannel); + if (!target_chan) + return; + +// spatialize + memset (target_chan, 0, sizeof(*target_chan)); + VectorCopy(origin, target_chan->origin); + target_chan->dist_mult = attenuation / sound_nominal_clip_dist; + target_chan->master_vol = vol; + target_chan->entnum = entnum; + target_chan->entchannel = entchannel; + SND_Spatialize(target_chan); + + if (!target_chan->leftvol && !target_chan->rightvol) + return; // not audible at all + +// new channel + sc = S_LoadSound (sfx); + if (!sc) + { + target_chan->sfx = NULL; + Con_Printf("couldnt load\n"); + return; // couldn't load the sound's data + } + + target_chan->sfx = sfx; + target_chan->pos = 0.0; + target_chan->end = paintedtime + sc->length; + +// if an identical sound has also been started this frame, offset the pos +// a bit to keep it from just making the first one louder + check = &channels[NUM_AMBIENTS]; + for (ch_idx=NUM_AMBIENTS ; ch_idx < NUM_AMBIENTS + MAX_DYNAMIC_CHANNELS ; ch_idx++, check++) + { + if (check == target_chan) + continue; + if (check->sfx == sfx && !check->pos) + { + skip = rand () % (int)(0.1*shm->speed); + if (skip >= target_chan->end) + skip = target_chan->end - 1; + target_chan->pos += skip; + target_chan->end -= skip; + break; + } + + } +} + +void S_StopSound (int entnum, int entchannel) +{ + int i; + + for (i=0 ; ibuffer && !pDSBuf)) +#else + if (!sound_started || !shm || !shm->buffer) +#endif + return; + + if (shm->samplebits == 8) + clear = 0x80; + else + clear = 0; + +#ifdef _WIN32 + if (pDSBuf) + { + DWORD dwSize; + DWORD *pData; + int reps; + HRESULT hresult; + + reps = 0; + + while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, &pData, &dwSize, NULL, NULL, 0)) != DS_OK) + { + if (hresult != DSERR_BUFFERLOST) + { + Con_Printf ("S_ClearBuffer: DS::Lock Sound Buffer Failed\n"); + S_Shutdown (); + return; + } + + if (++reps > 10000) + { + Con_Printf ("S_ClearBuffer: DS: couldn't restore buffer\n"); + S_Shutdown (); + return; + } + } + + memset(pData, clear, shm->samples * shm->samplebits/8); + + pDSBuf->lpVtbl->Unlock(pDSBuf, pData, dwSize, NULL, 0); + + } + else +#endif + { + memset(shm->buffer, clear, shm->samples * shm->samplebits/8); + } +} + + +/* +================= +S_StaticSound +================= +*/ +void S_StaticSound (sfx_t *sfx, vec3_t origin, float vol, float attenuation) +{ + channel_t *ss; + sfxcache_t *sc; + + if (!sfx) + return; + + if (total_channels == MAX_CHANNELS) + { + Con_Printf ("total_channels == MAX_CHANNELS\n"); + return; + } + + ss = &channels[total_channels]; + total_channels++; + + sc = S_LoadSound (sfx); + if (!sc) + return; + + if (sc->loopstart == -1) + { + Con_Printf ("Sound %s not looped\n", sfx->name); + return; + } + + ss->sfx = sfx; + VectorCopy (origin, ss->origin); + ss->master_vol = vol; + ss->dist_mult = (attenuation/64) / sound_nominal_clip_dist; + ss->end = paintedtime + sc->length; + + SND_Spatialize (ss); +} + + +//============================================================================= + +/* +=================== +S_UpdateAmbientSounds +=================== +*/ +void S_UpdateAmbientSounds (void) +{ + mleaf_t *l; + float vol; + int ambient_channel; + channel_t *chan; + + if (!snd_ambient) + return; + +// calc ambient sound levels + if (!cl.worldmodel) + return; + + l = Mod_PointInLeaf (listener_origin, cl.worldmodel); + if (!l || !ambient_level.value) + { + for (ambient_channel = 0 ; ambient_channel< NUM_AMBIENTS ; ambient_channel++) + channels[ambient_channel].sfx = NULL; + return; + } + + for (ambient_channel = 0 ; ambient_channel< NUM_AMBIENTS ; ambient_channel++) + { + chan = &channels[ambient_channel]; + chan->sfx = ambient_sfx[ambient_channel]; + + vol = ambient_level.value * l->ambient_sound_level[ambient_channel]; + if (vol < 8) + vol = 0; + + // don't adjust volume too fast + if (chan->master_vol < vol) + { + chan->master_vol += host_frametime * ambient_fade.value; + if (chan->master_vol > vol) + chan->master_vol = vol; + } + else if (chan->master_vol > vol) + { + chan->master_vol -= host_frametime * ambient_fade.value; + if (chan->master_vol < vol) + chan->master_vol = vol; + } + + chan->leftvol = chan->rightvol = chan->master_vol; + } +} + + +/* +============ +S_Update + +Called once each time through the main loop +============ +*/ +void S_Update (vec3_t origin, vec3_t forward, vec3_t right, vec3_t up) +{ + int i, j; + int total; + channel_t *ch; + channel_t *combine; + + if (!sound_started || (snd_blocked > 0)) + return; + + VectorCopy(origin, listener_origin); + VectorCopy(forward, listener_forward); + VectorCopy(right, listener_right); + VectorCopy(up, listener_up); + +// update general area ambient sound sources + S_UpdateAmbientSounds (); + + combine = NULL; + +// update spatialization for static and dynamic sounds + ch = channels+NUM_AMBIENTS; + for (i=NUM_AMBIENTS ; isfx) + continue; + SND_Spatialize(ch); // respatialize channel + if (!ch->leftvol && !ch->rightvol) + continue; + + // try to combine static sounds with a previous channel of the same + // sound effect so we don't mix five torches every frame + + if (i >= MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS) + { + // see if it can just use the last one + if (combine && combine->sfx == ch->sfx) + { + combine->leftvol += ch->leftvol; + combine->rightvol += ch->rightvol; + ch->leftvol = ch->rightvol = 0; + continue; + } + // search for one + combine = channels+MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS; + for (j=MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS ; jsfx == ch->sfx) + break; + + if (j == total_channels) + { + combine = NULL; + } + else + { + if (combine != ch) + { + combine->leftvol += ch->leftvol; + combine->rightvol += ch->rightvol; + ch->leftvol = ch->rightvol = 0; + } + continue; + } + } + + + } + +// +// debugging output +// + if (snd_show.value) + { + total = 0; + ch = channels; + for (i=0 ; isfx && (ch->leftvol || ch->rightvol) ) + { + //Con_Printf ("%3i %3i %s\n", ch->leftvol, ch->rightvol, ch->sfx->name); + total++; + } + + Con_Printf ("----(%i)----\n", total); + } + +// mix some sound + S_Update_(); +} + +void GetSoundtime (void) +{ + int samplepos; + static int buffers; + static int oldsamplepos; + int fullsamples; + + fullsamples = shm->samples / shm->channels; + +// it is possible to miscount buffers if it has wrapped twice between +// calls to S_Update. Oh well. + samplepos = SNDDMA_GetDMAPos(); + + if (samplepos < oldsamplepos) + { + buffers++; // buffer wrapped + + if (paintedtime > 0x40000000) + { // time to chop things off to avoid 32 bit limits + buffers = 0; + paintedtime = fullsamples; + S_StopAllSounds (true); + } + } + oldsamplepos = samplepos; + + soundtime = buffers*fullsamples + samplepos/shm->channels; +} + + +void S_ExtraUpdate (void) +{ + extern void IN_Accumulate (void); + +#ifdef _WIN32 + IN_Accumulate (); +#endif + + if (snd_noextraupdate.value) + return; // don't pollute timings + S_Update_(); +} + + + +void S_Update_ (void) +{ + unsigned endtime; + int samps; + + if (!sound_started || (snd_blocked > 0)) + return; + +// Updates DMA time + GetSoundtime(); + +// check to make sure that we haven't overshot + if (paintedtime < soundtime) + { + //Con_Printf ("S_Update_ : overflow\n"); + paintedtime = soundtime; + } + +// mix ahead of current position + endtime = soundtime + _snd_mixahead.value * shm->speed; + samps = shm->samples >> (shm->channels-1); + if (endtime - soundtime > samps) + endtime = soundtime + samps; + +#ifdef _WIN32 +// if the buffer was lost or stopped, restore it and/or restart it + { + DWORD dwStatus; + + if (pDSBuf) + { + if (pDSBuf->lpVtbl->GetStatus (pDSBuf, &dwStatus) != DD_OK) + Con_Printf ("Couldn't get sound buffer status\n"); + + if (dwStatus & DSBSTATUS_BUFFERLOST) + pDSBuf->lpVtbl->Restore (pDSBuf); + + if (!(dwStatus & DSBSTATUS_PLAYING)) + pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING); + } + } +#endif + + S_PaintChannels (endtime); + + SNDDMA_Submit (); +} + +/* +=============================================================================== + +console functions + +=============================================================================== +*/ + +void S_Play_f (void) +{ + static int hash=345; + int i; + char name[256]; + sfx_t *sfx; + + for (i=1; i < Cmd_Argc(); i++) + { + strcpy(name, Cmd_Argv(i)); + COM_DefaultExtension (name, ".wav"); + sfx = S_PrecacheSound(name); + S_StartSound(hash++, 0, sfx, listener_origin, 1.0, 0.0); + } +} + +void S_PlayVol_f (void) +{ + static int hash=543; + int i; + float vol; + char name[256]; + sfx_t *sfx; + + for (i=1; i < Cmd_Argc(); i+=2) + { + strcpy(name, Cmd_Argv(i)); + COM_DefaultExtension (name, ".wav"); + sfx = S_PrecacheSound(name); + vol = Q_atof(Cmd_Argv(i+1)); + S_StartSound(hash++, 0, sfx, listener_origin, vol, 0.0); + } +} + +void S_SoundList_f (void) +{ + int i; + sfx_t *sfx; + sfxcache_t *sc; + int size, total; + + total = 0; + for (sfx=known_sfx, i=0 ; icache); + if (!sc) + continue; + size = sc->length*sc->width*(sc->stereo+1); + total += size; + if (sc->loopstart >= 0) + Con_Printf ("L"); + else + Con_Printf (" "); + Con_Printf("(%2db) %6i : %s\n",sc->width*8, size, sfx->name); + } + Con_Printf ("Total resident: %i\n", total); +} + + +void S_LocalSound (char *sound) +{ + sfx_t *sfx; + + if (nosound.value) + return; + if (!sound_started) + return; + + sfx = S_PrecacheSound (sound); + if (!sfx) + { + Con_Printf ("S_LocalSound: can't cache %s\n", sound); + return; + } + S_StartSound (cl.viewentity, -1, sfx, vec3_origin, 1, 0); +} + + +void S_ClearPrecache (void) +{ +} + + +void S_BeginPrecaching (void) +{ +} + + +void S_EndPrecaching (void) +{ +} + diff --git a/source/snd_linux.c b/source/snd_linux.c index d64fdd97..a69469ce 100644 --- a/source/snd_linux.c +++ b/source/snd_linux.c @@ -1,252 +1,252 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "quakedef.h" -#include "sound.h" - -int audio_fd; -int snd_inited; - -static int tryrates[] = { 11025, 22051, 44100, 8000 }; - -qboolean SNDDMA_Init(void) -{ - - int rc; - int fmt; - int tmp; - int i; - char *s; - struct audio_buf_info info; - int caps; - - snd_inited = 0; - -// open /dev/dsp, confirm capability to mmap, and get size of dma buffer - - audio_fd = open("/dev/dsp", O_RDWR); - if (audio_fd < 0) - { - perror("/dev/dsp"); - Con_Printf("Could not open /dev/dsp\n"); - return 0; - } - - rc = ioctl(audio_fd, SNDCTL_DSP_RESET, 0); - if (rc < 0) - { - perror("/dev/dsp"); - Con_Printf("Could not reset /dev/dsp\n"); - close(audio_fd); - return 0; - } - - if (ioctl(audio_fd, SNDCTL_DSP_GETCAPS, &caps)==-1) - { - perror("/dev/dsp"); - Con_Printf("Sound driver too old\n"); - close(audio_fd); - return 0; - } - - if (!(caps & DSP_CAP_TRIGGER) || !(caps & DSP_CAP_MMAP)) - { - Con_Printf("Sorry but your soundcard can't do this\n"); - close(audio_fd); - return 0; - } - - if (ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &info)==-1) - { - perror("GETOSPACE"); - Con_Printf("Um, can't do GETOSPACE?\n"); - close(audio_fd); - return 0; - } - - shm = &sn; - shm->splitbuffer = 0; - -// set sample bits & speed - - s = getenv("QUAKE_SOUND_SAMPLEBITS"); - if (s) shm->samplebits = atoi(s); - else if ((i = COM_CheckParm("-sndbits")) != 0) - shm->samplebits = atoi(com_argv[i+1]); - if (shm->samplebits != 16 && shm->samplebits != 8) - { - ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &fmt); - if (fmt & AFMT_S16_LE) shm->samplebits = 16; - else if (fmt & AFMT_U8) shm->samplebits = 8; - } - - s = getenv("QUAKE_SOUND_SPEED"); - if (s) shm->speed = atoi(s); - else if ((i = COM_CheckParm("-sndspeed")) != 0) - shm->speed = atoi(com_argv[i+1]); - else - { - for (i=0 ; ispeed = tryrates[i]; - } - - s = getenv("QUAKE_SOUND_CHANNELS"); - if (s) shm->channels = atoi(s); - else if ((i = COM_CheckParm("-sndmono")) != 0) - shm->channels = 1; - else if ((i = COM_CheckParm("-sndstereo")) != 0) - shm->channels = 2; - else shm->channels = 2; - - shm->samples = info.fragstotal * info.fragsize / (shm->samplebits/8); - shm->submission_chunk = 1; - -// memory map the dma buffer - - shm->buffer = (unsigned char *) mmap(NULL, info.fragstotal - * info.fragsize, PROT_WRITE, MAP_FILE|MAP_SHARED, audio_fd, 0); - if (!shm->buffer) - { - perror("/dev/dsp"); - Con_Printf("Could not mmap /dev/dsp\n"); - close(audio_fd); - return 0; - } - - tmp = 0; - if (shm->channels == 2) - tmp = 1; - rc = ioctl(audio_fd, SNDCTL_DSP_STEREO, &tmp); - if (rc < 0) - { - perror("/dev/dsp"); - Con_Printf("Could not set /dev/dsp to stereo=%d", shm->channels); - close(audio_fd); - return 0; - } - if (tmp) - shm->channels = 2; - else - shm->channels = 1; - - rc = ioctl(audio_fd, SNDCTL_DSP_SPEED, &shm->speed); - if (rc < 0) - { - perror("/dev/dsp"); - Con_Printf("Could not set /dev/dsp speed to %d", shm->speed); - close(audio_fd); - return 0; - } - - if (shm->samplebits == 16) - { - rc = AFMT_S16_LE; - rc = ioctl(audio_fd, SNDCTL_DSP_SETFMT, &rc); - if (rc < 0) - { - perror("/dev/dsp"); - Con_Printf("Could not support 16-bit data. Try 8-bit.\n"); - close(audio_fd); - return 0; - } - } - else if (shm->samplebits == 8) - { - rc = AFMT_U8; - rc = ioctl(audio_fd, SNDCTL_DSP_SETFMT, &rc); - if (rc < 0) - { - perror("/dev/dsp"); - Con_Printf("Could not support 8-bit data.\n"); - close(audio_fd); - return 0; - } - } - else - { - perror("/dev/dsp"); - Con_Printf("%d-bit sound not supported.", shm->samplebits); - close(audio_fd); - return 0; - } - -// toggle the trigger & start her up - - tmp = 0; - rc = ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &tmp); - if (rc < 0) - { - perror("/dev/dsp"); - Con_Printf("Could not toggle.\n"); - close(audio_fd); - return 0; - } - tmp = PCM_ENABLE_OUTPUT; - rc = ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &tmp); - if (rc < 0) - { - perror("/dev/dsp"); - Con_Printf("Could not toggle.\n"); - close(audio_fd); - return 0; - } - - shm->samplepos = 0; - - snd_inited = 1; - return 1; - -} - -int SNDDMA_GetDMAPos(void) -{ - - struct count_info count; - - if (!snd_inited) return 0; - - if (ioctl(audio_fd, SNDCTL_DSP_GETOPTR, &count)==-1) - { - perror("/dev/dsp"); - Con_Printf("Uh, sound dead.\n"); - close(audio_fd); - snd_inited = 0; - return 0; - } -// shm->samplepos = (count.bytes / (shm->samplebits / 8)) & (shm->samples-1); -// fprintf(stderr, "%d \r", count.ptr); - shm->samplepos = count.ptr / (shm->samplebits / 8); - - return shm->samplepos; - -} - -void SNDDMA_Shutdown(void) -{ - if (snd_inited) - { - close(audio_fd); - snd_inited = 0; - } -} - -/* -============== -SNDDMA_Submit - -Send sound to device if buffer isn't really the dma buffer -=============== -*/ -void SNDDMA_Submit(void) -{ -} - +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "quakedef.h" +#include "sound.h" + +int audio_fd; +int snd_inited; + +static int tryrates[] = { 11025, 22051, 44100, 8000 }; + +qboolean SNDDMA_Init(void) +{ + + int rc; + int fmt; + int tmp; + int i; + char *s; + struct audio_buf_info info; + int caps; + + snd_inited = 0; + +// open /dev/dsp, confirm capability to mmap, and get size of dma buffer + + audio_fd = open("/dev/dsp", O_RDWR); + if (audio_fd < 0) + { + perror("/dev/dsp"); + Con_Printf("Could not open /dev/dsp\n"); + return 0; + } + + rc = ioctl(audio_fd, SNDCTL_DSP_RESET, 0); + if (rc < 0) + { + perror("/dev/dsp"); + Con_Printf("Could not reset /dev/dsp\n"); + close(audio_fd); + return 0; + } + + if (ioctl(audio_fd, SNDCTL_DSP_GETCAPS, &caps)==-1) + { + perror("/dev/dsp"); + Con_Printf("Sound driver too old\n"); + close(audio_fd); + return 0; + } + + if (!(caps & DSP_CAP_TRIGGER) || !(caps & DSP_CAP_MMAP)) + { + Con_Printf("Sorry but your soundcard can't do this\n"); + close(audio_fd); + return 0; + } + + if (ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &info)==-1) + { + perror("GETOSPACE"); + Con_Printf("Um, can't do GETOSPACE?\n"); + close(audio_fd); + return 0; + } + + shm = &sn; + shm->splitbuffer = 0; + +// set sample bits & speed + + s = getenv("QUAKE_SOUND_SAMPLEBITS"); + if (s) shm->samplebits = atoi(s); + else if ((i = COM_CheckParm("-sndbits")) != 0) + shm->samplebits = atoi(com_argv[i+1]); + if (shm->samplebits != 16 && shm->samplebits != 8) + { + ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &fmt); + if (fmt & AFMT_S16_LE) shm->samplebits = 16; + else if (fmt & AFMT_U8) shm->samplebits = 8; + } + + s = getenv("QUAKE_SOUND_SPEED"); + if (s) shm->speed = atoi(s); + else if ((i = COM_CheckParm("-sndspeed")) != 0) + shm->speed = atoi(com_argv[i+1]); + else + { + for (i=0 ; ispeed = tryrates[i]; + } + + s = getenv("QUAKE_SOUND_CHANNELS"); + if (s) shm->channels = atoi(s); + else if ((i = COM_CheckParm("-sndmono")) != 0) + shm->channels = 1; + else if ((i = COM_CheckParm("-sndstereo")) != 0) + shm->channels = 2; + else shm->channels = 2; + + shm->samples = info.fragstotal * info.fragsize / (shm->samplebits/8); + shm->submission_chunk = 1; + +// memory map the dma buffer + + shm->buffer = (unsigned char *) mmap(NULL, info.fragstotal + * info.fragsize, PROT_WRITE, MAP_FILE|MAP_SHARED, audio_fd, 0); + if (!shm->buffer) + { + perror("/dev/dsp"); + Con_Printf("Could not mmap /dev/dsp\n"); + close(audio_fd); + return 0; + } + + tmp = 0; + if (shm->channels == 2) + tmp = 1; + rc = ioctl(audio_fd, SNDCTL_DSP_STEREO, &tmp); + if (rc < 0) + { + perror("/dev/dsp"); + Con_Printf("Could not set /dev/dsp to stereo=%d", shm->channels); + close(audio_fd); + return 0; + } + if (tmp) + shm->channels = 2; + else + shm->channels = 1; + + rc = ioctl(audio_fd, SNDCTL_DSP_SPEED, &shm->speed); + if (rc < 0) + { + perror("/dev/dsp"); + Con_Printf("Could not set /dev/dsp speed to %d", shm->speed); + close(audio_fd); + return 0; + } + + if (shm->samplebits == 16) + { + rc = AFMT_S16_LE; + rc = ioctl(audio_fd, SNDCTL_DSP_SETFMT, &rc); + if (rc < 0) + { + perror("/dev/dsp"); + Con_Printf("Could not support 16-bit data. Try 8-bit.\n"); + close(audio_fd); + return 0; + } + } + else if (shm->samplebits == 8) + { + rc = AFMT_U8; + rc = ioctl(audio_fd, SNDCTL_DSP_SETFMT, &rc); + if (rc < 0) + { + perror("/dev/dsp"); + Con_Printf("Could not support 8-bit data.\n"); + close(audio_fd); + return 0; + } + } + else + { + perror("/dev/dsp"); + Con_Printf("%d-bit sound not supported.", shm->samplebits); + close(audio_fd); + return 0; + } + +// toggle the trigger & start her up + + tmp = 0; + rc = ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &tmp); + if (rc < 0) + { + perror("/dev/dsp"); + Con_Printf("Could not toggle.\n"); + close(audio_fd); + return 0; + } + tmp = PCM_ENABLE_OUTPUT; + rc = ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &tmp); + if (rc < 0) + { + perror("/dev/dsp"); + Con_Printf("Could not toggle.\n"); + close(audio_fd); + return 0; + } + + shm->samplepos = 0; + + snd_inited = 1; + return 1; + +} + +int SNDDMA_GetDMAPos(void) +{ + + struct count_info count; + + if (!snd_inited) return 0; + + if (ioctl(audio_fd, SNDCTL_DSP_GETOPTR, &count)==-1) + { + perror("/dev/dsp"); + Con_Printf("Uh, sound dead.\n"); + close(audio_fd); + snd_inited = 0; + return 0; + } +// shm->samplepos = (count.bytes / (shm->samplebits / 8)) & (shm->samples-1); +// fprintf(stderr, "%d \r", count.ptr); + shm->samplepos = count.ptr / (shm->samplebits / 8); + + return shm->samplepos; + +} + +void SNDDMA_Shutdown(void) +{ + if (snd_inited) + { + close(audio_fd); + snd_inited = 0; + } +} + +/* +============== +SNDDMA_Submit + +Send sound to device if buffer isn't really the dma buffer +=============== +*/ +void SNDDMA_Submit(void) +{ +} + diff --git a/source/snd_mem.c b/source/snd_mem.c index 32881462..a582da62 100644 --- a/source/snd_mem.c +++ b/source/snd_mem.c @@ -1,344 +1,344 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// snd_mem.c: sound caching - -#include "quakedef.h" -#include "sound.h" - -int cache_full_cycle; - -byte *S_Alloc (int size); - -/* -================ -ResampleSfx -================ -*/ -void ResampleSfx (sfx_t *sfx, int inrate, int inwidth, byte *data) -{ - int outcount; - int srcsample; - float stepscale; - int i; - int sample, samplefrac, fracstep; - sfxcache_t *sc; - - sc = Cache_Check (&sfx->cache); - if (!sc) - return; - - stepscale = (float)inrate / shm->speed; // this is usually 0.5, 1, or 2 - - outcount = sc->length / stepscale; - sc->length = outcount; - if (sc->loopstart != -1) - sc->loopstart = sc->loopstart / stepscale; - - sc->speed = shm->speed; - if (loadas8bit.value) - sc->width = 1; - else - sc->width = inwidth; - sc->stereo = 0; - -// resample / decimate to the current source rate - - if (stepscale == 1 && inwidth == 1 && sc->width == 1) - { -// fast special case - for (i=0 ; idata)[i] - = (int)( (unsigned char)(data[i]) - 128); - } - else - { -// general case - samplefrac = 0; - fracstep = stepscale*256; - for (i=0 ; i> 8; - samplefrac += fracstep; - if (inwidth == 2) - sample = LittleShort ( ((short *)data)[srcsample] ); - else - sample = (int)( (unsigned char)(data[srcsample]) - 128) << 8; - if (sc->width == 2) - ((short *)sc->data)[i] = sample; - else - ((signed char *)sc->data)[i] = sample >> 8; - } - } -} - -//============================================================================= - -/* -============== -S_LoadSound -============== -*/ -sfxcache_t *S_LoadSound (sfx_t *s) -{ - char namebuffer[256]; - byte *data; - wavinfo_t info; - int len; - float stepscale; - sfxcache_t *sc; - byte stackbuf[1*1024]; // avoid dirtying the cache heap - -// see if still in memory - sc = Cache_Check (&s->cache); - if (sc) - return sc; - -//Con_Printf ("S_LoadSound: %x\n", (int)stackbuf); -// load it in - strcpy(namebuffer, "sound/"); - strcat(namebuffer, s->name); - -// Con_Printf ("loading %s\n",namebuffer); - - data = COM_LoadStackFile(namebuffer, stackbuf, sizeof(stackbuf)); - - if (!data) - { - Con_Printf ("Couldn't load %s\n", namebuffer); - return NULL; - } - - info = GetWavinfo (s->name, data, com_filesize); - if (info.channels != 1) - { - Con_Printf ("%s is a stereo sample\n",s->name); - return NULL; - } - - stepscale = (float)info.rate / shm->speed; - len = info.samples / stepscale; - - len = len * info.width * info.channels; - - sc = Cache_Alloc ( &s->cache, len + sizeof(sfxcache_t), s->name); - if (!sc) - return NULL; - - sc->length = info.samples; - sc->loopstart = info.loopstart; - sc->speed = info.rate; - sc->width = info.width; - sc->stereo = info.channels; - - ResampleSfx (s, sc->speed, sc->width, data + info.dataofs); - - return sc; -} - - - -/* -=============================================================================== - -WAV loading - -=============================================================================== -*/ - - -byte *data_p; -byte *iff_end; -byte *last_chunk; -byte *iff_data; -int iff_chunk_len; - - -short GetLittleShort(void) -{ - short val = 0; - val = *data_p; - val = val + (*(data_p+1)<<8); - data_p += 2; - return val; -} - -int GetLittleLong(void) -{ - int val = 0; - val = *data_p; - val = val + (*(data_p+1)<<8); - val = val + (*(data_p+2)<<16); - val = val + (*(data_p+3)<<24); - data_p += 4; - return val; -} - -void FindNextChunk(char *name) -{ - while (1) - { - data_p=last_chunk; - - if (data_p >= iff_end) - { // didn't find the chunk - data_p = NULL; - return; - } - - data_p += 4; - iff_chunk_len = GetLittleLong(); - if (iff_chunk_len < 0) - { - data_p = NULL; - return; - } -// if (iff_chunk_len > 1024*1024) -// Sys_Error ("FindNextChunk: %i length is past the 1 meg sanity limit", iff_chunk_len); - data_p -= 8; - last_chunk = data_p + 8 + ( (iff_chunk_len + 1) & ~1 ); - if (!strncmp(data_p, name, 4)) - return; - } -} - -void FindChunk(char *name) -{ - last_chunk = iff_data; - FindNextChunk (name); -} - - -#if 0 -void DumpChunks(void) -{ - char str[5]; - - str[4] = 0; - data_p=iff_data; - do - { - memcpy (str, data_p, 4); - data_p += 4; - iff_chunk_len = GetLittleLong(); - Con_Printf ("0x%x : %s (%d)\n", (int)(data_p - 4), str, iff_chunk_len); - data_p += (iff_chunk_len + 1) & ~1; - } while (data_p < iff_end); -} -#endif - -/* -============ -GetWavinfo -============ -*/ -wavinfo_t GetWavinfo (char *name, byte *wav, int wavlength) -{ - wavinfo_t info; - int i; - int format; - int samples; - - memset (&info, 0, sizeof(info)); - - if (!wav) - return info; - - iff_data = wav; - iff_end = wav + wavlength; - -// find "RIFF" chunk - FindChunk("RIFF"); - if (!(data_p && !strncmp(data_p+8, "WAVE", 4))) - { - Con_Printf("Missing RIFF/WAVE chunks\n"); - return info; - } - -// get "fmt " chunk - iff_data = data_p + 12; -// DumpChunks (); - - FindChunk("fmt "); - if (!data_p) - { - Con_Printf("Missing fmt chunk\n"); - return info; - } - data_p += 8; - format = GetLittleShort(); - if (format != 1) - { - Con_Printf("Microsoft PCM format only\n"); - return info; - } - - info.channels = GetLittleShort(); - info.rate = GetLittleLong(); - data_p += 4+2; - info.width = GetLittleShort() / 8; - -// get cue chunk - FindChunk("cue "); - if (data_p) - { - data_p += 32; - info.loopstart = GetLittleLong(); -// Con_Printf("loopstart=%d\n", sfx->loopstart); - - // if the next chunk is a LIST chunk, look for a cue length marker - FindNextChunk ("LIST"); - if (data_p) - { - if (!strncmp (data_p + 28, "mark", 4)) - { // this is not a proper parse, but it works with cooledit... - data_p += 24; - i = GetLittleLong (); // samples in loop - info.samples = info.loopstart + i; -// Con_Printf("looped length: %i\n", i); - } - } - } - else - info.loopstart = -1; - -// find data chunk - FindChunk("data"); - if (!data_p) - { - Con_Printf("Missing data chunk\n"); - return info; - } - - data_p += 4; - samples = GetLittleLong () / info.width; - - if (info.samples) - { - if (samples < info.samples) - Sys_Error ("Sound %s has a bad loop length", name); - } - else - info.samples = samples; - - info.dataofs = data_p - wav; - - return info; -} - +/* +Copyright (C) 1996-1997 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. + +*/ +// snd_mem.c: sound caching + +#include "quakedef.h" +#include "sound.h" + +int cache_full_cycle; + +byte *S_Alloc (int size); + +/* +================ +ResampleSfx +================ +*/ +void ResampleSfx (sfx_t *sfx, int inrate, int inwidth, byte *data) +{ + int outcount; + int srcsample; + float stepscale; + int i; + int sample, samplefrac, fracstep; + sfxcache_t *sc; + + sc = Cache_Check (&sfx->cache); + if (!sc) + return; + + stepscale = (float)inrate / shm->speed; // this is usually 0.5, 1, or 2 + + outcount = sc->length / stepscale; + sc->length = outcount; + if (sc->loopstart != -1) + sc->loopstart = sc->loopstart / stepscale; + + sc->speed = shm->speed; + if (loadas8bit.value) + sc->width = 1; + else + sc->width = inwidth; + sc->stereo = 0; + +// resample / decimate to the current source rate + + if (stepscale == 1 && inwidth == 1 && sc->width == 1) + { +// fast special case + for (i=0 ; idata)[i] + = (int)( (unsigned char)(data[i]) - 128); + } + else + { +// general case + samplefrac = 0; + fracstep = stepscale*256; + for (i=0 ; i> 8; + samplefrac += fracstep; + if (inwidth == 2) + sample = LittleShort ( ((short *)data)[srcsample] ); + else + sample = (int)( (unsigned char)(data[srcsample]) - 128) << 8; + if (sc->width == 2) + ((short *)sc->data)[i] = sample; + else + ((signed char *)sc->data)[i] = sample >> 8; + } + } +} + +//============================================================================= + +/* +============== +S_LoadSound +============== +*/ +sfxcache_t *S_LoadSound (sfx_t *s) +{ + char namebuffer[256]; + byte *data; + wavinfo_t info; + int len; + float stepscale; + sfxcache_t *sc; + byte stackbuf[1*1024]; // avoid dirtying the cache heap + +// see if still in memory + sc = Cache_Check (&s->cache); + if (sc) + return sc; + +//Con_Printf ("S_LoadSound: %x\n", (int)stackbuf); +// load it in + strcpy(namebuffer, "sound/"); + strcat(namebuffer, s->name); + +// Con_Printf ("loading %s\n",namebuffer); + + data = COM_LoadStackFile(namebuffer, stackbuf, sizeof(stackbuf)); + + if (!data) + { + Con_Printf ("Couldn't load %s\n", namebuffer); + return NULL; + } + + info = GetWavinfo (s->name, data, com_filesize); + if (info.channels != 1) + { + Con_Printf ("%s is a stereo sample\n",s->name); + return NULL; + } + + stepscale = (float)info.rate / shm->speed; + len = info.samples / stepscale; + + len = len * info.width * info.channels; + + sc = Cache_Alloc ( &s->cache, len + sizeof(sfxcache_t), s->name); + if (!sc) + return NULL; + + sc->length = info.samples; + sc->loopstart = info.loopstart; + sc->speed = info.rate; + sc->width = info.width; + sc->stereo = info.channels; + + ResampleSfx (s, sc->speed, sc->width, data + info.dataofs); + + return sc; +} + + + +/* +=============================================================================== + +WAV loading + +=============================================================================== +*/ + + +byte *data_p; +byte *iff_end; +byte *last_chunk; +byte *iff_data; +int iff_chunk_len; + + +short GetLittleShort(void) +{ + short val = 0; + val = *data_p; + val = val + (*(data_p+1)<<8); + data_p += 2; + return val; +} + +int GetLittleLong(void) +{ + int val = 0; + val = *data_p; + val = val + (*(data_p+1)<<8); + val = val + (*(data_p+2)<<16); + val = val + (*(data_p+3)<<24); + data_p += 4; + return val; +} + +void FindNextChunk(char *name) +{ + while (1) + { + data_p=last_chunk; + + if (data_p >= iff_end) + { // didn't find the chunk + data_p = NULL; + return; + } + + data_p += 4; + iff_chunk_len = GetLittleLong(); + if (iff_chunk_len < 0) + { + data_p = NULL; + return; + } +// if (iff_chunk_len > 1024*1024) +// Sys_Error ("FindNextChunk: %i length is past the 1 meg sanity limit", iff_chunk_len); + data_p -= 8; + last_chunk = data_p + 8 + ( (iff_chunk_len + 1) & ~1 ); + if (!strncmp(data_p, name, 4)) + return; + } +} + +void FindChunk(char *name) +{ + last_chunk = iff_data; + FindNextChunk (name); +} + + +#if 0 +void DumpChunks(void) +{ + char str[5]; + + str[4] = 0; + data_p=iff_data; + do + { + memcpy (str, data_p, 4); + data_p += 4; + iff_chunk_len = GetLittleLong(); + Con_Printf ("0x%x : %s (%d)\n", (int)(data_p - 4), str, iff_chunk_len); + data_p += (iff_chunk_len + 1) & ~1; + } while (data_p < iff_end); +} +#endif + +/* +============ +GetWavinfo +============ +*/ +wavinfo_t GetWavinfo (char *name, byte *wav, int wavlength) +{ + wavinfo_t info; + int i; + int format; + int samples; + + memset (&info, 0, sizeof(info)); + + if (!wav) + return info; + + iff_data = wav; + iff_end = wav + wavlength; + +// find "RIFF" chunk + FindChunk("RIFF"); + if (!(data_p && !strncmp(data_p+8, "WAVE", 4))) + { + Con_Printf("Missing RIFF/WAVE chunks\n"); + return info; + } + +// get "fmt " chunk + iff_data = data_p + 12; +// DumpChunks (); + + FindChunk("fmt "); + if (!data_p) + { + Con_Printf("Missing fmt chunk\n"); + return info; + } + data_p += 8; + format = GetLittleShort(); + if (format != 1) + { + Con_Printf("Microsoft PCM format only\n"); + return info; + } + + info.channels = GetLittleShort(); + info.rate = GetLittleLong(); + data_p += 4+2; + info.width = GetLittleShort() / 8; + +// get cue chunk + FindChunk("cue "); + if (data_p) + { + data_p += 32; + info.loopstart = GetLittleLong(); +// Con_Printf("loopstart=%d\n", sfx->loopstart); + + // if the next chunk is a LIST chunk, look for a cue length marker + FindNextChunk ("LIST"); + if (data_p) + { + if (!strncmp (data_p + 28, "mark", 4)) + { // this is not a proper parse, but it works with cooledit... + data_p += 24; + i = GetLittleLong (); // samples in loop + info.samples = info.loopstart + i; +// Con_Printf("looped length: %i\n", i); + } + } + } + else + info.loopstart = -1; + +// find data chunk + FindChunk("data"); + if (!data_p) + { + Con_Printf("Missing data chunk\n"); + return info; + } + + data_p += 4; + samples = GetLittleLong () / info.width; + + if (info.samples) + { + if (samples < info.samples) + Sys_Error ("Sound %s has a bad loop length", name); + } + else + info.samples = samples; + + info.dataofs = data_p - wav; + + return info; +} + diff --git a/source/snd_mix.c b/source/snd_mix.c index 10bfcef8..7eff862f 100644 --- a/source/snd_mix.c +++ b/source/snd_mix.c @@ -1,399 +1,399 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// snd_mix.c -- portable code to mix sounds for snd_dma.c - -#include "quakedef.h" -#include "sound.h" - -#ifdef _WIN32 -#include "winquake.h" -#else -#define DWORD unsigned long -#endif - -#define PAINTBUFFER_SIZE 512 -portable_samplepair_t paintbuffer[PAINTBUFFER_SIZE]; -int snd_scaletable[32][256]; -int *snd_p, snd_linear_count, snd_vol; -short *snd_out; - -void Snd_WriteLinearBlastStereo16 (void); - -#if !id386 -void Snd_WriteLinearBlastStereo16 (void) -{ - int i; - int val; - - for (i=0 ; i>8; - if (val > 0x7fff) - snd_out[i] = 0x7fff; - else if (val < (short)0x8000) - snd_out[i] = (short)0x8000; - else - snd_out[i] = val; - - val = (snd_p[i+1]*snd_vol)>>8; - if (val > 0x7fff) - snd_out[i+1] = 0x7fff; - else if (val < (short)0x8000) - snd_out[i+1] = (short)0x8000; - else - snd_out[i+1] = val; - } -} -#endif - -void S_TransferStereo16 (int endtime) -{ - int lpos; - int lpaintedtime; - DWORD *pbuf; -#ifdef _WIN32 - int reps; - DWORD dwSize,dwSize2; - DWORD *pbuf2; - HRESULT hresult; -#endif - - snd_vol = volume.value*256; - - snd_p = (int *) paintbuffer; - lpaintedtime = paintedtime; - -#ifdef _WIN32 - if (pDSBuf) - { - reps = 0; - - while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, &pbuf, &dwSize, - &pbuf2, &dwSize2, 0)) != DS_OK) - { - if (hresult != DSERR_BUFFERLOST) - { - Con_Printf ("S_TransferStereo16: DS::Lock Sound Buffer Failed\n"); - S_Shutdown (); - S_Startup (); - return; - } - - if (++reps > 10000) - { - Con_Printf ("S_TransferStereo16: DS: couldn't restore buffer\n"); - S_Shutdown (); - S_Startup (); - return; - } - } - } - else -#endif - { - pbuf = (DWORD *)shm->buffer; - } - - while (lpaintedtime < endtime) - { - // handle recirculating buffer issues - lpos = lpaintedtime & ((shm->samples>>1)-1); - - snd_out = (short *) pbuf + (lpos<<1); - - snd_linear_count = (shm->samples>>1) - lpos; - if (lpaintedtime + snd_linear_count > endtime) - snd_linear_count = endtime - lpaintedtime; - - snd_linear_count <<= 1; - - // write a linear blast of samples - Snd_WriteLinearBlastStereo16 (); - - snd_p += snd_linear_count; - lpaintedtime += (snd_linear_count>>1); - } - -#ifdef _WIN32 - if (pDSBuf) - pDSBuf->lpVtbl->Unlock(pDSBuf, pbuf, dwSize, NULL, 0); -#endif -} - -void S_TransferPaintBuffer(int endtime) -{ - int out_idx; - int count; - int out_mask; - int *p; - int step; - int val; - int snd_vol; - DWORD *pbuf; -#ifdef _WIN32 - int reps; - DWORD dwSize,dwSize2; - DWORD *pbuf2; - HRESULT hresult; -#endif - - if (shm->samplebits == 16 && shm->channels == 2) - { - S_TransferStereo16 (endtime); - return; - } - - p = (int *) paintbuffer; - count = (endtime - paintedtime) * shm->channels; - out_mask = shm->samples - 1; - out_idx = paintedtime * shm->channels & out_mask; - step = 3 - shm->channels; - snd_vol = volume.value*256; - -#ifdef _WIN32 - if (pDSBuf) - { - reps = 0; - - while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, &pbuf, &dwSize, - &pbuf2,&dwSize2, 0)) != DS_OK) - { - if (hresult != DSERR_BUFFERLOST) - { - Con_Printf ("S_TransferPaintBuffer: DS::Lock Sound Buffer Failed\n"); - S_Shutdown (); - S_Startup (); - return; - } - - if (++reps > 10000) - { - Con_Printf ("S_TransferPaintBuffer: DS: couldn't restore buffer\n"); - S_Shutdown (); - S_Startup (); - return; - } - } - } - else -#endif - { - pbuf = (DWORD *)shm->buffer; - } - - if (shm->samplebits == 16) - { - short *out = (short *) pbuf; - while (count--) - { - val = (*p * snd_vol) >> 8; - p+= step; - if (val > 0x7fff) - val = 0x7fff; - else if (val < (short)0x8000) - val = (short)0x8000; - out[out_idx] = val; - out_idx = (out_idx + 1) & out_mask; - } - } - else if (shm->samplebits == 8) - { - unsigned char *out = (unsigned char *) pbuf; - while (count--) - { - val = (*p * snd_vol) >> 8; - p+= step; - if (val > 0x7fff) - val = 0x7fff; - else if (val < (short)0x8000) - val = (short)0x8000; - out[out_idx] = (val>>8) + 128; - out_idx = (out_idx + 1) & out_mask; - } - } - -#ifdef _WIN32 - if (pDSBuf) { - DWORD dwNewpos, dwWrite; - int il = paintedtime; - int ir = endtime - paintedtime; - - ir += il; - - pDSBuf->lpVtbl->Unlock(pDSBuf, pbuf, dwSize, NULL, 0); - - pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &dwNewpos, &dwWrite); - -// if ((dwNewpos >= il) && (dwNewpos <= ir)) -// Con_Printf("%d-%d p %d c\n", il, ir, dwNewpos); - } -#endif -} - - -/* -=============================================================================== - -CHANNEL MIXING - -=============================================================================== -*/ - -void SND_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int endtime); -void SND_PaintChannelFrom16 (channel_t *ch, sfxcache_t *sc, int endtime); - -void S_PaintChannels(int endtime) -{ - int i; - int end; - channel_t *ch; - sfxcache_t *sc; - int ltime, count; - - while (paintedtime < endtime) - { - // if paintbuffer is smaller than DMA buffer - end = endtime; - if (endtime - paintedtime > PAINTBUFFER_SIZE) - end = paintedtime + PAINTBUFFER_SIZE; - - // clear the paint buffer - memset(paintbuffer, 0, (end - paintedtime) * sizeof(portable_samplepair_t)); - - // paint in the channels. - ch = channels; - for (i=0; isfx) - continue; - if (!ch->leftvol && !ch->rightvol) - continue; - sc = S_LoadSound (ch->sfx); - if (!sc) - continue; - - ltime = paintedtime; - - while (ltime < end) - { // paint up to end - if (ch->end < end) - count = ch->end - ltime; - else - count = end - ltime; - - if (count > 0) - { - if (sc->width == 1) - SND_PaintChannelFrom8(ch, sc, count); - else - SND_PaintChannelFrom16(ch, sc, count); - - ltime += count; - } - - // if at end of loop, restart - if (ltime >= ch->end) - { - if (sc->loopstart >= 0) - { - ch->pos = sc->loopstart; - ch->end = ltime + sc->length - ch->pos; - } - else - { // channel just stopped - ch->sfx = NULL; - break; - } - } - } - - } - - // transfer out according to DMA format - S_TransferPaintBuffer(end); - paintedtime = end; - } -} - -void SND_InitScaletable (void) -{ - int i, j; - - for (i=0 ; i<32 ; i++) - for (j=0 ; j<256 ; j++) - snd_scaletable[i][j] = ((signed char)j) * i * 8; -} - - -#if !id386 - -void SND_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int count) -{ - int data; - int *lscale, *rscale; - unsigned char *sfx; - int i; - - if (ch->leftvol > 255) - ch->leftvol = 255; - if (ch->rightvol > 255) - ch->rightvol = 255; - - lscale = snd_scaletable[ch->leftvol >> 3]; - rscale = snd_scaletable[ch->rightvol >> 3]; - sfx = (signed char *)sc->data + ch->pos; - - for (i=0 ; ipos += count; -} - -#endif // !id386 - - -void SND_PaintChannelFrom16 (channel_t *ch, sfxcache_t *sc, int count) -{ - int data; - int left, right; - int leftvol, rightvol; - signed short *sfx; - int i; - - leftvol = ch->leftvol; - rightvol = ch->rightvol; - sfx = (signed short *)sc->data + ch->pos; - - for (i=0 ; i> 8; - right = (data * rightvol) >> 8; - paintbuffer[i].left += left; - paintbuffer[i].right += right; - } - - ch->pos += count; -} - +/* +Copyright (C) 1996-1997 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. + +*/ +// snd_mix.c -- portable code to mix sounds for snd_dma.c + +#include "quakedef.h" +#include "sound.h" + +#ifdef _WIN32 +#include "winquake.h" +#else +#define DWORD unsigned long +#endif + +#define PAINTBUFFER_SIZE 512 +portable_samplepair_t paintbuffer[PAINTBUFFER_SIZE]; +int snd_scaletable[32][256]; +int *snd_p, snd_linear_count, snd_vol; +short *snd_out; + +void Snd_WriteLinearBlastStereo16 (void); + +#if !id386 +void Snd_WriteLinearBlastStereo16 (void) +{ + int i; + int val; + + for (i=0 ; i>8; + if (val > 0x7fff) + snd_out[i] = 0x7fff; + else if (val < (short)0x8000) + snd_out[i] = (short)0x8000; + else + snd_out[i] = val; + + val = (snd_p[i+1]*snd_vol)>>8; + if (val > 0x7fff) + snd_out[i+1] = 0x7fff; + else if (val < (short)0x8000) + snd_out[i+1] = (short)0x8000; + else + snd_out[i+1] = val; + } +} +#endif + +void S_TransferStereo16 (int endtime) +{ + int lpos; + int lpaintedtime; + DWORD *pbuf; +#ifdef _WIN32 + int reps; + DWORD dwSize,dwSize2; + DWORD *pbuf2; + HRESULT hresult; +#endif + + snd_vol = volume.value*256; + + snd_p = (int *) paintbuffer; + lpaintedtime = paintedtime; + +#ifdef _WIN32 + if (pDSBuf) + { + reps = 0; + + while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, &pbuf, &dwSize, + &pbuf2, &dwSize2, 0)) != DS_OK) + { + if (hresult != DSERR_BUFFERLOST) + { + Con_Printf ("S_TransferStereo16: DS::Lock Sound Buffer Failed\n"); + S_Shutdown (); + S_Startup (); + return; + } + + if (++reps > 10000) + { + Con_Printf ("S_TransferStereo16: DS: couldn't restore buffer\n"); + S_Shutdown (); + S_Startup (); + return; + } + } + } + else +#endif + { + pbuf = (DWORD *)shm->buffer; + } + + while (lpaintedtime < endtime) + { + // handle recirculating buffer issues + lpos = lpaintedtime & ((shm->samples>>1)-1); + + snd_out = (short *) pbuf + (lpos<<1); + + snd_linear_count = (shm->samples>>1) - lpos; + if (lpaintedtime + snd_linear_count > endtime) + snd_linear_count = endtime - lpaintedtime; + + snd_linear_count <<= 1; + + // write a linear blast of samples + Snd_WriteLinearBlastStereo16 (); + + snd_p += snd_linear_count; + lpaintedtime += (snd_linear_count>>1); + } + +#ifdef _WIN32 + if (pDSBuf) + pDSBuf->lpVtbl->Unlock(pDSBuf, pbuf, dwSize, NULL, 0); +#endif +} + +void S_TransferPaintBuffer(int endtime) +{ + int out_idx; + int count; + int out_mask; + int *p; + int step; + int val; + int snd_vol; + DWORD *pbuf; +#ifdef _WIN32 + int reps; + DWORD dwSize,dwSize2; + DWORD *pbuf2; + HRESULT hresult; +#endif + + if (shm->samplebits == 16 && shm->channels == 2) + { + S_TransferStereo16 (endtime); + return; + } + + p = (int *) paintbuffer; + count = (endtime - paintedtime) * shm->channels; + out_mask = shm->samples - 1; + out_idx = paintedtime * shm->channels & out_mask; + step = 3 - shm->channels; + snd_vol = volume.value*256; + +#ifdef _WIN32 + if (pDSBuf) + { + reps = 0; + + while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, &pbuf, &dwSize, + &pbuf2,&dwSize2, 0)) != DS_OK) + { + if (hresult != DSERR_BUFFERLOST) + { + Con_Printf ("S_TransferPaintBuffer: DS::Lock Sound Buffer Failed\n"); + S_Shutdown (); + S_Startup (); + return; + } + + if (++reps > 10000) + { + Con_Printf ("S_TransferPaintBuffer: DS: couldn't restore buffer\n"); + S_Shutdown (); + S_Startup (); + return; + } + } + } + else +#endif + { + pbuf = (DWORD *)shm->buffer; + } + + if (shm->samplebits == 16) + { + short *out = (short *) pbuf; + while (count--) + { + val = (*p * snd_vol) >> 8; + p+= step; + if (val > 0x7fff) + val = 0x7fff; + else if (val < (short)0x8000) + val = (short)0x8000; + out[out_idx] = val; + out_idx = (out_idx + 1) & out_mask; + } + } + else if (shm->samplebits == 8) + { + unsigned char *out = (unsigned char *) pbuf; + while (count--) + { + val = (*p * snd_vol) >> 8; + p+= step; + if (val > 0x7fff) + val = 0x7fff; + else if (val < (short)0x8000) + val = (short)0x8000; + out[out_idx] = (val>>8) + 128; + out_idx = (out_idx + 1) & out_mask; + } + } + +#ifdef _WIN32 + if (pDSBuf) { + DWORD dwNewpos, dwWrite; + int il = paintedtime; + int ir = endtime - paintedtime; + + ir += il; + + pDSBuf->lpVtbl->Unlock(pDSBuf, pbuf, dwSize, NULL, 0); + + pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &dwNewpos, &dwWrite); + +// if ((dwNewpos >= il) && (dwNewpos <= ir)) +// Con_Printf("%d-%d p %d c\n", il, ir, dwNewpos); + } +#endif +} + + +/* +=============================================================================== + +CHANNEL MIXING + +=============================================================================== +*/ + +void SND_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int endtime); +void SND_PaintChannelFrom16 (channel_t *ch, sfxcache_t *sc, int endtime); + +void S_PaintChannels(int endtime) +{ + int i; + int end; + channel_t *ch; + sfxcache_t *sc; + int ltime, count; + + while (paintedtime < endtime) + { + // if paintbuffer is smaller than DMA buffer + end = endtime; + if (endtime - paintedtime > PAINTBUFFER_SIZE) + end = paintedtime + PAINTBUFFER_SIZE; + + // clear the paint buffer + memset(paintbuffer, 0, (end - paintedtime) * sizeof(portable_samplepair_t)); + + // paint in the channels. + ch = channels; + for (i=0; isfx) + continue; + if (!ch->leftvol && !ch->rightvol) + continue; + sc = S_LoadSound (ch->sfx); + if (!sc) + continue; + + ltime = paintedtime; + + while (ltime < end) + { // paint up to end + if (ch->end < end) + count = ch->end - ltime; + else + count = end - ltime; + + if (count > 0) + { + if (sc->width == 1) + SND_PaintChannelFrom8(ch, sc, count); + else + SND_PaintChannelFrom16(ch, sc, count); + + ltime += count; + } + + // if at end of loop, restart + if (ltime >= ch->end) + { + if (sc->loopstart >= 0) + { + ch->pos = sc->loopstart; + ch->end = ltime + sc->length - ch->pos; + } + else + { // channel just stopped + ch->sfx = NULL; + break; + } + } + } + + } + + // transfer out according to DMA format + S_TransferPaintBuffer(end); + paintedtime = end; + } +} + +void SND_InitScaletable (void) +{ + int i, j; + + for (i=0 ; i<32 ; i++) + for (j=0 ; j<256 ; j++) + snd_scaletable[i][j] = ((signed char)j) * i * 8; +} + + +#if !id386 + +void SND_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int count) +{ + int data; + int *lscale, *rscale; + unsigned char *sfx; + int i; + + if (ch->leftvol > 255) + ch->leftvol = 255; + if (ch->rightvol > 255) + ch->rightvol = 255; + + lscale = snd_scaletable[ch->leftvol >> 3]; + rscale = snd_scaletable[ch->rightvol >> 3]; + sfx = (signed char *)sc->data + ch->pos; + + for (i=0 ; ipos += count; +} + +#endif // !id386 + + +void SND_PaintChannelFrom16 (channel_t *ch, sfxcache_t *sc, int count) +{ + int data; + int left, right; + int leftvol, rightvol; + signed short *sfx; + int i; + + leftvol = ch->leftvol; + rightvol = ch->rightvol; + sfx = (signed short *)sc->data + ch->pos; + + for (i=0 ; i> 8; + right = (data * rightvol) >> 8; + paintbuffer[i].left += left; + paintbuffer[i].right += right; + } + + ch->pos += count; +} + diff --git a/source/snd_mixa.s b/source/snd_mixa.s index 44d4b0aa..e554477b 100644 --- a/source/snd_mixa.s +++ b/source/snd_mixa.s @@ -1,229 +1,229 @@ -/* - snd_mixa.s - - x86 assembly language sound code - - Copyright (C) 1996-1997 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: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - - $Id: snd_mixa.s,v 1.1.1.4 2004/10/13 18:54:51 vvd0 Exp $ -*/ - -#include "asm_i386.h" -#include "quakeasm.h" - -#ifdef id386 - - .text - - .extern C(snd_scaletable) - .extern C(paintbuffer) - .extern C(snd_linear_count) - .extern C(snd_p) - .extern C(snd_vol) - .extern C(snd_out) - -//---------------------------------------------------------------------- -// 8-bit sound-mixing code -//---------------------------------------------------------------------- - -#define ch 4+16 -#define sc 8+16 -#define count 12+16 - -.globl C(SND_PaintChannelFrom8) -C(SND_PaintChannelFrom8): - pushl %esi // preserve register variables - pushl %edi - pushl %ebx - pushl %ebp - -// int data; -// short *lscale, *rscale; -// unsigned char *sfx; -// int i; - - movl ch(%esp),%ebx - movl sc(%esp),%esi - -// if (ch->leftvol > 255) -// ch->leftvol = 255; -// if (ch->rightvol > 255) -// ch->rightvol = 255; - movl ch_leftvol(%ebx),%eax - movl ch_rightvol(%ebx),%edx - cmpl $255,%eax - jna LLeftSet - movl $255,%eax -LLeftSet: - cmpl $255,%edx - jna LRightSet - movl $255,%edx -LRightSet: - -// lscale = snd_scaletable[ch->leftvol >> 3]; -// rscale = snd_scaletable[ch->rightvol >> 3]; -// sfx = (signed char *)sc->data + ch->pos; -// ch->pos += count; - andl $0xF8,%eax - addl $(sfxc_data),%esi - andl $0xF8,%edx - movl ch_pos(%ebx),%edi - movl count(%esp),%ecx - addl %edi,%esi - shll $7,%eax - addl %ecx,%edi - shll $7,%edx - movl %edi,ch_pos(%ebx) - addl $(C(snd_scaletable)),%eax - addl $(C(snd_scaletable)),%edx - subl %ebx,%ebx - movb -1(%esi,%ecx,1),%bl - - testl $1,%ecx - jz LMix8Loop - - movl (%eax,%ebx,4),%edi - movl (%edx,%ebx,4),%ebp - addl C(paintbuffer)+psp_left-psp_size(,%ecx,psp_size),%edi - addl C(paintbuffer)+psp_right-psp_size(,%ecx,psp_size),%ebp - movl %edi,C(paintbuffer)+psp_left-psp_size(,%ecx,psp_size) - movl %ebp,C(paintbuffer)+psp_right-psp_size(,%ecx,psp_size) - movb -2(%esi,%ecx,1),%bl - - decl %ecx - jz LDone - -// for (i=0 ; i>8; -// if (val > 0x7fff) -// snd_out[i] = 0x7fff; -// else if (val < (short)0x8000) -// snd_out[i] = (short)0x8000; -// else -// snd_out[i] = val; - movl -8(%ebx,%ecx,4),%eax - imull %esi,%eax - sarl $8,%eax - cmpl $0x7FFF,%eax - jg LClampHigh - cmpl $0xFFFF8000,%eax - jnl LClampDone - movl $0xFFFF8000,%eax - jmp LClampDone -LClampHigh: - movl $0x7FFF,%eax -LClampDone: - -// val = (snd_p[i+1]*snd_vol)>>8; -// if (val > 0x7fff) -// snd_out[i+1] = 0x7fff; -// else if (val < (short)0x8000) -// snd_out[i+1] = (short)0x8000; -// else -// snd_out[i+1] = val; - movl -4(%ebx,%ecx,4),%edx - imull %esi,%edx - sarl $8,%edx - cmpl $0x7FFF,%edx - jg LClampHigh2 - cmpl $0xFFFF8000,%edx - jnl LClampDone2 - movl $0xFFFF8000,%edx - jmp LClampDone2 -LClampHigh2: - movl $0x7FFF,%edx -LClampDone2: - shll $16,%edx - andl $0xFFFF,%eax - orl %eax,%edx - movl %edx,-4(%edi,%ecx,2) - -// } - subl $2,%ecx - jnz LWLBLoopTop - -// snd_p += snd_linear_count; - - popl %ebx - popl %edi - popl %esi - - ret - - -#endif // id386 - +/* + snd_mixa.s + + x86 assembly language sound code + + Copyright (C) 1996-1997 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id: snd_mixa.s,v 1.1.1.5 2004/10/18 17:44:39 vvd0 Exp $ +*/ + +#include "asm_i386.h" +#include "quakeasm.h" + +#ifdef id386 + + .text + + .extern C(snd_scaletable) + .extern C(paintbuffer) + .extern C(snd_linear_count) + .extern C(snd_p) + .extern C(snd_vol) + .extern C(snd_out) + +//---------------------------------------------------------------------- +// 8-bit sound-mixing code +//---------------------------------------------------------------------- + +#define ch 4+16 +#define sc 8+16 +#define count 12+16 + +.globl C(SND_PaintChannelFrom8) +C(SND_PaintChannelFrom8): + pushl %esi // preserve register variables + pushl %edi + pushl %ebx + pushl %ebp + +// int data; +// short *lscale, *rscale; +// unsigned char *sfx; +// int i; + + movl ch(%esp),%ebx + movl sc(%esp),%esi + +// if (ch->leftvol > 255) +// ch->leftvol = 255; +// if (ch->rightvol > 255) +// ch->rightvol = 255; + movl ch_leftvol(%ebx),%eax + movl ch_rightvol(%ebx),%edx + cmpl $255,%eax + jna LLeftSet + movl $255,%eax +LLeftSet: + cmpl $255,%edx + jna LRightSet + movl $255,%edx +LRightSet: + +// lscale = snd_scaletable[ch->leftvol >> 3]; +// rscale = snd_scaletable[ch->rightvol >> 3]; +// sfx = (signed char *)sc->data + ch->pos; +// ch->pos += count; + andl $0xF8,%eax + addl $(sfxc_data),%esi + andl $0xF8,%edx + movl ch_pos(%ebx),%edi + movl count(%esp),%ecx + addl %edi,%esi + shll $7,%eax + addl %ecx,%edi + shll $7,%edx + movl %edi,ch_pos(%ebx) + addl $(C(snd_scaletable)),%eax + addl $(C(snd_scaletable)),%edx + subl %ebx,%ebx + movb -1(%esi,%ecx,1),%bl + + testl $1,%ecx + jz LMix8Loop + + movl (%eax,%ebx,4),%edi + movl (%edx,%ebx,4),%ebp + addl C(paintbuffer)+psp_left-psp_size(,%ecx,psp_size),%edi + addl C(paintbuffer)+psp_right-psp_size(,%ecx,psp_size),%ebp + movl %edi,C(paintbuffer)+psp_left-psp_size(,%ecx,psp_size) + movl %ebp,C(paintbuffer)+psp_right-psp_size(,%ecx,psp_size) + movb -2(%esi,%ecx,1),%bl + + decl %ecx + jz LDone + +// for (i=0 ; i>8; +// if (val > 0x7fff) +// snd_out[i] = 0x7fff; +// else if (val < (short)0x8000) +// snd_out[i] = (short)0x8000; +// else +// snd_out[i] = val; + movl -8(%ebx,%ecx,4),%eax + imull %esi,%eax + sarl $8,%eax + cmpl $0x7FFF,%eax + jg LClampHigh + cmpl $0xFFFF8000,%eax + jnl LClampDone + movl $0xFFFF8000,%eax + jmp LClampDone +LClampHigh: + movl $0x7FFF,%eax +LClampDone: + +// val = (snd_p[i+1]*snd_vol)>>8; +// if (val > 0x7fff) +// snd_out[i+1] = 0x7fff; +// else if (val < (short)0x8000) +// snd_out[i+1] = (short)0x8000; +// else +// snd_out[i+1] = val; + movl -4(%ebx,%ecx,4),%edx + imull %esi,%edx + sarl $8,%edx + cmpl $0x7FFF,%edx + jg LClampHigh2 + cmpl $0xFFFF8000,%edx + jnl LClampDone2 + movl $0xFFFF8000,%edx + jmp LClampDone2 +LClampHigh2: + movl $0x7FFF,%edx +LClampDone2: + shll $16,%edx + andl $0xFFFF,%eax + orl %eax,%edx + movl %edx,-4(%edi,%ecx,2) + +// } + subl $2,%ecx + jnz LWLBLoopTop + +// snd_p += snd_linear_count; + + popl %ebx + popl %edi + popl %esi + + ret + + +#endif // id386 + diff --git a/source/snd_win.c b/source/snd_win.c index 86b26928..74b725a0 100644 --- a/source/snd_win.c +++ b/source/snd_win.c @@ -1,729 +1,729 @@ -/* -Copyright (C) 1996-1997 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 "quakedef.h" -#include "winquake.h" -#include "sound.h" - - -#define iDirectSoundCreate(a,b,c) pDirectSoundCreate(a,b,c) - -HRESULT (WINAPI *pDirectSoundCreate)(GUID FAR *lpGUID, LPDIRECTSOUND FAR *lplpDS, IUnknown FAR *pUnkOuter); - -// 64K is > 1 second at 16-bit, 22050 Hz -#define WAV_BUFFERS 64 -#define WAV_MASK 0x3F -#define WAV_BUFFER_SIZE 0x0400 -#define SECONDARY_BUFFER_SIZE 0x10000 - -typedef enum {SIS_SUCCESS, SIS_FAILURE, SIS_NOTAVAIL} sndinitstat; - -static qboolean wavonly; -static qboolean dsound_init; -static qboolean wav_init; -static qboolean snd_firsttime = true, snd_isdirect, snd_iswave; -static qboolean primary_format_set; - -static int sample16; -static int snd_sent, snd_completed; - - -/* - * Global variables. Must be visible to window-procedure function - * so it can unlock and free the data block after it has been played. - */ - -HANDLE hData; -HPSTR lpData, lpData2; - -HGLOBAL hWaveHdr; -LPWAVEHDR lpWaveHdr; - -HWAVEOUT hWaveOut; - -WAVEOUTCAPS wavecaps; - -DWORD gSndBufSize; - -MMTIME mmstarttime; - -LPDIRECTSOUND pDS; -LPDIRECTSOUNDBUFFER pDSBuf, pDSPBuf; - -HINSTANCE hInstDS; - -qboolean SNDDMA_InitDirect (void); -qboolean SNDDMA_InitWav (void); - - -/* -================== -S_BlockSound -================== -*/ -void S_BlockSound (void) -{ - -// DirectSound takes care of blocking itself - if (snd_iswave) - { - snd_blocked++; - - if (snd_blocked == 1) - waveOutReset (hWaveOut); - } -} - - -/* -================== -S_UnblockSound -================== -*/ -void S_UnblockSound (void) -{ - -// DirectSound takes care of blocking itself - if (snd_iswave) - { - snd_blocked--; - } -} - - -/* -================== -FreeSound -================== -*/ -void FreeSound (void) -{ - int i; - - if (pDSBuf) - { - pDSBuf->lpVtbl->Stop(pDSBuf); - pDSBuf->lpVtbl->Release(pDSBuf); - } - -// only release primary buffer if it's not also the mixing buffer we just released - if (pDSPBuf && (pDSBuf != pDSPBuf)) - { - pDSPBuf->lpVtbl->Release(pDSPBuf); - } - - if (pDS) - { - pDS->lpVtbl->SetCooperativeLevel (pDS, mainwindow, DSSCL_NORMAL); - pDS->lpVtbl->Release(pDS); - } - - if (hWaveOut) - { - waveOutReset (hWaveOut); - - if (lpWaveHdr) - { - for (i=0 ; i< WAV_BUFFERS ; i++) - waveOutUnprepareHeader (hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR)); - } - - waveOutClose (hWaveOut); - - if (hWaveHdr) - { - GlobalUnlock(hWaveHdr); - GlobalFree(hWaveHdr); - } - - if (hData) - { - GlobalUnlock(hData); - GlobalFree(hData); - } - - } - - pDS = NULL; - pDSBuf = NULL; - pDSPBuf = NULL; - hWaveOut = 0; - hData = 0; - hWaveHdr = 0; - lpData = NULL; - lpWaveHdr = NULL; - dsound_init = false; - wav_init = false; -} - - -/* -================== -SNDDMA_InitDirect - -Direct-Sound support -================== -*/ -sndinitstat SNDDMA_InitDirect (void) -{ - DSBUFFERDESC dsbuf; - DSBCAPS dsbcaps; - DWORD dwSize, dwWrite; - DSCAPS dscaps; - WAVEFORMATEX format, pformat; - HRESULT hresult; - int reps; - - memset ((void *)&sn, 0, sizeof (sn)); - - shm = &sn; - - shm->channels = 2; - shm->samplebits = 16; - shm->speed = 11025; - - memset (&format, 0, sizeof(format)); - format.wFormatTag = WAVE_FORMAT_PCM; - format.nChannels = shm->channels; - format.wBitsPerSample = shm->samplebits; - format.nSamplesPerSec = shm->speed; - format.nBlockAlign = format.nChannels - *format.wBitsPerSample / 8; - format.cbSize = 0; - format.nAvgBytesPerSec = format.nSamplesPerSec - *format.nBlockAlign; - - if (!hInstDS) - { - hInstDS = LoadLibrary("dsound.dll"); - - if (hInstDS == NULL) - { - Con_SafePrintf ("Couldn't load dsound.dll\n"); - return SIS_FAILURE; - } - - pDirectSoundCreate = (void *)GetProcAddress(hInstDS,"DirectSoundCreate"); - - if (!pDirectSoundCreate) - { - Con_SafePrintf ("Couldn't get DS proc addr\n"); - return SIS_FAILURE; - } - } - - while ((hresult = iDirectSoundCreate(NULL, &pDS, NULL)) != DS_OK) - { - if (hresult != DSERR_ALLOCATED) - { - Con_SafePrintf ("DirectSound create failed\n"); - return SIS_FAILURE; - } - - if (MessageBox (NULL, - "The sound hardware is in use by another app.\n\n" - "Select Retry to try to start sound again or Cancel to run Quake with no sound.", - "Sound not available", - MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY) - { - Con_SafePrintf ("DirectSoundCreate failure\n" - " hardware already in use\n"); - return SIS_NOTAVAIL; - } - } - - dscaps.dwSize = sizeof(dscaps); - - if (DS_OK != pDS->lpVtbl->GetCaps (pDS, &dscaps)) - { - Con_SafePrintf ("Couldn't get DS caps\n"); - } - - if (dscaps.dwFlags & DSCAPS_EMULDRIVER) - { - Con_SafePrintf ("No DirectSound driver installed\n"); - FreeSound (); - return SIS_FAILURE; - } - - if (DS_OK != pDS->lpVtbl->SetCooperativeLevel (pDS, mainwindow, DSSCL_EXCLUSIVE)) - { - Con_SafePrintf ("Set coop level failed\n"); - FreeSound (); - return SIS_FAILURE; - } - -// get access to the primary buffer, if possible, so we can set the -// sound hardware format - memset (&dsbuf, 0, sizeof(dsbuf)); - dsbuf.dwSize = sizeof(DSBUFFERDESC); - dsbuf.dwFlags = DSBCAPS_PRIMARYBUFFER; - dsbuf.dwBufferBytes = 0; - dsbuf.lpwfxFormat = NULL; - - memset(&dsbcaps, 0, sizeof(dsbcaps)); - dsbcaps.dwSize = sizeof(dsbcaps); - primary_format_set = false; - - if (!COM_CheckParm ("-snoforceformat")) - { - if (DS_OK == pDS->lpVtbl->CreateSoundBuffer(pDS, &dsbuf, &pDSPBuf, NULL)) - { - pformat = format; - - if (DS_OK != pDSPBuf->lpVtbl->SetFormat (pDSPBuf, &pformat)) - { -// if (snd_firsttime) -// Con_SafePrintf ("Set primary sound buffer format: no\n"); - } - else -// { -// if (snd_firsttime) -// Con_SafePrintf ("Set primary sound buffer format: yes\n"); - - primary_format_set = true; -// } - } - } - - if (!primary_format_set || !COM_CheckParm ("-primarysound")) - { - // create the secondary buffer we'll actually work with - memset (&dsbuf, 0, sizeof(dsbuf)); - dsbuf.dwSize = sizeof(DSBUFFERDESC); - dsbuf.dwFlags = DSBCAPS_CTRLFREQUENCY | DSBCAPS_LOCSOFTWARE; - dsbuf.dwBufferBytes = SECONDARY_BUFFER_SIZE; - dsbuf.lpwfxFormat = &format; - - memset(&dsbcaps, 0, sizeof(dsbcaps)); - dsbcaps.dwSize = sizeof(dsbcaps); - - if (DS_OK != pDS->lpVtbl->CreateSoundBuffer(pDS, &dsbuf, &pDSBuf, NULL)) - { - Con_SafePrintf ("DS:CreateSoundBuffer Failed"); - FreeSound (); - return SIS_FAILURE; - } - - shm->channels = format.nChannels; - shm->samplebits = format.wBitsPerSample; - shm->speed = format.nSamplesPerSec; - - if (DS_OK != pDSBuf->lpVtbl->GetCaps (pDSBuf, &dsbcaps)) - { - Con_SafePrintf ("DS:GetCaps failed\n"); - FreeSound (); - return SIS_FAILURE; - } - -// if (snd_firsttime) -// Con_SafePrintf ("Using secondary sound buffer\n"); - } - else - { - if (DS_OK != pDS->lpVtbl->SetCooperativeLevel (pDS, mainwindow, DSSCL_WRITEPRIMARY)) - { - Con_SafePrintf ("Set coop level failed\n"); - FreeSound (); - return SIS_FAILURE; - } - - if (DS_OK != pDSPBuf->lpVtbl->GetCaps (pDSPBuf, &dsbcaps)) - { - Con_Printf ("DS:GetCaps failed\n"); - return SIS_FAILURE; - } - - pDSBuf = pDSPBuf; -// Con_SafePrintf ("Using primary sound buffer\n"); - } - - // Make sure mixer is active - pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING); - -/* if (snd_firsttime) - Con_SafePrintf(" %d channel(s)\n" - " %d bits/sample\n" - " %d bytes/sec\n", - shm->channels, shm->samplebits, shm->speed);*/ - - gSndBufSize = dsbcaps.dwBufferBytes; - -// initialize the buffer - reps = 0; - - while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, &lpData, &dwSize, NULL, NULL, 0)) != DS_OK) - { - if (hresult != DSERR_BUFFERLOST) - { - Con_SafePrintf ("SNDDMA_InitDirect: DS::Lock Sound Buffer Failed\n"); - FreeSound (); - return SIS_FAILURE; - } - - if (++reps > 10000) - { - Con_SafePrintf ("SNDDMA_InitDirect: DS: couldn't restore buffer\n"); - FreeSound (); - return SIS_FAILURE; - } - - } - - memset(lpData, 0, dwSize); -// lpData[4] = lpData[5] = 0x7f; // force a pop for debugging - - pDSBuf->lpVtbl->Unlock(pDSBuf, lpData, dwSize, NULL, 0); - - /* we don't want anyone to access the buffer directly w/o locking it first. */ - lpData = NULL; - - pDSBuf->lpVtbl->Stop(pDSBuf); - pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &mmstarttime.u.sample, &dwWrite); - pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING); - - shm->soundalive = true; - shm->splitbuffer = false; - shm->samples = gSndBufSize/(shm->samplebits/8); - shm->samplepos = 0; - shm->submission_chunk = 1; - shm->buffer = (unsigned char *) lpData; - sample16 = (shm->samplebits/8) - 1; - - dsound_init = true; - - return SIS_SUCCESS; -} - - -/* -================== -SNDDM_InitWav - -Crappy windows multimedia base -================== -*/ -qboolean SNDDMA_InitWav (void) -{ - WAVEFORMATEX format; - int i; - HRESULT hr; - - snd_sent = 0; - snd_completed = 0; - - shm = &sn; - - shm->channels = 2; - shm->samplebits = 16; - shm->speed = 11025; - - memset (&format, 0, sizeof(format)); - format.wFormatTag = WAVE_FORMAT_PCM; - format.nChannels = shm->channels; - format.wBitsPerSample = shm->samplebits; - format.nSamplesPerSec = shm->speed; - format.nBlockAlign = format.nChannels - *format.wBitsPerSample / 8; - format.cbSize = 0; - format.nAvgBytesPerSec = format.nSamplesPerSec - *format.nBlockAlign; - - /* Open a waveform device for output using window callback. */ - while ((hr = waveOutOpen((LPHWAVEOUT)&hWaveOut, WAVE_MAPPER, - &format, - 0, 0L, CALLBACK_NULL)) != MMSYSERR_NOERROR) - { - if (hr != MMSYSERR_ALLOCATED) - { - Con_SafePrintf ("waveOutOpen failed\n"); - return false; - } - - if (MessageBox (NULL, - "The sound hardware is in use by another app.\n\n" - "Select Retry to try to start sound again or Cancel to run Quake with no sound.", - "Sound not available", - MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY) - { - Con_SafePrintf ("waveOutOpen failure;\n" - " hardware already in use\n"); - return false; - } - } - - /* - * Allocate and lock memory for the waveform data. The memory - * for waveform data must be globally allocated with - * GMEM_MOVEABLE and GMEM_SHARE flags. - - */ - gSndBufSize = WAV_BUFFERS*WAV_BUFFER_SIZE; - hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, gSndBufSize); - if (!hData) - { - Con_SafePrintf ("Sound: Out of memory.\n"); - FreeSound (); - return false; - } - lpData = GlobalLock(hData); - if (!lpData) - { - Con_SafePrintf ("Sound: Failed to lock.\n"); - FreeSound (); - return false; - } - memset (lpData, 0, gSndBufSize); - - /* - * Allocate and lock memory for the header. This memory must - * also be globally allocated with GMEM_MOVEABLE and - * GMEM_SHARE flags. - */ - hWaveHdr = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, - (DWORD) sizeof(WAVEHDR) * WAV_BUFFERS); - - if (hWaveHdr == NULL) - { - Con_SafePrintf ("Sound: Failed to Alloc header.\n"); - FreeSound (); - return false; - } - - lpWaveHdr = (LPWAVEHDR) GlobalLock(hWaveHdr); - - if (lpWaveHdr == NULL) - { - Con_SafePrintf ("Sound: Failed to lock header.\n"); - FreeSound (); - return false; - } - - memset (lpWaveHdr, 0, sizeof(WAVEHDR) * WAV_BUFFERS); - - /* After allocation, set up and prepare headers. */ - for (i=0 ; isoundalive = true; - shm->splitbuffer = false; - shm->samples = gSndBufSize/(shm->samplebits/8); - shm->samplepos = 0; - shm->submission_chunk = 1; - shm->buffer = (unsigned char *) lpData; - sample16 = (shm->samplebits/8) - 1; - - wav_init = true; - - return true; -} - -/* -================== -SNDDMA_Init - -Try to find a sound device to mix for. -Returns false if nothing is found. -================== -*/ - -int SNDDMA_Init(void) -{ - sndinitstat stat; - - if (COM_CheckParm ("-wavonly")) - wavonly = true; - - dsound_init = wav_init = 0; - - stat = SIS_FAILURE; // assume DirectSound won't initialize - - /* Init DirectSound */ - if (!wavonly) - { - if (snd_firsttime || snd_isdirect) - { - stat = SNDDMA_InitDirect ();; - - if (stat == SIS_SUCCESS) - { - snd_isdirect = true; - - if (snd_firsttime) - Con_SafePrintf ("DirectSound initialized\n"); - } - else - { - snd_isdirect = false; - Con_SafePrintf ("DirectSound failed to init\n"); - } - } - } - -// if DirectSound didn't succeed in initializing, try to initialize -// waveOut sound, unless DirectSound failed because the hardware is -// already allocated (in which case the user has already chosen not -// to have sound) - if (!dsound_init && (stat != SIS_NOTAVAIL)) - { - if (snd_firsttime || snd_iswave) - { - - snd_iswave = SNDDMA_InitWav (); - - if (snd_iswave) - { - if (snd_firsttime) - Con_SafePrintf ("Wave sound initialized\n"); - } - else - { - Con_SafePrintf ("Wave sound failed to init\n"); - } - } - } - - snd_firsttime = false; - - if (!dsound_init && !wav_init) - { - if (snd_firsttime) - Con_SafePrintf ("No sound device initialized\n"); - - return 0; - } - - return 1; -} - -/* -============== -SNDDMA_GetDMAPos - -return the current sample position (in mono samples read) -inside the recirculating dma buffer, so the mixing code will know -how many sample are required to fill it up. -=============== -*/ -int SNDDMA_GetDMAPos(void) -{ - MMTIME mmtime; - int s; - DWORD dwWrite; - - if (dsound_init) - { - mmtime.wType = TIME_SAMPLES; - pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &mmtime.u.sample, &dwWrite); - s = mmtime.u.sample - mmstarttime.u.sample; - } - else if (wav_init) - { - s = snd_sent * WAV_BUFFER_SIZE; - } - - - s >>= sample16; - - s &= (shm->samples-1); - - return s; -} - -/* -============== -SNDDMA_Submit - -Send sound to device if buffer isn't really the dma buffer -=============== -*/ -void SNDDMA_Submit(void) -{ - LPWAVEHDR h; - int wResult; - - if (!wav_init) - return; - - // - // find which sound blocks have completed - // - while (1) - { - if ( snd_completed == snd_sent ) - { - Con_DPrintf ("Sound overrun\n"); - break; - } - - if ( ! (lpWaveHdr[ snd_completed & WAV_MASK].dwFlags & WHDR_DONE) ) - { - break; - } - - snd_completed++; // this buffer has been played - } - - // - // submit two new sound blocks - // - while (((snd_sent - snd_completed) >> sample16) < 4) - { - h = lpWaveHdr + ( snd_sent&WAV_MASK ); - - snd_sent++; - /* - * Now the data block can be sent to the output device. The - * waveOutWrite function returns immediately and waveform - * data is sent to the output device in the background. - */ - wResult = waveOutWrite(hWaveOut, h, sizeof(WAVEHDR)); - - if (wResult != MMSYSERR_NOERROR) - { - Con_SafePrintf ("Failed to write block to device\n"); - FreeSound (); - return; - } - } -} - -/* -============== -SNDDMA_Shutdown - -Reset the sound device for exiting -=============== -*/ -void SNDDMA_Shutdown(void) -{ - FreeSound (); -} - +/* +Copyright (C) 1996-1997 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 "quakedef.h" +#include "winquake.h" +#include "sound.h" + + +#define iDirectSoundCreate(a,b,c) pDirectSoundCreate(a,b,c) + +HRESULT (WINAPI *pDirectSoundCreate)(GUID FAR *lpGUID, LPDIRECTSOUND FAR *lplpDS, IUnknown FAR *pUnkOuter); + +// 64K is > 1 second at 16-bit, 22050 Hz +#define WAV_BUFFERS 64 +#define WAV_MASK 0x3F +#define WAV_BUFFER_SIZE 0x0400 +#define SECONDARY_BUFFER_SIZE 0x10000 + +typedef enum {SIS_SUCCESS, SIS_FAILURE, SIS_NOTAVAIL} sndinitstat; + +static qboolean wavonly; +static qboolean dsound_init; +static qboolean wav_init; +static qboolean snd_firsttime = true, snd_isdirect, snd_iswave; +static qboolean primary_format_set; + +static int sample16; +static int snd_sent, snd_completed; + + +/* + * Global variables. Must be visible to window-procedure function + * so it can unlock and free the data block after it has been played. + */ + +HANDLE hData; +HPSTR lpData, lpData2; + +HGLOBAL hWaveHdr; +LPWAVEHDR lpWaveHdr; + +HWAVEOUT hWaveOut; + +WAVEOUTCAPS wavecaps; + +DWORD gSndBufSize; + +MMTIME mmstarttime; + +LPDIRECTSOUND pDS; +LPDIRECTSOUNDBUFFER pDSBuf, pDSPBuf; + +HINSTANCE hInstDS; + +qboolean SNDDMA_InitDirect (void); +qboolean SNDDMA_InitWav (void); + + +/* +================== +S_BlockSound +================== +*/ +void S_BlockSound (void) +{ + +// DirectSound takes care of blocking itself + if (snd_iswave) + { + snd_blocked++; + + if (snd_blocked == 1) + waveOutReset (hWaveOut); + } +} + + +/* +================== +S_UnblockSound +================== +*/ +void S_UnblockSound (void) +{ + +// DirectSound takes care of blocking itself + if (snd_iswave) + { + snd_blocked--; + } +} + + +/* +================== +FreeSound +================== +*/ +void FreeSound (void) +{ + int i; + + if (pDSBuf) + { + pDSBuf->lpVtbl->Stop(pDSBuf); + pDSBuf->lpVtbl->Release(pDSBuf); + } + +// only release primary buffer if it's not also the mixing buffer we just released + if (pDSPBuf && (pDSBuf != pDSPBuf)) + { + pDSPBuf->lpVtbl->Release(pDSPBuf); + } + + if (pDS) + { + pDS->lpVtbl->SetCooperativeLevel (pDS, mainwindow, DSSCL_NORMAL); + pDS->lpVtbl->Release(pDS); + } + + if (hWaveOut) + { + waveOutReset (hWaveOut); + + if (lpWaveHdr) + { + for (i=0 ; i< WAV_BUFFERS ; i++) + waveOutUnprepareHeader (hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR)); + } + + waveOutClose (hWaveOut); + + if (hWaveHdr) + { + GlobalUnlock(hWaveHdr); + GlobalFree(hWaveHdr); + } + + if (hData) + { + GlobalUnlock(hData); + GlobalFree(hData); + } + + } + + pDS = NULL; + pDSBuf = NULL; + pDSPBuf = NULL; + hWaveOut = 0; + hData = 0; + hWaveHdr = 0; + lpData = NULL; + lpWaveHdr = NULL; + dsound_init = false; + wav_init = false; +} + + +/* +================== +SNDDMA_InitDirect + +Direct-Sound support +================== +*/ +sndinitstat SNDDMA_InitDirect (void) +{ + DSBUFFERDESC dsbuf; + DSBCAPS dsbcaps; + DWORD dwSize, dwWrite; + DSCAPS dscaps; + WAVEFORMATEX format, pformat; + HRESULT hresult; + int reps; + + memset ((void *)&sn, 0, sizeof (sn)); + + shm = &sn; + + shm->channels = 2; + shm->samplebits = 16; + shm->speed = 11025; + + memset (&format, 0, sizeof(format)); + format.wFormatTag = WAVE_FORMAT_PCM; + format.nChannels = shm->channels; + format.wBitsPerSample = shm->samplebits; + format.nSamplesPerSec = shm->speed; + format.nBlockAlign = format.nChannels + *format.wBitsPerSample / 8; + format.cbSize = 0; + format.nAvgBytesPerSec = format.nSamplesPerSec + *format.nBlockAlign; + + if (!hInstDS) + { + hInstDS = LoadLibrary("dsound.dll"); + + if (hInstDS == NULL) + { + Con_SafePrintf ("Couldn't load dsound.dll\n"); + return SIS_FAILURE; + } + + pDirectSoundCreate = (void *)GetProcAddress(hInstDS,"DirectSoundCreate"); + + if (!pDirectSoundCreate) + { + Con_SafePrintf ("Couldn't get DS proc addr\n"); + return SIS_FAILURE; + } + } + + while ((hresult = iDirectSoundCreate(NULL, &pDS, NULL)) != DS_OK) + { + if (hresult != DSERR_ALLOCATED) + { + Con_SafePrintf ("DirectSound create failed\n"); + return SIS_FAILURE; + } + + if (MessageBox (NULL, + "The sound hardware is in use by another app.\n\n" + "Select Retry to try to start sound again or Cancel to run Quake with no sound.", + "Sound not available", + MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY) + { + Con_SafePrintf ("DirectSoundCreate failure\n" + " hardware already in use\n"); + return SIS_NOTAVAIL; + } + } + + dscaps.dwSize = sizeof(dscaps); + + if (DS_OK != pDS->lpVtbl->GetCaps (pDS, &dscaps)) + { + Con_SafePrintf ("Couldn't get DS caps\n"); + } + + if (dscaps.dwFlags & DSCAPS_EMULDRIVER) + { + Con_SafePrintf ("No DirectSound driver installed\n"); + FreeSound (); + return SIS_FAILURE; + } + + if (DS_OK != pDS->lpVtbl->SetCooperativeLevel (pDS, mainwindow, DSSCL_EXCLUSIVE)) + { + Con_SafePrintf ("Set coop level failed\n"); + FreeSound (); + return SIS_FAILURE; + } + +// get access to the primary buffer, if possible, so we can set the +// sound hardware format + memset (&dsbuf, 0, sizeof(dsbuf)); + dsbuf.dwSize = sizeof(DSBUFFERDESC); + dsbuf.dwFlags = DSBCAPS_PRIMARYBUFFER; + dsbuf.dwBufferBytes = 0; + dsbuf.lpwfxFormat = NULL; + + memset(&dsbcaps, 0, sizeof(dsbcaps)); + dsbcaps.dwSize = sizeof(dsbcaps); + primary_format_set = false; + + if (!COM_CheckParm ("-snoforceformat")) + { + if (DS_OK == pDS->lpVtbl->CreateSoundBuffer(pDS, &dsbuf, &pDSPBuf, NULL)) + { + pformat = format; + + if (DS_OK != pDSPBuf->lpVtbl->SetFormat (pDSPBuf, &pformat)) + { +// if (snd_firsttime) +// Con_SafePrintf ("Set primary sound buffer format: no\n"); + } + else +// { +// if (snd_firsttime) +// Con_SafePrintf ("Set primary sound buffer format: yes\n"); + + primary_format_set = true; +// } + } + } + + if (!primary_format_set || !COM_CheckParm ("-primarysound")) + { + // create the secondary buffer we'll actually work with + memset (&dsbuf, 0, sizeof(dsbuf)); + dsbuf.dwSize = sizeof(DSBUFFERDESC); + dsbuf.dwFlags = DSBCAPS_CTRLFREQUENCY | DSBCAPS_LOCSOFTWARE; + dsbuf.dwBufferBytes = SECONDARY_BUFFER_SIZE; + dsbuf.lpwfxFormat = &format; + + memset(&dsbcaps, 0, sizeof(dsbcaps)); + dsbcaps.dwSize = sizeof(dsbcaps); + + if (DS_OK != pDS->lpVtbl->CreateSoundBuffer(pDS, &dsbuf, &pDSBuf, NULL)) + { + Con_SafePrintf ("DS:CreateSoundBuffer Failed"); + FreeSound (); + return SIS_FAILURE; + } + + shm->channels = format.nChannels; + shm->samplebits = format.wBitsPerSample; + shm->speed = format.nSamplesPerSec; + + if (DS_OK != pDSBuf->lpVtbl->GetCaps (pDSBuf, &dsbcaps)) + { + Con_SafePrintf ("DS:GetCaps failed\n"); + FreeSound (); + return SIS_FAILURE; + } + +// if (snd_firsttime) +// Con_SafePrintf ("Using secondary sound buffer\n"); + } + else + { + if (DS_OK != pDS->lpVtbl->SetCooperativeLevel (pDS, mainwindow, DSSCL_WRITEPRIMARY)) + { + Con_SafePrintf ("Set coop level failed\n"); + FreeSound (); + return SIS_FAILURE; + } + + if (DS_OK != pDSPBuf->lpVtbl->GetCaps (pDSPBuf, &dsbcaps)) + { + Con_Printf ("DS:GetCaps failed\n"); + return SIS_FAILURE; + } + + pDSBuf = pDSPBuf; +// Con_SafePrintf ("Using primary sound buffer\n"); + } + + // Make sure mixer is active + pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING); + +/* if (snd_firsttime) + Con_SafePrintf(" %d channel(s)\n" + " %d bits/sample\n" + " %d bytes/sec\n", + shm->channels, shm->samplebits, shm->speed);*/ + + gSndBufSize = dsbcaps.dwBufferBytes; + +// initialize the buffer + reps = 0; + + while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, &lpData, &dwSize, NULL, NULL, 0)) != DS_OK) + { + if (hresult != DSERR_BUFFERLOST) + { + Con_SafePrintf ("SNDDMA_InitDirect: DS::Lock Sound Buffer Failed\n"); + FreeSound (); + return SIS_FAILURE; + } + + if (++reps > 10000) + { + Con_SafePrintf ("SNDDMA_InitDirect: DS: couldn't restore buffer\n"); + FreeSound (); + return SIS_FAILURE; + } + + } + + memset(lpData, 0, dwSize); +// lpData[4] = lpData[5] = 0x7f; // force a pop for debugging + + pDSBuf->lpVtbl->Unlock(pDSBuf, lpData, dwSize, NULL, 0); + + /* we don't want anyone to access the buffer directly w/o locking it first. */ + lpData = NULL; + + pDSBuf->lpVtbl->Stop(pDSBuf); + pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &mmstarttime.u.sample, &dwWrite); + pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING); + + shm->soundalive = true; + shm->splitbuffer = false; + shm->samples = gSndBufSize/(shm->samplebits/8); + shm->samplepos = 0; + shm->submission_chunk = 1; + shm->buffer = (unsigned char *) lpData; + sample16 = (shm->samplebits/8) - 1; + + dsound_init = true; + + return SIS_SUCCESS; +} + + +/* +================== +SNDDM_InitWav + +Crappy windows multimedia base +================== +*/ +qboolean SNDDMA_InitWav (void) +{ + WAVEFORMATEX format; + int i; + HRESULT hr; + + snd_sent = 0; + snd_completed = 0; + + shm = &sn; + + shm->channels = 2; + shm->samplebits = 16; + shm->speed = 11025; + + memset (&format, 0, sizeof(format)); + format.wFormatTag = WAVE_FORMAT_PCM; + format.nChannels = shm->channels; + format.wBitsPerSample = shm->samplebits; + format.nSamplesPerSec = shm->speed; + format.nBlockAlign = format.nChannels + *format.wBitsPerSample / 8; + format.cbSize = 0; + format.nAvgBytesPerSec = format.nSamplesPerSec + *format.nBlockAlign; + + /* Open a waveform device for output using window callback. */ + while ((hr = waveOutOpen((LPHWAVEOUT)&hWaveOut, WAVE_MAPPER, + &format, + 0, 0L, CALLBACK_NULL)) != MMSYSERR_NOERROR) + { + if (hr != MMSYSERR_ALLOCATED) + { + Con_SafePrintf ("waveOutOpen failed\n"); + return false; + } + + if (MessageBox (NULL, + "The sound hardware is in use by another app.\n\n" + "Select Retry to try to start sound again or Cancel to run Quake with no sound.", + "Sound not available", + MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY) + { + Con_SafePrintf ("waveOutOpen failure;\n" + " hardware already in use\n"); + return false; + } + } + + /* + * Allocate and lock memory for the waveform data. The memory + * for waveform data must be globally allocated with + * GMEM_MOVEABLE and GMEM_SHARE flags. + + */ + gSndBufSize = WAV_BUFFERS*WAV_BUFFER_SIZE; + hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, gSndBufSize); + if (!hData) + { + Con_SafePrintf ("Sound: Out of memory.\n"); + FreeSound (); + return false; + } + lpData = GlobalLock(hData); + if (!lpData) + { + Con_SafePrintf ("Sound: Failed to lock.\n"); + FreeSound (); + return false; + } + memset (lpData, 0, gSndBufSize); + + /* + * Allocate and lock memory for the header. This memory must + * also be globally allocated with GMEM_MOVEABLE and + * GMEM_SHARE flags. + */ + hWaveHdr = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, + (DWORD) sizeof(WAVEHDR) * WAV_BUFFERS); + + if (hWaveHdr == NULL) + { + Con_SafePrintf ("Sound: Failed to Alloc header.\n"); + FreeSound (); + return false; + } + + lpWaveHdr = (LPWAVEHDR) GlobalLock(hWaveHdr); + + if (lpWaveHdr == NULL) + { + Con_SafePrintf ("Sound: Failed to lock header.\n"); + FreeSound (); + return false; + } + + memset (lpWaveHdr, 0, sizeof(WAVEHDR) * WAV_BUFFERS); + + /* After allocation, set up and prepare headers. */ + for (i=0 ; isoundalive = true; + shm->splitbuffer = false; + shm->samples = gSndBufSize/(shm->samplebits/8); + shm->samplepos = 0; + shm->submission_chunk = 1; + shm->buffer = (unsigned char *) lpData; + sample16 = (shm->samplebits/8) - 1; + + wav_init = true; + + return true; +} + +/* +================== +SNDDMA_Init + +Try to find a sound device to mix for. +Returns false if nothing is found. +================== +*/ + +int SNDDMA_Init(void) +{ + sndinitstat stat; + + if (COM_CheckParm ("-wavonly")) + wavonly = true; + + dsound_init = wav_init = 0; + + stat = SIS_FAILURE; // assume DirectSound won't initialize + + /* Init DirectSound */ + if (!wavonly) + { + if (snd_firsttime || snd_isdirect) + { + stat = SNDDMA_InitDirect ();; + + if (stat == SIS_SUCCESS) + { + snd_isdirect = true; + + if (snd_firsttime) + Con_SafePrintf ("DirectSound initialized\n"); + } + else + { + snd_isdirect = false; + Con_SafePrintf ("DirectSound failed to init\n"); + } + } + } + +// if DirectSound didn't succeed in initializing, try to initialize +// waveOut sound, unless DirectSound failed because the hardware is +// already allocated (in which case the user has already chosen not +// to have sound) + if (!dsound_init && (stat != SIS_NOTAVAIL)) + { + if (snd_firsttime || snd_iswave) + { + + snd_iswave = SNDDMA_InitWav (); + + if (snd_iswave) + { + if (snd_firsttime) + Con_SafePrintf ("Wave sound initialized\n"); + } + else + { + Con_SafePrintf ("Wave sound failed to init\n"); + } + } + } + + snd_firsttime = false; + + if (!dsound_init && !wav_init) + { + if (snd_firsttime) + Con_SafePrintf ("No sound device initialized\n"); + + return 0; + } + + return 1; +} + +/* +============== +SNDDMA_GetDMAPos + +return the current sample position (in mono samples read) +inside the recirculating dma buffer, so the mixing code will know +how many sample are required to fill it up. +=============== +*/ +int SNDDMA_GetDMAPos(void) +{ + MMTIME mmtime; + int s; + DWORD dwWrite; + + if (dsound_init) + { + mmtime.wType = TIME_SAMPLES; + pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &mmtime.u.sample, &dwWrite); + s = mmtime.u.sample - mmstarttime.u.sample; + } + else if (wav_init) + { + s = snd_sent * WAV_BUFFER_SIZE; + } + + + s >>= sample16; + + s &= (shm->samples-1); + + return s; +} + +/* +============== +SNDDMA_Submit + +Send sound to device if buffer isn't really the dma buffer +=============== +*/ +void SNDDMA_Submit(void) +{ + LPWAVEHDR h; + int wResult; + + if (!wav_init) + return; + + // + // find which sound blocks have completed + // + while (1) + { + if ( snd_completed == snd_sent ) + { + Con_DPrintf ("Sound overrun\n"); + break; + } + + if ( ! (lpWaveHdr[ snd_completed & WAV_MASK].dwFlags & WHDR_DONE) ) + { + break; + } + + snd_completed++; // this buffer has been played + } + + // + // submit two new sound blocks + // + while (((snd_sent - snd_completed) >> sample16) < 4) + { + h = lpWaveHdr + ( snd_sent&WAV_MASK ); + + snd_sent++; + /* + * Now the data block can be sent to the output device. The + * waveOutWrite function returns immediately and waveform + * data is sent to the output device in the background. + */ + wResult = waveOutWrite(hWaveOut, h, sizeof(WAVEHDR)); + + if (wResult != MMSYSERR_NOERROR) + { + Con_SafePrintf ("Failed to write block to device\n"); + FreeSound (); + return; + } + } +} + +/* +============== +SNDDMA_Shutdown + +Reset the sound device for exiting +=============== +*/ +void SNDDMA_Shutdown(void) +{ + FreeSound (); +} + diff --git a/source/sound.h b/source/sound.h index 12a1a4c9..941e620b 100644 --- a/source/sound.h +++ b/source/sound.h @@ -1,174 +1,174 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// sound.h -- client sound i/o functions - -#ifndef __SOUND__ -#define __SOUND__ - -// !!! if this is changed, it much be changed in asm_i386.h too !!! -typedef struct -{ - int left; - int right; -} portable_samplepair_t; - -typedef struct sfx_s -{ - char name[MAX_QPATH]; - cache_user_t cache; -} sfx_t; - -// !!! if this is changed, it much be changed in asm_i386.h too !!! -typedef struct -{ - int length; - int loopstart; - int speed; - int width; - int stereo; - byte data[1]; // variable sized -} sfxcache_t; - -typedef struct -{ - qboolean gamealive; - qboolean soundalive; - qboolean splitbuffer; - int channels; - int samples; // mono samples in buffer - int submission_chunk; // don't mix less than this # - int samplepos; // in mono samples - int samplebits; - int speed; - unsigned char *buffer; -} dma_t; - -// !!! if this is changed, it much be changed in asm_i386.h too !!! -typedef struct -{ - sfx_t *sfx; // sfx number - int leftvol; // 0-255 volume - int rightvol; // 0-255 volume - int end; // end time in global paintsamples - int pos; // sample position in sfx - int looping; // where to loop, -1 = no looping - int entnum; // to allow overriding a specific sound - int entchannel; // - vec3_t origin; // origin of sound effect - vec_t dist_mult; // distance multiplier (attenuation/clipK) - int master_vol; // 0-255 master volume -} channel_t; - -typedef struct -{ - int rate; - int width; - int channels; - int loopstart; - int samples; - int dataofs; // chunk starts this many bytes from file start -} wavinfo_t; - -void S_Init (void); -void S_Startup (void); -void S_Shutdown (void); -void S_StartSound (int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float fvol, float attenuation); -void S_StaticSound (sfx_t *sfx, vec3_t origin, float vol, float attenuation); -void S_StopSound (int entnum, int entchannel); -void S_StopAllSounds(qboolean clear); -void S_ClearBuffer (void); -void S_Update (vec3_t origin, vec3_t v_forward, vec3_t v_right, vec3_t v_up); -void S_ExtraUpdate (void); - -sfx_t *S_PrecacheSound (char *sample); -void S_TouchSound (char *sample); -void S_ClearPrecache (void); -void S_BeginPrecaching (void); -void S_EndPrecaching (void); -void S_PaintChannels(int endtime); -void S_InitPaintChannels (void); - -// picks a channel based on priorities, empty slots, number of channels -channel_t *SND_PickChannel(int entnum, int entchannel); - -// spatializes a channel -void SND_Spatialize(channel_t *ch); - -// initializes cycling through a DMA buffer and returns information on it -qboolean SNDDMA_Init(void); - -// gets the current DMA position -int SNDDMA_GetDMAPos(void); - -// shutdown the DMA xfer. -void SNDDMA_Shutdown(void); - -// ==================================================================== -// User-setable variables -// ==================================================================== - -#define MAX_CHANNELS 128 -#define MAX_DYNAMIC_CHANNELS 8 - - -extern channel_t channels[MAX_CHANNELS]; -// 0 to MAX_DYNAMIC_CHANNELS-1 = normal entity sounds -// MAX_DYNAMIC_CHANNELS to MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS -1 = water, etc -// MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS to total_channels = static sounds - -extern int total_channels; - -// -// Fake dma is a synchronous faking of the DMA progress used for -// isolating performance in the renderer. The fakedma_updates is -// number of times S_Update() is called per second. -// - -extern qboolean fakedma; -extern int fakedma_updates; -extern int paintedtime; -extern vec3_t listener_origin; -extern vec3_t listener_forward; -extern vec3_t listener_right; -extern vec3_t listener_up; -extern volatile dma_t *shm; -extern volatile dma_t sn; -extern vec_t sound_nominal_clip_dist; - -extern cvar_t loadas8bit; -extern cvar_t bgmvolume; -extern cvar_t volume; - -extern qboolean snd_initialized; - -extern int snd_blocked; - -void S_LocalSound (char *s); -sfxcache_t *S_LoadSound (sfx_t *s); - -wavinfo_t GetWavinfo (char *name, byte *wav, int wavlength); - -void SND_InitScaletable (void); -void SNDDMA_Submit(void); - -void S_AmbientOff (void); -void S_AmbientOn (void); - -#endif +/* +Copyright (C) 1996-1997 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. + +*/ +// sound.h -- client sound i/o functions + +#ifndef __SOUND__ +#define __SOUND__ + +// !!! if this is changed, it much be changed in asm_i386.h too !!! +typedef struct +{ + int left; + int right; +} portable_samplepair_t; + +typedef struct sfx_s +{ + char name[MAX_QPATH]; + cache_user_t cache; +} sfx_t; + +// !!! if this is changed, it much be changed in asm_i386.h too !!! +typedef struct +{ + int length; + int loopstart; + int speed; + int width; + int stereo; + byte data[1]; // variable sized +} sfxcache_t; + +typedef struct +{ + qboolean gamealive; + qboolean soundalive; + qboolean splitbuffer; + int channels; + int samples; // mono samples in buffer + int submission_chunk; // don't mix less than this # + int samplepos; // in mono samples + int samplebits; + int speed; + unsigned char *buffer; +} dma_t; + +// !!! if this is changed, it much be changed in asm_i386.h too !!! +typedef struct +{ + sfx_t *sfx; // sfx number + int leftvol; // 0-255 volume + int rightvol; // 0-255 volume + int end; // end time in global paintsamples + int pos; // sample position in sfx + int looping; // where to loop, -1 = no looping + int entnum; // to allow overriding a specific sound + int entchannel; // + vec3_t origin; // origin of sound effect + vec_t dist_mult; // distance multiplier (attenuation/clipK) + int master_vol; // 0-255 master volume +} channel_t; + +typedef struct +{ + int rate; + int width; + int channels; + int loopstart; + int samples; + int dataofs; // chunk starts this many bytes from file start +} wavinfo_t; + +void S_Init (void); +void S_Startup (void); +void S_Shutdown (void); +void S_StartSound (int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float fvol, float attenuation); +void S_StaticSound (sfx_t *sfx, vec3_t origin, float vol, float attenuation); +void S_StopSound (int entnum, int entchannel); +void S_StopAllSounds(qboolean clear); +void S_ClearBuffer (void); +void S_Update (vec3_t origin, vec3_t v_forward, vec3_t v_right, vec3_t v_up); +void S_ExtraUpdate (void); + +sfx_t *S_PrecacheSound (char *sample); +void S_TouchSound (char *sample); +void S_ClearPrecache (void); +void S_BeginPrecaching (void); +void S_EndPrecaching (void); +void S_PaintChannels(int endtime); +void S_InitPaintChannels (void); + +// picks a channel based on priorities, empty slots, number of channels +channel_t *SND_PickChannel(int entnum, int entchannel); + +// spatializes a channel +void SND_Spatialize(channel_t *ch); + +// initializes cycling through a DMA buffer and returns information on it +qboolean SNDDMA_Init(void); + +// gets the current DMA position +int SNDDMA_GetDMAPos(void); + +// shutdown the DMA xfer. +void SNDDMA_Shutdown(void); + +// ==================================================================== +// User-setable variables +// ==================================================================== + +#define MAX_CHANNELS 128 +#define MAX_DYNAMIC_CHANNELS 8 + + +extern channel_t channels[MAX_CHANNELS]; +// 0 to MAX_DYNAMIC_CHANNELS-1 = normal entity sounds +// MAX_DYNAMIC_CHANNELS to MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS -1 = water, etc +// MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS to total_channels = static sounds + +extern int total_channels; + +// +// Fake dma is a synchronous faking of the DMA progress used for +// isolating performance in the renderer. The fakedma_updates is +// number of times S_Update() is called per second. +// + +extern qboolean fakedma; +extern int fakedma_updates; +extern int paintedtime; +extern vec3_t listener_origin; +extern vec3_t listener_forward; +extern vec3_t listener_right; +extern vec3_t listener_up; +extern volatile dma_t *shm; +extern volatile dma_t sn; +extern vec_t sound_nominal_clip_dist; + +extern cvar_t loadas8bit; +extern cvar_t bgmvolume; +extern cvar_t volume; + +extern qboolean snd_initialized; + +extern int snd_blocked; + +void S_LocalSound (char *s); +sfxcache_t *S_LoadSound (sfx_t *s); + +wavinfo_t GetWavinfo (char *name, byte *wav, int wavlength); + +void SND_InitScaletable (void); +void SNDDMA_Submit(void); + +void S_AmbientOff (void); +void S_AmbientOn (void); + +#endif diff --git a/source/spritegn.h b/source/spritegn.h index 337c1d6b..b192b540 100644 --- a/source/spritegn.h +++ b/source/spritegn.h @@ -1,110 +1,110 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// -// spritegn.h: header file for sprite generation program -// - -// ********************************************************** -// * This file must be identical in the spritegen directory * -// * and in the Quake directory, because it's used to * -// * pass data from one to the other via .spr files. * -// ********************************************************** - -//------------------------------------------------------- -// This program generates .spr sprite package files. -// The format of the files is as follows: -// -// dsprite_t file header structure -// -// -// dspriteframe_t frame header structure -// sprite bitmap -// -// dspriteframe_t frame header structure -// sprite bitmap -// -//------------------------------------------------------- - -#ifdef INCLUDELIBS - -#include -#include -#include -#include - -#include "cmdlib.h" -#include "scriplib.h" -#include "dictlib.h" -#include "trilib.h" -#include "lbmlib.h" -#include "mathlib.h" - -#endif - -#define SPRITE_VERSION 1 - -// must match definition in modelgen.h -#ifndef SYNCTYPE_T -#define SYNCTYPE_T -typedef enum {ST_SYNC=0, ST_RAND } synctype_t; -#endif - -// TODO: shorten these? -typedef struct { - int ident; - int version; - int type; - float boundingradius; - int width; - int height; - int numframes; - float beamlength; - synctype_t synctype; -} dsprite_t; - -#define SPR_VP_PARALLEL_UPRIGHT 0 -#define SPR_FACING_UPRIGHT 1 -#define SPR_VP_PARALLEL 2 -#define SPR_ORIENTED 3 -#define SPR_VP_PARALLEL_ORIENTED 4 - -typedef struct { - int origin[2]; - int width; - int height; -} dspriteframe_t; - -typedef struct { - int numframes; -} dspritegroup_t; - -typedef struct { - float interval; -} dspriteinterval_t; - -typedef enum { SPR_SINGLE=0, SPR_GROUP } spriteframetype_t; - -typedef struct { - spriteframetype_t type; -} dspriteframetype_t; - -#define IDSPRITEHEADER (('P'<<24)+('S'<<16)+('D'<<8)+'I') - // little-endian "IDSP" - +/* +Copyright (C) 1996-1997 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. + +*/ +// +// spritegn.h: header file for sprite generation program +// + +// ********************************************************** +// * This file must be identical in the spritegen directory * +// * and in the Quake directory, because it's used to * +// * pass data from one to the other via .spr files. * +// ********************************************************** + +//------------------------------------------------------- +// This program generates .spr sprite package files. +// The format of the files is as follows: +// +// dsprite_t file header structure +// +// +// dspriteframe_t frame header structure +// sprite bitmap +// +// dspriteframe_t frame header structure +// sprite bitmap +// +//------------------------------------------------------- + +#ifdef INCLUDELIBS + +#include +#include +#include +#include + +#include "cmdlib.h" +#include "scriplib.h" +#include "dictlib.h" +#include "trilib.h" +#include "lbmlib.h" +#include "mathlib.h" + +#endif + +#define SPRITE_VERSION 1 + +// must match definition in modelgen.h +#ifndef SYNCTYPE_T +#define SYNCTYPE_T +typedef enum {ST_SYNC=0, ST_RAND } synctype_t; +#endif + +// TODO: shorten these? +typedef struct { + int ident; + int version; + int type; + float boundingradius; + int width; + int height; + int numframes; + float beamlength; + synctype_t synctype; +} dsprite_t; + +#define SPR_VP_PARALLEL_UPRIGHT 0 +#define SPR_FACING_UPRIGHT 1 +#define SPR_VP_PARALLEL 2 +#define SPR_ORIENTED 3 +#define SPR_VP_PARALLEL_ORIENTED 4 + +typedef struct { + int origin[2]; + int width; + int height; +} dspriteframe_t; + +typedef struct { + int numframes; +} dspritegroup_t; + +typedef struct { + float interval; +} dspriteinterval_t; + +typedef enum { SPR_SINGLE=0, SPR_GROUP } spriteframetype_t; + +typedef struct { + spriteframetype_t type; +} dspriteframetype_t; + +#define IDSPRITEHEADER (('P'<<24)+('S'<<16)+('D'<<8)+'I') + // little-endian "IDSP" + diff --git a/source/surf16.s b/source/surf16.s index 0c5742e8..36e5913c 100644 --- a/source/surf16.s +++ b/source/surf16.s @@ -1,178 +1,178 @@ -/* - surf16.S - - (description) - - Copyright (C) 1996-1997 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: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - - $Id: surf16.s,v 1.1.1.3 2004/10/13 18:54:52 vvd0 Exp $ -*/ -// surf16.s -// x86 assembly-language 16 bpp surface block drawing code. - -#include "asm_i386.h" -#include "quakeasm.h" -#include "asm_draw.h" - -#ifdef id386 - -//---------------------------------------------------------------------- -// Surface block drawer -//---------------------------------------------------------------------- - - .data - -k: .long 0 -loopentry: .long 0 - - .align 4 -blockjumptable16: - .long LEnter2_16 - .long LEnter4_16 - .long 0, LEnter8_16 - .long 0, 0, 0, LEnter16_16 - - - .text - - .align 4 -.globl C(R_Surf16Start) -C(R_Surf16Start): - - .align 4 -.globl C(R_DrawSurfaceBlock16) -C(R_DrawSurfaceBlock16): - pushl %ebp // preserve caller's stack frame - pushl %edi - pushl %esi // preserve register variables - pushl %ebx - - movl C(blocksize),%eax - movl C(prowdestbase),%edi - movl C(pbasesource),%esi - movl C(sourcesstep),%ebx - movl blockjumptable16-4(,%eax,2),%ecx - movl %eax,k - movl %ecx,loopentry - movl C(lightleft),%edx - movl C(lightright),%ebp - -Lblockloop16: - - subl %edx,%ebp - movb C(blockdivshift),%cl - sarl %cl,%ebp - jns Lp1_16 - testl C(blockdivmask),%ebp - jz Lp1_16 - incl %ebp -Lp1_16: - - subl %eax,%eax - subl %ecx,%ecx // high words must be 0 in loop for addressing - - jmp *loopentry - - .align 4 - -#include "block16.h" - - movl C(pbasesource),%esi - movl C(lightleft),%edx - movl C(lightright),%ebp - movl C(sourcetstep),%eax - movl C(lightrightstep),%ecx - movl C(prowdestbase),%edi - - addl %eax,%esi - addl %ecx,%ebp - - movl C(lightleftstep),%eax - movl C(surfrowbytes),%ecx - - addl %eax,%edx - addl %ecx,%edi - - movl %esi,C(pbasesource) - movl %ebp,C(lightright) - movl k,%eax - movl %edx,C(lightleft) - decl %eax - movl %edi,C(prowdestbase) - movl %eax,k - jnz Lblockloop16 - - popl %ebx // restore register variables - popl %esi - popl %edi - popl %ebp // restore the caller's stack frame - ret - -.globl C(R_Surf16End) -C(R_Surf16End): - -//---------------------------------------------------------------------- -// Code patching routines -//---------------------------------------------------------------------- - .data - - .align 4 -LPatchTable16: - .long LBPatch0-4 - .long LBPatch1-4 - .long LBPatch2-4 - .long LBPatch3-4 - .long LBPatch4-4 - .long LBPatch5-4 - .long LBPatch6-4 - .long LBPatch7-4 - .long LBPatch8-4 - .long LBPatch9-4 - .long LBPatch10-4 - .long LBPatch11-4 - .long LBPatch12-4 - .long LBPatch13-4 - .long LBPatch14-4 - .long LBPatch15-4 - - .text - - .align 4 -.globl C(R_Surf16Patch) -C(R_Surf16Patch): - pushl %ebx - - movl C(colormap),%eax - movl $LPatchTable16,%ebx - movl $16,%ecx -LPatchLoop16: - movl (%ebx),%edx - addl $4,%ebx - movl %eax,(%edx) - decl %ecx - jnz LPatchLoop16 - - popl %ebx - - ret - - -#endif // id386 +/* + surf16.S + + (description) + + Copyright (C) 1996-1997 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id: surf16.s,v 1.1.1.4 2004/10/18 17:44:39 vvd0 Exp $ +*/ +// surf16.s +// x86 assembly-language 16 bpp surface block drawing code. + +#include "asm_i386.h" +#include "quakeasm.h" +#include "asm_draw.h" + +#ifdef id386 + +//---------------------------------------------------------------------- +// Surface block drawer +//---------------------------------------------------------------------- + + .data + +k: .long 0 +loopentry: .long 0 + + .align 4 +blockjumptable16: + .long LEnter2_16 + .long LEnter4_16 + .long 0, LEnter8_16 + .long 0, 0, 0, LEnter16_16 + + + .text + + .align 4 +.globl C(R_Surf16Start) +C(R_Surf16Start): + + .align 4 +.globl C(R_DrawSurfaceBlock16) +C(R_DrawSurfaceBlock16): + pushl %ebp // preserve caller's stack frame + pushl %edi + pushl %esi // preserve register variables + pushl %ebx + + movl C(blocksize),%eax + movl C(prowdestbase),%edi + movl C(pbasesource),%esi + movl C(sourcesstep),%ebx + movl blockjumptable16-4(,%eax,2),%ecx + movl %eax,k + movl %ecx,loopentry + movl C(lightleft),%edx + movl C(lightright),%ebp + +Lblockloop16: + + subl %edx,%ebp + movb C(blockdivshift),%cl + sarl %cl,%ebp + jns Lp1_16 + testl C(blockdivmask),%ebp + jz Lp1_16 + incl %ebp +Lp1_16: + + subl %eax,%eax + subl %ecx,%ecx // high words must be 0 in loop for addressing + + jmp *loopentry + + .align 4 + +#include "block16.h" + + movl C(pbasesource),%esi + movl C(lightleft),%edx + movl C(lightright),%ebp + movl C(sourcetstep),%eax + movl C(lightrightstep),%ecx + movl C(prowdestbase),%edi + + addl %eax,%esi + addl %ecx,%ebp + + movl C(lightleftstep),%eax + movl C(surfrowbytes),%ecx + + addl %eax,%edx + addl %ecx,%edi + + movl %esi,C(pbasesource) + movl %ebp,C(lightright) + movl k,%eax + movl %edx,C(lightleft) + decl %eax + movl %edi,C(prowdestbase) + movl %eax,k + jnz Lblockloop16 + + popl %ebx // restore register variables + popl %esi + popl %edi + popl %ebp // restore the caller's stack frame + ret + +.globl C(R_Surf16End) +C(R_Surf16End): + +//---------------------------------------------------------------------- +// Code patching routines +//---------------------------------------------------------------------- + .data + + .align 4 +LPatchTable16: + .long LBPatch0-4 + .long LBPatch1-4 + .long LBPatch2-4 + .long LBPatch3-4 + .long LBPatch4-4 + .long LBPatch5-4 + .long LBPatch6-4 + .long LBPatch7-4 + .long LBPatch8-4 + .long LBPatch9-4 + .long LBPatch10-4 + .long LBPatch11-4 + .long LBPatch12-4 + .long LBPatch13-4 + .long LBPatch14-4 + .long LBPatch15-4 + + .text + + .align 4 +.globl C(R_Surf16Patch) +C(R_Surf16Patch): + pushl %ebx + + movl C(colormap),%eax + movl $LPatchTable16,%ebx + movl $16,%ecx +LPatchLoop16: + movl (%ebx),%edx + addl $4,%ebx + movl %eax,(%edx) + decl %ecx + jnz LPatchLoop16 + + popl %ebx + + ret + + +#endif // id386 diff --git a/source/surf8.s b/source/surf8.s index 261308e6..c9e16197 100644 --- a/source/surf8.s +++ b/source/surf8.s @@ -1,789 +1,789 @@ -/* - surf8.S - - (description) - - Copyright (C) 1996-1997 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: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - - $Id: surf8.s,v 1.1.1.3 2004/10/13 18:54:52 vvd0 Exp $ -*/ -// surf8.s -// x86 assembly-language 8 bpp surface block drawing code. - -#include "asm_i386.h" -#include "quakeasm.h" -#include "asm_draw.h" - -#ifdef id386 - - .data - -sb_v: .long 0 - - .text - - .align 4 -.globl C(R_Surf8Start) -C(R_Surf8Start): - -//---------------------------------------------------------------------- -// Surface block drawer for mip level 0 -//---------------------------------------------------------------------- - - .align 4 -.globl C(R_DrawSurfaceBlock8_mip0) -C(R_DrawSurfaceBlock8_mip0): - pushl %ebp // preserve caller's stack frame - pushl %edi - pushl %esi // preserve register variables - pushl %ebx - -// for (v=0 ; v> blockdivshift; -// lightrightstep = (lightptr[1] - lightright) >> blockdivshift; -// lightdeltastep = ((lightleftstep - lightrightstep) & 0xFFFFF) | -// 0xF0000000; - movl 4(%ebx),%ecx // lightptr[1] - movl (%ebx),%ebx // lightptr[0] - - subl %eax,%ebx - subl %edx,%ecx - - sarl $4,%ecx - orl $0xF0000000,%ebp - - sarl $4,%ebx - movl %ecx,C(lightrightstep) - - subl %ecx,%ebx - andl $0xFFFFF,%ebx - - orl $0xF0000000,%ebx - subl %ecx,%ecx // high word must be 0 in loop for addressing - - movl %ebx,C(lightdeltastep) - subl %ebx,%ebx // high word must be 0 in loop for addressing - -Lblockloop8_mip0: - movl %ebp,C(lightdelta) - movb 14(%esi),%cl - - sarl $4,%ebp - movb %dh,%bh - - movb 15(%esi),%bl - addl %ebp,%edx - - movb %dh,%ch - addl %ebp,%edx - - movb 0x12345678(%ebx),%ah -LBPatch0: - movb 13(%esi),%bl - - movb 0x12345678(%ecx),%al -LBPatch1: - movb 12(%esi),%cl - - movb %dh,%bh - addl %ebp,%edx - - rorl $16,%eax - movb %dh,%ch - - addl %ebp,%edx - movb 0x12345678(%ebx),%ah -LBPatch2: - - movb 11(%esi),%bl - movb 0x12345678(%ecx),%al -LBPatch3: - - movb 10(%esi),%cl - movl %eax,12(%edi) - - movb %dh,%bh - addl %ebp,%edx - - movb %dh,%ch - addl %ebp,%edx - - movb 0x12345678(%ebx),%ah -LBPatch4: - movb 9(%esi),%bl - - movb 0x12345678(%ecx),%al -LBPatch5: - movb 8(%esi),%cl - - movb %dh,%bh - addl %ebp,%edx - - rorl $16,%eax - movb %dh,%ch - - addl %ebp,%edx - movb 0x12345678(%ebx),%ah -LBPatch6: - - movb 7(%esi),%bl - movb 0x12345678(%ecx),%al -LBPatch7: - - movb 6(%esi),%cl - movl %eax,8(%edi) - - movb %dh,%bh - addl %ebp,%edx - - movb %dh,%ch - addl %ebp,%edx - - movb 0x12345678(%ebx),%ah -LBPatch8: - movb 5(%esi),%bl - - movb 0x12345678(%ecx),%al -LBPatch9: - movb 4(%esi),%cl - - movb %dh,%bh - addl %ebp,%edx - - rorl $16,%eax - movb %dh,%ch - - addl %ebp,%edx - movb 0x12345678(%ebx),%ah -LBPatch10: - - movb 3(%esi),%bl - movb 0x12345678(%ecx),%al -LBPatch11: - - movb 2(%esi),%cl - movl %eax,4(%edi) - - movb %dh,%bh - addl %ebp,%edx - - movb %dh,%ch - addl %ebp,%edx - - movb 0x12345678(%ebx),%ah -LBPatch12: - movb 1(%esi),%bl - - movb 0x12345678(%ecx),%al -LBPatch13: - movb (%esi),%cl - - movb %dh,%bh - addl %ebp,%edx - - rorl $16,%eax - movb %dh,%ch - - movb 0x12345678(%ebx),%ah -LBPatch14: - movl C(lightright),%edx - - movb 0x12345678(%ecx),%al -LBPatch15: - movl C(lightdelta),%ebp - - movl %eax,(%edi) - - addl C(sourcetstep),%esi - addl C(surfrowbytes),%edi - - addl C(lightrightstep),%edx - addl C(lightdeltastep),%ebp - - movl %edx,C(lightright) - jc Lblockloop8_mip0 - -// if (pbasesource >= r_sourcemax) -// pbasesource -= stepback; - - cmpl C(r_sourcemax),%esi - jb LSkip_mip0 - subl C(r_stepback),%esi -LSkip_mip0: - - movl C(r_lightptr),%ebx - decl sb_v - - jnz Lv_loop_mip0 - - popl %ebx // restore register variables - popl %esi - popl %edi - popl %ebp // restore the caller's stack frame - ret - - -//---------------------------------------------------------------------- -// Surface block drawer for mip level 1 -//---------------------------------------------------------------------- - - .align 4 -.globl C(R_DrawSurfaceBlock8_mip1) -C(R_DrawSurfaceBlock8_mip1): - pushl %ebp // preserve caller's stack frame - pushl %edi - pushl %esi // preserve register variables - pushl %ebx - -// for (v=0 ; v> blockdivshift; -// lightrightstep = (lightptr[1] - lightright) >> blockdivshift; -// lightdeltastep = ((lightleftstep - lightrightstep) & 0xFFFFF) | -// 0xF0000000; - movl 4(%ebx),%ecx // lightptr[1] - movl (%ebx),%ebx // lightptr[0] - - subl %eax,%ebx - subl %edx,%ecx - - sarl $3,%ecx - orl $0x70000000,%ebp - - sarl $3,%ebx - movl %ecx,C(lightrightstep) - - subl %ecx,%ebx - andl $0xFFFFF,%ebx - - orl $0xF0000000,%ebx - subl %ecx,%ecx // high word must be 0 in loop for addressing - - movl %ebx,C(lightdeltastep) - subl %ebx,%ebx // high word must be 0 in loop for addressing - -Lblockloop8_mip1: - movl %ebp,C(lightdelta) - movb 6(%esi),%cl - - sarl $3,%ebp - movb %dh,%bh - - movb 7(%esi),%bl - addl %ebp,%edx - - movb %dh,%ch - addl %ebp,%edx - - movb 0x12345678(%ebx),%ah -LBPatch22: - movb 5(%esi),%bl - - movb 0x12345678(%ecx),%al -LBPatch23: - movb 4(%esi),%cl - - movb %dh,%bh - addl %ebp,%edx - - rorl $16,%eax - movb %dh,%ch - - addl %ebp,%edx - movb 0x12345678(%ebx),%ah -LBPatch24: - - movb 3(%esi),%bl - movb 0x12345678(%ecx),%al -LBPatch25: - - movb 2(%esi),%cl - movl %eax,4(%edi) - - movb %dh,%bh - addl %ebp,%edx - - movb %dh,%ch - addl %ebp,%edx - - movb 0x12345678(%ebx),%ah -LBPatch26: - movb 1(%esi),%bl - - movb 0x12345678(%ecx),%al -LBPatch27: - movb (%esi),%cl - - movb %dh,%bh - addl %ebp,%edx - - rorl $16,%eax - movb %dh,%ch - - movb 0x12345678(%ebx),%ah -LBPatch28: - movl C(lightright),%edx - - movb 0x12345678(%ecx),%al -LBPatch29: - movl C(lightdelta),%ebp - - movl %eax,(%edi) - movl C(sourcetstep),%eax - - addl %eax,%esi - movl C(surfrowbytes),%eax - - addl %eax,%edi - movl C(lightrightstep),%eax - - addl %eax,%edx - movl C(lightdeltastep),%eax - - addl %eax,%ebp - movl %edx,C(lightright) - - jc Lblockloop8_mip1 - -// if (pbasesource >= r_sourcemax) -// pbasesource -= stepback; - - cmpl C(r_sourcemax),%esi - jb LSkip_mip1 - subl C(r_stepback),%esi -LSkip_mip1: - - movl C(r_lightptr),%ebx - decl sb_v - - jnz Lv_loop_mip1 - - popl %ebx // restore register variables - popl %esi - popl %edi - popl %ebp // restore the caller's stack frame - ret - - -//---------------------------------------------------------------------- -// Surface block drawer for mip level 2 -//---------------------------------------------------------------------- - - .align 4 -.globl C(R_DrawSurfaceBlock8_mip2) -C(R_DrawSurfaceBlock8_mip2): - pushl %ebp // preserve caller's stack frame - pushl %edi - pushl %esi // preserve register variables - pushl %ebx - -// for (v=0 ; v> blockdivshift; -// lightrightstep = (lightptr[1] - lightright) >> blockdivshift; -// lightdeltastep = ((lightleftstep - lightrightstep) & 0xFFFFF) | -// 0xF0000000; - movl 4(%ebx),%ecx // lightptr[1] - movl (%ebx),%ebx // lightptr[0] - - subl %eax,%ebx - subl %edx,%ecx - - sarl $2,%ecx - orl $0x30000000,%ebp - - sarl $2,%ebx - movl %ecx,C(lightrightstep) - - subl %ecx,%ebx - - andl $0xFFFFF,%ebx - - orl $0xF0000000,%ebx - subl %ecx,%ecx // high word must be 0 in loop for addressing - - movl %ebx,C(lightdeltastep) - subl %ebx,%ebx // high word must be 0 in loop for addressing - -Lblockloop8_mip2: - movl %ebp,C(lightdelta) - movb 2(%esi),%cl - - sarl $2,%ebp - movb %dh,%bh - - movb 3(%esi),%bl - addl %ebp,%edx - - movb %dh,%ch - addl %ebp,%edx - - movb 0x12345678(%ebx),%ah -LBPatch18: - movb 1(%esi),%bl - - movb 0x12345678(%ecx),%al -LBPatch19: - movb (%esi),%cl - - movb %dh,%bh - addl %ebp,%edx - - rorl $16,%eax - movb %dh,%ch - - movb 0x12345678(%ebx),%ah -LBPatch20: - movl C(lightright),%edx - - movb 0x12345678(%ecx),%al -LBPatch21: - movl C(lightdelta),%ebp - - movl %eax,(%edi) - movl C(sourcetstep),%eax - - addl %eax,%esi - movl C(surfrowbytes),%eax - - addl %eax,%edi - movl C(lightrightstep),%eax - - addl %eax,%edx - movl C(lightdeltastep),%eax - - addl %eax,%ebp - movl %edx,C(lightright) - - jc Lblockloop8_mip2 - -// if (pbasesource >= r_sourcemax) -// pbasesource -= stepback; - - cmpl C(r_sourcemax),%esi - jb LSkip_mip2 - subl C(r_stepback),%esi -LSkip_mip2: - - movl C(r_lightptr),%ebx - decl sb_v - - jnz Lv_loop_mip2 - - popl %ebx // restore register variables - popl %esi - popl %edi - popl %ebp // restore the caller's stack frame - ret - - -//---------------------------------------------------------------------- -// Surface block drawer for mip level 3 -//---------------------------------------------------------------------- - - .align 4 -.globl C(R_DrawSurfaceBlock8_mip3) -C(R_DrawSurfaceBlock8_mip3): - pushl %ebp // preserve caller's stack frame - pushl %edi - pushl %esi // preserve register variables - pushl %ebx - -// for (v=0 ; v> blockdivshift; -// lightrightstep = (lightptr[1] - lightright) >> blockdivshift; -// lightdeltastep = ((lightleftstep - lightrightstep) & 0xFFFFF) | -// 0xF0000000; - movl 4(%ebx),%ecx // lightptr[1] - movl (%ebx),%ebx // lightptr[0] - - subl %eax,%ebx - subl %edx,%ecx - - sarl $1,%ecx - - sarl $1,%ebx - movl %ecx,C(lightrightstep) - - subl %ecx,%ebx - andl $0xFFFFF,%ebx - - sarl $1,%ebp - orl $0xF0000000,%ebx - - movl %ebx,C(lightdeltastep) - subl %ebx,%ebx // high word must be 0 in loop for addressing - - movb 1(%esi),%bl - subl %ecx,%ecx // high word must be 0 in loop for addressing - - movb %dh,%bh - movb (%esi),%cl - - addl %ebp,%edx - movb %dh,%ch - - movb 0x12345678(%ebx),%al -LBPatch16: - movl C(lightright),%edx - - movb %al,1(%edi) - movb 0x12345678(%ecx),%al -LBPatch17: - - movb %al,(%edi) - movl C(sourcetstep),%eax - - addl %eax,%esi - movl C(surfrowbytes),%eax - - addl %eax,%edi - movl C(lightdeltastep),%eax - - movl C(lightdelta),%ebp - movb (%esi),%cl - - addl %eax,%ebp - movl C(lightrightstep),%eax - - sarl $1,%ebp - addl %eax,%edx - - movb %dh,%bh - movb 1(%esi),%bl - - addl %ebp,%edx - movb %dh,%ch - - movb 0x12345678(%ebx),%al -LBPatch30: - movl C(sourcetstep),%edx - - movb %al,1(%edi) - movb 0x12345678(%ecx),%al -LBPatch31: - - movb %al,(%edi) - movl C(surfrowbytes),%ebp - - addl %edx,%esi - addl %ebp,%edi - -// if (pbasesource >= r_sourcemax) -// pbasesource -= stepback; - - cmpl C(r_sourcemax),%esi - jb LSkip_mip3 - subl C(r_stepback),%esi -LSkip_mip3: - - movl C(r_lightptr),%ebx - decl sb_v - - jnz Lv_loop_mip3 - - popl %ebx // restore register variables - popl %esi - popl %edi - popl %ebp // restore the caller's stack frame - ret - - -.globl C(R_Surf8End) -C(R_Surf8End): - -//---------------------------------------------------------------------- -// Code patching routines -//---------------------------------------------------------------------- - .data - - .align 4 -LPatchTable8: - .long LBPatch0-4 - .long LBPatch1-4 - .long LBPatch2-4 - .long LBPatch3-4 - .long LBPatch4-4 - .long LBPatch5-4 - .long LBPatch6-4 - .long LBPatch7-4 - .long LBPatch8-4 - .long LBPatch9-4 - .long LBPatch10-4 - .long LBPatch11-4 - .long LBPatch12-4 - .long LBPatch13-4 - .long LBPatch14-4 - .long LBPatch15-4 - .long LBPatch16-4 - .long LBPatch17-4 - .long LBPatch18-4 - .long LBPatch19-4 - .long LBPatch20-4 - .long LBPatch21-4 - .long LBPatch22-4 - .long LBPatch23-4 - .long LBPatch24-4 - .long LBPatch25-4 - .long LBPatch26-4 - .long LBPatch27-4 - .long LBPatch28-4 - .long LBPatch29-4 - .long LBPatch30-4 - .long LBPatch31-4 - - .text - - .align 4 -.globl C(R_Surf8Patch) -C(R_Surf8Patch): - pushl %ebx - - movl C(colormap),%eax - movl $LPatchTable8,%ebx - movl $32,%ecx -LPatchLoop8: - movl (%ebx),%edx - addl $4,%ebx - movl %eax,(%edx) - decl %ecx - jnz LPatchLoop8 - - popl %ebx - - ret - -#endif // id386 +/* + surf8.S + + (description) + + Copyright (C) 1996-1997 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id: surf8.s,v 1.1.1.4 2004/10/18 17:44:39 vvd0 Exp $ +*/ +// surf8.s +// x86 assembly-language 8 bpp surface block drawing code. + +#include "asm_i386.h" +#include "quakeasm.h" +#include "asm_draw.h" + +#ifdef id386 + + .data + +sb_v: .long 0 + + .text + + .align 4 +.globl C(R_Surf8Start) +C(R_Surf8Start): + +//---------------------------------------------------------------------- +// Surface block drawer for mip level 0 +//---------------------------------------------------------------------- + + .align 4 +.globl C(R_DrawSurfaceBlock8_mip0) +C(R_DrawSurfaceBlock8_mip0): + pushl %ebp // preserve caller's stack frame + pushl %edi + pushl %esi // preserve register variables + pushl %ebx + +// for (v=0 ; v> blockdivshift; +// lightrightstep = (lightptr[1] - lightright) >> blockdivshift; +// lightdeltastep = ((lightleftstep - lightrightstep) & 0xFFFFF) | +// 0xF0000000; + movl 4(%ebx),%ecx // lightptr[1] + movl (%ebx),%ebx // lightptr[0] + + subl %eax,%ebx + subl %edx,%ecx + + sarl $4,%ecx + orl $0xF0000000,%ebp + + sarl $4,%ebx + movl %ecx,C(lightrightstep) + + subl %ecx,%ebx + andl $0xFFFFF,%ebx + + orl $0xF0000000,%ebx + subl %ecx,%ecx // high word must be 0 in loop for addressing + + movl %ebx,C(lightdeltastep) + subl %ebx,%ebx // high word must be 0 in loop for addressing + +Lblockloop8_mip0: + movl %ebp,C(lightdelta) + movb 14(%esi),%cl + + sarl $4,%ebp + movb %dh,%bh + + movb 15(%esi),%bl + addl %ebp,%edx + + movb %dh,%ch + addl %ebp,%edx + + movb 0x12345678(%ebx),%ah +LBPatch0: + movb 13(%esi),%bl + + movb 0x12345678(%ecx),%al +LBPatch1: + movb 12(%esi),%cl + + movb %dh,%bh + addl %ebp,%edx + + rorl $16,%eax + movb %dh,%ch + + addl %ebp,%edx + movb 0x12345678(%ebx),%ah +LBPatch2: + + movb 11(%esi),%bl + movb 0x12345678(%ecx),%al +LBPatch3: + + movb 10(%esi),%cl + movl %eax,12(%edi) + + movb %dh,%bh + addl %ebp,%edx + + movb %dh,%ch + addl %ebp,%edx + + movb 0x12345678(%ebx),%ah +LBPatch4: + movb 9(%esi),%bl + + movb 0x12345678(%ecx),%al +LBPatch5: + movb 8(%esi),%cl + + movb %dh,%bh + addl %ebp,%edx + + rorl $16,%eax + movb %dh,%ch + + addl %ebp,%edx + movb 0x12345678(%ebx),%ah +LBPatch6: + + movb 7(%esi),%bl + movb 0x12345678(%ecx),%al +LBPatch7: + + movb 6(%esi),%cl + movl %eax,8(%edi) + + movb %dh,%bh + addl %ebp,%edx + + movb %dh,%ch + addl %ebp,%edx + + movb 0x12345678(%ebx),%ah +LBPatch8: + movb 5(%esi),%bl + + movb 0x12345678(%ecx),%al +LBPatch9: + movb 4(%esi),%cl + + movb %dh,%bh + addl %ebp,%edx + + rorl $16,%eax + movb %dh,%ch + + addl %ebp,%edx + movb 0x12345678(%ebx),%ah +LBPatch10: + + movb 3(%esi),%bl + movb 0x12345678(%ecx),%al +LBPatch11: + + movb 2(%esi),%cl + movl %eax,4(%edi) + + movb %dh,%bh + addl %ebp,%edx + + movb %dh,%ch + addl %ebp,%edx + + movb 0x12345678(%ebx),%ah +LBPatch12: + movb 1(%esi),%bl + + movb 0x12345678(%ecx),%al +LBPatch13: + movb (%esi),%cl + + movb %dh,%bh + addl %ebp,%edx + + rorl $16,%eax + movb %dh,%ch + + movb 0x12345678(%ebx),%ah +LBPatch14: + movl C(lightright),%edx + + movb 0x12345678(%ecx),%al +LBPatch15: + movl C(lightdelta),%ebp + + movl %eax,(%edi) + + addl C(sourcetstep),%esi + addl C(surfrowbytes),%edi + + addl C(lightrightstep),%edx + addl C(lightdeltastep),%ebp + + movl %edx,C(lightright) + jc Lblockloop8_mip0 + +// if (pbasesource >= r_sourcemax) +// pbasesource -= stepback; + + cmpl C(r_sourcemax),%esi + jb LSkip_mip0 + subl C(r_stepback),%esi +LSkip_mip0: + + movl C(r_lightptr),%ebx + decl sb_v + + jnz Lv_loop_mip0 + + popl %ebx // restore register variables + popl %esi + popl %edi + popl %ebp // restore the caller's stack frame + ret + + +//---------------------------------------------------------------------- +// Surface block drawer for mip level 1 +//---------------------------------------------------------------------- + + .align 4 +.globl C(R_DrawSurfaceBlock8_mip1) +C(R_DrawSurfaceBlock8_mip1): + pushl %ebp // preserve caller's stack frame + pushl %edi + pushl %esi // preserve register variables + pushl %ebx + +// for (v=0 ; v> blockdivshift; +// lightrightstep = (lightptr[1] - lightright) >> blockdivshift; +// lightdeltastep = ((lightleftstep - lightrightstep) & 0xFFFFF) | +// 0xF0000000; + movl 4(%ebx),%ecx // lightptr[1] + movl (%ebx),%ebx // lightptr[0] + + subl %eax,%ebx + subl %edx,%ecx + + sarl $3,%ecx + orl $0x70000000,%ebp + + sarl $3,%ebx + movl %ecx,C(lightrightstep) + + subl %ecx,%ebx + andl $0xFFFFF,%ebx + + orl $0xF0000000,%ebx + subl %ecx,%ecx // high word must be 0 in loop for addressing + + movl %ebx,C(lightdeltastep) + subl %ebx,%ebx // high word must be 0 in loop for addressing + +Lblockloop8_mip1: + movl %ebp,C(lightdelta) + movb 6(%esi),%cl + + sarl $3,%ebp + movb %dh,%bh + + movb 7(%esi),%bl + addl %ebp,%edx + + movb %dh,%ch + addl %ebp,%edx + + movb 0x12345678(%ebx),%ah +LBPatch22: + movb 5(%esi),%bl + + movb 0x12345678(%ecx),%al +LBPatch23: + movb 4(%esi),%cl + + movb %dh,%bh + addl %ebp,%edx + + rorl $16,%eax + movb %dh,%ch + + addl %ebp,%edx + movb 0x12345678(%ebx),%ah +LBPatch24: + + movb 3(%esi),%bl + movb 0x12345678(%ecx),%al +LBPatch25: + + movb 2(%esi),%cl + movl %eax,4(%edi) + + movb %dh,%bh + addl %ebp,%edx + + movb %dh,%ch + addl %ebp,%edx + + movb 0x12345678(%ebx),%ah +LBPatch26: + movb 1(%esi),%bl + + movb 0x12345678(%ecx),%al +LBPatch27: + movb (%esi),%cl + + movb %dh,%bh + addl %ebp,%edx + + rorl $16,%eax + movb %dh,%ch + + movb 0x12345678(%ebx),%ah +LBPatch28: + movl C(lightright),%edx + + movb 0x12345678(%ecx),%al +LBPatch29: + movl C(lightdelta),%ebp + + movl %eax,(%edi) + movl C(sourcetstep),%eax + + addl %eax,%esi + movl C(surfrowbytes),%eax + + addl %eax,%edi + movl C(lightrightstep),%eax + + addl %eax,%edx + movl C(lightdeltastep),%eax + + addl %eax,%ebp + movl %edx,C(lightright) + + jc Lblockloop8_mip1 + +// if (pbasesource >= r_sourcemax) +// pbasesource -= stepback; + + cmpl C(r_sourcemax),%esi + jb LSkip_mip1 + subl C(r_stepback),%esi +LSkip_mip1: + + movl C(r_lightptr),%ebx + decl sb_v + + jnz Lv_loop_mip1 + + popl %ebx // restore register variables + popl %esi + popl %edi + popl %ebp // restore the caller's stack frame + ret + + +//---------------------------------------------------------------------- +// Surface block drawer for mip level 2 +//---------------------------------------------------------------------- + + .align 4 +.globl C(R_DrawSurfaceBlock8_mip2) +C(R_DrawSurfaceBlock8_mip2): + pushl %ebp // preserve caller's stack frame + pushl %edi + pushl %esi // preserve register variables + pushl %ebx + +// for (v=0 ; v> blockdivshift; +// lightrightstep = (lightptr[1] - lightright) >> blockdivshift; +// lightdeltastep = ((lightleftstep - lightrightstep) & 0xFFFFF) | +// 0xF0000000; + movl 4(%ebx),%ecx // lightptr[1] + movl (%ebx),%ebx // lightptr[0] + + subl %eax,%ebx + subl %edx,%ecx + + sarl $2,%ecx + orl $0x30000000,%ebp + + sarl $2,%ebx + movl %ecx,C(lightrightstep) + + subl %ecx,%ebx + + andl $0xFFFFF,%ebx + + orl $0xF0000000,%ebx + subl %ecx,%ecx // high word must be 0 in loop for addressing + + movl %ebx,C(lightdeltastep) + subl %ebx,%ebx // high word must be 0 in loop for addressing + +Lblockloop8_mip2: + movl %ebp,C(lightdelta) + movb 2(%esi),%cl + + sarl $2,%ebp + movb %dh,%bh + + movb 3(%esi),%bl + addl %ebp,%edx + + movb %dh,%ch + addl %ebp,%edx + + movb 0x12345678(%ebx),%ah +LBPatch18: + movb 1(%esi),%bl + + movb 0x12345678(%ecx),%al +LBPatch19: + movb (%esi),%cl + + movb %dh,%bh + addl %ebp,%edx + + rorl $16,%eax + movb %dh,%ch + + movb 0x12345678(%ebx),%ah +LBPatch20: + movl C(lightright),%edx + + movb 0x12345678(%ecx),%al +LBPatch21: + movl C(lightdelta),%ebp + + movl %eax,(%edi) + movl C(sourcetstep),%eax + + addl %eax,%esi + movl C(surfrowbytes),%eax + + addl %eax,%edi + movl C(lightrightstep),%eax + + addl %eax,%edx + movl C(lightdeltastep),%eax + + addl %eax,%ebp + movl %edx,C(lightright) + + jc Lblockloop8_mip2 + +// if (pbasesource >= r_sourcemax) +// pbasesource -= stepback; + + cmpl C(r_sourcemax),%esi + jb LSkip_mip2 + subl C(r_stepback),%esi +LSkip_mip2: + + movl C(r_lightptr),%ebx + decl sb_v + + jnz Lv_loop_mip2 + + popl %ebx // restore register variables + popl %esi + popl %edi + popl %ebp // restore the caller's stack frame + ret + + +//---------------------------------------------------------------------- +// Surface block drawer for mip level 3 +//---------------------------------------------------------------------- + + .align 4 +.globl C(R_DrawSurfaceBlock8_mip3) +C(R_DrawSurfaceBlock8_mip3): + pushl %ebp // preserve caller's stack frame + pushl %edi + pushl %esi // preserve register variables + pushl %ebx + +// for (v=0 ; v> blockdivshift; +// lightrightstep = (lightptr[1] - lightright) >> blockdivshift; +// lightdeltastep = ((lightleftstep - lightrightstep) & 0xFFFFF) | +// 0xF0000000; + movl 4(%ebx),%ecx // lightptr[1] + movl (%ebx),%ebx // lightptr[0] + + subl %eax,%ebx + subl %edx,%ecx + + sarl $1,%ecx + + sarl $1,%ebx + movl %ecx,C(lightrightstep) + + subl %ecx,%ebx + andl $0xFFFFF,%ebx + + sarl $1,%ebp + orl $0xF0000000,%ebx + + movl %ebx,C(lightdeltastep) + subl %ebx,%ebx // high word must be 0 in loop for addressing + + movb 1(%esi),%bl + subl %ecx,%ecx // high word must be 0 in loop for addressing + + movb %dh,%bh + movb (%esi),%cl + + addl %ebp,%edx + movb %dh,%ch + + movb 0x12345678(%ebx),%al +LBPatch16: + movl C(lightright),%edx + + movb %al,1(%edi) + movb 0x12345678(%ecx),%al +LBPatch17: + + movb %al,(%edi) + movl C(sourcetstep),%eax + + addl %eax,%esi + movl C(surfrowbytes),%eax + + addl %eax,%edi + movl C(lightdeltastep),%eax + + movl C(lightdelta),%ebp + movb (%esi),%cl + + addl %eax,%ebp + movl C(lightrightstep),%eax + + sarl $1,%ebp + addl %eax,%edx + + movb %dh,%bh + movb 1(%esi),%bl + + addl %ebp,%edx + movb %dh,%ch + + movb 0x12345678(%ebx),%al +LBPatch30: + movl C(sourcetstep),%edx + + movb %al,1(%edi) + movb 0x12345678(%ecx),%al +LBPatch31: + + movb %al,(%edi) + movl C(surfrowbytes),%ebp + + addl %edx,%esi + addl %ebp,%edi + +// if (pbasesource >= r_sourcemax) +// pbasesource -= stepback; + + cmpl C(r_sourcemax),%esi + jb LSkip_mip3 + subl C(r_stepback),%esi +LSkip_mip3: + + movl C(r_lightptr),%ebx + decl sb_v + + jnz Lv_loop_mip3 + + popl %ebx // restore register variables + popl %esi + popl %edi + popl %ebp // restore the caller's stack frame + ret + + +.globl C(R_Surf8End) +C(R_Surf8End): + +//---------------------------------------------------------------------- +// Code patching routines +//---------------------------------------------------------------------- + .data + + .align 4 +LPatchTable8: + .long LBPatch0-4 + .long LBPatch1-4 + .long LBPatch2-4 + .long LBPatch3-4 + .long LBPatch4-4 + .long LBPatch5-4 + .long LBPatch6-4 + .long LBPatch7-4 + .long LBPatch8-4 + .long LBPatch9-4 + .long LBPatch10-4 + .long LBPatch11-4 + .long LBPatch12-4 + .long LBPatch13-4 + .long LBPatch14-4 + .long LBPatch15-4 + .long LBPatch16-4 + .long LBPatch17-4 + .long LBPatch18-4 + .long LBPatch19-4 + .long LBPatch20-4 + .long LBPatch21-4 + .long LBPatch22-4 + .long LBPatch23-4 + .long LBPatch24-4 + .long LBPatch25-4 + .long LBPatch26-4 + .long LBPatch27-4 + .long LBPatch28-4 + .long LBPatch29-4 + .long LBPatch30-4 + .long LBPatch31-4 + + .text + + .align 4 +.globl C(R_Surf8Patch) +C(R_Surf8Patch): + pushl %ebx + + movl C(colormap),%eax + movl $LPatchTable8,%ebx + movl $32,%ecx +LPatchLoop8: + movl (%ebx),%edx + addl $4,%ebx + movl %eax,(%edx) + decl %ecx + jnz LPatchLoop8 + + popl %ebx + + ret + +#endif // id386 diff --git a/source/sv_ccmds.c b/source/sv_ccmds.c index 1b4a1574..6434a6aa 100644 --- a/source/sv_ccmds.c +++ b/source/sv_ccmds.c @@ -1,941 +1,1037 @@ -/* -Copyright (C) 1996-1997 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 "qwsvdef.h" - -qboolean sv_allow_cheats; - -int fp_messages=4, fp_persecond=4, fp_secondsdead=10; -char fp_msg[255] = { 0 }; -extern cvar_t cl_warncmd; - extern redirect_t sv_redirected; - - -/* -=============================================================================== - -OPERATOR CONSOLE ONLY COMMANDS - -These commands can only be entered from stdin or by a remote operator datagram -=============================================================================== -*/ - -/* -==================== -SV_SetMaster_f - -Make a master server current -==================== -*/ -void SV_SetMaster_f (void) -{ - char data[2]; - int i; - - memset (&master_adr, 0, sizeof(master_adr)); - - for (i=1 ; istate) - continue; - if (cl->userid == idnum) - { - host_client = cl; - sv_player = host_client->edict; - return true; - } - } - Con_Printf ("Userid %i is not on the server\n", idnum); - return false; -} - - -/* -================== -SV_God_f - -Sets client to godmode -================== -*/ -void SV_God_f (void) -{ - if (!sv_allow_cheats) - { - Con_Printf ("You must run the server with -cheats to enable this command.\n"); - return; - } - - if (!SV_SetPlayer ()) - return; - - sv_player->v.flags = (int)sv_player->v.flags ^ FL_GODMODE; - if (!((int)sv_player->v.flags & FL_GODMODE) ) - SV_ClientPrintf (host_client, PRINT_HIGH, "godmode OFF\n"); - else - SV_ClientPrintf (host_client, PRINT_HIGH, "godmode ON\n"); -} - - -void SV_Noclip_f (void) -{ - if (!sv_allow_cheats) - { - Con_Printf ("You must run the server with -cheats to enable this command.\n"); - return; - } - - if (!SV_SetPlayer ()) - return; - - if (sv_player->v.movetype != MOVETYPE_NOCLIP) - { - sv_player->v.movetype = MOVETYPE_NOCLIP; - SV_ClientPrintf (host_client, PRINT_HIGH, "noclip ON\n"); - } - else - { - sv_player->v.movetype = MOVETYPE_WALK; - SV_ClientPrintf (host_client, PRINT_HIGH, "noclip OFF\n"); - } -} - - -/* -================== -SV_Give_f -================== -*/ -void SV_Give_f (void) -{ - char *t; - int v; - - if (!sv_allow_cheats) - { - Con_Printf ("You must run the server with -cheats to enable this command.\n"); - return; - } - - if (!SV_SetPlayer ()) - return; - - t = Cmd_Argv(2); - v = atoi (Cmd_Argv(3)); - - switch (t[0]) - { - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - sv_player->v.items = (int)sv_player->v.items | IT_SHOTGUN<< (t[0] - '2'); - break; - - case 's': - sv_player->v.ammo_shells = v; - break; - case 'n': - sv_player->v.ammo_nails = v; - break; - case 'r': - sv_player->v.ammo_rockets = v; - break; - case 'h': - sv_player->v.health = v; - break; - case 'c': - sv_player->v.ammo_cells = v; - break; - } -} - - -void CL_Disconnect (); -int UDP_OpenSocket (int, qboolean); -void Host_ConnectLocal (); - -/* -====================== -SV_Map_f - -handle a -map -command from the console or progs. -====================== -*/ -void SV_Map_f (void) -{ - char level[MAX_QPATH]; - char expanded[MAX_QPATH]; - FILE *f; - - if (Cmd_Argc() != 2) - { - Con_Printf ("map : continue game on a new level\n"); - return; - } - strcpy (level, Cmd_Argv(1)); - - // check to make sure the level exists - sprintf (expanded, "maps/%s.bsp", level); - COM_FOpenFile (expanded, &f); - if (!f) - { - Con_Printf ("Can't find %s\n", expanded); - return; - } - fclose (f); - - if (sv.demorecording) - SV_Stop_f(); - - SV_BroadcastCommand ("changing\n"); - SV_SendMessagesToAll (); - - SV_SpawnServer (level); - - SV_BroadcastCommand ("reconnect\n"); -} - - -/* -================== -SV_Kick_f - -Kick a user off of the server -================== -*/ -void SV_Kick_f (void) -{ - int i, j; - client_t *cl; - int uid; - int c; - int saved_state; - char reason[80] = ""; - - c = Cmd_Argc (); - if (c < 2) { - Con_Printf ("kick [reason]\n"); - return; - } - - uid = atoi(Cmd_Argv(1)); - - for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) - { - if (!cl->state) - continue; - if (cl->userid == uid) - { - if (c > 2) { - strcpy (reason, " ("); - for (j=2 ; jstate; - cl->state = cs_free; // HACK: don't broadcast to this client - SV_BroadcastPrintf (PRINT_HIGH, "%s was kicked%s\n", cl->name, reason); - cl->state = saved_state; - SV_ClientPrintf (cl, PRINT_HIGH, "You were kicked from the game%s\n", reason); - SV_DropClient (cl); - return; - } - } - - Con_Printf ("Couldn't find user number %i\n", uid); -} - - -/* -================ -SV_Status_f -================ -*/ -void SV_Status_f (void) -{ - int i, j, l; - client_t *cl; - float cpu, avg, pak, demo = 0; - char *s; - - cpu = (svs.stats.latched_active+svs.stats.latched_idle); - - if (cpu) - { - demo = 100*svs.stats.latched_demo/cpu; - cpu = 100*svs.stats.latched_active/cpu; - } - - avg = 1000*svs.stats.latched_active / STATFRAMES; - pak = (float)svs.stats.latched_packets/ STATFRAMES; - - Con_Printf ("net address : %s\n",NET_AdrToString (net_local_adr)); - Con_Printf ("cpu utilization (overall) : %3i%%\n",(int)cpu); - Con_Printf ("cpu utilization (recording) : %3i%%\n", (int)demo); - Con_Printf ("avg response time : %i ms\n",(int)avg); - Con_Printf ("packets/frame : %5.2f (%d)\n", pak, num_prstr); - -// min fps lat drp - if (sv_redirected != RD_NONE) { - // most remote clients are 40 columns - // 0123456789012345678901234567890123456789 - Con_Printf ("name userid frags\n"); - Con_Printf (" address rate ping drop\n"); - Con_Printf (" ---------------- ---- ---- -----\n"); - for (i=0,cl=svs.clients ; istate) - continue; - - Con_Printf ("%-16.16s ", cl->name); - - Con_Printf ("%6i %5i", cl->userid, (int)cl->edict->v.frags); - if (cl->spectator) - Con_Printf(" (s)\n"); - else - Con_Printf("\n"); - - s = NET_BaseAdrToString ( cl->netchan.remote_address); - Con_Printf (" %-16.16s", s); - if (cl->state == cs_connected) - { - Con_Printf ("CONNECTING\n"); - continue; - } - if (cl->state == cs_zombie) - { - Con_Printf ("ZOMBIE\n"); - continue; - } - Con_Printf ("%4i %4i %5.2f\n" - , (int)(1000*cl->netchan.frame_rate) - , (int)SV_CalcPing (cl) - , 100.0*cl->netchan.drop_count / cl->netchan.incoming_sequence); - } - } else { - Con_Printf ("frags userid address name rate ping drop qport\n"); - Con_Printf ("----- ------ --------------- --------------- ---- ---- ----- -----\n"); - for (i=0,cl=svs.clients ; istate) - continue; - Con_Printf ("%5i %6i ", (int)cl->edict->v.frags, cl->userid); - - s = NET_BaseAdrToString ( cl->netchan.remote_address); - Con_Printf ("%s", s); - l = 16 - strlen(s); - for (j=0 ; jname); - l = 16 - strlen(cl->name); - for (j=0 ; jstate == cs_connected) - { - Con_Printf ("CONNECTING\n"); - continue; - } - if (cl->state == cs_zombie) - { - Con_Printf ("ZOMBIE\n"); - continue; - } - Con_Printf ("%4i %4i %3.1f %4i" - , (int)(1000*cl->netchan.frame_rate) - , (int)SV_CalcPing (cl) - , 100.0*cl->netchan.drop_count / cl->netchan.incoming_sequence - , cl->netchan.qport); - if (cl->spectator) - Con_Printf(" (s)\n"); - else - Con_Printf("\n"); - - - } - } - Con_Printf ("\n"); -} - -/* -================== -SV_ConSay_f -================== -*/ -void SV_ConSay_f(void) -{ - client_t *client; - int j; - char *p; - char text[1024]; - - if (Cmd_Argc () < 2) - return; - - strcpy (text, "console: "); - p = Cmd_Args(); - - if (*p == '"') - { - p++; - p[strlen(p)-1] = 0; - } - - strcat(text, p); - - for (j = 0, client = svs.clients; j < MAX_CLIENTS; j++, client++) - { - if (client->state != cs_spawned) - continue; - SV_ClientPrintf2(client, PRINT_CHAT, "%s\n", text); - } - - if (sv.demorecording) { - DemoReliableWrite_Begin (dem_all, 0, strlen(text)+3); - MSG_WriteByte (demo.buf, svc_print); - MSG_WriteByte (demo.buf, PRINT_CHAT); - MSG_WriteString (demo.buf, text); - } - -} - - -/* -================== -SV_Heartbeat_f -================== -*/ -void SV_Heartbeat_f (void) -{ - svs.last_heartbeat = -9999; -} - -void SV_SendServerInfoChange(char *key, char *value) -{ - if (!sv.state) - return; - - MSG_WriteByte (&sv.reliable_datagram, svc_serverinfo); - MSG_WriteString (&sv.reliable_datagram, key); - MSG_WriteString (&sv.reliable_datagram, value); -} - -/* -=========== -SV_Serverinfo_f - - Examine or change the serverinfo string -=========== -*/ -char *CopyString(char *s); -void SV_Serverinfo_f (void) -{ - cvar_t *var; - - if (Cmd_Argc() == 1) - { - Con_Printf ("Server info settings:\n"); - Info_Print (svs.info); - return; - } - - if (Cmd_Argc() != 3) - { - Con_Printf ("usage: serverinfo [ ]\n"); - return; - } - - if (Cmd_Argv(1)[0] == '*') - { - Con_Printf ("Star variables cannot be changed.\n"); - return; - } - Info_SetValueForKey (svs.info, Cmd_Argv(1), Cmd_Argv(2), MAX_SERVERINFO_STRING); - - // if this is a cvar, change it too - var = Cvar_FindVar (Cmd_Argv(1)); - if (var) - { - Z_Free (var->string); // free the old value string - var->string = CopyString (Cmd_Argv(2)); - var->value = Q_atof (var->string); - } - - SV_SendServerInfoChange(Cmd_Argv(1), Cmd_Argv(2)); -} - - -/* -=========== -SV_Serverinfo_f - - Examine or change the serverinfo string -=========== -*/ -char *CopyString(char *s); -void SV_Localinfo_f (void) -{ - if (Cmd_Argc() == 1) - { - Con_Printf ("Local info settings:\n"); - Info_Print (localinfo); - return; - } - - if (Cmd_Argc() != 3) - { - Con_Printf ("usage: localinfo [ ]\n"); - return; - } - - if (Cmd_Argv(1)[0] == '*') - { - Con_Printf ("Star variables cannot be changed.\n"); - return; - } - Info_SetValueForKey (localinfo, Cmd_Argv(1), Cmd_Argv(2), MAX_LOCALINFO_STRING); -} - - -/* -=========== -SV_User_f - -Examine a users info strings -=========== -*/ -void SV_User_f (void) -{ - if (Cmd_Argc() != 2) - { - Con_Printf ("Usage: info \n"); - return; - } - - if (!SV_SetPlayer ()) - return; - - Info_Print (host_client->userinfo); -} - -/* -================ -SV_Gamedir - -Sets the fake *gamedir to a different directory. -================ -*/ -void SV_Gamedir (void) -{ - char *dir; - - if (Cmd_Argc() == 1) - { - Con_Printf ("Current *gamedir: %s\n", Info_ValueForKey (svs.info, "*gamedir")); - return; - } - - if (Cmd_Argc() != 2) - { - Con_Printf ("Usage: sv_gamedir \n"); - return; - } - - dir = Cmd_Argv(1); - - if (strstr(dir, "..") || strstr(dir, "/") - || strstr(dir, "\\") || strstr(dir, ":") ) - { - Con_Printf ("*Gamedir should be a single filename, not a path\n"); - return; - } - - Info_SetValueForStarKey (svs.info, "*gamedir", dir, MAX_SERVERINFO_STRING); -} - -/* -================ -SV_Floodport_f - -Sets the gamedir and path to a different directory. -================ -*/ - -void SV_Floodprot_f (void) -{ - int arg1, arg2, arg3; - - if (Cmd_Argc() == 1) - { - if (fp_messages) { - Con_Printf ("Current floodprot settings: \nAfter %d msgs per %d seconds, silence for %d seconds\n", - fp_messages, fp_persecond, fp_secondsdead); - return; - } else - Con_Printf ("No floodprots enabled.\n"); - } - - if (Cmd_Argc() != 4) - { - Con_Printf ("Usage: floodprot <# of messages> \n"); - Con_Printf ("Use floodprotmsg to set a custom message to say to the flooder.\n"); - return; - } - - arg1 = atoi(Cmd_Argv(1)); - arg2 = atoi(Cmd_Argv(2)); - arg3 = atoi(Cmd_Argv(3)); - - if (arg1<=0 || arg2 <= 0 || arg3<=0) { - Con_Printf ("All values must be positive numbers\n"); - return; - } - - if (arg1 > 10) { - Con_Printf ("Can only track up to 10 messages.\n"); - return; - } - - fp_messages = arg1; - fp_persecond = arg2; - fp_secondsdead = arg3; -} - -void SV_Floodprotmsg_f (void) -{ - if (Cmd_Argc() == 1) { - Con_Printf("Current msg: %s\n", fp_msg); - return; - } else if (Cmd_Argc() != 2) { - Con_Printf("Usage: floodprotmsg \"\"\n"); - return; - } - sprintf(fp_msg, "%s", Cmd_Argv(1)); -} - -/* -================ -SV_Gamedir_f - -Sets the gamedir and path to a different directory. -================ -*/ -char gamedirfile[MAX_OSPATH]; -void SV_Gamedir_f (void) -{ - char *dir; - - if (Cmd_Argc() == 1) - { - Con_Printf ("Current gamedir: %s\n", com_gamedir); - return; - } - - if (Cmd_Argc() != 2) - { - Con_Printf ("Usage: gamedir \n"); - return; - } - - dir = Cmd_Argv(1); - - if (strstr(dir, "..") || strstr(dir, "/") - || strstr(dir, "\\") || strstr(dir, ":") ) - { - Con_Printf ("Gamedir should be a single filename, not a path\n"); - return; - } - - COM_Gamedir (dir); - Info_SetValueForStarKey (svs.info, "*gamedir", dir, MAX_SERVERINFO_STRING); -} - -/* -================ -SV_Snap -================ -*/ -void SV_Snap (int uid) -{ - client_t *cl; - char pcxname[80]; - char checkname[MAX_OSPATH]; - int i; - - for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) - { - if (!cl->state) - continue; - if (cl->userid == uid) - break; - } - if (i >= MAX_CLIENTS) { - Con_Printf ("userid not found\n"); - return; - } - - sprintf(pcxname, "%d-00.pcx", uid); - - sprintf(checkname, "%s/snap", gamedirfile); - Sys_mkdir(gamedirfile); - Sys_mkdir(checkname); - - for (i=0 ; i<=99 ; i++) - { - pcxname[strlen(pcxname) - 6] = i/10 + '0'; - pcxname[strlen(pcxname) - 5] = i%10 + '0'; - sprintf (checkname, "%s/snap/%s", gamedirfile, pcxname); - if (Sys_FileTime(checkname) == -1) - break; // file doesn't exist - } - if (i==100) - { - Con_Printf ("Snap: Couldn't create a file, clean some out.\n"); - return; - } - strcpy(cl->uploadfn, checkname); - - memcpy(&cl->snap_from, &net_from, sizeof(net_from)); - if (sv_redirected != RD_NONE) - cl->remote_snap = true; - else - cl->remote_snap = false; - - ClientReliableWrite_Begin (cl, svc_stufftext, 24); - ClientReliableWrite_String (cl, "cmd snap\n"); - Con_Printf ("Requesting snap from user %d...\n", uid); -} - -/* -================ -SV_Snap_f -================ -*/ -void SV_Snap_f (void) -{ - int uid; - - if (Cmd_Argc() != 2) - { - Con_Printf ("Usage: snap \n"); - return; - } - - uid = atoi(Cmd_Argv(1)); - - SV_Snap(uid); -} - -/* -================ -SV_Snap -================ -*/ -void SV_SnapAll_f (void) -{ - client_t *cl; - int i; - - for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) - { - if (cl->state < cs_connected || cl->spectator) - continue; - SV_Snap(cl->userid); - } -} - -/* -================== -SV_InitOperatorCommands -================== -*/ -void SV_InitOperatorCommands (void) -{ - if (COM_CheckParm ("-cheats")) - { - sv_allow_cheats = true; - Info_SetValueForStarKey (svs.info, "*cheats", "ON", MAX_SERVERINFO_STRING); - } - - Cmd_AddCommand ("logfile", SV_Logfile_f); - Cmd_AddCommand ("fraglogfile", SV_Fraglogfile_f); - - Cmd_AddCommand ("snap", SV_Snap_f); - Cmd_AddCommand ("snapall", SV_SnapAll_f); - Cmd_AddCommand ("kick", SV_Kick_f); - Cmd_AddCommand ("status", SV_Status_f); - - Cmd_AddCommand ("map", SV_Map_f); - Cmd_AddCommand ("setmaster", SV_SetMaster_f); - - Cmd_AddCommand ("heartbeat", SV_Heartbeat_f); - - Cmd_AddCommand ("say", SV_ConSay_f); - Cmd_AddCommand ("quit", SV_Quit_f); - - Cmd_AddCommand ("god", SV_God_f); - Cmd_AddCommand ("give", SV_Give_f); - Cmd_AddCommand ("noclip", SV_Noclip_f); - Cmd_AddCommand ("localinfo", SV_Localinfo_f); - - Cmd_AddCommand ("serverinfo", SV_Serverinfo_f); - Cmd_AddCommand ("user", SV_User_f); - - Cmd_AddCommand ("gamedir", SV_Gamedir_f); - Cmd_AddCommand ("sv_gamedir", SV_Gamedir); - Cmd_AddCommand ("floodprot", SV_Floodprot_f); - Cmd_AddCommand ("floodprotmsg", SV_Floodprotmsg_f); - - - - cl_warncmd.value = 1; -} +/* +Copyright (C) 1996-1997 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 "qwsvdef.h" + +qboolean sv_allow_cheats; + +int fp_messages=4, fp_persecond=4, fp_secondsdead=10; +char fp_msg[255] = { 0 }; +extern cvar_t cl_warncmd; + extern redirect_t sv_redirected; + + +/* +=============================================================================== + +OPERATOR CONSOLE ONLY COMMANDS + +These commands can only be entered from stdin or by a remote operator datagram +=============================================================================== +*/ + +/* +==================== +SV_SetMaster_f + +Make a master server current +==================== +*/ +void SV_SetMaster_f (void) +{ + char data[2]; + int i; + + memset (&master_adr, 0, sizeof(master_adr)); + + for (i=1 ; istate) + continue; + if (cl->userid == idnum) + { + host_client = cl; + sv_player = host_client->edict; + return true; + } + } + Con_Printf ("Userid %i is not on the server\n", idnum); + return false; +} + + +/* +================== +SV_God_f + +Sets client to godmode +================== +*/ +void SV_God_f (void) +{ + if (!sv_allow_cheats) + { + Con_Printf ("You must run the server with -cheats to enable this command.\n"); + return; + } + + if (!SV_SetPlayer ()) + return; + + sv_player->v.flags = (int)sv_player->v.flags ^ FL_GODMODE; + if (!((int)sv_player->v.flags & FL_GODMODE) ) + SV_ClientPrintf (host_client, PRINT_HIGH, "godmode OFF\n"); + else + SV_ClientPrintf (host_client, PRINT_HIGH, "godmode ON\n"); +} + + +void SV_Noclip_f (void) +{ + if (!sv_allow_cheats) + { + Con_Printf ("You must run the server with -cheats to enable this command.\n"); + return; + } + + if (!SV_SetPlayer ()) + return; + + if (sv_player->v.movetype != MOVETYPE_NOCLIP) + { + sv_player->v.movetype = MOVETYPE_NOCLIP; + SV_ClientPrintf (host_client, PRINT_HIGH, "noclip ON\n"); + } + else + { + sv_player->v.movetype = MOVETYPE_WALK; + SV_ClientPrintf (host_client, PRINT_HIGH, "noclip OFF\n"); + } +} + + +/* +================== +SV_Give_f +================== +*/ +void SV_Give_f (void) +{ + char *t; + int v; + + if (!sv_allow_cheats) + { + Con_Printf ("You must run the server with -cheats to enable this command.\n"); + return; + } + + if (!SV_SetPlayer ()) + return; + + t = Cmd_Argv(2); + v = atoi (Cmd_Argv(3)); + + switch (t[0]) + { + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + sv_player->v.items = (int)sv_player->v.items | IT_SHOTGUN<< (t[0] - '2'); + break; + + case 's': + sv_player->v.ammo_shells = v; + break; + case 'n': + sv_player->v.ammo_nails = v; + break; + case 'r': + sv_player->v.ammo_rockets = v; + break; + case 'h': + sv_player->v.health = v; + break; + case 'c': + sv_player->v.ammo_cells = v; + break; + } +} + + +void CL_Disconnect (); +int UDP_OpenSocket (int, qboolean); +void Host_ConnectLocal (); + +/* +====================== +SV_Map_f + +handle a +map +command from the console or progs. +====================== +*/ +void SV_Map (qboolean now) +{ + static char level[MAX_QPATH]; + static char expanded[MAX_QPATH]; + static qboolean changed = false; + FILE *f; + + // if now, change it + if (now) { + if (!changed) + return; + + changed = false; + + // uh, is it possible ? + COM_FOpenFile (expanded, &f); + if (!f) + { + Sys_Printf ("Can't find %s\n", expanded); + return; + } + fclose (f); + + if (sv.demorecording) + SV_Stop_f(); + + SV_BroadcastCommand ("changing\n"); + SV_SendMessagesToAll (); + + SV_SpawnServer (level); + + SV_BroadcastCommand ("reconnect\n"); + + return; + } + + // get the map name, but don't change now, could be executed from progs.dat + + if (Cmd_Argc() != 2) + { + Con_Printf ("map : continue game on a new level\n"); + return; + } + + strcpy (level, Cmd_Argv(1)); + + // check to make sure the level exists + sprintf (expanded, "maps/%s.bsp", level); + + COM_FOpenFile (expanded, &f); + if (!f) + { + Con_Printf ("Can't find %s\n", expanded); + return; + } + fclose (f); + + changed = true; +} + +void SV_Map_f (void) +{ + + SV_Map(false); +} + + +/* +================== +SV_Kick_f + +Kick a user off of the server +================== +*/ +void SV_Kick_f (void) +{ + int i, j; + client_t *cl; + int uid; + int c; + int saved_state; + char reason[80] = ""; + + c = Cmd_Argc (); + if (c < 2) { + Con_Printf ("kick [reason]\n"); + return; + } + + uid = atoi(Cmd_Argv(1)); + + for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) + { + if (!cl->state) + continue; + if (cl->userid == uid) + { + if (c > 2) { + strcpy (reason, " ("); + for (j=2 ; jstate; + cl->state = cs_free; // HACK: don't broadcast to this client + SV_BroadcastPrintf (PRINT_HIGH, "%s was kicked%s\n", cl->name, reason); + cl->state = saved_state; + SV_ClientPrintf (cl, PRINT_HIGH, "You were kicked from the game%s\n", reason); + SV_DropClient (cl); + return; + } + } + + Con_Printf ("Couldn't find user number %i\n", uid); +} + + +/* +================ +SV_Status_f +================ +*/ +void SV_Status_f (void) +{ + int i, j, l; + client_t *cl; + float cpu, avg, pak, demo = 0; + char *s; + + cpu = (svs.stats.latched_active+svs.stats.latched_idle); + + if (cpu) + { + demo = 100*svs.stats.latched_demo/cpu; + cpu = 100*svs.stats.latched_active/cpu; + } + + avg = 1000*svs.stats.latched_active / STATFRAMES; + pak = (float)svs.stats.latched_packets/ STATFRAMES; + + Con_Printf ("net address : %s\n",NET_AdrToString (net_local_adr)); + Con_Printf ("cpu utilization (overall) : %3i%%\n",(int)cpu); + Con_Printf ("cpu utilization (recording) : %3i%%\n", (int)demo); + Con_Printf ("avg response time : %i ms\n",(int)avg); + Con_Printf ("packets/frame : %5.2f (%d)\n", pak, num_prstr); + +// min fps lat drp + if (sv_redirected != RD_NONE && sv_redirected != RD_MOD) { + // most remote clients are 40 columns + // 0123456789012345678901234567890123456789 + Con_Printf ("name userid frags\n"); + Con_Printf (" address rate ping drop\n"); + Con_Printf (" real ip\n"); + Con_Printf (" ---------------- ---- ---- -----\n"); + for (i=0,cl=svs.clients ; istate) + continue; + + Con_Printf ("%-16.16s ", cl->name); + + Con_Printf ("%6i %5i", cl->userid, (int)cl->edict->v.frags); + if (cl->spectator) + Con_Printf(" (s)\n"); + else + Con_Printf("\n"); + + s = NET_BaseAdrToString ( cl->netchan.remote_address); + Con_Printf (" %-16.16s", s); + if (cl->state == cs_connected || cl->state == cs_preconnected) + { + Con_Printf ("CONNECTING\n"); + continue; + } + if (cl->state == cs_zombie) + { + Con_Printf ("ZOMBIE\n"); + continue; + } + Con_Printf ("%4i %4i %5.2f\n" + , (int)(1000*cl->netchan.frame_rate) + , (int)SV_CalcPing (cl) + , 100.0*cl->netchan.drop_count / cl->netchan.incoming_sequence); + + if (cl->realip.ip[0]) + { + s = NET_BaseAdrToString ( cl->realip); + Con_Printf (" %-16.16s\n", s); + } + } + } else { + Con_Printf ("frags id address name rate ping drop real ip\n"); + Con_Printf ("----- --- --------------- --------------- ---- ---- ----- ---------------\n"); + for (i=0,cl=svs.clients ; istate) + continue; + Con_Printf ("%5i %3i ", (int)cl->edict->v.frags, cl->userid); + + s = NET_BaseAdrToString ( cl->netchan.remote_address); + Con_Printf ("%s", s); + l = 16 - strlen(s); + for (j=0 ; jname); + l = 16 - strlen(cl->name); + for (j=0 ; jstate == cs_connected || cl->state == cs_preconnected) + { + Con_Printf ("CONNECTING\n"); + continue; + } + if (cl->state == cs_zombie) + { + Con_Printf ("ZOMBIE\n"); + continue; + } + Con_Printf ("%4i %4i %3.1f" + , (int)(1000*cl->netchan.frame_rate) + , (int)SV_CalcPing (cl) + , 100.0*cl->netchan.drop_count / cl->netchan.incoming_sequence); + + if (cl->realip.ip[0]) + { + s = NET_BaseAdrToString ( cl->realip); + Con_Printf (" %-16.16s", s); + } + + if (cl->spectator) + Con_Printf(" (s)"); + + Con_Printf("\n"); + + + } + } + Con_Printf ("\n"); +} + +/* +================== +SV_ConSay_f +================== +*/ +void SV_ConSay_f(void) +{ + client_t *client; + int j; + char *p; + char text[1024]; + + if (Cmd_Argc () < 2) + return; + + strcpy (text, "console: "); + p = Cmd_Args(); + + if (*p == '"') + { + p++; + p[strlen(p)-1] = 0; + } + + strcat(text, p); + + for (j = 0, client = svs.clients; j < MAX_CLIENTS; j++, client++) + { + if (client->state != cs_spawned) + continue; + SV_ClientPrintf2(client, PRINT_CHAT, "%s\n", text); + } + + if (sv.demorecording) { + DemoWrite_Begin (dem_all, 0, strlen(text)+3); + MSG_WriteByte ((sizebuf_t*)demo.dbuf, svc_print); + MSG_WriteByte ((sizebuf_t*)demo.dbuf, PRINT_CHAT); + MSG_WriteString ((sizebuf_t*)demo.dbuf, text); + } + +} + + +/* +================== +SV_Heartbeat_f +================== +*/ +void SV_Heartbeat_f (void) +{ + svs.last_heartbeat = -9999; +} + +void SV_SendServerInfoChange(char *key, char *value) +{ + if (!sv.state) + return; + + MSG_WriteByte (&sv.reliable_datagram, svc_serverinfo); + MSG_WriteString (&sv.reliable_datagram, key); + MSG_WriteString (&sv.reliable_datagram, value); +} + +/* +=========== +SV_Serverinfo_f + + Examine or change the serverinfo string +=========== +*/ +char *CopyString(char *s); +void SV_Serverinfo_f (void) +{ + cvar_t *var; + + if (Cmd_Argc() == 1) + { + Con_Printf ("Server info settings:\n"); + Info_Print (svs.info); + return; + } + + if (Cmd_Argc() != 3) + { + Con_Printf ("usage: serverinfo [ ]\n"); + return; + } + + if (Cmd_Argv(1)[0] == '*') + { + Con_Printf ("Star variables cannot be changed.\n"); + return; + } + Info_SetValueForKey (svs.info, Cmd_Argv(1), Cmd_Argv(2), MAX_SERVERINFO_STRING); + + // if this is a cvar, change it too + var = Cvar_FindVar (Cmd_Argv(1)); + if (var) + { + Z_Free (var->string); // free the old value string + var->string = CopyString (Cmd_Argv(2)); + var->value = Q_atof (var->string); + } + + SV_SendServerInfoChange(Cmd_Argv(1), Cmd_Argv(2)); +} + + +/* +=========== +SV_Serverinfo_f + + Examine or change the serverinfo string +=========== +*/ +char *CopyString(char *s); +void SV_Localinfo_f (void) +{ + if (Cmd_Argc() == 1) + { + Con_Printf ("Local info settings:\n"); + Info_Print (localinfo); + return; + } + + if (Cmd_Argc() != 3) + { + Con_Printf ("usage: localinfo [ ]\n"); + return; + } + + if (Cmd_Argv(1)[0] == '*') + { + Con_Printf ("Star variables cannot be changed.\n"); + return; + } + Info_SetValueForKey (localinfo, Cmd_Argv(1), Cmd_Argv(2), MAX_LOCALINFO_STRING); +} + + +/* +=========== +SV_User_f + +Examine a users info strings +=========== +*/ +void SV_User_f (void) +{ + if (Cmd_Argc() != 2) + { + Con_Printf ("Usage: info \n"); + return; + } + + if (!SV_SetPlayer ()) + return; + + Info_Print (host_client->userinfo); +} + +/* +================ +SV_Gamedir + +Sets the fake *gamedir to a different directory. +================ +*/ +void SV_Gamedir (void) +{ + char *dir; + + if (Cmd_Argc() == 1) + { + Con_Printf ("Current *gamedir: %s\n", Info_ValueForKey (svs.info, "*gamedir")); + return; + } + + if (Cmd_Argc() != 2) + { + Con_Printf ("Usage: sv_gamedir \n"); + return; + } + + dir = Cmd_Argv(1); + + if (strstr(dir, "..") || strstr(dir, "/") + || strstr(dir, "\\") || strstr(dir, ":") ) + { + Con_Printf ("*Gamedir should be a single filename, not a path\n"); + return; + } + + Info_SetValueForStarKey (svs.info, "*gamedir", dir, MAX_SERVERINFO_STRING); +} + +/* +================ +SV_Floodport_f + +Sets the gamedir and path to a different directory. +================ +*/ + +void SV_Floodprot_f (void) +{ + int arg1, arg2, arg3; + + if (Cmd_Argc() == 1) + { + if (fp_messages) { + Con_Printf ("Current floodprot settings: \nAfter %d msgs per %d seconds, silence for %d seconds\n", + fp_messages, fp_persecond, fp_secondsdead); + return; + } else + Con_Printf ("No floodprots enabled.\n"); + } + + if (Cmd_Argc() != 4) + { + Con_Printf ("Usage: floodprot <# of messages> \n"); + Con_Printf ("Use floodprotmsg to set a custom message to say to the flooder.\n"); + return; + } + + arg1 = atoi(Cmd_Argv(1)); + arg2 = atoi(Cmd_Argv(2)); + arg3 = atoi(Cmd_Argv(3)); + + if (arg1<=0 || arg2 <= 0 || arg3<=0) { + Con_Printf ("All values must be positive numbers\n"); + return; + } + + if (arg1 > 10) { + Con_Printf ("Can only track up to 10 messages.\n"); + return; + } + + fp_messages = arg1; + fp_persecond = arg2; + fp_secondsdead = arg3; +} + +void SV_Floodprotmsg_f (void) +{ + if (Cmd_Argc() == 1) { + Con_Printf("Current msg: %s\n", fp_msg); + return; + } else if (Cmd_Argc() != 2) { + Con_Printf("Usage: floodprotmsg \"\"\n"); + return; + } + sprintf(fp_msg, "%s", Cmd_Argv(1)); +} + +/* +================ +SV_Gamedir_f + +Sets the gamedir and path to a different directory. +================ +*/ +char gamedirfile[MAX_OSPATH]; +void SV_Gamedir_f (void) +{ + char *dir; + + if (Cmd_Argc() == 1) + { + Con_Printf ("Current gamedir: %s\n", com_gamedir); + return; + } + + if (Cmd_Argc() != 2) + { + Con_Printf ("Usage: gamedir \n"); + return; + } + + dir = Cmd_Argv(1); + + if (strstr(dir, "..") || strstr(dir, "/") + || strstr(dir, "\\") || strstr(dir, ":") ) + { + Con_Printf ("Gamedir should be a single filename, not a path\n"); + return; + } + + COM_Gamedir (dir); + Info_SetValueForStarKey (svs.info, "*gamedir", dir, MAX_SERVERINFO_STRING); +} + +/* +================ +SV_Snap +================ +*/ +void SV_Snap (int uid) +{ + client_t *cl; + char pcxname[80]; + char checkname[MAX_OSPATH]; + int i; + + for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) + { + if (!cl->state) + continue; + if (cl->userid == uid) + break; + } + if (i >= MAX_CLIENTS) { + Con_Printf ("userid not found\n"); + return; + } + + sprintf(pcxname, "%d-00.pcx", uid); + + sprintf(checkname, "%s/snap", gamedirfile); + Sys_mkdir(gamedirfile); + Sys_mkdir(checkname); + + for (i=0 ; i<=99 ; i++) + { + pcxname[strlen(pcxname) - 6] = i/10 + '0'; + pcxname[strlen(pcxname) - 5] = i%10 + '0'; + sprintf (checkname, "%s/snap/%s", gamedirfile, pcxname); + if (Sys_FileTime(checkname) == -1) + break; // file doesn't exist + } + if (i==100) + { + Con_Printf ("Snap: Couldn't create a file, clean some out.\n"); + return; + } + strcpy(cl->uploadfn, checkname); + + memcpy(&cl->snap_from, &net_from, sizeof(net_from)); + if (sv_redirected != RD_NONE) + cl->remote_snap = true; + else + cl->remote_snap = false; + + ClientReliableWrite_Begin (cl, svc_stufftext, 24); + ClientReliableWrite_String (cl, "cmd snap\n"); + Con_Printf ("Requesting snap from user %d...\n", uid); +} + +/* +================ +SV_Snap_f +================ +*/ +void SV_Snap_f (void) +{ + int uid; + + if (Cmd_Argc() != 2) + { + Con_Printf ("Usage: snap \n"); + return; + } + + uid = atoi(Cmd_Argv(1)); + + SV_Snap(uid); +} + +/* +================ +SV_Snap +================ +*/ +void SV_SnapAll_f (void) +{ + client_t *cl; + int i; + + for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) + { + if (cl->state < cs_preconnected || cl->spectator) + continue; + SV_Snap(cl->userid); + } +} + +/* +================== +SV_InitOperatorCommands +================== +*/ +void SV_InitOperatorCommands (void) +{ + if (COM_CheckParm ("-cheats")) + { + sv_allow_cheats = true; + Info_SetValueForStarKey (svs.info, "*cheats", "ON", MAX_SERVERINFO_STRING); + } + + Cmd_AddCommand ("logfile", SV_Logfile_f); + Cmd_AddCommand ("fraglogfile", SV_Fraglogfile_f); + Cmd_AddCommand ("logerrors", SV_ErrorLogfile_f); + Cmd_AddCommand ("logrcon", SV_RconLogfile_f); + + Cmd_AddCommand ("snap", SV_Snap_f); + Cmd_AddCommand ("snapall", SV_SnapAll_f); + Cmd_AddCommand ("kick", SV_Kick_f); + Cmd_AddCommand ("status", SV_Status_f); + + Cmd_AddCommand ("map", SV_Map_f); + Cmd_AddCommand ("setmaster", SV_SetMaster_f); + + Cmd_AddCommand ("heartbeat", SV_Heartbeat_f); + + Cmd_AddCommand ("say", SV_ConSay_f); + Cmd_AddCommand ("quit", SV_Quit_f); + + Cmd_AddCommand ("god", SV_God_f); + Cmd_AddCommand ("give", SV_Give_f); + Cmd_AddCommand ("noclip", SV_Noclip_f); + Cmd_AddCommand ("localinfo", SV_Localinfo_f); + + Cmd_AddCommand ("serverinfo", SV_Serverinfo_f); + Cmd_AddCommand ("user", SV_User_f); + + Cmd_AddCommand ("gamedir", SV_Gamedir_f); + Cmd_AddCommand ("sv_gamedir", SV_Gamedir); + Cmd_AddCommand ("floodprot", SV_Floodprot_f); + Cmd_AddCommand ("floodprotmsg", SV_Floodprotmsg_f); + + + + cl_warncmd.value = 1; +} diff --git a/source/sv_demo.c b/source/sv_demo.c index 2d560a48..fff6a623 100644 --- a/source/sv_demo.c +++ b/source/sv_demo.c @@ -1,1281 +1,1475 @@ -/* -Copyright (C) 1996-1997 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 included (GNU.txt) 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 "qwsvdef.h" -#include "winquake.h" - -#define MIN_DEMO_MEMORY 0x100000 -#define USACACHE (sv_demoUseCache.value && svs.demomemsize) -#define DWRITE(a,b,c,d) b*dwrite(a,b,c,d) - -extern cvar_t sv_demoUseCache; -extern cvar_t sv_demoCacheSize; -extern cvar_t sv_demoMaxDirSize; -static int demo_max_size; -static int demo_size; - -void SV_WriteDemoMessage (sizebuf_t *msg, int type, int to, float time); -size_t (*dwrite) ( const void *buffer, size_t size, size_t count, void *stream); - - -void SV_DemoPings (void) -{ - client_t *client; - int j; - - for (j = 0, client = svs.clients; j < MAX_CLIENTS; j++, client++) - { - if (client->state != cs_spawned) - continue; - - DemoReliableWrite_Begin (dem_all, 0, 7); - MSG_WriteByte(demo.buf, svc_updateping); - MSG_WriteByte(demo.buf, j); - MSG_WriteShort(demo.buf, SV_CalcPing(client)); - MSG_WriteByte(demo.buf, svc_updatepl); - MSG_WriteByte (demo.buf, j); - MSG_WriteByte (demo.buf, client->lossage); - } -} -/* -============== -SV_DemoWriteToDisk - -Writes to disk a message ment for specifc client -or all messages if type == 0 -Message is cleared from demobuf after that -============== -*/ - -#define POS_TO 1 -#define POS_SIZE 5 -#define POS_DATA 9 - -#define BUF_FULL (1<<31) - -void SV_DemoWriteToDisk(int type, int to, float time) -{ - int pos = 0; - byte *p; - int btype, bto, bsize; - sizebuf_t msg; - - //Con_Printf("to disk:%d:%d, demo.bufsize:%d, cur:%d:%d\n", type, to, demo.bufsize, demo.curtype, demo.curto); - p = demo.dbuf->data; - while (pos < demo.dbuf->size) - { - - btype = *p; - bto = *(int*)(p+POS_TO); - bsize = *(int*)(p+POS_SIZE) & (~BUF_FULL); - pos += POS_DATA + bsize; //pos now points to next message - - //Con_Printf("type %d:%d, size :%d\n", btype, bto, bsize); - - // no type means we are writing to disk everything - if (!type || (btype == type && bto == to)) - { - if (bsize) { - msg.data = p+POS_DATA; - msg.cursize = bsize; - - SV_WriteDemoMessage(&msg, btype, bto, time); - } - - // data is written so it need to be cleard from demobuf - //Con_Printf("memmove size:%d\n", demo.bufsize - pos); - memmove(p, p+POS_DATA+bsize, demo.dbuf->size - pos); - demo.dbuf->size -= bsize+POS_DATA; - pos -= bsize + POS_DATA; - if (demo.dbuf->cursize > pos) - demo.dbuf->cursize -= bsize + POS_DATA; - - if (btype == demo.dbuf->curtype && bto == demo.dbuf->curto) - { - //Con_Printf("remove\n"); - demo.dbuf->curtype = 0; - demo.dbuf->curto = 0; - demo.dbuf->msgsize = NULL; - } - } else { - // move along - p += POS_DATA + bsize; - } - } -} - -/* -============== -DemoSetBuf - -Sets position in the buf for writing to specific client -============== -*/ -void DemoSetBuf(int type, int to) -{ - byte *p; - int pos = 0; - int btype, bto, bsize; - - p = demo.dbuf->data; - while (pos < demo.dbuf->size) - { - btype = *p; - bto = *(int*)(p+POS_TO); - bsize = *(int*)(p+POS_SIZE); - pos += POS_DATA + (bsize & (~BUF_FULL)); - - //Con_Printf("type:%d, to:%d, size:%d\n", btype, bto, bsize); - - if (type == btype && to == bto && !(bsize&BUF_FULL)) - { - demo.dbuf->cursize = pos; - demo.dbuf->msgsize = (int*)(p + POS_SIZE); - demo.dbuf->curtype = type; - demo.dbuf->curto = to; - return; - } - p += POS_DATA + (bsize & (~BUF_FULL)); - } - // type&&to not exist in the buf, so add it - - *p = (byte)type; - *(int*)(p + POS_TO) = to; - *(int*)(p + POS_SIZE) = 0; - demo.bufsize += POS_DATA; - demo.dbuf->size += POS_DATA; - demo.dbuf->msgsize = (int*)(p + POS_SIZE); - demo.dbuf->curtype = type; - demo.dbuf->curto = to; - demo.dbuf->cursize = demo.dbuf->size; -} - -void DemoReliableWrite_Begin(int type, int to, int size) -{ - byte *p; - - if (!sv.demorecording) - return; - - // will it fit? - while (demo.dbuf->size + size + POS_DATA > demo.dbuf->maxsize) { - SV_DemoWritePackets(1); // does it work? - } - - if (demo.dbuf->curtype != type || demo.dbuf->curto != to) - DemoSetBuf(type, to); - - if (*demo.dbuf->msgsize + size > MAX_MSGLEN) - { - *demo.dbuf->msgsize |= BUF_FULL; - DemoSetBuf(type, to); - } - - // we have to make room for new data - if (demo.dbuf->cursize != demo.dbuf->size) { - p = demo.dbuf->data + demo.dbuf->cursize; - memmove(p+size, p, demo.dbuf->size - demo.dbuf->cursize); - } - - demo.dbuf->size += size; - *demo.dbuf->msgsize += size; - demo.bufsize += size; -} - -/* -==================== -SV_WriteDemoMessage - -Dumps the current net message, prefixed by the length and view angles -==================== -*/ -void SV_WriteDemoMessage (sizebuf_t *msg, int type, int to, float time) -{ - int len, i, msec; - byte c; - static double prevtime; - - if (!sv.demorecording) - return; - - msec = (time - prevtime)*1000; - prevtime = time; - if (msec > 255) msec = 255; - - c = msec; - demo.size += DWRITE(&c, sizeof(c), 1, demo.dest); - - if (demo.lasttype != type || demo.lastto != to) - { - demo.lasttype = type; - demo.lastto = to; - switch (demo.lasttype) - { - case dem_all: - c = dem_all; - demo.size += DWRITE (&c, sizeof(c), 1, demo.dest); - break; - case dem_multiple: - c = dem_multiple; - demo.size += DWRITE (&c, sizeof(c), 1, demo.dest); - - i = LittleLong(demo.lastto); - demo.size += DWRITE (&i, sizeof(i), 1, demo.dest); - break; - case dem_single: - case dem_stats: - c = demo.lasttype + (demo.lastto << 3); - demo.size += DWRITE (&c, sizeof(c), 1, demo.dest); - break; - default: - SV_Stop_f (); - Con_Printf("bad demo message type:%d", type); - return; - } - } else { - c = dem_read; - demo.size += DWRITE (&c, sizeof(c), 1, demo.dest); - } - - - len = LittleLong (msg->cursize); - demo.size += DWRITE (&len, 4, 1, demo.dest); - demo.size += DWRITE (msg->data, msg->cursize, 1, demo.dest); - - if (demo.disk) - fflush (demo.file); - else if (demo.size - demo_size > demo_max_size) - { - demo_size = demo.size; - (byte*) demo.dest -= 0x80000; - fwrite(svs.demomem, 1, 0x80000, demo.file); - fflush(demo.file); - memmove(svs.demomem, svs.demomem + 0x80000, demo.size - 0x80000); - } -} - - -/* -==================== -SV_DemoWritePackets - -Interpolates to get exact players position for current frame -and writes packets to the disk/memory -==================== -*/ - -float adjustangle(float current, float ideal, float fraction) -{ - float move; - - move = ideal - current; - if (ideal > current) - { - - if (move >= 180) - move = move - 360; - } - else - { - if (move <= -180) - move = move + 360; - } - - move *= fraction; - - return (current + move); -} - -#define DF_ORIGIN 1 -#define DF_ANGLES (1<<3) -#define DF_EFFECTS (1<<6) -#define DF_SKINNUM (1<<7) -#define DF_DEAD (1<<8) -#define DF_GIB (1<<9) -#define DF_WEAPONFRAME (1<<10) -#define DF_MODEL (1<<11) - -void SV_DemoWritePackets (int num) -{ - demo_frame_t *frame, *nextframe; - demo_client_t *cl, *nextcl; - int i, j, flags; - qboolean valid; - double time, playertime, nexttime; - float f; - vec3_t origin, angles; - sizebuf_t msg; - byte msg_buf[MAX_MSGLEN]; - demoinfo_t *demoinfo; - - if (!sv.demorecording) - return; - - msg.data = msg_buf; - msg.maxsize = sizeof(msg_buf); - - // 'num' frames to write - for ( ; num; num--, demo.lastwritten++) - { - frame = &demo.frames[demo.lastwritten&DEMO_FRAMES_MASK]; - time = frame->time; - nextframe = frame; - msg.cursize = 0; - - demo.dbuf = &frame->buf; - - // find two frames - // one before the exact time (time - msec) and one after, - // then we can interpolte exact position for current frame - for (i = 0, cl = frame->clients, demoinfo = demo.info; i < MAX_CLIENTS; i++, cl++, demoinfo++) - { - if (cl->parsecount != demo.lastwritten) - continue; // not valid - - nexttime = playertime = time - cl->sec; - - for (j = demo.lastwritten+1, valid = false; nexttime < time && j < demo.parsecount; j++) - { - nextframe = &demo.frames[j&DEMO_FRAMES_MASK]; - nextcl = &nextframe->clients[i]; - - if (nextcl->parsecount != j) - break; // disconnected? - if (nextcl->fixangle) - break; // respawned, or walked into teleport, do not interpolate! - if (!(nextcl->flags & DF_DEAD) && (cl->flags & DF_DEAD)) - break; // respawned, do not interpolate - - nexttime = nextframe->time - nextcl->sec; - - if (nexttime >= time) - { - // good, found what we were looking for - valid = true; - break; - } - } - - if (valid) - { - f = (time - nexttime)/(nexttime - playertime); - for (j=0;j<3;j++) { - angles[j] = adjustangle(cl->info.angles[j], nextcl->info.angles[j],1.0+f); - origin[j] = nextcl->info.origin[j] + f*(nextcl->info.origin[j]-cl->info.origin[j]); - } - } else { - VectorCopy(cl->info.origin, origin); - VectorCopy(cl->info.angles, angles); - } - - // now write it to buf - flags = cl->flags; - - if (cl->fixangle) { - demo.fixangletime[i] = cl->cmdtime; - } - - for (j=0; j < 3; j++) - if (origin[j] != demoinfo->origin[i]) - flags |= DF_ORIGIN << j; - - if (cl->fixangle || demo.fixangletime[i] != cl->cmdtime) - { - for (j=0; j < 3; j++) - if (angles[j] != demoinfo->angles[j]) - flags |= DF_ANGLES << j; - } - - if (cl->info.model != demoinfo->model) - flags |= DF_MODEL; - if (cl->info.effects != demoinfo->effects) - flags |= DF_EFFECTS; - if (cl->info.skinnum != demoinfo->skinnum) - flags |= DF_SKINNUM; - if (cl->info.weaponframe != demoinfo->weaponframe) - flags |= DF_WEAPONFRAME; - - MSG_WriteByte (&msg, svc_playerinfo); - MSG_WriteByte (&msg, i); - MSG_WriteShort (&msg, flags); - - MSG_WriteByte (&msg, cl->frame); - - for (j=0 ; j<3 ; j++) - if (flags & (DF_ORIGIN << j)) - MSG_WriteCoord (&msg, origin[j]); - - for (j=0 ; j<3 ; j++) - if (flags & (DF_ANGLES << j)) - MSG_WriteAngle16 (&msg, angles[j]); - - - if (flags & DF_MODEL) - MSG_WriteByte (&msg, cl->info.model); - - if (flags & DF_SKINNUM) - MSG_WriteByte (&msg, cl->info.skinnum); - - if (flags & DF_EFFECTS) - MSG_WriteByte (&msg, cl->info.effects); - - if (flags & DF_WEAPONFRAME) - MSG_WriteByte (&msg, cl->info.weaponframe); - - VectorCopy(cl->info.origin, demoinfo->origin); - VectorCopy(cl->info.angles, demoinfo->angles); - demoinfo->skinnum = cl->info.skinnum; - demoinfo->effects = cl->info.effects; - demoinfo->weaponframe = cl->info.weaponframe; - demoinfo->model = cl->info.model; - } - - SV_DemoWriteToDisk(demo.lasttype,demo.lastto, (float)time); // this goes first to reduce demo size a bit - SV_DemoWriteToDisk(0,0, (float)time); // now goes the rest - if (msg.cursize) - SV_WriteDemoMessage(&msg, dem_all, 0, (float)time); - } - - // now move the buffer back to make place for new data - - // size of mem that has been written to disk - j = demo.frames[demo.lastwritten&DEMO_FRAMES_MASK].buf.data - demo.buffer; - demo.bufsize -= j; - - memmove(demo.buffer, demo.buffer + j, demo.bufsize); - for (i = demo.lastwritten; i < demo.parsecount; i++) - { - demo.frames[i&DEMO_FRAMES_MASK].buf.data -= j; - demo.frames[i&DEMO_FRAMES_MASK].buf.maxsize += j; // is it necesery? - (byte*) demo.frames[i&DEMO_FRAMES_MASK].buf.msgsize -= j; - } - - demo.dbuf = &demo.frames[demo.parsecount&DEMO_FRAMES_MASK].buf; -} - -size_t memwrite ( const void *buffer, size_t size, size_t count, byte *mem) -{ - int i,c = count; - const byte *buf = buffer; - byte *m = mem; - - for (;count; count--) - for (i = size, buf = buffer; i; i--) - *mem++ = *buf++; - - (byte*) demo.dest += mem - m; - return c; -} - -void Demo_Init (void) -{ - int p, size = MIN_DEMO_MEMORY; - - p = COM_CheckParm ("-democache"); - if (p) - { - if (p < com_argc-1) - size = Q_atoi (com_argv[p+1]) * 1024; - else - Sys_Error ("Memory_Init: you must specify a size in KB after -democache"); - } - - if (size < MIN_DEMO_MEMORY) - { - Con_Printf("Minimum memory size for demo cache is %dk\n", MIN_DEMO_MEMORY / 1024); - size = MIN_DEMO_MEMORY; - } - - svs.demomem = Hunk_AllocName ( size, "demo" ); - svs.demomemsize = size; - demo_max_size = size - 0x80000; - Cvar_SetROM(&sv_demoCacheSize, va("%d", size/1024)); -} - -/* -==================== -SV_InitRecord -==================== -*/ - -qboolean SV_InitRecord(void) -{ - if (!USACACHE) - { - dwrite = &fwrite; - demo.dest = demo.file; - demo.disk = true; - } else - { - dwrite = &memwrite; - demo.dest = svs.demomem; - } - - demo_size = 0; - - return true; -} - -/* -==================== -SV_Stop - -stop recording a demo -==================== -*/ -void SV_Stop (int reason) -{ - if (!sv.demorecording) - { - Con_Printf ("Not recording a demo.\n"); - return; - } - -// write a disconnect message to the demo file - - // clearup to be sure message will fit - demo.dbuf->cursize = 0; - demo.dbuf->curtype = 0; - demo.dbuf->size = 0; - DemoReliableWrite_Begin(dem_all, 0, 2+strlen("EndOfDemo")); - MSG_WriteByte (demo.buf, svc_disconnect); - MSG_WriteString (demo.buf, "EndOfDemo"); - SV_DemoWritePackets(demo.parsecount - demo.lastwritten + 1); - -// finish up - if (!demo.disk) - { - fwrite(svs.demomem, 1, demo.size - demo_size, demo.file); - fflush(demo.file); - } - - fclose (demo.file); - - demo.file = NULL; - sv.demorecording = false; - if (!reason) - SV_BroadcastPrintf (PRINT_CHAT, "Server recording completed\n"); - else - SV_BroadcastPrintf (PRINT_CHAT, "Server recording stoped\nMax demo size exceeded\n"); - - Cvar_SetROM(&serverdemo, ""); -} - -/* -==================== -SV_Stop_f -==================== -*/ -void SV_Stop_f (void) -{ - SV_Stop(0); -} - -/* -==================== -SV_WriteDemoMessage - -Dumps the current net message, prefixed by the length and view angles -==================== -*/ - -void SV_WriteRecordDemoMessage (sizebuf_t *msg, int seq) -{ - int len; - byte c; - - if (!sv.demorecording) - return; - - c = 0; - demo.size += DWRITE (&c, sizeof(c), 1, demo.dest); - - c = dem_read; - demo.size += DWRITE (&c, sizeof(c), 1, demo.dest); - - len = LittleLong (msg->cursize); - demo.size += DWRITE (&len, 4, 1, demo.dest); - - demo.size += DWRITE (msg->data, msg->cursize, 1, demo.dest); - - if (demo.disk) - fflush (demo.file); -} - -void SV_WriteSetDemoMessage (void) -{ - int len; - byte c; - -//Con_Printf("write: %ld bytes, %4.4f\n", msg->cursize, realtime); - - if (!sv.demorecording) - return; - - c = 0; - demo.size += DWRITE (&c, sizeof(c), 1, demo.dest); - - c = dem_set; - demo.size += DWRITE (&c, sizeof(c), 1, demo.dest); - - - len = LittleLong(0); - demo.size += DWRITE (&len, 4, 1, demo.dest); - len = LittleLong(0); - demo.size += DWRITE (&len, 4, 1, demo.dest); - - if (demo.disk) - fflush (demo.file); -} - -static void SV_Record (void) -{ - sizebuf_t buf; - char buf_data[MAX_MSGLEN]; - int n, i; - char *s, info[MAX_INFO_STRING]; - - client_t *player; - char *gamedir; - int seq = 1; - - sv.demorecording = true; - demo.pingtime = demo.time = sv.time; - -/*-------------------------------------------------*/ - -// serverdata - // send the info about the new client to all connected clients - memset(&buf, 0, sizeof(buf)); - buf.data = buf_data; - buf.maxsize = sizeof(buf_data); - -// send the serverdata - - gamedir = Info_ValueForKey (svs.info, "*gamedir"); - if (!gamedir[0]) - gamedir = "qw"; - - MSG_WriteByte (&buf, svc_serverdata); - MSG_WriteLong (&buf, PROTOCOL_VERSION); - MSG_WriteLong (&buf, svs.spawncount); - MSG_WriteString (&buf, gamedir); - - - MSG_WriteFloat (&buf, sv.time); - - // send full levelname - MSG_WriteString (&buf, PR_GetString(sv.edicts->v.message)); - - // send the movevars - MSG_WriteFloat(&buf, movevars.gravity); - MSG_WriteFloat(&buf, movevars.stopspeed); - MSG_WriteFloat(&buf, movevars.maxspeed); - MSG_WriteFloat(&buf, movevars.spectatormaxspeed); - MSG_WriteFloat(&buf, movevars.accelerate); - MSG_WriteFloat(&buf, movevars.airaccelerate); - MSG_WriteFloat(&buf, movevars.wateraccelerate); - MSG_WriteFloat(&buf, movevars.friction); - MSG_WriteFloat(&buf, movevars.waterfriction); - MSG_WriteFloat(&buf, movevars.entgravity); - - // send music - MSG_WriteByte (&buf, svc_cdtrack); - MSG_WriteByte (&buf, 0); // none in demos - - // send server info string - MSG_WriteByte (&buf, svc_stufftext); - MSG_WriteString (&buf, va("fullserverinfo \"%s\"\n", svs.info) ); - - // flush packet - SV_WriteRecordDemoMessage (&buf, seq++); - SZ_Clear (&buf); - -// soundlist - MSG_WriteByte (&buf, svc_soundlist); - MSG_WriteByte (&buf, 0); - - n = 0; - s = sv.sound_precache[n+1]; - while (s) { - MSG_WriteString (&buf, s); - if (buf.cursize > MAX_MSGLEN/2) { - MSG_WriteByte (&buf, 0); - MSG_WriteByte (&buf, n); - SV_WriteRecordDemoMessage (&buf, seq++); - SZ_Clear (&buf); - MSG_WriteByte (&buf, svc_soundlist); - MSG_WriteByte (&buf, n + 1); - } - n++; - s = sv.sound_precache[n+1]; - } - - if (buf.cursize) { - MSG_WriteByte (&buf, 0); - MSG_WriteByte (&buf, 0); - SV_WriteRecordDemoMessage (&buf, seq++); - SZ_Clear (&buf); - } - -// modellist - MSG_WriteByte (&buf, svc_modellist); - MSG_WriteByte (&buf, 0); - - n = 0; - s = sv.model_precache[n+1]; - while (s) { - MSG_WriteString (&buf, s); - if (buf.cursize > MAX_MSGLEN/2) { - MSG_WriteByte (&buf, 0); - MSG_WriteByte (&buf, n); - SV_WriteRecordDemoMessage (&buf, seq++); - SZ_Clear (&buf); - MSG_WriteByte (&buf, svc_modellist); - MSG_WriteByte (&buf, n + 1); - } - n++; - s = sv.model_precache[n+1]; - } - if (buf.cursize) { - MSG_WriteByte (&buf, 0); - MSG_WriteByte (&buf, 0); - SV_WriteRecordDemoMessage (&buf, seq++); - SZ_Clear (&buf); - } - -// prespawn - - for (n = 0; n < sv.num_signon_buffers; n++) - { - SZ_Write (&buf, - sv.signon_buffers[n], - sv.signon_buffer_size[n]); - - if (buf.cursize > MAX_MSGLEN/2) { - SV_WriteRecordDemoMessage (&buf, seq++); - SZ_Clear (&buf); - } - } - - MSG_WriteByte (&buf, svc_stufftext); - MSG_WriteString (&buf, va("cmd spawn %i 0\n",svs.spawncount) ); - - if (buf.cursize) { - SV_WriteRecordDemoMessage (&buf, seq++); - SZ_Clear (&buf); - } - -// send current status of all other players - - for (i = 0; i < MAX_CLIENTS; i++) { - player = svs.clients + i; - - MSG_WriteByte (&buf, svc_updatefrags); - MSG_WriteByte (&buf, i); - MSG_WriteShort (&buf, player->old_frags); - - MSG_WriteByte (&buf, svc_updateping); - MSG_WriteByte (&buf, i); - MSG_WriteShort (&buf, SV_CalcPing(player)); - - MSG_WriteByte (&buf, svc_updatepl); - MSG_WriteByte (&buf, i); - MSG_WriteByte (&buf, player->lossage); - - MSG_WriteByte (&buf, svc_updateentertime); - MSG_WriteByte (&buf, i); - MSG_WriteFloat (&buf, realtime - player->connection_started); - - strcpy (info, player->userinfo); - Info_RemovePrefixedKeys (info, '_'); // server passwords, etc - - MSG_WriteByte (&buf, svc_updateuserinfo); - MSG_WriteByte (&buf, i); - MSG_WriteLong (&buf, player->userid); - MSG_WriteString (&buf, info); - - if (buf.cursize > MAX_MSGLEN/2) { - SV_WriteRecordDemoMessage (&buf, seq++); - SZ_Clear (&buf); - } - } - -// send all current light styles - for (i=0 ; i= 18 && *name <= 27) // liczby - { - *out = *name + 30; - } else if (*name >= 146 && *name <= 155) // liczby - { - *out = *name - 98; - } else if (*name == 29 || *name == 128 || *name == 157) - { - *out = '('; - } else if (*name == 31 || *name == 130 || *name == 159) - { - *out = ')'; - } else if (*name == 16 || *name == 144) - { - *out = '['; - } else if (*name == 17 || *name == 145) - { - *out = ']'; - } else if (*name > 128) - { - *out = *name - 128; - } else - *out = *name; - - if (*out >= 'A' && *out <= 'Z') - *out += 'a' - 'A'; - - *out &= 0x7F; // strip high bit - c = *out; - if (c<=' ' || c=='?' || c=='*' || c=='\\' || c=='/' || c==':' - || c=='<' || c=='>' || c=='"') - *out = '_'; - } - - *out = 0; - return text; -} -/* -==================== -SV_Record_f - -record -==================== -*/ -void SV_Record_f (void) -{ - int c, i; - char name[MAX_OSPATH], *s; - dir_t dir; - demo_frame_t *frame; - - c = Cmd_Argc(); - if (c != 2) - { - Con_Printf ("record \n"); - return; - } - - if (sv.state != ss_active) { - Con_Printf ("Not active yet.\n"); - return; - } - - if (sv.demorecording) - SV_Stop_f(); - - dir = Sys_listdir(va("%s/demos", com_gamedir), ".*"); - if (sv_demoMaxDirSize.value && dir.size > sv_demoMaxDirSize.value*1024) - { - Con_Printf("insufficient directory space, increase sv_demoMaxDirSize\n"); - return; - } - - sprintf (name, "%s/demos/%s", com_gamedir, SV_CleanName(Cmd_Argv(1))); - Sys_mkdir(va("%s/demos", com_gamedir)); - -// -// open the demo file -// - COM_ForceExtension (name, ".mvd"); - - memset(&demo, 0, sizeof(demo)); - - demo.dbuf = &demo.frames[0].buf; - demo.dbuf->data = demo.buffer; - demo.dbuf->maxsize = sizeof(demo.buffer); - demo.datagram.maxsize = sizeof(demo.datagram_data); - demo.datagram.data = demo.datagram_data; - - demo.file = fopen (name, "wb"); - if (!demo.file) - { - Con_Printf ("ERROR: couldn't open.\n"); - return; - } - - SV_InitRecord(); - - s = name + strlen(name); - while (*s != '/') s--; - strcpy(demo.name, s+1); - - SV_BroadcastPrintf (PRINT_CHAT, "Server starts recording (%s):\n%s\n", demo.disk ? "disk" : "memory", demo.name); - Cvar_SetROM(&serverdemo, demo.name); - SV_Record (); -} - -/* -==================== -SV_EasyRecord_f - -easyrecord [demoname] -==================== -*/ - -int Dem_CountPlayers () -{ - int i, count; - - count = 0; - for (i = 0; i < MAX_CLIENTS ; i++) { - if (svs.clients[i].name[0] && !svs.clients[i].spectator) - count++; - } - - return count; -} - -char *Dem_Team(int num) -{ - int i; - static char *lastteam[2]; - qboolean first = true; - client_t *client; - static int index = 0; - - index = 1 - index; - - for (i = 0, client = svs.clients; num && i < MAX_CLIENTS; i++, client++) - { - if (!client->name[0] || client->spectator) - continue; - - if (first || strcmp(lastteam[index], client->team)) - { - first = false; - num--; - lastteam[index] = client->team; - } - } - - if (num) - return ""; - - return lastteam[index]; -} - -char *Dem_PlayerName(int num) -{ - int i; - client_t *client; - - for (i = 0, client = svs.clients; i < MAX_CLIENTS; i++, client++) - { - if (!client->name[0] || client->spectator) - continue; - - if (!--num) - return client->name; - } - - return ""; -} - -void SV_EasyRecord_f (void) -{ - int c; - dir_t dir; - char name[1024], *s; - char name2[MAX_OSPATH*2]; - int i; - unsigned char *p; - FILE *f; - demo_frame_t *frame; - - c = Cmd_Argc(); - if (c > 2) - { - Con_Printf ("easyrecord [demoname]\n"); - return; - } - - if (sv.demorecording) - SV_Stop_f(); - - dir = Sys_listdir(va("%s/demos", com_gamedir), ".*"); - if (sv_demoMaxDirSize.value && dir.size > sv_demoMaxDirSize.value*1024) - { - Con_Printf("insufficient directory space, increase sv_demoMaxDirSize\n"); - return; - } - - if (c == 2) - sprintf (name, "%s", Cmd_Argv(1)); - else { - // guess game type and write demo name - i = Dem_CountPlayers(); - if (teamplay.value && i > 2) - { - // Teamplay - sprintf (name, "team_%s_vs_%s_%s", - Dem_Team(1), - Dem_Team(2), - sv.name); - } else { - if (i == 2) { - // Duel - sprintf (name, "duel_%s_vs_%s_%s", - Dem_PlayerName(1), - Dem_PlayerName(2), - sv.name); - } else { - // FFA - sprintf (name, "ffa_%s(%d)", - sv.name, - i); - } - } - } - -// Make sure the filename doesn't contain illegal characters - s = SV_CleanName(name); - - sprintf (name, va("%s/demos/%s", com_gamedir, s)); - Sys_mkdir(va("%s/demos", com_gamedir)); - -// find a filename that doesn't exist yet - strcpy (name2, name); - COM_ForceExtension (name2, ".mvd"); - f = fopen (name2, "rb"); - if (f) { - i = 0; - do { - fclose (f); - strcpy (name2, va("%s_%02i", name, i)); - COM_ForceExtension (name2, ".mvd"); - f = fopen (name2, "rb"); - i++; - } while (f); - } - -// -// open the demo file -// - - memset(&demo, 0, sizeof(demo)); - - demo.dbuf = &demo.frames[0].buf; - demo.dbuf->data = demo.buffer; - demo.dbuf->maxsize = sizeof(demo.buffer); - demo.datagram.maxsize = sizeof(demo.datagram_data); - demo.datagram.data = demo.datagram_data; - - demo.file = fopen (name2, "wb"); - if (!demo.file) - { - Con_Printf ("ERROR: couldn't open %s\n", name2); - return; - } - - SV_InitRecord(); - - s = name2 + strlen(name2); - while (*s != '/') s--; - strcpy(demo.name, s+1); - - SV_BroadcastPrintf (PRINT_CHAT, "Server starts recording (%s):\n%s\n", demo.disk ? "disk" : "memory", demo.name); - Cvar_SetROM(&serverdemo, demo.name); - SV_Record (); -} - -void SV_DemoList_f (void) -{ - dir_t dir; - file_t *list; - float f; - int i; - char *key; - - Con_Printf("content of %s/demos/*.mvd\n", com_gamedir); - dir = Sys_listdir(va("%s/demos", com_gamedir), ".mvd"); - list = dir.files; - if (!list->name[0]) - { - Con_Printf("no demos\n"); - } - - if (Cmd_Argc() == 2) - key = Cmd_Argv(1); - else - key = ""; - - for (i = 0; list->name[0]; i++, list++) - { - if (strstr(list->name, key) != NULL) { - if (sv.demorecording && !strcmp(list->name, demo.name)) - Con_Printf("*%d: %s %dk\n", i, list->name, demo.size/1024); - else - Con_Printf("%d: %s %dk\n", i, list->name, list->size/1024); - } - } - - if (sv.demorecording) - dir.size += demo.size; - - Con_Printf("\ndirectory size: %.1fMB\n",(float)dir.size/(1024*1024)); - if (sv_demoMaxDirSize.value) { - f = (sv_demoMaxDirSize.value*1024 - dir.size)/(1024*1024); - if ( f < 0) - f = 0; - Con_Printf("space available: %.1fMB\n", f); - } -} - -void SV_DemoRemove_f (void) -{ - char name[MAX_DEMO_NAME], *ptr; - char path[MAX_OSPATH]; - int i; - - if (Cmd_Argc() != 2) - { - Con_Printf("rmdemo - removes the demo\nrmdemo * - removes demo with in the name\nrmdemo * - removes all demos\n"); - return; - } - - ptr = Cmd_Argv(1); - if (*ptr == '*') - { - dir_t dir; - file_t *list; - - // remove all demos with specified token - ptr++; - - dir = Sys_listdir(va("%s/demos", com_gamedir), ".mvd"); - list = dir.files; - for (i = 0;list->name[0]; list++) - { - if (strstr(list->name, ptr)) - { - if (sv.demorecording && !strcmp(list->name, demo.name)) - SV_Stop_f(); - - // stop recording first; - sprintf(path, "%s/demos/%s", com_gamedir, list->name); - if (!Sys_remove(path)) { - Con_Printf("removing %s...\n", list->name); - i++; - } - } - } - - if (i) { - Con_Printf("%d demos removed\n", i); - } else { - Con_Printf("no matching found\n"); - } - - return; - } - - Q_strncpyz(name ,Cmd_Argv(1), MAX_DEMO_NAME); - COM_DefaultExtension(name, ".mvd"); - - sprintf(path, "%s/demos/%s", com_gamedir, name); - - if (sv.demorecording && !strcmp(name, demo.name)) - SV_Stop_f(); - - if (!Sys_remove(path)) - Con_Printf("demo %s succesfully removed\n", name); - else - Con_Printf("unable to remove demo %s\n", name); -} - -void SV_DemoRemoveNum_f (void) -{ - file_t *list; - dir_t dir; - int num; - char *val; - char path[MAX_OSPATH]; - - if (Cmd_Argc() != 2) - { - Con_Printf("rmdemonum <#>\n"); - return; - } - - val = Cmd_Argv(1); - if ((num = atoi(val)) == 0 && val[0] != '0') - { - Con_Printf("rmdemonum <#>\n"); - return; - } - - dir = Sys_listdir(va("%s/demos", com_gamedir), ".mvd"); - list = dir.files; - while (list->name[0] && num) {list++; num--;}; - - if (list->name[0]) { - if (sv.demorecording && !strcmp(list->name, demo.name)) - SV_Stop_f(); - - sprintf(path, "%s/demos/%s", com_gamedir, list->name); - if (!Sys_remove(path)) - Con_Printf("demo %s succesfully removed\n", list->name); - else - Con_Printf("unable to remove demo %s\n", list->name); - } else - Con_Printf("invalid demo num\n"); -} \ No newline at end of file +/* +Copyright (C) 1996-1997 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 included (GNU.txt) 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 "qwsvdef.h" +#include "winquake.h" + +#define MIN_DEMO_MEMORY 0x100000 +#define USACACHE (sv_demoUseCache.value && svs.demomemsize) +#define DWRITE(a,b,c,d) b*dwrite(a,b,c,d) +#define MAXSIZE (demobuffer->end < demobuffer->last ? \ + demobuffer->start - demobuffer->end : \ + demobuffer->maxsize - demobuffer->end) + + +extern cvar_t sv_demoUseCache; +extern cvar_t sv_demoCacheSize; +extern cvar_t sv_demoMaxDirSize; +extern cvar_t sv_demoDir; +extern cvar_t sv_demofps; +extern cvar_t sv_demoPings; +extern cvar_t sv_demoNoVis; +extern cvar_t sv_demoMaxSize; + +static int demo_max_size; +static int demo_size; +cvar_t sv_demoPrefix = {"sv_demoPrefix", ""}; +cvar_t sv_demoSuffix = {"sv_demoSuffix", ""}; +cvar_t sv_onrecordfinish = {"sv_onRecordFinish", ""}; + +void SV_WriteDemoMessage (sizebuf_t *msg, int type, int to, float time); +size_t (*dwrite) ( const void *buffer, size_t size, size_t count, void *stream); + +static dbuffer_t *demobuffer; +static int header = (int)&((header_t*)0)->data; + +entity_state_t demo_entities[UPDATE_MASK+1][MAX_DEMO_PACKET_ENTITIES]; + +// only one .. is allowed (security) +qboolean sv_demoDir_OnChange(cvar_t *cvar, char *value) +{ + if (!value[0]) + return true; + + if (value[0] == '.' && value[1] == '.') + value += 2; + if (strstr(value,"/..")) + return true; + + return false; +} + +void SV_DemoPings (void) +{ + client_t *client; + int j; + + for (j = 0, client = svs.clients; j < MAX_CLIENTS; j++, client++) + { + if (client->state != cs_spawned) + continue; + + DemoWrite_Begin (dem_all, 0, 7); + MSG_WriteByte((sizebuf_t*)demo.dbuf, svc_updateping); + MSG_WriteByte((sizebuf_t*)demo.dbuf, j); + MSG_WriteShort((sizebuf_t*)demo.dbuf, SV_CalcPing(client)); + MSG_WriteByte((sizebuf_t*)demo.dbuf, svc_updatepl); + MSG_WriteByte ((sizebuf_t*)demo.dbuf, j); + MSG_WriteByte ((sizebuf_t*)demo.dbuf, client->lossage); + } +} + +void DemoBuffer_Init(dbuffer_t *dbuffer, byte *buf, size_t size) +{ + demobuffer = dbuffer; + + demobuffer->data = buf; + demobuffer->maxsize = size; + demobuffer->start = 0; + demobuffer->end = 0; + demobuffer->last = 0; +} + +/* +============== +Demo_SetMsgBuf + +Sets the frame message buffer +============== +*/ + +void DemoSetMsgBuf(demobuf_t *prev,demobuf_t *cur) +{ + // fix the maxsize of previous msg buffer, + // we won't be able to write there anymore + if (prev != NULL) + prev->maxsize = prev->bufsize; + + demo.dbuf = cur; + memset(demo.dbuf, 0, sizeof(*demo.dbuf)); + + demo.dbuf->data = demobuffer->data + demobuffer->end; + demo.dbuf->maxsize = MAXSIZE; +} + +/* +============== +DemoWriteToDisk + +Writes to disk a message meant for specifc client +or all messages if type == 0 +Message is cleared from demobuf after that +============== +*/ + +void SV_DemoWriteToDisk(int type, int to, float time) +{ + int pos = 0, oldm, oldd; + header_t *p; + int size; + sizebuf_t msg; + + (byte*)p = demo.dbuf->data; + demo.dbuf->h = NULL; + + oldm = demo.dbuf->bufsize; + oldd = demobuffer->start; + while (pos < demo.dbuf->bufsize) + { + size = p->size; + pos += header + size; + + // no type means we are writing to disk everything + if (!type || (p->type == type && p->to == to)) + { + if (size) { + msg.data = p->data; + msg.cursize = size; + + SV_WriteDemoMessage(&msg, p->type, p->to, time); + } + + // data is written so it need to be cleard from demobuf + if (demo.dbuf->data != (byte*)p) + memmove(demo.dbuf->data + size + header, demo.dbuf->data, (byte*)p - demo.dbuf->data); + + demo.dbuf->bufsize -= size + header; + demo.dbuf->data += size + header; + pos -= size + header; + demo.dbuf->maxsize -= size + header; + demobuffer->start += size + header; + } + // move along + (byte*)p = p->data + size; + } + + if (demobuffer->start == demobuffer->last) { + if (demobuffer->start == demobuffer->end) { + demobuffer->end = 0; // demobuffer is empty + demo.dbuf->data = demobuffer->data; + } + + // go back to begining of the buffer + demobuffer->last = demobuffer->end; + demobuffer->start = 0; + } +} + +/* +============== +DemoSetBuf + +Sets position in the buf for writing to specific client +============== +*/ + +static void DemoSetBuf(byte type, int to) +{ + header_t *p; + int pos = 0; + + (byte*)p = demo.dbuf->data; + + while (pos < demo.dbuf->bufsize) + { + pos += header + p->size; + + if (type == p->type && to == p->to && !p->full) + { + demo.dbuf->cursize = pos; + demo.dbuf->h = p; + return; + } + + (byte*)p = p->data + p->size; + } + // type&&to not exist in the buf, so add it + + p->type = type; + p->to = to; + p->size = 0; + p->full = 0; + + demo.dbuf->bufsize += header; + demo.dbuf->cursize = demo.dbuf->bufsize; + demobuffer->end += header; + demo.dbuf->h = p; +} + +void DemoMoveBuf(void) +{ + // set the last message mark to the previous frame (i/e begining of this one) + demobuffer->last = demobuffer->end - demo.dbuf->bufsize; + + // move buffer to the begining of demo buffer + memmove(demobuffer->data, demo.dbuf->data, demo.dbuf->bufsize); + demo.dbuf->data = demobuffer->data; + demobuffer->end = demo.dbuf->bufsize; + demo.dbuf->h = NULL; // it will be setup again + demo.dbuf->maxsize = MAXSIZE + demo.dbuf->bufsize; +} + +void DemoWrite_Begin(byte type, int to, int size) +{ + byte *p; + qboolean move = false; + + // will it fit? + while (demo.dbuf->bufsize + size + header > demo.dbuf->maxsize) + { + // if we reached the end of buffer move msgbuf to the begining + if (!move && demobuffer->end > demobuffer->start) + move = true; + + SV_DemoWritePackets(1); + if (move && demobuffer->start > demo.dbuf->bufsize + header + size) + DemoMoveBuf(); + } + + if (demo.dbuf->h == NULL || demo.dbuf->h->type != type || demo.dbuf->h->to != to || demo.dbuf->h->full) { + DemoSetBuf(type, to); + } + + if (demo.dbuf->h->size + size > MAX_MSGLEN) + { + demo.dbuf->h->full = 1; + DemoSetBuf(type, to); + } + + // we have to make room for new data + if (demo.dbuf->cursize != demo.dbuf->bufsize) { + p = demo.dbuf->data + demo.dbuf->cursize; + memmove(p+size, p, demo.dbuf->bufsize - demo.dbuf->cursize); + } + + demo.dbuf->bufsize += size; + demo.dbuf->h->size += size; + if ((demobuffer->end += size) > demobuffer->last) + demobuffer->last = demobuffer->end; +} + +/* +==================== +SV_WriteDemoMessage + +Dumps the current net message, prefixed by the length and view angles +==================== +*/ +void SV_WriteDemoMessage (sizebuf_t *msg, int type, int to, float time) +{ + int len, i, msec; + byte c; + static double prevtime; + + if (!sv.demorecording) + return; + + msec = (time - prevtime)*1000; + prevtime += msec*0.001; + if (msec > 255) msec = 255; + if (msec < 2) msec = 0; + + c = msec; + demo.size += DWRITE(&c, sizeof(c), 1, demo.dest); + + if (demo.lasttype != type || demo.lastto != to) + { + demo.lasttype = type; + demo.lastto = to; + switch (demo.lasttype) + { + case dem_all: + c = dem_all; + demo.size += DWRITE (&c, sizeof(c), 1, demo.dest); + break; + case dem_multiple: + c = dem_multiple; + demo.size += DWRITE (&c, sizeof(c), 1, demo.dest); + + i = LittleLong(demo.lastto); + demo.size += DWRITE (&i, sizeof(i), 1, demo.dest); + break; + case dem_single: + case dem_stats: + c = demo.lasttype + (demo.lastto << 3); + demo.size += DWRITE (&c, sizeof(c), 1, demo.dest); + break; + default: + SV_Stop_f (); + Con_Printf("bad demo message type:%d", type); + return; + } + } else { + c = dem_read; + demo.size += DWRITE (&c, sizeof(c), 1, demo.dest); + } + + + len = LittleLong (msg->cursize); + demo.size += DWRITE (&len, 4, 1, demo.dest); + demo.size += DWRITE (msg->data, msg->cursize, 1, demo.dest); + + if (demo.disk) + fflush (demo.file); + else if (demo.size - demo_size > demo_max_size) + { + demo_size = demo.size; + demo.mfile -= 0x80000; + fwrite(svs.demomem, 1, 0x80000, demo.file); + fflush(demo.file); + memmove(svs.demomem, svs.demomem + 0x80000, demo.size - 0x80000); + } +} + + +/* +==================== +SV_DemoWritePackets + +Interpolates to get exact players position for current frame +and writes packets to the disk/memory +==================== +*/ + +float adjustangle(float current, float ideal, float fraction) +{ + float move; + + move = ideal - current; + if (ideal > current) + { + + if (move >= 180) + move = move - 360; + } + else + { + if (move <= -180) + move = move + 360; + } + + move *= fraction; + + return (current + move); +} + +#define DF_ORIGIN 1 +#define DF_ANGLES (1<<3) +#define DF_EFFECTS (1<<6) +#define DF_SKINNUM (1<<7) +#define DF_DEAD (1<<8) +#define DF_GIB (1<<9) +#define DF_WEAPONFRAME (1<<10) +#define DF_MODEL (1<<11) + +void SV_DemoWritePackets (int num) +{ + demo_frame_t *frame, *nextframe; + demo_client_t *cl, *nextcl; + int i, j, flags; + qboolean valid; + double time, playertime, nexttime; + float f; + vec3_t origin, angles; + sizebuf_t msg; + byte msg_buf[MAX_MSGLEN]; + demoinfo_t *demoinfo; + + if (!sv.demorecording) + return; + + msg.data = msg_buf; + msg.maxsize = sizeof(msg_buf); + + if (num > demo.parsecount - demo.lastwritten + 1) + num = demo.parsecount - demo.lastwritten + 1; + + // 'num' frames to write + for ( ; num; num--, demo.lastwritten++) + { + frame = &demo.frames[demo.lastwritten&DEMO_FRAMES_MASK]; + time = frame->time; + nextframe = frame; + msg.cursize = 0; + + demo.dbuf = &frame->buf; + + // find two frames + // one before the exact time (time - msec) and one after, + // then we can interpolte exact position for current frame + for (i = 0, cl = frame->clients, demoinfo = demo.info; i < MAX_CLIENTS; i++, cl++, demoinfo++) + { + if (cl->parsecount != demo.lastwritten) + continue; // not valid + + nexttime = playertime = time - cl->sec; + + for (j = demo.lastwritten+1, valid = false; nexttime < time && j < demo.parsecount; j++) + { + nextframe = &demo.frames[j&DEMO_FRAMES_MASK]; + nextcl = &nextframe->clients[i]; + + if (nextcl->parsecount != j) + break; // disconnected? + if (nextcl->fixangle) + break; // respawned, or walked into teleport, do not interpolate! + if (!(nextcl->flags & DF_DEAD) && (cl->flags & DF_DEAD)) + break; // respawned, do not interpolate + + nexttime = nextframe->time - nextcl->sec; + + if (nexttime >= time) + { + // good, found what we were looking for + valid = true; + break; + } + } + + if (valid) + { + f = (time - nexttime)/(nexttime - playertime); + for (j=0;j<3;j++) { + angles[j] = adjustangle(cl->info.angles[j], nextcl->info.angles[j],1.0+f); + origin[j] = nextcl->info.origin[j] + f*(nextcl->info.origin[j]-cl->info.origin[j]); + } + } else { + VectorCopy(cl->info.origin, origin); + VectorCopy(cl->info.angles, angles); + } + + // now write it to buf + flags = cl->flags; + + if (cl->fixangle) { + demo.fixangletime[i] = cl->cmdtime; + } + + for (j=0; j < 3; j++) + if (origin[j] != demoinfo->origin[i]) + flags |= DF_ORIGIN << j; + + if (cl->fixangle || demo.fixangletime[i] != cl->cmdtime) + { + for (j=0; j < 3; j++) + if (angles[j] != demoinfo->angles[j]) + flags |= DF_ANGLES << j; + } + + if (cl->info.model != demoinfo->model) + flags |= DF_MODEL; + if (cl->info.effects != demoinfo->effects) + flags |= DF_EFFECTS; + if (cl->info.skinnum != demoinfo->skinnum) + flags |= DF_SKINNUM; + if (cl->info.weaponframe != demoinfo->weaponframe) + flags |= DF_WEAPONFRAME; + + MSG_WriteByte (&msg, svc_playerinfo); + MSG_WriteByte (&msg, i); + MSG_WriteShort (&msg, flags); + + MSG_WriteByte (&msg, cl->frame); + + for (j=0 ; j<3 ; j++) + if (flags & (DF_ORIGIN << j)) + MSG_WriteCoord (&msg, origin[j]); + + for (j=0 ; j<3 ; j++) + if (flags & (DF_ANGLES << j)) + MSG_WriteAngle16 (&msg, angles[j]); + + + if (flags & DF_MODEL) + MSG_WriteByte (&msg, cl->info.model); + + if (flags & DF_SKINNUM) + MSG_WriteByte (&msg, cl->info.skinnum); + + if (flags & DF_EFFECTS) + MSG_WriteByte (&msg, cl->info.effects); + + if (flags & DF_WEAPONFRAME) + MSG_WriteByte (&msg, cl->info.weaponframe); + + VectorCopy(cl->info.origin, demoinfo->origin); + VectorCopy(cl->info.angles, demoinfo->angles); + demoinfo->skinnum = cl->info.skinnum; + demoinfo->effects = cl->info.effects; + demoinfo->weaponframe = cl->info.weaponframe; + demoinfo->model = cl->info.model; + } + + SV_DemoWriteToDisk(demo.lasttype,demo.lastto, (float)time); // this goes first to reduce demo size a bit + SV_DemoWriteToDisk(0,0, (float)time); // now goes the rest + if (msg.cursize) + SV_WriteDemoMessage(&msg, dem_all, 0, (float)time); + } + + if (demo.lastwritten > demo.parsecount) + demo.lastwritten = demo.parsecount; + + demo.dbuf = &demo.frames[demo.parsecount&DEMO_FRAMES_MASK].buf; + demo.dbuf->maxsize = MAXSIZE + demo.dbuf->bufsize; +} + +size_t memwrite ( const void *buffer, size_t size, size_t count, byte **mem) +{ + int i,c = count; + const byte *buf; + + for (;count; count--) + for (i = size, buf = buffer; i; i--) + *(*mem)++ = *buf++; + + return c; +} + +static char chartbl[256]; +void CleanName_Init (); + +void Demo_Init (void) +{ + int p, size = MIN_DEMO_MEMORY; + + Cvar_RegisterVariable (&sv_demofps); + Cvar_RegisterVariable (&sv_demoPings); + Cvar_RegisterVariable (&sv_demoNoVis); + Cvar_RegisterVariable (&sv_demoUseCache); + Cvar_RegisterVariable (&sv_demoCacheSize); + Cvar_RegisterVariable (&sv_demoMaxSize); + Cvar_RegisterVariable (&sv_demoMaxDirSize); + Cvar_RegisterVariable (&sv_demoDir); + Cvar_RegisterVariable (&sv_demoPrefix); + Cvar_RegisterVariable (&sv_demoSuffix); + Cvar_RegisterVariable (&sv_onrecordfinish); + + p = COM_CheckParm ("-democache"); + if (p) + { + if (p < com_argc-1) + size = Q_atoi (com_argv[p+1]) * 1024; + else + Sys_Error ("Memory_Init: you must specify a size in KB after -democache"); + } + + if (size < MIN_DEMO_MEMORY) + { + Con_Printf("Minimum memory size for demo cache is %dk\n", MIN_DEMO_MEMORY / 1024); + size = MIN_DEMO_MEMORY; + } + + svs.demomem = Hunk_AllocName ( size, "demo" ); + svs.demomemsize = size; + demo_max_size = size - 0x80000; + Cvar_SetROM(&sv_demoCacheSize, va("%d", size/1024)); + CleanName_Init(); +} + +/* +==================== +SV_InitRecord +==================== +*/ + +qboolean SV_InitRecord(void) +{ + if (!USACACHE) + { + dwrite = &fwrite; + demo.dest = demo.file; + demo.disk = true; + } else + { + dwrite = &memwrite; + demo.mfile = svs.demomem; + demo.dest = &demo.mfile; + } + + demo_size = 0; + + return true; +} + +/* +==================== +SV_Stop + +stop recording a demo +==================== +*/ +void SV_Stop (int reason) +{ + if (!sv.demorecording) + { + Con_Printf ("Not recording a demo.\n"); + return; + } + + if (reason == 2) + { + // stop and remove + if (demo.disk) + fclose(demo.file); + + Sys_remove(demo.namelong); + + demo.file = NULL; + sv.demorecording = false; + + SV_BroadcastPrintf (PRINT_CHAT, "Server recording canceled, demo removed\n"); + + Cvar_SetROM(&serverdemo, ""); + + return; + } +// write a disconnect message to the demo file + + // clearup to be sure message will fit + demo.dbuf->cursize = 0; + demo.dbuf->h = NULL; + demo.dbuf->bufsize = 0; + DemoWrite_Begin(dem_all, 0, 2+strlen("EndOfDemo")); + MSG_WriteByte ((sizebuf_t*)demo.dbuf, svc_disconnect); + MSG_WriteString ((sizebuf_t*)demo.dbuf, "EndOfDemo"); + + SV_DemoWritePackets(demo.parsecount - demo.lastwritten + 1); +// finish up + if (!demo.disk) + { + fwrite(svs.demomem, 1, demo.size - demo_size, demo.file); + fflush(demo.file); + } + + fclose (demo.file); + + demo.file = NULL; + sv.demorecording = false; + if (!reason) + SV_BroadcastPrintf (PRINT_CHAT, "Server recording completed\n"); + else + SV_BroadcastPrintf (PRINT_CHAT, "Server recording stoped\nMax demo size exceeded\n"); + + if (sv_onrecordfinish.string[0]) + { + Cmd_TokenizeString(va("blah %s \"%s/%s\"", sv_onrecordfinish.string, sv_demoDir.string, serverdemo.string)); + SV_Script_f(); + } + + Cvar_SetROM(&serverdemo, ""); +} + +/* +==================== +SV_Stop_f +==================== +*/ +void SV_Stop_f (void) +{ + SV_Stop(0); +} + +/* +==================== +SV_Cancel_f + +Stops recording, and removes the demo +==================== +*/ +void SV_Cancel_f (void) +{ + SV_Stop(2); +} + +/* +==================== +SV_WriteDemoMessage + +Dumps the current net message, prefixed by the length and view angles +==================== +*/ + +void SV_WriteRecordDemoMessage (sizebuf_t *msg, int seq) +{ + int len; + byte c; + + if (!sv.demorecording) + return; + + c = 0; + demo.size += DWRITE (&c, sizeof(c), 1, demo.dest); + + c = dem_read; + demo.size += DWRITE (&c, sizeof(c), 1, demo.dest); + + len = LittleLong (msg->cursize); + demo.size += DWRITE (&len, 4, 1, demo.dest); + + demo.size += DWRITE (msg->data, msg->cursize, 1, demo.dest); + + if (demo.disk) + fflush (demo.file); +} + +void SV_WriteSetDemoMessage (void) +{ + int len; + byte c; + +//Con_Printf("write: %ld bytes, %4.4f\n", msg->cursize, realtime); + + if (!sv.demorecording) + return; + + c = 0; + demo.size += DWRITE (&c, sizeof(c), 1, demo.dest); + + c = dem_set; + demo.size += DWRITE (&c, sizeof(c), 1, demo.dest); + + + len = LittleLong(0); + demo.size += DWRITE (&len, 4, 1, demo.dest); + len = LittleLong(0); + demo.size += DWRITE (&len, 4, 1, demo.dest); + + if (demo.disk) + fflush (demo.file); +} + +static void SV_Record (void) +{ + sizebuf_t buf; + char buf_data[MAX_MSGLEN]; + int n, i; + char *s, info[MAX_INFO_STRING]; + + client_t *player; + char *gamedir; + int seq = 1; + + sv.demorecording = true; + demo.pingtime = demo.time = sv.time; + +/*-------------------------------------------------*/ + +// serverdata + // send the info about the new client to all connected clients + memset(&buf, 0, sizeof(buf)); + buf.data = buf_data; + buf.maxsize = sizeof(buf_data); + +// send the serverdata + + gamedir = Info_ValueForKey (svs.info, "*gamedir"); + if (!gamedir[0]) + gamedir = "qw"; + + MSG_WriteByte (&buf, svc_serverdata); + MSG_WriteLong (&buf, PROTOCOL_VERSION); + MSG_WriteLong (&buf, svs.spawncount); + MSG_WriteString (&buf, gamedir); + + + MSG_WriteFloat (&buf, sv.time); + + // send full levelname + MSG_WriteString (&buf, PR_GetString(sv.edicts->v.message)); + + // send the movevars + MSG_WriteFloat(&buf, movevars.gravity); + MSG_WriteFloat(&buf, movevars.stopspeed); + MSG_WriteFloat(&buf, movevars.maxspeed); + MSG_WriteFloat(&buf, movevars.spectatormaxspeed); + MSG_WriteFloat(&buf, movevars.accelerate); + MSG_WriteFloat(&buf, movevars.airaccelerate); + MSG_WriteFloat(&buf, movevars.wateraccelerate); + MSG_WriteFloat(&buf, movevars.friction); + MSG_WriteFloat(&buf, movevars.waterfriction); + MSG_WriteFloat(&buf, movevars.entgravity); + + // send music + MSG_WriteByte (&buf, svc_cdtrack); + MSG_WriteByte (&buf, 0); // none in demos + + // send server info string + MSG_WriteByte (&buf, svc_stufftext); + MSG_WriteString (&buf, va("fullserverinfo \"%s\"\n", svs.info) ); + + // flush packet + SV_WriteRecordDemoMessage (&buf, seq++); + SZ_Clear (&buf); + +// soundlist + MSG_WriteByte (&buf, svc_soundlist); + MSG_WriteByte (&buf, 0); + + n = 0; + s = sv.sound_precache[n+1]; + while (s) { + MSG_WriteString (&buf, s); + if (buf.cursize > MAX_MSGLEN/2) { + MSG_WriteByte (&buf, 0); + MSG_WriteByte (&buf, n); + SV_WriteRecordDemoMessage (&buf, seq++); + SZ_Clear (&buf); + MSG_WriteByte (&buf, svc_soundlist); + MSG_WriteByte (&buf, n + 1); + } + n++; + s = sv.sound_precache[n+1]; + } + + if (buf.cursize) { + MSG_WriteByte (&buf, 0); + MSG_WriteByte (&buf, 0); + SV_WriteRecordDemoMessage (&buf, seq++); + SZ_Clear (&buf); + } + +// modellist + MSG_WriteByte (&buf, svc_modellist); + MSG_WriteByte (&buf, 0); + + n = 0; + s = sv.model_precache[n+1]; + while (s) { + MSG_WriteString (&buf, s); + if (buf.cursize > MAX_MSGLEN/2) { + MSG_WriteByte (&buf, 0); + MSG_WriteByte (&buf, n); + SV_WriteRecordDemoMessage (&buf, seq++); + SZ_Clear (&buf); + MSG_WriteByte (&buf, svc_modellist); + MSG_WriteByte (&buf, n + 1); + } + n++; + s = sv.model_precache[n+1]; + } + if (buf.cursize) { + MSG_WriteByte (&buf, 0); + MSG_WriteByte (&buf, 0); + SV_WriteRecordDemoMessage (&buf, seq++); + SZ_Clear (&buf); + } + +// prespawn + + for (n = 0; n < sv.num_signon_buffers; n++) + { + SZ_Write (&buf, + sv.signon_buffers[n], + sv.signon_buffer_size[n]); + + if (buf.cursize > MAX_MSGLEN/2) { + SV_WriteRecordDemoMessage (&buf, seq++); + SZ_Clear (&buf); + } + } + + MSG_WriteByte (&buf, svc_stufftext); + MSG_WriteString (&buf, va("cmd spawn %i 0\n",svs.spawncount) ); + + if (buf.cursize) { + SV_WriteRecordDemoMessage (&buf, seq++); + SZ_Clear (&buf); + } + +// send current status of all other players + + for (i = 0; i < MAX_CLIENTS; i++) { + player = svs.clients + i; + + MSG_WriteByte (&buf, svc_updatefrags); + MSG_WriteByte (&buf, i); + MSG_WriteShort (&buf, player->old_frags); + + MSG_WriteByte (&buf, svc_updateping); + MSG_WriteByte (&buf, i); + MSG_WriteShort (&buf, SV_CalcPing(player)); + + MSG_WriteByte (&buf, svc_updatepl); + MSG_WriteByte (&buf, i); + MSG_WriteByte (&buf, player->lossage); + + MSG_WriteByte (&buf, svc_updateentertime); + MSG_WriteByte (&buf, i); + MSG_WriteFloat (&buf, realtime - player->connection_started); + + strcpy (info, player->userinfo); + Info_RemovePrefixedKeys (info, '_'); // server passwords, etc + + MSG_WriteByte (&buf, svc_updateuserinfo); + MSG_WriteByte (&buf, i); + MSG_WriteLong (&buf, player->userid); + MSG_WriteString (&buf, info); + + if (buf.cursize > MAX_MSGLEN/2) { + SV_WriteRecordDemoMessage (&buf, seq++); + SZ_Clear (&buf); + } + } + +// send all current light styles + for (i=0 ; ifilename translation +==================== +*/ + +void CleanName_Init () +{ + int i; + + for (i = 0; i < 256; i++) + chartbl[i] = (((i&127) < 'a' || (i&127) > 'z') && ((i&127) < '0' || (i&127) > '9')) ? '_' : (i&127); + + // special cases + + // numbers + for (i = 18; i < 29; i++) + chartbl[i] = chartbl[i + 128] = i + 30; + + // allow lowercase only + for (i = 'A'; i <= 'Z'; i++) + chartbl[i] = chartbl[i+128] = i + 'a' - 'A'; + + // brackets + chartbl[29] = chartbl[29+128] = chartbl[128] = '('; + chartbl[31] = chartbl[31+128] = chartbl[130] = ')'; + chartbl[16] = chartbl[16 + 128]= '['; + chartbl[17] = chartbl[17 + 128] = ']'; + + // dot + chartbl[5] = chartbl[14] = chartbl[15] = chartbl[28] = chartbl[46] = '.'; + chartbl[5 + 128] = chartbl[14 + 128] = chartbl[15 + 128] = chartbl[28 + 128] = chartbl[46 + 128] = '.'; + + // ! + chartbl[33] = chartbl[33 + 128] = '!'; + + // # + chartbl[35] = chartbl[35 + 128] = '#'; + + // % + chartbl[37] = chartbl[37 + 128] = '%'; + + // & + chartbl[38] = chartbl[38 + 128] = '&'; + + // ' + chartbl[39] = chartbl[39 + 128] = '\''; + + // ( + chartbl[40] = chartbl[40 + 128] = '('; + + // ) + chartbl[41] = chartbl[41 + 128] = ')'; + + // + + chartbl[43] = chartbl[43 + 128] = '+'; + + // - + chartbl[45] = chartbl[45 + 128] = '-'; + + // @ + chartbl[64] = chartbl[64 + 128] = '@'; + + // ^ + chartbl[94] = chartbl[94 + 128] = '^'; + + + chartbl[91] = chartbl[91 + 128] = '['; + chartbl[93] = chartbl[93 + 128] = ']'; + + chartbl[16] = chartbl[16 + 128] = '['; + chartbl[17] = chartbl[17 + 128] = ']'; + + chartbl[123] = chartbl[123 + 128] = '{'; + chartbl[125] = chartbl[125 + 128] = '}'; +} + +/* +==================== +SV_CleanName + +Cleans the demo name, removes restricted chars, makes name lowercase +==================== +*/ + +char *SV_CleanName (unsigned char *name) +{ + static char text[1024]; + char *out = text; + + *out = chartbl[*name++]; + + while (*name) + if (*out == '_' && chartbl[*name] == '_') + name++; + else *++out = chartbl[*name++]; + + *++out = 0; + return text; +} + +/* +==================== +SV_Record_f + +record +==================== +*/ +void SV_Record_f (void) +{ + int c, i; + char name[MAX_OSPATH+MAX_DEMO_NAME], *s; + char newname[MAX_DEMO_NAME]; + dir_t dir; + + c = Cmd_Argc(); + if (c != 2) + { + Con_Printf ("record \n"); + return; + } + + if (sv.state != ss_active) { + Con_Printf ("Not active yet.\n"); + return; + } + + if (sv.demorecording) + SV_Stop_f(); + + dir = Sys_listdir(va("%s/%s", com_gamedir, sv_demoDir.string), ".*"); + if (sv_demoMaxDirSize.value && dir.size > sv_demoMaxDirSize.value*1024) + { + Con_Printf("insufficient directory space, increase sv_demoMaxDirSize\n"); + return; + } + + Q_strncpyz(newname, va("%s%s", sv_demoPrefix.string, SV_CleanName(Cmd_Argv(1))), sizeof(newname) - strlen(sv_demoSuffix.string) - 5); + strcat(newname, sv_demoSuffix.string); + + sprintf (name, "%s/%s/%s", com_gamedir, sv_demoDir.string, newname); + Sys_mkdir(va("%s/%s", com_gamedir, sv_demoDir.string)); + +// +// open the demo file +// + COM_ForceExtension (name, ".mvd"); + + memset(&demo, 0, sizeof(demo)); + for (i = 0; i < UPDATE_BACKUP; i++) + demo.recorder.frames[i].entities.entities = demo_entities[i]; + + DemoBuffer_Init(&demo.dbuffer, demo.buffer, sizeof(demo.buffer)); + DemoSetMsgBuf(NULL, &demo.frames[0].buf); + + demo.datagram.maxsize = sizeof(demo.datagram_data); + demo.datagram.data = demo.datagram_data; + + demo.file = fopen (name, "wb"); + if (!demo.file) + { + Con_Printf ("ERROR: couldn't open.\n"); + return; + } + + SV_InitRecord(); + + Q_strncpyz(demo.namelong, name, sizeof(demo.namelong)); + + s = name + strlen(name); + while (*s != '/') s--; + Q_strncpyz(demo.name, s+1, sizeof(demo.name)); + + SV_BroadcastPrintf (PRINT_CHAT, "Server starts recording (%s):\n%s\n", demo.disk ? "disk" : "memory", demo.name); + Cvar_SetROM(&serverdemo, demo.name); + SV_Record (); +} + +/* +==================== +SV_EasyRecord_f + +easyrecord [demoname] +==================== +*/ + +int Dem_CountPlayers () +{ + int i, count; + + count = 0; + for (i = 0; i < MAX_CLIENTS ; i++) { + if (svs.clients[i].name[0] && !svs.clients[i].spectator) + count++; + } + + return count; +} + +char *Dem_Team(int num) +{ + int i; + static char *lastteam[2]; + qboolean first = true; + client_t *client; + static int index = 0; + + index = 1 - index; + + for (i = 0, client = svs.clients; num && i < MAX_CLIENTS; i++, client++) + { + if (!client->name[0] || client->spectator) + continue; + + if (first || strcmp(lastteam[index], client->team)) + { + first = false; + num--; + lastteam[index] = client->team; + } + } + + if (num) + return ""; + + return lastteam[index]; +} + +char *Dem_PlayerName(int num) +{ + int i; + client_t *client; + + for (i = 0, client = svs.clients; i < MAX_CLIENTS; i++, client++) + { + if (!client->name[0] || client->spectator) + continue; + + if (!--num) + return client->name; + } + + return ""; +} + +void SV_EasyRecord_f (void) +{ + int c; + dir_t dir; + char name[1024], *s; + char name2[MAX_OSPATH*2]; + int i; + FILE *f; + + c = Cmd_Argc(); + if (c > 2) + { + Con_Printf ("easyrecord [demoname]\n"); + return; + } + + if (sv.demorecording) + SV_Stop_f(); + + dir = Sys_listdir(va("%s/%s", com_gamedir,sv_demoDir.string), ".*"); + if (sv_demoMaxDirSize.value && dir.size > sv_demoMaxDirSize.value*1024) + { + Con_Printf("insufficient directory space, increase sv_demoMaxDirSize\n"); + return; + } + + if (c == 2) + sprintf (name, "%s", Cmd_Argv(1)); + else { + // guess game type and write demo name + i = Dem_CountPlayers(); + if (teamplay.value && i > 2) + { + // Teamplay + sprintf (name, "team_%s_vs_%s_%s", + Dem_Team(1), + Dem_Team(2), + sv.name); + } else { + if (i == 2) { + // Duel + sprintf (name, "duel_%s_vs_%s_%s", + Dem_PlayerName(1), + Dem_PlayerName(2), + sv.name); + } else { + // FFA + sprintf (name, "ffa_%s(%d)", + sv.name, + i); + } + } + } + +// Make sure the filename doesn't contain illegal characters + Q_strncpyz(name, va("%s%s", sv_demoPrefix.string, SV_CleanName(name)), MAX_DEMO_NAME - strlen(sv_demoSuffix.string) - 7); + strcat(name, sv_demoSuffix.string); + sprintf (name, va("%s/%s/%s", com_gamedir, sv_demoDir.string, name)); + Sys_mkdir(va("%s/%s", com_gamedir, sv_demoDir.string)); + +// find a filename that doesn't exist yet + strcpy (name2, name); + COM_ForceExtension (name2, ".mvd"); + if ((f = fopen (name2, "rb")) == 0) + f = fopen(va("%s.gz", name2), "rb"); + + if (f) { + i = 1; + do { + fclose (f); + strcpy (name2, va("%s_%02i", name, i)); + COM_ForceExtension (name2, ".mvd"); + if ((f = fopen (name2, "rb")) == 0) + f = fopen(va("%s.gz", name2), "rb"); + i++; + } while (f); + } + +// +// open the demo file +// + memset(&demo, 0, sizeof(demo)); + for (i = 0; i < UPDATE_BACKUP; i++) + demo.recorder.frames[i].entities.entities = demo_entities[i]; + + DemoBuffer_Init(&demo.dbuffer, demo.buffer, sizeof(demo.buffer)); + DemoSetMsgBuf(NULL, &demo.frames[0].buf); + + demo.datagram.maxsize = sizeof(demo.datagram_data); + demo.datagram.data = demo.datagram_data; + + demo.file = fopen (name2, "wb"); + if (!demo.file) + { + Con_Printf ("ERROR: couldn't open %s\n", name2); + return; + } + + SV_InitRecord(); + + Q_strncpyz(demo.namelong, name2, sizeof(demo.namelong)); + + s = name2 + strlen(name2); + while (*s != '/') s--; + Q_strncpyz(demo.name, s+1, sizeof(demo.name)); + + SV_BroadcastPrintf (PRINT_CHAT, "Server starts recording (%s):\n%s\n", demo.disk ? "disk" : "memory", demo.name); + Cvar_SetROM(&serverdemo, demo.name); + SV_Record (); +} + +void SV_DemoList_f (void) +{ + dir_t dir; + file_t *list; + float f; + int i,j,show; + + Con_Printf("content of %s/%s/*.mvd\n", com_gamedir,sv_demoDir.string); + dir = Sys_listdir(va("%s/%s", com_gamedir,sv_demoDir.string), ".mvd"); + list = dir.files; + if (!list->name[0]) + { + Con_Printf("no demos\n"); + } + + for (i = 1; list->name[0]; i++, list++) + { + for (j = 1; j < Cmd_Argc(); j++) + if (strstr(list->name, Cmd_Argv(j)) == NULL) + break; + show = Cmd_Argc() == j; + + if (show) { + if (sv.demorecording && !strcmp(list->name, demo.name)) + Con_Printf("*%d: %s %dk\n", i, list->name, demo.size/1024); + else + Con_Printf("%d: %s %dk\n", i, list->name, list->size/1024); + } + } + + if (sv.demorecording) + dir.size += demo.size; + + Con_Printf("\ndirectory size: %.1fMB\n",(float)dir.size/(1024*1024)); + if (sv_demoMaxDirSize.value) { + f = (sv_demoMaxDirSize.value*1024 - dir.size)/(1024*1024); + if ( f < 0) + f = 0; + Con_Printf("space available: %.1fMB\n", f); + } +} + +char *SV_DemoNum(int num) +{ + file_t *list; + dir_t dir; + + dir = Sys_listdir(va("%s/%s", com_gamedir, sv_demoDir.string), ".mvd"); + list = dir.files; + + if (num <= 0) + return NULL; + + num--; + + while (list->name[0] && num) {list++; num--;}; + + if (list->name[0]) + return list->name; + + return NULL; +} + +void SV_DemoRemove_f (void) +{ + char name[MAX_DEMO_NAME], *ptr; + char path[MAX_OSPATH]; + int i; + + if (Cmd_Argc() != 2) + { + Con_Printf("rmdemo - removes the demo\nrmdemo * - removes demo with in the name\nrmdemo * - removes all demos\n"); + return; + } + + ptr = Cmd_Argv(1); + if (*ptr == '*') + { + dir_t dir; + file_t *list; + + // remove all demos with specified token + ptr++; + + dir = Sys_listdir(va("%s/%s", com_gamedir, sv_demoDir.string), ".mvd"); + list = dir.files; + for (i = 0;list->name[0]; list++) + { + if (strstr(list->name, ptr)) + { + if (sv.demorecording && !strcmp(list->name, demo.name)) + SV_Stop_f(); + + // stop recording first; + sprintf(path, "%s/%s/%s", com_gamedir, sv_demoDir.string, list->name); + if (!Sys_remove(path)) { + Con_Printf("removing %s...\n", list->name); + i++; + } + } + } + + if (i) { + Con_Printf("%d demos removed\n", i); + } else { + Con_Printf("no matching found\n"); + } + + return; + } + + Q_strncpyz(name ,Cmd_Argv(1), MAX_DEMO_NAME); + COM_DefaultExtension(name, ".mvd"); + + sprintf(path, "%s/%s/%s", com_gamedir, sv_demoDir.string, name); + + if (sv.demorecording && !strcmp(name, demo.name)) + SV_Stop_f(); + + if (!Sys_remove(path)) + Con_Printf("demo %s succesfully removed\n", name); + else + Con_Printf("unable to remove demo %s\n", name); +} + +void SV_DemoRemoveNum_f (void) +{ + int num; + char *val, *name; + char path[MAX_OSPATH]; + + if (Cmd_Argc() != 2) + { + Con_Printf("rmdemonum <#>\n"); + return; + } + + val = Cmd_Argv(1); + if ((num = atoi(val)) == 0 && val[0] != '0') + { + Con_Printf("rmdemonum <#>\n"); + return; + } + + name = SV_DemoNum(num); + + if (name != NULL) { + if (sv.demorecording && !strcmp(name, demo.name)) + SV_Stop_f(); + + sprintf(path, "%s/%s/%s", com_gamedir, sv_demoDir.string, name); + if (!Sys_remove(path)) + Con_Printf("demo %s succesfully removed\n", name); + else + Con_Printf("unable to remove demo %s\n", name); + } else + Con_Printf("invalid demo num\n"); +} diff --git a/source/sv_ents.c b/source/sv_ents.c index ca116def..e50c6df5 100644 --- a/source/sv_ents.c +++ b/source/sv_ents.c @@ -1,623 +1,622 @@ -/* -Copyright (C) 1996-1997 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 "qwsvdef.h" - -/* -============================================================================= - -The PVS must include a small area around the client to allow head bobbing -or other small motion on the client side. Otherwise, a bob might cause an -entity that should be visible to not show up, especially when the bob -crosses a waterline. - -============================================================================= -*/ - -int fatbytes; -byte fatpvs[MAX_MAP_LEAFS/8]; - -void SV_AddToFatPVS (vec3_t org, mnode_t *node) -{ - int i; - byte *pvs; - mplane_t *plane; - float d; - - while (1) - { - // if this is a leaf, accumulate the pvs bits - if (node->contents < 0) - { - if (node->contents != CONTENTS_SOLID) - { - pvs = Mod_LeafPVS ( (mleaf_t *)node, sv.worldmodel); - for (i=0 ; iplane; - d = DotProduct (org, plane->normal) - plane->dist; - if (d > 8) - node = node->children[0]; - else if (d < -8) - node = node->children[1]; - else - { // go down both - SV_AddToFatPVS (org, node->children[0]); - node = node->children[1]; - } - } -} - -/* -============= -SV_FatPVS - -Calculates a PVS that is the inclusive or of all leafs within 8 pixels of the -given point. -============= -*/ -byte *SV_FatPVS (vec3_t org) -{ - fatbytes = (sv.worldmodel->numleafs+31)>>3; - memset (fatpvs, 0, fatbytes); - SV_AddToFatPVS (org, sv.worldmodel->nodes); - return fatpvs; -} - -//============================================================================= - -// because there can be a lot of nails, there is a special -// network protocol for them -#define MAX_NAILS 32 -edict_t *nails[MAX_NAILS]; -int numnails; -int nailcount = 0; - -extern int sv_nailmodel, sv_supernailmodel, sv_playermodel; - -cvar_t sv_nailhack = {"sv_nailhack", "0"}; - - -qboolean SV_AddNailUpdate (edict_t *ent) -{ - if (sv_nailhack.value) - return false; - - if (ent->v.modelindex != sv_nailmodel - && ent->v.modelindex != sv_supernailmodel) - return false; - if (numnails == MAX_NAILS) - return true; - nails[numnails] = ent; - numnails++; - return true; -} - -void SV_EmitNailUpdate (sizebuf_t *msg, qboolean recorder) -{ - byte bits[6]; // [48 bits] xyzpy 12 12 12 4 8 - int n, i; - edict_t *ent; - int x, y, z, p, yaw; - - if (!numnails) - return; - - if (recorder) - MSG_WriteByte (msg, svc_nails2); - else - MSG_WriteByte (msg, svc_nails); - - MSG_WriteByte (msg, numnails); - - for (n=0 ; nv.colormap) { - if (!((++nailcount)&255)) nailcount++; - ent->v.colormap = nailcount&255; - } - - MSG_WriteByte (msg, (byte)ent->v.colormap); - } - - x = (int)(ent->v.origin[0]+4096)>>1; - y = (int)(ent->v.origin[1]+4096)>>1; - z = (int)(ent->v.origin[2]+4096)>>1; - p = (int)(16*ent->v.angles[0]/360)&15; - yaw = (int)(256*ent->v.angles[1]/360)&255; - - bits[0] = x; - bits[1] = (x>>8) | (y<<4); - bits[2] = (y>>4); - bits[3] = z; - bits[4] = (z>>8) | (p<<4); - bits[5] = yaw; - - for (i=0 ; i<6 ; i++) - MSG_WriteByte (msg, bits[i]); - } -} - -//============================================================================= - - -/* -================== -SV_WriteDelta - -Writes part of a packetentities message. -Can delta from either a baseline or a previous packet_entity -================== -*/ -void SV_WriteDelta (entity_state_t *from, entity_state_t *to, sizebuf_t *msg, qboolean force) -{ - int bits; - int i; - float miss; - -// send an update - bits = 0; - - for (i=0 ; i<3 ; i++) - { - miss = to->origin[i] - from->origin[i]; - if ( miss < -0.1 || miss > 0.1 ) - bits |= U_ORIGIN1<angles[0] != from->angles[0] ) - bits |= U_ANGLE1; - - if ( to->angles[1] != from->angles[1] ) - bits |= U_ANGLE2; - - if ( to->angles[2] != from->angles[2] ) - bits |= U_ANGLE3; - - if ( to->colormap != from->colormap ) - bits |= U_COLORMAP; - - if ( to->skinnum != from->skinnum ) - bits |= U_SKIN; - - if ( to->frame != from->frame ) - bits |= U_FRAME; - - if ( to->effects != from->effects ) - bits |= U_EFFECTS; - - if ( to->modelindex != from->modelindex ) - bits |= U_MODEL; - - if (bits & 511) - bits |= U_MOREBITS; - - if (to->flags & U_SOLID) - bits |= U_SOLID; - - // - // write the message - // - if (!to->number) - SV_Error ("Unset entity number"); - if (to->number >= 512) - SV_Error ("Entity number >= 512"); - - if (!bits && !force) - return; // nothing to send! - i = to->number | (bits&~511); - if (i & U_REMOVE) - Sys_Error ("U_REMOVE"); - MSG_WriteShort (msg, i); - - if (bits & U_MOREBITS) - MSG_WriteByte (msg, bits&255); - if (bits & U_MODEL) - MSG_WriteByte (msg, to->modelindex); - if (bits & U_FRAME) - MSG_WriteByte (msg, to->frame); - if (bits & U_COLORMAP) - MSG_WriteByte (msg, to->colormap); - if (bits & U_SKIN) - MSG_WriteByte (msg, to->skinnum); - if (bits & U_EFFECTS) - MSG_WriteByte (msg, to->effects); - if (bits & U_ORIGIN1) - MSG_WriteCoord (msg, to->origin[0]); - if (bits & U_ANGLE1) - MSG_WriteAngle(msg, to->angles[0]); - if (bits & U_ORIGIN2) - MSG_WriteCoord (msg, to->origin[1]); - if (bits & U_ANGLE2) - MSG_WriteAngle(msg, to->angles[1]); - if (bits & U_ORIGIN3) - MSG_WriteCoord (msg, to->origin[2]); - if (bits & U_ANGLE3) - MSG_WriteAngle(msg, to->angles[2]); -} - -/* -============= -SV_EmitPacketEntities - -Writes a delta update of a packet_entities_t to the message. - -============= -*/ -void SV_EmitPacketEntities (client_t *client, packet_entities_t *to, sizebuf_t *msg) -{ - edict_t *ent; - client_frame_t *fromframe; - packet_entities_t *from; - int oldindex, newindex; - int oldnum, newnum; - int oldmax; - - // this is the frame that we are going to delta update from - if (client->delta_sequence != -1) - { - fromframe = &client->frames[client->delta_sequence & UPDATE_MASK]; - from = &fromframe->entities; - oldmax = from->num_entities; - - MSG_WriteByte (msg, svc_deltapacketentities); - MSG_WriteByte (msg, client->delta_sequence); - } - else - { - oldmax = 0; // no delta update - from = NULL; - - MSG_WriteByte (msg, svc_packetentities); - } - - newindex = 0; - oldindex = 0; -//Con_Printf ("---%i to %i ----\n", client->delta_sequence & UPDATE_MASK -// , client->netchan.outgoing_sequence & UPDATE_MASK); - while (newindex < to->num_entities || oldindex < oldmax) - { - newnum = newindex >= to->num_entities ? 9999 : to->entities[newindex].number; - oldnum = oldindex >= oldmax ? 9999 : from->entities[oldindex].number; - - if (newnum == oldnum) - { // delta update from old position -//Con_Printf ("delta %i\n", newnum); - SV_WriteDelta (&from->entities[oldindex], &to->entities[newindex], msg, false); - oldindex++; - newindex++; - continue; - } - - if (newnum < oldnum) - { // this is a new entity, send it from the baseline - ent = EDICT_NUM(newnum); -//Con_Printf ("baseline %i\n", newnum); - SV_WriteDelta (&ent->baseline, &to->entities[newindex], msg, true); - newindex++; - continue; - } - - if (newnum > oldnum) - { // the old entity isn't present in the new message -//Con_Printf ("remove %i\n", oldnum); - MSG_WriteShort (msg, oldnum | U_REMOVE); - oldindex++; - continue; - } - } - - MSG_WriteShort (msg, 0); // end of packetentities -} - -/* -============= -SV_WritePlayersToClient -============= -*/ - -#define DF_ORIGIN 1 -#define DF_ANGLES (1<<3) -#define DF_EFFECTS (1<<6) -#define DF_SKINNUM (1<<7) -#define DF_DEAD (1<<8) -#define DF_GIB (1<<9) -#define DF_WEAPONFRAME (1<<10) -#define DF_MODEL (1<<11) - -void SV_WritePlayersToClient (client_t *client, edict_t *clent, byte *pvs, sizebuf_t *msg) -{ - int i, j; - client_t *cl; - edict_t *ent; - int msec; - usercmd_t cmd; - int pflags; - int dflags; - demoinfo_t demoinfo; - demo_frame_t *demo_frame; - demo_client_t *dcl; - - demo_frame = &demo.frames[demo.parsecount&DEMO_FRAMES_MASK]; - - for (j=0,cl=svs.clients, dcl = demo_frame->clients; jstate != cs_spawned) - continue; - - ent = cl->edict; - - if (clent == NULL) - { - if (cl->spectator) - continue; - - dcl->parsecount = demo.parsecount; - - VectorCopy(ent->v.origin, dcl->info.origin); - VectorCopy(ent->v.angles, dcl->info.angles); - dcl->info.angles[0] *= -3; - dcl->info.angles[2] = 0; // no roll angle - - if (ent->v.health <= 0) - { // don't show the corpse looking around... - dcl->info.angles[0] = 0; - dcl->info.angles[1] = ent->v.angles[1]; - dcl->info.angles[2] = 0; - } - - dcl->info.skinnum = ent->v.skin; - dcl->info.effects = ent->v.effects; - dcl->info.weaponframe = ent->v.weaponframe; - dcl->info.model = ent->v.modelindex; - dcl->sec = sv.time - cl->localtime; - dcl->frame = ent->v.frame; - dcl->flags = 0; - dcl->cmdtime = cl->localtime; - dcl->fixangle = demo.fixangle[j]; - demo.fixangle[j] = 0; - - if (ent->v.health <= 0) - dcl->flags |= DF_DEAD; - if (ent->v.mins[2] != -24) - dcl->flags |= DF_GIB; - continue; - } - - - // ZOID visibility tracking - if (ent != clent && - !(client->spec_track && client->spec_track - 1 == j)) - { - if (cl->spectator) - continue; - - // ignore if not touching a PV leaf - for (i=0 ; i < ent->num_leafs ; i++) - if (pvs[ent->leafnums[i] >> 3] & (1 << (ent->leafnums[i]&7) )) - break; - if (i == ent->num_leafs) - continue; // not visable - } - - pflags = PF_MSEC | PF_COMMAND; - - if (ent->v.modelindex != sv_playermodel) - pflags |= PF_MODEL; - for (i=0 ; i<3 ; i++) - if (ent->v.velocity[i]) - pflags |= PF_VELOCITY1<v.effects) - pflags |= PF_EFFECTS; - if (ent->v.skin) - pflags |= PF_SKINNUM; - if (ent->v.health <= 0) - pflags |= PF_DEAD; - if (ent->v.mins[2] != -24) - pflags |= PF_GIB; - - if (cl->spectator) - { // only sent origin and velocity to spectators - pflags &= PF_VELOCITY1 | PF_VELOCITY2 | PF_VELOCITY3; - } - else if (ent == clent) - { // don't send a lot of data on personal entity - pflags &= ~(PF_MSEC|PF_COMMAND); - if (ent->v.weaponframe) - pflags |= PF_WEAPONFRAME; - } - - if (client->spec_track && client->spec_track - 1 == j && - ent->v.weaponframe) - pflags |= PF_WEAPONFRAME; - - MSG_WriteByte (msg, svc_playerinfo); - MSG_WriteByte (msg, j); - MSG_WriteShort (msg, pflags); - - for (i=0 ; i<3 ; i++) - MSG_WriteCoord (msg, ent->v.origin[i]); - - MSG_WriteByte (msg, ent->v.frame); - - if (pflags & PF_MSEC) - { - msec = 1000*(sv.time - cl->localtime); - if (msec > 255) - msec = 255; - MSG_WriteByte (msg, msec); - } - - if (pflags & PF_COMMAND) - { - cmd = cl->lastcmd; - - if (ent->v.health <= 0) - { // don't show the corpse looking around... - cmd.angles[0] = 0; - cmd.angles[1] = ent->v.angles[1]; - cmd.angles[0] = 0; - } - - cmd.buttons = 0; // never send buttons - cmd.impulse = 0; // never send impulses - - MSG_WriteDeltaUsercmd (msg, &nullcmd, &cmd); - } - - for (i=0 ; i<3 ; i++) - if (pflags & (PF_VELOCITY1<v.velocity[i]); - - if (pflags & PF_MODEL) - MSG_WriteByte (msg, ent->v.modelindex); - - if (pflags & PF_SKINNUM) - MSG_WriteByte (msg, ent->v.skin); - - if (pflags & PF_EFFECTS) - MSG_WriteByte (msg, ent->v.effects); - - if (pflags & PF_WEAPONFRAME) - MSG_WriteByte (msg, ent->v.weaponframe); - } -} - - -/* -============= -SV_WriteEntitiesToClient - -Encodes the current state of the world as -a svc_packetentities messages and possibly -a svc_nails message and -svc_playerinfo messages -============= -*/ - -void SV_WriteEntitiesToClient (client_t *client, sizebuf_t *msg, qboolean recorder) -{ - int e, i, max_packet_entities = OLD_MAX_PACKET_ENTITIES; - byte *pvs; - vec3_t org; - edict_t *ent; - packet_entities_t *pack; - edict_t *clent; - client_frame_t *frame; - entity_state_t *state; - client_t *cl; - extern cvar_t sv_demoNoVis; - - // this is the frame we are creating - frame = &client->frames[client->netchan.incoming_sequence & UPDATE_MASK]; - - // find the client's PVS - clent = client->edict; - pvs = NULL; - if (!recorder) { - VectorAdd (clent->v.origin, clent->v.view_ofs, org); - pvs = SV_FatPVS (org); - } else - max_packet_entities = MAX_PACKET_ENTITIES; - - - for (i=0, cl=svs.clients ; istate != cs_spawned) - continue; - - if (cl->edict == clent) - continue; - - if (cl->spectator) - continue; - - if (pvs == NULL) - { - VectorAdd (cl->edict->v.origin, cl->edict->v.view_ofs, org); - pvs = SV_FatPVS (org); - } else { - if (client->POVs & (1 << i)) { - VectorAdd (cl->edict->v.origin, cl->edict->v.view_ofs, org); - SV_AddToFatPVS (org, sv.worldmodel->nodes); - } - } - } - - // send over the players in the PVS - SV_WritePlayersToClient (client, clent, pvs, msg); - - // put other visible entities into either a packet_entities or a nails message - pack = &frame->entities; - pack->num_entities = 0; - - numnails = 0; - - for (e=MAX_CLIENTS+1, ent=EDICT_NUM(e) ; ev.modelindex || !*PR_GetString(ent->v.model)) - continue; - - if (!sv_demoNoVis.value || !recorder) { - // ignore if not touching a PV leaf - for (i=0 ; i < ent->num_leafs ; i++) - if (pvs[ent->leafnums[i] >> 3] & (1 << (ent->leafnums[i]&7) )) - break; - - if (i == ent->num_leafs) - continue; // not visible - } - - if (SV_AddNailUpdate (ent)) - continue; // added to the special update list - - // add to the packetentities - if (pack->num_entities == max_packet_entities) - continue; // all full - - state = &pack->entities[pack->num_entities]; - pack->num_entities++; - - state->number = e; - state->flags = 0; - VectorCopy (ent->v.origin, state->origin); - VectorCopy (ent->v.angles, state->angles); - state->modelindex = ent->v.modelindex; - state->frame = ent->v.frame; - state->colormap = ent->v.colormap; - state->skinnum = ent->v.skin; - state->effects = ent->v.effects; - } - - // encode the packet entities as a delta from the - // last packetentities acknowledged by the client - - SV_EmitPacketEntities (client, pack, msg); - - // now add the specialized nail update - SV_EmitNailUpdate (msg, recorder); -} +/* +Copyright (C) 1996-1997 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 "qwsvdef.h" + +/* +============================================================================= + +The PVS must include a small area around the client to allow head bobbing +or other small motion on the client side. Otherwise, a bob might cause an +entity that should be visible to not show up, especially when the bob +crosses a waterline. + +============================================================================= +*/ + +int fatbytes; +byte fatpvs[MAX_MAP_LEAFS/8]; + +void SV_AddToFatPVS (vec3_t org, mnode_t *node) +{ + int i; + byte *pvs; + mplane_t *plane; + float d; + + while (1) + { + // if this is a leaf, accumulate the pvs bits + if (node->contents < 0) + { + if (node->contents != CONTENTS_SOLID) + { + pvs = Mod_LeafPVS ( (mleaf_t *)node, sv.worldmodel); + for (i=0 ; iplane; + d = DotProduct (org, plane->normal) - plane->dist; + if (d > 8) + node = node->children[0]; + else if (d < -8) + node = node->children[1]; + else + { // go down both + SV_AddToFatPVS (org, node->children[0]); + node = node->children[1]; + } + } +} + +/* +============= +SV_FatPVS + +Calculates a PVS that is the inclusive or of all leafs within 8 pixels of the +given point. +============= +*/ +byte *SV_FatPVS (vec3_t org) +{ + fatbytes = (sv.worldmodel->numleafs+31)>>3; + memset (fatpvs, 0, fatbytes); + SV_AddToFatPVS (org, sv.worldmodel->nodes); + return fatpvs; +} + +//============================================================================= + +// because there can be a lot of nails, there is a special +// network protocol for them +#define MAX_NAILS 32 +edict_t *nails[MAX_NAILS]; +int numnails; +int nailcount = 0; + +extern int sv_nailmodel, sv_supernailmodel, sv_playermodel; + +cvar_t sv_nailhack = {"sv_nailhack", "0"}; + + +qboolean SV_AddNailUpdate (edict_t *ent) +{ + if (sv_nailhack.value) + return false; + + if (ent->v.modelindex != sv_nailmodel + && ent->v.modelindex != sv_supernailmodel) + return false; + if (numnails == MAX_NAILS) + return true; + nails[numnails] = ent; + numnails++; + return true; +} + +void SV_EmitNailUpdate (sizebuf_t *msg, qboolean recorder) +{ + byte bits[6]; // [48 bits] xyzpy 12 12 12 4 8 + int n, i; + edict_t *ent; + int x, y, z, p, yaw; + + if (!numnails) + return; + + if (recorder) + MSG_WriteByte (msg, svc_nails2); + else + MSG_WriteByte (msg, svc_nails); + + MSG_WriteByte (msg, numnails); + + for (n=0 ; nv.colormap) { + if (!((++nailcount)&255)) nailcount++; + ent->v.colormap = nailcount&255; + } + + MSG_WriteByte (msg, (byte)ent->v.colormap); + } + + x = (int)(ent->v.origin[0]+4096)>>1; + y = (int)(ent->v.origin[1]+4096)>>1; + z = (int)(ent->v.origin[2]+4096)>>1; + p = (int)(16*ent->v.angles[0]/360)&15; + yaw = (int)(256*ent->v.angles[1]/360)&255; + + bits[0] = x; + bits[1] = (x>>8) | (y<<4); + bits[2] = (y>>4); + bits[3] = z; + bits[4] = (z>>8) | (p<<4); + bits[5] = yaw; + + for (i=0 ; i<6 ; i++) + MSG_WriteByte (msg, bits[i]); + } +} + +//============================================================================= + + +/* +================== +SV_WriteDelta + +Writes part of a packetentities message. +Can delta from either a baseline or a previous packet_entity +================== +*/ +void SV_WriteDelta (entity_state_t *from, entity_state_t *to, sizebuf_t *msg, qboolean force) +{ + int bits; + int i; + float miss; + +// send an update + bits = 0; + + for (i=0 ; i<3 ; i++) + { + miss = to->origin[i] - from->origin[i]; + if ( miss < -0.1 || miss > 0.1 ) + bits |= U_ORIGIN1<angles[0] != from->angles[0] ) + bits |= U_ANGLE1; + + if ( to->angles[1] != from->angles[1] ) + bits |= U_ANGLE2; + + if ( to->angles[2] != from->angles[2] ) + bits |= U_ANGLE3; + + if ( to->colormap != from->colormap ) + bits |= U_COLORMAP; + + if ( to->skinnum != from->skinnum ) + bits |= U_SKIN; + + if ( to->frame != from->frame ) + bits |= U_FRAME; + + if ( to->effects != from->effects ) + bits |= U_EFFECTS; + + if ( to->modelindex != from->modelindex ) + bits |= U_MODEL; + + if (bits & 511) + bits |= U_MOREBITS; + + if (to->flags & U_SOLID) + bits |= U_SOLID; + + // + // write the message + // + if (!to->number) + SV_Error ("Unset entity number"); + if (to->number >= 512) + SV_Error ("Entity number >= 512"); + + if (!bits && !force) + return; // nothing to send! + i = to->number | (bits&~511); + if (i & U_REMOVE) + Sys_Error ("U_REMOVE"); + MSG_WriteShort (msg, i); + + if (bits & U_MOREBITS) + MSG_WriteByte (msg, bits&255); + if (bits & U_MODEL) + MSG_WriteByte (msg, to->modelindex); + if (bits & U_FRAME) + MSG_WriteByte (msg, to->frame); + if (bits & U_COLORMAP) + MSG_WriteByte (msg, to->colormap); + if (bits & U_SKIN) + MSG_WriteByte (msg, to->skinnum); + if (bits & U_EFFECTS) + MSG_WriteByte (msg, to->effects); + if (bits & U_ORIGIN1) + MSG_WriteCoord (msg, to->origin[0]); + if (bits & U_ANGLE1) + MSG_WriteAngle(msg, to->angles[0]); + if (bits & U_ORIGIN2) + MSG_WriteCoord (msg, to->origin[1]); + if (bits & U_ANGLE2) + MSG_WriteAngle(msg, to->angles[1]); + if (bits & U_ORIGIN3) + MSG_WriteCoord (msg, to->origin[2]); + if (bits & U_ANGLE3) + MSG_WriteAngle(msg, to->angles[2]); +} + +/* +============= +SV_EmitPacketEntities + +Writes a delta update of a packet_entities_t to the message. + +============= +*/ +void SV_EmitPacketEntities (client_t *client, packet_entities_t *to, sizebuf_t *msg) +{ + edict_t *ent; + client_frame_t *fromframe; + packet_entities_t *from; + int oldindex, newindex; + int oldnum, newnum; + int oldmax; + + // this is the frame that we are going to delta update from + if (client->delta_sequence != -1) + { + fromframe = &client->frames[client->delta_sequence & UPDATE_MASK]; + from = &fromframe->entities; + oldmax = from->num_entities; + + MSG_WriteByte (msg, svc_deltapacketentities); + MSG_WriteByte (msg, client->delta_sequence); + } + else + { + oldmax = 0; // no delta update + from = NULL; + + MSG_WriteByte (msg, svc_packetentities); + } + + newindex = 0; + oldindex = 0; +//Con_Printf ("---%i to %i ----\n", client->delta_sequence & UPDATE_MASK +// , client->netchan.outgoing_sequence & UPDATE_MASK); + while (newindex < to->num_entities || oldindex < oldmax) + { + newnum = newindex >= to->num_entities ? 9999 : to->entities[newindex].number; + oldnum = oldindex >= oldmax ? 9999 : from->entities[oldindex].number; + + if (newnum == oldnum) + { // delta update from old position +//Con_Printf ("delta %i\n", newnum); + SV_WriteDelta (&from->entities[oldindex], &to->entities[newindex], msg, false); + oldindex++; + newindex++; + continue; + } + + if (newnum < oldnum) + { // this is a new entity, send it from the baseline + if (newnum == 9999) + { + Sys_Printf("LOL, %d, %d, %d, %d %d %d\n", newnum, oldnum, to->num_entities, oldmax, client->netchan.incoming_sequence & UPDATE_MASK, client->delta_sequence & UPDATE_MASK); + if (client->edict == NULL) + Sys_Printf("demo\n"); + } + ent = EDICT_NUM(newnum); +//Con_Printf ("baseline %i\n", newnum); + SV_WriteDelta (&ent->baseline, &to->entities[newindex], msg, true); + newindex++; + continue; + } + + if (newnum > oldnum) + { // the old entity isn't present in the new message +//Con_Printf ("remove %i\n", oldnum); + MSG_WriteShort (msg, oldnum | U_REMOVE); + oldindex++; + continue; + } + } + + MSG_WriteShort (msg, 0); // end of packetentities +} + +/* +============= +SV_WritePlayersToClient +============= +*/ + +#define DF_ORIGIN 1 +#define DF_ANGLES (1<<3) +#define DF_EFFECTS (1<<6) +#define DF_SKINNUM (1<<7) +#define DF_DEAD (1<<8) +#define DF_GIB (1<<9) +#define DF_WEAPONFRAME (1<<10) +#define DF_MODEL (1<<11) + +void SV_WritePlayersToClient (client_t *client, edict_t *clent, byte *pvs, sizebuf_t *msg) +{ + int i, j; + client_t *cl; + edict_t *ent; + int msec; + usercmd_t cmd; + int pflags; + demo_frame_t *demo_frame; + demo_client_t *dcl; + + demo_frame = &demo.frames[demo.parsecount&DEMO_FRAMES_MASK]; + + for (j=0,cl=svs.clients, dcl = demo_frame->clients; jstate != cs_spawned) + continue; + + ent = cl->edict; + + if (clent == NULL) + { + if (cl->spectator) + continue; + + dcl->parsecount = demo.parsecount; + + VectorCopy(ent->v.origin, dcl->info.origin); + VectorCopy(ent->v.angles, dcl->info.angles); + dcl->info.angles[0] *= -3; + dcl->info.angles[2] = 0; // no roll angle + + if (ent->v.health <= 0) + { // don't show the corpse looking around... + dcl->info.angles[0] = 0; + dcl->info.angles[1] = ent->v.angles[1]; + dcl->info.angles[2] = 0; + } + + dcl->info.skinnum = ent->v.skin; + dcl->info.effects = ent->v.effects; + dcl->info.weaponframe = ent->v.weaponframe; + dcl->info.model = ent->v.modelindex; + dcl->sec = sv.time - cl->localtime; + dcl->frame = ent->v.frame; + dcl->flags = 0; + dcl->cmdtime = cl->localtime; + dcl->fixangle = demo.fixangle[j]; + demo.fixangle[j] = 0; + + if (ent->v.health <= 0) + dcl->flags |= DF_DEAD; + if (ent->v.mins[2] != -24) + dcl->flags |= DF_GIB; + continue; + } + + + // ZOID visibility tracking + if (ent != clent && + !(client->spec_track && client->spec_track - 1 == j)) + { + if (cl->spectator) + continue; + + // ignore if not touching a PV leaf + for (i=0 ; i < ent->num_leafs ; i++) + if (pvs[ent->leafnums[i] >> 3] & (1 << (ent->leafnums[i]&7) )) + break; + if (i == ent->num_leafs) + continue; // not visable + } + + pflags = PF_MSEC | PF_COMMAND; + + if (ent->v.modelindex != sv_playermodel) + pflags |= PF_MODEL; + for (i=0 ; i<3 ; i++) + if (ent->v.velocity[i]) + pflags |= PF_VELOCITY1<v.effects) + pflags |= PF_EFFECTS; + if (ent->v.skin) + pflags |= PF_SKINNUM; + if (ent->v.health <= 0) + pflags |= PF_DEAD; + if (ent->v.mins[2] != -24) + pflags |= PF_GIB; + + if (cl->spectator) + { // only sent origin and velocity to spectators + pflags &= PF_VELOCITY1 | PF_VELOCITY2 | PF_VELOCITY3; + } + else if (ent == clent) + { // don't send a lot of data on personal entity + pflags &= ~(PF_MSEC|PF_COMMAND); + if (ent->v.weaponframe) + pflags |= PF_WEAPONFRAME; + } + + if (client->spec_track && client->spec_track - 1 == j && + ent->v.weaponframe) + pflags |= PF_WEAPONFRAME; + + MSG_WriteByte (msg, svc_playerinfo); + MSG_WriteByte (msg, j); + MSG_WriteShort (msg, pflags); + + for (i=0 ; i<3 ; i++) + MSG_WriteCoord (msg, ent->v.origin[i]); + + MSG_WriteByte (msg, ent->v.frame); + + if (pflags & PF_MSEC) + { + msec = 1000*(sv.time - cl->localtime); + if (msec > 255) + msec = 255; + MSG_WriteByte (msg, msec); + } + + if (pflags & PF_COMMAND) + { + cmd = cl->lastcmd; + + if (ent->v.health <= 0) + { // don't show the corpse looking around... + cmd.angles[0] = 0; + cmd.angles[1] = ent->v.angles[1]; + cmd.angles[0] = 0; + } + + cmd.buttons = 0; // never send buttons + cmd.impulse = 0; // never send impulses + + MSG_WriteDeltaUsercmd (msg, &nullcmd, &cmd); + } + + for (i=0 ; i<3 ; i++) + if (pflags & (PF_VELOCITY1<v.velocity[i]); + + if (pflags & PF_MODEL) + MSG_WriteByte (msg, ent->v.modelindex); + + if (pflags & PF_SKINNUM) + MSG_WriteByte (msg, ent->v.skin); + + if (pflags & PF_EFFECTS) + MSG_WriteByte (msg, ent->v.effects); + + if (pflags & PF_WEAPONFRAME) + MSG_WriteByte (msg, ent->v.weaponframe); + } +} + + +/* +============= +SV_WriteEntitiesToClient + +Encodes the current state of the world as +a svc_packetentities messages and possibly +a svc_nails message and +svc_playerinfo messages +============= +*/ + +void SV_WriteEntitiesToClient (client_t *client, sizebuf_t *msg, qboolean recorder) +{ + int e, i, max_packet_entities = MAX_PACKET_ENTITIES; + byte *pvs; + vec3_t org; + edict_t *ent; + packet_entities_t *pack; + edict_t *clent; + client_frame_t *frame; + entity_state_t *state; + client_t *cl; + extern cvar_t sv_demoNoVis; + + // this is the frame we are creating + frame = &client->frames[client->netchan.incoming_sequence & UPDATE_MASK]; + + // find the client's PVS + clent = client->edict; + pvs = NULL; + if (!recorder) { + VectorAdd (clent->v.origin, clent->v.view_ofs, org); + pvs = SV_FatPVS (org); + } else { + max_packet_entities = MAX_DEMO_PACKET_ENTITIES; + + for (i=0, cl=svs.clients ; istate != cs_spawned) + continue; + + if (cl->spectator) + continue; + + if (pvs == NULL) + { + VectorAdd (cl->edict->v.origin, cl->edict->v.view_ofs, org); + pvs = SV_FatPVS (org); + } else { + VectorAdd (cl->edict->v.origin, cl->edict->v.view_ofs, org); + SV_AddToFatPVS (org, sv.worldmodel->nodes); + } + } + } + + // send over the players in the PVS + SV_WritePlayersToClient (client, clent, pvs, msg); + + // put other visible entities into either a packet_entities or a nails message + pack = &frame->entities; + pack->num_entities = 0; + + numnails = 0; + + for (e=MAX_CLIENTS+1, ent=EDICT_NUM(e) ; ev.modelindex || !*PR_GetString(ent->v.model)) + continue; + + if (!sv_demoNoVis.value || !recorder) { + // ignore if not touching a PV leaf + for (i=0 ; i < ent->num_leafs ; i++) + if (pvs[ent->leafnums[i] >> 3] & (1 << (ent->leafnums[i]&7) )) + break; + + if (i == ent->num_leafs) + continue; // not visible + } + + if (SV_AddNailUpdate (ent)) + continue; // added to the special update list + + // add to the packetentities + if (pack->num_entities == max_packet_entities) + continue; // all full + + state = &pack->entities[pack->num_entities]; + pack->num_entities++; + + state->number = e; + state->flags = 0; + VectorCopy (ent->v.origin, state->origin); + VectorCopy (ent->v.angles, state->angles); + state->modelindex = ent->v.modelindex; + state->frame = ent->v.frame; + state->colormap = ent->v.colormap; + state->skinnum = ent->v.skin; + state->effects = ent->v.effects; + } + + // encode the packet entities as a delta from the + // last packetentities acknowledged by the client + + SV_EmitPacketEntities (client, pack, msg); + + // now add the specialized nail update + SV_EmitNailUpdate (msg, recorder); +} diff --git a/source/sv_init.c b/source/sv_init.c index db1060f5..bd6d2d97 100644 --- a/source/sv_init.c +++ b/source/sv_init.c @@ -1,434 +1,464 @@ -/* -Copyright (C) 1996-1997 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 "qwsvdef.h" -#include "version.h" - -server_static_t svs; // persistent server info -server_t sv; // local server -demo_t demo; // server demo struct - -char localmodels[MAX_MODELS][5]; // inline model names for precache - -char localinfo[MAX_LOCALINFO_STRING+1]; // local game info - -/* -================ -SV_ModelIndex - -================ -*/ -int SV_ModelIndex (char *name) -{ - int i; - - if (!name || !name[0]) - return 0; - - for (i=0 ; ifree) - continue; - // create baselines for all player slots, - // and any other edict that has a visible model - if (entnum > MAX_CLIENTS && !svent->v.modelindex) - continue; - - // - // create entity baseline - // - VectorCopy (svent->v.origin, svent->baseline.origin); - VectorCopy (svent->v.angles, svent->baseline.angles); - svent->baseline.frame = svent->v.frame; - svent->baseline.skinnum = svent->v.skin; - if (entnum > 0 && entnum <= MAX_CLIENTS) - { - svent->baseline.colormap = entnum; - svent->baseline.modelindex = SV_ModelIndex("progs/player.mdl"); - } - else - { - svent->baseline.colormap = 0; - svent->baseline.modelindex = - SV_ModelIndex(PR_GetString(svent->v.model)); - } - - // - // flush the signon message out to a seperate buffer if - // nearly full - // - SV_FlushSignon (); - - // - // add to the message - // - MSG_WriteByte (&sv.signon,svc_spawnbaseline); - MSG_WriteShort (&sv.signon,entnum); - - MSG_WriteByte (&sv.signon, svent->baseline.modelindex); - MSG_WriteByte (&sv.signon, svent->baseline.frame); - MSG_WriteByte (&sv.signon, svent->baseline.colormap); - MSG_WriteByte (&sv.signon, svent->baseline.skinnum); - for (i=0 ; i<3 ; i++) - { - MSG_WriteCoord(&sv.signon, svent->baseline.origin[i]); - MSG_WriteAngle(&sv.signon, svent->baseline.angles[i]); - } - } -} - - -/* -================ -SV_SaveSpawnparms - -Grabs the current state of the progs serverinfo flags -and each client for saving across the -transition to another level -================ -*/ -void SV_SaveSpawnparms (void) -{ - int i, j; - - if (!sv.state) - return; // no progs loaded yet - - // serverflags is the only game related thing maintained - svs.serverflags = pr_global_struct->serverflags; - - for (i=0, host_client = svs.clients ; istate != cs_spawned) - continue; - - // needs to reconnect - host_client->state = cs_connected; - - // call the progs to get default spawn parms for the new client - pr_global_struct->self = EDICT_TO_PROG(host_client->edict); - PR_ExecuteProgram (pr_global_struct->SetChangeParms); - for (j=0 ; jspawn_parms[j] = (&pr_global_struct->parm1)[j]; - } -} - -/* -================ -SV_CalcPHS - -Expands the PVS and calculates the PHS -(Potentially Hearable Set) -================ -*/ -void SV_CalcPHS (void) -{ - int rowbytes, rowwords; - int i, j, k, l, index, num; - int bitbyte; - unsigned *dest, *src; - byte *scan; - int count, vcount; - - Con_Printf ("Building PHS...\n"); - - num = sv.worldmodel->numleafs; - rowwords = (num+31)>>5; - rowbytes = rowwords*4; - - sv.pvs = Hunk_Alloc (rowbytes*num); - scan = sv.pvs; - vcount = 0; - for (i=0 ; ileafs+i, sv.worldmodel), - rowbytes); - if (i == 0) - continue; - for (j=0 ; j>3] & (1<<(j&7)) ) - { - vcount++; - } - } - } - - - sv.phs = Hunk_Alloc (rowbytes*num); - count = 0; - scan = sv.pvs; - dest = (unsigned *)sv.phs; - for (i=0 ; i= num) - continue; - src = (unsigned *)sv.pvs + index*rowwords; - for (l=0 ; l>3] & (1<<(j&7)) ) - count++; - } - - Con_Printf ("Average leafs visible / hearable / total: %i / %i / %i\n" - , vcount/num, count/num, num); -} - -unsigned SV_CheckModel(char *mdl) -{ - byte stackbuf[1024]; // avoid dirtying the cache heap - byte *buf; - unsigned short crc; - - buf = (byte *)COM_LoadStackFile (mdl, stackbuf, sizeof(stackbuf)); - if (!buf) - SV_Error ("SV_CheckModel: could not load %s\n", mdl); - crc = CRC_Block(buf, com_filesize); - - return crc; -} - -/* -================ -SV_SpawnServer - -Change the server to a new map, taking all connected -clients along with it. - -This is only called from the SV_Map_f() function. -================ -*/ -void D_FlushCaches (); - -void SV_SpawnServer (char *server) -{ - edict_t *ent; - int i; - extern cvar_t version; - - Con_DPrintf ("SpawnServer: %s\n",server); - - SV_SaveSpawnparms (); - - svs.spawncount++; // any partially connected client will be - // restarted - - sv.state = ss_dead; - sv.paused = false; - - Mod_ClearAll (); - Hunk_FreeToLowMark (host_hunklevel); - - if (coop.value) - Cvar_Set (&deathmatch, "0"); - current_skill = (int)(skill.value + 0.5); - if (current_skill < 0) - current_skill = 0; - if (current_skill > 3) - current_skill = 3; - Cvar_Set (&skill, va("%d", (int)current_skill)); - - - // wipe the entire per-level structure - memset (&sv, 0, sizeof(sv)); - - sv.datagram.maxsize = sizeof(sv.datagram_buf); - sv.datagram.data = sv.datagram_buf; - sv.datagram.allowoverflow = true; - - sv.reliable_datagram.maxsize = sizeof(sv.reliable_datagram_buf); - sv.reliable_datagram.data = sv.reliable_datagram_buf; - - sv.multicast.maxsize = sizeof(sv.multicast_buf); - sv.multicast.data = sv.multicast_buf; - - sv.master.maxsize = sizeof(sv.master_buf); - sv.master.data = sv.master_buf; - - sv.signon.maxsize = sizeof(sv.signon_buffers[0]); - sv.signon.data = sv.signon_buffers[0]; - sv.num_signon_buffers = 1; - - strcpy (sv.name, server); - - // load progs to get entity field count - // which determines how big each edict is - PR_LoadProgs (); - - // allocate edicts - - sv.edicts = Hunk_AllocName (MAX_EDICTS*pr_edict_size, "edicts"); - - // leave slots at start for clients only - sv.num_edicts = MAX_CLIENTS+1; - for (i=0 ; inumsubmodels ; i++) - { - sv.model_precache[1+i] = localmodels[i]; - sv.models[i+1] = Mod_ForName (localmodels[i], false); - } - - //check player/eyes models for hacks - sv.model_player_checksum = SV_CheckModel("progs/player.mdl"); - sv.eyes_player_checksum = SV_CheckModel("progs/eyes.mdl"); - - // - // spawn the rest of the entities on the map - // - - // precache and static commands can be issued during - // map initialization - sv.state = ss_loading; - - ent = EDICT_NUM(0); - ent->free = false; - ent->v.model = PR_SetString(sv.worldmodel->name); - ent->v.modelindex = 1; // world model - ent->v.solid = SOLID_BSP; - ent->v.movetype = MOVETYPE_PUSH; - - // information about the server - ent->v.netname = PR_SetString(version.string); - ent->v.targetname = PR_SetString("mvdsv"); - ent->v.impulse = QWE_VERNUM; - ent->v.items = QWE_FUNCS; - - pr_global_struct->mapname = /*sv.name - pr_strings;//*/PR_SetString(sv.name); - // serverflags are for cross level information (sigils) - pr_global_struct->serverflags = svs.serverflags; - - // run the frame start qc function to let progs check cvars - SV_ProgStartFrame (); - - // load and spawn all other entities - ED_LoadFromFile (sv.worldmodel->entities); - - // look up some model indexes for specialized message compression - SV_FindModelNumbers (); - - // all spawning is completed, any further precache statements - // or prog writes to the signon message are errors - sv.state = ss_active; - - // run two frames to allow everything to settle - sv_frametime = 0.1; - SV_Physics (); - SV_Physics (); - - // save movement vars - SV_SetMoveVars(); - - // create a baseline for more efficient communications - SV_CreateBaseline (); - sv.signon_buffer_size[sv.num_signon_buffers-1] = sv.signon.cursize; - - Info_SetValueForKey (svs.info, "map", sv.name, MAX_SERVERINFO_STRING); - Con_DPrintf ("Server spawned.\n"); -} - +/* +Copyright (C) 1996-1997 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 "qwsvdef.h" +#include "version.h" + +server_static_t svs; // persistent server info +server_t sv; // local server +demo_t demo; // server demo struct +entity_state_t cl_entities[MAX_CLIENTS][UPDATE_BACKUP+1][MAX_PACKET_ENTITIES]; // client entities + +char localmodels[MAX_MODELS][5]; // inline model names for precache + +char localinfo[MAX_LOCALINFO_STRING+1]; // local game info + +/* +================ +SV_ModelIndex + +================ +*/ +int SV_ModelIndex (char *name) +{ + int i; + + if (!name || !name[0]) + return 0; + + for (i=0 ; ifree) + continue; + // create baselines for all player slots, + // and any other edict that has a visible model + if (entnum > MAX_CLIENTS && !svent->v.modelindex) + continue; + + // + // create entity baseline + // + VectorCopy (svent->v.origin, svent->baseline.origin); + VectorCopy (svent->v.angles, svent->baseline.angles); + svent->baseline.frame = svent->v.frame; + svent->baseline.skinnum = svent->v.skin; + if (entnum > 0 && entnum <= MAX_CLIENTS) + { + svent->baseline.colormap = entnum; + svent->baseline.modelindex = SV_ModelIndex("progs/player.mdl"); + } + else + { + svent->baseline.colormap = 0; + svent->baseline.modelindex = + SV_ModelIndex(PR_GetString(svent->v.model)); + } + + // + // flush the signon message out to a seperate buffer if + // nearly full + // + SV_FlushSignon (); + + // + // add to the message + // + MSG_WriteByte (&sv.signon,svc_spawnbaseline); + MSG_WriteShort (&sv.signon,entnum); + + MSG_WriteByte (&sv.signon, svent->baseline.modelindex); + MSG_WriteByte (&sv.signon, svent->baseline.frame); + MSG_WriteByte (&sv.signon, svent->baseline.colormap); + MSG_WriteByte (&sv.signon, svent->baseline.skinnum); + for (i=0 ; i<3 ; i++) + { + MSG_WriteCoord(&sv.signon, svent->baseline.origin[i]); + MSG_WriteAngle(&sv.signon, svent->baseline.angles[i]); + } + } +} + + +/* +================ +SV_SaveSpawnparms + +Grabs the current state of the progs serverinfo flags +and each client for saving across the +transition to another level +================ +*/ +void SV_SaveSpawnparms (void) +{ + int i, j; + + if (!sv.state) + return; // no progs loaded yet + + // serverflags is the only game related thing maintained + svs.serverflags = pr_global_struct->serverflags; + + for (i=0, host_client = svs.clients ; istate != cs_spawned) + continue; + + // needs to reconnect + host_client->state = cs_connected; + + // call the progs to get default spawn parms for the new client + pr_global_struct->self = EDICT_TO_PROG(host_client->edict); + PR_ExecuteProgram (pr_global_struct->SetChangeParms); + for (j=0 ; jspawn_parms[j] = (&pr_global_struct->parm1)[j]; + + } +} + +/* +================ +SV_CalcPHS + +Expands the PVS and calculates the PHS +(Potentially Hearable Set) +================ +*/ +void SV_CalcPHS (void) +{ + int rowbytes, rowwords; + int i, j, k, l, index, num; + int bitbyte; + unsigned *dest, *src; + byte *scan; + int count, vcount; + + Con_Printf ("Building PHS...\n"); + + num = sv.worldmodel->numleafs; + rowwords = (num+31)>>5; + rowbytes = rowwords*4; + + sv.pvs = Hunk_Alloc (rowbytes*num); + scan = sv.pvs; + vcount = 0; + for (i=0 ; ileafs+i, sv.worldmodel), + rowbytes); + if (i == 0) + continue; + for (j=0 ; j>3] & (1<<(j&7)) ) + { + vcount++; + } + } + } + + + sv.phs = Hunk_Alloc (rowbytes*num); + count = 0; + scan = sv.pvs; + dest = (unsigned *)sv.phs; + for (i=0 ; i= num) + continue; + src = (unsigned *)sv.pvs + index*rowwords; + for (l=0 ; l>3] & (1<<(j&7)) ) + count++; + } + + Con_Printf ("Average leafs visible / hearable / total: %i / %i / %i\n" + , vcount/num, count/num, num); +} + +unsigned SV_CheckModel(char *mdl) +{ + byte stackbuf[1024]; // avoid dirtying the cache heap + byte *buf; + unsigned short crc; + + buf = (byte *)COM_LoadStackFile (mdl, stackbuf, sizeof(stackbuf)); + if (!buf) + SV_Error ("SV_CheckModel: could not load %s\n", mdl); + crc = CRC_Block(buf, com_filesize); + + return crc; +} + +/* +================ +SV_SpawnServer + +Change the server to a new map, taking all connected +clients along with it. + +This is only called from the SV_Map_f() function. +================ +*/ +void D_FlushCaches (); +dfunction_t *ED_FindFunction (char *name); + +void SV_SpawnServer (char *server) +{ + edict_t *ent; + int i; + extern cvar_t version; + extern int hunk_low_used; + dfunction_t *f; + + Con_DPrintf ("SpawnServer: %s\n",server); + + SV_SaveSpawnparms (); + + svs.spawncount++; // any partially connected client will be + // restarted + + sv.state = ss_dead; + sv.paused = false; + + Mod_ClearAll (); + Hunk_FreeToLowMark (host_hunklevel); + + //Sys_Sleep(100); + + if (coop.value) + Cvar_Set (&deathmatch, "0"); + current_skill = (int)(skill.value + 0.5); + if (current_skill < 0) + current_skill = 0; + if (current_skill > 3) + current_skill = 3; + Cvar_Set (&skill, va("%d", (int)current_skill)); + + + // wipe the entire per-level structure + memset (&sv, 0, sizeof(sv)); + + sv.datagram.maxsize = sizeof(sv.datagram_buf); + sv.datagram.data = sv.datagram_buf; + sv.datagram.allowoverflow = true; + + sv.reliable_datagram.maxsize = sizeof(sv.reliable_datagram_buf); + sv.reliable_datagram.data = sv.reliable_datagram_buf; + + sv.multicast.maxsize = sizeof(sv.multicast_buf); + sv.multicast.data = sv.multicast_buf; + + sv.master.maxsize = sizeof(sv.master_buf); + sv.master.data = sv.master_buf; + + sv.signon.maxsize = sizeof(sv.signon_buffers[0]); + sv.signon.data = sv.signon_buffers[0]; + sv.num_signon_buffers = 1; + + strcpy (sv.name, server); + + // load progs to get entity field count + // which determines how big each edict is + PR_LoadProgs (); + + //Sys_Sleep(100); + // allocate edicts + + sv.edicts = Hunk_AllocName (MAX_EDICTS*pr_edict_size, "edicts"); + + //Sys_Sleep(100); + + // leave slots at start for clients only + sv.num_edicts = MAX_CLIENTS+1; + for (i=0 ; inumsubmodels ; i++) + { + sv.model_precache[1+i] = localmodels[i]; + sv.models[i+1] = Mod_ForName (localmodels[i], false); + } + + //check player/eyes models for hacks + sv.model_player_checksum = SV_CheckModel("progs/player.mdl"); + sv.eyes_player_checksum = SV_CheckModel("progs/eyes.mdl"); + + // + // spawn the rest of the entities on the map + // + + // precache and static commands can be issued during + // map initialization + sv.state = ss_loading; + + ent = EDICT_NUM(0); + ent->free = false; + ent->v.model = PR_SetString(sv.worldmodel->name); + ent->v.modelindex = 1; // world model + ent->v.solid = SOLID_BSP; + ent->v.movetype = MOVETYPE_PUSH; + + // information about the server + ent->v.netname = PR_SetString(version.string); + ent->v.targetname = PR_SetString("mvdsv"); + ent->v.impulse = QWE_VERNUM; + ent->v.items = pr_numbuiltins - 1; + + pr_global_struct->mapname = /*sv.name - pr_strings;//*/PR_SetString(sv.name); + // serverflags are for cross level information (sigils) + pr_global_struct->serverflags = svs.serverflags; + + // run the frame start qc function to let progs check cvars + SV_ProgStartFrame (); + + // load and spawn all other entities + ED_LoadFromFile (sv.worldmodel->entities); + + // look up some model indexes for specialized message compression + SV_FindModelNumbers (); + + // all spawning is completed, any further precache statements + // or prog writes to the signon message are errors + sv.state = ss_active; + + // run two frames to allow everything to settle + sv_frametime = 0.1; + SV_Physics (); + SV_Physics (); + + // save movement vars + SV_SetMoveVars(); + + // create a baseline for more efficient communications + SV_CreateBaseline (); + sv.signon_buffer_size[sv.num_signon_buffers-1] = sv.signon.cursize; + + Info_SetValueForKey (svs.info, "map", sv.name, MAX_SERVERINFO_STRING); + + if ((f = ED_FindFunction ("timeofday")) != NULL) { + date_t date; + + Sys_TimeOfDay(&date); + + G_FLOAT(OFS_PARM0) = (float)date.sec; + G_FLOAT(OFS_PARM1) = (float)date.min; + G_FLOAT(OFS_PARM2) = (float)date.hour; + G_FLOAT(OFS_PARM3) = (float)date.day; + G_FLOAT(OFS_PARM4) = (float)date.mon; + G_FLOAT(OFS_PARM5) = (float)date.year; + G_INT(OFS_PARM6) = PR_SetTmpString(date.str); + + pr_global_struct->time = sv.time; + pr_global_struct->self = 0; + + PR_ExecuteProgram((func_t)(f - pr_functions)); + } + + Con_DPrintf ("Server spawned.\n"); +} + diff --git a/source/sv_login.c b/source/sv_login.c new file mode 100755 index 00000000..b889ea5b --- /dev/null +++ b/source/sv_login.c @@ -0,0 +1,590 @@ +/* +Copyright (C) 1996-1997 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 "qwsvdef.h" +#include "winquake.h" + +#define MAX_ACCOUNTS 1000 +#define MAX_FAILURES 10 +#define MAX_LOGINNAME 16 +#define ACC_FILE "accounts" +#define ACC_DIR "users" + +cvar_t sv_login = {"sv_login", "0"}; // if enabled, login required + +typedef enum {a_free, a_ok, a_blocked} acc_state_t; +typedef enum {use_log, use_ip} use_t; + +typedef struct +{ + char login[MAX_LOGINNAME]; + char pass[MAX_LOGINNAME]; + int failures; + int inuse; + ipfilter_t adress; + acc_state_t state; + use_t use; +} account_t; + +static account_t accounts[MAX_ACCOUNTS]; +static int num_accounts = 0; + +static qboolean validAcc(char *acc) +{ + char *s = acc; + + for (; *acc; acc++) + { + if (*acc < 'a' || *acc > 'z') + if (*acc < 'A' || *acc > 'Z') + if (*acc < '0' || *acc > '9') + return false; + } + + return acc - s <= MAX_LOGINNAME && acc - s >= 3 ; +} + +/* +================= +WriteAccounts + +Writes account list to disk +================= +*/ + +static void WriteAccounts() +{ + int i,c; + FILE *f; + account_t *acc; + + Sys_mkdir(ACC_DIR); + if ( (f = fopen( ACC_DIR "/" ACC_FILE ,"wt")) == NULL) + { + Con_Printf("Warning: couldn't open for writing " ACC_FILE "\n"); + return; + } + + for (acc = accounts, c = 0; c < num_accounts; acc++) + { + if (acc->state == a_free) + continue; + + if (acc->use == use_log) + fprintf(f, "%s %s %d %d\n", acc->login, acc->pass, acc->state, acc->failures); + else + fprintf(f, "%s %d\n", acc->login, acc->state); + + c++; + } + + fclose(f); +} + +/* +================= +SV_LoadAccounts + +loads account list from disk +================= +*/ + +void SV_LoadAccounts(void) +{ + int i,c; + FILE *f; + char tmp[128]; + account_t *acc = accounts; + + if ( (f = fopen( ACC_DIR "/" ACC_FILE ,"rt")) == NULL) + { + Con_Printf("couldn't open " ACC_FILE "\n"); + return; + } + + while (!feof(f)) + { + memset(acc, 0, sizeof(account_t)); + + fscanf(f, "%s", acc->login); + if (StringToFilter(acc->login, &acc->adress)) + { + strcpy(acc->pass, acc->login); + acc->use = use_ip; + fscanf(f, "%d\n", &acc->state); + } else + fscanf(f, "%s %d %d\n", acc->pass, &acc->state, &acc->failures); + + if (acc->state != a_free) // lol? + acc++; + } + + num_accounts = acc - accounts; + + fclose(f); +} + + + +/* +================= +SV_CreateAccount_f + +acc_create [] +if password is not given, login will be used for password +login/pass has to be max 16 chars and at least 3, only regular chars are acceptable +================= +*/ + +void SV_CreateAccount_f(void) +{ + int i, spot, c; + ipfilter_t adr; + use_t use; + + if (Cmd_Argc() < 2) + { + Con_Printf("usage: acc_create []\n acc_create \nmaximum %d characters for login/pass\n", MAX_LOGINNAME - 1); + return; + } + + if (num_accounts == MAX_ACCOUNTS) + { + Con_Printf("MAX_ACCOUNTS reached\n"); + return; + } + + if (StringToFilter(Cmd_Argv(1), &adr)) + use = use_ip; + else { + use = use_log; + + // validate user login/pass + if (!validAcc(Cmd_Argv(1))) + { + Con_Printf("Invalid login!\n"); + return; + } + + if (Cmd_Argc() == 4 && !validAcc(Cmd_Argv(2))) + { + Con_Printf("Invalid pass!\n"); + return; + } + } + + // find free spot, check if login exist; + for (i = 0, c = 0, spot = -1 ; c < num_accounts; i++) + { + if (accounts[i].state == a_free) { + if (spot == -1) spot = i; + continue; + } + + if (!stricmp(accounts[i].login, Cmd_Argv(1))) + break; + + c++; + } + + if (c < num_accounts) + { + Con_Printf("Login already in use\n"); + return; + } + + if (spot == -1) + spot = i; + + // create an account + num_accounts++; + strcpy(accounts[spot].login, Cmd_Argv(1)); + if (Cmd_Argc() == 3) + strcpy(accounts[spot].pass, Cmd_Argv(2)); + else + strcpy(accounts[spot].pass, Cmd_Argv(1)); + + accounts[spot].state = a_ok; + accounts[spot].use = use; + + Con_Printf("login %s created\n", Cmd_Argv(1)); + WriteAccounts(); +} + +/* +================= +SV_RemoveAccount_f + +acc_remove +removes the login +================= +*/ + +void SV_RemoveAccount_f(void) +{ + int i, c; + + if (Cmd_Argc() < 2) + { + Con_Printf("usage: acc_remove \n"); + return; + } + + for (i = 0, c = 0; c < num_accounts; i++) + { + if (accounts[i].state == a_free) + continue; + + if (!stricmp(accounts[i].login, Cmd_Argv(1))) + { + if (accounts[i].inuse) + SV_Logout(&svs.clients[accounts[i].inuse -1]); + + accounts[i].state = a_free; + num_accounts--; + Con_Printf("login %s removed\n", accounts[i].login); + WriteAccounts(); + return; + } + + c++; + } + + Con_Printf("account for %s not found\n", Cmd_Argv(1)); +} + +/* +================= +SV_ListAccount_f + +shows the list of accounts +================= +*/ + +void SV_ListAccount_f (void) +{ + int i,c; + + if (!num_accounts) + { + Con_Printf("account list is empty\n"); + return; + } + + Con_Printf("account list:\n"); + + for (i = 0, c = 0; c < num_accounts; i++) + { + if (accounts[i].state != a_free) + { + Con_Printf("%.16s %s\n", accounts[i].login, accounts[i].state == a_ok ? "" : "blocked"); + c++; + } + } + + Con_Printf("%d login(s) found\n", num_accounts); +} + +/* +================= +SV_blockAccount + +blocks/unblocks an account +================= +*/ + +void SV_blockAccount(qboolean block) +{ + int i, c; + + for (i = 0, c = 0; c < num_accounts; i++) + { + if (accounts[i].state == a_free) + continue; + + if (!stricmp(accounts[i].login, Cmd_Argv(1))) + { + if (block) + { + accounts[i].state = a_blocked; + Con_Printf("account %s blocked\n", Cmd_Argv(1)); + return; + } + + if (accounts[i].state != a_blocked) + Con_Printf("account %s not blocked\n", Cmd_Argv(1)); + else { + accounts[i].state = a_ok; + accounts[i].failures = 0; + Con_Printf("account %s unblocked\n", Cmd_Argv(1)); + } + + return; + } + c++; + } + + Con_Printf("account %s not found\n", Cmd_Argv(1)); +} + +void SV_UnblockAccount_f(void) +{ + if (Cmd_Argc() < 2) + { + Con_Printf("usage: acc_unblock \n"); + return; + } + + SV_blockAccount(false); + WriteAccounts(); +} + +void SV_BlockAccount_f(void) +{ + if (Cmd_Argc() < 2) + { + Con_Printf("usage: acc_block \n"); + return; + } + + SV_blockAccount(true); + WriteAccounts(); +} + + +/* +================= +checklogin + +returns positive value if login/pass are valid +values <= 0 indicates a failure +================= +*/ + +int checklogin(char *log, char *pass, int num, int use) +{ + int i,c; + + for (i = 0, c = 0; c < num_accounts; i++) + { + if (accounts[i].state == a_free) + continue; + + if (use == accounts[i].use && + /*use == use_log && accounts[i].use == use_log && */!stricmp(log, accounts[i].login)) + { + if (accounts[i].inuse) + return -1; + + if (accounts[i].state == a_blocked) + return -2; + + if (!stricmp(pass, accounts[i].pass)) { + accounts[i].failures = 0; + accounts[i].inuse = num; + return i+1; + } + + if (++accounts[i].failures >= MAX_FAILURES) + { + Sys_Printf("account %s blocked after %d failed login attempts\n", accounts[i].login, accounts[i].failures); + accounts[i].state = a_blocked; + } + + WriteAccounts(); + + return 0; + } + + /*if (use == use_ip && accounts[i].use == use_ip && ) + { + if (accounts[i].inuse) + return -1; + + if (accounts[i].state == a_blocked) + return -2; + + accounts[i].inuse = num; + return i+1; + } + */ + + c++; + } + + return 0; +} + +void Login_Init (void) +{ + Cvar_RegisterVariable (&sv_login); + + Cmd_AddCommand ("acc_create",SV_CreateAccount_f); + Cmd_AddCommand ("acc_remove",SV_RemoveAccount_f); + Cmd_AddCommand ("acc_list",SV_ListAccount_f); + Cmd_AddCommand ("acc_unblock",SV_UnblockAccount_f); + Cmd_AddCommand ("acc_block",SV_BlockAccount_f); + + // load account list + SV_LoadAccounts(); +} + +/* +=============== +SV_Login + +called on connect after cmd new is issued +=============== +*/ + +qboolean SV_Login(client_t *cl) +{ + char *ip; + + // is sv_login is disabled, login is not necessery + if (!sv_login.value) { + SV_Logout(cl); + cl->logged = -1; + return true; + } + + // if we're already logged return (probobly map change) + if (cl->logged > 0) + return true; + + // sv_login == 1 -> spectators don't login + if (sv_login.value == 1 && cl->spectator) + { + SV_Logout(cl); + cl->logged = -1; + return true; + } + + // check for account for ip + ip = va("%d.%d.%d.%d", cl->realip.ip[0], cl->realip.ip[1], cl->realip.ip[2], cl->realip.ip[3]); + if (cl->logged = checklogin(ip, ip, cl - svs.clients + 1, use_ip) > 0) + { + strcpy(cl->login, ip); + return true; + } + + // need to login before connecting + cl->logged = false; + cl->login[0] = 0; + + MSG_WriteByte (&cl->netchan.message, svc_print); + MSG_WriteByte (&cl->netchan.message, PRINT_HIGH); + MSG_WriteString (&cl->netchan.message, "Enter your login and password:\n"); + + return false; +} + +void SV_Logout(client_t *cl) +{ + if (cl->logged > 0) { + accounts[cl->logged-1].inuse = false; + cl->login[0] = 0; + cl->logged = 0; + } +} + +void SV_ParseLogin(client_t *cl) +{ + char *log, *pass; + + if (Cmd_Argc() > 2) { + log = Cmd_Argv(1); + pass = Cmd_Argv(2); + } else { // bah usually whole text in 'say' is put into "" + log = pass = Cmd_Argv(1); + while (*pass && *pass != ' ') + pass++; + + if (*pass) + *pass++ = 0; + + while (*pass == ' ') + pass++; + } + + // if login is parsed, we read just a password + if (cl->login[0]) { + pass = log; + log = cl->login; + } else { + Q_strncpyz(cl->login, log, sizeof(cl->login)); + } + + if (!*pass) + { + Q_strncpyz(cl->login, log, sizeof(cl->login)); + MSG_WriteByte (&cl->netchan.message, svc_print); + MSG_WriteByte (&cl->netchan.message, PRINT_HIGH); + MSG_WriteString (&cl->netchan.message, va("Password for %s:\n", cl->login)); + + return; + } + + cl->logged = checklogin(log, pass, cl - svs.clients + 1, use_log); + + switch (cl->logged) + { + case -2: + MSG_WriteByte (&cl->netchan.message, svc_print); + MSG_WriteByte (&cl->netchan.message, PRINT_HIGH); + MSG_WriteString (&cl->netchan.message, "Login blocked\n"); + cl->logged = 0; + cl->login[0] = 0; + break; + case -1: + MSG_WriteByte (&cl->netchan.message, svc_print); + MSG_WriteByte (&cl->netchan.message, PRINT_HIGH); + MSG_WriteString (&cl->netchan.message, "Login in use!\ntry again:\n"); + cl->logged = 0; + cl->login[0] = 0; + break; + case 0: + MSG_WriteByte (&cl->netchan.message, svc_print); + MSG_WriteByte (&cl->netchan.message, PRINT_HIGH); + MSG_WriteString (&cl->netchan.message, va("Acces denided\nPassword for %s:\n", cl->login)); + break; + default: + Sys_Printf("%s logged in as %s\n", cl->name, cl->login); + MSG_WriteByte (&cl->netchan.message, svc_print); + MSG_WriteByte (&cl->netchan.message, PRINT_HIGH); + MSG_WriteString (&cl->netchan.message, va("Welcome %s\n", log)); + + MSG_WriteByte (&cl->netchan.message, svc_stufftext); + MSG_WriteString (&cl->netchan.message, "cmd new\n"); + } +} + +void SV_LoginCheckTimeOut(client_t *cl) +{ + if (cl->connection_started && realtime - cl->connection_started > 60) + { + Sys_Printf("login time out for %s\n", cl->name); + + MSG_WriteByte (&cl->netchan.message, svc_print); + MSG_WriteByte (&cl->netchan.message, PRINT_HIGH); + MSG_WriteString (&cl->netchan.message, "waited too long, Bye!\n"); + SV_DropClient(cl); + } +} diff --git a/source/sv_main.c b/source/sv_main.c index 966c1c0d..36447328 100644 --- a/source/sv_main.c +++ b/source/sv_main.c @@ -1,2000 +1,2262 @@ -/* -Copyright (C) 1996-1997 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 "version.h" - -#include "qwsvdef.h" - -quakeparms_t host_parms; - -qboolean host_initialized; // true if into command execution (compatability) - -double sv_frametime; - - -double realtime; // without any filtering or bounding - -int host_hunklevel; - -int current_skill; // for entity spawnflags checking - -netadr_t master_adr[MAX_MASTERS]; // address of group servers - -client_t *host_client; // current client - - -cvar_t sv_mintic = {"sv_mintic","0.03"}; // bound the size of the - -cvar_t sv_maxtic = {"sv_maxtic","0.1"}; // - - -cvar_t developer = {"developer","0"}; // show extra messages - -cvar_t timeout = {"timeout","65"}; // seconds without any message -cvar_t zombietime = {"zombietime", "2"}; // seconds to sink messages - // after disconnect - - -cvar_t rcon_password = {"rcon_password", ""}; // password for remote server commands -cvar_t password = {"password", ""}; // password for entering the game - -cvar_t spectator_password = {"spectator_password", ""}; // password for entering as a sepctator - -cvar_t allow_download = {"allow_download", "1"}; -cvar_t allow_download_skins = {"allow_download_skins", "1"}; -cvar_t allow_download_models = {"allow_download_models", "1"}; -cvar_t allow_download_sounds = {"allow_download_sounds", "1"}; -cvar_t allow_download_maps = {"allow_download_maps", "1"}; - -cvar_t sv_highchars = {"sv_highchars", "1"}; - -cvar_t sv_phs = {"sv_phs", "1"}; - -cvar_t pausable = {"pausable", "1"}; - -cvar_t sv_maxrate = {"sv_maxrate", "0"}; -cvar_t sv_multiPOVlevel = {"sv_multiPOVlevel", "0"}; -cvar_t sv_demofps = {"sv_demofps", "20"}; -cvar_t sv_demoPings = {"sv_demopings", "3"}; -cvar_t sv_demoNoVis = {"sv_demonovis", "1"}; -cvar_t sv_demoUseCache = {"sv_demoUseCache", "0"}; -cvar_t sv_demoCacheSize = {"sv_demoCacheSize", "0", CVAR_ROM}; -cvar_t sv_demoMaxSize = {"sv_demoMaxSize", "10240"}; -cvar_t sv_demoMaxDirSize = {"sv_demoMaxDirSize", "102400"}; - -// -// game rules mirrored in svs.info -// -cvar_t fraglimit = {"fraglimit","0",CVAR_SERVERINFO}; -cvar_t timelimit = {"timelimit","0",CVAR_SERVERINFO}; -cvar_t teamplay = {"teamplay","0",CVAR_SERVERINFO}; -cvar_t samelevel = {"samelevel","0",CVAR_SERVERINFO}; -cvar_t maxclients = {"maxclients","8",CVAR_SERVERINFO}; -cvar_t maxspectators = {"maxspectators","8",CVAR_SERVERINFO}; -cvar_t deathmatch = {"deathmatch","1",CVAR_SERVERINFO}; // 0, 1, or 2 -cvar_t spawn = {"spawn","0",CVAR_SERVERINFO}; -cvar_t watervis = {"watervis","0",CVAR_SERVERINFO}; -cvar_t serverdemo = {"serverdemo","",CVAR_SERVERINFO | CVAR_ROM}; -// not mirrored -cvar_t skill = {"skill", "1"}; -cvar_t coop = {"coop", "0"}; - -cvar_t version = {"version", "QWExtended " QWE_VERSION, CVAR_ROM}; - -cvar_t hostname = {"hostname","unnamed",CVAR_SERVERINFO}; - -FILE *sv_logfile; -FILE *sv_fraglogfile; - -void SV_AcceptClient (netadr_t adr, int userid, char *userinfo); -void Master_Shutdown (void); - -//============================================================================ - -qboolean ServerPaused(void) -{ - return sv.paused; -} - - -/* -================ -SV_Shutdown - -Quake calls this before calling Sys_Quit or Sys_Error -================ -*/ -void SV_Shutdown (void) -{ - Master_Shutdown (); - if (sv_logfile) - { - fclose (sv_logfile); - sv_logfile = NULL; - } - if (sv_fraglogfile) - { - fclose (sv_fraglogfile); - sv_logfile = NULL; - } - if (sv.demorecording) - SV_Stop_f(); - - NET_Shutdown (); -} - -/* -================ -SV_Error - -Sends a datagram to all the clients informing them of the server crash, -then exits -================ -*/ -void SV_Error (char *error, ...) -{ - va_list argptr; - static char string[1024]; - static qboolean inerror = false; - - if (inerror) - Sys_Error ("SV_Error: recursively entered (%s)", string); - - inerror = true; - - va_start (argptr,error); - vsprintf (string,error,argptr); - va_end (argptr); - - Con_Printf ("SV_Error: %s\n",string); - - SV_FinalMessage (va("server crashed: %s\n", string)); - - SV_Shutdown (); - - Sys_Error ("SV_Error: %s\n",string); -} - -/* -================== -SV_FinalMessage - -Used by SV_Error and SV_Quit_f to send a final message to all connected -clients before the server goes down. The messages are sent immediately, -not just stuck on the outgoing message list, because the server is going -to totally exit after returning from this function. -================== -*/ -void SV_FinalMessage (char *message) -{ - int i; - client_t *cl; - - SZ_Clear (&net_message); - MSG_WriteByte (&net_message, svc_print); - MSG_WriteByte (&net_message, PRINT_HIGH); - MSG_WriteString (&net_message, message); - MSG_WriteByte (&net_message, svc_disconnect); - - for (i=0, cl = svs.clients ; istate >= cs_spawned) - Netchan_Transmit (&cl->netchan, net_message.cursize - , net_message.data); -} - - - -/* -===================== -SV_DropClient - -Called when the player is totally leaving the server, either willingly -or unwillingly. This is NOT called if the entire server is quiting -or crashing. -===================== -*/ -void SV_DropClient (client_t *drop) -{ - // add the disconnect - MSG_WriteByte (&drop->netchan.message, svc_disconnect); - - if (drop->state == cs_spawned) - if (!drop->spectator) - { - // call the prog function for removing a client - // this will set the body to a dead frame, among other things - pr_global_struct->self = EDICT_TO_PROG(drop->edict); - PR_ExecuteProgram (pr_global_struct->ClientDisconnect); - } - else if (SpectatorDisconnect) - { - // call the prog function for removing a client - // this will set the body to a dead frame, among other things - pr_global_struct->self = EDICT_TO_PROG(drop->edict); - PR_ExecuteProgram (SpectatorDisconnect); - } - - if (drop->spectator) - Con_Printf ("Spectator %s removed\n",drop->name); - else - Con_Printf ("Client %s removed\n",drop->name); - - if (drop->download) - { - fclose (drop->download); - drop->download = NULL; - } - if (drop->upload) - { - fclose (drop->upload); - drop->upload = NULL; - } - *drop->uploadfn = 0; - - drop->state = cs_zombie; // become free in a few seconds - drop->connection_started = realtime; // for zombie timeout - - drop->old_frags = 0; - drop->edict->v.frags = 0; - drop->name[0] = 0; - memset (drop->userinfo, 0, sizeof(drop->userinfo)); - -// send notification to all remaining clients - SV_FullClientUpdate (drop, &sv.reliable_datagram); -} - - -//==================================================================== - -/* -=================== -SV_CalcPing - -=================== -*/ -int SV_CalcPing (client_t *cl) -{ - float ping; - int i; - int count; - register client_frame_t *frame; - - ping = 0; - count = 0; - for (frame = cl->frames, i=0 ; iping_time > 0) - { - ping += frame->ping_time; - count++; - } - } - if (!count) - return 9999; - ping /= count; - - return ping*1000; -} - -/* -=================== -SV_FullClientUpdate - -Writes all update values to a sizebuf -=================== -*/ -void SV_FullClientUpdate (client_t *client, sizebuf_t *buf) -{ - int i; - char info[MAX_INFO_STRING]; - - i = client - svs.clients; - -//Sys_Printf("SV_FullClientUpdate: Updated frags for client %d\n", i); - - MSG_WriteByte (buf, svc_updatefrags); - MSG_WriteByte (buf, i); - MSG_WriteShort (buf, client->old_frags); - - MSG_WriteByte (buf, svc_updateping); - MSG_WriteByte (buf, i); - MSG_WriteShort (buf, SV_CalcPing (client)); - - MSG_WriteByte (buf, svc_updatepl); - MSG_WriteByte (buf, i); - MSG_WriteByte (buf, client->lossage); - - MSG_WriteByte (buf, svc_updateentertime); - MSG_WriteByte (buf, i); - MSG_WriteFloat (buf, realtime - client->connection_started); - - strcpy (info, client->userinfo); - Info_RemovePrefixedKeys (info, '_'); // server passwords, etc - - MSG_WriteByte (buf, svc_updateuserinfo); - MSG_WriteByte (buf, i); - MSG_WriteLong (buf, client->userid); - MSG_WriteString (buf, info); -} - -/* -=================== -SV_FullClientUpdateToClient - -Writes all update values to a client's reliable stream -=================== -*/ -void SV_FullClientUpdateToClient (client_t *client, client_t *cl) -{ - ClientReliableCheckBlock(cl, 24 + strlen(client->userinfo)); - if (cl->num_backbuf) { - SV_FullClientUpdate (client, &cl->backbuf); - ClientReliable_FinishWrite(cl); - } else - SV_FullClientUpdate (client, &cl->netchan.message); -} - - -/* -============================================================================== - -CONNECTIONLESS COMMANDS - -============================================================================== -*/ - -/* -================ -SVC_Status - -Responds with all the info that qplug or qspy can see -This message can be up to around 5k with worst case string lengths. -================ -*/ -void SVC_Status (void) -{ - int i; - client_t *cl; - int ping; - int top, bottom; - - SV_BeginRedirect (RD_PACKET); - Con_Printf ("%s\n", svs.info); - for (i=0 ; istate == cs_connected || cl->state == cs_spawned ) && !cl->spectator) - { - top = atoi(Info_ValueForKey (cl->userinfo, "topcolor")); - bottom = atoi(Info_ValueForKey (cl->userinfo, "bottomcolor")); - top = (top < 0) ? 0 : ((top > 13) ? 13 : top); - bottom = (bottom < 0) ? 0 : ((bottom > 13) ? 13 : bottom); - ping = SV_CalcPing (cl); - Con_Printf ("%i %i %i %i \"%s\" \"%s\" %i %i\n", cl->userid, - cl->old_frags, (int)(realtime - cl->connection_started)/60, - ping, cl->name, Info_ValueForKey (cl->userinfo, "skin"), top, bottom); - } - } - SV_EndRedirect (); -} - -/* -=================== -SV_CheckLog - -=================== -*/ -#define LOG_HIGHWATER (MAX_DATAGRAM - 128) -#define LOG_FLUSH 10*60 -void SV_CheckLog (void) -{ - sizebuf_t *sz; - - sz = &svs.log[svs.logsequence&1]; - - // bump sequence if allmost full, or ten minutes have passed and - // there is something still sitting there - if (sz->cursize > LOG_HIGHWATER - || (realtime - svs.logtime > LOG_FLUSH && sz->cursize) ) - { - // swap buffers and bump sequence - svs.logtime = realtime; - svs.logsequence++; - sz = &svs.log[svs.logsequence&1]; - sz->cursize = 0; - Con_DPrintf ("beginning fraglog sequence %i\n", svs.logsequence); - } - -} - -/* -================ -SVC_Log - -Responds with all the logged frags for ranking programs. -If a sequence number is passed as a parameter and it is -the same as the current sequence, an A2A_NACK will be returned -instead of the data. -================ -*/ -void SVC_Log (void) -{ - int seq; - char data[MAX_DATAGRAM+64]; - - if (Cmd_Argc() == 2) - seq = atoi(Cmd_Argv(1)); - else - seq = -1; - - if (seq == svs.logsequence-1 || !sv_fraglogfile) - { // they already have this data, or we aren't logging frags - data[0] = A2A_NACK; - NET_SendPacket (net_serversocket, 1, data, net_from); - return; - } - - Con_DPrintf ("sending log %i to %s\n", svs.logsequence-1, NET_AdrToString(net_from)); - - sprintf (data, "stdlog %i\n", svs.logsequence-1); - strcat (data, (char *)svs.log_buf[((svs.logsequence-1)&1)]); - - NET_SendPacket (net_serversocket, strlen(data)+1, data, net_from); -} - -/* -================ -SVC_Ping - -Just responds with an acknowledgement -================ -*/ -void SVC_Ping (void) -{ - char data; - - data = A2A_ACK; - - NET_SendPacket (net_serversocket, 1, &data, net_from); -} - -/* -================= -SVC_GetChallenge - -Returns a challenge number that can be used -in a subsequent client_connect command. -We do this to prevent denial of service attacks that -flood the server with invalid connection IPs. With a -challenge, they must give a valid IP address. -================= -*/ -void SVC_GetChallenge (void) -{ - int i; - int oldest; - int oldestTime; - - oldest = 0; - oldestTime = 0x7fffffff; - - // see if we already have a challenge for this ip - for (i = 0 ; i < MAX_CHALLENGES ; i++) - { - if (NET_CompareBaseAdr (net_from, svs.challenges[i].adr)) - break; - if (svs.challenges[i].time < oldestTime) - { - oldestTime = svs.challenges[i].time; - oldest = i; - } - } - - if (i == MAX_CHALLENGES) - { - // overwrite the oldest - svs.challenges[oldest].challenge = (rand() << 16) ^ rand(); - svs.challenges[oldest].adr = net_from; - svs.challenges[oldest].time = realtime; - i = oldest; - } - - // send it back - Netchan_OutOfBandPrint (net_serversocket, net_from, "%c%i", S2C_CHALLENGE, - svs.challenges[i].challenge); -} - -/* -================== -SVC_DirectConnect - -A connection request that did not come from the master -================== -*/ -void SVC_DirectConnect (void) -{ - char userinfo[1024]; - static int userid; - netadr_t adr; - int i; - client_t *cl, *newcl; - client_t temp; - edict_t *ent; - int edictnum; - char *s; - int clients, spectators; - qboolean spectator; - int qport; - int version; - int challenge; - - version = atoi(Cmd_Argv(1)); - if (version != PROTOCOL_VERSION) - { - Netchan_OutOfBandPrint (net_serversocket, net_from, "%c\nServer is version %4.2f.\n", A2C_PRINT, QW_VERSION); - Con_Printf ("* rejected connect from version %i\n", version); - return; - } - - qport = atoi(Cmd_Argv(2)); - - challenge = atoi(Cmd_Argv(3)); - - // note an extra byte is needed to replace spectator key - Q_strncpyz (userinfo, Cmd_Argv(4), sizeof(userinfo)-1); - - // see if the challenge is valid - for (i=0 ; iuserid = userid; - - // works properly - if (!sv_highchars.value) { - byte *p, *q; - - for (p = (byte *)newcl->userinfo, q = (byte *)userinfo; - *q && p < (byte *)newcl->userinfo + sizeof(newcl->userinfo)-1; q++) - if (*q > 31 && *q <= 127) - *p++ = *q; - } else - Q_strncpyz (newcl->userinfo, userinfo, sizeof(newcl->userinfo)); - - // if there is already a slot for this ip, drop it - for (i=0,cl=svs.clients ; istate == cs_free) - continue; - if (NET_CompareBaseAdr (adr, cl->netchan.remote_address) - && ( cl->netchan.qport == qport - || adr.port == cl->netchan.remote_address.port )) - { - if (cl->state == cs_connected) { - Con_Printf("%s:dup connect\n", NET_AdrToString (adr)); - userid--; - return; - } - - Con_Printf ("%s:reconnect\n", NET_AdrToString (adr)); - SV_DropClient (cl); - break; - } - } - - // count up the clients and spectators - clients = 0; - spectators = 0; - for (i=0,cl=svs.clients ; istate == cs_free) - continue; - if (cl->spectator) - spectators++; - else - clients++; - } - - // if at server limits, refuse connection - if ( maxclients.value > MAX_CLIENTS ) - Cvar_SetValue (&maxclients, MAX_CLIENTS); - if (maxspectators.value > MAX_CLIENTS) - Cvar_SetValue (&maxspectators, MAX_CLIENTS); - if (maxspectators.value + maxclients.value > MAX_CLIENTS) - Cvar_SetValue (&maxspectators, MAX_CLIENTS - maxclients.value); - if ( (spectator && spectators >= (int)maxspectators.value) - || (!spectator && clients >= (int)maxclients.value) ) - { - Con_Printf ("%s:full connect\n", NET_AdrToString (adr)); - Netchan_OutOfBandPrint (net_serversocket, adr, "%c\nserver is full\n\n", A2C_PRINT); - return; - } - - // find a client slot - newcl = NULL; - for (i=0,cl=svs.clients ; istate == cs_free) - { - newcl = cl; - break; - } - } - if (!newcl) - { - Con_Printf ("WARNING: miscounted available clients\n"); - return; - } - - - // build a new connection - // accept the new client - // this is the only place a client_t is ever initialized - *newcl = temp; - - Netchan_OutOfBandPrint (net_serversocket, adr, "%c", S2C_CONNECTION ); - - edictnum = (newcl-svs.clients)+1; - - Netchan_Setup (&newcl->netchan , adr, qport, net_serversocket); - - newcl->state = cs_connected; - - newcl->datagram.allowoverflow = true; - newcl->datagram.data = newcl->datagram_buf; - newcl->datagram.maxsize = sizeof(newcl->datagram_buf); - - // spectator mode can ONLY be set at join time - newcl->spectator = spectator; - - ent = EDICT_NUM(edictnum); - newcl->edict = ent; - - // parse some info from the info strings - SV_ExtractFromUserinfo (newcl); - - // JACK: Init the floodprot stuff. - for (i=0; i<10; i++) - newcl->whensaid[i] = 0.0; - newcl->whensaidhead = 0; - newcl->lockedtill = 0; - - // call the progs to get default spawn parms for the new client - PR_ExecuteProgram (pr_global_struct->SetNewParms); - for (i=0 ; ispawn_parms[i] = (&pr_global_struct->parm1)[i]; - - if (newcl->spectator) - Con_Printf ("Spectator %s connected\n", newcl->name); - else - Con_DPrintf ("Client %s connected\n", newcl->name); - newcl->sendinfo = true; -} - -int Rcon_Validate (void) -{ - if (!strlen (rcon_password.string)) - return 0; - - if (strcmp (Cmd_Argv(1), rcon_password.string) ) - return 0; - - return 1; -} - -/* -=============== -SVC_RemoteCommand - -A client issued an rcon command. -Shift down the remaining args -Redirect all printfs -=============== -*/ -void SVC_RemoteCommand (void) -{ - int i; - char remaining[1024]; - char *hide, *p; - - - if (!Rcon_Validate ()) { - Con_Printf ("Bad rcon from %s:\n%s\n" - , NET_AdrToString (net_from), net_message.data+4); - - SV_BeginRedirect (RD_PACKET); - - Con_Printf ("Bad rcon_password.\n"); - - } else { - hide = net_message.data + 9; - p = rcon_password.string; - while (*p) { - p++; - *hide++ = '*'; - } - - Con_Printf ("Rcon from %s:\n%s\n" - , NET_AdrToString (net_from), net_message.data+4); - - SV_BeginRedirect (RD_PACKET); - - remaining[0] = 0; - - for (i=2 ; i -removeip - -The ip address is specified in dot format, and any unspecified digits will match any value, so you can specify an entire class C network with "addip 192.246.40". - -Removeip will only remove an address specified exactly the same way. You cannot addip a subnet, then removeip a single host. - -listip -Prints the current list of filters. - -writeip -Dumps "addip " commands to listip.cfg so it can be execed at a later date. The filter lists are not saved and restored by default, because I beleive it would cause too much confusion. - -filterban <0 or 1> - -If 1 (the default), then ip addresses matching the current list will be prohibited from entering the game. This is the default setting. - -If 0, then only addresses matching the list will be allowed. This lets you easily set up a private game, or a game that only allows players from your local network. - - -============================================================================== -*/ - - -typedef struct -{ - unsigned mask; - unsigned compare; -} ipfilter_t; - -#define MAX_IPFILTERS 1024 - -ipfilter_t ipfilters[MAX_IPFILTERS]; -int numipfilters; - -cvar_t filterban = {"filterban", "1"}; - -/* -================= -StringToFilter -================= -*/ -qboolean StringToFilter (char *s, ipfilter_t *f) -{ - char num[128]; - int i, j; - byte b[4]; - byte m[4]; - - for (i=0 ; i<4 ; i++) - { - b[i] = 0; - m[i] = 0; - } - - for (i=0 ; i<4 ; i++) - { - if (*s < '0' || *s > '9') - { - Con_Printf ("Bad filter address: %s\n", s); - return false; - } - - j = 0; - while (*s >= '0' && *s <= '9') - { - num[j++] = *s++; - } - num[j] = 0; - b[i] = atoi(num); - if (b[i] != 0) - m[i] = 255; - - if (!*s) - break; - s++; - } - - f->mask = *(unsigned *)m; - f->compare = *(unsigned *)b; - - return true; -} - -/* -================= -SV_AddIP_f -================= -*/ -void SV_AddIP_f (void) -{ - int i; - - for (i=0 ; istate == cs_free) - continue; - if (!NET_CompareBaseAdr (net_from, cl->netchan.remote_address)) - continue; - if (cl->netchan.qport != qport) - continue; - if (cl->netchan.remote_address.port != net_from.port) - { - Con_DPrintf ("SV_ReadPackets: fixing up a translated port\n"); - cl->netchan.remote_address.port = net_from.port; - } - if (Netchan_Process(&cl->netchan)) - { // this is a valid, sequenced packet, so process it - svs.stats.packets++; - good = true; - cl->send_message = true; // reply at end of frame - if (cl->state != cs_zombie) - SV_ExecuteClientMessage (cl); - } - break; - } - - if (i != MAX_CLIENTS) - continue; - - // packet is not from a known client - // Con_Printf ("%s:sequenced packet without connection\n" - // ,NET_AdrToString(net_from)); - } -} - -/* -================== -SV_CheckTimeouts - -If a packet has not been received from a client in timeout.value -seconds, drop the conneciton. - -When a client is normally dropped, the client_t goes into a zombie state -for a few seconds to make sure any final reliable message gets resent -if necessary -================== -*/ -void SV_CheckTimeouts (void) -{ - int i; - client_t *cl; - float droptime; - int nclients; - - droptime = realtime - timeout.value; - nclients = 0; - - for (i=0,cl=svs.clients ; istate == cs_connected || cl->state == cs_spawned) { - if (!cl->spectator) - nclients++; - if (cl->netchan.last_received < droptime) { - SV_BroadcastPrintf (PRINT_HIGH, "%s timed out\n", cl->name); - SV_DropClient (cl); - cl->state = cs_free; // don't bother with zombie state - } - } - if (cl->state == cs_zombie && - realtime - cl->connection_started > zombietime.value) - { - cl->state = cs_free; // can now be reused - } - } - if (sv.paused && !nclients) { - // nobody left, unpause the server - SV_TogglePause("Pause released since no players are left.\n"); - } -} - -/* -=================== -SV_GetConsoleCommands - -Add them exactly as if they had been typed at the console -=================== -*/ -void SV_GetConsoleCommands (void) -{ - char *cmd; - - while (1) - { - cmd = Sys_ConsoleInput (); - if (!cmd) - break; - Cbuf_AddText (cmd); - Cbuf_AddText ("\n"); - } -} - - -/* -=================== -SV_BoundRate -=================== -*/ -int SV_BoundRate (int rate) -{ - if (!rate) - rate = 2500; - if (sv_maxrate.value && rate > sv_maxrate.value) - rate = sv_maxrate.value; - if (rate < 500) - rate = 500; - if (rate > 20000) - rate = 20000; - - return rate; -} - - -/* -=================== -SV_CheckVars - -=================== -*/ - -void SV_CheckMultiPOV(void); - -void SV_CheckVars (void) -{ - static char pw[MAX_INFO_STRING]="", spw[MAX_INFO_STRING]=""; - static float old_maxrate = 0; - static float old_tp = 0, old_multiPOVlevel = 0; - int v; - -// check password and spectator_password - if (strcmp(password.string, pw) || - strcmp(spectator_password.string, spw)) - { - Q_strncpyz (pw, password.string, sizeof(pw)); - Q_strncpyz (spw, spectator_password.string, sizeof(spw)); - Cvar_Set (&password, pw); - Cvar_Set (&spectator_password, spw); - - v = 0; - if (pw && pw[0] && strcmp(pw, "none")) - v |= 1; - if (spw && spw[0] && strcmp(spw, "none")) - v |= 2; - - Con_DPrintf ("Updated needpass.\n"); - if (!v) - Info_SetValueForKey (svs.info, "needpass", "", MAX_SERVERINFO_STRING); - else - Info_SetValueForKey (svs.info, "needpass", va("%i",v), MAX_SERVERINFO_STRING); - } - -// check sv_maxrate - if (sv_maxrate.value != old_maxrate) { - client_t *cl; - int i; - char *val; - - old_maxrate = sv_maxrate.value; - - for (i=0, cl = svs.clients ; istate < cs_connected) - continue; - - val = Info_ValueForKey (cl->userinfo, "rate"); - cl->netchan.rate = 1.0 / SV_BoundRate (atoi(val)); - } - } - - if (sv_multiPOVlevel.value < 0.0) { - Cvar_SetValue(&sv_multiPOVlevel, 0.0); - } - - if (sv_multiPOVlevel.value > 3.0) { - Cvar_SetValue(&sv_multiPOVlevel, 3.0); - } - - if (!old_tp != !teamplay.value || old_multiPOVlevel != sv_multiPOVlevel.value) - { - old_tp = teamplay.value; - old_multiPOVlevel = sv_multiPOVlevel.value; - - SV_CheckMultiPOV(); - } -} - - -/* -================== -SV_Frame - -================== -*/ -void SV_Frame (double time) -{ - static double start, end; - double demo_start, demo_end; - -#if 0 // disabled for now - - if (sv.state != ss_active || cls.state != ca_active || (int)maxclients.value > 1 || key_dest == key_game) - { - sv.paused &= ~2; - cl.paused &= ~4; - } - else - { - sv.paused |= 2; - cl.paused |= 4; - } -#endif - - start = Sys_DoubleTime (); - svs.stats.idle += start - end; - -// keep the random time dependent - rand (); - -// decide the simulation time - if (!sv.paused) - { - - realtime += time; - sv.time += time; - } - -// check timeouts - SV_CheckTimeouts (); - -// toggle the log buffer if full - SV_CheckLog (); - -// move autonomous things around if enough time has passed - if (!sv.paused) - SV_Physics (); - -// get packets - SV_ReadPackets (); - - -// check for commands typed to the host - SV_GetConsoleCommands (); - -// process console commands - Cbuf_Execute (); - - SV_CheckVars (); - -// send messages back to the clients that had packets read this frame - SV_SendClientMessages (); - - demo_start = Sys_DoubleTime (); - SV_SendDemoMessage(); - demo_end = Sys_DoubleTime (); - svs.stats.demo += demo_end - demo_start; - -// send a heartbeat to the master if needed - Master_Heartbeat (); - -// collect timing statistics - end = Sys_DoubleTime (); - svs.stats.active += end-start; - if (++svs.stats.count == STATFRAMES) - { - svs.stats.latched_active = svs.stats.active; - svs.stats.latched_idle = svs.stats.idle; - svs.stats.latched_packets = svs.stats.packets; - svs.stats.latched_demo = svs.stats.demo; - svs.stats.active = 0; - svs.stats.idle = 0; - svs.stats.packets = 0; - svs.stats.count = 0; - svs.stats.demo = 0; - } -} - -/* -=============== -SV_InitLocal -=============== -*/ -void SV_Record_f (void); -void SV_EasyRecord_f (void); -void SV_DemoList_f (void); -void SV_DemoRemove_f (void); -void SV_DemoRemoveNum_f (void); - -void SV_InitLocal (void) -{ - int i; - extern cvar_t sv_maxvelocity; - extern cvar_t sv_gravity; - extern cvar_t sv_aim; - extern cvar_t sv_stopspeed; - extern cvar_t sv_spectatormaxspeed; - extern cvar_t sv_accelerate; - extern cvar_t sv_airaccelerate; - extern cvar_t sv_wateraccelerate; - extern cvar_t sv_friction; - extern cvar_t sv_waterfriction; - extern cvar_t sv_nailhack; - - - Cvar_Init (); - - SV_InitOperatorCommands (); - SV_UserInit (); - - Cvar_RegisterVariable (&rcon_password); - Cvar_RegisterVariable (&password); - Cvar_RegisterVariable (&spectator_password); - - Cvar_RegisterVariable (&sv_nailhack); - - Cvar_RegisterVariable (&sv_mintic); - Cvar_RegisterVariable (&sv_maxtic); - - Cvar_RegisterVariable (&skill); - Cvar_RegisterVariable (&coop); - - Cvar_RegisterVariable (&fraglimit); - Cvar_RegisterVariable (&timelimit); - Cvar_RegisterVariable (&teamplay); - Cvar_RegisterVariable (&samelevel); - Cvar_RegisterVariable (&maxclients); - Cvar_RegisterVariable (&maxspectators); - Cvar_RegisterVariable (&hostname); - Cvar_RegisterVariable (&deathmatch); - Cvar_RegisterVariable (&spawn); - Cvar_RegisterVariable (&watervis); - Cvar_RegisterVariable (&serverdemo); - Cvar_RegisterVariable (&version); - - Cvar_RegisterVariable (&developer); - - Cvar_RegisterVariable (&timeout); - Cvar_RegisterVariable (&zombietime); - - Cvar_RegisterVariable (&sv_maxvelocity); - Cvar_RegisterVariable (&sv_gravity); - Cvar_RegisterVariable (&sv_stopspeed); - Cvar_RegisterVariable (&sv_maxspeed); - Cvar_RegisterVariable (&sv_spectatormaxspeed); - Cvar_RegisterVariable (&sv_accelerate); - Cvar_RegisterVariable (&sv_airaccelerate); - Cvar_RegisterVariable (&sv_wateraccelerate); - Cvar_RegisterVariable (&sv_friction); - Cvar_RegisterVariable (&sv_waterfriction); - - Cvar_RegisterVariable (&sv_aim); - - Cvar_RegisterVariable (&filterban); - - Cvar_RegisterVariable (&allow_download); - Cvar_RegisterVariable (&allow_download_skins); - Cvar_RegisterVariable (&allow_download_models); - Cvar_RegisterVariable (&allow_download_sounds); - Cvar_RegisterVariable (&allow_download_maps); - - Cvar_RegisterVariable (&sv_highchars); - - Cvar_RegisterVariable (&sv_phs); - - Cvar_RegisterVariable (&pausable); - - Cvar_RegisterVariable (&sv_maxrate); - Cvar_RegisterVariable (&sv_multiPOVlevel); - Cvar_RegisterVariable (&sv_demofps); - Cvar_RegisterVariable (&sv_demoPings); - Cvar_RegisterVariable (&sv_demoNoVis); - Cvar_RegisterVariable (&sv_demoUseCache); - Cvar_RegisterVariable (&sv_demoCacheSize); - Cvar_RegisterVariable (&sv_demoMaxSize); - Cvar_RegisterVariable (&sv_demoMaxDirSize); - - Cmd_AddCommand ("addip", SV_AddIP_f); - Cmd_AddCommand ("removeip", SV_RemoveIP_f); - Cmd_AddCommand ("listip", SV_ListIP_f); - Cmd_AddCommand ("writeip", SV_WriteIP_f); - Cmd_AddCommand ("record", SV_Record_f); - Cmd_AddCommand ("easyrecord", SV_EasyRecord_f); - Cmd_AddCommand ("stop", SV_Stop_f); - Cmd_AddCommand ("demolist", SV_DemoList_f); - Cmd_AddCommand ("rmdemo", SV_DemoRemove_f); - Cmd_AddCommand ("rmdemonum", SV_DemoRemoveNum_f); - - for (i=0 ; i (b)) ? (a) : (b)) -#define min(a,b) (((a) < (b)) ? (a) : (b)) -#endif - -#define MPOV_NONE 0 -#define MPOV_TEAM 1 -#define MPOV_ENEMY 2 -#define MPOV_ALL 3 - -char *mpov_names[] = { - "none", - "team", - "enemy", - "all" -}; - -void SV_SetMultiPOV(client_t *cl) -{ - char *val, team[32], team2[32]; - int num, i, level; - client_t *client; - - val = Info_ValueForKey (cl->userinfo, "multipov"); - if (!strcmp(val, "all")) { - cl->multipov = MPOV_ALL; - } else if (!strcmp(val, "team")) { - cl->multipov = MPOV_TEAM; - } else if (!strcmp(val, "enemy")) { - cl->multipov = MPOV_ENEMY; - } else cl->multipov = MPOV_NONE; - - if (cl->multipov > (int) sv_multiPOVlevel.value) - { - cl->multipov = (int) sv_multiPOVlevel.value; - Info_SetValueForKey (cl->userinfo, "multipov", mpov_names[cl->multipov], MAX_INFO_STRING); - MSG_WriteByte (&cl->netchan.message, svc_stufftext); - MSG_WriteString (&cl->netchan.message, va("setinfo multipov %s\n", mpov_names[cl->multipov] ) ); - - if (cl->state == cs_spawned) { - if (!cl->multipov) { - SV_ClientPrintf (cl, PRINT_HIGH, "multipov feature is disabled on this server\n"); - return; - } - - SV_ClientPrintf (cl, PRINT_HIGH, "multipov \"%s\" not allowed\nyour multipov setting has been changed to \"%s\"", val, mpov_names[cl->multipov]); - } - } - - // Decide client team, spectators uses tracking player team - strcpy(team,cl->team); - if (cl->spec_track > 0 && cl->spec_track <= MAX_CLIENTS) { - strcpy(team, svs.clients[cl->spec_track - 1].team); - } else if (cl->spectator) team[0] = 0; - - cl->teamPOVs; // it will be build from scratch - num = cl - svs.clients; - - for (i=0, client=svs.clients ; istate != cs_spawned) - continue; - - // decide client team, spectators uses tracking player team - strcpy(team2, client->team); - - if (client->spec_track > 0 && client->spec_track <= MAX_CLIENTS) { - strcpy(team2, svs.clients[client->spec_track - 1].team); - } else if (client->spectator) team2[0] = 0; - - if (!client->spectator && !strcmp(team, team2)) { - cl->teamPOVs |= 1 << i; - } - - if (!cl->spectator && !strcmp(team, team2)) { - client->teamPOVs |= 1 << num; - continue; - } - - client->teamPOVs &= ~(1 << num); - - } - - level = min(sv_multiPOVlevel.value, cl->multipov); - - if (!level) { - cl->POVs = 0; - return; - } - - if (level == MPOV_ALL) { - cl->POVs = ~0; - return; - } - - if (!teamplay.value) { - if (level == MPOV_TEAM) { - cl->POVs = 0; - return; - } - - cl->POVs = ~0; - return; - } - - if (level == MPOV_TEAM) { - cl->POVs = cl->teamPOVs; - return; - } - - cl->POVs = ~cl->teamPOVs; -} - -/* -==================== -SV_CheckMultiPOV - -Either sv_multiPOVlevel or teamplay cvar changed so fix multipov settings -==================== -*/ - -void SV_CheckMultiPOV(void) -{ - client_t *client; - int i, level; - - level = (int) sv_multiPOVlevel.value; - - for (i = 0, client = svs.clients; i < MAX_CLIENTS; i++, client++) - { - if (client->state != cs_spawned) - continue; - - if (client->multipov > level) { - SV_SetMultiPOV(client); - continue; - } - - if (!level) { - client->POVs = 0; - continue; - } - - if (level == MPOV_ALL) { - client->POVs = ~0; - continue; - } - - if (!teamplay.value) { - if (level == MPOV_TEAM) { - client->POVs = 0; - continue; - } - - client->POVs = ~0; - continue; - } - - if (level == MPOV_TEAM) { - client->POVs = client->teamPOVs; - continue; - } - - client->POVs = ~client->teamPOVs; - } -} - - -/* -================= -SV_ExtractFromUserinfo - -Pull specific info from a newly changed userinfo string -into a more C freindly form. -================= -*/ - -void SV_ExtractFromUserinfo (client_t *cl) -{ - char *val, *p, *q; - int i; - client_t *client; - int dupc = 1; - char newname[80]; - extern cvar_t sv_maxrate; - - // name for C code - val = Info_ValueForKey (cl->userinfo, "name"); - - // trim user name - Q_strncpyz (newname, val, sizeof(newname)); - - for (p = newname; (*p == ' ' || *p == '\r' || *p == '\n') && *p; p++) - ; - - if (p != newname && !*p) { - //white space only - strcpy(newname, "unnamed"); - p = newname; - } - - if (p != newname && *p) { - for (q = newname; *p; *q++ = *p++) - ; - *q = 0; - } - for (p = newname + strlen(newname) - 1; p != newname && (*p == ' ' || *p == '\r' || *p == '\n') ; p--) - ; - p[1] = 0; - - if (strcmp(val, newname)) { - Info_SetValueForKey (cl->userinfo, "name", newname, MAX_INFO_STRING); - val = Info_ValueForKey (cl->userinfo, "name"); - } - - if (!val[0] || !stricmp(val, "console")) { - Info_SetValueForKey (cl->userinfo, "name", "unnamed", MAX_INFO_STRING); - val = Info_ValueForKey (cl->userinfo, "name"); - } - - // check to see if another user by the same name exists - while (1) { - for (i=0, client = svs.clients ; istate != cs_spawned || client == cl) - continue; - if (!stricmp(client->name, val)) - break; - } - if (i != MAX_CLIENTS) { // dup name - if (strlen(val) > sizeof(cl->name) - 1) - val[sizeof(cl->name) - 4] = 0; - p = val; - - if (val[0] == '(') - if (val[2] == ')') - p = val + 3; - else if (val[3] == ')') - p = val + 4; - - sprintf(newname, "(%d)%-.40s", dupc++, p); - Info_SetValueForKey (cl->userinfo, "name", newname, MAX_INFO_STRING); - val = Info_ValueForKey (cl->userinfo, "name"); - } else - break; - } - - if (strncmp(val, cl->name, strlen(cl->name))) { - if (!sv.paused) { - if (!cl->lastnametime || realtime - cl->lastnametime > 5) { - cl->lastnamecount = 0; - cl->lastnametime = realtime; - } else if (cl->lastnamecount++ > 4) { - SV_BroadcastPrintf (PRINT_HIGH, "%s was kicked for name spam\n", cl->name); - SV_ClientPrintf (cl, PRINT_HIGH, "You were kicked from the game for name spamming\n"); - SV_DropClient (cl); - return; - } - } - - if (cl->state >= cs_spawned && !cl->spectator) - SV_BroadcastPrintf (PRINT_HIGH, "%s changed name to %s\n", cl->name, val); - } - - - Q_strncpyz (cl->name, val, sizeof(cl->name)); - - // team - Q_strncpyz (cl->team, Info_ValueForKey (cl->userinfo, "team"), sizeof(cl->team)); - - // rate - val = Info_ValueForKey (cl->userinfo, "rate"); - cl->netchan.rate = 1.0 / SV_BoundRate (atoi(val)); - - // message level - val = Info_ValueForKey (cl->userinfo, "msg"); - if (strlen(val)) - { - cl->messagelevel = atoi(val); - } - - SV_SetMultiPOV(cl); -} - - -//============================================================================ - -/* -==================== -SV_InitNet -==================== -*/ -void SV_InitNet (void) -{ - int port; - int p; - - port = PORT_SERVER; - p = COM_CheckParm ("-port"); - if (p && p < com_argc) - { - port = atoi(com_argv[p+1]); - Con_Printf ("Port: %i\n", port); - } - NET_Init (0, port); - - Netchan_Init (); - - // heartbeats will always be sent to the id master - svs.last_heartbeat = -99999; // send immediately -// NET_StringToAdr ("192.246.40.70:27000", &idmaster_adr); -} - - -/* -==================== -SV_Init -==================== -*/ -void SV_Init (quakeparms_t *parms) -{ - COM_InitArgv (parms->argc, parms->argv); - - if (COM_CheckParm ("-minmemory")) - parms->memsize = MINIMUM_MEMORY; - - host_parms = *parms; - - if (parms->memsize < MINIMUM_MEMORY) - SV_Error ("Only %4.1f megs of memory reported, can't execute game", parms->memsize / (float)0x100000); - - Memory_Init (parms->membase, parms->memsize); - Cbuf_Init (); - Cmd_Init (); - - COM_Init (); - - PR_Init (); - Mod_Init (); - - SV_InitNet (); - - SV_InitLocal (); - Sys_Init (); - Pmove_Init (); - - Demo_Init (); - - Hunk_AllocName (0, "-HOST_HUNKLEVEL-"); - host_hunklevel = Hunk_LowMark (); - - Cbuf_InsertText ("exec server.cfg\n"); - - host_initialized = true; - - Con_Printf ("Exe: "__TIME__" "__DATE__"\n"); - Con_Printf ("%4.1f megabyte heap\n",parms->memsize/ (1024*1024.0)); - -#ifdef RELEASE_VERSION - Con_Printf ("\nServer Version %s\n\n", QWE_VERSION); -#else - Con_Printf ("\nServer Version %s (Build %04d)\n\n", QWE_VERSION, build_number()); -#endif - - Con_Printf ("QWExtended Project home page: http://qwex.n3.net/\n\n"); - - Con_Printf ("======== QuakeWorld Initialized ========\n"); - -// process command line arguments - Cmd_StuffCmds_f (); - Cbuf_Execute (); - -// if a map wasn't specified on the command line, spawn start map - if (sv.state == ss_dead) - Cmd_ExecuteString ("map start"); - if (sv.state == ss_dead) - SV_Error ("Couldn't spawn a server"); -} +/* +Copyright (C) 1996-1997 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 "version.h" + +#include "qwsvdef.h" + +quakeparms_t host_parms; + +qboolean host_initialized; // true if into command execution (compatability) + +float sv_frametime; + + +double realtime; // without any filtering or bounding + +int host_hunklevel; + +int current_skill; // for entity spawnflags checking + +netadr_t master_adr[MAX_MASTERS]; // address of group servers + +client_t *host_client; // current client + +cvar_t sv_cpserver = {"sv_cpserver", "0"}; // some cp servers couse lags on map changes + +cvar_t sv_ticrate = {"sv_ticrate","0.014"}; // bound the size of the +cvar_t sv_mintic = {"sv_mintic","0.03"}; // bound the size of the + +cvar_t sv_maxtic = {"sv_maxtic","0.1"}; // + + +cvar_t developer = {"developer","0"}; // show extra messages + +cvar_t timeout = {"timeout","65"}; // seconds without any message +cvar_t zombietime = {"zombietime", "2"}; // seconds to sink messages + // after disconnect + +cvar_t rcon_password = {"rcon_password", ""}; // password for remote server commands +cvar_t password = {"password", ""}; // password for entering the game + +cvar_t spectator_password = {"spectator_password", ""}; // password for entering as a sepctator +cvar_t vip_password = {"vip_password", ""}; // password for entering as a VIP sepctator + +cvar_t allow_download = {"allow_download", "1"}; +cvar_t allow_download_skins = {"allow_download_skins", "1"}; +cvar_t allow_download_models = {"allow_download_models", "1"}; +cvar_t allow_download_sounds = {"allow_download_sounds", "1"}; +cvar_t allow_download_maps = {"allow_download_maps", "1"}; +cvar_t allow_download_demos = {"allow_download_demos", "1"}; + +cvar_t sv_highchars = {"sv_highchars", "1"}; + +cvar_t sv_phs = {"sv_phs", "1"}; + +cvar_t pausable = {"pausable", "1"}; + +cvar_t sv_maxrate = {"sv_maxrate", "0"}; + +cvar_t sv_demofps = {"sv_demofps", "20"}; +cvar_t sv_demoPings = {"sv_demopings", "3"}; +cvar_t sv_demoNoVis = {"sv_demonovis", "1"}; +cvar_t sv_demoUseCache = {"sv_demoUseCache", "0"}; +cvar_t sv_demoCacheSize = {"sv_demoCacheSize", "0", CVAR_ROM}; +cvar_t sv_demoMaxSize = {"sv_demoMaxSize", "20480"}; +cvar_t sv_demoMaxDirSize = {"sv_demoMaxDirSize", "102400"}; + +qboolean sv_demoDir_OnChange(cvar_t *cvar, char *value); +cvar_t sv_demoDir = {"sv_demoDir", "demos", 0, sv_demoDir_OnChange}; + +// +// game rules mirrored in svs.info +// +cvar_t fraglimit = {"fraglimit","0",CVAR_SERVERINFO}; +cvar_t timelimit = {"timelimit","0",CVAR_SERVERINFO}; +cvar_t teamplay = {"teamplay","0",CVAR_SERVERINFO}; +cvar_t samelevel = {"samelevel","0",CVAR_SERVERINFO}; +cvar_t maxclients = {"maxclients","8",CVAR_SERVERINFO}; +cvar_t maxspectators = {"maxspectators","8",CVAR_SERVERINFO}; +cvar_t maxvip_spectators = {"maxvip_spectators","0",CVAR_SERVERINFO}; +cvar_t deathmatch = {"deathmatch","1",CVAR_SERVERINFO}; // 0, 1, or 2 +cvar_t spawn = {"spawn","0",CVAR_SERVERINFO}; +cvar_t watervis = {"watervis","0",CVAR_SERVERINFO}; +cvar_t serverdemo = {"serverdemo","",CVAR_SERVERINFO | CVAR_ROM}; +// not mirrored +cvar_t skill = {"skill", "1"}; +cvar_t coop = {"coop", "0"}; + +cvar_t version = {"version", "QWExtended " QWE_VERSION, CVAR_ROM}; + +cvar_t hostname = {"hostname","unnamed",CVAR_SERVERINFO}; + +FILE *sv_logfile; +FILE *sv_fraglogfile; +FILE *sv_errorlogfile; +FILE *sv_rconlogfile; + +qboolean sv_error = 0; + +void SV_AcceptClient (netadr_t adr, int userid, char *userinfo); +void Master_Shutdown (void); + +//============================================================================ + +qboolean ServerPaused(void) +{ + return sv.paused; +} + + +/* +================ +SV_Shutdown + +Quake calls this before calling Sys_Quit or Sys_Error +================ +*/ +void SV_Shutdown (void) +{ + Master_Shutdown (); + if (sv_logfile) + { + fclose (sv_logfile); + sv_logfile = NULL; + } + if (sv_fraglogfile) + { + fclose (sv_fraglogfile); + sv_logfile = NULL; + } + + if (sv_errorlogfile) + { + fclose (sv_errorlogfile); + sv_errorlogfile = NULL; + } + + if (sv_rconlogfile) + { + fclose (sv_rconlogfile); + sv_rconlogfile = NULL; + } + if (sv.demorecording) + SV_Stop_f(); + + NET_Shutdown (); +} + +/* +================ +SV_Error + +Sends a datagram to all the clients informing them of the server crash, +then exits +================ +*/ +void SV_Error (char *error, ...) +{ + va_list argptr; + static char string[1024]; + static qboolean inerror = false; + + sv_error = true; + + if (inerror) + Sys_Error ("SV_Error: recursively entered (%s)", string); + + inerror = true; + + va_start (argptr,error); + vsprintf (string,error,argptr); + va_end (argptr); + + Con_Printf ("SV_Error: %s\n",string); + + SV_FinalMessage (va("server crashed: %s\n", string)); + + SV_Shutdown (); + + Sys_Error ("SV_Error: %s\n",string); +} + +/* +================== +SV_FinalMessage + +Used by SV_Error and SV_Quit_f to send a final message to all connected +clients before the server goes down. The messages are sent immediately, +not just stuck on the outgoing message list, because the server is going +to totally exit after returning from this function. +================== +*/ +void SV_FinalMessage (char *message) +{ + int i; + client_t *cl; + + SZ_Clear (&net_message); + MSG_WriteByte (&net_message, svc_print); + MSG_WriteByte (&net_message, PRINT_HIGH); + MSG_WriteString (&net_message, message); + MSG_WriteByte (&net_message, svc_disconnect); + + for (i=0, cl = svs.clients ; istate >= cs_spawned) + Netchan_Transmit (&cl->netchan, net_message.cursize + , net_message.data); +} + + + +/* +===================== +SV_DropClient + +Called when the player is totally leaving the server, either willingly +or unwillingly. This is NOT called if the entire server is quiting +or crashing. +===================== +*/ +void SV_DropClient (client_t *drop) +{ + // add the disconnect + MSG_WriteByte (&drop->netchan.message, svc_disconnect); + + if (drop->state == cs_spawned) { + if (!drop->spectator) + { + // call the prog function for removing a client + // this will set the body to a dead frame, among other things + pr_global_struct->self = EDICT_TO_PROG(drop->edict); + PR_ExecuteProgram (pr_global_struct->ClientDisconnect); + } + else if (SpectatorDisconnect) + { + // call the prog function for removing a client + // this will set the body to a dead frame, among other things + pr_global_struct->self = EDICT_TO_PROG(drop->edict); + PR_ExecuteProgram (SpectatorDisconnect); + } + } + + if (drop->spectator) + Con_Printf ("Spectator %s removed\n",drop->name); + else + Con_Printf ("Client %s removed\n",drop->name); + + if (drop->download) + { + fclose (drop->download); + drop->download = NULL; + } + if (drop->upload) + { + fclose (drop->upload); + drop->upload = NULL; + } + *drop->uploadfn = 0; + + SV_Logout(drop); + + drop->state = cs_zombie; // become free in a few seconds + drop->connection_started = realtime; // for zombie timeout + + drop->old_frags = 0; + drop->edict->v.frags = 0; + drop->name[0] = 0; + memset (drop->userinfo, 0, sizeof(drop->userinfo)); + +// send notification to all remaining clients + SV_FullClientUpdate (drop, &sv.reliable_datagram); +} + + +//==================================================================== + +/* +=================== +SV_CalcPing + +=================== +*/ +int SV_CalcPing (client_t *cl) +{ + float ping; + int i; + int count; + register client_frame_t *frame; + + ping = 0; + count = 0; + for (frame = cl->frames, i=0 ; iping_time > 0) + { + ping += frame->ping_time; + count++; + } + } + if (!count) + return 9999; + ping /= count; + + return ping*1000; +} + +/* +=================== +SV_FullClientUpdate + +Writes all update values to a sizebuf +=================== +*/ +void SV_FullClientUpdate (client_t *client, sizebuf_t *buf) +{ + int i; + char info[MAX_INFO_STRING]; + + i = client - svs.clients; + +//Sys_Printf("SV_FullClientUpdate: Updated frags for client %d\n", i); + + MSG_WriteByte (buf, svc_updatefrags); + MSG_WriteByte (buf, i); + MSG_WriteShort (buf, client->old_frags); + + MSG_WriteByte (buf, svc_updateping); + MSG_WriteByte (buf, i); + MSG_WriteShort (buf, SV_CalcPing (client)); + + MSG_WriteByte (buf, svc_updatepl); + MSG_WriteByte (buf, i); + MSG_WriteByte (buf, client->lossage); + + MSG_WriteByte (buf, svc_updateentertime); + MSG_WriteByte (buf, i); + MSG_WriteFloat (buf, realtime - client->connection_started); + + strcpy (info, client->userinfo); + Info_RemovePrefixedKeys (info, '_'); // server passwords, etc + + MSG_WriteByte (buf, svc_updateuserinfo); + MSG_WriteByte (buf, i); + MSG_WriteLong (buf, client->userid); + MSG_WriteString (buf, info); +} + +/* +=================== +SV_FullClientUpdateToClient + +Writes all update values to a client's reliable stream +=================== +*/ +void SV_FullClientUpdateToClient (client_t *client, client_t *cl) +{ + ClientReliableCheckBlock(cl, 24 + strlen(client->userinfo)); + if (cl->num_backbuf) { + SV_FullClientUpdate (client, &cl->backbuf); + ClientReliable_FinishWrite(cl); + } else + SV_FullClientUpdate (client, &cl->netchan.message); +} + + +/* +============================================================================== + +CONNECTIONLESS COMMANDS + +============================================================================== +*/ + +/* +================ +SVC_Status + +Responds with all the info that qplug or qspy can see +This message can be up to around 5k with worst case string lengths. +================ +*/ +void SVC_Status (void) +{ + int i; + client_t *cl; + int ping; + int top, bottom; + + SV_BeginRedirect (RD_PACKET); + Con_Printf ("%s\n", svs.info); + for (i=0 ; istate >= cs_preconnected/* || cl->state == cs_spawned */) && !cl->spectator) + { + top = atoi(Info_ValueForKey (cl->userinfo, "topcolor")); + bottom = atoi(Info_ValueForKey (cl->userinfo, "bottomcolor")); + top = (top < 0) ? 0 : ((top > 13) ? 13 : top); + bottom = (bottom < 0) ? 0 : ((bottom > 13) ? 13 : bottom); + ping = SV_CalcPing (cl); + Con_Printf ("%i %i %i %i \"%s\" \"%s\" %i %i\n", cl->userid, + cl->old_frags, (int)(realtime - cl->connection_started)/60, + ping, cl->name, Info_ValueForKey (cl->userinfo, "skin"), top, bottom); + } + } + SV_EndRedirect (); +} + +/* +=================== +SV_CheckLog + +=================== +*/ +#define LOG_HIGHWATER (MAX_DATAGRAM - 128) +#define LOG_FLUSH 10*60 +void SV_CheckLog (void) +{ + sizebuf_t *sz; + + sz = &svs.log[svs.logsequence&1]; + + // bump sequence if allmost full, or ten minutes have passed and + // there is something still sitting there + if (sz->cursize > LOG_HIGHWATER + || (realtime - svs.logtime > LOG_FLUSH && sz->cursize) ) + { + // swap buffers and bump sequence + svs.logtime = realtime; + svs.logsequence++; + sz = &svs.log[svs.logsequence&1]; + sz->cursize = 0; + Con_DPrintf ("beginning fraglog sequence %i\n", svs.logsequence); + } + +} + +/* +================ +SVC_Log + +Responds with all the logged frags for ranking programs. +If a sequence number is passed as a parameter and it is +the same as the current sequence, an A2A_NACK will be returned +instead of the data. +================ +*/ +void SVC_Log (void) +{ + int seq; + char data[MAX_DATAGRAM+64]; + + if (Cmd_Argc() == 2) + seq = atoi(Cmd_Argv(1)); + else + seq = -1; + + if (seq == svs.logsequence-1 || !sv_fraglogfile) + { // they already have this data, or we aren't logging frags + data[0] = A2A_NACK; + NET_SendPacket (net_serversocket, 1, data, net_from); + return; + } + + Con_DPrintf ("sending log %i to %s\n", svs.logsequence-1, NET_AdrToString(net_from)); + + sprintf (data, "stdlog %i\n", svs.logsequence-1); + strcat (data, (char *)svs.log_buf[((svs.logsequence-1)&1)]); + + NET_SendPacket (net_serversocket, strlen(data)+1, data, net_from); +} + +/* +================ +SVC_Ping + +Just responds with an acknowledgement +================ +*/ +void SVC_Ping (void) +{ + char data; + + data = A2A_ACK; + + NET_SendPacket (net_serversocket, 1, &data, net_from); +} + +/* +================= +SVC_GetChallenge + +Returns a challenge number that can be used +in a subsequent client_connect command. +We do this to prevent denial of service attacks that +flood the server with invalid connection IPs. With a +challenge, they must give a valid IP address. +================= +*/ +void SVC_GetChallenge (void) +{ + int i; + int oldest; + int oldestTime; + + oldest = 0; + oldestTime = 0x7fffffff; + + // see if we already have a challenge for this ip + for (i = 0 ; i < MAX_CHALLENGES ; i++) + { + if (NET_CompareBaseAdr (net_from, svs.challenges[i].adr)) + break; + if (svs.challenges[i].time < oldestTime) + { + oldestTime = svs.challenges[i].time; + oldest = i; + } + } + + if (i == MAX_CHALLENGES) + { + // overwrite the oldest + svs.challenges[oldest].challenge = (rand() << 16) ^ rand(); + svs.challenges[oldest].adr = net_from; + svs.challenges[oldest].time = realtime; + i = oldest; + } + + // send it back + Netchan_OutOfBandPrint (net_serversocket, net_from, "%c%i", S2C_CHALLENGE, + svs.challenges[i].challenge); +} + +qboolean ValidateUserInfo(char *userinfo) +{ + while (*userinfo) + { + if (*userinfo == '\\') + userinfo++; + + if (*userinfo++ == '\\') + return false; + while (*userinfo && *userinfo != '\\') + userinfo++; + } + return true; +} + +/* +================== +SVC_DirectConnect + +A connection request that did not come from the master +================== +*/ +int SV_VIPbyIP(netadr_t adr); +int SV_VIPbyPass (char *pass); + +void SVC_DirectConnect (void) +{ + char userinfo[1024], str[128]; + static int userid; + netadr_t adr; + int i; + client_t *cl, *newcl; + client_t temp; + edict_t *ent; + int edictnum; + char *s; + int clients, spectators, vips; + qboolean spectator; + int qport; + int version; + int challenge; + qboolean spass, vip; + + version = atoi(Cmd_Argv(1)); + if (version != PROTOCOL_VERSION) + { + Netchan_OutOfBandPrint (net_serversocket, net_from, "%c\nServer is version %4.2f.\n", A2C_PRINT, QW_VERSION); + Con_Printf ("* rejected connect from version %i\n", version); + return; + } + + qport = atoi(Cmd_Argv(2)); + + challenge = atoi(Cmd_Argv(3)); + + // note an extra byte is needed to replace spectator key + Q_strncpyz (userinfo, Cmd_Argv(4), sizeof(userinfo)-1); + if (!ValidateUserInfo(userinfo)) { + Netchan_OutOfBandPrint (net_serversocket, net_from, "%c\nInvalid userinfo. Restart your qwcl\n", A2C_PRINT); + return; + } + + // see if the challenge is valid + for (i=0 ; iuserid = userid; + + // works properly + if (!sv_highchars.value) { + byte *p, *q; + + for (p = (byte *)newcl->userinfo, q = (byte *)userinfo; + *q && p < (byte *)newcl->userinfo + sizeof(newcl->userinfo)-1; q++) + if (*q > 31 && *q <= 127) + *p++ = *q; + } else + Q_strncpyz (newcl->userinfo, userinfo, sizeof(newcl->userinfo)); + + // if there is already a slot for this ip, drop it + for (i=0,cl=svs.clients ; istate == cs_free) + continue; + if (NET_CompareBaseAdr (adr, cl->netchan.remote_address) + && ( cl->netchan.qport == qport + || adr.port == cl->netchan.remote_address.port )) + { + if (cl->state == cs_connected || cl->state == cs_preconnected) { + Con_Printf("%s:dup connect\n", NET_AdrToString (adr)); + userid--; + return; + } + + Con_Printf ("%s:reconnect\n", NET_AdrToString (adr)); + SV_DropClient (cl); + break; + } + } + + // count up the clients and spectators + clients = 0; + spectators = 0; + vips = 0; + for (i=0,cl=svs.clients ; istate == cs_free) + continue; + + if (cl->vip) + vips++; + if (cl->spectator) { + if (!cl->vip) + spectators++; + } else + clients++; + } + + // if at server limits, refuse connection + if ( maxclients.value > MAX_CLIENTS ) + Cvar_SetValue (&maxclients, MAX_CLIENTS); + if (maxspectators.value > MAX_CLIENTS) + Cvar_SetValue (&maxspectators, MAX_CLIENTS); + if (maxvip_spectators.value > MAX_CLIENTS) + Cvar_SetValue (&maxvip_spectators, MAX_CLIENTS); + + if (maxspectators.value + maxclients.value > MAX_CLIENTS) + Cvar_SetValue (&maxspectators, MAX_CLIENTS - maxclients.value); + if (maxspectators.value + maxclients.value + maxvip_spectators.value > MAX_CLIENTS) + Cvar_SetValue (&maxvip_spectators, MAX_CLIENTS - maxclients.value - maxspectators.value); + + if ( (vip && spectator && vips >= (int)maxvip_spectators.value && (spectators >= (int)maxspectators.value || !spass)) + || (!vip && spectator && (spectators >= (int)maxspectators.value || !spass)) + || (!spectator && clients >= (int)maxclients.value)) + { + if (spectator == 2 && maxvip_spectators.value > vips && !vip) + { + newcl->rip_vip = true; // yet can be connected if realip is on vip list + newcl->vip = 1; // :) + } else { + Con_Printf ("%s:full connect\n", NET_AdrToString (adr)); + Netchan_OutOfBandPrint (net_serversocket, adr, "%c\nserver is full\n\n", A2C_PRINT); + return; + } + } + + // find a client slot + newcl = NULL; + for (i=0,cl=svs.clients ; istate == cs_free) + { + newcl = cl; + break; + } + } + if (!newcl) + { + Con_Printf ("WARNING: miscounted available clients\n"); + return; + } + + + // build a new connection + // accept the new client + // this is the only place a client_t is ever initialized + *newcl = temp; + for (i = 0; i < UPDATE_BACKUP; i++) + newcl->frames[i].entities.entities = cl_entities[newcl-svs.clients][i]; + + + Netchan_OutOfBandPrint (net_serversocket, adr, "%c", S2C_CONNECTION ); + + edictnum = (newcl-svs.clients)+1; + + Netchan_Setup (&newcl->netchan , adr, qport, net_serversocket); + + newcl->state = cs_preconnected; + + newcl->datagram.allowoverflow = true; + newcl->datagram.data = newcl->datagram_buf; + newcl->datagram.maxsize = sizeof(newcl->datagram_buf); + + // spectator mode can ONLY be set at join time + newcl->spectator = spectator; + newcl->vip = vip; + + ent = EDICT_NUM(edictnum); + newcl->edict = ent; + + if (vip) s = va("%d", vip); + else s = ""; + + Info_SetValueForStarKey (newcl->userinfo, "*VIP", s, MAX_INFO_STRING); + + // parse some info from the info strings + SV_ExtractFromUserinfo (newcl, true); + + // JACK: Init the floodprot stuff. + for (i=0; i<10; i++) + newcl->whensaid[i] = 0.0; + newcl->whensaidhead = 0; + newcl->lockedtill = 0; + + newcl->realip_num = rand(); + + // call the progs to get default spawn parms for the new client + + PR_ExecuteProgram (pr_global_struct->SetNewParms); + for (i=0 ; ispawn_parms[i] = (&pr_global_struct->parm1)[i]; + + /* + if (newcl->vip && newcl->spectator) + Con_Printf ("VIP spectator %s connected\n", newcl->name); + else if (newcl->spectator) + Con_Printf ("Spectator %s connected\n", newcl->name); + else + Con_DPrintf ("Client %s connected\n", newcl->name); + */ + newcl->sendinfo = true; +} + +int Rcon_Validate (void) +{ + if (!strlen (rcon_password.string)) + return 0; + + if (strcmp (Cmd_Argv(1), rcon_password.string) ) + return 0; + + return 1; +} + +/* +=============== +SVC_RemoteCommand + +A client issued an rcon command. +Shift down the remaining args +Redirect all printfs +=============== +*/ +void SVC_RemoteCommand (void) +{ + int i; + char remaining[1024]; + char *hide, *p; + + + if (!Rcon_Validate ()) { + if (sv_rconlogfile) { + fprintf(sv_rconlogfile, "Bad rcon from %s:\n%s\n" + , NET_AdrToString (net_from), net_message.data+4); + fflush (sv_rconlogfile); + + } + + Con_Printf ("Bad rcon from %s: %s\n" + , NET_AdrToString (net_from), net_message.data+4); + + SV_BeginRedirect (RD_PACKET); + + Con_Printf ("Bad rcon_password.\n"); + + } else { + hide = net_message.data + 9; + p = rcon_password.string; + while (*p) { + p++; + *hide++ = '*'; + } + + if (sv_rconlogfile) { + fprintf(sv_rconlogfile, "Rcon from %s: %s\n" + , NET_AdrToString (net_from), net_message.data+4); + fflush (sv_rconlogfile); + } + + Con_Printf ("Rcon from %s:\n%s\n" + , NET_AdrToString (net_from), net_message.data+4); + + SV_BeginRedirect (RD_PACKET); + + remaining[0] = 0; + + for (i=2 ; i= MAX_CLIENTS) + return; + + client = &svs.clients[num]; + if (client->state != cs_preconnected) + return; + + // prevent cheating + if (client->realip_num != atoi(Cmd_Argv(2))) + return; + + // don't override previously set ip + if (client->realip.ip[0]) + return; + + client->realip = net_from; +} + + +/* +================= +SV_ConnectionlessPacket + +A connectionless packet has four leading 0xff +characters to distinguish it from a game channel. +Clients that are in the game can still send +connectionless packets. +================= +*/ +void SV_ConnectionlessPacket (void) +{ + char *s; + char *c; + + MSG_BeginReading (); + MSG_ReadLong (); // skip the -1 marker + + s = MSG_ReadStringLine (); + + Cmd_TokenizeString (s); + + c = Cmd_Argv(0); + + if (!strcmp(c, "ping") || ( c[0] == A2A_PING && (c[1] == 0 || c[1] == '\n')) ) + { + SVC_Ping (); + return; + } + if (c[0] == A2A_ACK && (c[1] == 0 || c[1] == '\n') ) + { + Con_Printf ("A2A_ACK from %s\n", NET_AdrToString (net_from)); + return; + } + else if (!strcmp(c,"status")) + { + SVC_Status (); + return; + } + else if (!strcmp(c,"log")) + { + SVC_Log (); + return; + } + else if (!strcmp(c,"connect")) + { + SVC_DirectConnect (); + return; + } + else if (!strcmp(c,"getchallenge")) + { + SVC_GetChallenge (); + return; + } + else if (!strcmp(c, "rcon")) + SVC_RemoteCommand (); + else if (!strcmp(c, "ip")) + { + SVC_IP(); + return; + } + else + Con_Printf ("bad connectionless packet from %s:\n%s\n" + , NET_AdrToString (net_from), s); +} + +/* +============================================================================== + +PACKET FILTERING + + +You can add or remove addresses from the filter list with: + +addip +removeip + +The ip address is specified in dot format, and any unspecified digits will match any value, so you can specify an entire class C network with "addip 192.246.40". + +Removeip will only remove an address specified exactly the same way. You cannot addip a subnet, then removeip a single host. + +listip +Prints the current list of filters. + +writeip +Dumps "addip " commands to listip.cfg so it can be execed at a later date. The filter lists are not saved and restored by default, because I beleive it would cause too much confusion. + +filterban <0 or 1> + +If 1 (the default), then ip addresses matching the current list will be prohibited from entering the game. This is the default setting. + +If 0, then only addresses matching the list will be allowed. This lets you easily set up a private game, or a game that only allows players from your local network. + + +============================================================================== +*/ + + +/*typedef struct +{ + unsigned mask; + unsigned compare; + int level; +} ipfilter_t; +*/ + +#define MAX_IPFILTERS 1024 + +ipfilter_t ipfilters[MAX_IPFILTERS]; +int numipfilters; + +ipfilter_t ipvip[MAX_IPFILTERS]; +int numipvips; + +cvar_t filterban = {"filterban", "1"}; + +/* +================= +StringToFilter +================= +*/ +qboolean StringToFilter (char *s, ipfilter_t *f) +{ + char num[128]; + int i, j; + byte b[4]; + byte m[4]; + + for (i=0 ; i<4 ; i++) + { + b[i] = 0; + m[i] = 0; + } + + for (i=0 ; i<4 ; i++) + { + if (*s < '0' || *s > '9') + { + //Con_Printf ("Bad filter address: %s\n", s); + return false; + } + + j = 0; + while (*s >= '0' && *s <= '9') + { + num[j++] = *s++; + } + num[j] = 0; + b[i] = atoi(num); + if (b[i] != 0) + m[i] = 255; + + if (!*s) + break; + s++; + } + + f->mask = *(unsigned *)m; + f->compare = *(unsigned *)b; + + return true; +} + +/* +================= +SV_AddIPVIP_f +================= +*/ +void SV_AddIPVIP_f (void) +{ + int i, l; + ipfilter_t f; + + if (!StringToFilter (Cmd_Argv(1), &f)) { + Con_Printf ("Bad filter address: %s\n", Cmd_Argv(1)); + return; + } + + l = atoi(Cmd_Argv(2)); + + if (l < 1) l = 1; + + for (i=0 ; i 32) + *s++ = *args++; + *s = 0; + + if ((value = Info_ValueForKey (svs.info, key)) == NULL || !*value) + value = Info_ValueForKey(localinfo, key); + + *p++ = '\"'; + if (value) while (*value) + *p++ = *value++; + *p++ = '\"'; + } else while (*args > 32) + *p++ = *args++; + } + + *p = 0; + + return string; +} + +void SV_Script_f (void) +{ + char *path, *p; + char args[1024]; + int i; + + if (Cmd_Argc() < 2) { + Con_Printf("usage: script []\n"); + return; + } + + path = Cmd_Argv(1); + + if (path[0] == '.' && path[1] == '.') + path += 2; + + if (strstr(path,"/..")) { + Con_Printf("invalid path\n"); + return; + } + + + p = Cmd_Args(); + while (*p > 32) + p++; + while (*p && *p <= 32) + p++; + + p = DecodeArgs(p); + + /*args[0] = 0; + for (i = 2; i < Cmd_Argc(); i++) + sprintf(args + strlen(args), "\"%s\" ", Cmd_Argv(i)); + */ + +#ifdef _WIN32 + if (!Sys_Script(path, p)) + Con_Printf("Couldn't run script %s.bat\n", Cmd_Argv(1)); + else + Con_Printf("Runnig %s.bat\n", Cmd_Argv(1)); +#else + Sys_Script(path, p); +#endif +} + +//============================================================================ + +/* +================= +SV_ReadPackets +================= +*/ +void SV_ReadPackets (void) +{ + int i; + client_t *cl; + qboolean good; + int qport; + + good = false; + while (NET_GetPacket(net_serversocket)) + { + if (SV_FilterPacket ()) + { + SV_SendBan (); // tell them we aren't listening... + continue; + } + + // check for connectionless packet (0xffffffff) first + if (*(int *)net_message.data == -1) + { + SV_ConnectionlessPacket (); + continue; + } + + // read the qport out of the message so we can fix up + // stupid address translating routers + MSG_BeginReading (); + MSG_ReadLong (); // sequence number + MSG_ReadLong (); // sequence number + qport = MSG_ReadShort () & 0xffff; + + // check for packets from connected clients + for (i=0, cl=svs.clients ; istate == cs_free) + continue; + if (!NET_CompareBaseAdr (net_from, cl->netchan.remote_address)) + continue; + if (cl->netchan.qport != qport) + continue; + if (cl->netchan.remote_address.port != net_from.port) + { + Con_DPrintf ("SV_ReadPackets: fixing up a translated port\n"); + cl->netchan.remote_address.port = net_from.port; + } + if (Netchan_Process(&cl->netchan)) + { // this is a valid, sequenced packet, so process it + svs.stats.packets++; + good = true; + cl->send_message = true; // reply at end of frame + if (cl->state != cs_zombie) + SV_ExecuteClientMessage (cl); + } + break; + } + + if (i != MAX_CLIENTS) + continue; + + // packet is not from a known client + // Con_Printf ("%s:sequenced packet without connection\n" + // ,NET_AdrToString(net_from)); + } +} + +/* +================== +SV_CheckTimeouts + +If a packet has not been received from a client in timeout.value +seconds, drop the conneciton. + +When a client is normally dropped, the client_t goes into a zombie state +for a few seconds to make sure any final reliable message gets resent +if necessary +================== +*/ +void SV_CheckTimeouts (void) +{ + int i; + client_t *cl; + float droptime; + int nclients; + + droptime = realtime - timeout.value; + nclients = 0; + + for (i=0,cl=svs.clients ; istate >= cs_preconnected /*|| cl->state == cs_spawned*/) { + if (!cl->spectator) + nclients++; + if (cl->netchan.last_received < droptime) { + SV_BroadcastPrintf (PRINT_HIGH, "%s timed out\n", cl->name); + SV_DropClient (cl); + cl->state = cs_free; // don't bother with zombie state + } + if (!cl->logged) + SV_LoginCheckTimeOut(cl); + } + if (cl->state == cs_zombie && + realtime - cl->connection_started > zombietime.value) + { + cl->state = cs_free; // can now be reused + } + } + if (sv.paused && !nclients) { + // nobody left, unpause the server + SV_TogglePause("Pause released since no players are left.\n"); + } +} + +/* +=================== +SV_GetConsoleCommands + +Add them exactly as if they had been typed at the console +=================== +*/ +void SV_GetConsoleCommands (void) +{ + char *cmd; + + while (1) + { + cmd = Sys_ConsoleInput (); + if (!cmd) + break; + Cbuf_AddText (cmd); + Cbuf_AddText ("\n"); + } +} + + +/* +=================== +SV_BoundRate +=================== +*/ +int SV_BoundRate (int rate) +{ + if (!rate) + rate = 2500; + if (sv_maxrate.value && rate > sv_maxrate.value) + rate = sv_maxrate.value; + if (rate < 500) + rate = 500; + if (rate > 100000) + rate = 100000; + + return rate; +} + + +/* +=================== +SV_CheckVars + +=================== +*/ + +void SV_CheckVars (void) +{ + static char pw[MAX_INFO_STRING]="", spw[MAX_INFO_STRING]="", vspw[MAX_INFO_STRING]=""; + static float old_maxrate = 0; + int v; + +// check password and spectator_password + if (strcmp(password.string, pw) || + strcmp(spectator_password.string, spw) || strcmp(vip_password.string, vspw)) + { + Q_strncpyz (pw, password.string, sizeof(pw)); + Q_strncpyz (spw, spectator_password.string, sizeof(spw)); + Q_strncpyz (vspw, vip_password.string, sizeof(vspw)); + Cvar_Set (&password, pw); + Cvar_Set (&spectator_password, spw); + Cvar_Set (&vip_password, vspw); + + v = 0; + if (pw && pw[0] && strcmp(pw, "none")) + v |= 1; + if (spw && spw[0] && strcmp(spw, "none")) + v |= 2; + if (vspw && vspw[0] && strcmp(vspw, "none")) + v |= 4; + + Con_DPrintf ("Updated needpass.\n"); + if (!v) + Info_SetValueForKey (svs.info, "needpass", "", MAX_SERVERINFO_STRING); + else + Info_SetValueForKey (svs.info, "needpass", va("%i",v), MAX_SERVERINFO_STRING); + } + +// check sv_maxrate + if (sv_maxrate.value != old_maxrate) { + client_t *cl; + int i; + char *val; + + old_maxrate = sv_maxrate.value; + + for (i=0, cl = svs.clients ; istate < cs_preconnected) + continue; + + val = Info_ValueForKey (cl->userinfo, "rate"); + cl->netchan.rate = 1.0 / SV_BoundRate (atoi(val)); + } + } +} + +/* +================== +SV_Frame + +================== +*/ +void SV_Frame (double time) +{ + static double start, end; + double demo_start, demo_end; + +#if 0 // disabled for now + + if (sv.state != ss_active || cls.state != ca_active || (int)maxclients.value > 1 || key_dest == key_game) + { + sv.paused &= ~2; + cl.paused &= ~4; + } + else + { + sv.paused |= 2; + cl.paused |= 4; + } +#endif + + start = Sys_DoubleTime (); + svs.stats.idle += start - end; + +// keep the random time dependent + rand (); + +// decide the simulation time + if (!sv.paused) + { + + realtime += time; +#ifndef NEWWAY + sv.time += time; +#endif + sv.gametime += time; + } + +// check timeouts + SV_CheckTimeouts (); + +// toggle the log buffer if full + SV_CheckLog (); + +// check for commands typed to the host + SV_GetConsoleCommands (); + +// process console commands + Cbuf_Execute (); +// check for map change; + SV_Map(true); + + SV_CheckVars (); + +#ifdef NEWWAY + if (sv.gametime < sv.time) + { + // never let the time get too far off + if (sv.time - sv.gametime > 0.1) + { + //if (sv_showclamp->value) + Con_Printf ("sv lowclamp\n"); + sv.gametime = sv.time - 0.1; + } + + end = Sys_DoubleTime (); + svs.stats.active += end-start; + + if (NET_Sleep(sv.time - sv.gametime) <= 0) + return; + + start = Sys_DoubleTime (); + svs.stats.idle += start - end; + //return; + } else if (!sv.paused) + SV_Physics (); + +// get packets + SV_ReadPackets (); +#else + +// get packets + SV_ReadPackets (); +// move autonomous things around if enough time has passed + if (!sv.paused) + SV_Physics (); +#endif + +// send messages back to the clients that had packets read this frame + SV_SendClientMessages (); + + demo_start = Sys_DoubleTime (); + SV_SendDemoMessage(); + demo_end = Sys_DoubleTime (); + svs.stats.demo += demo_end - demo_start; + +// send a heartbeat to the master if needed + Master_Heartbeat (); + +// collect timing statistics + end = Sys_DoubleTime (); + svs.stats.active += end-start; + if (++svs.stats.count == STATFRAMES) + { + svs.stats.latched_active = svs.stats.active; + svs.stats.latched_idle = svs.stats.idle; + svs.stats.latched_packets = svs.stats.packets; + svs.stats.latched_demo = svs.stats.demo; + svs.stats.active = 0; + svs.stats.idle = 0; + svs.stats.packets = 0; + svs.stats.count = 0; + svs.stats.demo = 0; + } +} + +/* +=============== +SV_InitLocal +=============== +*/ +void SV_Record_f (void); +void SV_EasyRecord_f (void); +void SV_DemoList_f (void); +void SV_DemoRemove_f (void); +void SV_DemoRemoveNum_f (void); +void SV_Cancel_f (void); + +void SV_InitLocal (void) +{ + int i; + extern cvar_t sv_maxvelocity; + extern cvar_t sv_gravity; + extern cvar_t sv_aim; + extern cvar_t sv_stopspeed; + extern cvar_t sv_spectatormaxspeed; + extern cvar_t sv_accelerate; + extern cvar_t sv_airaccelerate; + extern cvar_t sv_wateraccelerate; + extern cvar_t sv_friction; + extern cvar_t sv_waterfriction; + extern cvar_t sv_nailhack; + + + Cvar_Init (); + + SV_InitOperatorCommands (); + SV_UserInit (); + + Cvar_RegisterVariable (&sv_cpserver); + Cvar_RegisterVariable (&rcon_password); + Cvar_RegisterVariable (&password); + Cvar_RegisterVariable (&spectator_password); + Cvar_RegisterVariable (&vip_password); + + Cvar_RegisterVariable (&sv_nailhack); + + Cvar_RegisterVariable (&sv_ticrate); + Cvar_RegisterVariable (&sv_mintic); + Cvar_RegisterVariable (&sv_maxtic); + + Cvar_RegisterVariable (&skill); + Cvar_RegisterVariable (&coop); + + Cvar_RegisterVariable (&fraglimit); + Cvar_RegisterVariable (&timelimit); + Cvar_RegisterVariable (&teamplay); + Cvar_RegisterVariable (&samelevel); + Cvar_RegisterVariable (&maxclients); + Cvar_RegisterVariable (&maxspectators); + Cvar_RegisterVariable (&maxvip_spectators); + Cvar_RegisterVariable (&hostname); + Cvar_RegisterVariable (&deathmatch); + Cvar_RegisterVariable (&spawn); + Cvar_RegisterVariable (&watervis); + Cvar_RegisterVariable (&serverdemo); + Cvar_RegisterVariable (&version); + + Cvar_RegisterVariable (&developer); + + Cvar_RegisterVariable (&timeout); + Cvar_RegisterVariable (&zombietime); + + Cvar_RegisterVariable (&sv_maxvelocity); + Cvar_RegisterVariable (&sv_gravity); + Cvar_RegisterVariable (&sv_stopspeed); + Cvar_RegisterVariable (&sv_maxspeed); + Cvar_RegisterVariable (&sv_spectatormaxspeed); + Cvar_RegisterVariable (&sv_accelerate); + Cvar_RegisterVariable (&sv_airaccelerate); + Cvar_RegisterVariable (&sv_wateraccelerate); + Cvar_RegisterVariable (&sv_friction); + Cvar_RegisterVariable (&sv_waterfriction); + + Cvar_RegisterVariable (&sv_aim); + + Cvar_RegisterVariable (&filterban); + + Cvar_RegisterVariable (&allow_download); + Cvar_RegisterVariable (&allow_download_skins); + Cvar_RegisterVariable (&allow_download_models); + Cvar_RegisterVariable (&allow_download_sounds); + Cvar_RegisterVariable (&allow_download_maps); + Cvar_RegisterVariable (&allow_download_demos); + + Cvar_RegisterVariable (&sv_highchars); + + Cvar_RegisterVariable (&sv_phs); + + Cvar_RegisterVariable (&pausable); + + Cvar_RegisterVariable (&sv_maxrate); + + Cmd_AddCommand ("addip", SV_AddIP_f); + Cmd_AddCommand ("removeip", SV_RemoveIP_f); + Cmd_AddCommand ("listip", SV_ListIP_f); + Cmd_AddCommand ("writeip", SV_WriteIP_f); + Cmd_AddCommand ("vip_addip", SV_AddIPVIP_f); + Cmd_AddCommand ("vip_removeip", SV_RemoveIPVIP_f); + Cmd_AddCommand ("vip_listip", SV_ListIPVIP_f); + Cmd_AddCommand ("vip_writeip", SV_WriteIPVIP_f); + Cmd_AddCommand ("record", SV_Record_f); + Cmd_AddCommand ("easyrecord", SV_EasyRecord_f); + Cmd_AddCommand ("stop", SV_Stop_f); + Cmd_AddCommand ("cancel", SV_Cancel_f); + Cmd_AddCommand ("demolist", SV_DemoList_f); + Cmd_AddCommand ("rmdemo", SV_DemoRemove_f); + Cmd_AddCommand ("rmdemonum", SV_DemoRemoveNum_f); + Cmd_AddCommand ("script", SV_Script_f); + + for (i=0 ; i (b)) ? (a) : (b)) +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif + + +/* +================= +SV_ExtractFromUserinfo + +Pull specific info from a newly changed userinfo string +into a more C freindly form. +================= +*/ + +extern func_t UserInfo_Changed; + +void SV_ExtractFromUserinfo (client_t *cl, qboolean namechanged) +{ + char *val, *p, *q; + int i; + client_t *client; + int dupc = 1; + char newname[80]; + extern cvar_t sv_maxrate; + + if (namechanged) + { + // name for C code + val = Info_ValueForKey (cl->userinfo, "name"); + + // trim user name + Q_strncpyz (newname, val, sizeof(newname)); + + for (p = newname; (*p == ' ' || *p == '\r' || *p == '\n') && *p; p++) + ; + + if (p != newname && !*p) { + //white space only + strcpy(newname, "unnamed"); + p = newname; + } + + if (p != newname && *p) { + for (q = newname; *p; *q++ = *p++) + ; + *q = 0; + } + for (p = newname + strlen(newname) - 1; p != newname && (*p == ' ' || *p == '\r' || *p == '\n') ; p--) + ; + p[1] = 0; + + if (strcmp(val, newname)) { + Info_SetValueForKey (cl->userinfo, "name", newname, MAX_INFO_STRING); + val = Info_ValueForKey (cl->userinfo, "name"); + } + + if (!val[0] || !stricmp(val, "console")) { + Info_SetValueForKey (cl->userinfo, "name", "unnamed", MAX_INFO_STRING); + val = Info_ValueForKey (cl->userinfo, "name"); + } + + // check to see if another user by the same name exists + while (1) { + for (i=0, client = svs.clients ; istate != cs_spawned || client == cl) + continue; + if (!stricmp(client->name, val)) + break; + } + if (i != MAX_CLIENTS) { // dup name + if (strlen(val) > sizeof(cl->name) - 1) + val[sizeof(cl->name) - 4] = 0; + p = val; + + if (val[0] == '(') + if (val[2] == ')') + p = val + 3; + else if (val[3] == ')') + p = val + 4; + + sprintf(newname, "(%d)%-.40s", dupc++, p); + Info_SetValueForKey (cl->userinfo, "name", newname, MAX_INFO_STRING); + val = Info_ValueForKey (cl->userinfo, "name"); + } else + break; + } + + if (strncmp(val, cl->name, strlen(cl->name))) { + if (!sv.paused) { + if (!cl->lastnametime || realtime - cl->lastnametime > 5) { + cl->lastnamecount = 0; + cl->lastnametime = realtime; + } else if (cl->lastnamecount++ > 4) { + SV_BroadcastPrintf (PRINT_HIGH, "%s was kicked for name spam\n", cl->name); + SV_ClientPrintf (cl, PRINT_HIGH, "You were kicked from the game for name spamming\n"); + SV_DropClient (cl); + return; + } + } + + if (cl->state >= cs_spawned && !cl->spectator) + SV_BroadcastPrintf (PRINT_HIGH, "%s changed name to %s\n", cl->name, val); + } + + Q_strncpyz (cl->name, val, sizeof(cl->name)); + } + + // team + Q_strncpyz (cl->team, Info_ValueForKey (cl->userinfo, "team"), sizeof(cl->team)); + + // rate + val = Info_ValueForKey (cl->userinfo, "rate"); + cl->netchan.rate = 1.0 / SV_BoundRate (atoi(val)); + + // message level + val = Info_ValueForKey (cl->userinfo, "msg"); + if (strlen(val)) + { + cl->messagelevel = atoi(val); + } +} + + +//============================================================================ + +/* +==================== +SV_InitNet +==================== +*/ +void SV_InitNet (void) +{ + int port; + int p; + + port = PORT_SERVER; + p = COM_CheckParm ("-port"); + if (p && p < com_argc) + { + port = atoi(com_argv[p+1]); + Con_Printf ("Port: %i\n", port); + } + NET_Init (0, port); + + Netchan_Init (); + + // heartbeats will always be sent to the id master + svs.last_heartbeat = -99999; // send immediately +// NET_StringToAdr ("192.246.40.70:27000", &idmaster_adr); +} + + +/* +==================== +SV_Init +==================== +*/ +void SV_Init (quakeparms_t *parms) +{ + COM_InitArgv (parms->argc, parms->argv); + + if (COM_CheckParm ("-minmemory")) + parms->memsize = MINIMUM_MEMORY; + + host_parms = *parms; + + if (parms->memsize < MINIMUM_MEMORY) + SV_Error ("Only %4.1f megs of memory reported, can't execute game", parms->memsize / (float)0x100000); + + Memory_Init (parms->membase, parms->memsize); + Cbuf_Init (); + Cmd_Init (); + + COM_Init (); + + PR_Init (); + Mod_Init (); + + SV_InitNet (); + + SV_InitLocal (); + Sys_Init (); + Pmove_Init (); + + Demo_Init (); + Login_Init (); + + Hunk_AllocName (0, "-HOST_HUNKLEVEL-"); + host_hunklevel = Hunk_LowMark (); + + Cbuf_InsertText ("exec server.cfg\n"); + + host_initialized = true; + + Con_Printf ("Exe: "__TIME__" "__DATE__"\n"); + Con_Printf ("%4.1f megabyte heap\n",parms->memsize/ (1024*1024.0)); + +#ifdef RELEASE_VERSION + Con_Printf ("\nServer Version %s\n\n", QWE_VERSION); +#else + Con_Printf ("\nServer Version %s (Build %04d)\n\n", QWE_VERSION, build_number()); +#endif + + Con_Printf ("QWExtended Project home page: http://qwex.n3.net/\n\n"); + + Con_Printf ("======== QuakeWorld Initialized ========\n"); + +// process command line arguments + Cmd_StuffCmds_f (); + Cbuf_Execute (); + + SV_Map(true); + +// if a map wasn't specified on the command line, spawn start map + if (sv.state == ss_dead) { + Cmd_ExecuteString ("map start"); + SV_Map(true); + } + + if (sv.state == ss_dead) + SV_Error ("Couldn't spawn a server"); +} diff --git a/source/sv_math.s b/source/sv_math.s index ef2f45b3..6b658a72 100644 --- a/source/sv_math.s +++ b/source/sv_math.s @@ -1,331 +1,331 @@ -// -// math.s -// x86 assembly-language math routines. - -#include "asm_i386.h" -#include "quakeasm.h" - - -#if id386 - - .data - - .align 4 -Ljmptab: .long Lcase0, Lcase1, Lcase2, Lcase3 - .long Lcase4, Lcase5, Lcase6, Lcase7 - - .text - - -#define EMINS 4+4 -#define EMAXS 4+8 -#define P 4+12 - - .align 2 -.globl C(BoxOnPlaneSide) -C(BoxOnPlaneSide): - pushl %ebx - - movl P(%esp),%edx - movl EMINS(%esp),%ecx - xorl %eax,%eax - movl EMAXS(%esp),%ebx - movb pl_signbits(%edx),%al - cmpl $8,%al - jge Lerror - flds pl_normal(%edx) // p->normal[0] - fld %st(0) // p->normal[0] | p->normal[0] - jmp *Ljmptab(,%eax,4) - - -//dist1= p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; -//dist2= p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; -Lcase0: - fmuls (%ebx) // p->normal[0]*emaxs[0] | p->normal[0] - flds pl_normal+4(%edx) // p->normal[1] | p->normal[0]*emaxs[0] | - // p->normal[0] - fxch %st(2) // p->normal[0] | p->normal[0]*emaxs[0] | - // p->normal[1] - fmuls (%ecx) // p->normal[0]*emins[0] | - // p->normal[0]*emaxs[0] | p->normal[1] - fxch %st(2) // p->normal[1] | p->normal[0]*emaxs[0] | - // p->normal[0]*emins[0] - fld %st(0) // p->normal[1] | p->normal[1] | - // p->normal[0]*emaxs[0] | - // p->normal[0]*emins[0] - fmuls 4(%ebx) // p->normal[1]*emaxs[1] | p->normal[1] | - // p->normal[0]*emaxs[0] | - // p->normal[0]*emins[0] - flds pl_normal+8(%edx) // p->normal[2] | p->normal[1]*emaxs[1] | - // p->normal[1] | p->normal[0]*emaxs[0] | - // p->normal[0]*emins[0] - fxch %st(2) // p->normal[1] | p->normal[1]*emaxs[1] | - // p->normal[2] | p->normal[0]*emaxs[0] | - // p->normal[0]*emins[0] - fmuls 4(%ecx) // p->normal[1]*emins[1] | - // p->normal[1]*emaxs[1] | - // p->normal[2] | p->normal[0]*emaxs[0] | - // p->normal[0]*emins[0] - fxch %st(2) // p->normal[2] | p->normal[1]*emaxs[1] | - // p->normal[1]*emins[1] | - // p->normal[0]*emaxs[0] | - // p->normal[0]*emins[0] - fld %st(0) // p->normal[2] | p->normal[2] | - // p->normal[1]*emaxs[1] | - // p->normal[1]*emins[1] | - // p->normal[0]*emaxs[0] | - // p->normal[0]*emins[0] - fmuls 8(%ebx) // p->normal[2]*emaxs[2] | - // p->normal[2] | - // p->normal[1]*emaxs[1] | - // p->normal[1]*emins[1] | - // p->normal[0]*emaxs[0] | - // p->normal[0]*emins[0] - fxch %st(5) // p->normal[0]*emins[0] | - // p->normal[2] | - // p->normal[1]*emaxs[1] | - // p->normal[1]*emins[1] | - // p->normal[0]*emaxs[0] | - // p->normal[2]*emaxs[2] - faddp %st(0),%st(3) //p->normal[2] | - // p->normal[1]*emaxs[1] | - // p->normal[1]*emins[1]+p->normal[0]*emins[0]| - // p->normal[0]*emaxs[0] | - // p->normal[2]*emaxs[2] - fmuls 8(%ecx) //p->normal[2]*emins[2] | - // p->normal[1]*emaxs[1] | - // p->normal[1]*emins[1]+p->normal[0]*emins[0]| - // p->normal[0]*emaxs[0] | - // p->normal[2]*emaxs[2] - fxch %st(1) //p->normal[1]*emaxs[1] | - // p->normal[2]*emins[2] | - // p->normal[1]*emins[1]+p->normal[0]*emins[0]| - // p->normal[0]*emaxs[0] | - // p->normal[2]*emaxs[2] - faddp %st(0),%st(3) //p->normal[2]*emins[2] | - // p->normal[1]*emins[1]+p->normal[0]*emins[0]| - // p->normal[0]*emaxs[0]+p->normal[1]*emaxs[1]| - // p->normal[2]*emaxs[2] - fxch %st(3) //p->normal[2]*emaxs[2] + - // p->normal[1]*emins[1]+p->normal[0]*emins[0]| - // p->normal[0]*emaxs[0]+p->normal[1]*emaxs[1]| - // p->normal[2]*emins[2] - faddp %st(0),%st(2) //p->normal[1]*emins[1]+p->normal[0]*emins[0]| - // dist1 | p->normal[2]*emins[2] - - jmp LSetSides - -//dist1= p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; -//dist2= p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; -Lcase1: - fmuls (%ecx) // emins[0] - flds pl_normal+4(%edx) - fxch %st(2) - fmuls (%ebx) // emaxs[0] - fxch %st(2) - fld %st(0) - fmuls 4(%ebx) // emaxs[1] - flds pl_normal+8(%edx) - fxch %st(2) - fmuls 4(%ecx) // emins[1] - fxch %st(2) - fld %st(0) - fmuls 8(%ebx) // emaxs[2] - fxch %st(5) - faddp %st(0),%st(3) - fmuls 8(%ecx) // emins[2] - fxch %st(1) - faddp %st(0),%st(3) - fxch %st(3) - faddp %st(0),%st(2) - - jmp LSetSides - -//dist1= p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; -//dist2= p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; -Lcase2: - fmuls (%ebx) // emaxs[0] - flds pl_normal+4(%edx) - fxch %st(2) - fmuls (%ecx) // emins[0] - fxch %st(2) - fld %st(0) - fmuls 4(%ecx) // emins[1] - flds pl_normal+8(%edx) - fxch %st(2) - fmuls 4(%ebx) // emaxs[1] - fxch %st(2) - fld %st(0) - fmuls 8(%ebx) // emaxs[2] - fxch %st(5) - faddp %st(0),%st(3) - fmuls 8(%ecx) // emins[2] - fxch %st(1) - faddp %st(0),%st(3) - fxch %st(3) - faddp %st(0),%st(2) - - jmp LSetSides - -//dist1= p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; -//dist2= p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; -Lcase3: - fmuls (%ecx) // emins[0] - flds pl_normal+4(%edx) - fxch %st(2) - fmuls (%ebx) // emaxs[0] - fxch %st(2) - fld %st(0) - fmuls 4(%ecx) // emins[1] - flds pl_normal+8(%edx) - fxch %st(2) - fmuls 4(%ebx) // emaxs[1] - fxch %st(2) - fld %st(0) - fmuls 8(%ebx) // emaxs[2] - fxch %st(5) - faddp %st(0),%st(3) - fmuls 8(%ecx) // emins[2] - fxch %st(1) - faddp %st(0),%st(3) - fxch %st(3) - faddp %st(0),%st(2) - - jmp LSetSides - -//dist1= p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; -//dist2= p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; -Lcase4: - fmuls (%ebx) // emaxs[0] - flds pl_normal+4(%edx) - fxch %st(2) - fmuls (%ecx) // emins[0] - fxch %st(2) - fld %st(0) - fmuls 4(%ebx) // emaxs[1] - flds pl_normal+8(%edx) - fxch %st(2) - fmuls 4(%ecx) // emins[1] - fxch %st(2) - fld %st(0) - fmuls 8(%ecx) // emins[2] - fxch %st(5) - faddp %st(0),%st(3) - fmuls 8(%ebx) // emaxs[2] - fxch %st(1) - faddp %st(0),%st(3) - fxch %st(3) - faddp %st(0),%st(2) - - jmp LSetSides - -//dist1= p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; -//dist2= p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; -Lcase5: - fmuls (%ecx) // emins[0] - flds pl_normal+4(%edx) - fxch %st(2) - fmuls (%ebx) // emaxs[0] - fxch %st(2) - fld %st(0) - fmuls 4(%ebx) // emaxs[1] - flds pl_normal+8(%edx) - fxch %st(2) - fmuls 4(%ecx) // emins[1] - fxch %st(2) - fld %st(0) - fmuls 8(%ecx) // emins[2] - fxch %st(5) - faddp %st(0),%st(3) - fmuls 8(%ebx) // emaxs[2] - fxch %st(1) - faddp %st(0),%st(3) - fxch %st(3) - faddp %st(0),%st(2) - - jmp LSetSides - -//dist1= p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; -//dist2= p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; -Lcase6: - fmuls (%ebx) // emaxs[0] - flds pl_normal+4(%edx) - fxch %st(2) - fmuls (%ecx) // emins[0] - fxch %st(2) - fld %st(0) - fmuls 4(%ecx) // emins[1] - flds pl_normal+8(%edx) - fxch %st(2) - fmuls 4(%ebx) // emaxs[1] - fxch %st(2) - fld %st(0) - fmuls 8(%ecx) // emins[2] - fxch %st(5) - faddp %st(0),%st(3) - fmuls 8(%ebx) // emaxs[2] - fxch %st(1) - faddp %st(0),%st(3) - fxch %st(3) - faddp %st(0),%st(2) - - jmp LSetSides - -//dist1= p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; -//dist2= p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; -Lcase7: - fmuls (%ecx) // emins[0] - flds pl_normal+4(%edx) - fxch %st(2) - fmuls (%ebx) // emaxs[0] - fxch %st(2) - fld %st(0) - fmuls 4(%ecx) // emins[1] - flds pl_normal+8(%edx) - fxch %st(2) - fmuls 4(%ebx) // emaxs[1] - fxch %st(2) - fld %st(0) - fmuls 8(%ecx) // emins[2] - fxch %st(5) - faddp %st(0),%st(3) - fmuls 8(%ebx) // emaxs[2] - fxch %st(1) - faddp %st(0),%st(3) - fxch %st(3) - faddp %st(0),%st(2) - -LSetSides: - -// sides = 0; -// if (dist1 >= p->dist) -// sides = 1; -// if (dist2 < p->dist) -// sides |= 2; - - faddp %st(0),%st(2) // dist1 | dist2 - fcomps pl_dist(%edx) - xorl %ecx,%ecx - fnstsw %ax - fcomps pl_dist(%edx) - andb $1,%ah - xorb $1,%ah - addb %ah,%cl - - fnstsw %ax - andb $1,%ah - addb %ah,%ah - addb %ah,%cl - -// return sides; - - popl %ebx - movl %ecx,%eax // return status - - ret - - -Lerror: - call C(BOPS_Error) - -#endif // id386 +// +// math.s +// x86 assembly-language math routines. + +#include "asm_i386.h" +#include "quakeasm.h" + + +#if id386 + + .data + + .align 4 +Ljmptab: .long Lcase0, Lcase1, Lcase2, Lcase3 + .long Lcase4, Lcase5, Lcase6, Lcase7 + + .text + + +#define EMINS 4+4 +#define EMAXS 4+8 +#define P 4+12 + + .align 2 +.globl C(BoxOnPlaneSide) +C(BoxOnPlaneSide): + pushl %ebx + + movl P(%esp),%edx + movl EMINS(%esp),%ecx + xorl %eax,%eax + movl EMAXS(%esp),%ebx + movb pl_signbits(%edx),%al + cmpl $8,%al + jge Lerror + flds pl_normal(%edx) // p->normal[0] + fld %st(0) // p->normal[0] | p->normal[0] + jmp *Ljmptab(,%eax,4) + + +//dist1= p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; +//dist2= p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; +Lcase0: + fmuls (%ebx) // p->normal[0]*emaxs[0] | p->normal[0] + flds pl_normal+4(%edx) // p->normal[1] | p->normal[0]*emaxs[0] | + // p->normal[0] + fxch %st(2) // p->normal[0] | p->normal[0]*emaxs[0] | + // p->normal[1] + fmuls (%ecx) // p->normal[0]*emins[0] | + // p->normal[0]*emaxs[0] | p->normal[1] + fxch %st(2) // p->normal[1] | p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fld %st(0) // p->normal[1] | p->normal[1] | + // p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fmuls 4(%ebx) // p->normal[1]*emaxs[1] | p->normal[1] | + // p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + flds pl_normal+8(%edx) // p->normal[2] | p->normal[1]*emaxs[1] | + // p->normal[1] | p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fxch %st(2) // p->normal[1] | p->normal[1]*emaxs[1] | + // p->normal[2] | p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fmuls 4(%ecx) // p->normal[1]*emins[1] | + // p->normal[1]*emaxs[1] | + // p->normal[2] | p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fxch %st(2) // p->normal[2] | p->normal[1]*emaxs[1] | + // p->normal[1]*emins[1] | + // p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fld %st(0) // p->normal[2] | p->normal[2] | + // p->normal[1]*emaxs[1] | + // p->normal[1]*emins[1] | + // p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fmuls 8(%ebx) // p->normal[2]*emaxs[2] | + // p->normal[2] | + // p->normal[1]*emaxs[1] | + // p->normal[1]*emins[1] | + // p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fxch %st(5) // p->normal[0]*emins[0] | + // p->normal[2] | + // p->normal[1]*emaxs[1] | + // p->normal[1]*emins[1] | + // p->normal[0]*emaxs[0] | + // p->normal[2]*emaxs[2] + faddp %st(0),%st(3) //p->normal[2] | + // p->normal[1]*emaxs[1] | + // p->normal[1]*emins[1]+p->normal[0]*emins[0]| + // p->normal[0]*emaxs[0] | + // p->normal[2]*emaxs[2] + fmuls 8(%ecx) //p->normal[2]*emins[2] | + // p->normal[1]*emaxs[1] | + // p->normal[1]*emins[1]+p->normal[0]*emins[0]| + // p->normal[0]*emaxs[0] | + // p->normal[2]*emaxs[2] + fxch %st(1) //p->normal[1]*emaxs[1] | + // p->normal[2]*emins[2] | + // p->normal[1]*emins[1]+p->normal[0]*emins[0]| + // p->normal[0]*emaxs[0] | + // p->normal[2]*emaxs[2] + faddp %st(0),%st(3) //p->normal[2]*emins[2] | + // p->normal[1]*emins[1]+p->normal[0]*emins[0]| + // p->normal[0]*emaxs[0]+p->normal[1]*emaxs[1]| + // p->normal[2]*emaxs[2] + fxch %st(3) //p->normal[2]*emaxs[2] + + // p->normal[1]*emins[1]+p->normal[0]*emins[0]| + // p->normal[0]*emaxs[0]+p->normal[1]*emaxs[1]| + // p->normal[2]*emins[2] + faddp %st(0),%st(2) //p->normal[1]*emins[1]+p->normal[0]*emins[0]| + // dist1 | p->normal[2]*emins[2] + + jmp LSetSides + +//dist1= p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; +//dist2= p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; +Lcase1: + fmuls (%ecx) // emins[0] + flds pl_normal+4(%edx) + fxch %st(2) + fmuls (%ebx) // emaxs[0] + fxch %st(2) + fld %st(0) + fmuls 4(%ebx) // emaxs[1] + flds pl_normal+8(%edx) + fxch %st(2) + fmuls 4(%ecx) // emins[1] + fxch %st(2) + fld %st(0) + fmuls 8(%ebx) // emaxs[2] + fxch %st(5) + faddp %st(0),%st(3) + fmuls 8(%ecx) // emins[2] + fxch %st(1) + faddp %st(0),%st(3) + fxch %st(3) + faddp %st(0),%st(2) + + jmp LSetSides + +//dist1= p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; +//dist2= p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; +Lcase2: + fmuls (%ebx) // emaxs[0] + flds pl_normal+4(%edx) + fxch %st(2) + fmuls (%ecx) // emins[0] + fxch %st(2) + fld %st(0) + fmuls 4(%ecx) // emins[1] + flds pl_normal+8(%edx) + fxch %st(2) + fmuls 4(%ebx) // emaxs[1] + fxch %st(2) + fld %st(0) + fmuls 8(%ebx) // emaxs[2] + fxch %st(5) + faddp %st(0),%st(3) + fmuls 8(%ecx) // emins[2] + fxch %st(1) + faddp %st(0),%st(3) + fxch %st(3) + faddp %st(0),%st(2) + + jmp LSetSides + +//dist1= p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; +//dist2= p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; +Lcase3: + fmuls (%ecx) // emins[0] + flds pl_normal+4(%edx) + fxch %st(2) + fmuls (%ebx) // emaxs[0] + fxch %st(2) + fld %st(0) + fmuls 4(%ecx) // emins[1] + flds pl_normal+8(%edx) + fxch %st(2) + fmuls 4(%ebx) // emaxs[1] + fxch %st(2) + fld %st(0) + fmuls 8(%ebx) // emaxs[2] + fxch %st(5) + faddp %st(0),%st(3) + fmuls 8(%ecx) // emins[2] + fxch %st(1) + faddp %st(0),%st(3) + fxch %st(3) + faddp %st(0),%st(2) + + jmp LSetSides + +//dist1= p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; +//dist2= p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; +Lcase4: + fmuls (%ebx) // emaxs[0] + flds pl_normal+4(%edx) + fxch %st(2) + fmuls (%ecx) // emins[0] + fxch %st(2) + fld %st(0) + fmuls 4(%ebx) // emaxs[1] + flds pl_normal+8(%edx) + fxch %st(2) + fmuls 4(%ecx) // emins[1] + fxch %st(2) + fld %st(0) + fmuls 8(%ecx) // emins[2] + fxch %st(5) + faddp %st(0),%st(3) + fmuls 8(%ebx) // emaxs[2] + fxch %st(1) + faddp %st(0),%st(3) + fxch %st(3) + faddp %st(0),%st(2) + + jmp LSetSides + +//dist1= p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; +//dist2= p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; +Lcase5: + fmuls (%ecx) // emins[0] + flds pl_normal+4(%edx) + fxch %st(2) + fmuls (%ebx) // emaxs[0] + fxch %st(2) + fld %st(0) + fmuls 4(%ebx) // emaxs[1] + flds pl_normal+8(%edx) + fxch %st(2) + fmuls 4(%ecx) // emins[1] + fxch %st(2) + fld %st(0) + fmuls 8(%ecx) // emins[2] + fxch %st(5) + faddp %st(0),%st(3) + fmuls 8(%ebx) // emaxs[2] + fxch %st(1) + faddp %st(0),%st(3) + fxch %st(3) + faddp %st(0),%st(2) + + jmp LSetSides + +//dist1= p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; +//dist2= p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; +Lcase6: + fmuls (%ebx) // emaxs[0] + flds pl_normal+4(%edx) + fxch %st(2) + fmuls (%ecx) // emins[0] + fxch %st(2) + fld %st(0) + fmuls 4(%ecx) // emins[1] + flds pl_normal+8(%edx) + fxch %st(2) + fmuls 4(%ebx) // emaxs[1] + fxch %st(2) + fld %st(0) + fmuls 8(%ecx) // emins[2] + fxch %st(5) + faddp %st(0),%st(3) + fmuls 8(%ebx) // emaxs[2] + fxch %st(1) + faddp %st(0),%st(3) + fxch %st(3) + faddp %st(0),%st(2) + + jmp LSetSides + +//dist1= p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; +//dist2= p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; +Lcase7: + fmuls (%ecx) // emins[0] + flds pl_normal+4(%edx) + fxch %st(2) + fmuls (%ebx) // emaxs[0] + fxch %st(2) + fld %st(0) + fmuls 4(%ecx) // emins[1] + flds pl_normal+8(%edx) + fxch %st(2) + fmuls 4(%ebx) // emaxs[1] + fxch %st(2) + fld %st(0) + fmuls 8(%ecx) // emins[2] + fxch %st(5) + faddp %st(0),%st(3) + fmuls 8(%ebx) // emaxs[2] + fxch %st(1) + faddp %st(0),%st(3) + fxch %st(3) + faddp %st(0),%st(2) + +LSetSides: + +// sides = 0; +// if (dist1 >= p->dist) +// sides = 1; +// if (dist2 < p->dist) +// sides |= 2; + + faddp %st(0),%st(2) // dist1 | dist2 + fcomps pl_dist(%edx) + xorl %ecx,%ecx + fnstsw %ax + fcomps pl_dist(%edx) + andb $1,%ah + xorb $1,%ah + addb %ah,%cl + + fnstsw %ax + andb $1,%ah + addb %ah,%ah + addb %ah,%cl + +// return sides; + + popl %ebx + movl %ecx,%eax // return status + + ret + + +Lerror: + call C(BOPS_Error) + +#endif // id386 diff --git a/source/sv_model.c b/source/sv_model.c index 797cbeb2..0674e402 100644 --- a/source/sv_model.c +++ b/source/sv_model.c @@ -1,1137 +1,1137 @@ -/* -Copyright (C) 1996-1997 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 "qwsvdef.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; - -texture_t r_notexture_mip; - -unsigned *model_checksum; - -/* -=============== -Mod_Init -=============== -*/ -void Mod_Init (void) -{ - memset (mod_novis, 0xff, sizeof(mod_novis)); -} - -/* -=============== -Mod_PointInLeaf -=============== -*/ -mleaf_t *Mod_PointInLeaf (vec3_t p, model_t *model) -{ - mnode_t *node; - float d; - mplane_t *plane; - - if (!model || !model->nodes) - SV_Error ("Mod_PointInLeaf: bad model"); - - node = model->nodes; - while (1) - { - if (node->contents < 0) - 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->numleafs+7)>>3; - out = decompressed; - -#if 0 - memcpy (out, in, row); -#else - 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); -#endif - - return decompressed; -} - -byte *Mod_LeafPVS (mleaf_t *leaf, model_t *model) -{ - if (leaf == model->leafs) - return mod_novis; - return Mod_DecompressVis (leaf->compressed_vis, model); -} - -/* -=================== -Mod_ClearAll -=================== -*/ -void Mod_ClearAll (void) -{ - int i; - model_t *mod; - - for (i=0 , mod=mod_known ; itype != mod_alias) - mod->needload = true; -} - -/* -================== -Mod_FindName - -================== -*/ -model_t *Mod_FindName (char *name) -{ - int i; - model_t *mod; - - if (!name[0]) - SV_Error ("Mod_ForName: NULL name"); - -// -// search the currently loaded models -// - for (i=0 , mod=mod_known ; iname, name) ) - break; - - if (i == mod_numknown) - { - if (mod_numknown == MAX_MOD_KNOWN) - SV_Error ("mod_numknown == MAX_MOD_KNOWN"); - strcpy (mod->name, name); - mod->needload = true; - mod_numknown++; - } - - return mod; -} - - -/* -================== -Mod_LoadModel - -Loads a model into the cache -================== -*/ -model_t *Mod_LoadModel (model_t *mod, qboolean crash) -{ - void *d; - unsigned *buf; - byte stackbuf[1024]; // avoid dirtying the cache heap - - if (!mod->needload) - { - if (mod->type == mod_alias) - { - d = Cache_Check (&mod->cache); - if (d) - return mod; - } - else - return mod; // not cached at all - } - -// -// load the file -// - buf = (unsigned *)COM_LoadStackFile (mod->name, stackbuf, sizeof(stackbuf)); - if (!buf) - { - if (crash) - SV_Error ("Mod_NumForName: %s not found", mod->name); - return NULL; - } - -// -// allocate a new model -// - COM_FileBase (mod->name, loadname); - - loadmodel = mod; - -// -// fill it in -// - -// call the apropriate loader - mod->needload = false; - - Mod_LoadBrushModel (mod, buf); - - return mod; -} - -/* -================== -Mod_ForName - -Loads in a model for the given name -================== -*/ -model_t *Mod_ForName (char *name, qboolean crash) -{ - model_t *mod; - - mod = Mod_FindName (name); - - return Mod_LoadModel (mod, crash); -} - - -/* -=============================================================================== - - BRUSHMODEL LOADING - -=============================================================================== -*/ - -byte *mod_base; - - -/* -================= -Mod_LoadTextures -================= -*/ -void Mod_LoadTextures (lump_t *l) -{ - int i, j, pixels, num, max, altmax; - miptex_t *mt; - texture_t *tx, *tx2; - texture_t *anims[10]; - texture_t *altanims[10]; - dmiptexlump_t *m; - - if (!l->filelen) - { - loadmodel->textures = NULL; - return; - } - m = (dmiptexlump_t *)(mod_base + l->fileofs); - - m->nummiptex = LittleLong (m->nummiptex); - - loadmodel->numtextures = m->nummiptex; - loadmodel->textures = Hunk_AllocName (m->nummiptex * sizeof(*loadmodel->textures) , loadname); - - for (i=0 ; inummiptex ; i++) - { - m->dataofs[i] = LittleLong(m->dataofs[i]); - if (m->dataofs[i] == -1) - continue; - mt = (miptex_t *)((byte *)m + m->dataofs[i]); - mt->width = LittleLong (mt->width); - mt->height = LittleLong (mt->height); - for (j=0 ; joffsets[j] = LittleLong (mt->offsets[j]); - - if ( (mt->width & 15) || (mt->height & 15) ) - SV_Error ("Texture %s is not 16 aligned", mt->name); - pixels = mt->width*mt->height/64*85; - tx = Hunk_AllocName (sizeof(texture_t) +pixels, loadname ); - loadmodel->textures[i] = tx; - - memcpy (tx->name, mt->name, sizeof(tx->name)); - tx->width = mt->width; - tx->height = mt->height; - for (j=0 ; joffsets[j] = mt->offsets[j] + sizeof(texture_t) - sizeof(miptex_t); - // the pixels immediately follow the structures - memcpy ( tx+1, mt+1, pixels); - } - -// -// sequence the animations -// - for (i=0 ; inummiptex ; i++) - { - tx = loadmodel->textures[i]; - if (!tx || tx->name[0] != '+') - continue; - if (tx->anim_next) - continue; // already sequenced - - // find the number of frames in the animation - memset (anims, 0, sizeof(anims)); - memset (altanims, 0, sizeof(altanims)); - - max = tx->name[1]; - altmax = 0; - if (max >= 'a' && max <= 'z') - max -= 'a' - 'A'; - if (max >= '0' && max <= '9') - { - max -= '0'; - altmax = 0; - anims[max] = tx; - max++; - } - else if (max >= 'A' && max <= 'J') - { - altmax = max - 'A'; - max = 0; - altanims[altmax] = tx; - altmax++; - } - else - SV_Error ("Bad animating texture %s", tx->name); - - for (j=i+1 ; jnummiptex ; j++) - { - tx2 = loadmodel->textures[j]; - if (!tx2 || tx2->name[0] != '+') - continue; - if (strcmp (tx2->name+2, tx->name+2)) - continue; - - num = tx2->name[1]; - if (num >= 'a' && num <= 'z') - num -= 'a' - 'A'; - if (num >= '0' && num <= '9') - { - num -= '0'; - anims[num] = tx2; - if (num+1 > max) - max = num + 1; - } - else if (num >= 'A' && num <= 'J') - { - num = num - 'A'; - altanims[num] = tx2; - if (num+1 > altmax) - altmax = num+1; - } - else - SV_Error ("Bad animating texture %s", tx->name); - } - -#define ANIM_CYCLE 2 - // link them all together - for (j=0 ; jname); - tx2->anim_total = max * ANIM_CYCLE; - tx2->anim_min = j * ANIM_CYCLE; - tx2->anim_max = (j+1) * ANIM_CYCLE; - tx2->anim_next = anims[ (j+1)%max ]; - if (altmax) - tx2->alternate_anims = altanims[0]; - } - for (j=0 ; jname); - tx2->anim_total = altmax * ANIM_CYCLE; - tx2->anim_min = j * ANIM_CYCLE; - tx2->anim_max = (j+1) * ANIM_CYCLE; - tx2->anim_next = altanims[ (j+1)%altmax ]; - if (max) - tx2->alternate_anims = anims[0]; - } - } -} - -/* -================= -Mod_LoadLighting -================= -*/ -void Mod_LoadLighting (lump_t *l) -{ - if (!l->filelen) - { - loadmodel->lightdata = NULL; - return; - } - loadmodel->lightdata = Hunk_AllocName ( l->filelen, loadname); - memcpy (loadmodel->lightdata, mod_base + l->fileofs, l->filelen); -} - - -/* -================= -Mod_LoadVisibility -================= -*/ -void Mod_LoadVisibility (lump_t *l) -{ - if (!l->filelen) - { - loadmodel->visdata = NULL; - return; - } - loadmodel->visdata = Hunk_AllocName ( l->filelen, loadname); - memcpy (loadmodel->visdata, mod_base + l->fileofs, l->filelen); -} - - -/* -================= -Mod_LoadEntities -================= -*/ -void Mod_LoadEntities (lump_t *l) -{ - if (!l->filelen) - { - loadmodel->entities = NULL; - return; - } - loadmodel->entities = Hunk_AllocName ( l->filelen, loadname); - memcpy (loadmodel->entities, mod_base + l->fileofs, l->filelen); -} - - -/* -================= -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)) - SV_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); - count = l->filelen / sizeof(*in); - out = Hunk_AllocName ( count*sizeof(*out), loadname); - - 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)) - SV_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); - count = l->filelen / sizeof(*in); - out = Hunk_AllocName ( count*sizeof(*out), loadname); - - 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]); - } - for (j=0 ; jheadnode[j] = LittleLong (in->headnode[j]); - out->visleafs = LittleLong (in->visleafs); - 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)) - SV_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); - count = l->filelen / sizeof(*in); - out = Hunk_AllocName ( (count + 1) * sizeof(*out), loadname); - - 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; - int i, j, count; - int miptex; - float len1, len2; - - in = (void *)(mod_base + l->fileofs); - if (l->filelen % sizeof(*in)) - SV_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); - count = l->filelen / sizeof(*in); - out = Hunk_AllocName ( count*sizeof(*out), loadname); - - loadmodel->texinfo = out; - loadmodel->numtexinfo = count; - - for ( i=0 ; ivecs[0][j] = LittleFloat (in->vecs[0][j]); - len1 = Length (in->vecs[0]); - len2 = Length (in->vecs[1]); -#else - for (j=0 ; j<4 ; j++) { - out->vecs[0][j] = LittleFloat (in->vecs[0][j]); - out->vecs[1][j] = LittleFloat (in->vecs[1][j]); - } - len1 = Length (out->vecs[0]); - len2 = Length (out->vecs[1]); -#endif - if (len1 + len2 < 2 /*0.001*/) - out->mipadjust = 1; - else - out->mipadjust = 1 / floor( (len1+len2)/2 ); - - miptex = LittleLong (in->miptex); - out->flags = LittleLong (in->flags); - - if (!loadmodel->textures) - { - out->texture = &r_notexture_mip; // checkerboard texture - out->flags = 0; - } - else - { - if (miptex >= loadmodel->numtextures) - SV_Error ("miptex >= loadmodel->numtextures"); - out->texture = loadmodel->textures[miptex]; - if (!out->texture) - { - out->texture = &r_notexture_mip; // texture not found - out->flags = 0; - } - } - } -} - -/* -================ -CalcSurfaceExtents - -Fills in s->texturemins[] and s->extents[] -================ -*/ -void CalcSurfaceExtents (msurface_t *s) -{ - float mins[2], maxs[2], val; - int i,j, e; - mvertex_t *v; - 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++) - { - 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 ( !(tex->flags & TEX_SPECIAL) && s->extents[i] > 256) - SV_Error ("Bad surface extents"); - } -} - - -/* -================= -Mod_LoadFaces -================= -*/ -void Mod_LoadFaces (lump_t *l) -{ - dface_t *in; - msurface_t *out; - int i, count, surfnum; - int planenum, side; - - in = (void *)(mod_base + l->fileofs); - if (l->filelen % sizeof(*in)) - SV_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); - count = l->filelen / sizeof(*in); - out = Hunk_AllocName ( count*sizeof(*out), loadname); - - loadmodel->surfaces = out; - loadmodel->numsurfaces = count; - - for ( surfnum=0 ; surfnumfirstedge = LittleLong(in->firstedge); - out->numedges = LittleShort(in->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 - - for (i=0 ; istyles[i] = in->styles[i]; - i = LittleLong(in->lightofs); - if (i == -1) - out->samples = NULL; - else - out->samples = loadmodel->lightdata + i; - - // set the drawing flags flag - - if (!strncmp(out->texinfo->texture->name,"sky",3)) // sky - { - out->flags |= (SURF_DRAWSKY | SURF_DRAWTILED); - continue; - } - - if (!strncmp(out->texinfo->texture->name,"*",1)) // turbulent - { - out->flags |= (SURF_DRAWTURB | SURF_DRAWTILED); - for (i=0 ; i<2 ; i++) - { - out->extents[i] = 16384; - out->texturemins[i] = -8192; - } - continue; - } - } -} - - -/* -================= -Mod_SetParent -================= -*/ -void Mod_SetParent (mnode_t *node, mnode_t *parent) -{ - node->parent = parent; - if (node->contents < 0) - return; - Mod_SetParent (node->children[0], node); - Mod_SetParent (node->children[1], node); -} - -/* -================= -Mod_LoadNodes -================= -*/ -void Mod_LoadNodes (lump_t *l) -{ - int i, j, count, p; - dnode_t *in; - mnode_t *out; - - in = (void *)(mod_base + l->fileofs); - if (l->filelen % sizeof(*in)) - SV_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); - count = l->filelen / sizeof(*in); - out = Hunk_AllocName ( count*sizeof(*out), loadname); - - 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); - - for (j=0 ; j<2 ; j++) - { - p = LittleShort (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, p; - - in = (void *)(mod_base + l->fileofs); - if (l->filelen % sizeof(*in)) - SV_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); - count = l->filelen / sizeof(*in); - out = Hunk_AllocName ( count*sizeof(*out), loadname); - - loadmodel->leafs = out; - loadmodel->numleafs = count; - - for ( i=0 ; iminmaxs[j] = LittleShort (in->mins[j]); - out->minmaxs[3+j] = LittleShort (in->maxs[j]); - } - - p = LittleLong(in->contents); - out->contents = p; - - out->firstmarksurface = loadmodel->marksurfaces + - LittleShort(in->firstmarksurface); - out->nummarksurfaces = LittleShort(in->nummarksurfaces); - - p = LittleLong(in->visofs); - if (p == -1) - out->compressed_vis = NULL; - else - out->compressed_vis = loadmodel->visdata + p; - out->efrags = NULL; - - for (j=0 ; j<4 ; j++) - out->ambient_sound_level[j] = in->ambient_level[j]; - } -} - -/* -================= -Mod_LoadClipnodes -================= -*/ -void Mod_LoadClipnodes (lump_t *l) -{ - dclipnode_t *in, *out; - int i, count; - hull_t *hull; - - in = (void *)(mod_base + l->fileofs); - if (l->filelen % sizeof(*in)) - SV_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); - count = l->filelen / sizeof(*in); - out = Hunk_AllocName ( count*sizeof(*out), loadname); - - loadmodel->clipnodes = out; - loadmodel->numclipnodes = count; - - hull = &loadmodel->hulls[1]; - hull->clipnodes = out; - hull->firstclipnode = 0; - hull->lastclipnode = count-1; - hull->planes = loadmodel->planes; - hull->clip_mins[0] = -16; - hull->clip_mins[1] = -16; - hull->clip_mins[2] = -24; - hull->clip_maxs[0] = 16; - hull->clip_maxs[1] = 16; - hull->clip_maxs[2] = 32; - - hull = &loadmodel->hulls[2]; - hull->clipnodes = out; - hull->firstclipnode = 0; - hull->lastclipnode = count-1; - hull->planes = loadmodel->planes; - hull->clip_mins[0] = -32; - hull->clip_mins[1] = -32; - hull->clip_mins[2] = -24; - hull->clip_maxs[0] = 32; - hull->clip_maxs[1] = 32; - hull->clip_maxs[2] = 64; - - for (i=0 ; iplanenum = LittleLong(in->planenum); - out->children[0] = LittleShort(in->children[0]); - out->children[1] = LittleShort(in->children[1]); - } -} - -/* -================= -Mod_MakeHull0 - -Deplicate the drawing hull structure as a clipping hull -================= -*/ -void Mod_MakeHull0 (void) -{ - mnode_t *in, *child; - dclipnode_t *out; - int i, j, count; - hull_t *hull; - - hull = &loadmodel->hulls[0]; - - in = loadmodel->nodes; - count = loadmodel->numnodes; - out = Hunk_AllocName ( count*sizeof(*out), loadname); - - hull->clipnodes = out; - hull->firstclipnode = 0; - hull->lastclipnode = count-1; - hull->planes = loadmodel->planes; - - for (i=0 ; iplanenum = in->plane - loadmodel->planes; - for (j=0 ; j<2 ; j++) - { - child = in->children[j]; - if (child->contents < 0) - out->children[j] = child->contents; - else - out->children[j] = child - loadmodel->nodes; - } - } -} - -/* -================= -Mod_LoadMarksurfaces -================= -*/ -void Mod_LoadMarksurfaces (lump_t *l) -{ - int i, j, count; - short *in; - msurface_t **out; - - in = (void *)(mod_base + l->fileofs); - if (l->filelen % sizeof(*in)) - SV_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); - count = l->filelen / sizeof(*in); - out = Hunk_AllocName ( count*sizeof(*out), loadname); - - loadmodel->marksurfaces = out; - loadmodel->nummarksurfaces = count; - - for ( i=0 ; i= loadmodel->numsurfaces) - SV_Error ("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)) - SV_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); - count = l->filelen / sizeof(*in); - out = Hunk_AllocName ( count*sizeof(*out), loadname); - - loadmodel->surfedges = out; - loadmodel->numsurfedges = count; - - for ( i=0 ; ifileofs); - if (l->filelen % sizeof(*in)) - SV_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); - count = l->filelen / sizeof(*in); - out = Hunk_AllocName ( count*2*sizeof(*out), loadname); - - 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, j; - dheader_t *header; - dmodel_t *bm; - - loadmodel->type = mod_brush; - - header = (dheader_t *)buffer; - - i = LittleLong (header->version); - if (i != BSPVERSION) - SV_Error ("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 ; ichecksum = 0; - mod->checksum2 = 0; - - // checksum all of the map, except for entities - for (i = 0; i < HEADER_LUMPS; i++) { - if (i == LUMP_ENTITIES) - continue; - mod->checksum ^= LittleLong(Com_BlockChecksum(mod_base + header->lumps[i].fileofs, - header->lumps[i].filelen)); - - if (i == LUMP_VISIBILITY || i == LUMP_LEAFS || i == LUMP_NODES) - continue; - mod->checksum2 ^= LittleLong(Com_BlockChecksum(mod_base + header->lumps[i].fileofs, - header->lumps[i].filelen)); - } - - Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]); - Mod_LoadEdges (&header->lumps[LUMP_EDGES]); - Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]); - Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]); - 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_MARKSURFACES]); - Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]); - Mod_LoadLeafs (&header->lumps[LUMP_LEAFS]); - Mod_LoadNodes (&header->lumps[LUMP_NODES]); - Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES]); - Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]); - Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]); - - Mod_MakeHull0 (); - - mod->numframes = 2; // regular and alternate animation - -// -// set up the submodels (FIXME: this is confusing) -// - for (i=0 ; inumsubmodels ; i++) - { - bm = &mod->submodels[i]; - - mod->hulls[0].firstclipnode = bm->headnode[0]; - for (j=1 ; jhulls[j].firstclipnode = bm->headnode[j]; - mod->hulls[j].lastclipnode = mod->numclipnodes-1; - } - - mod->firstmodelsurface = bm->firstface; - mod->nummodelsurfaces = bm->numfaces; - - VectorCopy (bm->maxs, mod->maxs); - VectorCopy (bm->mins, mod->mins); - - mod->numleafs = bm->visleafs; - - if (i < mod->numsubmodels-1) - { // duplicate the basic information - char name[10]; - - sprintf (name, "*%i", i+1); - loadmodel = Mod_FindName (name); - *loadmodel = *mod; - strcpy (loadmodel->name, name); - mod = loadmodel; - } - } -} - +/* +Copyright (C) 1996-1997 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 "qwsvdef.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; + +texture_t r_notexture_mip; + +unsigned *model_checksum; + +/* +=============== +Mod_Init +=============== +*/ +void Mod_Init (void) +{ + memset (mod_novis, 0xff, sizeof(mod_novis)); +} + +/* +=============== +Mod_PointInLeaf +=============== +*/ +mleaf_t *Mod_PointInLeaf (vec3_t p, model_t *model) +{ + mnode_t *node; + float d; + mplane_t *plane; + + if (!model || !model->nodes) + SV_Error ("Mod_PointInLeaf: bad model"); + + node = model->nodes; + while (1) + { + if (node->contents < 0) + 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->numleafs+7)>>3; + out = decompressed; + +#if 0 + memcpy (out, in, row); +#else + 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); +#endif + + return decompressed; +} + +byte *Mod_LeafPVS (mleaf_t *leaf, model_t *model) +{ + if (leaf == model->leafs) + return mod_novis; + return Mod_DecompressVis (leaf->compressed_vis, model); +} + +/* +=================== +Mod_ClearAll +=================== +*/ +void Mod_ClearAll (void) +{ + int i; + model_t *mod; + + for (i=0 , mod=mod_known ; itype != mod_alias) + mod->needload = true; +} + +/* +================== +Mod_FindName + +================== +*/ +model_t *Mod_FindName (char *name) +{ + int i; + model_t *mod; + + if (!name[0]) + SV_Error ("Mod_ForName: NULL name"); + +// +// search the currently loaded models +// + for (i=0 , mod=mod_known ; iname, name) ) + break; + + if (i == mod_numknown) + { + if (mod_numknown == MAX_MOD_KNOWN) + SV_Error ("mod_numknown == MAX_MOD_KNOWN"); + strcpy (mod->name, name); + mod->needload = true; + mod_numknown++; + } + + return mod; +} + + +/* +================== +Mod_LoadModel + +Loads a model into the cache +================== +*/ +model_t *Mod_LoadModel (model_t *mod, qboolean crash) +{ + void *d; + unsigned *buf; + byte stackbuf[1024]; // avoid dirtying the cache heap + + if (!mod->needload) + { + if (mod->type == mod_alias) + { + d = Cache_Check (&mod->cache); + if (d) + return mod; + } + else + return mod; // not cached at all + } + +// +// load the file +// + buf = (unsigned *)COM_LoadStackFile (mod->name, stackbuf, sizeof(stackbuf)); + if (!buf) + { + if (crash) + SV_Error ("Mod_NumForName: %s not found", mod->name); + return NULL; + } + +// +// allocate a new model +// + COM_FileBase (mod->name, loadname); + + loadmodel = mod; + +// +// fill it in +// + +// call the apropriate loader + mod->needload = false; + + Mod_LoadBrushModel (mod, buf); + + return mod; +} + +/* +================== +Mod_ForName + +Loads in a model for the given name +================== +*/ +model_t *Mod_ForName (char *name, qboolean crash) +{ + model_t *mod; + + mod = Mod_FindName (name); + + return Mod_LoadModel (mod, crash); +} + + +/* +=============================================================================== + + BRUSHMODEL LOADING + +=============================================================================== +*/ + +byte *mod_base; + + +/* +================= +Mod_LoadTextures +================= +*/ +void Mod_LoadTextures (lump_t *l) +{ + int i, j, pixels, num, max, altmax; + miptex_t *mt; + texture_t *tx, *tx2; + texture_t *anims[10]; + texture_t *altanims[10]; + dmiptexlump_t *m; + + if (!l->filelen) + { + loadmodel->textures = NULL; + return; + } + m = (dmiptexlump_t *)(mod_base + l->fileofs); + + m->nummiptex = LittleLong (m->nummiptex); + + loadmodel->numtextures = m->nummiptex; + loadmodel->textures = Hunk_AllocName (m->nummiptex * sizeof(*loadmodel->textures) , loadname); + + for (i=0 ; inummiptex ; i++) + { + m->dataofs[i] = LittleLong(m->dataofs[i]); + if (m->dataofs[i] == -1) + continue; + mt = (miptex_t *)((byte *)m + m->dataofs[i]); + mt->width = LittleLong (mt->width); + mt->height = LittleLong (mt->height); + for (j=0 ; joffsets[j] = LittleLong (mt->offsets[j]); + + if ( (mt->width & 15) || (mt->height & 15) ) + SV_Error ("Texture %s is not 16 aligned", mt->name); + pixels = mt->width*mt->height/64*85; + tx = Hunk_AllocName (sizeof(texture_t) +pixels, loadname ); + loadmodel->textures[i] = tx; + + memcpy (tx->name, mt->name, sizeof(tx->name)); + tx->width = mt->width; + tx->height = mt->height; + for (j=0 ; joffsets[j] = mt->offsets[j] + sizeof(texture_t) - sizeof(miptex_t); + // the pixels immediately follow the structures + memcpy ( tx+1, mt+1, pixels); + } + +// +// sequence the animations +// + for (i=0 ; inummiptex ; i++) + { + tx = loadmodel->textures[i]; + if (!tx || tx->name[0] != '+') + continue; + if (tx->anim_next) + continue; // already sequenced + + // find the number of frames in the animation + memset (anims, 0, sizeof(anims)); + memset (altanims, 0, sizeof(altanims)); + + max = tx->name[1]; + altmax = 0; + if (max >= 'a' && max <= 'z') + max -= 'a' - 'A'; + if (max >= '0' && max <= '9') + { + max -= '0'; + altmax = 0; + anims[max] = tx; + max++; + } + else if (max >= 'A' && max <= 'J') + { + altmax = max - 'A'; + max = 0; + altanims[altmax] = tx; + altmax++; + } + else + SV_Error ("Bad animating texture %s", tx->name); + + for (j=i+1 ; jnummiptex ; j++) + { + tx2 = loadmodel->textures[j]; + if (!tx2 || tx2->name[0] != '+') + continue; + if (strcmp (tx2->name+2, tx->name+2)) + continue; + + num = tx2->name[1]; + if (num >= 'a' && num <= 'z') + num -= 'a' - 'A'; + if (num >= '0' && num <= '9') + { + num -= '0'; + anims[num] = tx2; + if (num+1 > max) + max = num + 1; + } + else if (num >= 'A' && num <= 'J') + { + num = num - 'A'; + altanims[num] = tx2; + if (num+1 > altmax) + altmax = num+1; + } + else + SV_Error ("Bad animating texture %s", tx->name); + } + +#define ANIM_CYCLE 2 + // link them all together + for (j=0 ; jname); + tx2->anim_total = max * ANIM_CYCLE; + tx2->anim_min = j * ANIM_CYCLE; + tx2->anim_max = (j+1) * ANIM_CYCLE; + tx2->anim_next = anims[ (j+1)%max ]; + if (altmax) + tx2->alternate_anims = altanims[0]; + } + for (j=0 ; jname); + tx2->anim_total = altmax * ANIM_CYCLE; + tx2->anim_min = j * ANIM_CYCLE; + tx2->anim_max = (j+1) * ANIM_CYCLE; + tx2->anim_next = altanims[ (j+1)%altmax ]; + if (max) + tx2->alternate_anims = anims[0]; + } + } +} + +/* +================= +Mod_LoadLighting +================= +*/ +void Mod_LoadLighting (lump_t *l) +{ + if (!l->filelen) + { + loadmodel->lightdata = NULL; + return; + } + loadmodel->lightdata = Hunk_AllocName ( l->filelen, loadname); + memcpy (loadmodel->lightdata, mod_base + l->fileofs, l->filelen); +} + + +/* +================= +Mod_LoadVisibility +================= +*/ +void Mod_LoadVisibility (lump_t *l) +{ + if (!l->filelen) + { + loadmodel->visdata = NULL; + return; + } + loadmodel->visdata = Hunk_AllocName ( l->filelen, loadname); + memcpy (loadmodel->visdata, mod_base + l->fileofs, l->filelen); +} + + +/* +================= +Mod_LoadEntities +================= +*/ +void Mod_LoadEntities (lump_t *l) +{ + if (!l->filelen) + { + loadmodel->entities = NULL; + return; + } + loadmodel->entities = Hunk_AllocName ( l->filelen, loadname); + memcpy (loadmodel->entities, mod_base + l->fileofs, l->filelen); +} + + +/* +================= +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)) + SV_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + 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)) + SV_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + 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]); + } + for (j=0 ; jheadnode[j] = LittleLong (in->headnode[j]); + out->visleafs = LittleLong (in->visleafs); + 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)) + SV_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( (count + 1) * sizeof(*out), loadname); + + 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; + int i, j, count; + int miptex; + float len1, len2; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + SV_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->texinfo = out; + loadmodel->numtexinfo = count; + + for ( i=0 ; ivecs[0][j] = LittleFloat (in->vecs[0][j]); + len1 = Length (in->vecs[0]); + len2 = Length (in->vecs[1]); +#else + for (j=0 ; j<4 ; j++) { + out->vecs[0][j] = LittleFloat (in->vecs[0][j]); + out->vecs[1][j] = LittleFloat (in->vecs[1][j]); + } + len1 = Length (out->vecs[0]); + len2 = Length (out->vecs[1]); +#endif + if (len1 + len2 < 2 /*0.001*/) + out->mipadjust = 1; + else + out->mipadjust = 1 / floor( (len1+len2)/2 ); + + miptex = LittleLong (in->miptex); + out->flags = LittleLong (in->flags); + + if (!loadmodel->textures) + { + out->texture = &r_notexture_mip; // checkerboard texture + out->flags = 0; + } + else + { + if (miptex >= loadmodel->numtextures) + SV_Error ("miptex >= loadmodel->numtextures"); + out->texture = loadmodel->textures[miptex]; + if (!out->texture) + { + out->texture = &r_notexture_mip; // texture not found + out->flags = 0; + } + } + } +} + +/* +================ +CalcSurfaceExtents + +Fills in s->texturemins[] and s->extents[] +================ +*/ +void CalcSurfaceExtents (msurface_t *s) +{ + float mins[2], maxs[2], val; + int i,j, e; + mvertex_t *v; + 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++) + { + 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 ( !(tex->flags & TEX_SPECIAL) && s->extents[i] > 256) + SV_Error ("Bad surface extents"); + } +} + + +/* +================= +Mod_LoadFaces +================= +*/ +void Mod_LoadFaces (lump_t *l) +{ + dface_t *in; + msurface_t *out; + int i, count, surfnum; + int planenum, side; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + SV_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->surfaces = out; + loadmodel->numsurfaces = count; + + for ( surfnum=0 ; surfnumfirstedge = LittleLong(in->firstedge); + out->numedges = LittleShort(in->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 + + for (i=0 ; istyles[i] = in->styles[i]; + i = LittleLong(in->lightofs); + if (i == -1) + out->samples = NULL; + else + out->samples = loadmodel->lightdata + i; + + // set the drawing flags flag + + if (!strncmp(out->texinfo->texture->name,"sky",3)) // sky + { + out->flags |= (SURF_DRAWSKY | SURF_DRAWTILED); + continue; + } + + if (!strncmp(out->texinfo->texture->name,"*",1)) // turbulent + { + out->flags |= (SURF_DRAWTURB | SURF_DRAWTILED); + for (i=0 ; i<2 ; i++) + { + out->extents[i] = 16384; + out->texturemins[i] = -8192; + } + continue; + } + } +} + + +/* +================= +Mod_SetParent +================= +*/ +void Mod_SetParent (mnode_t *node, mnode_t *parent) +{ + node->parent = parent; + if (node->contents < 0) + return; + Mod_SetParent (node->children[0], node); + Mod_SetParent (node->children[1], node); +} + +/* +================= +Mod_LoadNodes +================= +*/ +void Mod_LoadNodes (lump_t *l) +{ + int i, j, count, p; + dnode_t *in; + mnode_t *out; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + SV_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + 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); + + for (j=0 ; j<2 ; j++) + { + p = LittleShort (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, p; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + SV_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->leafs = out; + loadmodel->numleafs = count; + + for ( i=0 ; iminmaxs[j] = LittleShort (in->mins[j]); + out->minmaxs[3+j] = LittleShort (in->maxs[j]); + } + + p = LittleLong(in->contents); + out->contents = p; + + out->firstmarksurface = loadmodel->marksurfaces + + LittleShort(in->firstmarksurface); + out->nummarksurfaces = LittleShort(in->nummarksurfaces); + + p = LittleLong(in->visofs); + if (p == -1) + out->compressed_vis = NULL; + else + out->compressed_vis = loadmodel->visdata + p; + out->efrags = NULL; + + for (j=0 ; j<4 ; j++) + out->ambient_sound_level[j] = in->ambient_level[j]; + } +} + +/* +================= +Mod_LoadClipnodes +================= +*/ +void Mod_LoadClipnodes (lump_t *l) +{ + dclipnode_t *in, *out; + int i, count; + hull_t *hull; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + SV_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->clipnodes = out; + loadmodel->numclipnodes = count; + + hull = &loadmodel->hulls[1]; + hull->clipnodes = out; + hull->firstclipnode = 0; + hull->lastclipnode = count-1; + hull->planes = loadmodel->planes; + hull->clip_mins[0] = -16; + hull->clip_mins[1] = -16; + hull->clip_mins[2] = -24; + hull->clip_maxs[0] = 16; + hull->clip_maxs[1] = 16; + hull->clip_maxs[2] = 32; + + hull = &loadmodel->hulls[2]; + hull->clipnodes = out; + hull->firstclipnode = 0; + hull->lastclipnode = count-1; + hull->planes = loadmodel->planes; + hull->clip_mins[0] = -32; + hull->clip_mins[1] = -32; + hull->clip_mins[2] = -24; + hull->clip_maxs[0] = 32; + hull->clip_maxs[1] = 32; + hull->clip_maxs[2] = 64; + + for (i=0 ; iplanenum = LittleLong(in->planenum); + out->children[0] = LittleShort(in->children[0]); + out->children[1] = LittleShort(in->children[1]); + } +} + +/* +================= +Mod_MakeHull0 + +Deplicate the drawing hull structure as a clipping hull +================= +*/ +void Mod_MakeHull0 (void) +{ + mnode_t *in, *child; + dclipnode_t *out; + int i, j, count; + hull_t *hull; + + hull = &loadmodel->hulls[0]; + + in = loadmodel->nodes; + count = loadmodel->numnodes; + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + hull->clipnodes = out; + hull->firstclipnode = 0; + hull->lastclipnode = count-1; + hull->planes = loadmodel->planes; + + for (i=0 ; iplanenum = in->plane - loadmodel->planes; + for (j=0 ; j<2 ; j++) + { + child = in->children[j]; + if (child->contents < 0) + out->children[j] = child->contents; + else + out->children[j] = child - loadmodel->nodes; + } + } +} + +/* +================= +Mod_LoadMarksurfaces +================= +*/ +void Mod_LoadMarksurfaces (lump_t *l) +{ + int i, j, count; + short *in; + msurface_t **out; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + SV_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->marksurfaces = out; + loadmodel->nummarksurfaces = count; + + for ( i=0 ; i= loadmodel->numsurfaces) + SV_Error ("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)) + SV_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->surfedges = out; + loadmodel->numsurfedges = count; + + for ( i=0 ; ifileofs); + if (l->filelen % sizeof(*in)) + SV_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*2*sizeof(*out), loadname); + + 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, j; + dheader_t *header; + dmodel_t *bm; + + loadmodel->type = mod_brush; + + header = (dheader_t *)buffer; + + i = LittleLong (header->version); + if (i != BSPVERSION) + SV_Error ("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 ; ichecksum = 0; + mod->checksum2 = 0; + + // checksum all of the map, except for entities + for (i = 0; i < HEADER_LUMPS; i++) { + if (i == LUMP_ENTITIES) + continue; + mod->checksum ^= LittleLong(Com_BlockChecksum(mod_base + header->lumps[i].fileofs, + header->lumps[i].filelen)); + + if (i == LUMP_VISIBILITY || i == LUMP_LEAFS || i == LUMP_NODES) + continue; + mod->checksum2 ^= LittleLong(Com_BlockChecksum(mod_base + header->lumps[i].fileofs, + header->lumps[i].filelen)); + } + + Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]); + Mod_LoadEdges (&header->lumps[LUMP_EDGES]); + Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]); + Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]); + 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_MARKSURFACES]); + Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]); + Mod_LoadLeafs (&header->lumps[LUMP_LEAFS]); + Mod_LoadNodes (&header->lumps[LUMP_NODES]); + Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES]); + Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]); + Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]); + + Mod_MakeHull0 (); + + mod->numframes = 2; // regular and alternate animation + +// +// set up the submodels (FIXME: this is confusing) +// + for (i=0 ; inumsubmodels ; i++) + { + bm = &mod->submodels[i]; + + mod->hulls[0].firstclipnode = bm->headnode[0]; + for (j=1 ; jhulls[j].firstclipnode = bm->headnode[j]; + mod->hulls[j].lastclipnode = mod->numclipnodes-1; + } + + mod->firstmodelsurface = bm->firstface; + mod->nummodelsurfaces = bm->numfaces; + + VectorCopy (bm->maxs, mod->maxs); + VectorCopy (bm->mins, mod->mins); + + mod->numleafs = bm->visleafs; + + if (i < mod->numsubmodels-1) + { // duplicate the basic information + char name[10]; + + sprintf (name, "*%i", i+1); + loadmodel = Mod_FindName (name); + *loadmodel = *mod; + strcpy (loadmodel->name, name); + mod = loadmodel; + } + } +} + diff --git a/source/sv_move.c b/source/sv_move.c index 1fcad7ad..e9324bb2 100644 --- a/source/sv_move.c +++ b/source/sv_move.c @@ -1,419 +1,419 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// sv_move.c -- monster movement - -#include "qwsvdef.h" - -#define STEPSIZE 18 - -/* -============= -SV_CheckBottom - -Returns false if any part of the bottom of the entity is off an edge that -is not a staircase. - -============= -*/ -int c_yes, c_no; - -qboolean SV_CheckBottom (edict_t *ent) -{ - vec3_t mins, maxs, start, stop; - trace_t trace; - int x, y; - float mid, bottom; - - VectorAdd (ent->v.origin, ent->v.mins, mins); - VectorAdd (ent->v.origin, ent->v.maxs, maxs); - -// if all of the points under the corners are solid world, don't bother -// with the tougher checks -// the corners must be within 16 of the midpoint - start[2] = mins[2] - 1; - for (x=0 ; x<=1 ; x++) - for (y=0 ; y<=1 ; y++) - { - start[0] = x ? maxs[0] : mins[0]; - start[1] = y ? maxs[1] : mins[1]; - if (SV_PointContents (start) != CONTENTS_SOLID) - goto realcheck; - } - - c_yes++; - return true; // we got out easy - -realcheck: - c_no++; -// -// check it for real... -// - start[2] = mins[2]; - -// the midpoint must be within 16 of the bottom - start[0] = stop[0] = (mins[0] + maxs[0])*0.5; - start[1] = stop[1] = (mins[1] + maxs[1])*0.5; - stop[2] = start[2] - 2*STEPSIZE; - trace = SV_Move (start, vec3_origin, vec3_origin, stop, true, ent); - - if (trace.fraction == 1.0) - return false; - mid = bottom = trace.endpos[2]; - -// the corners must be within 16 of the midpoint - for (x=0 ; x<=1 ; x++) - for (y=0 ; y<=1 ; y++) - { - start[0] = stop[0] = x ? maxs[0] : mins[0]; - start[1] = stop[1] = y ? maxs[1] : mins[1]; - - trace = SV_Move (start, vec3_origin, vec3_origin, stop, true, ent); - - if (trace.fraction != 1.0 && trace.endpos[2] > bottom) - bottom = trace.endpos[2]; - if (trace.fraction == 1.0 || mid - trace.endpos[2] > STEPSIZE) - return false; - } - - c_yes++; - return true; -} - - -/* -============= -SV_movestep - -Called by monster program code. -The move will be adjusted for slopes and stairs, but if the move isn't -possible, no move is done, false is returned, and -pr_global_struct->trace_normal is set to the normal of the blocking wall -============= -*/ -qboolean SV_movestep (edict_t *ent, vec3_t move, qboolean relink) -{ - float dz; - vec3_t oldorg, neworg, end; - trace_t trace; - int i; - edict_t *enemy; - -// try the move - VectorCopy (ent->v.origin, oldorg); - VectorAdd (ent->v.origin, move, neworg); - -// flying monsters don't step up - if ( (int)ent->v.flags & (FL_SWIM | FL_FLY) ) - { - // try one move with vertical motion, then one without - for (i=0 ; i<2 ; i++) - { - VectorAdd (ent->v.origin, move, neworg); - enemy = PROG_TO_EDICT(ent->v.enemy); - if (i == 0 && enemy != sv.edicts) - { - dz = ent->v.origin[2] - PROG_TO_EDICT(ent->v.enemy)->v.origin[2]; - if (dz > 40) - neworg[2] -= 8; - if (dz < 30) - neworg[2] += 8; - } - trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, neworg, false, ent); - - if (trace.fraction == 1) - { - if ( ((int)ent->v.flags & FL_SWIM) && SV_PointContents(trace.endpos) == CONTENTS_EMPTY ) - return false; // swim monster left water - - VectorCopy (trace.endpos, ent->v.origin); - if (relink) - SV_LinkEdict (ent, true); - return true; - } - - if (enemy == sv.edicts) - break; - } - - return false; - } - -// push down from a step height above the wished position - neworg[2] += STEPSIZE; - VectorCopy (neworg, end); - end[2] -= STEPSIZE*2; - - trace = SV_Move (neworg, ent->v.mins, ent->v.maxs, end, false, ent); - - if (trace.allsolid) - return false; - - if (trace.startsolid) - { - neworg[2] -= STEPSIZE; - trace = SV_Move (neworg, ent->v.mins, ent->v.maxs, end, false, ent); - if (trace.allsolid || trace.startsolid) - return false; - } - if (trace.fraction == 1) - { - // if monster had the ground pulled out, go ahead and fall - if ( (int)ent->v.flags & FL_PARTIALGROUND ) - { - VectorAdd (ent->v.origin, move, ent->v.origin); - if (relink) - SV_LinkEdict (ent, true); - ent->v.flags = (int)ent->v.flags & ~FL_ONGROUND; -// Con_Printf ("fall down\n"); - return true; - } - - return false; // walked off an edge - } - -// check point traces down for dangling corners - VectorCopy (trace.endpos, ent->v.origin); - - if (!SV_CheckBottom (ent)) - { - if ( (int)ent->v.flags & FL_PARTIALGROUND ) - { // entity had floor mostly pulled out from underneath it - // and is trying to correct - if (relink) - SV_LinkEdict (ent, true); - return true; - } - VectorCopy (oldorg, ent->v.origin); - return false; - } - - if ( (int)ent->v.flags & FL_PARTIALGROUND ) - { -// Con_Printf ("back on ground\n"); - ent->v.flags = (int)ent->v.flags & ~FL_PARTIALGROUND; - } - ent->v.groundentity = EDICT_TO_PROG(trace.ent); - -// the move is ok - if (relink) - SV_LinkEdict (ent, true); - return true; -} - - -//============================================================================ - -/* -====================== -SV_StepDirection - -Turns to the movement direction, and walks the current distance if -facing it. - -====================== -*/ -void PF_changeyaw (void); -qboolean SV_StepDirection (edict_t *ent, float yaw, float dist) -{ - vec3_t move, oldorigin; - float delta; - - ent->v.ideal_yaw = yaw; - PF_changeyaw(); - - yaw = yaw*M_PI*2 / 360; - move[0] = cos(yaw)*dist; - move[1] = sin(yaw)*dist; - move[2] = 0; - - VectorCopy (ent->v.origin, oldorigin); - if (SV_movestep (ent, move, false)) - { - delta = ent->v.angles[YAW] - ent->v.ideal_yaw; - if (delta > 45 && delta < 315) - { // not turned far enough, so don't take the step - VectorCopy (oldorigin, ent->v.origin); - } - SV_LinkEdict (ent, true); - return true; - } - SV_LinkEdict (ent, true); - - return false; -} - -/* -====================== -SV_FixCheckBottom - -====================== -*/ -void SV_FixCheckBottom (edict_t *ent) -{ -// Con_Printf ("SV_FixCheckBottom\n"); - - ent->v.flags = (int)ent->v.flags | FL_PARTIALGROUND; -} - - - -/* -================ -SV_NewChaseDir - -================ -*/ -#define DI_NODIR -1 -void SV_NewChaseDir (edict_t *actor, edict_t *enemy, float dist) -{ - float deltax,deltay; - float d[3]; - float tdir, olddir, turnaround; - - olddir = anglemod( (int)(actor->v.ideal_yaw/45)*45 ); - turnaround = anglemod(olddir - 180); - - deltax = enemy->v.origin[0] - actor->v.origin[0]; - deltay = enemy->v.origin[1] - actor->v.origin[1]; - if (deltax>10) - d[1]= 0; - else if (deltax<-10) - d[1]= 180; - else - d[1]= DI_NODIR; - if (deltay<-10) - d[2]= 270; - else if (deltay>10) - d[2]= 90; - else - d[2]= DI_NODIR; - -// try direct route - if (d[1] != DI_NODIR && d[2] != DI_NODIR) - { - if (d[1] == 0) - tdir = d[2] == 90 ? 45 : 315; - else - tdir = d[2] == 90 ? 135 : 215; - - if (tdir != turnaround && SV_StepDirection(actor, tdir, dist)) - return; - } - -// try other directions - if ( ((rand()&3) & 1) || abs(deltay)>abs(deltax)) - { - tdir=d[1]; - d[1]=d[2]; - d[2]=tdir; - } - - if (d[1]!=DI_NODIR && d[1]!=turnaround - && SV_StepDirection(actor, d[1], dist)) - return; - - if (d[2]!=DI_NODIR && d[2]!=turnaround - && SV_StepDirection(actor, d[2], dist)) - return; - -/* there is no direct path to the player, so pick another direction */ - - if (olddir!=DI_NODIR && SV_StepDirection(actor, olddir, dist)) - return; - - if (rand()&1) /*randomly determine direction of search*/ - { - for (tdir=0 ; tdir<=315 ; tdir += 45) - if (tdir!=turnaround && SV_StepDirection(actor, tdir, dist) ) - return; - } - else - { - for (tdir=315 ; tdir >=0 ; tdir -= 45) - if (tdir!=turnaround && SV_StepDirection(actor, tdir, dist) ) - return; - } - - if (turnaround != DI_NODIR && SV_StepDirection(actor, turnaround, dist) ) - return; - - actor->v.ideal_yaw = olddir; // can't move - -// if a bridge was pulled out from underneath a monster, it may not have -// a valid standing position at all - - if (!SV_CheckBottom (actor)) - SV_FixCheckBottom (actor); - -} - -/* -====================== -SV_CloseEnough - -====================== -*/ -qboolean SV_CloseEnough (edict_t *ent, edict_t *goal, float dist) -{ - int i; - - for (i=0 ; i<3 ; i++) - { - if (goal->v.absmin[i] > ent->v.absmax[i] + dist) - return false; - if (goal->v.absmax[i] < ent->v.absmin[i] - dist) - return false; - } - return true; -} - -/* -====================== -SV_MoveToGoal - -====================== -*/ -void SV_MoveToGoal (void) -{ - edict_t *ent, *goal; - float dist; - - ent = PROG_TO_EDICT(pr_global_struct->self); - goal = PROG_TO_EDICT(ent->v.goalentity); - dist = G_FLOAT(OFS_PARM0); - - if ( !( (int)ent->v.flags & (FL_ONGROUND|FL_FLY|FL_SWIM) ) ) - { - G_FLOAT(OFS_RETURN) = 0; - return; - } - -// if the next step hits the enemy, return immediately - if ( PROG_TO_EDICT(ent->v.enemy) != sv.edicts && SV_CloseEnough (ent, goal, dist) ) - return; - -// bump around... - if ( (rand()&3)==1 || - !SV_StepDirection (ent, ent->v.ideal_yaw, dist)) - { - SV_NewChaseDir (ent, goal, dist); - } -} - +/* +Copyright (C) 1996-1997 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. + +*/ +// sv_move.c -- monster movement + +#include "qwsvdef.h" + +#define STEPSIZE 18 + +/* +============= +SV_CheckBottom + +Returns false if any part of the bottom of the entity is off an edge that +is not a staircase. + +============= +*/ +int c_yes, c_no; + +qboolean SV_CheckBottom (edict_t *ent) +{ + vec3_t mins, maxs, start, stop; + trace_t trace; + int x, y; + float mid, bottom; + + VectorAdd (ent->v.origin, ent->v.mins, mins); + VectorAdd (ent->v.origin, ent->v.maxs, maxs); + +// if all of the points under the corners are solid world, don't bother +// with the tougher checks +// the corners must be within 16 of the midpoint + start[2] = mins[2] - 1; + for (x=0 ; x<=1 ; x++) + for (y=0 ; y<=1 ; y++) + { + start[0] = x ? maxs[0] : mins[0]; + start[1] = y ? maxs[1] : mins[1]; + if (SV_PointContents (start) != CONTENTS_SOLID) + goto realcheck; + } + + c_yes++; + return true; // we got out easy + +realcheck: + c_no++; +// +// check it for real... +// + start[2] = mins[2]; + +// the midpoint must be within 16 of the bottom + start[0] = stop[0] = (mins[0] + maxs[0])*0.5; + start[1] = stop[1] = (mins[1] + maxs[1])*0.5; + stop[2] = start[2] - 2*STEPSIZE; + trace = SV_Move (start, vec3_origin, vec3_origin, stop, true, ent); + + if (trace.fraction == 1.0) + return false; + mid = bottom = trace.endpos[2]; + +// the corners must be within 16 of the midpoint + for (x=0 ; x<=1 ; x++) + for (y=0 ; y<=1 ; y++) + { + start[0] = stop[0] = x ? maxs[0] : mins[0]; + start[1] = stop[1] = y ? maxs[1] : mins[1]; + + trace = SV_Move (start, vec3_origin, vec3_origin, stop, true, ent); + + if (trace.fraction != 1.0 && trace.endpos[2] > bottom) + bottom = trace.endpos[2]; + if (trace.fraction == 1.0 || mid - trace.endpos[2] > STEPSIZE) + return false; + } + + c_yes++; + return true; +} + + +/* +============= +SV_movestep + +Called by monster program code. +The move will be adjusted for slopes and stairs, but if the move isn't +possible, no move is done, false is returned, and +pr_global_struct->trace_normal is set to the normal of the blocking wall +============= +*/ +qboolean SV_movestep (edict_t *ent, vec3_t move, qboolean relink) +{ + float dz; + vec3_t oldorg, neworg, end; + trace_t trace; + int i; + edict_t *enemy; + +// try the move + VectorCopy (ent->v.origin, oldorg); + VectorAdd (ent->v.origin, move, neworg); + +// flying monsters don't step up + if ( (int)ent->v.flags & (FL_SWIM | FL_FLY) ) + { + // try one move with vertical motion, then one without + for (i=0 ; i<2 ; i++) + { + VectorAdd (ent->v.origin, move, neworg); + enemy = PROG_TO_EDICT(ent->v.enemy); + if (i == 0 && enemy != sv.edicts) + { + dz = ent->v.origin[2] - PROG_TO_EDICT(ent->v.enemy)->v.origin[2]; + if (dz > 40) + neworg[2] -= 8; + if (dz < 30) + neworg[2] += 8; + } + trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, neworg, false, ent); + + if (trace.fraction == 1) + { + if ( ((int)ent->v.flags & FL_SWIM) && SV_PointContents(trace.endpos) == CONTENTS_EMPTY ) + return false; // swim monster left water + + VectorCopy (trace.endpos, ent->v.origin); + if (relink) + SV_LinkEdict (ent, true); + return true; + } + + if (enemy == sv.edicts) + break; + } + + return false; + } + +// push down from a step height above the wished position + neworg[2] += STEPSIZE; + VectorCopy (neworg, end); + end[2] -= STEPSIZE*2; + + trace = SV_Move (neworg, ent->v.mins, ent->v.maxs, end, false, ent); + + if (trace.allsolid) + return false; + + if (trace.startsolid) + { + neworg[2] -= STEPSIZE; + trace = SV_Move (neworg, ent->v.mins, ent->v.maxs, end, false, ent); + if (trace.allsolid || trace.startsolid) + return false; + } + if (trace.fraction == 1) + { + // if monster had the ground pulled out, go ahead and fall + if ( (int)ent->v.flags & FL_PARTIALGROUND ) + { + VectorAdd (ent->v.origin, move, ent->v.origin); + if (relink) + SV_LinkEdict (ent, true); + ent->v.flags = (int)ent->v.flags & ~FL_ONGROUND; +// Con_Printf ("fall down\n"); + return true; + } + + return false; // walked off an edge + } + +// check point traces down for dangling corners + VectorCopy (trace.endpos, ent->v.origin); + + if (!SV_CheckBottom (ent)) + { + if ( (int)ent->v.flags & FL_PARTIALGROUND ) + { // entity had floor mostly pulled out from underneath it + // and is trying to correct + if (relink) + SV_LinkEdict (ent, true); + return true; + } + VectorCopy (oldorg, ent->v.origin); + return false; + } + + if ( (int)ent->v.flags & FL_PARTIALGROUND ) + { +// Con_Printf ("back on ground\n"); + ent->v.flags = (int)ent->v.flags & ~FL_PARTIALGROUND; + } + ent->v.groundentity = EDICT_TO_PROG(trace.ent); + +// the move is ok + if (relink) + SV_LinkEdict (ent, true); + return true; +} + + +//============================================================================ + +/* +====================== +SV_StepDirection + +Turns to the movement direction, and walks the current distance if +facing it. + +====================== +*/ +void PF_changeyaw (void); +qboolean SV_StepDirection (edict_t *ent, float yaw, float dist) +{ + vec3_t move, oldorigin; + float delta; + + ent->v.ideal_yaw = yaw; + PF_changeyaw(); + + yaw = yaw*M_PI*2 / 360; + move[0] = cos(yaw)*dist; + move[1] = sin(yaw)*dist; + move[2] = 0; + + VectorCopy (ent->v.origin, oldorigin); + if (SV_movestep (ent, move, false)) + { + delta = ent->v.angles[YAW] - ent->v.ideal_yaw; + if (delta > 45 && delta < 315) + { // not turned far enough, so don't take the step + VectorCopy (oldorigin, ent->v.origin); + } + SV_LinkEdict (ent, true); + return true; + } + SV_LinkEdict (ent, true); + + return false; +} + +/* +====================== +SV_FixCheckBottom + +====================== +*/ +void SV_FixCheckBottom (edict_t *ent) +{ +// Con_Printf ("SV_FixCheckBottom\n"); + + ent->v.flags = (int)ent->v.flags | FL_PARTIALGROUND; +} + + + +/* +================ +SV_NewChaseDir + +================ +*/ +#define DI_NODIR -1 +void SV_NewChaseDir (edict_t *actor, edict_t *enemy, float dist) +{ + float deltax,deltay; + float d[3]; + float tdir, olddir, turnaround; + + olddir = anglemod( (int)(actor->v.ideal_yaw/45)*45 ); + turnaround = anglemod(olddir - 180); + + deltax = enemy->v.origin[0] - actor->v.origin[0]; + deltay = enemy->v.origin[1] - actor->v.origin[1]; + if (deltax>10) + d[1]= 0; + else if (deltax<-10) + d[1]= 180; + else + d[1]= DI_NODIR; + if (deltay<-10) + d[2]= 270; + else if (deltay>10) + d[2]= 90; + else + d[2]= DI_NODIR; + +// try direct route + if (d[1] != DI_NODIR && d[2] != DI_NODIR) + { + if (d[1] == 0) + tdir = d[2] == 90 ? 45 : 315; + else + tdir = d[2] == 90 ? 135 : 215; + + if (tdir != turnaround && SV_StepDirection(actor, tdir, dist)) + return; + } + +// try other directions + if ( ((rand()&3) & 1) || abs(deltay)>abs(deltax)) + { + tdir=d[1]; + d[1]=d[2]; + d[2]=tdir; + } + + if (d[1]!=DI_NODIR && d[1]!=turnaround + && SV_StepDirection(actor, d[1], dist)) + return; + + if (d[2]!=DI_NODIR && d[2]!=turnaround + && SV_StepDirection(actor, d[2], dist)) + return; + +/* there is no direct path to the player, so pick another direction */ + + if (olddir!=DI_NODIR && SV_StepDirection(actor, olddir, dist)) + return; + + if (rand()&1) /*randomly determine direction of search*/ + { + for (tdir=0 ; tdir<=315 ; tdir += 45) + if (tdir!=turnaround && SV_StepDirection(actor, tdir, dist) ) + return; + } + else + { + for (tdir=315 ; tdir >=0 ; tdir -= 45) + if (tdir!=turnaround && SV_StepDirection(actor, tdir, dist) ) + return; + } + + if (turnaround != DI_NODIR && SV_StepDirection(actor, turnaround, dist) ) + return; + + actor->v.ideal_yaw = olddir; // can't move + +// if a bridge was pulled out from underneath a monster, it may not have +// a valid standing position at all + + if (!SV_CheckBottom (actor)) + SV_FixCheckBottom (actor); + +} + +/* +====================== +SV_CloseEnough + +====================== +*/ +qboolean SV_CloseEnough (edict_t *ent, edict_t *goal, float dist) +{ + int i; + + for (i=0 ; i<3 ; i++) + { + if (goal->v.absmin[i] > ent->v.absmax[i] + dist) + return false; + if (goal->v.absmax[i] < ent->v.absmin[i] - dist) + return false; + } + return true; +} + +/* +====================== +SV_MoveToGoal + +====================== +*/ +void SV_MoveToGoal (void) +{ + edict_t *ent, *goal; + float dist; + + ent = PROG_TO_EDICT(pr_global_struct->self); + goal = PROG_TO_EDICT(ent->v.goalentity); + dist = G_FLOAT(OFS_PARM0); + + if ( !( (int)ent->v.flags & (FL_ONGROUND|FL_FLY|FL_SWIM) ) ) + { + G_FLOAT(OFS_RETURN) = 0; + return; + } + +// if the next step hits the enemy, return immediately + if ( PROG_TO_EDICT(ent->v.enemy) != sv.edicts && SV_CloseEnough (ent, goal, dist) ) + return; + +// bump around... + if ( (rand()&3)==1 || + !SV_StepDirection (ent, ent->v.ideal_yaw, dist)) + { + SV_NewChaseDir (ent, goal, dist); + } +} + diff --git a/source/sv_nchan.c b/source/sv_nchan.c index 98403748..f15107be 100644 --- a/source/sv_nchan.c +++ b/source/sv_nchan.c @@ -1,165 +1,165 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// sv_nchan.c, user reliable data stream writes - -#include "qwsvdef.h" - -// check to see if client block will fit, if not, rotate buffers -void ClientReliableCheckBlock(client_t *cl, int maxsize) -{ - if (cl->num_backbuf || - cl->netchan.message.cursize > - cl->netchan.message.maxsize - maxsize - 1) { - // we would probably overflow the buffer, save it for next - if (!cl->num_backbuf) { - memset(&cl->backbuf, 0, sizeof(cl->backbuf)); - cl->backbuf.allowoverflow = true; - cl->backbuf.data = cl->backbuf_data[0]; - cl->backbuf.maxsize = sizeof(cl->backbuf_data[0]); - cl->backbuf_size[0] = 0; - cl->num_backbuf++; - } - - if (cl->backbuf.cursize > cl->backbuf.maxsize - maxsize - 1) { - if (cl->num_backbuf == MAX_BACK_BUFFERS) { - Con_Printf ("WARNING: MAX_BACK_BUFFERS for %s\n", cl->name); - cl->backbuf.cursize = 0; // don't overflow without allowoverflow set - cl->netchan.message.overflowed = true; // this will drop the client - return; - } - memset(&cl->backbuf, 0, sizeof(cl->backbuf)); - cl->backbuf.allowoverflow = true; - cl->backbuf.data = cl->backbuf_data[cl->num_backbuf]; - cl->backbuf.maxsize = sizeof(cl->backbuf_data[cl->num_backbuf]); - cl->backbuf_size[cl->num_backbuf] = 0; - cl->num_backbuf++; - } - } -} - -// begin a client block, estimated maximum size -void ClientReliableWrite_Begin(client_t *cl, int c, int maxsize) -{ - ClientReliableCheckBlock(cl, maxsize); - ClientReliableWrite_Byte(cl, c); -} - -void ClientReliable_FinishWrite(client_t *cl) -{ - if (cl->num_backbuf) { - cl->backbuf_size[cl->num_backbuf - 1] = cl->backbuf.cursize; - - if (cl->backbuf.overflowed) { - Con_Printf ("WARNING: backbuf [%d] reliable overflow for %s\n",cl->num_backbuf,cl->name); - cl->netchan.message.overflowed = true; // this will drop the client - } - } -} - -void ClientReliableWrite_Angle(client_t *cl, float f) -{ - if (cl->num_backbuf) { - MSG_WriteAngle(&cl->backbuf, f); - ClientReliable_FinishWrite(cl); - } else - MSG_WriteAngle(&cl->netchan.message, f); -} - -void ClientReliableWrite_Angle16(client_t *cl, float f) -{ - if (cl->num_backbuf) { - MSG_WriteAngle16(&cl->backbuf, f); - ClientReliable_FinishWrite(cl); - } else - MSG_WriteAngle16(&cl->netchan.message, f); -} - -void ClientReliableWrite_Byte(client_t *cl, int c) -{ - if (cl->num_backbuf) { - MSG_WriteByte(&cl->backbuf, c); - ClientReliable_FinishWrite(cl); - } else - MSG_WriteByte(&cl->netchan.message, c); -} - -void ClientReliableWrite_Char(client_t *cl, int c) -{ - if (cl->num_backbuf) { - MSG_WriteChar(&cl->backbuf, c); - ClientReliable_FinishWrite(cl); - } else - MSG_WriteChar(&cl->netchan.message, c); -} - -void ClientReliableWrite_Float(client_t *cl, float f) -{ - if (cl->num_backbuf) { - MSG_WriteFloat(&cl->backbuf, f); - ClientReliable_FinishWrite(cl); - } else - MSG_WriteFloat(&cl->netchan.message, f); -} - -void ClientReliableWrite_Coord(client_t *cl, float f) -{ - if (cl->num_backbuf) { - MSG_WriteCoord(&cl->backbuf, f); - ClientReliable_FinishWrite(cl); - } else - MSG_WriteCoord(&cl->netchan.message, f); -} - -void ClientReliableWrite_Long(client_t *cl, int c) -{ - if (cl->num_backbuf) { - MSG_WriteLong(&cl->backbuf, c); - ClientReliable_FinishWrite(cl); - } else - MSG_WriteLong(&cl->netchan.message, c); -} - -void ClientReliableWrite_Short(client_t *cl, int c) -{ - if (cl->num_backbuf) { - MSG_WriteShort(&cl->backbuf, c); - ClientReliable_FinishWrite(cl); - } else - MSG_WriteShort(&cl->netchan.message, c); -} - -void ClientReliableWrite_String(client_t *cl, char *s) -{ - if (cl->num_backbuf) { - MSG_WriteString(&cl->backbuf, s); - ClientReliable_FinishWrite(cl); - } else - MSG_WriteString(&cl->netchan.message, s); -} - -void ClientReliableWrite_SZ(client_t *cl, void *data, int len) -{ - if (cl->num_backbuf) { - SZ_Write(&cl->backbuf, data, len); - ClientReliable_FinishWrite(cl); - } else - SZ_Write(&cl->netchan.message, data, len); -} - +/* +Copyright (C) 1996-1997 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. + +*/ +// sv_nchan.c, user reliable data stream writes + +#include "qwsvdef.h" + +// check to see if client block will fit, if not, rotate buffers +void ClientReliableCheckBlock(client_t *cl, int maxsize) +{ + if (cl->num_backbuf || + cl->netchan.message.cursize > + cl->netchan.message.maxsize - maxsize - 1) { + // we would probably overflow the buffer, save it for next + if (!cl->num_backbuf) { + memset(&cl->backbuf, 0, sizeof(cl->backbuf)); + cl->backbuf.allowoverflow = true; + cl->backbuf.data = cl->backbuf_data[0]; + cl->backbuf.maxsize = sizeof(cl->backbuf_data[0]); + cl->backbuf_size[0] = 0; + cl->num_backbuf++; + } + + if (cl->backbuf.cursize > cl->backbuf.maxsize - maxsize - 1) { + if (cl->num_backbuf == MAX_BACK_BUFFERS) { + Sys_Printf ("WARNING: MAX_BACK_BUFFERS for %s\n", cl->name); + cl->backbuf.cursize = 0; // don't overflow without allowoverflow set + cl->netchan.message.overflowed = true; // this will drop the client + return; + } + memset(&cl->backbuf, 0, sizeof(cl->backbuf)); + cl->backbuf.allowoverflow = true; + cl->backbuf.data = cl->backbuf_data[cl->num_backbuf]; + cl->backbuf.maxsize = sizeof(cl->backbuf_data[cl->num_backbuf]); + cl->backbuf_size[cl->num_backbuf] = 0; + cl->num_backbuf++; + } + } +} + +// begin a client block, estimated maximum size +void ClientReliableWrite_Begin(client_t *cl, int c, int maxsize) +{ + ClientReliableCheckBlock(cl, maxsize); + ClientReliableWrite_Byte(cl, c); +} + +void ClientReliable_FinishWrite(client_t *cl) +{ + if (cl->num_backbuf) { + cl->backbuf_size[cl->num_backbuf - 1] = cl->backbuf.cursize; + + if (cl->backbuf.overflowed) { + Sys_Printf ("WARNING: backbuf [%d] reliable overflow for %s\n",cl->num_backbuf,cl->name); + cl->netchan.message.overflowed = true; // this will drop the client + } + } +} + +void ClientReliableWrite_Angle(client_t *cl, float f) +{ + if (cl->num_backbuf) { + MSG_WriteAngle(&cl->backbuf, f); + ClientReliable_FinishWrite(cl); + } else + MSG_WriteAngle(&cl->netchan.message, f); +} + +void ClientReliableWrite_Angle16(client_t *cl, float f) +{ + if (cl->num_backbuf) { + MSG_WriteAngle16(&cl->backbuf, f); + ClientReliable_FinishWrite(cl); + } else + MSG_WriteAngle16(&cl->netchan.message, f); +} + +void ClientReliableWrite_Byte(client_t *cl, int c) +{ + if (cl->num_backbuf) { + MSG_WriteByte(&cl->backbuf, c); + ClientReliable_FinishWrite(cl); + } else + MSG_WriteByte(&cl->netchan.message, c); +} + +void ClientReliableWrite_Char(client_t *cl, int c) +{ + if (cl->num_backbuf) { + MSG_WriteChar(&cl->backbuf, c); + ClientReliable_FinishWrite(cl); + } else + MSG_WriteChar(&cl->netchan.message, c); +} + +void ClientReliableWrite_Float(client_t *cl, float f) +{ + if (cl->num_backbuf) { + MSG_WriteFloat(&cl->backbuf, f); + ClientReliable_FinishWrite(cl); + } else + MSG_WriteFloat(&cl->netchan.message, f); +} + +void ClientReliableWrite_Coord(client_t *cl, float f) +{ + if (cl->num_backbuf) { + MSG_WriteCoord(&cl->backbuf, f); + ClientReliable_FinishWrite(cl); + } else + MSG_WriteCoord(&cl->netchan.message, f); +} + +void ClientReliableWrite_Long(client_t *cl, int c) +{ + if (cl->num_backbuf) { + MSG_WriteLong(&cl->backbuf, c); + ClientReliable_FinishWrite(cl); + } else + MSG_WriteLong(&cl->netchan.message, c); +} + +void ClientReliableWrite_Short(client_t *cl, int c) +{ + if (cl->num_backbuf) { + MSG_WriteShort(&cl->backbuf, c); + ClientReliable_FinishWrite(cl); + } else + MSG_WriteShort(&cl->netchan.message, c); +} + +void ClientReliableWrite_String(client_t *cl, char *s) +{ + if (cl->num_backbuf) { + MSG_WriteString(&cl->backbuf, s); + ClientReliable_FinishWrite(cl); + } else + MSG_WriteString(&cl->netchan.message, s); +} + +void ClientReliableWrite_SZ(client_t *cl, void *data, int len) +{ + if (cl->num_backbuf) { + SZ_Write(&cl->backbuf, data, len); + ClientReliable_FinishWrite(cl); + } else + SZ_Write(&cl->netchan.message, data, len); +} + diff --git a/source/sv_phys.c b/source/sv_phys.c index 0ac6f928..a6b7c693 100644 --- a/source/sv_phys.c +++ b/source/sv_phys.c @@ -1,957 +1,976 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// sv_phys.c - -#include "qwsvdef.h" - -/* - - -pushmove objects do not obey gravity, and do not interact with each other or trigger fields, but block normal movement and push normal objects when they move. - -onground is set for toss objects when they come to a complete rest. it is set for steping or walking objects - -doors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH -bonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS -corpses are SOLID_NOT and MOVETYPE_TOSS -crates are SOLID_BBOX and MOVETYPE_TOSS -walking monsters are SOLID_SLIDEBOX and MOVETYPE_STEP -flying/floating monsters are SOLID_SLIDEBOX and MOVETYPE_FLY - -solid_edge items only clip against bsp models. - -*/ - -cvar_t sv_maxvelocity = {"sv_maxvelocity","2000"}; - -cvar_t sv_gravity = { "sv_gravity", "800"}; -cvar_t sv_stopspeed = { "sv_stopspeed", "100"}; -cvar_t sv_maxspeed = { "sv_maxspeed", "320"}; -cvar_t sv_spectatormaxspeed = { "sv_spectatormaxspeed", "500"}; -cvar_t sv_accelerate = { "sv_accelerate", "10"}; -cvar_t sv_airaccelerate = { "sv_airaccelerate", "10"}; -cvar_t sv_wateraccelerate = { "sv_wateraccelerate", "10"}; -cvar_t sv_friction = { "sv_friction", "4"}; -cvar_t sv_waterfriction = { "sv_waterfriction", "4"}; - - -#define MOVE_EPSILON 0.01 - -void SV_Physics_Toss (edict_t *ent); - -/* -================ -SV_CheckAllEnts -================ -*/ -void SV_CheckAllEnts (void) -{ - int e; - edict_t *check; - -// see if any solid entities are inside the final position - check = NEXT_EDICT(sv.edicts); - for (e=1 ; efree) - continue; - if (check->v.movetype == MOVETYPE_PUSH - || check->v.movetype == MOVETYPE_NONE - || check->v.movetype == MOVETYPE_NOCLIP) - continue; - - if (SV_TestEntityPosition (check)) - Con_Printf ("entity in invalid position\n"); - } -} - -/* -================ -SV_CheckVelocity -================ -*/ -void SV_CheckVelocity (edict_t *ent) -{ - int i; - float wishspeed; - -// -// bound velocity -// - for (i=0 ; i<3 ; i++) - { - if (IS_NAN(ent->v.velocity[i])) - { - Con_DPrintf ("Got a NaN velocity on %s\n", PR_GetString(ent->v.classname)); - ent->v.velocity[i] = 0; - } - if (IS_NAN(ent->v.origin[i])) - { - Con_DPrintf ("Got a NaN origin on %s\n", PR_GetString(ent->v.classname)); - ent->v.origin[i] = 0; - } -/* if (ent->v.velocity[i] > sv_maxvelocity.value) - ent->v.velocity[i] = sv_maxvelocity.value; - else if (ent->v.velocity[i] < -sv_maxvelocity.value) - ent->v.velocity[i] = -sv_maxvelocity.value; -*/ - } - - // SV_MAXVELOCITY fix by Maddes - wishspeed = Length(ent->v.velocity); - if (wishspeed > sv_maxvelocity.value) - { - VectorScale (ent->v.velocity, sv_maxvelocity.value/wishspeed, ent->v.velocity); - wishspeed = sv_maxvelocity.value; - } -} - -/* -============= -SV_RunThink - -Runs thinking code if time. There is some play in the exact time the think -function will be called, because it is called before any movement is done -in a frame. Not used for pushmove objects, because they must be exact. -Returns false if the entity removed itself. -============= -*/ -qboolean SV_RunThink (edict_t *ent) -{ - float thinktime; - - do - { - thinktime = ent->v.nextthink; - if (thinktime <= 0) - return true; - if (thinktime > sv.time + sv_frametime) - return true; - - if (thinktime < sv.time) - thinktime = sv.time; // don't let things stay in the past. - // it is possible to start that way - // by a trigger with a local time. - ent->v.nextthink = 0; - pr_global_struct->time = thinktime; - pr_global_struct->self = EDICT_TO_PROG(ent); - pr_global_struct->other = EDICT_TO_PROG(sv.edicts); - PR_ExecuteProgram (ent->v.think); - - if (ent->free) - return false; - } while (1); - - return true; -} - -/* -================== -SV_Impact - -Two entities have touched, so run their touch functions -================== -*/ -void SV_Impact (edict_t *e1, edict_t *e2) -{ - int old_self, old_other; - - old_self = pr_global_struct->self; - old_other = pr_global_struct->other; - - pr_global_struct->time = sv.time; - if (e1->v.touch && e1->v.solid != SOLID_NOT) - { - pr_global_struct->self = EDICT_TO_PROG(e1); - pr_global_struct->other = EDICT_TO_PROG(e2); - PR_ExecuteProgram (e1->v.touch); - } - - if (e2->v.touch && e2->v.solid != SOLID_NOT) - { - pr_global_struct->self = EDICT_TO_PROG(e2); - pr_global_struct->other = EDICT_TO_PROG(e1); - PR_ExecuteProgram (e2->v.touch); - } - - pr_global_struct->self = old_self; - pr_global_struct->other = old_other; -} - - -/* -================== -ClipVelocity - -Slide off of the impacting object -returns the blocked flags (1 = floor, 2 = step / wall) -================== -*/ -#define STOP_EPSILON 0.1 - -int ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce) -{ - float backoff; - float change; - int i, blocked; - - blocked = 0; - if (normal[2] > 0) - blocked |= 1; // floor - if (!normal[2]) - blocked |= 2; // step - - backoff = DotProduct (in, normal) * overbounce; - - for (i=0 ; i<3 ; i++) - { - change = normal[i]*backoff; - out[i] = in[i] - change; - if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON) - out[i] = 0; - } - - return blocked; -} - - -/* -============ -SV_FlyMove - -The basic solid body movement clip that slides along multiple planes -Returns the clipflags if the velocity was modified (hit something solid) -1 = floor -2 = wall / step -4 = dead stop -If steptrace is not NULL, the trace of any vertical wall hit will be stored -============ -*/ -#define MAX_CLIP_PLANES 5 -int SV_FlyMove (edict_t *ent, float time, trace_t *steptrace) -{ - int bumpcount, numbumps; - vec3_t dir; - float d; - int numplanes; - vec3_t planes[MAX_CLIP_PLANES]; - vec3_t primal_velocity, original_velocity, new_velocity; - int i, j; - trace_t trace; - vec3_t end; - float time_left; - int blocked; - - numbumps = 4; - - blocked = 0; - VectorCopy (ent->v.velocity, original_velocity); - VectorCopy (ent->v.velocity, primal_velocity); - numplanes = 0; - - time_left = time; - - for (bumpcount=0 ; bumpcountv.origin[i] + time_left * ent->v.velocity[i]; - - trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, false, ent); - - if (trace.allsolid) - { // entity is trapped in another solid - VectorCopy (vec3_origin, ent->v.velocity); - return 3; - } - - if (trace.fraction > 0) - { // actually covered some distance - VectorCopy (trace.endpos, ent->v.origin); - VectorCopy (ent->v.velocity, original_velocity); - numplanes = 0; - } - - if (trace.fraction == 1) - break; // moved the entire distance - - if (!trace.ent) - SV_Error ("SV_FlyMove: !trace.ent"); - - if (trace.plane.normal[2] > 0.7) - { - blocked |= 1; // floor - if (trace.ent->v.solid == SOLID_BSP) - { - ent->v.flags = (int)ent->v.flags | FL_ONGROUND; - ent->v.groundentity = EDICT_TO_PROG(trace.ent); - } - } - if (!trace.plane.normal[2]) - { - blocked |= 2; // step - if (steptrace) - *steptrace = trace; // save for player extrafriction - } - -// -// run the impact function -// - SV_Impact (ent, trace.ent); - if (ent->free) - break; // removed by the impact function - - - time_left -= time_left * trace.fraction; - - // cliped to another plane - if (numplanes >= MAX_CLIP_PLANES) - { // this shouldn't really happen - VectorCopy (vec3_origin, ent->v.velocity); - return 3; - } - - VectorCopy (trace.plane.normal, planes[numplanes]); - numplanes++; - -// -// modify original_velocity so it parallels all of the clip planes -// - for (i=0 ; iv.velocity); - } - else - { // go along the crease - if (numplanes != 2) - { -// Con_Printf ("clip velocity, numplanes == %i\n",numplanes); - VectorCopy (vec3_origin, ent->v.velocity); - return 7; - } - CrossProduct (planes[0], planes[1], dir); - d = DotProduct (dir, ent->v.velocity); - VectorScale (dir, d, ent->v.velocity); - } - -// -// if original velocity is against the original velocity, stop dead -// to avoid tiny occilations in sloping corners -// - if (DotProduct (ent->v.velocity, primal_velocity) <= 0) - { - VectorCopy (vec3_origin, ent->v.velocity); - return blocked; - } - } - - return blocked; -} - - -/* -============ -SV_AddGravity - -============ -*/ -void SV_AddGravity (edict_t *ent, float scale) -{ - ent->v.velocity[2] -= scale * movevars.gravity * sv_frametime; -} - -/* -=============================================================================== - -PUSHMOVE - -=============================================================================== -*/ - -/* -============ -SV_PushEntity - -Does not change the entities velocity at all -============ -*/ -trace_t SV_PushEntity (edict_t *ent, vec3_t push) -{ - trace_t trace; - vec3_t end; - - VectorAdd (ent->v.origin, push, end); - - if (ent->v.movetype == MOVETYPE_FLYMISSILE) - trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_MISSILE, ent); - else if (ent->v.solid == SOLID_TRIGGER || ent->v.solid == SOLID_NOT) - // only clip against bmodels - trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_NOMONSTERS, ent); - else - trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_NORMAL, ent); - - VectorCopy (trace.endpos, ent->v.origin); - SV_LinkEdict (ent, true); - - if (trace.ent) - SV_Impact (ent, trace.ent); - - return trace; -} - - -/* -============ -SV_Push - -============ -*/ -qboolean SV_Push (edict_t *pusher, vec3_t move) -{ - int i, e; - edict_t *check, *block; - vec3_t mins, maxs; - vec3_t pushorig; - int num_moved; - edict_t *moved_edict[MAX_EDICTS]; - vec3_t moved_from[MAX_EDICTS]; - - for (i=0 ; i<3 ; i++) - { - mins[i] = pusher->v.absmin[i] + move[i]; - maxs[i] = pusher->v.absmax[i] + move[i]; - } - - VectorCopy (pusher->v.origin, pushorig); - -// move the pusher to it's final position - - VectorAdd (pusher->v.origin, move, pusher->v.origin); - SV_LinkEdict (pusher, false); - -// see if any solid entities are inside the final position - num_moved = 0; - check = NEXT_EDICT(sv.edicts); - for (e=1 ; efree) - continue; - if (check->v.movetype == MOVETYPE_PUSH - || check->v.movetype == MOVETYPE_NONE - || check->v.movetype == MOVETYPE_NOCLIP) - continue; - - pusher->v.solid = SOLID_NOT; - block = SV_TestEntityPosition (check); - pusher->v.solid = SOLID_BSP; - if (block) - continue; - - // if the entity is standing on the pusher, it will definately be moved - if ( ! ( ((int)check->v.flags & FL_ONGROUND) - && PROG_TO_EDICT(check->v.groundentity) == pusher) ) - { - if ( check->v.absmin[0] >= maxs[0] - || check->v.absmin[1] >= maxs[1] - || check->v.absmin[2] >= maxs[2] - || check->v.absmax[0] <= mins[0] - || check->v.absmax[1] <= mins[1] - || check->v.absmax[2] <= mins[2] ) - continue; - - // see if the ent's bbox is inside the pusher's final position - if (!SV_TestEntityPosition (check)) - continue; - } - - VectorCopy (check->v.origin, moved_from[num_moved]); - moved_edict[num_moved] = check; - num_moved++; - - // try moving the contacted entity - VectorAdd (check->v.origin, move, check->v.origin); - block = SV_TestEntityPosition (check); - if (!block) - { // pushed ok - SV_LinkEdict (check, false); - continue; - } - - // if it is ok to leave in the old position, do it - VectorSubtract (check->v.origin, move, check->v.origin); - block = SV_TestEntityPosition (check); - if (!block) - { - num_moved--; - continue; - } - - // if it is still inside the pusher, block - if (check->v.mins[0] == check->v.maxs[0]) - { - SV_LinkEdict (check, false); - continue; - } - if (check->v.solid == SOLID_NOT || check->v.solid == SOLID_TRIGGER) - { // corpse - check->v.mins[0] = check->v.mins[1] = 0; - VectorCopy (check->v.mins, check->v.maxs); - SV_LinkEdict (check, false); - continue; - } - - VectorCopy (pushorig, pusher->v.origin); - SV_LinkEdict (pusher, false); - - // if the pusher has a "blocked" function, call it - // otherwise, just stay in place until the obstacle is gone - if (pusher->v.blocked) - { - pr_global_struct->self = EDICT_TO_PROG(pusher); - pr_global_struct->other = EDICT_TO_PROG(check); - PR_ExecuteProgram (pusher->v.blocked); - } - - // move back any entities we already moved - for (i=0 ; iv.origin); - SV_LinkEdict (moved_edict[i], false); - } - return false; - } - - return true; -} - -/* -============ -SV_PushMove - -============ -*/ -void SV_PushMove (edict_t *pusher, float movetime) -{ - int i; - vec3_t move; - - if (!pusher->v.velocity[0] && !pusher->v.velocity[1] && !pusher->v.velocity[2]) - { - pusher->v.ltime += movetime; - return; - } - - for (i=0 ; i<3 ; i++) - move[i] = pusher->v.velocity[i] * movetime; - - if (SV_Push (pusher, move)) - pusher->v.ltime += movetime; -} - - -/* -================ -SV_Physics_Pusher - -================ -*/ -void SV_Physics_Pusher (edict_t *ent) -{ - float thinktime; - float oldltime; - float movetime; -vec3_t oldorg, move; -float l; - - oldltime = ent->v.ltime; - - thinktime = ent->v.nextthink; - if (thinktime < ent->v.ltime + sv_frametime) - { - movetime = thinktime - ent->v.ltime; - if (movetime < 0) - movetime = 0; - } - else - movetime = sv_frametime; - - if (movetime) - { - SV_PushMove (ent, movetime); // advances ent->v.ltime if not blocked - } - - if (thinktime > oldltime && thinktime <= ent->v.ltime) - { -VectorCopy (ent->v.origin, oldorg); - ent->v.nextthink = 0; - pr_global_struct->time = sv.time; - pr_global_struct->self = EDICT_TO_PROG(ent); - pr_global_struct->other = EDICT_TO_PROG(sv.edicts); - PR_ExecuteProgram (ent->v.think); - if (ent->free) - return; -VectorSubtract (ent->v.origin, oldorg, move); - -l = Length(move); -if (l > 1.0/64) -{ -// Con_Printf ("**** snap: %f\n", Length (l)); - VectorCopy (oldorg, ent->v.origin); - SV_Push (ent, move); -} - - } - -} - - -/* -============= -SV_Physics_None - -Non moving objects can only think -============= -*/ -void SV_Physics_None (edict_t *ent) -{ -// regular thinking - SV_RunThink (ent); -} - -/* -============= -SV_Physics_Noclip - -A moving object that doesn't obey physics -============= -*/ -void SV_Physics_Noclip (edict_t *ent) -{ -// regular thinking - if (!SV_RunThink (ent)) - return; - - VectorMA (ent->v.angles, sv_frametime, ent->v.avelocity, ent->v.angles); - VectorMA (ent->v.origin, sv_frametime, ent->v.velocity, ent->v.origin); - - SV_LinkEdict (ent, false); -} - -/* -============================================================================== - -TOSS / BOUNCE - -============================================================================== -*/ - -/* -============= -SV_CheckWaterTransition - -============= -*/ -void SV_CheckWaterTransition (edict_t *ent) -{ - int cont; - - cont = SV_PointContents (ent->v.origin); - if (!ent->v.watertype) - { // just spawned here - ent->v.watertype = cont; - ent->v.waterlevel = 1; - return; - } - - if (cont <= CONTENTS_WATER) - { - if (ent->v.watertype == CONTENTS_EMPTY) - { // just crossed into water - SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1); - } - ent->v.watertype = cont; - ent->v.waterlevel = 1; - } - else - { - if (ent->v.watertype != CONTENTS_EMPTY) - { // just crossed into water - SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1); - } - ent->v.watertype = CONTENTS_EMPTY; - ent->v.waterlevel = cont; - } -} - -/* -============= -SV_Physics_Toss - -Toss, bounce, and fly movement. When onground, do nothing. -============= -*/ -void SV_Physics_Toss (edict_t *ent) -{ - trace_t trace; - vec3_t move; - float backoff; - -// regular thinking - if (!SV_RunThink (ent)) - return; - - if (ent->v.velocity[2] > 0) - ent->v.flags = (int)ent->v.flags & ~FL_ONGROUND; - -// if onground, return without moving - if ( ((int)ent->v.flags & FL_ONGROUND) ) - return; - - SV_CheckVelocity (ent); - -// add gravity - if (ent->v.movetype != MOVETYPE_FLY - && ent->v.movetype != MOVETYPE_FLYMISSILE) - SV_AddGravity (ent, 1.0); - -// move angles - VectorMA (ent->v.angles, sv_frametime, ent->v.avelocity, ent->v.angles); - -// move origin - VectorScale (ent->v.velocity, sv_frametime, move); - trace = SV_PushEntity (ent, move); - if (trace.fraction == 1) - return; - if (ent->free) - return; - - if (ent->v.movetype == MOVETYPE_BOUNCE) - backoff = 1.5; - else - backoff = 1; - - ClipVelocity (ent->v.velocity, trace.plane.normal, ent->v.velocity, backoff); - -// stop if on ground - if (trace.plane.normal[2] > 0.7) - { - if (ent->v.velocity[2] < 60 || ent->v.movetype != MOVETYPE_BOUNCE ) - { - ent->v.flags = (int)ent->v.flags | FL_ONGROUND; - ent->v.groundentity = EDICT_TO_PROG(trace.ent); - VectorCopy (vec3_origin, ent->v.velocity); - VectorCopy (vec3_origin, ent->v.avelocity); - } - } - -// check for in water - SV_CheckWaterTransition (ent); -} - -/* -=============================================================================== - -STEPPING MOVEMENT - -=============================================================================== -*/ - -/* -============= -SV_Physics_Step - -Monsters freefall when they don't have a ground entity, otherwise -all movement is done with discrete steps. - -This is also used for objects that have become still on the ground, but -will fall if the floor is pulled out from under them. -FIXME: is this true? -============= -*/ -void SV_Physics_Step (edict_t *ent) -{ - qboolean hitsound; - -// frefall if not onground - if ( ! ((int)ent->v.flags & (FL_ONGROUND | FL_FLY | FL_SWIM) ) ) - { - if (ent->v.velocity[2] < movevars.gravity*-0.1) - hitsound = true; - else - hitsound = false; - - SV_AddGravity (ent, 1.0); - SV_CheckVelocity (ent); - SV_FlyMove (ent, sv_frametime, NULL); - SV_LinkEdict (ent, true); - - if ( (int)ent->v.flags & FL_ONGROUND ) // just hit ground - { - if (hitsound) - SV_StartSound (ent, 0, "demon/dland2.wav", 255, 1); - } - } - -// regular thinking - SV_RunThink (ent); - - SV_CheckWaterTransition (ent); -} - -//============================================================================ - -void SV_ProgStartFrame (void) -{ -// let the progs know that a new frame has started - pr_global_struct->self = EDICT_TO_PROG(sv.edicts); - pr_global_struct->other = EDICT_TO_PROG(sv.edicts); - pr_global_struct->time = sv.time; - PR_ExecuteProgram (pr_global_struct->StartFrame); -} - -/* -================ -SV_RunEntity - -================ -*/ -void SV_RunEntity (edict_t *ent) -{ - if (ent->v.lastruntime == (float)realtime) - return; - ent->v.lastruntime = (float)realtime; - - switch ( (int)ent->v.movetype) - { - case MOVETYPE_PUSH: - SV_Physics_Pusher (ent); - break; - case MOVETYPE_NONE: - SV_Physics_None (ent); - break; - case MOVETYPE_NOCLIP: - SV_Physics_Noclip (ent); - break; - case MOVETYPE_STEP: - SV_Physics_Step (ent); - break; - case MOVETYPE_TOSS: - case MOVETYPE_BOUNCE: - case MOVETYPE_FLY: - case MOVETYPE_FLYMISSILE: - SV_Physics_Toss (ent); - break; - default: - SV_Error ("SV_Physics: bad movetype %i", (int)ent->v.movetype); - } -} - -/* -================ -SV_RunNewmis - -================ -*/ -void SV_RunNewmis (void) -{ - edict_t *ent; - - if (!pr_global_struct->newmis) - return; - ent = PROG_TO_EDICT(pr_global_struct->newmis); - sv_frametime = 0.05; - pr_global_struct->newmis = 0; - - SV_RunEntity (ent); -} - -/* -================ -SV_Physics - -================ -*/ -void SV_Physics (void) -{ - int i; - edict_t *ent; - static double old_time; - -// don't bother running a frame if sys_ticrate seconds haven't passed - sv_frametime = realtime - old_time; - if (sv_frametime < sv_mintic.value) - return; - if (sv_frametime > sv_maxtic.value) - sv_frametime = sv_maxtic.value; - old_time = realtime; - - pr_global_struct->frametime = sv_frametime; - - SV_ProgStartFrame (); - -// -// treat each object in turn -// even the world gets a chance to think -// - ent = sv.edicts; - for (i=0 ; ifree) - continue; - - if (pr_global_struct->force_retouch) - SV_LinkEdict (ent, true); // force retouch even for stationary - - if (i > 0 && i <= MAX_CLIENTS) - continue; // clients are run directly from packets - - SV_RunEntity (ent); - SV_RunNewmis (); - } - - if (pr_global_struct->force_retouch) - pr_global_struct->force_retouch--; -} - -void SV_SetMoveVars(void) -{ - movevars.gravity = sv_gravity.value; - movevars.stopspeed = sv_stopspeed.value; - movevars.maxspeed = sv_maxspeed.value; - movevars.spectatormaxspeed = sv_spectatormaxspeed.value; - movevars.accelerate = sv_accelerate.value; - movevars.airaccelerate = sv_airaccelerate.value; - movevars.wateraccelerate = sv_wateraccelerate.value; - movevars.friction = sv_friction.value; - movevars.waterfriction = sv_waterfriction.value; - movevars.entgravity = 1.0; -} +/* +Copyright (C) 1996-1997 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. + +*/ +// sv_phys.c + +#include "qwsvdef.h" + +/* + + +pushmove objects do not obey gravity, and do not interact with each other or trigger fields, but block normal movement and push normal objects when they move. + +onground is set for toss objects when they come to a complete rest. it is set for steping or walking objects + +doors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH +bonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS +corpses are SOLID_NOT and MOVETYPE_TOSS +crates are SOLID_BBOX and MOVETYPE_TOSS +walking monsters are SOLID_SLIDEBOX and MOVETYPE_STEP +flying/floating monsters are SOLID_SLIDEBOX and MOVETYPE_FLY + +solid_edge items only clip against bsp models. + +*/ + +cvar_t sv_maxvelocity = {"sv_maxvelocity","2000"}; + +cvar_t sv_gravity = { "sv_gravity", "800"}; +cvar_t sv_stopspeed = { "sv_stopspeed", "100"}; +cvar_t sv_maxspeed = { "sv_maxspeed", "320"}; +cvar_t sv_spectatormaxspeed = { "sv_spectatormaxspeed", "500"}; +cvar_t sv_accelerate = { "sv_accelerate", "10"}; +cvar_t sv_airaccelerate = { "sv_airaccelerate", "10"}; +cvar_t sv_wateraccelerate = { "sv_wateraccelerate", "10"}; +cvar_t sv_friction = { "sv_friction", "4"}; +cvar_t sv_waterfriction = { "sv_waterfriction", "4"}; + + +#define MOVE_EPSILON 0.01 + +void SV_Physics_Toss (edict_t *ent); + +/* +================ +SV_CheckAllEnts +================ +*/ +void SV_CheckAllEnts (void) +{ + int e; + edict_t *check; + +// see if any solid entities are inside the final position + check = NEXT_EDICT(sv.edicts); + for (e=1 ; efree) + continue; + if (check->v.movetype == MOVETYPE_PUSH + || check->v.movetype == MOVETYPE_NONE + || check->v.movetype == MOVETYPE_NOCLIP) + continue; + + if (SV_TestEntityPosition (check)) + Con_Printf ("entity in invalid position\n"); + } +} + +/* +================ +SV_CheckVelocity +================ +*/ +void SV_CheckVelocity (edict_t *ent) +{ + int i; + float wishspeed; + +// +// bound velocity +// + for (i=0 ; i<3 ; i++) + { + if (IS_NAN(ent->v.velocity[i])) + { + Con_DPrintf ("Got a NaN velocity on %s\n", PR_GetString(ent->v.classname)); + ent->v.velocity[i] = 0; + } + if (IS_NAN(ent->v.origin[i])) + { + Con_DPrintf ("Got a NaN origin on %s\n", PR_GetString(ent->v.classname)); + ent->v.origin[i] = 0; + } +/* if (ent->v.velocity[i] > sv_maxvelocity.value) + ent->v.velocity[i] = sv_maxvelocity.value; + else if (ent->v.velocity[i] < -sv_maxvelocity.value) + ent->v.velocity[i] = -sv_maxvelocity.value; +*/ + } + + // SV_MAXVELOCITY fix by Maddes + wishspeed = Length(ent->v.velocity); + if (wishspeed > sv_maxvelocity.value) + { + VectorScale (ent->v.velocity, sv_maxvelocity.value/wishspeed, ent->v.velocity); + wishspeed = sv_maxvelocity.value; + } +} + +/* +============= +SV_RunThink + +Runs thinking code if time. There is some play in the exact time the think +function will be called, because it is called before any movement is done +in a frame. Not used for pushmove objects, because they must be exact. +Returns false if the entity removed itself. +============= +*/ +qboolean SV_RunThink (edict_t *ent) +{ + float thinktime; + + do + { + thinktime = ent->v.nextthink; + if (thinktime <= 0) + return true; + if (thinktime > sv.time + sv_frametime) + return true; + + if (thinktime < sv.time) + thinktime = sv.time; // don't let things stay in the past. + // it is possible to start that way + // by a trigger with a local time. + ent->v.nextthink = 0; + pr_global_struct->time = thinktime; + pr_global_struct->self = EDICT_TO_PROG(ent); + pr_global_struct->other = EDICT_TO_PROG(sv.edicts); + PR_ExecuteProgram (ent->v.think); + + if (ent->free) + return false; + } while (1); + + return true; +} + +/* +================== +SV_Impact + +Two entities have touched, so run their touch functions +================== +*/ +void SV_Impact (edict_t *e1, edict_t *e2) +{ + int old_self, old_other; + + old_self = pr_global_struct->self; + old_other = pr_global_struct->other; + + pr_global_struct->time = sv.time; + if (e1->v.touch && e1->v.solid != SOLID_NOT) + { + pr_global_struct->self = EDICT_TO_PROG(e1); + pr_global_struct->other = EDICT_TO_PROG(e2); + PR_ExecuteProgram (e1->v.touch); + } + + if (e2->v.touch && e2->v.solid != SOLID_NOT) + { + pr_global_struct->self = EDICT_TO_PROG(e2); + pr_global_struct->other = EDICT_TO_PROG(e1); + PR_ExecuteProgram (e2->v.touch); + } + + pr_global_struct->self = old_self; + pr_global_struct->other = old_other; +} + + +/* +================== +ClipVelocity + +Slide off of the impacting object +returns the blocked flags (1 = floor, 2 = step / wall) +================== +*/ +#define STOP_EPSILON 0.1 + +int ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce) +{ + float backoff; + float change; + int i, blocked; + + blocked = 0; + if (normal[2] > 0) + blocked |= 1; // floor + if (!normal[2]) + blocked |= 2; // step + + backoff = DotProduct (in, normal) * overbounce; + + for (i=0 ; i<3 ; i++) + { + change = normal[i]*backoff; + out[i] = in[i] - change; + if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON) + out[i] = 0; + } + + return blocked; +} + + +/* +============ +SV_FlyMove + +The basic solid body movement clip that slides along multiple planes +Returns the clipflags if the velocity was modified (hit something solid) +1 = floor +2 = wall / step +4 = dead stop +If steptrace is not NULL, the trace of any vertical wall hit will be stored +============ +*/ +#define MAX_CLIP_PLANES 5 +int SV_FlyMove (edict_t *ent, float time, trace_t *steptrace) +{ + int bumpcount, numbumps; + vec3_t dir; + float d; + int numplanes; + vec3_t planes[MAX_CLIP_PLANES]; + vec3_t primal_velocity, original_velocity, new_velocity; + int i, j; + trace_t trace; + vec3_t end; + float time_left; + int blocked; + + numbumps = 4; + + blocked = 0; + VectorCopy (ent->v.velocity, original_velocity); + VectorCopy (ent->v.velocity, primal_velocity); + numplanes = 0; + + time_left = time; + + for (bumpcount=0 ; bumpcountv.origin[i] + time_left * ent->v.velocity[i]; + + trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, false, ent); + + if (trace.allsolid) + { // entity is trapped in another solid + VectorCopy (vec3_origin, ent->v.velocity); + return 3; + } + + if (trace.fraction > 0) + { // actually covered some distance + VectorCopy (trace.endpos, ent->v.origin); + VectorCopy (ent->v.velocity, original_velocity); + numplanes = 0; + } + + if (trace.fraction == 1) + break; // moved the entire distance + + if (!trace.ent) + SV_Error ("SV_FlyMove: !trace.ent"); + + if (trace.plane.normal[2] > 0.7) + { + blocked |= 1; // floor + if (trace.ent->v.solid == SOLID_BSP) + { + ent->v.flags = (int)ent->v.flags | FL_ONGROUND; + ent->v.groundentity = EDICT_TO_PROG(trace.ent); + } + } + if (!trace.plane.normal[2]) + { + blocked |= 2; // step + if (steptrace) + *steptrace = trace; // save for player extrafriction + } + +// +// run the impact function +// + SV_Impact (ent, trace.ent); + if (ent->free) + break; // removed by the impact function + + + time_left -= time_left * trace.fraction; + + // cliped to another plane + if (numplanes >= MAX_CLIP_PLANES) + { // this shouldn't really happen + VectorCopy (vec3_origin, ent->v.velocity); + return 3; + } + + VectorCopy (trace.plane.normal, planes[numplanes]); + numplanes++; + +// +// modify original_velocity so it parallels all of the clip planes +// + for (i=0 ; iv.velocity); + } + else + { // go along the crease + if (numplanes != 2) + { +// Con_Printf ("clip velocity, numplanes == %i\n",numplanes); + VectorCopy (vec3_origin, ent->v.velocity); + return 7; + } + CrossProduct (planes[0], planes[1], dir); + d = DotProduct (dir, ent->v.velocity); + VectorScale (dir, d, ent->v.velocity); + } + +// +// if original velocity is against the original velocity, stop dead +// to avoid tiny occilations in sloping corners +// + if (DotProduct (ent->v.velocity, primal_velocity) <= 0) + { + VectorCopy (vec3_origin, ent->v.velocity); + return blocked; + } + } + + return blocked; +} + + +/* +============ +SV_AddGravity + +============ +*/ +void SV_AddGravity (edict_t *ent, float scale) +{ + ent->v.velocity[2] -= scale * movevars.gravity * sv_frametime; +} + +/* +=============================================================================== + +PUSHMOVE + +=============================================================================== +*/ + +/* +============ +SV_PushEntity + +Does not change the entities velocity at all +============ +*/ +trace_t SV_PushEntity (edict_t *ent, vec3_t push) +{ + trace_t trace; + vec3_t end; + + VectorAdd (ent->v.origin, push, end); + + if (ent->v.movetype == MOVETYPE_FLYMISSILE) + trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_MISSILE, ent); + else if (ent->v.solid == SOLID_TRIGGER || ent->v.solid == SOLID_NOT) + // only clip against bmodels + trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_NOMONSTERS, ent); + else + trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_NORMAL, ent); + + VectorCopy (trace.endpos, ent->v.origin); + SV_LinkEdict (ent, true); + + if (trace.ent) + SV_Impact (ent, trace.ent); + + return trace; +} + + +/* +============ +SV_Push + +============ +*/ +qboolean SV_Push (edict_t *pusher, vec3_t move) +{ + int i, e; + edict_t *check, *block; + vec3_t mins, maxs; + vec3_t pushorig; + int num_moved; + edict_t *moved_edict[MAX_EDICTS]; + vec3_t moved_from[MAX_EDICTS]; + + for (i=0 ; i<3 ; i++) + { + mins[i] = pusher->v.absmin[i] + move[i]; + maxs[i] = pusher->v.absmax[i] + move[i]; + } + + VectorCopy (pusher->v.origin, pushorig); + +// move the pusher to it's final position + + VectorAdd (pusher->v.origin, move, pusher->v.origin); + SV_LinkEdict (pusher, false); + +// see if any solid entities are inside the final position + num_moved = 0; + check = NEXT_EDICT(sv.edicts); + for (e=1 ; efree) + continue; + if (check->v.movetype == MOVETYPE_PUSH + || check->v.movetype == MOVETYPE_NONE + || check->v.movetype == MOVETYPE_NOCLIP) + continue; + + pusher->v.solid = SOLID_NOT; + block = SV_TestEntityPosition (check); + pusher->v.solid = SOLID_BSP; + if (block) + continue; + + // if the entity is standing on the pusher, it will definately be moved + if ( ! ( ((int)check->v.flags & FL_ONGROUND) + && PROG_TO_EDICT(check->v.groundentity) == pusher) ) + { + if ( check->v.absmin[0] >= maxs[0] + || check->v.absmin[1] >= maxs[1] + || check->v.absmin[2] >= maxs[2] + || check->v.absmax[0] <= mins[0] + || check->v.absmax[1] <= mins[1] + || check->v.absmax[2] <= mins[2] ) + continue; + + // see if the ent's bbox is inside the pusher's final position + if (!SV_TestEntityPosition (check)) + continue; + } + + VectorCopy (check->v.origin, moved_from[num_moved]); + moved_edict[num_moved] = check; + num_moved++; + + // try moving the contacted entity + VectorAdd (check->v.origin, move, check->v.origin); + block = SV_TestEntityPosition (check); + if (!block) + { // pushed ok + SV_LinkEdict (check, false); + continue; + } + + // if it is ok to leave in the old position, do it + VectorSubtract (check->v.origin, move, check->v.origin); + block = SV_TestEntityPosition (check); + if (!block) + { + num_moved--; + continue; + } + + // if it is still inside the pusher, block + if (check->v.mins[0] == check->v.maxs[0]) + { + SV_LinkEdict (check, false); + continue; + } + if (check->v.solid == SOLID_NOT || check->v.solid == SOLID_TRIGGER) + { // corpse + check->v.mins[0] = check->v.mins[1] = 0; + VectorCopy (check->v.mins, check->v.maxs); + SV_LinkEdict (check, false); + continue; + } + + VectorCopy (pushorig, pusher->v.origin); + SV_LinkEdict (pusher, false); + + // if the pusher has a "blocked" function, call it + // otherwise, just stay in place until the obstacle is gone + if (pusher->v.blocked) + { + pr_global_struct->self = EDICT_TO_PROG(pusher); + pr_global_struct->other = EDICT_TO_PROG(check); + PR_ExecuteProgram (pusher->v.blocked); + } + + // move back any entities we already moved + for (i=0 ; iv.origin); + SV_LinkEdict (moved_edict[i], false); + } + return false; + } + + return true; +} + +/* +============ +SV_PushMove + +============ +*/ +void SV_PushMove (edict_t *pusher, float movetime) +{ + int i; + vec3_t move; + + if (!pusher->v.velocity[0] && !pusher->v.velocity[1] && !pusher->v.velocity[2]) + { + pusher->v.ltime += movetime; + return; + } + + for (i=0 ; i<3 ; i++) + move[i] = pusher->v.velocity[i] * movetime; + + if (SV_Push (pusher, move)) + pusher->v.ltime += movetime; +} + + +/* +================ +SV_Physics_Pusher + +================ +*/ +void SV_Physics_Pusher (edict_t *ent) +{ + float thinktime; + float oldltime; + float movetime; +vec3_t oldorg, move; +float l; + + oldltime = ent->v.ltime; + + thinktime = ent->v.nextthink; + if (thinktime < ent->v.ltime + sv_frametime) + { + movetime = thinktime - ent->v.ltime; + if (movetime < 0) + movetime = 0; + } + else + movetime = sv_frametime; + + if (movetime) + { + SV_PushMove (ent, movetime); // advances ent->v.ltime if not blocked + } + + if (thinktime > oldltime && thinktime <= ent->v.ltime) + { +VectorCopy (ent->v.origin, oldorg); + ent->v.nextthink = 0; + pr_global_struct->time = sv.time; + pr_global_struct->self = EDICT_TO_PROG(ent); + pr_global_struct->other = EDICT_TO_PROG(sv.edicts); + PR_ExecuteProgram (ent->v.think); + if (ent->free) + return; +VectorSubtract (ent->v.origin, oldorg, move); + +l = Length(move); +if (l > 1.0/64) +{ +// Con_Printf ("**** snap: %f\n", Length (l)); + VectorCopy (oldorg, ent->v.origin); + SV_Push (ent, move); +} + + } + +} + + +/* +============= +SV_Physics_None + +Non moving objects can only think +============= +*/ +void SV_Physics_None (edict_t *ent) +{ +// regular thinking + SV_RunThink (ent); +} + +/* +============= +SV_Physics_Noclip + +A moving object that doesn't obey physics +============= +*/ +void SV_Physics_Noclip (edict_t *ent) +{ +// regular thinking + if (!SV_RunThink (ent)) + return; + + VectorMA (ent->v.angles, sv_frametime, ent->v.avelocity, ent->v.angles); + VectorMA (ent->v.origin, sv_frametime, ent->v.velocity, ent->v.origin); + + SV_LinkEdict (ent, false); +} + +/* +============================================================================== + +TOSS / BOUNCE + +============================================================================== +*/ + +/* +============= +SV_CheckWaterTransition + +============= +*/ +void SV_CheckWaterTransition (edict_t *ent) +{ + int cont; + + cont = SV_PointContents (ent->v.origin); + if (!ent->v.watertype) + { // just spawned here + ent->v.watertype = cont; + ent->v.waterlevel = 1; + return; + } + + if (cont <= CONTENTS_WATER) + { + if (ent->v.watertype == CONTENTS_EMPTY) + { // just crossed into water + SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1); + } + ent->v.watertype = cont; + ent->v.waterlevel = 1; + } + else + { + if (ent->v.watertype != CONTENTS_EMPTY) + { // just crossed into water + SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1); + } + ent->v.watertype = CONTENTS_EMPTY; + ent->v.waterlevel = cont; + } +} + +/* +============= +SV_Physics_Toss + +Toss, bounce, and fly movement. When onground, do nothing. +============= +*/ +void SV_Physics_Toss (edict_t *ent) +{ + trace_t trace; + vec3_t move; + float backoff; + +// regular thinking + if (!SV_RunThink (ent)) + return; + + if (ent->v.velocity[2] > 0) + ent->v.flags = (int)ent->v.flags & ~FL_ONGROUND; + +// if onground, return without moving + if ( ((int)ent->v.flags & FL_ONGROUND) ) + return; + + SV_CheckVelocity (ent); + +// add gravity + if (ent->v.movetype != MOVETYPE_FLY + && ent->v.movetype != MOVETYPE_FLYMISSILE) + SV_AddGravity (ent, 1.0); + +// move angles + VectorMA (ent->v.angles, sv_frametime, ent->v.avelocity, ent->v.angles); + +// move origin + VectorScale (ent->v.velocity, sv_frametime, move); + trace = SV_PushEntity (ent, move); + if (trace.fraction == 1) + return; + if (ent->free) + return; + + if (ent->v.movetype == MOVETYPE_BOUNCE) + backoff = 1.5; + else + backoff = 1; + + ClipVelocity (ent->v.velocity, trace.plane.normal, ent->v.velocity, backoff); + +// stop if on ground + if (trace.plane.normal[2] > 0.7) + { + if (ent->v.velocity[2] < 60 || ent->v.movetype != MOVETYPE_BOUNCE ) + { + ent->v.flags = (int)ent->v.flags | FL_ONGROUND; + ent->v.groundentity = EDICT_TO_PROG(trace.ent); + VectorCopy (vec3_origin, ent->v.velocity); + VectorCopy (vec3_origin, ent->v.avelocity); + } + } + +// check for in water + SV_CheckWaterTransition (ent); +} + +/* +=============================================================================== + +STEPPING MOVEMENT + +=============================================================================== +*/ + +/* +============= +SV_Physics_Step + +Monsters freefall when they don't have a ground entity, otherwise +all movement is done with discrete steps. + +This is also used for objects that have become still on the ground, but +will fall if the floor is pulled out from under them. +FIXME: is this true? +============= +*/ +void SV_Physics_Step (edict_t *ent) +{ + qboolean hitsound; + +// frefall if not onground + if ( ! ((int)ent->v.flags & (FL_ONGROUND | FL_FLY | FL_SWIM) ) ) + { + if (ent->v.velocity[2] < movevars.gravity*-0.1) + hitsound = true; + else + hitsound = false; + + SV_AddGravity (ent, 1.0); + SV_CheckVelocity (ent); + SV_FlyMove (ent, sv_frametime, NULL); + SV_LinkEdict (ent, true); + + if ( (int)ent->v.flags & FL_ONGROUND ) // just hit ground + { + if (hitsound) + SV_StartSound (ent, 0, "demon/dland2.wav", 255, 1); + } + } + +// regular thinking + SV_RunThink (ent); + + SV_CheckWaterTransition (ent); +} + +//============================================================================ + +void SV_ProgStartFrame (void) +{ +// let the progs know that a new frame has started + pr_global_struct->self = EDICT_TO_PROG(sv.edicts); + pr_global_struct->other = EDICT_TO_PROG(sv.edicts); + pr_global_struct->time = sv.time; + PR_ExecuteProgram (pr_global_struct->StartFrame); +} + +/* +================ +SV_RunEntity + +================ +*/ +void SV_RunEntity (edict_t *ent) +{ + if ((float)sv.time == ent->v.lastruntime) + return; + ent->v.lastruntime = (float)sv.time; + + switch ( (int)ent->v.movetype) + { + case MOVETYPE_PUSH: + SV_Physics_Pusher (ent); + break; + case MOVETYPE_NONE: + SV_Physics_None (ent); + break; + case MOVETYPE_NOCLIP: + SV_Physics_Noclip (ent); + break; + case MOVETYPE_STEP: + SV_Physics_Step (ent); + break; + case MOVETYPE_TOSS: + case MOVETYPE_BOUNCE: + case MOVETYPE_FLY: + case MOVETYPE_FLYMISSILE: + SV_Physics_Toss (ent); + break; + default: + SV_Error ("SV_Physics: bad movetype %i", (int)ent->v.movetype); + } +} + +/* +================ +SV_RunNewmis + +================ +*/ +void SV_RunNewmis (void) +{ + edict_t *ent; + + if (!pr_global_struct->newmis) + return; + ent = PROG_TO_EDICT(pr_global_struct->newmis); + sv_frametime = 0.05; + pr_global_struct->newmis = 0; + + SV_RunEntity (ent); +} + +/* +================ +SV_Physics + +================ +*/ +void SV_Physics (void) +{ + int i; + edict_t *ent; + static double old_time; + +// don't bother running a frame if sys_ticrate seconds haven't passed + +#ifdef NEWWAY + sv_frametime = sv_ticrate.value; + sv.time += sv_ticrate.value; + if (sv.time < sv.gametime) + { + Con_Printf ("sv highclamp\n"); + sv.gametime = sv.time; + } +#else + if (sv.time < old_time) + old_time = sv.time - 0.1; + + //if (sv.time - old_time > 1.0) + // old_time = sv.time - 1.0; + + sv_frametime = (float) (sv.time - old_time); + + if (sv_frametime < sv_mintic.value) + return; + if (sv_frametime > sv_maxtic.value) + sv_frametime = sv_maxtic.value; + + old_time = sv.time; +#endif + + pr_global_struct->frametime = sv_frametime; + + SV_ProgStartFrame (); + +// +// treat each object in turn +// even the world gets a chance to think +// + ent = sv.edicts; + for (i=0 ; ifree) + continue; + + if (pr_global_struct->force_retouch) + SV_LinkEdict (ent, true); // force retouch even for stationary + + if (i > 0 && i <= MAX_CLIENTS) + continue; // clients are run directly from packets + + SV_RunEntity (ent); + SV_RunNewmis (); + } + + if (pr_global_struct->force_retouch) + pr_global_struct->force_retouch--; +} + +void SV_SetMoveVars(void) +{ + movevars.gravity = sv_gravity.value; + movevars.stopspeed = sv_stopspeed.value; + movevars.maxspeed = sv_maxspeed.value; + movevars.spectatormaxspeed = sv_spectatormaxspeed.value; + movevars.accelerate = sv_accelerate.value; + movevars.airaccelerate = sv_airaccelerate.value; + movevars.wateraccelerate = sv_wateraccelerate.value; + movevars.friction = sv_friction.value; + movevars.waterfriction = sv_waterfriction.value; + movevars.entgravity = 1.0; +} diff --git a/source/sv_send.c b/source/sv_send.c index 05807bb2..01a17ca5 100644 --- a/source/sv_send.c +++ b/source/sv_send.c @@ -1,1077 +1,1074 @@ -/* -Copyright (C) 1996-1997 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 "qwsvdef.h" - -#define CHAN_AUTO 0 -#define CHAN_WEAPON 1 -#define CHAN_VOICE 2 -#define CHAN_ITEM 3 -#define CHAN_BODY 4 - -/* -============================================================================= - -Con_Printf redirection - -============================================================================= -*/ - -#define MAX_REDIRECTMESSAGES 2 -char outputbuf[8000]; - -redirect_t sv_redirected; -static int sv_redirectbufcount; - -extern cvar_t sv_phs; - -/* -================== -SV_FlushRedirect -================== -*/ -void SV_FlushRedirect (void) -{ - char send[8000+6]; - - if (sv_redirected == RD_PACKET) - { - send[0] = 0xff; - send[1] = 0xff; - send[2] = 0xff; - send[3] = 0xff; - send[4] = A2C_PRINT; - memcpy (send+5, outputbuf, strlen(outputbuf)+1); - - NET_SendPacket (net_serversocket, strlen(send)+1, send, net_from); - } - else if (sv_redirected == RD_CLIENT && sv_redirectbufcount < MAX_REDIRECTMESSAGES) - { - ClientReliableWrite_Begin (host_client, svc_print, strlen(outputbuf)+3); - ClientReliableWrite_Byte (host_client, PRINT_HIGH); - ClientReliableWrite_String (host_client, outputbuf); - sv_redirectbufcount++; - } - - // clear it - outputbuf[0] = 0; -} - - -/* -================== -SV_BeginRedirect - - Send Con_Printf data to the remote client - instead of the console -================== -*/ -void SV_BeginRedirect (redirect_t rd) -{ - sv_redirected = rd; - outputbuf[0] = 0; - sv_redirectbufcount = 0; -} - -void SV_EndRedirect (void) -{ - SV_FlushRedirect (); - sv_redirected = RD_NONE; -} - - - -/* -================ -Con_Printf - -Handles cursor positioning, line wrapping, etc -================ -*/ -#define MAXPRINTMSG 4096 -// FIXME: make a buffer size safe vsprintf? -void Con_Printf (char *fmt, ...) -{ - va_list argptr; - char msg[MAXPRINTMSG]; - - va_start (argptr,fmt); - vsprintf (msg,fmt,argptr); - va_end (argptr); - - // add to redirected message - if (sv_redirected) - { - if (strlen (msg) + strlen(outputbuf) > sizeof(outputbuf) - 1) - SV_FlushRedirect (); - strcat (outputbuf, msg); - return; - } - - Sys_Printf ("%s", msg); // also echo to debugging console - if (sv_logfile) - fprintf (sv_logfile, "%s", msg); -} - -/* -================ -Con_DPrintf - -A Con_Printf that only shows up if the "developer" cvar is set -================ -*/ -void Con_DPrintf (char *fmt, ...) -{ - va_list argptr; - char msg[MAXPRINTMSG]; - - if (!developer.value) - return; - - va_start (argptr,fmt); - vsprintf (msg,fmt,argptr); - va_end (argptr); - - Con_Printf ("%s", msg); -} - -/* -============================================================================= - -EVENT MESSAGES - -============================================================================= -*/ - -static void SV_PrintToClient(client_t *cl, int level, char *string) -{ - ClientReliableWrite_Begin (cl, svc_print, strlen(string)+3); - ClientReliableWrite_Byte (cl, level); - ClientReliableWrite_String (cl, string); -} - - -/* -================= -SV_ClientPrintf - -Sends text across to be displayed if the level passes -================= -*/ -void SV_ClientPrintf (client_t *cl, int level, char *fmt, ...) -{ - va_list argptr; - char string[1024]; - - if (level < cl->messagelevel) - return; - - va_start (argptr,fmt); - vsprintf (string, fmt,argptr); - va_end (argptr); - - if (sv.demorecording) { - - DemoReliableWrite_Begin (dem_single, cl - svs.clients, strlen(string)+3); - MSG_WriteByte (demo.buf, svc_print); - MSG_WriteByte (demo.buf, level); - MSG_WriteString (demo.buf, string); - } - - SV_PrintToClient(cl, level, string); -} - -void SV_ClientPrintf2 (client_t *cl, int level, char *fmt, ...) -{ - va_list argptr; - char string[1024]; - - if (level < cl->messagelevel) - return; - - va_start (argptr,fmt); - vsprintf (string, fmt,argptr); - va_end (argptr); - - SV_PrintToClient(cl, level, string); -} - - -/* -================= -SV_BroadcastPrintf - -Sends text to all active clients -================= -*/ -void SV_BroadcastPrintf (int level, char *fmt, ...) -{ - va_list argptr; - char string[1024]; - client_t *cl; - int i; - - va_start (argptr,fmt); - vsprintf (string, fmt,argptr); - va_end (argptr); - - Sys_Printf ("%s", string); // print to the console - - for (i=0, cl = svs.clients ; imessagelevel) - continue; - if (!cl->state) - continue; - - SV_PrintToClient(cl, level, string); - } - - if (sv.demorecording) { - DemoReliableWrite_Begin (dem_all, 0, strlen(string)+3); - MSG_WriteByte (demo.buf, svc_print); - MSG_WriteByte (demo.buf, level); - MSG_WriteString (demo.buf, string); - } -} - -/* -================= -SV_BroadcastCommand - -Sends text to all active clients -================= -*/ -void SV_BroadcastCommand (char *fmt, ...) -{ - va_list argptr; - char string[1024]; - - if (!sv.state) - return; - va_start (argptr,fmt); - vsprintf (string, fmt,argptr); - va_end (argptr); - - MSG_WriteByte (&sv.reliable_datagram, svc_stufftext); - MSG_WriteString (&sv.reliable_datagram, string); -} - - -/* -================= -SV_Multicast - -Sends the contents of sv.multicast to a subset of the clients, -then clears sv.multicast. - -MULTICAST_ALL same as broadcast -MULTICAST_PVS send to clients potentially visible from org -MULTICAST_PHS send to clients potentially hearable from org -================= -*/ -void SV_Multicast (vec3_t origin, int to) -{ - client_t *client; - byte *mask; - mleaf_t *leaf; - int leafnum; - int j, i, cls = 0; - qboolean reliable; - qboolean inrangetbl[MAX_CLIENTS]; // for multipov messages - - - leaf = Mod_PointInLeaf (origin, sv.worldmodel); - if (!leaf) - leafnum = 0; - else - leafnum = leaf - sv.worldmodel->leafs; - - reliable = false; - - switch (to) - { - case MULTICAST_ALL_R: - reliable = true; // intentional fallthrough - case MULTICAST_ALL: - mask = sv.pvs; // leaf 0 is everything; - break; - - case MULTICAST_PHS_R: - reliable = true; // intentional fallthrough - case MULTICAST_PHS: - mask = sv.phs + leafnum * 4*((sv.worldmodel->numleafs+31)>>5); - break; - - case MULTICAST_PVS_R: - reliable = true; // intentional fallthrough - case MULTICAST_PVS: - mask = sv.pvs + leafnum * 4*((sv.worldmodel->numleafs+31)>>5); - break; - - default: - mask = NULL; - SV_Error ("SV_Multicast: bad to:%i", to); - } - - // send the data to all relevent clients - memset(inrangetbl,0,sizeof(inrangetbl)); - for (j = 0, client = svs.clients; j < MAX_CLIENTS; j++, client++) - { - if (client->state != cs_spawned) - continue; - - if (to == MULTICAST_PHS_R || to == MULTICAST_PHS) { - vec3_t delta; - VectorSubtract(origin, client->edict->v.origin, delta); - if (Length(delta) <= 1024) - goto inrange; - } - - leaf = Mod_PointInLeaf (client->edict->v.origin, sv.worldmodel); - if (leaf) - { - // -1 is because pvs rows are 1 based, not 0 based like leafs - leafnum = leaf - sv.worldmodel->leafs - 1; - if ( !(mask[leafnum>>3] & (1<<(leafnum&7)) ) ) - { -// Con_Printf ("supressed multicast\n"); - continue; - } - } - -inrange: - inrangetbl[j] = true; - } - - // now we send it where it's needed - for (j = 0, client = svs.clients; j < MAX_CLIENTS; j++, client++) - { - if (!inrangetbl[j]) { - for ( i = 0; i < MAX_CLIENTS; i++ ) { - if (!inrangetbl[i]) - continue; - - if (client->POVs & (1 << i)) // "if i == j" do we need it ? - break; - } - - if (i == MAX_CLIENTS) - continue; - } else { - cls |= 1 << j; - } - - if (reliable) { - ClientReliableCheckBlock(client, sv.multicast.cursize); - ClientReliableWrite_SZ(client, sv.multicast.data, sv.multicast.cursize); - } else - SZ_Write (&client->datagram, sv.multicast.data, sv.multicast.cursize); - } - - if (sv.demorecording/* && cls*/) - { - if (reliable) - { - //DemoReliableWrite_Begin(dem_multiple, cls, sv.multicast.cursize); - DemoReliableWrite_Begin(dem_all, 0, sv.multicast.cursize); - SZ_Write(demo.buf, sv.multicast.data, sv.multicast.cursize); - } else - SZ_Write(&demo.datagram, sv.multicast.data, sv.multicast.cursize); - } - - - SZ_Clear (&sv.multicast); -} - - -/* -================== -SV_StartSound - -Each entity can have eight independant sound sources, like voice, -weapon, feet, etc. - -Channel 0 is an auto-allocate channel, the others override anything -already running on that entity/channel pair. - -An attenuation of 0 will play full volume everywhere in the level. -Larger attenuations will drop off. (max 4 attenuation) - -================== -*/ -void SV_StartSound (edict_t *entity, int channel, char *sample, int volume, - float attenuation) -{ - int sound_num; - int field_mask; - int i; - int ent; - vec3_t origin; - qboolean use_phs; - qboolean reliable = false; - - if (volume < 0 || volume > 255) - SV_Error ("SV_StartSound: volume = %i", volume); - - if (attenuation < 0 || attenuation > 4) - SV_Error ("SV_StartSound: attenuation = %f", attenuation); - - if (channel < 0 || channel > 15) - SV_Error ("SV_StartSound: channel = %i", channel); - -// find precache number for sound - for (sound_num=1 ; sound_numv.solid == SOLID_BSP) - { - for (i=0 ; i<3 ; i++) - origin[i] = entity->v.origin[i]+0.5*(entity->v.mins[i]+entity->v.maxs[i]); - } - else - { - VectorCopy (entity->v.origin, origin); - } - - MSG_WriteByte (&sv.multicast, svc_sound); - MSG_WriteShort (&sv.multicast, channel); - if (channel & SND_VOLUME) - MSG_WriteByte (&sv.multicast, volume); - if (channel & SND_ATTENUATION) - MSG_WriteByte (&sv.multicast, attenuation*64); - MSG_WriteByte (&sv.multicast, sound_num); - for (i=0 ; i<3 ; i++) - MSG_WriteCoord (&sv.multicast, origin[i]); - - if (use_phs) - SV_Multicast (origin, reliable ? MULTICAST_PHS_R : MULTICAST_PHS); - else - SV_Multicast (origin, reliable ? MULTICAST_ALL_R : MULTICAST_ALL); -} - - -/* -=============================================================================== - -FRAME UPDATES - -=============================================================================== -*/ - -int sv_nailmodel, sv_supernailmodel, sv_playermodel; - -void SV_FindModelNumbers (void) -{ - int i; - - sv_nailmodel = -1; - sv_supernailmodel = -1; - sv_playermodel = -1; - - for (i=0 ; iedict; - - clnum = NUM_FOR_EDICT(ent) - 1; - - // send the chokecount for r_netgraph - if (client->chokecount) - { - MSG_WriteByte (msg, svc_chokecount); - MSG_WriteByte (msg, client->chokecount); - client->chokecount = 0; - } - - // send a damage message if the player got hit this frame - if (ent->v.dmg_take || ent->v.dmg_save) - { - other = PROG_TO_EDICT(ent->v.dmg_inflictor); - MSG_WriteByte (msg, svc_damage); - MSG_WriteByte (msg, ent->v.dmg_save); - MSG_WriteByte (msg, ent->v.dmg_take); - for (i=0 ; i<3 ; i++) - MSG_WriteCoord (msg, other->v.origin[i] + 0.5*(other->v.mins[i] + other->v.maxs[i])); - - ent->v.dmg_take = 0; - ent->v.dmg_save = 0; - } - - // add this to server demo - if (sv.demorecording && msg->cursize) - { - DemoReliableWrite_Begin(dem_single, clnum, msg->cursize); - SZ_Write(demo.buf, msg->data, msg->cursize); - } - - // a fixangle might get lost in a dropped packet. Oh well. - if ( ent->v.fixangle) - { - MSG_WriteByte (msg, svc_setangle); - for (i=0 ; i < 3 ; i++) - MSG_WriteAngle (msg, ent->v.angles[i] ); - VectorCopy(ent->v.angles, demo.angles[clnum]); - ent->v.fixangle = 0; - demo.fixangle[clnum] = true; - - if (sv.demorecording) - { - MSG_WriteByte (&demo.datagram, svc_setangle); - MSG_WriteByte (&demo.datagram, clnum); - for (i=0 ; i < 3 ; i++) - MSG_WriteAngle (&demo.datagram, demo.angles[clnum][i] ); - } - } -} - -/* -======================= -SV_UpdateClientStats - -Performs a delta update of the stats array. This should only be performed -when a reliable message can be delivered this frame. -======================= -*/ -void SV_UpdateClientStats (client_t *client) -{ - edict_t *ent; - int stats[MAX_CL_STATS]; - int i; - - ent = client->edict; - memset (stats, 0, sizeof(stats)); - - // if we are a spectator and we are tracking a player, we get his stats - // so our status bar reflects his - if (client->spectator && client->spec_track > 0) - ent = svs.clients[client->spec_track - 1].edict; - - stats[STAT_HEALTH] = ent->v.health; - stats[STAT_WEAPON] = SV_ModelIndex(PR_GetString(ent->v.weaponmodel)); - stats[STAT_AMMO] = ent->v.currentammo; - stats[STAT_ARMOR] = ent->v.armorvalue; - stats[STAT_SHELLS] = ent->v.ammo_shells; - stats[STAT_NAILS] = ent->v.ammo_nails; - stats[STAT_ROCKETS] = ent->v.ammo_rockets; - stats[STAT_CELLS] = ent->v.ammo_cells; - if (!client->spectator) - stats[STAT_ACTIVEWEAPON] = ent->v.weapon; - // stuff the sigil bits into the high bits of items for sbar - stats[STAT_ITEMS] = (int)ent->v.items | ((int)pr_global_struct->serverflags << 28); - - for (i=0 ; istats[i]) - { - client->stats[i] = stats[i]; - if (stats[i] >=0 && stats[i] <= 255) - { - ClientReliableWrite_Begin(client, svc_updatestat, 3); - ClientReliableWrite_Byte(client, i); - ClientReliableWrite_Byte(client, stats[i]); - } - else - { - ClientReliableWrite_Begin(client, svc_updatestatlong, 6); - ClientReliableWrite_Byte(client, i); - ClientReliableWrite_Long(client, stats[i]); - } - } -} - -/* -======================= -SV_SendClientDatagram -======================= -*/ -qboolean SV_SendClientDatagram (client_t *client) -{ - byte buf[MAX_DATAGRAM]; - sizebuf_t msg; - - msg.data = buf; - msg.maxsize = sizeof(buf); - msg.cursize = 0; - msg.allowoverflow = true; - msg.overflowed = false; - - // add the client specific data to the datagram - SV_WriteClientdataToMessage (client, &msg); - - // send over all the objects that are in the PVS - // this will include clients, a packetentities, and - // possibly a nails update - SV_WriteEntitiesToClient (client, &msg, false); - - // copy the accumulated multicast datagram - // for this client out to the message - if (client->datagram.overflowed) - Con_Printf ("WARNING: datagram overflowed for %s\n", client->name); - else - SZ_Write (&msg, client->datagram.data, client->datagram.cursize); - SZ_Clear (&client->datagram); - - // send deltas over reliable stream - if (Netchan_CanReliable (&client->netchan)) - SV_UpdateClientStats (client); - - if (msg.overflowed) - { - Con_Printf ("WARNING: msg overflowed for %s\n", client->name); - SZ_Clear (&msg); - } - - // send the datagram - Netchan_Transmit (&client->netchan, msg.cursize, buf); - - return true; -} - -/* -======================= -SV_UpdateToReliableMessages -======================= -*/ -void SV_UpdateToReliableMessages (void) -{ - int i, j, cls; - client_t *client; - eval_t *val; - edict_t *ent; - -// check for changes to be sent over the reliable streams to all clients - for (i=0, host_client = svs.clients ; istate != cs_spawned) - continue; - cls |= 1 << i; - - if (host_client->sendinfo) - { - host_client->sendinfo = false; - SV_FullClientUpdate (host_client, &sv.reliable_datagram); - } - if (host_client->old_frags != host_client->edict->v.frags) - { - cls = 0; - for (j=0, client = svs.clients ; jstate < cs_connected) - continue; - ClientReliableWrite_Begin(client, svc_updatefrags, 4); - ClientReliableWrite_Byte(client, i); - ClientReliableWrite_Short(client, host_client->edict->v.frags); - } - - if (sv.demorecording) { - DemoReliableWrite_Begin(dem_all, 0, 4); - MSG_WriteByte(demo.buf, svc_updatefrags); - MSG_WriteByte(demo.buf, i); - MSG_WriteShort(demo.buf, host_client->edict->v.frags); - } - - host_client->old_frags = host_client->edict->v.frags; - } - - // maxspeed/entgravity changes - ent = host_client->edict; - - val = GetEdictFieldValue(ent, "gravity"); - if (val && host_client->entgravity != val->_float) { - host_client->entgravity = val->_float; - ClientReliableWrite_Begin(host_client, svc_entgravity, 5); - ClientReliableWrite_Float(host_client, host_client->entgravity); - if (sv.demorecording) - { - DemoReliableWrite_Begin(dem_single, i, 5); - MSG_WriteByte(demo.buf, svc_entgravity); - MSG_WriteFloat(demo.buf, host_client->entgravity); - } - } - val = GetEdictFieldValue(ent, "maxspeed"); - if (val && host_client->maxspeed != val->_float) { - host_client->maxspeed = val->_float; - ClientReliableWrite_Begin(host_client, svc_maxspeed, 5); - ClientReliableWrite_Float(host_client, host_client->maxspeed); - if (sv.demorecording) - { - DemoReliableWrite_Begin(dem_single, i, 5); - MSG_WriteByte(demo.buf, svc_maxspeed); - MSG_WriteFloat(demo.buf, host_client->maxspeed); - } - } - - } - - if (sv.datagram.overflowed) - SZ_Clear (&sv.datagram); - - // append the broadcast messages to each client messages - cls = 0; - for (j=0, client = svs.clients ; jstate < cs_connected) - continue; // reliables go to all connected or spawned - - ClientReliableCheckBlock(client, sv.reliable_datagram.cursize); - ClientReliableWrite_SZ(client, sv.reliable_datagram.data, sv.reliable_datagram.cursize); - - if (client->state != cs_spawned) - continue; // datagrams only go to spawned - - SZ_Write (&client->datagram - , sv.datagram.data - , sv.datagram.cursize); - } - - if (sv.demorecording && sv.reliable_datagram.cursize) { - DemoReliableWrite_Begin(dem_all, 0, sv.reliable_datagram.cursize); - SZ_Write(demo.buf, sv.reliable_datagram.data, sv.reliable_datagram.cursize); - } - - if (sv.demorecording) - SZ_Write(&demo.datagram, sv.datagram.data, sv.datagram.cursize); // FIZME: ??? - - SZ_Clear (&sv.reliable_datagram); - SZ_Clear (&sv.datagram); -} - -#ifdef _WIN32 -#pragma optimize( "", off ) -#endif - - - -/* -======================= -SV_SendClientMessages -======================= -*/ -void SV_SendClientMessages (void) -{ - int i, j; - client_t *c; - -// update frags, names, etc - SV_UpdateToReliableMessages (); - -// build individual updates - for (i=0, c = svs.clients ; istate) - continue; - - if (c->drop) { - SV_DropClient(c); - c->drop = false; - continue; - } - - // check to see if we have a backbuf to stick in the reliable - if (c->num_backbuf) { - // will it fit? - if (c->netchan.message.cursize + c->backbuf_size[0] < - c->netchan.message.maxsize) { - - Con_DPrintf("%s: backbuf %d bytes\n", - c->name, c->backbuf_size[0]); - - // it'll fit - SZ_Write(&c->netchan.message, c->backbuf_data[0], - c->backbuf_size[0]); - - //move along, move along - for (j = 1; j < c->num_backbuf; j++) { - memcpy(c->backbuf_data[j - 1], c->backbuf_data[j], - c->backbuf_size[j]); - c->backbuf_size[j - 1] = c->backbuf_size[j]; - } - - c->num_backbuf--; - if (c->num_backbuf) { - memset(&c->backbuf, 0, sizeof(c->backbuf)); - c->backbuf.data = c->backbuf_data[c->num_backbuf - 1]; - c->backbuf.cursize = c->backbuf_size[c->num_backbuf - 1]; - c->backbuf.maxsize = sizeof(c->backbuf_data[c->num_backbuf - 1]); - } - } - } - - // if the reliable message overflowed, - // drop the client - if (c->netchan.message.overflowed) - { - SZ_Clear (&c->netchan.message); - SZ_Clear (&c->datagram); - SV_BroadcastPrintf (PRINT_HIGH, "%s overflowed\n", c->name); - Con_Printf ("WARNING: reliable overflow for %s\n",c->name); - SV_DropClient (c); - c->send_message = true; - c->netchan.cleartime = 0; // don't choke this message - } - - // only send messages if the client has sent one - // and the bandwidth is not choked - if (!c->send_message) - continue; - c->send_message = false; // try putting this after choke? - if (!sv.paused && !Netchan_CanPacket (&c->netchan)) - { - c->chokecount++; - continue; // bandwidth choke - } - - if (c->state == cs_spawned) - SV_SendClientDatagram (c); - else - Netchan_Transmit (&c->netchan, 0, NULL); // just update reliable - - } -} - -#ifndef max -#define max(a,b) (((a) > (b)) ? (a) : (b)) -#define min(a,b) (((a) < (b)) ? (a) : (b)) -#endif - -void SV_SendDemoMessage(void) -{ - int i, j, cls = 0; - client_t *c; - byte buf[MAX_DATAGRAM]; - sizebuf_t msg; - edict_t *ent; - int stats[MAX_CL_STATS]; - float min_fps; - extern cvar_t sv_demofps; - extern cvar_t sv_demoPings; - extern cvar_t sv_demoMaxSize; - demo_frame_t *frame; - - if (!sv.demorecording) - return; - - if (sv_demoPings.value) - { - if (sv.time - demo.pingtime > sv_demoPings.value) - { - SV_DemoPings(); - demo.pingtime = sv.time; - } - } - - - if (!sv_demofps.value) - min_fps = 30.0; - else - min_fps = sv_demofps.value; - - min_fps = max(4, min_fps); - if (sv.time - demo.time < 1.0/min_fps) - return; - - for (i=0, c = svs.clients ; istate != cs_spawned) - continue; // datagrams only go to spawned - - cls |= 1 << i; - } - - if (!cls) { - SZ_Clear (&demo.datagram); - return; - } - - msg.data = buf; - msg.maxsize = sizeof(buf); - msg.cursize = 0; - msg.allowoverflow = true; - msg.overflowed = false; - - for (i=0, c = svs.clients ; istate != cs_spawned) - continue; // datagrams only go to spawned - - if (c->spectator) - continue; - - ent = c->edict; - memset (stats, 0, sizeof(stats)); - - stats[STAT_HEALTH] = ent->v.health; - stats[STAT_WEAPON] = SV_ModelIndex(PR_GetString(ent->v.weaponmodel)); - stats[STAT_AMMO] = ent->v.currentammo; - stats[STAT_ARMOR] = ent->v.armorvalue; - stats[STAT_SHELLS] = ent->v.ammo_shells; - stats[STAT_NAILS] = ent->v.ammo_nails; - stats[STAT_ROCKETS] = ent->v.ammo_rockets; - stats[STAT_CELLS] = ent->v.ammo_cells; - stats[STAT_ACTIVEWEAPON] = ent->v.weapon; - - - // stuff the sigil bits into the high bits of items for sbar - stats[STAT_ITEMS] = (int)ent->v.items | ((int)pr_global_struct->serverflags << 28); - - for (j=0 ; j=0 && stats[j] <= 255) - { - DemoReliableWrite_Begin(dem_stats, i, 3); - MSG_WriteByte(demo.buf, svc_updatestat); - MSG_WriteByte(demo.buf, j); - MSG_WriteByte(demo.buf, stats[j]); - } - else - { - DemoReliableWrite_Begin(dem_stats, i, 6); - MSG_WriteByte(demo.buf, svc_updatestatlong); - MSG_WriteByte(demo.buf, j); - MSG_WriteLong(demo.buf, stats[j]); - } - } - } - - // send over all the objects that are in the PVS - // this will include clients, a packetentities, and - // possibly a nails update - msg.cursize = 0; - demo.recorder.POVs = ~0; - if (!demo.recorder.delta_sequence) - demo.recorder.delta_sequence = -1; - SV_WriteEntitiesToClient (&demo.recorder, &msg, true); - DemoReliableWrite_Begin(dem_all, 0, msg.cursize); - SZ_Write (demo.buf, msg.data, msg.cursize); - // copy the accumulated multicast datagram - // for this client out to the message - if (demo.datagram.cursize) { - DemoReliableWrite_Begin(dem_all, 0, demo.datagram.cursize); - SZ_Write (demo.buf, demo.datagram.data, demo.datagram.cursize); - SZ_Clear (&demo.datagram); - } - - demo.recorder.delta_sequence = demo.recorder.netchan.incoming_sequence&255; - demo.recorder.netchan.incoming_sequence++; - demo.frames[demo.parsecount&DEMO_FRAMES_MASK].time = demo.time = sv.time; - demo.parsecount++; - - if (demo.parsecount - demo.lastwritten > 60) // that's a backup of 3sec in 20fps, should be enough - { - SV_DemoWritePackets(1); - } - - demo.dbuf = &demo.frames[demo.parsecount&DEMO_FRAMES_MASK].buf; - demo.dbuf->data = demo.buffer + demo.bufsize; - demo.dbuf->maxsize = sizeof(demo.buffer) - demo.bufsize; - - demo.dbuf->msgsize = 0; - demo.dbuf->size = 0; - demo.dbuf->curto = 0; - demo.dbuf->curtype = 0; - demo.dbuf->cursize = 0; - - if (sv_demoMaxSize.value && demo.size > sv_demoMaxSize.value*1024) - SV_Stop(1); -} - - -#ifdef _WIN32 -#pragma optimize( "", on ) -#endif - - - -/* -======================= -SV_SendMessagesToAll - -FIXME: does this sequence right? -======================= -*/ -void SV_SendMessagesToAll (void) -{ - int i; - client_t *c; - - for (i=0, c = svs.clients ; istate) // FIXME: should this only send to active? - c->send_message = true; - - SV_SendClientMessages (); -} - +/* +Copyright (C) 1996-1997 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 "qwsvdef.h" + +#define CHAN_AUTO 0 +#define CHAN_WEAPON 1 +#define CHAN_VOICE 2 +#define CHAN_ITEM 3 +#define CHAN_BODY 4 + +/* +============================================================================= + +Con_Printf redirection + +============================================================================= +*/ + +#define MAX_REDIRECTMESSAGES 4 +char outputbuf[8000]; + +redirect_t sv_redirected; +static int sv_redirectbufcount; + +extern cvar_t sv_phs; + +/* +================== +SV_FlushRedirect +================== +*/ +void SV_FlushRedirect (void) +{ + char send[8000+6]; + + if (sv_redirected == RD_PACKET) + { + send[0] = 0xff; + send[1] = 0xff; + send[2] = 0xff; + send[3] = 0xff; + send[4] = A2C_PRINT; + memcpy (send+5, outputbuf, strlen(outputbuf)+1); + + NET_SendPacket (net_serversocket, strlen(send)+1, send, net_from); + } + else if (sv_redirected == RD_CLIENT && sv_redirectbufcount < MAX_REDIRECTMESSAGES) + { + ClientReliableWrite_Begin (host_client, svc_print, strlen(outputbuf)+3); + ClientReliableWrite_Byte (host_client, PRINT_HIGH); + ClientReliableWrite_String (host_client, outputbuf); + sv_redirectbufcount++; + } else if (sv_redirected == RD_MOD) { + //return; + } else if (sv_redirected > RD_MOD && sv_redirectbufcount < MAX_REDIRECTMESSAGES) + { + client_t *cl; + + cl = svs.clients + sv_redirected - RD_MOD - 1; + + if (cl->state == cs_spawned) + { + ClientReliableWrite_Begin (cl, svc_print, strlen(outputbuf)+3); + ClientReliableWrite_Byte (cl, PRINT_HIGH); + ClientReliableWrite_String (cl, outputbuf); + sv_redirectbufcount++; + } + } + + // clear it + outputbuf[0] = 0; +} + + +/* +================== +SV_BeginRedirect + + Send Con_Printf data to the remote client + instead of the console +================== +*/ +void SV_BeginRedirect (redirect_t rd) +{ + sv_redirected = rd; + outputbuf[0] = 0; + sv_redirectbufcount = 0; + +} + +void SV_EndRedirect (void) +{ + SV_FlushRedirect (); + sv_redirected = RD_NONE; +} + + + +/* +================ +Con_Printf + +Handles cursor positioning, line wrapping, etc +================ +*/ +#define MAXPRINTMSG 4096 +// FIXME: make a buffer size safe vsprintf? +void Con_Printf (char *fmt, ...) +{ + va_list argptr; + char msg[MAXPRINTMSG]; + + va_start (argptr,fmt); + vsprintf (msg,fmt,argptr); + va_end (argptr); + + // add to redirected message + if (sv_redirected) + { + if (strlen (msg) + strlen(outputbuf) > /*sizeof(outputbuf) - 1*/ MAX_MSGLEN - 10) + SV_FlushRedirect (); + strcat (outputbuf, msg); + return; + } + + Sys_Printf ("%s", msg); // also echo to debugging console + if (sv_logfile) + fprintf (sv_logfile, "%s", msg); + + // dumb error message to log file if + if (sv_error && sv_errorlogfile) + fprintf(sv_errorlogfile, "%s", msg); +} + +/* +================ +Con_DPrintf + +A Con_Printf that only shows up if the "developer" cvar is set +================ +*/ +void Con_DPrintf (char *fmt, ...) +{ + va_list argptr; + char msg[MAXPRINTMSG]; + + if (!developer.value) + return; + + va_start (argptr,fmt); + vsprintf (msg,fmt,argptr); + va_end (argptr); + + Con_Printf ("%s", msg); +} + +/* +============================================================================= + +EVENT MESSAGES + +============================================================================= +*/ + +static void SV_PrintToClient(client_t *cl, int level, char *string) +{ + ClientReliableWrite_Begin (cl, svc_print, strlen(string)+3); + ClientReliableWrite_Byte (cl, level); + ClientReliableWrite_String (cl, string); +} + + +/* +================= +SV_ClientPrintf + +Sends text across to be displayed if the level passes +================= +*/ +void SV_ClientPrintf (client_t *cl, int level, char *fmt, ...) +{ + va_list argptr; + char string[1024]; + + if (level < cl->messagelevel) + return; + + va_start (argptr,fmt); + vsprintf (string, fmt,argptr); + va_end (argptr); + + if (sv.demorecording) { + + DemoWrite_Begin (dem_single, cl - svs.clients, strlen(string)+3); + MSG_WriteByte ((sizebuf_t*)demo.dbuf, svc_print); + MSG_WriteByte ((sizebuf_t*)demo.dbuf, level); + MSG_WriteString ((sizebuf_t*)demo.dbuf, string); + } + + SV_PrintToClient(cl, level, string); +} + +void SV_ClientPrintf2 (client_t *cl, int level, char *fmt, ...) +{ + va_list argptr; + char string[1024]; + + if (level < cl->messagelevel) + return; + + va_start (argptr,fmt); + vsprintf (string, fmt,argptr); + va_end (argptr); + + SV_PrintToClient(cl, level, string); +} + + +/* +================= +SV_BroadcastPrintf + +Sends text to all active clients +================= +*/ +void SV_BroadcastPrintf (int level, char *fmt, ...) +{ + va_list argptr; + char string[1024]; + client_t *cl; + int i; + + va_start (argptr,fmt); + vsprintf (string, fmt,argptr); + va_end (argptr); + + Sys_Printf ("%s", string); // print to the console + + for (i=0, cl = svs.clients ; imessagelevel) + continue; + if (!cl->state) + continue; + + SV_PrintToClient(cl, level, string); + } + + if (sv.demorecording) { + DemoWrite_Begin (dem_all, 0, strlen(string)+3); + MSG_WriteByte ((sizebuf_t*)demo.dbuf, svc_print); + MSG_WriteByte ((sizebuf_t*)demo.dbuf, level); + MSG_WriteString ((sizebuf_t*)demo.dbuf, string); + } +} + +/* +================= +SV_BroadcastCommand + +Sends text to all active clients +================= +*/ +void SV_BroadcastCommand (char *fmt, ...) +{ + va_list argptr; + char string[1024]; + + if (!sv.state) + return; + va_start (argptr,fmt); + vsprintf (string, fmt,argptr); + va_end (argptr); + + MSG_WriteByte (&sv.reliable_datagram, svc_stufftext); + MSG_WriteString (&sv.reliable_datagram, string); +} + + +/* +================= +SV_Multicast + +Sends the contents of sv.multicast to a subset of the clients, +then clears sv.multicast. + +MULTICAST_ALL same as broadcast +MULTICAST_PVS send to clients potentially visible from org +MULTICAST_PHS send to clients potentially hearable from org +================= +*/ +void SV_Multicast (vec3_t origin, int to) +{ + client_t *client; + byte *mask; + mleaf_t *leaf; + int leafnum; + int j; + qboolean reliable; + + leaf = Mod_PointInLeaf (origin, sv.worldmodel); + if (!leaf) + leafnum = 0; + else + leafnum = leaf - sv.worldmodel->leafs; + + reliable = false; + + switch (to) + { + case MULTICAST_ALL_R: + reliable = true; // intentional fallthrough + case MULTICAST_ALL: + mask = sv.pvs; // leaf 0 is everything; + break; + + case MULTICAST_PHS_R: + reliable = true; // intentional fallthrough + case MULTICAST_PHS: + mask = sv.phs + leafnum * 4*((sv.worldmodel->numleafs+31)>>5); + break; + + case MULTICAST_PVS_R: + reliable = true; // intentional fallthrough + case MULTICAST_PVS: + mask = sv.pvs + leafnum * 4*((sv.worldmodel->numleafs+31)>>5); + break; + + default: + mask = NULL; + SV_Error ("SV_Multicast: bad to:%i", to); + } + + // send the data to all relevent clients + for (j = 0, client = svs.clients; j < MAX_CLIENTS; j++, client++) + { + if (client->state != cs_spawned) + continue; + + if (to == MULTICAST_PHS_R || to == MULTICAST_PHS) { + vec3_t delta; + VectorSubtract(origin, client->edict->v.origin, delta); + if (Length(delta) <= 1024) + goto inrange; + } + + leaf = Mod_PointInLeaf (client->edict->v.origin, sv.worldmodel); + if (leaf) + { + // -1 is because pvs rows are 1 based, not 0 based like leafs + leafnum = leaf - sv.worldmodel->leafs - 1; + if ( !(mask[leafnum>>3] & (1<<(leafnum&7)) ) ) + { +// Con_Printf ("supressed multicast\n"); + continue; + } + } + +inrange: + if (reliable) { + ClientReliableCheckBlock(client, sv.multicast.cursize); + ClientReliableWrite_SZ(client, sv.multicast.data, sv.multicast.cursize); + } else + SZ_Write (&client->datagram, sv.multicast.data, sv.multicast.cursize); + } + + if (sv.demorecording) + { + if (reliable) + { + //DemoWrite_Begin(dem_multiple, cls, sv.multicast.cursize); + DemoWrite_Begin(dem_all, 0, sv.multicast.cursize); + SZ_Write((sizebuf_t*)demo.dbuf, sv.multicast.data, sv.multicast.cursize); + } else + SZ_Write(&demo.datagram, sv.multicast.data, sv.multicast.cursize); + } + + + SZ_Clear (&sv.multicast); +} + + +/* +================== +SV_StartSound + +Each entity can have eight independant sound sources, like voice, +weapon, feet, etc. + +Channel 0 is an auto-allocate channel, the others override anything +already running on that entity/channel pair. + +An attenuation of 0 will play full volume everywhere in the level. +Larger attenuations will drop off. (max 4 attenuation) + +================== +*/ +void SV_StartSound (edict_t *entity, int channel, char *sample, int volume, + float attenuation) +{ + int sound_num; + int field_mask; + int i; + int ent; + vec3_t origin; + qboolean use_phs; + qboolean reliable = false; + + if (volume < 0 || volume > 255) + SV_Error ("SV_StartSound: volume = %i", volume); + + if (attenuation < 0 || attenuation > 4) + SV_Error ("SV_StartSound: attenuation = %f", attenuation); + + if (channel < 0 || channel > 15) + SV_Error ("SV_StartSound: channel = %i", channel); + +// find precache number for sound + for (sound_num=1 ; sound_numv.solid == SOLID_BSP) + { + for (i=0 ; i<3 ; i++) + origin[i] = entity->v.origin[i]+0.5*(entity->v.mins[i]+entity->v.maxs[i]); + } + else + { + VectorCopy (entity->v.origin, origin); + } + + MSG_WriteByte (&sv.multicast, svc_sound); + MSG_WriteShort (&sv.multicast, channel); + if (channel & SND_VOLUME) + MSG_WriteByte (&sv.multicast, volume); + if (channel & SND_ATTENUATION) + MSG_WriteByte (&sv.multicast, attenuation*64); + MSG_WriteByte (&sv.multicast, sound_num); + for (i=0 ; i<3 ; i++) + MSG_WriteCoord (&sv.multicast, origin[i]); + + if (use_phs) + SV_Multicast (origin, reliable ? MULTICAST_PHS_R : MULTICAST_PHS); + else + SV_Multicast (origin, reliable ? MULTICAST_ALL_R : MULTICAST_ALL); +} + + +/* +=============================================================================== + +FRAME UPDATES + +=============================================================================== +*/ + +int sv_nailmodel, sv_supernailmodel, sv_playermodel; + +void SV_FindModelNumbers (void) +{ + int i; + + sv_nailmodel = -1; + sv_supernailmodel = -1; + sv_playermodel = -1; + + for (i=0 ; iedict; + + clnum = NUM_FOR_EDICT(ent) - 1; + + // send the chokecount for r_netgraph + if (client->chokecount) + { + MSG_WriteByte (msg, svc_chokecount); + MSG_WriteByte (msg, client->chokecount); + client->chokecount = 0; + } + + // send a damage message if the player got hit this frame + if (ent->v.dmg_take || ent->v.dmg_save) + { + other = PROG_TO_EDICT(ent->v.dmg_inflictor); + MSG_WriteByte (msg, svc_damage); + MSG_WriteByte (msg, ent->v.dmg_save); + MSG_WriteByte (msg, ent->v.dmg_take); + for (i=0 ; i<3 ; i++) + MSG_WriteCoord (msg, other->v.origin[i] + 0.5*(other->v.mins[i] + other->v.maxs[i])); + + ent->v.dmg_take = 0; + ent->v.dmg_save = 0; + } + + // add this to server demo + if (sv.demorecording && msg->cursize) + { + DemoWrite_Begin(dem_single, clnum, msg->cursize); + SZ_Write((sizebuf_t*)demo.dbuf, msg->data, msg->cursize); + } + + // a fixangle might get lost in a dropped packet. Oh well. + if ( ent->v.fixangle) + { + MSG_WriteByte (msg, svc_setangle); + for (i=0 ; i < 3 ; i++) + MSG_WriteAngle (msg, ent->v.angles[i] ); + VectorCopy(ent->v.angles, demo.angles[clnum]); + ent->v.fixangle = 0; + demo.fixangle[clnum] = true; + + if (sv.demorecording) + { + MSG_WriteByte (&demo.datagram, svc_setangle); + MSG_WriteByte (&demo.datagram, clnum); + for (i=0 ; i < 3 ; i++) + MSG_WriteAngle (&demo.datagram, demo.angles[clnum][i] ); + } + } +} + +/* +======================= +SV_UpdateClientStats + +Performs a delta update of the stats array. This should only be performed +when a reliable message can be delivered this frame. +======================= +*/ +void SV_UpdateClientStats (client_t *client) +{ + edict_t *ent; + int stats[MAX_CL_STATS]; + int i; + + ent = client->edict; + memset (stats, 0, sizeof(stats)); + + // if we are a spectator and we are tracking a player, we get his stats + // so our status bar reflects his + if (client->spectator && client->spec_track > 0) + ent = svs.clients[client->spec_track - 1].edict; + + stats[STAT_HEALTH] = ent->v.health; + stats[STAT_WEAPON] = SV_ModelIndex(PR_GetString(ent->v.weaponmodel)); + stats[STAT_AMMO] = ent->v.currentammo; + stats[STAT_ARMOR] = ent->v.armorvalue; + stats[STAT_SHELLS] = ent->v.ammo_shells; + stats[STAT_NAILS] = ent->v.ammo_nails; + stats[STAT_ROCKETS] = ent->v.ammo_rockets; + stats[STAT_CELLS] = ent->v.ammo_cells; + if (!client->spectator) + stats[STAT_ACTIVEWEAPON] = ent->v.weapon; + // stuff the sigil bits into the high bits of items for sbar + stats[STAT_ITEMS] = (int)ent->v.items | ((int)pr_global_struct->serverflags << 28); + + for (i=0 ; istats[i]) + { + client->stats[i] = stats[i]; + if (stats[i] >=0 && stats[i] <= 255) + { + ClientReliableWrite_Begin(client, svc_updatestat, 3); + ClientReliableWrite_Byte(client, i); + ClientReliableWrite_Byte(client, stats[i]); + } + else + { + ClientReliableWrite_Begin(client, svc_updatestatlong, 6); + ClientReliableWrite_Byte(client, i); + ClientReliableWrite_Long(client, stats[i]); + } + } +} + +/* +======================= +SV_SendClientDatagram +======================= +*/ +qboolean SV_SendClientDatagram (client_t *client) +{ + byte buf[MAX_DATAGRAM]; + sizebuf_t msg; + + msg.data = buf; + msg.maxsize = sizeof(buf); + msg.cursize = 0; + msg.allowoverflow = true; + msg.overflowed = false; + + // add the client specific data to the datagram + SV_WriteClientdataToMessage (client, &msg); + + // send over all the objects that are in the PVS + // this will include clients, a packetentities, and + // possibly a nails update + SV_WriteEntitiesToClient (client, &msg, false); + + // copy the accumulated multicast datagram + // for this client out to the message + if (client->datagram.overflowed) + Con_Printf ("WARNING: datagram overflowed for %s\n", client->name); + else + SZ_Write (&msg, client->datagram.data, client->datagram.cursize); + SZ_Clear (&client->datagram); + + // send deltas over reliable stream + if (Netchan_CanReliable (&client->netchan)) + SV_UpdateClientStats (client); + + /*if (!client->realip.ip[0] && realtime - client->realip_time > 5) { // still haven't received packet with real ip + char str[128]; + + sprintf(str,"packet %s \"ip %d %d\"\n", NET_AdrToString(net_local_adr), svs.clients - client, client->realip_num); + + client->realip_time = realtime; + ClientReliableWrite_Begin(client, svc_stufftext, 2 + strlen(str)); + ClientReliableWrite_String (client, str); + } + */ + + if (msg.overflowed) + { + Con_Printf ("WARNING: msg overflowed for %s\n", client->name); + SZ_Clear (&msg); + } + + // send the datagram + Netchan_Transmit (&client->netchan, msg.cursize, buf); + + return true; +} + +/* +======================= +SV_UpdateToReliableMessages +======================= +*/ +void SV_UpdateToReliableMessages (void) +{ + int i, j, cls; + client_t *client; + eval_t *val; + edict_t *ent; + +// check for changes to be sent over the reliable streams to all clients + for (i=0, host_client = svs.clients ; istate != cs_spawned) + continue; + cls |= 1 << i; + + if (host_client->sendinfo) + { + host_client->sendinfo = false; + SV_FullClientUpdate (host_client, &sv.reliable_datagram); + } + if (host_client->old_frags != host_client->edict->v.frags) + { + cls = 0; + for (j=0, client = svs.clients ; jstate < cs_preconnected) + continue; + ClientReliableWrite_Begin(client, svc_updatefrags, 4); + ClientReliableWrite_Byte(client, i); + ClientReliableWrite_Short(client, host_client->edict->v.frags); + } + + if (sv.demorecording) { + DemoWrite_Begin(dem_all, 0, 4); + MSG_WriteByte((sizebuf_t*)demo.dbuf, svc_updatefrags); + MSG_WriteByte((sizebuf_t*)demo.dbuf, i); + MSG_WriteShort((sizebuf_t*)demo.dbuf, host_client->edict->v.frags); + } + + host_client->old_frags = host_client->edict->v.frags; + } + + // maxspeed/entgravity changes + ent = host_client->edict; + + val = GetEdictFieldValue(ent, "gravity"); + if (val && host_client->entgravity != val->_float) { + host_client->entgravity = val->_float; + ClientReliableWrite_Begin(host_client, svc_entgravity, 5); + ClientReliableWrite_Float(host_client, host_client->entgravity); + if (sv.demorecording) + { + DemoWrite_Begin(dem_single, i, 5); + MSG_WriteByte((sizebuf_t*)demo.dbuf, svc_entgravity); + MSG_WriteFloat((sizebuf_t*)demo.dbuf, host_client->entgravity); + } + } + val = GetEdictFieldValue(ent, "maxspeed"); + if (val && host_client->maxspeed != val->_float) { + host_client->maxspeed = val->_float; + ClientReliableWrite_Begin(host_client, svc_maxspeed, 5); + ClientReliableWrite_Float(host_client, host_client->maxspeed); + if (sv.demorecording) + { + DemoWrite_Begin(dem_single, i, 5); + MSG_WriteByte((sizebuf_t*)demo.dbuf, svc_maxspeed); + MSG_WriteFloat((sizebuf_t*)demo.dbuf, host_client->maxspeed); + } + } + + } + + if (sv.datagram.overflowed) + SZ_Clear (&sv.datagram); + + // append the broadcast messages to each client messages + cls = 0; + for (j=0, client = svs.clients ; jstate < cs_preconnected) + continue; // reliables go to all connected or spawned + + ClientReliableCheckBlock(client, sv.reliable_datagram.cursize); + ClientReliableWrite_SZ(client, sv.reliable_datagram.data, sv.reliable_datagram.cursize); + + if (client->state != cs_spawned) + continue; // datagrams only go to spawned + + SZ_Write (&client->datagram + , sv.datagram.data + , sv.datagram.cursize); + } + + if (sv.demorecording && sv.reliable_datagram.cursize) { + DemoWrite_Begin(dem_all, 0, sv.reliable_datagram.cursize); + SZ_Write((sizebuf_t*)demo.dbuf, sv.reliable_datagram.data, sv.reliable_datagram.cursize); + } + + if (sv.demorecording) + SZ_Write(&demo.datagram, sv.datagram.data, sv.datagram.cursize); // FIZME: ??? + + SZ_Clear (&sv.reliable_datagram); + SZ_Clear (&sv.datagram); +} + +#ifdef _WIN32 +#pragma optimize( "", off ) +#endif + + + +/* +======================= +SV_SendClientMessages +======================= +*/ +void SV_SendClientMessages (void) +{ + int i, j; + client_t *c; + +// update frags, names, etc + SV_UpdateToReliableMessages (); + +// build individual updates + for (i=0, c = svs.clients ; istate) + continue; + + if (c->drop) { + SV_DropClient(c); + c->drop = false; + continue; + } + + // check to see if we have a backbuf to stick in the reliable + if (c->num_backbuf) { + // will it fit? + if (c->netchan.message.cursize + c->backbuf_size[0] < + c->netchan.message.maxsize) { + + Con_DPrintf("%s: backbuf %d bytes\n", + c->name, c->backbuf_size[0]); + + // it'll fit + SZ_Write(&c->netchan.message, c->backbuf_data[0], + c->backbuf_size[0]); + + //move along, move along + for (j = 1; j < c->num_backbuf; j++) { + memcpy(c->backbuf_data[j - 1], c->backbuf_data[j], + c->backbuf_size[j]); + c->backbuf_size[j - 1] = c->backbuf_size[j]; + } + + c->num_backbuf--; + if (c->num_backbuf) { + memset(&c->backbuf, 0, sizeof(c->backbuf)); + c->backbuf.data = c->backbuf_data[c->num_backbuf - 1]; + c->backbuf.cursize = c->backbuf_size[c->num_backbuf - 1]; + c->backbuf.maxsize = sizeof(c->backbuf_data[c->num_backbuf - 1]); + } + } + } + + // if the reliable message overflowed, + // drop the client + if (c->netchan.message.overflowed) + { + SZ_Clear (&c->netchan.message); + SZ_Clear (&c->datagram); + SV_BroadcastPrintf (PRINT_HIGH, "%s overflowed\n", c->name); + Con_Printf ("WARNING: reliable overflow for %s\n",c->name); + SV_DropClient (c); + c->send_message = true; + c->netchan.cleartime = 0; // don't choke this message + } + + // only send messages if the client has sent one + // and the bandwidth is not choked + if (!c->send_message) + continue; + c->send_message = false; // try putting this after choke? + if (!sv.paused && !Netchan_CanPacket (&c->netchan)) + { + c->chokecount++; + continue; // bandwidth choke + } + + if (c->state == cs_spawned) + SV_SendClientDatagram (c); + else + Netchan_Transmit (&c->netchan, 0, NULL); // just update reliable + + } +} + +#ifndef max +#define max(a,b) (((a) > (b)) ? (a) : (b)) +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +void SV_SendDemoMessage(void) +{ + int i, j, cls = 0; + client_t *c; + byte buf[MAX_DATAGRAM]; + sizebuf_t msg; + edict_t *ent; + int stats[MAX_CL_STATS]; + float min_fps; + extern cvar_t sv_demofps; + extern cvar_t sv_demoPings; + extern cvar_t sv_demoMaxSize; + + if (!sv.demorecording) + return; + + if (sv_demoPings.value) + { + if (sv.time - demo.pingtime > sv_demoPings.value) + { + SV_DemoPings(); + demo.pingtime = sv.time; + } + } + + + if (!sv_demofps.value) + min_fps = 20.0; + else + min_fps = sv_demofps.value; + + min_fps = max(4, min_fps); + if (sv.time - demo.time < 1.0/min_fps) + return; + + for (i=0, c = svs.clients ; istate != cs_spawned) + continue; // datagrams only go to spawned + + cls |= 1 << i; + } + + if (!cls) { + SZ_Clear (&demo.datagram); + return; + } + + msg.data = buf; + msg.maxsize = sizeof(buf); + msg.cursize = 0; + msg.allowoverflow = true; + msg.overflowed = false; + + for (i=0, c = svs.clients ; istate != cs_spawned) + continue; // datagrams only go to spawned + + if (c->spectator) + continue; + + ent = c->edict; + memset (stats, 0, sizeof(stats)); + + stats[STAT_HEALTH] = ent->v.health; + stats[STAT_WEAPON] = SV_ModelIndex(PR_GetString(ent->v.weaponmodel)); + stats[STAT_AMMO] = ent->v.currentammo; + stats[STAT_ARMOR] = ent->v.armorvalue; + stats[STAT_SHELLS] = ent->v.ammo_shells; + stats[STAT_NAILS] = ent->v.ammo_nails; + stats[STAT_ROCKETS] = ent->v.ammo_rockets; + stats[STAT_CELLS] = ent->v.ammo_cells; + stats[STAT_ACTIVEWEAPON] = ent->v.weapon; + + + // stuff the sigil bits into the high bits of items for sbar + stats[STAT_ITEMS] = (int)ent->v.items | ((int)pr_global_struct->serverflags << 28); + + for (j=0 ; j=0 && stats[j] <= 255) + { + DemoWrite_Begin(dem_stats, i, 3); + MSG_WriteByte((sizebuf_t*)demo.dbuf, svc_updatestat); + MSG_WriteByte((sizebuf_t*)demo.dbuf, j); + MSG_WriteByte((sizebuf_t*)demo.dbuf, stats[j]); + } + else + { + DemoWrite_Begin(dem_stats, i, 6); + MSG_WriteByte((sizebuf_t*)demo.dbuf, svc_updatestatlong); + MSG_WriteByte((sizebuf_t*)demo.dbuf, j); + MSG_WriteLong((sizebuf_t*)demo.dbuf, stats[j]); + } + } + } + + // send over all the objects that are in the PVS + // this will include clients, a packetentities, and + // possibly a nails update + msg.cursize = 0; + if (!demo.recorder.delta_sequence) + demo.recorder.delta_sequence = -1; + SV_WriteEntitiesToClient (&demo.recorder, &msg, true); + DemoWrite_Begin(dem_all, 0, msg.cursize); + SZ_Write ((sizebuf_t*)demo.dbuf, msg.data, msg.cursize); + // copy the accumulated multicast datagram + // for this client out to the message + if (demo.datagram.cursize) { + DemoWrite_Begin(dem_all, 0, demo.datagram.cursize); + SZ_Write ((sizebuf_t*)demo.dbuf, demo.datagram.data, demo.datagram.cursize); + SZ_Clear (&demo.datagram); + } + + demo.recorder.delta_sequence = demo.recorder.netchan.incoming_sequence&255; + demo.recorder.netchan.incoming_sequence++; + demo.frames[demo.parsecount&DEMO_FRAMES_MASK].time = demo.time = sv.time; + + if (demo.parsecount - demo.lastwritten > 60) // that's a backup of 3sec in 20fps, should be enough + { + SV_DemoWritePackets(1); + } + + demo.parsecount++; + DemoSetMsgBuf(demo.dbuf,&demo.frames[demo.parsecount&DEMO_FRAMES_MASK].buf); + + if (sv_demoMaxSize.value && demo.size > sv_demoMaxSize.value*1024) + SV_Stop(1); +} + + +#ifdef _WIN32 +#pragma optimize( "", on ) +#endif + + + +/* +======================= +SV_SendMessagesToAll + +FIXME: does this sequence right? +======================= +*/ +void SV_SendMessagesToAll (void) +{ + int i; + client_t *c; + + for (i=0, c = svs.clients ; istate) // FIXME: should this only send to active? + c->send_message = true; + + SV_SendClientMessages (); +} + diff --git a/source/sv_sys_unix.c b/source/sv_sys_unix.c index b2aa9abb..53abf8bc 100644 --- a/source/sv_sys_unix.c +++ b/source/sv_sys_unix.c @@ -1,355 +1,461 @@ -/* -Copyright (C) 1996-1997 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 "qwsvdef.h" - -#ifdef NeXT -#include -#endif - -#if defined(__linux__) || defined(sun) -#include -#include -#include -#include -#else -#include -#endif - -cvar_t sys_nostdout = {"sys_nostdout","0"}; -cvar_t sys_extrasleep = {"sys_extrasleep","0"}; - -qboolean stdin_ready; - -/* -=============================================================================== - - REQUIRED SYS FUNCTIONS - -=============================================================================== -*/ - -/* -============ -Sys_FileTime - -returns -1 if not present -============ -*/ -int Sys_FileTime (char *path) -{ - struct stat buf; - - if (stat (path,&buf) == -1) - return -1; - - return buf.st_mtime; -} - -int Sys_FileSize (char *path) -{ - struct stat buf; - - if (stat (path,&buf) == -1) - return 0; - - return buf.st_size; -} - - -/* -============ -Sys_mkdir - -============ -*/ -void Sys_mkdir (char *path) -{ - if (mkdir (path, 0777) != -1) - return; - if (errno != EEXIST) - Sys_Error ("mkdir %s: %s",path, strerror(errno)); -} - -/* -================ -Sys_remove -================ -*/ -int Sys_remove (char *path) -{ - return system(va("rm \"%s\"", path)); -} - -/* -================ -Sys_listdir -================ -*/ - -dir_t Sys_listdir (char *path, char *ext) -{ - static file_t list[MAX_DIRFILES]; - dir_t d; - int i, extsize; - DIR *dir; - struct dirent *oneentry; - char pathname[MAX_OSPATH]; - qboolean all; - - memset(list, 0, sizeof(list)); - memset(&d, 0, sizeof(d)); - d.files = list; - extsize = strlen(ext); - all = !strcmp(ext, ".*"); - - dir=opendir(path); - if (!dir) { - return d; - } - - for(;;) - { - oneentry=readdir(dir); - if(!oneentry) - break; - - if (oneentry->d_type == DT_DIR || oneentry->d_type == DT_LNK) - { - d.numdirs++; - continue; - } - - sprintf(pathname, "%s/%s", path, oneentry->d_name); - list[d.numfiles].size = Sys_FileSize(pathname); - d.size += list[d.numfiles].size; - - i = strlen(oneentry->d_name); - if (!all && (i < extsize || (Q_strcasecmp(oneentry->d_name+i-extsize, ext)))) - continue; - - Q_strncpyz (list[d.numfiles].name, oneentry->d_name, MAX_DEMO_NAME); - - - if (++d.numfiles == MAX_DIRFILES) - break; - } - - closedir(dir); - - return d; -} - - -/* -================ -Sys_DoubleTime -================ -*/ -double Sys_DoubleTime (void) -{ - struct timeval tp; - struct timezone tzp; - static int secbase; - - gettimeofday(&tp, &tzp); - - if (!secbase) - { - secbase = tp.tv_sec; - return tp.tv_usec/1000000.0; - } - - return (tp.tv_sec - secbase) + tp.tv_usec/1000000.0; -} - -/* -================ -Sys_Error -================ -*/ -void Sys_Error (char *error, ...) -{ - va_list argptr; - char string[1024]; - - va_start (argptr,error); - vsprintf (string,error,argptr); - va_end (argptr); - printf ("Fatal error: %s\n",string); - - exit (1); -} - -/* -================ -Sys_Printf -================ -*/ -void Sys_Printf (char *fmt, ...) -{ - va_list argptr; - static char text[2048]; - unsigned char *p; - - va_start (argptr,fmt); - vsprintf (text,fmt,argptr); - va_end (argptr); - - if (strlen(text) > sizeof(text)) - Sys_Error("memory overwrite in Sys_Printf"); - - if (sys_nostdout.value) - return; - - for (p = (unsigned char *)text; *p; p++) { - *p &= 0x7f; - if ((*p > 128 || *p < 32) && *p != 10 && *p != 13 && *p != 9) - printf("[%02x]", *p); - else - putc(*p, stdout); - } - fflush(stdout); -} - - -/* -================ -Sys_Quit -================ -*/ -void Sys_Quit (void) -{ - exit (0); // appkit isn't running -} - -static int do_stdin = 1; - -/* -================ -Sys_ConsoleInput - -Checks for a complete line of text typed in at the console, then forwards -it to the host command processor -================ -*/ -char *Sys_ConsoleInput (void) -{ - static char text[256]; - int len; - - if (!stdin_ready || !do_stdin) - return NULL; // the select didn't say it was ready - stdin_ready = false; - - len = read (0, text, sizeof(text)); - if (len == 0) { - // end of file - do_stdin = 0; - return NULL; - } - if (len < 1) - return NULL; - text[len-1] = 0; // rip off the /n and terminate - - return text; -} - -/* -============= -Sys_Init - -Quake calls this so the system can register variables before host_hunklevel -is marked -============= -*/ -void Sys_Init (void) -{ - Cvar_RegisterVariable (&sys_nostdout); - Cvar_RegisterVariable (&sys_extrasleep); -} - -/* -============= -main -============= -*/ -void main (int argc, char *argv[]) -{ - double time, oldtime, newtime; - quakeparms_t parms; - fd_set fdset; - extern int net_socket; - struct timeval timeout; - int j; - - memset (&parms, 0, sizeof(parms)); - - COM_InitArgv (argc, argv); - parms.argc = com_argc; - parms.argv = com_argv; - - parms.memsize = 16*1024*1024; - - j = COM_CheckParm("-mem"); - if (j) - parms.memsize = (int) (Q_atof(com_argv[j+1]) * 1024 * 1024); - parms.membase = Q_Malloc (parms.memsize); - - parms.basedir = "."; - - SV_Init (&parms); - -// run one frame immediately for first heartbeat - SV_Frame (0.1); - -// -// main loop -// - oldtime = Sys_DoubleTime () - 0.1; - while (1) - { - // select on the net socket and stdin - // the only reason we have a timeout at all is so that if the last - // connected client times out, the message would not otherwise - // be printed until the next event. - FD_ZERO(&fdset); - if (do_stdin) - FD_SET(0, &fdset); - FD_SET(net_socket, &fdset); - timeout.tv_sec = 1; - timeout.tv_usec = 0; - if (select (net_socket+1, &fdset, NULL, NULL, &timeout) == -1) - continue; - stdin_ready = FD_ISSET(0, &fdset); - - // find time passed since last cycle - newtime = Sys_DoubleTime (); - time = newtime - oldtime; - oldtime = newtime; - - SV_Frame (time); - - // extrasleep is just a way to generate a fucked up connection on purpose - if (sys_extrasleep.value) - usleep (sys_extrasleep.value); - } -} - +/* +Copyright (C) 1996-1997 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 "qwsvdef.h" + +#ifdef NeXT +#include +#endif + +#if defined(__linux__) || defined(sun) || defined(__GNUC__) +#include +#include +#include +#include +#else +#include +#endif +#include + +cvar_t sys_nostdout = {"sys_nostdout","0"}; +cvar_t sys_extrasleep = {"sys_extrasleep","0"}; + +qboolean stdin_ready; + +/* +=============================================================================== + + REQUIRED SYS FUNCTIONS + +=============================================================================== +*/ + +/* +============ +Sys_FileTime + +returns -1 if not present +============ +*/ +int Sys_FileTime (char *path) +{ + struct stat buf; + + if (stat (path,&buf) == -1) + return -1; + + return buf.st_mtime; +} + +int Sys_FileSize (char *path) +{ + struct stat buf; + + if (stat (path,&buf) == -1) + return 0; + + return buf.st_size; +} + + +/* +============ +Sys_mkdir + +============ +*/ +void Sys_mkdir (char *path) +{ + if (mkdir (path, 0777) != -1) + return; + if (errno != EEXIST) + Sys_Error ("mkdir %s: %s",path, strerror(errno)); +} + +/* +================ +Sys_remove +================ +*/ +int Sys_remove (char *path) +{ + return system(va("rm \"%s\"", path)); +} + +/* +================ +Sys_listdir +================ +*/ + +dir_t Sys_listdir (char *path, char *ext) +{ + static file_t list[MAX_DIRFILES]; + dir_t d; + int i, extsize; + DIR *dir; + struct dirent *oneentry; + char pathname[MAX_OSPATH]; + qboolean all; + + memset(list, 0, sizeof(list)); + memset(&d, 0, sizeof(d)); + d.files = list; + extsize = strlen(ext); + all = !strcmp(ext, ".*"); + + dir=opendir(path); + if (!dir) { + return d; + } + + for(;;) + { + oneentry=readdir(dir); + if(!oneentry) + break; + + if (oneentry->d_type == DT_DIR || oneentry->d_type == DT_LNK) + { + d.numdirs++; + continue; + } + + sprintf(pathname, "%s/%s", path, oneentry->d_name); + list[d.numfiles].size = Sys_FileSize(pathname); + d.size += list[d.numfiles].size; + + i = strlen(oneentry->d_name); + //if (!all && (i < extsize || (Q_strcasecmp(oneentry->d_name+i-extsize, ext)))) + // continue; + if (!all && !strstr(oneentry->d_name, ext)) + continue; + + Q_strncpyz (list[d.numfiles].name, oneentry->d_name, MAX_DEMO_NAME); + + + if (++d.numfiles == MAX_DIRFILES - 1) + break; + } + + closedir(dir); + + return d; +} + +void Sys_TimeOfDay(date_t *date) +{ + struct tm *newtime; + time_t long_time; + + time( &long_time ); + newtime = localtime( &long_time ); + + date->day = newtime->tm_mday; + date->mon = newtime->tm_mon; + date->year = newtime->tm_year + 1900; + date->hour = newtime->tm_hour; + date->min = newtime->tm_min; + date->sec = newtime->tm_sec; + strftime( date->str, 128, + "%a %b %d, %H:%M %Y", newtime); +} + +/* +================ +Sys_DoubleTime +================ +*/ +double Sys_DoubleTime (void) +{ + struct timeval tp; + struct timezone tzp; + static int secbase; + + gettimeofday(&tp, &tzp); + + if (!secbase) + { + secbase = tp.tv_sec; + return tp.tv_usec/1000000.0; + } + + return (tp.tv_sec - secbase) + tp.tv_usec/1000000.0; +} + +/* +================ +Sys_Error +================ +*/ +void Sys_Error (char *error, ...) +{ + va_list argptr; + char string[1024]; + + va_start (argptr,error); + vsprintf (string,error,argptr); + va_end (argptr); + printf ("Fatal error: %s\n",string); + if (sv_errorlogfile) + fprintf(sv_errorlogfile, "Fatal error: %s\n", string); + + exit (1); +} + +/* +================ +Sys_Printf +================ +*/ +void Sys_Printf (char *fmt, ...) +{ + va_list argptr; + static char text[4096]; + unsigned char *p; + + va_start (argptr,fmt); + vsprintf (text,fmt,argptr); + va_end (argptr); + + if (strlen(text) > sizeof(text)) + Sys_Error("memory overwrite in Sys_Printf"); + + if (sys_nostdout.value) + return; + + for (p = (unsigned char *)text; *p; p++) { + *p &= 0x7f; + if ((*p > 128 || *p < 32) && *p != 10 && *p != 13 && *p != 9) + printf("[%02x]", *p); + else + putc(*p, stdout); + } + fflush(stdout); +} + + +/* +================ +Sys_Quit +================ +*/ +void Sys_Quit (void) +{ + exit (0); // appkit isn't running +} + +static int do_stdin = 1; + +/* +================ +Sys_ConsoleInput + +Checks for a complete line of text typed in at the console, then forwards +it to the host command processor +================ +*/ +char *Sys_ConsoleInput (void) +{ + static char text[256]; + int len; +#ifdef NEWWAY + fd_set fdset; + struct timeval timeout; + + if (!do_stdin) + return; + + FD_ZERO(&fdset); + FD_SET(0, &fdset); // stdin + timeout.tv_sec = 0; + timeout.tv_usec = 0; + if (select (1, &fdset, NULL, NULL, &timeout) == -1 || !FD_ISSET(0, &fdset)) + return NULL; +#else + + if (!stdin_ready || !do_stdin) + return NULL; // the select didn't say it was ready + stdin_ready = false; +#endif + + len = read (0, text, sizeof(text)); + if (len == 0) { + // end of file + do_stdin = 0; + return NULL; + } + if (len < 1) + return NULL; + text[len-1] = 0; // rip off the /n and terminate + + return text; +} + +/* +============= +Sys_Init + +Quake calls this so the system can register variables before host_hunklevel +is marked +============= +*/ +void Sys_Init (void) +{ + Cvar_RegisterVariable (&sys_nostdout); + Cvar_RegisterVariable (&sys_extrasleep); +} + +int NET_Sleep(double sec) +{ + struct timeval timeout; + fd_set fdset; + extern int net_socket; + + FD_ZERO(&fdset); + if (do_stdin) + FD_SET(0, &fdset); // stdin is processed too + FD_SET(net_socket, &fdset); // network socket + + timeout.tv_sec = (long) sec; + timeout.tv_usec = (sec - floor(sec))*1000000L; + return select(net_socket+1, &fdset, NULL, NULL, &timeout); +} + +void Sys_Sleep(unsigned long ms) +{ + usleep(ms * 1000); +} + +int Sys_Script(char *path, char *args) +{ + char str[1024]; + + sprintf(str,"cd %s\n./%s.qws %s\ncd ..", com_gamedir, path, args); + + if (system(str) == -1) + return 0; + + return 1; + +#if 0 + Cmd_TokenizeString(args); + for (i = 0; i < Cmd_Argc() && i < MAXSCRIPTARGS; i++) + Args[i] = Cmd_Argv(i); + + Args[i] = NULL; + + + if (_spawnv(_P_WAITNO, va("%s\\%s",com_gamedir, path), args) == -1) + return 0; + + return 1; +#endif +} + + +/* +============= +main +============= +*/ + +void main (int argc, char *argv[]) +{ + double time, oldtime, newtime; + quakeparms_t parms; + extern int net_socket; +#ifndef NEWWAY + struct timeval timeout; + fd_set fdset; +#endif + int j; + + memset (&parms, 0, sizeof(parms)); + + COM_InitArgv (argc, argv); + parms.argc = com_argc; + parms.argv = com_argv; + + parms.memsize = 16*1024*1024; + + j = COM_CheckParm("-mem"); + if (j) + parms.memsize = (int) (Q_atof(com_argv[j+1]) * 1024 * 1024); + parms.membase = Q_Malloc (parms.memsize); + + parms.basedir = "."; + + SV_Init (&parms); + +// run one frame immediately for first heartbeat + SV_Frame (0.1); + +// +// main loop +// + oldtime = Sys_DoubleTime () - 0.1; +#ifndef NEWWAY + while (1) + { + // select on the net socket and stdin + // the only reason we have a timeout at all is so that if the last + // connected client times out, the message would not otherwise + // be printed until the next event. + FD_ZERO(&fdset); + if (do_stdin) + FD_SET(0, &fdset); + FD_SET(net_socket, &fdset); + timeout.tv_sec = 1; + timeout.tv_usec = 0; + if (select (net_socket+1, &fdset, NULL, NULL, &timeout) == -1) + continue; + stdin_ready = FD_ISSET(0, &fdset); + + // find time passed since last cycle + newtime = Sys_DoubleTime (); + time = newtime - oldtime; + oldtime = newtime; + + SV_Frame (time); + + // extrasleep is just a way to generate a fucked up connection on purpose + if (sys_extrasleep.value) + usleep (sys_extrasleep.value); + } +#else + while (1) + { + do + { + newtime = Sys_DoubleTime (); + time = newtime - oldtime; + } while (time < 0.001); + + SV_Frame (time); + oldtime = newtime; + + // extrasleep is just a way to generate a fucked up connection on purpose + if (sys_extrasleep.value) + usleep (sys_extrasleep.value); + } +#endif +} + diff --git a/source/sv_sys_win.c b/source/sv_sys_win.c index 5ac06d06..6e0bd60e 100644 --- a/source/sv_sys_win.c +++ b/source/sv_sys_win.c @@ -1,427 +1,517 @@ -/* -Copyright (C) 1996-1997 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 -#include "qwsvdef.h" -#include -#include -#include -#include // _mkdir - -cvar_t sys_sleep = {"sys_sleep", "8"}; -cvar_t sys_nostdout = {"sys_nostdout","0"}; - -qboolean WinNT; - -/* -================ -Sys_FileTime -================ -*/ -int Sys_FileTime (char *path) -{ - FILE *f; - - f = fopen(path, "rb"); - if (f) - { - fclose(f); - return 1; - } - - return -1; -} - -/* -================ -Sys_mkdir -================ -*/ -void Sys_mkdir (char *path) -{ - _mkdir(path); -} - -/* -================ -Sys_remove -================ -*/ -int Sys_remove (char *path) -{ - return remove(path); -} - -/* -================ -Sys_listdir -================ -*/ - -dir_t Sys_listdir (char *path, char *ext) -{ - static file_t list[MAX_DIRFILES]; - dir_t dir; - HANDLE h; - WIN32_FIND_DATA fd; - int i, pos, size; - char name[MAX_DEMO_NAME], *s; - - memset(list, 0, sizeof(list)); - memset(&dir, 0, sizeof(dir)); - - dir.files = list; - - h = FindFirstFile (va("%s/*.*", path), &fd); - if (h == INVALID_HANDLE_VALUE) { - return dir; - } - - do { - if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) - { - dir.numdirs++; - continue; - } - - size = fd.nFileSizeLow; - Q_strncpyz (name, fd.cFileName, MAX_DEMO_NAME); - dir.size += size; - - for (s = fd.cFileName + strlen(fd.cFileName); s > fd.cFileName; s--) { - if (*s == '.') - break; - } - - if (strcmp(s, ext)) - continue; - - // inclusion sort - /* - for (i=0 ; ipos ; i--) - list[i] = list[i-1]; - - strcpy (list[i].name, name); - list[i].size = size; - if (dir.numfiles == MAX_DIRFILES) - break; - } while ( FindNextFile(h, &fd) ); - FindClose (h); - - return dir; -} - -/* -================ -Sys_Error -================ -*/ -void Sys_Error (char *error, ...) -{ - va_list argptr; - char text[1024]; - - va_start (argptr,error); - vsprintf (text, error,argptr); - va_end (argptr); - -// MessageBox(NULL, text, "Error", 0 /* MB_OK */ ); - printf ("ERROR: %s\n", text); - - exit (1); -} - - -#if 1 -double Sys_DoubleTime (void) -{ - static DWORD starttime; - static qboolean first = true; - DWORD now; - - now = timeGetTime(); - - if (first) { - first = false; - starttime = now; - return 0.0; - } - - if (now < starttime) // wrapped? - return (now / 1000.0) + (LONG_MAX - starttime / 1000.0); - - if (now - starttime == 0) - return 0.0; - - return (now - starttime) / 1000.0; -} - -#else - -/* -================ -Sys_DoubleTime -================ -*/ -double Sys_DoubleTime (void) -{ - double t; - struct _timeb tstruct; - static int starttime; - - _ftime( &tstruct ); - - if (!starttime) - starttime = tstruct.time; - t = (tstruct.time-starttime) + tstruct.millitm*0.001; - - return t; -} -#endif - - -/* -================ -Sys_ConsoleInput -================ -*/ -char *Sys_ConsoleInput (void) -{ - static char text[256]; - static int len; - int c; - - // read a line out - while (_kbhit()) - { - c = _getch(); - - if (c == 224) { - if (_kbhit()) { - // assume escape sequence (arrows etc), skip - _getch(); - continue; - } - // assume character - } - - if (c < 32 && c != '\r' && c != 8) - continue; - - putch (c); - if (c == '\r') - { - text[len] = 0; - putch ('\n'); - len = 0; - return text; - } - if (c == 8) - { - if (len) - { - putch (' '); - putch (c); - len--; - text[len] = 0; - } - continue; - } - - text[len] = c; - len++; - text[len] = 0; - if (len == sizeof(text)) - len = 0; - } - - return NULL; -} - - -/* -================ -Sys_Printf -================ -*/ -void Sys_Printf (char *fmt, ...) -{ - va_list argptr; - - if (sys_nostdout.value) - return; - - va_start (argptr,fmt); - vprintf (fmt,argptr); - va_end (argptr); -} - -/* -================ -Sys_Quit -================ -*/ -void Sys_Quit (void) -{ - exit (0); -} - - -/* -============= -Sys_Init - -Quake calls this so the system can register variables before host_hunklevel -is marked -============= -*/ -void Sys_Init (void) -{ - OSVERSIONINFO vinfo; - - // make sure the timer is high precision, otherwise - // NT gets 18ms resolution - timeBeginPeriod( 1 ); - - vinfo.dwOSVersionInfoSize = sizeof(vinfo); - - if (!GetVersionEx (&vinfo)) - Sys_Error ("Couldn't get OS info"); - - if ((vinfo.dwMajorVersion < 4) || - (vinfo.dwPlatformId == VER_PLATFORM_WIN32s)) - { - Sys_Error ("QuakeWorld requires at least Win95 or NT 4.0"); - } - - if (vinfo.dwPlatformId == VER_PLATFORM_WIN32_NT) - WinNT = true; - else - WinNT = false; - - Cvar_RegisterVariable (&sys_sleep); - Cvar_RegisterVariable (&sys_nostdout); - - if (COM_CheckParm ("-nopriority")) - { - Cvar_Set (&sys_sleep, "0"); - } - else - { - if ( ! SetPriorityClass (GetCurrentProcess(), HIGH_PRIORITY_CLASS)) - Con_Printf ("SetPriorityClass() failed\n"); - else - Con_Printf ("Process priority class set to HIGH\n"); - - // sys_sleep > 0 seems to cause packet loss on WinNT (why?) - if (WinNT) - Cvar_Set (&sys_sleep, "0"); - } -} - -/* -================== -main - -================== -*/ -char *newargv[256]; - -int main (int argc, char **argv) -{ - quakeparms_t parms; - double newtime, time, oldtime; - static char cwd[1024]; - struct timeval timeout; - fd_set fdset; - int t; - int sleep_msec; - - COM_InitArgv (argc, argv); - - parms.argc = com_argc; - parms.argv = com_argv; - - parms.memsize = 16*1024*1024; - - if ((t = COM_CheckParm ("-heapsize")) != 0 && - t + 1 < com_argc) - parms.memsize = Q_atoi (com_argv[t + 1]) * 1024; - - if ((t = COM_CheckParm ("-mem")) != 0 && - t + 1 < com_argc) - parms.memsize = Q_atoi (com_argv[t + 1]) * 1024 * 1024; - - parms.membase = Q_Malloc (parms.memsize); - - parms.basedir = "."; - - SV_Init (&parms); - -// run one frame immediately for first heartbeat - SV_Frame (0.1); - -// -// main loop -// - oldtime = Sys_DoubleTime () - 0.1; - while (1) - { - sleep_msec = sys_sleep.value; - if (sleep_msec > 0) - { - if (sleep_msec > 13) - sleep_msec = 13; - Sleep (sleep_msec); - } - - // select on the net socket and stdin - // the only reason we have a timeout at all is so that if the last - // connected client times out, the message would not otherwise - // be printed until the next event. - FD_ZERO(&fdset); - FD_SET(net_serversocket, &fdset); - timeout.tv_sec = 0; - timeout.tv_usec = 100; - if (select (net_serversocket+1, &fdset, NULL, NULL, &timeout) == -1) - continue; - - // find time passed since last cycle - newtime = Sys_DoubleTime (); - time = newtime - oldtime; - oldtime = newtime; - - SV_Frame (time); - } - - return true; -} - - +/* +Copyright (C) 1996-1997 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 +#include "qwsvdef.h" +#include +#include +#include +#include // _mkdir +#include +#include + +cvar_t sys_sleep = {"sys_sleep", "8"}; +cvar_t sys_nostdout = {"sys_nostdout","0"}; + +qboolean WinNT; + +/* +================ +Sys_FileTime +================ +*/ +int Sys_FileTime (char *path) +{ + FILE *f; + + f = fopen(path, "rb"); + if (f) + { + fclose(f); + return 1; + } + + return -1; +} + +/* +================ +Sys_mkdir +================ +*/ +void Sys_mkdir (char *path) +{ + _mkdir(path); +} + +/* +================ +Sys_remove +================ +*/ +int Sys_remove (char *path) +{ + return remove(path); +} + +/* +================ +Sys_listdir +================ +*/ + +dir_t Sys_listdir (char *path, char *ext) +{ + static file_t list[MAX_DIRFILES]; + dir_t dir; + HANDLE h; + WIN32_FIND_DATA fd; + int i, pos, size; + char name[MAX_DEMO_NAME]; + qboolean all; + + memset(list, 0, sizeof(list)); + memset(&dir, 0, sizeof(dir)); + + dir.files = list; + all = !strcmp(ext, ".*"); + + h = FindFirstFile (va("%s/*.*", path), &fd); + if (h == INVALID_HANDLE_VALUE) { + return dir; + } + + do { + if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + { + dir.numdirs++; + continue; + } + + size = fd.nFileSizeLow; + Q_strncpyz (name, fd.cFileName, MAX_DEMO_NAME); + dir.size += size; + + if (!all && !strstr(fd.cFileName, ext)) + continue; + + /*for (s = fd.cFileName + strlen(fd.cFileName); s > fd.cFileName; s--) { + if (*s == '.') + break; + } + + if (strcmp(s, ext)) + continue; + */ + + // inclusion sort + /* + for (i=0 ; ipos ; i--) + list[i] = list[i-1]; + + strcpy (list[i].name, name); + list[i].size = size; + if (dir.numfiles == MAX_DIRFILES - 1) + break; + } while ( FindNextFile(h, &fd) ); + FindClose (h); + + return dir; +} + +/* +================ +Sys_Error +================ +*/ +void Sys_Error (char *error, ...) +{ + va_list argptr; + char text[1024]; + + va_start (argptr,error); + vsprintf (text, error,argptr); + va_end (argptr); + +// MessageBox(NULL, text, "Error", 0 /* MB_OK */ ); + printf ("ERROR: %s\n", text); + if (sv_errorlogfile) + fprintf(sv_errorlogfile, "ERROR: %s\n", text); + + exit (1); +} + +void Sys_TimeOfDay(date_t *date) +{ + struct tm *newtime; + time_t long_time; + + time( &long_time ); + newtime = localtime( &long_time ); + + date->day = newtime->tm_mday; + date->mon = newtime->tm_mon; + date->year = newtime->tm_year + 1900; + date->hour = newtime->tm_hour; + date->min = newtime->tm_min; + date->sec = newtime->tm_sec; + strftime( date->str, 128, + "%a %b %d, %H:%M %Y", newtime); +} + + + +#if 1 +double Sys_DoubleTime (void) +{ + static DWORD starttime; + static qboolean first = true; + DWORD now; + + now = timeGetTime(); + + if (first) { + first = false; + starttime = now; + return 0.0; + } + + if (now < starttime) // wrapped? + return (now / 1000.0) + (LONG_MAX - starttime / 1000.0); + + if (now - starttime == 0) + return 0.0; + + return (now - starttime) / 1000.0; +} + +#else + +/* +================ +Sys_DoubleTime +================ +*/ +double Sys_DoubleTime (void) +{ + double t; + struct _timeb tstruct; + static int starttime; + + _ftime( &tstruct ); + + if (!starttime) + starttime = tstruct.time; + t = (tstruct.time-starttime) + tstruct.millitm*0.001; + + return t; +} +#endif + + +/* +================ +Sys_ConsoleInput +================ +*/ +char *Sys_ConsoleInput (void) +{ + static char text[256]; + static int len; + int c; + + // read a line out + while (_kbhit()) + { + c = _getch(); + + if (c == 224) { + if (_kbhit()) { + // assume escape sequence (arrows etc), skip + _getch(); + continue; + } + // assume character + } + + if (c < 32 && c != '\r' && c != 8) + continue; + + putch (c); + if (c == '\r') + { + text[len] = 0; + putch ('\n'); + len = 0; + return text; + } + if (c == 8) + { + if (len) + { + putch (' '); + putch (c); + len--; + text[len] = 0; + } + continue; + } + + text[len] = c; + len++; + text[len] = 0; + if (len == sizeof(text)) + len = 0; + } + + return NULL; +} + + +/* +================ +Sys_Printf +================ +*/ +void Sys_Printf (char *fmt, ...) +{ + va_list argptr; + + if (sys_nostdout.value) + return; + + va_start (argptr,fmt); + vprintf (fmt,argptr); + va_end (argptr); +} + +/* +================ +Sys_Quit +================ +*/ +void Sys_Quit (void) +{ + exit (0); +} + + +/* +============= +Sys_Init + +Quake calls this so the system can register variables before host_hunklevel +is marked +============= +*/ +void Sys_Init (void) +{ + OSVERSIONINFO vinfo; + + // make sure the timer is high precision, otherwise + // NT gets 18ms resolution + timeBeginPeriod( 1 ); + + vinfo.dwOSVersionInfoSize = sizeof(vinfo); + + if (!GetVersionEx (&vinfo)) + Sys_Error ("Couldn't get OS info"); + + if ((vinfo.dwMajorVersion < 4) || + (vinfo.dwPlatformId == VER_PLATFORM_WIN32s)) + { + Sys_Error ("QuakeWorld requires at least Win95 or NT 4.0"); + } + + if (vinfo.dwPlatformId == VER_PLATFORM_WIN32_NT) + WinNT = true; + else + WinNT = false; + + Cvar_RegisterVariable (&sys_sleep); + Cvar_RegisterVariable (&sys_nostdout); + + if (COM_CheckParm ("-nopriority")) + { + Cvar_Set (&sys_sleep, "0"); + } + else + { + if ( ! SetPriorityClass (GetCurrentProcess(), HIGH_PRIORITY_CLASS)) + Con_Printf ("SetPriorityClass() failed\n"); + else + Con_Printf ("Process priority class set to HIGH\n"); + + // sys_sleep > 0 seems to cause packet loss on WinNT (why?) + if (WinNT) + Cvar_Set (&sys_sleep, "0"); + } +} + +int NET_Sleep(double sec) +{ + struct timeval timeout; + fd_set fdset; + + FD_ZERO(&fdset); + FD_SET(net_serversocket, &fdset); + + timeout.tv_sec = (long) sec; + timeout.tv_usec = (sec - floor(sec))*1000000L; + //Sys_Printf("%lf, %ld %ld\n", sec, timeout.tv_sec, timeout.tv_usec); + return select(net_serversocket+1, &fdset, NULL, NULL, &timeout); +} + +void Sys_Sleep(unsigned long ms) +{ + Sleep(ms); +} + +int Sys_Script(char *path, char *args) +{ + STARTUPINFO si; + PROCESS_INFORMATION pi; + char cmdline[1024], curdir[MAX_OSPATH]; + + memset (&si, 0, sizeof(si)); + si.cb = sizeof(si); + si.dwFlags = STARTF_USESHOWWINDOW; + si.wShowWindow = SW_SHOWMINNOACTIVE; + + GetCurrentDirectory(sizeof(curdir), curdir); + strcat(curdir,va("\\%s", com_gamedir+2)); + + sprintf(cmdline, "%s\\%s.bat %s", curdir, path, args); + + return CreateProcess (NULL, cmdline, NULL, NULL, + FALSE, DETACHED_PROCESS/*CREATE_NEW_CONSOLE*/ , NULL, curdir, &si, &pi); +} +/* +================== +main + +================== +*/ +char *newargv[256]; + +int main (int argc, char **argv) +{ + quakeparms_t parms; + double newtime, time, oldtime; + static char cwd[1024]; +#ifndef NEWWAY + struct timeval timeout; + fd_set fdset; +#endif + int t; + int sleep_msec; + + COM_InitArgv (argc, argv); + + parms.argc = com_argc; + parms.argv = com_argv; + + parms.memsize = 16*1024*1024; + + if ((t = COM_CheckParm ("-heapsize")) != 0 && + t + 1 < com_argc) + parms.memsize = Q_atoi (com_argv[t + 1]) * 1024; + + if ((t = COM_CheckParm ("-mem")) != 0 && + t + 1 < com_argc) + parms.memsize = Q_atoi (com_argv[t + 1]) * 1024 * 1024; + + parms.membase = Q_Malloc (parms.memsize); + + parms.basedir = "."; + + SV_Init (&parms); + +// run one frame immediately for first heartbeat + SV_Frame (0.1); + +// +// main loop +// + oldtime = Sys_DoubleTime () - 0.1; +#ifndef NEWWAY + while (1) + { + sleep_msec = sys_sleep.value; + if (sleep_msec > 0) + { + if (sleep_msec > 13) + sleep_msec = 13; + Sleep (sleep_msec); + } + + // select on the net socket and stdin + // the only reason we have a timeout at all is so that if the last + // connected client times out, the message would not otherwise + // be printed until the next event. + FD_ZERO(&fdset); + FD_SET(net_serversocket, &fdset); + timeout.tv_sec = 0; + timeout.tv_usec = 100; + if (select (net_serversocket+1, &fdset, NULL, NULL, &timeout) == -1) + continue; + + // find time passed since last cycle + newtime = Sys_DoubleTime (); + time = newtime - oldtime; + oldtime = newtime; + + SV_Frame (time); + } +#else + + /* main window message loop */ + while (1) + { + // if at a full screen console, don't update unless needed + Sleep (1); + + do + { + newtime = Sys_DoubleTime (); + time = newtime - oldtime; + } while (time < 0.001); + + //_controlfp( _PC_24, _MCW_PC ); + + SV_Frame (time); + oldtime = newtime; + } +#endif + + return true; +} + + diff --git a/source/sv_user.c b/source/sv_user.c index 07956f20..72a1b61e 100644 --- a/source/sv_user.c +++ b/source/sv_user.c @@ -1,1770 +1,1956 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// sv_user.c -- server code for moving users - -#include "qwsvdef.h" - -edict_t *sv_player; - -usercmd_t cmd; - -cvar_t sv_rollspeed = {"cl_rollspeed", "200"}; -cvar_t sv_rollangle = {"cl_rollangle", "2.0"}; - -cvar_t sv_spectalk = {"sv_spectalk", "1"}; - -cvar_t sv_mapcheck = {"sv_mapcheck", "1"}; - -extern vec3_t player_mins; - -extern int fp_messages, fp_persecond, fp_secondsdead; -extern char fp_msg[]; -extern cvar_t pausable; - -/* -============================================================ - -USER STRINGCMD EXECUTION - -host_client and sv_player will be valid. -============================================================ -*/ - -/* -================ -SV_New_f - -Sends the first message from the server to a connected client. -This will be sent on the initial connection and upon each server load. -================ -*/ -void SV_New_f (void) -{ - char *gamedir; - int playernum; - - if (host_client->state == cs_spawned) - return; - - host_client->state = cs_connected; - host_client->connection_started = realtime; - - // send the info about the new client to all connected clients -// SV_FullClientUpdate (host_client, &sv.reliable_datagram); -// host_client->sendinfo = true; - - gamedir = Info_ValueForKey (svs.info, "*gamedir"); - if (!gamedir[0]) - gamedir = "qw"; - -//NOTE: This doesn't go through ClientReliableWrite since it's before the user -//spawns. These functions are written to not overflow - if (host_client->num_backbuf) { - Con_Printf("WARNING %s: [SV_New] Back buffered (%d0), clearing\n", host_client->name, host_client->netchan.message.cursize); - host_client->num_backbuf = 0; - SZ_Clear(&host_client->netchan.message); - } - - // send the serverdata - MSG_WriteByte (&host_client->netchan.message, svc_serverdata); - MSG_WriteLong (&host_client->netchan.message, PROTOCOL_VERSION); - MSG_WriteLong (&host_client->netchan.message, svs.spawncount); - MSG_WriteString (&host_client->netchan.message, gamedir); - - playernum = NUM_FOR_EDICT(host_client->edict)-1; - if (host_client->spectator) - playernum |= 128; - MSG_WriteByte (&host_client->netchan.message, playernum); - - // send full levelname - MSG_WriteString (&host_client->netchan.message, PR_GetString(sv.edicts->v.message)); - - // send the movevars - MSG_WriteFloat(&host_client->netchan.message, movevars.gravity); - MSG_WriteFloat(&host_client->netchan.message, movevars.stopspeed); - MSG_WriteFloat(&host_client->netchan.message, movevars.maxspeed); - MSG_WriteFloat(&host_client->netchan.message, movevars.spectatormaxspeed); - MSG_WriteFloat(&host_client->netchan.message, movevars.accelerate); - MSG_WriteFloat(&host_client->netchan.message, movevars.airaccelerate); - MSG_WriteFloat(&host_client->netchan.message, movevars.wateraccelerate); - MSG_WriteFloat(&host_client->netchan.message, movevars.friction); - MSG_WriteFloat(&host_client->netchan.message, movevars.waterfriction); - MSG_WriteFloat(&host_client->netchan.message, movevars.entgravity); - - // send music - MSG_WriteByte (&host_client->netchan.message, svc_cdtrack); - MSG_WriteByte (&host_client->netchan.message, sv.edicts->v.sounds); - - // send server info string - MSG_WriteByte (&host_client->netchan.message, svc_stufftext); - MSG_WriteString (&host_client->netchan.message, va("fullserverinfo \"%s\"\n", svs.info) ); -} - -/* -================== -SV_Soundlist_f -================== -*/ -void SV_Soundlist_f (void) -{ - char **s; - unsigned n; - - if (host_client->state != cs_connected) - { - Con_Printf ("soundlist not valid -- already spawned\n"); - return; - } - - // handle the case of a level changing while a client was connecting - if ( atoi(Cmd_Argv(1)) != svs.spawncount ) - { - Con_Printf ("SV_Soundlist_f from different level\n"); - SV_New_f (); - return; - } - - n = atoi(Cmd_Argv(2)); - if (n >= MAX_SOUNDS) { - SV_ClientPrintf (host_client, PRINT_HIGH, - "SV_Soundlist_f: Invalid soundlist index\n"); - SV_DropClient (host_client); - return; - } - -//NOTE: This doesn't go through ClientReliableWrite since it's before the user -//spawns. These functions are written to not overflow - if (host_client->num_backbuf) { - Con_Printf("WARNING %s: [SV_Soundlist] Back buffered (%d0), clearing\n", host_client->name, host_client->netchan.message.cursize); - host_client->num_backbuf = 0; - SZ_Clear(&host_client->netchan.message); - } - - MSG_WriteByte (&host_client->netchan.message, svc_soundlist); - MSG_WriteByte (&host_client->netchan.message, n); - for (s = sv.sound_precache+1 + n ; - *s && host_client->netchan.message.cursize < (MAX_MSGLEN/2); - s++, n++) - MSG_WriteString (&host_client->netchan.message, *s); - - MSG_WriteByte (&host_client->netchan.message, 0); - - // next msg - if (*s) - MSG_WriteByte (&host_client->netchan.message, n); - else - MSG_WriteByte (&host_client->netchan.message, 0); -} - -/* -================== -SV_Modellist_f -================== -*/ -void SV_Modellist_f (void) -{ - char **s; - unsigned n; - - if (host_client->state != cs_connected) - { - Con_Printf ("modellist not valid -- already spawned\n"); - return; - } - - // handle the case of a level changing while a client was connecting - if ( atoi(Cmd_Argv(1)) != svs.spawncount ) - { - Con_Printf ("SV_Modellist_f from different level\n"); - SV_New_f (); - return; - } - - n = atoi(Cmd_Argv(2)); - if (n >= MAX_MODELS) { - SV_ClientPrintf (host_client, PRINT_HIGH, - "SV_Modellist_f: Invalid modellist index\n"); - SV_DropClient (host_client); - return; - } - -//NOTE: This doesn't go through ClientReliableWrite since it's before the user -//spawns. These functions are written to not overflow - if (host_client->num_backbuf) { - Con_Printf("WARNING %s: [SV_Modellist] Back buffered (%d0), clearing\n", host_client->name, host_client->netchan.message.cursize); - host_client->num_backbuf = 0; - SZ_Clear(&host_client->netchan.message); - } - - MSG_WriteByte (&host_client->netchan.message, svc_modellist); - MSG_WriteByte (&host_client->netchan.message, n); - for (s = sv.model_precache+1+n ; - *s && host_client->netchan.message.cursize < (MAX_MSGLEN/2); - s++, n++) - MSG_WriteString (&host_client->netchan.message, *s); - MSG_WriteByte (&host_client->netchan.message, 0); - - // next msg - if (*s) - MSG_WriteByte (&host_client->netchan.message, n); - else - MSG_WriteByte (&host_client->netchan.message, 0); -} - -/* -================== -SV_PreSpawn_f -================== -*/ -void SV_PreSpawn_f (void) -{ - unsigned buf; - unsigned check; - - if (host_client->state != cs_connected) - { - Con_Printf ("prespawn not valid -- already spawned\n"); - return; - } - - // handle the case of a level changing while a client was connecting - if ( atoi(Cmd_Argv(1)) != svs.spawncount ) - { - Con_Printf ("SV_PreSpawn_f from different level\n"); - SV_New_f (); - return; - } - - buf = atoi(Cmd_Argv(2)); - if (buf >= sv.num_signon_buffers) - buf = 0; - - if (!buf) { - // should be three numbers following containing checksums - check = atoi(Cmd_Argv(3)); - -// Con_DPrintf("Client check = %d\n", check); - - if (sv_mapcheck.value && check != sv.worldmodel->checksum && - check != sv.worldmodel->checksum2) { - SV_ClientPrintf (host_client, PRINT_HIGH, - "Map model file does not match (%s), %i != %i/%i.\n" - "You may need a new version of the map, or the proper install files.\n", - sv.modelname, check, sv.worldmodel->checksum, sv.worldmodel->checksum2); - SV_DropClient (host_client); - return; - } - host_client->checksum = check; - } - -//NOTE: This doesn't go through ClientReliableWrite since it's before the user -//spawns. These functions are written to not overflow - if (host_client->num_backbuf) { - Con_Printf("WARNING %s: [SV_PreSpawn] Back buffered (%d0), clearing\n", host_client->name, host_client->netchan.message.cursize); - host_client->num_backbuf = 0; - SZ_Clear(&host_client->netchan.message); - } - - SZ_Write (&host_client->netchan.message, - sv.signon_buffers[buf], - sv.signon_buffer_size[buf]); - - buf++; - if (buf == sv.num_signon_buffers) - { // all done prespawning - MSG_WriteByte (&host_client->netchan.message, svc_stufftext); - MSG_WriteString (&host_client->netchan.message, va("cmd spawn %i 0\n",svs.spawncount) ); - } - else - { // need to prespawn more - MSG_WriteByte (&host_client->netchan.message, svc_stufftext); - MSG_WriteString (&host_client->netchan.message, - va("cmd prespawn %i %i\n", svs.spawncount, buf) ); - } -} - -/* -================== -SV_Spawn_f -================== -*/ -void SV_Spawn_f (void) -{ - int i; - client_t *client; - edict_t *ent; - eval_t *val; - unsigned n; - - if (host_client->state != cs_connected) - { - Con_Printf ("Spawn not valid -- already spawned\n"); - return; - } - -// handle the case of a level changing while a client was connecting - if ( atoi(Cmd_Argv(1)) != svs.spawncount ) - { - Con_Printf ("SV_Spawn_f from different level\n"); - SV_New_f (); - return; - } - - n = atoi(Cmd_Argv(2)); - if (n >= MAX_CLIENTS) { - SV_ClientPrintf (host_client, PRINT_HIGH, - "SV_Spawn_f: Invalid client start\n"); - SV_DropClient (host_client); - return; - } - -// send all current names, colors, and frag counts - // FIXME: is this a good thing? - SZ_Clear (&host_client->netchan.message); - -// send current status of all other players - - // normally this could overflow, but no need to check due to backbuf - for (i=n, client = svs.clients + n ; iedict; - - memset (&ent->v, 0, progs->entityfields * 4); - ent->v.colormap = NUM_FOR_EDICT(ent); - ent->v.team = 0; // FIXME - ent->v.netname = PR_SetString(host_client->name); - - host_client->entgravity = 1.0; - val = GetEdictFieldValue(ent, "gravity"); - if (val) - val->_float = 1.0; - host_client->maxspeed = sv_maxspeed.value; - val = GetEdictFieldValue(ent, "maxspeed"); - if (val) - val->_float = sv_maxspeed.value; - -// -// force stats to be updated -// - memset (host_client->stats, 0, sizeof(host_client->stats)); - - ClientReliableWrite_Begin (host_client, svc_updatestatlong, 6); - ClientReliableWrite_Byte (host_client, STAT_TOTALSECRETS); - ClientReliableWrite_Long (host_client, pr_global_struct->total_secrets); - - ClientReliableWrite_Begin (host_client, svc_updatestatlong, 6); - ClientReliableWrite_Byte (host_client, STAT_TOTALMONSTERS); - ClientReliableWrite_Long (host_client, pr_global_struct->total_monsters); - - ClientReliableWrite_Begin (host_client, svc_updatestatlong, 6); - ClientReliableWrite_Byte (host_client, STAT_SECRETS); - ClientReliableWrite_Long (host_client, pr_global_struct->found_secrets); - - ClientReliableWrite_Begin (host_client, svc_updatestatlong, 6); - ClientReliableWrite_Byte (host_client, STAT_MONSTERS); - ClientReliableWrite_Long (host_client, pr_global_struct->killed_monsters); - - // get the client to check and download skins - // when that is completed, a begin command will be issued - ClientReliableWrite_Begin (host_client, svc_stufftext, 8); - ClientReliableWrite_String (host_client, "skins\n" ); - -} - -/* -================== -SV_SpawnSpectator -================== -*/ -void SV_SpawnSpectator (void) -{ - int i; - edict_t *e; - - VectorCopy (vec3_origin, sv_player->v.origin); - VectorCopy (vec3_origin, sv_player->v.view_ofs); - sv_player->v.view_ofs[2] = 22; - - // search for an info_playerstart to spawn the spectator at - for (i=MAX_CLIENTS-1 ; iv.classname), "info_player_start")) - { - VectorCopy (e->v.origin, sv_player->v.origin); - return; - } - } - -} - -/* -================== -SV_Begin_f -================== -*/ -void SV_Begin_f (void) -{ - unsigned pmodel = 0, emodel = 0; - int i; - - if (host_client->state == cs_spawned) - return; // don't begin again - - host_client->state = cs_spawned; - - // handle the case of a level changing while a client was connecting - if ( atoi(Cmd_Argv(1)) != svs.spawncount ) - { - Con_Printf ("SV_Begin_f from different level\n"); - SV_New_f (); - return; - } - - if (host_client->spectator) - { - SV_SpawnSpectator (); - - if (SpectatorConnect) { - // copy spawn parms out of the client_t - for (i=0 ; i< NUM_SPAWN_PARMS ; i++) - (&pr_global_struct->parm1)[i] = host_client->spawn_parms[i]; - - // call the spawn function - pr_global_struct->time = sv.time; - pr_global_struct->self = EDICT_TO_PROG(sv_player); - PR_ExecuteProgram (SpectatorConnect); - } - } - else - { - // copy spawn parms out of the client_t - for (i=0 ; i< NUM_SPAWN_PARMS ; i++) - (&pr_global_struct->parm1)[i] = host_client->spawn_parms[i]; - - // call the spawn function - pr_global_struct->time = sv.time; - pr_global_struct->self = EDICT_TO_PROG(sv_player); - PR_ExecuteProgram (pr_global_struct->ClientConnect); - - // actually spawn the player - pr_global_struct->time = sv.time; - pr_global_struct->self = EDICT_TO_PROG(sv_player); - PR_ExecuteProgram (pr_global_struct->PutClientInServer); - } - - // clear the net statistics, because connecting gives a bogus picture - host_client->netchan.frame_latency = 0; - host_client->netchan.frame_rate = 0; - host_client->netchan.drop_count = 0; - host_client->netchan.good_count = 0; - - //check he's not cheating - - pmodel = atoi(Info_ValueForKey (host_client->userinfo, "pmodel")); - emodel = atoi(Info_ValueForKey (host_client->userinfo, "emodel")); - - if (pmodel != sv.model_player_checksum || - emodel != sv.eyes_player_checksum) - SV_BroadcastPrintf (PRINT_HIGH, "%s WARNING: non standard player/eyes model detected\n", host_client->name); - - sv.paused &= ~2; // FIXME!!! -- Tonik - - // if we are paused, tell the client - if (sv.paused) { - ClientReliableWrite_Begin (host_client, svc_setpause, 2); - ClientReliableWrite_Byte (host_client, sv.paused); - SV_ClientPrintf(host_client, PRINT_HIGH, "Server is paused.\n"); - } - -#if 0 -// -// send a fixangle over the reliable channel to make sure it gets there -// Never send a roll angle, because savegames can catch the server -// in a state where it is expecting the client to correct the angle -// and it won't happen if the game was just loaded, so you wind up -// with a permanent head tilt - ent = EDICT_NUM( 1 + (host_client - svs.clients) ); - MSG_WriteByte (&host_client->netchan.message, svc_setangle); - for (i=0 ; i < 2 ; i++) - MSG_WriteAngle (&host_client->netchan.message, ent->v.angles[i] ); - MSG_WriteAngle (&host_client->netchan.message, 0 ); -#endif -} - -//============================================================================= - -/* -================== -SV_NextDownload_f -================== -*/ -void SV_NextDownload_f (void) -{ - byte buffer[1024]; - int r; - int percent; - int size; - - if (!host_client->download) - return; - - r = host_client->downloadsize - host_client->downloadcount; - if (r > 768) - r = 768; - r = fread (buffer, 1, r, host_client->download); - ClientReliableWrite_Begin (host_client, svc_download, 6+r); - ClientReliableWrite_Short (host_client, r); - - host_client->downloadcount += r; - size = host_client->downloadsize; - if (!size) - size = 1; - percent = host_client->downloadcount*100/size; - ClientReliableWrite_Byte (host_client, percent); - ClientReliableWrite_SZ (host_client, buffer, r); - - if (host_client->downloadcount != host_client->downloadsize) - return; - - fclose (host_client->download); - host_client->download = NULL; - -} - -void OutofBandPrintf(netadr_t where, char *fmt, ...) -{ - va_list argptr; - char send[1024]; - - send[0] = 0xff; - send[1] = 0xff; - send[2] = 0xff; - send[3] = 0xff; - send[4] = A2C_PRINT; - va_start (argptr, fmt); - vsprintf (send+5, fmt, argptr); - va_end (argptr); - - NET_SendPacket (net_serversocket, strlen(send)+1, send, where); -} - -/* -================== -SV_NextUpload -================== -*/ -void SV_NextUpload (void) -{ - int percent; - int size; -// byte buffer[1024]; -// int r; -// client_t *client; - - if (!*host_client->uploadfn) { - SV_ClientPrintf(host_client, PRINT_HIGH, "Upload denied\n"); - ClientReliableWrite_Begin (host_client, svc_stufftext, 8); - ClientReliableWrite_String (host_client, "stopul"); - - // suck out rest of packet - size = MSG_ReadShort (); MSG_ReadByte (); - msg_readcount += size; - return; - } - - size = MSG_ReadShort (); - percent = MSG_ReadByte (); - - if (!host_client->upload) - { - host_client->upload = fopen(host_client->uploadfn, "wb"); - if (!host_client->upload) { - Sys_Printf("Can't create %s\n", host_client->uploadfn); - ClientReliableWrite_Begin (host_client, svc_stufftext, 8); - ClientReliableWrite_String (host_client, "stopul"); - *host_client->uploadfn = 0; - return; - } - Sys_Printf("Receiving %s from %d...\n", host_client->uploadfn, host_client->userid); - if (host_client->remote_snap) - OutofBandPrintf(host_client->snap_from, "Server receiving %s from %d...\n", host_client->uploadfn, host_client->userid); - } - - fwrite (net_message.data + msg_readcount, 1, size, host_client->upload); - msg_readcount += size; - -Con_DPrintf ("UPLOAD: %d received\n", size); - - if (percent != 100) { - ClientReliableWrite_Begin (host_client, svc_stufftext, 8); - ClientReliableWrite_String (host_client, "nextul\n"); - } else { - fclose (host_client->upload); - host_client->upload = NULL; - - Sys_Printf("%s upload completed.\n", host_client->uploadfn); - - if (host_client->remote_snap) { - char *p; - - if ((p = strchr(host_client->uploadfn, '/')) != NULL) - p++; - else - p = host_client->uploadfn; - OutofBandPrintf(host_client->snap_from, "%s upload completed.\nTo download, enter:\ndownload %s\n", - host_client->uploadfn, p); - } - } - -} - -/* -================== -SV_BeginDownload_f -================== -*/ -void SV_BeginDownload_f(void) -{ - char *name; - extern cvar_t allow_download; - extern cvar_t allow_download_skins; - extern cvar_t allow_download_models; - extern cvar_t allow_download_sounds; - extern cvar_t allow_download_maps; - extern int file_from_pak; // ZOID did file come from pak? - - name = Cmd_Argv(1); -// hacked by zoid to allow more conrol over download - // first off, no .. or global allow check - if (strstr (name, "..") || !allow_download.value - // leading dot is no good - || *name == '.' - // leading slash bad as well, must be in subdir - || *name == '/' - // next up, skin check - || (strncmp(name, "skins/", 6) == 0 && !allow_download_skins.value) - // now models - || (strncmp(name, "progs/", 6) == 0 && !allow_download_models.value) - // now sounds - || (strncmp(name, "sound/", 6) == 0 && !allow_download_sounds.value) - // now maps (note special case for maps, must not be in pak) - || (strncmp(name, "maps/", 6) == 0 && !allow_download_maps.value) - // MUST be in a subdirectory - || !strstr (name, "/") ) - { // don't allow anything with .. path - ClientReliableWrite_Begin (host_client, svc_download, 4); - ClientReliableWrite_Short (host_client, -1); - ClientReliableWrite_Byte (host_client, 0); - return; - } - - if (host_client->download) { - fclose (host_client->download); - host_client->download = NULL; - } - - // lowercase name (needed for casesen file systems) - { - char *p; - - for (p = name; *p; p++) - *p = (char)tolower(*p); - } - - - host_client->downloadsize = COM_FOpenFile (name, &host_client->download); - host_client->downloadcount = 0; - - if (!host_client->download - // special check for maps, if it came from a pak file, don't allow - // download ZOID - || (strncmp(name, "maps/", 5) == 0 && file_from_pak)) - { - if (host_client->download) { - fclose(host_client->download); - host_client->download = NULL; - } - - Sys_Printf ("Couldn't download %s to %s\n", name, host_client->name); - ClientReliableWrite_Begin (host_client, svc_download, 4); - ClientReliableWrite_Short (host_client, -1); - ClientReliableWrite_Byte (host_client, 0); - return; - } - - SV_NextDownload_f (); - Sys_Printf ("Downloading %s to %s\n", name, host_client->name); -} - -/* -================== -SV_BeginDownload_f -================== -*/ -void SV_StopDownload_f(void) -{ - if (host_client->download) { - fclose (host_client->download); - host_client->download = NULL; - } -} -//============================================================================= - -/* -================== -SV_Say -================== -*/ - -extern func_t ChatMessage; - -void SV_ClientPrintf2 (client_t *cl, int level, char *fmt, ...); - -void SV_Say (qboolean team) -{ - client_t *client; - int j, tmp, cls = 0; - char *p; - char text[2048]; - - if (Cmd_Argc () < 2) - return; - - p = Cmd_Args(); - - if (*p == '"') - { - p++; - p[strlen(p)-1] = 0; - - } - - strcpy(text, p); - strcat(text, "\n"); - -#if 1 - if (ChatMessage) - { - SV_EndRedirect (); - - G_INT(OFS_PARM0) = PR_SetString(p); - G_FLOAT(OFS_PARM1) = (float)team; - - pr_global_struct->time = sv.time; - pr_global_struct->self = EDICT_TO_PROG(sv_player); - PR_ExecuteProgram (ChatMessage); - SV_BeginRedirect (RD_CLIENT); - if (G_FLOAT(OFS_RETURN)) - return; - } -#endif - - if (host_client->spectator && (!sv_spectalk.value || team)) - strcpy(text, va("[SPEC] %s: %s", host_client->name, text)); - else if (team) - strcpy(text, va("(%s): %s", host_client->name, text)); - else { - strcpy(text, va("%s: %s", host_client->name, text)); - } - - if (fp_messages) { - if (!sv.paused && realtimelockedtill) { - SV_ClientPrintf(host_client, PRINT_CHAT, - "You can't talk for %d more seconds\n", - (int) (host_client->lockedtill - realtime)); - return; - } - tmp = host_client->whensaidhead - fp_messages + 1; - if (tmp < 0) - tmp = 10+tmp; - if (!sv.paused && - host_client->whensaid[tmp] && (realtime-host_client->whensaid[tmp] < fp_persecond)) { - host_client->lockedtill = realtime + fp_secondsdead; - if (fp_msg[0]) - SV_ClientPrintf(host_client, PRINT_CHAT, - "FloodProt: %s\n", fp_msg); - else - SV_ClientPrintf(host_client, PRINT_CHAT, - "FloodProt: You can't talk for %d seconds.\n", fp_secondsdead); - return; - } - host_client->whensaidhead++; - if (host_client->whensaidhead > 9) - host_client->whensaidhead = 0; - host_client->whensaid[host_client->whensaidhead] = realtime; - } - - Sys_Printf ("%s", text); - - for (j = 0, client = svs.clients; j < MAX_CLIENTS; j++, client++) - { - if (client->state != cs_spawned) - continue; - if (host_client->spectator && !sv_spectalk.value) - if (!client->spectator) - continue; - - if (team) - { - // the spectator team - if (host_client->spectator) { - if (!client->spectator) - continue; - } else { - if (strcmp(host_client->team, client->team) || client->spectator) - continue; // on different teams - } - } - - cls |= 1 << j; - SV_ClientPrintf2(client, PRINT_CHAT, "%s", text); - } - - if (!sv.demorecording || !cls) - return; - - // non-team messages should be seen allways, even if not tracking any player - if (!team && ((host_client->spectator && sv_spectalk.value) || !host_client->spectator)) - { - DemoReliableWrite_Begin (dem_all, 0, strlen(text)+3); - } else - DemoReliableWrite_Begin (dem_multiple, cls, strlen(text)+3); - - MSG_WriteByte (demo.buf, svc_print); - MSG_WriteByte (demo.buf, PRINT_CHAT); - MSG_WriteString (demo.buf, text); -} - - -/* -================== -SV_Say_f -================== -*/ -void SV_Say_f(void) -{ - SV_Say (false); -} -/* -================== -SV_Say_Team_f -================== -*/ -void SV_Say_Team_f(void) -{ - SV_Say (true); -} - - - -//============================================================================ - -/* -================= -SV_Pings_f - -The client is showing the scoreboard, so send new ping times for all -clients -================= -*/ -void SV_Pings_f (void) -{ - client_t *client; - int j; - - for (j = 0, client = svs.clients; j < MAX_CLIENTS; j++, client++) - { - if (client->state != cs_spawned) - continue; - - ClientReliableWrite_Begin (host_client, svc_updateping, 4); - ClientReliableWrite_Byte (host_client, j); - ClientReliableWrite_Short (host_client, SV_CalcPing(client)); - ClientReliableWrite_Begin (host_client, svc_updatepl, 4); - ClientReliableWrite_Byte (host_client, j); - ClientReliableWrite_Byte (host_client, client->lossage); - } -} - - - -/* -================== -SV_Kill_f -================== -*/ -void SV_Kill_f (void) -{ - if (sv_player->v.health <= 0) - { - SV_ClientPrintf (host_client, PRINT_HIGH, "Can't suicide -- already dead!\n"); - return; - } - - pr_global_struct->time = sv.time; - pr_global_struct->self = EDICT_TO_PROG(sv_player); - PR_ExecuteProgram (pr_global_struct->ClientKill); -} - -/* -================== -SV_TogglePause -================== -*/ -void SV_TogglePause (const char *msg) -{ - int i; - client_t *cl; - - sv.paused ^= 1; - - if (msg) - SV_BroadcastPrintf (PRINT_HIGH, "%s", msg); - - // send notification to all clients - for (i=0, cl = svs.clients ; istate) - continue; - ClientReliableWrite_Begin (cl, svc_setpause, 2); - ClientReliableWrite_Byte (cl, sv.paused ? 1 : 0); - } -} - - -/* -================== -SV_Pause_f -================== -*/ -void SV_Pause_f (void) -{ -// client_t *cl; - char st[sizeof(host_client->name) + 32]; - - if (!pausable.value) { - SV_ClientPrintf (host_client, PRINT_HIGH, "Pause not allowed.\n"); - return; - } - - if (host_client->spectator) { - SV_ClientPrintf (host_client, PRINT_HIGH, "Spectators can not pause.\n"); - return; - } - - if (!sv.paused) - sprintf (st, "%s paused the game\n", host_client->name); - else - sprintf (st, "%s unpaused the game\n", host_client->name); - - SV_TogglePause(st); -} - - -/* -================= -SV_Drop_f - -The client is going to disconnect, so remove the connection immediately -================= -*/ -void SV_Drop_f (void) -{ - SV_EndRedirect (); - if (!host_client->spectator) - SV_BroadcastPrintf (PRINT_HIGH, "%s dropped\n", host_client->name); - SV_DropClient (host_client); -} - -/* -================= -SV_PTrack_f - -Change the bandwidth estimate for a client -================= -*/ -void SV_PTrack_f (void) -{ - int i; - edict_t *ent, *tent; - - if (!host_client->spectator) - return; - - if (Cmd_Argc() != 2) - { - // turn off tracking - host_client->spec_track = 0; - ent = EDICT_NUM(host_client - svs.clients + 1); - tent = EDICT_NUM(0); - ent->v.goalentity = EDICT_TO_PROG(tent); - return; - } - - i = atoi(Cmd_Argv(1)); - if (i < 0 || i >= MAX_CLIENTS || svs.clients[i].state != cs_spawned || - svs.clients[i].spectator) { - SV_ClientPrintf (host_client, PRINT_HIGH, "Invalid client to track\n"); - host_client->spec_track = 0; - ent = EDICT_NUM(host_client - svs.clients + 1); - tent = EDICT_NUM(0); - ent->v.goalentity = EDICT_TO_PROG(tent); - return; - } - host_client->spec_track = i + 1; // now tracking - - ent = EDICT_NUM(host_client - svs.clients + 1); - tent = EDICT_NUM(i + 1); - ent->v.goalentity = EDICT_TO_PROG(tent); -} - - -/* -================= -SV_Rate_f - -Change the bandwidth estimate for a client -================= -*/ -void SV_Rate_f (void) -{ - int rate; - - if (Cmd_Argc() != 2) - { - SV_ClientPrintf (host_client, PRINT_HIGH, "Current rate is %i\n", - (int)(1.0/host_client->netchan.rate + 0.5)); - return; - } - - rate = SV_BoundRate (atoi(Cmd_Argv(1))); - - SV_ClientPrintf (host_client, PRINT_HIGH, "Net rate set to %i\n", rate); - host_client->netchan.rate = 1.0/rate; -} - - -/* -================= -SV_Msg_f - -Change the message level for a client -================= -*/ -void SV_Msg_f (void) -{ - if (Cmd_Argc() != 2) - { - SV_ClientPrintf (host_client, PRINT_HIGH, "Current msg level is %i\n", - host_client->messagelevel); - return; - } - - host_client->messagelevel = atoi(Cmd_Argv(1)); - - SV_ClientPrintf (host_client, PRINT_HIGH, "Msg level set to %i\n", host_client->messagelevel); -} - -/* -================== -SV_SetInfo_f - -Allow clients to change userinfo -================== -*/ -void SV_SetInfo_f (void) -{ - int i; - char oldval[MAX_INFO_STRING]; - - - if (Cmd_Argc() == 1) - { - Con_Printf ("User info settings:\n"); - Info_Print (host_client->userinfo); - return; - } - - if (Cmd_Argc() != 3) - { - Con_Printf ("usage: setinfo [ ]\n"); - return; - } - - if (Cmd_Argv(1)[0] == '*') - return; // don't set priveledged values - - strcpy(oldval, Info_ValueForKey(host_client->userinfo, Cmd_Argv(1))); - - Info_SetValueForKey (host_client->userinfo, Cmd_Argv(1), Cmd_Argv(2), MAX_INFO_STRING); -// name is extracted below in ExtractFromUserInfo -// Q_strncpyz (host_client->name, Info_ValueForKey (host_client->userinfo, "name") -// , sizeof(host_client->name)); -// SV_FullClientUpdate (host_client, &sv.reliable_datagram); -// host_client->sendinfo = true; - - if (!strcmp(Info_ValueForKey(host_client->userinfo, Cmd_Argv(1)), oldval)) - return; // key hasn't changed - - // process any changed values - SV_ExtractFromUserinfo (host_client); - - i = host_client - svs.clients; - MSG_WriteByte (&sv.reliable_datagram, svc_setinfo); - MSG_WriteByte (&sv.reliable_datagram, i); - MSG_WriteString (&sv.reliable_datagram, Cmd_Argv(1)); - MSG_WriteString (&sv.reliable_datagram, Info_ValueForKey(host_client->userinfo, Cmd_Argv(1))); -} - -/* -================== -SV_ShowServerinfo_f - -Dumps the serverinfo info string -================== -*/ -void SV_ShowServerinfo_f (void) -{ - Info_Print (svs.info); -} - -void SV_NoSnap_f(void) -{ - if (*host_client->uploadfn) { - *host_client->uploadfn = 0; - SV_BroadcastPrintf (PRINT_HIGH, "%s refused remote screenshot\n", host_client->name); - } -} - -void SV_POVlist_f(void) -{ - client_t *cl; - int i; - - for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) - { - if (cl->state != cs_spawned) - continue; - - if (host_client->POVs & (1 << i)) { - Con_Printf("%s\n", cl->name); - } - } -} - -void SV_DemoList_f (void); - -typedef struct -{ - char *name; - void (*func) (void); -} ucmd_t; - -ucmd_t ucmds[] = -{ - {"new", SV_New_f}, - {"modellist", SV_Modellist_f}, - {"soundlist", SV_Soundlist_f}, - {"prespawn", SV_PreSpawn_f}, - {"spawn", SV_Spawn_f}, - {"begin", SV_Begin_f}, - - {"drop", SV_Drop_f}, - {"pings", SV_Pings_f}, - -// issued by hand at client consoles - {"rate", SV_Rate_f}, - {"kill", SV_Kill_f}, - {"pause", SV_Pause_f}, - {"msg", SV_Msg_f}, - - {"say", SV_Say_f}, - {"say_team", SV_Say_Team_f}, - - {"setinfo", SV_SetInfo_f}, - - {"serverinfo", SV_ShowServerinfo_f}, - - {"download", SV_BeginDownload_f}, - {"nextdl", SV_NextDownload_f}, - - {"ptrack", SV_PTrack_f}, //ZOID - used with autocam - - {"snap", SV_NoSnap_f}, - {"povlist", SV_POVlist_f}, - {"stopdownload", SV_StopDownload_f}, - {"demolist", SV_DemoList_f}, - - {NULL, NULL} -}; - -/* -================== -SV_ExecuteUserCommand -================== -*/ -void SV_ExecuteUserCommand (char *s) -{ - ucmd_t *u; - - Cmd_TokenizeString (s); - sv_player = host_client->edict; - - SV_BeginRedirect (RD_CLIENT); - - for (u=ucmds ; u->name ; u++) - if (!strcmp (Cmd_Argv(0), u->name) ) - { - u->func (); - break; - } - - if (!u->name) - Con_Printf ("Bad user command: %s\n", Cmd_Argv(0)); - - SV_EndRedirect (); -} - -/* -=========================================================================== - -USER CMD EXECUTION - -=========================================================================== -*/ - -/* -=============== -SV_CalcRoll - -Used by view and sv_user -=============== -*/ -float SV_CalcRoll (vec3_t angles, vec3_t velocity) -{ - vec3_t forward, right, up; - float sign; - float side; - float value; - - AngleVectors (angles, forward, right, up); - side = DotProduct (velocity, right); - sign = side < 0 ? -1 : 1; - side = fabs(side); - - value = sv_rollangle.value; - - if (side < sv_rollspeed.value) - side = side * value / sv_rollspeed.value; - else - side = value; - - return side*sign; - -} - - - - -//============================================================================ - -vec3_t pmove_mins, pmove_maxs; - -/* -==================== -AddLinksToPmove - -==================== -*/ -void AddLinksToPmove ( areanode_t *node ) -{ - link_t *l, *next; - edict_t *check; - int pl; - int i; - physent_t *pe; - - pl = EDICT_TO_PROG(sv_player); - - // touch linked edicts - for (l = node->solid_edicts.next ; l != &node->solid_edicts ; l = next) - { - next = l->next; - check = EDICT_FROM_AREA(l); - - if (check->v.owner == pl) - continue; // player's own missile - if (check->v.solid == SOLID_BSP - || check->v.solid == SOLID_BBOX - || check->v.solid == SOLID_SLIDEBOX) - { - if (check == sv_player) - continue; - - for (i=0 ; i<3 ; i++) - if (check->v.absmin[i] > pmove_maxs[i] - || check->v.absmax[i] < pmove_mins[i]) - break; - if (i != 3) - continue; - if (pmove.numphysent == MAX_PHYSENTS) - return; - pe = &pmove.physents[pmove.numphysent]; - pmove.numphysent++; - - VectorCopy (check->v.origin, pe->origin); - pe->info = NUM_FOR_EDICT(check); - if (check->v.solid == SOLID_BSP) - pe->model = sv.models[(int)(check->v.modelindex)]; - else - { - pe->model = NULL; - VectorCopy (check->v.mins, pe->mins); - VectorCopy (check->v.maxs, pe->maxs); - } - } - } - -// recurse down both sides - if (node->axis == -1) - return; - - if ( pmove_maxs[node->axis] > node->dist ) - AddLinksToPmove ( node->children[0] ); - if ( pmove_mins[node->axis] < node->dist ) - AddLinksToPmove ( node->children[1] ); -} - - -/* -================ -AddAllEntsToPmove - -For debugging -================ -*/ -void AddAllEntsToPmove (void) -{ - int e; - edict_t *check; - int i; - physent_t *pe; - int pl; - - pl = EDICT_TO_PROG(sv_player); - check = NEXT_EDICT(sv.edicts); - for (e=1 ; efree) - continue; - if (check->v.owner == pl) - continue; - if (check->v.solid == SOLID_BSP - || check->v.solid == SOLID_BBOX - || check->v.solid == SOLID_SLIDEBOX) - { - if (check == sv_player) - continue; - - for (i=0 ; i<3 ; i++) - if (check->v.absmin[i] > pmove_maxs[i] - || check->v.absmax[i] < pmove_mins[i]) - break; - if (i != 3) - continue; - pe = &pmove.physents[pmove.numphysent]; - - VectorCopy (check->v.origin, pe->origin); - pmove.physents[pmove.numphysent].info = e; - if (check->v.solid == SOLID_BSP) - pe->model = sv.models[(int)(check->v.modelindex)]; - else - { - pe->model = NULL; - VectorCopy (check->v.mins, pe->mins); - VectorCopy (check->v.maxs, pe->maxs); - } - - if (++pmove.numphysent == MAX_PHYSENTS) - break; - } - } -} - -/* -=========== -SV_PreRunCmd -=========== -Done before running a player command. Clears the touch array -*/ -byte playertouch[(MAX_EDICTS+7)/8]; - -void SV_PreRunCmd(void) -{ - memset(playertouch, 0, sizeof(playertouch)); -} - -/* -=========== -SV_RunCmd -=========== -*/ -void SV_RunCmd (usercmd_t *ucmd) -{ - edict_t *ent; - int i, n; - int oldmsec; - - cmd = *ucmd; - - // chop up very long command - if (cmd.msec > 50) - { - oldmsec = ucmd->msec; - cmd.msec = oldmsec/2; - SV_RunCmd (&cmd); - cmd.msec = oldmsec/2; - cmd.impulse = 0; - SV_RunCmd (&cmd); - return; - } - - if (!sv_player->v.fixangle) - VectorCopy (ucmd->angles, sv_player->v.v_angle); - - sv_player->v.button0 = ucmd->buttons & 1; - sv_player->v.button2 = (ucmd->buttons & 2)>>1; - if (ucmd->impulse) - sv_player->v.impulse = ucmd->impulse; - -// -// angles -// show 1/3 the pitch angle and all the roll angle - if (sv_player->v.health > 0) - { - if (!sv_player->v.fixangle) - { - sv_player->v.angles[PITCH] = -sv_player->v.v_angle[PITCH]/3; - sv_player->v.angles[YAW] = sv_player->v.v_angle[YAW]; - } - sv_player->v.angles[ROLL] = - SV_CalcRoll (sv_player->v.angles, sv_player->v.velocity)*4; - } - - sv_frametime = ucmd->msec * 0.001; - if (sv_frametime > 0.1) - sv_frametime = 0.1; - - if (!host_client->spectator) - { - pr_global_struct->frametime = sv_frametime; - - pr_global_struct->time = sv.time; - pr_global_struct->self = EDICT_TO_PROG(sv_player); - PR_ExecuteProgram (pr_global_struct->PlayerPreThink); - - SV_RunThink (sv_player); - } - - for (i=0 ; i<3 ; i++) - pmove.origin[i] = sv_player->v.origin[i] + (sv_player->v.mins[i] - player_mins[i]); - VectorCopy (sv_player->v.velocity, pmove.velocity); - VectorCopy (sv_player->v.v_angle, pmove.angles); - - pmove.spectator = host_client->spectator; - pmove.waterjumptime = sv_player->v.teleport_time; - pmove.numphysent = 1; - pmove.physents[0].model = sv.worldmodel; - pmove.cmd = *ucmd; - pmove.dead = sv_player->v.health <= 0; - pmove.oldbuttons = host_client->oldbuttons; - - movevars.entgravity = host_client->entgravity; - movevars.maxspeed = host_client->maxspeed; - - for (i=0 ; i<3 ; i++) - { - pmove_mins[i] = pmove.origin[i] - 256; - pmove_maxs[i] = pmove.origin[i] + 256; - } -#if 1 - AddLinksToPmove ( sv_areanodes ); -#else - AddAllEntsToPmove (); -#endif - -#if 0 -{ - int before, after; - -before = PM_TestPlayerPosition (pmove.origin); - PlayerMove (); -after = PM_TestPlayerPosition (pmove.origin); - -if (sv_player->v.health > 0 && before && !after ) - Con_Printf ("player %s got stuck in playermove!!!!\n", host_client->name); -} -#else - PlayerMove (); -#endif - - host_client->oldbuttons = pmove.oldbuttons; - sv_player->v.teleport_time = pmove.waterjumptime; - sv_player->v.waterlevel = waterlevel; - sv_player->v.watertype = watertype; - if (onground != -1) - { - sv_player->v.flags = (int)sv_player->v.flags | FL_ONGROUND; - sv_player->v.groundentity = EDICT_TO_PROG(EDICT_NUM(pmove.physents[onground].info)); - } - else - sv_player->v.flags = (int)sv_player->v.flags & ~FL_ONGROUND; - for (i=0 ; i<3 ; i++) - sv_player->v.origin[i] = pmove.origin[i] - (sv_player->v.mins[i] - player_mins[i]); - -#if 0 - // truncate velocity the same way the net protocol will - for (i=0 ; i<3 ; i++) - sv_player->v.velocity[i] = (int)pmove.velocity[i]; -#else - VectorCopy (pmove.velocity, sv_player->v.velocity); -#endif - - VectorCopy (pmove.angles, sv_player->v.v_angle); - - if (!host_client->spectator) - { - // link into place and touch triggers - SV_LinkEdict (sv_player, true); - - // touch other objects - for (i=0 ; iv.touch || (playertouch[n/8]&(1<<(n%8)))) - continue; - pr_global_struct->self = EDICT_TO_PROG(ent); - pr_global_struct->other = EDICT_TO_PROG(sv_player); - PR_ExecuteProgram (ent->v.touch); - playertouch[n/8] |= 1 << (n%8); - } - } -} - -/* -=========== -SV_PostRunCmd -=========== -Done after running a player command. -*/ -void SV_PostRunCmd(void) -{ - // run post-think - - if (!host_client->spectator) { - pr_global_struct->time = sv.time; - pr_global_struct->self = EDICT_TO_PROG(sv_player); - PR_ExecuteProgram (pr_global_struct->PlayerPostThink); - SV_RunNewmis (); - } else if (SpectatorThink) { - pr_global_struct->time = sv.time; - pr_global_struct->self = EDICT_TO_PROG(sv_player); - PR_ExecuteProgram (SpectatorThink); - } -} - - -/* -=================== -SV_ExecuteClientMessage - -The current net_message is parsed for the given client -=================== -*/ -void SV_ExecuteClientMessage (client_t *cl) -{ - int c; - char *s; - usercmd_t oldest, oldcmd, newcmd; - client_frame_t *frame; - vec3_t o; - qboolean move_issued = false; //only allow one move command - int checksumIndex; - byte checksum, calculatedChecksum; - int seq_hash; - - // calc ping time - frame = &cl->frames[cl->netchan.incoming_acknowledged & UPDATE_MASK]; - frame->ping_time = realtime - frame->senttime; - - // make sure the reply sequence number matches the incoming - // sequence number - if (cl->netchan.incoming_sequence >= cl->netchan.outgoing_sequence) - cl->netchan.outgoing_sequence = cl->netchan.incoming_sequence; - else - cl->send_message = false; // don't reply, sequences have slipped - - // save time for ping calculations - cl->frames[cl->netchan.outgoing_sequence & UPDATE_MASK].senttime = realtime; - cl->frames[cl->netchan.outgoing_sequence & UPDATE_MASK].ping_time = -1; - - host_client = cl; - sv_player = host_client->edict; - -// seq_hash = (cl->netchan.incoming_sequence & 0xffff) ; // ^ QW_CHECK_HASH; - seq_hash = cl->netchan.incoming_sequence; - - // mark time so clients will know how much to predict - // other players - cl->localtime = sv.time; - cl->delta_sequence = -1; // no delta unless requested - while (1) - { - if (msg_badread) - { - Con_Printf ("SV_ReadClientMessage: badread\n"); - SV_DropClient (cl); - return; - } - - c = MSG_ReadByte (); - if (c == -1) - break; - - switch (c) - { - default: - Con_Printf ("SV_ReadClientMessage: unknown command char\n"); - SV_DropClient (cl); - return; - - case clc_nop: - break; - - case clc_delta: - cl->delta_sequence = MSG_ReadByte (); - break; - - case clc_move: - if (move_issued) - return; // someone is trying to cheat... - - move_issued = true; - - checksumIndex = MSG_GetReadCount(); - checksum = (byte)MSG_ReadByte (); - - // read loss percentage - cl->lossage = MSG_ReadByte(); - - MSG_ReadDeltaUsercmd (&nullcmd, &oldest); - MSG_ReadDeltaUsercmd (&oldest, &oldcmd); - MSG_ReadDeltaUsercmd (&oldcmd, &newcmd); - - if ( cl->state != cs_spawned ) - break; - - // if the checksum fails, ignore the rest of the packet - calculatedChecksum = COM_BlockSequenceCRCByte( - net_message.data + checksumIndex + 1, - MSG_GetReadCount() - checksumIndex - 1, - seq_hash); - - if (calculatedChecksum != checksum) - { - Con_DPrintf ("Failed command checksum for %s(%d) (%d != %d)\n", - cl->name, cl->netchan.incoming_sequence, checksum, calculatedChecksum); - return; - } - - if (!sv.paused) { - SV_PreRunCmd(); - - if (net_drop < 20) - { - while (net_drop > 2) - { - SV_RunCmd (&cl->lastcmd); - net_drop--; - } - if (net_drop > 1) - SV_RunCmd (&oldest); - if (net_drop > 0) - SV_RunCmd (&oldcmd); - } - SV_RunCmd (&newcmd); - - SV_PostRunCmd(); - } - - cl->lastcmd = newcmd; - cl->lastcmd.buttons = 0; // avoid multiple fires on lag - break; - - - case clc_stringcmd: - s = MSG_ReadString (); - SV_ExecuteUserCommand (s); - break; - - case clc_tmove: - o[0] = MSG_ReadCoord(); - o[1] = MSG_ReadCoord(); - o[2] = MSG_ReadCoord(); - // only allowed by spectators - if (host_client->spectator) { - VectorCopy(o, sv_player->v.origin); - SV_LinkEdict(sv_player, false); - } - break; - - case clc_upload: - SV_NextUpload(); - break; - - } - } -} - -/* -============== -SV_UserInit -============== -*/ -void SV_UserInit (void) -{ - Cvar_RegisterVariable (&sv_rollspeed); - Cvar_RegisterVariable (&sv_rollangle); - Cvar_RegisterVariable (&sv_spectalk); - Cvar_RegisterVariable (&sv_mapcheck); -} +/* +Copyright (C) 1996-1997 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. + +*/ +// sv_user.c -- server code for moving users + +#include "qwsvdef.h" + +edict_t *sv_player; + +usercmd_t cmd; + +cvar_t sv_rollspeed = {"cl_rollspeed", "200"}; +cvar_t sv_rollangle = {"cl_rollangle", "2.0"}; + +cvar_t sv_spectalk = {"sv_spectalk", "1"}; + +cvar_t sv_mapcheck = {"sv_mapcheck", "1"}; + +extern vec3_t player_mins; + +extern int fp_messages, fp_persecond, fp_secondsdead; +extern char fp_msg[]; +extern cvar_t pausable; + +/* +============================================================ + +USER STRINGCMD EXECUTION + +host_client and sv_player will be valid. +============================================================ +*/ + +/* +================ +SV_New_f + +Sends the first message from the server to a connected client. +This will be sent on the initial connection and upon each server load. +================ +*/ +void SV_New_f (void) +{ + int i; + char *gamedir; + int playernum; + extern cvar_t sv_login; + + if (host_client->state == cs_spawned) + return; + + if (!host_client->connection_started) + host_client->connection_started = realtime; + + // do not proceed if realip is unknown + if (host_client->realip.ip[0] == 0) + { + MSG_WriteByte (&host_client->netchan.message, svc_stufftext); + MSG_WriteString (&host_client->netchan.message, va("packet %s \"ip %d %d\"\ncmd new\n", NET_AdrToString(net_local_adr), host_client - svs.clients, host_client->realip_num)); + if (realtime - host_client->connection_started > 5) + { + Netchan_OutOfBandPrint (net_serversocket, net_from, "%c\nfaild to validate client's IP\n\n", A2C_PRINT); + SV_DropClient (host_client); + } + + return; + } + + // rip_vip means that client can be connected if he has VIP for he's real ip + // drop him if he hasn't + if (host_client->rip_vip ) + { + if ((host_client->vip = SV_VIPbyIP(host_client->realip)) == 0) + { + Con_Printf ("%s:full connect\n", NET_AdrToString (net_from)); + Netchan_OutOfBandPrint (net_serversocket, net_from, "%c\nserver is full\n\n", A2C_PRINT); + SV_DropClient (host_client);; + return; + } + + host_client->rip_vip = 0; + } + + // we can be connected now, announce it, and possibly login + if (host_client->state == cs_preconnected) + { + // get highest VIP level + if (host_client->vip < SV_VIPbyIP(host_client->realip)) + host_client->vip = SV_VIPbyIP(host_client->realip); + + if (host_client->vip && host_client->spectator) + Sys_Printf ("VIP spectator %s connected\n", host_client->name); + else if (host_client->spectator) + Sys_Printf ("Spectator %s connected\n", host_client->name); + else + Sys_Printf ("Client %s connected\n", host_client->name); + + Info_SetValueForStarKey (host_client->userinfo, "*VIP", host_client->vip ? va("%d", host_client->vip) : "", MAX_INFO_STRING); + + if (!SV_Login(host_client)) + return; + + // now we are connected + host_client->state = cs_connected; + } + + if (!host_client->logged && sv_login.value) + return; // not so fast; + +// send the info about the new client to all connected clients +// SV_FullClientUpdate (host_client, &sv.reliable_datagram); +// host_client->sendinfo = true; + + gamedir = Info_ValueForKey (svs.info, "*gamedir"); + if (!gamedir[0]) + gamedir = "qw"; + +//NOTE: This doesn't go through ClientReliableWrite since it's before the user +//spawns. These functions are written to not overflow + if (host_client->num_backbuf) { + Con_Printf("WARNING %s: [SV_New] Back buffered (%d0), clearing\n", host_client->name, host_client->netchan.message.cursize); + host_client->num_backbuf = 0; + SZ_Clear(&host_client->netchan.message); + } + + // send the serverdata + MSG_WriteByte (&host_client->netchan.message, svc_serverdata); + MSG_WriteLong (&host_client->netchan.message, PROTOCOL_VERSION); + MSG_WriteLong (&host_client->netchan.message, svs.spawncount); + MSG_WriteString (&host_client->netchan.message, gamedir); + + playernum = NUM_FOR_EDICT(host_client->edict)-1; + if (host_client->spectator) + playernum |= 128; + MSG_WriteByte (&host_client->netchan.message, playernum); + + // send full levelname + MSG_WriteString (&host_client->netchan.message, PR_GetString(sv.edicts->v.message)); + + // send the movevars + MSG_WriteFloat(&host_client->netchan.message, movevars.gravity); + MSG_WriteFloat(&host_client->netchan.message, movevars.stopspeed); + MSG_WriteFloat(&host_client->netchan.message, movevars.maxspeed); + MSG_WriteFloat(&host_client->netchan.message, movevars.spectatormaxspeed); + MSG_WriteFloat(&host_client->netchan.message, movevars.accelerate); + MSG_WriteFloat(&host_client->netchan.message, movevars.airaccelerate); + MSG_WriteFloat(&host_client->netchan.message, movevars.wateraccelerate); + MSG_WriteFloat(&host_client->netchan.message, movevars.friction); + MSG_WriteFloat(&host_client->netchan.message, movevars.waterfriction); + MSG_WriteFloat(&host_client->netchan.message, movevars.entgravity); + + // send music + MSG_WriteByte (&host_client->netchan.message, svc_cdtrack); + MSG_WriteByte (&host_client->netchan.message, sv.edicts->v.sounds); + + // send server info string + MSG_WriteByte (&host_client->netchan.message, svc_stufftext); + MSG_WriteString (&host_client->netchan.message, va("fullserverinfo \"%s\"\n", svs.info) ); +} + +void SV_New2_f (void) +{ + char *gamedir; + int playernum; + extern cvar_t sv_login; + + if (host_client->state == cs_spawned) + return; + + if (host_client->realip.ip[0] == 0) + return; // don't cheat! + + if (!host_client->logged && sv_login.value) + return; // not so fast; + + host_client->state = cs_connected; + //host_client->connection_started = realtime; + + // send the info about the new client to all connected clients +// SV_FullClientUpdate (host_client, &sv.reliable_datagram); +// host_client->sendinfo = true; + + gamedir = Info_ValueForKey (svs.info, "*gamedir"); + if (!gamedir[0]) + gamedir = "qw"; + +//NOTE: This doesn't go through ClientReliableWrite since it's before the user +//spawns. These functions are written to not overflow + if (host_client->num_backbuf) { + Con_Printf("WARNING %s: [SV_New] Back buffered (%d0), clearing\n", host_client->name, host_client->netchan.message.cursize); + host_client->num_backbuf = 0; + SZ_Clear(&host_client->netchan.message); + } + + // send the serverdata + MSG_WriteByte (&host_client->netchan.message, svc_serverdata); + MSG_WriteLong (&host_client->netchan.message, PROTOCOL_VERSION); + MSG_WriteLong (&host_client->netchan.message, svs.spawncount); + MSG_WriteString (&host_client->netchan.message, gamedir); + + playernum = NUM_FOR_EDICT(host_client->edict)-1; + if (host_client->spectator) + playernum |= 128; + MSG_WriteByte (&host_client->netchan.message, playernum); + + // send full levelname + MSG_WriteString (&host_client->netchan.message, PR_GetString(sv.edicts->v.message)); + + // send the movevars + MSG_WriteFloat(&host_client->netchan.message, movevars.gravity); + MSG_WriteFloat(&host_client->netchan.message, movevars.stopspeed); + MSG_WriteFloat(&host_client->netchan.message, movevars.maxspeed); + MSG_WriteFloat(&host_client->netchan.message, movevars.spectatormaxspeed); + MSG_WriteFloat(&host_client->netchan.message, movevars.accelerate); + MSG_WriteFloat(&host_client->netchan.message, movevars.airaccelerate); + MSG_WriteFloat(&host_client->netchan.message, movevars.wateraccelerate); + MSG_WriteFloat(&host_client->netchan.message, movevars.friction); + MSG_WriteFloat(&host_client->netchan.message, movevars.waterfriction); + MSG_WriteFloat(&host_client->netchan.message, movevars.entgravity); + + // send music + MSG_WriteByte (&host_client->netchan.message, svc_cdtrack); + MSG_WriteByte (&host_client->netchan.message, sv.edicts->v.sounds); + + // send server info string + MSG_WriteByte (&host_client->netchan.message, svc_stufftext); + MSG_WriteString (&host_client->netchan.message, va("fullserverinfo \"%s\"\n", svs.info) ); +} + +/* +================== +SV_Soundlist_f +================== +*/ +void SV_Soundlist_f (void) +{ + char **s; + unsigned n; + + if (host_client->state != cs_connected) + { + Con_Printf ("soundlist not valid -- already spawned\n"); + return; + } + + // handle the case of a level changing while a client was connecting + if ( atoi(Cmd_Argv(1)) != svs.spawncount ) + { + Con_Printf ("SV_Soundlist_f from different level\n"); + SV_New_f (); + return; + } + + n = atoi(Cmd_Argv(2)); + if (n >= MAX_SOUNDS) { + SV_ClientPrintf (host_client, PRINT_HIGH, + "SV_Soundlist_f: Invalid soundlist index\n"); + SV_DropClient (host_client); + return; + } + +//NOTE: This doesn't go through ClientReliableWrite since it's before the user +//spawns. These functions are written to not overflow + if (host_client->num_backbuf) { + Con_Printf("WARNING %s: [SV_Soundlist] Back buffered (%d0), clearing\n", host_client->name, host_client->netchan.message.cursize); + host_client->num_backbuf = 0; + SZ_Clear(&host_client->netchan.message); + } + + MSG_WriteByte (&host_client->netchan.message, svc_soundlist); + MSG_WriteByte (&host_client->netchan.message, n); + for (s = sv.sound_precache+1 + n ; + *s && host_client->netchan.message.cursize < (MAX_MSGLEN/2); + s++, n++) + MSG_WriteString (&host_client->netchan.message, *s); + + MSG_WriteByte (&host_client->netchan.message, 0); + + // next msg + if (*s) + MSG_WriteByte (&host_client->netchan.message, n); + else + MSG_WriteByte (&host_client->netchan.message, 0); +} + +/* +================== +SV_Modellist_f +================== +*/ +void SV_Modellist_f (void) +{ + char **s; + unsigned n; + + if (host_client->state != cs_connected) + { + Con_Printf ("modellist not valid -- already spawned\n"); + return; + } + + // handle the case of a level changing while a client was connecting + if ( atoi(Cmd_Argv(1)) != svs.spawncount ) + { + Con_Printf ("SV_Modellist_f from different level\n"); + SV_New_f (); + return; + } + + n = atoi(Cmd_Argv(2)); + if (n >= MAX_MODELS) { + SV_ClientPrintf (host_client, PRINT_HIGH, + "SV_Modellist_f: Invalid modellist index\n"); + SV_DropClient (host_client); + return; + } + +//NOTE: This doesn't go through ClientReliableWrite since it's before the user +//spawns. These functions are written to not overflow + if (host_client->num_backbuf) { + Con_Printf("WARNING %s: [SV_Modellist] Back buffered (%d0), clearing\n", host_client->name, host_client->netchan.message.cursize); + host_client->num_backbuf = 0; + SZ_Clear(&host_client->netchan.message); + } + + MSG_WriteByte (&host_client->netchan.message, svc_modellist); + MSG_WriteByte (&host_client->netchan.message, n); + for (s = sv.model_precache+1+n ; + *s && host_client->netchan.message.cursize < (MAX_MSGLEN/2); + s++, n++) + MSG_WriteString (&host_client->netchan.message, *s); + MSG_WriteByte (&host_client->netchan.message, 0); + + // next msg + if (*s) + MSG_WriteByte (&host_client->netchan.message, n); + else + MSG_WriteByte (&host_client->netchan.message, 0); +} + +/* +================== +SV_PreSpawn_f +================== +*/ +void SV_PreSpawn_f (void) +{ + unsigned buf; + unsigned check; + + if (host_client->state != cs_connected) + { + Con_Printf ("prespawn not valid -- already spawned\n"); + return; + } + + // handle the case of a level changing while a client was connecting + if ( atoi(Cmd_Argv(1)) != svs.spawncount ) + { + Con_Printf ("SV_PreSpawn_f from different level\n"); + SV_New_f (); + return; + } + + buf = atoi(Cmd_Argv(2)); + if (buf >= sv.num_signon_buffers) + buf = 0; + + if (!buf) { + // should be three numbers following containing checksums + check = atoi(Cmd_Argv(3)); + +// Con_DPrintf("Client check = %d\n", check); + + if (sv_mapcheck.value && check != sv.worldmodel->checksum && + check != sv.worldmodel->checksum2) { + SV_ClientPrintf (host_client, PRINT_HIGH, + "Map model file does not match (%s), %i != %i/%i.\n" + "You may need a new version of the map, or the proper install files.\n", + sv.modelname, check, sv.worldmodel->checksum, sv.worldmodel->checksum2); + SV_DropClient (host_client); + return; + } + host_client->checksum = check; + } + +//NOTE: This doesn't go through ClientReliableWrite since it's before the user +//spawns. These functions are written to not overflow + if (host_client->num_backbuf) { + Con_Printf("WARNING %s: [SV_PreSpawn] Back buffered (%d0), clearing\n", host_client->name, host_client->netchan.message.cursize); + host_client->num_backbuf = 0; + SZ_Clear(&host_client->netchan.message); + } + + SZ_Write (&host_client->netchan.message, + sv.signon_buffers[buf], + sv.signon_buffer_size[buf]); + + buf++; + if (buf == sv.num_signon_buffers) + { // all done prespawning + MSG_WriteByte (&host_client->netchan.message, svc_stufftext); + MSG_WriteString (&host_client->netchan.message, va("cmd spawn %i 0\n",svs.spawncount) ); + } + else + { // need to prespawn more + MSG_WriteByte (&host_client->netchan.message, svc_stufftext); + MSG_WriteString (&host_client->netchan.message, + va("cmd prespawn %i %i\n", svs.spawncount, buf) ); + } +} + +/* +================== +SV_Spawn_f +================== +*/ +void SV_Spawn_f (void) +{ + int i; + client_t *client; + edict_t *ent; + eval_t *val; + unsigned n; + + if (host_client->state != cs_connected) + { + Con_Printf ("Spawn not valid -- already spawned\n"); + return; + } + +// handle the case of a level changing while a client was connecting + if ( atoi(Cmd_Argv(1)) != svs.spawncount ) + { + Con_Printf ("SV_Spawn_f from different level\n"); + SV_New_f (); + return; + } + + n = atoi(Cmd_Argv(2)); + if (n >= MAX_CLIENTS) { + SV_ClientPrintf (host_client, PRINT_HIGH, + "SV_Spawn_f: Invalid client start\n"); + SV_DropClient (host_client); + return; + } + +// send all current names, colors, and frag counts + // FIXME: is this a good thing? + SZ_Clear (&host_client->netchan.message); + +// send current status of all other players + + // normally this could overflow, but no need to check due to backbuf + for (i=n, client = svs.clients + n ; inetchan.message.cursize < (MAX_MSGLEN/2); i++, client++) + SV_FullClientUpdateToClient (client, host_client); + + if (i < MAX_CLIENTS) { + MSG_WriteByte (&host_client->netchan.message, svc_stufftext); + MSG_WriteString (&host_client->netchan.message, + va("cmd spawn %i %d\n", svs.spawncount, i) ); + return; + } + +// send all current light styles + for (i=0 ; iedict; + + memset (&ent->v, 0, progs->entityfields * 4); + ent->v.colormap = NUM_FOR_EDICT(ent); + ent->v.team = 0; // FIXME + ent->v.netname = PR_SetString(host_client->name); + if (pr_teamfield) + E_INT(ent, pr_teamfield) = PR_SetString(host_client->team); + + host_client->entgravity = 1.0; + val = GetEdictFieldValue(ent, "gravity"); + if (val) + val->_float = 1.0; + host_client->maxspeed = sv_maxspeed.value; + val = GetEdictFieldValue(ent, "maxspeed"); + if (val) + val->_float = sv_maxspeed.value; + +// +// force stats to be updated +// + memset (host_client->stats, 0, sizeof(host_client->stats)); + + ClientReliableWrite_Begin (host_client, svc_updatestatlong, 6); + ClientReliableWrite_Byte (host_client, STAT_TOTALSECRETS); + ClientReliableWrite_Long (host_client, pr_global_struct->total_secrets); + + ClientReliableWrite_Begin (host_client, svc_updatestatlong, 6); + ClientReliableWrite_Byte (host_client, STAT_TOTALMONSTERS); + ClientReliableWrite_Long (host_client, pr_global_struct->total_monsters); + + ClientReliableWrite_Begin (host_client, svc_updatestatlong, 6); + ClientReliableWrite_Byte (host_client, STAT_SECRETS); + ClientReliableWrite_Long (host_client, pr_global_struct->found_secrets); + + ClientReliableWrite_Begin (host_client, svc_updatestatlong, 6); + ClientReliableWrite_Byte (host_client, STAT_MONSTERS); + ClientReliableWrite_Long (host_client, pr_global_struct->killed_monsters); + + // get the client to check and download skins + // when that is completed, a begin command will be issued + ClientReliableWrite_Begin (host_client, svc_stufftext, 8); + ClientReliableWrite_String (host_client, "skins\n" ); + +} + +/* +================== +SV_SpawnSpectator +================== +*/ +void SV_SpawnSpectator (void) +{ + int i; + edict_t *e; + + VectorCopy (vec3_origin, sv_player->v.origin); + VectorCopy (vec3_origin, sv_player->v.view_ofs); + sv_player->v.view_ofs[2] = 22; + + // search for an info_playerstart to spawn the spectator at + for (i=MAX_CLIENTS-1 ; iv.classname), "info_player_start")) + { + VectorCopy (e->v.origin, sv_player->v.origin); + return; + } + } + +} + +/* +================== +SV_Begin_f +================== +*/ +void SV_Begin_f (void) +{ + unsigned pmodel = 0, emodel = 0; + int i; + + if (host_client->state == cs_spawned) + return; // don't begin again + + host_client->state = cs_spawned; + + // handle the case of a level changing while a client was connecting + if ( atoi(Cmd_Argv(1)) != svs.spawncount ) + { + Con_Printf ("SV_Begin_f from different level\n"); + SV_New_f (); + return; + } + + if (host_client->spectator) + { + SV_SpawnSpectator (); + + if (SpectatorConnect) { + // copy spawn parms out of the client_t + for (i=0 ; i< NUM_SPAWN_PARMS ; i++) + (&pr_global_struct->parm1)[i] = host_client->spawn_parms[i]; + + // call the spawn function + pr_global_struct->time = sv.time; + pr_global_struct->self = EDICT_TO_PROG(sv_player); + G_FLOAT(OFS_PARM0) = (float) host_client->vip; + PR_ExecuteProgram (SpectatorConnect); + } + } + else + { + // copy spawn parms out of the client_t + for (i=0 ; i< NUM_SPAWN_PARMS ; i++) + (&pr_global_struct->parm1)[i] = host_client->spawn_parms[i]; + + // call the spawn function + pr_global_struct->time = sv.time; + pr_global_struct->self = EDICT_TO_PROG(sv_player); + G_FLOAT(OFS_PARM0) = (float) host_client->vip; + PR_ExecuteProgram (pr_global_struct->ClientConnect); + + // actually spawn the player + pr_global_struct->time = sv.time; + pr_global_struct->self = EDICT_TO_PROG(sv_player); + PR_ExecuteProgram (pr_global_struct->PutClientInServer); + } + + // clear the net statistics, because connecting gives a bogus picture + host_client->netchan.frame_latency = 0; + host_client->netchan.frame_rate = 0; + host_client->netchan.drop_count = 0; + host_client->netchan.good_count = 0; + + //check he's not cheating + + if ( !*Info_ValueForKey (host_client->userinfo, "pmodel") || + !*Info_ValueForKey (host_client->userinfo, "pmodel")) + { + SV_BroadcastPrintf (PRINT_HIGH, "%s WARNING: missing player/eyes model checksum\n", host_client->name); + } else { + pmodel = atoi(Info_ValueForKey (host_client->userinfo, "pmodel")); + emodel = atoi(Info_ValueForKey (host_client->userinfo, "emodel")); + + if (pmodel != sv.model_player_checksum || + emodel != sv.eyes_player_checksum) + SV_BroadcastPrintf (PRINT_HIGH, "%s WARNING: non standard player/eyes model detected\n", host_client->name); + } + + sv.paused &= ~2; // FIXME!!! -- Tonik + + // if we are paused, tell the client + if (sv.paused) { + ClientReliableWrite_Begin (host_client, svc_setpause, 2); + ClientReliableWrite_Byte (host_client, sv.paused); + SV_ClientPrintf(host_client, PRINT_HIGH, "Server is paused.\n"); + } + +#if 0 +// +// send a fixangle over the reliable channel to make sure it gets there +// Never send a roll angle, because savegames can catch the server +// in a state where it is expecting the client to correct the angle +// and it won't happen if the game was just loaded, so you wind up +// with a permanent head tilt + ent = EDICT_NUM( 1 + (host_client - svs.clients) ); + MSG_WriteByte (&host_client->netchan.message, svc_setangle); + for (i=0 ; i < 2 ; i++) + MSG_WriteAngle (&host_client->netchan.message, ent->v.angles[i] ); + MSG_WriteAngle (&host_client->netchan.message, 0 ); +#endif +} + +//============================================================================= + +/* +================== +SV_NextDownload_f +================== +*/ +void SV_NextDownload_f (void) +{ + byte buffer[1024]; + int r; + int percent; + int size; + + if (!host_client->download) + return; + + r = host_client->downloadsize - host_client->downloadcount; + if (r > 768) + r = 768; + r = fread (buffer, 1, r, host_client->download); + ClientReliableWrite_Begin (host_client, svc_download, 6+r); + ClientReliableWrite_Short (host_client, r); + + host_client->downloadcount += r; + size = host_client->downloadsize; + if (!size) + size = 1; + percent = host_client->downloadcount*100/size; + ClientReliableWrite_Byte (host_client, percent); + ClientReliableWrite_SZ (host_client, buffer, r); + + if (host_client->downloadcount != host_client->downloadsize) + return; + + fclose (host_client->download); + host_client->download = NULL; + +} + +void OutofBandPrintf(netadr_t where, char *fmt, ...) +{ + va_list argptr; + char send[1024]; + + send[0] = 0xff; + send[1] = 0xff; + send[2] = 0xff; + send[3] = 0xff; + send[4] = A2C_PRINT; + va_start (argptr, fmt); + vsprintf (send+5, fmt, argptr); + va_end (argptr); + + NET_SendPacket (net_serversocket, strlen(send)+1, send, where); +} + +/* +================== +SV_NextUpload +================== +*/ +void SV_NextUpload (void) +{ + int percent; + int size; +// byte buffer[1024]; +// int r; +// client_t *client; + + if (!*host_client->uploadfn) { + SV_ClientPrintf(host_client, PRINT_HIGH, "Upload denied\n"); + ClientReliableWrite_Begin (host_client, svc_stufftext, 8); + ClientReliableWrite_String (host_client, "stopul"); + + // suck out rest of packet + size = MSG_ReadShort (); MSG_ReadByte (); + msg_readcount += size; + return; + } + + size = MSG_ReadShort (); + percent = MSG_ReadByte (); + + if (!host_client->upload) + { + host_client->upload = fopen(host_client->uploadfn, "wb"); + if (!host_client->upload) { + Sys_Printf("Can't create %s\n", host_client->uploadfn); + ClientReliableWrite_Begin (host_client, svc_stufftext, 8); + ClientReliableWrite_String (host_client, "stopul"); + *host_client->uploadfn = 0; + return; + } + Sys_Printf("Receiving %s from %d...\n", host_client->uploadfn, host_client->userid); + if (host_client->remote_snap) + OutofBandPrintf(host_client->snap_from, "Server receiving %s from %d...\n", host_client->uploadfn, host_client->userid); + } + + fwrite (net_message.data + msg_readcount, 1, size, host_client->upload); + msg_readcount += size; + +Con_DPrintf ("UPLOAD: %d received\n", size); + + if (percent != 100) { + ClientReliableWrite_Begin (host_client, svc_stufftext, 8); + ClientReliableWrite_String (host_client, "nextul\n"); + } else { + fclose (host_client->upload); + host_client->upload = NULL; + + Sys_Printf("%s upload completed.\n", host_client->uploadfn); + + if (host_client->remote_snap) { + char *p; + + if ((p = strchr(host_client->uploadfn, '/')) != NULL) + p++; + else + p = host_client->uploadfn; + OutofBandPrintf(host_client->snap_from, "%s upload completed.\nTo download, enter:\ndownload %s\n", + host_client->uploadfn, p); + } + } + +} + +/* +================== +SV_BeginDownload_f +================== +*/ +void SV_BeginDownload_f(void) +{ + char *name, n[MAX_OSPATH]; + extern cvar_t allow_download; + extern cvar_t allow_download_skins; + extern cvar_t allow_download_models; + extern cvar_t allow_download_sounds; + extern cvar_t allow_download_maps; + extern cvar_t allow_download_demos; + extern cvar_t sv_demoDir; + extern int file_from_pak; // ZOID did file come from pak? + + name = Cmd_Argv(1); +// hacked by zoid to allow more conrol over download + // first off, no .. or global allow check + if (strstr (name, "..") || !allow_download.value + // leading dot is no good + || *name == '.' + // leading slash bad as well, must be in subdir + || *name == '/' + // next up, skin check + || (strncmp(name, "skins/", 6) == 0 && !allow_download_skins.value) + // now models + || (strncmp(name, "progs/", 6) == 0 && !allow_download_models.value) + // now sounds + || (strncmp(name, "sound/", 6) == 0 && !allow_download_sounds.value) + // now maps (note special case for maps, must not be in pak) + || (strncmp(name, "maps/", 5) == 0 && !allow_download_maps.value) + // now demos + || (strncmp(name, "demos/", 6) == 0 && !allow_download_demos.value) + || (strncmp(name, "demonum/", 8) == 0 && !allow_download_demos.value) + // MUST be in a subdirectory + || (!strstr (name, "/") ) ) + { // don't allow anything with .. path + ClientReliableWrite_Begin (host_client, svc_download, 4); + ClientReliableWrite_Short (host_client, -1); + ClientReliableWrite_Byte (host_client, 0); + return; + } + + if (host_client->download) { + fclose (host_client->download); + host_client->download = NULL; + } + + if ( !strncmp(name, "demos/", 6) && sv_demoDir.string[0]) { + sprintf(n, "%s/%s", sv_demoDir.string, name + 6); + name = n; + } else if (!strncmp(name, "demonum/", 8)) { + int num; + + if ((num = atoi(name + 8)) == 0 && name[8] != '0') + { + Con_Printf("usage: download demonum/nun\n"); + + ClientReliableWrite_Begin (host_client, svc_download, 4); + ClientReliableWrite_Short (host_client, -1); + ClientReliableWrite_Byte (host_client, 0); + return; + } + name = SV_DemoNum(num); + if (!name) { + Con_Printf("demo num %d not found\n", num); + + ClientReliableWrite_Begin (host_client, svc_download, 4); + ClientReliableWrite_Short (host_client, -1); + ClientReliableWrite_Byte (host_client, 0); + return; + } + Con_Printf("downloading demos/%s\n",name); + sprintf(n, "download demos/%s\n", name); + + ClientReliableWrite_Begin (host_client, svc_stufftext,strlen(n) + 2); + ClientReliableWrite_String (host_client, n); + return; + } + + + // lowercase name (needed for casesen file systems) + { + char *p; + + for (p = name; *p; p++) + *p = (char)tolower(*p); + } + + + host_client->downloadsize = COM_FOpenFile (name, &host_client->download); + host_client->downloadcount = 0; + + if (!host_client->download + // special check for maps, if it came from a pak file, don't allow + // download ZOID + || (strncmp(name, "maps/", 5) == 0 && file_from_pak)) + { + if (host_client->download) { + fclose(host_client->download); + host_client->download = NULL; + } + + Sys_Printf ("Couldn't download %s to %s\n", name, host_client->name); + ClientReliableWrite_Begin (host_client, svc_download, 4); + ClientReliableWrite_Short (host_client, -1); + ClientReliableWrite_Byte (host_client, 0); + return; + } + + SV_NextDownload_f (); + Sys_Printf ("Downloading %s to %s\n", name, host_client->name); +} + +/* +================== +SV_BeginDownload_f +================== +*/ +void SV_StopDownload_f(void) +{ + if (host_client->download) { + fclose (host_client->download); + host_client->download = NULL; + } +} +//============================================================================= + +/* +================== +SV_Say +================== +*/ + +extern func_t ChatMessage; + +void SV_ClientPrintf2 (client_t *cl, int level, char *fmt, ...); + +void SV_Say (qboolean team) +{ + client_t *client; + int j, tmp, cls = 0; + char *p; + char text[2048]; + + if (Cmd_Argc () < 2) + return; + + p = Cmd_Args(); + + if (*p == '"') + { + p++; + p[strlen(p)-1] = 0; + + } + + strcpy(text, p); + strcat(text, "\n"); + + if (!host_client->logged) + { + SV_ParseLogin(host_client); + return; + } + +#if 1 + if (ChatMessage) + { + SV_EndRedirect (); + + G_INT(OFS_PARM0) = PR_SetTmpString(p); + G_FLOAT(OFS_PARM1) = (float)team; + + pr_global_struct->time = sv.time; + pr_global_struct->self = EDICT_TO_PROG(sv_player); + PR_ExecuteProgram (ChatMessage); + SV_BeginRedirect (RD_CLIENT); + if (G_FLOAT(OFS_RETURN)) + return; + } +#endif + + if (host_client->spectator && (!sv_spectalk.value || team)) + strcpy(text, va("[SPEC] %s: %s", host_client->name, text)); + else if (team) + strcpy(text, va("(%s): %s", host_client->name, text)); + else { + strcpy(text, va("%s: %s", host_client->name, text)); + } + + if (fp_messages) { + if (!sv.paused && realtimelockedtill) { + SV_ClientPrintf(host_client, PRINT_CHAT, + "You can't talk for %d more seconds\n", + (int) (host_client->lockedtill - realtime)); + return; + } + tmp = host_client->whensaidhead - fp_messages + 1; + if (tmp < 0) + tmp = 10+tmp; + if (!sv.paused && + host_client->whensaid[tmp] && (realtime-host_client->whensaid[tmp] < fp_persecond)) { + host_client->lockedtill = realtime + fp_secondsdead; + if (fp_msg[0]) + SV_ClientPrintf(host_client, PRINT_CHAT, + "FloodProt: %s\n", fp_msg); + else + SV_ClientPrintf(host_client, PRINT_CHAT, + "FloodProt: You can't talk for %d seconds.\n", fp_secondsdead); + return; + } + host_client->whensaidhead++; + if (host_client->whensaidhead > 9) + host_client->whensaidhead = 0; + host_client->whensaid[host_client->whensaidhead] = realtime; + } + + Sys_Printf ("%s", text); + + for (j = 0, client = svs.clients; j < MAX_CLIENTS; j++, client++) + { + if (client->state != cs_spawned) + continue; + if (host_client->spectator && !sv_spectalk.value) + if (!client->spectator) + continue; + + if (team) + { + // the spectator team + if (host_client->spectator) { + if (!client->spectator) + continue; + } else { + if (strcmp(host_client->team, client->team) || client->spectator) + continue; // on different teams + } + } + + cls |= 1 << j; + SV_ClientPrintf2(client, PRINT_CHAT, "%s", text); + } + + if (!sv.demorecording || !cls) + return; + + // non-team messages should be seen allways, even if not tracking any player + if (!team && ((host_client->spectator && sv_spectalk.value) || !host_client->spectator)) + { + DemoWrite_Begin (dem_all, 0, strlen(text)+3); + } else + DemoWrite_Begin (dem_multiple, cls, strlen(text)+3); + + MSG_WriteByte ((sizebuf_t*)demo.dbuf, svc_print); + MSG_WriteByte ((sizebuf_t*)demo.dbuf, PRINT_CHAT); + MSG_WriteString ((sizebuf_t*)demo.dbuf, text); +} + + +/* +================== +SV_Say_f +================== +*/ +void SV_Say_f(void) +{ + SV_Say (false); +} +/* +================== +SV_Say_Team_f +================== +*/ +void SV_Say_Team_f(void) +{ + SV_Say (true); +} + + + +//============================================================================ + +/* +================= +SV_Pings_f + +The client is showing the scoreboard, so send new ping times for all +clients +================= +*/ +void SV_Pings_f (void) +{ + client_t *client; + int j; + + for (j = 0, client = svs.clients; j < MAX_CLIENTS; j++, client++) + { + if (client->state != cs_spawned) + continue; + + ClientReliableWrite_Begin (host_client, svc_updateping, 4); + ClientReliableWrite_Byte (host_client, j); + ClientReliableWrite_Short (host_client, SV_CalcPing(client)); + ClientReliableWrite_Begin (host_client, svc_updatepl, 4); + ClientReliableWrite_Byte (host_client, j); + ClientReliableWrite_Byte (host_client, client->lossage); + } +} + + +/* +================== +SV_Kill_f +================== +*/ +void SV_Kill_f (void) +{ + if (sv_player->v.health <= 0) + { + SV_ClientPrintf (host_client, PRINT_HIGH, "Can't suicide -- already dead!\n"); + return; + } + + pr_global_struct->time = sv.time; + pr_global_struct->self = EDICT_TO_PROG(sv_player); + PR_ExecuteProgram (pr_global_struct->ClientKill); +} + +/* +================== +SV_TogglePause +================== +*/ +void SV_TogglePause (const char *msg) +{ + int i; + client_t *cl; + + sv.paused ^= 1; + + if (msg) + SV_BroadcastPrintf (PRINT_HIGH, "%s", msg); + + // send notification to all clients + for (i=0, cl = svs.clients ; istate) + continue; + ClientReliableWrite_Begin (cl, svc_setpause, 2); + ClientReliableWrite_Byte (cl, sv.paused ? 1 : 0); + } +} + + +/* +================== +SV_Pause_f +================== +*/ +void SV_Pause_f (void) +{ +// client_t *cl; + char st[sizeof(host_client->name) + 32]; + + if (!pausable.value) { + SV_ClientPrintf (host_client, PRINT_HIGH, "Pause not allowed.\n"); + return; + } + + if (host_client->spectator) { + SV_ClientPrintf (host_client, PRINT_HIGH, "Spectators can not pause.\n"); + return; + } + + if (!sv.paused) + sprintf (st, "%s paused the game\n", host_client->name); + else + sprintf (st, "%s unpaused the game\n", host_client->name); + + SV_TogglePause(st); +} + + +/* +================= +SV_Drop_f + +The client is going to disconnect, so remove the connection immediately +================= +*/ +void SV_Drop_f (void) +{ + SV_EndRedirect (); + if (!host_client->spectator) + SV_BroadcastPrintf (PRINT_HIGH, "%s dropped\n", host_client->name); + SV_DropClient (host_client); +} + +/* +================= +SV_PTrack_f + +Change the bandwidth estimate for a client +================= +*/ +void SV_PTrack_f (void) +{ + int i; + edict_t *ent, *tent; + + if (!host_client->spectator) + return; + + if (Cmd_Argc() != 2) + { + // turn off tracking + host_client->spec_track = 0; + ent = EDICT_NUM(host_client - svs.clients + 1); + tent = EDICT_NUM(0); + ent->v.goalentity = EDICT_TO_PROG(tent); + return; + } + + i = atoi(Cmd_Argv(1)); + if (i < 0 || i >= MAX_CLIENTS || svs.clients[i].state != cs_spawned || + svs.clients[i].spectator) { + SV_ClientPrintf (host_client, PRINT_HIGH, "Invalid client to track\n"); + host_client->spec_track = 0; + ent = EDICT_NUM(host_client - svs.clients + 1); + tent = EDICT_NUM(0); + ent->v.goalentity = EDICT_TO_PROG(tent); + return; + } + host_client->spec_track = i + 1; // now tracking + + ent = EDICT_NUM(host_client - svs.clients + 1); + tent = EDICT_NUM(i + 1); + ent->v.goalentity = EDICT_TO_PROG(tent); +} + + +/* +================= +SV_Rate_f + +Change the bandwidth estimate for a client +================= +*/ +void SV_Rate_f (void) +{ + int rate; + + if (Cmd_Argc() != 2) + { + SV_ClientPrintf (host_client, PRINT_HIGH, "Current rate is %i\n", + (int)(1.0/host_client->netchan.rate + 0.5)); + return; + } + + rate = SV_BoundRate (atoi(Cmd_Argv(1))); + + SV_ClientPrintf (host_client, PRINT_HIGH, "Net rate set to %i\n", rate); + host_client->netchan.rate = 1.0/rate; +} + + +/* +================= +SV_Msg_f + +Change the message level for a client +================= +*/ +void SV_Msg_f (void) +{ + if (Cmd_Argc() != 2) + { + SV_ClientPrintf (host_client, PRINT_HIGH, "Current msg level is %i\n", + host_client->messagelevel); + return; + } + + host_client->messagelevel = atoi(Cmd_Argv(1)); + + SV_ClientPrintf (host_client, PRINT_HIGH, "Msg level set to %i\n", host_client->messagelevel); +} + +/* +================== +SV_SetInfo_f + +Allow clients to change userinfo +================== +*/ + +extern func_t UserInfo_Changed; + +void SV_SetInfo_f (void) +{ + int i; + char oldval[MAX_INFO_STRING]; + + if (Cmd_Argc() == 1) + { + Con_Printf ("User info settings:\n"); + Info_Print (host_client->userinfo); + return; + } + + if (Cmd_Argc() != 3) + { + Con_Printf ("usage: setinfo [ ]\n"); + return; + } + + if (Cmd_Argv(1)[0] == '*') + return; // don't set priveledged values + + if (strstr(Cmd_Argv(1), "\\") || strstr(Cmd_Argv(2), "\\")) + return; // illegal char + + strcpy(oldval, Info_ValueForKey(host_client->userinfo, Cmd_Argv(1))); + + Info_SetValueForKey (host_client->userinfo, Cmd_Argv(1), Cmd_Argv(2), MAX_INFO_STRING); +// name is extracted below in ExtractFromUserInfo +// Q_strncpyz (host_client->name, Info_ValueForKey (host_client->userinfo, "name") +// , sizeof(host_client->name)); +// SV_FullClientUpdate (host_client, &sv.reliable_datagram); +// host_client->sendinfo = true; + + //Info_ValueForKey(host_client->userinfo, Cmd_Argv(1)); + if (!strcmp(Info_ValueForKey(host_client->userinfo, Cmd_Argv(1)), oldval)) + return; // key hasn't changed + + // process any changed values + SV_ExtractFromUserinfo (host_client, !strcmp(Cmd_Argv(1), "name")); + + if (UserInfo_Changed) { + pr_global_struct->time = sv.time; + pr_global_struct->self = EDICT_TO_PROG(host_client->edict); + G_INT(OFS_PARM0) = PR_SetTmpString(Cmd_Argv(1)); + G_INT(OFS_PARM1) = PR_SetTmpString(oldval); + G_INT(OFS_PARM2) = PR_SetTmpString(Info_ValueForKey(host_client->userinfo, Cmd_Argv(1))); + PR_ExecuteProgram (UserInfo_Changed); + } + + i = host_client - svs.clients; + MSG_WriteByte (&sv.reliable_datagram, svc_setinfo); + MSG_WriteByte (&sv.reliable_datagram, i); + MSG_WriteString (&sv.reliable_datagram, Cmd_Argv(1)); + MSG_WriteString (&sv.reliable_datagram, Info_ValueForKey(host_client->userinfo, Cmd_Argv(1))); +} + +/* +================== +SV_ShowServerinfo_f + +Dumps the serverinfo info string +================== +*/ +void SV_ShowServerinfo_f (void) +{ + Info_Print (svs.info); +} + +void SV_NoSnap_f(void) +{ + if (*host_client->uploadfn) { + *host_client->uploadfn = 0; + SV_BroadcastPrintf (PRINT_HIGH, "%s refused remote screenshot\n", host_client->name); + } +} + +void SV_DemoList_f (void); + +typedef struct +{ + char *name; + void (*func) (void); +} ucmd_t; + +ucmd_t ucmds[] = +{ + {"new", SV_New_f}, + {"modellist", SV_Modellist_f}, + {"soundlist", SV_Soundlist_f}, + {"prespawn", SV_PreSpawn_f}, + {"spawn", SV_Spawn_f}, + {"begin", SV_Begin_f}, + + {"drop", SV_Drop_f}, + {"pings", SV_Pings_f}, + +// issued by hand at client consoles + {"rate", SV_Rate_f}, + {"kill", SV_Kill_f}, + {"pause", SV_Pause_f}, + {"msg", SV_Msg_f}, + + {"say", SV_Say_f}, + {"say_team", SV_Say_Team_f}, + + {"setinfo", SV_SetInfo_f}, + + {"serverinfo", SV_ShowServerinfo_f}, + + {"download", SV_BeginDownload_f}, + {"nextdl", SV_NextDownload_f}, + + {"ptrack", SV_PTrack_f}, //ZOID - used with autocam + + {"snap", SV_NoSnap_f}, + {"stopdownload", SV_StopDownload_f}, + {"demolist", SV_DemoList_f}, + + {NULL, NULL} +}; + +/* +================== +SV_ExecuteUserCommand +================== +*/ +qboolean PR_UserCmd(void); +void SV_ExecuteUserCommand (char *s) +{ + ucmd_t *u; + + Cmd_TokenizeString (s); + sv_player = host_client->edict; + + SV_BeginRedirect (RD_CLIENT); + + for (u=ucmds ; u->name ; u++) + if (!strcmp (Cmd_Argv(0), u->name) ) + { + u->func (); + break; + } + + if (!u->name) + if (!PR_UserCmd()) + Con_Printf ("Bad user command: %s\n", Cmd_Argv(0)); + + SV_EndRedirect (); +} + +/* +=========================================================================== + +USER CMD EXECUTION + +=========================================================================== +*/ + +/* +=============== +SV_CalcRoll + +Used by view and sv_user +=============== +*/ +float SV_CalcRoll (vec3_t angles, vec3_t velocity) +{ + vec3_t forward, right, up; + float sign; + float side; + float value; + + AngleVectors (angles, forward, right, up); + side = DotProduct (velocity, right); + sign = side < 0 ? -1 : 1; + side = fabs(side); + + value = sv_rollangle.value; + + if (side < sv_rollspeed.value) + side = side * value / sv_rollspeed.value; + else + side = value; + + return side*sign; + +} + + + + +//============================================================================ + +vec3_t pmove_mins, pmove_maxs; + +/* +==================== +AddLinksToPmove + +==================== +*/ +void AddLinksToPmove ( areanode_t *node ) +{ + link_t *l, *next; + edict_t *check; + int pl; + int i; + physent_t *pe; + + pl = EDICT_TO_PROG(sv_player); + + // touch linked edicts + for (l = node->solid_edicts.next ; l != &node->solid_edicts ; l = next) + { + next = l->next; + check = EDICT_FROM_AREA(l); + + if (check->v.owner == pl) + continue; // player's own missile + if (check->v.solid == SOLID_BSP + || check->v.solid == SOLID_BBOX + || check->v.solid == SOLID_SLIDEBOX) + { + if (check == sv_player) + continue; + + for (i=0 ; i<3 ; i++) + if (check->v.absmin[i] > pmove_maxs[i] + || check->v.absmax[i] < pmove_mins[i]) + break; + if (i != 3) + continue; + if (pmove.numphysent == MAX_PHYSENTS) + return; + pe = &pmove.physents[pmove.numphysent]; + pmove.numphysent++; + + VectorCopy (check->v.origin, pe->origin); + pe->info = NUM_FOR_EDICT(check); + if (check->v.solid == SOLID_BSP) + pe->model = sv.models[(int)(check->v.modelindex)]; + else + { + pe->model = NULL; + VectorCopy (check->v.mins, pe->mins); + VectorCopy (check->v.maxs, pe->maxs); + } + } + } + +// recurse down both sides + if (node->axis == -1) + return; + + if ( pmove_maxs[node->axis] > node->dist ) + AddLinksToPmove ( node->children[0] ); + if ( pmove_mins[node->axis] < node->dist ) + AddLinksToPmove ( node->children[1] ); +} + + +/* +================ +AddAllEntsToPmove + +For debugging +================ +*/ +void AddAllEntsToPmove (void) +{ + int e; + edict_t *check; + int i; + physent_t *pe; + int pl; + + pl = EDICT_TO_PROG(sv_player); + check = NEXT_EDICT(sv.edicts); + for (e=1 ; efree) + continue; + if (check->v.owner == pl) + continue; + if (check->v.solid == SOLID_BSP + || check->v.solid == SOLID_BBOX + || check->v.solid == SOLID_SLIDEBOX) + { + if (check == sv_player) + continue; + + for (i=0 ; i<3 ; i++) + if (check->v.absmin[i] > pmove_maxs[i] + || check->v.absmax[i] < pmove_mins[i]) + break; + if (i != 3) + continue; + pe = &pmove.physents[pmove.numphysent]; + + VectorCopy (check->v.origin, pe->origin); + pmove.physents[pmove.numphysent].info = e; + if (check->v.solid == SOLID_BSP) + pe->model = sv.models[(int)(check->v.modelindex)]; + else + { + pe->model = NULL; + VectorCopy (check->v.mins, pe->mins); + VectorCopy (check->v.maxs, pe->maxs); + } + + if (++pmove.numphysent == MAX_PHYSENTS) + break; + } + } +} + +/* +=========== +SV_PreRunCmd +=========== +Done before running a player command. Clears the touch array +*/ +byte playertouch[(MAX_EDICTS+7)/8]; + +void SV_PreRunCmd(void) +{ + memset(playertouch, 0, sizeof(playertouch)); +} + +/* +=========== +SV_RunCmd +=========== +*/ +void SV_RunCmd (usercmd_t *ucmd) +{ + edict_t *ent; + int i, n; + int oldmsec; + + cmd = *ucmd; + + // chop up very long command + if (cmd.msec > 50) + { + oldmsec = ucmd->msec; + cmd.msec = oldmsec/2; + SV_RunCmd (&cmd); + cmd.msec = oldmsec/2; + cmd.impulse = 0; + SV_RunCmd (&cmd); + return; + } + + if (!sv_player->v.fixangle) + VectorCopy (ucmd->angles, sv_player->v.v_angle); + + sv_player->v.button0 = ucmd->buttons & 1; + sv_player->v.button2 = (ucmd->buttons & 2)>>1; + if (ucmd->impulse) + sv_player->v.impulse = ucmd->impulse; + +// +// angles +// show 1/3 the pitch angle and all the roll angle + if (sv_player->v.health > 0) + { + if (!sv_player->v.fixangle) + { + sv_player->v.angles[PITCH] = -sv_player->v.v_angle[PITCH]/3; + sv_player->v.angles[YAW] = sv_player->v.v_angle[YAW]; + } + sv_player->v.angles[ROLL] = + SV_CalcRoll (sv_player->v.angles, sv_player->v.velocity)*4; + } + + sv_frametime = ucmd->msec * 0.001; + if (sv_frametime > 0.1) + sv_frametime = 0.1; + + if (!host_client->spectator) + { + pr_global_struct->frametime = sv_frametime; + + pr_global_struct->time = sv.time; + pr_global_struct->self = EDICT_TO_PROG(sv_player); + PR_ExecuteProgram (pr_global_struct->PlayerPreThink); + + SV_RunThink (sv_player); + } + + for (i=0 ; i<3 ; i++) + pmove.origin[i] = sv_player->v.origin[i] + (sv_player->v.mins[i] - player_mins[i]); + VectorCopy (sv_player->v.velocity, pmove.velocity); + VectorCopy (sv_player->v.v_angle, pmove.angles); + + pmove.spectator = host_client->spectator; + pmove.waterjumptime = sv_player->v.teleport_time; + pmove.numphysent = 1; + pmove.physents[0].model = sv.worldmodel; + pmove.cmd = *ucmd; + pmove.dead = sv_player->v.health <= 0; + pmove.oldbuttons = host_client->oldbuttons; + + movevars.entgravity = host_client->entgravity; + movevars.maxspeed = host_client->maxspeed; + + for (i=0 ; i<3 ; i++) + { + pmove_mins[i] = pmove.origin[i] - 256; + pmove_maxs[i] = pmove.origin[i] + 256; + } +#if 1 + AddLinksToPmove ( sv_areanodes ); +#else + AddAllEntsToPmove (); +#endif + +#if 0 +{ + int before, after; + +before = PM_TestPlayerPosition (pmove.origin); + PlayerMove (); +after = PM_TestPlayerPosition (pmove.origin); + +if (sv_player->v.health > 0 && before && !after ) + Con_Printf ("player %s got stuck in playermove!!!!\n", host_client->name); +} +#else + PlayerMove (); +#endif + + host_client->oldbuttons = pmove.oldbuttons; + sv_player->v.teleport_time = pmove.waterjumptime; + sv_player->v.waterlevel = waterlevel; + sv_player->v.watertype = watertype; + if (onground != -1) + { + sv_player->v.flags = (int)sv_player->v.flags | FL_ONGROUND; + sv_player->v.groundentity = EDICT_TO_PROG(EDICT_NUM(pmove.physents[onground].info)); + } + else + sv_player->v.flags = (int)sv_player->v.flags & ~FL_ONGROUND; + for (i=0 ; i<3 ; i++) + sv_player->v.origin[i] = pmove.origin[i] - (sv_player->v.mins[i] - player_mins[i]); + +#if 0 + // truncate velocity the same way the net protocol will + for (i=0 ; i<3 ; i++) + sv_player->v.velocity[i] = (int)pmove.velocity[i]; +#else + VectorCopy (pmove.velocity, sv_player->v.velocity); +#endif + + VectorCopy (pmove.angles, sv_player->v.v_angle); + + if (!host_client->spectator) + { + // link into place and touch triggers + SV_LinkEdict (sv_player, true); + + // touch other objects + for (i=0 ; iv.touch || (playertouch[n/8]&(1<<(n%8)))) + continue; + pr_global_struct->self = EDICT_TO_PROG(ent); + pr_global_struct->other = EDICT_TO_PROG(sv_player); + PR_ExecuteProgram (ent->v.touch); + playertouch[n/8] |= 1 << (n%8); + } + } +} + +/* +=========== +SV_PostRunCmd +=========== +Done after running a player command. +*/ +void SV_PostRunCmd(void) +{ + // run post-think + + if (!host_client->spectator) { + pr_global_struct->time = sv.time; + pr_global_struct->self = EDICT_TO_PROG(sv_player); + PR_ExecuteProgram (pr_global_struct->PlayerPostThink); + SV_RunNewmis (); + } else if (SpectatorThink) { + pr_global_struct->time = sv.time; + pr_global_struct->self = EDICT_TO_PROG(sv_player); + PR_ExecuteProgram (SpectatorThink); + } +} + + +/* +=================== +SV_ExecuteClientMessage + +The current net_message is parsed for the given client +=================== +*/ +void SV_ExecuteClientMessage (client_t *cl) +{ + int c; + char *s; + usercmd_t oldest, oldcmd, newcmd; + client_frame_t *frame; + vec3_t o; + qboolean move_issued = false; //only allow one move command + int checksumIndex; + byte checksum, calculatedChecksum; + int seq_hash; + + // calc ping time + frame = &cl->frames[cl->netchan.incoming_acknowledged & UPDATE_MASK]; + frame->ping_time = realtime - frame->senttime; + + // make sure the reply sequence number matches the incoming + // sequence number + if (cl->netchan.incoming_sequence >= cl->netchan.outgoing_sequence) + cl->netchan.outgoing_sequence = cl->netchan.incoming_sequence; + else + cl->send_message = false; // don't reply, sequences have slipped + + // save time for ping calculations + cl->frames[cl->netchan.outgoing_sequence & UPDATE_MASK].senttime = realtime; + cl->frames[cl->netchan.outgoing_sequence & UPDATE_MASK].ping_time = -1; + + host_client = cl; + sv_player = host_client->edict; + +// seq_hash = (cl->netchan.incoming_sequence & 0xffff) ; // ^ QW_CHECK_HASH; + seq_hash = cl->netchan.incoming_sequence; + + // mark time so clients will know how much to predict + // other players + cl->localtime = sv.time; + cl->delta_sequence = -1; // no delta unless requested + while (1) + { + if (msg_badread) + { + Con_Printf ("SV_ReadClientMessage: badread\n"); + SV_DropClient (cl); + return; + } + + c = MSG_ReadByte (); + if (c == -1) + break; + + switch (c) + { + default: + Con_Printf ("SV_ReadClientMessage: unknown command char\n"); + SV_DropClient (cl); + return; + + case clc_nop: + break; + + case clc_delta: + cl->delta_sequence = MSG_ReadByte (); + break; + + case clc_move: + if (move_issued) + return; // someone is trying to cheat... + + move_issued = true; + + checksumIndex = MSG_GetReadCount(); + checksum = (byte)MSG_ReadByte (); + + // read loss percentage + cl->lossage = MSG_ReadByte(); + + MSG_ReadDeltaUsercmd (&nullcmd, &oldest); + MSG_ReadDeltaUsercmd (&oldest, &oldcmd); + MSG_ReadDeltaUsercmd (&oldcmd, &newcmd); + + if ( cl->state != cs_spawned ) + break; + + // if the checksum fails, ignore the rest of the packet + calculatedChecksum = COM_BlockSequenceCRCByte( + net_message.data + checksumIndex + 1, + MSG_GetReadCount() - checksumIndex - 1, + seq_hash); + + if (calculatedChecksum != checksum) + { + Con_DPrintf ("Failed command checksum for %s(%d) (%d != %d)\n", + cl->name, cl->netchan.incoming_sequence, checksum, calculatedChecksum); + return; + } + + if (!sv.paused) { + SV_PreRunCmd(); + + if (net_drop < 20) + { + while (net_drop > 2) + { + SV_RunCmd (&cl->lastcmd); + net_drop--; + } + if (net_drop > 1) + SV_RunCmd (&oldest); + if (net_drop > 0) + SV_RunCmd (&oldcmd); + } + SV_RunCmd (&newcmd); + + SV_PostRunCmd(); + } + + cl->lastcmd = newcmd; + cl->lastcmd.buttons = 0; // avoid multiple fires on lag + break; + + + case clc_stringcmd: + s = MSG_ReadString (); + SV_ExecuteUserCommand (s); + break; + + case clc_tmove: + o[0] = MSG_ReadCoord(); + o[1] = MSG_ReadCoord(); + o[2] = MSG_ReadCoord(); + // only allowed by spectators + if (host_client->spectator) { + VectorCopy(o, sv_player->v.origin); + SV_LinkEdict(sv_player, false); + } + break; + + case clc_upload: + SV_NextUpload(); + break; + + } + } +} + +/* +============== +SV_UserInit +============== +*/ +void SV_UserInit (void) +{ + Cvar_RegisterVariable (&sv_rollspeed); + Cvar_RegisterVariable (&sv_rollangle); + Cvar_RegisterVariable (&sv_spectalk); + Cvar_RegisterVariable (&sv_mapcheck); +} diff --git a/source/sys.h b/source/sys.h index 3b8d4962..b9ea1f10 100644 --- a/source/sys.h +++ b/source/sys.h @@ -1,92 +1,96 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// sys.h -- non-portable functions - - -// -// file IO -// - -// returns the file size -// return -1 if file is not present -// the file should be in BINARY mode for stupid OSs that care -#define MAX_DIRFILES 1000 -#define MAX_DEMO_NAME 64 - -typedef struct -{ - char name[MAX_DEMO_NAME]; - int size; -} file_t; - -typedef struct -{ - file_t *files; - int size; - int numfiles; - int numdirs; -} dir_t; - -int Sys_FileOpenRead (char *path, int *hndl); - -int Sys_FileOpenWrite (char *path); -void Sys_FileClose (int handle); -void Sys_FileSeek (int handle, int position); -int Sys_FileRead (int handle, void *dest, int count); -int Sys_FileWrite (int handle, void *data, int count); -int Sys_FileTime (char *path); -void Sys_mkdir (char *path); -int Sys_remove (char *path); -dir_t Sys_listdir (char *path, char *ext); - -// -// memory protection -// -void Sys_MakeCodeWriteable (unsigned long startaddr, unsigned long length); - -// -// system IO -// -void Sys_DebugLog (char *file, char *fmt, ...); - -void Sys_Error (char *error, ...); -// an error will cause the entire program to exit - -void Sys_Printf (char *fmt, ...); -// send text to the console - -void Sys_Quit (void); - -double Sys_DoubleTime (void); - -char *Sys_ConsoleInput (void); - -void Sys_Sleep (void); -// called to yield for a little bit so as -// not to hog cpu when paused or debugging - -void Sys_SendKeyEvents (void); -// Perform Key_Event () callbacks until the input que is empty - -void Sys_LowFPPrecision (void); -void Sys_HighFPPrecision (void); -void Sys_SetFPCW (void); - -void Sys_Init (void); +/* +Copyright (C) 1996-1997 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. + +*/ +// sys.h -- non-portable functions + + +// +// file IO +// + +// returns the file size +// return -1 if file is not present +// the file should be in BINARY mode for stupid OSs that care +#define MAX_DIRFILES 1000 +#define MAX_DEMO_NAME 64 + +typedef struct +{ + char name[MAX_DEMO_NAME]; + int size; +} file_t; + +typedef struct +{ + file_t *files; + int size; + int numfiles; + int numdirs; +} dir_t; + +int Sys_FileOpenRead (char *path, int *hndl); + +int Sys_FileOpenWrite (char *path); +void Sys_FileClose (int handle); +void Sys_FileSeek (int handle, int position); +int Sys_FileRead (int handle, void *dest, int count); +int Sys_FileWrite (int handle, void *data, int count); +int Sys_FileTime (char *path); +void Sys_mkdir (char *path); +int Sys_remove (char *path); +dir_t Sys_listdir (char *path, char *ext); + +// +// memory protection +// +void Sys_MakeCodeWriteable (unsigned long startaddr, unsigned long length); + +// +// system IO +// +void Sys_DebugLog (char *file, char *fmt, ...); + +void Sys_Error (char *error, ...); +// an error will cause the entire program to exit + +void Sys_Printf (char *fmt, ...); +// send text to the console + +void Sys_Quit (void); + +double Sys_DoubleTime (void); + +char *Sys_ConsoleInput (void); + +//void Sys_Sleep (void); +// called to yield for a little bit so as +// not to hog cpu when paused or debugging + +void Sys_SendKeyEvents (void); +// Perform Key_Event () callbacks until the input que is empty + +void Sys_LowFPPrecision (void); +void Sys_HighFPPrecision (void); +void Sys_SetFPCW (void); + +void Sys_Init (void); +void Sys_Sleep (unsigned long ms); +int Sys_Script(char *path, char *args); + + diff --git a/source/sys_linux.c b/source/sys_linux.c index 51bb66c6..05a10aae 100644 --- a/source/sys_linux.c +++ b/source/sys_linux.c @@ -1,426 +1,426 @@ -/* -Copyright (C) 1996-1997 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "quakedef.h" - -int noconinput = 0; -int nostdout = 0; - -char *basedir = "."; - -cvar_t sys_linerefresh = {"sys_linerefresh","0"};// set for entity display - -// ======================================================================= -// General routines -// ======================================================================= - -void Sys_DebugNumber(int y, int val) -{ -} - -/* -void Sys_Printf (char *fmt, ...) -{ - va_list argptr; - char text[1024]; - - va_start (argptr,fmt); - vsprintf (text,fmt,argptr); - va_end (argptr); - fprintf(stderr, "%s", text); - - Con_Print (text); -} - -void Sys_Printf (char *fmt, ...) -{ - - va_list argptr; - char text[1024], *t_p; - int l, r; - - if (nostdout) - return; - - va_start (argptr,fmt); - vsprintf (text,fmt,argptr); - va_end (argptr); - - l = strlen(text); - t_p = text; - -// make sure everything goes through, even though we are non-blocking - while (l) - { - r = write (1, text, l); - if (r != l) - sleep (0); - if (r > 0) - { - t_p += r; - l -= r; - } - } - -} -*/ - -void Sys_Printf (char *fmt, ...) -{ - va_list argptr; - char text[2048]; - unsigned char *p; - - va_start (argptr,fmt); - vsprintf (text,fmt,argptr); - va_end (argptr); - - if (strlen(text) > sizeof(text)) - Sys_Error("memory overwrite in Sys_Printf"); - - if (nostdout) - return; - - for (p = (unsigned char *)text; *p; p++) - if ((*p > 128 || *p < 32) && *p != 10 && *p != 13 && *p != 9) - printf("[%02x]", *p); - else - putc(*p, stdout); -} - -void Sys_Quit (void) -{ - Host_Shutdown(); - fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) & ~FNDELAY); - exit(0); -} - -void Sys_Init(void) -{ -#if id386 - Sys_SetFPCW(); -#endif -} - -void Sys_Error (char *error, ...) -{ - va_list argptr; - char string[1024]; - -// change stdin to non blocking - fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) & ~FNDELAY); - - va_start (argptr,error); - vsprintf (string,error,argptr); - va_end (argptr); - fprintf(stderr, "Error: %s\n", string); - - Host_Shutdown (); - exit (1); - -} - -void Sys_Warn (char *warning, ...) -{ - va_list argptr; - char string[1024]; - - va_start (argptr,warning); - vsprintf (string,warning,argptr); - va_end (argptr); - fprintf(stderr, "Warning: %s", string); -} - -/* -============ -Sys_FileTime - -returns -1 if not present -============ -*/ -int Sys_FileTime (char *path) -{ - struct stat buf; - - if (stat (path,&buf) == -1) - return -1; - - return buf.st_mtime; -} - - -void Sys_mkdir (char *path) -{ - mkdir (path, 0777); -} - - -int Sys_FileOpenRead (char *path, int *handle) -{ - int h; - struct stat fileinfo; - - - h = open (path, O_RDONLY, 0666); - *handle = h; - if (h == -1) - return -1; - - if (fstat (h,&fileinfo) == -1) - Sys_Error ("Error fstating %s", path); - - return fileinfo.st_size; -} - -int Sys_FileOpenWrite (char *path) -{ - int handle; - - umask (0); - - handle = open(path,O_RDWR | O_CREAT | O_TRUNC - , 0666); - - if (handle == -1) - Sys_Error ("Error opening %s: %s", path,strerror(errno)); - - return handle; -} - -int Sys_FileWrite (int handle, void *src, int count) -{ - return write (handle, src, count); -} - -void Sys_FileClose (int handle) -{ - close (handle); -} - -void Sys_FileSeek (int handle, int position) -{ - lseek (handle, position, SEEK_SET); -} - -int Sys_FileRead (int handle, void *dest, int count) -{ - return read (handle, dest, count); -} - -void Sys_DebugLog(char *file, char *fmt, ...) -{ - va_list argptr; - static char data[1024]; - int fd; - - va_start(argptr, fmt); - vsprintf(data, fmt, argptr); - va_end(argptr); -// fd = open(file, O_WRONLY | O_BINARY | O_CREAT | O_APPEND, 0666); - fd = open(file, O_WRONLY | O_CREAT | O_APPEND, 0666); - write(fd, data, strlen(data)); - close(fd); -} - -void Sys_EditFile(char *filename) -{ - - char cmd[256]; - char *term; - char *editor; - - term = getenv("TERM"); - if (term && !strcmp(term, "xterm")) - { - editor = getenv("VISUAL"); - if (!editor) - editor = getenv("EDITOR"); - if (!editor) - editor = getenv("EDIT"); - if (!editor) - editor = "vi"; - sprintf(cmd, "xterm -e %s %s", editor, filename); - system(cmd); - } - -} - -double Sys_DoubleTime (void) -{ - struct timeval tp; - struct timezone tzp; - static int secbase; - - gettimeofday(&tp, &tzp); - - if (!secbase) - { - secbase = tp.tv_sec; - return tp.tv_usec/1000000.0; - } - - return (tp.tv_sec - secbase) + tp.tv_usec/1000000.0; -} - -// ======================================================================= -// Sleeps for microseconds -// ======================================================================= - -static volatile int oktogo; - -void alarm_handler(int x) -{ - oktogo=1; -} - -void Sys_LineRefresh(void) -{ -} - -void floating_point_exception_handler(int whatever) -{ -// Sys_Warn("floating point exception\n"); - signal(SIGFPE, floating_point_exception_handler); -} - -char *Sys_ConsoleInput(void) -{ -#if 0 - static char text[256]; - int len; - - if (cls.state == ca_dedicated) { - len = read (0, text, sizeof(text)); - if (len < 1) - return NULL; - text[len-1] = 0; // rip off the /n and terminate - - return text; - } -#endif - return NULL; -} - -#if !id386 -void Sys_HighFPPrecision (void) -{ -} - -void Sys_LowFPPrecision (void) -{ -} -#endif - -int skipframes; - -int main (int c, char **v) -{ - - double time, oldtime, newtime; - quakeparms_t parms; - int j; - -// static char cwd[1024]; - -// signal(SIGFPE, floating_point_exception_handler); - signal(SIGFPE, SIG_IGN); - - memset(&parms, 0, sizeof(parms)); - - COM_InitArgv(c, v); - parms.argc = com_argc; - parms.argv = com_argv; - - parms.memsize = 16*1024*1024; - - j = COM_CheckParm("-mem"); - if (j) - parms.memsize = (int) (Q_atof(com_argv[j+1]) * 1024 * 1024); - parms.membase = Q_Malloc (parms.memsize); - - parms.basedir = basedir; - - noconinput = COM_CheckParm("-noconinput"); - if (!noconinput) - fcntl(0, F_SETFL, fcntl (0, F_GETFL, 0) | FNDELAY); - - if (COM_CheckParm("-nostdout")) - nostdout = 1; - - Sys_Init(); - - Host_Init(&parms); - - oldtime = Sys_DoubleTime (); - while (1) - { -// find time spent rendering last frame - newtime = Sys_DoubleTime (); - time = newtime - oldtime; - - Host_Frame(time); - oldtime = newtime; - } - -} - - -/* -================ -Sys_MakeCodeWriteable -================ -*/ -void Sys_MakeCodeWriteable (unsigned long startaddr, unsigned long length) -{ - - int r; - unsigned long addr; - int psize = getpagesize(); - - addr = (startaddr & ~(psize-1)) - psize; - -// fprintf(stderr, "writable code %lx(%lx)-%lx, length=%lx\n", startaddr, -// addr, startaddr+length, length); - - r = mprotect((char*)addr, length + startaddr - addr + psize, 7); - - if (r < 0) - Sys_Error("Protection change failed\n"); - -} - +/* +Copyright (C) 1996-1997 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "quakedef.h" + +int noconinput = 0; +int nostdout = 0; + +char *basedir = "."; + +cvar_t sys_linerefresh = {"sys_linerefresh","0"};// set for entity display + +// ======================================================================= +// General routines +// ======================================================================= + +void Sys_DebugNumber(int y, int val) +{ +} + +/* +void Sys_Printf (char *fmt, ...) +{ + va_list argptr; + char text[1024]; + + va_start (argptr,fmt); + vsprintf (text,fmt,argptr); + va_end (argptr); + fprintf(stderr, "%s", text); + + Con_Print (text); +} + +void Sys_Printf (char *fmt, ...) +{ + + va_list argptr; + char text[1024], *t_p; + int l, r; + + if (nostdout) + return; + + va_start (argptr,fmt); + vsprintf (text,fmt,argptr); + va_end (argptr); + + l = strlen(text); + t_p = text; + +// make sure everything goes through, even though we are non-blocking + while (l) + { + r = write (1, text, l); + if (r != l) + sleep (0); + if (r > 0) + { + t_p += r; + l -= r; + } + } + +} +*/ + +void Sys_Printf (char *fmt, ...) +{ + va_list argptr; + char text[2048]; + unsigned char *p; + + va_start (argptr,fmt); + vsprintf (text,fmt,argptr); + va_end (argptr); + + if (strlen(text) > sizeof(text)) + Sys_Error("memory overwrite in Sys_Printf"); + + if (nostdout) + return; + + for (p = (unsigned char *)text; *p; p++) + if ((*p > 128 || *p < 32) && *p != 10 && *p != 13 && *p != 9) + printf("[%02x]", *p); + else + putc(*p, stdout); +} + +void Sys_Quit (void) +{ + Host_Shutdown(); + fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) & ~FNDELAY); + exit(0); +} + +void Sys_Init(void) +{ +#if id386 + Sys_SetFPCW(); +#endif +} + +void Sys_Error (char *error, ...) +{ + va_list argptr; + char string[1024]; + +// change stdin to non blocking + fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) & ~FNDELAY); + + va_start (argptr,error); + vsprintf (string,error,argptr); + va_end (argptr); + fprintf(stderr, "Error: %s\n", string); + + Host_Shutdown (); + exit (1); + +} + +void Sys_Warn (char *warning, ...) +{ + va_list argptr; + char string[1024]; + + va_start (argptr,warning); + vsprintf (string,warning,argptr); + va_end (argptr); + fprintf(stderr, "Warning: %s", string); +} + +/* +============ +Sys_FileTime + +returns -1 if not present +============ +*/ +int Sys_FileTime (char *path) +{ + struct stat buf; + + if (stat (path,&buf) == -1) + return -1; + + return buf.st_mtime; +} + + +void Sys_mkdir (char *path) +{ + mkdir (path, 0777); +} + + +int Sys_FileOpenRead (char *path, int *handle) +{ + int h; + struct stat fileinfo; + + + h = open (path, O_RDONLY, 0666); + *handle = h; + if (h == -1) + return -1; + + if (fstat (h,&fileinfo) == -1) + Sys_Error ("Error fstating %s", path); + + return fileinfo.st_size; +} + +int Sys_FileOpenWrite (char *path) +{ + int handle; + + umask (0); + + handle = open(path,O_RDWR | O_CREAT | O_TRUNC + , 0666); + + if (handle == -1) + Sys_Error ("Error opening %s: %s", path,strerror(errno)); + + return handle; +} + +int Sys_FileWrite (int handle, void *src, int count) +{ + return write (handle, src, count); +} + +void Sys_FileClose (int handle) +{ + close (handle); +} + +void Sys_FileSeek (int handle, int position) +{ + lseek (handle, position, SEEK_SET); +} + +int Sys_FileRead (int handle, void *dest, int count) +{ + return read (handle, dest, count); +} + +void Sys_DebugLog(char *file, char *fmt, ...) +{ + va_list argptr; + static char data[1024]; + int fd; + + va_start(argptr, fmt); + vsprintf(data, fmt, argptr); + va_end(argptr); +// fd = open(file, O_WRONLY | O_BINARY | O_CREAT | O_APPEND, 0666); + fd = open(file, O_WRONLY | O_CREAT | O_APPEND, 0666); + write(fd, data, strlen(data)); + close(fd); +} + +void Sys_EditFile(char *filename) +{ + + char cmd[256]; + char *term; + char *editor; + + term = getenv("TERM"); + if (term && !strcmp(term, "xterm")) + { + editor = getenv("VISUAL"); + if (!editor) + editor = getenv("EDITOR"); + if (!editor) + editor = getenv("EDIT"); + if (!editor) + editor = "vi"; + sprintf(cmd, "xterm -e %s %s", editor, filename); + system(cmd); + } + +} + +double Sys_DoubleTime (void) +{ + struct timeval tp; + struct timezone tzp; + static int secbase; + + gettimeofday(&tp, &tzp); + + if (!secbase) + { + secbase = tp.tv_sec; + return tp.tv_usec/1000000.0; + } + + return (tp.tv_sec - secbase) + tp.tv_usec/1000000.0; +} + +// ======================================================================= +// Sleeps for microseconds +// ======================================================================= + +static volatile int oktogo; + +void alarm_handler(int x) +{ + oktogo=1; +} + +void Sys_LineRefresh(void) +{ +} + +void floating_point_exception_handler(int whatever) +{ +// Sys_Warn("floating point exception\n"); + signal(SIGFPE, floating_point_exception_handler); +} + +char *Sys_ConsoleInput(void) +{ +#if 0 + static char text[256]; + int len; + + if (cls.state == ca_dedicated) { + len = read (0, text, sizeof(text)); + if (len < 1) + return NULL; + text[len-1] = 0; // rip off the /n and terminate + + return text; + } +#endif + return NULL; +} + +#if !id386 +void Sys_HighFPPrecision (void) +{ +} + +void Sys_LowFPPrecision (void) +{ +} +#endif + +int skipframes; + +int main (int c, char **v) +{ + + double time, oldtime, newtime; + quakeparms_t parms; + int j; + +// static char cwd[1024]; + +// signal(SIGFPE, floating_point_exception_handler); + signal(SIGFPE, SIG_IGN); + + memset(&parms, 0, sizeof(parms)); + + COM_InitArgv(c, v); + parms.argc = com_argc; + parms.argv = com_argv; + + parms.memsize = 16*1024*1024; + + j = COM_CheckParm("-mem"); + if (j) + parms.memsize = (int) (Q_atof(com_argv[j+1]) * 1024 * 1024); + parms.membase = Q_Malloc (parms.memsize); + + parms.basedir = basedir; + + noconinput = COM_CheckParm("-noconinput"); + if (!noconinput) + fcntl(0, F_SETFL, fcntl (0, F_GETFL, 0) | FNDELAY); + + if (COM_CheckParm("-nostdout")) + nostdout = 1; + + Sys_Init(); + + Host_Init(&parms); + + oldtime = Sys_DoubleTime (); + while (1) + { +// find time spent rendering last frame + newtime = Sys_DoubleTime (); + time = newtime - oldtime; + + Host_Frame(time); + oldtime = newtime; + } + +} + + +/* +================ +Sys_MakeCodeWriteable +================ +*/ +void Sys_MakeCodeWriteable (unsigned long startaddr, unsigned long length) +{ + + int r; + unsigned long addr; + int psize = getpagesize(); + + addr = (startaddr & ~(psize-1)) - psize; + +// fprintf(stderr, "writable code %lx(%lx)-%lx, length=%lx\n", startaddr, +// addr, startaddr+length, length); + + r = mprotect((char*)addr, length + startaddr - addr + psize, 7); + + if (r < 0) + Sys_Error("Protection change failed\n"); + +} + diff --git a/source/sys_null.c b/source/sys_null.c index 2252196e..9bbf8707 100644 --- a/source/sys_null.c +++ b/source/sys_null.c @@ -1,123 +1,123 @@ -// sys_null.h -- null system driver to aid porting efforts - -#include "quakedef.h" -#include "errno.h" - - -int Sys_FileTime (char *path) -{ - FILE *f; - - f = fopen(path, "rb"); - if (f) - { - fclose(f); - return 1; - } - - return -1; -} - -void Sys_mkdir (char *path) -{ -} - - -/* -=============================================================================== - -SYSTEM IO - -=============================================================================== -*/ - -void Sys_MakeCodeWriteable (unsigned long startaddr, unsigned long length) -{ -} - - -void Sys_DebugLog(char *file, char *fmt, ...) -{ -} - -void Sys_Error (char *error, ...) -{ - va_list argptr; - - printf ("I_Error: "); - va_start (argptr,error); - vprintf (error,argptr); - va_end (argptr); - printf ("\n"); - - exit (1); -} - -void Sys_Printf (char *fmt, ...) -{ - va_list argptr; - - va_start (argptr,fmt); - vprintf (fmt,argptr); - va_end (argptr); -} - -void Sys_Quit (void) -{ - exit (0); -} - -double Sys_FloatTime (void) -{ - static double t; - - t += 0.1; - - return t; -} - -char *Sys_ConsoleInput (void) -{ - return NULL; -} - -void Sys_Sleep (void) -{ -} - -void Sys_SendKeyEvents (void) -{ -} - -void Sys_HighFPPrecision (void) -{ -} - -void Sys_LowFPPrecision (void) -{ -} - -//============================================================================= - -void main (int argc, char **argv) -{ - quakeparms_t parms; - - parms.memsize = 5861376; - parms.membase = Q_Malloc (parms.memsize); - parms.basedir = "."; - - COM_InitArgv (argc, argv); - - parms.argc = com_argc; - parms.argv = com_argv; - - printf ("Host_Init\n"); - Host_Init (&parms); - while (1) - { - Host_Frame (0.1); - } -} - - +// sys_null.h -- null system driver to aid porting efforts + +#include "quakedef.h" +#include "errno.h" + + +int Sys_FileTime (char *path) +{ + FILE *f; + + f = fopen(path, "rb"); + if (f) + { + fclose(f); + return 1; + } + + return -1; +} + +void Sys_mkdir (char *path) +{ +} + + +/* +=============================================================================== + +SYSTEM IO + +=============================================================================== +*/ + +void Sys_MakeCodeWriteable (unsigned long startaddr, unsigned long length) +{ +} + + +void Sys_DebugLog(char *file, char *fmt, ...) +{ +} + +void Sys_Error (char *error, ...) +{ + va_list argptr; + + printf ("I_Error: "); + va_start (argptr,error); + vprintf (error,argptr); + va_end (argptr); + printf ("\n"); + + exit (1); +} + +void Sys_Printf (char *fmt, ...) +{ + va_list argptr; + + va_start (argptr,fmt); + vprintf (fmt,argptr); + va_end (argptr); +} + +void Sys_Quit (void) +{ + exit (0); +} + +double Sys_FloatTime (void) +{ + static double t; + + t += 0.1; + + return t; +} + +char *Sys_ConsoleInput (void) +{ + return NULL; +} + +void Sys_Sleep (void) +{ +} + +void Sys_SendKeyEvents (void) +{ +} + +void Sys_HighFPPrecision (void) +{ +} + +void Sys_LowFPPrecision (void) +{ +} + +//============================================================================= + +void main (int argc, char **argv) +{ + quakeparms_t parms; + + parms.memsize = 5861376; + parms.membase = Q_Malloc (parms.memsize); + parms.basedir = "."; + + COM_InitArgv (argc, argv); + + parms.argc = com_argc; + parms.argv = com_argv; + + printf ("Host_Init\n"); + Host_Init (&parms); + while (1) + { + Host_Frame (0.1); + } +} + + diff --git a/source/sys_win.c b/source/sys_win.c index eda62f3b..b2bc9f91 100644 --- a/source/sys_win.c +++ b/source/sys_win.c @@ -1,549 +1,549 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// sys_win.h - -#include "quakedef.h" -#include "winquake.h" -#include "resource.h" -#include -#include -#include -#include // _open, etc -#include // _mkdir -#include // _putch - -#define MINIMUM_WIN_MEMORY 0x0c00000 -#define MAXIMUM_WIN_MEMORY 0x1000000 - -#define PAUSE_SLEEP 50 // sleep time on pause or minimization -#define NOT_FOCUS_SLEEP 20 // sleep time when not focus - -int starttime; -qboolean ActiveApp, Minimized; -qboolean WinNT; - -HWND hwnd_dialog; // startup dialog box - -static double pfreq; -static double curtime = 0.0; -static double lastcurtime = 0.0; -static int lowshift; -static HANDLE hinput, houtput; - -HANDLE qwclsemaphore; - -static HANDLE tevent; - -void Sys_InitFloatTime (void); - -void MaskExceptions (void); -void Sys_PopFPCW (void); -void Sys_PushFPCW_SetHigh (void); - -void Sys_DebugLog(char *file, char *fmt, ...) -{ - va_list argptr; - static char data[1024]; - int fd; - - va_start(argptr, fmt); - vsprintf(data, fmt, argptr); - va_end(argptr); - fd = open(file, O_WRONLY | O_CREAT | O_APPEND, 0666); - write(fd, data, strlen(data)); - close(fd); -}; - -/* -=============================================================================== - -FILE IO - -=============================================================================== -*/ - -int Sys_FileTime (char *path) -{ - FILE *f; - int t, retval; - - t = VID_ForceUnlockedAndReturnState (); - - f = fopen(path, "rb"); - - if (f) - { - fclose(f); - retval = 1; - } - else - { - retval = -1; - } - - VID_ForceLockState (t); - return retval; -} - -void Sys_mkdir (char *path) -{ - _mkdir (path); -} - - -/* -=============================================================================== - -SYSTEM IO - -=============================================================================== -*/ - -/* -================ -Sys_MakeCodeWriteable -================ -*/ -void Sys_MakeCodeWriteable (unsigned long startaddr, unsigned long length) -{ - DWORD flOldProtect; - -//@@@ copy on write or just read-write? - if (!VirtualProtect((LPVOID)startaddr, length, PAGE_READWRITE, &flOldProtect)) - Sys_Error("Protection change failed\n"); -} - - -/* -================ -Sys_Init -================ -*/ -void Sys_Init (void) -{ - OSVERSIONINFO vinfo; - - // allocate a named semaphore on the client so the - // front end can tell if it is alive - - if (!COM_CheckParm("-allowmultiple")) - { - // mutex will fail if semaphore already exists - qwclsemaphore = CreateMutex( - NULL, /* Security attributes */ - 0, /* owner */ - "qwcl"); /* Semaphore name */ - if (!qwclsemaphore) - Sys_Error ("QWCL is already running on this system"); - CloseHandle (qwclsemaphore); - - qwclsemaphore = CreateSemaphore( - NULL, /* Security attributes */ - 0, /* Initial count */ - 1, /* Maximum count */ - "qwcl"); /* Semaphore name */ - } - - MaskExceptions (); - Sys_SetFPCW (); - - // make sure the timer is high precision, otherwise - // NT gets 18ms resolution - timeBeginPeriod( 1 ); - - vinfo.dwOSVersionInfoSize = sizeof(vinfo); - - if (!GetVersionEx (&vinfo)) - Sys_Error ("Couldn't get OS info"); - - if ((vinfo.dwMajorVersion < 4) || - (vinfo.dwPlatformId == VER_PLATFORM_WIN32s)) - { - Sys_Error ("QuakeWorld requires at least Win95 or NT 4.0"); - } - - if (vinfo.dwPlatformId == VER_PLATFORM_WIN32_NT) - WinNT = true; - else - WinNT = false; -} - - -void Sys_Error (char *error, ...) -{ - va_list argptr; - char text[1024]; - - Host_Shutdown (); - - va_start (argptr, error); - vsprintf (text, error, argptr); - va_end (argptr); - - MessageBox(NULL, text, "Error", 0 /* MB_OK */ ); - - if (qwclsemaphore) - CloseHandle (qwclsemaphore); - - exit (1); -} - -void Sys_Printf (char *fmt, ...) -{ - va_list argptr; - - va_start (argptr,fmt); - vprintf (fmt, argptr); - va_end (argptr); -} - -void Sys_Quit (void) -{ - VID_ForceUnlockedAndReturnState (); - - Host_Shutdown(); - if (tevent) - CloseHandle (tevent); - - if (qwclsemaphore) - CloseHandle (qwclsemaphore); - - exit (0); -} - - -double Sys_DoubleTime (void) -{ - static DWORD starttime; - static qboolean first = true; - DWORD now; - - now = timeGetTime(); - - if (first) { - first = false; - starttime = now; - return 0.0; - } - - if (now < starttime) // wrapped? - return (now / 1000.0) + (LONG_MAX - starttime / 1000.0); - - if (now - starttime == 0) - return 0.0; - - return (now - starttime) / 1000.0; -} - -char *Sys_ConsoleInput (void) -{ - static char text[256]; - static int len; - INPUT_RECORD recs[1024]; -// int count; - int i, dummy; - int ch, numread, numevents; - HANDLE th; - char *clipText, *textCopied; - - for ( ;; ) - { - if (!GetNumberOfConsoleInputEvents (hinput, &numevents)) - Sys_Error ("Error getting # of console events"); - - if (numevents <= 0) - break; - - if (!ReadConsoleInput(hinput, recs, 1, &numread)) - Sys_Error ("Error reading console input"); - - if (numread != 1) - Sys_Error ("Couldn't read console input"); - - if (recs[0].EventType == KEY_EVENT) - { - if (!recs[0].Event.KeyEvent.bKeyDown) - { - ch = recs[0].Event.KeyEvent.uChar.AsciiChar; - - switch (ch) - { - case '\r': - WriteFile(houtput, "\r\n", 2, &dummy, NULL); - - if (len) - { - text[len] = 0; - len = 0; - return text; - } - break; - - case '\b': - WriteFile(houtput, "\b \b", 3, &dummy, NULL); - if (len) - { - len--; - putch('\b'); - } - break; - - default: - Con_Printf("Stupid: %d\n", recs[0].Event.KeyEvent.dwControlKeyState); - if (((ch=='V' || ch=='v') && (recs[0].Event.KeyEvent.dwControlKeyState & - (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED))) || ((recs[0].Event.KeyEvent.dwControlKeyState - & SHIFT_PRESSED) && (recs[0].Event.KeyEvent.wVirtualKeyCode - ==VK_INSERT))) { - if (OpenClipboard(NULL)) { - th = GetClipboardData(CF_TEXT); - if (th) { - clipText = GlobalLock(th); - if (clipText) { - textCopied = Q_Malloc (GlobalSize(th)+1); - strcpy(textCopied, clipText); -/* Substitutes a NULL for every token */strtok(textCopied, "\n\r\b"); - i = strlen(textCopied); - if (i+len>=256) - i=256-len; - if (i>0) { - textCopied[i]=0; - text[len]=0; - strcat(text, textCopied); - len+=dummy; - WriteFile(houtput, textCopied, i, &dummy, NULL); - } - free(textCopied); - } - GlobalUnlock(th); - } - CloseClipboard(); - } - } else if (ch >= ' ') - { - WriteFile(houtput, &ch, 1, &dummy, NULL); - text[len] = ch; - len = (len + 1) & 0xff; - } - - break; - - } - } - } - } - - return NULL; -} - -void Sys_Sleep (void) -{ -} - - -void Sys_SendKeyEvents (void) -{ - MSG msg; - - while (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) - { - // we always update if there are any event, even if we're paused - scr_skipupdate = 0; - - if (!GetMessage (&msg, NULL, 0, 0)) - Sys_Quit (); - TranslateMessage (&msg); - DispatchMessage (&msg); - } -} - - -/* -============================================================================== - - WINDOWS CRAP - -============================================================================== -*/ - -/* -================== -WinMain -================== -*/ -void SleepUntilInput (int time) -{ - - MsgWaitForMultipleObjects(1, &tevent, FALSE, time, QS_ALLINPUT); -} - - - -/* -================== -WinMain -================== -*/ -HINSTANCE global_hInstance; -int global_nCmdShow; -char *argv[MAX_NUM_ARGVS]; -static char *empty_string = ""; -HWND hwnd_dialog; - - -int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) -{ -// MSG msg; - quakeparms_t parms; - double time, oldtime, newtime; - MEMORYSTATUS lpBuffer; - static char cwd[1024]; - int t; - RECT rect; - - /* previous instances do not exist in Win32 */ - if (hPrevInstance) - return 0; - - global_hInstance = hInstance; - global_nCmdShow = nCmdShow; - - lpBuffer.dwLength = sizeof(MEMORYSTATUS); - GlobalMemoryStatus (&lpBuffer); - - if (!GetCurrentDirectory (sizeof(cwd), cwd)) - Sys_Error ("Couldn't determine current directory"); - - parms.basedir = cwd; - - parms.argc = 1; - argv[0] = empty_string; - - while (*lpCmdLine && (parms.argc < MAX_NUM_ARGVS)) - { - while (*lpCmdLine && ((*lpCmdLine <= 32) || (*lpCmdLine > 126))) - lpCmdLine++; - - if (*lpCmdLine) - { - argv[parms.argc] = lpCmdLine; - parms.argc++; - - while (*lpCmdLine && ((*lpCmdLine > 32) && (*lpCmdLine <= 126))) - lpCmdLine++; - - if (*lpCmdLine) - { - *lpCmdLine = 0; - lpCmdLine++; - } - - } - } - - parms.argv = argv; - - COM_InitArgv (parms.argc, parms.argv); - - parms.argc = com_argc; - parms.argv = com_argv; - - hwnd_dialog = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, NULL); - - if (hwnd_dialog) - { - if (GetWindowRect (hwnd_dialog, &rect)) - { - if (rect.left > (rect.top * 2)) - { - SetWindowPos (hwnd_dialog, 0, - (rect.left / 2) - ((rect.right - rect.left) / 2), - rect.top, 0, 0, - SWP_NOZORDER | SWP_NOSIZE); - } - } - - ShowWindow (hwnd_dialog, SW_SHOWDEFAULT); - UpdateWindow (hwnd_dialog); - SetForegroundWindow (hwnd_dialog); - } - -// take the greater of all the available memory or half the total memory, -// but at least 8 Mb and no more than 16 Mb, unless they explicitly -// request otherwise - parms.memsize = lpBuffer.dwAvailPhys; - - if (parms.memsize < MINIMUM_WIN_MEMORY) - parms.memsize = MINIMUM_WIN_MEMORY; - - if (parms.memsize < (lpBuffer.dwTotalPhys >> 1)) - parms.memsize = lpBuffer.dwTotalPhys >> 1; - - if (parms.memsize > MAXIMUM_WIN_MEMORY) - parms.memsize = MAXIMUM_WIN_MEMORY; - - if (COM_CheckParm ("-heapsize")) - { - t = COM_CheckParm("-heapsize") + 1; - - if (t < com_argc) - parms.memsize = Q_atoi (com_argv[t]) * 1024; - } - - parms.membase = Q_Malloc (parms.memsize); - - tevent = CreateEvent(NULL, FALSE, FALSE, NULL); - - if (!tevent) - Sys_Error ("Couldn't create event"); - - Sys_Init (); - -// because sound is off until we become active - S_BlockSound (); - - Sys_Printf ("Host_Init\n"); - Host_Init (&parms); - - oldtime = Sys_DoubleTime (); - - /* main window message loop */ - while (1) - { - // yield the CPU for a little while when paused, minimized, or not the focus - if ((cl.paused && (!ActiveApp && !DDActive)) || Minimized || block_drawing) - { - SleepUntilInput (PAUSE_SLEEP); - scr_skipupdate = 1; // no point in bothering to draw - } - else if (!ActiveApp && !DDActive) - { - SleepUntilInput (NOT_FOCUS_SLEEP); - } - - newtime = Sys_DoubleTime (); - time = newtime - oldtime; - Host_Frame (time); - oldtime = newtime; - } - - /* return success of application */ - return TRUE; -} - +/* +Copyright (C) 1996-1997 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. + +*/ +// sys_win.h + +#include "quakedef.h" +#include "winquake.h" +#include "resource.h" +#include +#include +#include +#include // _open, etc +#include // _mkdir +#include // _putch + +#define MINIMUM_WIN_MEMORY 0x0c00000 +#define MAXIMUM_WIN_MEMORY 0x1000000 + +#define PAUSE_SLEEP 50 // sleep time on pause or minimization +#define NOT_FOCUS_SLEEP 20 // sleep time when not focus + +int starttime; +qboolean ActiveApp, Minimized; +qboolean WinNT; + +HWND hwnd_dialog; // startup dialog box + +static double pfreq; +static double curtime = 0.0; +static double lastcurtime = 0.0; +static int lowshift; +static HANDLE hinput, houtput; + +HANDLE qwclsemaphore; + +static HANDLE tevent; + +void Sys_InitFloatTime (void); + +void MaskExceptions (void); +void Sys_PopFPCW (void); +void Sys_PushFPCW_SetHigh (void); + +void Sys_DebugLog(char *file, char *fmt, ...) +{ + va_list argptr; + static char data[1024]; + int fd; + + va_start(argptr, fmt); + vsprintf(data, fmt, argptr); + va_end(argptr); + fd = open(file, O_WRONLY | O_CREAT | O_APPEND, 0666); + write(fd, data, strlen(data)); + close(fd); +}; + +/* +=============================================================================== + +FILE IO + +=============================================================================== +*/ + +int Sys_FileTime (char *path) +{ + FILE *f; + int t, retval; + + t = VID_ForceUnlockedAndReturnState (); + + f = fopen(path, "rb"); + + if (f) + { + fclose(f); + retval = 1; + } + else + { + retval = -1; + } + + VID_ForceLockState (t); + return retval; +} + +void Sys_mkdir (char *path) +{ + _mkdir (path); +} + + +/* +=============================================================================== + +SYSTEM IO + +=============================================================================== +*/ + +/* +================ +Sys_MakeCodeWriteable +================ +*/ +void Sys_MakeCodeWriteable (unsigned long startaddr, unsigned long length) +{ + DWORD flOldProtect; + +//@@@ copy on write or just read-write? + if (!VirtualProtect((LPVOID)startaddr, length, PAGE_READWRITE, &flOldProtect)) + Sys_Error("Protection change failed\n"); +} + + +/* +================ +Sys_Init +================ +*/ +void Sys_Init (void) +{ + OSVERSIONINFO vinfo; + + // allocate a named semaphore on the client so the + // front end can tell if it is alive + + if (!COM_CheckParm("-allowmultiple")) + { + // mutex will fail if semaphore already exists + qwclsemaphore = CreateMutex( + NULL, /* Security attributes */ + 0, /* owner */ + "qwcl"); /* Semaphore name */ + if (!qwclsemaphore) + Sys_Error ("QWCL is already running on this system"); + CloseHandle (qwclsemaphore); + + qwclsemaphore = CreateSemaphore( + NULL, /* Security attributes */ + 0, /* Initial count */ + 1, /* Maximum count */ + "qwcl"); /* Semaphore name */ + } + + MaskExceptions (); + Sys_SetFPCW (); + + // make sure the timer is high precision, otherwise + // NT gets 18ms resolution + timeBeginPeriod( 1 ); + + vinfo.dwOSVersionInfoSize = sizeof(vinfo); + + if (!GetVersionEx (&vinfo)) + Sys_Error ("Couldn't get OS info"); + + if ((vinfo.dwMajorVersion < 4) || + (vinfo.dwPlatformId == VER_PLATFORM_WIN32s)) + { + Sys_Error ("QuakeWorld requires at least Win95 or NT 4.0"); + } + + if (vinfo.dwPlatformId == VER_PLATFORM_WIN32_NT) + WinNT = true; + else + WinNT = false; +} + + +void Sys_Error (char *error, ...) +{ + va_list argptr; + char text[1024]; + + Host_Shutdown (); + + va_start (argptr, error); + vsprintf (text, error, argptr); + va_end (argptr); + + MessageBox(NULL, text, "Error", 0 /* MB_OK */ ); + + if (qwclsemaphore) + CloseHandle (qwclsemaphore); + + exit (1); +} + +void Sys_Printf (char *fmt, ...) +{ + va_list argptr; + + va_start (argptr,fmt); + vprintf (fmt, argptr); + va_end (argptr); +} + +void Sys_Quit (void) +{ + VID_ForceUnlockedAndReturnState (); + + Host_Shutdown(); + if (tevent) + CloseHandle (tevent); + + if (qwclsemaphore) + CloseHandle (qwclsemaphore); + + exit (0); +} + + +double Sys_DoubleTime (void) +{ + static DWORD starttime; + static qboolean first = true; + DWORD now; + + now = timeGetTime(); + + if (first) { + first = false; + starttime = now; + return 0.0; + } + + if (now < starttime) // wrapped? + return (now / 1000.0) + (LONG_MAX - starttime / 1000.0); + + if (now - starttime == 0) + return 0.0; + + return (now - starttime) / 1000.0; +} + +char *Sys_ConsoleInput (void) +{ + static char text[256]; + static int len; + INPUT_RECORD recs[1024]; +// int count; + int i, dummy; + int ch, numread, numevents; + HANDLE th; + char *clipText, *textCopied; + + for ( ;; ) + { + if (!GetNumberOfConsoleInputEvents (hinput, &numevents)) + Sys_Error ("Error getting # of console events"); + + if (numevents <= 0) + break; + + if (!ReadConsoleInput(hinput, recs, 1, &numread)) + Sys_Error ("Error reading console input"); + + if (numread != 1) + Sys_Error ("Couldn't read console input"); + + if (recs[0].EventType == KEY_EVENT) + { + if (!recs[0].Event.KeyEvent.bKeyDown) + { + ch = recs[0].Event.KeyEvent.uChar.AsciiChar; + + switch (ch) + { + case '\r': + WriteFile(houtput, "\r\n", 2, &dummy, NULL); + + if (len) + { + text[len] = 0; + len = 0; + return text; + } + break; + + case '\b': + WriteFile(houtput, "\b \b", 3, &dummy, NULL); + if (len) + { + len--; + putch('\b'); + } + break; + + default: + Con_Printf("Stupid: %d\n", recs[0].Event.KeyEvent.dwControlKeyState); + if (((ch=='V' || ch=='v') && (recs[0].Event.KeyEvent.dwControlKeyState & + (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED))) || ((recs[0].Event.KeyEvent.dwControlKeyState + & SHIFT_PRESSED) && (recs[0].Event.KeyEvent.wVirtualKeyCode + ==VK_INSERT))) { + if (OpenClipboard(NULL)) { + th = GetClipboardData(CF_TEXT); + if (th) { + clipText = GlobalLock(th); + if (clipText) { + textCopied = Q_Malloc (GlobalSize(th)+1); + strcpy(textCopied, clipText); +/* Substitutes a NULL for every token */strtok(textCopied, "\n\r\b"); + i = strlen(textCopied); + if (i+len>=256) + i=256-len; + if (i>0) { + textCopied[i]=0; + text[len]=0; + strcat(text, textCopied); + len+=dummy; + WriteFile(houtput, textCopied, i, &dummy, NULL); + } + free(textCopied); + } + GlobalUnlock(th); + } + CloseClipboard(); + } + } else if (ch >= ' ') + { + WriteFile(houtput, &ch, 1, &dummy, NULL); + text[len] = ch; + len = (len + 1) & 0xff; + } + + break; + + } + } + } + } + + return NULL; +} + +void Sys_Sleep (void) +{ +} + + +void Sys_SendKeyEvents (void) +{ + MSG msg; + + while (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) + { + // we always update if there are any event, even if we're paused + scr_skipupdate = 0; + + if (!GetMessage (&msg, NULL, 0, 0)) + Sys_Quit (); + TranslateMessage (&msg); + DispatchMessage (&msg); + } +} + + +/* +============================================================================== + + WINDOWS CRAP + +============================================================================== +*/ + +/* +================== +WinMain +================== +*/ +void SleepUntilInput (int time) +{ + + MsgWaitForMultipleObjects(1, &tevent, FALSE, time, QS_ALLINPUT); +} + + + +/* +================== +WinMain +================== +*/ +HINSTANCE global_hInstance; +int global_nCmdShow; +char *argv[MAX_NUM_ARGVS]; +static char *empty_string = ""; +HWND hwnd_dialog; + + +int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) +{ +// MSG msg; + quakeparms_t parms; + double time, oldtime, newtime; + MEMORYSTATUS lpBuffer; + static char cwd[1024]; + int t; + RECT rect; + + /* previous instances do not exist in Win32 */ + if (hPrevInstance) + return 0; + + global_hInstance = hInstance; + global_nCmdShow = nCmdShow; + + lpBuffer.dwLength = sizeof(MEMORYSTATUS); + GlobalMemoryStatus (&lpBuffer); + + if (!GetCurrentDirectory (sizeof(cwd), cwd)) + Sys_Error ("Couldn't determine current directory"); + + parms.basedir = cwd; + + parms.argc = 1; + argv[0] = empty_string; + + while (*lpCmdLine && (parms.argc < MAX_NUM_ARGVS)) + { + while (*lpCmdLine && ((*lpCmdLine <= 32) || (*lpCmdLine > 126))) + lpCmdLine++; + + if (*lpCmdLine) + { + argv[parms.argc] = lpCmdLine; + parms.argc++; + + while (*lpCmdLine && ((*lpCmdLine > 32) && (*lpCmdLine <= 126))) + lpCmdLine++; + + if (*lpCmdLine) + { + *lpCmdLine = 0; + lpCmdLine++; + } + + } + } + + parms.argv = argv; + + COM_InitArgv (parms.argc, parms.argv); + + parms.argc = com_argc; + parms.argv = com_argv; + + hwnd_dialog = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, NULL); + + if (hwnd_dialog) + { + if (GetWindowRect (hwnd_dialog, &rect)) + { + if (rect.left > (rect.top * 2)) + { + SetWindowPos (hwnd_dialog, 0, + (rect.left / 2) - ((rect.right - rect.left) / 2), + rect.top, 0, 0, + SWP_NOZORDER | SWP_NOSIZE); + } + } + + ShowWindow (hwnd_dialog, SW_SHOWDEFAULT); + UpdateWindow (hwnd_dialog); + SetForegroundWindow (hwnd_dialog); + } + +// take the greater of all the available memory or half the total memory, +// but at least 8 Mb and no more than 16 Mb, unless they explicitly +// request otherwise + parms.memsize = lpBuffer.dwAvailPhys; + + if (parms.memsize < MINIMUM_WIN_MEMORY) + parms.memsize = MINIMUM_WIN_MEMORY; + + if (parms.memsize < (lpBuffer.dwTotalPhys >> 1)) + parms.memsize = lpBuffer.dwTotalPhys >> 1; + + if (parms.memsize > MAXIMUM_WIN_MEMORY) + parms.memsize = MAXIMUM_WIN_MEMORY; + + if (COM_CheckParm ("-heapsize")) + { + t = COM_CheckParm("-heapsize") + 1; + + if (t < com_argc) + parms.memsize = Q_atoi (com_argv[t]) * 1024; + } + + parms.membase = Q_Malloc (parms.memsize); + + tevent = CreateEvent(NULL, FALSE, FALSE, NULL); + + if (!tevent) + Sys_Error ("Couldn't create event"); + + Sys_Init (); + +// because sound is off until we become active + S_BlockSound (); + + Sys_Printf ("Host_Init\n"); + Host_Init (&parms); + + oldtime = Sys_DoubleTime (); + + /* main window message loop */ + while (1) + { + // yield the CPU for a little while when paused, minimized, or not the focus + if ((cl.paused && (!ActiveApp && !DDActive)) || Minimized || block_drawing) + { + SleepUntilInput (PAUSE_SLEEP); + scr_skipupdate = 1; // no point in bothering to draw + } + else if (!ActiveApp && !DDActive) + { + SleepUntilInput (NOT_FOCUS_SLEEP); + } + + newtime = Sys_DoubleTime (); + time = newtime - oldtime; + Host_Frame (time); + oldtime = newtime; + } + + /* return success of application */ + return TRUE; +} + diff --git a/source/sys_x86.s b/source/sys_x86.s index 3314da54..e022ac6b 100644 --- a/source/sys_x86.s +++ b/source/sys_x86.s @@ -1,108 +1,108 @@ -/* -sys_x86.s - x86 assembly-language dependent routines. - -Copyright (C) 1996-1997 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 "asm_i386.h" -#include "quakeasm.h" - -#ifdef id386 - .data - - .align 4 -fpenv: - .long 0, 0, 0, 0, 0, 0, 0, 0 - - .text - -.globl C(MaskExceptions) -C(MaskExceptions): - fnstenv fpenv - orl $0x3F,fpenv - fldenv fpenv - - ret - -#if 0 -.globl C(unmaskexceptions) -C(unmaskexceptions): - fnstenv fpenv - andl $0xFFFFFFE0,fpenv - fldenv fpenv - - ret -#endif - - .data - - .align 4 -.globl ceil_cw, single_cw, full_cw, cw, pushed_cw -ceil_cw: .long 0 -single_cw: .long 0 -full_cw: .long 0 -cw: .long 0 -pushed_cw: .long 0 - - .text - -.globl C(Sys_LowFPPrecision) -C(Sys_LowFPPrecision): - fldcw single_cw - - ret - -.globl C(Sys_HighFPPrecision) -C(Sys_HighFPPrecision): - fldcw full_cw - - ret - -.globl C(Sys_PushFPCW_SetHigh) -C(Sys_PushFPCW_SetHigh): - fnstcw pushed_cw - fldcw full_cw - - ret - -.globl C(Sys_PopFPCW) -C(Sys_PopFPCW): - fldcw pushed_cw - - ret - -.globl C(Sys_SetFPCW) -C(Sys_SetFPCW): - fnstcw cw - movl cw,%eax - andb $0xF0,%ah - orb $0x03,%ah // round mode, 64-bit precision - movl %eax,full_cw - - andb $0xF0,%ah - orb $0x0C,%ah // chop mode, single precision - movl %eax,single_cw - - andb $0xF0,%ah - orb $0x08,%ah // ceil mode, single precision - movl %eax,ceil_cw - - ret -#endif // id386 +/* +sys_x86.s - x86 assembly-language dependent routines. + +Copyright (C) 1996-1997 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 "asm_i386.h" +#include "quakeasm.h" + +#ifdef id386 + .data + + .align 4 +fpenv: + .long 0, 0, 0, 0, 0, 0, 0, 0 + + .text + +.globl C(MaskExceptions) +C(MaskExceptions): + fnstenv fpenv + orl $0x3F,fpenv + fldenv fpenv + + ret + +#if 0 +.globl C(unmaskexceptions) +C(unmaskexceptions): + fnstenv fpenv + andl $0xFFFFFFE0,fpenv + fldenv fpenv + + ret +#endif + + .data + + .align 4 +.globl ceil_cw, single_cw, full_cw, cw, pushed_cw +ceil_cw: .long 0 +single_cw: .long 0 +full_cw: .long 0 +cw: .long 0 +pushed_cw: .long 0 + + .text + +.globl C(Sys_LowFPPrecision) +C(Sys_LowFPPrecision): + fldcw single_cw + + ret + +.globl C(Sys_HighFPPrecision) +C(Sys_HighFPPrecision): + fldcw full_cw + + ret + +.globl C(Sys_PushFPCW_SetHigh) +C(Sys_PushFPCW_SetHigh): + fnstcw pushed_cw + fldcw full_cw + + ret + +.globl C(Sys_PopFPCW) +C(Sys_PopFPCW): + fldcw pushed_cw + + ret + +.globl C(Sys_SetFPCW) +C(Sys_SetFPCW): + fnstcw cw + movl cw,%eax + andb $0xF0,%ah + orb $0x03,%ah // round mode, 64-bit precision + movl %eax,full_cw + + andb $0xF0,%ah + orb $0x0C,%ah // chop mode, single precision + movl %eax,single_cw + + andb $0xF0,%ah + orb $0x08,%ah // ceil mode, single precision + movl %eax,ceil_cw + + ret +#endif // id386 diff --git a/source/teamplay.c b/source/teamplay.c index 2e7b0c9d..9ccc6bd0 100644 --- a/source/teamplay.c +++ b/source/teamplay.c @@ -1,1753 +1,1753 @@ -/* - teamplay.c - - Teamplay enhancements ("proxy features") - - Copyright (C) 2000-2001 Anton Gavrilov - - 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: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA -*/ - -#include "quakedef.h" -#include "version.h" - -cvar_t cl_parsesay = {"cl_parsesay", "0"}; -cvar_t cl_triggers = {"cl_triggers", "0"}; -cvar_t cl_nofake = {"cl_nofake", "0"}; -cvar_t cl_loadlocs = {"cl_loadlocs", "0"}; -cvar_t cl_mapname = {"mapname", "", CVAR_ROM}; - -cvar_t cl_rocket2grenade = {"cl_r2g", "0"}; - -cvar_t cl_teamskin = {"teamskin", ""}; -cvar_t cl_enemyskin = {"enemyskin", ""}; - -cvar_t tp_name_axe = {"tp_name_axe", "axe"}; -cvar_t tp_name_sg = {"tp_name_sg", "sg"}; -cvar_t tp_name_ssg = {"tp_name_ssg", "ssg"}; -cvar_t tp_name_ng = {"tp_name_ng", "ng"}; -cvar_t tp_name_sng = {"tp_name_sng", "sng"}; -cvar_t tp_name_gl = {"tp_name_gl", "gl"}; -cvar_t tp_name_rl = {"tp_name_rl", "rl"}; -cvar_t tp_name_lg = {"tp_name_lg", "lg"}; -cvar_t tp_name_ra = {"tp_name_ra", "ra"}; -cvar_t tp_name_ya = {"tp_name_ya", "ya"}; -cvar_t tp_name_ga = {"tp_name_ga", "ga"}; -cvar_t tp_name_quad = {"tp_name_quad", "quad"}; -cvar_t tp_name_pent = {"tp_name_pent", "pent"}; -cvar_t tp_name_ring = {"tp_name_ring", "ring"}; -cvar_t tp_name_suit = {"tp_name_suit", "suit"}; -cvar_t tp_name_shells = {"tp_name_shells", "shells"}; -cvar_t tp_name_nails = {"tp_name_nails", "nails"}; -cvar_t tp_name_rockets = {"tp_name_rockets", "rockets"}; -cvar_t tp_name_cells = {"tp_name_cells", "cells"}; -cvar_t tp_name_mh = {"tp_name_mh", "mh"}; -cvar_t tp_name_health = {"tp_name_health", "health"}; -cvar_t tp_name_backpack = {"tp_name_backpack", "pack"}; -cvar_t tp_name_flag = {"tp_name_flag", "flag"}; - - -//=========================================================================== -// TRIGGERS -//=========================================================================== - -char *Macro_Location_f (void); -void TP_FindModelNumbers (void); - -#define MAX_LOC_NAME 32 - -// this structure is cleared after entering a new map -typedef struct tvars_s { - int health; - int items; - float respawntrigger_time; - float deathtrigger_time; - float f_version_reply_time; - char lastdeathloc[MAX_LOC_NAME]; - char tookitem[32]; - char last_tooktrigger[32]; -} tvars_t; - -tvars_t vars; - -void TP_ExecTrigger (char *s) -{ - if (!cl_triggers.value || cls.demoplayback) - return; - - if (Cmd_FindAlias(s)) - { - char *astr, *p; - qboolean quote = false; - - astr = Cmd_AliasString (s); - for (p=astr ; *p ; p++) - { - if (*p == '"') - quote = !quote; - if (!quote && *p == ';') - { - // more than one command, add it to the command buffer - Cbuf_AddText (astr); - Cbuf_AddText ("\n"); - return; - } - } - // a single line, so execute it right away - Cmd_ExecuteString (astr); - return; - } -} - - -/* -========================================================================== - MACRO FUNCTIONS -========================================================================== -*/ - -#define MAX_MACRO_VALUE 256 -static char macro_buf[MAX_MACRO_VALUE]; - - -char *Macro_Health_f (void) -{ - sprintf(macro_buf, "%i", cl.stats[STAT_HEALTH]); - return macro_buf; -} - -char *Macro_Armor_f (void) -{ - sprintf(macro_buf, "%i", cl.stats[STAT_ARMOR]); - return macro_buf; -} - -char *Macro_Shells_f (void) -{ - sprintf(macro_buf, "%i", cl.stats[STAT_SHELLS]); - return macro_buf; -} - -char *Macro_Nails_f (void) -{ - sprintf(macro_buf, "%i", cl.stats[STAT_NAILS]); - return macro_buf; -} - -char *Macro_Rockets_f (void) -{ - sprintf(macro_buf, "%i", cl.stats[STAT_ROCKETS]); - return macro_buf; -} - -char *Macro_Cells_f (void) -{ - sprintf(macro_buf, "%i", cl.stats[STAT_CELLS]); - return macro_buf; -} - -char *Macro_Ammo_f (void) -{ - sprintf(macro_buf, "%i", cl.stats[STAT_AMMO]); - return macro_buf; -} - -char *Macro_Weapon_f (void) -{ - switch (cl.stats[STAT_ACTIVEWEAPON]) - { - case IT_AXE: return "axe"; - case IT_SHOTGUN: return "sg"; - case IT_SUPER_SHOTGUN: return "ssg"; - case IT_NAILGUN: return "ng"; - case IT_SUPER_NAILGUN: return "sng"; - case IT_GRENADE_LAUNCHER: return "gl"; - case IT_ROCKET_LAUNCHER: return "rl"; - case IT_LIGHTNING: return "lg"; - default: - return ""; - } -} - -char *Macro_WeaponNum_f (void) -{ - switch (cl.stats[STAT_ACTIVEWEAPON]) - { - case IT_AXE: return "1"; - case IT_SHOTGUN: return "2"; - case IT_SUPER_SHOTGUN: return "3"; - case IT_NAILGUN: return "4"; - case IT_SUPER_NAILGUN: return "5"; - case IT_GRENADE_LAUNCHER: return "6"; - case IT_ROCKET_LAUNCHER: return "7"; - case IT_LIGHTNING: return "8"; - default: - return "0"; - } -} - -int _Macro_BestWeapon (void) -{ - int best; - - best = 0; - if (cl.stats[STAT_ITEMS] & IT_AXE) - best = IT_AXE; - if (cl.stats[STAT_ITEMS] & IT_SHOTGUN && cl.stats[STAT_SHELLS] >= 1) - best = IT_SHOTGUN; - if (cl.stats[STAT_ITEMS] & IT_SUPER_SHOTGUN && cl.stats[STAT_SHELLS] >= 2) - best = IT_SUPER_SHOTGUN; - if (cl.stats[STAT_ITEMS] & IT_NAILGUN && cl.stats[STAT_NAILS] >= 1) - best = IT_NAILGUN; - if (cl.stats[STAT_ITEMS] & IT_SUPER_NAILGUN && cl.stats[STAT_NAILS] >= 2) - best = IT_SUPER_NAILGUN; - if (cl.stats[STAT_ITEMS] & IT_GRENADE_LAUNCHER && cl.stats[STAT_ROCKETS] >= 1) - best = IT_GRENADE_LAUNCHER; - if (cl.stats[STAT_ITEMS] & IT_LIGHTNING && cl.stats[STAT_CELLS] >= 1) - best = IT_LIGHTNING; - if (cl.stats[STAT_ITEMS] & IT_ROCKET_LAUNCHER && cl.stats[STAT_ROCKETS] >= 1) - best = IT_ROCKET_LAUNCHER; - - return best; -} - -char *Macro_BestWeapon_f (void) -{ - switch (_Macro_BestWeapon()) - { - case IT_AXE: return "axe"; - case IT_SHOTGUN: return "sg"; - case IT_SUPER_SHOTGUN: return "ssg"; - case IT_NAILGUN: return "ng"; - case IT_SUPER_NAILGUN: return "sng"; - case IT_GRENADE_LAUNCHER: return "gl"; - case IT_ROCKET_LAUNCHER: return "rl"; - case IT_LIGHTNING: return "lg"; - default: - return ""; - } -} - -char *Macro_BestAmmo_f (void) -{ - switch (_Macro_BestWeapon()) - { - case IT_SHOTGUN: case IT_SUPER_SHOTGUN: - sprintf(macro_buf, "%i", cl.stats[STAT_SHELLS]); - return macro_buf; - - case IT_NAILGUN: case IT_SUPER_NAILGUN: - sprintf(macro_buf, "%i", cl.stats[STAT_NAILS]); - return macro_buf; - - case IT_GRENADE_LAUNCHER: case IT_ROCKET_LAUNCHER: - sprintf(macro_buf, "%i", cl.stats[STAT_ROCKETS]); - return macro_buf; - - case IT_LIGHTNING: - sprintf(macro_buf, "%i", cl.stats[STAT_CELLS]); - return macro_buf; - - default: - return "0"; - } -} - -// needed for %b parsing -char *Macro_BestWeaponAndAmmo_f (void) -{ - char buf[MAX_MACRO_VALUE]; - sprintf (buf, "%s:%s", Macro_BestWeapon_f(), Macro_BestAmmo_f()); - strcpy (macro_buf, buf); - return macro_buf; -} - -char *Macro_ArmorType_f (void) -{ - if (cl.stats[STAT_ITEMS] & IT_ARMOR1) - return "ga"; - else if (cl.stats[STAT_ITEMS] & IT_ARMOR2) - return "ya"; - else if (cl.stats[STAT_ITEMS] & IT_ARMOR3) - return "ra"; - else - return ""; // no armor at all -} - -char *Macro_Powerups_f (void) -{ - int effects; - - macro_buf[0] = 0; - - if (cl.stats[STAT_ITEMS] & IT_QUAD) - strcpy(macro_buf, "quad"); - - if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY) { - if (macro_buf[0]) - strcat(macro_buf, "/"); - strcat(macro_buf, "pent"); - } - - if (cl.stats[STAT_ITEMS] & IT_INVISIBILITY) { - if (macro_buf[0]) - strcat(macro_buf, "/"); - strcat(macro_buf, "ring"); - } - - effects = cl.frames[cl.parsecount&UPDATE_MASK].playerstate[cl.playernum].effects; - if ( (effects & (EF_FLAG1|EF_FLAG2)) || // CTF - (cl.teamfortress && cl.stats[STAT_ITEMS] & (IT_KEY1|IT_KEY2) // TF - /*&& (effects & EF_DIMLIGHT))*/) ) - { - if (macro_buf[0]) - strcat(macro_buf, "/"); - strcat(macro_buf, "flag"); - } - - return macro_buf; -} - -char *Macro_Location2_f (void) -{ - if (vars.deathtrigger_time && realtime - vars.deathtrigger_time <= 5) - return vars.lastdeathloc; - return Macro_Location_f(); -} - -char *Macro_LastDeath_f (void) -{ - if (vars.deathtrigger_time) - return vars.lastdeathloc; - else - return "someplace"; -} - -char *Macro_Time_f (void) -{ - time_t t; - struct tm *ptm; - - macro_buf[0] = 0; - time(&t); - ptm = localtime(&t); - strftime(macro_buf, sizeof(macro_buf)-1, "%H:%M", ptm); - return macro_buf; -} - -char *Macro_Date_f (void) -{ - time_t t; - struct tm *ptm; - - macro_buf[0] = 0; - time(&t); - ptm = localtime(&t); - strftime(macro_buf, sizeof(macro_buf)-1, "%d.%m.%y", ptm); - return macro_buf; -} - -// returns the last item picked up -char *Macro_Item_f (void) -{ - strcpy (macro_buf, vars.tookitem); - return macro_buf; -} - -// returns the last item that triggered f_took -char *Macro_Took_f (void) -{ - strcpy (macro_buf, vars.last_tooktrigger); - return macro_buf; -} - -typedef struct -{ - char *name; - char *(*func) (void); -} macro_command_t; - -// Note: longer macro names like "armortype" must be defined -// _before_ the shorter ones like "armor" to be parsed properly -macro_command_t macro_commands[] = -{ - {"health", Macro_Health_f}, - {"armortype", Macro_ArmorType_f}, - {"armor", Macro_Armor_f}, - {"shells", Macro_Shells_f}, - {"nails", Macro_Nails_f}, - {"rockets", Macro_Rockets_f}, - {"cells", Macro_Cells_f}, - {"weaponnum", Macro_WeaponNum_f}, - {"weapon", Macro_Weapon_f}, - {"ammo", Macro_Ammo_f}, - {"bestweapon", Macro_BestWeapon_f}, - {"bestammo", Macro_BestAmmo_f}, - {"powerups", Macro_Powerups_f}, - {"location", Macro_Location_f}, - {"time", Macro_Time_f}, - {"date", Macro_Date_f}, - {"item", Macro_Item_f}, - {"took", Macro_Took_f}, - {NULL, NULL} -}; - -#define MAX_MACRO_STRING 1024 - -/* -============== -TP_MacroString - -returns NULL if no matching macro was found -============== -*/ -int macro_length; // length of macro name - -char *TP_MacroString (char *s) -{ - static char buf[MAX_MACRO_STRING]; - macro_command_t *macro; - - macro = macro_commands; - while (macro->name) { - if (!Q_strncasecmp(s, macro->name, strlen(macro->name))) - { - macro_length = strlen(macro->name); - return macro->func(); - } - macro++; - } - - macro_length = 0; - return NULL; -} - -/* -============= -TP_ParseChatString - -Parses %a-like expressions -============= -*/ -char *TP_ParseMacroString (char *string) -{ - static char buf[MAX_MACRO_STRING]; - char *s; - int i; - char *macro_string; - char ch; - - if (!cl_parsesay.value) - return string; - - s = string; - i = 0; - - while (*s && i < MAX_MACRO_STRING-1) - { - // check %[P], etc - if (*s == '%' && s[1]=='[' && s[2] && s[3]==']') - { - static char mbuf[MAX_MACRO_VALUE]; - switch (s[2]) { - case 'a': - macro_string = Macro_ArmorType_f(); - if (!macro_string[0]) - macro_string = "a"; - if (cl.stats[STAT_ARMOR] < 30) - sprintf (mbuf, "\x10%s:%i\x11", macro_string, cl.stats[STAT_ARMOR]); - else - sprintf (mbuf, "%s:%i", macro_string, cl.stats[STAT_ARMOR]); - macro_string = mbuf; - break; - - case 'h': - if (cl.stats[STAT_HEALTH] >= 50) - sprintf (macro_buf, "%i", cl.stats[STAT_HEALTH]); - else - sprintf (macro_buf, "\x10%i\x11", cl.stats[STAT_HEALTH]); - macro_string = macro_buf; - break; - - case 'P': - macro_string = Macro_Powerups_f(); - if (macro_string[0]) - sprintf (mbuf, "\x10%s\x11", macro_string); - else - mbuf[0] = 0; - macro_string = mbuf; - break; - - // todo: %[w], %[b] - - default: - buf[i++] = *s++; - continue; - } - if (i + strlen(macro_string) >= MAX_MACRO_STRING-1) - Sys_Error("TP_ParseMacroString: macro string length > MAX_MACRO_STRING)"); - strcpy (&buf[i], macro_string); - i += strlen(macro_string); - s += 4; // skip %[] - continue; - } - - // check %a, etc - if (*s == '%') - { - switch (s[1]) - { - case 'a': macro_string = Macro_Armor_f(); break; - case 'A': macro_string = Macro_ArmorType_f(); break; - case 'b': macro_string = Macro_BestWeaponAndAmmo_f(); break; - case 'c': macro_string = Macro_Cells_f(); break; - case 'd': macro_string = Macro_LastDeath_f(); break; - case 'h': macro_string = Macro_Health_f(); break; - case 'i': macro_string = vars.tookitem; break; - case 'I': macro_string = vars.last_tooktrigger; break; - case 'l': macro_string = Macro_Location_f(); break; - case 'L': macro_string = Macro_Location2_f(); break; - case 'P': - case 'p': macro_string = Macro_Powerups_f(); break; - case 'r': macro_string = Macro_Rockets_f(); break; - case 'w': macro_string = Macro_Weapon_f(); break; - case 'W': macro_string = Macro_Ammo_f(); break; - default: - buf[i++] = *s++; - continue; - } - if (i + strlen(macro_string) >= MAX_MACRO_STRING-1) - Sys_Error("TP_ParseMacroString: macro string length > MAX_MACRO_STRING)"); - strcpy (&buf[i], macro_string); - i += strlen(macro_string); - s += 2; // skip % and letter - continue; - } - - // "fun chars" - if (*s == '$') - { - ch = 0; - switch (s[1]) - { - case '\\': ch = 0x0D; break; - case ':': ch = 0x0A; break; - case '[': ch = 0x10; break; - case ']': ch = 0x11; break; - case 'G': ch = 0x86; break; - case 'R': ch = 0x87; break; - case 'Y': ch = 0x88; break; - case 'B': ch = 0x89; break; - } - - if (ch) { - buf[i++] = ch; - s += 2; - continue; - } - } - - buf[i++] = *s++; - } - buf[i] = 0; - - return buf; -} - -/* -============== -TP_MacroList -============== -*/ -void TP_MacroList_f (void) -{ - macro_command_t *macro; - int i; - - for (macro=macro_commands,i=0 ; macro->name ; macro++,i++) - Con_Printf ("%s\n", macro->name); - - Con_Printf ("------------\n%d macros\n", i); -} - -/* -============================================================================= - - PROXY .LOC FILES - -============================================================================= -*/ - -typedef struct locdata_s { - vec3_t coord; - char name[MAX_LOC_NAME]; -} locdata_t; - -#define MAX_LOC_ENTRIES 1024 - -locdata_t locdata[MAX_LOC_ENTRIES]; // FIXME: allocate dynamically? -int loc_numentries; - -#define SKIPBLANKS(ptr) while (*ptr == ' ' || *ptr == 9 || *ptr == 13) ptr++ -#define SKIPTOEOL(ptr) while (*ptr != 10 && *ptr == 0) ptr++ - -void TP_LoadLocFile (char *path, qboolean quiet) -{ - char *buf, *p; - int i, n, sign; - int line; - int nameindex; - int mark; - char locname[MAX_OSPATH]; - - if (!*path) - return; - - strcpy (locname, "locs/"); - if (strlen(path) + strlen(locname) + 2+4 > MAX_OSPATH) - { - Con_Printf ("TP_LoadLocFile: path name > MAX_OSPATH\n"); - return; - } - strcat (locname, path); - if (!strstr(locname, ".")) - strcat (locname, ".loc"); // Add default extension - - mark = Hunk_LowMark (); - buf = (char *) COM_LoadHunkFile (locname); - - if (!buf) - { - if (!quiet) - Con_Printf ("Could not load %s\n", locname); - return; - } - -// Parse the whole file now - - loc_numentries = 0; - - p = buf; - line = 1; - - while (1) - { -// while (*buf == ' ' || *buf == 9) -// buf++; - SKIPBLANKS(p); - - if (*p == 0) - goto _endoffile; - - if (*p == 10 || (*p == '/' && p[1] == '/')) - { - p++; - goto _endofline; - } - - for (i = 0; i < 3; i++) - { - n = 0; - sign = 1; - while (1) - { - switch (*p++) - { - case ' ': case 9: - goto _next; - - case '-': - if (n) - { - Con_Printf ("Error in loc file on line #%i\n", line); - SKIPTOEOL(p); - goto _endofline; - } - sign = -1; - break; - - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - n = n*10 + (p[-1] - '0'); - break; - - default: // including eol or eof - Con_Printf ("Error in loc file on line #%i\n", line); - SKIPTOEOL(p); - goto _endofline; - } - } -_next: - n *= sign; - locdata[loc_numentries].coord[i] = n; - - SKIPBLANKS(p); - } - - -// Save the location's name -// - nameindex = 0; - - while (1) - { - switch (*p) - { - case 13: - p++; - break; - - case 10: case 0: - locdata[loc_numentries].name[nameindex] = 0; - loc_numentries++; - - if (loc_numentries >= MAX_LOC_ENTRIES) - goto _endoffile; - - // leave the 0 or 10 in buffer, so it is parsed properly - goto _endofline; - - default: - if (nameindex < MAX_LOC_NAME-1) - locdata[loc_numentries].name[nameindex++] = *p; - p++; - } - } -_endofline: - line++; - } -_endoffile: - - Hunk_FreeToLowMark (mark); - - if (quiet) - Con_Printf ("Loaded %s\n", locname); - else - Con_Printf ("Loaded %s (%i locations)\n", locname, loc_numentries); -} - -void TP_LoadLocFile_f (void) -{ - if (Cmd_Argc() != 2) - { - Con_Printf ("loadloc : load a loc file\n"); - return; - } - - TP_LoadLocFile (Cmd_Argv(1), false); -} - -extern int parsecountmod; -char *Macro_Location_f (void) -{ - int i; - int min_num; - vec_t min_dist; - vec3_t vec; - vec3_t org; - - if (!loc_numentries || (cls.state != ca_active)) - return "someplace"; - - VectorCopy (cl.frames[parsecountmod].playerstate[cl.playernum].origin, org); - for (i = 0; i < 3; i++) - org[i] *= 8; - - min_num = 0; - min_dist = 9999999; - - for (i = 0; i < loc_numentries; i++) - { -// Con_DPrintf ("%f %f %f: %s\n", locdata[i].coord[0], locdata[i].coord[1], locdata[i].coord[2], locdata[i].name); - VectorSubtract (org, locdata[i].coord, vec); - if (Length(vec) < min_dist) - { - min_num = i; - min_dist = Length(vec); - } - } - - return locdata[min_num].name; -} - -/* -============================================================================= - - MESSAGE TRIGGERS - -============================================================================= -*/ - -typedef struct msg_trigger_s { - char name[32]; - char string[64]; - int level; - struct msg_trigger_s *next; -} msg_trigger_t; - -static msg_trigger_t *msg_triggers; - -msg_trigger_t *TP_FindTrigger (char *name) -{ - msg_trigger_t *t; - - for (t=msg_triggers; t; t=t->next) - if (!strcmp(t->name, name)) - return t; - - return NULL; -} - - -void TP_MsgTrigger_f (void) -{ - int c; - char *name; - msg_trigger_t *trig; - - c = Cmd_Argc(); - - if (c > 5) { - Con_Printf ("msg_trigger \"string\" [-l ]\n"); - return; - } - - if (c == 1) { - if (!msg_triggers) - Con_Printf ("no triggers defined\n"); - else - for (trig=msg_triggers; trig; trig=trig->next) - Con_Printf ("%s : \"%s\"\n", trig->name, trig->string); - return; - } - - name = Cmd_Argv(1); - if (strlen(name) > 31) { - Con_Printf ("trigger name too long\n"); - return; - } - - if (c == 2) { - trig = TP_FindTrigger (name); - if (trig) - Con_Printf ("%s: \"%s\"\n", trig->name, trig->string); - else - Con_Printf ("trigger \"%s\" not found\n", name); - return; - } - - if (c >= 3) { - if (strlen(Cmd_Argv(2)) > 63) { - Con_Printf ("trigger string too long\n"); - return; - } - - trig = TP_FindTrigger (name); - - if (!trig) { - // allocate new trigger - trig = Z_Malloc (sizeof(msg_trigger_t)); - trig->next = msg_triggers; - msg_triggers = trig; - strcpy (trig->name, name); - trig->level = PRINT_HIGH; - } - - strcpy (trig->string, Cmd_Argv(2)); - if (c == 5 && !Q_strcasecmp (Cmd_Argv(3), "-l")) { - trig->level = Q_atoi (Cmd_Argv(4)); - if ((unsigned)trig->level > PRINT_CHAT) - trig->level = PRINT_HIGH; - } - } -} - -char *trigger_commands[] = { - "play", - "playvol", - "stopsound", - "set", - "echo", - "say", - "say_team", - "alias", - "unalias", - "msg_trigger", - "inc", - "bind", - "unbind", - "record", - "easyrecord", - "stop" - "if" -}; - -#define NUM_TRIGGER_COMMANDS (sizeof(trigger_commands)/sizeof(trigger_commands[0])) - -void TP_ExecuteTriggerString (char *text) -{ - static char buf[1024]; - char *arg0; - int i; - cmd_function_t *cmd; - - Cmd_ExpandString (text, buf); - Cmd_TokenizeString (buf); - - if (!Cmd_Argc()) - return; // no tokens - -// check cvars - if (Cvar_Command()) - return; - -// check commands - arg0 = Cmd_Argv(0); - - for (i=0; i < NUM_TRIGGER_COMMANDS ; i++) - if (!Q_strcasecmp(arg0, trigger_commands[i])) - { - cmd = Cmd_FindCommand (arg0); - if (cmd) { - if (!cmd->function) - Cmd_ForwardToServer (); - else - cmd->function (); - return; - } - } - - if (cl_warncmd.value || developer.value) - Con_Printf ("Invalid trigger command: \"%s\"\n", arg0); -} - - -void TP_ExecuteTriggerBuf (char *text) -{ - char line[1024]; - int i, quotes; - - while (*text) - { - quotes = 0; - for (i=0 ; text[i] ; i++) - { - if (text[i] == '"') - quotes++; - if ( !(quotes&1) && text[i] == ';' ) - break; // don't break if inside a quoted string - if (text[i] == '\n') - break; - } - memcpy (line, text, i); - line[i] = 0; - TP_ExecuteTriggerString (line); - if (!text[i]) - break; - text += i + 1; - } -} - -void TP_SearchForMsgTriggers (char *s, int level) -{ - msg_trigger_t *t; - char *string; - - if (cls.demoplayback) - return; - - for (t=msg_triggers; t; t=t->next) - if (t->level == level && t->string[0] && strstr(s, t->string)) - { - if (level == PRINT_CHAT && ( - strstr (s, "f_version") || strstr (s, "f_system") || - strstr (s, "f_speed") || strstr (s, "f_modified"))) - continue; // don't let llamas fake proxy replies - - string = Cmd_AliasString (t->name); - if (string) - TP_ExecuteTriggerBuf (string); - else - Con_Printf ("trigger \"%s\" has no matching alias\n", t->name); - } -} - - -void TP_CheckVersionRequest (char *s) -{ - char buf[11]; - int i; - - if (cl.spectator) - return; - - if (vars.f_version_reply_time - && realtime - vars.f_version_reply_time < 20) - return; // don't reply again if 20 seconds haven't passed - - while (1) - { - switch (*s++) - { - case 0: - case '\n': - return; - case ':': - case (char)':'|128: - goto ok; - } - } - return; - -ok: - for (i = 0; i < 11 && s[i]; i++) - buf[i] = s[i] &~ 128; // strip high bit - - if (!strncmp(buf, " f_version\n", 11) || !strncmp(buf, " z_version\n", 11)) - { -#ifdef RELEASE_VERSION - Cbuf_AddText (va("say QWExtended Client version %s " - QW_PLATFORM ":" QW_RENDERER "\n", QWE_VERSION)); -#else - Cbuf_AddText (va("say QWExtended Client version %s (Build %04d) " - QW_PLATFORM ":" QW_RENDERER "\n", QWE_VERSION, build_number())); -#endif - vars.f_version_reply_time = realtime; - } -} - - -int TP_CountPlayers () -{ - int i, count; - - count = 0; - for (i = 0; i < MAX_CLIENTS ; i++) { - if (cl.players[i].name[0] && !cl.players[i].spectator) - count++; - } - - return count; -} - -char *TP_EnemyTeam () -{ - int i; - char myteam[MAX_INFO_STRING]; - static char enemyteam[MAX_INFO_STRING]; - - strcpy (myteam, Info_ValueForKey(cls.userinfo, "team")); - - for (i = 0; i < MAX_CLIENTS ; i++) { - if (cl.players[i].name[0] && !cl.players[i].spectator) - { - strcpy (enemyteam, Info_ValueForKey(cl.players[i].userinfo, "team")); - if (strcmp(myteam, enemyteam) != 0) - return enemyteam; - } - } - return ""; -} - -char *TP_PlayerName () -{ - static char myname[MAX_INFO_STRING]; - - strcpy (myname, Info_ValueForKey(cl.players[cl.playernum].userinfo, "name")); - return myname; -} - -char *TP_PlayerTeam () -{ - static char myteam[MAX_INFO_STRING]; - - strcpy (myteam, Info_ValueForKey(cl.players[cl.playernum].userinfo, "team")); - return myteam; -} - -char *TP_EnemyName () -{ - int i; - char *myname; - static char enemyname[MAX_INFO_STRING]; - - myname = TP_PlayerName (); - - for (i = 0; i < MAX_CLIENTS ; i++) { - if (cl.players[i].name[0] && !cl.players[i].spectator) - { - strcpy (enemyname, Info_ValueForKey(cl.players[i].userinfo, "name")); - if (strcmp(enemyname, myname) != 0) - return enemyname; - } - } - return ""; -} - -char *TP_MapName () -{ - return cl_mapname.string; -} - -/* -============================================================================= - TEAMCOLOR & ENEMYCOLOR -============================================================================= -*/ - -int cl_teamtopcolor = -1; -int cl_teambottomcolor; -int cl_enemytopcolor = -1; -int cl_enemybottomcolor; - -void TP_TeamColor_f (void) -{ - int top, bottom; - int i; - - if (Cmd_Argc() == 1) - { - if (cl_teamtopcolor < 0) - Con_Printf ("\"teamcolor\" is \"off\"\n"); - else - Con_Printf ("\"teamcolor\" is \"%i %i\"\n", - cl_teamtopcolor, - cl_teambottomcolor); - return; - } - - if (!strcmp(Cmd_Argv(1), "off")) - { - cl_teamtopcolor = -1; - for (i = 0; i < MAX_CLIENTS; i++) - CL_NewTranslation(i); - return; - } - - if (Cmd_Argc() == 2) - top = bottom = atoi(Cmd_Argv(1)); - else { - top = atoi(Cmd_Argv(1)); - bottom = atoi(Cmd_Argv(2)); - } - - top &= 15; - if (top > 13) - top = 13; - bottom &= 15; - if (bottom > 13) - bottom = 13; - -// if (top != cl_teamtopcolor || bottom != cl_teambottomcolor) - { - cl_teamtopcolor = top; - cl_teambottomcolor = bottom; - - for (i = 0; i < MAX_CLIENTS; i++) - CL_NewTranslation(i); - } -} - -void TP_EnemyColor_f (void) -{ - int top, bottom; - int i; - - if (Cmd_Argc() == 1) - { - if (cl_enemytopcolor < 0) - Con_Printf ("\"enemycolor\" is \"off\"\n"); - else - Con_Printf ("\"enemycolor\" is \"%i %i\"\n", - cl_enemytopcolor, - cl_enemybottomcolor); - return; - } - - if (!strcmp(Cmd_Argv(1), "off")) - { - cl_enemytopcolor = -1; - for (i = 0; i < MAX_CLIENTS; i++) - CL_NewTranslation(i); - return; - } - - if (Cmd_Argc() == 2) - top = bottom = atoi(Cmd_Argv(1)); - else { - top = atoi(Cmd_Argv(1)); - bottom = atoi(Cmd_Argv(2)); - } - - top &= 15; - if (top > 13) - top = 13; - bottom &= 15; - if (bottom > 13) - bottom = 13; - -// if (top != cl_enemytopcolor || bottom != cl_enemybottomcolor) - { - cl_enemytopcolor = top; - cl_enemybottomcolor = bottom; - - for (i = 0; i < MAX_CLIENTS; i++) - CL_NewTranslation(i); - } -} - -//=================================================================== - -void TP_FixTeamSets(void) -{ - int i; - - for (i = 0; i < MAX_CLIENTS; i++) { - Skin_Find(&cl.players[i]); - CL_NewTranslation(i); - } -} - -void TP_NewMap () -{ - static char last_map[MAX_QPATH] = {'\0'}; - char mapname[MAX_QPATH]; - - memset (&vars, 0, sizeof(vars)); - TP_FindModelNumbers (); - - COM_StripExtension (COM_SkipPath (cl.worldmodel->name), mapname); - if (strcmp(mapname, last_map)) - { // map name has changed - loc_numentries = 0; // clear loc file - if (cl_loadlocs.value && !cls.demoplayback ) { - char locname[MAX_OSPATH]; - _snprintf (locname, MAX_OSPATH, "%s.loc", mapname); - TP_LoadLocFile (locname, true); - } - strcpy (last_map, mapname); - Cvar_SetROM (&cl_mapname, mapname); - } - - TP_ExecTrigger ("f_newmap"); -} - -/* -====================== -TP_CategorizeMessage - -returns a combination of these values: -0 -- unknown (probably generated by the server) -1 -- normal -2 -- team message -4 -- spectator -Note that sometimes we can't be sure who really sent the message, -e.g. when there's a player "unnamed" in your team and "(unnamed)" -in the enemy team. The result will be 3 (1+2) - -Never returns 2 if we are a spectator. -====================== -*/ -int TP_CategorizeMessage (char *s) -{ - int i, msglen, len; - int flags; - player_info_t *player; - - flags = 0; - msglen = strlen(s); - if (!msglen) - return 0; - - for (i=0, player=cl.players ; i < MAX_CLIENTS ; i++, player++) - { - len = strlen(player->name); - if (!len) - continue; - // check messagemode1 - if (len+2 <= msglen && s[len] == ':' && s[len+1] == ' ' && - !strncmp(player->name, s, len)) - { - if (player->spectator) - flags |= 4; - else - flags |= 1; - } - // check messagemode2 - else if (s[0] == '(' && !cl.spectator && len+4 <= msglen && - !strncmp(s+len+1, "): ", 3) && - !strncmp(player->name, s+1, len)) - { - // no team messages in teamplay 0, except for our own - if (i == cl.playernum || ( atoi(Info_ValueForKey(cl.serverinfo, "teamplay")) - && !strcmp(cl.players[cl.playernum].team, player->team)) ) - flags |= 2; - } - } - - return flags; -} - -//=================================================================== -// Pickup triggers -// - -// symbolic names used in tp_took command -char *pknames[] = {"quad", "pent", "ring", "suit", "ra", "ya", "ga", -"mh", "health", "lg", "rl", "gl", "sng", "ng", "ssg", "pack", -"cells", "rockets", "nails", "shells", "flag"}; - -enum {pk_quad, pk_pent, pk_ring, pk_suit, pk_ra, pk_ya, pk_ga, -pk_mh, pk_health, pk_lg, pk_rl, pk_gl, pk_sng, pk_ng, pk_ssg, pk_pack, -pk_cells, pk_rockets, pk_nails, pk_shells, pk_flag, MAX_PKFLAGS}; - -#define default_pkflags ((1<packet_entities; - bestdist = 250; - bestidx = 0; - for (i=0,ent=pak->entities ; inum_entities ; i++,ent++) - { - int j, dist; - - j = ent->modelindex; - switch (type) - { - case 0: // weapon - if (j != tp_ssgindex && j != tp_ngindex && j != tp_sngindex - && j != tp_glindex && j != tp_rlindex && j != tp_lgindex) - continue; - break; - case 1: // armor - if (j != tp_armorindex) - continue; - break; - case 2: // ammo or backpack - if (j != tp_packindex && j != tp_shells1index && j != tp_shells2index - && j != tp_nails1index && j != tp_nails2index - && j != tp_rockets1index && j != tp_rockets2index - && j != tp_cells1index && j != tp_cells2index) - continue; - } - VectorSubtract (ent->origin, org, v); - if ((dist = Length(v)) > bestdist) - continue; - bestdist = dist; - bestidx = j; - bestskin = ent->skinnum; - } - - if (type == 1 && bestidx) // armor - return -(bestskin + 1); // -1=green, -2=yellow, -3=red - - return bestidx; -} - -static int CountTeammates () -{ - int i, count; - player_info_t *player; - char *myteam; - - count = 0; - myteam = cl.players[cl.playernum].team; - for (i=0, player=cl.players; i < MAX_CLIENTS ; i++, player++) { - if (player->name[0] && !player->spectator && (i != cl.playernum) - && !strcmp(player->team, myteam)) - count++; - } - - return count; -} - -static void ExecTookTrigger (char *s, int flag) -{ - strcpy (vars.tookitem, s); - if (pkflags & (1< 0) - { - strcpy (vars.last_tooktrigger, s); - TP_ExecTrigger ("f_took"); - } - } -} - -void TP_CheckPickupSound (char *s) -{ - int idx; - - if (cl.spectator || !atoi(Info_ValueForKey(cl.serverinfo, "teamplay"))) - return; - - if (!strcmp(s, "items/damage.wav")) - ExecTookTrigger (tp_name_quad.string, pk_quad); - else if (!strcmp(s, "items/protect.wav")) - ExecTookTrigger (tp_name_pent.string, pk_pent); - else if (!strcmp(s, "items/inv1.wav")) - ExecTookTrigger (tp_name_ring.string, pk_ring); - else if (!strcmp(s, "items/suit.wav")) - ExecTookTrigger (tp_name_suit.string, pk_suit); - else if (!strcmp(s, "items/health1.wav") || - !strcmp(s, "items/r_item1.wav")) - ExecTookTrigger (tp_name_health.string, pk_health); - else if (!strcmp(s, "items/r_item2.wav")) - ExecTookTrigger (tp_name_mh.string, pk_mh); - else - goto more; - return; - -more: - if (!cl.validsequence) - return; - - // weapons - if (!strcmp(s, "weapons/pkup.wav")) - { - int deathmatch; - - deathmatch = atoi(Info_ValueForKey(cl.serverinfo, "deathmatch")); - if (deathmatch == 2 || deathmatch == 3) - return; - idx = FindNearestItem(0); - if (idx == tp_ssgindex) - ExecTookTrigger (tp_name_ssg.string, pk_ssg); - else if (idx == tp_ngindex) - ExecTookTrigger (tp_name_ng.string, pk_ng); - else if (idx == tp_sngindex) - ExecTookTrigger (tp_name_sng.string, pk_sng); - else if (idx == tp_glindex) - ExecTookTrigger (tp_name_gl.string, pk_gl); - else if (idx == tp_rlindex) - ExecTookTrigger (tp_name_rl.string, pk_rl); - else if (idx == tp_lgindex) - ExecTookTrigger (tp_name_lg.string, pk_lg); - return; - } - - // armor - if (!strcmp(s, "items/armor1.wav")) - { - idx = FindNearestItem (1); - - switch (idx) { - case -1: ExecTookTrigger (tp_name_ga.string, pk_ga); break; - case -2: ExecTookTrigger (tp_name_ya.string, pk_ya); break; - case -3: ExecTookTrigger (tp_name_ra.string, pk_ra); break; - } - return; - } - - // backpack or ammo - if (!strcmp (s, "weapons/lock4.wav")) - { - idx = FindNearestItem (2); - if (idx == tp_packindex) - ExecTookTrigger (tp_name_backpack.string, pk_pack); - else if (idx == tp_shells1index || idx == tp_shells2index) - ExecTookTrigger (tp_name_shells.string, pk_shells); - else if (idx == tp_nails1index || idx == tp_nails2index) - ExecTookTrigger (tp_name_nails.string, pk_nails); - else if (idx == tp_rockets1index || idx == tp_rockets2index) - ExecTookTrigger (tp_name_rockets.string, pk_rockets); - else if (idx == tp_cells1index || idx == tp_cells2index) - ExecTookTrigger (tp_name_cells.string, pk_cells); - return; - } -} - - -#define IT_WEAPONS (2|4|8|16|32|64) -void TP_StatChanged (int stat, int value) -{ - int i; - - if (stat == STAT_HEALTH) - { - if (value > 0) { - if (vars.health <= 0 /*&& last_health != -999*/ - /* && Q_strcasecmp(Info_ValueForKey(cl.serverinfo, "status"), - "standby") */) // detect Kombat Teams status - { - extern cshift_t cshift_empty; - vars.respawntrigger_time = realtime; - //if (cl.teamfortress) - memset (&cshift_empty, 0, sizeof(cshift_empty)); - if (!cl.spectator) - TP_ExecTrigger ("f_respawn"); - } - vars.health = value; - return; - } - if (vars.health > 0) { // We just died - vars.deathtrigger_time = realtime; - strcpy (vars.lastdeathloc, Macro_Location_f()); - if (!cl.spectator) - TP_ExecTrigger ("f_death"); - } - vars.health = value; - } - else if (stat == STAT_ITEMS) - { - i = value &~ vars.items; - - if (i & (IT_KEY1|IT_KEY2)) { - if (cl.teamfortress && !cl.spectator) - ExecTookTrigger (tp_name_flag.string, pk_flag); - } - - vars.items = value; - } -} - - -void TP_Init () -{ - Cvar_RegisterVariable (&cl_parsesay); - Cvar_RegisterVariable (&cl_triggers); - Cvar_RegisterVariable (&cl_nofake); - Cvar_RegisterVariable (&cl_loadlocs); - Cvar_RegisterVariable (&cl_rocket2grenade); - Cvar_RegisterVariable (&cl_mapname); - Cvar_RegisterVariable (&cl_teamskin); - Cvar_RegisterVariable (&cl_enemyskin); - Cvar_RegisterVariable (&tp_name_axe); - Cvar_RegisterVariable (&tp_name_sg); - Cvar_RegisterVariable (&tp_name_ssg); - Cvar_RegisterVariable (&tp_name_ng); - Cvar_RegisterVariable (&tp_name_sng); - Cvar_RegisterVariable (&tp_name_gl); - Cvar_RegisterVariable (&tp_name_rl); - Cvar_RegisterVariable (&tp_name_lg); - Cvar_RegisterVariable (&tp_name_ra); - Cvar_RegisterVariable (&tp_name_ya); - Cvar_RegisterVariable (&tp_name_ga); - Cvar_RegisterVariable (&tp_name_quad); - Cvar_RegisterVariable (&tp_name_pent); - Cvar_RegisterVariable (&tp_name_ring); - Cvar_RegisterVariable (&tp_name_suit); - Cvar_RegisterVariable (&tp_name_shells); - Cvar_RegisterVariable (&tp_name_nails); - Cvar_RegisterVariable (&tp_name_rockets); - Cvar_RegisterVariable (&tp_name_cells); - Cvar_RegisterVariable (&tp_name_mh); - Cvar_RegisterVariable (&tp_name_health); - Cvar_RegisterVariable (&tp_name_backpack); - Cvar_RegisterVariable (&tp_name_flag); - - Cmd_AddCommand ("macrolist", TP_MacroList_f); - Cmd_AddCommand ("loadloc", TP_LoadLocFile_f); - Cmd_AddCommand ("msg_trigger", TP_MsgTrigger_f); - Cmd_AddCommand ("teamcolor", TP_TeamColor_f); - Cmd_AddCommand ("enemycolor", TP_EnemyColor_f); - Cmd_AddCommand ("tp_took", TP_TookTrigger_f); -} +/* + teamplay.c + + Teamplay enhancements ("proxy features") + + Copyright (C) 2000-2001 Anton Gavrilov + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA +*/ + +#include "quakedef.h" +#include "version.h" + +cvar_t cl_parsesay = {"cl_parsesay", "0"}; +cvar_t cl_triggers = {"cl_triggers", "0"}; +cvar_t cl_nofake = {"cl_nofake", "0"}; +cvar_t cl_loadlocs = {"cl_loadlocs", "0"}; +cvar_t cl_mapname = {"mapname", "", CVAR_ROM}; + +cvar_t cl_rocket2grenade = {"cl_r2g", "0"}; + +cvar_t cl_teamskin = {"teamskin", ""}; +cvar_t cl_enemyskin = {"enemyskin", ""}; + +cvar_t tp_name_axe = {"tp_name_axe", "axe"}; +cvar_t tp_name_sg = {"tp_name_sg", "sg"}; +cvar_t tp_name_ssg = {"tp_name_ssg", "ssg"}; +cvar_t tp_name_ng = {"tp_name_ng", "ng"}; +cvar_t tp_name_sng = {"tp_name_sng", "sng"}; +cvar_t tp_name_gl = {"tp_name_gl", "gl"}; +cvar_t tp_name_rl = {"tp_name_rl", "rl"}; +cvar_t tp_name_lg = {"tp_name_lg", "lg"}; +cvar_t tp_name_ra = {"tp_name_ra", "ra"}; +cvar_t tp_name_ya = {"tp_name_ya", "ya"}; +cvar_t tp_name_ga = {"tp_name_ga", "ga"}; +cvar_t tp_name_quad = {"tp_name_quad", "quad"}; +cvar_t tp_name_pent = {"tp_name_pent", "pent"}; +cvar_t tp_name_ring = {"tp_name_ring", "ring"}; +cvar_t tp_name_suit = {"tp_name_suit", "suit"}; +cvar_t tp_name_shells = {"tp_name_shells", "shells"}; +cvar_t tp_name_nails = {"tp_name_nails", "nails"}; +cvar_t tp_name_rockets = {"tp_name_rockets", "rockets"}; +cvar_t tp_name_cells = {"tp_name_cells", "cells"}; +cvar_t tp_name_mh = {"tp_name_mh", "mh"}; +cvar_t tp_name_health = {"tp_name_health", "health"}; +cvar_t tp_name_backpack = {"tp_name_backpack", "pack"}; +cvar_t tp_name_flag = {"tp_name_flag", "flag"}; + + +//=========================================================================== +// TRIGGERS +//=========================================================================== + +char *Macro_Location_f (void); +void TP_FindModelNumbers (void); + +#define MAX_LOC_NAME 32 + +// this structure is cleared after entering a new map +typedef struct tvars_s { + int health; + int items; + float respawntrigger_time; + float deathtrigger_time; + float f_version_reply_time; + char lastdeathloc[MAX_LOC_NAME]; + char tookitem[32]; + char last_tooktrigger[32]; +} tvars_t; + +tvars_t vars; + +void TP_ExecTrigger (char *s) +{ + if (!cl_triggers.value || cls.demoplayback) + return; + + if (Cmd_FindAlias(s)) + { + char *astr, *p; + qboolean quote = false; + + astr = Cmd_AliasString (s); + for (p=astr ; *p ; p++) + { + if (*p == '"') + quote = !quote; + if (!quote && *p == ';') + { + // more than one command, add it to the command buffer + Cbuf_AddText (astr); + Cbuf_AddText ("\n"); + return; + } + } + // a single line, so execute it right away + Cmd_ExecuteString (astr); + return; + } +} + + +/* +========================================================================== + MACRO FUNCTIONS +========================================================================== +*/ + +#define MAX_MACRO_VALUE 256 +static char macro_buf[MAX_MACRO_VALUE]; + + +char *Macro_Health_f (void) +{ + sprintf(macro_buf, "%i", cl.stats[STAT_HEALTH]); + return macro_buf; +} + +char *Macro_Armor_f (void) +{ + sprintf(macro_buf, "%i", cl.stats[STAT_ARMOR]); + return macro_buf; +} + +char *Macro_Shells_f (void) +{ + sprintf(macro_buf, "%i", cl.stats[STAT_SHELLS]); + return macro_buf; +} + +char *Macro_Nails_f (void) +{ + sprintf(macro_buf, "%i", cl.stats[STAT_NAILS]); + return macro_buf; +} + +char *Macro_Rockets_f (void) +{ + sprintf(macro_buf, "%i", cl.stats[STAT_ROCKETS]); + return macro_buf; +} + +char *Macro_Cells_f (void) +{ + sprintf(macro_buf, "%i", cl.stats[STAT_CELLS]); + return macro_buf; +} + +char *Macro_Ammo_f (void) +{ + sprintf(macro_buf, "%i", cl.stats[STAT_AMMO]); + return macro_buf; +} + +char *Macro_Weapon_f (void) +{ + switch (cl.stats[STAT_ACTIVEWEAPON]) + { + case IT_AXE: return "axe"; + case IT_SHOTGUN: return "sg"; + case IT_SUPER_SHOTGUN: return "ssg"; + case IT_NAILGUN: return "ng"; + case IT_SUPER_NAILGUN: return "sng"; + case IT_GRENADE_LAUNCHER: return "gl"; + case IT_ROCKET_LAUNCHER: return "rl"; + case IT_LIGHTNING: return "lg"; + default: + return ""; + } +} + +char *Macro_WeaponNum_f (void) +{ + switch (cl.stats[STAT_ACTIVEWEAPON]) + { + case IT_AXE: return "1"; + case IT_SHOTGUN: return "2"; + case IT_SUPER_SHOTGUN: return "3"; + case IT_NAILGUN: return "4"; + case IT_SUPER_NAILGUN: return "5"; + case IT_GRENADE_LAUNCHER: return "6"; + case IT_ROCKET_LAUNCHER: return "7"; + case IT_LIGHTNING: return "8"; + default: + return "0"; + } +} + +int _Macro_BestWeapon (void) +{ + int best; + + best = 0; + if (cl.stats[STAT_ITEMS] & IT_AXE) + best = IT_AXE; + if (cl.stats[STAT_ITEMS] & IT_SHOTGUN && cl.stats[STAT_SHELLS] >= 1) + best = IT_SHOTGUN; + if (cl.stats[STAT_ITEMS] & IT_SUPER_SHOTGUN && cl.stats[STAT_SHELLS] >= 2) + best = IT_SUPER_SHOTGUN; + if (cl.stats[STAT_ITEMS] & IT_NAILGUN && cl.stats[STAT_NAILS] >= 1) + best = IT_NAILGUN; + if (cl.stats[STAT_ITEMS] & IT_SUPER_NAILGUN && cl.stats[STAT_NAILS] >= 2) + best = IT_SUPER_NAILGUN; + if (cl.stats[STAT_ITEMS] & IT_GRENADE_LAUNCHER && cl.stats[STAT_ROCKETS] >= 1) + best = IT_GRENADE_LAUNCHER; + if (cl.stats[STAT_ITEMS] & IT_LIGHTNING && cl.stats[STAT_CELLS] >= 1) + best = IT_LIGHTNING; + if (cl.stats[STAT_ITEMS] & IT_ROCKET_LAUNCHER && cl.stats[STAT_ROCKETS] >= 1) + best = IT_ROCKET_LAUNCHER; + + return best; +} + +char *Macro_BestWeapon_f (void) +{ + switch (_Macro_BestWeapon()) + { + case IT_AXE: return "axe"; + case IT_SHOTGUN: return "sg"; + case IT_SUPER_SHOTGUN: return "ssg"; + case IT_NAILGUN: return "ng"; + case IT_SUPER_NAILGUN: return "sng"; + case IT_GRENADE_LAUNCHER: return "gl"; + case IT_ROCKET_LAUNCHER: return "rl"; + case IT_LIGHTNING: return "lg"; + default: + return ""; + } +} + +char *Macro_BestAmmo_f (void) +{ + switch (_Macro_BestWeapon()) + { + case IT_SHOTGUN: case IT_SUPER_SHOTGUN: + sprintf(macro_buf, "%i", cl.stats[STAT_SHELLS]); + return macro_buf; + + case IT_NAILGUN: case IT_SUPER_NAILGUN: + sprintf(macro_buf, "%i", cl.stats[STAT_NAILS]); + return macro_buf; + + case IT_GRENADE_LAUNCHER: case IT_ROCKET_LAUNCHER: + sprintf(macro_buf, "%i", cl.stats[STAT_ROCKETS]); + return macro_buf; + + case IT_LIGHTNING: + sprintf(macro_buf, "%i", cl.stats[STAT_CELLS]); + return macro_buf; + + default: + return "0"; + } +} + +// needed for %b parsing +char *Macro_BestWeaponAndAmmo_f (void) +{ + char buf[MAX_MACRO_VALUE]; + sprintf (buf, "%s:%s", Macro_BestWeapon_f(), Macro_BestAmmo_f()); + strcpy (macro_buf, buf); + return macro_buf; +} + +char *Macro_ArmorType_f (void) +{ + if (cl.stats[STAT_ITEMS] & IT_ARMOR1) + return "ga"; + else if (cl.stats[STAT_ITEMS] & IT_ARMOR2) + return "ya"; + else if (cl.stats[STAT_ITEMS] & IT_ARMOR3) + return "ra"; + else + return ""; // no armor at all +} + +char *Macro_Powerups_f (void) +{ + int effects; + + macro_buf[0] = 0; + + if (cl.stats[STAT_ITEMS] & IT_QUAD) + strcpy(macro_buf, "quad"); + + if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY) { + if (macro_buf[0]) + strcat(macro_buf, "/"); + strcat(macro_buf, "pent"); + } + + if (cl.stats[STAT_ITEMS] & IT_INVISIBILITY) { + if (macro_buf[0]) + strcat(macro_buf, "/"); + strcat(macro_buf, "ring"); + } + + effects = cl.frames[cl.parsecount&UPDATE_MASK].playerstate[cl.playernum].effects; + if ( (effects & (EF_FLAG1|EF_FLAG2)) || // CTF + (cl.teamfortress && cl.stats[STAT_ITEMS] & (IT_KEY1|IT_KEY2) // TF + /*&& (effects & EF_DIMLIGHT))*/) ) + { + if (macro_buf[0]) + strcat(macro_buf, "/"); + strcat(macro_buf, "flag"); + } + + return macro_buf; +} + +char *Macro_Location2_f (void) +{ + if (vars.deathtrigger_time && realtime - vars.deathtrigger_time <= 5) + return vars.lastdeathloc; + return Macro_Location_f(); +} + +char *Macro_LastDeath_f (void) +{ + if (vars.deathtrigger_time) + return vars.lastdeathloc; + else + return "someplace"; +} + +char *Macro_Time_f (void) +{ + time_t t; + struct tm *ptm; + + macro_buf[0] = 0; + time(&t); + ptm = localtime(&t); + strftime(macro_buf, sizeof(macro_buf)-1, "%H:%M", ptm); + return macro_buf; +} + +char *Macro_Date_f (void) +{ + time_t t; + struct tm *ptm; + + macro_buf[0] = 0; + time(&t); + ptm = localtime(&t); + strftime(macro_buf, sizeof(macro_buf)-1, "%d.%m.%y", ptm); + return macro_buf; +} + +// returns the last item picked up +char *Macro_Item_f (void) +{ + strcpy (macro_buf, vars.tookitem); + return macro_buf; +} + +// returns the last item that triggered f_took +char *Macro_Took_f (void) +{ + strcpy (macro_buf, vars.last_tooktrigger); + return macro_buf; +} + +typedef struct +{ + char *name; + char *(*func) (void); +} macro_command_t; + +// Note: longer macro names like "armortype" must be defined +// _before_ the shorter ones like "armor" to be parsed properly +macro_command_t macro_commands[] = +{ + {"health", Macro_Health_f}, + {"armortype", Macro_ArmorType_f}, + {"armor", Macro_Armor_f}, + {"shells", Macro_Shells_f}, + {"nails", Macro_Nails_f}, + {"rockets", Macro_Rockets_f}, + {"cells", Macro_Cells_f}, + {"weaponnum", Macro_WeaponNum_f}, + {"weapon", Macro_Weapon_f}, + {"ammo", Macro_Ammo_f}, + {"bestweapon", Macro_BestWeapon_f}, + {"bestammo", Macro_BestAmmo_f}, + {"powerups", Macro_Powerups_f}, + {"location", Macro_Location_f}, + {"time", Macro_Time_f}, + {"date", Macro_Date_f}, + {"item", Macro_Item_f}, + {"took", Macro_Took_f}, + {NULL, NULL} +}; + +#define MAX_MACRO_STRING 1024 + +/* +============== +TP_MacroString + +returns NULL if no matching macro was found +============== +*/ +int macro_length; // length of macro name + +char *TP_MacroString (char *s) +{ + static char buf[MAX_MACRO_STRING]; + macro_command_t *macro; + + macro = macro_commands; + while (macro->name) { + if (!Q_strncasecmp(s, macro->name, strlen(macro->name))) + { + macro_length = strlen(macro->name); + return macro->func(); + } + macro++; + } + + macro_length = 0; + return NULL; +} + +/* +============= +TP_ParseChatString + +Parses %a-like expressions +============= +*/ +char *TP_ParseMacroString (char *string) +{ + static char buf[MAX_MACRO_STRING]; + char *s; + int i; + char *macro_string; + char ch; + + if (!cl_parsesay.value) + return string; + + s = string; + i = 0; + + while (*s && i < MAX_MACRO_STRING-1) + { + // check %[P], etc + if (*s == '%' && s[1]=='[' && s[2] && s[3]==']') + { + static char mbuf[MAX_MACRO_VALUE]; + switch (s[2]) { + case 'a': + macro_string = Macro_ArmorType_f(); + if (!macro_string[0]) + macro_string = "a"; + if (cl.stats[STAT_ARMOR] < 30) + sprintf (mbuf, "\x10%s:%i\x11", macro_string, cl.stats[STAT_ARMOR]); + else + sprintf (mbuf, "%s:%i", macro_string, cl.stats[STAT_ARMOR]); + macro_string = mbuf; + break; + + case 'h': + if (cl.stats[STAT_HEALTH] >= 50) + sprintf (macro_buf, "%i", cl.stats[STAT_HEALTH]); + else + sprintf (macro_buf, "\x10%i\x11", cl.stats[STAT_HEALTH]); + macro_string = macro_buf; + break; + + case 'P': + macro_string = Macro_Powerups_f(); + if (macro_string[0]) + sprintf (mbuf, "\x10%s\x11", macro_string); + else + mbuf[0] = 0; + macro_string = mbuf; + break; + + // todo: %[w], %[b] + + default: + buf[i++] = *s++; + continue; + } + if (i + strlen(macro_string) >= MAX_MACRO_STRING-1) + Sys_Error("TP_ParseMacroString: macro string length > MAX_MACRO_STRING)"); + strcpy (&buf[i], macro_string); + i += strlen(macro_string); + s += 4; // skip %[] + continue; + } + + // check %a, etc + if (*s == '%') + { + switch (s[1]) + { + case 'a': macro_string = Macro_Armor_f(); break; + case 'A': macro_string = Macro_ArmorType_f(); break; + case 'b': macro_string = Macro_BestWeaponAndAmmo_f(); break; + case 'c': macro_string = Macro_Cells_f(); break; + case 'd': macro_string = Macro_LastDeath_f(); break; + case 'h': macro_string = Macro_Health_f(); break; + case 'i': macro_string = vars.tookitem; break; + case 'I': macro_string = vars.last_tooktrigger; break; + case 'l': macro_string = Macro_Location_f(); break; + case 'L': macro_string = Macro_Location2_f(); break; + case 'P': + case 'p': macro_string = Macro_Powerups_f(); break; + case 'r': macro_string = Macro_Rockets_f(); break; + case 'w': macro_string = Macro_Weapon_f(); break; + case 'W': macro_string = Macro_Ammo_f(); break; + default: + buf[i++] = *s++; + continue; + } + if (i + strlen(macro_string) >= MAX_MACRO_STRING-1) + Sys_Error("TP_ParseMacroString: macro string length > MAX_MACRO_STRING)"); + strcpy (&buf[i], macro_string); + i += strlen(macro_string); + s += 2; // skip % and letter + continue; + } + + // "fun chars" + if (*s == '$') + { + ch = 0; + switch (s[1]) + { + case '\\': ch = 0x0D; break; + case ':': ch = 0x0A; break; + case '[': ch = 0x10; break; + case ']': ch = 0x11; break; + case 'G': ch = 0x86; break; + case 'R': ch = 0x87; break; + case 'Y': ch = 0x88; break; + case 'B': ch = 0x89; break; + } + + if (ch) { + buf[i++] = ch; + s += 2; + continue; + } + } + + buf[i++] = *s++; + } + buf[i] = 0; + + return buf; +} + +/* +============== +TP_MacroList +============== +*/ +void TP_MacroList_f (void) +{ + macro_command_t *macro; + int i; + + for (macro=macro_commands,i=0 ; macro->name ; macro++,i++) + Con_Printf ("%s\n", macro->name); + + Con_Printf ("------------\n%d macros\n", i); +} + +/* +============================================================================= + + PROXY .LOC FILES + +============================================================================= +*/ + +typedef struct locdata_s { + vec3_t coord; + char name[MAX_LOC_NAME]; +} locdata_t; + +#define MAX_LOC_ENTRIES 1024 + +locdata_t locdata[MAX_LOC_ENTRIES]; // FIXME: allocate dynamically? +int loc_numentries; + +#define SKIPBLANKS(ptr) while (*ptr == ' ' || *ptr == 9 || *ptr == 13) ptr++ +#define SKIPTOEOL(ptr) while (*ptr != 10 && *ptr == 0) ptr++ + +void TP_LoadLocFile (char *path, qboolean quiet) +{ + char *buf, *p; + int i, n, sign; + int line; + int nameindex; + int mark; + char locname[MAX_OSPATH]; + + if (!*path) + return; + + strcpy (locname, "locs/"); + if (strlen(path) + strlen(locname) + 2+4 > MAX_OSPATH) + { + Con_Printf ("TP_LoadLocFile: path name > MAX_OSPATH\n"); + return; + } + strcat (locname, path); + if (!strstr(locname, ".")) + strcat (locname, ".loc"); // Add default extension + + mark = Hunk_LowMark (); + buf = (char *) COM_LoadHunkFile (locname); + + if (!buf) + { + if (!quiet) + Con_Printf ("Could not load %s\n", locname); + return; + } + +// Parse the whole file now + + loc_numentries = 0; + + p = buf; + line = 1; + + while (1) + { +// while (*buf == ' ' || *buf == 9) +// buf++; + SKIPBLANKS(p); + + if (*p == 0) + goto _endoffile; + + if (*p == 10 || (*p == '/' && p[1] == '/')) + { + p++; + goto _endofline; + } + + for (i = 0; i < 3; i++) + { + n = 0; + sign = 1; + while (1) + { + switch (*p++) + { + case ' ': case 9: + goto _next; + + case '-': + if (n) + { + Con_Printf ("Error in loc file on line #%i\n", line); + SKIPTOEOL(p); + goto _endofline; + } + sign = -1; + break; + + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + n = n*10 + (p[-1] - '0'); + break; + + default: // including eol or eof + Con_Printf ("Error in loc file on line #%i\n", line); + SKIPTOEOL(p); + goto _endofline; + } + } +_next: + n *= sign; + locdata[loc_numentries].coord[i] = n; + + SKIPBLANKS(p); + } + + +// Save the location's name +// + nameindex = 0; + + while (1) + { + switch (*p) + { + case 13: + p++; + break; + + case 10: case 0: + locdata[loc_numentries].name[nameindex] = 0; + loc_numentries++; + + if (loc_numentries >= MAX_LOC_ENTRIES) + goto _endoffile; + + // leave the 0 or 10 in buffer, so it is parsed properly + goto _endofline; + + default: + if (nameindex < MAX_LOC_NAME-1) + locdata[loc_numentries].name[nameindex++] = *p; + p++; + } + } +_endofline: + line++; + } +_endoffile: + + Hunk_FreeToLowMark (mark); + + if (quiet) + Con_Printf ("Loaded %s\n", locname); + else + Con_Printf ("Loaded %s (%i locations)\n", locname, loc_numentries); +} + +void TP_LoadLocFile_f (void) +{ + if (Cmd_Argc() != 2) + { + Con_Printf ("loadloc : load a loc file\n"); + return; + } + + TP_LoadLocFile (Cmd_Argv(1), false); +} + +extern int parsecountmod; +char *Macro_Location_f (void) +{ + int i; + int min_num; + vec_t min_dist; + vec3_t vec; + vec3_t org; + + if (!loc_numentries || (cls.state != ca_active)) + return "someplace"; + + VectorCopy (cl.frames[parsecountmod].playerstate[cl.playernum].origin, org); + for (i = 0; i < 3; i++) + org[i] *= 8; + + min_num = 0; + min_dist = 9999999; + + for (i = 0; i < loc_numentries; i++) + { +// Con_DPrintf ("%f %f %f: %s\n", locdata[i].coord[0], locdata[i].coord[1], locdata[i].coord[2], locdata[i].name); + VectorSubtract (org, locdata[i].coord, vec); + if (Length(vec) < min_dist) + { + min_num = i; + min_dist = Length(vec); + } + } + + return locdata[min_num].name; +} + +/* +============================================================================= + + MESSAGE TRIGGERS + +============================================================================= +*/ + +typedef struct msg_trigger_s { + char name[32]; + char string[64]; + int level; + struct msg_trigger_s *next; +} msg_trigger_t; + +static msg_trigger_t *msg_triggers; + +msg_trigger_t *TP_FindTrigger (char *name) +{ + msg_trigger_t *t; + + for (t=msg_triggers; t; t=t->next) + if (!strcmp(t->name, name)) + return t; + + return NULL; +} + + +void TP_MsgTrigger_f (void) +{ + int c; + char *name; + msg_trigger_t *trig; + + c = Cmd_Argc(); + + if (c > 5) { + Con_Printf ("msg_trigger \"string\" [-l ]\n"); + return; + } + + if (c == 1) { + if (!msg_triggers) + Con_Printf ("no triggers defined\n"); + else + for (trig=msg_triggers; trig; trig=trig->next) + Con_Printf ("%s : \"%s\"\n", trig->name, trig->string); + return; + } + + name = Cmd_Argv(1); + if (strlen(name) > 31) { + Con_Printf ("trigger name too long\n"); + return; + } + + if (c == 2) { + trig = TP_FindTrigger (name); + if (trig) + Con_Printf ("%s: \"%s\"\n", trig->name, trig->string); + else + Con_Printf ("trigger \"%s\" not found\n", name); + return; + } + + if (c >= 3) { + if (strlen(Cmd_Argv(2)) > 63) { + Con_Printf ("trigger string too long\n"); + return; + } + + trig = TP_FindTrigger (name); + + if (!trig) { + // allocate new trigger + trig = Z_Malloc (sizeof(msg_trigger_t)); + trig->next = msg_triggers; + msg_triggers = trig; + strcpy (trig->name, name); + trig->level = PRINT_HIGH; + } + + strcpy (trig->string, Cmd_Argv(2)); + if (c == 5 && !Q_strcasecmp (Cmd_Argv(3), "-l")) { + trig->level = Q_atoi (Cmd_Argv(4)); + if ((unsigned)trig->level > PRINT_CHAT) + trig->level = PRINT_HIGH; + } + } +} + +char *trigger_commands[] = { + "play", + "playvol", + "stopsound", + "set", + "echo", + "say", + "say_team", + "alias", + "unalias", + "msg_trigger", + "inc", + "bind", + "unbind", + "record", + "easyrecord", + "stop" + "if" +}; + +#define NUM_TRIGGER_COMMANDS (sizeof(trigger_commands)/sizeof(trigger_commands[0])) + +void TP_ExecuteTriggerString (char *text) +{ + static char buf[1024]; + char *arg0; + int i; + cmd_function_t *cmd; + + Cmd_ExpandString (text, buf); + Cmd_TokenizeString (buf); + + if (!Cmd_Argc()) + return; // no tokens + +// check cvars + if (Cvar_Command()) + return; + +// check commands + arg0 = Cmd_Argv(0); + + for (i=0; i < NUM_TRIGGER_COMMANDS ; i++) + if (!Q_strcasecmp(arg0, trigger_commands[i])) + { + cmd = Cmd_FindCommand (arg0); + if (cmd) { + if (!cmd->function) + Cmd_ForwardToServer (); + else + cmd->function (); + return; + } + } + + if (cl_warncmd.value || developer.value) + Con_Printf ("Invalid trigger command: \"%s\"\n", arg0); +} + + +void TP_ExecuteTriggerBuf (char *text) +{ + char line[1024]; + int i, quotes; + + while (*text) + { + quotes = 0; + for (i=0 ; text[i] ; i++) + { + if (text[i] == '"') + quotes++; + if ( !(quotes&1) && text[i] == ';' ) + break; // don't break if inside a quoted string + if (text[i] == '\n') + break; + } + memcpy (line, text, i); + line[i] = 0; + TP_ExecuteTriggerString (line); + if (!text[i]) + break; + text += i + 1; + } +} + +void TP_SearchForMsgTriggers (char *s, int level) +{ + msg_trigger_t *t; + char *string; + + if (cls.demoplayback) + return; + + for (t=msg_triggers; t; t=t->next) + if (t->level == level && t->string[0] && strstr(s, t->string)) + { + if (level == PRINT_CHAT && ( + strstr (s, "f_version") || strstr (s, "f_system") || + strstr (s, "f_speed") || strstr (s, "f_modified"))) + continue; // don't let llamas fake proxy replies + + string = Cmd_AliasString (t->name); + if (string) + TP_ExecuteTriggerBuf (string); + else + Con_Printf ("trigger \"%s\" has no matching alias\n", t->name); + } +} + + +void TP_CheckVersionRequest (char *s) +{ + char buf[11]; + int i; + + if (cl.spectator) + return; + + if (vars.f_version_reply_time + && realtime - vars.f_version_reply_time < 20) + return; // don't reply again if 20 seconds haven't passed + + while (1) + { + switch (*s++) + { + case 0: + case '\n': + return; + case ':': + case (char)':'|128: + goto ok; + } + } + return; + +ok: + for (i = 0; i < 11 && s[i]; i++) + buf[i] = s[i] &~ 128; // strip high bit + + if (!strncmp(buf, " f_version\n", 11) || !strncmp(buf, " z_version\n", 11)) + { +#ifdef RELEASE_VERSION + Cbuf_AddText (va("say QWExtended Client version %s " + QW_PLATFORM ":" QW_RENDERER "\n", QWE_VERSION)); +#else + Cbuf_AddText (va("say QWExtended Client version %s (Build %04d) " + QW_PLATFORM ":" QW_RENDERER "\n", QWE_VERSION, build_number())); +#endif + vars.f_version_reply_time = realtime; + } +} + + +int TP_CountPlayers () +{ + int i, count; + + count = 0; + for (i = 0; i < MAX_CLIENTS ; i++) { + if (cl.players[i].name[0] && !cl.players[i].spectator) + count++; + } + + return count; +} + +char *TP_EnemyTeam () +{ + int i; + char myteam[MAX_INFO_STRING]; + static char enemyteam[MAX_INFO_STRING]; + + strcpy (myteam, Info_ValueForKey(cls.userinfo, "team")); + + for (i = 0; i < MAX_CLIENTS ; i++) { + if (cl.players[i].name[0] && !cl.players[i].spectator) + { + strcpy (enemyteam, Info_ValueForKey(cl.players[i].userinfo, "team")); + if (strcmp(myteam, enemyteam) != 0) + return enemyteam; + } + } + return ""; +} + +char *TP_PlayerName () +{ + static char myname[MAX_INFO_STRING]; + + strcpy (myname, Info_ValueForKey(cl.players[cl.playernum].userinfo, "name")); + return myname; +} + +char *TP_PlayerTeam () +{ + static char myteam[MAX_INFO_STRING]; + + strcpy (myteam, Info_ValueForKey(cl.players[cl.playernum].userinfo, "team")); + return myteam; +} + +char *TP_EnemyName () +{ + int i; + char *myname; + static char enemyname[MAX_INFO_STRING]; + + myname = TP_PlayerName (); + + for (i = 0; i < MAX_CLIENTS ; i++) { + if (cl.players[i].name[0] && !cl.players[i].spectator) + { + strcpy (enemyname, Info_ValueForKey(cl.players[i].userinfo, "name")); + if (strcmp(enemyname, myname) != 0) + return enemyname; + } + } + return ""; +} + +char *TP_MapName () +{ + return cl_mapname.string; +} + +/* +============================================================================= + TEAMCOLOR & ENEMYCOLOR +============================================================================= +*/ + +int cl_teamtopcolor = -1; +int cl_teambottomcolor; +int cl_enemytopcolor = -1; +int cl_enemybottomcolor; + +void TP_TeamColor_f (void) +{ + int top, bottom; + int i; + + if (Cmd_Argc() == 1) + { + if (cl_teamtopcolor < 0) + Con_Printf ("\"teamcolor\" is \"off\"\n"); + else + Con_Printf ("\"teamcolor\" is \"%i %i\"\n", + cl_teamtopcolor, + cl_teambottomcolor); + return; + } + + if (!strcmp(Cmd_Argv(1), "off")) + { + cl_teamtopcolor = -1; + for (i = 0; i < MAX_CLIENTS; i++) + CL_NewTranslation(i); + return; + } + + if (Cmd_Argc() == 2) + top = bottom = atoi(Cmd_Argv(1)); + else { + top = atoi(Cmd_Argv(1)); + bottom = atoi(Cmd_Argv(2)); + } + + top &= 15; + if (top > 13) + top = 13; + bottom &= 15; + if (bottom > 13) + bottom = 13; + +// if (top != cl_teamtopcolor || bottom != cl_teambottomcolor) + { + cl_teamtopcolor = top; + cl_teambottomcolor = bottom; + + for (i = 0; i < MAX_CLIENTS; i++) + CL_NewTranslation(i); + } +} + +void TP_EnemyColor_f (void) +{ + int top, bottom; + int i; + + if (Cmd_Argc() == 1) + { + if (cl_enemytopcolor < 0) + Con_Printf ("\"enemycolor\" is \"off\"\n"); + else + Con_Printf ("\"enemycolor\" is \"%i %i\"\n", + cl_enemytopcolor, + cl_enemybottomcolor); + return; + } + + if (!strcmp(Cmd_Argv(1), "off")) + { + cl_enemytopcolor = -1; + for (i = 0; i < MAX_CLIENTS; i++) + CL_NewTranslation(i); + return; + } + + if (Cmd_Argc() == 2) + top = bottom = atoi(Cmd_Argv(1)); + else { + top = atoi(Cmd_Argv(1)); + bottom = atoi(Cmd_Argv(2)); + } + + top &= 15; + if (top > 13) + top = 13; + bottom &= 15; + if (bottom > 13) + bottom = 13; + +// if (top != cl_enemytopcolor || bottom != cl_enemybottomcolor) + { + cl_enemytopcolor = top; + cl_enemybottomcolor = bottom; + + for (i = 0; i < MAX_CLIENTS; i++) + CL_NewTranslation(i); + } +} + +//=================================================================== + +void TP_FixTeamSets(void) +{ + int i; + + for (i = 0; i < MAX_CLIENTS; i++) { + Skin_Find(&cl.players[i]); + CL_NewTranslation(i); + } +} + +void TP_NewMap () +{ + static char last_map[MAX_QPATH] = {'\0'}; + char mapname[MAX_QPATH]; + + memset (&vars, 0, sizeof(vars)); + TP_FindModelNumbers (); + + COM_StripExtension (COM_SkipPath (cl.worldmodel->name), mapname); + if (strcmp(mapname, last_map)) + { // map name has changed + loc_numentries = 0; // clear loc file + if (cl_loadlocs.value && !cls.demoplayback ) { + char locname[MAX_OSPATH]; + _snprintf (locname, MAX_OSPATH, "%s.loc", mapname); + TP_LoadLocFile (locname, true); + } + strcpy (last_map, mapname); + Cvar_SetROM (&cl_mapname, mapname); + } + + TP_ExecTrigger ("f_newmap"); +} + +/* +====================== +TP_CategorizeMessage + +returns a combination of these values: +0 -- unknown (probably generated by the server) +1 -- normal +2 -- team message +4 -- spectator +Note that sometimes we can't be sure who really sent the message, +e.g. when there's a player "unnamed" in your team and "(unnamed)" +in the enemy team. The result will be 3 (1+2) + +Never returns 2 if we are a spectator. +====================== +*/ +int TP_CategorizeMessage (char *s) +{ + int i, msglen, len; + int flags; + player_info_t *player; + + flags = 0; + msglen = strlen(s); + if (!msglen) + return 0; + + for (i=0, player=cl.players ; i < MAX_CLIENTS ; i++, player++) + { + len = strlen(player->name); + if (!len) + continue; + // check messagemode1 + if (len+2 <= msglen && s[len] == ':' && s[len+1] == ' ' && + !strncmp(player->name, s, len)) + { + if (player->spectator) + flags |= 4; + else + flags |= 1; + } + // check messagemode2 + else if (s[0] == '(' && !cl.spectator && len+4 <= msglen && + !strncmp(s+len+1, "): ", 3) && + !strncmp(player->name, s+1, len)) + { + // no team messages in teamplay 0, except for our own + if (i == cl.playernum || ( atoi(Info_ValueForKey(cl.serverinfo, "teamplay")) + && !strcmp(cl.players[cl.playernum].team, player->team)) ) + flags |= 2; + } + } + + return flags; +} + +//=================================================================== +// Pickup triggers +// + +// symbolic names used in tp_took command +char *pknames[] = {"quad", "pent", "ring", "suit", "ra", "ya", "ga", +"mh", "health", "lg", "rl", "gl", "sng", "ng", "ssg", "pack", +"cells", "rockets", "nails", "shells", "flag"}; + +enum {pk_quad, pk_pent, pk_ring, pk_suit, pk_ra, pk_ya, pk_ga, +pk_mh, pk_health, pk_lg, pk_rl, pk_gl, pk_sng, pk_ng, pk_ssg, pk_pack, +pk_cells, pk_rockets, pk_nails, pk_shells, pk_flag, MAX_PKFLAGS}; + +#define default_pkflags ((1<packet_entities; + bestdist = 250; + bestidx = 0; + for (i=0,ent=pak->entities ; inum_entities ; i++,ent++) + { + int j, dist; + + j = ent->modelindex; + switch (type) + { + case 0: // weapon + if (j != tp_ssgindex && j != tp_ngindex && j != tp_sngindex + && j != tp_glindex && j != tp_rlindex && j != tp_lgindex) + continue; + break; + case 1: // armor + if (j != tp_armorindex) + continue; + break; + case 2: // ammo or backpack + if (j != tp_packindex && j != tp_shells1index && j != tp_shells2index + && j != tp_nails1index && j != tp_nails2index + && j != tp_rockets1index && j != tp_rockets2index + && j != tp_cells1index && j != tp_cells2index) + continue; + } + VectorSubtract (ent->origin, org, v); + if ((dist = Length(v)) > bestdist) + continue; + bestdist = dist; + bestidx = j; + bestskin = ent->skinnum; + } + + if (type == 1 && bestidx) // armor + return -(bestskin + 1); // -1=green, -2=yellow, -3=red + + return bestidx; +} + +static int CountTeammates () +{ + int i, count; + player_info_t *player; + char *myteam; + + count = 0; + myteam = cl.players[cl.playernum].team; + for (i=0, player=cl.players; i < MAX_CLIENTS ; i++, player++) { + if (player->name[0] && !player->spectator && (i != cl.playernum) + && !strcmp(player->team, myteam)) + count++; + } + + return count; +} + +static void ExecTookTrigger (char *s, int flag) +{ + strcpy (vars.tookitem, s); + if (pkflags & (1< 0) + { + strcpy (vars.last_tooktrigger, s); + TP_ExecTrigger ("f_took"); + } + } +} + +void TP_CheckPickupSound (char *s) +{ + int idx; + + if (cl.spectator || !atoi(Info_ValueForKey(cl.serverinfo, "teamplay"))) + return; + + if (!strcmp(s, "items/damage.wav")) + ExecTookTrigger (tp_name_quad.string, pk_quad); + else if (!strcmp(s, "items/protect.wav")) + ExecTookTrigger (tp_name_pent.string, pk_pent); + else if (!strcmp(s, "items/inv1.wav")) + ExecTookTrigger (tp_name_ring.string, pk_ring); + else if (!strcmp(s, "items/suit.wav")) + ExecTookTrigger (tp_name_suit.string, pk_suit); + else if (!strcmp(s, "items/health1.wav") || + !strcmp(s, "items/r_item1.wav")) + ExecTookTrigger (tp_name_health.string, pk_health); + else if (!strcmp(s, "items/r_item2.wav")) + ExecTookTrigger (tp_name_mh.string, pk_mh); + else + goto more; + return; + +more: + if (!cl.validsequence) + return; + + // weapons + if (!strcmp(s, "weapons/pkup.wav")) + { + int deathmatch; + + deathmatch = atoi(Info_ValueForKey(cl.serverinfo, "deathmatch")); + if (deathmatch == 2 || deathmatch == 3) + return; + idx = FindNearestItem(0); + if (idx == tp_ssgindex) + ExecTookTrigger (tp_name_ssg.string, pk_ssg); + else if (idx == tp_ngindex) + ExecTookTrigger (tp_name_ng.string, pk_ng); + else if (idx == tp_sngindex) + ExecTookTrigger (tp_name_sng.string, pk_sng); + else if (idx == tp_glindex) + ExecTookTrigger (tp_name_gl.string, pk_gl); + else if (idx == tp_rlindex) + ExecTookTrigger (tp_name_rl.string, pk_rl); + else if (idx == tp_lgindex) + ExecTookTrigger (tp_name_lg.string, pk_lg); + return; + } + + // armor + if (!strcmp(s, "items/armor1.wav")) + { + idx = FindNearestItem (1); + + switch (idx) { + case -1: ExecTookTrigger (tp_name_ga.string, pk_ga); break; + case -2: ExecTookTrigger (tp_name_ya.string, pk_ya); break; + case -3: ExecTookTrigger (tp_name_ra.string, pk_ra); break; + } + return; + } + + // backpack or ammo + if (!strcmp (s, "weapons/lock4.wav")) + { + idx = FindNearestItem (2); + if (idx == tp_packindex) + ExecTookTrigger (tp_name_backpack.string, pk_pack); + else if (idx == tp_shells1index || idx == tp_shells2index) + ExecTookTrigger (tp_name_shells.string, pk_shells); + else if (idx == tp_nails1index || idx == tp_nails2index) + ExecTookTrigger (tp_name_nails.string, pk_nails); + else if (idx == tp_rockets1index || idx == tp_rockets2index) + ExecTookTrigger (tp_name_rockets.string, pk_rockets); + else if (idx == tp_cells1index || idx == tp_cells2index) + ExecTookTrigger (tp_name_cells.string, pk_cells); + return; + } +} + + +#define IT_WEAPONS (2|4|8|16|32|64) +void TP_StatChanged (int stat, int value) +{ + int i; + + if (stat == STAT_HEALTH) + { + if (value > 0) { + if (vars.health <= 0 /*&& last_health != -999*/ + /* && Q_strcasecmp(Info_ValueForKey(cl.serverinfo, "status"), + "standby") */) // detect Kombat Teams status + { + extern cshift_t cshift_empty; + vars.respawntrigger_time = realtime; + //if (cl.teamfortress) + memset (&cshift_empty, 0, sizeof(cshift_empty)); + if (!cl.spectator) + TP_ExecTrigger ("f_respawn"); + } + vars.health = value; + return; + } + if (vars.health > 0) { // We just died + vars.deathtrigger_time = realtime; + strcpy (vars.lastdeathloc, Macro_Location_f()); + if (!cl.spectator) + TP_ExecTrigger ("f_death"); + } + vars.health = value; + } + else if (stat == STAT_ITEMS) + { + i = value &~ vars.items; + + if (i & (IT_KEY1|IT_KEY2)) { + if (cl.teamfortress && !cl.spectator) + ExecTookTrigger (tp_name_flag.string, pk_flag); + } + + vars.items = value; + } +} + + +void TP_Init () +{ + Cvar_RegisterVariable (&cl_parsesay); + Cvar_RegisterVariable (&cl_triggers); + Cvar_RegisterVariable (&cl_nofake); + Cvar_RegisterVariable (&cl_loadlocs); + Cvar_RegisterVariable (&cl_rocket2grenade); + Cvar_RegisterVariable (&cl_mapname); + Cvar_RegisterVariable (&cl_teamskin); + Cvar_RegisterVariable (&cl_enemyskin); + Cvar_RegisterVariable (&tp_name_axe); + Cvar_RegisterVariable (&tp_name_sg); + Cvar_RegisterVariable (&tp_name_ssg); + Cvar_RegisterVariable (&tp_name_ng); + Cvar_RegisterVariable (&tp_name_sng); + Cvar_RegisterVariable (&tp_name_gl); + Cvar_RegisterVariable (&tp_name_rl); + Cvar_RegisterVariable (&tp_name_lg); + Cvar_RegisterVariable (&tp_name_ra); + Cvar_RegisterVariable (&tp_name_ya); + Cvar_RegisterVariable (&tp_name_ga); + Cvar_RegisterVariable (&tp_name_quad); + Cvar_RegisterVariable (&tp_name_pent); + Cvar_RegisterVariable (&tp_name_ring); + Cvar_RegisterVariable (&tp_name_suit); + Cvar_RegisterVariable (&tp_name_shells); + Cvar_RegisterVariable (&tp_name_nails); + Cvar_RegisterVariable (&tp_name_rockets); + Cvar_RegisterVariable (&tp_name_cells); + Cvar_RegisterVariable (&tp_name_mh); + Cvar_RegisterVariable (&tp_name_health); + Cvar_RegisterVariable (&tp_name_backpack); + Cvar_RegisterVariable (&tp_name_flag); + + Cmd_AddCommand ("macrolist", TP_MacroList_f); + Cmd_AddCommand ("loadloc", TP_LoadLocFile_f); + Cmd_AddCommand ("msg_trigger", TP_MsgTrigger_f); + Cmd_AddCommand ("teamcolor", TP_TeamColor_f); + Cmd_AddCommand ("enemycolor", TP_EnemyColor_f); + Cmd_AddCommand ("tp_took", TP_TookTrigger_f); +} diff --git a/source/teamplay.h b/source/teamplay.h index 4f84a413..d327babd 100644 --- a/source/teamplay.h +++ b/source/teamplay.h @@ -1,69 +1,69 @@ -/* - teamplay.c - - Teamplay enhancements ("proxy features") - - Copyright (C) 2000-2001 Anton Gavrilov - - 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: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA -*/ - -extern cvar_t cl_parsesay; -extern cvar_t cl_triggers; -extern cvar_t cl_nofake; -extern cvar_t cl_loadlocs; -extern cvar_t cl_rocket2grenade; -extern cvar_t cl_teamskin; -extern cvar_t cl_enemyskin; - -// triggers -void TP_ExecTrigger (char *s); -void TP_StatChanged (int stat, int value); -void TP_CheckPickupSound (char *s); - -// message triggers -void TP_SearchForMsgTriggers (char *s, int level); - -// used by easyrecord command -int TP_CountPlayers(); -char *TP_MapName(); -char *TP_PlayerName(); -char *TP_PlayerTeam(); -char *TP_EnemyName(); -char *TP_EnemyTeam(); - -// teamcolor&enemycolor -extern int cl_teamtopcolor; -extern int cl_teambottomcolor; -extern int cl_enemytopcolor; -extern int cl_enemybottomcolor; - -void TP_LoadLocFile (char *path, qboolean quiet); -char *TP_ParseMacroString(char *string); -void TP_NewMap (); -void TP_CheckVersionRequest(char *s); -int TP_CategorizeMessage (char *s); -void TP_FixTeamSets(); - -void TP_Init (); - - -//#define FPD_NO_TEAM_MACROS 1 -#define FPD_NO_FORCE_SKIN 256 -#define FPD_NO_FORCE_COLOR 512 +/* + teamplay.c + + Teamplay enhancements ("proxy features") + + Copyright (C) 2000-2001 Anton Gavrilov + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA +*/ + +extern cvar_t cl_parsesay; +extern cvar_t cl_triggers; +extern cvar_t cl_nofake; +extern cvar_t cl_loadlocs; +extern cvar_t cl_rocket2grenade; +extern cvar_t cl_teamskin; +extern cvar_t cl_enemyskin; + +// triggers +void TP_ExecTrigger (char *s); +void TP_StatChanged (int stat, int value); +void TP_CheckPickupSound (char *s); + +// message triggers +void TP_SearchForMsgTriggers (char *s, int level); + +// used by easyrecord command +int TP_CountPlayers(); +char *TP_MapName(); +char *TP_PlayerName(); +char *TP_PlayerTeam(); +char *TP_EnemyName(); +char *TP_EnemyTeam(); + +// teamcolor&enemycolor +extern int cl_teamtopcolor; +extern int cl_teambottomcolor; +extern int cl_enemytopcolor; +extern int cl_enemybottomcolor; + +void TP_LoadLocFile (char *path, qboolean quiet); +char *TP_ParseMacroString(char *string); +void TP_NewMap (); +void TP_CheckVersionRequest(char *s); +int TP_CategorizeMessage (char *s); +void TP_FixTeamSets(); + +void TP_Init (); + + +//#define FPD_NO_TEAM_MACROS 1 +#define FPD_NO_FORCE_SKIN 256 +#define FPD_NO_FORCE_COLOR 512 diff --git a/source/test.bat b/source/test.bat new file mode 100755 index 00000000..25a06a57 --- /dev/null +++ b/source/test.bat @@ -0,0 +1,2 @@ +@echo %1 %2 %3 >> out.txt +cls \ No newline at end of file diff --git a/source/version.c b/source/version.c index 29efb75b..70be75bf 100644 --- a/source/version.c +++ b/source/version.c @@ -1,78 +1,78 @@ -/* - version.c - - Build number and version strings - - Copyright (C) 1996-1997 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: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA -*/ - -#include "quakedef.h" -#include "version.h" - -// char *date = "Oct 24 1996"; -static char *date = __DATE__ ; -static char *mon[12] = -{ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; -static char mond[12] = -{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; - -// returns days since Dec 21 1999 -int build_number (void) -{ - int m = 0; - int d = 0; - int y = 0; - static int b = 0; - - if (b != 0) - return b; - - for (m = 0; m < 11; m++) - { - if (Q_strncasecmp( &date[0], mon[m], 3 ) == 0) - break; - d += mond[m]; - } - - d += atoi( &date[4] ) - 1; - y = atoi( &date[7] ) - 1900; - b = d + (int)((y - 1) * 365.25); - - if (((y % 4) == 0) && m > 1) - b += 1; - - b -= 36148; // Dec 21 1999 - - return b; -} - - -/* -======================= -CL_Version_f -====================== -*/ -void CL_Version_f (void) -{ - Con_Printf ("QW version %4.2f\n", QW_VERSION); - Con_Printf ("QWExtended version %s (Build %04d)\n", QWE_VERSION, build_number()); - Con_Printf ("Exe: "__TIME__" "__DATE__"\n"); -} +/* + version.c + + Build number and version strings + + Copyright (C) 1996-1997 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA +*/ + +#include "quakedef.h" +#include "version.h" + +// char *date = "Oct 24 1996"; +static char *date = __DATE__ ; +static char *mon[12] = +{ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; +static char mond[12] = +{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + +// returns days since Dec 21 1999 +int build_number (void) +{ + int m = 0; + int d = 0; + int y = 0; + static int b = 0; + + if (b != 0) + return b; + + for (m = 0; m < 11; m++) + { + if (Q_strncasecmp( &date[0], mon[m], 3 ) == 0) + break; + d += mond[m]; + } + + d += atoi( &date[4] ) - 1; + y = atoi( &date[7] ) - 1900; + b = d + (int)((y - 1) * 365.25); + + if (((y % 4) == 0) && m > 1) + b += 1; + + b -= 36148; // Dec 21 1999 + + return b; +} + + +/* +======================= +CL_Version_f +====================== +*/ +void CL_Version_f (void) +{ + Con_Printf ("QW version %4.2f\n", QW_VERSION); + Con_Printf ("QWExtended version %s (Build %04d)\n", QWE_VERSION, build_number()); + Con_Printf ("Exe: "__TIME__" "__DATE__"\n"); +} diff --git a/source/version.h b/source/version.h index 9b96886c..cfadb38e 100644 --- a/source/version.h +++ b/source/version.h @@ -1,26 +1,25 @@ -// version.h - -#define GLQUAKE_VERSION 1.00 -#define QW_VERSION 2.40 -#define QWE_VERSION "0.14 Beta" -#define QWE_VERNUM 0.14 -#define QWE_FUNCS 86 -#define LINUX_VERSION 0.98 - -#define RELEASE_VERSION - -#ifdef _WIN32 -#define QW_PLATFORM "Win32" -#else -#define QW_PLATFORM "Linux" -#endif - -#ifdef GLQUAKE -#define QW_RENDERER "GL" -#else -#define QW_RENDERER "Soft" -#endif - - -int build_number (void); -void CL_Version_f (void); +// version.h + +#define GLQUAKE_VERSION 1.00 +#define QW_VERSION 2.40 +#define QWE_VERSION "0.158 Beta" +#define QWE_VERNUM 0.158 +#define LINUX_VERSION 0.98 + +#define RELEASE_VERSION + +#ifdef _WIN32 +#define QW_PLATFORM "Win32" +#else +#define QW_PLATFORM "Linux" +#endif + +#ifdef GLQUAKE +#define QW_RENDERER "GL" +#else +#define QW_RENDERER "Soft" +#endif + + +int build_number (void); +void CL_Version_f (void); diff --git a/source/vid.h b/source/vid.h index 6f2375e3..e3ce6df0 100644 --- a/source/vid.h +++ b/source/vid.h @@ -1,97 +1,97 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// vid.h -- video driver defs - -#define VID_CBITS 6 -#define VID_GRADES (1 << VID_CBITS) - -// a pixel can be one, two, or four bytes -typedef byte pixel_t; - -typedef struct vrect_s -{ - int x,y,width,height; - struct vrect_s *pnext; -} vrect_t; - -typedef struct -{ - pixel_t *buffer; // invisible buffer - pixel_t *colormap; // 256 * VID_GRADES size - unsigned short *colormap16; // 256 * VID_GRADES size - int fullbright; // index of first fullbright color - unsigned rowbytes; // may be > width if displayed in a window - unsigned width; - unsigned height; - float aspect; // width / height -- < 0 is taller than wide - int numpages; - int recalc_refdef; // if true, recalc vid-based stuff - pixel_t *conbuffer; - int conrowbytes; - unsigned conwidth; - unsigned conheight; - int maxwarpwidth; - int maxwarpheight; - pixel_t *direct; // direct drawing to framebuffer, if not - // NULL -} viddef_t; - -extern viddef_t vid; // global video state -extern unsigned short d_8to16table[256]; -extern unsigned d_8to24table[256]; -extern void (*vid_menudrawfn)(void); -extern void (*vid_menukeyfn)(int key); - -void VID_SetPalette (unsigned char *palette); -// called at startup and after any gamma correction - -void VID_ShiftPalette (unsigned char *palette); -// called for bonus and pain flashes, and for underwater color changes - -void VID_Init (unsigned char *palette); -// Called at startup to set up translation tables, takes 256 8 bit RGB values -// the palette data will go away after the call, so it must be copied off if -// the video driver will need it again - -void VID_Shutdown (void); -// Called at shutdown - -void VID_Update (vrect_t *rects); -// flushes the given rectangles from the view buffer to the screen - -int VID_SetMode (int modenum, unsigned char *palette); -// sets the mode; only used by the Quake engine for resetting to mode 0 (the -// base mode) on memory allocation failures - -void VID_HandlePause (qboolean pause); -// called only on Win32, when pause happens, so the mouse can be released - -void VID_LockBuffer (void); -void VID_UnlockBuffer (void); - - -#ifdef GLQUAKE -qboolean VID_Is8bit(void); -#endif - -#ifdef _WIN32 -void VID_SetDeviceGammaRamp (unsigned short *ramps); -extern qboolean vid_hwgamma_enabled; -#endif +/* +Copyright (C) 1996-1997 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. + +*/ +// vid.h -- video driver defs + +#define VID_CBITS 6 +#define VID_GRADES (1 << VID_CBITS) + +// a pixel can be one, two, or four bytes +typedef byte pixel_t; + +typedef struct vrect_s +{ + int x,y,width,height; + struct vrect_s *pnext; +} vrect_t; + +typedef struct +{ + pixel_t *buffer; // invisible buffer + pixel_t *colormap; // 256 * VID_GRADES size + unsigned short *colormap16; // 256 * VID_GRADES size + int fullbright; // index of first fullbright color + unsigned rowbytes; // may be > width if displayed in a window + unsigned width; + unsigned height; + float aspect; // width / height -- < 0 is taller than wide + int numpages; + int recalc_refdef; // if true, recalc vid-based stuff + pixel_t *conbuffer; + int conrowbytes; + unsigned conwidth; + unsigned conheight; + int maxwarpwidth; + int maxwarpheight; + pixel_t *direct; // direct drawing to framebuffer, if not + // NULL +} viddef_t; + +extern viddef_t vid; // global video state +extern unsigned short d_8to16table[256]; +extern unsigned d_8to24table[256]; +extern void (*vid_menudrawfn)(void); +extern void (*vid_menukeyfn)(int key); + +void VID_SetPalette (unsigned char *palette); +// called at startup and after any gamma correction + +void VID_ShiftPalette (unsigned char *palette); +// called for bonus and pain flashes, and for underwater color changes + +void VID_Init (unsigned char *palette); +// Called at startup to set up translation tables, takes 256 8 bit RGB values +// the palette data will go away after the call, so it must be copied off if +// the video driver will need it again + +void VID_Shutdown (void); +// Called at shutdown + +void VID_Update (vrect_t *rects); +// flushes the given rectangles from the view buffer to the screen + +int VID_SetMode (int modenum, unsigned char *palette); +// sets the mode; only used by the Quake engine for resetting to mode 0 (the +// base mode) on memory allocation failures + +void VID_HandlePause (qboolean pause); +// called only on Win32, when pause happens, so the mouse can be released + +void VID_LockBuffer (void); +void VID_UnlockBuffer (void); + + +#ifdef GLQUAKE +qboolean VID_Is8bit(void); +#endif + +#ifdef _WIN32 +void VID_SetDeviceGammaRamp (unsigned short *ramps); +extern qboolean vid_hwgamma_enabled; +#endif diff --git a/source/vid_null.c b/source/vid_null.c index baa0e7eb..b06dcb59 100644 --- a/source/vid_null.c +++ b/source/vid_null.c @@ -1,68 +1,68 @@ -// vid_null.c -- null video driver to aid porting efforts - -#include "quakedef.h" -#include "d_local.h" - -viddef_t vid; // global video state - -#define BASEWIDTH 320 -#define BASEHEIGHT 200 - -byte vid_buffer[BASEWIDTH*BASEHEIGHT]; -short zbuffer[BASEWIDTH*BASEHEIGHT]; -byte surfcache[256*1024]; - -unsigned short d_8to16table[256]; -unsigned d_8to24table[256]; - -void VID_SetPalette (unsigned char *palette) -{ -} - -void VID_ShiftPalette (unsigned char *palette) -{ -} - -void VID_Init (unsigned char *palette) -{ - vid.maxwarpwidth = vid.width = vid.conwidth = BASEWIDTH; - vid.maxwarpheight = vid.height = vid.conheight = BASEHEIGHT; - vid.aspect = 1.0; - vid.numpages = 1; - vid.colormap = host_colormap; - vid.fullbright = 256 - LittleLong (*((int *)vid.colormap + 2048)); - vid.buffer = vid.conbuffer = vid_buffer; - vid.rowbytes = vid.conrowbytes = BASEWIDTH; - - d_pzbuffer = zbuffer; - D_InitCaches (surfcache, sizeof(surfcache)); -} - -void VID_Shutdown (void) -{ -} - -void VID_Update (vrect_t *rects) -{ -} - -/* -================ -D_BeginDirectRect -================ -*/ -void D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height) -{ -} - - -/* -================ -D_EndDirectRect -================ -*/ -void D_EndDirectRect (int x, int y, int width, int height) -{ -} - - +// vid_null.c -- null video driver to aid porting efforts + +#include "quakedef.h" +#include "d_local.h" + +viddef_t vid; // global video state + +#define BASEWIDTH 320 +#define BASEHEIGHT 200 + +byte vid_buffer[BASEWIDTH*BASEHEIGHT]; +short zbuffer[BASEWIDTH*BASEHEIGHT]; +byte surfcache[256*1024]; + +unsigned short d_8to16table[256]; +unsigned d_8to24table[256]; + +void VID_SetPalette (unsigned char *palette) +{ +} + +void VID_ShiftPalette (unsigned char *palette) +{ +} + +void VID_Init (unsigned char *palette) +{ + vid.maxwarpwidth = vid.width = vid.conwidth = BASEWIDTH; + vid.maxwarpheight = vid.height = vid.conheight = BASEHEIGHT; + vid.aspect = 1.0; + vid.numpages = 1; + vid.colormap = host_colormap; + vid.fullbright = 256 - LittleLong (*((int *)vid.colormap + 2048)); + vid.buffer = vid.conbuffer = vid_buffer; + vid.rowbytes = vid.conrowbytes = BASEWIDTH; + + d_pzbuffer = zbuffer; + D_InitCaches (surfcache, sizeof(surfcache)); +} + +void VID_Shutdown (void) +{ +} + +void VID_Update (vrect_t *rects) +{ +} + +/* +================ +D_BeginDirectRect +================ +*/ +void D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height) +{ +} + + +/* +================ +D_EndDirectRect +================ +*/ +void D_EndDirectRect (int x, int y, int width, int height) +{ +} + + diff --git a/source/vid_svgalib.c b/source/vid_svgalib.c index 1f575aae..aa7c0a20 100644 --- a/source/vid_svgalib.c +++ b/source/vid_svgalib.c @@ -1,1023 +1,1023 @@ -/* -Copyright (C) 1996-1997 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 -#include -#include -#include -#include -#include - -#include - -#include "vga.h" -#include "vgakeyboard.h" -#include "vgamouse.h" - -#include "quakedef.h" -#include "d_local.h" -#include "keys.h" - -#define stringify(m) { #m, m } - -unsigned short d_8to16table[256]; -static byte *vid_surfcache; -static int VID_highhunkmark; - -int num_modes; -vga_modeinfo *modes; -int current_mode; - -int num_shades=32; - -struct -{ - char *name; - int num; -} mice[] = -{ - stringify(MOUSE_MICROSOFT), - stringify(MOUSE_MOUSESYSTEMS), - stringify(MOUSE_MMSERIES), - stringify(MOUSE_LOGITECH), - stringify(MOUSE_BUSMOUSE), - stringify(MOUSE_PS2), -}; - -static unsigned char scantokey[128]; -static byte vid_current_palette[768]; - -int num_mice = sizeof (mice) / sizeof(mice[0]); - -int d_con_indirect = 0; - -int svgalib_inited=0; -int UseMouse = 1; -int UseDisplay = 1; -int UseKeyboard = 1; - -int mouserate = MOUSE_DEFAULTSAMPLERATE; - -cvar_t vid_mode = {"vid_mode","5"}; -cvar_t vid_redrawfull = {"vid_redrawfull","0"}; -cvar_t vid_waitforrefresh = {"vid_waitforrefresh","0",CVAR_ARCHIVE}; - -char *framebuffer_ptr; - -cvar_t mouse_button_commands[3] = -{ - {"mouse1","+attack"}, - {"mouse2","+strafe"}, - {"mouse3","+forward"}, -}; - -int mouse_buttons; -int mouse_buttonstate; -int mouse_oldbuttonstate; -float mouse_x, mouse_y; -float old_mouse_x, old_mouse_y; -int mx, my; - -cvar_t _windowed_mouse = {"_windowed_mouse", "1", CVAR_ARCHIVE}; -cvar_t m_filter = {"m_filter","0"}; - -static byte backingbuf[48*24]; - -int VGA_width, VGA_height, VGA_rowbytes, VGA_bufferrowbytes, VGA_planar; -byte *VGA_pagebase; - -void VGA_UpdatePlanarScreen (void *srcbuffer); - -void D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height) -{ - int i, j, k, plane, reps, repshift, offset, vidpage, off; - - if (!svgalib_inited || !vid.direct || !vga_oktowrite()) return; - - if (vid.aspect > 1.5) - { - reps = 2; - repshift = 1; - } else { - reps = 1; - repshift = 0; - } - - vidpage = 0; - vga_setpage(0); - - if (VGA_planar) - { - for (plane=0 ; plane<4 ; plane++) - { - // select the correct plane for reading and writing - outb(0x02, 0x3C4); - outb(1 << plane, 0x3C5); - outb(4, 0x3CE); - outb(plane, 0x3CF); - - for (i=0 ; i<(height << repshift) ; i += reps) - { - for (k=0 ; k> 2) ; j++) - { - backingbuf[(i + k) * 24 + (j << 2) + plane] = - vid.direct[(y + i + k) * VGA_rowbytes + - (x >> 2) + j]; - vid.direct[(y + i + k) * VGA_rowbytes + (x>>2) + j] = - pbitmap[(i >> repshift) * 24 + - (j << 2) + plane]; - } - } - } - } - } else { - for (i=0 ; i<(height << repshift) ; i += reps) - { - for (j=0 ; j> repshift)*width], width); - } - } - } -} - -void D_EndDirectRect (int x, int y, int width, int height) -{ - int i, j, k, plane, reps, repshift, offset, vidpage, off; - - if (!svgalib_inited || !vid.direct || !vga_oktowrite()) return; - - if (vid.aspect > 1.5) - { - reps = 2; - repshift = 1; - } else { - reps = 1; - repshift = 0; - } - - vidpage = 0; - vga_setpage(0); - - if (VGA_planar) - { - for (plane=0 ; plane<4 ; plane++) - { - // select the correct plane for writing - outb(2, 0x3C4); - outb(1 << plane, 0x3C5); - outb(4, 0x3CE); - outb(plane, 0x3CF); - - for (i=0 ; i<(height << repshift) ; i += reps) - { - for (k=0 ; k> 2) ; j++) - { - vid.direct[(y + i + k) * VGA_rowbytes + (x>>2) + j] = - backingbuf[(i + k) * 24 + (j << 2) + plane]; - } - } - } - } - } else { - for (i=0 ; i<(height << repshift) ; i += reps) - { - for (j=0 ; j 255) - inf = 255; - palette[i] = inf; - } - - VID_SetPalette (palette); - - vid.recalc_refdef = 1; // force a surface cache flush - } -} - -void VID_DescribeMode_f (void) -{ - int modenum; - - modenum = Q_atoi (Cmd_Argv(1)); - if ((modenum >= num_modes) || (modenum < 0 ) || !modes[modenum].width) - Con_Printf("Invalid video mode: %d!\n",modenum); - Con_Printf("%d: %d x %d - ",modenum,modes[modenum].width,modes[modenum].height); - if (modes[modenum].bytesperpixel == 0) - Con_Printf("ModeX\n"); - else - Con_Printf("%d bpp\n", modes[modenum].bytesperpixel<<3); -} - -void VID_DescribeModes_f (void) -{ - int i; - - for (i=0;i> 2; - - if (UseDisplay && vga_oktowrite()) - vga_setpalvec(0, 256, tmppal); - - } -} - -int VID_SetMode (int modenum, unsigned char *palette) -{ - int bsize, zsize, tsize; - - if ((modenum >= num_modes) || (modenum < 0) || !modes[modenum].width) - { - Cvar_SetValue (&vid_mode, (float)current_mode); - - Con_Printf("No such video mode: %d\n",modenum); - - return 0; - } - - Cvar_SetValue (&vid_mode, (float)modenum); - - current_mode=modenum; - - vid.width = modes[current_mode].width; - vid.height = modes[current_mode].height; - - VGA_width = modes[current_mode].width; - VGA_height = modes[current_mode].height; - VGA_planar = modes[current_mode].bytesperpixel == 0; - VGA_rowbytes = modes[current_mode].linewidth; - vid.rowbytes = modes[current_mode].linewidth; - if (VGA_planar) { - VGA_bufferrowbytes = modes[current_mode].linewidth * 4; - vid.rowbytes = modes[current_mode].linewidth*4; - } - - vid.aspect = ((float)vid.height / (float)vid.width) * (320.0 / 240.0); - vid.colormap = (pixel_t *) host_colormap; - vid.fullbright = 256 - LittleLong (*((int *)vid.colormap + 2048)); - vid.conrowbytes = vid.rowbytes; - vid.conwidth = vid.width; - vid.conheight = vid.height; - vid.numpages = 1; - - vid.maxwarpwidth = WARP_WIDTH; - vid.maxwarpheight = WARP_HEIGHT; - - // alloc zbuffer and surface cache - if (d_pzbuffer) { - D_FlushCaches(); - Hunk_FreeToHighMark (VID_highhunkmark); - d_pzbuffer = NULL; - vid_surfcache = NULL; - } - - bsize = vid.rowbytes * vid.height; - tsize = D_SurfaceCacheForRes (vid.width, vid.height); - zsize = vid.width * vid.height * sizeof(*d_pzbuffer); - - VID_highhunkmark = Hunk_HighMark (); - - d_pzbuffer = Hunk_HighAllocName (bsize+tsize+zsize, "video"); - - vid_surfcache = ((byte *)d_pzbuffer) + zsize; - - vid.conbuffer = vid.buffer = (pixel_t *)(((byte *)d_pzbuffer) + zsize + tsize); - - D_InitCaches (vid_surfcache, tsize); - -// get goin' - - vga_setmode(current_mode); - VID_SetPalette(palette); - - VGA_pagebase = vid.direct = framebuffer_ptr = (char *) vga_getgraphmem(); -// if (vga_setlinearaddressing()>0) -// framebuffer_ptr = (char *) vga_getgraphmem(); - if (!framebuffer_ptr) - Sys_Error("This mode isn't hapnin'\n"); - - vga_setpage(0); - - svgalib_inited=1; - - vid.recalc_refdef = 1; // force a surface cache flush - - return 0; -} - -void VID_Init(unsigned char *palette) -{ - - int i; - int w, h, d; - - S_Init(); // sound gets initialized here - - if (svgalib_inited) - return; - -// Cmd_AddCommand ("gamma", VID_Gamma_f); - - if (UseDisplay) - { - vga_init(); - - VID_InitModes(); - - Cvar_RegisterVariable (&vid_mode); - Cvar_RegisterVariable (&vid_redrawfull); - Cvar_RegisterVariable (&vid_waitforrefresh); - - Cmd_AddCommand("vid_nummodes", VID_NumModes_f); - Cmd_AddCommand("vid_describemode", VID_DescribeMode_f); - Cmd_AddCommand("vid_describemodes", VID_DescribeModes_f); - Cmd_AddCommand("vid_debug", VID_Debug_f); - - // interpret command-line params - - w = h = d = 0; - if (getenv("GSVGAMODE")) - current_mode = get_mode(getenv("GSVGAMODE"), w, h, d); - else if (COM_CheckParm("-mode")) - current_mode = get_mode(com_argv[COM_CheckParm("-mode")+1], w, h, d); - else if (COM_CheckParm("-w") || COM_CheckParm("-h") - || COM_CheckParm("-d")) - { - if (COM_CheckParm("-w")) - w = Q_atoi(com_argv[COM_CheckParm("-w")+1]); - if (COM_CheckParm("-h")) - h = Q_atoi(com_argv[COM_CheckParm("-h")+1]); - if (COM_CheckParm("-d")) - d = Q_atoi(com_argv[COM_CheckParm("-d")+1]); - current_mode = get_mode(0, w, h, d); - } - else - current_mode = G320x200x256; - - // set vid parameters - VID_SetMode(current_mode, palette); - - VID_SetPalette(palette); - - // we do want to run in the background when switched away - vga_runinbackground(1); - } - - if (COM_CheckParm("-nokbd")) UseKeyboard = 0; - - if (UseKeyboard) - { - for (i=0 ; i<128 ; i++) - scantokey[i] = ' '; - - scantokey[ 1] = K_ESCAPE; - scantokey[ 2] = '1'; - scantokey[ 3] = '2'; - scantokey[ 4] = '3'; - scantokey[ 5] = '4'; - scantokey[ 6] = '5'; - scantokey[ 7] = '6'; - scantokey[ 8] = '7'; - scantokey[ 9] = '8'; - scantokey[ 10] = '9'; - scantokey[ 11] = '0'; - scantokey[ 12] = '-'; - scantokey[ 13] = '='; - scantokey[ 14] = K_BACKSPACE; - scantokey[ 15] = K_TAB; - scantokey[ 16] = 'q'; - scantokey[ 17] = 'w'; - scantokey[ 18] = 'e'; - scantokey[ 19] = 'r'; - scantokey[ 20] = 't'; - scantokey[ 21] = 'y'; - scantokey[ 22] = 'u'; - scantokey[ 23] = 'i'; - scantokey[ 24] = 'o'; - scantokey[ 25] = 'p'; - scantokey[ 26] = '['; - scantokey[ 27] = ']'; - scantokey[ 28] = K_ENTER; - scantokey[ 29] = K_CTRL; //left - scantokey[ 30] = 'a'; - scantokey[ 31] = 's'; - scantokey[ 32] = 'd'; - scantokey[ 33] = 'f'; - scantokey[ 34] = 'g'; - scantokey[ 35] = 'h'; - scantokey[ 36] = 'j'; - scantokey[ 37] = 'k'; - scantokey[ 38] = 'l'; - scantokey[ 39] = ';'; - scantokey[ 40] = '\''; - scantokey[ 41] = '`'; - scantokey[ 42] = K_SHIFT; //left - scantokey[ 43] = '\\'; - scantokey[ 44] = 'z'; - scantokey[ 45] = 'x'; - scantokey[ 46] = 'c'; - scantokey[ 47] = 'v'; - scantokey[ 48] = 'b'; - scantokey[ 49] = 'n'; - scantokey[ 50] = 'm'; - scantokey[ 51] = ','; - scantokey[ 52] = '.'; - scantokey[ 53] = '/'; - scantokey[ 54] = K_SHIFT; //right - scantokey[ 55] = '*'; //keypad - scantokey[ 56] = K_ALT; //left - scantokey[ 57] = ' '; - scantokey[ 58] = K_CAPSLOCK; - scantokey[ 59] = K_F1; - scantokey[ 60] = K_F2; - scantokey[ 61] = K_F3; - scantokey[ 62] = K_F4; - scantokey[ 63] = K_F5; - scantokey[ 64] = K_F6; - scantokey[ 65] = K_F7; - scantokey[ 66] = K_F8; - scantokey[ 67] = K_F9; - scantokey[ 68] = K_F10; - // 69 numlock - // 70 scrollock - scantokey[ 71] = K_HOME; - scantokey[ 72] = K_UPARROW; - scantokey[ 73] = K_PGUP; - scantokey[ 74] = '-'; - scantokey[ 75] = K_LEFTARROW; - scantokey[ 76] = '5'; - scantokey[ 77] = K_RIGHTARROW; - scantokey[ 79] = K_END; - scantokey[ 78] = '+'; - scantokey[ 80] = K_DOWNARROW; - scantokey[ 81] = K_PGDN; - scantokey[ 82] = K_INS; - scantokey[ 83] = K_DEL; - // 84 to 86 not used - scantokey[ 87] = K_F11; - scantokey[ 88] = K_F12; - // 89 to 95 not used - scantokey[ 96] = K_ENTER; //keypad enter - scantokey[ 97] = K_CTRL; //right - scantokey[ 98] = '/'; - scantokey[ 99] = K_F12; // print screen, bind to screenshot by default - scantokey[100] = K_ALT; // right - - scantokey[101] = K_PAUSE; // break - scantokey[102] = K_HOME; - scantokey[103] = K_UPARROW; - scantokey[104] = K_PGUP; - scantokey[105] = K_LEFTARROW; - scantokey[106] = K_RIGHTARROW; - scantokey[107] = K_END; - scantokey[108] = K_DOWNARROW; - scantokey[109] = K_PGDN; - scantokey[110] = K_INS; - scantokey[111] = K_DEL; - - scantokey[119] = K_PAUSE; - - if (keyboard_init()) - Sys_Error("keyboard_init() failed"); - keyboard_seteventhandler(keyhandler); - } - -} - -void VID_Update(vrect_t *rects) -{ - if (!svgalib_inited) - return; - - if (!vga_oktowrite()) - return; // can't update screen if it's not active - - if (vid_waitforrefresh.value) - vga_waitretrace(); - - if (VGA_planar) - VGA_UpdatePlanarScreen (vid.buffer); - - else if (vid_redrawfull.value) { - int total = vid.rowbytes * vid.height; - int offset; - - for (offset=0;offset0x10000)?0x10000:(total-offset))); - } - } else { - int ycount; - int offset; - int vidpage=0; - - vga_setpage(0); - - while (rects) - { - ycount = rects->height; - offset = rects->y * vid.rowbytes + rects->x; - while (ycount--) - { - register int i = offset % 0x10000; - - if ((offset / 0x10000) != vidpage) { - vidpage=offset / 0x10000; - vga_setpage(vidpage); - } - if (rects->width + i > 0x10000) { - memcpy(framebuffer_ptr + i, - vid.buffer + offset, - 0x10000 - i); - vga_setpage(++vidpage); - memcpy(framebuffer_ptr, - vid.buffer + offset + 0x10000 - i, - rects->width - 0x10000 + i); - } else - memcpy(framebuffer_ptr + i, - vid.buffer + offset, - rects->width); - offset += vid.rowbytes; - } - - rects = rects->pnext; - } - } - - if (vid_mode.value != current_mode) - VID_SetMode ((int)vid_mode.value, vid_current_palette); -} - -static int dither; - -void VID_DitherOn(void) -{ - if (dither == 0) - { -// R_ViewChanged (&vrect, sb_lines, vid.aspect); - dither = 1; - } -} - -void VID_DitherOff(void) -{ - if (dither) - { -// R_ViewChanged (&vrect, sb_lines, vid.aspect); - dither = 0; - } -} - -void Sys_SendKeyEvents(void) -{ - if (!svgalib_inited) - return; - - if (UseKeyboard) - while (keyboard_update()); -} - -void Force_CenterView_f (void) -{ - cl.viewangles[PITCH] = 0; -} - - -void mousehandler(int buttonstate, int dx, int dy) -{ - mouse_buttonstate = buttonstate; - mx += dx; - my += dy; -} - -void IN_Init(void) -{ - - int mtype; - char *mousedev; - int mouserate; - - Cvar_RegisterVariable (&m_filter); - - if (UseMouse) - { - Cvar_RegisterVariable (&mouse_button_commands[0]); - Cvar_RegisterVariable (&mouse_button_commands[1]); - Cvar_RegisterVariable (&mouse_button_commands[2]); - Cmd_AddCommand ("force_centerview", Force_CenterView_f); - - mouse_buttons = 3; - - mtype = vga_getmousetype(); - - mousedev = "/dev/mouse"; - if (getenv("MOUSEDEV")) mousedev = getenv("MOUSEDEV"); - if (COM_CheckParm("-mdev")) - mousedev = com_argv[COM_CheckParm("-mdev")+1]; - - mouserate = 1200; - if (getenv("MOUSERATE")) mouserate = atoi(getenv("MOUSERATE")); - if (COM_CheckParm("-mrate")) - mouserate = atoi(com_argv[COM_CheckParm("-mrate")+1]); - -// printf("Mouse: dev=%s,type=%s,speed=%d\n", -// mousedev, mice[mtype].name, mouserate); - if (mouse_init(mousedev, mtype, mouserate)) - { - Con_Printf("No mouse found\n"); - UseMouse = 0; - } - else - mouse_seteventhandler(mousehandler); - - } - -} - -void IN_Shutdown(void) -{ - if (UseMouse) - mouse_close(); -} - -/* -=========== -IN_Commands -=========== -*/ -void IN_Commands (void) -{ - if (UseMouse /*&& cls.state != ca_dedicated*/) - { - // poll mouse values - while (mouse_update()) - ; - - // perform button actions - if ((mouse_buttonstate & MOUSE_LEFTBUTTON) && - !(mouse_oldbuttonstate & MOUSE_LEFTBUTTON)) - Key_Event (K_MOUSE1, true); - else if (!(mouse_buttonstate & MOUSE_LEFTBUTTON) && - (mouse_oldbuttonstate & MOUSE_LEFTBUTTON)) - Key_Event (K_MOUSE1, false); - - if ((mouse_buttonstate & MOUSE_RIGHTBUTTON) && - !(mouse_oldbuttonstate & MOUSE_RIGHTBUTTON)) - Key_Event (K_MOUSE2, true); - else if (!(mouse_buttonstate & MOUSE_RIGHTBUTTON) && - (mouse_oldbuttonstate & MOUSE_RIGHTBUTTON)) - Key_Event (K_MOUSE2, false); - - if ((mouse_buttonstate & MOUSE_MIDDLEBUTTON) && - !(mouse_oldbuttonstate & MOUSE_MIDDLEBUTTON)) - Key_Event (K_MOUSE3, true); - else if (!(mouse_buttonstate & MOUSE_MIDDLEBUTTON) && - (mouse_oldbuttonstate & MOUSE_MIDDLEBUTTON)) - Key_Event (K_MOUSE3, false); - - mouse_oldbuttonstate = mouse_buttonstate; - } -} - -/* -=========== -IN_Move -=========== -*/ -void IN_MouseMove (usercmd_t *cmd) -{ - if (!UseMouse) - return; - - // poll mouse values - while (mouse_update()) - ; - - if (m_filter.value) - { - mouse_x = (mx + old_mouse_x) * 0.5; - mouse_y = (my + old_mouse_y) * 0.5; - } - else - { - mouse_x = mx; - mouse_y = my; - } - old_mouse_x = mx; - old_mouse_y = my; - mx = my = 0; // clear for next update - - mouse_x *= sensitivity.value; - mouse_y *= sensitivity.value; - -// add mouse X/Y movement to cmd - if ( (in_strafe.state & 1) || (lookstrafe.value && (in_mlook.state & 1) )) - cmd->sidemove += m_side.value * mouse_x; - else - cl.viewangles[YAW] -= m_yaw.value * mouse_x; - - if (in_mlook.state & 1) - V_StopPitchDrift (); - - if ( (in_mlook.state & 1) && !(in_strafe.state & 1)) - { - cl.viewangles[PITCH] += m_pitch.value * mouse_y; - if (cl.viewangles[PITCH] > 80) - cl.viewangles[PITCH] = 80; - if (cl.viewangles[PITCH] < -70) - cl.viewangles[PITCH] = -70; - } - else - { - cmd->forwardmove -= m_forward.value * mouse_y; - } -} - -void IN_Move (usercmd_t *cmd) -{ - IN_MouseMove(cmd); -} - - -/* -================ -VID_ModeInfo -================ -*/ -char *VID_ModeInfo (int modenum) -{ - static char *badmodestr = "Bad mode number"; - static char modestr[40]; - - if (modenum == 0) - { - sprintf (modestr, "%d x %d, %d bpp", - vid.width, vid.height, modes[current_mode].bytesperpixel*8); - return (modestr); - } - else - { - return (badmodestr); - } -} - -void VID_LockBuffer (void) -{ -} - -void VID_UnlockBuffer (void) -{ -} - - +/* +Copyright (C) 1996-1997 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 +#include +#include +#include +#include +#include + +#include + +#include "vga.h" +#include "vgakeyboard.h" +#include "vgamouse.h" + +#include "quakedef.h" +#include "d_local.h" +#include "keys.h" + +#define stringify(m) { #m, m } + +unsigned short d_8to16table[256]; +static byte *vid_surfcache; +static int VID_highhunkmark; + +int num_modes; +vga_modeinfo *modes; +int current_mode; + +int num_shades=32; + +struct +{ + char *name; + int num; +} mice[] = +{ + stringify(MOUSE_MICROSOFT), + stringify(MOUSE_MOUSESYSTEMS), + stringify(MOUSE_MMSERIES), + stringify(MOUSE_LOGITECH), + stringify(MOUSE_BUSMOUSE), + stringify(MOUSE_PS2), +}; + +static unsigned char scantokey[128]; +static byte vid_current_palette[768]; + +int num_mice = sizeof (mice) / sizeof(mice[0]); + +int d_con_indirect = 0; + +int svgalib_inited=0; +int UseMouse = 1; +int UseDisplay = 1; +int UseKeyboard = 1; + +int mouserate = MOUSE_DEFAULTSAMPLERATE; + +cvar_t vid_mode = {"vid_mode","5"}; +cvar_t vid_redrawfull = {"vid_redrawfull","0"}; +cvar_t vid_waitforrefresh = {"vid_waitforrefresh","0",CVAR_ARCHIVE}; + +char *framebuffer_ptr; + +cvar_t mouse_button_commands[3] = +{ + {"mouse1","+attack"}, + {"mouse2","+strafe"}, + {"mouse3","+forward"}, +}; + +int mouse_buttons; +int mouse_buttonstate; +int mouse_oldbuttonstate; +float mouse_x, mouse_y; +float old_mouse_x, old_mouse_y; +int mx, my; + +cvar_t _windowed_mouse = {"_windowed_mouse", "1", CVAR_ARCHIVE}; +cvar_t m_filter = {"m_filter","0"}; + +static byte backingbuf[48*24]; + +int VGA_width, VGA_height, VGA_rowbytes, VGA_bufferrowbytes, VGA_planar; +byte *VGA_pagebase; + +void VGA_UpdatePlanarScreen (void *srcbuffer); + +void D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height) +{ + int i, j, k, plane, reps, repshift, offset, vidpage, off; + + if (!svgalib_inited || !vid.direct || !vga_oktowrite()) return; + + if (vid.aspect > 1.5) + { + reps = 2; + repshift = 1; + } else { + reps = 1; + repshift = 0; + } + + vidpage = 0; + vga_setpage(0); + + if (VGA_planar) + { + for (plane=0 ; plane<4 ; plane++) + { + // select the correct plane for reading and writing + outb(0x02, 0x3C4); + outb(1 << plane, 0x3C5); + outb(4, 0x3CE); + outb(plane, 0x3CF); + + for (i=0 ; i<(height << repshift) ; i += reps) + { + for (k=0 ; k> 2) ; j++) + { + backingbuf[(i + k) * 24 + (j << 2) + plane] = + vid.direct[(y + i + k) * VGA_rowbytes + + (x >> 2) + j]; + vid.direct[(y + i + k) * VGA_rowbytes + (x>>2) + j] = + pbitmap[(i >> repshift) * 24 + + (j << 2) + plane]; + } + } + } + } + } else { + for (i=0 ; i<(height << repshift) ; i += reps) + { + for (j=0 ; j> repshift)*width], width); + } + } + } +} + +void D_EndDirectRect (int x, int y, int width, int height) +{ + int i, j, k, plane, reps, repshift, offset, vidpage, off; + + if (!svgalib_inited || !vid.direct || !vga_oktowrite()) return; + + if (vid.aspect > 1.5) + { + reps = 2; + repshift = 1; + } else { + reps = 1; + repshift = 0; + } + + vidpage = 0; + vga_setpage(0); + + if (VGA_planar) + { + for (plane=0 ; plane<4 ; plane++) + { + // select the correct plane for writing + outb(2, 0x3C4); + outb(1 << plane, 0x3C5); + outb(4, 0x3CE); + outb(plane, 0x3CF); + + for (i=0 ; i<(height << repshift) ; i += reps) + { + for (k=0 ; k> 2) ; j++) + { + vid.direct[(y + i + k) * VGA_rowbytes + (x>>2) + j] = + backingbuf[(i + k) * 24 + (j << 2) + plane]; + } + } + } + } + } else { + for (i=0 ; i<(height << repshift) ; i += reps) + { + for (j=0 ; j 255) + inf = 255; + palette[i] = inf; + } + + VID_SetPalette (palette); + + vid.recalc_refdef = 1; // force a surface cache flush + } +} + +void VID_DescribeMode_f (void) +{ + int modenum; + + modenum = Q_atoi (Cmd_Argv(1)); + if ((modenum >= num_modes) || (modenum < 0 ) || !modes[modenum].width) + Con_Printf("Invalid video mode: %d!\n",modenum); + Con_Printf("%d: %d x %d - ",modenum,modes[modenum].width,modes[modenum].height); + if (modes[modenum].bytesperpixel == 0) + Con_Printf("ModeX\n"); + else + Con_Printf("%d bpp\n", modes[modenum].bytesperpixel<<3); +} + +void VID_DescribeModes_f (void) +{ + int i; + + for (i=0;i> 2; + + if (UseDisplay && vga_oktowrite()) + vga_setpalvec(0, 256, tmppal); + + } +} + +int VID_SetMode (int modenum, unsigned char *palette) +{ + int bsize, zsize, tsize; + + if ((modenum >= num_modes) || (modenum < 0) || !modes[modenum].width) + { + Cvar_SetValue (&vid_mode, (float)current_mode); + + Con_Printf("No such video mode: %d\n",modenum); + + return 0; + } + + Cvar_SetValue (&vid_mode, (float)modenum); + + current_mode=modenum; + + vid.width = modes[current_mode].width; + vid.height = modes[current_mode].height; + + VGA_width = modes[current_mode].width; + VGA_height = modes[current_mode].height; + VGA_planar = modes[current_mode].bytesperpixel == 0; + VGA_rowbytes = modes[current_mode].linewidth; + vid.rowbytes = modes[current_mode].linewidth; + if (VGA_planar) { + VGA_bufferrowbytes = modes[current_mode].linewidth * 4; + vid.rowbytes = modes[current_mode].linewidth*4; + } + + vid.aspect = ((float)vid.height / (float)vid.width) * (320.0 / 240.0); + vid.colormap = (pixel_t *) host_colormap; + vid.fullbright = 256 - LittleLong (*((int *)vid.colormap + 2048)); + vid.conrowbytes = vid.rowbytes; + vid.conwidth = vid.width; + vid.conheight = vid.height; + vid.numpages = 1; + + vid.maxwarpwidth = WARP_WIDTH; + vid.maxwarpheight = WARP_HEIGHT; + + // alloc zbuffer and surface cache + if (d_pzbuffer) { + D_FlushCaches(); + Hunk_FreeToHighMark (VID_highhunkmark); + d_pzbuffer = NULL; + vid_surfcache = NULL; + } + + bsize = vid.rowbytes * vid.height; + tsize = D_SurfaceCacheForRes (vid.width, vid.height); + zsize = vid.width * vid.height * sizeof(*d_pzbuffer); + + VID_highhunkmark = Hunk_HighMark (); + + d_pzbuffer = Hunk_HighAllocName (bsize+tsize+zsize, "video"); + + vid_surfcache = ((byte *)d_pzbuffer) + zsize; + + vid.conbuffer = vid.buffer = (pixel_t *)(((byte *)d_pzbuffer) + zsize + tsize); + + D_InitCaches (vid_surfcache, tsize); + +// get goin' + + vga_setmode(current_mode); + VID_SetPalette(palette); + + VGA_pagebase = vid.direct = framebuffer_ptr = (char *) vga_getgraphmem(); +// if (vga_setlinearaddressing()>0) +// framebuffer_ptr = (char *) vga_getgraphmem(); + if (!framebuffer_ptr) + Sys_Error("This mode isn't hapnin'\n"); + + vga_setpage(0); + + svgalib_inited=1; + + vid.recalc_refdef = 1; // force a surface cache flush + + return 0; +} + +void VID_Init(unsigned char *palette) +{ + + int i; + int w, h, d; + + S_Init(); // sound gets initialized here + + if (svgalib_inited) + return; + +// Cmd_AddCommand ("gamma", VID_Gamma_f); + + if (UseDisplay) + { + vga_init(); + + VID_InitModes(); + + Cvar_RegisterVariable (&vid_mode); + Cvar_RegisterVariable (&vid_redrawfull); + Cvar_RegisterVariable (&vid_waitforrefresh); + + Cmd_AddCommand("vid_nummodes", VID_NumModes_f); + Cmd_AddCommand("vid_describemode", VID_DescribeMode_f); + Cmd_AddCommand("vid_describemodes", VID_DescribeModes_f); + Cmd_AddCommand("vid_debug", VID_Debug_f); + + // interpret command-line params + + w = h = d = 0; + if (getenv("GSVGAMODE")) + current_mode = get_mode(getenv("GSVGAMODE"), w, h, d); + else if (COM_CheckParm("-mode")) + current_mode = get_mode(com_argv[COM_CheckParm("-mode")+1], w, h, d); + else if (COM_CheckParm("-w") || COM_CheckParm("-h") + || COM_CheckParm("-d")) + { + if (COM_CheckParm("-w")) + w = Q_atoi(com_argv[COM_CheckParm("-w")+1]); + if (COM_CheckParm("-h")) + h = Q_atoi(com_argv[COM_CheckParm("-h")+1]); + if (COM_CheckParm("-d")) + d = Q_atoi(com_argv[COM_CheckParm("-d")+1]); + current_mode = get_mode(0, w, h, d); + } + else + current_mode = G320x200x256; + + // set vid parameters + VID_SetMode(current_mode, palette); + + VID_SetPalette(palette); + + // we do want to run in the background when switched away + vga_runinbackground(1); + } + + if (COM_CheckParm("-nokbd")) UseKeyboard = 0; + + if (UseKeyboard) + { + for (i=0 ; i<128 ; i++) + scantokey[i] = ' '; + + scantokey[ 1] = K_ESCAPE; + scantokey[ 2] = '1'; + scantokey[ 3] = '2'; + scantokey[ 4] = '3'; + scantokey[ 5] = '4'; + scantokey[ 6] = '5'; + scantokey[ 7] = '6'; + scantokey[ 8] = '7'; + scantokey[ 9] = '8'; + scantokey[ 10] = '9'; + scantokey[ 11] = '0'; + scantokey[ 12] = '-'; + scantokey[ 13] = '='; + scantokey[ 14] = K_BACKSPACE; + scantokey[ 15] = K_TAB; + scantokey[ 16] = 'q'; + scantokey[ 17] = 'w'; + scantokey[ 18] = 'e'; + scantokey[ 19] = 'r'; + scantokey[ 20] = 't'; + scantokey[ 21] = 'y'; + scantokey[ 22] = 'u'; + scantokey[ 23] = 'i'; + scantokey[ 24] = 'o'; + scantokey[ 25] = 'p'; + scantokey[ 26] = '['; + scantokey[ 27] = ']'; + scantokey[ 28] = K_ENTER; + scantokey[ 29] = K_CTRL; //left + scantokey[ 30] = 'a'; + scantokey[ 31] = 's'; + scantokey[ 32] = 'd'; + scantokey[ 33] = 'f'; + scantokey[ 34] = 'g'; + scantokey[ 35] = 'h'; + scantokey[ 36] = 'j'; + scantokey[ 37] = 'k'; + scantokey[ 38] = 'l'; + scantokey[ 39] = ';'; + scantokey[ 40] = '\''; + scantokey[ 41] = '`'; + scantokey[ 42] = K_SHIFT; //left + scantokey[ 43] = '\\'; + scantokey[ 44] = 'z'; + scantokey[ 45] = 'x'; + scantokey[ 46] = 'c'; + scantokey[ 47] = 'v'; + scantokey[ 48] = 'b'; + scantokey[ 49] = 'n'; + scantokey[ 50] = 'm'; + scantokey[ 51] = ','; + scantokey[ 52] = '.'; + scantokey[ 53] = '/'; + scantokey[ 54] = K_SHIFT; //right + scantokey[ 55] = '*'; //keypad + scantokey[ 56] = K_ALT; //left + scantokey[ 57] = ' '; + scantokey[ 58] = K_CAPSLOCK; + scantokey[ 59] = K_F1; + scantokey[ 60] = K_F2; + scantokey[ 61] = K_F3; + scantokey[ 62] = K_F4; + scantokey[ 63] = K_F5; + scantokey[ 64] = K_F6; + scantokey[ 65] = K_F7; + scantokey[ 66] = K_F8; + scantokey[ 67] = K_F9; + scantokey[ 68] = K_F10; + // 69 numlock + // 70 scrollock + scantokey[ 71] = K_HOME; + scantokey[ 72] = K_UPARROW; + scantokey[ 73] = K_PGUP; + scantokey[ 74] = '-'; + scantokey[ 75] = K_LEFTARROW; + scantokey[ 76] = '5'; + scantokey[ 77] = K_RIGHTARROW; + scantokey[ 79] = K_END; + scantokey[ 78] = '+'; + scantokey[ 80] = K_DOWNARROW; + scantokey[ 81] = K_PGDN; + scantokey[ 82] = K_INS; + scantokey[ 83] = K_DEL; + // 84 to 86 not used + scantokey[ 87] = K_F11; + scantokey[ 88] = K_F12; + // 89 to 95 not used + scantokey[ 96] = K_ENTER; //keypad enter + scantokey[ 97] = K_CTRL; //right + scantokey[ 98] = '/'; + scantokey[ 99] = K_F12; // print screen, bind to screenshot by default + scantokey[100] = K_ALT; // right + + scantokey[101] = K_PAUSE; // break + scantokey[102] = K_HOME; + scantokey[103] = K_UPARROW; + scantokey[104] = K_PGUP; + scantokey[105] = K_LEFTARROW; + scantokey[106] = K_RIGHTARROW; + scantokey[107] = K_END; + scantokey[108] = K_DOWNARROW; + scantokey[109] = K_PGDN; + scantokey[110] = K_INS; + scantokey[111] = K_DEL; + + scantokey[119] = K_PAUSE; + + if (keyboard_init()) + Sys_Error("keyboard_init() failed"); + keyboard_seteventhandler(keyhandler); + } + +} + +void VID_Update(vrect_t *rects) +{ + if (!svgalib_inited) + return; + + if (!vga_oktowrite()) + return; // can't update screen if it's not active + + if (vid_waitforrefresh.value) + vga_waitretrace(); + + if (VGA_planar) + VGA_UpdatePlanarScreen (vid.buffer); + + else if (vid_redrawfull.value) { + int total = vid.rowbytes * vid.height; + int offset; + + for (offset=0;offset0x10000)?0x10000:(total-offset))); + } + } else { + int ycount; + int offset; + int vidpage=0; + + vga_setpage(0); + + while (rects) + { + ycount = rects->height; + offset = rects->y * vid.rowbytes + rects->x; + while (ycount--) + { + register int i = offset % 0x10000; + + if ((offset / 0x10000) != vidpage) { + vidpage=offset / 0x10000; + vga_setpage(vidpage); + } + if (rects->width + i > 0x10000) { + memcpy(framebuffer_ptr + i, + vid.buffer + offset, + 0x10000 - i); + vga_setpage(++vidpage); + memcpy(framebuffer_ptr, + vid.buffer + offset + 0x10000 - i, + rects->width - 0x10000 + i); + } else + memcpy(framebuffer_ptr + i, + vid.buffer + offset, + rects->width); + offset += vid.rowbytes; + } + + rects = rects->pnext; + } + } + + if (vid_mode.value != current_mode) + VID_SetMode ((int)vid_mode.value, vid_current_palette); +} + +static int dither; + +void VID_DitherOn(void) +{ + if (dither == 0) + { +// R_ViewChanged (&vrect, sb_lines, vid.aspect); + dither = 1; + } +} + +void VID_DitherOff(void) +{ + if (dither) + { +// R_ViewChanged (&vrect, sb_lines, vid.aspect); + dither = 0; + } +} + +void Sys_SendKeyEvents(void) +{ + if (!svgalib_inited) + return; + + if (UseKeyboard) + while (keyboard_update()); +} + +void Force_CenterView_f (void) +{ + cl.viewangles[PITCH] = 0; +} + + +void mousehandler(int buttonstate, int dx, int dy) +{ + mouse_buttonstate = buttonstate; + mx += dx; + my += dy; +} + +void IN_Init(void) +{ + + int mtype; + char *mousedev; + int mouserate; + + Cvar_RegisterVariable (&m_filter); + + if (UseMouse) + { + Cvar_RegisterVariable (&mouse_button_commands[0]); + Cvar_RegisterVariable (&mouse_button_commands[1]); + Cvar_RegisterVariable (&mouse_button_commands[2]); + Cmd_AddCommand ("force_centerview", Force_CenterView_f); + + mouse_buttons = 3; + + mtype = vga_getmousetype(); + + mousedev = "/dev/mouse"; + if (getenv("MOUSEDEV")) mousedev = getenv("MOUSEDEV"); + if (COM_CheckParm("-mdev")) + mousedev = com_argv[COM_CheckParm("-mdev")+1]; + + mouserate = 1200; + if (getenv("MOUSERATE")) mouserate = atoi(getenv("MOUSERATE")); + if (COM_CheckParm("-mrate")) + mouserate = atoi(com_argv[COM_CheckParm("-mrate")+1]); + +// printf("Mouse: dev=%s,type=%s,speed=%d\n", +// mousedev, mice[mtype].name, mouserate); + if (mouse_init(mousedev, mtype, mouserate)) + { + Con_Printf("No mouse found\n"); + UseMouse = 0; + } + else + mouse_seteventhandler(mousehandler); + + } + +} + +void IN_Shutdown(void) +{ + if (UseMouse) + mouse_close(); +} + +/* +=========== +IN_Commands +=========== +*/ +void IN_Commands (void) +{ + if (UseMouse /*&& cls.state != ca_dedicated*/) + { + // poll mouse values + while (mouse_update()) + ; + + // perform button actions + if ((mouse_buttonstate & MOUSE_LEFTBUTTON) && + !(mouse_oldbuttonstate & MOUSE_LEFTBUTTON)) + Key_Event (K_MOUSE1, true); + else if (!(mouse_buttonstate & MOUSE_LEFTBUTTON) && + (mouse_oldbuttonstate & MOUSE_LEFTBUTTON)) + Key_Event (K_MOUSE1, false); + + if ((mouse_buttonstate & MOUSE_RIGHTBUTTON) && + !(mouse_oldbuttonstate & MOUSE_RIGHTBUTTON)) + Key_Event (K_MOUSE2, true); + else if (!(mouse_buttonstate & MOUSE_RIGHTBUTTON) && + (mouse_oldbuttonstate & MOUSE_RIGHTBUTTON)) + Key_Event (K_MOUSE2, false); + + if ((mouse_buttonstate & MOUSE_MIDDLEBUTTON) && + !(mouse_oldbuttonstate & MOUSE_MIDDLEBUTTON)) + Key_Event (K_MOUSE3, true); + else if (!(mouse_buttonstate & MOUSE_MIDDLEBUTTON) && + (mouse_oldbuttonstate & MOUSE_MIDDLEBUTTON)) + Key_Event (K_MOUSE3, false); + + mouse_oldbuttonstate = mouse_buttonstate; + } +} + +/* +=========== +IN_Move +=========== +*/ +void IN_MouseMove (usercmd_t *cmd) +{ + if (!UseMouse) + return; + + // poll mouse values + while (mouse_update()) + ; + + if (m_filter.value) + { + mouse_x = (mx + old_mouse_x) * 0.5; + mouse_y = (my + old_mouse_y) * 0.5; + } + else + { + mouse_x = mx; + mouse_y = my; + } + old_mouse_x = mx; + old_mouse_y = my; + mx = my = 0; // clear for next update + + mouse_x *= sensitivity.value; + mouse_y *= sensitivity.value; + +// add mouse X/Y movement to cmd + if ( (in_strafe.state & 1) || (lookstrafe.value && (in_mlook.state & 1) )) + cmd->sidemove += m_side.value * mouse_x; + else + cl.viewangles[YAW] -= m_yaw.value * mouse_x; + + if (in_mlook.state & 1) + V_StopPitchDrift (); + + if ( (in_mlook.state & 1) && !(in_strafe.state & 1)) + { + cl.viewangles[PITCH] += m_pitch.value * mouse_y; + if (cl.viewangles[PITCH] > 80) + cl.viewangles[PITCH] = 80; + if (cl.viewangles[PITCH] < -70) + cl.viewangles[PITCH] = -70; + } + else + { + cmd->forwardmove -= m_forward.value * mouse_y; + } +} + +void IN_Move (usercmd_t *cmd) +{ + IN_MouseMove(cmd); +} + + +/* +================ +VID_ModeInfo +================ +*/ +char *VID_ModeInfo (int modenum) +{ + static char *badmodestr = "Bad mode number"; + static char modestr[40]; + + if (modenum == 0) + { + sprintf (modestr, "%d x %d, %d bpp", + vid.width, vid.height, modes[current_mode].bytesperpixel*8); + return (modestr); + } + else + { + return (badmodestr); + } +} + +void VID_LockBuffer (void) +{ +} + +void VID_UnlockBuffer (void) +{ +} + + diff --git a/source/vid_wgl.c b/source/vid_wgl.c index 0bfda468..5cfdc823 100644 --- a/source/vid_wgl.c +++ b/source/vid_wgl.c @@ -1,1974 +1,1974 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// vid_wgl.c -- Windows 9x/NT OpenGL driver - -#include "quakedef.h" -#include "winquake.h" -#include "resource.h" -#include "keys.h" -#include "sound.h" -#include - -#define MAX_MODE_LIST 30 -#define VID_ROW_SIZE 3 -#define WARP_WIDTH 320 -#define WARP_HEIGHT 200 -#define MAXWIDTH 10000 -#define MAXHEIGHT 10000 -#define BASEWIDTH 320 -#define BASEHEIGHT 200 - -#define MODE_WINDOWED 0 -#define NO_MODE (MODE_WINDOWED - 1) -#define MODE_FULLSCREEN_DEFAULT (MODE_WINDOWED + 1) - -typedef struct { - modestate_t type; - int width; - int height; - int modenum; - int dib; - int fullscreen; - int bpp; - int halfscreen; - char modedesc[17]; -} vmode_t; - -typedef struct { - int width; - int height; -} lmode_t; - -lmode_t lowresmodes[] = { - {320, 200}, - {320, 240}, - {400, 300}, - {512, 384}, -}; - -const char *gl_vendor; -const char *gl_renderer; -const char *gl_version; -const char *gl_extensions; - -qboolean DDActive; -qboolean scr_skipupdate; - -static vmode_t modelist[MAX_MODE_LIST]; -static int nummodes; -static vmode_t *pcurrentmode; -static vmode_t badmode; - -static DEVMODE gdevmode; -static qboolean vid_initialized = false; -static qboolean windowed, leavecurrentmode; -static qboolean vid_canalttab = false; -static qboolean vid_wassuspended = false; -static int windowed_mouse; -extern qboolean mouseactive; // from in_win.c -static HICON hIcon; - -int DIBWidth, DIBHeight; -RECT WindowRect; -DWORD WindowStyle, ExWindowStyle; - -HWND mainwindow, dibwindow; - -int vid_modenum = NO_MODE; -int vid_realmode; -int vid_default = MODE_WINDOWED; -static int windowed_default; -unsigned char vid_curpal[256*3]; -static qboolean fullsbardraw = false; - -float vid_gamma = 1.0; - -HGLRC baseRC; -HDC maindc; - -glvert_t glv; - -cvar_t gl_ztrick = {"gl_ztrick","1"}; - -cvar_t vid_hwgammacontrol = {"vid_hwgammacontrol","1"}; -qboolean vid_gammaworks = false; -qboolean vid_hwgamma_enabled; -unsigned short *currentgammaramp = NULL; -void RestoreHWGamma (void); - -HWND WINAPI InitializeWindow (HINSTANCE hInstance, int nCmdShow); - - -unsigned short d_8to16table[256]; -unsigned d_8to24table[256]; -unsigned d_8to24table2[256]; -unsigned char d_15to8table[65536]; - -float gldepthmin, gldepthmax; - -modestate_t modestate = MS_UNINIT; - -void VID_MenuDraw (void); -void VID_MenuKey (int key); - -LONG WINAPI MainWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); -void AppActivate(BOOL fActive, BOOL minimize); -char *VID_GetModeDescription (int mode); -void ClearAllStates (void); -void VID_UpdateWindowStatus (void); -void GL_Init (void); - -PROC glArrayElementEXT; -PROC glColorPointerEXT; -PROC glTexCoordPointerEXT; -PROC glVertexPointerEXT; - -typedef void (APIENTRY *lp3DFXFUNC) (int, int, int, int, int, const void*); -lp3DFXFUNC glColorTableEXT; -qboolean is8bit = false; -qboolean isPermedia = false; -qboolean gl_mtexable = false; - -//==================================== - -cvar_t vid_mode = {"vid_mode","0"}; -// Note that 0 is MODE_WINDOWED -cvar_t _vid_default_mode = {"_vid_default_mode","0",CVAR_ARCHIVE}; -// Note that 3 is MODE_FULLSCREEN_DEFAULT -cvar_t _vid_default_mode_win = {"_vid_default_mode_win","3",CVAR_ARCHIVE}; -cvar_t vid_wait = {"vid_wait","0"}; -cvar_t vid_nopageflip = {"vid_nopageflip","0",CVAR_ARCHIVE}; -cvar_t _vid_wait_override = {"_vid_wait_override","0",CVAR_ARCHIVE}; -cvar_t vid_config_x = {"vid_config_x","800",CVAR_ARCHIVE}; -cvar_t vid_config_y = {"vid_config_y","600",CVAR_ARCHIVE}; -cvar_t vid_stretch_by_2 = {"vid_stretch_by_2","1",CVAR_ARCHIVE}; -cvar_t _windowed_mouse = {"_windowed_mouse","1",CVAR_ARCHIVE}; - -int window_center_x, window_center_y, window_x, window_y, window_width, window_height; -RECT window_rect; - -// direct draw software compatability stuff - -void VID_HandlePause (qboolean pause) -{ -} - -void VID_ForceLockState (int lk) -{ -} - -void VID_LockBuffer (void) -{ -} - -void VID_UnlockBuffer (void) -{ -} - -int VID_ForceUnlockedAndReturnState (void) -{ - return 0; -} - -void D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height) -{ -} - -void D_EndDirectRect (int x, int y, int width, int height) -{ -} - - -void CenterWindow(HWND hWndCenter, int width, int height, BOOL lefttopjustify) -{ -// RECT rect; - int CenterX, CenterY; - - CenterX = (GetSystemMetrics(SM_CXSCREEN) - width) / 2; - CenterY = (GetSystemMetrics(SM_CYSCREEN) - height) / 2; - if (CenterX > CenterY*2) - CenterX >>= 1; // dual screens - CenterX = (CenterX < 0) ? 0: CenterX; - CenterY = (CenterY < 0) ? 0: CenterY; - SetWindowPos (hWndCenter, NULL, CenterX, CenterY, 0, 0, - SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW | SWP_DRAWFRAME); -} - -qboolean VID_SetWindowedMode (int modenum) -{ - HDC hdc; - int lastmodestate, width, height; - RECT rect; - - lastmodestate = modestate; - - WindowRect.top = WindowRect.left = 0; - - WindowRect.right = modelist[modenum].width; - WindowRect.bottom = modelist[modenum].height; - - DIBWidth = modelist[modenum].width; - DIBHeight = modelist[modenum].height; - - WindowStyle = WS_OVERLAPPED | WS_BORDER | WS_CAPTION | WS_SYSMENU | - WS_MINIMIZEBOX; - ExWindowStyle = 0; - - rect = WindowRect; - AdjustWindowRectEx(&rect, WindowStyle, FALSE, 0); - - width = rect.right - rect.left; - height = rect.bottom - rect.top; - - // Create the DIB window - dibwindow = CreateWindowEx ( - ExWindowStyle, - "WinQuake", - "GLQuake", - WindowStyle, - rect.left, rect.top, - width, - height, - NULL, - NULL, - global_hInstance, - NULL); - - if (!dibwindow) - Sys_Error ("Couldn't create DIB window"); - - // Center and show the DIB window - CenterWindow(dibwindow, WindowRect.right - WindowRect.left, - WindowRect.bottom - WindowRect.top, false); - - ShowWindow (dibwindow, SW_SHOWDEFAULT); - UpdateWindow (dibwindow); - - modestate = MS_WINDOWED; - -// because we have set the background brush for the window to NULL -// (to avoid flickering when re-sizing the window on the desktop), -// we clear the window to black when created, otherwise it will be -// empty while Quake starts up. - hdc = GetDC(dibwindow); - PatBlt(hdc,0,0,WindowRect.right,WindowRect.bottom,BLACKNESS); - ReleaseDC(dibwindow, hdc); - - if (vid.conheight > modelist[modenum].height) - vid.conheight = modelist[modenum].height; - if (vid.conwidth > modelist[modenum].width) - vid.conwidth = modelist[modenum].width; - vid.width = vid.conwidth; - vid.height = vid.conheight; - - vid.numpages = 2; - - mainwindow = dibwindow; - - SendMessage (mainwindow, WM_SETICON, (WPARAM)TRUE, (LPARAM)hIcon); - SendMessage (mainwindow, WM_SETICON, (WPARAM)FALSE, (LPARAM)hIcon); - - return true; -} - - -qboolean VID_SetFullDIBMode (int modenum) -{ - HDC hdc; - int lastmodestate, width, height; - RECT rect; - - if (!leavecurrentmode) - { - gdevmode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; - gdevmode.dmBitsPerPel = modelist[modenum].bpp; - gdevmode.dmPelsWidth = modelist[modenum].width << - modelist[modenum].halfscreen; - gdevmode.dmPelsHeight = modelist[modenum].height; - gdevmode.dmSize = sizeof (gdevmode); - - if (ChangeDisplaySettings (&gdevmode, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) - Sys_Error ("Couldn't set fullscreen DIB mode"); - } - - lastmodestate = modestate; - modestate = MS_FULLDIB; - - WindowRect.top = WindowRect.left = 0; - - WindowRect.right = modelist[modenum].width; - WindowRect.bottom = modelist[modenum].height; - - DIBWidth = modelist[modenum].width; - DIBHeight = modelist[modenum].height; - - WindowStyle = WS_POPUP; - ExWindowStyle = 0; - - rect = WindowRect; - AdjustWindowRectEx(&rect, WindowStyle, FALSE, 0); - - width = rect.right - rect.left; - height = rect.bottom - rect.top; - - // Create the DIB window - dibwindow = CreateWindowEx ( - ExWindowStyle, - "WinQuake", - "GLQuake", - WindowStyle, - rect.left, rect.top, - width, - height, - NULL, - NULL, - global_hInstance, - NULL); - - if (!dibwindow) - Sys_Error ("Couldn't create DIB window"); - - ShowWindow (dibwindow, SW_SHOWDEFAULT); - UpdateWindow (dibwindow); - - // Because we have set the background brush for the window to NULL - // (to avoid flickering when re-sizing the window on the desktop), we - // clear the window to black when created, otherwise it will be - // empty while Quake starts up. - hdc = GetDC(dibwindow); - PatBlt(hdc,0,0,WindowRect.right,WindowRect.bottom,BLACKNESS); - ReleaseDC(dibwindow, hdc); - - if (vid.conheight > modelist[modenum].height) - vid.conheight = modelist[modenum].height; - if (vid.conwidth > modelist[modenum].width) - vid.conwidth = modelist[modenum].width; - vid.width = vid.conwidth; - vid.height = vid.conheight; - - vid.numpages = 2; - -// needed because we're not getting WM_MOVE messages fullscreen on NT - window_x = 0; - window_y = 0; - - mainwindow = dibwindow; - - SendMessage (mainwindow, WM_SETICON, (WPARAM)TRUE, (LPARAM)hIcon); - SendMessage (mainwindow, WM_SETICON, (WPARAM)FALSE, (LPARAM)hIcon); - - return true; -} - - -int VID_SetMode (int modenum, unsigned char *palette) -{ - int original_mode, temp; - qboolean stat; - MSG msg; -// HDC hdc; - - if ((windowed && (modenum != 0)) || - (!windowed && (modenum < 1)) || - (!windowed && (modenum >= nummodes))) - { - Sys_Error ("Bad video mode\n"); - } - -// so Con_Printfs don't mess us up by forcing vid and snd updates - temp = scr_disabled_for_loading; - scr_disabled_for_loading = true; - - CDAudio_Pause (); - - if (vid_modenum == NO_MODE) - original_mode = windowed_default; - else - original_mode = vid_modenum; - - // Set either the fullscreen or windowed mode - if (modelist[modenum].type == MS_WINDOWED) - { - if (_windowed_mouse.value && key_dest == key_game) - { - stat = VID_SetWindowedMode(modenum); - IN_ActivateMouse (); - IN_HideMouse (); - } - else - { - IN_DeactivateMouse (); - IN_ShowMouse (); - stat = VID_SetWindowedMode(modenum); - } - } - else if (modelist[modenum].type == MS_FULLDIB) - { - stat = VID_SetFullDIBMode(modenum); - IN_ActivateMouse (); - IN_HideMouse (); - } - else - { - Sys_Error ("VID_SetMode: Bad mode type in modelist"); - } - - window_width = DIBWidth; - window_height = DIBHeight; - VID_UpdateWindowStatus (); - - CDAudio_Resume (); - scr_disabled_for_loading = temp; - - if (!stat) - { - Sys_Error ("Couldn't set video mode"); - } - -// now we try to make sure we get the focus on the mode switch, because -// sometimes in some systems we don't. We grab the foreground, then -// finish setting up, pump all our messages, and sleep for a little while -// to let messages finish bouncing around the system, then we put -// ourselves at the top of the z order, then grab the foreground again, -// Who knows if it helps, but it probably doesn't hurt - SetForegroundWindow (mainwindow); -// VID_SetPalette (palette); - vid_modenum = modenum; - Cvar_SetValue (&vid_mode, (float)vid_modenum); - - while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) - { - TranslateMessage (&msg); - DispatchMessage (&msg); - } - - Sleep (100); - - SetWindowPos (mainwindow, HWND_TOP, 0, 0, 0, 0, - SWP_DRAWFRAME | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | - SWP_NOCOPYBITS); - - SetForegroundWindow (mainwindow); - -// fix the leftover Alt from any Alt-Tab or the like that switched us away - ClearAllStates (); - - if (!msg_suppress_1) - Con_SafePrintf ("Video mode %s initialized.\n", VID_GetModeDescription (vid_modenum)); - -// VID_SetPalette (palette); - - vid.recalc_refdef = 1; - - return true; -} - - -/* -================ -VID_UpdateWindowStatus -================ -*/ -void VID_UpdateWindowStatus (void) -{ - - window_rect.left = window_x; - window_rect.top = window_y; - window_rect.right = window_x + window_width; - window_rect.bottom = window_y + window_height; - window_center_x = (window_rect.left + window_rect.right) / 2; - window_center_y = (window_rect.top + window_rect.bottom) / 2; - - IN_UpdateClipCursor (); -} - - -//==================================== - -BINDTEXFUNCPTR bindTexFunc; - -#define TEXTURE_EXT_STRING "GL_EXT_texture_object" - - -void CheckTextureExtensions (void) -{ - char *tmp; - qboolean texture_ext; - HINSTANCE hInstGL; - - texture_ext = FALSE; - /* check for texture extension */ - tmp = (unsigned char *)glGetString(GL_EXTENSIONS); - while (*tmp) - { - if (strncmp((const char*)tmp, TEXTURE_EXT_STRING, strlen(TEXTURE_EXT_STRING)) == 0) - texture_ext = TRUE; - tmp++; - } - - if (!texture_ext || COM_CheckParm ("-gl11") ) - { - hInstGL = LoadLibrary("opengl32.dll"); - - if (hInstGL == NULL) - Sys_Error ("Couldn't load opengl32.dll\n"); - - bindTexFunc = (void *)GetProcAddress(hInstGL,"glBindTexture"); - - if (!bindTexFunc) - Sys_Error ("No texture objects!"); - return; - } - -/* load library and get procedure adresses for texture extension API */ - if ((bindTexFunc = (BINDTEXFUNCPTR) - wglGetProcAddress((LPCSTR) "glBindTextureEXT")) == NULL) - { - Sys_Error ("GetProcAddress for BindTextureEXT failed"); - return; - } -} - -void CheckArrayExtensions (void) -{ - char *tmp; - - /* check for texture extension */ - tmp = (unsigned char *)glGetString(GL_EXTENSIONS); - while (*tmp) - { - if (strncmp((const char*)tmp, "GL_EXT_vertex_array", strlen("GL_EXT_vertex_array")) == 0) - { - if ( -((glArrayElementEXT = wglGetProcAddress("glArrayElementEXT")) == NULL) || -((glColorPointerEXT = wglGetProcAddress("glColorPointerEXT")) == NULL) || -((glTexCoordPointerEXT = wglGetProcAddress("glTexCoordPointerEXT")) == NULL) || -((glVertexPointerEXT = wglGetProcAddress("glVertexPointerEXT")) == NULL) ) - { - Sys_Error ("GetProcAddress for vertex extension failed"); - return; - } - return; - } - tmp++; - } - - Sys_Error ("Vertex array extension not present"); -} - -//int texture_mode = GL_NEAREST; -//int texture_mode = GL_NEAREST_MIPMAP_NEAREST; -//int texture_mode = GL_NEAREST_MIPMAP_LINEAR; -int texture_mode = GL_LINEAR; -//int texture_mode = GL_LINEAR_MIPMAP_NEAREST; -//int texture_mode = GL_LINEAR_MIPMAP_LINEAR; - -int texture_extension_number = 1; - -void CheckMultiTextureExtensions (void) -{ - if (strstr(gl_extensions, "GL_ARB_multitexture ") && !COM_CheckParm("-nomtex")) { - qglMultiTexCoord2f = (void *) wglGetProcAddress("glMultiTexCoord2fARB"); - qglActiveTexture = (void *) wglGetProcAddress("glActiveTextureARB"); - if (!qglMultiTexCoord2f || !qglActiveTexture) - return; - Con_Printf("Multitexture extensions found.\n"); - gl_mtexable = true; - } -} - -/* -=============== -GL_Init -=============== -*/ -void GL_Init (void) -{ - gl_vendor = glGetString (GL_VENDOR); - Con_Printf ("GL_VENDOR: %s\n", gl_vendor); - gl_renderer = glGetString (GL_RENDERER); - Con_Printf ("GL_RENDERER: %s\n", gl_renderer); - - gl_version = glGetString (GL_VERSION); - Con_Printf ("GL_VERSION: %s\n", gl_version); - gl_extensions = glGetString (GL_EXTENSIONS); - Con_Printf ("GL_EXTENSIONS: %s\n", gl_extensions); - -// Con_Printf ("%s %s\n", gl_renderer, gl_version); - - if (strnicmp(gl_renderer,"PowerVR",7)==0) - fullsbardraw = true; - - if (strnicmp(gl_renderer,"Permedia",8)==0) - isPermedia = true; - - CheckTextureExtensions (); - CheckMultiTextureExtensions (); - - glClearColor (1,0,0,0); - glCullFace(GL_FRONT); - glEnable(GL_TEXTURE_2D); - - glEnable(GL_ALPHA_TEST); - glAlphaFunc(GL_GREATER, 0.666); - - glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); - glShadeModel (GL_FLAT); - - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - - glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - -// glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); - -#if 0 - CheckArrayExtensions (); - - glEnable (GL_VERTEX_ARRAY_EXT); - glEnable (GL_TEXTURE_COORD_ARRAY_EXT); - glVertexPointerEXT (3, GL_FLOAT, 0, 0, &glv.x); - glTexCoordPointerEXT (2, GL_FLOAT, 0, 0, &glv.s); - glColorPointerEXT (3, GL_FLOAT, 0, 0, &glv.r); -#endif -} - -/* -================= -GL_BeginRendering - -================= -*/ -void GL_BeginRendering (int *x, int *y, int *width, int *height) -{ - *x = *y = 0; - *width = WindowRect.right - WindowRect.left; - *height = WindowRect.bottom - WindowRect.top; -} - - -void GL_EndRendering (void) -{ - static qboolean old_hwgamma_enabled; - - vid_hwgamma_enabled = vid_hwgammacontrol.value && vid_gammaworks - && ActiveApp && !Minimized && modestate == MS_FULLDIB; - if (vid_hwgamma_enabled != old_hwgamma_enabled) { - old_hwgamma_enabled = vid_hwgamma_enabled; - if (vid_hwgamma_enabled && currentgammaramp) - VID_SetDeviceGammaRamp (currentgammaramp); - else - RestoreHWGamma (); - } - - if (!scr_skipupdate || block_drawing) - SwapBuffers(maindc); - -// handle the mouse state when windowed if that's changed - if (modestate == MS_WINDOWED) - { - if (!_windowed_mouse.value) { - if (windowed_mouse) { - IN_DeactivateMouse (); - IN_ShowMouse (); - windowed_mouse = false; - } - } else { - windowed_mouse = true; - if (key_dest == key_game && !mouseactive && ActiveApp) { - IN_ActivateMouse (); - IN_HideMouse (); - } else if (mouseactive && key_dest != key_game) { - IN_DeactivateMouse (); - IN_ShowMouse (); - } - } - } - if (fullsbardraw) - Sbar_Changed(); -} - -void VID_SetPalette (unsigned char *palette) -{ - byte *pal; - unsigned r,g,b; - unsigned v; - int r1,g1,b1; - int j,k,l; - unsigned short i; - unsigned *table; -// FILE *f; -// char s[255]; -// HWND hDlg, hProgress; - -// -// 8 8 8 encoding -// - pal = palette; - table = d_8to24table; - for (i=0 ; i<256 ; i++) - { - r = pal[0]; - g = pal[1]; - b = pal[2]; - pal += 3; - -// v = (255<<24) + (r<<16) + (g<<8) + (b<<0); -// v = (255<<0) + (r<<8) + (g<<16) + (b<<24); - v = (255<<24) + (r<<0) + (g<<8) + (b<<16); - *table++ = v; - } - d_8to24table[255] = 0; // 255 is transparent - -// Tonik: create a brighter palette for bmodel textures - pal = palette; - table = d_8to24table2; - for (i=0 ; i<256 ; i++) - { - r = pal[0] * (2.0/1.5); if (r > 255) r = 255; - g = pal[1] * (2.0/1.5); if (g > 255) g = 255; - b = pal[2] * (2.0/1.5); if (b > 255) b = 255; - pal += 3; - *table++ = (255<<24) + (r<<0) + (g<<8) + (b<<16); - } - d_8to24table2[255] = 0; // 255 is transparent - - // JACK: 3D distance calcs - k is last closest, l is the distance. - // FIXME: Precalculate this and cache to disk. - for (i=0; i < (1<<15); i++) { - /* Maps - 000000000000000 - 000000000011111 = Red = 0x1F - 000001111100000 = Blue = 0x03E0 - 111110000000000 = Grn = 0x7C00 - */ - r = ((i & 0x1F) << 3)+4; - g = ((i & 0x03E0) >> 2)+4; - b = ((i & 0x7C00) >> 7)+4; - pal = (unsigned char *)d_8to24table; - for (v=0,k=0,l=10000*10000; v<256; v++,pal+=4) { - r1 = r-pal[0]; - g1 = g-pal[1]; - b1 = b-pal[2]; - j = (r1*r1)+(g1*g1)+(b1*b1); - if (j 0) { - Key_Event(K_MWHEELUP, true); - Key_Event(K_MWHEELUP, false); - } else { - Key_Event(K_MWHEELDOWN, true); - Key_Event(K_MWHEELDOWN, false); - } - break; - - case WM_SIZE: - break; - - case WM_CLOSE: - if (MessageBox (mainwindow, "Are you sure you want to quit?", "Confirm Exit", - MB_YESNO | MB_SETFOREGROUND | MB_ICONQUESTION) == IDYES) - { - Sys_Quit (); - } - - break; - - case WM_ACTIVATE: - fActive = LOWORD(wParam); - fMinimized = (BOOL) HIWORD(wParam); - AppActivate(!(fActive == WA_INACTIVE), fMinimized); - - // fix the leftover Alt from any Alt-Tab or the like that switched us away - ClearAllStates (); - - break; - - case WM_DESTROY: - { - if (dibwindow) - DestroyWindow (dibwindow); - - PostQuitMessage (0); - } - break; - - case MM_MCINOTIFY: - lRet = CDAudio_MessageHandler (hWnd, uMsg, wParam, lParam); - break; - - default: - /* pass all unhandled messages to DefWindowProc */ - lRet = DefWindowProc (hWnd, uMsg, wParam, lParam); - break; - } - - /* return 1 if handled message, 0 if not */ - return lRet; -} - - -/* -================= -VID_NumModes -================= -*/ -int VID_NumModes (void) -{ - return nummodes; -} - - -/* -================= -VID_GetModePtr -================= -*/ -vmode_t *VID_GetModePtr (int modenum) -{ - - if ((modenum >= 0) && (modenum < nummodes)) - return &modelist[modenum]; - else - return &badmode; -} - - -/* -================= -VID_GetModeDescription -================= -*/ -char *VID_GetModeDescription (int mode) -{ - char *pinfo; - vmode_t *pv; - static char temp[100]; - - if ((mode < 0) || (mode >= nummodes)) - return NULL; - - if (!leavecurrentmode) - { - pv = VID_GetModePtr (mode); - pinfo = pv->modedesc; - } - else - { - sprintf (temp, "Desktop resolution (%dx%d)", - modelist[MODE_FULLSCREEN_DEFAULT].width, - modelist[MODE_FULLSCREEN_DEFAULT].height); - pinfo = temp; - } - - return pinfo; -} - - -// KJB: Added this to return the mode driver name in description for console - -char *VID_GetExtModeDescription (int mode) -{ - static char pinfo[40]; - vmode_t *pv; - - if ((mode < 0) || (mode >= nummodes)) - return NULL; - - pv = VID_GetModePtr (mode); - if (modelist[mode].type == MS_FULLDIB) - { - if (!leavecurrentmode) - { - sprintf(pinfo,"%s fullscreen", pv->modedesc); - } - else - { - sprintf (pinfo, "Desktop resolution (%dx%d)", - modelist[MODE_FULLSCREEN_DEFAULT].width, - modelist[MODE_FULLSCREEN_DEFAULT].height); - } - } - else - { - if (modestate == MS_WINDOWED) - sprintf(pinfo, "%s windowed", pv->modedesc); - else - sprintf(pinfo, "windowed"); - } - - return pinfo; -} - - -/* -================= -VID_DescribeCurrentMode_f -================= -*/ -void VID_DescribeCurrentMode_f (void) -{ - Con_Printf ("%s\n", VID_GetExtModeDescription (vid_modenum)); -} - - -/* -================= -VID_NumModes_f -================= -*/ -void VID_NumModes_f (void) -{ - - if (nummodes == 1) - Con_Printf ("%d video mode is available\n", nummodes); - else - Con_Printf ("%d video modes are available\n", nummodes); -} - - -/* -================= -VID_DescribeMode_f -================= -*/ -void VID_DescribeMode_f (void) -{ - int t, modenum; - - modenum = Q_atoi (Cmd_Argv(1)); - - t = leavecurrentmode; - leavecurrentmode = 0; - - Con_Printf ("%s\n", VID_GetExtModeDescription (modenum)); - - leavecurrentmode = t; -} - - -/* -================= -VID_DescribeModes_f -================= -*/ -void VID_DescribeModes_f (void) -{ - int i, lnummodes, t; - char *pinfo; - vmode_t *pv; - - lnummodes = VID_NumModes (); - - t = leavecurrentmode; - leavecurrentmode = 0; - - for (i=1 ; i8 bpp modes - originalnummodes = nummodes; - modenum = 0; - - do - { - stat = EnumDisplaySettings (NULL, modenum, &devmode); - - if ((devmode.dmBitsPerPel >= 15) && - (devmode.dmPelsWidth <= MAXWIDTH) && - (devmode.dmPelsHeight <= MAXHEIGHT) && - (nummodes < MAX_MODE_LIST)) - { - devmode.dmFields = DM_BITSPERPEL | - DM_PELSWIDTH | - DM_PELSHEIGHT; - - if (ChangeDisplaySettings (&devmode, CDS_TEST | CDS_FULLSCREEN) == - DISP_CHANGE_SUCCESSFUL) - { - modelist[nummodes].type = MS_FULLDIB; - modelist[nummodes].width = devmode.dmPelsWidth; - modelist[nummodes].height = devmode.dmPelsHeight; - modelist[nummodes].modenum = 0; - modelist[nummodes].halfscreen = 0; - modelist[nummodes].dib = 1; - modelist[nummodes].fullscreen = 1; - modelist[nummodes].bpp = devmode.dmBitsPerPel; - sprintf (modelist[nummodes].modedesc, "%dx%dx%d", - devmode.dmPelsWidth, devmode.dmPelsHeight, - devmode.dmBitsPerPel); - - // if the width is more than twice the height, reduce it by half because this - // is probably a dual-screen monitor - if (!COM_CheckParm("-noadjustaspect")) - { - if (modelist[nummodes].width > (modelist[nummodes].height << 1)) - { - modelist[nummodes].width >>= 1; - modelist[nummodes].halfscreen = 1; - sprintf (modelist[nummodes].modedesc, "%dx%dx%d", - modelist[nummodes].width, - modelist[nummodes].height, - modelist[nummodes].bpp); - } - } - - for (i=originalnummodes, existingmode = 0 ; i 3) - vid_gamma = 3; - - Cvar_SetValue (&gl_gamma, vid_gamma); - - for (i=0 ; i<768 ; i++) - { - f = pow ( (pal[i]+1)/256.0 , vid_gamma ); - inf = f*255 + 0.5; - if (inf < 0) - inf = 0; - if (inf > 255) - inf = 255; - palette[i] = inf; - } - - memcpy (pal, palette, sizeof(palette)); -} - -/* -=================== -VID_Init -=================== -*/ -void VID_Init (unsigned char *palette) -{ - int i, existingmode; - int basenummodes, width, height, bpp, findbpp, done; - char gldir[MAX_OSPATH]; - HDC hdc; - DEVMODE devmode; - - memset(&devmode, 0, sizeof(devmode)); - - Cvar_RegisterVariable (&vid_mode); - Cvar_RegisterVariable (&vid_wait); - Cvar_RegisterVariable (&vid_nopageflip); - Cvar_RegisterVariable (&_vid_wait_override); - Cvar_RegisterVariable (&_vid_default_mode); - Cvar_RegisterVariable (&_vid_default_mode_win); - Cvar_RegisterVariable (&vid_config_x); - Cvar_RegisterVariable (&vid_config_y); - Cvar_RegisterVariable (&vid_stretch_by_2); - Cvar_RegisterVariable (&_windowed_mouse); - Cvar_RegisterVariable (&gl_ztrick); - Cvar_RegisterVariable (&vid_hwgammacontrol); - - Cmd_AddCommand ("vid_nummodes", VID_NumModes_f); - Cmd_AddCommand ("vid_describecurrentmode", VID_DescribeCurrentMode_f); - Cmd_AddCommand ("vid_describemode", VID_DescribeMode_f); - Cmd_AddCommand ("vid_describemodes", VID_DescribeModes_f); - - hIcon = LoadIcon (global_hInstance, MAKEINTRESOURCE (IDI_ICON2)); - - InitCommonControls(); - - VID_InitDIB (global_hInstance); - basenummodes = nummodes = 1; - - VID_InitFullDIB (global_hInstance); - - if (COM_CheckParm("-window")) - { - hdc = GetDC (NULL); - - if (GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) - { - Sys_Error ("Can't run in non-RGB mode"); - } - - ReleaseDC (NULL, hdc); - - windowed = true; - - vid_default = MODE_WINDOWED; - } - else - { - if (nummodes == 1) - Sys_Error ("No RGB fullscreen modes available"); - - windowed = false; - - if (COM_CheckParm("-mode")) - { - vid_default = Q_atoi(com_argv[COM_CheckParm("-mode")+1]); - } - else - { - if (COM_CheckParm("-current")) - { - modelist[MODE_FULLSCREEN_DEFAULT].width = - GetSystemMetrics (SM_CXSCREEN); - modelist[MODE_FULLSCREEN_DEFAULT].height = - GetSystemMetrics (SM_CYSCREEN); - vid_default = MODE_FULLSCREEN_DEFAULT; - leavecurrentmode = 1; - } - else - { - if (COM_CheckParm("-width")) - { - width = Q_atoi(com_argv[COM_CheckParm("-width")+1]); - } - else - { - width = 640; - } - - if (COM_CheckParm("-bpp")) - { - bpp = Q_atoi(com_argv[COM_CheckParm("-bpp")+1]); - findbpp = 0; - } - else - { - bpp = 15; - findbpp = 1; - } - - if (COM_CheckParm("-height")) - height = Q_atoi(com_argv[COM_CheckParm("-height")+1]); - - // if they want to force it, add the specified mode to the list - if (COM_CheckParm("-force") && (nummodes < MAX_MODE_LIST)) - { - modelist[nummodes].type = MS_FULLDIB; - modelist[nummodes].width = width; - modelist[nummodes].height = height; - modelist[nummodes].modenum = 0; - modelist[nummodes].halfscreen = 0; - modelist[nummodes].dib = 1; - modelist[nummodes].fullscreen = 1; - modelist[nummodes].bpp = bpp; - sprintf (modelist[nummodes].modedesc, "%dx%dx%d", - devmode.dmPelsWidth, devmode.dmPelsHeight, - devmode.dmBitsPerPel); - - for (i=nummodes, existingmode = 0 ; iwidth)/2, 4, p); - - vid_wmodes = 0; - lnummodes = VID_NumModes (); - - for (i=1 ; (i 0) - { - M_Print (2*8, 36+0*8, "Fullscreen Modes (WIDTHxHEIGHTxBPP)"); - - column = 8; - row = 36+2*8; - - for (i=0 ; i"); - M_Print (3*8, 36 + MODE_AREA_HEIGHT * 8 + 8*4, - "and -bpp "); - M_Print (3*8, 36 + MODE_AREA_HEIGHT * 8 + 8*6, - "Select windowed mode with -window"); -} - - -/* -================ -VID_MenuKey -================ -*/ -void VID_MenuKey (int key) -{ - switch (key) - { - case K_ESCAPE: - S_LocalSound ("misc/menu1.wav"); - M_Menu_Options_f (); - break; - - default: - break; - } -} +/* +Copyright (C) 1996-1997 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. + +*/ +// vid_wgl.c -- Windows 9x/NT OpenGL driver + +#include "quakedef.h" +#include "winquake.h" +#include "resource.h" +#include "keys.h" +#include "sound.h" +#include + +#define MAX_MODE_LIST 30 +#define VID_ROW_SIZE 3 +#define WARP_WIDTH 320 +#define WARP_HEIGHT 200 +#define MAXWIDTH 10000 +#define MAXHEIGHT 10000 +#define BASEWIDTH 320 +#define BASEHEIGHT 200 + +#define MODE_WINDOWED 0 +#define NO_MODE (MODE_WINDOWED - 1) +#define MODE_FULLSCREEN_DEFAULT (MODE_WINDOWED + 1) + +typedef struct { + modestate_t type; + int width; + int height; + int modenum; + int dib; + int fullscreen; + int bpp; + int halfscreen; + char modedesc[17]; +} vmode_t; + +typedef struct { + int width; + int height; +} lmode_t; + +lmode_t lowresmodes[] = { + {320, 200}, + {320, 240}, + {400, 300}, + {512, 384}, +}; + +const char *gl_vendor; +const char *gl_renderer; +const char *gl_version; +const char *gl_extensions; + +qboolean DDActive; +qboolean scr_skipupdate; + +static vmode_t modelist[MAX_MODE_LIST]; +static int nummodes; +static vmode_t *pcurrentmode; +static vmode_t badmode; + +static DEVMODE gdevmode; +static qboolean vid_initialized = false; +static qboolean windowed, leavecurrentmode; +static qboolean vid_canalttab = false; +static qboolean vid_wassuspended = false; +static int windowed_mouse; +extern qboolean mouseactive; // from in_win.c +static HICON hIcon; + +int DIBWidth, DIBHeight; +RECT WindowRect; +DWORD WindowStyle, ExWindowStyle; + +HWND mainwindow, dibwindow; + +int vid_modenum = NO_MODE; +int vid_realmode; +int vid_default = MODE_WINDOWED; +static int windowed_default; +unsigned char vid_curpal[256*3]; +static qboolean fullsbardraw = false; + +float vid_gamma = 1.0; + +HGLRC baseRC; +HDC maindc; + +glvert_t glv; + +cvar_t gl_ztrick = {"gl_ztrick","1"}; + +cvar_t vid_hwgammacontrol = {"vid_hwgammacontrol","1"}; +qboolean vid_gammaworks = false; +qboolean vid_hwgamma_enabled; +unsigned short *currentgammaramp = NULL; +void RestoreHWGamma (void); + +HWND WINAPI InitializeWindow (HINSTANCE hInstance, int nCmdShow); + + +unsigned short d_8to16table[256]; +unsigned d_8to24table[256]; +unsigned d_8to24table2[256]; +unsigned char d_15to8table[65536]; + +float gldepthmin, gldepthmax; + +modestate_t modestate = MS_UNINIT; + +void VID_MenuDraw (void); +void VID_MenuKey (int key); + +LONG WINAPI MainWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); +void AppActivate(BOOL fActive, BOOL minimize); +char *VID_GetModeDescription (int mode); +void ClearAllStates (void); +void VID_UpdateWindowStatus (void); +void GL_Init (void); + +PROC glArrayElementEXT; +PROC glColorPointerEXT; +PROC glTexCoordPointerEXT; +PROC glVertexPointerEXT; + +typedef void (APIENTRY *lp3DFXFUNC) (int, int, int, int, int, const void*); +lp3DFXFUNC glColorTableEXT; +qboolean is8bit = false; +qboolean isPermedia = false; +qboolean gl_mtexable = false; + +//==================================== + +cvar_t vid_mode = {"vid_mode","0"}; +// Note that 0 is MODE_WINDOWED +cvar_t _vid_default_mode = {"_vid_default_mode","0",CVAR_ARCHIVE}; +// Note that 3 is MODE_FULLSCREEN_DEFAULT +cvar_t _vid_default_mode_win = {"_vid_default_mode_win","3",CVAR_ARCHIVE}; +cvar_t vid_wait = {"vid_wait","0"}; +cvar_t vid_nopageflip = {"vid_nopageflip","0",CVAR_ARCHIVE}; +cvar_t _vid_wait_override = {"_vid_wait_override","0",CVAR_ARCHIVE}; +cvar_t vid_config_x = {"vid_config_x","800",CVAR_ARCHIVE}; +cvar_t vid_config_y = {"vid_config_y","600",CVAR_ARCHIVE}; +cvar_t vid_stretch_by_2 = {"vid_stretch_by_2","1",CVAR_ARCHIVE}; +cvar_t _windowed_mouse = {"_windowed_mouse","1",CVAR_ARCHIVE}; + +int window_center_x, window_center_y, window_x, window_y, window_width, window_height; +RECT window_rect; + +// direct draw software compatability stuff + +void VID_HandlePause (qboolean pause) +{ +} + +void VID_ForceLockState (int lk) +{ +} + +void VID_LockBuffer (void) +{ +} + +void VID_UnlockBuffer (void) +{ +} + +int VID_ForceUnlockedAndReturnState (void) +{ + return 0; +} + +void D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height) +{ +} + +void D_EndDirectRect (int x, int y, int width, int height) +{ +} + + +void CenterWindow(HWND hWndCenter, int width, int height, BOOL lefttopjustify) +{ +// RECT rect; + int CenterX, CenterY; + + CenterX = (GetSystemMetrics(SM_CXSCREEN) - width) / 2; + CenterY = (GetSystemMetrics(SM_CYSCREEN) - height) / 2; + if (CenterX > CenterY*2) + CenterX >>= 1; // dual screens + CenterX = (CenterX < 0) ? 0: CenterX; + CenterY = (CenterY < 0) ? 0: CenterY; + SetWindowPos (hWndCenter, NULL, CenterX, CenterY, 0, 0, + SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW | SWP_DRAWFRAME); +} + +qboolean VID_SetWindowedMode (int modenum) +{ + HDC hdc; + int lastmodestate, width, height; + RECT rect; + + lastmodestate = modestate; + + WindowRect.top = WindowRect.left = 0; + + WindowRect.right = modelist[modenum].width; + WindowRect.bottom = modelist[modenum].height; + + DIBWidth = modelist[modenum].width; + DIBHeight = modelist[modenum].height; + + WindowStyle = WS_OVERLAPPED | WS_BORDER | WS_CAPTION | WS_SYSMENU | + WS_MINIMIZEBOX; + ExWindowStyle = 0; + + rect = WindowRect; + AdjustWindowRectEx(&rect, WindowStyle, FALSE, 0); + + width = rect.right - rect.left; + height = rect.bottom - rect.top; + + // Create the DIB window + dibwindow = CreateWindowEx ( + ExWindowStyle, + "WinQuake", + "GLQuake", + WindowStyle, + rect.left, rect.top, + width, + height, + NULL, + NULL, + global_hInstance, + NULL); + + if (!dibwindow) + Sys_Error ("Couldn't create DIB window"); + + // Center and show the DIB window + CenterWindow(dibwindow, WindowRect.right - WindowRect.left, + WindowRect.bottom - WindowRect.top, false); + + ShowWindow (dibwindow, SW_SHOWDEFAULT); + UpdateWindow (dibwindow); + + modestate = MS_WINDOWED; + +// because we have set the background brush for the window to NULL +// (to avoid flickering when re-sizing the window on the desktop), +// we clear the window to black when created, otherwise it will be +// empty while Quake starts up. + hdc = GetDC(dibwindow); + PatBlt(hdc,0,0,WindowRect.right,WindowRect.bottom,BLACKNESS); + ReleaseDC(dibwindow, hdc); + + if (vid.conheight > modelist[modenum].height) + vid.conheight = modelist[modenum].height; + if (vid.conwidth > modelist[modenum].width) + vid.conwidth = modelist[modenum].width; + vid.width = vid.conwidth; + vid.height = vid.conheight; + + vid.numpages = 2; + + mainwindow = dibwindow; + + SendMessage (mainwindow, WM_SETICON, (WPARAM)TRUE, (LPARAM)hIcon); + SendMessage (mainwindow, WM_SETICON, (WPARAM)FALSE, (LPARAM)hIcon); + + return true; +} + + +qboolean VID_SetFullDIBMode (int modenum) +{ + HDC hdc; + int lastmodestate, width, height; + RECT rect; + + if (!leavecurrentmode) + { + gdevmode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; + gdevmode.dmBitsPerPel = modelist[modenum].bpp; + gdevmode.dmPelsWidth = modelist[modenum].width << + modelist[modenum].halfscreen; + gdevmode.dmPelsHeight = modelist[modenum].height; + gdevmode.dmSize = sizeof (gdevmode); + + if (ChangeDisplaySettings (&gdevmode, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) + Sys_Error ("Couldn't set fullscreen DIB mode"); + } + + lastmodestate = modestate; + modestate = MS_FULLDIB; + + WindowRect.top = WindowRect.left = 0; + + WindowRect.right = modelist[modenum].width; + WindowRect.bottom = modelist[modenum].height; + + DIBWidth = modelist[modenum].width; + DIBHeight = modelist[modenum].height; + + WindowStyle = WS_POPUP; + ExWindowStyle = 0; + + rect = WindowRect; + AdjustWindowRectEx(&rect, WindowStyle, FALSE, 0); + + width = rect.right - rect.left; + height = rect.bottom - rect.top; + + // Create the DIB window + dibwindow = CreateWindowEx ( + ExWindowStyle, + "WinQuake", + "GLQuake", + WindowStyle, + rect.left, rect.top, + width, + height, + NULL, + NULL, + global_hInstance, + NULL); + + if (!dibwindow) + Sys_Error ("Couldn't create DIB window"); + + ShowWindow (dibwindow, SW_SHOWDEFAULT); + UpdateWindow (dibwindow); + + // Because we have set the background brush for the window to NULL + // (to avoid flickering when re-sizing the window on the desktop), we + // clear the window to black when created, otherwise it will be + // empty while Quake starts up. + hdc = GetDC(dibwindow); + PatBlt(hdc,0,0,WindowRect.right,WindowRect.bottom,BLACKNESS); + ReleaseDC(dibwindow, hdc); + + if (vid.conheight > modelist[modenum].height) + vid.conheight = modelist[modenum].height; + if (vid.conwidth > modelist[modenum].width) + vid.conwidth = modelist[modenum].width; + vid.width = vid.conwidth; + vid.height = vid.conheight; + + vid.numpages = 2; + +// needed because we're not getting WM_MOVE messages fullscreen on NT + window_x = 0; + window_y = 0; + + mainwindow = dibwindow; + + SendMessage (mainwindow, WM_SETICON, (WPARAM)TRUE, (LPARAM)hIcon); + SendMessage (mainwindow, WM_SETICON, (WPARAM)FALSE, (LPARAM)hIcon); + + return true; +} + + +int VID_SetMode (int modenum, unsigned char *palette) +{ + int original_mode, temp; + qboolean stat; + MSG msg; +// HDC hdc; + + if ((windowed && (modenum != 0)) || + (!windowed && (modenum < 1)) || + (!windowed && (modenum >= nummodes))) + { + Sys_Error ("Bad video mode\n"); + } + +// so Con_Printfs don't mess us up by forcing vid and snd updates + temp = scr_disabled_for_loading; + scr_disabled_for_loading = true; + + CDAudio_Pause (); + + if (vid_modenum == NO_MODE) + original_mode = windowed_default; + else + original_mode = vid_modenum; + + // Set either the fullscreen or windowed mode + if (modelist[modenum].type == MS_WINDOWED) + { + if (_windowed_mouse.value && key_dest == key_game) + { + stat = VID_SetWindowedMode(modenum); + IN_ActivateMouse (); + IN_HideMouse (); + } + else + { + IN_DeactivateMouse (); + IN_ShowMouse (); + stat = VID_SetWindowedMode(modenum); + } + } + else if (modelist[modenum].type == MS_FULLDIB) + { + stat = VID_SetFullDIBMode(modenum); + IN_ActivateMouse (); + IN_HideMouse (); + } + else + { + Sys_Error ("VID_SetMode: Bad mode type in modelist"); + } + + window_width = DIBWidth; + window_height = DIBHeight; + VID_UpdateWindowStatus (); + + CDAudio_Resume (); + scr_disabled_for_loading = temp; + + if (!stat) + { + Sys_Error ("Couldn't set video mode"); + } + +// now we try to make sure we get the focus on the mode switch, because +// sometimes in some systems we don't. We grab the foreground, then +// finish setting up, pump all our messages, and sleep for a little while +// to let messages finish bouncing around the system, then we put +// ourselves at the top of the z order, then grab the foreground again, +// Who knows if it helps, but it probably doesn't hurt + SetForegroundWindow (mainwindow); +// VID_SetPalette (palette); + vid_modenum = modenum; + Cvar_SetValue (&vid_mode, (float)vid_modenum); + + while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) + { + TranslateMessage (&msg); + DispatchMessage (&msg); + } + + Sleep (100); + + SetWindowPos (mainwindow, HWND_TOP, 0, 0, 0, 0, + SWP_DRAWFRAME | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | + SWP_NOCOPYBITS); + + SetForegroundWindow (mainwindow); + +// fix the leftover Alt from any Alt-Tab or the like that switched us away + ClearAllStates (); + + if (!msg_suppress_1) + Con_SafePrintf ("Video mode %s initialized.\n", VID_GetModeDescription (vid_modenum)); + +// VID_SetPalette (palette); + + vid.recalc_refdef = 1; + + return true; +} + + +/* +================ +VID_UpdateWindowStatus +================ +*/ +void VID_UpdateWindowStatus (void) +{ + + window_rect.left = window_x; + window_rect.top = window_y; + window_rect.right = window_x + window_width; + window_rect.bottom = window_y + window_height; + window_center_x = (window_rect.left + window_rect.right) / 2; + window_center_y = (window_rect.top + window_rect.bottom) / 2; + + IN_UpdateClipCursor (); +} + + +//==================================== + +BINDTEXFUNCPTR bindTexFunc; + +#define TEXTURE_EXT_STRING "GL_EXT_texture_object" + + +void CheckTextureExtensions (void) +{ + char *tmp; + qboolean texture_ext; + HINSTANCE hInstGL; + + texture_ext = FALSE; + /* check for texture extension */ + tmp = (unsigned char *)glGetString(GL_EXTENSIONS); + while (*tmp) + { + if (strncmp((const char*)tmp, TEXTURE_EXT_STRING, strlen(TEXTURE_EXT_STRING)) == 0) + texture_ext = TRUE; + tmp++; + } + + if (!texture_ext || COM_CheckParm ("-gl11") ) + { + hInstGL = LoadLibrary("opengl32.dll"); + + if (hInstGL == NULL) + Sys_Error ("Couldn't load opengl32.dll\n"); + + bindTexFunc = (void *)GetProcAddress(hInstGL,"glBindTexture"); + + if (!bindTexFunc) + Sys_Error ("No texture objects!"); + return; + } + +/* load library and get procedure adresses for texture extension API */ + if ((bindTexFunc = (BINDTEXFUNCPTR) + wglGetProcAddress((LPCSTR) "glBindTextureEXT")) == NULL) + { + Sys_Error ("GetProcAddress for BindTextureEXT failed"); + return; + } +} + +void CheckArrayExtensions (void) +{ + char *tmp; + + /* check for texture extension */ + tmp = (unsigned char *)glGetString(GL_EXTENSIONS); + while (*tmp) + { + if (strncmp((const char*)tmp, "GL_EXT_vertex_array", strlen("GL_EXT_vertex_array")) == 0) + { + if ( +((glArrayElementEXT = wglGetProcAddress("glArrayElementEXT")) == NULL) || +((glColorPointerEXT = wglGetProcAddress("glColorPointerEXT")) == NULL) || +((glTexCoordPointerEXT = wglGetProcAddress("glTexCoordPointerEXT")) == NULL) || +((glVertexPointerEXT = wglGetProcAddress("glVertexPointerEXT")) == NULL) ) + { + Sys_Error ("GetProcAddress for vertex extension failed"); + return; + } + return; + } + tmp++; + } + + Sys_Error ("Vertex array extension not present"); +} + +//int texture_mode = GL_NEAREST; +//int texture_mode = GL_NEAREST_MIPMAP_NEAREST; +//int texture_mode = GL_NEAREST_MIPMAP_LINEAR; +int texture_mode = GL_LINEAR; +//int texture_mode = GL_LINEAR_MIPMAP_NEAREST; +//int texture_mode = GL_LINEAR_MIPMAP_LINEAR; + +int texture_extension_number = 1; + +void CheckMultiTextureExtensions (void) +{ + if (strstr(gl_extensions, "GL_ARB_multitexture ") && !COM_CheckParm("-nomtex")) { + qglMultiTexCoord2f = (void *) wglGetProcAddress("glMultiTexCoord2fARB"); + qglActiveTexture = (void *) wglGetProcAddress("glActiveTextureARB"); + if (!qglMultiTexCoord2f || !qglActiveTexture) + return; + Con_Printf("Multitexture extensions found.\n"); + gl_mtexable = true; + } +} + +/* +=============== +GL_Init +=============== +*/ +void GL_Init (void) +{ + gl_vendor = glGetString (GL_VENDOR); + Con_Printf ("GL_VENDOR: %s\n", gl_vendor); + gl_renderer = glGetString (GL_RENDERER); + Con_Printf ("GL_RENDERER: %s\n", gl_renderer); + + gl_version = glGetString (GL_VERSION); + Con_Printf ("GL_VERSION: %s\n", gl_version); + gl_extensions = glGetString (GL_EXTENSIONS); + Con_Printf ("GL_EXTENSIONS: %s\n", gl_extensions); + +// Con_Printf ("%s %s\n", gl_renderer, gl_version); + + if (strnicmp(gl_renderer,"PowerVR",7)==0) + fullsbardraw = true; + + if (strnicmp(gl_renderer,"Permedia",8)==0) + isPermedia = true; + + CheckTextureExtensions (); + CheckMultiTextureExtensions (); + + glClearColor (1,0,0,0); + glCullFace(GL_FRONT); + glEnable(GL_TEXTURE_2D); + + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_GREATER, 0.666); + + glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); + glShadeModel (GL_FLAT); + + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + +// glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + +#if 0 + CheckArrayExtensions (); + + glEnable (GL_VERTEX_ARRAY_EXT); + glEnable (GL_TEXTURE_COORD_ARRAY_EXT); + glVertexPointerEXT (3, GL_FLOAT, 0, 0, &glv.x); + glTexCoordPointerEXT (2, GL_FLOAT, 0, 0, &glv.s); + glColorPointerEXT (3, GL_FLOAT, 0, 0, &glv.r); +#endif +} + +/* +================= +GL_BeginRendering + +================= +*/ +void GL_BeginRendering (int *x, int *y, int *width, int *height) +{ + *x = *y = 0; + *width = WindowRect.right - WindowRect.left; + *height = WindowRect.bottom - WindowRect.top; +} + + +void GL_EndRendering (void) +{ + static qboolean old_hwgamma_enabled; + + vid_hwgamma_enabled = vid_hwgammacontrol.value && vid_gammaworks + && ActiveApp && !Minimized && modestate == MS_FULLDIB; + if (vid_hwgamma_enabled != old_hwgamma_enabled) { + old_hwgamma_enabled = vid_hwgamma_enabled; + if (vid_hwgamma_enabled && currentgammaramp) + VID_SetDeviceGammaRamp (currentgammaramp); + else + RestoreHWGamma (); + } + + if (!scr_skipupdate || block_drawing) + SwapBuffers(maindc); + +// handle the mouse state when windowed if that's changed + if (modestate == MS_WINDOWED) + { + if (!_windowed_mouse.value) { + if (windowed_mouse) { + IN_DeactivateMouse (); + IN_ShowMouse (); + windowed_mouse = false; + } + } else { + windowed_mouse = true; + if (key_dest == key_game && !mouseactive && ActiveApp) { + IN_ActivateMouse (); + IN_HideMouse (); + } else if (mouseactive && key_dest != key_game) { + IN_DeactivateMouse (); + IN_ShowMouse (); + } + } + } + if (fullsbardraw) + Sbar_Changed(); +} + +void VID_SetPalette (unsigned char *palette) +{ + byte *pal; + unsigned r,g,b; + unsigned v; + int r1,g1,b1; + int j,k,l; + unsigned short i; + unsigned *table; +// FILE *f; +// char s[255]; +// HWND hDlg, hProgress; + +// +// 8 8 8 encoding +// + pal = palette; + table = d_8to24table; + for (i=0 ; i<256 ; i++) + { + r = pal[0]; + g = pal[1]; + b = pal[2]; + pal += 3; + +// v = (255<<24) + (r<<16) + (g<<8) + (b<<0); +// v = (255<<0) + (r<<8) + (g<<16) + (b<<24); + v = (255<<24) + (r<<0) + (g<<8) + (b<<16); + *table++ = v; + } + d_8to24table[255] = 0; // 255 is transparent + +// Tonik: create a brighter palette for bmodel textures + pal = palette; + table = d_8to24table2; + for (i=0 ; i<256 ; i++) + { + r = pal[0] * (2.0/1.5); if (r > 255) r = 255; + g = pal[1] * (2.0/1.5); if (g > 255) g = 255; + b = pal[2] * (2.0/1.5); if (b > 255) b = 255; + pal += 3; + *table++ = (255<<24) + (r<<0) + (g<<8) + (b<<16); + } + d_8to24table2[255] = 0; // 255 is transparent + + // JACK: 3D distance calcs - k is last closest, l is the distance. + // FIXME: Precalculate this and cache to disk. + for (i=0; i < (1<<15); i++) { + /* Maps + 000000000000000 + 000000000011111 = Red = 0x1F + 000001111100000 = Blue = 0x03E0 + 111110000000000 = Grn = 0x7C00 + */ + r = ((i & 0x1F) << 3)+4; + g = ((i & 0x03E0) >> 2)+4; + b = ((i & 0x7C00) >> 7)+4; + pal = (unsigned char *)d_8to24table; + for (v=0,k=0,l=10000*10000; v<256; v++,pal+=4) { + r1 = r-pal[0]; + g1 = g-pal[1]; + b1 = b-pal[2]; + j = (r1*r1)+(g1*g1)+(b1*b1); + if (j 0) { + Key_Event(K_MWHEELUP, true); + Key_Event(K_MWHEELUP, false); + } else { + Key_Event(K_MWHEELDOWN, true); + Key_Event(K_MWHEELDOWN, false); + } + break; + + case WM_SIZE: + break; + + case WM_CLOSE: + if (MessageBox (mainwindow, "Are you sure you want to quit?", "Confirm Exit", + MB_YESNO | MB_SETFOREGROUND | MB_ICONQUESTION) == IDYES) + { + Sys_Quit (); + } + + break; + + case WM_ACTIVATE: + fActive = LOWORD(wParam); + fMinimized = (BOOL) HIWORD(wParam); + AppActivate(!(fActive == WA_INACTIVE), fMinimized); + + // fix the leftover Alt from any Alt-Tab or the like that switched us away + ClearAllStates (); + + break; + + case WM_DESTROY: + { + if (dibwindow) + DestroyWindow (dibwindow); + + PostQuitMessage (0); + } + break; + + case MM_MCINOTIFY: + lRet = CDAudio_MessageHandler (hWnd, uMsg, wParam, lParam); + break; + + default: + /* pass all unhandled messages to DefWindowProc */ + lRet = DefWindowProc (hWnd, uMsg, wParam, lParam); + break; + } + + /* return 1 if handled message, 0 if not */ + return lRet; +} + + +/* +================= +VID_NumModes +================= +*/ +int VID_NumModes (void) +{ + return nummodes; +} + + +/* +================= +VID_GetModePtr +================= +*/ +vmode_t *VID_GetModePtr (int modenum) +{ + + if ((modenum >= 0) && (modenum < nummodes)) + return &modelist[modenum]; + else + return &badmode; +} + + +/* +================= +VID_GetModeDescription +================= +*/ +char *VID_GetModeDescription (int mode) +{ + char *pinfo; + vmode_t *pv; + static char temp[100]; + + if ((mode < 0) || (mode >= nummodes)) + return NULL; + + if (!leavecurrentmode) + { + pv = VID_GetModePtr (mode); + pinfo = pv->modedesc; + } + else + { + sprintf (temp, "Desktop resolution (%dx%d)", + modelist[MODE_FULLSCREEN_DEFAULT].width, + modelist[MODE_FULLSCREEN_DEFAULT].height); + pinfo = temp; + } + + return pinfo; +} + + +// KJB: Added this to return the mode driver name in description for console + +char *VID_GetExtModeDescription (int mode) +{ + static char pinfo[40]; + vmode_t *pv; + + if ((mode < 0) || (mode >= nummodes)) + return NULL; + + pv = VID_GetModePtr (mode); + if (modelist[mode].type == MS_FULLDIB) + { + if (!leavecurrentmode) + { + sprintf(pinfo,"%s fullscreen", pv->modedesc); + } + else + { + sprintf (pinfo, "Desktop resolution (%dx%d)", + modelist[MODE_FULLSCREEN_DEFAULT].width, + modelist[MODE_FULLSCREEN_DEFAULT].height); + } + } + else + { + if (modestate == MS_WINDOWED) + sprintf(pinfo, "%s windowed", pv->modedesc); + else + sprintf(pinfo, "windowed"); + } + + return pinfo; +} + + +/* +================= +VID_DescribeCurrentMode_f +================= +*/ +void VID_DescribeCurrentMode_f (void) +{ + Con_Printf ("%s\n", VID_GetExtModeDescription (vid_modenum)); +} + + +/* +================= +VID_NumModes_f +================= +*/ +void VID_NumModes_f (void) +{ + + if (nummodes == 1) + Con_Printf ("%d video mode is available\n", nummodes); + else + Con_Printf ("%d video modes are available\n", nummodes); +} + + +/* +================= +VID_DescribeMode_f +================= +*/ +void VID_DescribeMode_f (void) +{ + int t, modenum; + + modenum = Q_atoi (Cmd_Argv(1)); + + t = leavecurrentmode; + leavecurrentmode = 0; + + Con_Printf ("%s\n", VID_GetExtModeDescription (modenum)); + + leavecurrentmode = t; +} + + +/* +================= +VID_DescribeModes_f +================= +*/ +void VID_DescribeModes_f (void) +{ + int i, lnummodes, t; + char *pinfo; + vmode_t *pv; + + lnummodes = VID_NumModes (); + + t = leavecurrentmode; + leavecurrentmode = 0; + + for (i=1 ; i8 bpp modes + originalnummodes = nummodes; + modenum = 0; + + do + { + stat = EnumDisplaySettings (NULL, modenum, &devmode); + + if ((devmode.dmBitsPerPel >= 15) && + (devmode.dmPelsWidth <= MAXWIDTH) && + (devmode.dmPelsHeight <= MAXHEIGHT) && + (nummodes < MAX_MODE_LIST)) + { + devmode.dmFields = DM_BITSPERPEL | + DM_PELSWIDTH | + DM_PELSHEIGHT; + + if (ChangeDisplaySettings (&devmode, CDS_TEST | CDS_FULLSCREEN) == + DISP_CHANGE_SUCCESSFUL) + { + modelist[nummodes].type = MS_FULLDIB; + modelist[nummodes].width = devmode.dmPelsWidth; + modelist[nummodes].height = devmode.dmPelsHeight; + modelist[nummodes].modenum = 0; + modelist[nummodes].halfscreen = 0; + modelist[nummodes].dib = 1; + modelist[nummodes].fullscreen = 1; + modelist[nummodes].bpp = devmode.dmBitsPerPel; + sprintf (modelist[nummodes].modedesc, "%dx%dx%d", + devmode.dmPelsWidth, devmode.dmPelsHeight, + devmode.dmBitsPerPel); + + // if the width is more than twice the height, reduce it by half because this + // is probably a dual-screen monitor + if (!COM_CheckParm("-noadjustaspect")) + { + if (modelist[nummodes].width > (modelist[nummodes].height << 1)) + { + modelist[nummodes].width >>= 1; + modelist[nummodes].halfscreen = 1; + sprintf (modelist[nummodes].modedesc, "%dx%dx%d", + modelist[nummodes].width, + modelist[nummodes].height, + modelist[nummodes].bpp); + } + } + + for (i=originalnummodes, existingmode = 0 ; i 3) + vid_gamma = 3; + + Cvar_SetValue (&gl_gamma, vid_gamma); + + for (i=0 ; i<768 ; i++) + { + f = pow ( (pal[i]+1)/256.0 , vid_gamma ); + inf = f*255 + 0.5; + if (inf < 0) + inf = 0; + if (inf > 255) + inf = 255; + palette[i] = inf; + } + + memcpy (pal, palette, sizeof(palette)); +} + +/* +=================== +VID_Init +=================== +*/ +void VID_Init (unsigned char *palette) +{ + int i, existingmode; + int basenummodes, width, height, bpp, findbpp, done; + char gldir[MAX_OSPATH]; + HDC hdc; + DEVMODE devmode; + + memset(&devmode, 0, sizeof(devmode)); + + Cvar_RegisterVariable (&vid_mode); + Cvar_RegisterVariable (&vid_wait); + Cvar_RegisterVariable (&vid_nopageflip); + Cvar_RegisterVariable (&_vid_wait_override); + Cvar_RegisterVariable (&_vid_default_mode); + Cvar_RegisterVariable (&_vid_default_mode_win); + Cvar_RegisterVariable (&vid_config_x); + Cvar_RegisterVariable (&vid_config_y); + Cvar_RegisterVariable (&vid_stretch_by_2); + Cvar_RegisterVariable (&_windowed_mouse); + Cvar_RegisterVariable (&gl_ztrick); + Cvar_RegisterVariable (&vid_hwgammacontrol); + + Cmd_AddCommand ("vid_nummodes", VID_NumModes_f); + Cmd_AddCommand ("vid_describecurrentmode", VID_DescribeCurrentMode_f); + Cmd_AddCommand ("vid_describemode", VID_DescribeMode_f); + Cmd_AddCommand ("vid_describemodes", VID_DescribeModes_f); + + hIcon = LoadIcon (global_hInstance, MAKEINTRESOURCE (IDI_ICON2)); + + InitCommonControls(); + + VID_InitDIB (global_hInstance); + basenummodes = nummodes = 1; + + VID_InitFullDIB (global_hInstance); + + if (COM_CheckParm("-window")) + { + hdc = GetDC (NULL); + + if (GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) + { + Sys_Error ("Can't run in non-RGB mode"); + } + + ReleaseDC (NULL, hdc); + + windowed = true; + + vid_default = MODE_WINDOWED; + } + else + { + if (nummodes == 1) + Sys_Error ("No RGB fullscreen modes available"); + + windowed = false; + + if (COM_CheckParm("-mode")) + { + vid_default = Q_atoi(com_argv[COM_CheckParm("-mode")+1]); + } + else + { + if (COM_CheckParm("-current")) + { + modelist[MODE_FULLSCREEN_DEFAULT].width = + GetSystemMetrics (SM_CXSCREEN); + modelist[MODE_FULLSCREEN_DEFAULT].height = + GetSystemMetrics (SM_CYSCREEN); + vid_default = MODE_FULLSCREEN_DEFAULT; + leavecurrentmode = 1; + } + else + { + if (COM_CheckParm("-width")) + { + width = Q_atoi(com_argv[COM_CheckParm("-width")+1]); + } + else + { + width = 640; + } + + if (COM_CheckParm("-bpp")) + { + bpp = Q_atoi(com_argv[COM_CheckParm("-bpp")+1]); + findbpp = 0; + } + else + { + bpp = 15; + findbpp = 1; + } + + if (COM_CheckParm("-height")) + height = Q_atoi(com_argv[COM_CheckParm("-height")+1]); + + // if they want to force it, add the specified mode to the list + if (COM_CheckParm("-force") && (nummodes < MAX_MODE_LIST)) + { + modelist[nummodes].type = MS_FULLDIB; + modelist[nummodes].width = width; + modelist[nummodes].height = height; + modelist[nummodes].modenum = 0; + modelist[nummodes].halfscreen = 0; + modelist[nummodes].dib = 1; + modelist[nummodes].fullscreen = 1; + modelist[nummodes].bpp = bpp; + sprintf (modelist[nummodes].modedesc, "%dx%dx%d", + devmode.dmPelsWidth, devmode.dmPelsHeight, + devmode.dmBitsPerPel); + + for (i=nummodes, existingmode = 0 ; iwidth)/2, 4, p); + + vid_wmodes = 0; + lnummodes = VID_NumModes (); + + for (i=1 ; (i 0) + { + M_Print (2*8, 36+0*8, "Fullscreen Modes (WIDTHxHEIGHTxBPP)"); + + column = 8; + row = 36+2*8; + + for (i=0 ; i"); + M_Print (3*8, 36 + MODE_AREA_HEIGHT * 8 + 8*4, + "and -bpp "); + M_Print (3*8, 36 + MODE_AREA_HEIGHT * 8 + 8*6, + "Select windowed mode with -window"); +} + + +/* +================ +VID_MenuKey +================ +*/ +void VID_MenuKey (int key) +{ + switch (key) + { + case K_ESCAPE: + S_LocalSound ("misc/menu1.wav"); + M_Menu_Options_f (); + break; + + default: + break; + } +} diff --git a/source/vid_win.c b/source/vid_win.c index 6c30982e..5090a087 100644 --- a/source/vid_win.c +++ b/source/vid_win.c @@ -1,3373 +1,3373 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// vid_win.c -- Win32 video driver - -#include "quakedef.h" -#include "winquake.h" -#include "d_local.h" -#include "keys.h" -#include "resource.h" -#include "sound.h" - -#define MINIMUM_MEMORY 0x550000 - -#define MAX_MODE_LIST 30 -#define VID_ROW_SIZE 3 - -qboolean dibonly; - -extern int Minimized; - -HWND mainwindow; - -HWND WINAPI InitializeWindow (HINSTANCE hInstance, int nCmdShow); - -int DIBWidth, DIBHeight; -qboolean DDActive; -RECT WindowRect; -DWORD WindowStyle, ExWindowStyle; - -int window_center_x, window_center_y, window_x, window_y, window_width, window_height; -RECT window_rect; - -static DEVMODE gdevmode; -static qboolean startwindowed = 0, windowed_mode_set; -static int firstupdate = 1; -static qboolean vid_initialized = false, vid_palettized; -static int lockcount; -static int vid_fulldib_on_focus_mode; -static qboolean force_minimized, in_mode_set, is_mode0x13, force_mode_set; -static int vid_stretched, windowed_mouse; -static qboolean palette_changed, syscolchg, vid_mode_set, hide_window, pal_is_nostatic; -static HICON hIcon; -extern qboolean mouseactive; // from in_win.c - -#define MODE_WINDOWED 0 -#define MODE_SETTABLE_WINDOW 2 -#define NO_MODE (MODE_WINDOWED - 1) -#define MODE_FULLSCREEN_DEFAULT (MODE_WINDOWED + 3) - -// Note that 0 is MODE_WINDOWED -cvar_t vid_mode = {"vid_mode","0"}; -// Note that 0 is MODE_WINDOWED -cvar_t _vid_default_mode = {"_vid_default_mode","0",CVAR_ARCHIVE}; -// Note that 3 is MODE_FULLSCREEN_DEFAULT -cvar_t _vid_default_mode_win = {"_vid_default_mode_win","3",CVAR_ARCHIVE}; -cvar_t vid_wait = {"vid_wait","0"}; -cvar_t vid_nopageflip = {"vid_nopageflip","0",CVAR_ARCHIVE}; -cvar_t _vid_wait_override = {"_vid_wait_override","0",CVAR_ARCHIVE}; -cvar_t vid_config_x = {"vid_config_x","800",CVAR_ARCHIVE}; -cvar_t vid_config_y = {"vid_config_y","600",CVAR_ARCHIVE}; -cvar_t vid_stretch_by_2 = {"vid_stretch_by_2","1",CVAR_ARCHIVE}; -cvar_t _windowed_mouse = {"_windowed_mouse","0",CVAR_ARCHIVE}; -cvar_t vid_fullscreen_mode = {"vid_fullscreen_mode","3",CVAR_ARCHIVE}; -cvar_t vid_windowed_mode = {"vid_windowed_mode","0",CVAR_ARCHIVE}; -cvar_t block_switch = {"block_switch","0",CVAR_ARCHIVE}; -cvar_t vid_window_x = {"vid_window_x","0",CVAR_ARCHIVE}; -cvar_t vid_window_y = {"vid_window_y","0",CVAR_ARCHIVE}; -cvar_t vid_resetonswitch = {"vid_resetonswitch","0",CVAR_ARCHIVE}; - -typedef struct { - int width; - int height; -} lmode_t; - -lmode_t lowresmodes[] = { - {320, 200}, - {320, 240}, - {400, 300}, - {512, 384}, -}; - -int vid_modenum = NO_MODE; -int vid_testingmode, vid_realmode; -double vid_testendtime; -int vid_default = MODE_WINDOWED; -static int windowed_default; - -modestate_t modestate = MS_UNINIT; - -static byte *vid_surfcache; -static int vid_surfcachesize; -static int VID_highhunkmark; - -unsigned char vid_curpal[256*3]; - -unsigned short d_8to16table[256]; -unsigned d_8to24table[256]; - -int driver = grDETECT,mode; -qboolean useWinDirect = true, useDirectDraw = true; -MGLDC *mgldc = NULL,*memdc = NULL,*dibdc = NULL,*windc = NULL; - -typedef struct { - modestate_t type; - int width; - int height; - int modenum; - int mode13; - int stretched; - int dib; - int fullscreen; - int bpp; - int halfscreen; - char modedesc[13]; -} vmode_t; - -static vmode_t modelist[MAX_MODE_LIST]; -static int nummodes; -static vmode_t *pcurrentmode; - -int aPage; // Current active display page -int vPage; // Current visible display page -int waitVRT = true; // True to wait for retrace on flip - -static vmode_t badmode; - -static byte backingbuf[48*24]; - -void VID_MenuDraw (void); -void VID_MenuKey (int key); - -LONG WINAPI MainWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); -void AppActivate(BOOL fActive, BOOL minimize); - - -/* -================ -VID_RememberWindowPos -================ -*/ -void VID_RememberWindowPos (void) -{ - RECT rect; - - if (GetWindowRect (mainwindow, &rect)) - { - if ((rect.left < GetSystemMetrics (SM_CXSCREEN)) && - (rect.top < GetSystemMetrics (SM_CYSCREEN)) && - (rect.right > 0) && - (rect.bottom > 0)) - { - Cvar_SetValue (&vid_window_x, (float)rect.left); - Cvar_SetValue (&vid_window_y, (float)rect.top); - } - } -} - - -/* -================ -VID_CheckWindowXY -================ -*/ -void VID_CheckWindowXY (void) -{ - - if (((int)vid_window_x.value > (GetSystemMetrics (SM_CXSCREEN) - 160)) || - ((int)vid_window_y.value > (GetSystemMetrics (SM_CYSCREEN) - 120)) || - ((int)vid_window_x.value < 0) || - ((int)vid_window_y.value < 0)) - { - Cvar_SetValue (&vid_window_x, 0.0); - Cvar_SetValue (&vid_window_y, 0.0 ); - } -} - - -/* -================ -VID_UpdateWindowStatus -================ -*/ -void VID_UpdateWindowStatus (void) -{ - - window_rect.left = window_x; - window_rect.top = window_y; - window_rect.right = window_x + window_width; - window_rect.bottom = window_y + window_height; - window_center_x = (window_rect.left + window_rect.right) / 2; - window_center_y = (window_rect.top + window_rect.bottom) / 2; - - IN_UpdateClipCursor (); -} - - -/* -================ -ClearAllStates -================ -*/ -void ClearAllStates (void) -{ - extern void IN_ClearStates (void); - extern qboolean keydown[256]; - int i; - -// send an up event for each key, to make sure the server clears them all - for (i=0 ; i<256 ; i++) - { - if (keydown[i]) - Key_Event (i, false); - } - - Key_ClearStates (); - IN_ClearStates (); -} - - -/* -================ -VID_CheckAdequateMem -================ -*/ -qboolean VID_CheckAdequateMem (int width, int height) -{ - int tbuffersize; - - tbuffersize = width * height * sizeof (*d_pzbuffer); - - tbuffersize += D_SurfaceCacheForRes (width, height); - -// see if there's enough memory, allowing for the normal mode 0x13 pixel, -// z, and surface buffers - if ((host_parms.memsize - tbuffersize + SURFCACHE_SIZE_AT_320X200 + - 0x10000 * 3) < MINIMUM_MEMORY) - { - return false; // not enough memory for mode - } - - return true; -} - - -/* -================ -VID_AllocBuffers -================ -*/ -qboolean VID_AllocBuffers (int width, int height) -{ - int tsize, tbuffersize; - - tbuffersize = width * height * sizeof (*d_pzbuffer); - - tsize = D_SurfaceCacheForRes (width, height); - - tbuffersize += tsize; - -// see if there's enough memory, allowing for the normal mode 0x13 pixel, -// z, and surface buffers - if ((host_parms.memsize - tbuffersize + SURFCACHE_SIZE_AT_320X200 + - 0x10000 * 3) < MINIMUM_MEMORY) - { - Con_SafePrintf ("Not enough memory for video mode\n"); - return false; // not enough memory for mode - } - - vid_surfcachesize = tsize; - - if (d_pzbuffer) - { - D_FlushCaches (); - Hunk_FreeToHighMark (VID_highhunkmark); - d_pzbuffer = NULL; - } - - VID_highhunkmark = Hunk_HighMark (); - - d_pzbuffer = Hunk_HighAllocName (tbuffersize, "video"); - - vid_surfcache = (byte *)d_pzbuffer + - width * height * sizeof (*d_pzbuffer); - - return true; -} - - -void initFatalError(void) -{ - MGL_exit(); - MGL_fatalError(MGL_errorMsg(MGL_result())); - exit(EXIT_FAILURE); -} - -#if 0 //def NEW_SUSPEND - -int VID_Suspend (MGLDC *dc, int flags) -{ - int i; - if (flags & MGL_DEACTIVATE) - { - IN_RestoreOriginalMouseState (); - CDAudio_Pause (); - - // keep WM_PAINT from trying to redraw - in_mode_set = true; - block_drawing = true; - } - else if (flags & MGL_REACTIVATE) - { - IN_SetQuakeMouseState (); - // fix the leftover Alt from any Alt-Tab or the like that switched us away - ClearAllStates (); - CDAudio_Resume (); - in_mode_set = false; - - block_drawing = false; -// vid.recalc_refdef = 1; - force_mode_set = 1; - i = msg_suppress_1; - msg_suppress_1 = 1; - VID_Fullscreen_f(); - msg_suppress_1 = i; - force_mode_set = 0; - } - - return 1; -} - -#else - -int VID_Suspend (MGLDC *dc, int flags) -{ - - if (flags & MGL_DEACTIVATE) - { - // FIXME: this doesn't currently work on NT - if (block_switch.value && !WinNT) - { - return MGL_NO_DEACTIVATE; - } - - S_BlockSound (); - S_ClearBuffer (); - - IN_RestoreOriginalMouseState (); - CDAudio_Pause (); - - // keep WM_PAINT from trying to redraw - in_mode_set = true; - - block_drawing = true; // so we don't try to draw while switched away - - return MGL_NO_SUSPEND_APP; - } - else if (flags & MGL_REACTIVATE) - { - IN_SetQuakeMouseState (); - // fix the leftover Alt from any Alt-Tab or the like that switched us away - ClearAllStates (); - CDAudio_Resume (); - S_UnblockSound (); - - in_mode_set = false; - - vid.recalc_refdef = 1; - - block_drawing = false; - - return MGL_NO_SUSPEND_APP; - } - - return MGL_NO_SUSPEND_APP; -} -#endif - - -void registerAllDispDrivers(void) -{ - /* Event though these driver require WinDirect, we register - * them so that they will still be available even if DirectDraw - * is present and the user has disable the high performance - * WinDirect modes. - */ - MGL_registerDriver(MGL_VGA8NAME,VGA8_driver); -// MGL_registerDriver(MGL_VGAXNAME,VGAX_driver); - - /* Register display drivers */ - if (useWinDirect) - { -//we don't want VESA 1.X drivers MGL_registerDriver(MGL_SVGA8NAME,SVGA8_driver); - MGL_registerDriver(MGL_LINEAR8NAME,LINEAR8_driver); - - if (!COM_CheckParm ("-novbeaf")) - MGL_registerDriver(MGL_ACCEL8NAME,ACCEL8_driver); - } - - if (useDirectDraw) - { - MGL_registerDriver(MGL_DDRAW8NAME,DDRAW8_driver); - } -} - - -void registerAllMemDrivers(void) -{ - /* Register memory context drivers */ - MGL_registerDriver(MGL_PACKED8NAME,PACKED8_driver); -} - - -void VID_InitMGLFull (HINSTANCE hInstance) -{ - int i, xRes, yRes, bits, /*vMode,*/ lowres, curmode, temp; - int lowstretchedres, stretchedmode, lowstretched; - uchar *m; - -// FIXME: NT is checked for because MGL currently has a bug that causes it -// to try to use WinDirect modes even on NT - if (COM_CheckParm("-nowindirect") || - COM_CheckParm("-nowd") || - COM_CheckParm("-novesa") || - WinNT) - { - useWinDirect = false; - } - - if (COM_CheckParm("-nodirectdraw") || COM_CheckParm("-noddraw") || COM_CheckParm("-nodd")) - useDirectDraw = false; - - // Initialise the MGL - MGL_unregisterAllDrivers(); - registerAllDispDrivers(); - registerAllMemDrivers(); - MGL_detectGraph(&driver,&mode); - m = MGL_availableModes(); - - if (m[0] != 0xFF) - { - lowres = lowstretchedres = 99999; - lowstretched = 0; - curmode = 0; - - // find the lowest-res mode, or a mode we can stretch up to and get - // lowest-res that way - for (i = 0; m[i] != 0xFF; i++) - { - MGL_modeResolution(m[i], &xRes, &yRes,&bits); - - if ((bits == 8) && - (xRes <= MAXWIDTH) && - (yRes <= MAXHEIGHT) && - (curmode < MAX_MODE_LIST)) - { - if (m[i] == grVGA_320x200x256) - is_mode0x13 = true; - - if (!COM_CheckParm("-noforcevga")) - { - if (m[i] == grVGA_320x200x256) - { - mode = i; - break; - } - } - - if (xRes < lowres) - { - lowres = xRes; - mode = i; - } - - if ((xRes < lowstretchedres) && ((xRes >> 1) >= 320)) - { - lowstretchedres = xRes >> 1; - stretchedmode = i; - } - } - - curmode++; - } - - // if there's a mode we can stretch by 2 up to, thereby effectively getting - // a lower-res mode than the lowest-res real but still at least 320x200, that - // will be our default mode - if (lowstretchedres < lowres) - { - mode = stretchedmode; - lowres = lowstretchedres; - lowstretched = 1; - } - - // build the mode list, leaving room for the low-res stretched mode, if any - nummodes++; // leave room for default mode - - for (i = 0; m[i] != 0xFF; i++) - { - MGL_modeResolution(m[i], &xRes, &yRes,&bits); - - if ((bits == 8) && - (xRes <= MAXWIDTH) && - (yRes <= MAXHEIGHT) && - (nummodes < MAX_MODE_LIST)) - { - if (i == mode) - { - if (lowstretched) - { - stretchedmode = nummodes; - curmode = nummodes++; - } - else - { - curmode = MODE_FULLSCREEN_DEFAULT; - } - } - else - { - curmode = nummodes++; - } - - modelist[curmode].type = MS_FULLSCREEN; - modelist[curmode].width = xRes; - modelist[curmode].height = yRes; - sprintf (modelist[curmode].modedesc, "%dx%d", xRes, yRes); - - if (m[i] == grVGA_320x200x256) - modelist[curmode].mode13 = 1; - else - modelist[curmode].mode13 = 0; - - modelist[curmode].modenum = m[i]; - modelist[curmode].stretched = 0; - modelist[curmode].dib = 0; - modelist[curmode].fullscreen = 1; - modelist[curmode].halfscreen = 0; - modelist[curmode].bpp = 8; - } - } - - if (lowstretched) - { - modelist[MODE_FULLSCREEN_DEFAULT] = modelist[stretchedmode]; - modelist[MODE_FULLSCREEN_DEFAULT].stretched = 1; - modelist[MODE_FULLSCREEN_DEFAULT].width >>= 1; - modelist[MODE_FULLSCREEN_DEFAULT].height >>= 1; - sprintf (modelist[MODE_FULLSCREEN_DEFAULT].modedesc, "%dx%d", - modelist[MODE_FULLSCREEN_DEFAULT].width, - modelist[MODE_FULLSCREEN_DEFAULT].height); - } - - vid_default = MODE_FULLSCREEN_DEFAULT; - - temp = m[0]; - - if (!MGL_init(&driver, &temp, "")) - { - initFatalError(); - } - } - - MGL_setSuspendAppCallback(VID_Suspend); -} - - -MGLDC *createDisplayDC(int forcemem) -/**************************************************************************** -* -* Function: createDisplayDC -* Returns: Pointer to the MGL device context to use for the application -* -* Description: Initialises the MGL and creates an appropriate display -* device context to be used by the GUI. This creates and -* apropriate device context depending on the system being -* compile for, and should be the only place where system -* specific code is required. -* -****************************************************************************/ -{ - MGLDC *dc; - pixel_format_t pf; - int npages; - - // Start the specified video mode - if (!MGL_changeDisplayMode(mode)) - initFatalError(); - - npages = MGL_availablePages(mode); - - if (npages > 3) - npages = 3; - - if (!COM_CheckParm ("-notriplebuf")) - { - if (npages > 2) - { - npages = 2; - } - } - - if ((dc = MGL_createDisplayDC(npages)) == NULL) - return NULL; - - if (!forcemem && (MGL_surfaceAccessType(dc)) == MGL_LINEAR_ACCESS && (dc->mi.maxPage > 0)) - { - MGL_makeCurrentDC(dc); - memdc = NULL; - } - else - { - // Set up for blitting from a memory buffer - memdc = MGL_createMemoryDC(MGL_sizex(dc)+1,MGL_sizey(dc)+1,8,&pf); - MGL_makeCurrentDC(memdc); - } - - // Enable page flipping even for even for blitted surfaces - if (forcemem) - { - vid.numpages = 1; - } - else - { - vid.numpages = dc->mi.maxPage + 1; - - if (vid.numpages > 1) - { - // Set up for page flipping - MGL_setActivePage(dc, aPage = 1); - MGL_setVisualPage(dc, vPage = 0, false); - } - - if (vid.numpages > 3) - vid.numpages = 3; - } - - if (vid.numpages == 2) - waitVRT = true; - else - waitVRT = false; - - return dc; -} - - -void VID_InitMGLDIB (HINSTANCE hInstance) -{ - WNDCLASS wc; - HDC hdc; - - hIcon = LoadIcon (hInstance, MAKEINTRESOURCE (IDI_ICON2)); - - /* Register the frame class */ - wc.style = 0; - wc.lpfnWndProc = (WNDPROC)MainWndProc; - wc.cbClsExtra = 0; - wc.cbWndExtra = 0; - wc.hInstance = hInstance; - wc.hIcon = 0; - wc.hCursor = LoadCursor (NULL,IDC_ARROW); - wc.hbrBackground = NULL; - wc.lpszMenuName = 0; - wc.lpszClassName = "WinQuake"; - - if (!RegisterClass (&wc) ) - Sys_Error ("Couldn't register window class"); - - /* Find the size for the DIB window */ - /* Initialise the MGL for windowed operation */ - MGL_setAppInstance(hInstance); - registerAllMemDrivers(); - MGL_initWindowed(""); - - modelist[0].type = MS_WINDOWED; - modelist[0].width = 320; - modelist[0].height = 240; - strcpy (modelist[0].modedesc, "320x240"); - modelist[0].mode13 = 0; - modelist[0].modenum = MODE_WINDOWED; - modelist[0].stretched = 0; - modelist[0].dib = 1; - modelist[0].fullscreen = 0; - modelist[0].halfscreen = 0; - modelist[0].bpp = 8; - - modelist[1].type = MS_WINDOWED; - modelist[1].width = 640; - modelist[1].height = 480; - strcpy (modelist[1].modedesc, "640x480"); - modelist[1].mode13 = 0; - modelist[1].modenum = MODE_WINDOWED + 1; - modelist[1].stretched = 1; - modelist[1].dib = 1; - modelist[1].fullscreen = 0; - modelist[1].halfscreen = 0; - modelist[1].bpp = 8; - - modelist[2].type = MS_WINDOWED; - modelist[2].width = 800; - modelist[2].height = 600; - strcpy (modelist[2].modedesc, "800x600"); - modelist[2].mode13 = 0; - modelist[2].modenum = MODE_WINDOWED + 2; - modelist[2].stretched = 1; - modelist[2].dib = 1; - modelist[2].fullscreen = 0; - modelist[2].halfscreen = 0; - modelist[2].bpp = 8; - -// automatically stretch the default mode up if > 640x480 desktop resolution - hdc = GetDC(NULL); - - if ((GetDeviceCaps(hdc, HORZRES) > 640) && !COM_CheckParm("-noautostretch")) - { - vid_default = MODE_WINDOWED + 1; - } - else - { - vid_default = MODE_WINDOWED; - } - - windowed_default = vid_default; - - ReleaseDC(NULL,hdc); - - nummodes = 3; // reserve space for windowed mode - - DDActive = 0; -} - - -/* -================= -VID_InitFullDIB -================= -*/ -void VID_InitFullDIB (HINSTANCE hInstance) -{ - DEVMODE devmode; - int i, j, modenum, /*cmodes,*/ existingmode, originalnummodes, lowestres; - int numlowresmodes, bpp, done; - int cstretch, istretch, mstretch; - BOOL stat; - -// enumerate 8 bpp modes - originalnummodes = nummodes; - modenum = 0; - lowestres = 99999; - - do - { - stat = EnumDisplaySettings (NULL, modenum, &devmode); - - if ((devmode.dmBitsPerPel == 8) && - (devmode.dmPelsWidth <= MAXWIDTH) && - (devmode.dmPelsHeight <= MAXHEIGHT) && - (nummodes < MAX_MODE_LIST)) - { - devmode.dmFields = DM_BITSPERPEL | - DM_PELSWIDTH | - DM_PELSHEIGHT; - - if (ChangeDisplaySettings (&devmode, CDS_TEST | CDS_FULLSCREEN) == - DISP_CHANGE_SUCCESSFUL) - { - modelist[nummodes].type = MS_FULLDIB; - modelist[nummodes].width = devmode.dmPelsWidth; - modelist[nummodes].height = devmode.dmPelsHeight; - modelist[nummodes].modenum = 0; - modelist[nummodes].mode13 = 0; - modelist[nummodes].stretched = 0; - modelist[nummodes].halfscreen = 0; - modelist[nummodes].dib = 1; - modelist[nummodes].fullscreen = 1; - modelist[nummodes].bpp = devmode.dmBitsPerPel; - sprintf (modelist[nummodes].modedesc, "%dx%d", - devmode.dmPelsWidth, devmode.dmPelsHeight); - - // if the width is more than twice the height, reduce it by half because this - // is probably a dual-screen monitor - if (!COM_CheckParm("-noadjustaspect")) - { - if (modelist[nummodes].width > (modelist[nummodes].height << 1)) - { - modelist[nummodes].width >>= 1; - modelist[nummodes].halfscreen = 1; - sprintf (modelist[nummodes].modedesc, "%dx%d", - modelist[nummodes].width, - modelist[nummodes].height); - } - } - - for (i=originalnummodes, existingmode = 0 ; i 8 bpp - if (nummodes == originalnummodes) - { - modenum = 0; - lowestres = 99999; - - Con_SafePrintf ("No 8-bpp fullscreen DIB modes found\n"); - - do - { - stat = EnumDisplaySettings (NULL, modenum, &devmode); - - if ((((devmode.dmPelsWidth <= MAXWIDTH) && - (devmode.dmPelsHeight <= MAXHEIGHT)) || - (!COM_CheckParm("-noadjustaspect") && - (devmode.dmPelsWidth <= (MAXWIDTH*2)) && - (devmode.dmPelsWidth > (devmode.dmPelsHeight*2)))) && - (nummodes < MAX_MODE_LIST) && - (devmode.dmBitsPerPel > 8)) - { - devmode.dmFields = DM_BITSPERPEL | - DM_PELSWIDTH | - DM_PELSHEIGHT; - - if (ChangeDisplaySettings (&devmode, CDS_TEST | CDS_FULLSCREEN) == - DISP_CHANGE_SUCCESSFUL) - { - modelist[nummodes].type = MS_FULLDIB; - modelist[nummodes].width = devmode.dmPelsWidth; - modelist[nummodes].height = devmode.dmPelsHeight; - modelist[nummodes].modenum = 0; - modelist[nummodes].mode13 = 0; - modelist[nummodes].stretched = 0; - modelist[nummodes].halfscreen = 0; - modelist[nummodes].dib = 1; - modelist[nummodes].fullscreen = 1; - modelist[nummodes].bpp = devmode.dmBitsPerPel; - sprintf (modelist[nummodes].modedesc, "%dx%d", - devmode.dmPelsWidth, devmode.dmPelsHeight); - - // if the width is more than twice the height, reduce it by half because this - // is probably a dual-screen monitor - if (!COM_CheckParm("-noadjustaspect")) - { - if (modelist[nummodes].width > (modelist[nummodes].height*2)) - { - modelist[nummodes].width >>= 1; - modelist[nummodes].halfscreen = 1; - sprintf (modelist[nummodes].modedesc, "%dx%d", - modelist[nummodes].width, - modelist[nummodes].height); - } - } - - for (i=originalnummodes, existingmode = 0 ; i= modelist[i].bpp)) - { - existingmode = 1; - break; - } - } - - if (!existingmode) - { - if (modelist[nummodes].width < lowestres) - lowestres = modelist[nummodes].width; - - nummodes++; - } - } - } - - switch (bpp) - { - case 8: - bpp = 16; - break; - - case 16: - bpp = 32; - break; - - case 32: - done = 1; - break; - } - } - -// now add the lowest stretch-by-2 pseudo-modes between 320-wide -// (inclusive) and lowest real res (not inclusive) -// don't bother if we have a real VGA mode 0x13 mode - if (!is_mode0x13) - { - for (i=originalnummodes, cstretch=0 ; i> 1) < lowestres) && - ((modelist[i].width >> 1) >= 320)) - { - lowestres = modelist[i].width >> 1; - cstretch = 1; - mstretch = i; - } - } - - if ((nummodes + cstretch) > MAX_MODE_LIST) - cstretch = MAX_MODE_LIST - nummodes; - - if (cstretch > 0) - { - for (i=(nummodes-1) ; i>=originalnummodes ; i--) - modelist[i+cstretch] = modelist[i]; - - nummodes += cstretch; - istretch = originalnummodes; - - modelist[istretch] = modelist[mstretch]; - modelist[istretch].width >>= 1; - modelist[istretch].height >>= 1; - modelist[istretch].stretched = 1; - sprintf (modelist[istretch].modedesc, "%dx%d", - modelist[istretch].width, modelist[istretch].height); - } - } - - if (nummodes != originalnummodes) - vid_default = MODE_FULLSCREEN_DEFAULT; - else - Con_SafePrintf ("No fullscreen DIB modes found\n"); -} - - -/* -================= -VID_NumModes -================= -*/ -int VID_NumModes (void) -{ - return nummodes; -} - - -/* -================= -VID_GetModePtr -================= -*/ -vmode_t *VID_GetModePtr (int modenum) -{ - - if ((modenum >= 0) && (modenum < nummodes)) - return &modelist[modenum]; - else - return &badmode; -} - - -/* -================= -VID_CheckModedescFixup -================= -*/ -void VID_CheckModedescFixup (int mode) -{ - int x, y, stretch; - - if (mode == MODE_SETTABLE_WINDOW) - { - modelist[mode].stretched = (int)vid_stretch_by_2.value; - stretch = modelist[mode].stretched; - - if (vid_config_x.value < (320 << stretch)) - vid_config_x.value = 320 << stretch; - - if (vid_config_y.value < (200 << stretch)) - vid_config_y.value = 200 << stretch; - - x = (int)vid_config_x.value; - y = (int)vid_config_y.value; - sprintf (modelist[mode].modedesc, "%dx%d", x, y); - modelist[mode].width = x; - modelist[mode].height = y; - } -} - - -/* -================= -VID_GetModeDescriptionMemCheck -================= -*/ -char *VID_GetModeDescriptionMemCheck (int mode) -{ - char *pinfo; - vmode_t *pv; - - if ((mode < 0) || (mode >= nummodes)) - return NULL; - - VID_CheckModedescFixup (mode); - - pv = VID_GetModePtr (mode); - pinfo = pv->modedesc; - - if (VID_CheckAdequateMem (pv->width, pv->height)) - { - return pinfo; - } - else - { - return NULL; - } -} - - -/* -================= -VID_GetModeDescription -================= -*/ -char *VID_GetModeDescription (int mode) -{ - char *pinfo; - vmode_t *pv; - - if ((mode < 0) || (mode >= nummodes)) - return NULL; - - VID_CheckModedescFixup (mode); - - pv = VID_GetModePtr (mode); - pinfo = pv->modedesc; - return pinfo; -} - - -/* -================= -VID_GetModeDescription2 - -Tacks on "windowed" or "fullscreen" -================= -*/ -char *VID_GetModeDescription2 (int mode) -{ - static char pinfo[40]; - vmode_t *pv; - - if ((mode < 0) || (mode >= nummodes)) - return NULL; - - VID_CheckModedescFixup (mode); - - pv = VID_GetModePtr (mode); - - if (modelist[mode].type == MS_FULLSCREEN) - { - sprintf(pinfo,"%s fullscreen", pv->modedesc); - } - else if (modelist[mode].type == MS_FULLDIB) - { - sprintf(pinfo,"%s fullscreen", pv->modedesc); - } - else - { - sprintf(pinfo, "%s windowed", pv->modedesc); - } - - return pinfo; -} - - -// KJB: Added this to return the mode driver name in description for console - -char *VID_GetExtModeDescription (int mode) -{ - static char pinfo[40]; - vmode_t *pv; - - if ((mode < 0) || (mode >= nummodes)) - return NULL; - - VID_CheckModedescFixup (mode); - - pv = VID_GetModePtr (mode); - if (modelist[mode].type == MS_FULLSCREEN) - { - sprintf(pinfo,"%s fullscreen %s",pv->modedesc, - MGL_modeDriverName(pv->modenum)); - } - else if (modelist[mode].type == MS_FULLDIB) - { - sprintf(pinfo,"%s fullscreen DIB", pv->modedesc); - } - else - { - sprintf(pinfo, "%s windowed", pv->modedesc); - } - - return pinfo; -} - - -void DestroyDIBWindow (void) -{ - - if (modestate == MS_WINDOWED) - { - // destroy the associated MGL DC's; the window gets reused - if (windc) - MGL_destroyDC(windc); - if (dibdc) - MGL_destroyDC(dibdc); - windc = dibdc = NULL; - } -} - - -void DestroyFullscreenWindow (void) -{ - - if (modestate == MS_FULLSCREEN) - { - // destroy the existing fullscreen mode and DC's - if (mgldc) - MGL_destroyDC (mgldc); - if (memdc) - MGL_destroyDC (memdc); - mgldc = memdc = NULL; - } -} - - - -void DestroyFullDIBWindow (void) -{ - if (modestate == MS_FULLDIB) - { - ChangeDisplaySettings (NULL, CDS_FULLSCREEN); - - // Destroy the fullscreen DIB window and associated MGL DC's - if (windc) - MGL_destroyDC(windc); - if (dibdc) - MGL_destroyDC(dibdc); - windc = dibdc = NULL; - } -} - - -qboolean VID_SetWindowedMode (int modenum) -{ - HDC hdc; - pixel_format_t pf; - qboolean stretched; - int lastmodestate; -// LONG wlong; - - if (!windowed_mode_set) - { - if (COM_CheckParm ("-resetwinpos")) - { - Cvar_SetValue (&vid_window_x, 0.0); - Cvar_SetValue (&vid_window_y, 0.0); - } - - windowed_mode_set; - } - - VID_CheckModedescFixup (modenum); - - DDActive = 0; - lastmodestate = modestate; - - DestroyFullscreenWindow (); - DestroyFullDIBWindow (); - - if (windc) - MGL_destroyDC(windc); - if (dibdc) - MGL_destroyDC(dibdc); - windc = dibdc = NULL; - -// KJB: Signal to the MGL that we are going back to windowed mode - if (!MGL_changeDisplayMode(grWINDOWED)) - initFatalError(); - - WindowRect.top = WindowRect.left = 0; - - WindowRect.right = modelist[modenum].width; - WindowRect.bottom = modelist[modenum].height; - stretched = modelist[modenum].stretched; - - DIBWidth = modelist[modenum].width; - DIBHeight = modelist[modenum].height; - - if (stretched) - { - DIBWidth >>= 1; - DIBHeight >>= 1; - } - - WindowStyle = WS_OVERLAPPED | WS_BORDER | WS_CAPTION | WS_SYSMENU | - WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_CLIPSIBLINGS | - WS_CLIPCHILDREN; - ExWindowStyle = 0; - AdjustWindowRectEx(&WindowRect, WindowStyle, FALSE, 0); - -// the first time we're called to set the mode, create the window we'll use -// for the rest of the session - if (!vid_mode_set) - { - mainwindow = CreateWindowEx ( - ExWindowStyle, - "WinQuake", - "QuakeWorld", - WindowStyle, - 0, 0, - WindowRect.right - WindowRect.left, - WindowRect.bottom - WindowRect.top, - NULL, - NULL, - global_hInstance, - NULL); - - if (!mainwindow) - Sys_Error ("Couldn't create DIB window"); - - // tell MGL to use this window for fullscreen modes - MGL_registerFullScreenWindow (mainwindow); - - vid_mode_set = true; - } - else - { - SetWindowLong(mainwindow, GWL_STYLE, WindowStyle | WS_VISIBLE); - SetWindowLong(mainwindow, GWL_EXSTYLE, ExWindowStyle); - } - - if (!SetWindowPos (mainwindow, - NULL, - 0, 0, - WindowRect.right - WindowRect.left, - WindowRect.bottom - WindowRect.top, - SWP_NOCOPYBITS | SWP_NOZORDER | - SWP_HIDEWINDOW)) - { - Sys_Error ("Couldn't resize DIB window"); - } - - if (hide_window) - return true; - -// position and show the DIB window - VID_CheckWindowXY (); - SetWindowPos (mainwindow, NULL, (int)vid_window_x.value, - (int)vid_window_y.value, 0, 0, - SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW | SWP_DRAWFRAME); - - if (force_minimized) - ShowWindow (mainwindow, SW_MINIMIZE); - else - ShowWindow (mainwindow, SW_SHOWDEFAULT); - - UpdateWindow (mainwindow); - - modestate = MS_WINDOWED; - vid_fulldib_on_focus_mode = 0; - -// because we have set the background brush for the window to NULL -// (to avoid flickering when re-sizing the window on the desktop), -// we clear the window to black when created, otherwise it will be -// empty while Quake starts up. - hdc = GetDC(mainwindow); - PatBlt(hdc,0,0,WindowRect.right,WindowRect.bottom,BLACKNESS); - ReleaseDC(mainwindow, hdc); - - /* Create the MGL window DC and the MGL memory DC */ - if ((windc = MGL_createWindowedDC(mainwindow)) == NULL) - MGL_fatalError("Unable to create Windowed DC!"); - - if ((dibdc = MGL_createMemoryDC(DIBWidth,DIBHeight,8,&pf)) == NULL) - MGL_fatalError("Unable to create Memory DC!"); - - MGL_makeCurrentDC(dibdc); - - vid.buffer = vid.conbuffer = vid.direct = dibdc->surface; - vid.rowbytes = vid.conrowbytes = dibdc->mi.bytesPerLine; - vid.numpages = 1; - vid.maxwarpwidth = WARP_WIDTH; - vid.maxwarpheight = WARP_HEIGHT; - vid.height = vid.conheight = DIBHeight; - vid.width = vid.conwidth = DIBWidth; - vid.aspect = ((float)vid.height / (float)vid.width) * - (320.0 / 240.0); - - vid_stretched = stretched; - - SendMessage (mainwindow, WM_SETICON, (WPARAM)TRUE, (LPARAM)hIcon); - SendMessage (mainwindow, WM_SETICON, (WPARAM)FALSE, (LPARAM)hIcon); - - // Tonik: this is a workaroung for the bug with garbaged - // screen on Riva TNT - if (lastmodestate == MS_FULLSCREEN && vid_resetonswitch.value) - ChangeDisplaySettings (NULL, CDS_RESET); - - return true; -} - - -qboolean VID_SetFullscreenMode (int modenum) -{ - - DDActive = 1; - - DestroyDIBWindow (); - DestroyFullDIBWindow (); - - mode = modelist[modenum].modenum; - - // Destroy old DC's, resetting back to fullscreen mode - if (mgldc) - MGL_destroyDC (mgldc); - if (memdc) - MGL_destroyDC (memdc); - mgldc = memdc = NULL; - - if ((mgldc = createDisplayDC (modelist[modenum].stretched || - (int)vid_nopageflip.value)) == NULL) - { - return false; - } - - modestate = MS_FULLSCREEN; - vid_fulldib_on_focus_mode = 0; - - vid.buffer = vid.conbuffer = vid.direct = NULL; - vid.maxwarpwidth = WARP_WIDTH; - vid.maxwarpheight = WARP_HEIGHT; - DIBHeight = vid.height = vid.conheight = modelist[modenum].height; - DIBWidth = vid.width = vid.conwidth = modelist[modenum].width; - vid.aspect = ((float)vid.height / (float)vid.width) * - (320.0 / 240.0); - - vid_stretched = modelist[modenum].stretched; - -// needed because we're not getting WM_MOVE messages fullscreen on NT - window_x = 0; - window_y = 0; - -// set the large icon, so the Quake icon will show up in the taskbar - SendMessage (mainwindow, WM_SETICON, (WPARAM)1, (LPARAM)hIcon); - SendMessage (mainwindow, WM_SETICON, (WPARAM)0, (LPARAM)hIcon); - -// shouldn't be needed, but Kendall needs to let us get the activation -// message for this not to be needed on NT - AppActivate (true, false); - - return true; -} - - -qboolean VID_SetFullDIBMode (int modenum) -{ - HDC hdc; - pixel_format_t pf; - int lastmodestate; - - DDActive = 0; - - DestroyFullscreenWindow (); - DestroyDIBWindow (); - - if (windc) - MGL_destroyDC(windc); - if (dibdc) - MGL_destroyDC(dibdc); - windc = dibdc = NULL; - - // KJB: Signal to the MGL that we are going back to windowed mode - if (!MGL_changeDisplayMode(grWINDOWED)) - initFatalError(); - - gdevmode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; - gdevmode.dmBitsPerPel = modelist[modenum].bpp; - gdevmode.dmPelsWidth = modelist[modenum].width << modelist[modenum].stretched << - modelist[modenum].halfscreen; - gdevmode.dmPelsHeight = modelist[modenum].height << modelist[modenum].stretched; - gdevmode.dmSize = sizeof (gdevmode); - - if (ChangeDisplaySettings (&gdevmode, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) - Sys_Error ("Couldn't set fullscreen DIB mode"); - - lastmodestate = modestate; - modestate = MS_FULLDIB; - vid_fulldib_on_focus_mode = modenum; - - WindowRect.top = WindowRect.left = 0; - - hdc = GetDC(NULL); - - WindowRect.right = modelist[modenum].width << modelist[modenum].stretched; - WindowRect.bottom = modelist[modenum].height << modelist[modenum].stretched; - - ReleaseDC(NULL,hdc); - - DIBWidth = modelist[modenum].width; - DIBHeight = modelist[modenum].height; - - WindowStyle = WS_POPUP | WS_SYSMENU | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; - ExWindowStyle = 0; - AdjustWindowRectEx(&WindowRect, WindowStyle, FALSE, 0); - - SetWindowLong(mainwindow, GWL_STYLE, WindowStyle | WS_VISIBLE); - SetWindowLong(mainwindow, GWL_EXSTYLE, ExWindowStyle); - - if (!SetWindowPos (mainwindow, - NULL, - 0, 0, - WindowRect.right - WindowRect.left, - WindowRect.bottom - WindowRect.top, - SWP_NOCOPYBITS | SWP_NOZORDER)) - { - Sys_Error ("Couldn't resize DIB window"); - } - -// position and show the DIB window - SetWindowPos (mainwindow, HWND_TOPMOST, 0, 0, 0, 0, - SWP_NOSIZE | SWP_SHOWWINDOW | SWP_DRAWFRAME); - ShowWindow (mainwindow, SW_SHOWDEFAULT); - UpdateWindow (mainwindow); - - // Because we have set the background brush for the window to NULL - // (to avoid flickering when re-sizing the window on the desktop), we - // clear the window to black when created, otherwise it will be - // empty while Quake starts up. - hdc = GetDC(mainwindow); - PatBlt(hdc,0,0,WindowRect.right,WindowRect.bottom,BLACKNESS); - ReleaseDC(mainwindow, hdc); - - /* Create the MGL window DC and the MGL memory DC */ - if ((windc = MGL_createWindowedDC(mainwindow)) == NULL) - MGL_fatalError("Unable to create Fullscreen DIB DC!"); - - if ((dibdc = MGL_createMemoryDC(DIBWidth,DIBHeight,8,&pf)) == NULL) - MGL_fatalError("Unable to create Memory DC!"); - - MGL_makeCurrentDC(dibdc); - - vid.buffer = vid.conbuffer = vid.direct = dibdc->surface; - vid.rowbytes = vid.conrowbytes = dibdc->mi.bytesPerLine; - vid.numpages = 1; - vid.maxwarpwidth = WARP_WIDTH; - vid.maxwarpheight = WARP_HEIGHT; - vid.height = vid.conheight = DIBHeight; - vid.width = vid.conwidth = DIBWidth; - vid.aspect = ((float)vid.height / (float)vid.width) * - (320.0 / 240.0); - - vid_stretched = modelist[modenum].stretched; - -// needed because we're not getting WM_MOVE messages fullscreen on NT - window_x = 0; - window_y = 0; - - return true; -} - - -void VID_RestoreOldMode (int original_mode) -{ - static qboolean inerror = false; - - if (inerror) - return; - - in_mode_set = false; - inerror = true; - -// make sure mode set happens (video mode changes) - vid_modenum = original_mode - 1; - - if (!VID_SetMode (original_mode, vid_curpal)) - { - vid_modenum = MODE_WINDOWED - 1; - - if (!VID_SetMode (windowed_default, vid_curpal)) - Sys_Error ("Can't set any video mode"); - } - - inerror = false; -} - - -void VID_SetDefaultMode (void) -{ - - if (vid_initialized) - VID_SetMode (0, vid_curpal); - - IN_DeactivateMouse (); -} - - -int VID_SetMode (int modenum, unsigned char *palette) -{ - int original_mode, temp /*, dummy */; - qboolean stat; - MSG msg; - HDC hdc; - - while ((modenum >= nummodes) || (modenum < 0)) - { - if (vid_modenum == NO_MODE) - { - if (modenum == vid_default) - { - modenum = windowed_default; - } - else - { - modenum = vid_default; - } - - Cvar_SetValue (&vid_mode, (float)modenum); - } - else - { - Cvar_SetValue (&vid_mode, (float)vid_modenum); - return 0; - } - } - - if (!force_mode_set && (modenum == vid_modenum)) - return true; - -// so Con_Printfs don't mess us up by forcing vid and snd updates - temp = scr_disabled_for_loading; - scr_disabled_for_loading = true; - in_mode_set = true; - - CDAudio_Pause (); - S_ClearBuffer (); - - if (vid_modenum == NO_MODE) - original_mode = windowed_default; - else - original_mode = vid_modenum; - - // Set either the fullscreen or windowed mode - if (modelist[modenum].type == MS_WINDOWED) - { - if (_windowed_mouse.value && key_dest == key_game) - { - stat = VID_SetWindowedMode(modenum); - IN_ActivateMouse (); - IN_HideMouse (); - } - else - { - IN_DeactivateMouse (); - IN_ShowMouse (); - stat = VID_SetWindowedMode(modenum); - } - } - else if (modelist[modenum].type == MS_FULLDIB) - { - stat = VID_SetFullDIBMode(modenum); - IN_ActivateMouse (); - IN_HideMouse (); - } - else - { - stat = VID_SetFullscreenMode(modenum); - IN_ActivateMouse (); - IN_HideMouse (); - } - - window_width = vid.width << vid_stretched; - window_height = vid.height << vid_stretched; - VID_UpdateWindowStatus (); - - CDAudio_Resume (); - scr_disabled_for_loading = temp; - - if (!stat) - { - VID_RestoreOldMode (original_mode); - return false; - } - - if (hide_window) - return true; - -// now we try to make sure we get the focus on the mode switch, because -// sometimes in some systems we don't. We grab the foreground, then -// finish setting up, pump all our messages, and sleep for a little while -// to let messages finish bouncing around the system, then we put -// ourselves at the top of the z order, then grab the foreground again, -// Who knows if it helps, but it probably doesn't hurt - if (!force_minimized) - SetForegroundWindow (mainwindow); - - hdc = GetDC(NULL); - - if (GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) - vid_palettized = true; - else - vid_palettized = false; - - VID_SetPalette (palette); - - ReleaseDC(NULL,hdc); - - vid_modenum = modenum; - Cvar_SetValue (&vid_mode, (float)vid_modenum); - - if (!VID_AllocBuffers (vid.width, vid.height)) - { - // couldn't get memory for this mode; try to fall back to previous mode - VID_RestoreOldMode (original_mode); - return false; - } - - D_InitCaches (vid_surfcache, vid_surfcachesize); - - while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) - { - TranslateMessage (&msg); - DispatchMessage (&msg); - } - - Sleep (100); - - if (!force_minimized) - { - SetWindowPos (mainwindow, HWND_TOP, 0, 0, 0, 0, - SWP_DRAWFRAME | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | - SWP_NOCOPYBITS); - - SetForegroundWindow (mainwindow); - } - -// fix the leftover Alt from any Alt-Tab or the like that switched us away - ClearAllStates (); - - if (!msg_suppress_1) - Con_SafePrintf ("Video mode %s initialized\n", VID_GetModeDescription (vid_modenum)); - - VID_SetPalette (palette); - - in_mode_set = false; - vid.recalc_refdef = 1; - - return true; -} - -void VID_LockBuffer (void) -{ - - if (dibdc) - return; - - lockcount++; - - if (lockcount > 1) - return; - - MGL_beginDirectAccess(); - - if (memdc) - { - // Update surface pointer for linear access modes - vid.buffer = vid.conbuffer = vid.direct = memdc->surface; - vid.rowbytes = vid.conrowbytes = memdc->mi.bytesPerLine; - } - else if (mgldc) - { - // Update surface pointer for linear access modes - vid.buffer = vid.conbuffer = vid.direct = mgldc->surface; - vid.rowbytes = vid.conrowbytes = mgldc->mi.bytesPerLine; - } - - if (r_dowarp) - d_viewbuffer = r_warpbuffer; - else - d_viewbuffer = (void *)(byte *)vid.buffer; - - if (r_dowarp) - screenwidth = WARP_WIDTH; - else - screenwidth = vid.rowbytes; -} - - -void VID_UnlockBuffer (void) -{ - if (dibdc) - return; - - lockcount--; - - if (lockcount > 0) - return; - - if (lockcount < 0) - Sys_Error ("Unbalanced unlock"); - - MGL_endDirectAccess(); - -// to turn up any unlocked accesses - vid.buffer = vid.conbuffer = vid.direct = d_viewbuffer = NULL; - -} - - -int VID_ForceUnlockedAndReturnState (void) -{ - int lk; - - if (!lockcount) - return 0; - - lk = lockcount; - - if (dibdc) - { - lockcount = 0; - } - else - { - lockcount = 1; - VID_UnlockBuffer (); - } - - return lk; -} - - -void VID_ForceLockState (int lk) -{ - - if (!dibdc && lk) - { - lockcount = 0; - VID_LockBuffer (); - } - - lockcount = lk; -} - - -void VID_SetPalette (unsigned char *palette) -{ - INT i; - palette_t pal[256]; - HDC hdc; - - if (!Minimized) - { - palette_changed = true; - - // make sure we have the static colors if we're the active app - hdc = GetDC(NULL); - - if (vid_palettized && ActiveApp) - { - if (GetSystemPaletteUse(hdc) == SYSPAL_STATIC) - { - // switch to SYSPAL_NOSTATIC and remap the colors - SetSystemPaletteUse(hdc, SYSPAL_NOSTATIC); - syscolchg = true; - pal_is_nostatic = true; - } - } - - ReleaseDC(NULL,hdc); - - // Translate the palette values to an MGL palette array and - // set the values. - for (i = 0; i < 256; i++) - { - pal[i].red = palette[i*3]; - pal[i].green = palette[i*3+1]; - pal[i].blue = palette[i*3+2]; - } - - if (DDActive) - { - if (!mgldc) - return; - - MGL_setPalette(mgldc,pal,256,0); - MGL_realizePalette(mgldc,256,0,false); - if (memdc) - MGL_setPalette(memdc,pal,256,0); - } - else - { - if (!windc) - return; - - MGL_setPalette(windc,pal,256,0); - MGL_realizePalette(windc,256,0,false); - if (dibdc) - { - MGL_setPalette(dibdc,pal,256,0); - MGL_realizePalette(dibdc,256,0,false); - } - } - } - - memcpy (vid_curpal, palette, sizeof(vid_curpal)); - - if (syscolchg) - { - PostMessage (HWND_BROADCAST, WM_SYSCOLORCHANGE, (WPARAM)0, (LPARAM)0); - syscolchg = false; - } -} - - -void VID_ShiftPalette (unsigned char *palette) -{ - VID_SetPalette (palette); -} - - -/* -================= -VID_DescribeCurrentMode_f -================= -*/ -void VID_DescribeCurrentMode_f (void) -{ - Con_Printf ("%s\n", VID_GetExtModeDescription (vid_modenum)); -} - - -/* -================= -VID_NumModes_f -================= -*/ -void VID_NumModes_f (void) -{ - - if (nummodes == 1) - Con_Printf ("%d video mode is available\n", nummodes); - else - Con_Printf ("%d video modes are available\n", nummodes); -} - - -/* -================= -VID_DescribeMode_f -================= -*/ -void VID_DescribeMode_f (void) -{ - int modenum; - - modenum = Q_atoi (Cmd_Argv(1)); - - Con_Printf ("%s\n", VID_GetExtModeDescription (modenum)); -} - - -/* -================= -VID_DescribeModes_f -================= -*/ -void VID_DescribeModes_f (void) -{ - int i, lnummodes; - char *pinfo; - qboolean na; - vmode_t *pv; - - na = false; - - lnummodes = VID_NumModes (); - - for (i=0 ; iwidth, pv->height)) - { - Con_Printf ("%2d: %s\n", i, pinfo); - } - else - { - Con_Printf ("**: %s\n", pinfo); - na = true; - } - } - - if (na) - { - Con_Printf ("\n[**: not enough system RAM for mode]\n"); - } -} - - -/* -================= -VID_TestMode_f -================= -*/ -void VID_TestMode_f (void) -{ - int modenum; - double testduration; - - if (!vid_testingmode) - { - modenum = Q_atoi (Cmd_Argv(1)); - - if (VID_SetMode (modenum, vid_curpal)) - { - vid_testingmode = 1; - testduration = Q_atof (Cmd_Argv(2)); - if (testduration == 0) - testduration = 5.0; - vid_testendtime = realtime + testduration; - } - } -} - -/* -================= -VID_Windowed_f -================= -*/ -void VID_Windowed_f (void) -{ - - VID_SetMode ((int)vid_windowed_mode.value, vid_curpal); -} - - -/* -================= -VID_Fullscreen_f -================= -*/ -void VID_Fullscreen_f (void) -{ - - VID_SetMode ((int)vid_fullscreen_mode.value, vid_curpal); -} - -/* -================= -VID_Minimize_f -================= -*/ -void VID_Minimize_f (void) -{ - -// we only support minimizing windows; if you're fullscreen, -// switch to windowed first - if (modestate == MS_WINDOWED) - ShowWindow (mainwindow, SW_MINIMIZE); -} - - - -/* -================= -VID_ForceMode_f -================= -*/ -void VID_ForceMode_f (void) -{ - int modenum; -// double testduration; - - if (!vid_testingmode) - { - modenum = Q_atoi (Cmd_Argv(1)); - - force_mode_set = 1; - VID_SetMode (modenum, vid_curpal); - force_mode_set = 0; - } -} - - -void VID_Init (unsigned char *palette) -{ - int i, bestmatch, bestmatchmetric, t, dr, dg, db; - int basenummodes; - byte *ptmp; - - Cvar_RegisterVariable (&vid_mode); - Cvar_RegisterVariable (&vid_wait); - Cvar_RegisterVariable (&vid_nopageflip); - Cvar_RegisterVariable (&_vid_wait_override); - Cvar_RegisterVariable (&_vid_default_mode); - Cvar_RegisterVariable (&_vid_default_mode_win); - Cvar_RegisterVariable (&vid_config_x); - Cvar_RegisterVariable (&vid_config_y); - Cvar_RegisterVariable (&vid_stretch_by_2); - Cvar_RegisterVariable (&_windowed_mouse); - Cvar_RegisterVariable (&vid_fullscreen_mode); - Cvar_RegisterVariable (&vid_windowed_mode); - Cvar_RegisterVariable (&block_switch); - Cvar_RegisterVariable (&vid_window_x); - Cvar_RegisterVariable (&vid_window_y); - Cvar_RegisterVariable (&vid_resetonswitch); - - Cmd_AddCommand ("vid_testmode", VID_TestMode_f); - Cmd_AddCommand ("vid_nummodes", VID_NumModes_f); - Cmd_AddCommand ("vid_describecurrentmode", VID_DescribeCurrentMode_f); - Cmd_AddCommand ("vid_describemode", VID_DescribeMode_f); - Cmd_AddCommand ("vid_describemodes", VID_DescribeModes_f); - Cmd_AddCommand ("vid_forcemode", VID_ForceMode_f); - Cmd_AddCommand ("vid_windowed", VID_Windowed_f); - Cmd_AddCommand ("vid_fullscreen", VID_Fullscreen_f); - Cmd_AddCommand ("vid_minimize", VID_Minimize_f); - - if (COM_CheckParm ("-dibonly")) - dibonly = true; - - VID_InitMGLDIB (global_hInstance); - - basenummodes = nummodes; - - if (!dibonly) - VID_InitMGLFull (global_hInstance); - -// if there are no non-windowed modes, or only windowed and mode 0x13, then use -// fullscreen DIBs as well - if (((nummodes == basenummodes) || - ((nummodes == (basenummodes + 1)) && is_mode0x13)) && - !COM_CheckParm ("-nofulldib")) - - { - VID_InitFullDIB (global_hInstance); - } - - vid.maxwarpwidth = WARP_WIDTH; - vid.maxwarpheight = WARP_HEIGHT; - vid.colormap = host_colormap; - vid.fullbright = 256 - LittleLong (*((int *)vid.colormap + 2048)); - vid_testingmode = 0; - -// GDI doesn't let us remap palette index 0, so we'll remap color -// mappings from that black to another one - bestmatchmetric = 256*256*3; - - for (i=1 ; i<256 ; i++) - { - dr = palette[0] - palette[i*3]; - dg = palette[1] - palette[i*3+1]; - db = palette[2] - palette[i*3+2]; - - t = (dr * dr) + (dg * dg) + (db * db); - - if (t < bestmatchmetric) - { - bestmatchmetric = t; - bestmatch = i; - - if (t == 0) - break; - } - } - - for (i=0, ptmp = vid.colormap ; i<(1<<(VID_CBITS+8)) ; i++, ptmp++) - { - if (*ptmp == 0) - *ptmp = bestmatch; - } - - if (COM_CheckParm("-startwindowed")) - { - startwindowed = 1; - vid_default = windowed_default; - } - - if (hwnd_dialog) - DestroyWindow (hwnd_dialog); - -// sound initialization has to go here, preceded by a windowed mode set, -// so there's a window for DirectSound to work with but we're not yet -// fullscreen so the "hardware already in use" dialog is visible if it -// gets displayed - -// keep the window minimized until we're ready for the first real mode set - hide_window = true; - VID_SetMode (MODE_WINDOWED, palette); - hide_window = false; - S_Init (); - - vid_initialized = true; - - force_mode_set = true; - VID_SetMode (vid_default, palette); - force_mode_set = false; - - vid_realmode = vid_modenum; - - VID_SetPalette (palette); - - vid_menudrawfn = VID_MenuDraw; - vid_menukeyfn = VID_MenuKey; - - strcpy (badmode.modedesc, "Bad mode"); -} - - -void VID_Shutdown (void) -{ -// HDC hdc; -// int dummy; - - if (vid_initialized) - { - if (modestate == MS_FULLDIB) - ChangeDisplaySettings (NULL, CDS_FULLSCREEN); - - PostMessage (HWND_BROADCAST, WM_PALETTECHANGED, (WPARAM)mainwindow, (LPARAM)0); - PostMessage (HWND_BROADCAST, WM_SYSCOLORCHANGE, (WPARAM)0, (LPARAM)0); - - AppActivate(false, false); - DestroyDIBWindow (); - DestroyFullscreenWindow (); - DestroyFullDIBWindow (); - - if (hwnd_dialog) - DestroyWindow (hwnd_dialog); - - if (mainwindow) - DestroyWindow(mainwindow); - - MGL_exit(); - - vid_testingmode = 0; - vid_initialized = 0; - - // Tonik: this is a workaroung for the bug with garbaged - // screen on Riva TNT - if (modestate == MS_FULLSCREEN && vid_resetonswitch.value) - ChangeDisplaySettings (NULL, CDS_RESET); - } -} - - -/* -================ -FlipScreen -================ -*/ -void FlipScreen(vrect_t *rects) -{ -// HRESULT ddrval; - - // Flip the surfaces - - if (DDActive) - { - if (mgldc) - { - if (memdc) - { - while (rects) - { - if (vid_stretched) - { - MGL_stretchBltCoord(mgldc, memdc, - rects->x, - rects->y, - rects->x + rects->width, - rects->y + rects->height, - rects->x << 1, - rects->y << 1, - (rects->x + rects->width) << 1, - (rects->y + rects->height) << 1); - } - else - { - MGL_bitBltCoord(mgldc, memdc, - rects->x, rects->y, - (rects->x + rects->width), - (rects->y + rects->height), - rects->x, rects->y, MGL_REPLACE_MODE); - } - - rects = rects->pnext; - } - } - - if (vid.numpages > 1) - { - // We have a flipping surface, so do a hard page flip - aPage = (aPage+1) % vid.numpages; - vPage = (vPage+1) % vid.numpages; - MGL_setActivePage(mgldc,aPage); - MGL_setVisualPage(mgldc,vPage,waitVRT); - } - } - } - else - { - HDC hdcScreen; - - hdcScreen = GetDC(mainwindow); - - if (windc && dibdc) - { - MGL_setWinDC(windc,hdcScreen); - - while (rects) - { - if (vid_stretched) - { - MGL_stretchBltCoord(windc,dibdc, - rects->x, rects->y, - rects->x + rects->width, rects->y + rects->height, - rects->x << 1, rects->y << 1, - (rects->x + rects->width) << 1, - (rects->y + rects->height) << 1); - } - else - { - MGL_bitBltCoord(windc,dibdc, - rects->x, rects->y, - rects->x + rects->width, rects->y + rects->height, - rects->x, rects->y, MGL_REPLACE_MODE); - } - - rects = rects->pnext; - } - } - - ReleaseDC(mainwindow, hdcScreen); - } -} - - -void VID_Update (vrect_t *rects) -{ - vrect_t rect; - RECT trect; - - if (!vid_palettized && palette_changed) - { - palette_changed = false; - rect.x = 0; - rect.y = 0; - rect.width = vid.width; - rect.height = vid.height; - rect.pnext = NULL; - rects = ▭ - } - - if (firstupdate) - { - if (modestate == MS_WINDOWED) - { - GetWindowRect (mainwindow, &trect); - - if ((trect.left != (int)vid_window_x.value) || - (trect.top != (int)vid_window_y.value)) - { - if (COM_CheckParm ("-resetwinpos")) - { - Cvar_SetValue (&vid_window_x, 0.0); - Cvar_SetValue (&vid_window_y, 0.0); - } - - VID_CheckWindowXY (); - SetWindowPos (mainwindow, NULL, (int)vid_window_x.value, - (int)vid_window_y.value, 0, 0, - SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW | SWP_DRAWFRAME); - } - } - - if ((_vid_default_mode_win.value != vid_default) && - (!startwindowed || (_vid_default_mode_win.value < MODE_FULLSCREEN_DEFAULT))) - { - firstupdate = 0; - - if (COM_CheckParm ("-resetwinpos")) - { - Cvar_SetValue (&vid_window_x, 0.0); - Cvar_SetValue (&vid_window_y, 0.0); - } - - if ((_vid_default_mode_win.value < 0) || - (_vid_default_mode_win.value >= nummodes)) - { - Cvar_SetValue (&_vid_default_mode_win, windowed_default); - } - - Cvar_SetValue (&vid_mode, _vid_default_mode_win.value); - } - } - - // We've drawn the frame; copy it to the screen - FlipScreen (rects); - - if (vid_testingmode) - { - if (realtime >= vid_testendtime) - { - VID_SetMode (vid_realmode, vid_curpal); - vid_testingmode = 0; - } - } - else - { - if ((int)vid_mode.value != vid_realmode) - { - VID_SetMode ((int)vid_mode.value, vid_curpal); - Cvar_SetValue (&vid_mode, (float)vid_modenum); - // so if mode set fails, we don't keep on - // trying to set that mode - vid_realmode = vid_modenum; - } - } - -// handle the mouse state when windowed if that's changed - if (modestate == MS_WINDOWED) - { - if (!_windowed_mouse.value) { - if (windowed_mouse) { - IN_DeactivateMouse (); - IN_ShowMouse (); - } - windowed_mouse = false; - } else { - windowed_mouse = true; - if (key_dest == key_game && !mouseactive && ActiveApp) { - IN_ActivateMouse (); - IN_HideMouse (); - } else if (mouseactive && key_dest != key_game) { - IN_DeactivateMouse (); - IN_ShowMouse (); - } - } - } -} - - -/* -================ -D_BeginDirectRect -================ -*/ -void D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height) -{ - int i, j, reps, repshift; - vrect_t rect; - - if (!vid_initialized) - return; - - if (vid.aspect > 1.5) - { - reps = 2; - repshift = 1; - } - else - { - reps = 1; - repshift = 0; - } - - if (vid.numpages == 1) - { - VID_LockBuffer (); - - if (!vid.direct) - Sys_Error ("NULL vid.direct pointer"); - - for (i=0 ; i<(height << repshift) ; i += reps) - { - for (j=0 ; j> repshift) * width], - width); - } - } - - VID_UnlockBuffer (); - - rect.x = x; - rect.y = y; - rect.width = width; - rect.height = height << repshift; - rect.pnext = NULL; - - FlipScreen (&rect); - } - else - { - // unlock if locked - if (lockcount > 0) - MGL_endDirectAccess(); - - // set the active page to the displayed page - MGL_setActivePage (mgldc, vPage); - - // lock the screen - MGL_beginDirectAccess (); - - // save from and draw to screen - for (i=0 ; i<(height << repshift) ; i += reps) - { - for (j=0 ; jsurface + x + - ((y << repshift) + i + j) * mgldc->mi.bytesPerLine, - width); - memcpy ((byte *)mgldc->surface + x + - ((y << repshift) + i + j) * mgldc->mi.bytesPerLine, - &pbitmap[(i >> repshift) * width], - width); - } - } - - // unlock the screen - MGL_endDirectAccess (); - - // restore the original active page - MGL_setActivePage (mgldc, aPage); - - // relock the screen if it was locked - if (lockcount > 0) - MGL_beginDirectAccess(); - } -} - - -/* -================ -D_EndDirectRect -================ -*/ -void D_EndDirectRect (int x, int y, int width, int height) -{ - int i, j, reps, repshift; - vrect_t rect; - - if (!vid_initialized) - return; - - if (vid.aspect > 1.5) - { - reps = 2; - repshift = 1; - } - else - { - reps = 1; - repshift = 0; - } - - if (vid.numpages == 1) - { - VID_LockBuffer (); - - if (!vid.direct) - Sys_Error ("NULL vid.direct pointer"); - - for (i=0 ; i<(height << repshift) ; i += reps) - { - for (j=0 ; j 0) - MGL_endDirectAccess(); - - // set the active page to the displayed page - MGL_setActivePage (mgldc, vPage); - - // lock the screen - MGL_beginDirectAccess (); - - // restore to the screen - for (i=0 ; i<(height << repshift) ; i += reps) - { - for (j=0 ; jsurface + x + - ((y << repshift) + i + j) * mgldc->mi.bytesPerLine, - &backingbuf[(i + j) * 24], - width); - } - } - - // unlock the screen - MGL_endDirectAccess (); - - // restore the original active page - MGL_setActivePage (mgldc, aPage); - - // relock the screen if it was locked - if (lockcount > 0) - MGL_beginDirectAccess(); - } -} - - -//========================================================================== - - -void AppActivate(BOOL fActive, BOOL minimize) -/**************************************************************************** -* -* Function: AppActivate -* Parameters: fActive - True if app is activating -* -* Description: If the application is activating, then swap the system -* into SYSPAL_NOSTATIC mode so that our palettes will display -* correctly. -* -****************************************************************************/ -{ - HDC hdc; - int i, t; - static BOOL sound_active; - - ActiveApp = fActive; - -// messy, but it seems to work - if (vid_fulldib_on_focus_mode) - { - Minimized = minimize; - - if (Minimized) - ActiveApp = false; - } - - MGL_appActivate(windc, ActiveApp); - - if (vid_initialized) - { - // yield the palette if we're losing the focus - hdc = GetDC(NULL); - - if (GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) - { - if (ActiveApp) - { - if ((modestate == MS_WINDOWED) || (modestate == MS_FULLDIB)) - { - if (GetSystemPaletteUse(hdc) == SYSPAL_STATIC) - { - // switch to SYSPAL_NOSTATIC and remap the colors - SetSystemPaletteUse(hdc, SYSPAL_NOSTATIC); - syscolchg = true; - pal_is_nostatic = true; - } - } - } - else if (pal_is_nostatic) - { - if (GetSystemPaletteUse(hdc) == SYSPAL_NOSTATIC) - { - // switch back to SYSPAL_STATIC and the old mapping - SetSystemPaletteUse(hdc, SYSPAL_STATIC); - syscolchg = true; - } - - pal_is_nostatic = false; - } - } - - if (!Minimized) - VID_SetPalette (vid_curpal); - - scr_fullupdate = 0; - - ReleaseDC(NULL,hdc); - } - -// enable/disable sound on focus gain/loss - if (!ActiveApp && sound_active) - { - S_BlockSound (); - S_ClearBuffer (); - sound_active = false; - } - else if (ActiveApp && !sound_active) - { - S_UnblockSound (); - S_ClearBuffer (); - sound_active = true; - } - -// minimize/restore fulldib windows/mouse-capture normal windows on demand - if (!in_mode_set) - { - if (ActiveApp) - { - if (vid_fulldib_on_focus_mode) - { - if (vid_initialized) - { - msg_suppress_1 = true; // don't want to see normal mode set message - VID_SetMode (vid_fulldib_on_focus_mode, vid_curpal); - msg_suppress_1 = false; - - t = in_mode_set; - in_mode_set = true; - AppActivate (true, false); - in_mode_set = t; - } - - IN_ActivateMouse (); - IN_HideMouse (); - } - else if ((modestate == MS_WINDOWED) && _windowed_mouse.value && key_dest == key_game) - { - IN_ActivateMouse (); - IN_HideMouse (); - } - } - - if (!ActiveApp) - { - if (modestate == MS_FULLDIB) - { - if (vid_initialized) - { - force_minimized = true; - i = vid_fulldib_on_focus_mode; - msg_suppress_1 = true; // don't want to see normal mode set message - VID_SetMode (windowed_default, vid_curpal); - msg_suppress_1 = false; - vid_fulldib_on_focus_mode = i; - force_minimized = false; - - // we never seem to get WM_ACTIVATE inactive from this mode set, so we'll - // do it manually - t = in_mode_set; - in_mode_set = true; - AppActivate (false, true); - in_mode_set = t; - } - - IN_DeactivateMouse (); - IN_ShowMouse (); - } - else if ((modestate == MS_WINDOWED) && _windowed_mouse.value /* && mouseactive */) - { - IN_DeactivateMouse (); - IN_ShowMouse (); - } - } - } -} - - -/* -================ -VID_HandlePause -================ -*/ -void VID_HandlePause (qboolean pause) -{ -#if 0 - if ((modestate == MS_WINDOWED) && _windowed_mouse.value) - { - if (pause) - { - IN_DeactivateMouse (); - IN_ShowMouse (); - } - else - { - IN_ActivateMouse (); - IN_HideMouse (); - } - } -#endif -} - - -/* -=================================================================== - -MAIN WINDOW - -=================================================================== -*/ - -LONG CDAudio_MessageHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); - -int IN_MapKey (int key); - -/* main window procedure */ -LONG WINAPI MainWndProc ( - HWND hWnd, - UINT uMsg, - WPARAM wParam, - LPARAM lParam) -{ - LONG lRet = 0; - int /* fwKeys, xPos, yPos,*/ fActive, fMinimized, temp; - HDC hdc; - PAINTSTRUCT ps; - extern unsigned int uiWheelMessage; - static int recursiveflag; - - if ( uMsg == uiWheelMessage ) { - uMsg = WM_MOUSEWHEEL; - wParam <<= 16; - } - - - switch (uMsg) - { - case WM_CREATE: - break; - - case WM_SYSCOMMAND: - - // Check for maximize being hit - switch (wParam & ~0x0F) - { - case SC_MAXIMIZE: - // if minimized, bring up as a window before going fullscreen, - // so MGL will have the right state to restore - if (Minimized) - { - force_mode_set = true; - VID_SetMode (vid_modenum, vid_curpal); - force_mode_set = false; - } - - VID_SetMode ((int)vid_fullscreen_mode.value, vid_curpal); - break; - - case SC_SCREENSAVE: - case SC_MONITORPOWER: - if (modestate != MS_WINDOWED) - { - // don't call DefWindowProc() because we don't want to start - // the screen saver fullscreen - break; - } - - // fall through windowed and allow the screen saver to start - - default: - if (!in_mode_set) - { - S_BlockSound (); - S_ClearBuffer (); - } - - lRet = DefWindowProc (hWnd, uMsg, wParam, lParam); - - if (!in_mode_set) - { - S_UnblockSound (); - } - } - break; - - case WM_MOVE: - window_x = (int) LOWORD(lParam); - window_y = (int) HIWORD(lParam); - VID_UpdateWindowStatus (); - - if ((modestate == MS_WINDOWED) && !in_mode_set && !Minimized) - VID_RememberWindowPos (); - - break; - - case WM_SIZE: - Minimized = false; - - if (!(wParam & SIZE_RESTORED)) - { - if (wParam & SIZE_MINIMIZED) - Minimized = true; - } - break; - - case WM_SYSCHAR: - // keep Alt-Space from happening - break; - - case WM_ACTIVATE: - fActive = LOWORD(wParam); - fMinimized = (BOOL) HIWORD(wParam); - AppActivate(!(fActive == WA_INACTIVE), fMinimized); - - // Tonik: this is a workaroung for the bug with garbaged - // screen on Riva TNT - if (fActive == WA_INACTIVE && modestate == MS_FULLSCREEN - && vid_resetonswitch.value) - ChangeDisplaySettings (NULL, CDS_RESET); - - // fix the leftover Alt from any Alt-Tab or the like that switched us away - ClearAllStates (); - - if (!in_mode_set) - { - if (windc) - MGL_activatePalette(windc,true); - - VID_SetPalette(vid_curpal); - } - - break; - - case WM_PAINT: - hdc = BeginPaint(hWnd, &ps); - - if (!in_mode_set && host_initialized) - SCR_UpdateWholeScreen (); - - EndPaint(hWnd, &ps); - break; - - case WM_KEYDOWN: - case WM_SYSKEYDOWN: - if (!in_mode_set) - Key_Event (IN_MapKey(lParam), true); - break; - - case WM_KEYUP: - case WM_SYSKEYUP: - if (!in_mode_set) - Key_Event (IN_MapKey(lParam), false); - break; - - // this is complicated because Win32 seems to pack multiple mouse events into - // one update sometimes, so we always check all states and look for events - case WM_LBUTTONDOWN: - case WM_LBUTTONUP: - case WM_RBUTTONDOWN: - case WM_RBUTTONUP: - case WM_MBUTTONDOWN: - case WM_MBUTTONUP: - case WM_MOUSEMOVE: - if (!in_mode_set) - { - temp = 0; - - if (wParam & MK_LBUTTON) - temp |= 1; - - if (wParam & MK_RBUTTON) - temp |= 2; - - if (wParam & MK_MBUTTON) - temp |= 4; - - IN_MouseEvent (temp); - } - break; - // JACK: This is the mouse wheel with the Intellimouse - // Its delta is either positive or neg, and we generate the proper - // Event. - case WM_MOUSEWHEEL: - if ((short) HIWORD(wParam) > 0) { - Key_Event(K_MWHEELUP, true); - Key_Event(K_MWHEELUP, false); - } else { - Key_Event(K_MWHEELDOWN, true); - Key_Event(K_MWHEELDOWN, false); - } - break; - // KJB: Added these new palette functions - case WM_PALETTECHANGED: - if ((HWND)wParam == hWnd) - break; - /* Fall through to WM_QUERYNEWPALETTE */ - case WM_QUERYNEWPALETTE: - hdc = GetDC(NULL); - - if (GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) - vid_palettized = true; - else - vid_palettized = false; - - ReleaseDC(NULL,hdc); - - scr_fullupdate = 0; - - if (vid_initialized && !in_mode_set && windc && MGL_activatePalette(windc,false) && !Minimized) - { - VID_SetPalette (vid_curpal); - InvalidateRect (mainwindow, NULL, false); - - // specifically required if WM_QUERYNEWPALETTE realizes a new palette - lRet = TRUE; - } - break; - - case WM_DISPLAYCHANGE: - if (!in_mode_set && (modestate == MS_WINDOWED) && !vid_fulldib_on_focus_mode) - { - force_mode_set = true; - VID_SetMode (vid_modenum, vid_curpal); - force_mode_set = false; - } - break; - - case WM_CLOSE: - // this causes Close in the right-click task bar menu not to work, but right - // now bad things happen if Close is handled in that case (garbage and a - // crash on Win95) - if (!in_mode_set) - { - if (MessageBox (mainwindow, "Are you sure you want to quit?", "Confirm Exit", - MB_YESNO | MB_SETFOREGROUND | MB_ICONQUESTION) == IDYES) - { - Sys_Quit (); - } - } - break; - - case MM_MCINOTIFY: - lRet = CDAudio_MessageHandler (hWnd, uMsg, wParam, lParam); - break; - - default: - /* pass all unhandled messages to DefWindowProc */ - lRet = DefWindowProc (hWnd, uMsg, wParam, lParam); - break; - } - - /* return 0 if handled message, 1 if not */ - return lRet; -} - - -extern void M_Menu_Options_f (void); -extern void M_Print (int cx, int cy, char *str); -extern void M_PrintWhite (int cx, int cy, char *str); -extern void M_DrawCharacter (int cx, int line, int num); -extern void M_DrawTransPic (int x, int y, qpic_t *pic); -extern void M_DrawPic (int x, int y, qpic_t *pic); - -static int vid_line, vid_wmodes; - -typedef struct -{ - int modenum; - char *desc; - int iscur; - int ismode13; - int width; -} modedesc_t; - -#define MAX_COLUMN_SIZE 5 -#define MODE_AREA_HEIGHT (MAX_COLUMN_SIZE + 6) -#define MAX_MODEDESCS (MAX_COLUMN_SIZE*3) - -static modedesc_t modedescs[MAX_MODEDESCS]; - -/* -================ -VID_MenuDraw -================ -*/ -void VID_MenuDraw (void) -{ - qpic_t *p; - char *ptr; - int lnummodes, i, j, k, column, row, dup, dupmode; - char temp[100]; - vmode_t *pv; - modedesc_t tmodedesc; - - p = Draw_CachePic ("gfx/vidmodes.lmp"); - M_DrawPic ( (320-p->width)/2, 4, p); - - for (i=0 ; i<3 ; i++) - { - ptr = VID_GetModeDescriptionMemCheck (i); - modedescs[i].modenum = modelist[i].modenum; - modedescs[i].desc = ptr; - modedescs[i].ismode13 = 0; - modedescs[i].iscur = 0; - - if (vid_modenum == i) - modedescs[i].iscur = 1; - } - - vid_wmodes = 3; - lnummodes = VID_NumModes (); - - for (i=3 ; iwidth != 360) || COM_CheckParm("-allow360"))) - { - dup = 0; - - for (j=3 ; jmode13; - modedescs[k].iscur = 0; - modedescs[k].width = pv->width; - - if (i == vid_modenum) - modedescs[k].iscur = 1; - - if (!dup) - vid_wmodes++; - } - } - } - } - -// sort the modes on width (to handle picking up oddball dibonly modes -// after all the others) - for (i=3 ; i<(vid_wmodes-1) ; i++) - { - for (j=(i+1) ; j modedescs[j].width) - { - tmodedesc = modedescs[i]; - modedescs[i] = modedescs[j]; - modedescs[j] = tmodedesc; - } - } - } - - - M_Print (13*8, 36, "Windowed Modes"); - - column = 16; - row = 36+2*8; - - for (i=0 ; i<3; i++) - { - if (modedescs[i].iscur) - M_PrintWhite (column, row, modedescs[i].desc); - else - M_Print (column, row, modedescs[i].desc); - - column += 13*8; - } - - if (vid_wmodes > 3) - { - M_Print (12*8, 36+4*8, "Fullscreen Modes"); - - column = 16; - row = 36+6*8; - - for (i=3 ; i= 3) - row += 3*8; - - M_DrawCharacter (column, row, 12+((int)(realtime*4)&1)); - } -} - - -/* -================ -VID_MenuKey -================ -*/ -void VID_MenuKey (int key) -{ - if (vid_testingmode) - return; - - switch (key) - { - case K_ESCAPE: - S_LocalSound ("misc/menu1.wav"); - M_Menu_Options_f (); - break; - - case K_LEFTARROW: - - S_LocalSound ("misc/menu1.wav"); - vid_line = ((vid_line / VID_ROW_SIZE) * VID_ROW_SIZE) + - ((vid_line + 2) % VID_ROW_SIZE); - - if (vid_line >= vid_wmodes) - vid_line = vid_wmodes - 1; - break; - - case K_RIGHTARROW: - S_LocalSound ("misc/menu1.wav"); - vid_line = ((vid_line / VID_ROW_SIZE) * VID_ROW_SIZE) + - ((vid_line + 4) % VID_ROW_SIZE); - - if (vid_line >= vid_wmodes) - vid_line = (vid_line / VID_ROW_SIZE) * VID_ROW_SIZE; - break; - - case K_UPARROW: - S_LocalSound ("misc/menu1.wav"); - vid_line -= VID_ROW_SIZE; - - if (vid_line < 0) - { - vid_line += ((vid_wmodes + (VID_ROW_SIZE - 1)) / - VID_ROW_SIZE) * VID_ROW_SIZE; - - while (vid_line >= vid_wmodes) - vid_line -= VID_ROW_SIZE; - } - break; - - case K_DOWNARROW: - S_LocalSound ("misc/menu1.wav"); - vid_line += VID_ROW_SIZE; - - if (vid_line >= vid_wmodes) - { - vid_line -= ((vid_wmodes + (VID_ROW_SIZE - 1)) / - VID_ROW_SIZE) * VID_ROW_SIZE; - - while (vid_line < 0) - vid_line += VID_ROW_SIZE; - } - break; - - case K_ENTER: - S_LocalSound ("misc/menu1.wav"); - VID_SetMode (modedescs[vid_line].modenum, vid_curpal); - break; - - case 'T': - case 't': - S_LocalSound ("misc/menu1.wav"); - // have to set this before setting the mode because WM_PAINT - // happens during the mode set and does a VID_Update, which - // checks vid_testingmode - vid_testingmode = 1; - vid_testendtime = realtime + 5.0; - - if (!VID_SetMode (modedescs[vid_line].modenum, vid_curpal)) - { - vid_testingmode = 0; - } - break; - - case 'D': - case 'd': - S_LocalSound ("misc/menu1.wav"); - firstupdate = 0; - Cvar_SetValue (&_vid_default_mode_win, vid_modenum); - break; - - default: - break; - } -} +/* +Copyright (C) 1996-1997 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. + +*/ +// vid_win.c -- Win32 video driver + +#include "quakedef.h" +#include "winquake.h" +#include "d_local.h" +#include "keys.h" +#include "resource.h" +#include "sound.h" + +#define MINIMUM_MEMORY 0x550000 + +#define MAX_MODE_LIST 30 +#define VID_ROW_SIZE 3 + +qboolean dibonly; + +extern int Minimized; + +HWND mainwindow; + +HWND WINAPI InitializeWindow (HINSTANCE hInstance, int nCmdShow); + +int DIBWidth, DIBHeight; +qboolean DDActive; +RECT WindowRect; +DWORD WindowStyle, ExWindowStyle; + +int window_center_x, window_center_y, window_x, window_y, window_width, window_height; +RECT window_rect; + +static DEVMODE gdevmode; +static qboolean startwindowed = 0, windowed_mode_set; +static int firstupdate = 1; +static qboolean vid_initialized = false, vid_palettized; +static int lockcount; +static int vid_fulldib_on_focus_mode; +static qboolean force_minimized, in_mode_set, is_mode0x13, force_mode_set; +static int vid_stretched, windowed_mouse; +static qboolean palette_changed, syscolchg, vid_mode_set, hide_window, pal_is_nostatic; +static HICON hIcon; +extern qboolean mouseactive; // from in_win.c + +#define MODE_WINDOWED 0 +#define MODE_SETTABLE_WINDOW 2 +#define NO_MODE (MODE_WINDOWED - 1) +#define MODE_FULLSCREEN_DEFAULT (MODE_WINDOWED + 3) + +// Note that 0 is MODE_WINDOWED +cvar_t vid_mode = {"vid_mode","0"}; +// Note that 0 is MODE_WINDOWED +cvar_t _vid_default_mode = {"_vid_default_mode","0",CVAR_ARCHIVE}; +// Note that 3 is MODE_FULLSCREEN_DEFAULT +cvar_t _vid_default_mode_win = {"_vid_default_mode_win","3",CVAR_ARCHIVE}; +cvar_t vid_wait = {"vid_wait","0"}; +cvar_t vid_nopageflip = {"vid_nopageflip","0",CVAR_ARCHIVE}; +cvar_t _vid_wait_override = {"_vid_wait_override","0",CVAR_ARCHIVE}; +cvar_t vid_config_x = {"vid_config_x","800",CVAR_ARCHIVE}; +cvar_t vid_config_y = {"vid_config_y","600",CVAR_ARCHIVE}; +cvar_t vid_stretch_by_2 = {"vid_stretch_by_2","1",CVAR_ARCHIVE}; +cvar_t _windowed_mouse = {"_windowed_mouse","0",CVAR_ARCHIVE}; +cvar_t vid_fullscreen_mode = {"vid_fullscreen_mode","3",CVAR_ARCHIVE}; +cvar_t vid_windowed_mode = {"vid_windowed_mode","0",CVAR_ARCHIVE}; +cvar_t block_switch = {"block_switch","0",CVAR_ARCHIVE}; +cvar_t vid_window_x = {"vid_window_x","0",CVAR_ARCHIVE}; +cvar_t vid_window_y = {"vid_window_y","0",CVAR_ARCHIVE}; +cvar_t vid_resetonswitch = {"vid_resetonswitch","0",CVAR_ARCHIVE}; + +typedef struct { + int width; + int height; +} lmode_t; + +lmode_t lowresmodes[] = { + {320, 200}, + {320, 240}, + {400, 300}, + {512, 384}, +}; + +int vid_modenum = NO_MODE; +int vid_testingmode, vid_realmode; +double vid_testendtime; +int vid_default = MODE_WINDOWED; +static int windowed_default; + +modestate_t modestate = MS_UNINIT; + +static byte *vid_surfcache; +static int vid_surfcachesize; +static int VID_highhunkmark; + +unsigned char vid_curpal[256*3]; + +unsigned short d_8to16table[256]; +unsigned d_8to24table[256]; + +int driver = grDETECT,mode; +qboolean useWinDirect = true, useDirectDraw = true; +MGLDC *mgldc = NULL,*memdc = NULL,*dibdc = NULL,*windc = NULL; + +typedef struct { + modestate_t type; + int width; + int height; + int modenum; + int mode13; + int stretched; + int dib; + int fullscreen; + int bpp; + int halfscreen; + char modedesc[13]; +} vmode_t; + +static vmode_t modelist[MAX_MODE_LIST]; +static int nummodes; +static vmode_t *pcurrentmode; + +int aPage; // Current active display page +int vPage; // Current visible display page +int waitVRT = true; // True to wait for retrace on flip + +static vmode_t badmode; + +static byte backingbuf[48*24]; + +void VID_MenuDraw (void); +void VID_MenuKey (int key); + +LONG WINAPI MainWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); +void AppActivate(BOOL fActive, BOOL minimize); + + +/* +================ +VID_RememberWindowPos +================ +*/ +void VID_RememberWindowPos (void) +{ + RECT rect; + + if (GetWindowRect (mainwindow, &rect)) + { + if ((rect.left < GetSystemMetrics (SM_CXSCREEN)) && + (rect.top < GetSystemMetrics (SM_CYSCREEN)) && + (rect.right > 0) && + (rect.bottom > 0)) + { + Cvar_SetValue (&vid_window_x, (float)rect.left); + Cvar_SetValue (&vid_window_y, (float)rect.top); + } + } +} + + +/* +================ +VID_CheckWindowXY +================ +*/ +void VID_CheckWindowXY (void) +{ + + if (((int)vid_window_x.value > (GetSystemMetrics (SM_CXSCREEN) - 160)) || + ((int)vid_window_y.value > (GetSystemMetrics (SM_CYSCREEN) - 120)) || + ((int)vid_window_x.value < 0) || + ((int)vid_window_y.value < 0)) + { + Cvar_SetValue (&vid_window_x, 0.0); + Cvar_SetValue (&vid_window_y, 0.0 ); + } +} + + +/* +================ +VID_UpdateWindowStatus +================ +*/ +void VID_UpdateWindowStatus (void) +{ + + window_rect.left = window_x; + window_rect.top = window_y; + window_rect.right = window_x + window_width; + window_rect.bottom = window_y + window_height; + window_center_x = (window_rect.left + window_rect.right) / 2; + window_center_y = (window_rect.top + window_rect.bottom) / 2; + + IN_UpdateClipCursor (); +} + + +/* +================ +ClearAllStates +================ +*/ +void ClearAllStates (void) +{ + extern void IN_ClearStates (void); + extern qboolean keydown[256]; + int i; + +// send an up event for each key, to make sure the server clears them all + for (i=0 ; i<256 ; i++) + { + if (keydown[i]) + Key_Event (i, false); + } + + Key_ClearStates (); + IN_ClearStates (); +} + + +/* +================ +VID_CheckAdequateMem +================ +*/ +qboolean VID_CheckAdequateMem (int width, int height) +{ + int tbuffersize; + + tbuffersize = width * height * sizeof (*d_pzbuffer); + + tbuffersize += D_SurfaceCacheForRes (width, height); + +// see if there's enough memory, allowing for the normal mode 0x13 pixel, +// z, and surface buffers + if ((host_parms.memsize - tbuffersize + SURFCACHE_SIZE_AT_320X200 + + 0x10000 * 3) < MINIMUM_MEMORY) + { + return false; // not enough memory for mode + } + + return true; +} + + +/* +================ +VID_AllocBuffers +================ +*/ +qboolean VID_AllocBuffers (int width, int height) +{ + int tsize, tbuffersize; + + tbuffersize = width * height * sizeof (*d_pzbuffer); + + tsize = D_SurfaceCacheForRes (width, height); + + tbuffersize += tsize; + +// see if there's enough memory, allowing for the normal mode 0x13 pixel, +// z, and surface buffers + if ((host_parms.memsize - tbuffersize + SURFCACHE_SIZE_AT_320X200 + + 0x10000 * 3) < MINIMUM_MEMORY) + { + Con_SafePrintf ("Not enough memory for video mode\n"); + return false; // not enough memory for mode + } + + vid_surfcachesize = tsize; + + if (d_pzbuffer) + { + D_FlushCaches (); + Hunk_FreeToHighMark (VID_highhunkmark); + d_pzbuffer = NULL; + } + + VID_highhunkmark = Hunk_HighMark (); + + d_pzbuffer = Hunk_HighAllocName (tbuffersize, "video"); + + vid_surfcache = (byte *)d_pzbuffer + + width * height * sizeof (*d_pzbuffer); + + return true; +} + + +void initFatalError(void) +{ + MGL_exit(); + MGL_fatalError(MGL_errorMsg(MGL_result())); + exit(EXIT_FAILURE); +} + +#if 0 //def NEW_SUSPEND + +int VID_Suspend (MGLDC *dc, int flags) +{ + int i; + if (flags & MGL_DEACTIVATE) + { + IN_RestoreOriginalMouseState (); + CDAudio_Pause (); + + // keep WM_PAINT from trying to redraw + in_mode_set = true; + block_drawing = true; + } + else if (flags & MGL_REACTIVATE) + { + IN_SetQuakeMouseState (); + // fix the leftover Alt from any Alt-Tab or the like that switched us away + ClearAllStates (); + CDAudio_Resume (); + in_mode_set = false; + + block_drawing = false; +// vid.recalc_refdef = 1; + force_mode_set = 1; + i = msg_suppress_1; + msg_suppress_1 = 1; + VID_Fullscreen_f(); + msg_suppress_1 = i; + force_mode_set = 0; + } + + return 1; +} + +#else + +int VID_Suspend (MGLDC *dc, int flags) +{ + + if (flags & MGL_DEACTIVATE) + { + // FIXME: this doesn't currently work on NT + if (block_switch.value && !WinNT) + { + return MGL_NO_DEACTIVATE; + } + + S_BlockSound (); + S_ClearBuffer (); + + IN_RestoreOriginalMouseState (); + CDAudio_Pause (); + + // keep WM_PAINT from trying to redraw + in_mode_set = true; + + block_drawing = true; // so we don't try to draw while switched away + + return MGL_NO_SUSPEND_APP; + } + else if (flags & MGL_REACTIVATE) + { + IN_SetQuakeMouseState (); + // fix the leftover Alt from any Alt-Tab or the like that switched us away + ClearAllStates (); + CDAudio_Resume (); + S_UnblockSound (); + + in_mode_set = false; + + vid.recalc_refdef = 1; + + block_drawing = false; + + return MGL_NO_SUSPEND_APP; + } + + return MGL_NO_SUSPEND_APP; +} +#endif + + +void registerAllDispDrivers(void) +{ + /* Event though these driver require WinDirect, we register + * them so that they will still be available even if DirectDraw + * is present and the user has disable the high performance + * WinDirect modes. + */ + MGL_registerDriver(MGL_VGA8NAME,VGA8_driver); +// MGL_registerDriver(MGL_VGAXNAME,VGAX_driver); + + /* Register display drivers */ + if (useWinDirect) + { +//we don't want VESA 1.X drivers MGL_registerDriver(MGL_SVGA8NAME,SVGA8_driver); + MGL_registerDriver(MGL_LINEAR8NAME,LINEAR8_driver); + + if (!COM_CheckParm ("-novbeaf")) + MGL_registerDriver(MGL_ACCEL8NAME,ACCEL8_driver); + } + + if (useDirectDraw) + { + MGL_registerDriver(MGL_DDRAW8NAME,DDRAW8_driver); + } +} + + +void registerAllMemDrivers(void) +{ + /* Register memory context drivers */ + MGL_registerDriver(MGL_PACKED8NAME,PACKED8_driver); +} + + +void VID_InitMGLFull (HINSTANCE hInstance) +{ + int i, xRes, yRes, bits, /*vMode,*/ lowres, curmode, temp; + int lowstretchedres, stretchedmode, lowstretched; + uchar *m; + +// FIXME: NT is checked for because MGL currently has a bug that causes it +// to try to use WinDirect modes even on NT + if (COM_CheckParm("-nowindirect") || + COM_CheckParm("-nowd") || + COM_CheckParm("-novesa") || + WinNT) + { + useWinDirect = false; + } + + if (COM_CheckParm("-nodirectdraw") || COM_CheckParm("-noddraw") || COM_CheckParm("-nodd")) + useDirectDraw = false; + + // Initialise the MGL + MGL_unregisterAllDrivers(); + registerAllDispDrivers(); + registerAllMemDrivers(); + MGL_detectGraph(&driver,&mode); + m = MGL_availableModes(); + + if (m[0] != 0xFF) + { + lowres = lowstretchedres = 99999; + lowstretched = 0; + curmode = 0; + + // find the lowest-res mode, or a mode we can stretch up to and get + // lowest-res that way + for (i = 0; m[i] != 0xFF; i++) + { + MGL_modeResolution(m[i], &xRes, &yRes,&bits); + + if ((bits == 8) && + (xRes <= MAXWIDTH) && + (yRes <= MAXHEIGHT) && + (curmode < MAX_MODE_LIST)) + { + if (m[i] == grVGA_320x200x256) + is_mode0x13 = true; + + if (!COM_CheckParm("-noforcevga")) + { + if (m[i] == grVGA_320x200x256) + { + mode = i; + break; + } + } + + if (xRes < lowres) + { + lowres = xRes; + mode = i; + } + + if ((xRes < lowstretchedres) && ((xRes >> 1) >= 320)) + { + lowstretchedres = xRes >> 1; + stretchedmode = i; + } + } + + curmode++; + } + + // if there's a mode we can stretch by 2 up to, thereby effectively getting + // a lower-res mode than the lowest-res real but still at least 320x200, that + // will be our default mode + if (lowstretchedres < lowres) + { + mode = stretchedmode; + lowres = lowstretchedres; + lowstretched = 1; + } + + // build the mode list, leaving room for the low-res stretched mode, if any + nummodes++; // leave room for default mode + + for (i = 0; m[i] != 0xFF; i++) + { + MGL_modeResolution(m[i], &xRes, &yRes,&bits); + + if ((bits == 8) && + (xRes <= MAXWIDTH) && + (yRes <= MAXHEIGHT) && + (nummodes < MAX_MODE_LIST)) + { + if (i == mode) + { + if (lowstretched) + { + stretchedmode = nummodes; + curmode = nummodes++; + } + else + { + curmode = MODE_FULLSCREEN_DEFAULT; + } + } + else + { + curmode = nummodes++; + } + + modelist[curmode].type = MS_FULLSCREEN; + modelist[curmode].width = xRes; + modelist[curmode].height = yRes; + sprintf (modelist[curmode].modedesc, "%dx%d", xRes, yRes); + + if (m[i] == grVGA_320x200x256) + modelist[curmode].mode13 = 1; + else + modelist[curmode].mode13 = 0; + + modelist[curmode].modenum = m[i]; + modelist[curmode].stretched = 0; + modelist[curmode].dib = 0; + modelist[curmode].fullscreen = 1; + modelist[curmode].halfscreen = 0; + modelist[curmode].bpp = 8; + } + } + + if (lowstretched) + { + modelist[MODE_FULLSCREEN_DEFAULT] = modelist[stretchedmode]; + modelist[MODE_FULLSCREEN_DEFAULT].stretched = 1; + modelist[MODE_FULLSCREEN_DEFAULT].width >>= 1; + modelist[MODE_FULLSCREEN_DEFAULT].height >>= 1; + sprintf (modelist[MODE_FULLSCREEN_DEFAULT].modedesc, "%dx%d", + modelist[MODE_FULLSCREEN_DEFAULT].width, + modelist[MODE_FULLSCREEN_DEFAULT].height); + } + + vid_default = MODE_FULLSCREEN_DEFAULT; + + temp = m[0]; + + if (!MGL_init(&driver, &temp, "")) + { + initFatalError(); + } + } + + MGL_setSuspendAppCallback(VID_Suspend); +} + + +MGLDC *createDisplayDC(int forcemem) +/**************************************************************************** +* +* Function: createDisplayDC +* Returns: Pointer to the MGL device context to use for the application +* +* Description: Initialises the MGL and creates an appropriate display +* device context to be used by the GUI. This creates and +* apropriate device context depending on the system being +* compile for, and should be the only place where system +* specific code is required. +* +****************************************************************************/ +{ + MGLDC *dc; + pixel_format_t pf; + int npages; + + // Start the specified video mode + if (!MGL_changeDisplayMode(mode)) + initFatalError(); + + npages = MGL_availablePages(mode); + + if (npages > 3) + npages = 3; + + if (!COM_CheckParm ("-notriplebuf")) + { + if (npages > 2) + { + npages = 2; + } + } + + if ((dc = MGL_createDisplayDC(npages)) == NULL) + return NULL; + + if (!forcemem && (MGL_surfaceAccessType(dc)) == MGL_LINEAR_ACCESS && (dc->mi.maxPage > 0)) + { + MGL_makeCurrentDC(dc); + memdc = NULL; + } + else + { + // Set up for blitting from a memory buffer + memdc = MGL_createMemoryDC(MGL_sizex(dc)+1,MGL_sizey(dc)+1,8,&pf); + MGL_makeCurrentDC(memdc); + } + + // Enable page flipping even for even for blitted surfaces + if (forcemem) + { + vid.numpages = 1; + } + else + { + vid.numpages = dc->mi.maxPage + 1; + + if (vid.numpages > 1) + { + // Set up for page flipping + MGL_setActivePage(dc, aPage = 1); + MGL_setVisualPage(dc, vPage = 0, false); + } + + if (vid.numpages > 3) + vid.numpages = 3; + } + + if (vid.numpages == 2) + waitVRT = true; + else + waitVRT = false; + + return dc; +} + + +void VID_InitMGLDIB (HINSTANCE hInstance) +{ + WNDCLASS wc; + HDC hdc; + + hIcon = LoadIcon (hInstance, MAKEINTRESOURCE (IDI_ICON2)); + + /* Register the frame class */ + wc.style = 0; + wc.lpfnWndProc = (WNDPROC)MainWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = hInstance; + wc.hIcon = 0; + wc.hCursor = LoadCursor (NULL,IDC_ARROW); + wc.hbrBackground = NULL; + wc.lpszMenuName = 0; + wc.lpszClassName = "WinQuake"; + + if (!RegisterClass (&wc) ) + Sys_Error ("Couldn't register window class"); + + /* Find the size for the DIB window */ + /* Initialise the MGL for windowed operation */ + MGL_setAppInstance(hInstance); + registerAllMemDrivers(); + MGL_initWindowed(""); + + modelist[0].type = MS_WINDOWED; + modelist[0].width = 320; + modelist[0].height = 240; + strcpy (modelist[0].modedesc, "320x240"); + modelist[0].mode13 = 0; + modelist[0].modenum = MODE_WINDOWED; + modelist[0].stretched = 0; + modelist[0].dib = 1; + modelist[0].fullscreen = 0; + modelist[0].halfscreen = 0; + modelist[0].bpp = 8; + + modelist[1].type = MS_WINDOWED; + modelist[1].width = 640; + modelist[1].height = 480; + strcpy (modelist[1].modedesc, "640x480"); + modelist[1].mode13 = 0; + modelist[1].modenum = MODE_WINDOWED + 1; + modelist[1].stretched = 1; + modelist[1].dib = 1; + modelist[1].fullscreen = 0; + modelist[1].halfscreen = 0; + modelist[1].bpp = 8; + + modelist[2].type = MS_WINDOWED; + modelist[2].width = 800; + modelist[2].height = 600; + strcpy (modelist[2].modedesc, "800x600"); + modelist[2].mode13 = 0; + modelist[2].modenum = MODE_WINDOWED + 2; + modelist[2].stretched = 1; + modelist[2].dib = 1; + modelist[2].fullscreen = 0; + modelist[2].halfscreen = 0; + modelist[2].bpp = 8; + +// automatically stretch the default mode up if > 640x480 desktop resolution + hdc = GetDC(NULL); + + if ((GetDeviceCaps(hdc, HORZRES) > 640) && !COM_CheckParm("-noautostretch")) + { + vid_default = MODE_WINDOWED + 1; + } + else + { + vid_default = MODE_WINDOWED; + } + + windowed_default = vid_default; + + ReleaseDC(NULL,hdc); + + nummodes = 3; // reserve space for windowed mode + + DDActive = 0; +} + + +/* +================= +VID_InitFullDIB +================= +*/ +void VID_InitFullDIB (HINSTANCE hInstance) +{ + DEVMODE devmode; + int i, j, modenum, /*cmodes,*/ existingmode, originalnummodes, lowestres; + int numlowresmodes, bpp, done; + int cstretch, istretch, mstretch; + BOOL stat; + +// enumerate 8 bpp modes + originalnummodes = nummodes; + modenum = 0; + lowestres = 99999; + + do + { + stat = EnumDisplaySettings (NULL, modenum, &devmode); + + if ((devmode.dmBitsPerPel == 8) && + (devmode.dmPelsWidth <= MAXWIDTH) && + (devmode.dmPelsHeight <= MAXHEIGHT) && + (nummodes < MAX_MODE_LIST)) + { + devmode.dmFields = DM_BITSPERPEL | + DM_PELSWIDTH | + DM_PELSHEIGHT; + + if (ChangeDisplaySettings (&devmode, CDS_TEST | CDS_FULLSCREEN) == + DISP_CHANGE_SUCCESSFUL) + { + modelist[nummodes].type = MS_FULLDIB; + modelist[nummodes].width = devmode.dmPelsWidth; + modelist[nummodes].height = devmode.dmPelsHeight; + modelist[nummodes].modenum = 0; + modelist[nummodes].mode13 = 0; + modelist[nummodes].stretched = 0; + modelist[nummodes].halfscreen = 0; + modelist[nummodes].dib = 1; + modelist[nummodes].fullscreen = 1; + modelist[nummodes].bpp = devmode.dmBitsPerPel; + sprintf (modelist[nummodes].modedesc, "%dx%d", + devmode.dmPelsWidth, devmode.dmPelsHeight); + + // if the width is more than twice the height, reduce it by half because this + // is probably a dual-screen monitor + if (!COM_CheckParm("-noadjustaspect")) + { + if (modelist[nummodes].width > (modelist[nummodes].height << 1)) + { + modelist[nummodes].width >>= 1; + modelist[nummodes].halfscreen = 1; + sprintf (modelist[nummodes].modedesc, "%dx%d", + modelist[nummodes].width, + modelist[nummodes].height); + } + } + + for (i=originalnummodes, existingmode = 0 ; i 8 bpp + if (nummodes == originalnummodes) + { + modenum = 0; + lowestres = 99999; + + Con_SafePrintf ("No 8-bpp fullscreen DIB modes found\n"); + + do + { + stat = EnumDisplaySettings (NULL, modenum, &devmode); + + if ((((devmode.dmPelsWidth <= MAXWIDTH) && + (devmode.dmPelsHeight <= MAXHEIGHT)) || + (!COM_CheckParm("-noadjustaspect") && + (devmode.dmPelsWidth <= (MAXWIDTH*2)) && + (devmode.dmPelsWidth > (devmode.dmPelsHeight*2)))) && + (nummodes < MAX_MODE_LIST) && + (devmode.dmBitsPerPel > 8)) + { + devmode.dmFields = DM_BITSPERPEL | + DM_PELSWIDTH | + DM_PELSHEIGHT; + + if (ChangeDisplaySettings (&devmode, CDS_TEST | CDS_FULLSCREEN) == + DISP_CHANGE_SUCCESSFUL) + { + modelist[nummodes].type = MS_FULLDIB; + modelist[nummodes].width = devmode.dmPelsWidth; + modelist[nummodes].height = devmode.dmPelsHeight; + modelist[nummodes].modenum = 0; + modelist[nummodes].mode13 = 0; + modelist[nummodes].stretched = 0; + modelist[nummodes].halfscreen = 0; + modelist[nummodes].dib = 1; + modelist[nummodes].fullscreen = 1; + modelist[nummodes].bpp = devmode.dmBitsPerPel; + sprintf (modelist[nummodes].modedesc, "%dx%d", + devmode.dmPelsWidth, devmode.dmPelsHeight); + + // if the width is more than twice the height, reduce it by half because this + // is probably a dual-screen monitor + if (!COM_CheckParm("-noadjustaspect")) + { + if (modelist[nummodes].width > (modelist[nummodes].height*2)) + { + modelist[nummodes].width >>= 1; + modelist[nummodes].halfscreen = 1; + sprintf (modelist[nummodes].modedesc, "%dx%d", + modelist[nummodes].width, + modelist[nummodes].height); + } + } + + for (i=originalnummodes, existingmode = 0 ; i= modelist[i].bpp)) + { + existingmode = 1; + break; + } + } + + if (!existingmode) + { + if (modelist[nummodes].width < lowestres) + lowestres = modelist[nummodes].width; + + nummodes++; + } + } + } + + switch (bpp) + { + case 8: + bpp = 16; + break; + + case 16: + bpp = 32; + break; + + case 32: + done = 1; + break; + } + } + +// now add the lowest stretch-by-2 pseudo-modes between 320-wide +// (inclusive) and lowest real res (not inclusive) +// don't bother if we have a real VGA mode 0x13 mode + if (!is_mode0x13) + { + for (i=originalnummodes, cstretch=0 ; i> 1) < lowestres) && + ((modelist[i].width >> 1) >= 320)) + { + lowestres = modelist[i].width >> 1; + cstretch = 1; + mstretch = i; + } + } + + if ((nummodes + cstretch) > MAX_MODE_LIST) + cstretch = MAX_MODE_LIST - nummodes; + + if (cstretch > 0) + { + for (i=(nummodes-1) ; i>=originalnummodes ; i--) + modelist[i+cstretch] = modelist[i]; + + nummodes += cstretch; + istretch = originalnummodes; + + modelist[istretch] = modelist[mstretch]; + modelist[istretch].width >>= 1; + modelist[istretch].height >>= 1; + modelist[istretch].stretched = 1; + sprintf (modelist[istretch].modedesc, "%dx%d", + modelist[istretch].width, modelist[istretch].height); + } + } + + if (nummodes != originalnummodes) + vid_default = MODE_FULLSCREEN_DEFAULT; + else + Con_SafePrintf ("No fullscreen DIB modes found\n"); +} + + +/* +================= +VID_NumModes +================= +*/ +int VID_NumModes (void) +{ + return nummodes; +} + + +/* +================= +VID_GetModePtr +================= +*/ +vmode_t *VID_GetModePtr (int modenum) +{ + + if ((modenum >= 0) && (modenum < nummodes)) + return &modelist[modenum]; + else + return &badmode; +} + + +/* +================= +VID_CheckModedescFixup +================= +*/ +void VID_CheckModedescFixup (int mode) +{ + int x, y, stretch; + + if (mode == MODE_SETTABLE_WINDOW) + { + modelist[mode].stretched = (int)vid_stretch_by_2.value; + stretch = modelist[mode].stretched; + + if (vid_config_x.value < (320 << stretch)) + vid_config_x.value = 320 << stretch; + + if (vid_config_y.value < (200 << stretch)) + vid_config_y.value = 200 << stretch; + + x = (int)vid_config_x.value; + y = (int)vid_config_y.value; + sprintf (modelist[mode].modedesc, "%dx%d", x, y); + modelist[mode].width = x; + modelist[mode].height = y; + } +} + + +/* +================= +VID_GetModeDescriptionMemCheck +================= +*/ +char *VID_GetModeDescriptionMemCheck (int mode) +{ + char *pinfo; + vmode_t *pv; + + if ((mode < 0) || (mode >= nummodes)) + return NULL; + + VID_CheckModedescFixup (mode); + + pv = VID_GetModePtr (mode); + pinfo = pv->modedesc; + + if (VID_CheckAdequateMem (pv->width, pv->height)) + { + return pinfo; + } + else + { + return NULL; + } +} + + +/* +================= +VID_GetModeDescription +================= +*/ +char *VID_GetModeDescription (int mode) +{ + char *pinfo; + vmode_t *pv; + + if ((mode < 0) || (mode >= nummodes)) + return NULL; + + VID_CheckModedescFixup (mode); + + pv = VID_GetModePtr (mode); + pinfo = pv->modedesc; + return pinfo; +} + + +/* +================= +VID_GetModeDescription2 + +Tacks on "windowed" or "fullscreen" +================= +*/ +char *VID_GetModeDescription2 (int mode) +{ + static char pinfo[40]; + vmode_t *pv; + + if ((mode < 0) || (mode >= nummodes)) + return NULL; + + VID_CheckModedescFixup (mode); + + pv = VID_GetModePtr (mode); + + if (modelist[mode].type == MS_FULLSCREEN) + { + sprintf(pinfo,"%s fullscreen", pv->modedesc); + } + else if (modelist[mode].type == MS_FULLDIB) + { + sprintf(pinfo,"%s fullscreen", pv->modedesc); + } + else + { + sprintf(pinfo, "%s windowed", pv->modedesc); + } + + return pinfo; +} + + +// KJB: Added this to return the mode driver name in description for console + +char *VID_GetExtModeDescription (int mode) +{ + static char pinfo[40]; + vmode_t *pv; + + if ((mode < 0) || (mode >= nummodes)) + return NULL; + + VID_CheckModedescFixup (mode); + + pv = VID_GetModePtr (mode); + if (modelist[mode].type == MS_FULLSCREEN) + { + sprintf(pinfo,"%s fullscreen %s",pv->modedesc, + MGL_modeDriverName(pv->modenum)); + } + else if (modelist[mode].type == MS_FULLDIB) + { + sprintf(pinfo,"%s fullscreen DIB", pv->modedesc); + } + else + { + sprintf(pinfo, "%s windowed", pv->modedesc); + } + + return pinfo; +} + + +void DestroyDIBWindow (void) +{ + + if (modestate == MS_WINDOWED) + { + // destroy the associated MGL DC's; the window gets reused + if (windc) + MGL_destroyDC(windc); + if (dibdc) + MGL_destroyDC(dibdc); + windc = dibdc = NULL; + } +} + + +void DestroyFullscreenWindow (void) +{ + + if (modestate == MS_FULLSCREEN) + { + // destroy the existing fullscreen mode and DC's + if (mgldc) + MGL_destroyDC (mgldc); + if (memdc) + MGL_destroyDC (memdc); + mgldc = memdc = NULL; + } +} + + + +void DestroyFullDIBWindow (void) +{ + if (modestate == MS_FULLDIB) + { + ChangeDisplaySettings (NULL, CDS_FULLSCREEN); + + // Destroy the fullscreen DIB window and associated MGL DC's + if (windc) + MGL_destroyDC(windc); + if (dibdc) + MGL_destroyDC(dibdc); + windc = dibdc = NULL; + } +} + + +qboolean VID_SetWindowedMode (int modenum) +{ + HDC hdc; + pixel_format_t pf; + qboolean stretched; + int lastmodestate; +// LONG wlong; + + if (!windowed_mode_set) + { + if (COM_CheckParm ("-resetwinpos")) + { + Cvar_SetValue (&vid_window_x, 0.0); + Cvar_SetValue (&vid_window_y, 0.0); + } + + windowed_mode_set; + } + + VID_CheckModedescFixup (modenum); + + DDActive = 0; + lastmodestate = modestate; + + DestroyFullscreenWindow (); + DestroyFullDIBWindow (); + + if (windc) + MGL_destroyDC(windc); + if (dibdc) + MGL_destroyDC(dibdc); + windc = dibdc = NULL; + +// KJB: Signal to the MGL that we are going back to windowed mode + if (!MGL_changeDisplayMode(grWINDOWED)) + initFatalError(); + + WindowRect.top = WindowRect.left = 0; + + WindowRect.right = modelist[modenum].width; + WindowRect.bottom = modelist[modenum].height; + stretched = modelist[modenum].stretched; + + DIBWidth = modelist[modenum].width; + DIBHeight = modelist[modenum].height; + + if (stretched) + { + DIBWidth >>= 1; + DIBHeight >>= 1; + } + + WindowStyle = WS_OVERLAPPED | WS_BORDER | WS_CAPTION | WS_SYSMENU | + WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_CLIPSIBLINGS | + WS_CLIPCHILDREN; + ExWindowStyle = 0; + AdjustWindowRectEx(&WindowRect, WindowStyle, FALSE, 0); + +// the first time we're called to set the mode, create the window we'll use +// for the rest of the session + if (!vid_mode_set) + { + mainwindow = CreateWindowEx ( + ExWindowStyle, + "WinQuake", + "QuakeWorld", + WindowStyle, + 0, 0, + WindowRect.right - WindowRect.left, + WindowRect.bottom - WindowRect.top, + NULL, + NULL, + global_hInstance, + NULL); + + if (!mainwindow) + Sys_Error ("Couldn't create DIB window"); + + // tell MGL to use this window for fullscreen modes + MGL_registerFullScreenWindow (mainwindow); + + vid_mode_set = true; + } + else + { + SetWindowLong(mainwindow, GWL_STYLE, WindowStyle | WS_VISIBLE); + SetWindowLong(mainwindow, GWL_EXSTYLE, ExWindowStyle); + } + + if (!SetWindowPos (mainwindow, + NULL, + 0, 0, + WindowRect.right - WindowRect.left, + WindowRect.bottom - WindowRect.top, + SWP_NOCOPYBITS | SWP_NOZORDER | + SWP_HIDEWINDOW)) + { + Sys_Error ("Couldn't resize DIB window"); + } + + if (hide_window) + return true; + +// position and show the DIB window + VID_CheckWindowXY (); + SetWindowPos (mainwindow, NULL, (int)vid_window_x.value, + (int)vid_window_y.value, 0, 0, + SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW | SWP_DRAWFRAME); + + if (force_minimized) + ShowWindow (mainwindow, SW_MINIMIZE); + else + ShowWindow (mainwindow, SW_SHOWDEFAULT); + + UpdateWindow (mainwindow); + + modestate = MS_WINDOWED; + vid_fulldib_on_focus_mode = 0; + +// because we have set the background brush for the window to NULL +// (to avoid flickering when re-sizing the window on the desktop), +// we clear the window to black when created, otherwise it will be +// empty while Quake starts up. + hdc = GetDC(mainwindow); + PatBlt(hdc,0,0,WindowRect.right,WindowRect.bottom,BLACKNESS); + ReleaseDC(mainwindow, hdc); + + /* Create the MGL window DC and the MGL memory DC */ + if ((windc = MGL_createWindowedDC(mainwindow)) == NULL) + MGL_fatalError("Unable to create Windowed DC!"); + + if ((dibdc = MGL_createMemoryDC(DIBWidth,DIBHeight,8,&pf)) == NULL) + MGL_fatalError("Unable to create Memory DC!"); + + MGL_makeCurrentDC(dibdc); + + vid.buffer = vid.conbuffer = vid.direct = dibdc->surface; + vid.rowbytes = vid.conrowbytes = dibdc->mi.bytesPerLine; + vid.numpages = 1; + vid.maxwarpwidth = WARP_WIDTH; + vid.maxwarpheight = WARP_HEIGHT; + vid.height = vid.conheight = DIBHeight; + vid.width = vid.conwidth = DIBWidth; + vid.aspect = ((float)vid.height / (float)vid.width) * + (320.0 / 240.0); + + vid_stretched = stretched; + + SendMessage (mainwindow, WM_SETICON, (WPARAM)TRUE, (LPARAM)hIcon); + SendMessage (mainwindow, WM_SETICON, (WPARAM)FALSE, (LPARAM)hIcon); + + // Tonik: this is a workaroung for the bug with garbaged + // screen on Riva TNT + if (lastmodestate == MS_FULLSCREEN && vid_resetonswitch.value) + ChangeDisplaySettings (NULL, CDS_RESET); + + return true; +} + + +qboolean VID_SetFullscreenMode (int modenum) +{ + + DDActive = 1; + + DestroyDIBWindow (); + DestroyFullDIBWindow (); + + mode = modelist[modenum].modenum; + + // Destroy old DC's, resetting back to fullscreen mode + if (mgldc) + MGL_destroyDC (mgldc); + if (memdc) + MGL_destroyDC (memdc); + mgldc = memdc = NULL; + + if ((mgldc = createDisplayDC (modelist[modenum].stretched || + (int)vid_nopageflip.value)) == NULL) + { + return false; + } + + modestate = MS_FULLSCREEN; + vid_fulldib_on_focus_mode = 0; + + vid.buffer = vid.conbuffer = vid.direct = NULL; + vid.maxwarpwidth = WARP_WIDTH; + vid.maxwarpheight = WARP_HEIGHT; + DIBHeight = vid.height = vid.conheight = modelist[modenum].height; + DIBWidth = vid.width = vid.conwidth = modelist[modenum].width; + vid.aspect = ((float)vid.height / (float)vid.width) * + (320.0 / 240.0); + + vid_stretched = modelist[modenum].stretched; + +// needed because we're not getting WM_MOVE messages fullscreen on NT + window_x = 0; + window_y = 0; + +// set the large icon, so the Quake icon will show up in the taskbar + SendMessage (mainwindow, WM_SETICON, (WPARAM)1, (LPARAM)hIcon); + SendMessage (mainwindow, WM_SETICON, (WPARAM)0, (LPARAM)hIcon); + +// shouldn't be needed, but Kendall needs to let us get the activation +// message for this not to be needed on NT + AppActivate (true, false); + + return true; +} + + +qboolean VID_SetFullDIBMode (int modenum) +{ + HDC hdc; + pixel_format_t pf; + int lastmodestate; + + DDActive = 0; + + DestroyFullscreenWindow (); + DestroyDIBWindow (); + + if (windc) + MGL_destroyDC(windc); + if (dibdc) + MGL_destroyDC(dibdc); + windc = dibdc = NULL; + + // KJB: Signal to the MGL that we are going back to windowed mode + if (!MGL_changeDisplayMode(grWINDOWED)) + initFatalError(); + + gdevmode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; + gdevmode.dmBitsPerPel = modelist[modenum].bpp; + gdevmode.dmPelsWidth = modelist[modenum].width << modelist[modenum].stretched << + modelist[modenum].halfscreen; + gdevmode.dmPelsHeight = modelist[modenum].height << modelist[modenum].stretched; + gdevmode.dmSize = sizeof (gdevmode); + + if (ChangeDisplaySettings (&gdevmode, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) + Sys_Error ("Couldn't set fullscreen DIB mode"); + + lastmodestate = modestate; + modestate = MS_FULLDIB; + vid_fulldib_on_focus_mode = modenum; + + WindowRect.top = WindowRect.left = 0; + + hdc = GetDC(NULL); + + WindowRect.right = modelist[modenum].width << modelist[modenum].stretched; + WindowRect.bottom = modelist[modenum].height << modelist[modenum].stretched; + + ReleaseDC(NULL,hdc); + + DIBWidth = modelist[modenum].width; + DIBHeight = modelist[modenum].height; + + WindowStyle = WS_POPUP | WS_SYSMENU | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; + ExWindowStyle = 0; + AdjustWindowRectEx(&WindowRect, WindowStyle, FALSE, 0); + + SetWindowLong(mainwindow, GWL_STYLE, WindowStyle | WS_VISIBLE); + SetWindowLong(mainwindow, GWL_EXSTYLE, ExWindowStyle); + + if (!SetWindowPos (mainwindow, + NULL, + 0, 0, + WindowRect.right - WindowRect.left, + WindowRect.bottom - WindowRect.top, + SWP_NOCOPYBITS | SWP_NOZORDER)) + { + Sys_Error ("Couldn't resize DIB window"); + } + +// position and show the DIB window + SetWindowPos (mainwindow, HWND_TOPMOST, 0, 0, 0, 0, + SWP_NOSIZE | SWP_SHOWWINDOW | SWP_DRAWFRAME); + ShowWindow (mainwindow, SW_SHOWDEFAULT); + UpdateWindow (mainwindow); + + // Because we have set the background brush for the window to NULL + // (to avoid flickering when re-sizing the window on the desktop), we + // clear the window to black when created, otherwise it will be + // empty while Quake starts up. + hdc = GetDC(mainwindow); + PatBlt(hdc,0,0,WindowRect.right,WindowRect.bottom,BLACKNESS); + ReleaseDC(mainwindow, hdc); + + /* Create the MGL window DC and the MGL memory DC */ + if ((windc = MGL_createWindowedDC(mainwindow)) == NULL) + MGL_fatalError("Unable to create Fullscreen DIB DC!"); + + if ((dibdc = MGL_createMemoryDC(DIBWidth,DIBHeight,8,&pf)) == NULL) + MGL_fatalError("Unable to create Memory DC!"); + + MGL_makeCurrentDC(dibdc); + + vid.buffer = vid.conbuffer = vid.direct = dibdc->surface; + vid.rowbytes = vid.conrowbytes = dibdc->mi.bytesPerLine; + vid.numpages = 1; + vid.maxwarpwidth = WARP_WIDTH; + vid.maxwarpheight = WARP_HEIGHT; + vid.height = vid.conheight = DIBHeight; + vid.width = vid.conwidth = DIBWidth; + vid.aspect = ((float)vid.height / (float)vid.width) * + (320.0 / 240.0); + + vid_stretched = modelist[modenum].stretched; + +// needed because we're not getting WM_MOVE messages fullscreen on NT + window_x = 0; + window_y = 0; + + return true; +} + + +void VID_RestoreOldMode (int original_mode) +{ + static qboolean inerror = false; + + if (inerror) + return; + + in_mode_set = false; + inerror = true; + +// make sure mode set happens (video mode changes) + vid_modenum = original_mode - 1; + + if (!VID_SetMode (original_mode, vid_curpal)) + { + vid_modenum = MODE_WINDOWED - 1; + + if (!VID_SetMode (windowed_default, vid_curpal)) + Sys_Error ("Can't set any video mode"); + } + + inerror = false; +} + + +void VID_SetDefaultMode (void) +{ + + if (vid_initialized) + VID_SetMode (0, vid_curpal); + + IN_DeactivateMouse (); +} + + +int VID_SetMode (int modenum, unsigned char *palette) +{ + int original_mode, temp /*, dummy */; + qboolean stat; + MSG msg; + HDC hdc; + + while ((modenum >= nummodes) || (modenum < 0)) + { + if (vid_modenum == NO_MODE) + { + if (modenum == vid_default) + { + modenum = windowed_default; + } + else + { + modenum = vid_default; + } + + Cvar_SetValue (&vid_mode, (float)modenum); + } + else + { + Cvar_SetValue (&vid_mode, (float)vid_modenum); + return 0; + } + } + + if (!force_mode_set && (modenum == vid_modenum)) + return true; + +// so Con_Printfs don't mess us up by forcing vid and snd updates + temp = scr_disabled_for_loading; + scr_disabled_for_loading = true; + in_mode_set = true; + + CDAudio_Pause (); + S_ClearBuffer (); + + if (vid_modenum == NO_MODE) + original_mode = windowed_default; + else + original_mode = vid_modenum; + + // Set either the fullscreen or windowed mode + if (modelist[modenum].type == MS_WINDOWED) + { + if (_windowed_mouse.value && key_dest == key_game) + { + stat = VID_SetWindowedMode(modenum); + IN_ActivateMouse (); + IN_HideMouse (); + } + else + { + IN_DeactivateMouse (); + IN_ShowMouse (); + stat = VID_SetWindowedMode(modenum); + } + } + else if (modelist[modenum].type == MS_FULLDIB) + { + stat = VID_SetFullDIBMode(modenum); + IN_ActivateMouse (); + IN_HideMouse (); + } + else + { + stat = VID_SetFullscreenMode(modenum); + IN_ActivateMouse (); + IN_HideMouse (); + } + + window_width = vid.width << vid_stretched; + window_height = vid.height << vid_stretched; + VID_UpdateWindowStatus (); + + CDAudio_Resume (); + scr_disabled_for_loading = temp; + + if (!stat) + { + VID_RestoreOldMode (original_mode); + return false; + } + + if (hide_window) + return true; + +// now we try to make sure we get the focus on the mode switch, because +// sometimes in some systems we don't. We grab the foreground, then +// finish setting up, pump all our messages, and sleep for a little while +// to let messages finish bouncing around the system, then we put +// ourselves at the top of the z order, then grab the foreground again, +// Who knows if it helps, but it probably doesn't hurt + if (!force_minimized) + SetForegroundWindow (mainwindow); + + hdc = GetDC(NULL); + + if (GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) + vid_palettized = true; + else + vid_palettized = false; + + VID_SetPalette (palette); + + ReleaseDC(NULL,hdc); + + vid_modenum = modenum; + Cvar_SetValue (&vid_mode, (float)vid_modenum); + + if (!VID_AllocBuffers (vid.width, vid.height)) + { + // couldn't get memory for this mode; try to fall back to previous mode + VID_RestoreOldMode (original_mode); + return false; + } + + D_InitCaches (vid_surfcache, vid_surfcachesize); + + while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) + { + TranslateMessage (&msg); + DispatchMessage (&msg); + } + + Sleep (100); + + if (!force_minimized) + { + SetWindowPos (mainwindow, HWND_TOP, 0, 0, 0, 0, + SWP_DRAWFRAME | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | + SWP_NOCOPYBITS); + + SetForegroundWindow (mainwindow); + } + +// fix the leftover Alt from any Alt-Tab or the like that switched us away + ClearAllStates (); + + if (!msg_suppress_1) + Con_SafePrintf ("Video mode %s initialized\n", VID_GetModeDescription (vid_modenum)); + + VID_SetPalette (palette); + + in_mode_set = false; + vid.recalc_refdef = 1; + + return true; +} + +void VID_LockBuffer (void) +{ + + if (dibdc) + return; + + lockcount++; + + if (lockcount > 1) + return; + + MGL_beginDirectAccess(); + + if (memdc) + { + // Update surface pointer for linear access modes + vid.buffer = vid.conbuffer = vid.direct = memdc->surface; + vid.rowbytes = vid.conrowbytes = memdc->mi.bytesPerLine; + } + else if (mgldc) + { + // Update surface pointer for linear access modes + vid.buffer = vid.conbuffer = vid.direct = mgldc->surface; + vid.rowbytes = vid.conrowbytes = mgldc->mi.bytesPerLine; + } + + if (r_dowarp) + d_viewbuffer = r_warpbuffer; + else + d_viewbuffer = (void *)(byte *)vid.buffer; + + if (r_dowarp) + screenwidth = WARP_WIDTH; + else + screenwidth = vid.rowbytes; +} + + +void VID_UnlockBuffer (void) +{ + if (dibdc) + return; + + lockcount--; + + if (lockcount > 0) + return; + + if (lockcount < 0) + Sys_Error ("Unbalanced unlock"); + + MGL_endDirectAccess(); + +// to turn up any unlocked accesses + vid.buffer = vid.conbuffer = vid.direct = d_viewbuffer = NULL; + +} + + +int VID_ForceUnlockedAndReturnState (void) +{ + int lk; + + if (!lockcount) + return 0; + + lk = lockcount; + + if (dibdc) + { + lockcount = 0; + } + else + { + lockcount = 1; + VID_UnlockBuffer (); + } + + return lk; +} + + +void VID_ForceLockState (int lk) +{ + + if (!dibdc && lk) + { + lockcount = 0; + VID_LockBuffer (); + } + + lockcount = lk; +} + + +void VID_SetPalette (unsigned char *palette) +{ + INT i; + palette_t pal[256]; + HDC hdc; + + if (!Minimized) + { + palette_changed = true; + + // make sure we have the static colors if we're the active app + hdc = GetDC(NULL); + + if (vid_palettized && ActiveApp) + { + if (GetSystemPaletteUse(hdc) == SYSPAL_STATIC) + { + // switch to SYSPAL_NOSTATIC and remap the colors + SetSystemPaletteUse(hdc, SYSPAL_NOSTATIC); + syscolchg = true; + pal_is_nostatic = true; + } + } + + ReleaseDC(NULL,hdc); + + // Translate the palette values to an MGL palette array and + // set the values. + for (i = 0; i < 256; i++) + { + pal[i].red = palette[i*3]; + pal[i].green = palette[i*3+1]; + pal[i].blue = palette[i*3+2]; + } + + if (DDActive) + { + if (!mgldc) + return; + + MGL_setPalette(mgldc,pal,256,0); + MGL_realizePalette(mgldc,256,0,false); + if (memdc) + MGL_setPalette(memdc,pal,256,0); + } + else + { + if (!windc) + return; + + MGL_setPalette(windc,pal,256,0); + MGL_realizePalette(windc,256,0,false); + if (dibdc) + { + MGL_setPalette(dibdc,pal,256,0); + MGL_realizePalette(dibdc,256,0,false); + } + } + } + + memcpy (vid_curpal, palette, sizeof(vid_curpal)); + + if (syscolchg) + { + PostMessage (HWND_BROADCAST, WM_SYSCOLORCHANGE, (WPARAM)0, (LPARAM)0); + syscolchg = false; + } +} + + +void VID_ShiftPalette (unsigned char *palette) +{ + VID_SetPalette (palette); +} + + +/* +================= +VID_DescribeCurrentMode_f +================= +*/ +void VID_DescribeCurrentMode_f (void) +{ + Con_Printf ("%s\n", VID_GetExtModeDescription (vid_modenum)); +} + + +/* +================= +VID_NumModes_f +================= +*/ +void VID_NumModes_f (void) +{ + + if (nummodes == 1) + Con_Printf ("%d video mode is available\n", nummodes); + else + Con_Printf ("%d video modes are available\n", nummodes); +} + + +/* +================= +VID_DescribeMode_f +================= +*/ +void VID_DescribeMode_f (void) +{ + int modenum; + + modenum = Q_atoi (Cmd_Argv(1)); + + Con_Printf ("%s\n", VID_GetExtModeDescription (modenum)); +} + + +/* +================= +VID_DescribeModes_f +================= +*/ +void VID_DescribeModes_f (void) +{ + int i, lnummodes; + char *pinfo; + qboolean na; + vmode_t *pv; + + na = false; + + lnummodes = VID_NumModes (); + + for (i=0 ; iwidth, pv->height)) + { + Con_Printf ("%2d: %s\n", i, pinfo); + } + else + { + Con_Printf ("**: %s\n", pinfo); + na = true; + } + } + + if (na) + { + Con_Printf ("\n[**: not enough system RAM for mode]\n"); + } +} + + +/* +================= +VID_TestMode_f +================= +*/ +void VID_TestMode_f (void) +{ + int modenum; + double testduration; + + if (!vid_testingmode) + { + modenum = Q_atoi (Cmd_Argv(1)); + + if (VID_SetMode (modenum, vid_curpal)) + { + vid_testingmode = 1; + testduration = Q_atof (Cmd_Argv(2)); + if (testduration == 0) + testduration = 5.0; + vid_testendtime = realtime + testduration; + } + } +} + +/* +================= +VID_Windowed_f +================= +*/ +void VID_Windowed_f (void) +{ + + VID_SetMode ((int)vid_windowed_mode.value, vid_curpal); +} + + +/* +================= +VID_Fullscreen_f +================= +*/ +void VID_Fullscreen_f (void) +{ + + VID_SetMode ((int)vid_fullscreen_mode.value, vid_curpal); +} + +/* +================= +VID_Minimize_f +================= +*/ +void VID_Minimize_f (void) +{ + +// we only support minimizing windows; if you're fullscreen, +// switch to windowed first + if (modestate == MS_WINDOWED) + ShowWindow (mainwindow, SW_MINIMIZE); +} + + + +/* +================= +VID_ForceMode_f +================= +*/ +void VID_ForceMode_f (void) +{ + int modenum; +// double testduration; + + if (!vid_testingmode) + { + modenum = Q_atoi (Cmd_Argv(1)); + + force_mode_set = 1; + VID_SetMode (modenum, vid_curpal); + force_mode_set = 0; + } +} + + +void VID_Init (unsigned char *palette) +{ + int i, bestmatch, bestmatchmetric, t, dr, dg, db; + int basenummodes; + byte *ptmp; + + Cvar_RegisterVariable (&vid_mode); + Cvar_RegisterVariable (&vid_wait); + Cvar_RegisterVariable (&vid_nopageflip); + Cvar_RegisterVariable (&_vid_wait_override); + Cvar_RegisterVariable (&_vid_default_mode); + Cvar_RegisterVariable (&_vid_default_mode_win); + Cvar_RegisterVariable (&vid_config_x); + Cvar_RegisterVariable (&vid_config_y); + Cvar_RegisterVariable (&vid_stretch_by_2); + Cvar_RegisterVariable (&_windowed_mouse); + Cvar_RegisterVariable (&vid_fullscreen_mode); + Cvar_RegisterVariable (&vid_windowed_mode); + Cvar_RegisterVariable (&block_switch); + Cvar_RegisterVariable (&vid_window_x); + Cvar_RegisterVariable (&vid_window_y); + Cvar_RegisterVariable (&vid_resetonswitch); + + Cmd_AddCommand ("vid_testmode", VID_TestMode_f); + Cmd_AddCommand ("vid_nummodes", VID_NumModes_f); + Cmd_AddCommand ("vid_describecurrentmode", VID_DescribeCurrentMode_f); + Cmd_AddCommand ("vid_describemode", VID_DescribeMode_f); + Cmd_AddCommand ("vid_describemodes", VID_DescribeModes_f); + Cmd_AddCommand ("vid_forcemode", VID_ForceMode_f); + Cmd_AddCommand ("vid_windowed", VID_Windowed_f); + Cmd_AddCommand ("vid_fullscreen", VID_Fullscreen_f); + Cmd_AddCommand ("vid_minimize", VID_Minimize_f); + + if (COM_CheckParm ("-dibonly")) + dibonly = true; + + VID_InitMGLDIB (global_hInstance); + + basenummodes = nummodes; + + if (!dibonly) + VID_InitMGLFull (global_hInstance); + +// if there are no non-windowed modes, or only windowed and mode 0x13, then use +// fullscreen DIBs as well + if (((nummodes == basenummodes) || + ((nummodes == (basenummodes + 1)) && is_mode0x13)) && + !COM_CheckParm ("-nofulldib")) + + { + VID_InitFullDIB (global_hInstance); + } + + vid.maxwarpwidth = WARP_WIDTH; + vid.maxwarpheight = WARP_HEIGHT; + vid.colormap = host_colormap; + vid.fullbright = 256 - LittleLong (*((int *)vid.colormap + 2048)); + vid_testingmode = 0; + +// GDI doesn't let us remap palette index 0, so we'll remap color +// mappings from that black to another one + bestmatchmetric = 256*256*3; + + for (i=1 ; i<256 ; i++) + { + dr = palette[0] - palette[i*3]; + dg = palette[1] - palette[i*3+1]; + db = palette[2] - palette[i*3+2]; + + t = (dr * dr) + (dg * dg) + (db * db); + + if (t < bestmatchmetric) + { + bestmatchmetric = t; + bestmatch = i; + + if (t == 0) + break; + } + } + + for (i=0, ptmp = vid.colormap ; i<(1<<(VID_CBITS+8)) ; i++, ptmp++) + { + if (*ptmp == 0) + *ptmp = bestmatch; + } + + if (COM_CheckParm("-startwindowed")) + { + startwindowed = 1; + vid_default = windowed_default; + } + + if (hwnd_dialog) + DestroyWindow (hwnd_dialog); + +// sound initialization has to go here, preceded by a windowed mode set, +// so there's a window for DirectSound to work with but we're not yet +// fullscreen so the "hardware already in use" dialog is visible if it +// gets displayed + +// keep the window minimized until we're ready for the first real mode set + hide_window = true; + VID_SetMode (MODE_WINDOWED, palette); + hide_window = false; + S_Init (); + + vid_initialized = true; + + force_mode_set = true; + VID_SetMode (vid_default, palette); + force_mode_set = false; + + vid_realmode = vid_modenum; + + VID_SetPalette (palette); + + vid_menudrawfn = VID_MenuDraw; + vid_menukeyfn = VID_MenuKey; + + strcpy (badmode.modedesc, "Bad mode"); +} + + +void VID_Shutdown (void) +{ +// HDC hdc; +// int dummy; + + if (vid_initialized) + { + if (modestate == MS_FULLDIB) + ChangeDisplaySettings (NULL, CDS_FULLSCREEN); + + PostMessage (HWND_BROADCAST, WM_PALETTECHANGED, (WPARAM)mainwindow, (LPARAM)0); + PostMessage (HWND_BROADCAST, WM_SYSCOLORCHANGE, (WPARAM)0, (LPARAM)0); + + AppActivate(false, false); + DestroyDIBWindow (); + DestroyFullscreenWindow (); + DestroyFullDIBWindow (); + + if (hwnd_dialog) + DestroyWindow (hwnd_dialog); + + if (mainwindow) + DestroyWindow(mainwindow); + + MGL_exit(); + + vid_testingmode = 0; + vid_initialized = 0; + + // Tonik: this is a workaroung for the bug with garbaged + // screen on Riva TNT + if (modestate == MS_FULLSCREEN && vid_resetonswitch.value) + ChangeDisplaySettings (NULL, CDS_RESET); + } +} + + +/* +================ +FlipScreen +================ +*/ +void FlipScreen(vrect_t *rects) +{ +// HRESULT ddrval; + + // Flip the surfaces + + if (DDActive) + { + if (mgldc) + { + if (memdc) + { + while (rects) + { + if (vid_stretched) + { + MGL_stretchBltCoord(mgldc, memdc, + rects->x, + rects->y, + rects->x + rects->width, + rects->y + rects->height, + rects->x << 1, + rects->y << 1, + (rects->x + rects->width) << 1, + (rects->y + rects->height) << 1); + } + else + { + MGL_bitBltCoord(mgldc, memdc, + rects->x, rects->y, + (rects->x + rects->width), + (rects->y + rects->height), + rects->x, rects->y, MGL_REPLACE_MODE); + } + + rects = rects->pnext; + } + } + + if (vid.numpages > 1) + { + // We have a flipping surface, so do a hard page flip + aPage = (aPage+1) % vid.numpages; + vPage = (vPage+1) % vid.numpages; + MGL_setActivePage(mgldc,aPage); + MGL_setVisualPage(mgldc,vPage,waitVRT); + } + } + } + else + { + HDC hdcScreen; + + hdcScreen = GetDC(mainwindow); + + if (windc && dibdc) + { + MGL_setWinDC(windc,hdcScreen); + + while (rects) + { + if (vid_stretched) + { + MGL_stretchBltCoord(windc,dibdc, + rects->x, rects->y, + rects->x + rects->width, rects->y + rects->height, + rects->x << 1, rects->y << 1, + (rects->x + rects->width) << 1, + (rects->y + rects->height) << 1); + } + else + { + MGL_bitBltCoord(windc,dibdc, + rects->x, rects->y, + rects->x + rects->width, rects->y + rects->height, + rects->x, rects->y, MGL_REPLACE_MODE); + } + + rects = rects->pnext; + } + } + + ReleaseDC(mainwindow, hdcScreen); + } +} + + +void VID_Update (vrect_t *rects) +{ + vrect_t rect; + RECT trect; + + if (!vid_palettized && palette_changed) + { + palette_changed = false; + rect.x = 0; + rect.y = 0; + rect.width = vid.width; + rect.height = vid.height; + rect.pnext = NULL; + rects = ▭ + } + + if (firstupdate) + { + if (modestate == MS_WINDOWED) + { + GetWindowRect (mainwindow, &trect); + + if ((trect.left != (int)vid_window_x.value) || + (trect.top != (int)vid_window_y.value)) + { + if (COM_CheckParm ("-resetwinpos")) + { + Cvar_SetValue (&vid_window_x, 0.0); + Cvar_SetValue (&vid_window_y, 0.0); + } + + VID_CheckWindowXY (); + SetWindowPos (mainwindow, NULL, (int)vid_window_x.value, + (int)vid_window_y.value, 0, 0, + SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW | SWP_DRAWFRAME); + } + } + + if ((_vid_default_mode_win.value != vid_default) && + (!startwindowed || (_vid_default_mode_win.value < MODE_FULLSCREEN_DEFAULT))) + { + firstupdate = 0; + + if (COM_CheckParm ("-resetwinpos")) + { + Cvar_SetValue (&vid_window_x, 0.0); + Cvar_SetValue (&vid_window_y, 0.0); + } + + if ((_vid_default_mode_win.value < 0) || + (_vid_default_mode_win.value >= nummodes)) + { + Cvar_SetValue (&_vid_default_mode_win, windowed_default); + } + + Cvar_SetValue (&vid_mode, _vid_default_mode_win.value); + } + } + + // We've drawn the frame; copy it to the screen + FlipScreen (rects); + + if (vid_testingmode) + { + if (realtime >= vid_testendtime) + { + VID_SetMode (vid_realmode, vid_curpal); + vid_testingmode = 0; + } + } + else + { + if ((int)vid_mode.value != vid_realmode) + { + VID_SetMode ((int)vid_mode.value, vid_curpal); + Cvar_SetValue (&vid_mode, (float)vid_modenum); + // so if mode set fails, we don't keep on + // trying to set that mode + vid_realmode = vid_modenum; + } + } + +// handle the mouse state when windowed if that's changed + if (modestate == MS_WINDOWED) + { + if (!_windowed_mouse.value) { + if (windowed_mouse) { + IN_DeactivateMouse (); + IN_ShowMouse (); + } + windowed_mouse = false; + } else { + windowed_mouse = true; + if (key_dest == key_game && !mouseactive && ActiveApp) { + IN_ActivateMouse (); + IN_HideMouse (); + } else if (mouseactive && key_dest != key_game) { + IN_DeactivateMouse (); + IN_ShowMouse (); + } + } + } +} + + +/* +================ +D_BeginDirectRect +================ +*/ +void D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height) +{ + int i, j, reps, repshift; + vrect_t rect; + + if (!vid_initialized) + return; + + if (vid.aspect > 1.5) + { + reps = 2; + repshift = 1; + } + else + { + reps = 1; + repshift = 0; + } + + if (vid.numpages == 1) + { + VID_LockBuffer (); + + if (!vid.direct) + Sys_Error ("NULL vid.direct pointer"); + + for (i=0 ; i<(height << repshift) ; i += reps) + { + for (j=0 ; j> repshift) * width], + width); + } + } + + VID_UnlockBuffer (); + + rect.x = x; + rect.y = y; + rect.width = width; + rect.height = height << repshift; + rect.pnext = NULL; + + FlipScreen (&rect); + } + else + { + // unlock if locked + if (lockcount > 0) + MGL_endDirectAccess(); + + // set the active page to the displayed page + MGL_setActivePage (mgldc, vPage); + + // lock the screen + MGL_beginDirectAccess (); + + // save from and draw to screen + for (i=0 ; i<(height << repshift) ; i += reps) + { + for (j=0 ; jsurface + x + + ((y << repshift) + i + j) * mgldc->mi.bytesPerLine, + width); + memcpy ((byte *)mgldc->surface + x + + ((y << repshift) + i + j) * mgldc->mi.bytesPerLine, + &pbitmap[(i >> repshift) * width], + width); + } + } + + // unlock the screen + MGL_endDirectAccess (); + + // restore the original active page + MGL_setActivePage (mgldc, aPage); + + // relock the screen if it was locked + if (lockcount > 0) + MGL_beginDirectAccess(); + } +} + + +/* +================ +D_EndDirectRect +================ +*/ +void D_EndDirectRect (int x, int y, int width, int height) +{ + int i, j, reps, repshift; + vrect_t rect; + + if (!vid_initialized) + return; + + if (vid.aspect > 1.5) + { + reps = 2; + repshift = 1; + } + else + { + reps = 1; + repshift = 0; + } + + if (vid.numpages == 1) + { + VID_LockBuffer (); + + if (!vid.direct) + Sys_Error ("NULL vid.direct pointer"); + + for (i=0 ; i<(height << repshift) ; i += reps) + { + for (j=0 ; j 0) + MGL_endDirectAccess(); + + // set the active page to the displayed page + MGL_setActivePage (mgldc, vPage); + + // lock the screen + MGL_beginDirectAccess (); + + // restore to the screen + for (i=0 ; i<(height << repshift) ; i += reps) + { + for (j=0 ; jsurface + x + + ((y << repshift) + i + j) * mgldc->mi.bytesPerLine, + &backingbuf[(i + j) * 24], + width); + } + } + + // unlock the screen + MGL_endDirectAccess (); + + // restore the original active page + MGL_setActivePage (mgldc, aPage); + + // relock the screen if it was locked + if (lockcount > 0) + MGL_beginDirectAccess(); + } +} + + +//========================================================================== + + +void AppActivate(BOOL fActive, BOOL minimize) +/**************************************************************************** +* +* Function: AppActivate +* Parameters: fActive - True if app is activating +* +* Description: If the application is activating, then swap the system +* into SYSPAL_NOSTATIC mode so that our palettes will display +* correctly. +* +****************************************************************************/ +{ + HDC hdc; + int i, t; + static BOOL sound_active; + + ActiveApp = fActive; + +// messy, but it seems to work + if (vid_fulldib_on_focus_mode) + { + Minimized = minimize; + + if (Minimized) + ActiveApp = false; + } + + MGL_appActivate(windc, ActiveApp); + + if (vid_initialized) + { + // yield the palette if we're losing the focus + hdc = GetDC(NULL); + + if (GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) + { + if (ActiveApp) + { + if ((modestate == MS_WINDOWED) || (modestate == MS_FULLDIB)) + { + if (GetSystemPaletteUse(hdc) == SYSPAL_STATIC) + { + // switch to SYSPAL_NOSTATIC and remap the colors + SetSystemPaletteUse(hdc, SYSPAL_NOSTATIC); + syscolchg = true; + pal_is_nostatic = true; + } + } + } + else if (pal_is_nostatic) + { + if (GetSystemPaletteUse(hdc) == SYSPAL_NOSTATIC) + { + // switch back to SYSPAL_STATIC and the old mapping + SetSystemPaletteUse(hdc, SYSPAL_STATIC); + syscolchg = true; + } + + pal_is_nostatic = false; + } + } + + if (!Minimized) + VID_SetPalette (vid_curpal); + + scr_fullupdate = 0; + + ReleaseDC(NULL,hdc); + } + +// enable/disable sound on focus gain/loss + if (!ActiveApp && sound_active) + { + S_BlockSound (); + S_ClearBuffer (); + sound_active = false; + } + else if (ActiveApp && !sound_active) + { + S_UnblockSound (); + S_ClearBuffer (); + sound_active = true; + } + +// minimize/restore fulldib windows/mouse-capture normal windows on demand + if (!in_mode_set) + { + if (ActiveApp) + { + if (vid_fulldib_on_focus_mode) + { + if (vid_initialized) + { + msg_suppress_1 = true; // don't want to see normal mode set message + VID_SetMode (vid_fulldib_on_focus_mode, vid_curpal); + msg_suppress_1 = false; + + t = in_mode_set; + in_mode_set = true; + AppActivate (true, false); + in_mode_set = t; + } + + IN_ActivateMouse (); + IN_HideMouse (); + } + else if ((modestate == MS_WINDOWED) && _windowed_mouse.value && key_dest == key_game) + { + IN_ActivateMouse (); + IN_HideMouse (); + } + } + + if (!ActiveApp) + { + if (modestate == MS_FULLDIB) + { + if (vid_initialized) + { + force_minimized = true; + i = vid_fulldib_on_focus_mode; + msg_suppress_1 = true; // don't want to see normal mode set message + VID_SetMode (windowed_default, vid_curpal); + msg_suppress_1 = false; + vid_fulldib_on_focus_mode = i; + force_minimized = false; + + // we never seem to get WM_ACTIVATE inactive from this mode set, so we'll + // do it manually + t = in_mode_set; + in_mode_set = true; + AppActivate (false, true); + in_mode_set = t; + } + + IN_DeactivateMouse (); + IN_ShowMouse (); + } + else if ((modestate == MS_WINDOWED) && _windowed_mouse.value /* && mouseactive */) + { + IN_DeactivateMouse (); + IN_ShowMouse (); + } + } + } +} + + +/* +================ +VID_HandlePause +================ +*/ +void VID_HandlePause (qboolean pause) +{ +#if 0 + if ((modestate == MS_WINDOWED) && _windowed_mouse.value) + { + if (pause) + { + IN_DeactivateMouse (); + IN_ShowMouse (); + } + else + { + IN_ActivateMouse (); + IN_HideMouse (); + } + } +#endif +} + + +/* +=================================================================== + +MAIN WINDOW + +=================================================================== +*/ + +LONG CDAudio_MessageHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + +int IN_MapKey (int key); + +/* main window procedure */ +LONG WINAPI MainWndProc ( + HWND hWnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) +{ + LONG lRet = 0; + int /* fwKeys, xPos, yPos,*/ fActive, fMinimized, temp; + HDC hdc; + PAINTSTRUCT ps; + extern unsigned int uiWheelMessage; + static int recursiveflag; + + if ( uMsg == uiWheelMessage ) { + uMsg = WM_MOUSEWHEEL; + wParam <<= 16; + } + + + switch (uMsg) + { + case WM_CREATE: + break; + + case WM_SYSCOMMAND: + + // Check for maximize being hit + switch (wParam & ~0x0F) + { + case SC_MAXIMIZE: + // if minimized, bring up as a window before going fullscreen, + // so MGL will have the right state to restore + if (Minimized) + { + force_mode_set = true; + VID_SetMode (vid_modenum, vid_curpal); + force_mode_set = false; + } + + VID_SetMode ((int)vid_fullscreen_mode.value, vid_curpal); + break; + + case SC_SCREENSAVE: + case SC_MONITORPOWER: + if (modestate != MS_WINDOWED) + { + // don't call DefWindowProc() because we don't want to start + // the screen saver fullscreen + break; + } + + // fall through windowed and allow the screen saver to start + + default: + if (!in_mode_set) + { + S_BlockSound (); + S_ClearBuffer (); + } + + lRet = DefWindowProc (hWnd, uMsg, wParam, lParam); + + if (!in_mode_set) + { + S_UnblockSound (); + } + } + break; + + case WM_MOVE: + window_x = (int) LOWORD(lParam); + window_y = (int) HIWORD(lParam); + VID_UpdateWindowStatus (); + + if ((modestate == MS_WINDOWED) && !in_mode_set && !Minimized) + VID_RememberWindowPos (); + + break; + + case WM_SIZE: + Minimized = false; + + if (!(wParam & SIZE_RESTORED)) + { + if (wParam & SIZE_MINIMIZED) + Minimized = true; + } + break; + + case WM_SYSCHAR: + // keep Alt-Space from happening + break; + + case WM_ACTIVATE: + fActive = LOWORD(wParam); + fMinimized = (BOOL) HIWORD(wParam); + AppActivate(!(fActive == WA_INACTIVE), fMinimized); + + // Tonik: this is a workaroung for the bug with garbaged + // screen on Riva TNT + if (fActive == WA_INACTIVE && modestate == MS_FULLSCREEN + && vid_resetonswitch.value) + ChangeDisplaySettings (NULL, CDS_RESET); + + // fix the leftover Alt from any Alt-Tab or the like that switched us away + ClearAllStates (); + + if (!in_mode_set) + { + if (windc) + MGL_activatePalette(windc,true); + + VID_SetPalette(vid_curpal); + } + + break; + + case WM_PAINT: + hdc = BeginPaint(hWnd, &ps); + + if (!in_mode_set && host_initialized) + SCR_UpdateWholeScreen (); + + EndPaint(hWnd, &ps); + break; + + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + if (!in_mode_set) + Key_Event (IN_MapKey(lParam), true); + break; + + case WM_KEYUP: + case WM_SYSKEYUP: + if (!in_mode_set) + Key_Event (IN_MapKey(lParam), false); + break; + + // this is complicated because Win32 seems to pack multiple mouse events into + // one update sometimes, so we always check all states and look for events + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: + case WM_MOUSEMOVE: + if (!in_mode_set) + { + temp = 0; + + if (wParam & MK_LBUTTON) + temp |= 1; + + if (wParam & MK_RBUTTON) + temp |= 2; + + if (wParam & MK_MBUTTON) + temp |= 4; + + IN_MouseEvent (temp); + } + break; + // JACK: This is the mouse wheel with the Intellimouse + // Its delta is either positive or neg, and we generate the proper + // Event. + case WM_MOUSEWHEEL: + if ((short) HIWORD(wParam) > 0) { + Key_Event(K_MWHEELUP, true); + Key_Event(K_MWHEELUP, false); + } else { + Key_Event(K_MWHEELDOWN, true); + Key_Event(K_MWHEELDOWN, false); + } + break; + // KJB: Added these new palette functions + case WM_PALETTECHANGED: + if ((HWND)wParam == hWnd) + break; + /* Fall through to WM_QUERYNEWPALETTE */ + case WM_QUERYNEWPALETTE: + hdc = GetDC(NULL); + + if (GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) + vid_palettized = true; + else + vid_palettized = false; + + ReleaseDC(NULL,hdc); + + scr_fullupdate = 0; + + if (vid_initialized && !in_mode_set && windc && MGL_activatePalette(windc,false) && !Minimized) + { + VID_SetPalette (vid_curpal); + InvalidateRect (mainwindow, NULL, false); + + // specifically required if WM_QUERYNEWPALETTE realizes a new palette + lRet = TRUE; + } + break; + + case WM_DISPLAYCHANGE: + if (!in_mode_set && (modestate == MS_WINDOWED) && !vid_fulldib_on_focus_mode) + { + force_mode_set = true; + VID_SetMode (vid_modenum, vid_curpal); + force_mode_set = false; + } + break; + + case WM_CLOSE: + // this causes Close in the right-click task bar menu not to work, but right + // now bad things happen if Close is handled in that case (garbage and a + // crash on Win95) + if (!in_mode_set) + { + if (MessageBox (mainwindow, "Are you sure you want to quit?", "Confirm Exit", + MB_YESNO | MB_SETFOREGROUND | MB_ICONQUESTION) == IDYES) + { + Sys_Quit (); + } + } + break; + + case MM_MCINOTIFY: + lRet = CDAudio_MessageHandler (hWnd, uMsg, wParam, lParam); + break; + + default: + /* pass all unhandled messages to DefWindowProc */ + lRet = DefWindowProc (hWnd, uMsg, wParam, lParam); + break; + } + + /* return 0 if handled message, 1 if not */ + return lRet; +} + + +extern void M_Menu_Options_f (void); +extern void M_Print (int cx, int cy, char *str); +extern void M_PrintWhite (int cx, int cy, char *str); +extern void M_DrawCharacter (int cx, int line, int num); +extern void M_DrawTransPic (int x, int y, qpic_t *pic); +extern void M_DrawPic (int x, int y, qpic_t *pic); + +static int vid_line, vid_wmodes; + +typedef struct +{ + int modenum; + char *desc; + int iscur; + int ismode13; + int width; +} modedesc_t; + +#define MAX_COLUMN_SIZE 5 +#define MODE_AREA_HEIGHT (MAX_COLUMN_SIZE + 6) +#define MAX_MODEDESCS (MAX_COLUMN_SIZE*3) + +static modedesc_t modedescs[MAX_MODEDESCS]; + +/* +================ +VID_MenuDraw +================ +*/ +void VID_MenuDraw (void) +{ + qpic_t *p; + char *ptr; + int lnummodes, i, j, k, column, row, dup, dupmode; + char temp[100]; + vmode_t *pv; + modedesc_t tmodedesc; + + p = Draw_CachePic ("gfx/vidmodes.lmp"); + M_DrawPic ( (320-p->width)/2, 4, p); + + for (i=0 ; i<3 ; i++) + { + ptr = VID_GetModeDescriptionMemCheck (i); + modedescs[i].modenum = modelist[i].modenum; + modedescs[i].desc = ptr; + modedescs[i].ismode13 = 0; + modedescs[i].iscur = 0; + + if (vid_modenum == i) + modedescs[i].iscur = 1; + } + + vid_wmodes = 3; + lnummodes = VID_NumModes (); + + for (i=3 ; iwidth != 360) || COM_CheckParm("-allow360"))) + { + dup = 0; + + for (j=3 ; jmode13; + modedescs[k].iscur = 0; + modedescs[k].width = pv->width; + + if (i == vid_modenum) + modedescs[k].iscur = 1; + + if (!dup) + vid_wmodes++; + } + } + } + } + +// sort the modes on width (to handle picking up oddball dibonly modes +// after all the others) + for (i=3 ; i<(vid_wmodes-1) ; i++) + { + for (j=(i+1) ; j modedescs[j].width) + { + tmodedesc = modedescs[i]; + modedescs[i] = modedescs[j]; + modedescs[j] = tmodedesc; + } + } + } + + + M_Print (13*8, 36, "Windowed Modes"); + + column = 16; + row = 36+2*8; + + for (i=0 ; i<3; i++) + { + if (modedescs[i].iscur) + M_PrintWhite (column, row, modedescs[i].desc); + else + M_Print (column, row, modedescs[i].desc); + + column += 13*8; + } + + if (vid_wmodes > 3) + { + M_Print (12*8, 36+4*8, "Fullscreen Modes"); + + column = 16; + row = 36+6*8; + + for (i=3 ; i= 3) + row += 3*8; + + M_DrawCharacter (column, row, 12+((int)(realtime*4)&1)); + } +} + + +/* +================ +VID_MenuKey +================ +*/ +void VID_MenuKey (int key) +{ + if (vid_testingmode) + return; + + switch (key) + { + case K_ESCAPE: + S_LocalSound ("misc/menu1.wav"); + M_Menu_Options_f (); + break; + + case K_LEFTARROW: + + S_LocalSound ("misc/menu1.wav"); + vid_line = ((vid_line / VID_ROW_SIZE) * VID_ROW_SIZE) + + ((vid_line + 2) % VID_ROW_SIZE); + + if (vid_line >= vid_wmodes) + vid_line = vid_wmodes - 1; + break; + + case K_RIGHTARROW: + S_LocalSound ("misc/menu1.wav"); + vid_line = ((vid_line / VID_ROW_SIZE) * VID_ROW_SIZE) + + ((vid_line + 4) % VID_ROW_SIZE); + + if (vid_line >= vid_wmodes) + vid_line = (vid_line / VID_ROW_SIZE) * VID_ROW_SIZE; + break; + + case K_UPARROW: + S_LocalSound ("misc/menu1.wav"); + vid_line -= VID_ROW_SIZE; + + if (vid_line < 0) + { + vid_line += ((vid_wmodes + (VID_ROW_SIZE - 1)) / + VID_ROW_SIZE) * VID_ROW_SIZE; + + while (vid_line >= vid_wmodes) + vid_line -= VID_ROW_SIZE; + } + break; + + case K_DOWNARROW: + S_LocalSound ("misc/menu1.wav"); + vid_line += VID_ROW_SIZE; + + if (vid_line >= vid_wmodes) + { + vid_line -= ((vid_wmodes + (VID_ROW_SIZE - 1)) / + VID_ROW_SIZE) * VID_ROW_SIZE; + + while (vid_line < 0) + vid_line += VID_ROW_SIZE; + } + break; + + case K_ENTER: + S_LocalSound ("misc/menu1.wav"); + VID_SetMode (modedescs[vid_line].modenum, vid_curpal); + break; + + case 'T': + case 't': + S_LocalSound ("misc/menu1.wav"); + // have to set this before setting the mode because WM_PAINT + // happens during the mode set and does a VID_Update, which + // checks vid_testingmode + vid_testingmode = 1; + vid_testendtime = realtime + 5.0; + + if (!VID_SetMode (modedescs[vid_line].modenum, vid_curpal)) + { + vid_testingmode = 0; + } + break; + + case 'D': + case 'd': + S_LocalSound ("misc/menu1.wav"); + firstupdate = 0; + Cvar_SetValue (&_vid_default_mode_win, vid_modenum); + break; + + default: + break; + } +} diff --git a/source/vid_x.c b/source/vid_x.c index 9ae02a69..dc981470 100644 --- a/source/vid_x.c +++ b/source/vid_x.c @@ -1,1114 +1,1114 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// vid_x.c -- general x video driver - -#define _BSD - -typedef unsigned short PIXEL; - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "quakedef.h" -#include "d_local.h" -#include "keys.h" - -cvar_t _windowed_mouse = {"_windowed_mouse","0",CVAR_ARCHIVE}; -cvar_t m_filter = {"m_filter","0",CVAR_ARCHIVE}; -float old_windowed_mouse; - -// not used -int VGA_width, VGA_height, VGA_rowbytes, VGA_bufferrowbytes, VGA_planar; -byte *VGA_pagebase; - - -qboolean mouse_avail; -int mouse_buttons=3; -int mouse_oldbuttonstate; -int mouse_buttonstate; -float mouse_x, mouse_y; -float old_mouse_x, old_mouse_y; -int p_mouse_x; -int p_mouse_y; -int ignorenext; -int bits_per_pixel; - -typedef struct -{ - int input; - int output; -} keymap_t; - -extern viddef_t vid; // global video state -unsigned short d_8to16table[256]; - -int num_shades=32; - -int d_con_indirect = 0; - -int vid_buffersize; - -static qboolean doShm; -static Display *x_disp; -static Colormap x_cmap; -static Window x_win; -static GC x_gc; -static Visual *x_vis; -static XVisualInfo *x_visinfo; -//static XImage *x_image; - -static int x_shmeventtype; -//static XShmSegmentInfo x_shminfo; - -static qboolean oktodraw = false; - -int XShmQueryExtension(Display *); -int XShmGetEventBase(Display *); - -int current_framebuffer; -static XImage *x_framebuffer[2] = { 0, 0 }; -static XShmSegmentInfo x_shminfo[2]; - -static int verbose=0; - -static byte current_palette[768]; - -static long X11_highhunkmark; -static long X11_buffersize; - -int vid_surfcachesize; -void *vid_surfcache; - -void (*vid_menudrawfn)(void); -void (*vid_menukeyfn)(int key); -void VID_MenuKey (int key); - -static PIXEL st2d_8to16table[256]; -static int shiftmask_fl=0; -static long r_shift,g_shift,b_shift; -static unsigned long r_mask,g_mask,b_mask; - -void shiftmask_init() -{ - unsigned int x; - r_mask=x_vis->red_mask; - g_mask=x_vis->green_mask; - b_mask=x_vis->blue_mask; - for(r_shift=-8,x=1;x0) { - p=(r<<(r_shift))&r_mask; - } else if(r_shift<0) { - p=(r>>(-r_shift))&r_mask; - } else p|=(r&r_mask); - - if(g_shift>0) { - p|=(g<<(g_shift))&g_mask; - } else if(g_shift<0) { - p|=(g>>(-g_shift))&g_mask; - } else p|=(g&g_mask); - - if(b_shift>0) { - p|=(b<<(b_shift))&b_mask; - } else if(b_shift<0) { - p|=(b>>(-b_shift))&b_mask; - } else p|=(b&b_mask); - - return p; -} - -void st2_fixup( XImage *framebuf, int x, int y, int width, int height) -{ - int xi,yi; - unsigned char *src; - PIXEL *dest; - - if( (x<0)||(y<0) )return; - - for (yi = y; yi < (y+height); yi++) { - src = &framebuf->data [yi * framebuf->bytes_per_line]; - dest = (PIXEL*)src; - for(xi = (x+width-1); xi >= x; xi--) { - dest[xi] = st2d_8to16table[src[xi]]; - } - } -} - - -// ======================================================================== -// Tragic death handler -// ======================================================================== - -void TragicDeath(int signal_num) -{ - XAutoRepeatOn(x_disp); - XCloseDisplay(x_disp); - Sys_Error("This death brought to you by the number %d\n", signal_num); -} - -// ======================================================================== -// makes a null cursor -// ======================================================================== - -static Cursor CreateNullCursor(Display *display, Window root) -{ - Pixmap cursormask; - XGCValues xgc; - GC gc; - XColor dummycolour; - Cursor cursor; - - cursormask = XCreatePixmap(display, root, 1, 1, 1/*depth*/); - xgc.function = GXclear; - gc = XCreateGC(display, cursormask, GCFunction, &xgc); - XFillRectangle(display, cursormask, gc, 0, 0, 1, 1); - dummycolour.pixel = 0; - dummycolour.red = 0; - dummycolour.flags = 04; - cursor = XCreatePixmapCursor(display, cursormask, cursormask, - &dummycolour,&dummycolour, 0,0); - XFreePixmap(display,cursormask); - XFreeGC(display,gc); - return cursor; -} - -void ResetFrameBuffer(void) -{ - int mem; - int pwidth; - - if (x_framebuffer[0]) - { - free(x_framebuffer[0]->data); - free(x_framebuffer[0]); - } - - if (d_pzbuffer) - { - D_FlushCaches (); - Hunk_FreeToHighMark (X11_highhunkmark); - d_pzbuffer = NULL; - } - X11_highhunkmark = Hunk_HighMark (); - -// alloc an extra line in case we want to wrap, and allocate the z-buffer - X11_buffersize = vid.width * vid.height * sizeof (*d_pzbuffer); - - vid_surfcachesize = D_SurfaceCacheForRes (vid.width, vid.height); - - X11_buffersize += vid_surfcachesize; - - d_pzbuffer = Hunk_HighAllocName (X11_buffersize, "video"); - if (d_pzbuffer == NULL) - Sys_Error ("Not enough memory for video mode\n"); - - vid_surfcache = (byte *) d_pzbuffer - + vid.width * vid.height * sizeof (*d_pzbuffer); - - D_InitCaches(vid_surfcache, vid_surfcachesize); - - pwidth = x_visinfo->depth / 8; - if (pwidth == 3) pwidth = 4; - mem = ((vid.width*pwidth+7)&~7) * vid.height; - - x_framebuffer[0] = XCreateImage( x_disp, - x_vis, - x_visinfo->depth, - ZPixmap, - 0, - Q_Malloc (mem), - vid.width, vid.height, - 32, - 0); - - if (!x_framebuffer[0]) - Sys_Error("VID: XCreateImage failed\n"); - - vid.buffer = (byte*) (x_framebuffer[0]); - vid.conbuffer = vid.buffer; - -} - -void ResetSharedFrameBuffers(void) -{ - - int size; - int key; - int minsize = getpagesize(); - int frm; - - if (d_pzbuffer) - { - D_FlushCaches (); - Hunk_FreeToHighMark (X11_highhunkmark); - d_pzbuffer = NULL; - } - - X11_highhunkmark = Hunk_HighMark (); - -// alloc an extra line in case we want to wrap, and allocate the z-buffer - X11_buffersize = vid.width * vid.height * sizeof (*d_pzbuffer); - - vid_surfcachesize = D_SurfaceCacheForRes (vid.width, vid.height); - - X11_buffersize += vid_surfcachesize; - - d_pzbuffer = Hunk_HighAllocName (X11_buffersize, "video"); - if (d_pzbuffer == NULL) - Sys_Error ("Not enough memory for video mode\n"); - - vid_surfcache = (byte *) d_pzbuffer - + vid.width * vid.height * sizeof (*d_pzbuffer); - - D_InitCaches(vid_surfcache, vid_surfcachesize); - - for (frm=0 ; frm<2 ; frm++) - { - - // free up old frame buffer memory - - if (x_framebuffer[frm]) - { - XShmDetach(x_disp, &x_shminfo[frm]); - free(x_framebuffer[frm]); - shmdt(x_shminfo[frm].shmaddr); - } - - // create the image - - x_framebuffer[frm] = XShmCreateImage( x_disp, - x_vis, - x_visinfo->depth, - ZPixmap, - 0, - &x_shminfo[frm], - vid.width, - vid.height ); - - // grab shared memory - - size = x_framebuffer[frm]->bytes_per_line - * x_framebuffer[frm]->height; - if (size < minsize) - Sys_Error("VID: Window must use at least %d bytes\n", minsize); - - key = random(); - x_shminfo[frm].shmid = shmget((key_t)key, size, IPC_CREAT|0777); - if (x_shminfo[frm].shmid==-1) - Sys_Error("VID: Could not get any shared memory\n"); - - // attach to the shared memory segment - x_shminfo[frm].shmaddr = - (void *) shmat(x_shminfo[frm].shmid, 0, 0); - - printf("VID: shared memory id=%d, addr=0x%lx\n", x_shminfo[frm].shmid, - (long) x_shminfo[frm].shmaddr); - - x_framebuffer[frm]->data = x_shminfo[frm].shmaddr; - - // get the X server to attach to it - - if (!XShmAttach(x_disp, &x_shminfo[frm])) - Sys_Error("VID: XShmAttach() failed\n"); - XSync(x_disp, 0); - shmctl(x_shminfo[frm].shmid, IPC_RMID, 0); - - } - -} - -// Called at startup to set up translation tables, takes 256 8 bit RGB values -// the palette data will go away after the call, so it must be copied off if -// the video driver will need it again - -void VID_Init (unsigned char *palette) -{ - - int pnum, i; - XVisualInfo template; - int num_visuals; - int template_mask; - - S_Init(); - - ignorenext=0; - vid.width = 320; - vid.height = 200; - vid.maxwarpwidth = WARP_WIDTH; - vid.maxwarpheight = WARP_HEIGHT; - vid.numpages = 2; - vid.colormap = host_colormap; - // vid.cbits = VID_CBITS; - // vid.grades = VID_GRADES; - vid.fullbright = 256 - LittleLong (*((int *)vid.colormap + 2048)); - - srandom(getpid()); - - verbose=COM_CheckParm("-verbose"); - -// open the display - x_disp = XOpenDisplay(0); - if (!x_disp) - { - if (getenv("DISPLAY")) - Sys_Error("VID: Could not open display [%s]\n", - getenv("DISPLAY")); - else - Sys_Error("VID: Could not open local display\n"); - } - -// catch signals so i can turn on auto-repeat - - { - struct sigaction sa; - sigaction(SIGINT, 0, &sa); - sa.sa_handler = TragicDeath; - sigaction(SIGINT, &sa, 0); - sigaction(SIGTERM, &sa, 0); - } - - XAutoRepeatOff(x_disp); - -// for debugging only - XSynchronize(x_disp, True); - -// check for command-line window size - if ((pnum=COM_CheckParm("-winsize"))) - { - if (pnum >= com_argc-2) - Sys_Error("VID: -winsize \n"); - vid.width = Q_atoi(com_argv[pnum+1]); - vid.height = Q_atoi(com_argv[pnum+2]); - if (!vid.width || !vid.height) - Sys_Error("VID: Bad window width/height\n"); - } - if ((pnum=COM_CheckParm("-width"))) { - if (pnum >= com_argc-1) - Sys_Error("VID: -width \n"); - vid.width = Q_atoi(com_argv[pnum+1]); - if (!vid.width) - Sys_Error("VID: Bad window width\n"); - } - if ((pnum=COM_CheckParm("-height"))) { - if (pnum >= com_argc-1) - Sys_Error("VID: -height \n"); - vid.height = Q_atoi(com_argv[pnum+1]); - if (!vid.height) - Sys_Error("VID: Bad window height\n"); - } - - template_mask = 0; - -// specify a visual id - if ((pnum=COM_CheckParm("-visualid"))) - { - if (pnum >= com_argc-1) - Sys_Error("VID: -visualid \n"); - template.visualid = Q_atoi(com_argv[pnum+1]); - template_mask = VisualIDMask; - } - -// If not specified, use default visual - else - { - int screen; - screen = XDefaultScreen(x_disp); - template.visualid = - XVisualIDFromVisual(XDefaultVisual(x_disp, screen)); - template_mask = VisualIDMask; - } - -// pick a visual- warn if more than one was available - x_visinfo = XGetVisualInfo(x_disp, template_mask, &template, &num_visuals); - if (num_visuals > 1) - { - printf("Found more than one visual id at depth %d:\n", template.depth); - for (i=0 ; ivisualid)); - printf(" screen %d\n", x_visinfo->screen); - printf(" red_mask 0x%x\n", (int)(x_visinfo->red_mask)); - printf(" green_mask 0x%x\n", (int)(x_visinfo->green_mask)); - printf(" blue_mask 0x%x\n", (int)(x_visinfo->blue_mask)); - printf(" colormap_size %d\n", x_visinfo->colormap_size); - printf(" bits_per_rgb %d\n", x_visinfo->bits_per_rgb); - } - - x_vis = x_visinfo->visual; - -// setup attributes for main window - { - int attribmask = CWEventMask | CWColormap | CWBorderPixel; - XSetWindowAttributes attribs; - Colormap tmpcmap; - - tmpcmap = XCreateColormap(x_disp, XRootWindow(x_disp, - x_visinfo->screen), x_vis, AllocNone); - - attribs.event_mask = StructureNotifyMask | KeyPressMask - | KeyReleaseMask | ExposureMask | PointerMotionMask | - ButtonPressMask | ButtonReleaseMask; - attribs.border_pixel = 0; - attribs.colormap = tmpcmap; - -// create the main window - x_win = XCreateWindow( x_disp, - XRootWindow(x_disp, x_visinfo->screen), - 0, 0, // x, y - vid.width, vid.height, - 0, // borderwidth - x_visinfo->depth, - InputOutput, - x_vis, - attribmask, - &attribs ); - XStoreName( x_disp,x_win,"xquake"); - - - if (x_visinfo->class != TrueColor) - XFreeColormap(x_disp, tmpcmap); - - } - - if (x_visinfo->depth == 8) - { - - // create and upload the palette - if (x_visinfo->class == PseudoColor) - { - x_cmap = XCreateColormap(x_disp, x_win, x_vis, AllocAll); - VID_SetPalette(palette); - XSetWindowColormap(x_disp, x_win, x_cmap); - } - - } - -// inviso cursor - XDefineCursor(x_disp, x_win, CreateNullCursor(x_disp, x_win)); - -// create the GC - { - XGCValues xgcvalues; - int valuemask = GCGraphicsExposures; - xgcvalues.graphics_exposures = False; - x_gc = XCreateGC(x_disp, x_win, valuemask, &xgcvalues ); - } - -// map the window - XMapWindow(x_disp, x_win); - -// wait for first exposure event - { - XEvent event; - do - { - XNextEvent(x_disp, &event); - if (event.type == Expose && !event.xexpose.count) - oktodraw = true; - } while (!oktodraw); - } -// now safe to draw - -// even if MITSHM is available, make sure it's a local connection - if (XShmQueryExtension(x_disp)) - { - char *displayname; - doShm = true; - displayname = (char *) getenv("DISPLAY"); - if (displayname) - { - char *d = displayname; - while (*d && (*d != ':')) d++; - if (*d) *d = 0; - if (!(!strcasecmp(displayname, "unix") || !*displayname)) - doShm = false; - } - } - - if (doShm) - { - x_shmeventtype = XShmGetEventBase(x_disp) + ShmCompletion; - ResetSharedFrameBuffers(); - } - else - ResetFrameBuffer(); - - current_framebuffer = 0; - vid.rowbytes = x_framebuffer[0]->bytes_per_line; - vid.buffer = x_framebuffer[0]->data; - vid.direct = 0; - vid.conbuffer = x_framebuffer[0]->data; - vid.conrowbytes = vid.rowbytes; - vid.conwidth = vid.width; - vid.conheight = vid.height; - vid.aspect = ((float)vid.height / (float)vid.width) * (320.0 / 240.0); - -// XSynchronize(x_disp, False); - -} - -void VID_ShiftPalette(unsigned char *p) -{ - VID_SetPalette(p); -} - - - -void VID_SetPalette(unsigned char *palette) -{ - - int i; - XColor colors[256]; - - for(i=0;i<256;i++) - st2d_8to16table[i]= xlib_rgb(palette[i*3], - palette[i*3+1],palette[i*3+2]); - - if (x_visinfo->class == PseudoColor && x_visinfo->depth == 8) - { - if (palette != current_palette) - memcpy(current_palette, palette, 768); - for (i=0 ; i<256 ; i++) - { - colors[i].pixel = i; - colors[i].flags = DoRed|DoGreen|DoBlue; - colors[i].red = palette[i*3] * 257; - colors[i].green = palette[i*3+1] * 257; - colors[i].blue = palette[i*3+2] * 257; - } - XStoreColors(x_disp, x_cmap, colors, 256); - } - -} - -// Called at shutdown - -void VID_Shutdown (void) -{ - Con_Printf("VID_Shutdown\n"); - XAutoRepeatOn(x_disp); - XCloseDisplay(x_disp); -} - -int XLateKey(XKeyEvent *ev) -{ - - int key; - char buf[64]; - KeySym keysym; - - key = 0; - - XLookupString(ev, buf, sizeof buf, &keysym, 0); - - switch(keysym) - { - case XK_KP_Page_Up: - case XK_Page_Up: key = K_PGUP; break; - - case XK_KP_Page_Down: - case XK_Page_Down: key = K_PGDN; break; - - case XK_KP_Home: - case XK_Home: key = K_HOME; break; - - case XK_KP_End: - case XK_End: key = K_END; break; - - case XK_KP_Left: - case XK_Left: key = K_LEFTARROW; break; - - case XK_KP_Right: - case XK_Right: key = K_RIGHTARROW; break; - - case XK_KP_Down: - case XK_Down: key = K_DOWNARROW; break; - - case XK_KP_Up: - case XK_Up: key = K_UPARROW; break; - - case XK_Escape: key = K_ESCAPE; break; - - case XK_KP_Enter: - case XK_Return: key = K_ENTER; break; - - case XK_Tab: key = K_TAB; break; - - case XK_F1: key = K_F1; break; - - case XK_F2: key = K_F2; break; - - case XK_F3: key = K_F3; break; - - case XK_F4: key = K_F4; break; - - case XK_F5: key = K_F5; break; - - case XK_F6: key = K_F6; break; - - case XK_F7: key = K_F7; break; - - case XK_F8: key = K_F8; break; - - case XK_F9: key = K_F9; break; - - case XK_F10: key = K_F10; break; - - case XK_F11: key = K_F11; break; - - case XK_F12: key = K_F12; break; - - case XK_BackSpace: key = K_BACKSPACE; break; - - case XK_KP_Delete: - case XK_Delete: key = K_DEL; break; - - case XK_Pause: key = K_PAUSE; break; - - case XK_Shift_L: - case XK_Shift_R: key = K_SHIFT; break; - - case XK_Execute: - case XK_Control_L: - case XK_Control_R: key = K_CTRL; break; - - case XK_Alt_L: - case XK_Meta_L: - case XK_Alt_R: - case XK_Meta_R: key = K_ALT; break; - - case XK_KP_Begin: key = K_AUX30; break; - - case XK_Insert: - case XK_KP_Insert: key = K_INS; break; - - case XK_KP_Multiply: key = '*'; break; - case XK_KP_Add: key = '+'; break; - case XK_KP_Subtract: key = '-'; break; - case XK_KP_Divide: key = '/'; break; - -#if 0 - case 0x021: key = '1';break;/* [!] */ - case 0x040: key = '2';break;/* [@] */ - case 0x023: key = '3';break;/* [#] */ - case 0x024: key = '4';break;/* [$] */ - case 0x025: key = '5';break;/* [%] */ - case 0x05e: key = '6';break;/* [^] */ - case 0x026: key = '7';break;/* [&] */ - case 0x02a: key = '8';break;/* [*] */ - case 0x028: key = '9';;break;/* [(] */ - case 0x029: key = '0';break;/* [)] */ - case 0x05f: key = '-';break;/* [_] */ - case 0x02b: key = '=';break;/* [+] */ - case 0x07c: key = '\'';break;/* [|] */ - case 0x07d: key = '[';break;/* [}] */ - case 0x07b: key = ']';break;/* [{] */ - case 0x022: key = '\'';break;/* ["] */ - case 0x03a: key = ';';break;/* [:] */ - case 0x03f: key = '/';break;/* [?] */ - case 0x03e: key = '.';break;/* [>] */ - case 0x03c: key = ',';break;/* [<] */ -#endif - - default: - key = *(unsigned char*)buf; - if (key >= 'A' && key <= 'Z') - key = key - 'A' + 'a'; -// fprintf(stdout, "case 0x0%x: key = ___;break;/* [%c] */\n", keysym); - break; - } - - return key; -} - -struct -{ - int key; - int down; -} keyq[64]; -int keyq_head=0; -int keyq_tail=0; - -int config_notify=0; -int config_notify_width; -int config_notify_height; - -void GetEvent(void) -{ - XEvent x_event; - int b; - - XNextEvent(x_disp, &x_event); - switch(x_event.type) { - case KeyPress: - keyq[keyq_head].key = XLateKey(&x_event.xkey); - keyq[keyq_head].down = true; - keyq_head = (keyq_head + 1) & 63; - break; - case KeyRelease: - keyq[keyq_head].key = XLateKey(&x_event.xkey); - keyq[keyq_head].down = false; - keyq_head = (keyq_head + 1) & 63; - break; - - case MotionNotify: - if (_windowed_mouse.value) { - mouse_x = (float) ((int)x_event.xmotion.x - (int)(vid.width/2)); - mouse_y = (float) ((int)x_event.xmotion.y - (int)(vid.height/2)); -//printf("m: x=%d,y=%d, mx=%3.2f,my=%3.2f\n", -// x_event.xmotion.x, x_event.xmotion.y, mouse_x, mouse_y); - - /* move the mouse to the window center again */ - XSelectInput(x_disp,x_win,StructureNotifyMask|KeyPressMask - |KeyReleaseMask|ExposureMask - |ButtonPressMask - |ButtonReleaseMask); - XWarpPointer(x_disp,None,x_win,0,0,0,0, - (vid.width/2),(vid.height/2)); - XSelectInput(x_disp,x_win,StructureNotifyMask|KeyPressMask - |KeyReleaseMask|ExposureMask - |PointerMotionMask|ButtonPressMask - |ButtonReleaseMask); - } else { - mouse_x = (float) (x_event.xmotion.x-p_mouse_x); - mouse_y = (float) (x_event.xmotion.y-p_mouse_y); - p_mouse_x=x_event.xmotion.x; - p_mouse_y=x_event.xmotion.y; - } - break; - - case ButtonPress: - b=-1; - if (x_event.xbutton.button == 1) - b = 0; - else if (x_event.xbutton.button == 2) - b = 2; - else if (x_event.xbutton.button == 3) - b = 1; - if (b>=0) - mouse_buttonstate |= 1<=0) - mouse_buttonstate &= ~(1<bytes_per_line; - vid.buffer = x_framebuffer[current_framebuffer]->data; - vid.conbuffer = vid.buffer; - vid.conwidth = vid.width; - vid.conheight = vid.height; - vid.conrowbytes = vid.rowbytes; - vid.recalc_refdef = 1; // force a surface cache flush - Con_CheckResize(); - Con_Clear_f(); - return; - } - - if (doShm) - { - - while (rects) - { - if (x_visinfo->depth != 8) - st2_fixup( x_framebuffer[current_framebuffer], - rects->x, rects->y, rects->width, - rects->height); - if (!XShmPutImage(x_disp, x_win, x_gc, - x_framebuffer[current_framebuffer], rects->x, rects->y, - rects->x, rects->y, rects->width, rects->height, True)) - Sys_Error("VID_Update: XShmPutImage failed\n"); - oktodraw = false; - while (!oktodraw) GetEvent(); - rects = rects->pnext; - } - current_framebuffer = !current_framebuffer; - vid.buffer = x_framebuffer[current_framebuffer]->data; - vid.conbuffer = vid.buffer; - XSync(x_disp, False); - } - else - { - while (rects) - { - if (x_visinfo->depth != 8) - st2_fixup( x_framebuffer[current_framebuffer], - rects->x, rects->y, rects->width, - rects->height); - XPutImage(x_disp, x_win, x_gc, x_framebuffer[0], rects->x, - rects->y, rects->x, rects->y, rects->width, rects->height); - rects = rects->pnext; - } - XSync(x_disp, False); - } - -} - -static int dither; - -void VID_DitherOn(void) -{ - if (dither == 0) - { - vid.recalc_refdef = 1; - dither = 1; - } -} - -void VID_DitherOff(void) -{ - if (dither) - { - vid.recalc_refdef = 1; - dither = 0; - } -} - -int Sys_OpenWindow(void) -{ - return 0; -} - -void Sys_EraseWindow(int window) -{ -} - -void Sys_DrawCircle(int window, int x, int y, int r) -{ -} - -void Sys_DisplayWindow(int window) -{ -} - -void Sys_SendKeyEvents(void) -{ -// get events from x server - if (x_disp) - { - while (XPending(x_disp)) GetEvent(); - while (keyq_head != keyq_tail) - { - Key_Event(keyq[keyq_tail].key, keyq[keyq_tail].down); - keyq_tail = (keyq_tail + 1) & 63; - } - } -} - -#if 0 -char *Sys_ConsoleInput (void) -{ - - static char text[256]; - int len; - fd_set readfds; - int ready; - struct timeval timeout; - - timeout.tv_sec = 0; - timeout.tv_usec = 0; - FD_ZERO(&readfds); - FD_SET(0, &readfds); - ready = select(1, &readfds, 0, 0, &timeout); - - if (ready>0) - { - len = read (0, text, sizeof(text)); - if (len >= 1) - { - text[len-1] = 0; // rip off the /n and terminate - return text; - } - } - - return 0; - -} -#endif - -void D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height) -{ -// direct drawing of the "accessing disk" icon isn't supported under Linux -} - -void D_EndDirectRect (int x, int y, int width, int height) -{ -// direct drawing of the "accessing disk" icon isn't supported under Linux -} - -void IN_Init (void) -{ - Cvar_RegisterVariable (&_windowed_mouse); - Cvar_RegisterVariable (&m_filter); - if ( COM_CheckParm ("-nomouse") ) - return; - mouse_x = mouse_y = 0.0; - mouse_avail = 1; -} - -void IN_Shutdown (void) -{ - mouse_avail = 0; -} - -void IN_Commands (void) -{ - int i; - - if (!mouse_avail) return; - - for (i=0 ; isidemove += m_side.value * mouse_x; - else - cl.viewangles[YAW] -= m_yaw.value * mouse_x; - if (in_mlook.state & 1) - V_StopPitchDrift (); - - if ( (in_mlook.state & 1) && !(in_strafe.state & 1)) { - cl.viewangles[PITCH] += m_pitch.value * mouse_y; - if (cl.viewangles[PITCH] > 80) - cl.viewangles[PITCH] = 80; - if (cl.viewangles[PITCH] < -70) - cl.viewangles[PITCH] = -70; - } else { - cmd->forwardmove -= m_forward.value * mouse_y; - } - mouse_x = mouse_y = 0.0; -} - -void VID_LockBuffer (void) -{ -} - -void VID_UnlockBuffer (void) -{ -} - +/* +Copyright (C) 1996-1997 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. + +*/ +// vid_x.c -- general x video driver + +#define _BSD + +typedef unsigned short PIXEL; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "quakedef.h" +#include "d_local.h" +#include "keys.h" + +cvar_t _windowed_mouse = {"_windowed_mouse","0",CVAR_ARCHIVE}; +cvar_t m_filter = {"m_filter","0",CVAR_ARCHIVE}; +float old_windowed_mouse; + +// not used +int VGA_width, VGA_height, VGA_rowbytes, VGA_bufferrowbytes, VGA_planar; +byte *VGA_pagebase; + + +qboolean mouse_avail; +int mouse_buttons=3; +int mouse_oldbuttonstate; +int mouse_buttonstate; +float mouse_x, mouse_y; +float old_mouse_x, old_mouse_y; +int p_mouse_x; +int p_mouse_y; +int ignorenext; +int bits_per_pixel; + +typedef struct +{ + int input; + int output; +} keymap_t; + +extern viddef_t vid; // global video state +unsigned short d_8to16table[256]; + +int num_shades=32; + +int d_con_indirect = 0; + +int vid_buffersize; + +static qboolean doShm; +static Display *x_disp; +static Colormap x_cmap; +static Window x_win; +static GC x_gc; +static Visual *x_vis; +static XVisualInfo *x_visinfo; +//static XImage *x_image; + +static int x_shmeventtype; +//static XShmSegmentInfo x_shminfo; + +static qboolean oktodraw = false; + +int XShmQueryExtension(Display *); +int XShmGetEventBase(Display *); + +int current_framebuffer; +static XImage *x_framebuffer[2] = { 0, 0 }; +static XShmSegmentInfo x_shminfo[2]; + +static int verbose=0; + +static byte current_palette[768]; + +static long X11_highhunkmark; +static long X11_buffersize; + +int vid_surfcachesize; +void *vid_surfcache; + +void (*vid_menudrawfn)(void); +void (*vid_menukeyfn)(int key); +void VID_MenuKey (int key); + +static PIXEL st2d_8to16table[256]; +static int shiftmask_fl=0; +static long r_shift,g_shift,b_shift; +static unsigned long r_mask,g_mask,b_mask; + +void shiftmask_init() +{ + unsigned int x; + r_mask=x_vis->red_mask; + g_mask=x_vis->green_mask; + b_mask=x_vis->blue_mask; + for(r_shift=-8,x=1;x0) { + p=(r<<(r_shift))&r_mask; + } else if(r_shift<0) { + p=(r>>(-r_shift))&r_mask; + } else p|=(r&r_mask); + + if(g_shift>0) { + p|=(g<<(g_shift))&g_mask; + } else if(g_shift<0) { + p|=(g>>(-g_shift))&g_mask; + } else p|=(g&g_mask); + + if(b_shift>0) { + p|=(b<<(b_shift))&b_mask; + } else if(b_shift<0) { + p|=(b>>(-b_shift))&b_mask; + } else p|=(b&b_mask); + + return p; +} + +void st2_fixup( XImage *framebuf, int x, int y, int width, int height) +{ + int xi,yi; + unsigned char *src; + PIXEL *dest; + + if( (x<0)||(y<0) )return; + + for (yi = y; yi < (y+height); yi++) { + src = &framebuf->data [yi * framebuf->bytes_per_line]; + dest = (PIXEL*)src; + for(xi = (x+width-1); xi >= x; xi--) { + dest[xi] = st2d_8to16table[src[xi]]; + } + } +} + + +// ======================================================================== +// Tragic death handler +// ======================================================================== + +void TragicDeath(int signal_num) +{ + XAutoRepeatOn(x_disp); + XCloseDisplay(x_disp); + Sys_Error("This death brought to you by the number %d\n", signal_num); +} + +// ======================================================================== +// makes a null cursor +// ======================================================================== + +static Cursor CreateNullCursor(Display *display, Window root) +{ + Pixmap cursormask; + XGCValues xgc; + GC gc; + XColor dummycolour; + Cursor cursor; + + cursormask = XCreatePixmap(display, root, 1, 1, 1/*depth*/); + xgc.function = GXclear; + gc = XCreateGC(display, cursormask, GCFunction, &xgc); + XFillRectangle(display, cursormask, gc, 0, 0, 1, 1); + dummycolour.pixel = 0; + dummycolour.red = 0; + dummycolour.flags = 04; + cursor = XCreatePixmapCursor(display, cursormask, cursormask, + &dummycolour,&dummycolour, 0,0); + XFreePixmap(display,cursormask); + XFreeGC(display,gc); + return cursor; +} + +void ResetFrameBuffer(void) +{ + int mem; + int pwidth; + + if (x_framebuffer[0]) + { + free(x_framebuffer[0]->data); + free(x_framebuffer[0]); + } + + if (d_pzbuffer) + { + D_FlushCaches (); + Hunk_FreeToHighMark (X11_highhunkmark); + d_pzbuffer = NULL; + } + X11_highhunkmark = Hunk_HighMark (); + +// alloc an extra line in case we want to wrap, and allocate the z-buffer + X11_buffersize = vid.width * vid.height * sizeof (*d_pzbuffer); + + vid_surfcachesize = D_SurfaceCacheForRes (vid.width, vid.height); + + X11_buffersize += vid_surfcachesize; + + d_pzbuffer = Hunk_HighAllocName (X11_buffersize, "video"); + if (d_pzbuffer == NULL) + Sys_Error ("Not enough memory for video mode\n"); + + vid_surfcache = (byte *) d_pzbuffer + + vid.width * vid.height * sizeof (*d_pzbuffer); + + D_InitCaches(vid_surfcache, vid_surfcachesize); + + pwidth = x_visinfo->depth / 8; + if (pwidth == 3) pwidth = 4; + mem = ((vid.width*pwidth+7)&~7) * vid.height; + + x_framebuffer[0] = XCreateImage( x_disp, + x_vis, + x_visinfo->depth, + ZPixmap, + 0, + Q_Malloc (mem), + vid.width, vid.height, + 32, + 0); + + if (!x_framebuffer[0]) + Sys_Error("VID: XCreateImage failed\n"); + + vid.buffer = (byte*) (x_framebuffer[0]); + vid.conbuffer = vid.buffer; + +} + +void ResetSharedFrameBuffers(void) +{ + + int size; + int key; + int minsize = getpagesize(); + int frm; + + if (d_pzbuffer) + { + D_FlushCaches (); + Hunk_FreeToHighMark (X11_highhunkmark); + d_pzbuffer = NULL; + } + + X11_highhunkmark = Hunk_HighMark (); + +// alloc an extra line in case we want to wrap, and allocate the z-buffer + X11_buffersize = vid.width * vid.height * sizeof (*d_pzbuffer); + + vid_surfcachesize = D_SurfaceCacheForRes (vid.width, vid.height); + + X11_buffersize += vid_surfcachesize; + + d_pzbuffer = Hunk_HighAllocName (X11_buffersize, "video"); + if (d_pzbuffer == NULL) + Sys_Error ("Not enough memory for video mode\n"); + + vid_surfcache = (byte *) d_pzbuffer + + vid.width * vid.height * sizeof (*d_pzbuffer); + + D_InitCaches(vid_surfcache, vid_surfcachesize); + + for (frm=0 ; frm<2 ; frm++) + { + + // free up old frame buffer memory + + if (x_framebuffer[frm]) + { + XShmDetach(x_disp, &x_shminfo[frm]); + free(x_framebuffer[frm]); + shmdt(x_shminfo[frm].shmaddr); + } + + // create the image + + x_framebuffer[frm] = XShmCreateImage( x_disp, + x_vis, + x_visinfo->depth, + ZPixmap, + 0, + &x_shminfo[frm], + vid.width, + vid.height ); + + // grab shared memory + + size = x_framebuffer[frm]->bytes_per_line + * x_framebuffer[frm]->height; + if (size < minsize) + Sys_Error("VID: Window must use at least %d bytes\n", minsize); + + key = random(); + x_shminfo[frm].shmid = shmget((key_t)key, size, IPC_CREAT|0777); + if (x_shminfo[frm].shmid==-1) + Sys_Error("VID: Could not get any shared memory\n"); + + // attach to the shared memory segment + x_shminfo[frm].shmaddr = + (void *) shmat(x_shminfo[frm].shmid, 0, 0); + + printf("VID: shared memory id=%d, addr=0x%lx\n", x_shminfo[frm].shmid, + (long) x_shminfo[frm].shmaddr); + + x_framebuffer[frm]->data = x_shminfo[frm].shmaddr; + + // get the X server to attach to it + + if (!XShmAttach(x_disp, &x_shminfo[frm])) + Sys_Error("VID: XShmAttach() failed\n"); + XSync(x_disp, 0); + shmctl(x_shminfo[frm].shmid, IPC_RMID, 0); + + } + +} + +// Called at startup to set up translation tables, takes 256 8 bit RGB values +// the palette data will go away after the call, so it must be copied off if +// the video driver will need it again + +void VID_Init (unsigned char *palette) +{ + + int pnum, i; + XVisualInfo template; + int num_visuals; + int template_mask; + + S_Init(); + + ignorenext=0; + vid.width = 320; + vid.height = 200; + vid.maxwarpwidth = WARP_WIDTH; + vid.maxwarpheight = WARP_HEIGHT; + vid.numpages = 2; + vid.colormap = host_colormap; + // vid.cbits = VID_CBITS; + // vid.grades = VID_GRADES; + vid.fullbright = 256 - LittleLong (*((int *)vid.colormap + 2048)); + + srandom(getpid()); + + verbose=COM_CheckParm("-verbose"); + +// open the display + x_disp = XOpenDisplay(0); + if (!x_disp) + { + if (getenv("DISPLAY")) + Sys_Error("VID: Could not open display [%s]\n", + getenv("DISPLAY")); + else + Sys_Error("VID: Could not open local display\n"); + } + +// catch signals so i can turn on auto-repeat + + { + struct sigaction sa; + sigaction(SIGINT, 0, &sa); + sa.sa_handler = TragicDeath; + sigaction(SIGINT, &sa, 0); + sigaction(SIGTERM, &sa, 0); + } + + XAutoRepeatOff(x_disp); + +// for debugging only + XSynchronize(x_disp, True); + +// check for command-line window size + if ((pnum=COM_CheckParm("-winsize"))) + { + if (pnum >= com_argc-2) + Sys_Error("VID: -winsize \n"); + vid.width = Q_atoi(com_argv[pnum+1]); + vid.height = Q_atoi(com_argv[pnum+2]); + if (!vid.width || !vid.height) + Sys_Error("VID: Bad window width/height\n"); + } + if ((pnum=COM_CheckParm("-width"))) { + if (pnum >= com_argc-1) + Sys_Error("VID: -width \n"); + vid.width = Q_atoi(com_argv[pnum+1]); + if (!vid.width) + Sys_Error("VID: Bad window width\n"); + } + if ((pnum=COM_CheckParm("-height"))) { + if (pnum >= com_argc-1) + Sys_Error("VID: -height \n"); + vid.height = Q_atoi(com_argv[pnum+1]); + if (!vid.height) + Sys_Error("VID: Bad window height\n"); + } + + template_mask = 0; + +// specify a visual id + if ((pnum=COM_CheckParm("-visualid"))) + { + if (pnum >= com_argc-1) + Sys_Error("VID: -visualid \n"); + template.visualid = Q_atoi(com_argv[pnum+1]); + template_mask = VisualIDMask; + } + +// If not specified, use default visual + else + { + int screen; + screen = XDefaultScreen(x_disp); + template.visualid = + XVisualIDFromVisual(XDefaultVisual(x_disp, screen)); + template_mask = VisualIDMask; + } + +// pick a visual- warn if more than one was available + x_visinfo = XGetVisualInfo(x_disp, template_mask, &template, &num_visuals); + if (num_visuals > 1) + { + printf("Found more than one visual id at depth %d:\n", template.depth); + for (i=0 ; ivisualid)); + printf(" screen %d\n", x_visinfo->screen); + printf(" red_mask 0x%x\n", (int)(x_visinfo->red_mask)); + printf(" green_mask 0x%x\n", (int)(x_visinfo->green_mask)); + printf(" blue_mask 0x%x\n", (int)(x_visinfo->blue_mask)); + printf(" colormap_size %d\n", x_visinfo->colormap_size); + printf(" bits_per_rgb %d\n", x_visinfo->bits_per_rgb); + } + + x_vis = x_visinfo->visual; + +// setup attributes for main window + { + int attribmask = CWEventMask | CWColormap | CWBorderPixel; + XSetWindowAttributes attribs; + Colormap tmpcmap; + + tmpcmap = XCreateColormap(x_disp, XRootWindow(x_disp, + x_visinfo->screen), x_vis, AllocNone); + + attribs.event_mask = StructureNotifyMask | KeyPressMask + | KeyReleaseMask | ExposureMask | PointerMotionMask | + ButtonPressMask | ButtonReleaseMask; + attribs.border_pixel = 0; + attribs.colormap = tmpcmap; + +// create the main window + x_win = XCreateWindow( x_disp, + XRootWindow(x_disp, x_visinfo->screen), + 0, 0, // x, y + vid.width, vid.height, + 0, // borderwidth + x_visinfo->depth, + InputOutput, + x_vis, + attribmask, + &attribs ); + XStoreName( x_disp,x_win,"xquake"); + + + if (x_visinfo->class != TrueColor) + XFreeColormap(x_disp, tmpcmap); + + } + + if (x_visinfo->depth == 8) + { + + // create and upload the palette + if (x_visinfo->class == PseudoColor) + { + x_cmap = XCreateColormap(x_disp, x_win, x_vis, AllocAll); + VID_SetPalette(palette); + XSetWindowColormap(x_disp, x_win, x_cmap); + } + + } + +// inviso cursor + XDefineCursor(x_disp, x_win, CreateNullCursor(x_disp, x_win)); + +// create the GC + { + XGCValues xgcvalues; + int valuemask = GCGraphicsExposures; + xgcvalues.graphics_exposures = False; + x_gc = XCreateGC(x_disp, x_win, valuemask, &xgcvalues ); + } + +// map the window + XMapWindow(x_disp, x_win); + +// wait for first exposure event + { + XEvent event; + do + { + XNextEvent(x_disp, &event); + if (event.type == Expose && !event.xexpose.count) + oktodraw = true; + } while (!oktodraw); + } +// now safe to draw + +// even if MITSHM is available, make sure it's a local connection + if (XShmQueryExtension(x_disp)) + { + char *displayname; + doShm = true; + displayname = (char *) getenv("DISPLAY"); + if (displayname) + { + char *d = displayname; + while (*d && (*d != ':')) d++; + if (*d) *d = 0; + if (!(!strcasecmp(displayname, "unix") || !*displayname)) + doShm = false; + } + } + + if (doShm) + { + x_shmeventtype = XShmGetEventBase(x_disp) + ShmCompletion; + ResetSharedFrameBuffers(); + } + else + ResetFrameBuffer(); + + current_framebuffer = 0; + vid.rowbytes = x_framebuffer[0]->bytes_per_line; + vid.buffer = x_framebuffer[0]->data; + vid.direct = 0; + vid.conbuffer = x_framebuffer[0]->data; + vid.conrowbytes = vid.rowbytes; + vid.conwidth = vid.width; + vid.conheight = vid.height; + vid.aspect = ((float)vid.height / (float)vid.width) * (320.0 / 240.0); + +// XSynchronize(x_disp, False); + +} + +void VID_ShiftPalette(unsigned char *p) +{ + VID_SetPalette(p); +} + + + +void VID_SetPalette(unsigned char *palette) +{ + + int i; + XColor colors[256]; + + for(i=0;i<256;i++) + st2d_8to16table[i]= xlib_rgb(palette[i*3], + palette[i*3+1],palette[i*3+2]); + + if (x_visinfo->class == PseudoColor && x_visinfo->depth == 8) + { + if (palette != current_palette) + memcpy(current_palette, palette, 768); + for (i=0 ; i<256 ; i++) + { + colors[i].pixel = i; + colors[i].flags = DoRed|DoGreen|DoBlue; + colors[i].red = palette[i*3] * 257; + colors[i].green = palette[i*3+1] * 257; + colors[i].blue = palette[i*3+2] * 257; + } + XStoreColors(x_disp, x_cmap, colors, 256); + } + +} + +// Called at shutdown + +void VID_Shutdown (void) +{ + Con_Printf("VID_Shutdown\n"); + XAutoRepeatOn(x_disp); + XCloseDisplay(x_disp); +} + +int XLateKey(XKeyEvent *ev) +{ + + int key; + char buf[64]; + KeySym keysym; + + key = 0; + + XLookupString(ev, buf, sizeof buf, &keysym, 0); + + switch(keysym) + { + case XK_KP_Page_Up: + case XK_Page_Up: key = K_PGUP; break; + + case XK_KP_Page_Down: + case XK_Page_Down: key = K_PGDN; break; + + case XK_KP_Home: + case XK_Home: key = K_HOME; break; + + case XK_KP_End: + case XK_End: key = K_END; break; + + case XK_KP_Left: + case XK_Left: key = K_LEFTARROW; break; + + case XK_KP_Right: + case XK_Right: key = K_RIGHTARROW; break; + + case XK_KP_Down: + case XK_Down: key = K_DOWNARROW; break; + + case XK_KP_Up: + case XK_Up: key = K_UPARROW; break; + + case XK_Escape: key = K_ESCAPE; break; + + case XK_KP_Enter: + case XK_Return: key = K_ENTER; break; + + case XK_Tab: key = K_TAB; break; + + case XK_F1: key = K_F1; break; + + case XK_F2: key = K_F2; break; + + case XK_F3: key = K_F3; break; + + case XK_F4: key = K_F4; break; + + case XK_F5: key = K_F5; break; + + case XK_F6: key = K_F6; break; + + case XK_F7: key = K_F7; break; + + case XK_F8: key = K_F8; break; + + case XK_F9: key = K_F9; break; + + case XK_F10: key = K_F10; break; + + case XK_F11: key = K_F11; break; + + case XK_F12: key = K_F12; break; + + case XK_BackSpace: key = K_BACKSPACE; break; + + case XK_KP_Delete: + case XK_Delete: key = K_DEL; break; + + case XK_Pause: key = K_PAUSE; break; + + case XK_Shift_L: + case XK_Shift_R: key = K_SHIFT; break; + + case XK_Execute: + case XK_Control_L: + case XK_Control_R: key = K_CTRL; break; + + case XK_Alt_L: + case XK_Meta_L: + case XK_Alt_R: + case XK_Meta_R: key = K_ALT; break; + + case XK_KP_Begin: key = K_AUX30; break; + + case XK_Insert: + case XK_KP_Insert: key = K_INS; break; + + case XK_KP_Multiply: key = '*'; break; + case XK_KP_Add: key = '+'; break; + case XK_KP_Subtract: key = '-'; break; + case XK_KP_Divide: key = '/'; break; + +#if 0 + case 0x021: key = '1';break;/* [!] */ + case 0x040: key = '2';break;/* [@] */ + case 0x023: key = '3';break;/* [#] */ + case 0x024: key = '4';break;/* [$] */ + case 0x025: key = '5';break;/* [%] */ + case 0x05e: key = '6';break;/* [^] */ + case 0x026: key = '7';break;/* [&] */ + case 0x02a: key = '8';break;/* [*] */ + case 0x028: key = '9';;break;/* [(] */ + case 0x029: key = '0';break;/* [)] */ + case 0x05f: key = '-';break;/* [_] */ + case 0x02b: key = '=';break;/* [+] */ + case 0x07c: key = '\'';break;/* [|] */ + case 0x07d: key = '[';break;/* [}] */ + case 0x07b: key = ']';break;/* [{] */ + case 0x022: key = '\'';break;/* ["] */ + case 0x03a: key = ';';break;/* [:] */ + case 0x03f: key = '/';break;/* [?] */ + case 0x03e: key = '.';break;/* [>] */ + case 0x03c: key = ',';break;/* [<] */ +#endif + + default: + key = *(unsigned char*)buf; + if (key >= 'A' && key <= 'Z') + key = key - 'A' + 'a'; +// fprintf(stdout, "case 0x0%x: key = ___;break;/* [%c] */\n", keysym); + break; + } + + return key; +} + +struct +{ + int key; + int down; +} keyq[64]; +int keyq_head=0; +int keyq_tail=0; + +int config_notify=0; +int config_notify_width; +int config_notify_height; + +void GetEvent(void) +{ + XEvent x_event; + int b; + + XNextEvent(x_disp, &x_event); + switch(x_event.type) { + case KeyPress: + keyq[keyq_head].key = XLateKey(&x_event.xkey); + keyq[keyq_head].down = true; + keyq_head = (keyq_head + 1) & 63; + break; + case KeyRelease: + keyq[keyq_head].key = XLateKey(&x_event.xkey); + keyq[keyq_head].down = false; + keyq_head = (keyq_head + 1) & 63; + break; + + case MotionNotify: + if (_windowed_mouse.value) { + mouse_x = (float) ((int)x_event.xmotion.x - (int)(vid.width/2)); + mouse_y = (float) ((int)x_event.xmotion.y - (int)(vid.height/2)); +//printf("m: x=%d,y=%d, mx=%3.2f,my=%3.2f\n", +// x_event.xmotion.x, x_event.xmotion.y, mouse_x, mouse_y); + + /* move the mouse to the window center again */ + XSelectInput(x_disp,x_win,StructureNotifyMask|KeyPressMask + |KeyReleaseMask|ExposureMask + |ButtonPressMask + |ButtonReleaseMask); + XWarpPointer(x_disp,None,x_win,0,0,0,0, + (vid.width/2),(vid.height/2)); + XSelectInput(x_disp,x_win,StructureNotifyMask|KeyPressMask + |KeyReleaseMask|ExposureMask + |PointerMotionMask|ButtonPressMask + |ButtonReleaseMask); + } else { + mouse_x = (float) (x_event.xmotion.x-p_mouse_x); + mouse_y = (float) (x_event.xmotion.y-p_mouse_y); + p_mouse_x=x_event.xmotion.x; + p_mouse_y=x_event.xmotion.y; + } + break; + + case ButtonPress: + b=-1; + if (x_event.xbutton.button == 1) + b = 0; + else if (x_event.xbutton.button == 2) + b = 2; + else if (x_event.xbutton.button == 3) + b = 1; + if (b>=0) + mouse_buttonstate |= 1<=0) + mouse_buttonstate &= ~(1<bytes_per_line; + vid.buffer = x_framebuffer[current_framebuffer]->data; + vid.conbuffer = vid.buffer; + vid.conwidth = vid.width; + vid.conheight = vid.height; + vid.conrowbytes = vid.rowbytes; + vid.recalc_refdef = 1; // force a surface cache flush + Con_CheckResize(); + Con_Clear_f(); + return; + } + + if (doShm) + { + + while (rects) + { + if (x_visinfo->depth != 8) + st2_fixup( x_framebuffer[current_framebuffer], + rects->x, rects->y, rects->width, + rects->height); + if (!XShmPutImage(x_disp, x_win, x_gc, + x_framebuffer[current_framebuffer], rects->x, rects->y, + rects->x, rects->y, rects->width, rects->height, True)) + Sys_Error("VID_Update: XShmPutImage failed\n"); + oktodraw = false; + while (!oktodraw) GetEvent(); + rects = rects->pnext; + } + current_framebuffer = !current_framebuffer; + vid.buffer = x_framebuffer[current_framebuffer]->data; + vid.conbuffer = vid.buffer; + XSync(x_disp, False); + } + else + { + while (rects) + { + if (x_visinfo->depth != 8) + st2_fixup( x_framebuffer[current_framebuffer], + rects->x, rects->y, rects->width, + rects->height); + XPutImage(x_disp, x_win, x_gc, x_framebuffer[0], rects->x, + rects->y, rects->x, rects->y, rects->width, rects->height); + rects = rects->pnext; + } + XSync(x_disp, False); + } + +} + +static int dither; + +void VID_DitherOn(void) +{ + if (dither == 0) + { + vid.recalc_refdef = 1; + dither = 1; + } +} + +void VID_DitherOff(void) +{ + if (dither) + { + vid.recalc_refdef = 1; + dither = 0; + } +} + +int Sys_OpenWindow(void) +{ + return 0; +} + +void Sys_EraseWindow(int window) +{ +} + +void Sys_DrawCircle(int window, int x, int y, int r) +{ +} + +void Sys_DisplayWindow(int window) +{ +} + +void Sys_SendKeyEvents(void) +{ +// get events from x server + if (x_disp) + { + while (XPending(x_disp)) GetEvent(); + while (keyq_head != keyq_tail) + { + Key_Event(keyq[keyq_tail].key, keyq[keyq_tail].down); + keyq_tail = (keyq_tail + 1) & 63; + } + } +} + +#if 0 +char *Sys_ConsoleInput (void) +{ + + static char text[256]; + int len; + fd_set readfds; + int ready; + struct timeval timeout; + + timeout.tv_sec = 0; + timeout.tv_usec = 0; + FD_ZERO(&readfds); + FD_SET(0, &readfds); + ready = select(1, &readfds, 0, 0, &timeout); + + if (ready>0) + { + len = read (0, text, sizeof(text)); + if (len >= 1) + { + text[len-1] = 0; // rip off the /n and terminate + return text; + } + } + + return 0; + +} +#endif + +void D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height) +{ +// direct drawing of the "accessing disk" icon isn't supported under Linux +} + +void D_EndDirectRect (int x, int y, int width, int height) +{ +// direct drawing of the "accessing disk" icon isn't supported under Linux +} + +void IN_Init (void) +{ + Cvar_RegisterVariable (&_windowed_mouse); + Cvar_RegisterVariable (&m_filter); + if ( COM_CheckParm ("-nomouse") ) + return; + mouse_x = mouse_y = 0.0; + mouse_avail = 1; +} + +void IN_Shutdown (void) +{ + mouse_avail = 0; +} + +void IN_Commands (void) +{ + int i; + + if (!mouse_avail) return; + + for (i=0 ; isidemove += m_side.value * mouse_x; + else + cl.viewangles[YAW] -= m_yaw.value * mouse_x; + if (in_mlook.state & 1) + V_StopPitchDrift (); + + if ( (in_mlook.state & 1) && !(in_strafe.state & 1)) { + cl.viewangles[PITCH] += m_pitch.value * mouse_y; + if (cl.viewangles[PITCH] > 80) + cl.viewangles[PITCH] = 80; + if (cl.viewangles[PITCH] < -70) + cl.viewangles[PITCH] = -70; + } else { + cmd->forwardmove -= m_forward.value * mouse_y; + } + mouse_x = mouse_y = 0.0; +} + +void VID_LockBuffer (void) +{ +} + +void VID_UnlockBuffer (void) +{ +} + diff --git a/source/view.c b/source/view.c index eb0b8f58..6686f053 100644 --- a/source/view.c +++ b/source/view.c @@ -1,1198 +1,1205 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// view.c -- player eye positioning - -#include "quakedef.h" -#include "r_local.h" -#include "pmove.h" - -/* - -The view is allowed to move slightly from it's true position for bobbing, -but if it exceeds 8 pixels linear distance (spherical, not box), the list of -entities sent from the server may not include everything in the pvs, especially -when crossing a water boudnary. - -*/ - -cvar_t cl_rollspeed = {"cl_rollspeed", "200"}; -cvar_t cl_rollangle = {"cl_rollangle", "2.0"}; - -cvar_t cl_bob = {"cl_bob","0.02"}; -cvar_t cl_bobcycle = {"cl_bobcycle","0.6"}; -cvar_t cl_bobup = {"cl_bobup","0.5"}; - -cvar_t v_kicktime = {"v_kicktime", "0.5"}; -cvar_t v_kickroll = {"v_kickroll", "0.6"}; -cvar_t v_kickpitch = {"v_kickpitch", "0.6"}; - -qboolean V_OnIdleChange (cvar_t *var, char *value); -cvar_t v_iyaw_cycle = {"v_iyaw_cycle", "2", 0, V_OnIdleChange}; -cvar_t v_iroll_cycle = {"v_iroll_cycle", "0.5", 0, V_OnIdleChange}; -cvar_t v_ipitch_cycle = {"v_ipitch_cycle", "1", 0, V_OnIdleChange}; -cvar_t v_iyaw_level = {"v_iyaw_level", "0.3", 0, V_OnIdleChange}; -cvar_t v_iroll_level = {"v_iroll_level", "0.1", 0, V_OnIdleChange}; -cvar_t v_ipitch_level = {"v_ipitch_level", "0.3", 0, V_OnIdleChange}; - -cvar_t v_idlescale = {"v_idlescale", "0", 0, V_OnIdleChange}; - -cvar_t crosshair = {"crosshair","2",CVAR_ARCHIVE}; -cvar_t crosshaircolor = {"crosshaircolor","79",CVAR_ARCHIVE}; - -cvar_t cl_crossx = {"cl_crossx", "0", CVAR_ARCHIVE}; -cvar_t cl_crossy = {"cl_crossy", "0", CVAR_ARCHIVE}; - -cvar_t v_contentblend = {"v_contentblend", "1"}; - -cvar_t v_damagecshift = {"v_damagecshift", "1"}; -cvar_t v_quadcshift = {"v_quadcshift", "1"}; -cvar_t v_suitcshift = {"v_suitcshift", "1"}; -cvar_t v_ringcshift = {"v_ringcshift", "1"}; -cvar_t v_pentcshift = {"v_pentcshift", "1"}; - -cvar_t v_bonusflash = {"cl_bonusflash", "1"}; - -float v_dmg_time, v_dmg_roll, v_dmg_pitch; - -extern int in_forward, in_forward2, in_back; - -frame_t *view_frame; -player_state_t *view_message; - - -qboolean V_OnIdleChange (cvar_t *var, char *value) -{ - // Don't allow cheating in TF - if (cl.teamfortress && cls.state >= ca_connected && - cbuf_current != &cbuf_svc) - return true; - return false; -} - -/* -=============== -V_CalcRoll - -=============== -*/ -float V_CalcRoll (vec3_t angles, vec3_t velocity) -{ - vec3_t forward, right, up; - float sign; - float side; - float value; - - AngleVectors (angles, forward, right, up); - side = DotProduct (velocity, right); - sign = side < 0 ? -1 : 1; - side = fabs(side); - - value = cl_rollangle.value; - - if (side < cl_rollspeed.value) - side = side * value / cl_rollspeed.value; - else - side = value; - - return side*sign; - -} - - -/* -=============== -V_CalcBob - -=============== -*/ -float V_CalcBob (void) -{ - static double bobtime; - static float bob; - float cycle; - - if (cl.spectator) - return 0; - - if (onground == -1) - return bob; // just use old value - - bobtime += host_frametime; - cycle = bobtime - (int)(bobtime/cl_bobcycle.value)*cl_bobcycle.value; - cycle /= cl_bobcycle.value; - if (cycle < cl_bobup.value) - cycle = M_PI * cycle / cl_bobup.value; - else - cycle = M_PI + M_PI*(cycle-cl_bobup.value)/(1.0 - cl_bobup.value); - -// bob is proportional to simulated velocity in the xy plane -// (don't count Z, or jumping messes it up) - - bob = sqrt(cl.simvel[0]*cl.simvel[0] + cl.simvel[1]*cl.simvel[1]) * cl_bob.value; - bob = bob*0.3 + bob*0.7*sin(cycle); - if (bob > 4) - bob = 4; - else if (bob < -7) - bob = -7; - return bob; - -} - - -//============================================================================= - - -cvar_t v_centermove = {"v_centermove", "0.15"}; -cvar_t v_centerspeed = {"v_centerspeed","500"}; - - -void V_StartPitchDrift (void) -{ -#if 1 - if (cl.laststop == cl.time) - { - return; // something else is keeping it from drifting - } -#endif - if (cl.nodrift || !cl.pitchvel) - { - cl.pitchvel = v_centerspeed.value; - cl.nodrift = false; - cl.driftmove = 0; - } -} - -void V_StopPitchDrift (void) -{ - cl.laststop = cl.time; - cl.nodrift = true; - cl.pitchvel = 0; -} - -/* -=============== -V_DriftPitch - -Moves the client pitch angle towards cl.idealpitch sent by the server. - -If the user is adjusting pitch manually, either with lookup/lookdown, -mlook and mouse, or klook and keyboard, pitch drifting is constantly stopped. - -Drifting is enabled when the center view key is hit, mlook is released and -lookspring is non 0, or when -=============== -*/ -void V_DriftPitch (void) -{ - float delta, move; - - if (view_message->onground == -1 || cls.demoplayback) - { - cl.driftmove = 0; - cl.pitchvel = 0; - return; - } - -// don't count small mouse motion - if (cl.nodrift) - { - if ( fabs(cl.frames[(cls.netchan.outgoing_sequence-1)&UPDATE_MASK].cmd.forwardmove) < 200) - cl.driftmove = 0; - else - cl.driftmove += host_frametime; - - if ( cl.driftmove > v_centermove.value) - { - V_StartPitchDrift (); - } - return; - } - - delta = 0 - cl.viewangles[PITCH]; - - if (!delta) - { - cl.pitchvel = 0; - return; - } - - move = host_frametime * cl.pitchvel; - cl.pitchvel += host_frametime * v_centerspeed.value; - -//Con_Printf ("move: %f (%f)\n", move, host_frametime); - - if (delta > 0) - { - if (move > delta) - { - cl.pitchvel = 0; - move = delta; - } - cl.viewangles[PITCH] += move; - } - else if (delta < 0) - { - if (move > -delta) - { - cl.pitchvel = 0; - move = -delta; - } - cl.viewangles[PITCH] -= move; - } -} - - - - - -/* -============================================================================== - - PALETTE FLASHES - -============================================================================== -*/ - - -cshift_t cshift_empty = { {130,80,50}, 0 }; -cshift_t cshift_water = { {130,80,50}, 128 }; -cshift_t cshift_slime = { {0,25,5}, 150 }; -cshift_t cshift_lava = { {255,80,0}, 150 }; - -cvar_t v_gamma = {"gamma","0.7",CVAR_ARCHIVE}; -cvar_t v_contrast = {"contrast","1",CVAR_ARCHIVE}; - -byte gammatable[256]; // palette is sent through this - -byte current_pal[768]; // Tonik: used for screenshots - - -#ifdef GLQUAKE - -cvar_t gl_cshiftpercent = {"gl_cshiftpercent", "100"}; -cvar_t gl_gamma = {"gl_gamma","1",CVAR_ARCHIVE}; -cvar_t gl_contrast = {"gl_contrast","1",CVAR_ARCHIVE}; -float v_blend[4]; // rgba 0.0 - 1.0 -unsigned short ramps[3][256]; - -#endif // GLQUAKE - - -#ifndef GLQUAKE -void BuildGammaTable (float g, float c) -{ - int i, inf; - - if (g < 0.3) g = 0.3; - if (g > 3) g = 3; - if (c < 1) c = 1; - if (c > 3) c = 3; - - if (g == 1 && c == 1) - { - for (i=0 ; i<256 ; i++) - gammatable[i] = i; - return; - } - - for (i=0 ; i<256 ; i++) - { - inf = 255 * pow ((i+0.5)/255.5*c, g) + 0.5; - if (inf < 0) - inf = 0; - if (inf > 255) - inf = 255; - gammatable[i] = inf; - } -} - -/* -================= -V_CheckGamma -================= -*/ -qboolean V_CheckGamma (void) -{ - static float old_gamma; - static float old_contrast; - - if (v_gamma.value == old_gamma && v_contrast.value == old_contrast) - return false; - old_gamma = v_gamma.value; - old_contrast = v_contrast.value; - - BuildGammaTable (v_gamma.value, v_contrast.value); - vid.recalc_refdef = 1; // force a surface cache flush - - return true; -} -#endif // !GLQUAKE - - -/* -=============== -V_ParseDamage -=============== -*/ -void V_ParseDamage (void) -{ - int armor, blood; - vec3_t from; - int i; - vec3_t forward, right, up; - float side; - float count; - float fraction; - - armor = MSG_ReadByte (); - blood = MSG_ReadByte (); - for (i=0 ; i<3 ; i++) - from[i] = MSG_ReadCoord (); - - count = blood*0.5 + armor*0.5; - if (count < 10) - count = 10; - - cl.faceanimtime = cl.time + 0.2; // but sbar face into pain frame - - cl.cshifts[CSHIFT_DAMAGE].percent += 3*count; - if (cl.cshifts[CSHIFT_DAMAGE].percent < 0) - cl.cshifts[CSHIFT_DAMAGE].percent = 0; - if (cl.cshifts[CSHIFT_DAMAGE].percent > 150) - cl.cshifts[CSHIFT_DAMAGE].percent = 150; - - fraction = v_damagecshift.value; - if (fraction < 0) fraction = 0; - if (fraction > 1) fraction = 1; - cl.cshifts[CSHIFT_DAMAGE].percent *= fraction; - - if (armor > blood) - { - cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 200; - cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 100; - cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 100; - } - else if (armor) - { - cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 220; - cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 50; - cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 50; - } - else - { - cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 255; - cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 0; - cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 0; - } - -// -// calculate view angle kicks -// - VectorSubtract (from, cl.simorg, from); - VectorNormalize (from); - - AngleVectors (cl.simangles, forward, right, up); - - side = DotProduct (from, right); - v_dmg_roll = count*side*v_kickroll.value; - - side = DotProduct (from, forward); - v_dmg_pitch = count*side*v_kickpitch.value; - - v_dmg_time = v_kicktime.value; -} - - -/* -================== -V_cshift_f -================== -*/ -void V_cshift_f (void) -{ - // don't allow cheating in TF - if (cls.state >= ca_connected && cl.teamfortress - && cbuf_current != &cbuf_svc) - return; - - cshift_empty.destcolor[0] = atoi(Cmd_Argv(1)); - cshift_empty.destcolor[1] = atoi(Cmd_Argv(2)); - cshift_empty.destcolor[2] = atoi(Cmd_Argv(3)); - cshift_empty.percent = atoi(Cmd_Argv(4)); -} - - -/* -================== -V_BonusFlash_f - -When you run over an item, the server sends this command -================== -*/ -void V_BonusFlash_f (void) -{ - if (!v_bonusflash.value && cbuf_current == &cbuf_svc) - return; - - cl.cshifts[CSHIFT_BONUS].destcolor[0] = 215; - cl.cshifts[CSHIFT_BONUS].destcolor[1] = 186; - cl.cshifts[CSHIFT_BONUS].destcolor[2] = 69; - cl.cshifts[CSHIFT_BONUS].percent = 50; -} - -/* -============= -V_SetContentsColor - -Underwater, lava, etc each has a color shift -============= -*/ -void V_SetContentsColor (int contents) -{ - if (!v_contentblend.value) { - cl.cshifts[CSHIFT_CONTENTS] = cshift_empty; - return; - } - - switch (contents) - { - case CONTENTS_EMPTY: - cl.cshifts[CSHIFT_CONTENTS] = cshift_empty; - break; - case CONTENTS_LAVA: - cl.cshifts[CSHIFT_CONTENTS] = cshift_lava; - break; - case CONTENTS_SOLID: - case CONTENTS_SLIME: - cl.cshifts[CSHIFT_CONTENTS] = cshift_slime; - break; - default: - cl.cshifts[CSHIFT_CONTENTS] = cshift_water; - } - - if (v_contentblend.value > 0 && v_contentblend.value < 1 - && contents != CONTENTS_EMPTY) - cl.cshifts[CSHIFT_CONTENTS].percent *= v_contentblend.value; - -#ifdef GLQUAKE - if (!gl_polyblend.value && !cl.teamfortress) - cl.cshifts[CSHIFT_CONTENTS].percent = 0; - else { - // ignore gl_cshiftpercent on custom cshifts (set with v_cshift - // command) to avoid cheating in TF - if (contents != CONTENTS_EMPTY) { - if (!gl_polyblend.value) - cl.cshifts[CSHIFT_CONTENTS].percent = 0; - else - cl.cshifts[CSHIFT_CONTENTS].percent *= gl_cshiftpercent.value; - } - else - cl.cshifts[CSHIFT_CONTENTS].percent *= 100; - } - -#endif -} - -/* -============= -V_CalcPowerupCshift -============= -*/ -void V_CalcPowerupCshift (void) -{ - float fraction; - - if (cl.stats[STAT_ITEMS] & IT_QUAD) - { - cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0; - cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 0; - cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 255; - fraction = v_quadcshift.value; - if (fraction < 0) fraction = 0; - if (fraction > 1) fraction = 1; - cl.cshifts[CSHIFT_POWERUP].percent = 30 * fraction; - } - else if (cl.stats[STAT_ITEMS] & IT_SUIT) - { - cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0; - cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255; - cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0; - fraction = v_suitcshift.value; - if (fraction < 0) fraction = 0; - if (fraction > 1) fraction = 1; - cl.cshifts[CSHIFT_POWERUP].percent = 20 * fraction; - } - else if (cl.stats[STAT_ITEMS] & IT_INVISIBILITY) - { - cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 100; - cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 100; - cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 100; - fraction = v_ringcshift.value; - if (fraction < 0) fraction = 0; - if (fraction > 1) fraction = 1; - cl.cshifts[CSHIFT_POWERUP].percent = 100 * fraction; - } - else if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY) - { - cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 255; - cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255; - cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0; - fraction = v_pentcshift.value; - if (fraction < 0) fraction = 0; - if (fraction > 1) fraction = 1; - cl.cshifts[CSHIFT_POWERUP].percent = 30 * fraction; - } - else - cl.cshifts[CSHIFT_POWERUP].percent = 0; -} - - -/* -============= -V_CalcBlend -============= -*/ -#ifdef GLQUAKE -void V_CalcBlend (void) -{ - float r, g, b, a, a2; - int j; - - r = 0; - g = 0; - b = 0; - a = 0; - - V_CalcPowerupCshift (); - -// drop the damage value - cl.cshifts[CSHIFT_DAMAGE].percent -= host_frametime*150; - if (cl.cshifts[CSHIFT_DAMAGE].percent <= 0) - cl.cshifts[CSHIFT_DAMAGE].percent = 0; - -// drop the bonus value - cl.cshifts[CSHIFT_BONUS].percent -= host_frametime*100; - if (cl.cshifts[CSHIFT_BONUS].percent <= 0) - cl.cshifts[CSHIFT_BONUS].percent = 0; - - for (j=0 ; j 1) - v_blend[3] = 1; - if (v_blend[3] < 0) - v_blend[3] = 0; -} - -void V_AddLightBlend (float r, float g, float b, float a2) -{ - float a; - - if (!gl_polyblend.value || !gl_cshiftpercent.value) - return; - - a2 = a2 * gl_cshiftpercent.value / 100.0; - - v_blend[3] = a = v_blend[3] + a2*(1-v_blend[3]); - - a2 = a2/a; - - v_blend[0] = v_blend[1]*(1-a2) + r*a2; - v_blend[1] = v_blend[1]*(1-a2) + g*a2; - v_blend[2] = v_blend[2]*(1-a2) + b*a2; -} -#endif - - -/* -============= -V_UpdatePalette -============= -*/ -#ifdef GLQUAKE -void V_UpdatePalette (void) -{ - int i, j; - qboolean new; - static float prev_blend[4]; - float a, rgb[3]; - int c; - float gamma, contrast; - static float old_gamma, old_contrast; - extern float vid_gamma; - - if (cls.state != ca_active) { - cl.cshifts[CSHIFT_CONTENTS] = cshift_empty; - cl.cshifts[CSHIFT_POWERUP].percent = 0; - } - else - V_CalcPowerupCshift (); - - new = false; - - for (i=0 ; i<4 ; i++) { - if (v_blend[i] != prev_blend[i]) { - new = true; - prev_blend[i] = v_blend[i]; - } - } - - gamma = gl_gamma.value; - if (gamma < 0.3) - gamma = 0.3; - if (gamma > 3) - gamma = 3; - if (gamma != old_gamma) { - old_gamma = gamma; - new = true; - } - - contrast = gl_contrast.value; - if (contrast > 3) - contrast = 3; - if (contrast < 1) - contrast = 1; - if (contrast != old_contrast) { - old_contrast = contrast; - new = true; - } - - if (!new) - return; - - a = v_blend[3]; - - if (!vid_hwgamma_enabled) - a = 0; - - rgb[0] = 255*v_blend[0]*a; - rgb[1] = 255*v_blend[1]*a; - rgb[2] = 255*v_blend[2]*a; - - a = 1-a; - - if (vid_gamma != 1.0) { - contrast = pow (contrast, vid_gamma); - gamma = gamma/vid_gamma; - } - - for (i=0 ; i<256 ; i++) - { - for (j=0 ; j<3 ; j++) { - // apply blend and contrast - c = (i*a + rgb[j]) * contrast; - if (c > 255) - c = 255; - // apply gamma - c = 255 * pow((c + 0.5)/255.5, gamma) + 0.5; - if (c < 0) - c = 0; - if (c > 255) - c = 255; - ramps[j][i] = c << 8; - } - } - - VID_SetDeviceGammaRamp ((unsigned short *) ramps); -} -#else // !GLQUAKE -/* -============= -V_UpdatePalette -============= -*/ -void V_UpdatePalette (void) -{ - int i, j; - qboolean new; - byte *basepal, *newpal; -// byte pal[768]; - int r,g,b; - qboolean force; - static cshift_t prev_cshifts[NUM_CSHIFTS]; - - if (cls.state != ca_active) { - cl.cshifts[CSHIFT_CONTENTS] = cshift_empty; - cl.cshifts[CSHIFT_POWERUP].percent = 0; - } - else - V_CalcPowerupCshift (); - - new = false; - - for (i=0 ; i>8; - g += (cl.cshifts[j].percent*(cl.cshifts[j].destcolor[1]-g))>>8; - b += (cl.cshifts[j].percent*(cl.cshifts[j].destcolor[2]-b))>>8; - } - - newpal[0] = gammatable[r]; - newpal[1] = gammatable[g]; - newpal[2] = gammatable[b]; - newpal += 3; - } - - VID_ShiftPalette (current_pal); -} - -#endif // !GLQUAKE - -/* -============================================================================== - - VIEW RENDERING - -============================================================================== -*/ - -float angledelta (float a) -{ - a = anglemod(a); - if (a > 180) - a -= 360; - return a; -} - -/* -================== -CalcGunAngle -================== -*/ -void CalcGunAngle (void) -{ - float yaw, pitch, move; - static float oldyaw = 0; - static float oldpitch = 0; - - yaw = r_refdef.viewangles[YAW]; - pitch = -r_refdef.viewangles[PITCH]; - - yaw = angledelta(yaw - r_refdef.viewangles[YAW]) * 0.4; - if (yaw > 10) - yaw = 10; - if (yaw < -10) - yaw = -10; - pitch = angledelta(-pitch - r_refdef.viewangles[PITCH]) * 0.4; - if (pitch > 10) - pitch = 10; - if (pitch < -10) - pitch = -10; - move = host_frametime*20; - if (yaw > oldyaw) - { - if (oldyaw + move < yaw) - yaw = oldyaw + move; - } - else - { - if (oldyaw - move > yaw) - yaw = oldyaw - move; - } - - if (pitch > oldpitch) - { - if (oldpitch + move < pitch) - pitch = oldpitch + move; - } - else - { - if (oldpitch - move > pitch) - pitch = oldpitch - move; - } - - oldyaw = yaw; - oldpitch = pitch; - - cl.viewent.angles[YAW] = r_refdef.viewangles[YAW] + yaw; - cl.viewent.angles[PITCH] = - (r_refdef.viewangles[PITCH] + pitch); -} - -/* -============== -V_BoundOffsets -============== -*/ -void V_BoundOffsets (void) -{ -// absolutely bound refresh relative to entity clipping hull -// so the view can never be inside a solid wall - - if (r_refdef.vieworg[0] < cl.simorg[0] - 14) - r_refdef.vieworg[0] = cl.simorg[0] - 14; - else if (r_refdef.vieworg[0] > cl.simorg[0] + 14) - r_refdef.vieworg[0] = cl.simorg[0] + 14; - if (r_refdef.vieworg[1] < cl.simorg[1] - 14) - r_refdef.vieworg[1] = cl.simorg[1] - 14; - else if (r_refdef.vieworg[1] > cl.simorg[1] + 14) - r_refdef.vieworg[1] = cl.simorg[1] + 14; - if (r_refdef.vieworg[2] < cl.simorg[2] - 22) - r_refdef.vieworg[2] = cl.simorg[2] - 22; - else if (r_refdef.vieworg[2] > cl.simorg[2] + 30) - r_refdef.vieworg[2] = cl.simorg[2] + 30; -} - -/* -============== -V_AddIdle - -Idle swaying -============== -*/ -void V_AddIdle (void) -{ - r_refdef.viewangles[ROLL] += v_idlescale.value * sin(cl.time*v_iroll_cycle.value) * v_iroll_level.value; - r_refdef.viewangles[PITCH] += v_idlescale.value * sin(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value; - r_refdef.viewangles[YAW] += v_idlescale.value * sin(cl.time*v_iyaw_cycle.value) * v_iyaw_level.value; - - cl.viewent.angles[ROLL] -= v_idlescale.value * sin(cl.time*v_iroll_cycle.value) * v_iroll_level.value; - cl.viewent.angles[PITCH] -= v_idlescale.value * sin(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value; - cl.viewent.angles[YAW] -= v_idlescale.value * sin(cl.time*v_iyaw_cycle.value) * v_iyaw_level.value; -} - - -/* -============== -V_CalcViewRoll - -Roll is induced by movement and damage -============== -*/ -void V_CalcViewRoll (void) -{ - float side; - - side = V_CalcRoll (cl.simangles, cl.simvel); - r_refdef.viewangles[ROLL] += side; - - if (v_dmg_time > 0) - { - r_refdef.viewangles[ROLL] += v_dmg_time/v_kicktime.value*v_dmg_roll; - r_refdef.viewangles[PITCH] += v_dmg_time/v_kicktime.value*v_dmg_pitch; - v_dmg_time -= host_frametime; - } - -} - - -/* -================== -V_CalcIntermissionRefdef - -================== -*/ -void V_CalcIntermissionRefdef (void) -{ - entity_t *view; - float old; - -// view is the weapon model - view = &cl.viewent; - - VectorCopy (cl.simorg, r_refdef.vieworg); - VectorCopy (cl.simangles, r_refdef.viewangles); - view->model = NULL; - -// always idle in intermission - old = v_idlescale.value; - v_idlescale.value = 1; - V_AddIdle (); - v_idlescale.value = old; -} - -/* -================== -V_CalcRefdef - -================== -*/ -void V_CalcRefdef (void) -{ - entity_t *view; - int i; - vec3_t forward, right, up; - float bob; - - V_DriftPitch (); - -// view is the weapon model (only visible from inside body) - view = &cl.viewent; - - bob = V_CalcBob (); - -// refresh position from simulated origin - VectorCopy (cl.simorg, r_refdef.vieworg); - - r_refdef.vieworg[2] += bob; - -// never let it sit exactly on a node line, because a water plane can -// dissapear when viewed with the eye exactly on it. -// the server protocol only specifies to 1/8 pixel, so add 1/16 in each axis - r_refdef.vieworg[0] += 1.0/16; - r_refdef.vieworg[1] += 1.0/16; - r_refdef.vieworg[2] += 1.0/16; - - VectorCopy (cl.simangles, r_refdef.viewangles); - V_CalcViewRoll (); - V_AddIdle (); - - if (view_message->flags & PF_GIB) - r_refdef.vieworg[2] += 8; // gib view height - else if (view_message->flags & PF_DEAD) - r_refdef.vieworg[2] -= 16; // corpse view height - else - r_refdef.vieworg[2] += 22; // view height - - if (view_message->flags & PF_DEAD) // PF_GIB will also set PF_DEAD - r_refdef.viewangles[ROLL] = 80; // dead view angle - - -// offsets - AngleVectors (cl.simangles, forward, right, up); - -// set up gun position - VectorCopy (cl.simangles, view->angles); - - CalcGunAngle (); - - VectorCopy (cl.simorg, view->origin); - view->origin[2] += 22; - - for (i=0 ; i<3 ; i++) - { - view->origin[i] += forward[i]*bob*0.4; -// view->origin[i] += right[i]*bob*0.4; -// view->origin[i] += up[i]*bob*0.8; - } - view->origin[2] += bob; - -// fudge position around to keep amount of weapon visible -// roughly equal with different FOV - if (scr_viewsize.value == 110) - view->origin[2] += 1; - else if (scr_viewsize.value == 100) - view->origin[2] += 2; - else if (scr_viewsize.value == 90) - view->origin[2] += 1; - else if (scr_viewsize.value == 80) - view->origin[2] += 0.5; - - if (view_message->flags & (PF_GIB|PF_DEAD) ) - view->model = NULL; - else - view->model = cl.model_precache[cl.stats[STAT_WEAPON]]; - view->frame = view_message->weaponframe; - view->colormap = vid.colormap; - -// set up the refresh position - r_refdef.viewangles[PITCH] += cl.punchangle; - -// smooth out stair step ups - r_refdef.vieworg[2] += cl.crouch; - view->origin[2] += cl.crouch; -} - -/* -============= -DropPunchAngle -============= -*/ -void DropPunchAngle (void) -{ - cl.punchangle -= 10*host_frametime; - if (cl.punchangle < 0) - cl.punchangle = 0; -} - - -/* -================== -V_RenderView - -The player's clipping box goes from (-16 -16 -24) to (16 16 32) from -the entity origin, so any view position inside that will be valid -================== -*/ -extern vrect_t scr_vrect; - -void V_RenderView (void) -{ -// if (cl.simangles[ROLL]) -// Sys_Error ("cl.simangles[ROLL]"); // DEBUG -cl.simangles[ROLL] = 0; // FIXME @@@ - - if (cls.state != ca_active) { -#ifdef GLQUAKE - V_CalcBlend (); -#endif - return; - } - - view_frame = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK]; - view_message = &view_frame->playerstate[cl.playernum]; - - DropPunchAngle (); - if (cl.intermission) - { // intermission / finale rendering - V_CalcIntermissionRefdef (); - } - else - { - V_CalcRefdef (); - } - - R_PushDlights (); - R_RenderView (); - -#ifndef GLQUAKE - if (crosshair.value) - Draw_Crosshair(); -#endif - -} - -//============================================================================ - -/* -============= -V_Init -============= -*/ -void V_Init (void) -{ - Cmd_AddCommand ("v_cshift", V_cshift_f); - Cmd_AddCommand ("bf", V_BonusFlash_f); - Cmd_AddCommand ("centerview", V_StartPitchDrift); - - Cvar_RegisterVariable (&v_centermove); - Cvar_RegisterVariable (&v_centerspeed); - - Cvar_RegisterVariable (&v_iyaw_cycle); - Cvar_RegisterVariable (&v_iroll_cycle); - Cvar_RegisterVariable (&v_ipitch_cycle); - Cvar_RegisterVariable (&v_iyaw_level); - Cvar_RegisterVariable (&v_iroll_level); - Cvar_RegisterVariable (&v_ipitch_level); - - Cvar_RegisterVariable (&v_contentblend); - Cvar_RegisterVariable (&v_damagecshift); - Cvar_RegisterVariable (&v_quadcshift); - Cvar_RegisterVariable (&v_suitcshift); - Cvar_RegisterVariable (&v_ringcshift); - Cvar_RegisterVariable (&v_pentcshift); - - Cvar_RegisterVariable (&v_bonusflash); - - Cvar_RegisterVariable (&v_idlescale); - Cvar_RegisterVariable (&crosshaircolor); - Cvar_RegisterVariable (&crosshair); - Cvar_RegisterVariable (&cl_crossx); - Cvar_RegisterVariable (&cl_crossy); -#ifdef GLQUAKE - Cvar_RegisterVariable (&gl_cshiftpercent); -#endif - - Cvar_RegisterVariable (&cl_rollspeed); - Cvar_RegisterVariable (&cl_rollangle); - Cvar_RegisterVariable (&cl_bob); - Cvar_RegisterVariable (&cl_bobcycle); - Cvar_RegisterVariable (&cl_bobup); - - Cvar_RegisterVariable (&v_kicktime); - Cvar_RegisterVariable (&v_kickroll); - Cvar_RegisterVariable (&v_kickpitch); - - Cvar_RegisterVariable (&v_gamma); - Cvar_RegisterVariable (&v_contrast); -#ifdef GLQUAKE - Cvar_RegisterVariable (&gl_gamma); - Cvar_RegisterVariable (&gl_contrast); -#endif - -#ifndef GLQUAKE - BuildGammaTable (v_gamma.value, v_contrast.value); -#endif - -} - - +/* +Copyright (C) 1996-1997 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. + +*/ +// view.c -- player eye positioning + +#include "quakedef.h" +#include "r_local.h" +#include "pmove.h" + +/* + +The view is allowed to move slightly from it's true position for bobbing, +but if it exceeds 8 pixels linear distance (spherical, not box), the list of +entities sent from the server may not include everything in the pvs, especially +when crossing a water boudnary. + +*/ + +cvar_t cl_rollspeed = {"cl_rollspeed", "200"}; +cvar_t cl_rollangle = {"cl_rollangle", "2.0"}; + +cvar_t cl_bob = {"cl_bob","0.02"}; +cvar_t cl_bobcycle = {"cl_bobcycle","0.6"}; +cvar_t cl_bobup = {"cl_bobup","0.5"}; + +cvar_t v_kicktime = {"v_kicktime", "0.5"}; +cvar_t v_kickroll = {"v_kickroll", "0.6"}; +cvar_t v_kickpitch = {"v_kickpitch", "0.6"}; + +qboolean V_OnIdleChange (cvar_t *var, char *value); +cvar_t v_iyaw_cycle = {"v_iyaw_cycle", "2", 0, V_OnIdleChange}; +cvar_t v_iroll_cycle = {"v_iroll_cycle", "0.5", 0, V_OnIdleChange}; +cvar_t v_ipitch_cycle = {"v_ipitch_cycle", "1", 0, V_OnIdleChange}; +cvar_t v_iyaw_level = {"v_iyaw_level", "0.3", 0, V_OnIdleChange}; +cvar_t v_iroll_level = {"v_iroll_level", "0.1", 0, V_OnIdleChange}; +cvar_t v_ipitch_level = {"v_ipitch_level", "0.3", 0, V_OnIdleChange}; + +cvar_t v_idlescale = {"v_idlescale", "0", 0, V_OnIdleChange}; + +cvar_t crosshair = {"crosshair","2",CVAR_ARCHIVE}; +cvar_t crosshaircolor = {"crosshaircolor","79",CVAR_ARCHIVE}; + +cvar_t cl_crossx = {"cl_crossx", "0", CVAR_ARCHIVE}; +cvar_t cl_crossy = {"cl_crossy", "0", CVAR_ARCHIVE}; + +cvar_t v_contentblend = {"v_contentblend", "1"}; + +cvar_t v_damagecshift = {"v_damagecshift", "1"}; +cvar_t v_quadcshift = {"v_quadcshift", "1"}; +cvar_t v_suitcshift = {"v_suitcshift", "1"}; +cvar_t v_ringcshift = {"v_ringcshift", "1"}; +cvar_t v_pentcshift = {"v_pentcshift", "1"}; + +cvar_t v_bonusflash = {"cl_bonusflash", "1"}; + +float v_dmg_time, v_dmg_roll, v_dmg_pitch; + +extern int in_forward, in_forward2, in_back; + +frame_t *view_frame; +player_state_t *view_message; + + +qboolean V_OnIdleChange (cvar_t *var, char *value) +{ + // Don't allow cheating in TF + if (cl.teamfortress && cls.state >= ca_connected && + cbuf_current != &cbuf_svc) + return true; + return false; +} + +/* +=============== +V_CalcRoll + +=============== +*/ +float V_CalcRoll (vec3_t angles, vec3_t velocity) +{ + vec3_t forward, right, up; + float sign; + float side; + float value; + + AngleVectors (angles, forward, right, up); + side = DotProduct (velocity, right); + sign = side < 0 ? -1 : 1; + side = fabs(side); + + value = cl_rollangle.value; + + if (side < cl_rollspeed.value) + side = side * value / cl_rollspeed.value; + else + side = value; + + return side*sign; + +} + + +/* +=============== +V_CalcBob + +=============== +*/ +float V_CalcBob (void) +{ + static double bobtime; + static float bob; + float cycle; + + if (cl.spectator) + return 0; + + if (onground == -1) + return bob; // just use old value + + bobtime += host_frametime; + cycle = bobtime - (int)(bobtime/cl_bobcycle.value)*cl_bobcycle.value; + cycle /= cl_bobcycle.value; + if (cycle < cl_bobup.value) + cycle = M_PI * cycle / cl_bobup.value; + else + cycle = M_PI + M_PI*(cycle-cl_bobup.value)/(1.0 - cl_bobup.value); + +// bob is proportional to simulated velocity in the xy plane +// (don't count Z, or jumping messes it up) + + bob = sqrt(cl.simvel[0]*cl.simvel[0] + cl.simvel[1]*cl.simvel[1]) * cl_bob.value; + bob = bob*0.3 + bob*0.7*sin(cycle); + if (bob > 4) + bob = 4; + else if (bob < -7) + bob = -7; + return bob; + +} + + +//============================================================================= + + +cvar_t v_centermove = {"v_centermove", "0.15"}; +cvar_t v_centerspeed = {"v_centerspeed","500"}; + + +void V_StartPitchDrift (void) +{ +#if 1 + if (cl.laststop == cl.time) + { + return; // something else is keeping it from drifting + } +#endif + if (cl.nodrift || !cl.pitchvel) + { + cl.pitchvel = v_centerspeed.value; + cl.nodrift = false; + cl.driftmove = 0; + } +} + +void V_StopPitchDrift (void) +{ + cl.laststop = cl.time; + cl.nodrift = true; + cl.pitchvel = 0; +} + +/* +=============== +V_DriftPitch + +Moves the client pitch angle towards cl.idealpitch sent by the server. + +If the user is adjusting pitch manually, either with lookup/lookdown, +mlook and mouse, or klook and keyboard, pitch drifting is constantly stopped. + +Drifting is enabled when the center view key is hit, mlook is released and +lookspring is non 0, or when +=============== +*/ +void V_DriftPitch (void) +{ + float delta, move; + + if (view_message->onground == -1 || cls.demoplayback) + { + cl.driftmove = 0; + cl.pitchvel = 0; + return; + } + +// don't count small mouse motion + if (cl.nodrift) + { + if ( fabs(cl.frames[(cls.netchan.outgoing_sequence-1)&UPDATE_MASK].cmd.forwardmove) < 200) + cl.driftmove = 0; + else + cl.driftmove += host_frametime; + + if ( cl.driftmove > v_centermove.value) + { + V_StartPitchDrift (); + } + return; + } + + delta = 0 - cl.viewangles[PITCH]; + + if (!delta) + { + cl.pitchvel = 0; + return; + } + + move = host_frametime * cl.pitchvel; + cl.pitchvel += host_frametime * v_centerspeed.value; + +//Con_Printf ("move: %f (%f)\n", move, host_frametime); + + if (delta > 0) + { + if (move > delta) + { + cl.pitchvel = 0; + move = delta; + } + cl.viewangles[PITCH] += move; + } + else if (delta < 0) + { + if (move > -delta) + { + cl.pitchvel = 0; + move = -delta; + } + cl.viewangles[PITCH] -= move; + } +} + + + + + +/* +============================================================================== + + PALETTE FLASHES + +============================================================================== +*/ + + +cshift_t cshift_empty = { {130,80,50}, 0 }; +cshift_t cshift_water = { {130,80,50}, 128 }; +cshift_t cshift_slime = { {0,25,5}, 150 }; +cshift_t cshift_lava = { {255,80,0}, 150 }; + +cvar_t v_gamma = {"gamma","0.7",CVAR_ARCHIVE}; +cvar_t v_contrast = {"contrast","1",CVAR_ARCHIVE}; + +byte gammatable[256]; // palette is sent through this + +byte current_pal[768]; // Tonik: used for screenshots + + +#ifdef GLQUAKE + +cvar_t gl_cshiftpercent = {"gl_cshiftpercent", "100"}; +cvar_t gl_gamma = {"gl_gamma","1",CVAR_ARCHIVE}; +cvar_t gl_contrast = {"gl_contrast","1",CVAR_ARCHIVE}; +float v_blend[4]; // rgba 0.0 - 1.0 +unsigned short ramps[3][256]; + +#endif // GLQUAKE + + +#ifndef GLQUAKE +void BuildGammaTable (float g, float c) +{ + int i, inf; + + if (g < 0.3) g = 0.3; + if (g > 3) g = 3; + if (c < 1) c = 1; + if (c > 3) c = 3; + + if (g == 1 && c == 1) + { + for (i=0 ; i<256 ; i++) + gammatable[i] = i; + return; + } + + for (i=0 ; i<256 ; i++) + { + inf = 255 * pow ((i+0.5)/255.5*c, g) + 0.5; + if (inf < 0) + inf = 0; + if (inf > 255) + inf = 255; + gammatable[i] = inf; + } +} + +/* +================= +V_CheckGamma +================= +*/ +qboolean V_CheckGamma (void) +{ + static float old_gamma; + static float old_contrast; + + if (v_gamma.value == old_gamma && v_contrast.value == old_contrast) + return false; + old_gamma = v_gamma.value; + old_contrast = v_contrast.value; + + BuildGammaTable (v_gamma.value, v_contrast.value); + vid.recalc_refdef = 1; // force a surface cache flush + + return true; +} +#endif // !GLQUAKE + + +/* +=============== +V_ParseDamage +=============== +*/ +void V_ParseDamage (void) +{ + int armor, blood; + vec3_t from; + int i; + vec3_t forward, right, up; + float side; + float count; + float fraction; + + armor = MSG_ReadByte (); + blood = MSG_ReadByte (); + for (i=0 ; i<3 ; i++) + from[i] = MSG_ReadCoord (); + + count = blood*0.5 + armor*0.5; + if (count < 10) + count = 10; + + cl.faceanimtime = cl.time + 0.2; // but sbar face into pain frame + + cl.cshifts[CSHIFT_DAMAGE].percent += 3*count; + if (cl.cshifts[CSHIFT_DAMAGE].percent < 0) + cl.cshifts[CSHIFT_DAMAGE].percent = 0; + if (cl.cshifts[CSHIFT_DAMAGE].percent > 150) + cl.cshifts[CSHIFT_DAMAGE].percent = 150; + + fraction = v_damagecshift.value; + if (fraction < 0) fraction = 0; + if (fraction > 1) fraction = 1; + cl.cshifts[CSHIFT_DAMAGE].percent *= fraction; + + if (armor > blood) + { + cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 200; + cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 100; + cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 100; + } + else if (armor) + { + cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 220; + cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 50; + cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 50; + } + else + { + cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 255; + cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 0; + cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 0; + } + +// +// calculate view angle kicks +// + VectorSubtract (from, cl.simorg, from); + VectorNormalize (from); + + AngleVectors (cl.simangles, forward, right, up); + + side = DotProduct (from, right); + v_dmg_roll = count*side*v_kickroll.value; + + side = DotProduct (from, forward); + v_dmg_pitch = count*side*v_kickpitch.value; + + v_dmg_time = v_kicktime.value; +} + + +/* +================== +V_cshift_f +================== +*/ +void V_cshift_f (void) +{ + // don't allow cheating in TF + if (cls.state >= ca_connected && cl.teamfortress + && cbuf_current != &cbuf_svc) + return; + + cshift_empty.destcolor[0] = atoi(Cmd_Argv(1)); + cshift_empty.destcolor[1] = atoi(Cmd_Argv(2)); + cshift_empty.destcolor[2] = atoi(Cmd_Argv(3)); + cshift_empty.percent = atoi(Cmd_Argv(4)); +} + + +/* +================== +V_BonusFlash_f + +When you run over an item, the server sends this command +================== +*/ +void V_BonusFlash_f (void) +{ + if (!v_bonusflash.value && cbuf_current == &cbuf_svc) + return; + + cl.cshifts[CSHIFT_BONUS].destcolor[0] = 215; + cl.cshifts[CSHIFT_BONUS].destcolor[1] = 186; + cl.cshifts[CSHIFT_BONUS].destcolor[2] = 69; + cl.cshifts[CSHIFT_BONUS].percent = 50; +} + +/* +============= +V_SetContentsColor + +Underwater, lava, etc each has a color shift +============= +*/ +void V_SetContentsColor (int contents) +{ + if (!v_contentblend.value) { + cl.cshifts[CSHIFT_CONTENTS] = cshift_empty; + return; + } + + switch (contents) + { + case CONTENTS_EMPTY: + cl.cshifts[CSHIFT_CONTENTS] = cshift_empty; + break; + case CONTENTS_LAVA: + cl.cshifts[CSHIFT_CONTENTS] = cshift_lava; + break; + case CONTENTS_SOLID: + case CONTENTS_SLIME: + cl.cshifts[CSHIFT_CONTENTS] = cshift_slime; + break; + default: + cl.cshifts[CSHIFT_CONTENTS] = cshift_water; + } + + if (v_contentblend.value > 0 && v_contentblend.value < 1 + && contents != CONTENTS_EMPTY) + cl.cshifts[CSHIFT_CONTENTS].percent *= v_contentblend.value; + +#ifdef GLQUAKE + if (!gl_polyblend.value && !cl.teamfortress) + cl.cshifts[CSHIFT_CONTENTS].percent = 0; + else { + // ignore gl_cshiftpercent on custom cshifts (set with v_cshift + // command) to avoid cheating in TF + if (contents != CONTENTS_EMPTY) { + if (!gl_polyblend.value) + cl.cshifts[CSHIFT_CONTENTS].percent = 0; + else + cl.cshifts[CSHIFT_CONTENTS].percent *= gl_cshiftpercent.value; + } + else + cl.cshifts[CSHIFT_CONTENTS].percent *= 100; + } + +#endif +} + +/* +============= +V_CalcPowerupCshift +============= +*/ +void V_CalcPowerupCshift (void) +{ + float fraction; + + if (cl.stats[STAT_ITEMS] & IT_QUAD) + { + cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0; + cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 0; + cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 255; + fraction = v_quadcshift.value; + if (fraction < 0) fraction = 0; + if (fraction > 1) fraction = 1; + cl.cshifts[CSHIFT_POWERUP].percent = 30 * fraction; + } + else if (cl.stats[STAT_ITEMS] & IT_SUIT) + { + cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0; + cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255; + cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0; + fraction = v_suitcshift.value; + if (fraction < 0) fraction = 0; + if (fraction > 1) fraction = 1; + cl.cshifts[CSHIFT_POWERUP].percent = 20 * fraction; + } + else if (cl.stats[STAT_ITEMS] & IT_INVISIBILITY) + { + cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 100; + cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 100; + cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 100; + fraction = v_ringcshift.value; + if (fraction < 0) fraction = 0; + if (fraction > 1) fraction = 1; + cl.cshifts[CSHIFT_POWERUP].percent = 100 * fraction; + } + else if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY) + { + cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 255; + cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255; + cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0; + fraction = v_pentcshift.value; + if (fraction < 0) fraction = 0; + if (fraction > 1) fraction = 1; + cl.cshifts[CSHIFT_POWERUP].percent = 30 * fraction; + } + else + cl.cshifts[CSHIFT_POWERUP].percent = 0; +} + + +/* +============= +V_CalcBlend +============= +*/ +#ifdef GLQUAKE +void V_CalcBlend (void) +{ + float r, g, b, a, a2; + int j; + + r = 0; + g = 0; + b = 0; + a = 0; + + V_CalcPowerupCshift (); + +// drop the damage value + cl.cshifts[CSHIFT_DAMAGE].percent -= host_frametime*150; + if (cl.cshifts[CSHIFT_DAMAGE].percent <= 0) + cl.cshifts[CSHIFT_DAMAGE].percent = 0; + +// drop the bonus value + cl.cshifts[CSHIFT_BONUS].percent -= host_frametime*100; + if (cl.cshifts[CSHIFT_BONUS].percent <= 0) + cl.cshifts[CSHIFT_BONUS].percent = 0; + + for (j=0 ; j 1) + v_blend[3] = 1; + if (v_blend[3] < 0) + v_blend[3] = 0; +} + +void V_AddLightBlend (float r, float g, float b, float a2) +{ + float a; + + if (!gl_polyblend.value || !gl_cshiftpercent.value) + return; + + a2 = a2 * gl_cshiftpercent.value / 100.0; + + v_blend[3] = a = v_blend[3] + a2*(1-v_blend[3]); + + a2 = a2/a; + + v_blend[0] = v_blend[1]*(1-a2) + r*a2; + v_blend[1] = v_blend[1]*(1-a2) + g*a2; + v_blend[2] = v_blend[2]*(1-a2) + b*a2; +} +#endif + + +/* +============= +V_UpdatePalette +============= +*/ +#ifdef GLQUAKE +void V_UpdatePalette (void) +{ + int i, j; + qboolean new; + static float prev_blend[4]; + float a, rgb[3]; + int c; + float gamma, contrast; + static float old_gamma, old_contrast; +#ifdef WIN32 + extern float vid_gamma; +#endif + + if (cls.state != ca_active) { + cl.cshifts[CSHIFT_CONTENTS] = cshift_empty; + cl.cshifts[CSHIFT_POWERUP].percent = 0; + } + else + V_CalcPowerupCshift (); + + new = false; + + for (i=0 ; i<4 ; i++) { + if (v_blend[i] != prev_blend[i]) { + new = true; + prev_blend[i] = v_blend[i]; + } + } + + gamma = gl_gamma.value; + if (gamma < 0.3) + gamma = 0.3; + if (gamma > 3) + gamma = 3; + if (gamma != old_gamma) { + old_gamma = gamma; + new = true; + } + + contrast = gl_contrast.value; + if (contrast > 3) + contrast = 3; + if (contrast < 1) + contrast = 1; + if (contrast != old_contrast) { + old_contrast = contrast; + new = true; + } + + if (!new) + return; + + a = v_blend[3]; + +#ifdef WIN32 + if (!vid_hwgamma_enabled) + a = 0; +#endif + + rgb[0] = 255*v_blend[0]*a; + rgb[1] = 255*v_blend[1]*a; + rgb[2] = 255*v_blend[2]*a; + + a = 1-a; +#ifdef WIN32 + if (vid_gamma != 1.0) { + contrast = pow (contrast, vid_gamma); + gamma = gamma/vid_gamma; + } +#endif + + for (i=0 ; i<256 ; i++) + { + for (j=0 ; j<3 ; j++) { + // apply blend and contrast + c = (i*a + rgb[j]) * contrast; + if (c > 255) + c = 255; + // apply gamma + c = 255 * pow((c + 0.5)/255.5, gamma) + 0.5; + if (c < 0) + c = 0; + if (c > 255) + c = 255; + ramps[j][i] = c << 8; + } + } + +#ifdef WIN32 + VID_SetDeviceGammaRamp ((unsigned short *) ramps); +#endif +} +#else // !GLQUAKE +/* +============= +V_UpdatePalette +============= +*/ +void V_UpdatePalette (void) +{ + int i, j; + qboolean new; + byte *basepal, *newpal; +// byte pal[768]; + int r,g,b; + qboolean force; + static cshift_t prev_cshifts[NUM_CSHIFTS]; + + if (cls.state != ca_active) { + cl.cshifts[CSHIFT_CONTENTS] = cshift_empty; + cl.cshifts[CSHIFT_POWERUP].percent = 0; + } + else + V_CalcPowerupCshift (); + + new = false; + + for (i=0 ; i>8; + g += (cl.cshifts[j].percent*(cl.cshifts[j].destcolor[1]-g))>>8; + b += (cl.cshifts[j].percent*(cl.cshifts[j].destcolor[2]-b))>>8; + } + + newpal[0] = gammatable[r]; + newpal[1] = gammatable[g]; + newpal[2] = gammatable[b]; + newpal += 3; + } + + VID_ShiftPalette (current_pal); +} + +#endif // !GLQUAKE + +/* +============================================================================== + + VIEW RENDERING + +============================================================================== +*/ + +float angledelta (float a) +{ + a = anglemod(a); + if (a > 180) + a -= 360; + return a; +} + +/* +================== +CalcGunAngle +================== +*/ +void CalcGunAngle (void) +{ + float yaw, pitch, move; + static float oldyaw = 0; + static float oldpitch = 0; + + yaw = r_refdef.viewangles[YAW]; + pitch = -r_refdef.viewangles[PITCH]; + + yaw = angledelta(yaw - r_refdef.viewangles[YAW]) * 0.4; + if (yaw > 10) + yaw = 10; + if (yaw < -10) + yaw = -10; + pitch = angledelta(-pitch - r_refdef.viewangles[PITCH]) * 0.4; + if (pitch > 10) + pitch = 10; + if (pitch < -10) + pitch = -10; + move = host_frametime*20; + if (yaw > oldyaw) + { + if (oldyaw + move < yaw) + yaw = oldyaw + move; + } + else + { + if (oldyaw - move > yaw) + yaw = oldyaw - move; + } + + if (pitch > oldpitch) + { + if (oldpitch + move < pitch) + pitch = oldpitch + move; + } + else + { + if (oldpitch - move > pitch) + pitch = oldpitch - move; + } + + oldyaw = yaw; + oldpitch = pitch; + + cl.viewent.angles[YAW] = r_refdef.viewangles[YAW] + yaw; + cl.viewent.angles[PITCH] = - (r_refdef.viewangles[PITCH] + pitch); +} + +/* +============== +V_BoundOffsets +============== +*/ +void V_BoundOffsets (void) +{ +// absolutely bound refresh relative to entity clipping hull +// so the view can never be inside a solid wall + + if (r_refdef.vieworg[0] < cl.simorg[0] - 14) + r_refdef.vieworg[0] = cl.simorg[0] - 14; + else if (r_refdef.vieworg[0] > cl.simorg[0] + 14) + r_refdef.vieworg[0] = cl.simorg[0] + 14; + if (r_refdef.vieworg[1] < cl.simorg[1] - 14) + r_refdef.vieworg[1] = cl.simorg[1] - 14; + else if (r_refdef.vieworg[1] > cl.simorg[1] + 14) + r_refdef.vieworg[1] = cl.simorg[1] + 14; + if (r_refdef.vieworg[2] < cl.simorg[2] - 22) + r_refdef.vieworg[2] = cl.simorg[2] - 22; + else if (r_refdef.vieworg[2] > cl.simorg[2] + 30) + r_refdef.vieworg[2] = cl.simorg[2] + 30; +} + +/* +============== +V_AddIdle + +Idle swaying +============== +*/ +void V_AddIdle (void) +{ + r_refdef.viewangles[ROLL] += v_idlescale.value * sin(cl.time*v_iroll_cycle.value) * v_iroll_level.value; + r_refdef.viewangles[PITCH] += v_idlescale.value * sin(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value; + r_refdef.viewangles[YAW] += v_idlescale.value * sin(cl.time*v_iyaw_cycle.value) * v_iyaw_level.value; + + cl.viewent.angles[ROLL] -= v_idlescale.value * sin(cl.time*v_iroll_cycle.value) * v_iroll_level.value; + cl.viewent.angles[PITCH] -= v_idlescale.value * sin(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value; + cl.viewent.angles[YAW] -= v_idlescale.value * sin(cl.time*v_iyaw_cycle.value) * v_iyaw_level.value; +} + + +/* +============== +V_CalcViewRoll + +Roll is induced by movement and damage +============== +*/ +void V_CalcViewRoll (void) +{ + float side; + + side = V_CalcRoll (cl.simangles, cl.simvel); + r_refdef.viewangles[ROLL] += side; + + if (v_dmg_time > 0) + { + r_refdef.viewangles[ROLL] += v_dmg_time/v_kicktime.value*v_dmg_roll; + r_refdef.viewangles[PITCH] += v_dmg_time/v_kicktime.value*v_dmg_pitch; + v_dmg_time -= host_frametime; + } + +} + + +/* +================== +V_CalcIntermissionRefdef + +================== +*/ +void V_CalcIntermissionRefdef (void) +{ + entity_t *view; + float old; + +// view is the weapon model + view = &cl.viewent; + + VectorCopy (cl.simorg, r_refdef.vieworg); + VectorCopy (cl.simangles, r_refdef.viewangles); + view->model = NULL; + +// always idle in intermission + old = v_idlescale.value; + v_idlescale.value = 1; + V_AddIdle (); + v_idlescale.value = old; +} + +/* +================== +V_CalcRefdef + +================== +*/ +void V_CalcRefdef (void) +{ + entity_t *view; + int i; + vec3_t forward, right, up; + float bob; + + V_DriftPitch (); + +// view is the weapon model (only visible from inside body) + view = &cl.viewent; + + bob = V_CalcBob (); + +// refresh position from simulated origin + VectorCopy (cl.simorg, r_refdef.vieworg); + + r_refdef.vieworg[2] += bob; + +// never let it sit exactly on a node line, because a water plane can +// dissapear when viewed with the eye exactly on it. +// the server protocol only specifies to 1/8 pixel, so add 1/16 in each axis + r_refdef.vieworg[0] += 1.0/16; + r_refdef.vieworg[1] += 1.0/16; + r_refdef.vieworg[2] += 1.0/16; + + VectorCopy (cl.simangles, r_refdef.viewangles); + V_CalcViewRoll (); + V_AddIdle (); + + if (view_message->flags & PF_GIB) + r_refdef.vieworg[2] += 8; // gib view height + else if (view_message->flags & PF_DEAD) + r_refdef.vieworg[2] -= 16; // corpse view height + else + r_refdef.vieworg[2] += 22; // view height + + if (view_message->flags & PF_DEAD) // PF_GIB will also set PF_DEAD + r_refdef.viewangles[ROLL] = 80; // dead view angle + + +// offsets + AngleVectors (cl.simangles, forward, right, up); + +// set up gun position + VectorCopy (cl.simangles, view->angles); + + CalcGunAngle (); + + VectorCopy (cl.simorg, view->origin); + view->origin[2] += 22; + + for (i=0 ; i<3 ; i++) + { + view->origin[i] += forward[i]*bob*0.4; +// view->origin[i] += right[i]*bob*0.4; +// view->origin[i] += up[i]*bob*0.8; + } + view->origin[2] += bob; + +// fudge position around to keep amount of weapon visible +// roughly equal with different FOV + if (scr_viewsize.value == 110) + view->origin[2] += 1; + else if (scr_viewsize.value == 100) + view->origin[2] += 2; + else if (scr_viewsize.value == 90) + view->origin[2] += 1; + else if (scr_viewsize.value == 80) + view->origin[2] += 0.5; + + if (view_message->flags & (PF_GIB|PF_DEAD) ) + view->model = NULL; + else + view->model = cl.model_precache[cl.stats[STAT_WEAPON]]; + view->frame = view_message->weaponframe; + view->colormap = vid.colormap; + +// set up the refresh position + r_refdef.viewangles[PITCH] += cl.punchangle; + +// smooth out stair step ups + r_refdef.vieworg[2] += cl.crouch; + view->origin[2] += cl.crouch; +} + +/* +============= +DropPunchAngle +============= +*/ +void DropPunchAngle (void) +{ + cl.punchangle -= 10*host_frametime; + if (cl.punchangle < 0) + cl.punchangle = 0; +} + + +/* +================== +V_RenderView + +The player's clipping box goes from (-16 -16 -24) to (16 16 32) from +the entity origin, so any view position inside that will be valid +================== +*/ +extern vrect_t scr_vrect; + +void V_RenderView (void) +{ +// if (cl.simangles[ROLL]) +// Sys_Error ("cl.simangles[ROLL]"); // DEBUG +cl.simangles[ROLL] = 0; // FIXME @@@ + + if (cls.state != ca_active) { +#ifdef GLQUAKE + V_CalcBlend (); +#endif + return; + } + + view_frame = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK]; + view_message = &view_frame->playerstate[cl.playernum]; + + DropPunchAngle (); + if (cl.intermission) + { // intermission / finale rendering + V_CalcIntermissionRefdef (); + } + else + { + V_CalcRefdef (); + } + + R_PushDlights (); + R_RenderView (); + +#ifndef GLQUAKE + if (crosshair.value) + Draw_Crosshair(); +#endif + +} + +//============================================================================ + +/* +============= +V_Init +============= +*/ +void V_Init (void) +{ + Cmd_AddCommand ("v_cshift", V_cshift_f); + Cmd_AddCommand ("bf", V_BonusFlash_f); + Cmd_AddCommand ("centerview", V_StartPitchDrift); + + Cvar_RegisterVariable (&v_centermove); + Cvar_RegisterVariable (&v_centerspeed); + + Cvar_RegisterVariable (&v_iyaw_cycle); + Cvar_RegisterVariable (&v_iroll_cycle); + Cvar_RegisterVariable (&v_ipitch_cycle); + Cvar_RegisterVariable (&v_iyaw_level); + Cvar_RegisterVariable (&v_iroll_level); + Cvar_RegisterVariable (&v_ipitch_level); + + Cvar_RegisterVariable (&v_contentblend); + Cvar_RegisterVariable (&v_damagecshift); + Cvar_RegisterVariable (&v_quadcshift); + Cvar_RegisterVariable (&v_suitcshift); + Cvar_RegisterVariable (&v_ringcshift); + Cvar_RegisterVariable (&v_pentcshift); + + Cvar_RegisterVariable (&v_bonusflash); + + Cvar_RegisterVariable (&v_idlescale); + Cvar_RegisterVariable (&crosshaircolor); + Cvar_RegisterVariable (&crosshair); + Cvar_RegisterVariable (&cl_crossx); + Cvar_RegisterVariable (&cl_crossy); +#ifdef GLQUAKE + Cvar_RegisterVariable (&gl_cshiftpercent); +#endif + + Cvar_RegisterVariable (&cl_rollspeed); + Cvar_RegisterVariable (&cl_rollangle); + Cvar_RegisterVariable (&cl_bob); + Cvar_RegisterVariable (&cl_bobcycle); + Cvar_RegisterVariable (&cl_bobup); + + Cvar_RegisterVariable (&v_kicktime); + Cvar_RegisterVariable (&v_kickroll); + Cvar_RegisterVariable (&v_kickpitch); + + Cvar_RegisterVariable (&v_gamma); + Cvar_RegisterVariable (&v_contrast); +#ifdef GLQUAKE + Cvar_RegisterVariable (&gl_gamma); + Cvar_RegisterVariable (&gl_contrast); +#endif + +#ifndef GLQUAKE + BuildGammaTable (v_gamma.value, v_contrast.value); +#endif + +} + + diff --git a/source/view.h b/source/view.h index 99ffc8b1..65e43669 100644 --- a/source/view.h +++ b/source/view.h @@ -1,34 +1,34 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// view.h - -extern cvar_t v_gamma; -extern cvar_t v_contrast; - -#ifdef GLQUAKE -extern float v_blend[4]; -void V_AddLightBlend (float r, float g, float b, float a2); -#endif - -void V_Init (void); -void V_RenderView (void); -float V_CalcRoll (vec3_t angles, vec3_t velocity); -void V_UpdatePalette (void); - +/* +Copyright (C) 1996-1997 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. + +*/ +// view.h + +extern cvar_t v_gamma; +extern cvar_t v_contrast; + +#ifdef GLQUAKE +extern float v_blend[4]; +void V_AddLightBlend (float r, float g, float b, float a2); +#endif + +void V_Init (void); +void V_RenderView (void); +float V_CalcRoll (vec3_t angles, vec3_t velocity); +void V_UpdatePalette (void); + diff --git a/source/wad.c b/source/wad.c index 1eb8b59b..c84f72c6 100644 --- a/source/wad.c +++ b/source/wad.c @@ -1,158 +1,158 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// wad.c - -#include "quakedef.h" - -int wad_numlumps; -lumpinfo_t *wad_lumps; -byte *wad_base; - -void SwapPic (qpic_t *pic); - -/* -================== -W_CleanupName - -Lowercases name and pads with spaces and a terminating 0 to the length of -lumpinfo_t->name. -Used so lumpname lookups can proceed rapidly by comparing 4 chars at a time -Space padding is so names can be printed nicely in tables. -Can safely be performed in place. -================== -*/ -void W_CleanupName (char *in, char *out) -{ - int i; - int c; - - for (i=0 ; i<16 ; i++ ) - { - c = in[i]; - if (!c) - break; - - if (c >= 'A' && c <= 'Z') - c += ('a' - 'A'); - out[i] = c; - } - - for ( ; i< 16 ; i++ ) - out[i] = 0; -} - - - -/* -==================== -W_LoadWadFile -==================== -*/ -void W_LoadWadFile (char *filename) -{ - lumpinfo_t *lump_p; - wadinfo_t *header; - unsigned i; - int infotableofs; - - wad_base = COM_LoadHunkFile (filename); - if (!wad_base) - Sys_Error ("W_LoadWadFile: couldn't load %s", filename); - - header = (wadinfo_t *)wad_base; - - if (header->identification[0] != 'W' - || header->identification[1] != 'A' - || header->identification[2] != 'D' - || header->identification[3] != '2') - Sys_Error ("Wad file %s doesn't have WAD2 id\n",filename); - - wad_numlumps = LittleLong(header->numlumps); - infotableofs = LittleLong(header->infotableofs); - wad_lumps = (lumpinfo_t *)(wad_base + infotableofs); - - for (i=0, lump_p = wad_lumps ; ifilepos = LittleLong(lump_p->filepos); - lump_p->size = LittleLong(lump_p->size); - W_CleanupName (lump_p->name, lump_p->name); - if (lump_p->type == TYP_QPIC) - SwapPic ( (qpic_t *)(wad_base + lump_p->filepos)); - } -} - - -/* -============= -W_GetLumpinfo -============= -*/ -lumpinfo_t *W_GetLumpinfo (char *name) -{ - int i; - lumpinfo_t *lump_p; - char clean[16]; - - W_CleanupName (name, clean); - - for (lump_p=wad_lumps, i=0 ; iname)) - return lump_p; - } - - Sys_Error ("W_GetLumpinfo: %s not found", name); - return NULL; -} - -void *W_GetLumpName (char *name) -{ - lumpinfo_t *lump; - - lump = W_GetLumpinfo (name); - - return (void *)(wad_base + lump->filepos); -} - -void *W_GetLumpNum (int num) -{ - lumpinfo_t *lump; - - if (num < 0 || num > wad_numlumps) - Sys_Error ("W_GetLumpNum: bad number: %i", num); - - lump = wad_lumps + num; - - return (void *)(wad_base + lump->filepos); -} - -/* -============================================================================= - -automatic byte swapping - -============================================================================= -*/ - -void SwapPic (qpic_t *pic) -{ - pic->width = LittleLong(pic->width); - pic->height = LittleLong(pic->height); -} +/* +Copyright (C) 1996-1997 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. + +*/ +// wad.c + +#include "quakedef.h" + +int wad_numlumps; +lumpinfo_t *wad_lumps; +byte *wad_base; + +void SwapPic (qpic_t *pic); + +/* +================== +W_CleanupName + +Lowercases name and pads with spaces and a terminating 0 to the length of +lumpinfo_t->name. +Used so lumpname lookups can proceed rapidly by comparing 4 chars at a time +Space padding is so names can be printed nicely in tables. +Can safely be performed in place. +================== +*/ +void W_CleanupName (char *in, char *out) +{ + int i; + int c; + + for (i=0 ; i<16 ; i++ ) + { + c = in[i]; + if (!c) + break; + + if (c >= 'A' && c <= 'Z') + c += ('a' - 'A'); + out[i] = c; + } + + for ( ; i< 16 ; i++ ) + out[i] = 0; +} + + + +/* +==================== +W_LoadWadFile +==================== +*/ +void W_LoadWadFile (char *filename) +{ + lumpinfo_t *lump_p; + wadinfo_t *header; + unsigned i; + int infotableofs; + + wad_base = COM_LoadHunkFile (filename); + if (!wad_base) + Sys_Error ("W_LoadWadFile: couldn't load %s", filename); + + header = (wadinfo_t *)wad_base; + + if (header->identification[0] != 'W' + || header->identification[1] != 'A' + || header->identification[2] != 'D' + || header->identification[3] != '2') + Sys_Error ("Wad file %s doesn't have WAD2 id\n",filename); + + wad_numlumps = LittleLong(header->numlumps); + infotableofs = LittleLong(header->infotableofs); + wad_lumps = (lumpinfo_t *)(wad_base + infotableofs); + + for (i=0, lump_p = wad_lumps ; ifilepos = LittleLong(lump_p->filepos); + lump_p->size = LittleLong(lump_p->size); + W_CleanupName (lump_p->name, lump_p->name); + if (lump_p->type == TYP_QPIC) + SwapPic ( (qpic_t *)(wad_base + lump_p->filepos)); + } +} + + +/* +============= +W_GetLumpinfo +============= +*/ +lumpinfo_t *W_GetLumpinfo (char *name) +{ + int i; + lumpinfo_t *lump_p; + char clean[16]; + + W_CleanupName (name, clean); + + for (lump_p=wad_lumps, i=0 ; iname)) + return lump_p; + } + + Sys_Error ("W_GetLumpinfo: %s not found", name); + return NULL; +} + +void *W_GetLumpName (char *name) +{ + lumpinfo_t *lump; + + lump = W_GetLumpinfo (name); + + return (void *)(wad_base + lump->filepos); +} + +void *W_GetLumpNum (int num) +{ + lumpinfo_t *lump; + + if (num < 0 || num > wad_numlumps) + Sys_Error ("W_GetLumpNum: bad number: %i", num); + + lump = wad_lumps + num; + + return (void *)(wad_base + lump->filepos); +} + +/* +============================================================================= + +automatic byte swapping + +============================================================================= +*/ + +void SwapPic (qpic_t *pic) +{ + pic->width = LittleLong(pic->width); + pic->height = LittleLong(pic->height); +} diff --git a/source/wad.h b/source/wad.h index 4965e999..c37498e0 100644 --- a/source/wad.h +++ b/source/wad.h @@ -1,80 +1,80 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// wad.h - -#ifndef _WAD_H -#define _WAD_H - -//=============== -// TYPES -//=============== - -#define CMP_NONE 0 -#define CMP_LZSS 1 - -#define TYP_NONE 0 -#define TYP_LABEL 1 - -#define TYP_LUMPY 64 // 64 + grab command number -#define TYP_PALETTE 64 -#define TYP_QTEX 65 -#define TYP_QPIC 66 -#define TYP_SOUND 67 -#define TYP_MIPTEX 68 - -typedef struct -{ - int width, height; - byte data[4]; // variably sized -} qpic_t; - - - -typedef struct -{ - char identification[4]; // should be WAD2 or 2DAW - int numlumps; - int infotableofs; -} wadinfo_t; - -typedef struct -{ - int filepos; - int disksize; - int size; // uncompressed - char type; - char compression; - char pad1, pad2; - char name[16]; // must be null terminated -} lumpinfo_t; - -extern int wad_numlumps; -extern lumpinfo_t *wad_lumps; -extern byte *wad_base; - -void W_LoadWadFile (char *filename); -void W_CleanupName (char *in, char *out); -lumpinfo_t *W_GetLumpinfo (char *name); -void *W_GetLumpName (char *name); -void *W_GetLumpNum (int num); - -void SwapPic (qpic_t *pic); - -#endif // _WAD_H +/* +Copyright (C) 1996-1997 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. + +*/ +// wad.h + +#ifndef _WAD_H +#define _WAD_H + +//=============== +// TYPES +//=============== + +#define CMP_NONE 0 +#define CMP_LZSS 1 + +#define TYP_NONE 0 +#define TYP_LABEL 1 + +#define TYP_LUMPY 64 // 64 + grab command number +#define TYP_PALETTE 64 +#define TYP_QTEX 65 +#define TYP_QPIC 66 +#define TYP_SOUND 67 +#define TYP_MIPTEX 68 + +typedef struct +{ + int width, height; + byte data[4]; // variably sized +} qpic_t; + + + +typedef struct +{ + char identification[4]; // should be WAD2 or 2DAW + int numlumps; + int infotableofs; +} wadinfo_t; + +typedef struct +{ + int filepos; + int disksize; + int size; // uncompressed + char type; + char compression; + char pad1, pad2; + char name[16]; // must be null terminated +} lumpinfo_t; + +extern int wad_numlumps; +extern lumpinfo_t *wad_lumps; +extern byte *wad_base; + +void W_LoadWadFile (char *filename); +void W_CleanupName (char *in, char *out); +lumpinfo_t *W_GetLumpinfo (char *name); +void *W_GetLumpName (char *name); +void *W_GetLumpNum (int num); + +void SwapPic (qpic_t *pic); + +#endif // _WAD_H diff --git a/source/winquake.h b/source/winquake.h index 05d13074..fac1fc50 100644 --- a/source/winquake.h +++ b/source/winquake.h @@ -1,114 +1,114 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// winquake.h: Win32-specific Quake header file - -#ifdef _WIN32 -#pragma warning( disable : 4229 ) // mgraph gets this - -#include -#define WM_MOUSEWHEEL 0x020A - -#ifndef SERVERONLY -#include -#include -#ifndef GLQUAKE -#include -#endif -#endif - -extern HINSTANCE global_hInstance; -extern int global_nCmdShow; - -#ifndef SERVERONLY - -extern LPDIRECTDRAW lpDD; -extern qboolean DDActive; -extern LPDIRECTDRAWSURFACE lpPrimary; -extern LPDIRECTDRAWSURFACE lpFrontBuffer; -extern LPDIRECTDRAWSURFACE lpBackBuffer; -extern LPDIRECTDRAWPALETTE lpDDPal; -extern LPDIRECTSOUND pDS; -extern LPDIRECTSOUNDBUFFER pDSBuf; - -extern DWORD gSndBufSize; -//#define SNDBUFSIZE 65536 - -void VID_LockBuffer (void); -void VID_UnlockBuffer (void); - -#endif - -typedef enum {MS_WINDOWED, MS_FULLSCREEN, MS_FULLDIB, MS_UNINIT} modestate_t; - -extern modestate_t modestate; - -extern HWND mainwindow; -extern qboolean ActiveApp, Minimized; - -extern qboolean WinNT; - -int VID_ForceUnlockedAndReturnState (void); -void VID_ForceLockState (int lk); - -void IN_ShowMouse (void); -void IN_DeactivateMouse (void); -void IN_HideMouse (void); -void IN_ActivateMouse (void); -void IN_RestoreOriginalMouseState (void); -void IN_SetQuakeMouseState (void); -void IN_MouseEvent (int mstate); - -extern qboolean winsock_lib_initialized; - -extern int window_center_x, window_center_y; -extern RECT window_rect; - -extern qboolean mouseinitialized; -extern HWND hwnd_dialog; - -extern HANDLE hinput, houtput; - -void IN_UpdateClipCursor (void); -void CenterWindow(HWND hWndCenter, int width, int height, BOOL lefttopjustify); - -void S_BlockSound (void); -void S_UnblockSound (void); - -void VID_SetDefaultMode (void); - -int (PASCAL FAR *pWSAStartup)(WORD wVersionRequired, LPWSADATA lpWSAData); -int (PASCAL FAR *pWSACleanup)(void); -int (PASCAL FAR *pWSAGetLastError)(void); -SOCKET (PASCAL FAR *psocket)(int af, int type, int protocol); -int (PASCAL FAR *pioctlsocket)(SOCKET s, long cmd, u_long FAR *argp); -int (PASCAL FAR *psetsockopt)(SOCKET s, int level, int optname, - const char FAR * optval, int optlen); -int (PASCAL FAR *precvfrom)(SOCKET s, char FAR * buf, int len, int flags, - struct sockaddr FAR *from, int FAR * fromlen); -int (PASCAL FAR *psendto)(SOCKET s, const char FAR * buf, int len, int flags, - const struct sockaddr FAR *to, int tolen); -int (PASCAL FAR *pclosesocket)(SOCKET s); -int (PASCAL FAR *pgethostname)(char FAR * name, int namelen); -struct hostent FAR * (PASCAL FAR *pgethostbyname)(const char FAR * name); -struct hostent FAR * (PASCAL FAR *pgethostbyaddr)(const char FAR * addr, - int len, int type); -int (PASCAL FAR *pgetsockname)(SOCKET s, struct sockaddr FAR *name, - int FAR * namelen); -#endif +/* +Copyright (C) 1996-1997 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. + +*/ +// winquake.h: Win32-specific Quake header file + +#ifdef _WIN32 +#pragma warning( disable : 4229 ) // mgraph gets this + +#include +#define WM_MOUSEWHEEL 0x020A + +#ifndef SERVERONLY +#include +#include +#ifndef GLQUAKE +#include +#endif +#endif + +extern HINSTANCE global_hInstance; +extern int global_nCmdShow; + +#ifndef SERVERONLY + +extern LPDIRECTDRAW lpDD; +extern qboolean DDActive; +extern LPDIRECTDRAWSURFACE lpPrimary; +extern LPDIRECTDRAWSURFACE lpFrontBuffer; +extern LPDIRECTDRAWSURFACE lpBackBuffer; +extern LPDIRECTDRAWPALETTE lpDDPal; +extern LPDIRECTSOUND pDS; +extern LPDIRECTSOUNDBUFFER pDSBuf; + +extern DWORD gSndBufSize; +//#define SNDBUFSIZE 65536 + +void VID_LockBuffer (void); +void VID_UnlockBuffer (void); + +#endif + +typedef enum {MS_WINDOWED, MS_FULLSCREEN, MS_FULLDIB, MS_UNINIT} modestate_t; + +extern modestate_t modestate; + +extern HWND mainwindow; +extern qboolean ActiveApp, Minimized; + +extern qboolean WinNT; + +int VID_ForceUnlockedAndReturnState (void); +void VID_ForceLockState (int lk); + +void IN_ShowMouse (void); +void IN_DeactivateMouse (void); +void IN_HideMouse (void); +void IN_ActivateMouse (void); +void IN_RestoreOriginalMouseState (void); +void IN_SetQuakeMouseState (void); +void IN_MouseEvent (int mstate); + +extern qboolean winsock_lib_initialized; + +extern int window_center_x, window_center_y; +extern RECT window_rect; + +extern qboolean mouseinitialized; +extern HWND hwnd_dialog; + +extern HANDLE hinput, houtput; + +void IN_UpdateClipCursor (void); +void CenterWindow(HWND hWndCenter, int width, int height, BOOL lefttopjustify); + +void S_BlockSound (void); +void S_UnblockSound (void); + +void VID_SetDefaultMode (void); + +int (PASCAL FAR *pWSAStartup)(WORD wVersionRequired, LPWSADATA lpWSAData); +int (PASCAL FAR *pWSACleanup)(void); +int (PASCAL FAR *pWSAGetLastError)(void); +SOCKET (PASCAL FAR *psocket)(int af, int type, int protocol); +int (PASCAL FAR *pioctlsocket)(SOCKET s, long cmd, u_long FAR *argp); +int (PASCAL FAR *psetsockopt)(SOCKET s, int level, int optname, + const char FAR * optval, int optlen); +int (PASCAL FAR *precvfrom)(SOCKET s, char FAR * buf, int len, int flags, + struct sockaddr FAR *from, int FAR * fromlen); +int (PASCAL FAR *psendto)(SOCKET s, const char FAR * buf, int len, int flags, + const struct sockaddr FAR *to, int tolen); +int (PASCAL FAR *pclosesocket)(SOCKET s); +int (PASCAL FAR *pgethostname)(char FAR * name, int namelen); +struct hostent FAR * (PASCAL FAR *pgethostbyname)(const char FAR * name); +struct hostent FAR * (PASCAL FAR *pgethostbyaddr)(const char FAR * addr, + int len, int type); +int (PASCAL FAR *pgetsockname)(SOCKET s, struct sockaddr FAR *name, + int FAR * namelen); +#endif diff --git a/source/world.c b/source/world.c index affa4c88..cbaa0b5f 100644 --- a/source/world.c +++ b/source/world.c @@ -1,924 +1,924 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// world.c -- world query functions - -#include "qwsvdef.h" - -/* - -entities never clip against themselves, or their owner - -line of sight checks trace->crosscontent, but bullets don't - -*/ - - -typedef struct -{ - vec3_t boxmins, boxmaxs;// enclose the test object along entire move - float *mins, *maxs; // size of the moving object - vec3_t mins2, maxs2; // size when clipping against mosnters - float *start, *end; - trace_t trace; - int type; - edict_t *passedict; -} moveclip_t; - - -int SV_HullPointContents (hull_t *hull, int num, vec3_t p); - -/* -=============================================================================== - -HULL BOXES - -=============================================================================== -*/ - - -static hull_t box_hull; -static dclipnode_t box_clipnodes[6]; -static mplane_t box_planes[6]; - -/* -=================== -SV_InitBoxHull - -Set up the planes and clipnodes so that the six floats of a bounding box -can just be stored out and get a proper hull_t structure. -=================== -*/ -void SV_InitBoxHull (void) -{ - int i; - int side; - - box_hull.clipnodes = box_clipnodes; - box_hull.planes = box_planes; - box_hull.firstclipnode = 0; - box_hull.lastclipnode = 5; - - for (i=0 ; i<6 ; i++) - { - box_clipnodes[i].planenum = i; - - side = i&1; - - box_clipnodes[i].children[side] = CONTENTS_EMPTY; - if (i != 5) - box_clipnodes[i].children[side^1] = i + 1; - else - box_clipnodes[i].children[side^1] = CONTENTS_SOLID; - - box_planes[i].type = i>>1; - box_planes[i].normal[i>>1] = 1; - } - -} - - -/* -=================== -SV_HullForBox - -To keep everything totally uniform, bounding boxes are turned into small -BSP trees instead of being compared directly. -=================== -*/ -hull_t *SV_HullForBox (vec3_t mins, vec3_t maxs) -{ - box_planes[0].dist = maxs[0]; - box_planes[1].dist = mins[0]; - box_planes[2].dist = maxs[1]; - box_planes[3].dist = mins[1]; - box_planes[4].dist = maxs[2]; - box_planes[5].dist = mins[2]; - - return &box_hull; -} - - - -/* -================ -SV_HullForEntity - -Returns a hull that can be used for testing or clipping an 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. -================ -*/ -hull_t *SV_HullForEntity (edict_t *ent, vec3_t mins, vec3_t maxs, vec3_t offset) -{ - model_t *model; - vec3_t size; - vec3_t hullmins, hullmaxs; - hull_t *hull; - -// decide which clipping hull to use, based on the size - if (ent->v.solid == SOLID_BSP) - { // explicit hulls in the BSP model - if (ent->v.movetype != MOVETYPE_PUSH) - SV_Error ("SOLID_BSP without MOVETYPE_PUSH"); - - model = sv.models[ (int)ent->v.modelindex ]; - - if (!model || model->type != mod_brush) - SV_Error ("MOVETYPE_PUSH with a non bsp model"); - - VectorSubtract (maxs, mins, size); - if (size[0] < 3) - hull = &model->hulls[0]; - else if (size[0] <= 32) - hull = &model->hulls[1]; - else - hull = &model->hulls[2]; - -// calculate an offset value to center the origin - VectorSubtract (hull->clip_mins, mins, offset); - VectorAdd (offset, ent->v.origin, offset); - } - else - { // create a temp hull from bounding box sizes - - VectorSubtract (ent->v.mins, maxs, hullmins); - VectorSubtract (ent->v.maxs, mins, hullmaxs); - hull = SV_HullForBox (hullmins, hullmaxs); - - VectorCopy (ent->v.origin, offset); - } - - - return hull; -} - -/* -=============================================================================== - -ENTITY AREA CHECKING - -=============================================================================== -*/ - - -areanode_t sv_areanodes[AREA_NODES]; -int sv_numareanodes; - -/* -=============== -SV_CreateAreaNode - -=============== -*/ -areanode_t *SV_CreateAreaNode (int depth, vec3_t mins, vec3_t maxs) -{ - areanode_t *anode; - vec3_t size; - vec3_t mins1, maxs1, mins2, maxs2; - - anode = &sv_areanodes[sv_numareanodes]; - sv_numareanodes++; - - ClearLink (&anode->trigger_edicts); - ClearLink (&anode->solid_edicts); - - if (depth == AREA_DEPTH) - { - anode->axis = -1; - anode->children[0] = anode->children[1] = NULL; - return anode; - } - - VectorSubtract (maxs, mins, size); - if (size[0] > size[1]) - anode->axis = 0; - else - anode->axis = 1; - - anode->dist = 0.5 * (maxs[anode->axis] + mins[anode->axis]); - VectorCopy (mins, mins1); - VectorCopy (mins, mins2); - VectorCopy (maxs, maxs1); - VectorCopy (maxs, maxs2); - - maxs1[anode->axis] = mins2[anode->axis] = anode->dist; - - anode->children[0] = SV_CreateAreaNode (depth+1, mins2, maxs2); - anode->children[1] = SV_CreateAreaNode (depth+1, mins1, maxs1); - - return anode; -} - -/* -=============== -SV_ClearWorld - -=============== -*/ -void SV_ClearWorld (void) -{ - SV_InitBoxHull (); - - memset (sv_areanodes, 0, sizeof(sv_areanodes)); - sv_numareanodes = 0; - SV_CreateAreaNode (0, sv.worldmodel->mins, sv.worldmodel->maxs); -} - - -/* -=============== -SV_UnlinkEdict - -=============== -*/ -void SV_UnlinkEdict (edict_t *ent) -{ - if (!ent->area.prev) - return; // not linked in anywhere - RemoveLink (&ent->area); - ent->area.prev = ent->area.next = NULL; -} - - -/* -==================== -SV_TouchLinks -==================== -*/ -void SV_TouchLinks ( edict_t *ent, areanode_t *node ) -{ - link_t *l, *next; - edict_t *touch; - int old_self, old_other; - -// touch linked edicts - for (l = node->trigger_edicts.next ; l != &node->trigger_edicts ; l = next) - { - next = l->next; - touch = EDICT_FROM_AREA(l); - if (touch == ent) - continue; - if (!touch->v.touch || touch->v.solid != SOLID_TRIGGER) - continue; - if (ent->v.absmin[0] > touch->v.absmax[0] - || ent->v.absmin[1] > touch->v.absmax[1] - || ent->v.absmin[2] > touch->v.absmax[2] - || ent->v.absmax[0] < touch->v.absmin[0] - || ent->v.absmax[1] < touch->v.absmin[1] - || ent->v.absmax[2] < touch->v.absmin[2] ) - continue; - - old_self = pr_global_struct->self; - old_other = pr_global_struct->other; - - pr_global_struct->self = EDICT_TO_PROG(touch); - pr_global_struct->other = EDICT_TO_PROG(ent); - pr_global_struct->time = sv.time; - PR_ExecuteProgram (touch->v.touch); - - pr_global_struct->self = old_self; - pr_global_struct->other = old_other; - } - -// recurse down both sides - if (node->axis == -1) - return; - - if ( ent->v.absmax[node->axis] > node->dist ) - SV_TouchLinks ( ent, node->children[0] ); - if ( ent->v.absmin[node->axis] < node->dist ) - SV_TouchLinks ( ent, node->children[1] ); -} - - -/* -=============== -SV_FindTouchedLeafs - -=============== -*/ -void SV_FindTouchedLeafs (edict_t *ent, mnode_t *node) -{ - mplane_t *splitplane; - mleaf_t *leaf; - int sides; - int leafnum; - - if (node->contents == CONTENTS_SOLID) - return; - -// add an efrag if the node is a leaf - - if ( node->contents < 0) - { - if (ent->num_leafs == MAX_ENT_LEAFS) - return; - - leaf = (mleaf_t *)node; - leafnum = leaf - sv.worldmodel->leafs - 1; - - ent->leafnums[ent->num_leafs] = leafnum; - ent->num_leafs++; - return; - } - -// NODE_MIXED - - splitplane = node->plane; - sides = BOX_ON_PLANE_SIDE(ent->v.absmin, ent->v.absmax, splitplane); - -// recurse down the contacted sides - if (sides & 1) - SV_FindTouchedLeafs (ent, node->children[0]); - - if (sides & 2) - SV_FindTouchedLeafs (ent, node->children[1]); -} - -/* -=============== -SV_LinkEdict - -=============== -*/ -void SV_LinkEdict (edict_t *ent, qboolean touch_triggers) -{ - areanode_t *node; - - if (ent->area.prev) - SV_UnlinkEdict (ent); // unlink from old position - - if (ent == sv.edicts) - return; // don't add the world - - if (ent->free) - return; - -// set the abs box - VectorAdd (ent->v.origin, ent->v.mins, ent->v.absmin); - VectorAdd (ent->v.origin, ent->v.maxs, ent->v.absmax); - -// -// to make items easier to pick up and allow them to be grabbed off -// of shelves, the abs sizes are expanded -// - if ((int)ent->v.flags & FL_ITEM) - { - ent->v.absmin[0] -= 15; - ent->v.absmin[1] -= 15; - ent->v.absmax[0] += 15; - ent->v.absmax[1] += 15; - } - else - { // because movement is clipped an epsilon away from an actual edge, - // we must fully check even when bounding boxes don't quite touch - ent->v.absmin[0] -= 1; - ent->v.absmin[1] -= 1; - ent->v.absmin[2] -= 1; - ent->v.absmax[0] += 1; - ent->v.absmax[1] += 1; - ent->v.absmax[2] += 1; - } - -// link to PVS leafs - ent->num_leafs = 0; - if (ent->v.modelindex) - SV_FindTouchedLeafs (ent, sv.worldmodel->nodes); - - if (ent->v.solid == SOLID_NOT) - return; - -// find the first node that the ent's box crosses - node = sv_areanodes; - while (1) - { - if (node->axis == -1) - break; - if (ent->v.absmin[node->axis] > node->dist) - node = node->children[0]; - else if (ent->v.absmax[node->axis] < node->dist) - node = node->children[1]; - else - break; // crosses the node - } - -// link it in - - if (ent->v.solid == SOLID_TRIGGER) - InsertLinkBefore (&ent->area, &node->trigger_edicts); - else - InsertLinkBefore (&ent->area, &node->solid_edicts); - -// if touch_triggers, touch all entities at this node and decend for more - if (touch_triggers) - SV_TouchLinks ( ent, sv_areanodes ); -} - - - -/* -=============================================================================== - -POINT TESTING IN HULLS - -=============================================================================== -*/ - -#if !id386 - -/* -================== -SV_HullPointContents - -================== -*/ -int SV_HullPointContents (hull_t *hull, int num, vec3_t p) -{ - float d; - dclipnode_t *node; - mplane_t *plane; - - while (num >= 0) - { - if (num < hull->firstclipnode || num > hull->lastclipnode) - SV_Error ("SV_HullPointContents: bad node number"); - - node = hull->clipnodes + num; - plane = hull->planes + node->planenum; - - if (plane->type < 3) - d = p[plane->type] - plane->dist; - else - d = DotProduct (plane->normal, p) - plane->dist; - if (d < 0) - num = node->children[1]; - else - num = node->children[0]; - } - - return num; -} - -#endif // !id386 - - -/* -================== -SV_PointContents - -================== -*/ -int SV_PointContents (vec3_t p) -{ - return SV_HullPointContents (&sv.worldmodel->hulls[0], 0, p); -} - -//=========================================================================== - -/* -============ -SV_TestEntityPosition - -A small wrapper around SV_BoxInSolidEntity that never clips against the -supplied entity. -============ -*/ -edict_t *SV_TestEntityPosition (edict_t *ent) -{ - trace_t trace; - - trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, ent->v.origin, 0, ent); - - if (trace.startsolid) - return sv.edicts; - - return NULL; -} - -/* -=============================================================================== - -LINE TESTING IN HULLS - -=============================================================================== -*/ - -// 1/32 epsilon to keep floating point happy -#define DIST_EPSILON (0.03125) - -/* -================== -SV_RecursiveHullCheck - -================== -*/ -qboolean SV_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, trace_t *trace) -{ - dclipnode_t *node; - mplane_t *plane; - float t1, t2; - float frac; - int i; - vec3_t mid; - int side; - float midf; - -// check for empty - if (num < 0) - { - if (num != CONTENTS_SOLID) - { - trace->allsolid = false; - if (num == CONTENTS_EMPTY) - trace->inopen = true; - else - trace->inwater = true; - } - else - trace->startsolid = true; - return true; // empty - } - - if (num < hull->firstclipnode || num > hull->lastclipnode) - SV_Error ("SV_RecursiveHullCheck: bad node number"); - -// -// find the point distances -// - node = hull->clipnodes + num; - plane = hull->planes + node->planenum; - - if (plane->type < 3) - { - t1 = p1[plane->type] - plane->dist; - t2 = p2[plane->type] - plane->dist; - } - else - { - t1 = DotProduct (plane->normal, p1) - plane->dist; - t2 = DotProduct (plane->normal, p2) - plane->dist; - } - -#if 1 - if (t1 >= 0 && t2 >= 0) - return SV_RecursiveHullCheck (hull, node->children[0], p1f, p2f, p1, p2, trace); - if (t1 < 0 && t2 < 0) - return SV_RecursiveHullCheck (hull, node->children[1], p1f, p2f, p1, p2, trace); -#else - if ( (t1 >= DIST_EPSILON && t2 >= DIST_EPSILON) || (t2 > t1 && t1 >= 0) ) - return SV_RecursiveHullCheck (hull, node->children[0], p1f, p2f, p1, p2, trace); - if ( (t1 <= -DIST_EPSILON && t2 <= -DIST_EPSILON) || (t2 < t1 && t1 <= 0) ) - return SV_RecursiveHullCheck (hull, node->children[1], p1f, p2f, p1, p2, trace); -#endif - -// put the crosspoint DIST_EPSILON pixels on the near side - if (t1 < 0) - frac = (t1 + DIST_EPSILON)/(t1-t2); - else - frac = (t1 - DIST_EPSILON)/(t1-t2); - if (frac < 0) - frac = 0; - if (frac > 1) - frac = 1; - - midf = p1f + (p2f - p1f)*frac; - for (i=0 ; i<3 ; i++) - mid[i] = p1[i] + frac*(p2[i] - p1[i]); - - side = (t1 < 0); - -// move up to the node - if (!SV_RecursiveHullCheck (hull, node->children[side], p1f, midf, p1, mid, trace) ) - return false; - -#ifdef PARANOID - if (SV_HullPointContents (sv_hullmodel, mid, node->children[side]) - == CONTENTS_SOLID) - { - Con_Printf ("mid PointInHullSolid\n"); - return false; - } -#endif - - if (SV_HullPointContents (hull, node->children[side^1], mid) - != CONTENTS_SOLID) -// go past the node - return SV_RecursiveHullCheck (hull, node->children[side^1], midf, p2f, mid, p2, trace); - - if (trace->allsolid) - return false; // never got out of the solid area - -//================== -// the other side of the node is solid, this is the impact point -//================== - if (!side) - { - VectorCopy (plane->normal, trace->plane.normal); - trace->plane.dist = plane->dist; - } - else - { - VectorSubtract (vec3_origin, plane->normal, trace->plane.normal); - trace->plane.dist = -plane->dist; - } - - while (SV_HullPointContents (hull, hull->firstclipnode, mid) - == CONTENTS_SOLID) - { // shouldn't really happen, but does occasionally - frac -= 0.1; - if (frac < 0) - { - trace->fraction = midf; - VectorCopy (mid, trace->endpos); - Con_DPrintf ("backup past 0\n"); // Tonik: was Con_Printf - return false; - } - midf = p1f + (p2f - p1f)*frac; - for (i=0 ; i<3 ; i++) - mid[i] = p1[i] + frac*(p2[i] - p1[i]); - } - - trace->fraction = midf; - VectorCopy (mid, trace->endpos); - - return false; -} - - -/* -================== -SV_ClipMoveToEntity - -Handles selection or creation of a clipping hull, and offseting (and -eventually rotation) of the end points -================== -*/ -trace_t SV_ClipMoveToEntity (edict_t *ent, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end) -{ - trace_t trace; - vec3_t offset; - vec3_t start_l, end_l; - hull_t *hull; - -// fill in a default trace - memset (&trace, 0, sizeof(trace_t)); - trace.fraction = 1; - trace.allsolid = true; - VectorCopy (end, trace.endpos); - -// get the clipping hull - hull = SV_HullForEntity (ent, mins, maxs, offset); - - VectorSubtract (start, offset, start_l); - VectorSubtract (end, offset, end_l); - -// trace a line through the apropriate clipping hull - SV_RecursiveHullCheck (hull, hull->firstclipnode, 0, 1, start_l, end_l, &trace); - -// fix trace up by the offset - if (trace.fraction != 1) - VectorAdd (trace.endpos, offset, trace.endpos); - -// did we clip the move? - if (trace.fraction < 1 || trace.startsolid ) - trace.ent = ent; - - return trace; -} - -//=========================================================================== - -/* -==================== -SV_ClipToLinks - -Mins and maxs enclose the entire area swept by the move -==================== -*/ -void SV_ClipToLinks ( areanode_t *node, moveclip_t *clip ) -{ - link_t *l, *next; - edict_t *touch; - trace_t trace; - -// touch linked edicts - for (l = node->solid_edicts.next ; l != &node->solid_edicts ; l = next) - { - next = l->next; - touch = EDICT_FROM_AREA(l); - if (touch->v.solid == SOLID_NOT) - continue; - if (touch == clip->passedict) - continue; - if (touch->v.solid == SOLID_TRIGGER) - SV_Error ("Trigger in clipping list"); - - if (clip->type == MOVE_NOMONSTERS && touch->v.solid != SOLID_BSP) - continue; - - if (clip->boxmins[0] > touch->v.absmax[0] - || clip->boxmins[1] > touch->v.absmax[1] - || clip->boxmins[2] > touch->v.absmax[2] - || clip->boxmaxs[0] < touch->v.absmin[0] - || clip->boxmaxs[1] < touch->v.absmin[1] - || clip->boxmaxs[2] < touch->v.absmin[2] ) - continue; - - if (clip->passedict && clip->passedict->v.size[0] && !touch->v.size[0]) - continue; // points never interact - - // might intersect, so do an exact clip - if (clip->trace.allsolid) - return; - if (clip->passedict) - { - if (PROG_TO_EDICT(touch->v.owner) == clip->passedict) - continue; // don't clip against own missiles - if (PROG_TO_EDICT(clip->passedict->v.owner) == touch) - continue; // don't clip against owner - } - - if ((int)touch->v.flags & FL_MONSTER) - trace = SV_ClipMoveToEntity (touch, clip->start, clip->mins2, clip->maxs2, clip->end); - else - trace = SV_ClipMoveToEntity (touch, clip->start, clip->mins, clip->maxs, clip->end); - if (trace.allsolid || trace.startsolid || - trace.fraction < clip->trace.fraction) - { - trace.ent = touch; - if (clip->trace.startsolid) - { - clip->trace = trace; - clip->trace.startsolid = true; - } - else - clip->trace = trace; - } - else if (trace.startsolid) - clip->trace.startsolid = true; - } - -// recurse down both sides - if (node->axis == -1) - return; - - if ( clip->boxmaxs[node->axis] > node->dist ) - SV_ClipToLinks ( node->children[0], clip ); - if ( clip->boxmins[node->axis] < node->dist ) - SV_ClipToLinks ( node->children[1], clip ); -} - - -/* -================== -SV_MoveBounds -================== -*/ -void SV_MoveBounds (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, vec3_t boxmins, vec3_t boxmaxs) -{ -#if 0 -// debug to test against everything -boxmins[0] = boxmins[1] = boxmins[2] = -9999; -boxmaxs[0] = boxmaxs[1] = boxmaxs[2] = 9999; -#else - int i; - - for (i=0 ; i<3 ; i++) - { - if (end[i] > start[i]) - { - boxmins[i] = start[i] + mins[i] - 1; - boxmaxs[i] = end[i] + maxs[i] + 1; - } - else - { - boxmins[i] = end[i] + mins[i] - 1; - boxmaxs[i] = start[i] + maxs[i] + 1; - } - } -#endif -} - -/* -================== -SV_Move -================== -*/ -trace_t SV_Move (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int type, edict_t *passedict) -{ - moveclip_t clip; - int i; - - memset ( &clip, 0, sizeof ( moveclip_t ) ); - -// clip to world - clip.trace = SV_ClipMoveToEntity ( sv.edicts, start, mins, maxs, end ); - - clip.start = start; - clip.end = end; - clip.mins = mins; - clip.maxs = maxs; - clip.type = type; - clip.passedict = passedict; - - if (type == MOVE_MISSILE) - { - for (i=0 ; i<3 ; i++) - { - clip.mins2[i] = -15; - clip.maxs2[i] = 15; - } - } - else - { - VectorCopy (mins, clip.mins2); - VectorCopy (maxs, clip.maxs2); - } - -// create the bounding box of the entire move - SV_MoveBounds ( start, clip.mins2, clip.maxs2, end, clip.boxmins, clip.boxmaxs ); - -// clip to entities - SV_ClipToLinks ( sv_areanodes, &clip ); - - return clip.trace; -} - -//============================================================================= - -/* -============ -SV_TestPlayerPosition - -============ -*/ -edict_t *SV_TestPlayerPosition (edict_t *ent, vec3_t origin) -{ - hull_t *hull; - edict_t *check; - vec3_t boxmins, boxmaxs; - vec3_t offset; - int e; - -// check world first - hull = &sv.worldmodel->hulls[1]; - if ( SV_HullPointContents (hull, hull->firstclipnode, origin) != CONTENTS_EMPTY ) - return sv.edicts; - -// check all entities - VectorAdd (origin, ent->v.mins, boxmins); - VectorAdd (origin, ent->v.maxs, boxmaxs); - - check = NEXT_EDICT(sv.edicts); - for (e=1 ; efree) - continue; - if (check->v.solid != SOLID_BSP && - check->v.solid != SOLID_BBOX && - check->v.solid != SOLID_SLIDEBOX) - continue; - - if (boxmins[0] > check->v.absmax[0] - || boxmins[1] > check->v.absmax[1] - || boxmins[2] > check->v.absmax[2] - || boxmaxs[0] < check->v.absmin[0] - || boxmaxs[1] < check->v.absmin[1] - || boxmaxs[2] < check->v.absmin[2] ) - continue; - - if (check == ent) - continue; - - // get the clipping hull - hull = SV_HullForEntity (check, ent->v.mins, ent->v.maxs, offset); - - VectorSubtract (origin, offset, offset); - - // test the point - if ( SV_HullPointContents (hull, hull->firstclipnode, offset) != CONTENTS_EMPTY ) - return check; - } - - return NULL; -} - - +/* +Copyright (C) 1996-1997 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. + +*/ +// world.c -- world query functions + +#include "qwsvdef.h" + +/* + +entities never clip against themselves, or their owner + +line of sight checks trace->crosscontent, but bullets don't + +*/ + + +typedef struct +{ + vec3_t boxmins, boxmaxs;// enclose the test object along entire move + float *mins, *maxs; // size of the moving object + vec3_t mins2, maxs2; // size when clipping against mosnters + float *start, *end; + trace_t trace; + int type; + edict_t *passedict; +} moveclip_t; + + +int SV_HullPointContents (hull_t *hull, int num, vec3_t p); + +/* +=============================================================================== + +HULL BOXES + +=============================================================================== +*/ + + +static hull_t box_hull; +static dclipnode_t box_clipnodes[6]; +static mplane_t box_planes[6]; + +/* +=================== +SV_InitBoxHull + +Set up the planes and clipnodes so that the six floats of a bounding box +can just be stored out and get a proper hull_t structure. +=================== +*/ +void SV_InitBoxHull (void) +{ + int i; + int side; + + box_hull.clipnodes = box_clipnodes; + box_hull.planes = box_planes; + box_hull.firstclipnode = 0; + box_hull.lastclipnode = 5; + + for (i=0 ; i<6 ; i++) + { + box_clipnodes[i].planenum = i; + + side = i&1; + + box_clipnodes[i].children[side] = CONTENTS_EMPTY; + if (i != 5) + box_clipnodes[i].children[side^1] = i + 1; + else + box_clipnodes[i].children[side^1] = CONTENTS_SOLID; + + box_planes[i].type = i>>1; + box_planes[i].normal[i>>1] = 1; + } + +} + + +/* +=================== +SV_HullForBox + +To keep everything totally uniform, bounding boxes are turned into small +BSP trees instead of being compared directly. +=================== +*/ +hull_t *SV_HullForBox (vec3_t mins, vec3_t maxs) +{ + box_planes[0].dist = maxs[0]; + box_planes[1].dist = mins[0]; + box_planes[2].dist = maxs[1]; + box_planes[3].dist = mins[1]; + box_planes[4].dist = maxs[2]; + box_planes[5].dist = mins[2]; + + return &box_hull; +} + + + +/* +================ +SV_HullForEntity + +Returns a hull that can be used for testing or clipping an 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. +================ +*/ +hull_t *SV_HullForEntity (edict_t *ent, vec3_t mins, vec3_t maxs, vec3_t offset) +{ + model_t *model; + vec3_t size; + vec3_t hullmins, hullmaxs; + hull_t *hull; + +// decide which clipping hull to use, based on the size + if (ent->v.solid == SOLID_BSP) + { // explicit hulls in the BSP model + if (ent->v.movetype != MOVETYPE_PUSH) + SV_Error ("SOLID_BSP without MOVETYPE_PUSH"); + + model = sv.models[ (int)ent->v.modelindex ]; + + if (!model || model->type != mod_brush) + SV_Error ("MOVETYPE_PUSH with a non bsp model"); + + VectorSubtract (maxs, mins, size); + if (size[0] < 3) + hull = &model->hulls[0]; + else if (size[0] <= 32) + hull = &model->hulls[1]; + else + hull = &model->hulls[2]; + +// calculate an offset value to center the origin + VectorSubtract (hull->clip_mins, mins, offset); + VectorAdd (offset, ent->v.origin, offset); + } + else + { // create a temp hull from bounding box sizes + + VectorSubtract (ent->v.mins, maxs, hullmins); + VectorSubtract (ent->v.maxs, mins, hullmaxs); + hull = SV_HullForBox (hullmins, hullmaxs); + + VectorCopy (ent->v.origin, offset); + } + + + return hull; +} + +/* +=============================================================================== + +ENTITY AREA CHECKING + +=============================================================================== +*/ + + +areanode_t sv_areanodes[AREA_NODES]; +int sv_numareanodes; + +/* +=============== +SV_CreateAreaNode + +=============== +*/ +areanode_t *SV_CreateAreaNode (int depth, vec3_t mins, vec3_t maxs) +{ + areanode_t *anode; + vec3_t size; + vec3_t mins1, maxs1, mins2, maxs2; + + anode = &sv_areanodes[sv_numareanodes]; + sv_numareanodes++; + + ClearLink (&anode->trigger_edicts); + ClearLink (&anode->solid_edicts); + + if (depth == AREA_DEPTH) + { + anode->axis = -1; + anode->children[0] = anode->children[1] = NULL; + return anode; + } + + VectorSubtract (maxs, mins, size); + if (size[0] > size[1]) + anode->axis = 0; + else + anode->axis = 1; + + anode->dist = 0.5 * (maxs[anode->axis] + mins[anode->axis]); + VectorCopy (mins, mins1); + VectorCopy (mins, mins2); + VectorCopy (maxs, maxs1); + VectorCopy (maxs, maxs2); + + maxs1[anode->axis] = mins2[anode->axis] = anode->dist; + + anode->children[0] = SV_CreateAreaNode (depth+1, mins2, maxs2); + anode->children[1] = SV_CreateAreaNode (depth+1, mins1, maxs1); + + return anode; +} + +/* +=============== +SV_ClearWorld + +=============== +*/ +void SV_ClearWorld (void) +{ + SV_InitBoxHull (); + + memset (sv_areanodes, 0, sizeof(sv_areanodes)); + sv_numareanodes = 0; + SV_CreateAreaNode (0, sv.worldmodel->mins, sv.worldmodel->maxs); +} + + +/* +=============== +SV_UnlinkEdict + +=============== +*/ +void SV_UnlinkEdict (edict_t *ent) +{ + if (!ent->area.prev) + return; // not linked in anywhere + RemoveLink (&ent->area); + ent->area.prev = ent->area.next = NULL; +} + + +/* +==================== +SV_TouchLinks +==================== +*/ +void SV_TouchLinks ( edict_t *ent, areanode_t *node ) +{ + link_t *l, *next; + edict_t *touch; + int old_self, old_other; + +// touch linked edicts + for (l = node->trigger_edicts.next ; l != &node->trigger_edicts ; l = next) + { + next = l->next; + touch = EDICT_FROM_AREA(l); + if (touch == ent) + continue; + if (!touch->v.touch || touch->v.solid != SOLID_TRIGGER) + continue; + if (ent->v.absmin[0] > touch->v.absmax[0] + || ent->v.absmin[1] > touch->v.absmax[1] + || ent->v.absmin[2] > touch->v.absmax[2] + || ent->v.absmax[0] < touch->v.absmin[0] + || ent->v.absmax[1] < touch->v.absmin[1] + || ent->v.absmax[2] < touch->v.absmin[2] ) + continue; + + old_self = pr_global_struct->self; + old_other = pr_global_struct->other; + + pr_global_struct->self = EDICT_TO_PROG(touch); + pr_global_struct->other = EDICT_TO_PROG(ent); + pr_global_struct->time = sv.time; + PR_ExecuteProgram (touch->v.touch); + + pr_global_struct->self = old_self; + pr_global_struct->other = old_other; + } + +// recurse down both sides + if (node->axis == -1) + return; + + if ( ent->v.absmax[node->axis] > node->dist ) + SV_TouchLinks ( ent, node->children[0] ); + if ( ent->v.absmin[node->axis] < node->dist ) + SV_TouchLinks ( ent, node->children[1] ); +} + + +/* +=============== +SV_FindTouchedLeafs + +=============== +*/ +void SV_FindTouchedLeafs (edict_t *ent, mnode_t *node) +{ + mplane_t *splitplane; + mleaf_t *leaf; + int sides; + int leafnum; + + if (node->contents == CONTENTS_SOLID) + return; + +// add an efrag if the node is a leaf + + if ( node->contents < 0) + { + if (ent->num_leafs == MAX_ENT_LEAFS) + return; + + leaf = (mleaf_t *)node; + leafnum = leaf - sv.worldmodel->leafs - 1; + + ent->leafnums[ent->num_leafs] = leafnum; + ent->num_leafs++; + return; + } + +// NODE_MIXED + + splitplane = node->plane; + sides = BOX_ON_PLANE_SIDE(ent->v.absmin, ent->v.absmax, splitplane); + +// recurse down the contacted sides + if (sides & 1) + SV_FindTouchedLeafs (ent, node->children[0]); + + if (sides & 2) + SV_FindTouchedLeafs (ent, node->children[1]); +} + +/* +=============== +SV_LinkEdict + +=============== +*/ +void SV_LinkEdict (edict_t *ent, qboolean touch_triggers) +{ + areanode_t *node; + + if (ent->area.prev) + SV_UnlinkEdict (ent); // unlink from old position + + if (ent == sv.edicts) + return; // don't add the world + + if (ent->free) + return; + +// set the abs box + VectorAdd (ent->v.origin, ent->v.mins, ent->v.absmin); + VectorAdd (ent->v.origin, ent->v.maxs, ent->v.absmax); + +// +// to make items easier to pick up and allow them to be grabbed off +// of shelves, the abs sizes are expanded +// + if ((int)ent->v.flags & FL_ITEM) + { + ent->v.absmin[0] -= 15; + ent->v.absmin[1] -= 15; + ent->v.absmax[0] += 15; + ent->v.absmax[1] += 15; + } + else + { // because movement is clipped an epsilon away from an actual edge, + // we must fully check even when bounding boxes don't quite touch + ent->v.absmin[0] -= 1; + ent->v.absmin[1] -= 1; + ent->v.absmin[2] -= 1; + ent->v.absmax[0] += 1; + ent->v.absmax[1] += 1; + ent->v.absmax[2] += 1; + } + +// link to PVS leafs + ent->num_leafs = 0; + if (ent->v.modelindex) + SV_FindTouchedLeafs (ent, sv.worldmodel->nodes); + + if (ent->v.solid == SOLID_NOT) + return; + +// find the first node that the ent's box crosses + node = sv_areanodes; + while (1) + { + if (node->axis == -1) + break; + if (ent->v.absmin[node->axis] > node->dist) + node = node->children[0]; + else if (ent->v.absmax[node->axis] < node->dist) + node = node->children[1]; + else + break; // crosses the node + } + +// link it in + + if (ent->v.solid == SOLID_TRIGGER) + InsertLinkBefore (&ent->area, &node->trigger_edicts); + else + InsertLinkBefore (&ent->area, &node->solid_edicts); + +// if touch_triggers, touch all entities at this node and decend for more + if (touch_triggers) + SV_TouchLinks ( ent, sv_areanodes ); +} + + + +/* +=============================================================================== + +POINT TESTING IN HULLS + +=============================================================================== +*/ + +#if !id386 + +/* +================== +SV_HullPointContents + +================== +*/ +int SV_HullPointContents (hull_t *hull, int num, vec3_t p) +{ + float d; + dclipnode_t *node; + mplane_t *plane; + + while (num >= 0) + { + if (num < hull->firstclipnode || num > hull->lastclipnode) + SV_Error ("SV_HullPointContents: bad node number"); + + node = hull->clipnodes + num; + plane = hull->planes + node->planenum; + + if (plane->type < 3) + d = p[plane->type] - plane->dist; + else + d = DotProduct (plane->normal, p) - plane->dist; + if (d < 0) + num = node->children[1]; + else + num = node->children[0]; + } + + return num; +} + +#endif // !id386 + + +/* +================== +SV_PointContents + +================== +*/ +int SV_PointContents (vec3_t p) +{ + return SV_HullPointContents (&sv.worldmodel->hulls[0], 0, p); +} + +//=========================================================================== + +/* +============ +SV_TestEntityPosition + +A small wrapper around SV_BoxInSolidEntity that never clips against the +supplied entity. +============ +*/ +edict_t *SV_TestEntityPosition (edict_t *ent) +{ + trace_t trace; + + trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, ent->v.origin, 0, ent); + + if (trace.startsolid) + return sv.edicts; + + return NULL; +} + +/* +=============================================================================== + +LINE TESTING IN HULLS + +=============================================================================== +*/ + +// 1/32 epsilon to keep floating point happy +#define DIST_EPSILON (0.03125) + +/* +================== +SV_RecursiveHullCheck + +================== +*/ +qboolean SV_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, trace_t *trace) +{ + dclipnode_t *node; + mplane_t *plane; + float t1, t2; + float frac; + int i; + vec3_t mid; + int side; + float midf; + +// check for empty + if (num < 0) + { + if (num != CONTENTS_SOLID) + { + trace->allsolid = false; + if (num == CONTENTS_EMPTY) + trace->inopen = true; + else + trace->inwater = true; + } + else + trace->startsolid = true; + return true; // empty + } + + if (num < hull->firstclipnode || num > hull->lastclipnode) + SV_Error ("SV_RecursiveHullCheck: bad node number"); + +// +// find the point distances +// + node = hull->clipnodes + num; + plane = hull->planes + node->planenum; + + if (plane->type < 3) + { + t1 = p1[plane->type] - plane->dist; + t2 = p2[plane->type] - plane->dist; + } + else + { + t1 = DotProduct (plane->normal, p1) - plane->dist; + t2 = DotProduct (plane->normal, p2) - plane->dist; + } + +#if 1 + if (t1 >= 0 && t2 >= 0) + return SV_RecursiveHullCheck (hull, node->children[0], p1f, p2f, p1, p2, trace); + if (t1 < 0 && t2 < 0) + return SV_RecursiveHullCheck (hull, node->children[1], p1f, p2f, p1, p2, trace); +#else + if ( (t1 >= DIST_EPSILON && t2 >= DIST_EPSILON) || (t2 > t1 && t1 >= 0) ) + return SV_RecursiveHullCheck (hull, node->children[0], p1f, p2f, p1, p2, trace); + if ( (t1 <= -DIST_EPSILON && t2 <= -DIST_EPSILON) || (t2 < t1 && t1 <= 0) ) + return SV_RecursiveHullCheck (hull, node->children[1], p1f, p2f, p1, p2, trace); +#endif + +// put the crosspoint DIST_EPSILON pixels on the near side + if (t1 < 0) + frac = (t1 + DIST_EPSILON)/(t1-t2); + else + frac = (t1 - DIST_EPSILON)/(t1-t2); + if (frac < 0) + frac = 0; + if (frac > 1) + frac = 1; + + midf = p1f + (p2f - p1f)*frac; + for (i=0 ; i<3 ; i++) + mid[i] = p1[i] + frac*(p2[i] - p1[i]); + + side = (t1 < 0); + +// move up to the node + if (!SV_RecursiveHullCheck (hull, node->children[side], p1f, midf, p1, mid, trace) ) + return false; + +#ifdef PARANOID + if (SV_HullPointContents (sv_hullmodel, mid, node->children[side]) + == CONTENTS_SOLID) + { + Con_Printf ("mid PointInHullSolid\n"); + return false; + } +#endif + + if (SV_HullPointContents (hull, node->children[side^1], mid) + != CONTENTS_SOLID) +// go past the node + return SV_RecursiveHullCheck (hull, node->children[side^1], midf, p2f, mid, p2, trace); + + if (trace->allsolid) + return false; // never got out of the solid area + +//================== +// the other side of the node is solid, this is the impact point +//================== + if (!side) + { + VectorCopy (plane->normal, trace->plane.normal); + trace->plane.dist = plane->dist; + } + else + { + VectorSubtract (vec3_origin, plane->normal, trace->plane.normal); + trace->plane.dist = -plane->dist; + } + + while (SV_HullPointContents (hull, hull->firstclipnode, mid) + == CONTENTS_SOLID) + { // shouldn't really happen, but does occasionally + frac -= 0.1; + if (frac < 0) + { + trace->fraction = midf; + VectorCopy (mid, trace->endpos); + Con_DPrintf ("backup past 0\n"); // Tonik: was Con_Printf + return false; + } + midf = p1f + (p2f - p1f)*frac; + for (i=0 ; i<3 ; i++) + mid[i] = p1[i] + frac*(p2[i] - p1[i]); + } + + trace->fraction = midf; + VectorCopy (mid, trace->endpos); + + return false; +} + + +/* +================== +SV_ClipMoveToEntity + +Handles selection or creation of a clipping hull, and offseting (and +eventually rotation) of the end points +================== +*/ +trace_t SV_ClipMoveToEntity (edict_t *ent, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end) +{ + trace_t trace; + vec3_t offset; + vec3_t start_l, end_l; + hull_t *hull; + +// fill in a default trace + memset (&trace, 0, sizeof(trace_t)); + trace.fraction = 1; + trace.allsolid = true; + VectorCopy (end, trace.endpos); + +// get the clipping hull + hull = SV_HullForEntity (ent, mins, maxs, offset); + + VectorSubtract (start, offset, start_l); + VectorSubtract (end, offset, end_l); + +// trace a line through the apropriate clipping hull + SV_RecursiveHullCheck (hull, hull->firstclipnode, 0, 1, start_l, end_l, &trace); + +// fix trace up by the offset + if (trace.fraction != 1) + VectorAdd (trace.endpos, offset, trace.endpos); + +// did we clip the move? + if (trace.fraction < 1 || trace.startsolid ) + trace.ent = ent; + + return trace; +} + +//=========================================================================== + +/* +==================== +SV_ClipToLinks + +Mins and maxs enclose the entire area swept by the move +==================== +*/ +void SV_ClipToLinks ( areanode_t *node, moveclip_t *clip ) +{ + link_t *l, *next; + edict_t *touch; + trace_t trace; + +// touch linked edicts + for (l = node->solid_edicts.next ; l != &node->solid_edicts ; l = next) + { + next = l->next; + touch = EDICT_FROM_AREA(l); + if (touch->v.solid == SOLID_NOT) + continue; + if (touch == clip->passedict) + continue; + if (touch->v.solid == SOLID_TRIGGER) + SV_Error ("Trigger in clipping list"); + + if (clip->type == MOVE_NOMONSTERS && touch->v.solid != SOLID_BSP) + continue; + + if (clip->boxmins[0] > touch->v.absmax[0] + || clip->boxmins[1] > touch->v.absmax[1] + || clip->boxmins[2] > touch->v.absmax[2] + || clip->boxmaxs[0] < touch->v.absmin[0] + || clip->boxmaxs[1] < touch->v.absmin[1] + || clip->boxmaxs[2] < touch->v.absmin[2] ) + continue; + + if (clip->passedict && clip->passedict->v.size[0] && !touch->v.size[0]) + continue; // points never interact + + // might intersect, so do an exact clip + if (clip->trace.allsolid) + return; + if (clip->passedict) + { + if (PROG_TO_EDICT(touch->v.owner) == clip->passedict) + continue; // don't clip against own missiles + if (PROG_TO_EDICT(clip->passedict->v.owner) == touch) + continue; // don't clip against owner + } + + if ((int)touch->v.flags & FL_MONSTER) + trace = SV_ClipMoveToEntity (touch, clip->start, clip->mins2, clip->maxs2, clip->end); + else + trace = SV_ClipMoveToEntity (touch, clip->start, clip->mins, clip->maxs, clip->end); + if (trace.allsolid || trace.startsolid || + trace.fraction < clip->trace.fraction) + { + trace.ent = touch; + if (clip->trace.startsolid) + { + clip->trace = trace; + clip->trace.startsolid = true; + } + else + clip->trace = trace; + } + else if (trace.startsolid) + clip->trace.startsolid = true; + } + +// recurse down both sides + if (node->axis == -1) + return; + + if ( clip->boxmaxs[node->axis] > node->dist ) + SV_ClipToLinks ( node->children[0], clip ); + if ( clip->boxmins[node->axis] < node->dist ) + SV_ClipToLinks ( node->children[1], clip ); +} + + +/* +================== +SV_MoveBounds +================== +*/ +void SV_MoveBounds (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, vec3_t boxmins, vec3_t boxmaxs) +{ +#if 0 +// debug to test against everything +boxmins[0] = boxmins[1] = boxmins[2] = -9999; +boxmaxs[0] = boxmaxs[1] = boxmaxs[2] = 9999; +#else + int i; + + for (i=0 ; i<3 ; i++) + { + if (end[i] > start[i]) + { + boxmins[i] = start[i] + mins[i] - 1; + boxmaxs[i] = end[i] + maxs[i] + 1; + } + else + { + boxmins[i] = end[i] + mins[i] - 1; + boxmaxs[i] = start[i] + maxs[i] + 1; + } + } +#endif +} + +/* +================== +SV_Move +================== +*/ +trace_t SV_Move (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int type, edict_t *passedict) +{ + moveclip_t clip; + int i; + + memset ( &clip, 0, sizeof ( moveclip_t ) ); + +// clip to world + clip.trace = SV_ClipMoveToEntity ( sv.edicts, start, mins, maxs, end ); + + clip.start = start; + clip.end = end; + clip.mins = mins; + clip.maxs = maxs; + clip.type = type; + clip.passedict = passedict; + + if (type == MOVE_MISSILE) + { + for (i=0 ; i<3 ; i++) + { + clip.mins2[i] = -15; + clip.maxs2[i] = 15; + } + } + else + { + VectorCopy (mins, clip.mins2); + VectorCopy (maxs, clip.maxs2); + } + +// create the bounding box of the entire move + SV_MoveBounds ( start, clip.mins2, clip.maxs2, end, clip.boxmins, clip.boxmaxs ); + +// clip to entities + SV_ClipToLinks ( sv_areanodes, &clip ); + + return clip.trace; +} + +//============================================================================= + +/* +============ +SV_TestPlayerPosition + +============ +*/ +edict_t *SV_TestPlayerPosition (edict_t *ent, vec3_t origin) +{ + hull_t *hull; + edict_t *check; + vec3_t boxmins, boxmaxs; + vec3_t offset; + int e; + +// check world first + hull = &sv.worldmodel->hulls[1]; + if ( SV_HullPointContents (hull, hull->firstclipnode, origin) != CONTENTS_EMPTY ) + return sv.edicts; + +// check all entities + VectorAdd (origin, ent->v.mins, boxmins); + VectorAdd (origin, ent->v.maxs, boxmaxs); + + check = NEXT_EDICT(sv.edicts); + for (e=1 ; efree) + continue; + if (check->v.solid != SOLID_BSP && + check->v.solid != SOLID_BBOX && + check->v.solid != SOLID_SLIDEBOX) + continue; + + if (boxmins[0] > check->v.absmax[0] + || boxmins[1] > check->v.absmax[1] + || boxmins[2] > check->v.absmax[2] + || boxmaxs[0] < check->v.absmin[0] + || boxmaxs[1] < check->v.absmin[1] + || boxmaxs[2] < check->v.absmin[2] ) + continue; + + if (check == ent) + continue; + + // get the clipping hull + hull = SV_HullForEntity (check, ent->v.mins, ent->v.maxs, offset); + + VectorSubtract (origin, offset, offset); + + // test the point + if ( SV_HullPointContents (hull, hull->firstclipnode, offset) != CONTENTS_EMPTY ) + return check; + } + + return NULL; +} + + diff --git a/source/world.h b/source/world.h index 56acaa28..bc60c864 100644 --- a/source/world.h +++ b/source/world.h @@ -1,93 +1,93 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// world.h - -typedef struct -{ - vec3_t normal; - float dist; -} plane_t; - -typedef struct -{ - qboolean allsolid; // if true, plane is not valid - qboolean startsolid; // if true, the initial point was in a solid area - qboolean inopen, inwater; - float fraction; // time completed, 1.0 = didn't hit anything - vec3_t endpos; // final position - plane_t plane; // surface normal at impact - edict_t *ent; // entity the surface is on -} trace_t; - - -#define MOVE_NORMAL 0 -#define MOVE_NOMONSTERS 1 -#define MOVE_MISSILE 2 - -typedef struct areanode_s -{ - int axis; // -1 = leaf node - float dist; - struct areanode_s *children[2]; - link_t trigger_edicts; - link_t solid_edicts; -} areanode_t; - -#define AREA_DEPTH 4 -#define AREA_NODES 32 - -extern areanode_t sv_areanodes[AREA_NODES]; - - -void SV_ClearWorld (void); -// called after the world model has been loaded, before linking any entities - -void SV_UnlinkEdict (edict_t *ent); -// call before removing an entity, and before trying to move one, -// so it doesn't clip against itself -// flags ent->v.modified - -void SV_LinkEdict (edict_t *ent, qboolean touch_triggers); -// Needs to be called any time an entity changes origin, mins, maxs, or solid -// flags ent->v.modified -// sets ent->v.absmin and ent->v.absmax -// if touchtriggers, calls prog functions for the intersected triggers - -int SV_PointContents (vec3_t p); -// returns the CONTENTS_* value from the world at the given point. -// does not check any entities at all - -edict_t *SV_TestEntityPosition (edict_t *ent); - -trace_t SV_Move (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int type, edict_t *passedict); -// mins and maxs are relative - -// if the entire move stays in a solid volume, trace.allsolid will be set - -// if the starting point is in a solid, it will be allowed to move out -// to an open area - -// nomonsters is used for line of sight or edge testing, where mosnters -// shouldn't be considered solid objects - -// passedict is explicitly excluded from clipping checks (normally NULL) - - -edict_t *SV_TestPlayerPosition (edict_t *ent, vec3_t origin); +/* +Copyright (C) 1996-1997 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. + +*/ +// world.h + +typedef struct +{ + vec3_t normal; + float dist; +} plane_t; + +typedef struct +{ + qboolean allsolid; // if true, plane is not valid + qboolean startsolid; // if true, the initial point was in a solid area + qboolean inopen, inwater; + float fraction; // time completed, 1.0 = didn't hit anything + vec3_t endpos; // final position + plane_t plane; // surface normal at impact + edict_t *ent; // entity the surface is on +} trace_t; + + +#define MOVE_NORMAL 0 +#define MOVE_NOMONSTERS 1 +#define MOVE_MISSILE 2 + +typedef struct areanode_s +{ + int axis; // -1 = leaf node + float dist; + struct areanode_s *children[2]; + link_t trigger_edicts; + link_t solid_edicts; +} areanode_t; + +#define AREA_DEPTH 4 +#define AREA_NODES 32 + +extern areanode_t sv_areanodes[AREA_NODES]; + + +void SV_ClearWorld (void); +// called after the world model has been loaded, before linking any entities + +void SV_UnlinkEdict (edict_t *ent); +// call before removing an entity, and before trying to move one, +// so it doesn't clip against itself +// flags ent->v.modified + +void SV_LinkEdict (edict_t *ent, qboolean touch_triggers); +// Needs to be called any time an entity changes origin, mins, maxs, or solid +// flags ent->v.modified +// sets ent->v.absmin and ent->v.absmax +// if touchtriggers, calls prog functions for the intersected triggers + +int SV_PointContents (vec3_t p); +// returns the CONTENTS_* value from the world at the given point. +// does not check any entities at all + +edict_t *SV_TestEntityPosition (edict_t *ent); + +trace_t SV_Move (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int type, edict_t *passedict); +// mins and maxs are relative + +// if the entire move stays in a solid volume, trace.allsolid will be set + +// if the starting point is in a solid, it will be allowed to move out +// to an open area + +// nomonsters is used for line of sight or edge testing, where mosnters +// shouldn't be considered solid objects + +// passedict is explicitly excluded from clipping checks (normally NULL) + + +edict_t *SV_TestPlayerPosition (edict_t *ent, vec3_t origin); diff --git a/source/worlda.s b/source/worlda.s index 8d657c0e..e0b96852 100644 --- a/source/worlda.s +++ b/source/worlda.s @@ -1,144 +1,144 @@ -/* - worlda.s - - x86 assembly-language server testing stuff - - Copyright (C) 1996-1997 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: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA -*/ - -#include "asm_i386.h" -#include "quakeasm.h" - -#ifdef id386 - - .data - -Ltemp: .long 0 - - .text - -//---------------------------------------------------------------------- -// hull-point test -//---------------------------------------------------------------------- - -#define hull 4+8 // because only partially pushed -#define num 8+4 // because only partially pushed -#define p 12+12 // because only partially pushed - - .align 4 -.globl C(SV_HullPointContents) -C(SV_HullPointContents): - pushl %edi // preserve register variables - movl num(%esp),%eax - testl %eax,%eax - js Lhquickout - -// float d; -// dclipnode_t *node; -// mplane_t *plane; - - pushl %ebx - movl hull(%esp),%ebx - - pushl %ebp - movl p(%esp),%edx - - movl hu_clipnodes(%ebx),%edi - movl hu_planes(%ebx),%ebp - - subl %ebx,%ebx - pushl %esi - -// %ebx: 0 -// %eax: num -// %edx: p -// %edi: hull->clipnodes -// %ebp: hull->planes - -// while (num >= 0) -// { - -Lhloop: - -// node = hull->clipnodes + num; -// plane = hull->planes + node->planenum; -// !!! if the size of dclipnode_t changes, the scaling of %eax needs to be -// changed !!! - movl nd_planenum(%edi,%eax,8),%ecx - movl nd_children(%edi,%eax,8),%eax - movl %eax,%esi - rorl $16,%eax - leal (%ecx,%ecx,4),%ecx - -// if (plane->type < 3) -// d = p[plane->type] - plane->dist; - movb pl_type(%ebp,%ecx,4),%bl - cmpb $3,%bl - jb Lnodot - -// else -// d = DotProduct (plane->normal, p) - plane->dist; - flds pl_normal(%ebp,%ecx,4) - fmuls 0(%edx) - flds pl_normal+4(%ebp,%ecx,4) - fmuls 4(%edx) - flds pl_normal+8(%ebp,%ecx,4) - fmuls 8(%edx) - fxch %st(1) - faddp %st(0),%st(2) - faddp %st(0),%st(1) - fsubs pl_dist(%ebp,%ecx,4) - jmp Lsub - -Lnodot: - flds pl_dist(%ebp,%ecx,4) - fsubrs (%edx,%ebx,4) - -Lsub: - sarl $16,%eax - sarl $16,%esi - -// if (d < 0) -// num = node->children[1]; -// else -// num = node->children[0]; - fstps Ltemp - movl Ltemp,%ecx - sarl $31,%ecx - andl %ecx,%esi - xorl $0xFFFFFFFF,%ecx - andl %ecx,%eax - orl %esi,%eax - jns Lhloop - -// return num; -Lhdone: - popl %esi - popl %ebp - popl %ebx // restore register variables - -Lhquickout: - popl %edi - - ret - -#endif // id386 - +/* + worlda.s + + x86 assembly-language server testing stuff + + Copyright (C) 1996-1997 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA +*/ + +#include "asm_i386.h" +#include "quakeasm.h" + +#ifdef id386 + + .data + +Ltemp: .long 0 + + .text + +//---------------------------------------------------------------------- +// hull-point test +//---------------------------------------------------------------------- + +#define hull 4+8 // because only partially pushed +#define num 8+4 // because only partially pushed +#define p 12+12 // because only partially pushed + + .align 4 +.globl C(SV_HullPointContents) +C(SV_HullPointContents): + pushl %edi // preserve register variables + movl num(%esp),%eax + testl %eax,%eax + js Lhquickout + +// float d; +// dclipnode_t *node; +// mplane_t *plane; + + pushl %ebx + movl hull(%esp),%ebx + + pushl %ebp + movl p(%esp),%edx + + movl hu_clipnodes(%ebx),%edi + movl hu_planes(%ebx),%ebp + + subl %ebx,%ebx + pushl %esi + +// %ebx: 0 +// %eax: num +// %edx: p +// %edi: hull->clipnodes +// %ebp: hull->planes + +// while (num >= 0) +// { + +Lhloop: + +// node = hull->clipnodes + num; +// plane = hull->planes + node->planenum; +// !!! if the size of dclipnode_t changes, the scaling of %eax needs to be +// changed !!! + movl nd_planenum(%edi,%eax,8),%ecx + movl nd_children(%edi,%eax,8),%eax + movl %eax,%esi + rorl $16,%eax + leal (%ecx,%ecx,4),%ecx + +// if (plane->type < 3) +// d = p[plane->type] - plane->dist; + movb pl_type(%ebp,%ecx,4),%bl + cmpb $3,%bl + jb Lnodot + +// else +// d = DotProduct (plane->normal, p) - plane->dist; + flds pl_normal(%ebp,%ecx,4) + fmuls 0(%edx) + flds pl_normal+4(%ebp,%ecx,4) + fmuls 4(%edx) + flds pl_normal+8(%ebp,%ecx,4) + fmuls 8(%edx) + fxch %st(1) + faddp %st(0),%st(2) + faddp %st(0),%st(1) + fsubs pl_dist(%ebp,%ecx,4) + jmp Lsub + +Lnodot: + flds pl_dist(%ebp,%ecx,4) + fsubrs (%edx,%ebx,4) + +Lsub: + sarl $16,%eax + sarl $16,%esi + +// if (d < 0) +// num = node->children[1]; +// else +// num = node->children[0]; + fstps Ltemp + movl Ltemp,%ecx + sarl $31,%ecx + andl %ecx,%esi + xorl $0xFFFFFFFF,%ecx + andl %ecx,%eax + orl %esi,%eax + jns Lhloop + +// return num; +Lhdone: + popl %esi + popl %ebp + popl %ebx // restore register variables + +Lhquickout: + popl %edi + + ret + +#endif // id386 + diff --git a/source/zone.c b/source/zone.c index 706f8c41..679ebd19 100644 --- a/source/zone.c +++ b/source/zone.c @@ -1,960 +1,968 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -// zone.c - -#include "quakedef.h" - -#define DYNAMIC_SIZE 0x20000 - -#define ZONEID 0x1d4a11 -#define MINFRAGMENT 64 - -typedef struct memblock_s -{ - int size; // including the header and possibly tiny fragments - int tag; // a tag of 0 is a free block - int id; // should be ZONEID - struct memblock_s *next, *prev; - int pad; // pad to 64 bit boundary -} memblock_t; - -typedef struct -{ - int size; // total bytes malloced, including header - memblock_t blocklist; // start / end cap for linked list - memblock_t *rover; -} memzone_t; - -void Cache_FreeLow (int new_low_hunk); -void Cache_FreeHigh (int new_high_hunk); - - -/* -=================== -Q_Malloc - -Use it instead of malloc so that if memory allocation fails, -the program exits with a message saying there's not enough memory -instead of crashing after trying to use a NULL pointer -=================== -*/ -void *Q_Malloc (size_t size) -{ - void *p; - - p = malloc(size); - if (!p) - Sys_Error ("Not enough memory free; check disk space"); - - return p; -} - -/* -============================================================================== - - ZONE MEMORY ALLOCATION - -There is never any space between memblocks, and there will never be two -contiguous free memblocks. - -The rover can be left pointing at a non-empty block - -The zone calls are pretty much only used for small strings and structures, -all big things are allocated on the hunk. -============================================================================== -*/ - -memzone_t *mainzone; - -void Z_ClearZone (memzone_t *zone, int size); - - -/* -======================== -Z_ClearZone -======================== -*/ -void Z_ClearZone (memzone_t *zone, int size) -{ - memblock_t *block; - -// set the entire zone to one free block - - zone->blocklist.next = zone->blocklist.prev = block = - (memblock_t *)( (byte *)zone + sizeof(memzone_t) ); - zone->blocklist.tag = 1; // in use block - zone->blocklist.id = 0; - zone->blocklist.size = 0; - zone->rover = block; - - block->prev = block->next = &zone->blocklist; - block->tag = 0; // free block - block->id = ZONEID; - block->size = size - sizeof(memzone_t); -} - - -/* -======================== -Z_Free -======================== -*/ -void Z_Free (void *ptr) -{ - memblock_t *block, *other; - - if (!ptr) - Sys_Error ("Z_Free: NULL pointer"); - - block = (memblock_t *) ( (byte *)ptr - sizeof(memblock_t)); - if (block->id != ZONEID) - Sys_Error ("Z_Free: freed a pointer without ZONEID"); - if (block->tag == 0) - Sys_Error ("Z_Free: freed a freed pointer"); - - block->tag = 0; // mark as free - - other = block->prev; - if (!other->tag) - { // merge with previous free block - other->size += block->size; - other->next = block->next; - other->next->prev = other; - if (block == mainzone->rover) - mainzone->rover = other; - block = other; - } - - other = block->next; - if (!other->tag) - { // merge the next free block onto the end - block->size += other->size; - block->next = other->next; - block->next->prev = block; - if (other == mainzone->rover) - mainzone->rover = block; - } -} - - -/* -======================== -Z_Malloc -======================== -*/ -void *Z_Malloc (int size) -{ - void *buf; - -// Z_CheckHeap (); // DEBUG - buf = Z_TagMalloc (size, 1); - if (!buf) - Sys_Error ("Z_Malloc: failed on allocation of %i bytes",size); - memset (buf, 0, size); - - return buf; -} - -void *Z_TagMalloc (int size, int tag) -{ - int extra; - memblock_t *start, *rover, *new, *base; - - if (!tag) - Sys_Error ("Z_TagMalloc: tried to use a 0 tag"); - -// -// scan through the block list looking for the first free block -// of sufficient size -// - size += sizeof(memblock_t); // account for size of block header - size += 4; // space for memory trash tester - size = (size + 7) & ~7; // align to 8-byte boundary - - base = rover = mainzone->rover; - start = base->prev; - - do - { - if (rover == start) // scaned all the way around the list - return NULL; - if (rover->tag) - base = rover = rover->next; - else - rover = rover->next; - } while (base->tag || base->size < size); - -// -// found a block big enough -// - extra = base->size - size; - if (extra > MINFRAGMENT) - { // there will be a free fragment after the allocated block - new = (memblock_t *) ((byte *)base + size ); - new->size = extra; - new->tag = 0; // free block - new->prev = base; - new->id = ZONEID; - new->next = base->next; - new->next->prev = new; - base->next = new; - base->size = size; - } - - base->tag = tag; // no longer a free block - - mainzone->rover = base->next; // next allocation will start looking here - - base->id = ZONEID; - -// marker for memory trash testing - *(int *)((byte *)base + base->size - 4) = ZONEID; - - return (void *) ((byte *)base + sizeof(memblock_t)); -} - - -/* -======================== -Z_Print -======================== -*/ -void Z_Print (memzone_t *zone) -{ - memblock_t *block; - - Con_Printf ("zone size: %i location: %p\n",mainzone->size,mainzone); - - for (block = zone->blocklist.next ; ; block = block->next) - { - Con_Printf ("block:%p size:%7i tag:%3i\n", - block, block->size, block->tag); - - if (block->next == &zone->blocklist) - break; // all blocks have been hit - if ( (byte *)block + block->size != (byte *)block->next) - Con_Printf ("ERROR: block size does not touch the next block\n"); - if ( block->next->prev != block) - Con_Printf ("ERROR: next block doesn't have proper back link\n"); - if (!block->tag && !block->next->tag) - Con_Printf ("ERROR: two consecutive free blocks\n"); - } -} - - -/* -======================== -Z_CheckHeap -======================== -*/ -void Z_CheckHeap (void) -{ - memblock_t *block; - - for (block = mainzone->blocklist.next ; ; block = block->next) - { - if (block->next == &mainzone->blocklist) - break; // all blocks have been hit - if ( (byte *)block + block->size != (byte *)block->next) - Sys_Error ("Z_CheckHeap: block size does not touch the next block\n"); - if ( block->next->prev != block) - Sys_Error ("Z_CheckHeap: next block doesn't have proper back link\n"); - if (!block->tag && !block->next->tag) - Sys_Error ("Z_CheckHeap: two consecutive free blocks\n"); - } -} - -//============================================================================ - -#define HUNK_SENTINAL 0x1df001ed - -typedef struct -{ - int sentinal; - int size; // including sizeof(hunk_t), -1 = not allocated - char name[8]; -} hunk_t; - -byte *hunk_base; -int hunk_size; - -int hunk_low_used; -int hunk_high_used; - -qboolean hunk_tempactive; -int hunk_tempmark; - -void R_FreeTextures (void); - -/* -============== -Hunk_Check - -Run consistancy and sentinal trahing checks -============== -*/ -void Hunk_Check (void) -{ - hunk_t *h; - - for (h = (hunk_t *)hunk_base ; (byte *)h != hunk_base + hunk_low_used ; ) - { - if (h->sentinal != HUNK_SENTINAL) - Sys_Error ("Hunk_Check: trahsed sentinal"); - if (h->size < 16 || h->size + (byte *)h - hunk_base > hunk_size) - Sys_Error ("Hunk_Check: bad size"); - h = (hunk_t *)((byte *)h+h->size); - } -} - -/* -============== -Hunk_Print - -If "all" is specified, every single allocation is printed. -Otherwise, allocations with the same name will be totaled up before printing. -============== -*/ -void Hunk_Print (qboolean all) -{ - hunk_t *h, *next, *endlow, *starthigh, *endhigh; - int count, sum; - int totalblocks; - char name[9]; - - name[8] = 0; - count = 0; - sum = 0; - totalblocks = 0; - - h = (hunk_t *)hunk_base; - endlow = (hunk_t *)(hunk_base + hunk_low_used); - starthigh = (hunk_t *)(hunk_base + hunk_size - hunk_high_used); - endhigh = (hunk_t *)(hunk_base + hunk_size); - - Con_Printf (" :%8i total hunk size\n", hunk_size); - Con_Printf ("-------------------------\n"); - - while (1) - { - // - // skip to the high hunk if done with low hunk - // - if ( h == endlow ) - { - Con_Printf ("-------------------------\n"); - Con_Printf (" :%8i REMAINING\n", hunk_size - hunk_low_used - hunk_high_used); - Con_Printf ("-------------------------\n"); - h = starthigh; - } - - // - // if totally done, break - // - if ( h == endhigh ) - break; - - // - // run consistancy checks - // - if (h->sentinal != HUNK_SENTINAL) - Sys_Error ("Hunk_Check: trahsed sentinal"); - if (h->size < 16 || h->size + (byte *)h - hunk_base > hunk_size) - Sys_Error ("Hunk_Check: bad size"); - - next = (hunk_t *)((byte *)h+h->size); - count++; - totalblocks++; - sum += h->size; - - // - // print the single block - // - memcpy (name, h->name, 8); - if (all) - Con_Printf ("%8p :%8i %8s\n",h, h->size, name); - - // - // print the total - // - if (next == endlow || next == endhigh || - strncmp (h->name, next->name, 8) ) - { - if (!all) - Con_Printf (" :%8i %8s (TOTAL)\n",sum, name); - count = 0; - sum = 0; - } - - h = next; - } - - Con_Printf ("-------------------------\n"); - Con_Printf ("%8i total blocks\n", totalblocks); - -} - -/* -=================== -Hunk_AllocName -=================== -*/ -void *Hunk_AllocName (int size, char *name) -{ - hunk_t *h; - -#ifdef PARANOID - Hunk_Check (); -#endif - - if (size < 0) - Sys_Error ("Hunk_Alloc: bad size: %i", size); - - size = sizeof(hunk_t) + ((size+15)&~15); - - if (hunk_size - hunk_low_used - hunk_high_used < size) -// Sys_Error ("Hunk_Alloc: failed on %i bytes",size); -#ifdef _WIN32 - Sys_Error ("Not enough RAM allocated. Try starting using \"-heapsize 16000\" on the QuakeWorld command line."); -#else - Sys_Error ("Not enough RAM allocated. Try starting using \"-mem 16\" on the QuakeWorld command line."); -#endif - - h = (hunk_t *)(hunk_base + hunk_low_used); - hunk_low_used += size; - - Cache_FreeLow (hunk_low_used); - - memset (h, 0, size); - - h->size = size; - h->sentinal = HUNK_SENTINAL; - strncpy (h->name, name, 8); - - return (void *)(h+1); -} - -/* -=================== -Hunk_Alloc -=================== -*/ -void *Hunk_Alloc (int size) -{ - return Hunk_AllocName (size, "unknown"); -} - -int Hunk_LowMark (void) -{ - return hunk_low_used; -} - -void Hunk_FreeToLowMark (int mark) -{ - if (mark < 0 || mark > hunk_low_used) - Sys_Error ("Hunk_FreeToLowMark: bad mark %i", mark); - memset (hunk_base + mark, 0, hunk_low_used - mark); - hunk_low_used = mark; -} - -int Hunk_HighMark (void) -{ - if (hunk_tempactive) - { - hunk_tempactive = false; - Hunk_FreeToHighMark (hunk_tempmark); - } - - return hunk_high_used; -} - -void Hunk_FreeToHighMark (int mark) -{ - if (hunk_tempactive) - { - hunk_tempactive = false; - Hunk_FreeToHighMark (hunk_tempmark); - } - if (mark < 0 || mark > hunk_high_used) - Sys_Error ("Hunk_FreeToHighMark: bad mark %i", mark); - memset (hunk_base + hunk_size - hunk_high_used, 0, hunk_high_used - mark); - hunk_high_used = mark; -} - - -/* -=================== -Hunk_HighAllocName -=================== -*/ -void *Hunk_HighAllocName (int size, char *name) -{ - hunk_t *h; - - if (size < 0) - Sys_Error ("Hunk_HighAllocName: bad size: %i", size); - - if (hunk_tempactive) - { - Hunk_FreeToHighMark (hunk_tempmark); - hunk_tempactive = false; - } - -#ifdef PARANOID - Hunk_Check (); -#endif - - size = sizeof(hunk_t) + ((size+15)&~15); - - if (hunk_size - hunk_low_used - hunk_high_used < size) - { - Con_Printf ("Hunk_HighAlloc: failed on %i bytes\n",size); - return NULL; - } - - hunk_high_used += size; - Cache_FreeHigh (hunk_high_used); - - h = (hunk_t *)(hunk_base + hunk_size - hunk_high_used); - - memset (h, 0, size); - h->size = size; - h->sentinal = HUNK_SENTINAL; - strncpy (h->name, name, 8); - - return (void *)(h+1); -} - - -/* -================= -Hunk_TempAlloc - -Return space from the top of the hunk -================= -*/ -void *Hunk_TempAlloc (int size) -{ - void *buf; - - size = (size+15)&~15; - - if (hunk_tempactive) - { - Hunk_FreeToHighMark (hunk_tempmark); - hunk_tempactive = false; - } - - hunk_tempmark = Hunk_HighMark (); - - buf = Hunk_HighAllocName (size, "temp"); - - hunk_tempactive = true; - - return buf; -} - -/* -=============================================================================== - -CACHE MEMORY - -=============================================================================== -*/ - -typedef struct cache_system_s -{ - int size; // including this header - cache_user_t *user; - char name[16]; - struct cache_system_s *prev, *next; - struct cache_system_s *lru_prev, *lru_next; // for LRU flushing -} cache_system_t; - -cache_system_t *Cache_TryAlloc (int size, qboolean nobottom); - -cache_system_t cache_head; - -/* -=========== -Cache_Move -=========== -*/ -void Cache_Move ( cache_system_t *c) -{ - cache_system_t *new; - -// we are clearing up space at the bottom, so only allocate it late - new = Cache_TryAlloc (c->size, true); - if (new) - { -// Con_Printf ("cache_move ok\n"); - - memcpy ( new+1, c+1, c->size - sizeof(cache_system_t) ); - new->user = c->user; - memcpy (new->name, c->name, sizeof(new->name)); - Cache_Free (c->user); - new->user->data = (void *)(new+1); - } - else - { -// Con_Printf ("cache_move failed\n"); - - Cache_Free (c->user); // tough luck... - } -} - -/* -============ -Cache_FreeLow - -Throw things out until the hunk can be expanded to the given point -============ -*/ -void Cache_FreeLow (int new_low_hunk) -{ - cache_system_t *c; - - while (1) - { - c = cache_head.next; - if (c == &cache_head) - return; // nothing in cache at all - if ((byte *)c >= hunk_base + new_low_hunk) - return; // there is space to grow the hunk - Cache_Move ( c ); // reclaim the space - } -} - -/* -============ -Cache_FreeHigh - -Throw things out until the hunk can be expanded to the given point -============ -*/ -void Cache_FreeHigh (int new_high_hunk) -{ - cache_system_t *c, *prev; - - prev = NULL; - while (1) - { - c = cache_head.prev; - if (c == &cache_head) - return; // nothing in cache at all - if ( (byte *)c + c->size <= hunk_base + hunk_size - new_high_hunk) - return; // there is space to grow the hunk - if (c == prev) - Cache_Free (c->user); // didn't move out of the way - else - { - Cache_Move (c); // try to move it - prev = c; - } - } -} - -void Cache_UnlinkLRU (cache_system_t *cs) -{ - if (!cs->lru_next || !cs->lru_prev) - Sys_Error ("Cache_UnlinkLRU: NULL link"); - - cs->lru_next->lru_prev = cs->lru_prev; - cs->lru_prev->lru_next = cs->lru_next; - - cs->lru_prev = cs->lru_next = NULL; -} - -void Cache_MakeLRU (cache_system_t *cs) -{ - if (cs->lru_next || cs->lru_prev) - Sys_Error ("Cache_MakeLRU: active link"); - - cache_head.lru_next->lru_prev = cs; - cs->lru_next = cache_head.lru_next; - cs->lru_prev = &cache_head; - cache_head.lru_next = cs; -} - -/* -============ -Cache_TryAlloc - -Looks for a free block of memory between the high and low hunk marks -Size should already include the header and padding -============ -*/ -cache_system_t *Cache_TryAlloc (int size, qboolean nobottom) -{ - cache_system_t *cs, *new; - -// is the cache completely empty? - - if (!nobottom && cache_head.prev == &cache_head) - { - if (hunk_size - hunk_high_used - hunk_low_used < size) - Sys_Error ("Cache_TryAlloc: %i is greater then free hunk", size); - - new = (cache_system_t *) (hunk_base + hunk_low_used); - memset (new, 0, sizeof(*new)); - new->size = size; - - cache_head.prev = cache_head.next = new; - new->prev = new->next = &cache_head; - - Cache_MakeLRU (new); - return new; - } - -// search from the bottom up for space - - new = (cache_system_t *) (hunk_base + hunk_low_used); - cs = cache_head.next; - - do - { - if (!nobottom || cs != cache_head.next) - { - if ( (byte *)cs - (byte *)new >= size) - { // found space - memset (new, 0, sizeof(*new)); - new->size = size; - - new->next = cs; - new->prev = cs->prev; - cs->prev->next = new; - cs->prev = new; - - Cache_MakeLRU (new); - - return new; - } - } - - // continue looking - new = (cache_system_t *)((byte *)cs + cs->size); - cs = cs->next; - - } while (cs != &cache_head); - -// try to allocate one at the very end - if ( hunk_base + hunk_size - hunk_high_used - (byte *)new >= size) - { - memset (new, 0, sizeof(*new)); - new->size = size; - - new->next = &cache_head; - new->prev = cache_head.prev; - cache_head.prev->next = new; - cache_head.prev = new; - - Cache_MakeLRU (new); - - return new; - } - - return NULL; // couldn't allocate -} - -/* -============ -Cache_Flush - -Throw everything out, so new data will be demand cached -============ -*/ -void Cache_Flush (void) -{ - while (cache_head.next != &cache_head) - Cache_Free ( cache_head.next->user ); // reclaim the space -} - - -/* -============ -Cache_Print - -============ -*/ -void Cache_Print (void) -{ - cache_system_t *cd; - - for (cd = cache_head.next ; cd != &cache_head ; cd = cd->next) - { - Con_Printf ("%8i : %s\n", cd->size, cd->name); - } -} - -/* -============ -Cache_Report - -============ -*/ -void Cache_Report (void) -{ - Con_DPrintf ("%4.1f megabyte data cache\n", (hunk_size - hunk_high_used - hunk_low_used) / (float)(1024*1024) ); -} - -/* -============ -Cache_Compact - -============ -*/ -void Cache_Compact (void) -{ -} - -/* -============ -Cache_Init - -============ -*/ -void Cache_Init (void) -{ - cache_head.next = cache_head.prev = &cache_head; - cache_head.lru_next = cache_head.lru_prev = &cache_head; - - Cmd_AddCommand ("flush", Cache_Flush); -} - -/* -============== -Cache_Free - -Frees the memory and removes it from the LRU list -============== -*/ -void Cache_Free (cache_user_t *c) -{ - cache_system_t *cs; - - if (!c->data) - Sys_Error ("Cache_Free: not allocated"); - - cs = ((cache_system_t *)c->data) - 1; - - cs->prev->next = cs->next; - cs->next->prev = cs->prev; - cs->next = cs->prev = NULL; - - c->data = NULL; - - Cache_UnlinkLRU (cs); -} - - - -/* -============== -Cache_Check -============== -*/ -void *Cache_Check (cache_user_t *c) -{ - cache_system_t *cs; - - if (!c->data) - return NULL; - - cs = ((cache_system_t *)c->data) - 1; - -// move to head of LRU - Cache_UnlinkLRU (cs); - Cache_MakeLRU (cs); - - return c->data; -} - - -/* -============== -Cache_Alloc -============== -*/ -void *Cache_Alloc (cache_user_t *c, int size, char *name) -{ - cache_system_t *cs; - - if (c->data) - Sys_Error ("Cache_Alloc: already allocated"); - - if (size <= 0) - Sys_Error ("Cache_Alloc: size %i", size); - - size = (size + sizeof(cache_system_t) + 15) & ~15; - -// find memory for it - while (1) - { - cs = Cache_TryAlloc (size, false); - if (cs) - { - strncpy (cs->name, name, sizeof(cs->name)-1); - c->data = (void *)(cs+1); - cs->user = c; - break; - } - - // free the least recently used cahedat - if (cache_head.lru_prev == &cache_head) - Sys_Error ("Cache_Alloc: out of memory"); - // not enough memory at all - Cache_Free ( cache_head.lru_prev->user ); - } - - return Cache_Check (c); -} - -//============================================================================ - - -/* -======================== -Memory_Init -======================== -*/ -void Memory_Init (void *buf, int size) -{ - int p; - int zonesize = DYNAMIC_SIZE; - - hunk_base = buf; - hunk_size = size; - hunk_low_used = 0; - hunk_high_used = 0; - - Cache_Init (); - p = COM_CheckParm ("-zone"); - if (p) - { - if (p < com_argc-1) - zonesize = Q_atoi (com_argv[p+1]) * 1024; - else - Sys_Error ("Memory_Init: you must specify a size in KB after -zone"); - } - mainzone = Hunk_AllocName ( zonesize, "zone" ); - Z_ClearZone (mainzone, zonesize); -} - +/* +Copyright (C) 1996-1997 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. + +*/ +// zone.c + +#include "quakedef.h" + +#define DYNAMIC_SIZE 0x20000 + +#define ZONEID 0x1d4a11 +#define MINFRAGMENT 64 + +typedef struct memblock_s +{ + int size; // including the header and possibly tiny fragments + int tag; // a tag of 0 is a free block + int id; // should be ZONEID + struct memblock_s *next, *prev; + int pad; // pad to 64 bit boundary +} memblock_t; + +typedef struct +{ + int size; // total bytes malloced, including header + memblock_t blocklist; // start / end cap for linked list + memblock_t *rover; +} memzone_t; + +void Cache_FreeLow (int new_low_hunk); +void Cache_FreeHigh (int new_high_hunk); + + +/* +=================== +Q_Malloc + +Use it instead of malloc so that if memory allocation fails, +the program exits with a message saying there's not enough memory +instead of crashing after trying to use a NULL pointer +=================== +*/ +void *Q_Malloc (size_t size) +{ + void *p; + + p = malloc(size); + if (!p) + Sys_Error ("Not enough memory free; check disk space"); + + return p; +} + +/* +============================================================================== + + ZONE MEMORY ALLOCATION + +There is never any space between memblocks, and there will never be two +contiguous free memblocks. + +The rover can be left pointing at a non-empty block + +The zone calls are pretty much only used for small strings and structures, +all big things are allocated on the hunk. +============================================================================== +*/ + +memzone_t *mainzone; + +void Z_ClearZone (memzone_t *zone, int size); + + +/* +======================== +Z_ClearZone +======================== +*/ +void Z_ClearZone (memzone_t *zone, int size) +{ + memblock_t *block; + +// set the entire zone to one free block + + zone->blocklist.next = zone->blocklist.prev = block = + (memblock_t *)( (byte *)zone + sizeof(memzone_t) ); + zone->blocklist.tag = 1; // in use block + zone->blocklist.id = 0; + zone->blocklist.size = 0; + zone->rover = block; + + block->prev = block->next = &zone->blocklist; + block->tag = 0; // free block + block->id = ZONEID; + block->size = size - sizeof(memzone_t); +} + + +/* +======================== +Z_Free +======================== +*/ +void Z_Free (void *ptr) +{ + memblock_t *block, *other; + + if (!ptr) + Sys_Error ("Z_Free: NULL pointer"); + + block = (memblock_t *) ( (byte *)ptr - sizeof(memblock_t)); + if (block->id != ZONEID) + Sys_Error ("Z_Free: freed a pointer without ZONEID"); + if (block->tag == 0) + Sys_Error ("Z_Free: freed a freed pointer"); + + block->tag = 0; // mark as free + + other = block->prev; + if (!other->tag) + { // merge with previous free block + other->size += block->size; + other->next = block->next; + other->next->prev = other; + if (block == mainzone->rover) + mainzone->rover = other; + block = other; + } + + other = block->next; + if (!other->tag) + { // merge the next free block onto the end + block->size += other->size; + block->next = other->next; + block->next->prev = block; + if (other == mainzone->rover) + mainzone->rover = block; + } +} + + +/* +======================== +Z_Malloc +======================== +*/ +void *Z_Malloc (int size) +{ + void *buf; + +// Z_CheckHeap (); // DEBUG + buf = Z_TagMalloc (size, 1); + if (!buf) + Sys_Error ("Z_Malloc: failed on allocation of %i bytes",size); + memset (buf, 0, size); + + return buf; +} + +void *Z_TagMalloc (int size, int tag) +{ + int extra; + memblock_t *start, *rover, *new, *base; + + if (!tag) + Sys_Error ("Z_TagMalloc: tried to use a 0 tag"); + +// +// scan through the block list looking for the first free block +// of sufficient size +// + size += sizeof(memblock_t); // account for size of block header + size += 4; // space for memory trash tester + size = (size + 7) & ~7; // align to 8-byte boundary + + base = rover = mainzone->rover; + start = base->prev; + + do + { + if (rover == start) // scaned all the way around the list + return NULL; + if (rover->tag) + base = rover = rover->next; + else + rover = rover->next; + } while (base->tag || base->size < size); + +// +// found a block big enough +// + extra = base->size - size; + if (extra > MINFRAGMENT) + { // there will be a free fragment after the allocated block + new = (memblock_t *) ((byte *)base + size ); + new->size = extra; + new->tag = 0; // free block + new->prev = base; + new->id = ZONEID; + new->next = base->next; + new->next->prev = new; + base->next = new; + base->size = size; + } + + base->tag = tag; // no longer a free block + + mainzone->rover = base->next; // next allocation will start looking here + + base->id = ZONEID; + +// marker for memory trash testing + *(int *)((byte *)base + base->size - 4) = ZONEID; + + return (void *) ((byte *)base + sizeof(memblock_t)); +} + + +/* +======================== +Z_Print +======================== +*/ +void Z_Print (memzone_t *zone) +{ + memblock_t *block; + + Con_Printf ("zone size: %i location: %p\n",mainzone->size,mainzone); + + for (block = zone->blocklist.next ; ; block = block->next) + { + Con_Printf ("block:%p size:%7i tag:%3i\n", + block, block->size, block->tag); + + if (block->next == &zone->blocklist) + break; // all blocks have been hit + if ( (byte *)block + block->size != (byte *)block->next) + Con_Printf ("ERROR: block size does not touch the next block\n"); + if ( block->next->prev != block) + Con_Printf ("ERROR: next block doesn't have proper back link\n"); + if (!block->tag && !block->next->tag) + Con_Printf ("ERROR: two consecutive free blocks\n"); + } +} + + +/* +======================== +Z_CheckHeap +======================== +*/ +void Z_CheckHeap (void) +{ + memblock_t *block; + + for (block = mainzone->blocklist.next ; ; block = block->next) + { + if (block->next == &mainzone->blocklist) + break; // all blocks have been hit + if ( (byte *)block + block->size != (byte *)block->next) + Sys_Error ("Z_CheckHeap: block size does not touch the next block\n"); + if ( block->next->prev != block) + Sys_Error ("Z_CheckHeap: next block doesn't have proper back link\n"); + if (!block->tag && !block->next->tag) + Sys_Error ("Z_CheckHeap: two consecutive free blocks\n"); + } +} + +//============================================================================ + +#define HUNK_SENTINAL 0x1df001ed + +typedef struct +{ + int sentinal; + int size; // including sizeof(hunk_t), -1 = not allocated + char name[8]; +} hunk_t; + +byte *hunk_base; +int hunk_size; + +int hunk_low_used; +int hunk_high_used; + +qboolean hunk_tempactive; +int hunk_tempmark; + +void R_FreeTextures (void); + +/* +============== +Hunk_Check + +Run consistancy and sentinal trahing checks +============== +*/ +void Hunk_Check (void) +{ + hunk_t *h; + + for (h = (hunk_t *)hunk_base ; (byte *)h != hunk_base + hunk_low_used ; ) + { + if (h->sentinal != HUNK_SENTINAL) + Sys_Error ("Hunk_Check: trahsed sentinal"); + if (h->size < 16 || h->size + (byte *)h - hunk_base > hunk_size) + Sys_Error ("Hunk_Check: bad size"); + h = (hunk_t *)((byte *)h+h->size); + } +} + +/* +============== +Hunk_Print + +If "all" is specified, every single allocation is printed. +Otherwise, allocations with the same name will be totaled up before printing. +============== +*/ +void Hunk_Print (qboolean all) +{ + hunk_t *h, *next, *endlow, *starthigh, *endhigh; + int count, sum; + int totalblocks; + char name[9]; + + name[8] = 0; + count = 0; + sum = 0; + totalblocks = 0; + + h = (hunk_t *)hunk_base; + endlow = (hunk_t *)(hunk_base + hunk_low_used); + starthigh = (hunk_t *)(hunk_base + hunk_size - hunk_high_used); + endhigh = (hunk_t *)(hunk_base + hunk_size); + + Con_Printf (" :%8i total hunk size\n", hunk_size); + Con_Printf ("-------------------------\n"); + + while (1) + { + // + // skip to the high hunk if done with low hunk + // + if ( h == endlow ) + { + Con_Printf ("-------------------------\n"); + Con_Printf (" :%8i REMAINING\n", hunk_size - hunk_low_used - hunk_high_used); + Con_Printf ("-------------------------\n"); + h = starthigh; + } + + // + // if totally done, break + // + if ( h == endhigh ) + break; + + // + // run consistancy checks + // + if (h->sentinal != HUNK_SENTINAL) + Sys_Error ("Hunk_Check: trahsed sentinal"); + if (h->size < 16 || h->size + (byte *)h - hunk_base > hunk_size) + Sys_Error ("Hunk_Check: bad size"); + + next = (hunk_t *)((byte *)h+h->size); + count++; + totalblocks++; + sum += h->size; + + // + // print the single block + // + memcpy (name, h->name, 8); + if (all) + Con_Printf ("%8p :%8i %8s\n",h, h->size, name); + + // + // print the total + // + if (next == endlow || next == endhigh || + strncmp (h->name, next->name, 8) ) + { + if (!all) + Con_Printf (" :%8i %8s (TOTAL)\n",sum, name); + count = 0; + sum = 0; + } + + h = next; + } + + Con_Printf ("-------------------------\n"); + Con_Printf ("%8i total blocks\n", totalblocks); + +} + +/* +=================== +Hunk_AllocName +=================== +*/ + +void *Hunk_AllocName_f (int size, char *name, qboolean clean) +{ + hunk_t *h; + +#ifdef PARANOID + Hunk_Check (); +#endif + + if (size < 0) + Sys_Error ("Hunk_Alloc: bad size: %i", size); + + size = sizeof(hunk_t) + ((size+15)&~15); + + if (hunk_size - hunk_low_used - hunk_high_used < size) +// Sys_Error ("Hunk_Alloc: failed on %i bytes",size); +#ifdef _WIN32 + Sys_Error ("Not enough RAM allocated. Try starting using \"-heapsize 16000\" on the QuakeWorld command line."); +#else + Sys_Error ("Not enough RAM allocated. Try starting using \"-mem 16\" on the QuakeWorld command line."); +#endif + + h = (hunk_t *)(hunk_base + hunk_low_used); + hunk_low_used += size; + + Cache_FreeLow (hunk_low_used); + + if (clean) + memset (h, 0, size); + + h->size = size; + h->sentinal = HUNK_SENTINAL; + strncpy (h->name, name, 8); + + return (void *)(h+1); +} + +void *Hunk_AllocName (int size, char *name) +{ + return Hunk_AllocName_f(size, name, true); +} + + +/* +=================== +Hunk_Alloc +=================== +*/ +void *Hunk_Alloc (int size) +{ + return Hunk_AllocName (size, "unknown"); +} + +int Hunk_LowMark (void) +{ + return hunk_low_used; +} + +void Hunk_FreeToLowMark (int mark) +{ + if (mark < 0 || mark > hunk_low_used) + Sys_Error ("Hunk_FreeToLowMark: bad mark %i", mark); + memset (hunk_base + mark, 0, hunk_low_used - mark); + hunk_low_used = mark; +} + +int Hunk_HighMark (void) +{ + if (hunk_tempactive) + { + hunk_tempactive = false; + Hunk_FreeToHighMark (hunk_tempmark); + } + + return hunk_high_used; +} + +void Hunk_FreeToHighMark (int mark) +{ + if (hunk_tempactive) + { + hunk_tempactive = false; + Hunk_FreeToHighMark (hunk_tempmark); + } + if (mark < 0 || mark > hunk_high_used) + Sys_Error ("Hunk_FreeToHighMark: bad mark %i", mark); + memset (hunk_base + hunk_size - hunk_high_used, 0, hunk_high_used - mark); + hunk_high_used = mark; +} + + +/* +=================== +Hunk_HighAllocName +=================== +*/ +void *Hunk_HighAllocName (int size, char *name) +{ + hunk_t *h; + + if (size < 0) + Sys_Error ("Hunk_HighAllocName: bad size: %i", size); + + if (hunk_tempactive) + { + Hunk_FreeToHighMark (hunk_tempmark); + hunk_tempactive = false; + } + +#ifdef PARANOID + Hunk_Check (); +#endif + + size = sizeof(hunk_t) + ((size+15)&~15); + + if (hunk_size - hunk_low_used - hunk_high_used < size) + { + Con_Printf ("Hunk_HighAlloc: failed on %i bytes\n",size); + return NULL; + } + + hunk_high_used += size; + Cache_FreeHigh (hunk_high_used); + + h = (hunk_t *)(hunk_base + hunk_size - hunk_high_used); + + memset (h, 0, size); + h->size = size; + h->sentinal = HUNK_SENTINAL; + strncpy (h->name, name, 8); + + return (void *)(h+1); +} + + +/* +================= +Hunk_TempAlloc + +Return space from the top of the hunk +================= +*/ +void *Hunk_TempAlloc (int size) +{ + void *buf; + + size = (size+15)&~15; + + if (hunk_tempactive) + { + Hunk_FreeToHighMark (hunk_tempmark); + hunk_tempactive = false; + } + + hunk_tempmark = Hunk_HighMark (); + + buf = Hunk_HighAllocName (size, "temp"); + + hunk_tempactive = true; + + return buf; +} + +/* +=============================================================================== + +CACHE MEMORY + +=============================================================================== +*/ + +typedef struct cache_system_s +{ + int size; // including this header + cache_user_t *user; + char name[16]; + struct cache_system_s *prev, *next; + struct cache_system_s *lru_prev, *lru_next; // for LRU flushing +} cache_system_t; + +cache_system_t *Cache_TryAlloc (int size, qboolean nobottom); + +cache_system_t cache_head; + +/* +=========== +Cache_Move +=========== +*/ +void Cache_Move ( cache_system_t *c) +{ + cache_system_t *new; + +// we are clearing up space at the bottom, so only allocate it late + new = Cache_TryAlloc (c->size, true); + if (new) + { +// Con_Printf ("cache_move ok\n"); + + memcpy ( new+1, c+1, c->size - sizeof(cache_system_t) ); + new->user = c->user; + memcpy (new->name, c->name, sizeof(new->name)); + Cache_Free (c->user); + new->user->data = (void *)(new+1); + } + else + { +// Con_Printf ("cache_move failed\n"); + + Cache_Free (c->user); // tough luck... + } +} + +/* +============ +Cache_FreeLow + +Throw things out until the hunk can be expanded to the given point +============ +*/ +void Cache_FreeLow (int new_low_hunk) +{ + cache_system_t *c; + + while (1) + { + c = cache_head.next; + if (c == &cache_head) + return; // nothing in cache at all + if ((byte *)c >= hunk_base + new_low_hunk) + return; // there is space to grow the hunk + Cache_Move ( c ); // reclaim the space + } +} + +/* +============ +Cache_FreeHigh + +Throw things out until the hunk can be expanded to the given point +============ +*/ +void Cache_FreeHigh (int new_high_hunk) +{ + cache_system_t *c, *prev; + + prev = NULL; + while (1) + { + c = cache_head.prev; + if (c == &cache_head) + return; // nothing in cache at all + if ( (byte *)c + c->size <= hunk_base + hunk_size - new_high_hunk) + return; // there is space to grow the hunk + if (c == prev) + Cache_Free (c->user); // didn't move out of the way + else + { + Cache_Move (c); // try to move it + prev = c; + } + } +} + +void Cache_UnlinkLRU (cache_system_t *cs) +{ + if (!cs->lru_next || !cs->lru_prev) + Sys_Error ("Cache_UnlinkLRU: NULL link"); + + cs->lru_next->lru_prev = cs->lru_prev; + cs->lru_prev->lru_next = cs->lru_next; + + cs->lru_prev = cs->lru_next = NULL; +} + +void Cache_MakeLRU (cache_system_t *cs) +{ + if (cs->lru_next || cs->lru_prev) + Sys_Error ("Cache_MakeLRU: active link"); + + cache_head.lru_next->lru_prev = cs; + cs->lru_next = cache_head.lru_next; + cs->lru_prev = &cache_head; + cache_head.lru_next = cs; +} + +/* +============ +Cache_TryAlloc + +Looks for a free block of memory between the high and low hunk marks +Size should already include the header and padding +============ +*/ +cache_system_t *Cache_TryAlloc (int size, qboolean nobottom) +{ + cache_system_t *cs, *new; + +// is the cache completely empty? + + if (!nobottom && cache_head.prev == &cache_head) + { + if (hunk_size - hunk_high_used - hunk_low_used < size) + Sys_Error ("Cache_TryAlloc: %i is greater then free hunk", size); + + new = (cache_system_t *) (hunk_base + hunk_low_used); + memset (new, 0, sizeof(*new)); + new->size = size; + + cache_head.prev = cache_head.next = new; + new->prev = new->next = &cache_head; + + Cache_MakeLRU (new); + return new; + } + +// search from the bottom up for space + + new = (cache_system_t *) (hunk_base + hunk_low_used); + cs = cache_head.next; + + do + { + if (!nobottom || cs != cache_head.next) + { + if ( (byte *)cs - (byte *)new >= size) + { // found space + memset (new, 0, sizeof(*new)); + new->size = size; + + new->next = cs; + new->prev = cs->prev; + cs->prev->next = new; + cs->prev = new; + + Cache_MakeLRU (new); + + return new; + } + } + + // continue looking + new = (cache_system_t *)((byte *)cs + cs->size); + cs = cs->next; + + } while (cs != &cache_head); + +// try to allocate one at the very end + if ( hunk_base + hunk_size - hunk_high_used - (byte *)new >= size) + { + memset (new, 0, sizeof(*new)); + new->size = size; + + new->next = &cache_head; + new->prev = cache_head.prev; + cache_head.prev->next = new; + cache_head.prev = new; + + Cache_MakeLRU (new); + + return new; + } + + return NULL; // couldn't allocate +} + +/* +============ +Cache_Flush + +Throw everything out, so new data will be demand cached +============ +*/ +void Cache_Flush (void) +{ + while (cache_head.next != &cache_head) + Cache_Free ( cache_head.next->user ); // reclaim the space +} + + +/* +============ +Cache_Print + +============ +*/ +void Cache_Print (void) +{ + cache_system_t *cd; + + for (cd = cache_head.next ; cd != &cache_head ; cd = cd->next) + { + Con_Printf ("%8i : %s\n", cd->size, cd->name); + } +} + +/* +============ +Cache_Report + +============ +*/ +void Cache_Report (void) +{ + Con_DPrintf ("%4.1f megabyte data cache\n", (hunk_size - hunk_high_used - hunk_low_used) / (float)(1024*1024) ); +} + +/* +============ +Cache_Compact + +============ +*/ +void Cache_Compact (void) +{ +} + +/* +============ +Cache_Init + +============ +*/ +void Cache_Init (void) +{ + cache_head.next = cache_head.prev = &cache_head; + cache_head.lru_next = cache_head.lru_prev = &cache_head; + + Cmd_AddCommand ("flush", Cache_Flush); +} + +/* +============== +Cache_Free + +Frees the memory and removes it from the LRU list +============== +*/ +void Cache_Free (cache_user_t *c) +{ + cache_system_t *cs; + + if (!c->data) + Sys_Error ("Cache_Free: not allocated"); + + cs = ((cache_system_t *)c->data) - 1; + + cs->prev->next = cs->next; + cs->next->prev = cs->prev; + cs->next = cs->prev = NULL; + + c->data = NULL; + + Cache_UnlinkLRU (cs); +} + + + +/* +============== +Cache_Check +============== +*/ +void *Cache_Check (cache_user_t *c) +{ + cache_system_t *cs; + + if (!c->data) + return NULL; + + cs = ((cache_system_t *)c->data) - 1; + +// move to head of LRU + Cache_UnlinkLRU (cs); + Cache_MakeLRU (cs); + + return c->data; +} + + +/* +============== +Cache_Alloc +============== +*/ +void *Cache_Alloc (cache_user_t *c, int size, char *name) +{ + cache_system_t *cs; + + if (c->data) + Sys_Error ("Cache_Alloc: already allocated"); + + if (size <= 0) + Sys_Error ("Cache_Alloc: size %i", size); + + size = (size + sizeof(cache_system_t) + 15) & ~15; + +// find memory for it + while (1) + { + cs = Cache_TryAlloc (size, false); + if (cs) + { + strncpy (cs->name, name, sizeof(cs->name)-1); + c->data = (void *)(cs+1); + cs->user = c; + break; + } + + // free the least recently used cahedat + if (cache_head.lru_prev == &cache_head) + Sys_Error ("Cache_Alloc: out of memory"); + // not enough memory at all + Cache_Free ( cache_head.lru_prev->user ); + } + + return Cache_Check (c); +} + +//============================================================================ + + +/* +======================== +Memory_Init +======================== +*/ +void Memory_Init (void *buf, int size) +{ + int p; + int zonesize = DYNAMIC_SIZE; + + hunk_base = buf; + hunk_size = size; + hunk_low_used = 0; + hunk_high_used = 0; + + Cache_Init (); + p = COM_CheckParm ("-zone"); + if (p) + { + if (p < com_argc-1) + zonesize = Q_atoi (com_argv[p+1]) * 1024; + else + Sys_Error ("Memory_Init: you must specify a size in KB after -zone"); + } + mainzone = Hunk_AllocName ( zonesize, "zone" ); + Z_ClearZone (mainzone, zonesize); +} + diff --git a/source/zone.h b/source/zone.h index a2258459..2dcf0a3e 100644 --- a/source/zone.h +++ b/source/zone.h @@ -1,133 +1,133 @@ -/* -Copyright (C) 1996-1997 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. - -*/ -/* - memory allocation - - -H_??? The hunk manages the entire memory block given to quake. It must be -contiguous. Memory can be allocated from either the low or high end in a -stack fashion. The only way memory is released is by resetting one of the -pointers. - -Hunk allocations should be given a name, so the Hunk_Print () function -can display usage. - -Hunk allocations are guaranteed to be 16 byte aligned. - -The video buffers are allocated high to avoid leaving a hole underneath -server allocations when changing to a higher video mode. - - -Z_??? Zone memory functions used for small, dynamic allocations like text -strings from command input. There is only about 48K for it, allocated at -the very bottom of the hunk. - -Cache_??? Cache memory is for objects that can be dynamically loaded and -can usefully stay persistant between levels. The size of the cache -fluctuates from level to level. - -To allocate a cachable object - - -Temp_??? Temp memory is used for file loading and surface caching. The size -of the cache memory is adjusted so that there is a minimum of 512k remaining -for temp memory. - - ------- Top of Memory ------- - -high hunk allocations - -<--- high hunk reset point held by vid - -video buffer - -z buffer - -surface cache - -<--- high hunk used - -cachable memory - -<--- low hunk used - -client and server low hunk allocations - -<-- low hunk reset point held by host - -startup hunk allocations - -Zone block - ------ Bottom of Memory ----- - - - -*/ - -void Memory_Init (void *buf, int size); - -void *Q_Malloc (size_t size); - -void Z_Free (void *ptr); -void *Z_Malloc (int size); // returns 0 filled memory -void *Z_TagMalloc (int size, int tag); - -void Z_DumpHeap (void); -void Z_CheckHeap (void); -int Z_FreeMemory (void); - -void *Hunk_Alloc (int size); // returns 0 filled memory -void *Hunk_AllocName (int size, char *name); - -void *Hunk_HighAllocName (int size, char *name); - -int Hunk_LowMark (void); -void Hunk_FreeToLowMark (int mark); - -int Hunk_HighMark (void); -void Hunk_FreeToHighMark (int mark); - -void *Hunk_TempAlloc (int size); - -void Hunk_Check (void); - -typedef struct cache_user_s -{ - void *data; -} cache_user_t; - -void Cache_Flush (void); - -void *Cache_Check (cache_user_t *c); -// returns the cached data, and moves to the head of the LRU list -// if present, otherwise returns NULL - -void Cache_Free (cache_user_t *c); - -void *Cache_Alloc (cache_user_t *c, int size, char *name); -// Returns NULL if all purgable data was tossed and there still -// wasn't enough room. - -void Cache_Report (void); - - - +/* +Copyright (C) 1996-1997 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. + +*/ +/* + memory allocation + + +H_??? The hunk manages the entire memory block given to quake. It must be +contiguous. Memory can be allocated from either the low or high end in a +stack fashion. The only way memory is released is by resetting one of the +pointers. + +Hunk allocations should be given a name, so the Hunk_Print () function +can display usage. + +Hunk allocations are guaranteed to be 16 byte aligned. + +The video buffers are allocated high to avoid leaving a hole underneath +server allocations when changing to a higher video mode. + + +Z_??? Zone memory functions used for small, dynamic allocations like text +strings from command input. There is only about 48K for it, allocated at +the very bottom of the hunk. + +Cache_??? Cache memory is for objects that can be dynamically loaded and +can usefully stay persistant between levels. The size of the cache +fluctuates from level to level. + +To allocate a cachable object + + +Temp_??? Temp memory is used for file loading and surface caching. The size +of the cache memory is adjusted so that there is a minimum of 512k remaining +for temp memory. + + +------ Top of Memory ------- + +high hunk allocations + +<--- high hunk reset point held by vid + +video buffer + +z buffer + +surface cache + +<--- high hunk used + +cachable memory + +<--- low hunk used + +client and server low hunk allocations + +<-- low hunk reset point held by host + +startup hunk allocations + +Zone block + +----- Bottom of Memory ----- + + + +*/ + +void Memory_Init (void *buf, int size); + +void *Q_Malloc (size_t size); + +void Z_Free (void *ptr); +void *Z_Malloc (int size); // returns 0 filled memory +void *Z_TagMalloc (int size, int tag); + +void Z_DumpHeap (void); +void Z_CheckHeap (void); +int Z_FreeMemory (void); + +void *Hunk_Alloc (int size); // returns 0 filled memory +void *Hunk_AllocName (int size, char *name); + +void *Hunk_HighAllocName (int size, char *name); + +int Hunk_LowMark (void); +void Hunk_FreeToLowMark (int mark); + +int Hunk_HighMark (void); +void Hunk_FreeToHighMark (int mark); + +void *Hunk_TempAlloc (int size); + +void Hunk_Check (void); + +typedef struct cache_user_s +{ + void *data; +} cache_user_t; + +void Cache_Flush (void); + +void *Cache_Check (cache_user_t *c); +// returns the cached data, and moves to the head of the LRU list +// if present, otherwise returns NULL + +void Cache_Free (cache_user_t *c); + +void *Cache_Alloc (cache_user_t *c, int size, char *name); +// Returns NULL if all purgable data was tossed and there still +// wasn't enough room. + +void Cache_Report (void); + + +