mirror of
https://github.com/id-Software/DOOM-3-BFG.git
synced 2025-04-24 10:38:53 +00:00
Merge branch 'master' into 635-nvrhi1
This commit is contained in:
commit
a18d2609ce
34 changed files with 2647 additions and 310 deletions
|
@ -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)
|
||||
|
|
13
README.md
13
README.md
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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."
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 );
|
||||
|
||||
|
|
|
@ -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
|
@ -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();
|
||||
|
|
|
@ -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] );
|
||||
|
|
|
@ -142,6 +142,7 @@ protected:
|
|||
idVec3 texMat[2];
|
||||
idVec3 origin;
|
||||
|
||||
public:
|
||||
// RB
|
||||
idVec3 planepts[ 3 ]; // for writing back original planepts
|
||||
ProjectionType projection;
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
}
|
704
neo/libs/binpack2d/binpack2d.h
Normal file
704
neo/libs/binpack2d/binpack2d.h
Normal 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 ***/
|
|
@ -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 )
|
||||
{
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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" );
|
||||
|
||||
|
|
|
@ -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 )
|
||||
{
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 )
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 )
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue