Merge branch 'master' into 635-nvrhi1

This commit is contained in:
Robert Beckebans 2022-03-30 12:08:40 +02:00
commit a18d2609ce
34 changed files with 2647 additions and 310 deletions

View file

@ -646,6 +646,34 @@ They are also licensed under the MIT open source license, if you have lawyers wh
Every source file includes an explicit dual-license for you to choose from.
BinPack2D is a 2 dimensional, multi-bin, bin-packer.
-----------------------------------------------------------------------------
neo/libs/binpack2d/*
Copyright (c) 2013, Christopher Stones < chris.stones@zoho.com >
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Research Samples by Morgan McGuire
-----------------------------------------------------------------------------
base/renderprogs/builtin/SSAO/* (AlchemyAO)

View file

@ -116,14 +116,16 @@ Very certain short term goals are to port and extend some improvements from Just
* Native C++ AI & monsters code
* Quake 3 gladiator multiplayer bot
Other short term goals:
* DX12/Vulkan renderer backend using the [NVIDIA Rendering Hardware Interface](https://github.com/NVIDIAGameWorks/nvrhi) by Stephen Pridham which will lead the path to advanced Ray Tracing techniques
* Optional RmlUI support as an alternative to Flash
---
# May or may not ".plan" <a name="plan2"></a>
* Scrap expensive multipass forward shading with a faster forward+ solution
* Add [Volumetric Lighting](http://www.alexandre-pestana.com/volumetric-lights/)
* Add a DX12 Ultimate renderer backend
* Explore Screen Space Global Illumination with Christoph Schieds' A-SVGF realtime denoising because A-SVGF works really well in Q2RTX
* Update texture compression based on [Basis Universal GPU Texture and Texture Video Compression Codec](https://github.com/binomialLLC/basis_universal)
* Rip & Tear renderer backend with modern approaches by [The-Forge](https://github.com/ConfettiFX/The-Forge)
* Replace collision detection and physics with PhysX 4.1
---
@ -654,9 +656,9 @@ dmap `[glview]` mapfile | DMap option that exports the BSP areas
bakeEnvironmentProbes | Command after loading a map. Captures all env_probe entities and stores them to disc
bakeLightGrids [`<switches>`...] | `<Switches>` limit[num] : max probes per BSP area (default 16384) bounce[num] : number of bounces or number of light reuse (default 1) grid( xdim ydim zdim ) : light grid size steps into each direction (default 64 64 128)
exportScriptEvents | Command: Generates a new script/doom_events.script that reflects all registered class events in the idClass C++ system. The gamecode still needs to be extended to add the original comments of the events
exportFGD `[nomodels]` | Command: Exports all entity defs to exported/_tb/*.fgd for usage in convertMapToValve220 `<map>` |
exportImagesToTrenchBroom | Command: Decompresses and saves all TB relevant .bimage images to _tb/*.png files
exportModelsToTrenchBroom | Command: Saves all .base|.blwo|.bmd5mesh models to _tb/*.obj files
exportFGD `[nomodels]` | Command: Exports all entity defs to base/_tb/*.fgd for usage in convertMapToValve220 `<map>` |
exportImagesToTrenchBroom | Command: Decompresses and saves all TB relevant .bimage images to base/_tb/*.png files
exportModelsToTrenchBroom | Command: Saves all .base|.blwo|.bmd5mesh models to base/_tb/*.obj files
exportEntityDefsToBlender | Command: Exports all entity and model defs to exported/entities.json for usage in Blender
before loading a map.
exportMapToOBJ | Command: Convert .map file to .obj/.mtl
@ -676,7 +678,6 @@ convertMapToJSON mapfile | Command: Convert .map file to new .json
* Some lights cause shadow acne with shadow mapping
* Some shadows might almost disappear due to the shadow filtering or look off ("Peter panning" problem)
* [HDR] HDR does not work with old-school stencil shadows
* [HDR] MSAA anti-aliasing modes don't work with HDR: Use SMAA with r_antiAliasing 1
* [Vulkan] **Vulkan backend is unfinished in general**
* [Vulkan] Shadow Mapping is not supported yet

View file

@ -15,15 +15,27 @@ Thank you for downloading RBDOOM-3-BFG.
_______________________________________
TBD - RBDOOM-3-BFG 1.4.0
06 March 2022 - RBDOOM-3-BFG 1.4.0
_______________________________
<img src="https://i.imgur.com/3sUxOZi.jpg">
<img src="https://i.imgur.com/ez4M4PE.jpg">
<img src="https://i.imgur.com/8j4VmuR.jpg">
## .plan
This version improves support for mapping with TrenchBroom. Until now you needed to extract and copy the vanilla Doom 3 models and textures over to the base/ folder to see the content in the TrenchBroom entity browser and texture viewer.
Owning the original game next to the BFG edition is not necessary anymore.
This version comes with a couple of new console commands that lets you export particular parts of the .resources files to the base/_tb/ folder.
You need to call exportImagesToTrenchBroom and exportModelsToTrenchBroom once and you are good to go to start mapping with the TrenchBroom level editor.
This version comes with a couple of new RBDOOM-3-BFG console commands that lets you export particular parts of the .resources files to the base/_tb/ folder.
You need to call exportImagesToTrenchBroom once and you are good to go to start mapping with the TrenchBroom level editor.
TrenchBroom comes with several more Doom 3 specific changes. After loading a map TrenchBroom generates unique entity names and also fixes missing or bad "model" keys for brush based entitites.
Also creating new entities like light will automatically receive names like light_2.
This patch also contains a couple of func_group related bugfixes. func_group works now with brush based entities, point entities and just regular brushes.
## Changelog
@ -37,16 +49,25 @@ You need to call exportImagesToTrenchBroom and exportModelsToTrenchBroom once an
* Drastically improved loading time of textures for materials in TrenchBroom
* Added RBDoom console command convertMapToValve220 `<map>`
* Added RBDoom console command convertMapToValve220 `<map>`. You can also type `exec convert_maps_to_valve220.cfg` to convert all Doom 3 .map files into the TrenchBroom friendly format. Converted maps are saved with the _valve220.map suffix.
* Added RBDoom console command exportImagesToTrenchBroom which decompresses and saves all .bimage images to _tb/*.png files
* Added RBDoom console command exportImagesToTrenchBroom which decompresses and saves all .bimage images to base/_tb/*.png files
* Added RBDoom console command exportModelsToTrenchBroom which saves all .base|.blwo|.bmd5mesh models to _tb/*.obj files
* Added RBDoom console command exportModelsToTrenchBroom which saves all .base|.blwo|.bmd5mesh models to _tb/*.obj proxy files. This commands also generates helper entities for TrenchBroom so all mapobject/models are also available in the Entity Inspector using the DOOM-3-models.fgd.
* Added RBDoom console command makeZooMapForModels which makes a Source engine style zoo map with mapobject/models like .blwo, .base et cetera and saves it to maps/zoomaps/zoo_models.map. This helps mappers to get a good overview of the trememdous amount of custom models available in Doom 3 BFG by sorting them into categories and arranging them in 3D. It also filters models so that only modular models are picked that can be reused in new maps.
* TrenchBroom got several Doom 3 specific issue generators to help mappers avoiding pitfalls during mapping
* Changed TrenchBroom's rotation tool to use the "angles" key by default to remove some Quake related limitations
[MISCELLANEOUS]
* Stencil shadows work again (thanks to Stephen Pridham)
* Fixed black screen after using the reloadImages command
* Added CMake options STANDALONE and DOOM_CLASSIC
* Added command convertMapQuakeToDoom `<map>` that expects a Quake 1 .map in the Valve220 format and does some Doom 3 specific fixes
@ -80,6 +101,10 @@ Steve Saunders contributed
* Added base/convert_maps_to_valve220.cfg which lets you convert all maps to the Valve 220 .map format in one shot
* Added base/maps/zoomaps/zoo_models.map
_______________________________________
30 October 2021 - RBDOOM-3-BFG 1.3.0 - Download it from the [RBDOOM-3-BFG ModDB Page](https://www.moddb.com/mods/rbdoom-3-bfg)

View file

@ -2,8 +2,8 @@ entityDef misc_model
{
"inherit" "func_static"
"editor_color" "0 .5 .8"
"editor_mins" "-12 -12 -12"
"editor_maxs" "12 12 12"
"editor_mins" "?"
"editor_maxs" "?"
"editor_rotatable" "1"
"editor_usage" "Inherits from a func_static but uses a model and is a FGD PointClass so it displays correctly in TrenchBroom."

View file

@ -229,7 +229,7 @@ this is the canonical renderEntity parm parsing,
which should be used by dmap and the editor
================
*/
void idGameEdit::ParseSpawnArgsToRenderEntity( const idDict* args, renderEntity_t* renderEntity )
void idGameEdit::ParseSpawnArgsToRenderEntity( const idDict* args, renderEntity_t* renderEntity, const idDeclEntityDef* def )
{
int i;
const char* temp;
@ -254,6 +254,33 @@ void idGameEdit::ParseSpawnArgsToRenderEntity( const idDict* args, renderEntity_
renderEntity->hModel = renderModelManager->FindModel( temp );
}
}
// RB: TrenchBroom interop
else if( def != NULL )
{
// if the name starts with genmodel_ it means that it was generated by exportModelsToTrenchBroom
// and we have a special entityDef for it in base/def/_tb_models.def
temp = args->GetString( "name" );
if( idStr::Icmpn( temp, "genmodel_", 9 ) == 0 )
{
// grab the model key from the definition instead
temp = def->dict.GetString( "model" );
if( temp[0] != '\0' )
{
modelDef = static_cast<const idDeclModelDef*>( declManager->FindType( DECL_MODELDEF, temp, false ) );
if( modelDef )
{
renderEntity->hModel = modelDef->ModelHandle();
}
if( !renderEntity->hModel )
{
renderEntity->hModel = renderModelManager->FindModel( temp );
}
}
}
}
// RB end
if( renderEntity->hModel )
{
renderEntity->bounds = renderEntity->hModel->Bounds( renderEntity );
@ -637,7 +664,7 @@ void idEntity::Spawn()
health = spawnArgs.GetInt( "health" );
InitDefaultPhysics( origin, axis );
InitDefaultPhysics( origin, axis, def );
SetOrigin( origin );
SetAxis( axis );
@ -652,6 +679,27 @@ void idEntity::Spawn()
origin += GetOriginBrushOffset();
SetOrigin( origin );
}
// RB: TrenchBroom interop
else if( def != NULL )
{
// if the name starts with genmodel_ it means that it was generated by exportModelsToTrenchBroom
// and we have a special entityDef for it in base/def/_tb_models.def
if( idStr::Icmpn( name.c_str(), "genmodel_", 9 ) == 0 )
{
// grab the model key from the definition instead
temp = def->dict.GetString( "model" );
if( temp != NULL && *temp != '\0' )
{
SetModel( temp );
// Entities without models don't have origin brushes,
// so it makes sense to apply this only IF there is a model
origin += GetOriginBrushOffset();
SetOrigin( origin );
}
}
}
// RB end
if( spawnArgs.GetString( "bind", "", &temp ) )
{
@ -2834,7 +2882,7 @@ void idEntity::QuitTeam()
idEntity::InitDefaultPhysics
================
*/
void idEntity::InitDefaultPhysics( const idVec3& origin, const idMat3& axis )
void idEntity::InitDefaultPhysics( const idVec3& origin, const idMat3& axis, const idDeclEntityDef* def )
{
const char* temp;
idClipModel* clipModel = NULL;
@ -2910,6 +2958,25 @@ void idEntity::InitDefaultPhysics( const idVec3& origin, const idMat3& axis )
clipModel = new( TAG_PHYSICS_CLIP_ENTITY ) idClipModel( temp );
}
}
// RB: TrenchBroom interop
else if( def != NULL )
{
// if the name starts with genmodel_ it means that it was generated by exportModelsToTrenchBroom
// and we have a special entityDef for it in base/def/_tb_models.def
if( idStr::Icmpn( name.c_str(), "genmodel_", 9 ) == 0 )
{
// grab the model key from the definition instead
temp = def->dict.GetString( "model" );
if( ( temp != NULL ) && ( *temp != 0 ) )
{
if( idClipModel::CheckModel( temp ) )
{
clipModel = new( TAG_PHYSICS_CLIP_ENTITY ) idClipModel( temp );
}
}
}
}
// RB end
}
}

View file

@ -556,7 +556,7 @@ private:
// physics
// initialize the default physics
void InitDefaultPhysics( const idVec3& origin, const idMat3& axis );
void InitDefaultPhysics( const idVec3& origin, const idMat3& axis, const idDeclEntityDef* def );
// update visual position from the physics
void UpdateFromPhysics( bool moveBack );
// get physics timestep

