mirror of
https://github.com/Poikilos/b3view.git
synced 2023-10-03 07:58:48 -07:00
Auto-load nearby texture. Simplify&test cycling functions.
This commit is contained in:
parent
e5a8ed0204
commit
460690fded
209
CHANGELOG.md
209
CHANGELOG.md
@ -1,170 +1,225 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
|
|
||||||
|
|
||||||
|
## [git] - 2019-07-03
|
||||||
|
(poikilos)
|
||||||
|
### Added
|
||||||
|
- `replaceAll`
|
||||||
|
- `TestUtility` ("Utility.cpp" now tests itself, but only one feature
|
||||||
|
so far.)
|
||||||
|
- `getTextureCount` (can examine node by examining all materials, or
|
||||||
|
examine material
|
||||||
|
- Blitz3D format notes
|
||||||
|
- `OnSelectMesh` (cleans up model-specific variables)
|
||||||
|
- `getSuffix`
|
||||||
|
- `getPrefix`
|
||||||
|
- `startsWithAny`
|
||||||
|
- `endsWithAny`
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Rename some member variables to start with `m_`.
|
||||||
|
- Detect textures better, and simplify code:
|
||||||
|
- Search for substring and substring without underscores within
|
||||||
|
potential textures.
|
||||||
|
- Detect as soon as model is loaded if the model has no textures.
|
||||||
|
- Cache both the full and matching texture lists.
|
||||||
|
- If there are any matching textures (named like model), only use that
|
||||||
|
list for cycling with F3 (or Shift F3).
|
||||||
|
- Combine `m_NextPath` and `m_PreviousPath` into `m_LoadedMeshPath`
|
||||||
|
and change usage.
|
||||||
|
- Update screenshot.
|
||||||
|
- Reduce line length in some places.
|
||||||
|
- Improve Changelog formatting.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Fix crash trying to load a non-mesh after a mesh was loaded
|
||||||
|
(See "Manipulating mesh on failed load" section of README.md).
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
- `m_NextPath` and `m_PreviousPath` (replaced by `m_LoadedMeshPath` and
|
||||||
|
simplified cycling code)
|
||||||
|
|
||||||
|
|
||||||
## [git] - 2019-07-03
|
## [git] - 2019-07-03
|
||||||
(poikilos)
|
(poikilos)
|
||||||
### Changes
|
### Changes
|
||||||
* Move display mode booleans to engine.
|
- Move the display mode booleans to Engine.
|
||||||
* Add more string utilities.
|
- Add more string utilities.
|
||||||
* Do fuzzy search against actual texture names if can't find theoretical
|
- Do fuzzy search against actual texture names if can't find theoretical
|
||||||
ones.
|
ones.
|
||||||
|
|
||||||
|
|
||||||
## [git] - 2019-05-16
|
## [git] - 2019-05-16
|
||||||
(poikilos)
|
(poikilos)
|
||||||
### Added
|
### Added
|
||||||
* playback menu
|
- playback menu
|
||||||
- Move framerate controls to playback menu.
|
- Move framerate controls to playback menu.
|
||||||
* fix frame-by-frame hotkeys
|
- fix frame-by-frame hotkeys
|
||||||
- move code to new incrementFrame method
|
- move code to new incrementFrame method
|
||||||
|
|
||||||
# Changelog
|
|
||||||
## [git] - 2019-05-16
|
## [git] - 2019-05-16
|
||||||
(poikilos)
|
(poikilos)
|
||||||
### Changed
|
### Changed
|
||||||
* improve minetest texture detection (alternate conventions)
|
- improve minetest texture detection (alternate conventions)
|
||||||
* turn off interpolation if loadNextTexture detects minetest directory
|
- turn off interpolation if loadNextTexture detects minetest directory
|
||||||
structure (../textures/<texture filename based on model name>)
|
structure (../textures/<texture filename based on model name>)
|
||||||
|
|
||||||
|
|
||||||
## [git] - 2019-05-16
|
## [git] - 2019-05-16
|
||||||
(poikilos)
|
(poikilos)
|
||||||
### Added
|
### Added
|
||||||
* export COLLADA (non-Blender), IRR, IRRMESH, OBJ, STL
|
- export COLLADA (non-Blender), IRR, IRRMESH, OBJ, STL
|
||||||
* show dialog box if operation can't be performed
|
- show dialog box if operation can't be performed
|
||||||
- improve error reporting in called methods
|
- improve error reporting in called methods
|
||||||
* add irr mimetype (Irrlicht Scene, mesh file references and settings
|
- add irr mimetype (Irrlicht Scene, mesh file references and settings
|
||||||
only)
|
only)
|
||||||
* add irrlicht mimetype (static/non-animated Irrlicht mesh)
|
- add irrlicht mimetype (static/non-animated Irrlicht mesh)
|
||||||
|
|
||||||
|
|
||||||
## [git] - 2019-04-19
|
## [git] - 2019-04-19
|
||||||
(poikilos)
|
(poikilos)
|
||||||
### Added
|
### Added
|
||||||
* box for axis length (size of the axis widget)
|
- box for axis length (size of the axis widget)
|
||||||
* box for frame rate
|
- box for frame rate
|
||||||
* camera target widget
|
- camera target widget
|
||||||
* option for turning off origin axis widget
|
- option for turning off origin axis widget
|
||||||
* Add menu items for hotkeys, and show hotkey on relevant menu items.
|
- Add menu items for hotkeys, and show hotkey on relevant menu items.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
* Reorder items on panel.
|
- Reorder items on panel.
|
||||||
* Hotkeys are different so they're not triggered when typing in the
|
- Hotkeys are different so they're not triggered when typing in the
|
||||||
panel.
|
panel.
|
||||||
* Don't reset yaw nor camera distance when panning.
|
- Don't reset yaw nor camera distance when panning.
|
||||||
* Show name of loaded model on title bar.
|
- Show name of loaded model on title bar.
|
||||||
* Fix crash on loading texture before model.
|
- Fix crash on loading texture before model.
|
||||||
* Fix use of unsigned frame delta for slow and fast options.
|
- Fix use of unsigned frame delta for slow and fast options.
|
||||||
|
|
||||||
|
|
||||||
## [git] - 2019-04-08
|
## [git] - 2019-04-08
|
||||||
(poikilos)
|
(poikilos)
|
||||||
### Added
|
### Added
|
||||||
* snapWidgets (move playbackWindow on resize, not leave past edge)
|
- snapWidgets (move playbackWindow on resize, not leave past edge)
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
* changed enum values to leave room in between, comment unused
|
- changed enum values to leave room in between, comment unused
|
||||||
* fixed issue in Utility not detecting backslashes correctly
|
- fixed issue in Utility not detecting backslashes correctly
|
||||||
* renamed Utils.* to Utility.* to match class name
|
- renamed Utils.* to Utility.* to match class name
|
||||||
* coding style to WebKit (run ./etc/quality.sh to check)
|
- coding style to WebKit (run ./etc/quality.sh to check)
|
||||||
* improve pan - don't reset view
|
- improve pan - don't reset view
|
||||||
* improve initial camera settings: angle calculation
|
- improve initial camera settings: angle calculation
|
||||||
|
|
||||||
|
|
||||||
## [git] - 2019-04-08
|
## [git] - 2019-04-08
|
||||||
(poikilos)
|
(poikilos)
|
||||||
### Added
|
### Added
|
||||||
* toggle texture interpolation (via checkbox and `x` hotkey)
|
- toggle texture interpolation (via checkbox and `x` hotkey)
|
||||||
* INDEX_ variables to store ID of GUI elements
|
- INDEX_ variables to store ID of GUI elements
|
||||||
* Text box show name of loaded texture path
|
- Text box show name of loaded texture path
|
||||||
### Changed
|
### Changed
|
||||||
* check if model is loaded before changing view options (prevents crash)
|
- check if model is loaded before changing view options (prevents crash)
|
||||||
* unified checkboxes with m_* booleans, by tracking whether box is
|
- unified checkboxes with m_* booleans, by tracking whether box is
|
||||||
checked via INDEX_ variables for each ID of GUI elements.
|
checked via INDEX_ variables for each ID of GUI elements.
|
||||||
* look for ../textures/<model basename>.png & .jpg 1st time pressing `t`
|
- look for ../textures/<model basename>.png & .jpg 1st time pressing `t`
|
||||||
* Use alpha on textures by default
|
- Use alpha on textures by default
|
||||||
(see EMT_TRANSPARENT_ALPHA_CHANNEL_REF in Engine.cpp)
|
(see EMT_TRANSPARENT_ALPHA_CHANNEL_REF in Engine.cpp)
|
||||||
|
|
||||||
## [git] - 2019-03-09
|
|
||||||
(poikilos)
|
|
||||||
### Added
|
|
||||||
* completed rotation controls (Blender-like)
|
|
||||||
* pan up and down (Blender-like, but only up and down)
|
|
||||||
* Z or Y to switch ("up" axis)
|
|
||||||
* change up axis to Z when 3ds is loaded
|
|
||||||
* model-ms3d.xml mime type file
|
|
||||||
|
|
||||||
## [git] - 2019-03-09
|
## [git] - 2019-03-09
|
||||||
(poikilos)
|
(poikilos)
|
||||||
### Added
|
### Added
|
||||||
* hotkeys to reload model/texture
|
- completed rotation controls (Blender-like)
|
||||||
* license (see README.md for licensing history)
|
- pan up and down (Blender-like, but only up and down)
|
||||||
|
- Z or Y to switch ("up" axis)
|
||||||
|
- change up axis to Z when 3ds is loaded
|
||||||
|
- model-ms3d.xml mime type file
|
||||||
|
|
||||||
|
|
||||||
|
## [git] - 2019-03-09
|
||||||
|
(poikilos)
|
||||||
|
### Added
|
||||||
|
- hotkeys to reload model/texture
|
||||||
|
- license (see README.md for licensing history)
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
* only try to load png or jpg textures--skip others when cycling
|
- only try to load png or jpg textures--skip others when cycling
|
||||||
* cycle backwards correctly
|
- cycle backwards correctly
|
||||||
* fix some of the header creep (remove unecessary includes in h files)
|
- fix some of the header creep (remove unecessary includes in h files)
|
||||||
* improve initial camera position and angle (see top of characters since
|
- improve initial camera position and angle (see top of characters since
|
||||||
camera is higher; z-forward characters face camera at an angle)
|
camera is higher; z-forward characters face camera at an angle)
|
||||||
* Clarify relationship between camera start position in m_Engine and
|
- Clarify relationship between camera start position in m_Engine and
|
||||||
m_View's rotation (m_Pitch and m_Yaw). Now, `setNewCameraPosition`
|
m_View's rotation (m_Pitch and m_Yaw). Now, `setNewCameraPosition`
|
||||||
operates on view correctly (relatively) no matter where camera starts.
|
operates on view correctly (relatively) no matter where camera starts.
|
||||||
|
|
||||||
|
|
||||||
## [git] - 2019-03-07
|
## [git] - 2019-03-07
|
||||||
(poikilos)
|
(poikilos)
|
||||||
### Added
|
### Added
|
||||||
* playback controls
|
- playback controls
|
||||||
|
|
||||||
|
|
||||||
## [git] - 2019-03-06
|
## [git] - 2019-03-06
|
||||||
(poikilos)
|
(poikilos)
|
||||||
### Added
|
### Added
|
||||||
* created install.sh and install.bat, and added Install and Usage
|
- created install.sh and install.bat, and added Install and Usage
|
||||||
to README.md
|
to README.md
|
||||||
* icon, install scripts, and mime type (`model/b3d`)--see README.md
|
- icon, install scripts, and mime type (`model/b3d`)--see README.md
|
||||||
* mime type (`model/x`)
|
- mime type (`model/x`)
|
||||||
* added ClearSansRegular.ttf
|
- added ClearSansRegular.ttf
|
||||||
* hotkeys to cycle ../textures/*
|
- hotkeys to cycle ../textures/*
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
* The program can now start without "test.b3d" in the current working
|
- The program can now start without "test.b3d" in the current working
|
||||||
directory (fixed Segmentation Fault).
|
directory (fixed Segmentation Fault).
|
||||||
* set `TARGET = b3view` in B3View.pro, so that binary is lowercase as
|
- set `TARGET = b3view` in B3View.pro, so that binary is lowercase as
|
||||||
per usual Linux naming conventions.
|
per usual Linux naming conventions.
|
||||||
* check for font load failure properly, and load properly if succeeds
|
- check for font load failure properly, and load properly if succeeds
|
||||||
* check for "ClearSansRegular.ttf" instead of "arial.ttf"
|
- check for "ClearSansRegular.ttf" instead of "arial.ttf"
|
||||||
* move `using namespace` directives from `h` files and specify upon use,
|
- move `using namespace` directives from `h` files and specify upon use,
|
||||||
as per C++ best practices; add directives to `cpp` files only as
|
as per C++ best practices; add directives to `cpp` files only as
|
||||||
needed (removed cumulative namespace creep).
|
needed (removed cumulative namespace creep).
|
||||||
|
|
||||||
|
|
||||||
## [git-94e3b8f] - 2019-03-06
|
## [git-94e3b8f] - 2019-03-06
|
||||||
(poikilos)
|
(poikilos)
|
||||||
### Added
|
### Added
|
||||||
* README.md
|
- README.md
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
(CGUITTFont methods are in CGUITTFont class unless otherwise specified)
|
(CGUITTFont methods are in CGUITTFont class unless otherwise specified)
|
||||||
* fixed instances of "0 as null pointer constant" (changed to `nullptr`)
|
- fixed instances of "0 as null pointer constant" (changed to `nullptr`)
|
||||||
* changed inconsistent use of spaces and tabs (changed tabs to 4 spaces)
|
- changed inconsistent use of spaces and tabs (changed tabs to 4 spaces)
|
||||||
* (UserInterface.cpp) fixed "logical not is only applied to the left
|
- (UserInterface.cpp) fixed "logical not is only applied to the left
|
||||||
hand side of this comparison..." (put parenthesis around
|
hand side of this comparison..." (put parenthesis around
|
||||||
`event.EventType == EET_GUI_EVENT`)
|
`event.EventType == EET_GUI_EVENT`)
|
||||||
* Silently degrade to pixel font if font file cannot be read (fixes
|
- Silently degrade to pixel font if font file cannot be read (fixes
|
||||||
Segmentation Fault when font file cannot be read).
|
Segmentation Fault when font file cannot be read).
|
||||||
* check for nullptr before using:
|
- check for nullptr before using:
|
||||||
* (CGUITTFont.cpp) `tt_face->face` in `getWidthFromCharacter`,
|
- (CGUITTFont.cpp) `tt_face->face` in `getWidthFromCharacter`,
|
||||||
`getGlyphByChar` (return 0 as bad as per convention:
|
`getGlyphByChar` (return 0 as bad as per convention:
|
||||||
existing code already checks for 0--see
|
existing code already checks for 0--see
|
||||||
`getWidthFromCharacter`), `getKerningWidth`,
|
`getWidthFromCharacter`), `getKerningWidth`,
|
||||||
`draw`, `attach` (also don't copy null by
|
`draw`, `attach` (also don't copy null by
|
||||||
reference there--instead, set to nullptr if source is nullptr)
|
reference there--instead, set to nullptr if source is nullptr)
|
||||||
* check length of array before using
|
- check length of array before using
|
||||||
* (CGUITTFont.cpp) elements of `Glyph` array (type
|
- (CGUITTFont.cpp) elements of `Glyph` array (type
|
||||||
`core::array<CGUITTGlyph>`) in `getHeightFromCharacter`
|
`core::array<CGUITTGlyph>`) in `getHeightFromCharacter`
|
||||||
* (CGUITTFont.cpp) check whether file can be read in
|
- (CGUITTFont.cpp) check whether file can be read in
|
||||||
`CGUITTFace::load` before proceeding
|
`CGUITTFace::load` before proceeding
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
* arial.tff removed, since it may be the "real" Arial font, which has a
|
- arial.tff removed, since it may be the "real" Arial font, which has a
|
||||||
proprietary license
|
proprietary license
|
||||||
|
|
||||||
|
|
||||||
## [git-d964384] - 2019-03-06
|
## [git-d964384] - 2019-03-06
|
||||||
### Changed
|
### Changed
|
||||||
(first poikilos commit, based on https://github.com/egrath)
|
(first poikilos commit, based on https://github.com/egrath)
|
||||||
* changed `#include <irrlicht.h>` to `#include <irrlicht/irrlicht.h>`
|
- changed `#include <irrlicht.h>` to `#include <irrlicht/irrlicht.h>`
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
* .gitignore (a [Qt .gitignore](https://github.com/github/gitignore/blob/master/Qt.gitignore))
|
- .gitignore (a [Qt .gitignore](https://github.com/github/gitignore/blob/master/Qt.gitignore))
|
||||||
* CHANGELOG.md
|
- CHANGELOG.md
|
||||||
|
108
Engine.cpp
108
Engine.cpp
@ -89,8 +89,10 @@ void Engine::setupScene()
|
|||||||
// further down.
|
// further down.
|
||||||
ICameraSceneNode* camera = m_Scene->addCameraSceneNode(nullptr, m_CamPos,
|
ICameraSceneNode* camera = m_Scene->addCameraSceneNode(nullptr, m_CamPos,
|
||||||
m_CamTarget);
|
m_CamTarget);
|
||||||
camera->setAspectRatio(static_cast<f32>(m_Driver->getScreenSize().Width)
|
camera->setAspectRatio(
|
||||||
/ static_cast<f32>(m_Driver->getScreenSize().Height));
|
static_cast<f32>(m_Driver->getScreenSize().Width)
|
||||||
|
/ static_cast<f32>(m_Driver->getScreenSize().Height)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
IGUIEnvironment* Engine::getGUIEnvironment() const
|
IGUIEnvironment* Engine::getGUIEnvironment() const
|
||||||
@ -189,10 +191,10 @@ void Engine::drawAxisLines()
|
|||||||
|
|
||||||
if (enableAxisWidget) {
|
if (enableAxisWidget) {
|
||||||
m_Driver->setMaterial(xMaterial);
|
m_Driver->setMaterial(xMaterial);
|
||||||
m_Driver->draw3DLine(vector3df(), vector3df(axisLength, 0, 0),
|
m_Driver->draw3DLine(vector3df(), vector3df(m_AxisLength, 0, 0),
|
||||||
SColor(255, 255, 0, 0));
|
SColor(255, 255, 0, 0));
|
||||||
position2d<s32> textPos = m_Scene->getSceneCollisionManager()->getScreenCoordinatesFrom3DPosition(
|
position2d<s32> textPos = m_Scene->getSceneCollisionManager()->getScreenCoordinatesFrom3DPosition(
|
||||||
vector3df(axisLength + axisLength*.1f, 0, 0)
|
vector3df(m_AxisLength + m_AxisLength*.1f, 0, 0)
|
||||||
);
|
);
|
||||||
dimension2d<u32> textSize;
|
dimension2d<u32> textSize;
|
||||||
if (m_AxisFont != nullptr) {
|
if (m_AxisFont != nullptr) {
|
||||||
@ -202,10 +204,10 @@ void Engine::drawAxisLines()
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_Driver->setMaterial(yMaterial);
|
m_Driver->setMaterial(yMaterial);
|
||||||
m_Driver->draw3DLine(vector3df(), vector3df(0, axisLength, 0),
|
m_Driver->draw3DLine(vector3df(), vector3df(0, m_AxisLength, 0),
|
||||||
SColor(255, 0, 255, 0));
|
SColor(255, 0, 255, 0));
|
||||||
textPos = m_Scene->getSceneCollisionManager()->getScreenCoordinatesFrom3DPosition(
|
textPos = m_Scene->getSceneCollisionManager()->getScreenCoordinatesFrom3DPosition(
|
||||||
vector3df(0, axisLength + axisLength*.1f, 0)
|
vector3df(0, m_AxisLength + m_AxisLength*.1f, 0)
|
||||||
);
|
);
|
||||||
if (m_AxisFont != nullptr) {
|
if (m_AxisFont != nullptr) {
|
||||||
textSize = m_AxisFont->getDimension(L"Y+");
|
textSize = m_AxisFont->getDimension(L"Y+");
|
||||||
@ -214,10 +216,10 @@ void Engine::drawAxisLines()
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_Driver->setMaterial(zMaterial);
|
m_Driver->setMaterial(zMaterial);
|
||||||
m_Driver->draw3DLine(vector3df(), vector3df(0, 0, axisLength),
|
m_Driver->draw3DLine(vector3df(), vector3df(0, 0, m_AxisLength),
|
||||||
SColor(255, 0, 0, 255));
|
SColor(255, 0, 0, 255));
|
||||||
textPos = m_Scene->getSceneCollisionManager()->getScreenCoordinatesFrom3DPosition(
|
textPos = m_Scene->getSceneCollisionManager()->getScreenCoordinatesFrom3DPosition(
|
||||||
vector3df(0, 0, axisLength + axisLength*.1f)
|
vector3df(0, 0, m_AxisLength + m_AxisLength*.1f)
|
||||||
);
|
);
|
||||||
if (m_AxisFont != nullptr) {
|
if (m_AxisFont != nullptr) {
|
||||||
textSize = m_AxisFont->getDimension(L"Z+");
|
textSize = m_AxisFont->getDimension(L"Z+");
|
||||||
@ -288,12 +290,12 @@ Engine::Engine()
|
|||||||
this->m_EnableWireframe = false;
|
this->m_EnableWireframe = false;
|
||||||
this->m_EnableLighting = false;
|
this->m_EnableLighting = false;
|
||||||
this->m_EnableTextureInterpolation = true;
|
this->m_EnableTextureInterpolation = true;
|
||||||
this->axisLength = 10;
|
this->m_AxisLength = 10;
|
||||||
this->worldFPS = 60;
|
this->m_WorldFPS = 60;
|
||||||
this->prevFPS = 30;
|
this->m_PrevFPS = 30;
|
||||||
this->textureExtensions.push_back(L"png");
|
this->m_TextureExtensions.push_back(L"png");
|
||||||
this->textureExtensions.push_back(L"jpg");
|
this->m_TextureExtensions.push_back(L"jpg");
|
||||||
this->textureExtensions.push_back(L"bmp");
|
this->m_TextureExtensions.push_back(L"bmp");
|
||||||
#if WIN32
|
#if WIN32
|
||||||
m_Device = createDevice(EDT_DIRECT3D9, dimension2d<u32>(1024, 768), 32,
|
m_Device = createDevice(EDT_DIRECT3D9, dimension2d<u32>(1024, 768), 32,
|
||||||
false, false, false, nullptr);
|
false, false, false, nullptr);
|
||||||
@ -364,23 +366,28 @@ vector3df Engine::camTarget()
|
|||||||
bool Engine::loadMesh(const wstring& fileName)
|
bool Engine::loadMesh(const wstring& fileName)
|
||||||
{
|
{
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
this->m_PreviousPath = fileName; // even if bad, set this
|
|
||||||
// to allow F5 to reload
|
|
||||||
|
|
||||||
if (m_LoadedMesh != nullptr)
|
|
||||||
m_LoadedMesh->remove();
|
|
||||||
|
|
||||||
irr::scene::IAnimatedMesh* mesh = m_Scene->getMesh(fileName.c_str());
|
irr::scene::IAnimatedMesh* mesh = m_Scene->getMesh(fileName.c_str());
|
||||||
if (mesh != nullptr) {
|
if (mesh != nullptr) {
|
||||||
|
this->m_LoadedTexturePath = L"";
|
||||||
|
this->m_LoadedMeshPath = fileName; // even if bad, set this
|
||||||
|
// to allow F5 to reload
|
||||||
|
|
||||||
|
if (m_LoadedMesh != nullptr)
|
||||||
|
m_LoadedMesh->remove();
|
||||||
|
this->m_LoadedMesh = nullptr;
|
||||||
|
|
||||||
m_Device->setWindowCaption((wstring(L"b3view - ") + fileName).c_str());
|
m_Device->setWindowCaption((wstring(L"b3view - ") + fileName).c_str());
|
||||||
m_LoadedMesh = m_Scene->addAnimatedMeshSceneNode(mesh);
|
m_LoadedMesh = m_Scene->addAnimatedMeshSceneNode(mesh);
|
||||||
Utility::dumpMeshInfoToConsole(m_LoadedMesh);
|
Utility::dumpMeshInfoToConsole(m_LoadedMesh);
|
||||||
|
std::cerr << "Arranging scene..." << std::flush;
|
||||||
if (Utility::toLower(Utility::extensionOf(fileName)) == L"3ds") {
|
if (Utility::toLower(Utility::extensionOf(fileName)) == L"3ds") {
|
||||||
m_View->setZUp(true);
|
m_View->setZUp(true);
|
||||||
} else {
|
} else {
|
||||||
m_View->setZUp(false);
|
m_View->setZUp(false);
|
||||||
}
|
}
|
||||||
if (m_LoadedMesh != nullptr) {
|
if (m_LoadedMesh != nullptr) {
|
||||||
|
std::cerr << "unloading old mesh..." << std::flush;
|
||||||
ret = true;
|
ret = true;
|
||||||
this->m_UserInterface->playbackFPSEditBox->setText(
|
this->m_UserInterface->playbackFPSEditBox->setText(
|
||||||
Utility::toWstring(m_LoadedMesh->getAnimationSpeed()).c_str()
|
Utility::toWstring(m_LoadedMesh->getAnimationSpeed()).c_str()
|
||||||
@ -431,19 +438,38 @@ bool Engine::loadMesh(const wstring& fileName)
|
|||||||
video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF
|
video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF
|
||||||
);
|
);
|
||||||
// EMT_TRANSPARENT_ALPHA_CHANNEL: constant transparency
|
// EMT_TRANSPARENT_ALPHA_CHANNEL: constant transparency
|
||||||
|
|
||||||
}
|
}
|
||||||
|
std::cerr << "setting display mode..." << std::flush;
|
||||||
|
this->setMeshDisplayMode(this->m_EnableWireframe, this->m_EnableLighting,
|
||||||
|
this->m_EnableTextureInterpolation);
|
||||||
|
std::cerr << "preparing UI..." << std::flush;
|
||||||
|
if (this->m_UserInterface != nullptr)
|
||||||
|
this->m_UserInterface->OnSelectMesh();
|
||||||
|
std::cerr << "checking for textures..." << std::flush;
|
||||||
|
std::cerr << "OK" << std::endl;
|
||||||
|
if (Utility::getTextureCount(m_LoadedMesh) == 0) {
|
||||||
|
// NOTE: getMaterialCount doesn't work, since there may not
|
||||||
|
// be loaded textures in any material.
|
||||||
|
if (this->m_UserInterface != nullptr) {
|
||||||
|
this->m_UserInterface->loadNextTexture(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
this->setMeshDisplayMode(this->m_EnableWireframe, this->m_EnableLighting,
|
// Don't do anything outside of the mesh != nullptr case that will try to
|
||||||
this->m_EnableTextureInterpolation);
|
// use mesh!
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Engine::reloadMesh()
|
bool Engine::reloadMesh()
|
||||||
{
|
{
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
if (this->m_PreviousPath.length() > 0) {
|
if (this->m_LoadedMeshPath.length() > 0) {
|
||||||
ret = loadMesh(this->m_PreviousPath);
|
ret = loadMesh(this->m_LoadedMeshPath);
|
||||||
}
|
}
|
||||||
|
if (this->m_UserInterface != nullptr)
|
||||||
|
this->m_UserInterface->OnSelectMesh();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -547,11 +573,11 @@ std::wstring Engine::saveMesh(const io::path path, const std::string& nameOrBlan
|
|||||||
|
|
||||||
void Engine::reloadTexture()
|
void Engine::reloadTexture()
|
||||||
{
|
{
|
||||||
if (this->m_PrevTexturePath.length() > 0) {
|
if (this->m_LoadedTexturePath.length() > 0) {
|
||||||
if (wcslen(this->m_UserInterface->texturePathEditBox->getText()) == 0)
|
if (wcslen(this->m_UserInterface->texturePathEditBox->getText()) == 0)
|
||||||
loadTexture(this->m_UserInterface->texturePathEditBox->getText());
|
loadTexture(this->m_UserInterface->texturePathEditBox->getText());
|
||||||
else
|
else
|
||||||
loadTexture(this->m_PrevTexturePath);
|
loadTexture(this->m_LoadedTexturePath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -564,11 +590,15 @@ bool Engine::loadTexture(const wstring& fileName)
|
|||||||
m_LoadedMesh->setMaterialTexture(0, texture);
|
m_LoadedMesh->setMaterialTexture(0, texture);
|
||||||
ret = true;
|
ret = true;
|
||||||
}
|
}
|
||||||
this->m_PrevTexturePath = fileName;
|
this->m_LoadedTexturePath = fileName;
|
||||||
|
std::cerr << "Setting texture path box to " << Utility::toString(this->m_LoadedTexturePath) << std::endl;
|
||||||
this->m_UserInterface->texturePathEditBox->setText(
|
this->m_UserInterface->texturePathEditBox->setText(
|
||||||
this->m_PrevTexturePath.c_str()
|
this->m_LoadedTexturePath.c_str()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
std::cerr << "NOT Setting texture path box to " << Utility::toString(this->m_LoadedTexturePath) << std::endl;
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -638,7 +668,7 @@ void Engine::setMeshDisplayMode(bool wireframe, bool lighting,
|
|||||||
|
|
||||||
bool Engine::isAnimating()
|
bool Engine::isAnimating()
|
||||||
{
|
{
|
||||||
return this->isPlaying;
|
return this->m_IsPlaying;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Engine::playAnimation()
|
void Engine::playAnimation()
|
||||||
@ -648,24 +678,24 @@ void Engine::playAnimation()
|
|||||||
}
|
}
|
||||||
if (!this->isAnimating()) {
|
if (!this->isAnimating()) {
|
||||||
if (this->m_LoadedMesh != nullptr) {
|
if (this->m_LoadedMesh != nullptr) {
|
||||||
if (this->prevFPS < 1)
|
if (this->m_PrevFPS < 1)
|
||||||
this->prevFPS = 5;
|
this->m_PrevFPS = 5;
|
||||||
this->m_LoadedMesh->setAnimationSpeed(this->prevFPS);
|
this->m_LoadedMesh->setAnimationSpeed(this->m_PrevFPS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this->isPlaying = true;
|
this->m_IsPlaying = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Engine::pauseAnimation()
|
void Engine::pauseAnimation()
|
||||||
{
|
{
|
||||||
if (this->isAnimating()) {
|
if (this->isAnimating()) {
|
||||||
this->prevFPS = animationFPS();
|
this->m_PrevFPS = animationFPS();
|
||||||
if (this->m_LoadedMesh != nullptr) {
|
if (this->m_LoadedMesh != nullptr) {
|
||||||
this->prevFPS = this->m_LoadedMesh->getAnimationSpeed();
|
this->m_PrevFPS = this->m_LoadedMesh->getAnimationSpeed();
|
||||||
this->m_LoadedMesh->setAnimationSpeed(0);
|
this->m_LoadedMesh->setAnimationSpeed(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this->isPlaying = false;
|
this->m_IsPlaying = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Engine::toggleAnimation()
|
void Engine::toggleAnimation()
|
||||||
@ -682,7 +712,7 @@ void Engine::toggleAnimation()
|
|||||||
void Engine::setAnimationFPS(u32 animationFPS)
|
void Engine::setAnimationFPS(u32 animationFPS)
|
||||||
{
|
{
|
||||||
if (this->m_LoadedMesh != nullptr) {
|
if (this->m_LoadedMesh != nullptr) {
|
||||||
if (animationFPS > 0) this->isPlaying = true;
|
if (animationFPS > 0) this->m_IsPlaying = true;
|
||||||
// Do NOT call playAnimation, otherwise infinite recursion occurs
|
// Do NOT call playAnimation, otherwise infinite recursion occurs
|
||||||
// (it calls setAnimationFPS).
|
// (it calls setAnimationFPS).
|
||||||
this->m_LoadedMesh->setAnimationSpeed(animationFPS);
|
this->m_LoadedMesh->setAnimationSpeed(animationFPS);
|
||||||
@ -728,8 +758,8 @@ u32 Engine::animationFPS()
|
|||||||
void Engine::run()
|
void Engine::run()
|
||||||
{
|
{
|
||||||
u32 timePerFrame = 1000.0f;
|
u32 timePerFrame = 1000.0f;
|
||||||
if (this->worldFPS > 0) {
|
if (this->m_WorldFPS > 0) {
|
||||||
timePerFrame = static_cast<u32>(1000.0f / this->worldFPS);
|
timePerFrame = static_cast<u32>(1000.0f / this->m_WorldFPS);
|
||||||
}
|
}
|
||||||
ITimer* timer = m_Device->getTimer();
|
ITimer* timer = m_Device->getTimer();
|
||||||
|
|
||||||
@ -739,7 +769,7 @@ void Engine::run()
|
|||||||
|
|
||||||
checkResize();
|
checkResize();
|
||||||
if (this->m_LoadedMesh != nullptr) {
|
if (this->m_LoadedMesh != nullptr) {
|
||||||
if (isPlaying) {
|
if (m_IsPlaying) {
|
||||||
this->m_LoadedMesh->setLoopMode(true);
|
this->m_LoadedMesh->setLoopMode(true);
|
||||||
this->m_UserInterface->playbackSetFrameEditBox->setText(
|
this->m_UserInterface->playbackSetFrameEditBox->setText(
|
||||||
Utility::toWstring(this->m_LoadedMesh->getFrameNr()).c_str()
|
Utility::toWstring(this->m_LoadedMesh->getFrameNr()).c_str()
|
||||||
|
15
Engine.h
15
Engine.h
@ -25,7 +25,6 @@ class Engine {
|
|||||||
friend class View;
|
friend class View;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::wstring m_NextPath;
|
|
||||||
irr::IrrlichtDevice* m_Device;
|
irr::IrrlichtDevice* m_Device;
|
||||||
irr::video::IVideoDriver* m_Driver;
|
irr::video::IVideoDriver* m_Driver;
|
||||||
irr::scene::ISceneManager* m_Scene;
|
irr::scene::ISceneManager* m_Scene;
|
||||||
@ -51,10 +50,10 @@ private:
|
|||||||
void checkResize();
|
void checkResize();
|
||||||
irr::gui::IGUIEnvironment* getGUIEnvironment() const;
|
irr::gui::IGUIEnvironment* getGUIEnvironment() const;
|
||||||
irr::s32 getNumberOfVertices();
|
irr::s32 getNumberOfVertices();
|
||||||
bool isPlaying;
|
bool m_IsPlaying;
|
||||||
irr::u32 worldFPS;
|
irr::u32 m_WorldFPS;
|
||||||
irr::u32 prevFPS;
|
irr::u32 m_PrevFPS;
|
||||||
std::vector<std::wstring> textureExtensions;
|
std::vector<std::wstring> m_TextureExtensions;
|
||||||
// Making materials in contructor or setupScene causes segfault at
|
// Making materials in contructor or setupScene causes segfault at
|
||||||
// `m_Driver->setMaterial(*lineX);` in
|
// `m_Driver->setMaterial(*lineX);` in
|
||||||
// `Engine::drawAxisLines` for unknown reason:
|
// `Engine::drawAxisLines` for unknown reason:
|
||||||
@ -69,9 +68,9 @@ private:
|
|||||||
irr::s32 LMouseState, RMouseState;
|
irr::s32 LMouseState, RMouseState;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
std::wstring m_PreviousPath;
|
std::wstring m_LoadedMeshPath;
|
||||||
std::wstring m_PrevTexturePath;
|
std::wstring m_LoadedTexturePath;
|
||||||
irr::f32 axisLength;
|
irr::f32 m_AxisLength;
|
||||||
bool m_zUp;
|
bool m_zUp;
|
||||||
|
|
||||||
Engine();
|
Engine();
|
||||||
|
79
README.md
79
README.md
@ -9,6 +9,7 @@ bird: [github.com/poikilos/mobs_sky](https://github.com/poikilos/mobs_sky)
|
|||||||
|
|
||||||
Website: [poikilos.org](https://poikilos.org)
|
Website: [poikilos.org](https://poikilos.org)
|
||||||
|
|
||||||
|
|
||||||
## Main Features in poikilos fork
|
## 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)
|
makes sure model is loaded before setting View options)
|
||||||
@ -23,14 +24,46 @@ Website: [poikilos.org](https://poikilos.org)
|
|||||||
* export feature: COLLADA (non-Blender), IRR (Irrlicht Scene settings
|
* export feature: COLLADA (non-Blender), IRR (Irrlicht Scene settings
|
||||||
and mesh file paths only), IRRMESH (Static Irrlicht Mesh), OBJ
|
and mesh file paths only), IRRMESH (Static Irrlicht Mesh), OBJ
|
||||||
(Wavefront), STL (stereolithography)
|
(Wavefront), STL (stereolithography)
|
||||||
* Turn off interpolation if loadNextTexture (F3) detects minetest
|
* Turn off interpolation if loadNextTexture (F3) detects the following
|
||||||
directory structure
|
Minetest-like directory structure and texture naming:
|
||||||
(../textures/<texture filename based on model name>)
|
"<texture directory>/<texture filename based on model name>" where
|
||||||
|
"<texture directory>" is either `.` (same directory as model)
|
||||||
|
or `../textures` (where model would be in a parallel directory next to
|
||||||
|
textures).
|
||||||
|
|
||||||
## Related Projects:
|
|
||||||
|
## Related Software
|
||||||
- [https://github.com/stujones11/SAM-Viewer](SAM-Viewer): View a
|
- [https://github.com/stujones11/SAM-Viewer](SAM-Viewer): View a
|
||||||
minetest player model and see the effect of changing various wield
|
minetest player model and see the effect of changing various wield
|
||||||
settings that are available in the minetest Lua API.
|
settings that are available in the minetest Lua API.
|
||||||
|
- Blitz3d: Blitz3d was released
|
||||||
|
[on GitHub](https://github.com/blitz-research/blitz3d) under the
|
||||||
|
zlib/libpng license in 2014!
|
||||||
|
- Blitz3D plug-in for [Ultimate Unwrap
|
||||||
|
3D](https://www.unwrap3d.com/u3d/formats.aspx): Ultimate Unwrap 3D is
|
||||||
|
a standalone unwrapping tool ("UV Mapping Software").
|
||||||
|
- Milkshape can export B3D and import x without animations.
|
||||||
|
- TREEmagik Plus by AlienCodec (the original version is now
|
||||||
|
[free](http://www.aliencodec.com/Aliencodec%C2%A9+-+Software+Developers);
|
||||||
|
superceded by TREEmagik-G2) can export to b3d.
|
||||||
|
|
||||||
|
|
||||||
|
## B3D Format
|
||||||
|
B3D in this case is the Blitz3D model format.
|
||||||
|
- "stores model information in 'chunks;' may contain textures, brushes,
|
||||||
|
vertices, triangles, meshes, bones, or animation data."
|
||||||
|
-<https://fileinfo.com/extension/b3d>
|
||||||
|
|
||||||
|
### What it is not
|
||||||
|
The B3D format (Blitz3D format) supported by Irrlicht has nothing to do
|
||||||
|
with other formats which also have the B3D extension.
|
||||||
|
- It is not [.B3D - Maxon Bodypaint 3D texture
|
||||||
|
file](http://http.maxon.net/pub/bp2/docu/bodypaint3d_r2_reference_e.pdf),
|
||||||
|
an internal format that Cinema4D uses to store [multi-layer
|
||||||
|
textures](https://forums.creativecow.net/docs/forums/post.php?forumid=19&postid=236712&univpostid=236712&pview=t&archive=T).
|
||||||
|
- It is not ASCII
|
||||||
|
- not [.B3D - Ben's 3D Format](https://www.bcchang.com/research/vr/b3d.php)
|
||||||
|
|
||||||
|
|
||||||
## Compile
|
## Compile
|
||||||
(the original version of this section is from
|
(the original version of this section is from
|
||||||
@ -91,6 +124,7 @@ only applies to Visual Studio users.)
|
|||||||
gnu-free/FreeSansBold.ttf, dejavu/DejaVuSans-Bold.ttf,
|
gnu-free/FreeSansBold.ttf, dejavu/DejaVuSans-Bold.ttf,
|
||||||
google-droid/DroidSans-Bold.ttf
|
google-droid/DroidSans-Bold.ttf
|
||||||
|
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
### Windows
|
### Windows
|
||||||
* If you are not using a release version, compile the program (see
|
* If you are not using a release version, compile the program (see
|
||||||
@ -135,14 +169,17 @@ only applies to Visual Studio users.)
|
|||||||
animation runs as 30 fps (Irrlicht does interpolation automatically).
|
animation runs as 30 fps (Irrlicht does interpolation automatically).
|
||||||
- Edit the frame rate manually using the input box under "Faster" and
|
- Edit the frame rate manually using the input box under "Faster" and
|
||||||
"Slower."
|
"Slower."
|
||||||
* `F3` / `Shift F3`: Cycle through textures in `../textures` using `F3`
|
* `F3` / `Shift F3`: Cycle through textures where the filename contains
|
||||||
key (`Shift` to go backward) such as for Minetest mods, where model
|
the model filename (or that without underscores) in the current
|
||||||
must be in `modname/models/` and texture must be in
|
directory or `../textures`. If there are no matches, use a list of
|
||||||
`modname/textures/`.
|
all found textures. The `F3` key goes to the next texture file (hold
|
||||||
- If `"../textures/" + basename(modelName) + ".png"` or `".jpg"` is
|
`Shift` and press`F3` to go backward), but does nothing on the first
|
||||||
present, pressing `F3` for the first time will load it.
|
press if the model had loaded its own texture.
|
||||||
- If `../textures` doesn't exist relative to the model file's
|
- Example: Both automatic loading (when you open a mesh) and manually
|
||||||
directory, the model file's own directory will be used.
|
cycling using F3 works for Minetest mods, where the model should be
|
||||||
|
in `modname/models/` and the texture should be in
|
||||||
|
`modname/textures/` (but occasionally is in the same directory as
|
||||||
|
the model).
|
||||||
* `Ctrl i`: toggle texture interpolation (shortcut for View, Texture
|
* `Ctrl i`: toggle texture interpolation (shortcut for View, Texture
|
||||||
Interpolation)
|
Interpolation)
|
||||||
* `F5`: Reload last model file
|
* `F5`: Reload last model file
|
||||||
@ -154,6 +191,7 @@ only applies to Visual Studio users.)
|
|||||||
* View, choose "Up" axis: change camera "up" axis to Z or Y (Y is
|
* View, choose "Up" axis: change camera "up" axis to Z or Y (Y is
|
||||||
default; automatically changed to Z when 3ds file is loaded)
|
default; automatically changed to Z when 3ds file is loaded)
|
||||||
|
|
||||||
|
|
||||||
## Known Issues
|
## Known Issues
|
||||||
* Warn on missing texture.
|
* Warn on missing texture.
|
||||||
* Test and complete install.bat on Windows.
|
* Test and complete install.bat on Windows.
|
||||||
@ -162,6 +200,7 @@ only applies to Visual Studio users.)
|
|||||||
* (View.cpp) Set pitch correctly for shift & middle mouse button drag.
|
* (View.cpp) Set pitch correctly for shift & middle mouse button drag.
|
||||||
* Lighting not correct until you rotate or enable z-Up
|
* Lighting not correct until you rotate or enable z-Up
|
||||||
|
|
||||||
|
|
||||||
## Authors
|
## Authors
|
||||||
* ClearSansRegular.ttf (**Apache 2.0 License**) by Intel
|
* ClearSansRegular.ttf (**Apache 2.0 License**) by Intel
|
||||||
<https://01.org/clear-sans> via
|
<https://01.org/clear-sans> via
|
||||||
@ -181,3 +220,19 @@ only applies to Visual Studio users.)
|
|||||||
**GPL v3** as per <https://code.google.com/archive/p/b3view/>
|
**GPL v3** as per <https://code.google.com/archive/p/b3view/>
|
||||||
(see [LICENSE](https://github.com/poikilos/b3view/blob/master/LICENSE)
|
(see [LICENSE](https://github.com/poikilos/b3view/blob/master/LICENSE)
|
||||||
file in your favorite text editor).
|
file in your favorite text editor).
|
||||||
|
|
||||||
|
|
||||||
|
## Developer Notes
|
||||||
|
|
||||||
|
### Regression Tests
|
||||||
|
|
||||||
|
#### Manipulating mesh on failed load
|
||||||
|
- steps to reproduce
|
||||||
|
- File, Open, choose a mesh file such as animal_bat.b3d
|
||||||
|
- File, Open, choose a texture (purposely incorrect input)
|
||||||
|
- incorrect behaviors:
|
||||||
|
- manipulating the loaded scene, such as calling remove()
|
||||||
|
- SEGFAULT
|
||||||
|
- correct behaviors:
|
||||||
|
- Do nothing to the current scene.
|
||||||
|
- Show a message saying that the format is incorrect.
|
||||||
|
@ -185,7 +185,7 @@ void UserInterface::setupUserInterface()
|
|||||||
|
|
||||||
y += size_y + spacing_y;
|
y += size_y + spacing_y;
|
||||||
axisSizeEditBox = m_Gui->addEditBox(
|
axisSizeEditBox = m_Gui->addEditBox(
|
||||||
L"",
|
std::to_wstring(this->m_Engine->m_AxisLength).c_str(),
|
||||||
rect<s32>(vector2d<s32>(spacing_x, y),
|
rect<s32>(vector2d<s32>(spacing_x, y),
|
||||||
dimension2d<s32>(size_x, size_y)),
|
dimension2d<s32>(size_x, size_y)),
|
||||||
true,
|
true,
|
||||||
@ -255,7 +255,7 @@ void UserInterface::displayLoadTextureDialog()
|
|||||||
void UserInterface::incrementFrame(f32 frameCount, bool enableRound)
|
void UserInterface::incrementFrame(f32 frameCount, bool enableRound)
|
||||||
{
|
{
|
||||||
if (this->m_Engine->m_LoadedMesh != nullptr) {
|
if (this->m_Engine->m_LoadedMesh != nullptr) {
|
||||||
if (this->m_Engine->isPlaying)
|
if (this->m_Engine->m_IsPlaying)
|
||||||
this->m_Engine->toggleAnimation();
|
this->m_Engine->toggleAnimation();
|
||||||
this->m_Engine->m_LoadedMesh->setCurrentFrame(
|
this->m_Engine->m_LoadedMesh->setCurrentFrame(
|
||||||
enableRound
|
enableRound
|
||||||
@ -475,21 +475,36 @@ void UserInterface::drawStatusLine() const
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool UserInterface::OnSelectMesh() {
|
||||||
|
this->m_MatchingTextures.clear();
|
||||||
|
this->m_AllTextures.clear();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load the next texture from the list of found textures.
|
||||||
|
* Files are only listed once for speed, so you must reload the
|
||||||
|
* model to trigger another list ("dir") operation (since loading
|
||||||
|
* a mesh calls OnSelectMesh() which clears allTextures and matchingTextures).
|
||||||
|
*
|
||||||
|
* @param direction Specify <0 to choose previous texture, >0 for next, 0 to
|
||||||
|
* reload current texture if any; otherwise, only select a texture if any
|
||||||
|
* matching textures (named like model) are present in . or ../textures.
|
||||||
|
* @return Any texture was loaded (true/false).
|
||||||
|
*/
|
||||||
bool UserInterface::loadNextTexture(int direction)
|
bool UserInterface::loadNextTexture(int direction)
|
||||||
{
|
{
|
||||||
|
cerr << "Loading texture..." << flush;
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
this->m_Engine->m_NextPath = L"";
|
|
||||||
std::wstring basePath = L".";
|
std::wstring basePath = L".";
|
||||||
if (this->m_Engine->m_PreviousPath.length() > 0) {
|
if (this->m_Engine->m_LoadedMeshPath.length() > 0) {
|
||||||
std::wstring prevModelName = Utility::basename(
|
std::wstring prevModelName = Utility::basename(
|
||||||
this->m_Engine->m_PreviousPath
|
this->m_Engine->m_LoadedMeshPath
|
||||||
);
|
);
|
||||||
//vector<wstring> dotExtensions;
|
|
||||||
//dotExtensions.push_back(L".png");
|
|
||||||
//dotExtensions.push_back(L".jpg");
|
|
||||||
wstring foundPath;
|
wstring foundPath;
|
||||||
wstring prevModelNoExt;
|
wstring prevModelNoExt;
|
||||||
prevModelNoExt = Utility::withoutExtension(prevModelName);
|
prevModelNoExt = Utility::withoutExtension(prevModelName);
|
||||||
|
/*
|
||||||
vector<wstring> names;
|
vector<wstring> names;
|
||||||
names.push_back(prevModelNoExt+L"_mesh");
|
names.push_back(prevModelNoExt+L"_mesh");
|
||||||
names.push_back(prevModelNoExt);
|
names.push_back(prevModelNoExt);
|
||||||
@ -506,185 +521,148 @@ bool UserInterface::loadNextTexture(int direction)
|
|||||||
names.push_back(prevModelNoExt+L"_f");
|
names.push_back(prevModelNoExt+L"_f");
|
||||||
names.push_back(prevModelNoExt+L"_male");
|
names.push_back(prevModelNoExt+L"_male");
|
||||||
names.push_back(prevModelNoExt+L"_m");
|
names.push_back(prevModelNoExt+L"_m");
|
||||||
|
*/
|
||||||
vector<wstring> badSuffixes;
|
vector<wstring> badSuffixes;
|
||||||
badSuffixes.push_back(L"_inv");
|
badSuffixes.push_back(L"_inv");
|
||||||
|
|
||||||
std::wstring lastDirPath = Utility::parentOfPath(
|
std::wstring lastDirPath = Utility::parentOfPath(
|
||||||
this->m_Engine->m_PreviousPath
|
this->m_Engine->m_LoadedMeshPath
|
||||||
);
|
);
|
||||||
std::wstring parentPath = Utility::parentOfPath(lastDirPath);
|
std::wstring parentPath = Utility::parentOfPath(lastDirPath);
|
||||||
std::wstring dirSeparator = Utility::delimiter(
|
std::wstring dirSeparator = Utility::delimiter(
|
||||||
this->m_Engine->m_PreviousPath
|
this->m_Engine->m_LoadedMeshPath
|
||||||
);
|
);
|
||||||
std::wstring texturesPath = parentPath + dirSeparator + L"textures";
|
std::wstring texturesPath = parentPath + dirSeparator + L"textures";
|
||||||
std::wstring tryTexPath = texturesPath + dirSeparator + prevModelNoExt
|
std::wstring tryTexPath = texturesPath + dirSeparator + prevModelNoExt
|
||||||
+ L".png";
|
+ L".png";
|
||||||
if (direction == 0 && Utility::isFile(tryTexPath)) {
|
vector<wstring> texturePaths;
|
||||||
this->m_Engine->m_NextPath = tryTexPath;
|
|
||||||
this->m_Engine->loadTexture(this->m_Engine->m_NextPath);
|
|
||||||
} else {
|
|
||||||
tryTexPath = lastDirPath + dirSeparator
|
|
||||||
+ prevModelNoExt + L".png";
|
|
||||||
if (direction == 0 && Utility::isFile(tryTexPath)) {
|
|
||||||
this->m_Engine->m_NextPath = tryTexPath;
|
|
||||||
ret = this->m_Engine->loadTexture(this->m_Engine->m_NextPath);
|
|
||||||
} else {
|
|
||||||
std::wstring path = texturesPath;
|
|
||||||
|
|
||||||
if (!fs::is_directory(fs::status(path)))
|
texturePaths.push_back(lastDirPath);
|
||||||
path = lastDirPath; // cycle in model's directory instead
|
|
||||||
|
|
||||||
fs::directory_iterator end_itr; // default yields past-the-end
|
if (fs::is_directory(fs::status(texturesPath))) {
|
||||||
|
texturePaths.push_back(texturesPath);
|
||||||
|
}
|
||||||
|
vector<wstring> dotExts;
|
||||||
|
for (auto ext : this->m_Engine->m_TextureExtensions) {
|
||||||
|
dotExts.push_back(L"." + ext);
|
||||||
|
}
|
||||||
|
|
||||||
std::wstring nextPath = L"";
|
if (this->m_MatchingTextures.size() + this->m_AllTextures.size() < 1) {
|
||||||
std::wstring retroPath = L"";
|
for (auto path : texturePaths) {
|
||||||
std::wstring lastPath = L"";
|
for (const auto& itr : fs::directory_iterator(path)) {
|
||||||
|
if (fs::is_regular_file(itr.status())) {
|
||||||
bool found = false;
|
std::wstring name = itr.path().filename().wstring();
|
||||||
bool force = false;
|
std::wstring suffix = Utility::getSuffix(name, dotExts,
|
||||||
wstring tryPath;
|
true);
|
||||||
if (fs::is_directory(fs::status(path))) {
|
bool isUsable = true;
|
||||||
if (this->m_Engine->m_PrevTexturePath.length() == 0) {
|
std::wstring nameNoExt = Utility::withoutExtension(
|
||||||
// if (this->m_Engine->m_PreviousPath.length() > 0) {
|
name
|
||||||
for (auto name : names) {
|
|
||||||
for (auto extension : this->m_Engine->textureExtensions) {
|
|
||||||
tryPath = texturesPath + dirSeparator
|
|
||||||
+ name + L"." + extension;
|
|
||||||
// tryPath = Utility::toWstring(Utility::toString(tryPath));
|
|
||||||
if (Utility::isFile(tryPath)) {
|
|
||||||
foundPath = tryPath;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// else
|
|
||||||
// debug() << " - no '"
|
|
||||||
// << Utility::toString(tryPath)
|
|
||||||
// << "'" << endl;
|
|
||||||
}
|
|
||||||
if (foundPath.length() > 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (foundPath.length() > 0) {
|
|
||||||
nextPath = foundPath;
|
|
||||||
found = true;
|
|
||||||
force = true;
|
|
||||||
this->m_Engine->setEnableTextureInterpolation(false);
|
|
||||||
viewMenu->setItemChecked(
|
|
||||||
viewTextureInterpolationIdx,
|
|
||||||
this->m_Engine->getEnableTextureInterpolation()
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
nextPath = tryPath;
|
|
||||||
found = true;
|
|
||||||
force = true;
|
|
||||||
}
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do fuzzy texture name search
|
|
||||||
// (If found no texture yet, match instead of predict name):
|
|
||||||
if ((this->m_Engine->m_PrevTexturePath.length() == 0)
|
|
||||||
&& (foundPath.length() == 0)) {
|
|
||||||
for (const auto& itr : fs::directory_iterator(path)) {
|
|
||||||
std::wstring ext = Utility::extensionOf(
|
|
||||||
itr.path().wstring()
|
|
||||||
); // no dot!
|
|
||||||
std::wstring nameNoExt = Utility::withoutExtension(itr.path().filename().wstring());
|
|
||||||
// std::wstring rightName = Utility::rightOf(nameNoExt, L"_", true);
|
|
||||||
// std::wstring rightLastName = Utility::rightOfLast(nameNoExt, L"_", true);
|
|
||||||
// debug() << "itr.path().filename().wstring(): " << itr.path().filename().c_str() << endl;
|
|
||||||
if (Utility::startsWith(nameNoExt, prevModelNoExt)) {
|
|
||||||
wstring remainder = Utility::rightOf(nameNoExt, prevModelNoExt, true);
|
|
||||||
if (std::find(badSuffixes.begin(),
|
|
||||||
badSuffixes.end(), remainder)
|
|
||||||
== badSuffixes.end()) {
|
|
||||||
foundPath = itr.path().wstring();
|
|
||||||
nextPath = foundPath;
|
|
||||||
found = true;
|
|
||||||
force = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (auto name : names) {
|
|
||||||
for (auto extension : this->m_Engine->textureExtensions) {
|
|
||||||
wstring tryEnd = name + L"." + extension;
|
|
||||||
//if (Utility::endsWith(nameNoExt, name)) {
|
|
||||||
if (Utility::endsWith(itr.path().filename().wstring(), tryEnd)) {
|
|
||||||
foundPath = itr.path().wstring();
|
|
||||||
nextPath = foundPath;
|
|
||||||
found = true;
|
|
||||||
force = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
debug() << "!endsWith("
|
|
||||||
<< Utility::toString(itr.path().filename().wstring())
|
|
||||||
<< "," << Utility::toString(tryEnd)
|
|
||||||
<< endl;
|
|
||||||
// debug() << "!endsWith("
|
|
||||||
// << Utility::toString(nameNoExt)
|
|
||||||
// << "," << Utility::toString(name)
|
|
||||||
// << endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (foundPath.length() > 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (foundPath.length() > 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (force) {
|
|
||||||
this->m_Engine->setEnableTextureInterpolation(false);
|
|
||||||
viewMenu->setItemChecked(
|
|
||||||
viewTextureInterpolationIdx,
|
|
||||||
this->m_Engine->getEnableTextureInterpolation()
|
|
||||||
);
|
);
|
||||||
}
|
if (Utility::endsWithAny(nameNoExt, badSuffixes, true))
|
||||||
for (const auto& itr : fs::directory_iterator(path)) {
|
isUsable = false;
|
||||||
std::wstring ext = Utility::extensionOf(
|
if (isUsable && suffix.length() > 0) {
|
||||||
itr.path().wstring()
|
this->m_AllTextures.push_back(
|
||||||
); // no dot!
|
path + dirSeparator + name
|
||||||
if (!is_directory(itr.status())
|
);
|
||||||
&& std::find(m_Engine->textureExtensions.begin(),
|
if (Utility::startsWith(name, prevModelNoExt)) {
|
||||||
m_Engine->textureExtensions.end(), ext)
|
this->m_MatchingTextures.push_back(
|
||||||
!= m_Engine->textureExtensions.end()) {
|
path + dirSeparator + name
|
||||||
// cycle through files (go to next after
|
);
|
||||||
// m_PrevTexturePath if any previously loaded,
|
}
|
||||||
// otherwise first)
|
else if (name.find(prevModelNoExt) != std::wstring::npos) {
|
||||||
if (nextPath.length() == 0)
|
this->m_MatchingTextures.push_back(
|
||||||
nextPath = itr.path().wstring();
|
path + dirSeparator + name
|
||||||
lastPath = itr.path().wstring();
|
);
|
||||||
if (found && direction > 0) {
|
}
|
||||||
if (!force)
|
else if (name.find(Utility::replaceAll(prevModelNoExt, L"_", L"")) != std::wstring::npos) {
|
||||||
nextPath = itr.path().wstring();
|
this->m_MatchingTextures.push_back(
|
||||||
break;
|
path + dirSeparator + name
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (itr.path().wstring()
|
|
||||||
== this->m_Engine->m_PrevTexturePath)
|
|
||||||
found = true;
|
|
||||||
if (!found)
|
|
||||||
retroPath = itr.path().wstring();
|
|
||||||
}
|
}
|
||||||
else debug() << Utility::toString(ext)
|
|
||||||
<< "is not a valid extension for: "
|
|
||||||
<< Utility::toString(itr.path().filename().wstring())
|
|
||||||
<< endl;
|
|
||||||
}
|
|
||||||
if (retroPath.length() == 0)
|
|
||||||
retroPath = lastPath; // previous is last if at start
|
|
||||||
if (direction < 0)
|
|
||||||
nextPath = retroPath;
|
|
||||||
if (nextPath.length() > 0) {
|
|
||||||
ret = this->m_Engine->loadTexture(nextPath);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
vector<wstring> paths = this->m_MatchingTextures;
|
||||||
|
if (this->m_MatchingTextures.size() < 1) {
|
||||||
|
paths = this->m_AllTextures;
|
||||||
|
debug() << "There were no matching textures."
|
||||||
|
<< " The entire list of " << this->m_AllTextures.size()
|
||||||
|
<< " found textures will be used." << std::endl;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Assume the user wants to view name-matched texture using
|
||||||
|
// the render settings of Minetest.
|
||||||
|
this->m_Engine->setEnableTextureInterpolation(false);
|
||||||
|
viewMenu->setItemChecked(
|
||||||
|
viewTextureInterpolationIdx,
|
||||||
|
this->m_Engine->getEnableTextureInterpolation()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
std::wstring prevTexture = L"";
|
||||||
|
std::wstring nextTexture = L"";
|
||||||
|
std::wstring lastTexture = L"";
|
||||||
|
std::wstring firstTexture = L"";
|
||||||
|
bool found = false;
|
||||||
|
for (auto path : paths) {
|
||||||
|
if (firstTexture.length() == 0)
|
||||||
|
firstTexture = path;
|
||||||
|
lastTexture = path;
|
||||||
|
if (this->m_Engine->m_LoadedTexturePath.length() > 0) {
|
||||||
|
if (path == this->m_Engine->m_LoadedTexturePath) {
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
else if (!found) {
|
||||||
|
prevTexture = path;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (nextTexture.length() == 0)
|
||||||
|
nextTexture = path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
prevTexture = path; // Use the last one as the previous.
|
||||||
|
if (nextTexture.length() == 0)
|
||||||
|
nextTexture = path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (nextTexture.length() == 0)
|
||||||
|
nextTexture = firstTexture; // The last is current, so next is 1st.
|
||||||
|
if (prevTexture.length() == 0) {
|
||||||
|
if (lastTexture != firstTexture)
|
||||||
|
prevTexture = lastTexture; // Wrap to end.
|
||||||
|
else
|
||||||
|
prevTexture = firstTexture; // Use the only texture.
|
||||||
|
}
|
||||||
|
if (lastTexture.length() > 0) {
|
||||||
|
if (direction < 0) {
|
||||||
|
ret = this->m_Engine->loadTexture(prevTexture);
|
||||||
|
}
|
||||||
|
else if (direction > 0) {
|
||||||
|
ret = this->m_Engine->loadTexture(nextTexture);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// If direction is 0 (such as when a model is loaded that has
|
||||||
|
// no texture), only load a preloaded or matching texture.
|
||||||
|
if (this->m_Engine->m_LoadedTexturePath.length() > 0) {
|
||||||
|
ret = this->m_Engine->loadTexture(
|
||||||
|
this->m_Engine->m_LoadedTexturePath
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else if (this->m_MatchingTextures.size() >= 1) {
|
||||||
|
ret = this->m_Engine->loadTexture(firstTexture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (this->m_Engine->m_LoadedTexturePath.length() > 0) {
|
||||||
|
ret = this->m_Engine->loadTexture(
|
||||||
|
this->m_Engine->m_LoadedTexturePath
|
||||||
|
);
|
||||||
|
}
|
||||||
} else
|
} else
|
||||||
debug() << "Can't cycle texture since no file was opened" << endl;
|
debug() << "Can't cycle texture since no file was opened" << endl;
|
||||||
|
cerr << (ret?"OK":"FAILED") << endl;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -705,8 +683,8 @@ void UserInterface::exportMeshToHome(std::string extension)
|
|||||||
std::cout << "Your PATH is: " << where.c_str() << '\n';
|
std::cout << "Your PATH is: " << where.c_str() << '\n';
|
||||||
}
|
}
|
||||||
std::string name = "";
|
std::string name = "";
|
||||||
if (m_Engine->m_PreviousPath.length() > 0) {
|
if (m_Engine->m_LoadedMeshPath.length() > 0) {
|
||||||
name = Utility::toString(Utility::withoutExtension(Utility::basename(m_Engine->m_PreviousPath)));
|
name = Utility::toString(Utility::withoutExtension(Utility::basename(m_Engine->m_LoadedMeshPath)));
|
||||||
}
|
}
|
||||||
|
|
||||||
wstring result = m_Engine->saveMesh(where, name, extension);
|
wstring result = m_Engine->saveMesh(where, name, extension);
|
||||||
@ -837,7 +815,7 @@ bool UserInterface::OnEvent(const SEvent& event)
|
|||||||
break;
|
break;
|
||||||
case UIE_AXISSIZEEDITBOX:
|
case UIE_AXISSIZEEDITBOX:
|
||||||
if (ge->EventType == EGET_EDITBOX_ENTER) {
|
if (ge->EventType == EGET_EDITBOX_ENTER) {
|
||||||
this->m_Engine->axisLength = Utility::toF32(
|
this->m_Engine->m_AxisLength = Utility::toF32(
|
||||||
this->axisSizeEditBox->getText()
|
this->axisSizeEditBox->getText()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -858,7 +836,7 @@ bool UserInterface::OnEvent(const SEvent& event)
|
|||||||
m_Engine->reloadTexture();
|
m_Engine->reloadTexture();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (m_Engine->m_PreviousPath.length() > 0) {
|
if (m_Engine->m_LoadedMeshPath.length() > 0) {
|
||||||
bool result = m_Engine->reloadMesh();
|
bool result = m_Engine->reloadMesh();
|
||||||
if (!result) {
|
if (!result) {
|
||||||
this->m_Engine->m_Device->getGUIEnvironment()->addMessageBox(
|
this->m_Engine->m_Device->getGUIEnvironment()->addMessageBox(
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include <irrlicht/irrlicht.h>
|
#include <irrlicht/irrlicht.h>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
// Forward declaration of class Engine
|
// Forward declaration of class Engine
|
||||||
class Engine;
|
class Engine;
|
||||||
@ -74,6 +75,8 @@ private:
|
|||||||
|
|
||||||
irr::gui::IGUIWindow* playbackWindow;
|
irr::gui::IGUIWindow* playbackWindow;
|
||||||
irr::core::dimension2d<irr::u32> m_WindowSize; // previous size
|
irr::core::dimension2d<irr::u32> m_WindowSize; // previous size
|
||||||
|
std::vector<std::wstring> m_AllTextures;
|
||||||
|
std::vector<std::wstring> m_MatchingTextures;
|
||||||
public:
|
public:
|
||||||
irr::gui::IGUIContextMenu* menu;
|
irr::gui::IGUIContextMenu* menu;
|
||||||
irr::gui::IGUIContextMenu* fileMenu;
|
irr::gui::IGUIContextMenu* fileMenu;
|
||||||
@ -104,6 +107,7 @@ public:
|
|||||||
void drawStatusLine() const;
|
void drawStatusLine() const;
|
||||||
bool loadNextTexture(int direction);
|
bool loadNextTexture(int direction);
|
||||||
void exportMeshToHome(std::string extension);
|
void exportMeshToHome(std::string extension);
|
||||||
|
bool OnSelectMesh();
|
||||||
|
|
||||||
// IEventReceiver
|
// IEventReceiver
|
||||||
virtual bool OnEvent(const irr::SEvent& event);
|
virtual bool OnEvent(const irr::SEvent& event);
|
||||||
|
139
Utility.cpp
139
Utility.cpp
@ -8,6 +8,7 @@
|
|||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
#include "Debug.h"
|
#include "Debug.h"
|
||||||
|
|
||||||
@ -21,6 +22,21 @@ void Utility::dumpVectorToConsole(const vector3df& vector)
|
|||||||
debug() << "X: " << vector.X << " Y: " << vector.Y << " Z: " << vector.Z << endl;
|
debug() << "X: " << vector.X << " Y: " << vector.Y << " Z: " << vector.Z << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Utility::getTextureCount(const SMaterial& material) {
|
||||||
|
int count = 0;
|
||||||
|
for (irr::u32 ti = 0; ti < MATERIAL_MAX_TEXTURES; ti++)
|
||||||
|
if (material.getTexture(ti) != nullptr)
|
||||||
|
count++;
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
int Utility::getTextureCount(IAnimatedMeshSceneNode* node) {
|
||||||
|
int count = 0;
|
||||||
|
for (irr::u32 matIndex = 0; matIndex < node->getMaterialCount(); matIndex++) {
|
||||||
|
count += getTextureCount(node->getMaterial(matIndex));
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
void Utility::dumpMeshInfoToConsole(IAnimatedMeshSceneNode* node)
|
void Utility::dumpMeshInfoToConsole(IAnimatedMeshSceneNode* node)
|
||||||
{
|
{
|
||||||
if (node == nullptr) {
|
if (node == nullptr) {
|
||||||
@ -55,11 +71,7 @@ void Utility::dumpMeshInfoToConsole(IAnimatedMeshSceneNode* node)
|
|||||||
<< material.Shininess << endl;
|
<< material.Shininess << endl;
|
||||||
|
|
||||||
// check for # textures
|
// check for # textures
|
||||||
int textures = 0;
|
debug() << "[MESH]: # of textures : " << Utility::getTextureCount(material) << endl;
|
||||||
for (irr::u32 ti = 0; ti < MATERIAL_MAX_TEXTURES; ti++)
|
|
||||||
if (material.getTexture(ti) != nullptr)
|
|
||||||
textures++;
|
|
||||||
debug() << "[MESH]: # of textures : " << textures << endl;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,6 +152,44 @@ bool Utility::startsWith(const std::wstring& haystack, const std::wstring& needl
|
|||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wstring Utility::replaceAll(const wstring &subject, const wstring &from, const wstring &to)
|
||||||
|
{
|
||||||
|
size_t i = 0;
|
||||||
|
if (from.length() == 0) {
|
||||||
|
return subject;
|
||||||
|
}
|
||||||
|
wstring result = subject;
|
||||||
|
while (i < result.length()) {
|
||||||
|
if (result.substr(i, from.length()) == from) {
|
||||||
|
result = result.substr(0, i) + to + result.substr(i + from.length());
|
||||||
|
i += to.length();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Utility::replaceAll(const std::string &subject, const std::string &from, const std::string &to)
|
||||||
|
{
|
||||||
|
size_t i = 0;
|
||||||
|
if (from.length() == 0) {
|
||||||
|
return subject;
|
||||||
|
}
|
||||||
|
std::string result = subject;
|
||||||
|
while (i < result.length()) {
|
||||||
|
if (result.substr(i, from.length()) == from) {
|
||||||
|
result = result.substr(0, i) + to + result.substr(i + from.length());
|
||||||
|
i += to.length();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
bool Utility::endsWith(const std::wstring& haystack, const std::wstring& needle) {
|
bool Utility::endsWith(const std::wstring& haystack, const std::wstring& needle) {
|
||||||
bool found = false;
|
bool found = false;
|
||||||
if (haystack.length() >= needle.length()) {
|
if (haystack.length() >= needle.length()) {
|
||||||
@ -150,6 +200,48 @@ bool Utility::endsWith(const std::wstring& haystack, const std::wstring& needle)
|
|||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Utility::startsWithAny(const std::wstring& haystack, const std::vector<std::wstring>& needles, bool CI) {
|
||||||
|
return getPrefix(haystack, needles, CI).length() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Utility::endsWithAny(const std::wstring& haystack, const std::vector<std::wstring>& needles, bool CI) {
|
||||||
|
return getSuffix(haystack, needles, CI).length() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring Utility::getPrefix(const std::wstring& haystack, const std::vector<std::wstring>& needles, bool CI) {
|
||||||
|
if (CI) {
|
||||||
|
std::wstring haystackLower = Utility::toLower(haystack);
|
||||||
|
for (auto needle : needles) {
|
||||||
|
if (Utility::startsWith(haystackLower, Utility::toLower(needle)))
|
||||||
|
return needle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (auto needle : needles) {
|
||||||
|
if (Utility::startsWith(haystack, needle))
|
||||||
|
return needle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return L"";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring Utility::getSuffix(const std::wstring& haystack, const std::vector<std::wstring>& needles, bool CI) {
|
||||||
|
if (CI) {
|
||||||
|
std::wstring haystackLower = Utility::toLower(haystack);
|
||||||
|
for (auto needle : needles) {
|
||||||
|
if (Utility::endsWith(haystackLower, Utility::toLower(needle)))
|
||||||
|
return needle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (auto needle : needles) {
|
||||||
|
if (Utility::endsWith(haystack, needle))
|
||||||
|
return needle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return L"";
|
||||||
|
}
|
||||||
|
|
||||||
/// Get any substring to the left of the last delimiter.
|
/// Get any substring to the left of the last delimiter.
|
||||||
/// allIfNotFound: Return whole string on no delimiter, vs empty string.
|
/// allIfNotFound: Return whole string on no delimiter, vs empty string.
|
||||||
wstring Utility::leftOfLast(const wstring& path, const wstring& delimiter, bool allIfNotFound)
|
wstring Utility::leftOfLast(const wstring& path, const wstring& delimiter, bool allIfNotFound)
|
||||||
@ -362,3 +454,40 @@ std::string Utility::toString(irr::f32 val)
|
|||||||
// return abs(f2-f1) < .00000001; // TODO: kEpsilon? (see also
|
// return abs(f2-f1) < .00000001; // TODO: kEpsilon? (see also
|
||||||
// // <https://en.wikipedia.org/wiki/Machine_epsilon#How_to_determine_machine_epsilon>)
|
// // <https://en.wikipedia.org/wiki/Machine_epsilon#How_to_determine_machine_epsilon>)
|
||||||
// }
|
// }
|
||||||
|
TestUtility::TestUtility() {
|
||||||
|
std::cerr << "TestUtility..." << std::flush;
|
||||||
|
testReplaceAll(L"***water_dragon***", L"_", L"", L"***waterdragon***");
|
||||||
|
testReplaceAll(L"*water_dragon*", L"*", L"***", L"***water_dragon***");
|
||||||
|
|
||||||
|
testReplaceAll(L"***water_dragon***", L"***", L"", L"water_dragon");
|
||||||
|
testReplaceAll(L"***water_dragon***", L"", L"***", L"***water_dragon***"); // do nothing
|
||||||
|
std::cerr << "OK" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestUtility::testReplaceAll(const wstring &subject, const wstring &from, const wstring &to, const wstring &expectedResult)
|
||||||
|
{
|
||||||
|
this->assertEqual(Utility::replaceAll(subject, from, to), expectedResult);
|
||||||
|
};
|
||||||
|
void TestUtility::testReplaceAll(const std::string &subject, const std::string &from, const std::string &to, const std::string &expectedResult)
|
||||||
|
{
|
||||||
|
std::string result = Utility::replaceAll(subject, from, to);
|
||||||
|
this->assertEqual(result, expectedResult);
|
||||||
|
};
|
||||||
|
|
||||||
|
void TestUtility::assertEqual(const wstring& subject, const wstring& expectedResult)
|
||||||
|
{
|
||||||
|
if (subject != expectedResult) {
|
||||||
|
cerr << "The test expected \"" << Utility::toString(expectedResult) << "\" but got \"" << Utility::toString(subject) << std::endl;
|
||||||
|
}
|
||||||
|
assert(subject == expectedResult);
|
||||||
|
}
|
||||||
|
void TestUtility::assertEqual(const std::string subject, const std::string expectedResult)
|
||||||
|
{
|
||||||
|
if (subject != expectedResult) {
|
||||||
|
cerr << "The test expected \"" << expectedResult << "\" but got \"" << subject << std::endl;
|
||||||
|
}
|
||||||
|
assert(subject == expectedResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static TestUtility testutility;
|
||||||
|
18
Utility.h
18
Utility.h
@ -5,10 +5,13 @@
|
|||||||
|
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
class Utility {
|
class Utility {
|
||||||
public:
|
public:
|
||||||
static void dumpVectorToConsole(const irr::core::vector3df& vector);
|
static void dumpVectorToConsole(const irr::core::vector3df& vector);
|
||||||
|
static int getTextureCount(const irr::video::SMaterial& material);
|
||||||
|
static int getTextureCount(irr::scene::IAnimatedMeshSceneNode* node);
|
||||||
static void dumpMeshInfoToConsole(irr::scene::IAnimatedMeshSceneNode* node);
|
static void dumpMeshInfoToConsole(irr::scene::IAnimatedMeshSceneNode* node);
|
||||||
static std::wstring parentOfPath(const std::wstring& path);
|
static std::wstring parentOfPath(const std::wstring& path);
|
||||||
static std::wstring basename(const std::wstring& path);
|
static std::wstring basename(const std::wstring& path);
|
||||||
@ -17,7 +20,13 @@ public:
|
|||||||
static std::wstring rightOf(const std::wstring& path, const std::wstring& delimiter, bool allIfNotFound);
|
static std::wstring rightOf(const std::wstring& path, const std::wstring& delimiter, bool allIfNotFound);
|
||||||
static std::wstring rightOfLast(const std::wstring& path, const std::wstring& delimiter, bool allIfNotFound);
|
static std::wstring rightOfLast(const std::wstring& path, const std::wstring& delimiter, bool allIfNotFound);
|
||||||
static bool startsWith(const std::wstring& haystack, const std::wstring& needle);
|
static bool startsWith(const std::wstring& haystack, const std::wstring& needle);
|
||||||
|
static std::wstring replaceAll(const std::wstring& subject, const std::wstring& from, const std::wstring& to);
|
||||||
|
static std::string replaceAll(const std::string& subject, const std::string& from, const std::string& to);
|
||||||
static bool endsWith(const std::wstring& haystack, const std::wstring& needle);
|
static bool endsWith(const std::wstring& haystack, const std::wstring& needle);
|
||||||
|
static std::wstring getPrefix(const std::wstring& haystack, const std::vector<std::wstring>& needles, bool CI);
|
||||||
|
static std::wstring getSuffix(const std::wstring& haystack, const std::vector<std::wstring>& needles, bool CI);
|
||||||
|
static bool startsWithAny(const std::wstring& haystack, const std::vector<std::wstring>& needles, bool CI);
|
||||||
|
static bool endsWithAny(const std::wstring& haystack, const std::vector<std::wstring>& needles, bool CI);
|
||||||
static std::wstring withoutExtension(const std::wstring& path);
|
static std::wstring withoutExtension(const std::wstring& path);
|
||||||
static std::wstring extensionOf(const std::wstring& path);
|
static std::wstring extensionOf(const std::wstring& path);
|
||||||
static std::wstring delimiter(const std::wstring& path);
|
static std::wstring delimiter(const std::wstring& path);
|
||||||
@ -44,4 +53,13 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class TestUtility {
|
||||||
|
public:
|
||||||
|
TestUtility();
|
||||||
|
void assertEqual(const std::wstring& subject, const std::wstring& expectedResult);
|
||||||
|
void assertEqual(const std::string subject, const std::string expectedResult);
|
||||||
|
void testReplaceAll(const std::wstring& subject, const std::wstring& from, const std::wstring& to, const std::wstring& expectedResult);
|
||||||
|
void testReplaceAll(const std::string& subject, const std::string& from, const std::string& to, const std::string& expectedResult);
|
||||||
|
};
|
||||||
|
|
||||||
#endif // UTILS_H
|
#endif // UTILS_H
|
||||||
|
BIN
screenshot.jpg
BIN
screenshot.jpg
Binary file not shown.
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 58 KiB |
Loading…
x
Reference in New Issue
Block a user