implement playback controls

master
poikilos 2019-03-08 00:52:29 -05:00
parent ec30dae805
commit 1a77f1cc33
12 changed files with 380 additions and 44 deletions

View File

@ -1,5 +1,10 @@
# Changelog
## [git] - 2019-03-07
(poikilos)
### Added
* playback controls
## [git] - 2019-03-06
(poikilos)
### Added

View File

@ -124,6 +124,8 @@ s32 Engine::getNumberOfVertices()
Engine::Engine()
{
this->worldFPS = 60;
this->prevFPS = 30;
#if WIN32
m_Device = createDevice( EDT_DIRECT3D9, dimension2d<u32>( 1024, 768 ), 32, false, false, false, nullptr );
#else
@ -170,6 +172,7 @@ Engine::Engine()
m_WindowSize = new dimension2d<u32>();
m_WindowSize->Width = m_Driver->getScreenSize().Width;
m_WindowSize->Height = m_Driver->getScreenSize().Height;
this->playAnimation();
}
Engine::~Engine()
@ -187,8 +190,8 @@ void Engine::loadMesh( const wstring &fileName )
//fn.assign(fileName.c_str());
// std::wcerr << "fileName = " << fn << endl;
// std::wcerr << "fileName = " << fileName << endl;
this->m_EventHandler->m_PreviousPath = fileName;
// std::wcerr << "this->m_EventHandler->m_PreviousPath = " << this->m_EventHandler->m_PreviousPath.c_str() << endl;
this->m_PreviousPath = fileName;
// std::wcerr << "this->m_PreviousPath = " << this->m_PreviousPath.c_str() << endl;
// }
if( m_LoadedMesh != nullptr )
m_LoadedMesh->remove();
@ -199,9 +202,9 @@ void Engine::loadMesh( const wstring &fileName )
void Engine::reloadMesh()
{
if (this->m_EventHandler->m_PreviousPath.length() > 0) {
std::wcerr << "this->m_EventHandler->m_PreviousPath = " << this->m_EventHandler->m_PreviousPath.c_str() << endl;
loadMesh(this->m_EventHandler->m_PreviousPath);
if (this->m_PreviousPath.length() > 0) {
std::wcerr << "this->m_PreviousPath = " << this->m_PreviousPath.c_str() << endl;
loadMesh(this->m_PreviousPath);
}
}
@ -231,18 +234,89 @@ void Engine::setMeshDisplayMode( bool wireframe, bool lighting )
}
}
bool Engine::isAnimating()
{
return this->isPlaying;
}
void Engine::playAnimation()
{
if (this->animationFPS() < 1) {
this->setAnimationFPS(5);
}
if (!this->isAnimating()) {
if (this->m_LoadedMesh != nullptr) {
if (this->prevFPS < 1) this->prevFPS = 5;
this->m_LoadedMesh->setAnimationSpeed(this->prevFPS);
}
}
this->isPlaying = true;
}
void Engine::pauseAnimation()
{
if (this->isAnimating()) {
this->prevFPS = animationFPS();
if (this->m_LoadedMesh != nullptr) {
this->prevFPS = this->m_LoadedMesh->getAnimationSpeed();
this->m_LoadedMesh->setAnimationSpeed(0);
}
}
this->isPlaying = false;
}
void Engine::toggleAnimation()
{
if (this->isAnimating()) {
this->pauseAnimation();
debug() << "paused " << this->animationFPS() << "fps" << endl;
}
else {
this->playAnimation();
debug() << "unpaused " << this->animationFPS() << "fps" << endl;
}
}
void Engine::setAnimationFPS(u32 animationFPS)
{
if (this->m_LoadedMesh != nullptr) {
this->m_LoadedMesh->setAnimationSpeed(animationFPS);
}
}
u32 Engine::animationFPS()
{
u32 ret = 0;
if (this->m_LoadedMesh != nullptr) {
ret = this->m_LoadedMesh->getAnimationSpeed();
}
return ret;
}
void Engine::run()
{
u32 timePerFrame = ( u32 ) ( 1000.0f / 60 );
u32 timePerFrame = 1000.0f;
if (this->worldFPS > 0) {
timePerFrame = ( u32 ) ( 1000.0f / this->worldFPS );
}
ITimer *timer = m_Device->getTimer();
// Run the Device with 60 frames/sec
// Run the Device with fps frames/sec
while( m_Device->run() && m_RunEngine )
{
u32 startTime = timer->getRealTime();
checkResize();
if (this->m_LoadedMesh != nullptr) {
//this->m_LoadedMesh->setAnimationSpeed(this->fps);
if (isPlaying) {
this->m_LoadedMesh->setLoopMode(true);
}
else {
this->m_LoadedMesh->setLoopMode(false);
}
}
m_Driver->beginScene();
drawBackground(); // Draw Background
drawAxisLines(); // Draw XYZ Axis

View File

@ -29,6 +29,7 @@ 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,8 +52,14 @@ private:
void checkResize();
irr::gui::IGUIEnvironment *getGUIEnvironment() const;
irr::s32 getNumberOfVertices();
bool isPlaying;
irr::u32 worldFPS;
irr::u32 prevFPS;
public:
std::wstring m_PreviousPath;
Engine();
~Engine();
@ -61,6 +68,12 @@ public:
void reloadMesh();
void loadTexture( const std::wstring &fileName );
void setMeshDisplayMode( bool wireframe = false, bool lighting = true );
bool isAnimating();
void playAnimation();
void pauseAnimation();
void toggleAnimation();
void setAnimationFPS(irr::u32 animationFPS);
irr::u32 animationFPS();
};
#endif // ENGINE_H

View File

@ -11,14 +11,6 @@ using namespace irr::gui;
// Public
EventHandler::EventHandler( IrrlichtDevice *device )
{
// For monitoring single press: see
// <http://irrlicht.sourceforge.net/forum/viewtopic.php?p=210744>
for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
KeyIsDown[i] = false;
for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
keyState[i] = 0;
LMouseState = 0;
RMouseState = 0;
m_Device = device;
m_EventReceivers = new map<EventReceiverType, IEventReceiver *>();
}
@ -49,16 +41,8 @@ bool EventHandler::OnEvent( const SEvent &event )
iter->second->OnEvent( event );
}
else if (event.EventType == EET_KEY_INPUT_EVENT) {
if (event.KeyInput.PressedDown && !KeyIsDown[event.KeyInput.Key]) {
std::wstring basePath = L".";
if (this->m_PreviousPath.length() > 0) {
// std::wcerr << "this->m_PreviousPath: " << this->m_PreviousPath.c_str() << endl;
std::wstring lastDirPath = Utility::parentOfPath(m_PreviousPath);
std::wcerr << "lastDirPath: " << lastDirPath << endl;
}
else debug() << "Keydown" << endl;
}
KeyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
map<EventReceiverType,IEventReceiver *>::iterator iter = m_EventReceivers->find( ERT_USERINTERFACE );
iter->second->OnEvent( event );
}
else if (event.EventType == EET_USER_EVENT)
{

View File

@ -30,9 +30,6 @@ class EventHandler : public irr::IEventReceiver
private:
irr::IrrlichtDevice *m_Device;
map<EventReceiverType, IEventReceiver*> *m_EventReceivers;
bool KeyIsDown[irr::KEY_KEY_CODES_COUNT];
irr::s32 keyState[irr::KEY_KEY_CODES_COUNT];
irr::s32 LMouseState,RMouseState;
public:
EventHandler( irr::IrrlichtDevice *device );
~EventHandler();
@ -41,7 +38,6 @@ public:
// IEventReceiver
virtual bool OnEvent( const irr::SEvent &event );
std::wstring m_PreviousPath;
};
#endif // EVENTHANDLER_H