View file

@ -240,7 +240,7 @@ public:
// These are the canonical idDict to parameter parsing routines used by both the game and tools.
virtual void ParseSpawnArgsToRenderLight( const idDict* args, renderLight_t* renderLight );
virtual void ParseSpawnArgsToRenderEntity( const idDict* args, renderEntity_t* renderEntity );
virtual void ParseSpawnArgsToRenderEntity( const idDict* args, renderEntity_t* renderEntity, const idDeclEntityDef* def = NULL );
virtual void ParseSpawnArgsToRenderEnvprobe( const idDict* args, renderEnvironmentProbe_t* renderEnvprobe ); // RB
virtual void ParseSpawnArgsToRefSound( const idDict* args, refSound_t* refSound );

View file

@ -1581,10 +1581,6 @@ void idCommonLocal::Shutdown()
printf( "uiManager->Shutdown();\n" );
uiManager->Shutdown();
// shut down the sound system
printf( "soundSystem->Shutdown();\n" );
soundSystem->Shutdown();
// shut down the user command input code
printf( "usercmdGen->Shutdown();\n" );
usercmdGen->Shutdown();
@ -1594,9 +1590,16 @@ void idCommonLocal::Shutdown()
eventLoop->Shutdown();
// shutdown the decl manager
// SRS - Note this also shuts down all cinematic resources, including cinematic audio voices
printf( "declManager->Shutdown();\n" );
declManager->Shutdown();
// shut down the sound system
// SRS - Shut down sound system after decl manager so cinematic audio voices are destroyed first
// Important for XAudio2 where the mastering voice cannot be destroyed if any other voices exist
printf( "soundSystem->Shutdown();\n" );
soundSystem->Shutdown();
// shut down the renderSystem
printf( "renderSystem->Shutdown();\n" );
renderSystem->Shutdown();

File diff suppressed because it is too large Load diff

View file

