2137 lines
71 KiB
C++
2137 lines
71 KiB
C++
/*
|
|
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
|
|
For a list of contributors, see the accompanying CONTRIBUTORS file.
|
|
|
|
This file is part of GtkRadiant.
|
|
|
|
GtkRadiant is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
GtkRadiant is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with GtkRadiant; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
//
|
|
// Texture Window
|
|
//
|
|
// Leonardo Zide (leo@lokigames.com)
|
|
//
|
|
|
|
#include "texwindow.h"
|
|
|
|
#include "debugging/debugging.h"
|
|
|
|
#include "ifilesystem.h"
|
|
#include "iundo.h"
|
|
#include "igl.h"
|
|
#include "iarchive.h"
|
|
#include "moduleobserver.h"
|
|
|
|
#include <set>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include <QVBoxLayout>
|
|
#include <QHBoxLayout>
|
|
#include <QLineEdit>
|
|
#include <QApplication>
|
|
#include <QStyle>
|
|
#include <QTreeView>
|
|
#include <QHeaderView>
|
|
#include <QStandardItemModel>
|
|
#include <QListWidget>
|
|
#include <QListWidgetItem>
|
|
#include <QItemDelegate>
|
|
#include <QMetaProperty>
|
|
#include <QScrollBar>
|
|
#include <QSplitter>
|
|
#include <QOpenGLWidget>
|
|
#include <QTabWidget>
|
|
|
|
#include "signal/signal.h"
|
|
#include "math/vector.h"
|
|
#include "texturelib.h"
|
|
#include "string/string.h"
|
|
#include "shaderlib.h"
|
|
#include "os/file.h"
|
|
#include "os/path.h"
|
|
#include "stream/memstream.h"
|
|
#include "stream/textfilestream.h"
|
|
#include "stream/stringstream.h"
|
|
#include "commandlib.h"
|
|
#include "texmanip.h"
|
|
#include "textures.h"
|
|
|
|
#include "gtkutil/menu.h"
|
|
#include "gtkutil/nonmodal.h"
|
|
#include "gtkutil/cursor.h"
|
|
#include "gtkutil/widget.h"
|
|
#include "gtkutil/glwidget.h"
|
|
#include "gtkutil/messagebox.h"
|
|
#include "gtkutil/toolbar.h"
|
|
#include "gtkutil/mousepresses.h"
|
|
#include "gtkutil/guisettings.h"
|
|
|
|
#include "error.h"
|
|
#include "map.h"
|
|
#include "qgl.h"
|
|
#include "select.h"
|
|
#include "brush_primit.h"
|
|
#include "brushmanip.h"
|
|
#include "patchmanip.h"
|
|
#include "plugin.h"
|
|
#include "qe3.h"
|
|
#include "gtkdlgs.h"
|
|
#include "gtkmisc.h"
|
|
#include "mainframe.h"
|
|
#include "findtexturedialog.h"
|
|
#include "surfacedialog.h"
|
|
#include "patchdialog.h"
|
|
#include "groupdialog.h"
|
|
#include "preferences.h"
|
|
#include "commands.h"
|
|
|
|
bool string_equal_start( const char* string, StringRange start ){
|
|
return string_equal_n( string, start.data(), start.size() );
|
|
}
|
|
|
|
// sort case insensitively, as it is user friendly
|
|
// still preserve unequal names, as it is needed for linux case sensitive FS
|
|
struct TextureGroups_compare
|
|
{
|
|
bool operator()( const CopiedString& a, const CopiedString& b ) const {
|
|
const int cmp_nocase = string_compare_nocase( a.c_str(), b.c_str() );
|
|
if( cmp_nocase != 0 )
|
|
return cmp_nocase < 0;
|
|
else
|
|
return string_less( a.c_str(), b.c_str() );
|
|
}
|
|
};
|
|
|
|
typedef std::set<CopiedString, TextureGroups_compare> TextureGroups;
|
|
|
|
void TextureGroups_addWad( TextureGroups& groups, const char* archive ){
|
|
if ( path_extension_is( archive, "wad" ) ) {
|
|
#if 1
|
|
groups.insert( archive );
|
|
#else
|
|
CopiedString archiveBaseName( PathFilename( archive ) );
|
|
groups.insert( archiveBaseName );
|
|
#endif
|
|
}
|
|
}
|
|
typedef ReferenceCaller1<TextureGroups, const char*, TextureGroups_addWad> TextureGroupsAddWadCaller;
|
|
|
|
void TextureGroups_addShader( TextureGroups& groups, const char* shaderName ){
|
|
const char* texture = path_make_relative( shaderName, "textures/" );
|
|
if ( texture != shaderName ) {
|
|
const char* last = path_remove_directory( texture );
|
|
if ( !string_empty( last ) ) {
|
|
groups.insert( CopiedString( StringRange( texture, --last ) ) );
|
|
}
|
|
}
|
|
}
|
|
typedef ReferenceCaller1<TextureGroups, const char*, TextureGroups_addShader> TextureGroupsAddShaderCaller;
|
|
|
|
void TextureGroups_addDirectory( TextureGroups& groups, const char* directory ){
|
|
groups.insert( directory );
|
|
}
|
|
typedef ReferenceCaller1<TextureGroups, const char*, TextureGroups_addDirectory> TextureGroupsAddDirectoryCaller;
|
|
|
|
namespace
|
|
{
|
|
bool g_TextureBrowser_shaderlistOnly = false;
|
|
bool g_TextureBrowser_fixedSize = true;
|
|
bool g_TextureBrowser_filterNotex = false;
|
|
bool g_TextureBrowser_enableAlpha = false;
|
|
bool g_TextureBrowser_filter_searchFromStart = false;
|
|
}
|
|
|
|
|
|
enum StartupShaders
|
|
{
|
|
STARTUPSHADERS_NONE = 0,
|
|
STARTUPSHADERS_COMMON,
|
|
};
|
|
|
|
|
|
class TextureBrowser
|
|
{
|
|
int m_originy;
|
|
int m_nTotalHeight;
|
|
public:
|
|
int m_width, m_height;
|
|
|
|
CopiedString m_shader; // current shader
|
|
|
|
QWidget* m_parent;
|
|
QOpenGLWidget* m_gl_widget;
|
|
QScrollBar* m_texture_scroll;
|
|
QTabWidget* m_tabs;
|
|
QTreeView* m_treeView;
|
|
QStandardItemModel* m_treeViewModel;
|
|
QListWidget* m_tagsListWidget;
|
|
QMenu* m_tagsMenu;
|
|
QAction* m_shader_info_item{};
|
|
QLineEdit* m_filter_entry;
|
|
QAction* m_filter_action;
|
|
CopiedString m_filter_string;
|
|
|
|
std::set<CopiedString> m_all_tags;
|
|
std::vector<CopiedString> m_copied_tags;
|
|
std::set<CopiedString> m_found_shaders;
|
|
|
|
ToggleItem m_hideunused_item;
|
|
ToggleItem m_showshaders_item;
|
|
ToggleItem m_showtextures_item;
|
|
ToggleItem m_showshaderlistonly_item;
|
|
ToggleItem m_fixedsize_item;
|
|
ToggleItem m_filternotex_item;
|
|
ToggleItem m_enablealpha_item;
|
|
ToggleItem m_tags_item;
|
|
ToggleItem m_filter_searchFromStart_item;
|
|
|
|
bool m_heightChanged;
|
|
bool m_originInvalid;
|
|
|
|
DeferredAdjustment m_scrollAdjustment;
|
|
FreezePointer m_freezePointer;
|
|
|
|
Vector3 m_color_textureback;
|
|
// the increment step we use against the wheel mouse
|
|
int m_mouseWheelScrollIncrement;
|
|
std::size_t m_textureScale;
|
|
|
|
bool m_showShaders;
|
|
bool m_showTextures;
|
|
bool m_showTextureScrollbar;
|
|
StartupShaders m_startupShaders;
|
|
// if true, the texture window will only display in-use shaders
|
|
// if false, all the shaders in memory are displayed
|
|
bool m_hideUnused;
|
|
bool m_searchedTags; // flag to show m_found_shaders
|
|
bool m_tags; // whether to show tags gui
|
|
bool m_move_started;
|
|
// The uniform size (in pixels) that textures are resized to when m_resizeTextures is true.
|
|
int m_uniformTextureSize;
|
|
int m_uniformTextureMinSize;
|
|
|
|
bool m_hideNonShadersInCommon;
|
|
|
|
static bool wads;
|
|
|
|
TextureBrowser() :
|
|
m_texture_scroll( 0 ),
|
|
m_hideunused_item( BoolExportCaller( m_hideUnused ) ),
|
|
m_showshaders_item( BoolExportCaller( m_showShaders ) ),
|
|
m_showtextures_item( BoolExportCaller( m_showTextures ) ),
|
|
m_showshaderlistonly_item( BoolExportCaller( g_TextureBrowser_shaderlistOnly ) ),
|
|
m_fixedsize_item( BoolExportCaller( g_TextureBrowser_fixedSize ) ),
|
|
m_filternotex_item( BoolExportCaller( g_TextureBrowser_filterNotex ) ),
|
|
m_enablealpha_item( BoolExportCaller( g_TextureBrowser_enableAlpha ) ),
|
|
m_tags_item( BoolExportCaller( m_tags ) ),
|
|
m_filter_searchFromStart_item( BoolExportCaller( g_TextureBrowser_filter_searchFromStart ) ),
|
|
m_heightChanged( true ),
|
|
m_originInvalid( true ),
|
|
m_scrollAdjustment( [this]( int value ){
|
|
//globalOutputStream() << "vertical scroll\n";
|
|
setOriginY( -value );
|
|
} ),
|
|
m_color_textureback( 0.25f, 0.25f, 0.25f ),
|
|
m_mouseWheelScrollIncrement( 64 ),
|
|
m_textureScale( 50 ),
|
|
m_showShaders( true ),
|
|
m_showTextures( true ),
|
|
m_showTextureScrollbar( true ),
|
|
m_startupShaders( STARTUPSHADERS_NONE ),
|
|
m_hideUnused( false ),
|
|
m_searchedTags( false ),
|
|
m_tags( false ),
|
|
m_move_started( false ),
|
|
m_uniformTextureSize( 160 ),
|
|
m_uniformTextureMinSize( 48 ),
|
|
m_hideNonShadersInCommon( true ){
|
|
}
|
|
void queueDraw() const {
|
|
if ( m_gl_widget != nullptr )
|
|
widget_queue_draw( *m_gl_widget );
|
|
}
|
|
void draw();
|
|
void setOriginY( int originy ){
|
|
m_originy = originy;
|
|
clampOriginY();
|
|
updateScroll();
|
|
queueDraw();
|
|
}
|
|
private:
|
|
void clampOriginY(){
|
|
m_originy = std::clamp( m_originy, m_height - totalHeight(), 0 );
|
|
}
|
|
void evaluateHeight();
|
|
int totalHeight(){
|
|
evaluateHeight();
|
|
return m_nTotalHeight;
|
|
}
|
|
public:
|
|
int getOriginY(){
|
|
if ( m_originInvalid ) {
|
|
m_originInvalid = false;
|
|
clampOriginY();
|
|
updateScroll();
|
|
}
|
|
return m_originy;
|
|
}
|
|
void heightChanged(){
|
|
m_heightChanged = true;
|
|
updateScroll();
|
|
queueDraw();
|
|
}
|
|
void updateScroll(){
|
|
if ( m_showTextureScrollbar ) {
|
|
const int total_height = std::max( totalHeight(), m_height );
|
|
|
|
QScrollBar* s = m_texture_scroll;
|
|
s->setMinimum( 0 );
|
|
s->setMaximum( total_height - m_height );
|
|
s->setValue( - getOriginY() );
|
|
s->setPageStep( m_height );
|
|
s->setSingleStep( 20 );
|
|
}
|
|
}
|
|
// Return the display width of a texture in the texture browser
|
|
auto getTextureWH( const qtexture_t* tex ) const {
|
|
// Don't use uniform size
|
|
int W = std::max( std::size_t( 1 ), tex->width * m_textureScale / 100 );
|
|
int H = std::max( std::size_t( 1 ), tex->height * m_textureScale / 100 );
|
|
|
|
if ( g_TextureBrowser_fixedSize ){
|
|
if ( W >= H ) {
|
|
// Texture is square, or wider than it is tall
|
|
if ( W > m_uniformTextureSize ){
|
|
H = m_uniformTextureSize * H / W;
|
|
W = m_uniformTextureSize;
|
|
}
|
|
else if ( W < m_uniformTextureMinSize ){
|
|
H = m_uniformTextureMinSize * H / W;
|
|
W = m_uniformTextureMinSize;
|
|
}
|
|
}
|
|
else {
|
|
// Texture taller than it is wide
|
|
if ( H > m_uniformTextureSize ){
|
|
W = m_uniformTextureSize * W / H;
|
|
H = m_uniformTextureSize;
|
|
}
|
|
else if ( H < m_uniformTextureMinSize ){
|
|
W = m_uniformTextureMinSize * W / H;
|
|
H = m_uniformTextureMinSize;
|
|
}
|
|
}
|
|
}
|
|
return std::pair( W, H );
|
|
}
|
|
};
|
|
bool TextureBrowser::wads = false;
|
|
|
|
static TextureBrowser g_TexBro;
|
|
|
|
void ( *TextureBrowser_textureSelected )( const char* shader );
|
|
|
|
|
|
inline const char* TextureBrowser_getCommonShadersName(){
|
|
static const char* const value = string_empty( g_pGameDescription->getKeyValue( "common_shaders_name" ) )
|
|
? "Common"
|
|
: g_pGameDescription->getKeyValue( "common_shaders_name" );
|
|
return value;
|
|
}
|
|
|
|
inline const char* TextureBrowser_getCommonShadersDir(){
|
|
static const char* const value = string_empty( g_pGameDescription->getKeyValue( "common_shaders_dir" ) )
|
|
? "common/"
|
|
: g_pGameDescription->getKeyValue( "common_shaders_dir" );
|
|
return value;
|
|
}
|
|
|
|
inline int TextureBrowser_fontHeight(){
|
|
return GlobalOpenGL().m_font->getPixelHeight();
|
|
}
|
|
|
|
const char* TextureBrowser_GetSelectedShader(){
|
|
return g_TexBro.m_shader.c_str();
|
|
}
|
|
|
|
void TextureBrowser_SetStatus( const char* name ){
|
|
IShader* shader = QERApp_Shader_ForName( name );
|
|
qtexture_t* q = shader->getTexture();
|
|
StringOutputStream strTex( 256 );
|
|
strTex << ( string_equal_prefix_nocase( name, "textures/" )? name + 9 : name ) << " W: " << q->width << " H: " << q->height;
|
|
shader->DecRef();
|
|
g_pParentWnd->SetStatusText( c_status_texture, strTex.c_str() );
|
|
}
|
|
|
|
void TextureBrowser_Focus( TextureBrowser& textureBrowser, const char* name );
|
|
void TextureBrowser_tagsSetCheckboxesForShader( const char *shader );
|
|
|
|
void TextureBrowser_SetSelectedShader( TextureBrowser& textureBrowser, const char* shader ){
|
|
textureBrowser.m_shader = shader;
|
|
TextureBrowser_SetStatus( shader );
|
|
TextureBrowser_Focus( textureBrowser, shader );
|
|
|
|
if ( FindTextureDialog_isOpen() ) {
|
|
FindTextureDialog_selectTexture( shader );
|
|
}
|
|
|
|
// disable the menu item "shader info" if no shader was selected
|
|
if ( textureBrowser.m_shader_info_item != nullptr ){
|
|
IShader* ishader = QERApp_Shader_ForName( shader );
|
|
CopiedString filename = ishader->getShaderFileName();
|
|
|
|
textureBrowser.m_shader_info_item->setDisabled( filename.empty() );
|
|
|
|
ishader->DecRef();
|
|
}
|
|
|
|
if( textureBrowser.m_tabs->currentIndex() == 1 )
|
|
TextureBrowser_tagsSetCheckboxesForShader( shader );
|
|
}
|
|
|
|
void TextureBrowser_SetSelectedShader( const char* shader ){
|
|
TextureBrowser_SetSelectedShader( g_TexBro, shader );
|
|
}
|
|
|
|
|
|
CopiedString g_TextureBrowser_currentDirectory;
|
|
|
|
/*
|
|
============================================================================
|
|
|
|
TEXTURE LAYOUT
|
|
|
|
TTimo: now based on a rundown through all the shaders
|
|
NOTE: we expect the Active shaders count doesn't change during a Texture_StartPos .. Texture_NextPos cycle
|
|
otherwise we may need to rely on a list instead of an array storage
|
|
============================================================================
|
|
*/
|
|
|
|
struct TextureLayout
|
|
{
|
|
// texture layout functions
|
|
// TTimo: now based on shaders
|
|
int current_x = 8;
|
|
int current_y = -4;
|
|
int current_row = 0;
|
|
|
|
auto nextPos( const TextureBrowser& textureBrowser, qtexture_t* current_texture ){
|
|
const auto [nWidth, nHeight] = textureBrowser.getTextureWH( current_texture );
|
|
if ( current_x + nWidth > textureBrowser.m_width - 8 && current_row ) { // go to the next row unless the texture is the first on the row
|
|
current_x = 8;
|
|
current_y -= current_row + TextureBrowser_fontHeight() + 1;//+4
|
|
current_row = 0;
|
|
}
|
|
|
|
const int x = current_x;
|
|
const int y = current_y;
|
|
|
|
// Is our texture larger than the row? If so, grow the
|
|
// row height to match it
|
|
|
|
if ( current_row < nHeight ) {
|
|
current_row = nHeight;
|
|
}
|
|
|
|
// never go less than 96, or the names get all crunched up
|
|
current_x += std::max( nWidth, 96 ) + 8;
|
|
|
|
return std::pair( x, y );
|
|
}
|
|
};
|
|
|
|
bool Texture_filtered( const char* name, const TextureBrowser& textureBrowser ){
|
|
const char* filter = textureBrowser.m_filter_string.c_str();
|
|
if( string_empty( filter ) ){
|
|
return false;
|
|
}
|
|
if( g_TextureBrowser_filter_searchFromStart ){
|
|
if( string_equal_prefix_nocase( name, filter ) ){
|
|
return false;
|
|
}
|
|
}
|
|
else{
|
|
if( string_in_string_nocase( name, filter ) != 0 ){
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
CopiedString g_notex;
|
|
CopiedString g_shadernotex;
|
|
|
|
// if texture_showinuse jump over non in-use textures
|
|
/*
|
|
bool show_shaders, bool show_textures, bool hideUnused, bool hideNonShadersInCommon
|
|
textureBrowser.m_showShaders, textureBrowser.m_showTextures, textureBrowser.m_hideUnused, textureBrowser.m_hideNonShadersInCommon
|
|
*/
|
|
bool Texture_IsShown( IShader* shader, const TextureBrowser& textureBrowser ){
|
|
// filter notex / shadernotex images
|
|
if ( g_TextureBrowser_filterNotex && ( string_equal( g_notex.c_str(), shader->getTexture()->name ) || string_equal( g_shadernotex.c_str(), shader->getTexture()->name ) ) ) {
|
|
return false;
|
|
}
|
|
|
|
if ( !shader_equal_prefix( shader->getName(), "textures/" ) ) {
|
|
return false;
|
|
}
|
|
|
|
if ( !textureBrowser.m_showShaders && !shader->IsDefault() ) {
|
|
return false;
|
|
}
|
|
|
|
if ( !textureBrowser.m_showTextures && shader->IsDefault() ) {
|
|
return false;
|
|
}
|
|
|
|
if ( textureBrowser.m_hideUnused && !shader->IsInUse() ) {
|
|
return false;
|
|
}
|
|
|
|
if( textureBrowser.m_hideNonShadersInCommon && shader->IsDefault() && !shader->IsInUse() //&& g_TextureBrowser_currentDirectory != ""
|
|
&& shader_equal_prefix( shader_get_textureName( shader->getName() ), TextureBrowser_getCommonShadersDir() ) ){
|
|
return false;
|
|
}
|
|
|
|
if ( textureBrowser.m_searchedTags ) {
|
|
return textureBrowser.m_found_shaders.find( shader->getName() ) != textureBrowser.m_found_shaders.cend();
|
|
}
|
|
else {
|
|
if ( !shader_equal_prefix( shader_get_textureName( shader->getName() ), g_TextureBrowser_currentDirectory.c_str() ) ) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if( Texture_filtered( path_get_filename_start( shader->getName() ), textureBrowser ) ){
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void TextureBrowser::evaluateHeight(){
|
|
if ( m_heightChanged ) {
|
|
m_heightChanged = false;
|
|
|
|
m_nTotalHeight = 0;
|
|
|
|
TextureLayout layout;
|
|
for ( QERApp_ActiveShaders_IteratorBegin(); !QERApp_ActiveShaders_IteratorAtEnd(); QERApp_ActiveShaders_IteratorIncrement() )
|
|
{
|
|
IShader* shader = QERApp_ActiveShaders_IteratorCurrent();
|
|
|
|
if ( Texture_IsShown( shader, *this ) ) {
|
|
layout.nextPos( *this, shader->getTexture() );
|
|
const auto [nWidth, nHeight] = getTextureWH( shader->getTexture() );
|
|
m_nTotalHeight = std::max( m_nTotalHeight, abs( layout.current_y ) + TextureBrowser_fontHeight() + nHeight + 4 );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
Signal0 g_activeShadersChangedCallbacks;
|
|
|
|
void TextureBrowser_addActiveShadersChangedCallback( const SignalHandler& handler ){
|
|
g_activeShadersChangedCallbacks.connectLast( handler );
|
|
}
|
|
|
|
class ShadersObserver : public ModuleObserver
|
|
{
|
|
Signal0 m_realiseCallbacks;
|
|
public:
|
|
void realise(){
|
|
m_realiseCallbacks();
|
|
}
|
|
void unrealise(){
|
|
}
|
|
void insert( const SignalHandler& handler ){
|
|
m_realiseCallbacks.connectLast( handler );
|
|
}
|
|
};
|
|
|
|
namespace
|
|
{
|
|
ShadersObserver g_ShadersObserver;
|
|
}
|
|
|
|
void TextureBrowser_addShadersRealiseCallback( const SignalHandler& handler ){
|
|
g_ShadersObserver.insert( handler );
|
|
}
|
|
|
|
void TextureBrowser_activeShadersChanged( TextureBrowser& textureBrowser ){
|
|
textureBrowser.heightChanged();
|
|
textureBrowser.m_originInvalid = true;
|
|
|
|
g_activeShadersChangedCallbacks();
|
|
}
|
|
|
|
void TextureBrowser_importShowScrollbar( TextureBrowser& textureBrowser, bool value ){
|
|
textureBrowser.m_showTextureScrollbar = value;
|
|
if ( textureBrowser.m_texture_scroll != 0 ) {
|
|
textureBrowser.m_texture_scroll->setVisible( textureBrowser.m_showTextureScrollbar );
|
|
textureBrowser.updateScroll();
|
|
}
|
|
}
|
|
typedef ReferenceCaller1<TextureBrowser, bool, TextureBrowser_importShowScrollbar> TextureBrowserImportShowScrollbarCaller;
|
|
|
|
|
|
/*
|
|
==============
|
|
TextureBrowser_ShowDirectory
|
|
relies on texture_directory global for the directory to use
|
|
1) Load the shaders for the given directory
|
|
2) Scan the remaining texture, load them and assign them a default shader (the "noshader" shader)
|
|
NOTE: when writing a texture plugin, or some texture extensions, this function may need to be overridden, and made
|
|
available through the IShaders interface
|
|
NOTE: for texture window layout:
|
|
all shaders are stored with alphabetical order after load
|
|
previously loaded and displayed stuff is hidden, only in-use and newly loaded is shown
|
|
( the GL textures are not flushed though)
|
|
==============
|
|
*/
|
|
|
|
inline bool texture_name_ignore( const char* name ){
|
|
auto temp = StringOutputStream( 64 )( LowerCase( name ) );
|
|
|
|
return
|
|
string_equal_suffix( temp, ".specular" ) ||
|
|
string_equal_suffix( temp, ".glow" ) ||
|
|
string_equal_suffix( temp, ".bump" ) ||
|
|
string_equal_suffix( temp, ".diffuse" ) ||
|
|
string_equal_suffix( temp, ".blend" ) ||
|
|
string_equal_suffix( temp, ".alpha" ) ||
|
|
string_equal_suffix( temp, "_norm" ) ||
|
|
string_equal_suffix( temp, "_bump" ) ||
|
|
string_equal_suffix( temp, "_glow" ) ||
|
|
string_equal_suffix( temp, "_gloss" ) ||
|
|
string_equal_suffix( temp, "_pants" ) ||
|
|
string_equal_suffix( temp, "_shirt" ) ||
|
|
string_equal_suffix( temp, "_reflect" ) ||
|
|
string_equal_suffix( temp, "_alpha" ) ||
|
|
0;
|
|
}
|
|
|
|
class LoadShaderVisitor : public Archive::Visitor
|
|
{
|
|
public:
|
|
void visit( const char* name ){
|
|
IShader* shader = QERApp_Shader_ForName( CopiedString( PathExtensionless( name ) ).c_str() );
|
|
shader->DecRef();
|
|
}
|
|
};
|
|
|
|
void TextureBrowser_SetHideUnused( TextureBrowser& textureBrowser, bool hideUnused );
|
|
|
|
QWidget* g_page_textures;
|
|
|
|
void TextureBrowser_toggleShow(){
|
|
GroupDialog_showPage( g_page_textures );
|
|
}
|
|
|
|
|
|
void TextureBrowser_updateTitle(){
|
|
GroupDialog_updatePageTitle( g_page_textures );
|
|
}
|
|
|
|
|
|
|
|
class TextureCategoryLoadShader
|
|
{
|
|
const char* m_directory;
|
|
std::size_t& m_count;
|
|
public:
|
|
typedef const char* first_argument_type;
|
|
|
|
TextureCategoryLoadShader( const char* directory, std::size_t& count )
|
|
: m_directory( directory ), m_count( count ){
|
|
m_count = 0;
|
|
}
|
|
void operator()( const char* name ) const {
|
|
if ( shader_equal_prefix( name, "textures/" )
|
|
&& shader_equal_prefix( name + string_length( "textures/" ), m_directory ) ) {
|
|
++m_count;
|
|
// request the shader, this will load the texture if needed
|
|
// this Shader_ForName call is a kind of hack
|
|
IShader *pFoo = QERApp_Shader_ForName( name );
|
|
pFoo->DecRef();
|
|
}
|
|
}
|
|
};
|
|
|
|
void TextureDirectory_loadTexture( const char* directory, const char* texture ){
|
|
const auto name = StringOutputStream( 256 )( directory, PathExtensionless( texture ) );
|
|
|
|
if ( texture_name_ignore( name.c_str() ) ) {
|
|
return;
|
|
}
|
|
|
|
if ( !shader_valid( name.c_str() ) ) {
|
|
globalWarningStream() << "Skipping invalid texture name: [" << name.c_str() << "]\n";
|
|
return;
|
|
}
|
|
|
|
// if a texture is already in use to represent a shader, ignore it
|
|
IShader* shader = QERApp_Shader_ForName( name.c_str() );
|
|
shader->DecRef();
|
|
}
|
|
typedef ConstPointerCaller1<char, const char*, TextureDirectory_loadTexture> TextureDirectoryLoadTextureCaller;
|
|
|
|
class LoadTexturesByTypeVisitor : public ImageModules::Visitor
|
|
{
|
|
const char* m_dirstring;
|
|
public:
|
|
LoadTexturesByTypeVisitor( const char* dirstring )
|
|
: m_dirstring( dirstring ){
|
|
}
|
|
void visit( const char* minor, const _QERPlugImageTable& table ) const {
|
|
GlobalFileSystem().forEachFile( m_dirstring, minor, TextureDirectoryLoadTextureCaller( m_dirstring ) );
|
|
}
|
|
};
|
|
|
|
void TextureBrowser_ShowDirectory( const char* directory ){
|
|
g_TexBro.m_searchedTags = false;
|
|
if ( TextureBrowser::wads ) {
|
|
Archive* archive = GlobalFileSystem().getArchive( directory );
|
|
//ASSERT_NOTNULL( archive );
|
|
if( archive ){
|
|
globalOutputStream() << "Loading " << makeQuoted( directory ) << " wad file.\n";
|
|
LoadShaderVisitor visitor;
|
|
archive->forEachFile( Archive::VisitorFunc( visitor, Archive::eFiles, 0 ), "textures/" );
|
|
}
|
|
else{
|
|
globalErrorStream() << "Attempted to load " << makeQuoted( directory ) << " wad file.\n";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
g_TextureBrowser_currentDirectory = directory;
|
|
|
|
std::size_t shaders_count;
|
|
GlobalShaderSystem().foreachShaderName( makeCallback1( TextureCategoryLoadShader( directory, shaders_count ) ) );
|
|
globalOutputStream() << "Showing " << shaders_count << " shaders.\n";
|
|
|
|
if ( g_pGameDescription->mGameType != "doom3" ) {
|
|
// load remaining texture files
|
|
|
|
StringOutputStream dirstring( 64 );
|
|
dirstring << "textures/" << directory;
|
|
|
|
Radiant_getImageModules().foreachModule( LoadTexturesByTypeVisitor( dirstring.c_str() ) );
|
|
}
|
|
}
|
|
|
|
TextureBrowser_SetHideUnused( g_TexBro, false );
|
|
g_TexBro.setOriginY( 0 );
|
|
TextureBrowser_updateTitle();
|
|
}
|
|
|
|
|
|
void TextureBrowser_SetHideUnused( TextureBrowser& textureBrowser, bool hideUnused ){
|
|
textureBrowser.m_hideUnused = hideUnused;
|
|
|
|
textureBrowser.m_hideunused_item.update();
|
|
|
|
textureBrowser.heightChanged();
|
|
textureBrowser.m_originInvalid = true;
|
|
}
|
|
|
|
void TextureBrowser_ShowStartupShaders(){
|
|
if ( g_TexBro.m_startupShaders == STARTUPSHADERS_COMMON ) {
|
|
TextureBrowser_ShowDirectory( TextureBrowser_getCommonShadersDir() );
|
|
}
|
|
}
|
|
|
|
|
|
//++timo NOTE: this is a mix of Shader module stuff and texture explorer
|
|
// it might need to be split in parts or moved out .. dunno
|
|
// scroll origin so the specified texture is completely on screen
|
|
// if current texture is not displayed, nothing is changed
|
|
void TextureBrowser_Focus( TextureBrowser& textureBrowser, const char* name ){
|
|
TextureLayout layout;
|
|
// scroll origin so the texture is completely on screen
|
|
|
|
for ( QERApp_ActiveShaders_IteratorBegin(); !QERApp_ActiveShaders_IteratorAtEnd(); QERApp_ActiveShaders_IteratorIncrement() )
|
|
{
|
|
IShader* shader = QERApp_ActiveShaders_IteratorCurrent();
|
|
|
|
if ( !Texture_IsShown( shader, textureBrowser ) ) {
|
|
continue;
|
|
}
|
|
|
|
const auto [ x, y ] = layout.nextPos( textureBrowser, shader->getTexture() );
|
|
qtexture_t* q = shader->getTexture();
|
|
if ( !q ) {
|
|
break;
|
|
}
|
|
|
|
// we have found when texdef->name and the shader name match
|
|
// NOTE: as everywhere else for our comparisons, we are not case sensitive
|
|
if ( shader_equal( name, shader->getName() ) ) {
|
|
//int textureHeight = (int)( q->height * ( (float)textureBrowser.m_textureScale / 100 ) ) + 2 * TextureBrowser_fontHeight();
|
|
auto [textureWidth, textureHeight] = textureBrowser.getTextureWH( q );
|
|
textureHeight += 2 * TextureBrowser_fontHeight();
|
|
|
|
|
|
int originy = textureBrowser.getOriginY();
|
|
if ( y > originy ) {
|
|
originy = y + 4;
|
|
}
|
|
|
|
if ( y - textureHeight < originy - textureBrowser.m_height ) {
|
|
originy = ( y - textureHeight ) + textureBrowser.m_height;
|
|
}
|
|
|
|
textureBrowser.setOriginY( originy );
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
IShader* Texture_At( TextureBrowser& textureBrowser, int mx, int my ){
|
|
my = textureBrowser.getOriginY() - my - 1;
|
|
|
|
TextureLayout layout;
|
|
for ( QERApp_ActiveShaders_IteratorBegin(); !QERApp_ActiveShaders_IteratorAtEnd(); QERApp_ActiveShaders_IteratorIncrement() )
|
|
{
|
|
IShader* shader = QERApp_ActiveShaders_IteratorCurrent();
|
|
|
|
if ( !Texture_IsShown( shader, textureBrowser ) ) {
|
|
continue;
|
|
}
|
|
|
|
const auto [ x, y ] = layout.nextPos( textureBrowser, shader->getTexture() );
|
|
qtexture_t *q = shader->getTexture();
|
|
if ( !q ) {
|
|
break;
|
|
}
|
|
|
|
const auto [nWidth, nHeight] = textureBrowser.getTextureWH( q );
|
|
if ( mx > x && mx - x < nWidth
|
|
&& my < y && y - my < nHeight + TextureBrowser_fontHeight() ) {
|
|
return shader;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
SelectTexture
|
|
|
|
By mouse click
|
|
==============
|
|
*/
|
|
void SelectTexture( TextureBrowser& textureBrowser, int mx, int my, bool texturizeSelection ){
|
|
IShader* shader = Texture_At( textureBrowser, mx, my );
|
|
if ( shader != 0 ) {
|
|
TextureBrowser_SetSelectedShader( textureBrowser, shader->getName() );
|
|
TextureBrowser_textureSelected( shader->getName() );
|
|
|
|
if ( !FindTextureDialog_isOpen() && !texturizeSelection ) {
|
|
Select_SetShader_Undo( shader->getName() );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
============================================================================
|
|
|
|
MOUSE ACTIONS
|
|
|
|
============================================================================
|
|
*/
|
|
|
|
void TextureBrowser_Tracking_MouseUp( TextureBrowser& textureBrowser ){
|
|
if( textureBrowser.m_move_started ){
|
|
textureBrowser.m_move_started = false;
|
|
textureBrowser.m_freezePointer.unfreeze_pointer( false );
|
|
}
|
|
}
|
|
|
|
void TextureBrowser_Tracking_MouseDown( TextureBrowser& textureBrowser ){
|
|
TextureBrowser_Tracking_MouseUp( textureBrowser );
|
|
textureBrowser.m_move_started = true;
|
|
textureBrowser.m_freezePointer.freeze_pointer( textureBrowser.m_gl_widget,
|
|
[&textureBrowser]( int x, int y, const QMouseEvent *event ){
|
|
if ( y != 0 ) {
|
|
const int scale = event->modifiers().testFlag( Qt::KeyboardModifier::ShiftModifier )? 4 : 1;
|
|
const int originy = textureBrowser.getOriginY() + y * scale;
|
|
textureBrowser.setOriginY( originy );
|
|
}
|
|
},
|
|
[&textureBrowser](){
|
|
TextureBrowser_Tracking_MouseUp( textureBrowser );
|
|
} );
|
|
}
|
|
|
|
void TextureBrowser_ViewShader( TextureBrowser& textureBrowser, Qt::KeyboardModifiers modifiers, int pointx, int pointy ){
|
|
IShader* shader = Texture_At( textureBrowser, pointx, pointy );
|
|
if ( shader != 0 ) {
|
|
if ( shader->IsDefault() ) {
|
|
globalWarningStream() << shader->getName() << " is not a shader, it's a texture.\n";
|
|
}
|
|
else{
|
|
DoShaderView( shader->getShaderFileName(), shader->getName(), modifiers.testFlag( Qt::KeyboardModifier::ControlModifier ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
============================================================================
|
|
|
|
DRAWING
|
|
|
|
============================================================================
|
|
*/
|
|
|
|
/*
|
|
============
|
|
TTimo: relying on the shaders list to display the textures
|
|
we must query all qtexture_t* to manage and display through the IShaders interface
|
|
this allows a plugin to completely override the texture system
|
|
============
|
|
*/
|
|
void TextureBrowser::draw(){
|
|
evaluateHeight();
|
|
const int fontHeight = TextureBrowser_fontHeight();
|
|
const int fontDescent = GlobalOpenGL().m_font->getPixelDescent();
|
|
const int originy = getOriginY();
|
|
|
|
gl().glClearColor( m_color_textureback[0],
|
|
m_color_textureback[1],
|
|
m_color_textureback[2],
|
|
0 );
|
|
gl().glViewport( 0, 0, m_width, m_height );
|
|
gl().glMatrixMode( GL_PROJECTION );
|
|
gl().glLoadIdentity();
|
|
|
|
gl().glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
|
|
gl().glDisable( GL_DEPTH_TEST );
|
|
gl().glDisable( GL_MULTISAMPLE );
|
|
if ( g_TextureBrowser_enableAlpha ) {
|
|
gl().glEnable( GL_BLEND );
|
|
gl().glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
|
|
}
|
|
else {
|
|
gl().glDisable( GL_BLEND );
|
|
}
|
|
|
|
gl().glOrtho( 0, m_width, originy - m_height, originy, -100, 100 );
|
|
gl().glEnable( GL_TEXTURE_2D );
|
|
|
|
gl().glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
|
|
|
|
TextureLayout layout;
|
|
for ( QERApp_ActiveShaders_IteratorBegin(); !QERApp_ActiveShaders_IteratorAtEnd(); QERApp_ActiveShaders_IteratorIncrement() )
|
|
{
|
|
IShader* shader = QERApp_ActiveShaders_IteratorCurrent();
|
|
|
|
if ( !Texture_IsShown( shader, *this ) ) {
|
|
continue;
|
|
}
|
|
|
|
const auto [ x, y ] = layout.nextPos( *this, shader->getTexture() );
|
|
qtexture_t *q = shader->getTexture();
|
|
if ( !q ) {
|
|
break;
|
|
}
|
|
|
|
const auto [nWidth, nHeight] = getTextureWH( q );
|
|
|
|
// Is this texture visible?
|
|
if ( ( y - nHeight - fontHeight < originy )
|
|
&& ( y > originy - m_height ) ) {
|
|
gl().glLineWidth( 1 );
|
|
gl().glDisable( GL_TEXTURE_2D );
|
|
const float xf = x;
|
|
const float yf = y - fontHeight;
|
|
float xfMax = xf + 1.5 + nWidth;
|
|
float xfMin = xf - 1.5;
|
|
float yfMax = yf + 1.5;
|
|
float yfMin = yf - nHeight - 1.5;
|
|
#define TEXBRO_RENDER_BORDER \
|
|
gl().glBegin( GL_LINE_LOOP ); \
|
|
gl().glVertex2f( xfMin, yfMax ); \
|
|
gl().glVertex2f( xfMin, yfMin ); \
|
|
gl().glVertex2f( xfMax, yfMin ); \
|
|
gl().glVertex2f( xfMax, yfMax ); \
|
|
gl().glEnd();
|
|
|
|
//selected texture
|
|
if ( shader_equal( m_shader.c_str(), shader->getName() ) ) {
|
|
gl().glLineWidth( 2 );
|
|
gl().glColor3f( 1, 0, 0 );
|
|
xfMax += .5;
|
|
xfMin -= .5;
|
|
yfMax += .5;
|
|
yfMin -= .5;
|
|
TEXBRO_RENDER_BORDER
|
|
}
|
|
// highlight in-use textures
|
|
else if ( !m_hideUnused && shader->IsInUse() ) {
|
|
gl().glColor3f( 0.5, 1, 0.5 );
|
|
TEXBRO_RENDER_BORDER
|
|
}
|
|
// shader white border:
|
|
else if ( !shader->IsDefault() ) {
|
|
gl().glColor3f( 1, 1, 1 );
|
|
TEXBRO_RENDER_BORDER
|
|
}
|
|
|
|
// shader stipple:
|
|
if ( !shader->IsDefault() ) {
|
|
gl().glEnable( GL_LINE_STIPPLE );
|
|
gl().glLineStipple( 1, 0xF000 );
|
|
gl().glColor3f( 0, 0, 0 );
|
|
TEXBRO_RENDER_BORDER
|
|
gl().glDisable( GL_LINE_STIPPLE );
|
|
}
|
|
|
|
// draw checkerboard for transparent textures
|
|
if ( g_TextureBrowser_enableAlpha )
|
|
{
|
|
gl().glBegin( GL_QUADS );
|
|
for ( int i = 0; i < nHeight; i += 8 )
|
|
for ( int j = 0; j < nWidth; j += 8 )
|
|
{
|
|
const unsigned char color = ( i + j ) / 8 % 2 ? 0x66 : 0x99;
|
|
gl().glColor3ub( color, color, color );
|
|
const int left = j;
|
|
const int right = std::min( j + 8, nWidth );
|
|
const int top = i;
|
|
const int bottom = std::min( i + 8, nHeight );
|
|
gl().glVertex2i( x + right, y - nHeight - fontHeight + top );
|
|
gl().glVertex2i( x + left, y - nHeight - fontHeight + top );
|
|
gl().glVertex2i( x + left, y - nHeight - fontHeight + bottom );
|
|
gl().glVertex2i( x + right, y - nHeight - fontHeight + bottom );
|
|
}
|
|
gl().glEnd();
|
|
}
|
|
|
|
// Draw the texture
|
|
gl().glEnable( GL_TEXTURE_2D );
|
|
gl().glBindTexture( GL_TEXTURE_2D, q->texture_number );
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
gl().glColor3f( 1, 1, 1 );
|
|
gl().glBegin( GL_QUADS );
|
|
gl().glTexCoord2i( 0, 0 );
|
|
gl().glVertex2i( x, y - fontHeight );
|
|
gl().glTexCoord2i( 1, 0 );
|
|
gl().glVertex2i( x + nWidth, y - fontHeight );
|
|
gl().glTexCoord2i( 1, 1 );
|
|
gl().glVertex2i( x + nWidth, y - fontHeight - nHeight );
|
|
gl().glTexCoord2i( 0, 1 );
|
|
gl().glVertex2i( x, y - fontHeight - nHeight );
|
|
gl().glEnd();
|
|
|
|
// draw the texture name
|
|
// gl().glDisable( GL_TEXTURE_2D );
|
|
// gl().glColor3f( 1, 1, 1 ); //already set
|
|
|
|
gl().glRasterPos2i( x, y - fontHeight - fontDescent + 3 );//+5
|
|
|
|
// don't draw the directory name
|
|
const char* name = shader->getName();
|
|
name += strlen( name );
|
|
while ( name != shader->getName() && *( name - 1 ) != '/' && *( name - 1 ) != '\\' )
|
|
name--;
|
|
|
|
GlobalOpenGL().drawString( name );
|
|
}
|
|
}
|
|
|
|
// reset the current texture
|
|
gl().glBindTexture( GL_TEXTURE_2D, 0 );
|
|
gl().glDisable( GL_BLEND );
|
|
//qglFinish();
|
|
}
|
|
|
|
|
|
void TextureBrowser_setScale( TextureBrowser& textureBrowser, std::size_t scale ){
|
|
textureBrowser.m_textureScale = scale;
|
|
|
|
textureBrowser.m_heightChanged = true;
|
|
textureBrowser.m_originInvalid = true;
|
|
g_activeShadersChangedCallbacks();
|
|
|
|
textureBrowser.queueDraw();
|
|
}
|
|
|
|
void TextureBrowser_setUniformSize( TextureBrowser& textureBrowser, std::size_t scale ){
|
|
textureBrowser.m_uniformTextureSize = scale;
|
|
|
|
textureBrowser.m_heightChanged = true;
|
|
textureBrowser.m_originInvalid = true;
|
|
g_activeShadersChangedCallbacks();
|
|
|
|
textureBrowser.queueDraw();
|
|
}
|
|
|
|
void TextureBrowser_setUniformMinSize( TextureBrowser& textureBrowser, std::size_t scale ){
|
|
textureBrowser.m_uniformTextureMinSize = scale;
|
|
|
|
textureBrowser.m_heightChanged = true;
|
|
textureBrowser.m_originInvalid = true;
|
|
g_activeShadersChangedCallbacks();
|
|
|
|
textureBrowser.queueDraw();
|
|
}
|
|
|
|
void TextureBrowser_ToggleHideUnused(){
|
|
TextureBrowser_SetHideUnused( g_TexBro, !g_TexBro.m_hideUnused );
|
|
}
|
|
|
|
void TextureGroups_constructTreeModel( TextureGroups groups, QStandardItemModel* model ){
|
|
auto root = model->invisibleRootItem();
|
|
|
|
TextureGroups::const_iterator i = groups.begin();
|
|
while ( i != groups.end() )
|
|
{
|
|
const char* dirName = ( *i ).c_str();
|
|
const char* firstUnderscore = strchr( dirName, '_' );
|
|
StringRange dirRoot( dirName, ( firstUnderscore == 0 ) ? dirName : firstUnderscore + 1 );
|
|
|
|
TextureGroups::const_iterator next = std::next( i );
|
|
if ( firstUnderscore != 0
|
|
&& next != groups.end()
|
|
&& string_equal_start( ( *next ).c_str(), dirRoot ) ) {
|
|
auto subroot = new QStandardItem( CopiedString( StringRange( dirName, firstUnderscore ) ).c_str() );
|
|
root->appendRow( subroot );
|
|
|
|
// keep going...
|
|
while ( i != groups.end() && string_equal_start( ( *i ).c_str(), dirRoot ) )
|
|
{
|
|
auto item = new QStandardItem( ( *i ).c_str() );
|
|
item->setData( ( *i ).c_str(), Qt::ItemDataRole::ToolTipRole );
|
|
subroot->appendRow( item );
|
|
++i;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
auto item = new QStandardItem( dirName );
|
|
item->setData( dirName, Qt::ItemDataRole::ToolTipRole );
|
|
root->appendRow( item );
|
|
++i;
|
|
}
|
|
}
|
|
}
|
|
|
|
void TextureGroups_constructTreeModel_childless( TextureGroups groups, QStandardItemModel* model ){
|
|
auto root = model->invisibleRootItem();
|
|
|
|
TextureGroups::const_iterator i = groups.begin();
|
|
while ( i != groups.end() )
|
|
{
|
|
const char* dirName = ( *i ).c_str();
|
|
const char* pakName = strrchr( dirName, '/' );
|
|
const char* pakNameEnd = strrchr( dirName, '.' );
|
|
ASSERT_MESSAGE( pakName != 0 && pakNameEnd != 0 && pakNameEnd > pakName, "interesting wad path" );
|
|
{
|
|
auto item = new QStandardItem( CopiedString( StringRange( pakName + 1, pakNameEnd ) ).c_str() );
|
|
item->setData( dirName, Qt::ItemDataRole::ToolTipRole );
|
|
root->appendRow( item );
|
|
++i;
|
|
}
|
|
}
|
|
}
|
|
|
|
void TextureGroups_constructTreeView( TextureGroups& groups ){
|
|
if ( TextureBrowser::wads ) {
|
|
GlobalFileSystem().forEachArchive( TextureGroupsAddWadCaller( groups ) );
|
|
}
|
|
else
|
|
{
|
|
// scan texture dirs and pak files only if not restricting to shaderlist
|
|
if ( g_pGameDescription->mGameType != "doom3" && !g_TextureBrowser_shaderlistOnly ) {
|
|
GlobalFileSystem().forEachDirectory( "textures/", TextureGroupsAddDirectoryCaller( groups ) );
|
|
}
|
|
|
|
GlobalShaderSystem().foreachShaderName( TextureGroupsAddShaderCaller( groups ) );
|
|
}
|
|
}
|
|
|
|
void TextureBrowser_constructTreeStore(){
|
|
TextureGroups groups;
|
|
TextureGroups_constructTreeView( groups );
|
|
|
|
auto model = new QStandardItemModel( g_TexBro.m_treeView ); //. ? delete old or clear() & reuse
|
|
|
|
// store display name in column #0 and load path in data( Qt::ItemDataRole::ToolTipRole )
|
|
// tooltips are only wanted for TextureBrowser::wads, but well
|
|
if( !TextureBrowser::wads )
|
|
TextureGroups_constructTreeModel( groups, model );
|
|
else
|
|
TextureGroups_constructTreeModel_childless( groups, model );
|
|
|
|
g_TexBro.m_treeView->setModel( model );
|
|
}
|
|
|
|
void TreeView_onRowActivated( const QModelIndex& index ){
|
|
auto dirName = index.data( Qt::ItemDataRole::ToolTipRole ).toByteArray();
|
|
if( !dirName.isEmpty() ){ // empty = directory group root
|
|
g_TexBro.m_searchedTags = false;
|
|
|
|
if ( !TextureBrowser::wads ) {
|
|
dirName.append( '/' );
|
|
}
|
|
|
|
ScopeDisableScreenUpdates disableScreenUpdates( dirName, "Loading Textures" );
|
|
TextureBrowser_ShowDirectory( dirName.data() );
|
|
g_TexBro.queueDraw();
|
|
//deactivate, so SPACE and RETURN wont be broken for 2d
|
|
g_TexBro.m_treeView->clearFocus();
|
|
}
|
|
}
|
|
|
|
class TexBro_QTreeView : public QTreeView
|
|
{
|
|
protected:
|
|
bool event( QEvent *event ) override {
|
|
if( event->type() == QEvent::ShortcutOverride ){
|
|
event->accept();
|
|
return true;
|
|
}
|
|
return QTreeView::event( event );
|
|
}
|
|
};
|
|
|
|
void TextureBrowser_createTreeViewTree(){
|
|
g_TexBro.m_treeView = new TexBro_QTreeView;
|
|
g_TexBro.m_treeView->setHeaderHidden( true );
|
|
g_TexBro.m_treeView->setEditTriggers( QAbstractItemView::EditTrigger::NoEditTriggers );
|
|
g_TexBro.m_treeView->setUniformRowHeights( true ); // optimization
|
|
g_TexBro.m_treeView->setFocusPolicy( Qt::FocusPolicy::ClickFocus );
|
|
g_TexBro.m_treeView->setExpandsOnDoubleClick( false );
|
|
g_TexBro.m_treeView->header()->setStretchLastSection( false ); // non greedy column sizing; + QHeaderView::ResizeMode::ResizeToContents = no text elision 🤷♀️
|
|
g_TexBro.m_treeView->header()->setSectionResizeMode( QHeaderView::ResizeMode::ResizeToContents );
|
|
|
|
QObject::connect( g_TexBro.m_treeView, &QAbstractItemView::activated, TreeView_onRowActivated );
|
|
|
|
TextureBrowser_constructTreeStore();
|
|
}
|
|
|
|
static QMenu* TextureBrowser_constructViewMenu(){
|
|
QMenu *menu = new QMenu( "View" );
|
|
|
|
menu->setTearOffEnabled( g_Layout_enableDetachableMenus.m_value );
|
|
|
|
create_check_menu_item_with_mnemonic( menu, "Hide Unused", "ShowInUse" );
|
|
create_menu_item_with_mnemonic( menu, "Show All", "ShowAllTextures" );
|
|
menu->addSeparator();
|
|
|
|
|
|
// we always want to show shaders but don't want a "Show Shaders" menu for doom3 and .wad file games
|
|
if ( g_pGameDescription->mGameType == "doom3" || TextureBrowser::wads ) {
|
|
g_TexBro.m_showShaders = true;
|
|
}
|
|
else
|
|
{
|
|
create_check_menu_item_with_mnemonic( menu, "Show Shaders", "ToggleShowShaders" );
|
|
create_check_menu_item_with_mnemonic( menu, "Show Textures", "ToggleShowTextures" );
|
|
menu->addSeparator();
|
|
}
|
|
|
|
if ( g_pGameDescription->mGameType != "doom3" && !TextureBrowser::wads ) {
|
|
create_check_menu_item_with_mnemonic( menu, "ShaderList Only", "ToggleShowShaderlistOnly" );
|
|
}
|
|
if ( !TextureBrowser::wads ) {
|
|
create_check_menu_item_with_mnemonic( menu, "Hide Image Missing", "FilterNotex" );
|
|
menu->addSeparator();
|
|
}
|
|
|
|
create_check_menu_item_with_mnemonic( menu, "Fixed Size", "FixedSize" );
|
|
create_check_menu_item_with_mnemonic( menu, "Transparency", "EnableAlpha" );
|
|
|
|
menu->addSeparator();
|
|
create_check_menu_item_with_mnemonic( menu, "Tags GUI", "TagsToggleGui" );
|
|
|
|
if ( !TextureBrowser::wads ) {
|
|
menu->addSeparator();
|
|
g_TexBro.m_shader_info_item = create_menu_item_with_mnemonic( menu, "Shader Info", "ShaderInfo" );
|
|
g_TexBro.m_shader_info_item->setDisabled( true );
|
|
}
|
|
|
|
return menu;
|
|
}
|
|
|
|
void Popup_View_Menu( QMenu *menu ){
|
|
menu->popup( QCursor::pos() );
|
|
}
|
|
|
|
|
|
#include "xml/xmltextags.h"
|
|
XmlTagBuilder TagBuilder;
|
|
|
|
|
|
static QMenu* TextureBrowser_constructTagsMenu(){
|
|
auto menu = new QMenu( "Tags" );
|
|
|
|
menu->setTearOffEnabled( g_Layout_enableDetachableMenus.m_value );
|
|
|
|
create_menu_item_with_mnemonic( menu, "Add tag", "TagAdd" );
|
|
create_menu_item_with_mnemonic( menu, "Rename tag", "TagRename" );
|
|
create_menu_item_with_mnemonic( menu, "Delete tag", "TagDelete" );
|
|
menu->addSeparator();
|
|
create_menu_item_with_mnemonic( menu, "Copy tags from selected", "TagCopy" );
|
|
create_menu_item_with_mnemonic( menu, "Paste tags to selected", "TagPaste" );
|
|
menu->addSeparator();
|
|
create_menu_item_with_mnemonic( menu, "Search tag", "TagSearch" );
|
|
create_menu_item_with_mnemonic( menu, "Search Untagged", "TagSearchUntagged" );
|
|
|
|
return menu;
|
|
}
|
|
|
|
inline void TextureBrowser_tagsEnableGui( bool enable ){
|
|
if( enable )
|
|
g_TexBro.m_tabs->addTab( g_TexBro.m_tagsListWidget, "Tags" );
|
|
else
|
|
g_TexBro.m_tabs->removeTab( 1 );
|
|
}
|
|
|
|
class Tags_QListWidget : public QListWidget
|
|
{
|
|
using QListWidget::QListWidget;
|
|
protected:
|
|
bool event( QEvent *event ) override {
|
|
if( event->type() == QEvent::ShortcutOverride ){
|
|
event->accept();
|
|
return true;
|
|
}
|
|
return QListWidget::event( event );
|
|
}
|
|
void contextMenuEvent( QContextMenuEvent *event ) override {
|
|
g_TexBro.m_tagsMenu->popup( event->globalPos() );
|
|
}
|
|
};
|
|
|
|
void TextureBrowser_tagsSetCheckboxesForShader( const char *shader ){
|
|
std::vector<CopiedString> assigned_tags;
|
|
TagBuilder.GetShaderTags( shader, assigned_tags );
|
|
|
|
const auto contains = [&assigned_tags]( const char *tag )->bool {
|
|
for( auto it = assigned_tags.cbegin(); it != assigned_tags.cend(); ++it )
|
|
if( string_equal( tag, it->c_str() ) ){
|
|
assigned_tags.erase( it ); // assuming / hoping that tag names are unique, thus can run faster
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
for( int i = 0; i < g_TexBro.m_tagsListWidget->count(); ++i )
|
|
{
|
|
auto item = g_TexBro.m_tagsListWidget->item( i );
|
|
item->setCheckState( contains( item->data( Qt::ItemDataRole::DisplayRole ).toByteArray() )? Qt::CheckState::Checked : Qt::CheckState::Unchecked );
|
|
}
|
|
}
|
|
|
|
void TextureBrowser_tagAssignmentChanged( const QModelIndex &index ){
|
|
const bool assigned = Qt::CheckState::Checked == static_cast<Qt::CheckState>( index.data( Qt::ItemDataRole::CheckStateRole ).toInt() );
|
|
const auto tag = index.data( Qt::ItemDataRole::DisplayRole ).toByteArray();
|
|
|
|
if( assigned ){
|
|
if ( !TagBuilder.CheckShaderTag( g_TexBro.m_shader.c_str() ) ) {
|
|
// create a custom shader/texture entry
|
|
IShader* ishader = QERApp_Shader_ForName( g_TexBro.m_shader.c_str() );
|
|
|
|
if ( ishader->IsDefault() ) {
|
|
// it's a texture
|
|
TagBuilder.AddShaderNode( g_TexBro.m_shader.c_str(), TextureType::CUSTOM, NodeShaderType::TEXTURE );
|
|
}
|
|
else {
|
|
// it's a shader
|
|
TagBuilder.AddShaderNode( g_TexBro.m_shader.c_str(), TextureType::CUSTOM, NodeShaderType::SHADER );
|
|
}
|
|
ishader->DecRef();
|
|
}
|
|
TagBuilder.AddShaderTag( g_TexBro.m_shader.c_str(), tag, NodeTagType::TAG );
|
|
}
|
|
else{
|
|
TagBuilder.DeleteShaderTag( g_TexBro.m_shader.c_str(), tag );
|
|
}
|
|
}
|
|
|
|
class Tags_QItemDelegate : public QItemDelegate
|
|
{
|
|
using QItemDelegate::QItemDelegate;
|
|
protected:
|
|
/* track user edit of tag name */
|
|
void setModelData( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const override {
|
|
const QByteArray propname = editor->metaObject()->userProperty().name();
|
|
const auto newName = propname.isEmpty()? QByteArray() : editor->property( propname ).toByteArray();
|
|
if ( newName.isEmpty() ){
|
|
qt_MessageBox( g_TexBro.m_parent, "New tag name is empty :0", ":o", EMessageBoxType::Error );
|
|
}
|
|
else{
|
|
const auto oldName = index.data( Qt::ItemDataRole::DisplayRole ).toByteArray();
|
|
|
|
if( oldName != newName // is changed
|
|
&& g_TexBro.m_all_tags.find( newName.constData() ) != g_TexBro.m_all_tags.cend() ){ // & found in existing names
|
|
qt_MessageBox( g_TexBro.m_parent, "New tag name is already taken :0", newName.constData(), EMessageBoxType::Error );
|
|
}
|
|
else{
|
|
TagBuilder.RenameShaderTag( oldName, newName.constData() );
|
|
|
|
g_TexBro.m_all_tags.erase( oldName.constData() );
|
|
g_TexBro.m_all_tags.insert( newName.constData() );
|
|
|
|
QItemDelegate::setModelData( editor, model, index ); // normal processing
|
|
}
|
|
}
|
|
}
|
|
bool editorEvent( QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index ) override {
|
|
/* let's do some infamous juggling to track user unduced CheckState change */
|
|
if( event->type() == QEvent::MouseButtonRelease || event->type() == QEvent::KeyPress ){
|
|
if( const QVariant value = index.data( Qt::ItemDataRole::CheckStateRole ); value.isValid() ){
|
|
const bool ret = QItemDelegate::editorEvent( event, model, option, index );
|
|
if( ret && value != index.data( Qt::ItemDataRole::CheckStateRole ) ){
|
|
TextureBrowser_tagAssignmentChanged( index );
|
|
}
|
|
return ret;
|
|
}
|
|
}
|
|
return QItemDelegate::editorEvent( event, model, option, index );
|
|
}
|
|
};
|
|
|
|
void TextureBrowser_searchTags(){
|
|
const auto selected = g_TexBro.m_tagsListWidget->selectedItems();
|
|
|
|
if ( !selected.empty() ) {
|
|
auto buffer = StringOutputStream( 256 )( "/root/*/*[tag='" );
|
|
auto tags_searched = StringOutputStream( 256 )( "[TAGS] " );
|
|
|
|
for ( auto it = selected.begin(); it != selected.end(); ++it )
|
|
{
|
|
const auto tag = ( *it )->text().toLatin1();
|
|
buffer << tag.constData();
|
|
tags_searched << tag.constData();
|
|
if ( it + 1 != selected.end() ) {
|
|
buffer << "' and tag='";
|
|
tags_searched << ", ";
|
|
}
|
|
}
|
|
|
|
buffer << "']";
|
|
|
|
g_TexBro.m_found_shaders.clear(); // delete old list
|
|
TagBuilder.TagSearch( buffer, g_TexBro.m_found_shaders );
|
|
|
|
if ( !g_TexBro.m_found_shaders.empty() ) { // found something
|
|
globalOutputStream() << "Found " << g_TexBro.m_found_shaders.size() << " textures and shaders with " << tags_searched << "\n";
|
|
ScopeDisableScreenUpdates disableScreenUpdates( "Searching...", "Loading Textures" );
|
|
|
|
for ( const CopiedString& shader : g_TexBro.m_found_shaders )
|
|
{
|
|
std::string path = shader.c_str();
|
|
const size_t pos = path.find_last_of( "/" );
|
|
const std::string name = path.substr( pos + 1 );
|
|
path = path.substr( 0, pos + 1 );
|
|
TextureDirectory_loadTexture( path.c_str(), name.c_str() );
|
|
}
|
|
}
|
|
TextureBrowser_SetHideUnused( g_TexBro, false );
|
|
g_TexBro.m_searchedTags = true;
|
|
g_TextureBrowser_currentDirectory = tags_searched;
|
|
|
|
g_TexBro.heightChanged();
|
|
g_TexBro.m_originInvalid = true;
|
|
TextureBrowser_updateTitle();
|
|
|
|
//deactivate, so SPACE and RETURN wont be broken for 2d
|
|
g_TexBro.m_tagsListWidget->clearFocus();
|
|
}
|
|
}
|
|
|
|
void TextureBrowser_showUntagged(){
|
|
EMessageBoxReturn result = qt_MessageBox( g_TexBro.m_parent,
|
|
"WARNING! This function might need <b>a lot</b> of memory and time.<br>"
|
|
"It shows all textures & shaders indexed by ShaderPlug plugin, but having no tag.<br>"
|
|
"Are you sure you want to use it?",
|
|
"Show Untagged", EMessageBoxType::Warning, eIDYES | eIDNO );
|
|
|
|
if ( result == eIDYES ) {
|
|
g_TexBro.m_found_shaders.clear();
|
|
TagBuilder.GetUntagged( g_TexBro.m_found_shaders );
|
|
|
|
ScopeDisableScreenUpdates disableScreenUpdates( "Searching untagged textures...", "Loading Textures" );
|
|
|
|
for ( const CopiedString& shader : g_TexBro.m_found_shaders )
|
|
{
|
|
std::string path = shader.c_str();
|
|
size_t pos = path.find_last_of( "/", path.size() );
|
|
std::string name = path.substr( pos + 1, path.size() );
|
|
path = path.substr( 0, pos + 1 );
|
|
TextureDirectory_loadTexture( path.c_str(), name.c_str() );
|
|
}
|
|
|
|
TextureBrowser_SetHideUnused( g_TexBro, false );
|
|
g_TexBro.m_searchedTags = true;
|
|
g_TextureBrowser_currentDirectory = "Untagged";
|
|
g_TexBro.heightChanged();
|
|
g_TexBro.m_originInvalid = true;
|
|
TextureBrowser_updateTitle();
|
|
}
|
|
}
|
|
|
|
void TextureBrowser_checkTagFile(){
|
|
const auto rc_filename = StringOutputStream( 256 )( LocalRcPath_get(), SHADERTAG_FILE );
|
|
const auto default_filename = StringOutputStream( 256 )( g_pGameDescription->mGameToolsPath, SHADERTAG_FILE );
|
|
|
|
if ( file_exists( rc_filename ) && TagBuilder.OpenXmlDoc( rc_filename ) )
|
|
{
|
|
globalOutputStream() << "Loaded tag file " << rc_filename << ".\n";
|
|
}
|
|
else if ( file_exists( default_filename ) && TagBuilder.OpenXmlDoc( default_filename, rc_filename ) ) // load default tagfile
|
|
{
|
|
globalOutputStream() << "Loaded default tag file " << default_filename << ".\n";
|
|
}
|
|
else
|
|
{
|
|
// globalWarningStream() << "Unable to find default tag file " << default_filename << ". No tag support. Plugins -> ShaderPlug -> Create tag file: to start using texture tags\n";
|
|
const bool ok = TagBuilder.CreateXmlDocument( rc_filename );
|
|
ASSERT_MESSAGE( ok, "empty tag document was not created" );
|
|
globalOutputStream() << "Created empty tag file " << rc_filename << ". Plugins -> ShaderPlug -> Create tag file: to index all textures and shaders, if needed.\n";
|
|
}
|
|
}
|
|
|
|
void TextureBrowser_addTag(){
|
|
auto tag = StringOutputStream( 64 )( "NewTag" );
|
|
int index = 0;
|
|
while( g_TexBro.m_all_tags.find( tag.c_str() ) != g_TexBro.m_all_tags.cend() )
|
|
tag( "NewTag", ++index );
|
|
|
|
auto item = new QListWidgetItem( tag.c_str() );
|
|
item->setFlags( Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemNeverHasChildren );
|
|
item->setCheckState( Qt::CheckState::Unchecked ); // is needed to see checkbox
|
|
g_TexBro.m_tagsListWidget->addItem( item );
|
|
|
|
g_TexBro.m_all_tags.insert( tag.c_str() );
|
|
|
|
g_TexBro.m_tagsListWidget->scrollToItem( item );
|
|
g_TexBro.m_tagsListWidget->editItem( item );
|
|
}
|
|
|
|
void TextureBrowser_renameTag(){
|
|
const auto selected = g_TexBro.m_tagsListWidget->selectedItems();
|
|
|
|
if ( !selected.empty() ) {
|
|
g_TexBro.m_tagsListWidget->editItem( selected.front() );
|
|
}
|
|
}
|
|
|
|
void TextureBrowser_deleteTag(){
|
|
const auto selected = g_TexBro.m_tagsListWidget->selectedItems();
|
|
if ( !selected.empty() ) {
|
|
if ( eIDYES == qt_MessageBox( g_TexBro.m_parent, "Are you sure you want to delete the selected tags?", "Delete Tag", EMessageBoxType::Question ) ) {
|
|
for( auto item : selected ){
|
|
auto tag = item->text().toLatin1();
|
|
delete item;
|
|
TagBuilder.DeleteTag( tag.constData() );
|
|
g_TexBro.m_all_tags.erase( tag.constData() );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void TextureBrowser_copyTag(){
|
|
g_TexBro.m_copied_tags.clear();
|
|
TagBuilder.GetShaderTags( g_TexBro.m_shader.c_str(), g_TexBro.m_copied_tags );
|
|
}
|
|
|
|
void TextureBrowser_pasteTag(){
|
|
const CopiedString shader = g_TexBro.m_shader;
|
|
|
|
if ( !TagBuilder.CheckShaderTag( shader.c_str() ) ) {
|
|
IShader* ishader = QERApp_Shader_ForName( shader.c_str() );
|
|
if ( ishader->IsDefault() ) {
|
|
// it's a texture
|
|
TagBuilder.AddShaderNode( shader.c_str(), TextureType::CUSTOM, NodeShaderType::TEXTURE );
|
|
}
|
|
else
|
|
{
|
|
// it's a shader
|
|
TagBuilder.AddShaderNode( shader.c_str(), TextureType::CUSTOM, NodeShaderType::SHADER );
|
|
}
|
|
ishader->DecRef();
|
|
|
|
for ( const CopiedString& tag : g_TexBro.m_copied_tags )
|
|
{
|
|
TagBuilder.AddShaderTag( shader.c_str(), tag.c_str(), NodeTagType::TAG );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for ( const CopiedString& tag : g_TexBro.m_copied_tags )
|
|
{
|
|
if ( !TagBuilder.CheckShaderTag( shader.c_str(), tag.c_str() ) ) {
|
|
// the tag doesn't exist - let's add it
|
|
TagBuilder.AddShaderTag( shader.c_str(), tag.c_str(), NodeTagType::TAG );
|
|
}
|
|
}
|
|
}
|
|
|
|
TextureBrowser_tagsSetCheckboxesForShader( shader.c_str() );
|
|
}
|
|
|
|
|
|
void TextureBrowser_SetNotex(){
|
|
StringOutputStream name( 256 );
|
|
name << GlobalRadiant().getAppPath() << "bitmaps/notex.png";
|
|
g_notex = name.c_str();
|
|
|
|
name.clear();
|
|
name << GlobalRadiant().getAppPath() << "bitmaps/shadernotex.png";
|
|
g_shadernotex = name.c_str();
|
|
}
|
|
|
|
|
|
class Filter_QLineEdit : public QLineEdit
|
|
{
|
|
protected:
|
|
void enterEvent( QEvent *event ) override {
|
|
setFocus();
|
|
}
|
|
void leaveEvent( QEvent *event ) override {
|
|
clearFocus();
|
|
}
|
|
bool event( QEvent *event ) override {
|
|
if( event->type() == QEvent::ShortcutOverride ){
|
|
QKeyEvent *keyEvent = static_cast<QKeyEvent*>( event );
|
|
if( keyEvent->key() == Qt::Key_Escape ){
|
|
clear();
|
|
event->accept();
|
|
}
|
|
}
|
|
return QLineEdit::event( event );
|
|
}
|
|
};
|
|
|
|
void TextureBrowser_filterSetModeIcon( QAction *action ){
|
|
action->setIcon( QApplication::style()->standardIcon(
|
|
g_TextureBrowser_filter_searchFromStart
|
|
? QStyle::StandardPixmap::SP_CommandLink
|
|
: QStyle::StandardPixmap::SP_FileDialogContentsView ) );
|
|
}
|
|
|
|
#include "timer.h"
|
|
|
|
class TexWndGLWidget : public QOpenGLWidget
|
|
{
|
|
TextureBrowser& m_texBro;
|
|
qreal m_scale;
|
|
MousePresses m_mouse;
|
|
public:
|
|
TexWndGLWidget( TextureBrowser& textureBrowser ) : QOpenGLWidget(), m_texBro( textureBrowser )
|
|
{
|
|
}
|
|
|
|
~TexWndGLWidget() override {
|
|
glwidget_context_destroyed();
|
|
}
|
|
protected:
|
|
void initializeGL() override
|
|
{
|
|
glwidget_context_created( *this );
|
|
// show definitely after gl init, otherwise crash
|
|
TextureBrowser_ShowStartupShaders();
|
|
}
|
|
void resizeGL( int w, int h ) override
|
|
{
|
|
m_scale = devicePixelRatioF();
|
|
m_texBro.m_width = float_to_integer( w * m_scale );
|
|
m_texBro.m_height = float_to_integer( h * m_scale );
|
|
m_texBro.heightChanged();
|
|
m_texBro.m_originInvalid = true;
|
|
}
|
|
void paintGL() override
|
|
{
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
m_texBro.draw();
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
}
|
|
|
|
void mousePressEvent( QMouseEvent *event ) override {
|
|
setFocus();
|
|
const auto press = m_mouse.press( event );
|
|
if( press == MousePresses::Left2x || press == MousePresses::Right2x ){
|
|
mouseDoubleClick( press );
|
|
}
|
|
else if ( press == MousePresses::Right ) {
|
|
TextureBrowser_Tracking_MouseDown( m_texBro );
|
|
}
|
|
else if ( press == MousePresses::Left || press == MousePresses::Middle ) {
|
|
if ( !event->modifiers().testFlag( Qt::KeyboardModifier::ShiftModifier ) )
|
|
SelectTexture( m_texBro, event->x() * m_scale, event->y() * m_scale, press == MousePresses::Middle );
|
|
}
|
|
}
|
|
void mouseDoubleClick( MousePresses::Result press ){
|
|
/* loads directory, containing active shader + focuses on it */
|
|
if ( press == MousePresses::Left2x && !TextureBrowser::wads ) {
|
|
const StringRange range( strchr( m_texBro.m_shader.c_str(), '/' ) + 1, strrchr( m_texBro.m_shader.c_str(), '/' ) + 1 );
|
|
if( !range.empty() ){
|
|
const CopiedString dir = range;
|
|
ScopeDisableScreenUpdates disableScreenUpdates( dir.c_str(), "Loading Textures" );
|
|
TextureBrowser_ShowDirectory( dir.c_str() );
|
|
TextureBrowser_Focus( m_texBro, m_texBro.m_shader.c_str() );
|
|
m_texBro.queueDraw();
|
|
}
|
|
}
|
|
else if ( press == MousePresses::Right2x ) {
|
|
ScopeDisableScreenUpdates disableScreenUpdates( TextureBrowser_getCommonShadersDir(), "Loading Textures" );
|
|
TextureBrowser_ShowDirectory( TextureBrowser_getCommonShadersDir() );
|
|
m_texBro.queueDraw();
|
|
}
|
|
}
|
|
void mouseReleaseEvent( QMouseEvent *event ) override {
|
|
const auto release = m_mouse.release( event );
|
|
if ( release == MousePresses::Right ) {
|
|
TextureBrowser_Tracking_MouseUp( m_texBro );
|
|
}
|
|
else if ( release == MousePresses::Left && event->modifiers().testFlag( Qt::KeyboardModifier::ShiftModifier ) ) {
|
|
TextureBrowser_ViewShader( m_texBro, event->modifiers(), event->x() * m_scale, event->y() * m_scale );
|
|
}
|
|
}
|
|
void wheelEvent( QWheelEvent *event ) override {
|
|
setFocus();
|
|
|
|
if( !m_texBro.m_parent->isActiveWindow() ){
|
|
m_texBro.m_parent->activateWindow();
|
|
m_texBro.m_parent->raise();
|
|
}
|
|
|
|
const int originy = m_texBro.getOriginY() + std::copysign( m_texBro.m_mouseWheelScrollIncrement, event->angleDelta().y() );
|
|
m_texBro.setOriginY( originy );
|
|
}
|
|
};
|
|
|
|
|
|
QWidget* TextureBrowser_constructWindow( QWidget* toplevel ){
|
|
TextureBrowser_checkTagFile();
|
|
TextureBrowser_SetNotex();
|
|
|
|
GlobalShaderSystem().setActiveShadersChangedNotify( ReferenceCaller<TextureBrowser, TextureBrowser_activeShadersChanged>( g_TexBro ) );
|
|
|
|
g_TexBro.m_parent = toplevel;
|
|
|
|
QSplitter *splitter = new QSplitter;
|
|
QWidget *containerWidgetLeft = new QWidget; // Adding a QLayout to a QSplitter is not supported, use proxy widget
|
|
QWidget *containerWidgetRight = new QWidget; // Adding a QLayout to a QSplitter is not supported, use proxy widget
|
|
splitter->addWidget( containerWidgetLeft );
|
|
splitter->addWidget( containerWidgetRight );
|
|
QVBoxLayout *vbox = new QVBoxLayout( containerWidgetLeft );
|
|
QHBoxLayout *hbox = new QHBoxLayout( containerWidgetRight );
|
|
|
|
hbox->setContentsMargins( 0, 0, 0, 0 );
|
|
vbox->setContentsMargins( 0, 0, 0, 0 );
|
|
hbox->setSpacing( 0 );
|
|
vbox->setSpacing( 0 );
|
|
|
|
|
|
{ // menu bar
|
|
QToolBar *toolbar = new QToolBar;
|
|
vbox->addWidget( toolbar );
|
|
|
|
QMenu* menu_view = TextureBrowser_constructViewMenu();
|
|
|
|
//show detached menu over floating tex bro and main wnd...
|
|
menu_view->setParent( toolbar, menu_view->windowFlags() ); //don't reset windowFlags
|
|
|
|
//view menu button
|
|
toolbar_append_button( toolbar, "View", "texbro_view.png", PointerCaller<QMenu, Popup_View_Menu>( menu_view ) );
|
|
|
|
toolbar_append_button( toolbar, "Find / Replace...", "texbro_gtk-find-and-replace.png", "FindReplaceTextures" );
|
|
|
|
toolbar_append_button( toolbar, "Flush & Reload Shaders", "texbro_refresh.png", "RefreshShaders" );
|
|
}
|
|
{ // filter entry
|
|
QLineEdit *entry = g_TexBro.m_filter_entry = new Filter_QLineEdit;
|
|
vbox->addWidget( entry );
|
|
entry->setClearButtonEnabled( true );
|
|
entry->setFocusPolicy( Qt::FocusPolicy::ClickFocus );
|
|
|
|
QAction *action = g_TexBro.m_filter_action = entry->addAction( QApplication::style()->standardIcon( QStyle::StandardPixmap::SP_CommandLink ), QLineEdit::LeadingPosition );
|
|
TextureBrowser_filterSetModeIcon( action );
|
|
action->setToolTip( "toggle match mode ( start / any position )" );
|
|
|
|
QObject::connect( entry, &QLineEdit::textChanged, []( const QString& text ){
|
|
g_TexBro.m_filter_string = text.toLatin1().constData();
|
|
g_TexBro.heightChanged();
|
|
g_TexBro.m_originInvalid = true;
|
|
} );
|
|
QObject::connect( action, &QAction::triggered, GlobalToggles_find( "SearchFromStart" ).m_command.m_callback );
|
|
}
|
|
{ // Texture TreeView
|
|
TextureBrowser_createTreeViewTree();
|
|
}
|
|
{ // gl_widget
|
|
g_TexBro.m_gl_widget = new TexWndGLWidget( g_TexBro );
|
|
|
|
hbox->addWidget( g_TexBro.m_gl_widget );
|
|
}
|
|
{ // gl_widget scrollbar
|
|
auto scroll = g_TexBro.m_texture_scroll = new QScrollBar;
|
|
hbox->addWidget( scroll );
|
|
|
|
QObject::connect( scroll, &QAbstractSlider::valueChanged, []( int value ){
|
|
g_TexBro.m_scrollAdjustment.value_changed( value );
|
|
} );
|
|
|
|
scroll->setVisible( g_TexBro.m_showTextureScrollbar );
|
|
}
|
|
|
|
{ // tag stuff
|
|
g_TexBro.m_tagsListWidget = new Tags_QListWidget;
|
|
g_TexBro.m_tagsListWidget->setSortingEnabled( true );
|
|
g_TexBro.m_tagsListWidget->setSelectionMode( QAbstractItemView::SelectionMode::ExtendedSelection );
|
|
g_TexBro.m_tagsListWidget->setEditTriggers( QAbstractItemView::EditTrigger::SelectedClicked | QAbstractItemView::EditTrigger::EditKeyPressed );
|
|
g_TexBro.m_tagsListWidget->setUniformItemSizes( true ); // optimization
|
|
g_TexBro.m_tagsListWidget->setFocusPolicy( Qt::FocusPolicy::ClickFocus );
|
|
|
|
g_TexBro.m_tagsListWidget->setItemDelegate( new Tags_QItemDelegate( g_TexBro.m_tagsListWidget ) );
|
|
|
|
QObject::connect( g_TexBro.m_tagsListWidget, &QListWidget::activated, TextureBrowser_searchTags );
|
|
|
|
TagBuilder.GetAllTags( g_TexBro.m_all_tags );
|
|
for ( const CopiedString& tag : g_TexBro.m_all_tags ){
|
|
auto item = new QListWidgetItem( tag.c_str() );
|
|
item->setFlags( Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemNeverHasChildren );
|
|
item->setCheckState( Qt::CheckState::Unchecked ); // is needed to see checkbox
|
|
g_TexBro.m_tagsListWidget->addItem( item );
|
|
}
|
|
}
|
|
{ // tag context menu
|
|
g_TexBro.m_tagsMenu = TextureBrowser_constructTagsMenu();
|
|
|
|
//show detached menu over floating tex bro and main wnd...
|
|
g_TexBro.m_tagsMenu->setParent( g_TexBro.m_tagsListWidget, g_TexBro.m_tagsMenu->windowFlags() ); //don't reset windowFlags
|
|
}
|
|
|
|
{ // Texture/Tag notebook
|
|
g_TexBro.m_tabs = new QTabWidget;
|
|
g_TexBro.m_tabs->setFocusPolicy( Qt::FocusPolicy::ClickFocus );
|
|
g_TexBro.m_tabs->setDocumentMode( true );
|
|
g_TexBro.m_tabs->setTabBarAutoHide( true );
|
|
g_TexBro.m_tabs->addTab( g_TexBro.m_treeView, "Textures" );
|
|
static_cast<QObject*>( g_TexBro.m_tagsListWidget )->setParent( g_TexBro.m_tabs );
|
|
TextureBrowser_tagsEnableGui( g_TexBro.m_tags );
|
|
vbox->addWidget( g_TexBro.m_tabs );
|
|
|
|
QObject::connect( g_TexBro.m_tabs, &QTabWidget::currentChanged, []( int index ){
|
|
if( index == 1 )
|
|
TextureBrowser_tagsSetCheckboxesForShader( g_TexBro.m_shader.c_str() );
|
|
} );
|
|
}
|
|
|
|
splitter->setStretchFactor( 0, 0 ); // consistent treeview side sizing on resizes
|
|
splitter->setStretchFactor( 1, 1 );
|
|
g_guiSettings.addSplitter( splitter, "TextureBrowser/splitter", { 100, 800 } );
|
|
return splitter;
|
|
}
|
|
|
|
void TextureBrowser_destroyWindow(){
|
|
GlobalShaderSystem().setActiveShadersChangedNotify( Callback() );
|
|
}
|
|
|
|
const Vector3& TextureBrowser_getBackgroundColour(){
|
|
return g_TexBro.m_color_textureback;
|
|
}
|
|
|
|
void TextureBrowser_setBackgroundColour( const Vector3& colour ){
|
|
g_TexBro.m_color_textureback = colour;
|
|
g_TexBro.queueDraw();
|
|
}
|
|
|
|
void TextureBrowser_shaderInfo(){
|
|
const char* name = TextureBrowser_GetSelectedShader();
|
|
IShader* shader = QERApp_Shader_ForName( name );
|
|
|
|
DoShaderInfoDlg( name, shader->getShaderFileName(), "Shader Info" );
|
|
|
|
shader->DecRef();
|
|
}
|
|
|
|
void RefreshShaders(){
|
|
g_TextureBrowser_currentDirectory = "";
|
|
g_TexBro.m_searchedTags = false;
|
|
TextureBrowser_updateTitle();
|
|
|
|
ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Shaders" );
|
|
GlobalShaderSystem().refresh();
|
|
TextureBrowser_constructTreeStore(); /* texturebrowser tree update on vfs restart */
|
|
UpdateAllWindows();
|
|
}
|
|
|
|
void TextureBrowser_ToggleShowShaders(){
|
|
g_TexBro.m_showShaders ^= 1;
|
|
g_TexBro.m_showshaders_item.update();
|
|
|
|
g_TexBro.m_heightChanged = true;
|
|
g_TexBro.m_originInvalid = true;
|
|
g_activeShadersChangedCallbacks();
|
|
|
|
g_TexBro.queueDraw();
|
|
}
|
|
|
|
void TextureBrowser_ToggleShowTextures(){
|
|
g_TexBro.m_showTextures ^= 1;
|
|
g_TexBro.m_showtextures_item.update();
|
|
|
|
g_TexBro.m_heightChanged = true;
|
|
g_TexBro.m_originInvalid = true;
|
|
g_activeShadersChangedCallbacks();
|
|
|
|
g_TexBro.queueDraw();
|
|
}
|
|
|
|
void TextureBrowser_ToggleShowShaderListOnly(){
|
|
g_TextureBrowser_shaderlistOnly ^= 1;
|
|
g_TexBro.m_showshaderlistonly_item.update();
|
|
|
|
TextureBrowser_constructTreeStore();
|
|
}
|
|
|
|
void TextureBrowser_showAll(){
|
|
g_TextureBrowser_currentDirectory = "";
|
|
g_TexBro.m_searchedTags = false;
|
|
// TextureBrowser_SetHideUnused( g_TexBro, false );
|
|
TextureBrowser_ToggleHideUnused(); //toggle to show all used on the first hit and all on the second
|
|
TextureBrowser_updateTitle();
|
|
}
|
|
|
|
void TextureBrowser_FixedSize(){
|
|
g_TextureBrowser_fixedSize ^= 1;
|
|
g_TexBro.m_fixedsize_item.update();
|
|
TextureBrowser_activeShadersChanged( g_TexBro );
|
|
}
|
|
|
|
void TextureBrowser_FilterNotex(){
|
|
g_TextureBrowser_filterNotex ^= 1;
|
|
g_TexBro.m_filternotex_item.update();
|
|
TextureBrowser_activeShadersChanged( g_TexBro );
|
|
}
|
|
|
|
void TextureBrowser_EnableAlpha(){
|
|
g_TextureBrowser_enableAlpha ^= 1;
|
|
g_TexBro.m_enablealpha_item.update();
|
|
TextureBrowser_activeShadersChanged( g_TexBro );
|
|
}
|
|
|
|
void TextureBrowser_tagsToggleGui(){
|
|
g_TexBro.m_tags ^= 1;
|
|
g_TexBro.m_tags_item.update();
|
|
TextureBrowser_tagsEnableGui( g_TexBro.m_tags );
|
|
}
|
|
|
|
void TextureBrowser_filter_searchFromStart(){
|
|
g_TextureBrowser_filter_searchFromStart ^= 1;
|
|
g_TexBro.m_filter_searchFromStart_item.update();
|
|
TextureBrowser_activeShadersChanged( g_TexBro );
|
|
TextureBrowser_filterSetModeIcon( g_TexBro.m_filter_action );
|
|
}
|
|
|
|
|
|
void TextureBrowser_exportTitle( const StringImportCallback& importer ){
|
|
StringOutputStream buffer( 64 );
|
|
buffer << "Textures: ";
|
|
if ( !g_TextureBrowser_currentDirectory.empty() ) {
|
|
buffer << g_TextureBrowser_currentDirectory;
|
|
}
|
|
else
|
|
{
|
|
buffer << "all";
|
|
}
|
|
importer( buffer.c_str() );
|
|
}
|
|
|
|
|
|
void TextureScaleImport( TextureBrowser& textureBrowser, int value ){
|
|
switch ( value )
|
|
{
|
|
case 0:
|
|
TextureBrowser_setScale( textureBrowser, 10 );
|
|
break;
|
|
case 1:
|
|
TextureBrowser_setScale( textureBrowser, 25 );
|
|
break;
|
|
case 2:
|
|
TextureBrowser_setScale( textureBrowser, 50 );
|
|
break;
|
|
case 3:
|
|
TextureBrowser_setScale( textureBrowser, 100 );
|
|
break;
|
|
case 4:
|
|
TextureBrowser_setScale( textureBrowser, 200 );
|
|
break;
|
|
}
|
|
}
|
|
typedef ReferenceCaller1<TextureBrowser, int, TextureScaleImport> TextureScaleImportCaller;
|
|
|
|
void TextureScaleExport( TextureBrowser& textureBrowser, const IntImportCallback& importer ){
|
|
switch ( textureBrowser.m_textureScale )
|
|
{
|
|
case 10:
|
|
importer( 0 );
|
|
break;
|
|
case 25:
|
|
importer( 1 );
|
|
break;
|
|
case 50:
|
|
importer( 2 );
|
|
break;
|
|
case 100:
|
|
importer( 3 );
|
|
break;
|
|
case 200:
|
|
importer( 4 );
|
|
break;
|
|
}
|
|
}
|
|
typedef ReferenceCaller1<TextureBrowser, const IntImportCallback&, TextureScaleExport> TextureScaleExportCaller;
|
|
|
|
void UniformTextureSizeImport( TextureBrowser& textureBrowser, int value ){
|
|
if ( value >= 16 )
|
|
TextureBrowser_setUniformSize( textureBrowser, value );
|
|
}
|
|
typedef ReferenceCaller1<TextureBrowser, int, UniformTextureSizeImport> UniformTextureSizeImportCaller;
|
|
|
|
void UniformTextureMinSizeImport( TextureBrowser& textureBrowser, int value ){
|
|
if ( value >= 16 )
|
|
TextureBrowser_setUniformMinSize( textureBrowser, value );
|
|
}
|
|
typedef ReferenceCaller1<TextureBrowser, int, UniformTextureMinSizeImport> UniformTextureMinSizeImportCaller;
|
|
|
|
void TextureBrowser_constructPreferences( PreferencesPage& page ){
|
|
page.appendCheckBox(
|
|
"", "Texture scrollbar",
|
|
TextureBrowserImportShowScrollbarCaller( g_TexBro ),
|
|
BoolExportCaller( g_TexBro.m_showTextureScrollbar )
|
|
);
|
|
{
|
|
const char* texture_scale[] = { "10%", "25%", "50%", "100%", "200%" };
|
|
page.appendCombo(
|
|
"Texture Thumbnail Scale",
|
|
StringArrayRange( texture_scale ),
|
|
IntImportCallback( TextureScaleImportCaller( g_TexBro ) ),
|
|
IntExportCallback( TextureScaleExportCaller( g_TexBro ) )
|
|
);
|
|
}
|
|
page.appendSpinner( "Thumbnails Max Size", g_TexBro.m_uniformTextureSize, 16, 8192 );
|
|
page.appendSpinner( "Thumbnails Min Size", g_TexBro.m_uniformTextureMinSize, 16, 8192 );
|
|
page.appendSpinner( "Mousewheel Increment", g_TexBro.m_mouseWheelScrollIncrement, 0, 8192 );
|
|
{
|
|
const char* startup_shaders[] = { "None", TextureBrowser_getCommonShadersName() };
|
|
page.appendCombo( "Load Shaders at Startup", reinterpret_cast<int&>( g_TexBro.m_startupShaders ), StringArrayRange( startup_shaders ) );
|
|
}
|
|
{
|
|
StringOutputStream sstream( 256 );
|
|
sstream << "Hide nonShaders in " << TextureBrowser_getCommonShadersDir() << " folder";
|
|
page.appendCheckBox(
|
|
"", sstream.c_str(),
|
|
g_TexBro.m_hideNonShadersInCommon
|
|
);
|
|
}
|
|
}
|
|
void TextureBrowser_constructPage( PreferenceGroup& group ){
|
|
PreferencesPage page( group.createPage( "Texture Browser", "Texture Browser Preferences" ) );
|
|
TextureBrowser_constructPreferences( page );
|
|
}
|
|
void TextureBrowser_registerPreferencesPage(){
|
|
PreferencesDialog_addSettingsPage( FreeCaller1<PreferenceGroup&, TextureBrowser_constructPage>() );
|
|
}
|
|
|
|
|
|
#include "preferencesystem.h"
|
|
#include "stringio.h"
|
|
|
|
typedef ReferenceCaller1<TextureBrowser, std::size_t, TextureBrowser_setScale> TextureBrowserSetScaleCaller;
|
|
|
|
|
|
|
|
void TextureClipboard_textureSelected( const char* shader );
|
|
|
|
void TextureBrowser_Construct(){
|
|
GlobalCommands_insert( "ShaderInfo", FreeCaller<TextureBrowser_shaderInfo>() );
|
|
GlobalCommands_insert( "TagSearchUntagged", FreeCaller<TextureBrowser_showUntagged>() );
|
|
GlobalCommands_insert( "TagSearch", FreeCaller<TextureBrowser_searchTags>() );
|
|
GlobalCommands_insert( "TagAdd", FreeCaller<TextureBrowser_addTag>() );
|
|
GlobalCommands_insert( "TagRename", FreeCaller<TextureBrowser_renameTag>() );
|
|
GlobalCommands_insert( "TagDelete", FreeCaller<TextureBrowser_deleteTag>() );
|
|
GlobalCommands_insert( "TagCopy", FreeCaller<TextureBrowser_copyTag>() );
|
|
GlobalCommands_insert( "TagPaste", FreeCaller<TextureBrowser_pasteTag>() );
|
|
GlobalCommands_insert( "RefreshShaders", FreeCaller<RefreshShaders>() );
|
|
GlobalToggles_insert( "ShowInUse", FreeCaller<TextureBrowser_ToggleHideUnused>(), ToggleItem::AddCallbackCaller( g_TexBro.m_hideunused_item ), QKeySequence( "U" ) );
|
|
GlobalCommands_insert( "ShowAllTextures", FreeCaller<TextureBrowser_showAll>(), QKeySequence( "Ctrl+A" ) );
|
|
GlobalCommands_insert( "ToggleTextures", FreeCaller<TextureBrowser_toggleShow>(), QKeySequence( "T" ) );
|
|
GlobalToggles_insert( "ToggleShowShaders", FreeCaller<TextureBrowser_ToggleShowShaders>(), ToggleItem::AddCallbackCaller( g_TexBro.m_showshaders_item ) );
|
|
GlobalToggles_insert( "ToggleShowTextures", FreeCaller<TextureBrowser_ToggleShowTextures>(), ToggleItem::AddCallbackCaller( g_TexBro.m_showtextures_item ) );
|
|
GlobalToggles_insert( "ToggleShowShaderlistOnly", FreeCaller<TextureBrowser_ToggleShowShaderListOnly>(), ToggleItem::AddCallbackCaller( g_TexBro.m_showshaderlistonly_item ) );
|
|
GlobalToggles_insert( "FixedSize", FreeCaller<TextureBrowser_FixedSize>(), ToggleItem::AddCallbackCaller( g_TexBro.m_fixedsize_item ) );
|
|
GlobalToggles_insert( "FilterNotex", FreeCaller<TextureBrowser_FilterNotex>(), ToggleItem::AddCallbackCaller( g_TexBro.m_filternotex_item ) );
|
|
GlobalToggles_insert( "EnableAlpha", FreeCaller<TextureBrowser_EnableAlpha>(), ToggleItem::AddCallbackCaller( g_TexBro.m_enablealpha_item ) );
|
|
GlobalToggles_insert( "TagsToggleGui", FreeCaller<TextureBrowser_tagsToggleGui>(), ToggleItem::AddCallbackCaller( g_TexBro.m_tags_item ) );
|
|
GlobalToggles_insert( "SearchFromStart", FreeCaller<TextureBrowser_filter_searchFromStart>(), ToggleItem::AddCallbackCaller( g_TexBro.m_filter_searchFromStart_item ) );
|
|
|
|
GlobalPreferenceSystem().registerPreference( "TextureScale",
|
|
makeSizeStringImportCallback( TextureBrowserSetScaleCaller( g_TexBro ) ),
|
|
SizeExportStringCaller( g_TexBro.m_textureScale )
|
|
);
|
|
GlobalPreferenceSystem().registerPreference( "UniformTextureSize",
|
|
makeIntStringImportCallback(UniformTextureSizeImportCaller(g_TexBro)),
|
|
IntExportStringCaller(g_TexBro.m_uniformTextureSize) );
|
|
GlobalPreferenceSystem().registerPreference( "UniformTextureMinSize",
|
|
makeIntStringImportCallback(UniformTextureMinSizeImportCaller(g_TexBro)),
|
|
IntExportStringCaller(g_TexBro.m_uniformTextureMinSize) );
|
|
GlobalPreferenceSystem().registerPreference( "TextureScrollbar",
|
|
makeBoolStringImportCallback( TextureBrowserImportShowScrollbarCaller( g_TexBro ) ),
|
|
BoolExportStringCaller( g_TexBro.m_showTextureScrollbar )
|
|
);
|
|
GlobalPreferenceSystem().registerPreference( "ShowShaders", BoolImportStringCaller( g_TexBro.m_showShaders ), BoolExportStringCaller( g_TexBro.m_showShaders ) );
|
|
GlobalPreferenceSystem().registerPreference( "ShowTextures", BoolImportStringCaller( g_TexBro.m_showTextures ), BoolExportStringCaller( g_TexBro.m_showTextures ) );
|
|
GlobalPreferenceSystem().registerPreference( "ShowShaderlistOnly", BoolImportStringCaller( g_TextureBrowser_shaderlistOnly ), BoolExportStringCaller( g_TextureBrowser_shaderlistOnly ) );
|
|
GlobalPreferenceSystem().registerPreference( "FixedSize", BoolImportStringCaller( g_TextureBrowser_fixedSize ), BoolExportStringCaller( g_TextureBrowser_fixedSize ) );
|
|
GlobalPreferenceSystem().registerPreference( "FilterNotex", BoolImportStringCaller( g_TextureBrowser_filterNotex ), BoolExportStringCaller( g_TextureBrowser_filterNotex ) );
|
|
GlobalPreferenceSystem().registerPreference( "EnableAlpha", BoolImportStringCaller( g_TextureBrowser_enableAlpha ), BoolExportStringCaller( g_TextureBrowser_enableAlpha ) );
|
|
GlobalPreferenceSystem().registerPreference( "TagsShowGui", BoolImportStringCaller( g_TexBro.m_tags ), BoolExportStringCaller( g_TexBro.m_tags ) );
|
|
GlobalPreferenceSystem().registerPreference( "SearchFromStart", BoolImportStringCaller( g_TextureBrowser_filter_searchFromStart ), BoolExportStringCaller( g_TextureBrowser_filter_searchFromStart ) );
|
|
GlobalPreferenceSystem().registerPreference( "LoadShaders", IntImportStringCaller( reinterpret_cast<int&>( g_TexBro.m_startupShaders ) ), IntExportStringCaller( reinterpret_cast<int&>( g_TexBro.m_startupShaders ) ) );
|
|
GlobalPreferenceSystem().registerPreference( "WheelMouseInc", IntImportStringCaller( g_TexBro.m_mouseWheelScrollIncrement ), IntExportStringCaller( g_TexBro.m_mouseWheelScrollIncrement ) );
|
|
GlobalPreferenceSystem().registerPreference( "SI_Colors0", Vector3ImportStringCaller( g_TexBro.m_color_textureback ), Vector3ExportStringCaller( g_TexBro.m_color_textureback ) );
|
|
GlobalPreferenceSystem().registerPreference( "HideNonShadersInCommon", BoolImportStringCaller( g_TexBro.m_hideNonShadersInCommon ), BoolExportStringCaller( g_TexBro.m_hideNonShadersInCommon ) );
|
|
|
|
g_TexBro.m_shader = texdef_name_default();
|
|
|
|
TextureBrowser::wads = !string_empty( g_pGameDescription->getKeyValue( "show_wads" ) );
|
|
|
|
Textures_setModeChangedNotify( ConstMemberCaller<TextureBrowser, &TextureBrowser::queueDraw>( g_TexBro ) );
|
|
|
|
TextureBrowser_registerPreferencesPage();
|
|
|
|
GlobalShaderSystem().attach( g_ShadersObserver );
|
|
|
|
TextureBrowser_textureSelected = TextureClipboard_textureSelected;
|
|
}
|
|
void TextureBrowser_Destroy(){
|
|
TagBuilder.SaveXmlDoc();
|
|
|
|
GlobalShaderSystem().detach( g_ShadersObserver );
|
|
|
|
Textures_setModeChangedNotify( Callback() );
|
|
}
|