View File

@ -55,6 +55,10 @@ This is a modernized fork by poikilos (see CHANGELOG.md).
file. However, the program is much easier to use if you associate the
format with b3view (see "Installation" above) so you can just double-
click the file to open b3view automatically.
* To change frame rate in increments of 5 fps, click "Faster" or
"Slower," or use `-` key or `+`/`=` key. By default, the world runs
at 60fps and the animation runs as 30 fps (Irrlicht does interpolation
automatically).
## Known Issues
* Warn on missing texture.

View File

@ -27,18 +27,39 @@ void UserInterface::setupUserInterface()
viewMenu->addItem( L"Wireframe Mesh", UIC_VIEW_WIREFRAME, true, false, false, true );
viewMenu->addItem( L"Lighting",UIC_VIEW_LIGHTING, true, false, true, true );
// TODO: Playback Control Window
// dimension2d<u32> windowSize = m_Engine->m_Driver->getScreenSize();
// IGUIWindow *playbackWindow = m_Gui->addWindow(
// rect<s32>( vector2d<s32>( windowSize.Width - 4 - 160, 28 ), dimension2d<s32>( 160, 300 )), false, L"Playback", nullptr, UIE_PLAYBACKWINDOW );
// playbackWindow->getCloseButton()->setVisible( false );
// IGUIButton *playbackStartStopButton = m_Gui->addButton(
// rect<s32>( vector2d<s32>( 4, 24 ), dimension2d<s32>( playbackWindow->getClientRect().getWidth() - 8, 24 )),
// playbackWindow,
// UIE_PLAYBACKSTARTSTOPBUTTON,
// L"Start/Stop",
// nullptr
// );
// Playback Control Window
dimension2d<u32> windowSize = m_Engine->m_Driver->getScreenSize();
IGUIWindow *playbackWindow = m_Gui->addWindow(
rect<s32>( vector2d<s32>( windowSize.Width - 4 - 160, 28 ), dimension2d<s32>( 160, 300 )), false, L"Playback", nullptr, UIE_PLAYBACKWINDOW );
playbackWindow->getCloseButton()->setVisible( false );
s32 spacing_x = 4;
s32 spacing_y = 4;
s32 size_x = playbackWindow->getClientRect().getWidth() - 8;
s32 size_y = 24;
s32 y = 24;
playbackStartStopButton = m_Gui->addButton(
rect<s32>( vector2d<s32>( spacing_x, y ), dimension2d<s32>( size_x, size_y )),
playbackWindow,
UIE_PLAYBACKSTARTSTOPBUTTON,
L"Start/Stop",
nullptr
);
y += size_y + spacing_y;
playbackIncreaseButton = m_Gui->addButton(
rect<s32>( vector2d<s32>( spacing_x, y ), dimension2d<s32>( size_x, size_y )),
playbackWindow,
UIE_PLAYBACKINCREASEBUTTON,
L"Faster",
nullptr
);
y += size_y + spacing_y;
playbackDecreaseButton = m_Gui->addButton(
rect<s32>( vector2d<s32>( spacing_x, y ), dimension2d<s32>( size_x, size_y )),
playbackWindow,
UIE_PLAYBACKDECREASEBUTTON,
L"Slower",
nullptr
);
// Set Font for UI Elements
m_GuiFontFace = new CGUITTFace();
@ -104,6 +125,16 @@ void UserInterface::handleMenuItemPressed( IGUIContextMenu *menu )
// PUBLIC
UserInterface::UserInterface( Engine *engine )
{
this->playbackStartStopButton = nullptr;
// For monitoring single press: see
// <http://irrlicht.sourceforge.net/forum/viewtopic.php?p=210744>
for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
KeyIsDown[i] = false;
for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
keyState[i] = 0;
LMouseState = 0;
RMouseState = 0;
m_Engine = engine;
m_Gui = engine->getGUIEnvironment();
@ -132,11 +163,90 @@ void UserInterface::drawStatusLine() const
bool UserInterface::OnEvent( const SEvent &event )
{
// Events arriving here should be destined for us
if (!(event.EventType == EET_GUI_EVENT))
if (event.EventType == EET_KEY_INPUT_EVENT) {
if (event.KeyInput.PressedDown && !KeyIsDown[event.KeyInput.Key]) {
if (event.KeyInput.Key == irr::KEY_KEY_T) {
this->m_Engine->m_NextPath = L"";
std::wstring basePath = L".";
if (this->m_Engine->m_PreviousPath.length() > 0) {
// std::wcerr << "this->m_PreviousPath: " << this->m_PreviousPath.c_str() << endl;
std::wstring lastName = Utility::basename(this->m_Engine->m_PreviousPath);
std::wstring lastDirPath = Utility::parentOfPath(this->m_Engine->m_PreviousPath);
// std::wcerr << "lastDirPath: " << lastDirPath << endl;
std::wstring parentPath = Utility::parentOfPath(lastDirPath);
// std::wcerr << "parentPath: " << parentPath << endl;
std::wstring dirSeparator = Utility::delimiter(this->m_Engine->m_PreviousPath);
std::wstring texturesPath = parentPath + dirSeparator + L"textures";
// std::wcerr << "lastName: " << lastName << endl;
// std::wcerr << "pathWithoutExt: " << Utility::withoutExtension(m_PreviousPath) << endl;
// std::wcerr << "nameWithoutExt: " << Utility::withoutExtension(lastName) << endl;
std::wstring tryTexPath = texturesPath + dirSeparator + Utility::withoutExtension(lastName) + L".png";
if (Utility::exists(tryTexPath)) {
this->m_Engine->m_NextPath = tryTexPath;
this->m_Engine->loadTexture(this->m_Engine->m_NextPath);
}
}
else debug() << "Can't cycle texture since no file was opened" << endl;
}
else if (event.KeyInput.Char == L'+' || event.KeyInput.Char == L'=') {
m_Engine->setAnimationFPS(m_Engine->animationFPS() + 5);
// std::wcerr << "m_Engine->animationFPS(): " << m_Engine->animationFPS() << endl;
}
else if (event.KeyInput.Char == L'-') {
if (m_Engine->animationFPS() > 0) {
m_Engine->setAnimationFPS(m_Engine->animationFPS() - 5);
}
// std::wcerr << "m_Engine->animationFPS(): " << m_Engine->animationFPS() << endl;
}
else if (event.KeyInput.Char == L' ') {
m_Engine->toggleAnimation();
// std::wcerr << "m_Engine->animationFPS(): " << m_Engine->animationFPS() << endl;
}
// std::wcerr << "Char: " << event.KeyInput.Char << endl;
}
KeyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
return true;
}
else if (event.EventType == EET_MOUSE_INPUT_EVENT)
{
switch ( event.MouseInput.Event)
{
case EMIE_LMOUSE_LEFT_UP:
if ( LMouseState == 2)
{
LMouseState = 3;
}
break;
case EMIE_LMOUSE_PRESSED_DOWN:
if ( LMouseState == 0)
{
LMouseState = 1;
}
break;
case EMIE_RMOUSE_LEFT_UP:
if ( RMouseState == 2)
{
RMouseState = 3;
}
break;
case EMIE_RMOUSE_PRESSED_DOWN:
if ( RMouseState == 0)
{
RMouseState = 1;
}
break;
}
}
else if (!(event.EventType == EET_GUI_EVENT))
return false;
const SEvent::SGUIEvent *ge = &( event.GUIEvent );
bool isPress = false;
switch( ge->Caller->getID() )
{
case UIE_FILEMENU:
@ -161,6 +271,26 @@ bool UserInterface::OnEvent( const SEvent &event )
}
break;
case UIE_PLAYBACKSTARTSTOPBUTTON:
if ( ge->EventType == EGET_BUTTON_CLICKED) {
this->m_Engine->toggleAnimation();
}
break;
case UIE_PLAYBACKINCREASEBUTTON:
if ( ge->EventType == EGET_BUTTON_CLICKED) {
this->m_Engine->setAnimationFPS(this->m_Engine->animationFPS() + 5);
}
break;
case UIE_PLAYBACKDECREASEBUTTON:
if ( ge->EventType == EGET_BUTTON_CLICKED) {
if (this->m_Engine->animationFPS() >= 5) {
this->m_Engine->setAnimationFPS(this->m_Engine->animationFPS() - 5);
}
}
break;
default:
break;
}

View File

@ -21,7 +21,9 @@ enum UserInterfaceElements
UIE_FILEMENU = 1003,
UIE_PLAYBACKSTARTSTOPBUTTON = 1004,
UIE_VIEWMENU = 1005,
UIE_LOADTEXTUREDIALOG = 1006
UIE_LOADTEXTUREDIALOG = 1006,
UIE_PLAYBACKINCREASEBUTTON = 1007,
UIE_PLAYBACKDECREASEBUTTON = 1008
};
enum UserInterfaceCommands
@ -49,7 +51,15 @@ private:
bool m_WireframeDisplay;
bool m_Lighting;
bool KeyIsDown[irr::KEY_KEY_CODES_COUNT];
irr::s32 keyState[irr::KEY_KEY_CODES_COUNT];
irr::s32 LMouseState,RMouseState;
public:
irr::gui::IGUIButton *playbackStartStopButton;
irr::gui::IGUIButton *playbackIncreaseButton;
irr::gui::IGUIButton *playbackDecreaseButton;
UserInterface( Engine *device );
~UserInterface();
irr::gui::IGUIEnvironment *getGUIEnvironment() const;

View File

@ -1,4 +1,11 @@
#include <string>
#include <iostream>
#include <clocale>
#include <locale>
#include <vector>
#include "Utils.h"
using namespace irr::core;
using namespace irr::scene;
using namespace irr::video;
@ -59,4 +66,89 @@ std::wstring Utility::parentOfPath(const wstring &path)
return ret;
}
wstring Utility::basename(const wstring &path)
{
std::wstring ret = path;
std::wstring::size_type lastSlashPos = path.find_last_of(L"/");
if (lastSlashPos == std::wstring::npos) {
std::wstring::size_type lastSlashPos = path.find_last_of(L"\\");
}
if (lastSlashPos != std::wstring::npos) {
ret = path.substr(lastSlashPos+1);
}
return ret;
}
wstring Utility::withoutExtension(const wstring &path)
{
std::wstring ret = path;
std::wstring::size_type lastDotPos = path.find_last_of(L".");
if (lastDotPos != std::wstring::npos) {
std::wstring::size_type lastSlashPos = path.find_last_of(L"/");
if (lastSlashPos == std::wstring::npos) {
std::wstring::size_type lastSlashPos = path.find_last_of(L"\\");
}
if (lastSlashPos != std::wstring::npos) {
if (lastDotPos > lastSlashPos) ret = path.substr(0, lastDotPos);
}
else ret = path.substr(0, lastDotPos);
}
return ret;
}
wstring Utility::delimiter(const wstring &path)
{
std::wstring ret = L"/";
std::wstring::size_type lastSlashPos = path.find_last_of(L"/");
if (lastSlashPos == std::wstring::npos) {
// ret = "/";
}
else {
std::wstring::size_type lastSlashPos = path.find_last_of(L"\\");
if (lastSlashPos != std::wstring::npos) {
ret = L"\\";
}
}
return ret;
}
bool Utility::exists(const std::string& name) {
if (FILE *file = fopen(name.c_str(), "r")) {
fclose(file);
return true;
} else {
return false;
}
}
std::string Utility::toString(const std::wstring& ws) {
std::string ret;
// convert to w_string using locale: see Phillipp on <https://stackoverflow.com/questions/4804298/how-to-convert-wstring-into-string>
std::setlocale(LC_ALL, "");
const std::locale locale("");
typedef std::codecvt<wchar_t, char, std::mbstate_t> converter_type;
const converter_type& converter = std::use_facet<converter_type>(locale);
std::vector<char> to(ws.length() * converter.max_length());
std::mbstate_t state;
const wchar_t* from_next;
char* to_next;
const converter_type::result result = converter.out(state, ws.data(), ws.data() + ws.length(), from_next, &to[0], &to[0] + to.size(), to_next);
if (result == converter_type::ok or result == converter_type::noconv) {
const std::string s(&to[0], to_next);
std::cout <<"std::string = "<<s<<std::endl;
ret += s;
}
return ret;
}
bool Utility::exists(const std::wstring& name) {
std::string name_s = toString(name);
if (FILE *file = fopen(name_s.c_str(), "r")) {
fclose(file);
return true;
} else {
return false;
}
}

