Created
December 14, 2016 18:09
-
-
Save mittorn/1d3c1ad2593439f7341ec350f27aae63 to your computer and use it in GitHub Desktop.
This file has been truncated, but you can view the full file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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( &s[i] ); } | |
-void AMP_FreeAll() { int i; for( i = 0; i < CAMPS; i++ ) AMP_Free( &s[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 = &s[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 = &_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