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
|
||||
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
|
||||
(poikilos)
|
||||
### Changes
|
||||
* Move display mode booleans to engine.
|
||||
* Add more string utilities.
|
||||
* Do fuzzy search against actual texture names if can't find theoretical
|
||||
- Move the display mode booleans to Engine.
|
||||
- Add more string utilities.
|
||||
- Do fuzzy search against actual texture names if can't find theoretical
|
||||
ones.
|
||||
|
||||
|
||||
## [git] - 2019-05-16
|
||||
(poikilos)
|
||||
### Added
|
||||
* playback menu
|
||||
- playback menu
|
||||
- Move framerate controls to playback menu.
|
||||
* fix frame-by-frame hotkeys
|
||||
- fix frame-by-frame hotkeys
|
||||
- move code to new incrementFrame method
|
||||
|
||||
# Changelog
|
||||
|
||||
## [git] - 2019-05-16
|
||||
(poikilos)
|
||||
### Changed
|
||||
* improve minetest texture detection (alternate conventions)
|
||||
* turn off interpolation if loadNextTexture detects minetest directory
|
||||
- improve minetest texture detection (alternate conventions)
|
||||
- turn off interpolation if loadNextTexture detects minetest directory
|
||||
structure (../textures/<texture filename based on model name>)
|
||||
|
||||
|
||||
## [git] - 2019-05-16
|
||||
(poikilos)
|
||||
### Added
|
||||
* export COLLADA (non-Blender), IRR, IRRMESH, OBJ, STL
|
||||
* show dialog box if operation can't be performed
|
||||
- export COLLADA (non-Blender), IRR, IRRMESH, OBJ, STL
|
||||
- show dialog box if operation can't be performed
|
||||
- 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)
|
||||
* add irrlicht mimetype (static/non-animated Irrlicht mesh)
|
||||
- add irrlicht mimetype (static/non-animated Irrlicht mesh)
|
||||
|
||||
|
||||
## [git] - 2019-04-19
|
||||
(poikilos)
|
||||
### Added
|
||||
* box for axis length (size of the axis widget)
|
||||
* box for frame rate
|
||||
* camera target widget
|
||||
* option for turning off origin axis widget
|
||||
* Add menu items for hotkeys, and show hotkey on relevant menu items.
|
||||
- box for axis length (size of the axis widget)
|
||||
- box for frame rate
|
||||
- camera target widget
|
||||
- option for turning off origin axis widget
|
||||
- Add menu items for hotkeys, and show hotkey on relevant menu items.
|
||||
|
||||
### Changed
|
||||
* Reorder items on panel.
|
||||
* Hotkeys are different so they're not triggered when typing in the
|
||||
- Reorder items on panel.
|
||||
- Hotkeys are different so they're not triggered when typing in the
|
||||
panel.
|
||||
* Don't reset yaw nor camera distance when panning.
|
||||
* Show name of loaded model on title bar.
|
||||
* Fix crash on loading texture before model.
|
||||
* Fix use of unsigned frame delta for slow and fast options.
|
||||
- Don't reset yaw nor camera distance when panning.
|
||||
- Show name of loaded model on title bar.
|
||||
- Fix crash on loading texture before model.
|
||||
- Fix use of unsigned frame delta for slow and fast options.
|
||||
|
||||
|
||||
## [git] - 2019-04-08
|
||||
(poikilos)
|
||||
### Added
|
||||
* snapWidgets (move playbackWindow on resize, not leave past edge)
|
||||
- snapWidgets (move playbackWindow on resize, not leave past edge)
|
||||
|
||||
### Changed
|
||||
* changed enum values to leave room in between, comment unused
|
||||
* fixed issue in Utility not detecting backslashes correctly
|
||||
* renamed Utils.* to Utility.* to match class name
|
||||
* coding style to WebKit (run ./etc/quality.sh to check)
|
||||
* improve pan - don't reset view
|
||||
* improve initial camera settings: angle calculation
|
||||
- changed enum values to leave room in between, comment unused
|
||||
- fixed issue in Utility not detecting backslashes correctly
|
||||
- renamed Utils.* to Utility.* to match class name
|
||||
- coding style to WebKit (run ./etc/quality.sh to check)
|
||||
- improve pan - don't reset view
|
||||
- improve initial camera settings: angle calculation
|
||||
|
||||
|
||||
## [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
|
||||
- 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
|
||||
- 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/<model basename>.png & .jpg 1st time pressing `t`
|
||||
* Use alpha on textures by default
|
||||
- look for ../textures/<model basename>.png & .jpg 1st time pressing `t`
|
||||
- Use alpha on textures by default
|
||||
(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
|
||||
(poikilos)
|
||||
### Added
|
||||
* hotkeys to reload model/texture
|
||||
* license (see README.md for licensing history)
|
||||
- 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
|
||||
(poikilos)
|
||||
### Added
|
||||
- hotkeys to reload model/texture
|
||||
- license (see README.md for licensing history)
|
||||
|
||||
### Changed
|
||||
* only try to load png or jpg textures--skip others when cycling
|
||||
* cycle backwards correctly
|
||||
* fix some of the header creep (remove unecessary includes in h files)
|
||||
* improve initial camera position and angle (see top of characters since
|
||||
- only try to load png or jpg textures--skip others when cycling
|
||||
- cycle backwards correctly
|
||||
- fix some of the header creep (remove unecessary includes in h files)
|
||||
- improve initial camera position and angle (see top of characters since
|
||||
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`
|
||||
operates on view correctly (relatively) no matter where camera starts.
|
||||
|
||||
|
||||
## [git] - 2019-03-07
|
||||
(poikilos)
|
||||
### Added
|
||||
* playback controls
|
||||
- playback controls
|
||||
|
||||
|
||||
## [git] - 2019-03-06
|
||||
(poikilos)
|
||||
### 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
|
||||
* icon, install scripts, and mime type (`model/b3d`)--see README.md
|
||||
* mime type (`model/x`)
|
||||
* added ClearSansRegular.ttf
|
||||
* hotkeys to cycle ../textures/*
|
||||
- icon, install scripts, and mime type (`model/b3d`)--see README.md
|
||||
- mime type (`model/x`)
|
||||
- added ClearSansRegular.ttf
|
||||
- hotkeys to cycle ../textures/*
|
||||
|
||||
### 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).
|
||||
* 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.
|
||||
* check for font load failure properly, and load properly if succeeds
|
||||
* check for "ClearSansRegular.ttf" instead of "arial.ttf"
|
||||
* move `using namespace` directives from `h` files and specify upon use,
|
||||
- check for font load failure properly, and load properly if succeeds
|
||||
- check for "ClearSansRegular.ttf" instead of "arial.ttf"
|
||||
- move `using namespace` directives from `h` files and specify upon use,
|
||||
as per C++ best practices; add directives to `cpp` files only as
|
||||
needed (removed cumulative namespace creep).
|
||||
|
||||
|
||||
## [git-94e3b8f] - 2019-03-06
|
||||
(poikilos)
|
||||
### Added
|
||||
* README.md
|
||||
- README.md
|
||||
|
||||
### Changed
|
||||
(CGUITTFont methods are in CGUITTFont class unless otherwise specified)
|
||||
* fixed instances of "0 as null pointer constant" (changed to `nullptr`)
|
||||
* changed inconsistent use of spaces and tabs (changed tabs to 4 spaces)
|
||||
* (UserInterface.cpp) fixed "logical not is only applied to the left
|
||||
- fixed instances of "0 as null pointer constant" (changed to `nullptr`)
|
||||
- changed inconsistent use of spaces and tabs (changed tabs to 4 spaces)
|
||||
- (UserInterface.cpp) fixed "logical not is only applied to the left
|
||||
hand side of this comparison..." (put parenthesis around
|
||||
`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).
|
||||
* check for nullptr before using:
|
||||
* (CGUITTFont.cpp) `tt_face->face` in `getWidthFromCharacter`,
|
||||
- check for nullptr before using:
|
||||
- (CGUITTFont.cpp) `tt_face->face` in `getWidthFromCharacter`,
|
||||
`getGlyphByChar` (return 0 as bad as per convention:
|
||||
existing code already checks for 0--see
|
||||
`getWidthFromCharacter`), `getKerningWidth`,
|
||||
`draw`, `attach` (also don't copy null by
|
||||
reference there--instead, set to nullptr if source is nullptr)
|
||||
* check length of array before using
|
||||
* (CGUITTFont.cpp) elements of `Glyph` array (type
|
||||
- check length of array before using
|
||||
- (CGUITTFont.cpp) elements of `Glyph` array (type
|
||||
`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
|
||||
|
||||
### 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
|
||||
|
||||
|
||||
## [git-d964384] - 2019-03-06
|
||||
### Changed
|
||||
(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
|
||||
* .gitignore (a [Qt .gitignore](https://github.com/github/gitignore/blob/master/Qt.gitignore))
|
||||
* CHANGELOG.md
|
||||
- .gitignore (a [Qt .gitignore](https://github.com/github/gitignore/blob/master/Qt.gitignore))
|
||||
- CHANGELOG.md
|
||||
|
108
Engine.cpp
108
Engine.cpp
@ -89,8 +89,10 @@ void Engine::setupScene()
|
||||
// further down.
|
||||
ICameraSceneNode* camera = m_Scene->addCameraSceneNode(nullptr, m_CamPos,
|
||||
m_CamTarget);
|
||||
camera->setAspectRatio(static_cast<f32>(m_Driver->getScreenSize().Width)
|
||||
/ static_cast<f32>(m_Driver->getScreenSize().Height));
|
||||
camera->setAspectRatio(
|
||||
static_cast<f32>(m_Driver->getScreenSize().Width)
|
||||
/ static_cast<f32>(m_Driver->getScreenSize().Height)
|
||||
);
|
||||
}
|
||||
|
||||
IGUIEnvironment* Engine::getGUIEnvironment() const
|
||||
@ -189,10 +191,10 @@ void Engine::drawAxisLines()
|
||||
|
||||
if (enableAxisWidget) {
|
||||
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));
|
||||
position2d<s32> textPos = m_Scene->getSceneCollisionManager()->getScreenCoordinatesFrom3DPosition(
|
||||
vector3df(axisLength + axisLength*.1f, 0, 0)
|
||||
vector3df(m_AxisLength + m_AxisLength*.1f, 0, 0)
|
||||
);
|
||||
dimension2d<u32> textSize;
|
||||
if (m_AxisFont != nullptr) {
|
||||
@ -202,10 +204,10 @@ void Engine::drawAxisLines()
|
||||
}
|
||||
|
||||
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));
|
||||
textPos = m_Scene->getSceneCollisionManager()->getScreenCoordinatesFrom3DPosition(
|
||||
vector3df(0, axisLength + axisLength*.1f, 0)
|
||||
vector3df(0, m_AxisLength + m_AxisLength*.1f, 0)
|
||||
);
|
||||
if (m_AxisFont != nullptr) {
|
||||
textSize = m_AxisFont->getDimension(L"Y+");
|
||||
@ -214,10 +216,10 @@ void Engine::drawAxisLines()
|
||||
}
|
||||
|
||||
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));
|
||||
textPos = m_Scene->getSceneCollisionManager()->getScreenCoordinatesFrom3DPosition(
|
||||
vector3df(0, 0, axisLength + axisLength*.1f)
|
||||
vector3df(0, 0, m_AxisLength + m_AxisLength*.1f)
|
||||
);
|
||||
if (m_AxisFont != nullptr) {
|
||||
textSize = m_AxisFont->getDimension(L"Z+");
|
||||
@ -288,12 +290,12 @@ Engine::Engine()
|
||||
this->m_EnableWireframe = false;
|
||||
this->m_EnableLighting = false;
|
||||
this->m_EnableTextureInterpolation = true;
|
||||
this->axisLength = 10;
|
||||
this->worldFPS = 60;
|
||||
this->prevFPS = 30;
|
||||
this->textureExtensions.push_back(L"png");
|
||||
this->textureExtensions.push_back(L"jpg");
|
||||
this->textureExtensions.push_back(L"bmp");
|
||||
this->m_AxisLength = 10;
|
||||
this->m_WorldFPS = 60;
|
||||
this->m_PrevFPS = 30;
|
||||
this->m_TextureExtensions.push_back(L"png");
|
||||
this->m_TextureExtensions.push_back(L"jpg");
|
||||
this->m_TextureExtensions.push_back(L"bmp");
|
||||
#if WIN32
|
||||
m_Device = createDevice(EDT_DIRECT3D9, dimension2d<u32>(1024, 768), 32,
|
||||
false, false, false, nullptr);
|
||||
@ -364,23 +366,28 @@ vector3df Engine::camTarget()
|
||||
bool Engine::loadMesh(const wstring& fileName)
|
||||
{
|
||||
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());
|
||||
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_LoadedMesh = m_Scene->addAnimatedMeshSceneNode(mesh);
|
||||
Utility::dumpMeshInfoToConsole(m_LoadedMesh);
|
||||
std::cerr << "Arranging scene..." << std::flush;
|
||||
if (Utility::toLower(Utility::extensionOf(fileName)) == L"3ds") {
|
||||
m_View->setZUp(true);
|
||||
} else {
|
||||
m_View->setZUp(false);
|
||||
}
|
||||
if (m_LoadedMesh != nullptr) {
|
||||
std::cerr << "unloading old mesh..." << std::flush;
|
||||
ret = true;
|
||||
this->m_UserInterface->playbackFPSEditBox->setText(
|
||||
Utility::toWstring(m_LoadedMesh->getAnimationSpeed()).c_str()
|
||||
@ -431,19 +438,38 @@ bool Engine::loadMesh(const wstring& fileName)
|
||||
video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF
|
||||
);
|
||||
// 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,
|
||||
this->m_EnableTextureInterpolation);
|
||||
// Don't do anything outside of the mesh != nullptr case that will try to
|
||||
// use mesh!
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Engine::reloadMesh()
|
||||
{
|
||||
bool ret = false;
|
||||
if (this->m_PreviousPath.length() > 0) {
|
||||
ret = loadMesh(this->m_PreviousPath);
|
||||
if (this->m_LoadedMeshPath.length() > 0) {
|
||||
ret = loadMesh(this->m_LoadedMeshPath);
|
||||
}
|
||||
if (this->m_UserInterface != nullptr)
|
||||
this->m_UserInterface->OnSelectMesh();
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -547,11 +573,11 @@ std::wstring Engine::saveMesh(const io::path path, const std::string& nameOrBlan
|
||||
|
||||
void Engine::reloadTexture()
|
||||
{
|
||||
if (this->m_PrevTexturePath.length() > 0) {
|
||||
if (this->m_LoadedTexturePath.length() > 0) {
|
||||
if (wcslen(this->m_UserInterface->texturePathEditBox->getText()) == 0)
|
||||
loadTexture(this->m_UserInterface->texturePathEditBox->getText());
|
||||
else
|
||||
loadTexture(this->m_PrevTexturePath);
|
||||
loadTexture(this->m_LoadedTexturePath);
|
||||
}
|
||||
}
|
||||
|
||||
@ -564,11 +590,15 @@ bool Engine::loadTexture(const wstring& fileName)
|
||||
m_LoadedMesh->setMaterialTexture(0, texture);
|
||||
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_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;
|
||||
}
|
||||
|
||||
@ -638,7 +668,7 @@ void Engine::setMeshDisplayMode(bool wireframe, bool lighting,
|
||||
|
||||
bool Engine::isAnimating()
|
||||
{
|
||||
return this->isPlaying;
|
||||
return this->m_IsPlaying;
|
||||
}
|
||||
|
||||
void Engine::playAnimation()
|
||||
@ -648,24 +678,24 @@ void Engine::playAnimation()
|
||||
}
|
||||
if (!this->isAnimating()) {
|
||||
if (this->m_LoadedMesh != nullptr) {
|
||||
if (this->prevFPS < 1)
|
||||
this->prevFPS = 5;
|
||||
this->m_LoadedMesh->setAnimationSpeed(this->prevFPS);
|
||||
if (this->m_PrevFPS < 1)
|
||||
this->m_PrevFPS = 5;
|
||||
this->m_LoadedMesh->setAnimationSpeed(this->m_PrevFPS);
|
||||
}
|
||||
}
|
||||
this->isPlaying = true;
|
||||
this->m_IsPlaying = true;
|
||||
}
|
||||
|
||||
void Engine::pauseAnimation()
|
||||
{
|
||||
if (this->isAnimating()) {
|
||||
this->prevFPS = animationFPS();
|
||||
this->m_PrevFPS = animationFPS();
|
||||
if (this->m_LoadedMesh != nullptr) {
|
||||
this->prevFPS = this->m_LoadedMesh->getAnimationSpeed();
|
||||
this->m_PrevFPS = this->m_LoadedMesh->getAnimationSpeed();
|
||||
this->m_LoadedMesh->setAnimationSpeed(0);
|
||||
}
|
||||
}
|
||||
this->isPlaying = false;
|
||||
this->m_IsPlaying = false;
|
||||
}
|
||||
|
||||
void Engine::toggleAnimation()
|
||||
@ -682,7 +712,7 @@ void Engine::toggleAnimation()
|
||||
void Engine::setAnimationFPS(u32 animationFPS)
|
||||
{
|
||||
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
|
||||
// (it calls setAnimationFPS).
|
||||
this->m_LoadedMesh->setAnimationSpeed(animationFPS);
|
||||
@ -728,8 +758,8 @@ u32 Engine::animationFPS()
|
||||
void Engine::run()
|
||||
{
|
||||
u32 timePerFrame = 1000.0f;
|
||||
if (this->worldFPS > 0) {
|
||||
timePerFrame = static_cast<u32>(1000.0f / this->worldFPS);
|
||||
if (this->m_WorldFPS > 0) {
|
||||
timePerFrame = static_cast<u32>(1000.0f / this->m_WorldFPS);
|
||||
}
|
||||
ITimer* timer = m_Device->getTimer();
|
||||
|
||||
@ -739,7 +769,7 @@ void Engine::run()
|
||||
|
||||
checkResize();
|
||||
if (this->m_LoadedMesh != nullptr) {
|
||||
if (isPlaying) {
|
||||
if (m_IsPlaying) {
|
||||
this->m_LoadedMesh->setLoopMode(true);
|
||||
this->m_UserInterface->playbackSetFrameEditBox->setText(
|
||||
Utility::toWstring(this->m_LoadedMesh->getFrameNr()).c_str()
|
||||
|
15
Engine.h
15
Engine.h
@ -25,7 +25,6 @@ class Engine {
|
||||
friend class View;
|
||||
|
||||
private:
|
||||
std::wstring m_NextPath;
|
||||
irr::IrrlichtDevice* m_Device;
|
||||
irr::video::IVideoDriver* m_Driver;
|
||||
irr::scene::ISceneManager* m_Scene;
|
||||
@ -51,10 +50,10 @@ private:
|
||||
void checkResize();
|
||||
irr::gui::IGUIEnvironment* getGUIEnvironment() const;
|
||||
irr::s32 getNumberOfVertices();
|
||||
bool isPlaying;
|
||||
irr::u32 worldFPS;
|
||||
irr::u32 prevFPS;
|
||||
std::vector<std::wstring> textureExtensions;
|
||||
bool m_IsPlaying;
|
||||
irr::u32 m_WorldFPS;
|
||||
irr::u32 m_PrevFPS;
|
||||
std::vector<std::wstring> m_TextureExtensions;
|
||||
// Making materials in contructor or setupScene causes segfault at
|
||||
// `m_Driver->setMaterial(*lineX);` in
|
||||
// `Engine::drawAxisLines` for unknown reason:
|
||||
@ -69,9 +68,9 @@ private:
|
||||
irr::s32 LMouseState, RMouseState;
|
||||
|
||||
public:
|
||||
std::wstring m_PreviousPath;
|
||||
std::wstring m_PrevTexturePath;
|
||||
irr::f32 axisLength;
|
||||
std::wstring m_LoadedMeshPath;
|
||||
std::wstring m_LoadedTexturePath;
|
||||
irr::f32 m_AxisLength;
|
||||
bool m_zUp;
|
||||
|
||||
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)
|
||||
|
||||
|
||||
## Main Features in poikilos fork
|
||||
* stabilized (makes sure font, model or texture loads before using;
|
||||
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
|
||||
and mesh file paths only), IRRMESH (Static Irrlicht Mesh), OBJ
|
||||
(Wavefront), STL (stereolithography)
|
||||
* Turn off interpolation if loadNextTexture (F3) detects minetest
|
||||
directory structure
|
||||
(../textures/<texture filename based on model name>)
|
||||
* Turn off interpolation if loadNextTexture (F3) detects the following
|
||||
Minetest-like directory structure and texture naming:
|
||||
"<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
|
||||
minetest player model and see the effect of changing various wield
|
||||
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
|
||||
(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,
|
||||
google-droid/DroidSans-Bold.ttf
|
||||
|
||||
|
||||
## Install
|
||||
### Windows
|
||||
* 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).
|
||||
- Edit the frame rate manually using the input box under "Faster" and
|
||||
"Slower."
|
||||
* `F3` / `Shift F3`: Cycle through textures in `../textures` using `F3`
|
||||
key (`Shift` to go backward) such as for Minetest mods, where model
|
||||
must be in `modname/models/` and texture must be in
|
||||
`modname/textures/`.
|
||||
- If `"../textures/" + basename(modelName) + ".png"` or `".jpg"` is
|
||||
present, pressing `F3` 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.
|
||||
* `F3` / `Shift F3`: Cycle through textures where the filename contains
|
||||
the model filename (or that without underscores) in the current
|
||||
directory or `../textures`. If there are no matches, use a list of
|
||||
all found textures. The `F3` key goes to the next texture file (hold
|
||||
`Shift` and press`F3` to go backward), but does nothing on the first
|
||||
press if the model had loaded its own texture.
|
||||
- Example: Both automatic loading (when you open a mesh) and manually
|
||||
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
|
||||
Interpolation)
|
||||
* `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
|
||||
default; automatically changed to Z when 3ds file is loaded)
|
||||
|
||||
|
||||
## Known Issues
|
||||
* Warn on missing texture.
|
||||
* 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.
|
||||
* Lighting not correct until you rotate or enable z-Up
|
||||
|
||||
|
||||
## Authors
|
||||
* ClearSansRegular.ttf (**Apache 2.0 License**) by Intel
|
||||
<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/>
|
||||
(see [LICENSE](https://github.com/poikilos/b3view/blob/master/LICENSE)
|
||||
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;
|
||||
axisSizeEditBox = m_Gui->addEditBox(
|
||||
L"",
|
||||
std::to_wstring(this->m_Engine->m_AxisLength).c_str(),
|
||||
rect<s32>(vector2d<s32>(spacing_x, y),
|
||||
dimension2d<s32>(size_x, size_y)),
|
||||
true,
|
||||
@ -255,7 +255,7 @@ void UserInterface::displayLoadTextureDialog()
|
||||
void UserInterface::incrementFrame(f32 frameCount, bool enableRound)
|
||||
{
|
||||
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->m_LoadedMesh->setCurrentFrame(
|
||||
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)
|
||||
{
|
||||
cerr << "Loading texture..." << flush;
|
||||
bool ret = false;
|
||||
this->m_Engine->m_NextPath = 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(
|
||||
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 prevModelNoExt;
|
||||
prevModelNoExt = Utility::withoutExtension(prevModelName);
|
||||
/*
|
||||
vector<wstring> names;
|
||||
names.push_back(prevModelNoExt+L"_mesh");
|
||||
names.push_back(prevModelNoExt);
|
||||
@ -506,185 +521,148 @@ bool UserInterface::loadNextTexture(int direction)
|
||||
names.push_back(prevModelNoExt+L"_f");
|
||||
names.push_back(prevModelNoExt+L"_male");
|
||||
names.push_back(prevModelNoExt+L"_m");
|
||||
*/
|
||||
vector<wstring> badSuffixes;
|
||||
badSuffixes.push_back(L"_inv");
|
||||
|
||||
std::wstring lastDirPath = Utility::parentOfPath(
|
||||
this->m_Engine->m_PreviousPath
|
||||
this->m_Engine->m_LoadedMeshPath
|
||||
);
|
||||
std::wstring parentPath = Utility::parentOfPath(lastDirPath);
|
||||
std::wstring dirSeparator = Utility::delimiter(
|
||||
this->m_Engine->m_PreviousPath
|
||||
this->m_Engine->m_LoadedMeshPath
|
||||
);
|
||||
std::wstring texturesPath = parentPath + dirSeparator + L"textures";
|
||||
std::wstring tryTexPath = texturesPath + dirSeparator + prevModelNoExt
|
||||
+ L".png";
|
||||
if (direction == 0 && Utility::isFile(tryTexPath)) {
|
||||
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;
|
||||
vector<wstring> texturePaths;
|
||||
|
||||
if (!fs::is_directory(fs::status(path)))
|
||||
path = lastDirPath; // cycle in model's directory instead
|
||||
texturePaths.push_back(lastDirPath);
|
||||
|
||||
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"";
|
||||
std::wstring retroPath = L"";
|
||||
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) {
|
||||
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 (this->m_MatchingTextures.size() + this->m_AllTextures.size() < 1) {
|
||||
for (auto path : texturePaths) {
|
||||
for (const auto& itr : fs::directory_iterator(path)) {
|
||||
if (fs::is_regular_file(itr.status())) {
|
||||
std::wstring name = itr.path().filename().wstring();
|
||||
std::wstring suffix = Utility::getSuffix(name, dotExts,
|
||||
true);
|
||||
bool isUsable = true;
|
||||
std::wstring nameNoExt = Utility::withoutExtension(
|
||||
name
|
||||
);
|
||||
}
|
||||
for (const auto& itr : fs::directory_iterator(path)) {
|
||||
std::wstring ext = Utility::extensionOf(
|
||||
itr.path().wstring()
|
||||
); // no dot!
|
||||
if (!is_directory(itr.status())
|
||||
&& std::find(m_Engine->textureExtensions.begin(),
|
||||
m_Engine->textureExtensions.end(), ext)
|
||||
!= m_Engine->textureExtensions.end()) {
|
||||
// cycle through files (go to next after
|
||||
// m_PrevTexturePath if any previously loaded,
|
||||
// otherwise first)
|
||||
if (nextPath.length() == 0)
|
||||
nextPath = itr.path().wstring();
|
||||
lastPath = itr.path().wstring();
|
||||
if (found && direction > 0) {
|
||||
if (!force)
|
||||
nextPath = itr.path().wstring();
|
||||
break;
|
||||
if (Utility::endsWithAny(nameNoExt, badSuffixes, true))
|
||||
isUsable = false;
|
||||
if (isUsable && suffix.length() > 0) {
|
||||
this->m_AllTextures.push_back(
|
||||
path + dirSeparator + name
|
||||
);
|
||||
if (Utility::startsWith(name, prevModelNoExt)) {
|
||||
this->m_MatchingTextures.push_back(
|
||||
path + dirSeparator + name
|
||||
);
|
||||
}
|
||||
else if (name.find(prevModelNoExt) != std::wstring::npos) {
|
||||
this->m_MatchingTextures.push_back(
|
||||
path + dirSeparator + name
|
||||
);
|
||||
}
|
||||
else if (name.find(Utility::replaceAll(prevModelNoExt, L"_", L"")) != std::wstring::npos) {
|
||||
this->m_MatchingTextures.push_back(
|
||||
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
|
||||
debug() << "Can't cycle texture since no file was opened" << endl;
|
||||
cerr << (ret?"OK":"FAILED") << endl;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -705,8 +683,8 @@ void UserInterface::exportMeshToHome(std::string extension)
|
||||
std::cout << "Your PATH is: " << where.c_str() << '\n';
|
||||
}
|
||||
std::string name = "";
|
||||
if (m_Engine->m_PreviousPath.length() > 0) {
|
||||
name = Utility::toString(Utility::withoutExtension(Utility::basename(m_Engine->m_PreviousPath)));
|
||||
if (m_Engine->m_LoadedMeshPath.length() > 0) {
|
||||
name = Utility::toString(Utility::withoutExtension(Utility::basename(m_Engine->m_LoadedMeshPath)));
|
||||
}
|
||||
|
||||
wstring result = m_Engine->saveMesh(where, name, extension);
|
||||
@ -837,7 +815,7 @@ bool UserInterface::OnEvent(const SEvent& event)
|
||||
break;
|
||||
case UIE_AXISSIZEEDITBOX:
|
||||
if (ge->EventType == EGET_EDITBOX_ENTER) {
|
||||
this->m_Engine->axisLength = Utility::toF32(
|
||||
this->m_Engine->m_AxisLength = Utility::toF32(
|
||||
this->axisSizeEditBox->getText()
|
||||
);
|
||||
}
|
||||
@ -858,7 +836,7 @@ bool UserInterface::OnEvent(const SEvent& event)
|
||||
m_Engine->reloadTexture();
|
||||
}
|
||||
else {
|
||||
if (m_Engine->m_PreviousPath.length() > 0) {
|
||||
if (m_Engine->m_LoadedMeshPath.length() > 0) {
|
||||
bool result = m_Engine->reloadMesh();
|
||||
if (!result) {
|
||||
this->m_Engine->m_Device->getGUIEnvironment()->addMessageBox(
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <irrlicht/irrlicht.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// Forward declaration of class Engine
|
||||
class Engine;
|
||||
@ -74,6 +75,8 @@ private:
|
||||
|
||||
irr::gui::IGUIWindow* playbackWindow;
|
||||
irr::core::dimension2d<irr::u32> m_WindowSize; // previous size
|
||||
std::vector<std::wstring> m_AllTextures;
|
||||
std::vector<std::wstring> m_MatchingTextures;
|
||||
public:
|
||||
irr::gui::IGUIContextMenu* menu;
|
||||
irr::gui::IGUIContextMenu* fileMenu;
|
||||
@ -104,6 +107,7 @@ public:
|
||||
void drawStatusLine() const;
|
||||
bool loadNextTexture(int direction);
|
||||
void exportMeshToHome(std::string extension);
|
||||
bool OnSelectMesh();
|
||||
|
||||
// IEventReceiver
|
||||
virtual bool OnEvent(const irr::SEvent& event);
|
||||
|
139
Utility.cpp
139
Utility.cpp
@ -8,6 +8,7 @@
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <assert.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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (node == nullptr) {
|
||||
@ -55,11 +71,7 @@ void Utility::dumpMeshInfoToConsole(IAnimatedMeshSceneNode* node)
|
||||
<< material.Shininess << endl;
|
||||
|
||||
// check for # textures
|
||||
int textures = 0;
|
||||
for (irr::u32 ti = 0; ti < MATERIAL_MAX_TEXTURES; ti++)
|
||||
if (material.getTexture(ti) != nullptr)
|
||||
textures++;
|
||||
debug() << "[MESH]: # of textures : " << textures << endl;
|
||||
debug() << "[MESH]: # of textures : " << Utility::getTextureCount(material) << endl;
|
||||
}
|
||||
}
|
||||
|
||||
@ -140,6 +152,44 @@ bool Utility::startsWith(const std::wstring& haystack, const std::wstring& needl
|
||||
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 found = false;
|
||||
if (haystack.length() >= needle.length()) {
|
||||
@ -150,6 +200,48 @@ bool Utility::endsWith(const std::wstring& haystack, const std::wstring& needle)
|
||||
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.
|
||||
/// allIfNotFound: Return whole string on no delimiter, vs empty string.
|
||||
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
|
||||
// // <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 <string>
|
||||
#include <vector>
|
||||
|
||||
class Utility {
|
||||
public:
|
||||
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 std::wstring parentOfPath(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 rightOfLast(const std::wstring& path, const std::wstring& delimiter, bool allIfNotFound);
|
||||
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 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 extensionOf(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
|
||||
|
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