View File

@ -11,6 +11,12 @@ public:
static void dumpVectorToConsole( const irr::core::vector3df &vector );
static void dumpMeshInfoToConsole( irr::scene::IAnimatedMeshSceneNode *node );
static std::wstring parentOfPath(const std::wstring &path);
static std::wstring basename(const std::wstring &path);
static std::wstring withoutExtension(const std::wstring &path);
static std::wstring delimiter(const std::wstring &path);
static bool exists(const std::string &name);
static bool exists(const std::wstring &name);
static std::string toString(const std::wstring &name);
};
#endif // UTILS_H

View File

@ -125,6 +125,17 @@ if [ -f "$mime_path" ]; then
echo "Successfully copied '$MIMETYPES_DB_PATH/packages/$mime_name'"
fi
# Since OBJ Mime type is broken on linux (detected as TGIF), trying
# to add an overlapping mime type breaks it worse (KDE detects the
# file as "plain text file" if the xml file below is installed)
mime_name=model-obj.xml
mime_path="$mimes_path/$mime_name"
# cp -f "$mime_path" "$MIMETYPES_DB_PATH/packages/"
# if [ -f "$MIMETYPES_DB_PATH/packages/$mime_name" ]; then
# echo "Successfully copied '$MIMETYPES_DB_PATH/packages/$mime_name'"
# rm -f "$MIMETYPES_DB_PATH/packages/$mime_name"
# fi
echo "Updating mime type database '$MIMETYPES_DB_PATH'..."
update-mime-database "$MIMETYPES_DB_PATH" # must contain packages
else

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
<mime-type type="model/obj">
<comment>OBJ file</comment>
<icon name="model-obj"/>
<glob-deleteall/>
<remove fileExtension=".obj" />
<mimeMap fileExtension=".obj" mimeType="text/plain" />
<glob pattern="*.obj"/>
</mime-type>
</mime-info>