diff --git a/CHANGELOG.md b/CHANGELOG.md index 04270c1..d8a768f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## [git] - 2019-04-08 +(poikilos) +### Added +* toggle texture interpolation (via checkbox and `x` hotkey) +* INDEX_ variables to store ID of GUI elements +* Text box show name of loaded texture path +### Changed +* check if model is loaded before changing view options (prevents crash) +* unified checkboxes with m_* booleans, by tracking whether box is + checked via INDEX_ variables for each ID of GUI elements. +* look for ../textures/.png & .jpg 1st time pressing `t` + ## [git] - 2019-03-09 (poikilos) ### Added diff --git a/Engine.cpp b/Engine.cpp index b43ceb7..50b76f8 100644 --- a/Engine.cpp +++ b/Engine.cpp @@ -265,6 +265,8 @@ void Engine::loadMesh( const wstring &fileName ) } } } + m_LoadedMesh->setMaterialType(video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF); + // EMT_TRANSPARENT_ALPHA_CHANNEL: constant transparency } } } @@ -279,7 +281,10 @@ void Engine::reloadMesh() void Engine::reloadTexture() { if (this->m_PrevTexturePath.length() > 0) { - loadTexture(this->m_PrevTexturePath); + if (this->m_UserInterface->texturePathEditBox->getText() != L"") + loadTexture(this->m_UserInterface->texturePathEditBox->getText()); + else + loadTexture(this->m_PrevTexturePath); } } @@ -292,28 +297,53 @@ bool Engine::loadTexture(const wstring &fileName) ret = true; } this->m_PrevTexturePath = fileName; + this->m_UserInterface->texturePathEditBox->setText(this->m_PrevTexturePath.c_str()); return ret; } -void Engine::setMeshDisplayMode( bool wireframe, bool lighting ) +void Engine::setMeshDisplayMode( bool wireframe, bool lighting, bool textureInterpolation) { - for( int materialIndex = 0; materialIndex < m_LoadedMesh->getMaterialCount(); materialIndex ++ ) - { - // Set Wireframe display - m_LoadedMesh->getMaterial( materialIndex ).Wireframe = wireframe; + if (m_LoadedMesh != nullptr) { + for( int materialIndex = 0; materialIndex < m_LoadedMesh->getMaterialCount(); materialIndex ++ ) + { + // Set Wireframe display + m_LoadedMesh->getMaterial(materialIndex).Wireframe = wireframe; - // Set Lighting - if( ! lighting ) - { - m_LoadedMesh->getMaterial( materialIndex ).Lighting = false; - m_LoadedMesh->getMaterial( materialIndex ).EmissiveColor = SColor( 255, 255, 255, 255 ); - } - else - { - m_LoadedMesh->getMaterial( materialIndex ).Lighting = true; - m_LoadedMesh->getMaterial( materialIndex ).EmissiveColor = SColor( 255, 0, 0, 0 ); + // Set Lighting + if( ! lighting ) + { + m_LoadedMesh->getMaterial(materialIndex).Lighting = false; + m_LoadedMesh->getMaterial(materialIndex).EmissiveColor = SColor( 255, 255, 255, 255 ); + } + else + { + m_LoadedMesh->getMaterial(materialIndex).Lighting = true; + m_LoadedMesh->getMaterial(materialIndex).EmissiveColor = SColor( 255, 0, 0, 0 ); + } + // m_LoadedMesh->setMaterialType(video::EMT_TRANSPARENT_ALPHA_CHANNEL); //already done on load + // m_LoadedMesh->setMaterialFlag(video::E_ALPHA_SOURCE, true); // requires EMT_ONETEXTURE + if (textureInterpolation) { + m_LoadedMesh->setMaterialFlag(video::EMF_BILINEAR_FILTER, true); + m_LoadedMesh->setMaterialFlag(video::EMF_TRILINEAR_FILTER, true); + } + else { + m_LoadedMesh->setMaterialFlag(video::EMF_BILINEAR_FILTER, false); + m_LoadedMesh->setMaterialFlag(video::EMF_TRILINEAR_FILTER, false); + //m_LoadedMesh->setMaterialFlag(video::E_ALPHA_SOURCE, true); + + // below doesn't work for some reason: + // video::SMaterial mat = m_LoadedMesh->getMaterial(materialIndex); + // mat.UseMipMaps = false; + // mat.setFlag(video::EMF_BILINEAR_FILTER, false); + // mat.setFlag(video::EMF_TRILINEAR_FILTER, false); + + // below would require patching Irrlicht: + // GLint filteringMipMaps = GL_NEAREST_MIPMAP_NEAREST + // // above is used by glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filteringMipMaps); + } } } + else debug() << "WARNING in setMeshDisplayMode: No mesh is loaded " << endl; } bool Engine::isAnimating() diff --git a/Engine.h b/Engine.h index ae2674e..ee79dbe 100644 --- a/Engine.h +++ b/Engine.h @@ -80,7 +80,7 @@ public: void reloadMesh(); void reloadTexture(); bool loadTexture( const std::wstring &fileName ); - void setMeshDisplayMode( bool wireframe = false, bool lighting = true ); + void setMeshDisplayMode(bool wireframe = false, bool lighting = true, bool textureInterpolation = true); bool isAnimating(); void playAnimation(); void pauseAnimation(); diff --git a/README.md b/README.md index b2707c8..607ea7a 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,8 @@ bird: [github.com/poikilos/mobs_sky](https://github.com/poikilos/mobs_sky) Website: [poikilos.org](https://poikilos.org) ## Main Features in poikilos fork -* stabilized (makes sure font, model or texture loads before using) +* stabilized (makes sure font, model or texture loads before using; + makes sure model is loaded before setting View options) * modernized includes (`#include` statements specify "irrlicht" directory instead of assuming it) * double-click after you associate this program with the file types @@ -123,10 +124,15 @@ only applies to Visual Studio users.) * `t` / `e`: cycle through textures in `../textures` using `t` key (`e` to go back) such as for Minetest mods, where model must be in `modname/models/` and texture must be in `modname/textures/`. - If `../textures` doesn't exist relative to the model file's directory, - the model file's own directory will be used. + - If `"../textures/" + basename(modelName) + ".png"` or `".jpg"` is + present, pressing `t` for the first time will load it. + - If `../textures` doesn't exist relative to the model file's + directory, the model file's own directory will be used. +* `x`: toggle texture interpolation (shortcut for View, Texture + Interpolation) * `F5`: Reload last model file -* `r`: Reload last texture file +* `r`: Reload last texture file (may not be working due to caching, + but does try to load different file if texture edit box changed). * drag with middle button: rotate view * drag with middle button while holding shift key: pan up and down * `z` or `y`: change camera "up" axis to Z or Y (Y is default; diff --git a/UserInterface.cpp b/UserInterface.cpp index 0c48575..9bd727b 100644 --- a/UserInterface.cpp +++ b/UserInterface.cpp @@ -29,20 +29,21 @@ namespace fs = std::experimental::filesystem; void UserInterface::setupUserInterface() { // Menu - IGUIContextMenu *menu = m_Gui->addMenu(); + menu = m_Gui->addMenu(); menu->addItem( L"File", UIE_FILEMENU, true, true ); menu->addItem( L"View", UIE_VIEWMENU, true, true ); // File Menu - IGUIContextMenu *fileMenu = menu->getSubMenu( 0 ); + fileMenu = menu->getSubMenu( 0 ); fileMenu->addItem( L"Load", UIC_FILE_LOAD ); fileMenu->addItem( L"LoadTexture", UIC_FILE_LOAD_TEXTURE ); fileMenu->addItem( L"Quit", UIC_FILE_QUIT ); // View Menu - IGUIContextMenu *viewMenu = menu->getSubMenu( 1 ); - viewMenu->addItem( L"Wireframe Mesh", UIC_VIEW_WIREFRAME, true, false, false, true ); - viewMenu->addItem( L"Lighting",UIC_VIEW_LIGHTING, true, false, true, true ); + viewMenu = menu->getSubMenu( 1 ); + INDEX_VIEW_WIREFRAME_MESH = viewMenu->addItem(L"Wireframe Mesh", UIC_VIEW_WIREFRAME, true, false, this->m_WireframeDisplay, true); + INDEX_VIEW_LIGHTING = viewMenu->addItem(L"Lighting", UIC_VIEW_LIGHTING, true, false, this->m_Lighting, true ); + INDEX_VIEW_TEXTURE_INTERPOLATION = viewMenu->addItem(L"Texture Interpolation", UIC_VIEW_TEXTURE_INTERPOLATION, true, false, this->m_TextureInterpolation, true); // Playback Control Window dimension2d windowSize = m_Engine->m_Driver->getScreenSize(); @@ -87,6 +88,25 @@ void UserInterface::setupUserInterface() UIE_PLAYBACKSETFRAMEEDITBOX ); + y += size_y + spacing_y; + texturePathStaticText = m_Gui->addStaticText( + L"Texture Path:", + rect( vector2d( spacing_x, y ), dimension2d( size_x, size_y )), + true, + true, + playbackWindow, + UIE_TEXTUREPATHSTATICTEXT, + false + ); + y += size_y + spacing_y; + texturePathEditBox = m_Gui->addEditBox( + L"", + rect( vector2d( spacing_x, y ), dimension2d( size_x, size_y )), + true, + playbackWindow, + UIE_TEXTUREPATHEDITBOX + ); + // Set Font for UI Elements m_GuiFontFace = new CGUITTFace(); // irrString defines stringc as string @@ -155,20 +175,31 @@ void UserInterface::handleMenuItemPressed( IGUIContextMenu *menu ) break; case UIC_VIEW_WIREFRAME: - m_WireframeDisplay = m_WireframeDisplay ? false : true; - m_Engine->setMeshDisplayMode( m_WireframeDisplay, m_Lighting ); + m_WireframeDisplay = viewMenu->isItemChecked(INDEX_VIEW_WIREFRAME_MESH); + m_Engine->setMeshDisplayMode(m_WireframeDisplay, m_Lighting, m_TextureInterpolation); break; case UIC_VIEW_LIGHTING: - m_Lighting = m_Lighting ? false : true; - m_Engine->setMeshDisplayMode( m_WireframeDisplay, m_Lighting ); + m_Lighting = viewMenu->isItemChecked(INDEX_VIEW_LIGHTING); + m_Engine->setMeshDisplayMode(m_WireframeDisplay, m_Lighting, m_TextureInterpolation); break; + + case UIC_VIEW_TEXTURE_INTERPOLATION: + m_TextureInterpolation = viewMenu->isItemChecked(INDEX_VIEW_TEXTURE_INTERPOLATION); + m_Engine->setMeshDisplayMode(m_WireframeDisplay, m_Lighting, m_TextureInterpolation); + break; + } + + } // PUBLIC UserInterface::UserInterface( Engine *engine ) { + INDEX_VIEW_TEXTURE_INTERPOLATION = -1; + INDEX_VIEW_WIREFRAME_MESH = -1; + INDEX_VIEW_LIGHTING = -1; this->playbackStartStopButton = nullptr; m_Engine = engine; @@ -176,6 +207,7 @@ UserInterface::UserInterface( Engine *engine ) m_WireframeDisplay = false; m_Lighting = true; + m_TextureInterpolation = true; setupUserInterface(); } @@ -230,7 +262,40 @@ bool UserInterface::loadNextTexture(int direction) std::wstring lastPath = L""; bool found = false; + bool force = false; + wstring tryPath; if (fs::is_directory(fs::status(path))) { + if (this->m_Engine->m_PrevTexturePath.length() == 0) { + if (this->m_Engine->m_PreviousPath.length() > 0 ) { + //debug() << "tryPath..." << endl; + tryPath = texturesPath + dirSeparator + Utility::withoutExtension(Utility::basename(this->m_Engine->m_PreviousPath)) + L".png"; + // debug() << "tryPath 1a " << Utility::toString(tryPath) << "..." << endl; + tryPath = Utility::toWstring(Utility::toString(tryPath)); + // debug() << "tryPath 1b " << Utility::toString(tryPath) << "..." << endl; + // tryPath = texturesPath + dirSeparator + Utility::basename(this->m_Engine->m_PreviousPath) + L".png"; + if (!Utility::isFile(tryPath)) { + //asdf + tryPath = texturesPath + dirSeparator + Utility::withoutExtension(Utility::basename(this->m_Engine->m_PreviousPath)) + L".jpg"; + // debug() << "tryPath 2a " << Utility::toString(tryPath) << "..." << endl; + tryPath = Utility::toWstring(Utility::toString(tryPath)); + // tryPath = Utility::toWstring(Utility::toString(L"debug1")); // ../iconv/loop.c:457: internal_utf8_loop_single: Assertion `inptr - (state->__count & 7)' failed. + // debug() << "tryPath 2b " << Utility::toString(tryPath) << "..." << endl; + // tryPath = texturesPath + dirSeparator + Utility::basename(this->m_Engine->m_PreviousPath) + L".jpg"; + if (Utility::isFile(tryPath)) { + nextPath = tryPath; + found = true; + force = true; + } + } + else { + nextPath = tryPath; + found = true; + force = true; + } + } + } + //debug() << "tryPath: " << Utility::toString(tryPath) << endl; + //debug() << "nextPath: " << Utility::toString(nextPath) << endl; for (const auto & itr : fs::directory_iterator(path)) { std::wstring ext = Utility::extensionOf(itr.path().wstring()); // no dot! if (!is_directory(itr.status()) @@ -240,14 +305,14 @@ bool UserInterface::loadNextTexture(int direction) if (nextPath.length() == 0) nextPath = itr.path().wstring(); lastPath = itr.path().wstring(); if (found && direction > 0) { - nextPath = itr.path().wstring(); + if (!force) nextPath = itr.path().wstring(); break; } - if (itr.path().wstring()==this->m_Engine->m_PrevTexturePath) found = true; + if (itr.path().wstring() == this->m_Engine->m_PrevTexturePath) found = true; if (!found) retroPath = itr.path().wstring(); } } - if (retroPath.length()==0) + if (retroPath.length() == 0) retroPath = lastPath; // previous is last if at beginning if (direction < 0) nextPath = retroPath; @@ -286,6 +351,14 @@ bool UserInterface::OnEvent( const SEvent &event ) else if (event.KeyInput.Key == irr::KEY_KEY_Y) { m_Engine->setZUp(false); } + else if (event.KeyInput.Key == irr::KEY_KEY_X) { + // IGUIContextMenu* textureInterpolationElement = dynamic_cast(viewMenu->getElementFromId(UIC_VIEW_TEXTURE_INTERPOLATION)); + //m_TextureInterpolation = textureInterpolationElement->isItemChecked(UIC_VIEW_TEXTURE_INTERPOLATION); + m_TextureInterpolation = m_TextureInterpolation ? false : true; + //doesn't work: m_TextureInterpolation = viewMenu->isItemChecked(UIC_VIEW_TEXTURE_INTERPOLATION); + m_Engine->setMeshDisplayMode(m_WireframeDisplay, m_Lighting, m_TextureInterpolation); + viewMenu->setItemChecked(INDEX_VIEW_TEXTURE_INTERPOLATION, m_TextureInterpolation); + } else if (event.KeyInput.Char == L'+' || event.KeyInput.Char == L'=') { m_Engine->setAnimationFPS(m_Engine->animationFPS() + 5); } diff --git a/UserInterface.h b/UserInterface.h index 3ca5eb2..86daa34 100644 --- a/UserInterface.h +++ b/UserInterface.h @@ -18,16 +18,19 @@ enum UserInterfaceElements UIE_LOADTEXTUREDIALOG = 1006, UIE_PLAYBACKINCREASEBUTTON = 1007, UIE_PLAYBACKDECREASEBUTTON = 1008, - UIE_PLAYBACKSETFRAMEEDITBOX = 1009 + UIE_PLAYBACKSETFRAMEEDITBOX = 1009, + UIE_TEXTUREPATHSTATICTEXT = 1010, + UIE_TEXTUREPATHEDITBOX = 1011 }; enum UserInterfaceCommands { - UIC_FILE_LOAD = 1000, - UIC_FILE_QUIT = 1001, - UIC_FILE_LOAD_TEXTURE = 1002, - UIC_VIEW_WIREFRAME = 2000, - UIC_VIEW_LIGHTING = 2001 + UIC_FILE_LOAD = 1000, + UIC_FILE_QUIT = 1001, + UIC_FILE_LOAD_TEXTURE = 1002, + UIC_VIEW_WIREFRAME = 2000, + UIC_VIEW_LIGHTING = 2001, + UIC_VIEW_TEXTURE_INTERPOLATION = 2002 }; class UserInterface : public irr::IEventReceiver @@ -45,12 +48,22 @@ private: bool m_WireframeDisplay; bool m_Lighting; + bool m_TextureInterpolation; public: + irr::gui::IGUIContextMenu *menu; + irr::gui::IGUIContextMenu *fileMenu; + irr::gui::IGUIContextMenu *viewMenu; irr::gui::IGUIButton *playbackStartStopButton; irr::gui::IGUIButton *playbackIncreaseButton; irr::gui::IGUIButton *playbackDecreaseButton; irr::gui::IGUIEditBox *playbackSetFrameEditBox; + irr::gui::IGUIStaticText *texturePathStaticText; + irr::gui::IGUIEditBox *texturePathEditBox; + irr::u32 INDEX_VIEW_TEXTURE_INTERPOLATION; + irr::u32 INDEX_VIEW_WIREFRAME_MESH; + irr::u32 INDEX_VIEW_LIGHTING; + UserInterface( Engine *device ); ~UserInterface(); diff --git a/Utils.cpp b/Utils.cpp index f434db1..5a5fb86 100644 --- a/Utils.cpp +++ b/Utils.cpp @@ -123,7 +123,7 @@ wstring Utility::delimiter(const wstring &path) std::wstring ret = L"/"; std::wstring::size_type lastSlashPos = path.find_last_of(L"/"); if (lastSlashPos == std::wstring::npos) { - // ret = "/"; + // ret = L"/"; } else { std::wstring::size_type lastSlashPos = path.find_last_of(L"\\"); @@ -145,22 +145,33 @@ bool Utility::isFile(const std::string& name) { std::string Utility::toString(const std::wstring& ws) { std::string ret; - // convert to w_string using locale: see Phillipp on - std::setlocale(LC_ALL, ""); - const std::locale locale(""); - typedef std::codecvt converter_type; - const converter_type& converter = std::use_facet(locale); - std::vector to(ws.length() * converter.max_length()); - std::mbstate_t state; - const wchar_t* from_next; - char* to_next; - const converter_type::result result = converter.out(state, ws.data(), ws.data() + ws.length(), from_next, &to[0], &to[0] + to.size(), to_next); - if (result == converter_type::ok or result == converter_type::noconv) { - const std::string s(&to[0], to_next); - //std::cout <<"std::string = "< 0) { + // std::string str = "Hello"; + ret = std::string(ws.length(), L' '); // Make room for characters + // Copy string to wstring. + std::copy(ws.begin(), ws.end(), ret.begin()); } return ret; + + //below sometimes results in "internal_utf8_loop_single: Assertion `inptr - bytebuf > (state->__count & 7)' failed." on the converter.out call: +// if (ws.length() > 0) { +// // convert to w_string using locale: see Phillipp on +// std::setlocale(LC_ALL, ""); +// const std::locale locale(""); +// typedef std::codecvt converter_type; +// const converter_type& converter = std::use_facet(locale); +// std::vector to(ws.length() * converter.max_length()); +// std::mbstate_t state; +// const wchar_t* from_next = nullptr; +// char* to_next = nullptr; +// const converter_type::result result = converter.out(state, ws.data(), ws.data() + ws.length(), from_next, &to[0], &to[0] + to.size(), to_next); +// if (result == converter_type::ok or result == converter_type::noconv) { +// const std::string s(&to[0], to_next); +// //std::cout <<"std::string = "< 0) { + // std::string str = "Hello"; + ret = std::wstring(str.length(), L' '); // Make room for characters + // Copy string to wstring. + std::copy(str.begin(), str.end(), ret.begin()); + } + return ret; +} + irr::f32 Utility::toF32(wstring val) { std::wstringstream ss(val); diff --git a/Utils.h b/Utils.h index 7ee7c89..ed6d024 100644 --- a/Utils.h +++ b/Utils.h @@ -23,6 +23,7 @@ public: static std::wstring toLower(const std::wstring &s); static std::wstring toWstring(irr::f32 val); static std::wstring toWstring(int val); + static std::wstring toWstring(const std::string &str); static irr::f32 toF32(std::wstring val); // compiler doesn't like template function when class is not a template--instantiate immediately // see http://processors.wiki.ti.com/index.php/C%2B%2B_Template_Instantiation_Issues diff --git a/build/install.sh b/build/install.sh index 7940a0f..003fb03 100755 --- a/build/install.sh +++ b/build/install.sh @@ -54,7 +54,7 @@ if [ ! -f "$try_dest_bin" ]; then echo "WARNING: can't write to $prev_dir, so" fi if [ "@$PROFILE_ENABLE" = "@true" ]; then - dest_bin_dir="$USER/.local/bin" + dest_bin_dir="$HOME/.local/bin" echo "installing to '$dest_bin_dir'." echo "Press Ctrl C to cancel..." sleep 1 diff --git a/build/owner/.local/share/applications/org.poikilos.b3view.desktop b/build/owner/.local/share/applications/org.poikilos.b3view.desktop deleted file mode 100644 index 7546bea..0000000 --- a/build/owner/.local/share/applications/org.poikilos.b3view.desktop +++ /dev/null @@ -1,20 +0,0 @@ -[Desktop Entry] -Comment[en_US]= -Comment= -GenericName[en_US]=Irrlicht Model Viewer -GenericName=Irrlicht Model Viewer -MimeType= -Name[en_US]=b3view -Name=b3view -Path= -StartupNotify=true -Terminal=false -TerminalOptions= -Type=Application -X-DBUS-ServiceName= -X-DBUS-StartupType= -X-KDE-SubstituteUID=false -X-KDE-Username= -Exec=owner/.local/bin/b3view -Icon=owner/.local/share/icons/b3view.png - diff --git a/build/owner/.local/share/icons/b3view.png b/build/owner/.local/share/icons/b3view.png deleted file mode 100644 index fb1039f..0000000 Binary files a/build/owner/.local/share/icons/b3view.png and /dev/null differ diff --git a/install.sh b/install.sh index 73b1ba8..8e91f2b 100755 --- a/install.sh +++ b/install.sh @@ -1,4 +1,15 @@ #!/bin/sh -echo "Use the install.sh in build instead. Switching to build/install.sh..." && cd build || echo "ERROR: No build directory" && exit 1 + +customDie() { + echo + echo "ERROR:" + echo "$1" + echo + echo + exit 1 +} + +echo "Use the install.sh in build instead. Switching to build/install.sh..." +cd build || customDie "ERROR: No build directory" chmod +x install.sh ./install.sh