@ -862,7 +862,7 @@ void idCommonLocal::Frame()
// RB begin
#if defined(USE_DOOMCLASSIC)
// If we're in Doom or Doom 2, run tics and upload the new texture.
// SRS - Add check for com_pause cvar to make sure window is in focus - if not classic game should be paused (FIXME: but classic music still plays in background)
// SRS - Add check for com_pause cvar to make sure window is in focus - if not classic game should be paused
if( ( GetCurrentGame() == DOOM_CLASSIC || GetCurrentGame() == DOOM2_CLASSIC ) && !( Dialog().IsDialogPausing() || session->IsSystemUIShowing() || com_pause.GetInteger() ) )
{
RunDoomClassicFrame();
@ -922,16 +922,18 @@ void idCommonLocal::Frame()
{
soundWorld->Pause();
soundSystem->SetPlayingSoundWorld( menuSoundWorld );
soundSystem->SetMute( false );
}
else
{
soundWorld->UnPause();
soundSystem->SetPlayingSoundWorld( soundWorld );
soundSystem->SetMute( false );
}
// SRS - Play silence when dialog waiting or window not in focus
// SRS - Mute all sound output when dialog waiting or window not in focus (mutes Doom3, Classic, Cinematic Audio)
if( Dialog().IsDialogPausing() || session->IsSystemUIShowing() || com_pause.GetInteger() )
{
soundSystem->SetPlayingSoundWorld( NULL );
soundSystem->SetMute( true );
}
soundSystem->Render();

View file

@ -136,6 +136,11 @@ inline bool BrushPrimitive_Degenerate( const idVec3& bpTexMatX, const idVec3& bp
// heavily inspired by Valve220_from_BP from Netradiant-custom
void idMapBrushSide::ConvertToValve220Format( const idMat4& entityTransform, idStrList& textureCollections )
{
if( projection == idMapBrushSide::PROJECTION_VALVE220 )
{
return;
}
// create p1, p2, p3
idVec3 forward = plane.Normal();
idVec3 p1 = forward * plane.Dist();
@ -215,6 +220,16 @@ void idMapBrushSide::ConvertToValve220Format( const idMat4& entityTransform, idS
texScale[0] = 1.0f / idMath::Sqrt( localMat[0][0] * localMat[0][0] + localMat[1][0] * localMat[1][0] );
texScale[1] = 1.0f / idMath::Sqrt( localMat[0][1] * localMat[0][1] + localMat[1][1] * localMat[1][1] );
if( IsNAN( texScale[0] ) )
{
texScale[0] = 0.5f;
}
if( IsNAN( texScale[1] ) )
{
texScale[1] = 0.5f;
}
if( texMat[0][0] < idMath::FLOAT_EPSILON )
{
texValve[0] = -texValve[0];
@ -2904,7 +2919,7 @@ bool idMapFile::ConvertToValve220Format()
const idKeyValue* modelPair = ent->epairs.FindKey( "model" );
idStr model = ent->epairs.GetString( "model" );
#if 1
// HACK: convert every old .lwo, .ase model to .obj
// HACK: convert every old .lwo, .ase model to an .obj proxy model so it can be displayed properly in TrenchBroom
idStr ext;
model.ExtractFileExtension( ext );
@ -2913,7 +2928,7 @@ bool idMapFile::ConvertToValve220Format()
model.SetFileExtension( "obj" );
model = "_tb/" + model;
ent->epairs.Set( "model", model );
ent->epairs.Set( "proxymodel", model );
}
#endif
// is this oldschool brushes & patches?
@ -3793,8 +3808,271 @@ static quakeToDoom_t textureConvertNames[] =
{"textures/wwood1_7", "textures/id1/wwood1_7"},
{"textures/z_exit", "textures/id1/z_exit"},
// prototype.wad
{"textures/#64_water1_1", "textures/prototype/64_water1_1"},
{"textures/#64_water1_2", "textures/prototype/64_water1_2"},
{"textures/#64_water1_3", "textures/prototype/64_water1_3"},
{"textures/#64_water2_1", "textures/prototype/64_water2_1"},
{"textures/#64_water2_2", "textures/prototype/64_water2_2"},
{"textures/#64_water2_3", "textures/prototype/64_water2_3"},
{"textures/#lava2_1", "textures/prototype/lava2_1"},
{"textures/#lava2_2", "textures/prototype/lava2_2"},
{"textures/#lava2_3", "textures/prototype/lava2_3"},
{"textures/#lava_64_1", "textures/prototype/lava_64_1"},
{"textures/#lava_64_2", "textures/prototype/lava_64_2"},
{"textures/#lava_64_3", "textures/prototype/lava_64_3"},
{"textures/#lavaskip", "textures/prototype/lavaskip"},
{"textures/#slime2_1", "textures/prototype/slime2_1"},
{"textures/#slime2_2", "textures/prototype/slime2_2"},
{"textures/#slime2_3", "textures/prototype/slime2_3"},
{"textures/#slimeskip", "textures/prototype/slimeskip"},
{"textures/#tele01", "textures/prototype/tele01"},
{"textures/#water3_1", "textures/prototype/water3_1"},
{"textures/#water3_2", "textures/prototype/water3_2"},
{"textures/#water3_3", "textures/prototype/water3_3"},
{"textures/#waterskip", "textures/prototype/waterskip"},
{"textures/+0button_0", "textures/prototype/0button_0"},
{"textures/+0button_1", "textures/prototype/0button_1"},
{"textures/+0button_2", "textures/prototype/0button_2"},
{"textures/+0button_3", "textures/prototype/0button_3"},
{"textures/+0shbtn_1", "textures/prototype/0shbtn_1"},
{"textures/+0shbtn_2", "textures/prototype/0shbtn_2"},
{"textures/+0shbtn_3", "textures/prototype/0shbtn_3"},
{"textures/+0shbtn_4", "textures/prototype/0shbtn_4"},
{"textures/+0shbtn_5", "textures/prototype/0shbtn_5"},
{"textures/+0shbtn_6", "textures/prototype/0shbtn_6"},
{"textures/+0shbtn_7", "textures/prototype/0shbtn_7"},
{"textures/+1button_0", "textures/prototype/1button_0"},
{"textures/+1button_1", "textures/prototype/1button_1"},
{"textures/+1button_2", "textures/prototype/1button_2"},
{"textures/+1button_3", "textures/prototype/1button_3"},
{"textures/+1shbtn_1", "textures/prototype/1shbtn_1"},
{"textures/+1shbtn_2", "textures/prototype/1shbtn_2"},
{"textures/+1shbtn_3", "textures/prototype/1shbtn_3"},
{"textures/+1shbtn_4", "textures/prototype/1shbtn_4"},
{"textures/+1shbtn_5", "textures/prototype/1shbtn_5"},
{"textures/+1shbtn_6", "textures/prototype/1shbtn_6"},
{"textures/+1shbtn_7", "textures/prototype/1shbtn_7"},
{"textures/128_blood_1", "textures/prototype/128_blood_1"},
{"textures/128_blood_2", "textures/prototype/128_blood_2"},
{"textures/128_blood_3", "textures/prototype/128_blood_3"},
{"textures/128_blue_1", "textures/prototype/128_blue_1"},
{"textures/128_blue_2", "textures/prototype/128_blue_2"},
{"textures/128_blue_3", "textures/prototype/128_blue_3"},
{"textures/128_brown_1", "textures/prototype/128_brown_1"},
{"textures/128_brown_2", "textures/prototype/128_brown_2"},
{"textures/128_brown_3", "textures/prototype/128_brown_3"},
{"textures/128_cyan_1", "textures/prototype/128_cyan_1"},
{"textures/128_cyan_2", "textures/prototype/128_cyan_2"},
{"textures/128_cyan_3", "textures/prototype/128_cyan_3"},
{"textures/128_gold_1", "textures/prototype/128_gold_1"},
{"textures/128_gold_2", "textures/prototype/128_gold_2"},
{"textures/128_gold_3", "textures/prototype/128_gold_3"},
{"textures/128_green_1", "textures/prototype/128_green_1"},
{"textures/128_green_2", "textures/prototype/128_green_2"},
{"textures/128_green_3", "textures/prototype/128_green_3"},
{"textures/128_grey_1", "textures/prototype/128_grey_1"},
{"textures/128_grey_2", "textures/prototype/128_grey_2"},
{"textures/128_grey_3", "textures/prototype/128_grey_3"},
{"textures/128_honey_1", "textures/prototype/128_honey_1"},
{"textures/128_honey_2", "textures/prototype/128_honey_2"},
{"textures/128_honey_3", "textures/prototype/128_honey_3"},
{"textures/16_blood_1", "textures/prototype/16_blood_1"},
{"textures/16_blood_2", "textures/prototype/16_blood_2"},
{"textures/16_blood_3", "textures/prototype/16_blood_3"},
{"textures/16_blue_1", "textures/prototype/16_blue_1"},
{"textures/16_blue_2", "textures/prototype/16_blue_2"},
{"textures/16_blue_3", "textures/prototype/16_blue_3"},
{"textures/16_brown_1", "textures/prototype/16_brown_1"},
{"textures/16_brown_2", "textures/prototype/16_brown_2"},
{"textures/16_brown_3", "textures/prototype/16_brown_3"},
{"textures/16_cyan_1", "textures/prototype/16_cyan_1"},
{"textures/16_cyan_2", "textures/prototype/16_cyan_2"},
{"textures/16_cyan_3", "textures/prototype/16_cyan_3"},
{"textures/16_gold_1", "textures/prototype/16_gold_1"},
{"textures/16_gold_2", "textures/prototype/16_gold_2"},
{"textures/16_gold_3", "textures/prototype/16_gold_3"},
{"textures/16_green_1", "textures/prototype/16_green_1"},
{"textures/16_green_2", "textures/prototype/16_green_2"},
{"textures/16_green_3", "textures/prototype/16_green_3"},
{"textures/16_grey_1", "textures/prototype/16_grey_1"},
{"textures/16_grey_2", "textures/prototype/16_grey_2"},
{"textures/16_grey_3", "textures/prototype/16_grey_3"},
{"textures/16_honey_1", "textures/prototype/16_honey_1"},
{"textures/16_honey_2", "textures/prototype/16_honey_2"},
{"textures/16_honey_3", "textures/prototype/16_honey_3"},
{"textures/32_blood_1", "textures/prototype/32_blood_1"},
{"textures/32_blood_2", "textures/prototype/32_blood_2"},
{"textures/32_blood_3", "textures/prototype/32_blood_3"},
{"textures/32_blue_1", "textures/prototype/32_blue_1"},
{"textures/32_blue_2", "textures/prototype/32_blue_2"},
{"textures/32_blue_3", "textures/prototype/32_blue_3"},
{"textures/32_brown_1", "textures/prototype/32_brown_1"},
{"textures/32_brown_2", "textures/prototype/32_brown_2"},
{"textures/32_brown_3", "textures/prototype/32_brown_3"},
{"textures/32_cyan_1", "textures/prototype/32_cyan_1"},
{"textures/32_cyan_2", "textures/prototype/32_cyan_2"},
{"textures/32_cyan_3", "textures/prototype/32_cyan_3"},
{"textures/32_gold_1", "textures/prototype/32_gold_1"},
{"textures/32_gold_2", "textures/prototype/32_gold_2"},
{"textures/32_gold_3", "textures/prototype/32_gold_3"},
{"textures/32_green_1", "textures/prototype/32_green_1"},
{"textures/32_green_2", "textures/prototype/32_green_2"},
{"textures/32_green_3", "textures/prototype/32_green_3"},
{"textures/32_grey_1", "textures/prototype/32_grey_1"},
{"textures/32_grey_2", "textures/prototype/32_grey_2"},
{"textures/32_grey_3", "textures/prototype/32_grey_3"},
{"textures/32_honey_1", "textures/prototype/32_honey_1"},
{"textures/32_honey_2", "textures/prototype/32_honey_2"},
{"textures/32_honey_3", "textures/prototype/32_honey_3"},
{"textures/64_blood_1", "textures/prototype/64_blood_1"},
{"textures/64_blood_2", "textures/prototype/64_blood_2"},
{"textures/64_blood_3", "textures/prototype/64_blood_3"},
{"textures/64_blue_1", "textures/prototype/64_blue_1"},
{"textures/64_blue_2", "textures/prototype/64_blue_2"},
{"textures/64_blue_3", "textures/prototype/64_blue_3"},
{"textures/64_brown_1", "textures/prototype/64_brown_1"},
{"textures/64_brown_2", "textures/prototype/64_brown_2"},
{"textures/64_brown_3", "textures/prototype/64_brown_3"},
{"textures/64_cyan_1", "textures/prototype/64_cyan_1"},
{"textures/64_cyan_2", "textures/prototype/64_cyan_2"},
{"textures/64_cyan_3", "textures/prototype/64_cyan_3"},
{"textures/64_gold_1", "textures/prototype/64_gold_1"},
{"textures/64_gold_2", "textures/prototype/64_gold_2"},
{"textures/64_gold_3", "textures/prototype/64_gold_3"},
{"textures/64_green_1", "textures/prototype/64_green_1"},
{"textures/64_green_2", "textures/prototype/64_green_2"},
{"textures/64_green_3", "textures/prototype/64_green_3"},
{"textures/64_grey_1", "textures/prototype/64_grey_1"},
{"textures/64_grey_3", "textures/prototype/64_grey_3"},
{"textures/64_grey_4", "textures/prototype/64_grey_4"},
{"textures/64_honey_1", "textures/prototype/64_honey_1"},
{"textures/64_honey_2", "textures/prototype/64_honey_2"},
{"textures/64_honey_3", "textures/prototype/64_honey_3"},
{"textures/blood_1", "textures/prototype/blood_1"},
{"textures/blood_2", "textures/prototype/blood_2"},
{"textures/blood_3", "textures/prototype/blood_3"},
{"textures/blue_1", "textures/prototype/blue_1"},
{"textures/blue_2", "textures/prototype/blue_2"},
{"textures/blue_3", "textures/prototype/blue_3"},
{"textures/brown_1", "textures/prototype/brown_1"},
{"textures/brown_2", "textures/prototype/brown_2"},
{"textures/brown_3", "textures/prototype/brown_3"},
{"textures/clip", "textures/prototype/clip"},
{"textures/cyan_1", "textures/prototype/cyan_1"},
{"textures/cyan_2", "textures/prototype/cyan_2"},
{"textures/cyan_3", "textures/prototype/cyan_3"},
{"textures/fullbright_1", "textures/prototype/fullbright_1"},
{"textures/fullbright_2", "textures/prototype/fullbright_2"},
{"textures/fullbright_3", "textures/prototype/fullbright_3"},
{"textures/fullbright_4", "textures/prototype/fullbright_4"},
{"textures/fullbright_5", "textures/prototype/fullbright_5"},
{"textures/fullbright_6", "textures/prototype/fullbright_6"},
{"textures/fullbright_7", "textures/prototype/fullbright_7"},
{"textures/fullbright_8", "textures/prototype/fullbright_8"},
{"textures/fullbright_9", "textures/prototype/fullbright_9"},
{"textures/gold_1", "textures/prototype/gold_1"},
{"textures/gold_2", "textures/prototype/gold_2"},
{"textures/gold_3", "textures/prototype/gold_3"},
{"textures/green_1", "textures/prototype/green_1"},
{"textures/green_2", "textures/prototype/green_2"},
{"textures/green_3", "textures/prototype/green_3"},
{"textures/grey_1", "textures/prototype/grey_1"},
{"textures/grey_2", "textures/prototype/grey_2"},
{"textures/grey_3", "textures/prototype/grey_3"},
{"textures/hint", "textures/prototype/hint"},
{"textures/hintskip", "textures/prototype/hintskip"},
{"textures/honey_1", "textures/prototype/honey_1"},
{"textures/honey_2", "textures/prototype/honey_2"},
{"textures/honey_3", "textures/prototype/honey_3"},
{"textures/key_gold_1", "textures/prototype/key_gold_1"},
{"textures/key_silver_1", "textures/prototype/key_silver_1"},
{"textures/light1_big", "textures/prototype/light1_big"},
{"textures/light1_grt", "textures/prototype/light1_grt"},
{"textures/light1_sml", "textures/prototype/light1_sml"},
{"textures/origin", "textures/common/origin"},
{"textures/skip", "textures/prototype/skip"},
{"textures/sky1", "textures/prototype/sky1"},
{"textures/sky2", "textures/prototype/sky2"},
{"textures/sky_blood_1", "textures/prototype/sky_blood_1"},
{"textures/sky_blood_2", "textures/prototype/sky_blood_2"},
{"textures/sky_blood_3", "textures/prototype/sky_blood_3"},
{"textures/sky_blue_1", "textures/prototype/sky_blue_1"},
{"textures/sky_blue_2", "textures/prototype/sky_blue_2"},
{"textures/sky_blue_3", "textures/prototype/sky_blue_3"},
{"textures/sky_brown_1", "textures/prototype/sky_brown_1"},
{"textures/sky_brown_2", "textures/prototype/sky_brown_2"},
{"textures/sky_brown_3", "textures/prototype/sky_brown_3"},
{"textures/sky_cyan_1", "textures/prototype/sky_cyan_1"},
{"textures/sky_cyan_2", "textures/prototype/sky_cyan_2"},
{"textures/sky_cyan_3", "textures/prototype/sky_cyan_3"},
{"textures/sky_gold_1", "textures/prototype/sky_gold_1"},
{"textures/sky_gold_2", "textures/prototype/sky_gold_2"},
{"textures/sky_gold_3", "textures/prototype/sky_gold_3"},
{"textures/sky_green_1", "textures/prototype/sky_green_1"},
{"textures/sky_green_2", "textures/prototype/sky_green_2"},
{"textures/sky_green_3", "textures/prototype/sky_green_3"},
{"textures/sky_grey_1", "textures/prototype/sky_grey_1"},
{"textures/sky_grey_2", "textures/prototype/sky_grey_2"},
{"textures/sky_grey_3", "textures/prototype/sky_grey_3"},
{"textures/sky_honey_1", "textures/prototype/sky_honey_1"},
{"textures/sky_honey_2", "textures/prototype/sky_honey_2"},
{"textures/sky_honey_3", "textures/prototype/sky_honey_3"},
{"textures/sky_void", "textures/prototype/sky_void"},
{"textures/text_0", "textures/prototype/text_0"},
{"textures/text_1", "textures/prototype/text_1"},
{"textures/text_2", "textures/prototype/text_2"},
{"textures/text_3", "textures/prototype/text_3"},
{"textures/text_4", "textures/prototype/text_4"},
{"textures/text_5", "textures/prototype/text_5"},
{"textures/text_6", "textures/prototype/text_6"},
{"textures/text_7", "textures/prototype/text_7"},
{"textures/text_8", "textures/prototype/text_8"},
{"textures/text_9", "textures/prototype/text_9"},
{"textures/text_A", "textures/prototype/text_A"},
{"textures/text_B", "textures/prototype/text_B"},
{"textures/text_C", "textures/prototype/text_C"},
{"textures/text_D", "textures/prototype/text_D"},
{"textures/text_E", "textures/prototype/text_E"},
{"textures/text_F", "textures/prototype/text_F"},
{"textures/text_G", "textures/prototype/text_G"},
{"textures/text_H", "textures/prototype/text_H"},
{"textures/text_I", "textures/prototype/text_I"},
{"textures/text_J", "textures/prototype/text_J"},
{"textures/text_K", "textures/prototype/text_K"},
{"textures/text_L", "textures/prototype/text_L"},
{"textures/text_M", "textures/prototype/text_M"},
{"textures/text_N", "textures/prototype/text_N"},
{"textures/text_O", "textures/prototype/text_O"},
{"textures/text_P", "textures/prototype/text_P"},
{"textures/text_Q", "textures/prototype/text_Q"},
{"textures/text_R", "textures/prototype/text_R"},
{"textures/text_S", "textures/prototype/text_S"},
{"textures/text_T", "textures/prototype/text_T"},
{"textures/text_U", "textures/prototype/text_U"},
{"textures/text_V", "textures/prototype/text_V"},
{"textures/text_W", "textures/prototype/text_W"},
{"textures/text_X", "textures/prototype/text_X"},
{"textures/text_Y", "textures/prototype/text_Y"},
{"textures/text_Z", "textures/prototype/text_Z"},
{"textures/text_amper", "textures/prototype/text_amper"},
{"textures/text_arrow", "textures/prototype/text_arrow"},
{"textures/text_circle", "textures/prototype/text_circle"},
{"textures/text_comma", "textures/prototype/text_comma"},
{"textures/text_dash", "textures/prototype/text_dash"},
{"textures/text_dot", "textures/prototype/text_dot"},
{"textures/text_emark", "textures/prototype/text_emark"},
{"textures/text_hash", "textures/prototype/text_hash"},
{"textures/text_light", "textures/prototype/text_light"},
{"textures/text_percent", "textures/prototype/text_percent"},
{"textures/text_qmark", "textures/prototype/text_qmark"},
{"textures/text_smiley", "textures/prototype/text_smiley"},
{"textures/text_sound", "textures/prototype/text_sound"},
{"textures/text_uscore", "textures/prototype/text_uscore"},
{"textures/trigger", "textures/common/trigger"},
{"textures/void", "textures/prototype/void"},
// TODO add more wads like prototype, Makkon et cetera
// TODO add more wads like Makkon et cetera
};
static const int numTextureConvertNames = sizeof( textureConvertNames ) / sizeof( textureConvertNames[0] );

View file

@ -142,6 +142,7 @@ protected:
idVec3 texMat[2];
idVec3 origin;
public:
// RB
idVec3 planepts[ 3 ]; // for writing back original planepts
ProjectionType projection;

View file

@ -29,6 +29,9 @@ If you have questions concerning this license or the applicable additional terms
#pragma hdrstop
#include "../libs/binpack2d/binpack2d.h"
/*
This routine performs a tight packing of a list of rectangles, attempting to minimize the area
@ -79,7 +82,8 @@ public:
const idList<idVec2i>* inputSizes;
};
void RectAllocator( const idList<idVec2i>& inputSizes, idList<idVec2i>& outputPositions, idVec2i& totalSize )
// RB: added START_MAX and imageMax
void RectAllocator( const idList<idVec2i>& inputSizes, idList<idVec2i>& outputPositions, idVec2i& totalSize, const int START_MAX, const int imageMax )
{
outputPositions.SetNum( inputSizes.Num() );
if( inputSizes.Num() == 0 )
@ -109,7 +113,7 @@ void RectAllocator( const idList<idVec2i>& inputSizes, idList<idVec2i>& outputPo
// Somewhat better allocation could be had by checking all the combinations of x and y edges
// in the allocated rectangles, rather than just the corners of each rectangle, but it
// still does a pretty good job.
static const int START_MAX = 1 << 14;
//static const int START_MAX = 1 << 14;
for( int i = 1; i < inputSizes.Num(); i++ )
{
idVec2i best( 0, 0 );
@ -137,7 +141,9 @@ void RectAllocator( const idList<idVec2i>& inputSizes, idList<idVec2i>& outputPo
// don't let an image get larger than 1024 DXT block, or PS3 crashes
// FIXME: pass maxSize in as a parameter
if( newMax[0] > 1024 || newMax[1] > 1024 )
//if( newMax[0] > 1024 || newMax[1] > 1024 )
if( imageMax > 0 && ( newMax[0] > imageMax || newMax[1] > imageMax ) )
{
continue;
}
@ -190,3 +196,117 @@ void RectAllocator( const idList<idVec2i>& inputSizes, idList<idVec2i>& outputPo
}
}
// RB
class MyContent
{
public:
int itemIndex;
idStr str;
MyContent() : str( "default string" ) {}
MyContent( const idStr& str ) : str( str ) {}
};
void RectAllocatorBinPack2D( const idList<idVec2i>& inputSizes, const idStrList& inputNames, idList<idVec2i>& outputPositions, idVec2i& totalSize, const int START_MAX )
{
outputPositions.SetNum( inputSizes.Num() );
if( inputSizes.Num() == 0 )
{
totalSize.Set( 0, 0 );
return;
}
// Create some 'content' to work on.
BinPack2D::ContentAccumulator<MyContent> inputContent;
for( int i = 0; i < inputSizes.Num(); i++ )
{
// random size for this content
int width = inputSizes[ i ].x;
int height = inputSizes[ i ].y;
// whatever data you want to associate with this content
MyContent mycontent( inputNames[ i ] );
mycontent.itemIndex = i;
// Add it
inputContent += BinPack2D::Content<MyContent>( mycontent, BinPack2D::Coord(), BinPack2D::Size( width, height ), false );
}
// Sort the input content by size... usually packs better.
inputContent.Sort();
// Create some bins! ( 2 bins, 128x128 in this example )
BinPack2D::CanvasArray<MyContent> canvasArray =
BinPack2D::UniformCanvasArrayBuilder<MyContent>( START_MAX, START_MAX, 2 ).Build();
// A place to store content that didnt fit into the canvas array.
BinPack2D::ContentAccumulator<MyContent> remainder;
// try to pack content into the bins.
bool success = canvasArray.Place( inputContent, remainder );
// A place to store packed content.
BinPack2D::ContentAccumulator<MyContent> outputContent;
// Read all placed content.
canvasArray.CollectContent( outputContent );
// parse output.
typedef BinPack2D::Content<MyContent>::Vector::iterator binpack2d_iterator;
//idLib::Printf( "PLACED:\n" );
totalSize.x = 0;
totalSize.y = 0;
int i = 0;
for( binpack2d_iterator itor = outputContent.Get().begin(); itor != outputContent.Get().end(); itor++, i++ )
{
const BinPack2D::Content<MyContent>& content = *itor;
// retreive your data.
const MyContent& myContent = content.content;
int index = myContent.itemIndex;
outputPositions[ index ].x = content.coord.x;
outputPositions[ index ].y = content.coord.y;
if( ( content.coord.x + content.size.w ) > totalSize.x )
{
totalSize.x = content.coord.x + content.size.w;
}
if( ( content.coord.y + content.size.h ) > totalSize.y )
{
totalSize.y = content.coord.y + content.size.h;
}
#if 0
idLib::Printf( "\t%9s of size %3dx%3d at position %3d,%3d,%2d rotated=%s\n",
myContent.str.c_str(),
content.size.w,
content.size.h,
content.coord.x,
content.coord.y,
content.coord.z,
( content.rotated ? "yes" : " no" ) );
#endif
}
for( binpack2d_iterator itor = remainder.Get().begin(); itor != remainder.Get().end(); itor++ )
{
const BinPack2D::Content<MyContent>& content = *itor;
const MyContent& myContent = content.content;
int index = myContent.itemIndex;
outputPositions[ index ].x = -1;
outputPositions[ index ].y = -1;
idLib::Printf( "\tFailed to place %9s of size %3dx%3d\n",
myContent.str.c_str(),
content.size.w,
content.size.h );
}
}

View file

@ -0,0 +1,704 @@
/*
Copyright (c) 2013, christopher stones < chris.stones@zoho.com >
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* BinPack2D is a 2 dimensional, multi-bin, bin-packer. ( Texture Atlas Array! )
* It supports an arbitrary number of bins, at arbitrary sizes.
* rectangles can be added one at a time, chunks at a time, or all at once.
* rectangles that dont fit are reported back.
* Data can be associated to rectangles before processing via a template, and recalled after processing.
*
* There is no documentation, See ExampleProgram() below for a taste.
*
* Instead of tracking 'free rectangles' like other solutions I've found online,
* this algorithm tracks free 'top lefts', keeps them sorted by closest to origin, and puts new rectangles into
* the first free top left that doesnt collide. Consuming a top left creates 2 new top lefts (x+w,y) and (x,y+h).
* If a rectangle doesnt fit into a bin, before condisering the next bin, the current bin is re-tried with the rectangle rotated.
* This SOMTIMES helps... but not always.. i might disable this in future !?
*
* This Header was origonally part of my rh_texture_packer program.
* A program I wrote to take advantage of my nexus-7's GL_EXT_texture_array extension.
* I wanted to be able to render out whole scenes with a single glDraw*
* blah blah blah...
*/
/** ***** EXAMPLE CODE **************************************
// Your data - whatever you want to associate with 'rectangle'
class MyContent {
public:
std::string str;
MyContent() : str("default string") {}
MyContent(const std::string &str) : str(str) {}
};
int ExampleProgram() {
srandom(0x69);
// Create some 'content' to work on.
BinPack2D::ContentAccumulator<MyContent> inputContent;
for(int i=0;i<20;i++) {
// random size for this content
int width = ((random() % 32)+1) * ((random() % 10)+1);
int height = ((random() % 32)+1) * ((random() % 10)+1);
// whatever data you want to associate with this content
std::stringstream ss;
ss << "box " << i;
MyContent mycontent( ss.str().c_str() );
// Add it
inputContent += BinPack2D::Content<MyContent>(mycontent, BinPack2D::Coord(), BinPack2D::Size(width, height), false );
}
// Sort the input content by size... usually packs better.
inputContent.Sort();
// Create some bins! ( 2 bins, 128x128 in this example )
BinPack2D::CanvasArray<MyContent> canvasArray =
BinPack2D::UniformCanvasArrayBuilder<MyContent>(128,128,2).Build();
// A place to store content that didnt fit into the canvas array.
BinPack2D::ContentAccumulator<MyContent> remainder;
// try to pack content into the bins.
bool success = canvasArray.Place( inputContent, remainder );
// A place to store packed content.
BinPack2D::ContentAccumulator<MyContent> outputContent;
// Read all placed content.
canvasArray.CollectContent( outputContent );
// parse output.
typedef BinPack2D::Content<MyContent>::Vector::iterator binpack2d_iterator;
printf("PLACED:\n");
for( binpack2d_iterator itor = outputContent.Get().begin(); itor != outputContent.Get().end(); itor++ ) {
const BinPack2D::Content<MyContent> &content = *itor;
// retreive your data.
const MyContent &myContent = content.content;
printf("\t%9s of size %3dx%3d at position %3d,%3d,%2d rotated=%s\n",
myContent.str.c_str(),
content.size.w,
content.size.h,
content.coord.x,
content.coord.y,
content.coord.z,
(content.rotated ? "yes":" no"));
}
printf("NOT PLACED:\n");
for( binpack2d_iterator itor = remainder.Get().begin(); itor != remainder.Get().end(); itor++ ) {
const BinPack2D::Content<MyContent> &content = *itor;
const MyContent &myContent = content.content;
printf("\t%9s of size %3dx%3d\n",
myContent.str.c_str(),
content.size.w,
content.size.h);
}
exit(0);
}
*/
#pragma once
#include<vector>
#include<map>
#include<list>
#include<algorithm>
#include<math.h>
#include<sstream>
namespace BinPack2D
{
class Size
{
public:
/*const*/
int w;
/*const*/ int h;
Size( int w, int h )
: w( w ),
h( h )
{}
bool operator < ( const Size& that ) const
{
if( this->w != that.w )
{
return this->w < that.w;
}
if( this->h != that.h )
{
return this->h < that.h;
}
return false;
}
};
class Coord
{
public:
typedef std::vector<Coord> Vector;
typedef std::list<Coord> List;
/*const*/ int x;
/*const*/ int y;
/*const*/ int z;
Coord()
: x( 0 ),
y( 0 ),
z( 0 )
{}
Coord( int x, int y )
: x( x ),
y( y ),
z( 0 )
{}
Coord( int x, int y, int z )
: x( x ),
y( y ),
z( z )
{}
bool operator < ( const Coord& that ) const
{
if( this->x != that.x )
{
return this->x < that.x;
}
if( this->y != that.y )
{
return this->y < that.y;
}
if( this->z != that.z )
{
return this->z < that.z;
}
return false;
}
};
template<typename _T> class Content
{
public:
typedef std::vector<Content<_T> > Vector;
/*const*/ bool rotated;
/*const*/ Coord coord;
/*const*/ Size size;
/*const*/ _T content;
Content( const Content<_T>& src )
: rotated( src.rotated ),
coord( src.coord ),
size( src.size ),
content( src.content )
{}
Content( const _T& content, const Coord& coord, const Size& size, bool rotated )
:
content( content ),
coord( coord ),
size( size ),
rotated( rotated )
{}
void Rotate()
{
rotated = !rotated;
size = Size( size.h, size.w );
}
bool intersects( const Content<_T>& that ) const
{
if( this->coord.x >= ( that.coord.x + that.size.w ) )
{
return false;
}
if( this->coord.y >= ( that.coord.y + that.size.h ) )
{
return false;
}
if( that.coord.x >= ( this->coord.x + this->size.w ) )
{
return false;
}
if( that.coord.y >= ( this->coord.y + this->size.h ) )
{
return false;
}
return true;
}
};
template<typename _T> class Canvas
{
Coord::List topLefts;
typename Content<_T>::Vector contentVector;
bool needToSort;
public:
typedef Canvas<_T> CanvasT;
typedef typename std::vector<CanvasT> Vector;
static bool Place( Vector& canvasVector, const typename Content<_T>::Vector& contentVector, typename Content<_T>::Vector& remainder )
{
typename Content<_T>::Vector todo = contentVector;
for( typename Vector::iterator itor = canvasVector.begin(); itor != canvasVector.end(); itor++ )
{
Canvas <_T>& canvas = *itor;
remainder.clear();
canvas.Place( todo, remainder );
todo = remainder;
}
if( remainder.size() == 0 )
{
return true;
}
return false;
}
static bool Place( Vector& canvasVector, const typename Content<_T>::Vector& contentVector )
{
typename Content<_T>::Vector remainder;
return Place( canvasVector, contentVector, remainder );
}
static bool Place( Vector& canvasVector, const Content<_T>& content )
{
typename Content<_T>::Vector contentVector( 1, content );
return Place( canvasVector, contentVector );
}
const int w;
const int h;
Canvas( int w, int h )
: needToSort( false ),
w( w ),
h( h )
{
topLefts.push_back( Coord( 0, 0 ) );
}
bool HasContent() const
{
return ( contentVector.size() > 0 ) ;
}
const typename Content<_T>::Vector& GetContents( ) const
{
return contentVector;
}
bool operator < ( const Canvas& that ) const
{
if( this->w != that.w )
{
return this->w < that.w;
}
if( this->h != that.h )
{
return this->h < that.h;
}
return false;
}
bool Place( const typename Content<_T>::Vector& contentVector, typename Content<_T>::Vector& remainder )
{
bool placedAll = true;
for( typename Content<_T>::Vector::const_iterator itor = contentVector.begin(); itor != contentVector.end(); itor++ )
{
const Content<_T>& content = *itor;
if( Place( content ) == false )
{
placedAll = false;
remainder.push_back( content );
}
}
return placedAll;
}
bool Place( Content<_T> content )
{
Sort();
for( Coord::List::iterator itor = topLefts.begin(); itor != topLefts.end(); itor++ )
{
content.coord = *itor;
if( Fits( content ) )
{
Use( content );
topLefts.erase( itor );
return true;
}
}
// EXPERIMENTAL - TRY ROTATED?
content.Rotate();
for( Coord::List::iterator itor = topLefts.begin(); itor != topLefts.end(); itor++ )
{
content.coord = *itor;
if( Fits( content ) )
{
Use( content );
topLefts.erase( itor );
return true;
}
}
////////////////////////////////
return false;
}
private:
bool Fits( const Content<_T>& content ) const
{
if( ( content.coord.x + content.size.w ) > w )
{
return false;
}
if( ( content.coord.y + content.size.h ) > h )
{
return false;
}
for( typename Content<_T>::Vector::const_iterator itor = contentVector.begin(); itor != contentVector.end(); itor++ )
if( content.intersects( *itor ) )
{
return false;
}
return true;
}
bool Use( const Content<_T>& content )
{
const Size& size = content.size;
const Coord& coord = content.coord;
topLefts.push_front( Coord( coord.x + size.w, coord.y ) );
topLefts.push_back( Coord( coord.x , coord.y + size.h ) );
contentVector.push_back( content );
needToSort = true;
return true;
}
private:
struct TopToBottomLeftToRightSort
{
bool operator()( const Coord& a, const Coord& b ) const
{
return ( a.x * a.x + a.y * a.y ) < ( b.x * b.x + b.y * b.y );
}
};
public:
void Sort()
{
if( !needToSort )
{
return;
}
topLefts.sort( TopToBottomLeftToRightSort() );
needToSort = false;
}
};
template <typename _T> class ContentAccumulator
{
typename Content<_T>::Vector contentVector;
public:
ContentAccumulator()
{}
const typename Content<_T>::Vector& Get() const
{
return contentVector;
}
typename Content<_T>::Vector& Get()
{
return contentVector;
}
ContentAccumulator<_T>& operator += ( const Content<_T>& content )
{
contentVector.push_back( content );
return *this;
}
ContentAccumulator<_T>& operator += ( const typename Content<_T>::Vector& content )
{
contentVector.insert( contentVector.end(), content.begin(), content.end() );
return *this;
}
ContentAccumulator<_T> operator + ( const Content<_T>& content )
{
ContentAccumulator<_T> temp = *this;
temp += content;
return temp;
}
ContentAccumulator<_T> operator + ( const typename Content<_T>::Vector& content )
{
ContentAccumulator<_T> temp = *this;
temp += content;
return temp;
}
private:
struct GreatestWidthThenGreatestHeightSort
{
bool operator()( const Content<_T>& a, const Content<_T>& b ) const
{
const Size& sa = a.size;
const Size& sb = b.size;
// return( sa.w * sa.h > sb.w * sb.h );
if( sa.w != sb.w )
{
return sa.w > sb.w;
}
return sa.h > sb.h;
}
};
struct MakeHorizontal
{
Content<_T> operator()( const Content<_T>& elem )
{
if( elem.size.h > elem.size.w )
{
Content<_T> r = elem;
r.size.w = elem.size.h;
r.size.h = elem.size.w;
r.rotated = !elem.rotated;
return r;
}
return elem;
}
};
public:
void Sort()
{
// if(allow_rotation)
// std::transform(contentVector.begin(), contentVector.end(), contentVector.begin(), MakeHorizontal());
std::sort( contentVector.begin(), contentVector.end(), GreatestWidthThenGreatestHeightSort() );
}
};
template <typename _T> class UniformCanvasArrayBuilder
{
int w;
int h;
int d;
public:
UniformCanvasArrayBuilder( int w, int h, int d )
: w( w ),
h( h ),
d( d )
{}
typename Canvas<_T>::Vector Build()
{
return typename Canvas<_T>::Vector( d, Canvas<_T>( w, h ) );
}
};
template<typename _T> class CanvasArray
{
typename Canvas<_T>::Vector canvasArray;
public:
CanvasArray( const typename Canvas<_T>::Vector& canvasArray )
: canvasArray( canvasArray )
{}
bool Place( const typename Content<_T>::Vector& contentVector, typename Content<_T>::Vector& remainder )
{
return Canvas<_T>::Place( canvasArray, contentVector, remainder );
}
bool Place( const ContentAccumulator<_T>& content, ContentAccumulator<_T>& remainder )
{
return Place( content.Get(), remainder.Get() );
}
bool Place( const typename Content<_T>::Vector& contentVector )
{
return Canvas<_T>::Place( canvasArray, contentVector );
}
bool Place( const ContentAccumulator<_T>& content )
{
return Place( content.Get() );
}
bool CollectContent( typename Content<_T>::Vector& contentVector ) const
{
int z = 0;
for( typename Canvas<_T>::Vector::const_iterator itor = canvasArray.begin(); itor != canvasArray.end(); itor++ )
{
const typename Content<_T>::Vector& contents = itor->GetContents();
for( typename Content<_T>::Vector::const_iterator itor = contents.begin(); itor != contents.end(); itor++ )
{
Content<_T> content = *itor;
content.coord.z = z;
contentVector.push_back( content );
}
z++;
}
return true;
}
bool CollectContent( ContentAccumulator<_T>& content ) const
{
return CollectContent( content.Get() );
}
};
} /*** BinPack2D ***/

View file

@ -71,11 +71,7 @@ extern "C"
#include <libswresample/swresample.h>
#include <libavutil/imgutils.h>
}
// SRS - For handling cinematic audio packets
#include <queue>
#define NUM_PACKETS 4
#define NUM_LAG_FRAMES 15 // SRS - Lag cinematic audio by 15 frames (~1/2 sec at 30 fps) to sync with FFMPEG video
bool hasplanar = true;
#define NUM_LAG_FRAMES 15 // SRS - Lag audio by 15 frames (~1/2 sec at 30 fps) for ffmpeg bik decoder AV sync
#endif
#ifdef USE_BINKDEC
@ -118,8 +114,10 @@ private:
AVFrame* frame3; //GK: make extra frame for audio
#if LIBAVCODEC_VERSION_MAJOR > 58
const AVCodec* dec;
const AVCodec* dec2; // SRS - Separate decoder for audio
#else
AVCodec* dec;
AVCodec* dec2; // SRS - Separate decoder for audio
#endif
AVCodecContext* dec_ctx;
AVCodecContext* dec_ctx2;
@ -127,14 +125,15 @@ private:
bool hasFrame;
long framePos;
AVSampleFormat dst_smp;
bool hasplanar;
SwrContext* swr_ctx;
cinData_t ImageForTimeFFMPEG( int milliseconds, nvrhi::ICommandList* commandList );
bool InitFromFFMPEGFile( const char* qpath, bool looping, nvrhi::ICommandList* commandList );
void FFMPEGReset();
std::queue<AVPacket> packets[NUM_PACKETS];
uint8_t* lagBuffer[NUM_LAG_FRAMES] = {};
int lagBufSize[NUM_LAG_FRAMES] = {};
int lagIndex;
bool skipLag;
#endif
#ifdef USE_BINKDEC
BinkHandle binkHandle;
@ -436,7 +435,6 @@ idCinematicLocal::idCinematicLocal()
isRoQ = false; // SRS - Initialize isRoQ for all cases, not just FFMPEG
#if defined(USE_FFMPEG)
// Carl: ffmpeg stuff, for bink and normal video files:
// fmt_ctx = avformat_alloc_context();
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55,28,1)
frame = av_frame_alloc();
frame2 = av_frame_alloc();
@ -451,10 +449,13 @@ idCinematicLocal::idCinematicLocal()
fmt_ctx = NULL;
video_stream_index = -1;
audio_stream_index = -1;
hasplanar = false;
swr_ctx = NULL;
img_convert_ctx = NULL;
hasFrame = false;
framePos = -1;
lagIndex = 0;
skipLag = false;
#endif
#ifdef USE_BINKDEC
@ -548,30 +549,9 @@ idCinematicLocal::~idCinematicLocal()
av_freep( &frame2 );
av_freep( &frame3 );
#endif
// SRS - Free any lagged cinematic audio buffers
for( int i = 0; i < NUM_LAG_FRAMES; i++ )
{
av_freep( &lagBuffer[ i ] );
}
if( fmt_ctx )
{
avformat_free_context( fmt_ctx );
}
if( img_convert_ctx )
{
sws_freeContext( img_convert_ctx );
}
#endif
#ifdef USE_BINKDEC
if( binkHandle.isValid )
{
Bink_Close( binkHandle );
}
delete imgY;
imgY = NULL;
delete imgCr;
@ -677,8 +657,20 @@ bool idCinematicLocal::InitFromFFMPEGFile( const char* qpath, bool amilooping, n
if( ( ret = avformat_open_input( &fmt_ctx, fullpath, NULL, NULL ) ) < 0 )
{
common->Warning( "idCinematic: Cannot open FFMPEG video file: '%s', %d\n", qpath, looping );
return false;
// SRS - another case sensitivity hack for Linux, this time for ffmpeg and RoQ files
idStr ext;
fullpath.ExtractFileExtension( ext );
if( idStr::Cmp( ext.c_str(), "roq" ) == 0 )
{
// SRS - If ffmpeg can't open .roq file, then try again with .RoQ extension instead
fullpath.Replace( ".roq", ".RoQ" );
ret = avformat_open_input( &fmt_ctx, fullpath, NULL, NULL );
}
if( ret < 0 )
{
common->Warning( "idCinematic: Cannot open FFMPEG video file: '%s', %d\n", qpath, looping );
return false;
}
}
if( ( ret = avformat_find_stream_info( fmt_ctx, NULL ) ) < 0 )
{
@ -712,12 +704,12 @@ bool idCinematicLocal::InitFromFFMPEGFile( const char* qpath, bool amilooping, n
return false;
}
//GK:Begin
//After the video decoder is open then try to open audio decoder since it will re-bind the main decoder from video to audio
ret2 = av_find_best_stream( fmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, &dec, 0 );
//After the video decoder is open then try to open audio decoder
ret2 = av_find_best_stream( fmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, &dec2, 0 );
if( ret2 >= 0 ) //Make audio optional (only intro video has audio no other)
{
audio_stream_index = ret2;
dec_ctx2 = avcodec_alloc_context3( dec );
dec_ctx2 = avcodec_alloc_context3( dec2 );
if( ( ret2 = avcodec_parameters_to_context( dec_ctx2, fmt_ctx->streams[audio_stream_index]->codecpar ) ) < 0 )
{
char* error = new char[256];
@ -727,7 +719,7 @@ bool idCinematicLocal::InitFromFFMPEGFile( const char* qpath, bool amilooping, n
dec_ctx2->time_base = fmt_ctx->streams[audio_stream_index]->time_base;
dec_ctx2->framerate = fmt_ctx->streams[audio_stream_index]->avg_frame_rate;
dec_ctx2->pkt_timebase = fmt_ctx->streams[audio_stream_index]->time_base;
if( ( ret2 = avcodec_open2( dec_ctx2, dec, NULL ) ) < 0 )
if( ( ret2 = avcodec_open2( dec_ctx2, dec2, NULL ) ) < 0 )
{
common->Warning( "idCinematic: Cannot open audio decoder for: '%s', %d\n", qpath, looping );
//return false;
@ -777,28 +769,32 @@ bool idCinematicLocal::InitFromFFMPEGFile( const char* qpath, bool amilooping, n
//GK: No duration is given. Check if we get at least bitrate to calculate the length, otherwise set it to a fixed 100 seconds (should it be lower ?)
if( durationSec < 0 )
{
if( dec_ctx->bit_rate > 0 )
// SRS - First check the file context bit rate and estimate duration using file size and overall bit rate
if( fmt_ctx->bit_rate > 0 )
{
durationSec = file_size / dec_ctx->bit_rate;
durationSec = file_size * 8.0 / fmt_ctx->bit_rate;
}
// SRS - Likely an RoQ file, so use the video bit rate tolerance plus audio bit rate to estimate duration, then add 10% to correct for variable bit rate
else if( dec_ctx->bit_rate_tolerance > 0 )
{
durationSec = file_size * 8.0 / ( dec_ctx->bit_rate_tolerance + ( dec_ctx2 ? dec_ctx2->bit_rate : 0 ) ) * 1.1;
}
// SRS - Otherwise just set a large max duration
else
{
durationSec = 100;
durationSec = 100.0;
}
}
animationLength = durationSec * 1000;
frameRate = av_q2d( fmt_ctx->streams[video_stream_index]->avg_frame_rate );
common->Printf( "Loaded FFMPEG file: '%s', looping=%d, %dx%d, %f FPS, %f sec\n", qpath, looping, CIN_WIDTH, CIN_HEIGHT, frameRate, durationSec );
common->Printf( "Loaded FFMPEG file: '%s', looping=%d, %dx%d, %3.2f FPS, %4.1f sec\n", qpath, looping, CIN_WIDTH, CIN_HEIGHT, frameRate, durationSec );
image = ( byte* )Mem_Alloc( CIN_WIDTH * CIN_HEIGHT * 4 * 2, TAG_CINEMATIC );
// SRS - Get number of image bytes needed by querying with NULL first, then allocate image and fill with correct parameters
int img_bytes = av_image_fill_arrays( frame2->data, frame2->linesize, NULL, AV_PIX_FMT_BGR32, CIN_WIDTH, CIN_HEIGHT, 1 );
image = ( byte* )Mem_Alloc( img_bytes, TAG_CINEMATIC );
av_image_fill_arrays( frame2->data, frame2->linesize, image, AV_PIX_FMT_BGR32, CIN_WIDTH, CIN_HEIGHT, 1 ); //GK: Straight out of the FFMPEG source code
if( img_convert_ctx )
{
sws_freeContext( img_convert_ctx );
}
img_convert_ctx = sws_getContext( dec_ctx->width, dec_ctx->height, dec_ctx->pix_fmt, CIN_WIDTH, CIN_HEIGHT, AV_PIX_FMT_BGR32, SWS_BICUBIC, NULL, NULL, NULL );
buf = NULL;
status = FMV_PLAY;
hasFrame = false;
framePos = -1;
@ -822,11 +818,37 @@ void idCinematicLocal::FFMPEGReset()
framePos = -1;
if( av_seek_frame( fmt_ctx, video_stream_index, 0, 0 ) >= 0 )
// SRS - If we have an ffmpeg audio context and are not looping, or skipLag is true, reset audio to release any stale buffers
if( dec_ctx2 && ( !( looping && status == FMV_EOF ) || skipLag ) )
{
cinematicAudio->ResetAudio();
for( int i = 0; i < NUM_LAG_FRAMES; i++ )
{
lagBufSize[ i ] = 0;
if( lagBuffer[ i ] )
{
av_freep( &lagBuffer[ i ] );
}
}
}
// SRS - For non-RoQ (i.e. bik) files, use standard frame seek to rewind the stream
if( dec_ctx->codec_id != AV_CODEC_ID_ROQ && av_seek_frame( fmt_ctx, video_stream_index, 0, 0 ) >= 0 )
{
status = FMV_LOOPED;
}
else if( av_seek_frame( fmt_ctx, audio_stream_index, 0, 0 ) < 0 && av_seek_frame( fmt_ctx, video_stream_index, 0, 0 ) < 0 )
// SRS - Special handling for RoQ files: only byte seek works and ffmpeg RoQ decoder needs reset
else if( dec_ctx->codec_id == AV_CODEC_ID_ROQ && av_seek_frame( fmt_ctx, video_stream_index, 0, AVSEEK_FLAG_BYTE ) >= 0 )
{
// Close and reopen the ffmpeg RoQ codec without clearing the context - this seems to reset the decoder properly
avcodec_close( dec_ctx );
avcodec_open2( dec_ctx, dec, NULL );
status = FMV_LOOPED;
}
// SRS - Can't rewind the stream so we really are at EOF
else
{
status = FMV_EOF;
}
@ -888,7 +910,7 @@ bool idCinematicLocal::InitFromBinkDecFile( const char* qpath, bool amilooping )
{
trackIndex = 0; // SRS - Use the first audio track - is this reasonable?
binkInfo = Bink_GetAudioTrackDetails( binkHandle, trackIndex );
common->Printf( "Cinematic audio stream found: Sample Rate=%d Hz, Channels=%d\n", binkInfo.sampleRate, binkInfo.nChannels );
common->Printf( "Cinematic audio stream found: Sample Rate=%d Hz, Channels=%d, Format=16-bit\n", binkInfo.sampleRate, binkInfo.nChannels );
cinematicAudio->InitAudio( &binkInfo );
}
@ -896,11 +918,10 @@ bool idCinematicLocal::InitFromBinkDecFile( const char* qpath, bool amilooping )
numFrames = Bink_GetNumFrames( binkHandle );
float durationSec = numFrames / frameRate; // SRS - fixed Bink durationSec calculation
animationLength = durationSec * 1000; // SRS - animationLength is in milliseconds
common->Printf( "Loaded BinkDec file: '%s', looping=%d, %dx%d, %f FPS, %f sec\n", qpath, looping, CIN_WIDTH, CIN_HEIGHT, frameRate, durationSec );
common->Printf( "Loaded BinkDec file: '%s', looping=%d, %dx%d, %3.2f FPS, %4.1f sec\n", qpath, looping, CIN_WIDTH, CIN_HEIGHT, frameRate, durationSec );
memset( yuvBuffer, 0, sizeof( yuvBuffer ) );
buf = NULL;
status = FMV_PLAY;
hasFrame = false; // SRS - Implemented hasFrame for BinkDec behaviour consistency with FFMPEG
framePos = -1;
@ -913,6 +934,13 @@ bool idCinematicLocal::InitFromBinkDecFile( const char* qpath, bool amilooping )
void idCinematicLocal::BinkDecReset()
{
framePos = -1;
// SRS - If we have bink audio tracks, reset audio to release any stale buffers (even if looping)
if( audioTracks > 0 )
{
cinematicAudio->ResetAudio();
}
Bink_GotoFrame( binkHandle, 0 );
status = FMV_LOOPED;
}
@ -927,7 +955,8 @@ bool idCinematicLocal::InitFromFile( const char* qpath, bool amilooping, nvrhi::
{
unsigned short RoQID;
Close();
// SRS - Don't need to call Close() here, all initialization is handled by constructor
//Close();
inMemory = 0;
animationLength = 100000;
@ -942,33 +971,42 @@ bool idCinematicLocal::InitFromFile( const char* qpath, bool amilooping, nvrhi::
sprintf( fileName, "%s", qpath );
}
// Carl: Look for original Doom 3 RoQ files first:
idStr ext;
fileName.ExtractFileExtension( ext );
fileName = fileName.StripFileExtension();
fileName = fileName + ".roq";
//if (fileName == "video\\loadvideo.roq") {
// fileName = "video\\idlogo.roq";
idStr temp = fileName.StripFileExtension() + ".roq";
// SRS - Cool legacy support, but leaving this disabled since it might break existing mods
//if( temp == "video\\loadvideo.roq" )
//{
// temp = "video\\idlogo.roq";
//}
iFile = fileSystem->OpenFileRead( fileName );
iFile = fileSystem->OpenFileRead( temp );
// Carl: If the RoQ file doesn't exist, try using ffmpeg instead:
// Carl: If the RoQ file doesn't exist, try using bik file extension instead:
if( !iFile )
{
//idLib::Warning( "Original Doom 3 RoQ Cinematic not found: '%s'\n", temp.c_str() );
#if defined(USE_FFMPEG)
//idLib::Warning( "Original Doom 3 RoQ Cinematic not found: '%s'\n", fileName.c_str() );
idStr temp = fileName.StripFileExtension() + ".bik";
temp = fileName.StripFileExtension() + ".bik";
skipLag = false; // SRS - Enable lag buffer for ffmpeg bik decoder AV sync
// SRS - Support RoQ cinematic playback via ffmpeg decoder - better quality plus audio support
}
else
{
fileSystem->CloseFile( iFile ); // SRS - Close the RoQ file and let ffmpeg reopen it
iFile = NULL;
skipLag = true; // SRS - Disable lag buffer for ffmpeg RoQ decoder AV sync
}
{
// SRS End
animationLength = 0;
hasFrame = false;
RoQShutdown();
fileName = temp;
//idLib::Warning( "New filename: '%s'\n", fileName.c_str() );
return InitFromFFMPEGFile( fileName.c_str(), amilooping, commandList );
#elif defined(USE_BINKDEC)
idStr temp = fileName.StripFileExtension() + ".bik";
temp = fileName.StripFileExtension() + ".bik";
animationLength = 0;
hasFrame = false;
RoQShutdown();
fileName = temp;
//idLib::Warning( "New filename: '%s'\n", fileName.c_str() );
return InitFromBinkDecFile( fileName.c_str(), amilooping );
@ -979,6 +1017,7 @@ bool idCinematicLocal::InitFromFile( const char* qpath, bool amilooping, nvrhi::
}
// Carl: The rest of this function is for original Doom 3 RoQ files:
isRoQ = true;
fileName = temp;
ROQSize = iFile->Length();
looping = amilooping;
@ -1004,6 +1043,7 @@ bool idCinematicLocal::InitFromFile( const char* qpath, bool amilooping, nvrhi::
RoQ_init();
status = FMV_PLAY;
ImageForTime( 0, commandList );
common->Printf( "Loaded RoQ file: '%s', looping=%d, %dx%d, %3.2f FPS\n", fileName.c_str(), looping, CIN_WIDTH, CIN_HEIGHT, frameRate );
status = ( looping ) ? FMV_PLAY : FMV_IDLE;
return true;
}
@ -1027,23 +1067,43 @@ void idCinematicLocal::Close()
status = FMV_EOF;
}
RoQShutdown();
if( isRoQ )
{
RoQShutdown();
}
#if defined(USE_FFMPEG)
hasFrame = false;
if( !isRoQ )
else //if( !isRoQ )
{
if( img_convert_ctx )
{
sws_freeContext( img_convert_ctx );
img_convert_ctx = NULL;
}
img_convert_ctx = NULL;
// SRS - Free audio codec context, resample context, and any lagged audio buffers
if( dec_ctx2 )
{
avcodec_free_context( &dec_ctx2 );
// SRS - Free resample context if we were decoding planar audio
if( swr_ctx )
{
swr_free( &swr_ctx );
}
for( int i = 0; i < NUM_LAG_FRAMES; i++ )
{
lagBufSize[ i ] = 0;
if( lagBuffer[ i ] )
{
av_freep( &lagBuffer[ i ] );
}
}
}
if( dec_ctx )
{
avcodec_close( dec_ctx );
avcodec_free_context( &dec_ctx );
}
if( fmt_ctx )
@ -1052,11 +1112,8 @@ void idCinematicLocal::Close()
}
status = FMV_EOF;
}
#endif
#ifdef USE_BINKDEC
hasFrame = false;
if( !isRoQ )
#elif defined(USE_BINKDEC)
else //if( !isRoQ )
{
if( binkHandle.isValid )
{
@ -1121,8 +1178,7 @@ cinData_t idCinematicLocal::ImageForTime( int thisTime, nvrhi::ICommandList* com
{
return ImageForTimeFFMPEG( thisTime, commandList );
}
#endif
#ifdef USE_BINKDEC // DG: libbinkdec support
#elif defined(USE_BINKDEC) // DG: libbinkdec support
if( !isRoQ )
{
return ImageForTimeBinkDec( thisTime );
@ -1132,15 +1188,16 @@ cinData_t idCinematicLocal::ImageForTime( int thisTime, nvrhi::ICommandList* com
// Carl: Handle original Doom 3 RoQ video files
cinData_t cinData;
if( thisTime == 0 )
// SRS - Changed from == 0 to <= 0 to match behaviour of FFMPEG and BinkDec decoders
if( thisTime <= 0 )
{
thisTime = Sys_Milliseconds();
}
if( thisTime < 0 )
{
thisTime = 0;
}
//if( thisTime < 0 )
//{
// thisTime = 0;
//}
memset( &cinData, 0, sizeof( cinData ) );
@ -1177,7 +1234,8 @@ cinData_t idCinematicLocal::ImageForTime( int thisTime, nvrhi::ICommandList* com
tfps = 0;
}
if( tfps < numQuads )
// SRS - Need to use numQuads - 1 for frame position (otherwise get into reset loop at start)
if( tfps < numQuads - 1 )
{
RoQReset();
buf = NULL;
@ -1186,6 +1244,7 @@ cinData_t idCinematicLocal::ImageForTime( int thisTime, nvrhi::ICommandList* com
if( buf == NULL )
{
// SRS - This frame init loop is not really necessary, but leaving in to avoid breakage
while( buf == NULL )
{
RoQInterrupt();
@ -1193,32 +1252,31 @@ cinData_t idCinematicLocal::ImageForTime( int thisTime, nvrhi::ICommandList* com
}
else
{
while( ( tfps != numQuads && status == FMV_PLAY ) )
// SRS - This frame loop is really all we need and could handle the above case as well
while( ( numQuads - 1 < tfps && status == FMV_PLAY ) )
{
RoQInterrupt();
}
}
if( status == FMV_LOOPED )
{
status = FMV_PLAY;
while( buf == NULL && status == FMV_PLAY )
{
RoQInterrupt();
}
startTime = thisTime;
}
// SRS - This is redundant code, virtually identical logic correctly handles looping below
//if( status == FMV_LOOPED )
//{
// status = FMV_PLAY;
// while( buf == NULL && status == FMV_PLAY )
// {
// RoQInterrupt();
// }
// startTime = thisTime;
//}
if( status == FMV_EOF )
if( status == FMV_LOOPED || status == FMV_EOF )
{
if( looping )
{
RoQReset();
//RoQReset(); // SRS - RoQReset() already called by RoQInterrupt() when looping
buf = NULL;
if( status == FMV_LOOPED )
{
status = FMV_PLAY;
}
status = FMV_PLAY;
while( buf == NULL && status == FMV_PLAY )
{
RoQInterrupt();
@ -1227,8 +1285,10 @@ cinData_t idCinematicLocal::ImageForTime( int thisTime, nvrhi::ICommandList* com
}
else
{
buf = NULL;
status = FMV_IDLE;
RoQShutdown();
//RoQShutdown(); //SRS - RoQShutdown() not needed on EOF, return null data instead
return cinData;
}
}
@ -1288,6 +1348,8 @@ cinData_t idCinematicLocal::ImageForTimeFFMPEG( int thisTime, nvrhi::ICommandLis
if( desiredFrame < framePos )
{
FFMPEGReset();
hasFrame = false;
status = FMV_PLAY;
}
if( hasFrame && desiredFrame == framePos )
@ -1320,7 +1382,7 @@ cinData_t idCinematicLocal::ImageForTimeFFMPEG( int thisTime, nvrhi::ICommandLis
{
desiredFrame = 0;
FFMPEGReset();
framePos = -1;
hasFrame = false;
startTime = thisTime;
if( av_read_frame( fmt_ctx, &packet ) < 0 )
{
@ -1331,6 +1393,7 @@ cinData_t idCinematicLocal::ImageForTimeFFMPEG( int thisTime, nvrhi::ICommandLis
}
else
{
hasFrame = false;
status = FMV_IDLE;
return cinData;
}
@ -1358,8 +1421,7 @@ cinData_t idCinematicLocal::ImageForTimeFFMPEG( int thisTime, nvrhi::ICommandLis
//GK:Begin
else if( packet.stream_index == audio_stream_index ) //Check if it found any audio data
{
packets->push( packet );
res = avcodec_send_packet( dec_ctx2, &packets->front() );
res = avcodec_send_packet( dec_ctx2, &packet );
if( res != 0 && res != AVERROR( EAGAIN ) )
{
char* error = new char[256];
@ -1368,8 +1430,6 @@ cinData_t idCinematicLocal::ImageForTimeFFMPEG( int thisTime, nvrhi::ICommandLis
}
else
{
packet = packets->front();
packets->pop();
if( ( frameFinished1 = avcodec_receive_frame( dec_ctx2, frame3 ) ) != 0 )
{
char* error = new char[256];
@ -1436,7 +1496,7 @@ cinData_t idCinematicLocal::ImageForTimeFFMPEG( int thisTime, nvrhi::ICommandLis
lagBuffer[ lagIndex ] = audioBuffer;
lagBufSize[ lagIndex ] = num_bytes;
lagIndex = ( lagIndex + 1 ) % NUM_LAG_FRAMES;
lagIndex = ( lagIndex + 1 ) % ( skipLag ? 1 : NUM_LAG_FRAMES );
}
return cinData;
@ -1484,28 +1544,12 @@ cinData_t idCinematicLocal::ImageForTimeBinkDec( int thisTime )
desiredFrame = 0;
}
if( desiredFrame >= numFrames )
{
status = FMV_EOF;
if( looping )
{
desiredFrame = 0;
BinkDecReset();
framePos = -1;
startTime = thisTime;
status = FMV_PLAY;
}
else
{
status = FMV_IDLE;
return cinData;
}
}
// SRS - Enable video replay within PDAs
if( desiredFrame < framePos )
{
BinkDecReset();
hasFrame = false;
status = FMV_PLAY;
}
// SRS end
@ -1521,6 +1565,25 @@ cinData_t idCinematicLocal::ImageForTimeBinkDec( int thisTime )
return cinData;
}
if( desiredFrame >= numFrames )
{
status = FMV_EOF;
if( looping )
{
desiredFrame = 0;
BinkDecReset();
hasFrame = false;
startTime = thisTime;
status = FMV_PLAY;
}
else
{
hasFrame = false;
status = FMV_IDLE;
return cinData;
}
}
// Bink_GotoFrame(binkHandle, desiredFrame);
// apparently Bink_GotoFrame() doesn't work super well, so skip frames
// (if necessary) by calling Bink_GetNextFrame()
@ -1586,7 +1649,7 @@ cinData_t idCinematicLocal::ImageForTimeBinkDec( int thisTime )
if( audioTracks > 0 )
{
audioBuffer = ( int16_t* )malloc( binkInfo.idealBufferSize );
audioBuffer = ( int16_t* )Mem_Alloc( binkInfo.idealBufferSize, TAG_AUDIO );
num_bytes = Bink_GetAudioData( binkHandle, trackIndex, audioBuffer );
// SRS - If we have cinematic audio data, start playing it now
@ -1598,7 +1661,7 @@ cinData_t idCinematicLocal::ImageForTimeBinkDec( int thisTime )
else
{
// SRS - Even though we have no audio data to play, still need to free the audio buffer
free( audioBuffer );
Mem_Free( audioBuffer );
}
}
@ -3077,7 +3140,7 @@ redump:
//
// read in next frame data
//
if( RoQPlayed >= ROQSize )
if( RoQPlayed >= ROQSize || status == FMV_EOF ) // SRS - handle FMV_EOF case
{
if( looping )
{
@ -3153,11 +3216,14 @@ idCinematicLocal::RoQShutdown
*/
void idCinematicLocal::RoQShutdown()
{
// SRS - Depending on status, this could prevent closing of iFile on shutdown, disable it
/*
if( status == FMV_IDLE )
{
return;
}
status = FMV_IDLE;
*/
status = FMV_EOF; // SRS - Changed from FMV_IDLE to FMV_EOF for shutdown consistency
if( iFile )
{

View file

@ -72,10 +72,9 @@ public:
void AddDepthBuffer( int format, int multiSamples = 0 );
void AddStencilBuffer( int format, int multiSamples = 0 );
void AttachImage2D( int target, const idImage* image, int index, int mipmapLod = 0 );
void AttachImage3D( const idImage* image );
void AttachImageDepth( int target, const idImage* image );
void AttachImageDepthLayer( const idImage* image, int layer );
void AttachImage2D( int target, idImage* image, int index, int mipmapLod = 0 );
void AttachImageDepth( int target, idImage* image );
void AttachImageDepthLayer( idImage* image, int layer );
// check for OpenGL errors
void Check();

View file

@ -1149,6 +1149,7 @@ void idImage::GenerateShadowArray( int width, int height, textureFilter_t filter
opts.numLevels = 0;
opts.isRenderTarget = true;
DeriveOpts();
// The image will be uploaded to the gpu on a deferred state.

View file

@ -527,7 +527,38 @@ void Framebuffer::AddStencilBuffer( int format, int multiSamples )
GL_CheckErrors();
}
void Framebuffer::AttachImage2D( int target, const idImage* image, int index, int mipmapLod )
void Framebuffer::AddStencilBuffer( int format, int multiSamples )
{
stencilFormat = format;
bool notCreatedYet = stencilBuffer == 0;
if( notCreatedYet )
{
glGenRenderbuffers( 1, &stencilBuffer );
}
glBindRenderbuffer( GL_RENDERBUFFER, stencilBuffer );
if( multiSamples > 0 )
{
glRenderbufferStorageMultisample( GL_RENDERBUFFER, multiSamples, format, width, height );
msaaSamples = true;
}
else
{
glRenderbufferStorage( GL_RENDERBUFFER, format, width, height );
}
if( notCreatedYet )
{
glFramebufferRenderbuffer( GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, stencilBuffer );
}
GL_CheckErrors();
}
void Framebuffer::AttachImage2D( int target, idImage* image, int index, int mipmapLod )
{
if( ( target != GL_TEXTURE_2D ) && ( target != GL_TEXTURE_2D_MULTISAMPLE ) && ( target < GL_TEXTURE_CUBE_MAP_POSITIVE_X || target > GL_TEXTURE_CUBE_MAP_NEGATIVE_Z ) )
{
@ -542,9 +573,11 @@ void Framebuffer::AttachImage2D( int target, const idImage* image, int index, in
}
glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + index, target, image->texnum, mipmapLod );
image->opts.isRenderTarget = true;
}
void Framebuffer::AttachImageDepth( int target, const idImage* image )
void Framebuffer::AttachImageDepth( int target, idImage* image )
{
if( ( target != GL_TEXTURE_2D ) && ( target != GL_TEXTURE_2D_MULTISAMPLE ) )
{
@ -553,11 +586,15 @@ void Framebuffer::AttachImageDepth( int target, const idImage* image )
}
glFramebufferTexture2D( GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, target, image->texnum, 0 );
image->opts.isRenderTarget = true;
}
void Framebuffer::AttachImageDepthLayer( const idImage* image, int layer )
void Framebuffer::AttachImageDepthLayer( idImage* image, int layer )
{
glFramebufferTextureLayer( GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, image->texnum, 0, layer );
image->opts.isRenderTarget = true;
}
void Framebuffer::Check()

View file

@ -417,16 +417,6 @@ void idImage::SetSamplerState( textureFilter_t tf, textureRepeat_t tr )
SetTexParameters();
}
/*
========================
idImage::SetPixel
========================
*/
void idImage::SetPixel( int mipLevel, int x, int y, const void* data, int dataSize )
{
SubImageUpload( mipLevel, x, y, 0, 1, 1, data );
}
/*
========================
idImage::SetTexParameters

View file

@ -3256,13 +3256,20 @@ void idRenderBackend::DBG_TestImage()
{
cinData_t cin;
cin = tr.testVideo->ImageForTime( viewDef->renderView.time[1] - tr.testVideoStartTime );
// SRS - Don't need calibrated time for testing cinematics, so just call ImageForTime( 0 ) for current system time
// This simplification allows cinematic test playback to work over both 2D and 3D background scenes
cin = tr.testVideo->ImageForTime( 0 /*viewDef->renderView.time[1] - tr.testVideoStartTime*/ );
if( cin.imageY != NULL )
{
image = cin.imageY;
imageCr = cin.imageCr;
imageCb = cin.imageCb;
}
// SRS - Also handle ffmpeg and original RoQ decoders for test videos (using cin.image)
else if( cin.image != NULL )
{
image = cin.image;
}
else
{
tr.testImage = NULL;
@ -3302,9 +3309,9 @@ void idRenderBackend::DBG_TestImage()
float scale[16] = { 0 };
scale[0] = w; // scale
scale[5] = -h; // scale
scale[5] = h; // scale (SRS - changed h from -ve to +ve so video plays right side up)
scale[12] = halfScreenWidth - ( halfScreenWidth * w ); // translate
scale[13] = halfScreenHeight - ( halfScreenHeight * h ); // translate
scale[13] = halfScreenHeight - ( halfScreenHeight * h ) - h; // translate (SRS - moved up by h)
scale[10] = 1.0f;
scale[15] = 1.0f;
@ -3340,7 +3347,9 @@ void idRenderBackend::DBG_TestImage()
imageCr->Bind();
GL_SelectTexture( 2 );
imageCb->Bind();
renderProgManager.BindShader_Bink();
// SRS - Use Bink shader without sRGB to linear conversion, otherwise cinematic colours may be wrong
// BindShader_BinkGUI() does not seem to work here - perhaps due to vertex shader input dependencies?
renderProgManager.BindShader_Bink_sRGB();
}
else
{

View file

@ -312,8 +312,8 @@ void idRenderBackend::BindVariableStageImage( const textureStage_t* texture, con
GL_SelectTexture( 0 );
cin.image->Bind();
/*
if( backEnd.viewDef->is2Dgui )
// SRS - Reenable shaders so ffmpeg and RoQ decoder cinematics are rendered with correct colour
if( viewDef->is2Dgui )
{
renderProgManager.BindShader_TextureVertexColor_sRGB();
}
@ -321,7 +321,6 @@ void idRenderBackend::BindVariableStageImage( const textureStage_t* texture, con
{
renderProgManager.BindShader_TextureVertexColor();
}
*/
}
else
{
@ -5072,6 +5071,7 @@ void idRenderBackend::Bloom( const viewDef_t* _viewDef )
renderLog.OpenMainBlock( MRB_BLOOM );
renderLog.OpenBlock( "Render_Bloom", colorBlue );
// BRIGHTPASS
renderLog.OpenBlock( "Brightpass" );

View file

@ -856,6 +856,12 @@ public:
BindShader_Builtin( BUILTIN_BINK );
}
// SRS - Added Bink shader without sRGB to linear conversion for testVideo cmd
void BindShader_Bink_sRGB()
{
BindShader_Builtin( BUILTIN_BINK_SRGB );
}
void BindShader_BinkGUI()
{
BindShader_Builtin( BUILTIN_BINK_GUI );
@ -949,6 +955,7 @@ private:
idStr StripDeadCode( const idStr& in, const char* name, const idStrList& compileMacros, bool builtin );
idStr ConvertCG2GLSL( const idStr& in, const char* name, rpStage_t stage, idStr& outLayout, bool vkGLSL, bool hasGPUSkinning, vertexLayoutType_t vertexLayout );
BUILTIN_BINK_SRGB, // SRS - Added Bink shader without sRGB to linear conversion for testVideo cmd
int builtinShaders[MAX_BUILTINS];
void BindShader_Builtin( int i )
{

View file

@ -652,7 +652,8 @@ void R_TestVideo_f( const idCmdArgs& args )
cinData_t cin;
cin = tr.testVideo->ImageForTime( 0 );
if( cin.imageY == NULL )
// SRS - Also handle ffmpeg and original RoQ decoders for test videos (using cin.image)
if( cin.imageY == NULL && cin.image == NULL )
{
delete tr.testVideo;
tr.testVideo = NULL;
@ -665,7 +666,8 @@ void R_TestVideo_f( const idCmdArgs& args )
int len = tr.testVideo->AnimationLength();
common->Printf( "%5.1f seconds of video\n", len * 0.001 );
tr.testVideoStartTime = tr.primaryRenderView.time[1];
// SRS - Not needed or used since InitFromFile() sets the correct start time automatically
//tr.testVideoStartTime = tr.primaryRenderView.time[1];
// try to play the matching wav file
idStr wavString = args.Argv( ( args.Argc() == 2 ) ? 1 : 2 );

View file

@ -493,17 +493,6 @@ void idImage::CopyDepthbuffer( int x, int y, int imageWidth, int imageHeight )
}
/*
========================
idImage::SetPixel
========================
*/
void idImage::SetPixel( int mipLevel, int x, int y, const void* data, int dataSize )
{
SubImageUpload( mipLevel, x, y, 0, 1, 1, data );
}
/*
========================
idImage::SetTexParameters

View file

@ -546,13 +546,20 @@ void idRenderBackend::DBG_TestImage()
{
cinData_t cin;
cin = tr.testVideo->ImageForTime( viewDef->renderView.time[1] - tr.testVideoStartTime );
// SRS - Don't need calibrated time for testing cinematics, so just call ImageForTime( 0 ) for current system time
// This simplification allows cinematic test playback to work over both 2D and 3D background scenes
cin = tr.testVideo->ImageForTime( 0 /*viewDef->renderView.time[1] - tr.testVideoStartTime*/ );
if( cin.imageY != NULL )
{
image = cin.imageY;
imageCr = cin.imageCr;
imageCb = cin.imageCb;
}
// SRS - Also handle ffmpeg and original RoQ decoders for test videos (using cin.image)
else if( cin.image != NULL )
{
image = cin.image;
}
else
{
tr.testImage = NULL;
@ -593,7 +600,7 @@ void idRenderBackend::DBG_TestImage()
scale[0] = w; // scale
scale[5] = h; // scale
scale[12] = halfScreenWidth - ( halfScreenWidth * w ); // translate
scale[13] = halfScreenHeight - ( halfScreenHeight * h ); // translate
scale[13] = halfScreenHeight - ( halfScreenHeight * h ) - h; // translate (SRS - moved up by h)
scale[10] = 1.0f;
scale[15] = 1.0f;
@ -629,7 +636,9 @@ void idRenderBackend::DBG_TestImage()
GL_SelectTexture( 2 );
imageCb->Bind();
renderProgManager.BindShader_Bink();
// SRS - Use Bink shader with no sRGB to linear conversion, otherwise cinematic colours may be wrong
// BindShader_BinkGUI() does not seem to work here - perhaps due to vertex shader input dependencies?
renderProgManager.BindShader_Bink_sRGB();
}
else
{

View file

@ -30,6 +30,7 @@ class CinematicAudio
public:
virtual void InitAudio( void* audioContext ) = 0;
virtual void PlayAudio( uint8_t* data, int size ) = 0;
virtual void ResetAudio() = 0;
virtual void ShutdownAudio() = 0;
};

View file

@ -108,28 +108,35 @@ void CinematicAudio_OpenAL::PlayAudio( uint8_t* data, int size )
if( trigger )
{
tBuffer->push( data );
sizes->push( size );
tBuffer.push( data );
sizes.push( size );
while( processed > 0 )
{
ALuint bufid;
alSourceUnqueueBuffers( alMusicSourceVoicecin, 1, &bufid );
processed--;
if( !tBuffer->empty() )
// SRS - Only unqueue an alBuffer if we don't already have a free bufid to use
if( bufids.size() == 0 )
{
int tempSize = sizes->front();
sizes->pop();
uint8_t* tempdata = tBuffer->front();
tBuffer->pop();
alSourceUnqueueBuffers( alMusicSourceVoicecin, 1, &bufid );
bufids.push( bufid );
processed--;
}
if( !tBuffer.empty() )
{
uint8_t* tempdata = tBuffer.front();
tBuffer.pop();
int tempSize = sizes.front();
sizes.pop();
if( tempSize > 0 )
{
bufid = bufids.front();
bufids.pop();
alBufferData( bufid, av_sample_cin, tempdata, tempSize, av_rate_cin );
// SRS - We must free the audio buffer once it has been copied into an alBuffer
#if defined(USE_FFMPEG)
av_freep( &tempdata );
#elif defined(USE_BINKDEC)
free( tempdata );
Mem_Free( tempdata );
#endif
alSourceQueueBuffers( alMusicSourceVoicecin, 1, &bufid );
ALenum error = alGetError();
@ -140,6 +147,10 @@ void CinematicAudio_OpenAL::PlayAudio( uint8_t* data, int size )
}
}
}
else // SRS - When no new audio frames left to queue, break and continue playing
{
break;
}
}
}
else
@ -149,7 +160,7 @@ void CinematicAudio_OpenAL::PlayAudio( uint8_t* data, int size )
#if defined(USE_FFMPEG)
av_freep( &data );
#elif defined(USE_BINKDEC)
free( data );
Mem_Free( data );
#endif
offset++;
if( offset == NUM_BUFFERS )
@ -186,18 +197,44 @@ void CinematicAudio_OpenAL::PlayAudio( uint8_t* data, int size )
}
}
void CinematicAudio_OpenAL::ResetAudio()
{
if( alIsSource( alMusicSourceVoicecin ) )
{
alSourceRewind( alMusicSourceVoicecin );
alSourcei( alMusicSourceVoicecin, AL_BUFFER, 0 );
}
while( !tBuffer.empty() )
{
uint8_t* tempdata = tBuffer.front();
tBuffer.pop();
sizes.pop();
if( tempdata )
{
// SRS - We must free any audio buffers that have not been copied into an alBuffer
#if defined(USE_FFMPEG)
av_freep( &tempdata );
#elif defined(USE_BINKDEC)
Mem_Free( tempdata );
#endif
}
}
while( !bufids.empty() )
{
bufids.pop();
}
offset = 0;
trigger = false;
}
void CinematicAudio_OpenAL::ShutdownAudio()
{
if( alIsSource( alMusicSourceVoicecin ) )
{
alSourceStop( alMusicSourceVoicecin );
// SRS - Make sure we don't try to unqueue buffers that were never processed
ALint processed;
alGetSourcei( alMusicSourceVoicecin, AL_BUFFERS_PROCESSED, &processed );
if( processed > 0 )
{
alSourceUnqueueBuffers( alMusicSourceVoicecin, processed, alMusicBuffercin );
}
alSourcei( alMusicSourceVoicecin, AL_BUFFER, 0 );
alDeleteSources( 1, &alMusicSourceVoicecin );
if( CheckALErrors() == AL_NO_ERROR )
@ -210,29 +247,25 @@ void CinematicAudio_OpenAL::ShutdownAudio()
{
alDeleteBuffers( NUM_BUFFERS, alMusicBuffercin );
}
if( !tBuffer->empty() )
while( !tBuffer.empty() )
{
int buffersize = tBuffer->size();
while( buffersize > 0 )
uint8_t* tempdata = tBuffer.front();
tBuffer.pop();
sizes.pop();
if( tempdata )
{
uint8_t* tempdata = tBuffer->front();
tBuffer->pop();
// SRS - We must free any audio buffers that have not been copied into an alBuffer
#if defined(USE_FFMPEG)
av_freep( &tempdata );
#elif defined(USE_BINKDEC)
free( tempdata );
Mem_Free( tempdata );
#endif
buffersize--;
}
}
if( !sizes->empty() )
while( !bufids.empty() )
{
int buffersize = sizes->size();
while( buffersize > 0 )
{
sizes->pop();
buffersize--;
}
bufids.pop();
}
}

View file

@ -41,6 +41,7 @@ public:
CinematicAudio_OpenAL();
void InitAudio( void* audioContext );
void PlayAudio( uint8_t* data, int size );
void ResetAudio();
void ShutdownAudio();
private:
ALuint alMusicSourceVoicecin;
@ -55,8 +56,9 @@ private:
// So, what happens if there are no freely available buffers but we still geting audio frames ? Loss of data.
// That why now I am using two queues in order to store the frames (and their sizes) and when we have available buffers,
// then start popping those frames instead of the current, so we don't lose any audio frames and the sound doesn't crack anymore.
std::queue<uint8_t*> tBuffer[NUM_BUFFERS];
std::queue<int> sizes[NUM_BUFFERS];
std::queue<uint8_t*> tBuffer;
std::queue<int> sizes;
std::queue<ALuint> bufids; // SRS - Added queue of free alBuffer ids to handle audio frame underflow (starvation) case
};
#endif

View file

@ -206,11 +206,14 @@ void idSoundVoice_OpenAL::DestroyInternal()
idLib::Printf( "%dms: %i destroyed\n", Sys_Milliseconds(), openalSource );
}
// SRS - Make sure the source is stopped before detaching buffers
alSourceStop( openalSource );
alSourcei( openalSource, AL_BUFFER, 0 );
// SRS - Delete source only after detaching buffers above
alDeleteSources( 1, &openalSource );
openalSource = 0;
alSourcei( openalSource, AL_BUFFER, 0 );
if( openalStreamingBuffer[0] && openalStreamingBuffer[1] && openalStreamingBuffer[2] )
{
CheckALErrors();

View file

@ -52,7 +52,7 @@ public:
#if defined(USE_FFMPEG)
av_freep( &data );
#elif defined(USE_BINKDEC)
free( data );
Mem_Free( data );
#endif
}
//Unused methods are stubs
@ -179,6 +179,15 @@ void CinematicAudio_XAudio2::PlayAudio( uint8_t* data, int size )
}
}
void CinematicAudio_XAudio2::ResetAudio()
{
if( pMusicSourceVoice1 )
{
pMusicSourceVoice1->Stop();
pMusicSourceVoice1->FlushSourceBuffers();
}
}
void CinematicAudio_XAudio2::ShutdownAudio()
{
if( pMusicSourceVoice1 )

View file

@ -33,6 +33,7 @@ public:
CinematicAudio_XAudio2();
void InitAudio( void* audioContext );
void PlayAudio( uint8_t* data, int size );
void ResetAudio();
void ShutdownAudio();
private:
WAVEFORMATEX voiceFormatcine = { 0 };

View file

@ -532,6 +532,7 @@ cinData_t idSoundSystemLocal::ImageForTime( const int milliseconds, const bool w
cd.imageY = NULL;
cd.imageCr = NULL;
cd.imageCb = NULL;
cd.image = NULL;
cd.imageWidth = 0;
cd.imageHeight = 0;
cd.status = FMV_IDLE;

View file

@ -183,7 +183,7 @@ Now that all images have been found, allocate them in an atlas
and write it out.
========================
*/
void RectAllocator( const idList<idVec2i>& inputSizes, idList<idVec2i>& outputPositions, idVec2i& totalSize );
void RectAllocator( const idList<idVec2i>& inputSizes, idList<idVec2i>& outputPositions, idVec2i& totalSize, const int START_MAX = 16384, const int imageMax = 1024 );
float RectPackingFraction( const idList<idVec2i>& inputSizes, const idVec2i totalSize );
void idSWF::WriteSwfImageAtlas( const char* filename )

View file

@ -1,5 +1,6 @@
REM 7z a RBDOOM-3-BFG-1.3.1.1-lite-win64-20220109-git-xxxxxxx.7z -r base/env/ base/maps/*.lightgrid base/maps/*_extra_ents.map -x!generated
set filename=RBDOOM-3-BFG-1.4.0.5-lite-win64-20220219-git-xxxxxxx.7z
7z a %filename% README.md RELEASE-NOTES.md base/devtools.cfg base/modelviewer.cfg base/extract_resources.cfg base/convert_maps_to_valve220.cfg base/def/*.def base/materials/*.mtr base/textures/common base/textures/editor -x!generated -xr!*.xcf -xr!*.blend
set filename=RBDOOM-3-BFG-1.4.0.9-lite-win64-20220306-git-xxxxxxx.7z
7z a %filename% README.md RELEASE-NOTES.md base/devtools.cfg base/modelviewer.cfg base/extract_resources.cfg base/convert_maps_to_valve220.cfg base/def/*.def base/materials/*.mtr base/textures/common base/textures/editor base/maps/zoomaps -x!generated -xr!autosave -xr!*.xcf -xr!*.blend
7z a %filename% README.md RELEASE-NOTES.md base/_tb/fgd/*.fgd
7z a %filename% README.md RELEASE-NOTES.md tools/trenchbroom -xr!TrenchBroom-nomanual* -xr!TrenchBroom.pdb
pause