Skip to content

Instantly share code, notes, and snippets.

@mittorn
Created December 14, 2016 18:09
Show Gist options
  • Save mittorn/1d3c1ad2593439f7341ec350f27aae63 to your computer and use it in GitHub Desktop.
Save mittorn/1d3c1ad2593439f7341ec350f27aae63 to your computer and use it in GitHub Desktop.
This file has been truncated, but you can view the full file.
diff --git b/change.log a/change.log
new file mode 100644
index 0000000..1993d14
--- /dev/null
+++ a/change.log
@@ -0,0 +1,797 @@
+build 3597
+
+Engine: implemenantion of generic extension of BSP file format. Some extra lumps used by mods to get a new features like terrains, improved lighting etc
+Engine: rewrite PVS system for variable PVS radius that can be used as FAT pvs or FAT phs without recalculating vis-array
+Engine: starting to implement a new global feature that called HOST_FIXED_FRAMERATE to get client and server with constant fps (xash-based mods only)
+Render: some unsed funcs from RenderAPI was replaced with usefully funcs. Backward compatibility stay keep on
+Render: added few function in RenderAPI for SDLash3D by Albatross request
+Render: get support for loading multi-layered textures (for potential landscape implemenantion)
+Client: added two funcs into engine interface for custom interpolation on the client
+Server: allow to write map lums on a server to store userdata (physic collision, ai nodegraph as example). Works with extended BSP format only
+Server: physic interface was updated and expanded to get more control over player and entities simulation
+Client: demo protocol was changed to 2 ( allow comment in demo header)
+Client: demo playing is now using interpolation of client view angles
+Client: fixup some issues on demo playback
+Client: fix broken parametric entities (rockets in TFC as example) in multiplayer
+Client: net graph implementation like in GoldSrc
+Client: fixup ugly "un-duck" effect while after changing level (see for example transition from c0a0b to c0a0c)
+Client: clean-up and rewrite predicting code to get more compatibility for original prediction
+Client: handle colon separately for client version of COM_ParseFile (like in goldsrc)
+Client: finalize NETAPI. Now it can handle servers list from a master server (and display on a built-in client browser of course)
+GameUI: fixup color-strings that sended across network as server names or player names
+Client: texturebrower and overview mode now is not affected to player moving in world
+Client: accumulate pmove entities right after handling delta's not after world rendering
+Client: change master server address and request to actual
+Client: exclude dead bodies from solid objects in pmove processing
+Engine: fixup wad parsing from "wad" string in worldspawn settings (broken in previous version)
+Client: new style of displaying FPS counter
+Network: remove compression routine (very low efficiency)
+Client: fix muzzleflashes decoding from an studio animation event
+Engine: fix crash\hanging while trying to play AVI-file when codec is not-installed
+Client: change color handle for viewbeams (remove color normalization)
+Render: rewrite waves on water surfaces, uses table sin, increase speed and look close GoldSrc
+Render: completely rewrite texture loader, remove obsolete code, added support for multi-layered textures and float textures, fix errors
+Render: now "gl_texturemode" is obsolete and removed. Use "gl_texture_nearest" cvar instead
+Render: improved doble-cloud layering sky for Quake. reduce parallax distorsion
+Render: completely rewrite OpenGL loader. Remove obsolete extensions, fixup some errors
+Render: moved lightstyle animation from render frame loop to client frame loop (to prevent execute lightstyle animation on multipass viewing)
+Client: fixup studio culling on models with non-default scaling (large models in SoHL)
+Sound: change DSP code to get DSP effects like in original GoldSrc
+Sound: rewite sound PHS code. Now it can be useful
+Sound: first implenantion of raw-channels manager (in future this will be used for a custom video playing in xash-based mods and voice stream)
+Sound: sentence sounds now can be freed after playing (and saves some memory)
+Client: get VGUI backend to implement of lastest version of vgui.dll
+Engine: sort commands and cvars in alpha-bethical order. Include scripting functions from SDLash3D
+Console: now map list show an extension or type of various map and mapstats check level for transparent water support
+Console: replace the console buffer with effectively circular buffer (up to 1mb messages)
+FS: do complete revision of filesystem code, remove WAD2 support
+ImageLib: fix crash in quantizer while image is completely black
+Server: ignore sounds from pmove code when prediction is enabled
+Server: change userinfo handling, fixup info send to all the clients
+Server: fixup player animtion timings
+Server: enable cl_msglevel to filter up unneeded messages
+Server: restart server right if circular buffer pointer was exceeded 32-bit limit value
+Server: fixup player think timings
+
+
+build 3366
+
+Render: get support for custom DXT encoding (custom renderers only)
+Render: remove image program stuff (just not used)
+Engine: adding support for new wad filetypes (like DDS images) and wad imagetypes (normalmap, glossmap etc)
+Render: implement tiling on the studiomodels
+Client: do revision of predicting implementation, fix errors, more clean code
+Client: implement prediction error to avoid ugly blinking view on moving platforms when predicting is enabled
+Render: fixup the DDS loading code (invalid calc for mip-sizes)
+Client: fixup parser of detailtextures when texturename contain symbol '{'
+Render: added experimental hint for Nvidia drivers for force select Nvidia videocard when engine is running
+Engine: rewrote condition to calculate level CRC (singleplayer or multiplayer)
+Engine: added cvar r_wadtextures like in HL. First load textures from the wad, then from BSP
+Server: fix bug with cl_updaterate variable (always get default values if not changed by user)
+Server: recalc the ping time correctly
+Server: fix bug with too long player name
+GameUI: fix bug with 4-bit bmps buttons (image cutter)
+
+build 3224
+
+Client: make players solid for prediction code
+Client: change out of band request to make compatibility with custom master-servers
+Client: reject triggers and other like things for prediction code
+Client: fix the potential crash in prediction code
+Render: fix the potential crash in image loader
+Client: fix thirdperson camera culling
+Console: experimental fix for console problems with word-wrapping
+Network: fix bug in BF_WriteBitAngle function (thx mittorn)
+Network: rewrite clamping bounds for delta-variables (it was incorrectly)
+Network: change address of master server (now master is working)
+Server: some changes for client connection\disconnection to prevent possible errors or crash the server
+
+build 3153
+
+Render: RenderAPI update. New parm PARM_REBUILD_GAMMA to differentiate from map restart or change level when called from GL_BuildLightmaps
+GameUI: increased slots for savedgames\multiplayer maps up to 900
+
+build 3145
+
+Engine: add support for studiomodels with fixed texcoords multiplier (a studiomodel format extension)
+Engine: first experimental implementation of client movement predicting code (thx SovietCoder)
+Engine: first experimental implementation of client movement interpolation code (thx SovietCoder)
+Engine: some small bugfixes
+
+build 3030
+
+Client: remove demoheader.tmp while engine is shutting down
+Render: allow .dds format for detail textures
+Render: get support up to 16384 verts per submodel for studio models
+Render: disable vertical sync while level is loading
+ImageLib: allow support big textures up to 8192x8192
+ImageLib: rewrited code for detecting alpha in DXT3, DXT5 formats
+
+build 3000
+
+Render: new render info parm PARM_TEX_GLFORMAT for getting a real format for a given texture
+Server: added a engine memory allocations through internal mempool and function GetTextureData (a part of tracing alpha-textures)
+Client: clamping client pmove time if fps is too high
+Client: new fps counter-style that showing min and max values (cl_showfps 2 to enable)
+Render: get support for DDS textures (DXT1, DXT3, DXT5 and ARGB is allowed)
+Render: get support for floating depth-buffer texture (high prescision depth-buffer)
+Render: added VSDCT internal texture
+Render: add support for seamless cubemaps
+Render: fullscreen resolution auto-detect on first launch
+Render: added resolution 1600x900
+Render: new command line option -gldebug (enable internal GL-driver debug info)
+Engine: fix bug with CRC calculation on BSP31 format (thx [WPMG]PRoSToTeM@)
+FS: additional check to prevent error "Mem_Free: not allocated or double freed (free at filesystem.c:1489)"
+FS: fix FS_Eof function (thx [WPMG]PRoSToTeM@)
+VGUI: handle ESC key while VGUI is shown
+Engine: allows to load "deluxedata" from base folder even if a map placed in game folder (probably it was a stupid limitation)
+Server: replace Host_Error "ED_Alloc: no free edicts" with Sys_Error to prevent possible troubles
+
+build 2900
+
+Console: add detection for Paranoia 2 maps (show message in console)
+Engine: fix playing video when fps_max is 0 and framerate too high
+Engine: add function TraceSurface into pmove, EventAPI and PhysicAPI interfaces
+
+build 2867
+
+Client: another extension for EventAPI: function EV_SoundForIndex to get soundpath from soundindex
+Render: RenderAPI update. New flag TF_NOCOMPARE to disable comparing for depth-textures and PARM_CLIENT_ACTIVE (self-explanatory)
+Server: PhysicAPI update. Add support for file searching.
+Client: fix a little bug in CL_AddVisibleEntity
+Client: check rectangles right for hud.txt
+Client: fix demoplaying in background mode (changelevel etc)
+Client: fix SOLID_CUSTOM support in client trace
+Render: create debug context for powerful OpenGL debugging
+Render: fix bug with kRenderWorldGlow
+Sound: add two new cmds: spk and speak like in GoldSrc
+GameUI: fix buttons bmp loader
+
+build 2664
+
+Engine: restore right ordering of arguments of SV_StudioSetupBones export
+Render: a some cosmetic changes in RenderAPI
+Client: make levelshots for background demos
+Client: now cmd 'startdemos' invoke to play demos sequence like as background map
+Client: increase demo auto-naming from 100 up to 10000
+Client: fix bug with inverted PITCH of non-local playermodel
+Client: added FireCustomDecal into EfxAPI (was missed)
+GameUI: fix some crashes after end the background map (like call of trigger_endsection)
+Client: eliminate muzzleflash copy from mirror reflection in normal view
+Render: fix bug with max_texture_units clamping
+Render: completely remove glScissor calls (just unused)
+Render: update thirdperson code for mirrors
+Render: rewrite viewport setup code
+Render: fix software gamma adjustment bug while flashlight is enabled
+Render: reset vid_displayfrequency if current value isn't support by user display. Throw warning message
+Engine: allow new param in gameinfo.txt who called soundclip_dist <radius>. Default is 1000.
+Sound: new cmd 'playvol' like in GoldSrc
+Server: fix crash in 'Gunman Chronicles' at map end1.bsp
+Engine: show more infos with cmd 'mapstats'
+Network: fix bug with DT_TIMEWINDOW_8
+Server: fix bug with triggers with 'liquid' textures
+Server: fix some bugs in PVS calculation on server
+GameUI: enable auto-refresh of actual multiplayer maps list
+GameUI: update 3D playermodel preview for 16:9 screen aspect
+
+build 2636
+
+Engine: added internal loader for deluxemap data (.dlit file that produces VHLT)
+Engine: msurfmesh_t was reorganized to complex vertex data instead of single arrays (VBO-ready)
+Engine: decal_t now contain msurfmesh_t with all vertices and texcoords
+Render: RenderAPI interface updated to version 35
+Render: get support for float textures format (GL_ARB_texture_float is required)
+Render: implementation of image program preprocessor, like in Doom3, syntax: AddNormals( texture1.tga, texture2.tga );
+Render: get acess to internal tesselator through RenderAPI
+Server: a little update for PhysicInterface: get support for custom decal save\restore
+Client: separate levelshots for wide-screen and normal screen
+Client: revert parametric rocket implementation (previous was lost between two backups)
+Client: fix a potentially crash when calling function IsSpectateOnly
+Client: now 'ESC' button while playing video invoke jump to next video in list (instead of completely stopping video)
+Client: fix bug when demo ends up (Connection Problem)
+Client: now compensate screenshot gamma is toggleable (cvar "gl_compensate_gamma_screenshots")
+Render: optimize decal code, remove unused functions
+Render: now all the lightmaps stored into 1024x1024 texture
+Render: add cvar "gl_detailscale" for customize _detail.txt generation
+Render: fix some errors with studiomodels lighting
+Sound: increase maximum count of words in sentence up to 64 (from 29)
+Engine: fix broken recursion in Cmd_CheckMapLis_R (potentially crash)
+Client: passed keyup event through HUD_KeyEvent callback
+Network: change delta params for skycolor_* variables (in some cases color value was incorrect received)
+Network: fixed TIMEWINDOW_BIG mode
+Engine: add engine build number into console log
+
+build 2463
+
+Engine: reorganize data in texture_t. Release one variable for mod-makers
+Engine: change decal_t structure get compatibility with GoldSrc
+Engine: expanded mextrasurf_t reserved fields up 32
+Engine: update player_info_t (added customization_t like in SDK 2.3)
+Engine: increase local_state_t->weapondata up 64 slots
+Engine: update IVoiceTweak interface
+Engine: new lightstyle system with local time and custom interpolation
+Engine: fix bug with lightstyle save\restore (only first 64 symbols of pattern was saved)
+Engine: update r_efx_api_t interface
+Engine: update engine_studio_api_t, remove uncompatible function StudioGetTexture, added three new (thats came from CS:CZ)
+Engine: added ref_overview to support custom overview implementation
+Engine: render interface is outdated now. New render interface has version 30 (too much changed)
+Engine: update triangleapi_t interface
+Engine: update cl_dll interface (also added support for signle export that called 'F')
+Engine: a lttle update for enginefuncs_t (server interface)
+Client: fixed crash on shutdown when custom renderer uses AVI-files
+Engine: applaying scale for decals on brushmodels or world geometry
+Engine: update model_state_t thats keep info about studiomodels for studio decals. Include body and skin
+Engine: get support for custom studiocache on static and tempents
+Engine: write R_Srpite_WallPuff from pEfxAPI
+Client: fix bug with beam sorting (solid beams was drawing in translucent pass)
+Render: add special flag for decals thats indicated local space (any decal after first shoot)
+Render: apply emboss filter on studiomodel textures
+Render: rewrite client event system for studiomodels. Get more predictable results
+Network: write existing decals and static entities into new demo
+Network: protocol was changed to 48
+ImageLib: fix old bug with save non-aligned 8-bit bmp files
+Server: fix bug with reloading hl.dll when map was changed every time
+Server: a client part of save-file is outdated. New version is 0.68
+
+build 2402
+
+Engine: added new feature flag for compensate stupid quake bug
+Client: update the render interface, added usefully function GL_TextureTarget
+Render: get support for GL_RECTANGLE textures
+Client: fix playerangles in event receive (Paranoia bug with decals)
+Render: fixed mipmap generation for normalmaps (TF_NORMALMAP)
+Render: added two built-in textures: *blankbump and *whiteCube (self-explanatory names)
+Engine: made better check for Half-Life alpha maps (version 29 with colored lighting)
+
+build 2271
+
+Client: fix message TE_GLOWSPRITE. The gaussgun in Xash Mod is properly worked now.
+Client: restore studio check for missed models and ignore them instead of call the Sys_Error
+Server: fix crash in Gunman Chronicles (scorcher issues)
+
+build 2223
+
+Engine: added option "nomodels" for liblist.gam (disallow user to choose playermodel)
+Client: a new callback for render interface who called R_DrawCubemapView. This fixes cmd "envshot" for XashXT
+Client: store screenshots into root of folder "scrshots" instead of "scrshots\mapname"
+Client: engine using callback CL_CameraOffset now
+Client: fix angles for cmd "envshot"
+Render: rename a miss cvar "r_anisotropy" to real name "gl_anisotropy"
+Render: now "r_speeds 4" displays an actual count of "real" static entities that was created by call of MAKE_STATIC function
+Render: fix bug with blinking Quake Sky while autosave in progress
+Render: keep actual hardware gamma for multiple instances of application
+Engine: get support for Half-Life alpha maps (that has version 29)
+Server: fix the client rejection mechanism
+Server: using the pfnClientDisconnect callback
+Server: some changes in physics code (for MOVETYPE_PUSH)
+
+build 2153
+
+Render: added cvar "gl_nosort" that disables sorting of translucent surfaces
+
+build 2148
+
+Engine: implement support of new BSP format that called BSP31
+Engine: added new feature - big lightmaps 256x256 instead of 128x128. This is used for new BSP format 31
+Render: new texture viewer implemented (navigate pages with arrow keys)
+Engine: added cvar gl_keeptjunctions from Quake (removes a collinear points, economy some vertexes)
+Sound: test thing: release sentence sounds after playing
+Engine: rewrited code for anti (_-=ZhekA=-_) system
+Console: don't moving cursor if autocomplete was failed on second or all next arguments
+Engine: release all elements of client game cvar (was potential memory leak)
+Engine: allow change game for dedicated servers
+Server: added default case for Studio Blending Interface while server is not loaded (e.g. remote connection). Was here a potential crashpoint.
+Engine: parse "wad" field from entity string and use wad ordering for loading textures that may have matched names but placed in different wads
+Network: change protocol to 47. Old demos will stop working.
+Network: rewrite delta-comparing code. In theory this may reduce a network traffic
+Server: fix crash for fast switching between singleplayer and multiplayer
+Server: optimize MOVETYPE_COMPOUND code
+Server: added missed flag FL_FAKECLIENT for bots
+
+build 2112
+
+Engine: fix bug with ambient sounds that won't writes into demo
+Client: allow plaque 'loading' between demos change
+Client: make work fade flag FFADE_MODULATE
+Render: fixed underwater fog recursive leaf search code (thx XaeroX)
+Render: replace all 'random' calls in CL_RocketTrail code from RANDOM_LONG to rand() to get more compatibility with original quake particles
+Render: adding default studiomodel bbox for right culling client static entities
+Sound: add info about background track state into console command "s_info"
+Sound: increase static channels count up to 128
+Client: write background track state into demo
+Engine: fix crash when typing 'cvarlist' into console
+FS: allows lookup system files into root directory (e.g. vgui.dll etc)
+Engine: added new command 'modellist" (prints list about all loaded models)
+Engine: add terminator for entity string to be guranteed have valid end of the entity string
+Engine: purge all fake bmodels from previous map when server is changed
+Memory: increase check for filename debug length from 32 to 128 characters
+Server: background track which will be specfied from worldpsawn settings now are looped
+Engine: fix bug with recorded demos in Quake Remake after changelevel
+GameUI: 'gamestartup.mp3' now are looped
+Server: fix the SV_StudioSetupBones interface declaration error (thx maricool)
+Render: change MAXSTUDIOTEXTURES limit from 128 to 256
+Client: change passed argument for HUD_Frame callback from cl.time to host.frametime (thx XWider)
+Client: remove screen align to prevent deform on resolution 1366x768
+FS: do check what mod folder is really existed (this helps avoid to creating empty folder)
+GameUI: replace checkbox "Allow Software" with "Vertical Sync" in menu "Video Modes"
+
+
+build 2015
+
+Server: fix the sound problem with weapons
+Render: added rendermode kRenderWorldGlow (6) like in HL2
+Server: added new callback into PhysicsInterface that named SV_TriggerTouch
+Client: kill a little jitter for monsters that standing on elevators
+Client: fix very old bug (initially comes from Quake1) with efrags relinking on a static client entities
+Sound: got rid of message "S_PickChannel: no free channels" when breakble objects were broken.
+Engine: ignore to load HD-textures when dedicated server is running
+Client: count of static entities increased up to 512
+Render: fixed bug with wrong clamping on 3D textures
+Render: added a new one internal texture - gray cubemap that named as "*grayCube" without quotes
+Render: disable depth mask on studiomodels when render mode is "additive" (original HL rules)
+Sound: added a new untested feature for cull sounds by PAS on the client. Cvar "s_phs".
+Sound: add save\restore for all dynamic sounds in-game
+Sound: add save\restore for background track
+Engine: added two new cvars called "build" and "ver" for more info
+Engine: get support for loading game dlls from packfile
+Engine: get support for transparent conveyor belts. Texture name must starting from "{scroll"
+Sound: fix bug in wav-streaming code
+Server: add save\restore for client static entities (engine function MAKE_STATIC is now useful!)
+Server: remove command "map_backgound" in dedicated server mode
+Server: disable "Touch" function when playersonly-mode is active
+GameUI: don't draw logo.avi from root folder with user mods
+Client: added partially HD-textures support for sprites
+Server: now custom message code check all the visible portals (Xash-mod feature)
+Server: add quake-style for BSP hulls selection (cvar sv_quakehulls set to 1)
+Engine: remove jpeg image support (just unneeded)
+Server: remove ugly movement delay after map loaging in singleplayer
+Render: check studio skins for negative values to prevent possible crashes
+Console: fixup negative values for "scr_conspeed" variable
+Render: fix interpolation bug for studiomodels on internal studio renderer
+Physic: transform trace BBox into local space of bmodel
+Engine: implement new system of engine features that can be enabled by Mod-Maker request
+Engine: build BSP surface meshes for Mod-Makers. e.g. for build collision tree of VBO\VA custom rendering
+Engine: allow support of large maps up to +\- 16384 units (need to edit delta.lst and enable feature in the engine)
+Engine: rewrite MOVETYPE_TOSS, MOVETYPE_BOUNCE etc
+Client: implement new draw type TRI_POINTS for TriAPI
+Client: changed snapshot name from mapnameXXXX.bmp to mapname_XXXX.bmp (by Qwertyus request)
+Client: write experimental code for interpolate bmodels movement (pev->animtime must not equal 0 for enable)
+GameUI: allow scissor for enginefunc pfnDrawCharacter
+Network: protocol changed. replace obsolete message svc_frame with message svc_restoresound
+Client: remove pieces that stuck in the walls for TE_BREAKMODEL message
+Render: fix mode r_lighting_extended 1 for prevent permanently black lighting in some cases
+Render: global fog update. get the uniform color for all the underwater objects (thx n00b)
+Render: fix issues with conveyor HD-textures (it can moves slower than physical conveyor speed)
+Render: added support for HD-textures for normal sprite frames (non-HUD sprites)
+Render: sorting meshes for studiomodel surfaces (draw "adiitive" surfaces at end of the list)
+Render: release GL-context when engine is shutting down
+Render: set gl_allow_static to zero as default
+Sound: set s_cull to zero as default
+Input: fix problems with call WC_SYSMENU instead of user-defined command when ALT is pressed
+Console: add a new command that called "mapstats" and works like bspinfo.exe
+Physic: added new meta-type SOLID_CUSTOM that could be traced in game dlls with physic API
+Pmove: get to work PM_CheckStuck on a server-side
+Server: added a new cvar that called a "sv_validate_changelevel" for skip any checks with changelevel problems
+Server: make check for recursive changelevel (and ignore it)
+Server: fix the problem with non-sended events when a player sight cross-line contents
+Server: rewrite MOVETYPE_STEP and MOVETYPE_PUSHSTEP physics
+Server: allow to MOVETYEP_PUSH blocking by dead bodies
+Server: fix bug with MOVETYPE_FOLLOW
+Server: added TriAPI through Server_PhysicsInterface for debug purposes
+GameUI: replace broken letter 'ё' with 'е'
+GameUI: don't draw logo.avi for mods without this file in gamedir
+GameUI: fix buttons loader bug
+GameUI: enable scissor for all ScrollList menus
+GameUI: restore the menu buttons control from keyboard
+Client: get walk animation support for 'wrong' player models that uses different skeleton
+Server: ignore savegame during intermission (used for Quake remake)
+Render: merge mirrors with same plane into one pass (perfomance option)
+Render: fix errors in function GL_CleanupTextureUnits (it was cause problems in XashXT)
+Render: allow decals on 'Solid" surfaces: grates, ladders etc (thx n00b)
+Render: rewrite "r_lighting_extended 2" mode
+Render: add optional texture sorting for models with Additive and Transparent textures (r_studio_sort_textures cvar)
+Engine: makes AABB transform trace an option for switchable engine features
+Engine: allow studiomodel textures up to 4096x4096 (for indexed images)
+Server: merge PVS for looking for client through portal cameras
+Server: fix bug with collect savegame info from basedir (e.g. valve)
+
+
+build 1905
+
+Physic: fix trace bug when model is missing
+Client: implement function TriScreenToWorld
+Server: add local times for all the clients
+Server: implement unlag system
+Client: added ex_interp variable
+GameUI: added support for playermodel images (preview thumbnails)
+Engine: fix potentially crash in menu after calling Host_Error
+Engine: fix crash on Cry Of Fear modification (memory corrupts)
+Engine: first implementation of HLTV
+Render: fix a little bug with engine mirrors
+Sound: implement separate volume controls for background track and game sounds
+Sound: fix wrong position for dynamic sounds in case parent entity never has updates on the client
+Client: first implementation of extended engineinterface.
+Server: fix bug with update movevars after the server initialization.
+Input: cancel mouse movement while switches between menu\console and game
+Render: added function R_EntityRemoveDecals into RendererInterface
+Render: fixed bug with crash engine on custom resolutions while makes a screenshot (e.g. 1366x768)
+
+build 1850
+
+Physic: add check for liquid brushes that has only hull0
+Render: draw decals without culling on the transparent surfaces
+Engine: fix decal transition bug on global entities
+Client: fix "r_drawentities 5" debug code in client.dll
+Render: rewrited detail texture code for using detail scale for each diffuse texture instead of each detail texture
+Render: custom render interface is changed to version 26
+Engine: added custom studio decals serializtaion
+Server: fixed check for russian letters in the commands "say" and "say_team"
+Server: physics interface is changed to version 6
+Client: allow console in multiplayer even while dev-mode is disabled
+Engine: added new message SVC_STUDIODECAL that passed through engine and call function in extended rendered interface
+Engine: hook PrintScreen and manually writing screenshot into clipboard
+Render: fix decals drawing when rendermode is TransColor
+Render: add support for cubemaps, 1D and 3D textures
+Render: added some new internal textures that using by Xash-Mod 0.5
+Render: fix reflection for Glow-Sprites and follow beams
+Studio: fix poly-counter for studio models
+Engine: add support for userconfig
+Engine: allow letter 'ё' in console and chat-mode
+Engine: fix REG_USER_MSG for messages that registering in-game
+Server: clear savedir when new game is started
+GameUI: loading maps.lst from basedir while mod doesn't contain multiplayer maps
+Network: implemented QueryCvarValue and QueryCvarValue2
+Physic: new pm-trace code, new server trace code, new studio hitbox trace code
+Client: rewrite demo record and playback
+Render: add support for STUDIO_NF_FULLBRIGHT
+Physic: fix pmove trace bug
+
+build 1770
+
+Client: add command "on"-"off" for virtual CD-player
+Client: add command "snapshot" that save screenshots into root folder
+Client: add studiomodel missed flags like in Quake (EF_ROTATE, EF_ROCKET, EF_GIB etc)
+Sound: clear LOOP flag for engine funcs PlaySoundByIndex and PlaySoundAtLocation
+Render: fix r_wateralpha, move cvar to server and allow save-restore it
+Render: fix old bug with surface->flags
+Render: fix crash when studiomodel isn't loaded but trying to draw
+Render: remove cvar gl_texturebits
+Render: allow 16-bit color mode when decktop has same
+Render: rename "vid_gamma" to "gamma", make backward compatibility with GoldSource config
+Sound: get support for automatic ambient sounds like in Quake
+Sound: add cvar "s_combine_channels" that trying to combine mutilpe channels with same sounds into one
+Engine: add "secure" option support for both liblist.gam and gameinfo.txt
+Engine: fix bug determine current directory
+Server: fix bug when some sound messages can't be sended to client (e.g. not vised map)
+Render: allow to load hi-res quake sky (two layers called as sky_solid and sky_alpha)
+Physic: fix trace bug when bbox mins are 0 0 0 and bbox maxs are positive values (like quake boxes)
+GameUI: add checkbox "allow materials" in menu Video Options.
+Client: implement "viewsize" cvar
+GameUI: add new function to inteface called as pfnProcessImage
+Client: add support for default studiomodel flags like quake effects (light, smoke, blood etc)
+Render: add engine mirrors (can be set on map with texture "decals.wad\reflect1")
+Client: rewrite client font system to allow support for "hud_scale" feature
+Client: re-enable client static entities (see engine function MAKE_STATIC for details)
+Sound: clear "loop" flags for client engine functions PlaySoundByName and PlaySoundByIndex
+Client: fix potentially crash in StudioRemap code
+Client: finalize 'GlowShell' effect on StudioModels
+Render: implement software gamma control that based on lightmap adjusting (gl_ignorehwgamma 1)
+Render: restore projection and modelview matrices before call V_CalcRefdef to avoid errors on custom rendering (e.g. HLFX 0.5, Trinity Renderers)
+Render: remove all stuff for 3dfx gamma control
+Render: add missing function R_ScreenToWorld
+Engine: add "icon" option support for both liblist.gam and gameinfo.txt
+Render: get support for rotational skybox that can be saved and restored with current angle
+Engine: fix bug with incorrect detecting Blue-Shift maps in some rare cases
+Engine: add autocomplete for 'entpatch' command
+Engine: fix Host_Error issues
+Network: add IPX and IPX_BROADCAST for backward compatibility with GoldSrc
+Engine: do revision for 'allow_studio_scale' cvar in trace code
+GameUI: added support for Steam backgrounds
+GameUI: hide input symbols for "password" field in "create server" menu page
+Client: initial implementation of NetAPI
+Render: clear decals code. Add transparent rendering for 'glass' decals
+GameUI: added new argument for pfnPIC_Load.
+GameUI: fix loading flipped Steam background images
+Client: remove gravity for R_Implosion effect
+Sound: add SND_MoveMouth16 for support 16-bit sounds lip-sync
+Engine: fix potentially crash during parsing titles.txt when file is empty
+Engine: increase MAX_VALUE field up to 2048 characters
+Console: rename con_gamemaps to con_mapfilter
+Sound: add check by PVS for dynamic entity channels
+Sound: add sound culling by geometry (cvar 's_cull' for enable or disable)
+Server: fix changelevel bug
+Engine: fix sound pathes with backward slash
+Engine: rewrite COM_LoadFile and LoadFileForMe for use malloc instead of engine Mem_Alloc
+Server: check date for entitypatch to avoid loading too old pathes (when map is never than path)
+Server: bug fixed in CreateNamedEntity (on create not existed entities).
+Server: rewrite engine func pfnAlertMessage to avoid crash in AM:Rebrith
+Server: align memory for edicts by 4 (this help to avoid crash in Poke646)
+Render: bugfixed with rotational brush culling (perfomance)
+Server: changelevel bug fixed (accumulate global entities)
+Server: changelevel bug when entitypath on new level is too old (new level is never than him entitypath)
+Server: physical inetrface for custom physic implementation is updated to version 5
+Physic: fix bug with MOVETYPE_COMPOUND
+Server: fix bug with force_retouch on start the level (to avoid crash in Todesangst2 on t2e1m10)
+Render: fix rendering for FACE_UPRIGHT sprite types (doom-like sprite monsters)
+Protocol: shifted up additional EF_ flags to avoid collisions with Trinity Renderers
+Render: now hardware gamma-control is fixed
+Client: implement new render interface for custom renderer implementation (current version is 12)
+Client: added two new funcstions into event API (IndexForEvent and EventForIndex)
+Client: added new function into IEngineStudio interface (StudioGetTexture) for custom rendering implementation
+Client: passed server beam entity through client function HUD_AddEntity, make force to add menu entity (player setup)
+Client: passed static client entities through client function HUD_AddEntity
+Physic: add support for rotational water and triggers
+
+build 1662
+
+Client: implement StudioRemapColors function
+Client: add simple shadows for stduiomodels (disabled like in GoldSrc)
+Client: fix some Paranoia bugs when custom renderer is disabled
+Client: implement overview tool (dev_overview)
+Client: add debug commands linefile and pointfile
+Client: get support for full-color external textures (tga format) - world, studiomodels and decals
+Client: fixed some HLFX 0.6 bugs
+Client: fixed follow studiomodels (like flags in CTF)
+Server: add pfnGetApproxWavePlayLen
+Sound: get support for mp3's with wav header
+Server: fixed FIND_CLIENT_IN_PVS
+Server: fixed PlaybackEvent, use camera PVS point when client see in
+Render: enable lightmaps on a transparent surfaces like windows (r_lighting_extended 2)
+Server: func_pushable can push players which standing on (sv_fix_pushstep)
+Render: partially fix for underwater fog (temporary solution)
+
+build 1613
+
+Client: fix drawing beams for 'solid' mode
+Image: fix BMP loader for 4-bit color bmp's
+Client: fix lightlevel calculating for local client (remove 'ambient' base from final value)
+GameUI: first implementation of custom strings and support 'strings.lst' parsing
+GameUI: replace unneeded button 'credits' with button 'previews'
+Render: fix sprite interpolation
+Render: fix angled sprites offset
+Render: implement detail textures like in Steam Half-Life (thx n00b)
+Client: rework env_funnel effect
+Engine: get full support for input, processing and drawing russian letters
+Console: add console commands 'messagemode' and 'messagemode2'
+Console: fix bug with autocomplete (enable sort for found cmds)
+Client: added HUD_ChatInputPosition for CS 1.6
+
+build 1598
+
+Client: fix crosshair drawing
+Sound: change libmad mp3 decoder on a mpg123
+Client: fix gibs randomization for TE_BREAKMODEL
+Engine: fix modelloader bug (engine crash after not found map)
+Video: add resolution 1366x768
+GameUI: fix skill select
+Network: change 'svc_setangle' message, include rollangle
+Render: fix chrome rendering on a studiomodels
+Render: added video memory economy mode - cvar 'gl_luminance_textures'
+GameDLL: first implementation of extended engineinterface. Metamod is supported now
+
+build 1557
+
+Sound: fixed wrong sound angles when client see in the camera
+Render: fix crash on change gl_texturemode and gl_anisotropy.
+Client: change relationsip for GetLocalPlayer. Now it's always valid.
+Server: rewrite SV_Multicast for correct work with custom user cameras
+Server: rewrite FIND_CLIENT_IN_PVS like in QW
+
+build 1540
+
+Fixed many-many small and large bugs before final release
+
+build 1529
+
+FS: add "fallback_dir" option
+Server: fix func_pushable interaction with movers. Add new cvar "sv_allow_rotate_pushables"
+Server: added support for 'SV_SaveGameComment' export
+Server: fixed backup for quick.sav and autosave.save (quick01.sav and autosave01.sav)
+Client: add commandline option "-nointro" to skip start movies
+Pmove: add sv_clienttrace that shared across network
+Render: implement "envshot" and "skyshot" commands for make cubemaps or skyboxes
+Server: fix remote connection (rcon)
+Render: add glare reduction option in menu
+Server: fix FindEntityInSphere bug (satchel issues in multiplayer)
+GameUI: added to scrollbar in srcoll lists (thx ADAMIX)
+
+build 1516
+
+Engine: fix Sys_Error blowout
+GameUI: implement new credits effect in Half-Life (when game is end)
+GameUI: use system cursor instead of emulated
+
+build 1515
+
+Engine: fix some bugs
+
+build 1507
+
+Console: implement Con_NPrintf and Con_NXPrintf
+Render: adding better lighting for studiomodels (right lighting for long sequences e.g. forklift.mdl)
+Network: clamp all integer values to prevent them out of range
+Client: VGUI implementation
+Render: fix decals loading
+Server: fix NAN error on a crossbow launch
+
+build 1488
+
+Render: fix invisible sprites when game is paused
+Render: implement new better sprites lighting
+GameUI: HL-style buttons (old good menu form WON version)
+Render: fix some rendering bugs
+
+build 1482
+
+Engine: fixed critical bug
+Launch: remove all built-in tools
+Engine: add underwater fog
+Engine: add cvar to control studio model scaling (enable or disable)
+Engine: fixed bug with mouse in multiplayer (not work in menu)
+Engine: fixed lag on rpg laserspot
+Engine: added weapon and movement prediction (may be bugly, use with caution)
+GameUI: added pfnRandomLong and pfnRandomFloat built-ins (and keep compatibility with old versions)
+Engine: added map_background (special command for loading background maps like in Source Engine)
+GameUI: added support for chaptermapbackground.txt script-file (random choose background maps)
+Render: fix some rendering bugs for mods with custom renderer (Paranoia, HLFX etc)
+
+build 1433
+
+Engine: rework hitbox trace
+Engine: implement new check for blue-shift map format
+Engine: fix PointContents for custom contents checking (spirit)
+Engine: fix "angle" field on maps (typically for gearbox)
+GameUI: implement 'mouse look' checkbox
+
+build 1430
+
+Engine: fix crash with invalid room_type set (more than 60)
+Engine: fix stuck on elevators or tracktrains for clients
+
+build 1428
+
+Engine: fix trigger_camera serialization bug
+Engine: many-many physics bugs fixed
+Engine: reworking monster's movement and path finding
+Engine: hlfx 0.6 is now working
+FS: fix bug with game and base directory dectecting when they matches
+FS: watch for changes in liblist.gam and update gameinfo.txt (feature)
+Engine: fix some rendering bugs
+GameUI: added 'suitvolume' control
+GameUI: rewrite playeyrmodel drawing, so you can shows changes when hi\low resolution button is toggled
+GameUI: remove choosing audio and video library (interface changed!)
+Engine: fix train startup sound on new level
+
+build 1422
+
+Render: fix decals serialization bug
+Engine: fix movie plaback bug (black screen)
+Engine: fix crash on loading encrypted client.dll
+GameUI: now recalc resolution when vid_mode is changed
+Sound: fix musicvolume bug
+Image: fix indexalpha palette bug
+Physic: fix stuck on some items (weapons, ammo)
+
+build 1418
+
+Tools: move all tools into launch.dll
+Sound: moving snd_dx.dll into engine.dll
+Sound: implement CDAudio emulator with support mp3 tracks from original Half-Life
+Sound: implement mp3 support
+GameUI: GameUI.dll renamed to MainUI.dll to avoid conflict with original valve's GameUI.dll
+Engine: support for StartupVids.txt
+Engine: get full compatibility with hl.dll
+FS: recode wad resource management (now support lumps from wads with same name)
+Engine: trigger_camera is now correctly saved and restored
+Render: add sorting for translucent surfaces
+Render: make support for 'static' models (any opaque non-moving brushes engine automatically make as part of world)
+Render: correct serialization for decals on bmodels
+
+build 1338
+
+Engine: fix a broken demos recording\playing
+GameUI: get support for internal resources (built-in)
+GameUI: make font.bmp, cursor.bmp and typing.bmp as part of GameUI.dll
+GameUI: remove demo menus heads.
+
+build 1334
+
+Engine: prevent auto-repeat for most keys in-game
+Engine: implement AVI movies support (instead of RoQ support)
+Engine: enable logo.avi in main menu (see GameUI source for details)
+
+build 1313
+
+Launch: code revision, remove dead code, implement some missed functions
+Sound: fix some errors and crash
+Sound: implement music volume
+Sound: reworking sound mixer
+Sound: implement DSP
+Engine: add support for five mouse extra-buttons
+Engine: fix some physics bugs
+SDK: add blue-shift game source to SDK (combined with main game source)
+
+build 1305
+
+Engine: implement bit-stream network buffer
+Engine: implement custom delta-encoding with user defined script (delta.lst)
+Engine: reworking client entity for get more compatibility with original HL client
+Engine: make SDK compatible with HLSDK 2.3 on the server-side
+Engine: fixup across transition time-bug
+Engine: completely rewrite server trace
+Engine: rewrite hitbox trace
+Engine: rewrite SV_PointContents
+Engine: implement a new movetype: compound for gluing two entities together (like movewith in spirit)
+Engine: fix toss entities on conveyors
+Engine: rewrite MOVETYPE_PUSH
+Engine: rewrite monsters movement code
+Engine: reworking client and game interfaces
+Engine: fix camera bugs (no sounds when client see in the camera)
+SDK: fix many-many small HL bugs in original sdk code
+Engine: fix trigger retouching system
+Engine: adjust beam visibility
+
+build 1271
+
+Engine: enable server.cfg, listenserver.cfg, mapcycle.txt etc
+Engine: got to work mapcycle.txt
+GameUI: implement redefine keys menu
+Engine: added autocomplete for cmd 'exec'
+Engine: added version info in menu
+
+build 1270
+
+SDK: Shared launcher code
+Engine: Partially fix bmodel interpolation
+Engine: use standard .cfg files instead of .rc files
+
+build 1269
+
+Render: cut invisible water polygons
+Render: implement EF_NOWATERCSG for control func_water backface culling
+Tools: fix wadlib and spritegen round bugs
+FS: implement binary search for wadlumps
+Engine: revert low-res timer
+Network: fixup userinfo fields 'model' and 'name'
+Sound: implement custom pause for various sources
+Input: disable mouse events when level is loading
+GameUI: adding some missed dialogs
+Render: fix interpolation on flying monsters
+Render: fix wrong sprite attachments
+Render: fix invalid frustum culling for studiomodels ( e.g. barnacle.mdl )
+Physic: fix trace for rotating bmodels
+Engine: fixup physinfo save\restore bug
+
+build 1262
+
+Engine: add 'allow_levelshots' cvar (disabled by default) to switch between custom levelshots and normal 'loading' screen
+Client: remove fmod.dll implementation
+Engine: implement variable-sized fonts (console, menu hints)
+Sound: added support for stereo wavs plays (menu sounds)
+Render: enable custom game icons (game.ico in mod folder)
+Engine: move menu code into new user dll called GameUI.dll (based on original q2e 0.40 menu code)
+FS: implement simple converter liblist.gam to gameinfo.txt
+
+build 1254
+
+SoundLib: ogg loop points (LOOP_START comment)
+Client: recalc fov y for more matched with original HL
+Bshift: fix env_laser entity
+Client: fix fadeout for break model pieces
+Gfx: replace default.bmp font with fixed alpha-channel (thx LokiMb)
+Render: fix invisible beams behind glass
+Render: fix glow-sprites drawing through walls (see also r_cullflares cvar)
+FS: implement filter wadlumps by wadname (e.g. gfx.wad/backtile)
+Render: implement kRenderTransColor
+Engine: completely moving particle code into the client.dll
+Client: implement tracers for sparks, garg streaks and bullets
+
+build 1249
+
+ImageLib: added support for 4-bits and monochrome uncompressed BMPs.
+ImageLib: fix data align for NPOT textures in menu (e.g. slider.bmp).
+StdLib: skip empty symbols in numerical string for atoi and atof.
+Render: implement LoadSprite for custom client sprites (e.g. hud)
+Sound: fixed bug with background music looping
+Fonts: implement Half-Life creditfonts
+Client: move client.dll to valve folder
\ No newline at end of file
diff --git b/cl_dll/hud_servers.cpp a/cl_dll/hud_servers.cpp
index 7a5a147..61f6454 100644
--- b/cl_dll/hud_servers.cpp
+++ a/cl_dll/hud_servers.cpp
@@ -17,7 +17,7 @@
static int context_id;
// Default master server address in case we can't read any from woncomm.lst file
-#define VALVE_MASTER_ADDRESS "half-life.east.won.net"
+#define VALVE_MASTER_ADDRESS "ms.xash.su"
#define PORT_MASTER 27010
#define PORT_SERVER 27015
@@ -902,7 +902,7 @@ void CHudServers::RequestList( void )
NET_API->InitNetworking();
// Kill off left overs if any
- NET_API->CancelAllRequests();
+ CancelRequest();
// Request Server List from master
NET_API->SendRequest( context_id++, NETAPI_REQUEST_SERVERLIST, 0, 5.0, &adr, ::ListResponse );
@@ -935,7 +935,7 @@ void CHudServers::RequestBroadcastList( int clearpending )
if ( clearpending )
{
// Kill off left overs if any
- NET_API->CancelAllRequests();
+ CancelRequest();
}
adr.type = NA_BROADCAST;
diff --git b/common/bspfile.h a/common/bspfile.h
index 51452cd..12b8d65 100644
--- b/common/bspfile.h
+++ a/common/bspfile.h
@@ -31,7 +31,8 @@ BRUSH MODELS
#define XTBSP_VERSION 31 // extended lightmaps and expanded clipnodes limit
#define IDEXTRAHEADER (('H'<<24)+('S'<<16)+('A'<<8)+'X') // little-endian "XASH"
-#define EXTRA_VERSION 2 // because version 1 was occupied by old versions of XashXT
+#define EXTRA_VERSION 4 // ver. 1 was occupied by old versions of XashXT, ver. 2 was occupied by old vesrions of P2:savior
+ // ver. 3 was occupied by experimental versions of P2:savior change fmt
#define DELUXEMAP_VERSION 1
#define IDDELUXEMAPHEADER (('T'<<24)+('I'<<16)+('L'<<8)+'Q') // little-endian "QLIT"
@@ -103,11 +104,19 @@ BRUSH MODELS
#define LUMP_CLIPNODES3 16 // hull2 goes into LUMP_CLIPNODES2, hull3 goes into LUMP_CLIPNODES3
#define HEADER_LUMPS_31 17
-#define LUMP_FACES_EXTRADATA 0 // extension of dface_t
-#define LUMP_VERTS_EXTRADATA 1 // extension of dvertex_t
-#define LUMP_CUBEMAPS 2 // cubemap description
-
-#define EXTRA_LUMPS 8 // g-cont. just for future expansions
+#define LUMP_LIGHTVECS 0 // deluxemap data
+#define LUMP_FACEINFO 1 // landscape and lightmap resolution info
+#define LUMP_CUBEMAPS 2 // cubemap description
+#define LUMP_VERTNORMALS 3 // phong shaded vertex normals
+#define LUMP_LEAF_LIGHTING 4 // contain compressed light cubes per empty leafs
+#define LUMP_WORLDLIGHTS 5 // list of all the virtual and real lights (used to relight models in-game)
+#define LUMP_COLLISION 6 // physics engine collision hull dump
+#define LUMP_AINODEGRAPH 7 // node graph that stored into the bsp
+#define LUMP_UNUSED0 8 // one lump reserved for me
+#define LUMP_UNUSED1 9 // one lump reserved for me
+#define LUMP_UNUSED2 10 // one lump reserved for me
+#define LUMP_UNUSED3 11 // one lump reserved for me
+#define EXTRA_LUMPS 12 // count of the extra lumps
// texture flags
#define TEX_SPECIAL BIT( 0 ) // sky or slime, no lightmap or 256 subdivision
@@ -216,9 +225,18 @@ typedef struct
{
float vecs[2][4]; // texmatrix [s/t][xyz offset]
int miptex;
- int flags;
+ short flags;
+ short faceinfo; // -1 no face info otherwise dfaceinfo_t
} dtexinfo_t;
+typedef struct
+{
+ char landname[16]; // name of decsription in mapname_land.txt
+ unsigned short texture_step; // default is 16, pixels\luxels ratio
+ unsigned short max_extent; // default is 16, subdivision step ((texture_step * max_extent) - texture_step)
+ short groupid; // to determine equal landscapes from various groups, -1 - no group
+} dfaceinfo_t;
+
typedef word dmarkface_t; // leaf marksurfaces indexes
typedef int dsurfedge_t; // map surfedges
diff --git b/common/com_model.h a/common/com_model.h
index 709f23d..24077e0 100644
--- b/common/com_model.h
+++ a/common/com_model.h
@@ -84,10 +84,20 @@ typedef struct texture_s
typedef struct
{
+ char landname[16]; // name of decsription in mapname_land.txt
+ unsigned short texture_step; // default is 16, pixels\luxels ratio
+ unsigned short max_extent; // default is 16, subdivision step ((texture_step * max_extent) - texture_step)
+ short groupid; // to determine equal landscapes from various groups, -1 - no group
+
+ int reserved[32]; // just for future expansions or mod-makers
+} mfaceinfo_t;
+
+typedef struct
+{
float vecs[2][4]; // [s/t] unit vectors in world space.
// [i][3] is the s/t offset relative to the origin.
// s or t = dot( 3Dpoint, vecs[i] ) + vecs[i][3]
- float mipadjust; // mipmap limits for very small surfaces
+ mfaceinfo_t *faceinfo; // pointer to landscape info and lightmap resolution (may be NULL)
texture_t *texture;
int flags; // sky or slime, no lightmap or 256 subdivision
} mtexinfo_t;
@@ -168,7 +178,7 @@ typedef struct mleaf_s
msurface_t **firstmarksurface;
int nummarksurfaces;
- byte *compressed_pas;
+ int cluster; // helper to acess to uncompressed visdata
byte ambient_sound_level[NUM_AMBIENTS];
} mleaf_t;
diff --git b/common/const.h a/common/const.h
index e80c8a4..192b263 100644
--- b/common/const.h
+++ a/common/const.h
@@ -50,6 +50,7 @@
#define FL_ONTRAIN (1<<24) // Player is _controlling_ a train, so movement commands should be ignored on client during prediction.
#define FL_WORLDBRUSH (1<<25) // Not moveable/removeable brush entity (really part of the world, but represented as an entity for transparency or something)
#define FL_SPECTATOR (1<<26) // This client is a spectator, don't run touch functions, etc.
+
#define FL_CUSTOMENTITY (1<<29) // This is a custom entity
#define FL_KILLME (1<<30) // This entity is marked for death -- This allows the engine to kill ents at the appropriate time
#define FL_DORMANT (1<<31) // Entity is dormant, no updates to client
diff --git b/common/cvardef.h a/common/cvardef.h
index f822cc7..614c898 100644
--- b/common/cvardef.h
+++ a/common/cvardef.h
@@ -25,6 +25,8 @@
#define FCVAR_PRINTABLEONLY (1<<7) // This cvar's string cannot contain unprintable characters ( e.g., used for player name etc ).
#define FCVAR_UNLOGGED (1<<8) // If this is a FCVAR_SERVER, don't log changes to the log file / console if we are creating a log
+#define FCVAR_GLCONFIG (1<<18) // write it into opengl.cfg
+
typedef struct cvar_s
{
char *name;
diff --git b/common/entity_state.h a/common/entity_state.h
index ef5448f..4d04e8f 100644
--- b/common/entity_state.h
+++ a/common/entity_state.h
@@ -176,11 +176,13 @@ typedef struct clientdata_s
#include "weaponinfo.h"
+#define MAX_LOCAL_WEAPONS 64 // max weapons that can be predicted on the client
+
typedef struct local_state_s
{
entity_state_t playerstate;
clientdata_t client;
- weapon_data_t weapondata[64];
+ weapon_data_t weapondata[MAX_LOCAL_WEAPONS];
} local_state_t;
#endif//ENTITY_STATE_H
\ No newline at end of file
diff --git b/common/features.h a/common/features.h
index efc0bab..73f184e 100644
--- b/common/features.h
+++ a/common/features.h
@@ -25,6 +25,6 @@ GNU General Public License for more details.
#define ENGINE_COMPENSATE_QUAKE_BUG (1<<5) // compensate stupid quake bug (inverse pitch) for mods where this bug is fixed
#define ENGINE_DISABLE_HDTEXTURES (1<<6) // disable support of HD-textures in case custom renderer have separate way to load them
#define ENGINE_COMPUTE_STUDIO_LERP (1<<7) // enable MOVETYPE_STEP lerping back in engine
-#define ENGINE_THREADED_MAIN_LOOP (1<<8) // simulate dedictated thread for main engine loop (prefomance)
+#define ENGINE_FIXED_FRAMERATE (1<<8) // keep constant rate for client and server (but don't clamp renderer calls)
#endif//FEATURES_H
\ No newline at end of file
diff --git b/common/render_api.h a/common/render_api.h
index 8f2b364..6bc60f9 100644
--- b/common/render_api.h
+++ a/common/render_api.h
@@ -44,11 +44,11 @@ GNU General Public License for more details.
#define PARM_TEX_TARGET 8
#define PARM_TEX_TEXNUM 9
#define PARM_TEX_FLAGS 10
-#define PARM_TEX_TYPE 11
+#define PARM_TEX_DEPTH 11 // 3D texture depth or 2D array num layers
#define PARM_TEX_CACHEFRAME 12 // compare with worldmodel->needload
#define PARM_TEX_GLFORMAT 13 // get a texture GL-format
#define PARM_TEX_ENCODE 14 // custom encoding for DXT image
-// reserved
+#define PARM_TEX_MIPCOUNT 15 // count of mipmaps (0 - autogenerated, 1 - disabled of mipmapping)
#define PARM_WORLD_VERSION 16 // return the version of bsp
#define PARM_SKY_SPHERE 17 // sky is quake sphere ?
#define PARM_MAP_HAS_MIRRORS 18 // current map has mirorrs
@@ -65,6 +65,10 @@ GNU General Public License for more details.
#define PARM_MAX_IMAGE_UNITS 29
#define PARM_CLIENT_ACTIVE 30
#define PARM_REBUILD_GAMMA 31 // if true lightmaps rebuilding for gamma change
+#define PARM_DEDICATED_SERVER 32
+#define PARM_SURF_SAMPLESIZE 33 // lightmap resolution per face (second arg interpret as facenumber)
+#define PARM_GL_CONTEXT_TYPE 34 // opengl or opengles
+#define PARM_GLES_WRAPPER 35 //
enum
{
@@ -79,25 +83,6 @@ enum
typedef enum
{
- TEX_INVALID = 0, // free slot
- TEX_SYSTEM, // generated by engine
- TEX_NOMIP, // hud pics, menu etc
- TEX_BRUSH, // a map texture
- TEX_SPRITE, // sprite frames
- TEX_STUDIO, // studio skins
- TEX_LIGHTMAP, // lightmap textures
- TEX_DECAL, // decals
- TEX_VGUI, // vgui fonts or images
- TEX_CUBEMAP, // cubemap textures (sky)
- TEX_DETAIL, // detail textures
- TEX_REMAP, // local copy of remap texture
- TEX_SCREENCOPY, // keep screen copy e.g. for mirror
- TEX_CUSTOM, // user created texture
- TEX_DEPTHMAP // shadowmap texture
-} texType_t;
-
-typedef enum
-{
TF_NEAREST = (1<<0), // disable texfilter
TF_KEEP_RGBDATA = (1<<1), // some images keep source
TF_NOFLIP_TGA = (1<<2), // Steam background completely ignore tga attribute 0x20
@@ -106,7 +91,7 @@ typedef enum
TF_UNCOMPRESSED = (1<<5), // don't compress texture in video memory
TF_CUBEMAP = (1<<6), // it's cubemap texture
TF_DEPTHMAP = (1<<7), // custom texture filter used
- TF_INTENSITY = (1<<8), // monochrome intensity image
+// reserved
TF_LUMINANCE = (1<<9), // force image to grayscale
TF_SKYSIDE = (1<<10), // this is a part of skybox
TF_CLAMP = (1<<11), // clamp texcoords to [0..1] range
@@ -119,16 +104,29 @@ typedef enum
TF_TEXTURE_1D = (1<<18), // this is GL_TEXTURE_1D
TF_BORDER = (1<<19), // zero clamp for projected textures
TF_TEXTURE_3D = (1<<20), // this is GL_TEXTURE_3D
- TF_STATIC = (1<<21), // a marker for purge mechanism (not used by engine)
+// reserved
TF_TEXTURE_RECTANGLE= (1<<22), // this is GL_TEXTURE_RECTANGLE
- TF_ALPHA_BORDER = (1<<23), // clamp to (0,0,0,255) (probably no difference)
-
- TF_ALPHACONTRAST = (1<<25), // special texture flags for internal usage
- TF_FLOAT = (1<<26), // float textures
+// reserved
+ TF_TEXTURE_2D_ARRAY = (1<<24), // this is 2D texture array (multi-layers)
+ TF_IMG_UPLOADED = (1<<25), // this is set for first time when called glTexImage, otherwise it will be call glTexSubImage
+ TF_ARB_FLOAT = (1<<26), // float textures
TF_NOCOMPARE = (1<<27), // disable comparing for depth textures
- TF_FLOATDATA = (1<<28), // incoming dataType has type GL_FLOAT
+ TF_ARB_16BIT = (1<<28), // keep image as 16-bit (not 24)
} texFlags_t;
+typedef enum
+{
+ CONTEXT_TYPE_GL = 0,
+ CONTEXT_TYPE_GLES_1_X,
+ CONTEXT_TYPE_GLES_2_x
+} gl_context_type_t;
+
+typedef enum
+{
+ GLES_WRAPPER_NONE = 0, // native GLES
+ GLES_WRAPPER_NANOGL, // used on GLES platforms
+} gles_wrapper_t;
+
typedef struct beam_s BEAM;
typedef struct particle_s particle_t;
@@ -184,8 +182,8 @@ typedef struct render_api_s
const byte* (*GL_TextureData)( unsigned int texnum ); // may be NULL
int (*GL_LoadTexture)( const char *name, const byte *buf, size_t size, int flags );
int (*GL_CreateTexture)( const char *name, int width, int height, const void *buffer, int flags );
- void (*GL_SetTextureType)( unsigned int texnum, unsigned int type );
- void (*GL_TextureCacheFrame)( unsigned int texnum );
+ int (*GL_LoadTextureArray)( const char **names, int flags );
+ int (*GL_CreateTextureArray)( const char *name, int width, int height, int depth, const void *buffer, int flags );
void (*GL_FreeTexture)( unsigned int texnum );
// Decals manipulating (draw & remove)
@@ -211,11 +209,11 @@ typedef struct render_api_s
void (*GL_TexGen)( unsigned int coord, unsigned int mode );
void (*GL_TextureTarget)( unsigned int target ); // change texture unit mode without bind texture
void (*GL_TexCoordArrayMode)( unsigned int texmode );
+ void* (*GL_GetProcAddress)( const char *name );
void (*GL_Reserved0)( void ); // for potential interface expansion without broken compatibility
void (*GL_Reserved1)( void );
void (*GL_Reserved2)( void );
- void (*GL_Reserved3)( void );
-
+
// Misc renderer functions
void (*GL_DrawParticles)( const float *vieworg, const float *fwd, const float *rt, const float *up, unsigned int clipFlags );
void (*EnvShot)( const float *vieworg, const char *name, qboolean skyshot, int shotsize ); // creates a cubemap or skybox into gfx\env folder
@@ -255,8 +253,10 @@ typedef struct render_interface_s
qboolean (*R_SpeedsMessage)( char *out, size_t size );
// replace with built-in R_DrawCubemapView for make skyshots or envshots
qboolean (*R_DrawCubemapView)( const float *origin, const float *angles, int size );
- // alloc or destroy studiomodel custom data
+ // alloc or destroy model custom data
void (*Mod_ProcessUserData)( struct model_s *mod, qboolean create, const byte *buffer );
+ // alloc or destroy entity custom data
+ void (*R_ProcessEntData)( qboolean allocate );
} render_interface_t;
#endif//RENDER_API_H
\ No newline at end of file
diff --git b/common/wadfile.h a/common/wadfile.h
index ae85648..a7e0231 100644
--- b/common/wadfile.h
+++ a/common/wadfile.h
@@ -20,7 +20,7 @@
========================================================================
.WAD archive format (WhereAllData - WAD)
-List of compressed files, that can be identify only by TYPE_*
+List of compressed files, that can be identify only by TYP_*
<format>
header: dwadinfo_t[dwadinfo_t]
@@ -33,7 +33,7 @@ infotable dlumpinfo_t[dwadinfo_t->numlumps]
========================================================================
*/
-#define IDWAD3HEADER (('3'<<24)+('D'<<16)+('A'<<8)+'W')
+#define IDWAD3HEADER (('3'<<24)+('D'<<16)+('A'<<8)+'W') // little-endian "WAD3" half-life wads
// dlumpinfo_t->attribs
#define ATTR_NONE 0 // allow to read-write
diff --git b/engine/cdll_exp.h a/engine/cdll_exp.h
index bf43654..c059231 100644
--- b/engine/cdll_exp.h
+++ a/engine/cdll_exp.h
@@ -64,6 +64,8 @@ typedef struct cldll_func_s
// Xash3D extension
int (*pfnGetRenderInterface)( int version, render_api_t *renderfuncs, render_interface_t *callback );
void (*pfnClipMoveToEntity)( struct physent_s *pe, const vec3_t start, vec3_t mins, vec3_t maxs, const vec3_t end, struct pmtrace_s *tr );
+ void (*pfnUpdateEntityState)( cl_entity_t *ent, entity_state_t *newstate, int noInterp ); // custom interp
+ void (*pfnInterpolateEntity)( cl_entity_t *ent, float lerpFrac );
} cldll_func_t;
#endif//CDLL_EXP_H
\ No newline at end of file
diff --git b/engine/client/cl_cmds.c a/engine/client/cl_cmds.c
index af77f70..74b9e06 100644
--- b/engine/client/cl_cmds.c
+++ a/engine/client/cl_cmds.c
@@ -70,7 +70,8 @@ void CL_PlayCDTrack_f( void )
if( Cmd_Argc() < 2 ) return;
command = Cmd_Argv( 1 );
- if( !enabled && Q_stricmp( command, "on" )) return; // CD-player is disabled
+ if( !enabled && Q_stricmp( command, "on" ))
+ return; // CD-player is disabled
if( !Q_stricmp( command, "play" ))
{
@@ -135,15 +136,14 @@ void CL_PlayCDTrack_f( void )
CL_ScreenshotGetName
==================
*/
-void CL_ScreenshotGetName( int lastnum, char *filename )
+qboolean CL_ScreenshotGetName( int lastnum, char *filename )
{
int a, b, c, d;
if( lastnum < 0 || lastnum > 9999 )
{
- // bound
- Q_sprintf( filename, "scrshots/%s/!error.bmp", clgame.mapname );
- return;
+ MsgDev( D_ERROR, "unable to write screenshot\n" );
+ return false;
}
a = lastnum / 1000;
@@ -155,6 +155,8 @@ void CL_ScreenshotGetName( int lastnum, char *filename )
d = lastnum;
Q_sprintf( filename, "scrshots/%s_shot%i%i%i%i.bmp", clgame.mapname, a, b, c, d );
+
+ return true;
}
/*
@@ -216,7 +218,9 @@ void CL_ScreenShot_f( void )
// scan for a free filename
for( i = 0; i < 9999; i++ )
{
- CL_ScreenshotGetName( i, checkname );
+ if( !CL_ScreenshotGetName( i, checkname ))
+ return; // no namespace
+
if( !FS_FileExists( checkname, false ))
break;
}
diff --git b/engine/client/cl_demo.c a/engine/client/cl_demo.c
index c2f8765..8aeee5c 100644
--- b/engine/client/cl_demo.c
+++ a/engine/client/cl_demo.c
@@ -30,7 +30,7 @@ GNU General Public License for more details.
#define DEMO_NORMAL 1 // this lump contains playback info of messages, etc., needed during playback.
#define IDEMOHEADER (('M'<<24)+('E'<<16)+('D'<<8)+'I') // little-endian "IDEM"
-#define DEMO_PROTOCOL 1
+#define DEMO_PROTOCOL 2
const char *demo_cmd[dem_lastcmd+1] =
{
@@ -49,6 +49,7 @@ typedef struct
int dem_protocol; // should be DEMO_PROTOCOL
int net_protocol; // should be PROTOCOL_VERSION
char mapname[64]; // name of map
+ char comment[64]; // comment for demo
char gamedir[64]; // name of game directory (FS_Gamedir())
int directory_offset; // offset of Entry Directory.
} demoheader_t;
@@ -68,6 +69,13 @@ typedef struct
int numentries; // number of tracks
} demodirectory_t;
+// add angles
+typedef struct
+{
+ float starttime;
+ vec3_t viewangles;
+} demoangle_t;
+
// private demo states
struct
{
@@ -77,7 +85,13 @@ struct
int framecount;
float starttime;
float realstarttime;
+ float timestamp;
+ float lasttime;
int entryIndex;
+
+ // interpolation stuff
+ demoangle_t cmds[ANGLE_BACKUP];
+ int angle_position;
} demo;
/*
@@ -261,11 +275,7 @@ void CL_WriteDemoMessage( qboolean startup, int start, sizebuf_t *msg )
swlen = MSG_GetNumBytesWritten( msg ) - start;
if( swlen <= 0 ) return;
- if( !startup )
- {
- cls.demotime += host.frametime;
- demo.framecount++;
- }
+ if( !startup ) demo.framecount++;
// demo playback should read this as an incoming message.
c = (cls.state != ca_active) ? dem_norewind : dem_read;
@@ -313,9 +323,9 @@ Write demo header
*/
void CL_WriteDemoHeader( const char *name )
{
- fs_offset_t copysize;
- fs_offset_t savepos;
- fs_offset_t curpos;
+ long copysize;
+ long savepos;
+ long curpos;
MsgDev( D_INFO, "recording to %s.\n", name );
cls.demofile = FS_Open( name, "wb", false );
@@ -336,6 +346,7 @@ void CL_WriteDemoHeader( const char *name )
demo.header.dem_protocol = DEMO_PROTOCOL;
demo.header.net_protocol = PROTOCOL_VERSION;
Q_strncpy( demo.header.mapname, clgame.mapname, sizeof( demo.header.mapname ));
+ Q_strncpy( demo.header.comment, clgame.maptitle, sizeof( demo.header.comment ));
Q_strncpy( demo.header.gamedir, FS_Gamedir(), sizeof( demo.header.gamedir ));
// write header
@@ -420,9 +431,7 @@ void CL_StopRecord( void )
FS_Write( cls.demofile, &demo.directory.numentries, sizeof( int ));
for( i = 0; i < demo.directory.numentries; i++ )
- {
FS_Write( cls.demofile, &demo.directory.entries[i], sizeof( demoentry_t ));
- }
Mem_Free( demo.directory.entries );
demo.directory.numentries = 0;
@@ -438,7 +447,7 @@ void CL_StopRecord( void )
gameui.globals->demoname[0] = '\0';
Msg( "Completed demo\n" );
- MsgDev( D_INFO, "Recording time %.2f\n", cls.demotime );
+ MsgDev( D_INFO, "Recording time: %02d:%02d", (int)(cls.demotime / 60.0f ), (int)fmod( cls.demotime, 60.0f ));
cls.demotime = 0.0;
}
@@ -449,16 +458,17 @@ CL_DrawDemoRecording
*/
void CL_DrawDemoRecording( void )
{
- char string[64];
- rgba_t color = { 255, 255, 255, 255 };
- fs_offset_t pos;
- int len;
+ char string[64];
+ rgba_t color = { 255, 255, 255, 255 };
+ long pos;
+ int len;
if(!( host.developer && cls.demorecording ))
return;
pos = FS_Tell( cls.demofile );
- Q_snprintf( string, sizeof( string ), "RECORDING %s: %ik", cls.demoname, pos / 1024 );
+ Q_snprintf( string, sizeof( string ), "^1RECORDING:^7 %s: %s time: %02d:%02d", cls.demoname,
+ Q_memprint( pos ), (int)(cls.demotime / 60.0f ), (int)fmod( cls.demotime, 60.0f ));
Con_DrawStringLen( string, &len, NULL );
Con_DrawString(( scr_width->integer - len) >> 1, scr_height->integer >> 2, string, color );
@@ -511,8 +521,9 @@ void CL_ReadDemoUserCmd( qboolean discard )
if( !discard )
{
- usercmd_t nullcmd;
- sizebuf_t buf;
+ usercmd_t nullcmd;
+ sizebuf_t buf;
+ demoangle_t *a;
memset( &nullcmd, 0, sizeof( nullcmd ));
MSG_Init( &buf, "UserCmd", data, sizeof( data ));
@@ -530,6 +541,20 @@ void CL_ReadDemoUserCmd( qboolean discard )
MSG_ReadDeltaUsercmd( &buf, &nullcmd, cl.refdef.cmd );
+ // make sure what interp info contain angles from different frames
+ // or lerping will stop working
+ if( demo.lasttime != demo.timestamp )
+ {
+ // select entry into circular buffer
+ demo.angle_position = (demo.angle_position + 1) & ANGLE_MASK;
+ a = &demo.cmds[demo.angle_position];
+
+ // record update
+ a->starttime = demo.timestamp;
+ VectorCopy( cl.refdef.cmd->viewangles, a->viewangles );
+ demo.lasttime = demo.timestamp;
+ }
+
// NOTE: we need to have the current outgoing sequence correct
// so we can do prediction correctly during playback
cls.netchan.outgoing_sequence = outgoing_sequence;
@@ -674,7 +699,6 @@ reads demo data and write it to client
*/
qboolean CL_DemoReadMessage( byte *buffer, size_t *length )
{
- float f = 0.0f;
long curpos = 0;
float fElapsedTime = 0.0f;
qboolean swallowmessages = true;
@@ -690,8 +714,7 @@ qboolean CL_DemoReadMessage( byte *buffer, size_t *length )
}
// HACKHACK: changedemo issues
- if( !cls.netchan.remote_address.type )
- cls.netchan.remote_address.type = NA_LOOPBACK;
+ if( !cls.netchan.remote_address.type ) cls.netchan.remote_address.type = NA_LOOPBACK;
if(( !cl.background && ( cl.refdef.paused || cls.key_dest != key_game )) || cls.key_dest == key_console )
{
@@ -706,16 +729,14 @@ qboolean CL_DemoReadMessage( byte *buffer, size_t *length )
if( !cls.demofile ) break;
curpos = FS_Tell( cls.demofile );
- CL_ReadDemoCmdHeader( &cmd, &f );
+ CL_ReadDemoCmdHeader( &cmd, &demo.timestamp );
fElapsedTime = CL_GetDemoPlaybackClock() - demo.starttime;
- bSkipMessage = (f >= fElapsedTime) ? true : false;
+ bSkipMessage = ((demo.timestamp - cl_serverframetime()) >= fElapsedTime) ? true : false;
+ if( cls.changelevel ) demo.framecount = 1;
- if( cls.changelevel )
- demo.framecount = 1;
-
- // HACKHACK: changelevel issues
- if( demo.framecount <= 10 && ( fElapsedTime - f ) > host.frametime )
+ // changelevel issues
+ if( demo.framecount <= 2 && ( fElapsedTime - demo.timestamp ) > host.frametime )
demo.starttime = CL_GetDemoPlaybackClock();
// not ready for a message yet, put it back on the file.
@@ -778,6 +799,79 @@ qboolean CL_DemoReadMessage( byte *buffer, size_t *length )
return CL_ReadRawNetworkData( buffer, length );
}
+void CL_DemoFindInterpolatedViewAngles( float t, float *frac, demoangle_t **prev, demoangle_t **next )
+{
+ int i, i0, i1, imod;
+ float at;
+
+ imod = demo.angle_position - 1;
+ i0 = (imod + 1) & ANGLE_MASK;
+ i1 = (imod + 0) & ANGLE_MASK;
+
+ if( demo.cmds[i0].starttime >= t )
+ {
+ for( i = 0; i < ANGLE_BACKUP - 2; i++ )
+ {
+ at = demo.cmds[imod & ANGLE_MASK].starttime;
+ if( at == 0.0f ) break;
+
+ if( at < t )
+ {
+ i0 = (imod + 1) & ANGLE_MASK;
+ i1 = (imod + 0) & ANGLE_MASK;
+ break;
+ }
+ imod--;
+ }
+ }
+
+ *next = &demo.cmds[i0];
+ *prev = &demo.cmds[i1];
+
+ // avoid division by zero (probably this should never happens)
+ if((*prev)->starttime == (*next)->starttime )
+ {
+ *prev = *next;
+ *frac = 0.0f;
+ return;
+ }
+
+ // time spans the two entries
+ *frac = ( t - (*prev)->starttime ) / ((*next)->starttime - (*prev)->starttime );
+ *frac = bound( 0.0f, *frac, 1.0f );
+}
+
+/*
+==============
+CL_DemoInterpolateAngles
+
+We can predict or inpolate player movement with standed client code
+but viewangles interpolate here
+==============
+*/
+void CL_DemoInterpolateAngles( void )
+{
+ float curtime = (CL_GetDemoPlaybackClock() - demo.starttime) - host.frametime;
+ demoangle_t *prev = NULL, *next = NULL;
+ float frac = 0.0f;
+
+ if( curtime > demo.timestamp )
+ curtime = demo.timestamp; // don't run too far
+
+ CL_DemoFindInterpolatedViewAngles( curtime, &frac, &prev, &next );
+
+ if( prev && next )
+ {
+ vec4_t q, q1, q2;
+
+ AngleQuaternion( next->viewangles, q1, false );
+ AngleQuaternion( prev->viewangles, q2, false );
+ QuaternionSlerp( q2, q1, frac, q );
+ QuaternionAngle( q, cl.refdef.cl_viewangles );
+ }
+ else VectorCopy( cl.refdef.cmd->viewangles, cl.refdef.cl_viewangles );
+}
+
/*
==============
CL_StopPlayback
@@ -804,16 +898,21 @@ void CL_StopPlayback( void )
cls.demoname[0] = '\0'; // clear demoname too
gameui.globals->demoname[0] = '\0';
- S_StopAllSounds();
- S_StopBackgroundTrack();
-
- if( !cls.changedemo )
+ if( cls.changedemo )
+ {
+ S_StopAllSounds();
+ S_StopBackgroundTrack();
+ }
+ else
{
// let game known about demo state
Cvar_FullSet( "cl_background", "0", CVAR_READ_ONLY );
cls.state = ca_disconnected;
- cl.background = 0;
+ cls.connect_time = 0;
cls.demonum = -1;
+
+ // and finally clear the state
+ CL_ClearState ();
}
}
@@ -876,7 +975,7 @@ qboolean CL_GetComment( const char *demoname, char *comment )
// split comment to sections
Q_strncpy( comment, demohdr.mapname, CS_SIZE );
- Q_strncpy( comment + CS_SIZE, "<No Title>", CS_SIZE ); // TODO: write titles or somewhat
+ Q_strncpy( comment + CS_SIZE, demohdr.comment, CS_SIZE );
Q_strncpy( comment + CS_SIZE * 2, va( "%g sec", playtime ), CS_TIME );
// all done
@@ -889,8 +988,7 @@ qboolean CL_GetComment( const char *demoname, char *comment )
==================
CL_NextDemo
-Called when a demo or cinematic finishes
-If the "nextdemo" cvar is set, that command will be issued
+Called when a demo finishes
==================
*/
qboolean CL_NextDemo( void )
@@ -1086,14 +1184,11 @@ void CL_PlayDemo_f( void )
if( demo.header.net_protocol != PROTOCOL_VERSION || demo.header.dem_protocol != DEMO_PROTOCOL )
{
- MsgDev( D_ERROR, "demo protocol outdated\n"
- "Demo file protocols Network(%i), Demo(%i)\n"
- "Server protocol is at Network(%i), Demo(%i)\n",
- demo.header.net_protocol,
- demo.header.dem_protocol,
- PROTOCOL_VERSION,
- DEMO_PROTOCOL
- );
+ if( demo.header.dem_protocol != DEMO_PROTOCOL )
+ MsgDev( D_ERROR, "playdemo: demo protocol outdated (%i should be %i)\n", demo.header.dem_protocol, DEMO_PROTOCOL );
+
+ if( demo.header.net_protocol != PROTOCOL_VERSION )
+ MsgDev( D_ERROR, "playdemo: net protocol outdated (%i should be %i)\n", demo.header.net_protocol, PROTOCOL_VERSION );
FS_Close( cls.demofile );
cls.demofile = NULL;
@@ -1129,7 +1224,7 @@ void CL_PlayDemo_f( void )
CL_Disconnect();
Host_ShutdownServer();
- Con_Close();
+ Con_FastClose();
UI_SetActiveMenu( false );
}
@@ -1154,9 +1249,12 @@ void CL_PlayDemo_f( void )
Netchan_Setup( NS_CLIENT, &cls.netchan, net_from, Cvar_VariableValue( "net_qport" ));
+ memset( demo.cmds, 0, sizeof( demo.cmds ));
+ demo.angle_position = 1;
demo.framecount = 0;
cls.lastoutgoingcommand = -1;
cls.nextcmdtime = host.realtime;
+ cl.last_command_ack = -1;
// g-cont. is this need?
Q_strncpy( cls.servername, demoname, sizeof( cls.servername ));
diff --git b/engine/client/cl_events.c a/engine/client/cl_events.c
index 7b023c5..a6c9ee3 100644
--- b/engine/client/cl_events.c
+++ a/engine/client/cl_events.c
@@ -142,7 +142,7 @@ qboolean CL_FireEvent( event_info_t *ei )
if( !ev )
{
- idx = bound( 1, ei->index, MAX_EVENTS );
+ idx = bound( 1, ei->index, ( MAX_EVENTS - 1 ));
MsgDev( D_ERROR, "CL_FireEvent: %s not precached\n", cl.event_precache[idx] );
break;
}
@@ -173,10 +173,9 @@ called right before draw frame
*/
void CL_FireEvents( void )
{
- int i;
event_state_t *es;
event_info_t *ei;
- qboolean success;
+ int i;
es = &cl.events;
@@ -191,7 +190,7 @@ void CL_FireEvents( void )
if( ei->fire_time && ( ei->fire_time > cl.time ))
continue;
- success = CL_FireEvent( ei );
+ CL_FireEvent( ei );
// zero out the remaining fields
CL_ResetEvent( ei );
@@ -395,8 +394,15 @@ void CL_ParseEvent( sizebuf_t *msg )
args.angles[PITCH] = -state->angles[PITCH] * 3;
args.angles[YAW] = state->angles[YAW];
args.angles[ROLL] = 0; // no roll
+
+ if( VectorIsNull( args.origin ))
+ VectorCopy( state->origin, args.origin );
+ if( VectorIsNull( args.velocity ))
+ VectorCopy( state->velocity, args.velocity );
}
- }
+
+ COM_NormalizeAngles( args.angles );
+ }
else if( state )
{
if( VectorIsNull( args.origin ))
@@ -443,6 +449,13 @@ void CL_PlaybackEvent( int flags, const edict_t *pInvoker, word eventindex, floa
MsgDev( D_ERROR, "CL_PlaybackEvent: invalid eventindex %i\n", eventindex );
return;
}
+
+ if( flags & FEV_SERVER )
+ {
+ MsgDev( D_WARN, "CL_PlaybackEvent: event with FEV_SERVER flag!\n" );
+ return;
+ }
+
// check event for precached
if( !CL_EventIndex( cl.event_precache[eventindex] ))
{
@@ -459,15 +472,27 @@ void CL_PlaybackEvent( int flags, const edict_t *pInvoker, word eventindex, floa
args.flags = 0;
args.entindex = invokerIndex;
-// TODO: restore checks when predicting will be done
-// if( !angles || VectorIsNull( angles ))
+ if( !angles || VectorIsNull( angles ))
VectorCopy( cl.refdef.cl_viewangles, args.angles );
+ else VectorCopy( angles, args.angles );
-// if( !origin || VectorIsNull( origin ))
- VectorCopy( cl.frame.client.origin, args.origin );
+ if( !origin || VectorIsNull( origin ))
+ {
+ if( CL_IsPredicted( )) VectorCopy( cl.predicted.origin, args.origin );
+ else VectorCopy( cl.frame.client.origin, args.origin );
+ }
+ else VectorCopy( origin, args.origin );
- VectorCopy( cl.frame.client.velocity, args.velocity );
- args.ducking = cl.frame.client.bInDuck;
+ if( CL_IsPredicted( ))
+ {
+ VectorCopy( cl.predicted.velocity, args.velocity );
+ args.ducking = (cl.predicted.usehull == 1);
+ }
+ else
+ {
+ VectorCopy( cl.frame.client.velocity, args.velocity );
+ args.ducking = cl.frame.client.bInDuck;
+ }
args.fparam1 = fparam1;
args.fparam2 = fparam2;
diff --git b/engine/client/cl_frame.c a/engine/client/cl_frame.c
index f1f1529..de18211 100644
--- b/engine/client/cl_frame.c
+++ a/engine/client/cl_frame.c
@@ -38,7 +38,7 @@ qboolean CL_IsPredicted( void )
if( !cl_predict->integer || !cl.frame.valid || cl.background )
return false;
- if(( cls.netchan.outgoing_sequence - cls.netchan.incoming_acknowledged ) >= ( CL_UPDATE_BACKUP - 1 ))
+ if( !cl.validsequence || ( cls.netchan.outgoing_sequence - cls.netchan.incoming_acknowledged ) >= CL_UPDATE_MASK )
return false;
return true;
@@ -123,7 +123,7 @@ qboolean CL_FindInterpolationUpdates( cl_entity_t *ent, float targettime, positi
int CL_InterpolateModel( cl_entity_t *e )
{
- position_history_t *ph0, *ph1;
+ position_history_t *ph0 = NULL, *ph1 = NULL;
vec3_t origin, angles, delta;
float t, t1, t2, frac;
int i;
@@ -131,13 +131,20 @@ int CL_InterpolateModel( cl_entity_t *e )
VectorCopy( e->curstate.origin, e->origin );
VectorCopy( e->curstate.angles, e->angles );
- if( e->model == NULL || cl.maxclients <= 1 )
+ if( cl.first_frame ) return 0;
+
+ if( !e->model || ( e->model->name[0] == '*' && !cl_bmodelinterp->integer ) || RP_LOCALCLIENT( e ) || cl.maxclients <= 1 )
+ return 1;
+
+ if( cl.predicted.moving && cl.predicted.onground == e->index )
return 1;
+ if( e->curstate.starttime != 0.0f && e->curstate.impacttime != 0.0f )
+ return 1; // don't interpolate parametric entities
+
t = cl.time - cl_interp->value;
- if( !CL_FindInterpolationUpdates( e, t, &ph0, &ph1, NULL ))
- return 0;
+ CL_FindInterpolationUpdates( e, t, &ph0, &ph1, NULL );
if( ph0 == NULL || ph1 == NULL )
return 0;
@@ -148,7 +155,7 @@ int CL_InterpolateModel( cl_entity_t *e )
if( t - t2 < 0.0f )
return 0;
- if( t2 == 0.0f || VectorIsNull( ph1->origin ) && !VectorIsNull( ph0->origin ))
+ if( t2 == 0.0f || ( VectorIsNull( ph1->origin ) && !VectorIsNull( ph0->origin )))
{
VectorCopy( ph0->origin, e->origin );
VectorCopy( ph0->angles, e->angles );
@@ -193,6 +200,36 @@ int CL_InterpolateModel( cl_entity_t *e )
return 1;
}
+void CL_InterpolateMovingEntity( cl_entity_t *ent )
+{
+ float d, f = 0.0f;
+ int i;
+
+ // don't do it if the goalstarttime hasn't updated in a while.
+ // NOTE: Because we need to interpolate multiplayer characters, the interpolation time limit
+ // was increased to 1.0 s., which is 2x the max lag we are accounting for.
+ if(( cl.time < ent->curstate.animtime + 1.0f ) && ( ent->curstate.animtime != ent->latched.prevanimtime ))
+ f = ( cl.time - ent->curstate.animtime ) / ( ent->curstate.animtime - ent->latched.prevanimtime );
+
+ f = f - 1.0f;
+
+ ent->origin[0] += ( ent->origin[0] - ent->latched.prevorigin[0] ) * f;
+ ent->origin[1] += ( ent->origin[1] - ent->latched.prevorigin[1] ) * f;
+ ent->origin[2] += ( ent->origin[2] - ent->latched.prevorigin[2] ) * f;
+
+ for( i = 0; i < 3; i++ )
+ {
+ float ang1, ang2;
+
+ ang1 = ent->angles[i];
+ ang2 = ent->latched.prevangles[i];
+ d = ang1 - ang2;
+ if( d > 180.0f ) d -= 360.0f;
+ else if( d < -180.0f ) d += 360.0f;
+ ent->angles[i] += d * f;
+ }
+}
+
void CL_UpdateEntityFields( cl_entity_t *ent )
{
// parametric rockets code
@@ -217,8 +254,12 @@ void CL_UpdateEntityFields( cl_entity_t *ent )
if( ent->player && RP_LOCALCLIENT( ent )) // stupid Half-Life bug
ent->angles[PITCH] = -ent->angles[PITCH] / 3.0f;
- // make me lerp
- if( ent->model && ent->model->type == mod_brush && ent->curstate.animtime != 0.0f )
+ // make me lerp (multiplayer only. this code visually breaks XashXT parent system)
+ if( ent->index == cl.predicted.onground && cl.predicted.moving && ( cl.maxclients > 1 ))
+ {
+ CL_InterpolateMovingEntity( ent );
+ }
+ else if( ent->model && ent->model->type == mod_brush && ent->curstate.animtime != 0.0f )
{
float d, f = 0.0f;
int i;
@@ -324,7 +365,7 @@ void CL_UpdateEntityFields( cl_entity_t *ent )
}
}
- // move code from StudioSetupTransform here
+ // moved code from StudioSetupTransform here
if( host.features & ENGINE_COMPUTE_STUDIO_LERP )
{
ent->origin[0] += ( ent->curstate.origin[0] - ent->latched.prevorigin[0] ) * f;
@@ -513,7 +554,8 @@ void CL_WeaponAnim( int iAnim, int body )
{
cl_entity_t *view = &clgame.viewent;
- view->curstate.modelindex = cl.frame.client.viewmodel;
+ cl.weaponstarttime = 0;
+ cl.weaponsequence = iAnim;
// anim is changed. update latchedvars
if( iAnim != view->curstate.sequence )
@@ -538,6 +580,9 @@ void CL_WeaponAnim( int iAnim, int body )
view->curstate.frame = 0.0f;
view->curstate.body = body;
+ view->curstate.rendermode = kRenderNormal;
+ view->curstate.renderamt = 255;
+
#if 0 // g-cont. for GlowShell testing
view->curstate.renderfx = kRenderFxGlowShell;
view->curstate.rendercolor.r = 255;
@@ -652,8 +697,16 @@ void CL_DeltaEntity( sizebuf_t *msg, frame_t *frame, int newnum, entity_state_t
qboolean newent = (old) ? false : true;
qboolean result = true;
- ent = CL_EDICT_NUM( newnum );
state = &cls.packet_entities[cls.next_client_entities % cls.num_client_entities];
+
+ if(( newnum < 0 ) || ( newnum >= clgame.maxEntities ))
+ {
+ if( !unchanged )
+ MSG_ReadDeltaEntity( msg, old, state, newnum, CL_IsPlayerIndex( newnum ), cl.mtime[0] );
+ return;
+ }
+
+ ent = CL_EDICT_NUM( newnum );
ent->index = newnum;
if( newent ) old = &ent->baseline;
@@ -663,7 +716,21 @@ void CL_DeltaEntity( sizebuf_t *msg, frame_t *frame, int newnum, entity_state_t
if( !result )
{
- if( newent ) Host_Error( "Cl_DeltaEntity: tried to release new entity\n" );
+ if( newent )
+ {
+ MsgDev( D_WARN, "Cl_DeltaEntity: tried to release new entity\n" );
+
+ // perform remove, entity was created and removed between packets
+ if( state->number == -1 )
+ {
+ MsgDev( D_NOTE, "Entity %i was removed from server\n", newnum );
+ ent->curstate.messagenum = 0;
+ ent->baseline.number = 0;
+ }
+ else MsgDev( D_NOTE, "Entity %i was removed from delta-message\n", newnum );
+
+ return;
+ }
CL_KillDeadBeams( ent ); // release dead beams
#if 0
@@ -703,14 +770,21 @@ void CL_DeltaEntity( sizebuf_t *msg, frame_t *frame, int newnum, entity_state_t
ent->prevstate = ent->curstate;
}
- // NOTE: always check modelindex for new state not current
- if( Mod_GetType( state->modelindex ) == mod_studio )
+ if( clgame.dllFuncs.pfnUpdateEntityState != NULL )
{
- CL_UpdateStudioVars( ent, state, newent );
+ clgame.dllFuncs.pfnUpdateEntityState( ent, state, newent );
}
- else if( Mod_GetType( state->modelindex ) == mod_brush )
+ else
{
- CL_UpdateBmodelVars( ent, state, newent );
+ // NOTE: always check modelindex for new state not current
+ if( Mod_GetType( state->modelindex ) == mod_studio )
+ {
+ CL_UpdateStudioVars( ent, state, newent );
+ }
+ else if( Mod_GetType( state->modelindex ) == mod_brush )
+ {
+ CL_UpdateBmodelVars( ent, state, newent );
+ }
}
// set right current state
@@ -722,6 +796,8 @@ void CL_DeltaEntity( sizebuf_t *msg, frame_t *frame, int newnum, entity_state_t
/*
=================
CL_FlushEntityPacket
+
+Read and ignore whole entity packet.
=================
*/
void CL_FlushEntityPacket( sizebuf_t *msg )
@@ -729,7 +805,6 @@ void CL_FlushEntityPacket( sizebuf_t *msg )
int newnum;
entity_state_t from, to;
- MsgDev( D_INFO, "FlushEntityPacket()\n" );
memset( &from, 0, sizeof( from ));
cl.frames[cl.parsecountmod].valid = false;
@@ -756,11 +831,13 @@ An svc_packetentities has just been parsed, deal with the
rest of the data stream.
==================
*/
-void CL_ParsePacketEntities( sizebuf_t *msg, qboolean delta )
+int CL_ParsePacketEntities( sizebuf_t *msg, qboolean delta )
{
frame_t *newframe, *oldframe;
int oldindex, newnum, oldnum;
+ int playerbytes = 0;
int oldpacket;
+ int bufStart;
cl_entity_t *player;
entity_state_t *oldent;
int i, count;
@@ -778,6 +855,7 @@ void CL_ParsePacketEntities( sizebuf_t *msg, qboolean delta )
newframe->first_entity = cls.next_client_entities;
newframe->num_entities = 0;
newframe->valid = true; // assume valid
+ memset( &newframe->graphdata, 0, sizeof( netbandwidthgraph_t ));
if( delta )
{
@@ -788,25 +866,29 @@ void CL_ParsePacketEntities( sizebuf_t *msg, qboolean delta )
if( subtracted == 0 )
{
- Host_Error( "CL_DeltaPacketEntities: update too old, connection dropped.\n" );
- return;
+ MsgDev( D_NOTE, "CL_DeltaPacketEntities: update too old (flush)\n" );
+ Con_NPrintf( 2, "^3Warning:^1 update too old\n^7\n" );
+ CL_FlushEntityPacket( msg );
+ return playerbytes;
}
if( subtracted >= CL_UPDATE_MASK )
{
// we can't use this, it is too old
+ MsgDev( D_NOTE, "CL_ParsePacketEntities: delta frame is too old: overflow (flush)\n");
Con_NPrintf( 2, "^3Warning:^1 delta frame is too old^7\n" );
CL_FlushEntityPacket( msg );
- return;
+ return playerbytes;
}
oldframe = &cl.frames[oldpacket & CL_UPDATE_MASK];
if(( cls.next_client_entities - oldframe->first_entity ) > ( cls.num_client_entities - 128 ))
{
+ MsgDev( D_NOTE, "CL_ParsePacketEntities: delta frame is too old (flush)\n");
Con_NPrintf( 2, "^3Warning:^1 delta frame is too old^7\n" );
CL_FlushEntityPacket( msg );
- return;
+ return playerbytes;
}
}
else
@@ -814,7 +896,7 @@ void CL_ParsePacketEntities( sizebuf_t *msg, qboolean delta )
// this is a full update that we can start delta compressing from now
oldframe = NULL;
oldpacket = -1; // delta too old or is initial message
- cl.force_send_usercmd = true; // send reply
+ cl.send_reply = true; // send reply
cls.demowaiting = false; // we can start recording now
}
@@ -851,8 +933,11 @@ void CL_ParsePacketEntities( sizebuf_t *msg, qboolean delta )
while( oldnum < newnum )
{
+ bufStart = MSG_GetNumBytesRead( msg );
// one or more entities from the old packet are unchanged
CL_DeltaEntity( msg, newframe, oldnum, oldent, true );
+ if( CL_IsPlayerIndex( oldnum ) )
+ playerbytes += MSG_GetNumBytesRead( msg ) - bufStart;
oldindex++;
@@ -870,7 +955,10 @@ void CL_ParsePacketEntities( sizebuf_t *msg, qboolean delta )
if( oldnum == newnum )
{
// delta from previous state
+ bufStart = MSG_GetNumBytesRead( msg );
CL_DeltaEntity( msg, newframe, newnum, oldent, false );
+ if( CL_IsPlayerIndex( newnum ) )
+ playerbytes += MSG_GetNumBytesRead( msg ) - bufStart;
oldindex++;
if( oldindex >= oldframe->num_entities )
@@ -888,7 +976,10 @@ void CL_ParsePacketEntities( sizebuf_t *msg, qboolean delta )
if( oldnum > newnum )
{
// delta from baseline ?
+ bufStart = MSG_GetNumBytesRead( msg );
CL_DeltaEntity( msg, newframe, newnum, NULL, false );
+ if( CL_IsPlayerIndex( newnum ) )
+ playerbytes += MSG_GetNumBytesRead( msg ) - bufStart;
continue;
}
}
@@ -897,7 +988,10 @@ void CL_ParsePacketEntities( sizebuf_t *msg, qboolean delta )
while( oldnum != MAX_ENTNUMBER )
{
// one or more entities from the old packet are unchanged
+ bufStart = MSG_GetNumBytesRead( msg );
CL_DeltaEntity( msg, newframe, oldnum, oldent, true );
+ if( CL_IsPlayerIndex( oldnum ) )
+ playerbytes += MSG_GetNumBytesRead( msg ) - bufStart;
oldindex++;
if( oldindex >= oldframe->num_entities )
@@ -916,7 +1010,7 @@ void CL_ParsePacketEntities( sizebuf_t *msg, qboolean delta )
cl.frame = *newframe;
- if( !cl.frame.valid ) return;
+ if( !cl.frame.valid ) return playerbytes; // frame is not valid but message was parsed
player = CL_GetLocalPlayer();
@@ -950,11 +1044,15 @@ void CL_ParsePacketEntities( sizebuf_t *msg, qboolean delta )
if(( cls.demoplayback || cls.disable_servercount != cl.servercount ) && cl.video_prepped )
SCR_EndLoadingPlaque(); // get rid of loading plaque
+ cl.first_frame = true; // first server frame received
}
else
{
CL_CheckPredictionError();
+ cl.first_frame = false;
}
+
+ return playerbytes;
}
/*
@@ -972,23 +1070,34 @@ CL_SetIdealPitch
void CL_SetIdealPitch( void )
{
float angleval, sinval, cosval;
- vec3_t top, bottom;
- float z[MAX_FORWARD];
+ float z[MAX_FORWARD], view_z;
+ vec3_t top, bottom, origin;
int i, j;
int step, dir, steps;
pmtrace_t tr;
if( !( cl.frame.client.flags & FL_ONGROUND ))
return;
-
+
+ if( CL_IsPredicted( ))
+ {
+ VectorCopy( cl.predicted.origin, origin );
+ view_z = cl.predicted.viewofs[2];
+ }
+ else
+ {
+ VectorCopy( cl.frame.client.origin, origin );
+ view_z = cl.frame.client.view_ofs[2];
+ }
+
angleval = cl.frame.playerstate[cl.playernum].angles[YAW] * M_PI2 / 360.0f;
SinCos( angleval, &sinval, &cosval );
for( i = 0; i < MAX_FORWARD; i++ )
{
- top[0] = cl.frame.client.origin[0] + cosval * (i + 3.0f) * 12.0f;
- top[1] = cl.frame.client.origin[1] + sinval * (i + 3.0f) * 12.0f;
- top[2] = cl.frame.client.origin[2] + cl.frame.client.view_ofs[2];
+ top[0] = origin[0] + cosval * (i + 3.0f) * 12.0f;
+ top[1] = origin[1] + sinval * (i + 3.0f) * 12.0f;
+ top[2] = origin[2] + view_z;
bottom[0] = top[0];
bottom[1] = top[1];
@@ -1052,7 +1161,9 @@ void CL_AddPacketEntities( frame_t *frame )
if( !ent || ent == clgame.entities )
continue;
- CL_UpdateEntityFields( ent );
+ if( clgame.dllFuncs.pfnInterpolateEntity != NULL )
+ clgame.dllFuncs.pfnInterpolateEntity( ent, cl.lerpFrac );
+ else CL_UpdateEntityFields( ent );
if( ent->player ) entityType = ET_PLAYER;
else if( ent->curstate.entityType == ENTITY_BEAM )
diff --git b/engine/client/cl_game.c a/engine/client/cl_game.c
index 613cbc7..2d6b729 100644
--- b/engine/client/cl_game.c
+++ a/engine/client/cl_game.c
@@ -30,6 +30,7 @@ GNU General Public License for more details.
#include "vgui_draw.h"
#include "sound.h" // SND_STOP_LOOPING
+#define MAX_LINELENGTH 80
#define MAX_TEXTCHANNELS 8 // must be power of two (GoldSrc uses 4 channels)
#define TEXT_MSGNAME "TextMessage%i"
@@ -88,6 +89,8 @@ static dllfunc_t cdll_new_exports[] = // allowed only in SDK 2.3 and higher
{ "HUD_GetRenderInterface", (void **)&clgame.dllFuncs.pfnGetRenderInterface }, // Xash3D ext
{ "HUD_GetPlayerTeam", (void **)&clgame.dllFuncs.pfnGetPlayerTeam },
{ "HUD_ClipMoveToEntity", (void **)&clgame.dllFuncs.pfnClipMoveToEntity }, // Xash3D ext
+{ "HUD_UpdateEntityState", (void **)&clgame.dllFuncs.pfnUpdateEntityState },
+{ "HUD_InterpolateEntity", (void **)&clgame.dllFuncs.pfnInterpolateEntity },
{ NULL, NULL }
};
@@ -406,30 +409,6 @@ static void SPR_AdjustSize( float *x, float *y, float *w, float *h )
/*
====================
-TextAdjustSize
-
-draw hudsprite routine
-====================
-*/
-void TextAdjustSize( int *x, int *y, int *w, int *h )
-{
- float xscale, yscale;
-
- if( !clgame.ds.adjust_size ) return;
- if( !x && !y && !w && !h ) return;
-
- // scale for screen sizes
- xscale = scr_width->integer / (float)clgame.scrInfo.iWidth;
- yscale = scr_height->integer / (float)clgame.scrInfo.iHeight;
-
- if( x ) *x *= xscale;
- if( y ) *y *= yscale;
- if( w ) *w *= xscale;
- if( h ) *h *= yscale;
-}
-
-/*
-====================
PictAdjustSize
draw hudsprite routine
@@ -573,7 +552,7 @@ void CL_DrawCenterPrint( void )
char *pText;
int i, j, x, y;
int width, lineLength;
- byte *colorDefault, line[80];
+ byte *colorDefault, line[MAX_LINELENGTH];
int charWidth, charHeight;
if( !clgame.centerPrint.time )
@@ -596,7 +575,7 @@ void CL_DrawCenterPrint( void )
lineLength = 0;
width = 0;
- while( *pText && *pText != '\n' )
+ while( *pText && *pText != '\n' && lineLength < MAX_LINELENGTH )
{
byte c = *pText;
line[lineLength] = c;
@@ -606,6 +585,9 @@ void CL_DrawCenterPrint( void )
pText++;
}
+ if( lineLength == MAX_LINELENGTH )
+ lineLength--;
+
pText++; // Skip LineFeed
line[lineLength] = 0;
@@ -1084,8 +1066,10 @@ void CL_InitEdicts( void )
{
ASSERT( clgame.entities == NULL );
+ if( !clgame.mempool ) return; // Host_Error without client
+
CL_UPDATE_BACKUP = ( cl.maxclients == 1 ) ? SINGLEPLAYER_BACKUP : MULTIPLAYER_BACKUP;
- cls.num_client_entities = CL_UPDATE_BACKUP * 64;
+ cls.num_client_entities = CL_UPDATE_BACKUP * NUM_PACKET_ENTITIES;
cls.packet_entities = Z_Realloc( cls.packet_entities, sizeof( entity_state_t ) * cls.num_client_entities );
clgame.entities = Mem_Alloc( clgame.mempool, sizeof( cl_entity_t ) * clgame.maxEntities );
clgame.static_entities = Mem_Alloc( clgame.mempool, sizeof( cl_entity_t ) * MAX_STATIC_ENTITIES );
@@ -1097,10 +1081,22 @@ void CL_InitEdicts( void )
clgame.maxRemapInfos = clgame.maxEntities + 1;
clgame.remap_info = (remap_info_t **)Mem_Alloc( clgame.mempool, sizeof( remap_info_t* ) * clgame.maxRemapInfos );
}
+
+ if( clgame.drawFuncs.R_ProcessEntData != NULL )
+ {
+ // let the client.dll free custom data
+ clgame.drawFuncs.R_ProcessEntData( true );
+ }
}
void CL_FreeEdicts( void )
{
+ if( clgame.drawFuncs.R_ProcessEntData != NULL )
+ {
+ // let the client.dll free custom data
+ clgame.drawFuncs.R_ProcessEntData( false );
+ }
+
if( clgame.entities )
Mem_Free( clgame.entities );
clgame.entities = NULL;
@@ -1208,7 +1204,8 @@ HSPRITE pfnSPR_LoadExt( const char *szPicName, uint texFlags )
// load new model
if( CL_LoadHudSprite( name, &clgame.sprites[i], false, texFlags ))
{
- clgame.sprites[i].needload = clgame.load_sequence;
+ if( i < ( MAX_IMAGES - 1 ))
+ clgame.sprites[i].needload = clgame.load_sequence;
return i;
}
return 0;
@@ -1414,11 +1411,11 @@ static client_sprite_t *pfnSPR_GetList( char *psz, int *piCount )
/*
=============
-pfnFillRGBA
+CL_FillRGBA
=============
*/
-static void pfnFillRGBA( int x, int y, int width, int height, int r, int g, int b, int a )
+void CL_FillRGBA( int x, int y, int width, int height, int r, int g, int b, int a )
{
r = bound( 0, r, 255 );
g = bound( 0, g, 255 );
@@ -2098,7 +2095,7 @@ static void pfnHookEvent( const char *filename, pfnEventHook pfn )
if( !Q_stricmp( name, ev->name ) && ev->func != NULL )
{
- MsgDev( D_WARN, "CL_HookEvent: %s already hooked!\n" );
+ MsgDev( D_WARN, "CL_HookEvent: %s already hooked!\n", name );
return;
}
}
@@ -2199,6 +2196,8 @@ pfnLocalPlayerDucking
*/
int pfnLocalPlayerDucking( void )
{
+ if( CL_IsPredicted( ))
+ return (cl.predicted.usehull == 1);
return cl.frame.client.bInDuck;
}
@@ -2214,7 +2213,7 @@ void pfnLocalPlayerViewheight( float *view_ofs )
if( !view_ofs ) return;
if( CL_IsPredicted( ))
- VectorCopy( cl.predicted_viewofs, view_ofs );
+ VectorCopy( cl.predicted.viewofs, view_ofs );
else VectorCopy( cl.frame.client.view_ofs, view_ofs );
}
@@ -2267,101 +2266,12 @@ physent_t *pfnGetPhysent( int idx )
/*
=============
-pfnSetUpPlayerPrediction
-
-FIXME: finalize
-=============
-*/
-void pfnSetUpPlayerPrediction( int dopred, int bIncludeLocalClient )
-{
-#if 0
- entity_state_t *playerstate = cl.frames[cl.parsecountmod].playerstate;
- predicted_player_t *player = cls.predicted_players;
- cl_entity_t *clent;
- int j, v12;
-
- for( j = 0; j < MAX_CLIENTS; j++, player++, playerstate++ )
- {
- player->active = false;
-
- if( playerstate->messagenum != cl.parsecount )
- continue; // not present this frame
-
- if( !playerstate->modelindex )
- continue;
-
- // special for EF_NODRAW and local client?
- if(( playerstate->effects & EF_NODRAW ) && !bIncludeLocalClient )
- {
- // don't include local player?
- if( cl.playernum != j )
- {
- player->active = true;
- player->movetype = playerstate->movetype;
- player->solid = playerstate->solid;
- player->usehull = playerstate->usehull;
-
- clent = CL_EDICT_NUM( j + 1 );
-// CL_ComputePlayerOrigin( v9 );
- VectorCopy( clent->origin, player->origin );
- VectorCopy( clent->angles, player->angles );
- }
- else continue;
- }
- else
- {
- if( cl.playernum == j )
- continue;
-
- player->active = true;
- player->movetype = playerstate->movetype;
- player->solid = playerstate->solid;
- player->usehull = playerstate->usehull;
-
- v12 = 17080 * cl.parsecountmod + 340 * j;
- player->origin[0] = cl.frames[0].playerstate[0].origin[0] + v12;
- player->origin[1] = cl.frames[0].playerstate[0].origin[1] + v12;
- player->origin[2] = cl.frames[0].playerstate[0].origin[2] + v12;
-
- player->angles[0] = cl.frames[0].playerstate[0].angles[0] + v12;
- player->angles[1] = cl.frames[0].playerstate[0].angles[1] + v12;
- player->angles[2] = cl.frames[0].playerstate[0].angles[2] + v12;
- }
- }
-#endif
-}
-
-/*
-=============
-pfnPushPMStates
-
-=============
-*/
-void pfnPushPMStates( void )
-{
- clgame.oldcount = clgame.pmove->numphysent;
-}
-
-/*
-=============
-pfnPopPMStates
-
-=============
-*/
-void pfnPopPMStates( void )
-{
- clgame.pmove->numphysent = clgame.oldcount;
-}
-
-/*
-=============
pfnSetTraceHull
=============
*/
void CL_SetTraceHull( int hull )
{
- clgame.old_trace_hull = clgame.pmove->usehull;
clgame.pmove->usehull = bound( 0, hull, 3 );
}
@@ -2376,7 +2286,6 @@ void CL_PlayerTrace( float *start, float *end, int traceFlags, int ignore_pe, pm
{
if( !tr ) return;
*tr = PM_PlayerTraceExt( clgame.pmove, start, end, traceFlags, clgame.pmove->numphysent, clgame.pmove->physents, ignore_pe, NULL );
- clgame.pmove->usehull = clgame.old_trace_hull; // restore old trace hull
}
/*
@@ -2389,7 +2298,6 @@ void CL_PlayerTraceExt( float *start, float *end, int traceFlags, int (*pfnIgnor
{
if( !tr ) return;
*tr = PM_PlayerTraceExt( clgame.pmove, start, end, traceFlags, clgame.pmove->numphysent, clgame.pmove->physents, -1, pfnIgnore );
- clgame.pmove->usehull = clgame.old_trace_hull; // restore old trace hull
}
/*
@@ -2594,7 +2502,7 @@ const char *PlayerInfo_ValueForKey( int playerNum, const char *key )
if(( playerNum > cl.maxclients ) || ( playerNum < 1 ))
return NULL;
- if(( cl.players[playerNum-1].name == NULL ) || (*(cl.players[playerNum-1].name) == 0 ))
+ if( !cl.players[playerNum-1].name[0] )
return NULL;
return Info_ValueForKey( cl.players[playerNum-1].userinfo, key );
@@ -2788,6 +2696,24 @@ void pfnSetLightmapScale( float scale )
/*
=============
+pfnParseFile
+
+handle colon separately
+=============
+*/
+char *pfnParseFile( char *data, char *token )
+{
+ char *out;
+
+ host.com_handlecolon = true;
+ out = COM_ParseFile( data, token );
+ host.com_handlecolon = false;
+
+ return out;
+}
+
+/*
+=============
pfnSPR_DrawGeneric
=============
@@ -2808,7 +2734,11 @@ TODO: implement
*/
int pfnDrawString( int x, int y, const char *str, int r, int g, int b )
{
- return 0;
+ // draw the string until we hit the null character or a newline character
+ for( ; *str != 0 && *str != '\n'; str++ )
+ x += pfnDrawCharacter( x, y, (byte)*str, r, g, b );
+
+ return x;
}
/*
@@ -2820,7 +2750,14 @@ TODO: implement
*/
int pfnDrawStringReverse( int x, int y, const char *str, int r, int g, int b )
{
- return 0;
+ char *szIt;
+
+ // find the end of the string
+ for( szIt = (char *)str; *szIt != 0; szIt++ )
+ x -= clgame.scrInfo.charWidths[(byte)*szIt];
+ pfnDrawString( x, y, str, r, g, b );
+
+ return x;
}
/*
@@ -2920,11 +2857,11 @@ void pfnPlaySoundByNameAtPitch( char *filename, float volume, int pitch )
/*
=============
-pfnFillRGBABlend
+CL_FillRGBABlend
=============
*/
-void pfnFillRGBABlend( int x, int y, int width, int height, int r, int g, int b, int a )
+void CL_FillRGBABlend( int x, int y, int width, int height, int r, int g, int b, int a )
{
r = bound( 0, r, 255 );
g = bound( 0, g, 255 );
@@ -2951,7 +2888,7 @@ pfnGetAppID
*/
int pfnGetAppID( void )
{
- return 220; // standard Valve value
+ return 130; // borrowed from SDLash3D
}
/*
@@ -3137,7 +3074,7 @@ void TriBrightness( float brightness )
rgba[1] = clgame.ds.triColor[1] * brightness;
rgba[2] = clgame.ds.triColor[2] * brightness;
- pglColor3ub( rgba[0], rgba[1], rgba[2] );
+ pglColor4ub( rgba[0], rgba[1], rgba[2], clgame.ds.triColor[3] );
}
/*
@@ -3185,7 +3122,7 @@ int TriSpriteTexture( model_t *pSpriteModel, int frame )
if( psprite->texFormat == SPR_ALPHTEST )
{
pglEnable( GL_ALPHA_TEST );
- pglAlphaFunc( GL_GREATER, 0.0f );
+ pglAlphaFunc( GL_GEQUAL, 0.5f );
}
GL_Bind( GL_TEXTURE0, gl_texturenum );
@@ -3322,7 +3259,8 @@ TriForParams
*/
void TriFogParams( float flDensity, int iFogSkybox )
{
- // TODO: implement
+ RI.fogDensity = flDensity;
+ RI.fogCustom = iFogSkybox;
}
/*
@@ -3405,7 +3343,7 @@ void NetAPI_Status( net_status_t *status )
status->connected = NET_IsLocalAddress( cls.netchan.remote_address ) ? false : true;
status->connection_time = host.realtime - cls.netchan.connect_time;
status->remote_address = cls.netchan.remote_address;
- status->packet_loss = cls.packet_loss / 100; // percent
+ status->packet_loss = cls.packet_loss / 100.0; // percent
status->latency = cl.frame.latency;
status->local_address = net_local;
status->rate = cls.netchan.rate;
@@ -3433,15 +3371,14 @@ void NetAPI_SendRequest( int context, int request, int flags, double timeout, ne
for( i = 0; i < MAX_REQUESTS; i++ )
{
nr = &clgame.net_requests[i];
- if( !nr->pfnFunc || nr->timeout < host.realtime )
- break;
+ if( !nr->pfnFunc ) break;
}
if( i == MAX_REQUESTS )
{
double max_timeout = 0;
- // no free requests? use older
+ // no free requests? use oldest
for( i = 0, nr = NULL; i < MAX_REQUESTS; i++ )
{
if(( host.realtime - clgame.net_requests[i].timesend ) > max_timeout )
@@ -3468,11 +3405,20 @@ void NetAPI_SendRequest( int context, int request, int flags, double timeout, ne
if( request == NETAPI_REQUEST_SERVERLIST )
{
- // UNDONE: build request for master-server
+ char fullquery[512] = "1\xFF" "0.0.0.0:0\0" "\\gamedir\\";
+
+ // make sure what port is specified
+ if( !nr->resp.remote_address.port ) nr->resp.remote_address.port = MSG_BigShort( PORT_MASTER );
+
+ // grab the list from the master server
+ Q_strcpy( &fullquery[22], GI->gamedir );
+ NET_SendPacket( NS_CLIENT, Q_strlen( GI->gamedir ) + 23, fullquery, nr->resp.remote_address );
+ clgame.request_type = NET_REQUEST_CLIENT;
+ clgame.master_request = nr; // holds the master request unitl the master acking
}
else
{
- // send request over the net
+ // local servers request
Q_snprintf( req, sizeof( req ), "netinfo %i %i %i", PROTOCOL_VERSION, context, request );
Netchan_OutOfBandPrint( NS_CLIENT, nr->resp.remote_address, req );
}
@@ -3486,15 +3432,31 @@ NetAPI_CancelRequest
*/
void NetAPI_CancelRequest( int context )
{
- int i;
+ net_request_t *nr;
+ int i;
// find a specified request
for( i = 0; i < MAX_REQUESTS; i++ )
{
+ nr = &clgame.net_requests[i];
+
if( clgame.net_requests[i].resp.context == context )
{
- MsgDev( D_NOTE, "Request with context %i cancelled\n", context );
+ if( nr->pfnFunc )
+ {
+ SetBits( nr->resp.error, NET_ERROR_TIMEOUT );
+ nr->resp.ping = host.realtime - nr->timesend;
+ nr->pfnFunc( &nr->resp );
+ }
+
memset( &clgame.net_requests[i], 0, sizeof( net_request_t ));
+
+ if( clgame.net_requests[i].resp.type == NETAPI_REQUEST_SERVERLIST && &clgame.net_requests[i] == clgame.master_request )
+ {
+ if( clgame.request_type == NET_REQUEST_CLIENT )
+ clgame.request_type = NET_REQUEST_CANCEL;
+ clgame.master_request = NULL;
+ }
break;
}
}
@@ -3508,7 +3470,22 @@ NetAPI_CancelAllRequests
*/
void NetAPI_CancelAllRequests( void )
{
+ net_request_t *nr;
+ int i;
+
+ // tell the user about cancel
+ for( i = 0; i < MAX_REQUESTS; i++ )
+ {
+ nr = &clgame.net_requests[i];
+ if( !nr->pfnFunc ) continue; // not used
+ SetBits( nr->resp.error, NET_ERROR_TIMEOUT );
+ nr->resp.ping = host.realtime - nr->timesend;
+ nr->pfnFunc( &nr->resp );
+ }
+
memset( clgame.net_requests, 0, sizeof( clgame.net_requests ));
+ clgame.request_type = NET_REQUEST_CANCEL;
+ clgame.master_request = NULL;
}
/*
@@ -3758,9 +3735,9 @@ static event_api_t gEventApi =
pfnLocalPlayerBounds,
pfnIndexFromTrace,
pfnGetPhysent,
- pfnSetUpPlayerPrediction,
- pfnPushPMStates,
- pfnPopPMStates,
+ CL_SetUpPlayerPrediction,
+ CL_PushPMStates,
+ CL_PopPMStates,
CL_SetSolidPlayers,
CL_SetTraceHull,
CL_PlayerTrace,
@@ -3823,13 +3800,13 @@ static cl_enginefunc_t gEngfuncs =
SPR_EnableScissor,
SPR_DisableScissor,
pfnSPR_GetList,
- pfnFillRGBA,
+ CL_FillRGBA,
pfnGetScreenInfo,
pfnSetCrosshair,
- pfnCvar_RegisterVariable,
+ pfnCvar_RegisterClientVariable,
Cvar_VariableValue,
Cvar_VariableString,
- pfnAddClientCommand,
+ Cmd_AddClientCommand,
pfnHookUserMsg,
pfnServerCmd,
pfnClientCmd,
@@ -3892,7 +3869,7 @@ static cl_enginefunc_t gEngfuncs =
VGui_GetPanel,
VGui_ViewportPaintBackground,
COM_LoadFile,
- COM_ParseFile,
+ pfnParseFile,
COM_FreeFile,
&gTriApi,
&gEfxApi,
@@ -3942,7 +3919,7 @@ static cl_enginefunc_t gEngfuncs =
pfnConstructTutorMessageDecayBuffer,
pfnResetTutorMessageDecayData,
pfnPlaySoundByNameAtPitch,
- pfnFillRGBABlend,
+ CL_FillRGBABlend,
pfnGetAppID,
Cmd_AliasGetList,
pfnVguiWrap2_GetMouseDelta,
@@ -3972,7 +3949,7 @@ void CL_UnloadProgs( void )
Mem_FreePool( &clgame.mempool );
memset( &clgame, 0, sizeof( clgame ));
- Cvar_Unlink();
+ Cvar_Unlink( CVAR_CLIENTDLL );
Cmd_Unlink( CMD_CLIENTDLL );
}
@@ -4073,8 +4050,8 @@ qboolean CL_LoadProgs( const char *name )
return false;
}
- Cvar_Get( "cl_nopred", "1", CVAR_ARCHIVE|CVAR_USERINFO, "disable client movement predicting" );
- Cvar_Get( "cl_lw", "0", CVAR_ARCHIVE|CVAR_USERINFO, "enable client weapon predicting" );
+ Cvar_Get( "cl_nopred", "1", CVAR_ARCHIVE, "disable client movement predicting" );
+ cl_lw = Cvar_Get( "cl_lw", "0", CVAR_ARCHIVE|CVAR_USERINFO, "enable client weapon predicting" );
Cvar_Get( "cl_lc", "0", CVAR_ARCHIVE|CVAR_USERINFO, "enable lag compensation" );
Cvar_FullSet( "host_clientloaded", "1", CVAR_INIT );
@@ -4086,13 +4063,12 @@ qboolean CL_LoadProgs( const char *name )
CL_InitParticles ();
CL_InitViewBeams ();
CL_InitTempEnts ();
- CL_InitEdicts (); // initailize local player and world
- CL_InitClientMove(); // initialize pm_shared
if( !R_InitRenderAPI()) // Xash3D extension
- {
MsgDev( D_WARN, "CL_LoadProgs: couldn't get render API\n" );
- }
+
+ CL_InitEdicts (); // initailize local player and world
+ CL_InitClientMove(); // initialize pm_shared
// initialize game
clgame.dllFuncs.pfnInit();
diff --git b/engine/client/cl_menu.c a/engine/client/cl_gameui.c
similarity index 98%
rename from engine/client/cl_menu.c
rename to engine/client/cl_gameui.c
index 5f77853..6c15883 100644
--- b/engine/client/cl_menu.c
+++ a/engine/client/cl_gameui.c
@@ -21,9 +21,9 @@ GNU General Public License for more details.
#include "input.h"
static MENUAPI GetMenuAPI;
-static void UI_UpdateUserinfo( void );
+static void UI_UpdateUserinfo( void );
-menu_static_t gameui;
+gameui_static_t gameui;
void UI_UpdateMenu( float realtime )
{
@@ -891,12 +891,12 @@ static ui_enginefuncs_t gEngfuncs =
pfnPIC_EnableScissor,
pfnPIC_DisableScissor,
pfnFillRGBA,
- pfnCvar_RegisterVariable,
+ pfnCvar_RegisterGameUIVariable,
Cvar_VariableValue,
Cvar_VariableString,
Cvar_Set,
Cvar_SetFloat,
- pfnAddClientCommand,
+ Cmd_AddGameUICommand,
pfnClientCmd,
Cmd_RemoveCommand,
Cmd_Argc,
@@ -968,9 +968,14 @@ void UI_UnloadProgs( void )
// deinitialize game
gameui.dllFuncs.pfnShutdown();
+ Cvar_FullSet( "host_gameuiloaded", "0", CVAR_INIT );
+
Com_FreeLibrary( gameui.hInstance );
Mem_FreePool( &gameui.mempool );
memset( &gameui, 0, sizeof( gameui ));
+
+ Cvar_Unlink( CVAR_GAMEUIDLL );
+ Cmd_Unlink( CMD_GAMEUIDLL );
}
qboolean UI_LoadProgs( void )
@@ -1019,6 +1024,8 @@ qboolean UI_LoadProgs( void )
return false;
}
+ Cvar_FullSet( "host_gameuiloaded", "1", CVAR_INIT );
+
// setup gameinfo
for( i = 0; i < SI.numgames; i++ )
{
@@ -1035,4 +1042,4 @@ qboolean UI_LoadProgs( void )
gameui.dllFuncs.pfnInit();
return true;
-}
+}
\ No newline at end of file
diff --git b/engine/client/cl_main.c a/engine/client/cl_main.c
index 9916d5f..7cf1978 100644
--- b/engine/client/cl_main.c
+++ a/engine/client/cl_main.c
@@ -22,15 +22,15 @@ GNU General Public License for more details.
#include "../cl_dll/kbutton.h"
#include "vgui_draw.h"
-#define MAX_TOTAL_CMDS 16
-#define MIN_CMD_RATE 10.0
-#define MAX_CMD_BUFFER 4000
+#define MAX_TOTAL_CMDS 32
+#define MAX_CMD_BUFFER 8000
#define CONNECTION_PROBLEM_TIME 15.0 // 15 seconds
convar_t *rcon_client_password;
convar_t *rcon_address;
-convar_t *cl_smooth;
+convar_t *cl_nosmooth;
+convar_t *cl_smoothtime;
convar_t *cl_timeout;
convar_t *cl_predict;
convar_t *cl_showfps;
@@ -38,13 +38,16 @@ convar_t *cl_nodelta;
convar_t *cl_crosshair;
convar_t *cl_cmdbackup;
convar_t *cl_showerror;
+convar_t *cl_bmodelinterp;
convar_t *cl_draw_particles;
convar_t *cl_lightstyle_lerping;
convar_t *cl_idealpitchscale;
convar_t *cl_solid_players;
convar_t *cl_draw_beams;
+convar_t *cl_updaterate;
convar_t *cl_cmdrate;
convar_t *cl_interp;
+convar_t *cl_lw;
//
// userinfo
@@ -170,8 +173,8 @@ qboolean CL_ChangeGame( const char *gamefolder, qboolean bReset )
clgame.dllFuncs.IN_ActivateMouse();
// restore mlook state
- if( mlook_active ) Cmd_ExecuteString( "+mlook\n", src_command );
- if( jlook_active ) Cmd_ExecuteString( "+jlook\n", src_command );
+ if( mlook_active ) Cmd_ExecuteString( "+mlook\n" );
+ if( jlook_active ) Cmd_ExecuteString( "+jlook\n" );
return true;
}
@@ -190,7 +193,7 @@ static float CL_LerpPoint( void )
{
float f, frac;
- f = cl.mtime[0] - cl.mtime[1];
+ f = cl_serverframetime();
if( !f || SV_Active( ))
{
@@ -284,6 +287,87 @@ CLIENT MOVEMENT COMMUNICATION
=======================================================================
*/
/*
+===============
+CL_ProcessShowTexturesCmds
+
+navigate around texture atlas
+===============
+*/
+qboolean CL_ProcessShowTexturesCmds( usercmd_t *cmd )
+{
+ static int oldbuttons;
+ int changed;
+ int pressed, released;
+
+ if( !gl_showtextures->integer || gl_overview->integer )
+ return false;
+
+ changed = (oldbuttons ^ cmd->buttons);
+ pressed = changed & cmd->buttons;
+ released = changed & (~cmd->buttons);
+
+ if( released & ( IN_RIGHT|IN_MOVERIGHT ))
+ Cvar_SetFloat( "r_showtextures", gl_showtextures->integer + 1 );
+ if( released & ( IN_LEFT|IN_MOVELEFT ))
+ Cvar_SetFloat( "r_showtextures", max( 1, gl_showtextures->integer - 1 ));
+ oldbuttons = cmd->buttons;
+
+ return true;
+}
+
+/*
+===============
+CL_ProcessOverviewCmds
+
+Transform user movement into overview adjust
+===============
+*/
+qboolean CL_ProcessOverviewCmds( usercmd_t *cmd )
+{
+ ref_overview_t *ov = &clgame.overView;
+ int sign = 1;
+ float size = world.size[!ov->rotated] / world.size[ov->rotated];
+ float step = (2.0f / size) * host.realframetime;
+ float step2 = step * 100.0f * (2.0f / ov->flZoom);
+
+ if( !gl_overview->integer || gl_showtextures->integer )
+ return false;
+
+ if( ov->flZoom < 0.0f ) sign = -1;
+
+ if( cmd->upmove > 0.0f ) ov->zNear += step;
+ else if( cmd->upmove < 0.0f ) ov->zNear -= step;
+
+ if( cmd->buttons & IN_JUMP ) ov->zFar += step;
+ else if( cmd->buttons & IN_DUCK ) ov->zFar -= step;
+
+ if( cmd->buttons & IN_FORWARD ) ov->origin[ov->rotated] -= sign * step2;
+ else if( cmd->buttons & IN_BACK ) ov->origin[ov->rotated] += sign * step2;
+
+ if( ov->rotated )
+ {
+ if( cmd->buttons & ( IN_RIGHT|IN_MOVERIGHT ))
+ ov->origin[0] -= sign * step2;
+ else if( cmd->buttons & ( IN_LEFT|IN_MOVELEFT ))
+ ov->origin[0] += sign * step2;
+ }
+ else
+ {
+ if( cmd->buttons & ( IN_RIGHT|IN_MOVERIGHT ))
+ ov->origin[1] += sign * step2;
+ else if( cmd->buttons & ( IN_LEFT|IN_MOVELEFT ))
+ ov->origin[1] -= sign * step2;
+ }
+
+ if( cmd->buttons & IN_ATTACK ) ov->flZoom += step;
+ else if( cmd->buttons & IN_ATTACK2 ) ov->flZoom -= step;
+
+ if( ov->flZoom == 0.0f ) ov->flZoom = 0.0001f; // to prevent disivion by zero
+
+ return true;
+}
+
+/*
=================
CL_CreateCmd
=================
@@ -295,6 +379,7 @@ void CL_CreateCmd( void )
color24 color;
vec3_t angles;
qboolean active;
+ int input_override;
int i, ms;
ms = host.frametime * 1000;
@@ -302,23 +387,23 @@ void CL_CreateCmd( void )
else if( ms <= 0 ) ms = 1; // keep time an actual
memset( &cmd, 0, sizeof( cmd ));
+ input_override = 0;
- // build list of all solid entities per next frame (exclude clients)
- CL_SetSolidEntities ();
- CL_SetSolidPlayers ( cl.playernum );
+ CL_PushPMStates();
+ CL_SetSolidPlayers( cl.playernum );
VectorCopy( cl.refdef.cl_viewangles, angles );
VectorCopy( cl.frame.client.origin, cl.data.origin );
VectorCopy( cl.refdef.cl_viewangles, cl.data.viewangles );
cl.data.iWeaponBits = cl.frame.client.weapons;
- cl.data.fov = cl.frame.client.fov;
+ cl.data.fov = cl.scr_fov;
- clgame.dllFuncs.pfnUpdateClientData( &cl.data, cl.time );
-
- // grab changes
- VectorCopy( cl.data.viewangles, cl.refdef.cl_viewangles );
- cl.frame.client.weapons = cl.data.iWeaponBits;
- cl.frame.client.fov = cl.data.fov;
+ if( clgame.dllFuncs.pfnUpdateClientData( &cl.data, cl.time ))
+ {
+ // grab changes if successful
+ VectorCopy( cl.data.viewangles, cl.refdef.cl_viewangles );
+ cl.scr_fov = cl.data.fov;
+ }
// allways dump the first ten messages,
// because it may contain leftover inputs
@@ -330,37 +415,45 @@ void CL_CreateCmd( void )
cl.refdef.cmd = &cl.commands[cls.netchan.outgoing_sequence & CL_UPDATE_MASK].cmd;
*cl.refdef.cmd = cmd;
}
+
+ CL_PopPMStates();
return;
}
// message we are constructing.
i = cls.netchan.outgoing_sequence & CL_UPDATE_MASK;
-
pcmd = &cl.commands[i];
- pcmd->senttime = cls.demoplayback ? 0.0 : host.realtime;
- memset( &pcmd->cmd, 0, sizeof( pcmd->cmd ));
- pcmd->receivedtime = -1.0;
- pcmd->processedfuncs = false;
- pcmd->heldback = false;
- pcmd->sendsize = 0;
+ if( !cls.demoplayback )
+ {
+ pcmd->senttime = host.realtime;
+ memset( &pcmd->cmd, 0, sizeof( pcmd->cmd ));
+ pcmd->receivedtime = -1.0;
+ pcmd->processedfuncs = false;
+ pcmd->heldback = false;
+ pcmd->sendsize = 0;
+ }
active = ( cls.state == ca_active && !cl.refdef.paused && !cls.demoplayback );
clgame.dllFuncs.CL_CreateMove( cl.time - cl.oldtime, &pcmd->cmd, active );
+ CL_PopPMStates();
- R_LightForPoint( cl.frame.client.origin, &color, false, false, 128.0f );
- pcmd->cmd.lightlevel = (color.r + color.g + color.b) / 3;
-
- // never let client.dll calc frametime for player
- // because is potential backdoor for cheating
- pcmd->cmd.msec = ms;
- pcmd->cmd.lerp_msec = cl_interp->value * 1000;
- pcmd->cmd.lerp_msec = bound( 0, cmd.lerp_msec, 250 );
+ if( !cls.demoplayback )
+ {
+ R_LightForPoint( cl.frame.client.origin, &color, false, false, 128.0f );
+ pcmd->cmd.lightlevel = (color.r + color.g + color.b) / 3;
+
+ // never let client.dll calc frametime for player
+ // because is potential backdoor for cheating
+ pcmd->cmd.msec = ms;
+ pcmd->cmd.lerp_msec = cl_interp->value * 1000;
+ pcmd->cmd.lerp_msec = bound( 0, cmd.lerp_msec, 250 );
+ }
- V_ProcessOverviewCmds( &pcmd->cmd );
- V_ProcessShowTexturesCmds( &pcmd->cmd );
+ input_override |= CL_ProcessOverviewCmds( &pcmd->cmd );
+ input_override |= CL_ProcessShowTexturesCmds( &pcmd->cmd );
- if(( cl.background && !cls.demoplayback ) || gl_overview->integer || cls.changelevel )
+ if(( cl.background && !cls.demoplayback ) || input_override || cls.changelevel )
{
VectorCopy( angles, cl.refdef.cl_viewangles );
VectorCopy( angles, pcmd->cmd.viewangles );
@@ -424,31 +517,34 @@ void CL_WritePacket( void )
CL_ComputePacketLoss ();
-#ifndef _DEBUG
- if( cl_cmdrate->value < MIN_CMD_RATE )
- {
- Cvar_SetFloat( "cl_cmdrate", MIN_CMD_RATE );
- }
-#endif
MSG_Init( &buf, "ClientData", data, sizeof( data ));
// Determine number of backup commands to send along
numbackup = bound( 0, cl_cmdbackup->integer, MAX_BACKUP_COMMANDS );
if( cls.state == ca_connected ) numbackup = 0;
+ // clamp cmdrate
+ if( cl_cmdrate->integer < 0 ) Cvar_Set( "cl_cmdrate", "0" );
+ else if( cl_cmdrate->integer > 100 ) Cvar_Set( "cl_cmdrate", "100" );
+
// Check to see if we can actually send this command
+ // always reply at end of frame during the connection process
+ if( cls.state > ca_disconnected && cls.state < ca_active )
+ send_command = true;
+
// In single player, send commands as fast as possible
// Otherwise, only send when ready and when not choking bandwidth
- if(( cl.maxclients == 1 ) || ( NET_IsLocalAddress( cls.netchan.remote_address ) && !host_limitlocal->integer ))
+ if( cl.maxclients == 1 || ( NET_IsLocalAddress( cls.netchan.remote_address ) && !host_limitlocal->integer ))
send_command = true;
- else if(( host.realtime >= cls.nextcmdtime ) && Netchan_CanPacket( &cls.netchan ))
+
+ if(( host.realtime >= cls.nextcmdtime ) && Netchan_CanPacket( &cls.netchan ))
send_command = true;
- if( cl.force_send_usercmd )
+ if( cl.send_reply )
{
+ cl.send_reply = false;
send_command = true;
- cl.force_send_usercmd = false;
}
if(( cls.netchan.outgoing_sequence - cls.netchan.incoming_acknowledged ) >= CL_UPDATE_MASK )
@@ -461,10 +557,8 @@ void CL_WritePacket( void )
}
if( cl_nodelta->integer )
- {
cl.validsequence = 0;
- }
-
+
// send a userinfo update if needed
if( userinfo->modified )
{
@@ -476,8 +570,8 @@ void CL_WritePacket( void )
{
int outgoing_sequence;
- if( cl_cmdrate->integer > 0 )
- cls.nextcmdtime = host.realtime + ( 1.0f / cl_cmdrate->value );
+ if( cl_cmdrate->integer > 0 ) // clamped between 10 and 100 fps
+ cls.nextcmdtime = host.realtime + bound( 0.1f, ( 1.0f / cl_cmdrate->value ), 0.01f );
else cls.nextcmdtime = host.realtime; // always able to send right away
if( cls.lastoutgoingcommand == -1 )
@@ -515,14 +609,13 @@ void CL_WritePacket( void )
for( i = numcmds - 1; i >= 0; i-- )
{
cmdnumber = ( cls.netchan.outgoing_sequence - i ) & CL_UPDATE_MASK;
- if( i == 0 ) cl.commands[cmdnumber].processedfuncs = true; // only last cmd allow to run funcs
to = cmdnumber;
CL_WriteUsercmd( &buf, from, to );
from = to;
if( MSG_CheckOverflow( &buf ))
- Host_Error( "CL_Move, overflowed command buffer (%i bytes)\n", MAX_CMD_BUFFER );
+ Host_Error( "CL_WritePacket: overflowed command buffer (%i bytes)\n", MAX_CMD_BUFFER );
}
// calculate a checksum over the move commands
@@ -547,9 +640,7 @@ void CL_WritePacket( void )
}
if( MSG_CheckOverflow( &buf ))
- {
- Host_Error( "CL_Move, overflowed command buffer (%i bytes)\n", MAX_CMD_BUFFER );
- }
+ Host_Error( "CL_WritePacket: overflowed command buffer (%i bytes)\n", MAX_CMD_BUFFER );
// remember outgoing command that we are sending
cls.lastoutgoingcommand = cls.netchan.outgoing_sequence;
@@ -714,7 +805,7 @@ void CL_Connect_f( void )
return;
}
- Q_strncpy( server, Cmd_Argv( 1 ), sizeof( cls.servername ));
+ Q_strncpy( server, Cmd_Argv( 1 ), sizeof( server ));
if( Host_ServerState())
{
@@ -814,6 +905,9 @@ void CL_ClearState( void )
Cvar_FullSet( "cl_background", "0", CVAR_READ_ONLY );
cl.refdef.movevars = &clgame.movevars;
cl.maxclients = 1; // allow to drawing player in menu
+ cl.mtime[0] = cl.mtime[1] = 1.0f; // because level starts from 1.0f second
+ NetAPI_CancelAllRequests();
+ cl.scr_fov = 90.0f;
Cvar_SetFloat( "scr_download", 0.0f );
Cvar_SetFloat( "scr_loading", 0.0f );
@@ -941,7 +1035,7 @@ CL_InternetServers_f
void CL_InternetServers_f( void )
{
netadr_t adr;
- char fullquery[512] = "\x31\xFF" "0.0.0.0:0\0" "\\gamedir\\";
+ char fullquery[512] = "1\xFF" "0.0.0.0:0\0" "\\gamedir\\";
MsgDev( D_INFO, "Scanning for servers on the internet area...\n" );
NET_Config( true ); // allow remote
@@ -949,9 +1043,14 @@ void CL_InternetServers_f( void )
if( !NET_StringToAdr( MASTERSERVER_ADR, &adr ) )
MsgDev( D_INFO, "Can't resolve adr: %s\n", MASTERSERVER_ADR );
- Q_strcpy( &fullquery[21], GI->gamedir );
+ Q_strcpy( &fullquery[22], GI->gamedir );
+
+ NET_SendPacket( NS_CLIENT, Q_strlen( GI->gamedir ) + 23, fullquery, adr );
- NET_SendPacket( NS_CLIENT, Q_strlen( GI->gamedir ) + 22, fullquery, adr );
+ // now we clearing the vgui request
+ if( clgame.master_request != NULL )
+ memset( clgame.master_request, 0, sizeof( net_request_t ));
+ clgame.request_type = NET_REQUEST_GAMEUI;
}
/*
@@ -983,11 +1082,11 @@ void CL_Packet_f( void )
return;
}
- if( !adr.port ) adr.port = MSG_BigShort( PORT_SERVER );
+ if( adr.port == 0 ) adr.port = MSG_BigShort( PORT_SERVER );
in = Cmd_Argv( 2 );
out = send + 4;
- send[0] = send[1] = send[2] = send[3] = (char)0xff;
+ send[0] = send[1] = send[2] = send[3] = (char)0xFF;
l = Q_strlen( in );
@@ -1058,6 +1157,70 @@ void CL_Reconnect_f( void )
/*
=================
+CL_FixupColorStringsForInfoString
+
+all the keys and values must be ends with ^7
+=================
+*/
+void CL_FixupColorStringsForInfoString( const char *in, char *out )
+{
+ qboolean hasPrefix = false;
+ qboolean endOfKeyVal = false;
+ int color = 7;
+ int count = 0;
+
+ if( *in == '\\' )
+ {
+ *out++ = *in++;
+ count++;
+ }
+
+ while( *in && count < MAX_INFO_STRING )
+ {
+ if( IsColorString( in ))
+ color = ColorIndex( *(in+1));
+
+ // color the not reset while end of key (or value) was found!
+ if( *in == '\\' && color != 7 )
+ {
+ if( IsColorString( out - 2 ))
+ {
+ *(out - 1) = '7';
+ }
+ else
+ {
+ *out++ = '^';
+ *out++ = '7';
+ count += 2;
+ }
+ color = 7;
+ }
+
+ *out++ = *in++;
+ count++;
+ }
+
+ // check the remaining value
+ if( color != 7 )
+ {
+ // if the ends with another color rewrite it
+ if( IsColorString( out - 2 ))
+ {
+ *(out - 1) = '7';
+ }
+ else
+ {
+ *out++ = '^';
+ *out++ = '7';
+ count += 2;
+ }
+ }
+
+ *out = '\0';
+}
+
+/*
+=================
CL_ParseStatusMessage
Handle a reply from a info
@@ -1065,10 +1228,15 @@ Handle a reply from a info
*/
void CL_ParseStatusMessage( netadr_t from, sizebuf_t *msg )
{
- char *s;
+ static char infostring[MAX_INFO_STRING+8];
+ char *s = MSG_ReadString( msg );
+
+ CL_FixupColorStringsForInfoString( s, infostring );
- s = MSG_ReadString( msg );
- UI_AddServerToList( from, s );
+ // more info about servers
+ MsgDev( D_INFO, "Server: %s, Game: %s\n", NET_AdrToString( from ), Info_ValueForKey( infostring, "gamedir" ));
+
+ UI_AddServerToList( from, infostring );
}
/*
@@ -1078,15 +1246,27 @@ CL_ParseNETInfoMessage
Handle a reply from a netinfo
=================
*/
-void CL_ParseNETInfoMessage( netadr_t from, sizebuf_t *msg )
+void CL_ParseNETInfoMessage( netadr_t from, sizebuf_t *msg, const char *s )
{
- char *s;
net_request_t *nr;
+ static char infostring[MAX_INFO_STRING+8];
int i, context, type;
+ int errorBits = 0;
+ char *val;
context = Q_atoi( Cmd_Argv( 1 ));
type = Q_atoi( Cmd_Argv( 2 ));
- s = Cmd_Argv( 3 );
+ while( *s != '\\' ) s++; // fetching infostring
+
+ // check for errors
+ val = Info_ValueForKey( s, "neterror" );
+
+ if( !Q_stricmp( val, "protocol" ))
+ SetBits( errorBits, NET_ERROR_PROTO_UNSUPPORTED );
+ else if( !Q_stricmp( val, "undefined" ))
+ SetBits( errorBits, NET_ERROR_UNDEFINED );
+
+ CL_FixupColorStringsForInfoString( s, infostring );
// find a request with specified context
for( i = 0; i < MAX_REQUESTS; i++ )
@@ -1095,28 +1275,81 @@ void CL_ParseNETInfoMessage( netadr_t from, sizebuf_t *msg )
if( nr->resp.context == context && nr->resp.type == type )
{
- if( nr->timeout > host.realtime )
- {
- // setup the answer
- nr->resp.response = s;
- nr->resp.remote_address = from;
- nr->resp.error = NET_SUCCESS;
- nr->resp.ping = host.realtime - nr->timesend;
- nr->pfnFunc( &nr->resp );
-
- if(!( nr->flags & FNETAPI_MULTIPLE_RESPONSE ))
- memset( nr, 0, sizeof( *nr )); // done
- }
- else
- {
- memset( nr, 0, sizeof( *nr ));
- }
+ // setup the answer
+ nr->resp.response = infostring;
+ nr->resp.remote_address = from;
+ nr->resp.error = NET_SUCCESS;
+ nr->resp.ping = host.realtime - nr->timesend;
+
+ if( nr->timeout <= host.realtime )
+ SetBits( nr->resp.error, NET_ERROR_TIMEOUT );
+ SetBits( nr->resp.error, errorBits ); // misc error bits
+
+ nr->pfnFunc( &nr->resp );
+
+ if( !FBitSet( nr->flags, FNETAPI_MULTIPLE_RESPONSE ))
+ memset( nr, 0, sizeof( *nr )); // done
return;
}
}
}
+/*
+=================
+CL_ProcessNetRequests
+
+check for timeouts
+=================
+*/
+void CL_ProcessNetRequests( void )
+{
+ net_request_t *nr;
+ int i;
+
+ // find a request with specified context
+ for( i = 0; i < MAX_REQUESTS; i++ )
+ {
+ nr = &clgame.net_requests[i];
+ if( !nr->pfnFunc ) continue; // not used
+
+ if( nr->timeout <= host.realtime )
+ {
+ // setup the answer
+ SetBits( nr->resp.error, NET_ERROR_TIMEOUT );
+ nr->resp.ping = host.realtime - nr->timesend;
+
+ nr->pfnFunc( &nr->resp );
+ memset( nr, 0, sizeof( *nr )); // done
+ }
+ }
+}
+
//===================================================================
+/*
+===============
+CL_SetupOverviewParams
+
+Get initial overview values
+===============
+*/
+void CL_SetupOverviewParams( void )
+{
+ ref_overview_t *ov = &clgame.overView;
+ float mapAspect, screenAspect, aspect;
+
+ ov->rotated = ( world.size[1] <= world.size[0] ) ? true : false;
+
+ // calculate nearest aspect
+ mapAspect = world.size[!ov->rotated] / world.size[ov->rotated];
+ screenAspect = (float)glState.width / (float)glState.height;
+ aspect = Q_max( mapAspect, screenAspect );
+
+ ov->zNear = world.maxs[2];
+ ov->zFar = world.mins[2];
+ ov->flZoom = ( 8192.0f / world.size[ov->rotated] ) / aspect;
+
+ VectorAverage( world.mins, world.maxs, ov->origin );
+}
/*
======================
@@ -1144,26 +1377,6 @@ void CL_PrepSound( void )
}
S_EndRegistration();
-
- if( host.soundList )
- {
- // need to reapply all ambient sounds after restarting
- for( i = 0; i < host.numsounds; i++)
- {
- soundlist_t *entry = &host.soundList[i];
- if( entry->looping && entry->entnum != -1 )
- {
- MsgDev( D_NOTE, "Restarting sound %s...\n", entry->name );
- S_AmbientSound( entry->origin, entry->entnum,
- S_RegisterSound( entry->name ), entry->volume, entry->attenuation,
- entry->pitch, 0 );
- }
- }
- }
-
- host.soundList = NULL;
- host.numsounds = 0;
-
cl.audio_prepped = true;
}
@@ -1220,7 +1433,7 @@ void CL_PrepVideo( void )
R_NewMap(); // tell the render about new map
- V_SetupOverviewState(); // set overview bounds
+ CL_SetupOverviewParams(); // set overview bounds
// must be called after lightmap loading!
clgame.dllFuncs.pfnVidInit();
@@ -1237,44 +1450,6 @@ void CL_PrepVideo( void )
Cvar_SetFloat( "scr_loading", 100.0f ); // all done
- if( host.decalList )
- {
- // need to reapply all decals after restarting
- for( i = 0; i < host.numdecals; i++ )
- {
- decallist_t *entry = &host.decalList[i];
- cl_entity_t *pEdict = CL_GetEntityByIndex( entry->entityIndex );
- int decalIndex = CL_DecalIndex( CL_DecalIndexFromName( entry->name ));
- int modelIndex = 0;
-
- if( pEdict ) modelIndex = pEdict->curstate.modelindex;
- CL_DecalShoot( decalIndex, entry->entityIndex, modelIndex, entry->position, entry->flags );
- }
- Z_Free( host.decalList );
- }
-
- host.decalList = NULL;
- host.numdecals = 0;
-
- if( host.soundList )
- {
- // need to reapply all ambient sounds after restarting
- for( i = 0; i < host.numsounds; i++ )
- {
- soundlist_t *entry = &host.soundList[i];
- if( entry->looping && entry->entnum != -1 )
- {
- MsgDev( D_NOTE, "Restarting sound %s...\n", entry->name );
- S_AmbientSound( entry->origin, entry->entnum,
- S_RegisterSound( entry->name ), entry->volume, entry->attenuation,
- entry->pitch, 0 );
- }
- }
- }
-
- host.soundList = NULL;
- host.numsounds = 0;
-
if( host.developer <= 2 )
Con_ClearNotify(); // clear any lines of console text
@@ -1341,7 +1516,7 @@ void CL_ConnectionlessPacket( netadr_t from, sizebuf_t *msg )
else if( !Q_strcmp( c, "netinfo" ))
{
// server responding to a status broadcast
- CL_ParseNETInfoMessage( from, msg );
+ CL_ParseNETInfoMessage( from, msg, args );
}
else if( !Q_strcmp( c, "cmd" ))
{
@@ -1386,26 +1561,69 @@ void CL_ConnectionlessPacket( netadr_t from, sizebuf_t *msg )
// dropped the connection but it is still getting packets from us
CL_Disconnect();
}
- else if( msg->pData[0] == 0xFF && msg->pData[1] == 0xFF && msg->pData[2] == 0xFF && msg->pData[3] == 0xFF && msg->pData[4] == 0x66 && msg->pData[5] == 0x0A )
+ else if( !Q_strcmp( c, "f" ))
{
- dataoffset = 6;
-
- while( 1 )
+ // serverlist got from masterserver
+ while( MSG_GetNumBitsLeft( msg ) > 8 )
{
+ MSG_ReadBytes( msg, servadr.ip, sizeof( servadr.ip )); // 4 bytes for IP
+ servadr.port = MSG_ReadShort( msg ); // 2 bytes for Port
servadr.type = NA_IP;
- memcpy( servadr.ip, &msg->pData[dataoffset], sizeof(servadr.ip));
- servadr.port = *(word *)&msg->pData[dataoffset + 4];
+ // list is ends here
if( !servadr.port )
+ {
+ if( clgame.request_type == NET_REQUEST_CLIENT && clgame.master_request != NULL )
+ {
+ net_request_t *nr = clgame.master_request;
+ net_adrlist_t *list, **prev;
+
+ // setup the answer
+ nr->resp.remote_address = from;
+ nr->resp.error = NET_SUCCESS;
+ nr->resp.ping = host.realtime - nr->timesend;
+
+ if( nr->timeout <= host.realtime )
+ SetBits( nr->resp.error, NET_ERROR_TIMEOUT );
+
+ MsgDev( D_INFO, "serverlist call: %s\n", NET_AdrToString( from ));
+ nr->pfnFunc( &nr->resp );
+
+ // throw the list, now it will be stored in user area
+ prev = &((net_adrlist_t *)nr->resp.response);
+
+ while( 1 )
+ {
+ list = *prev;
+ if( !list ) break;
+
+ // throw out any variables the game created
+ *prev = list->next;
+ Mem_Free( list );
+ }
+ memset( nr, 0, sizeof( *nr )); // done
+ clgame.request_type = NET_REQUEST_CANCEL;
+ clgame.master_request = NULL;
+ }
break;
+ }
- MsgDev( D_INFO, "Found server: %s\n", NET_AdrToString( servadr ));
-
- NET_Config( true ); // allow remote
-
- Netchan_OutOfBandPrint( NS_CLIENT, servadr, "info %i", PROTOCOL_VERSION );
-
- dataoffset += 6;
+ if( clgame.request_type == NET_REQUEST_CLIENT && clgame.master_request != NULL )
+ {
+ net_request_t *nr = clgame.master_request;
+ net_adrlist_t *list;
+
+ // adding addresses into list
+ list = Z_Malloc( sizeof( *list ));
+ list->remote_address = servadr;
+ list->next = nr->resp.response;
+ nr->resp.response = list;
+ }
+ else if( clgame.request_type == NET_REQUEST_GAMEUI )
+ {
+ NET_Config( true ); // allow remote
+ Netchan_OutOfBandPrint( NS_CLIENT, servadr, "info %i", PROTOCOL_VERSION );
+ }
}
}
else if( clgame.dllFuncs.pfnConnectionlessPacket( &from, args, buf, &len ))
@@ -1477,6 +1695,7 @@ void CL_ReadNetMessage( void )
continue; // wasn't accepted for some reason
CL_ParseServerMessage( &net_message );
+ cl.send_reply = true;
}
// check for fragmentation/reassembly related packets.
@@ -1499,6 +1718,9 @@ void CL_ReadNetMessage( void )
}
Netchan_UpdateProgress( &cls.netchan );
+
+ // check requests for time-expire
+ CL_ProcessNetRequests();
}
void CL_ReadPackets( void )
@@ -1516,6 +1738,9 @@ void CL_ReadPackets( void )
#endif
CL_UpdateFrameLerp ();
+ // build list of all solid entities per next frame (exclude clients)
+ CL_SetSolidEntities();
+
// singleplayer never has connection timeout
if( NET_IsLocalAddress( cls.netchan.remote_address ))
return;
@@ -1641,7 +1866,7 @@ void CL_InitLocal( void )
cls.state = ca_disconnected;
// register our variables
- cl_predict = Cvar_Get( "cl_predict", "0", CVAR_ARCHIVE, "enable client movement prediction" );
+ cl_predict = Cvar_Get( "cl_predict", "0", CVAR_ARCHIVE|CVAR_USERINFO, "enable client movement prediction" );
cl_crosshair = Cvar_Get( "crosshair", "1", CVAR_ARCHIVE, "show weapon chrosshair" );
cl_nodelta = Cvar_Get ("cl_nodelta", "0", 0, "disable delta-compression for usercommnds" );
cl_idealpitchscale = Cvar_Get( "cl_idealpitchscale", "0.8", 0, "how much to look up/down slopes and stairs when not using freelook" );
@@ -1661,18 +1886,21 @@ void CL_InitLocal( void )
rate = Cvar_Get( "rate", "25000", CVAR_USERINFO|CVAR_ARCHIVE, "player network rate" );
hltv = Cvar_Get( "hltv", "0", CVAR_USERINFO|CVAR_LATCH, "HLTV mode" );
cl_showfps = Cvar_Get( "cl_showfps", "1", CVAR_ARCHIVE, "show client fps" );
- cl_smooth = Cvar_Get ("cl_smooth", "0", CVAR_ARCHIVE, "smooth up stair climbing and interpolate position in multiplayer" );
+ cl_nosmooth = Cvar_Get( "cl_nosmooth", "0", CVAR_ARCHIVE, "disable smooth up stair climbing and interpolate position in multiplayer" );
+ cl_smoothtime = Cvar_Get( "cl_smoothtime", "0.1", CVAR_ARCHIVE, "time to smooth up" );
cl_cmdbackup = Cvar_Get( "cl_cmdbackup", "10", CVAR_ARCHIVE, "how many additional history commands are sent" );
cl_cmdrate = Cvar_Get( "cl_cmdrate", "30", CVAR_ARCHIVE, "Max number of command packets sent to server per second" );
cl_draw_particles = Cvar_Get( "cl_draw_particles", "1", CVAR_ARCHIVE, "Disable any particle effects" );
cl_draw_beams = Cvar_Get( "cl_draw_beams", "1", CVAR_ARCHIVE, "Disable view beams" );
cl_lightstyle_lerping = Cvar_Get( "cl_lightstyle_lerping", "0", CVAR_ARCHIVE, "enables animated light lerping (perfomance option)" );
cl_showerror = Cvar_Get( "cl_showerror", "0", CVAR_ARCHIVE, "show prediction error" );
+ cl_bmodelinterp = Cvar_Get( "cl_bmodelinterp", "1", CVAR_ARCHIVE, "enable bmodel interpolation" );
Cvar_Get( "hud_scale", "0", CVAR_ARCHIVE|CVAR_LATCH, "scale hud at current resolution" );
Cvar_Get( "skin", "", CVAR_USERINFO, "player skin" ); // XDM 3.3 want this cvar
- Cvar_Get( "cl_updaterate", "60", CVAR_USERINFO|CVAR_ARCHIVE, "refresh rate of server messages" );
+ cl_updaterate = Cvar_Get( "cl_updaterate", "60", CVAR_USERINFO|CVAR_ARCHIVE, "refresh rate of server messages" );
Cvar_Get( "cl_background", "0", CVAR_READ_ONLY, "indicate what background map is running" );
+ Cvar_Get( "cl_msglevel", "0", CVAR_USERINFO|CVAR_ARCHIVE, "message filter for server notifications" );
// these two added to shut up CS 1.5 about 'unknown' commands
Cvar_Get( "lightgamma", "1", CVAR_ARCHIVE, "ambient lighting level (legacy, unused)" );
@@ -1755,6 +1983,43 @@ void CL_SendCommand( void )
/*
==================
+CL_PrepareFrame
+
+setup camera, update visible ents
+==================
+*/
+void CL_PrepareFrame( void )
+{
+ if( cls.state != ca_active )
+ return; // not in game
+
+ if( !cl.video_prepped || ( UI_IsVisible() && !cl.background ))
+ return; // still loading
+
+ if( cl.frame.valid && ( cl.force_refdef || !cl.refdef.paused ))
+ {
+ cl.force_refdef = false;
+ V_SetupRefDef ();
+ }
+}
+
+/*
+==================
+Host_RenderFrame
+
+==================
+*/
+void Host_RenderFrame( void )
+{
+ // if client is not active, do nothing
+ if( !cls.initialized ) return;
+
+ // update the screen
+ SCR_UpdateScreen ();
+}
+
+/*
+==================
Host_ClientFrame
==================
@@ -1768,6 +2033,10 @@ void Host_ClientFrame( void )
cl.oldtime = cl.time;
cl.time += host.frametime;
+ // demo time
+ if( cls.demorecording && !cls.demowaiting )
+ cls.demotime += host.frametime;
+
if( gameui.hInstance )
{
// menu time (not paused, not clamped)
@@ -1796,25 +2065,43 @@ void Host_ClientFrame( void )
if( !cl.audio_prepped ) CL_PrepSound();
}
- // update the screen
- SCR_UpdateScreen ();
+ if( FBitSet( host.features, ENGINE_FIXED_FRAMERATE ))
+ {
+ // send a new command message to the server
+ CL_SendCommand();
+
+ // predict all unacknowledged movements
+ CL_PredictMovement();
- // update audio
- S_RenderFrame( &cl.refdef );
+ // setup view, add visible ents
+ CL_PrepareFrame();
- // send a new command message to the server
- CL_SendCommand();
+ // update audio
+ S_RenderFrame( &cl.refdef );
+ }
+ else
+ {
+ // update the screen
+ SCR_UpdateScreen ();
- // predict all unacknowledged movements
- CL_PredictMovement();
+ // update audio
+ S_RenderFrame( &cl.refdef );
+
+ // send a new command message to the server
+ CL_SendCommand();
+
+ // predict all unacknowledged movements
+ CL_PredictMovement();
+ }
+
+ // animate lightestyles
+ CL_RunLightStyles();
// decay dynamic lights
CL_DecayLights ();
SCR_RunCinematic();
Con_RunConsole();
-
- cls.framecount++;
}
@@ -1874,4 +2161,6 @@ void CL_Shutdown( void )
SCR_FreeCinematic (); // release AVI's *after* client.dll because custom renderer may use them
S_Shutdown ();
R_Shutdown ();
+
+ Con_Shutdown ();
}
\ No newline at end of file
diff --git b/engine/client/cl_netgraph.c a/engine/client/cl_netgraph.c
new file mode 100644
index 0000000..f88456f
--- /dev/null
+++ a/engine/client/cl_netgraph.c
@@ -0,0 +1,511 @@
+/*
+cl_netgraph.c - Draw Net statistics (borrowed from Xash3D SDL code)
+Copyright (C) 2016 Uncle Mike
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+*/
+
+#include "common.h"
+#include "client.h"
+#include "gl_local.h"
+
+#define NET_TIMINGS 1024
+#define NET_TIMINGS_MASK (NET_TIMINGS - 1)
+#define LATENCY_AVG_FRAC 0.5
+#define PACKETLOSS_AVG_FRAC 0.5
+#define PACKETCHOKE_AVG_FRAC 0.5
+#define NETGRAPH_LERP_HEIGHT 24
+#define NUM_LATENCY_SAMPLES 8
+
+convar_t *net_graph;
+convar_t *net_graphpos;
+convar_t *net_graphwidth;
+convar_t *net_graphheight;
+convar_t *net_scale;
+convar_t *net_graphfillsegments;
+
+static struct packet_latency_t
+{
+ int latency;
+ int choked;
+} netstat_packet_latency[NET_TIMINGS];
+
+static struct cmdinfo_t
+{
+ float cmd_lerp;
+ int size;
+ qboolean sent;
+} netstat_cmdinfo[NET_TIMINGS];
+
+static byte netcolors[NETGRAPH_LERP_HEIGHT+5][4] =
+{
+ { 255, 0, 0, 255 },
+ { 0, 0, 255, 255 },
+ { 240, 127, 63, 255 },
+ { 255, 255, 0, 255 },
+ { 63, 255, 63, 150 }
+ // other will be generated through NetGraph_InitColors()
+};
+
+static netbandwidthgraph_t netstat_graph[NET_TIMINGS];
+static float packet_loss;
+static float packet_choke;
+
+/*
+==========
+NetGraph_DrawLine
+
+CL_FillRGBA shortcut
+==========
+*/
+static void NetGraph_DrawRect( wrect_t *rect, byte colors[4] )
+{
+ CL_FillRGBA( rect->left, rect->top, rect->right, rect->bottom, colors[0], colors[1], colors[2], colors[3] );
+}
+
+/*
+==========
+NetGraph_InitColors
+
+init netgraph colors
+==========
+*/
+void NetGraph_InitColors( void )
+{
+ int i;
+
+ for( i = 0; i < NETGRAPH_LERP_HEIGHT; i++ )
+ {
+ if( i <= NETGRAPH_LERP_HEIGHT / 3 )
+ {
+ netcolors[i + 5][0] = i * -7.875f + 63;
+ netcolors[i + 5][1] = i * 7.875f;
+ netcolors[i + 5][2] = i * 19.375f + 100;
+ }
+ else
+ {
+ netcolors[i + 5][0] = ( i - 8 ) * -0.3125f + 255;
+ netcolors[i + 5][1] = ( i - 8 ) * -7.9375f + 127;
+ netcolors[i + 5][2] = 0;
+ }
+ }
+}
+
+/*
+==========
+NetGraph_GetFrameData
+
+get frame data info, like chokes, packet losses, also update graph, packet and cmdinfo
+==========
+*/
+void NetGraph_GetFrameData( int *biggest_message, float *latency, int *latency_count )
+{
+ int i, choke_count = 0, loss_count = 0;
+ float loss, choke;
+
+ *biggest_message = *latency_count = 0;
+ *latency = 0.0f;
+
+ for( i = cls.netchan.incoming_sequence - CL_UPDATE_BACKUP + 1; i <= cls.netchan.incoming_sequence; i++ )
+ {
+ frame_t *f = cl.frames + ( i & CL_UPDATE_MASK );
+ struct packet_latency_t *p = netstat_packet_latency + ( i & NET_TIMINGS_MASK );
+ netbandwidthgraph_t *g = netstat_graph + ( i & NET_TIMINGS_MASK );
+
+ p->choked = f->receivedtime == -2.0f ? true : false;
+ if( p->choked )
+ choke_count++;
+
+ if( !f->valid || f->receivedtime == -2.0 )
+ {
+ p->latency = 9998; // broken delta
+ }
+ else if( f->receivedtime == -1.0 )
+ {
+ p->latency = 9999; // dropped
+ loss_count++;
+ }
+ else
+ {
+ int frame_latency = Q_min( 1.0f, f->latency );
+ p->latency = (( frame_latency + 0.1 ) / 1.1 ) * ( net_graphheight->value - NETGRAPH_LERP_HEIGHT - 2 );
+
+ if( i > cls.netchan.incoming_sequence - NUM_LATENCY_SAMPLES )
+ {
+ *latency += 1000.0f * f->latency;
+ latency_count++;
+ }
+ }
+
+ memcpy( g, &f->graphdata, sizeof( netbandwidthgraph_t ));
+
+ if( *biggest_message < g->msgbytes )
+ *biggest_message = g->msgbytes;
+ }
+
+ for( i = cls.netchan.outgoing_sequence - CL_UPDATE_BACKUP + 1; i <= cls.netchan.outgoing_sequence; i++ )
+ {
+ netstat_cmdinfo[i & NET_TIMINGS_MASK].cmd_lerp = cl.commands[i & CL_UPDATE_MASK].frame_lerp;
+ netstat_cmdinfo[i & NET_TIMINGS_MASK].sent = cl.commands[i & CL_UPDATE_MASK].heldback ? false : true;
+ netstat_cmdinfo[i & NET_TIMINGS_MASK].size = cl.commands[i & CL_UPDATE_MASK].sendsize;
+ }
+
+ // packet loss
+ loss = 100.0 * (float)loss_count / CL_UPDATE_BACKUP;
+ packet_loss = PACKETLOSS_AVG_FRAC * packet_loss + ( 1.0 - PACKETLOSS_AVG_FRAC ) * loss;
+
+ // packet choke
+ choke = 100.0 * (float)choke_count / CL_UPDATE_BACKUP;
+ packet_choke = PACKETCHOKE_AVG_FRAC * packet_choke + ( 1.0 - PACKETCHOKE_AVG_FRAC ) * choke;
+}
+
+/*
+===========
+NetGraph_DrawTimes
+
+===========
+*/
+void NetGraph_DrawTimes( wrect_t rect, int x, int w )
+{
+ int i, j, extrap_point = NETGRAPH_LERP_HEIGHT / 3, a, h;
+ POINT pt = { Q_max( x + w - 1 - 25, 1 ), Q_max( rect.top + rect.bottom - 4 - NETGRAPH_LERP_HEIGHT + 1, 1 ) };
+ rgba_t colors = { 0.9 * 255, 0.9 * 255, 0.7 * 255, 255 };
+ wrect_t fill;
+
+ Con_DrawString( pt.x, pt.y, va( "%i/s", cl_cmdrate->integer ), colors );
+
+ for( a = 0; a < w; a++ )
+ {
+ i = ( cls.netchan.outgoing_sequence - a ) & NET_TIMINGS_MASK;
+ h = ( netstat_cmdinfo[i].cmd_lerp / 3.0f ) * NETGRAPH_LERP_HEIGHT;
+
+ fill.left = x + w - a - 1;
+ fill.right = fill.bottom = 1;
+ fill.top = rect.top + rect.bottom - 4;
+
+ if( h >= extrap_point )
+ {
+ int start = 0;
+
+ h -= extrap_point;
+ fill.top = extrap_point;
+
+ for( j = start; j < h; j++ )
+ {
+ memcpy( colors, netcolors[j + extrap_point], sizeof( byte ) * 3 );
+ NetGraph_DrawRect( &fill, colors );
+ fill.top--;
+ }
+ }
+ else
+ {
+ int oldh = h;
+
+ fill.top -= h;
+ h = extrap_point - h;
+
+ for( j = 0; j < h; j++ )
+ {
+ memcpy( colors, netcolors[j + oldh], sizeof( byte ) * 3 );
+ NetGraph_DrawRect( &fill, colors );
+ fill.top--;
+ }
+ }
+
+ fill.top = rect.top + rect.bottom - 4 - extrap_point;
+
+ CL_FillRGBA( fill.left, fill.top, fill.right, fill.bottom, 255, 255, 255, 255 );
+
+ fill.top = rect.top + rect.bottom - 3;
+
+ if( !netstat_cmdinfo[i].sent )
+ CL_FillRGBA( fill.left, fill.top, fill.right, fill.bottom, 255, 0, 0, 255 );
+ }
+}
+
+/*
+===========
+NetGraph_DrawHatches
+
+===========
+*/
+void NetGraph_DrawHatches( int x, int y, int maxmsgbytes )
+{
+ int ystep = max((int)( 10.0 / net_scale->value ), 1 );
+ wrect_t hatch = { x, 4, y, 1 };
+ int starty;
+
+ for( starty = hatch.top; hatch.top > 0 && ((starty - hatch.top) * net_scale->value < (maxmsgbytes + 50)); hatch.top -= ystep )
+ {
+ CL_FillRGBA( hatch.left, hatch.top, hatch.right, hatch.bottom, 63, 63, 0, 200 );
+ }
+}
+
+/*
+===========
+NetGraph_DrawTextFields
+
+===========
+*/
+void NetGraph_DrawTextFields( int x, int y, int count, float avg, int packet_loss, int packet_choke )
+{
+ static int lastout;
+ rgba_t colors = { 0.9 * 255, 0.9 * 255, 0.7 * 255, 255 };
+ int i = ( cls.netchan.outgoing_sequence - 1 ) & NET_TIMINGS_MASK;
+ float latency = count > 0 ? Q_max( 0, avg / count - 0.5 * host.frametime - 1000.0 / cl_updaterate->value ) : 0;
+ float framerate = 1.0 / host.realframetime;
+
+ Con_DrawString( x, y - net_graphheight->integer, va( "%.1ffps" , framerate ), colors );
+ Con_DrawString( x + 75, y - net_graphheight->integer, va( "%i ms" , (int)latency ), colors );
+ Con_DrawString( x + 150, y - net_graphheight->integer, va( "%i/s" , cl_updaterate->integer ), colors );
+
+ if( netstat_cmdinfo[i].size )
+ lastout = netstat_cmdinfo[i].size;
+
+ Con_DrawString( x, y - net_graphheight->integer + 15, va( "in : %i %.2f k/s", netstat_graph[i].msgbytes, cls.netchan.flow[FLOW_INCOMING].avgkbytespersec ), colors );
+
+ Con_DrawString( x, y - net_graphheight->integer + 30, va( "out: %i %.2f k/s", lastout, cls.netchan.flow[FLOW_OUTGOING].avgkbytespersec ), colors );
+
+ if( net_graph->integer > 2 )
+ Con_DrawString( x, y - net_graphheight->integer + 45, va( "loss: %i choke: %i", packet_loss, packet_choke ), colors );
+
+}
+
+/*
+===========
+NetGraph_DrawDataSegment
+
+===========
+*/
+int NetGraph_DrawDataSegment( wrect_t *fill, int bytes, byte r, byte g, byte b, byte a )
+{
+ int h = bytes / net_scale->value;
+ byte colors[4] = { r, g, b, a };
+
+ fill->top -= h;
+
+ if( net_graphfillsegments->integer )
+ fill->bottom = h;
+ else fill->bottom = 1;
+
+ if( fill->top > 1 )
+ {
+ NetGraph_DrawRect( fill, colors );
+ return 1;
+ }
+ return 0;
+}
+
+/*
+===========
+NetGraph_ColorForHeight
+
+color based on packet latency
+===========
+*/
+void NetGraph_ColorForHeight( struct packet_latency_t *packet, byte color[4], int *ping )
+{
+ switch( packet->latency )
+ {
+ case 9999:
+ memcpy( color, netcolors[0], sizeof( byte ) * 4 ); // dropped
+ *ping = 0;
+ break;
+ case 9998:
+ memcpy( color, netcolors[1], sizeof( byte ) * 4 ); // invalid
+ *ping = 0;
+ break;
+ case 9997:
+ memcpy( color, netcolors[2], sizeof( byte ) * 4 ); // skipped
+ *ping = 0;
+ break;
+ default:
+ *ping = 1;
+ if( packet->choked )
+ {
+ memcpy( color, netcolors[3], sizeof( byte ) * 4 );
+ }
+ else
+ {
+ memcpy( color, netcolors[4], sizeof( byte ) * 4 );
+ }
+ }
+}
+
+/*
+===========
+NetGraph_DrawDataUsage
+
+===========
+*/
+void NetGraph_DrawDataUsage( int x, int y, int w, int maxmsgbytes )
+{
+ int a, i, h, lastvalidh = 0, ping;
+ wrect_t fill = { 0 };
+ byte color[4];
+
+ for( a = 0; a < w; a++ )
+ {
+ i = (cls.netchan.incoming_sequence - a) & NET_TIMINGS_MASK;
+ h = netstat_packet_latency[i].latency;
+
+ NetGraph_ColorForHeight( &netstat_packet_latency[i], color, &ping );
+
+ if( !ping ) h = lastvalidh;
+ else lastvalidh = h;
+
+ if( h > net_graphheight->value - NETGRAPH_LERP_HEIGHT - 2 )
+ h = net_graphheight->value - NETGRAPH_LERP_HEIGHT - 2;
+
+ fill.left = x + w - a - 1;
+ fill.top = y - h;
+ fill.right = 1;
+ fill.bottom = ping ? 1: h;
+
+ NetGraph_DrawRect( &fill, color );
+
+ fill.top = y;
+ fill.bottom = 1;
+
+ color[0] = 0; color[1] = 255; color[2] = 0; color[3] = 160;
+
+ NetGraph_DrawRect( &fill, color );
+
+ if( net_graph->integer < 2 )
+ continue;
+
+ fill.top = y - net_graphheight->value - 1;
+ fill.bottom = 1;
+ color[0] = color[1] = color[2] = color[3] = 255;
+
+ NetGraph_DrawRect( &fill, color );
+
+ fill.top -= 1;
+
+ if( netstat_packet_latency[i].latency > 9995 )
+ continue; // skip invalid
+
+ if( !NetGraph_DrawDataSegment( &fill, netstat_graph[i].client, 255, 0, 0, 128 ))
+ continue;
+
+ if( !NetGraph_DrawDataSegment( &fill, netstat_graph[i].players, 255, 255, 0, 128 ))
+ continue;
+
+ if( !NetGraph_DrawDataSegment( &fill, netstat_graph[i].entities, 255, 0, 255, 128 ))
+ continue;
+
+ if( !NetGraph_DrawDataSegment( &fill, netstat_graph[i].tentities, 0, 0, 255, 128 ))
+ continue;
+
+ if( !NetGraph_DrawDataSegment( &fill, netstat_graph[i].sound, 0, 255, 0, 128 ))
+ continue;
+
+ if( !NetGraph_DrawDataSegment( &fill, netstat_graph[i].event, 0, 255, 255, 128 ))
+ continue;
+
+ if( !NetGraph_DrawDataSegment( &fill, netstat_graph[i].usr, 200, 200, 200, 128 ))
+ continue;
+
+ // special case for absolute usage
+ h = netstat_graph[i].msgbytes / net_scale->value;
+
+ color[0] = color[1] = color[2] = 240; color[3] = 255;
+
+ fill.bottom = 1;
+ fill.top = y - net_graphheight->value - 1 - h;
+
+ if( fill.top < 2 ) continue;
+
+ NetGraph_DrawRect( &fill, color );
+ }
+
+ if( net_graph->integer >= 2 )
+ NetGraph_DrawHatches( x, y - net_graphheight->value - 1, maxmsgbytes );
+}
+
+/*
+===========
+NetGraph_GetScreenPos
+
+===========
+*/
+void NetGraph_GetScreenPos( wrect_t *rect, int *w, int *x, int *y )
+{
+ rect->left = rect->top = 0;
+ rect->right = clgame.scrInfo.iWidth;
+ rect->bottom = clgame.scrInfo.iHeight;
+
+ *w = Q_min( NET_TIMINGS, net_graphwidth->integer );
+ if( rect->right < *w + 10 )
+ *w = rect->right - 10;
+
+ // detect x and y position
+ switch( net_graphpos->integer )
+ {
+ case 1: // right sided
+ *x = rect->left + rect->right - 5 - *w;
+ break;
+ case 2: // center
+ *x = rect->left + ( rect->right - 10 - *w ) / 2;
+ break;
+ default: // left sided
+ *x = rect->left + 5;
+ break;
+ }
+
+ *y = rect->bottom + rect->top - NETGRAPH_LERP_HEIGHT - 5;
+}
+
+/*
+===========
+SCR_DrawNetGraph
+
+===========
+*/
+void SCR_DrawNetGraph( void )
+{
+ wrect_t rect;
+ float avg_ping;
+ int ping_count;
+ int maxmsgbytes;
+ int w, x, y;
+
+ if( !net_graph->integer )
+ return;
+
+ if( net_scale->value <= 0 )
+ Cvar_SetFloat( "net_scale", 0.1f );
+
+ NetGraph_GetScreenPos( &rect, &w, &x, &y );
+
+ NetGraph_GetFrameData( &maxmsgbytes, &avg_ping, &ping_count );
+
+ if( net_graph->integer < 3 )
+ {
+ NetGraph_DrawTimes( rect, x, w );
+ NetGraph_DrawDataUsage( x, y, w, maxmsgbytes );
+ }
+
+ NetGraph_DrawTextFields( x, y, ping_count, avg_ping, packet_loss, packet_choke );
+}
+
+void CL_InitNetgraph( void )
+{
+ net_graph = Cvar_Get( "net_graph", "0", CVAR_ARCHIVE, "draw network usage graph" );
+ net_graphpos = Cvar_Get( "net_graphpos", "1", CVAR_ARCHIVE, "network usage graph position" );
+ net_scale = Cvar_Get( "net_scale", "5", CVAR_ARCHIVE, "network usage graph scale level" );
+ net_graphwidth = Cvar_Get( "net_graphwidth", "192", CVAR_ARCHIVE, "network usage graph width" );
+ net_graphheight = Cvar_Get( "net_graphheight", "64", CVAR_ARCHIVE, "network usage graph height" );
+ net_graphfillsegments = Cvar_Get( "net_graphfillsegments", "1", CVAR_ARCHIVE, "fill segments in network usage graph" );
+ packet_loss = packet_choke = 0.0;
+
+ NetGraph_InitColors();
+}
\ No newline at end of file
diff --git b/engine/client/cl_parse.c a/engine/client/cl_parse.c
index 2cf84aa..08c1686 100644
--- b/engine/client/cl_parse.c
+++ a/engine/client/cl_parse.c
@@ -285,6 +285,9 @@ void CL_ParseSoundPacket( sizebuf_t *msg, qboolean is_ambient )
}
else handle = cl.sound_index[sound]; // see precached sound
+ if( !cl.audio_prepped )
+ return; // too early
+
if( is_ambient )
{
S_AmbientSound( pos, entnum, handle, volume, attn, pitch, flags );
@@ -354,6 +357,18 @@ void CL_ParseRestoreSoundPacket( sizebuf_t *msg )
/*
==================
+CL_ParseServerTime
+
+==================
+*/
+void CL_ParseServerTime( sizebuf_t *msg )
+{
+ cl.mtime[1] = cl.mtime[0];
+ cl.mtime[0] = MSG_ReadFloat( msg );
+}
+
+/*
+==================
CL_ParseMovevars
==================
@@ -371,6 +386,7 @@ void CL_ParseMovevars( sizebuf_t *msg )
memcpy( &clgame.oldmovevars, &clgame.movevars, sizeof( movevars_t ));
// keep features an actual!
clgame.oldmovevars.features = clgame.movevars.features = host.features;
+ clgame.entities->curstate.scale = cl.refdef.movevars->waveHeight;
}
/*
@@ -1444,7 +1460,7 @@ void CL_ParseServerMessage( sizebuf_t *msg )
char *s;
int i, j, cmd;
int param1, param2;
- int bufStart;
+ int bufStart, playerbytes;
cls_message_debug.parsing = true; // begin parsing
starting_count = MSG_GetNumBytesRead( msg ); // updates each frame
@@ -1513,6 +1529,7 @@ void CL_ParseServerMessage( sizebuf_t *msg )
break;
case svc_sound:
CL_ParseSoundPacket( msg, false );
+ cl.frames[cl.parsecountmod].graphdata.sound += MSG_GetNumBytesRead( msg ) - bufStart;
break;
case svc_time:
// shuffle timestamps
@@ -1543,12 +1560,17 @@ void CL_ParseServerMessage( sizebuf_t *msg )
break;
case svc_clientdata:
CL_ParseClientData( msg );
+ cl.frames[cl.parsecountmod].graphdata.client += MSG_GetNumBytesRead( msg ) - bufStart;
break;
case svc_packetentities:
- CL_ParsePacketEntities( msg, false );
+ playerbytes = CL_ParsePacketEntities( msg, false );
+ cl.frames[cl.parsecountmod].graphdata.players += playerbytes;
+ cl.frames[cl.parsecountmod].graphdata.entities += MSG_GetNumBytesRead( msg ) - bufStart - playerbytes;
break;
case svc_deltapacketentities:
- CL_ParsePacketEntities( msg, true );
+ playerbytes = CL_ParsePacketEntities( msg, true );
+ cl.frames[cl.parsecountmod].graphdata.players += playerbytes;
+ cl.frames[cl.parsecountmod].graphdata.entities += MSG_GetNumBytesRead( msg ) - bufStart - playerbytes;
break;
case svc_updatepings:
CL_UpdateUserPings( msg );
@@ -1561,12 +1583,14 @@ void CL_ParseServerMessage( sizebuf_t *msg )
break;
case svc_restoresound:
CL_ParseRestoreSoundPacket( msg );
+ cl.frames[cl.parsecountmod].graphdata.sound += MSG_GetNumBytesRead( msg ) - bufStart;
break;
case svc_spawnstatic:
CL_ParseStaticEntity( msg );
break;
case svc_ambientsound:
CL_ParseSoundPacket( msg, true );
+ cl.frames[cl.parsecountmod].graphdata.sound += MSG_GetNumBytesRead( msg ) - bufStart;
break;
case svc_crosshairangle:
CL_ParseCrosshairAngle( msg );
@@ -1576,6 +1600,7 @@ void CL_ParseServerMessage( sizebuf_t *msg )
break;
case svc_temp_entity:
CL_ParseTempEntity( msg );
+ cl.frames[cl.parsecountmod].graphdata.tentities += MSG_GetNumBytesRead( msg ) - bufStart;
break;
case svc_setpause:
cl.refdef.paused = ( MSG_ReadOneBit( msg ) != 0 );
@@ -1591,9 +1616,11 @@ void CL_ParseServerMessage( sizebuf_t *msg )
break;
case svc_event:
CL_ParseEvent( msg );
+ cl.frames[cl.parsecountmod].graphdata.event += MSG_GetNumBytesRead( msg ) - bufStart;
break;
case svc_event_reliable:
CL_ParseReliableEvent( msg );
+ cl.frames[cl.parsecountmod].graphdata.event += MSG_GetNumBytesRead( msg ) - bufStart;
break;
case svc_updateuserinfo:
CL_UpdateUserinfo( msg );
@@ -1667,6 +1694,7 @@ void CL_ParseServerMessage( sizebuf_t *msg )
break;
default:
CL_ParseUserMessage( msg, cmd );
+ cl.frames[cl.parsecountmod].graphdata.usr += MSG_GetNumBytesRead( msg ) - bufStart;
break;
}
}
diff --git b/engine/client/cl_pmove.c a/engine/client/cl_pmove.c
index d736b64..f599843 100644
--- b/engine/client/cl_pmove.c
+++ a/engine/client/cl_pmove.c
@@ -29,6 +29,119 @@ void CL_ClearPhysEnts( void )
clgame.pmove->numphysent = 0;
}
+/*
+=============
+CL_PushPMStates
+
+=============
+*/
+void CL_PushPMStates( void )
+{
+ if( clgame.pushed )
+ {
+ MsgDev( D_ERROR, "PushPMStates called with pushed stack\n");
+ }
+ else
+ {
+ clgame.oldphyscount = clgame.pmove->numphysent;
+ clgame.oldviscount = clgame.pmove->numvisent;
+ clgame.pushed = true;
+ }
+
+}
+
+/*
+=============
+CL_PopPMStates
+
+=============
+*/
+void CL_PopPMStates( void )
+{
+ if( clgame.pushed )
+ {
+ clgame.pmove->numphysent = clgame.oldphyscount;
+ clgame.pmove->numvisent = clgame.oldviscount;
+ clgame.pushed = false;
+ }
+ else
+ {
+ MsgDev( D_ERROR, "PopPMStates called without stack\n");
+ }
+}
+
+/*
+=============
+CL_ComputePlayerOrigin
+
+FIXME: implement
+=============
+*/
+void CL_ComputePlayerOrigin( cl_entity_t *clent )
+{
+}
+
+/*
+=============
+CL_SetUpPlayerPrediction
+
+=============
+*/
+void CL_SetUpPlayerPrediction( int dopred, int bIncludeLocalClient )
+{
+ entity_state_t *state;
+ predicted_player_t *player;
+ cl_entity_t *clent;
+ int i;
+
+ for( i = 0; i < MAX_CLIENTS; i++ )
+ {
+ state = &cl.frames[cl.parsecountmod].playerstate[i];
+ player = &cls.predicted_players[i];
+
+ player->active = false;
+
+ if( state->messagenum != cl.parsecount )
+ continue; // not present this frame
+
+ if( !state->modelindex )
+ continue;
+
+ clent = CL_GetEntityByIndex( i + 1 );
+
+ // special for EF_NODRAW and local client?
+ if( FBitSet( state->effects, EF_NODRAW ) && !bIncludeLocalClient )
+ {
+ // don't include local player?
+ if( cl.playernum == i ) continue;
+
+ player->active = true;
+ player->movetype = state->movetype;
+ player->solid = state->solid;
+ player->usehull = state->usehull;
+
+ CL_ComputePlayerOrigin( clent );
+
+ VectorCopy( clent->origin, player->origin );
+ VectorCopy( clent->angles, player->angles );
+ }
+ else
+ {
+ player->active = true;
+ player->movetype = state->movetype;
+ player->solid = state->solid;
+ player->usehull = state->usehull;
+
+ // don't rewrite origin and angles of local client
+ if( cl.playernum == i )
+ continue;
+
+ VectorCopy( state->origin, player->origin );
+ VectorCopy( state->angles, player->angles );
+ }
+ }
+}
+
void CL_ClipPMoveToEntity( physent_t *pe, const vec3_t start, vec3_t mins, vec3_t maxs, const vec3_t end, pmtrace_t *tr )
{
ASSERT( tr != NULL );
@@ -211,25 +324,35 @@ pmove must be setup with world and solid entity hulls before calling
*/
void CL_SetSolidPlayers( int playernum )
{
- int j;
- extern vec3_t player_mins;
- extern vec3_t player_maxs;
+ entity_state_t *state;
cl_entity_t *ent;
physent_t *pe;
+ int i;
if( !cl_solid_players->integer )
return;
- for( j = 0; j < cl.maxclients; j++ )
+ for( i = 0; i < cl.maxclients; i++ )
{
// the player object never gets added
- if( j == playernum ) continue;
+ if( i == playernum ) continue;
- ent = CL_GetEntityByIndex( j + 1 );
+ ent = CL_GetEntityByIndex( i + 1 );
if( !ent || !ent->player )
continue; // not present this frame
+ state = cl.frames[cl.parsecountmod].playerstate + i;
+
+ if( state->effects & EF_NODRAW )
+ continue; // skip invisible
+
+ if( !state->solid )
+ continue; // not solid
+
+ if( !state->movetype )
+ continue; // dead
+
pe = &clgame.pmove->physents[clgame.pmove->numphysent];
if( CL_CopyEntityToPhysEnt( pe, ent ))
clgame.pmove->numphysent++;
@@ -550,9 +673,10 @@ static const char *pfnTraceTexture( int ground, float *vstart, float *vend )
static void pfnPlaySound( int channel, const char *sample, float volume, float attenuation, int fFlags, int pitch )
{
- sound_t snd = S_RegisterSound( sample );
+ if( !clgame.pmove->runfuncs )
+ return;
- S_StartSound( NULL, clgame.pmove->player_index + 1, channel, snd, volume, attenuation, pitch, fFlags );
+ S_StartSound( NULL, clgame.pmove->player_index + 1, channel, S_RegisterSound( sample ), volume, attenuation, pitch, fFlags );
}
static void pfnPlaybackEventFull( int flags, int clientindex, word eventindex, float delay, float *origin,
@@ -857,6 +981,10 @@ void CL_RunUsercmd( local_state_t *from, local_state_t *to, usercmd_t *u, qboole
// copy results back to client
CL_FinishPMove( clgame.pmove, to );
+ cl.predicted.lastground = clgame.pmove->onground;
+ if( cl.predicted.lastground > 0 && cl.predicted.lastground < clgame.pmove->numphysent )
+ cl.predicted.lastground = clgame.pmove->physents[cl.predicted.lastground].info;
+
clgame.dllFuncs.pfnPostRunCmd( from, to, &cmd, runfuncs, *time, random_seed );
*time += (double)cmd.msec / 1000.0;
}
@@ -878,26 +1006,30 @@ void CL_CheckPredictionError( void )
frame = ( cls.netchan.incoming_acknowledged ) & CL_UPDATE_MASK;
// compare what the server returned with what we had predicted it to be
- VectorSubtract( cl.frame.playerstate[cl.playernum].origin, cl.predicted_origins[frame], delta );
+ VectorSubtract( cl.frame.playerstate[cl.playernum].origin, cl.predicted.origins[frame], delta );
maxspd = ( clgame.movevars.maxvelocity * host.frametime );
len = VectorLength( delta );
// save the prediction error for interpolation
- if(( cl.frame.client.flags & EF_NOINTERP ) || len > maxspd )
+// if(( cl.frame.client.flags & EF_NOINTERP ) || len > maxspd )
+ if( len > 64.0f )
{
// a teleport or something or gamepaused
- VectorClear( cl.prediction_error );
+ VectorClear( cl.predicted.error );
}
else
{
if( cl_showerror->value && len > 0.5f )
MsgDev( D_ERROR, "prediction error on %i: %g\n", cl.parsecount, len );
- VectorCopy( cl.frame.playerstate[cl.playernum].origin, cl.predicted_origins[frame] );
+ VectorCopy( cl.frame.playerstate[cl.playernum].origin, cl.predicted.origins[frame] );
+
+ // save for error interpolation
+ VectorCopy( delta, cl.predicted.error );
- // save for error itnerpolation
- VectorCopy( delta, cl.prediction_error );
+ if(( len > 0.25f ) && ( cl.maxclients > 1 ))
+ cl.predicted.correction_time = cl_smoothtime->value;
}
}
@@ -912,6 +1044,8 @@ void CL_PostRunCmd( usercmd_t *ucmd, int random_seed )
{
local_state_t from, to;
+ memset( &from, 0, sizeof( local_state_t ));
+ memset( &to, 0, sizeof( local_state_t ));
memcpy( from.weapondata, cl.frame.weapondata, sizeof( from.weapondata ));
from.playerstate = cl.frame.playerstate[cl.playernum];
from.client = cl.frame.client;
@@ -922,6 +1056,35 @@ void CL_PostRunCmd( usercmd_t *ucmd, int random_seed )
/*
=================
+CL_FakeUsercmd
+
+Runs client weapons prediction code
+=================
+*/
+void CL_FakeUsercmd( local_state_t *from, local_state_t *to, usercmd_t *u, qboolean runfuncs, double *pfElapsed, unsigned int random_seed )
+{
+ usercmd_t cmd;
+ local_state_t temp;
+ usercmd_t split;
+
+ while( u->msec > 50 )
+ {
+ split = *u;
+ split.msec /= 2;
+ CL_FakeUsercmd( from, &temp, &split, runfuncs, pfElapsed, random_seed );
+ from = &temp;
+ u = &split;
+ }
+
+ cmd = *u;
+ *to = *from;
+
+ clgame.dllFuncs.pfnPostRunCmd( from, to, &cmd, runfuncs, *pfElapsed, random_seed );
+ *pfElapsed += cmd.msec / 1000.0;
+}
+
+/*
+=================
CL_PredictMovement
Sets cl.predicted_origin and cl.predicted_angles
@@ -941,22 +1104,66 @@ void CL_PredictMovement( void )
if( cls.state != ca_active ) return;
if( cls.demoplayback && cl.refdef.cmd != NULL )
- {
- // restore viewangles from cmd.angles
- VectorCopy( cl.refdef.cmd->viewangles, cl.refdef.cl_viewangles );
- }
+ CL_DemoInterpolateAngles();
if( !CL_IsInGame( )) return;
+ CL_SetUpPlayerPrediction( false, false );
+
// unpredicted pure angled values converted into axis
AngleVectors( cl.refdef.cl_viewangles, cl.refdef.forward, cl.refdef.right, cl.refdef.up );
ASSERT( cl.refdef.cmd != NULL );
if( !CL_IsPredicted( ))
- {
- // run commands even if client predicting is disabled - client expected it
- CL_PostRunCmd( cl.refdef.cmd, cls.lastoutgoingcommand );
+ {
+ // fake prediction code
+ // we need to perform cl_lw prediction while cl_predict is disabled
+ // because cl_lw is enabled by default in Half-Life
+ if( !cl_lw->integer )
+ {
+ cl.predicted.viewmodel = cl.frame.client.viewmodel;
+ return;
+ }
+
+ ack = cls.netchan.incoming_acknowledged;
+ outgoing_command = cls.netchan.outgoing_sequence;
+
+ from = &cl.predict[cl.parsecountmod];
+ from->playerstate = cl.frame.playerstate[cl.playernum];
+ from->client = cl.frame.client;
+ memcpy( from->weapondata, cl.frame.weapondata, sizeof( from->weapondata ));
+
+ time = cl.frame.time;
+
+ while( 1 )
+ {
+ // we've run too far forward
+ if( frame >= ( CL_UPDATE_BACKUP - 1 ))
+ break;
+
+ // Incoming_acknowledged is the last usercmd the server acknowledged having acted upon
+ current_command = ack + frame;
+ current_command_mod = current_command & CL_UPDATE_MASK;
+
+ // we've caught up to the current command.
+ if( current_command >= outgoing_command )
+ break;
+
+ to = &cl.predict[( cl.parsecountmod + frame ) & CL_UPDATE_MASK];
+
+ CL_FakeUsercmd( from, to, &cl.commands[current_command_mod].cmd,
+ !cl.commands[current_command_mod].processedfuncs,
+ &time, cls.netchan.incoming_acknowledged + frame );
+
+ cl.commands[current_command_mod].processedfuncs = true;
+
+ from = to;
+ frame++;
+ }
+
+ if( to )
+ cl.predicted.viewmodel = to->client.viewmodel;
return;
}
@@ -970,7 +1177,6 @@ void CL_PredictMovement( void )
time = cl.frame.time;
- CL_SetSolidEntities();
CL_SetSolidPlayers( cl.playernum );
while( 1 )
@@ -995,7 +1201,7 @@ void CL_PredictMovement( void )
cl.commands[current_command_mod].processedfuncs = true;
// save for debug checking
- VectorCopy( to->playerstate.origin, cl.predicted_origins[current_command_mod] );
+ VectorCopy( to->playerstate.origin, cl.predicted.origins[current_command_mod] );
from = to;
frame++;
@@ -1003,9 +1209,109 @@ void CL_PredictMovement( void )
if( to )
{
- VectorCopy( to->playerstate.origin, cl.predicted_origin );
- VectorCopy( to->client.velocity, cl.predicted_velocity );
- VectorCopy( to->client.view_ofs, cl.predicted_viewofs );
- VectorCopy( to->client.punchangle, cl.predicted_punchangle );
+ float t0 = cl.commands[( cl.parsecountmod + frame - 1) & CL_UPDATE_MASK].senttime;
+ float t1 = cl.commands[( cl.parsecountmod + frame ) & CL_UPDATE_MASK].senttime;
+ float t;
+
+ if( t0 == t1 )
+ {
+ t = 0.0f;
+ }
+ else
+ {
+ t = (host.realtime - t0) / (t1 - t0);
+ t = bound( 0.0f, t, 1.0f );
+ }
+
+ // was teleported
+ if( fabs( to->playerstate.origin[0] - from->playerstate.origin[0] ) > 128.0f ||
+ fabs( to->playerstate.origin[1] - from->playerstate.origin[1] ) > 128.0f ||
+ fabs( to->playerstate.origin[2] - from->playerstate.origin[2] ) > 128.0f )
+ {
+ VectorCopy( to->playerstate.origin, cl.predicted.origin );
+ VectorCopy( to->client.velocity, cl.predicted.velocity );
+ VectorCopy( to->client.punchangle, cl.predicted.punchangle );
+ VectorCopy( to->client.view_ofs, cl.predicted.viewofs );
+ }
+ else
+ {
+ vec3_t delta_origin, delta_punch, delta_vel;
+
+ VectorSubtract( to->playerstate.origin, from->playerstate.origin, delta_origin );
+ VectorSubtract( to->client.velocity, from->client.velocity, delta_vel );
+ VectorSubtract( to->client.punchangle, from->client.punchangle, delta_punch );
+
+ VectorMA( from->playerstate.origin, t, delta_origin, cl.predicted.origin );
+ VectorMA( from->client.velocity, t, delta_vel, cl.predicted.velocity );
+ VectorMA( from->client.punchangle, t, delta_punch, cl.predicted.punchangle );
+
+ if( from->playerstate.usehull == to->playerstate.usehull )
+ {
+ vec3_t delta_viewofs;
+
+ VectorSubtract( to->client.view_ofs, from->client.view_ofs, delta_viewofs );
+ VectorMA( from->client.view_ofs, t, delta_viewofs, cl.predicted.viewofs );
+ }
+ }
+
+ cl.predicted.waterlevel = to->client.waterlevel;
+ cl.predicted.viewmodel = to->client.viewmodel;
+ cl.predicted.usehull = to->playerstate.usehull;
+
+ if( to->client.flags & FL_ONGROUND )
+ {
+ cl_entity_t *ent = CL_GetEntityByIndex( cl.predicted.lastground );
+
+ cl.predicted.onground = cl.predicted.lastground;
+ cl.predicted.moving = 0;
+
+ if( ent )
+ {
+ vec3_t delta;
+ delta[0] = ent->curstate.origin[0] - ent->prevstate.origin[0];
+ delta[1] = ent->curstate.origin[1] - ent->prevstate.origin[1];
+ delta[2] = 0.0f;
+
+ if( VectorLength( delta ) > 0.0f )
+ {
+ cl.predicted.correction_time = 0;
+ cl.predicted.moving = 1;
+ }
+ }
+ }
+ else
+ {
+ cl.predicted.onground = -1;
+ cl.predicted.moving = 0;
+ }
+
+ if ( cl.predicted.correction_time > 0.0 && !cl_nosmooth->value && cl_smoothtime->value )
+ {
+ float d;
+ int i;
+
+ cl.predicted.correction_time = cl.predicted.correction_time - host.frametime;
+
+ if( cl_smoothtime->value <= 0 )
+ Cvar_SetFloat( "cl_smoothtime", 0.1 );
+
+ if( cl.predicted.correction_time < 0 )
+ cl.predicted.correction_time = 0;
+
+ if( cl_smoothtime->value <= cl.predicted.correction_time )
+ cl.predicted.correction_time = cl_smoothtime->value;
+
+ d = cl.predicted.correction_time / cl_smoothtime->value;
+
+ for( i = 0; i < 3; i++ )
+ {
+ cl.predicted.origin[i] = cl.predicted.lastorigin[i] + ( cl.predicted.origin[i] - cl.predicted.lastorigin[i] ) * (1.0 - d);
+ }
+ }
+
+ VectorCopy( cl.predicted.origin, cl.predicted.lastorigin );
+ CL_SetIdealPitch();
}
+
+ CL_CheckPredictionError();
}
\ No newline at end of file
diff --git b/engine/client/cl_remap.c a/engine/client/cl_remap.c
index ec5ff8e..869512e 100644
--- b/engine/client/cl_remap.c
+++ a/engine/client/cl_remap.c
@@ -127,7 +127,6 @@ void CL_DuplicateTexture( mstudiotexture_t *ptexture, int topcolor, int bottomco
raw = CL_CreateRawTextureFromPixels( tx, &size, topcolor, bottomcolor );
ptexture->index = GL_LoadTexture( texname, raw, size, TF_FORCE_COLOR, NULL ); // do copy
- GL_SetTextureType( ptexture->index, TEX_REMAP );
// restore original palette
memcpy( pal, paletteBackup, 768 );
diff --git b/engine/client/cl_scrn.c a/engine/client/cl_scrn.c
index f4dd7bf..4fe17b7 100644
--- b/engine/client/cl_scrn.c
+++ a/engine/client/cl_scrn.c
@@ -44,21 +44,21 @@ static qboolean scr_init = false;
SCR_DrawFPS
==============
*/
-void SCR_DrawFPS( void )
+void SCR_DrawFPS( int height )
{
float calc;
rgba_t color;
+ double newtime;
static double nexttime = 0, lasttime = 0;
static double framerate = 0;
static int framecount = 0;
static int minfps = 9999;
static int maxfps = 0;
- double newtime;
char fpsstring[64];
int offset;
- if( cls.state != ca_active ) return;
- if( !cl_showfps->integer || cl.background ) return;
+ if( cls.state != ca_active || !cl_showfps->integer || cl.background )
+ return;
switch( cls.scrshot_action )
{
@@ -74,12 +74,12 @@ void SCR_DrawFPS( void )
{
framerate = framecount / (newtime - lasttime);
lasttime = newtime;
- nexttime = max( nexttime + 1, lasttime - 1 );
+ nexttime = Q_max( nexttime + 1.0, lasttime - 1.0 );
framecount = 0;
}
- framecount++;
calc = framerate;
+ framecount++;
if( calc < 1.0f )
{
@@ -100,7 +100,7 @@ void SCR_DrawFPS( void )
}
Con_DrawStringLen( fpsstring, &offset, NULL );
- Con_DrawString( scr_width->integer - offset - 3, 4, fpsstring, color );
+ Con_DrawString( scr_width->integer - offset - 4, height, fpsstring, color );
}
/*
@@ -116,42 +116,36 @@ void SCR_NetSpeeds( void )
int x, y, height;
char *p, *start, *end;
float time = cl.mtime[0];
+ static int min_svfps = 100;
+ static int max_svfps = 0;
+ int cur_svfps = 0;
+ static int min_clfps = 100;
+ static int max_clfps = 0;
+ int cur_clfps = 0;
rgba_t color;
- if( !net_speeds->integer ) return;
- if( cls.state != ca_active ) return;
+ if( !net_speeds->integer || cls.demoplayback || cls.state != ca_active )
+ return;
- switch( net_speeds->integer )
+ if( cl_serverframetime() != 0 )
{
- case 1:
- if( cls.netchan.compress )
- {
- Q_snprintf( msg, sizeof( msg ), "Game Time: %02d:%02d\nTotal received from server:\n Huffman %s\nUncompressed %s\n",
- (int)(time / 60.0f ), (int)fmod( time, 60.0f ), Q_memprint( cls.netchan.total_received ), Q_memprint( cls.netchan.total_received_uncompressed ));
- }
- else
- {
- Q_snprintf( msg, sizeof( msg ), "Game Time: %02d:%02d\nTotal received from server:\nUncompressed %s\n",
- (int)(time / 60.0f ), (int)fmod( time, 60.0f ), Q_memprint( cls.netchan.total_received_uncompressed ));
- }
- break;
- case 2:
- if( cls.netchan.compress )
- {
- Q_snprintf( msg, sizeof( msg ), "Game Time: %02d:%02d\nTotal sended to server:\nHuffman %s\nUncompressed %s\n",
- (int)(time / 60.0f ), (int)fmod( time, 60.0f ), Q_memprint( cls.netchan.total_sended ), Q_memprint( cls.netchan.total_sended_uncompressed ));
- }
- else
- {
- Q_snprintf( msg, sizeof( msg ), "Game Time: %02d:%02d\nTotal sended to server:\nUncompressed %s\n",
- (int)(time / 60.0f ), (int)fmod( time, 60.0f ), Q_memprint( cls.netchan.total_sended_uncompressed ));
- }
- break;
- default: return;
+ cur_svfps = Q_rint( 1.0f / cl_serverframetime( ));
+ if( cur_svfps < min_svfps ) min_svfps = cur_svfps;
+ if( cur_svfps > max_svfps ) max_svfps = cur_svfps;
}
+ if( cl_clientframetime() != 0 )
+ {
+ cur_clfps = Q_rint( 1.0f / cl_clientframetime( ));
+ if( cur_clfps < min_clfps ) min_clfps = cur_clfps;
+ if( cur_clfps > max_clfps ) max_clfps = cur_clfps;
+ }
+
+ Q_snprintf( msg, sizeof( msg ), "sv fps: ^1%4i min, ^3%4i cur, ^2%4i max\ncl fps: ^1%4i min, ^3%4i cur, ^2%4i max\nGame Time: %02d:%02d\nTotal sended to server: %s\nTotal received from server: %s\n",
+ min_svfps, cur_svfps, max_svfps, min_clfps, cur_clfps, max_clfps, (int)(time / 60.0f ), (int)fmod( time, 60.0f ), Q_memprint( cls.netchan.total_sended ), Q_memprint( cls.netchan.total_received ));
+
x = scr_width->integer - 320;
- y = 256;
+ y = 384;
Con_DrawStringLen( NULL, NULL, &height );
MakeRGBA( color, 255, 255, 255, 255 );
@@ -258,7 +252,7 @@ void SCR_MakeScreenShot( void )
{
// snapshots don't writes message about image
if( cls.scrshot_action != scrshot_snapshot )
- MsgDev( D_AICONSOLE, "Write %s\n", cls.shotname );
+ MsgDev( D_REPORT, "Write %s\n", cls.shotname );
}
else MsgDev( D_ERROR, "Unable to write %s\n", cls.shotname );
@@ -619,9 +613,9 @@ void SCR_Init( void )
Cmd_AddCommand( "sizeup", SCR_SizeUp_f, "screen size up to 10 points" );
Cmd_AddCommand( "sizedown", SCR_SizeDown_f, "screen size down to 10 points" );
- if( host.state != HOST_RESTART && !UI_LoadProgs( ))
+ if( !UI_LoadProgs( ))
{
- Msg( "^1Error: ^7can't initialize menu.dll\n" ); // there is non fatal for us
+ Msg( "^1Error: ^7can't initialize gameui.dll\n" ); // there is non fatal for us
if( !host.developer ) host.developer = 1; // we need console, because menu is missing
}
@@ -629,14 +623,12 @@ void SCR_Init( void )
SCR_InstallParticlePalette ();
SCR_RegisterTextures ();
SCR_InitCinematic();
+ CL_InitNetgraph();
SCR_VidInit();
- if( host.state != HOST_RESTART )
- {
- if( host.developer && Sys_CheckParm( "-toconsole" ))
- Cbuf_AddText( "toggleconsole\n" );
- else UI_SetActiveMenu( true );
- }
+ if( host.developer && Sys_CheckParm( "-toconsole" ))
+ Cbuf_AddText( "toggleconsole\n" );
+ else UI_SetActiveMenu( true );
scr_init = true;
}
@@ -650,9 +642,7 @@ void SCR_Shutdown( void )
Cmd_RemoveCommand( "skyname" );
Cmd_RemoveCommand( "viewpos" );
UI_SetActiveMenu( false );
-
- if( host.state != HOST_RESTART )
- UI_UnloadProgs();
+ UI_UnloadProgs();
scr_init = false;
}
\ No newline at end of file
diff --git b/engine/client/cl_tent.c a/engine/client/cl_tent.c
index d047f9c..0681936 100644
--- b/engine/client/cl_tent.c
+++ a/engine/client/cl_tent.c
@@ -32,7 +32,7 @@ TEMPENTS MANAGEMENT
==============================================================
*/
-#define MAX_MUZZLEFLASH 4
+#define MAX_MUZZLEFLASH 3
#define SHARD_VOLUME 12.0f // on shard ever n^3 units
#define SF_FUNNEL_REVERSE 1
@@ -65,7 +65,6 @@ void CL_RegisterMuzzleFlashes( void )
cl_muzzleflash[0] = CL_FindModelIndex( "sprites/muzzleflash1.spr" );
cl_muzzleflash[1] = CL_FindModelIndex( "sprites/muzzleflash2.spr" );
cl_muzzleflash[2] = CL_FindModelIndex( "sprites/muzzleflash3.spr" );
- cl_muzzleflash[3] = CL_FindModelIndex( "sprites/muzzleflash.spr" );
// update registration for shellchrome
cls.hChromeSprite = pfnSPR_Load( "sprites/shellchrome.spr" );
@@ -442,6 +441,9 @@ void CL_FizzEffect( cl_entity_t *pent, int modelIndex, int density )
if( !pent || Mod_GetType( modelIndex ) == mod_bad )
return;
+ if( pent->curstate.modelindex <= 0 )
+ return;
+
count = density + 1;
density = count * 3 + 6;
@@ -524,7 +526,7 @@ void CL_Bubbles( const vec3_t mins, const vec3_t maxs, float height, int modelIn
pTemp->x = origin[0];
pTemp->y = origin[1];
- angle = Com_RandomLong( -M_PI, M_PI );
+ angle = Com_RandomFloat( -M_PI, M_PI );
SinCos( angle, &sine, &cosine );
zspeed = Com_RandomLong( 80, 140 );
@@ -569,7 +571,7 @@ void CL_BubbleTrail( const vec3_t start, const vec3_t end, float flWaterZ, int m
pTemp->x = origin[0];
pTemp->y = origin[1];
- angle = Com_RandomLong( -M_PI, M_PI );
+ angle = Com_RandomFloat( -M_PI, M_PI );
zspeed = Com_RandomLong( 80, 140 );
VectorSet( pTemp->entity.baseline.origin, speed * cos( angle ), speed * sin( angle ), zspeed );
@@ -757,8 +759,8 @@ void CL_MuzzleFlash( const vec3_t pos, int type )
int index, modelIndex, frameCount;
float scale;
- index = bound( 0, type % 5, MAX_MUZZLEFLASH - 1 );
- scale = (type / 5) * 0.1f;
+ index = ( type % 10 ) % MAX_MUZZLEFLASH;
+ scale = ( type / 10 ) * 0.1f;
if( scale == 0.0f ) scale = 0.5f;
modelIndex = cl_muzzleflash[index];
@@ -2343,7 +2345,7 @@ void CL_SetLightstyle( int style, const char *s, float f )
break;
}
}
- MsgDev( D_AICONSOLE, "Lightstyle %i (%s), interp %s\n", style, ls->pattern, ls->interp ? "Yes" : "No" );
+ MsgDev( D_REPORT, "Lightstyle %i (%s), interp %s\n", style, ls->pattern, ls->interp ? "Yes" : "No" );
}
/*
@@ -2650,7 +2652,7 @@ int CL_DecalIndexFromName( const char *name )
return 0;
// look through the loaded sprite name list for SpriteName
- for( i = 0; i < MAX_DECALS && host.draw_decals[i+1][0]; i++ )
+ for( i = 0; i < (MAX_DECALS - 1) && host.draw_decals[i+1][0]; i++ )
{
if( !Q_stricmp( name, host.draw_decals[i+1] ))
return i+1;
diff --git b/engine/client/cl_video.c a/engine/client/cl_video.c
index 2160274..4a7dea0 100644
--- b/engine/client/cl_video.c
+++ a/engine/client/cl_video.c
@@ -59,14 +59,17 @@ qboolean SCR_NextMovie( void )
{
string str;
- S_StopAllSounds();
- SCR_StopCinematic();
-
if( cls.movienum == -1 )
+ {
+ S_StopAllSounds();
+ SCR_StopCinematic();
return false; // don't play movies
+ }
if( !cls.movies[cls.movienum][0] || cls.movienum == MAX_MOVIES )
{
+ S_StopAllSounds();
+ SCR_StopCinematic();
cls.movienum = -1;
return false;
}
@@ -146,7 +149,10 @@ void SCR_RunCinematic( void )
return;
if( !AVI_IsActive( cin_state ))
+ {
+ SCR_NextMovie( );
return;
+ }
if( UI_IsVisible( ))
{
diff --git b/engine/client/cl_view.c a/engine/client/cl_view.c
index a587f65..221b49b 100644
--- b/engine/client/cl_view.c
+++ a/engine/client/cl_view.c
@@ -20,194 +20,6 @@ GNU General Public License for more details.
#include "gl_local.h"
#include "vgui_draw.h"
-/*
-===============
-V_SetupRefDef
-
-update refdef values each frame
-===============
-*/
-void V_SetupRefDef( void )
-{
- cl_entity_t *clent;
- int size;
- int sb_lines;
-
- clent = CL_GetLocalPlayer ();
-
- clgame.entities->curstate.scale = clgame.movevars.waveHeight;
- clgame.viewent.curstate.modelindex = cl.frame.client.viewmodel;
- clgame.viewent.model = Mod_Handle( clgame.viewent.curstate.modelindex );
- clgame.viewent.curstate.number = cl.playernum + 1;
- clgame.viewent.curstate.entityType = ET_NORMAL;
- clgame.viewent.index = cl.playernum + 1;
-
- cl.refdef.movevars = &clgame.movevars;
- cl.refdef.onground = ( cl.frame.client.flags & FL_ONGROUND ) ? 1 : 0;
- cl.refdef.health = cl.frame.client.health;
- cl.refdef.playernum = cl.playernum;
- cl.refdef.max_entities = clgame.maxEntities;
- cl.refdef.maxclients = cl.maxclients;
- cl.refdef.time = cl.time;
- cl.refdef.frametime = cl.time - cl.oldtime;
- cl.refdef.demoplayback = cls.demoplayback;
- cl.refdef.smoothing = cl_smooth->integer;
- cl.refdef.viewsize = scr_viewsize->integer;
- cl.refdef.waterlevel = cl.frame.client.waterlevel;
- cl.refdef.onlyClientDraw = 0; // reset clientdraw
- cl.refdef.hardware = true; // always true
- cl.refdef.spectator = (clent->curstate.spectator != 0);
- cl.refdef.nextView = 0;
-
- SCR_AddDirtyPoint( 0, 0 );
- SCR_AddDirtyPoint( scr_width->integer - 1, scr_height->integer - 1 );
-
- if( cl.refdef.viewsize >= 120 )
- sb_lines = 0; // no status bar at all
- else if( cl.refdef.viewsize >= 110 )
- sb_lines = 24; // no inventory
- else sb_lines = 48;
-
- size = min( scr_viewsize->integer, 100 );
-
- cl.refdef.viewport[2] = scr_width->integer * size / 100;
- cl.refdef.viewport[3] = scr_height->integer * size / 100;
-
- if( cl.refdef.viewport[3] > scr_height->integer - sb_lines )
- cl.refdef.viewport[3] = scr_height->integer - sb_lines;
- if( cl.refdef.viewport[3] > scr_height->integer )
- cl.refdef.viewport[3] = scr_height->integer;
-
- cl.refdef.viewport[0] = (scr_width->integer - cl.refdef.viewport[2]) / 2;
- cl.refdef.viewport[1] = (scr_height->integer - sb_lines - cl.refdef.viewport[3]) / 2;
-
- // calc FOV
- cl.refdef.fov_x = cl.data.fov; // this is a final fov value
- cl.refdef.fov_y = V_CalcFov( &cl.refdef.fov_x, cl.refdef.viewport[2], cl.refdef.viewport[3] );
-
- // adjust FOV for widescreen
- if( glState.wideScreen && r_adjust_fov->integer )
- V_AdjustFov( &cl.refdef.fov_x, &cl.refdef.fov_y, cl.refdef.viewport[2], cl.refdef.viewport[3], false );
-
- if( CL_IsPredicted( ) && !cl.refdef.demoplayback )
- {
- VectorMA( cl.predicted_origin, -cl.lerpBack, cl.prediction_error, cl.refdef.simorg );
- VectorCopy( cl.predicted_origin, cl.refdef.simorg );
- VectorCopy( cl.predicted_velocity, cl.refdef.simvel );
- VectorCopy( cl.predicted_viewofs, cl.refdef.viewheight );
- VectorCopy( cl.predicted_punchangle, cl.refdef.punchangle );
- }
- else
- {
- VectorCopy( cl.frame.client.origin, cl.refdef.simorg );
- VectorCopy( cl.frame.client.view_ofs, cl.refdef.viewheight );
- VectorCopy( cl.frame.client.velocity, cl.refdef.simvel );
- VectorCopy( cl.frame.client.punchangle, cl.refdef.punchangle );
- }
-}
-
-/*
-===============
-V_SetupOverviewState
-
-Get initial overview values
-===============
-*/
-void V_SetupOverviewState( void )
-{
- ref_overview_t *ov = &clgame.overView;
- float mapAspect, screenAspect, aspect;
-
- ov->rotated = ( world.size[1] <= world.size[0] ) ? true : false;
-
- // calculate nearest aspect
- mapAspect = world.size[!ov->rotated] / world.size[ov->rotated];
- screenAspect = (float)glState.width / (float)glState.height;
- aspect = max( mapAspect, screenAspect );
-
- ov->zNear = world.maxs[2];
- ov->zFar = world.mins[2];
- ov->flZoom = ( 8192.0f / world.size[ov->rotated] ) / aspect;
-
- VectorAverage( world.mins, world.maxs, ov->origin );
-}
-
-/*
-===============
-V_WriteOverviewScript
-
-Create overview scrip file
-===============
-*/
-void V_WriteOverviewScript( void )
-{
- ref_overview_t *ov = &clgame.overView;
- string filename;
- file_t *f;
-
- Q_snprintf( filename, sizeof( filename ), "overviews/%s.txt", clgame.mapname );
-
- f = FS_Open( filename, "w", false );
- if( !f ) return;
-
- FS_Printf( f, "// overview description file for %s.bsp\n\n", clgame.mapname );
- FS_Print( f, "global\n{\n" );
- FS_Printf( f, "\tZOOM\t%.2f\n", ov->flZoom );
- FS_Printf( f, "\tORIGIN\t%.f\t%.f\t%.f\n", ov->origin[0], ov->origin[1], ov->zFar + 1 );
- FS_Printf( f, "\tROTATED\t%i\n", ov->rotated ? 1 : 0 );
- FS_Print( f, "}\n\nlayer\n{\n" );
- FS_Printf( f, "\tIMAGE\t\"overviews/%s.bmp\"\n", clgame.mapname );
- FS_Printf( f, "\tHEIGHT\t%.f\n", ov->zFar + 1 ); // ???
- FS_Print( f, "}\n" );
-
- FS_Close( f );
-}
-
-/*
-===============
-V_ProcessOverviewCmds
-
-Transform user movement into overview adjust
-===============
-*/
-void V_ProcessOverviewCmds( usercmd_t *cmd )
-{
- ref_overview_t *ov = &clgame.overView;
- int sign = 1;
-
- if( !gl_overview->integer ) return;
-
- if( ov->flZoom < 0.0f ) sign = -1;
-
- if( cmd->upmove > 0.0f ) ov->zNear += 1.0f;
- else if( cmd->upmove < 0.0f ) ov->zNear -= 1.0f;
-
- if( cmd->buttons & IN_JUMP ) ov->zFar += 1.0f;
- else if( cmd->buttons & IN_DUCK ) ov->zFar -= 1.0f;
-
- if( cmd->buttons & IN_FORWARD ) ov->origin[ov->rotated] -= sign * 1.0f;
- else if( cmd->buttons & IN_BACK ) ov->origin[ov->rotated] += sign * 1.0f;
-
- if( ov->rotated )
- {
- if( cmd->buttons & ( IN_RIGHT|IN_MOVERIGHT ))
- ov->origin[0] -= sign * 1.0f;
- else if( cmd->buttons & ( IN_LEFT|IN_MOVELEFT ))
- ov->origin[0] += sign * 1.0f;
- }
- else
- {
- if( cmd->buttons & ( IN_RIGHT|IN_MOVERIGHT ))
- ov->origin[1] += sign * 1.0f;
- else if( cmd->buttons & ( IN_LEFT|IN_MOVELEFT ))
- ov->origin[1] -= sign * 1.0f;
- }
-
- if( cmd->buttons & IN_ATTACK ) ov->flZoom += 0.01f;
- else if( cmd->buttons & IN_ATTACK2 ) ov->flZoom -= 0.01f;
-
- if( ov->flZoom == 0.0f ) ov->flZoom = 0.0001f; // to prevent disivion by zero
-}
/*
===============
@@ -216,7 +28,7 @@ V_MergeOverviewRefdef
merge refdef with overview settings
===============
*/
-void V_MergeOverviewRefdef( ref_params_t *fd )
+void V_MergeOverviewRefdef( void )
{
ref_overview_t *ov = &clgame.overView;
float aspect;
@@ -243,99 +55,134 @@ void V_MergeOverviewRefdef( ref_params_t *fd )
ov->flZoom, ov->origin[0], ov->origin[1], ov->origin[2], ov->zNear, ov->zFar, ov->rotated );
}
- VectorCopy( ov->origin, fd->vieworg );
- fd->vieworg[2] = ov->zFar + ov->zNear;
- Vector2Copy( fd->vieworg, mins );
- Vector2Copy( fd->vieworg, maxs );
+ VectorCopy( ov->origin, cl.refdef.vieworg );
+ cl.refdef.vieworg[2] = ov->zFar + ov->zNear;
+ Vector2Copy( cl.refdef.vieworg, mins );
+ Vector2Copy( cl.refdef.vieworg, maxs );
mins[!ov->rotated] += ov->xLeft;
maxs[!ov->rotated] += ov->xRight;
mins[ov->rotated] += ov->xTop;
maxs[ov->rotated] += ov->xBottom;
- fd->viewangles[0] = 90.0f;
- fd->viewangles[1] = 90.0f;
- fd->viewangles[2] = (ov->rotated) ? (ov->flZoom < 0.0f) ? 180.0f : 0.0f : (ov->flZoom < 0.0f) ? -90.0f : 90.0f;
+ cl.refdef.viewangles[0] = 90.0f;
+ cl.refdef.viewangles[1] = 90.0f;
+ cl.refdef.viewangles[2] = (ov->rotated) ? (ov->flZoom < 0.0f) ? 180.0f : 0.0f : (ov->flZoom < 0.0f) ? -90.0f : 90.0f;
Mod_SetOrthoBounds( mins, maxs );
}
/*
===============
-V_ProcessShowTexturesCmds
+V_CalcViewRect
-navigate around texture atlas
+calc frame rectangle (Quake1 style)
===============
*/
-void V_ProcessShowTexturesCmds( usercmd_t *cmd )
+void V_CalcViewRect( void )
{
- static int oldbuttons;
- int changed;
- int pressed, released;
+ int size, sb_lines;
+
+ if( scr_viewsize->integer >= 120 )
+ sb_lines = 0; // no status bar at all
+ else if( scr_viewsize->integer >= 110 )
+ sb_lines = 24; // no inventory
+ else sb_lines = 48;
- if( !gl_showtextures->integer ) return;
+ size = Q_min( scr_viewsize->integer, 100 );
+
+ cl.refdef.viewport[2] = scr_width->integer * size / 100;
+ cl.refdef.viewport[3] = scr_height->integer * size / 100;
+
+ if( cl.refdef.viewport[3] > scr_height->integer - sb_lines )
+ cl.refdef.viewport[3] = scr_height->integer - sb_lines;
+ if( cl.refdef.viewport[3] > scr_height->integer )
+ cl.refdef.viewport[3] = scr_height->integer;
- changed = (oldbuttons ^ cmd->buttons);
- pressed = changed & cmd->buttons;
- released = changed & (~cmd->buttons);
+ cl.refdef.viewport[0] = ( scr_width->integer - cl.refdef.viewport[2] ) / 2;
+ cl.refdef.viewport[1] = ( scr_height->integer - sb_lines - cl.refdef.viewport[3] ) / 2;
- if( released & ( IN_RIGHT|IN_MOVERIGHT ))
- Cvar_SetFloat( "r_showtextures", gl_showtextures->integer + 1 );
- if( released & ( IN_LEFT|IN_MOVELEFT ))
- Cvar_SetFloat( "r_showtextures", max( 1, gl_showtextures->integer - 1 ));
- oldbuttons = cmd->buttons;
}
/*
===============
-V_CalcRefDef
+V_SetupRefDef
-sets cl.refdef view values
+update refdef values each frame
===============
*/
-void V_CalcRefDef( void )
+void V_SetupRefDef( void )
{
- R_Set2DMode( false );
- tr.framecount++; // g-cont. keep actual frame for all viewpasses
+ cl_entity_t *clent;
- do
- {
- clgame.dllFuncs.pfnCalcRefdef( &cl.refdef );
- V_MergeOverviewRefdef( &cl.refdef );
- R_RenderFrame( &cl.refdef, true );
- cl.refdef.onlyClientDraw = false;
- } while( cl.refdef.nextView );
+ // compute viewport rectangle
+ V_CalcViewRect();
- // Xash3D extension. draw debug triangles on a server
- SV_DrawDebugTriangles ();
+ clent = CL_GetLocalPlayer ();
- SCR_AddDirtyPoint( cl.refdef.viewport[0], cl.refdef.viewport[1] );
- SCR_AddDirtyPoint( cl.refdef.viewport[0] + cl.refdef.viewport[2] - 1, cl.refdef.viewport[1] + cl.refdef.viewport[3] - 1 );
-}
+ clgame.entities->curstate.scale = clgame.movevars.waveHeight;
-//============================================================================
+ cl.refdef.movevars = &clgame.movevars;
+ cl.refdef.health = cl.frame.client.health;
+ cl.refdef.playernum = cl.playernum;
+ cl.refdef.max_entities = clgame.maxEntities;
+ cl.refdef.maxclients = cl.maxclients;
+ cl.refdef.time = cl.time;
+ cl.refdef.frametime = cl.time - cl.oldtime;
+ cl.refdef.demoplayback = cls.demoplayback;
+ cl.refdef.viewsize = scr_viewsize->integer;
+ cl.refdef.onlyClientDraw = 0; // reset clientdraw
+ cl.refdef.hardware = true; // always true
+ cl.refdef.spectator = (clent->curstate.spectator != 0);
+ cl.refdef.smoothing = cl.first_frame; // NOTE: currently this used to prevent ugly un-duck effect while level is changed
+ cl.scr_fov = bound( 1.0f, cl.scr_fov, 179.0f );
+ cl.refdef.nextView = 0;
-/*
-==================
-V_RenderView
+ // calc FOV
+ cl.refdef.fov_x = cl.scr_fov; // this is a final fov value
+ cl.refdef.fov_y = V_CalcFov( &cl.refdef.fov_x, cl.refdef.viewport[2], cl.refdef.viewport[3] );
-==================
-*/
-void V_RenderView( void )
-{
- if( !cl.video_prepped || ( UI_IsVisible() && !cl.background ))
- return; // still loading
+ // adjust FOV for widescreen
+ if( glState.wideScreen && r_adjust_fov->integer )
+ V_AdjustFov( &cl.refdef.fov_x, &cl.refdef.fov_y, cl.refdef.viewport[2], cl.refdef.viewport[3], false );
+
+ if( CL_IsPredicted( ) && !cl.first_frame )
+ {
+ VectorCopy( cl.predicted.origin, cl.refdef.simorg );
+ VectorCopy( cl.predicted.velocity, cl.refdef.simvel );
+ VectorCopy( cl.predicted.viewofs, cl.refdef.viewheight );
+ VectorCopy( cl.predicted.punchangle, cl.refdef.punchangle );
+ cl.refdef.onground = ( cl.predicted.onground == -1 ) ? false : true;
+ cl.refdef.waterlevel = cl.predicted.waterlevel;
+ }
+ else
+ {
+ VectorCopy( cl.frame.client.origin, cl.refdef.simorg );
+ VectorCopy( cl.frame.client.view_ofs, cl.refdef.viewheight );
+ VectorCopy( cl.frame.client.velocity, cl.refdef.simvel );
+ VectorCopy( cl.frame.client.punchangle, cl.refdef.punchangle );
+ cl.refdef.onground = (cl.frame.client.flags & FL_ONGROUND) ? 1 : 0;
+ cl.refdef.waterlevel = cl.frame.client.waterlevel;
+ }
+
+ // setup the viewent variables
+ if( cl_lw->value ) clgame.viewent.curstate.modelindex = cl.predicted.viewmodel;
+ else clgame.viewent.curstate.modelindex = cl.frame.client.viewmodel;
+ clgame.viewent.model = Mod_Handle( clgame.viewent.curstate.modelindex );
+ clgame.viewent.curstate.number = cl.playernum + 1;
+ clgame.viewent.curstate.entityType = ET_NORMAL;
+ clgame.viewent.index = cl.playernum + 1;
- if( cl.frame.valid && ( cl.force_refdef || !cl.refdef.paused ))
+ // calc refdef first so viewent can get an actual
+ // player position, angles etc
+ if( FBitSet( host.features, ENGINE_FIXED_FRAMERATE ))
{
- cl.force_refdef = false;
+ clgame.dllFuncs.pfnCalcRefdef( &cl.refdef );
+ V_MergeOverviewRefdef();
R_ClearScene ();
CL_AddEntities ();
- V_SetupRefDef ();
}
-
- V_CalcRefDef ();
}
/*
@@ -361,7 +208,7 @@ qboolean V_PreRender( void )
{
if(( host.realtime - cls.disable_screen ) > cl_timeout->value )
{
- MsgDev( D_NOTE, "V_PreRender: loading plaque timed out.\n" );
+ MsgDev( D_ERROR, "V_PreRender: loading plaque timed out\n" );
cls.disable_screen = 0.0f;
}
return false;
@@ -372,6 +219,53 @@ qboolean V_PreRender( void )
return true;
}
+//============================================================================
+
+/*
+==================
+V_RenderView
+
+==================
+*/
+void V_RenderView( void )
+{
+ if( !cl.video_prepped || ( UI_IsVisible() && !cl.background ))
+ return; // still loading
+
+ if( !FBitSet( host.features, ENGINE_FIXED_FRAMERATE ))
+ {
+ if( cl.frame.valid && ( cl.force_refdef || !cl.refdef.paused ))
+ {
+ cl.force_refdef = false;
+
+ R_ClearScene ();
+ CL_AddEntities ();
+ V_SetupRefDef ();
+ }
+ }
+
+ R_Set2DMode( false );
+ SCR_AddDirtyPoint( 0, 0 );
+ SCR_AddDirtyPoint( scr_width->integer - 1, scr_height->integer - 1 );
+
+ tr.framecount++; // g-cont. keep actual frame for all viewpasses
+
+ if( !FBitSet( host.features, ENGINE_FIXED_FRAMERATE ))
+ {
+ do
+ {
+ clgame.dllFuncs.pfnCalcRefdef( &cl.refdef );
+ V_MergeOverviewRefdef();
+ R_RenderFrame( &cl.refdef, true );
+ cl.refdef.onlyClientDraw = false;
+ } while( cl.refdef.nextView );
+ }
+ else R_RenderFrame( &cl.refdef, true );
+
+ // draw debug triangles on a server
+ SV_DrawDebugTriangles ();
+}
+
/*
==================
V_PostRender
@@ -380,11 +274,12 @@ V_PostRender
*/
void V_PostRender( void )
{
- qboolean draw_2d = false;
+ static double oldtime;
+ qboolean draw_2d = false;
R_Set2DMode( true );
- if( cls.state == ca_active )
+ if( cls.state == ca_active && cls.scrshot_action != scrshot_mapshot )
{
SCR_TileClear();
CL_DrawHUD( CL_ACTIVE );
@@ -404,16 +299,28 @@ void V_PostRender( void )
{
SCR_RSpeeds();
SCR_NetSpeeds();
- SCR_DrawFPS();
+ SCR_DrawNetGraph();
SV_DrawOrthoTriangles();
CL_DrawDemoRecording();
- R_ShowTextures();
CL_DrawHUD( CL_CHANGELEVEL );
+ R_ShowTextures();
Con_DrawConsole();
UI_UpdateMenu( host.realtime );
Con_DrawVersion();
Con_DrawDebug(); // must be last
- S_ExtraUpdate();
+
+ if( !FBitSet( host.features, ENGINE_FIXED_FRAMERATE ))
+ S_ExtraUpdate();
+ }
+
+ if( FBitSet( host.features, ENGINE_FIXED_FRAMERATE ))
+ {
+ // don't update sound too fast
+ if(( host.realtime - oldtime ) >= HOST_FRAMETIME )
+ {
+ oldtime = host.realtime;
+ CL_ExtraUpdate();
+ }
}
SCR_MakeScreenShot();
diff --git b/engine/client/client.h a/engine/client/client.h
index 45f9b50..bc984ba 100644
--- b/engine/client/client.h
+++ a/engine/client/client.h
@@ -37,7 +37,7 @@ GNU General Public License for more details.
#define MAX_CDTRACKS 32
#define MAX_IMAGES 256 // SpriteTextures
#define MAX_EFRAGS 4096
-#define MAX_REQUESTS 32
+#define MAX_REQUESTS 64
// screenshot types
#define VID_SCREENSHOT 0
@@ -49,6 +49,19 @@ GNU General Public License for more details.
typedef int sound_t;
//=============================================================================
+typedef struct netbandwithgraph_s
+{
+ word client;
+ word players;
+ word entities; // entities bytes, except for players
+ word tentities;// temp entities
+ word sound;
+ word event;
+ word usr;
+ word msgbytes;
+ word voicebytes;
+} netbandwidthgraph_t;
+
typedef struct frame_s
{
// received from server
@@ -58,7 +71,8 @@ typedef struct frame_s
clientdata_t client; // local client private data
entity_state_t playerstate[MAX_CLIENTS];
- weapon_data_t weapondata[64];
+ weapon_data_t weapondata[MAX_LOCAL_WEAPONS];
+ netbandwidthgraph_t graphdata;
int num_entities;
int first_entity; // into the circular cl_packet_entities[]
@@ -79,6 +93,9 @@ typedef struct runcmd_s
int sendsize;
} runcmd_t;
+#define ANGLE_BACKUP 16
+#define ANGLE_MASK (ANGLE_BACKUP - 1)
+
#define CMD_BACKUP MULTIPLAYER_BACKUP // allow a lot of command backups for very fast systems
#define CMD_MASK (CMD_BACKUP - 1)
@@ -87,6 +104,27 @@ extern int CL_UPDATE_BACKUP;
#define INVALID_HANDLE 0xFFFF // for XashXT cache system
+#define cl_serverframetime() (cl.mtime[0] - cl.mtime[1])
+#define cl_clientframetime() (host.frametime)
+
+typedef struct
+{
+ vec3_t origin; // generated by CL_PredictMovement
+ vec3_t viewofs;
+ vec3_t velocity;
+ vec3_t punchangle;
+ vec3_t origins[CMD_BACKUP];
+ vec3_t error;
+ vec3_t lastorigin;
+ double correction_time;
+ int viewmodel;
+ int onground;
+ int waterlevel;
+ int usehull;
+ int moving;
+ int lastground;
+} cl_predicted_data_t; // data we got from prediction system
+
// the client_t structure is wiped completely at every
// server map change
typedef struct
@@ -111,9 +149,10 @@ typedef struct
int last_command_ack;
int last_incoming_sequence;
- qboolean force_send_usercmd;
+ qboolean send_reply;
qboolean thirdperson;
qboolean background; // not real game, just a background
+ qboolean first_frame; // first rendering frame
uint checksum; // for catching cheater maps
@@ -140,12 +179,7 @@ typedef struct
event_state_t events;
// predicting stuff
- vec3_t predicted_origin; // generated by CL_PredictMovement
- vec3_t predicted_viewofs;
- vec3_t predicted_velocity;
- vec3_t predicted_punchangle;
- vec3_t predicted_origins[CMD_BACKUP];
- vec3_t prediction_error;
+ cl_predicted_data_t predicted; // generated from CL_PredictMovement
// server state information
int playernum;
@@ -163,6 +197,11 @@ typedef struct
cl_entity_t *world;
model_t *worldmodel; // pointer to world
+
+ // weapon predict stuff
+ float scr_fov;
+ float weaponstarttime;
+ int weaponsequence;
} client_t;
/*
@@ -316,6 +355,13 @@ typedef struct
model_t *model; // for catch model changes
} remap_info_t;
+typedef enum
+{
+ NET_REQUEST_CANCEL = 0, // request was cancelled for some reasons
+ NET_REQUEST_GAMEUI, // called from GameUI
+ NET_REQUEST_CLIENT, // called from Client
+} net_request_type_t;
+
typedef struct
{
net_response_t resp;
@@ -351,8 +397,9 @@ typedef struct
movevars_t oldmovevars;
playermove_t *pmove; // pmove state
- int old_trace_hull; // used by PM_Push\Pop state
- int oldcount; // used by PM_Push\Pop state
+ qboolean pushed; // used by PM_Push\Pop state
+ int oldviscount; // used by PM_Push\Pop state
+ int oldphyscount; // used by PM_Push\Pop state
vec3_t player_mins[MAX_MAP_HULLS]; // 4 hulls allowed
vec3_t player_maxs[MAX_MAP_HULLS]; // 4 hulls allowed
@@ -376,7 +423,9 @@ typedef struct
client_textmessage_t *titles; // title messages, not network messages
int numTitles;
+ net_request_type_t request_type; // filter the requests
net_request_t net_requests[MAX_REQUESTS]; // no reason to keep more
+ net_request_t *master_request; // queued master request
efrag_t *free_efrags; // linked efrags
cl_entity_t viewent; // viewmodel
@@ -401,7 +450,7 @@ typedef struct
long logo_xres;
long logo_yres;
float logo_length;
-} menu_static_t;
+} gameui_static_t;
typedef struct
{
@@ -423,7 +472,6 @@ typedef struct
byte *mempool; // client premamnent pool: edicts etc
- int framecount;
int quakePort; // a 16 bit value that allows quake servers
// to work around address translating routers
// g-cont. this port allow many copies of engine in multiplayer game
@@ -502,7 +550,7 @@ extern "C" {
extern client_t cl;
extern client_static_t cls;
extern clgame_static_t clgame;
-extern menu_static_t gameui;
+extern gameui_static_t gameui;
#ifdef __cplusplus
}
@@ -512,15 +560,18 @@ extern menu_static_t gameui;
// cvars
//
extern convar_t *cl_predict;
-extern convar_t *cl_smooth;
extern convar_t *cl_showfps;
extern convar_t *cl_envshot_size;
extern convar_t *cl_timeout;
extern convar_t *cl_nodelta;
extern convar_t *cl_interp;
extern convar_t *cl_showerror;
+extern convar_t *cl_nosmooth;
+extern convar_t *cl_smoothtime;
extern convar_t *cl_crosshair;
extern convar_t *cl_testlights;
+extern convar_t *cl_cmdrate;
+extern convar_t *cl_updaterate;
extern convar_t *cl_solid_players;
extern convar_t *cl_idealpitchscale;
extern convar_t *cl_allow_levelshots;
@@ -528,6 +579,9 @@ extern convar_t *cl_lightstyle_lerping;
extern convar_t *cl_draw_particles;
extern convar_t *cl_levelshot_name;
extern convar_t *cl_draw_beams;
+extern convar_t *gl_showtextures;
+extern convar_t *cl_bmodelinterp;
+extern convar_t *cl_lw; // local weapons
extern convar_t *scr_centertime;
extern convar_t *scr_viewsize;
extern convar_t *scr_download;
@@ -586,6 +640,7 @@ void CL_WriteDemoUserCmd( int cmdnumber );
void CL_WriteDemoMessage( qboolean startup, int start, sizebuf_t *msg );
void CL_WriteDemoUserMessage( const byte *buffer, size_t size );
qboolean CL_DemoReadMessage( byte *buffer, size_t *length );
+void CL_DemoInterpolateAngles( void );
void CL_WriteDemoJumpTime( void );
void CL_CloseDemoHeader( void );
void CL_StopPlayback( void );
@@ -632,11 +687,13 @@ void CL_TextMessageParse( byte *pMemFile, int fileSize );
client_textmessage_t *CL_TextMessageGet( const char *pName );
int pfnDecalIndexFromName( const char *szDecalName );
int pfnIndexFromTrace( struct pmtrace_s *pTrace );
+void NetAPI_CancelAllRequests( void );
int CL_FindModelIndex( const char *m );
HSPRITE pfnSPR_Load( const char *szPicName );
HSPRITE pfnSPR_LoadExt( const char *szPicName, uint texFlags );
-void TextAdjustSize( int *x, int *y, int *w, int *h );
void PicAdjustSize( float *x, float *y, float *w, float *h );
+void CL_FillRGBA( int x, int y, int width, int height, int r, int g, int b, int a );
+void CL_FillRGBABlend( int x, int y, int width, int height, int r, int g, int b, int a );
void CL_PlayerTrace( float *start, float *end, int traceFlags, int ignore_pe, pmtrace_t *tr );
void CL_PlayerTraceExt( float *start, float *end, int traceFlags, int (*pfnIgnore)( physent_t *pe ), pmtrace_t *tr );
void CL_SetTraceHull( int hull );
@@ -672,7 +729,13 @@ void SCR_MakeScreenShot( void );
void SCR_MakeLevelShot( void );
void SCR_NetSpeeds( void );
void SCR_RSpeeds( void );
-void SCR_DrawFPS( void );
+void SCR_DrawFPS( int height );
+
+//
+// cl_netgraph.c
+//
+void CL_InitNetgraph( void );
+void SCR_DrawNetGraph( void );
//
// cl_view.c
@@ -683,11 +746,7 @@ void V_Shutdown( void );
qboolean V_PreRender( void );
void V_PostRender( void );
void V_RenderView( void );
-void V_SetupOverviewState( void );
-void V_ProcessOverviewCmds( usercmd_t *cmd );
-void V_MergeOverviewRefdef( ref_params_t *fd );
-void V_ProcessShowTexturesCmds( usercmd_t *cmd );
-void V_WriteOverviewScript( void );
+void V_SetupRefDef( void );
//
// cl_pmove.c
@@ -705,6 +764,9 @@ cl_entity_t *CL_GetWaterEntity( const float *rgflPos );
void CL_SetupPMove( playermove_t *pmove, local_state_t *from, usercmd_t *ucmd, qboolean runfuncs, double time );
pmtrace_t CL_TraceLine( vec3_t start, vec3_t end, int flags );
void CL_ClearPhysEnts( void );
+void CL_PushPMStates( void );
+void CL_PopPMStates( void );
+void CL_SetUpPlayerPrediction( int dopred, int bIncludeLocalClient );
//
// cl_studio.c
@@ -714,12 +776,13 @@ void CL_InitStudioAPI( void );
//
// cl_frame.c
//
-void CL_ParsePacketEntities( sizebuf_t *msg, qboolean delta );
+int CL_ParsePacketEntities( sizebuf_t *msg, qboolean delta );
qboolean CL_AddVisibleEntity( cl_entity_t *ent, int entityType );
void CL_UpdateStudioVars( cl_entity_t *ent, entity_state_t *newstate, qboolean noInterp );
qboolean CL_GetEntitySpatialization( int entnum, vec3_t origin, float *pradius );
void CL_UpdateEntityFields( cl_entity_t *ent );
qboolean CL_IsPlayerIndex( int idx );
+void CL_SetIdealPitch( void );
//
// cl_remap.c
@@ -761,6 +824,7 @@ void CL_ParseViewBeam( sizebuf_t *msg, int beamType );
void CL_RegisterMuzzleFlashes( void );
void CL_ReadPointFile_f( void );
void CL_ReadLineFile_f( void );
+void CL_RunLightStyles( void );
//
// console.c
@@ -769,6 +833,7 @@ extern convar_t *con_fontsize;
qboolean Con_Visible( void );
void Con_Init( void );
void Con_VidInit( void );
+void Con_Shutdown( void );
void Con_ToggleConsole_f( void );
void Con_ClearNotify( void );
void Con_DrawDebug( void );
@@ -786,7 +851,7 @@ void Con_CharEvent( int key );
void Con_RestoreFont( void );
void Key_Console( int key );
void Key_Message( int key );
-void Con_Close( void );
+void Con_FastClose( void );
//
// s_main.c
@@ -810,7 +875,7 @@ void S_RenderFrame( struct ref_params_s *fd );
void S_ExtraUpdate( void );
//
-// cl_menu.c
+// cl_gameui.c
//
void UI_UnloadProgs( void );
qboolean UI_LoadProgs( void );
diff --git b/engine/client/gl_backend.c a/engine/client/gl_backend.c
index f0188a5..6f477e3 100644
--- b/engine/client/gl_backend.c
+++ a/engine/client/gl_backend.c
@@ -76,7 +76,7 @@ void GL_BackendEndFrame( void )
break;
case 2:
Q_snprintf( r_speeds_msg, sizeof( r_speeds_msg ), "visible leafs:\n%3i leafs\ncurrent leaf %3i",
- r_stats.c_world_leafs, r_viewleaf - cl.worldmodel->leafs );
+ r_stats.c_world_leafs, Mod_PointInLeaf( RI.pvsorigin, cl.worldmodel->nodes ) - cl.worldmodel->leafs );
break;
case 3:
Q_snprintf( r_speeds_msg, sizeof( r_speeds_msg ), "%3i studio models drawn\n%3i sprites drawn",
@@ -182,10 +182,6 @@ void GL_SelectTexture( GLint tmu )
if( tmu < glConfig.max_texture_coords )
pglClientActiveTextureARB( tmu + GL_TEXTURE0_ARB );
}
- else if( pglSelectTextureSGIS )
- {
- pglSelectTextureSGIS( tmu + GL_TEXTURE0_SGIS );
- }
}
/*
@@ -228,20 +224,31 @@ void GL_CleanUpTextureUnits( int last )
}
/*
+==============
+GL_CleanupAllTextureUnits
+==============
+*/
+void GL_CleanupAllTextureUnits( void )
+{
+ // force to cleanup all the units
+ GL_SelectTexture( GL_MaxTextureUnits() - 1 );
+ GL_CleanUpTextureUnits( 0 );
+}
+
+/*
=================
GL_MultiTexCoord2f
=================
*/
void GL_MultiTexCoord2f( GLenum texture, GLfloat s, GLfloat t )
{
+ if( !GL_Support( GL_ARB_MULTITEXTURE ))
+ return;
+
if( pglMultiTexCoord2f )
{
pglMultiTexCoord2f( texture + GL_TEXTURE0_ARB, s, t );
}
- else if( pglMTexCoord2fSGIS )
- {
- pglMTexCoord2fSGIS( texture + GL_TEXTURE0_SGIS, s, t );
- }
}
/*
@@ -470,6 +477,37 @@ void VID_ImageAdjustGamma( byte *in, uint width, uint height )
}
}
+/*
+===============
+VID_WriteOverviewScript
+
+Create overview script file
+===============
+*/
+void VID_WriteOverviewScript( void )
+{
+ ref_overview_t *ov = &clgame.overView;
+ string filename;
+ file_t *f;
+
+ Q_snprintf( filename, sizeof( filename ), "overviews/%s.txt", clgame.mapname );
+
+ f = FS_Open( filename, "w", false );
+ if( !f ) return;
+
+ FS_Printf( f, "// overview description file for %s.bsp\n\n", clgame.mapname );
+ FS_Print( f, "global\n{\n" );
+ FS_Printf( f, "\tZOOM\t%.2f\n", ov->flZoom );
+ FS_Printf( f, "\tORIGIN\t%.2f\t%.2f\t%.2f\n", ov->origin[0], ov->origin[1], ov->origin[2] );
+ FS_Printf( f, "\tROTATED\t%i\n", ov->rotated ? 1 : 0 );
+ FS_Print( f, "}\n\nlayer\n{\n" );
+ FS_Printf( f, "\tIMAGE\t\"overviews/%s.bmp\"\n", clgame.mapname );
+ FS_Printf( f, "\tHEIGHT\t%.2f\n", ov->zFar ); // ???
+ FS_Print( f, "}\n" );
+
+ FS_Close( f );
+}
+
qboolean VID_ScreenShot( const char *filename, int shot_type )
{
rgbdata_t *r_shot;
@@ -519,7 +557,7 @@ qboolean VID_ScreenShot( const char *filename, int shot_type )
width = 320;
break;
case VID_MAPSHOT:
- V_WriteOverviewScript(); // store overview script too
+ VID_WriteOverviewScript(); // store overview script too
flags |= IMAGE_RESAMPLE|IMAGE_QUANTIZE; // GoldSrc request overviews in 8-bit format
height = 768;
width = 1024;
@@ -637,8 +675,8 @@ void R_ShowTextures( void )
{
gltexture_t *image;
float x, y, w, h;
- int i, j, k, base_w, base_h;
int total, start, end;
+ int i, j, k, base_w, base_h;
rgba_t color = { 192, 192, 192, 255 };
int charHeight, numTries = 0;
static qboolean showHelp = true;
@@ -649,15 +687,16 @@ void R_ShowTextures( void )
if( showHelp )
{
- CL_CenterPrint( "use '<-' and '->' keys for view all the textures", 0.25f );
+ CL_CenterPrint( "use '<-' and '->' keys to change atlas page, ESC to quit", 0.25f );
showHelp = false;
}
+ GL_SetRenderMode( kRenderNormal );
pglClear( GL_COLOR_BUFFER_BIT );
pglFinish();
- base_w = 8;
- base_h = 6;
+ base_w = 8; // textures view by horizontal
+ base_h = 6; // textures view by vertical
rebuild_page:
total = base_w * base_h;
@@ -698,27 +737,27 @@ rebuild_page:
pglColor4f( 1.0f, 1.0f, 1.0f, 1.0f );
GL_Bind( GL_TEXTURE0, i ); // NOTE: don't use image->texnum here, because skybox has a 'wrong' indexes
- if(( image->flags & TF_DEPTHMAP ) && !( image->flags & TF_NOCOMPARE ))
+ if( FBitSet( image->flags, TF_DEPTHMAP ) && !FBitSet( image->flags, TF_NOCOMPARE ))
pglTexParameteri( image->target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE );
pglBegin( GL_QUADS );
pglTexCoord2f( 0, 0 );
pglVertex2f( x, y );
- if( image->flags & TF_TEXTURE_RECTANGLE )
+ if( image->target == GL_TEXTURE_RECTANGLE_EXT )
pglTexCoord2f( image->width, 0 );
else pglTexCoord2f( 1, 0 );
pglVertex2f( x + w, y );
- if( image->flags & TF_TEXTURE_RECTANGLE )
+ if( image->target == GL_TEXTURE_RECTANGLE_EXT )
pglTexCoord2f( image->width, image->height );
else pglTexCoord2f( 1, 1 );
pglVertex2f( x + w, y + h );
- if( image->flags & TF_TEXTURE_RECTANGLE )
+ if( image->target == GL_TEXTURE_RECTANGLE_EXT )
pglTexCoord2f( 0, image->height );
else pglTexCoord2f( 0, 1 );
pglVertex2f( x, y + h );
pglEnd();
- if(( image->flags & TF_DEPTHMAP ) && !( image->flags & TF_NOCOMPARE ))
+ if( FBitSet( image->flags, TF_DEPTHMAP ) && !FBitSet( image->flags, TF_NOCOMPARE ))
pglTexParameteri( image->target, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB );
FS_FileBase( image->name, shortname );
@@ -735,6 +774,4 @@ rebuild_page:
CL_DrawCenterPrint ();
pglFinish();
-}
-
-//=======================================================
\ No newline at end of file
+}
\ No newline at end of file
diff --git b/engine/client/gl_beams.c a/engine/client/gl_beams.c
index 0697335..d601abf 100644
--- b/engine/client/gl_beams.c
+++ a/engine/client/gl_beams.c
@@ -33,7 +33,6 @@ typedef struct
vec3_t color;
float texcoord; // Y texture coordinate
float width;
- float alpha;
} beamseg_t;
static float rgNoise[NOISE_DIVISIONS+1]; // global noise array
@@ -88,24 +87,12 @@ static cl_entity_t *CL_GetBeamEntityByIndex( int index )
return ent;
}
-void BeamNormalizeColor( BEAM *pBeam, float r, float g, float b, float brightness )
+void BeamNormalizeColor( BEAM *pBeam, int r, int g, int b, float brightness )
{
- float max, scale;
-
- max = max( max( r, g ), b );
-
- if( max == 0 )
- {
- pBeam->r = pBeam->g = pBeam->b = 255.0f;
- pBeam->brightness = brightness;
- }
-
- scale = 255.0f / max;
-
- pBeam->r = r * scale;
- pBeam->g = g * scale;
- pBeam->b = b * scale;
- pBeam->brightness = ( brightness > 1.0f ) ? brightness : brightness * 255.0f;
+ pBeam->r = (float)r;
+ pBeam->g = (float)g;
+ pBeam->b = (float)b;
+ pBeam->brightness = brightness;
}
static qboolean ComputeBeamEntPosition( int beamEnt, vec3_t pt )
@@ -269,7 +256,6 @@ static void CL_DrawSegs( int modelIndex, float frame, int rendermode, const vec3
vec3_t vPoint1, vPoint2;
ASSERT( noiseIndex < ( NOISE_DIVISIONS << 16 ));
- nextSeg.alpha = 1.0f;
fraction = i * div;
@@ -350,12 +336,12 @@ static void CL_DrawSegs( int modelIndex, float frame, int rendermode, const vec3
VectorMA( curSeg.pos, ( curSeg.width * 0.5f ), vAveNormal, vPoint1 );
VectorMA( curSeg.pos, (-curSeg.width * 0.5f ), vAveNormal, vPoint2 );
- pglColor4f( curSeg.color[0], curSeg.color[1], curSeg.color[2], curSeg.alpha );
+ pglColor4f( curSeg.color[0], curSeg.color[1], curSeg.color[2], 1.0f );
pglTexCoord2f( 0.0f, curSeg.texcoord );
pglNormal3fv( vAveNormal );
pglVertex3fv( vPoint1 );
- pglColor4f( curSeg.color[0], curSeg.color[1], curSeg.color[2], curSeg.alpha );
+ pglColor4f( curSeg.color[0], curSeg.color[1], curSeg.color[2], 1.0f );
pglTexCoord2f( 1.0f, curSeg.texcoord );
pglNormal3fv( vAveNormal );
pglVertex3fv( vPoint2 );
@@ -371,12 +357,12 @@ static void CL_DrawSegs( int modelIndex, float frame, int rendermode, const vec3
VectorMA( curSeg.pos, (-curSeg.width * 0.5f ), vLastNormal, vPoint2 );
// specify the points.
- pglColor4f( curSeg.color[0], curSeg.color[1], curSeg.color[2], curSeg.alpha );
+ pglColor4f( curSeg.color[0], curSeg.color[1], curSeg.color[2], 1.0f );
pglTexCoord2f( 0.0f, curSeg.texcoord );
pglNormal3fv( vLastNormal );
pglVertex3fv( vPoint1 );
- pglColor4f( curSeg.color[0], curSeg.color[1], curSeg.color[2], curSeg.alpha );
+ pglColor4f( curSeg.color[0], curSeg.color[1], curSeg.color[2], 1.0f );
pglTexCoord2f( 1.0f, curSeg.texcoord );
pglNormal3fv( vLastNormal );
pglVertex3fv( vPoint2 );
@@ -1442,12 +1428,10 @@ void CL_DrawBeam( BEAM *pbeam )
pStart = CL_GetBeamEntityByIndex( pbeam->startEntity );
if( pStart && pStart->curstate.rendermode != kRenderNormal )
pbeam->brightness = pStart->curstate.renderamt;
- }
-
- VectorScale( color, ( pbeam->brightness / 255.0f ), color );
- VectorScale( color, ( 1.0f / 255.0f ), color );
- pglShadeModel( GL_SMOOTH );
+ VectorScale( color, ( pbeam->brightness / 255.0f ), color );
+ VectorScale( color, ( 1.0f / 255.0f ), color );
+ }
switch( pbeam->type )
{
@@ -1478,7 +1462,6 @@ void CL_DrawBeam( BEAM *pbeam )
MsgDev( D_ERROR, "CL_DrawBeam: Unknown beam type %i\n", pbeam->type );
break;
}
- pglShadeModel( GL_FLAT );
}
/*
@@ -1517,9 +1500,9 @@ void CL_DrawCustomBeam( cl_entity_t *pbeam )
beam.width = pbeam->curstate.scale;
beam.amplitude = (float)(pbeam->curstate.body * 0.1f);
beam.brightness = pbeam->curstate.renderamt;
- beam.r = pbeam->curstate.rendercolor.r;
- beam.g = pbeam->curstate.rendercolor.g;
- beam.b = pbeam->curstate.rendercolor.b;
+ beam.r = pbeam->curstate.rendercolor.r / 255.0f;
+ beam.g = pbeam->curstate.rendercolor.g / 255.0f;
+ beam.b = pbeam->curstate.rendercolor.b / 255.0f;
beam.flags = 0;
VectorSubtract( beam.target, beam.source, beam.delta );
@@ -1985,9 +1968,9 @@ void CL_ParseViewBeam( sizebuf_t *msg, int beamType )
life = (float)(MSG_ReadByte( msg ) * 0.1f);
width = (float)(MSG_ReadByte( msg ) * 0.1f);
noise = (float)(MSG_ReadByte( msg ) * 0.1f);
- r = (float)MSG_ReadByte( msg );
- g = (float)MSG_ReadByte( msg );
- b = (float)MSG_ReadByte( msg );
+ r = (float)MSG_ReadByte( msg ) / 255.0f;
+ g = (float)MSG_ReadByte( msg ) / 255.0f;
+ b = (float)MSG_ReadByte( msg ) / 255.0f;
brightness = (float)MSG_ReadByte( msg );
speed = (float)(MSG_ReadByte( msg ) * 0.1f);
CL_BeamPoints( start, end, modelIndex, life, width, noise, brightness, speed, startFrame,
@@ -2004,9 +1987,9 @@ void CL_ParseViewBeam( sizebuf_t *msg, int beamType )
life = (float)(MSG_ReadByte( msg ) * 0.1f);
width = (float)(MSG_ReadByte( msg ) * 0.1f);
noise = (float)(MSG_ReadByte( msg ) * 0.01f);
- r = (float)MSG_ReadByte( msg );
- g = (float)MSG_ReadByte( msg );
- b = (float)MSG_ReadByte( msg );
+ r = (float)MSG_ReadByte( msg ) / 255.0f;
+ g = (float)MSG_ReadByte( msg ) / 255.0f;
+ b = (float)MSG_ReadByte( msg ) / 255.0f;
brightness = (float)MSG_ReadByte( msg );
speed = (float)(MSG_ReadByte( msg ) * 0.1f);
CL_BeamEntPoint( startEnt, end, modelIndex, life, width, noise, brightness, speed, startFrame,
@@ -2034,9 +2017,9 @@ void CL_ParseViewBeam( sizebuf_t *msg, int beamType )
life = (float)(MSG_ReadByte( msg ) * 0.1f);
width = (float)(MSG_ReadByte( msg ) * 0.1f);
noise = (float)(MSG_ReadByte( msg ) * 0.01f);
- r = (float)MSG_ReadByte( msg );
- g = (float)MSG_ReadByte( msg );
- b = (float)MSG_ReadByte( msg );
+ r = (float)MSG_ReadByte( msg ) / 255.0f;
+ g = (float)MSG_ReadByte( msg ) / 255.0f;
+ b = (float)MSG_ReadByte( msg ) / 255.0f;
brightness = (float)MSG_ReadByte( msg );
speed = (float)(MSG_ReadByte( msg ) * 0.1f);
CL_BeamEnts( startEnt, endEnt, modelIndex, life, width, noise, brightness, speed, startFrame,
@@ -2071,9 +2054,9 @@ void CL_ParseViewBeam( sizebuf_t *msg, int beamType )
life = (float)(MSG_ReadByte( msg ) * 0.1f);
width = (float)MSG_ReadByte( msg );
noise = (float)(MSG_ReadByte( msg ) * 0.1f);
- r = (float)MSG_ReadByte( msg );
- g = (float)MSG_ReadByte( msg );
- b = (float)MSG_ReadByte( msg );
+ r = (float)MSG_ReadByte( msg ) / 255.0f;
+ g = (float)MSG_ReadByte( msg ) / 255.0f;
+ b = (float)MSG_ReadByte( msg ) / 255.0f;
brightness = (float)MSG_ReadByte( msg );
speed = (float)(MSG_ReadByte( msg ) * 0.1f);
CL_BeamCirclePoints( beamType, start, end, modelIndex, life, width, noise, brightness, speed,
@@ -2099,9 +2082,9 @@ void CL_ParseViewBeam( sizebuf_t *msg, int beamType )
life = (float)(MSG_ReadByte( msg ) * 0.1f);
width = (float)(MSG_ReadByte( msg ) * 0.1f);
noise = (float)(MSG_ReadByte( msg ) * 0.1f);
- r = (float)MSG_ReadByte( msg );
- g = (float)MSG_ReadByte( msg );
- b = (float)MSG_ReadByte( msg );
+ r = (float)MSG_ReadByte( msg ) / 255.0f;
+ g = (float)MSG_ReadByte( msg ) / 255.0f;
+ b = (float)MSG_ReadByte( msg ) / 255.0f;
brightness = (float)MSG_ReadByte( msg );
speed = (float)(MSG_ReadByte( msg ) * 0.1f);
CL_BeamRing( startEnt, endEnt, modelIndex, life, width, noise, brightness, speed, startFrame,
@@ -2166,7 +2149,7 @@ void CL_ReadLineFile_f( void )
if( token[0] != '-' )
{
- MsgDev( D_ERROR, "%s is corrupted\n" );
+ MsgDev( D_ERROR, "%s is corrupted\n", filename );
break;
}
diff --git b/engine/client/gl_cull.c a/engine/client/gl_cull.c
index 806c6e7..72eb1cb 100644
--- b/engine/client/gl_cull.c
+++ a/engine/client/gl_cull.c
@@ -180,7 +180,7 @@ qboolean R_CullSurface( msurface_t *surf, uint clipflags )
if( r_faceplanecull->integer && glState.faceCull != 0 )
{
- if( RI.currentWaveHeight == 0.0f )
+ if( e->curstate.scale == 0.0f )
{
if( !VectorIsNull( surf->plane->normal ))
{
diff --git b/engine/client/gl_decals.c a/engine/client/gl_decals.c
index 57fe7c6..20371da 100644
--- b/engine/client/gl_decals.c
+++ a/engine/client/gl_decals.c
@@ -404,22 +404,23 @@ static void R_DecalVertsLight( float *v, msurface_t *surf, int vertCount )
{
float s, t;
mtexinfo_t *tex;
- int j;
+ int j, sample_size;
+ sample_size = Mod_SampleSizeForFace( surf );
tex = surf->texinfo;
for( j = 0; j < vertCount; j++, v += VERTEXSIZE )
{
// lightmap texture coordinates
s = DotProduct( v, tex->vecs[0] ) + tex->vecs[0][3] - surf->texturemins[0];
- s += surf->light_s * LM_SAMPLE_SIZE;
- s += LM_SAMPLE_SIZE >> 1;
- s /= BLOCK_SIZE * LM_SAMPLE_SIZE; //fa->texinfo->texture->width;
+ s += surf->light_s * sample_size;
+ s += sample_size >> 1;
+ s /= BLOCK_SIZE * sample_size; //fa->texinfo->texture->width;
t = DotProduct( v, tex->vecs[1] ) + tex->vecs[1][3] - surf->texturemins[1];
- t += surf->light_t * LM_SAMPLE_SIZE;
- t += LM_SAMPLE_SIZE >> 1;
- t /= BLOCK_SIZE * LM_SAMPLE_SIZE; //fa->texinfo->texture->height;
+ t += surf->light_t * sample_size;
+ t += sample_size >> 1;
+ t /= BLOCK_SIZE * sample_size; //fa->texinfo->texture->height;
v[5] = s;
v[6] = t;
@@ -1232,7 +1233,11 @@ void R_DecalRemoveAll( int textureIndex )
{
pdecal = &gDecalPool[i];
- if( !textureIndex || pdecal->texture == textureIndex )
+ // don't remove permanent decals
+ if( pdecal->flags & FDECAL_PERMANENT )
+ continue;
+
+ if( !textureIndex || ( pdecal->texture == textureIndex ))
R_DecalUnlink( pdecal );
}
}
diff --git b/engine/client/gl_draw.c a/engine/client/gl_draw.c
index 9c8cf7c..d8620f4 100644
--- b/engine/client/gl_draw.c
+++ a/engine/client/gl_draw.c
@@ -231,7 +231,7 @@ void R_UploadStretchRaw( int texture, int cols, int rows, int width, int height,
tex->height = rows;
pglTexImage2D( GL_TEXTURE_2D, 0, tex->format, cols, rows, 0, GL_BGRA, GL_UNSIGNED_BYTE, raw );
- GL_TexFilter( tex, false );
+ GL_ApplyTextureParams( tex );
}
/*
diff --git b/engine/client/gl_export.h a/engine/client/gl_export.h
index 9aefb51..1422401 100644
--- b/engine/client/gl_export.h
+++ a/engine/client/gl_export.h
@@ -235,6 +235,29 @@ typedef float GLmatrix[16];
#define GL_PROXY_TEXTURE_2D 0x8064
#define GL_MAX_TEXTURE_SIZE 0x0D33
+#define GL_RG 0x8227
+#define GL_RG_INTEGER 0x8228
+#define GL_R8 0x8229
+#define GL_R16 0x822A
+#define GL_RG8 0x822B
+#define GL_RG16 0x822C
+#define GL_R16F 0x822D
+#define GL_R32F 0x822E
+#define GL_RG16F 0x822F
+#define GL_RG32F 0x8230
+#define GL_R8I 0x8231
+#define GL_R8UI 0x8232
+#define GL_R16I 0x8233
+#define GL_R16UI 0x8234
+#define GL_R32I 0x8235
+#define GL_R32UI 0x8236
+#define GL_RG8I 0x8237
+#define GL_RG8UI 0x8238
+#define GL_RG16I 0x8239
+#define GL_RG16UI 0x823A
+#define GL_RG32I 0x823B
+#define GL_RG32UI 0x823C
+
// texture coord name
#define GL_S 0x2000
#define GL_T 0x2001
@@ -433,7 +456,7 @@ typedef float GLmatrix[16];
#define GL_TEXTURE_WRAP_R 0x8072
#define GL_MAX_3D_TEXTURE_SIZE 0x8073
#define GL_TEXTURE_BINDING_3D 0x806A
-
+#define GL_TEXTURE_CUBE_MAP_SEAMLESS 0x884F
#define GL_STENCIL_TEST_TWO_SIDE_EXT 0x8910
#define GL_ACTIVE_STENCIL_FACE_EXT 0x8911
#define GL_STENCIL_BACK_FUNC 0x8800
@@ -441,6 +464,24 @@ typedef float GLmatrix[16];
#define GL_STENCIL_BACK_PASS_DEPTH_FAIL 0x8802
#define GL_STENCIL_BACK_PASS_DEPTH_PASS 0x8803
+#define GL_MAX_DRAW_BUFFERS_ARB 0x8824
+#define GL_DRAW_BUFFER0_ARB 0x8825
+#define GL_DRAW_BUFFER1_ARB 0x8826
+#define GL_DRAW_BUFFER2_ARB 0x8827
+#define GL_DRAW_BUFFER3_ARB 0x8828
+#define GL_DRAW_BUFFER4_ARB 0x8829
+#define GL_DRAW_BUFFER5_ARB 0x882A
+#define GL_DRAW_BUFFER6_ARB 0x882B
+#define GL_DRAW_BUFFER7_ARB 0x882C
+#define GL_DRAW_BUFFER8_ARB 0x882D
+#define GL_DRAW_BUFFER9_ARB 0x882E
+#define GL_DRAW_BUFFER10_ARB 0x882F
+#define GL_DRAW_BUFFER11_ARB 0x8830
+#define GL_DRAW_BUFFER12_ARB 0x8831
+#define GL_DRAW_BUFFER13_ARB 0x8832
+#define GL_DRAW_BUFFER14_ARB 0x8833
+#define GL_DRAW_BUFFER15_ARB 0x8834
+
#define GL_DEPTH_TEXTURE_MODE_ARB 0x884B
#define GL_TEXTURE_COMPARE_MODE_ARB 0x884C
#define GL_TEXTURE_COMPARE_FUNC_ARB 0x884D
@@ -614,6 +655,15 @@ typedef float GLmatrix[16];
#define GL_DOT3_RGB_ARB 0x86AE
#define GL_DOT3_RGBA_ARB 0x86AF
+#define GL_TEXTURE_1D_ARRAY_EXT 0x8C18
+#define GL_PROXY_TEXTURE_1D_ARRAY_EXT 0x8C19
+#define GL_TEXTURE_2D_ARRAY_EXT 0x8C1A
+#define GL_PROXY_TEXTURE_2D_ARRAY_EXT 0x8C1B
+#define GL_TEXTURE_BINDING_1D_ARRAY_EXT 0x8C1C
+#define GL_TEXTURE_BINDING_2D_ARRAY_EXT 0x8C1D
+#define GL_MAX_ARRAY_TEXTURE_LAYERS_EXT 0x88FF
+#define GL_COMPARE_REF_DEPTH_TO_TEXTURE_EXT 0x884E
+
#define GL_MULTISAMPLE_ARB 0x809D
#define GL_SAMPLE_ALPHA_TO_COVERAGE_ARB 0x809E
#define GL_SAMPLE_ALPHA_TO_ONE_ARB 0x809F
@@ -719,6 +769,29 @@ typedef float GLmatrix[16];
#define GL_MAX_TEXTURE_COORDS_ARB 0x8871
#define GL_MAX_TEXTURE_IMAGE_UNITS_ARB 0x8872
+#define GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB 0x8242
+#define GL_MAX_DEBUG_MESSAGE_LENGTH_ARB 0x9143
+#define GL_MAX_DEBUG_LOGGED_MESSAGES_ARB 0x9144
+#define GL_DEBUG_LOGGED_MESSAGES_ARB 0x9145
+#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_ARB 0x8243
+#define GL_DEBUG_CALLBACK_FUNCTION_ARB 0x8244
+#define GL_DEBUG_CALLBACK_USER_PARAM_ARB 0x8245
+#define GL_DEBUG_SOURCE_API_ARB 0x8246
+#define GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB 0x8247
+#define GL_DEBUG_SOURCE_SHADER_COMPILER_ARB 0x8248
+#define GL_DEBUG_SOURCE_THIRD_PARTY_ARB 0x8249
+#define GL_DEBUG_SOURCE_APPLICATION_ARB 0x824A
+#define GL_DEBUG_SOURCE_OTHER_ARB 0x824B
+#define GL_DEBUG_TYPE_ERROR_ARB 0x824C
+#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB 0x824D
+#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB 0x824E
+#define GL_DEBUG_TYPE_PORTABILITY_ARB 0x824F
+#define GL_DEBUG_TYPE_PERFORMANCE_ARB 0x8250
+#define GL_DEBUG_TYPE_OTHER_ARB 0x8251
+#define GL_DEBUG_SEVERITY_HIGH_ARB 0x9146
+#define GL_DEBUG_SEVERITY_MEDIUM_ARB 0x9147
+#define GL_DEBUG_SEVERITY_LOW_ARB 0x9148
+
#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
#define WGL_CONTEXT_LAYER_PLANE_ARB 0x2093
@@ -1153,6 +1226,10 @@ void ( APIENTRY *pglDisableVertexAttribArrayARB)(GLuint index);
void ( APIENTRY *pglBindAttribLocationARB)(GLhandleARB programObj, GLuint index, const GLcharARB *name);
void ( APIENTRY *pglGetActiveAttribARB)(GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei *length, GLint *size, GLenum *type, GLcharARB *name);
GLint ( APIENTRY *pglGetAttribLocationARB)(GLhandleARB programObj, const GLcharARB *name);
+void ( APIENTRY *pglBindFragDataLocation)(GLuint programObj, GLuint index, const GLcharARB *name);
+void ( APIENTRY *pglVertexAttrib2fARB)( GLuint index, GLfloat x, GLfloat y );
+void ( APIENTRY *pglVertexAttrib2fvARB)( GLuint index, const GLfloat *v );
+void ( APIENTRY *pglVertexAttrib3fvARB)( GLuint index, const GLfloat *v );
void ( APIENTRY *pglBindBufferARB) (GLenum target, GLuint buffer);
void ( APIENTRY *pglDeleteBuffersARB) (GLsizei n, const GLuint *buffers);
void ( APIENTRY *pglGenBuffersARB) (GLsizei n, GLuint *buffers);
@@ -1169,8 +1246,36 @@ void ( APIENTRY *pglEndQueryARB) (GLenum target);
void ( APIENTRY *pglGetQueryivARB) (GLenum target, GLenum pname, GLint *params);
void ( APIENTRY *pglGetQueryObjectivARB) (GLuint id, GLenum pname, GLint *params);
void ( APIENTRY *pglGetQueryObjectuivARB) (GLuint id, GLenum pname, GLuint *params);
-void ( APIENTRY * pglSelectTextureSGIS) ( GLenum );
-void ( APIENTRY * pglMTexCoord2fSGIS) ( GLenum, GLfloat, GLfloat );
+typedef void ( APIENTRY *pglDebugProcARB)( GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLcharARB* message, GLvoid* userParam );
+void ( APIENTRY *pglDebugMessageControlARB)( GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint* ids, GLboolean enabled );
+void ( APIENTRY *pglDebugMessageInsertARB)( GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const char* buf );
+void ( APIENTRY *pglDebugMessageCallbackARB)( pglDebugProcARB callback, void* userParam );
+GLuint ( APIENTRY *pglGetDebugMessageLogARB)( GLuint count, GLsizei bufsize, GLenum* sources, GLenum* types, GLuint* ids, GLuint* severities, GLsizei* lengths, char* messageLog );
+GLboolean ( APIENTRY *pglIsRenderbuffer )(GLuint renderbuffer);
+void ( APIENTRY *pglBindRenderbuffer )(GLenum target, GLuint renderbuffer);
+void ( APIENTRY *pglDeleteRenderbuffers )(GLsizei n, const GLuint *renderbuffers);
+void ( APIENTRY *pglGenRenderbuffers )(GLsizei n, GLuint *renderbuffers);
+void ( APIENTRY *pglRenderbufferStorage )(GLenum target, GLenum internalformat, GLsizei width, GLsizei height);
+void ( APIENTRY *pglRenderbufferStorageMultisample )(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height);
+void ( APIENTRY *pglGetRenderbufferParameteriv )(GLenum target, GLenum pname, GLint *params);
+GLboolean (APIENTRY *pglIsFramebuffer )(GLuint framebuffer);
+void ( APIENTRY *pglBindFramebuffer )(GLenum target, GLuint framebuffer);
+void ( APIENTRY *pglDeleteFramebuffers )(GLsizei n, const GLuint *framebuffers);
+void ( APIENTRY *pglGenFramebuffers )(GLsizei n, GLuint *framebuffers);
+GLenum ( APIENTRY *pglCheckFramebufferStatus )(GLenum target);
+void ( APIENTRY *pglFramebufferTexture1D )(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);
+void ( APIENTRY *pglFramebufferTexture2D )(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);
+void ( APIENTRY *pglFramebufferTexture3D )(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint layer);
+void ( APIENTRY *pglFramebufferTextureLayer )(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer);
+void ( APIENTRY *pglFramebufferRenderbuffer )(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer);
+void ( APIENTRY *pglGetFramebufferAttachmentParameteriv )(GLenum target, GLenum attachment, GLenum pname, GLint *params);
+void ( APIENTRY *pglBlitFramebuffer )(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);
+void ( APIENTRY *pglDrawBuffersARB)( GLsizei n, const GLenum *bufs );
+void ( APIENTRY *pglGenerateMipmap )( GLenum target );
+void ( APIENTRY *pglBindVertexArray )( GLuint array );
+void ( APIENTRY *pglDeleteVertexArrays )( GLsizei n, const GLuint *arrays );
+void ( APIENTRY *pglGenVertexArrays )( GLsizei n, const GLuint *arrays );
+GLboolean ( APIENTRY *pglIsVertexArray )( GLuint array );
void ( APIENTRY * pglSwapInterval) ( int interval );
extern void *pglGetProcAddress( const GLubyte * );
BOOL ( WINAPI * pwglSwapBuffers)(HDC);
@@ -1191,5 +1296,6 @@ BOOL ( WINAPI * pwglRealizeLayerPalette)(HDC, int, BOOL);
BOOL ( WINAPI * pwglSwapLayerBuffers)(HDC, UINT);
BOOL ( WINAPI * pwglSwapIntervalEXT)( int interval );
HGLRC ( WINAPI * pwglCreateContextAttribsARB)( HDC hDC, HGLRC hShareContext, const int *attribList );
+const char *( WINAPI * pwglGetExtensionsStringEXT)( void );
#endif//GL_EXPORT_H
\ No newline at end of file
diff --git b/engine/client/gl_image.c a/engine/client/gl_image.c
index e93e5e7..46e9cf8 100644
--- b/engine/client/gl_image.c
+++ a/engine/client/gl_image.c
@@ -18,16 +18,13 @@ GNU General Public License for more details.
#include "gl_local.h"
#include "studio.h"
-#define TEXTURES_HASH_SIZE 64
+#define TEXTURES_HASH_SIZE (MAX_TEXTURES >> 2)
-static int r_textureMinFilter = GL_LINEAR_MIPMAP_LINEAR;
-static int r_textureMagFilter = GL_LINEAR;
-static gltexture_t r_textures[MAX_TEXTURES];
-static gltexture_t *r_texturesHashTable[TEXTURES_HASH_SIZE];
-static int r_numTextures;
-static byte *scaledImage = NULL; // pointer to a scaled image
-static byte data2D[BLOCK_SIZE_MAX*BLOCK_SIZE_MAX*4]; // intermediate texbuffer
-static rgbdata_t r_image; // generic pixelbuffer used for internal textures
+static gltexture_t r_textures[MAX_TEXTURES];
+static gltexture_t *r_texturesHashTable[TEXTURES_HASH_SIZE];
+static int r_numTextures;
+static byte data2D[BLOCK_SIZE_MAX*BLOCK_SIZE_MAX*4]; // intermediate texbuffer
+static rgbdata_t r_image; // generic pixelbuffer used for internal textures
// internal tables
static vec3_t r_luminanceTable[256]; // RGB to luminance
@@ -43,7 +40,25 @@ static byte r_particleTexture[8][8] =
{0,0,0,0,0,0,0,0},
};
-const char *GL_Target( GLenum target )
+/*
+=================
+R_GetTexture
+
+acess to array elem
+=================
+*/
+gltexture_t *R_GetTexture( GLenum texnum )
+{
+ ASSERT( texnum >= 0 && texnum < MAX_TEXTURES );
+ return &r_textures[texnum];
+}
+
+/*
+=================
+GL_TargetToString
+=================
+*/
+static const char *GL_TargetToString( GLenum target )
{
switch( target )
{
@@ -55,6 +70,8 @@ const char *GL_Target( GLenum target )
return "3D";
case GL_TEXTURE_CUBE_MAP_ARB:
return "Cube";
+ case GL_TEXTURE_2D_ARRAY_EXT:
+ return "Array";
case GL_TEXTURE_RECTANGLE_EXT:
return "Rect";
}
@@ -69,6 +86,7 @@ GL_Bind
void GL_Bind( GLint tmu, GLenum texnum )
{
gltexture_t *texture;
+ GLuint glTarget;
// missed texture ?
if( texnum <= 0 ) texnum = tr.defaultTexture;
@@ -79,12 +97,16 @@ void GL_Bind( GLint tmu, GLenum texnum )
else tmu = glState.activeTMU;
texture = &r_textures[texnum];
+ glTarget = texture->target;
+
+ if( glTarget == GL_TEXTURE_2D_ARRAY_EXT )
+ glTarget = GL_TEXTURE_2D;
- if( glState.currentTextureTargets[tmu] != texture->target )
+ if( glState.currentTextureTargets[tmu] != glTarget )
{
if( glState.currentTextureTargets[tmu] != GL_NONE )
pglDisable( glState.currentTextureTargets[tmu] );
- glState.currentTextureTargets[tmu] = texture->target;
+ glState.currentTextureTargets[tmu] = glTarget;
pglEnable( glState.currentTextureTargets[tmu] );
}
@@ -97,125 +119,103 @@ void GL_Bind( GLint tmu, GLenum texnum )
/*
=================
-R_GetTexture
+GL_ApplyTextureParams
=================
*/
-gltexture_t *R_GetTexture( GLenum texnum )
+void GL_ApplyTextureParams( gltexture_t *tex )
{
- ASSERT( texnum >= 0 && texnum < MAX_TEXTURES );
- return &r_textures[texnum];
-}
+ vec4_t border = { 0.0f, 0.0f, 0.0f, 1.0f };
-/*
-=================
-GL_SetTextureType
-
-Just for debug (r_showtextures uses it)
-=================
-*/
-void GL_SetTextureType( GLenum texnum, GLenum type )
-{
- if( texnum <= 0 ) return;
- ASSERT( texnum >= 0 && texnum < MAX_TEXTURES );
- r_textures[texnum].texType = type;
-}
-
-/*
-=================
-GL_TexFilter
-=================
-*/
-void GL_TexFilter( gltexture_t *tex, qboolean update )
-{
- qboolean allowNearest;
- vec4_t zeroClampBorder = { 0.0f, 0.0f, 0.0f, 1.0f };
- vec4_t alphaZeroClampBorder = { 0.0f, 0.0f, 0.0f, 0.0f };
-
- switch( tex->texType )
- {
- case TEX_NOMIP:
- case TEX_CUBEMAP:
- case TEX_LIGHTMAP:
- allowNearest = false;
- break;
- default:
- allowNearest = true;
- break;
- }
+ ASSERT( tex != NULL );
// set texture filter
- if( tex->flags & TF_DEPTHMAP )
+ if( FBitSet( tex->flags, TF_DEPTHMAP ))
{
- pglTexParameteri( tex->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
- pglTexParameteri( tex->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
-
- if( !( tex->flags & TF_NOCOMPARE ))
+ if( !FBitSet( tex->flags, TF_NOCOMPARE ))
{
- pglTexParameteri( tex->target, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL );
pglTexParameteri( tex->target, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB );
+ pglTexParameteri( tex->target, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL );
}
- if( tex->flags & TF_LUMINANCE )
+ if( FBitSet( tex->flags, TF_LUMINANCE ))
pglTexParameteri( tex->target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE );
else pglTexParameteri( tex->target, GL_DEPTH_TEXTURE_MODE_ARB, GL_INTENSITY );
+ if( FBitSet( tex->flags, TF_NEAREST ))
+ {
+ pglTexParameteri( tex->target, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
+ pglTexParameteri( tex->target, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
+ }
+ else
+ {
+ pglTexParameteri( tex->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
+ pglTexParameteri( tex->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
+ }
+
+ // allow max anisotropy as 1.0f on depth textures
if( GL_Support( GL_ANISOTROPY_EXT ))
pglTexParameterf( tex->target, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0f );
}
- else if( tex->flags & TF_NOMIPMAP )
+ else if( FBitSet( tex->flags, TF_NOMIPMAP ) || tex->numMips <= 1 )
{
- if( tex->flags & TF_NEAREST )
+ if( FBitSet( tex->flags, TF_NEAREST ))
{
pglTexParameteri( tex->target, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
pglTexParameteri( tex->target, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
}
else
{
- if( r_textureMagFilter == GL_NEAREST && allowNearest )
- {
- pglTexParameteri( tex->target, GL_TEXTURE_MIN_FILTER, r_textureMagFilter );
- pglTexParameteri( tex->target, GL_TEXTURE_MAG_FILTER, r_textureMagFilter );
- }
- else
- {
- pglTexParameteri( tex->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
- pglTexParameteri( tex->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
- }
+ pglTexParameteri( tex->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
+ pglTexParameteri( tex->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
}
}
else
{
- if( tex->flags & TF_NEAREST )
+ if( FBitSet( tex->flags, TF_NEAREST ) || gl_texture_nearest->integer )
{
pglTexParameteri( tex->target, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST );
pglTexParameteri( tex->target, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
}
else
{
- pglTexParameteri( tex->target, GL_TEXTURE_MIN_FILTER, r_textureMinFilter );
- pglTexParameteri( tex->target, GL_TEXTURE_MAG_FILTER, r_textureMagFilter );
+ pglTexParameteri( tex->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
+ pglTexParameteri( tex->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
}
// set texture anisotropy if available
- if( GL_Support( GL_ANISOTROPY_EXT ) && !( tex->flags & TF_ALPHACONTRAST ))
+ if( GL_Support( GL_ANISOTROPY_EXT ) && ( tex->numMips > 1 ))
pglTexParameterf( tex->target, GL_TEXTURE_MAX_ANISOTROPY_EXT, gl_texture_anisotropy->value );
// set texture LOD bias if available
- if( GL_Support( GL_TEXTURE_LODBIAS ))
+ if( GL_Support( GL_TEXTURE_LOD_BIAS ) && ( tex->numMips > 1 ))
pglTexParameterf( tex->target, GL_TEXTURE_LOD_BIAS_EXT, gl_texture_lodbias->value );
}
- if( update ) return;
-
- if( tex->flags & ( TF_BORDER|TF_ALPHA_BORDER ) && !GL_Support( GL_CLAMP_TEXBORDER_EXT ))
+ // check if border is not supported
+ if( FBitSet( tex->flags, TF_BORDER ) && !GL_Support( GL_CLAMP_TEXBORDER_EXT ))
{
- // border is not support, use clamp instead
- tex->flags &= ~(TF_BORDER||TF_ALPHA_BORDER);
- tex->flags |= TF_CLAMP;
+ ClearBits( tex->flags, TF_BORDER );
+ SetBits( tex->flags, TF_CLAMP );
}
+ // only seamless cubemaps allows wrap 'clamp_to_border"
+ if( tex->target == GL_TEXTURE_CUBE_MAP_ARB && !GL_Support( GL_ARB_SEAMLESS_CUBEMAP ) && FBitSet( tex->flags, TF_BORDER ))
+ ClearBits( tex->flags, TF_BORDER );
+
// set texture wrap
- if( tex->flags & TF_CLAMP )
+ if( FBitSet( tex->flags, TF_BORDER ))
+ {
+ pglTexParameteri( tex->target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER );
+
+ if( tex->target != GL_TEXTURE_1D )
+ pglTexParameteri( tex->target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER );
+
+ if( tex->target == GL_TEXTURE_3D || tex->target == GL_TEXTURE_CUBE_MAP_ARB )
+ pglTexParameteri( tex->target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER );
+
+ pglTexParameterfv( tex->target, GL_TEXTURE_BORDER_COLOR, border );
+ }
+ else if( FBitSet( tex->flags, TF_CLAMP ))
{
if( GL_Support( GL_CLAMPTOEDGE_EXT ))
{
@@ -238,21 +238,6 @@ void GL_TexFilter( gltexture_t *tex, qboolean update )
pglTexParameteri( tex->target, GL_TEXTURE_WRAP_R, GL_CLAMP );
}
}
- else if( tex->flags & ( TF_BORDER|TF_ALPHA_BORDER ))
- {
- pglTexParameteri( tex->target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER );
-
- if( tex->target != GL_TEXTURE_1D )
- pglTexParameteri( tex->target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER );
-
- if( tex->target == GL_TEXTURE_3D || tex->target == GL_TEXTURE_CUBE_MAP_ARB )
- pglTexParameteri( tex->target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER );
-
- if( tex->flags & TF_BORDER )
- pglTexParameterfv( tex->target, GL_TEXTURE_BORDER_COLOR, zeroClampBorder );
- else if( tex->flags & TF_ALPHA_BORDER )
- pglTexParameterfv( tex->target, GL_TEXTURE_BORDER_COLOR, alphaZeroClampBorder );
- }
else
{
pglTexParameteri( tex->target, GL_TEXTURE_WRAP_S, GL_REPEAT );
@@ -267,53 +252,49 @@ void GL_TexFilter( gltexture_t *tex, qboolean update )
/*
=================
-R_SetTextureParameters
+GL_UpdateTextureParams
=================
*/
-void R_SetTextureParameters( void )
+static void GL_UpdateTextureParams( int iTexture )
{
- gltexture_t *texture;
- int i;
+ gltexture_t *tex = &r_textures[iTexture];
- if( !Q_stricmp( gl_texturemode->string, "GL_NEAREST" ))
- {
- r_textureMinFilter = GL_NEAREST;
- r_textureMagFilter = GL_NEAREST;
- }
- else if( !Q_stricmp( gl_texturemode->string, "GL_LINEAR" ))
- {
- r_textureMinFilter = GL_LINEAR;
- r_textureMagFilter = GL_LINEAR;
- }
- else if( !Q_stricmp( gl_texturemode->string, "GL_NEAREST_MIPMAP_NEAREST" ))
- {
- r_textureMinFilter = GL_NEAREST_MIPMAP_NEAREST;
- r_textureMagFilter = GL_NEAREST;
- }
- else if( !Q_stricmp( gl_texturemode->string, "GL_LINEAR_MIPMAP_NEAREST" ))
- {
- r_textureMinFilter = GL_LINEAR_MIPMAP_NEAREST;
- r_textureMagFilter = GL_LINEAR;
- }
- else if( !Q_stricmp( gl_texturemode->string, "GL_NEAREST_MIPMAP_LINEAR" ))
- {
- r_textureMinFilter = GL_NEAREST_MIPMAP_LINEAR;
- r_textureMagFilter = GL_NEAREST;
- }
- else if( !Q_stricmp( gl_texturemode->string, "GL_LINEAR_MIPMAP_LINEAR" ))
+ ASSERT( tex != NULL );
+
+ if( !tex->texnum ) return; // free slot
+
+ GL_Bind( GL_TEXTURE0, iTexture );
+
+ // set texture anisotropy if available
+ if( GL_Support( GL_ANISOTROPY_EXT ) && ( tex->numMips > 1 ) && !FBitSet( tex->flags, TF_DEPTHMAP ))
+ pglTexParameterf( tex->target, GL_TEXTURE_MAX_ANISOTROPY_EXT, gl_texture_anisotropy->value );
+
+ // set texture LOD bias if available
+ if( GL_Support( GL_TEXTURE_LOD_BIAS ) && ( tex->numMips > 1 ) && !FBitSet( tex->flags, TF_DEPTHMAP ))
+ pglTexParameterf( tex->target, GL_TEXTURE_LOD_BIAS_EXT, gl_texture_lodbias->value );
+
+ if( tex->numMips <= 1 ) return;
+
+ if( FBitSet( tex->flags, TF_NEAREST ) || gl_texture_nearest->integer )
{
- r_textureMinFilter = GL_LINEAR_MIPMAP_LINEAR;
- r_textureMagFilter = GL_LINEAR;
+ pglTexParameteri( tex->target, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST );
+ pglTexParameteri( tex->target, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
}
else
{
- MsgDev( D_ERROR, "gl_texturemode invalid mode %s, defaulting to GL_LINEAR_MIPMAP_LINEAR\n", gl_texturemode->string );
- Cvar_Set( "gl_texturemode", "GL_LINEAR_MIPMAP_LINEAR" );
- r_textureMinFilter = GL_LINEAR_MIPMAP_LINEAR;
- r_textureMagFilter = GL_LINEAR;
+ pglTexParameteri( tex->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
+ pglTexParameteri( tex->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
}
+}
- gl_texturemode->modified = false;
+/*
+=================
+R_SetTextureParameters
+=================
+*/
+void R_SetTextureParameters( void )
+{
+ int i;
if( GL_Support( GL_ANISOTROPY_EXT ))
{
@@ -323,222 +304,21 @@ void R_SetTextureParameters( void )
Cvar_SetFloat( "gl_anisotropy", 1.0f );
}
- gl_texture_anisotropy->modified = false;
-
- if( GL_Support( GL_TEXTURE_LODBIAS ))
+ if( GL_Support( GL_TEXTURE_LOD_BIAS ))
{
- if( gl_texture_lodbias->value > glConfig.max_texture_lodbias )
- Cvar_SetFloat( "gl_texture_lodbias", glConfig.max_texture_lodbias );
- else if( gl_texture_lodbias->value < -glConfig.max_texture_lodbias )
- Cvar_SetFloat( "gl_texture_lodbias", -glConfig.max_texture_lodbias );
+ if( gl_texture_lodbias->value < -glConfig.max_texture_lod_bias )
+ Cvar_SetFloat( "gl_texture_lodbias", -glConfig.max_texture_lod_bias );
+ else if( gl_texture_lodbias->value > glConfig.max_texture_lod_bias )
+ Cvar_SetFloat( "gl_texture_lodbias", glConfig.max_texture_lod_bias );
}
+ gl_texture_anisotropy->modified = false;
gl_texture_lodbias->modified = false;
+ gl_texture_nearest->modified = false;
// change all the existing mipmapped texture objects
- for( i = 0, texture = r_textures; i < r_numTextures; i++, texture++ )
- {
- if( !texture->texnum ) continue; // free slot
- GL_Bind( GL_TEXTURE0, i );
- GL_TexFilter( texture, true );
- }
-}
-
-/*
-===============
-R_TextureList_f
-===============
-*/
-void R_TextureList_f( void )
-{
- gltexture_t *image;
- int i, texCount, bytes = 0;
-
- Msg( "\n" );
- Msg(" -w-- -h-- -size- -fmt- type -data-- -encode-- -wrap-- -name--------\n" );
-
- for( i = texCount = 0, image = r_textures; i < r_numTextures; i++, image++ )
- {
- if( !image->texnum ) continue;
-
- bytes += image->size;
- texCount++;
-
- Msg( "%4i: ", i );
- Msg( "%4i %4i ", image->width, image->height );
- Msg( "%5ik ", image->size >> 10 );
-
- switch( image->format )
- {
- case GL_COMPRESSED_RGBA_ARB:
- Msg( "CRGBA " );
- break;
- case GL_COMPRESSED_RGB_ARB:
- Msg( "CRGB " );
- break;
- case GL_COMPRESSED_LUMINANCE_ALPHA_ARB:
- Msg( "CLA " );
- break;
- case GL_COMPRESSED_LUMINANCE_ARB:
- Msg( "CL " );
- break;
- case GL_COMPRESSED_ALPHA_ARB:
- Msg( "CA " );
- break;
- case GL_COMPRESSED_INTENSITY_ARB:
- Msg( "CI " );
- break;
- case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
- Msg( "DXT1c " );
- break;
- case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
- Msg( "DXT1a " );
- break;
- case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
- Msg( "DXT3 " );
- break;
- case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
- Msg( "DXT5 " );
- break;
- case GL_RGBA:
- Msg( "RGBA " );
- break;
- case GL_RGBA8:
- Msg( "RGBA8 " );
- break;
- case GL_RGBA4:
- Msg( "RGBA4 " );
- break;
- case GL_RGB:
- Msg( "RGB " );
- break;
- case GL_RGB8:
- Msg( "RGB8 " );
- break;
- case GL_RGB5:
- Msg( "RGB5 " );
- break;
- case GL_LUMINANCE4_ALPHA4:
- Msg( "L4A4 " );
- break;
- case GL_LUMINANCE_ALPHA:
- case GL_LUMINANCE8_ALPHA8:
- Msg( "L8A8 " );
- break;
- case GL_LUMINANCE4:
- Msg( "L4 " );
- break;
- case GL_LUMINANCE:
- case GL_LUMINANCE8:
- Msg( "L8 " );
- break;
- case GL_ALPHA8:
- Msg( "A8 " );
- break;
- case GL_INTENSITY8:
- Msg( "I8 " );
- break;
- case GL_DEPTH_COMPONENT:
- case GL_DEPTH_COMPONENT24:
- Msg( "DEPTH24" );
- break;
- case GL_DEPTH_COMPONENT32F:
- Msg( "DEPTH32" );
- break;
- case GL_LUMINANCE16F_ARB:
- Msg( "L16F " );
- break;
- case GL_LUMINANCE32F_ARB:
- Msg( "L32F " );
- break;
- case GL_LUMINANCE_ALPHA16F_ARB:
- Msg( "LA16F " );
- break;
- case GL_LUMINANCE_ALPHA32F_ARB:
- Msg( "LA32F " );
- break;
- case GL_RGB16F_ARB:
- Msg( "RGB16F" );
- break;
- case GL_RGB32F_ARB:
- Msg( "RGB32F" );
- break;
- case GL_RGBA16F_ARB:
- Msg( "RGBA16F" );
- break;
- case GL_RGBA32F_ARB:
- Msg( "RGBA32F" );
- break;
- default:
- Msg( "????? " );
- break;
- }
-
- switch( image->target )
- {
- case GL_TEXTURE_1D:
- Msg( " 1D " );
- break;
- case GL_TEXTURE_2D:
- Msg( " 2D " );
- break;
- case GL_TEXTURE_3D:
- Msg( " 3D " );
- break;
- case GL_TEXTURE_CUBE_MAP_ARB:
- Msg( "CUBE " );
- break;
- case GL_TEXTURE_RECTANGLE_EXT:
- Msg( "RECT " );
- break;
- default:
- Msg( "???? " );
- break;
- }
-
- if( image->flags & TF_NORMALMAP )
- Msg( "normal " );
- else Msg( "diffuse " );
-
- switch( image->encode )
- {
- case DXT_ENCODE_COLOR_YCoCg:
- Msg( "YCoCg " );
- break;
- case DXT_ENCODE_NORMAL_AG_ORTHO:
- Msg( "ortho " );
- break;
- case DXT_ENCODE_NORMAL_AG_STEREO:
- Msg( "stereo " );
- break;
- case DXT_ENCODE_NORMAL_AG_PARABOLOID:
- Msg( "parabolic " );
- break;
- case DXT_ENCODE_NORMAL_AG_QUARTIC:
- Msg( "quartic " );
- break;
- case DXT_ENCODE_NORMAL_AG_AZIMUTHAL:
- Msg( "azimuthal " );
- break;
- default:
- Msg( "default " );
- break;
- }
-
- if( image->flags & TF_CLAMP )
- Msg( "clamp " );
- else if( image->flags & TF_BORDER )
- Msg( "border " );
- else if( image->flags & TF_ALPHA_BORDER )
- Msg( "aborder" );
- else Msg( "repeat " );
- Msg( " %s\n", image->name );
- }
-
- Msg( "---------------------------------------------------------\n" );
- Msg( "%i total textures\n", texCount );
- Msg( "%s total memory used\n", Q_memprint( bytes ));
- Msg( "\n" );
+ for( i = 0; i < r_numTextures; i++ )
+ GL_UpdateTextureParams( i );
}
/*
@@ -546,259 +326,407 @@ void R_TextureList_f( void )
GL_CalcTextureSamples
================
*/
-int GL_CalcTextureSamples( int flags )
+static int GL_CalcTextureSamples( int flags )
{
- if( flags & IMAGE_HAS_COLOR )
- return (flags & IMAGE_HAS_ALPHA) ? 4 : 3;
- return (flags & IMAGE_HAS_ALPHA) ? 2 : 1;
+ if( FBitSet( flags, IMAGE_HAS_COLOR ))
+ return FBitSet( flags, IMAGE_HAS_ALPHA ) ? 4 : 3;
+ return FBitSet( flags, IMAGE_HAS_ALPHA ) ? 2 : 1;
}
/*
-================
-GL_ImageFlagsFromSamples
-================
+==================
+GL_CalcImageSize
+==================
*/
-int GL_ImageFlagsFromSamples( int samples )
+static size_t GL_CalcImageSize( pixformat_t format, int width, int height, int depth )
{
- switch( samples )
+ size_t size = 0;
+
+ // check the depth error
+ depth = Q_max( 1, depth );
+
+ switch( format )
{
- case 2: return IMAGE_HAS_ALPHA;
- case 3: return IMAGE_HAS_COLOR;
- case 4: return (IMAGE_HAS_COLOR|IMAGE_HAS_ALPHA);
+ case PF_RGB_24:
+ case PF_BGR_24:
+ size = width * height * depth * 3;
+ break;
+ case PF_BGRA_32:
+ case PF_RGBA_32:
+ size = width * height * depth * 4;
+ break;
+ case PF_DXT1:
+ size = (((width + 3) >> 2) * ((height + 3) >> 2) * 8) * depth;
+ break;
+ case PF_DXT3:
+ case PF_DXT5:
+ size = (((width + 3) >> 2) * ((height + 3) >> 2) * 16) * depth;
+ break;
}
- return 0;
+ return size;
}
/*
-================
-GL_CalcImageSamples
-================
+==================
+GL_CalcTextureSize
+==================
*/
-int GL_CalcImageSamples( int s1, int s2 )
+static size_t GL_CalcTextureSize( GLenum format, int width, int height, int depth )
{
- int samples;
+ size_t size = 0;
- if( s1 == 1 ) samples = s2;
- else if( s1 == 2 )
+ // check the depth error
+ depth = Q_max( 1, depth );
+
+ switch( format )
{
- if( s2 == 3 || s2 == 4 )
- samples = 4;
- else samples = 2;
+ case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
+ case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
+ size = (((width + 3) >> 2) * ((height + 3) >> 2) * 8) * depth;
+ break;
+ case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
+ case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
+ size = (((width + 3) >> 2) * ((height + 3) >> 2) * 16) * depth;
+ break;
+ case GL_RGBA8:
+ case GL_RGBA:
+ size = width * height * depth * 4;
+ break;
+ case GL_RGB8:
+ case GL_RGB:
+ size = width * height * depth * 4;
+ break;
+ case GL_INTENSITY:
+ case GL_LUMINANCE:
+ case GL_INTENSITY8:
+ case GL_LUMINANCE8:
+ size = (width * height * depth);
+ break;
+ case GL_LUMINANCE_ALPHA:
+ case GL_LUMINANCE8_ALPHA8:
+ size = width * height * depth * 2;
+ break;
+ case GL_R8:
+ size = width * height * depth;
+ break;
+ case GL_RG8:
+ size = width * height * depth * 2;
+ break;
+ case GL_R16:
+ size = width * height * depth * 2;
+ break;
+ case GL_RG16:
+ size = width * height * depth * 4;
+ break;
+ case GL_R16F:
+ case GL_LUMINANCE16F_ARB:
+ size = width * height * depth * 2; // half-floats
+ break;
+ case GL_R32F:
+ case GL_LUMINANCE32F_ARB:
+ size = width * height * depth * 4;
+ break;
+ case GL_RG16F:
+ case GL_LUMINANCE_ALPHA16F_ARB:
+ size = width * height * depth * 4;
+ break;
+ case GL_RG32F:
+ case GL_LUMINANCE_ALPHA32F_ARB:
+ size = width * height * depth * 8;
+ break;
+ case GL_RGB16F_ARB:
+ case GL_RGBA16F_ARB:
+ size = width * height * depth * 8;
+ break;
+ case GL_RGB32F_ARB:
+ case GL_RGBA32F_ARB:
+ size = width * height * depth * 16;
+ break;
+ case GL_DEPTH_COMPONENT16:
+ size = width * height * depth * 2;
+ break;
+ case GL_DEPTH_COMPONENT24:
+ size = width * height * depth * 4;
+ break;
+ case GL_DEPTH_COMPONENT32F:
+ size = width * height * depth * 4;
+ break;
+ default:
+ Host_Error( "GL_CalcTextureSize: bad texture internal format (%u)\n", format );
+ break;
}
- else if( s1 == 3 )
+
+ return size;
+}
+
+static int GL_CalcMipmapCount( gltexture_t *tex, qboolean haveBuffer )
+{
+ int width, height;
+ int mipcount;
+
+ ASSERT( tex != NULL );
+
+ if( !haveBuffer || tex->target == GL_TEXTURE_3D )
+ return 1;
+
+ // generate mip-levels by user request
+ if( FBitSet( tex->flags, TF_NOMIPMAP ))
+ return 1;
+
+ // mip-maps can't exceeds 16
+ for( mipcount = 0; mipcount < 16; mipcount++ )
{
- if( s2 == 2 || s2 == 4 )
- samples = 4;
- else samples = 3;
+ width = Q_max( 1, ( tex->width >> mipcount ));
+ height = Q_max( 1, ( tex->height >> mipcount ));
+ if( width == 1 && height == 1 )
+ break;
}
- else samples = s1;
- return samples;
+ return mipcount + 1;
}
/*
================
-GL_RoundImageDimensions
+GL_SetTextureDimensions
================
*/
-void GL_RoundImageDimensions( word *width, word *height, texFlags_t flags, qboolean force )
+static void GL_SetTextureDimensions( gltexture_t *tex, int width, int height, int depth )
{
- int scaledWidth, scaledHeight;
-
- scaledWidth = *width;
- scaledHeight = *height;
+ int maxTextureSize;
+ int maxDepthSize = 1;
- if( flags & ( TF_TEXTURE_1D|TF_TEXTURE_3D )) return;
+ ASSERT( tex != NULL );
- if( force || !GL_Support( GL_ARB_TEXTURE_NPOT_EXT ))
+ switch( tex->target )
{
- // find nearest power of two, rounding down if desired
- scaledWidth = NearestPOW( scaledWidth, gl_round_down->integer );
- scaledHeight = NearestPOW( scaledHeight, gl_round_down->integer );
+ case GL_TEXTURE_1D:
+ case GL_TEXTURE_2D:
+ maxTextureSize = glConfig.max_2d_texture_size;
+ break;
+ case GL_TEXTURE_2D_ARRAY_EXT:
+ maxDepthSize = glConfig.max_2d_texture_layers;
+ maxTextureSize = glConfig.max_2d_texture_size;
+ break;
+ case GL_TEXTURE_RECTANGLE_EXT:
+ maxTextureSize = glConfig.max_2d_rectangle_size;
+ break;
+ case GL_TEXTURE_CUBE_MAP_ARB:
+ maxTextureSize = glConfig.max_cubemap_size;
+ break;
+ case GL_TEXTURE_3D:
+ maxDepthSize = glConfig.max_3d_texture_size;
+ maxTextureSize = glConfig.max_3d_texture_size;
+ break;
}
- if( flags & TF_SKYSIDE )
- {
- // let people sample down the sky textures for speed
- scaledWidth >>= gl_skymip->integer;
- scaledHeight >>= gl_skymip->integer;
- }
- else if(!( flags & TF_NOPICMIP ))
+ // store original sizes
+ tex->srcWidth = width;
+ tex->srcHeight = height;
+
+ if( !GL_Support( GL_ARB_TEXTURE_NPOT_EXT ))
{
- // let people sample down the world textures for speed
- scaledWidth >>= gl_picmip->integer;
- scaledHeight >>= gl_picmip->integer;
+ width = (width + 3) & ~3;
+ height = (height + 3) & ~3;
}
- if( flags & TF_CUBEMAP )
+ if( width > maxTextureSize || height > maxTextureSize || depth > maxDepthSize )
{
- while( scaledWidth > glConfig.max_cubemap_size || scaledHeight > glConfig.max_cubemap_size )
+ if( tex->target == GL_TEXTURE_1D )
{
- scaledWidth >>= 1;
- scaledHeight >>= 1;
+ while( width > maxTextureSize )
+ width >>= 1;
}
- }
- else
- {
- if( flags & TF_TEXTURE_RECTANGLE )
+ else if( tex->target == GL_TEXTURE_3D || tex->target == GL_TEXTURE_2D_ARRAY_EXT )
{
- while( scaledWidth > glConfig.max_2d_rectangle_size || scaledHeight > glConfig.max_2d_rectangle_size )
+ while( width > maxTextureSize || height > maxTextureSize || depth > maxDepthSize )
{
- scaledWidth >>= 1;
- scaledHeight >>= 1;
+ width >>= 1;
+ height >>= 1;
+ depth >>= 1;
}
}
- else
+ else // all remaining cases
{
- while( scaledWidth > glConfig.max_2d_texture_size || scaledHeight > glConfig.max_2d_texture_size )
+ while( width > maxTextureSize || height > maxTextureSize )
{
- scaledWidth >>= 1;
- scaledHeight >>= 1;
+ width >>= 1;
+ height >>= 1;
}
}
}
- if( scaledWidth < 1 ) scaledWidth = 1;
- if( scaledHeight < 1 ) scaledHeight = 1;
+ // apply custom downscales
+ if( FBitSet( tex->flags, TF_SKYSIDE ))
+ {
+ // let people sample down the sky textures for speed
+ width >>= gl_skymip->integer;
+ height >>= gl_skymip->integer;
+ }
+ else if( !FBitSet( tex->flags, TF_NOPICMIP ))
+ {
+ // let people sample down the world textures for speed
+ width >>= gl_picmip->integer;
+ height >>= gl_picmip->integer;
+ }
- *width = scaledWidth;
- *height = scaledHeight;
+ // set the texture dimensions
+ tex->width = Q_max( 1, width );
+ tex->height = Q_max( 1, height );
+ tex->depth = Q_max( 1, depth );
}
/*
===============
-GL_TextureFormat
+GL_SetTextureTarget
===============
*/
-static GLenum GL_TextureFormat( gltexture_t *tex, int *samples )
+static void GL_SetTextureTarget( gltexture_t *tex, rgbdata_t *pic )
{
- qboolean compress;
- GLenum format;
+ ASSERT( pic != NULL );
+ ASSERT( tex != NULL );
+
+ // correct depth size
+ pic->depth = Q_max( 1, pic->depth );
+ tex->numMips = 0; // begin counting
+
+ // correct mip count
+ pic->numMips = max( 1, pic->numMips );
+
+ // trying to determine texture type
+ if( pic->width > 1 && pic->height <= 1 )
+ tex->target = GL_TEXTURE_1D;
+ else if( FBitSet( pic->flags, IMAGE_CUBEMAP ))
+ tex->target = GL_TEXTURE_CUBE_MAP_ARB;
+ else if( FBitSet( pic->flags, IMAGE_MULTILAYER ) && pic->depth >= 1 )
+ tex->target = GL_TEXTURE_2D_ARRAY_EXT;
+ else if( pic->width > 1 && pic->height > 1 && pic->depth > 1 )
+ tex->target = GL_TEXTURE_3D;
+ else if( FBitSet( tex->flags, TF_TEXTURE_RECTANGLE ) && pic->width == glState.width && pic->height == glState.height )
+ tex->target = GL_TEXTURE_RECTANGLE_EXT;
+ else tex->target = GL_TEXTURE_2D; // default case
+
+ // check for hardware support
+ if(( tex->target == GL_TEXTURE_CUBE_MAP_ARB ) && !GL_Support( GL_TEXTURE_CUBEMAP_EXT ))
+ tex->target = GL_NONE;
+
+ if(( tex->target == GL_TEXTURE_RECTANGLE_EXT ) && !GL_Support( GL_TEXTURE_2D_RECT_EXT ))
+ tex->target = GL_TEXTURE_2D; // fallback
+
+ if(( tex->target == GL_TEXTURE_2D_ARRAY_EXT ) && !GL_Support( GL_TEXTURE_ARRAY_EXT ))
+ tex->target = GL_NONE;
+
+ if(( tex->target == GL_TEXTURE_3D ) && !GL_Support( GL_TEXTURE_3D_EXT ))
+ tex->target = GL_NONE;
+
+ // depth cubemaps only allowed when GL_EXT_gpu_shader4 is supported
+ if( tex->target == GL_TEXTURE_CUBE_MAP_ARB && !GL_Support( GL_EXT_GPU_SHADER4 ) && FBitSet( tex->flags, TF_DEPTHMAP ))
+ tex->target = GL_NONE;
+}
- // check if it should be compressed
- if( !gl_compress_textures->integer || ( tex->flags & TF_UNCOMPRESSED ))
- compress = false;
- else compress = GL_Support( GL_TEXTURE_COMPRESSION_EXT );
+/*
+===============
+GL_SetTextureFormat
+===============
+*/
+static void GL_SetTextureFormat( gltexture_t *tex, pixformat_t format, int channelMask )
+{
+ qboolean haveColor = ( channelMask & IMAGE_HAS_COLOR );
+ qboolean haveAlpha = ( channelMask & IMAGE_HAS_ALPHA );
+ qboolean compressImage = false;
- // set texture format
- if( tex->flags & TF_DEPTHMAP )
+ ASSERT( tex != NULL );
+
+ if( !FBitSet( tex->flags, TF_UNCOMPRESSED ) && !ImageDXT( format ))
{
- if( tex->flags & TF_FLOAT && GL_Support( GL_ARB_DEPTH_FLOAT_EXT ))
- format = GL_DEPTH_COMPONENT32F;
- else format = GL_DEPTH_COMPONENT24;
- tex->flags &= ~TF_INTENSITY;
+ // check if it should be compressed
+ if( gl_compress_textures->integer && GL_Support( GL_TEXTURE_COMPRESSION_EXT ))
+ compressImage = true;
}
- else if( tex->flags & TF_FLOAT && GL_Support( GL_ARB_TEXTURE_FLOAT_EXT ))
- {
- int bits = glw_state.desktopBitsPixel;
- switch( *samples )
+ if( ImageDXT( format ))
+ {
+ switch( format )
{
- case 1:
- switch( bits )
- {
- case 16: format = GL_LUMINANCE16F_ARB; break;
- default: format = GL_LUMINANCE32F_ARB; break;
- }
- break;
- case 2:
- switch( bits )
- {
- case 16: format = GL_LUMINANCE_ALPHA16F_ARB; break;
- default: format = GL_LUMINANCE_ALPHA32F_ARB; break;
- }
- break;
- case 3:
- switch( bits )
- {
- case 16: format = GL_RGB16F_ARB; break;
- default: format = GL_RGB32F_ARB; break;
- }
- break;
- case 4:
- default:
- switch( bits )
- {
- case 16: format = GL_RGBA16F_ARB; break;
- default: format = GL_RGBA32F_ARB; break;
- }
- break;
+ case PF_DXT1: tex->format = GL_COMPRESSED_RGB_S3TC_DXT1_EXT; break; // never use DXT1 with 1-bit alpha
+ case PF_DXT3: tex->format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; break;
+ case PF_DXT5: tex->format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; break;
}
+ return;
+ }
+ else if( FBitSet( tex->flags, TF_DEPTHMAP ))
+ {
+ if( FBitSet( tex->flags, TF_ARB_16BIT ))
+ tex->format = GL_DEPTH_COMPONENT16;
+ else if( FBitSet( tex->flags, TF_ARB_FLOAT ) && GL_Support( GL_ARB_DEPTH_FLOAT_EXT ))
+ tex->format = GL_DEPTH_COMPONENT32F;
+ else tex->format = GL_DEPTH_COMPONENT24;
}
- else if( compress )
+ else if( FBitSet( tex->flags, TF_ARB_FLOAT ) && GL_Support( GL_ARB_TEXTURE_FLOAT_EXT ))
{
- switch( *samples )
+ if( haveColor && haveAlpha )
{
- case 1: format = GL_COMPRESSED_LUMINANCE_ARB; break;
- case 2: format = GL_COMPRESSED_LUMINANCE_ALPHA_ARB; break;
- case 3: format = GL_COMPRESSED_RGB_ARB; break;
- case 4:
- default: format = GL_COMPRESSED_RGBA_ARB; break;
+ if( FBitSet( tex->flags, TF_ARB_16BIT ) || glw_state.desktopBitsPixel == 16 )
+ tex->format = GL_RGBA16F_ARB;
+ else tex->format = GL_RGBA32F_ARB;
+ }
+ else if( haveColor )
+ {
+ if( FBitSet( tex->flags, TF_ARB_16BIT ) || glw_state.desktopBitsPixel == 16 )
+ tex->format = GL_RGB16F_ARB;
+ else tex->format = GL_RGB32F_ARB;
+ }
+ else if( haveAlpha )
+ {
+ if( FBitSet( tex->flags, TF_ARB_16BIT ) || glw_state.desktopBitsPixel == 16 )
+ tex->format = GL_LUMINANCE_ALPHA16F_ARB;
+ else tex->format = GL_LUMINANCE_ALPHA32F_ARB;
+ }
+ else
+ {
+ if( FBitSet( tex->flags, TF_ARB_16BIT ) || glw_state.desktopBitsPixel == 16 )
+ tex->format = GL_LUMINANCE16F_ARB;
+ else tex->format = GL_LUMINANCE32F_ARB;
+ }
+ }
+ else if( compressImage )
+ {
+ switch( GL_CalcTextureSamples( channelMask ))
+ {
+ case 1: tex->format = GL_LUMINANCE8; break;
+ case 2: tex->format = GL_LUMINANCE8_ALPHA8; break;
+ case 3: tex->format = GL_COMPRESSED_RGB_S3TC_DXT1_EXT; break;
+ case 4: tex->format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; break;
}
-
- if( tex->flags & TF_INTENSITY )
- format = GL_COMPRESSED_INTENSITY_ARB;
- tex->flags &= ~TF_INTENSITY;
}
else
{
+ // NOTE: not all the types will be compressed
int bits = glw_state.desktopBitsPixel;
- switch( *samples )
+ switch( GL_CalcTextureSamples( channelMask ) )
{
- case 1: format = GL_LUMINANCE8; break;
- case 2: format = GL_LUMINANCE8_ALPHA8; break;
+ case 1: tex->format = GL_LUMINANCE8; break;
+ case 2: tex->format = GL_LUMINANCE8_ALPHA8; break;
case 3:
- if( gl_luminance_textures->integer && !( tex->flags & TF_UNCOMPRESSED ))
- {
- switch( bits )
- {
- case 16: format = GL_LUMINANCE4; break;
- case 32: format = GL_LUMINANCE8; break;
- default: format = GL_LUMINANCE; break;
- }
- *samples = 1; // merge for right calc statistics
- }
- else
+ switch( bits )
{
- switch( bits )
- {
- case 16: format = GL_RGB5; break;
- case 32: format = GL_RGB8; break;
- default: format = GL_RGB; break;
- }
+ case 16: tex->format = GL_RGB5; break;
+ case 32: tex->format = GL_RGB8; break;
+ default: tex->format = GL_RGB; break;
}
- break;
+ break;
case 4:
default:
- if( gl_luminance_textures->integer && !( tex->flags & TF_UNCOMPRESSED ))
- {
- switch( bits )
- {
- case 16: format = GL_LUMINANCE4_ALPHA4; break;
- case 32: format = GL_LUMINANCE8_ALPHA8; break;
- default: format = GL_LUMINANCE_ALPHA; break;
- }
- *samples = 2; // merge for right calc statistics
- }
- else
+ switch( bits )
{
- switch( bits )
- {
- case 16: format = GL_RGBA4; break;
- case 32: format = GL_RGBA8; break;
- default: format = GL_RGBA; break;
- }
+ case 16: tex->format = GL_RGBA4; break;
+ case 32: tex->format = GL_RGBA8; break;
+ default: tex->format = GL_RGBA; break;
}
break;
}
-
- if( tex->flags & TF_INTENSITY )
- format = GL_INTENSITY8;
- tex->flags &= ~TF_INTENSITY;
}
- return format;
}
/*
@@ -815,6 +743,7 @@ byte *GL_ResampleTexture( const byte *source, int inWidth, int inHeight, int out
uint p1[0x1000], p2[0x1000];
byte *pix1, *pix2, *pix3, *pix4;
uint *out, *inRow1, *inRow2;
+ static byte *scaledImage = NULL; // pointer to a scaled image
vec3_t normal;
int i, x, y;
@@ -842,8 +771,8 @@ byte *GL_ResampleTexture( const byte *source, int inWidth, int inHeight, int out
{
for( y = 0; y < outHeight; y++, out += outWidth )
{
- inRow1 = in + inWidth * (int)(((float)y + 0.25f) * inHeight/outHeight);
- inRow2 = in + inWidth * (int)(((float)y + 0.75f) * inHeight/outHeight);
+ inRow1 = in + inWidth * (int)(((float)y + 0.25f) * inHeight / outHeight);
+ inRow2 = in + inWidth * (int)(((float)y + 0.75f) * inHeight / outHeight);
for( x = 0; x < outWidth; x++ )
{
@@ -852,16 +781,16 @@ byte *GL_ResampleTexture( const byte *source, int inWidth, int inHeight, int out
pix3 = (byte *)inRow2 + p1[x];
pix4 = (byte *)inRow2 + p2[x];
- normal[0] = (pix1[0] * (1.0f/127.0f) - 1.0f) + (pix2[0] * (1.0f/127.0f) - 1.0f) + (pix3[0] * (1.0f/127.0f) - 1.0f) + (pix4[0] * (1.0f/127.0f) - 1.0f);
- normal[1] = (pix1[1] * (1.0f/127.0f) - 1.0f) + (pix2[1] * (1.0f/127.0f) - 1.0f) + (pix3[1] * (1.0f/127.0f) - 1.0f) + (pix4[1] * (1.0f/127.0f) - 1.0f);
- normal[2] = (pix1[2] * (1.0f/127.0f) - 1.0f) + (pix2[2] * (1.0f/127.0f) - 1.0f) + (pix3[2] * (1.0f/127.0f) - 1.0f) + (pix4[2] * (1.0f/127.0f) - 1.0f);
+ normal[0] = MAKE_SIGNED( pix1[0] ) + MAKE_SIGNED( pix2[0] ) + MAKE_SIGNED( pix3[0] ) + MAKE_SIGNED( pix4[0] );
+ normal[1] = MAKE_SIGNED( pix1[1] ) + MAKE_SIGNED( pix2[1] ) + MAKE_SIGNED( pix3[1] ) + MAKE_SIGNED( pix4[1] );
+ normal[2] = MAKE_SIGNED( pix1[2] ) + MAKE_SIGNED( pix2[2] ) + MAKE_SIGNED( pix3[2] ) + MAKE_SIGNED( pix4[2] );
if( !VectorNormalizeLength( normal ))
- VectorSet( normal, 0.0f, 0.0f, 1.0f );
+ VectorSet( normal, 0.5f, 0.5f, 1.0f );
- ((byte *)(out+x))[0] = (byte)(128 + 127 * normal[0]);
- ((byte *)(out+x))[1] = (byte)(128 + 127 * normal[1]);
- ((byte *)(out+x))[2] = (byte)(128 + 127 * normal[2]);
+ ((byte *)(out+x))[0] = 128 + (byte)(127.0f * normal[0]);
+ ((byte *)(out+x))[1] = 128 + (byte)(127.0f * normal[1]);
+ ((byte *)(out+x))[2] = 128 + (byte)(127.0f * normal[2]);
((byte *)(out+x))[3] = 255;
}
}
@@ -870,8 +799,8 @@ byte *GL_ResampleTexture( const byte *source, int inWidth, int inHeight, int out
{
for( y = 0; y < outHeight; y++, out += outWidth )
{
- inRow1 = in + inWidth * (int)(((float)y + 0.25f) * inHeight/outHeight);
- inRow2 = in + inWidth * (int)(((float)y + 0.75f) * inHeight/outHeight);
+ inRow1 = in + inWidth * (int)(((float)y + 0.25f) * inHeight / outHeight);
+ inRow2 = in + inWidth * (int)(((float)y + 0.75f) * inHeight / outHeight);
for( x = 0; x < outWidth; x++ )
{
@@ -887,6 +816,7 @@ byte *GL_ResampleTexture( const byte *source, int inWidth, int inHeight, int out
}
}
}
+
return scaledImage;
}
@@ -922,96 +852,82 @@ GL_BuildMipMap
Operates in place, quartering the size of the texture
=================
*/
-static void GL_BuildMipMap( byte *in, int width, int height, qboolean isNormalMap )
+static void GL_BuildMipMap( byte *in, int srcWidth, int srcHeight, int srcDepth, qboolean isNormalMap )
{
byte *out = in;
+ int instride = ALIGN( srcWidth * 4, 1 );
+ int mipWidth, mipHeight, outpadding;
+ int row, x, y, z;
vec3_t normal;
- int x, y;
-
- width <<= 2;
- height >>= 1;
-
- if( isNormalMap )
- {
- for( y = 0; y < height; y++, in += width )
- {
- for( x = 0; x < width; x += 8, in += 8, out += 4 )
- {
- normal[0] = (in[0] * (1.0f/127.0f) - 1.0f) + (in[4] * (1.0f/127.0f) - 1.0f) + (in[width+0] * (1.0f/127.0f) - 1.0f) + (in[width+4] * (1.0f/127.0f) - 1.0f);
- normal[1] = (in[1] * (1.0f/127.0f) - 1.0f) + (in[5] * (1.0f/127.0f) - 1.0f) + (in[width+1] * (1.0f/127.0f) - 1.0f) + (in[width+5] * (1.0f/127.0f) - 1.0f);
- normal[2] = (in[2] * (1.0f/127.0f) - 1.0f) + (in[6] * (1.0f/127.0f) - 1.0f) + (in[width+2] * (1.0f/127.0f) - 1.0f) + (in[width+6] * (1.0f/127.0f) - 1.0f);
- if( !VectorNormalizeLength( normal ))
- VectorSet( normal, 0.0f, 0.0f, 1.0f );
-
- out[0] = (byte)(128 + 127 * normal[0]);
- out[1] = (byte)(128 + 127 * normal[1]);
- out[2] = (byte)(128 + 127 * normal[2]);
- out[3] = 255;
- }
- }
- }
- else
- {
- for( y = 0; y < height; y++, in += width )
- {
- for( x = 0; x < width; x += 8, in += 8, out += 4 )
- {
- out[0] = (in[0] + in[4] + in[width+0] + in[width+4]) >> 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_GenerateMipmaps
-
-sgis generate mipmap
-===============
-*/
-void GL_GenerateMipmaps( byte *buffer, rgbdata_t *pic, gltexture_t *tex, GLenum glTarget, GLenum inFormat, int side, qboolean subImage )
-{
- int mipLevel;
- int dataType = GL_UNSIGNED_BYTE;
- int w, h;
-
- // not needs
- if( tex->flags & TF_NOMIPMAP )
- return;
-
- if( GL_Support( GL_SGIS_MIPMAPS_EXT ) && !( tex->flags & ( TF_NORMALMAP|TF_ALPHACONTRAST )))
- {
- pglHint( GL_GENERATE_MIPMAP_HINT_SGIS, GL_NICEST );
- pglTexParameteri( glTarget, GL_GENERATE_MIPMAP_SGIS, GL_TRUE );
- pglGetError(); // clear error queue on mips generate
- return;
- }
-
- // screen texture?
- if( !buffer ) return;
-
- mipLevel = 0;
- w = tex->width;
- h = tex->height;
-
- // software mipmap generator
- while( w > 1 || h > 1 )
- {
- // build the mipmap
- if( tex->flags & TF_ALPHACONTRAST ) memset( buffer, pic->width >> mipLevel, w * h * 4 );
- else GL_BuildMipMap( buffer, w, h, ( tex->flags & TF_NORMALMAP ));
+ if( !in ) return;
- w = (w+1)>>1;
- h = (h+1)>>1;
- mipLevel++;
+ mipWidth = max( 1, ( srcWidth >> 1 ));
+ mipHeight = max( 1, ( srcHeight >> 1 ));
+ outpadding = ALIGN( mipWidth * 4, 1 ) - mipWidth * 4;
+ row = srcWidth << 2;
- if( subImage ) pglTexSubImage2D( tex->target + side, mipLevel, 0, 0, w, h, inFormat, dataType, buffer );
- else pglTexImage2D( tex->target + side, mipLevel, tex->format, w, h, 0, inFormat, dataType, buffer );
- if( pglGetError( )) break; // can't create mip levels
+ // move through all layers
+ for( z = 0; z < srcDepth; z++ )
+ {
+ if( isNormalMap )
+ {
+ for( y = 0; y < mipHeight; y++, in += instride * 2, out += outpadding )
+ {
+ byte *next = ((( y << 1 ) + 1 ) < srcHeight ) ? ( in + instride ) : in;
+ for( x = 0, row = 0; x < mipWidth; x++, row += 8, out += 4 )
+ {
+ if((( x << 1 ) + 1 ) < srcWidth )
+ {
+ normal[0] = MAKE_SIGNED( in[row+0] ) + MAKE_SIGNED( in[row+4] )
+ + MAKE_SIGNED( next[row+0] ) + MAKE_SIGNED( next[row+4] );
+ normal[1] = MAKE_SIGNED( in[row+1] ) + MAKE_SIGNED( in[row+5] )
+ + MAKE_SIGNED( next[row+1] ) + MAKE_SIGNED( next[row+5] );
+ normal[2] = MAKE_SIGNED( in[row+2] ) + MAKE_SIGNED( in[row+6] )
+ + MAKE_SIGNED( next[row+2] ) + MAKE_SIGNED( next[row+6] );
+ }
+ else
+ {
+ normal[0] = MAKE_SIGNED( in[row+0] ) + MAKE_SIGNED( next[row+0] );
+ normal[1] = MAKE_SIGNED( in[row+1] ) + MAKE_SIGNED( next[row+1] );
+ normal[2] = MAKE_SIGNED( in[row+2] ) + MAKE_SIGNED( next[row+2] );
+ }
+
+
+ if( !VectorNormalizeLength( normal ))
+ VectorSet( normal, 0.5f, 0.5f, 1.0f );
+
+ out[0] = 128 + (byte)(127.0f * normal[0]);
+ out[1] = 128 + (byte)(127.0f * normal[1]);
+ out[2] = 128 + (byte)(127.0f * normal[2]);
+ out[3] = 255;
+ }
+ }
+ }
+ else
+ {
+ for( y = 0; y < mipHeight; y++, in += instride * 2, out += outpadding )
+ {
+ byte *next = ((( y << 1 ) + 1 ) < srcHeight ) ? ( in + instride ) : in;
+ for( x = 0, row = 0; x < mipWidth; x++, row += 8, out += 4 )
+ {
+ if((( x << 1 ) + 1 ) < srcWidth )
+ {
+ out[0] = (in[row+0] + in[row+4] + next[row+0] + next[row+4]) >> 2;
+ out[1] = (in[row+1] + in[row+5] + next[row+1] + next[row+5]) >> 2;
+ out[2] = (in[row+2] + in[row+6] + next[row+2] + next[row+6]) >> 2;
+ out[3] = (in[row+3] + in[row+7] + next[row+3] + next[row+7]) >> 2;
+ }
+ else
+ {
+ out[0] = (in[row+0] + next[row+0]) >> 1;
+ out[1] = (in[row+1] + next[row+1]) >> 1;
+ out[2] = (in[row+2] + next[row+2]) >> 1;
+ out[3] = (in[row+3] + next[row+3]) >> 1;
+ }
+ }
+ }
+ }
}
}
@@ -1045,193 +961,85 @@ void GL_MakeLuminance( rgbdata_t *in )
}
}
-static void GL_TextureImage( GLenum inFormat, GLenum outFormat, GLenum glTarget, GLint side, GLint level, GLint width, GLint height, GLint depth, qboolean subImage, size_t size, const void *data )
+static void GL_TextureImageRAW( gltexture_t *tex, GLint side, GLint level, GLint width, GLint height, GLint depth, GLint type, const void *data )
{
+ GLuint cubeTarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB;
+ qboolean subImage = ( tex->flags & TF_IMG_UPLOADED );
+ GLenum inFormat = PFDesc[type].glFormat;
GLint dataType = GL_UNSIGNED_BYTE;
- if( glTarget == GL_TEXTURE_1D )
+ ASSERT( tex != NULL );
+
+ if( tex->flags & TF_DEPTHMAP )
+ inFormat = GL_DEPTH_COMPONENT;
+
+ if( tex->target == GL_TEXTURE_1D )
{
- if( subImage ) pglTexSubImage1D( glTarget, level, 0, width, inFormat, dataType, data );
- else pglTexImage1D( glTarget, level, outFormat, width, 0, inFormat, dataType, data );
+ if( subImage ) pglTexSubImage1D( tex->target, level, 0, width, inFormat, dataType, data );
+ else pglTexImage1D( tex->target, level, tex->format, width, 0, inFormat, dataType, data );
}
- else if( glTarget == GL_TEXTURE_CUBE_MAP_ARB )
+ else if( tex->target == GL_TEXTURE_CUBE_MAP_ARB )
{
- if( subImage ) pglTexSubImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB + side, level, 0, 0, width, height, inFormat, dataType, data );
- else pglTexImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB + side, level, outFormat, width, height, 0, inFormat, dataType, data );
+ if( subImage ) pglTexSubImage2D( cubeTarget + side, level, 0, 0, width, height, inFormat, dataType, data );
+ else pglTexImage2D( cubeTarget + side, level, tex->format, width, height, 0, inFormat, dataType, data );
}
- else if( glTarget == GL_TEXTURE_3D )
+ else if( tex->target == GL_TEXTURE_3D || tex->target == GL_TEXTURE_2D_ARRAY_EXT )
{
- if( subImage ) pglTexSubImage3D( glTarget, level, 0, 0, 0, width, height, depth, inFormat, dataType, data );
- else pglTexImage3D( glTarget, level, outFormat, width, height, depth, 0, inFormat, dataType, data );
+ if( subImage ) pglTexSubImage3D( tex->target, level, 0, 0, 0, width, height, depth, inFormat, dataType, data );
+ else pglTexImage3D( tex->target, level, tex->format, width, height, depth, 0, inFormat, dataType, data );
}
- else
+ else // 2D or RECT
{
- if( subImage ) pglTexSubImage2D( glTarget, level, 0, 0, width, height, inFormat, dataType, data );
- else pglTexImage2D( glTarget, level, outFormat, width, height, 0, inFormat, dataType, data );
+ if( subImage ) pglTexSubImage2D( tex->target, level, 0, 0, width, height, inFormat, dataType, data );
+ else pglTexImage2D( tex->target, level, tex->format, width, height, 0, inFormat, dataType, data );
}
}
-static void GL_TextureImageDXT( GLenum format, GLenum glTarget, GLint side, GLint level, GLint width, GLint height, GLint depth, qboolean subImage, size_t size, const void *data )
+static void GL_TextureImageDXT( gltexture_t *tex, GLint side, GLint level, GLint width, GLint height, GLint depth, size_t size, const void *data )
{
- if( glTarget == GL_TEXTURE_1D )
+ GLuint cubeTarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB;
+ qboolean subImage = ( tex->flags & TF_IMG_UPLOADED );
+
+ ASSERT( tex != NULL );
+
+ if( tex->target == GL_TEXTURE_1D )
{
- if( subImage ) pglCompressedTexSubImage1DARB( glTarget, level, 0, width, format, size, data );
- else pglCompressedTexImage1DARB( glTarget, level, format, width, 0, size, data );
+ if( subImage ) pglCompressedTexSubImage1DARB( tex->target, level, 0, width, tex->format, size, data );
+ else pglCompressedTexImage1DARB( tex->target, level, tex->format, width, 0, size, data );
}
- else if( glTarget == GL_TEXTURE_CUBE_MAP_ARB )
+ else if( tex->target == GL_TEXTURE_CUBE_MAP_ARB )
{
- if( subImage ) pglCompressedTexSubImage2DARB( GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB + side, level, 0, 0, width, height, format, size, data );
- else pglCompressedTexImage2DARB( GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB + side, level, format, width, height, 0, size, data );
+ if( subImage ) pglCompressedTexSubImage2DARB( cubeTarget + side, level, 0, 0, width, height, tex->format, size, data );
+ else pglCompressedTexImage2DARB( cubeTarget + side, level, tex->format, width, height, 0, size, data );
}
- else if( glTarget == GL_TEXTURE_3D )
+ else if( tex->target == GL_TEXTURE_3D || tex->target == GL_TEXTURE_2D_ARRAY_EXT )
{
- if( subImage ) pglCompressedTexSubImage3DARB( glTarget, level, 0, 0, 0, width, height, depth, format, size, data );
- else pglCompressedTexImage3DARB( glTarget, level, format, width, height, depth, 0, size, data );
+ if( subImage ) pglCompressedTexSubImage3DARB( tex->target, level, 0, 0, 0, width, height, depth, tex->format, size, data );
+ else pglCompressedTexImage3DARB( tex->target, level, tex->format, width, height, depth, 0, size, data );
}
else // 2D or RECT
{
- if( subImage ) pglCompressedTexSubImage2DARB( glTarget, level, 0, 0, width, height, format, size, data );
- else pglCompressedTexImage2DARB( glTarget, level, format, width, height, 0, size, data );
+ if( subImage ) pglCompressedTexSubImage2DARB( tex->target, level, 0, 0, width, height, tex->format, size, data );
+ else pglCompressedTexImage2DARB( tex->target, level, tex->format, width, height, 0, size, data );
}
}
/*
===============
-GL_UploadTextureDXT
+GL_CheckTexImageError
-upload compressed texture into video memory
+show GL-errors on load images
===============
*/
-static void GL_UploadTextureDXT( rgbdata_t *pic, gltexture_t *tex, qboolean subImage, imgfilter_t *filter )
+static void GL_CheckTexImageError( gltexture_t *tex )
{
- byte *buf;
- const byte *bufend;
- GLenum inFormat, glTarget;
- uint width, height, depth;
- int texsize = 0, samples;
- uint i, j, s, numSides;
- int numMips, err;
-
- ASSERT( pic != NULL && tex != NULL );
-
- tex->srcWidth = tex->width = pic->width;
- tex->srcHeight = tex->height = pic->height;
- s = tex->srcWidth * tex->srcHeight;
-
- tex->fogParams[0] = pic->fogParams[0];
- tex->fogParams[1] = pic->fogParams[1];
- tex->fogParams[2] = pic->fogParams[2];
- tex->fogParams[3] = pic->fogParams[3];
-
- // NOTE: normalmaps must be power of two or software mip generator will stop working
- GL_RoundImageDimensions( &tex->width, &tex->height, tex->flags, ( tex->flags & TF_NORMALMAP ));
-
- if( s&3 )
- {
- // will be resample, just tell me for debug targets
- MsgDev( D_NOTE, "GL_Upload: %s s&3 [%d x %d]\n", tex->name, tex->srcWidth, tex->srcHeight );
- }
-
- // clear all the unsupported flags
- tex->flags &= ~TF_KEEP_8BIT;
- tex->flags &= ~TF_KEEP_RGBDATA;
- tex->flags |= TF_NOPICMIP;
- tex->encode = pic->encode; // share encode method
-
- samples = GL_CalcTextureSamples( pic->flags );
-
- if( pic->flags & IMAGE_HAS_ALPHA )
- tex->flags |= TF_HAS_ALPHA;
-
- if( !pic->numMips ) tex->flags |= TF_NOMIPMAP; // disable mipmapping by user request
-
- // determine format
- inFormat = PFDesc[pic->type].glFormat;
-
- if( ImageDXT( pic->type ))
- tex->format = inFormat;
- else tex->format = GL_TextureFormat( tex, &samples );
-
- if( !( tex->flags & TF_HAS_ALPHA ) && inFormat == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT )
- tex->format = inFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT; // OpenGL hint
-
- // determine target
- tex->target = glTarget = GL_TEXTURE_2D;
-
- numMips = (pic->numMips > 0) ? pic->numMips : 1;
- numSides = 1;
-
- if( pic->flags & IMAGE_CUBEMAP )
- {
- if( GL_Support( GL_TEXTURECUBEMAP_EXT ))
- {
- numSides = 6;
- tex->target = glTarget = GL_TEXTURE_CUBE_MAP_ARB;
- tex->flags |= TF_CUBEMAP;
-
- if( !GL_Support( GL_ARB_SEAMLESS_CUBEMAP ) && ( tex->flags & ( TF_BORDER|TF_ALPHA_BORDER )))
- {
- // don't use border for cubemaps (but allow for seamless cubemaps)
- tex->flags &= ~(TF_BORDER|TF_ALPHA_BORDER);
- tex->flags |= TF_CLAMP;
- }
- }
- else
- {
- MsgDev( D_WARN, "GL_UploadTexture: cubemaps isn't supported, %s ignored\n", tex->name );
- tex->flags &= ~TF_CUBEMAP;
- }
- }
- else if( tex->flags & TF_TEXTURE_1D || pic->height <= 1 )
- {
- // determine target
- tex->target = glTarget = GL_TEXTURE_1D;
- }
- else if( tex->flags & TF_TEXTURE_RECTANGLE )
- {
- if( glConfig.max_2d_rectangle_size )
- tex->target = glTarget = glConfig.texRectangle;
- // or leave as GL_TEXTURE_2D
- }
- else if( tex->flags & TF_TEXTURE_3D )
- {
- // determine target
- tex->target = glTarget = GL_TEXTURE_3D;
- }
-
- pglBindTexture( tex->target, tex->texnum );
-
- buf = pic->buffer;
- bufend = pic->buffer + pic->size;
- tex->size = pic->size;
-
- // uploading texture into video memory
- for( i = 0; i < numSides; i++ )
- {
- if( buf != NULL && buf >= bufend )
- Host_Error( "GL_UploadTextureDXT: %s image buffer overflow\n", tex->name );
-
- width = pic->width;
- height = pic->height;
- depth = pic->depth;
-
- for( j = 0; j < numMips; j++ )
- {
- width = max( 1, ( pic->width >> j ));
- height = max( 1, ( pic->height >> j ));
- texsize = Image_DXTGetLinearSize( pic->type, width, height, depth );
- if( ImageDXT( pic->type ))
- GL_TextureImageDXT( inFormat, glTarget, i, j, width, height, depth, subImage, texsize, buf );
- else GL_TextureImage( inFormat, tex->format, glTarget, i, j, width, height, depth, subImage, texsize, buf );
-
- buf += texsize; // move pointer
+ int err;
- // catch possible errors
- if(( err = pglGetError()) != GL_NO_ERROR )
- MsgDev( D_ERROR, "GL_UploadTexture: error %x while uploading %s [%s]\n", err, tex->name, GL_Target( glTarget ));
+ ASSERT( tex != NULL );
- }
- }
+ // catch possible errors
+ if(( err = pglGetError()) != GL_NO_ERROR )
+ MsgDev( D_ERROR, "GL_UploadTexture: error %x while uploading %s [%s]\n", err, tex->name, GL_TargetToString( tex->target ));
}
/*
@@ -1241,211 +1049,192 @@ GL_UploadTexture
upload texture into video memory
===============
*/
-static void GL_UploadTexture( rgbdata_t *pic, gltexture_t *tex, qboolean subImage, imgfilter_t *filter )
+static qboolean GL_UploadTexture( gltexture_t *tex, rgbdata_t *pic )
{
byte *buf, *data;
+ size_t texsize, size;
+ uint width, height;
+ uint i, j, numSides;
+ uint offset = 0;
+ qboolean normalMap;
const byte *bufend;
- GLenum outFormat, inFormat, glTarget;
- uint i, s, numSides, offset = 0, err;
- int texsize = 0, img_flags = 0, samples;
- GLint dataType = GL_UNSIGNED_BYTE;
- ASSERT( pic != NULL && tex != NULL );
+ ASSERT( pic != NULL );
+ ASSERT( tex != NULL );
- if( pic->flags & IMAGE_DDS_FORMAT )
+ GL_SetTextureTarget( tex, pic ); // must be first
+
+ // make sure what target is correct
+ if( tex->target == GL_NONE )
{
- // special case for DDS textures
- GL_UploadTextureDXT( pic, tex, subImage, filter );
- return;
+ MsgDev( D_ERROR, "GL_UploadTexture: %s is not supported by your hardware\n", tex->name );
+ return false;
}
- tex->srcWidth = tex->width = pic->width;
- tex->srcHeight = tex->height = pic->height;
- s = tex->srcWidth * tex->srcHeight;
+ GL_SetTextureDimensions( tex, pic->width, pic->height, pic->depth );
+ GL_SetTextureFormat( tex, pic->type, pic->flags );
tex->fogParams[0] = pic->fogParams[0];
tex->fogParams[1] = pic->fogParams[1];
tex->fogParams[2] = pic->fogParams[2];
tex->fogParams[3] = pic->fogParams[3];
- // NOTE: normalmaps must be power of two or software mip generator will stop working
- GL_RoundImageDimensions( &tex->width, &tex->height, tex->flags, ( tex->flags & TF_NORMALMAP ));
-
- if( s&3 )
- {
- // will be resample, just tell me for debug targets
- MsgDev( D_NOTE, "GL_Upload: %s s&3 [%d x %d]\n", tex->name, tex->srcWidth, tex->srcHeight );
- }
-
- // copy flag about luma pixels
- if( pic->flags & IMAGE_HAS_LUMA )
- tex->flags |= TF_HAS_LUMA;
-
- // create luma texture from quake texture
- if( tex->flags & TF_MAKELUMA )
+ if(( pic->width * pic->height ) & 3 )
{
- img_flags |= IMAGE_MAKE_LUMA;
- tex->flags &= ~TF_MAKELUMA;
+ // will be resampled, just tell me for debug targets
+ MsgDev( D_NOTE, "GL_UploadTexture: %s s&3 [%d x %d]\n", tex->name, pic->width, pic->height );
}
- if( !subImage && tex->flags & TF_KEEP_8BIT )
- tex->original = FS_CopyImage( pic ); // because current pic will be expanded to rgba
-
- if( !subImage && tex->flags & TF_KEEP_RGBDATA )
- tex->original = pic; // no need to copy
-
- // we need to expand image into RGBA buffer
- if( pic->type == PF_INDEXED_24 || pic->type == PF_INDEXED_32 )
- img_flags |= IMAGE_FORCE_RGBA;
+ buf = pic->buffer;
+ bufend = pic->buffer + pic->size; // total image size include all the layers, cube sides, mipmaps
+ offset = GL_CalcImageSize( pic->type, pic->width, pic->height, pic->depth );
+ texsize = GL_CalcTextureSize( tex->format, tex->width, tex->height, tex->depth );
+ normalMap = ( tex->flags & TF_NORMALMAP ) ? true : false;
+ numSides = ( pic->flags & IMAGE_CUBEMAP ) ? 6 : 1;
- // processing image before uploading (force to rgba, make luma etc)
- if( pic->buffer ) Image_Process( &pic, 0, 0, 0.0f, img_flags, filter );
+ // uploading texture into video memory
+ pglBindTexture( tex->target, tex->texnum );
- if( tex->flags & TF_LUMINANCE )
+ for( i = 0; i < numSides; i++ )
{
- if( !( tex->flags & TF_DEPTHMAP ))
+ // track the buffer bounds
+ if( buf != NULL && buf >= bufend )
+ Host_Error( "GL_UploadTexture: %s image buffer overflow\n", tex->name );
+
+ if( ImageDXT( pic->type ))
{
- GL_MakeLuminance( pic );
- tex->flags &= ~TF_LUMINANCE;
+ for( j = 0; j < max( 1, pic->numMips ); j++ )
+ {
+ width = max( 1, ( tex->width >> j ));
+ height = max( 1, ( tex->height >> j ));
+ texsize = GL_CalcTextureSize( tex->format, width, height, tex->depth );
+ size = GL_CalcImageSize( pic->type, width, height, tex->depth );
+ GL_TextureImageDXT( tex, i, j, width, height, tex->depth, size, buf );
+ tex->size += texsize;
+ buf += size; // move pointer
+ tex->numMips++;
+
+ GL_CheckTexImageError( tex );
+ }
}
- pic->flags &= ~IMAGE_HAS_COLOR;
- }
-
- samples = GL_CalcTextureSamples( pic->flags );
-
- if( pic->flags & IMAGE_HAS_ALPHA )
- tex->flags |= TF_HAS_ALPHA;
-
- // determine format
- inFormat = PFDesc[pic->type].glFormat;
- outFormat = GL_TextureFormat( tex, &samples );
- tex->format = outFormat;
+ else if( max( 1, pic->numMips ) > 1 ) // not-compressed DDS
+ {
+ for( j = 0; j < max( 1, pic->numMips ); j++ )
+ {
+ width = max( 1, ( tex->width >> j ));
+ height = max( 1, ( tex->height >> j ));
+ texsize = GL_CalcTextureSize( tex->format, width, height, tex->depth );
+ size = GL_CalcImageSize( pic->type, width, height, tex->depth );
+ GL_TextureImageRAW( tex, i, j, width, height, tex->depth, pic->type, buf );
+ tex->size += texsize;
+ buf += size; // move pointer
+ tex->numMips++;
- // determine target
- tex->target = glTarget = GL_TEXTURE_2D;
- numSides = 1;
+ GL_CheckTexImageError( tex );
- if( tex->flags & TF_FLOATDATA )
- dataType = GL_FLOAT;
+ }
+ }
+ else // RGBA32
+ {
+ int mipCount = GL_CalcMipmapCount( tex, ( buf != NULL ));
- if( tex->flags & TF_DEPTHMAP )
- inFormat = GL_DEPTH_COMPONENT;
+ // NOTE: only single uncompressed textures can be resamples, no mips, no layers, no sides
+ if(( tex->depth == 1 ) && ( pic->width != tex->width ) || ( pic->height != tex->height ))
+ data = GL_ResampleTexture( buf, pic->width, pic->height, tex->width, tex->height, normalMap );
+ else data = buf;
- if( pic->flags & IMAGE_CUBEMAP )
- {
- if( GL_Support( GL_TEXTURECUBEMAP_EXT ))
- {
- numSides = 6;
- tex->target = glTarget = GL_TEXTURE_CUBE_MAP_ARB;
- tex->flags |= TF_CUBEMAP;
+ if( !glConfig.deviceSupportsGamma )
+ {
+ if( !ImageDXT( pic->type ) && !( tex->flags & TF_NOMIPMAP ) && !( tex->flags & TF_SKYSIDE ))
+ data = GL_ApplyGamma( data, tex->width * tex->height * tex->depth, ( tex->flags & TF_NORMALMAP ));
+ }
- if( !GL_Support( GL_ARB_SEAMLESS_CUBEMAP ) && ( tex->flags & ( TF_BORDER|TF_ALPHA_BORDER )))
+ // mips will be auto-generated if desired
+ for( j = 0; j < mipCount; j++ )
{
- // don't use border for cubemaps
- tex->flags &= ~(TF_BORDER|TF_ALPHA_BORDER);
- tex->flags |= TF_CLAMP;
+ width = max( 1, ( tex->width >> j ));
+ height = max( 1, ( tex->height >> j ));
+ texsize = GL_CalcTextureSize( tex->format, width, height, tex->depth );
+ size = GL_CalcImageSize( pic->type, width, height, tex->depth );
+ GL_TextureImageRAW( tex, i, j, width, height, tex->depth, pic->type, data );
+ if( mipCount > 1 )
+ GL_BuildMipMap( data, width, height, tex->depth, normalMap );
+ tex->size += texsize;
+ tex->numMips++;
+
+ GL_CheckTexImageError( tex );
}
+
+ // move to next side
+ if( numSides > 1 && ( buf != NULL ))
+ buf += GL_CalcImageSize( pic->type, pic->width, pic->height, 1 );
}
- else
- {
- MsgDev( D_WARN, "GL_UploadTexture: cubemaps isn't supported, %s ignored\n", tex->name );
- tex->flags &= ~TF_CUBEMAP;
- }
- }
- else if( tex->flags & TF_TEXTURE_1D )
- {
- // determine target
- tex->target = glTarget = GL_TEXTURE_1D;
- }
- else if( tex->flags & TF_TEXTURE_RECTANGLE )
- {
- if( glConfig.max_2d_rectangle_size )
- tex->target = glTarget = glConfig.texRectangle;
- // or leave as GL_TEXTURE_2D
- }
- else if( tex->flags & TF_TEXTURE_3D )
- {
- // determine target
- tex->target = glTarget = GL_TEXTURE_3D;
}
- pglBindTexture( tex->target, tex->texnum );
+ tex->flags |= TF_IMG_UPLOADED; // done
- buf = pic->buffer;
- bufend = pic->buffer + pic->size;
- offset = pic->width * pic->height * PFDesc[pic->type].bpp;
+ return true;
+}
- // NOTE: probably this code relies when gl_compressed_textures is enabled
- texsize = tex->width * tex->height * samples;
+/*
+===============
+GL_ProcessImage
- // determine some texTypes
- if( tex->flags & TF_NOPICMIP )
- tex->texType = TEX_NOMIP;
- else if( tex->flags & TF_CUBEMAP )
- tex->texType = TEX_CUBEMAP;
- else if(( tex->flags & TF_DECAL ) == TF_DECAL )
- tex->texType = TEX_DECAL;
+do specified actions on pixels
+===============
+*/
+static void GL_ProcessImage( gltexture_t *tex, rgbdata_t *pic, imgfilter_t *filter )
+{
+ uint img_flags = 0;
- // uploading texture into video memory
- for( i = 0; i < numSides; i++ )
- {
- if( buf != NULL && buf >= bufend )
- Host_Error( "GL_UploadTexture: %s image buffer overflow\n", tex->name );
+ // force upload texture as RGB or RGBA (detail textures requires this)
+ if( tex->flags & TF_FORCE_COLOR ) pic->flags |= IMAGE_HAS_COLOR;
+ if( pic->flags & IMAGE_HAS_ALPHA ) tex->flags |= TF_HAS_ALPHA;
- // copy or resample the texture
- if(( tex->width == tex->srcWidth && tex->height == tex->srcHeight ) || ( tex->flags & ( TF_TEXTURE_1D|TF_TEXTURE_3D )))
- {
- data = buf;
- }
- else
- {
- data = GL_ResampleTexture( buf, tex->srcWidth, tex->srcHeight, tex->width, tex->height, ( tex->flags & TF_NORMALMAP ));
- }
+ tex->encode = pic->encode; // share encode method
- if( !glConfig.deviceSupportsGamma )
- {
- if(!( tex->flags & TF_NOMIPMAP ) && !( tex->flags & TF_SKYSIDE ) && !( tex->flags & TF_TEXTURE_3D ))
- data = GL_ApplyGamma( data, tex->width * tex->height, ( tex->flags & TF_NORMALMAP ));
- }
+ if( ImageDXT( pic->type ))
+ {
+ if( !pic->numMips )
+ tex->flags |= TF_NOMIPMAP; // disable mipmapping by user request
- if( glTarget == GL_TEXTURE_1D )
- {
- if( subImage ) pglTexSubImage1D( tex->target, 0, 0, tex->width, inFormat, dataType, data );
- else pglTexImage1D( tex->target, 0, outFormat, tex->width, 0, inFormat, dataType, data );
- }
- else if( glTarget == GL_TEXTURE_CUBE_MAP_ARB )
- {
- if( GL_Support( GL_SGIS_MIPMAPS_EXT ) && !( tex->flags & TF_NORMALMAP ))
- GL_GenerateMipmaps( data, pic, tex, glTarget, inFormat, i, subImage );
- if( subImage ) pglTexSubImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB + i, 0, 0, 0, tex->width, tex->height, inFormat, dataType, data );
- else pglTexImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB + i, 0, outFormat, tex->width, tex->height, 0, inFormat, dataType, data );
- if( !GL_Support( GL_SGIS_MIPMAPS_EXT ) || ( tex->flags & TF_NORMALMAP ))
- GL_GenerateMipmaps( data, pic, tex, glTarget, inFormat, i, subImage );
- }
- else if( glTarget == GL_TEXTURE_3D )
- {
- if( subImage ) pglTexSubImage3D( tex->target, 0, 0, 0, 0, tex->width, tex->height, pic->depth, inFormat, dataType, data );
- else pglTexImage3D( tex->target, 0, outFormat, tex->width, tex->height, pic->depth, 0, inFormat, dataType, data );
- }
- else
+ // clear all the unsupported flags
+ tex->flags &= ~TF_KEEP_8BIT;
+ tex->flags &= ~TF_KEEP_RGBDATA;
+ tex->flags |= TF_NOPICMIP;
+ }
+ else
+ {
+ // copy flag about luma pixels
+ if( pic->flags & IMAGE_HAS_LUMA )
+ tex->flags |= TF_HAS_LUMA;
+
+ // create luma texture from quake texture
+ if( tex->flags & TF_MAKELUMA )
{
- if( GL_Support( GL_SGIS_MIPMAPS_EXT ) && !( tex->flags & ( TF_NORMALMAP|TF_ALPHACONTRAST )))
- GL_GenerateMipmaps( data, pic, tex, glTarget, inFormat, i, subImage );
- if( subImage ) pglTexSubImage2D( tex->target, 0, 0, 0, tex->width, tex->height, inFormat, dataType, data );
- else pglTexImage2D( tex->target, 0, outFormat, tex->width, tex->height, 0, inFormat, dataType, data );
- if( !GL_Support( GL_SGIS_MIPMAPS_EXT ) || ( tex->flags & ( TF_NORMALMAP|TF_ALPHACONTRAST )))
- GL_GenerateMipmaps( data, pic, tex, glTarget, inFormat, i, subImage );
+ img_flags |= IMAGE_MAKE_LUMA;
+ tex->flags &= ~TF_MAKELUMA;
}
- if( numSides > 1 && buf != NULL )
- buf += offset;
- tex->size += texsize;
+ if( !( tex->flags & TF_IMG_UPLOADED ) && ( tex->flags & ( TF_KEEP_8BIT|TF_KEEP_RGBDATA )))
+ tex->original = FS_CopyImage( pic ); // because current pic will be expanded to rgba
+
+ // we need to expand image into RGBA buffer
+ if( pic->type == PF_INDEXED_24 || pic->type == PF_INDEXED_32 )
+ img_flags |= IMAGE_FORCE_RGBA;
- // catch possible errors
- err = pglGetError();
+ // processing image before uploading (force to rgba, make luma etc)
+ if( pic->buffer ) Image_Process( &pic, 0, 0, 0.0f, img_flags, filter );
- if( err != GL_NO_ERROR )
- MsgDev( D_ERROR, "GL_UploadTexture: error %x while uploading %s [%s]\n", err, tex->name, GL_Target( glTarget ));
+ if( tex->flags & TF_LUMINANCE )
+ {
+ if( !( tex->flags & TF_DEPTHMAP ))
+ {
+ GL_MakeLuminance( pic );
+ tex->flags &= ~TF_LUMINANCE;
+ }
+ pic->flags &= ~IMAGE_HAS_COLOR;
+ }
}
}
@@ -1499,9 +1288,6 @@ int GL_LoadTexture( const char *name, const byte *buf, size_t size, int flags, i
pic = FS_LoadImage( name, buf, size );
if( !pic ) return 0; // couldn't loading image
- // force upload texture as RGB or RGBA (detail textures requires this)
- if( flags & TF_FORCE_COLOR ) pic->flags |= IMAGE_HAS_COLOR;
-
// find a free texture slot
if( r_numTextures == MAX_TEXTURES )
Host_Error( "GL_LoadTexture: MAX_TEXTURES limit exceeds\n" );
@@ -1525,11 +1311,192 @@ int GL_LoadTexture( const char *name, const byte *buf, size_t size, int flags, i
tex->texnum = tr.skyboxbasenum++;
else tex->texnum = i; // texnum is used for fast acess into r_textures array too
- GL_UploadTexture( pic, tex, false, filter );
- GL_TexFilter( tex, false ); // update texture filter, wrap etc
+ GL_ProcessImage( tex, pic, filter );
+
+ if( !GL_UploadTexture( tex, pic ))
+ {
+ memset( tex, 0, sizeof( gltexture_t ));
+ FS_FreeImage( pic ); // release source texture
+ return 0;
+ }
+
+ GL_ApplyTextureParams( tex ); // update texture filter, wrap etc
+ FS_FreeImage( pic ); // release source texture
+
+ // add to hash table
+ hash = Com_HashKey( tex->name, TEXTURES_HASH_SIZE );
+ tex->nextHash = r_texturesHashTable[hash];
+ r_texturesHashTable[hash] = tex;
+
+ // NOTE: always return texnum as index in array or engine will stop work !!!
+ return i;
+}
+
+/*
+================
+GL_LoadTextureArray
+================
+*/
+int GL_LoadTextureArray( const char **names, int flags, imgfilter_t *filter )
+{
+ gltexture_t *tex;
+ rgbdata_t *pic, *src;
+ char basename[256];
+ uint numLayers = 0;
+ uint picFlags = 0;
+ char name[256];
+ uint i, j, hash;
+
+ if( !names || !names[0] || !glw_state.initialized )
+ return 0;
+
+ // count layers (g-cont. this is pontentially unsafe loop)
+ for( i = 0; i < glConfig.max_2d_texture_layers && ( *names[i] != '\0' ); i++ )
+ numLayers++;
+ name[0] = '\0';
+
+ if( numLayers <= 0 ) return 0;
+
+ // create complexname from layer names
+ for( i = 0; i < numLayers; i++ )
+ {
+ FS_FileBase( names[i], basename );
+ Q_strncat( name, va( "%s", basename ), sizeof( name ));
+ if( i != ( numLayers - 1 )) Q_strncat( name, "|", sizeof( name ));
+ }
+
+ Q_strncat( name, va( "[%i]", numLayers ), sizeof( name ));
+
+ if( Q_strlen( name ) >= sizeof( r_textures->name ))
+ {
+ MsgDev( D_ERROR, "GL_LoadTextureArray: too long name %s\n", name );
+ return 0;
+ }
+
+ // see if already loaded
+ hash = Com_HashKey( name, TEXTURES_HASH_SIZE );
+
+ for( tex = r_texturesHashTable[hash]; tex != NULL; tex = tex->nextHash )
+ {
+ if( !Q_stricmp( tex->name, name ))
+ {
+ // prolonge registration
+ tex->cacheframe = world.load_sequence;
+ return (tex - r_textures);
+ }
+ }
+
+ // load all the images and pack it into single image
+ for( i = 0, pic = NULL; i < numLayers; i++ )
+ {
+ size_t srcsize, dstsize, mipsize;
+
+ src = FS_LoadImage( names[i], NULL, 0 );
+ if( !src ) break; // coldn't find layer
+
+ if( pic )
+ {
+ // mixed mode: DXT + RGB
+ if( pic->type != src->type )
+ {
+ MsgDev( D_ERROR, "GL_LoadTextureArray: mismatch image format for %s and %s\n", names[0], names[i] );
+ break;
+ }
+
+ // different mipcount
+ if( pic->numMips != src->numMips )
+ {
+ MsgDev( D_ERROR, "GL_LoadTextureArray: mismatch mip count for %s and %s\n", names[0], names[i] );
+ break;
+ }
+
+ if( pic->encode != src->encode )
+ {
+ MsgDev( D_ERROR, "GL_LoadTextureArray: mismatch custom encoding for %s and %s\n", names[0], names[i] );
+ break;
+ }
+
+ // but allow to rescale raw images
+ if( ImageRAW( pic->type ) && ImageRAW( src->type ) && ( pic->width != src->width || pic->height != src->height ))
+ Image_Process( &src, pic->width, pic->height, 0.0f, IMAGE_RESAMPLE, NULL );
+
+ if( pic->size != src->size )
+ {
+ MsgDev( D_ERROR, "GL_LoadTextureArray: mismatch image size for %s and %s\n", names[0], names[i] );
+ break;
+ }
+ }
+ else
+ {
+ // create new image
+ pic = Mem_Alloc( host.imagepool, sizeof( rgbdata_t ));
+ memcpy( pic, src, sizeof( rgbdata_t ));
+
+ // expand pic buffer for all layers
+ pic->buffer = Mem_Alloc( host.imagepool, pic->size * numLayers );
+ pic->depth = 0;
+ }
+
+ mipsize = srcsize = dstsize = 0;
+
+ for( j = 0; j < max( 1, pic->numMips ); j++ )
+ {
+ int width = max( 1, ( pic->width >> j ));
+ int height = max( 1, ( pic->height >> j ));
+ mipsize = GL_CalcImageSize( pic->type, width, height, 1 );
+ memcpy( pic->buffer + dstsize + mipsize * i, src->buffer + srcsize, mipsize );
+ dstsize += mipsize * numLayers;
+ srcsize += mipsize;
+ }
+
+ FS_FreeImage( src );
+
+ // increase layers
+ pic->depth++;
+ }
+
+ // there were errors
+ if( !pic || ( pic->depth != numLayers ))
+ {
+ MsgDev( D_ERROR, "GL_LoadTextureArray: not all layers were loaded. Texture array is not created\n" );
+ if( pic ) FS_FreeImage( pic );
+ return 0;
+ }
+
+ // it's multilayer image!
+ pic->flags |= IMAGE_MULTILAYER;
+ pic->size *= numLayers;
+
+ // find a free texture slot
+ if( r_numTextures == MAX_TEXTURES )
+ Host_Error( "GL_LoadTexture: MAX_TEXTURES limit exceeds\n" );
+
+ // find a free texture_t slot
+ for( i = 0, tex = r_textures; i < r_numTextures; i++, tex++ )
+ if( !tex->name[0] ) break;
+
+ if( i == r_numTextures )
+ {
+ if( r_numTextures == MAX_TEXTURES )
+ Host_Error( "GL_LoadTexture: MAX_TEXTURES limit exceeds\n" );
+ r_numTextures++;
+ }
+
+ tex = &r_textures[i];
+ Q_strncpy( tex->name, name, sizeof( tex->name ));
+ tex->flags = flags;
+ tex->texnum = i; // texnum is used for fast acess into r_textures array too
- if(!( flags & ( TF_KEEP_8BIT|TF_KEEP_RGBDATA )))
+ GL_ProcessImage( tex, pic, filter );
+ if( !GL_UploadTexture( tex, pic ))
+ {
+ memset( tex, 0, sizeof( gltexture_t ));
FS_FreeImage( pic ); // release source texture
+ return 0;
+ }
+
+ GL_ApplyTextureParams( tex ); // update texture filter, wrap etc
+ FS_FreeImage( pic ); // release source texture
// add to hash table
hash = Com_HashKey( tex->name, TEXTURES_HASH_SIZE );
@@ -1579,9 +1546,6 @@ int GL_LoadTextureInternal( const char *name, rgbdata_t *pic, texFlags_t flags,
Host_Error( "Couldn't find texture %s for update\n", name );
}
- // force upload texture as RGB or RGBA (detail textures requires this)
- if( flags & TF_FORCE_COLOR ) pic->flags |= IMAGE_HAS_COLOR;
-
// find a free texture slot
if( r_numTextures == MAX_TEXTURES )
Host_Error( "GL_LoadTexture: MAX_TEXTURES limit exceeds\n" );
@@ -1610,8 +1574,15 @@ int GL_LoadTextureInternal( const char *name, rgbdata_t *pic, texFlags_t flags,
tex->flags |= flags;
}
- GL_UploadTexture( pic, tex, update, NULL );
- GL_TexFilter( tex, update ); // update texture filter, wrap etc
+ GL_ProcessImage( tex, pic, NULL );
+ if( !GL_UploadTexture( tex, pic ))
+ {
+ memset( tex, 0, sizeof( gltexture_t ));
+ FS_FreeImage( pic ); // release source texture
+ return 0;
+ }
+
+ GL_ApplyTextureParams( tex ); // update texture filter, wrap etc
if( !update )
{
@@ -1628,7 +1599,7 @@ int GL_LoadTextureInternal( const char *name, rgbdata_t *pic, texFlags_t flags,
================
GL_CreateTexture
-creates an empty 32-bit texture (just reserve slot)
+creates texture from buffer
================
*/
int GL_CreateTexture( const char *name, int width, int height, const void *buffer, texFlags_t flags )
@@ -1654,7 +1625,7 @@ int GL_CreateTexture( const char *name, int width, int height, const void *buffe
if( !GL_Support( GL_TEXTURE_3D_EXT ))
return 0;
- r_empty.depth = r_empty.width;
+ r_empty.depth = r_empty.width; // HACKHACK
r_empty.size = r_empty.width * r_empty.height * r_empty.depth * 4;
}
else if( flags & TF_CUBEMAP )
@@ -1666,9 +1637,43 @@ int GL_CreateTexture( const char *name, int width, int height, const void *buffe
texture = GL_LoadTextureInternal( name, &r_empty, flags, false );
- if( flags & TF_DEPTHMAP )
- GL_SetTextureType( texture, TEX_DEPTHMAP );
- else GL_SetTextureType( texture, TEX_CUSTOM );
+ return texture;
+}
+
+/*
+================
+GL_CreateTextureArray
+
+creates texture array from buffer
+================
+*/
+int GL_CreateTextureArray( const char *name, int width, int height, int depth, const void *buffer, texFlags_t flags )
+{
+ rgbdata_t r_empty;
+ int texture;
+
+ memset( &r_empty, 0, sizeof( r_empty ));
+ r_empty.width = width;
+ r_empty.height = height;
+ r_empty.depth = depth;
+ r_empty.type = PF_RGBA_32;
+ r_empty.size = r_empty.width * r_empty.height * r_empty.depth * 4;
+ r_empty.flags = IMAGE_HAS_COLOR | (( flags & TF_HAS_ALPHA ) ? IMAGE_HAS_ALPHA : 0 );
+ r_empty.buffer = (byte *)buffer;
+
+ if( flags & TF_TEXTURE_3D )
+ {
+ if( !GL_Support( GL_TEXTURE_3D_EXT ))
+ return 0;
+ }
+ else
+ {
+ if( !GL_Support( GL_TEXTURE_ARRAY_EXT ))
+ return 0;
+ r_empty.flags |= IMAGE_MULTILAYER;
+ }
+
+ texture = GL_LoadTextureInternal( name, &r_empty, flags, false );
return texture;
}
@@ -1703,7 +1708,7 @@ void GL_ProcessTexture( int texnum, float gamma, int topColor, int bottomColor )
return;
}
- if(!( image->flags & (TF_KEEP_RGBDATA|TF_KEEP_8BIT)) || !image->original )
+ if( !image->original )
{
MsgDev( D_ERROR, "GL_ProcessTexture: no input data for %s\n", image->name );
return;
@@ -1719,8 +1724,8 @@ void GL_ProcessTexture( int texnum, float gamma, int topColor, int bottomColor )
pic = FS_CopyImage( image->original );
Image_Process( &pic, topColor, bottomColor, gamma, flags, NULL );
- GL_UploadTexture( pic, image, true, NULL );
- GL_TexFilter( image, true ); // update texture filter, wrap etc
+ GL_UploadTexture( image, pic );
+ GL_ApplyTextureParams( image ); // update texture filter, wrap etc
FS_FreeImage( pic );
}
@@ -1777,7 +1782,7 @@ void GL_FreeImage( const char *name )
if( Q_strlen( name ) >= sizeof( r_textures->name ))
{
- MsgDev( D_ERROR, "GL_FreeImage: too long name %s\n", name, sizeof( r_textures->name ));
+ MsgDev( D_ERROR, "GL_FreeImage: too long name %s\n", name );
return;
}
@@ -1847,7 +1852,7 @@ void R_FreeImage( gltexture_t *image )
}
// release source
- if( image->flags & (TF_KEEP_RGBDATA|TF_KEEP_8BIT) && image->original )
+ if( image->original )
FS_FreeImage( image->original );
pglDeleteTextures( 1, &image->texnum );
@@ -2272,7 +2277,7 @@ static rgbdata_t *R_InitNormalizeCubemap( texFlags_t *flags )
float s, t;
vec3_t normal;
- if( !GL_Support( GL_TEXTURECUBEMAP_EXT ))
+ if( !GL_Support( GL_TEXTURE_CUBEMAP_EXT ))
return NULL;
// normal cube map texture
@@ -2328,7 +2333,7 @@ static rgbdata_t *R_InitDlightCubemap( texFlags_t *flags )
byte *dataCM = data2D;
int dx2, dy, d;
- if( !GL_Support( GL_TEXTURECUBEMAP_EXT ))
+ if( !GL_Support( GL_TEXTURE_CUBEMAP_EXT ))
return NULL;
// normal cube map texture
@@ -2372,7 +2377,7 @@ static rgbdata_t *R_InitGrayCubemap( texFlags_t *flags )
int size = 4;
byte *dataCM = data2D;
- if( !GL_Support( GL_TEXTURECUBEMAP_EXT ))
+ if( !GL_Support( GL_TEXTURE_CUBEMAP_EXT ))
return NULL;
// gray cubemap - just stub for pointlights
@@ -2399,7 +2404,7 @@ static rgbdata_t *R_InitWhiteCubemap( texFlags_t *flags )
int size = 4;
byte *dataCM = data2D;
- if( !GL_Support( GL_TEXTURECUBEMAP_EXT ))
+ if( !GL_Support( GL_TEXTURE_CUBEMAP_EXT ))
return NULL;
// white cubemap - just stub for pointlights
@@ -2418,29 +2423,6 @@ static rgbdata_t *R_InitWhiteCubemap( texFlags_t *flags )
/*
==================
-R_InitAlphaContrast
-==================
-*/
-static rgbdata_t *R_InitAlphaContrast( texFlags_t *flags )
-{
- int size = 64;
- byte *data = data2D;
-
- *flags = (TF_NOPICMIP|TF_UNCOMPRESSED|TF_ALPHACONTRAST|TF_INTENSITY);
-
- r_image.width = r_image.height = 64;
- r_image.size = r_image.width * r_image.height * 4;
-
- memset( data, size, r_image.size );
-
- r_image.buffer = data2D;
- r_image.type = PF_RGBA_32;
-
- return &r_image;
-}
-
-/*
-==================
R_InitVSDCTCubemap
==================
*/
@@ -2488,33 +2470,32 @@ static void R_InitBuiltinTextures( void )
char *name;
int *texnum;
rgbdata_t *(*init)( texFlags_t *flags );
- int texType;
}
+
textures[] =
{
- { "*default", &tr.defaultTexture, R_InitDefaultTexture, TEX_SYSTEM },
- { "*white", &tr.whiteTexture, R_InitWhiteTexture, TEX_SYSTEM },
- { "*gray", &tr.grayTexture, R_InitGrayTexture, TEX_SYSTEM },
- { "*black", &tr.blackTexture, R_InitBlackTexture, TEX_SYSTEM },
- { "*particle", &tr.particleTexture, R_InitParticleTexture, TEX_SYSTEM },
- { "*particle2", &tr.particleTexture2, R_InitParticleTexture2, TEX_SYSTEM },
- { "*cintexture", &tr.cinTexture, R_InitCinematicTexture, TEX_NOMIP }, // force linear filter
- { "*dlight", &tr.dlightTexture, R_InitDlightTexture, TEX_LIGHTMAP },
- { "*dlight2", &tr.dlightTexture2, R_InitDlightTexture2, TEX_LIGHTMAP },
- { "*atten", &tr.attenuationTexture, R_InitAttenuationTexture, TEX_SYSTEM },
- { "*atten2", &tr.attenuationTexture2, R_InitAttenuationTexture2, TEX_SYSTEM },
- { "*atten3", &tr.attenuationTexture3, R_InitAttenuationTexture3, TEX_SYSTEM },
- { "*attnno", &tr.attenuationStubTexture, R_InitAttenuationTextureNoAtten, TEX_SYSTEM },
- { "*normalize", &tr.normalizeTexture, R_InitNormalizeCubemap, TEX_CUBEMAP },
- { "*blankbump", &tr.blankbumpTexture, R_InitBlankBumpTexture, TEX_SYSTEM },
- { "*blankdeluxe", &tr.blankdeluxeTexture, R_InitBlankDeluxeTexture, TEX_SYSTEM },
- { "*lightCube", &tr.dlightCubeTexture, R_InitDlightCubemap, TEX_CUBEMAP },
- { "*grayCube", &tr.grayCubeTexture, R_InitGrayCubemap, TEX_CUBEMAP },
- { "*whiteCube", &tr.whiteCubeTexture, R_InitWhiteCubemap, TEX_CUBEMAP },
- { "*atten3D", &tr.attenuationTexture3D, R_InitAttenTexture3D, TEX_SYSTEM },
- { "*sky", &tr.skyTexture, R_InitSkyTexture, TEX_SYSTEM },
- { "*alphaContrast", &tr.acontTexture, R_InitAlphaContrast, TEX_SYSTEM },
- { "*vsdct", &tr.vsdctCubeTexture, R_InitVSDCTCubemap, TEX_SYSTEM },
+ { "*default", &tr.defaultTexture, R_InitDefaultTexture },
+ { "*white", &tr.whiteTexture, R_InitWhiteTexture },
+ { "*gray", &tr.grayTexture, R_InitGrayTexture },
+ { "*black", &tr.blackTexture, R_InitBlackTexture },
+ { "*particle", &tr.particleTexture, R_InitParticleTexture },
+ { "*particle2", &tr.particleTexture2, R_InitParticleTexture2 },
+ { "*cintexture", &tr.cinTexture, R_InitCinematicTexture }, // force linear filter
+ { "*dlight", &tr.dlightTexture, R_InitDlightTexture },
+ { "*dlight2", &tr.dlightTexture2, R_InitDlightTexture2 },
+ { "*atten", &tr.attenuationTexture, R_InitAttenuationTexture },
+ { "*atten2", &tr.attenuationTexture2, R_InitAttenuationTexture2 },
+ { "*atten3", &tr.attenuationTexture3, R_InitAttenuationTexture3 },
+ { "*attnno", &tr.attenuationStubTexture, R_InitAttenuationTextureNoAtten },
+ { "*normalize", &tr.normalizeTexture, R_InitNormalizeCubemap },
+ { "*blankbump", &tr.blankbumpTexture, R_InitBlankBumpTexture },
+ { "*blankdeluxe", &tr.blankdeluxeTexture, R_InitBlankDeluxeTexture },
+ { "*lightCube", &tr.dlightCubeTexture, R_InitDlightCubemap },
+ { "*grayCube", &tr.grayCubeTexture, R_InitGrayCubemap },
+ { "*whiteCube", &tr.whiteCubeTexture, R_InitWhiteCubemap },
+ { "*atten3D", &tr.attenuationTexture3D, R_InitAttenTexture3D },
+ { "*sky", &tr.skyTexture, R_InitSkyTexture },
+ { "*vsdct", &tr.vsdctCubeTexture, R_InitVSDCTCubemap },
{ NULL, NULL, NULL }
};
size_t i, num_builtin_textures = sizeof( textures ) / sizeof( textures[0] ) - 1;
@@ -2527,9 +2508,206 @@ static void R_InitBuiltinTextures( void )
pic = textures[i].init( &flags );
if( pic == NULL ) continue;
*textures[i].texnum = GL_LoadTextureInternal( textures[i].name, pic, flags, false );
+ }
+}
+
+/*
+===============
+R_TextureList_f
+===============
+*/
+void R_TextureList_f( void )
+{
+ gltexture_t *image;
+ int i, texCount, bytes = 0;
+
+ Msg( "\n" );
+ Msg(" -w-- -h-- -size- -fmt- type -data-- -encode-- -wrap-- -depth- -name--------\n" );
+
+ for( i = texCount = 0, image = r_textures; i < r_numTextures; i++, image++ )
+ {
+ if( !image->texnum ) continue;
+
+ bytes += image->size;
+ texCount++;
+
+ Msg( "%4i: ", i );
+ Msg( "%4i %4i ", image->width, image->height );
+ Msg( "%5ik ", image->size >> 10 );
+
+ switch( image->format )
+ {
+ case GL_COMPRESSED_RGBA_ARB:
+ Msg( "CRGBA " );
+ break;
+ case GL_COMPRESSED_RGB_ARB:
+ Msg( "CRGB " );
+ break;
+ case GL_COMPRESSED_LUMINANCE_ALPHA_ARB:
+ Msg( "CLA " );
+ break;
+ case GL_COMPRESSED_LUMINANCE_ARB:
+ Msg( "CL " );
+ break;
+ case GL_COMPRESSED_ALPHA_ARB:
+ Msg( "CA " );
+ break;
+ case GL_COMPRESSED_INTENSITY_ARB:
+ Msg( "CI " );
+ break;
+ case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
+ Msg( "DXT1c " );
+ break;
+ case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
+ Msg( "DXT1a " );
+ break;
+ case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
+ Msg( "DXT3 " );
+ break;
+ case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
+ Msg( "DXT5 " );
+ break;
+ case GL_RGBA:
+ Msg( "RGBA " );
+ break;
+ case GL_RGBA8:
+ Msg( "RGBA8 " );
+ break;
+ case GL_RGBA4:
+ Msg( "RGBA4 " );
+ break;
+ case GL_RGB:
+ Msg( "RGB " );
+ break;
+ case GL_RGB8:
+ Msg( "RGB8 " );
+ break;
+ case GL_RGB5:
+ Msg( "RGB5 " );
+ break;
+ case GL_LUMINANCE4_ALPHA4:
+ Msg( "L4A4 " );
+ break;
+ case GL_LUMINANCE_ALPHA:
+ case GL_LUMINANCE8_ALPHA8:
+ Msg( "L8A8 " );
+ break;
+ case GL_LUMINANCE4:
+ Msg( "L4 " );
+ break;
+ case GL_LUMINANCE:
+ case GL_LUMINANCE8:
+ Msg( "L8 " );
+ break;
+ case GL_ALPHA8:
+ Msg( "A8 " );
+ break;
+ case GL_INTENSITY8:
+ Msg( "I8 " );
+ break;
+ case GL_DEPTH_COMPONENT:
+ case GL_DEPTH_COMPONENT24:
+ Msg( "DPTH24" );
+ break;
+ case GL_DEPTH_COMPONENT32F:
+ Msg( "DPTH32" );
+ break;
+ case GL_LUMINANCE16F_ARB:
+ Msg( "L16F " );
+ break;
+ case GL_LUMINANCE32F_ARB:
+ Msg( "L32F " );
+ break;
+ case GL_LUMINANCE_ALPHA16F_ARB:
+ Msg( "LA16F " );
+ break;
+ case GL_LUMINANCE_ALPHA32F_ARB:
+ Msg( "LA32F " );
+ break;
+ case GL_RGB16F_ARB:
+ Msg( "RGB16F" );
+ break;
+ case GL_RGB32F_ARB:
+ Msg( "RGB32F" );
+ break;
+ case GL_RGBA16F_ARB:
+ Msg( "RGBA16F" );
+ break;
+ case GL_RGBA32F_ARB:
+ Msg( "RGBA32F" );
+ break;
+ default:
+ Msg( "????? " );
+ break;
+ }
+
+ switch( image->target )
+ {
+ case GL_TEXTURE_1D:
+ Msg( " 1D " );
+ break;
+ case GL_TEXTURE_2D:
+ Msg( " 2D " );
+ break;
+ case GL_TEXTURE_3D:
+ Msg( " 3D " );
+ break;
+ case GL_TEXTURE_CUBE_MAP_ARB:
+ Msg( "CUBE " );
+ break;
+ case GL_TEXTURE_RECTANGLE_EXT:
+ Msg( "RECT " );
+ break;
+ case GL_TEXTURE_2D_ARRAY_EXT:
+ Msg( "ARRAY " );
+ break;
+ default:
+ Msg( "???? " );
+ break;
+ }
+
+ if( image->flags & TF_NORMALMAP )
+ Msg( "normal " );
+ else Msg( "diffuse " );
+
+ switch( image->encode )
+ {
+ case DXT_ENCODE_COLOR_YCoCg:
+ Msg( "YCoCg " );
+ break;
+ case DXT_ENCODE_NORMAL_AG_ORTHO:
+ Msg( "ortho " );
+ break;
+ case DXT_ENCODE_NORMAL_AG_STEREO:
+ Msg( "stereo " );
+ break;
+ case DXT_ENCODE_NORMAL_AG_PARABOLOID:
+ Msg( "parabolic " );
+ break;
+ case DXT_ENCODE_NORMAL_AG_QUARTIC:
+ Msg( "quartic " );
+ break;
+ case DXT_ENCODE_NORMAL_AG_AZIMUTHAL:
+ Msg( "azimuthal " );
+ break;
+ default:
+ Msg( "default " );
+ break;
+ }
- GL_SetTextureType( *textures[i].texnum, textures[i].texType );
+ if( image->flags & TF_CLAMP )
+ Msg( "clamp " );
+ else if( image->flags & TF_BORDER )
+ Msg( "border " );
+ else Msg( "repeat " );
+ Msg( " %d ", image->depth );
+ Msg( " %s\n", image->name );
}
+
+ Msg( "---------------------------------------------------------\n" );
+ Msg( "%i total textures\n", texCount );
+ Msg( "%s total memory used\n", Q_memprint( bytes ));
+ Msg( "\n" );
}
/*
@@ -2542,10 +2720,9 @@ void R_InitImages( void )
uint i, hash;
float f;
- r_numTextures = 0;
- scaledImage = NULL;
memset( r_textures, 0, sizeof( r_textures ));
memset( r_texturesHashTable, 0, sizeof( r_texturesHashTable ));
+ r_numTextures = 0;
// create unused 0-entry
Q_strncpy( r_textures->name, "*unused*", sizeof( r_textures->name ));
@@ -2568,6 +2745,7 @@ void R_InitImages( void )
R_InitBuiltinTextures();
R_ParseTexFilters( "scripts/texfilter.txt" );
+ Cmd_AddCommand( "texturelist", R_TextureList_f, "display loaded textures list" );
}
/*
@@ -2582,23 +2760,11 @@ void R_ShutdownImages( void )
if( !glw_state.initialized ) return;
- for( i = ( MAX_TEXTURE_UNITS - 1); i >= 0; i-- )
- {
- if( i >= GL_MaxTextureUnits( ))
- continue;
-
- GL_SelectTexture( i );
- pglBindTexture( GL_TEXTURE_2D, 0 );
-
- if( GL_Support( GL_TEXTURECUBEMAP_EXT ))
- pglBindTexture( GL_TEXTURE_CUBE_MAP_ARB, 0 );
- }
+ Cmd_RemoveCommand( "texturelist" );
+ GL_CleanupAllTextureUnits();
for( i = 0, image = r_textures; i < r_numTextures; i++, image++ )
- {
- if( !image->texnum ) continue;
- GL_FreeTexture( i );
- }
+ R_FreeImage( image );
memset( tr.lightmapTextures, 0, sizeof( tr.lightmapTextures ));
memset( r_texturesHashTable, 0, sizeof( r_texturesHashTable ));
diff --git b/engine/client/gl_local.h a/engine/client/gl_local.h
index 1a9deb1..314c199 100644
--- b/engine/client/gl_local.h
+++ a/engine/client/gl_local.h
@@ -48,7 +48,7 @@ extern byte *r_temppool;
#define RP_FLIPFRONTFACE BIT( 4 ) // e.g. for mirrors drawing
#define RP_NONVIEWERREF (RP_MIRRORVIEW|RP_ENVVIEW)
-#define R_StudioOpaque( e ) ( e->curstate.rendermode == kRenderNormal || e->curstate.rendermode == kRenderTransAlpha )
+#define R_StudioOpaque( rm ) ( rm == kRenderNormal || rm == kRenderTransAlpha )
#define RP_LOCALCLIENT( e ) (CL_GetLocalPlayer() && ((e)->index == CL_GetLocalPlayer()->index && e->curstate.entityType == ET_PLAYER ))
#define RP_NORMALPASS() ((RI.params & RP_NONVIEWERREF) == 0 )
@@ -64,6 +64,8 @@ typedef struct gltexture_s
word srcHeight;
word width; // upload width\height
word height;
+ word depth; // texture depth or count of layers for 2D_ARRAY
+ byte numMips; // mipmap count
uint cacheframe; // worldmodel->load_sequence
@@ -78,7 +80,6 @@ typedef struct gltexture_s
rgbdata_t *original; // keep original image
// debug info
- byte texType; // used for gl_showtextures
size_t size; // upload size for debug targets
// detail textures stuff
@@ -113,6 +114,8 @@ typedef struct
int viewport[4];
mplane_t frustum[6];
+ mleaf_t *viewleaf;
+ mleaf_t *oldviewleaf;
vec3_t pvsorigin;
vec3_t vieworg; // locked vieworigin
vec3_t vforward;
@@ -135,9 +138,6 @@ typedef struct
float fogEnd;
int cached_contents; // in water
- float waveHeight; // global waveHeight
- float currentWaveHeight; // current entity waveHeight
-
float skyMins[2][6];
float skyMaxs[2][6];
@@ -147,8 +147,7 @@ typedef struct
matrix4x4 projectionMatrix;
matrix4x4 worldviewProjectionMatrix; // worldviewMatrix * projectionMatrix
- int lightstylevalue[MAX_LIGHTSTYLES]; // value 0 - 65536
- float lightcache[MAX_LIGHTSTYLES];
+ byte visbytes[(MAX_MAP_LEAFS+7)/8];// actual PVS for current frame
float viewplanedist;
mplane_t clipPlane;
@@ -161,7 +160,6 @@ typedef struct
int whiteTexture;
int grayTexture;
int blackTexture;
- int acontTexture;
int defaultTexture; // use for bad textures
int particleTexture; // particle texture
int particleTexture2; // unsmoothed particle texture
@@ -211,6 +209,9 @@ typedef struct
int realframecount; // not including passes
int framecount;
+ int lightstylevalue[MAX_LIGHTSTYLES]; // value 0 - 65536
+ float lightcache[MAX_LIGHTSTYLES];
+
// cull info
vec3_t modelorg; // relative to viewpoint
} ref_globals_t;
@@ -265,6 +266,7 @@ void GL_LoadTexMatrixExt( const float *glmatrix );
void GL_LoadMatrix( const matrix4x4 source );
void GL_TexGen( GLenum coord, GLenum mode );
void GL_SelectTexture( GLint texture );
+void GL_CleanupAllTextureUnits( void );
void GL_LoadIdentityTexMatrix( void );
void GL_DisableAllTexGens( void );
void GL_SetRenderMode( int mode );
@@ -302,13 +304,14 @@ void R_UploadStretchRaw( int texture, int cols, int rows, int width, int height,
//
void R_SetTextureParameters( void );
gltexture_t *R_GetTexture( GLenum texnum );
-void GL_SetTextureType( GLenum texnum, GLenum type );
int GL_LoadTexture( const char *name, const byte *buf, size_t size, int flags, imgfilter_t *filter );
+int GL_LoadTextureArray( const char **names, int flags, imgfilter_t *filter );
int GL_LoadTextureInternal( const char *name, rgbdata_t *pic, texFlags_t flags, qboolean update );
byte *GL_ResampleTexture( const byte *source, int in_w, int in_h, int out_w, int out_h, qboolean isNormalMap );
int GL_CreateTexture( const char *name, int width, int height, const void *buffer, texFlags_t flags );
+int GL_CreateTextureArray( const char *name, int width, int height, int depth, const void *buffer, texFlags_t flags );
void GL_ProcessTexture( int texnum, float gamma, int topColor, int bottomColor );
-void GL_TexFilter( gltexture_t *tex, qboolean update );
+void GL_ApplyTextureParams( gltexture_t *tex );
void R_FreeImage( gltexture_t *image );
int GL_FindTexture( const char *name );
void GL_FreeTexture( GLenum texnum );
@@ -422,16 +425,15 @@ void R_InitSky( struct mip_s *mt, struct texture_s *tx );
void R_AddSkyBoxSurface( msurface_t *fa );
void R_ClearSkyBox( void );
void R_DrawSkyBox( void );
-void EmitSkyLayers( msurface_t *fa );
-void EmitSkyPolys( msurface_t *fa );
-void EmitWaterPolys( glpoly_t *polys, qboolean noCull );
-void R_DrawSkyChain( msurface_t *s );
+void R_DrawClouds( void );
+void EmitWaterPolys( glpoly_t *polys, qboolean noCull, qboolean direction );
//
// gl_vidnt.c
//
#define GL_CheckForErrors() GL_CheckForErrors_( __FILE__, __LINE__ )
void GL_CheckForErrors_( const char *filename, const int fileline );
+void *GL_GetProcAddress( const char *name );
void GL_UpdateSwapInterval( void );
void GL_UpdateGammaRamp( void );
qboolean GL_DeleteContext( void );
@@ -492,40 +494,36 @@ void R_NewMap( void );
enum
{
GL_OPENGL_110 = 0, // base
+ GL_WGL_EXTENSIONS,
GL_WGL_SWAPCONTROL,
GL_WGL_PROCADDRESS,
- GL_HARDWARE_GAMMA_CONTROL,
GL_ARB_VERTEX_BUFFER_OBJECT_EXT,
- GL_ENV_COMBINE_EXT,
+ GL_ARB_VERTEX_ARRAY_OBJECT_EXT,
GL_ARB_MULTITEXTURE,
- GL_TEXTURECUBEMAP_EXT,
- GL_DOT3_ARB_EXT,
+ GL_TEXTURE_CUBEMAP_EXT,
GL_ANISOTROPY_EXT,
- GL_TEXTURE_LODBIAS,
+ GL_TEXTURE_LOD_BIAS,
GL_OCCLUSION_QUERIES_EXT,
GL_TEXTURE_COMPRESSION_EXT,
GL_SHADER_GLSL100_EXT,
- GL_SGIS_MIPMAPS_EXT,
GL_DRAW_RANGEELEMENTS_EXT,
- GL_LOCKARRAYS_EXT,
+ GL_TEXTURE_2D_RECT_EXT,
+ GL_TEXTURE_ARRAY_EXT,
GL_TEXTURE_3D_EXT,
GL_CLAMPTOEDGE_EXT,
- GL_BLEND_MINMAX_EXT,
- GL_STENCILTWOSIDE_EXT,
- GL_BLEND_SUBTRACT_EXT,
GL_SHADER_OBJECTS_EXT,
- GL_VERTEX_SHADER_EXT, // glsl vertex program
- GL_FRAGMENT_SHADER_EXT, // glsl fragment program
- GL_EXT_POINTPARAMETERS,
- GL_SEPARATESTENCIL_EXT,
GL_ARB_TEXTURE_NPOT_EXT,
- GL_CUSTOM_VERTEX_ARRAY_EXT,
- GL_TEXTURE_ENV_ADD_EXT,
GL_CLAMP_TEXBORDER_EXT,
GL_ARB_TEXTURE_FLOAT_EXT,
+ GL_ARB_HALF_FLOAT_EXT,
GL_ARB_DEPTH_FLOAT_EXT,
GL_ARB_SEAMLESS_CUBEMAP,
+ GL_FRAMEBUFFER_OBJECT,
+ GL_DRAW_BUFFERS_EXT,
+ GL_EXT_GPU_SHADER4, // shaders only
+ GL_ARB_TEXTURE_RG,
GL_DEPTH_TEXTURE,
+ GL_DEBUG_OUTPUT,
GL_SHADOW_EXT,
GL_EXTCOUNT, // must be last
};
@@ -540,14 +538,24 @@ enum
MAX_TEXTURE_UNITS = 32 // can't acess to all over units without GLSL or cg
};
+typedef enum
+{
+ GLHW_GENERIC, // where everthing works the way it should
+ GLHW_RADEON, // where you don't have proper GLSL support
+ GLHW_NVIDIA // Geforce 8/9 class DX10 hardware
+} glHWType_t;
+
typedef struct
{
const char *renderer_string; // ptrs to OpenGL32.dll, use with caution
const char *vendor_string;
const char *version_string;
+ glHWType_t hardware_type;
+
// list of supported extensions
const char *extensions_string;
+ const char *wgl_extensions_string;
byte extension[GL_EXTCOUNT];
int max_texture_units;
@@ -555,12 +563,13 @@ typedef struct
int max_teximage_units;
GLint max_2d_texture_size;
GLint max_2d_rectangle_size;
+ GLint max_2d_texture_layers;
GLint max_3d_texture_size;
GLint max_cubemap_size;
- GLint texRectangle;
+ GLint max_draw_buffers;
GLfloat max_texture_anisotropy;
- GLfloat max_texture_lodbias;
+ GLfloat max_texture_lod_bias;
GLint max_vertex_uniforms;
GLint max_vertex_attribs;
@@ -570,6 +579,9 @@ typedef struct
int depth_bits;
int stencil_bits;
+ gl_context_type_t context;
+ gles_wrapper_t wrapper;
+
qboolean softwareGammaUpdate;
qboolean deviceSupportsGamma;
int prev_mode;
@@ -608,8 +620,8 @@ typedef struct
int desktopWidth;
int desktopHeight;
- qboolean software; // OpenGL software emulation
qboolean initialized; // OpenGL subsystem started
+ qboolean extended; // extended context allows to GL_Debug
} glwstate_t;
extern glconfig_t glConfig;
@@ -619,7 +631,6 @@ extern glwstate_t glw_state;
//
// renderer cvars
//
-extern convar_t *gl_allow_software;
extern convar_t *gl_texture_anisotropy;
extern convar_t *gl_extensions;
extern convar_t *gl_stencilbits;
@@ -627,11 +638,9 @@ extern convar_t *gl_ignorehwgamma;
extern convar_t *gl_swapInterval;
extern convar_t *gl_check_errors;
extern convar_t *gl_round_down;
-extern convar_t *gl_texturemode;
extern convar_t *gl_texture_lodbias;
-extern convar_t *gl_showtextures;
+extern convar_t *gl_texture_nearest;
extern convar_t *gl_compress_textures;
-extern convar_t *gl_luminance_textures;
extern convar_t *gl_compensate_gamma_screenshots;
extern convar_t *gl_keeptjunctions;
extern convar_t *gl_detailscale;
@@ -672,7 +681,6 @@ extern convar_t *r_fastsky;
extern convar_t *vid_displayfrequency;
extern convar_t *vid_fullscreen;
extern convar_t *vid_gamma;
-extern convar_t *vid_texgamma;
extern convar_t *vid_mode;
#endif//GL_LOCAL_H
\ No newline at end of file
diff --git b/engine/client/gl_mirror.c a/engine/client/gl_mirror.c
index 797ed0c..72983dc 100644
--- b/engine/client/gl_mirror.c
+++ a/engine/client/gl_mirror.c
@@ -160,7 +160,6 @@ int R_AllocateMirrorTexture( void )
r_screen.flags = IMAGE_HAS_COLOR;
r_screen.buffer = NULL; // create empty texture for now
tr.mirrorTextures[i] = GL_LoadTextureInternal( txName, &r_screen, TF_IMAGE, false );
- GL_SetTextureType( tr.mirrorTextures[i], TEX_SCREENCOPY );
texture = tr.mirrorTextures[i];
}
@@ -291,10 +290,6 @@ void R_DrawMirrors( void )
VectorCopy( origin, RI.pvsorigin );
- // combine two leafs from client and mirror views
- r_viewleaf = Mod_PointInLeaf( oldRI.pvsorigin, cl.worldmodel->nodes );
- r_viewleaf2 = Mod_PointInLeaf( RI.pvsorigin, cl.worldmodel->nodes );
-
if( GL_Support( GL_ARB_TEXTURE_NPOT_EXT ))
{
// allow screen size
@@ -341,7 +336,6 @@ void R_DrawMirrors( void )
tr.mirror_entities[i].ent = NULL;
}
- r_oldviewleaf = r_viewleaf = NULL; // force markleafs next frame
tr.framecount = oldframecount; // restore real framecount
tr.num_mirror_entities = 0;
tr.num_mirrors_used = 0;
diff --git b/engine/client/gl_rlight.c a/engine/client/gl_rlight.c
index 9e7a28a..19048eb 100644
--- b/engine/client/gl_rlight.c
+++ a/engine/client/gl_rlight.c
@@ -29,11 +29,11 @@ DYNAMIC LIGHTS
*/
/*
==================
-R_AnimateLight
+CL_RunLightStyles
==================
*/
-void R_AnimateLight( void )
+void CL_RunLightStyles( void )
{
int i, k, flight, clight;
float l, c, lerpfrac, backlerp;
@@ -51,36 +51,36 @@ void R_AnimateLight( void )
{
if( r_fullbright->integer || !cl.worldmodel->lightdata )
{
- RI.lightstylevalue[i] = 256 * 256;
- RI.lightcache[i] = 3.0f;
+ tr.lightstylevalue[i] = 256 * 256;
+ tr.lightcache[i] = 3.0f;
continue;
}
if( !RI.refdef.paused && RI.refdef.frametime <= 0.1f )
ls->time += RI.refdef.frametime; // evaluate local time
- flight = (int)floor( ls->time * 10 );
- clight = (int)ceil( ls->time * 10 );
+ flight = (int)Q_floor( ls->time * 10 );
+ clight = (int)Q_ceil( ls->time * 10 );
lerpfrac = ( ls->time * 10 ) - flight;
backlerp = 1.0f - lerpfrac;
if( !ls->length )
{
- RI.lightstylevalue[i] = 256 * scale;
- RI.lightcache[i] = 3.0f * scale;
+ tr.lightstylevalue[i] = 256 * scale;
+ tr.lightcache[i] = 3.0f * scale;
continue;
}
else if( ls->length == 1 )
{
// single length style so don't bother interpolating
- RI.lightstylevalue[i] = ls->map[0] * 22 * scale;
- RI.lightcache[i] = ( ls->map[0] / 12.0f ) * 3.0f * scale;
+ tr.lightstylevalue[i] = ls->map[0] * 22 * scale;
+ tr.lightcache[i] = ( ls->map[0] / 12.0f ) * 3.0f * scale;
continue;
}
else if( !ls->interp || !cl_lightstyle_lerping->integer )
{
- RI.lightstylevalue[i] = ls->map[flight%ls->length] * 22 * scale;
- RI.lightcache[i] = ( ls->map[flight%ls->length] / 12.0f ) * 3.0f * scale;
+ tr.lightstylevalue[i] = ls->map[flight%ls->length] * 22 * scale;
+ tr.lightcache[i] = ( ls->map[flight%ls->length] / 12.0f ) * 3.0f * scale;
continue;
}
@@ -95,8 +95,8 @@ void R_AnimateLight( void )
l += (float)( k * 22.0f ) * lerpfrac;
c += (float)( k / 12.0f ) * lerpfrac;
- RI.lightstylevalue[i] = (int)l * scale;
- RI.lightcache[i] = c * 3.0f * scale;
+ tr.lightstylevalue[i] = (int)l * scale;
+ tr.lightcache[i] = c * 3.0f * scale;
}
}
@@ -237,6 +237,7 @@ static qboolean R_RecursiveLightPoint( model_t *model, mnode_t *node, const vec3
{
float front, back, frac;
int i, map, side, size, s, t;
+ int sample_size;
msurface_t *surf;
mtexinfo_t *tex;
color24 *lm;
@@ -269,6 +270,7 @@ static qboolean R_RecursiveLightPoint( model_t *model, mnode_t *node, const vec3
// check for impact on this node
surf = model->surfaces + node->firstsurface;
+ sample_size = Mod_SampleSizeForFace( surf );
for( i = 0; i < node->numsurfaces; i++, surf++ )
{
@@ -283,20 +285,20 @@ static qboolean R_RecursiveLightPoint( model_t *model, mnode_t *node, const vec3
if(( s < 0 || s > surf->extents[0] ) || ( t < 0 || t > surf->extents[1] ))
continue;
- s /= LM_SAMPLE_SIZE;
- t /= LM_SAMPLE_SIZE;
+ s /= sample_size;
+ t /= sample_size;
if( !surf->samples )
return true;
VectorClear( r_pointColor );
- lm = surf->samples + (t * ((surf->extents[0] / LM_SAMPLE_SIZE) + 1) + s);
- size = ((surf->extents[0] / LM_SAMPLE_SIZE) + 1) * ((surf->extents[1] / LM_SAMPLE_SIZE) + 1);
+ lm = surf->samples + (t * ((surf->extents[0] / sample_size) + 1) + s);
+ size = ((surf->extents[0] / sample_size) + 1) * ((surf->extents[1] / sample_size) + 1);
for( map = 0; map < MAXLIGHTMAPS && surf->styles[map] != 255; map++ )
{
- uint scale = RI.lightstylevalue[surf->styles[map]];
+ uint scale = tr.lightstylevalue[surf->styles[map]];
r_pointColor[0] += TextureToTexGamma( lm->r ) * scale;
r_pointColor[1] += TextureToTexGamma( lm->g ) * scale;
diff --git b/engine/client/gl_rmain.c a/engine/client/gl_rmain.c
index 8ac980b..6b5592e 100644
--- b/engine/client/gl_rmain.c
+++ a/engine/client/gl_rmain.c
@@ -30,9 +30,6 @@ float gldepthmin, gldepthmax;
ref_params_t r_lastRefdef;
ref_instance_t RI, prevRI;
-mleaf_t *r_viewleaf, *r_oldviewleaf;
-mleaf_t *r_viewleaf2, *r_oldviewleaf2;
-
static int R_RankForRenderMode( cl_entity_t *ent )
{
switch( ent->curstate.rendermode )
@@ -370,16 +367,16 @@ int R_ComputeFxBlend( cl_entity_t *e )
break;
}
- if( e->model->type != mod_brush )
+ if( e->model && e->model->type != mod_brush )
{
// NOTE: never pass sprites with rendercolor '0 0 0' it's a stupid Valve Hammer Editor bug
if( !e->curstate.rendercolor.r && !e->curstate.rendercolor.g && !e->curstate.rendercolor.b )
e->curstate.rendercolor.r = e->curstate.rendercolor.g = e->curstate.rendercolor.b = 255;
- }
- // apply scale to studiomodels and sprites only
- if( e->model && e->model->type != mod_brush && !e->curstate.scale )
- e->curstate.scale = 1.0f;
+ // apply scale to studiomodels and sprites only
+ if( !e->curstate.scale )
+ e->curstate.scale = 1.0f;
+ }
blend = bound( 0, blend, 255 );
@@ -745,35 +742,8 @@ R_FindViewLeaf
*/
void R_FindViewLeaf( void )
{
- float height;
- mleaf_t *leaf;
- vec3_t tmp;
-
- r_oldviewleaf = r_viewleaf;
- r_oldviewleaf2 = r_viewleaf2;
- leaf = Mod_PointInLeaf( RI.pvsorigin, cl.worldmodel->nodes );
- r_viewleaf2 = r_viewleaf = leaf;
- height = RI.waveHeight ? RI.waveHeight : 16;
-
- // check above and below so crossing solid water doesn't draw wrong
- if( leaf->contents == CONTENTS_EMPTY )
- {
- // look down a bit
- VectorCopy( RI.pvsorigin, tmp );
- tmp[2] -= height;
- leaf = Mod_PointInLeaf( tmp, cl.worldmodel->nodes );
- if(( leaf->contents != CONTENTS_SOLID ) && ( leaf != r_viewleaf2 ))
- r_viewleaf2 = leaf;
- }
- else
- {
- // look up a bit
- VectorCopy( RI.pvsorigin, tmp );
- tmp[2] += height;
- leaf = Mod_PointInLeaf( tmp, cl.worldmodel->nodes );
- if(( leaf->contents != CONTENTS_SOLID ) && ( leaf != r_viewleaf2 ))
- r_viewleaf2 = leaf;
- }
+ RI.oldviewleaf = RI.viewleaf;
+ RI.viewleaf = Mod_PointInLeaf( RI.pvsorigin, cl.worldmodel->nodes );
}
/*
@@ -805,7 +775,6 @@ static void R_SetupFrame( void )
VectorCopy( RI.vup, RI.cull_vup );
}
- R_AnimateLight();
R_RunViewmodelEvents();
// sort opaque entities by model type to avoid drawing model shadows under alpha-surfaces
@@ -820,11 +789,8 @@ static void R_SetupFrame( void )
// current viewleaf
if( RI.drawWorld )
{
- RI.waveHeight = cl.refdef.movevars->waveHeight * 2.0f; // set global waveheight
RI.isSkyVisible = false; // unknown at this moment
-
- if(!( RI.params & RP_OLDVIEWLEAF ))
- R_FindViewLeaf();
+ R_FindViewLeaf();
}
}
@@ -999,13 +965,13 @@ static void R_CheckFog( void )
RI.fogEnabled = false;
- if( RI.refdef.waterlevel < 2 || !RI.drawWorld || !r_viewleaf )
+ if( RI.refdef.waterlevel < 2 || !RI.drawWorld || !RI.viewleaf )
return;
ent = CL_GetWaterEntity( RI.vieworg );
if( ent && ent->model && ent->model->type == mod_brush && ent->curstate.skin < 0 )
cnt = ent->curstate.skin;
- else cnt = r_viewleaf->contents;
+ else cnt = RI.viewleaf->contents;
if( IsLiquidContents( RI.cached_contents ) && !IsLiquidContents( cnt ))
{
@@ -1038,8 +1004,8 @@ static void R_CheckFog( void )
}
else
{
- tex = R_RecursiveFindWaterTexture( r_viewleaf->parent, NULL, false );
- if( tex ) RI.cached_contents = r_viewleaf->contents;
+ tex = R_RecursiveFindWaterTexture( RI.viewleaf->parent, NULL, false );
+ if( tex ) RI.cached_contents = RI.viewleaf->contents;
}
if( !tex ) return; // no valid fogs
@@ -1167,7 +1133,10 @@ void R_DrawEntitiesOnList( void )
}
if( RI.drawWorld )
+ {
+ pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
clgame.dllFuncs.pfnDrawTransparentTriangles ();
+ }
if( !RI.refdef.onlyClientDraw )
{
@@ -1185,7 +1154,8 @@ void R_DrawEntitiesOnList( void )
R_DrawViewModel();
- CL_ExtraUpdate();
+ if( !FBitSet( host.features, ENGINE_FIXED_FRAMERATE ))
+ CL_ExtraUpdate();
}
/*
@@ -1213,7 +1183,8 @@ void R_RenderScene( const ref_params_t *fd )
R_CheckFog();
R_DrawWorld();
- CL_ExtraUpdate (); // don't let sound get messed up if going slow
+ if( !FBitSet( host.features, ENGINE_FIXED_FRAMERATE ))
+ CL_ExtraUpdate (); // don't let sound get messed up if going slow
R_DrawEntitiesOnList();
@@ -1248,7 +1219,7 @@ void R_BeginFrame( qboolean clearScene )
else
{
glConfig.softwareGammaUpdate = true;
- BuildGammaTable( vid_gamma->value, vid_texgamma->value );
+ BuildGammaTable( vid_gamma->value, GAMMA );
GL_RebuildLightmaps();
glConfig.softwareGammaUpdate = false;
}
@@ -1259,15 +1230,15 @@ void R_BeginFrame( qboolean clearScene )
// draw buffer stuff
pglDrawBuffer( GL_BACK );
- // texturemode stuff
// update texture parameters
- if( gl_texturemode->modified || gl_texture_anisotropy->modified || gl_texture_lodbias->modified )
+ if( gl_texture_nearest->modified || gl_texture_anisotropy->modified || gl_texture_lodbias->modified )
R_SetTextureParameters();
// swapinterval stuff
GL_UpdateSwapInterval();
- CL_ExtraUpdate ();
+ if( !FBitSet( host.features, ENGINE_FIXED_FRAMERATE ))
+ CL_ExtraUpdate ();
}
/*
@@ -1343,7 +1314,10 @@ void R_EndFrame( void )
R_Set2DMode( false );
if( !pwglSwapBuffers( glw_state.hDC ))
- Sys_Error( "wglSwapBuffers() failed!\n" );
+ {
+ Msg( "Error: WGL: failed to swap buffers\n" );
+ Host_NewInstance( va("#%s", GI->gamefolder ), "stopped" );
+ }
}
/*
@@ -1382,7 +1356,7 @@ void R_DrawCubemapView( const vec3_t origin, const vec3_t angles, int size )
R_RenderScene( fd );
- r_oldviewleaf = r_viewleaf = NULL; // force markleafs next frame
+ RI.oldviewleaf = RI.viewleaf = NULL; // force markleafs next frame
}
static int GL_RenderGetParm( int parm, int arg )
@@ -1409,6 +1383,12 @@ static int GL_RenderGetParm( int parm, int arg )
case PARM_TEX_ENCODE:
glt = R_GetTexture( arg );
return glt->encode;
+ case PARM_TEX_MIPCOUNT:
+ glt = R_GetTexture( arg );
+ return glt->numMips;
+ case PARM_TEX_DEPTH:
+ glt = R_GetTexture( arg );
+ return glt->depth;
case PARM_TEX_SKYBOX:
ASSERT( arg >= 0 && arg < 6 );
return tr.skyboxTextures[arg];
@@ -1418,7 +1398,7 @@ static int GL_RenderGetParm( int parm, int arg )
ASSERT( arg >= 0 && arg < MAX_LIGHTMAPS );
return tr.lightmapTextures[arg];
case PARM_SKY_SPHERE:
- return world.sky_sphere;
+ return world.sky_sphere && !world.custom_skybox;
case PARM_WORLD_VERSION:
if( cls.state != ca_active )
return bmodel_version;
@@ -1455,9 +1435,6 @@ static int GL_RenderGetParm( int parm, int arg )
return glt->cacheframe;
case PARM_MAP_HAS_DELUXE:
return (world.deluxedata != NULL);
- case PARM_TEX_TYPE:
- glt = R_GetTexture( arg );
- return glt->texType;
case PARM_CACHEFRAME:
return world.load_sequence;
case PARM_MAX_IMAGE_UNITS:
@@ -1466,6 +1443,16 @@ static int GL_RenderGetParm( int parm, int arg )
return (cls.state == ca_active);
case PARM_REBUILD_GAMMA:
return glConfig.softwareGammaUpdate;
+ case PARM_DEDICATED_SERVER:
+ return (host.type == HOST_DEDICATED);
+ case PARM_SURF_SAMPLESIZE:
+ if( arg >= 0 && arg < cl.worldmodel->numsurfaces )
+ return Mod_SampleSizeForFace( &cl.worldmodel->surfaces[arg] );
+ return LM_SAMPLE_SIZE;
+ case PARM_GL_CONTEXT_TYPE:
+ return glConfig.context;
+ case PARM_GLES_WRAPPER:
+ return glConfig.wrapper;
}
return 0;
}
@@ -1604,6 +1591,11 @@ static int GL_LoadTextureNoFilter( const char *name, const byte *buf, size_t siz
return GL_LoadTexture( name, buf, size, flags, NULL );
}
+static int GL_LoadTextureArrayNoFilter( const char **names, int flags )
+{
+ return GL_LoadTextureArray( names, flags, NULL );
+}
+
static const ref_overview_t *GL_GetOverviewParms( void )
{
return &clgame.overView;
@@ -1616,6 +1608,7 @@ static void *R_Mem_Alloc( size_t cb, const char *filename, const int fileline )
static void R_Mem_Free( void *mem, const char *filename, const int fileline )
{
+ if( !mem ) return;
_Mem_Free( mem, filename, fileline );
}
@@ -1662,8 +1655,8 @@ static render_api_t gRenderAPI =
GL_TextureData,
GL_LoadTextureNoFilter,
GL_CreateTexture,
- GL_SetTextureType,
- GL_TextureUpdateCache,
+ GL_LoadTextureArrayNoFilter,
+ GL_CreateTextureArray,
GL_FreeTexture,
DrawSingleDecal,
R_DecalSetupVerts,
@@ -1683,7 +1676,7 @@ static render_api_t gRenderAPI =
GL_TexGen,
GL_TextureTarget,
GL_SetTexCoordArrayMode,
- NULL,
+ GL_GetProcAddress,
NULL,
NULL,
NULL,
@@ -1718,7 +1711,7 @@ qboolean R_InitRenderAPI( void )
{
if( clgame.dllFuncs.pfnGetRenderInterface( CL_RENDER_INTERFACE_VERSION, &gRenderAPI, &clgame.drawFuncs ))
{
- MsgDev( D_AICONSOLE, "CL_LoadProgs: ^2initailized extended RenderAPI ^7ver. %i\n", CL_RENDER_INTERFACE_VERSION );
+ MsgDev( D_REPORT, "CL_LoadProgs: ^2initailized extended RenderAPI ^7ver. %i\n", CL_RENDER_INTERFACE_VERSION );
return true;
}
diff --git b/engine/client/gl_rmisc.c a/engine/client/gl_rmisc.c
index a1160b5..bf8624c 100644
--- b/engine/client/gl_rmisc.c
+++ a/engine/client/gl_rmisc.c
@@ -291,7 +291,6 @@ void R_ParseDetailTextures( const char *filename )
{
gltexture_t *glt;
- GL_SetTextureType( tex->dt_texturenum, TEX_DETAIL );
glt = R_GetTexture( tex->gl_texturenum );
glt->xscale = xScale;
glt->yscale = yScale;
@@ -460,7 +459,6 @@ void R_NewMap( void )
cl.worldmodel->leafs[i+1].efrags = NULL;
tr.skytexturenum = -1;
- r_viewleaf = r_oldviewleaf = NULL;
// clearing texture chains
for( i = 0; i < cl.worldmodel->numtextures; i++ )
diff --git b/engine/client/gl_rpart.c a/engine/client/gl_rpart.c
index e42d40c..5fb971f 100644
--- b/engine/client/gl_rpart.c
+++ a/engine/client/gl_rpart.c
@@ -843,20 +843,64 @@ void CL_BloodStream( const vec3_t org, const vec3_t dir, int pcolor, int speed )
{
particle_t *p;
int i, j;
+ float arc;
- for( i = 0; i < speed * 20; i++ )
+ for( arc = 0.05f, i = 0; i < 100; i++, arc -= 0.005f )
{
p = CL_AllocParticle( NULL );
+
if( !p ) return;
- p->die += Com_RandomFloat( 0.2f, 0.8f );
+ p->die += 2.0f;
p->type = pt_vox_grav;
- p->color = pcolor;
+ p->color = pcolor + Com_RandomLong( 0, 9 );
- for( j = 0; j < 3; j++ )
+ VectorCopy( org, p->org );
+ VectorCopy( dir, p->vel );
+
+ p->vel[2] -= arc;
+ arc -= 0.005f;
+
+ VectorScale( p->vel, speed, p->vel );
+ }
+
+ for( arc = 0.075f, i = 0; i < speed / 2; i++, arc -= 0.005f )
+ {
+ float num;
+
+ p = CL_AllocParticle( NULL );
+ if( !p ) return;
+
+ p->die += 3.0f;
+ p->color = pcolor + Com_RandomLong( 0, 9 );
+ p->type = pt_vox_slowgrav;
+
+ VectorCopy( org, p->org );
+ VectorCopy( dir, p->vel );
+
+ p->vel[2] -= arc;
+
+ num = Com_RandomFloat( 0.0f, 1.0f );
+ num = 1.7f * num * (int)(num * speed);
+ VectorScale( p->vel, num, p->vel );
+
+ for( j = 0; j < 2; j++ )
{
- p->org[j] = org[j];
- p->vel[j] = dir[j] * speed;
+ p = CL_AllocParticle( NULL );
+ if( !p ) return;
+
+ p->die += 3.0f;
+ p->color = pcolor + Com_RandomLong( 0, 9 );
+ p->type = pt_vox_slowgrav;
+
+ p->org[0] = org[0] + Com_RandomFloat( -1.0f, 1.0f );
+ p->org[1] = org[1] + Com_RandomFloat( -1.0f, 1.0f );
+ p->org[2] = org[2] + Com_RandomFloat( -1.0f, 1.0f );
+
+ VectorCopy( dir, p->vel );
+ p->vel[2] -= arc;
+
+ VectorScale( p->vel, num, p->vel );
}
}
}
diff --git b/engine/client/gl_rsurf.c a/engine/client/gl_rsurf.c
index a8ede91..2391c3a 100644
--- b/engine/client/gl_rsurf.c
+++ a/engine/client/gl_rsurf.c
@@ -31,7 +31,6 @@ typedef struct
static int nColinElim; // stats
static vec2_t world_orthocenter;
static vec2_t world_orthohalf;
-static byte visbytes[MAX_MAP_LEAFS/8];
static uint r_blocklights[BLOCK_SIZE_MAX*BLOCK_SIZE_MAX*3];
static glpoly_t *fullbright_polys[MAX_TEXTURES];
static qboolean draw_fullbrights = false;
@@ -44,7 +43,7 @@ static void LM_UploadBlock( int lightmapnum );
byte *Mod_GetCurrentVis( void )
{
- return Mod_LeafPVS( r_viewleaf, cl.worldmodel );
+ return RI.visbytes;
}
void Mod_SetOrthoBounds( float *mins, float *maxs )
@@ -79,6 +78,7 @@ static void BoundPoly( int numverts, float *verts, vec3_t mins, vec3_t maxs )
static void SubdividePolygon_r( msurface_t *warpface, int numverts, float *verts )
{
int i, j, k, f, b;
+ int sample_size;
vec3_t mins, maxs;
float m, frac, s, t, *v, vertsDiv;
vec3_t front[SUBDIVIDE_SIZE], back[SUBDIVIDE_SIZE], total;
@@ -88,6 +88,7 @@ static void SubdividePolygon_r( msurface_t *warpface, int numverts, float *verts
if( numverts > ( SUBDIVIDE_SIZE - 4 ))
Host_Error( "Mod_SubdividePolygon: too many vertexes on face ( %i )\n", numverts );
+ sample_size = Mod_SampleSizeForFace( warpface );
BoundPoly( numverts, verts, mins, maxs );
for( i = 0; i < 3; i++ )
@@ -182,15 +183,15 @@ static void SubdividePolygon_r( msurface_t *warpface, int numverts, float *verts
// lightmap texture coordinates
s = DotProduct( verts, warpface->texinfo->vecs[0] ) + warpface->texinfo->vecs[0][3];
s -= warpface->texturemins[0];
- s += warpface->light_s * LM_SAMPLE_SIZE;
- s += LM_SAMPLE_SIZE >> 1;
- s /= BLOCK_SIZE * LM_SAMPLE_SIZE; //fa->texinfo->texture->width;
+ s += warpface->light_s * sample_size;
+ s += sample_size >> 1;
+ s /= BLOCK_SIZE * sample_size; //fa->texinfo->texture->width;
t = DotProduct( verts, warpface->texinfo->vecs[1] ) + warpface->texinfo->vecs[1][3];
t -= warpface->texturemins[1];
- t += warpface->light_t * LM_SAMPLE_SIZE;
- t += LM_SAMPLE_SIZE >> 1;
- t /= BLOCK_SIZE * LM_SAMPLE_SIZE; //fa->texinfo->texture->height;
+ t += warpface->light_t * sample_size;
+ t += sample_size >> 1;
+ t /= BLOCK_SIZE * sample_size; //fa->texinfo->texture->height;
poly->verts[i+1][5] = s;
poly->verts[i+1][6] = t;
@@ -285,8 +286,8 @@ GL_BuildPolygonFromSurface
void GL_BuildPolygonFromSurface( model_t *mod, msurface_t *fa )
{
int i, lindex, lnumverts;
+ int vertpage, sample_size;
medge_t *pedges, *r_pedge;
- int vertpage;
texture_t *tex;
gltexture_t *glt;
float *vec;
@@ -310,6 +311,8 @@ void GL_BuildPolygonFromSurface( model_t *mod, msurface_t *fa )
glt->srcHeight = tex->height;
}
+ sample_size = Mod_SampleSizeForFace( fa );
+
// reconstruct the polygon
pedges = mod->edges;
lnumverts = fa->numedges;
@@ -350,15 +353,15 @@ void GL_BuildPolygonFromSurface( model_t *mod, msurface_t *fa )
// lightmap texture coordinates
s = DotProduct( vec, fa->texinfo->vecs[0] ) + fa->texinfo->vecs[0][3];
s -= fa->texturemins[0];
- s += fa->light_s * LM_SAMPLE_SIZE;
- s += LM_SAMPLE_SIZE >> 1;
- s /= BLOCK_SIZE * LM_SAMPLE_SIZE; //fa->texinfo->texture->width;
+ s += fa->light_s * sample_size;
+ s += sample_size >> 1;
+ s /= BLOCK_SIZE * sample_size; //fa->texinfo->texture->width;
t = DotProduct( vec, fa->texinfo->vecs[1] ) + fa->texinfo->vecs[1][3];
t -= fa->texturemins[1];
- t += fa->light_t * LM_SAMPLE_SIZE;
- t += LM_SAMPLE_SIZE >> 1;
- t /= BLOCK_SIZE * LM_SAMPLE_SIZE; //fa->texinfo->texture->height;
+ t += fa->light_t * sample_size;
+ t += sample_size >> 1;
+ t /= BLOCK_SIZE * sample_size; //fa->texinfo->texture->height;
poly->verts[i][5] = s;
poly->verts[i][6] = t;
@@ -467,6 +470,7 @@ void R_AddDynamicLights( msurface_t *surf )
int lnum, s, t, sd, td, smax, tmax;
float sl, tl, sacc, tacc;
vec3_t impact, origin_l;
+ int sample_size;
mtexinfo_t *tex;
dlight_t *dl;
uint *bl;
@@ -474,8 +478,9 @@ void R_AddDynamicLights( msurface_t *surf )
// no dlighted surfaces here
if( !R_CountSurfaceDlights( surf )) return;
- smax = (surf->extents[0] / LM_SAMPLE_SIZE) + 1;
- tmax = (surf->extents[1] / LM_SAMPLE_SIZE) + 1;
+ sample_size = Mod_SampleSizeForFace( surf );
+ smax = (surf->extents[0] / sample_size) + 1;
+ tmax = (surf->extents[1] / sample_size) + 1;
tex = surf->texinfo;
for( lnum = 0; lnum < MAX_DLIGHTS; lnum++ )
@@ -512,12 +517,12 @@ void R_AddDynamicLights( msurface_t *surf )
tl = DotProduct( impact, tex->vecs[1] ) + tex->vecs[1][3] - surf->texturemins[1];
bl = r_blocklights;
- for( t = 0, tacc = 0; t < tmax; t++, tacc += LM_SAMPLE_SIZE )
+ for( t = 0, tacc = 0; t < tmax; t++, tacc += sample_size )
{
td = tl - tacc;
if( td < 0 ) td = -td;
- for( s = 0, sacc = 0; s < smax; s++, sacc += LM_SAMPLE_SIZE, bl += 3 )
+ for( s = 0, sacc = 0; s < smax; s++, sacc += sample_size, bl += 3 )
{
sd = sl - sacc;
if( sd < 0 ) sd = -sd;
@@ -547,7 +552,7 @@ void R_SetCacheState( msurface_t *surf )
for( maps = 0; maps < MAXLIGHTMAPS && surf->styles[maps] != 255; maps++ )
{
- surf->cached_light[maps] = RI.lightstylevalue[surf->styles[maps]];
+ surf->cached_light[maps] = tr.lightstylevalue[surf->styles[maps]];
}
}
@@ -637,7 +642,6 @@ static void LM_UploadBlock( qboolean dynamic )
r_lightmap.flags = ( world.version == Q1BSP_VERSION ) ? 0 : IMAGE_HAS_COLOR;
r_lightmap.buffer = gl_lms.lightmap_buffer;
tr.lightmapTextures[i] = GL_LoadTextureInternal( lmName, &r_lightmap, TF_FONT, false );
- GL_SetTextureType( tr.lightmapTextures[i], TEX_LIGHTMAP );
if( ++gl_lms.current_lightmap_texture == MAX_LIGHTMAPS )
Host_Error( "AllocBlock: full\n" );
@@ -657,10 +661,12 @@ static void R_BuildLightMap( msurface_t *surf, byte *dest, int stride, qboolean
int smax, tmax;
uint *bl, scale;
int i, map, size, s, t;
+ int sample_size;
color24 *lm;
- smax = ( surf->extents[0] / LM_SAMPLE_SIZE ) + 1;
- tmax = ( surf->extents[1] / LM_SAMPLE_SIZE ) + 1;
+ sample_size = Mod_SampleSizeForFace( surf );
+ smax = ( surf->extents[0] / sample_size ) + 1;
+ tmax = ( surf->extents[1] / sample_size ) + 1;
size = smax * tmax;
lm = surf->samples;
@@ -670,7 +676,7 @@ static void R_BuildLightMap( msurface_t *surf, byte *dest, int stride, qboolean
// add all the lightmaps
for( map = 0; map < MAXLIGHTMAPS && surf->styles[map] != 255 && lm; map++ )
{
- scale = RI.lightstylevalue[surf->styles[map]];
+ scale = tr.lightstylevalue[surf->styles[map]];
for( i = 0, bl = r_blocklights; i < size; i++, bl += 3, lm++ )
{
@@ -716,6 +722,8 @@ void DrawGLPoly( glpoly_t *p, float xScale, float yScale )
cl_entity_t *e = RI.currententity;
int i, hasScale = false;
+ if( !p ) return;
+
// special hack for non-lightmapped surfaces
if( p->flags & SURF_DRAWTILED )
GL_ResetFogColor();
@@ -879,10 +887,12 @@ void R_BlendLightmaps( void )
for( surf = gl_lms.dynamic_surfaces; surf != NULL; surf = surf->lightmapchain )
{
int smax, tmax;
+ int sample_size;
byte *base;
- smax = ( surf->extents[0] / LM_SAMPLE_SIZE ) + 1;
- tmax = ( surf->extents[1] / LM_SAMPLE_SIZE ) + 1;
+ sample_size = Mod_SampleSizeForFace( surf );
+ smax = ( surf->extents[0] / sample_size ) + 1;
+ tmax = ( surf->extents[1] / sample_size ) + 1;
info = SURF_INFO( surf, RI.currentmodel );
if( LM_AllocBlock( smax, tmax, &info->dlight_s, &info->dlight_t ))
@@ -996,7 +1006,7 @@ void R_RenderFullbrights( void )
for( p = fullbright_polys[i]; p; p = p->next )
{
if( p->flags & SURF_DRAWTURB )
- EmitWaterPolys( p, ( p->flags & SURF_NOCULL ));
+ EmitWaterPolys( p, ( p->flags & SURF_NOCULL ), false );
else DrawGLPoly( p, 0.0f, 0.0f );
}
@@ -1086,15 +1096,8 @@ void R_RenderBrushPoly( msurface_t *fa )
else r_stats.c_brush_polys++;
if( fa->flags & SURF_DRAWSKY )
- {
- if( world.sky_sphere )
- {
- // warp texture, no lightmaps
- EmitSkyLayers( fa );
- }
- return;
- }
-
+ return; // already handled
+
t = R_TextureAnimation( fa->texinfo->texture, fa - RI.currententity->model->surfaces );
if( RP_NORMALPASS() && fa->flags & SURF_REFLECT )
@@ -1122,13 +1125,13 @@ void R_RenderBrushPoly( msurface_t *fa )
if( fa->flags & SURF_DRAWTURB )
{
// warp texture, no lightmaps
- EmitWaterPolys( fa->polys, ( fa->flags & SURF_NOCULL ));
+ EmitWaterPolys( fa->polys, ( fa->flags & SURF_NOCULL ), false );
if( is_mirror ) R_EndDrawMirror();
// END WATER STUFF
return;
}
- if( t->fb_texturenum )
+ if( t->fb_texturenum && fa->polys )
{
// HACKHACK: store fullbrights in poly->next (only for non-water surfaces)
fa->polys->next = fullbright_polys[t->fb_texturenum];
@@ -1182,7 +1185,7 @@ void R_RenderBrushPoly( msurface_t *fa )
// check for lightmap modification
for( maps = 0; maps < MAXLIGHTMAPS && fa->styles[maps] != 255; maps++ )
{
- if( RI.lightstylevalue[fa->styles[maps]] != fa->cached_light[maps] )
+ if( tr.lightstylevalue[fa->styles[maps]] != fa->cached_light[maps] )
goto dynamic;
}
@@ -1199,10 +1202,12 @@ dynamic:
if(( fa->styles[maps] >= 32 || fa->styles[maps] == 0 ) && ( fa->dlightframe != tr.framecount ))
{
byte temp[132*132*4];
+ int sample_size;
int smax, tmax;
- smax = ( fa->extents[0] / LM_SAMPLE_SIZE ) + 1;
- tmax = ( fa->extents[1] / LM_SAMPLE_SIZE ) + 1;
+ sample_size = Mod_SampleSizeForFace( fa );
+ smax = ( fa->extents[0] / sample_size ) + 1;
+ tmax = ( fa->extents[1] / sample_size ) + 1;
R_BuildLightMap( fa, temp, smax * 4, true );
R_SetCacheState( fa );
@@ -1249,31 +1254,40 @@ void R_DrawTextureChains( void )
RI.currententity = clgame.entities;
RI.currentmodel = RI.currententity->model;
+ if( world.sky_sphere && !world.custom_skybox )
+ {
+ pglDisable( GL_TEXTURE_2D );
+ pglColor3f( 1.0f, 1.0f, 1.0f );
+ }
+
// clip skybox surfaces
for( s = skychain; s != NULL; s = s->texturechain )
R_AddSkyBoxSurface( s );
+ if( world.sky_sphere && !world.custom_skybox )
+ {
+ pglEnable( GL_TEXTURE_2D );
+
+ if( skychain )
+ R_DrawClouds();
+ skychain = NULL;
+ }
+
for( i = 0; i < cl.worldmodel->numtextures; i++ )
{
t = cl.worldmodel->textures[i];
if( !t ) continue;
s = t->texturechain;
- if( !s ) continue;
- if( i == tr.skytexturenum )
- {
- if( world.sky_sphere )
- R_DrawSkyChain( s );
- }
- else
- {
- if(( s->flags & SURF_DRAWTURB ) && cl.refdef.movevars->wateralpha < 1.0f )
- continue; // draw translucent water later
+ if( !s || ( i == tr.skytexturenum ))
+ continue;
- for( ; s != NULL; s = s->texturechain )
- R_RenderBrushPoly( s );
- }
+ if(( s->flags & SURF_DRAWTURB ) && cl.refdef.movevars->wateralpha < 1.0f )
+ continue; // draw translucent water later
+
+ for( ; s != NULL; s = s->texturechain )
+ R_RenderBrushPoly( s );
t->texturechain = NULL;
}
@@ -1298,6 +1312,10 @@ void R_DrawWaterSurfaces( void )
if( cl.refdef.movevars->wateralpha >= 1.0f )
return;
+ // restore worldmodel
+ RI.currententity = clgame.entities;
+ RI.currentmodel = RI.currententity->model;
+
// go back to the world matrix
R_LoadIdentity();
@@ -1323,7 +1341,7 @@ void R_DrawWaterSurfaces( void )
GL_Bind( GL_TEXTURE0, t->gl_texturenum );
for( ; s; s = s->texturechain )
- EmitWaterPolys( s->polys, ( s->flags & SURF_NOCULL ));
+ EmitWaterPolys( s->polys, ( s->flags & SURF_NOCULL ), false );
t->texturechain = NULL;
}
@@ -1389,7 +1407,6 @@ void R_DrawBrushModel( cl_entity_t *e )
if( !RI.drawWorld ) return;
clmodel = e->model;
- RI.currentWaveHeight = RI.currententity->curstate.scale * 32.0f;
// don't reflect this entity in mirrors
if( e->curstate.effects & EF_NOREFLECT && RI.params & RP_MIRRORVIEW )
@@ -1551,7 +1568,7 @@ void R_DrawStaticModel( cl_entity_t *e )
if( R_CullSurface( psurf, RI.clipFlags ))
continue;
- if( psurf->flags & SURF_DRAWSKY && !world.sky_sphere )
+ if( psurf->flags & SURF_DRAWSKY )
{
// make sky chain to right clip the skybox
psurf->texturechain = skychain;
@@ -1677,7 +1694,7 @@ void R_RecursiveWorldNode( mnode_t *node, uint clipflags )
if( R_CullSurface( surf, clipflags ))
continue;
- if( surf->flags & SURF_DRAWSKY && !world.sky_sphere )
+ if( surf->flags & SURF_DRAWSKY )
{
// make sky chain to right clip the skybox
surf->texturechain = skychain;
@@ -1900,7 +1917,6 @@ void R_DrawWorld( void )
memset( fullbright_polys, 0, sizeof( fullbright_polys ));
memset( detail_surfaces, 0, sizeof( detail_surfaces ));
- RI.currentWaveHeight = RI.waveHeight;
GL_SetRenderMode( kRenderNormal );
gl_lms.dynamic_surfaces = NULL;
@@ -1941,7 +1957,6 @@ Mark the leaves and nodes that are in the PVS for the current leaf
*/
void R_MarkLeaves( void )
{
- byte *vis;
mnode_t *node;
int i;
@@ -1952,21 +1967,20 @@ void R_MarkLeaves( void )
// force recalc viewleaf
r_novis->modified = false;
tr.fResetVis = false;
- r_viewleaf = NULL;
+ RI.viewleaf = NULL;
}
- if( r_viewleaf == r_oldviewleaf && r_viewleaf2 == r_oldviewleaf2 && !r_novis->integer && r_viewleaf != NULL )
+ if( RI.viewleaf == RI.oldviewleaf && !r_novis->integer && RI.viewleaf != NULL )
return;
// development aid to let you run around
// and see exactly where the pvs ends
if( r_lockpvs->integer ) return;
+ RI.oldviewleaf = RI.viewleaf;
tr.visframecount++;
- r_oldviewleaf = r_viewleaf;
- r_oldviewleaf2 = r_viewleaf2;
-
- if( r_novis->integer || RI.drawOrtho || !r_viewleaf || !cl.worldmodel->visdata )
+
+ if( r_novis->integer || RI.drawOrtho || !RI.viewleaf || !cl.worldmodel->visdata )
{
// mark everything
for( i = 0; i < cl.worldmodel->numleafs; i++ )
@@ -1976,26 +1990,11 @@ void R_MarkLeaves( void )
return;
}
- // may have to combine two clusters
- // because of solid water boundaries
- vis = Mod_LeafPVS( r_viewleaf, cl.worldmodel );
-
- if( r_viewleaf != r_viewleaf2 )
- {
- int longs = ( cl.worldmodel->numleafs + 31 ) >> 5;
-
- memcpy( visbytes, vis, longs << 2 );
- vis = Mod_LeafPVS( r_viewleaf2, cl.worldmodel );
-
- for( i = 0; i < longs; i++ )
- ((int *)visbytes)[i] |= ((int *)vis)[i];
-
- vis = visbytes;
- }
+ Mod_FatPVS( RI.pvsorigin, REFPVS_RADIUS, RI.visbytes, world.visbytes, FBitSet( RI.params, RP_OLDVIEWLEAF ), r_novis->integer );
for( i = 0; i < cl.worldmodel->numleafs; i++ )
{
- if( vis[i>>3] & ( 1<<( i & 7 )))
+ if( CHECKVISBIT( RI.visbytes, i ))
{
node = (mnode_t *)&cl.worldmodel->leafs[i+1];
do
@@ -2017,14 +2016,16 @@ GL_CreateSurfaceLightmap
void GL_CreateSurfaceLightmap( msurface_t *surf )
{
int smax, tmax;
+ int sample_size;
byte *base;
if( !cl.worldmodel->lightdata ) return;
if( surf->flags & SURF_DRAWTILED )
return;
- smax = ( surf->extents[0] / LM_SAMPLE_SIZE ) + 1;
- tmax = ( surf->extents[1] / LM_SAMPLE_SIZE ) + 1;
+ sample_size = Mod_SampleSizeForFace( surf );
+ smax = ( surf->extents[0] / sample_size ) + 1;
+ tmax = ( surf->extents[1] / sample_size ) + 1;
if( !LM_AllocBlock( smax, tmax, &surf->light_s, &surf->light_t ))
{
@@ -2075,7 +2076,7 @@ void GL_RebuildLightmaps( void )
gl_lms.current_lightmap_texture = 0;
// setup all the lightstyles
- R_AnimateLight();
+ CL_RunLightStyles();
LM_InitBlock();
@@ -2131,7 +2132,7 @@ void GL_BuildLightmaps( void )
memset( tr.lightmapTextures, 0, sizeof( tr.lightmapTextures ));
memset( tr.mirror_entities, 0, sizeof( tr.mirror_entities ));
memset( tr.mirrorTextures, 0, sizeof( tr.mirrorTextures ));
- memset( visbytes, 0x00, sizeof( visbytes ));
+ memset( &RI, 0, sizeof( RI ));
skychain = NULL;
@@ -2142,7 +2143,7 @@ void GL_BuildLightmaps( void )
nColinElim = 0;
// setup all the lightstyles
- R_AnimateLight();
+ CL_RunLightStyles();
LM_InitBlock();
@@ -2166,9 +2167,6 @@ void GL_BuildLightmaps( void )
if( m->surfaces[j].flags & SURF_DRAWTURB )
continue;
- if( m->surfaces[j].flags & SURF_DRAWSKY && world.sky_sphere )
- continue;
-
GL_BuildPolygonFromSurface( m, m->surfaces + j );
}
diff --git b/engine/client/gl_sprite.c a/engine/client/gl_sprite.c
index 331e5d6..e07dc25 100644
--- b/engine/client/gl_sprite.c
+++ a/engine/client/gl_sprite.c
@@ -104,8 +104,6 @@ static dframetype_t *R_SpriteLoadFrame( model_t *mod, void *pin, mspriteframe_t
pspriteframe->gl_texturenum = gl_texturenum;
*ppframe = pspriteframe;
- GL_SetTextureType( pspriteframe->gl_texturenum, TEX_SPRITE );
-
return (dframetype_t *)((byte *)(pinframe + 1) + pinframe->width * pinframe->height );
}
@@ -200,10 +198,10 @@ void Mod_LoadSpriteModel( model_t *mod, const void *buffer, qboolean *loaded, ui
psprite->radius = pin->boundingradius;
psprite->synctype = pin->synctype;
- mod->mins[0] = mod->mins[1] = -pin->bounds[0] / 2;
- mod->maxs[0] = mod->maxs[1] = pin->bounds[0] / 2;
- mod->mins[2] = -pin->bounds[1] / 2;
- mod->maxs[2] = pin->bounds[1] / 2;
+ mod->mins[0] = mod->mins[1] = -pin->bounds[0] * 0.5f;
+ mod->maxs[0] = mod->maxs[1] = pin->bounds[0] * 0.5f;
+ mod->mins[2] = -pin->bounds[1] * 0.5f;
+ mod->maxs[2] = pin->bounds[1] * 0.5f;
numi = (short *)(pin + 1);
if( host.type == HOST_DEDICATED )
@@ -336,7 +334,7 @@ void Mod_LoadMapSprite( model_t *mod, const void *buffer, size_t size, qboolean
psprite->type = SPR_FWD_PARALLEL_ORIENTED;
psprite->texFormat = SPR_ALPHTEST;
psprite->numframes = mod->numframes = numframes;
- psprite->radius = sqrt((( w >> 1) * (w >> 1)) + ((h >> 1) * (h >> 1)));
+ psprite->radius = sqrt(((w >> 1) * (w >> 1)) + ((h >> 1) * (h >> 1)));
mod->mins[0] = mod->mins[1] = -w / 2;
mod->maxs[0] = mod->maxs[1] = w / 2;
@@ -384,8 +382,7 @@ void Mod_LoadMapSprite( model_t *mod, const void *buffer, size_t size, qboolean
pspriteframe->down = ( h >> 1 ) - h;
pspriteframe->right = w + -( w >> 1 );
pspriteframe->gl_texturenum = GL_LoadTextureInternal( texname, &temp, TF_IMAGE, false );
- GL_SetTextureType( pspriteframe->gl_texturenum, TEX_NOMIP );
-
+
xl += w;
if( xl >= pix->width )
{
@@ -462,13 +459,16 @@ mspriteframe_t *R_GetSpriteFrame( const model_t *pModel, int frame, float yaw )
mspritegroup_t *pspritegroup;
mspriteframe_t *pspriteframe = NULL;
float *pintervals, fullinterval;
- float targettime, time;
int i, numframes;
+ float targettime;
ASSERT( pModel );
psprite = pModel->cache.data;
- if( frame < 0 ) frame = 0;
+ if( frame < 0 )
+ {
+ frame = 0;
+ }
else if( frame >= psprite->numframes )
{
MsgDev( D_WARN, "R_GetSpriteFrame: no such frame %d (%s)\n", frame, pModel->name );
@@ -485,11 +485,10 @@ mspriteframe_t *R_GetSpriteFrame( const model_t *pModel, int frame, float yaw )
pintervals = pspritegroup->intervals;
numframes = pspritegroup->numframes;
fullinterval = pintervals[numframes-1];
- time = cl.time;
// when loading in Mod_LoadSpriteGroup, we guaranteed all interval values
// are positive, so we don't have to worry about division by zero
- targettime = time - ((int)(time / fullinterval)) * fullinterval;
+ targettime = cl.time - ((int)( cl.time / fullinterval )) * fullinterval;
for( i = 0; i < (numframes - 1); i++ )
{
@@ -520,98 +519,89 @@ between frames where are we lerping
*/
float R_GetSpriteFrameInterpolant( cl_entity_t *ent, mspriteframe_t **oldframe, mspriteframe_t **curframe )
{
- msprite_t *psprite;
- mspritegroup_t *pspritegroup;
- int i, j, numframes, frame;
- float lerpFrac, time, jtime, jinterval;
- float *pintervals, fullinterval, targettime;
- int m_fDoInterp;
+ msprite_t *psprite;
+ float lerpFrac = 1.0f, frame;
+ int m_fDoInterp, oldf, newf;
+ float frametime = 0.0f;
+ int i, j, iframe;
psprite = ent->model->cache.data;
- frame = (int)ent->curstate.frame;
- lerpFrac = 1.0f;
+
+ if( ent->curstate.framerate > 0.0f )
+ frametime = (1.0f / ent->curstate.framerate);
+
+ frame = Q_max( 0.0f, ent->curstate.frame - host.frametime * ent->curstate.framerate );
+ iframe = (int)frame;
// misc info
- if( r_sprite_lerping->integer )
+ if( r_sprite_lerping->integer && psprite->numframes > 1 )
m_fDoInterp = (ent->curstate.effects & EF_NOINTERP) ? false : true;
else m_fDoInterp = false;
- if( frame < 0 )
+ if( m_fDoInterp == false )
{
- frame = 0;
- }
- else if( frame >= psprite->numframes )
+ // interpolation disabled for some reasons
+ *oldframe = *curframe = R_GetSpriteFrame( ent->model, ent->curstate.frame, ent->angles[YAW] );
+ return lerpFrac;
+ }
+
+ if( iframe < 0 )
+ {
+ iframe = 0;
+ }
+ else if( iframe >= psprite->numframes )
{
MsgDev( D_WARN, "R_GetSpriteFrameInterpolant: no such frame %d (%s)\n", frame, ent->model->name );
- frame = psprite->numframes - 1;
+ iframe = psprite->numframes - 1;
}
- if( psprite->frames[frame].type == FRAME_SINGLE )
+ // calc interpolant range
+ oldf = (int)Q_floor( frame );
+ newf = (int)Q_ceil( frame );
+
+ // allow interp between first and last frame
+ oldf = oldf % ( psprite->numframes - 1 );
+ newf = newf % ( psprite->numframes - 1 );
+
+ // NOTE: we allow interpolation between single and angled frames e.g. for Doom monsters
+ if( psprite->frames[iframe].type == FRAME_SINGLE || psprite->frames[iframe].type == FRAME_ANGLED )
{
- if( m_fDoInterp )
- {
- if( ent->latched.prevblending[0] >= psprite->numframes || psprite->frames[ent->latched.prevblending[0]].type != FRAME_SINGLE )
- {
- // this can be happens when rendering switched between single and angled frames
- // or change model on replace delta-entity
- ent->latched.prevblending[0] = ent->latched.prevblending[1] = frame;
- ent->latched.prevanimtime = RI.refdef.time;
- lerpFrac = 1.0f;
- }
-
- if( ent->latched.prevanimtime < RI.refdef.time )
- {
- if( frame != ent->latched.prevblending[1] )
- {
- ent->latched.prevblending[0] = ent->latched.prevblending[1];
- ent->latched.prevblending[1] = frame;
- ent->latched.prevanimtime = RI.refdef.time;
- lerpFrac = 0.0f;
- }
- else lerpFrac = (RI.refdef.time - ent->latched.prevanimtime) * 10;
- }
- else
- {
- ent->latched.prevblending[0] = ent->latched.prevblending[1] = frame;
- ent->latched.prevanimtime = RI.refdef.time;
- lerpFrac = 0.0f;
- }
- }
- else
+ // frame was changed
+ if( newf != ent->latched.prevframe )
{
- ent->latched.prevblending[0] = ent->latched.prevblending[1] = frame;
- lerpFrac = 1.0f;
+ ent->latched.prevanimtime = cl.time + frametime;
+ ent->latched.prevframe = newf;
+ lerpFrac = 1.0f; // reset lerp
}
+
+ if( ent->latched.prevanimtime != 0.0f && ent->latched.prevanimtime > cl.time )
+ lerpFrac = (ent->latched.prevanimtime - cl.time) * ent->curstate.framerate;
- if( ent->latched.prevblending[0] >= psprite->numframes )
- {
- // reset interpolation on change model
- ent->latched.prevblending[0] = ent->latched.prevblending[1] = frame;
- ent->latched.prevanimtime = RI.refdef.time;
- lerpFrac = 0.0f;
- }
+ // compute lerp factor
+ lerpFrac = (int)(10000 * lerpFrac) / 10000.0f;
+ lerpFrac = bound( 0.0f, 1.0f - lerpFrac, 1.0f );
// get the interpolated frames
- if( oldframe ) *oldframe = psprite->frames[ent->latched.prevblending[0]].frameptr;
- if( curframe ) *curframe = psprite->frames[frame].frameptr;
+ if( oldframe ) *oldframe = R_GetSpriteFrame( ent->model, oldf, ent->angles[YAW] );
+ if( curframe ) *curframe = R_GetSpriteFrame( ent->model, newf, ent->angles[YAW] );
}
- else if( psprite->frames[frame].type == FRAME_GROUP )
+ else if( psprite->frames[iframe].type == FRAME_GROUP )
{
- pspritegroup = (mspritegroup_t *)psprite->frames[frame].frameptr;
- pintervals = pspritegroup->intervals;
- numframes = pspritegroup->numframes;
- fullinterval = pintervals[numframes-1];
+ mspritegroup_t *pspritegroup = (mspritegroup_t *)psprite->frames[iframe].frameptr;
+ float *pintervals = pspritegroup->intervals;
+ float fullinterval, targettime, jinterval;
+ float jtime = 0.0f;
+
+ fullinterval = pintervals[pspritegroup->numframes-1];
jinterval = pintervals[1] - pintervals[0];
- time = RI.refdef.time;
- jtime = 0.0f;
// when loading in Mod_LoadSpriteGroup, we guaranteed all interval values
// are positive, so we don't have to worry about division by zero
- targettime = time - ((int)(time / fullinterval)) * fullinterval;
+ targettime = cl.time - ((int)( cl.time / fullinterval )) * fullinterval;
// LordHavoc: since I can't measure the time properly when it loops from numframes - 1 to 0,
// i instead measure the time of the first frame, hoping it is consistent
- for( i = 0, j = numframes - 1; i < (numframes - 1); i++ )
+ for( i = 0, j = (pspritegroup->numframes - 1); i < (pspritegroup->numframes - 1); i++ )
{
if( pintervals[i] > targettime )
break;
@@ -620,61 +610,12 @@ float R_GetSpriteFrameInterpolant( cl_entity_t *ent, mspriteframe_t **oldframe,
jtime = pintervals[i];
}
- if( m_fDoInterp )
- lerpFrac = (targettime - jtime) / jinterval;
- else j = i; // no lerping
+ lerpFrac = (targettime - jtime) / jinterval;
// get the interpolated frames
if( oldframe ) *oldframe = pspritegroup->frames[j];
if( curframe ) *curframe = pspritegroup->frames[i];
}
- else if( psprite->frames[frame].type == FRAME_ANGLED )
- {
- // e.g. doom-style sprite monsters
- float yaw = ent->angles[YAW];
- int angleframe = (int)(Q_rint(( RI.refdef.viewangles[1] - yaw + 45.0f ) / 360 * 8) - 4) & 7;
-
- if( m_fDoInterp )
- {
- if( ent->latched.prevblending[0] >= psprite->numframes || psprite->frames[ent->latched.prevblending[0]].type != FRAME_ANGLED )
- {
- // this can be happens when rendering switched between single and angled frames
- // or change model on replace delta-entity
- ent->latched.prevblending[0] = ent->latched.prevblending[1] = frame;
- ent->latched.prevanimtime = RI.refdef.time;
- lerpFrac = 1.0f;
- }
-
- if( ent->latched.prevanimtime < RI.refdef.time )
- {
- if( frame != ent->latched.prevblending[1] )
- {
- ent->latched.prevblending[0] = ent->latched.prevblending[1];
- ent->latched.prevblending[1] = frame;
- ent->latched.prevanimtime = RI.refdef.time;
- lerpFrac = 0.0f;
- }
- else lerpFrac = (RI.refdef.time - ent->latched.prevanimtime) * ent->curstate.framerate;
- }
- else
- {
- ent->latched.prevblending[0] = ent->latched.prevblending[1] = frame;
- ent->latched.prevanimtime = RI.refdef.time;
- lerpFrac = 0.0f;
- }
- }
- else
- {
- ent->latched.prevblending[0] = ent->latched.prevblending[1] = frame;
- lerpFrac = 1.0f;
- }
-
- pspritegroup = (mspritegroup_t *)psprite->frames[ent->latched.prevblending[0]].frameptr;
- if( oldframe ) *oldframe = pspritegroup->frames[angleframe];
-
- pspritegroup = (mspritegroup_t *)psprite->frames[frame].frameptr;
- if( curframe ) *curframe = pspritegroup->frames[angleframe];
- }
return lerpFrac;
}
diff --git b/engine/client/gl_studio.c a/engine/client/gl_studio.c
index 8f6fbf0..0bb18c4 100644
--- b/engine/client/gl_studio.c
+++ a/engine/client/gl_studio.c
@@ -28,7 +28,7 @@ GNU General Public License for more details.
#define STUDIO_MERGE_TEXTURES
#define EVENT_CLIENT 5000 // less than this value it's a server-side studio events
-#define MAXARRAYVERTS 20000 // used for draw shadows
+#define MAXARRAYVERTS 32768 // used for draw shadows
#define LEGS_BONES_COUNT 8
static vec3_t hullcolor[8] =
@@ -138,7 +138,7 @@ R_StudioInit
*/
void R_StudioInit( void )
{
- float pixelAspect;
+ float pixelAspect, fov_x = 90.0f, fov_y;
r_studio_lambert = Cvar_Get( "r_studio_lambert", "2", CVAR_ARCHIVE, "bonelighting lambert value" );
r_studio_lerping = Cvar_Get( "r_studio_lerping", "1", CVAR_ARCHIVE, "enables studio animation lerping" );
@@ -156,7 +156,8 @@ void R_StudioInit( void )
pixelAspect *= (320.0f / 240.0f);
else pixelAspect *= (640.0f / 480.0f);
- aliasXscale = (float)scr_width->integer / RI.refdef.fov_y;
+ fov_y = V_CalcFov( &fov_x, scr_width->integer, scr_height->integer );
+ aliasXscale = (float)scr_width->integer / fov_y; // stub
aliasYscale = aliasXscale * pixelAspect;
Matrix3x4_LoadIdentity( g_aliastransform );
@@ -257,10 +258,22 @@ static qboolean R_StudioComputeBBox( cl_entity_t *e, vec3_t bbox[8] )
// rotate the bounding box
VectorCopy( e->angles, angles );
-
+#if 0
if( e->player ) angles[PITCH] = 0.0f; // don't rotate player model, only aim
AngleVectors( angles, vectors[0], vectors[1], vectors[2] );
+#else
+ vectors[0][0] = g_rotationmatrix[0][0];
+ vectors[0][1] = g_rotationmatrix[1][0];
+ vectors[0][2] = g_rotationmatrix[2][0];
+
+ vectors[1][0] = g_rotationmatrix[0][1];
+ vectors[1][1] = g_rotationmatrix[1][1];
+ vectors[1][2] = g_rotationmatrix[2][1];
+ vectors[2][0] = g_rotationmatrix[0][2];
+ vectors[2][1] = g_rotationmatrix[1][2];
+ vectors[2][2] = g_rotationmatrix[2][2];
+#endif
// compute a full bounding box
for( i = 0; i < 8; i++ )
{
@@ -270,7 +283,7 @@ static qboolean R_StudioComputeBBox( cl_entity_t *e, vec3_t bbox[8] )
// rotate by YAW
p2[0] = DotProduct( p1, vectors[0] );
- p2[1] = DotProduct( p1, vectors[1] );
+ p2[1] = -DotProduct( p1, vectors[1] );
p2[2] = DotProduct( p1, vectors[2] );
if( bbox ) VectorAdd( p2, e->origin, bbox[i] );
@@ -736,7 +749,7 @@ StudioCalcBoneAdj
void R_StudioCalcBoneAdj( float dadt, float *adj, const byte *pcontroller1, const byte *pcontroller2, byte mouthopen )
{
mstudiobonecontroller_t *pbonecontroller;
- float value;
+ float value = 0.0f;
int i, j;
pbonecontroller = (mstudiobonecontroller_t *)((byte *)m_pStudioHeader + m_pStudioHeader->bonecontrollerindex);
@@ -875,13 +888,13 @@ void R_StudioCalcBoneQuaterion( int frame, float s, mstudiobone_t *pbone, mstudi
if( !VectorCompare( angle1, angle2 ))
{
- AngleQuaternion( angle1, q1 );
- AngleQuaternion( angle2, q2 );
+ AngleQuaternion( angle1, q1, true );
+ AngleQuaternion( angle2, q2, true );
QuaternionSlerp( q1, q2, s, q );
}
else
{
- AngleQuaternion( angle1, q );
+ AngleQuaternion( angle1, q, true );
}
}
@@ -2017,10 +2030,10 @@ static void R_StudioDrawPoints( void )
pglColor4ub( clr->r, clr->g, clr->b, 255 );
alpha = 1.0f;
}
- else if( g_nFaceFlags & STUDIO_NF_TRANSPARENT && R_StudioOpaque( RI.currententity ))
+ else if( g_nFaceFlags & STUDIO_NF_TRANSPARENT && R_StudioOpaque( g_iRenderMode ))
{
GL_SetRenderMode( kRenderTransAlpha );
- pglAlphaFunc( GL_GREATER, 0.0f );
+ pglAlphaFunc( GL_GEQUAL, 0.5f );
alpha = 1.0f;
}
else if( g_nFaceFlags & STUDIO_NF_ADDITIVE )
@@ -2443,7 +2456,7 @@ static model_t *R_StudioSetupPlayerModel( int index )
if( cls.key_dest == key_menu && !index )
{
- // we are in menu.
+ // we are in gameui.
info = &gameui.playerinfo;
}
else
@@ -2565,15 +2578,16 @@ R_StudioSetupRenderer
*/
static void R_StudioSetupRenderer( int rendermode )
{
+ if( rendermode > kRenderTransAdd ) rendermode = 0;
g_iRenderMode = bound( 0, rendermode, kRenderTransAdd );
- pglShadeModel( GL_SMOOTH ); // enable gouraud shading
if( clgame.ds.cullMode != GL_NONE ) GL_Cull( GL_FRONT );
// enable depthmask on studiomodels
if( glState.drawTrans && g_iRenderMode != kRenderTransAdd )
pglDepthMask( GL_TRUE );
- pglAlphaFunc( GL_GREATER, 0.0f );
+ pglAlphaFunc( GL_GEQUAL, 0.5f );
+ pglShadeModel( GL_SMOOTH );
if( g_iBackFaceCull )
GL_FrontFace( true );
@@ -2588,7 +2602,6 @@ R_StudioRestoreRenderer
static void R_StudioRestoreRenderer( void )
{
pglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
- pglShadeModel( GL_FLAT );
// restore depthmask state for sprites etc
if( glState.drawTrans && g_iRenderMode != kRenderTransAdd )
@@ -2948,7 +2961,7 @@ R_StudioDrawPlayer
static int R_StudioDrawPlayer( int flags, entity_state_t *pplayer )
{
int m_nPlayerIndex;
- float gaitframe, gaityaw;
+ float gaitframe = 0.0f, gaityaw = 0.0f;
vec3_t dir, prevgaitorigin;
alight_t lighting;
@@ -3305,6 +3318,10 @@ void R_RunViewmodelEvents( void )
RI.currentmodel = RI.currententity->model;
if( !RI.currentmodel ) return;
+ if( !cl.weaponstarttime ) cl.weaponstarttime = cl.time;
+ RI.currententity->curstate.animtime = cl.weaponstarttime;
+ RI.currententity->curstate.sequence = cl.weaponsequence;
+
pStudioDraw->StudioDrawModel( STUDIO_EVENTS );
RI.currententity = NULL;
@@ -3344,6 +3361,10 @@ void R_DrawViewModel( void )
if( r_lefthand->integer == 1 || g_iBackFaceCull )
GL_FrontFace( !glState.frontFace );
+ if( !cl.weaponstarttime ) cl.weaponstarttime = cl.time;
+ RI.currententity->curstate.animtime = cl.weaponstarttime;
+ RI.currententity->curstate.sequence = cl.weaponsequence;
+
pStudioDraw->StudioDrawModel( STUDIO_RENDER );
// restore depth range
@@ -3433,7 +3454,7 @@ static void R_StudioLoadTexture( model_t *mod, studiohdr_t *phdr, mstudiotexture
filter = R_FindTexFilter( va( "%s.mdl/%s", mdlname, name )); // grab texture filter
// NOTE: colormaps must have the palette for properly work. Ignore it.
- if( Mod_AllowMaterials( ) && !( ptexture->flags & STUDIO_NF_COLORMAP ))
+ if( Mod_AllowMaterials( ) && !FBitSet( ptexture->flags, STUDIO_NF_COLORMAP ))
{
int gl_texturenum = 0;
@@ -3455,7 +3476,7 @@ static void R_StudioLoadTexture( model_t *mod, studiohdr_t *phdr, mstudiotexture
ptexture->index = (int)((byte *)phdr) + ptexture->index;
size = sizeof( mstudiotexture_t ) + ptexture->width * ptexture->height + 768;
- if( host.features & ENGINE_DISABLE_HDTEXTURES && ptexture->flags & STUDIO_NF_TRANSPARENT )
+ if( FBitSet( host.features, ENGINE_DISABLE_HDTEXTURES ) && FBitSet( ptexture->flags, STUDIO_NF_TRANSPARENT ))
flags |= TF_KEEP_8BIT; // Paranoia2 alpha-tracing
// build the texname
@@ -3473,7 +3494,6 @@ static void R_StudioLoadTexture( model_t *mod, studiohdr_t *phdr, mstudiotexture
{
// duplicate texnum for easy acess
if( tx ) tx->gl_texturenum = ptexture->index;
- GL_SetTextureType( ptexture->index, TEX_STUDIO );
}
}
diff --git b/engine/client/gl_vidnt.c a/engine/client/gl_vidnt.c
index 482bed3..96700ce 100644
--- b/engine/client/gl_vidnt.c
+++ a/engine/client/gl_vidnt.c
@@ -22,35 +22,28 @@ GNU General Public License for more details.
#define VID_AUTOMODE "-1"
#define VID_DEFAULTMODE 2.0f
#define DISP_CHANGE_BADDUALVIEW -6 // MSVC 6.0 doesn't
-#define num_vidmodes ( sizeof( vidmode ) / sizeof( vidmode[0] ))
+#define num_vidmodes ARRAYSIZE( vidmode )
#define WINDOW_STYLE (WS_OVERLAPPED|WS_BORDER|WS_SYSMENU|WS_CAPTION|WS_VISIBLE)
#define WINDOW_EX_STYLE (0)
-#define WINDOW_NAME "Xash Window" // Half-Life
-
-#ifdef WIN32
-// Enable NVIDIA High Performance Graphics while using Integrated Graphics.
-__declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
-#endif
+#define WINDOW_NAME "Xash3D Window" // Half-Life
convar_t *renderinfo;
-convar_t *gl_allow_software;
convar_t *gl_extensions;
convar_t *gl_alphabits;
convar_t *gl_stencilbits;
convar_t *gl_ignorehwgamma;
convar_t *gl_texture_anisotropy;
+convar_t *gl_texture_lodbias;
+convar_t *gl_texture_nearest;
convar_t *gl_compress_textures;
-convar_t *gl_luminance_textures;
convar_t *gl_compensate_gamma_screenshots;
convar_t *gl_keeptjunctions;
-convar_t *gl_texture_lodbias;
convar_t *gl_showtextures;
convar_t *gl_detailscale;
convar_t *gl_swapInterval;
convar_t *gl_check_errors;
convar_t *gl_allow_static;
convar_t *gl_allow_mirrors;
-convar_t *gl_texturemode;
convar_t *gl_wireframe;
convar_t *gl_round_down;
convar_t *gl_overview;
@@ -90,7 +83,6 @@ convar_t *r_fastsky;
convar_t *vid_displayfrequency;
convar_t *vid_fullscreen;
convar_t *vid_gamma;
-convar_t *vid_texgamma;
convar_t *vid_mode;
byte *r_temppool;
@@ -147,328 +139,403 @@ vidmode_t vidmode[] =
static dllfunc_t opengl_110funcs[] =
{
-{ "glClearColor" , (void **)&pglClearColor },
-{ "glClear" , (void **)&pglClear },
-{ "glAlphaFunc" , (void **)&pglAlphaFunc },
-{ "glBlendFunc" , (void **)&pglBlendFunc },
-{ "glCullFace" , (void **)&pglCullFace },
-{ "glDrawBuffer" , (void **)&pglDrawBuffer },
-{ "glReadBuffer" , (void **)&pglReadBuffer },
-{ "glEnable" , (void **)&pglEnable },
-{ "glDisable" , (void **)&pglDisable },
-{ "glEnableClientState" , (void **)&pglEnableClientState },
-{ "glDisableClientState" , (void **)&pglDisableClientState },
-{ "glGetBooleanv" , (void **)&pglGetBooleanv },
-{ "glGetDoublev" , (void **)&pglGetDoublev },
-{ "glGetFloatv" , (void **)&pglGetFloatv },
-{ "glGetIntegerv" , (void **)&pglGetIntegerv },
-{ "glGetError" , (void **)&pglGetError },
-{ "glGetString" , (void **)&pglGetString },
-{ "glFinish" , (void **)&pglFinish },
-{ "glFlush" , (void **)&pglFlush },
-{ "glClearDepth" , (void **)&pglClearDepth },
-{ "glDepthFunc" , (void **)&pglDepthFunc },
-{ "glDepthMask" , (void **)&pglDepthMask },
-{ "glDepthRange" , (void **)&pglDepthRange },
-{ "glFrontFace" , (void **)&pglFrontFace },
-{ "glDrawElements" , (void **)&pglDrawElements },
-{ "glColorMask" , (void **)&pglColorMask },
-{ "glIndexPointer" , (void **)&pglIndexPointer },
-{ "glVertexPointer" , (void **)&pglVertexPointer },
-{ "glNormalPointer" , (void **)&pglNormalPointer },
-{ "glColorPointer" , (void **)&pglColorPointer },
-{ "glTexCoordPointer" , (void **)&pglTexCoordPointer },
-{ "glArrayElement" , (void **)&pglArrayElement },
-{ "glColor3f" , (void **)&pglColor3f },
-{ "glColor3fv" , (void **)&pglColor3fv },
-{ "glColor4f" , (void **)&pglColor4f },
-{ "glColor4fv" , (void **)&pglColor4fv },
-{ "glColor3ub" , (void **)&pglColor3ub },
-{ "glColor4ub" , (void **)&pglColor4ub },
-{ "glColor4ubv" , (void **)&pglColor4ubv },
-{ "glTexCoord1f" , (void **)&pglTexCoord1f },
-{ "glTexCoord2f" , (void **)&pglTexCoord2f },
-{ "glTexCoord3f" , (void **)&pglTexCoord3f },
-{ "glTexCoord4f" , (void **)&pglTexCoord4f },
-{ "glTexGenf" , (void **)&pglTexGenf },
-{ "glTexGenfv" , (void **)&pglTexGenfv },
-{ "glTexGeni" , (void **)&pglTexGeni },
-{ "glVertex2f" , (void **)&pglVertex2f },
-{ "glVertex3f" , (void **)&pglVertex3f },
-{ "glVertex3fv" , (void **)&pglVertex3fv },
-{ "glNormal3f" , (void **)&pglNormal3f },
-{ "glNormal3fv" , (void **)&pglNormal3fv },
-{ "glBegin" , (void **)&pglBegin },
-{ "glEnd" , (void **)&pglEnd },
-{ "glLineWidth" , (void**)&pglLineWidth },
-{ "glPointSize" , (void**)&pglPointSize },
-{ "glMatrixMode" , (void **)&pglMatrixMode },
-{ "glOrtho" , (void **)&pglOrtho },
-{ "glRasterPos2f" , (void **)&pglRasterPos2f },
-{ "glFrustum" , (void **)&pglFrustum },
-{ "glViewport" , (void **)&pglViewport },
-{ "glPushMatrix" , (void **)&pglPushMatrix },
-{ "glPopMatrix" , (void **)&pglPopMatrix },
-{ "glPushAttrib" , (void **)&pglPushAttrib },
-{ "glPopAttrib" , (void **)&pglPopAttrib },
-{ "glLoadIdentity" , (void **)&pglLoadIdentity },
-{ "glLoadMatrixd" , (void **)&pglLoadMatrixd },
-{ "glLoadMatrixf" , (void **)&pglLoadMatrixf },
-{ "glMultMatrixd" , (void **)&pglMultMatrixd },
-{ "glMultMatrixf" , (void **)&pglMultMatrixf },
-{ "glRotated" , (void **)&pglRotated },
-{ "glRotatef" , (void **)&pglRotatef },
-{ "glScaled" , (void **)&pglScaled },
-{ "glScalef" , (void **)&pglScalef },
-{ "glTranslated" , (void **)&pglTranslated },
-{ "glTranslatef" , (void **)&pglTranslatef },
-{ "glReadPixels" , (void **)&pglReadPixels },
-{ "glDrawPixels" , (void **)&pglDrawPixels },
-{ "glStencilFunc" , (void **)&pglStencilFunc },
-{ "glStencilMask" , (void **)&pglStencilMask },
-{ "glStencilOp" , (void **)&pglStencilOp },
-{ "glClearStencil" , (void **)&pglClearStencil },
-{ "glIsEnabled" , (void **)&pglIsEnabled },
-{ "glIsList" , (void **)&pglIsList },
-{ "glIsTexture" , (void **)&pglIsTexture },
-{ "glTexEnvf" , (void **)&pglTexEnvf },
-{ "glTexEnvfv" , (void **)&pglTexEnvfv },
-{ "glTexEnvi" , (void **)&pglTexEnvi },
-{ "glTexParameterf" , (void **)&pglTexParameterf },
-{ "glTexParameterfv" , (void **)&pglTexParameterfv },
-{ "glTexParameteri" , (void **)&pglTexParameteri },
-{ "glHint" , (void **)&pglHint },
-{ "glPixelStoref" , (void **)&pglPixelStoref },
-{ "glPixelStorei" , (void **)&pglPixelStorei },
-{ "glGenTextures" , (void **)&pglGenTextures },
-{ "glDeleteTextures" , (void **)&pglDeleteTextures },
-{ "glBindTexture" , (void **)&pglBindTexture },
-{ "glTexImage1D" , (void **)&pglTexImage1D },
-{ "glTexImage2D" , (void **)&pglTexImage2D },
-{ "glTexSubImage1D" , (void **)&pglTexSubImage1D },
-{ "glTexSubImage2D" , (void **)&pglTexSubImage2D },
-{ "glCopyTexImage1D" , (void **)&pglCopyTexImage1D },
-{ "glCopyTexImage2D" , (void **)&pglCopyTexImage2D },
-{ "glCopyTexSubImage1D" , (void **)&pglCopyTexSubImage1D },
-{ "glCopyTexSubImage2D" , (void **)&pglCopyTexSubImage2D },
-{ "glScissor" , (void **)&pglScissor },
-{ "glGetTexEnviv" , (void **)&pglGetTexEnviv },
-{ "glPolygonOffset" , (void **)&pglPolygonOffset },
-{ "glPolygonMode" , (void **)&pglPolygonMode },
-{ "glPolygonStipple" , (void **)&pglPolygonStipple },
-{ "glClipPlane" , (void **)&pglClipPlane },
-{ "glGetClipPlane" , (void **)&pglGetClipPlane },
-{ "glShadeModel" , (void **)&pglShadeModel },
-{ "glFogfv" , (void **)&pglFogfv },
-{ "glFogf" , (void **)&pglFogf },
-{ "glFogi" , (void **)&pglFogi },
-{ NULL, NULL }
-};
-
-static dllfunc_t pointparametersfunc[] =
-{
-{ "glPointParameterfEXT" , (void **)&pglPointParameterfEXT },
-{ "glPointParameterfvEXT" , (void **)&pglPointParameterfvEXT },
-{ NULL, NULL }
+{ "glClearColor" , (void **)&pglClearColor },
+{ "glClear" , (void **)&pglClear },
+{ "glAlphaFunc" , (void **)&pglAlphaFunc },
+{ "glBlendFunc" , (void **)&pglBlendFunc },
+{ "glCullFace" , (void **)&pglCullFace },
+{ "glDrawBuffer" , (void **)&pglDrawBuffer },
+{ "glReadBuffer" , (void **)&pglReadBuffer },
+{ "glAccum" , (void **)&pglAccum },
+{ "glEnable" , (void **)&pglEnable },
+{ "glDisable" , (void **)&pglDisable },
+{ "glEnableClientState" , (void **)&pglEnableClientState },
+{ "glDisableClientState" , (void **)&pglDisableClientState },
+{ "glGetBooleanv" , (void **)&pglGetBooleanv },
+{ "glGetDoublev" , (void **)&pglGetDoublev },
+{ "glGetFloatv" , (void **)&pglGetFloatv },
+{ "glGetIntegerv" , (void **)&pglGetIntegerv },
+{ "glGetError" , (void **)&pglGetError },
+{ "glGetString" , (void **)&pglGetString },
+{ "glFinish" , (void **)&pglFinish },
+{ "glFlush" , (void **)&pglFlush },
+{ "glClearDepth" , (void **)&pglClearDepth },
+{ "glDepthFunc" , (void **)&pglDepthFunc },
+{ "glDepthMask" , (void **)&pglDepthMask },
+{ "glDepthRange" , (void **)&pglDepthRange },
+{ "glFrontFace" , (void **)&pglFrontFace },
+{ "glDrawElements" , (void **)&pglDrawElements },
+{ "glDrawArrays" , (void **)&pglDrawArrays },
+{ "glColorMask" , (void **)&pglColorMask },
+{ "glIndexPointer" , (void **)&pglIndexPointer },
+{ "glVertexPointer" , (void **)&pglVertexPointer },
+{ "glNormalPointer" , (void **)&pglNormalPointer },
+{ "glColorPointer" , (void **)&pglColorPointer },
+{ "glTexCoordPointer" , (void **)&pglTexCoordPointer },
+{ "glArrayElement" , (void **)&pglArrayElement },
+{ "glColor3f" , (void **)&pglColor3f },
+{ "glColor3fv" , (void **)&pglColor3fv },
+{ "glColor4f" , (void **)&pglColor4f },
+{ "glColor4fv" , (void **)&pglColor4fv },
+{ "glColor3ub" , (void **)&pglColor3ub },
+{ "glColor4ub" , (void **)&pglColor4ub },
+{ "glColor4ubv" , (void **)&pglColor4ubv },
+{ "glTexCoord1f" , (void **)&pglTexCoord1f },
+{ "glTexCoord2f" , (void **)&pglTexCoord2f },
+{ "glTexCoord3f" , (void **)&pglTexCoord3f },
+{ "glTexCoord4f" , (void **)&pglTexCoord4f },
+{ "glTexCoord1fv" , (void **)&pglTexCoord1fv },
+{ "glTexCoord2fv" , (void **)&pglTexCoord2fv },
+{ "glTexCoord3fv" , (void **)&pglTexCoord3fv },
+{ "glTexCoord4fv" , (void **)&pglTexCoord4fv },
+{ "glTexGenf" , (void **)&pglTexGenf },
+{ "glTexGenfv" , (void **)&pglTexGenfv },
+{ "glTexGeni" , (void **)&pglTexGeni },
+{ "glVertex2f" , (void **)&pglVertex2f },
+{ "glVertex3f" , (void **)&pglVertex3f },
+{ "glVertex3fv" , (void **)&pglVertex3fv },
+{ "glNormal3f" , (void **)&pglNormal3f },
+{ "glNormal3fv" , (void **)&pglNormal3fv },
+{ "glBegin" , (void **)&pglBegin },
+{ "glEnd" , (void **)&pglEnd },
+{ "glLineWidth" , (void**)&pglLineWidth },
+{ "glPointSize" , (void**)&pglPointSize },
+{ "glMatrixMode" , (void **)&pglMatrixMode },
+{ "glOrtho" , (void **)&pglOrtho },
+{ "glRasterPos2f" , (void **) &pglRasterPos2f },
+{ "glFrustum" , (void **)&pglFrustum },
+{ "glViewport" , (void **)&pglViewport },
+{ "glPushMatrix" , (void **)&pglPushMatrix },
+{ "glPopMatrix" , (void **)&pglPopMatrix },
+{ "glPushAttrib" , (void **)&pglPushAttrib },
+{ "glPopAttrib" , (void **)&pglPopAttrib },
+{ "glLoadIdentity" , (void **)&pglLoadIdentity },
+{ "glLoadMatrixd" , (void **)&pglLoadMatrixd },
+{ "glLoadMatrixf" , (void **)&pglLoadMatrixf },
+{ "glMultMatrixd" , (void **)&pglMultMatrixd },
+{ "glMultMatrixf" , (void **)&pglMultMatrixf },
+{ "glRotated" , (void **)&pglRotated },
+{ "glRotatef" , (void **)&pglRotatef },
+{ "glScaled" , (void **)&pglScaled },
+{ "glScalef" , (void **)&pglScalef },
+{ "glTranslated" , (void **)&pglTranslated },
+{ "glTranslatef" , (void **)&pglTranslatef },
+{ "glReadPixels" , (void **)&pglReadPixels },
+{ "glDrawPixels" , (void **)&pglDrawPixels },
+{ "glStencilFunc" , (void **)&pglStencilFunc },
+{ "glStencilMask" , (void **)&pglStencilMask },
+{ "glStencilOp" , (void **)&pglStencilOp },
+{ "glClearStencil" , (void **)&pglClearStencil },
+{ "glIsEnabled" , (void **)&pglIsEnabled },
+{ "glIsList" , (void **)&pglIsList },
+{ "glIsTexture" , (void **)&pglIsTexture },
+{ "glTexEnvf" , (void **)&pglTexEnvf },
+{ "glTexEnvfv" , (void **)&pglTexEnvfv },
+{ "glTexEnvi" , (void **)&pglTexEnvi },
+{ "glTexParameterf" , (void **)&pglTexParameterf },
+{ "glTexParameterfv" , (void **)&pglTexParameterfv },
+{ "glTexParameteri" , (void **)&pglTexParameteri },
+{ "glHint" , (void **)&pglHint },
+{ "glPixelStoref" , (void **)&pglPixelStoref },
+{ "glPixelStorei" , (void **)&pglPixelStorei },
+{ "glGenTextures" , (void **)&pglGenTextures },
+{ "glDeleteTextures" , (void **)&pglDeleteTextures },
+{ "glBindTexture" , (void **)&pglBindTexture },
+{ "glTexImage1D" , (void **)&pglTexImage1D },
+{ "glTexImage2D" , (void **)&pglTexImage2D },
+{ "glTexSubImage1D" , (void **)&pglTexSubImage1D },
+{ "glTexSubImage2D" , (void **)&pglTexSubImage2D },
+{ "glCopyTexImage1D" , (void **)&pglCopyTexImage1D },
+{ "glCopyTexImage2D" , (void **)&pglCopyTexImage2D },
+{ "glCopyTexSubImage1D" , (void **)&pglCopyTexSubImage1D },
+{ "glCopyTexSubImage2D" , (void **)&pglCopyTexSubImage2D },
+{ "glScissor" , (void **)&pglScissor },
+{ "glGetTexImage" , (void **)&pglGetTexImage },
+{ "glGetTexEnviv" , (void **)&pglGetTexEnviv },
+{ "glPolygonOffset" , (void **)&pglPolygonOffset },
+{ "glPolygonMode" , (void **)&pglPolygonMode },
+{ "glPolygonStipple" , (void **)&pglPolygonStipple },
+{ "glClipPlane" , (void **)&pglClipPlane },
+{ "glGetClipPlane" , (void **)&pglGetClipPlane },
+{ "glShadeModel" , (void **)&pglShadeModel },
+{ "glGetTexLevelParameteriv" , (void **)&pglGetTexLevelParameteriv },
+{ "glGetTexLevelParameterfv" , (void **)&pglGetTexLevelParameterfv },
+{ "glFogfv" , (void **)&pglFogfv },
+{ "glFogf" , (void **)&pglFogf },
+{ "glFogi" , (void **)&pglFogi },
+{ NULL , NULL }
};
static dllfunc_t drawrangeelementsfuncs[] =
{
-{ "glDrawRangeElements" , (void **)&pglDrawRangeElements },
-{ NULL, NULL }
+{ "glDrawRangeElements" , (void **)&pglDrawRangeElements },
+{ NULL , NULL }
};
static dllfunc_t drawrangeelementsextfuncs[] =
{
-{ "glDrawRangeElementsEXT" , (void **)&pglDrawRangeElementsEXT },
-{ NULL, NULL }
+{ "glDrawRangeElementsEXT" , (void **)&pglDrawRangeElementsEXT },
+{ NULL , NULL }
};
-static dllfunc_t sgis_multitexturefuncs[] =
+static dllfunc_t debugoutputfuncs[] =
{
-{ "glSelectTextureSGIS" , (void **)&pglSelectTextureSGIS },
-{ "glMTexCoord2fSGIS" , (void **)&pglMTexCoord2fSGIS },
-{ NULL, NULL }
+{ "glDebugMessageControlARB" , (void **)&pglDebugMessageControlARB },
+{ "glDebugMessageInsertARB" , (void **)&pglDebugMessageInsertARB },
+{ "glDebugMessageCallbackARB" , (void **)&pglDebugMessageCallbackARB },
+{ "glGetDebugMessageLogARB" , (void **)&pglGetDebugMessageLogARB },
+{ NULL , NULL }
};
static dllfunc_t multitexturefuncs[] =
{
-{ "glMultiTexCoord1fARB" , (void **)&pglMultiTexCoord1f },
-{ "glMultiTexCoord2fARB" , (void **)&pglMultiTexCoord2f },
-{ "glMultiTexCoord3fARB" , (void **)&pglMultiTexCoord3f },
-{ "glMultiTexCoord4fARB" , (void **)&pglMultiTexCoord4f },
-{ "glActiveTextureARB" , (void **)&pglActiveTextureARB },
-{ "glClientActiveTextureARB" , (void **)&pglClientActiveTexture },
-{ "glClientActiveTextureARB" , (void **)&pglClientActiveTextureARB },
-{ NULL, NULL }
-};
-
-static dllfunc_t compiledvertexarrayfuncs[] =
-{
-{ "glLockArraysEXT" , (void **)&pglLockArraysEXT },
-{ "glUnlockArraysEXT" , (void **)&pglUnlockArraysEXT },
-{ "glDrawArrays" , (void **)&pglDrawArrays },
-{ NULL, NULL }
+{ "glMultiTexCoord1fARB" , (void **)&pglMultiTexCoord1f },
+{ "glMultiTexCoord2fARB" , (void **)&pglMultiTexCoord2f },
+{ "glMultiTexCoord3fARB" , (void **)&pglMultiTexCoord3f },
+{ "glMultiTexCoord4fARB" , (void **)&pglMultiTexCoord4f },
+{ "glActiveTextureARB" , (void **)&pglActiveTexture },
+{ "glActiveTextureARB" , (void **)&pglActiveTextureARB },
+{ "glClientActiveTextureARB" , (void **)&pglClientActiveTexture },
+{ "glClientActiveTextureARB" , (void **)&pglClientActiveTextureARB },
+{ NULL , NULL }
};
static dllfunc_t texture3dextfuncs[] =
{
-{ "glTexImage3DEXT" , (void **)&pglTexImage3D },
-{ "glTexSubImage3DEXT" , (void **)&pglTexSubImage3D },
-{ "glCopyTexSubImage3DEXT" , (void **)&pglCopyTexSubImage3D },
-{ NULL, NULL }
+{ "glTexImage3DEXT" , (void **)&pglTexImage3D },
+{ "glTexSubImage3DEXT" , (void **)&pglTexSubImage3D },
+{ "glCopyTexSubImage3DEXT" , (void **)&pglCopyTexSubImage3D },
+{ NULL , NULL }
};
-static dllfunc_t atiseparatestencilfuncs[] =
+static dllfunc_t shaderobjectsfuncs[] =
{
-{ "glStencilOpSeparateATI" , (void **)&pglStencilOpSeparate },
-{ "glStencilFuncSeparateATI" , (void **)&pglStencilFuncSeparate },
-{ NULL, NULL }
+{ "glDeleteObjectARB" , (void **)&pglDeleteObjectARB },
+{ "glGetHandleARB" , (void **)&pglGetHandleARB },
+{ "glDetachObjectARB" , (void **)&pglDetachObjectARB },
+{ "glCreateShaderObjectARB" , (void **)&pglCreateShaderObjectARB },
+{ "glShaderSourceARB" , (void **)&pglShaderSourceARB },
+{ "glCompileShaderARB" , (void **)&pglCompileShaderARB },
+{ "glCreateProgramObjectARB" , (void **)&pglCreateProgramObjectARB },
+{ "glAttachObjectARB" , (void **)&pglAttachObjectARB },
+{ "glLinkProgramARB" , (void **)&pglLinkProgramARB },
+{ "glUseProgramObjectARB" , (void **)&pglUseProgramObjectARB },
+{ "glValidateProgramARB" , (void **)&pglValidateProgramARB },
+{ "glUniform1fARB" , (void **)&pglUniform1fARB },
+{ "glUniform2fARB" , (void **)&pglUniform2fARB },
+{ "glUniform3fARB" , (void **)&pglUniform3fARB },
+{ "glUniform4fARB" , (void **)&pglUniform4fARB },
+{ "glUniform1iARB" , (void **)&pglUniform1iARB },
+{ "glUniform2iARB" , (void **)&pglUniform2iARB },
+{ "glUniform3iARB" , (void **)&pglUniform3iARB },
+{ "glUniform4iARB" , (void **)&pglUniform4iARB },
+{ "glUniform1fvARB" , (void **)&pglUniform1fvARB },
+{ "glUniform2fvARB" , (void **)&pglUniform2fvARB },
+{ "glUniform3fvARB" , (void **)&pglUniform3fvARB },
+{ "glUniform4fvARB" , (void **)&pglUniform4fvARB },
+{ "glUniform1ivARB" , (void **)&pglUniform1ivARB },
+{ "glUniform2ivARB" , (void **)&pglUniform2ivARB },
+{ "glUniform3ivARB" , (void **)&pglUniform3ivARB },
+{ "glUniform4ivARB" , (void **)&pglUniform4ivARB },
+{ "glUniformMatrix2fvARB" , (void **)&pglUniformMatrix2fvARB },
+{ "glUniformMatrix3fvARB" , (void **)&pglUniformMatrix3fvARB },
+{ "glUniformMatrix4fvARB" , (void **)&pglUniformMatrix4fvARB },
+{ "glGetObjectParameterfvARB" , (void **)&pglGetObjectParameterfvARB },
+{ "glGetObjectParameterivARB" , (void **)&pglGetObjectParameterivARB },
+{ "glGetInfoLogARB" , (void **)&pglGetInfoLogARB },
+{ "glGetAttachedObjectsARB" , (void **)&pglGetAttachedObjectsARB },
+{ "glGetUniformLocationARB" , (void **)&pglGetUniformLocationARB },
+{ "glGetActiveUniformARB" , (void **)&pglGetActiveUniformARB },
+{ "glGetUniformfvARB" , (void **)&pglGetUniformfvARB },
+{ "glGetUniformivARB" , (void **)&pglGetUniformivARB },
+{ "glGetShaderSourceARB" , (void **)&pglGetShaderSourceARB },
+{ "glVertexAttribPointerARB" , (void **)&pglVertexAttribPointerARB },
+{ "glEnableVertexAttribArrayARB" , (void **)&pglEnableVertexAttribArrayARB },
+{ "glDisableVertexAttribArrayARB" , (void **)&pglDisableVertexAttribArrayARB },
+{ "glBindAttribLocationARB" , (void **)&pglBindAttribLocationARB },
+{ "glGetActiveAttribARB" , (void **)&pglGetActiveAttribARB },
+{ "glGetAttribLocationARB" , (void **)&pglGetAttribLocationARB },
+{ "glVertexAttrib2f" , (void **)&pglVertexAttrib2fARB },
+{ "glVertexAttrib2fv" , (void **)&pglVertexAttrib2fvARB },
+{ "glVertexAttrib3fv" , (void **)&pglVertexAttrib3fvARB },
+{ NULL , NULL }
};
-static dllfunc_t gl2separatestencilfuncs[] =
+static dllfunc_t vbofuncs[] =
{
-{ "glStencilOpSeparate" , (void **)&pglStencilOpSeparate },
-{ "glStencilFuncSeparate" , (void **)&pglStencilFuncSeparate },
-{ NULL, NULL }
+{ "glBindBufferARB" , (void **)&pglBindBufferARB },
+{ "glDeleteBuffersARB" , (void **)&pglDeleteBuffersARB },
+{ "glGenBuffersARB" , (void **)&pglGenBuffersARB },
+{ "glIsBufferARB" , (void **)&pglIsBufferARB },
+{ "glMapBufferARB" , (void **)&pglMapBufferARB },
+{ "glUnmapBufferARB" , (void **)&pglUnmapBufferARB },
+{ "glBufferDataARB" , (void **)&pglBufferDataARB },
+{ "glBufferSubDataARB" , (void **)&pglBufferSubDataARB },
+{ NULL , NULL}
};
-static dllfunc_t stenciltwosidefuncs[] =
+static dllfunc_t vaofuncs[] =
{
-{ "glActiveStencilFaceEXT" , (void **)&pglActiveStencilFaceEXT },
-{ NULL, NULL }
+{ "glBindVertexArray" , (void **)&pglBindVertexArray },
+{ "glDeleteVertexArrays" , (void **)&pglDeleteVertexArrays },
+{ "glGenVertexArrays" , (void **)&pglGenVertexArrays },
+{ "glIsVertexArray" , (void **)&pglIsVertexArray },
+{ NULL , NULL }
};
-static dllfunc_t blendequationfuncs[] =
+static dllfunc_t arbfbofuncs[] =
{
-{ "glBlendEquationEXT" , (void **)&pglBlendEquationEXT },
-{ NULL, NULL }
+{ "glIsRenderbuffer" , (void **)&pglIsRenderbuffer },
+{ "glBindRenderbuffer" , (void **)&pglBindRenderbuffer },
+{ "glDeleteRenderbuffers" , (void **)&pglDeleteRenderbuffers },
+{ "glGenRenderbuffers" , (void **)&pglGenRenderbuffers },
+{ "glRenderbufferStorage" , (void **)&pglRenderbufferStorage },
+{ "glRenderbufferStorageMultisample" , (void **)&pglRenderbufferStorageMultisample }, // not in GL_EXT_framebuffer_object
+{ "glGetRenderbufferParameteriv" , (void **)&pglGetRenderbufferParameteriv },
+{ "glIsFramebuffer" , (void **)&pglIsFramebuffer },
+{ "glBindFramebuffer" , (void **)&pglBindFramebuffer },
+{ "glDeleteFramebuffers" , (void **)&pglDeleteFramebuffers },
+{ "glGenFramebuffers" , (void **)&pglGenFramebuffers },
+{ "glCheckFramebufferStatus" , (void **)&pglCheckFramebufferStatus },
+{ "glFramebufferTexture1D" , (void **)&pglFramebufferTexture1D },
+{ "glFramebufferTexture2D" , (void **)&pglFramebufferTexture2D },
+{ "glFramebufferTexture3D" , (void **)&pglFramebufferTexture3D },
+{ "glFramebufferTextureLayer" , (void **)&pglFramebufferTextureLayer }, // not in GL_EXT_framebuffer_object
+{ "glFramebufferRenderbuffer" , (void **)&pglFramebufferRenderbuffer },
+{ "glGetFramebufferAttachmentParameteriv" , (void **)&pglGetFramebufferAttachmentParameteriv },
+{ "glBlitFramebuffer" , (void **)&pglBlitFramebuffer }, // not in GL_EXT_framebuffer_object
+{ "glGenerateMipmap" , (void **)&pglGenerateMipmap },
+{ NULL , NULL}
};
-static dllfunc_t shaderobjectsfuncs[] =
+static dllfunc_t extfbofuncs[] =
{
-{ "glDeleteObjectARB" , (void **)&pglDeleteObjectARB },
-{ "glGetHandleARB" , (void **)&pglGetHandleARB },
-{ "glDetachObjectARB" , (void **)&pglDetachObjectARB },
-{ "glCreateShaderObjectARB" , (void **)&pglCreateShaderObjectARB },
-{ "glShaderSourceARB" , (void **)&pglShaderSourceARB },
-{ "glCompileShaderARB" , (void **)&pglCompileShaderARB },
-{ "glCreateProgramObjectARB" , (void **)&pglCreateProgramObjectARB },
-{ "glAttachObjectARB" , (void **)&pglAttachObjectARB },
-{ "glLinkProgramARB" , (void **)&pglLinkProgramARB },
-{ "glUseProgramObjectARB" , (void **)&pglUseProgramObjectARB },
-{ "glValidateProgramARB" , (void **)&pglValidateProgramARB },
-{ "glUniform1fARB" , (void **)&pglUniform1fARB },
-{ "glUniform2fARB" , (void **)&pglUniform2fARB },
-{ "glUniform3fARB" , (void **)&pglUniform3fARB },
-{ "glUniform4fARB" , (void **)&pglUniform4fARB },
-{ "glUniform1iARB" , (void **)&pglUniform1iARB },
-{ "glUniform2iARB" , (void **)&pglUniform2iARB },
-{ "glUniform3iARB" , (void **)&pglUniform3iARB },
-{ "glUniform4iARB" , (void **)&pglUniform4iARB },
-{ "glUniform1fvARB" , (void **)&pglUniform1fvARB },
-{ "glUniform2fvARB" , (void **)&pglUniform2fvARB },
-{ "glUniform3fvARB" , (void **)&pglUniform3fvARB },
-{ "glUniform4fvARB" , (void **)&pglUniform4fvARB },
-{ "glUniform1ivARB" , (void **)&pglUniform1ivARB },
-{ "glUniform2ivARB" , (void **)&pglUniform2ivARB },
-{ "glUniform3ivARB" , (void **)&pglUniform3ivARB },
-{ "glUniform4ivARB" , (void **)&pglUniform4ivARB },
-{ "glUniformMatrix2fvARB" , (void **)&pglUniformMatrix2fvARB },
-{ "glUniformMatrix3fvARB" , (void **)&pglUniformMatrix3fvARB },
-{ "glUniformMatrix4fvARB" , (void **)&pglUniformMatrix4fvARB },
-{ "glGetObjectParameterfvARB" , (void **)&pglGetObjectParameterfvARB },
-{ "glGetObjectParameterivARB" , (void **)&pglGetObjectParameterivARB },
-{ "glGetInfoLogARB" , (void **)&pglGetInfoLogARB },
-{ "glGetAttachedObjectsARB" , (void **)&pglGetAttachedObjectsARB },
-{ "glGetUniformLocationARB" , (void **)&pglGetUniformLocationARB },
-{ "glGetActiveUniformARB" , (void **)&pglGetActiveUniformARB },
-{ "glGetUniformfvARB" , (void **)&pglGetUniformfvARB },
-{ "glGetUniformivARB" , (void **)&pglGetUniformivARB },
-{ "glGetShaderSourceARB" , (void **)&pglGetShaderSourceARB },
-{ "glVertexAttribPointerARB" , (void **)&pglVertexAttribPointerARB },
-{ "glEnableVertexAttribArrayARB" , (void **)&pglEnableVertexAttribArrayARB },
-{ "glDisableVertexAttribArrayARB" , (void **)&pglDisableVertexAttribArrayARB },
-{ "glBindAttribLocationARB" , (void **)&pglBindAttribLocationARB },
-{ "glGetActiveAttribARB" , (void **)&pglGetActiveAttribARB },
-{ "glGetAttribLocationARB" , (void **)&pglGetAttribLocationARB },
-{ NULL, NULL }
+{ "glIsRenderbufferEXT" , (void **)&pglIsRenderbuffer },
+{ "glBindRenderbufferEXT" , (void **)&pglBindRenderbuffer },
+{ "glDeleteRenderbuffersEXT" , (void **)&pglDeleteRenderbuffers },
+{ "glGenRenderbuffersEXT" , (void **)&pglGenRenderbuffers },
+{ "glRenderbufferStorageEXT" , (void **)&pglRenderbufferStorage },
+{ "glGetRenderbufferParameterivEXT" , (void **)&pglGetRenderbufferParameteriv },
+{ "glIsFramebufferEXT" , (void **)&pglIsFramebuffer },
+{ "glBindFramebufferEXT" , (void **)&pglBindFramebuffer },
+{ "glDeleteFramebuffersEXT" , (void **)&pglDeleteFramebuffers },
+{ "glGenFramebuffersEXT" , (void **)&pglGenFramebuffers },
+{ "glCheckFramebufferStatusEXT" , (void **)&pglCheckFramebufferStatus },
+{ "glFramebufferTexture1DEXT" , (void **)&pglFramebufferTexture1D },
+{ "glFramebufferTexture2DEXT" , (void **)&pglFramebufferTexture2D },
+{ "glFramebufferTexture3DEXT" , (void **)&pglFramebufferTexture3D },
+{ "glFramebufferRenderbufferEXT" , (void **)&pglFramebufferRenderbuffer },
+{ "glGetFramebufferAttachmentParameterivEXT" , (void **)&pglGetFramebufferAttachmentParameteriv },
+{ "glGenerateMipmapEXT" , (void **)&pglGenerateMipmap },
+{ NULL, NULL}
};
-static dllfunc_t vertexshaderfuncs[] =
+static dllfunc_t occlusionfunc[] =
{
-{ "glVertexAttribPointerARB" , (void **)&pglVertexAttribPointerARB },
-{ "glEnableVertexAttribArrayARB" , (void **)&pglEnableVertexAttribArrayARB },
-{ "glDisableVertexAttribArrayARB" , (void **)&pglDisableVertexAttribArrayARB },
-{ "glBindAttribLocationARB" , (void **)&pglBindAttribLocationARB },
-{ "glGetActiveAttribARB" , (void **)&pglGetActiveAttribARB },
-{ "glGetAttribLocationARB" , (void **)&pglGetAttribLocationARB },
-{ NULL, NULL }
+{ "glGenQueriesARB" , (void **)&pglGenQueriesARB },
+{ "glDeleteQueriesARB" , (void **)&pglDeleteQueriesARB },
+{ "glIsQueryARB" , (void **)&pglIsQueryARB },
+{ "glBeginQueryARB" , (void **)&pglBeginQueryARB },
+{ "glEndQueryARB" , (void **)&pglEndQueryARB },
+{ "glGetQueryivARB" , (void **)&pglGetQueryivARB },
+{ "glGetQueryObjectivARB" , (void **)&pglGetQueryObjectivARB },
+{ "glGetQueryObjectuivARB" , (void **)&pglGetQueryObjectuivARB },
+{ NULL , NULL }
};
-static dllfunc_t vbofuncs[] =
+static dllfunc_t texturecompressionfuncs[] =
{
-{ "glBindBufferARB" , (void **)&pglBindBufferARB },
-{ "glDeleteBuffersARB" , (void **)&pglDeleteBuffersARB },
-{ "glGenBuffersARB" , (void **)&pglGenBuffersARB },
-{ "glIsBufferARB" , (void **)&pglIsBufferARB },
-{ "glMapBufferARB" , (void **)&pglMapBufferARB },
-{ "glUnmapBufferARB" , (void **)&pglUnmapBufferARB },
-{ "glBufferDataARB" , (void **)&pglBufferDataARB },
-{ "glBufferSubDataARB" , (void **)&pglBufferSubDataARB },
-{ NULL, NULL}
+{ "glCompressedTexImage3DARB" , (void **)&pglCompressedTexImage3DARB },
+{ "glCompressedTexImage2DARB" , (void **)&pglCompressedTexImage2DARB },
+{ "glCompressedTexImage1DARB" , (void **)&pglCompressedTexImage1DARB },
+{ "glCompressedTexSubImage3DARB" , (void **)&pglCompressedTexSubImage3DARB },
+{ "glCompressedTexSubImage2DARB" , (void **)&pglCompressedTexSubImage2DARB },
+{ "glCompressedTexSubImage1DARB" , (void **)&pglCompressedTexSubImage1DARB },
+{ "glGetCompressedTexImageARB" , (void **)&pglGetCompressedTexImage },
+{ NULL , NULL }
};
-static dllfunc_t occlusionfunc[] =
+static dllfunc_t drawbuffersfuncs[] =
{
-{ "glGenQueriesARB" , (void **)&pglGenQueriesARB },
-{ "glDeleteQueriesARB" , (void **)&pglDeleteQueriesARB },
-{ "glIsQueryARB" , (void **)&pglIsQueryARB },
-{ "glBeginQueryARB" , (void **)&pglBeginQueryARB },
-{ "glEndQueryARB" , (void **)&pglEndQueryARB },
-{ "glGetQueryivARB" , (void **)&pglGetQueryivARB },
-{ "glGetQueryObjectivARB" , (void **)&pglGetQueryObjectivARB },
-{ "glGetQueryObjectuivARB" , (void **)&pglGetQueryObjectuivARB },
-{ NULL, NULL }
+{ "glDrawBuffersARB" , (void **)&pglDrawBuffersARB },
+{ NULL , NULL }
};
-static dllfunc_t texturecompressionfuncs[] =
+static dllfunc_t wgl_funcs[] =
{
-{ "glCompressedTexImage3DARB" , (void **)&pglCompressedTexImage3DARB },
-{ "glCompressedTexImage2DARB" , (void **)&pglCompressedTexImage2DARB },
-{ "glCompressedTexImage1DARB" , (void **)&pglCompressedTexImage1DARB },
-{ "glCompressedTexSubImage3DARB" , (void **)&pglCompressedTexSubImage3DARB },
-{ "glCompressedTexSubImage2DARB" , (void **)&pglCompressedTexSubImage2DARB },
-{ "glCompressedTexSubImage1DARB" , (void **)&pglCompressedTexSubImage1DARB },
-{ "glGetCompressedTexImageARB" , (void **)&pglGetCompressedTexImage },
-{ NULL, NULL }
+{ "wglSwapBuffers" , (void **)&pwglSwapBuffers },
+{ "wglCreateContext" , (void **)&pwglCreateContext },
+{ "wglDeleteContext" , (void **)&pwglDeleteContext },
+{ "wglMakeCurrent" , (void **)&pwglMakeCurrent },
+{ "wglGetCurrentContext" , (void **)&pwglGetCurrentContext },
+{ NULL , NULL }
};
-static dllfunc_t wgl_funcs[] =
+static dllfunc_t wglproc_funcs[] =
{
-{ "wglSwapBuffers" , (void **)&pwglSwapBuffers },
-{ "wglCreateContext" , (void **)&pwglCreateContext },
-{ "wglDeleteContext" , (void **)&pwglDeleteContext },
-{ "wglMakeCurrent" , (void **)&pwglMakeCurrent },
-{ "wglGetCurrentContext" , (void **)&pwglGetCurrentContext },
+{ "wglGetProcAddress" , (void **)&pwglGetProcAddress },
{ NULL, NULL }
};
-static dllfunc_t wglproc_funcs[] =
+static dllfunc_t wglswapintervalfuncs[] =
{
-{ "wglGetProcAddress" , (void **)&pwglGetProcAddress },
+{ "wglSwapIntervalEXT" , (void **)&pwglSwapIntervalEXT },
{ NULL, NULL }
};
-static dllfunc_t wglswapintervalfuncs[] =
+static dllfunc_t wglgetextensionsstring[] =
{
-{ "wglSwapIntervalEXT" , (void **)&pwglSwapIntervalEXT },
+{ "wglGetExtensionsStringEXT" , (void **)&pwglGetExtensionsStringEXT },
{ NULL, NULL }
};
dll_info_t opengl_dll = { "opengl32.dll", wgl_funcs, true };
/*
+========================
+DebugCallback
+
+For ARB_debug_output
+========================
+*/
+static void CALLBACK GL_DebugOutput( GLuint source, GLuint type, GLuint id, GLuint severity, GLint length, const GLcharARB *message, GLvoid *userParam )
+{
+ switch( type )
+ {
+ case GL_DEBUG_TYPE_ERROR_ARB:
+ if( host.developer < D_ERROR ) // "-dev 2"
+ return;
+ Con_Printf( "^1OpenGL Error:^7 %s\n", message );
+ break;
+ case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB:
+ if( host.developer < D_WARN ) // "-dev 3"
+ return;
+ Con_Printf( "^3OpenGL Warning:^7 %s\n", message );
+ break;
+ case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB:
+ if( host.developer < D_WARN ) // "-dev 3"
+ return;
+ Con_Printf( "^3OpenGL Warning:^7 %s\n", message );
+ break;
+ case GL_DEBUG_TYPE_PORTABILITY_ARB:
+ if( host.developer < D_REPORT ) // "-dev 4"
+ return;
+ Con_Printf( "^3OpenGL Warning:^7 %s\n", message );
+ break;
+ case GL_DEBUG_TYPE_PERFORMANCE_ARB:
+ if( host.developer < D_REPORT ) // "-dev 4"
+ return;
+ Con_Printf( "OpenGL Notify: %s\n", message );
+ break;
+ case GL_DEBUG_TYPE_OTHER_ARB:
+ default: if( host.developer < D_NOTE ) // "-dev 5"
+ return;
+ Con_Printf( "OpenGL: %s\n", message );
+ break;
+ }
+}
+
+/*
=================
GL_SetExtension
=================
@@ -502,7 +569,7 @@ GL_MaxTextureUnits
int GL_MaxTextureUnits( void )
{
if( GL_Support( GL_SHADER_GLSL100_EXT ))
- return min( max( glConfig.max_texture_coords, glConfig.max_teximage_units ), MAX_TEXTURE_UNITS );
+ return Q_min( Q_max( glConfig.max_texture_coords, glConfig.max_teximage_units ), MAX_TEXTURE_UNITS );
return glConfig.max_texture_units;
}
@@ -531,6 +598,7 @@ void GL_CheckExtension( const char *name, const dllfunc_t *funcs, const char *cv
{
const dllfunc_t *func;
convar_t *parm;
+ const char *extensions_string;
MsgDev( D_NOTE, "GL_CheckExtension: %s ", name );
@@ -548,7 +616,12 @@ void GL_CheckExtension( const char *name, const dllfunc_t *funcs, const char *cv
GL_SetExtension( r_ext, 1 );
}
- if(( name[2] == '_' || name[3] == '_' ) && !Q_strstr( glConfig.extensions_string, name ))
+ extensions_string = glConfig.extensions_string;
+
+ if( name[0] == 'W' && name[1] == 'G' && name[2] == 'L' && glConfig.wgl_extensions_string != NULL )
+ extensions_string = glConfig.wgl_extensions_string;
+
+ if(( name[2] == '_' || name[3] == '_' ) && !Q_strstr( extensions_string, name ))
{
GL_SetExtension( r_ext, false ); // update render info
MsgDev( D_NOTE, "- ^1failed\n" );
@@ -579,8 +652,9 @@ GL_BuildGammaTable
*/
void GL_BuildGammaTable( void )
{
+ double invGamma;
+ double div;
int i, v;
- double invGamma, div;
invGamma = 1.0 / bound( 0.5, vid_gamma->value, 2.3 );
div = (double) 1.0 / 255.5;
@@ -699,13 +773,15 @@ qboolean GL_CreateContext( void )
{
HGLRC hBaseRC;
+ glw_state.extended = false;
+
if(!( glw_state.hGLRC = pwglCreateContext( glw_state.hDC )))
return GL_DeleteContext();
if(!( pwglMakeCurrent( glw_state.hDC, glw_state.hGLRC )))
return GL_DeleteContext();
- if( !Sys_CheckParm( "-gldebug" ) || host.developer < 1 ) // debug bit the kills perfomance
+ if( !Sys_CheckParm( "-gldebug" ) || host.developer < D_INFO ) // debug bit the kills perfomance
return true;
pwglCreateContextAttribsARB = GL_GetProcAddress( "wglCreateContextAttribsARB" );
@@ -741,6 +817,7 @@ qboolean GL_CreateContext( void )
MsgDev( D_NOTE, "GL_CreateContext: using extended context\n" );
pwglDeleteContext( hBaseRC ); // release first context
+ glw_state.extended = true;
}
return true;
@@ -762,6 +839,8 @@ qboolean GL_UpdateContext( void )
/*
=================
GL_DeleteContext
+
+always return false
=================
*/
qboolean GL_DeleteContext( void )
@@ -842,6 +921,13 @@ static int VID_ChoosePFD( PIXELFORMATDESCRIPTOR *pfd, int colorBits, int alphaBi
return pixelFormat;
}
+/*
+=================
+pfnEnumWnd
+
+callback to enumerate active windows
+=================
+*/
BOOL CALLBACK pfnEnumWnd( HWND hwnd, LPARAM lParam )
{
string wndname;
@@ -854,6 +940,11 @@ BOOL CALLBACK pfnEnumWnd( HWND hwnd, LPARAM lParam )
return true;
}
+/*
+=================
+VID_EnumerateInstances
+=================
+*/
uint VID_EnumerateInstances( void )
{
num_instances = 0;
@@ -863,6 +954,11 @@ uint VID_EnumerateInstances( void )
return 1;
}
+/*
+=================
+VID_StartupGamma
+=================
+*/
void VID_StartupGamma( void )
{
size_t gamma_size;
@@ -882,14 +978,11 @@ void VID_StartupGamma( void )
if( gl_ignorehwgamma->integer )
{
glConfig.deviceSupportsGamma = false; // even if supported!
- BuildGammaTable( vid_gamma->value, vid_texgamma->value );
+ BuildGammaTable( vid_gamma->value, GAMMA );
MsgDev( D_NOTE, "VID_StartupGamma: software gamma initialized\n" );
return;
}
- // share this extension so engine can grab them
- GL_SetExtension( GL_HARDWARE_GAMMA_CONTROL, glConfig.deviceSupportsGamma );
-
savedGamma = FS_LoadFile( "gamma.dat", &gamma_size, false );
if( !savedGamma || gamma_size != sizeof( glState.stateRamp ))
@@ -956,6 +1049,11 @@ void VID_StartupGamma( void )
vid_gamma->modified = true;
}
+/*
+=================
+VID_RestoreGamma
+=================
+*/
void VID_RestoreGamma( void )
{
if( !glw_state.hDC || !glConfig.deviceSupportsGamma )
@@ -1021,12 +1119,6 @@ qboolean GL_SetPixelformat( void )
if( PFD.dwFlags & PFD_GENERIC_ACCELERATED )
{
MsgDev( D_NOTE, "VID_ChoosePFD: using Generic MCD acceleration\n" );
- glw_state.software = false;
- }
- else if( gl_allow_software->integer )
- {
- MsgDev( D_NOTE, "VID_ChoosePFD: using software emulation\n" );
- glw_state.software = true;
}
else
{
@@ -1037,7 +1129,6 @@ qboolean GL_SetPixelformat( void )
else
{
MsgDev( D_NOTE, "VID_ChoosePFD: using hardware acceleration\n");
- glw_state.software = false;
}
glConfig.color_bits = PFD.cColorBits;
@@ -1055,6 +1146,11 @@ qboolean GL_SetPixelformat( void )
return true;
}
+/*
+=================
+R_SaveVideoMode
+=================
+*/
void R_SaveVideoMode( int vid_mode )
{
int mode = bound( 0, vid_mode, num_vidmodes ); // check range
@@ -1070,6 +1166,11 @@ void R_SaveVideoMode( int vid_mode )
MsgDev( D_NOTE, "Set: %s [%dx%d]\n", vidmode[mode].desc, vidmode[mode].width, vidmode[mode].height );
}
+/*
+=================
+R_DescribeVIDMode
+=================
+*/
qboolean R_DescribeVIDMode( int width, int height )
{
int i;
@@ -1087,16 +1188,21 @@ qboolean R_DescribeVIDMode( int width, int height )
return false;
}
+/*
+=================
+VID_CreateWindow
+=================
+*/
qboolean VID_CreateWindow( int width, int height, qboolean fullscreen )
{
- WNDCLASS wc;
- RECT rect;
int x = 0, y = 0, w, h;
int stylebits = WINDOW_STYLE;
int exstyle = WINDOW_EX_STYLE;
static string wndname;
HWND window;
-
+ RECT rect;
+ WNDCLASS wc;
+
Q_strncpy( wndname, GI->title, sizeof( wndname ));
// register the frame class
@@ -1109,22 +1215,22 @@ qboolean VID_CreateWindow( int width, int height, qboolean fullscreen )
wc.hbrBackground = (void *)COLOR_3DSHADOW;
wc.lpszClassName = WINDOW_NAME;
wc.lpszMenuName = 0;
+ wc.hIcon = 0;
// find the icon file in the filesystem
if( FS_FileExists( GI->iconpath, true ))
{
- char localPath[MAX_PATH];
-
- Q_snprintf( localPath, sizeof( localPath ), "%s/%s", GI->gamedir, GI->iconpath );
- wc.hIcon = LoadImage( NULL, localPath, IMAGE_ICON, 0, 0, LR_LOADFROMFILE|LR_DEFAULTSIZE );
-
- if( !wc.hIcon )
+ if( FS_GetDiskPath( GI->iconpath, true ))
{
- MsgDev( D_INFO, "Extract %s from pak if you want to see it.\n", GI->iconpath );
- wc.hIcon = LoadIcon( host.hInst, MAKEINTRESOURCE( 101 ));
+ string localPath;
+ Q_snprintf( localPath, sizeof( localPath ), "%s/%s", GI->gamedir, GI->iconpath );
+ wc.hIcon = LoadImage( NULL, localPath, IMAGE_ICON, 0, 0, LR_LOADFROMFILE|LR_DEFAULTSIZE );
}
+ else MsgDev( D_INFO, "Extract %s from pak if you want to see it.\n", GI->iconpath );
}
- else wc.hIcon = LoadIcon( host.hInst, MAKEINTRESOURCE( 101 ));
+
+ // couldn't loaded for some reasons? use default
+ if( !wc.hIcon ) wc.hIcon = LoadIcon( host.hInst, MAKEINTRESOURCE( 101 ));
if( !RegisterClass( &wc ))
{
@@ -1172,13 +1278,13 @@ qboolean VID_CreateWindow( int width, int height, qboolean fullscreen )
if( host.hWnd != window )
{
- // probably never happens
+ // make sure what CreateWindowEx call the IN_WndProc
MsgDev( D_WARN, "VID_CreateWindow: bad hWnd for '%s'\n", wndname );
}
- // host.hWnd must be filled in IN_WndProc
if( !host.hWnd )
{
+ // host.hWnd must be filled in IN_WndProc
MsgDev( D_ERROR, "VID_CreateWindow: couldn't create '%s'\n", wndname );
return false;
}
@@ -1218,6 +1324,11 @@ qboolean VID_CreateWindow( int width, int height, qboolean fullscreen )
return true;
}
+/*
+=================
+VID_DestroyWindow
+=================
+*/
void VID_DestroyWindow( void )
{
if( pwglMakeCurrent )
@@ -1244,6 +1355,11 @@ void VID_DestroyWindow( void )
}
}
+/*
+=================
+R_ChangeDisplaySettings
+=================
+*/
rserr_t R_ChangeDisplaySettings( int vid_mode, qboolean fullscreen )
{
int width, height;
@@ -1346,7 +1462,7 @@ rserr_t R_ChangeDisplaySettings( int vid_mode, qboolean fullscreen )
return rserr_invalid_mode;
if( freq_specified )
- MsgDev( D_ERROR, "VID_SetMode: display frequency %i Hz not supported by your display\n", freq_specified );
+ MsgDev( D_ERROR, "VID_SetMode: display frequency %i Hz is not supported\n", freq_specified );
glState.fullScreen = true;
return rserr_ok;
}
@@ -1375,8 +1491,6 @@ qboolean VID_SetMode( void )
qboolean fullscreen;
rserr_t err;
- gl_swapInterval->modified = true;
-
if( vid_mode->integer == -1 ) // trying to get resolution automatically by default
{
HDC hDCScreen = GetDC( NULL );
@@ -1398,6 +1512,7 @@ qboolean VID_SetMode( void )
}
fullscreen = vid_fullscreen->integer;
+ gl_swapInterval->modified = true;
if(( err = R_ChangeDisplaySettings( vid_mode->integer, fullscreen )) == rserr_ok )
{
@@ -1447,10 +1562,10 @@ void VID_CheckChanges( void )
if( renderinfo->modified )
{
- if( !VID_SetMode())
+ if( !VID_SetMode( ))
{
- // can't initialize video subsystem
- Host_NewInstance( va("#%s", GI->gamefolder ), "fallback to dedicated mode\n" );
+ Msg( "Error: can't initialize video subsystem\n" );
+ Host_NewInstance( va("#%s", GI->gamefolder ), "stopped" );
}
else
{
@@ -1472,7 +1587,8 @@ qboolean R_Init_OpenGL( void )
if( !opengl_dll.link )
return false;
- GL_CheckExtension( "OpenGL Internal ProcAddress", wglproc_funcs, NULL, GL_WGL_PROCADDRESS );
+ if( Sys_CheckParm( "-gldebug" ) && host.developer >= 1 )
+ GL_CheckExtension( "OpenGL Internal ProcAddress", wglproc_funcs, NULL, GL_WGL_PROCADDRESS );
return VID_SetMode();
}
@@ -1493,7 +1609,7 @@ void R_Free_OpenGL( void )
Sys_FreeLibrary( &opengl_dll );
// now all extensions are disabled
- memset( glConfig.extension, 0, sizeof( glConfig.extension[0] ) * GL_EXTCOUNT );
+ memset( glConfig.extension, 0, sizeof( glConfig.extension ));
glw_state.initialized = false;
}
@@ -1504,8 +1620,6 @@ GL_SetDefaults
*/
static void GL_SetDefaults( void )
{
- int i;
-
pglFinish();
pglClearColor( 0.5f, 0.5f, 0.5f, 1.0f );
@@ -1529,39 +1643,20 @@ static void GL_SetDefaults( void )
pglPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
pglPolygonOffset( -1.0f, -2.0f );
- // properly disable multitexturing at startup
- for( i = (MAX_TEXTURE_UNITS - 1); i > 0; i-- )
- {
- if( i >= GL_MaxTextureUnits( ))
- continue;
+ GL_CleanupAllTextureUnits();
- GL_SelectTexture( i );
- pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
- pglDisable( GL_BLEND );
- pglDisable( GL_TEXTURE_2D );
- }
-
- GL_SelectTexture( 0 );
pglDisable( GL_BLEND );
pglDisable( GL_ALPHA_TEST );
pglDisable( GL_POLYGON_OFFSET_FILL );
- pglAlphaFunc( GL_GREATER, 0.0f );
+ pglAlphaFunc( GL_GEQUAL, 0.5f );
pglEnable( GL_TEXTURE_2D );
- pglShadeModel( GL_FLAT );
+ pglShadeModel( GL_SMOOTH );
pglPointSize( 1.2f );
pglLineWidth( 1.2f );
- GL_Cull( 0 );
+ GL_Cull( GL_NONE );
GL_FrontFace( 0 );
-
- R_SetTextureParameters();
-
- pglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
- pglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
-
- pglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
- pglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
}
/*
@@ -1577,19 +1672,26 @@ void R_RenderInfo_f( void )
Msg( "GL_VERSION: %s\n", glConfig.version_string );
// don't spam about extensions
- if( host.developer >= 4 )
+ if( host.developer >= D_REPORT )
+ {
Msg( "GL_EXTENSIONS: %s\n", glConfig.extensions_string );
+ if( glConfig.wgl_extensions_string != NULL )
+ Msg( "\nWGL_EXTENSIONS: %s\n", glConfig.wgl_extensions_string );
+ }
+
Msg( "GL_MAX_TEXTURE_SIZE: %i\n", glConfig.max_2d_texture_size );
if( GL_Support( GL_ARB_MULTITEXTURE ))
Msg( "GL_MAX_TEXTURE_UNITS_ARB: %i\n", glConfig.max_texture_units );
- if( GL_Support( GL_TEXTURECUBEMAP_EXT ))
+ if( GL_Support( GL_TEXTURE_CUBEMAP_EXT ))
Msg( "GL_MAX_CUBE_MAP_TEXTURE_SIZE_ARB: %i\n", glConfig.max_cubemap_size );
if( GL_Support( GL_ANISOTROPY_EXT ))
Msg( "GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT: %.1f\n", glConfig.max_texture_anisotropy );
- if( glConfig.texRectangle )
- Msg( "GL_MAX_RECTANGLE_TEXTURE_SIZE_NV: %i\n", glConfig.max_2d_rectangle_size );
+ if( GL_Support( GL_TEXTURE_2D_RECT_EXT ))
+ Msg( "GL_MAX_RECTANGLE_TEXTURE_SIZE: %i\n", glConfig.max_2d_rectangle_size );
+ if( GL_Support( GL_TEXTURE_ARRAY_EXT ))
+ Msg( "GL_MAX_ARRAY_TEXTURE_LAYERS_EXT: %i\n", glConfig.max_2d_texture_layers );
if( GL_Support( GL_SHADER_GLSL100_EXT ))
{
Msg( "GL_MAX_TEXTURE_COORDS_ARB: %i\n", glConfig.max_texture_coords );
@@ -1599,12 +1701,11 @@ void R_RenderInfo_f( void )
}
Msg( "\n" );
- Msg( "MODE: %i, %i x %i %s\n", vid_mode->integer, r_width->integer, r_height->integer );
+ Msg( "MODE: %i, %i x %i %s\n", vid_mode->integer, r_width->integer, r_height->integer, vidmode[vid_mode->integer].desc );
Msg( "GAMMA: %s\n", (glConfig.deviceSupportsGamma) ? "hardware" : "software" );
Msg( "\n" );
Msg( "PICMIP: %i\n", gl_picmip->integer );
Msg( "SKYMIP: %i\n", gl_skymip->integer );
- Msg( "TEXTUREMODE: %s\n", gl_texturemode->string );
Msg( "VERTICAL SYNC: %s\n", gl_swapInterval->integer ? "enabled" : "disabled" );
Msg( "Color %d bits, Alpha %d bits, Depth %d bits, Stencil %d bits\n", glConfig.color_bits,
glConfig.alpha_bits, glConfig.depth_bits, glConfig.stencil_bits );
@@ -1612,9 +1713,13 @@ void R_RenderInfo_f( void )
//=======================================================================
+/*
+=================
+GL_InitCommands
+=================
+*/
void GL_InitCommands( void )
{
- Cbuf_AddText( "vidlatch\n" );
Cbuf_Execute();
// system screen width and height (don't suppose for change from console at all)
@@ -1631,7 +1736,7 @@ void GL_InitCommands( void )
r_novis = Cvar_Get( "r_novis", "0", 0, "ignore vis information (perfomance test)" );
r_nocull = Cvar_Get( "r_nocull", "0", 0, "ignore frustrum culling (perfomance test)" );
r_faceplanecull = Cvar_Get( "r_faceplanecull", "1", 0, "ignore face plane culling (perfomance test)" );
- r_detailtextures = Cvar_Get( "r_detailtextures", "1", CVAR_ARCHIVE, "enable detail textures support, use \"2\" for auto-generate mapname_detail.txt" );
+ r_detailtextures = Cvar_Get( "r_detailtextures", "1", CVAR_ARCHIVE, "enable detail textures support, use '2' for autogenerate detail.txt" );
r_lockpvs = Cvar_Get( "r_lockpvs", "0", CVAR_CHEAT, "lockpvs area at current point (pvs test)" );
r_lockcull = Cvar_Get( "r_lockcull", "0", CVAR_CHEAT, "lock frustrum area at current point (cull test)" );
r_dynamic = Cvar_Get( "r_dynamic", "1", CVAR_ARCHIVE, "allow dynamic lighting (dlights, lightstyles)" );
@@ -1647,9 +1752,8 @@ void GL_InitCommands( void )
gl_picmip = Cvar_Get( "gl_picmip", "0", CVAR_GLCONFIG, "reduces resolution of textures by powers of 2" );
gl_skymip = Cvar_Get( "gl_skymip", "0", CVAR_GLCONFIG, "reduces resolution of skybox textures by powers of 2" );
gl_ignorehwgamma = Cvar_Get( "gl_ignorehwgamma", "0", CVAR_GLCONFIG, "ignore hardware gamma" );
- gl_allow_software = Cvar_Get( "gl_allow_software", "0", CVAR_ARCHIVE, "allow OpenGL software emulation" );
gl_alphabits = Cvar_Get( "gl_alphabits", "8", CVAR_GLCONFIG, "pixelformat alpha bits (0 - auto)" );
- gl_texturemode = Cvar_Get( "gl_texturemode", "GL_LINEAR_MIPMAP_LINEAR", CVAR_ARCHIVE, "texture filter" );
+ gl_texture_nearest = Cvar_Get( "gl_texture_nearest", "0", CVAR_ARCHIVE, "disable texture filter" );
gl_round_down = Cvar_Get( "gl_round_down", "0", CVAR_GLCONFIG, "down size non-power of two textures" );
gl_max_size = Cvar_Get( "gl_max_size", "512", CVAR_ARCHIVE, "no effect in Xash3D just a legacy" );
gl_stencilbits = Cvar_Get( "gl_stencilbits", "8", CVAR_GLCONFIG, "pixelformat stencil bits (0 - auto)" );
@@ -1658,19 +1762,18 @@ void GL_InitCommands( void )
gl_extensions = Cvar_Get( "gl_extensions", "1", CVAR_GLCONFIG, "allow gl_extensions" );
gl_detailscale = Cvar_Get( "gl_detailscale", "4.0", CVAR_ARCHIVE, "default scale applies while auto-generate list of detail textures" );
gl_texture_anisotropy = Cvar_Get( "gl_anisotropy", "2.0", CVAR_ARCHIVE, "textures anisotropic filter" );
- gl_texture_lodbias = Cvar_Get( "gl_texture_lodbias", "0.0", CVAR_ARCHIVE, "LOD bias for mipmapped textures" );
+ gl_texture_lodbias = Cvar_Get( "gl_texture_lodbias", "0.0", CVAR_ARCHIVE, "LOD bias for mipmapped textures (prefomance|quality)" );
gl_compress_textures = Cvar_Get( "gl_compress_textures", "0", CVAR_GLCONFIG, "compress textures to safe video memory" );
- gl_luminance_textures = Cvar_Get( "gl_luminance_textures", "0", CVAR_GLCONFIG, "force all textures to luminance" );
- gl_compensate_gamma_screenshots = Cvar_Get( "gl_compensate_gamma_screenshots", "0", CVAR_ARCHIVE, "allow to apply gamma value for screenshots and snapshots" );
- gl_keeptjunctions = Cvar_Get( "gl_keeptjunctions", "1", CVAR_ARCHIVE, "disable to reduce vertexes count but removing tjuncs causes blinking pixels" );
+ gl_compensate_gamma_screenshots = Cvar_Get( "gl_compensate_gamma_screenshots", "0", CVAR_ARCHIVE, "allow to apply gamma for screenshots" );
+ gl_keeptjunctions = Cvar_Get( "gl_keeptjunctions", "1", CVAR_ARCHIVE, "but removing tjuncs causes blinking pixels" );
gl_allow_static = Cvar_Get( "gl_allow_static", "0", CVAR_ARCHIVE, "force to drawing non-moveable brushes as part of world (save FPS)" );
gl_allow_mirrors = Cvar_Get( "gl_allow_mirrors", "1", CVAR_ARCHIVE, "allow to draw mirror surfaces" );
- gl_showtextures = Cvar_Get( "r_showtextures", "0", CVAR_CHEAT, "show all uploaded textures (type values from 1 to 13)" );
+ gl_showtextures = Cvar_Get( "r_showtextures", "0", CVAR_CHEAT, "show all uploaded textures" );
gl_finish = Cvar_Get( "gl_finish", "0", CVAR_ARCHIVE, "use glFinish instead of glFlush" );
gl_nosort = Cvar_Get( "gl_nosort", "0", CVAR_ARCHIVE, "disable sorting of translucent surfaces" );
gl_clear = Cvar_Get( "gl_clear", "0", CVAR_ARCHIVE, "clearing screen after each frame" );
gl_test = Cvar_Get( "gl_test", "0", 0, "engine developer cvar for quick testing new features" );
- gl_wireframe = Cvar_Get( "gl_wireframe", "0", 0, "show wireframe overlay" );
+ gl_wireframe = Cvar_Get( "gl_wireframe", "0", CVAR_ARCHIVE, "show wireframe overlay" );
gl_overview = Cvar_Get( "dev_overview", "0", 0, "show level overview" );
// these cvar not used by engine but some mods requires this
@@ -1680,21 +1783,28 @@ void GL_InitCommands( void )
gl_swapInterval->modified = true;
vid_gamma = Cvar_Get( "gamma", "1.0", CVAR_ARCHIVE, "gamma amount" );
- vid_texgamma = Cvar_Get( "texgamma", "2.2", CVAR_GLCONFIG, "texgamma amount (default Half-Life artwork gamma)" );
vid_mode = Cvar_Get( "vid_mode", VID_AUTOMODE, CVAR_RENDERINFO, "display resolution mode" );
vid_fullscreen = Cvar_Get( "fullscreen", "0", CVAR_RENDERINFO, "set in 1 to enable fullscreen mode" );
vid_displayfrequency = Cvar_Get ( "vid_displayfrequency", "0", CVAR_RENDERINFO, "fullscreen refresh rate" );
Cmd_AddCommand( "r_info", R_RenderInfo_f, "display renderer info" );
- Cmd_AddCommand( "texturelist", R_TextureList_f, "display loaded textures list" );
}
+/*
+=================
+GL_RemoveCommands
+=================
+*/
void GL_RemoveCommands( void )
{
Cmd_RemoveCommand( "r_info");
- Cmd_RemoveCommand( "texturelist" );
}
+/*
+=================
+GL_InitExtensions
+=================
+*/
void GL_InitExtensions( void )
{
// initialize gl extensions
@@ -1707,6 +1817,35 @@ void GL_InitExtensions( void )
glConfig.extensions_string = pglGetString( GL_EXTENSIONS );
MsgDev( D_INFO, "Video: %s\n", glConfig.renderer_string );
+ // intialize wrapper type
+ glConfig.context = CONTEXT_TYPE_GL;
+ glConfig.wrapper = GLES_WRAPPER_NONE;
+
+ if( Q_stristr( glConfig.renderer_string, "geforce" ))
+ glConfig.hardware_type = GLHW_NVIDIA;
+ else if( Q_stristr( glConfig.renderer_string, "quadro fx" ))
+ glConfig.hardware_type = GLHW_NVIDIA;
+ else if( Q_stristr(glConfig.renderer_string, "rv770" ))
+ glConfig.hardware_type = GLHW_RADEON;
+ else if( Q_stristr(glConfig.renderer_string, "radeon hd" ))
+ glConfig.hardware_type = GLHW_RADEON;
+ else if( Q_stristr( glConfig.renderer_string, "eah4850" ) || Q_stristr( glConfig.renderer_string, "eah4870" ))
+ glConfig.hardware_type = GLHW_RADEON;
+ else if( Q_stristr( glConfig.renderer_string, "radeon" ))
+ glConfig.hardware_type = GLHW_RADEON;
+ else glConfig.hardware_type = GLHW_GENERIC;
+
+ // initalize until base opengl functions loaded (old-context)
+ if( !Sys_CheckParm( "-gldebug" ) || host.developer < D_INFO )
+ GL_CheckExtension( "OpenGL Internal ProcAddress", wglproc_funcs, NULL, GL_WGL_PROCADDRESS );
+
+ // windows-specific extensions
+ GL_CheckExtension( "WGL Extensions String", wglgetextensionsstring, NULL, GL_WGL_EXTENSIONS );
+
+ if( pwglGetExtensionsStringEXT != NULL )
+ glConfig.wgl_extensions_string = pwglGetExtensionsStringEXT();
+ else glConfig.wgl_extensions_string = NULL;
+
// initalize until base opengl functions loaded
GL_CheckExtension( "WGL_EXT_swap_control", wglswapintervalfuncs, NULL, GL_WGL_SWAPCONTROL );
@@ -1715,26 +1854,15 @@ void GL_InitExtensions( void )
if( !GL_Support( GL_DRAW_RANGEELEMENTS_EXT ))
GL_CheckExtension( "GL_EXT_draw_range_elements", drawrangeelementsextfuncs, "gl_drawrangeelments", GL_DRAW_RANGEELEMENTS_EXT );
+ // we don't care if it's an extension or not, they are identical functions, so keep it simple in the rendering code
+ if( pglDrawRangeElementsEXT == NULL ) pglDrawRangeElementsEXT = pglDrawRangeElements;
+
// multitexture
glConfig.max_texture_units = glConfig.max_texture_coords = glConfig.max_teximage_units = 1;
GL_CheckExtension( "GL_ARB_multitexture", multitexturefuncs, "gl_arb_multitexture", GL_ARB_MULTITEXTURE );
if( GL_Support( GL_ARB_MULTITEXTURE ))
- {
pglGetIntegerv( GL_MAX_TEXTURE_UNITS_ARB, &glConfig.max_texture_units );
- GL_CheckExtension( "GL_ARB_texture_env_combine", NULL, "gl_texture_env_combine", GL_ENV_COMBINE_EXT );
-
- if( !GL_Support( GL_ENV_COMBINE_EXT ))
- GL_CheckExtension( "GL_EXT_texture_env_combine", NULL, "gl_texture_env_combine", GL_ENV_COMBINE_EXT );
-
- if( GL_Support( GL_ENV_COMBINE_EXT ))
- GL_CheckExtension( "GL_ARB_texture_env_dot3", NULL, "gl_texture_env_dot3", GL_DOT3_ARB_EXT );
- }
- else
- {
- GL_CheckExtension( "GL_SGIS_multitexture", sgis_multitexturefuncs, "gl_sgis_multitexture", GL_ARB_MULTITEXTURE );
- if( GL_Support( GL_ARB_MULTITEXTURE )) glConfig.max_texture_units = 2;
- }
if( glConfig.max_texture_units == 1 )
GL_SetExtension( GL_ARB_MULTITEXTURE, false );
@@ -1753,12 +1881,18 @@ void GL_InitExtensions( void )
}
}
- GL_CheckExtension( "GL_SGIS_generate_mipmap", NULL, "gl_sgis_generate_mipmaps", GL_SGIS_MIPMAPS_EXT );
+ // 2d texture array support
+ GL_CheckExtension( "GL_EXT_texture_array", texture3dextfuncs, "gl_texture_2d_array", GL_TEXTURE_ARRAY_EXT );
+
+ if( GL_Support( GL_TEXTURE_ARRAY_EXT ))
+ pglGetIntegerv( GL_MAX_ARRAY_TEXTURE_LAYERS_EXT, &glConfig.max_2d_texture_layers );
// hardware cubemaps
- GL_CheckExtension( "GL_ARB_texture_cube_map", NULL, "gl_texture_cubemap", GL_TEXTURECUBEMAP_EXT );
+ GL_CheckExtension( "GL_ARB_texture_cube_map", NULL, "gl_texture_cubemap", GL_TEXTURE_CUBEMAP_EXT );
- if( GL_Support( GL_TEXTURECUBEMAP_EXT ))
+ GL_CheckExtension( "GL_ARB_draw_buffers", drawbuffersfuncs, "gl_draw_buffers", GL_DRAW_BUFFERS_EXT );
+
+ if( GL_Support( GL_TEXTURE_CUBEMAP_EXT ))
{
pglGetIntegerv( GL_MAX_CUBE_MAP_TEXTURE_SIZE_ARB, &glConfig.max_cubemap_size );
@@ -1766,15 +1900,8 @@ void GL_InitExtensions( void )
GL_CheckExtension( "GL_ARB_seamless_cube_map", NULL, "gl_seamless_cubemap", GL_ARB_SEAMLESS_CUBEMAP );
}
- // point particles extension
- GL_CheckExtension( "GL_EXT_point_parameters", pointparametersfunc, NULL, GL_EXT_POINTPARAMETERS );
-
GL_CheckExtension( "GL_ARB_texture_non_power_of_two", NULL, "gl_texture_npot", GL_ARB_TEXTURE_NPOT_EXT );
GL_CheckExtension( "GL_ARB_texture_compression", texturecompressionfuncs, "gl_dds_hardware_support", GL_TEXTURE_COMPRESSION_EXT );
- GL_CheckExtension( "GL_EXT_compiled_vertex_array", compiledvertexarrayfuncs, "gl_cva_support", GL_CUSTOM_VERTEX_ARRAY_EXT );
-
- if( !GL_Support( GL_CUSTOM_VERTEX_ARRAY_EXT ))
- GL_CheckExtension( "GL_SGI_compiled_vertex_array", compiledvertexarrayfuncs, "gl_cva_support", GL_CUSTOM_VERTEX_ARRAY_EXT );
GL_CheckExtension( "GL_EXT_texture_edge_clamp", NULL, "gl_clamp_to_edge", GL_CLAMPTOEDGE_EXT );
@@ -1787,47 +1914,56 @@ void GL_InitExtensions( void )
if( GL_Support( GL_ANISOTROPY_EXT ))
pglGetFloatv( GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &glConfig.max_texture_anisotropy );
- GL_CheckExtension( "GL_EXT_texture_lod_bias", NULL, "gl_ext_texture_lodbias", GL_TEXTURE_LODBIAS );
- if( GL_Support( GL_TEXTURE_LODBIAS ))
- pglGetFloatv( GL_MAX_TEXTURE_LOD_BIAS_EXT, &glConfig.max_texture_lodbias );
-
- GL_CheckExtension( "GL_ARB_texture_border_clamp", NULL, "gl_ext_texborder_clamp", GL_CLAMP_TEXBORDER_EXT );
-
- GL_CheckExtension( "GL_EXT_blend_minmax", blendequationfuncs, "gl_ext_customblend", GL_BLEND_MINMAX_EXT );
- GL_CheckExtension( "GL_EXT_blend_subtract", blendequationfuncs, "gl_ext_customblend", GL_BLEND_SUBTRACT_EXT );
+ GL_CheckExtension( "GL_EXT_texture_lod_bias", NULL, "gl_ext_texture_lod_bias", GL_TEXTURE_LOD_BIAS );
- GL_CheckExtension( "glStencilOpSeparate", gl2separatestencilfuncs, "gl_separate_stencil", GL_SEPARATESTENCIL_EXT );
+ if( GL_Support( GL_TEXTURE_LOD_BIAS ))
+ pglGetFloatv( GL_MAX_TEXTURE_LOD_BIAS_EXT, &glConfig.max_texture_lod_bias );
- if( !GL_Support( GL_SEPARATESTENCIL_EXT ))
- GL_CheckExtension("GL_ATI_separate_stencil", atiseparatestencilfuncs, "gl_separate_stencil", GL_SEPARATESTENCIL_EXT );
+ GL_CheckExtension( "GL_ARB_texture_border_clamp", NULL, "gl_ext_texborder_clamp", GL_CLAMP_TEXBORDER_EXT );
- GL_CheckExtension( "GL_EXT_stencil_two_side", stenciltwosidefuncs, "gl_stenciltwoside", GL_STENCILTWOSIDE_EXT );
GL_CheckExtension( "GL_ARB_vertex_buffer_object", vbofuncs, "gl_vertex_buffer_object", GL_ARB_VERTEX_BUFFER_OBJECT_EXT );
-
- // we don't care if it's an extension or not, they are identical functions, so keep it simple in the rendering code
- if( pglDrawRangeElementsEXT == NULL ) pglDrawRangeElementsEXT = pglDrawRangeElements;
-
- GL_CheckExtension( "GL_ARB_texture_env_add", NULL, "gl_texture_env_add", GL_TEXTURE_ENV_ADD_EXT );
+ GL_CheckExtension( "GL_ARB_vertex_array_object", vaofuncs, "gl_vertex_array_object", GL_ARB_VERTEX_ARRAY_OBJECT_EXT );
+ GL_CheckExtension( "GL_ARB_half_float_pixel", NULL, "gl_half_float", GL_ARB_HALF_FLOAT_EXT );
// vp and fp shaders
GL_CheckExtension( "GL_ARB_shader_objects", shaderobjectsfuncs, "gl_shaderobjects", GL_SHADER_OBJECTS_EXT );
- GL_CheckExtension( "GL_ARB_shading_language_100", NULL, "gl_glslprogram", GL_SHADER_GLSL100_EXT );
- GL_CheckExtension( "GL_ARB_vertex_shader", vertexshaderfuncs, "gl_vertexshader", GL_VERTEX_SHADER_EXT );
- GL_CheckExtension( "GL_ARB_fragment_shader", NULL, "gl_pixelshader", GL_FRAGMENT_SHADER_EXT );
+ GL_CheckExtension( "GL_ARB_shading_language_100", NULL, "gl_shading_language", GL_SHADER_GLSL100_EXT );
GL_CheckExtension( "GL_ARB_depth_texture", NULL, "gl_depthtexture", GL_DEPTH_TEXTURE );
GL_CheckExtension( "GL_ARB_shadow", NULL, "gl_arb_shadow", GL_SHADOW_EXT );
GL_CheckExtension( "GL_ARB_texture_float", NULL, "gl_arb_texture_float", GL_ARB_TEXTURE_FLOAT_EXT );
GL_CheckExtension( "GL_ARB_depth_buffer_float", NULL, "gl_arb_depth_float", GL_ARB_DEPTH_FLOAT_EXT );
+ GL_CheckExtension( "GL_ARB_texture_rg", NULL, "gl_arb_texture_rg", GL_ARB_TEXTURE_RG );
+ GL_CheckExtension( "GL_EXT_gpu_shader4", NULL, "gl_ext_gpu_shader4", GL_EXT_GPU_SHADER4 );
+
+ // this won't work without extended context
+ if( glw_state.extended )
+ GL_CheckExtension( "GL_ARB_debug_output", debugoutputfuncs, "gl_debug_output", GL_DEBUG_OUTPUT );
+
+ // FBO support
+ GL_CheckExtension( "GL_ARB_framebuffer_object", arbfbofuncs, "gl_framebuffer_object", GL_FRAMEBUFFER_OBJECT );
+
+ if( !GL_Support( GL_FRAMEBUFFER_OBJECT ))
+ GL_CheckExtension( "GL_EXT_framebuffer_object", extfbofuncs, "gl_framebuffer_object", GL_FRAMEBUFFER_OBJECT );
// occlusion queries
GL_CheckExtension( "GL_ARB_occlusion_query", occlusionfunc, "gl_occlusion_queries", GL_OCCLUSION_QUERIES_EXT );
+ // rectangle textures support
+ GL_CheckExtension( "GL_ARB_texture_rectangle", NULL, "gl_texture_rectangle", GL_TEXTURE_2D_RECT_EXT );
+
if( GL_Support( GL_SHADER_GLSL100_EXT ))
{
pglGetIntegerv( GL_MAX_TEXTURE_COORDS_ARB, &glConfig.max_texture_coords );
pglGetIntegerv( GL_MAX_TEXTURE_IMAGE_UNITS_ARB, &glConfig.max_teximage_units );
+
+ // check for hardware skinning
+ pglGetIntegerv( GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB, &glConfig.max_vertex_uniforms );
+ pglGetIntegerv( GL_MAX_VERTEX_ATTRIBS_ARB, &glConfig.max_vertex_attribs );
+
+ if( glConfig.hardware_type == GLHW_RADEON )
+ glConfig.max_vertex_uniforms /= 4; // radeon returns not correct info
}
else
{
@@ -1835,42 +1971,41 @@ void GL_InitExtensions( void )
glConfig.max_texture_coords = glConfig.max_teximage_units = glConfig.max_texture_units;
}
- // rectangle textures support
- if( Q_strstr( glConfig.extensions_string, "GL_NV_texture_rectangle" ))
- {
- glConfig.texRectangle = GL_TEXTURE_RECTANGLE_NV;
- pglGetIntegerv( GL_MAX_RECTANGLE_TEXTURE_SIZE_NV, &glConfig.max_2d_rectangle_size );
- }
- else if( Q_strstr( glConfig.extensions_string, "GL_EXT_texture_rectangle" ))
- {
- glConfig.texRectangle = GL_TEXTURE_RECTANGLE_EXT;
- pglGetIntegerv( GL_MAX_RECTANGLE_TEXTURE_SIZE_EXT, &glConfig.max_2d_rectangle_size );
- }
- else glConfig.texRectangle = glConfig.max_2d_rectangle_size = 0; // no rectangle
-
- glConfig.max_2d_texture_size = 0;
pglGetIntegerv( GL_MAX_TEXTURE_SIZE, &glConfig.max_2d_texture_size );
if( glConfig.max_2d_texture_size <= 0 ) glConfig.max_2d_texture_size = 256;
+ pglGetIntegerv( GL_MAX_DRAW_BUFFERS_ARB, &glConfig.max_draw_buffers );
+
+ if( GL_Support( GL_TEXTURE_2D_RECT_EXT ))
+ pglGetIntegerv( GL_MAX_RECTANGLE_TEXTURE_SIZE_EXT, &glConfig.max_2d_rectangle_size );
+
Cvar_Get( "gl_max_texture_size", "0", CVAR_INIT, "opengl texture max dims" );
Cvar_Set( "gl_max_texture_size", va( "%i", glConfig.max_2d_texture_size ));
- pglGetIntegerv( GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB, &glConfig.max_vertex_uniforms );
- pglGetIntegerv( GL_MAX_VERTEX_ATTRIBS_ARB, &glConfig.max_vertex_attribs );
-
// MCD has buffering issues
- if(Q_strstr( glConfig.renderer_string, "gdi" ))
+ if( Q_stristr( glConfig.renderer_string, "gdi" ))
Cvar_SetFloat( "gl_finish", 1 );
Cvar_Set( "gl_anisotropy", va( "%f", bound( 0, gl_texture_anisotropy->value, glConfig.max_texture_anisotropy )));
- // software mipmap generator does wrong result with NPOT textures ...
- if( !GL_Support( GL_SGIS_MIPMAPS_EXT ))
- GL_SetExtension( GL_ARB_TEXTURE_NPOT_EXT, false );
-
if( GL_Support( GL_TEXTURE_COMPRESSION_EXT ))
Image_AddCmdFlags( IL_DDS_HARDWARE );
+ // enable gldebug if allowed
+ if( GL_Support( GL_DEBUG_OUTPUT ))
+ {
+ if( host.developer >= D_ERROR )
+ pglDebugMessageCallbackARB( GL_DebugOutput, NULL );
+
+ // force everything to happen in the main thread instead of in a separate driver thread
+ if( host.developer >= D_WARN )
+ pglEnable( GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB );
+
+ // enable all the low priority messages
+ if( host.developer >= D_REPORT )
+ pglDebugMessageControlARB( GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_LOW_ARB, 0, NULL, true );
+ }
+
glw_state.initialized = true;
tr.framecount = tr.visframecount = 1;
@@ -1898,8 +2033,8 @@ qboolean R_Init( void )
GL_RemoveCommands();
R_Free_OpenGL();
- // can't initialize video subsystem
- Host_NewInstance( va("#%s", GI->gamefolder ), "fallback to dedicated mode\n" );
+ Msg( "^1Error:^7 can't initialize video subsystem\n" );
+ Host_NewInstance( va( "#%s", GI->gamefolder ), "stopped" );
return false;
}
@@ -1990,5 +2125,5 @@ void GL_CheckForErrors_( const char *filename, const int fileline )
break;
}
- Host_Error( "GL_CheckForErrors: %s (called at %s:%i)\n", str, filename, fileline );
+ MsgDev( D_ERROR, "OpenGL: %s (called at %s:%i)\n", str, filename, fileline );
}
\ No newline at end of file
diff --git b/engine/client/gl_warp.c a/engine/client/gl_warp.c
index 5023b70..06903ec 100644
--- b/engine/client/gl_warp.c
+++ a/engine/client/gl_warp.c
@@ -19,9 +19,9 @@ GNU General Public License for more details.
#include "com_model.h"
#include "wadfile.h"
-#define MAX_CLIP_VERTS 64 // skybox clip vertices
+#define SKYCLOUDS_QUALITY 12
+#define MAX_CLIP_VERTS 128 // skybox clip vertices
#define TURBSCALE ( 256.0f / ( M_PI2 ))
-static float speedscale;
static const char* r_skyBoxSuffix[6] = { "rt", "bk", "lf", "ft", "up", "dn" };
static const int r_skyTexOrder[6] = { 0, 2, 1, 3, 4, 5 };
@@ -127,6 +127,8 @@ void DrawSkyPolygon( int nump, vec3_t vecs )
j = vec_to_st[axis][2];
dv = (j > 0) ? vecs[j-1] : -vecs[-j-1];
+ if( dv == 0.0f ) continue;
+
j = vec_to_st[axis][0];
s = (j < 0) ? -vecs[-j-1] / dv : vecs[j-1] / dv;
@@ -302,6 +304,7 @@ void R_AddSkyBoxSurface( msurface_t *fa )
{
vec3_t verts[MAX_CLIP_VERTS];
glpoly_t *p;
+ float *v;
int i;
if( r_fastsky->integer )
@@ -317,6 +320,20 @@ void R_AddSkyBoxSurface( msurface_t *fa )
}
}
+ if( world.sky_sphere && fa->polys && !world.custom_skybox )
+ {
+ glpoly_t *p = fa->polys;
+
+ // draw the sky poly
+ pglBegin( GL_POLYGON );
+ for( i = 0, v = p->verts[0]; i < p->numverts; i++, v += VERTEXSIZE )
+ {
+ pglTexCoord2f( v[3], v[4] );
+ pglVertex3fv( v );
+ }
+ pglEnd ();
+ }
+
// calculate vertex values for sky box
for( p = fa->polys; p; p = p->next )
{
@@ -347,6 +364,7 @@ void R_UnloadSkybox( void )
tr.skyboxbasenum = 5800; // set skybox base (to let some mods load hi-res skyboxes)
memset( tr.skyboxTextures, 0, sizeof( tr.skyboxTextures ));
+ world.custom_skybox = false;
}
/*
@@ -445,11 +463,14 @@ void R_SetupSky( const char *skyboxname )
{
Q_snprintf( sidename, sizeof( sidename ), "%s%s", loadname, r_skyBoxSuffix[i] );
tr.skyboxTextures[i] = GL_LoadTexture( sidename, NULL, 0, TF_CLAMP|TF_SKY, NULL );
- GL_SetTextureType( tr.skyboxTextures[i], TEX_CUBEMAP );
if( !tr.skyboxTextures[i] ) break;
}
- if( i == 6 ) return; // loaded
+ if( i == 6 )
+ {
+ world.custom_skybox = true;
+ return; // loaded
+ }
// clear previous and try again
R_UnloadSkybox();
@@ -458,16 +479,193 @@ void R_SetupSky( const char *skyboxname )
{
Q_snprintf( sidename, sizeof( sidename ), "%s_%s", loadname, r_skyBoxSuffix[i] );
tr.skyboxTextures[i] = GL_LoadTexture( sidename, NULL, 0, TF_CLAMP|TF_SKY, NULL );
- GL_SetTextureType( tr.skyboxTextures[i], TEX_CUBEMAP );
if( !tr.skyboxTextures[i] ) break;
}
- if( i == 6 ) return; // loaded
+
+ if( i == 6 )
+ {
+ world.custom_skybox = true;
+ return; // loaded
+ }
// completely couldn't load skybox (probably never happens)
MsgDev( D_ERROR, "R_SetupSky: couldn't load skybox '%s'\n", skyboxname );
R_UnloadSkybox();
}
+//==============================================================================
+//
+// RENDER CLOUDS
+//
+//==============================================================================
+/*
+==============
+R_CloudVertex
+==============
+*/
+void R_CloudVertex( float s, float t, int axis, vec3_t v )
+{
+ int j, k, farclip;
+ vec3_t b;
+
+ farclip = RI.farClip;
+
+ b[0] = s * (farclip >> 1);
+ b[1] = t * (farclip >> 1);
+ b[2] = (farclip >> 1);
+
+ for( j = 0; j < 3; j++ )
+ {
+ k = st_to_vec[axis][j];
+ v[j] = (k < 0) ? -b[-k-1] : b[k-1];
+ v[j] += RI.cullorigin[j];
+ }
+}
+
+/*
+=============
+R_CloudTexCoord
+=============
+*/
+void R_CloudTexCoord( vec3_t v, float speed, float *s, float *t )
+{
+ float length, speedscale;
+ vec3_t dir;
+
+ speedscale = cl.time * speed;
+ speedscale -= (int)speedscale & ~127;
+
+ VectorSubtract( v, RI.vieworg, dir );
+ dir[2] *= 3.0f; // flatten the sphere
+
+ length = VectorLength( dir );
+ length = 6.0f * 63.0f / length;
+
+ *s = ( speedscale + dir[0] * length ) * (1.0f / 128.0f);
+ *t = ( speedscale + dir[1] * length ) * (1.0f / 128.0f);
+}
+
+/*
+===============
+R_CloudDrawPoly
+===============
+*/
+void R_CloudDrawPoly( glpoly_t *p )
+{
+ float s, t;
+ float *v;
+ int i;
+
+ GL_SetRenderMode( kRenderNormal );
+ GL_Bind( GL_TEXTURE0, tr.solidskyTexture );
+
+ pglBegin( GL_QUADS );
+ for( i = 0, v = p->verts[0]; i < 4; i++, v += VERTEXSIZE )
+ {
+ R_CloudTexCoord( v, 8.0f, &s, &t );
+ pglTexCoord2f( s, t );
+ pglVertex3fv( v );
+ }
+ pglEnd();
+
+ GL_SetRenderMode( kRenderTransTexture );
+ GL_Bind( GL_TEXTURE0, tr.alphaskyTexture );
+
+ pglBegin( GL_QUADS );
+ for( i = 0, v = p->verts[0]; i < 4; i++, v += VERTEXSIZE )
+ {
+ R_CloudTexCoord( v, 16.0f, &s, &t );
+ pglTexCoord2f( s, t );
+ pglVertex3fv( v );
+ }
+ pglEnd();
+
+ pglDisable( GL_BLEND );
+}
+
+/*
+==============
+R_CloudRenderSide
+==============
+*/
+void R_CloudRenderSide( int axis )
+{
+ vec3_t verts[4];
+ float di, qi, dj, qj;
+ vec3_t vup, vright;
+ vec3_t temp, temp2;
+ glpoly_t p[1];
+ int i, j;
+
+ R_CloudVertex( -1.0f, -1.0f, axis, verts[0] );
+ R_CloudVertex( -1.0f, 1.0f, axis, verts[1] );
+ R_CloudVertex( 1.0f, 1.0f, axis, verts[2] );
+ R_CloudVertex( 1.0f, -1.0f, axis, verts[3] );
+
+ VectorSubtract( verts[2], verts[3], vup );
+ VectorSubtract( verts[2], verts[1], vright );
+
+ p->numverts = 4;
+ di = SKYCLOUDS_QUALITY;
+ qi = 1.0 / di;
+ dj = (axis < 4) ? di * 2 : di; //subdivide vertically more than horizontally on skybox sides
+ qj = 1.0 / dj;
+
+ for( i = 0; i < di; i++ )
+ {
+ for( j = 0; j < dj; j++ )
+ {
+ if( i * qi < RI.skyMins[0][axis] / 2 + 0.5f - qi
+ || i * qi > RI.skyMaxs[0][axis] / 2 + 0.5f
+ || j * qj < RI.skyMins[1][axis] / 2 + 0.5f - qj
+ || j * qj > RI.skyMaxs[1][axis] / 2 + 0.5f )
+ continue;
+
+ VectorScale( vright, qi * i, temp );
+ VectorScale( vup, qj * j, temp2 );
+ VectorAdd( temp, temp2, temp );
+ VectorAdd( verts[0], temp, p->verts[0] );
+
+ VectorScale( vup, qj, temp );
+ VectorAdd( p->verts[0], temp, p->verts[1] );
+
+ VectorScale( vright, qi, temp );
+ VectorAdd( p->verts[1], temp, p->verts[2] );
+
+ VectorAdd( p->verts[0], temp, p->verts[3] );
+
+ R_CloudDrawPoly( p );
+ }
+ }
+}
+
+/*
+==============
+R_DrawClouds
+
+Quake-style clouds
+==============
+*/
+void R_DrawClouds( void )
+{
+ int i;
+
+ RI.isSkyVisible = true;
+
+ pglDepthFunc( GL_GEQUAL );
+ pglDepthMask( 0 );
+
+ for( i = 0; i < 6; i++ )
+ {
+ if( RI.skyMins[0][i] >= RI.skyMaxs[0][i] || RI.skyMins[1][i] >= RI.skyMaxs[1][i] )
+ continue;
+ R_CloudRenderSide( i );
+ }
+
+ pglDepthMask( GL_TRUE );
+ pglDepthFunc( GL_LEQUAL );
+}
+
/*
=============
R_InitSky
@@ -502,7 +700,7 @@ void R_InitSky( mip_t *mt, texture_t *tx )
}
// make sure what sky image is valid
- if( !r_sky || !r_sky->palette || r_sky->type != PF_INDEXED_32 )
+ if( !r_sky || !r_sky->palette || r_sky->type != PF_INDEXED_32 || r_sky->height == 0 )
{
MsgDev( D_ERROR, "R_InitSky: unable to load sky texture %s\n", tx->name );
FS_FreeImage( r_sky );
@@ -567,9 +765,6 @@ void R_InitSky( mip_t *mt, texture_t *tx )
// load it in
tr.alphaskyTexture = GL_LoadTextureInternal( "alpha_sky", &r_temp, TF_UNCOMPRESSED|TF_NOMIPMAP, false );
- GL_SetTextureType( tr.solidskyTexture, TEX_BRUSH );
- GL_SetTextureType( tr.alphaskyTexture, TEX_BRUSH );
-
// clean up
FS_FreeImage( r_sky );
Mem_Free( trans );
@@ -582,17 +777,20 @@ EmitWaterPolys
Does a water warp on the pre-fragmented glpoly_t chain
=============
*/
-void EmitWaterPolys( glpoly_t *polys, qboolean noCull )
+void EmitWaterPolys( glpoly_t *polys, qboolean noCull, qboolean direction )
{
- glpoly_t *p;
+ glpoly_t *p = polys;
float *v, nv, waveHeight;
float s, t, os, ot;
int i;
+ if( !polys ) return;
if( noCull ) pglDisable( GL_CULL_FACE );
// set the current waveheight
- waveHeight = RI.currentWaveHeight;
+ if( p->verts[0][2] >= RI.refdef.vieworg[2] )
+ waveHeight = -RI.currententity->curstate.scale;
+ else waveHeight = RI.currententity->curstate.scale;
// reset fog color for nonlightmapped water
GL_ResetFogColor();
@@ -601,29 +799,37 @@ void EmitWaterPolys( glpoly_t *polys, qboolean noCull )
{
pglBegin( GL_POLYGON );
- for( i = 0, v = p->verts[0]; i < p->numverts; i++, v += VERTEXSIZE )
+ if( direction )
+ v = p->verts[0] + ( p->numverts - 1 ) * VERTEXSIZE;
+ else v = p->verts[0];
+
+ for( i = 0; i < p->numverts; i++ )
{
if( waveHeight )
{
- nv = v[2] + waveHeight + ( waveHeight * sin(v[0] * 0.02f + cl.time)
- * sin(v[1] * 0.02 + cl.time) * sin(v[2] * 0.02f + cl.time));
- nv -= waveHeight;
+ nv = r_turbsin[(int)(cl.time * 160.0f + v[1] + v[0]) & 255] + 8.0f;
+ nv = (r_turbsin[(int)(v[0] * 5.0f + cl.time * 171.0f - v[1]) & 255] + 8.0f ) * 0.8f + nv;
+ nv = nv * waveHeight + v[2];
}
else nv = v[2];
os = v[3];
ot = v[4];
- s = os + r_turbsin[(int)((ot * 0.125f + cl.time ) * TURBSCALE) & 255];
+ s = os + r_turbsin[(int)((ot * 0.125f + cl.time) * TURBSCALE) & 255];
s *= ( 1.0f / SUBDIVIDE_SIZE );
- t = ot + r_turbsin[(int)((os * 0.125f + cl.time ) * TURBSCALE) & 255];
+ t = ot + r_turbsin[(int)((os * 0.125f + cl.time) * TURBSCALE) & 255];
t *= ( 1.0f / SUBDIVIDE_SIZE );
if( glState.activeTMU != 0 )
GL_MultiTexCoord2f( glState.activeTMU, s, t );
else pglTexCoord2f( s, t );
pglVertex3f( v[0], v[1], nv );
+
+ if( direction )
+ v -= VERTEXSIZE;
+ else v += VERTEXSIZE;
}
pglEnd();
}
@@ -632,103 +838,4 @@ void EmitWaterPolys( glpoly_t *polys, qboolean noCull )
if( noCull ) pglEnable( GL_CULL_FACE );
GL_SetupFogColorForSurfaces();
-}
-
-/*
-=============
-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 )
- {
- pglBegin( GL_POLYGON );
-
- for( i = 0, v = p->verts[0]; i < p->numverts; i++, v += VERTEXSIZE )
- {
- VectorSubtract( v, RI.vieworg, dir );
- dir[2] *= 3.0f; // flatten the sphere
-
- length = VectorLength( dir );
- length = 6.0f * 63.0f / length;
-
- dir[0] *= length;
- dir[1] *= length;
-
- s = ( speedscale + dir[0] ) * (1.0f / 128.0f);
- t = ( speedscale + dir[1] ) * (1.0f / 128.0f);
-
- pglTexCoord2f( s, t );
- pglVertex3fv( v );
- }
- pglEnd ();
- }
-}
-
-/*
-=================
-R_DrawSkyChain
-=================
-*/
-void R_DrawSkyChain( msurface_t *s )
-{
- msurface_t *fa;
-
- GL_SetRenderMode( kRenderNormal );
- GL_Bind( GL_TEXTURE0, tr.solidskyTexture );
-
- speedscale = cl.time * 8.0f;
- speedscale -= (int)speedscale & ~127;
-
- for( fa = s; fa; fa = fa->texturechain )
- EmitSkyPolys( fa );
-
- GL_SetRenderMode( kRenderTransTexture );
- GL_Bind( GL_TEXTURE0, tr.alphaskyTexture );
-
- speedscale = cl.time * 16.0f;
- speedscale -= (int)speedscale & ~127;
-
- for( fa = s; fa; fa = fa->texturechain )
- EmitSkyPolys( fa );
-
- pglDisable( GL_BLEND );
-}
-
-/*
-===============
-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 EmitSkyLayers( msurface_t *fa )
-{
- GL_SetRenderMode( kRenderNormal );
- GL_Bind( GL_TEXTURE0, tr.solidskyTexture );
-
- speedscale = cl.time * 8.0f;
- speedscale -= (int)speedscale & ~127;
-
- EmitSkyPolys( fa );
-
- GL_SetRenderMode( kRenderTransTexture );
- GL_Bind( GL_TEXTURE0, tr.alphaskyTexture );
-
- speedscale = cl.time * 16.0f;
- speedscale -= (int)speedscale & ~127;
-
- EmitSkyPolys( fa );
-
- pglDisable( GL_BLEND );
}
\ No newline at end of file
diff --git b/engine/client/s_backend.c a/engine/client/s_backend.c
index 071d55d..2221c5f 100644
--- b/engine/client/s_backend.c
+++ a/engine/client/s_backend.c
@@ -361,7 +361,7 @@ int SNDDMA_Init( void *hInst )
else
{
if( snd_firsttime )
- MsgDev( D_ERROR, "SNDDMA_Init: can't initialize sound device\n" );
+ MsgDev( D_ERROR, "Audio: can't initialize sound device\n" );
return false;
}
diff --git b/engine/client/s_dsp.c a/engine/client/s_dsp.c
index cc7e01d..efed07c 100644
--- b/engine/client/s_dsp.c
+++ a/engine/client/s_dsp.c
@@ -14,5445 +14,917 @@ GNU General Public License for more details.
*/
#include "common.h"
+#include "client.h"
#include "sound.h"
-#define SIGN( d ) (( d ) < 0 ? -1 : 1 )
-#define ABS( a ) abs( a )
-#define MSEC_TO_SAMPS( a ) ((( a ) * SOUND_DMA_SPEED) / 1000 ) // convert milliseconds to # samples in equivalent time
-#define SEC_TO_SAMPS( a ) (( a ) * SOUND_DMA_SPEED) // conver seconds to # samples in equivalent time
-#define CLIP_DSP( x ) ( x )
-#define SOUND_MS_PER_FT 1 // sound travels approx 1 foot per millisecond
-#define ROOM_MAX_SIZE 1000 // max size in feet of room simulation for dsp
+#define MAX_DELAY 0.4f
+#define MAX_ROOM_TYPES ARRAYSIZE( rgsxpre )
-// Performance notes:
+#define MONODLY 0
+#define MAX_MONO_DELAY 0.4f
-// DSP processing should take no more than 3ms total time per frame to remain on par with hl1
-// Assume a min frame rate of 24fps = 42ms per frame
-// at 24fps, to maintain 44.1khz output rate, we must process about 1840 mono samples per frame.
-// So we must process 1840 samples in 3ms.
+#define REVERBPOS 1
+#define MAX_REVERB_DELAY 0.1f
-// on a 1Ghz CPU (mid-low end CPU) 3ms provides roughly 3,000,000 cycles.
-// Thus we have 3e6 / 1840 = 1630 cycles per sample.
+#define STEREODLY 3
+#define MAX_STEREO_DELAY 0.1f
-#define PBITS 12 // parameter bits
-#define PMAX ((1 << PBITS)-1) // parameter max size
+#define REVERB_XFADE 32
-// crossfade from y2 to y1 at point r (0 < r < PMAX )
-#define XFADE( y1, y2, r ) (((y1) * (r)) >> PBITS) + (((y2) * (PMAX - (r))) >> PBITS);
-#define XFADEF( y1, y2, r ) (((y1) * (r)) / (float)(PMAX)) + (((y2) * (PMAX - (r))) / (float)(PMAX));
+#define MAXDLY (STEREODLY + 1)
+#define MAXLP 10
+#define MAXPRESETS ARRAYSIZE( rgsxpre )
-/////////////////////
-// dsp helpers
-/////////////////////
-
-// dot two integer vectors of length M+1
-// M is filter order, h is filter vector, w is filter state vector
-_inline int dot ( int M, int *h, int *w )
-{
- int i, y;
-
- for( y = 0, i = 0; i <= M; i++ )
- y += ( h[i] * w[i] ) >> PBITS;
- return y;
-}
-
-// delay array w[] by D samples
-// w[0] = input, w[D] = output
-// practical for filters, but not for large values of D
-_inline void delay( int D, int *w )
-{
- int i;
-
- for( i = D; i >= 1; i-- ) // reverse order updating
- w[i] = w[i-1];
-}
-
-// circular wrap of pointer p, relative to array w
-// D delay line size in samples w[0...D]
-// w delay line buffer pointer, dimension D+1
-// p circular pointer
-_inline void wrap( int D, int *w, int **p )
-{
- if( *p > w + D ) *p -= D + 1; // when *p = w + D + 1, it wraps around to *p = w
- if( *p < w ) *p += D + 1; // when *p = w - 1, it wraps around to *p = w + D
-}
-
-// simple averaging filter for performance - a[] is 0, b[] is 1, L is # of samples to average
-_inline int avg_filter( int M, int *a, int L, int *b, int *w, int x )
-{
- int i, y = 0;
-
- w[0] = x;
-
- // output adder
- switch( L )
- {
- default:
- case 12: y += w[12];
- case 11: y += w[11];
- case 10: y += w[10];
- case 9: y += w[9];
- case 8: y += w[8];
- case 7: y += w[7];
- case 6: y += w[6];
- case 5: y += w[5];
- case 4: y += w[4];
- case 3: y += w[3];
- case 2: y += w[2];
- case 1: y += w[1];
- case 0: y += w[0];
- }
-
- for( i = L; i >= 1; i-- ) // reverse update internal state
- w[i] = w[i-1];
-
- switch( L )
- {
- default:
- case 12: return y / 13;
- case 11: return y / 12;
- case 10: return y / 11;
- case 9: return y / 10;
- case 8: return y / 9;
- case 7: return y >> 3;
- case 6: return y / 7;
- case 5: return y / 6;
- case 4: return y / 5;
- case 3: return y >> 2;
- case 2: return y / 3;
- case 1: return y >> 1;
- case 0: return y;
- }
-}
-
-// IIR filter, cannonical form
-// returns single sample y for current input value x
-// x is input sample
-// w = internal state vector, dimension max(M,L) + 1
-// L, M numerator and denominator filter orders
-// a,b are M+1 dimensional arrays of filter params
-//
-// for M = 4:
-//
-// 1 w0(n) b0
-// x(n)--->(+)--(*)-----.------(*)->(+)---> y(n)
-// ^ | ^
-// | [Delay d] |
-// | | |
-// | -a1 |W1 b1 |
-// ----(*)---.------(*)----
-// ^ | ^
-// | [Delay d] |
-// | | |
-// | -a2 |W2 b2 |
-// ----(*)---.------(*)----
-// ^ | ^
-// | [Delay d] |
-// | | |
-// | -a3 |W3 b3 |
-// ----(*)---.------(*)----
-// ^ | ^
-// | [Delay d] |
-// | | |
-// | -a4 |W4 b4 |
-// ----(*)---.------(*)----
-//
-// for each input sample x, do:
-// w0 = x - a1*w1 - a2*w2 - ... aMwM
-// y = b0*w0 + b1*w1 + ...bL*wL
-// wi = wi-1, i = K, K-1, ..., 1
-
-_inline int iir_filter( int M, int *a, int L, int *b, int *w, int x )
-{
- int K, i, y, x0;
-
- if( M == 0 )
- return avg_filter( M, a, L, b, w, x );
-
- y = 0;
- x0 = x;
-
- K = max ( M, L );
-
- // for (i = 1; i <= M; i++) // input adder
- // w[0] -= ( a[i] * w[i] ) >> PBITS;
-
- // M is clamped between 1 and FLT_M
- // change this switch statement if FLT_M changes!
-
- switch( M )
- {
- case 12: x0 -= ( a[12] * w[12] ) >> PBITS;
- case 11: x0 -= ( a[11] * w[11] ) >> PBITS;
- case 10: x0 -= ( a[10] * w[10] ) >> PBITS;
- case 9: x0 -= ( a[9] * w[9] ) >> PBITS;
- case 8: x0 -= ( a[8] * w[8] ) >> PBITS;
- case 7: x0 -= ( a[7] * w[7] ) >> PBITS;
- case 6: x0 -= ( a[6] * w[6] ) >> PBITS;
- case 5: x0 -= ( a[5] * w[5] ) >> PBITS;
- case 4: x0 -= ( a[4] * w[4] ) >> PBITS;
- case 3: x0 -= ( a[3] * w[3] ) >> PBITS;
- case 2: x0 -= ( a[2] * w[2] ) >> PBITS;
- default:
- case 1: x0 -= ( a[1] * w[1] ) >> PBITS;
- }
-
- w[0] = x0;
-
- // for( i = 0; i <= L; i++ ) // output adder
- // y += ( b[i] * w[i] ) >> PBITS;
-
- switch( L )
- {
- case 12: y += ( b[12] * w[12] ) >> PBITS;
- case 11: y += ( b[11] * w[11] ) >> PBITS;
- case 10: y += ( b[10] * w[10] ) >> PBITS;
- case 9: y += ( b[9] * w[9] ) >> PBITS;
- case 8: y += ( b[8] * w[8] ) >> PBITS;
- case 7: y += ( b[7] * w[7] ) >> PBITS;
- case 6: y += ( b[6] * w[6] ) >> PBITS;
- case 5: y += ( b[5] * w[5] ) >> PBITS;
- case 4: y += ( b[4] * w[4] ) >> PBITS;
- case 3: y += ( b[3] * w[3] ) >> PBITS;
- case 2: y += ( b[2] * w[2] ) >> PBITS;
- default:
- case 1: y += ( b[1] * w[1] ) >> PBITS;
- case 0: y += ( b[0] * w[0] ) >> PBITS;
- }
-
- for( i = K; i >= 1; i-- ) // reverse update internal state
- w[i] = w[i-1];
-
- return y; // current output sample
-}
-
-// IIR filter, cannonical form, using dot product and delay implementation
-// (may be easier to optimize this routine.)
-_inline int iir_filter2( int M, int *a, int L, int *b, int *w, int x )
-{
- int K, y;
-
- K = max( M, L ); // K = max (M, L)
- w[0] = 0; // needed for dot (M, a, w)
-
- w[0] = x - dot( M, a, w ); // input adder
- y = dot( L, b, w ); // output adder
-
- delay( K, w ); // update delay line
-
- return y; // current output sample
-}
-
-
-// fir filter - no feedback = high stability but also may be more expensive computationally
-_inline int fir_filter( int M, int *h, int *w, int x )
-{
- int i, y;
-
- w[0] = x;
-
- for( y = 0, i = 0; i <= M; i++ )
- y += h[i] * w[i];
-
- for( i = M; i >= -1; i-- )
- w[i] = w[i-1];
-
- return y;
-}
-
-// fir filter, using dot product and delay implementation
-_inline int fir_filter2( int M, int *h, int *w, int x )
-{
- int y;
-
- w[0] = x;
- y = dot( M, h, w );
- delay( M, w );
-
- return y;
-}
-
-
-// tap - i-th tap of circular delay line buffer
-// D delay line size in samples
-// w delay line buffer pointer, of dimension D+1
-// p circular pointer
-// t = 0...D
-int tap( int D, int *w, int *p, int t )
-{
- return w[(p - w + t) % (D + 1)];
-}
-
-// tapi - interpolated tap output of a delay line
-// interpolates sample between adjacent samples in delay line for 'frac' part of delay
-// D delay line size in samples
-// w delay line buffer pointer, of dimension D+1
-// p circular pointer
-// t - delay tap integer value 0...D. (complete delay is t.frac )
-// frac - varying 16 bit fractional delay value 0...32767 (normalized to 0.0 - 1.0)
-_inline int tapi( int D, int *w, int *p, int t, int frac )
-{
- int i, j;
- int si, sj;
-
- i = t; // tap value, interpolate between adjacent samples si and sj
- j = (i + 1) % (D+1); // if i = D, then j = 0; otherwise, j = i + 1
-
- si = tap( D, w, p, i ); // si(n) = x(n - i)
- sj = tap( D, w, p, j ); // sj(n) = x(n - j)
-
- return si + (((frac) * (sj - si) ) >> 16);
-}
-
-// circular delay line, D-fold delay
-// D delay line size in samples w[0..D]
-// w delay line buffer pointer, dimension D+1
-// p circular pointer
-_inline void cdelay( int D, int *w, int **p )
-{
- (*p)--; // decrement pointer and wrap modulo (D+1)
- wrap ( D, w, p ); // when *p = w-1, it wraps around to *p = w+D
-}
-
-// plain reverberator with circular delay line
-// D delay line size in samples
-// t tap from this location - <= D
-// w delay line buffer pointer of dimension D+1
-// p circular pointer, must be init to &w[0] before first call
-// a feedback value, 0-PMAX (normalized to 0.0-1.0)
-// b gain
-// x input sample
-
-// w0(n) b
-// x(n)--->(+)--------.-----(*)-> y(n)
-// ^ |
-// | [Delay d]
-// | |
-// | a |Wd(n)
-// ----(*)---.
-
-_inline int dly_plain( int D, int t, int *w, int **p, int a, int b, int x )
-{
- int y, sD;
-
- sD = tap( D, w, *p, t ); // Tth tap delay output
- y = x + (( a * sD ) >> PBITS); // filter output
- **p = y; // delay input
- cdelay( D, w, p ); // update delay line
-
- return (( y * b ) >> PBITS );
-}
-
-// straight delay line
-//
-// D delay line size in samples
-// t tap from this location - <= D
-// w delay line buffer pointer of dimension D+1
-// p circular pointer, must be init to &w[0] before first call
-// x input sample
-//
-// x(n)--->[Delay d]---> y(n)
-//
-_inline int dly_linear ( int D, int t, int *w, int **p, int x )
-{
- int y;
-
- y = tap( D, w, *p, t ); // Tth tap delay output
- **p = x; // delay input
- cdelay( D, w, p ); // update delay line
-
- return y;
-}
-
-// lowpass reverberator, replace feedback multiplier 'a' in
-// plain reverberator with a low pass filter
-// D delay line size in samples
-// t tap from this location - <= D
-// w delay line buffer pointer of dimension D+1
-// p circular pointer, must be init to &w[0] before first call
-// a feedback gain
-// b output gain
-// M filter order
-// bf filter numerator, 0-PMAX (normalized to 0.0-1.0), M+1 dimensional
-// af filter denominator, 0-PMAX (normalized to 0.0-1.0), M+1 dimensional
-// vf filter state, M+1 dimensional
-// x input sample
-// w0(n) b
-// x(n)--->(+)--------------.----(*)--> y(n)
-// ^ |
-// | [Delay d]
-// | |
-// | a |Wd(n)
-// --(*)--[Filter])-
-
-int dly_lowpass( int D, int t, int *w, int **p, int a, int b, int M, int *af, int L, int *bf, int *vf, int x )
-{
- int y, sD;
-
- sD = tap( D, w, *p, t ); // delay output is filter input
- y = x + ((iir_filter ( M, af, L, bf, vf, sD ) * a) >> PBITS); // filter output with gain
- **p = y; // delay input
- cdelay( D, w, p ); // update delay line
-
- return (( y * b ) >> PBITS ); // output with gain
-}
-
-// allpass reverberator with circular delay line
-// D delay line size in samples
-// t tap from this location - <= D
-// w delay line buffer pointer of dimension D+1
-// p circular pointer, must be init to &w[0] before first call
-// a feedback value, 0-PMAX (normalized to 0.0-1.0)
-// b gain
-
-// w0(n) -a b
-// x(n)--->(+)--------.-----(*)-->(+)--(*)-> y(n)
-// ^ | ^
-// | [Delay d] |
-// | | |
-// | a |Wd(n) |
-// ----(*)---.-------------
-//
-// for each input sample x, do:
-// w0 = x + a*Wd
-// y = -a*w0 + Wd
-// delay (d, W) - w is the delay buffer array
-//
-// or, using circular delay, for each input sample x do:
-//
-// Sd = tap (D,w,p,D)
-// S0 = x + a*Sd
-// y = -a*S0 + Sd
-// *p = S0
-// cdelay(D, w, &p)
-
-_inline int dly_allpass( int D, int t, int *w, int **p, int a, int b, int x )
-{
- int y, s0, sD;
-
- sD = tap( D, w, *p, t ); // Dth tap delay output
- s0 = x + (( a * sD ) >> PBITS);
-
- y = (( -a * s0 ) >> PBITS ) + sD; // filter output
- **p = s0; // delay input
- cdelay( D, w, p ); // update delay line
-
- return (( y * b ) >> PBITS );
-}
-
-
-///////////////////////////////////////////////////////////////////////////////////
-// fixed point math for real-time wave table traversing, pitch shifting, resampling
-///////////////////////////////////////////////////////////////////////////////////
-#define FIX20_BITS 20 // 20 bits of fractional part
-#define FIX20_SCALE (1 << FIX20_BITS)
-#define FIX20_INTMAX ((1 << (32 - FIX20_BITS))-1) // maximum step integer
-#define FLOAT_TO_FIX20(a) ((int)((a) * (float)FIX20_SCALE)) // convert float to fixed point
-#define INT_TO_FIX20(a) (((int)(a)) << FIX20_BITS) // convert int to fixed point
-#define FIX20_TO_FLOAT(a) ((float)(a) / (float)FIX20_SCALE) // convert fix20 to float
-#define FIX20_INTPART(a) (((int)(a)) >> FIX20_BITS) // get integer part of fixed point
-#define FIX20_FRACPART(a) ((a) - (((a) >> FIX20_BITS) << FIX20_BITS)) // get fractional part of fixed point
-#define FIX20_FRACTION(a,b) (FIX(a)/(b)) // convert int a to fixed point, divide by b
-
-typedef int fix20int;
-
-/////////////////////////////////
-// DSP processor parameter block
-/////////////////////////////////
-
-// NOTE: these prototypes must match the XXX_Params ( prc_t *pprc ) and XXX_GetNext ( XXX_t *p, int x ) functions
-
-typedef void * (*prc_Param_t)( void *pprc ); // individual processor allocation functions
-typedef int (*prc_GetNext_t)( void *pdata, int x ); // get next function for processor
-typedef int (*prc_GetNextN_t)( void *pdata, portable_samplepair_t *pbuffer, int SampleCount, int op); // batch version of getnext
-typedef void (*prc_Free_t)( void *pdata ); // free function for processor
-typedef void (*prc_Mod_t)(void *pdata, float v); // modulation function for processor
-
-#define OP_LEFT 0 // batch process left channel in place
-#define OP_RIGHT 1 // batch process right channel in place
-#define OP_LEFT_DUPLICATE 2 // batch process left channel in place, duplicate to right channel
-
-#define PRC_NULL 0 // pass through - must be 0
-#define PRC_DLY 1 // simple feedback reverb
-#define PRC_RVA 2 // parallel reverbs
-#define PRC_FLT 3 // lowpass or highpass filter
-#define PRC_CRS 4 // chorus
-#define PRC_PTC 5 // pitch shifter
-#define PRC_ENV 6 // adsr envelope
-#define PRC_LFO 7 // lfo
-#define PRC_EFO 8 // envelope follower
-#define PRC_MDY 9 // mod delay
-#define PRC_DFR 10 // diffusor - n series allpass delays
-#define PRC_AMP 11 // amplifier with distortion
-
-#define QUA_LO 0 // quality of filter or reverb. Must be 0,1,2,3.
-#define QUA_MED 1
-#define QUA_HI 2
-#define QUA_VHI 3
-#define QUA_MAX QUA_VHI
-
-#define CPRCPARAMS 16 // up to 16 floating point params for each processor type
-
-// processor definition - one for each running instance of a dsp processor
-typedef struct
-{
- int type; // PRC type
-
- float prm[CPRCPARAMS]; // dsp processor parameters - array of floats
-
- prc_Param_t pfnParam; // allocation function - takes ptr to prc, returns ptr to specialized data struct for proc type
- prc_GetNext_t pfnGetNext; // get next function
- prc_GetNextN_t pfnGetNextN; // batch version of get next
- prc_Free_t pfnFree; // free function
- prc_Mod_t pfnMod; // modulation function
-
- void *pdata; // processor state data - ie: pdly, pflt etc.
-} prc_t;
-
-// processor parameter ranges - for validating parameters during allocation of new processor
-typedef struct prm_rng_s
-{
- int iprm; // parameter index
- float lo; // min value of parameter
- float hi; // max value of parameter
-} prm_rng_t;
-
-void PRC_CheckParams( prc_t *pprc, prm_rng_t *prng );
-
-///////////
-// Filters
-///////////
-
-#define CFLTS 64 // max number of filters simultaneously active
-#define FLT_M 12 // max order of any filter
-
-#define FLT_LP 0 // lowpass filter
-#define FLT_HP 1 // highpass filter
-#define FTR_MAX FLT_HP
-
-// flt parameters
-
-typedef struct
-{
- qboolean fused; // true if slot in use
-
- int b[FLT_M+1]; // filter numerator parameters (convert 0.0-1.0 to 0-PMAX representation)
- int a[FLT_M+1]; // filter denominator parameters (convert 0.0-1.0 to 0-PMAX representation)
- int w[FLT_M+1]; // filter state - samples (dimension of max (M, L))
- int L; // filter order numerator (dimension of a[M+1])
- int M; // filter order denominator (dimension of b[L+1])
-} flt_t;
-
-// flt flts
-flt_t flts[CFLTS];
-
-void FLT_Init( flt_t *pf ) { if( pf ) memset( pf, 0, sizeof( flt_t )); }
-void FLT_InitAll( void ) { int i; for( i = 0; i < CFLTS; i++ ) FLT_Init( &flts[i] ); }
-void FLT_Free( flt_t *pf ) { if( pf ) memset( pf, 0, sizeof( flt_t )); }
-void FLT_FreeAll( void ) { int i; for( i = 0; i < CFLTS; i++ ) FLT_Free( &flts[i] ); }
-
-
-// find a free filter from the filter pool
-// initialize filter numerator, denominator b[0..M], a[0..L]
-flt_t * FLT_Alloc( int M, int L, int *a, int *b )
-{
- int i, j;
- flt_t *pf = NULL;
-
- for( i = 0; i < CFLTS; i++ )
- {
- if( !flts[i].fused )
- {
- pf = &flts[i];
-
- // transfer filter params into filter struct
- pf->M = M;
- pf->L = L;
- for( j = 0; j <= M; j++ )
- pf->a[j] = a[j];
-
- for( j = 0; j <= L; j++ )
- pf->b[j] = b[j];
-
- pf->fused = true;
- break;
- }
- }
-
- ASSERT( pf ); // make sure we're not trying to alloc more than CFLTS flts
-
- return pf;
-}
-
-// convert filter params cutoff and type into
-// iir transfer function params M, L, a[], b[]
-// iir filter, 1st order, transfer function is H(z) = b0 + b1 Z^-1 / a0 + a1 Z^-1
-// or H(z) = b0 - b1 Z^-1 / a0 + a1 Z^-1 for lowpass
-// design cutoff filter at 3db (.5 gain) p579
-void FLT_Design_3db_IIR( float cutoff, float ftype, int *pM, int *pL, int *a, int *b )
-{
- // ftype: FLT_LP, FLT_HP, FLT_BP
-
- double Wc = M_PI2 * cutoff / SOUND_DMA_SPEED; // radians per sample
- double Oc;
- double fa;
- double fb;
-
- // calculations:
- // Wc = 2pi * fc/44100 convert to radians
- // Oc = tan (Wc/2) * Gc / sqt ( 1 - Gc^2) get analog version, low pass
- // Oc = tan (Wc/2) * (sqt (1 - Gc^2)) / Gc analog version, high pass
- // Gc = 10 ^ (-Ac/20) gain at cutoff. Ac = 3db, so Gc^2 = 0.5
- // a = ( 1 - Oc ) / ( 1 + Oc )
- // b = ( 1 - a ) / 2
-
- Oc = tan( Wc / 2.0 );
-
- fa = ( 1.0 - Oc ) / ( 1.0 + Oc );
-
- fb = ( 1.0 - fa ) / 2.0;
-
- if( ftype == FLT_HP )
- fb = ( 1.0 + fa ) / 2.0;
-
- a[0] = 0; // a0 always ignored
- a[1] = (int)( -fa * PMAX ); // quantize params down to 0-PMAX >> PBITS
- b[0] = (int)( fb * PMAX );
- b[1] = b[0];
-
- if( ftype == FLT_HP )
- b[1] = -b[1];
-
- *pM = *pL = 1;
-}
-
-
-// convolution of x[n] with h[n], resulting in y[n]
-// h, x, y filter, input and output arrays (double precision)
-// M = filter order, L = input length
-// h is M+1 dimensional
-// x is L dimensional
-// y is L+M dimensional
-void conv( int M, double *h, int L, double *x, double *y )
-{
- int n, m;
-
- for( n = 0; n < L+M; n++ )
- {
- for( y[n] = 0, m = max(0, n-L+1); m <= min(n, M); m++ )
- {
- y[n] += h[m] * x[n-m];
- }
- }
-}
-
-// cas2can - convert cascaded, second order section parameter arrays to
-// canonical numerator/denominator arrays. Canonical implementations
-// have half as many multiplies as cascaded implementations.
-
-// K is number of cascaded sections
-// A is Kx3 matrix of sos params A[K] = A[0]..A[K-1]
-// a is (2K + 1) -dimensional output of canonical params
-
-#define KMAX 32 // max # of sos sections - 8 is the most we should ever see at runtime
-
-void cas2can( int K, double A[KMAX+1][3], int *aout )
-{
- int i, j;
- double d[2*KMAX + 1];
- double a[2*KMAX + 1];
-
- ASSERT( K <= KMAX );
-
- memset( d, 0, sizeof( double ) * ( 2 * KMAX + 1 ));
- memset( a, 0, sizeof( double ) * ( 2 * KMAX + 1 ));
-
- a[0] = 1;
-
- for( i = 0; i < K; i++ )
- {
- conv( 2, A[i], 2 * i + 1, a, d );
-
- for( j = 0; j < 2 * i + 3; j++ )
- a[j] = d[j];
- }
-
- for( i = 0; i < (2*K + 1); i++ )
- aout[i] = a[i] * PMAX;
-}
-
-
-// chebyshev IIR design, type 2, Lowpass or Highpass
-
-#define lnf( e ) ( 2.303 * log10( e ))
-#define acosh( e ) ( lnf( (e) + sqrt(( e ) * ( e ) - 1) ))
-#define asinh( e ) ( lnf( (e) + sqrt(( e ) * ( e ) + 1) ))
-
-
-// returns a[], b[] which are Kx3 matrices of cascaded second-order sections
-// these matrices may be passed directly to the iir_cas() routine for evaluation
-// Nmax - maximum order of filter
-// cutoff, ftype, qwidth - filter cutoff in hz, filter type FLT_LOWPASS/HIGHPASS, qwidth in hz
-// pM - denominator order
-// pL - numerator order
-// a - array of canonical filter params
-// b - array of canonical filter params
-void FLT_Design_Cheb( int Nmax, float cutoff, float ftype, float qwidth, int *pM, int *pL, int *a, int *b )
-{
-// p769 - converted from MATLAB
-
- double s = (ftype == FLT_LP ? 1 : -1 ); // 1 for LP, -1 for HP
- double fs = SOUND_DMA_SPEED; // sampling frequency
- double fpass = cutoff; // cutoff frequency
- double fstop = fpass + max (2000, qwidth); // stop frequency
- double Apass = 0.5; // max attenuation of pass band UNDONE: use Quality to select this
- double Astop = 10; // max amplitude of stop band UNDONE: use Quality to select this
-
- double Wpass, Wstop, epass, estop, Nex, aa;
- double W3, f3, W0, G, Wi2, W02, a1, a2, th, Wi, D, b1;
- int i, K, r, N;
- double A[KMAX+1][3]; // denominator output matrices, second order sections
- double B[KMAX+1][3]; // numerator output matrices, second order sections
-
- Wpass = tan( M_PI * fpass / fs );
- Wpass = pow( Wpass, s );
- Wstop = tan( M_PI * fstop / fs );
- Wstop = pow( Wstop, s );
-
- epass = sqrt( pow( (float)10.0f, (float)Apass/10.0f ) - 1 );
- estop = sqrt( pow( (float)10.0f, (float)Astop/10.0f ) - 1 );
-
- // calculate filter order N
-
- Nex = acosh( estop/epass ) / acosh ( Wstop/Wpass );
- N = min ( ceil(Nex), Nmax ); // don't exceed Nmax for filter order
- r = ( (int)N & 1); // r == 1 if N is odd
- K = (N - r ) / 2;
-
- aa = asinh ( estop ) / N;
- W3 = Wstop / cosh( acosh( estop ) / N );
- f3 = (fs / M_PI) * atan( pow( W3, s ));
-
- W0 = sinh( aa ) / Wstop;
- W02 = W0 * W0;
-
- // 1st order section for N odd
- if( r == 1 )
- {
- G = 1 / (1 + W0);
- A[0][0] = 1; A[0][1] = s * (2*G-1); A[0][2] = 0;
- B[0][0] = G; B[0][1] = G * s; B[0][2] = 0;
- }
- else
- {
- A[0][0] = 1; A[0][1] = 0; A[0][2] = 0;
- B[0][0] = 1; B[0][1] = 0; B[0][2] = 0;
- }
-
- for( i = 1; i <= K ; i++ )
- {
- th = M_PI * (N - 1 + 2 * i) / (2 * N);
- Wi = sin( th ) / Wstop;
- Wi2 = Wi * Wi;
-
- D = 1 - 2 * W0 * cos( th ) + W02 + Wi2;
- G = ( 1 + Wi2 ) / D;
-
- b1 = 2 * ( 1 - Wi2 ) / ( 1 + Wi2 );
- a1 = 2 * ( 1 - W02 - Wi2) / D;
- a2 = ( 1 + 2 * W0 * cos( th ) + W02 + Wi2) / D;
-
- A[i][0] = 1;
- A[i][1] = s * a1;
- A[i][2] = a2;
-
- B[i][0] = G;
- B[i][1] = G* s* b1;
- B[i][2] = G;
- }
-
- // convert cascade parameters to canonical parameters
-
- cas2can( K, A, a );
- *pM = 2*K + 1;
-
- cas2can( K, B, b );
- *pL = 2*K + 1;
-}
-
-// filter parameter order
-
-typedef enum
-{
- flt_iftype,
- flt_icutoff,
- flt_iqwidth,
- flt_iquality,
-
- flt_cparam // # of params
-} flt_e;
-
-// filter parameter ranges
-
-prm_rng_t flt_rng[] =
-{
-{ flt_cparam, 0, 0 }, // first entry is # of parameters
-{ flt_iftype, 0, FTR_MAX }, // filter type FLT_LP, FLT_HP, FLT_BP (UNDONE: FLT_BP currently ignored)
-{ flt_icutoff, 10, 22050 }, // cutoff frequency in hz at -3db gain
-{ flt_iqwidth, 100, 11025 }, // width of BP, or steepness of LP/HP (ie: fcutoff + qwidth = -60db gain point)
-{ flt_iquality, 0, QUA_MAX }, // QUA_LO, _MED, _HI 0,1,2,3
-};
-
-
-// convert prc float params to iir filter params, alloc filter and return ptr to it
-// filter quality set by prc quality - 0,1,2
-flt_t * FLT_Params ( prc_t *pprc )
-{
- float qual = pprc->prm[flt_iquality];
- float cutoff = pprc->prm[flt_icutoff];
- float ftype = pprc->prm[flt_iftype];
- float qwidth = pprc->prm[flt_iqwidth];
-
- int L = 0; // numerator order
- int M = 0; // denominator order
- int b[FLT_M+1]; // numerator params 0..PMAX
- int a[FLT_M+1]; // denominator params 0..PMAX
-
- // low pass and highpass filter design
-
- if( (int)qual == QUA_LO )
- qual = QUA_MED; // disable lowest quality filter - check perf on lowend KDB
-
- switch ( (int)qual )
- {
- case QUA_LO:
- // lowpass averaging filter: perf KDB
- ASSERT( ftype == FLT_LP );
- ASSERT( cutoff <= SOUND_DMA_SPEED );
- M = 0;
-
- // L is # of samples to average
-
- L = 0;
- if( cutoff <= SOUND_DMA_SPEED / 4 ) L = 1; // 11k
- if( cutoff <= SOUND_DMA_SPEED / 8 ) L = 2; // 5.5k
- if( cutoff <= SOUND_DMA_SPEED / 16 ) L = 4; // 2.75k
- if( cutoff <= SOUND_DMA_SPEED / 32 ) L = 8; // 1.35k
- if( cutoff <= SOUND_DMA_SPEED / 64 ) L = 12; // 750hz
-
- break;
- case QUA_MED:
- // 1st order IIR filter, 3db cutoff at fc
- FLT_Design_3db_IIR( cutoff, ftype, &M, &L, a, b );
-
- M = bound( 1, M, FLT_M );
- L = bound( 1, L, FLT_M );
- break;
- case QUA_HI:
- // type 2 chebyshev N = 4 IIR
- FLT_Design_Cheb( 4, cutoff, ftype, qwidth, &M, &L, a, b );
-
- M = bound( 1, M, FLT_M );
- L = bound( 1, L, FLT_M );
- break;
- case QUA_VHI:
- // type 2 chebyshev N = 7 IIR
- FLT_Design_Cheb( 8, cutoff, ftype, qwidth, &M, &L, a, b );
-
- M = bound( 1, M, FLT_M );
- L = bound( 1, L, FLT_M );
- break;
- }
-
- return FLT_Alloc( M, L, a, b );
-}
-
-_inline void * FLT_VParams( void *p )
-{
- PRC_CheckParams(( prc_t *)p, flt_rng );
- return (void *)FLT_Params ((prc_t *)p);
-}
-
-_inline void FLT_Mod( void *p, float v )
-{
-}
-
-// get next filter value for filter pf and input x
-_inline int FLT_GetNext( flt_t *pf, int x )
-{
- return iir_filter( pf->M, pf->a, pf->L, pf->b, pf->w, x );
-}
-
-// batch version for performance
-_inline void FLT_GetNextN( flt_t *pflt, portable_samplepair_t *pbuffer, int SampleCount, int op )
-{
- int count = SampleCount;
- portable_samplepair_t *pb = pbuffer;
-
- switch( op )
- {
- default:
- case OP_LEFT:
- while( count-- )
- {
- pb->left = FLT_GetNext( pflt, pb->left );
- pb++;
- }
- break;
- case OP_RIGHT:
- while( count-- )
- {
- pb->right = FLT_GetNext( pflt, pb->right );
- pb++;
- }
- break;
- case OP_LEFT_DUPLICATE:
- while( count-- )
- {
- pb->left = pb->right = FLT_GetNext( pflt, pb->left );
- pb++;
- }
- break;
- }
-}
-
-///////////////////////////////////////////////////////////////////////////
-// Positional updaters for pitch shift etc
-///////////////////////////////////////////////////////////////////////////
-
-// looping position within a wav, with integer and fractional parts
-// used for pitch shifting, upsampling/downsampling
-// 20 bits of fraction, 8+ bits of integer
-typedef struct
-{
-
- fix20int step; // wave table whole and fractional step value
- fix20int cstep; // current cummulative step value
- int pos; // current position within wav table
-
- int D; // max dimension of array w[0...D] ie: # of samples = D+1
-} pos_t;
-
-// circular wrap of pointer p, relative to array w
-// D max buffer index w[0...D] (count of samples in buffer is D+1)
-// i circular index
-_inline void POS_Wrap( int D, int *i )
-{
- if( *i > D ) *i -= D + 1; // when *pi = D + 1, it wraps around to *pi = 0
- if( *i < 0 ) *i += D + 1; // when *pi = - 1, it wraps around to *pi = D
-}
-
-// set initial update value - fstep can have no more than 8 bits of integer and 20 bits of fract
-// D is array max dimension w[0...D] (ie: size D+1)
-// w is ptr to array
-// p is ptr to pos_t to initialize
-_inline void POS_Init( pos_t *p, int D, float fstep )
-{
- float step = fstep;
-
- // make sure int part of step is capped at fix20_intmax
-
- if( (int)step > FIX20_INTMAX )
- step = (step - (int)step) + FIX20_INTMAX;
-
- p->step = FLOAT_TO_FIX20( step ); // convert fstep to fixed point
- p->cstep = 0;
- p->pos = 0; // current update value
- p->D = D; // always init to end value, in case we're stepping backwards
-}
-
-// change step value - this is an instantaneous change, not smoothed.
-_inline void POS_ChangeVal( pos_t *p, float fstepnew )
-{
- p->step = FLOAT_TO_FIX20( fstepnew ); // convert fstep to fixed point
-}
-
-// return current integer position, then update internal position value
-_inline int POS_GetNext ( pos_t *p )
-{
- // float f = FIX20_TO_FLOAT( p->cstep );
- // int i1 = FIX20_INTPART( p->cstep );
- // float f1 = FIX20_TO_FLOAT( FIX20_FRACPART( p->cstep ));
- // float f2 = FIX20_TO_FLOAT( p->step );
-
- p->cstep += p->step; // update accumulated fraction step value (fixed point)
- p->pos += FIX20_INTPART( p->cstep ); // update pos with integer part of accumulated step
- p->cstep = FIX20_FRACPART( p->cstep ); // throw away the integer part of accumulated step
-
- // wrap pos around either end of buffer if needed
- POS_Wrap( p->D, &( p->pos ));
-
- // make sure returned position is within array bounds
- ASSERT( p->pos <= p->D );
-
- return p->pos;
-}
-
-// oneshot position within wav
-typedef struct
-{
- pos_t p; // pos_t
- qboolean fhitend; // flag indicating we hit end of oneshot wav
-} pos_one_t;
-
-// set initial update value - fstep can have no more than 8 bits of integer and 20 bits of fract
-// one shot position - play only once, don't wrap, when hit end of buffer, return last position
-_inline void POS_ONE_Init( pos_one_t *p1, int D, float fstep )
-{
- POS_Init( &p1->p, D, fstep ) ;
-
- p1->fhitend = false;
-}
-
-// return current integer position, then update internal position value
-_inline int POS_ONE_GetNext( pos_one_t *p1 )
-{
- int pos;
- pos_t *p0;
-
- pos = p1->p.pos; // return current position
-
- if( p1->fhitend )
- return pos;
-
- p0 = &(p1->p);
- p0->cstep += p0->step; // update accumulated fraction step value (fixed point)
- p0->pos += FIX20_INTPART( p0->cstep ); // update pos with integer part of accumulated step
- //p0->cstep = SIGN(p0->cstep) * FIX20_FRACPART( p0->cstep );
- p0->cstep = FIX20_FRACPART( p0->cstep ); // throw away the integer part of accumulated step
-
- // if we wrapped, stop updating, always return last position
- // if step value is 0, return hit end
-
- if( !p0->step || p0->pos < 0 || p0->pos >= p0->D )
- p1->fhitend = true;
- else pos = p0->pos;
-
- // make sure returned value is within array bounds
- ASSERT( pos <= p0->D );
-
- return pos;
-}
-
-/////////////////////
-// Reverbs and delays
-/////////////////////
-#define CDLYS 128 // max delay lines active. Also used for lfos.
-
-#define DLY_PLAIN 0 // single feedback loop
-#define DLY_ALLPASS 1 // feedback and feedforward loop - flat frequency response (diffusor)
-#define DLY_LOWPASS 2 // lowpass filter in feedback loop
-#define DLY_LINEAR 3 // linear delay, no feedback, unity gain
-#define DLY_MAX DLY_LINEAR
-
-// delay line
-typedef struct
-{
- qboolean fused; // true if dly is in use
- int type; // delay type
- int D; // delay size, in samples
- int t; // current tap, <= D
- int D0; // original delay size (only relevant if calling DLY_ChangeVal)
- int *p; // circular buffer pointer
- int *w; // array of samples
- int a; // feedback value 0..PMAX,normalized to 0-1.0
- int b; // gain value 0..PMAX, normalized to 0-1.0
- flt_t *pflt; // pointer to filter, if type DLY_LOWPASS
- HANDLE h; // memory handle for sample array
-} dly_t;
-
-dly_t dlys[CDLYS]; // delay lines
-
-void DLY_Init( dly_t *pdly ) { if( pdly ) memset( pdly, 0, sizeof( dly_t )); }
-void DLY_InitAll( void ) { int i; for( i = 0; i < CDLYS; i++ ) DLY_Init( &dlys[i] ); }
-void DLY_Free( dly_t *pdly )
-{
- // free memory buffer
- if( pdly )
- {
- FLT_Free( pdly->pflt );
-
- if( pdly->w )
- {
- GlobalUnlock( pdly->h );
- GlobalFree( pdly->h );
- }
-
- // free dly slot
- memset( pdly, 0, sizeof( dly_t ));
- }
-}
-
-
-void DLY_FreeAll( void ) { int i; for( i = 0; i < CDLYS; i++ ) DLY_Free( &dlys[i] ); }
-
-// set up 'b' gain parameter of feedback delay to
-// compensate for gain caused by feedback.
-void DLY_SetNormalizingGain( dly_t *pdly )
-{
- // compute normalized gain, set as output gain
-
- // calculate gain of delay line with feedback, and use it to
- // reduce output. ie: force delay line with feedback to unity gain
-
- // for constant input x with feedback fb:
-
- // out = x + x*fb + x * fb^2 + x * fb^3...
- // gain = out/x
- // so gain = 1 + fb + fb^2 + fb^3...
- // which, by the miracle of geometric series, equates to 1/1-fb
- // thus, gain = 1/(1-fb)
-
- float fgain = 0;
- float gain;
- int b;
-
- // if b is 0, set b to PMAX (1)
- b = pdly->b ? pdly->b : PMAX;
-
- // fgain = b * (1.0 / (1.0 - (float)pdly->a / (float)PMAX)) / (float)PMAX;
- fgain = (1.0 / (1.0 - (float)pdly->a / (float)PMAX ));
-
- // compensating gain - multiply rva output by gain then >> PBITS
- gain = (int)((1.0 / fgain) * PMAX);
-
- gain = gain * 4; // compensate for fact that gain calculation is for +/- 32767 amplitude wavs
- // ie: ok to allow a bit more gain because most wavs are not at theoretical peak amplitude at all times
-
- gain = min( gain, PMAX ); // cap at PMAX
- gain = ((float)b/(float)PMAX) * gain; // scale final gain by pdly->b.
-
- pdly->b = (int)gain;
-}
-
-// allocate a new delay line
-// D number of samples to delay
-// a feedback value (0-PMAX normalized to 0.0-1.0)
-// b gain value (0-PMAX normalized to 0.0-1.0)
-// if DLY_LOWPASS:
-// L - numerator order of filter
-// M - denominator order of filter
-// fb - numerator params, M+1
-// fa - denominator params, L+1
-
-dly_t * DLY_AllocLP( int D, int a, int b, int type, int M, int L, int *fa, int *fb )
-{
- HANDLE h;
- int cb;
- int *w;
- int i;
- dly_t *pdly = NULL;
-
- // find open slot
- for( i = 0; i < CDLYS; i++ )
- {
- if( !dlys[i].fused )
- {
- pdly = &dlys[i];
- DLY_Init( pdly );
- break;
- }
- }
-
- if( i == CDLYS )
- {
- MsgDev( D_WARN, "DSP: failed to allocate delay line.\n" );
- return NULL; // all delay lines in use
- }
-
- cb = (D + 1) * sizeof( int ); // assume all samples are signed integers
-
- if( type == DLY_LOWPASS )
- {
- // alloc lowpass fir_filter
- pdly->pflt = FLT_Alloc( M, L, fa, fb );
- if( !pdly->pflt )
- {
- MsgDev( D_WARN, "DSP: failed to allocate filter for delay line.\n" );
- return NULL;
- }
- }
-
- // alloc delay memory
- h = GlobalAlloc( GMEM_MOVEABLE|GMEM_SHARE, cb );
- if( !h )
- {
- MsgDev( D_ERROR, "Sound DSP: Out of memory.\n" );
- FLT_Free( pdly->pflt );
- return NULL;
- }
-
- // lock delay memory
- w = (int *)GlobalLock( h );
-
- if( !w )
- {
- MsgDev( D_ERROR, "Sound DSP: Failed to lock.\n" );
- GlobalFree( h );
- FLT_Free( pdly->pflt );
- return NULL;
- }
-
- // clear delay array
- memset( w, 0, cb );
-
- // init values
- pdly->type = type;
- pdly->D = D;
- pdly->t = D; // set delay tap to full delay
- pdly->D0 = D;
- pdly->p = w; // init circular pointer to head of buffer
- pdly->w = w;
- pdly->h = h;
- pdly->a = min( a, PMAX ); // do not allow 100% feedback
- pdly->b = b;
- pdly->fused = true;
-
- if( type == DLY_LINEAR )
- {
- // linear delay has no feedback and unity gain
- pdly->a = 0;
- pdly->b = PMAX;
- }
- else
- {
- // adjust b to compensate for feedback gain
- DLY_SetNormalizingGain( pdly );
- }
-
- return pdly;
-}
-
-// allocate lowpass or allpass delay
-dly_t * DLY_Alloc( int D, int a, int b, int type )
-{
- return DLY_AllocLP( D, a, b, type, 0, 0, 0, 0 );
-}
-
-
-// Allocate new delay, convert from float params in prc preset to internal parameters
-// Uses filter params in prc if delay is type lowpass
-
-// delay parameter order
-typedef enum
-{
- dly_idtype, // NOTE: first 8 params must match those in mdy_e
- dly_idelay,
- dly_ifeedback,
- dly_igain,
- dly_iftype,
- dly_icutoff,
- dly_iqwidth,
- dly_iquality,
- dly_cparam
-} dly_e;
-
-
-// delay parameter ranges
-prm_rng_t dly_rng[] =
-{
-{ dly_cparam, 0, 0 }, // first entry is # of parameters
-
-// delay params
-{ dly_idtype, 0, DLY_MAX }, // delay type DLY_PLAIN, DLY_LOWPASS, DLY_ALLPASS
-{ dly_idelay, 0.0, 1000.0 }, // delay in milliseconds
-{ dly_ifeedback, 0.0, 0.99 }, // feedback 0-1.0
-{ dly_igain, 0.0, 1.0 }, // final gain of output stage, 0-1.0
-
-// filter params if dly type DLY_LOWPASS
-{ dly_iftype, 0, FTR_MAX },
-{ dly_icutoff, 10.0, 22050.0 },
-{ dly_iqwidth, 100.0, 11025.0 },
-{ dly_iquality, 0, QUA_MAX },
-};
-
-dly_t * DLY_Params( prc_t *pprc )
-{
- dly_t *pdly = NULL;
- int D, a, b;
-
- float delay = pprc->prm[dly_idelay];
- float feedback = pprc->prm[dly_ifeedback];
- float gain = pprc->prm[dly_igain];
- int type = pprc->prm[dly_idtype];
-
- float ftype = pprc->prm[dly_iftype];
- float cutoff = pprc->prm[dly_icutoff];
- float qwidth = pprc->prm[dly_iqwidth];
- float qual = pprc->prm[dly_iquality];
-
- D = MSEC_TO_SAMPS( delay ); // delay samples
- a = feedback * PMAX; // feedback
- b = gain * PMAX; // gain
-
- switch( type )
- {
- case DLY_PLAIN:
- case DLY_ALLPASS:
- case DLY_LINEAR:
- pdly = DLY_Alloc( D, a, b, type );
- break;
- case DLY_LOWPASS:
- {
- // set up dummy lowpass filter to convert params
- prc_t prcf;
- flt_t *pflt;
-
- // 0,1,2 - high, medium, low (low quality implies faster execution time)
- prcf.prm[flt_iquality] = qual;
- prcf.prm[flt_icutoff] = cutoff;
- prcf.prm[flt_iftype] = ftype;
- prcf.prm[flt_iqwidth] = qwidth;
-
- pflt = (flt_t *)FLT_Params( &prcf );
-
- if( !pflt )
- {
- MsgDev( D_WARN, "DSP: failed to allocate filter.\n" );
- return NULL;
- }
-
- pdly = DLY_AllocLP( D, a, b, type, pflt->M, pflt->L, pflt->a, pflt->b );
-
- FLT_Free( pflt );
- break;
- }
- }
- return pdly;
-}
-
-_inline void *DLY_VParams( void *p )
-{
- PRC_CheckParams(( prc_t *)p, dly_rng );
- return (void *) DLY_Params((prc_t *)p);
-}
-
-// get next value from delay line, move x into delay line
-int DLY_GetNext( dly_t *pdly, int x )
-{
- switch( pdly->type )
- {
- default:
- case DLY_PLAIN:
- return dly_plain( pdly->D, pdly->t, pdly->w, &pdly->p, pdly->a, pdly->b, x );
- case DLY_ALLPASS:
- return dly_allpass( pdly->D, pdly->t, pdly->w, &pdly->p, pdly->a, pdly->b, x );
- case DLY_LOWPASS:
- return dly_lowpass( pdly->D, pdly->t, pdly->w, &(pdly->p), pdly->a, pdly->b, pdly->pflt->M, pdly->pflt->a, pdly->pflt->L, pdly->pflt->b, pdly->pflt->w, x );
- case DLY_LINEAR:
- return dly_linear( pdly->D, pdly->t, pdly->w, &pdly->p, x );
- }
-}
-
-// batch version for performance
-void DLY_GetNextN( dly_t *pdly, portable_samplepair_t *pbuffer, int SampleCount, int op )
-{
- int count = SampleCount;
- portable_samplepair_t *pb = pbuffer;
-
- switch( op )
- {
- default:
- case OP_LEFT:
- while( count-- )
- {
- pb->left = DLY_GetNext( pdly, pb->left );
- pb++;
- }
- break;
- case OP_RIGHT:
- while( count-- )
- {
- pb->right = DLY_GetNext( pdly, pb->right );
- pb++;
- }
- break;
- case OP_LEFT_DUPLICATE:
- while( count-- )
- {
- pb->left = pb->right = DLY_GetNext( pdly, pb->left );
- pb++;
- }
- break;
- }
-}
-
-// get tap on t'th sample in delay - don't update buffer pointers, this is done via DLY_GetNext
-_inline int DLY_GetTap( dly_t *pdly, int t )
-{
- return tap( pdly->D, pdly->w, pdly->p, t );
-}
-
-
-// make instantaneous change to new delay value D.
-// t tap value must be <= original D (ie: we don't do any reallocation here)
-void DLY_ChangeVal( dly_t *pdly, int t )
-{
- // never set delay > original delay
- pdly->t = min( t, pdly->D0 );
-}
-
-// ignored - use MDY_ for modulatable delay
-_inline void DLY_Mod( void *p, float v )
-{
-}
-
-///////////////////
-// Parallel reverbs
-///////////////////
-
-// Reverb A
-// M parallel reverbs, mixed to mono output
-
-#define CRVAS 64 // max number of parallel series reverbs active
-#define CRVA_DLYS 12 // max number of delays making up reverb_a
-
-typedef struct
-{
- qboolean fused;
- int m; // number of parallel plain or lowpass delays
- int fparallel; // true if filters in parallel with delays, otherwise single output filter
- flt_t *pflt;
-
- dly_t *pdlys[CRVA_DLYS]; // array of pointers to delays
-} rva_t;
-
-rva_t rvas[CRVAS];
-
-void RVA_Init( rva_t *prva ) { if( prva ) memset( prva, 0, sizeof( rva_t )); }
-void RVA_InitAll( void ) { int i; for( i = 0; i < CRVAS; i++ ) RVA_Init( &rvas[i] ); }
-
-// free parallel series reverb
-void RVA_Free( rva_t *prva )
-{
- if( prva )
- {
- int i;
-
- // free all delays
- for( i = 0; i < CRVA_DLYS; i++)
- DLY_Free ( prva->pdlys[i] );
-
- FLT_Free( prva->pflt );
- memset( prva, 0, sizeof (rva_t) );
- }
-}
-
-
-void RVA_FreeAll( void ) { int i; for( i = 0; i < CRVAS; i++ ) RVA_Free( &rvas[i] ); }
-
-// create parallel reverb - m parallel reverbs summed
-// D array of CRVB_DLYS reverb delay sizes max sample index w[0...D] (ie: D+1 samples)
-// a array of reverb feedback parms for parallel reverbs (CRVB_P_DLYS)
-// b array of CRVB_P_DLYS - mix params for parallel reverbs
-// m - number of parallel delays
-// pflt - filter template, to be used by all parallel delays
-// fparallel - true if filter operates in parallel with delays, otherwise filter output only
-rva_t *RVA_Alloc( int *D, int *a, int *b, int m, flt_t *pflt, int fparallel )
-{
- int i;
- rva_t *prva;
- flt_t *pflt2 = NULL;
-
- // find open slot
- for( i = 0; i < CRVAS; i++ )
- {
- if( !rvas[i].fused )
- break;
- }
-
- // return null if no free slots
- if( i == CRVAS )
- {
- MsgDev( D_WARN, "DSP: failed to allocate reverb.\n" );
- return NULL;
- }
-
- prva = &rvas[i];
-
- // if series filter specified, alloc
- if( pflt && !fparallel )
- {
- // use filter data as template for a filter on output
- pflt2 = FLT_Alloc( pflt->M, pflt->L, pflt->a, pflt->b );
-
- if( !pflt2 )
- {
- MsgDev( D_WARN, "DSP: failed to allocate flt for reverb.\n" );
- return NULL;
- }
- }
-
- // alloc parallel reverbs
- if( pflt && fparallel )
- {
-
- // use this filter data as a template to alloc a filter for each parallel delay
- for( i = 0; i < m; i++ )
- prva->pdlys[i] = DLY_AllocLP( D[i], a[i], b[i], DLY_LOWPASS, pflt->M, pflt->L, pflt->a, pflt->b );
- }
- else
- {
- // no filter specified, use plain delays in parallel sections
- for( i = 0; i < m; i++ )
- prva->pdlys[i] = DLY_Alloc( D[i], a[i], b[i], DLY_PLAIN );
- }
-
-
- // if we failed to alloc any reverb, free all, return NULL
- for( i = 0; i < m; i++ )
- {
- if( !prva->pdlys[i] )
- {
- FLT_Free( pflt2 );
- RVA_Free( prva );
- MsgDev( D_WARN, "DSP: failed to allocate delay for reverb.\n" );
- return NULL;
- }
- }
-
- prva->fused = true;
- prva->m = m;
- prva->fparallel = fparallel;
- prva->pflt = pflt2;
-
- return prva;
-}
-
-
-// parallel reverberator
-//
-// for each input sample x do:
-// x0 = plain(D0,w0,&p0,a0,x)
-// x1 = plain(D1,w1,&p1,a1,x)
-// x2 = plain(D2,w2,&p2,a2,x)
-// x3 = plain(D3,w3,&p3,a3,x)
-// y = b0*x0 + b1*x1 + b2*x2 + b3*x3
-//
-// rgdly - array of 6 delays:
-// D - Delay values (typical - 29, 37, 44, 50, 27, 31)
-// w - array of delayed values
-// p - array of pointers to circular delay line pointers
-// a - array of 6 feedback values (typical - all equal, like 0.75 * PMAX)
-// b - array of 6 gain values for plain reverb outputs (1, .9, .8, .7)
-// xin - input value
-// if fparallel, filters are built into delays,
-// otherwise, filter output
-
-_inline int RVA_GetNext( rva_t *prva, int x )
-{
- int m = prva->m;
- int i, y, sum;
-
- sum = 0;
-
- for( i = 0; i < m; i++ )
- sum += DLY_GetNext( prva->pdlys[i], x );
-
- // m is clamped between RVA_BASEM & CRVA_DLYS
-
- if( m ) y = sum/m;
- else y = x;
-#if 0
- // PERFORMANCE:
- // UNDONE: build as array
- int mm;
-
- switch( m )
- {
- case 12: mm = (PMAX/12); break;
- case 11: mm = (PMAX/11); break;
- case 10: mm = (PMAX/10); break;
- case 9: mm = (PMAX/9); break;
- case 8: mm = (PMAX/8); break;
- case 7: mm = (PMAX/7); break;
- case 6: mm = (PMAX/6); break;
- case 5: mm = (PMAX/5); break;
- case 4: mm = (PMAX/4); break;
- case 3: mm = (PMAX/3); break;
- case 2: mm = (PMAX/2); break;
- default:
- case 1: mm = (PMAX/1); break;
- }
-
- y = (sum * mm) >> PBITS;
-
-#endif // 0
-
- // run series filter if present
- if( prva->pflt && !prva->fparallel )
- y = FLT_GetNext( prva->pflt, y );
-
- return y;
-}
-
-// batch version for performance
-_inline void RVA_GetNextN( rva_t *prva, portable_samplepair_t *pbuffer, int SampleCount, int op )
-{
- int count = SampleCount;
- portable_samplepair_t *pb = pbuffer;
-
- switch( op )
- {
- default:
- case OP_LEFT:
- while( count-- )
- {
- pb->left = RVA_GetNext( prva, pb->left );
- pb++;
- }
- break;
- case OP_RIGHT:
- while( count-- )
- {
- pb->right = RVA_GetNext( prva, pb->right );
- pb++;
- }
- break;
- case OP_LEFT_DUPLICATE:
- while( count-- )
- {
- pb->left = pb->right = RVA_GetNext( prva, pb->left );
- pb++;
- }
- break;
- }
-}
-
-#define RVA_BASEM 3 // base number of parallel delays
-
-// nominal delay and feedback values
-
-//float rvadlys[] = { 29, 37, 44, 50, 62, 75, 96, 118, 127, 143, 164, 175 };
-float rvadlys[] = { 18, 23, 28, 36, 47, 21, 26, 33, 40, 49, 45, 38 };
-float rvafbs[] = { 0.7, 0.7, 0.7, 0.8, 0.8, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9 };
-
-// reverb parameter order
-typedef enum
-{
- // parameter order
- rva_isize,
- rva_idensity,
- rva_idecay,
- rva_iftype,
- rva_icutoff,
- rva_iqwidth,
- rva_ifparallel,
- rva_cparam // # of params
-} rva_e;
-
-// filter parameter ranges
-prm_rng_t rva_rng[] =
-{
-{ rva_cparam, 0, 0 }, // first entry is # of parameters
-
-// reverb params
-{ rva_isize, 0.0, 2.0 }, // 0-2.0 scales nominal delay parameters (starting at approx 20ms)
-{ rva_idensity, 0.0, 2.0 }, // 0-2.0 density of reverbs (room shape) - controls # of parallel or series delays
-{ rva_idecay, 0.0, 2.0 }, // 0-2.0 scales feedback parameters (starting at approx 0.15)
-
-// filter params for each parallel reverb (quality set to 0 for max execution speed)
-{ rva_iftype, 0, FTR_MAX },
-{ rva_icutoff, 10, 22050 },
-{ rva_iqwidth, 100, 11025 },
-{ rva_ifparallel, 0, 1 } // if 1, then all filters operate in parallel with delays. otherwise filter output only
-};
-
-rva_t * RVA_Params( prc_t *pprc )
-{
- flt_t *pflt;
- rva_t *prva;
- float size = pprc->prm[rva_isize]; // 0-2.0 controls scale of delay parameters
- float density = pprc->prm[rva_idensity]; // 0-2.0 density of reverbs (room shape) - controls # of parallel delays
- float decay = pprc->prm[rva_idecay]; // 0-1.0 controls feedback parameters
-
- float ftype = pprc->prm[rva_iftype];
- float cutoff = pprc->prm[rva_icutoff];
- float qwidth = pprc->prm[rva_iqwidth];
-
- float fparallel = pprc->prm[rva_ifparallel];
-
- // D array of CRVB_DLYS reverb delay sizes max sample index w[0...D] (ie: D+1 samples)
- // a array of reverb feedback parms for parallel delays
- // b array of CRVB_P_DLYS - mix params for parallel reverbs
- // m - number of parallel delays
-
- int D[CRVA_DLYS];
- int a[CRVA_DLYS];
- int b[CRVA_DLYS];
- int m = RVA_BASEM;
- int i;
-
- m = density * CRVA_DLYS / 2;
-
- // limit # delays 3-12
- m = bound( RVA_BASEM, m, CRVA_DLYS );
-
- // average time sound takes to travel from most distant wall
- // (cap at 1000 ft room)
- for( i = 0; i < m; i++ )
- {
- // delays of parallel reverb
- D[i] = MSEC_TO_SAMPS( rvadlys[i] * size );
-
- // feedback and gain of parallel reverb
- a[i] = (int)min( 0.9 * PMAX, rvafbs[i] * (float)PMAX * decay );
- b[i] = PMAX;
- }
-
- // add filter
- pflt = NULL;
-
- if( cutoff )
- {
- // set up dummy lowpass filter to convert params
- prc_t prcf;
-
- prcf.prm[flt_iquality] = QUA_LO; // force filter to low quality for faster execution time
- prcf.prm[flt_icutoff] = cutoff;
- prcf.prm[flt_iftype] = ftype;
- prcf.prm[flt_iqwidth] = qwidth;
-
- pflt = (flt_t *)FLT_Params( &prcf );
- }
-
- prva = RVA_Alloc( D, a, b, m, pflt, fparallel );
- FLT_Free( pflt );
-
- return prva;
-}
-
-_inline void *RVA_VParams( void *p )
-{
- PRC_CheckParams((prc_t *)p, rva_rng );
- return (void *)RVA_Params((prc_t *)p );
-}
-
-_inline void RVA_Mod( void *p, float v )
-{
-}
-
-
-////////////
-// Diffusor
-///////////
-
-// (N series allpass reverbs)
-#define CDFRS 64 // max number of series reverbs active
-#define CDFR_DLYS 16 // max number of delays making up diffusor
-
-typedef struct
-{
- qboolean fused;
- int n; // series allpass delays
- int w[CDFR_DLYS]; // internal state array for series allpass filters
- dly_t *pdlys[CDFR_DLYS]; // array of pointers to delays
-} dfr_t;
-
-dfr_t dfrs[CDFRS];
-
-void DFR_Init( dfr_t *pdfr ) { if( pdfr ) memset( pdfr, 0, sizeof( dfr_t )); }
-void DFR_InitAll( void ) { int i; for( i = 0; i < CDFRS; i++ ) DFR_Init ( &dfrs[i] ); }
-
-// free parallel series reverb
-void DFR_Free( dfr_t *pdfr )
-{
- if( pdfr )
- {
- int i;
-
- // free all delays
- for( i = 0; i < CDFR_DLYS; i++ )
- DLY_Free( pdfr->pdlys[i] );
-
- memset( pdfr, 0, sizeof( dfr_t ));
- }
-}
-
-
-void DFR_FreeAll( void ) { int i; for( i = 0; i < CDFRS; i++ ) DFR_Free( &dfrs[i] ); }
-
-// create n series allpass reverbs
-// D array of CRVB_DLYS reverb delay sizes max sample index w[0...D] (ie: D+1 samples)
-// a array of reverb feedback parms for series delays
-// b array of gain params for parallel reverbs
-// n - number of series delays
-
-dfr_t *DFR_Alloc( int *D, int *a, int *b, int n )
-{
- int i;
- dfr_t *pdfr;
-
- // find open slot
- for( i = 0; i < CDFRS; i++ )
- {
- if( !dfrs[i].fused )
- break;
- }
-
- // return null if no free slots
- if( i == CDFRS )
- {
- MsgDev( D_WARN, "DSP: failed to allocate diffusor.\n" );
- return NULL;
- }
-
- pdfr = &dfrs[i];
-
- DFR_Init( pdfr );
-
- // alloc reverbs
- for( i = 0; i < n; i++ )
- pdfr->pdlys[i] = DLY_Alloc( D[i], a[i], b[i], DLY_ALLPASS );
-
- // if we failed to alloc any reverb, free all, return NULL
- for( i = 0; i < n; i++ )
- {
- if( !pdfr->pdlys[i] )
- {
- DFR_Free( pdfr );
- MsgDev( D_WARN, "DSP: failed to allocate delay for diffusor.\n" );
- return NULL;
- }
- }
-
- pdfr->fused = true;
- pdfr->n = n;
-
- return pdfr;
-}
-
-// series reverberator
-_inline int DFR_GetNext( dfr_t *pdfr, int x )
-{
- int i, y;
- int n = pdfr->n;
-
- y = x;
- for( i = 0; i < n; i++ )
- y = DLY_GetNext( pdfr->pdlys[i], y );
- return y;
-
-#if 0
- // alternate method, using internal state - causes PREDELAY = sum of delay times
-
- int *v = pdfr->w; // intermediate results
-
- v[0] = x;
-
- // reverse evaluate series delays
- // w[0] w[1] w[2] w[n-1] w[n]
- // x---->D[0]--->D[1]--->D[2]...-->D[n-1]--->out
- //
-
- for( i = n; i > 0; i-- )
- v[i] = DLY_GetNext( pdfr->pdlys[i-1], v[i-1] );
-
- return v[n];
-#endif
-}
-
-// batch version for performance
-_inline void DFR_GetNextN( dfr_t *pdfr, portable_samplepair_t *pbuffer, int SampleCount, int op )
-{
- int count = SampleCount;
- portable_samplepair_t *pb = pbuffer;
-
- switch( op )
- {
- default:
- case OP_LEFT:
- while( count-- )
- {
- pb->left = DFR_GetNext( pdfr, pb->left );
- pb++;
- }
- break;
- case OP_RIGHT:
- while( count-- )
- {
- pb->right = DFR_GetNext( pdfr, pb->right );
- pb++;
- }
- break;
- case OP_LEFT_DUPLICATE:
- while( count-- )
- {
- pb->left = pb->right = DFR_GetNext( pdfr, pb->left );
- pb++;
- }
- break;
- }
-}
-
-#define DFR_BASEN 2 // base number of series allpass delays
-
-// nominal diffusor delay and feedback values
-//float dfrdlys[] = { 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95 };
-float dfrdlys[] = { 13, 19, 26, 21, 32, 36, 38, 16, 24, 28, 41, 35, 10, 46, 50, 27 };
-float dfrfbs[] = { 0.15, 0.15, 0.15, 0.15, 0.15, 0.15, 0.15, 0.15, 0.15, 0.15, 0.15, 0.15, 0.15, 0.15, 0.15, 0.15 };
-
-
-// diffusor parameter order
-
-typedef enum
-{
- // parameter order
- dfr_isize,
- dfr_idensity,
- dfr_idecay,
- dfr_cparam // # of params
-
-} dfr_e;
-
-// diffusor parameter ranges
-
-prm_rng_t dfr_rng[] =
-{
-{ dfr_cparam, 0, 0 }, // first entry is # of parameters
-{ dfr_isize, 0.0, 1.0 }, // 0-1.0 scales all delays
-{ dfr_idensity, 0.0, 1.0 }, // 0-1.0 controls # of series delays
-{ dfr_idecay, 0.0, 1.0 }, // 0-1.0 scales all feedback parameters
-};
-
-dfr_t *DFR_Params( prc_t *pprc )
-{
- dfr_t *pdfr;
- int i, s;
- float size = pprc->prm[dfr_isize]; // 0-1.0 scales all delays
- float density = pprc->prm[dfr_idensity]; // 0-1.0 controls # of series delays
- float diffusion = pprc->prm[dfr_idecay]; // 0-1.0 scales all feedback parameters
-
- // D array of CRVB_DLYS reverb delay sizes max sample index w[0...D] (ie: D+1 samples)
- // a array of reverb feedback parms for series delays (CRVB_S_DLYS)
- // b gain of each reverb section
- // n - number of series delays
-
- int D[CDFR_DLYS];
- int a[CDFR_DLYS];
- int b[CDFR_DLYS];
- int n = DFR_BASEN;
-
- // increase # of series diffusors with increased density
- n += density * 2;
-
- // limit m, n to half max number of delays
- n = min( CDFR_DLYS / 2, n );
-
- // compute delays for diffusors
- for( i = 0; i < n; i++ )
- {
- s = (int)( dfrdlys[i] * size );
-
- // delay of diffusor
- D[i] = MSEC_TO_SAMPS( s );
-
- // feedback and gain of diffusor
- a[i] = min( 0.9 * PMAX, dfrfbs[i] * PMAX * diffusion );
- b[i] = PMAX;
- }
-
- pdfr = DFR_Alloc( D, a, b, n );
-
- return pdfr;
-}
-
-_inline void *DFR_VParams( void *p )
-{
- PRC_CheckParams((prc_t *)p, dfr_rng );
- return (void *)DFR_Params((prc_t *)p );
-}
-
-_inline void DFR_Mod( void *p, float v )
-{
-}
-
-//////////////////////
-// LFO wav definitions
-//////////////////////
-
-#define CLFOSAMPS 512 // samples per wav table - single cycle only
-#define LFOBITS 14 // bits of peak amplitude of lfo wav
-#define LFOAMP ((1<<LFOBITS)-1) // peak amplitude of lfo wav
-
-//types of lfo wavs
-
-#define LFO_SIN 0 // sine wav
-#define LFO_TRI 1 // triangle wav
-#define LFO_SQR 2 // square wave, 50% duty cycle
-#define LFO_SAW 3 // forward saw wav
-#define LFO_RND 4 // random wav
-#define LFO_LOG_IN 5 // logarithmic fade in
-#define LFO_LOG_OUT 6 // logarithmic fade out
-#define LFO_LIN_IN 7 // linear fade in
-#define LFO_LIN_OUT 8 // linear fade out
-#define LFO_MAX LFO_LIN_OUT
-
-#define CLFOWAV 9 // number of LFO wav tables
-
-typedef struct // lfo or envelope wave table
-{
- int type; // lfo type
- dly_t *pdly; // delay holds wav values and step pointers
-} lfowav_t;
-
-lfowav_t lfowavs[CLFOWAV];
-
-// deallocate lfo wave table. Called only when sound engine exits.
-void LFOWAV_Free( lfowav_t *plw )
-{
- // free delay
- if( plw ) DLY_Free( plw->pdly );
-
- memset( plw, 0, sizeof( lfowav_t ));
-}
-
-// deallocate all lfo wave tables. Called only when sound engine exits.
-void LFOWAV_FreeAll( void )
-{
- int i;
-
- for( i = 0; i < CLFOWAV; i++ )
- LFOWAV_Free( &lfowavs[i] );
-}
-
-// fill lfo array w with count samples of lfo type 'type'
-// all lfo wavs except fade out, rnd, and log_out should start with 0 output
-void LFOWAV_Fill( int *w, int count, int type )
-{
- int i,x;
-
- switch( type )
- {
- default:
- case LFO_SIN: // sine wav, all values 0 <= x <= LFOAMP, initial value = 0
- for( i = 0; i < count; i++ )
- {
- x = ( int )(( float)(LFOAMP) * sin( (M_PI2 * (float)i / (float)count ) + ( M_PI_F * 1.5 )));
- w[i] = (x + LFOAMP)/2;
- }
- break;
- case LFO_TRI: // triangle wav, all values 0 <= x <= LFOAMP, initial value = 0
- for( i = 0; i < count; i++ )
- {
- w[i] = ( int ) ( (float)(2 * LFOAMP * i ) / (float)(count) );
-
- if( i > count / 2 )
- w[i] = ( int )( (float) (2 * LFOAMP) - (float)( 2 * LFOAMP * i ) / (float)( count ));
- }
- break;
- case LFO_SQR: // square wave, 50% duty cycle, all values 0 <= x <= LFOAMP, initial value = 0
- for( i = 0; i < count; i++ )
- w[i] = i > count / 2 ? 0 : LFOAMP;
- break;
- case LFO_SAW: // forward saw wav, aall values 0 <= x <= LFOAMP, initial value = 0
- for( i = 0; i < count; i++ )
- w[i] = ( int )( (float)(LFOAMP) * (float)i / (float)( count ));
- break;
- case LFO_RND: // random wav, all values 0 <= x <= LFOAMP
- for( i = 0; i < count; i++ )
- w[i] = ( int )( Com_RandomLong( 0, LFOAMP ));
- break;
- case LFO_LOG_IN: // logarithmic fade in, all values 0 <= x <= LFOAMP, initial value = 0
- for( i = 0; i < count; i++ )
- w[i] = ( int ) ( (float)(LFOAMP) * pow( (float)i / (float)count, 2 ));
- break;
- case LFO_LOG_OUT: // logarithmic fade out, all values 0 <= x <= LFOAMP, initial value = LFOAMP
- for( i = 0; i < count; i++ )
- w[i] = ( int ) ( (float)(LFOAMP) * pow( 1.0 - ((float)i / (float)count), 2 ));
- break;
- case LFO_LIN_IN: // linear fade in, all values 0 <= x <= LFOAMP, initial value = 0
- for( i = 0; i < count; i++ )
- w[i] = ( int )( (float)(LFOAMP) * (float)i / (float)(count) );
- break;
- case LFO_LIN_OUT: // linear fade out, all values 0 <= x <= LFOAMP, initial value = LFOAMP
- for( i = 0; i < count; i++ )
- w[i] = LFOAMP - ( int )( (float)(LFOAMP) * (float)i / (float)(count) );
- break;
- }
-}
-
-// allocate all lfo wave tables. Called only when sound engine loads.
-void LFOWAV_InitAll( void )
-{
- int i;
- dly_t *pdly;
-
- memset( lfowavs, 0, sizeof( lfowavs ));
-
- // alloc space for each lfo wav type
- for( i = 0; i < CLFOWAV; i++ )
- {
- pdly = DLY_Alloc( CLFOSAMPS, 0, 0 , DLY_PLAIN );
-
- lfowavs[i].pdly = pdly;
- lfowavs[i].type = i;
-
- LFOWAV_Fill( pdly->w, CLFOSAMPS, i );
- }
-
- // if any dlys fail to alloc, free all
- for( i = 0; i < CLFOWAV; i++ )
- {
- if( !lfowavs[i].pdly )
- LFOWAV_FreeAll();
- }
-}
-
-
-////////////////////////////////////////
-// LFO iterators - one shot and looping
-////////////////////////////////////////
-
-#define CLFO 16 // max active lfos (this steals from active delays)
-
-typedef struct
-{
- qboolean fused; // true if slot take
- dly_t *pdly; // delay points to lfo wav within lfowav_t (don't free this)
- float f; // playback frequency in hz
- pos_t pos; // current position within wav table, looping
- pos_one_t pos1; // current position within wav table, one shot
- int foneshot; // true - one shot only, don't repeat
-} lfo_t;
-
-lfo_t lfos[CLFO];
-
-void LFO_Init( lfo_t *plfo ) { if( plfo ) memset( plfo, 0, sizeof( lfo_t )); }
-void LFO_InitAll( void ) { int i; for( i = 0; i < CLFO; i++ ) LFO_Init( &lfos[i] ); }
-void LFO_Free( lfo_t *plfo ) { if( plfo ) memset( plfo, 0, sizeof( lfo_t )); }
-void LFO_FreeAll( void ) { int i; for( i = 0; i < CLFO; i++ ) LFO_Free( &lfos[i] ); }
-
-
-// get step value given desired playback frequency
-_inline float LFO_HzToStep( float freqHz )
-{
- float lfoHz;
-
- // calculate integer and fractional step values,
- // assume an update rate of SOUND_DMA_SPEED samples/sec
-
- // 1 cycle/CLFOSAMPS * SOUND_DMA_SPEED samps/sec = cycles/sec = current lfo rate
- //
- // lforate * X = freqHz so X = freqHz/lforate = update rate
- lfoHz = (float)(SOUND_DMA_SPEED) / (float)(CLFOSAMPS);
-
- return freqHz / lfoHz;
-}
-
-// return pointer to new lfo
-
-lfo_t *LFO_Alloc( int wtype, float freqHz, qboolean foneshot )
-{
- int i, type = min( CLFOWAV - 1, wtype );
- float lfostep;
-
- for( i = 0; i < CLFO; i++ )
- {
- if( !lfos[i].fused )
- {
- lfo_t *plfo = &lfos[i];
-
- LFO_Init( plfo );
-
- plfo->fused = true;
- plfo->pdly = lfowavs[type].pdly; // pdly in lfo points to wav table data in lfowavs
- plfo->f = freqHz;
- plfo->foneshot = foneshot;
-
- lfostep = LFO_HzToStep( freqHz );
-
- // init positional pointer (ie: fixed point updater for controlling pitch of lfo)
- if( !foneshot ) POS_Init(&(plfo->pos), plfo->pdly->D, lfostep );
- else POS_ONE_Init(&(plfo->pos1), plfo->pdly->D,lfostep );
-
- return plfo;
- }
- }
-
- MsgDev( D_WARN, "DSP: failed to allocate LFO.\n" );
- return NULL;
-}
-
-// get next lfo value
-// Value returned is 0..LFOAMP. can be normalized by shifting right by LFOBITS
-// To play back at correct passed in frequency, routien should be
-// called once for every output sample (ie: at SOUND_DMA_SPEED)
-// x is dummy param
-_inline int LFO_GetNext( lfo_t *plfo, int x )
-{
- int i;
-
- // get current position
- if( !plfo->foneshot ) i = POS_GetNext( &plfo->pos );
- else i = POS_ONE_GetNext( &plfo->pos1 );
-
- // return current sample
- return plfo->pdly->w[i];
-}
-
-// batch version for performance
-_inline void LFO_GetNextN( lfo_t *plfo, portable_samplepair_t *pbuffer, int SampleCount, int op )
-{
- int count = SampleCount;
- portable_samplepair_t *pb = pbuffer;
-
- switch( op )
- {
- default:
- case OP_LEFT:
- while( count-- )
- {
- pb->left = LFO_GetNext( plfo, pb->left );
- pb++;
- }
- break;
- case OP_RIGHT:
- while( count-- )
- {
- pb->right = LFO_GetNext( plfo, pb->right );
- pb++;
- }
- break;
- case OP_LEFT_DUPLICATE:
- while( count-- )
- {
- pb->left = pb->right = LFO_GetNext( plfo, pb->left );
- pb++;
- }
- break;
- }
-}
-
-// uses lfowav, rate, foneshot
-typedef enum
-{
- // parameter order
- lfo_iwav,
- lfo_irate,
- lfo_ifoneshot,
- lfo_cparam // # of params
-
-} lfo_e;
-
-// parameter ranges
-
-prm_rng_t lfo_rng[] =
-{
-{ lfo_cparam, 0, 0 }, // first entry is # of parameters
-{ lfo_iwav, 0.0, LFO_MAX }, // lfo type to use (LFO_SIN, LFO_RND...)
-{ lfo_irate, 0.0, 16000.0 }, // modulation rate in hz. for MDY, 1/rate = 'glide' time in seconds
-{ lfo_ifoneshot, 0.0, 1.0 }, // 1.0 if lfo is oneshot
-};
-
-lfo_t * LFO_Params( prc_t *pprc )
-{
- lfo_t *plfo;
- qboolean foneshot = pprc->prm[lfo_ifoneshot] > 0 ? true : false;
-
- plfo = LFO_Alloc( pprc->prm[lfo_iwav], pprc->prm[lfo_irate], foneshot );
-
- return plfo;
-}
-
-void LFO_ChangeVal( lfo_t *plfo, float fhz )
-{
- float fstep = LFO_HzToStep( fhz );
-
- // change lfo playback rate to new frequency fhz
- if( plfo->foneshot ) POS_ChangeVal( &plfo->pos, fstep );
- else POS_ChangeVal( &plfo->pos1.p, fstep );
-}
-
-_inline void *LFO_VParams( void *p )
-{
- PRC_CheckParams((prc_t *)p, lfo_rng );
- return (void *)LFO_Params((prc_t *)p);
-}
-
-// v is +/- 0-1.0
-// v changes current lfo frequency up/down by +/- v%
-_inline void LFO_Mod( lfo_t *plfo, float v )
-{
- float fhz;
- float fhznew;
-
- fhz = plfo->f;
- fhznew = fhz * (1.0 + v);
-
- LFO_ChangeVal( plfo, fhznew );
-
- return;
-}
-
-
-/////////////////////////////////////////////////////////////////////////////
-// Ramp - used for varying smoothly between int parameters ie: modulation delays
-/////////////////////////////////////////////////////////////////////////////
-typedef struct
-{
- int initval; // initial ramp value
- int target; // final ramp value
- int sign; // increasing (1) or decreasing (-1) ramp
- int yprev; // previous output value
- qboolean fhitend; // true if hit end of ramp
- pos_one_t ps; // current ramp output
-} rmp_t;
-
-// ramp smoothly between initial value and target value in approx 'ramptime' seconds.
-// (initial value may be greater or less than target value)
-// never changes output by more than +1 or -1 (which can cause the ramp to take longer to complete than ramptime)
-// called once per sample while ramping
-// ramptime - duration of ramp in seconds
-// initval - initial ramp value
-// targetval - target ramp value
-void RMP_Init( rmp_t *prmp, float ramptime, int initval, int targetval )
-{
- int rise;
- int run;
-
- if( prmp ) memset( prmp, 0, sizeof( rmp_t ));
-
- run = (int)( ramptime * SOUND_DMA_SPEED ); // 'samples' in ramp
- rise = (targetval - initval); // height of ramp
-
- // init fixed point iterator to iterate along the height of the ramp 'rise'
- // always iterates from 0..'rise', increasing in value
-
- POS_ONE_Init( &prmp->ps, ABS( rise ), ABS((float) rise) / ((float) run));
-
- prmp->yprev = initval;
- prmp->initval = initval;
- prmp->target = targetval;
- prmp->sign = SIGN( rise );
-
-}
-
-// continues from current position to new target position
-void RMP_SetNext( rmp_t *prmp, float ramptime, int targetval )
-{
- RMP_Init( prmp, ramptime, prmp->yprev, targetval );
-}
-
-_inline qboolean RMP_HitEnd( rmp_t *prmp )
-{
- return prmp->fhitend;
-}
-
-_inline void RMP_SetEnd( rmp_t *prmp )
-{
- prmp->fhitend = true;
-}
-
-// get next ramp value & update ramp, never varies by more than +1 or -1 between calls
-// when ramp hits target value, it thereafter always returns last value
-
-_inline int RMP_GetNext( rmp_t *prmp )
-{
- int y, d;
-
- // if we hit ramp end, return last value
- if( prmp->fhitend )
- return prmp->yprev;
-
- // get next integer position in ramp height.
- d = POS_ONE_GetNext( &prmp->ps );
-
- if( prmp->ps.fhitend )
- prmp->fhitend = true;
-
- // increase or decrease from initval, depending on ramp sign
- if( prmp->sign > 0 )
- y = prmp->initval + d;
- else y = prmp->initval - d;
-
- // only update current height by a max of +1 or -1
- // this means that for short ramp times, we may not hit target
- if( ABS( y - prmp->yprev ) >= 1 )
- prmp->yprev += prmp->sign;
-
- return prmp->yprev;
-}
-
-// get current ramp value, don't update ramp
-_inline int RMP_GetCurrent( rmp_t *prmp )
-{
- return prmp->yprev;
-}
-
-////////////////////////////////////////
-// Time Compress/expand with pitch shift
-////////////////////////////////////////
-
-// realtime pitch shift - ie: pitch shift without change to playback rate
-
-#define CPTCS 64
-
-typedef struct
-{
- qboolean fused;
- dly_t *pdly_in; // input buffer space
- dly_t *pdly_out; // output buffer space
- int *pin; // input buffer (pdly_in->w)
- int *pout; // output buffer (pdly_out->w)
- int cin; // # samples in input buffer
- int cout; // # samples in output buffer
- int cxfade; // # samples in crossfade segment
- int ccut; // # samples to cut
- int cduplicate; // # samples to duplicate (redundant - same as ccut)
- int iin; // current index into input buffer (reading)
- pos_one_t psn; // stepping index through output buffer
- qboolean fdup; // true if duplicating, false if cutting
- float fstep; // pitch shift & time compress/expand
-} ptc_t;
-
-ptc_t ptcs[CPTCS];
-
-void PTC_Init( ptc_t *pptc ) { if( pptc ) memset( pptc, 0, sizeof( ptc_t )); };
-void PTC_Free( ptc_t *pptc )
-{
- if( pptc )
- {
- DLY_Free( pptc->pdly_in );
- DLY_Free( pptc->pdly_out );
-
- memset( pptc, 0, sizeof( ptc_t ));
- }
-};
-
-void PTC_InitAll() { int i; for( i = 0; i < CPTCS; i++ ) PTC_Init( &ptcs[i] ); };
-void PTC_FreeAll() { int i; for( i = 0; i < CPTCS; i++ ) PTC_Free( &ptcs[i] ); };
-
-// Time compressor/expander with pitch shift (ie: pitch changes, playback rate does not)
-//
-// Algorithm:
-// 1) Duplicate or discard chunks of sound to provide tslice * fstep seconds of sound.
-// (The user-selectable size of the buffer to process is tslice milliseconds in length)
-// 2) Resample this compressed/expanded buffer at fstep to produce a pitch shifted
-// output with the same duration as the input (ie: #samples out = # samples in, an
-// obvious requirement for realtime _inline processing).
-
-// timeslice is size in milliseconds of full buffer to process.
-// timeslice * fstep is the size of the expanded/compressed buffer
-// timexfade is length in milliseconds of crossfade region between duplicated or cut sections
-// fstep is % expanded/compressed sound normalized to 0.01-2.0 (1% - 200%)
-
-// input buffer:
-
-// iin-->
-
-// [0... tslice ...D] input samples 0...D (D is NEWEST sample)
-// [0... ...n][m... tseg ...D] region to be cut or duplicated m...D
-
-// [0... [p..txf1..n][m... tseg ...D] fade in region 1 txf1 p...n
-// [0... ...n][m..[q..txf2..D] fade out region 2 txf2 q...D
-
-
-// pitch up: duplicate into output buffer: tdup = tseg
-
-// [0... ...n][m... tdup ...D][m... tdup ...D] output buffer size with duplicate region
-// [0... ...n][m..[p...xf1..n][m... tdup ...D] fade in p...n while fading out q...D
-// [0... ...n][m..[q...xf2..D][m... tdup ...D]
-// [0... ...n][m..[.XFADE...n][m... tdup ...D] final duplicated output buffer - resample at fstep
-
-// pitch down: cut into output buffer: tcut = tseg
-
-// [0... ...n][m... tcut ...D] input samples with cut region delineated m...D
-// [0... ...n] output buffer size after cut
-// [0... [q..txf2...D] fade in txf1 q...D while fade out txf2 p...n
-// [0... [.XFADE ...D] final cut output buffer - resample at fstep
-
-
-ptc_t * PTC_Alloc( float timeslice, float timexfade, float fstep )
-{
- int i;
- ptc_t *pptc;
- float tout;
- int cin, cout;
- float tslice = timeslice;
- float txfade = timexfade;
- float tcutdup;
-
- // find time compressor slot
- for( i = 0; i < CPTCS; i++ )
- {
- if( !ptcs[i].fused )
- break;
- }
-
- if( i == CPTCS )
- {
- MsgDev( D_WARN, "DSP: failed to allocate pitch shifter.\n" );
- return NULL;
- }
-
- pptc = &ptcs[i];
- PTC_Init( pptc );
-
- // get size of region to cut or duplicate
- tcutdup = abs(( fstep - 1.0 ) * timeslice );
-
- // to prevent buffer overruns:
-
- // make sure timeslice is greater than cut/dup time
- tslice = max ( tslice, 1.1 * tcutdup);
-
- // make sure xfade time smaller than cut/dup time, and smaller than (timeslice-cutdup) time
- txfade = min( txfade, 0.9 * tcutdup );
- txfade = min( txfade, 0.9 * ( tslice - tcutdup ));
-
- pptc->cxfade = MSEC_TO_SAMPS( txfade );
- pptc->ccut = MSEC_TO_SAMPS( tcutdup );
- pptc->cduplicate = MSEC_TO_SAMPS( tcutdup );
-
- // alloc delay lines (buffers)
- tout = tslice * fstep;
-
- cin = MSEC_TO_SAMPS( tslice );
- cout = MSEC_TO_SAMPS( tout );
-
- pptc->pdly_in = DLY_Alloc( cin, 0, 1, DLY_LINEAR ); // alloc input buffer
- pptc->pdly_out = DLY_Alloc( cout, 0, 1, DLY_LINEAR ); // alloc output buffer
-
- if( !pptc->pdly_in || !pptc->pdly_out )
- {
- PTC_Free( pptc );
- MsgDev( D_WARN, "DSP: failed to allocate delay for pitch shifter.\n" );
- return NULL;
- }
-
- // buffer pointers
- pptc->pin = pptc->pdly_in->w;
- pptc->pout = pptc->pdly_out->w;
-
- // input buffer index
- pptc->iin = 0;
-
- // output buffer index
- POS_ONE_Init( &pptc->psn, cout, fstep );
-
- // if fstep > 1.0 we're pitching shifting up, so fdup = true
- pptc->fdup = fstep > 1.0 ? true : false;
-
- pptc->cin = cin;
- pptc->cout = cout;
-
- pptc->fstep = fstep;
- pptc->fused = true;
-
- return pptc;
-}
-
-// linear crossfader
-// yfadein - instantaneous value fading in
-// ydafeout -instantaneous value fading out
-// nsamples - duration in #samples of fade
-// isample - index in to fade 0...nsamples-1
-_inline int xfade( int yfadein, int yfadeout, int nsamples, int isample )
-{
- int yout;
- int m = (isample << PBITS ) / nsamples;
-
- yout = ((yfadein * m) >> PBITS) + ((yfadeout * (PMAX - m)) >> PBITS);
-
- return yout;
-}
-
-// w - pointer to start of input buffer samples
-// v - pointer to start of output buffer samples
-// cin - # of input buffer samples
-// cout = # of output buffer samples
-// cxfade = # of crossfade samples
-// cduplicate = # of samples in duplicate/cut segment
-void TimeExpand( int *w, int *v, int cin, int cout, int cxfade, int cduplicate )
-{
- int i, j;
- int m;
- int p;
- int q;
- int D;
-
- // input buffer
- // xfade source duplicate
- // [0...........][p.......n][m...........D]
-
- // output buffer
- // xfade region duplicate
- // [0.....................n][m..[q.......D][m...........D]
-
- // D - index of last sample in input buffer
- // m - index of 1st sample in duplication region
- // p - index of 1st sample of crossfade source
- // q - index of 1st sample in crossfade region
-
- D = cin - 1;
- m = cin - cduplicate;
- p = m - cxfade;
- q = cin - cxfade;
-
- // copy up to crossfade region
- for( i = 0; i < q; i++ )
- v[i] = w[i];
-
- // crossfade region
- j = p;
-
- for( i = q; i <= D; i++ )
- v[i] = xfade( w[j++], w[i], cxfade, i-q ); // fade out p..n, fade in q..D
-
- // duplicate region
- j = D+1;
-
- for( i = m; i <= D; i++ )
- v[j++] = w[i];
-
-}
-
-// cut ccut samples from end of input buffer, crossfade end of cut section
-// with end of remaining section
-
-// w - pointer to start of input buffer samples
-// v - pointer to start of output buffer samples
-// cin - # of input buffer samples
-// cout = # of output buffer samples
-// cxfade = # of crossfade samples
-// ccut = # of samples in cut segment
-void TimeCompress( int *w, int *v, int cin, int cout, int cxfade, int ccut )
-{
- int i, j;
- int m;
- int p;
- int q;
- int D;
-
- // input buffer
- // xfade source
- // [0.....................n][m..[p.......D]
-
- // xfade region cut
- // [0...........][q.......n][m...........D]
-
- // output buffer
- // xfade to source
- // [0...........][p.......D]
-
- // D - index of last sample in input buffer
- // m - index of 1st sample in cut region
- // p - index of 1st sample of crossfade source
- // q - index of 1st sample in crossfade region
-
- D = cin - 1;
- m = cin - ccut;
- p = cin - cxfade;
- q = m - cxfade;
-
- // copy up to crossfade region
-
- for( i = 0; i < q; i++ )
- v[i] = w[i];
-
- // crossfade region
- j = p;
-
- for( i = q; i < m; i++ )
- v[i] = xfade( w[j++], w[i], cxfade, i-q ); // fade out p..n, fade in q..D
-
- // skip rest of input buffer
-}
-
-// get next sample
-
-// put input sample into input (delay) buffer
-// get output sample from output buffer, step by fstep %
-// output buffer is time expanded or compressed version of previous input buffer
-_inline int PTC_GetNext( ptc_t *pptc, int x )
-{
- int iout, xout;
- qboolean fhitend = false;
-
- // write x into input buffer
- ASSERT( pptc->iin < pptc->cin );
-
- pptc->pin[pptc->iin] = x;
-
- pptc->iin++;
-
- // check for end of input buffer
- if( pptc->iin >= pptc->cin )
- fhitend = true;
-
- // read sample from output buffer, resampling at fstep
- iout = POS_ONE_GetNext( &pptc->psn );
- ASSERT( iout < pptc->cout );
- xout = pptc->pout[iout];
-
- if( fhitend )
- {
- // if hit end of input buffer (ie: input buffer is full)
- // reset input buffer pointer
- // reset output buffer pointer
- // rebuild entire output buffer (TimeCompress/TimeExpand)
-
- pptc->iin = 0;
-
- POS_ONE_Init( &pptc->psn, pptc->cout, pptc->fstep );
-
- if( pptc->fdup ) TimeExpand ( pptc->pin, pptc->pout, pptc->cin, pptc->cout, pptc->cxfade, pptc->cduplicate );
- else TimeCompress ( pptc->pin, pptc->pout, pptc->cin, pptc->cout, pptc->cxfade, pptc->ccut );
- }
-
- return xout;
-}
-
-// batch version for performance
-_inline void PTC_GetNextN( ptc_t *pptc, portable_samplepair_t *pbuffer, int SampleCount, int op )
-{
- int count = SampleCount;
- portable_samplepair_t *pb = pbuffer;
-
- switch( op )
- {
- default:
- case OP_LEFT:
- while( count-- )
- {
- pb->left = PTC_GetNext( pptc, pb->left );
- pb++;
- }
- break;
- case OP_RIGHT:
- while( count-- )
- {
- pb->right = PTC_GetNext( pptc, pb->right );
- pb++;
- }
- break;
- case OP_LEFT_DUPLICATE:
- while( count-- )
- {
- pb->left = pb->right = PTC_GetNext( pptc, pb->left );
- pb++;
- }
- break;
- }
-}
-
-// change time compression to new value
-// fstep is new value
-// ramptime is how long change takes in seconds (ramps smoothly), 0 for no ramp
-
-void PTC_ChangeVal( ptc_t *pptc, float fstep, float ramptime )
-{
-// UNDONE: ignored
-// UNDONE: just realloc time compressor with new fstep
-}
-
-// uses pitch:
-// 1.0 = playback normal rate
-// 0.5 = cut 50% of sound (2x playback)
-// 1.5 = add 50% sound (0.5x playback)
-
-typedef enum
-{
- // parameter order
- ptc_ipitch,
- ptc_itimeslice,
- ptc_ixfade,
- ptc_cparam // # of params
-} ptc_e;
-
-// diffusor parameter ranges
-prm_rng_t ptc_rng[] =
-{
-{ ptc_cparam, 0, 0 }, // first entry is # of parameters
-{ ptc_ipitch, 0.1, 4.0 }, // 0-n.0 where 1.0 = 1 octave up and 0.5 is one octave down
-{ ptc_itimeslice, 20.0, 300.0 }, // in milliseconds - size of sound chunk to analyze and cut/duplicate - 100ms nominal
-{ ptc_ixfade, 1.0, 200.0 }, // in milliseconds - size of crossfade region between spliced chunks - 20ms nominal
-};
-
-ptc_t *PTC_Params( prc_t *pprc )
-{
- ptc_t *pptc;
-
- float pitch = pprc->prm[ptc_ipitch];
- float timeslice = pprc->prm[ptc_itimeslice];
- float txfade = pprc->prm[ptc_ixfade];
-
- pptc = PTC_Alloc( timeslice, txfade, pitch );
-
- return pptc;
-}
-
-_inline void *PTC_VParams( void *p )
-{
- PRC_CheckParams((prc_t *)p, ptc_rng );
- return (void *)PTC_Params((prc_t *)p);
-}
-
-// change to new pitch value
-// v is +/- 0-1.0
-// v changes current pitch up/down by +/- v%
-void PTC_Mod( ptc_t *pptc, float v )
-{
- float fstep;
- float fstepnew;
-
- fstep = pptc->fstep;
- fstepnew = fstep * (1.0 + v);
-
- PTC_ChangeVal( pptc, fstepnew, 0.01 );
-}
-
-
-////////////////////
-// ADSR envelope
-////////////////////
-
-#define CENVS 64 // max # of envelopes active
-#define CENVRMPS 4 // A, D, S, R
-
-#define ENV_LIN 0 // linear a,d,s,r
-#define ENV_EXP 1 // exponential a,d,s,r
-#define ENV_MAX ENV_EXP
-
-#define ENV_BITS 14 // bits of resolution of ramp
-
-typedef struct
-{
- qboolean fused;
- qboolean fhitend; // true if done
- int ienv; // current ramp
- rmp_t rmps[CENVRMPS]; // ramps
-} env_t;
-
-env_t envs[CENVS];
-
-void ENV_Init( env_t *penv ) { if( penv ) memset( penv, 0, sizeof( env_t )); };
-void ENV_Free( env_t *penv ) { if( penv ) memset( penv, 0, sizeof( env_t )); };
-void ENV_InitAll() { int i; for( i = 0; i < CENVS; i++ ) ENV_Init( &envs[i] ); };
-void ENV_FreeAll() { int i; for( i = 0; i < CENVS; i++ ) ENV_Free( &envs[i] ); };
-
-
-// allocate ADSR envelope
-// all times are in seconds
-// amp1 - attack amplitude multiplier 0-1.0
-// amp2 - sustain amplitude multiplier 0-1.0
-// amp3 - end of sustain amplitude multiplier 0-1.0
-env_t *ENV_Alloc( int type, float famp1, float famp2, float famp3, float attack, float decay, float sustain, float release )
-{
- int i;
- env_t *penv;
-
- for( i = 0; i < CENVS; i++ )
- {
- if( !envs[i].fused )
- {
- int amp1 = famp1 * (1 << ENV_BITS); // ramp resolution
- int amp2 = famp2 * (1 << ENV_BITS);
- int amp3 = famp3 * (1 << ENV_BITS);
-
- penv = &envs[i];
-
- ENV_Init( penv );
-
- // UNDONE: ignoring type = ENV_EXP - use oneshot LFOS instead with sawtooth/exponential
-
- // set up ramps
- RMP_Init( &penv->rmps[0], attack, 0, amp1 );
- RMP_Init( &penv->rmps[1], decay, amp1, amp2 );
- RMP_Init( &penv->rmps[2], sustain, amp2, amp3 );
- RMP_Init( &penv->rmps[3], release, amp3, 0 );
-
- penv->ienv = 0;
- penv->fused = true;
- penv->fhitend = false;
-
- return penv;
- }
- }
-
- MsgDev( D_WARN, "DSP: failed to allocate envelope.\n" );
- return NULL;
-}
-
-_inline int ENV_GetNext( env_t *penv, int x )
-{
- if( !penv->fhitend )
- {
- int i, y;
-
- i = penv->ienv;
- y = RMP_GetNext( &penv->rmps[i] );
-
- // check for next ramp
- if( penv->rmps[i].fhitend )
- i++;
-
- penv->ienv = i;
-
- // check for end of all ramps
- if( i > 3 ) penv->fhitend = true;
-
- // multiply input signal by ramp
- return (x * y) >> ENV_BITS;
- }
- return 0;
-}
-
-// batch version for performance
-
-_inline void ENV_GetNextN( env_t *penv, portable_samplepair_t *pbuffer, int SampleCount, int op )
-{
- int count = SampleCount;
- portable_samplepair_t *pb = pbuffer;
-
- switch( op )
- {
- default:
- case OP_LEFT:
- while( count-- )
- {
- pb->left = ENV_GetNext( penv, pb->left );
- pb++;
- }
- break;
- case OP_RIGHT:
- while( count-- )
- {
- pb->right = ENV_GetNext( penv, pb->right );
- pb++;
- }
- break;
- case OP_LEFT_DUPLICATE:
- while( count-- )
- {
- pb->left = pb->right = ENV_GetNext( penv, pb->left );
- pb++;
- }
- break;
- }
-}
-
-// uses lfowav, amp1, amp2, amp3, attack, decay, sustain, release
-// lfowav is type, currently ignored - ie: LFO_LIN_IN, LFO_LOG_IN
-
-// parameter order
-typedef enum
-{
- env_itype,
- env_iamp1,
- env_iamp2,
- env_iamp3,
- env_iattack,
- env_idecay,
- env_isustain,
- env_irelease,
- env_cparam // # of params
-
-} env_e;
-
-// parameter ranges
-prm_rng_t env_rng[] =
-{
-{ env_cparam, 0, 0 }, // first entry is # of parameters
-{ env_itype, 0.0, ENV_MAX }, // ENV_LINEAR, ENV_LOG - currently ignored
-{ env_iamp1, 0.0, 1.0 }, // attack peak amplitude 0-1.0
-{ env_iamp2, 0.0, 1.0 }, // decay target amplitued 0-1.0
-{ env_iamp3, 0.0, 1.0 }, // sustain target amplitude 0-1.0
-{ env_iattack, 0.0, 20000.0 }, // attack time in milliseconds
-{ env_idecay, 0.0, 20000.0 }, // envelope decay time in milliseconds
-{ env_isustain, 0.0, 20000.0 }, // sustain time in milliseconds
-{ env_irelease, 0.0, 20000.0 }, // release time in milliseconds
-};
-
-env_t *ENV_Params( prc_t *pprc )
-{
- env_t *penv;
-
- float type = pprc->prm[env_itype];
- float amp1 = pprc->prm[env_iamp1];
- float amp2 = pprc->prm[env_iamp2];
- float amp3 = pprc->prm[env_iamp3];
- float attack = pprc->prm[env_iattack] / 1000.0f;
- float decay = pprc->prm[env_idecay] / 1000.0f;
- float sustain = pprc->prm[env_isustain] / 1000.0f;
- float release = pprc->prm[env_irelease] / 1000.0f;
-
- penv = ENV_Alloc( type, amp1, amp2, amp3, attack, decay, sustain, release );
- return penv;
-}
-
-_inline void *ENV_VParams( void *p )
-{
- PRC_CheckParams((prc_t *)p, env_rng );
- return (void *)ENV_Params((prc_t *)p);
-}
-
-_inline void ENV_Mod ( void *p, float v )
-{
-}
-
-////////////////////
-// envelope follower
-////////////////////
-#define CEFOS 64 // max # of envelope followers active
-
-#define CEFOBITS 6 // size 2^6 = 64
-#define CEFOWINDOW (1 << (CEFOBITS)) // size of sample window
-
-typedef struct
-{
- qboolean fused;
- int avg; // accumulating average over sample window
- int cavg; // count down
- int xout; // current output value
-
-} efo_t;
-
-efo_t efos[CEFOS];
-
-void EFO_Init( efo_t *pefo ) { if( pefo ) memset( pefo, 0, sizeof( efo_t )); };
-void EFO_Free( efo_t *pefo ) { if( pefo ) memset( pefo, 0, sizeof( efo_t )); };
-void EFO_InitAll() { int i; for( i = 0; i < CEFOS; i++ ) EFO_Init( &efos[i] ); };
-void EFO_FreeAll() { int i; for( i = 0; i < CEFOS; i++ ) EFO_Free( &efos[i] ); };
-
-// allocate enveloper follower
-efo_t *EFO_Alloc( void )
-{
- int i;
- efo_t *pefo;
-
- for( i = 0; i < CEFOS; i++ )
- {
- if( !efos[i].fused )
- {
- pefo = &efos[i];
-
- EFO_Init( pefo );
-
- pefo->xout = 0;
- pefo->cavg = CEFOWINDOW;
- pefo->fused = true;
-
- return pefo;
- }
- }
-
- MsgDev( D_WARN, "DSP: failed to allocate envelope follower.\n" );
- return NULL;
-}
-
-
-_inline int EFO_GetNext( efo_t *pefo, int x )
-{
- int xa = ABS( x ); // rectify input wav
-
- // get running sum / 2
- pefo->avg += xa >> 1; // divide by 2 to prevent overflow
-
- pefo->cavg--;
-
- if( !pefo->cavg )
- {
- // new output value - end of window
-
- // get average over window
- pefo->xout = pefo->avg >> (CEFOBITS - 1); // divide by window size / 2
- pefo->cavg = CEFOWINDOW;
- pefo->avg = 0;
- }
-
- return pefo->xout;
-}
-
-// batch version for performance
-_inline void EFO_GetNextN( efo_t *pefo, portable_samplepair_t *pbuffer, int SampleCount, int op )
-{
- int count = SampleCount;
- portable_samplepair_t *pb = pbuffer;
-
- switch( op )
- {
- default:
- case OP_LEFT:
- while( count-- )
- {
- pb->left = EFO_GetNext( pefo, pb->left );
- pb++;
- }
- break;
- case OP_RIGHT:
- while( count-- )
- {
- pb->right = EFO_GetNext( pefo, pb->right );
- pb++;
- }
- break;
- case OP_LEFT_DUPLICATE:
- while( count-- )
- {
- pb->left = pb->right = EFO_GetNext( pefo, pb->left );
- pb++;
- }
- break;
- }
-}
-
-
-efo_t * EFO_Params( prc_t *pprc )
-{
- return EFO_Alloc();
-}
-
-_inline void *EFO_VParams( void *p )
-{
- // PRC_CheckParams(( prc_t *)p, efo_rng ); - efo has no params
- return (void *)EFO_Params((prc_t *)p );
-}
-
-_inline void EFO_Mod( void *p, float v )
-{
-}
-
-//////////////
-// mod delay
-//////////////
-
-// modulate delay time anywhere from 0..D using MDY_ChangeVal. no output glitches (uses RMP)
-
-#define CMDYS 64 // max # of mod delays active (steals from delays)
-
-typedef struct
-{
- qboolean fused;
- qboolean fchanging; // true if modulating to new delay value
- dly_t *pdly; // delay
- int Dcur; // current delay value
- float ramptime; // ramp 'glide' time - time in seconds to change between values
- int mtime; // time in samples between delay changes. 0 implies no self-modulating
- int mtimecur; // current time in samples until next delay change
- float depth; // modulate delay from D to D - (D*depth) depth 0-1.0
- int xprev; // previous delay output, used to smooth transitions between delays
- rmp_t rmp; // ramp
-} mdy_t;
-
-mdy_t mdys[CMDYS];
-
-void MDY_Init( mdy_t *pmdy ) { if( pmdy ) memset( pmdy, 0, sizeof( mdy_t )); };
-void MDY_Free( mdy_t *pmdy ) { if( pmdy ) { DLY_Free( pmdy->pdly ); memset( pmdy, 0, sizeof( mdy_t )); } };
-void MDY_InitAll() { int i; for( i = 0; i < CMDYS; i++ ) MDY_Init( &mdys[i] ); };
-void MDY_FreeAll() { int i; for( i = 0; i < CMDYS; i++ ) MDY_Free( &mdys[i] ); };
-
-
-// allocate mod delay, given previously allocated dly
-// ramptime is time in seconds for delay to change from dcur to dnew
-// modtime is time in seconds between modulations. 0 if no self-modulation
-// depth is 0-1.0 multiplier, new delay values when modulating are Dnew = randomlong (D - D*depth, D)
-mdy_t *MDY_Alloc( dly_t *pdly, float ramptime, float modtime, float depth )
-{
- int i;
- mdy_t *pmdy;
-
- if( !pdly )
- return NULL;
-
- for( i = 0; i < CMDYS; i++ )
- {
- if( !mdys[i].fused )
- {
- pmdy = &mdys[i];
-
- MDY_Init( pmdy );
-
- pmdy->pdly = pdly;
-
- if( !pmdy->pdly )
- {
- MsgDev( D_WARN, "DSP: failed to allocate delay for mod delay.\n" );
- return NULL;
- }
-
- pmdy->Dcur = pdly->D0;
- pmdy->fused = true;
- pmdy->ramptime = ramptime;
- pmdy->mtime = SEC_TO_SAMPS( modtime );
- pmdy->mtimecur = pmdy->mtime;
- pmdy->depth = depth;
-
- return pmdy;
- }
- }
-
- MsgDev( D_WARN, "DSP: failed to allocate mod delay.\n" );
- return NULL;
-}
-
-// change to new delay tap value t samples, ramp linearly over ramptime seconds
-void MDY_ChangeVal( mdy_t *pmdy, int t )
-{
- // if D > original delay value, cap at original value
-
- t = min( pmdy->pdly->D0, t );
- pmdy->fchanging = true;
-
- RMP_Init( &pmdy->rmp, pmdy->ramptime, pmdy->Dcur, t );
-}
-
-// get next value from modulating delay
-int MDY_GetNext( mdy_t *pmdy, int x )
-{
- int xout;
- int xcur;
-
- // get current delay output
- xcur = DLY_GetNext( pmdy->pdly, x );
-
- // return right away if not modulating (not changing and not self modulating)
- if( !pmdy->fchanging && !pmdy->mtime )
- {
- pmdy->xprev = xcur;
- return xcur;
- }
-
- xout = xcur;
-
- // if currently changing to new delay target, get next delay value
- if( pmdy->fchanging )
- {
- // get next ramp value, test for done
- int r = RMP_GetNext( &pmdy->rmp );
-
- if( RMP_HitEnd( &pmdy->rmp ))
- pmdy->fchanging = false;
-
- // if new delay different from current delay, change delay
- if( r != pmdy->Dcur )
- {
- // ramp never changes by more than + or - 1
-
- // change delay tap value to r
- DLY_ChangeVal( pmdy->pdly, r );
-
- pmdy->Dcur = r;
-
- // filter delay output within transitions.
- // note: xprev = xcur = 0 if changing delay on 1st sample
- xout = ( xcur + pmdy->xprev ) >> 1;
- }
- }
-
- // if self-modulating and timer has expired, get next change
- if( pmdy->mtime && !pmdy->mtimecur-- )
- {
- int D0 = pmdy->pdly->D0;
- int Dnew;
- float D1;
-
- pmdy->mtimecur = pmdy->mtime;
-
- // modulate between 0 and 100% of d0
- D1 = (float)D0 * (1.0 - pmdy->depth);
- Dnew = Com_RandomLong( (int)D1, D0 );
-
- MDY_ChangeVal( pmdy, Dnew );
- }
-
- pmdy->xprev = xcur;
-
- return xout;
-}
-
-// batch version for performance
-_inline void MDY_GetNextN( mdy_t *pmdy, portable_samplepair_t *pbuffer, int SampleCount, int op )
-{
- int count = SampleCount;
- portable_samplepair_t *pb = pbuffer;
-
- switch( op )
- {
- default:
- case OP_LEFT:
- while( count-- )
- {
- pb->left = MDY_GetNext( pmdy, pb->left );
- pb++;
- }
- return;
- case OP_RIGHT:
- while( count-- )
- {
- pb->right = MDY_GetNext( pmdy, pb->right );
- pb++;
- }
- return;
- case OP_LEFT_DUPLICATE:
- while( count-- )
- {
- pb->left = pb->right = MDY_GetNext( pmdy, pb->left );
- pb++;
- }
- return;
- }
-}
-
-// parameter order
-typedef enum
-{
- mdy_idtype, // NOTE: first 8 params must match params in dly_e
- mdy_idelay,
- mdy_ifeedback,
- mdy_igain,
- mdy_iftype,
- mdy_icutoff,
- mdy_iqwidth,
- mdy_iquality,
- mdy_imodrate,
- mdy_imoddepth,
- mdy_imodglide,
- mdy_cparam
-} mdy_e;
-
-
-// parameter ranges
-prm_rng_t mdy_rng[] =
-{
-{ mdy_cparam, 0, 0 }, // first entry is # of parameters
-
-// delay params
-{ mdy_idtype, 0, DLY_MAX }, // delay type DLY_PLAIN, DLY_LOWPASS, DLY_ALLPASS
-{ mdy_idelay, 0.0, 1000.0 }, // delay in milliseconds
-{ mdy_ifeedback, 0.0, 0.99 }, // feedback 0-1.0
-{ mdy_igain, 0.0, 1.0 }, // final gain of output stage, 0-1.0
-
-// filter params if mdy type DLY_LOWPASS
-{ mdy_iftype, 0, FTR_MAX },
-{ mdy_icutoff, 10.0, 22050.0 },
-{ mdy_iqwidth, 100.0, 11025.0 },
-{ mdy_iquality, 0, QUA_MAX },
-{ mdy_imodrate, 0.01, 200.0 }, // frequency at which delay values change to new random value. 0 is no self-modulation
-{ mdy_imoddepth, 0.0, 1.0 }, // how much delay changes (decreases) from current value (0-1.0)
-{ mdy_imodglide, 0.01, 100.0 }, // glide time between dcur and dnew in milliseconds
-};
-
-// convert user parameters to internal parameters, allocate and return
-mdy_t *MDY_Params( prc_t *pprc )
-{
- mdy_t *pmdy;
- dly_t *pdly;
-
- float ramptime = pprc->prm[mdy_imodglide] / 1000.0; // get ramp time in seconds
- float modtime = 1.0 / pprc->prm[mdy_imodrate]; // time between modulations in seconds
- float depth = pprc->prm[mdy_imoddepth]; // depth of modulations 0-1.0
-
- // alloc plain, allpass or lowpass delay
- pdly = DLY_Params( pprc );
- if( !pdly ) return NULL;
-
- pmdy = MDY_Alloc( pdly, ramptime, modtime, depth );
-
- return pmdy;
-}
-
-_inline void * MDY_VParams( void *p )
-{
- PRC_CheckParams(( prc_t *)p, mdy_rng );
- return (void *)MDY_Params ((prc_t *)p );
-}
-
-// v is +/- 0-1.0
-// change current delay value 0..D
-void MDY_Mod( mdy_t *pmdy, float v )
-{
- int D = pmdy->Dcur;
- float v2 = -(v + 1.0)/2.0; // v2 varies -1.0-0.0
-
- // D varies 0..D
- D = D + (int)((float)D * v2);
-
- // change delay
- MDY_ChangeVal( pmdy, D );
-}
-
-
-///////////////////////////////////////////
-// Chorus - lfo modulated delay
-///////////////////////////////////////////
-
-
-#define CCRSS 64 // max number chorus' active
-
-typedef struct
-{
- qboolean fused;
- mdy_t *pmdy; // modulatable delay
- lfo_t *plfo; // modulating lfo
- int lfoprev; // previous modulator value from lfo
- int mix; // mix of clean & chorus signal - 0..PMAX
-} crs_t;
-
-crs_t crss[CCRSS];
-
-void CRS_Init( crs_t *pcrs ) { if( pcrs ) memset( pcrs, 0, sizeof( crs_t )); };
-void CRS_Free( crs_t *pcrs )
-{
- if( pcrs )
- {
- MDY_Free( pcrs->pmdy );
- LFO_Free( pcrs->plfo );
- memset( pcrs, 0, sizeof( crs_t ));
- }
-}
-
-void CRS_InitAll() { int i; for( i = 0; i < CCRSS; i++ ) CRS_Init( &crss[i] ); }
-void CRS_FreeAll() { int i; for( i = 0; i < CCRSS; i++ ) CRS_Free( &crss[i] ); }
-
-// fstep is base pitch shift, ie: floating point step value, where 1.0 = +1 octave, 0.5 = -1 octave
-// lfotype is LFO_SIN, LFO_RND, LFO_TRI etc (LFO_RND for chorus, LFO_SIN for flange)
-// fHz is modulation frequency in Hz
-// depth is modulation depth, 0-1.0
-// mix is mix of chorus and clean signal
-
-#define CRS_DELAYMAX 100 // max milliseconds of sweepable delay
-#define CRS_RAMPTIME 5 // milliseconds to ramp between new delay values
-
-crs_t * CRS_Alloc( int lfotype, float fHz, float fdepth, float mix )
-{
- int i, D;
- crs_t *pcrs;
- dly_t *pdly;
- mdy_t *pmdy;
- lfo_t *plfo;
- float ramptime;
-
- // find free chorus slot
- for( i = 0; i < CCRSS; i++ )
- {
- if( !crss[i].fused )
- break;
- }
-
- if( i == CCRSS )
- {
- MsgDev( D_WARN, "DSP: failed to allocate chorus.\n" );
- return NULL;
- }
-
- pcrs = &crss[i];
- CRS_Init( pcrs );
-
- D = fdepth * MSEC_TO_SAMPS( CRS_DELAYMAX ); // sweep from 0 - n milliseconds
-
- ramptime = (float)CRS_RAMPTIME / 1000.0f; // # milliseconds to ramp between new values
-
- pdly = DLY_Alloc( D, 0, 1, DLY_LINEAR );
- pmdy = MDY_Alloc( pdly, ramptime, 0.0, 0.0 );
- plfo = LFO_Alloc( lfotype, fHz, false );
-
- if( !plfo || !pmdy )
- {
- LFO_Free( plfo );
- MDY_Free( pmdy );
- MsgDev( D_WARN, "DSP: failed to allocate lfo or mdy for chorus.\n" );
- return NULL;
- }
-
- pcrs->pmdy = pmdy;
- pcrs->plfo = plfo;
- pcrs->mix = (int)( PMAX * mix );
- pcrs->fused = true;
-
- return pcrs;
-}
-
-// return next chorused sample (modulated delay) mixed with input sample
-_inline int CRS_GetNext( crs_t *pcrs, int x )
-{
- int l, y;
-
- // get current mod delay value
- y = MDY_GetNext( pcrs->pmdy, x );
-
- // get next lfo value for modulation
- // note: lfo must return 0 as first value
- l = LFO_GetNext( pcrs->plfo, x );
-
- // if modulator has changed, change mdy
- if( l != pcrs->lfoprev )
- {
- // calculate new tap (starts at D)
- int D = pcrs->pmdy->pdly->D0;
- int tap;
-
- // lfo should always output values 0 <= l <= LFOMAX
-
- if( l < 0 ) l = 0;
-
- tap = D - ((l * D) >> LFOBITS);
- MDY_ChangeVal ( pcrs->pmdy, tap );
- pcrs->lfoprev = l;
- }
-
- return ((y * pcrs->mix) >> PBITS) + x;
-}
-
-// batch version for performance
-_inline void CRS_GetNextN( crs_t *pcrs, portable_samplepair_t *pbuffer, int SampleCount, int op )
-{
- int count = SampleCount;
- portable_samplepair_t *pb = pbuffer;
-
- switch( op )
- {
- default:
- case OP_LEFT:
- while( count-- )
- {
- pb->left = CRS_GetNext( pcrs, pb->left );
- pb++;
- }
- break;
- case OP_RIGHT:
- while( count-- )
- {
- pb->right = CRS_GetNext( pcrs, pb->right );
- pb++;
- }
- break;
- case OP_LEFT_DUPLICATE:
- while( count-- )
- {
- pb->left = pb->right = CRS_GetNext( pcrs, pb->left );
- pb++;
- }
- break;
- }
-}
-
-// parameter order
-typedef enum
-{
- crs_ilfotype,
- crs_irate,
- crs_idepth,
- crs_imix,
- crs_cparam
-} crs_e;
-
-
-// parameter ranges
-prm_rng_t crs_rng[] =
-{
-{ crs_cparam, 0, 0 }, // first entry is # of parameters
-{ crs_ilfotype, 0, LFO_MAX }, // lfotype is LFO_SIN, LFO_RND, LFO_TRI etc (LFO_RND for chorus, LFO_SIN for flange)
-{ crs_irate, 0.0, 1000.0 }, // rate is modulation frequency in Hz
-{ crs_idepth, 0.0, 1.0 }, // depth is modulation depth, 0-1.0
-{ crs_imix, 0.0, 1.0 }, // mix is mix of chorus and clean signal
-};
-
-// uses pitch, lfowav, rate, depth
-crs_t *CRS_Params( prc_t *pprc )
-{
- crs_t *pcrs;
-
- pcrs = CRS_Alloc( pprc->prm[crs_ilfotype], pprc->prm[crs_irate], pprc->prm[crs_idepth], pprc->prm[crs_imix] );
-
- return pcrs;
-}
-
-_inline void *CRS_VParams( void *p )
-{
- PRC_CheckParams((prc_t *)p, crs_rng );
- return (void *)CRS_Params((prc_t *)p );
-}
-
-_inline void CRS_Mod( void *p, float v )
-{
-}
-
-////////////////////////////////////////////////////
-// amplifier - modulatable gain, distortion
-////////////////////////////////////////////////////
-
-#define CAMPS 64 // max number amps active
-#define AMPSLEW 10 // milliseconds of slew time between gain changes
-
-typedef struct
-{
- qboolean fused;
- float gain; // amplification 0-6.0
- float vthresh; // clip distortion threshold 0-1.0
- float distmix; // 0-1.0 mix of distortion with clean
- float vfeed; // 0-1.0 feedback with distortion;
- float gaintarget; // new gain
- float gaindif; // incrementer
-} amp_t;
-
-amp_t amps[CAMPS];
-
-void AMP_Init( amp_t *pamp ) { if( pamp ) memset( pamp, 0, sizeof( amp_t )); }
-void AMP_Free( amp_t *pamp ) { if( pamp ) memset( pamp, 0, sizeof( amp_t )); }
-void AMP_InitAll() { int i; for( i = 0; i < CAMPS; i++ ) AMP_Init( &amps[i] ); }
-void AMP_FreeAll() { int i; for( i = 0; i < CAMPS; i++ ) AMP_Free( &amps[i] ); }
-
-amp_t *AMP_Alloc( float gain, float vthresh, float distmix, float vfeed )
-{
- int i;
- amp_t *pamp;
-
- // find free amp slot
- for( i = 0; i < CAMPS; i++ )
- {
- if ( !amps[i].fused )
- break;
- }
-
- if( i == CAMPS )
- {
- MsgDev( D_WARN, "DSP: failed to allocate amp.\n" );
- return NULL;
- }
-
- pamp = &amps[i];
-
- AMP_Init ( pamp );
-
- pamp->gain = gain;
- pamp->vthresh = vthresh;
- pamp->distmix = distmix;
- pamp->vfeed = vfeed;
-
- return pamp;
-}
-
-// return next amplified sample
-_inline int AMP_GetNext( amp_t *pamp, int x )
-{
- float y = (float)x;
- float yin;
- float gain = pamp->gain;
-
- yin = y;
-
- // slew between gains
- if( gain != pamp->gaintarget )
- {
- float gaintarget = pamp->gaintarget;
- float gaindif = pamp->gaindif;
-
- if( gain > gaintarget )
- {
- gain -= gaindif;
- if( gain <= gaintarget )
- pamp->gaintarget = gain;
- }
- else
- {
- gain += gaindif;
- if( gain >= gaintarget )
- pamp->gaintarget = gain;
- }
-
- pamp->gain = gain;
- }
-
- // if distortion is on, add distortion, feedback
- if( pamp->vthresh < 1.0 )
- {
- float fclip = pamp->vthresh * 32767.0;
-
- if( pamp->vfeed > 0.0 )
- {
- // UNDONE: feedback
- }
-
- // clip distort
- y = ( y > fclip ? fclip : ( y < -fclip ? -fclip : y));
-
- // mix distorted with clean (1.0 = full distortion)
- if( pamp->distmix > 0.0 )
- y = y * pamp->distmix + yin * (1.0 - pamp->distmix);
- }
-
- // amplify
- y *= gain;
-
- return (int)y;
-}
-
-// batch version for performance
-_inline void AMP_GetNextN( amp_t *pamp, portable_samplepair_t *pbuffer, int SampleCount, int op )
-{
- int count = SampleCount;
- portable_samplepair_t *pb = pbuffer;
-
- switch( op )
- {
- default:
- case OP_LEFT:
- while( count-- )
- {
- pb->left = AMP_GetNext( pamp, pb->left );
- pb++;
- }
- break;
- case OP_RIGHT:
- while( count-- )
- {
- pb->right = AMP_GetNext( pamp, pb->right );
- pb++;
- }
- break;
- case OP_LEFT_DUPLICATE:
- while( count-- )
- {
- pb->left = pb->right = AMP_GetNext( pamp, pb->left );
- pb++;
- }
- break;
- }
-}
-
-_inline void AMP_Mod( amp_t *pamp, float v )
-{
- float vmod = bound( v, 0.0, 1.0 );
- float samps = MSEC_TO_SAMPS( AMPSLEW ); // # samples to slew between amp values
-
- // ramp to new amplification value
- pamp->gaintarget = pamp->gain * vmod;
-
- pamp->gaindif = fabs( pamp->gain - pamp->gaintarget ) / samps;
-
- if( pamp->gaindif == 0.0f )
- pamp->gaindif = fabs( pamp->gain - pamp->gaintarget ) / 100;
-}
-
-
-// parameter order
-typedef enum
-{
- amp_gain,
- amp_vthresh,
- amp_distmix,
- amp_vfeed,
- amp_cparam
-} amp_e;
-
-
-// parameter ranges
-prm_rng_t amp_rng[] =
-{
-{ amp_cparam, 0, 0 }, // first entry is # of parameters
-{ amp_gain, 0.0, 10.0 }, // amplification
-{ amp_vthresh, 0.0, 1.0 }, // threshold for distortion (1.0 = no distortion)
-{ amp_distmix, 0.0, 1.0 }, // mix of clean and distortion (1.0 = full distortion, 0.0 = full clean)
-{ amp_vfeed, 0.0, 1.0 }, // distortion feedback
-};
-
-amp_t * AMP_Params( prc_t *pprc )
-{
- amp_t *pamp;
-
- pamp = AMP_Alloc( pprc->prm[amp_gain], pprc->prm[amp_vthresh], pprc->prm[amp_distmix], pprc->prm[amp_vfeed] );
-
- return pamp;
-}
-
-_inline void *AMP_VParams( void *p )
-{
- PRC_CheckParams((prc_t *)p, amp_rng );
- return (void *)AMP_Params((prc_t *)p );
-}
-
-
-/////////////////
-// NULL processor
-/////////////////
-typedef struct
-{
- int type;
-} nul_t;
-
-nul_t nuls[] = { 0 };
-
-void NULL_Init( nul_t *pnul ) { }
-void NULL_InitAll( ) { }
-void NULL_Free( nul_t *pnul ) { }
-void NULL_FreeAll( ) { }
-nul_t *NULL_Alloc( ) { return &nuls[0]; }
-
-_inline int NULL_GetNext( void *p, int x ) { return x; }
-_inline void NULL_GetNextN( nul_t *pnul, portable_samplepair_t *pbuffer, int SampleCount, int op ) { return; }
-_inline void NULL_Mod( void *p, float v ) { return; }
-_inline void * NULL_VParams( void *p ) { return (void *)(&nuls[0]); }
-
-//////////////////////////
-// DSP processors presets
-//////////////////////////
-
-// A dsp processor (prc) performs a single-sample function, such as pitch shift, delay, reverb, filter
-
-// note, this array must have CPRCPARMS entries
-#define PRMZERO 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
-#define PFNZERO NULL,NULL,NULL,NULL,NULL // zero pointers for pfnparam...pdata within prc_t
-
-//////////////////
-// NULL processor
-/////////////////
-
-#define PRC_NULL1 { PRC_NULL, PRMZERO, PFNZERO }
-
-#define PRC0 PRC_NULL1
-
-//////////////
-// Amplifiers
-//////////////
-
-// {amp_gain, 0.0, 10.0 }, // amplification
-// {amp_vthresh, 0.0, 1.0 }, // threshold for distortion (1.0 = no distortion)
-// {amp_distmix, 0.0, 1.0 }, // mix of clean and distortion (1.0 = full distortion, 0.0 = full clean)
-// {amp_vfeed, 0.0, 1.0 }, // distortion feedback
-
-// prctype gain vthresh distmix vfeed
-#define PRC_AMP1 {PRC_AMP, { 1.0, 1.0, 0.0, 0.0, }, PFNZERO } // modulatable unity gain amp
-#define PRC_AMP2 {PRC_AMP, { 1.5, 0.75, 1.0, 0.0, }, PFNZERO } // amp with light distortion
-#define PRC_AMP3 {PRC_AMP, { 2.0, 0.5, 1.0, 0.0, }, PFNZERO } // amp with medium distortion
-#define PRC_AMP4 {PRC_AMP, { 4.0, 0.25, 1.0, 0.0, }, PFNZERO } // amp with heavy distortion
-#define PRC_AMP5 {PRC_AMP, { 10.0, 0.10, 1.0, 0.0, }, PFNZERO } // mega distortion
-
-#define PRC_AMP6 {PRC_AMP, { 0.1, 1.0, 0.0, 0.0, }, PFNZERO } // fade out
-#define PRC_AMP7 {PRC_AMP, { 0.2, 1.0, 0.0, 0.0, }, PFNZERO } // fade out
-#define PRC_AMP8 {PRC_AMP, { 0.3, 1.0, 0.0, 0.0, }, PFNZERO } // fade out
-
-#define PRC_AMP9 {PRC_AMP, { 0.75, 1.0, 0.0, 0.0, }, PFNZERO } // duck out
-
-
-///////////
-// Filters
-///////////
-
-// ftype: filter type FLT_LP, FLT_HP, FLT_BP (UNDONE: FLT_BP currently ignored)
-// cutoff: cutoff frequency in hz at -3db gain
-// qwidth: width of BP, or steepness of LP/HP (ie: fcutoff + qwidth = -60db gain point)
-// quality: QUA_LO, _MED, _HI 0,1,2
-
-// prctype ftype cutoff qwidth quality
-#define PRC_FLT1 {PRC_FLT, { FLT_LP, 3000, 1000, QUA_MED, }, PFNZERO }
-#define PRC_FLT2 {PRC_FLT, { FLT_LP, 2000, 2000, QUA_MED, }, PFNZERO } // lowpass for facing away
-#define PRC_FLT3 {PRC_FLT, { FLT_LP, 1000, 1000, QUA_MED, }, PFNZERO }
-#define PRC_FLT4 {PRC_FLT, { FLT_LP, 700, 700, QUA_LO, }, PFNZERO } // muffle filter
-
-#define PRC_FLT5 {PRC_FLT, { FLT_HP, 700, 200, QUA_MED, }, PFNZERO } // highpass (bandpass pair)
-#define PRC_FLT6 {PRC_FLT, { FLT_HP, 2000, 1000, QUA_MED, }, PFNZERO } // lowpass (bandpass pair)
-
-//////////
-// Delays
-//////////
-
-// dtype: delay type DLY_PLAIN, DLY_LOWPASS, DLY_ALLPASS
-// delay: delay in milliseconds
-// feedback: feedback 0-1.0
-// gain: final gain of output stage, 0-1.0
-
-// prctype dtype delay feedbk gain ftype cutoff qwidth quality
-#define PRC_DLY1 {PRC_DLY, { DLY_PLAIN, 500.0, 0.5, 0.6, 0.0, 0.0, 0.0, 0.0, }, PFNZERO }
-#define PRC_DLY2 {PRC_DLY, { DLY_LOWPASS, 45.0, 0.8, 0.6, FLT_LP, 3000, 3000, QUA_LO, }, PFNZERO }
-#define PRC_DLY3 {PRC_DLY, { DLY_LOWPASS, 300.0, 0.5, 0.6, FLT_LP, 2000, 2000, QUA_LO, }, PFNZERO } // outside S
-#define PRC_DLY4 {PRC_DLY, { DLY_LOWPASS, 400.0, 0.5, 0.6, FLT_LP, 1500, 1500, QUA_LO, }, PFNZERO } // outside M
-#define PRC_DLY5 {PRC_DLY, { DLY_LOWPASS, 750.0, 0.5, 0.6, FLT_LP, 1000, 1000, QUA_LO, }, PFNZERO } // outside L
-#define PRC_DLY6 {PRC_DLY, { DLY_LOWPASS, 1000.0, 0.5, 0.6, FLT_LP, 800, 400, QUA_LO, }, PFNZERO } // outside VL
-#define PRC_DLY7 {PRC_DLY, { DLY_LOWPASS, 45.0, 0.4, 0.5, FLT_LP, 3000, 3000, QUA_LO, }, PFNZERO } // tunnel S
-#define PRC_DLY8 {PRC_DLY, { DLY_LOWPASS, 55.0, 0.4, 0.5, FLT_LP, 3000, 3000, QUA_LO, }, PFNZERO } // tunnel M
-#define PRC_DLY9 {PRC_DLY, { DLY_LOWPASS, 65.0, 0.4, 0.5, FLT_LP, 3000, 3000, QUA_LO, }, PFNZERO } // tunnel L
-#define PRC_DLY10 {PRC_DLY, { DLY_LOWPASS, 150.0, 0.5, 0.6, FLT_LP, 3000, 3000, QUA_LO, }, PFNZERO } // cavern S
-#define PRC_DLY11 {PRC_DLY, { DLY_LOWPASS, 200.0, 0.7, 0.6, FLT_LP, 3000, 3000, QUA_LO, }, PFNZERO } // cavern M
-#define PRC_DLY12 {PRC_DLY, { DLY_LOWPASS, 300.0, 0.7, 0.6, FLT_LP, 3000, 3000, QUA_LO, }, PFNZERO } // cavern L
-#define PRC_DLY13 {PRC_DLY, { DLY_LINEAR, 300.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0,}, PFNZERO } // straight delay 300ms
-#define PRC_DLY14 {PRC_DLY, { DLY_LINEAR, 80.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0,}, PFNZERO } // straight delay 80ms
-
-///////////
-// Reverbs
-///////////
-
-// size: 0-2.0 scales nominal delay parameters (starting at approx 20ms)
-// density: 0-2.0 density of reverbs (room shape) - controls # of parallel or series delays
-// decay: 0-2.0 scales feedback parameters (starting at approx 0.15)
-
-// prctype size density decay ftype cutoff qwidth fparallel
-#define PRC_RVA1 {PRC_RVA, {2.0, 0.5, 1.5, FLT_LP, 6000, 2000, 1}, PFNZERO }
-#define PRC_RVA2 {PRC_RVA, {1.0, 0.2, 1.5, 0, 0, 0, 0}, PFNZERO }
-
-#define PRC_RVA3 {PRC_RVA, {0.8, 0.5, 1.5, FLT_LP, 2500, 2000, 0}, PFNZERO } // metallic S
-#define PRC_RVA4 {PRC_RVA, {1.0, 0.5, 1.5, FLT_LP, 2500, 2000, 0}, PFNZERO } // metallic M
-#define PRC_RVA5 {PRC_RVA, {1.2, 0.5, 1.5, FLT_LP, 2500, 2000, 0}, PFNZERO } // metallic L
-
-#define PRC_RVA6 {PRC_RVA, {0.8, 0.3, 1.5, FLT_LP, 4000, 2000, 0}, PFNZERO } // tunnel S
-#define PRC_RVA7 {PRC_RVA, {0.9, 0.3, 1.5, FLT_LP, 4000, 2000, 0}, PFNZERO } // tunnel M
-#define PRC_RVA8 {PRC_RVA, {1.0, 0.3, 1.5, FLT_LP, 4000, 2000, 0}, PFNZERO } // tunnel L
-
-#define PRC_RVA9 {PRC_RVA, {2.0, 1.5, 2.0, FLT_LP, 1500, 1500, 1}, PFNZERO } // cavern S
-#define PRC_RVA10 {PRC_RVA, {2.0, 1.5, 2.0, FLT_LP, 1500, 1500, 1}, PFNZERO } // cavern M
-#define PRC_RVA11 {PRC_RVA, {2.0, 1.5, 2.0, FLT_LP, 1500, 1500, 1}, PFNZERO } // cavern L
-
-#define PRC_RVA12 {PRC_RVA, {2.0, 0.5, 1.5, FLT_LP, 6000, 2000, 1}, PFNZERO } // chamber S
-#define PRC_RVA13 {PRC_RVA, {2.0, 1.0, 1.5, FLT_LP, 6000, 2000, 1}, PFNZERO } // chamber M
-#define PRC_RVA14 {PRC_RVA, {2.0, 2.0, 1.5, FLT_LP, 6000, 2000, 1}, PFNZERO } // chamber L
-
-#define PRC_RVA15 {PRC_RVA, {1.7, 1.0, 1.2, FLT_LP, 5000, 4000, 1}, PFNZERO } // brite S
-#define PRC_RVA16 {PRC_RVA, {1.75, 1.0, 1.5, FLT_LP, 5000, 4000, 1}, PFNZERO } // brite M
-#define PRC_RVA17 {PRC_RVA, {1.85, 1.0, 2.0, FLT_LP, 6000, 4000, 1}, PFNZERO } // brite L
-
-#define PRC_RVA18 {PRC_RVA, {1.0, 1.5, 1.0, FLT_LP, 1000, 1000, 0}, PFNZERO } // generic
-
-#define PRC_RVA19 {PRC_RVA, {1.9, 1.8, 1.25, FLT_LP, 4000, 2000, 1}, PFNZERO } // concrete S
-#define PRC_RVA20 {PRC_RVA, {2.0, 1.8, 1.5, FLT_LP, 3500, 2000, 1}, PFNZERO } // concrete M
-#define PRC_RVA21 {PRC_RVA, {2.0, 1.8, 1.75, FLT_LP, 3000, 2000, 1}, PFNZERO } // concrete L
-
-#define PRC_RVA22 {PRC_RVA, {1.8, 1.5, 1.5, FLT_LP, 1000, 1000, 0}, PFNZERO } // water S
-#define PRC_RVA23 {PRC_RVA, {1.9, 1.75, 1.5, FLT_LP, 1000, 1000, 0}, PFNZERO } // water M
-#define PRC_RVA24 {PRC_RVA, {2.0, 2.0, 1.5, FLT_LP, 1000, 1000, 0}, PFNZERO } // water L
-
-
-/////////////
-// Diffusors
-/////////////
-
-// size: 0-1.0 scales all delays
-// density: 0-1.0 controls # of series delays
-// decay: 0-1.0 scales all feedback parameters
-
-// prctype size density decay
-#define PRC_DFR1 {PRC_DFR, { 1.0, 0.5, 1.0 }, PFNZERO }
-#define PRC_DFR2 {PRC_DFR, { 0.5, 0.3, 0.5 }, PFNZERO } // S
-#define PRC_DFR3 {PRC_DFR, { 0.75, 0.5, 0.75 }, PFNZERO } // M
-#define PRC_DFR4 {PRC_DFR, { 1.0, 0.5, 1.0 }, PFNZERO } // L
-#define PRC_DFR5 {PRC_DFR, { 1.0, 1.0, 1.0 }, PFNZERO } // VL
-
-////////
-// LFOs
-////////
-
-// wavtype: lfo type to use (LFO_SIN, LFO_RND...)
-// rate: modulation rate in hz. for MDY, 1/rate = 'glide' time in seconds
-// foneshot: 1.0 if lfo is oneshot
-
-// prctype wavtype rate foneshot
-#define PRC_LFO1 {PRC_LFO, { LFO_SIN, 440.0, 0.0, }, PFNZERO}
-#define PRC_LFO2 {PRC_LFO, { LFO_SIN, 3000.0, 0.0, }, PFNZERO} // ear noise ring
-#define PRC_LFO3 {PRC_LFO, { LFO_SIN, 4500.0, 0.0, }, PFNZERO} // ear noise ring
-#define PRC_LFO4 {PRC_LFO, { LFO_SIN, 6000.0, 0.0, }, PFNZERO} // ear noise ring
-#define PRC_LFO5 {PRC_LFO, { LFO_SAW, 100.0, 0.0, }, PFNZERO} // sub bass
-
-/////////
-// Pitch
-/////////
-
-// pitch: 0-n.0 where 1.0 = 1 octave up and 0.5 is one octave down
-// timeslice: in milliseconds - size of sound chunk to analyze and cut/duplicate - 100ms nominal
-// xfade: in milliseconds - size of crossfade region between spliced chunks - 20ms nominal
-
-// prctype pitch timeslice xfade
-#define PRC_PTC1 {PRC_PTC, { 1.1, 100.0, 20.0 }, PFNZERO} // pitch up 10%
-#define PRC_PTC2 {PRC_PTC, { 0.9, 100.0, 20.0 }, PFNZERO} // pitch down 10%
-#define PRC_PTC3 {PRC_PTC, { 0.95, 100.0, 20.0 }, PFNZERO} // pitch down 5%
-#define PRC_PTC4 {PRC_PTC, { 1.01, 100.0, 20.0 }, PFNZERO} // pitch up 1%
-#define PRC_PTC5 {PRC_PTC, { 0.5, 100.0, 20.0 }, PFNZERO} // pitch down 50%
-
-/////////////
-// Envelopes
-/////////////
-
-// etype: ENV_LINEAR, ENV_LOG - currently ignored
-// amp1: attack peak amplitude 0-1.0
-// amp2: decay target amplitued 0-1.0
-// amp3: sustain target amplitude 0-1.0
-// attack time in milliseconds
-// envelope decay time in milliseconds
-// sustain time in milliseconds
-// release time in milliseconds
-
-// prctype etype amp1 amp2 amp3 attack decay sustain release
-#define PRC_ENV1 {PRC_ENV, {ENV_LIN, 1.0, 0.5, 0.4, 500, 500, 3000, 6000 }, PFNZERO}
-
-
-//////////////
-// Mod delays
-//////////////
-
-// dtype: delay type DLY_PLAIN, DLY_LOWPASS, DLY_ALLPASS
-// delay: delay in milliseconds
-// feedback: feedback 0-1.0
-// gain: final gain of output stage, 0-1.0
-
-// modrate: frequency at which delay values change to new random value. 0 is no self-modulation
-// moddepth: how much delay changes (decreases) from current value (0-1.0)
-// modglide: glide time between dcur and dnew in milliseconds
-
-// prctype dtype delay feedback gain ftype cutoff qwidth qual modrate moddepth modglide
-#define PRC_MDY1 {PRC_MDY, {DLY_PLAIN, 500.0, 0.5, 1.0, 0, 0, 0, 0, 10, 0.8, 5,}, PFNZERO}
-#define PRC_MDY2 {PRC_MDY, {DLY_PLAIN, 50.0, 0.8, 1.0, 0, 0, 0, 0, 5, 0.8, 5,}, PFNZERO}
-
-#define PRC_MDY3 {PRC_MDY, {DLY_PLAIN, 300.0, 0.2, 1.0, 0, 0, 0, 0, 30, 0.01, 15,}, PFNZERO } // weird 1
-#define PRC_MDY4 {PRC_MDY, {DLY_PLAIN, 400.0, 0.3, 1.0, 0, 0, 0, 0, 0.25, 0.01, 15,}, PFNZERO } // weird 2
-#define PRC_MDY5 {PRC_MDY, {DLY_PLAIN, 500.0, 0.4, 1.0, 0, 0, 0, 0, 0.25, 0.01, 15,}, PFNZERO } // weird 3
-
-//////////
-// Chorus
-//////////
-
-// lfowav: lfotype is LFO_SIN, LFO_RND, LFO_TRI etc (LFO_RND for chorus, LFO_SIN for flange)
-// rate: rate is modulation frequency in Hz
-// depth: depth is modulation depth, 0-1.0
-// mix: mix is mix of chorus and clean signal
-
-// prctype lfowav rate depth mix
-#define PRC_CRS1 {PRC_CRS, { LFO_SIN, 10, 1.0, 0.5, }, PFNZERO }
-
-/////////////////////
-// Envelope follower
-/////////////////////
-
-// takes no parameters
-#define PRC_EFO1 {PRC_EFO, { PRMZERO }, PFNZERO }
-
-// init array of processors - first store pfnParam, pfnGetNext and pfnFree functions for type,
-// then call the pfnParam function to initialize each processor
-
-// prcs - an array of prc structures, all with initialized params
-// count - number of elements in the array
-// returns false if failed to init one or more processors
-
-qboolean PRC_InitAll( prc_t *prcs, int count )
-{
- int i;
- prc_Param_t pfnParam; // allocation function - takes ptr to prc, returns ptr to specialized data struct for proc type
- prc_GetNext_t pfnGetNext; // get next function
- prc_GetNextN_t pfnGetNextN; // get next function, batch version
- prc_Free_t pfnFree;
- prc_Mod_t pfnMod;
- qboolean fok = true;
-
- // set up pointers to XXX_Free, XXX_GetNext and XXX_Params functions
-
- for( i = 0; i < count; i++ )
- {
- switch (prcs[i].type)
- {
- case PRC_DLY:
- pfnFree = &(prc_Free_t)DLY_Free;
- pfnGetNext = &(prc_GetNext_t)DLY_GetNext;
- pfnGetNextN = &(prc_GetNextN_t)DLY_GetNextN;
- pfnParam = &DLY_VParams;
- pfnMod = &(prc_Mod_t)DLY_Mod;
- break;
- case PRC_RVA:
- pfnFree = &(prc_Free_t)RVA_Free;
- pfnGetNext = &(prc_GetNext_t)RVA_GetNext;
- pfnGetNextN = &(prc_GetNextN_t)RVA_GetNextN;
- pfnParam = &RVA_VParams;
- pfnMod = &(prc_Mod_t)RVA_Mod;
- break;
- case PRC_FLT:
- pfnFree = &(prc_Free_t)FLT_Free;
- pfnGetNext = &(prc_GetNext_t)FLT_GetNext;
- pfnGetNextN = &(prc_GetNextN_t)FLT_GetNextN;
- pfnParam = &FLT_VParams;
- pfnMod = &(prc_Mod_t)FLT_Mod;
- break;
- case PRC_CRS:
- pfnFree = &(prc_Free_t)CRS_Free;
- pfnGetNext = &(prc_GetNext_t)CRS_GetNext;
- pfnGetNextN = &(prc_GetNextN_t)CRS_GetNextN;
- pfnParam = &CRS_VParams;
- pfnMod = &(prc_Mod_t)CRS_Mod;
- break;
- case PRC_PTC:
- pfnFree = &(prc_Free_t)PTC_Free;
- pfnGetNext = &(prc_GetNext_t)PTC_GetNext;
- pfnGetNextN = &(prc_GetNextN_t)PTC_GetNextN;
- pfnParam = &PTC_VParams;
- pfnMod = &(prc_Mod_t)PTC_Mod;
- break;
- case PRC_ENV:
- pfnFree = &(prc_Free_t)ENV_Free;
- pfnGetNext = &(prc_GetNext_t)ENV_GetNext;
- pfnGetNextN = &(prc_GetNextN_t)ENV_GetNextN;
- pfnParam = &ENV_VParams;
- pfnMod = &(prc_Mod_t)ENV_Mod;
- break;
- case PRC_LFO:
- pfnFree = &(prc_Free_t)LFO_Free;
- pfnGetNext = &(prc_GetNext_t)LFO_GetNext;
- pfnGetNextN = &(prc_GetNextN_t)LFO_GetNextN;
- pfnParam = &LFO_VParams;
- pfnMod = &(prc_Mod_t)LFO_Mod;
- break;
- case PRC_EFO:
- pfnFree = &(prc_Free_t)EFO_Free;
- pfnGetNext = &(prc_GetNext_t)EFO_GetNext;
- pfnGetNextN = &(prc_GetNextN_t)EFO_GetNextN;
- pfnParam = &EFO_VParams;
- pfnMod = &(prc_Mod_t)EFO_Mod;
- break;
- case PRC_MDY:
- pfnFree = &(prc_Free_t)MDY_Free;
- pfnGetNext = &(prc_GetNext_t)MDY_GetNext;
- pfnGetNextN = &(prc_GetNextN_t)MDY_GetNextN;
- pfnParam = &MDY_VParams;
- pfnMod = &(prc_Mod_t)MDY_Mod;
- break;
- case PRC_DFR:
- pfnFree = &(prc_Free_t)DFR_Free;
- pfnGetNext = &(prc_GetNext_t)DFR_GetNext;
- pfnGetNextN = &(prc_GetNextN_t)DFR_GetNextN;
- pfnParam = &DFR_VParams;
- pfnMod = &(prc_Mod_t)DFR_Mod;
- break;
- case PRC_AMP:
- pfnFree = &(prc_Free_t)AMP_Free;
- pfnGetNext = &(prc_GetNext_t)AMP_GetNext;
- pfnGetNextN = &(prc_GetNextN_t)AMP_GetNextN;
- pfnParam = &AMP_VParams;
- pfnMod = &(prc_Mod_t)AMP_Mod;
- break;
- case PRC_NULL:
- default:
- pfnFree = &(prc_Free_t)NULL_Free;
- pfnGetNext = &(prc_GetNext_t)NULL_GetNext;
- pfnGetNextN = &(prc_GetNextN_t)NULL_GetNextN;
- pfnParam = &NULL_VParams;
- pfnMod = &(prc_Mod_t)NULL_Mod;
- break;
- }
-
- // set up function pointers
- prcs[i].pfnParam = pfnParam;
- prcs[i].pfnGetNext = pfnGetNext;
- prcs[i].pfnGetNextN = pfnGetNextN;
- prcs[i].pfnFree = pfnFree;
-
- // call param function, store pdata for the processor type
- prcs[i].pdata = pfnParam((void *)( &prcs[i] ));
-
- if( !prcs[i].pdata )
- fok = false;
- }
- return fok;
-}
-
-// free individual processor's data
-void PRC_Free( prc_t *pprc )
-{
- if( pprc->pfnFree && pprc->pdata )
- pprc->pfnFree( pprc->pdata );
-}
-
-// free all processors for supplied array
-// prcs - array of processors
-// count - elements in array
-void PRC_FreeAll( prc_t *prcs, int count )
-{
- int i;
-
- for( i = 0; i < count; i++ )
- PRC_Free( &prcs[i] );
-}
-
-// get next value for processor - (usually called directly by PSET_GetNext)
-_inline int PRC_GetNext( prc_t *pprc, int x )
-{
- return pprc->pfnGetNext( pprc->pdata, x );
-}
-
-// automatic parameter range limiting
-// force parameters between specified min/max in param_rng
-void PRC_CheckParams( prc_t *pprc, prm_rng_t *prng )
-{
- // first entry in param_rng is # of parameters
- int cprm = prng[0].iprm;
- int i;
-
- for( i = 0; i < cprm; i++)
- {
- // if parameter is 0.0f, always allow it (this is 'off' for most params)
- if( pprc->prm[i] != 0.0f && ( pprc->prm[i] > prng[i+1].hi || pprc->prm[i] < prng[i+1].lo ))
- {
- MsgDev( D_WARN, "DSP: clamping out of range parameter.\n" );
- pprc->prm[i] = bound( prng[i+1].lo, pprc->prm[i], prng[i+1].hi );
- }
- }
-}
-
-// DSP presets
-// A dsp preset comprises one or more dsp processors in linear, parallel or feedback configuration
-// preset configurations
-//
-#define PSET_SIMPLE 0
-
-// x(n)--->P(0)--->y(n)
-#define PSET_LINEAR 1
-
-// x(n)--->P(0)-->P(1)-->...P(m)--->y(n)
-#define PSET_PARALLEL6 4
-
-// x(n)-P(0)-->P(1)-->P(2)-->(+)-P(5)->y(n)
-// | ^
-// | |
-// -->P(3)-->P(4)---->
-
-
-#define PSET_PARALLEL2 5
-
-// x(n)--->P(0)-->(+)-->y(n)
-// ^
-// |
-// x(n)--->P(1)-----
-
-#define PSET_PARALLEL4 6
-
-// x(n)--->P(0)-->P(1)-->(+)-->y(n)
-// ^
-// |
-// x(n)--->P(2)-->P(3)-----
-
-#define PSET_PARALLEL5 7
-
-// x(n)--->P(0)-->P(1)-->(+)-->P(4)-->y(n)
-// ^
-// |
-// x(n)--->P(2)-->P(3)-----
-
-#define PSET_FEEDBACK 8
-
-// x(n)-P(0)--(+)-->P(1)-->P(2)-->P(5)->y(n)
-// ^ |
-// | v
-// -----P(4)<--P(3)--
-
-#define PSET_FEEDBACK3 9
-
-// x(n)---(+)-->P(0)--------->y(n)
-// ^ |
-// | v
-// -----P(2)<--P(1)--
-
-#define PSET_FEEDBACK4 10
-
-// x(n)---(+)-->P(0)-------->P(3)--->y(n)
-// ^ |
-// | v
-// ---P(2)<--P(1)--
-
-#define PSET_MOD 11
-
-//
-// x(n)------>P(1)--P(2)--P(3)--->y(n)
-// ^
-// x(n)------>P(0)....:
-
-#define PSET_MOD2 12
-
-//
-// x(n)-------P(1)-->y(n)
-// ^
-// x(n)-->P(0)..:
-
-
-#define PSET_MOD3 13
-
-//
-// x(n)-------P(1)-->P(2)-->y(n)
-// ^
-// x(n)-->P(0)..:
-
-
-#define CPSETS 64 // max number of presets simultaneously active
-
-#define CPSET_PRCS 6 // max # of processors per dsp preset
-#define CPSET_STATES (CPSET_PRCS+3) // # of internal states
-
-// NOTE: do not reorder members of pset_t - psettemplates relies on it!!!
-typedef struct
-{
- int type; // preset configuration type
- int cprcs; // number of processors for this preset
- prc_t prcs[CPSET_PRCS]; // processor preset data
- float gain; // preset gain 0.1->2.0
- int w[CPSET_STATES]; // internal states
- int fused;
-} pset_t;
-
-pset_t psets[CPSETS];
-
-// array of dsp presets, each with up to 6 processors per preset
-
-#define WZERO {0,0,0,0,0,0,0,0,0}, 0
-
-pset_t psettemplates[] =
-{
-// presets 0-29 map to legacy room_type 0-29
-
-// type # proc P0 P1 P2 P3 P4 P5 GAIN
-{PSET_SIMPLE, 1, { PRC_NULL1, PRC0, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // OFF 0
-{PSET_SIMPLE, 1, { PRC_RVA18, PRC0, PRC0, PRC0, PRC0, PRC0 },1.4, WZERO }, // GENERIC 1 // general, low reflective, diffuse room
-{PSET_LINEAR, 2, { PRC_DFR1, PRC_RVA3, PRC0, PRC0, PRC0, PRC0 },1.4, WZERO }, // METALIC_S 2 // highly reflective, parallel surfaces
-{PSET_LINEAR, 2, { PRC_DFR1, PRC_RVA4, PRC0, PRC0, PRC0, PRC0 },1.4, WZERO }, // METALIC_M 3
-{PSET_LINEAR, 2, { PRC_DFR1, PRC_RVA5, PRC0, PRC0, PRC0, PRC0 },1.4, WZERO }, // METALIC_L 4
-{PSET_LINEAR, 2, { PRC_DFR1, PRC_RVA6, PRC0, PRC0, PRC0, PRC0 },2.0, WZERO }, // TUNNEL_S 5 // resonant reflective, long surfaces
-{PSET_LINEAR, 2, { PRC_DFR1, PRC_RVA7, PRC0, PRC0, PRC0, PRC0 },1.8, WZERO }, // TUNNEL_M 6
-{PSET_LINEAR, 2, { PRC_DFR1, PRC_RVA8, PRC0, PRC0, PRC0, PRC0 },1.7, WZERO }, // TUNNEL_L 7
-{PSET_LINEAR, 2, { PRC_DFR1, PRC_RVA12,PRC0, PRC0, PRC0, PRC0 },1.7, WZERO }, // CHAMBER_S 8 // diffuse, moderately reflective surfaces
-{PSET_LINEAR, 2, { PRC_DFR1, PRC_RVA13,PRC0, PRC0, PRC0, PRC0 },1.7, WZERO }, // CHAMBER_M 9
-{PSET_LINEAR, 2, { PRC_DFR1, PRC_RVA14,PRC0, PRC0, PRC0, PRC0 },1.9, WZERO }, // CHAMBER_L 10
-{PSET_SIMPLE, 1, { PRC_RVA15, PRC0, PRC0, PRC0, PRC0, PRC0 },1.5, WZERO }, // BRITE_S 11 // diffuse, highly reflective
-{PSET_SIMPLE, 1, { PRC_RVA16, PRC0, PRC0, PRC0, PRC0, PRC0 },1.6, WZERO }, // BRITE_M 12
-{PSET_SIMPLE, 1, { PRC_RVA17, PRC0, PRC0, PRC0, PRC0, PRC0 },1.7, WZERO }, // BRITE_L 13
-{PSET_LINEAR, 2, { PRC_DFR1, PRC_RVA22,PRC0, PRC0, PRC0, PRC0 },1.8, WZERO }, // WATER1 14 // underwater fx
-{PSET_LINEAR, 2, { PRC_DFR1, PRC_RVA23,PRC0, PRC0, PRC0, PRC0 },1.8, WZERO }, // WATER2 15
-{PSET_LINEAR, 3, { PRC_DFR1, PRC_RVA24,PRC_MDY5, PRC0, PRC0, PRC0 },1.8, WZERO }, // WATER3 16
-{PSET_LINEAR, 2, { PRC_DFR1, PRC_RVA19,PRC0, PRC0, PRC0, PRC0 },1.7, WZERO }, // CONCRTE_S 17 // bare, reflective, parallel surfaces
-{PSET_LINEAR, 2, { PRC_DFR1, PRC_RVA20,PRC0, PRC0, PRC0, PRC0 },1.8, WZERO }, // CONCRTE_M 18
-{PSET_LINEAR, 2, { PRC_DFR1, PRC_RVA21,PRC0, PRC0, PRC0, PRC0 },1.9, WZERO }, // CONCRTE_L 19
-{PSET_LINEAR, 2, { PRC_DFR1, PRC_DLY3, PRC0, PRC0, PRC0, PRC0 },1.7, WZERO }, // OUTSIDE1 20 // echoing, moderately reflective
-{PSET_LINEAR, 2, { PRC_DFR1, PRC_DLY4, PRC0, PRC0, PRC0, PRC0 },1.7, WZERO }, // OUTSIDE2 21 // echoing, dull
-{PSET_LINEAR, 3, { PRC_DFR1, PRC_DFR1, PRC_DLY5, PRC0, PRC0, PRC0 },1.6, WZERO }, // OUTSIDE3 22 // echoing, very dull
-{PSET_LINEAR, 2, { PRC_DLY10, PRC_RVA10,PRC0, PRC0, PRC0, PRC0 },2.8, WZERO }, // CAVERN_S 23 // large, echoing area
-{PSET_LINEAR, 2, { PRC_DLY11, PRC_RVA10,PRC0, PRC0, PRC0, PRC0 },2.6, WZERO }, // CAVERN_M 24
-{PSET_LINEAR, 3, { PRC_DFR1, PRC_DLY12,PRC_RVA11,PRC0, PRC0, PRC0 },2.6, WZERO }, // CAVERN_L 25
-{PSET_LINEAR, 2, { PRC_DLY7, PRC_DFR1, PRC0, PRC0, PRC0, PRC0 },2.0, WZERO }, // WEIRDO1 26
-{PSET_LINEAR, 2, { PRC_DLY8, PRC_DFR1, PRC0, PRC0, PRC0, PRC0 },1.9, WZERO }, // WEIRDO2 27
-{PSET_LINEAR, 2, { PRC_DLY9, PRC_DFR1, PRC0, PRC0, PRC0, PRC0 },1.8, WZERO }, // WEIRDO3 28
-{PSET_LINEAR, 2, { PRC_DLY9, PRC_DFR1, PRC0, PRC0, PRC0, PRC0 },1.8, WZERO }, // WEIRDO4 29
-
-// presets 30-40 are new presets
-{PSET_SIMPLE, 1, { PRC_FLT2, PRC0, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 30 lowpass - facing away
-{PSET_LINEAR, 2, { PRC_FLT3, PRC_DLY14,PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 31 lowpass - facing away+80ms delay
-//{PSET_PARALLEL2,2, { PRC_AMP6, PRC_LFO2, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 32 explosion ring 1
-//{PSET_PARALLEL2,2, { PRC_AMP7, PRC_LFO3, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 33 explosion ring 2
-//{PSET_PARALLEL2,2, { PRC_AMP8, PRC_LFO4, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 34 explosion ring 3
-{PSET_LINEAR, 3, { PRC_DFR1, PRC_DFR1, PRC_FLT3, PRC0, PRC0, PRC0 },0.25, WZERO }, // 32 explosion ring
-{PSET_LINEAR, 3, { PRC_DFR1, PRC_DFR1, PRC_FLT3, PRC0, PRC0, PRC0 },0.25, WZERO }, // 33 explosion ring 2
-{PSET_LINEAR, 3, { PRC_DFR1, PRC_DFR1, PRC_FLT3, PRC0, PRC0, PRC0 },0.25, WZERO }, // 34 explosion ring 3
-{PSET_PARALLEL2,2, { PRC_DFR1, PRC_LFO2, PRC0, PRC0, PRC0, PRC0 },0.25, WZERO }, // 35 shock muffle 1
-{PSET_PARALLEL2,2, { PRC_DFR1, PRC_LFO2, PRC0, PRC0, PRC0, PRC0 },0.25, WZERO }, // 36 shock muffle 2
-{PSET_PARALLEL2,2, { PRC_DFR1, PRC_LFO2, PRC0, PRC0, PRC0, PRC0 },0.25, WZERO }, // 37 shock muffle 3
-//{PSET_LINEAR, 3, { PRC_DFR1, PRC_LFO4, PRC_FLT3, PRC0, PRC0, PRC0 },1.0, WZERO }, // 35 shock muffle 1
-//{PSET_LINEAR, 3, { PRC_DFR1, PRC_LFO4, PRC_FLT3, PRC0, PRC0, PRC0 },1.0, WZERO }, // 36 shock muffle 2
-//{PSET_LINEAR, 3, { PRC_DFR1, PRC_LFO4, PRC_FLT3, PRC0, PRC0, PRC0 },1.0, WZERO }, // 37 shock muffle 3
-{PSET_FEEDBACK3,3, { PRC_DLY13, PRC_PTC4, PRC_FLT2, PRC0, PRC0, PRC0 },0.25, WZERO }, // 38 fade pitchdown 1
-{PSET_LINEAR, 3, { PRC_AMP3, PRC_FLT5, PRC_FLT6, PRC0, PRC0, PRC0 },2.0, WZERO }, // 39 distorted speaker 1
-
-// fade out fade in
-
-// presets 40+ are test presets
-{PSET_SIMPLE, 1, { PRC_NULL1, PRC0, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 39 null
-{PSET_SIMPLE, 1, { PRC_DLY1, PRC0, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 40 delay
-{PSET_SIMPLE, 1, { PRC_RVA1, PRC0, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 41 parallel reverb
-{PSET_SIMPLE, 1, { PRC_DFR1, PRC0, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 42 series diffusor
-{PSET_LINEAR, 2, { PRC_DFR1, PRC_RVA1, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 43 diff & reverb
-{PSET_SIMPLE, 1, { PRC_DLY2, PRC0, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 44 lowpass delay
-{PSET_SIMPLE, 1, { PRC_MDY2, PRC0, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 45 modulating delay
-{PSET_SIMPLE, 1, { PRC_PTC1, PRC0, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 46 pitch shift
-{PSET_SIMPLE, 1, { PRC_PTC2, PRC0, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 47 pitch shift
-{PSET_SIMPLE, 1, { PRC_FLT1, PRC0, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 48 filter
-{PSET_SIMPLE, 1, { PRC_CRS1, PRC0, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 49 chorus
-{PSET_SIMPLE, 1, { PRC_ENV1, PRC0, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 50
-{PSET_SIMPLE, 1, { PRC_LFO1, PRC0, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 51 lfo
-{PSET_SIMPLE, 1, { PRC_EFO1, PRC0, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 52
-{PSET_SIMPLE, 1, { PRC_MDY1, PRC0, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 53 modulating delay
-{PSET_SIMPLE, 1, { PRC_FLT2, PRC0, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 54 lowpass - facing away
-{PSET_PARALLEL2, 2, { PRC_PTC2, PRC_PTC1, PRC0, PRC0, PRC0, PRC0 },1.0, WZERO }, // 55 ptc1/ptc2
-{PSET_FEEDBACK, 6, { PRC_DLY1, PRC0, PRC0, PRC_PTC1, PRC_FLT1, PRC0 },1.0, WZERO }, // 56 dly/ptc1
-{PSET_MOD, 4, { PRC_EFO1, PRC0, PRC_PTC1, PRC0, PRC0, PRC0 },1.0, WZERO }, // 57 efo mod ptc
-{PSET_LINEAR, 3, { PRC_DLY1, PRC_RVA1, PRC_CRS1, PRC0, PRC0, PRC0 },1.0, WZERO } // 58 dly/rvb/crs
-};
-
-
-// number of presets currently defined above
-
-#define CPSETTEMPLATES 60 //(sizeof( psets ) / sizeof( pset_t ))
-
-// init a preset - just clear state array
-void PSET_Init( pset_t *ppset )
-{
- // clear state array
- if( ppset ) memset( ppset->w, 0, sizeof( int ) * ( CPSET_STATES ));
-}
-
-// clear runtime slots
-void PSET_InitAll( void )
-{
- int i;
-
- for( i = 0; i < CPSETS; i++ )
- memset( &psets[i], 0, sizeof( pset_t ));
-}
-
-// free the preset - free all processors
-
-void PSET_Free( pset_t *ppset )
-{
- if( ppset )
- {
- // free processors
- PRC_FreeAll( ppset->prcs, ppset->cprcs );
-
- // clear
- memset( ppset, 0, sizeof( pset_t ));
- }
-}
-
-void PSET_FreeAll() { int i; for( i = 0; i < CPSETS; i++ ) PSET_Free( &psets[i] ); };
-
-// return preset struct, given index into preset template array
-// NOTE: should not ever be more than 2 or 3 of these active simultaneously
-pset_t *PSET_Alloc( int ipsettemplate )
-{
- pset_t *ppset;
- qboolean fok;
- int i;
-
- // don't excede array bounds
- if( ipsettemplate >= CPSETTEMPLATES )
- ipsettemplate = 0;
-
- // find free slot
- for( i = 0; i < CPSETS; i++)
- {
- if( !psets[i].fused )
- break;
- }
-
- if( i == CPSETS )
- return NULL;
-
- ppset = &psets[i];
-
- // copy template into preset
- *ppset = psettemplates[ipsettemplate];
-
- ppset->fused = true;
-
- // clear state array
- PSET_Init( ppset );
-
- // init all processors, set up processor function pointers
- fok = PRC_InitAll( ppset->prcs, ppset->cprcs );
-
- if( !fok )
- {
- // failed to init one or more processors
- MsgDev( D_ERROR, "Sound DSP: preset failed to init.\n");
- PRC_FreeAll( ppset->prcs, ppset->cprcs );
- return NULL;
- }
- return ppset;
-}
-
-// batch version of PSET_GetNext for linear array of processors. For performance.
-
-// ppset - preset array
-// pbuffer - input sample data
-// SampleCount - size of input buffer
-// OP: OP_LEFT - process left channel in place
-// OP_RIGHT - process right channel in place
-// OP_LEFT_DUPLICATe - process left channel, duplicate into right
-
-_inline void PSET_GetNextN( pset_t *ppset, portable_samplepair_t *pbf, int SampleCount, int op )
-{
- prc_t *pprc;
- int i, count = ppset->cprcs;
-
- switch( ppset->type )
- {
- default:
- case PSET_SIMPLE:
- {
- // x(n)--->P(0)--->y(n)
- ppset->prcs[0].pfnGetNextN( ppset->prcs[0].pdata, pbf, SampleCount, op );
- break;
- }
- case PSET_LINEAR:
- {
-
- // w0 w1 w2
- // x(n)--->P(0)-->P(1)-->...P(count-1)--->y(n)
-
- // w0 w1 w2 w3 w4 w5
- // x(n)--->P(0)-->P(1)-->P(2)-->P(3)-->P(4)-->y(n)
-
- // call batch processors in sequence - no internal state for batch processing
- // point to first processor
-
- pprc = &ppset->prcs[0];
-
- for( i = 0; i < count; i++ )
- {
- pprc->pfnGetNextN( pprc->pdata, pbf, SampleCount, op );
- pprc++;
- }
- break;
- }
- }
-}
-
-
-// Get next sample from this preset. called once for every sample in buffer
-// ppset is pointer to preset
-// x is input sample
-_inline int PSET_GetNext( pset_t *ppset, int x )
-{
- int *w = ppset->w;
- prc_t *pprc;
- int count = ppset->cprcs;
-
- // initialized 0'th element of state array
-
- w[0] = x;
-
- switch( ppset->type )
- {
- default:
- case PSET_SIMPLE:
- {
- // x(n)--->P(0)--->y(n)
- return ppset->prcs[0].pfnGetNext (ppset->prcs[0].pdata, x);
- }
- case PSET_LINEAR:
- {
- // w0 w1 w2
- // x(n)--->P(0)-->P(1)-->...P(count-1)--->y(n)
-
- // w0 w1 w2 w3 w4 w5
- // x(n)--->P(0)-->P(1)-->P(2)-->P(3)-->P(4)-->y(n)
-
- // call processors in reverse order, from count to 1
-
- // point to last processor
-
- pprc = &ppset->prcs[count-1];
-
- switch( count )
- {
- default:
- case 5:
- w[5] = pprc->pfnGetNext (pprc->pdata, w[4]);
- pprc--;
- case 4:
- w[4] = pprc->pfnGetNext (pprc->pdata, w[3]);
- pprc--;
- case 3:
- w[3] = pprc->pfnGetNext (pprc->pdata, w[2]);
- pprc--;
- case 2:
- w[2] = pprc->pfnGetNext (pprc->pdata, w[1]);
- pprc--;
- case 1:
- w[1] = pprc->pfnGetNext (pprc->pdata, w[0]);
- }
- return w[count];
- }
-
- case PSET_PARALLEL6:
- {
- // w0 w1 w2 w3 w6 w7
- // x(n)-P(0)-->P(1)-->P(2)-->(+)---P(5)--->y(n)
- // | ^
- // | w4 w5 |
- // -->P(3)-->P(4)---->
-
- pprc = &ppset->prcs[0];
-
- // start with all adders
-
- w[6] = w[3] + w[5];
-
- // top branch - evaluate in reverse order
-
- w[7] = pprc[5].pfnGetNext( pprc[5].pdata, w[6] );
- w[3] = pprc[2].pfnGetNext( pprc[2].pdata, w[2] );
- w[2] = pprc[1].pfnGetNext( pprc[1].pdata, w[1] );
-
- // bottom branch - evaluate in reverse order
-
- w[5] = pprc[4].pfnGetNext( pprc[4].pdata, w[4] );
- w[4] = pprc[3].pfnGetNext( pprc[3].pdata, w[1] );
-
- w[1] = pprc[0].pfnGetNext( pprc[0].pdata, w[0] );
-
- return w[7];
- }
- case PSET_PARALLEL2:
- { // w0 w1 w3
- // x(n)--->P(0)-->(+)-->y(n)
- // ^
- // w0 w2 |
- // x(n)--->P(1)-----
-
- pprc = &ppset->prcs[0];
-
- w[3] = w[1] + w[2];
-
- w[1] = pprc->pfnGetNext( pprc->pdata, w[0] );
- pprc++;
- w[2] = pprc->pfnGetNext( pprc->pdata, w[0] );
-
- return w[3];
- }
-
- case PSET_PARALLEL4:
- {
- // w0 w1 w2 w5
- // x(n)--->P(0)-->P(1)-->(+)-->y(n)
- // ^
- // w0 w3 w4 |
- // x(n)--->P(2)-->P(3)-----
+typedef struct sx_preset_s
+{
+ float room_lp; // lowpass
+ float room_mod; // modulation
- pprc = &ppset->prcs[0];
+ // reverb
+ float room_size;
+ float room_refl;
+ float room_rvblp;
- w[5] = w[2] + w[4];
+ // delay
+ float room_delay;
+ float room_feedback;
+ float room_dlylp;
+ float room_left;
+} sx_preset_t;
- w[2] = pprc[1].pfnGetNext( pprc[1].pdata, w[1] );
- w[4] = pprc[3].pfnGetNext( pprc[3].pdata, w[3] );
+typedef struct dly_s
+{
+ size_t cdelaysamplesmax; // delay line array size
- w[1] = pprc[0].pfnGetNext( pprc[0].pdata, w[0] );
- w[3] = pprc[2].pfnGetNext( pprc[2].pdata, w[0] );
+ // delay line pointers
+ size_t idelayinput;
+ size_t idelayoutput;
- return w[5];
- }
+ // crossfade
+ size_t idelayoutputxf; // output pointer
+ int xfade; // value
- case PSET_PARALLEL5:
- {
- // w0 w1 w2 w5 w6
- // x(n)--->P(0)-->P(1)-->(+)-->P(4)-->y(n)
- // ^
- // w0 w3 w4 |
- // x(n)--->P(2)-->P(3)-----
+ int delaysamples; // delay setting
+ int delayfeedback; // feedback setting
- pprc = &ppset->prcs[0];
+ // lowpass
+ int lp; // is lowpass enabled
+ int lp0, lp1, lp2; // lowpass buffer
- w[5] = w[2] + w[4];
+ // modulation
+ int mod;
+ int modcur;
- w[6] = pprc[4].pfnGetNext( pprc[4].pdata, w[5] );
+ // delay line
+ int *lpdelayline;
+} dly_t;
- w[2] = pprc[1].pfnGetNext( pprc[1].pdata, w[1] );
- w[4] = pprc[3].pfnGetNext( pprc[3].pdata, w[3] );
+const sx_preset_t rgsxpre[] =
+{
+// -------reverb-------- -------delay--------
+// lp mod size refl rvblp delay feedback dlylp left
+{ 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 2.0, 0.0 }, // 0 off
+{ 0.0, 0.0, 0.0, 0.0, 1.0, 0.065, 0.1, 0.0, 0.01 }, // 1 generic
+{ 0.0, 0.0, 0.0, 0.0, 1.0, 0.02, 0.75, 0.0, 0.01 }, // 2 metalic
+{ 0.0, 0.0, 0.0, 0.0, 1.0, 0.03, 0.78, 0.0, 0.02 }, // 3
+{ 0.0, 0.0, 0.0, 0.0, 1.0, 0.06, 0.77, 0.0, 0.03 }, // 4
+{ 0.0, 0.0, 0.05, 0.85, 1.0, 0.008, 0.96, 2.0, 0.01 }, // 5 tunnel
+{ 0.0, 0.0, 0.05, 0.88, 1.0, 0.01, 0.98, 2.0, 0.02 }, // 6
+{ 0.0, 0.0, 0.05, 0.92, 1.0, 0.015, 0.995, 2.0, 0.04 }, // 7
+{ 0.0, 0.0, 0.05, 0.84, 1.0, 0.0, 0.0, 2.0, 0.012 }, // 8 chamber
+{ 0.0, 0.0, 0.05, 0.9, 1.0, 0.0, 0.0, 2.0, 0.008 }, // 9
+{ 0.0, 0.0, 0.05, 0.95, 1.0, 0.0, 0.0, 2.0, 0.004 }, // 10
+{ 0.0, 0.0, 0.05, 0.7, 0.0, 0.0, 0.0, 2.0, 0.012 }, // 11 brite
+{ 0.0, 0.0, 0.055, 0.78, 0.0, 0.0, 0.0, 2.0, 0.008 }, // 12
+{ 0.0, 0.0, 0.05, 0.86, 0.0, 0.0, 0.0, 2.0, 0.002 }, // 13
+{ 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 2.0, 0.01 }, // 14 water
+{ 1.0, 0.0, 0.0, 0.0, 1.0, 0.06, 0.85, 2.0, 0.02 }, // 15
+{ 1.0, 0.0, 0.0, 0.0, 1.0, 0.2, 0.6, 2.0, 0.05 }, // 16
+{ 0.0, 0.0, 0.05, 0.8, 1.0, 0.0, 0.48, 2.0, 0.016 }, // 17 concrete
+{ 0.0, 0.0, 0.06, 0.9, 1.0, 0.0, 0.52, 2.0, 0.01 }, // 18
+{ 0.0, 0.0, 0.07, 0.94, 1.0, 0.3, 0.6, 2.0, 0.008 }, // 19
+{ 0.0, 0.0, 0.0, 0.0, 1.0, 0.3, 0.42, 2.0, 0.0 }, // 20 outside
+{ 0.0, 0.0, 0.0, 0.0, 1.0, 0.35, 0.48, 2.0, 0.0 }, // 21
+{ 0.0, 0.0, 0.0, 0.0, 1.0, 0.38, 0.6, 2.0, 0.0 }, // 22
+{ 0.0, 0.0, 0.05, 0.9, 1.0, 0.2, 0.28, 0.0, 0.0 }, // 23 cavern
+{ 0.0, 0.0, 0.07, 0.9, 1.0, 0.3, 0.4, 0.0, 0.0 }, // 24
+{ 0.0, 0.0, 0.09, 0.9, 1.0, 0.35, 0.5, 0.0, 0.0 }, // 25
+{ 0.0, 1.0, 0.01, 0.9, 0.0, 0.0, 0.0, 2.0, 0.05 }, // 26 weirdo
+{ 0.0, 0.0, 0.0, 0.0, 1.0, 0.009, 0.999, 2.0, 0.04 }, // 27
+{ 0.0, 0.0, 0.001, 0.999, 0.0, 0.2, 0.8, 2.0, 0.05 } // 28
+};
- w[1] = pprc[0].pfnGetNext( pprc[0].pdata, w[0] );
- w[3] = pprc[2].pfnGetNext( pprc[2].pdata, w[0] );
+// cvars
+convar_t *dsp_off; // disable dsp
+convar_t *roomwater_type; // water room_type
+convar_t *room_type; // current room type
+convar_t *hisound; // DSP quality
- return w[6];
- }
+// underwater/special fx modulations
+convar_t *sxmod_mod;
+convar_t *sxmod_lowpass;
- case PSET_FEEDBACK:
- {
- // w0 w1 w2 w3 w4 w7
- // x(n)-P(0)--(+)-->P(1)-->P(2)-->P(5)->y(n)
- // ^ |
- // | w6 w5 v
- // -----P(4)<--P(3)--
-
- pprc = &ppset->prcs[0];
-
- // start with adders
-
- w[2] = w[1] + w[6];
-
- // evaluate in reverse order
-
- w[7] = pprc[5].pfnGetNext( pprc[5].pdata, w[4] );
- w[6] = pprc[4].pfnGetNext( pprc[4].pdata, w[5] );
- w[5] = pprc[3].pfnGetNext( pprc[3].pdata, w[4] );
- w[4] = pprc[2].pfnGetNext( pprc[2].pdata, w[3] );
- w[3] = pprc[1].pfnGetNext( pprc[1].pdata, w[2] );
- w[1] = pprc[0].pfnGetNext( pprc[0].pdata, w[0] );
-
- return w[7];
- }
- case PSET_FEEDBACK3:
- {
- // w0 w1 w2
- // x(n)---(+)-->P(0)--------->y(n)
- // ^ |
- // | w4 w3 v
- // -----P(2)<--P(1)--
-
- pprc = &ppset->prcs[0];
-
- // start with adders
-
- w[1] = w[0] + w[4];
-
- // evaluate in reverse order
-
- w[4] = pprc[2].pfnGetNext( pprc[2].pdata, w[3] );
- w[3] = pprc[1].pfnGetNext( pprc[1].pdata, w[2] );
- w[2] = pprc[0].pfnGetNext( pprc[0].pdata, w[1] );
-
- return w[2];
- }
- case PSET_FEEDBACK4:
- {
- // w0 w1 w2 w5
- // x(n)---(+)-->P(0)-------->P(3)--->y(n)
- // ^ |
- // | w4 w3 v
- // ---P(2)<--P(1)--
-
- pprc = &ppset->prcs[0];
-
- // start with adders
-
- w[1] = w[0] + w[4];
-
- // evaluate in reverse order
-
- w[5] = pprc[3].pfnGetNext( pprc[3].pdata, w[2] );
- w[4] = pprc[2].pfnGetNext( pprc[2].pdata, w[3] );
- w[3] = pprc[1].pfnGetNext( pprc[1].pdata, w[2] );
- w[2] = pprc[0].pfnGetNext( pprc[0].pdata, w[1] );
-
- return w[2];
- }
- case PSET_MOD:
- {
- // w0 w1 w3 w4
- // x(n)------>P(1)--P(2)--P(3)--->y(n)
- // w0 w2 ^
- // x(n)------>P(0)....:
+// stereo delay(no feedback)
+convar_t *sxste_delay; // straight left delay
- pprc = &ppset->prcs[0];
+// mono reverb
+convar_t *sxrvb_lp; // lowpass
+convar_t *sxrvb_feedback; // reverb decay. Higher -- longer
+convar_t *sxrvb_size; // room size. Higher -- larger
- w[4] = pprc[3].pfnGetNext( pprc[3].pdata, w[3] );
+// mono delay
+convar_t *sxdly_lp; // lowpass
+convar_t *sxdly_feedback; // cycles
+convar_t *sxdly_delay; // current delay in seconds
- w[3] = pprc[2].pfnGetNext( pprc[2].pdata, w[1] );
+convar_t *dsp_room; // for compability
+int idsp_dma_speed;
+int idsp_room;
+int room_typeprev;
- // modulate processor 2
+// routines
+int sxamodl, sxamodr; // amplitude modulation values
+int sxamodlt, sxamodrt; // modulation targets
+int sxmod1cur, sxmod2cur;
+int sxmod1, sxmod2;
+int sxhires;
- pprc[2].pfnMod( pprc[2].pdata, ((float)w[2] / (float)PMAX));
+portable_samplepair_t *paintto = NULL;
- // get modulator output
+dly_t rgsxdly[MAXDLY]; // stereo is last
+int rgsxlp[MAXLP];
- w[2] = pprc[0].pfnGetNext( pprc[0].pdata, w[0] );
+void SX_Profiling_f( void );
- w[1] = pprc[1].pfnGetNext( pprc[1].pdata, w[0] );
+/*
+============
+SX_ReloadRoomFX
- return w[4];
- }
- case PSET_MOD2:
- {
- // w0 w2
- // x(n)---------P(1)-->y(n)
- // w0 w1 ^
- // x(n)-->P(0)....:
+============
+*/
+void SX_ReloadRoomFX( void )
+{
+ if( !dsp_room ) return; // not initialized
- pprc = &ppset->prcs[0];
+ sxste_delay->modified = true;
+ sxrvb_feedback->modified = true;
+ sxdly_delay->modified = true;
+ room_type->modified = true;
+}
- // modulate processor 1
+/*
+============
+SX_Init()
- pprc[1].pfnMod( pprc[1].pdata, ((float)w[1] / (float)PMAX));
+Starts sound crackling system
+============
+*/
+void SX_Init( void )
+{
+ memset( rgsxdly, 0, sizeof( rgsxdly ));
+ memset( rgsxlp, 0, sizeof( rgsxlp ));
- // get modulator output
+ sxamodr = sxamodl = sxamodrt = sxamodlt = 255;
+ idsp_dma_speed = SOUND_11k;
- w[1] = pprc[0].pfnGetNext( pprc[0].pdata, w[0] );
+ hisound = Cvar_Get( "room_hires", "2", CVAR_ARCHIVE, "dsp quality. 1 for 22k, 2 for 44k(recommended) and 3 for 96k" );
+ sxhires = 2;
- w[2] = pprc[1].pfnGetNext( pprc[1].pdata, w[0] );
+ sxmod1cur = sxmod1 = 350 * ( idsp_dma_speed / SOUND_11k );
+ sxmod2cur = sxmod2 = 450 * ( idsp_dma_speed / SOUND_11k );
- return w[2];
+ dsp_off = Cvar_Get( "dsp_off", "0", 0, "disable DSP processing" );
+ roomwater_type = Cvar_Get( "waterroom_type", "14", 0, "water room type" );
+ room_type = Cvar_Get( "room_type", "0", 0, "current room type preset" );
- }
- case PSET_MOD3:
- {
- // w0 w2 w3
- // x(n)----------P(1)-->P(2)-->y(n)
- // w0 w1 ^
- // x(n)-->P(0).....:
+ sxmod_lowpass = Cvar_Get( "room_lp", "0", 0, "for water fx, lowpass for entire room" );
+ sxmod_mod = Cvar_Get( "room_mod", "0", 0, "stereo amptitude modulation for room" );
- pprc = &ppset->prcs[0];
+ sxrvb_size = Cvar_Get( "room_size", "0", 0, "reverb: initial reflection size" );
+ sxrvb_feedback = Cvar_Get( "room_refl", "0", 0, "reverb: decay time" );
+ sxrvb_lp = Cvar_Get( "room_rvblp", "1", 0, "reverb: low pass filtering level" );
- w[3] = pprc[2].pfnGetNext( pprc[2].pdata, w[2] );
+ sxdly_delay = Cvar_Get( "room_delay", "0.8", 0, "mono delay: delay time" );
+ sxdly_feedback = Cvar_Get( "room_feedback", "0.2", 0, "mono delay: decay time" );
+ sxdly_lp = Cvar_Get( "room_dlylp", "1", 0, "mono delay: low pass filtering level" );
- // modulate processor 1
+ sxste_delay = Cvar_Get( "room_left", "0", 0, "left channel delay time" );
- pprc[1].pfnMod( pprc[1].pdata, ((float)w[1] / (float)PMAX));
+ Cmd_AddCommand( "dsp_profile", SX_Profiling_f, "dsp stress-test, first argument is room_type" );
- // get modulator output
+ // for compability
+ dsp_room = room_type;
+ SX_ReloadRoomFX();
+}
- w[1] = pprc[0].pfnGetNext( pprc[0].pdata, w[0] );
+/*
+===========
+DLY_Free
- w[2] = pprc[1].pfnGetNext( pprc[1].pdata, w[0] );
+Free memory allocated for DSP
+===========
+*/
+void DLY_Free( int idelay )
+{
+ Assert( idelay >= 0 && idelay < MAXDLY );
- return w[2];
- }
+ if( rgsxdly[idelay].lpdelayline )
+ {
+ Z_Free( rgsxdly[idelay].lpdelayline );
+ rgsxdly[idelay].lpdelayline = NULL;
}
}
+/*
+==========
+SX_Shutdown
-/////////////
-// DSP system
-/////////////
-
-// Main interface
-
-// Whenever the preset # changes on any of these processors, the old processor is faded out, new is faded in.
-// dsp_chan is optionally set when a sound is played - a preset is sent with the start_static/dynamic sound.
-//
-// sound1---->dsp_chan--> -------------(+)---->dsp_water--->dsp_player--->out
-// sound2---->dsp_chan--> | |
-// sound3---------------> ----dsp_room---
-// | |
-// --dsp_indirect-
-
-// dsp_room - set this cvar to a preset # to change the room dsp. room fx are more prevalent farther from player.
-// use: when player moves into a new room, all sounds played in room take on its reverberant character
-// dsp_water - set this cvar (once) to a preset # for serial underwater sound.
-// use: when player goes under water, all sounds pass through this dsp (such as low pass filter)
-// dsp_player - set this cvar to a preset # to cause all sounds to run through the effect (serial, in-line).
-// use: player is deafened, player fires special weapon, player is hit by special weapon.
-// dsp_facingaway- set this cvar to a preset # appropriate for sounds which are played facing away from player (weapon,voice)
-
-// Dsp presets
+Stop DSP processor
+==========
+*/
+void SX_Free( void )
+{
+ int i;
-convar_t *dsp_room; // room dsp preset - sounds more distant from player (1ch)
+ for( i = 0; i <= 3; i++ )
+ DLY_Free( i );
-int ipset_room_prev;
+ Cmd_RemoveCommand( "dsp_profile" );
+}
-// legacy room_type support
-convar_t *dsp_room_type;
-int ipset_room_typeprev;
+/*
+===========
+DLY_Init
-// DSP processors
+Initialize dly
+===========
+*/
+int DLY_Init( int idelay, float delay )
+{
+ dly_t *cur;
-int idsp_room;
-convar_t *dsp_stereo; // set to 1 for true stereo processing. 2x perf hit.
+ // DLY_Init called anytime with constants. So valid it in debug builds only.
+ Assert( idelay >= 0 && idelay < MAXDLY );
+ Assert( delay > 0.0f && delay <= MAX_DELAY );
-// DSP preset executor
-#define CDSPS 32 // max number dsp executors active
-#define DSPCHANMAX 4 // max number of channels dsp can process (allocs a separte processor for each chan)
+ DLY_Free( idelay ); // free dly if it's allocated
-typedef struct
-{
- qboolean fused;
- int cchan; // 1-4 channels, ie: mono, FrontLeft, FrontRight, RearLeft, RearRight
- pset_t *ppset[DSPCHANMAX]; // current preset (1-4 channels)
- int ipset; // current ipreset
- pset_t *ppsetprev[DSPCHANMAX]; // previous preset (1-4 channels)
- int ipsetprev; // previous ipreset
- float xfade; // crossfade time between previous preset and new
- rmp_t xramp; // crossfade ramp
-} dsp_t;
-
-dsp_t dsps[CDSPS];
-
-void DSP_Init( int idsp )
-{
- dsp_t *pdsp;
-
- if( idsp < 0 || idsp > CDSPS )
- return;
-
- pdsp = &dsps[idsp];
- memset( pdsp, 0, sizeof( dsp_t ));
-}
+ cur = &rgsxdly[idelay];
+ cur->cdelaysamplesmax = ((int)(delay * idsp_dma_speed) << sxhires) + 1;
+ cur->lpdelayline = (int *)Z_Malloc( cur->cdelaysamplesmax * sizeof( int ));
+ cur->xfade = 0;
-void DSP_Free( int idsp )
-{
- dsp_t *pdsp;
- int i;
+ // init modulation
+ cur->mod = cur->modcur = 0;
- if( idsp < 0 || idsp > CDSPS )
- return;
-
- pdsp = &dsps[idsp];
+ // init lowpass
+ cur->lp = 1;
+ cur->lp0 = cur->lp1 = cur->lp2 = 0;
- for( i = 0; i < pdsp->cchan; i++ )
- {
- if( pdsp->ppset[i] )
- PSET_Free( pdsp->ppset[i] );
-
- if( pdsp->ppsetprev[i] )
- PSET_Free( pdsp->ppsetprev[i] );
- }
+ cur->idelayinput = 0;
+ cur->idelayoutput = cur->cdelaysamplesmax - cur->delaysamples; // NOTE: delaysamples must be set!!!
- memset( pdsp, 0, sizeof( dsp_t ));
-}
-// Init all dsp processors - called once, during engine startup
-void DSP_InitAll( void )
-{
- int idsp;
-
- // order is important, don't rearange.
- FLT_InitAll();
- DLY_InitAll();
- RVA_InitAll();
- LFOWAV_InitAll();
- LFO_InitAll();
-
- CRS_InitAll();
- PTC_InitAll();
- ENV_InitAll();
- EFO_InitAll();
- MDY_InitAll();
- AMP_InitAll();
-
- PSET_InitAll();
-
- for( idsp = 0; idsp < CDSPS; idsp++ )
- DSP_Init( idsp );
+ return 1;
}
-// free all resources associated with dsp - called once, during engine shutdown
+/*
+============
+DLY_MovePointer
-void DSP_FreeAll( void )
+Checks overflow and moves pointer
+============
+*/
+_inline void DLY_MovePointer( dly_t *dly )
{
- int idsp;
-
- // order is important, don't rearange.
- for( idsp = 0; idsp < CDSPS; idsp++ )
- DSP_Free( idsp );
-
- AMP_FreeAll();
- MDY_FreeAll();
- EFO_FreeAll();
- ENV_FreeAll();
- PTC_FreeAll();
- CRS_FreeAll();
-
- LFO_FreeAll();
- LFOWAV_FreeAll();
- RVA_FreeAll();
- DLY_FreeAll();
- FLT_FreeAll();
+ if( ++dly->idelayinput >= dly->cdelaysamplesmax )
+ dly->idelayinput = 0;
+
+ if( ++dly->idelayoutput >= dly->cdelaysamplesmax )
+ dly->idelayoutput = 0;
}
+/*
+=============
+DLY_CheckNewStereoDelayVal
-// allocate a new dsp processor chain, kill the old processor. Called by DSP_CheckNewPreset()
-// ipset is new preset
-// xfade is crossfade time when switching between presets (milliseconds)
-// cchan is how many simultaneous preset channels to allocate (1-4)
-// return index to new dsp
-int DSP_Alloc( int ipset, float xfade, int cchan )
+Update stereo processor settings if we are in new room
+=============
+*/
+void DLY_CheckNewStereoDelayVal( void )
{
- dsp_t *pdsp;
- int i, idsp;
- int cchans = bound( 1, cchan, DSPCHANMAX);
+ dly_t *const dly = &rgsxdly[STEREODLY];
+ float delay = sxste_delay->value;
+
+ if( !sxste_delay->modified )
+ return;
- // find free slot
- for( idsp = 0; idsp < CDSPS; idsp++ )
+ if( delay == 0 )
{
- if( !dsps[idsp].fused )
- break;
+ DLY_Free( STEREODLY );
}
+ else
+ {
+ int samples;
- if( idsp == CDSPS )
- return -1;
+ delay = Q_min( delay, MAX_STEREO_DELAY );
+ samples = (int)(delay * idsp_dma_speed) << sxhires;
- pdsp = &dsps[idsp];
+ // re-init dly
+ if( !dly->lpdelayline )
+ {
+ dly->delaysamples = samples;
+ DLY_Init( STEREODLY, MAX_STEREO_DELAY );
+ }
- DSP_Init( idsp );
+ if( dly->delaysamples != samples )
+ {
+ dly->xfade = 128;
+ dly->idelayoutputxf = dly->idelayinput - samples;
+ if( dly->idelayoutputxf < 0 )
+ dly->idelayoutputxf += dly->cdelaysamplesmax;
+ }
- pdsp->fused = true;
- pdsp->cchan = cchans;
+ dly->modcur = dly->mod = 0;
- // allocate a preset processor for each channel
- pdsp->ipset = ipset;
- pdsp->ipsetprev = 0;
-
- for( i = 0; i < pdsp->cchan; i++ )
- {
- pdsp->ppset[i] = PSET_Alloc( ipset );
- pdsp->ppsetprev[i] = NULL;
+ if( dly->delaysamples == 0 )
+ DLY_Free( STEREODLY );
}
- // set up crossfade time in seconds
- pdsp->xfade = xfade / 1000.0f;
-
- RMP_SetEnd( &pdsp->xramp );
-
- return idsp;
+ sxste_delay->modified = false;
}
-// return gain for current preset associated with dsp
-// get crossfade to new gain if switching from previous preset (from preset crossfader value)
-// Returns 1.0 gain if no preset (preset 0)
-float DSP_GetGain( int idsp )
+/*
+=============
+DLY_DoStereoDelay
+
+Do stereo processing
+=============
+*/
+void DLY_DoStereoDelay( int count )
{
- float gain_target = 0.0;
- float gain_prev = 0.0;
- float gain;
- dsp_t *pdsp;
- int r;
-
- if( idsp < 0 || idsp > CDSPS )
- return 1.0f;
-
- pdsp = &dsps[idsp];
-
- // get current preset's gain
- if( pdsp->ppset[0] )
- gain_target = pdsp->ppset[0]->gain;
- else gain_target = 1.0f;
-
- // if not crossfading, return current preset gain
- if( RMP_HitEnd( &pdsp->xramp ))
- {
- // return current preset's gain
- return gain_target;
- }
-
- // get previous preset gain
+ int delay, samplexf;
+ dly_t *const dly = &rgsxdly[STEREODLY];
+ portable_samplepair_t *paint = paintto;
- if( pdsp->ppsetprev[0] )
- gain_prev = pdsp->ppsetprev[0]->gain;
- else gain_prev = 1.0;
+ if( !dly->lpdelayline )
+ return; // inactive
- // if current gain = target preset gain, return
- if( gain_target == gain_prev )
+ for( ; count; count--, paint++ )
{
- if( gain_target == 0.0f )
- return 1.0f;
- return gain_target;
- }
+ if( dly->mod && --dly->modcur < 0 )
+ dly->modcur = dly->mod;
- // get crossfade ramp value (updated elsewhere, when actually crossfading preset data)
- r = RMP_GetCurrent( &pdsp->xramp );
+ delay = dly->lpdelayline[dly->idelayoutput];
- // crossfade from previous to current preset gain
- if( gain_target > gain_prev )
- {
- // ramping gain up - ramp up gain to target in last 10% of ramp
- float rf = (float)r;
- float pmax = (float)PMAX;
+ // process only if crossfading, active left value or delayline
+ if( delay || paint->left || dly->xfade )
+ {
+ // set up new crossfade, if not crossfading, not modulating, but going to
+ if( !dly->xfade && !dly->modcur && dly->mod )
+ {
+ dly->idelayoutputxf = dly->idelayoutput + ((Com_RandomLong( 0, 255 ) * dly->delaysamples ) >> 9 );
- rf = rf / pmax; // rf 0->1.0
+ dly->xfade = 128;
+ }
- if( rf < 0.9 ) rf = 0.0;
- else rf = (rf - 0.9) / (1.0 - 0.9); // 0->1.0 after rf > 0.9
+ dly->idelayoutputxf %= dly->cdelaysamplesmax;
- // crossfade gain from prev to target over rf
- gain = gain_prev + (gain_target - gain_prev) * rf;
+ // modify delay, if crossfading
+ if( dly->xfade )
+ {
+ samplexf = dly->lpdelayline[dly->idelayoutputxf] * (128 - dly->xfade) >> 7;
+ delay = samplexf + ((delay * dly->xfade) >> 7);
- return gain;
- }
- else
- {
- // ramping gain down - drop gain to target in first 10% of ramp
- float rf = (float) r;
- float pmax = (float)PMAX;
+ if( ++dly->idelayoutputxf >= dly->cdelaysamplesmax )
+ dly->idelayoutputxf = 0;
- rf = rf / pmax; // rf 0.0->1.0
+ if( --dly->xfade == 0 )
+ dly->idelayoutput = dly->idelayoutputxf;
+ }
- if( rf < 0.1 ) rf = (rf - 0.1) / (0.0 - 0.1); // 1.0->0.0 if rf < 0.1
- else rf = 0.0;
+ // save left value to delay line
+ dly->lpdelayline[dly->idelayinput] = CLIP( paint->left );
- // crossfade gain from prev to target over rf
- gain = gain_prev + (gain_target - gain_prev) * (1.0 - rf);
+ // paint new delay value
+ paint->left = delay;
+ }
+ else
+ {
+ // clear delay line
+ dly->lpdelayline[dly->idelayinput] = 0;
+ }
- return gain;
+ DLY_MovePointer( dly );
}
}
-// free previous preset if not 0
-_inline void DSP_FreePrevPreset( dsp_t *pdsp )
+/*
+=============
+DLY_CheckNewDelayVal
+
+Update delay processor settings if we are in new room
+=============
+*/
+void DLY_CheckNewDelayVal( void )
{
- // free previous presets if non-null - ie: rapid change of preset just kills old without xfade
- if( pdsp->ipsetprev )
- {
- int i;
+ float delay = sxdly_delay->value;
+ dly_t *const dly = &rgsxdly[MONODLY];
- for( i = 0; i < pdsp->cchan; i++ )
+ if( sxdly_delay->modified )
+ {
+ if( delay == 0 )
{
- if( pdsp->ppsetprev[i] )
+ DLY_Free( MONODLY );
+ }
+ else
+ {
+ delay = min( delay, MAX_MONO_DELAY );
+ dly->delaysamples = (int)(delay * idsp_dma_speed) << sxhires;
+
+ // init dly
+ if( !dly->lpdelayline )
+ DLY_Init( MONODLY, MAX_MONO_DELAY );
+
+ if( dly->lpdelayline )
{
- PSET_Free( pdsp->ppsetprev[i] );
- pdsp->ppsetprev[i] = NULL;
+ memset( dly->lpdelayline, 0, dly->cdelaysamplesmax * sizeof( int ) );
+ dly->lp0 = dly->lp1 = dly->lp2 = 0;
}
+
+ dly->idelayinput = 0;
+ dly->idelayoutput = dly->cdelaysamplesmax - dly->delaysamples;
+
+ if( !dly->delaysamples )
+ DLY_Free( MONODLY );
+
}
- pdsp->ipsetprev = 0;
}
+ sxdly_delay->modified = false;
+ dly->lp = sxdly_lp->integer;
+ dly->delayfeedback = 255 * sxdly_feedback->value;
}
-// alloc new preset if different from current
-// xfade from prev to new preset
-// free previous preset, copy current into previous, set up xfade from previous to new
-void DSP_SetPreset( int idsp, int ipsetnew )
+/*
+=============
+DLY_DoDelay
+
+Do delay processing
+=============
+*/
+void DLY_DoDelay( int count )
{
- dsp_t *pdsp;
- pset_t *ppsetnew[DSPCHANMAX];
- int i;
+ dly_t *const dly = &rgsxdly[MONODLY];
+ portable_samplepair_t *paint = paintto;
+ int delay;
- ASSERT( idsp >= 0 && idsp < CDSPS );
+ if( !dly->lpdelayline || !count )
+ return; // inactive
- pdsp = &dsps[idsp];
+ for( ; count; count--, paint++ )
+ {
+ delay = dly->lpdelayline[dly->idelayoutput];
- // validate new preset range
- if( ipsetnew >= CPSETTEMPLATES || ipsetnew < 0 )
- return;
+ // don't process if delay line and left/right samples are zero
+ if( delay || paint->left || paint->right )
+ {
+ // calculate delayed value from average
+ int val = (( paint->left + paint->right ) >> 1 ) + (( dly->delayfeedback * delay ) >> 8);
+ val = CLIP( val );
- // ignore if new preset is same as current preset
- if( ipsetnew == pdsp->ipset )
- return;
+ if( dly->lp ) // lowpass
+ {
+ dly->lp0 = dly->lp1;
+ dly->lp1 = val;
+ val = ( dly->lp0 + dly->lp1 + (val << 1) ) >> 2;
+ }
- // alloc new presets (each channel is a duplicate preset)
- ASSERT( pdsp->cchan <= DSPCHANMAX );
+ dly->lpdelayline[dly->idelayinput] = val;
- for( i = 0; i < pdsp->cchan; i++ )
- {
- ppsetnew[i] = PSET_Alloc( ipsetnew );
+ val >>= 2;
- if( !ppsetnew[i] )
+ paint->left = CLIP( paint->left + val );
+ paint->right = CLIP( paint->right + val );
+ }
+ else
{
- MsgDev( D_NOTE, "DSP preset failed to allocate.\n" );
- return;
+ dly->lpdelayline[dly->idelayinput] = 0;
+ dly->lp0 = dly->lp1 = 0;
}
+
+ DLY_MovePointer( dly );
}
+}
+
+/*
+===========
+RVB_SetUpDly
- ASSERT( pdsp );
+Set up dly for reverb
+===========
+*/
+void RVB_SetUpDly( int pos, float delay, int kmod )
+{
+ int samples;
- // free PREVIOUS previous preset if not 0
- DSP_FreePrevPreset( pdsp );
+ delay = Q_min( delay, MAX_REVERB_DELAY );
+ samples = (int)(delay * idsp_dma_speed) << sxhires;
- for( i = 0; i < pdsp->cchan; i++ )
+ if( !rgsxdly[pos].lpdelayline )
{
- // current becomes previous
- pdsp->ppsetprev[i] = pdsp->ppset[i];
-
- // new becomes current
- pdsp->ppset[i] = ppsetnew[i];
+ rgsxdly[pos].delaysamples = samples;
+ DLY_Init( pos, MAX_REVERB_DELAY );
}
-
- pdsp->ipsetprev = pdsp->ipset;
- pdsp->ipset = ipsetnew;
- // clear ramp
- RMP_SetEnd( &pdsp->xramp );
-
- // make sure previous dsp preset has data
- ASSERT( pdsp->ppsetprev[0] );
+ rgsxdly[pos].modcur = rgsxdly[pos].mod = (int)(kmod * idsp_dma_speed / SOUND_11k) << sxhires;
+
+ // set up crossfade, if delay has changed
+ if( rgsxdly[pos].delaysamples != samples )
+ {
+ rgsxdly[pos].idelayoutputxf = rgsxdly[pos].idelayinput - samples;
+ if( rgsxdly[pos].idelayoutputxf < 0 )
+ rgsxdly[pos].idelayoutputxf += rgsxdly[pos].cdelaysamplesmax;
+ rgsxdly[pos].xfade = 32;
+ }
- // shouldn't be crossfading if current dsp preset == previous dsp preset
- ASSERT( pdsp->ipset != pdsp->ipsetprev );
+ if( !rgsxdly[pos].delaysamples )
+ DLY_Free( pos );
- RMP_Init( &pdsp->xramp, pdsp->xfade, 0, PMAX );
}
-///////////////////////////////////////
-// Helpers: called only from DSP_Process
-///////////////////////////////////////
+/*
+===========
+RVB_CheckNewReverbVal
-// return true if batch processing version of preset exists
-_inline qboolean FBatchPreset( pset_t *ppset )
+Update reverb settings if we are in new room
+===========
+*/
+void RVB_CheckNewReverbVal( void )
{
- switch( ppset->type )
+ dly_t *const dly1 = &rgsxdly[REVERBPOS];
+ dly_t *const dly2 = &rgsxdly[REVERBPOS + 1];
+ float delay = sxrvb_size->value;
+
+ if( sxrvb_size->modified )
{
- case PSET_LINEAR:
- return true;
- case PSET_SIMPLE:
- return true;
- default:
- return false;
+ if( delay == 0.0f )
+ {
+ DLY_Free( REVERBPOS );
+ DLY_Free( REVERBPOS + 1 );
+ }
+ else
+ {
+ RVB_SetUpDly( REVERBPOS, sxrvb_size->value, 500 );
+ RVB_SetUpDly( REVERBPOS+1, sxrvb_size->value * 0.71f, 700 );
+ }
}
+
+ sxrvb_size->modified = false;
+ dly1->lp = dly2->lp = sxrvb_lp->integer;
+ dly1->delayfeedback = dly2->delayfeedback = (int)(255 * sxrvb_feedback->value);
}
-// Helper: called only from DSP_Process
-// mix front stereo buffer to mono buffer, apply dsp fx
-_inline void DSP_ProcessStereoToMono( dsp_t *pdsp, portable_samplepair_t *pbfront, int sampleCount, qboolean bcrossfading )
+/*
+===========
+RVB_DoReverbForOneDly
+
+Do reverberation for one dly
+===========
+*/
+int RVB_DoReverbForOneDly( dly_t *dly, const int vlr, const portable_samplepair_t *samplepair )
{
- portable_samplepair_t *pbf = pbfront; // pointer to buffer of front stereo samples to process
- int count = sampleCount;
- int av, x;
+ int delay;
+ int samplexf;
+ int val, valt;
+ int voutm = 0;
+
+ if( --dly->modcur < 0 )
+ dly->modcur = dly->mod;
+
+ delay = dly->lpdelayline[dly->idelayoutput];
- if( !bcrossfading )
+ if( dly->xfade || delay || samplepair->left || samplepair->right )
{
- if( FBatchPreset( pdsp->ppset[0] ))
+ // modulate delay rate
+ if( !dly->mod )
{
- // convert Stereo to Mono in place, then batch process fx: perf KDB
+ dly->idelayoutputxf = dly->idelayoutput + ((Com_RandomLong( 0, 255 ) * delay) >> 9 );
- // front->left + front->right / 2 into front->left, front->right duplicated.
- while( count-- )
- {
- pbf->left = (pbf->left + pbf->right) >> 1;
- pbf++;
- }
-
- // process left (mono), duplicate output into right
- PSET_GetNextN( pdsp->ppset[0], pbfront, sampleCount, OP_LEFT_DUPLICATE);
+ if( dly->idelayoutputxf >= dly->cdelaysamplesmax )
+ dly->idelayoutputxf -= dly->cdelaysamplesmax;
+
+ dly->xfade = REVERB_XFADE;
}
- else
+
+ if( dly->xfade )
{
- // avg left and right -> mono fx -> duplcate out left and right
- while( count-- )
- {
- av = ( ( pbf->left + pbf->right ) >> 1 );
- x = PSET_GetNext( pdsp->ppset[0], av );
- x = CLIP_DSP( x );
- pbf->left = pbf->right = x;
- pbf++;
- }
+ samplexf = (dly->lpdelayline[dly->idelayoutputxf] * (REVERB_XFADE - dly->xfade)) / REVERB_XFADE;
+ delay = ((delay * dly->xfade) / REVERB_XFADE) + samplexf;
+
+ if( ++dly->idelayoutputxf >= dly->cdelaysamplesmax )
+ dly->idelayoutputxf = 0;
+
+ if( --dly->xfade == 0 )
+ dly->idelayoutput = dly->idelayoutputxf;
}
- return;
- }
- // crossfading to current preset from previous preset
- if( bcrossfading )
- {
- int r = -1;
- int fl, flp;
- int xf_fl;
-
- while( count-- )
+
+ if( delay )
{
- av = ( ( pbf->left + pbf->right ) >> 1 );
-
- // get current preset values
- fl = PSET_GetNext( pdsp->ppset[0], av );
-
- // get previous preset values
- flp = PSET_GetNext( pdsp->ppsetprev[0], av );
-
- fl = CLIP_DSP(fl);
- flp = CLIP_DSP(flp);
-
- // get current ramp value
- r = RMP_GetNext( &pdsp->xramp );
-
- // crossfade from previous to current preset
- xf_fl = XFADE( fl, flp, r ); // crossfade front left previous to front left
-
- pbf->left = xf_fl; // crossfaded front left, duplicate in right channel
- pbf->right = xf_fl;
-
- pbf++;
+ val = vlr + ( ( dly->delayfeedback * delay ) >> 8 );
+ val = CLIP( val );
+ }
+ else
+ val = vlr;
+ if( dly->lp )
+ {
+ valt = (dly->lp0 + val) >> 1;
+ dly->lp0 = val;
}
+ else
+ valt = val;
+ voutm = dly->lpdelayline[dly->idelayinput] = valt;
+ }
+ else
+ {
+ voutm = dly->lpdelayline[dly->idelayinput] = 0;
+ dly->lp0 = 0;
}
+
+ DLY_MovePointer( dly );
+
+ return voutm;
+
}
-// Helper: called only from DSP_Process
-// DSP_Process stereo in to stereo out (if more than 2 procs, ignore them)
-_inline void DSP_ProcessStereoToStereo( dsp_t *pdsp, portable_samplepair_t *pbfront, int sampleCount, qboolean bcrossfading )
+/*
+===========
+RVB_DoReverb
+
+Do reverberation processing
+===========
+*/
+void RVB_DoReverb( int count )
{
- portable_samplepair_t *pbf = pbfront; // pointer to buffer of front stereo samples to process
- int count = sampleCount;
- int fl, fr;
+ dly_t *const dly1 = &rgsxdly[REVERBPOS];
+ dly_t *const dly2 = &rgsxdly[REVERBPOS+1];
+ portable_samplepair_t *paint = paintto;
+ int vlr, voutm;
+
+ if( !dly1->lpdelayline )
+ return;
- if( !bcrossfading )
+ for( ; count; count--, paint++ )
{
+ vlr = ( paint->left + paint->right ) >> 1;
- if( FBatchPreset( pdsp->ppset[0] ) && FBatchPreset( pdsp->ppset[1] ))
- {
- // process left & right
- PSET_GetNextN( pdsp->ppset[0], pbfront, sampleCount, OP_LEFT );
- PSET_GetNextN( pdsp->ppset[1], pbfront, sampleCount, OP_RIGHT );
- }
- else
- {
- // left -> left fx, right -> right fx
- while( count-- )
- {
- fl = PSET_GetNext( pdsp->ppset[0], pbf->left );
- fr = PSET_GetNext( pdsp->ppset[1], pbf->right );
+ voutm = RVB_DoReverbForOneDly( dly1, vlr, paint );
+ voutm += RVB_DoReverbForOneDly( dly2, vlr, paint );
- fl = CLIP_DSP( fl );
- fr = CLIP_DSP( fr );
+ voutm = (11 * voutm) >> 6;
- pbf->left = fl;
- pbf->right = fr;
- pbf++;
- }
- }
- return;
+ paint->left = CLIP( paint->left + voutm );
+ paint->right = CLIP( paint->right + voutm );
}
+}
+
+/*
+===========
+RVB_DoAMod
+
+Do amplification modulation processing
+===========
+*/
+void RVB_DoAMod( int count )
+{
+ portable_samplepair_t *paint = paintto;
+
+ if( !sxmod_lowpass->integer && !sxmod_mod->integer )
+ return;
- // crossfading to current preset from previous preset
- if( bcrossfading )
+ for( ; count; count--, paint++ )
{
- int r, flp, frp;
- int xf_fl, xf_fr;
+ portable_samplepair_t res = *paint;
- while( count-- )
+ if( sxmod_lowpass->value )
{
- // get current preset values
- fl = PSET_GetNext( pdsp->ppset[0], pbf->left );
- fr = PSET_GetNext( pdsp->ppset[1], pbf->right );
-
- // get previous preset values
- flp = PSET_GetNext( pdsp->ppsetprev[0], pbf->left );
- frp = PSET_GetNext( pdsp->ppsetprev[1], pbf->right );
-
- // get current ramp value
- r = RMP_GetNext( &pdsp->xramp );
-
- fl = CLIP_DSP( fl );
- fr = CLIP_DSP( fr );
- flp = CLIP_DSP( flp );
- frp = CLIP_DSP( frp );
-
- // crossfade from previous to current preset
- xf_fl = XFADE( fl, flp, r ); // crossfade front left previous to front left
- xf_fr = XFADE( fr, frp, r );
-
- pbf->left = xf_fl; // crossfaded front left
- pbf->right = xf_fr;
-
- pbf++;
- }
- }
-}
+ res.left = rgsxlp[0] + rgsxlp[1] + rgsxlp[2] + rgsxlp[3] + rgsxlp[4] + res.left;
+ res.right = rgsxlp[5] + rgsxlp[6] + rgsxlp[7] + rgsxlp[8] + rgsxlp[9] + res.right;
-void DSP_ClearState( void )
-{
- if( !dsp_room ) return; // not init
+ res.left >>= 2;
+ res.right >>= 2;
- Cvar_SetFloat( "dsp_room", 0.0f );
- Cvar_SetFloat( "room_type", 0.0f );
+ rgsxlp[0] = rgsxlp[1];
+ rgsxlp[1] = rgsxlp[2];
+ rgsxlp[2] = rgsxlp[3];
+ rgsxlp[3] = rgsxlp[4];
+ rgsxlp[4] = paint->left;
- CheckNewDspPresets();
+ rgsxlp[5] = rgsxlp[6];
+ rgsxlp[6] = rgsxlp[7];
+ rgsxlp[7] = rgsxlp[8];
+ rgsxlp[8] = rgsxlp[9];
+ rgsxlp[9] = paint->right;
+ }
- // don't crossfade
- dsps[0].xramp.fhitend = true;
-}
+ if( sxmod_mod->integer )
+ {
+ if( --sxmod1cur < 0 )
+ sxmod1cur = sxmod1;
-// Main DSP processing routine:
-// process samples in buffers using pdsp processor
-// continue crossfade between 2 dsp processors if crossfading on switch
-// pfront - front stereo buffer to process
-// prear - rear stereo buffer to process (may be NULL)
-// sampleCount - number of samples in pbuf to process
-// This routine also maps the # processing channels in the pdsp to the number of channels
-// supplied. ie: if the pdsp has 4 channels and pbfront and pbrear are both non-null, the channels
-// map 1:1 through the processors.
+ if( !sxmod1 )
+ sxamodlt = Com_RandomLong( 32, 255 );
-void DSP_Process( int idsp, portable_samplepair_t *pbfront, int sampleCount )
-{
- qboolean bcrossfading;
- int cprocs; // output cannels (1, 2 or 4)
- dsp_t *pdsp;
+ if( --sxmod2cur < 0 )
+ sxmod2cur = sxmod2;
- if( idsp < 0 || idsp >= CDSPS )
- return;
+ if( !sxmod2 )
+ sxamodrt = Com_RandomLong( 32, 255 );
- ASSERT ( idsp < CDSPS ); // make sure idsp is valid
+ res.left = (sxamodl * res.left) >> 8;
+ res.right = (sxamodr * res.right) >> 8;
- pdsp = &dsps[idsp];
+ if( sxamodl < sxamodlt )
+ sxamodl++;
+ else if( sxamodl > sxamodlt )
+ sxamodl--;
- // if current and previous preset 0, return - preset 0 is 'off'
- if( !pdsp->ipset && !pdsp->ipsetprev )
- return;
+ if( sxamodr < sxamodrt )
+ sxamodr++;
+ else if( sxamodr > sxamodrt )
+ sxamodr--;
+ }
+
+ paint->left = CLIP(res.left);
+ paint->right = CLIP(res.right);
+ }
+}
- ASSERT( pbfront );
+/*
+===========
+DSP_Process
- // return right away if fx processing is turned off
+(xash dsp interface)
+===========
+*/
+void DSP_Process( int idsp, portable_samplepair_t *pbfront, int sampleCount )
+{
if( dsp_off->integer )
return;
- if( sampleCount < 0 )
+ // HACKHACK: don't process while in menu
+ if( cls.key_dest == key_menu || !sampleCount )
return;
-
- bcrossfading = !RMP_HitEnd( &pdsp->xramp );
- // if not crossfading, and previous channel is not null, free previous
- if( !bcrossfading ) DSP_FreePrevPreset( pdsp );
+ // preset is already installed by CheckNewDspPresets
+ paintto = pbfront;
- cprocs = pdsp->cchan;
-
- // NOTE: when mixing between different channel sizes,
- // always AVERAGE down to fewer channels and DUPLICATE up more channels.
- // The following routines always process cchan_in channels.
- // ie: QuadToMono still updates 4 values in buffer
+ RVB_DoAMod( sampleCount );
+ RVB_DoReverb( sampleCount );
+ DLY_DoDelay( sampleCount );
+ DLY_DoStereoDelay( sampleCount );
+}
- // DSP_Process stereo in to mono out (ie: left and right are averaged)
- if( cprocs == 1 )
- {
- DSP_ProcessStereoToMono( pdsp, pbfront, sampleCount, bcrossfading );
- return;
- }
+/*
+===========
+DSP_ClearState
- // DSP_Process stereo in to stereo out (if more than 2 procs, ignore them)
- if( cprocs >= 2 )
- {
- DSP_ProcessStereoToStereo( pdsp, pbfront, sampleCount, bcrossfading );
- return;
- }
+(xash dsp interface)
+===========
+*/
+void DSP_ClearState( void )
+{
+ Cvar_SetFloat( "room_type", 0.0f );
+ SX_ReloadRoomFX();
}
-// DSP helpers
+/*
+===========
+AllocDsps
-// free all dsp processors
-void FreeDsps( void )
+(xash dsp interface)
+===========
+*/
+qboolean AllocDsps( void )
{
- DSP_Free( idsp_room );
- idsp_room = 0;
-
- DSP_FreeAll();
+ SX_Init();
+
+ return 1;
}
-// alloc dsp processors
-qboolean AllocDsps( void )
+/*
+===========
+FreeDsps
+
+(xash dsp interface)
+===========
+*/
+void FreeDsps( void )
{
- DSP_InitAll();
+ SX_Free();
+}
- idsp_room = -1.0;
+/*
+===========
+CheckNewDspPresets
+
+(xash dsp interface)
+===========
+*/
+void CheckNewDspPresets( void )
+{
+ if( dsp_off->value != 0.0f )
+ return;
- // initialize DSP cvars
- dsp_room = Cvar_Get( "dsp_room", "0", 0, "room dsp preset - sounds more distant from player (1ch)" );
- dsp_room_type = Cvar_Get( "room_type", "0", 0, "duplicate for dsp_room cvar for backward compatibility" );
- dsp_stereo = Cvar_Get( "dsp_stereo", "0", 0, "set to 1 for true stereo processing. 2x perf hits" );
+ if( s_listener.waterlevel > 2 )
+ idsp_room = roomwater_type->value;
+ else idsp_room = room_type->value;
- // alloc dsp room channel (mono, stereo if dsp_stereo is 1)
+ if( hisound->modified )
+ {
+ sxhires = hisound->integer;
+ hisound->modified = false;
+ }
- // dsp room is mono, 300ms fade time
- idsp_room = DSP_Alloc( dsp_room->integer, 300, dsp_stereo->integer * 2 );
+ if( idsp_room == room_typeprev && idsp_room == 0 )
+ return;
- // init prev values
- ipset_room_prev = dsp_room->integer;
- ipset_room_typeprev = dsp_room_type->integer;
+ if( idsp_room > MAX_ROOM_TYPES )
+ return;
- if( idsp_room < 0 )
+ if( idsp_room != room_typeprev )
{
- MsgDev( D_WARN, "DSP processor failed to initialize! \n" );
+ const sx_preset_t *cur = rgsxpre + idsp_room;
- FreeDsps();
- return false;
+ Cvar_SetFloat( "room_lp", cur->room_lp );
+ Cvar_SetFloat( "room_mod", cur->room_mod );
+ Cvar_SetFloat( "room_size", cur->room_size );
+ Cvar_SetFloat( "room_refl", cur->room_refl );
+ Cvar_SetFloat( "room_rvblp", cur->room_rvblp );
+ Cvar_SetFloat( "room_delay", cur->room_delay );
+ Cvar_SetFloat( "room_feedback", cur->room_feedback );
+ Cvar_SetFloat( "room_dlylp", cur->room_dlylp );
+ Cvar_SetFloat( "room_left", cur->room_left );
}
- return true;
+
+ room_typeprev = idsp_room;
+
+ RVB_CheckNewReverbVal( );
+ DLY_CheckNewDelayVal( );
+ DLY_CheckNewStereoDelayVal();
}
+/*
+===========
+DSP_GetGain
-// Helper to check for change in preset of any of 4 processors
-// if switching to a new preset, alloc new preset, simulate both presets in DSP_Process & xfade,
-void CheckNewDspPresets( void )
+(xash dsp interface)
+===========
+*/
+float DSP_GetGain( int idsp )
{
- int iroomtype = dsp_room_type->integer;
- int iroom;
+ return 1.0f;
+}
- if( dsp_off->integer )
- return;
+void SX_Profiling_f( void )
+{
+ portable_samplepair_t testbuffer[512];
+ int i, calls = 10000;
+ double start, end;
+ float oldroom = room_type->value;
- if( s_listener.waterlevel > 2 )
- iroom = 15;
- else if( s_listener.inmenu )
- iroom = 0;
- else iroom = dsp_room->integer;
+ for( i = 0; i < 512; i++ )
+ {
+ testbuffer[i].left = Com_RandomLong( 0, 3000 );
+ testbuffer[i].right = Com_RandomLong( 0, 3000 );
+ }
- // legacy code support for "room_type" Cvar
- if( iroomtype != ipset_room_typeprev )
+ if( Cmd_Argc() > 1 )
{
- // force dsp_room = room_type
- ipset_room_typeprev = iroomtype;
- Cvar_SetFloat( "dsp_room", iroomtype );
+ Cvar_SetFloat( "room_type", Q_atof( Cmd_Argv( 1 )));
+ SX_ReloadRoomFX();
+ CheckNewDspPresets(); // we just need idsp_room immediately, for message below
}
- if( iroom != ipset_room_prev )
+ MsgDev( D_INFO, "Profiling 10000 calls to DSP. Sample count is 512, room_type is %i\n", idsp_room );
+
+ start = Sys_DoubleTime();
+ for( ; calls; calls-- )
{
- DSP_SetPreset( idsp_room, iroom );
- ipset_room_prev = iroom;
+ DSP_Process( idsp_room, testbuffer, 512 );
+ }
+ end = Sys_DoubleTime();
- // force room_type = dsp_room
- Cvar_SetFloat( "room_type", iroom );
- ipset_room_typeprev = iroom;
+ MsgDev( D_INFO, "----------\nTook %g seconds.\n", end - start );
+
+ if( Cmd_Argc() > 1 )
+ {
+ Cvar_SetFloat( "room_type", oldroom );
+ SX_ReloadRoomFX();
+ CheckNewDspPresets();
}
}
\ No newline at end of file
diff --git b/engine/client/s_load.c a/engine/client/s_load.c
index e9e90ac..0a0ed45 100644
--- b/engine/client/s_load.c
+++ a/engine/client/s_load.c
@@ -68,8 +68,8 @@ void S_SoundList_f( void )
// return true if char 'c' is one of 1st 2 characters in pch
qboolean S_TestSoundChar( const char *pch, char c )
{
- int i;
char *pcht = (char *)pch;
+ int i;
if( !pch || !*pch )
return false;
@@ -141,15 +141,11 @@ wavdata_t *S_LoadSound( sfx_t *sfx )
if( sc->rate < SOUND_11k ) // some bad sounds
Sound_Process( &sc, SOUND_11k, sc->width, SOUND_RESAMPLE );
-#if SOUND_DMA_SPEED > SOUND_11k
else if( sc->rate > SOUND_11k && sc->rate < SOUND_22k ) // some bad sounds
Sound_Process( &sc, SOUND_22k, sc->width, SOUND_RESAMPLE );
-#endif
-
-#if SOUND_DMA_SPEED > SOUND_32k
else if( sc->rate > SOUND_22k && sc->rate <= SOUND_32k ) // some bad sounds
Sound_Process( &sc, SOUND_44k, sc->width, SOUND_RESAMPLE );
-#endif
+
sfx->cache = sc;
return sfx->cache;
@@ -201,9 +197,7 @@ sfx_t *S_FindName( const char *pname, int *pfInCache )
// find a free sfx slot spot
for( i = 0, sfx = s_knownSfx; i < s_numSfx; i++, sfx++)
- {
if( !sfx->name[0] ) break; // free spot
- }
if( i == s_numSfx )
{
diff --git b/engine/client/s_main.c a/engine/client/s_main.c
index ab9386c..85c71d8 100644
--- b/engine/client/s_main.c
+++ a/engine/client/s_main.c
@@ -20,7 +20,6 @@ GNU General Public License for more details.
#include "ref_params.h"
#include "pm_local.h"
-#define MAX_DUPLICATED_CHANNELS 4 // threshold for identical static channels (probably error)
#define SND_CLIP_DISTANCE (float)(GI->soundclip_dist)
dma_t dma;
@@ -28,6 +27,7 @@ byte *sndpool;
static soundfade_t soundfade;
channel_t channels[MAX_CHANNELS];
sound_t ambient_sfx[NUM_AMBIENTS];
+rawchan_t *raw_channels[MAX_RAW_CHANNELS];
qboolean snd_ambient = false;
listener_t s_listener;
int total_channels;
@@ -35,6 +35,7 @@ int soundtime; // sample PAIRS
int paintedtime; // sample PAIRS
static int trace_count = 0;
static int last_trace_chan = 0;
+static byte s_fatphs[MAX_MAP_LEAFS/8]; // PHS array for snd module
convar_t *s_volume;
convar_t *s_musicvolume;
@@ -51,7 +52,6 @@ convar_t *snd_gain_max;
convar_t *snd_gain_min;
convar_t *s_refdist;
convar_t *s_refdb;
-convar_t *dsp_off; // set to 1 to disable all dsp processing
convar_t *s_cull; // cull sounds by geometry
convar_t *s_test; // cvar for testing new effects
convar_t *s_phs;
@@ -356,21 +356,8 @@ already playing.
channel_t *SND_PickStaticChannel( int entnum, sfx_t *sfx, const vec3_t pos )
{
channel_t *ch = NULL;
- int i, dupe = 0;
-
-#if 1
- // TODO: remove this code when predicting is will be done
- // check for duplicate sounds
- for( i = 0; i < total_channels; i++ )
- {
- if( channels[i].sfx == sfx && VectorCompare( channels[i].origin, pos ))
- dupe++;
- }
+ int i;
- // check for duplicated static channels (same origin and same sfx)
- if( dupe > MAX_DUPLICATED_CHANNELS )
- return NULL;
-#endif
// check for replacement sound, or find the best one to replace
for( i = MAX_DYNAMIC_CHANNELS; i < total_channels; i++ )
{
@@ -703,27 +690,26 @@ float SND_GetGain( channel_t *ch, qboolean fplayersound, qboolean flooping, floa
return gain;
}
+/*
+=================
+SND_CheckPHS
+
+using a 'fat' radius
+=================
+*/
qboolean SND_CheckPHS( channel_t *ch )
{
mleaf_t *leaf;
- int leafnum;
- byte *mask = NULL;
- // cull sounds by PHS
- if( !s_phs->integer )
- return true;
+ if( !ch->dist_mult || !s_phs->integer )
+ return true; // no attenuation
leaf = Mod_PointInLeaf( ch->origin, cl.worldmodel->nodes );
- mask = Mod_LeafPHS( leaf, cl.worldmodel );
- if( mask )
- {
- leafnum = Mod_PointLeafnum( s_listener.origin ) - 1;
+ if( CHECKVISBIT( s_listener.pasbytes, leaf->cluster ))
+ return true;
- if( leafnum != -1 && (!(mask[leafnum>>3] & (1<<( leafnum & 7 )))))
- return false;
- }
- return true;
+ return false;
}
/*
@@ -1369,7 +1355,7 @@ void S_UpdateAmbientSounds( void )
if( !chan->sfx ) continue;
vol = s_ambient_level->value * leaf->ambient_sound_level[ambient_channel];
- if( vol < 8 ) vol = 0;
+ if( vol < 0 ) vol = 0;
// don't adjust volume too fast
if( chan->master_vol < vol )
@@ -1388,13 +1374,369 @@ void S_UpdateAmbientSounds( void )
}
/*
+=============================================================================
+
+ SOUND STREAM RAW SAMPLES
+
+=============================================================================
+*/
+/*
+===================
+S_FindRawChannel
+===================
+*/
+rawchan_t *S_FindRawChannel( int entnum, qboolean create )
+{
+ int i, free;
+ int best, best_time;
+ size_t raw_samples = 0;
+ rawchan_t *ch;
+
+ if( !entnum ) return NULL; // world is unused
+
+ // check for replacement sound, or find the best one to replace
+ best_time = 0x7fffffff;
+ best = free = -1;
+
+ for( i = 0; i < MAX_RAW_CHANNELS; i++ )
+ {
+ ch = raw_channels[i];
+
+ if( free < 0 && !ch )
+ {
+ free = i;
+ }
+ else if( ch )
+ {
+ int time;
+
+ // exact match
+ if( ch->entnum == entnum )
+ return ch;
+
+ time = ch->s_rawend - paintedtime;
+ if( time < best_time )
+ {
+ best = i;
+ best_time = time;
+ }
+ }
+ }
+
+ if( !create ) return NULL;
+
+ if( free >= 0 ) best = free;
+ if( best < 0 ) return NULL; // no free slots
+
+ if( !raw_channels[best] )
+ {
+ raw_samples = MAX_RAW_SAMPLES;
+ raw_channels[best] = Mem_Alloc( sndpool, sizeof( *ch ) + sizeof( portable_samplepair_t ) * ( raw_samples - 1 ));
+ }
+
+ ch = raw_channels[best];
+ ch->max_samples = raw_samples;
+ ch->entnum = entnum;
+ ch->s_rawend = 0;
+
+ return ch;
+}
+
+/*
+===================
+S_RawSamplesStereo
+===================
+*/
+static uint S_RawSamplesStereo( portable_samplepair_t *rawsamples, uint rawend, uint max_samples, uint samples, uint rate, word width, word channels, const byte *data )
+{
+ uint fracstep, samplefrac;
+ uint src, dst;
+
+ if( rawend < paintedtime )
+ rawend = paintedtime;
+
+ fracstep = ((double) rate / (double)SOUND_DMA_SPEED) * (double)(1 << S_RAW_SAMPLES_PRECISION_BITS);
+ samplefrac = 0;
+
+ if( width == 2 )
+ {
+ const short *in = (const short *)data;
+
+ if( channels == 2 )
+ {
+ for( src = 0; src < samples; samplefrac += fracstep, src = ( samplefrac >> S_RAW_SAMPLES_PRECISION_BITS ))
+ {
+ dst = rawend++ & ( max_samples - 1 );
+ rawsamples[dst].left = in[src*2+0];
+ rawsamples[dst].right = in[src*2+1];
+ }
+ }
+ else
+ {
+ for( src = 0; src < samples; samplefrac += fracstep, src = ( samplefrac >> S_RAW_SAMPLES_PRECISION_BITS ))
+ {
+ dst = rawend++ & ( max_samples - 1 );
+ rawsamples[dst].left = in[src];
+ rawsamples[dst].right = in[src];
+ }
+ }
+ }
+ else
+ {
+ if( channels == 2 )
+ {
+ const char *in = (const char *)data;
+
+ for( src = 0; src < samples; samplefrac += fracstep, src = ( samplefrac >> S_RAW_SAMPLES_PRECISION_BITS ))
+ {
+ dst = rawend++ & ( max_samples - 1 );
+ rawsamples[dst].left = in[src*2+0] << 8;
+ rawsamples[dst].right = in[src*2+1] << 8;
+ }
+ }
+ else
+ {
+ for( src = 0; src < samples; samplefrac += fracstep, src = ( samplefrac >> S_RAW_SAMPLES_PRECISION_BITS ))
+ {
+ dst = rawend++ & ( max_samples - 1 );
+ rawsamples[dst].left = ( data[src] - 128 ) << 8;
+ rawsamples[dst].right = ( data[src] - 128 ) << 8;
+ }
+ }
+ }
+
+ return rawend;
+}
+
+/*
+===================
+S_RawEntSamples
+===================
+*/
+static void S_RawEntSamples( int entnum, uint samples, uint rate, word width, word channels, const byte *data, int snd_vol )
+{
+ rawchan_t *ch;
+
+ if( snd_vol < 0 )
+ snd_vol = 0;
+
+ if( !( ch = S_FindRawChannel( entnum, true )))
+ return;
+
+ ch->master_vol = snd_vol;
+ ch->dist_mult = (ATTN_NONE / SND_CLIP_DISTANCE);
+ ch->s_rawend = S_RawSamplesStereo( ch->rawsamples, ch->s_rawend, ch->max_samples, samples, rate, width, channels, data );
+ ch->leftvol = ch->rightvol = snd_vol;
+}
+
+/*
+===================
+S_RawSamples
+===================
+*/
+void S_RawSamples( uint samples, uint rate, word width, word channels, const byte *data, int entnum )
+{
+ int snd_vol;
+
+ if( entnum < 0 ) snd_vol = 128; // bg track or movie track
+ if( snd_vol < 0 ) snd_vol = 0; // fixup negative values
+
+ S_RawEntSamples( entnum, samples, rate, width, channels, data, snd_vol );
+}
+
+/*
+===================
+S_PositionedRawSamples
+===================
+*/
+static void S_PositionedRawSamples( int entnum, float fvol, float attn, uint samples, uint rate, word width, word channels, const byte *data )
+{
+ rawchan_t *ch;
+
+ if( entnum < 0 || entnum >= GI->max_edicts )
+ return;
+
+ if( !( ch = S_FindRawChannel( entnum, true )))
+ return;
+
+ ch->master_vol = bound( 0, fvol * 255, 255 );
+ ch->dist_mult = (attn / SND_CLIP_DISTANCE);
+ ch->s_rawend = S_RawSamplesStereo( ch->rawsamples, ch->s_rawend, ch->max_samples, samples, rate, width, channels, data );
+}
+
+/*
+===================
+S_GetRawSamplesLength
+===================
+*/
+uint S_GetRawSamplesLength( int entnum )
+{
+ rawchan_t *ch;
+
+ if( !( ch = S_FindRawChannel( entnum, false )))
+ return 0;
+
+ return ch->s_rawend <= paintedtime ? 0 : (float)(ch->s_rawend - paintedtime) * DMA_MSEC_PER_SAMPLE;
+}
+
+/*
+===================
+S_ClearRawChannel
+===================
+*/
+void S_ClearRawChannel( int entnum )
+{
+ rawchan_t *ch;
+
+ if( !( ch = S_FindRawChannel( entnum, false )))
+ return;
+
+ ch->s_rawend = 0;
+}
+
+/*
+===================
+S_FreeIdleRawChannels
+
+Free raw channel that have been idling for too long.
+===================
+*/
+static void S_FreeIdleRawChannels( void )
+{
+ int i;
+
+ for( i = 0; i < MAX_RAW_CHANNELS; i++ )
+ {
+ rawchan_t *ch = raw_channels[i];
+
+ if( !ch ) continue;
+
+ if( ch->s_rawend >= paintedtime )
+ continue;
+
+ if(( paintedtime - ch->s_rawend ) / SOUND_DMA_SPEED >= S_RAW_SOUND_IDLE_SEC )
+ {
+ raw_channels[i] = NULL;
+ Mem_Free( ch );
+ }
+ }
+}
+
+/*
+===================
+S_ClearRawChannels
+===================
+*/
+static void S_ClearRawChannels( void )
+{
+ int i;
+
+ for( i = 0; i < MAX_RAW_CHANNELS; i++ )
+ {
+ rawchan_t *ch = raw_channels[i];
+
+ if( !ch ) continue;
+ ch->s_rawend = 0;
+ }
+}
+
+/*
+===================
+S_SpatializeRawChannels
+===================
+*/
+static void S_SpatializeRawChannels( void )
+{
+ int i;
+
+ for( i = 0; i < MAX_RAW_CHANNELS; i++ )
+ {
+ rawchan_t *ch = raw_channels[i];
+ vec3_t source_vec;
+ float dist, dot;
+
+ if( !ch ) continue;
+
+ if( ch->s_rawend < paintedtime )
+ {
+ ch->leftvol = ch->rightvol = 0;
+ continue;
+ }
+
+ // spatialization
+ if( !S_IsClient( ch->entnum ) && ch->dist_mult && ch->entnum >= 0 && ch->entnum < GI->max_edicts )
+ {
+ if( !CL_GetEntitySpatialization( ch->entnum, ch->origin, &ch->radius ))
+ {
+ // origin is null and entity not exist on client
+ ch->leftvol = ch->rightvol = 0;
+ }
+ else
+ {
+ VectorSubtract( ch->origin, s_listener.origin, source_vec );
+
+ // normalize source_vec and get distance from listener to source
+ dist = VectorNormalizeLength( source_vec );
+ dot = DotProduct( s_listener.right, source_vec );
+
+ // for sounds with a radius, spatialize left/right evenly within the radius
+ if( ch->radius > 0 && dist < ch->radius )
+ {
+ float interval = ch->radius * 0.5f;
+ float blend = dist - interval;
+
+ if( blend < 0 ) blend = 0;
+ blend /= interval;
+
+ // blend is 0.0 - 1.0, from 50% radius -> 100% radius
+ // at radius * 0.5, dot is 0 (ie: sound centered left/right)
+ // at radius dot == dot
+ dot *= blend;
+ }
+
+ // don't pan sounds with no attenuation
+ if( ch->dist_mult <= 0.0f ) dot = 0.0f;
+
+ // fill out channel volumes for single location
+ S_SpatializeChannel( &ch->leftvol, &ch->rightvol, ch->master_vol, 1.0f, dot, dist * ch->dist_mult );
+ }
+ }
+ else
+ {
+ ch->leftvol = ch->rightvol = ch->master_vol;
+ }
+ }
+}
+
+/*
+===================
+S_FreeRawChannels
+===================
+*/
+static void S_FreeRawChannels( void )
+{
+ int i;
+
+ // free raw samples
+ for( i = 0; i < MAX_RAW_CHANNELS; i++ )
+ {
+ if( raw_channels[i] )
+ Mem_Free( raw_channels[i] );
+ }
+
+ memset( raw_channels, 0, sizeof( raw_channels ));
+}
+
+//=============================================================================
+
+/*
==================
S_ClearBuffer
==================
*/
void S_ClearBuffer( void )
{
- s_rawend = 0;
+ S_ClearRawChannels();
SNDDMA_BeginPainting ();
if( dma.buffer ) memset( dma.buffer, 0, dma.samples * 2 );
@@ -1520,6 +1862,9 @@ void S_RenderFrame( ref_params_t *fd )
// update any client side sound fade
S_UpdateSoundFade();
+ // release raw-channels that no longer used more than 10 secs
+ S_FreeIdleRawChannels();
+
s_listener.entnum = fd->viewentity; // can be camera entity too
s_listener.frametime = fd->frametime;
s_listener.waterlevel = fd->waterlevel;
@@ -1531,6 +1876,9 @@ void S_RenderFrame( ref_params_t *fd )
VectorCopy( fd->simvel, s_listener.velocity );
AngleVectors( fd->viewangles, s_listener.forward, s_listener.right, s_listener.up );
+ if( cl.worldmodel != NULL )
+ Mod_FatPVS( s_listener.origin, FATPHS_RADIUS, s_listener.pasbytes, world.visbytes, false, !s_phs->integer );
+
// update general area ambient sound sources
S_UpdateAmbientSounds();
@@ -1585,6 +1933,8 @@ void S_RenderFrame( ref_params_t *fd )
}
}
+ S_SpatializeRawChannels();
+
// debugging output
if( s_show->value )
{
@@ -1605,11 +1955,16 @@ void S_RenderFrame( ref_params_t *fd )
}
// to differentiate modes
- if( s_cull->integer ) VectorSet( info.color, 0.0f, 1.0f, 0.0f );
+ if( s_cull->integer && s_phs->integer )
+ VectorSet( info.color, 0.0f, 1.0f, 0.0f );
+ else if( s_phs->integer )
+ VectorSet( info.color, 1.0f, 1.0f, 0.0f );
+ else if( s_cull->integer )
+ VectorSet( info.color, 1.0f, 0.0f, 0.0f );
else VectorSet( info.color, 1.0f, 1.0f, 1.0f );
info.index = 0;
- Con_NXPrintf( &info, "----(%i)---- painted: %i\n", total - 1, paintedtime );
+ Con_NXPrintf( &info, "room_type: %i ----(%i)---- painted: %i\n", idsp_room, total - 1, paintedtime );
}
S_StreamBackgroundTrack ();
@@ -1773,9 +2128,8 @@ qboolean S_Init( void )
s_mixahead = Cvar_Get( "_snd_mixahead", "0.12", 0, "how much sound to mix ahead of time" );
s_show = Cvar_Get( "s_show", "0", CVAR_ARCHIVE, "show playing sounds" );
s_lerping = Cvar_Get( "s_lerping", "0", CVAR_ARCHIVE, "apply interpolation to sound output" );
- dsp_off = Cvar_Get( "dsp_off", "0", CVAR_ARCHIVE, "set to 1 to disable all dsp processing" );
- s_ambient_level = Cvar_Get( "ambient_level", "0.3", 0, "volume of environment noises (water and wind)" );
- s_ambient_fade = Cvar_Get( "ambient_fade", "100", 0, "rate of volume fading when client is moving" );
+ s_ambient_level = Cvar_Get( "ambient_level", "0.3", CVAR_ARCHIVE, "volume of environment noises (water and wind)" );
+ s_ambient_fade = Cvar_Get( "ambient_fade", "1000", CVAR_ARCHIVE, "rate of volume fading when client is moving" );
s_combine_sounds = Cvar_Get( "s_combine_channels", "1", CVAR_ARCHIVE, "combine channels with same sounds" );
snd_foliage_db_loss = Cvar_Get( "snd_foliage_db_loss", "4", 0, "foliage loss factor" );
snd_gain_max = Cvar_Get( "snd_gain_max", "1", 0, "gain maximal threshold" );
@@ -1836,10 +2190,11 @@ void S_Shutdown( void )
Cmd_RemoveCommand( "s_info" );
Cmd_RemoveCommand( "+voicerecord" );
Cmd_RemoveCommand( "-voicerecord" );
- Cmd_RemoveCommand( "spk" );
Cmd_RemoveCommand( "speak" );
+ Cmd_RemoveCommand( "spk" );
S_StopAllSounds ();
+ S_FreeRawChannels ();
S_FreeSounds ();
VOX_Shutdown ();
FreeDsps ();
diff --git b/engine/client/s_mix.c a/engine/client/s_mix.c
index 9a4a899..a902fcc 100644
--- b/engine/client/s_mix.c
+++ a/engine/client/s_mix.c
@@ -216,8 +216,8 @@ CHANNEL MIXING
*/
void S_PaintMonoFrom8( portable_samplepair_t *pbuf, int *volume, byte *pData, int outCount )
{
- int i, data;
int *lscale, *rscale;
+ int i, data;
lscale = snd_scaletable[volume[0] >> SND_SCALE_SHIFT];
rscale = snd_scaletable[volume[1] >> SND_SCALE_SHIFT];
@@ -252,8 +252,8 @@ void S_PaintStereoFrom8( portable_samplepair_t *pbuf, int *volume, byte *pData,
void S_PaintMonoFrom16( portable_samplepair_t *pbuf, int *volume, short *pData, int outCount )
{
- int i, data;
int left, right;
+ int i, data;
for( i = 0; i < outCount; i++ )
{
@@ -473,20 +473,13 @@ int S_MixDataToDevice( channel_t *pChannel, int sampleCount, int outputRate, int
for( i = 0; i < CPAINTBUFFERS; i++ )
{
- if( paintbuffers[i].factive )
- {
- // mix chan into all active paintbuffers
- MIX_SetCurrentPaintbuffer( i );
-
- S_MixChannel(
- pChannel, // Channel.
- pData, // Input buffer.
- outputOffset, // Output position.
- FIX_FLOAT( sampleFraction ), // Iterators.
- FIX_FLOAT( rate ),
- outputSampleCount
- );
- }
+ if( !paintbuffers[i].factive )
+ continue;
+
+ // mix chan into all active paintbuffers
+ MIX_SetCurrentPaintbuffer( i );
+
+ S_MixChannel( pChannel, pData, outputOffset, FIX_FLOAT( sampleFraction ), FIX_FLOAT( rate ), outputSampleCount );
}
MIX_SetCurrentPaintbuffer( j );
@@ -692,7 +685,7 @@ void S_Interpolate2xCubic( portable_samplepair_t *pbuffer, portable_samplepair_t
{
// get source sample pointer
psamp0 = S_GetNextpFilter( i-1, pbuffer, pfiltermem );
- psamp1 = S_GetNextpFilter( i, pbuffer, pfiltermem );
+ psamp1 = S_GetNextpFilter( i+0, pbuffer, pfiltermem );
psamp2 = S_GetNextpFilter( i+1, pbuffer, pfiltermem );
psamp3 = S_GetNextpFilter( i+2, pbuffer, pfiltermem );
@@ -778,9 +771,9 @@ void S_Interpolate2xLinear( portable_samplepair_t *pbuffer, portable_samplepair_
// filtertype: FILTERTYPE_NONE, _LINEAR, _CUBIC etc. Must match prevfilter.
void S_MixBufferUpsample2x( int count, portable_samplepair_t *pbuffer, portable_samplepair_t *pfiltermem, int cfltmem, int filtertype )
{
- int i, j;
int upCount = count<<1;
-
+ int i, j;
+
// reverse through buffer, duplicating contents for 'count' samples
for( i = upCount - 1, j = count - 1; j >= 0; i-=2, j-- )
{
@@ -893,57 +886,16 @@ void S_MixUpsample( int sampleCount, int filtertype )
ppaint->ifilter++;
}
-// mix and upsample channels to 44khz 'ipaintbuffer'
-// mix channels matching 'flags' (SOUND_MIX_DRY or SOUND_MIX_WET) into specified paintbuffer
-// upsamples 11khz, 22khz channels to 44khz.
-
-// NOTE: only call this on channels that will be mixed into only 1 paintbuffer
-// and that will not be mixed until the next mix pass! otherwise, MIX_MixChannelsToPaintbuffer
-// will advance any internal pointers on mixed channels; subsequent calls will be at
-// incorrect offset.
-void MIX_MixUpsampleBuffer( int ipaintbuffer, int end, int count )
-{
- int ipaintcur = MIX_GetCurrentPaintbufferIndex(); // save current paintbuffer
-
- // reset paintbuffer upsampling filter index
- MIX_ResetPaintbufferFilterCounter( ipaintbuffer );
-
- // prevent other paintbuffers from being mixed
- MIX_DeactivateAllPaintbuffers();
-
- MIX_ActivatePaintbuffer( ipaintbuffer ); // operates on MIX_MixChannelsToPaintbuffer
- MIX_SetCurrentPaintbuffer( ipaintbuffer ); // operates on MixUpSample
-
- // mix 11khz channels to buffer
- MIX_MixChannelsToPaintbuffer( end, SOUND_11k, SOUND_11k );
-
- // upsample 11khz buffer by 2x
- S_MixUpsample( count / (SOUND_DMA_SPEED / SOUND_11k), FILTERTYPE_LINEAR );
-
- // mix 22khz channels to buffer
- MIX_MixChannelsToPaintbuffer( end, SOUND_22k, SOUND_22k );
-
- // upsample 22khz buffer by 2x
-#if (SOUND_DMA_SPEED > SOUND_22k)
- S_MixUpsample( count / (SOUND_DMA_SPEED / SOUND_22k), FILTERTYPE_LINEAR );
-#endif
- // mix 44khz channels to buffer
- MIX_MixChannelsToPaintbuffer( end, SOUND_44k, SOUND_DMA_SPEED );
-
- MIX_DeactivateAllPaintbuffers();
-
- // restore previous paintbuffer
- MIX_SetCurrentPaintbuffer( ipaintcur );
-}
-
void MIX_MixStreamBuffer( int end )
{
portable_samplepair_t *pbuf;
+ rawchan_t *ch;
pbuf = MIX_GetPFrontFromIPaint( ISTREAMBUFFER );
+ ch = S_FindRawChannel( S_RAW_SOUND_BACKGROUNDTRACK, false );
// clear the paint buffer
- if( s_listener.paused || s_rawend < paintedtime )
+ if( s_listener.paused || !ch || ch->s_rawend < paintedtime )
{
memset( pbuf, 0, (end - paintedtime) * sizeof( portable_samplepair_t ));
}
@@ -952,18 +904,54 @@ void MIX_MixStreamBuffer( int end )
int i, stop;
// copy from the streaming sound source
- stop = (end < s_rawend) ? end : s_rawend;
+ stop = (end < ch->s_rawend) ? end : ch->s_rawend;
for( i = paintedtime; i < stop; i++ )
- pbuf[i - paintedtime] = s_rawsamples[i & (MAX_RAW_SAMPLES - 1)];
-
+ {
+ pbuf[i-paintedtime].left = ( ch->rawsamples[i & ( ch->max_samples - 1 )].left * ch->leftvol ) >> 8;
+ pbuf[i-paintedtime].right = ( ch->rawsamples[i & ( ch->max_samples - 1 )].right * ch->rightvol ) >> 8;
+ }
+
for( ; i < end; i++ )
pbuf[i-paintedtime].left = pbuf[i-paintedtime].right = 0;
}
}
+void MIX_MixRawSamplesBuffer( int end )
+{
+ portable_samplepair_t *pbuf;
+ uint i, j, stop;
+
+ pbuf = MIX_GetCurrentPaintbufferPtr()->pbuf;
+
+ if( s_listener.paused ) return;
+
+ // paint in the raw channels
+ for( i = 0; i < MAX_RAW_CHANNELS; i++ )
+ {
+ // copy from the streaming sound source
+ rawchan_t *ch = raw_channels[i];
+
+ // background track should be mixing into another buffer
+ if( !ch || ch->entnum == S_RAW_SOUND_BACKGROUNDTRACK )
+ continue;
+
+ // not audible
+ if( !ch->leftvol && !ch->rightvol )
+ continue;
+
+ stop = (end < ch->s_rawend) ? end : ch->s_rawend;
+
+ for( j = paintedtime; j < stop; j++ )
+ {
+ pbuf[j-paintedtime].left += ( ch->rawsamples[j & ( ch->max_samples - 1 )].left * ch->leftvol ) >> 8;
+ pbuf[j-paintedtime].right += ( ch->rawsamples[j & ( ch->max_samples - 1 )].right * ch->rightvol ) >> 8;
+ }
+ }
+}
+
// upsample and mix sounds into final 44khz versions of:
-// IROOMBUFFER, IFACINGBUFFER, IFACINGAWAY, IDRYBUFFER
+// IROOMBUFFER, IFACINGBUFFER, IFACINGAWAY
// dsp fx are then applied to these buffers by the caller.
// caller also remixes all into final IPAINTBUFFER output.
void MIX_UpsampleAllPaintbuffers( int end, int count )
@@ -1000,14 +988,17 @@ void MIX_UpsampleAllPaintbuffers( int end, int count )
MIX_MixChannelsToPaintbuffer( end, SOUND_22k, SOUND_22k );
// upsample all 22khz buffers by 2x
-#if (SOUND_DMA_SPEED > SOUND_22k)
// only upsample roombuffer if dsp fx are on KDB: perf
MIX_SetCurrentPaintbuffer( IROOMBUFFER );
S_MixUpsample( count / ( SOUND_DMA_SPEED / SOUND_22k ), FILTERTYPE_LINEAR );
-#endif
+
// mix all 44khz sounds to all active paintbuffers
MIX_MixChannelsToPaintbuffer( end, SOUND_44k, SOUND_DMA_SPEED );
+ // mix raw samples from the video streams
+ MIX_SetCurrentPaintbuffer( IROOMBUFFER );
+ MIX_MixRawSamplesBuffer( end );
+
MIX_DeactivateAllPaintbuffers();
MIX_SetCurrentPaintbuffer( IPAINTBUFFER );
}
diff --git b/engine/client/s_mouth.c a/engine/client/s_mouth.c
index 41b88ea..c242f8b 100644
--- b/engine/client/s_mouth.c
+++ a/engine/client/s_mouth.c
@@ -32,8 +32,8 @@ void SND_InitMouth( int entnum, int entchannel )
if( clientEntity )
{
clientEntity->mouth.mouthopen = 0;
- clientEntity->mouth.sndavg = 0;
clientEntity->mouth.sndcount = 0;
+ clientEntity->mouth.sndavg = 0;
}
}
}
@@ -59,8 +59,8 @@ void SND_MoveMouth8( channel_t *ch, wavdata_t *pSource, int count )
cl_entity_t *clientEntity;
char *pdata = NULL;
mouth_t *pMouth = NULL;
- int savg, data;
int scount, pos = 0;
+ int savg, data;
uint i;
clientEntity = CL_GetEntityByIndex( ch->entnum );
diff --git b/engine/client/s_stream.c a/engine/client/s_stream.c
index 2be6e30..84886d8 100644
--- b/engine/client/s_stream.c
+++ a/engine/client/s_stream.c
@@ -17,11 +17,14 @@ GNU General Public License for more details.
#include "sound.h"
#include "client.h"
-portable_samplepair_t s_rawsamples[MAX_RAW_SAMPLES];
static bg_track_t s_bgTrack;
static musicfade_t musicfade; // controlled by game dlls
-int s_rawend;
+/*
+=================
+S_PrintBackgroundTrackState
+=================
+*/
void S_PrintBackgroundTrackState( void )
{
if( s_bgTrack.current[0] && s_bgTrack.loopName[0] )
@@ -32,18 +35,6 @@ void S_PrintBackgroundTrackState( void )
Msg( "BackgroundTrack: %s [loop]\n", s_bgTrack.loopName );
}
-void S_CheckLerpingState( void )
-{
- wavdata_t *info;
-
- s_listener.lerping = false;
- if( !s_bgTrack.stream ) return;
- info = FS_StreamInfo( s_bgTrack.stream );
-
- if( info && ((float)info->rate / SOUND_DMA_SPEED ) >= 1.0f )
- s_listener.lerping = s_lerping->integer;
-}
-
/*
=================
S_FadeMusicVolume
@@ -68,6 +59,7 @@ float S_GetMusicVolume( void )
scale = bound( 0.0f, musicfade.percent / 100.0f, 1.0f );
scale = 1.0f - scale;
}
+
return s_musicvolume->value * scale;
}
@@ -109,10 +101,13 @@ void S_StartBackgroundTrack( const char *introTrack, const char *mainTrack, long
// restore message, update song position
FS_SetStreamPos( s_bgTrack.stream, position );
}
-
- S_CheckLerpingState();
}
+/*
+=================
+S_StopBackgroundTrack
+=================
+*/
void S_StopBackgroundTrack( void )
{
s_listener.stream_paused = false;
@@ -123,10 +118,13 @@ void S_StopBackgroundTrack( void )
FS_FreeStream( s_bgTrack.stream );
memset( &s_bgTrack, 0, sizeof( bg_track_t ));
memset( &musicfade, 0, sizeof( musicfade ));
- s_listener.lerping = false;
- s_rawend = 0;
}
+/*
+=================
+S_StreamSetPause
+=================
+*/
void S_StreamSetPause( int pause )
{
s_listener.stream_paused = pause;
@@ -175,10 +173,10 @@ void S_StreamBackgroundTrack( void )
int fileSamples;
byte raw[MAX_RAW_SAMPLES];
int r, fileBytes;
+ rawchan_t *ch = NULL;
- if( !dma.initialized ) return;
- if( !s_bgTrack.stream ) return;
- if( s_listener.streaming ) return; // we are playing movie or somewhat
+ if( !dma.initialized || !s_bgTrack.stream || s_listener.streaming )
+ return;
// don't bother playing anything if musicvolume is 0
if( !s_musicvolume->value || s_listener.paused || s_listener.stream_paused )
@@ -193,15 +191,19 @@ void S_StreamBackgroundTrack( void )
else if( cls.key_dest == key_console )
return;
+ ch = S_FindRawChannel( S_RAW_SOUND_BACKGROUNDTRACK, true );
+
+ ASSERT( ch != NULL );
+
// see how many samples should be copied into the raw buffer
- if( s_rawend < soundtime )
- s_rawend = soundtime;
+ if( ch->s_rawend < soundtime )
+ ch->s_rawend = soundtime;
- while( s_rawend < soundtime + MAX_RAW_SAMPLES )
+ while( ch->s_rawend < soundtime + ch->max_samples )
{
wavdata_t *info = FS_StreamInfo( s_bgTrack.stream );
- bufferSamples = MAX_RAW_SAMPLES - (s_rawend - soundtime);
+ bufferSamples = ch->max_samples - (ch->s_rawend - soundtime);
// decide how much data needs to be read from the file
fileSamples = bufferSamples * ((float)info->rate / SOUND_DMA_SPEED );
@@ -228,7 +230,7 @@ void S_StreamBackgroundTrack( void )
if( r > 0 )
{
// add to raw buffer
- S_StreamRawSamples( fileSamples, info->rate, info->width, info->channels, raw );
+ S_RawSamples( fileSamples, info->rate, info->width, info->channels, raw, S_RAW_SOUND_BACKGROUNDTRACK );
}
else
{
@@ -240,7 +242,6 @@ void S_StreamBackgroundTrack( void )
Q_strncpy( s_bgTrack.current, s_bgTrack.loopName, sizeof( s_bgTrack.current ));
if( !s_bgTrack.stream ) return;
- S_CheckLerpingState();
}
else
{
@@ -262,7 +263,6 @@ void S_StartStreaming( void )
if( !dma.initialized ) return;
// begin streaming movie soundtrack
s_listener.streaming = true;
- s_listener.lerping = false;
}
/*
@@ -274,8 +274,6 @@ void S_StopStreaming( void )
{
if( !dma.initialized ) return;
s_listener.streaming = false;
- s_listener.lerping = false;
- s_rawend = 0;
}
/*
@@ -289,19 +287,26 @@ void S_StreamSoundTrack( void )
int fileSamples;
byte raw[MAX_RAW_SAMPLES];
int r, fileBytes;
+ rawchan_t *ch = NULL;
- if( !dma.initialized ) return;
- if( !s_listener.streaming || s_listener.paused ) return;
+ if( !dma.initialized || !s_listener.streaming || s_listener.paused )
+ return;
+
+ ch = S_FindRawChannel( S_RAW_SOUND_SOUNDTRACK, true );
+
+ ASSERT( ch != NULL );
// see how many samples should be copied into the raw buffer
- if( s_rawend < soundtime )
- s_rawend = soundtime;
+ if( ch->s_rawend < soundtime )
+ ch->s_rawend = soundtime;
- while( s_rawend < soundtime + MAX_RAW_SAMPLES )
+ while( ch->s_rawend < soundtime + ch->max_samples )
{
wavdata_t *info = SCR_GetMovieInfo();
- bufferSamples = MAX_RAW_SAMPLES - (s_rawend - soundtime);
+ if( !info ) break; // bad soundtrack?
+
+ bufferSamples = ch->max_samples - (ch->s_rawend - soundtime);
// decide how much data needs to be read from the file
fileSamples = bufferSamples * ((float)info->rate / SOUND_DMA_SPEED );
@@ -328,68 +333,8 @@ void S_StreamSoundTrack( void )
if( r > 0 )
{
// add to raw buffer
- S_StreamRawSamples( fileSamples, info->rate, info->width, info->channels, raw );
+ S_RawSamples( fileSamples, info->rate, info->width, info->channels, raw, S_RAW_SOUND_SOUNDTRACK );
}
else break; // no more samples for this frame
}
-}
-
-/*
-============
-S_StreamRawSamples
-
-Cinematic streaming and voice over network
-============
-*/
-void S_StreamRawSamples( int samples, int rate, int width, int channels, const byte *data )
-{
- int i, a, b, src, dst;
- int fracstep, samplefrac;
- int incount, outcount;
-
- src = 0;
- samplefrac = 0;
- fracstep = (((double)rate) / (double)SOUND_DMA_SPEED) * 256.0;
- outcount = (double)samples * (double)SOUND_DMA_SPEED / (double)rate;
- incount = samples * channels;
-
-#define TAKE_SAMPLE( s ) (sizeof(*in) == 1 ? (a = (in[src+(s)]-128)<<8,\
- b = (src < incount - channels) ? (in[src+channels+(s)]-128)<<8 : 128) : \
- (a = in[src+(s)],\
- b = (src < incount - channels) ? (in[src+channels+(s)]) : 0))
-
- // NOTE: disable lerping for cinematic sountracks
-#define LERP_SAMPLE s_listener.lerping ? (((((b - a) * (samplefrac & 255)) >> 8) + a)) : a
-
-#define RESAMPLE_RAW \
- if( channels == 2 ) { \
- for( i = 0; i < outcount; i++, samplefrac += fracstep, src = (samplefrac >> 8) << 1 ) { \
- dst = s_rawend++ & (MAX_RAW_SAMPLES - 1); \
- TAKE_SAMPLE(0); \
- s_rawsamples[dst].left = LERP_SAMPLE; \
- TAKE_SAMPLE(1); \
- s_rawsamples[dst].right = LERP_SAMPLE; \
- } \
- } else { \
- for( i = 0; i < outcount; i++, samplefrac += fracstep, src = (samplefrac >> 8) << 0 ) { \
- dst = s_rawend++ & (MAX_RAW_SAMPLES - 1); \
- TAKE_SAMPLE(0); \
- s_rawsamples[dst].left = LERP_SAMPLE; \
- s_rawsamples[dst].right = s_rawsamples[dst].left; \
- } \
- }
-
- if( s_rawend < paintedtime )
- s_rawend = paintedtime;
-
- if( width == 2 )
- {
- short *in = (short *)data;
- RESAMPLE_RAW
- }
- else
- {
- byte *in = (unsigned char *)data;
- RESAMPLE_RAW
- }
}
\ No newline at end of file
diff --git b/engine/client/s_utils.c a/engine/client/s_utils.c
index 8e065cb..24c046c 100644
--- b/engine/client/s_utils.c
+++ a/engine/client/s_utils.c
@@ -35,7 +35,7 @@ float S_SimpleSpline( float value )
float valueSquared = value * value;
// nice little ease-in, ease-out spline-like curve
- return (3 * valueSquared - 2 * valueSquared * value);
+ return (3.0f * valueSquared - 2.0f * valueSquared * value);
}
//-----------------------------------------------------------------------------
diff --git b/engine/client/s_vox.c a/engine/client/s_vox.c
index 2601301..8889d45 100644
--- b/engine/client/s_vox.c
+++ a/engine/client/s_vox.c
@@ -383,17 +383,17 @@ void VOX_FreeWord( channel_t *pchan )
pchan->currentWord = NULL; // sentence is finished
memset( &pchan->pMixer, 0, sizeof( pchan->pMixer ));
-#if 0 // release unused sounds ?
+ // release unused sounds
if( pchan->words[pchan->wordIndex].sfx )
{
// If this wave wasn't precached by the game code
if( !pchan->words[pchan->wordIndex].fKeepCached )
{
- S_FreeSound( pchan->words[pchan->wordIndex].sfx );
+ FS_FreeSound( pchan->words[pchan->wordIndex].sfx->cache );
+ pchan->words[pchan->wordIndex].sfx->cache = NULL;
pchan->words[pchan->wordIndex].sfx = NULL;
}
}
-#endif
}
void VOX_LoadFirstWord( channel_t *pchan, voxword_t *pwords )
diff --git b/engine/client/sound.h a/engine/client/sound.h
index e69f736..23479f9 100644
--- b/engine/client/sound.h
+++ a/engine/client/sound.h
@@ -20,17 +20,14 @@ extern byte *sndpool;
#include "mathlib.h"
-// local flags (never sending acorss the net)
-#define SND_LOCALSOUND (1<<9) // not paused, not looped, for internal use
-#define SND_STOP_LOOPING (1<<10) // stop all looping sounds on the entity.
-
// sound engine rate defines
-#define SOUND_DMA_SPEED 44100 // hardware playback rate
-#define SOUND_11k 11025 // 11khz sample rate
-#define SOUND_16k 16000 // 16khz sample rate
-#define SOUND_22k 22050 // 22khz sample rate
-#define SOUND_32k 32000 // 32khz sample rate
-#define SOUND_44k 44100 // 44khz sample rate
+#define SOUND_DMA_SPEED 44100 // hardware playback rate
+#define SOUND_11k 11025 // 11khz sample rate
+#define SOUND_16k 16000 // 16khz sample rate
+#define SOUND_22k 22050 // 22khz sample rate
+#define SOUND_32k 32000 // 32khz sample rate
+#define SOUND_44k 44100 // 44khz sample rate
+#define DMA_MSEC_PER_SAMPLE ((float)(1000.0 / SOUND_DMA_SPEED))
#define SND_TRACE_UPDATE_MAX 2 // max of N channels may be checked for obscured source per frame
#define SND_RADIUS_MAX 240.0f // max sound source radius
@@ -50,14 +47,14 @@ extern byte *sndpool;
#define SND_GAIN_PLAYER_WEAPON_DB 2.0f // increase player weapon gain by N dB
// fixed point stuff for real-time resampling
-#define FIX_BITS 28
-#define FIX_SCALE (1 << FIX_BITS)
-#define FIX_MASK ((1 << FIX_BITS)-1)
-#define FIX_FLOAT(a) ((int)((a) * FIX_SCALE))
-#define FIX(a) (((int)(a)) << FIX_BITS)
-#define FIX_INTPART(a) (((int)(a)) >> FIX_BITS)
-#define FIX_FRACTION(a,b) (FIX(a)/(b))
-#define FIX_FRACPART(a) ((a) & FIX_MASK)
+#define FIX_BITS 28
+#define FIX_SCALE (1 << FIX_BITS)
+#define FIX_MASK ((1 << FIX_BITS)-1)
+#define FIX_FLOAT(a) ((int)((a) * FIX_SCALE))
+#define FIX(a) (((int)(a)) << FIX_BITS)
+#define FIX_INTPART(a) (((int)(a)) >> FIX_BITS)
+#define FIX_FRACTION(a,b) (FIX(a)/(b))
+#define FIX_FRACPART(a) ((a) & FIX_MASK)
#define SNDLVL_TO_DIST_MULT( sndlvl ) \
( sndlvl ? ((pow( 10, s_refdb->value / 20 ) / pow( 10, (float)sndlvl / 20 )) / s_refdist->value ) : 0 )
@@ -66,26 +63,30 @@ extern byte *sndpool;
(int)( dist_mult ? ( 20 * log10( pow( 10, s_refdb->value / 20 ) / (dist_mult * s_refdist->value ))) : 0 )
// NOTE: clipped sound at 32760 to avoid overload
-#define CLIP( x ) (( x ) > 32760 ? 32760 : (( x ) < -32760 ? -32760 : ( x )))
-#define SWAP( a, b, t ) {(t) = (a); (a) = (b); (b) = (t);}
-#define AVG( a, b ) (((a) + (b)) >> 1 )
-#define AVG4( a, b, c, d ) (((a) + (b) + (c) + (d)) >> 2 )
-
-#define PAINTBUFFER_SIZE 1024 // 44k: was 512
-#define PAINTBUFFER (g_curpaintbuffer)
-#define CPAINTBUFFERS 3
+#define CLIP( x ) (( x ) > 32760 ? 32760 : (( x ) < -32760 ? -32760 : ( x )))
+#define SWAP( a, b, t ) {(t) = (a); (a) = (b); (b) = (t);}
+#define AVG( a, b ) (((a) + (b)) >> 1 )
+#define AVG4( a, b, c, d ) (((a) + (b) + (c) + (d)) >> 2 )
-typedef struct
-{
- int left;
- int right;
-} portable_samplepair_t;
+#define PAINTBUFFER_SIZE 1024 // 44k: was 512
+#define PAINTBUFFER (g_curpaintbuffer)
+#define CPAINTBUFFERS 3
// sound mixing buffer
-
#define CPAINTFILTERMEM 3
#define CPAINTFILTERS 4 // maximum number of consecutive upsample passes per paintbuffer
+#define S_RAW_SOUND_IDLE_SEC 10 // time interval for idling raw sound before it's freed
+#define S_RAW_SOUND_BACKGROUNDTRACK -2
+#define S_RAW_SOUND_SOUNDTRACK -1
+#define S_RAW_SAMPLES_PRECISION_BITS 14
+
+typedef struct
+{
+ int left;
+ int right;
+} portable_samplepair_t;
+
typedef struct
{
qboolean factive; // if true, mix to this paintbuffer using flags
@@ -104,7 +105,6 @@ typedef struct sfx_s
struct sfx_s *hashNext;
} sfx_t;
-extern portable_samplepair_t drybuffer[];
extern portable_samplepair_t paintbuffer[];
extern portable_samplepair_t roombuffer[];
extern portable_samplepair_t temppaintbuffer[];
@@ -146,6 +146,20 @@ typedef struct
qboolean finished;
} mixer_t;
+typedef struct
+{
+ int entnum;
+ int master_vol;
+ int leftvol; // 0-255 left volume
+ int rightvol; // 0-255 right volume
+ float dist_mult; // distance multiplier (attenuation/clipK)
+ vec3_t origin; // only use if fixed_origin is set
+ float radius; // radius of this sound effect
+ volatile uint s_rawend;
+ size_t max_samples; // buffer length
+ portable_samplepair_t rawsamples[1]; // variable sized
+} rawchan_t;
+
typedef struct channel_s
{
char name[16]; // keept sentence name
@@ -196,8 +210,9 @@ typedef struct
qboolean inmenu; // listener in-menu ?
qboolean paused;
qboolean streaming; // playing AVI-file
- qboolean lerping; // lerp stream ?
qboolean stream_paused; // pause only background track
+
+ byte pasbytes[(MAX_MAP_LEAFS+7)/8];// actual PHS for current frame
} listener_t;
typedef struct
@@ -226,18 +241,19 @@ void SNDDMA_Submit( void );
#define MAX_DYNAMIC_CHANNELS (28 + NUM_AMBIENTS)
#define MAX_CHANNELS (128 + MAX_DYNAMIC_CHANNELS) // Scourge Of Armagon has too many static sounds on hip2m4.bsp
+#define MAX_RAW_CHANNELS 16
#define MAX_RAW_SAMPLES 8192
extern sound_t ambient_sfx[NUM_AMBIENTS];
extern qboolean snd_ambient;
extern channel_t channels[MAX_CHANNELS];
+extern rawchan_t *raw_channels[MAX_RAW_CHANNELS];
extern int total_channels;
extern int paintedtime;
-extern int s_rawend;
extern int soundtime;
-extern dma_t dma;
extern listener_t s_listener;
extern int idsp_room;
+extern dma_t dma;
extern convar_t *s_volume;
extern convar_t *s_musicvolume;
@@ -245,10 +261,7 @@ extern convar_t *s_show;
extern convar_t *s_mixahead;
extern convar_t *s_lerping;
extern convar_t *dsp_off;
-extern convar_t *s_test;
-extern convar_t *s_phs;
-
-extern portable_samplepair_t s_rawsamples[MAX_RAW_SAMPLES];
+extern convar_t *s_test; // cvar to testify new effects
void S_InitScaletable( void );
wavdata_t *S_LoadSound( sfx_t *sfx );
@@ -296,7 +309,11 @@ channel_t *SND_PickStaticChannel( int entnum, sfx_t *sfx, const vec3_t pos );
int S_GetCurrentStaticSounds( soundlist_t *pout, int size );
int S_GetCurrentDynamicSounds( soundlist_t *pout, int size );
sfx_t *S_GetSfxByHandle( sound_t handle );
+rawchan_t *S_FindRawChannel( int entnum, qboolean create );
+void S_RawSamples( uint samples, uint rate, word width, word channels, const byte *data, int entnum );
void S_StopSound( int entnum, int channel, const char *soundname );
+uint S_GetRawSamplesLength( int entnum );
+void S_ClearRawChannel( int entnum );
void S_StopAllSounds( void );
void S_FreeSounds( void );
diff --git b/engine/client/vgui/utlvector.h a/engine/client/vgui/utlvector.h
index 86554e3..4bbaac5 100644
--- b/engine/client/vgui/utlvector.h
+++ a/engine/client/vgui/utlvector.h
@@ -332,7 +332,7 @@ void CUtlVector<T>::ShiftElementsLeft( int elem, int num )
memmove( &Element(elem), &Element(elem+num), numToMove * sizeof(T) );
#ifdef _DEBUG
- Q_memset( &Element(m_Size-num), 0xDD, num * sizeof(T) );
+ memset( &Element(m_Size-num), 0xDD, num * sizeof(T) );
#endif
}
}
diff --git b/engine/client/vgui/vgui_draw.c a/engine/client/vgui/vgui_draw.c
index 2afaa8d..3eca12e 100644
--- b/engine/client/vgui/vgui_draw.c
+++ a/engine/client/vgui/vgui_draw.c
@@ -32,7 +32,7 @@ Startup VGUI backend
*/
void VGUI_DrawInit( void )
{
- Q_memset( g_textures, 0, sizeof( g_textures ));
+ memset( g_textures, 0, sizeof( g_textures ));
g_textureId = g_iBoundTexture = 0;
vgui_colorstrings = Cvar_Get( "vgui_colorstrings", "0", CVAR_ARCHIVE, "allow colorstrings in VGUI texts" );
@@ -65,7 +65,7 @@ generate unique texture number
int VGUI_GenerateTexture( void )
{
if( ++g_textureId >= VGUI_MAX_TEXTURES )
- Sys_Error( "VGUI_GenerateTexture: VGUI_MAX_TEXTURES limit exceeded\n" );
+ Host_Error( "VGUI_GenerateTexture: VGUI_MAX_TEXTURES limit exceeded\n" );
return g_textureId;
}
@@ -88,7 +88,7 @@ void VGUI_UploadTexture( int id, const char *buffer, int width, int height )
}
Q_snprintf( texName, sizeof( texName ), "*vgui%i", id );
- Q_memset( &r_image, 0, sizeof( r_image ));
+ memset( &r_image, 0, sizeof( r_image ));
r_image.width = width;
r_image.height = height;
@@ -98,7 +98,6 @@ void VGUI_UploadTexture( int id, const char *buffer, int width, int height )
r_image.buffer = (byte *)buffer;
g_textures[id] = GL_LoadTextureInternal( texName, &r_image, TF_IMAGE, false );
- GL_SetTextureType( g_textures[id], TEX_VGUI );
g_iBoundTexture = id;
}
@@ -121,7 +120,7 @@ void VGUI_CreateTexture( int id, int width, int height )
}
Q_snprintf( texName, sizeof( texName ), "*vgui%i", id );
- Q_memset( &r_image, 0, sizeof( r_image ));
+ memset( &r_image, 0, sizeof( r_image ));
r_image.width = width;
r_image.height = height;
@@ -131,7 +130,6 @@ void VGUI_CreateTexture( int id, int width, int height )
r_image.buffer = NULL;
g_textures[id] = GL_LoadTextureInternal( texName, &r_image, TF_IMAGE|TF_NEAREST, false );
- GL_SetTextureType( g_textures[id], TEX_VGUI );
g_iBoundTexture = id;
}
@@ -233,7 +231,8 @@ generic method to fill rectangle
*/
void VGUI_DrawQuad( const vpoint_t *ul, const vpoint_t *lr )
{
- ASSERT( ul != NULL && lr != NULL );
+ Assert( ul != NULL );
+ Assert( lr != NULL );
pglBegin( GL_QUADS );
pglTexCoord2f( ul->coord[0], ul->coord[1] );
diff --git b/engine/client/vgui/vgui_draw.h a/engine/client/vgui/vgui_draw.h
index fa1da80..a40225e 100644
--- b/engine/client/vgui/vgui_draw.h
+++ a/engine/client/vgui/vgui_draw.h
@@ -46,7 +46,7 @@ void VGUI_EnableTexture( qboolean enable );
void VGUI_CreateTexture( int id, int width, int height );
void VGUI_UploadTexture( int id, const char *buffer, int width, int height );
void VGUI_UploadTextureBlock( int id, int drawX, int drawY, const byte *rgba, int blockWidth, int blockHeight );
-long VGUI_SurfaceWndProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
+LONG VGUI_SurfaceWndProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
void VGUI_DrawQuad( const vpoint_t *ul, const vpoint_t *lr );
void VGUI_GetTextureSizes( int *width, int *height );
int VGUI_GenerateTexture( void );
diff --git b/engine/client/vgui/vgui_input.cpp a/engine/client/vgui/vgui_input.cpp
index f05ee2f..1ac8e2f 100644
--- b/engine/client/vgui/vgui_input.cpp
+++ a/engine/client/vgui/vgui_input.cpp
@@ -106,7 +106,7 @@ void VGUI_InitKeyTranslationTable( void )
bInitted = true;
// set virtual key translation table
- Q_memset( s_pVirtualKeyTrans, -1, sizeof( s_pVirtualKeyTrans ));
+ memset( s_pVirtualKeyTrans, -1, sizeof( s_pVirtualKeyTrans ));
s_pVirtualKeyTrans['0'] = KEY_0;
s_pVirtualKeyTrans['1'] = KEY_1;
@@ -217,9 +217,9 @@ KeyCode VGUI_MapKey( int keyCode )
{
VGUI_InitKeyTranslationTable();
- if( keyCode < 0 || keyCode >= sizeof( s_pVirtualKeyTrans ) / sizeof( s_pVirtualKeyTrans[0] ))
+ if( keyCode < 0 || keyCode >= ARRAYSIZE( s_pVirtualKeyTrans ))
{
- Assert( false );
+ Assert( 0 );
return (KeyCode)-1;
}
else
@@ -228,7 +228,7 @@ KeyCode VGUI_MapKey( int keyCode )
}
}
-long VGUI_SurfaceWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
+LONG VGUI_SurfaceWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
SurfaceBase *surface = NULL;
CEnginePanel *panel = NULL;
@@ -238,11 +238,13 @@ long VGUI_SurfaceWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
return 0;
panel = (CEnginePanel *)VGui_GetPanel();
+ ASSERT( panel != NULL );
+
surface = panel->getSurfaceBase();
- pApp = panel->getApp();
+ ASSERT( surface != NULL );
+ pApp = panel->getApp();
ASSERT( pApp != NULL );
- ASSERT( surface != NULL );
switch( uMsg )
{
@@ -284,7 +286,7 @@ long VGUI_SurfaceWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
break;
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
- if(!( lParam & ( 1 << 30 )))
+ if( !FBitSet( lParam, BIT( 30 )))
pApp->internalKeyPressed( VGUI_MapKey( wParam ), surface );
pApp->internalKeyTyped( VGUI_MapKey( wParam ), surface );
break;
diff --git b/engine/client/vgui/vgui_int.cpp a/engine/client/vgui/vgui_int.cpp
index a461670..bb30f94 100644
--- b/engine/client/vgui/vgui_int.cpp
+++ a/engine/client/vgui/vgui_int.cpp
@@ -68,7 +68,7 @@ void VGui_Startup( void )
{
if( rootpanel )
{
- rootpanel->setSize( menu.globals->scrWidth, menu.globals->scrHeight );
+ rootpanel->setSize( gameui.globals->scrWidth, gameui.globals->scrHeight );
return;
}
diff --git b/engine/client/vgui/vgui_main.h a/engine/client/vgui/vgui_main.h
index 8c354d8..788010b 100644
--- b/engine/client/vgui/vgui_main.h
+++ a/engine/client/vgui/vgui_main.h
@@ -19,6 +19,8 @@ GNU General Public License for more details.
#include "utlvector.h"
#include "utlrbtree.h"
+//#define NEW_VGUI_DLL
+
#include<VGUI.h>
#include<VGUI_App.h>
#include<VGUI_Font.h>
@@ -127,6 +129,9 @@ public:
virtual void setTitle( const char *title ) { }
virtual void createPopup( Panel* embeddedPanel ) { }
virtual bool isWithin( int x, int y ) { return true; }
+#ifdef NEW_VGUI_DLL
+ virtual void GetMousePos( int &x, int &y );
+#endif
virtual bool hasFocus( void );
protected:
virtual int createNewTextureID( void );
diff --git b/engine/client/vgui/vgui_surf.cpp a/engine/client/vgui/vgui_surf.cpp
index d240012..1a73772 100644
--- b/engine/client/vgui/vgui_surf.cpp
+++ a/engine/client/vgui/vgui_surf.cpp
@@ -27,8 +27,8 @@ CEngineSurface :: CEngineSurface( Panel *embeddedPanel ):SurfaceBase( embeddedPa
_drawTextColor[0] = _drawTextColor[1] = _drawTextColor[2] = _drawTextColor[3] = 255;
_surfaceExtents[0] = _surfaceExtents[1] = 0;
- _surfaceExtents[2] = menu.globals->scrWidth;
- _surfaceExtents[3] = menu.globals->scrHeight;
+ _surfaceExtents[2] = gameui.globals->scrWidth;
+ _surfaceExtents[3] = gameui.globals->scrHeight;
_drawTextPos[0] = _drawTextPos[1] = 0;
_hCurrentFont = null;
@@ -56,6 +56,19 @@ void CEngineSurface :: setCursor( Cursor *cursor )
VGUI_CursorSelect( cursor );
}
+#ifdef NEW_VGUI_DLL
+void CEngineSurface :: GetMousePos( int &x, int &y )
+{
+ POINT curpos;
+
+ GetCursorPos( &curpos );
+ ScreenToClient( host.hWnd, &curpos );
+
+ x = curpos.x;
+ y = curpos.y;
+}
+#endif
+
void CEngineSurface :: SetupPaintState( const paintState_t &paintState )
{
_translateX = paintState.iTranslateX;
diff --git b/engine/common/avikit.c a/engine/common/avikit.c
index 44976c4..c3fc08d 100644
--- b/engine/common/avikit.c
+++ a/engine/common/avikit.c
@@ -139,8 +139,6 @@ qboolean AVI_ACMConvertAudio( movie_state_t *Avi )
dword dest_length;
short bits;
- ASSERT( Avi != NULL );
-
// WMA codecs, both versions - they simply don't work.
if( Avi->audio_header->wFormatTag == 0x160 || Avi->audio_header->wFormatTag == 0x161 )
{
@@ -249,8 +247,6 @@ qboolean AVI_ACMConvertAudio( movie_state_t *Avi )
qboolean AVI_GetVideoInfo( movie_state_t *Avi, long *xres, long *yres, float *duration )
{
- ASSERT( Avi != NULL );
-
if( !Avi->active )
return false;
@@ -269,8 +265,6 @@ qboolean AVI_GetVideoInfo( movie_state_t *Avi, long *xres, long *yres, float *du
// returns a unique frame identifier
long AVI_GetVideoFrameNumber( movie_state_t *Avi, float time )
{
- ASSERT( Avi != NULL );
-
if( !Avi->active )
return 0;
@@ -284,8 +278,6 @@ byte *AVI_GetVideoFrame( movie_state_t *Avi, long frame )
byte *frame_raw, *tmp;
int i;
- ASSERT( Avi != NULL );
-
if( !Avi->active ) return NULL;
if( frame >= Avi->video_frames )
@@ -310,8 +302,6 @@ byte *AVI_GetVideoFrame( movie_state_t *Avi, long frame )
qboolean AVI_GetAudioInfo( movie_state_t *Avi, wavdata_t *snd_info )
{
- ASSERT( Avi != NULL );
-
if( !Avi->active || Avi->audio_stream == NULL || snd_info == NULL )
{
return false;
@@ -335,8 +325,6 @@ qboolean AVI_SeekPosition( movie_state_t *Avi, dword offset )
{
int breaker;
- ASSERT( Avi != NULL );
-
if( offset < Avi->cpa_blockoffset ) // well, shit. we can't seek backwards... restart
{
if( Avi->cpa_blockoffset - offset < 500000 )
@@ -382,12 +370,10 @@ qboolean AVI_SeekPosition( movie_state_t *Avi, dword offset )
}
// get a chunk of audio from the stream (in bytes)
-fs_offset_t AVI_GetAudioChunk( movie_state_t *Avi, char *audiodata, long offset, long length )
+long AVI_GetAudioChunk( movie_state_t *Avi, char *audiodata, long offset, long length )
{
- int i;
long result = 0;
-
- ASSERT( Avi != NULL );
+ int i;
// zero data past the end of the file
if( offset + length > Avi->audio_length )
@@ -457,8 +443,6 @@ fs_offset_t AVI_GetAudioChunk( movie_state_t *Avi, char *audiodata, long offset,
void AVI_CloseVideo( movie_state_t *Avi )
{
- ASSERT( Avi != NULL );
-
if( Avi->active )
{
pAVIStreamGetFrameClose( Avi->video_getframe );
@@ -494,8 +478,6 @@ void AVI_OpenVideo( movie_state_t *Avi, const char *filename, qboolean load_audi
long opened_streams = 0;
LONG hr;
- ASSERT( Avi != NULL );
-
// default state: non-working.
Avi->active = false;
Avi->quiet = quiet;
diff --git b/engine/common/build.c a/engine/common/build.c
index b34aa6a..d227ac7 100644
--- b/engine/common/build.c
+++ a/engine/common/build.c
@@ -48,6 +48,6 @@ int Q_buildnum( void )
return b;
#else
- return 3366;
+ return 3598;
#endif
}
\ No newline at end of file
diff --git b/engine/common/cmd.c a/engine/common/cmd.c
index 4ad5be6..c6236e8 100644
--- b/engine/common/cmd.c
+++ a/engine/common/cmd.c
@@ -17,8 +17,8 @@ GNU General Public License for more details.
#include "client.h"
#include "server.h"
-#define MAX_CMD_BUFFER 16384
-#define MAX_CMD_LINE 1024
+#define MAX_CMD_BUFFER 32768
+#define MAX_CMD_LINE 2048
typedef struct
{
@@ -31,6 +31,8 @@ qboolean cmd_wait;
cmdbuf_t cmd_text;
byte cmd_text_buf[MAX_CMD_BUFFER];
cmdalias_t *cmd_alias;
+uint cmd_condition;
+int cmd_condlevel;
/*
=============================================================================
@@ -92,17 +94,16 @@ Adds command text at the end of the buffer
*/
void Cbuf_AddText( const char *text )
{
- int l;
-
- l = Q_strlen( text );
+ int l = Q_strlen( text );
if( cmd_text.cursize + l >= cmd_text.maxsize )
{
MsgDev( D_WARN, "Cbuf_AddText: overflow\n" );
- return;
}
-
- memcpy( Cbuf_GetSpace( &cmd_text, l ), text, l );
+ else
+ {
+ memcpy( Cbuf_GetSpace( &cmd_text, l ), text, l );
+ }
}
/*
@@ -115,28 +116,17 @@ Adds a \n to the text
*/
void Cbuf_InsertText( const char *text )
{
- char *temp;
- int templen;
-
- // copy off any commands still remaining in the exec buffer
- templen = cmd_text.cursize;
+ int l = Q_strlen( text );
- if( templen )
+ if( cmd_text.cursize + l >= cmd_text.maxsize )
{
- temp = Z_Malloc( templen );
- memcpy( temp, cmd_text.data, templen );
- cmd_text.cursize = 0;
+ MsgDev( D_WARN, "Cbuf_InsertText: overflow\n" );
}
- else temp = NULL;
-
- // add the entire text of the file
- Cbuf_AddText( text );
-
- // add the copied off data
- if( templen )
+ else
{
- memcpy( Cbuf_GetSpace( &cmd_text, templen ), temp, templen );
- Z_Free( temp );
+ memmove( cmd_text.data + l, cmd_text.data, cmd_text.cursize );
+ memcpy( cmd_text.data, text, l );
+ cmd_text.cursize += l;
}
}
@@ -150,28 +140,50 @@ void Cbuf_Execute( void )
char *text;
char line[MAX_CMD_LINE];
int i, quotes;
+ char *comment;
while( cmd_text.cursize )
{
// find a \n or ; line break
text = (char *)cmd_text.data;
- quotes = 0;
+ quotes = false;
+ comment = NULL;
for( i = 0; i < cmd_text.cursize; i++ )
{
- if( text[i] == '"' ) quotes++;
- if(!( quotes & 1 ) && text[i] == ';' )
- break; // don't break if inside a quoted string
+ if( !comment )
+ {
+ if( text[i] == '"' ) quotes = !quotes;
+
+ if( quotes )
+ {
+ // make sure i doesn't get > cursize which causes a negative size in memmove, which is fatal --blub
+ if( i < ( cmd_text.cursize - 1 ) && ( text[i+0] == '\\' && (text[i+1] == '"' || text[i+1] == '\\')))
+ i++;
+ }
+ else
+ {
+ if( text[i+0] == '/' && text[i+1] == '/' && ( i == 0 || (byte)text[i - 1] <= ' ' ))
+ comment = &text[i];
+ if( text[i] == ';' ) break; // don't break if inside a quoted string or comment
+ }
+ }
+
if( text[i] == '\n' || text[i] == '\r' )
break;
}
if( i >= ( MAX_CMD_LINE - 1 ))
- Sys_Error( "Cbuf_Execute: command string owerflow\n" );
-
- memcpy( line, text, i );
- line[i] = 0;
+ {
+ MsgDev( D_ERROR, "Cbuf_Execute: command string owerflow\n" );
+ line[0] = 0;
+ }
+ else
+ {
+ memcpy( line, text, comment ? (comment - text) : i );
+ line[comment ? (comment - text) : i] = 0;
+ }
// delete the text from the command buffer and move remaining commands down
// this is necessary because commands (exec) can insert data at the
@@ -184,11 +196,11 @@ void Cbuf_Execute( void )
{
i++;
cmd_text.cursize -= i;
- memcpy( text, text + i, cmd_text.cursize );
+ memmove( cmd_text.data, text + i, cmd_text.cursize );
}
// execute the command line
- Cmd_ExecuteString( line, src_command );
+ Cmd_ExecuteString( line );
if( cmd_wait )
{
@@ -332,7 +344,7 @@ void Cmd_Alias_f( void )
return;
}
- // if the alias allready exists, reuse it
+ // if the alias already exists, reuse it
for( a = cmd_alias; a; a = a->next )
{
if( !Q_strcmp( s, a->name ))
@@ -344,12 +356,19 @@ void Cmd_Alias_f( void )
if( !a )
{
+ cmdalias_t *cur, *prev;
+
a = Z_Malloc( sizeof( cmdalias_t ));
- a->next = cmd_alias;
- cmd_alias = a;
- }
- Q_strncpy( a->name, s, sizeof( a->name ));
+ Q_strncpy( a->name, s, sizeof( a->name ));
+
+ // insert it at the right alphanumeric position
+ for( prev = NULL, cur = cmd_alias; cur && Q_strcmp( cur->name, a->name ) < 0; prev = cur, cur = cur->next );
+
+ if( prev ) prev->next = a;
+ else cmd_alias = a;
+ a->next = cur;
+ }
// copy the rest of the command line
cmd[0] = 0; // start out with a null string
@@ -358,15 +377,56 @@ void Cmd_Alias_f( void )
for( i = 2; i < c; i++ )
{
- Q_strcat( cmd, Cmd_Argv( i ));
- if( i != c ) Q_strcat( cmd, " " );
+ if( i != 2 ) Q_strncat( cmd, " ", sizeof( cmd ));
+ Q_strncat( cmd, Cmd_Argv( i ), sizeof( cmd ));
}
- Q_strcat( cmd, "\n" );
+ Q_strncat( cmd, "\n", sizeof( cmd ));
a->value = copystring( cmd );
}
/*
+===============
+Cmd_UnAlias_f
+
+Remove existing aliases.
+===============
+*/
+static void Cmd_UnAlias_f ( void )
+{
+ cmdalias_t *a, *p;
+ const char *s;
+ int i;
+
+ if( Cmd_Argc() == 1 )
+ {
+ Msg( "Usage: unalias alias1 [alias2 ...]\n" );
+ return;
+ }
+
+ for( i = 1; i < Cmd_Argc(); i++ )
+ {
+ s = Cmd_Argv( i );
+ p = NULL;
+
+ for( a = cmd_alias; a; p = a, a = a->next )
+ {
+ if( !Q_strcmp( s, a->name ))
+ {
+ if( a == cmd_alias )
+ cmd_alias = a->next;
+ if( p ) p->next = a->next;
+ Mem_Free( a->value );
+ Mem_Free( a );
+ break;
+ }
+ }
+
+ if( !a ) Msg( "unalias: %s alias not found\n", s );
+ }
+}
+
+/*
=============================================================================
COMMAND EXECUTION
@@ -387,7 +447,6 @@ static char *cmd_args = NULL;
static char *cmd_argv[MAX_CMD_TOKENS];
static char cmd_tokenized[MAX_CMD_BUFFER]; // will have 0 bytes inserted
static cmd_t *cmd_functions; // possible commands to execute
-cmd_source_t cmd_source;
/*
============
@@ -489,12 +548,14 @@ void Cmd_TokenizeString( char *text )
while( 1 )
{
// skip whitespace up to a /n
- while( *text && ((byte)*text) <= ' ' && *text != '\n' )
+ while( *text && ((byte)*text) <= ' ' && *text != '\r' && *text != '\n' )
text++;
-
- if( *text == '\n' )
- {
+
+ if( *text == '\n' || *text == '\r' )
+ {
// a newline seperates commands in the buffer
+ if( *text == '\r' && text[1] == '\n' )
+ text++;
text++;
break;
}
@@ -516,7 +577,6 @@ void Cmd_TokenizeString( char *text )
}
}
-
/*
============
Cmd_AddCommand
@@ -524,7 +584,7 @@ Cmd_AddCommand
*/
void Cmd_AddCommand( const char *cmd_name, xcommand_t function, const char *cmd_desc )
{
- cmd_t *cmd;
+ cmd_t *cmd, *cur, *prev;
// fail if the command is a variable name
if( Cvar_FindVar( cmd_name ))
@@ -545,41 +605,57 @@ void Cmd_AddCommand( const char *cmd_name, xcommand_t function, const char *cmd_
cmd->name = copystring( cmd_name );
cmd->desc = copystring( cmd_desc );
cmd->function = function;
- cmd->next = cmd_functions;
- cmd_functions = cmd;
+
+ // insert it at the right alphanumeric position
+ for( prev = NULL, cur = cmd_functions; cur && Q_strcmp( cur->name, cmd_name ) < 0; prev = cur, cur = cur->next );
+
+ if( prev ) prev->next = cmd;
+ else cmd_functions = cmd;
+ cmd->next = cur;
}
/*
============
-Cmd_AddGameCommand
+Cmd_AddServerCommand
============
*/
-void Cmd_AddGameCommand( const char *cmd_name, xcommand_t function )
+void Cmd_AddServerCommand( const char *cmd_name, xcommand_t function )
{
- cmd_t *cmd;
+ cmd_t *cmd, *cur, *prev;
+
+ if( !cmd_name || !*cmd_name )
+ {
+ MsgDev( D_INFO, "Cmd_AddServerCommand: NULL name\n" );
+ return;
+ }
// fail if the command is a variable name
if( Cvar_FindVar( cmd_name ))
{
- MsgDev( D_INFO, "Cmd_AddCommand: %s already defined as a var\n", cmd_name );
+ MsgDev( D_ERROR, "Cmd_AddServerCommand: %s already defined as a var\n", cmd_name );
return;
}
// fail if the command already exists
if( Cmd_Exists( cmd_name ))
{
- MsgDev(D_INFO, "Cmd_AddCommand: %s already defined\n", cmd_name);
+ MsgDev( D_ERROR, "Cmd_AddServerCommand: %s already defined\n", cmd_name );
return;
}
// use a small malloc to avoid zone fragmentation
cmd = Z_Malloc( sizeof( cmd_t ));
cmd->name = copystring( cmd_name );
- cmd->desc = copystring( "game command" );
+ cmd->desc = copystring( "server command" );
cmd->function = function;
- cmd->flags = CMD_EXTDLL;
- cmd->next = cmd_functions;
- cmd_functions = cmd;
+ cmd->flags = CMD_SERVERDLL;
+
+ // insert it at the right alphanumeric position
+ for( prev = NULL, cur = cmd_functions; cur && Q_strcmp( cur->name, cmd_name ) < 0; prev = cur, cur = cur->next );
+
+ if( prev ) prev->next = cmd;
+ else cmd_functions = cmd;
+ cmd->next = cur;
}
/*
@@ -587,22 +663,28 @@ void Cmd_AddGameCommand( const char *cmd_name, xcommand_t function )
Cmd_AddClientCommand
============
*/
-void Cmd_AddClientCommand( const char *cmd_name, xcommand_t function )
+int Cmd_AddClientCommand( const char *cmd_name, xcommand_t function )
{
- cmd_t *cmd;
+ cmd_t *cmd, *cur, *prev;
+
+ if( !cmd_name || !*cmd_name )
+ {
+ MsgDev( D_INFO, "Cmd_AddClientCommand: NULL name\n" );
+ return 0;
+ }
// fail if the command is a variable name
if( Cvar_FindVar( cmd_name ))
{
- MsgDev( D_INFO, "Cmd_AddCommand: %s already defined as a var\n", cmd_name );
- return;
+ MsgDev( D_INFO, "Cmd_AddClientCommand: %s already defined as a var\n", cmd_name );
+ return 0;
}
// fail if the command already exists
if( Cmd_Exists( cmd_name ))
{
- MsgDev(D_INFO, "Cmd_AddCommand: %s already defined\n", cmd_name);
- return;
+ MsgDev(D_INFO, "Cmd_AddClientCommand: %s already defined\n", cmd_name );
+ return 0;
}
// use a small malloc to avoid zone fragmentation
@@ -611,8 +693,61 @@ void Cmd_AddClientCommand( const char *cmd_name, xcommand_t function )
cmd->desc = copystring( "client command" );
cmd->function = function;
cmd->flags = CMD_CLIENTDLL;
- cmd->next = cmd_functions;
- cmd_functions = cmd;
+
+ // insert it at the right alphanumeric position
+ for( prev = NULL, cur = cmd_functions; cur && Q_strcmp( cur->name, cmd_name ) < 0; prev = cur, cur = cur->next );
+
+ if( prev ) prev->next = cmd;
+ else cmd_functions = cmd;
+ cmd->next = cur;
+
+ return 1;
+}
+
+/*
+============
+Cmd_AddGameUICommand
+============
+*/
+int Cmd_AddGameUICommand( const char *cmd_name, xcommand_t function )
+{
+ cmd_t *cmd, *cur, *prev;
+
+ if( !cmd_name || !*cmd_name )
+ {
+ MsgDev( D_INFO, "Cmd_AddGameUICommand: NULL name\n" );
+ return 0;
+ }
+
+ // fail if the command is a variable name
+ if( Cvar_FindVar( cmd_name ))
+ {
+ MsgDev( D_INFO, "Cmd_AddGameUICommand: %s already defined as a var\n", cmd_name );
+ return 0;
+ }
+
+ // fail if the command already exists
+ if( Cmd_Exists( cmd_name ))
+ {
+ MsgDev(D_INFO, "Cmd_AddGameUICommand: %s already defined\n", cmd_name );
+ return 0;
+ }
+
+ // use a small malloc to avoid zone fragmentation
+ cmd = Z_Malloc( sizeof( cmd_t ));
+ cmd->name = copystring( cmd_name );
+ cmd->desc = copystring( "GameUI command" );
+ cmd->function = function;
+ cmd->flags = CMD_GAMEUIDLL;
+
+ // insert it at the right alphanumeric position
+ for( prev = NULL, cur = cmd_functions; cur && Q_strcmp( cur->name, cmd_name ) < 0; prev = cur, cur = cur->next );
+
+ if( prev ) prev->next = cmd;
+ else cmd_functions = cmd;
+ cmd->next = cur;
+
+ return 1;
}
/*
@@ -693,25 +828,143 @@ qboolean Cmd_Exists( const char *cmd_name )
/*
============
+Cmd_If_f
+
+Compare and et condition bit if true
+============
+*/
+void Cmd_If_f( void )
+{
+ // reset bit first
+ cmd_condition &= ~BIT( cmd_condlevel );
+
+ // usage
+ if( cmd_argc == 1 )
+ {
+ Msg( "Usage: if <op1> [ <operator> <op2> ]\n");
+ Msg( ":<action1>\n" );
+ Msg( ":<action2>\n" );
+ Msg( "else\n" );
+ Msg( ":<action3>\n" );
+ Msg( "operands are string or float values\n" );
+ Msg( "and substituted cvars like '$cl_lw'\n" );
+ Msg( "operator is '='', '==', '>', '<', '>=', '<=' or '!='\n" );
+ return;
+ }
+
+ // one argument - check if nonzero
+ if( cmd_argc == 2 )
+ {
+ if( Q_atof( cmd_argv[1] ))
+ cmd_condition |= BIT( cmd_condlevel );
+ }
+ else if( cmd_argc == 4 )
+ {
+ // simple compare
+ float f1 = Q_atof( cmd_argv[1] );
+ float f2 = Q_atof( cmd_argv[3] );
+
+ if( !cmd_argv[2][0] ) // this is wrong
+ return;
+
+ if(( cmd_argv[2][0] == '=' ) || ( cmd_argv[2][1] == '=' )) // =, ==, >=, <=
+ {
+ if( !Q_strcmp( cmd_argv[1], cmd_argv[3] ) || (( f1 || f2 ) && ( f1 == f2 )))
+ cmd_condition |= BIT( cmd_condlevel );
+ }
+
+ if( cmd_argv[2][0] == '!' ) // !=
+ {
+ cmd_condition ^= BIT( cmd_condlevel );
+ return;
+ }
+
+ if(( cmd_argv[2][0] == '>' ) && ( f1 > f2 )) // >, >=
+ cmd_condition |= BIT( cmd_condlevel );
+
+ if(( cmd_argv[2][0] == '<' ) && ( f1 < f2 )) // <, <=
+ cmd_condition |= BIT( cmd_condlevel );
+ }
+}
+
+/*
+============
+Cmd_Else_f
+
+Invert condition bit
+============
+*/
+void Cmd_Else_f( void )
+{
+ cmd_condition ^= BIT( cmd_condlevel );
+}
+
+/*
+============
Cmd_ExecuteString
A complete command line has been parsed, so try to execute it
============
*/
-void Cmd_ExecuteString( char *text, cmd_source_t src )
+void Cmd_ExecuteString( char *text )
{
- cmd_t *cmd;
+ cmd_t *cmd;
cmdalias_t *a;
+ char command[MAX_CMD_LINE];
+ char *pcmd = command;
+ int len = 0;
+
+ cmd_condlevel = 0;
+
+ // cvar value substitution
+ if( cmd_scripting && cmd_scripting->integer )
+ {
+ while( *text )
+ {
+ // check for escape
+ if(( *text == '\\' || *text == '$' ) && (*( text + 1 ) == '$' ))
+ {
+ text ++;
+ }
+ else if( *text == '$' )
+ {
+ char token[MAX_CMD_LINE];
+ char *ptoken = token;
+
+ // check for correct cvar name
+ text++;
+ while(( *text >= '0' && *text <= '9' ) || ( *text >= 'A' && *text <= 'Z' ) || ( *text >= 'a' && *text <= 'z' ) || ( *text == '_' ))
+ *ptoken++ = *text++;
+ *ptoken = 0;
+
+ len += Q_strncpy( pcmd, Cvar_VariableString( token ), MAX_CMD_LINE - len );
+ pcmd = command + len;
+
+ if( !*text ) break;
+ }
+
+ *pcmd++ = *text++;
+ len++;
+ }
+
+ *pcmd = 0;
+ text = command;
+
+ while( *text == ':' )
+ {
+ if( !FBitSet( cmd_condition, BIT( cmd_condlevel )))
+ return;
+ cmd_condlevel++;
+ text++;
+ }
+ }
- // set cmd source
- cmd_source = src;
-
// execute the command line
Cmd_TokenizeString( text );
if( !Cmd_Argc()) return; // no tokens
- // check alias
+ // check aliases
for( a = cmd_alias; a; a = a->next )
{
if( !Q_stricmp( cmd_argv[0], a->name ))
@@ -735,13 +988,10 @@ void Cmd_ExecuteString( char *text, cmd_source_t src )
if( Cvar_Command( )) return;
// forward the command line to the server, so the entity DLL can parse it
- if( cmd_source == src_command && host.type == HOST_NORMAL )
+ if( host.type == HOST_NORMAL )
{
if( cls.state >= ca_connected )
- {
Cmd_ForwardToServer();
- return;
- }
}
else if( text[0] != '@' && host.type == HOST_NORMAL )
{
@@ -808,11 +1058,16 @@ void Cmd_List_f( void )
for( cmd = cmd_functions; cmd; cmd = cmd->next )
{
+ if( cmd->name[0] == '@' )
+ continue; // never show system cmds
+
if( match && !Q_stricmpext( match, cmd->name ))
continue;
- Msg( "%10s %s\n", cmd->name, cmd->desc );
+
+ Msg( " %-*s ^3%s^7\n", 32, cmd->name, cmd->desc );
i++;
}
+
Msg( "%i commands\n", i );
}
@@ -820,7 +1075,7 @@ void Cmd_List_f( void )
============
Cmd_Unlink
-unlink all commands with flag CVAR_EXTDLL
+unlink all commands with specified flag
============
*/
void Cmd_Unlink( int group )
@@ -829,15 +1084,21 @@ void Cmd_Unlink( int group )
cmd_t **prev;
int count = 0;
- if( Cvar_VariableInteger( "host_gameloaded" ) && ( group & CMD_EXTDLL ))
+ if( Cvar_VariableInteger( "host_gameloaded" ) && FBitSet( group, CMD_SERVERDLL ))
+ {
+ MsgDev( D_INFO, "can't unlink commands while server is loaded\n" );
+ return;
+ }
+
+ if( Cvar_VariableInteger( "host_clientloaded" ) && FBitSet( group, CMD_CLIENTDLL ))
{
- Msg( "can't unlink cvars while game is loaded\n" );
+ MsgDev( D_INFO, "can't unlink commands while client is loaded\n" );
return;
}
- if( Cvar_VariableInteger( "host_clientloaded" ) && ( group & CMD_CLIENTDLL ))
+ if( Cvar_VariableInteger( "host_gameuiloaded" ) && FBitSet( group, CMD_GAMEUIDLL ))
{
- Msg( "can't unlink cvars while client is loaded\n" );
+ MsgDev( D_INFO, "can't unlink commands while GameUI is loaded\n" );
return;
}
@@ -847,7 +1108,8 @@ void Cmd_Unlink( int group )
cmd = *prev;
if( !cmd ) break;
- if( group && !( cmd->flags & group ))
+ // do filter by specified group
+ if( group && !FBitSet( cmd->flags, group ))
{
prev = &cmd->next;
continue;
@@ -855,11 +1117,8 @@ void Cmd_Unlink( int group )
*prev = cmd->next;
- if( cmd->name )
- Mem_Free( cmd->name );
-
- if( cmd->desc )
- Mem_Free( cmd->desc );
+ if( cmd->name ) Mem_Free( cmd->name );
+ if( cmd->desc ) Mem_Free( cmd->desc );
Mem_Free( cmd );
count++;
@@ -885,13 +1144,21 @@ Cmd_Init
void Cmd_Init( void )
{
Cbuf_Init();
+
cmd_functions = NULL;
+ cmd_condition = 0;
+ cmd_alias = NULL;
+ cmd_args = NULL;
+ cmd_argc = 0;
// register our commands
- Cmd_AddCommand ("echo", Cmd_Echo_f, "print a message to the console (useful in scripts)" );
- Cmd_AddCommand ("wait", Cmd_Wait_f, "make script execution wait for some rendered frames" );
- Cmd_AddCommand ("cmdlist", Cmd_List_f, "display all console commands beginning with the specified prefix" );
- Cmd_AddCommand ("stuffcmds", Cmd_StuffCmds_f, va( "execute commandline parameters (must be present in %s.rc script)", SI.ModuleName ));
- Cmd_AddCommand ("cmd", Cmd_ForwardToServer, "send a console commandline to the server" );
- Cmd_AddCommand ("alias", Cmd_Alias_f, "create a script function. Without arguments show the list of all alias" );
+ Cmd_AddCommand( "echo", Cmd_Echo_f, "print a message to the console (useful in scripts)" );
+ Cmd_AddCommand( "wait", Cmd_Wait_f, "make script execution wait for some rendered frames" );
+ Cmd_AddCommand( "cmdlist", Cmd_List_f, "display all console commands beginning with the specified prefix" );
+ Cmd_AddCommand( "stuffcmds", Cmd_StuffCmds_f, va( "execute commandline parameters (must be present in %s.rc script)", SI.ModuleName ));
+ Cmd_AddCommand( "cmd", Cmd_ForwardToServer, "send a console commandline to the server" );
+ Cmd_AddCommand( "alias", Cmd_Alias_f, "create a script function. Without arguments show the list of all alias" );
+ Cmd_AddCommand( "unalias", Cmd_UnAlias_f, "remove a script function" );
+ Cmd_AddCommand( "if", Cmd_If_f, "compare and set condition bits" );
+ Cmd_AddCommand( "else", Cmd_Else_f, "invert condition bit" );
}
\ No newline at end of file
diff --git b/engine/common/common.c a/engine/common/common.c
index a15e971..559aba6 100644
--- b/engine/common/common.c
+++ a/engine/common/common.c
@@ -22,6 +22,39 @@ GNU General Public License for more details.
/*
==============
+COM_IsSingleChar
+
+interpert this character as single
+==============
+*/
+static int COM_IsSingleChar( char c )
+{
+ if( c == '{' || c == '}' || c == ')' || c == '(' || c == '\'' || c == ',' )
+ return true;
+
+ if( host.com_handlecolon && c == ':' )
+ return true;
+
+ return false;
+}
+
+/*
+==============
+COM_IsWhiteSpace
+
+interpret symbol as whitespace
+==============
+*/
+
+static int COM_IsWhiteSpace( char space )
+{
+ if( space == ' ' || space == '\t' || space == '\r' || space == '\n' )
+ return 1;
+ return 0;
+}
+
+/*
+==============
COM_ParseFile
text parser
@@ -74,7 +107,7 @@ skipwhite:
}
// parse single characters
- if( c == '{' || c == '}' || c == ')' || c == '(' || c == '\'' || c == ',' )
+ if( COM_IsSingleChar( c ))
{
token[len] = c;
len++;
@@ -90,7 +123,7 @@ skipwhite:
len++;
c = ((byte)*data);
- if( c == '{' || c == '}' || c == ')' || c == '(' || c == '\'' || c == ',' )
+ if( COM_IsSingleChar( c ))
break;
} while( c > 32 );
@@ -100,6 +133,56 @@ skipwhite:
}
/*
+================
+COM_ParseVector
+
+================
+*/
+qboolean COM_ParseVector( char **pfile, float *v, size_t size )
+{
+ string token;
+ qboolean bracket = false;
+ char *saved;
+ uint i;
+
+ if( v == NULL || size == 0 )
+ return false;
+
+ memset( v, 0, sizeof( *v ) * size );
+
+ if( size == 1 )
+ {
+ *pfile = COM_ParseFile( *pfile, token );
+ v[0] = Q_atof( token );
+ return true;
+ }
+
+ saved = *pfile;
+
+ if(( *pfile = COM_ParseFile( *pfile, token )) == NULL )
+ return false;
+
+ if( token[0] == '(' )
+ bracket = true;
+ else *pfile = saved; // restore token to right get it again
+
+ for( i = 0; i < size; i++ )
+ {
+ *pfile = COM_ParseFile( *pfile, token );
+ v[i] = Q_atof( token );
+ }
+
+ if( !bracket ) return true; // done
+
+ if(( *pfile = COM_ParseFile( *pfile, token )) == NULL )
+ return false;
+
+ if( token[0] == ')' )
+ return true;
+ return false;
+}
+
+/*
=============
COM_FileSize
@@ -164,6 +247,39 @@ int COM_ExpandFilename( const char *fileName, char *nameOutBuffer, int nameOutBu
}
/*
+=============
+COM_TrimSpace
+
+trims all whitespace from the front
+and end of a string
+=============
+*/
+void COM_TrimSpace( const char *source, char *dest )
+{
+ int start, end, length;
+
+ start = 0;
+ end = Q_strlen( source );
+
+ while( source[start] && COM_IsWhiteSpace( source[start] ))
+ start++;
+ end--;
+
+ while( end > 0 && COM_IsWhiteSpace( source[end] ))
+ end--;
+ end++;
+
+ length = end - start;
+
+ if( length > 0 )
+ memcpy( dest, source + start, length );
+ else length = 0;
+
+ // terminate the dest string
+ dest[length] = 0;
+}
+
+/*
============
COM_FixSlashes
@@ -213,7 +329,6 @@ char *COM_MemFgets( byte *pMemFile, int fileSize, int *filePos, char *pBuffer, i
i++;
}
-
// if we actually advanced the pointer, copy it over
if( i != *filePos )
{
@@ -266,7 +381,8 @@ byte* COM_LoadFileForMe( const char *filename, int *pLength )
if( !filename || !*filename )
{
- if( pLength ) *pLength = 0;
+ if( pLength )
+ *pLength = 0;
return NULL;
}
@@ -279,8 +395,11 @@ byte* COM_LoadFileForMe( const char *filename, int *pLength )
if( pfile )
{
file = malloc( iLength + 1 );
- memcpy( file, pfile, iLength );
- file[iLength] = '\0';
+ if( file != NULL )
+ {
+ memcpy( file, pfile, iLength );
+ file[iLength] = '\0';
+ }
Mem_Free( pfile );
pfile = file;
}
@@ -296,34 +415,26 @@ COM_LoadFile
*/
byte *COM_LoadFile( const char *filename, int usehunk, int *pLength )
{
- string name;
- byte *file, *pfile;
- int iLength;
+ return COM_LoadFileForMe( filename, pLength );
+}
- ASSERT( usehunk == 5 );
+/*
+=============
+COM_LoadFile
+=============
+*/
+int COM_SaveFile( const char *filename, const void *data, long len )
+{
+ // check for empty filename
if( !filename || !*filename )
- {
- if( pLength ) *pLength = 0;
- return NULL;
- }
-
- Q_strncpy( name, filename, sizeof( name ));
- COM_FixSlashes( name );
+ return false;
- pfile = FS_LoadFile( name, &iLength, false );
- if( pLength ) *pLength = iLength;
-
- if( pfile )
- {
- file = malloc( iLength + 1 );
- memcpy( file, pfile, iLength );
- file[iLength] = '\0';
- Mem_Free( pfile );
- pfile = file;
- }
+ // check for null data
+ if( !data || len <= 0 )
+ return false;
- return pfile;
+ return FS_WriteFile( filename, data, len );
}
/*
@@ -339,6 +450,25 @@ void COM_FreeFile( void *buffer )
/*
=============
+COM_NormalizeAngles
+
+=============
+*/
+void COM_NormalizeAngles( vec3_t angles )
+{
+ int i;
+
+ for( i = 0; i < 3; i++ )
+ {
+ if( angles[i] > 180.0f )
+ angles[i] -= 360.0f;
+ else if( angles[i] < -180.0f )
+ angles[i] += 360.0f;
+ }
+}
+
+/*
+=============
pfnGetModelType
=============
@@ -376,42 +506,36 @@ pfnCvar_RegisterVariable
=============
*/
-cvar_t *pfnCvar_RegisterVariable( const char *szName, const char *szValue, int flags )
+cvar_t *pfnCvar_RegisterClientVariable( const char *szName, const char *szValue, int flags )
{
+ if( FBitSet( flags, FCVAR_GLCONFIG ))
+ return (cvar_t *)Cvar_Get( szName, szValue, flags, va( "enable or disable %s", szName ));
return (cvar_t *)Cvar_Get( szName, szValue, flags|CVAR_CLIENTDLL, "client cvar" );
}
/*
=============
-pfnCVarGetPointer
+pfnCvar_RegisterVariable
-can return NULL
=============
*/
-cvar_t *pfnCVarGetPointer( const char *szVarName )
+cvar_t *pfnCvar_RegisterGameUIVariable( const char *szName, const char *szValue, int flags )
{
- cvar_t *cvPtr;
-
- cvPtr = (cvar_t *)Cvar_FindVar( szVarName );
-
- return cvPtr;
+ if( FBitSet( flags, FCVAR_GLCONFIG ))
+ return (cvar_t *)Cvar_Get( szName, szValue, flags, va( "enable or disable %s", szName ));
+ return (cvar_t *)Cvar_Get( szName, szValue, flags|CVAR_GAMEUIDLL, "GameUI cvar" );
}
/*
=============
-pfnAddClientCommand
+pfnCVarGetPointer
+can return NULL
=============
*/
-int pfnAddClientCommand( const char *cmd_name, xcommand_t func )
+cvar_t *pfnCVarGetPointer( const char *szVarName )
{
- if( !cmd_name || !*cmd_name )
- return 0;
-
- // NOTE: if( func == NULL ) cmd will be forwarded to a server
- Cmd_AddClientCommand( cmd_name, func );
-
- return 1;
+ return (cvar_t *)Cvar_FindVar( szVarName );
}
/*
@@ -429,7 +553,7 @@ void Con_Printf( char *szFmt, ... )
return;
va_start( args, szFmt );
- Q_vsnprintf( buffer, 16384, szFmt, args );
+ Q_vsnprintf( buffer, sizeof( buffer ), szFmt, args );
va_end( args );
Sys_Print( buffer );
@@ -450,7 +574,7 @@ void Con_DPrintf( char *szFmt, ... )
return;
va_start( args, szFmt );
- Q_vsnprintf( buffer, 16384, szFmt, args );
+ Q_vsnprintf( buffer, sizeof( buffer ), szFmt, args );
va_end( args );
Sys_Print( buffer );
diff --git b/engine/common/common.h a/engine/common/common.h
index 7fcf37f..6149c0b 100644
--- b/engine/common/common.h
+++ a/engine/common/common.h
@@ -36,9 +36,15 @@ extern "C" {
#define MAX_STRING 256 // generic string
#define MAX_INFO_STRING 256 // infostrings are transmitted across network
#define MAX_SYSPATH 1024 // system filepath
+#define MAX_PRINT_MSG 8192 // how many symbols can handle single call of Msg or MsgDev
#define MAX_MODS 512 // environment games that engine can keep visible
#define EXPORT __declspec( dllexport )
#define BIT( n ) (1<<( n ))
+#define GAMMA ( 2.2 ) // Valve Software gamma
+#define INVGAMMA ( 1.0 / 2.2 ) // back to 1.0
+#define SetBits( iBitVector, bits ) ((iBitVector) = (iBitVector) | (bits))
+#define ClearBits( iBitVector, bits ) ((iBitVector) = (iBitVector) & ~(bits))
+#define FBitSet( iBitVector, bit ) ((iBitVector) & (bit))
#ifndef __cplusplus
#define NULL ((void *)0)
@@ -53,7 +59,6 @@ extern "C" {
typedef unsigned long dword;
typedef unsigned int uint;
typedef char string[MAX_STRING];
-typedef long fs_offset_t;
typedef struct file_s file_t; // normal file
typedef struct wfile_s wfile_t; // wad file
typedef struct stream_s stream_t; // sound stream for background music playing
@@ -70,7 +75,7 @@ enum
D_INFO = 1, // "-dev 1", shows various system messages
D_WARN, // "-dev 2", shows not critical system warnings
D_ERROR, // "-dev 3", shows critical warnings
- D_AICONSOLE, // "-dev 4", special case for game aiconsole
+ D_REPORT, // "-dev 4", special case for game reports
D_NOTE // "-dev 5", show system notifications for engine developers
};
@@ -88,12 +93,19 @@ typedef enum
#define XASH_VERSION 0.98f // engine current version
// PERFORMANCE INFO
-#define MIN_FPS 15.0 // host minimum fps value for maxfps.
+#define MIN_FPS 20.0 // host minimum fps value for maxfps.
#define MAX_FPS 500.0 // upper limit for maxfps.
#define MAX_FRAMETIME 0.1
#define MIN_FRAMETIME 0.000001
+// HOST_FIXED_FRAMERATE stuff
+#define HOST_MINFPS 20.0
+#define HOST_MAXFPS 72.0
+#define GAME_FPS 20.0
+#define HOST_FPS 60.0 // client and the server clamped at 60.0 fps max. Render clamped at fps_max cvar
+#define HOST_FRAMETIME ( 1.0 / HOST_FPS )
+
#define MAX_CMD_TOKENS 80 // cmd tokens
#define MAX_ENTNUMBER 99999 // for server and client parsing
#define MAX_HEARTBEAT -99999 // connection time
@@ -133,6 +145,7 @@ extern convar_t *scr_width;
extern convar_t *scr_height;
extern convar_t *scr_loading;
extern convar_t *scr_download;
+extern convar_t *cmd_scripting;
extern convar_t *cl_allow_levelshots;
extern convar_t *mod_allow_materials;
extern convar_t *host_limitlocal;
@@ -214,7 +227,6 @@ typedef enum
HOST_ERR_FATAL, // sys error
HOST_SLEEP, // sleeped by different reason, e.g. minimize window
HOST_NOFOCUS, // same as HOST_FRAME, but disable mouse
- HOST_RESTART, // during the changes video mode
HOST_CRASHED // an exception handler called
} host_state;
@@ -267,6 +279,11 @@ typedef struct host_redirect_s
void (*flush)( netadr_t adr, rdtype_t target, char *buffer );
} host_redirect_t;
+// local flags (never sending acorss the net)
+#define SND_LOCALSOUND (1<<9) // not paused, not looped, for internal use
+#define SND_STOP_LOOPING (1<<10) // stop all looping sounds on the entity.
+#define SND_FILTER_CLIENT (1<<11) // don't send sound from local player if prediction was enabled
+
typedef struct
{
char name[64];
@@ -315,6 +332,7 @@ typedef struct host_parm_s
qboolean key_overstrike; // key overstrike mode
qboolean stuffcmdsrun; // execute stuff commands
qboolean con_showalways; // show console always (developer and dedicated)
+ qboolean com_handlecolon; // allow COM_ParseFile to handle colon as single char
qboolean change_game; // initialize when game is changed
qboolean mouse_visible; // vgui override cursor control
qboolean input_enabled; // vgui override mouse & keyboard input
@@ -338,9 +356,6 @@ typedef struct host_parm_s
struct decallist_s *decalList; // used for keep decals, when renderer is restarted or changed
int numdecals;
-
- soundlist_t *soundList; // used for keep ambient sounds, when renderer or sound is restarted
- int numsounds;
} host_parm_t;
extern host_parm_t host;
@@ -361,43 +376,45 @@ void FS_LoadGameInfo( const char *rootfolder );
void FS_FileBase( const char *in, char *out );
const char *FS_FileExtension( const char *in );
void FS_DefaultExtension( char *path, const char *extension );
-void FS_ExtractFilePath( const char* const path, char* dest );
+void FS_ExtractFilePath( const char *path, char *dest );
const char *FS_GetDiskPath( const char *name, qboolean gamedironly );
const char *FS_FileWithoutPath( const char *in );
-wfile_t *W_Open( const char *filename, const char *mode );
+wfile_t *W_Open( const char *filename, const char *mode, int *errorcode );
byte *W_LoadLump( wfile_t *wad, const char *lumpname, size_t *lumpsizeptr, const char type );
void W_Close( wfile_t *wad );
-file_t *FS_OpenFile( const char *path, fs_offset_t *filesizeptr, qboolean gamedironly );
-byte *FS_LoadFile( const char *path, fs_offset_t *filesizeptr, qboolean gamedironly );
-qboolean FS_WriteFile( const char *filename, const void *data, fs_offset_t len );
+file_t *FS_OpenFile( const char *path, long *filesizeptr, qboolean gamedironly );
+byte *FS_LoadFile( const char *path, long *filesizeptr, qboolean gamedironly );
+qboolean FS_WriteFile( const char *filename, const void *data, long len );
+qboolean COM_ParseVector( char **pfile, float *v, size_t size );
+void COM_NormalizeAngles( vec3_t angles );
int COM_FileSize( const char *filename );
void COM_FixSlashes( char *pname );
void COM_FreeFile( void *buffer );
int COM_CompareFileTime( const char *filename1, const char *filename2, int *iCompare );
search_t *FS_Search( const char *pattern, int caseinsensitive, int gamedironly );
file_t *FS_Open( const char *filepath, const char *mode, qboolean gamedironly );
-fs_offset_t FS_Write( file_t *file, const void *data, size_t datasize );
-fs_offset_t FS_Read( file_t *file, void *buffer, size_t buffersize );
+long FS_Write( file_t *file, const void *data, size_t datasize );
+long FS_Read( file_t *file, void *buffer, size_t buffersize );
int FS_VPrintf( file_t *file, const char *format, va_list ap );
-int FS_Seek( file_t *file, fs_offset_t offset, int whence );
+int FS_Seek( file_t *file, long offset, int whence );
int FS_Gets( file_t *file, byte *string, size_t bufsize );
int FS_Printf( file_t *file, const char *format, ... );
-fs_offset_t FS_FileSize( const char *filename, qboolean gamedironly );
-fs_offset_t FS_FileTime( const char *filename, qboolean gamedironly );
+long FS_FileSize( const char *filename, qboolean gamedironly );
+long FS_FileTime( const char *filename, qboolean gamedironly );
int FS_Print( file_t *file, const char *msg );
qboolean FS_Rename( const char *oldname, const char *newname );
qboolean FS_FileExists( const char *filename, qboolean gamedironly );
-void FS_FileCopy( file_t *pOutput, file_t *pInput, int fileSize );
+qboolean FS_FileCopy( file_t *pOutput, file_t *pInput, int fileSize );
qboolean FS_Delete( const char *path );
int FS_UnGetc( file_t *file, byte c );
void FS_StripExtension( char *path );
-fs_offset_t FS_Tell( file_t *file );
+long FS_Tell( file_t *file );
qboolean FS_Eof( file_t *file );
void FS_Purge( file_t *file );
int FS_Close( file_t *file );
int FS_Getc( file_t *file );
qboolean FS_Eof( file_t *file );
-fs_offset_t FS_FileLength( file_t *f );
+long FS_FileLength( file_t *f );
//
// network.c
@@ -424,6 +441,7 @@ typically expanded to rgba buffer
NOTE: number at end of pixelformat name it's a total bitscount e.g. PF_RGB_24 == PF_RGB_888
========================================================================
*/
+#define ImageRAW( type ) (type == PF_RGBA_32 || type == PF_BGRA_32 || type == PF_RGB_24 || type == PF_BGR_24)
#define ImageDXT( type ) (type == PF_DXT1 || type == PF_DXT3 || type == PF_DXT5)
typedef enum
@@ -435,9 +453,9 @@ typedef enum
PF_BGRA_32, // big endian RGBA (MacOS)
PF_RGB_24, // uncompressed dds or another 24-bit image
PF_BGR_24, // big-endian RGB (MacOS)
- PF_DXT1, // nvidia DXT1 format
- PF_DXT3, // nvidia DXT3 format
- PF_DXT5, // nvidia DXT5 format
+ PF_DXT1, // s3tc DXT1 format
+ PF_DXT3, // s3tc DXT3 format
+ PF_DXT5, // s3tc DXT5 format
PF_TOTALCOUNT, // must be last
} pixformat_t;
@@ -483,6 +501,7 @@ typedef enum
IMAGE_SKYBOX = BIT(5), // only used by FS_SaveImage - for write right suffixes
IMAGE_QUAKESKY = BIT(6), // it's a quake sky double layered clouds (so keep it as 8 bit)
IMAGE_DDS_FORMAT = BIT(7), // a hint for GL loader
+ IMAGE_MULTILAYER = BIT(8), // to differentiate from 3D texture
// Image_Process manipulation flags
IMAGE_FLIP_X = BIT(16), // flip the image by width
@@ -679,15 +698,17 @@ qboolean SV_Active( void );
==============================================================
*/
-cvar_t *pfnCvar_RegisterVariable( const char *szName, const char *szValue, int flags );
+cvar_t *pfnCvar_RegisterClientVariable( const char *szName, const char *szValue, int flags );
+cvar_t *pfnCvar_RegisterGameUIVariable( const char *szName, const char *szValue, int flags );
char *COM_MemFgets( byte *pMemFile, int fileSize, int *filePos, char *pBuffer, int bufferSize );
+int COM_SaveFile( const char *filename, const void *data, long len );
byte* COM_LoadFileForMe( const char *filename, int *pLength );
cvar_t *pfnCVarGetPointer( const char *szVarName );
int pfnDrawConsoleString( int x, int y, char *string );
void pfnDrawSetTextColor( float r, float g, float b );
void pfnDrawConsoleStringLen( const char *pText, int *length, int *height );
-int pfnAddClientCommand( const char *cmd_name, xcommand_t func );
void *Cache_Check( byte *mempool, struct cache_user_s *c );
+void COM_TrimSpace( const char *source, char *dest );
edict_t* pfnPEntityOfEntIndex( int iEntIndex );
void pfnGetModelBounds( model_t *mod, float *mins, float *maxs );
void pfnGetGameDir( char *szGetGameDir );
@@ -773,7 +794,7 @@ long AVI_GetVideoFrameNumber( movie_state_t *Avi, float time );
byte *AVI_GetVideoFrame( movie_state_t *Avi, long frame );
qboolean AVI_GetVideoInfo( movie_state_t *Avi, long *xres, long *yres, float *duration );
qboolean AVI_GetAudioInfo( movie_state_t *Avi, wavdata_t *snd_info );
-fs_offset_t AVI_GetAudioChunk( movie_state_t *Avi, char *audiodata, long offset, long length );
+long AVI_GetAudioChunk( movie_state_t *Avi, char *audiodata, long offset, long length );
void AVI_OpenVideo( movie_state_t *Avi, const char *filename, qboolean load_audio, qboolean ignore_hwgamma, int quiet );
movie_state_t *AVI_LoadVideo( const char *filename, qboolean load_audio, qboolean ignore_hwgamma );
movie_state_t *AVI_LoadVideoNoSound( const char *filename, qboolean ignore_hwgamma );
@@ -803,12 +824,13 @@ void COM_AddAppDirectoryToSearchPath( const char *pszBaseDir, const char *appNam
int COM_ExpandFilename( const char *fileName, char *nameOutBuffer, int nameOutBufferSize );
struct pmtrace_s *PM_TraceLine( float *start, float *end, int flags, int usehull, int ignore_pe );
void SV_StartSound( edict_t *ent, int chan, const char *sample, float vol, float attn, int flags, int pitch );
-void SV_StartMusic( const char *curtrack, const char *looptrack, fs_offset_t position );
+void SV_StartMusic( const char *curtrack, const char *looptrack, long position );
void SV_CreateDecal( struct sizebuf_s *msg, const float *origin, int decalIndex, int entityIndex, int modelIndex, int flags, float scale );
void SV_CreateStudioDecal( struct sizebuf_s *msg, const float *origin, const float *start, int decalIndex, int entityIndex, int modelIndex,
int flags, struct modelstate_s *state );
struct sizebuf_s *SV_GetReliableDatagram( void );
qboolean SV_RestoreCustomDecal( struct decallist_s *entry, edict_t *pEdict, qboolean adjacent );
+void SV_BroadcastPrintf( struct sv_client_s *ignore, int level, char *fmt, ... );
int R_CreateDecalList( struct decallist_s *pList, qboolean changelevel );
void R_ClearAllDecals( void );
void R_ClearStaticEntities( void );
diff --git b/engine/common/con_utils.c a/engine/common/con_utils.c
index 115ebae..2ce6ef3 100644
--- b/engine/common/con_utils.c
+++ a/engine/common/con_utils.c
@@ -67,7 +67,7 @@ qboolean Cmd_GetMapList( const char *s, char *completedname, int length )
int ver = -1, mapver = -1, lumpofs = 0, lumplen = 0;
const char *ext = FS_FileExtension( t->filenames[i] );
char *ents = NULL, *pfile;
- qboolean paranoia = false;
+ int version = 0;
qboolean gearbox = false;
if( Q_stricmp( ext, "bsp" )) continue;
@@ -108,8 +108,7 @@ qboolean Cmd_GetMapList( const char *s, char *completedname, int length )
hdrext = (dextrahdr_t *)((byte *)buf + sizeof( dheader31_t ));
else hdrext = (dextrahdr_t *)((byte *)buf + sizeof( dheader_t ));
- if( hdrext->id == IDEXTRAHEADER && hdrext->version == EXTRA_VERSION )
- paranoia = true;
+ if( hdrext->id == IDEXTRAHEADER ) version = hdrext->version;
Q_strncpy( entfilename, t->filenames[i], sizeof( entfilename ));
FS_StripExtension( entfilename );
@@ -163,11 +162,17 @@ qboolean Cmd_GetMapList( const char *s, char *completedname, int length )
break;
case HLBSP_VERSION:
if( gearbox ) Q_strncpy( buf, "Blue-Shift", sizeof( buf ));
- else if( paranoia ) Q_strncpy( buf, "Paranoia 2", sizeof( buf ));
+ else if( version == 1 ) Q_strncpy( buf, "XashXT old format", sizeof( buf ));
+ else if( version == 2 ) Q_strncpy( buf, "Paranoia 2: Savior", sizeof( buf ));
+ else if( version == 3 ) Q_strncpy( buf, "not supported", sizeof( buf ));
+ else if( version == 4 ) Q_strncpy( buf, "Half-Life extended", sizeof( buf ));
else Q_strncpy( buf, "Half-Life", sizeof( buf ));
break;
case XTBSP_VERSION:
- if( paranoia ) Q_strncpy( buf, "Paranoia 2", sizeof( buf ));
+ if( version == 1 ) Q_strncpy( buf, "XashXT old format", sizeof( buf ));
+ else if( version == 2 ) Q_strncpy( buf, "Paranoia 2: Savior", sizeof( buf ));
+ else if( version == 3 ) Q_strncpy( buf, "not supported", sizeof( buf ));
+ else if( version == 4 ) Q_strncpy( buf, "Xash3D extended", sizeof( buf ));
else Q_strncpy( buf, "Xash3D", sizeof( buf ));
break;
default: Q_strncpy( buf, "??", sizeof( buf )); break;
@@ -304,8 +309,8 @@ qboolean Cmd_GetMusicList( const char *s, char *completedname, int length )
{
const char *ext = FS_FileExtension( t->filenames[i] );
- if( !Q_stricmp( ext, "wav" ) || !Q_stricmp( ext, "mp3" ));
- else continue;
+ if( Q_stricmp( ext, "wav" ) && Q_stricmp( ext, "mp3" ))
+ continue;
FS_FileBase( t->filenames[i], matchbuf );
Msg( "%16s\n", matchbuf );
@@ -563,46 +568,40 @@ qboolean Cmd_GetCustomList( const char *s, char *completedname, int length )
/*
=====================================
-Cmd_GetTexturemodes
+Cmd_GetGameList
-Prints or complete sound filename
+Prints or complete gamedir name
=====================================
*/
-qboolean Cmd_GetTexturemodes( const char *s, char *completedname, int length )
+qboolean Cmd_GetGamesList( const char *s, char *completedname, int length )
{
- int i, numtexturemodes;
- string texturemodes[6]; // keep an actual ( sizeof( gl_texturemode) / sizeof( gl_texturemode[0] ))
+ int i, numgamedirs;
+ string gamedirs[MAX_MODS];
string matchbuf;
- const char *gl_texturemode[] =
- {
- "GL_LINEAR",
- "GL_LINEAR_MIPMAP_LINEAR",
- "GL_LINEAR_MIPMAP_NEAREST",
- "GL_NEAREST",
- "GL_NEAREST_MIPMAP_LINEAR",
- "GL_NEAREST_MIPMAP_NEAREST",
- };
+ // stand-alone games doesn't have cmd "game"
+ if( !Cmd_Exists( "game" ))
+ return false;
// compare gamelist with current keyword
- for( i = 0, numtexturemodes = 0; i < 6; i++ )
+ for( i = 0, numgamedirs = 0; i < SI.numgames; i++ )
{
- if(( *s == '*' ) || !Q_strnicmp( gl_texturemode[i], s, Q_strlen( s )))
- Q_strcpy( texturemodes[numtexturemodes++], gl_texturemode[i] );
+ if(( *s == '*' ) || !Q_strnicmp( SI.games[i]->gamefolder, s, Q_strlen( s )))
+ Q_strcpy( gamedirs[numgamedirs++], SI.games[i]->gamefolder );
}
- if( !numtexturemodes ) return false;
- Q_strncpy( matchbuf, gl_texturemode[0], MAX_STRING );
+ if( !numgamedirs ) return false;
+ Q_strncpy( matchbuf, gamedirs[0], MAX_STRING );
if( completedname && length ) Q_strncpy( completedname, matchbuf, length );
- if( numtexturemodes == 1 ) return true;
+ if( numgamedirs == 1 ) return true;
- for( i = 0; i < numtexturemodes; i++ )
+ for( i = 0; i < numgamedirs; i++ )
{
- Q_strncpy( matchbuf, texturemodes[i], MAX_STRING );
+ Q_strncpy( matchbuf, gamedirs[i], MAX_STRING );
Msg( "%16s\n", matchbuf );
}
- Msg( "\n^3 %i filters found.\n", numtexturemodes );
+ Msg( "\n^3 %i games found.\n", numgamedirs );
// cut shortestMatch to the amount common with s
if( completedname && length )
@@ -618,40 +617,48 @@ qboolean Cmd_GetTexturemodes( const char *s, char *completedname, int length )
/*
=====================================
-Cmd_GetGameList
+Cmd_GetCDList
-Prints or complete gamedir name
+Prints or complete CD command name
=====================================
*/
-qboolean Cmd_GetGamesList( const char *s, char *completedname, int length )
+qboolean Cmd_GetCDList( const char *s, char *completedname, int length )
{
- int i, numgamedirs;
- string gamedirs[MAX_MODS];
+ int i, numcdcommands;
+ string cdcommands[8];
string matchbuf;
- // stand-alone games doesn't have cmd "game"
- if( !Cmd_Exists( "game" ))
- return false;
+ const char *cd_command[] =
+ {
+ "info",
+ "loop",
+ "off",
+ "on",
+ "pause",
+ "play",
+ "resume",
+ "stop",
+ };
- // compare gamelist with current keyword
- for( i = 0, numgamedirs = 0; i < SI.numgames; i++ )
+ // compare CD command list with current keyword
+ for( i = 0, numcdcommands = 0; i < 8; i++ )
{
- if(( *s == '*' ) || !Q_strnicmp( SI.games[i]->gamefolder, s, Q_strlen( s )))
- Q_strcpy( gamedirs[numgamedirs++], SI.games[i]->gamefolder );
+ if(( *s == '*' ) || !Q_strnicmp( cd_command[i], s, Q_strlen( s )))
+ Q_strcpy( cdcommands[numcdcommands++], cd_command[i] );
}
- if( !numgamedirs ) return false;
- Q_strncpy( matchbuf, gamedirs[0], MAX_STRING );
+ if( !numcdcommands ) return false;
+ Q_strncpy( matchbuf, cdcommands[0], MAX_STRING );
if( completedname && length ) Q_strncpy( completedname, matchbuf, length );
- if( numgamedirs == 1 ) return true;
+ if( numcdcommands == 1 ) return true;
- for( i = 0; i < numgamedirs; i++ )
+ for( i = 0; i < numcdcommands; i++ )
{
- Q_strncpy( matchbuf, gamedirs[i], MAX_STRING );
+ Q_strncpy( matchbuf, cdcommands[i], MAX_STRING );
Msg( "%16s\n", matchbuf );
}
- Msg( "\n^3 %i games found.\n", numgamedirs );
+ Msg( "\n^3 %i commands found.\n", numcdcommands );
// cut shortestMatch to the amount common with s
if( completedname && length )
@@ -815,7 +822,6 @@ qboolean Cmd_CheckMapsList( qboolean fRefresh )
autocomplete_list_t cmd_list[] =
{
-{ "gl_texturemode", Cmd_GetTexturemodes },
{ "map_background", Cmd_GetMapList },
{ "changelevel", Cmd_GetMapList },
{ "playdemo", Cmd_GetDemoList, },
@@ -832,6 +838,7 @@ autocomplete_list_t cmd_list[] =
{ "load", Cmd_GetSavesList },
{ "play", Cmd_GetSoundList },
{ "map", Cmd_GetMapList },
+{ "cd", Cmd_GetCDList },
{ NULL }, // termiantor
};
diff --git b/engine/common/console.c a/engine/common/console.c
index c4fa2e6..d7e1e9a 100644
--- b/engine/common/console.c
+++ a/engine/common/console.c
@@ -25,26 +25,31 @@ convar_t *con_notifytime;
convar_t *scr_conspeed;
convar_t *con_fontsize;
-#define CON_TIMES 5 // need for 4 lines
+#define CON_TIMES 4 // notify lines
#define COLOR_DEFAULT '7'
#define CON_HISTORY 64
#define MAX_DBG_NOTIFY 128
#define CON_MAXCMDS 4096 // auto-complete intermediate list
#define CON_NUMFONTS 3 // maxfonts
-#define CON_TEXTSIZE 131072 // 128 kb buffer
+#define CON_LINES( i ) (con.lines[(con.lines_first + (i)) % con.maxlines])
+#define CON_LINES_COUNT con.lines_count
+#define CON_LINES_LAST() CON_LINES( CON_LINES_COUNT - 1 )
+
+#define CON_TEXTSIZE 1048576 // max scrollback buffer characters in console (1 Mb)
+#define CON_MAXLINES 16384 // max scrollback buffer lines in console
// console color typeing
rgba_t g_color_table[8] =
{
-{ 0, 0, 0, 255}, // black
-{255, 0, 0, 255}, // red
-{ 0, 255, 0, 255}, // green
-{255, 255, 0, 255}, // yellow
-{ 0, 0, 255, 255}, // blue
-{ 0, 255, 255, 255}, // cyan
-{255, 0, 255, 255}, // magenta
-{240, 180, 24, 255}, // default color (can be changed by user)
+{ 0, 0, 0, 255 }, // black
+{ 255, 0, 0, 255 }, // red
+{ 0, 255, 0, 255 }, // green
+{ 255, 255, 0, 255 }, // yellow
+{ 0, 0, 255, 255 }, // blue
+{ 0, 255, 255, 255 }, // cyan
+{ 255, 0, 255, 255 }, // magenta
+{ 240, 180, 24, 255 }, // default color (can be changed by user)
};
typedef struct
@@ -63,29 +68,38 @@ typedef struct
int key_dest;
} notify_t;
+typedef struct con_lineinfo_s
+{
+ char *start;
+ size_t length;
+ double addtime; // notify stuff
+} con_lineinfo_t;
+
typedef struct
{
qboolean initialized;
- short text[CON_TEXTSIZE];
- int current; // line where next message will be printed
- int display; // bottom of console displays this line
- int x; // offset in current line for next print
+ // conbuffer
+ char *buffer; // common buffer for all console lines
+ int bufsize; // CON_TEXSIZE
+ con_lineinfo_t *lines; // console lines
+ int maxlines; // CON_MAXLINES
- int linewidth; // characters across screen
- int totallines; // total lines in console scrollback
+ int lines_first; // cyclic buffer
+ int lines_count;
- float displayFrac; // aproaches finalFrac at scr_conspeed
- float finalFrac; // 0.0 to 1.0 lines of console to display
+ // console scroll
+ int backscroll; // lines up from bottom to display
+ int linewidth; // characters across screen
+ // console animation
+ int showlines; // how many lines we should display
int vislines; // in scanlines
- double times[CON_TIMES]; // host.realtime the line was generated for transparent notify lines
- rgba_t color;
// console images
int background; // console background
- // conchars
+ // console fonts
cl_font_t chars[CON_NUMFONTS];// fonts.wad/font1.fnt
cl_font_t *curFont, *lastUsedFont;
@@ -108,6 +122,7 @@ typedef struct
string shortestMatch;
field_t *completionField; // con.input or dedicated server fake field-line
char *completionString;
+ char *completionBuffer;
char *cmds[CON_MAXCMDS];
int matchCount;
} console_t;
@@ -123,11 +138,8 @@ Con_Clear_f
*/
void Con_Clear_f( void )
{
- int i;
-
- for( i = 0; i < CON_TEXTSIZE; i++ )
- con.text[i] = ( ColorIndex( COLOR_DEFAULT ) << 8 ) | ' ';
- con.display = con.current; // go to end
+ con.lines_count = 0;
+ con.backscroll = 0; // go to end
}
/*
@@ -168,8 +180,8 @@ void Con_ClearNotify( void )
{
int i;
- for( i = 0; i < CON_TIMES; i++ )
- con.times[i] = 0;
+ for( i = 0; i < CON_LINES_COUNT; i++ )
+ CON_LINES( i ).addtime = 0.0;
}
/*
@@ -272,9 +284,8 @@ Con_ToggleConsole_f
*/
void Con_ToggleConsole_f( void )
{
- if( !host.developer ) return; // disabled
-
- if( UI_CreditsActive( )) return; // disabled by final credits
+ if( !host.developer || UI_CreditsActive( ))
+ return; // disabled
// show console only in game or by special call from menu
if( cls.state != ca_active || cls.key_dest == key_menu )
@@ -298,69 +309,157 @@ void Con_ToggleConsole_f( void )
/*
================
-Con_CheckResize
+Con_FixTimes
-If the line width has changed, reformat the buffer.
+Notifies the console code about the current time
+(and shifts back times of other entries when the time
+went backwards)
================
*/
-void Con_CheckResize( void )
+void Con_FixTimes( void )
{
- int i, j, width, numlines, numchars;
- int oldwidth, oldtotallines;
- short tbuf[CON_TEXTSIZE];
- int charWidth = 8;
+ double diff;
+ int i;
- if( con.curFont && con.curFont->hFontTexture )
- charWidth = con.curFont->charWidths['M'] - 1;
+ if( con.lines_count <= 0 ) return;
- width = ( scr_width->integer / charWidth );
+ diff = cl.time - CON_LINES_LAST().addtime;
+ if( diff >= 0.0 ) return; // nothing to fix
- if( width == con.linewidth )
+ for( i = 0; i < con.lines_count; i++ )
+ CON_LINES( i ).addtime += diff;
+}
+
+/*
+================
+Con_DeleteLine
+
+Deletes the first line from the console history.
+================
+*/
+void Con_DeleteLine( void )
+{
+ if( con.lines_count == 0 )
return;
+ con.lines_count--;
+ con.lines_first = (con.lines_first + 1) % con.maxlines;
+}
- if( !glw_state.initialized )
- {
- // video hasn't been initialized yet
- con.linewidth = width;
- con.totallines = CON_TEXTSIZE / con.linewidth;
+/*
+================
+Con_DeleteLastLine
+
+Deletes the last line from the console history.
+================
+*/
+void Con_DeleteLastLine( void )
+{
+ if( con.lines_count == 0 )
+ return;
+ con.lines_count--;
+}
- for( i = 0; i < CON_TEXTSIZE; i++ )
- con.text[i] = ( ColorIndex( COLOR_DEFAULT ) << 8 ) | ' ';
+/*
+================
+Con_BytesLeft
+
+Checks if there is space for a line of the given length, and if yes, returns a
+pointer to the start of such a space, and NULL otherwise.
+================
+*/
+static char *Con_BytesLeft( int length )
+{
+ if( length > con.bufsize )
+ return NULL;
+
+ if( con.lines_count == 0 )
+ {
+ return con.buffer;
}
else
{
- oldwidth = con.linewidth;
- con.linewidth = width;
- oldtotallines = con.totallines;
- con.totallines = CON_TEXTSIZE / con.linewidth;
- numlines = oldtotallines;
+ char *firstline_start = con.lines[con.lines_first].start;
+ char *lastline_onepastend = CON_LINES_LAST().start + CON_LINES_LAST().length;
- if( con.totallines < numlines )
- numlines = con.totallines;
+ // the buffer is cyclic, so we first have two cases...
+ if( firstline_start < lastline_onepastend ) // buffer is contiguous
+ {
+ // put at end?
+ if( length <= con.buffer + con.bufsize - lastline_onepastend )
+ return lastline_onepastend;
+ // put at beginning?
+ else if( length <= firstline_start - con.buffer )
+ return con.buffer;
+
+ return NULL;
+ }
+ else
+ {
+ // buffer has a contiguous hole
+ if( length <= firstline_start - lastline_onepastend )
+ return lastline_onepastend;
- numchars = oldwidth;
-
- if( con.linewidth < numchars )
- numchars = con.linewidth;
+ return NULL;
+ }
+ }
+}
- memcpy( tbuf, con.text, CON_TEXTSIZE * sizeof( short ));
+/*
+================
+Con_AddLine
- for( i = 0; i < CON_TEXTSIZE; i++ )
- con.text[i] = ( ColorIndex( COLOR_DEFAULT ) << 8 ) | ' ';
+Appends a given string as a new line to the console.
+================
+*/
+void Con_AddLine( const char *line, int length )
+{
+ byte *putpos;
+ con_lineinfo_t *p;
- for( i = 0; i < numlines; i++ )
- {
- for( j = 0; j < numchars; j++ )
- {
- con.text[(con.totallines - 1 - i) * con.linewidth + j] =
- tbuf[((con.current - i + oldtotallines) % oldtotallines) * oldwidth + j + con.x];
- }
- }
- Con_ClearNotify ();
- }
+ if( !con.initialized ) return;
+
+ Con_FixTimes();
+ length++; // reserve space for term
+
+ ASSERT( length < CON_TEXTSIZE );
+
+ while( !( putpos = Con_BytesLeft( length )) || con.lines_count >= con.maxlines )
+ Con_DeleteLine();
- con.current = con.totallines - 1;
- con.display = con.current;
+ memcpy( putpos, line, length );
+ putpos[length - 1] = '\0';
+ con.lines_count++;
+
+ p = &CON_LINES_LAST();
+ p->start = putpos;
+ p->length = length;
+ p->addtime = cl.time;
+}
+
+/*
+================
+Con_CheckResize
+
+If the line width has changed, reformat the buffer.
+================
+*/
+void Con_CheckResize( void )
+{
+ int charWidth = 8;
+ int i, width;
+
+ if( con.curFont && con.curFont->hFontTexture )
+ charWidth = con.curFont->charWidths['M'] - 1;
+
+ width = ( scr_width->integer / charWidth ) - 2;
+ if( !glw_state.initialized ) width = 78;
+
+ if( width == con.linewidth )
+ return;
+
+ Con_ClearNotify();
+ con.linewidth = width;
+ con.backscroll = 0;
con.input.widthInChars = con.linewidth;
@@ -375,10 +474,7 @@ Con_PageUp
*/
void Con_PageUp( int lines )
{
- con.display -= abs( lines );
-
- if( con.current - con.display >= con.totallines )
- con.display = con.current - con.totallines + 1;
+ con.backscroll += abs( lines );
}
/*
@@ -388,10 +484,7 @@ Con_PageDown
*/
void Con_PageDown( int lines )
{
- con.display += abs( lines );
-
- if( con.display > con.current )
- con.display = con.current;
+ con.backscroll -= abs( lines );
}
/*
@@ -401,10 +494,7 @@ Con_Top
*/
void Con_Top( void )
{
- con.display = con.totallines;
-
- if( con.current - con.display >= con.totallines )
- con.display = con.current - con.totallines + 1;
+ con.backscroll = CON_MAXLINES;
}
/*
@@ -414,7 +504,7 @@ Con_Bottom
*/
void Con_Bottom( void )
{
- con.display = con.current;
+ con.backscroll = 0;
}
/*
@@ -424,7 +514,7 @@ Con_Visible
*/
qboolean Con_Visible( void )
{
- return (con.displayFrac != 0.0f);
+ return (con.vislines > 0);
}
/*
@@ -436,8 +526,6 @@ static void Con_LoadConsoleFont( int fontNumber, cl_font_t *font )
{
int fontWidth;
- ASSERT( font != NULL );
-
if( font->valid ) return; // already loaded
// loading conchars
@@ -501,6 +589,36 @@ static void Con_LoadConchars( void )
}
+/*
+====================
+Con_TextAdjustSize
+
+draw charcters routine
+====================
+*/
+static void Con_TextAdjustSize( int *x, int *y, int *w, int *h )
+{
+ float xscale, yscale;
+
+ if( !x && !y && !w && !h ) return;
+
+ // scale for screen sizes
+ xscale = scr_width->integer / (float)clgame.scrInfo.iWidth;
+ yscale = scr_height->integer / (float)clgame.scrInfo.iHeight;
+
+ if( x ) *x *= xscale;
+ if( y ) *y *= yscale;
+ if( w ) *w *= xscale;
+ if( h ) *h *= yscale;
+}
+
+/*
+====================
+Con_DrawGenericChar
+
+draw console single character
+====================
+*/
static int Con_DrawGenericChar( int x, int y, int number, rgba_t color )
{
int width, height;
@@ -529,36 +647,73 @@ static int Con_DrawGenericChar( int x, int y, int number, rgba_t color )
width = rc->right - rc->left;
height = rc->bottom - rc->top;
- TextAdjustSize( &x, &y, &width, &height );
+ if( clgame.ds.adjust_size )
+ Con_TextAdjustSize( &x, &y, &width, &height );
R_DrawStretchPic( x, y, width, height, s1, t1, s2, t2, con.curFont->hFontTexture );
pglColor4ub( 255, 255, 255, 255 ); // don't forget reset color
return con.curFont->charWidths[number];
}
+/*
+====================
+Con_SetFont
+
+choose font size
+====================
+*/
void Con_SetFont( int fontNum )
{
fontNum = bound( 0, fontNum, 2 );
con.curFont = &con.chars[fontNum];
}
+/*
+====================
+Con_RestoreFont
+
+restore auto-selected console font
+(that based on screen resolution)
+====================
+*/
void Con_RestoreFont( void )
{
con.curFont = con.lastUsedFont;
}
+/*
+====================
+Con_DrawCharacter
+
+client version of routine
+====================
+*/
int Con_DrawCharacter( int x, int y, int number, rgba_t color )
{
GL_SetRenderMode( kRenderTransTexture );
return Con_DrawGenericChar( x, y, number, color );
}
+/*
+====================
+Con_DrawCharacterLen
+
+returns character sizes in screen pixels
+====================
+*/
void Con_DrawCharacterLen( int number, int *width, int *height )
{
if( width && con.curFont ) *width = con.curFont->charWidths[number];
if( height && con.curFont ) *height = con.curFont->charHeight;
}
+/*
+====================
+Con_DrawStringLen
+
+compute string width and height in screen pixels
+====================
+*/
void Con_DrawStringLen( const char *pText, int *length, int *height )
{
int curLength = 0;
@@ -613,10 +768,10 @@ int Con_DrawGenericString( int x, int y, const char *string, rgba_t setColor, qb
if( !con.curFont ) return 0; // no font set
// draw the colored text
- s = string;
*(uint *)color = *(uint *)setColor;
+ s = string;
- while ( *s )
+ while( *s )
{
if( *s == '\n' )
{
@@ -652,6 +807,13 @@ int Con_DrawGenericString( int x, int y, const char *string, rgba_t setColor, qb
return drawLen;
}
+/*
+====================
+Con_DrawString
+
+client version of routine
+====================
+*/
int Con_DrawString( int x, int y, const char *string, rgba_t setColor )
{
return Con_DrawGenericString( x, y, string, setColor, false, -1 );
@@ -669,10 +831,17 @@ void Con_Init( void )
// must be init before startup video subsystem
scr_width = Cvar_Get( "width", "640", 0, "screen width" );
scr_height = Cvar_Get( "height", "480", 0, "screen height" );
- scr_conspeed = Cvar_Get( "scr_conspeed", "600", 0, "console moving speed" );
- con_notifytime = Cvar_Get( "con_notifytime", "3", 0, "notify time to live" );
+ scr_conspeed = Cvar_Get( "scr_conspeed", "600", CVAR_ARCHIVE, "console moving speed" );
+ con_notifytime = Cvar_Get( "con_notifytime", "3", CVAR_ARCHIVE, "notify time to live" );
con_fontsize = Cvar_Get( "con_fontsize", "1", CVAR_ARCHIVE, "console font number (0, 1 or 2)" );
+ // init the console buffer
+ con.bufsize = CON_TEXTSIZE;
+ con.buffer = (char *)Z_Malloc( con.bufsize );
+ con.maxlines = CON_MAXLINES;
+ con.lines = (con_lineinfo_t *)Z_Malloc( con.maxlines * sizeof( *con.lines ));
+ con.lines_first = con.lines_count = 0;
+
Con_CheckResize();
Con_ClearField( &con.input );
@@ -697,28 +866,23 @@ void Con_Init( void )
con.initialized = true;
}
-
/*
-===============
-Con_Linefeed
-===============
+================
+Con_Shutdown
+================
*/
-void Con_Linefeed( void )
+void Con_Shutdown( void )
{
- int i;
+ con.initialized = false;
- // mark time for transparent overlay
- if( con.current >= 0 )
- con.times[con.current % CON_TIMES] = host.realtime;
+ if( con.buffer )
+ Mem_Free( con.buffer );
- con.x = 0;
- if( con.display == con.current )
- con.display++;
+ if( con.lines )
+ Mem_Free( con.lines );
- con.current++;
-
- for( i = 0; i < con.linewidth; i++ )
- con.text[(con.current % con.totallines) * con.linewidth+i] = ( ColorIndex( COLOR_DEFAULT ) << 8 ) | ' ';
+ con.buffer = NULL;
+ con.lines = NULL;
}
/*
@@ -726,63 +890,52 @@ void Con_Linefeed( void )
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 text will appear at the top of the game window
+All console printing must go through this in order to be displayed
+If no console is visible, the notify window will pop up.
================
*/
void Con_Print( const char *txt )
{
- int y, c, l, color;
+ static int cr_pending = 0;
+ static char buf[MAX_PRINT_MSG];
+ static int bufpos = 0;
// client not running
- if( host.type == HOST_DEDICATED ) return;
- if( !con.initialized ) return;
-
- color = ColorIndex( COLOR_DEFAULT );
+ if( !con.initialized || !con.buffer || host.type == HOST_DEDICATED )
+ return;
- while(( c = *txt ) != 0 )
+ for( ; *txt; txt++ )
{
- if( IsColorString( txt ))
+ if( cr_pending )
{
- color = ColorIndex( *( txt + 1 ));
- txt += 2;
- continue;
+ Con_DeleteLastLine();
+ cr_pending = 0;
}
- // count word length
- for( l = 0; l < con.linewidth; l++ )
- {
- if( txt[l] <= ' ')
- break;
- }
-#if 0
- // g-cont. experiment from SDLash3D
- // word wrap
- if( l != con.linewidth && ( con.x + l >= con.linewidth ))
- Con_Linefeed();
-#endif
- txt++;
-
- switch( c )
+ switch( *txt )
{
- case '\n':
- Con_Linefeed();
+ case '\0':
break;
case '\r':
- con.x = 0;
+ Con_AddLine( buf, bufpos );
+ cr_pending = 1;
+ bufpos = 0;
break;
- default: // display character and advance
- y = con.current % con.totallines;
- con.text[y*con.linewidth+con.x] = (color << 8) | c;
- con.x++;
- if( con.x >= con.linewidth )
+ case '\n':
+ Con_AddLine( buf, bufpos );
+ bufpos = 0;
+ break;
+ default:
+ buf[bufpos++] = *txt;
+ if(( bufpos >= sizeof( buf ) - 1 ) || bufpos >= ( con.linewidth - 1 ))
{
- Con_Linefeed();
- con.x = 0;
+ Con_AddLine( buf, bufpos );
+ bufpos = 0;
}
break;
}
}
+
}
/*
@@ -949,7 +1102,7 @@ static int Con_SortCmds( const char **arg1, const char **arg2 )
/*
===============
-pfnPrintMatches
+Con_PrintMatches
===============
*/
static void Con_PrintMatches( const char *s, const char *unused1, const char *m, void *unused2 )
@@ -961,7 +1114,12 @@ static void Con_PrintMatches( const char *s, const char *unused1, const char *m,
}
}
-static void ConcatRemaining( const char *src, const char *start )
+/*
+===============
+Con_ConcatRemaining
+===============
+*/
+static void Con_ConcatRemaining( const char *src, const char *start )
{
char *arg;
int i;
@@ -1018,11 +1176,16 @@ void Con_CompleteCommand( field_t *field )
nextcmd = ( con.completionField->buffer[Q_strlen( con.completionField->buffer ) - 1] == ' ' ) ? true : false;
con.completionString = Cmd_Argv( 0 );
+ con.completionBuffer = Cmd_Argv( 1 );
// skip backslash
while( *con.completionString && ( *con.completionString == '\\' || *con.completionString == '/' ))
con.completionString++;
+ // skip backslash
+ while( *con.completionBuffer && ( *con.completionBuffer == '\\' || *con.completionBuffer == '/' ))
+ con.completionBuffer++;
+
if( !Q_strlen( con.completionString ))
return;
@@ -1051,12 +1214,15 @@ void Con_CompleteCommand( field_t *field )
{
qboolean result = false;
+ if( !Q_strlen( con.completionBuffer ))
+ return;
+
// autocomplete second arg
for( list = cmd_list; list->name; list++ )
{
if( Cmd_CheckName( list->name ))
{
- result = list->func( Cmd_Argv( 1 ), filename, MAX_STRING );
+ result = list->func( con.completionBuffer, filename, MAX_STRING );
break;
}
}
@@ -1080,7 +1246,7 @@ void Con_CompleteCommand( field_t *field )
{
Q_sprintf( con.completionField->buffer, "\\%s", con.cmds[0] );
if( Cmd_Argc() == 1 ) Q_strncat( con.completionField->buffer, " ", sizeof( con.completionField->buffer ));
- else ConcatRemaining( temp.buffer, con.completionString );
+ else Con_ConcatRemaining( temp.buffer, con.completionString );
con.completionField->cursor = Q_strlen( con.completionField->buffer );
}
else
@@ -1108,7 +1274,7 @@ void Con_CompleteCommand( field_t *field )
// multiple matches, complete to shortest
Q_sprintf( con.completionField->buffer, "\\%s", con.shortestMatch );
con.completionField->cursor = Q_strlen( con.completionField->buffer );
- ConcatRemaining( temp.buffer, con.completionString );
+ Con_ConcatRemaining( temp.buffer, con.completionString );
Msg( "]%s\n", con.completionField->buffer );
@@ -1135,7 +1301,6 @@ void Field_Paste( field_t *edit )
pasteLen = Q_strlen( cbd );
for( i = 0; i < pasteLen; i++ )
Field_CharEvent( edit, cbd[i] );
- Mem_Free( cbd );
}
/*
@@ -1313,7 +1478,7 @@ void Field_DrawInputLine( int x, int y, field_t *edit )
drawLen = len - prestep;
// extract <drawLen> characters from the field at <prestep>
- ASSERT( drawLen < MAX_SYSPATH );
+ drawLen = Q_min( drawLen, MAX_SYSPATH - 1 );
memcpy( str, edit->buffer + prestep, drawLen );
str[drawLen] = 0;
@@ -1370,7 +1535,7 @@ void Key_Console( int key )
}
// enter finishes the line
- if ( key == K_ENTER || key == K_KP_ENTER )
+ if( key == K_ENTER || key == K_KP_ENTER )
{
// if not in the game explicitly prepent a slash if needed
if( cls.state != ca_active && con.input.buffer[0] != '\\' && con.input.buffer[0] != '/' )
@@ -1418,9 +1583,7 @@ void Key_Console( int key )
if(( key == K_MWHEELUP && Key_IsDown( K_SHIFT )) || ( key == K_UPARROW ) || (( Q_tolower(key) == 'p' ) && Key_IsDown( K_CTRL )))
{
if( con.nextHistoryLine - con.historyLine < CON_HISTORY && con.historyLine > 0 )
- {
con.historyLine--;
- }
con.input = con.historyLines[con.historyLine % CON_HISTORY];
return;
}
@@ -1436,13 +1599,13 @@ void Key_Console( int key )
// console scrolling
if( key == K_PGUP )
{
- Con_PageUp( 2 );
+ Con_PageUp( 1 );
return;
}
if( key == K_PGDN )
{
- Con_PageDown( 2 );
+ Con_PageDown( 1 );
return;
}
@@ -1528,21 +1691,17 @@ Con_DrawInput
The input line scrolls horizontally if typing goes beyond the right edge
================
*/
-void Con_DrawInput( void )
+void Con_DrawInput( int lines )
{
- byte *colorDefault;
- int x, y;
+ int y;
// don't draw anything (always draw if not active)
- if( cls.key_dest != key_console ) return;
- if( !con.curFont ) return;
-
- x = QCHAR_WIDTH; // room for ']'
- y = con.vislines - ( con.curFont->charHeight * 2 );
- colorDefault = g_color_table[ColorIndex( COLOR_DEFAULT )];
+ if( cls.key_dest != key_console || !con.curFont )
+ return;
- Con_DrawCharacter( QCHAR_WIDTH >> 1, y, ']', colorDefault );
- Field_DrawInputLine( x, y, &con.input );
+ y = lines - ( con.curFont->charHeight * 2 );
+ Con_DrawCharacter( 8, y, ']', g_color_table[7] );
+ Field_DrawInputLine( 16, y, &con.input );
}
/*
@@ -1555,8 +1714,8 @@ Custom debug messages
int Con_DrawDebugLines( void )
{
int i, count = 0;
- int y = 20;
int defaultX;
+ int y = 20;
defaultX = glState.width / 4;
@@ -1568,7 +1727,7 @@ int Con_DrawDebugLines( void )
int fontTall;
Con_DrawStringLen( con.notify[i].szNotify, &len, &fontTall );
- x = scr_width->integer - max( defaultX, len ) - 10;
+ x = scr_width->integer - Q_max( defaultX, len ) - 10;
fontTall += 1;
if( y + fontTall > (int)scr_height->integer - 20 )
@@ -1611,38 +1770,24 @@ Draws the last few lines of output transparently over the game top
*/
void Con_DrawNotify( void )
{
- int i, x, v = 0;
- int start, currentColor;
- short *text;
- float time;
+ double time = cl.time;
+ int i, x, y = 0;
if( !con.curFont ) return;
+ x = con.curFont->charWidths[' ']; // offset one space at left screen side
+
if( host.developer && ( !Cvar_VariableInteger( "cl_background" ) && !Cvar_VariableInteger( "sv_background" )))
{
- currentColor = 7;
- pglColor4ubv( g_color_table[currentColor] );
-
- for( i = con.current - CON_TIMES + 1; i <= con.current; i++ )
+ for( i = CON_LINES_COUNT - CON_TIMES; i < CON_LINES_COUNT; i++ )
{
- if( i < 0 ) continue;
- time = con.times[i % CON_TIMES];
- if( time == 0 ) continue;
- time = host.realtime - time;
+ con_lineinfo_t *l = &CON_LINES( i );
- if( time > con_notifytime->value )
- continue; // expired
+ if( l->addtime < ( time - con_notifytime->value ))
+ continue;
- text = con.text + (i % con.totallines) * con.linewidth;
- start = con.curFont->charWidths[' ']; // offset one space at left screen side
-
- for( x = 0; x < con.linewidth; x++ )
- {
- if((( text[x] >> 8 ) & 7 ) != currentColor )
- currentColor = ( text[x] >> 8 ) & 7;
- start += Con_DrawCharacter( start, v, text[x] & 0xFF, g_color_table[currentColor] );
- }
- v += con.curFont->charHeight;
+ Con_DrawString( x, y, l->start, g_color_table[7] );
+ y += con.curFont->charHeight;
}
}
@@ -1651,21 +1796,16 @@ void Con_DrawNotify( void )
string buf;
int len;
- currentColor = 7;
- pglColor4ubv( g_color_table[currentColor] );
-
- start = con.curFont->charWidths[' ']; // offset one space at left screen side
-
// update chatline position from client.dll
if( clgame.dllFuncs.pfnChatInputPosition )
- clgame.dllFuncs.pfnChatInputPosition( &start, &v );
+ clgame.dllFuncs.pfnChatInputPosition( &x, &y );
Q_snprintf( buf, sizeof( buf ), "%s: ", con.chat_cmd );
Con_DrawStringLen( buf, &len, NULL );
- Con_DrawString( start, v, buf, g_color_table[7] );
+ Con_DrawString( x, y, buf, g_color_table[7] );
- Field_DrawInputLine( start + len, v, &con.chat );
+ Field_DrawInputLine( x + len, y, &con.chat );
}
pglColor4ub( 255, 255, 255, 255 );
@@ -1673,98 +1813,135 @@ void Con_DrawNotify( void )
/*
================
+Con_DrawConsoleLine
+
+Draws a line of the console; returns its height in lines.
+If alpha is 0, the line is not drawn, but still wrapped and its height
+returned.
+================
+*/
+int Con_DrawConsoleLine( int y, int lineno )
+{
+ con_lineinfo_t *li = &CON_LINES( lineno );
+
+ if( y >= con.curFont->charHeight )
+ Con_DrawGenericString( con.curFont->charWidths[' '], y, li->start, g_color_table[7], false, -1 );
+
+ return con.curFont->charHeight;
+}
+
+/*
+================
+Con_LastVisibleLine
+
+Calculates the last visible line index and how much to show
+of it based on con.backscroll.
+================
+*/
+static void Con_LastVisibleLine( int *lastline )
+{
+ int i, lines_seen = 0;
+
+ con.backscroll = Q_max( 0, con.backscroll );
+ *lastline = 0;
+
+ // now count until we saw con_backscroll actual lines
+ for( i = CON_LINES_COUNT - 1; i >= 0; i-- )
+ {
+ // line is the last visible line?
+ *lastline = i;
+
+ if( lines_seen + 1 > con.backscroll && lines_seen <= con.backscroll )
+ return;
+
+ lines_seen += 1;
+ }
+
+ // if we get here, no line was on screen - scroll so that one line is visible then.
+ con.backscroll = lines_seen - 1;
+}
+
+/*
+================
Con_DrawConsole
Draws the console with the solid background
================
*/
-void Con_DrawSolidConsole( float frac )
+void Con_DrawSolidConsole( int lines )
{
int i, x, y;
- int rows;
- short *text;
- int row;
- int lines, start;
- int currentColor;
- string curbuild;
+ float fraction;
+ int start;
- lines = scr_height->integer * frac;
if( lines <= 0 ) return;
- if( lines > scr_height->integer )
- lines = scr_height->integer;
// draw the background
- y = frac * scr_height->integer;
+ GL_SetRenderMode( kRenderNormal );
+ pglColor4ub( 255, 255, 255, 255 ); // to prevent grab color from screenfade
+ R_DrawStretchPic( 0, lines - scr_height->integer, scr_width->integer, scr_height->integer, 0, 0, 1, 1, con.background );
- if( y >= 1 )
- {
- GL_SetRenderMode( kRenderNormal );
- R_DrawStretchPic( 0, y - scr_height->integer, scr_width->integer, scr_height->integer, 0, 0, 1, 1, con.background );
- }
- else y = 0;
+ if( !con.curFont || host.developer <= 0 )
+ return; // nothing to draw
- if( !con.curFont ) return; // nothing to draw
-
- if( host.developer )
+ if( host.developer > 0 )
{
// draw current version
- byte *color = g_color_table[7];
int stringLen, width = 0, charH;
+ string curbuild;
+ byte color[4];
+
+ memcpy( color, g_color_table[7], sizeof( color ));
Q_snprintf( curbuild, MAX_STRING, "Xash3D %i/%g (hw build %i)", PROTOCOL_VERSION, XASH_VERSION, Q_buildnum( ));
Con_DrawStringLen( curbuild, &stringLen, &charH );
start = scr_width->integer - stringLen;
stringLen = Con_StringLength( curbuild );
+ fraction = lines / (float)scr_height->integer;
+ color[3] = Q_min( fraction * 2.0f, 1.0f ) * 255; // fadeout version number
+
for( i = 0; i < stringLen; i++ )
width += Con_DrawCharacter( start + width, 0, curbuild[i], color );
}
// draw the text
- con.vislines = lines;
- rows = ( lines - QCHAR_WIDTH ) / QCHAR_WIDTH; // rows of text to draw
- y = lines - ( con.curFont->charHeight * 3 );
-
- // draw from the bottom up
- if( con.display != con.current )
+ if( CON_LINES_COUNT > 0 )
{
- start = con.curFont->charWidths[' ']; // offset one space at left screen side
-
- // draw red arrows to show the buffer is backscrolled
- for( x = 0; x < con.linewidth; x += 4 )
- Con_DrawCharacter(( x + 1 ) * start, y, '^', g_color_table[1] );
- y -= con.curFont->charHeight;
- rows--;
- }
-
- row = con.display;
- if( con.x == 0 ) row--;
+ int ymax = lines - (con.curFont->charHeight * 2.0f);
+ int lastline;
- currentColor = 7;
- pglColor4ubv( g_color_table[currentColor] );
+ Con_LastVisibleLine( &lastline );
+ y = ymax - con.curFont->charHeight;
- for( i = 0; i < rows; i++, y -= con.curFont->charHeight, row-- )
- {
- if( row < 0 ) break;
- if( con.current - row >= con.totallines )
+ if( con.backscroll )
{
- // past scrollback wrap point
- continue;
- }
+ start = con.curFont->charWidths[' ']; // offset one space at left screen side
- text = con.text + ( row % con.totallines ) * con.linewidth;
- start = con.curFont->charWidths[' ']; // offset one space at left screen side
+ // draw red arrows to show the buffer is backscrolled
+ for( x = 0; x < con.linewidth; x += 4 )
+ Con_DrawCharacter(( x + 1 ) * start, y, '^', g_color_table[1] );
+ y -= con.curFont->charHeight;
+ }
+ x = lastline;
- for( x = 0; x < con.linewidth; x++ )
+ while( 1 )
{
- if((( text[x] >> 8 ) & 7 ) != currentColor )
- currentColor = ( text[x] >> 8 ) & 7;
- start += Con_DrawCharacter( start, y, text[x] & 0xFF, g_color_table[currentColor] );
+ y -= Con_DrawConsoleLine( y, x );
+
+ // top of console buffer or console window
+ if( x == 0 || y < con.curFont->charHeight )
+ break;
+ x--;
}
}
// draw the input prompt, user text, and cursor if desired
- Con_DrawInput();
+ Con_DrawInput( lines );
+
+ y = lines - ( con.curFont->charHeight * 1.2f );
+ SCR_DrawFPS( max( y, 4 )); // to avoid to hide fps counter
+
pglColor4ub( 255, 255, 255, 255 );
}
@@ -1787,18 +1964,18 @@ void Con_DrawConsole( void )
if( !cl_allow_levelshots->integer )
{
if(( Cvar_VariableInteger( "cl_background" ) || Cvar_VariableInteger( "sv_background" )) && cls.key_dest != key_console )
- con.displayFrac = con.finalFrac = 0.0f;
- else con.displayFrac = con.finalFrac = 1.0f;
+ con.vislines = con.showlines = 0;
+ else con.vislines = con.showlines = scr_height->integer;
}
else
{
if( host.developer >= 4 )
{
- con.displayFrac = 0.5f; // keep console open
+ con.vislines = (scr_height->integer >> 1); // keep console open
}
else
{
- con.finalFrac = 0.0f;
+ con.showlines = 0;
Con_RunConsole();
if( host.developer >= 2 )
@@ -1815,31 +1992,37 @@ void Con_DrawConsole( void )
case ca_disconnected:
if( cls.key_dest != key_menu && host.developer )
{
- Con_DrawSolidConsole( 1.0f );
+ Con_DrawSolidConsole( scr_height->integer );
Key_SetKeyDest( key_console );
}
break;
case ca_connected:
case ca_connecting:
// force to show console always for -dev 3 and higher
- if( con.displayFrac ) Con_DrawSolidConsole( con.displayFrac );
+ if( con.vislines )
+ {
+ GL_CleanupAllTextureUnits(); // ugly hack to remove blinking voiceicon.spr during loading
+ Con_DrawSolidConsole( con.vislines );
+ }
break;
case ca_active:
case ca_cinematic:
if( Cvar_VariableInteger( "cl_background" ) || Cvar_VariableInteger( "sv_background" ))
{
if( cls.key_dest == key_console )
- Con_DrawSolidConsole( 1.0f );
+ Con_DrawSolidConsole( scr_height->integer );
}
else
{
- if( con.displayFrac )
- Con_DrawSolidConsole( con.displayFrac );
+ if( con.vislines )
+ Con_DrawSolidConsole( con.vislines );
else if( cls.state == ca_active && ( cls.key_dest == key_game || cls.key_dest == key_message ))
Con_DrawNotify(); // draw notify lines
}
break;
}
+
+ if( !Con_Visible( )) SCR_DrawFPS( 4 );
}
/*
@@ -1893,30 +2076,38 @@ Scroll it up or down
*/
void Con_RunConsole( void )
{
+ int lines_per_frame;
+
// decide on the destination height of the console
if( host.developer && cls.key_dest == key_console )
{
if( cls.state == ca_disconnected )
- con.finalFrac = 1.0f;// full screen
- else con.finalFrac = 0.5f; // half screen
+ con.showlines = scr_height->integer; // full screen
+ else con.showlines = (scr_height->integer >> 1); // half screen
}
- else con.finalFrac = 0; // none visible
+ else con.showlines = 0; // none visible
// when level is loading frametime may be is wrong
if( cls.state == ca_connecting || cls.state == ca_connected )
- host.realframetime = ( MAX_FPS / host_maxfps->value ) * MIN_FRAMETIME;
+ {
+ if( !FBitSet( host.features, ENGINE_FIXED_FRAMERATE ))
+ host.realframetime = ( MAX_FPS / host_maxfps->value ) * MIN_FRAMETIME;
+ else host.realframetime = HOST_FRAMETIME;
+ }
+
+ lines_per_frame = bound( 1, fabs( scr_conspeed->value ) * host.realframetime, scr_height->integer );
- if( con.finalFrac < con.displayFrac )
+ if( con.showlines < con.vislines )
{
- con.displayFrac -= fabs( scr_conspeed->value ) * 0.002f * host.realframetime;
- if( con.finalFrac > con.displayFrac )
- con.displayFrac = con.finalFrac;
+ con.vislines -= lines_per_frame;
+ if( con.showlines > con.vislines )
+ con.vislines = con.showlines;
}
- else if( con.finalFrac > con.displayFrac )
+ else if( con.showlines > con.vislines )
{
- con.displayFrac += fabs( scr_conspeed->value ) * 0.002f * host.realframetime;
- if( con.finalFrac < con.displayFrac )
- con.displayFrac = con.finalFrac;
+ con.vislines += lines_per_frame;
+ if( con.showlines < con.vislines )
+ con.vislines = con.showlines;
}
}
@@ -1947,6 +2138,14 @@ void Con_CharEvent( int key )
}
}
+/*
+=========
+Con_VidInit
+
+reload background
+resize console
+=========
+*/
void Con_VidInit( void )
{
Con_CheckResize();
@@ -1954,42 +2153,62 @@ void Con_VidInit( void )
// loading console image
if( host.developer )
{
- if( scr_width->integer < 640 )
- {
- if( FS_FileExists( "cached/conback400", false ))
- con.background = GL_LoadTexture( "cached/conback400", NULL, 0, TF_IMAGE, NULL );
- else con.background = GL_LoadTexture( "cached/conback", NULL, 0, TF_IMAGE, NULL );
- }
- else
+ // trying to load truecolor image first
+ if( FS_FileExists( "gfx/shell/conback.bmp", false ) || FS_FileExists( "gfx/shell/conback.tga", false ))
+ con.background = GL_LoadTexture( "gfx/shell/conback", NULL, 0, TF_IMAGE, NULL );
+
+ if( !con.background )
{
- if( FS_FileExists( "cached/conback640", false ))
- con.background = GL_LoadTexture( "cached/conback640", NULL, 0, TF_IMAGE, NULL );
- else con.background = GL_LoadTexture( "cached/conback", NULL, 0, TF_IMAGE, NULL );
+ if( scr_width->integer < 640 )
+ {
+ if( FS_FileExists( "cached/conback400", false ))
+ con.background = GL_LoadTexture( "cached/conback400", NULL, 0, TF_IMAGE, NULL );
+ else con.background = GL_LoadTexture( "cached/conback", NULL, 0, TF_IMAGE, NULL );
+ }
+ else
+ {
+ if( FS_FileExists( "cached/conback640", false ))
+ con.background = GL_LoadTexture( "cached/conback640", NULL, 0, TF_IMAGE, NULL );
+ else con.background = GL_LoadTexture( "cached/conback", NULL, 0, TF_IMAGE, NULL );
+ }
}
}
else
{
- if( scr_width->integer < 640 )
- {
- if( FS_FileExists( "cached/loading400", false ))
- con.background = GL_LoadTexture( "cached/loading400", NULL, 0, TF_IMAGE, NULL );
- else con.background = GL_LoadTexture( "cached/loading", NULL, 0, TF_IMAGE, NULL );
- }
- else
+ // trying to load truecolor image first
+ if( FS_FileExists( "gfx/shell/loading.bmp", false ) || FS_FileExists( "gfx/shell/loading.tga", false ))
+ con.background = GL_LoadTexture( "gfx/shell/loading", NULL, 0, TF_IMAGE, NULL );
+
+ if( !con.background )
{
- if( FS_FileExists( "cached/loading640", false ))
- con.background = GL_LoadTexture( "cached/loading640", NULL, 0, TF_IMAGE, NULL );
- else con.background = GL_LoadTexture( "cached/loading", NULL, 0, TF_IMAGE, NULL );
+ if( scr_width->integer < 640 )
+ {
+ if( FS_FileExists( "cached/loading400", false ))
+ con.background = GL_LoadTexture( "cached/loading400", NULL, 0, TF_IMAGE, NULL );
+ else con.background = GL_LoadTexture( "cached/loading", NULL, 0, TF_IMAGE, NULL );
+ }
+ else
+ {
+ if( FS_FileExists( "cached/loading640", false ))
+ con.background = GL_LoadTexture( "cached/loading640", NULL, 0, TF_IMAGE, NULL );
+ else con.background = GL_LoadTexture( "cached/loading", NULL, 0, TF_IMAGE, NULL );
+ }
}
}
- // missed console image will be replaced as white (GoldSrc rules)
+ // missed console image will be replaced as gray background like X-Ray or Crysis
if( con.background == tr.defaultTexture || con.background == 0 )
- con.background = tr.whiteTexture;
+ con.background = tr.grayTexture;
Con_LoadConchars();
}
+/*
+=========
+Con_InvalidateFonts
+
+=========
+*/
void Con_InvalidateFonts( void )
{
memset( con.chars, 0, sizeof( con.chars ));
@@ -2022,12 +2241,19 @@ void Cmd_AutoComplete( char *complete_string )
else Q_strncpy( complete_string, input.buffer, sizeof( input.buffer ));
}
-void Con_Close( void )
+/*
+=========
+Con_FastClose
+
+immediately close the console
+=========
+*/
+void Con_FastClose( void )
{
Con_ClearField( &con.input );
Con_ClearNotify();
- con.finalFrac = 0.0f; // none visible
- con.displayFrac = 0.0f;
+ con.showlines = 0;
+ con.vislines = 0;
}
/*
diff --git b/engine/common/crtlib.c a/engine/common/crtlib.c
index 9a3c348..8a44b25 100644
--- b/engine/common/crtlib.c
+++ a/engine/common/crtlib.c
@@ -71,6 +71,29 @@ int Q_strlen( const char *string )
return len;
}
+int Q_colorstr( const char *string )
+{
+ int len;
+ const char *p;
+
+ if( !string ) return 0;
+
+ len = 0;
+ p = string;
+ while( *p )
+ {
+ if( IsColorString( p ))
+ {
+ len += 2;
+ p += 2;
+ continue;
+ }
+ p++;
+ }
+
+ return len;
+}
+
char Q_toupper( const char in )
{
char out;
@@ -572,6 +595,26 @@ int Q_sprintf( char *buffer, const char *format, ... )
return result;
}
+uint Q_hashkey( const char *string, uint hashSize, qboolean caseinsensitive )
+{
+ uint i, hashKey = 0;
+
+ if( caseinsensitive )
+ {
+ for( i = 0; string[i]; i++)
+ hashKey += (i * 119) * Q_tolower( string[i] );
+ }
+ else
+ {
+ for( i = 0; string[i]; i++ )
+ hashKey += (i + 119) * (int)string[i];
+ }
+
+ hashKey = ((hashKey ^ (hashKey >> 10)) ^ (hashKey >> 20)) & (hashSize - 1);
+
+ return hashKey;
+}
+
char *Q_pretifymem( float value, int digitsafterdecimal )
{
static char output[8][32];
@@ -666,36 +709,4 @@ char *va( const char *format, ... )
va_end( argptr );
return s;
-}
-
-void _memcpy( void *dest, const void *src, size_t count, const char *filename, int fileline )
-{
- if( src == NULL || count <= 0 ) return; // nothing to copy
- if( dest == NULL ) Sys_Error( "memcpy: dest == NULL (called at %s:%i)\n", filename, fileline );
- memcpy( dest, src, count );
-}
-
-void _memset( void *dest, int set, size_t count, const char *filename, int fileline )
-{
- if( dest == NULL ) Sys_Error( "memset: dest == NULL (called at %s:%i)\n", filename, fileline );
- memset( dest, set, count );
-}
-
-int _memcmp( const void *src0, const void *src1, size_t count, const char *filename, int fileline )
-{
- if( src0 == NULL ) Sys_Error( "memcmp: src1 == NULL (called at %s:%i)\n", filename, fileline );
- if( src1 == NULL ) Sys_Error( "memcmp: src2 == NULL (called at %s:%i)\n", filename, fileline );
- return memcmp( src0, src1, count );
-}
-
-void _memmove( void *dest, const void *src, size_t count, const char *filename, int fileline )
-{
- if( src == NULL || count <= 0 ) return; // nothing to move
- if( dest == NULL ) Sys_Error( "memmove: dest == NULL (called at %s:%i)\n", filename, fileline );
- memmove( dest, src, count );
-}
-
-void CRT_Init( void )
-{
- Memory_Init();
}
\ No newline at end of file
diff --git b/engine/common/crtlib.h a/engine/common/crtlib.h
index 012d4e0..d4c994e 100644
--- b/engine/common/crtlib.h
+++ a/engine/common/crtlib.h
@@ -27,21 +27,13 @@ enum
TIME_FILENAME,
};
-#define CMD_EXTDLL BIT( 0 ) // added by game.dll
+#define CMD_SERVERDLL BIT( 0 ) // added by server.dll
#define CMD_CLIENTDLL BIT( 1 ) // added by client.dll
+#define CMD_GAMEUIDLL BIT( 2 ) // added by GameUI.dll
typedef void (*setpair_t)( const char *key, const char *value, void *buffer, void *numpairs );
typedef void (*xcommand_t)( void );
-typedef enum
-{
- src_client, // came in over a net connection as a clc_stringcmd
- // host_client will be valid during this state.
- src_command // from the command buffer
-} cmd_source_t;
-
-extern cmd_source_t cmd_source;
-
// NOTE: if this is changed, it must be changed in cvardef.h too
typedef struct convar_s
{
@@ -63,25 +55,26 @@ typedef struct convar_s
// cvar flags
typedef enum
{
- CVAR_ARCHIVE = BIT(0), // set to cause it to be saved to config.cfg
- CVAR_USERINFO = BIT(1), // added to userinfo when changed
- CVAR_SERVERNOTIFY = BIT(2), // notifies players when changed
- CVAR_EXTDLL = BIT(3), // defined by external DLL
- CVAR_CLIENTDLL = BIT(4), // defined by the client dll
- CVAR_PROTECTED = BIT(5), // it's a server cvar, but we don't send the data since it's a password, etc.
- CVAR_SPONLY = BIT(6), // this cvar cannot be changed by clients connected to a multiplayer server.
- CVAR_PRINTABLEONLY = BIT(7), // this cvar's string cannot contain unprintable characters ( player name )
- CVAR_UNLOGGED = BIT(8), // if this is a FCVAR_SERVER, don't log changes to the log file / console
- CVAR_SERVERINFO = BIT(9), // added to serverinfo when changed
- CVAR_PHYSICINFO = BIT(10),// added to physinfo when changed
- CVAR_RENDERINFO = BIT(11),// save to a seperate config called opengl.cfg
- CVAR_CHEAT = BIT(12),// can not be changed if cheats are disabled
- CVAR_INIT = BIT(13),// don't allow change from console at all, but can be set from the command line
- CVAR_LATCH = BIT(14),// save changes until server restart
- CVAR_READ_ONLY = BIT(15),// display only, cannot be set by user at all
- CVAR_LATCH_VIDEO = BIT(16),// save changes until render restart
- CVAR_USER_CREATED = BIT(17),// created by a set command (dll's used)
- CVAR_GLCONFIG = BIT(18),// set to cause it to be saved to opengl.cfg
+ CVAR_ARCHIVE = BIT( 0 ), // set to cause it to be saved to config.cfg
+ CVAR_USERINFO = BIT( 1 ), // added to userinfo when changed
+ CVAR_SERVERNOTIFY = BIT( 2 ), // notifies players when changed
+ CVAR_SERVERDLL = BIT( 3 ), // defined by the server DLL
+ CVAR_CLIENTDLL = BIT( 4 ), // defined by the client dll
+ CVAR_PROTECTED = BIT( 5 ), // it's a server cvar, but we don't send the data since it's a password, etc.
+ CVAR_SPONLY = BIT( 6 ), // this cvar cannot be changed by clients connected to a multiplayer server.
+ CVAR_PRINTABLEONLY = BIT( 7 ), // this cvar's string cannot contain unprintable characters ( player name )
+ CVAR_UNLOGGED = BIT( 8 ), // if this is a FCVAR_SERVER, don't log changes to the log file / console
+ CVAR_NOWHITEPACE = BIT( 9 ), // strip trailing/leading white space from this cvar
+ CVAR_SERVERINFO = BIT( 10 ), // added to serverinfo when changed
+ CVAR_PHYSICINFO = BIT( 11 ), // added to physinfo when changed
+ CVAR_RENDERINFO = BIT( 12 ), // save to a seperate config called opengl.cfg
+ CVAR_CHEAT = BIT( 13 ), // can not be changed if cheats are disabled
+ CVAR_INIT = BIT( 14 ), // don't allow change from console at all, but can be set from the command line
+ CVAR_LATCH = BIT( 15 ), // save changes until server restart
+ CVAR_READ_ONLY = BIT( 16 ), // display only, cannot be set by user at all
+ CVAR_GAMEUIDLL = BIT( 17 ), // defined by the GameUI DLL
+ CVAR_GLCONFIG = BIT( 18 ), // set to cause it to be saved to opengl.cfg
+ CVAR_USER_CREATED = BIT( 19 ), // created by a set command (dll's used)
} cvar_flags_t;
#include "cvardef.h"
@@ -109,7 +102,7 @@ void Cvar_WriteVariables( file_t *f );
void Cvar_Init( void );
char *Cvar_Userinfo( void );
char *Cvar_Serverinfo( void );
-void Cvar_Unlink( void );
+void Cvar_Unlink( int group );
//
// cmd.c
@@ -125,8 +118,9 @@ char *Cmd_Argv( int arg );
void Cmd_Init( void );
void Cmd_Unlink( int group );
void Cmd_AddCommand( const char *cmd_name, xcommand_t function, const char *cmd_desc );
-void Cmd_AddGameCommand( const char *cmd_name, xcommand_t function );
-void Cmd_AddClientCommand( const char *cmd_name, xcommand_t function );
+void Cmd_AddServerCommand( const char *cmd_name, xcommand_t function );
+int Cmd_AddClientCommand( const char *cmd_name, xcommand_t function );
+int Cmd_AddGameUICommand( const char *cmd_name, xcommand_t function );
void Cmd_RemoveCommand( const char *cmd_name );
qboolean Cmd_Exists( const char *cmd_name );
void Cmd_LookupCmds( char *buffer, void *ptr, setpair_t callback );
@@ -134,7 +128,7 @@ qboolean Cmd_GetMapList( const char *s, char *completedname, int length );
qboolean Cmd_GetDemoList( const char *s, char *completedname, int length );
qboolean Cmd_GetMovieList( const char *s, char *completedname, int length );
void Cmd_TokenizeString( char *text );
-void Cmd_ExecuteString( char *text, cmd_source_t src );
+void Cmd_ExecuteString( char *text );
void Cmd_ForwardToServer( void );
//
@@ -145,6 +139,7 @@ void Q_strnupr( const char *in, char *out, size_t size_out );
#define Q_strlwr( int, out ) Q_strnlwr( in, out, 99999 )
void Q_strnlwr( const char *in, char *out, size_t size_out );
int Q_strlen( const char *string );
+int Q_colorstr( const char *string );
char Q_toupper( const char in );
char Q_tolower( const char in );
#define Q_strcat( dst, src ) Q_strncat( dst, src, 99999 )
@@ -153,6 +148,7 @@ size_t Q_strncat( char *dst, const char *src, size_t siz );
size_t Q_strncpy( char *dst, const char *src, size_t siz );
#define copystring( s ) _copystring( host.mempool, s, __FILE__, __LINE__ )
char *_copystring( byte *mempool, const char *s, const char *filename, int fileline );
+uint Q_hashkey( const char *string, uint hashSize, qboolean caseinsensitive );
qboolean Q_isdigit( const char *str );
int Q_atoi( const char *str );
float Q_atof( const char *str );
@@ -174,14 +170,6 @@ int Q_sprintf( char *buffer, const char *format, ... );
#define Q_memprint( val ) Q_pretifymem( val, 2 )
char *Q_pretifymem( float value, int digitsafterdecimal );
char *va( const char *format, ... );
-#define Q_memcpy( dest, src, size ) _Q_memcpy( dest, src, size, __FILE__, __LINE__ )
-#define Q_memset( dest, val, size ) _Q_memset( dest, val, size, __FILE__, __LINE__ )
-#define Q_memcmp( src0, src1, siz ) _Q_memcmp( src0, src1, siz, __FILE__, __LINE__ )
-#define Q_memmove( dest, src, size ) _Q_memmove( dest, src, size, __FILE__, __LINE__ )
-void _Q_memset( void *dest, int set, size_t count, const char *filename, int fileline );
-void _Q_memcpy( void *dest, const void *src, size_t count, const char *filename, int fileline );
-int _Q_memcmp( const void *src0, const void *src1, size_t count, const char *filename, int fileline );
-void _Q_memmove( void *dest, const void *src, size_t count, const char *filename, int fileline );
//
// zone.c
@@ -206,7 +194,5 @@ void Mem_PrintStats( void );
#define Mem_EmptyPool( pool ) _Mem_EmptyPool( pool, __FILE__, __LINE__ )
#define Mem_IsAllocated( mem ) Mem_IsAllocatedExt( NULL, mem )
#define Mem_Check() _Mem_Check( __FILE__, __LINE__ )
-
-void CRT_Init( void ); // must be call first
#endif//STDLIB_H
\ No newline at end of file
diff --git b/engine/common/cvar.c a/engine/common/cvar.c
index cb7f6b6..6b93895 100644
--- b/engine/common/cvar.c
+++ a/engine/common/cvar.c
@@ -17,6 +17,7 @@ GNU General Public License for more details.
convar_t *cvar_vars; // head of list
convar_t *userinfo, *physinfo, *serverinfo, *renderinfo;
+convar_t *cmd_scripting;
/*
============
@@ -139,7 +140,7 @@ void Cvar_LookupVars( int checkbit, void *buffer, void *ptr, setpair_t callback
else
{
// NOTE: dlls cvars doesn't have description
- if( cvar->flags & CVAR_EXTDLL )
+ if( FBitSet( cvar->flags, CVAR_SERVERDLL ))
callback( cvar->name, cvar->string, "game cvar", ptr );
else callback( cvar->name, cvar->string, cvar->description, ptr );
}
@@ -156,18 +157,25 @@ The flags will be or'ed in if the variable exists.
*/
convar_t *Cvar_Get( const char *var_name, const char *var_value, int flags, const char *var_desc )
{
- convar_t *var;
+ convar_t *cur, *find, *var;
if( !var_name )
{
- Sys_Error( "Cvar_Get: passed NULL name\n" );
+ MsgDev( D_ERROR, "Cvar_Get: passed NULL name\n" );
+ return NULL;
+ }
+
+ // check for command coexisting
+ if( Cmd_Exists( var_name ))
+ {
+ MsgDev( D_ERROR, "Cvar_Get: %s is a command\n", var_name );
return NULL;
}
if( !var_value ) var_value = "0"; // just apply default value
// all broadcast cvars must be passed this check
- if( flags & ( CVAR_USERINFO|CVAR_SERVERINFO|CVAR_PHYSICINFO ))
+ if( FBitSet( flags, CVAR_USERINFO|CVAR_SERVERINFO|CVAR_PHYSICINFO ))
{
if( !Cvar_ValidateString( var_name, false ))
{
@@ -182,37 +190,30 @@ convar_t *Cvar_Get( const char *var_name, const char *var_value, int flags, cons
}
}
- // check for command coexisting
- if( Cmd_Exists( var_name ))
- {
- MsgDev( D_ERROR, "Cvar_Get: %s is a command\n", var_name );
- return NULL;
- }
-
var = Cvar_FindVar( var_name );
if( var )
{
// fast check for short cvars
- if( var->flags & CVAR_EXTDLL )
+ if( FBitSet( var->flags, CVAR_SERVERDLL ))
{
- var->flags |= flags;
+ SetBits( var->flags, flags );
return var;
}
// if the C code is now specifying a variable that the user already
// set a value for, take the new value as the reset value
- if(( var->flags & CVAR_USER_CREATED ) && !( flags & CVAR_USER_CREATED ) && var_value[0] )
+ if( FBitSet( var->flags, CVAR_USER_CREATED ) && !FBitSet( flags, CVAR_USER_CREATED ) && var_value[0] )
{
- var->flags &= ~CVAR_USER_CREATED;
Mem_Free( var->reset_string );
var->reset_string = copystring( var_value );
+ ClearBits( var->flags, CVAR_USER_CREATED );
}
- var->flags |= flags;
+ SetBits( var->flags, flags );
// only allow one non-empty reset string without a warning
- if( !var->reset_string[0] )
+ if( var->reset_string != NULL && !var->reset_string[0] )
{
// we don't have a reset string yet
Mem_Free( var->reset_string );
@@ -220,7 +221,7 @@ convar_t *Cvar_Get( const char *var_name, const char *var_value, int flags, cons
}
// if we have a latched string, take that value now
- if( var->latched_string )
+ if( var->latched_string != NULL )
{
char *s = var->latched_string;
var->latched_string = NULL; // otherwise cvar_set2 would free it
@@ -228,12 +229,13 @@ convar_t *Cvar_Get( const char *var_name, const char *var_value, int flags, cons
Mem_Free( s );
}
- if( var_desc )
+ if( var_desc != NULL )
{
// update description if needs
if( var->description ) Mem_Free( var->description );
var->description = copystring( var_desc );
}
+
return var;
}
@@ -248,9 +250,24 @@ convar_t *Cvar_Get( const char *var_name, const char *var_value, int flags, cons
var->modified = true;
var->flags = flags;
- // link the variable in
- var->next = cvar_vars;
- cvar_vars = var;
+ // link the variable in alphanumerical order
+ for( cur = NULL, find = cvar_vars; find && Q_strcmp( find->name, var->name ) < 0; cur = find, find = find->next );
+
+ if( cur ) cur->next = var;
+ else cvar_vars = var;
+ var->next = find;
+
+ if( var->flags & CVAR_USERINFO )
+ userinfo->modified = true; // transmit at next oportunity
+
+ if( var->flags & CVAR_PHYSICINFO )
+ physinfo->modified = true; // transmit at next oportunity
+
+ if( var->flags & CVAR_SERVERINFO )
+ serverinfo->modified = true; // transmit at next oportunity
+
+ if( var->flags & CVAR_RENDERINFO )
+ renderinfo->modified = true; // transmit at next oportunity
return var;
}
@@ -264,7 +281,7 @@ Adds a freestanding variable to the variable list.
*/
void Cvar_RegisterVariable( cvar_t *var )
{
- convar_t **prev, *cur = NULL;
+ convar_t *cur = NULL;
convar_t *find;
ASSERT( var != NULL );
@@ -281,65 +298,67 @@ void Cvar_RegisterVariable( cvar_t *var )
{
// this cvar is already registered with Cvar_RegisterVariable
// so we can't replace it
- if( cur->flags & CVAR_EXTDLL )
+ if( FBitSet( cur->flags, CVAR_SERVERDLL ))
{
MsgDev( D_ERROR, "can't register variable %s, allready defined\n", var->name );
return;
}
- }
-
- if( cur )
- {
- prev = &cvar_vars;
-
- while( 1 )
+ else
{
- find = *prev;
+ var->string = cur->string; // we already have right string
+ var->value = Q_atof( var->string );
+ SetBits( var->flags, CVAR_SERVERDLL ); // all cvars passed this function are game cvars
+ var->next = (cvar_t *)cur->next;
- ASSERT( find != NULL );
-
- if( cur == cvar_vars )
+ if( cvar_vars == cur )
{
// relink at tail
cvar_vars = (convar_t *)var;
- break;
}
-
- // search for previous cvar
- if( cur != find->next )
+ else
{
- prev = &find->next;
- continue;
+ // otherwise find it somewhere in the list
+ for( find = cvar_vars; find->next != cur; find = find->next );
+
+ ASSERT( find != NULL );
+
+ find->next = (convar_t *)var;
}
- // link new variable
- find->next = (convar_t *)var;
- break;
+ // release current cvar (but keep string)
+ if( cur->name ) Mem_Free( cur->name );
+ if( cur->latched_string ) Mem_Free( cur->latched_string );
+ if( cur->reset_string ) Mem_Free( cur->reset_string );
+ if( cur->description ) Mem_Free( cur->description );
+ Mem_Free( cur );
}
-
- var->string = cur->string; // we already have right string
- var->value = Q_atof( var->string );
- var->flags |= CVAR_EXTDLL; // all cvars passed this function are game cvars
- var->next = (cvar_t *)cur->next;
-
- // release current cvar (but keep string)
- if( cur->name ) Mem_Free( cur->name );
- if( cur->latched_string ) Mem_Free( cur->latched_string );
- if( cur->reset_string ) Mem_Free( cur->reset_string );
- if( cur->description ) Mem_Free( cur->description );
- Mem_Free( cur );
}
else
{
// copy the value off, because future sets will Z_Free it
var->string = copystring( var->string );
var->value = Q_atof( var->string );
- var->flags |= CVAR_EXTDLL; // all cvars passed this function are game cvars
+ SetBits( var->flags, CVAR_SERVERDLL ); // all cvars passed this function are game cvars
+
+ // link the variable in alphanumerical order
+ for( cur = NULL, find = cvar_vars; find && Q_strcmp( find->name, var->name ) < 0; cur = find, find = find->next );
- // link the variable in
- var->next = (cvar_t *)cvar_vars;
- cvar_vars = (convar_t *)var;
+ if( cur ) cur->next = (convar_t *)var;
+ else cvar_vars = (convar_t *)var;
+ var->next = (cvar_t *)find;
}
+
+ if( var->flags & CVAR_USERINFO )
+ userinfo->modified = true; // transmit at next oportunity
+
+ if( var->flags & CVAR_PHYSICINFO )
+ physinfo->modified = true; // transmit at next oportunity
+
+ if( var->flags & CVAR_SERVERINFO )
+ serverinfo->modified = true; // transmit at next oportunity
+
+ if( var->flags & CVAR_RENDERINFO )
+ renderinfo->modified = true; // transmit at next oportunity
}
/*
@@ -352,7 +371,6 @@ convar_t *Cvar_Set2( const char *var_name, const char *value, qboolean force )
convar_t *var;
const char *pszValue;
char szNew[MAX_SYSPATH];
- qboolean dll_variable = false;
if( !Cvar_ValidateString( var_name, false ))
{
@@ -361,6 +379,7 @@ convar_t *Cvar_Set2( const char *var_name, const char *value, qboolean force )
}
var = Cvar_FindVar( var_name );
+
if( !var )
{
// create it
@@ -369,14 +388,10 @@ convar_t *Cvar_Set2( const char *var_name, const char *value, qboolean force )
}
- // use this check to prevent acessing for unexisting fields
- // for cvar_t: latched_string, description, etc
- if( var->flags & CVAR_EXTDLL )
- dll_variable = true;
-
if( !value )
{
- if( dll_variable ) value = "0";
+ if( FBitSet( var->flags, CVAR_SERVERDLL ))
+ value = "0";
else value = var->reset_string;
}
@@ -384,23 +399,24 @@ convar_t *Cvar_Set2( const char *var_name, const char *value, qboolean force )
return var;
// any latched values not allowed for game cvars
- if( dll_variable ) force = true;
+ if( FBitSet( var->flags, CVAR_SERVERDLL ))
+ force = true;
if( !force )
{
- if( var->flags & ( CVAR_READ_ONLY|CVAR_GLCONFIG ))
+ if( FBitSet( var->flags, CVAR_READ_ONLY|CVAR_GLCONFIG ))
{
MsgDev( D_INFO, "%s is read only.\n", var_name );
return var;
}
- if( var->flags & CVAR_INIT )
+ if( FBitSet( var->flags, CVAR_INIT ))
{
MsgDev( D_INFO, "%s is write protected.\n", var_name );
return var;
}
- if( var->flags & ( CVAR_LATCH|CVAR_LATCH_VIDEO ))
+ if( FBitSet( var->flags, CVAR_LATCH ))
{
if( var->latched_string )
{
@@ -414,16 +430,11 @@ convar_t *Cvar_Set2( const char *var_name, const char *value, qboolean force )
return var;
}
- if( var->flags & CVAR_LATCH && Cvar_VariableInteger( "host_serverstate" ))
+ if( FBitSet( var->flags, CVAR_LATCH ) && Cvar_VariableInteger( "host_serverstate" ))
{
MsgDev( D_INFO, "%s will be changed upon restarting.\n", var->name );
var->latched_string = copystring( value );
}
- else if( var->flags & CVAR_LATCH_VIDEO )
- {
- MsgDev( D_INFO, "%s will be changed upon restarting video.\n", var->name );
- var->latched_string = copystring( value );
- }
else
{
Mem_Free( var->string ); // free the old value string
@@ -436,7 +447,7 @@ convar_t *Cvar_Set2( const char *var_name, const char *value, qboolean force )
return var;
}
- if(( var->flags & CVAR_CHEAT ) && !Cvar_VariableInteger( "sv_cheats" ))
+ if( FBitSet( var->flags, CVAR_CHEAT ) && !Cvar_VariableInteger( "sv_cheats" ))
{
MsgDev( D_INFO, "%s is cheat protected.\n", var_name );
return var;
@@ -444,7 +455,7 @@ convar_t *Cvar_Set2( const char *var_name, const char *value, qboolean force )
}
else
{
- if( !dll_variable && var->latched_string )
+ if( !FBitSet( var->flags, CVAR_SERVERDLL ) && var->latched_string )
{
Mem_Free( var->latched_string );
var->latched_string = NULL;
@@ -453,39 +464,32 @@ convar_t *Cvar_Set2( const char *var_name, const char *value, qboolean force )
pszValue = value;
- // This cvar's string must only contain printable characters.
- // Strip out any other crap.
- // We'll fill in "empty" if nothing is left
- if( var->flags & CVAR_PRINTABLEONLY )
+ // this cvar's string must only contain printable characters.
+ // strip out any other crap.
+ // we'll fill in "empty" if nothing is left
+ if( FBitSet( var->flags, CVAR_PRINTABLEONLY ))
{
- const char *pS;
- char *pD;
+ const char *s;
+ char *d;
- // clear out new string
szNew[0] = '\0';
-
- pS = pszValue;
- pD = szNew;
+ s = pszValue;
+ d = szNew;
// step through the string, only copying back in characters that are printable
- while( *pS )
+ while( *s )
{
- if( ((byte)*pS) < 32 || ((byte)*pS) > 255 )
+ if( ((byte)*s) < 32 )
{
- pS++;
+ s++;
continue;
}
- *pD++ = *pS++;
+ *d++ = *s++;
}
+ *d = '\0';
- // terminate the new string
- *pD = '\0';
-
- // if it's empty, then insert a marker string
if( !Q_strlen( szNew ))
- {
- Q_strcpy( szNew, "default" );
- }
+ Q_strncpy( szNew, "default", sizeof( szNew ));
// point the value here.
pszValue = szNew;
@@ -495,24 +499,39 @@ convar_t *Cvar_Set2( const char *var_name, const char *value, qboolean force )
if( !Q_strcmp( pszValue, var->string ))
return var;
- if( var->flags & CVAR_USERINFO )
- userinfo->modified = true; // transmit at next oportunity
+ if( FBitSet( var->flags, CVAR_SERVERNOTIFY ))
+ {
+ if( !FBitSet( var->flags, CVAR_UNLOGGED ))
+ {
+ if( FBitSet( var->flags, CVAR_PROTECTED ))
+ {
+ SV_BroadcastPrintf( NULL, PRINT_HIGH, "\"%s\" changed to \"%s\"\n", var->name, "***PROTECTED***" );
+ }
+ else
+ {
+ SV_BroadcastPrintf( NULL, PRINT_HIGH, "\"%s\" changed to \"%s\"\n", var->name, pszValue );
+ }
+ }
+ }
- if( var->flags & CVAR_PHYSICINFO )
- physinfo->modified = true; // transmit at next oportunity
+ if( FBitSet( var->flags, CVAR_USERINFO ))
+ userinfo->modified = true; // transmit at next oportunity
- if( var->flags & CVAR_SERVERINFO )
- serverinfo->modified = true; // transmit at next oportunity
+ if( FBitSet( var->flags, CVAR_PHYSICINFO ))
+ physinfo->modified = true; // transmit at next oportunity
- if( var->flags & CVAR_RENDERINFO )
- renderinfo->modified = true; // transmit at next oportunity
+ if( FBitSet( var->flags, CVAR_SERVERINFO ))
+ serverinfo->modified = true; // transmit at next oportunity
+
+ if( FBitSet( var->flags, CVAR_RENDERINFO ))
+ renderinfo->modified = true; // transmit at next oportunity
// free the old value string
Mem_Free( var->string );
var->string = copystring( pszValue );
var->value = Q_atof( var->string );
- if( !dll_variable )
+ if( !FBitSet( var->flags, CVAR_SERVERDLL ))
{
var->integer = Q_atoi( var->string );
var->modified = true;
@@ -549,53 +568,33 @@ Cvar_FullSet
void Cvar_FullSet( const char *var_name, const char *value, int flags )
{
convar_t *var;
- qboolean dll_variable = false;
- var = Cvar_FindVar( var_name );
- if( !var )
+ if(( var = Cvar_FindVar( var_name )) == NULL )
{
// create it
Cvar_Get( var_name, value, flags, "" );
return;
}
- // use this check to prevent acessing for unexisting fields
- // for cvar_t: latechd_string, description, etc
- if( var->flags & CVAR_EXTDLL )
- {
- dll_variable = true;
- }
+ if( FBitSet( var->flags, CVAR_USERINFO ))
+ userinfo->modified = true; // transmit at next oportunity
- if( var->flags & CVAR_USERINFO )
- {
- // transmit at next oportunity
- userinfo->modified = true;
- }
+ if( FBitSet( var->flags, CVAR_PHYSICINFO ))
+ physinfo->modified = true; // transmit at next oportunity
- if( var->flags & CVAR_PHYSICINFO )
- {
- // transmit at next oportunity
- physinfo->modified = true;
- }
+ if( FBitSet( var->flags, CVAR_SERVERINFO ))
+ serverinfo->modified = true; // transmit at next oportunity
- if( var->flags & CVAR_SERVERINFO )
- {
- // transmit at next oportunity
- serverinfo->modified = true;
- }
-
- if( var->flags & CVAR_RENDERINFO )
- {
- // transmit at next oportunity
- renderinfo->modified = true;
- }
+ if( FBitSet( var->flags, CVAR_RENDERINFO ))
+ renderinfo->modified = true; // transmit at next oportunity
Mem_Free( var->string ); // free the old value string
var->string = copystring( value );
var->value = Q_atof( var->string );
var->flags = flags;
- if( dll_variable ) return; // below fields doesn't exist in cvar_t
+ if( FBitSet( var->flags, CVAR_SERVERDLL ))
+ return; // below fields doesn't exist in cvar_t
var->integer = Q_atoi( var->string );
var->modified = true;
@@ -608,15 +607,17 @@ Cvar_DirectSet
*/
void Cvar_DirectSet( cvar_t *var, const char *value )
{
- cvar_t *test;
const char *pszValue;
char szNew[MAX_SYSPATH];
if( !var ) return; // GET_CVAR_POINTER is failed ?
// make sure what is really pointer to the cvar
- test = (cvar_t *)Cvar_FindVar( var->name );
- ASSERT( var == test );
+ if( var != (cvar_t *)Cvar_FindVar( var->name ))
+ {
+ MsgDev( D_ERROR, "Cvar_DirectSet: invalid pointer to cvar\n" );
+ return;
+ }
if( value && !Cvar_ValidateString( value, true ))
{
@@ -624,60 +625,59 @@ void Cvar_DirectSet( cvar_t *var, const char *value )
value = "0";
}
- if( !value ) value = "0";
+ if( !value )
+ {
+ if( FBitSet( var->flags, CVAR_SERVERDLL ))
+ value = "0";
+ else value = ((convar_t *)var)->reset_string;
+ }
- if( var->flags & ( CVAR_READ_ONLY|CVAR_GLCONFIG|CVAR_INIT|CVAR_RENDERINFO|CVAR_LATCH|CVAR_LATCH_VIDEO ))
+ if( FBitSet( var->flags, CVAR_READ_ONLY|CVAR_GLCONFIG|CVAR_INIT|CVAR_RENDERINFO|CVAR_LATCH ))
{
// Cvar_DirectSet cannot change these cvars at all
return;
}
- if(( var->flags & CVAR_CHEAT ) && !Cvar_VariableInteger( "sv_cheats" ))
+ if( FBitSet( var->flags, CVAR_CHEAT ) && !Cvar_VariableInteger( "sv_cheats" ))
{
- // cheats are disabled
+ MsgDev( D_INFO, "%s is cheat protected.\n", var->name );
return;
}
pszValue = value;
- // This cvar's string must only contain printable characters.
- // Strip out any other crap.
- // We'll fill in "empty" if nothing is left
- if( var->flags & CVAR_PRINTABLEONLY )
+ // this cvar's string must only contain printable characters.
+ // strip out any other crap.
+ // we'll fill in "empty" if nothing is left
+ if( FBitSet( var->flags, CVAR_PRINTABLEONLY ))
{
- const char *pS;
- char *pD;
+ const char *s;
+ char *d;
- // clear out new string
szNew[0] = '\0';
-
- pS = pszValue;
- pD = szNew;
+ s = pszValue;
+ d = szNew;
// step through the string, only copying back in characters that are printable
- while( *pS )
+ while( *s )
{
- if( *pS < 32 || *pS > 255 )
+ if( ((byte)*s) < 32 )
{
- pS++;
+ s++;
continue;
}
- *pD++ = *pS++;
+ *d++ = *s++;
}
+ *d = '\0';
- // terminate the new string
- *pD = '\0';
-
- // if it's empty, then insert a marker string
if( !Q_strlen( szNew ))
- {
- Q_strcpy( szNew, "default" );
- }
+ Q_strncpy( szNew, "default", sizeof( szNew ));
// point the value here.
pszValue = szNew;
}
+ // nothing to change
if( !Q_strcmp( pszValue, var->string ))
return;
@@ -697,6 +697,12 @@ void Cvar_DirectSet( cvar_t *var, const char *value )
Mem_Free( var->string );
var->string = copystring( pszValue );
var->value = Q_atof( var->string );
+
+ if( FBitSet( var->flags, CVAR_SERVERDLL ))
+ return; // below fields doesn't exist in cvar_t
+
+ ((convar_t *)var)->integer = Q_atoi( var->string );
+ ((convar_t *)var)->modified = true;
}
/*
@@ -739,10 +745,10 @@ void Cvar_SetCheatState( void )
for( var = cvar_vars; var; var = var->next )
{
// can't process dll cvars - missed latched_string, reset_string
- if( var->flags & CVAR_EXTDLL )
+ if( FBitSet( var->flags, CVAR_SERVERDLL ))
continue;
- if( var->flags & CVAR_CHEAT )
+ if( FBitSet( var->flags, CVAR_CHEAT ))
{
// the CVAR_LATCHED|CVAR_CHEAT vars might escape the reset here
// because of a different var->latched_string
@@ -753,9 +759,7 @@ void Cvar_SetCheatState( void )
}
if( Q_strcmp( var->reset_string, var->string ))
- {
Cvar_Set( var->name, var->reset_string );
- }
}
}
}
@@ -778,7 +782,8 @@ qboolean Cvar_Command( void )
// perform a variable print or set
if( Cmd_Argc() == 1 )
{
- if( v->flags & ( CVAR_INIT|CVAR_EXTDLL )) Msg( "%s: %s\n", v->name, v->string );
+ if( FBitSet( v->flags, CVAR_INIT|CVAR_SERVERDLL ))
+ Msg( "%s: %s\n", v->name, v->string );
else Msg( "%s: %s ( ^3%s^7 )\n", v->name, v->string, v->reset_string );
return true;
}
@@ -805,8 +810,7 @@ void Cvar_Toggle_f( void )
return;
}
- v = Cvar_VariableValue( Cmd_Argv( 1 ));
- v = !v;
+ v = !Cvar_VariableInteger( Cmd_Argv( 1 ));
Cvar_Set2( Cmd_Argv( 1 ), va( "%i", v ), false );
}
@@ -825,16 +829,18 @@ void Cvar_Set_f( void )
char combined[MAX_CMD_TOKENS];
c = Cmd_Argc();
+
if( c < 3 )
{
Msg( "Usage: set <variable> <value>\n" );
return;
}
- combined[0] = 0;
+
+ combined[0] = '\0';
for( i = 2; i < c; i++ )
{
- len = Q_strlen( Cmd_Argv(i) + 1 );
+ len = Q_strlen( Cmd_Argv( i ) + 1 );
if( l + len >= MAX_CMD_TOKENS - 2 )
break;
Q_strcat( combined, Cmd_Argv( i ));
@@ -866,7 +872,7 @@ void Cvar_SetU_f( void )
v = Cvar_FindVar( Cmd_Argv( 1 ));
if( !v ) return;
- v->flags |= CVAR_USERINFO;
+ SetBits( v->flags, CVAR_USERINFO );
}
/*
@@ -890,7 +896,7 @@ void Cvar_SetP_f( void )
v = Cvar_FindVar( Cmd_Argv( 1 ));
if( !v ) return;
- v->flags |= CVAR_PHYSICINFO;
+ SetBits( v->flags, CVAR_PHYSICINFO );
}
/*
@@ -909,11 +915,12 @@ void Cvar_SetS_f( void )
Msg( "Usage: sets <variable> <value>\n" );
return;
}
+
Cvar_Set_f();
v = Cvar_FindVar( Cmd_Argv( 1 ));
if( !v ) return;
- v->flags |= CVAR_SERVERINFO;
+ SetBits( v->flags, CVAR_SERVERINFO );
}
/*
@@ -937,7 +944,7 @@ void Cvar_SetA_f( void )
v = Cvar_FindVar( Cmd_Argv( 1 ));
if( !v ) return;
- v->flags |= CVAR_ARCHIVE;
+ SetBits( v->flags, CVAR_ARCHIVE );
}
/*
@@ -988,6 +995,7 @@ void Cvar_Reset_f( void )
Msg( "Usage: reset <variable>\n" );
return;
}
+
Cvar_Reset( Cmd_Argv( 1 ));
}
@@ -1000,12 +1008,13 @@ void Cvar_List_f( void )
{
convar_t *var;
char *match = NULL;
- int i = 0, j = 0;
+ char *value;
+ int i = 0;
if( Cmd_Argc() > 1 )
match = Cmd_Argv( 1 );
- for( var = cvar_vars; var; var = var->next, i++ )
+ for( var = cvar_vars; var; var = var->next )
{
if( var->name[0] == '@' )
continue; // never shows system cvars
@@ -1013,44 +1022,17 @@ void Cvar_List_f( void )
if( match && !Q_stricmpext( match, var->name ))
continue;
- if( var->flags & CVAR_SERVERINFO ) Msg( "SV " );
- else Msg( " " );
-
- if( var->flags & CVAR_USERINFO ) Msg( "USER " );
- else Msg( " " );
-
- if( var->flags & CVAR_PHYSICINFO ) Msg( "PHYS " );
- else Msg( " " );
-
- if( var->flags & CVAR_READ_ONLY ) Msg( "READ " );
- else Msg( " " );
-
- if( var->flags & CVAR_INIT ) Msg( "INIT " );
- else Msg( " " );
-
- if( var->flags & CVAR_ARCHIVE ) Msg( "ARCH " );
- else Msg( " " );
+ if( Q_colorstr( var->string ))
+ value = va( "\"%s\"", var->string );
+ else value = va( "\"^2%s^7\"", var->string );
- if( var->flags & CVAR_LATCH ) Msg( "LATCH " );
- else Msg( " " );
-
- if( var->flags & CVAR_LATCH_VIDEO ) Msg( "VIDEO " );
- else Msg( " " );
-
- if( var->flags & CVAR_GLCONFIG ) Msg( "OPENGL" );
- else Msg( " " );
-
- if( var->flags & CVAR_CHEAT ) Msg( "CHEAT " );
- else Msg( " " );
-
- if( var->flags & CVAR_EXTDLL )
- Msg(" %s \"%s\" %s\n", var->name, var->string, "game cvar" );
- else Msg(" %s \"%s\" %s\n", var->name, var->string, var->description );
- j++;
+ if( FBitSet( var->flags, CVAR_SERVERDLL ))
+ Msg( " %-*s %s ^3%s^7\n", 32, var->name, value, "server cvar" );
+ else Msg( " %-*s %s ^3%s^7\n", 32, var->name, value, var->description );
+ i++;
}
- Msg( "\n%i cvars\n", j );
- Msg( "%i total cvars\n", i );
+ Msg( "\n%i cvars\n", i );
}
/*
@@ -1072,16 +1054,15 @@ void Cvar_Restart_f( void )
var = *prev;
if( !var ) break;
- // don't mess with rom values, or some inter-module
- // communication will get broken (cl.active, etc)
- if( var->flags & ( CVAR_READ_ONLY|CVAR_GLCONFIG|CVAR_INIT|CVAR_RENDERINFO|CVAR_EXTDLL ))
+ // don't mess with rom values, or some inter-module communication will get broken (cl.active, etc)
+ if( FBitSet( var->flags, CVAR_READ_ONLY|CVAR_GLCONFIG|CVAR_INIT|CVAR_RENDERINFO|CVAR_SERVERDLL ))
{
prev = &var->next;
continue;
}
// throw out any variables the user created
- if( var->flags & CVAR_USER_CREATED )
+ if( FBitSet( var->flags, CVAR_USER_CREATED ))
{
*prev = var->next;
if( var->name ) Mem_Free( var->name );
@@ -1118,113 +1099,51 @@ void Cvar_Latched_f( void )
var = *prev;
if( !var ) break;
- if( var->flags & CVAR_EXTDLL )
+ if( FBitSet( var->flags, CVAR_SERVERDLL ))
{
prev = &var->next;
continue;
}
- if( var->flags & CVAR_LATCH && var->latched_string )
+ if( FBitSet( var->flags, CVAR_LATCH ) && var->latched_string )
{
Cvar_FullSet( var->name, var->latched_string, var->flags );
Mem_Free( var->latched_string );
var->latched_string = NULL;
}
- prev = &var->next;
- }
-}
-
-/*
-============
-Cvar_LatchedVideo_f
-
-Now all latched video strings is valid
-============
-*/
-void Cvar_LatchedVideo_f( void )
-{
- convar_t *var;
- convar_t **prev;
-
- prev = &cvar_vars;
-
- while ( 1 )
- {
- var = *prev;
- if( !var ) break;
-
- if( var->flags & CVAR_EXTDLL )
- {
- prev = &var->next;
- continue;
- }
- if( var->flags & CVAR_LATCH_VIDEO && var->latched_string )
- {
- Cvar_FullSet( var->name, var->latched_string, var->flags );
- Mem_Free( var->latched_string );
- var->latched_string = NULL;
- }
prev = &var->next;
}
}
/*
============
-Cvar_Unlink_f
+Cvar_Unlink
-unlink all cvars with flag CVAR_EXTDLL
+unlink all cvars with specified flag
============
*/
-void Cvar_Unlink_f( void )
+void Cvar_Unlink( int group )
{
convar_t *var;
convar_t **prev;
int count = 0;
- if( Cvar_VariableInteger( "host_gameloaded" ))
+ if( Cvar_VariableInteger( "host_gameloaded" ) && FBitSet( group, CVAR_SERVERDLL ))
{
- MsgDev( D_NOTE, "can't unlink cvars while game is loaded\n" );
+ MsgDev( D_INFO, "can't unlink variables while server is loaded\n" );
return;
}
- prev = &cvar_vars;
-
- while( 1 )
+ if( Cvar_VariableInteger( "host_clientloaded" ) && FBitSet( group, CVAR_CLIENTDLL ))
{
- var = *prev;
- if( !var ) break;
-
- // ignore all non-game cvars
- if( !( var->flags & CVAR_EXTDLL ))
- {
- prev = &var->next;
- continue;
- }
-
- // throw out any variables the game created
- *prev = var->next;
- if( var->string ) Mem_Free( var->string );
- count++;
+ MsgDev( D_INFO, "can't unlink variables while client is loaded\n" );
+ return;
}
-}
-
-/*
-============
-Cvar_Unlink
-
-unlink all cvars with flag CVAR_CLIENTDLL
-============
-*/
-void Cvar_Unlink( void )
-{
- convar_t *var;
- convar_t **prev;
- int count = 0;
- if( Cvar_VariableInteger( "host_clientloaded" ))
+ if( Cvar_VariableInteger( "host_gameuiloaded" ) && FBitSet( group, CVAR_GAMEUIDLL ))
{
- MsgDev( D_NOTE, "can't unlink cvars while client is loaded\n" );
+ MsgDev( D_INFO, "can't unlink variables while GameUI is loaded\n" );
return;
}
@@ -1235,8 +1154,8 @@ void Cvar_Unlink( void )
var = *prev;
if( !var ) break;
- // ignore all non-client cvars
- if(!( var->flags & CVAR_CLIENTDLL ))
+ // do filter by specified group
+ if( group && !FBitSet( var->flags, group ))
{
prev = &var->next;
continue;
@@ -1244,12 +1163,17 @@ void Cvar_Unlink( void )
// throw out any variables the game created
*prev = var->next;
- if( var->name ) Mem_Free( var->name );
if( var->string ) Mem_Free( var->string );
- if( var->latched_string ) Mem_Free( var->latched_string );
- if( var->reset_string ) Mem_Free( var->reset_string );
- if( var->description ) Mem_Free( var->description );
- Mem_Free( var );
+
+ if( !FBitSet( var->flags, CVAR_SERVERDLL ))
+ {
+ if( var->name ) Mem_Free( var->name );
+ if( var->latched_string ) Mem_Free( var->latched_string );
+ if( var->reset_string ) Mem_Free( var->reset_string );
+ if( var->description ) Mem_Free( var->description );
+ Mem_Free( var );
+ }
+
count++;
}
}
@@ -1268,19 +1192,18 @@ void Cvar_Init( void )
physinfo = Cvar_Get( "@physinfo", "0", CVAR_READ_ONLY, "" ); // use ->modified value only
serverinfo = Cvar_Get( "@serverinfo", "0", CVAR_READ_ONLY, "" ); // use ->modified value only
renderinfo = Cvar_Get( "@renderinfo", "0", CVAR_READ_ONLY, "" ); // use ->modified value only
-
- Cmd_AddCommand ("toggle", Cvar_Toggle_f, "toggles a console variable's values (use for more info)" );
- Cmd_AddCommand ("set", Cvar_Set_f, "create or change the value of a console variable" );
- Cmd_AddCommand ("sets", Cvar_SetS_f, "create or change the value of a serverinfo variable" );
- Cmd_AddCommand ("setu", Cvar_SetU_f, "create or change the value of a userinfo variable" );
- Cmd_AddCommand ("setp", Cvar_SetP_f, "create or change the value of a physicinfo variable" );
- Cmd_AddCommand ("setr", Cvar_SetR_f, "create or change the value of a renderinfo variable" );
- Cmd_AddCommand ("setgl", Cvar_SetGL_f, "create or change the value of a opengl variable" );
- Cmd_AddCommand ("seta", Cvar_SetA_f, "create or change the value of a console variable that will be saved to config.cfg" );
- Cmd_AddCommand ("reset", Cvar_Reset_f, "reset any type variable to initial value" );
- Cmd_AddCommand ("latch", Cvar_Latched_f, "apply latched values" );
- Cmd_AddCommand ("vidlatch", Cvar_LatchedVideo_f, "apply latched values for video subsystem" );
- Cmd_AddCommand ("cvarlist", Cvar_List_f, "display all console variables beginning with the specified prefix" );
- Cmd_AddCommand ("unsetall", Cvar_Restart_f, "reset all console variables to their default values" );
- Cmd_AddCommand ("@unlink", Cvar_Unlink_f, "unlink static cvars defined in gamedll" );
-}
+ cmd_scripting = Cvar_Get( "cmd_scripting", "0", CVAR_ARCHIVE, "enable simple condition checking and variable operations" );
+
+ Cmd_AddCommand( "toggle", Cvar_Toggle_f, "toggles a console variable's values (use for more info)" );
+ Cmd_AddCommand( "set", Cvar_Set_f, "create or change the value of a console variable" );
+ Cmd_AddCommand( "sets", Cvar_SetS_f, "create or change the value of a serverinfo variable" );
+ Cmd_AddCommand( "setu", Cvar_SetU_f, "create or change the value of a userinfo variable" );
+ Cmd_AddCommand( "setp", Cvar_SetP_f, "create or change the value of a physicinfo variable" );
+ Cmd_AddCommand( "setr", Cvar_SetR_f, "create or change the value of a renderinfo variable" );
+ Cmd_AddCommand( "setgl", Cvar_SetGL_f, "create or change the value of a opengl variable" );
+ Cmd_AddCommand( "seta", Cvar_SetA_f, "create or change the value of a console variable that will be saved to config.cfg" );
+ Cmd_AddCommand( "reset", Cvar_Reset_f, "reset any type variable to initial value" );
+ Cmd_AddCommand( "latch", Cvar_Latched_f, "apply latched values" );
+ Cmd_AddCommand( "cvarlist", Cvar_List_f, "display all console variables beginning with the specified prefix" );
+ Cmd_AddCommand( "unsetall", Cvar_Restart_f, "reset all console variables to their default values" );
+}
\ No newline at end of file
diff --git b/engine/common/filesystem.c a/engine/common/filesystem.c
index a32fa7c..0a184ab 100644
--- b/engine/common/filesystem.c
+++ a/engine/common/filesystem.c
@@ -24,14 +24,26 @@ GNU General Public License for more details.
#include "library.h"
#include "mathlib.h"
-#define FILE_BUFF_SIZE 2048
+#define FILE_COPY_SIZE (1024 * 1024)
+#define FILE_BUFF_SIZE (65535)
+
+// PAK errors
#define PAK_LOAD_OK 0
#define PAK_LOAD_COULDNT_OPEN 1
#define PAK_LOAD_BAD_HEADER 2
#define PAK_LOAD_BAD_FOLDERS 3
#define PAK_LOAD_TOO_MANY_FILES 4
#define PAK_LOAD_NO_FILES 5
-#define PAK_LOAD_CORRUPTED 6
+#define PAK_LOAD_CORRUPTED 6
+
+// WAD errors
+#define WAD_LOAD_OK 0
+#define WAD_LOAD_COULDNT_OPEN 1
+#define WAD_LOAD_BAD_HEADER 2
+#define WAD_LOAD_BAD_FOLDERS 3
+#define WAD_LOAD_TOO_MANY_FILES 4
+#define WAD_LOAD_NO_FILES 5
+#define WAD_LOAD_CORRUPTED 6
typedef struct stringlist_s
{
@@ -50,19 +62,19 @@ typedef struct wadtype_s
typedef struct file_s
{
int handle; // file descriptor
- fs_offset_t real_length; // uncompressed file size (for files opened in "read" mode)
- fs_offset_t position; // current position in the file
- fs_offset_t offset; // offset into the package (0 if external file)
+ long real_length; // uncompressed file size (for files opened in "read" mode)
+ long position; // current position in the file
+ long offset; // offset into the package (0 if external file)
int ungetc; // single stored character from ungetc, cleared to EOF when read
time_t filetime; // pak, wad or real filetime
- // Contents buffer
- fs_offset_t buff_ind, buff_len; // buffer current index and length
+ // contents buffer
+ long buff_ind, buff_len; // buffer current index and length
byte buff[FILE_BUFF_SIZE]; // intermediate buffer
};
typedef struct wfile_s
{
- char filename[MAX_SYSPATH];
+ string filename;
int infotableofs;
byte *mempool; // W_ReadLump temp buffers
int numlumps;
@@ -72,47 +84,41 @@ typedef struct wfile_s
time_t filetime;
};
-typedef struct packfile_s
-{
- char name[56];
- fs_offset_t offset;
- fs_offset_t realsize; // real file size (uncompressed)
-} packfile_t;
-
typedef struct pack_s
{
- char filename[MAX_SYSPATH];
+ string filename;
int handle;
int numfiles;
time_t filetime; // common for all packed files
- packfile_t *files;
+ dpackfile_t *files;
} pack_t;
typedef struct searchpath_s
{
- char filename[MAX_SYSPATH];
+ string filename;
pack_t *pack;
wfile_t *wad;
int flags;
struct searchpath_s *next;
} searchpath_t;
-byte *fs_mempool;
-searchpath_t *fs_searchpaths = NULL;
-searchpath_t fs_directpath; // static direct path
-char fs_rootdir[MAX_SYSPATH]; // engine root directory
-char fs_basedir[MAX_SYSPATH]; // base directory of game
-char fs_falldir[MAX_SYSPATH]; // game falling directory
-char fs_gamedir[MAX_SYSPATH]; // game current directory
-char gs_basedir[MAX_SYSPATH]; // initial dir before loading gameinfo.txt (used for compilers too)
-qboolean fs_ext_path = false; // attempt to read\write from ./ or ../ pathes
+byte *fs_mempool;
+searchpath_t *fs_searchpaths = NULL; // chain
+searchpath_t fs_directpath; // static direct path
+char fs_rootdir[MAX_SYSPATH]; // engine root directory
+char fs_basedir[MAX_SYSPATH]; // base directory of game
+char fs_falldir[MAX_SYSPATH]; // game falling directory
+char fs_gamedir[MAX_SYSPATH]; // game current directory
+char gs_basedir[MAX_SYSPATH]; // initial dir before loading gameinfo.txt (used for compilers too)
+qboolean fs_ext_path = false; // attempt to read\write from ./ or ../ pathes
+static const wadtype_t wad_hints[10];
static void FS_InitMemory( void );
const char *FS_FileExtension( const char *in );
static searchpath_t *FS_FindFile( const char *name, int *index, qboolean gamedironly );
static dlumpinfo_t *W_FindLump( wfile_t *wad, const char *name, const char matchtype );
-static packfile_t* FS_AddFileToPack( const char* name, pack_t *pack, fs_offset_t offset, fs_offset_t size );
-static byte *W_LoadFile( const char *path, fs_offset_t *filesizeptr, qboolean gamedironly );
+static dpackfile_t* FS_AddFileToPack( const char* name, pack_t *pack, long offset, long size );
+static byte *W_LoadFile( const char *path, long *filesizeptr, qboolean gamedironly );
static qboolean FS_SysFileExists( const char *path );
static qboolean FS_SysFolderExists( const char *path );
static long FS_SysFileTime( const char *filename );
@@ -126,56 +132,53 @@ FILEMATCH COMMON SYSTEM
=============================================================================
*/
-int matchpattern( const char *in, const char *pattern, qboolean caseinsensitive )
+int matchpattern( const char *str, const char *cmp, qboolean caseinsensitive )
{
int c1, c2;
- while( *pattern )
+ while( *cmp )
{
- switch( *pattern )
+ switch( *cmp )
{
case 0: return 1; // end of pattern
case '?': // match any single character
- if( *in == 0 || *in == '/' || *in == '\\' || *in == ':' )
+ if( *str == 0 || *str == '/' || *str == '\\' || *str == ':' )
return 0; // no match
- in++;
- pattern++;
+ str++;
+ cmp++;
break;
case '*': // match anything until following string
- if( !*in ) return 1; // match
- pattern++;
- while( *in )
+ if( !*str ) return 1; // match
+ cmp++;
+ while( *str )
{
- if( *in == '/' || *in == '\\' || *in == ':' )
+ if( *str == '/' || *str == '\\' || *str == ':' )
break;
// see if pattern matches at this offset
- if( matchpattern( in, pattern, caseinsensitive ))
+ if( matchpattern( str, cmp, caseinsensitive ))
return 1;
// nope, advance to next offset
- in++;
+ str++;
}
break;
default:
- if( *in != *pattern )
+ if( *str != *cmp )
{
if( !caseinsensitive )
return 0; // no match
- c1 = *in;
- if( c1 >= 'A' && c1 <= 'Z' )
- c1 += 'a' - 'A';
- c2 = *pattern;
- if( c2 >= 'A' && c2 <= 'Z' )
- c2 += 'a' - 'A';
- if( c1 != c2) return 0; // no match
+ c1 = Q_tolower( *str );
+ c2 = Q_tolower( *cmp );
+ if( c1 != c2 ) return 0; // no match
}
- in++;
- pattern++;
+
+ str++;
+ cmp++;
break;
}
}
- if( *in ) return 0; // reached end of pattern but not end of input
- return 1; // success
+ // reached end of pattern but not end of input?
+ return (*str) ? 0 : 1;
}
void stringlistinit( stringlist_t *list )
@@ -194,23 +197,25 @@ void stringlistfreecontents( stringlist_t *list )
list->strings[i] = NULL;
}
+ if( list->strings )
+ Mem_Free( list->strings );
+
list->numstrings = 0;
list->maxstrings = 0;
- if( list->strings ) Mem_Free( list->strings );
+ list->strings = NULL;
}
void stringlistappend( stringlist_t *list, char *text )
{
size_t textlen;
- char **oldstrings;
+
+ if( !Q_stricmp( text, "." ) || !Q_stricmp( text, ".." ))
+ return; // ignore the virtual directories
if( list->numstrings >= list->maxstrings )
{
- oldstrings = list->strings;
list->maxstrings += 4096;
- list->strings = Mem_Alloc( fs_mempool, list->maxstrings * sizeof( *list->strings ));
- if( list->numstrings ) memcpy( list->strings, oldstrings, list->numstrings * sizeof( *list->strings ));
- if( oldstrings ) Mem_Free( oldstrings );
+ list->strings = Mem_Realloc( fs_mempool, list->strings, list->maxstrings * sizeof( *list->strings ));
}
textlen = Q_strlen( text ) + 1;
@@ -221,8 +226,8 @@ void stringlistappend( stringlist_t *list, char *text )
void stringlistsort( stringlist_t *list )
{
- int i, j;
char *temp;
+ int i, j;
// this is a selection sort (finds the best entry for each slot)
for( i = 0; i < list->numstrings - 1; i++ )
@@ -241,10 +246,11 @@ void stringlistsort( stringlist_t *list )
void listdirectory( stringlist_t *list, const char *path )
{
- int i;
- char pattern[4096], *c;
+ char pattern[4096];
struct _finddata_t n_file;
long hFile;
+ char *c;
+ int i;
Q_strncpy( pattern, path, sizeof( pattern ));
Q_strncat( pattern, "*", sizeof( pattern ));
@@ -264,10 +270,7 @@ void listdirectory( stringlist_t *list, const char *path )
for( i = 0; i < list->numstrings; i++ )
{
for( c = list->strings[i]; *c; c++ )
- {
- if( *c >= 'A' && *c <= 'Z' )
- *c += 'a' - 'A';
- }
+ *c = Q_tolower( *c );
}
}
@@ -285,10 +288,10 @@ FS_AddFileToPack
Add a file to the list of files contained into a package
====================
*/
-static packfile_t* FS_AddFileToPack( const char* name, pack_t* pack, fs_offset_t offset, fs_offset_t size )
+static dpackfile_t* FS_AddFileToPack( const char* name, pack_t* pack, long offset, long size )
{
int left, right, middle;
- packfile_t *pfile;
+ dpackfile_t *pfile;
// look for the slot we should put that file into (binary search)
left = 0;
@@ -301,7 +304,7 @@ static packfile_t* FS_AddFileToPack( const char* name, pack_t* pack, fs_offset_t
diff = Q_stricmp( pack->files[middle].name, name );
// If we found the file, there's a problem
- if( !diff ) MsgDev( D_NOTE, "Package %s contains the file %s several times\n", pack->filename, name );
+ if( !diff ) MsgDev( D_WARN, "Package %s contains the file %s several times\n", pack->filename, name );
// If we're too far in the list
if( diff > 0 ) right = middle - 1;
@@ -314,8 +317,8 @@ static packfile_t* FS_AddFileToPack( const char* name, pack_t* pack, fs_offset_t
pack->numfiles++;
Q_strncpy( pfile->name, name, sizeof( pfile->name ));
- pfile->offset = offset;
- pfile->realsize = size;
+ pfile->filepos = offset;
+ pfile->filelen = size;
return pfile;
}
@@ -363,7 +366,8 @@ void FS_Path_f( void )
else if( s->wad ) Msg( "%s (%i files)", s->wad->filename, s->wad->numlumps );
else Msg( "%s", s->filename );
- if( s->flags & FS_GAMEDIR_PATH ) Msg( " ^2gamedir^7\n" );
+ if( FBitSet( s->flags, FS_GAMEDIR_PATH ))
+ Msg( " ^2gamedir^7\n" );
else Msg( "\n" );
}
}
@@ -436,8 +440,8 @@ of the list so they override previous pack files.
pack_t *FS_LoadPackPAK( const char *packfile, int *error )
{
dpackheader_t header;
- int i, numpackfiles;
int packhandle;
+ int i, numpackfiles;
pack_t *pack;
dpackfile_t *info;
@@ -500,16 +504,14 @@ pack_t *FS_LoadPackPAK( const char *packfile, int *error )
pack = (pack_t *)Mem_Alloc( fs_mempool, sizeof( pack_t ));
Q_strncpy( pack->filename, packfile, sizeof( pack->filename ));
+ pack->files = (dpackfile_t *)Mem_Alloc( fs_mempool, numpackfiles * sizeof( dpackfile_t ));
+ pack->filetime = FS_SysFileTime( packfile );
pack->handle = packhandle;
pack->numfiles = 0;
- pack->files = (packfile_t *)Mem_Alloc( fs_mempool, numpackfiles * sizeof( packfile_t ));
- pack->filetime = FS_SysFileTime( packfile );
// parse the directory
for( i = 0; i < numpackfiles; i++ )
- {
FS_AddFileToPack( info[i].name, pack, info[i].filepos, info[i].filelen );
- }
MsgDev( D_NOTE, "Adding packfile: %s (%i files)\n", packfile, numpackfiles );
if( error ) *error = PAK_LOAD_OK;
@@ -520,7 +522,7 @@ pack_t *FS_LoadPackPAK( const char *packfile, int *error )
/*
================
-FS_AddPack_Fullpath
+FS_AddPak_Fullpath
Adds the given pack to the search path.
The pack type is autodetected by the file extension.
@@ -532,7 +534,7 @@ If keep_plain_dirs is set, the pack will be added AFTER the first sequence of
plain directories.
================
*/
-static qboolean FS_AddPack_Fullpath( const char *pakfile, qboolean *already_loaded, qboolean keep_plain_dirs, int flags )
+static qboolean FS_AddPak_Fullpath( const char *pakfile, qboolean *already_loaded, qboolean keep_plain_dirs, int flags )
{
searchpath_t *search;
pack_t *pak = NULL;
@@ -558,7 +560,7 @@ static qboolean FS_AddPack_Fullpath( const char *pakfile, qboolean *already_load
if( keep_plain_dirs )
{
// find the first item whose next one is a pack or NULL
- searchpath_t *insertion_point = 0;
+ searchpath_t *insertion_point = NULL;
if( fs_searchpaths && !fs_searchpaths->pack )
{
insertion_point = fs_searchpaths;
@@ -603,7 +605,7 @@ static qboolean FS_AddPack_Fullpath( const char *pakfile, qboolean *already_load
else
{
if( errorcode != PAK_LOAD_NO_FILES )
- MsgDev( D_ERROR, "FS_AddPack_Fullpath: unable to load pak \"%s\"\n", pakfile );
+ MsgDev( D_ERROR, "FS_AddPak_Fullpath: unable to load pak \"%s\"\n", pakfile );
return false;
}
}
@@ -613,11 +615,12 @@ static qboolean FS_AddPack_Fullpath( const char *pakfile, qboolean *already_load
FS_AddWad_Fullpath
====================
*/
-static qboolean FS_AddWad_Fullpath( const char *wadfile, qboolean *already_loaded, qboolean keep_plain_dirs )
+static qboolean FS_AddWad_Fullpath( const char *wadfile, qboolean *already_loaded, qboolean keep_plain_dirs, int flags )
{
searchpath_t *search;
wfile_t *wad = NULL;
const char *ext = FS_FileExtension( wadfile );
+ int errorcode = WAD_LOAD_COULDNT_OPEN;
for( search = fs_searchpaths; search; search = search->next )
{
@@ -629,7 +632,7 @@ static qboolean FS_AddWad_Fullpath( const char *wadfile, qboolean *already_loade
}
if( already_loaded ) *already_loaded = false;
- if( !Q_stricmp( ext, "wad" )) wad = W_Open( wadfile, "rb" );
+ if( !Q_stricmp( ext, "wad" )) wad = W_Open( wadfile, "rb", &errorcode );
else MsgDev( D_ERROR, "\"%s\" doesn't have a wad extension\n", wadfile );
if( wad )
@@ -656,6 +659,7 @@ static qboolean FS_AddWad_Fullpath( const char *wadfile, qboolean *already_loade
search = (searchpath_t *)Mem_Alloc( fs_mempool, sizeof( searchpath_t ));
search->wad = wad;
search->next = fs_searchpaths;
+ search->flags |= flags;
fs_searchpaths = search;
}
else // otherwise we want to append directly after insertion_point.
@@ -663,6 +667,7 @@ static qboolean FS_AddWad_Fullpath( const char *wadfile, qboolean *already_loade
search = (searchpath_t *)Mem_Alloc( fs_mempool, sizeof( searchpath_t ));
search->wad = wad;
search->next = insertion_point->next;
+ search->flags |= flags;
insertion_point->next = search;
}
}
@@ -671,6 +676,7 @@ static qboolean FS_AddWad_Fullpath( const char *wadfile, qboolean *already_loade
search = (searchpath_t *)Mem_Alloc( fs_mempool, sizeof( searchpath_t ));
search->wad = wad;
search->next = fs_searchpaths;
+ search->flags |= flags;
fs_searchpaths = search;
}
@@ -679,7 +685,8 @@ static qboolean FS_AddWad_Fullpath( const char *wadfile, qboolean *already_loade
}
else
{
- MsgDev( D_ERROR, "FS_AddWad_Fullpath: unable to load wad \"%s\"\n", wadfile );
+ if( errorcode != WAD_LOAD_NO_FILES )
+ MsgDev( D_ERROR, "FS_AddWad_Fullpath: unable to load wad \"%s\"\n", wadfile );
return false;
}
}
@@ -699,7 +706,7 @@ void FS_AddGameDirectory( const char *dir, int flags )
string fullpath;
int i;
- if(!( flags & FS_NOWRITE_PATH ))
+ if( !FBitSet( flags, FS_NOWRITE_PATH ))
Q_strncpy( fs_gamedir, dir, sizeof( fs_gamedir ));
stringlistinit( &list );
@@ -712,7 +719,7 @@ void FS_AddGameDirectory( const char *dir, int flags )
if( !Q_stricmp( FS_FileExtension( list.strings[i] ), "pak" ))
{
Q_sprintf( fullpath, "%s%s", dir, list.strings[i] );
- FS_AddPack_Fullpath( fullpath, NULL, false, flags );
+ FS_AddPak_Fullpath( fullpath, NULL, false, flags );
}
}
@@ -722,7 +729,7 @@ void FS_AddGameDirectory( const char *dir, int flags )
if( !Q_stricmp( FS_FileExtension( list.strings[i] ), "wad" ))
{
Q_sprintf( fullpath, "%s%s", dir, list.strings[i] );
- FS_AddWad_Fullpath( fullpath, NULL, false );
+ FS_AddWad_Fullpath( fullpath, NULL, false, flags );
}
}
@@ -761,13 +768,20 @@ const char *FS_FileExtension( const char *in )
separator = Q_strrchr( in, '/' );
backslash = Q_strrchr( in, '\\' );
- if( !separator || separator < backslash ) separator = backslash;
+
+ if( !separator || separator < backslash )
+ separator = backslash;
+
colon = Q_strrchr( in, ':' );
- if( !separator || separator < colon ) separator = colon;
+
+ if( !separator || separator < colon )
+ separator = colon;
dot = Q_strrchr( in, '.' );
+
if( dot == NULL || ( separator && ( dot < separator )))
return "";
+
return dot + 1;
}
@@ -782,9 +796,15 @@ const char *FS_FileWithoutPath( const char *in )
separator = Q_strrchr( in, '/' );
backslash = Q_strrchr( in, '\\' );
- if( !separator || separator < backslash ) separator = backslash;
+
+ if( !separator || separator < backslash )
+ separator = backslash;
+
colon = Q_strrchr( in, ':' );
- if( !separator || separator < colon ) separator = colon;
+
+ if( !separator || separator < colon )
+ separator = colon;
+
return separator ? separator + 1 : in;
}
@@ -793,10 +813,9 @@ const char *FS_FileWithoutPath( const char *in )
FS_ExtractFilePath
============
*/
-void FS_ExtractFilePath( const char* const path, char* dest )
+void FS_ExtractFilePath( const char *path, char *dest )
{
- const char *src;
- src = path + Q_strlen( path ) - 1;
+ const char *src = path + Q_strlen( path ) - 1;
// back up until a \ or the start
while( src != path && !(*(src - 1) == '\\' || *(src - 1) == '/' ))
@@ -821,7 +840,9 @@ void FS_ClearSearchPath( void )
{
searchpath_t *search = fs_searchpaths;
- if( search->flags & FS_STATIC_PATH )
+ if( !search ) break;
+
+ if( FBitSet( search->flags, FS_STATIC_PATH ))
{
// skip read-only pathes
if( search->next )
@@ -841,7 +862,7 @@ void FS_ClearSearchPath( void )
W_Close( search->wad );
}
- if( search ) Mem_Free( search );
+ Mem_Free( search );
}
}
@@ -874,15 +895,7 @@ int FS_CheckNastyPath( const char *path, qboolean isgamedir )
// Windows and UNIXes: don't allow absolute paths
if( path[0] == '/' && !fs_ext_path ) return 2; // attempt to go outside the game directory
-#if 0
- // all: don't allow . characters before the last slash (it should only be used in filenames, not path elements),
- // this catches all imaginable cases of ./, ../, .../, etc
- if( Q_strchr( path, '.' ) && !fs_ext_path )
- {
- if( isgamedir ) return 2; // gamedir is entirely path elements, so simply forbid . entirely
- if( Q_strchr( path, '.' ) < Q_strrchr( path, '/' )) return 2; // possible attempt to go outside the game directory
- }
-#endif
+
// all: forbid trailing slash on gamedir
if( isgamedir && !fs_ext_path && path[Q_strlen( path )-1] == '/' ) return 2;
@@ -912,55 +925,16 @@ void FS_Rescan( void )
FS_AddGameHierarchy( GI->gamedir, FS_GAMEDIR_PATH );
}
+/*
+================
+FS_Rescan_f
+================
+*/
void FS_Rescan_f( void )
{
FS_Rescan();
}
-static qboolean FS_ParseVector( char **pfile, float *v, size_t size )
-{
- string token;
- qboolean bracket = false;
- char *saved;
- uint i;
-
- if( v == NULL || size == 0 )
- return false;
-
- memset( v, 0, sizeof( *v ) * size );
-
- if( size == 1 )
- {
- *pfile = COM_ParseFile( *pfile, token );
- v[0] = Q_atof( token );
- return true;
- }
-
- saved = *pfile;
-
- if(( *pfile = COM_ParseFile( *pfile, token )) == NULL )
- return false;
-
- if( token[0] == '(' )
- bracket = true;
- else *pfile = saved; // restore token to right get it again
-
- for( i = 0; i < size; i++ )
- {
- *pfile = COM_ParseFile( *pfile, token );
- v[i] = Q_atof( token );
- }
-
- if( !bracket ) return true; // done
-
- if(( *pfile = COM_ParseFile( *pfile, token )) == NULL )
- return false;
-
- if( token[0] == ')' )
- return true;
- return false;
-}
-
/*
================
FS_WriteGameInfo
@@ -1113,6 +1087,11 @@ void FS_CreateDefaultGameInfo( const char *filename )
FS_WriteGameInfo( filename, &defGI );
}
+/*
+================
+FS_ParseLiblistGam
+================
+*/
static qboolean FS_ParseLiblistGam( const char *filename, const char *gamedir, gameinfo_t *GameInfo )
{
char *afile, *pfile;
@@ -1461,8 +1440,8 @@ static qboolean FS_ParseGameInfo( const char *gamedir, gameinfo_t *GameInfo )
}
else
{
- FS_ParseVector( &pfile, GameInfo->client_mins[hullNum], 3 );
- FS_ParseVector( &pfile, GameInfo->client_maxs[hullNum], 3 );
+ COM_ParseVector( &pfile, GameInfo->client_mins[hullNum], 3 );
+ COM_ParseVector( &pfile, GameInfo->client_maxs[hullNum], 3 );
}
}
else if( !Q_strnicmp( token, "ambient", 7 ))
@@ -1657,18 +1636,22 @@ static file_t* FS_SysOpen( const char* filepath, const char* mode )
// Parse the mode string
switch( mode[0] )
{
- case 'r':
+ case 'r': // read
mod = O_RDONLY;
opt = 0;
break;
- case 'w':
+ case 'w': // write
mod = O_WRONLY;
opt = O_CREAT | O_TRUNC;
break;
- case 'a':
+ case 'a': // append
mod = O_WRONLY;
opt = O_CREAT | O_APPEND;
break;
+ case 'e': // edit
+ mod = O_WRONLY;
+ opt = O_CREAT;
+ break;
default:
MsgDev( D_ERROR, "FS_SysOpen(%s, %s): invalid mode\n", filepath, mode );
return NULL;
@@ -1720,13 +1703,13 @@ Open a packed file using its package file descriptor
*/
file_t *FS_OpenPackedFile( pack_t *pack, int pack_ind )
{
- packfile_t *pfile;
+ dpackfile_t *pfile;
int dup_handle;
file_t *file;
pfile = &pack->files[pack_ind];
- if( lseek( pack->handle, pfile->offset, SEEK_SET ) == -1 )
+ if( lseek( pack->handle, pfile->filepos, SEEK_SET ) == -1 )
return NULL;
dup_handle = dup( pack->handle );
@@ -1735,10 +1718,9 @@ file_t *FS_OpenPackedFile( pack_t *pack, int pack_ind )
return NULL;
file = (file_t *)Mem_Alloc( fs_mempool, sizeof( *file ));
- memset( file, 0, sizeof( *file ));
file->handle = dup_handle;
- file->real_length = pfile->realsize;
- file->offset = pfile->offset;
+ file->real_length = pfile->filelen;
+ file->offset = pfile->filepos;
file->position = 0;
file->ungetc = EOF;
@@ -1755,10 +1737,10 @@ Look for a file in the filesystem only
qboolean FS_SysFileExists( const char *path )
{
int desc;
-
- desc = open( path, O_RDONLY|O_BINARY );
- if( desc < 0 ) return false;
+ if(( desc = open( path, O_RDONLY|O_BINARY )) < 0 )
+ return false;
+
close( desc );
return true;
}
@@ -1774,7 +1756,7 @@ qboolean FS_SysFolderExists( const char *path )
{
DWORD dwFlags = GetFileAttributes( path );
- return ( dwFlags != -1 ) && ( dwFlags & FILE_ATTRIBUTE_DIRECTORY );
+ return ( dwFlags != -1 ) && FBitSet( dwFlags, FILE_ATTRIBUTE_DIRECTORY );
}
/*
@@ -1796,7 +1778,7 @@ static searchpath_t *FS_FindFile( const char *name, int* index, qboolean gamedir
// search through the path, one element at a time
for( search = fs_searchpaths; search; search = search->next )
{
- if( gamedironly & !( search->flags & FS_GAMEDIR_PATH ))
+ if( gamedironly & !FBitSet( search->flags, FS_GAMEDIR_PATH ))
continue;
// is the element a pak file?
@@ -1987,7 +1969,7 @@ file_t *FS_Open( const char *filepath, const char *mode, qboolean gamedironly )
return NULL;
// if the file is opened in "write", "append", or "read/write" mode
- if( mode[0] == 'w' || mode[0] == 'a' || Q_strchr( mode, '+' ))
+ if( mode[0] == 'w' || mode[0] == 'a'|| mode[0] == 'e' || Q_strchr( mode, '+' ))
{
char real_path[MAX_SYSPATH];
@@ -2024,9 +2006,9 @@ FS_Write
Write "datasize" bytes into a file
====================
*/
-fs_offset_t FS_Write( file_t *file, const void *data, size_t datasize )
+long FS_Write( file_t *file, const void *data, size_t datasize )
{
- fs_offset_t result;
+ long result;
if( !file ) return 0;
@@ -2038,7 +2020,7 @@ fs_offset_t FS_Write( file_t *file, const void *data, size_t datasize )
FS_Purge( file );
// write the buffer and update the position
- result = write( file->handle, data, (fs_offset_t)datasize );
+ result = write( file->handle, data, (long)datasize );
file->position = lseek( file->handle, 0, SEEK_CUR );
if( file->real_length < file->position )
file->real_length = file->position;
@@ -2055,10 +2037,10 @@ FS_Read
Read up to "buffersize" bytes from a file
====================
*/
-fs_offset_t FS_Read( file_t *file, void *buffer, size_t buffersize )
+long FS_Read( file_t *file, void *buffer, size_t buffersize )
{
- fs_offset_t count, done;
- fs_offset_t nb;
+ long count, done;
+ long nb;
// nothing to copy
if( buffersize == 0 ) return 1;
@@ -2078,7 +2060,7 @@ fs_offset_t FS_Read( file_t *file, void *buffer, size_t buffersize )
{
count = file->buff_len - file->buff_ind;
- done += ((fs_offset_t)buffersize > count ) ? count : (fs_offset_t)buffersize;
+ done += ((long)buffersize > count ) ? count : (long)buffersize;
memcpy( buffer, &file->buff[file->buff_ind], done );
file->buff_ind += done;
@@ -2095,8 +2077,8 @@ fs_offset_t FS_Read( file_t *file, void *buffer, size_t buffersize )
// if we have a lot of data to get, put them directly into "buffer"
if( buffersize > sizeof( file->buff ) / 2 )
{
- if( count > (fs_offset_t)buffersize )
- count = (fs_offset_t)buffersize;
+ if( count > (long)buffersize )
+ count = (long)buffersize;
lseek( file->handle, file->offset + file->position, SEEK_SET );
nb = read (file->handle, &((byte *)buffer)[done], count );
@@ -2104,14 +2086,14 @@ fs_offset_t FS_Read( file_t *file, void *buffer, size_t buffersize )
{
done += nb;
file->position += nb;
- // Purge cached data
+ // purge cached data
FS_Purge( file );
}
}
else
{
- if( count > (fs_offset_t)sizeof( file->buff ))
- count = (fs_offset_t)sizeof( file->buff );
+ if( count > (long)sizeof( file->buff ))
+ count = (long)sizeof( file->buff );
lseek( file->handle, file->offset + file->position, SEEK_SET );
nb = read( file->handle, file->buff, count );
@@ -2121,7 +2103,7 @@ fs_offset_t FS_Read( file_t *file, void *buffer, size_t buffersize )
file->position += nb;
// copy the requested data in "buffer" (as much as we can)
- count = (fs_offset_t)buffersize > file->buff_len ? file->buff_len : (fs_offset_t)buffersize;
+ count = (long)buffersize > file->buff_len ? file->buff_len : (long)buffersize;
memcpy( &((byte *)buffer)[done], file->buff, count );
file->buff_ind = count;
done += count;
@@ -2171,9 +2153,9 @@ Print a string into a file
*/
int FS_VPrintf( file_t *file, const char* format, va_list ap )
{
- int len;
- fs_offset_t buff_size = MAX_SYSPATH;
- char *tempbuff;
+ int len;
+ long buff_size = MAX_SYSPATH;
+ char *tempbuff;
if( !file ) return 0;
@@ -2181,7 +2163,10 @@ int FS_VPrintf( file_t *file, const char* format, va_list ap )
{
tempbuff = (char *)Mem_Alloc( fs_mempool, buff_size );
len = Q_vsprintf( tempbuff, format, ap );
- if( len >= 0 && len < buff_size ) break;
+
+ if( len >= 0 && len < buff_size )
+ break;
+
Mem_Free( tempbuff );
buff_size *= 2;
}
@@ -2251,7 +2236,8 @@ int FS_Gets( file_t *file, byte *string, size_t bufsize )
if( c == '\r' )
{
c = FS_Getc( file );
- if( c != '\n' ) FS_UnGetc( file, (byte)c );
+ if( c != '\n' )
+ FS_UnGetc( file, (byte)c );
}
return c;
@@ -2264,7 +2250,7 @@ FS_Seek
Move the position index in a file
====================
*/
-int FS_Seek( file_t *file, fs_offset_t offset, int whence )
+int FS_Seek( file_t *file, long offset, int whence )
{
// compute the file offset
switch( whence )
@@ -2281,7 +2267,7 @@ int FS_Seek( file_t *file, fs_offset_t offset, int whence )
return -1;
}
- if( offset < 0 || offset > (long)file->real_length )
+ if( offset < 0 || offset > file->real_length )
return -1;
// if we have the data in our read buffer, we don't need to actually seek
@@ -2308,7 +2294,7 @@ FS_Tell
Give the current position in a file
====================
*/
-fs_offset_t FS_Tell( file_t* file )
+long FS_Tell( file_t* file )
{
if( !file ) return 0;
return file->position - file->buff_len + file->buff_ind;
@@ -2349,11 +2335,11 @@ Filename are relative to the xash directory.
Always appends a 0 byte.
============
*/
-byte *FS_LoadFile( const char *path, fs_offset_t *filesizeptr, qboolean gamedironly )
+byte *FS_LoadFile( const char *path, long *filesizeptr, qboolean gamedironly )
{
- file_t *file;
- byte *buf = NULL;
- fs_offset_t filesize = 0;
+ file_t *file;
+ byte *buf = NULL;
+ long filesize = 0;
file = FS_Open( path, "rb", gamedironly );
@@ -2383,7 +2369,7 @@ FS_OpenFile
Simply version of FS_Open
============
*/
-file_t *FS_OpenFile( const char *path, fs_offset_t *filesizeptr, qboolean gamedironly )
+file_t *FS_OpenFile( const char *path, long *filesizeptr, qboolean gamedironly )
{
file_t *file = FS_Open( path, "rb", gamedironly );
@@ -2402,7 +2388,7 @@ FS_WriteFile
The filename will be prefixed by the current game directory
============
*/
-qboolean FS_WriteFile( const char *filename, const void *data, fs_offset_t len )
+qboolean FS_WriteFile( const char *filename, const void *data, long len )
{
file_t *file;
@@ -2414,8 +2400,9 @@ qboolean FS_WriteFile( const char *filename, const void *data, fs_offset_t len )
return false;
}
- FS_Write (file, data, len);
- FS_Close (file);
+ FS_Write( file, data, len );
+ FS_Close( file );
+
return true;
}
@@ -2493,18 +2480,16 @@ const char *FS_GetDiskPath( const char *name, qboolean gamedironly )
{
int index;
searchpath_t *search;
-
+
search = FS_FindFile( name, &index, gamedironly );
if( search )
{
- if( index != -1 )
- {
- // file in pack or wad
+ if( index != -1 ) // file in pack or wad
return NULL;
- }
return va( "%s%s", search->filename, name );
}
+
return NULL;
}
@@ -2611,7 +2596,7 @@ FS_FileSize
return size of file in bytes
==================
*/
-fs_offset_t FS_FileSize( const char *filename, qboolean gamedironly )
+long FS_FileSize( const char *filename, qboolean gamedironly )
{
file_t *fp;
int length = 0;
@@ -2635,7 +2620,7 @@ FS_FileLength
return size of file in bytes
==================
*/
-fs_offset_t FS_FileLength( file_t *f )
+long FS_FileLength( file_t *f )
{
if( !f ) return 0;
return f->real_length;
@@ -2648,7 +2633,7 @@ FS_FileTime
return time of creation file in seconds
==================
*/
-fs_offset_t FS_FileTime( const char *filename, qboolean gamedironly )
+long FS_FileTime( const char *filename, qboolean gamedironly )
{
searchpath_t *search;
int pack_ind;
@@ -2725,22 +2710,32 @@ FS_FileCopy
==================
*/
-void FS_FileCopy( file_t *pOutput, file_t *pInput, int fileSize )
+qboolean FS_FileCopy( file_t *pOutput, file_t *pInput, int fileSize )
{
- char buf[MAX_SYSPATH]; // A small buffer for the copy
- int size;
+ char *buf = Mem_Alloc( fs_mempool, FILE_COPY_SIZE );
+ int size, readSize;
+ qboolean done = true;
while( fileSize > 0 )
{
- if( fileSize > MAX_SYSPATH )
- size = MAX_SYSPATH;
+ if( fileSize > FILE_COPY_SIZE )
+ size = FILE_COPY_SIZE;
else size = fileSize;
- FS_Read( pInput, buf, size );
- FS_Write( pOutput, buf, size );
-
+ if(( readSize = FS_Read( pInput, buf, size )) < size )
+ {
+ MsgDev( D_ERROR, "FS_FileCopy: unexpected end of input file\n" );
+ fileSize = 0;
+ done = false;
+ break;
+ }
+
+ FS_Write( pOutput, buf, readSize );
fileSize -= size;
}
+
+ Mem_Free( buf );
+ return done;
}
/*
@@ -2787,7 +2782,7 @@ search_t *FS_Search( const char *pattern, int caseinsensitive, int gamedironly )
// search through the path, one element at a time
for( searchpath = fs_searchpaths; searchpath; searchpath = searchpath->next )
{
- if( gamedironly && !( searchpath->flags & FS_GAMEDIR_PATH ))
+ if( gamedironly && !FBitSet( searchpath->flags, FS_GAMEDIR_PATH ))
continue;
// is the element a pak file?
@@ -2809,9 +2804,7 @@ search_t *FS_Search( const char *pattern, int caseinsensitive, int gamedironly )
}
if( resultlistindex == resultlist.numstrings )
- {
stringlistappend( &resultlist, temp );
- }
}
// strip off one path element at a time until empty
// this way directories are added to the listing if they match the pattern
@@ -2866,7 +2859,9 @@ search_t *FS_Search( const char *pattern, int caseinsensitive, int gamedironly )
if( type != TYP_ANY && wad->lumps[i].type != type )
continue;
- Q_strncpy( temp, wad->lumps[i].name, sizeof( temp ));
+ // build the lumpname with image suffix (if present)
+ Q_snprintf( temp, sizeof( temp ), "%s%s", wad->lumps[i].name, wad_hints[wad->lumps[i].img_type].ext );
+
while( temp[0] )
{
if( matchpattern( temp, wadpattern, true ))
@@ -2920,11 +2915,10 @@ search_t *FS_Search( const char *pattern, int caseinsensitive, int gamedironly )
}
if( resultlistindex == resultlist.numstrings )
- {
stringlistappend( &resultlist, temp );
- }
}
}
+
stringlistfreecontents( &dirlist );
}
}
@@ -3006,6 +3000,13 @@ static const wadtype_t wad_hints[10] =
{ NULL, 0 } // terminator
};
+/*
+===========
+W_TypeFromExt
+
+Extracts file type from extension
+===========
+*/
static char W_TypeFromExt( const char *lumpname )
{
const char *ext = FS_FileExtension( lumpname );
@@ -3057,11 +3058,17 @@ char W_HintFromSuf( const char *lumpname )
{
char barename[64];
char suffix[8];
+ size_t namelen;
const wadtype_t *hint;
// trying to extract hint from the name
FS_FileBase( lumpname, barename );
- Q_strncpy( suffix, barename + Q_strlen( barename ) - HINT_NAMELEN, sizeof( suffix ));
+ namelen = Q_strlen( barename );
+
+ if( namelen <= HINT_NAMELEN )
+ return IMG_DIFFUSE;
+
+ Q_strncpy( suffix, barename + namelen - HINT_NAMELEN, sizeof( suffix ));
// we not known about filetype, so match only by filename
for( hint = wad_hints; hint->ext; hint++ )
@@ -3074,11 +3081,19 @@ char W_HintFromSuf( const char *lumpname )
return IMG_DIFFUSE;
}
+/*
+===========
+W_FindLump
+
+Serach for already existed lump
+===========
+*/
static dlumpinfo_t *W_FindLump( wfile_t *wad, const char *name, const char matchtype )
{
char img_type = IMG_DIFFUSE;
char barename[64], suffix[8];
int left, right;
+ size_t namelen;
const wadtype_t *hint;
if( !wad || !wad->lumps || matchtype == TYP_NONE )
@@ -3086,20 +3101,25 @@ static dlumpinfo_t *W_FindLump( wfile_t *wad, const char *name, const char match
// trying to extract hint from the name
FS_FileBase( name, barename );
- Q_strncpy( suffix, barename + Q_strlen( barename ) - HINT_NAMELEN, sizeof( suffix ));
+ namelen = Q_strlen( barename );
- // we not known about filetype, so match only by filename
- for( hint = wad_hints; hint->ext; hint++ )
+ if( namelen > HINT_NAMELEN )
{
- if( !Q_stricmp( suffix, hint->ext ))
+ Q_strncpy( suffix, barename + namelen - HINT_NAMELEN, sizeof( suffix ));
+
+ // we not known about filetype, so match only by filename
+ for( hint = wad_hints; hint->ext; hint++ )
{
- img_type = hint->type;
- break;
+ if( !Q_stricmp( suffix, hint->ext ))
+ {
+ img_type = hint->type;
+ break;
+ }
}
- }
- if( img_type != IMG_DIFFUSE )
- barename[Q_strlen( barename ) - HINT_NAMELEN] = '\0'; // kill the suffix
+ if( img_type != IMG_DIFFUSE )
+ barename[namelen - HINT_NAMELEN] = '\0'; // kill the suffix
+ }
// look for the file (binary search)
left = 0;
@@ -3135,9 +3155,10 @@ static dlumpinfo_t *W_FindLump( wfile_t *wad, const char *name, const char match
/*
====================
-FS_AddFileToWad
+W_AddFileToWad
Add a file to the list of files contained into a package
+and sort LAT in alpha-bethical order
====================
*/
static dlumpinfo_t *W_AddFileToWad( const char *name, wfile_t *wad, dlumpinfo_t *newlump )
@@ -3145,14 +3166,6 @@ static dlumpinfo_t *W_AddFileToWad( const char *name, wfile_t *wad, dlumpinfo_t
int left, right;
dlumpinfo_t *plump;
- // convert all qmip types to miptex
- if( newlump->type == TYP_RAWDATA )
- newlump->type = TYP_MIPTEX;
-
- // check for Quake 'conchars' issues (only lmp loader supposed to read this lame pic)
- if( !Q_stricmp( newlump->name, "conchars" ) && newlump->type == TYP_RAWDATA )
- newlump->type = TYP_GFXPIC;
-
// look for the slot we should put that file into (binary search)
left = 0;
right = wad->numlumps - 1;
@@ -3172,7 +3185,7 @@ static dlumpinfo_t *W_AddFileToWad( const char *name, wfile_t *wad, dlumpinfo_t
diff = 1;
else if( wad->lumps[middle].type > newlump->type )
diff = -1;
- else MsgDev( D_NOTE, "Wad %s contains the file %s several times\n", wad->filename, name );
+ else MsgDev( D_WARN, "Wad %s contains the file %s several times\n", wad->filename, name );
}
// If we're too far in the list
@@ -3191,53 +3204,17 @@ static dlumpinfo_t *W_AddFileToWad( const char *name, wfile_t *wad, dlumpinfo_t
return plump;
}
-static qboolean W_ReadLumpTable( wfile_t *wad )
-{
- size_t lat_size;
- dlumpinfo_t *srclumps;
- int i, k, numlumps;
-
- // nothing to convert ?
- if( !wad || !wad->numlumps )
- return false;
-
- lat_size = wad->numlumps * sizeof( dlumpinfo_t );
- srclumps = (dlumpinfo_t *)Mem_Alloc( wad->mempool, lat_size );
- numlumps = wad->numlumps;
- wad->numlumps = 0; // reset it
-
- if( read( wad->handle, srclumps, lat_size ) != lat_size )
- {
- MsgDev( D_ERROR, "W_ReadLumpTable: %s has corrupted lump allocation table\n", wad->filename );
- W_Close( wad );
- return false;
- }
-
- // swap everything
- for( i = 0; i < numlumps; i++ )
- {
- char name[16];
-
- // cleanup lumpname
- Q_strnlwr( srclumps[i].name, name, sizeof( srclumps[i].name ));
-
- // check for '*' symbol issues
- k = Q_strlen( Q_strrchr( name, '*' ));
- if( k ) name[Q_strlen( name ) - k] = '!'; // quake1 issues (can't save images that contain '*' symbol)
-
- W_AddFileToWad( name, wad, &srclumps[i] );
- }
-
- // release source lumps
- Mem_Free( srclumps );
-
- return true;
-}
+/*
+===========
+W_ReadLump
-byte *W_ReadLump( wfile_t *wad, dlumpinfo_t *lump, size_t *lumpsizeptr )
+reading lump into temp buffer
+===========
+*/
+byte *W_ReadLump( wfile_t *wad, dlumpinfo_t *lump, long *lumpsizeptr )
{
+ size_t oldpos, size = 0;
byte *buf;
- size_t size = 0;
// assume error
if( lumpsizeptr ) *lumpsizeptr = 0;
@@ -3245,9 +3222,12 @@ byte *W_ReadLump( wfile_t *wad, dlumpinfo_t *lump, size_t *lumpsizeptr )
// no wads loaded
if( !wad || !lump ) return NULL;
+ oldpos = tell( wad->handle ); // don't forget restore original position
+
if( lseek( wad->handle, lump->filepos, SEEK_SET ) == -1 )
{
MsgDev( D_ERROR, "W_ReadLump: %s is corrupted\n", lump->name );
+ lseek( wad->handle, oldpos, SEEK_SET );
return NULL;
}
@@ -3256,47 +3236,129 @@ byte *W_ReadLump( wfile_t *wad, dlumpinfo_t *lump, size_t *lumpsizeptr )
if( size < lump->disksize )
{
MsgDev( D_WARN, "W_ReadLump: %s is probably corrupted\n", lump->name );
+ lseek( wad->handle, oldpos, SEEK_SET );
Mem_Free( buf );
return NULL;
}
+
if( lumpsizeptr ) *lumpsizeptr = lump->size;
+ lseek( wad->handle, oldpos, SEEK_SET );
+
return buf;
}
/*
+===========
+W_WriteLump
+
+compress and write lump
+===========
+*/
+qboolean W_WriteLump( wfile_t *wad, dlumpinfo_t *lump, const void *data, size_t datasize )
+{
+ if( !wad || !lump ) return false;
+
+ if( !data || !datasize )
+ {
+ MsgDev( D_WARN, "W_WriteLump: ignore blank lump %s - nothing to save\n", lump->name );
+ return false;
+ }
+
+ if( wad->mode == O_RDONLY )
+ {
+ MsgDev( D_ERROR, "W_WriteLump: %s opened in readonly mode\n", wad->filename );
+ return false;
+ }
+
+ lump->size = lump->disksize = datasize;
+
+ if( write( wad->handle, data, datasize ) == datasize )
+ return true;
+ return false;
+}
+
+/*
=============================================================================
WADSYSTEM PUBLIC BASE FUNCTIONS
=============================================================================
*/
-wfile_t *W_Open( const char *filename, const char *mode )
+/*
+===========
+W_Open
+
+open the wad for reading & writing
+===========
+*/
+wfile_t *W_Open( const char *filename, const char *mode, int *error )
{
dwadinfo_t header;
wfile_t *wad = (wfile_t *)Mem_Alloc( fs_mempool, sizeof( wfile_t ));
- const char *comment = "Generated by Xash WadLib. ";
+ const char *comment = "Created by Xash3D Engine.\0";
+ int i, ind, mod, opt, lumpcount;
+ size_t wadsize, lat_size;
+ dlumpinfo_t *srclumps;
- if( mode[0] == 'a' ) wad->handle = open( filename, O_RDWR|O_BINARY, 0x666 );
- else if( mode[0] == 'w' ) wad->handle = open( filename, O_CREAT|O_TRUNC|O_WRONLY|O_BINARY, 0x666 );
- else if( mode[0] == 'r' ) wad->handle = open( filename, O_RDONLY|O_BINARY, 0x666 );
+ // parse the mode string
+ switch( mode[0] )
+ {
+ case 'r':
+ mod = O_RDONLY;
+ opt = 0;
+ break;
+ case 'w':
+ mod = O_WRONLY;
+ opt = O_CREAT|O_TRUNC;
+ break;
+ case 'a':
+ mod = O_WRONLY;
+ opt = O_CREAT;
+ break;
+ default:
+ MsgDev( D_ERROR, "W_Open(%s, %s): invalid mode\n", filename, mode );
+ return NULL;
+ }
+
+ for( ind = 1; mode[ind] != '\0'; ind++ )
+ {
+ switch( mode[ind] )
+ {
+ case '+':
+ mod = O_RDWR;
+ break;
+ case 'b':
+ opt |= O_BINARY;
+ break;
+ default:
+ MsgDev( D_ERROR, "W_Open: %s: unknown char in mode (%c)\n", filename, mode, mode[ind] );
+ break;
+ }
+ }
+
+ wad->handle = open( filename, mod|opt, 0666 );
if( wad->handle < 0 )
{
- W_Close( wad );
MsgDev( D_ERROR, "W_Open: couldn't open %s\n", filename );
+ if( error ) *error = WAD_LOAD_COULDNT_OPEN;
+ W_Close( wad );
return NULL;
}
// copy wad name
Q_strncpy( wad->filename, filename, sizeof( wad->filename ));
- wad->mempool = Mem_AllocPool( filename );
wad->filetime = FS_SysFileTime( filename );
+ wad->mempool = Mem_AllocPool( filename );
+
+ wadsize = lseek( wad->handle, 0, SEEK_END );
+ lseek( wad->handle, 0, SEEK_SET );
// if the file is opened in "write", "append", or "read/write" mode
- if( mode[0] == 'w' )
+ if( mod == O_WRONLY || !wadsize )
{
- dwadinfo_t hdr;
+ dwadinfo_t hdr;
wad->numlumps = 0; // blank wad
wad->lumps = NULL; //
@@ -3310,95 +3372,122 @@ wfile_t *W_Open( const char *filename, const char *mode )
write( wad->handle, comment, Q_strlen( comment ) + 1 );
wad->infotableofs = tell( wad->handle );
}
- else if( mode[0] == 'r' || mode[0] == 'a' )
+ else if( mod == O_RDWR || mod == O_RDONLY )
{
- if( mode[0] == 'a' )
- {
- lseek( wad->handle, 0, SEEK_SET );
+ if( mod == O_RDWR )
wad->mode = O_APPEND;
- }
+ else wad->mode = O_RDONLY;
if( read( wad->handle, &header, sizeof( dwadinfo_t )) != sizeof( dwadinfo_t ))
{
MsgDev( D_ERROR, "W_Open: %s can't read header\n", filename );
+ if( error ) *error = WAD_LOAD_BAD_HEADER;
W_Close( wad );
return NULL;
}
- switch( header.ident )
+ if( header.ident != IDWAD3HEADER )
{
- case IDWAD2HEADER:
- case IDWAD3HEADER:
- break; // WAD2, WAD3 allow r\w mode
- default:
- MsgDev( D_ERROR, "W_Open: %s unknown wadtype\n", filename );
+ MsgDev( D_ERROR, "W_Open: %s is not a WAD3 file\n", filename );
+ if( error ) *error = WAD_LOAD_BAD_HEADER;
W_Close( wad );
return NULL;
}
- wad->numlumps = header.numlumps;
- if( wad->numlumps >= MAX_FILES_IN_WAD && wad->mode == O_APPEND )
+ lumpcount = header.numlumps;
+
+ if( lumpcount >= MAX_FILES_IN_WAD && wad->mode == O_APPEND )
{
- MsgDev( D_WARN, "W_Open: %s is full (%i lumps)\n", wad->numlumps );
+ MsgDev( D_WARN, "W_Open: %s is full (%i lumps)\n", filename, lumpcount );
+ if( error ) *error = WAD_LOAD_TOO_MANY_FILES;
wad->mode = O_RDONLY; // set read-only mode
}
+ else if( lumpcount <= 0 && wad->mode == O_RDONLY )
+ {
+ MsgDev( D_ERROR, "W_Open: %s has no lumps\n", filename );
+ if( error ) *error = WAD_LOAD_NO_FILES;
+ W_Close( wad );
+ return NULL;
+ }
+ else if( error ) *error = WAD_LOAD_OK;
+
wad->infotableofs = header.infotableofs; // save infotableofs position
+
if( lseek( wad->handle, wad->infotableofs, SEEK_SET ) == -1 )
{
MsgDev( D_ERROR, "W_Open: %s can't find lump allocation table\n", filename );
+ if( error ) *error = WAD_LOAD_BAD_FOLDERS;
W_Close( wad );
return NULL;
}
+ lat_size = lumpcount * sizeof( dlumpinfo_t );
+
// NOTE: lumps table can be reallocated for O_APPEND mode
- wad->lumps = Mem_Alloc( wad->mempool, wad->numlumps * sizeof( dlumpinfo_t ));
+ srclumps = (dlumpinfo_t *)Mem_Alloc( wad->mempool, lat_size );
- if( wad->mode == O_APPEND )
- {
- size_t lat_size = wad->numlumps * sizeof( dlumpinfo_t );
+ if( read( wad->handle, srclumps, lat_size ) != lat_size )
+ {
+ MsgDev( D_ERROR, "W_ReadLumpTable: %s has corrupted lump allocation table\n", wad->filename );
+ if( error ) *error = WAD_LOAD_CORRUPTED;
+ Mem_Free( srclumps );
+ W_Close( wad );
+ return NULL;
+ }
- if( read( wad->handle, wad->lumps, lat_size ) != lat_size )
- {
- MsgDev( D_ERROR, "W_ReadLumpTable: %s has corrupted lump allocation table\n", wad->filename );
- W_Close( wad );
- return NULL;
- }
+ // starting to add lumps
+ wad->lumps = (dlumpinfo_t *)Mem_Alloc( wad->mempool, lat_size );
+ wad->numlumps = 0;
- // if we are in append mode - we need started from infotableofs poisition
- // overwrite lumptable as well, we have her copy in wad->lumps
- lseek( wad->handle, wad->infotableofs, SEEK_SET );
- }
- else
+ // sort lumps for binary search
+ for( i = 0; i < lumpcount; i++ )
{
- // setup lump allocation table
- switch( header.ident )
- {
- case IDWAD2HEADER:
- case IDWAD3HEADER:
- if(!W_ReadLumpTable( wad ))
- return NULL;
- break;
- }
+ char name[16];
+ int k;
+
+ // cleanup lumpname
+ Q_strnlwr( srclumps[i].name, name, sizeof( srclumps[i].name ));
+
+ // check for '*' symbol issues (quake1)
+ k = Q_strlen( Q_strrchr( name, '*' ));
+ if( k ) name[Q_strlen( name ) - k] = '!';
+
+ W_AddFileToWad( name, wad, &srclumps[i] );
}
+
+ // release source lumps
+ Mem_Free( srclumps );
+
+ // if we are in append mode - we need started from infotableofs poisition
+ // overwrite lumptable as well, we have her copy in wad->lumps
+ if( wad->mode == O_APPEND )
+ lseek( wad->handle, wad->infotableofs, SEEK_SET );
}
- // and leaves the file open
+
+ // and leave the file open
return wad;
}
+/*
+===========
+W_Close
+
+finalize wad or just close
+===========
+*/
void W_Close( wfile_t *wad )
{
- fs_offset_t ofs;
-
if( !wad ) return;
if( wad->handle >= 0 && ( wad->mode == O_APPEND || wad->mode == O_WRONLY ))
{
dwadinfo_t hdr;
+ long ofs;
// write the lumpinfo
ofs = tell( wad->handle );
write( wad->handle, wad->lumps, wad->numlumps * sizeof( dlumpinfo_t ));
-
+
// write the header
hdr.ident = IDWAD3HEADER;
hdr.numlumps = wad->numlumps;
@@ -3409,7 +3498,9 @@ void W_Close( wfile_t *wad )
}
Mem_FreePool( &wad->mempool );
- if( wad->handle >= 0 ) close( wad->handle );
+ if( wad->handle >= 0 )
+ close( wad->handle );
+
Mem_Free( wad ); // free himself
}
@@ -3420,7 +3511,127 @@ FILESYSTEM IMPLEMENTATION
=============================================================================
*/
-static byte *W_LoadFile( const char *path, fs_offset_t *lumpsizeptr, qboolean gamedironly )
+/*
+===========
+W_SaveLump
+
+write new or replace existed lump
+===========
+*/
+size_t W_SaveFile( wfile_t *wad, const char *lump, const void *data, size_t datasize, char type, qboolean replace )
+{
+ dlumpinfo_t *find, newlump;
+ size_t lat_size, oldpos;
+ char hint, lumpname[64];
+
+ if( !wad || !lump ) return -1;
+
+ if( !data || !datasize )
+ {
+ MsgDev( D_WARN, "W_SaveLump: ignore blank lump %s - nothing to save\n", lump );
+ return -1;
+ }
+
+ if( wad->mode == O_RDONLY )
+ {
+ MsgDev( D_ERROR, "W_SaveLump: %s opened in readonly mode\n", wad->filename );
+ return -1;
+ }
+
+ if( wad->numlumps >= MAX_FILES_IN_WAD )
+ {
+ MsgDev( D_ERROR, "W_SaveLump: %s is full\n", wad->filename );
+ return -1;
+ }
+
+ find = W_FindLump( wad, lump, type );
+
+ if( find != NULL && replace )
+ {
+ if( FBitSet( find->attribs, ATTR_READONLY ))
+ {
+ // g-cont. i left this limitation as a protect of the replacement of compressed lumps
+ MsgDev( D_ERROR, "W_ReplaceLump: %s is read-only\n", find->name );
+ return -1;
+ }
+
+ if( datasize != find->size )
+ {
+ MsgDev( D_ERROR, "W_ReplaceLump: %s [%s] should be [%s]\n",
+ lumpname, Q_memprint( datasize ), Q_memprint( find->size ));
+ return -1;
+ }
+
+ oldpos = tell( wad->handle ); // don't forget restore original position
+
+ if( lseek( wad->handle, find->filepos, SEEK_SET ) == -1 )
+ {
+ MsgDev( D_ERROR, "W_ReplaceLump: %s is corrupted\n", find->name );
+ lseek( wad->handle, oldpos, SEEK_SET );
+ return -1;
+ }
+
+ if( write( wad->handle, data, datasize ) != find->disksize )
+ MsgDev( D_WARN, "W_ReplaceLump: %s probably replaced with errors\n", find->name );
+
+ // restore old position
+ lseek( wad->handle, oldpos, SEEK_SET );
+
+ return wad->numlumps;
+ }
+ else
+ {
+ MsgDev( D_ERROR, "W_SaveLump: %s already exist\n", lump );
+ return -1;
+ }
+
+ // prepare lump name
+ Q_strncpy( lumpname, lump, sizeof( lumpname ));
+
+ // extract image hint
+ hint = W_HintFromSuf( lumpname );
+
+ if( hint != IMG_DIFFUSE )
+ lumpname[Q_strlen( lumpname ) - HINT_NAMELEN] = '\0'; // kill the suffix
+
+ if( Q_strlen( lumpname ) >= WAD3_NAMELEN )
+ {
+ // name is too long
+ MsgDev( D_ERROR, "W_SaveLump: %s more than %i symbols\n", lumpname, WAD3_NAMELEN );
+ return -1;
+ }
+
+ lat_size = sizeof( dlumpinfo_t ) * (wad->numlumps + 1);
+
+ // reallocate lumptable
+ wad->lumps = (dlumpinfo_t *)Mem_Realloc( wad->mempool, wad->lumps, lat_size );
+
+ memset( &newlump, 0, sizeof( newlump ));
+
+ // write header
+ Q_strnupr( lumpname, newlump.name, WAD3_NAMELEN );
+ newlump.filepos = tell( wad->handle );
+ newlump.attribs = ATTR_NONE;
+ newlump.img_type = hint;
+ newlump.type = type;
+
+ if( !W_WriteLump( wad, &newlump, data, datasize ))
+ return -1;
+
+ // record entry and re-sort table
+ W_AddFileToWad( lumpname, wad, &newlump );
+
+ return wad->numlumps;
+}
+
+/*
+===========
+W_LoadFile
+
+loading lump into the tmp buffer
+===========
+*/
+static byte *W_LoadFile( const char *path, long *lumpsizeptr, qboolean gamedironly )
{
searchpath_t *search;
int index;
diff --git b/engine/common/filesystem.h a/engine/common/filesystem.h
index a5bf2cc..9f5cbeb 100644
--- b/engine/common/filesystem.h
+++ a/engine/common/filesystem.h
@@ -58,9 +58,6 @@ file_n: byte[dwadinfo_t[num]->disksize]
infotable dlumpinfo_t[dwadinfo_t->numlumps]
========================================================================
*/
-#define IDWAD2HEADER (('2'<<24)+('D'<<16)+('A'<<8)+'W') // little-endian "WAD2" quake1 gfx.wad
-#define IDWAD3HEADER (('3'<<24)+('D'<<16)+('A'<<8)+'W') // little-endian "WAD3" half-life wads
-
#define WAD3_NAMELEN 16
#define HINT_NAMELEN 5 // e.g. _mask, _norm
#define MAX_FILES_IN_WAD 65535 // real limit as above <2Gb size not a lumpcount
@@ -73,9 +70,9 @@ infotable dlumpinfo_t[dwadinfo_t->numlumps]
typedef struct
{
- int ident; // should be IWAD, WAD2 or WAD3
+ int ident; // should be WAD3
int numlumps; // num files
- int infotableofs;
+ int infotableofs; // LUT offset
} dwadinfo_t;
typedef struct
diff --git b/engine/common/host.c a/engine/common/host.c
index 84b5468..cb32669 100644
--- b/engine/common/host.c
+++ a/engine/common/host.c
@@ -39,8 +39,6 @@ convar_t *host_framerate;
convar_t *con_gamemaps;
convar_t *build, *ver;
-static int num_decals;
-
// these cvars will be duplicated on each client across network
int Host_ServerState( void )
{
@@ -74,23 +72,26 @@ Host_PrintEngineFeatures
*/
void Host_PrintEngineFeatures( void )
{
- if( host.features & ENGINE_WRITE_LARGE_COORD )
- MsgDev( D_AICONSOLE, "^3EXT:^7 big world support enabled\n" );
+ if( FBitSet( host.features, ENGINE_WRITE_LARGE_COORD ))
+ MsgDev( D_REPORT, "^3EXT:^7 big world support enabled\n" );
+
+ if( FBitSet( host.features, ENGINE_BUILD_SURFMESHES ))
+ MsgDev( D_REPORT, "^3EXT:^7 surfmeshes enabled\n" );
- if( host.features & ENGINE_BUILD_SURFMESHES )
- MsgDev( D_AICONSOLE, "^3EXT:^7 surfmeshes enabled\n" );
+ if( FBitSet( host.features, ENGINE_LOAD_DELUXEDATA ))
+ MsgDev( D_REPORT, "^3EXT:^7 deluxemap support enabled\n" );
- if( host.features & ENGINE_LOAD_DELUXEDATA )
- MsgDev( D_AICONSOLE, "^3EXT:^7 deluxemap support enabled\n" );
+ if( FBitSet( host.features, ENGINE_TRANSFORM_TRACE_AABB ))
+ MsgDev( D_REPORT, "^3EXT:^7 Transform trace AABB enabled\n" );
- if( host.features & ENGINE_TRANSFORM_TRACE_AABB )
- MsgDev( D_AICONSOLE, "^3EXT:^7 Transform trace AABB enabled\n" );
+ if( FBitSet( host.features, ENGINE_LARGE_LIGHTMAPS ))
+ MsgDev( D_REPORT, "^3EXT:^7 Large lightmaps enabled\n" );
- if( host.features & ENGINE_LARGE_LIGHTMAPS )
- MsgDev( D_AICONSOLE, "^3EXT:^7 Large lightmaps enabled\n" );
+ if( FBitSet( host.features, ENGINE_COMPENSATE_QUAKE_BUG ))
+ MsgDev( D_REPORT, "^3EXT:^7 Compensate quake bug enabled\n" );
- if( host.features & ENGINE_COMPENSATE_QUAKE_BUG )
- MsgDev( D_AICONSOLE, "^3EXT:^7 Compensate quake bug enabled\n" );
+ if( FBitSet( host.features, ENGINE_FIXED_FRAMERATE ))
+ MsgDev( D_REPORT, "^3EXT:^7 fixed main cycle\n" );
}
/*
@@ -118,7 +119,7 @@ void Host_EndGame( const char *message, ... )
static char string[MAX_SYSPATH];
va_start( argptr, message );
- Q_vsprintf( string, message, argptr );
+ Q_vsnprintf( string, sizeof( string ), message, argptr );
va_end( argptr );
MsgDev( D_INFO, "Host_EndGame: %s\n", string );
@@ -233,10 +234,10 @@ void Host_Exec_f( void )
return;
}
- // HACKHACK: don't execute listenserver.cfg in singleplayer
- if( !Q_stricmp( Cvar_VariableString( "lservercfgfile" ), Cmd_Argv( 1 )))
+ // don't execute listenserver.cfg in singleplayer
+ if( !Q_stricmp( Cvar_VariableString( "lservercfgfile" ), Cmd_Argv( 1 )))
{
- if( Cvar_VariableValue( "maxplayers" ) == 1.0f )
+ if( Cvar_VariableInteger( "maxplayers" ) == 1 )
return;
}
@@ -309,12 +310,12 @@ qboolean Host_IsLocalClient( void )
Host_RegisterDecal
=================
*/
-qboolean Host_RegisterDecal( const char *name )
+qboolean Host_RegisterDecal( const char *name, int *count )
{
char shortname[CS_SIZE];
int i;
- if( !name || !name[0] )
+ if( !name || !*name )
return 0;
FS_FileBase( name, shortname );
@@ -333,7 +334,7 @@ qboolean Host_RegisterDecal( const char *name )
// register new decal
Q_strncpy( host.draw_decals[i], shortname, sizeof( host.draw_decals[i] ));
- num_decals++;
+ *count += 1;
return true;
}
@@ -346,17 +347,16 @@ Host_InitDecals
void Host_InitDecals( void )
{
search_t *t;
- int i;
+ int i, num_decals = 0;
memset( host.draw_decals, 0, sizeof( host.draw_decals ));
- num_decals = 0;
// lookup all decals in decals.wad
t = FS_Search( "decals.wad/*.*", true, false );
for( i = 0; t && i < t->numfilenames; i++ )
{
- if( !Host_RegisterDecal( t->filenames[i] ))
+ if( !Host_RegisterDecal( t->filenames[i], &num_decals ))
break;
}
@@ -373,27 +373,25 @@ Write ambient sounds into demo
*/
void Host_RestartAmbientSounds( void )
{
- soundlist_t soundInfo[64];
+ soundlist_t soundInfo[128];
string curtrack, looptrack;
int i, nSounds;
- fs_offset_t position;
+ long position;
- if( !SV_Active( ))
- {
- return;
- }
+ if( !SV_Active( )) return;
- nSounds = S_GetCurrentStaticSounds( soundInfo, 64 );
+ nSounds = S_GetCurrentStaticSounds( soundInfo, 128 );
for( i = 0; i < nSounds; i++ )
{
- if( !soundInfo[i].looping || soundInfo[i].entnum == -1 )
+ soundlist_t *si = &soundInfo[i];
+
+ if( !si->looping || si->entnum == -1 )
continue;
MsgDev( D_NOTE, "Restarting sound %s...\n", soundInfo[i].name );
- S_StopSound( soundInfo[i].entnum, soundInfo[i].channel, soundInfo[i].name );
- SV_StartSound( pfnPEntityOfEntIndex( soundInfo[i].entnum ), CHAN_STATIC, soundInfo[i].name,
- soundInfo[i].volume, soundInfo[i].attenuation, 0, soundInfo[i].pitch );
+ S_StopSound( si->entnum, si->channel, si->name );
+ SV_StartSound( pfnPEntityOfEntIndex( si->entnum ), CHAN_STATIC, si->name, si->volume, si->attenuation, 0, si->pitch );
}
// restart soundtrack
@@ -418,10 +416,7 @@ void Host_RestartDecals( void )
sizebuf_t *msg;
int i;
- if( !SV_Active( ))
- {
- return;
- }
+ if( !SV_Active( )) return;
// g-cont. add space for studiodecals if present
host.decalList = (decallist_t *)Z_Malloc( sizeof( decallist_t ) * MAX_RENDER_DECALS * 2 );
@@ -465,20 +460,103 @@ void Host_RestartDecals( void )
/*
===================
-Host_GetConsoleCommands
+Host_GetCommands
Add them exactly as if they had been typed at the console
===================
*/
-void Host_GetConsoleCommands( void )
+void Host_GetCommands( void )
{
char *cmd;
- if( host.type == HOST_DEDICATED )
+ if( host.type != HOST_DEDICATED )
+ return;
+
+ cmd = Con_Input();
+ if( cmd ) Cbuf_AddText( cmd );
+}
+
+/*
+===================
+Host_FrameTime
+
+Returns false if the time is too short to run a frame
+===================
+*/
+qboolean Host_FrameTime( float time )
+{
+ static double oldtime;
+ double minframetime;
+ double fps;
+
+ host.realtime += time;
+
+ // limit fps to withing tolerable range
+ fps = bound( HOST_MINFPS, HOST_FPS, HOST_MAXFPS );
+ minframetime = ( 1.0 / fps );
+
+ if(( host.realtime - oldtime ) < minframetime )
+ {
+ // framerate is too high
+ return false;
+ }
+
+ host.frametime = host.realtime - oldtime;
+ host.realframetime = bound( MIN_FRAMETIME, host.frametime, MAX_FRAMETIME );
+ oldtime = host.realtime;
+
+ if( host_framerate->value > 0 && ( Host_IsLocalGame( )))
+ {
+ float fps = host_framerate->value;
+ if( fps > 1 ) fps = 1.0f / fps;
+ host.frametime = fps;
+ }
+ else
+ { // don't allow really long or short frames
+ host.frametime = bound( MIN_FRAMETIME, host.frametime, MAX_FRAMETIME );
+ }
+
+ return true;
+}
+
+/*
+===================
+Host_RenderTime
+
+Returns false if the time is too short to render a frame
+===================
+*/
+qboolean Host_RenderTime( float time )
+{
+ static double oldtime;
+ static double newtime;
+ double fps;
+
+ newtime += time;
+
+ // dedicated's tic_rate regulates server frame rate. Don't apply fps filter here.
+ fps = host_maxfps->value;
+
+ // clamp the fps in multiplayer games
+ if( fps != 0 )
{
- cmd = Con_Input();
- if( cmd ) Cbuf_AddText( cmd );
+ double minframetime;
+
+ // limit fps to withing tolerable range
+ fps = bound( MIN_FPS, fps, MAX_FPS );
+
+ minframetime = 1.0 / fps;
+
+ if(( newtime - oldtime ) < minframetime )
+ {
+ // framerate is too high
+ return false;
+ }
}
+
+ oldtime = newtime;
+
+ return true;
}
/*
@@ -498,6 +576,7 @@ qboolean Host_FilterTime( float time )
// dedicated's tic_rate regulates server frame rate. Don't apply fps filter here.
fps = host_maxfps->value;
+ // clamp the fps in multiplayer games
if( fps != 0 )
{
float minframetime;
@@ -542,18 +621,39 @@ void Host_Frame( float time )
if( setjmp( host.abortframe ))
return;
- Host_InputFrame (); // input frame
+ // new-style game loop
+ if( FBitSet( host.features, ENGINE_FIXED_FRAMERATE ))
+ {
+ // decide the simulation time
+ if( Host_FrameTime( time ))
+ {
+ Host_InputFrame (); // input frame
+ Host_GetCommands(); // dedicated
+ Host_ServerFrame(); // server frame
+ Host_ClientFrame(); // client frame
+ host.framecount++;
+ }
- // decide the simulation time
- if( !Host_FilterTime( time ))
- return;
+ // clamp the renderer time
+ if( !Host_RenderTime( time ))
+ return;
+
+ Host_RenderFrame (); // render frame
+ }
+ else // classic game loop
+ {
+ Host_InputFrame (); // input frame
- Host_GetConsoleCommands ();
+ // decide the simulation time
+ if( !Host_FilterTime( time ))
+ return;
- Host_ServerFrame (); // server frame
- Host_ClientFrame (); // client frame
+ Host_GetCommands ();
+ Host_ServerFrame (); // server frame
+ Host_ClientFrame (); // client frame
- host.framecount++;
+ host.framecount++;
+ }
}
/*
@@ -707,7 +807,7 @@ void Host_InitCommon( const char *progname, qboolean bChangeGame )
GlobalMemoryStatus( &lpBuffer );
if( !GetCurrentDirectory( sizeof( host.rootdir ), host.rootdir ))
- Sys_Error( "couldn't determine current directory" );
+ Sys_Error( "couldn't determine current directory\n" );
if( host.rootdir[Q_strlen( host.rootdir ) - 1] == '/' )
host.rootdir[Q_strlen( host.rootdir ) - 1] = 0;
@@ -718,7 +818,7 @@ void Host_InitCommon( const char *progname, qboolean bChangeGame )
host.state = HOST_INIT; // initialzation started
host.developer = host.old_developer = 0;
- CRT_Init(); // init some CRT functions
+ Memory_Init(); // init memory subsystem
// some commands may turn engine into infinity loop,
// e.g. xash.exe +game xash -game xash
@@ -728,7 +828,9 @@ void Host_InitCommon( const char *progname, qboolean bChangeGame )
host.mempool = Mem_AllocPool( "Zone Engine" );
- if( Sys_CheckParm( "-console" )) host.developer = 1;
+ if( Sys_CheckParm( "-console" ))
+ host.developer = 1;
+
if( Sys_CheckParm( "-dev" ))
{
if( Sys_GetParmFromCmdLine( "-dev", dev_level ))
@@ -943,9 +1045,9 @@ int EXPORT Host_Main( const char *progname, int bChangeGame, pfnChangeGame func
Cmd_RemoveCommand( "setgl" );
// we need to execute it again here
- Cmd_ExecuteString( "exec config.cfg\n", src_command );
- oldtime = Sys_DoubleTime();
- SCR_CheckStartupVids(); // must be last
+ Cmd_ExecuteString( "exec config.cfg\n" );
+ oldtime = Sys_DoubleTime() - 0.1;
+ SCR_CheckStartupVids(); // must be last
// main window message loop
while( !host.crashed )
diff --git b/engine/common/imagelib/img_bmp.c a/engine/common/imagelib/img_bmp.c
index e57f5ae..c7d6f31 100644
--- b/engine/common/imagelib/img_bmp.c
+++ a/engine/common/imagelib/img_bmp.c
@@ -14,6 +14,7 @@ GNU General Public License for more details.
*/
#include "imagelib.h"
+#include "mathlib.h"
/*
=============
@@ -26,6 +27,7 @@ qboolean Image_LoadBMP( const char *name, const byte *buffer, size_t filesize )
byte palette[256][4];
int i, columns, column, rows, row, bpp = 1;
int cbPalBytes = 0, padSize = 0, bps = 0;
+ int reflectivity[3] = { 0, 0, 0 };
qboolean load_qfont = false;
bmp_t bhdr;
@@ -53,7 +55,7 @@ qboolean Image_LoadBMP( const char *name, const byte *buffer, size_t filesize )
if( bhdr.reserved0 != 0 ) return false;
if( bhdr.planes != 1 ) return false;
- if( Q_memcmp( bhdr.id, "BM", 2 ))
+ if( memcmp( bhdr.id, "BM", 2 ))
{
MsgDev( D_ERROR, "Image_LoadBMP: only Windows-style BMP files supported (%s)\n", name );
return false;
@@ -108,7 +110,7 @@ qboolean Image_LoadBMP( const char *name, const byte *buffer, size_t filesize )
else cbPalBytes = bhdr.colors * sizeof( RGBQUAD );
}
- Q_memcpy( palette, buf_p, cbPalBytes );
+ memcpy( palette, buf_p, cbPalBytes );
if( host.overview_loading && bhdr.bitsPerPixel == 8 )
{
@@ -280,13 +282,20 @@ qboolean Image_LoadBMP( const char *name, const byte *buffer, size_t filesize )
Mem_Free( image.rgba );
return false;
}
+
if( !Image_CheckFlag( IL_KEEP_8BIT ) && ( red != green || green != blue ))
image.flags |= IMAGE_HAS_COLOR;
+
+ reflectivity[0] += red;
+ reflectivity[1] += green;
+ reflectivity[2] += blue;
}
buf_p += padSize; // actual only for 4-bit bmps
}
+ VectorDivide( reflectivity, ( image.width * image.height ), image.fogParams );
if( image.palette ) Image_GetPaletteBMP( image.palette );
+ image.depth = 1;
return true;
}
@@ -378,7 +387,7 @@ qboolean Image_SaveBMP( const char *name, rgbdata_t *pix )
if( host.write_to_clipboard )
{
- Q_memcpy( clipbuf + cur_size, &bmih, sizeof( bmih ));
+ memcpy( clipbuf + cur_size, &bmih, sizeof( bmih ));
cur_size += sizeof( bmih );
}
else
@@ -409,7 +418,7 @@ qboolean Image_SaveBMP( const char *name, rgbdata_t *pix )
if( host.write_to_clipboard )
{
- Q_memcpy( clipbuf + cur_size, rgrgbPalette, cbPalBytes );
+ memcpy( clipbuf + cur_size, rgrgbPalette, cbPalBytes );
cur_size += cbPalBytes;
}
else
@@ -450,7 +459,7 @@ qboolean Image_SaveBMP( const char *name, rgbdata_t *pix )
if( host.write_to_clipboard )
{
- Q_memcpy( clipbuf + cur_size, pbBmpBits, cbBmpBits );
+ memcpy( clipbuf + cur_size, pbBmpBits, cbBmpBits );
cur_size += cbBmpBits;
Sys_SetClipboardData( clipbuf, total_size );
Z_Free( clipbuf );
diff --git b/engine/common/imagelib/img_dds.c a/engine/common/imagelib/img_dds.c
index 837ade9..c6f36f8 100644
--- b/engine/common/imagelib/img_dds.c
+++ a/engine/common/imagelib/img_dds.c
@@ -14,6 +14,7 @@ GNU General Public License for more details.
*/
#include "imagelib.h"
+#include "mathlib.h"
qboolean Image_CheckDXT3Alpha( dds_t *hdr, byte *fin )
{
@@ -175,10 +176,10 @@ size_t Image_DXTCalcMipmapSize( dds_t *hdr )
int i, width, height;
// now correct buffer size
- for( i = 0; i < hdr->dwMipMapCount; i++ )
+ for( i = 0; i < Q_max( 1, ( hdr->dwMipMapCount )); i++ )
{
- width = max( 1, ( hdr->dwWidth >> i ));
- height = max( 1, ( hdr->dwHeight >> i ));
+ width = Q_max( 1, ( hdr->dwWidth >> i ));
+ height = Q_max( 1, ( hdr->dwHeight >> i ));
buffsize += Image_DXTGetLinearSize( image.type, width, height, image.depth );
}
@@ -247,7 +248,7 @@ qboolean Image_LoadDDS( const char *name, const byte *buffer, size_t filesize )
return false;
}
- Q_memcpy( &header, buffer, sizeof( dds_t ));
+ memcpy( &header, buffer, sizeof( dds_t ));
if( header.dwIdent != DDSHEADER )
return false; // it's not a dds file, just skip it
@@ -295,27 +296,37 @@ qboolean Image_LoadDDS( const char *name, const byte *buffer, size_t filesize )
switch( image.encode )
{
case DXT_ENCODE_COLOR_YCoCg:
- image.flags |= IMAGE_HAS_COLOR;
+ SetBits( image.flags, IMAGE_HAS_COLOR );
break;
case DXT_ENCODE_NORMAL_AG_ORTHO:
case DXT_ENCODE_NORMAL_AG_STEREO:
case DXT_ENCODE_NORMAL_AG_PARABOLOID:
case DXT_ENCODE_NORMAL_AG_QUARTIC:
case DXT_ENCODE_NORMAL_AG_AZIMUTHAL:
- image.flags |= IMAGE_HAS_COLOR;
+ SetBits( image.flags, IMAGE_HAS_COLOR );
break;
default: // check for real alpha-pixels
if( image.type == PF_DXT3 && Image_CheckDXT3Alpha( &header, fin ))
- image.flags |= IMAGE_HAS_ALPHA;
+ SetBits( image.flags, IMAGE_HAS_ALPHA );
else if( image.type == PF_DXT5 && Image_CheckDXT5Alpha( &header, fin ))
- image.flags |= IMAGE_HAS_ALPHA;
- image.flags |= IMAGE_HAS_COLOR;
+ SetBits( image.flags, IMAGE_HAS_ALPHA );
+ if( !FBitSet( header.dsPixelFormat.dwFlags, DDS_LUMINANCE ))
+ SetBits( image.flags, IMAGE_HAS_COLOR ); // FIXME: analyze colors
break;
}
+ if( header.dwReserved1[1] != 0 )
+ {
+ // store texture reflectivity
+ image.fogParams[0] = ((header.dwReserved1[1] & 0x000000FF) >> 0 );
+ image.fogParams[1] = ((header.dwReserved1[1] & 0x0000FF00) >> 8 );
+ image.fogParams[2] = ((header.dwReserved1[1] & 0x00FF0000) >> 16);
+ image.fogParams[3] = ((header.dwReserved1[1] & 0xFF000000) >> 24);
+ }
+
// dds files will be uncompressed on a render. requires minimal of info for set this
image.rgba = Mem_Alloc( host.imagepool, image.size );
- Q_memcpy( image.rgba, fin, image.size );
+ memcpy( image.rgba, fin, image.size );
image.flags |= IMAGE_DDS_FORMAT;
return true;
diff --git b/engine/common/imagelib/img_main.c a/engine/common/imagelib/img_main.c
index 8ed68f0..0b86257 100644
--- b/engine/common/imagelib/img_main.c
+++ a/engine/common/imagelib/img_main.c
@@ -102,12 +102,12 @@ void Image_Reset( void )
image.source_width = image.source_height = 0;
image.source_type = image.num_mips = 0;
image.num_sides = image.flags = 0;
+ image.encode = DXT_ENCODE_DEFAULT;
image.type = PF_UNKNOWN;
image.fogParams[0] = 0;
image.fogParams[1] = 0;
image.fogParams[2] = 0;
image.fogParams[3] = 0;
- image.encode = 0;
// pointers will be saved with prevoius picture struct
// don't care about it
@@ -203,7 +203,7 @@ qboolean FS_AddSideToPack( const char *name, int adjust_flags )
if( resampled ) image.rgba = Image_Copy( image.size );
image.cubemap = Mem_Realloc( host.imagepool, image.cubemap, image.ptr + image.size );
- Q_memcpy( image.cubemap + image.ptr, image.rgba, image.size ); // add new side
+ memcpy( image.cubemap + image.ptr, image.rgba, image.size ); // add new side
Mem_Free( image.rgba ); // release source buffer
image.ptr += image.size; // move to next
@@ -494,13 +494,13 @@ rgbdata_t *FS_CopyImage( rgbdata_t *in )
if( palSize )
{
out->palette = Mem_Alloc( host.imagepool, palSize );
- Q_memcpy( out->palette, in->palette, palSize );
+ memcpy( out->palette, in->palette, palSize );
}
if( in->size )
{
out->buffer = Mem_Alloc( host.imagepool, in->size );
- Q_memcpy( out->buffer, in->buffer, in->size );
+ memcpy( out->buffer, in->buffer, in->size );
}
return out;
diff --git b/engine/common/imagelib/img_quant.c a/engine/common/imagelib/img_quant.c
index 4ea8250..2ad8f7d 100644
--- b/engine/common/imagelib/img_quant.c
+++ a/engine/common/imagelib/img_quant.c
@@ -375,31 +375,25 @@ void learn( void )
if( rad <= 1 ) rad = 0;
for( i = 0; i < rad; i++ )
- {
- radpower[i] = alpha * (((rad * rad - i * i) * radbias) / (rad * rad));
- }
+ radpower[i] = alpha * ((( rad * rad - i * i ) * radbias ) / ( rad * rad ));
+
+ if( delta <= 0 ) return;
if(( lengthcount % prime1 ) != 0 )
{
- step = image.bpp * prime1;
+ step = prime1 * image.bpp;
+ }
+ else if(( lengthcount % prime2 ) != 0 )
+ {
+ step = prime2 * image.bpp;
+ }
+ else if(( lengthcount % prime3 ) != 0 )
+ {
+ step = prime3 * image.bpp;
}
else
{
- if(( lengthcount % prime2 ) != 0 )
- {
- step = image.bpp * prime2;
- }
- else
- {
- if(( lengthcount % prime3 ) != 0 )
- {
- step = image.bpp * prime3;
- }
- else
- {
- step = image.bpp * prime4;
- }
- }
+ step = prime4 * image.bpp;
}
i = 0;
@@ -427,7 +421,7 @@ void learn( void )
if( rad <= 1 ) rad = 0;
for( j = 0; j < rad; j++ )
- radpower[j] = alpha * (((rad * rad - j * j) * radbias) / (rad * rad));
+ radpower[j] = alpha * ((( rad * rad - j * j ) * radbias ) / ( rad * rad ));
}
}
}
@@ -470,7 +464,7 @@ rgbdata_t *Image_Quantize( rgbdata_t *pic )
}
pic->buffer = Mem_Realloc( host.imagepool, pic->buffer, image.size );
- Q_memcpy( pic->buffer, image.tempbuffer, image.size );
+ memcpy( pic->buffer, image.tempbuffer, image.size );
pic->type = PF_INDEXED_24;
pic->size = image.size;
diff --git b/engine/common/imagelib/img_tga.c a/engine/common/imagelib/img_tga.c
index 8c771b1..9fa019d 100644
--- b/engine/common/imagelib/img_tga.c
+++ a/engine/common/imagelib/img_tga.c
@@ -14,6 +14,7 @@ GNU General Public License for more details.
*/
#include "imagelib.h"
+#include "mathlib.h"
/*
=============
@@ -26,6 +27,7 @@ qboolean Image_LoadTGA( const char *name, const byte *buffer, size_t filesize )
byte *buf_p, *pixbuf, *targa_rgba;
byte palette[256][4], red = 0, green = 0, blue = 0, alpha = 0;
int readpixelcount, pixelcount;
+ int reflectivity[3] = { 0, 0, 0 };
qboolean compressed;
tga_t targa_header;
@@ -193,6 +195,10 @@ qboolean Image_LoadTGA( const char *name, const byte *buffer, size_t filesize )
if( red != green || green != blue )
image.flags |= IMAGE_HAS_COLOR;
+ reflectivity[0] += red;
+ reflectivity[1] += green;
+ reflectivity[2] += blue;
+
*pixbuf++ = red;
*pixbuf++ = green;
*pixbuf++ = blue;
@@ -206,6 +212,10 @@ qboolean Image_LoadTGA( const char *name, const byte *buffer, size_t filesize )
}
}
}
+
+ VectorDivide( reflectivity, ( image.width * image.height ), image.fogParams );
+ image.depth = 1;
+
return true;
}
@@ -229,7 +239,6 @@ qboolean Image_SaveTGA( const char *name, rgbdata_t *pix )
else outsize = pix->width * pix->height * 3 + 18 + Q_strlen( comment );
buffer = (byte *)Mem_Alloc( host.imagepool, outsize );
- Q_memset( buffer, 0, 18 );
// prepare header
buffer[0] = Q_strlen( comment ); // tga comment length
diff --git b/engine/common/imagelib/img_utils.c a/engine/common/imagelib/img_utils.c
index 651d270..23c5c0b 100644
--- b/engine/common/imagelib/img_utils.c
+++ a/engine/common/imagelib/img_utils.c
@@ -177,6 +177,11 @@ void Image_Init( void )
image.loadformats = load_game;
image.saveformats = save_game;
break;
+ case HOST_DEDICATED:
+ image.cmd_flags = 0;
+ image.loadformats = load_game;
+ image.saveformats = save_null;
+ break;
default: // all other instances not using imagelib or will be reinstalling later
image.loadformats = load_null;
image.saveformats = save_null;
@@ -197,7 +202,7 @@ byte *Image_Copy( size_t size )
byte *out;
out = Mem_Alloc( host.imagepool, size );
- Q_memcpy( out, image.tempbuffer, size );
+ memcpy( out, image.tempbuffer, size );
return out;
}
@@ -279,9 +284,9 @@ int Image_ComparePalette( const byte *pal )
{
if( pal == NULL )
return PAL_INVALID;
- else if( !Q_memcmp( palette_q1, pal, 768 ))
+ else if( !memcmp( palette_q1, pal, 768 ))
return PAL_QUAKE1;
- else if( !Q_memcmp( palette_hl, pal, 768 ))
+ else if( !memcmp( palette_hl, pal, 768 ))
return PAL_HALFLIFE;
return PAL_CUSTOM;
}
@@ -442,7 +447,7 @@ void Image_CopyPalette32bit( void )
{
if( image.palette ) return; // already created ?
image.palette = Mem_Alloc( host.imagepool, 1024 );
- Q_memcpy( image.palette, image.d_currentpal, 1024 );
+ memcpy( image.palette, image.d_currentpal, 1024 );
}
void Image_PaletteHueReplace( byte *palSrc, int newHue, int start, int end )
@@ -462,6 +467,8 @@ void Image_PaletteHueReplace( byte *palSrc, int newHue, int start, int end )
maxcol = max( max( r, g ), b ) / 255.0f;
mincol = min( min( r, g ), b ) / 255.0f;
+
+ if( maxcol == 0 ) continue;
val = maxcol;
sat = (maxcol - mincol) / maxcol;
@@ -528,7 +535,7 @@ void Image_CopyParms( rgbdata_t *src )
image.size = src->size;
image.palette = src->palette; // may be NULL
- Q_memcpy( image.fogParams, src->fogParams, sizeof( image.fogParams ));
+ memcpy( image.fogParams, src->fogParams, sizeof( image.fogParams ));
}
/*
@@ -567,7 +574,7 @@ qboolean Image_Copy8bitRGBA( const byte *in, byte *out, int pixels )
// check for color
for( i = 0; i < 256; i++ )
{
- col = (rgba_t *)image.d_currentpal[i];
+ col = (rgba_t *)&image.d_currentpal[i];
if( col[0] != col[1] || col[1] != col[2] )
{
image.flags |= IMAGE_HAS_COLOR;
@@ -713,7 +720,7 @@ void Image_Resample32Lerp( const void *indata, int inwidth, int inheight, void *
if( yi != oldy )
{
inrow = (byte *)indata + inwidth4 * yi;
- if (yi == oldy+1) Q_memcpy( resamplerow1, resamplerow2, outwidth4 );
+ if (yi == oldy+1) memcpy( resamplerow1, resamplerow2, outwidth4 );
else Image_Resample32LerpLine( inrow, resamplerow1, inwidth, outwidth );
Image_Resample32LerpLine( inrow + inwidth4, resamplerow2, inwidth, outwidth );
oldy = yi;
@@ -779,12 +786,12 @@ void Image_Resample32Lerp( const void *indata, int inwidth, int inheight, void *
if( yi != oldy )
{
inrow = (byte *)indata + inwidth4*yi;
- if( yi == oldy + 1 ) Q_memcpy( resamplerow1, resamplerow2, outwidth4 );
+ if( yi == oldy + 1 ) memcpy( resamplerow1, resamplerow2, outwidth4 );
else Image_Resample32LerpLine( inrow, resamplerow1, inwidth, outwidth);
oldy = yi;
}
- Q_memcpy( out, resamplerow1, outwidth4 );
+ memcpy( out, resamplerow1, outwidth4 );
}
}
@@ -860,7 +867,7 @@ void Image_Resample24Lerp( const void *indata, int inwidth, int inheight, void *
if( yi != oldy )
{
inrow = (byte *)indata + inwidth3 * yi;
- if( yi == oldy + 1) Q_memcpy( resamplerow1, resamplerow2, outwidth3 );
+ if( yi == oldy + 1) memcpy( resamplerow1, resamplerow2, outwidth3 );
else Image_Resample24LerpLine( inrow, resamplerow1, inwidth, outwidth );
Image_Resample24LerpLine( inrow + inwidth3, resamplerow2, inwidth, outwidth );
oldy = yi;
@@ -919,12 +926,12 @@ void Image_Resample24Lerp( const void *indata, int inwidth, int inheight, void *
if( yi != oldy )
{
inrow = (byte *)indata + inwidth3*yi;
- if( yi == oldy + 1) Q_memcpy( resamplerow1, resamplerow2, outwidth3 );
+ if( yi == oldy + 1) memcpy( resamplerow1, resamplerow2, outwidth3 );
else Image_Resample24LerpLine( inrow, resamplerow1, inwidth, outwidth );
oldy = yi;
}
- Q_memcpy( out, resamplerow1, outwidth3 );
+ memcpy( out, resamplerow1, outwidth3 );
}
}
@@ -1103,8 +1110,8 @@ byte *Image_FloodInternal( const byte *indata, int inwidth, int inheight, int ou
return (byte *)indata;
}
- if( samples == 1 ) Q_memset( out, 0xFF, newsize ); // last palette color
- else Q_memset( out, 0x00808080, newsize ); // gray (alpha leaved 0x00)
+ if( samples == 1 ) memset( out, 0xFF, newsize ); // last palette color
+ else memset( out, 0x00808080, newsize ); // gray (alpha leaved 0x00)
for( y = 0; y < outheight; y++ )
{
@@ -1114,7 +1121,7 @@ byte *Image_FloodInternal( const byte *indata, int inwidth, int inheight, int ou
{
if( x < inwidth )
*out++ = *in++;
- else *out++;
+ else out++;
}
}
}
@@ -1260,7 +1267,7 @@ qboolean Image_AddIndexedImageToPack( const byte *in, int width, int height )
// reallocate image buffer
image.rgba = Mem_Alloc( host.imagepool, image.size );
- if( !expand_to_rgba ) Q_memcpy( image.rgba, in, image.size );
+ if( !expand_to_rgba ) memcpy( image.rgba, in, image.size );
else if( !Image_Copy8bitRGBA( in, image.rgba, mipsize ))
return false; // probably pallette not installed
@@ -1331,7 +1338,7 @@ qboolean Image_Decompress( const byte *data )
break;
case PF_RGBA_32:
// fast default case
- Q_memcpy( fout, fin, size );
+ memcpy( fout, fin, size );
break;
default: return false;
}
@@ -1357,7 +1364,7 @@ rgbdata_t *Image_DecompressInternal( rgbdata_t *pic )
pic->type = PF_RGBA_32;
pic->buffer = Mem_Realloc( host.imagepool, pic->buffer, image.size );
- Q_memcpy( pic->buffer, image.tempbuffer, image.size );
+ memcpy( pic->buffer, image.tempbuffer, image.size );
if( pic->palette ) Mem_Free( pic->palette );
pic->flags = image.flags;
pic->palette = NULL;
@@ -1538,7 +1545,7 @@ qboolean Image_ApplyFilter( rgbdata_t *pic, int filter, float factor, float bias
}
// copy result back
- Q_memcpy( fin, fout, size );
+ memcpy( fin, fout, size );
return true;
}
@@ -1567,7 +1574,7 @@ qboolean Image_Process( rgbdata_t **pix, int width, int height, float gamma, uin
if( flags & IMAGE_MAKE_LUMA )
{
out = Image_CreateLumaInternal( pic->buffer, pic->width, pic->height, pic->type, pic->flags );
- if( pic->buffer != out ) Q_memcpy( pic->buffer, image.tempbuffer, pic->size );
+ if( pic->buffer != out ) memcpy( pic->buffer, image.tempbuffer, pic->size );
pic->flags &= ~IMAGE_HAS_LUMA;
}
@@ -1585,7 +1592,7 @@ qboolean Image_Process( rgbdata_t **pix, int width, int height, float gamma, uin
if( filter ) Image_ApplyFilter( pic, filter->filter, filter->factor, filter->bias, filter->flags, filter->blendFunc );
out = Image_FlipInternal( pic->buffer, &pic->width, &pic->height, pic->type, flags );
- if( pic->buffer != out ) Q_memcpy( pic->buffer, image.tempbuffer, pic->size );
+ if( pic->buffer != out ) memcpy( pic->buffer, image.tempbuffer, pic->size );
if(( flags & IMAGE_RESAMPLE && width > 0 && height > 0 ) || ( flags & IMAGE_ROUND ) || ( flags & IMAGE_ROUNDFILLER ))
{
diff --git b/engine/common/imagelib/img_wad.c a/engine/common/imagelib/img_wad.c
index 3c0f490..4f90112 100644
--- b/engine/common/imagelib/img_wad.c
+++ a/engine/common/imagelib/img_wad.c
@@ -58,6 +58,7 @@ qboolean Image_LoadPAL( const char *name, const byte *buffer, size_t filesize )
image.rgba = NULL; // only palette, not real image
image.size = 1024; // expanded palette
image.width = image.height = 0;
+ image.depth = 1;
return true;
}
@@ -80,7 +81,7 @@ qboolean Image_LoadFNT( const char *name, const byte *buffer, size_t filesize )
if( filesize < sizeof( font ))
return false;
- Q_memcpy( &font, buffer, sizeof( font ));
+ memcpy( &font, buffer, sizeof( font ));
// last sixty four bytes - what the hell ????
size = sizeof( qfont_t ) - 4 + ( font.height * font.width * QCHAR_WIDTH ) + sizeof( short ) + 768 + 64;
@@ -119,6 +120,7 @@ qboolean Image_LoadFNT( const char *name, const byte *buffer, size_t filesize )
}
image.type = PF_INDEXED_32; // 32-bit palette
+ image.depth = 1;
return Image_AddIndexedImageToPack( fin, image.width, image.height );
}
@@ -170,6 +172,7 @@ qboolean Image_LoadMDL( const char *name, const byte *buffer, size_t filesize )
}
image.type = PF_INDEXED_32; // 32-bit palete
+ image.depth = 1;
return Image_AddIndexedImageToPack( fin, image.width, image.height );
}
@@ -214,6 +217,7 @@ qboolean Image_LoadSPR( const char *name, const byte *buffer, size_t filesize )
// sorry, can't validate palette rendermode
if( !Image_LumpValidSize( name )) return false;
image.type = PF_INDEXED_32; // 32-bit palete
+ image.depth = 1;
// detect alpha-channel by palette type
switch( image.d_rendermode )
@@ -253,24 +257,12 @@ qboolean Image_LoadLMP( const char *name, const byte *buffer, size_t filesize )
if( Q_stristr( name, "palette.lmp" ))
return Image_LoadPAL( name, buffer, filesize );
- // greatest hack from id software
- if( image.hint != IL_HINT_HL && Q_stristr( name, "conchars" ))
- {
- image.width = image.height = 128;
- image.flags |= IMAGE_HAS_ALPHA;
- rendermode = LUMP_QFONT;
- filesize += sizeof(lmp);
- fin = (byte *)buffer;
- }
- else
- {
- fin = (byte *)buffer;
- Q_memcpy( &lmp, fin, sizeof( lmp ));
- image.width = lmp.width;
- image.height = lmp.height;
- rendermode = LUMP_NORMAL;
- fin += sizeof(lmp);
- }
+ fin = (byte *)buffer;
+ memcpy( &lmp, fin, sizeof( lmp ));
+ image.width = lmp.width;
+ image.height = lmp.height;
+ rendermode = LUMP_NORMAL;
+ fin += sizeof( lmp );
pixels = image.width * image.height;
@@ -305,6 +297,7 @@ qboolean Image_LoadLMP( const char *name, const byte *buffer, size_t filesize )
if( fin[0] == 255 ) image.flags |= IMAGE_HAS_ALPHA;
Image_GetPaletteLMP( pal, rendermode );
image.type = PF_INDEXED_32; // 32-bit palete
+ image.depth = 1;
return Image_AddIndexedImageToPack( fin, image.width, image.height );
}
@@ -321,6 +314,7 @@ qboolean Image_LoadMIP( const char *name, const byte *buffer, size_t filesize )
byte *fin, *pal;
int ofs[4], rendermode;
int i, pixels, numcolors;
+ int reflectivity[3] = { 0, 0, 0 };
if( filesize < sizeof( mip ))
{
@@ -328,14 +322,14 @@ qboolean Image_LoadMIP( const char *name, const byte *buffer, size_t filesize )
return false;
}
- Q_memcpy( &mip, buffer, sizeof( mip ));
+ memcpy( &mip, buffer, sizeof( mip ));
image.width = mip.width;
image.height = mip.height;
if( !Image_ValidSize( name ))
return false;
- Q_memcpy( ofs, mip.offsets, sizeof( ofs ));
+ memcpy( ofs, mip.offsets, sizeof( ofs ));
pixels = image.width * image.height;
if( image.hint != IL_HINT_Q1 && filesize >= (int)sizeof(mip) + ((pixels * 85)>>6) + sizeof(short) + 768)
@@ -462,7 +456,20 @@ qboolean Image_LoadMIP( const char *name, const byte *buffer, size_t filesize )
// calc the decal reflectivity
image.fogParams[3] = VectorAvg( image.fogParams );
}
+ else if( pal != NULL )// calc texture reflectivity
+ {
+ for( i = 0; i < 256; i++ )
+ {
+ reflectivity[0] += pal[i*3+0];
+ reflectivity[1] += pal[i*3+1];
+ reflectivity[2] += pal[i*3+2];
+ }
+
+ VectorDivide( reflectivity, 256, image.fogParams );
+ }
image.type = PF_INDEXED_32; // 32-bit palete
+ image.depth = 1;
+
return Image_AddIndexedImageToPack( fin, image.width, image.height );
}
\ No newline at end of file
diff --git b/engine/common/input.c a/engine/common/input.c
index 046f082..8a0f684 100644
--- b/engine/common/input.c
+++ a/engine/common/input.c
@@ -105,8 +105,8 @@ static int Host_MapKey( int key )
case 0x0D: return K_KP_ENTER;
case 0x2F: return K_KP_SLASH;
case 0xAF: return K_KP_PLUS;
+ default: return result;
}
- return result;
}
}
@@ -290,7 +290,7 @@ void IN_DeactivateMouse( void )
/*
================
-IN_Mouse
+IN_MouseMove
================
*/
void IN_MouseMove( void )
@@ -331,12 +331,12 @@ void IN_MouseEvent( int mstate )
// perform button actions
for( i = 0; i < in_mouse_buttons; i++ )
{
- if(( mstate & ( 1<<i )) && !( in_mouse_oldbuttonstate & ( 1<<i )))
+ if( FBitSet( mstate, BIT( i )) && !FBitSet( in_mouse_oldbuttonstate, BIT( i )))
{
Key_Event( K_MOUSE1 + i, true );
}
- if(!( mstate & ( 1<<i )) && ( in_mouse_oldbuttonstate & ( 1<<i )))
+ if( !FBitSet( mstate, BIT( i )) && FBitSet( in_mouse_oldbuttonstate, BIT( i )))
{
Key_Event( K_MOUSE1 + i, false );
}
@@ -383,13 +383,13 @@ void Host_InputFrame( void )
Cbuf_Execute ();
- if( host.state == HOST_RESTART )
- host.state = HOST_FRAME; // restart is finished
-
if( host.type == HOST_DEDICATED )
{
- // let the dedicated server some sleep
- Sys_Sleep( 1 );
+ if( !FBitSet( host.features, ENGINE_FIXED_FRAMERATE ))
+ {
+ // let the dedicated server some sleep
+ Sys_Sleep( 1 );
+ }
}
else
{
@@ -435,7 +435,7 @@ IN_WndProc
main window procedure
====================
*/
-long IN_WndProc( void *hWnd, uint uMsg, uint wParam, long lParam )
+LONG IN_WndProc( HWND hWnd, UINT uMsg, UINT wParam, LONG lParam )
{
int i, temp = 0;
qboolean fActivate;
@@ -455,7 +455,8 @@ long IN_WndProc( void *hWnd, uint uMsg, uint wParam, long lParam )
IN_ActivateCursor();
break;
case WM_MOUSEWHEEL:
- if( !in_mouseactive ) break;
+ if( !in_mouseactive )
+ break;
if(( short )HIWORD( wParam ) > 0 )
{
Key_Event( K_MWHEELUP, true );
@@ -478,17 +479,12 @@ long IN_WndProc( void *hWnd, uint uMsg, uint wParam, long lParam )
case WM_ACTIVATE:
if( host.state == HOST_SHUTDOWN )
break; // no need to activate
- if( host.state != HOST_RESTART )
- {
- if( HIWORD( wParam ))
- host.state = HOST_SLEEP;
- else if( LOWORD( wParam ) == WA_INACTIVE )
- host.state = HOST_NOFOCUS;
- else host.state = HOST_FRAME;
- fActivate = (host.state == HOST_FRAME) ? true : false;
- }
- else fActivate = true; // video sucessfully restarted
-
+ if( HIWORD( wParam ))
+ host.state = HOST_SLEEP;
+ else if( LOWORD( wParam ) == WA_INACTIVE )
+ host.state = HOST_NOFOCUS;
+ else host.state = HOST_FRAME;
+ fActivate = (host.state == HOST_FRAME) ? true : false;
wnd_caption = GetSystemMetrics( SM_CYCAPTION ) + WND_BORDER;
S_Activate( fActivate, host.hWnd );
@@ -500,7 +496,7 @@ long IN_WndProc( void *hWnd, uint uMsg, uint wParam, long lParam )
SetForegroundWindow( hWnd );
ShowWindow( hWnd, SW_RESTORE );
}
- else if( Cvar_VariableInteger( "fullscreen" ) && host.state != HOST_RESTART )
+ else if( Cvar_VariableInteger( "fullscreen" ))
{
ShowWindow( hWnd, SW_MINIMIZE );
}
diff --git b/engine/common/input.h a/engine/common/input.h
index 284cfa2..9bff4fc 100644
--- b/engine/common/input.h
+++ a/engine/common/input.h
@@ -45,7 +45,7 @@ void IN_MouseEvent( int mstate );
void IN_ActivateMouse( qboolean force );
void IN_DeactivateMouse( void );
void IN_ToggleClientMouse( int newstate, int oldstate );
-long IN_WndProc( void *hWnd, uint uMsg, uint wParam, long lParam );
+LONG IN_WndProc( HWND hWnd, UINT uMsg, UINT wParam, LONG lParam );
void IN_SetCursor( HICON hCursor );
#endif//INPUT_H
\ No newline at end of file
diff --git b/engine/common/keys.c a/engine/common/keys.c
index 4674409..aa9e2ab 100644
--- b/engine/common/keys.c
+++ a/engine/common/keys.c
@@ -46,18 +46,18 @@ keyname_t keynames[] =
{"RIGHTARROW", K_RIGHTARROW, "+right" },
{"ALT", K_ALT, "+strafe" },
{"CTRL", K_CTRL, "+attack" },
-{"SHIFT", K_SHIFT, "+speed" }, // replace with +attack2 ?
+{"SHIFT", K_SHIFT, "+speed" },
{"CAPSLOCK", K_CAPSLOCK, "" },
{"F1", K_F1, "cmd help" },
{"F2", K_F2, "menu_savegame" },
{"F3", K_F3, "menu_loadgame" },
-{"F4", K_F4, "menu_keys" },
-{"F5", K_F5, "menu_startserver" },
+{"F4", K_F4, "menu_controls" },
+{"F5", K_F5, "menu_creategame" },
{"F6", K_F6, "savequick" },
{"F7", K_F7, "loadquick" },
{"F8", K_F8, "stop" },
{"F9", K_F9, "" },
-{"F10", K_F10, "menu_quit" },
+{"F10", K_F10, "menu_main" },
{"F11", K_F11, "" },
{"F12", K_F12, "screenshot" },
{"INS", K_INS, "" },
@@ -106,7 +106,7 @@ Key_IsDown
*/
qboolean Key_IsDown( int keynum )
{
- if ( keynum == -1 )
+ if( keynum == -1 )
return false;
return keys[keynum].down;
}
@@ -179,6 +179,7 @@ int Key_StringToKeynum( const char *str )
if( !Q_stricmp( str, kn->name ))
return kn->keynum;
}
+
return -1;
}
@@ -275,6 +276,7 @@ int Key_GetKey( const char *binding )
if( keys[i].binding && !Q_stricmp( binding, keys[i].binding ))
return i;
}
+
return -1;
}
@@ -294,11 +296,13 @@ void Key_Unbind_f( void )
}
b = Key_StringToKeynum( Cmd_Argv( 1 ));
+
if( b == -1 )
{
Msg( "\"%s\" isn't a valid key\n", Cmd_Argv( 1 ));
return;
}
+
Key_SetBinding( b, "" );
}
@@ -325,8 +329,8 @@ Key_Reset_f
*/
void Key_Reset_f( void )
{
- int i;
keyname_t *kn;
+ int i;
// clear all keys first
for( i = 0; i < 256; i++ )
@@ -347,8 +351,8 @@ Key_Bind_f
*/
void Key_Bind_f( void )
{
- int i, c, b;
char cmd[1024];
+ int i, c, b;
c = Cmd_Argc();
@@ -398,12 +402,13 @@ void Key_WriteBindings( file_t *f )
int i;
if( !f ) return;
+
FS_Printf( f, "unbindall\n" );
for( i = 0; i < 256; i++ )
{
if( keys[i].binding && keys[i].binding[0] )
- FS_Printf( f, "bind %s \"%s\"\n", Key_KeynumToString(i), keys[i].binding );
+ FS_Printf( f, "bind %s \"%s\"\n", Key_KeynumToString( i ), keys[i].binding );
}
}
@@ -551,11 +556,15 @@ void Key_Event( int key, qboolean down )
// escape is always handled special
if( key == K_ESCAPE && down )
{
- kb = keys[key].binding;
-
switch( cls.key_dest )
{
case key_game:
+ if( gl_showtextures->integer )
+ {
+ // close texture atlas
+ Cvar_SetFloat( "r_showtextures", 0.0f );
+ return;
+ }
if( host.mouse_visible && cls.state != ca_cinematic )
{
clgame.dllFuncs.pfnKey_Event( down, key, keys[key].binding );
@@ -717,9 +726,7 @@ void Key_ClearStates( void )
}
if( clgame.hInstance )
- {
clgame.dllFuncs.IN_ClearStates();
- }
}
/*
diff --git b/engine/common/library.c a/engine/common/library.c
index f2688e3..efd3cfc 100644
--- b/engine/common/library.c
+++ a/engine/common/library.c
@@ -53,10 +53,10 @@ typedef BOOL (WINAPI *DllEntryProc)( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID
static void CopySections( const byte *data, PIMAGE_NT_HEADERS old_headers, PMEMORYMODULE module )
{
- int i, size;
- byte *dest;
- byte *codeBase = module->codeBase;
- PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION( module->headers );
+ PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION( module->headers );
+ byte *codeBase = module->codeBase;
+ int i, size;
+ byte *dest;
for( i = 0; i < module->headers->FileHeader.NumberOfSections; i++, section++ )
{
@@ -85,9 +85,9 @@ static void CopySections( const byte *data, PIMAGE_NT_HEADERS old_headers, PMEMO
static void FreeSections( PIMAGE_NT_HEADERS old_headers, PMEMORYMODULE module )
{
- int i, size;
- byte *codeBase = module->codeBase;
- PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(module->headers);
+ PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(module->headers);
+ byte *codeBase = module->codeBase;
+ int i, size;
for( i = 0; i < module->headers->FileHeader.NumberOfSections; i++, section++ )
{
@@ -96,13 +96,13 @@ static void FreeSections( PIMAGE_NT_HEADERS old_headers, PMEMORYMODULE module )
size = old_headers->OptionalHeader.SectionAlignment;
if( size > 0 )
{
- VirtualFree( codeBase + section->VirtualAddress, size, MEM_DECOMMIT );
+ VirtualFree((byte *)CALCULATE_ADDRESS( codeBase, section->VirtualAddress ), size, MEM_DECOMMIT );
section->Misc.PhysicalAddress = 0;
}
continue;
}
- VirtualFree( codeBase + section->VirtualAddress, section->SizeOfRawData, MEM_DECOMMIT );
+ VirtualFree((byte *)CALCULATE_ADDRESS( codeBase, section->VirtualAddress ), section->SizeOfRawData, MEM_DECOMMIT );
section->Misc.PhysicalAddress = 0;
}
}
@@ -147,16 +147,16 @@ static void FinalizeSections( MEMORYMODULE *module )
{
// change memory access flags
if( !VirtualProtect((LPVOID)section->Misc.PhysicalAddress, size, protect, &oldProtect ))
- Sys_Error( "Com_FinalizeSections: error protecting memory page\n" );
+ Sys_Error( "FinalizeSections: error protecting memory page\n" );
}
}
}
static void PerformBaseRelocation( MEMORYMODULE *module, DWORD delta )
{
- DWORD i;
- byte *codeBase = module->codeBase;
- PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY( module, IMAGE_DIRECTORY_ENTRY_BASERELOC );
+ PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY( module, IMAGE_DIRECTORY_ENTRY_BASERELOC );
+ byte *codeBase = module->codeBase;
+ DWORD i;
if( directory->Size > 0 )
{
@@ -200,12 +200,12 @@ static void PerformBaseRelocation( MEMORYMODULE *module, DWORD delta )
static FARPROC MemoryGetProcAddress( void *module, const char *name )
{
- int idx = -1;
- DWORD i, *nameRef;
- WORD *ordinal;
- PIMAGE_EXPORT_DIRECTORY exports;
- byte *codeBase = ((PMEMORYMODULE)module)->codeBase;
- PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY((MEMORYMODULE *)module, IMAGE_DIRECTORY_ENTRY_EXPORT );
+ PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY((MEMORYMODULE *)module, IMAGE_DIRECTORY_ENTRY_EXPORT );
+ byte *codeBase = ((PMEMORYMODULE)module)->codeBase;
+ PIMAGE_EXPORT_DIRECTORY exports;
+ int idx = -1;
+ DWORD i, *nameRef;
+ WORD *ordinal;
if( directory->Size == 0 )
{
@@ -253,9 +253,9 @@ static FARPROC MemoryGetProcAddress( void *module, const char *name )
static int BuildImportTable( MEMORYMODULE *module )
{
- int result=1;
- byte *codeBase = module->codeBase;
- PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY( module, IMAGE_DIRECTORY_ENTRY_IMPORT );
+ PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY( module, IMAGE_DIRECTORY_ENTRY_IMPORT );
+ byte *codeBase = module->codeBase;
+ int result = 1;
if( directory->Size > 0 )
{
diff --git b/engine/common/mathlib.c a/engine/common/mathlib.c
index 9d31d24..0811f85 100644
--- b/engine/common/mathlib.c
+++ a/engine/common/mathlib.c
@@ -430,17 +430,22 @@ AngleQuaternion
====================
*/
-void AngleQuaternion( const vec3_t angles, vec4_t q )
+void AngleQuaternion( const vec3_t angles, vec4_t q, qboolean studio )
{
- float angle;
float sr, sp, sy, cr, cp, cy;
- angle = angles[2] * 0.5f;
- SinCos( angle, &sy, &cy );
- angle = angles[1] * 0.5f;
- SinCos( angle, &sp, &cp );
- angle = angles[0] * 0.5f;
- SinCos( angle, &sr, &cr );
+ if( studio )
+ {
+ SinCos( angles[ROLL] * 0.5f, &sy, &cy );
+ SinCos( angles[YAW] * 0.5f, &sp, &cp );
+ SinCos( angles[PITCH] * 0.5f, &sr, &cr );
+ }
+ else
+ {
+ SinCos( DEG2RAD( angles[YAW] ) * 0.5f, &sy, &cy );
+ SinCos( DEG2RAD( angles[PITCH] ) * 0.5f, &sp, &cp );
+ SinCos( DEG2RAD( angles[ROLL] ) * 0.5f, &sr, &cr );
+ }
q[0] = sr * cp * cy - cr * sp * sy; // X
q[1] = cr * sp * cy + sr * cp * sy; // Y
@@ -450,6 +455,19 @@ void AngleQuaternion( const vec3_t angles, vec4_t q )
/*
====================
+QuaternionAngle
+
+====================
+*/
+void QuaternionAngle( const vec4_t q, vec3_t angles )
+{
+ matrix3x4 mat;
+ Matrix3x4_FromOriginQuat( mat, q, vec3_origin );
+ Matrix3x4_AnglesFromMatrix( mat, angles );
+}
+
+/*
+====================
QuaternionSlerp
====================
diff --git b/engine/common/mathlib.h a/engine/common/mathlib.h
index f44e3ff..c88c8e6 100644
--- b/engine/common/mathlib.h
+++ a/engine/common/mathlib.h
@@ -55,10 +55,21 @@ GNU General Public License for more details.
#define RAD_TO_STUDIO (32768.0 / M_PI)
#define STUDIO_TO_RAD (M_PI / 32768.0)
-#define nanmask (255<<23)
+
+#define INV127F ( 1.0f / 127.0f )
+#define INV255F ( 1.0f / 255.0f )
+#define MAKE_SIGNED( x ) ((( x ) * INV127F ) - 1.0f )
+
+#define Q_min( a, b ) (((a) < (b)) ? (a) : (b))
+#define Q_max( a, b ) (((a) > (b)) ? (a) : (b))
+#define Q_recip( a ) ((float)(1.0f / (float)(a)))
+#define Q_floor( a ) ((float)(long)(a))
+#define Q_ceil( a ) ((float)(long)((a) + 1))
#define Q_rint(x) ((x) < 0 ? ((int)((x)-0.5f)) : ((int)((x)+0.5f)))
-#define IS_NAN(x) (((*(int *)&x)&nanmask)==nanmask)
+#define IS_NAN(x) (((*(int *)&x) & (255<<23)) == (255<<23))
+
+#define ALIGN( x, a ) ((( x ) + (( size_t )( a ) - 1 )) & ~(( size_t )( a ) - 1 ))
#define VectorIsNAN(v) (IS_NAN(v[0]) || IS_NAN(v[1]) || IS_NAN(v[2]))
#define DotProduct(x,y) ((x)[0]*(y)[0]+(x)[1]*(y)[1]+(x)[2]*(y)[2])
@@ -123,7 +134,8 @@ qboolean BoundsIntersect( const vec3_t mins1, const vec3_t maxs1, const vec3_t m
qboolean BoundsAndSphereIntersect( const vec3_t mins, const vec3_t maxs, const vec3_t origin, float radius );
float RadiusFromBounds( const vec3_t mins, const vec3_t maxs );
-void AngleQuaternion( const vec3_t angles, vec4_t q );
+void AngleQuaternion( const vec3_t angles, vec4_t q, qboolean studio );
+void QuaternionAngle( const vec4_t q, vec3_t angles );
void QuaternionSlerp( const vec4_t p, vec4_t q, float t, vec4_t qt );
float RemapVal( float val, float A, float B, float C, float D );
float ApproachVal( float target, float value, float speed );
@@ -132,7 +144,7 @@ float ApproachVal( float target, float value, float speed );
// matrixlib.c
//
#define Matrix3x4_LoadIdentity( mat ) Matrix3x4_Copy( mat, matrix3x4_identity )
-#define Matrix3x4_Copy( out, in ) Q_memcpy( out, in, sizeof( matrix3x4 ))
+#define Matrix3x4_Copy( out, in ) memcpy( out, in, sizeof( matrix3x4 ))
void Matrix3x4_VectorTransform( const matrix3x4 in, const float v[3], float out[3] );
void Matrix3x4_VectorITransform( const matrix3x4 in, const float v[3], float out[3] );
@@ -145,9 +157,10 @@ void Matrix3x4_TransformPositivePlane( const matrix3x4 in, const vec3_t normal,
void Matrix3x4_SetOrigin( matrix3x4 out, float x, float y, float z );
void Matrix3x4_Invert_Simple( matrix3x4 out, const matrix3x4 in1 );
void Matrix3x4_OriginFromMatrix( const matrix3x4 in, float *out );
+void Matrix3x4_AnglesFromMatrix( const matrix3x4 in, vec3_t out );
#define Matrix4x4_LoadIdentity( mat ) Matrix4x4_Copy( mat, matrix4x4_identity )
-#define Matrix4x4_Copy( out, in ) Q_memcpy( out, in, sizeof( matrix4x4 ))
+#define Matrix4x4_Copy( out, in ) memcpy( out, in, sizeof( matrix4x4 ))
void Matrix4x4_VectorTransform( const matrix4x4 in, const float v[3], float out[3] );
void Matrix4x4_VectorITransform( const matrix4x4 in, const float v[3], float out[3] );
diff --git b/engine/common/matrixlib.c a/engine/common/matrixlib.c
index af992d2..fdeea66 100644
--- b/engine/common/matrixlib.c
+++ a/engine/common/matrixlib.c
@@ -94,6 +94,26 @@ void Matrix3x4_OriginFromMatrix( const matrix3x4 in, float *out )
out[2] = in[2][3];
}
+void Matrix3x4_AnglesFromMatrix( const matrix3x4 in, vec3_t out )
+{
+ float xyDist = sqrt( in[0][0] * in[0][0] + in[1][0] * in[1][0] );
+
+ if( xyDist > 0.001f )
+ {
+ // enough here to get angles?
+ out[0] = RAD2DEG( atan2( -in[2][0], xyDist ));
+ out[1] = RAD2DEG( atan2( in[1][0], in[0][0] ));
+ out[2] = RAD2DEG( atan2( in[2][1], in[2][2] ));
+ }
+ else
+ {
+ // forward is mostly Z, gimbal lock
+ out[0] = RAD2DEG( atan2( -in[2][0], xyDist ));
+ out[1] = RAD2DEG( atan2( -in[0][1], in[1][1] ));
+ out[2] = 0.0f;
+ }
+}
+
void Matrix3x4_FromOriginQuat( matrix3x4 out, const vec4_t quaternion, const vec3_t origin )
{
out[0][0] = 1.0f - 2.0f * quaternion[1] * quaternion[1] - 2.0f * quaternion[2] * quaternion[2];
diff --git b/engine/common/mod_local.h a/engine/common/mod_local.h
index 896c944..0f61ce5 100644
--- b/engine/common/mod_local.h
+++ a/engine/common/mod_local.h
@@ -42,6 +42,14 @@ GNU General Public License for more details.
#define SURF_INFO( surf, mod ) ((mextrasurf_t *)mod->cache.data + (surf - mod->surfaces))
#define INFO_SURF( surf, mod ) (mod->surfaces + (surf - (mextrasurf_t *)mod->cache.data))
+#define CHECKVISBIT( vis, b ) ((b) >= 0 ? (byte)((vis)[(b) >> 3] & (1 << ((b) & 7))) : (byte)false )
+#define SETVISBIT( vis, b )( void ) ((b) >= 0 ? (byte)((vis)[(b) >> 3] |= (1 << ((b) & 7))) : (byte)false )
+#define CLEARVISBIT( vis, b )( void ) ((b) >= 0 ? (byte)((vis)[(b) >> 3] &= ~(1 << ((b) & 7))) : (byte)false )
+
+#define REFPVS_RADIUS 2.0f // radius for rendering
+#define FATPVS_RADIUS 8.0f // FatPVS use radius smaller than the FatPHS
+#define FATPHS_RADIUS 16.0f
+
// model flags (stored in model_t->flags)
#define MODEL_CONVEYOR BIT( 0 )
#define MODEL_HAS_ORIGIN BIT( 1 )
@@ -72,19 +80,30 @@ typedef struct
vec3_t hull_sizes[MAX_MAP_HULLS]; // actual hull sizes
msurface_t **draw_surfaces; // used for sorting translucent surfaces
int max_surfaces; // max surfaces per submodel (for all models)
- size_t visdatasize; // actual size of the visdata
- size_t litdatasize; // actual size of the lightdata
- size_t vecdatasize; // actual size of the deluxdata
- size_t entdatasize; // actual size of the entity string
- size_t texdatasize; // actual size of the textures lump
+
qboolean loading; // true if worldmodel is loading
qboolean sky_sphere; // true when quake sky-sphere is used
qboolean has_mirrors; // one or more brush models contain reflective textures
+ qboolean custom_skybox; // if sky_sphere is active and custom skybox set
+ qboolean water_alpha; // allow translucency water
int lm_sample_size; // defaulting to 16 (BSP31 uses 8)
int block_size; // lightmap blocksize
color24 *deluxedata; // deluxemap data pointer
char message[2048]; // just for debug
+ // visibility info
+ byte *visdata; // uncompressed visdata
+ size_t visbytes; // cluster size
+ size_t fatbytes; // fatpvs size
+ int visclusters; // num visclusters
+
+ // world stats
+ size_t visdatasize; // actual size of the visdata
+ size_t litdatasize; // actual size of the lightdata
+ size_t vecdatasize; // actual size of the deluxdata
+ size_t entdatasize; // actual size of the entity string
+ size_t texdatasize; // actual size of the textures lump
+
vec3_t mins; // real accuracy world bounds
vec3_t maxs;
vec3_t size;
@@ -117,20 +136,21 @@ model_t *Mod_FindName( const char *name, qboolean create );
model_t *Mod_LoadModel( model_t *mod, qboolean world );
model_t *Mod_ForName( const char *name, qboolean world );
qboolean Mod_RegisterModel( const char *name, int index );
-int Mod_PointLeafnum( const vec3_t p );
-byte *Mod_LeafPVS( mleaf_t *leaf, model_t *model );
-byte *Mod_LeafPHS( mleaf_t *leaf, model_t *model );
mleaf_t *Mod_PointInLeaf( const vec3_t p, mnode_t *node );
+qboolean Mod_HeadnodeVisible( mnode_t *node, const byte *visbits, short *lastleaf );
void Mod_TesselatePolygon( msurface_t *surf, model_t *mod, float tessSize );
int Mod_BoxLeafnums( const vec3_t mins, const vec3_t maxs, short *list, int listsize, int *lastleaf );
+int Mod_FatPVS( const vec3_t org, float radius, byte *visbuffer, int visbytes, qboolean merge, qboolean fullvis );
qboolean Mod_BoxVisible( const vec3_t mins, const vec3_t maxs, const byte *visbits );
+int Mod_CheckLump( const char *filename, const int lump, int *lumpsize );
+int Mod_ReadLump( const char *filename, const int lump, void **lumpdata, int *lumpsize );
+int Mod_SaveLump( const char *filename, const int lump, void *lumpdata, int lumpsize );
void Mod_BuildSurfacePolygons( msurface_t *surf, mextrasurf_t *info );
void Mod_AmbientLevels( const vec3_t p, byte *pvolumes );
-byte *Mod_CompressVis( const byte *in, size_t *size );
-byte *Mod_DecompressVis( const byte *in );
+int Mod_SampleSizeForFace( msurface_t *surf );
+byte *Mod_GetPVSForPoint( const vec3_t p );
modtype_t Mod_GetType( int handle );
model_t *Mod_Handle( int handle );
-struct wadlist_s *Mod_WadList( void );
//
// mod_studio.c
diff --git b/engine/common/mod_studio.c a/engine/common/mod_studio.c
index a64d856..264a55b 100644
--- b/engine/common/mod_studio.c
+++ a/engine/common/mod_studio.c
@@ -208,8 +208,6 @@ hull_t *Mod_HullForStudio( model_t *model, float frame, int sequence, vec3_t ang
mstudiobbox_t *phitbox;
int i, j;
- ASSERT( numhitboxes );
-
*numhitboxes = 0; // assume error
if( mod_studiocache->integer )
@@ -230,8 +228,6 @@ hull_t *Mod_HullForStudio( model_t *model, float frame, int sequence, vec3_t ang
mod_studiohdr = Mod_Extradata( model );
if( !mod_studiohdr ) return NULL; // probably not a studiomodel
- ASSERT( pBlendAPI != NULL );
-
VectorCopy( angles, angles2 );
if( !( host.features & ENGINE_COMPENSATE_QUAKE_BUG ))
@@ -406,13 +402,13 @@ static void Mod_StudioCalcBoneQuaterion( int frame, float s, mstudiobone_t *pbon
if( !VectorCompare( angle1, angle2 ))
{
- AngleQuaternion( angle1, q1 );
- AngleQuaternion( angle2, q2 );
+ AngleQuaternion( angle1, q1, true );
+ AngleQuaternion( angle2, q2, true );
QuaternionSlerp( q1, q2, s, q );
}
else
{
- AngleQuaternion( angle1, q );
+ AngleQuaternion( angle1, q, true );
}
}
@@ -745,8 +741,6 @@ void Mod_StudioGetAttachment( const edict_t *e, int iAttachment, float *origin,
if( mod_studiohdr->numattachments <= 0 )
return;
- ASSERT( pBlendAPI != NULL );
-
if( mod_studiohdr->numattachments > MAXSTUDIOATTACHMENTS )
{
mod_studiohdr->numattachments = MAXSTUDIOATTACHMENTS; // reduce it
@@ -794,8 +788,6 @@ void Mod_GetBonePosition( const edict_t *e, int iBone, float *origin, float *ang
mod_studiohdr = (studiohdr_t *)Mod_Extradata( mod );
if( !mod_studiohdr ) return;
- ASSERT( pBlendAPI != NULL );
-
pBlendAPI->SV_StudioSetupBones( mod, e->v.frame, e->v.sequence, e->v.angles, e->v.origin,
e->v.controller, e->v.blending, iBone, e );
@@ -987,7 +979,7 @@ void Mod_InitStudioAPI( void )
pBlendIface = (STUDIOAPI)Com_GetProcAddress( svgame.hInstance, "Server_GetBlendingInterface" );
if( pBlendIface && pBlendIface( SV_BLENDING_INTERFACE_VERSION, &pBlendAPI, &gStudioAPI, &studio_transform, &studio_bones ))
{
- MsgDev( D_AICONSOLE, "SV_LoadProgs: ^2initailized Server Blending interface ^7ver. %i\n", SV_BLENDING_INTERFACE_VERSION );
+ MsgDev( D_REPORT, "SV_LoadProgs: ^2initailized Server Blending interface ^7ver. %i\n", SV_BLENDING_INTERFACE_VERSION );
return;
}
diff --git b/engine/common/model.c a/engine/common/model.c
index e0c9c24..678ec15 100644
--- b/engine/common/model.c
+++ a/engine/common/model.c
@@ -22,6 +22,7 @@ GNU General Public License for more details.
#include "gl_local.h"
#include "features.h"
#include "client.h"
+#include "server.h" // LUMP_ error codes
#define MAX_SIDE_VERTS 512 // per one polygon
@@ -160,6 +161,7 @@ void Mod_PrintBSPFileSizes_f( void )
Msg( "=== Total BSP file data space used: %s ===\n", Q_memprint( totalmemory ));
Msg( "World size ( %g %g %g ) units\n", world.size[0], world.size[1], world.size[2] );
+ Msg( "Supports transparency world water: %s\n", world.water_alpha ? "Yes" : "No" );
Msg( "original name: ^1%s\n", worldmodel->name );
Msg( "internal name: %s\n", (world.message[0]) ? va( "^2%s", world.message ) : "none" );
}
@@ -204,94 +206,50 @@ void Mod_SetupHulls( vec3_t mins[MAX_MAP_HULLS], vec3_t maxs[MAX_MAP_HULLS] )
/*
===================
-Mod_CompressVis
+Mod_DecompressVis
===================
*/
-byte *Mod_CompressVis( const byte *in, size_t *size )
+static void Mod_DecompressVis( const byte *in, const byte *inend, byte *out, byte *outend )
{
- int j, rep;
- int visrow;
- byte *dest_p;
+ byte *outstart = out;
+ int c;
- if( !worldmodel )
+ while( out < outend )
{
- Host_Error( "Mod_CompressVis: no worldmodel\n" );
- return NULL;
- }
-
- dest_p = visdata;
- visrow = (worldmodel->numleafs + 7) >> 3;
-
- for( j = 0; j < visrow; j++ )
- {
- *dest_p++ = in[j];
- if( in[j] ) continue;
-
- rep = 1;
- for( j++; j < visrow; j++ )
+ if( in == inend )
{
- if( in[j] || rep == 255 )
- break;
- else rep++;
+ MsgDev( D_WARN, "Mod_DecompressVis: input underrun (decompressed %i of %i output bytes)\n",
+ (int)(out - outstart), (int)(outend - outstart));
+ return;
}
- *dest_p++ = rep;
- j--;
- }
-
- if( size ) *size = dest_p - visdata;
-
- return visdata;
-}
-/*
-===================
-Mod_DecompressVis
-===================
-*/
-byte *Mod_DecompressVis( const byte *in )
-{
- int c, row;
- byte *out;
+ c = *in++;
- if( !worldmodel )
- {
- Host_Error( "Mod_DecompressVis: no worldmodel\n" );
- return NULL;
- }
-
- row = (worldmodel->numleafs + 7) >> 3;
- out = visdata;
-
- if( !in )
- {
- // no vis info, so make all visible
- while( row )
+ if( c )
{
- *out++ = 0xff;
- row--;
+ *out++ = c;
}
- return visdata;
- }
-
- do
- {
- if( *in )
+ else
{
- *out++ = *in++;
- continue;
- }
-
- c = in[1];
- in += 2;
+ if( in == inend )
+ {
+ MsgDev( D_REPORT, "Mod_DecompressVis: input underrun (during zero-run) (decompressed %i of %i output bytes)\n",
+ (int)(out - outstart), (int)(outend - outstart));
+ return;
+ }
- while( c )
- {
- *out++ = 0;
- c--;
+ for( c = *in++; c > 0; c-- )
+ {
+ if( out == outend )
+ {
+ MsgDev( D_REPORT, "Mod_DecompressVis: output overrun (decompressed %i of %i output bytes)\n",
+ (int)(out - outstart), (int)(outend - outstart));
+ return;
+ }
+ *out++ = 0;
+ }
}
- } while( out - visdata < row );
-
- return visdata;
+ }
}
/*
@@ -317,41 +275,99 @@ mleaf_t *Mod_PointInLeaf( const vec3_t p, mnode_t *node )
/*
==================
-Mod_LeafPVS
+Mod_GetPVSForPoint
+Returns PVS data for a given point
+NOTE: can return NULL
==================
*/
-byte *Mod_LeafPVS( mleaf_t *leaf, model_t *model )
+byte *Mod_GetPVSForPoint( const vec3_t p )
{
- if( !model || !leaf || leaf == model->leafs || !model->visdata )
- return Mod_DecompressVis( NULL );
- return Mod_DecompressVis( leaf->compressed_vis );
+ mnode_t *node;
+ mleaf_t *leaf = NULL;
+
+ ASSERT( worldmodel != NULL );
+
+ node = worldmodel->nodes;
+
+ while( 1 )
+ {
+ if( node->contents < 0 )
+ {
+ leaf = (mleaf_t *)node;
+ break; // we found a leaf
+ }
+ node = node->children[PlaneDiff( p, node->plane ) < 0];
+ }
+
+ if( leaf && leaf->cluster >= 0 )
+ return world.visdata + leaf->cluster * world.visbytes;
+ return NULL;
}
/*
==================
-Mod_LeafPHS
+Mod_FatPVS_RecursiveBSPNode
==================
*/
-byte *Mod_LeafPHS( mleaf_t *leaf, model_t *model )
+static void Mod_FatPVS_RecursiveBSPNode( const vec3_t org, float radius, byte *visbuffer, int visbytes, mnode_t *node )
{
- if( !model || !leaf || leaf == model->leafs || !model->visdata )
- return Mod_DecompressVis( NULL );
- return Mod_DecompressVis( leaf->compressed_pas );
+ int i;
+
+ while( node->contents >= 0 )
+ {
+ float d = PlaneDiff( org, node->plane );
+
+ if( d > radius )
+ node = node->children[0];
+ else if( d < -radius )
+ node = node->children[1];
+ else
+ {
+ // go down both sides
+ Mod_FatPVS_RecursiveBSPNode( org, radius, visbuffer, visbytes, node->children[0] );
+ node = node->children[1];
+ }
+ }
+
+ // if this leaf is in a cluster, accumulate the vis bits
+ if(((mleaf_t *)node)->cluster >= 0 )
+ {
+ byte *vis = world.visdata + ((mleaf_t *)node)->cluster * world.visbytes;
+
+ for( i = 0; i < visbytes; i++ )
+ visbuffer[i] |= vis[i];
+ }
}
/*
==================
-Mod_PointLeafnum
+Mod_FatPVS_RecursiveBSPNode
+Calculates a PVS that is the inclusive or of all leafs
+within radius pixels of the given point.
==================
*/
-int Mod_PointLeafnum( const vec3_t p )
+int Mod_FatPVS( const vec3_t org, float radius, byte *visbuffer, int visbytes, qboolean merge, qboolean fullvis )
{
- // map not loaded
- if ( !worldmodel ) return 0;
- return Mod_PointInLeaf( p, worldmodel->nodes ) - worldmodel->leafs;
+ mleaf_t *leaf = Mod_PointInLeaf( org, worldmodel->nodes );
+ int bytes = world.visbytes;
+
+ bytes = Q_min( bytes, visbytes );
+
+ // enable full visibility for some reasons
+ if( fullvis || !world.visclusters || !leaf || leaf->cluster < 0 )
+ {
+ memset( visbuffer, 0xFF, bytes );
+ return bytes;
+ }
+
+ if( !merge ) memset( visbuffer, 0x00, bytes );
+
+ Mod_FatPVS_RecursiveBSPNode( org, radius, visbuffer, bytes, worldmodel->nodes );
+
+ return bytes;
}
/*
@@ -363,8 +379,7 @@ LEAF LISTING
*/
static void Mod_BoxLeafnums_r( leaflist_t *ll, mnode_t *node )
{
- mplane_t *plane;
- int s;
+ int sides;
while( 1 )
{
@@ -382,18 +397,17 @@ static void Mod_BoxLeafnums_r( leaflist_t *ll, mnode_t *node )
return;
}
- ll->list[ll->count++] = leaf - worldmodel->leafs - 1;
+ ll->list[ll->count++] = leaf->cluster;
return;
}
- plane = node->plane;
- s = BOX_ON_PLANE_SIDE( ll->mins, ll->maxs, plane );
+ sides = BOX_ON_PLANE_SIDE( ll->mins, ll->maxs, node->plane );
- if( s == 1 )
+ if( sides == 1 )
{
node = node->children[0];
}
- else if( s == 2 )
+ else if( sides == 2 )
{
node = node->children[1];
}
@@ -421,11 +435,12 @@ int Mod_BoxLeafnums( const vec3_t mins, const vec3_t maxs, short *list, int list
VectorCopy( mins, ll.mins );
VectorCopy( maxs, ll.maxs );
- ll.count = 0;
+
ll.maxcount = listsize;
- ll.list = list;
- ll.topnode = -1;
ll.overflowed = false;
+ ll.topnode = -1;
+ ll.list = list;
+ ll.count = 0;
Mod_BoxLeafnums_r( &ll, worldmodel->nodes );
@@ -453,15 +468,42 @@ qboolean Mod_BoxVisible( const vec3_t mins, const vec3_t maxs, const byte *visbi
for( i = 0; i < count; i++ )
{
- int leafnum = leafList[i];
-
- if( leafnum != -1 && visbits[leafnum>>3] & (1<<( leafnum & 7 )))
+ if( CHECKVISBIT( visbits, leafList[i] ))
return true;
}
return false;
}
/*
+=============
+Mod_HeadnodeVisible
+=============
+*/
+qboolean Mod_HeadnodeVisible( mnode_t *node, const byte *visbits, short *lastleaf )
+{
+ if( !node || node->contents == CONTENTS_SOLID )
+ return false;
+
+ if( node->contents < 0 )
+ {
+ if( !CHECKVISBIT( visbits, ((mleaf_t *)node)->cluster ))
+ return false;
+
+ if( lastleaf )
+ *lastleaf = ((mleaf_t *)node)->cluster;
+ return true;
+ }
+
+ if( Mod_HeadnodeVisible( node->children[0], visbits, lastleaf ))
+ return true;
+
+ if( Mod_HeadnodeVisible( node->children[1], visbits, lastleaf ))
+ return true;
+
+ return false;
+}
+
+/*
==================
Mod_AmbientLevels
@@ -480,6 +522,56 @@ void Mod_AmbientLevels( const vec3_t p, byte *pvolumes )
}
/*
+==================
+Mod_SampleSizeForFace
+
+return the current lightmap resolution per face
+==================
+*/
+int Mod_SampleSizeForFace( msurface_t *surf )
+{
+ if( !surf || !surf->texinfo || !surf->texinfo->faceinfo )
+ return LM_SAMPLE_SIZE;
+
+ return surf->texinfo->faceinfo->texture_step;
+}
+
+/*
+==================
+Mod_CheckWaterAlphaSupport
+
+converted maps potential may don't
+support water transparency
+==================
+*/
+static qboolean Mod_CheckWaterAlphaSupport( void )
+{
+ mleaf_t *leaf;
+ int i, j;
+ const byte *pvs;
+
+ if( world.visdatasize <= 0 ) return true;
+
+ // check all liquid leafs to see if they can see into empty leafs, if any
+ // can we can assume this map supports r_wateralpha
+ for( i = 0, leaf = loadmodel->leafs; i < loadmodel->numleafs; i++, leaf++ )
+ {
+ if(( leaf->contents == CONTENTS_WATER || leaf->contents == CONTENTS_SLIME ) && leaf->cluster >= 0 )
+ {
+ pvs = world.visdata + leaf->cluster * world.visbytes;
+
+ for( j = 0; j < loadmodel->numleafs; j++ )
+ {
+ if( CHECKVISBIT( pvs, loadmodel->leafs[j].cluster ) && loadmodel->leafs[j].contents == CONTENTS_EMPTY )
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+/*
================
Mod_FreeUserData
================
@@ -490,10 +582,21 @@ static void Mod_FreeUserData( model_t *mod )
if( !mod || !mod->name[0] )
return;
- if( clgame.drawFuncs.Mod_ProcessUserData != NULL )
+ if( host.type == HOST_DEDICATED )
{
- // let the client.dll free custom data
- clgame.drawFuncs.Mod_ProcessUserData( mod, false, NULL );
+ if( svgame.physFuncs.Mod_ProcessUserData != NULL )
+ {
+ // let the server.dll free custom data
+ svgame.physFuncs.Mod_ProcessUserData( mod, false, NULL );
+ }
+ }
+ else
+ {
+ if( clgame.drawFuncs.Mod_ProcessUserData != NULL )
+ {
+ // let the client.dll free custom data
+ clgame.drawFuncs.Mod_ProcessUserData( mod, false, NULL );
+ }
}
}
@@ -618,7 +721,7 @@ static void Mod_LoadSubmodels( const dlump_t *l )
{
for( j = 0; j < 3; j++ )
{
- // spread the mins / maxs by a pixel
+ // spread the mins / maxs by a unit
out->mins[j] = in->mins[j] - 1.0f;
out->maxs[j] = in->maxs[j] + 1.0f;
out->origin[j] = in->origin[j];
@@ -640,7 +743,7 @@ static void Mod_LoadSubmodels( const dlump_t *l )
VectorAverage( out->mins, out->maxs, out->origin );
}
- world.max_surfaces = max( world.max_surfaces, out->numfaces );
+ world.max_surfaces = Q_max( world.max_surfaces, out->numfaces );
}
if( world.loading )
@@ -671,6 +774,7 @@ static void Mod_LoadTextures( const dlump_t *l )
GL_FreeTexture( tr.alphaskyTexture );
tr.solidskyTexture = tr.alphaskyTexture = 0;
world.texdatasize = l->filelen;
+ world.custom_skybox = false;
world.has_mirrors = false;
world.sky_sphere = false;
}
@@ -744,7 +848,6 @@ static void Mod_LoadTextures( const dlump_t *l )
if( load_external )
{
tr.solidskyTexture = GL_LoadTexture( texname, NULL, 0, TF_UNCOMPRESSED|TF_NOMIPMAP, NULL );
- GL_SetTextureType( tr.solidskyTexture, TEX_BRUSH );
load_external = false;
}
@@ -766,7 +869,6 @@ static void Mod_LoadTextures( const dlump_t *l )
if( load_external )
{
tr.alphaskyTexture = GL_LoadTexture( texname, NULL, 0, TF_UNCOMPRESSED|TF_NOMIPMAP, NULL );
- GL_SetTextureType( tr.alphaskyTexture, TEX_BRUSH );
load_external = false;
}
}
@@ -918,13 +1020,6 @@ static void Mod_LoadTextures( const dlump_t *l )
}
}
}
-
- if( tx->gl_texturenum != tr.defaultTexture )
- {
- // apply texture type (just for debug)
- GL_SetTextureType( tx->gl_texturenum, TEX_BRUSH );
- GL_SetTextureType( tx->fb_texturenum, TEX_BRUSH );
- }
}
// sequence the animations
@@ -932,11 +1027,11 @@ static void Mod_LoadTextures( const dlump_t *l )
{
tx = loadmodel->textures[i];
- if( !tx || tx->name[0] != '+' )
+ if( tx->name[0] != '+' || tx->name[1] == 0 || tx->name[2] == 0 )
continue;
if( tx->anim_next )
- continue; // allready sequenced
+ continue; // already sequenced
// find the number of frames in the animation
memset( anims, 0, sizeof( anims ));
@@ -959,23 +1054,22 @@ static void Mod_LoadTextures( const dlump_t *l )
altanims[altmax] = tx;
altmax++;
}
- else Host_Error( "Mod_LoadTextures: bad animating texture %s\n", tx->name );
+ else MsgDev( D_ERROR, "Mod_LoadTextures: bad animating texture %s\n", tx->name );
for( j = i + 1; j < loadmodel->numtextures; j++ )
{
tx2 = loadmodel->textures[j];
- if( !tx2 || tx2->name[0] != '+' )
- continue;
- if( Q_strcmp( tx2->name + 2, tx->name + 2 ))
+ if( tx2->name[0] != '+' || Q_strcmp( tx2->name + 2, tx->name + 2 ))
continue;
num = tx2->name[1];
+
if( num >= '0' && num <= '9' )
{
num -= '0';
anims[num] = tx2;
- if (num+1 > max)
+ if( num + 1 > max )
max = num + 1;
}
else if( num >= 'a' && num <= 'j' )
@@ -985,14 +1079,21 @@ static void Mod_LoadTextures( const dlump_t *l )
if( num + 1 > altmax )
altmax = num + 1;
}
- else Host_Error( "Mod_LoadTextures: bad animating texture %s\n", tx->name );
+ else MsgDev( D_ERROR, "Mod_LoadTextures: bad animating texture %s\n", tx->name );
}
// link them all together
for( j = 0; j < max; j++ )
{
tx2 = anims[j];
- if( !tx2 ) Host_Error( "Mod_LoadTextures: missing frame %i of %s\n", j, tx->name );
+
+ if( !tx2 )
+ {
+ MsgDev( D_ERROR, "Mod_LoadTextures: missing frame %i of %s\n", j, tx->name );
+ tx->anim_total = 0;
+ break;
+ }
+
tx2->anim_total = max * ANIM_CYCLE;
tx2->anim_min = j * ANIM_CYCLE;
tx2->anim_max = (j + 1) * ANIM_CYCLE;
@@ -1003,7 +1104,14 @@ static void Mod_LoadTextures( const dlump_t *l )
for( j = 0; j < altmax; j++ )
{
tx2 = altanims[j];
- if( !tx2 ) Host_Error( "Mod_LoadTextures: missing frame %i of %s\n", j, tx->name );
+
+ if( !tx2 )
+ {
+ MsgDev( D_ERROR, "Mod_LoadTextures: missing frame %i of %s\n", j, tx->name );
+ tx->anim_total = 0;
+ break;
+ }
+
tx2->anim_total = altmax * ANIM_CYCLE;
tx2->anim_min = j * ANIM_CYCLE;
tx2->anim_max = (j+1) * ANIM_CYCLE;
@@ -1017,11 +1125,11 @@ static void Mod_LoadTextures( const dlump_t *l )
{
tx = loadmodel->textures[i];
- if( !tx || tx->name[0] != '-' )
+ if( tx->name[0] != '-' || tx->name[1] == 0 || tx->name[2] == 0 )
continue;
if( tx->anim_next )
- continue; // allready sequenced
+ continue; // already sequenced
// find the number of frames in the sequence
memset( anims, 0, sizeof( anims ));
@@ -1034,33 +1142,39 @@ static void Mod_LoadTextures( const dlump_t *l )
anims[max] = tx;
max++;
}
- else Host_Error( "Mod_LoadTextures: bad detail texture %s\n", tx->name );
+ else MsgDev( D_ERROR, "Mod_LoadTextures: bad detail texture %s\n", tx->name );
for( j = i + 1; j < loadmodel->numtextures; j++ )
{
tx2 = loadmodel->textures[j];
- if( !tx2 || tx2->name[0] != '-' )
- continue;
- if( Q_strcmp( tx2->name + 2, tx->name + 2 ))
+ if( tx2->name[0] != '-' || Q_strcmp( tx2->name + 2, tx->name + 2 ))
continue;
num = tx2->name[1];
+
if( num >= '0' && num <= '9' )
{
num -= '0';
anims[num] = tx2;
- if( num+1 > max )
+ if( num + 1 > max )
max = num + 1;
}
- else Host_Error( "Mod_LoadTextures: bad detail texture %s\n", tx->name );
+ else MsgDev( D_ERROR, "Mod_LoadTextures: bad detail texture %s\n", tx->name );
}
// link them all together
for( j = 0; j < max; j++ )
{
tx2 = anims[j];
- if( !tx2 ) Host_Error( "Mod_LoadTextures: missing frame %i of %s\n", j, tx->name );
+
+ if( !tx2 )
+ {
+ MsgDev( D_ERROR, "Mod_LoadTextures: missing frame %i of %s\n", j, tx->name );
+ tx->anim_total = 0;
+ break;
+ }
+
tx2->anim_total = -( max * ANIM_CYCLE ); // to differentiate from animations
tx2->anim_min = j * ANIM_CYCLE;
tx2->anim_max = (j + 1) * ANIM_CYCLE;
@@ -1074,14 +1188,49 @@ static void Mod_LoadTextures( const dlump_t *l )
Mod_LoadTexInfo
=================
*/
-static void Mod_LoadTexInfo( const dlump_t *l )
+static mfaceinfo_t *Mod_LoadFaceInfo( const dlump_t *l, int *numfaceinfo )
+{
+ dfaceinfo_t *in;
+ mfaceinfo_t *out, *faceinfo;
+ int i, count;
+
+ in = (void *)(mod_base + l->fileofs);
+ if( l->filelen % sizeof( *in ))
+ Host_Error( "Mod_LoadFaceInfo: funny lump size in %s\n", loadmodel->name );
+
+ count = l->filelen / sizeof( *in );
+ faceinfo = out = Mem_Alloc( loadmodel->mempool, count * sizeof( *out ));
+
+ for( i = 0; i < count; i++, in++, out++ )
+ {
+ Q_strncpy( out->landname, in->landname, sizeof( out->landname ));
+ out->texture_step = in->texture_step;
+ out->max_extent = in->max_extent;
+ out->groupid = in->groupid;
+ }
+
+ *numfaceinfo = count;
+
+ return faceinfo;
+}
+
+/*
+=================
+Mod_LoadTexInfo
+=================
+*/
+static void Mod_LoadTexInfo( const dlump_t *l, dextrahdr_t *extrahdr )
{
dtexinfo_t *in;
mtexinfo_t *out;
int miptex;
+ mfaceinfo_t *fi = NULL;
+ int fi_count;
int i, j, count;
- float len1, len2;
-
+
+ if( extrahdr != NULL )
+ fi = Mod_LoadFaceInfo( &extrahdr->lumps[LUMP_FACEINFO], &fi_count );
+
in = (void *)(mod_base + l->fileofs);
if( l->filelen % sizeof( *in ))
Host_Error( "Mod_LoadTexInfo: funny lump size in %s\n", loadmodel->name );
@@ -1097,22 +1246,16 @@ static void Mod_LoadTexInfo( const dlump_t *l )
for( j = 0; j < 8; j++ )
out->vecs[0][j] = in->vecs[0][j];
- len1 = VectorLength( out->vecs[0] );
- len2 = VectorLength( out->vecs[1] );
- len1 = ( len1 + len2 ) / 2;
-
- // g-cont: can use this info for GL_TEXTURE_LOAD_BIAS_EXT ?
- if( len1 < 0.32f ) out->mipadjust = 4;
- else if( len1 < 0.49f ) out->mipadjust = 3;
- else if( len1 < 0.99f ) out->mipadjust = 2;
- else out->mipadjust = 1;
-
miptex = in->miptex;
if( miptex < 0 || miptex > loadmodel->numtextures )
Host_Error( "Mod_LoadTexInfo: bad miptex number in '%s'\n", loadmodel->name );
out->texture = loadmodel->textures[miptex];
out->flags = in->flags;
+
+ // make sure what faceinfo is really exist
+ if( extrahdr != NULL && in->faceinfo != -1 && in->faceinfo < fi_count )
+ out->faceinfo = &fi[in->faceinfo];
}
}
@@ -1182,10 +1325,35 @@ static void Mod_LoadDeluxemap( void )
/*
=================
+Mod_LoadLightVecs
+=================
+*/
+static void Mod_LoadLightVecs( const dlump_t *l )
+{
+ byte *in;
+
+ in = (void *)(mod_base + l->fileofs);
+ world.vecdatasize = l->filelen;
+
+ if( world.vecdatasize != world.litdatasize )
+ {
+ MsgDev( D_ERROR, "Mod_LoadLightVecs: has mismatched size (%i should be %i)\n", world.vecdatasize, world.litdatasize );
+ world.deluxedata = NULL;
+ world.vecdatasize = 0;
+ return;
+ }
+
+ world.deluxedata = Mem_Alloc( loadmodel->mempool, world.vecdatasize );
+ memcpy( world.deluxedata, in, world.vecdatasize );
+ MsgDev( D_INFO, "Mod_LoadLightVecs: loaded\n" );
+}
+
+/*
+=================
Mod_LoadLighting
=================
*/
-static void Mod_LoadLighting( const dlump_t *l )
+static void Mod_LoadLighting( const dlump_t *l, dextrahdr_t *extrahdr )
{
byte d, *in;
color24 *out;
@@ -1229,8 +1397,20 @@ static void Mod_LoadLighting( const dlump_t *l )
break;
}
+ if( !world.loading ) return; // only world can have deluxedata (FIXME: what about quake models?)
+
+ // not supposed to be load ?
+ if( !FBitSet( host.features, ENGINE_LOAD_DELUXEDATA ))
+ {
+ world.deluxedata = NULL;
+ world.vecdatasize = 0;
+ return;
+ }
+
// try to loading deluxemap too
- Mod_LoadDeluxemap ();
+ if( extrahdr != NULL )
+ Mod_LoadLightVecs( &extrahdr->lumps[LUMP_LIGHTVECS] );
+ else Mod_LoadDeluxemap (); // old method
}
/*
@@ -1244,10 +1424,11 @@ static void Mod_CalcSurfaceExtents( msurface_t *surf )
{
float mins[2], maxs[2], val;
int bmins[2], bmaxs[2];
- int i, j, e;
+ int i, j, e, sample_size;
mtexinfo_t *
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment