Jump to content
The Dark Mod Forums

Search the Community

Showing results for '/tags/forums/texture/'.

  • Search By Tags

    Type tags separated by commas.
  • Search By Author

Content Type


Forums

  • General Discussion
    • News & Announcements
    • The Dark Mod
    • Fan Missions
    • Off-Topic
  • Feedback and Support
    • TDM Tech Support
    • DarkRadiant Feedback and Development
    • I want to Help
  • Editing and Design
    • TDM Editors Guild
    • Art Assets
    • Music & SFX

Find results in...

Find results that contain...


Date Created

  • Start

    End


Last Updated

  • Start

    End


Filter by number of...

Joined

  • Start

    End


Group


AIM


MSN


Website URL


ICQ


Yahoo


Jabber


Skype


Location


Interests

  1. I don't recall a system for noise masking. It sounds like it'd be a good idea, but when you get into the details you realize it'd be complicated to implement. It's not only noise that that goes into it, I think. E.g., a high register can cut through even a loud but low register rumble. And it's not like the .wav file even has data on the register of what it's playing. So either you have to add meta-data (which is insane), or you have to have a system to literally check pitch on the .wav data and paramaterize it in time to know when it's going to cut through what other parameters from other sounds. For that matter, it doesn't even have the data on the loudness either, so you'd have to get that off the file too and time the peaks with the "simultaneous" moment at arbitrary places in every other sound file correctly. And then position is going to matter independently for each AI. So it's not like you can have one computation that works the same for all AI. You'd have to compute the masking level for each one, and then you get into the expense you're mentioning. I know there was a long discussion about it in the internal forums, and probably on the public subforums too, but it's been so long ago now I can't even remember the gist of them. Anyway the main issue is I don't know if you'll find a champion that wants to work on it. But if you're really curious to see how it might work, you could always try your hand at coding & implementing it. Nothing beats a good demo to test an idea in action. And there's no better way to learn how to code than a little project like that. I always encourage people to try to implement an idea they have, whether or not it may be a good idea, just because it shows the power of an open source game. We fans can try anything we want and see if it works!
  2. I'm looking for the decal often used on the floor in front of secret doors. I've searched though textures/darkmod/decals and many other locations, but can't find it. Does anyone know where it's located or can post the texture? Thanks.
  3. Sign out of TDM forums, close browser, re-open later, "Huh...I'm still signed in?"

    1. Show previous comments  1 more
    2. Bikerdude

      Bikerdude

      Its a cookie thats keeping you logged in.

    3. Sotha

      Sotha

      Another visitor! Stay a while; stay forever!

    4. Tarhiel

      Tarhiel

      We´re like a Shalebridge Cradle: what comes in must never leave :)

  4. Hmm... The texture name and path are the same... See if there is another material that has "textures/darkmod/sfx/thunderpuff" as it's name and see if it has a sort value. If it is set to sort last ( so that it renders over water ) it might be fighting with itself when the particles are dense. That said, new custom materials should generally have a different name to the texture path since the material will use the texture name as a path location ( a sorta sloppy inheritance design meant to supposedly make things easier for compound materials that reference other materials ) I'll look into it tonight if I get a chance.
  5. I see what you mean now, sorry my brain can be slow sometimes: Instead of modifying the existing skins and textures, I should indeed define a new skin intended for colorization. That's definitely better if I think about it, thanks for bringing this to my attention. I only have one problem then: I don't want to create a second diffuse texture, only the color map meant to override the colored area. In my custom material, how can I first subtract the shirt color from the original texture to blacken it out? This is to avoid intermediary colors in areas where they blend, so if you colorize your guard to red you don't see purple at a pixel where the color map intensity is 0.5 and blends over the blue in default. I could avoid this is there was a material blend option to change the hue based on my map, instead of applying a new color on top it... I'm not aware of a way to do that though. Any thoughts please? Thanks. The original texture would be grayscale but I should use that keyword for safe measure. Saving the tga / dds as grayscale would have helped to reduce file size, unfortunately some engines have issues with that format so I'll have to check if TDM recognizes non-RGB. For now I can just save it normally and use the existing compression, specular maps don't seem to saved this way either.
  6. That's also an option but it has some drawbacks: The mapper needs to extract and edit the textures in an image editor... we can assume they have Photoshop and know how to change the hue, but many won't bother going that far if it's not a readily available option. Also keep in mind the texture contains multiple colors, for instance you need to edit only the green vest without changing the hue on brown leather, this can be trickier to do... with a texture map it will automatically change only the colored part. It also requires redistributing the textures and increasing the FM size, probably not by a lot but if you want to keep it light and minimize extra assets it can matter. With this approach you can have the same character in multiple colors without loading a new texture in memory for each, a commoner with a red tunic and one with a blue one would use the same textures in memory. Plus you can freely change and preview the color in DR, much easier than having to edit textures. I'll probably try this just for the city watch as a test first then share some screenshots: Shouldn't be too much work for just one character to see what results I get. If that works and is considered okay, I'll do it just for builders nobles and some commoners... should only be done for those few characters that have a colored fabric vest.
  7. DarkRadiant 3.4.0 is ready for download. What's new: Feature: Allow Layers to be arranged into a Tree Fixed: Readable Editor displays "shader not found" in view Fixed: Undoing snap to grid with prefabs causes crash Fixed: Include doc in building instructions Fixed: Decal textures causes DR to crash - (textures/darkmod/decals/dirt/long_drip_pattern01) Fixed: Skin chooser: double click on materials list closes window Fixed: Selecting and deselecting a filtered child brush through layers leaves the brush selected Fixed: Material editor re-sorts stages on pasting image map resulting in wrong material stages list and wrong selected stage Fixed: Crash on start if engine path is choosen (Doom 3) Feature: Layers can now be arranged to form a hierarchy Windows and Mac Downloads are available on Github: https://github.com/codereader/DarkRadiant/releases/tag/3.4.0 and of course linked from the website https://www.darkradiant.net Thanks to all the awesome people who keep using DarkRadiant to create Fan Missions - they are the main reason for me to keep going. Please report any bugs or feature requests here in these forums, following these guidelines: Bugs (including steps for reproduction) can go directly on the tracker. When unsure about a bug/issue, feel free to ask. If you run into a crash, please record a crashdump: Crashdump Instructions Feature requests should be suggested (and possibly discussed) here in these forums before they may be added to the tracker. The list of changes can be found on the our bugtracker changelog. Have fun mapping!
  8. I'm using the version from kcghost. I just tested and I can't see any difference inside the inventory. On the stats itself it doesn't show the different loot types (still seen in the inventory), but instead gives more info on stealth score. Edit: I see Dragofer made an updated version of his script. I have to check that out. Edit2: That version works: https://forums.thedarkmod.com/applications/core/interface/file/attachment.php?id=21272&key=02755164a3bed10498683771fe9a0453
  9. DarkRadiant 3.5.0 is ready for download. What's new: Feature: More customisable layout, all windows and panes can be dragged and arranged Layouts like Embedded, Regular and Splitpane are superseded and have been removed Tweak: The LayerControlPanel's tooltip popup is now less annoying Tweak: Clarify distinction between Shadow render mode and other render modes Fixed: Show/hide Light Volumes for combined entities inconsistent Fixed: Currently applied particleDef not selected in Particle Selector Fixed: Layer visibility checkbox not reacting to double-clicks Fixed: Cannot toggle visibility of layers in Linux Fixed: Drag-and-dropping layers is not working in Linux Feature: Customisable Layout (click to see the videos) Windows and Mac Downloads are available on Github: https://github.com/codereader/DarkRadiant/releases/tag/3.5.0 and of course linked from the website https://www.darkradiant.net Thanks to all the awesome people who keep using DarkRadiant to create Fan Missions - they are the main reason for me to keep going. Please report any bugs or feature requests here in these forums, following these guidelines: Bugs (including steps for reproduction) can go directly on the tracker. When unsure about a bug/issue, feel free to ask. If you run into a crash, please record a crashdump: Crashdump Instructions Feature requests should be suggested (and possibly discussed) here in these forums before they may be added to the tracker. The list of changes can be found on the our bugtracker changelog. Have fun mapping!
  10. @stgatilov, you said, RE string comparisons: I'm unclear if you are referring just to changes you made in 2.11. I think equality testing of two strings (one of which is "text") was working and is a good and expected capability, and should be supported. Including against an empty string. I noticed in your bug activities that you did remove some comparisons with ... == "". I see you also removed string concatenation with "\". No problem, but does that mean multiline macros are no longer a thing? (If so, I'll need to change some examples) BTW, the series so far hasn't really tried to cover the 2.11 changes, since I figured it's a work in progress. But since you did a great deal of GUI work in July, perhaps it's stable enough to try to consider it. I see the logs listed in bugtracker, but don't have access to the private forum threads mentioned there: https://forums.thedarkmod.com/index.php?/topic/20526-gui-refactoring/&do=findComment&comment=477179 https://forums.thedarkmod.com/index.php?/topic/21535-order-of-evaluation-in-expressions-materials-and-gui/ (Nor do I have SVN currently set up on my newer machine, for changelogs from there.) Any place else I should look?
  11. I looked but didn't see this video posted in these forums. It's pretty cool.
  12. Announcing the Release of 'Requiem' for The Dark Mod! Download Download the latest version of the Dark Mod here: http://www.thedarkmo...wnload-the-mod/ Download the mission here: Mediafire: http://www.mediafire...u89/requiem.pk4 Southquarter: http://www.southquar...ons/requiem.pk4 Fidcal.com: http://www.fidcal.co...ons/requiem.pk4 Create a folder in your Dark Mod install with the path "darkmod/fms/requiem" and place the downloaded .pk4 file inside. When you load up The Dark Mod, the mission will appear on the "New Mission" page. Requiem can also be found directly using the in-game loader. Gameplay Notes While this mission is playable in TDM 1.8, for an optimal experience please download and play in TDM 2.0 (or higher). Most inventory items in the game can be dropped, so no need to carry them around after they are no longer of any use. Note that If you use noclip or other console commands while playing, there is a good chance that you will break the intended flow of gameplay. Credits Mapping and Readables: Gelo R. Fleisher Voice Acting: Goldwell Additional scripting: Obsttorte Additional textures and assets: Flanders, Sotha, Grayman, Springheel, Bikerdude, Obsttorte Additional map optimizations: Bikerdude Testers: Bikerdude, Obsttorte, Gnartsch, AluminumHaste, Baal, nbohr1more, PPoe Custom Soundtrack: Leonardo Badinella - http://leonardobadinella.com/ Additional Music: Lee Rosevere - http://freemusicarch...c/Lee_Rosevere/ Marianne Lihannah - http://www.funeralsinger.net/ Vox Vulgaris - http://www.last.fm/music/Vox+Vulgaris/ A note from the author Hi all. While I've been involved in indie game development for a while now, I'm first and foremost a writer. My most recent project has been a novella that tries to capture the visual feel and tone of the Thief series (you can find the link below). As I was writing, I found myself playing a lot of Thief and Dark Mod fan missions, and got to thinking that maybe I wanted to make one myself, as a companion piece to the book. When I finished up writing, I had a bit of down time and decided to take the plunge. Having never done any serious mapping before, my plan was to make a small mission that I could bang out in a month or two and call it a day. Well, as sometimes happens, the project got a little bit bigger than I had planned. Ten months, and lots of elbow grease later, Requiem is finally ready for you to play. I'd like to thank everyone who helped pitch in to help make Requiem come alive, from those who took the time to answer my many questions on the forums to those who actively contributed to the FM. I especially want to thank Bikerdude who served as my mapping mentor, and Obsttorte whose clever scripts really turned what was in my head into the game that you are playing. Above all, I want to thank you for downloading and playing Requiem; I hope you enjoy it. Links of Interest Author's Blog: http://gfleisher.blogspot.com/ Companion Novella (Amazon): http://www.amazon.co...k/dp/B00BYEW02M Companion Novella (Smashwords): http://www.smashword...oks/view/298956
  13. It wasn't a "sacrifice", it was a deliberate decision. People wanted the game to be as close as possible to the original, including pixelated graphics. If you ask me, the former version based on the Unity engine looked and felt better. But, hey... I guess I'm not the right person to judge that, as I never played the original, and always found that the art style of System Shock 2 is much better anyway. This also illustrates the issue with community funded games: Too many cooks spoil the broth. In game design, you need freedom, not thousands of people who want you to do this and this and that. Just take a look at the Steam forums and see how all those wimps complain again about everything. Hopeless.
  14. Ok heres the plan move the arb backend into the exp renderer since it allready has a switch to select either rendering method. Next move the math functions to idlib and the texture creation tools to image_load.cpp. Then grab the shaders from the BFG cache files and we can start improving on the code from there. Current code here -> /* =========================================================================== Doom 3 GPL Source Code Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). Doom 3 Source Code is free software: you can 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. Doom 3 Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>. In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. =========================================================================== */ #include "../idlib/precompiled.h" #pragma hdrstop #include "tr_local.h" #include "../sys/win32/win_local.h" /* strictly experimental / research codepaths !!!if we use front facing occluders, we can portal flow from light centers try depth_component_16 rendering do we care about portals from light perspective? back / front face issues. how do we do weapon depth hacks with shadow buffers? distort their world space vertexes instead of offsetting their depth? jittering off the side of a projection will give wrong shadows really huge lights, like sunlight, are going to be problematic with fixed projections we could tile the projections and let the auto-resize cut them down as necessary It sucks that depth buffers are non-linear, because the bias and compares change with distance polygon offset factor causes occasional texture holes from highly angled textures */ static bool initialized; static int lightBufferSize = 1024; static int maxLightBufferSize = 1024; static float lightBufferSizeFraction = 0.5; static int viewBufferSize = 1024; static int viewBufferHeight = 768; static int maxViewBufferSize = 1024; static float viewBufferSizeFraction = 0.5; static float viewBufferHeightFraction = 0.5; static bool nativeViewBuffer = false; // true if viewBufferSize is the viewport width static HPBUFFERARB floatPbuffer; static HDC floatPbufferDC; static idImage *floatPbufferImage; static HPBUFFERARB floatPbuffer2; static HDC floatPbuffer2DC; static idImage *floatPbuffer2Image; static HPBUFFERARB floatPbufferQuarter; static HDC floatPbufferQuarterDC; static idImage *floatPbufferQuarterImage; static HGLRC floatContext; static HPBUFFERARB shadowPbuffer; static HDC shadowPbufferDC; static HPBUFFERARB viewPbuffer; static HDC viewPbufferDC; static idImage *shadowImage[3]; static idImage *viewDepthImage; static idImage *viewAlphaImage; static idImage *viewShadowImage; static idImage *jitterImage16; static idImage *jitterImage4; static idImage *jitterImage1; static idImage *random256Image; static int shadowVertexProgram; static int shadowFragmentProgram16; static int shadowFragmentProgram4; static int shadowFragmentProgram1; static int shadowFragmentProgram0; static int screenSpaceShadowVertexProgram; static int screenSpaceShadowFragmentProgram16; static int screenSpaceShadowFragmentProgram4; static int screenSpaceShadowFragmentProgram1; static int screenSpaceShadowFragmentProgram0; static int depthMidpointVertexProgram; static int depthMidpointFragmentProgram; static int shadowResampleVertexProgram; static int shadowResampleFragmentProgram; static int gammaDitherVertexProgram; static int gammaDitherFragmentProgram; static int downSampleVertexProgram; static int downSampleFragmentProgram; static int bloomVertexProgram; static int bloomFragmentProgram; static float viewLightAxialSize; idCVar r_sb_lightResolution( "r_sb_lightResolution", "1024", CVAR_RENDERER | CVAR_INTEGER, "Pixel dimensions for each shadow buffer, 64 - 2048" ); idCVar r_sb_viewResolution( "r_sb_viewResolution", "1024", CVAR_RENDERER | CVAR_INTEGER, "Width of screen space shadow sampling" ); idCVar r_sb_noShadows( "r_sb_noShadows", "0", CVAR_RENDERER | CVAR_BOOL, "don't draw any occluders" ); idCVar r_sb_usePbuffer( "r_sb_usePbuffer", "1", CVAR_RENDERER | CVAR_BOOL, "draw offscreen" ); idCVar r_sb_jitterScale( "r_sb_jitterScale", "0.006", CVAR_RENDERER | CVAR_FLOAT, "scale factor for jitter offset" ); idCVar r_sb_biasScale( "r_sb_biasScale", "0.0001", CVAR_RENDERER | CVAR_FLOAT, "scale factor for jitter bias" ); idCVar r_sb_samples( "r_sb_samples", "4", CVAR_RENDERER | CVAR_INTEGER, "0, 1, 4, or 16" ); idCVar r_sb_randomize( "r_sb_randomize", "1", CVAR_RENDERER | CVAR_BOOL, "randomly offset jitter texture each draw" ); // polyOfsFactor causes holes in low res images idCVar r_sb_polyOfsFactor( "r_sb_polyOfsFactor", "2", CVAR_RENDERER | CVAR_FLOAT, "polygonOffset factor for drawing shadow buffer" ); idCVar r_sb_polyOfsUnits( "r_sb_polyOfsUnits", "3000", CVAR_RENDERER | CVAR_FLOAT, "polygonOffset units for drawing shadow buffer" ); idCVar r_sb_occluderFacing( "r_sb_occluderFacing", "0", CVAR_RENDERER | CVAR_INTEGER, "0 = front faces, 1 = back faces, 2 = midway between" ); // r_sb_randomizeBufferOrientation? idCVar r_sb_frustomFOV( "r_sb_frustomFOV", "92", CVAR_RENDERER | CVAR_FLOAT, "oversize FOV for point light side matching" ); idCVar r_sb_showFrustumPixels( "r_sb_showFrustumPixels", "0", CVAR_RENDERER | CVAR_BOOL, "color the pixels contained in the frustum" ); idCVar r_sb_singleSide( "r_sb_singleSide", "-1", CVAR_RENDERER | CVAR_INTEGER, "only draw a single side (0-5) of point lights" ); idCVar r_sb_useCulling( "r_sb_useCulling", "1", CVAR_RENDERER | CVAR_BOOL, "cull geometry to individual side frustums" ); idCVar r_sb_linearFilter( "r_sb_linearFilter", "1", CVAR_RENDERER | CVAR_BOOL, "use GL_LINEAR instead of GL_NEAREST on shadow maps" ); idCVar r_sb_screenSpaceShadow( "r_sb_screenSpaceShadow", "1", CVAR_RENDERER | CVAR_BOOL, "build shadows in screen space instead of on surfaces" ); idCVar r_hdr_useFloats( "r_hdr_useFloats", "0", CVAR_RENDERER | CVAR_BOOL, "use a floating point rendering buffer" ); idCVar r_hdr_exposure( "r_hdr_exposure", "1.0", CVAR_RENDERER | CVAR_FLOAT, "maximum light scale" ); idCVar r_hdr_bloomFraction( "r_hdr_bloomFraction", "0.1", CVAR_RENDERER | CVAR_FLOAT, "fraction to smear across neighbors" ); idCVar r_hdr_gamma( "r_hdr_gamma", "1", CVAR_RENDERER | CVAR_FLOAT, "monitor gamma power" ); idCVar r_hdr_monitorDither( "r_hdr_monitorDither", "0.01", CVAR_RENDERER | CVAR_FLOAT, "random dither in monitor space" ); // from world space to light origin, looking down the X axis static float unflippedLightMatrix[16]; // from world space to OpenGL view space, looking down the negative Z axis static float lightMatrix[16]; // from OpenGL view space to OpenGL NDC ( -1 : 1 in XYZ ) static float lightProjectionMatrix[16]; typedef struct { const char *name; int num; } wglString_t; wglString_t wglString[] = { { "WGL_NUMBER_PIXEL_FORMATS_ARB", 0x2000 }, { "WGL_DRAW_TO_WINDOW_ARB", 0x2001 }, { "WGL_DRAW_TO_BITMAP_ARB", 0x2002 }, { "WGL_ACCELERATION_ARB", 0x2003 }, { "WGL_NEED_PALETTE_ARB", 0x2004 }, { "WGL_NEED_SYSTEM_PALETTE_ARB", 0x2005 }, { "WGL_SWAP_LAYER_BUFFERS_ARB", 0x2006 }, { "WGL_SWAP_METHOD_ARB", 0x2007 }, { "WGL_NUMBER_OVERLAYS_ARB", 0x2008 }, { "WGL_NUMBER_UNDERLAYS_ARB", 0x2009 }, { "WGL_TRANSPARENT_ARB", 0x200A }, { "WGL_TRANSPARENT_RED_VALUE_ARB", 0x2037 }, { "WGL_TRANSPARENT_GREEN_VALUE_ARB", 0x2038 }, { "WGL_TRANSPARENT_BLUE_VALUE_ARB", 0x2039 }, { "WGL_TRANSPARENT_ALPHA_VALUE_ARB", 0x203A }, { "WGL_TRANSPARENT_INDEX_VALUE_ARB", 0x203B }, { "WGL_SHARE_DEPTH_ARB", 0x200C }, { "WGL_SHARE_STENCIL_ARB", 0x200D }, { "WGL_SHARE_ACCUM_ARB", 0x200E }, { "WGL_SUPPORT_GDI_ARB", 0x200F }, { "WGL_SUPPORT_OPENGL_ARB", 0x2010 }, { "WGL_DOUBLE_BUFFER_ARB", 0x2011 }, { "WGL_STEREO_ARB", 0x2012 }, { "WGL_PIXEL_TYPE_ARB", 0x2013 }, { "WGL_COLOR_BITS_ARB", 0x2014 }, { "WGL_RED_BITS_ARB", 0x2015 }, { "WGL_RED_SHIFT_ARB", 0x2016 }, { "WGL_GREEN_BITS_ARB", 0x2017 }, { "WGL_GREEN_SHIFT_ARB", 0x2018 }, { "WGL_BLUE_BITS_ARB", 0x2019 }, { "WGL_BLUE_SHIFT_ARB", 0x201A }, { "WGL_ALPHA_BITS_ARB", 0x201B }, { "WGL_ALPHA_SHIFT_ARB", 0x201C }, { "WGL_ACCUM_BITS_ARB", 0x201D }, { "WGL_ACCUM_RED_BITS_ARB", 0x201E }, { "WGL_ACCUM_GREEN_BITS_ARB", 0x201F }, { "WGL_ACCUM_BLUE_BITS_ARB", 0x2020 }, { "WGL_ACCUM_ALPHA_BITS_ARB", 0x2021 }, { "WGL_DEPTH_BITS_ARB", 0x2022 }, { "WGL_STENCIL_BITS_ARB", 0x2023 }, { "WGL_AUX_BUFFERS_ARB", 0x2024 }, { "WGL_NO_ACCELERATION_ARB", 0x2025 }, { "WGL_GENERIC_ACCELERATION_ARB", 0x2026 }, { "WGL_FULL_ACCELERATION_ARB", 0x2027 }, { "WGL_SWAP_EXCHANGE_ARB", 0x2028 }, { "WGL_SWAP_COPY_ARB", 0x2029 }, { "WGL_SWAP_UNDEFINED_ARB", 0x202A }, { "WGL_TYPE_RGBA_ARB", 0x202B }, { "WGL_TYPE_COLORINDEX_ARB", 0x202C }, }; static const int NUM_WGL_STRINGS = sizeof( wglString ) / sizeof( wglString[0] ); static void R_CheckWglErrors( void ) { int err = GetLastError(); char *name; err &= 0xffff; switch( err ) { case 13: name = "ERROR_INVALID_DATA"; break; case 6: name = "ERROR_INVALID_HANDLE"; break; case 4317: name = "ERROR_INVALID_OPERATION"; break; default: name = va( "code %i", err ); break; } common->Printf( "GetLastError: %s\n", name ); } static void R_MakeCurrent( HDC dc, HGLRC context, HPBUFFERARB pbuffer ) { if( pbuffer ) { if( !wglReleaseTexImageARB( pbuffer, WGL_FRONT_LEFT_ARB ) ) { R_CheckWglErrors(); common->Error( "wglReleaseTexImageARB failed" ); } } if( !wglMakeCurrent( dc, context ) ) { R_CheckWglErrors(); common->FatalError( "wglMakeCurrent failed" ); } } static void R_BindTexImage( HPBUFFERARB pbuffer ) { if( !wglReleaseTexImageARB( pbuffer, WGL_FRONT_LEFT_ARB ) ) { R_CheckWglErrors(); common->Error( "wglReleaseTexImageARB failed" ); } if( !wglBindTexImageARB( pbuffer, WGL_FRONT_LEFT_ARB ) ) { R_CheckWglErrors(); common->Error( "failed wglBindTexImageARB" ); } } static int MakePowerOfTwo(int num) { int pot; for (pot = 1; pot < num; pot <<= 1) {} return pot; } /* ==================== RB_CreateBloomTable ==================== */ static const int BLOOM_RADIUS = 8; static void RB_CreateBloomTable( void ) { float bloom[BLOOM_RADIUS]; float total = 0; // gaussian float stdDev = 2.0; for( int i = 0; i < BLOOM_RADIUS; i++ ) { float f = ( float )i / stdDev; bloom[i] = exp( -0.5 * f * f ); total += bloom[i]; } total = ( total - bloom[0] ) * 2 + bloom[0]; // normalize to 1.0 contribution, so a full row or column will equal 1.0 for( int i = 0; i < BLOOM_RADIUS; i++ ) { bloom[i] *= 1.0 / total; common->Printf( "PARAM bloom%i = { %f };\n", i, bloom[i] ); } } /* ==================== GL_SelectTextureNoClient ==================== */ static void GL_SelectTextureNoClient( int unit ) { backEnd.glState.currenttmu = unit; glActiveTextureARB( GL_TEXTURE0_ARB + unit ); } /* ================ R_CreateShadowBufferImage ================ */ static void R_CreateShadowBufferImage( idImage *image ) { byte *data = ( byte * )Mem_Alloc( lightBufferSize * lightBufferSize ); memset( data, 0, lightBufferSize * lightBufferSize ); image->GenerateImage( ( byte * )data, 4, 4, TF_LINEAR, false, TR_CLAMP_TO_BORDER, TD_HIGH_QUALITY ); // now reset it to a shadow depth image GL_CheckErrors(); image->uploadWidth = image->uploadHeight = lightBufferSize; glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24_ARB, lightBufferSize, lightBufferSize, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, data ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL ); // explicit zero depth border float color[4]; color[0] = color[1] = color[2] = color[3] = 0; glTexParameterfv( GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, color ); GL_CheckErrors(); Mem_Free( data ); } /* ================ R_CreateViewAlphaImage ================ */ static void R_CreateViewAlphaImage( idImage *image ) { int c = viewBufferSize * viewBufferSize * 4; byte *data = ( byte * )Mem_Alloc( c ); // don't let it pick an intensity format for( int i = 0; i < c; i++ ) { data[i] = i; } memset( data, 0, viewBufferSize * viewBufferSize ); image->GenerateImage( ( byte * )data, viewBufferSize, viewBufferSize, TF_LINEAR, false, TR_CLAMP, TD_HIGH_QUALITY ); } /* ================ R_CreateStubImage ================ */ static void R_CreateStubImage( idImage *image ) { float data[3][4][4]; // generate the texture number glGenTextures( 1, &image->texnum ); glBindTexture( GL_TEXTURE_RECTANGLE_NV, image->texnum ); memset( data, 0, sizeof( data ) ); glTexImage2D( GL_TEXTURE_RECTANGLE_NV, 0, GL_FLOAT_RGBA16_NV, 4, 3, 0, GL_RGBA, GL_FLOAT, &data ); } /* ================ R_CreateJitterImage16 ================ */ const static int JITTER_SIZE = 128; static void R_CreateJitterImage16( idImage *image ) { byte data[JITTER_SIZE][JITTER_SIZE * 16][4]; for( int i = 0; i < JITTER_SIZE; i++ ) { for( int s = 0; s < 16; s++ ) { int sOfs = 64 * ( s & 3 ); int tOfs = 64 * ( ( s >> 2 ) & 3 ); for( int j = 0; j < JITTER_SIZE; j++ ) { data[i][s * JITTER_SIZE + j][0] = ( rand() & 63 ) | sOfs; data[i][s * JITTER_SIZE + j][1] = ( rand() & 63 ) | tOfs; data[i][s * JITTER_SIZE + j][2] = rand(); data[i][s * JITTER_SIZE + j][3] = 0; } } } image->GenerateImage( ( byte * )data, JITTER_SIZE * 16, JITTER_SIZE, TF_NEAREST, false, TR_REPEAT, TD_HIGH_QUALITY ); } /* ================ R_CreateJitterImage4 ================ */ static void R_CreateJitterImage4( idImage *image ) { byte data[JITTER_SIZE][JITTER_SIZE * 4][4]; for( int i = 0; i < JITTER_SIZE; i++ ) { for( int s = 0; s < 4; s++ ) { int sOfs = 128 * ( s & 1 ); int tOfs = 128 * ( ( s >> 1 ) & 1 ); for( int j = 0; j < JITTER_SIZE; j++ ) { data[i][s * JITTER_SIZE + j][0] = ( rand() & 127 ) | sOfs; data[i][s * JITTER_SIZE + j][1] = ( rand() & 127 ) | tOfs; data[i][s * JITTER_SIZE + j][2] = rand(); data[i][s * JITTER_SIZE + j][3] = 0; } } } image->GenerateImage( ( byte * )data, JITTER_SIZE * 4, JITTER_SIZE, TF_NEAREST, false, TR_REPEAT, TD_HIGH_QUALITY ); } /* ================ R_CreateJitterImage1 ================ */ static void R_CreateJitterImage1( idImage *image ) { byte data[JITTER_SIZE][JITTER_SIZE][4]; for( int i = 0; i < JITTER_SIZE; i++ ) { for( int j = 0; j < JITTER_SIZE; j++ ) { data[i][j][0] = rand(); data[i][j][1] = rand(); data[i][j][2] = rand(); data[i][j][3] = 0; } } image->GenerateImage( ( byte * )data, JITTER_SIZE, JITTER_SIZE, TF_NEAREST, false, TR_REPEAT, TD_HIGH_QUALITY ); } /* ================ R_CreateRandom256Image ================ */ static void R_CreateRandom256Image( idImage *image ) { byte data[256][256][4]; for( int i = 0; i < 256; i++ ) { for( int j = 0; j < 256; j++ ) { data[i][j][0] = rand(); data[i][j][1] = rand(); data[i][j][2] = rand(); data[i][j][3] = rand(); } } image->GenerateImage( ( byte * )data, 256, 256, TF_NEAREST, false, TR_REPEAT, TD_HIGH_QUALITY ); } /* ================== R_PrintPixelFormat ================== */ void R_PrintPixelFormat( int pixelFormat ) { int res; int iAttribute; int iValue; common->Printf( "----- pixelFormat %i -----\n", pixelFormat ); for( int i = 1; i < NUM_WGL_STRINGS; i++ ) { iAttribute = wglString[i].num; res = wglGetPixelFormatAttribivARB( win32.hDC, pixelFormat, 0, 1, &iAttribute, &iValue ); if( res && iValue ) { common->Printf( "%s : %i\n", wglString[i].name, iValue ); } } } /* ================== R_Backend_Allocate ================== */ void R_Backend_Allocate( void ) { // find a pixel format for our floating point pbuffer int iAttributes[NUM_WGL_STRINGS * 2], *atr_p; FLOAT fAttributes[] = { 0, 0 }; UINT numFormats; int pixelformats[1024]; int ret; int pbiAttributes[] = { 0, 0 }; initialized = true; // // allocate the floating point rendering buffer // atr_p = iAttributes; *atr_p++ = WGL_DRAW_TO_PBUFFER_ARB; *atr_p++ = TRUE; *atr_p++ = WGL_FLOAT_COMPONENTS_NV; *atr_p++ = TRUE; *atr_p++ = WGL_BIND_TO_TEXTURE_RECTANGLE_FLOAT_RGBA_NV; *atr_p++ = TRUE; *atr_p++ = WGL_DEPTH_BITS_ARB; *atr_p++ = 24; *atr_p++ = WGL_STENCIL_BITS_ARB; *atr_p++ = 8; *atr_p++ = 0; *atr_p++ = 0; ret = wglChoosePixelFormatARB( win32.hDC, iAttributes, fAttributes, sizeof( pixelformats ) / sizeof( pixelformats[0] ), pixelformats, &numFormats ); common->Printf( "\nfloatPbuffer:\n" ); R_PrintPixelFormat( pixelformats[0] ); // allocate a pbuffer with this pixel format int pbiAttributesTexture[] = { WGL_TEXTURE_FORMAT_ARB, WGL_TEXTURE_FLOAT_RGBA_NV, WGL_TEXTURE_TARGET_ARB, WGL_TEXTURE_RECTANGLE_NV, // WGL_TEXTURE_2D_ARB, 0, 0 }; floatPbuffer = wglCreatePbufferARB( win32.hDC, pixelformats[0], glConfig.vidWidth, glConfig.vidHeight, pbiAttributesTexture ); if( !floatPbuffer ) { common->Printf( "failed to create floatPbuffer.\n" ); GL_CheckErrors(); } floatPbufferDC = wglGetPbufferDCARB( floatPbuffer ); floatPbufferImage = globalImages->ImageFromFunction( "_floatPbuffer", R_CreateStubImage ); // create a second buffer for ping-pong operations floatPbuffer2 = wglCreatePbufferARB( win32.hDC, pixelformats[0], glConfig.vidWidth, glConfig.vidHeight, pbiAttributesTexture ); if( !floatPbuffer2 ) { common->Printf( "failed to create floatPbuffer.\n" ); GL_CheckErrors(); } floatPbuffer2DC = wglGetPbufferDCARB( floatPbuffer2 ); floatPbuffer2Image = globalImages->ImageFromFunction( "_floatPbuffer2", R_CreateStubImage ); // create a third buffer for down sampling operations floatPbufferQuarter = wglCreatePbufferARB( win32.hDC, pixelformats[0], glConfig.vidWidth / 4, glConfig.vidHeight / 4, pbiAttributesTexture ); if( !floatPbufferQuarter ) { common->Printf( "failed to create floatPbuffer.\n" ); GL_CheckErrors(); } floatPbufferQuarterDC = wglGetPbufferDCARB( floatPbufferQuarter ); floatPbufferQuarterImage = globalImages->ImageFromFunction( "floatPbufferQuarter", R_CreateStubImage ); // create a new GL context for this pixel format and share textures floatContext = wglCreateContext( floatPbufferDC ); if( !floatContext ) { common->Printf( "failed to create context for floatPbufferDC.\n" ); GL_CheckErrors(); } if( !wglShareLists( floatContext, win32.hGLRC ) ) { common->Printf( "failed to share lists.\n" ); } // create a rendering context for this pixel format and share textures // allocate a texture for the rendering //================================================================================= // // allocate the shadow pbuffer // atr_p = iAttributes; *atr_p++ = WGL_DRAW_TO_PBUFFER_ARB; *atr_p++ = TRUE; *atr_p++ = WGL_RED_BITS_ARB; *atr_p++ = 8; *atr_p++ = WGL_GREEN_BITS_ARB; *atr_p++ = 8; *atr_p++ = WGL_BLUE_BITS_ARB; *atr_p++ = 8; *atr_p++ = WGL_ALPHA_BITS_ARB; *atr_p++ = 8; *atr_p++ = WGL_DEPTH_BITS_ARB; *atr_p++ = 24; *atr_p++ = WGL_STENCIL_BITS_ARB; *atr_p++ = 8; *atr_p++ = 0; *atr_p++ = 0; ret = wglChoosePixelFormatARB( win32.hDC, iAttributes, fAttributes, sizeof( pixelformats ) / sizeof( pixelformats[0] ), pixelformats, &numFormats ); common->Printf( "\nshadowPbuffer:\n" ); R_PrintPixelFormat( pixelformats[0] ); pixelformats[0] = win32.pixelformat; // forced to do this by wgl... //----------------------------------- lightBufferSize = maxLightBufferSize; // allocate a pbuffer with this pixel format shadowPbuffer = wglCreatePbufferARB( win32.hDC, pixelformats[0], lightBufferSize, lightBufferSize, pbiAttributes ); // allocate a rendering context for the pbuffer shadowPbufferDC = wglGetPbufferDCARB( shadowPbuffer ); // generate the texture number shadowImage[0] = globalImages->ImageFromFunction( va( "_shadowBuffer%i_0", lightBufferSize ), R_CreateShadowBufferImage ); shadowImage[1] = globalImages->ImageFromFunction( va( "_shadowBuffer%i_1", lightBufferSize ), R_CreateShadowBufferImage ); shadowImage[2] = globalImages->ImageFromFunction( va( "_shadowBuffer%i_2", lightBufferSize ), R_CreateShadowBufferImage ); //----------------------------------- lightBufferSize = maxViewBufferSize; // allocate a pbuffer with this pixel format viewPbuffer = wglCreatePbufferARB( win32.hDC, pixelformats[0], maxViewBufferSize, maxViewBufferSize, pbiAttributes ); // allocate a rendering context for the pbuffer viewPbufferDC = wglGetPbufferDCARB( viewPbuffer ); // create the image space depth buffer for image-space shadow trnasforms viewDepthImage = globalImages->ImageFromFunction( "_viewDepth", R_CreateShadowBufferImage ); // create the image space shadow alpha buffer for subsampling the shadow calculation viewAlphaImage = globalImages->ImageFromFunction( "_viewAlpha", R_CreateViewAlphaImage ); //----------------------------------- // generate the jitter image jitterImage16 = globalImages->ImageFromFunction( "_jitter16", R_CreateJitterImage16 ); jitterImage4 = globalImages->ImageFromFunction( "_jitter4", R_CreateJitterImage4 ); jitterImage1 = globalImages->ImageFromFunction( "_jitter1", R_CreateJitterImage1 ); depthMidpointVertexProgram = R_FindARBProgram( GL_VERTEX_PROGRAM_ARB, "depthMidpoint.vfp" ); depthMidpointFragmentProgram = R_FindARBProgram( GL_FRAGMENT_PROGRAM_ARB, "depthMidpoint.vfp" ); shadowResampleVertexProgram = R_FindARBProgram( GL_VERTEX_PROGRAM_ARB, "shadowResample.vfp" ); shadowResampleFragmentProgram = R_FindARBProgram( GL_FRAGMENT_PROGRAM_ARB, "shadowResample.vfp" ); screenSpaceShadowVertexProgram = R_FindARBProgram( GL_VERTEX_PROGRAM_ARB, "screenSpaceShadow1.vfp" ); screenSpaceShadowFragmentProgram0 = R_FindARBProgram( GL_FRAGMENT_PROGRAM_ARB, "screenSpaceShadow0.vfp" ); screenSpaceShadowFragmentProgram1 = R_FindARBProgram( GL_FRAGMENT_PROGRAM_ARB, "screenSpaceShadow1.vfp" ); screenSpaceShadowFragmentProgram4 = R_FindARBProgram( GL_FRAGMENT_PROGRAM_ARB, "screenSpaceShadow4.vfp" ); screenSpaceShadowFragmentProgram16 = R_FindARBProgram( GL_FRAGMENT_PROGRAM_ARB, "screenSpaceShadow16.vfp" ); shadowVertexProgram = R_FindARBProgram( GL_VERTEX_PROGRAM_ARB, "shadowBufferInteraction1.vfp" ); shadowFragmentProgram0 = R_FindARBProgram( GL_FRAGMENT_PROGRAM_ARB, "shadowBufferInteraction0.vfp" ); shadowFragmentProgram1 = R_FindARBProgram( GL_FRAGMENT_PROGRAM_ARB, "shadowBufferInteraction1.vfp" ); shadowFragmentProgram4 = R_FindARBProgram( GL_FRAGMENT_PROGRAM_ARB, "shadowBufferInteraction4.vfp" ); shadowFragmentProgram16 = R_FindARBProgram( GL_FRAGMENT_PROGRAM_ARB, "shadowBufferInteraction16.vfp" ); gammaDitherVertexProgram = R_FindARBProgram( GL_VERTEX_PROGRAM_ARB, "gammaDither.vfp" ); gammaDitherFragmentProgram = R_FindARBProgram( GL_FRAGMENT_PROGRAM_ARB, "gammaDither.vfp" ); downSampleVertexProgram = R_FindARBProgram( GL_VERTEX_PROGRAM_ARB, "downSample.vfp" ); downSampleFragmentProgram = R_FindARBProgram( GL_FRAGMENT_PROGRAM_ARB, "downSample.vfp" ); bloomVertexProgram = R_FindARBProgram( GL_VERTEX_PROGRAM_ARB, "bloom.vfp" ); bloomFragmentProgram = R_FindARBProgram( GL_FRAGMENT_PROGRAM_ARB, "bloom.vfp" ); random256Image = globalImages->ImageFromFunction( "_random256", R_CreateRandom256Image ); } //=========================================================================================== static const int CULL_RECEIVER = 1; // still draw occluder, but it is out of the view static const int CULL_OCCLUDER_AND_RECEIVER = 2; // the surface doesn't effect the view at all /* ================== RB_Backend_CullInteractions Sets surfaceInteraction_t->cullBits ================== */ void RB_Backend_CullInteractions( viewLight_t *vLight, idPlane frustumPlanes[6] ) { for( idInteraction *inter = vLight->lightDef->firstInteraction; inter; inter = inter->lightNext ) { const idRenderEntityLocal *entityDef = inter->entityDef; if( !entityDef ) { continue; } if( inter->numSurfaces < 1 ) { continue; } int culled = 0; if( r_sb_useCulling.GetBool() ) { // transform light frustum into object space, positive side points outside the light idPlane localPlanes[6]; int plane; for( plane = 0; plane < 6; plane++ ) { R_GlobalPlaneToLocal( entityDef->modelMatrix, frustumPlanes[plane], localPlanes[plane] ); } // cull the entire entity bounding box // has referenceBounds been tightened to the actual model bounds? idVec3 corners[8]; for( int i = 0; i < 8; i++ ) { corners[i][0] = entityDef->referenceBounds[i & 1][0]; corners[i][1] = entityDef->referenceBounds[( i >> 1 ) & 1][1]; corners[i][2] = entityDef->referenceBounds[( i >> 2 ) & 1][2]; } for( plane = 0; plane < 6; plane++ ) { int j; for( j = 0; j < 8; j++ ) { // if a corner is on the negative side (inside) of the frustum, the surface is not culled // by this plane if( corners[j] * localPlanes[plane].ToVec4().ToVec3() + localPlanes[plane][3] < 0 ) { break; } } if( j == 8 ) { break; // all points outside the light } } if( plane < 6 ) { culled = CULL_OCCLUDER_AND_RECEIVER; } } for( int i = 0; i < inter->numSurfaces; i++ ) { surfaceInteraction_t *surfInt = &inter->surfaces[i]; if( !surfInt->ambientTris ) { continue; } surfInt->expCulled = culled; } } } /* ================== RB_Backend_RenderOccluders ================== */ void RB_Backend_RenderOccluders( viewLight_t *vLight ) { for( idInteraction *inter = vLight->lightDef->firstInteraction; inter; inter = inter->lightNext ) { const idRenderEntityLocal *entityDef = inter->entityDef; if( !entityDef ) { continue; } if( inter->numSurfaces < 1 ) { continue; } // no need to check for current on this, because each interaction is always // a different space float matrix[16]; R_MatrixMultiply( inter->entityDef->modelMatrix, lightMatrix, matrix ); glLoadMatrixf( matrix ); // draw each surface for( int i = 0; i < inter->numSurfaces; i++ ) { surfaceInteraction_t *surfInt = &inter->surfaces[i]; if( !surfInt->ambientTris ) { continue; } if( surfInt->shader && !surfInt->shader->SurfaceCastsShadow() ) { continue; } // cull it if( surfInt->expCulled == CULL_OCCLUDER_AND_RECEIVER ) { continue; } // render it const srfTriangles_t *tri = surfInt->ambientTris; if( !tri->ambientCache ) { R_CreateAmbientCache( const_cast<srfTriangles_t *>( tri ), false ); } idDrawVert *ac = ( idDrawVert * )vertexCache.Position( tri->ambientCache ); glVertexPointer( 3, GL_FLOAT, sizeof( idDrawVert ), ac->xyz.ToFloatPtr() ); glTexCoordPointer( 2, GL_FLOAT, sizeof( idDrawVert ), ac->st.ToFloatPtr() ); if( surfInt->shader ) { surfInt->shader->GetEditorImage()->BindTexture(); } RB_DrawElementsWithCounters( tri ); } } } /* ================== RB_RenderShadowBuffer ================== */ void RB_RenderShadowBuffer( viewLight_t *vLight, int side ) { float xmin, xmax, ymin, ymax; float width, height; float zNear; float fov = r_sb_frustomFOV.GetFloat(); // // set up 90 degree projection matrix // zNear = 4; ymax = zNear * tan( fov * idMath::PI / 360.0f ); ymin = -ymax; xmax = zNear * tan( fov * idMath::PI / 360.0f ); xmin = -xmax; width = xmax - xmin; height = ymax - ymin; lightProjectionMatrix[0] = 2 * zNear / width; lightProjectionMatrix[4] = 0; lightProjectionMatrix[8] = 0; lightProjectionMatrix[12] = 0; lightProjectionMatrix[1] = 0; lightProjectionMatrix[5] = 2 * zNear / height; lightProjectionMatrix[9] = 0; lightProjectionMatrix[13] = 0; // this is the far-plane-at-infinity formulation, and // crunches the Z range slightly so w=0 vertexes do not // rasterize right at the wraparound point lightProjectionMatrix[2] = 0; lightProjectionMatrix[6] = 0; lightProjectionMatrix[10] = -0.999f; lightProjectionMatrix[14] = -2.0f * zNear; lightProjectionMatrix[3] = 0; lightProjectionMatrix[7] = 0; lightProjectionMatrix[11] = -1; lightProjectionMatrix[15] = 0; if( r_sb_usePbuffer.GetBool() ) { // set the current openGL drawable to the shadow buffer R_MakeCurrent( shadowPbufferDC, win32.hGLRC, NULL /* !@# shadowPbuffer */ ); } glMatrixMode( GL_PROJECTION ); glLoadMatrixf( lightProjectionMatrix ); glMatrixMode( GL_MODELVIEW ); glViewport( 0, 0, lightBufferSize, lightBufferSize ); glScissor( 0, 0, lightBufferSize, lightBufferSize ); glStencilFunc( GL_ALWAYS, 0, 255 ); glClearColor( 0, 1, 0, 0 ); GL_State( GLS_DEPTHFUNC_LESS | GLS_SRCBLEND_ONE | GLS_DSTBLEND_ZERO ); // make sure depth mask is off before clear glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); // draw all the occluders glColor3f( 1, 1, 1 ); GL_SelectTexture( 0 ); glEnableClientState( GL_TEXTURE_COORD_ARRAY ); backEnd.currentSpace = NULL; static float s_flipMatrix[16] = { // convert from our coordinate system (looking down X) // to OpenGL's coordinate system (looking down -Z) 0, 0, -1, 0, -1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1 }; float viewMatrix[16]; idVec3 vec; idVec3 origin = vLight->lightDef->globalLightOrigin; if( side == -1 ) { // projected light vec = vLight->lightDef->parms.target; vec.Normalize(); viewMatrix[0] = vec[0]; viewMatrix[4] = vec[1]; viewMatrix[8] = vec[2]; vec = vLight->lightDef->parms.right; vec.Normalize(); viewMatrix[1] = -vec[0]; viewMatrix[5] = -vec[1]; viewMatrix[9] = -vec[2]; vec = vLight->lightDef->parms.up; vec.Normalize(); viewMatrix[2] = vec[0]; viewMatrix[6] = vec[1]; viewMatrix[10] = vec[2]; } else { // side of a point light memset( viewMatrix, 0, sizeof( viewMatrix ) ); switch( side ) { case 0: viewMatrix[0] = 1; viewMatrix[9] = 1; viewMatrix[6] = -1; break; case 1: viewMatrix[0] = -1; viewMatrix[9] = -1; viewMatrix[6] = -1; break; case 2: viewMatrix[4] = 1; viewMatrix[1] = -1; viewMatrix[10] = 1; break; case 3: viewMatrix[4] = -1; viewMatrix[1] = -1; viewMatrix[10] = -1; break; case 4: viewMatrix[8] = 1; viewMatrix[1] = -1; viewMatrix[6] = -1; break; case 5: viewMatrix[8] = -1; viewMatrix[1] = 1; viewMatrix[6] = -1; break; } } viewMatrix[12] = -origin[0] * viewMatrix[0] + -origin[1] * viewMatrix[4] + -origin[2] * viewMatrix[8]; viewMatrix[13] = -origin[0] * viewMatrix[1] + -origin[1] * viewMatrix[5] + -origin[2] * viewMatrix[9]; viewMatrix[14] = -origin[0] * viewMatrix[2] + -origin[1] * viewMatrix[6] + -origin[2] * viewMatrix[10]; viewMatrix[3] = 0; viewMatrix[7] = 0; viewMatrix[11] = 0; viewMatrix[15] = 1; memcpy( unflippedLightMatrix, viewMatrix, sizeof( unflippedLightMatrix ) ); R_MatrixMultiply( viewMatrix, s_flipMatrix, lightMatrix ); // create frustum planes idPlane globalFrustum[6]; // near clip globalFrustum[0][0] = -viewMatrix[0]; globalFrustum[0][1] = -viewMatrix[4]; globalFrustum[0][2] = -viewMatrix[8]; globalFrustum[0][3] = -( origin[0] * globalFrustum[0][0] + origin[1] * globalFrustum[0][1] + origin[2] * globalFrustum[0][2] ); // far clip globalFrustum[1][0] = viewMatrix[0]; globalFrustum[1][1] = viewMatrix[4]; globalFrustum[1][2] = viewMatrix[8]; globalFrustum[1][3] = -globalFrustum[0][3] - viewLightAxialSize; // side clips globalFrustum[2][0] = -viewMatrix[0] + viewMatrix[1]; globalFrustum[2][1] = -viewMatrix[4] + viewMatrix[5]; globalFrustum[2][2] = -viewMatrix[8] + viewMatrix[9]; globalFrustum[3][0] = -viewMatrix[0] - viewMatrix[1]; globalFrustum[3][1] = -viewMatrix[4] - viewMatrix[5]; globalFrustum[3][2] = -viewMatrix[8] - viewMatrix[9]; globalFrustum[4][0] = -viewMatrix[0] + viewMatrix[2]; globalFrustum[4][1] = -viewMatrix[4] + viewMatrix[6]; globalFrustum[4][2] = -viewMatrix[8] + viewMatrix[10]; globalFrustum[5][0] = -viewMatrix[0] - viewMatrix[2]; globalFrustum[5][1] = -viewMatrix[4] - viewMatrix[6]; globalFrustum[5][2] = -viewMatrix[8] - viewMatrix[10]; // is this normalization necessary? for( int i = 0; i < 6; i++ ) { globalFrustum[i].ToVec4().ToVec3().Normalize(); } for( int i = 2; i < 6; i++ ) { globalFrustum[i][3] = -( origin * globalFrustum[i].ToVec4().ToVec3() ); } RB_Backend_CullInteractions( vLight, globalFrustum ); // FIXME: we want to skip the sampling as well as the generation when not casting shadows if( !r_sb_noShadows.GetBool() && vLight->lightShader->LightCastsShadows() ) { // set polygon offset for the rendering switch( r_sb_occluderFacing.GetInteger() ) { case 0: // front sides glPolygonOffset( r_sb_polyOfsFactor.GetFloat(), r_sb_polyOfsUnits.GetFloat() ); glEnable( GL_POLYGON_OFFSET_FILL ); RB_Backend_RenderOccluders( vLight ); glDisable( GL_POLYGON_OFFSET_FILL ); break; case 1: // back sides glPolygonOffset( -r_sb_polyOfsFactor.GetFloat(), -r_sb_polyOfsUnits.GetFloat() ); glEnable( GL_POLYGON_OFFSET_FILL ); GL_Cull( CT_BACK_SIDED ); RB_Backend_RenderOccluders( vLight ); GL_Cull( CT_FRONT_SIDED ); glDisable( GL_POLYGON_OFFSET_FILL ); break; case 2: // both sides GL_Cull( CT_BACK_SIDED ); RB_Backend_RenderOccluders( vLight ); GL_Cull( CT_FRONT_SIDED ); shadowImage[2]->BindTexture(); glCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, 0, 0, lightBufferSize, lightBufferSize ); RB_Backend_RenderOccluders( vLight ); shadowImage[1]->BindTexture(); glCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, 0, 0, lightBufferSize, lightBufferSize ); // fragment program to combine the two depth images glBindProgramARB( GL_VERTEX_PROGRAM_ARB, depthMidpointVertexProgram ); glBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, depthMidpointFragmentProgram ); glEnable( GL_VERTEX_PROGRAM_ARB ); glEnable( GL_FRAGMENT_PROGRAM_ARB ); GL_SelectTextureNoClient( 1 ); shadowImage[1]->BindTexture(); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); GL_SelectTextureNoClient( 0 ); shadowImage[2]->BindTexture(); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); // draw a full screen quad glMatrixMode( GL_PROJECTION ); glLoadIdentity(); glOrtho( 0, 1, 0, 1, -1, 1 ); glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); GL_State( GLS_DEPTHFUNC_ALWAYS ); glBegin( GL_TRIANGLE_FAN ); glTexCoord2f( 0, 0 ); glVertex2f( 0, 0 ); glTexCoord2f( 0, lightBufferSizeFraction ); glVertex2f( 0, 1 ); glTexCoord2f( lightBufferSizeFraction, lightBufferSizeFraction ); glVertex2f( 1, 1 ); glTexCoord2f( lightBufferSizeFraction, 0 ); glVertex2f( 1, 0 ); glEnd(); glDisable( GL_VERTEX_PROGRAM_ARB ); glDisable( GL_FRAGMENT_PROGRAM_ARB ); break; } } // copy to the texture shadowImage[0]->BindTexture(); glCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, 0, 0, lightBufferSize, lightBufferSize ); glDisableClientState( GL_TEXTURE_COORD_ARRAY ); // reset the normal view matrix glMatrixMode( GL_PROJECTION ); glLoadMatrixf( backEnd.viewDef->projectionMatrix ); glMatrixMode( GL_MODELVIEW ); // the current modelView matrix is not valid backEnd.currentSpace = NULL; } /* ================== RB_Backend_DrawInteraction ================== */ void RB_Backend_DrawInteraction( const drawInteraction_t *din ) { // load all the vertex program parameters glProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_ORIGIN, din->localLightOrigin.ToFloatPtr() ); glProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_VIEW_ORIGIN, din->localViewOrigin.ToFloatPtr() ); glProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_PROJECT_S, din->lightProjection[0].ToFloatPtr() ); glProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_PROJECT_T, din->lightProjection[1].ToFloatPtr() ); glProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_PROJECT_Q, din->lightProjection[2].ToFloatPtr() ); glProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_FALLOFF_S, din->lightProjection[3].ToFloatPtr() ); glProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_BUMP_MATRIX_S, din->bumpMatrix[0].ToFloatPtr() ); glProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_BUMP_MATRIX_T, din->bumpMatrix[1].ToFloatPtr() ); glProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_DIFFUSE_MATRIX_S, din->diffuseMatrix[0].ToFloatPtr() ); glProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_DIFFUSE_MATRIX_T, din->diffuseMatrix[1].ToFloatPtr() ); glProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_SPECULAR_MATRIX_S, din->specularMatrix[0].ToFloatPtr() ); glProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_SPECULAR_MATRIX_T, din->specularMatrix[1].ToFloatPtr() ); // calculate depth projection for shadow buffer float sRow[4]; float tRow[4]; float rRow[4]; float qRow[4]; float matrix[16]; float matrix2[16]; R_MatrixMultiply( din->surf->space->modelMatrix, lightMatrix, matrix ); R_MatrixMultiply( matrix, lightProjectionMatrix, matrix2 ); // the final values need to be in 0.0 : 1.0 range instead of -1 : 1 sRow[0] = 0.5 * lightBufferSizeFraction * ( matrix2[0] + matrix2[3] ); sRow[1] = 0.5 * lightBufferSizeFraction * ( matrix2[4] + matrix2[7] ); sRow[2] = 0.5 * lightBufferSizeFraction * ( matrix2[8] + matrix2[11] ); sRow[3] = 0.5 * lightBufferSizeFraction * ( matrix2[12] + matrix2[15] ); glProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, 18, sRow ); tRow[0] = 0.5 * lightBufferSizeFraction * ( matrix2[1] + matrix2[3] ); tRow[1] = 0.5 * lightBufferSizeFraction * ( matrix2[5] + matrix2[7] ); tRow[2] = 0.5 * lightBufferSizeFraction * ( matrix2[9] + matrix2[11] ); tRow[3] = 0.5 * lightBufferSizeFraction * ( matrix2[13] + matrix2[15] ); glProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, 19, tRow ); rRow[0] = 0.5 * ( matrix2[2] + matrix2[3] ); rRow[1] = 0.5 * ( matrix2[6] + matrix2[7] ); rRow[2] = 0.5 * ( matrix2[10] + matrix2[11] ); rRow[3] = 0.5 * ( matrix2[14] + matrix2[15] ); glProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, 20, rRow ); qRow[0] = matrix2[3]; qRow[1] = matrix2[7]; qRow[2] = matrix2[11]; qRow[3] = matrix2[15]; glProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, 21, qRow ); // testing fragment based normal mapping if( r_testARBProgram.GetBool() ) { glProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 2, din->localLightOrigin.ToFloatPtr() ); glProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 3, din->localViewOrigin.ToFloatPtr() ); } static const float zero[4] = { 0, 0, 0, 0 }; static const float one[4] = { 1, 1, 1, 1 }; static const float negOne[4] = { -1, -1, -1, -1 }; switch( din->vertexColor ) { case SVC_IGNORE: glProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_COLOR_MODULATE, zero ); glProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_COLOR_ADD, one ); break; case SVC_MODULATE: glProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_COLOR_MODULATE, one ); glProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_COLOR_ADD, zero ); break; case SVC_INVERSE_MODULATE: glProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_COLOR_MODULATE, negOne ); glProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_COLOR_ADD, one ); break; } // set the constant colors glProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 0, din->diffuseColor.ToFloatPtr() ); glProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 1, din->specularColor.ToFloatPtr() ); //----------------------------------------------------- // screen power of two correction factor float parm[4]; parm[0] = 1.0 / ( JITTER_SIZE * r_sb_samples.GetInteger() ); parm[1] = 1.0 / JITTER_SIZE; parm[2] = 0; parm[3] = 1; glProgramLocalParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 2, parm ); // jitter tex scale parm[0] = parm[1] = r_sb_jitterScale.GetFloat() * lightBufferSizeFraction; parm[2] = -r_sb_biasScale.GetFloat(); parm[3] = 0; glProgramLocalParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 3, parm ); // jitter tex offset if( r_sb_randomize.GetBool() ) { parm[0] = ( rand() & 255 ) / 255.0; parm[1] = ( rand() & 255 ) / 255.0; } else { parm[0] = parm[1] = 0; } parm[2] = 0; parm[3] = 0; glProgramLocalParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 4, parm ); //----------------------------------------------------- // set the textures // texture 1 will be the per-surface bump map GL_SelectTextureNoClient( 1 ); din->bumpImage->BindTexture(); // texture 2 will be the light falloff texture GL_SelectTextureNoClient( 2 ); din->lightFalloffImage->BindTexture(); // texture 3 will be the light projection texture GL_SelectTextureNoClient( 3 ); din->lightImage->BindTexture(); // texture 4 is the per-surface diffuse map GL_SelectTextureNoClient( 4 ); din->diffuseImage->BindTexture(); // texture 5 is the per-surface specular map GL_SelectTextureNoClient( 5 ); din->specularImage->BindTexture(); // draw it RB_DrawElementsWithCounters( din->surf->geo ); } /* ============= RB_Backend_CreateDrawInteractions ============= */ void RB_Backend_CreateDrawInteractions( const drawSurf_t *surf ) { if( !surf ) { return; } if( r_sb_screenSpaceShadow.GetBool() ) { // perform setup here that will be constant for all interactions GL_State( GLS_SRCBLEND_DST_ALPHA | GLS_DSTBLEND_ONE | GLS_DEPTHMASK | backEnd.depthFunc ); if( r_testARBProgram.GetBool() ) { glBindProgramARB( GL_VERTEX_PROGRAM_ARB, VPROG_TEST ); glBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, FPROG_TEST ); } else { glBindProgramARB( GL_VERTEX_PROGRAM_ARB, VPROG_INTERACTION ); glBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, FPROG_INTERACTION ); } } else { // perform setup here that will be constant for all interactions GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_DEPTHMASK | backEnd.depthFunc ); GL_State( GLS_DEPTHMASK | GLS_DEPTHFUNC_ALWAYS ); //!@# // bind the vertex program glBindProgramARB( GL_VERTEX_PROGRAM_ARB, shadowVertexProgram ); if( r_sb_samples.GetInteger() == 16 ) { glBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, shadowFragmentProgram16 ); } else if( r_sb_samples.GetInteger() == 4 ) { glBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, shadowFragmentProgram4 ); } else if( r_sb_samples.GetInteger() == 1 ) { glBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, shadowFragmentProgram1 ); } else { glBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, shadowFragmentProgram0 ); } } glEnable( GL_VERTEX_PROGRAM_ARB ); glEnable( GL_FRAGMENT_PROGRAM_ARB ); // enable the vertex arrays glEnableVertexAttribArrayARB( 8 ); glEnableVertexAttribArrayARB( 9 ); glEnableVertexAttribArrayARB( 10 ); glEnableVertexAttribArrayARB( 11 ); glEnableClientState( GL_COLOR_ARRAY ); // texture 0 is the normalization cube map for the vector towards the light GL_SelectTextureNoClient( 0 ); if( backEnd.vLight->lightShader->IsAmbientLight() ) { globalImages->normalCubeMapImage->BindTexture(); glBindProgramARB( GL_VERTEX_PROGRAM_ARB, VPROG_AMBIENT ); glBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, FPROG_AMBIENT ); } else { globalImages->normalCubeMapImage->BindTexture(); } // texture 6 is the specular lookup table GL_SelectTextureNoClient( 6 ); if( r_testARBProgram.GetBool() ) { globalImages->specular2DTableImage->BindTexture(); // variable specularity in alpha channel } else { globalImages->specularTableImage->BindTexture(); } for( ; surf; surf = surf->nextOnLight ) { // perform setup here that will not change over multiple interaction passes if( backEnd.vLight->lightShader->IsAmbientLight() ) { float parm[4]; parm[0] = surf->space->modelMatrix[0]; parm[1] = surf->space->modelMatrix[4]; parm[2] = surf->space->modelMatrix[8]; parm[3] = 0; glProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, 20, parm ); parm[0] = surf->space->modelMatrix[1]; parm[1] = surf->space->modelMatrix[5]; parm[2] = surf->space->modelMatrix[9]; parm[3] = 0; glProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, 21, parm ); parm[0] = surf->space->modelMatrix[2]; parm[1] = surf->space->modelMatrix[6]; parm[2] = surf->space->modelMatrix[10]; parm[3] = 0; glProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, 22, parm ); GL_SelectTextureNoClient( 0 ); const shaderStage_t *stage = backEnd.vLight->lightShader->GetStage( 0 ); if( stage->newStage ) { stage->newStage->fragmentProgramImages[7]->BindFragment(); } } // set the vertex pointers idDrawVert *ac = ( idDrawVert * )vertexCache.Position( surf->geo->ambientCache ); glColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( idDrawVert ), ac->color ); glVertexAttribPointerARB( 11, 3, GL_FLOAT, false, sizeof( idDrawVert ), ac->normal.ToFloatPtr() ); glVertexAttribPointerARB( 10, 3, GL_FLOAT, false, sizeof( idDrawVert ), ac->tangents[1].ToFloatPtr() ); glVertexAttribPointerARB( 9, 3, GL_FLOAT, false, sizeof( idDrawVert ), ac->tangents[0].ToFloatPtr() ); glVertexAttribPointerARB( 8, 2, GL_FLOAT, false, sizeof( idDrawVert ), ac->st.ToFloatPtr() ); glVertexPointer( 3, GL_FLOAT, sizeof( idDrawVert ), ac->xyz.ToFloatPtr() ); // this may cause RB_DrawInteraction to be exacuted multiple // times with different colors and images if the surface or light have multiple layers if( r_sb_screenSpaceShadow.GetBool() ) { RB_CreateSingleDrawInteractions( surf, RB_DrawInteraction ); } else { RB_CreateSingleDrawInteractions( surf, RB_Backend_DrawInteraction ); } } glDisableVertexAttribArrayARB( 8 ); glDisableVertexAttribArrayARB( 9 ); glDisableVertexAttribArrayARB( 10 ); glDisableVertexAttribArrayARB( 11 ); glDisableClientState( GL_COLOR_ARRAY ); // disable features GL_SelectTextureNoClient( 6 ); globalImages->BindNull(); GL_SelectTextureNoClient( 5 ); globalImages->BindNull(); GL_SelectTextureNoClient( 4 ); globalImages->BindNull(); GL_SelectTextureNoClient( 3 ); globalImages->BindNull(); GL_SelectTextureNoClient( 2 ); globalImages->BindNull(); GL_SelectTextureNoClient( 1 ); globalImages->BindNull(); backEnd.glState.currenttmu = -1; GL_SelectTexture( 0 ); glDisable( GL_VERTEX_PROGRAM_ARB ); glDisable( GL_FRAGMENT_PROGRAM_ARB ); } // TODO: move to idlib void InvertByTranspose( const float a[16], float r[16] ) { r[0] = a[0]; r[1] = a[4]; r[2] = a[8]; r[3] = 0; r[4] = a[1]; r[5] = a[5]; r[6] = a[9]; r[7] = 0; r[8] = a[2]; r[9] = a[6]; r[10] = a[10]; r[11] = 0; r[12] = -( r[0] * a[12] + r[4] * a[13] + r[8] * a[14] ); r[13] = -( r[1] * a[12] + r[5] * a[13] + r[9] * a[14] ); r[14] = -( r[2] * a[12] + r[6] * a[13] + r[10] * a[14] ); r[15] = 1; } void FullInvert( const float a[16], float r[16] ) { idMat4 am; for( int i = 0; i < 4; i++ ) { for( int j = 0; j < 4; j++ ) { am[i][j] = a[j * 4 + i]; } } if( !am.InverseSelf() ) { common->Error( "Invert failed" ); } for( int i = 0; i < 4; i++ ) { for( int j = 0; j < 4; j++ ) { r[j * 4 + i] = am[i][j]; } } } // TODO: move to idlib /* ================== RB_Backend_TrianglesForFrustum ================== */ const srfTriangles_t *RB_Backend_TrianglesForFrustum( viewLight_t *vLight, int side ) { const srfTriangles_t *tri; static srfTriangles_t frustumTri; static idDrawVert verts[5]; static glIndex_t indexes[18] = { 0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 1, 2, 1, 4, 2, 4, 3 }; if( side == -1 ) { tri = vLight->frustumTris; } else { memset( verts, 0, sizeof( verts ) ); for( int i = 0; i < 5; i++ ) { verts[i].xyz = vLight->globalLightOrigin; } memset( &frustumTri, 0, sizeof( frustumTri ) ); frustumTri.indexes = indexes; frustumTri.verts = verts; frustumTri.numIndexes = 18; frustumTri.numVerts = 5; tri = &frustumTri; float size = viewLightAxialSize; switch( side ) { case 0: verts[1].xyz[0] += size; verts[2].xyz[0] += size; verts[3].xyz[0] += size; verts[4].xyz[0] += size; verts[1].xyz[1] += size; verts[1].xyz[2] += size; verts[2].xyz[1] -= size; verts[2].xyz[2] += size; verts[3].xyz[1] -= size; verts[3].xyz[2] -= size; verts[4].xyz[1] += size; verts[4].xyz[2] -= size; break; case 1: verts[1].xyz[0] -= size; verts[2].xyz[0] -= size; verts[3].xyz[0] -= size; verts[4].xyz[0] -= size; verts[1].xyz[1] -= size; verts[1].xyz[2] += size; verts[2].xyz[1] += size; verts[2].xyz[2] += size; verts[3].xyz[1] += size; verts[3].xyz[2] -= size; verts[4].xyz[1] -= size; verts[4].xyz[2] -= size; break; case 2: verts[1].xyz[1] += size; verts[2].xyz[1] += size; verts[3].xyz[1] += size; verts[4].xyz[1] += size; verts[1].xyz[0] -= size; verts[1].xyz[2] += size; verts[2].xyz[0] += size; verts[2].xyz[2] += size; verts[3].xyz[0] += size; verts[3].xyz[2] -= size; verts[4].xyz[0] -= size; verts[4].xyz[2] -= size; break; case 3: verts[1].xyz[1] -= size; verts[2].xyz[1] -= size; verts[3].xyz[1] -= size; verts[4].xyz[1] -= size; verts[1].xyz[0] += size; verts[1].xyz[2] += size; verts[2].xyz[0] -= size; verts[2].xyz[2] += size; verts[3].xyz[0] -= size; verts[3].xyz[2] -= size; verts[4].xyz[0] += size; verts[4].xyz[2] -= size; break; case 4: verts[1].xyz[2] += size; verts[2].xyz[2] += size; verts[3].xyz[2] += size; verts[4].xyz[2] += size; verts[1].xyz[0] += size; verts[1].xyz[1] += size; verts[2].xyz[0] -= size; verts[2].xyz[1] += size; verts[3].xyz[0] -= size; verts[3].xyz[1] -= size; verts[4].xyz[0] += size; verts[4].xyz[1] -= size; break; case 5: verts[1].xyz[2] -= size; verts[2].xyz[2] -= size; verts[3].xyz[2] -= size; verts[4].xyz[2] -= size; verts[1].xyz[0] -= size; verts[1].xyz[1] += size; verts[2].xyz[0] += size; verts[2].xyz[1] += size; verts[3].xyz[0] += size; verts[3].xyz[1] -= size; verts[4].xyz[0] -= size; verts[4].xyz[1] -= size; break; } frustumTri.ambientCache = vertexCache.AllocFrameTemp( verts, sizeof( verts ) ); } return tri; } /* ================== RB_Backend_SelectFrustum ================== */ void RB_Backend_SelectFrustum( viewLight_t *vLight, int side ) { glLoadMatrixf( backEnd.viewDef->worldSpace.modelViewMatrix ); const srfTriangles_t *tri = RB_Backend_TrianglesForFrustum( vLight, side ); idDrawVert *ac = ( idDrawVert * )vertexCache.Position( tri->ambientCache ); glVertexPointer( 3, GL_FLOAT, sizeof( idDrawVert ), ac->xyz.ToFloatPtr() ); glDisable( GL_TEXTURE_2D ); glDisableClientState( GL_TEXTURE_COORD_ARRAY ); // clear stencil buffer glEnable( GL_SCISSOR_TEST ); glEnable( GL_STENCIL_TEST ); glClearStencil( 1 ); glClear( GL_STENCIL_BUFFER_BIT ); // draw front faces of the light frustum, incrementing the stencil buffer on depth fail // so we can't draw on those pixels GL_State( GLS_COLORMASK | GLS_ALPHAMASK | GLS_DEPTHMASK | GLS_DEPTHFUNC_LESS ); glStencilFunc( GL_ALWAYS, 0, 255 ); glStencilOp( GL_KEEP, GL_INCR, GL_KEEP ); GL_Cull( CT_FRONT_SIDED ); RB_DrawElementsWithCounters( tri ); // draw back faces of the light frustum with // depth test greater // stencil test of equal 1 // zero stencil stencil when depth test passes, so subsequent surface drawing // can occur on those pixels // this pass does all the shadow filtering glStencilFunc( GL_EQUAL, 1, 255 ); glStencilOp( GL_KEEP, GL_KEEP, GL_ZERO ); GL_Cull( CT_BACK_SIDED ); glDepthFunc( GL_GREATER ); // write to destination alpha if( r_sb_showFrustumPixels.GetBool() ) { GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_DEPTHMASK | GLS_DEPTHFUNC_LESS ); glDisable( GL_TEXTURE_2D ); glColor4f( 0, 0.25, 0, 1 ); } else { GL_State( GLS_COLORMASK | GLS_DEPTHMASK | GLS_DEPTHFUNC_LESS ); glEnable( GL_VERTEX_PROGRAM_ARB ); glEnable( GL_FRAGMENT_PROGRAM_ARB ); glBindProgramARB( GL_VERTEX_PROGRAM_ARB, screenSpaceShadowVertexProgram ); switch( r_sb_samples.GetInteger() ) { case 0: glBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, screenSpaceShadowFragmentProgram0 ); break; case 1: glBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, screenSpaceShadowFragmentProgram1 ); break; case 4: glBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, screenSpaceShadowFragmentProgram4 ); break; case 16: glBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, screenSpaceShadowFragmentProgram16 ); break; } } /* texture[0] = view depth texture texture[1] = jitter texture texture[2] = light depth texture */ GL_SelectTextureNoClient( 2 ); shadowImage[0]->BindTexture(); GL_SelectTextureNoClient( 1 ); if( r_sb_samples.GetInteger() == 16 ) { jitterImage16->BindTexture(); } else if( r_sb_samples.GetInteger() == 4 ) { jitterImage4->BindTexture(); } else { jitterImage1->BindTexture(); } GL_SelectTextureNoClient( 0 ); viewDepthImage->BindTexture(); /* PARAM positionToDepthTexScale = program.local[0]; # fragment.position to screen depth texture transformation PARAM zProject = program.local[1]; # projection[10], projection[14], 0, 0 PARAM positionToViewSpace = program.local[2]; # X add, Y add, X mul, Y mul PARAM viewToLightS = program.local[3]; PARAM viewToLightT = program.local[4]; PARAM viewToLightR = program.local[5]; PARAM viewToLightQ = program.local[6]; PARAM positionToJitterTexScale = program.local[7]; # fragment.position to jitter texture PARAM jitterTexScale = program.local[8]; PARAM jitterTexOffset = program.local[9]; */ float parm[4]; int pot; // calculate depth projection for shadow buffer float sRow[4]; float tRow[4]; float rRow[4]; float qRow[4]; float invertedView[16]; float invertedProjection[16]; float matrix[16]; float matrix2[16]; // we need the inverse of the projection matrix to go from NDC to view FullInvert( backEnd.viewDef->projectionMatrix, invertedProjection ); /* from window to NDC: ( x - xMid ) * 1.0 / xMid ( y - yMid ) * 1.0 / yMid ( z - 0.5 ) * 2 from NDC to clip coordinates: rcp(1/w) */ // we need the inverse of the viewMatrix to go from view (looking down negative Z) to world InvertByTranspose( backEnd.viewDef->worldSpace.modelViewMatrix, invertedView ); // then we go from world to light view space (looking down negative Z) R_MatrixMultiply( invertedView, lightMatrix, matrix ); // then to light projection, giving X/w, Y/w, Z/w in the -1 : 1 range R_MatrixMultiply( matrix, lightProjectionMatrix, matrix2 ); // the final values need to be in 0.0 : 1.0 range instead of -1 : 1 sRow[0] = 0.5 * ( matrix2[0] + matrix2[3] ) * lightBufferSizeFraction; sRow[1] = 0.5 * ( matrix2[4] + matrix2[7] ) * lightBufferSizeFraction; sRow[2] = 0.5 * ( matrix2[8] + matrix2[11] ) * lightBufferSizeFraction; sRow[3] = 0.5 * ( matrix2[12] + matrix2[15] ) * lightBufferSizeFraction; glProgramLocalParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 3, sRow ); tRow[0] = 0.5 * ( matrix2[1] + matrix2[3] ) * lightBufferSizeFraction; tRow[1] = 0.5 * ( matrix2[5] + matrix2[7] ) * lightBufferSizeFraction; tRow[2] = 0.5 * ( matrix2[9] + matrix2[11] ) * lightBufferSizeFraction; tRow[3] = 0.5 * ( matrix2[13] + matrix2[15] ) * lightBufferSizeFraction; glProgramLocalParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 4, tRow ); rRow[0] = 0.5 * ( matrix2[2] + matrix2[3] ); rRow[1] = 0.5 * ( matrix2[6] + matrix2[7] ); rRow[2] = 0.5 * ( matrix2[10] + matrix2[11] ); rRow[3] = 0.5 * ( matrix2[14] + matrix2[15] ); glProgramLocalParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 5, rRow ); qRow[0] = matrix2[3]; qRow[1] = matrix2[7]; qRow[2] = matrix2[11]; qRow[3] = matrix2[15]; glProgramLocalParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 6, qRow ); //----------------------------------------------------- // these should be constant for the entire frame // convert 0..viewport-1 sizes to fractions inside the POT screen depth texture int w = viewBufferSize; pot = MakePowerOfTwo( w ); parm[0] = 1.0 / maxViewBufferSize; // * ( (float)viewBufferSize / w ); int h = viewBufferHeight; pot = MakePowerOfTwo( h ); parm[1] = parm[0]; // 1.0 / pot; parm[2] = 0; parm[3] = 1; glProgramLocalParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 0, parm ); // zProject values parm[0] = backEnd.viewDef->projectionMatrix[10]; parm[1] = backEnd.viewDef->projectionMatrix[14]; parm[2] = 0; parm[3] = 0; glProgramLocalParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 1, parm ); // positionToViewSpace parm[0] = -1.0 / backEnd.viewDef->projectionMatrix[0]; parm[1] = -1.0 / backEnd.viewDef->projectionMatrix[5]; parm[2] = 2.0 / viewBufferSize; parm[3] = 2.0 / viewBufferSize; glProgramLocalParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 2, parm ); // positionToJitterTexScale parm[0] = 1.0 / ( JITTER_SIZE * r_sb_samples.GetInteger() ); parm[1] = 1.0 / JITTER_SIZE; parm[2] = 0; parm[3] = 1; glProgramLocalParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 7, parm ); // jitter tex scale parm[0] = parm[1] = r_sb_jitterScale.GetFloat() * lightBufferSizeFraction; parm[2] = -r_sb_biasScale.GetFloat(); parm[3] = 0; glProgramLocalParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 8, parm ); // jitter tex offset if( r_sb_randomize.GetBool() ) { parm[0] = ( rand() & 255 ) / 255.0; parm[1] = ( rand() & 255 ) / 255.0; } else { parm[0] = parm[1] = 0; } parm[2] = 0; parm[3] = 0; glProgramLocalParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 9, parm ); //----------------------------------------------------- RB_DrawElementsWithCounters( tri ); glDisable( GL_VERTEX_PROGRAM_ARB ); glDisable( GL_FRAGMENT_PROGRAM_ARB ); GL_Cull( CT_FRONT_SIDED ); // glEnableClientState( GL_TEXTURE_COORD_ARRAY ); glDepthFunc( GL_LEQUAL ); if( r_sb_showFrustumPixels.GetBool() ) { glEnable( GL_TEXTURE_2D ); glColor3f( 1, 1, 1 ); } // after all the frustums have been drawn, the surfaces that have been drawn on will get interactions // scissor may still be a win even with the stencil test for very fast rejects glStencilFunc( GL_EQUAL, 0, 255 ); glStencilOp( GL_KEEP, GL_KEEP, GL_KEEP ); // we can avoid clearing the stencil buffer by changing the hasLight value for each light } /* ================== R_Backend_CalcLightAxialSize all light side projections must currently match, so non-centered and non-cubic lights must take the largest length ================== */ float R_Backend_CalcLightAxialSize( viewLight_t *vLight ) { float max = 0; if( !vLight->lightDef->parms.pointLight ) { idVec3 dir = vLight->lightDef->parms.target - vLight->lightDef->parms.origin; max = dir.Length(); return max; } for( int i = 0; i < 3; i++ ) { float dist = fabs( vLight->lightDef->parms.lightCenter[i] ); dist += vLight->lightDef->parms.lightRadius[i]; if( dist > max ) { max = dist; } } return max; } /* ================== R_Backend_RenderViewDepthImage This could be avoided by drop sampling the native view depth buffer with render to texture Bilerp might even be apropriate, although it would cause issues at edges ================== */ void R_Backend_RenderViewDepthImage( void ) { if( !r_sb_screenSpaceShadow.GetBool() ) { return; } // the screen resolution is allready exactly the window width, so we can // use the depth buffer we already have if( r_sb_usePbuffer.GetBool() ) { GL_CheckErrors(); // set the current openGL drawable to the shadow buffer R_MakeCurrent( viewPbufferDC, win32.hGLRC, NULL ); } // render the depth to the new size glViewport( 0, 0, viewBufferSize, viewBufferHeight ); glScissor( 0, 0, viewBufferSize, viewBufferHeight ); glClear( GL_DEPTH_BUFFER_BIT ); glStencilFunc( GL_ALWAYS, 0, 255 ); // the first texture will be used for alpha tested surfaces GL_SelectTexture( 0 ); glEnableClientState( GL_TEXTURE_COORD_ARRAY ); GL_State( GLS_DEPTHFUNC_LESS ); RB_RenderDrawSurfListWithFunction( backEnd.viewDef->drawSurfs, backEnd.viewDef->numDrawSurfs, RB_T_FillDepthBuffer ); // copy it to a texture viewDepthImage->BindTexture(); glCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, 0, 0, viewBufferSize, viewBufferHeight ); if( r_sb_usePbuffer.GetBool() ) { // set the normal screen drawable current R_MakeCurrent( win32.hDC, win32.hGLRC, NULL ); } // reset the window clipping glMatrixMode( GL_PROJECTION ); glLoadMatrixf( backEnd.viewDef->projectionMatrix ); glMatrixMode( GL_MODELVIEW ); glViewport( tr.viewportOffset[0] + backEnd.viewDef->viewport.x1, tr.viewportOffset[1] + backEnd.viewDef->viewport.y1, backEnd.viewDef->viewport.x2 + 1 - backEnd.viewDef->viewport.x1, backEnd.viewDef->viewport.y2 + 1 - backEnd.viewDef->viewport.y1 ); glScissor( tr.viewportOffset[0] + backEnd.viewDef->viewport.x1, tr.viewportOffset[1] + backEnd.viewDef->viewport.y1, backEnd.viewDef->viewport.x2 + 1 - backEnd.viewDef->viewport.x1, backEnd.viewDef->viewport.y2 + 1 - backEnd.viewDef->viewport.y1 ); // the current modelView matrix is not valid backEnd.currentSpace = NULL; glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); } /* ================== RB_Backend_SetNativeBuffer This is always the back buffer, and scissor is set full screen ================== */ void RB_Backend_SetNativeBuffer( void ) { // set the normal screen drawable current R_MakeCurrent( win32.hDC, win32.hGLRC, NULL ); glViewport( tr.viewportOffset[0] + backEnd.viewDef->viewport.x1, tr.viewportOffset[1] + backEnd.viewDef->viewport.y1, backEnd.viewDef->viewport.x2 + 1 - backEnd.viewDef->viewport.x1, backEnd.viewDef->viewport.y2 + 1 - backEnd.viewDef->viewport.y1 ); backEnd.currentScissor = backEnd.viewDef->viewport; if( r_useScissor.GetBool() ) { glScissor( backEnd.viewDef->viewport.x1 + backEnd.currentScissor.x1, backEnd.viewDef->viewport.y1 + backEnd.currentScissor.y1, backEnd.currentScissor.x2 + 1 - backEnd.currentScissor.x1, backEnd.currentScissor.y2 + 1 - backEnd.currentScissor.y1 ); } } /* ================== RB_Backend_SetRenderBuffer This may be to a float pBuffer, and scissor is set to cover only the light ================== */ void RB_Backend_SetRenderBuffer( viewLight_t *vLight ) { if( r_hdr_useFloats.GetBool() ) { R_MakeCurrent( floatPbufferDC, floatContext, floatPbuffer ); } else { if( !wglMakeCurrent( win32.hDC, win32.hGLRC ) ) { GL_CheckErrors(); common->FatalError( "Couldn't return to normal drawing context" ); } } glViewport( tr.viewportOffset[0] + backEnd.viewDef->viewport.x1, tr.viewportOffset[1] + backEnd.viewDef->viewport.y1, backEnd.viewDef->viewport.x2 + 1 - backEnd.viewDef->viewport.x1, backEnd.viewDef->viewport.y2 + 1 - backEnd.viewDef->viewport.y1 ); if( !vLight ) { backEnd.currentScissor = backEnd.viewDef->viewport; } else { backEnd.currentScissor = vLight->scissorRect; } if( r_useScissor.GetBool() ) { glScissor( backEnd.viewDef->viewport.x1 + backEnd.currentScissor.x1, backEnd.viewDef->viewport.y1 + backEnd.currentScissor.y1, backEnd.currentScissor.x2 + 1 - backEnd.currentScissor.x1, backEnd.currentScissor.y2 + 1 - backEnd.currentScissor.y1 ); } } /* ================== RB_shadowResampleAlpha ================== */ void RB_shadowResampleAlpha( void ) { viewAlphaImage->BindTexture(); // we could make this a subimage, but it isn't relevent once we have render-to-texture glCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, 0, 0, viewBufferSize, viewBufferHeight ); RB_Backend_SetRenderBuffer( backEnd.vLight ); //===================== glLoadMatrixf( backEnd.viewDef->worldSpace.modelViewMatrix ); // this uses the full light, not side frustums const srfTriangles_t *tri = backEnd.vLight->frustumTris; idDrawVert *ac = ( idDrawVert * )vertexCache.Position( tri->ambientCache ); glVertexPointer( 3, GL_FLOAT, sizeof( idDrawVert ), ac->xyz.ToFloatPtr() ); // clear stencil buffer glEnable( GL_SCISSOR_TEST ); glEnable( GL_STENCIL_TEST ); glClearStencil( 1 ); glClear( GL_STENCIL_BUFFER_BIT ); // draw front faces of the light frustum, incrementing the stencil buffer on depth fail // so we can't draw on those pixels GL_State( GLS_COLORMASK | GLS_ALPHAMASK | GLS_DEPTHMASK | GLS_DEPTHFUNC_LESS ); glStencilFunc( GL_ALWAYS, 0, 255 ); glStencilOp( GL_KEEP, GL_INCR, GL_KEEP ); GL_Cull( CT_FRONT_SIDED ); // set fragment / vertex program? RB_DrawElementsWithCounters( tri ); // draw back faces of the light frustum with // depth test greater // stencil test of equal 1 // zero stencil stencil when depth test passes, so subsequent interaction drawing // can occur on those pixels // this pass does all the shadow filtering glStencilFunc( GL_EQUAL, 1, 255 ); glStencilOp( GL_KEEP, GL_KEEP, GL_ZERO ); // write to destination alpha if( r_sb_showFrustumPixels.GetBool() ) { GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_DEPTHMASK | GLS_DEPTHFUNC_LESS ); glDisable( GL_TEXTURE_2D ); glColor4f( 0, 0.25, 0, 1 ); } else { GL_State( GLS_COLORMASK | GLS_DEPTHMASK | GLS_DEPTHFUNC_LESS ); glEnable( GL_VERTEX_PROGRAM_ARB ); glEnable( GL_FRAGMENT_PROGRAM_ARB ); glBindProgramARB( GL_VERTEX_PROGRAM_ARB, shadowResampleVertexProgram ); glBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, shadowResampleFragmentProgram ); // convert 0..viewport-1 sizes to fractions inside the POT screen depth texture // shrink by one unit for bilerp float parm[4]; parm[0] = 1.0 / ( maxViewBufferSize + 1 ) * viewBufferSize / maxViewBufferSize; parm[1] = parm[0]; parm[2] = 0; parm[3] = 1; glProgramLocalParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 0, parm ); } GL_Cull( CT_BACK_SIDED ); glDepthFunc( GL_GREATER ); RB_DrawElementsWithCounters( tri ); glDisable( GL_VERTEX_PROGRAM_ARB ); glDisable( GL_FRAGMENT_PROGRAM_ARB ); GL_Cull( CT_FRONT_SIDED ); glDepthFunc( GL_LEQUAL ); if( r_sb_showFrustumPixels.GetBool() ) { glEnable( GL_TEXTURE_2D ); glColor3f( 1, 1, 1 ); } // after all the frustums have been drawn, the surfaces that have been drawn on will get interactions // scissor may still be a win even with the stencil test for very fast rejects glStencilFunc( GL_EQUAL, 0, 255 ); glStencilOp( GL_KEEP, GL_KEEP, GL_KEEP ); } /* ================== RB_Backend_CoverScreen ================== */ void RB_Backend_CoverScreen( void ) { // draw a full screen quad glMatrixMode( GL_PROJECTION ); glLoadIdentity(); glOrtho( 0, 1, 0, 1, -1, 1 ); glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); glBegin( GL_TRIANGLE_FAN ); glVertex2f( 0, 0 ); glVertex2f( 0, 1 ); glVertex2f( 1, 1 ); glVertex2f( 1, 0 ); glEnd(); } /* ================== RB_Backend_ReadFloatBuffer ================== */ void RB_Backend_ReadFloatBuffer( void ) { int pixels = glConfig.vidWidth * glConfig.vidHeight; float *buf = ( float * )R_StaticAlloc( pixels * 4 * sizeof( float ) ); glReadPixels( 0, 0, glConfig.vidWidth, glConfig.vidHeight, GL_RGBA, GL_FLOAT, buf ); float mins[4] = { 9999, 9999, 9999, 9999 }; float maxs[4] = { -9999, -9999, -9999, -9999 }; for( int i = 0; i < pixels; i++ ) { for( int j = 0; j < 4; j++ ) { float v = buf[i * 4 + j]; if( v < mins[j] ) { mins[j] = v; } if( v > maxs[j] ) { maxs[j] = v; } } } RB_Backend_SetNativeBuffer(); glLoadIdentity(); glMatrixMode( GL_PROJECTION ); GL_State( GLS_DEPTHFUNC_ALWAYS ); glColor3f( 1, 1, 1 ); glPushMatrix(); glLoadIdentity(); glDisable( GL_TEXTURE_2D ); glOrtho( 0, 1, 0, 1, -1, 1 ); glRasterPos2f( 0.01f, 0.01f ); glDrawPixels( glConfig.vidWidth, glConfig.vidHeight, GL_RGBA, GL_FLOAT, buf ); glPopMatrix(); glEnable( GL_TEXTURE_2D ); glMatrixMode( GL_MODELVIEW ); R_StaticFree( buf ); } /* ================== RB_Backend_GammaDither ================== */ void RB_Backend_GammaDither( void ) { if( !r_hdr_useFloats.GetBool() ) { return; } RB_Backend_SetNativeBuffer(); /* # texture 0 is the high dynamic range buffer # texture 1 is the random dither texture # texture 2 is the light bloom texture # writes result.color as the 32 bit dithered and gamma corrected values PARAM exposure = program.local[0]; # multiply HDR value by this to get screen pixels PARAM gammaPower = program.local[1]; PARAM monitorDither = program.local[2]; PARAM positionToDitherScale = program.local[3]; PARAM bloomFraction = program.local[4]; PARAM positionToBloomScale = program.local[5]; */ glBindProgramARB( GL_VERTEX_PROGRAM_ARB, gammaDitherVertexProgram ); glBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, gammaDitherFragmentProgram ); glEnable( GL_VERTEX_PROGRAM_ARB ); glEnable( GL_FRAGMENT_PROGRAM_ARB ); glActiveTextureARB( GL_TEXTURE2_ARB ); glBindTexture( GL_TEXTURE_RECTANGLE_NV, floatPbufferQuarterImage->texnum ); R_BindTexImage( floatPbufferQuarter ); glActiveTextureARB( GL_TEXTURE1_ARB ); random256Image->BindFragment(); glActiveTextureARB( GL_TEXTURE0_ARB ); glBindTexture( GL_TEXTURE_RECTANGLE_NV, floatPbufferImage->texnum ); R_BindTexImage( floatPbuffer ); float parm[4]; parm[0] = r_hdr_exposure.GetFloat(); parm[1] = 0; parm[2] = 0; parm[3] = 0; glProgramLocalParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 0, parm ); parm[0] = r_hdr_gamma.GetFloat(); parm[1] = 0; parm[2] = 0; parm[3] = 0; glProgramLocalParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 1, parm ); parm[0] = r_hdr_monitorDither.GetFloat(); parm[1] = 0; parm[2] = 0; parm[3] = 0; glProgramLocalParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 2, parm ); parm[0] = 1.0 / 256; parm[1] = parm[0]; parm[2] = rand() / 65535.0; parm[3] = rand() / 65535.0; glProgramLocalParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 3, parm ); parm[0] = 1.0 - r_hdr_bloomFraction.GetFloat(); parm[1] = r_hdr_bloomFraction.GetFloat(); parm[2] = 0; parm[3] = 0; glProgramLocalParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 4, parm ); parm[0] = 0.25; parm[1] = 0.25; parm[2] = 0; parm[3] = 0; glProgramLocalParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 5, parm ); glDisable( GL_STENCIL_TEST ); glDisable( GL_SCISSOR_TEST ); glDisable( GL_DEPTH_TEST ); RB_Backend_CoverScreen(); glEnable( GL_DEPTH_TEST ); glDisable( GL_VERTEX_PROGRAM_ARB ); glDisable( GL_FRAGMENT_PROGRAM_ARB ); } /* ================== RB_Backend_Bloom ================== */ void RB_Backend_Bloom( void ) { if( !r_hdr_useFloats.GetBool() ) { return; } if( r_hdr_bloomFraction.GetFloat() == 0 ) { return; } GL_CheckErrors(); // // mip map // // draw to the second floatPbuffer R_MakeCurrent( floatPbuffer2DC, floatContext, floatPbuffer2 ); GL_State( 0 ); glDisable( GL_DEPTH_TEST ); glDisable( GL_SCISSOR_TEST ); glEnable( GL_VERTEX_PROGRAM_ARB ); glEnable( GL_FRAGMENT_PROGRAM_ARB ); glClearColor( 1.0, 0.5, 0, 0 ); glClear( GL_COLOR_BUFFER_BIT ); glViewport( 0, 0, glConfig.vidWidth >> 1, glConfig.vidHeight >> 1 ); // read from the original floatPbuffer glActiveTextureARB( GL_TEXTURE0_ARB ); glBindTexture( GL_TEXTURE_RECTANGLE_NV, floatPbufferImage->texnum ); R_BindTexImage( floatPbuffer ); glBindProgramARB( GL_VERTEX_PROGRAM_ARB, downSampleVertexProgram ); glBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, downSampleFragmentProgram ); RB_Backend_CoverScreen(); // // mip map again // glViewport( 0, 0, glConfig.vidWidth >> 2, glConfig.vidHeight >> 2 ); // draw to the second floatPbuffer R_MakeCurrent( floatPbufferQuarterDC, floatContext, floatPbufferQuarter ); // read from the original floatPbuffer glBindTexture( GL_TEXTURE_RECTANGLE_NV, floatPbuffer2Image->texnum ); R_BindTexImage( floatPbuffer2 ); RB_Backend_CoverScreen(); // // blur horizontally // /* # texture 0 is the high dynamic range buffer # writes result.color as the fp16 result of a smeared bloom PARAM step = program.local[0]; # { 1, 0 } or { 0, 1 } for horizontal / vertical separation */ // draw to the second floatPbuffer R_MakeCurrent( floatPbuffer2DC, floatContext, floatPbuffer2 ); glBindProgramARB( GL_VERTEX_PROGRAM_ARB, bloomVertexProgram ); glBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, bloomFragmentProgram ); glEnable( GL_VERTEX_PROGRAM_ARB ); glEnable( GL_FRAGMENT_PROGRAM_ARB ); GL_SelectTextureNoClient( 0 ); // blur horizontally first to the second floatPbuffer glBindTexture( GL_TEXTURE_RECTANGLE_NV, floatPbufferQuarterImage->texnum ); R_BindTexImage( floatPbufferQuarter ); float parm[4]; parm[0] = 1; parm[1] = 0; parm[2] = 0; parm[3] = 0; glProgramLocalParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 0, parm ); RB_Backend_CoverScreen(); // now blur vertically back to the quarter pbuffer R_MakeCurrent( floatPbufferQuarterDC, floatContext, floatPbufferQuarter ); glBindTexture( GL_TEXTURE_RECTANGLE_NV, floatPbuffer2Image->texnum ); R_BindTexImage( floatPbuffer2 ); parm[0] = 0; parm[1] = 1; parm[2] = 0; parm[3] = 0; glProgramLocalParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 0, parm ); RB_Backend_CoverScreen(); //======================== glEnable( GL_DEPTH_TEST ); glEnable( GL_SCISSOR_TEST ); glDisable( GL_VERTEX_PROGRAM_ARB ); glDisable( GL_FRAGMENT_PROGRAM_ARB ); GL_CheckErrors(); } /* ================== RB_Backend_DrawInteractions ================== */ void RB_Backend_DrawInteractions( void ) { if( !initialized ) { R_Backend_Allocate(); } if( !backEnd.viewDef->viewLights ) { return; } // validate the samples if( r_sb_samples.GetInteger() != 16 && r_sb_samples.GetInteger() != 4 && r_sb_samples.GetInteger() != 1 ) { r_sb_samples.SetInteger( 0 ); } // validate the light resolution if( r_sb_lightResolution.GetInteger() < 64 ) { r_sb_lightResolution.SetInteger( 64 ); } else if( r_sb_lightResolution.GetInteger() > maxLightBufferSize ) { r_sb_lightResolution.SetInteger( maxLightBufferSize ); } lightBufferSize = r_sb_lightResolution.GetInteger(); lightBufferSizeFraction = ( float )lightBufferSize / maxLightBufferSize; // validate the view resolution if( r_sb_viewResolution.GetInteger() < 64 ) { r_sb_viewResolution.SetInteger( 64 ); } else if( r_sb_viewResolution.GetInteger() > maxViewBufferSize ) { r_sb_viewResolution.SetInteger( maxViewBufferSize ); } viewBufferSize = r_sb_viewResolution.GetInteger(); viewBufferHeight = viewBufferSize * 3 / 4; viewBufferSizeFraction = ( float )viewBufferSize / maxViewBufferSize; viewBufferHeightFraction = ( float )viewBufferHeight / maxViewBufferSize; if( viewBufferSize == backEnd.viewDef->viewport.x2 - backEnd.viewDef->viewport.x1 + 1 ) { nativeViewBuffer = true; } else { nativeViewBuffer = false; } // set up for either point sampled or percentage-closer filtering for the shadow sampling shadowImage[0]->BindFragment(); if( r_sb_linearFilter.GetBool() ) { glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); } else { glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); } glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE ); globalImages->BindNull(); // copy the current depth buffer to a texture for image-space shadowing, // or re-render at a lower resolution R_Backend_RenderViewDepthImage(); GL_SelectTexture( 0 ); glDisableClientState( GL_TEXTURE_COORD_ARRAY ); // disable stencil shadow test glStencilFunc( GL_ALWAYS, 128, 255 ); // the jitter image will be used to offset sample centers GL_SelectTextureNoClient( 8 ); if( r_sb_samples.GetInteger() == 16 ) { jitterImage16->BindFragment(); } else if( r_sb_samples.GetInteger() == 4 ) { jitterImage4->BindFragment(); } else { jitterImage1->BindFragment(); } // if we are using a float buffer, clear it now if( r_hdr_useFloats.GetBool() ) { RB_Backend_SetRenderBuffer( NULL ); // we need to set a lot of things, because this is a completely different context RB_SetDefaultGLState(); glClearColor( 0.001f, 1.0f, 0.01f, 0.1f ); glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); // clear the z buffer, set the projection matrix, etc RB_BeginDrawingView(); RB_STD_FillDepthBuffer( ( drawSurf_t ** )&backEnd.viewDef->drawSurfs[0], backEnd.viewDef->numDrawSurfs ); } // // for each light, perform adding and shadowing // for( viewLight_t *vLight = backEnd.viewDef->viewLights; vLight; vLight = vLight->next ) { backEnd.vLight = vLight; const idMaterial *lightShader = vLight->lightShader; // do fogging later if( lightShader->IsFogLight() ) { continue; } if( lightShader->IsBlendLight() ) { continue; } if( !vLight->localInteractions && !vLight->globalInteractions && !vLight->translucentInteractions ) { continue; } if( !vLight->frustumTris->ambientCache ) { R_CreateAmbientCache( const_cast<srfTriangles_t *>( vLight->frustumTris ), false ); } // all light side projections must currently match, so non-centered // and non-cubic lights must take the largest length viewLightAxialSize = R_Backend_CalcLightAxialSize( vLight ); int side, sideStop; if( vLight->lightDef->parms.pointLight ) { if( r_sb_singleSide.GetInteger() != -1 ) { side = r_sb_singleSide.GetInteger(); sideStop = side + 1; } else { side = 0; sideStop = 6; } } else { side = -1; sideStop = 0; } for( ; side < sideStop; side++ ) { // FIXME: check for frustums completely off the screen // render a shadow buffer RB_RenderShadowBuffer( vLight, side ); // back to view rendering, possibly in the off-screen buffer if( nativeViewBuffer || !r_sb_screenSpaceShadow.GetBool() ) { // directly to screen RB_Backend_SetRenderBuffer( vLight ); } else { // to off screen buffer if( r_sb_usePbuffer.GetBool() ) { GL_CheckErrors(); // set the current openGL drawable to the shadow buffer R_MakeCurrent( viewPbufferDC, win32.hGLRC, viewPbuffer ); } glViewport( 0, 0, viewBufferSize, viewBufferHeight ); glScissor( 0, 0, viewBufferSize, viewBufferHeight ); // !@# FIXME: scale light scissor } // render the shadows into destination alpha on the included pixels RB_Backend_SelectFrustum( vLight, side ); if( !r_sb_screenSpaceShadow.GetBool() ) { // bind shadow buffer to texture GL_SelectTextureNoClient( 7 ); shadowImage[0]->BindFragment(); RB_Backend_CreateDrawInteractions( vLight->localInteractions ); RB_Backend_CreateDrawInteractions( vLight->globalInteractions ); backEnd.depthFunc = GLS_DEPTHFUNC_LESS; RB_Backend_CreateDrawInteractions( vLight->translucentInteractions ); backEnd.depthFunc = GLS_DEPTHFUNC_EQUAL; } } // render the native window coordinates interactions if( r_sb_screenSpaceShadow.GetBool() ) { if( !nativeViewBuffer ) { RB_shadowResampleAlpha(); glEnable( GL_STENCIL_TEST ); } else { RB_Backend_SetRenderBuffer( vLight ); if( r_ignore.GetBool() ) { glEnable( GL_STENCIL_TEST ); //!@# } else { glDisable( GL_STENCIL_TEST ); //!@# } } RB_Backend_CreateDrawInteractions( vLight->localInteractions ); RB_Backend_CreateDrawInteractions( vLight->globalInteractions ); backEnd.depthFunc = GLS_DEPTHFUNC_LESS; RB_Backend_CreateDrawInteractions( vLight->translucentInteractions ); backEnd.depthFunc = GLS_DEPTHFUNC_EQUAL; } } GL_SelectTexture( 0 ); glEnableClientState( GL_TEXTURE_COORD_ARRAY ); // experimental transfer function for( int i = 7; i >= 0; i-- ) { GL_SelectTextureNoClient( i ); globalImages->BindNull(); } GL_State( 0 ); RB_Backend_Bloom(); RB_Backend_GammaDither(); // these haven't been state saved for( int i = 0; i < 8; i++ ) { backEnd.glState.tmu[i].current2DMap = -1; } // take it out of texture compare mode so I can testImage it for debugging shadowImage[0]->BindFragment(); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE ); } /* ================== R_Backend_Init ================== */ void R_Backend_Init( void ) { glConfig.allowExpPath = false; common->Printf( "---------- R_Backend_Init ----------\n" ); if( !glConfig.ARBVertexProgramAvailable || !glConfig.ARBFragmentProgramAvailable ) { common->Printf( "Not available.\n" ); return; } RB_CreateBloomTable(); common->Printf( "Available.\n" ); if( !idStr::Icmp( r_renderer.GetString(), "exp" ) ) { R_Backend_Allocate(); } common->Printf( "--------------------------------------------\n" ); glConfig.allowExpPath = true; } I removed the nulled out code for getting the depthbuffer since we allready calculate it somewhere else. Also on the TODO: make codepaths for linux since wgl is windows only. Keep the use function names the same so that we can just drop in the new code without having to rewrite stuff. Sounds ok ?
  15. https://www.ttlg.com/forums/showthread.php?t=152224 There is a new mapping contest over on TTLG for the Thief: Deadly Shadows 20th Anniversary and the organizers were kind enough to include The Dark Mod along with all of the Thief games as an options for making a mission to submit as an entry. The deadline is a year from yesterday and the rules are pretty open. I recommend going to the original thread for the details but I will summarize here: Rules: - The mission(s) can be for Thief 1, Thief 2, Deadly Shadows or The Dark Mod. - Collaborations are allowed. - Contestants can use any custom resource they want, though TDM cannot use the Deadly Shadows resource pack. - Contestants can submit more than one mission. - Contestants can enter anonymously. - The mission(s) can be of any size. Using prefabs is allowed but the idea is this is a new mission and starting from an abandoned map or importing large areas from other maps is not allowed. Naturally this is on the honor system as we have no way of validating. Mission themes and contents: There is no requirement from a theme or story viewpoint, however contestants might consider that many players may expect or prefer missions to be celebratory of Thief: Deadly Shadows in this respect: castles, manors, museums, ruins inhabited by Pagans and the like, with a balance of magic versus technology. This is entirely up to the authors, though, to follow or not - it is just mentioned here as an FYI and, while individual voters may of course choose to vote higher or lower based on this on their own, it will not be a criteria used explicitly in voting or scoring. Deadline: May 25th, 2024 at 23:59 Pacific Time. See the TTLG thread for details on submissions and the voting process. Provided I can make the deadline I hope to participate. It would be nice to see the entire community do something together, and expressing our complicated relationship with this divisive game seems as good a pretext as any.
  16. What I understood is that the idea of TDM was born from that it was unclear if T3 would get a level editor at the time. Source: https://web.archive.org/web/20050218173856/http://evilavatar.com/forums/showthread.php?t=268
  17. This one is really essential: https://www.ttlg.com/forums/showthread.php?t=138607 Should work fine with the GOG version.
  18. During beta-testing, I had one tester report a black texture, but everyone else see is properly. The texture has an alpha channel, but that is the only unusual thing about it I can think of. Anyone able to test this, just add the entity movers/doors/atdm:mover_door_industrial01_grate to your map and see how it appears to you. Below is how it looks to me, and how it looks to Xarg:
  19. The Black Parade is coming! http://www.ttlg.com/forums/showthread.php?t=146501 Woo-hoo!

    1. Show previous comments  8 more
    2. demagogue

      demagogue

      I suppose making a campaign as an individual and building for Thief Gold go hand & hand. You need to be pretty obsessed to do either. XD

    3. Epifire

      Epifire

      I'm just very happy to see how much work still goes on with the original Thief games. Good mods = long life after release!

    4. lowenz
  20. I have returned to TheDarkMod after a few years (I played it a lot around 2015-16, even tried making a map, it did not go anywhere so I donated those few assets). The missions are great. In this one in particular, the navigation is awesome. The city is very good. I have noticed a trend though: the puzzles are more complicated; a lever may open something *somewhere*. Do you think that large TDM missions are suffering from "puzzle creep", that the veteran and in-community players do not notice? [/spoiler] So you have to go *all the way back* to use the crystal key on the teleporter thingy? And I don't know how to deal with the burning crystals either. I ended up killing nearly all enemies in the mages' place, with sword and holy water, because, yeah, I need to figure things out and I can't be bothered to stealth from one place to another anymore, lol. I will try checking the readables and environment with a bit more patience and see. [spoiler] The old format for spoiler tags does not work, not does changing the '/'. Wow, what a way to advertise "I am dumb" XD IMO the best way is to place gradual spoilers in the forum thread. I know it's a bit of a burden on the authors but answering 20 questions disjointly from different users is one as well.
  21. Personally I think Moonbo's Requiem FM is about as good as one could wish for in terms of a spiritual successor to the Thief trilogy. The story hits all the important notes, and the level design is uniformly top notch across all the expected axes of stealth gameplay. And there is even a sequel that trailblazes entirely new territory from its predecessor in gameplay, story, and tone. That's just icing on the cake. Indeed there several FMs for TDM that I believe equal or even surpass the quality of the original games' levels. The trouble is how do you discover them in that giant downloader list? Are there other great missions in there that I have yet to find? That is undoubtedly the greatest current weakness of this project. The ability to sort FMs in the game client (i.e. both the downloader and the mission launcher) by date, size, and author would be a great help. Search by keywords or tags and support for grouping missions into collections would also be very useful.
    1. SeriousToni

      SeriousToni

      What? The mysterious saviour is back to make our Thief lives happier again after such a long absence? I can't believe it's true! Wow thanks so much!

    2. demagogue

      demagogue

      He's like our own Game of Thones mystery crow that leads us to secret treasures.

    3. SeriousToni

      SeriousToni

      Or to an old tree with lots of zombies around.. xD

  22. OK, I'll try to rearrange to that directory layout. I'll have to lookup how to do that without screwing up Tortoise. I'm not on that machine at the moment, so I can't check your BTW. I don't remember about "branches", but I do remember a local "tags" directory... don't know if it was populated.
  23. Ideally, you should have checked out "trunk" directory only, so that its contents get into "tdm11dev/darkmod_src" instead of "tdm11dev/darkmod_src/trunk". Do you BTW have all the "branches" and "tags" directories near "trunk" ? Maybe not, because they are still closed for public for some weird reason...
×
×
  • Create New...