SAM-Viewer/src/viewer.cpp

717 lines
19 KiB
C++

#include <stdlib.h>
#include <iostream>
#include <sstream>
#include <irrlicht.h>
#include "config.h"
#include "scene.h"
#include "trackball.h"
#include "gui.h"
#include "dialog.h"
#include "controls.h"
#include "viewer.h"
#define M_ZOOM_IN(fov) std::max(fov - DEGTORAD * 2, PI * 0.0125f)
#define M_ZOOM_OUT(fov) std::min(fov + DEGTORAD * 2, PI * 0.5f)
Viewer::Viewer(Config *conf) :
conf(conf),
device(0),
camera(0),
scene(0),
trackball(0),
gui(0),
animation(0)
{}
Viewer::~Viewer()
{
if (scene)
scene->drop();
if (trackball)
delete trackball;
if (gui)
delete gui;
if (animation)
delete animation;
}
bool Viewer::run(IrrlichtDevice *irr_device)
{
device = irr_device;
device->getFileSystem()->addFileArchive("../assets/");
device->getFileSystem()->addFileArchive("../media/");
device->getFileSystem()->changeWorkingDirectoryTo("../media/");
device->setEventReceiver(this);
IVideoDriver *driver = device->getVideoDriver();
ISceneManager *smgr = device->getSceneManager();
IGUIEnvironment *env = device->getGUIEnvironment();
screen = driver->getScreenSize();
trackball = new Trackball(screen.Width, screen.Height);
scene = new Scene(smgr->getRootSceneNode(), smgr, E_SCENE_ID);
scene->addAnimator(trackball);
gui = new GUI(device, conf);
gui->initMenu();
gui->initToolBar();
if (!scene->load(conf))
return false;
animation = new AnimState(env);
animation->load(scene->getNode(E_SCENE_ID_MODEL));
animation->setField(E_GUI_ID_ANIM_START, conf->getInt("anim_start"));
animation->setField(E_GUI_ID_ANIM_END, conf->getInt("anim_end"));
animation->setField(E_GUI_ID_ANIM_SPEED, conf->getInt("anim_speed"));
animation->setField(E_GUI_ID_ANIM_FRAME, conf->getInt("anim_start"));
scene->setAnimation(conf->getInt("anim_start"),
conf->getInt("anim_start"), conf->getInt("anim_speed"));
camera = smgr->addCameraSceneNode(0, vector3df(0,0,30), vector3df(0,0,0));
fov = camera->getFOV();
fov_home = fov;
jump_time = 0;
setCaptionFileName(conf->getCStr("model_mesh"));
setBackgroundColor(conf->getHex("bg_color"));
setProjection();
while (device->run())
{
resize();
driver->beginScene(true, true, bg_color);
smgr->drawAll();
env->drawAll();
driver->endScene();
animation->update(scene->getNode(E_SCENE_ID_MODEL));
}
return true;
}
void Viewer::resize()
{
IVideoDriver *driver = device->getVideoDriver();
dimension2du dim = driver->getScreenSize();
if (screen == dim)
return;
const vector2di move = vector2di(dim.Width - screen.Width, 0);
gui->moveElement(E_GUI_ID_TOOLBOX_MODEL, move);
gui->moveElement(E_GUI_ID_ANIM_CTRL, move);
screen = dim;
trackball->setBounds(screen.Width, screen.Height);
camera->setAspectRatio((f32)screen.Width / (f32)screen.Height);
setProjection();
}
void Viewer::setProjection()
{
f32 width = (f32)screen.Width * fov / 20.0f;
f32 height = (f32)screen.Height * fov / 20.0f;
ortho.buildProjectionMatrixOrthoLH(width, height, 1.0f, 1000.f);
if (conf->getBool("ortho"))
camera->setProjectionMatrix(ortho, true);
else
camera->setFOV(fov);
}
void Viewer::setBackgroundColor(const u32 &color)
{
bg_color.color = color;
bg_color.setAlpha(255);
}
void Viewer::setCaptionFileName(const io::path &filename)
{
io::IFileSystem *fs = device->getFileSystem();
stringw caption = fs->getFileBasename(filename) + L" - SAM-Viewer";
device->setWindowCaption(caption.c_str());
}
void Viewer::exportStaticMesh(const char *caption, const char **filters,
const int filter_count, EMESH_WRITER_TYPE id)
{
io::IFileSystem *fs = device->getFileSystem();
const char *fn = dialog::fileSaveDialog(fs, caption, filters,
filter_count);
if (!fn || stringc(fn).empty())
return;
u32 flags = conf->getInt("export_flags");
u32 scale = conf->getInt("export_scale");
IAnimatedMeshSceneNode *clone = 0;
IAnimatedMeshSceneNode *model =
(IAnimatedMeshSceneNode*)scene->getNode(E_SCENE_ID_MODEL);
ISceneManager *smgr = device->getSceneManager();
IMesh *mesh = model->getMesh();
io::IWriteFile *file;
IMeshWriter *writer;
if (!(flags & E_MESH_EXPORT_ANIM))
{
clone = (IAnimatedMeshSceneNode*)model->clone();
mesh = clone->getMesh();
}
file = fs->createAndWriteFile(fn);
writer = smgr->createMeshWriter(id);
writer->writeMesh(file, mesh);
writer->drop();
file->drop();
if (clone)
clone->remove();
if (scale == 100 && (flags == 0 || flags == E_MESH_EXPORT_ANIM))
return;
IMeshManipulator *manip = smgr->getMeshManipulator();
mesh = smgr->getMesh(fn);
if (flags & E_MESH_EXPORT_FLIP)
manip->flipSurfaces(mesh);
if (flags & E_MESH_EXPORT_TRANSFORM)
manip->transform(mesh, model->getRelativeTransformation());
if (scale != 100)
{
f32 sf = (f32)scale / 100.f;
manip->scale(mesh, vector3df(sf, sf, sf));
}
if (scale != 100 || flags & E_MESH_EXPORT_NORMAL)
manip->recalculateNormals(mesh);
file = fs->createAndWriteFile(fn);
writer = smgr->createMeshWriter(id);
writer->writeMesh(file, mesh);
writer->drop();
file->drop();
}
static inline std::string boolToString(bool b)
{
return (b) ? "true" : "false";
}
static inline std::string vectorToString(vector3df v)
{
std::ostringstream ss;
ss << v.X << "," << v.Y << "," << v.Z;
std::string str(ss.str());
return str;
}
bool Viewer::OnEvent(const SEvent &event)
{
if (event.EventType == EET_GUI_EVENT)
{
if (event.GUIEvent.EventType == EGET_MENU_ITEM_SELECTED)
{
IGUIContextMenu *menu = (IGUIContextMenu*)event.GUIEvent.Caller;
s32 item = menu->getSelectedItem();
s32 id = menu->getItemCommandId(item);
io::IFileSystem *fs = device->getFileSystem();
switch (id)
{
case E_GUI_ID_LOAD_MODEL_MESH:
{
const char *fn = dialog::fileOpenDialog(fs,
"Open main model file", dialog::model_filters,
dialog::model_filter_count);
if (fn && !stringc(fn).empty() && scene->loadModelMesh(fn))
{
ISceneNode *model = scene->getNode(E_SCENE_ID_MODEL);
if (model)
{
animation->load(model);
setCaptionFileName(fn);
gui->reloadToolBox(E_GUI_ID_TOOLBOX_MODEL);
conf->set("model_mesh", fn);
}
}
break;
}
case E_GUI_ID_LOAD_WIELD_MESH:
{
const char *fn = dialog::fileOpenDialog(fs,
"Open wield model file", dialog::model_filters,
dialog::model_filter_count);
if (fn && !stringc(fn).empty() && scene->loadWieldMesh(fn))
{
gui->reloadToolBox(E_GUI_ID_TOOLBOX_WIELD);
conf->set("wield_mesh", fn);
}
break;
}
case E_GUI_ID_EXPORT_MESH_IRR:
{
const char *filters[] = {"*.irrmesh"};
exportStaticMesh("Export Irrlicht Mesh",
filters, 1, EMWT_IRR_MESH);
break;
}
case E_GUI_ID_EXPORT_MESH_COL:
{
const char *filters[] = {"*.dae", "*.xml"};
exportStaticMesh("Export Collada Mesh",
filters, 2, EMWT_COLLADA);
break;
}
case E_GUI_ID_EXPORT_MESH_STL:
{
const char *filters[] = {"*.stl"};
exportStaticMesh("Export STL Mesh",
filters, 1, EMWT_STL);
break;
}
case E_GUI_ID_EXPORT_MESH_OBJ:
{
const char *filters[] = {"*.obj"};
exportStaticMesh("Export Wavefront Mesh",
filters, 1, EMWT_OBJ);
break;
}
case E_GUI_ID_EXPORT_MESH_PLY:
{
const char *filters[] = {"*.ply"};
exportStaticMesh("Export Polygon File",
filters, 1, EMWT_PLY);
break;
}
case E_GUI_ID_ENABLE_WIELD:
{
ISceneNode *wield = scene->getNode(E_SCENE_ID_WIELD);
if (wield)
{
wield->setVisible(menu->isItemChecked(item));
conf->set("wield_show",
boolToString(menu->isItemChecked(item)));
}
break;
}
case E_GUI_ID_QUIT:
device->closeDevice();
break;
case E_DIALOG_ID_TEXTURES:
gui->showTexturesDialog();
break;
case E_DIALOG_ID_SETTINGS:
gui->showSettingsDialog();
break;
case E_DIALOG_ID_LIGHTS:
gui->showLightsDialog();
break;
case E_GUI_ID_TOOLBOX_MODEL:
{
if (menu->isItemChecked(item))
gui->showToolBox(E_GUI_ID_TOOLBOX_MODEL);
else
gui->closeToolBox(E_GUI_ID_TOOLBOX_MODEL);
break;
}
case E_GUI_ID_TOOLBOX_WIELD:
{
if (menu->isItemChecked(item))
gui->showToolBox(E_GUI_ID_TOOLBOX_WIELD);
else
gui->closeToolBox(E_GUI_ID_TOOLBOX_WIELD);
break;
}
case E_GUI_ID_SHOW_GRID:
scene->setGridVisible(menu->isItemChecked(item));
menu->setItemEnabled(item + 1, menu->isItemChecked(item));
break;
case E_GUI_ID_SHOW_AXES:
scene->setAxesVisible(menu->isItemChecked(item));
break;
case E_GUI_ID_SHOW_LIGHTS:
scene->setLightsVisible(menu->isItemChecked(item));
break;
case E_GUI_ID_BILINEAR:
scene->setFilter(EMF_BILINEAR_FILTER,
menu->isItemChecked(item));
conf->set("bilinear",
boolToString(menu->isItemChecked(item)));
break;
case E_GUI_ID_TRILINEAR:
scene->setFilter(EMF_TRILINEAR_FILTER,
menu->isItemChecked(item));
conf->set("trilinear",
boolToString(menu->isItemChecked(item)));
break;
case E_GUI_ID_ANISOTROPIC:
scene->setFilter(EMF_ANISOTROPIC_FILTER,
menu->isItemChecked(item));
conf->set("anisotropic",
boolToString(menu->isItemChecked(item)));
break;
case E_GUI_ID_PERSPECTIVE:
menu->setItemChecked(item + 1, !menu->isItemChecked(item));
conf->set("ortho", boolToString(!menu->isItemChecked(item)));
setProjection();
break;
case E_GUI_ID_ORTHOGONAL:
menu->setItemChecked(item - 1, !menu->isItemChecked(item));
conf->set("ortho", boolToString(menu->isItemChecked(item)));
setProjection();
break;
case E_GUI_ID_BACK_FACE_CULL:
scene->setBackFaceCulling(menu->isItemChecked(item));
conf->set("backface_cull",
boolToString(menu->isItemChecked(item)));
break;
case E_GUI_ID_LIGHT:
case E_GUI_ID_LIGHT + 1:
case E_GUI_ID_LIGHT + 2:
scene->setLightEnabled(menu->getSelectedItem(),
menu->isItemChecked(item));
conf->set("light_enabled_" +
std::to_string(menu->getSelectedItem() + 1),
boolToString(menu->isItemChecked(item)));
break;
case E_GUI_ID_LIGHTING:
scene->setLighting(menu->isItemChecked(item));
conf->set("lighting", boolToString(menu->isItemChecked(item)));
menu->setItemEnabled(5, menu->isItemChecked(item));
menu->setItemEnabled(9, menu->isItemChecked(item));
break;
case E_GUI_ID_DEBUG_INFO:
scene->setDebugInfo(menu->isItemChecked(item));
conf->set("debug_info",
boolToString(menu->isItemChecked(item)));
break;
case E_DIALOG_ID_ABOUT:
gui->showAboutDialog();
break;
case E_GUI_ID_SAVE_CONFIG:
{
ISceneNode *model = scene->getNode(E_SCENE_ID_MODEL);
if (model)
{
conf->set("model_position",
vectorToString(model->getPosition()));
conf->set("model_rotation",
vectorToString(model->getRotation()));
conf->set("model_scale",
std::to_string(model->getScale().Y * 100));
conf->set("model_material",
std::to_string(model->getMaterial(0).MaterialType));
}
ISceneNode *wield = scene->getNode(E_SCENE_ID_WIELD);
if (wield)
{
conf->set("wield_position",
vectorToString(wield->getPosition()));
conf->set("wield_rotation",
vectorToString(wield->getRotation()));
conf->set("wield_scale",
std::to_string(wield->getScale().Y * 100));
conf->set("wield_material",
std::to_string(wield->getMaterial(0).MaterialType));
}
conf->set("anim_start",
std::to_string(animation->getField(E_GUI_ID_ANIM_START)));
conf->set("anim_end",
std::to_string(animation->getField(E_GUI_ID_ANIM_END)));
conf->set("anim_speed",
std::to_string(animation->getField(E_GUI_ID_ANIM_SPEED)));
conf->save();
break;
}
default:
break;
}
}
else if (event.GUIEvent.EventType == EGET_ELEMENT_CLOSED)
{
IGUIContextMenu *menu =
(IGUIContextMenu*)gui->getElement(E_GUI_ID_MENU);
if (menu)
{
s32 id = event.GUIEvent.Caller->getID();
if (id == E_GUI_ID_TOOLBOX_MODEL)
menu->getSubMenu(2)->setItemChecked(0, false);
else if (id == E_GUI_ID_TOOLBOX_WIELD)
menu->getSubMenu(2)->setItemChecked(1, false);
}
gui->setFocused(false);
}
else if (event.GUIEvent.EventType == EGET_BUTTON_CLICKED)
{
s32 id = event.GUIEvent.Caller->getID();
switch (id)
{
case E_CTRL_ID_SKIP_REV:
scene->setAnimation(
animation->getField(E_GUI_ID_ANIM_START),
animation->getField(E_GUI_ID_ANIM_START),
animation->getField(E_GUI_ID_ANIM_SPEED));
animation->setState(E_ANIM_STATE_PAUSED);
gui->setFocused(false);
break;
case E_CTRL_ID_PLAY_REV:
scene->setAnimation(
animation->getField(E_GUI_ID_ANIM_START),
animation->getField(E_GUI_ID_ANIM_END),
-animation->getField(E_GUI_ID_ANIM_SPEED));
animation->setState(E_ANIM_STATE_PLAY_REV);
gui->setFocused(false);
break;
case E_CTRL_ID_PAUSE:
scene->setAnimation(
animation->getFrame(),
animation->getFrame(),
animation->getField(E_GUI_ID_ANIM_SPEED));
animation->setState(E_ANIM_STATE_PAUSED);
gui->setFocused(false);
break;
case E_CTRL_ID_PLAY_FWD:
scene->setAnimation(
animation->getField(E_GUI_ID_ANIM_START),
animation->getField(E_GUI_ID_ANIM_END),
animation->getField(E_GUI_ID_ANIM_SPEED));
animation->setState(E_ANIM_STATE_PLAY_FWD);
gui->setFocused(false);
break;
case E_CTRL_ID_SKIP_FWD:
scene->setAnimation(
animation->getField(E_GUI_ID_ANIM_END),
animation->getField(E_GUI_ID_ANIM_END),
animation->getField(E_GUI_ID_ANIM_SPEED));
animation->setState(E_ANIM_STATE_PAUSED);
gui->setFocused(false);
break;
case E_DIALOG_ID_SETTINGS_OK:
event.GUIEvent.Caller->getParent()->getParent()->remove();
setBackgroundColor(conf->getHex("bg_color"));
scene->setGridColor(conf->getHex("grid_color"));
scene->setAttachment();
scene->setDebugInfo(conf->getBool("debug_info"));
gui->setFocused(false);
break;
case E_DIALOG_ID_SETTINGS_CANCEL:
case E_DIALOG_ID_TEXTURES_CANCEL:
case E_DIALOG_ID_LIGHTS_CANCEL:
case E_DIALOG_ID_LIGHTS_OK:
case E_DIALOG_ID_ABOUT_OK:
event.GUIEvent.Caller->getParent()->getParent()->remove();
gui->setFocused(false);
break;
case E_DIALOG_ID_TEXTURES_OK:
scene->refresh();
event.GUIEvent.Caller->getParent()->getParent()->remove();
gui->setFocused(false);
break;
default:
break;
}
}
else if (event.GUIEvent.EventType == EGET_SPINBOX_CHANGED)
{
s32 id = event.GUIEvent.Caller->getID();
switch (id)
{
case E_GUI_ID_ANIM_START:
case E_GUI_ID_ANIM_END:
{
if (animation->getState() != E_ANIM_STATE_PAUSED)
{
IAnimatedMeshSceneNode *model = (IAnimatedMeshSceneNode*)
scene->getNode(E_SCENE_ID_MODEL);
scene->setAnimation(
animation->getField(E_GUI_ID_ANIM_START),
animation->getField(E_GUI_ID_ANIM_END),
model->getAnimationSpeed());
}
break;
}
case E_GUI_ID_ANIM_SPEED:
{
IAnimatedMeshSceneNode *model = (IAnimatedMeshSceneNode*)
scene->getNode(E_SCENE_ID_MODEL);
s32 speed = animation->getField(E_GUI_ID_ANIM_SPEED);
if (animation->getState() == E_ANIM_STATE_PLAY_REV)
speed = -speed;
model->setAnimationSpeed(speed);
break;
}
case E_GUI_ID_ANIM_FRAME:
{
if (animation->getState() == E_ANIM_STATE_PAUSED)
{
u32 frame = animation->getField(E_GUI_ID_ANIM_FRAME);
scene->setAnimation(frame, frame,
animation->getField(E_GUI_ID_ANIM_SPEED));
}
break;
}
default:
break;
}
}
else if (event.GUIEvent.EventType == EGET_ELEMENT_FOCUS_LOST)
{
gui->setFocused(false);
}
else if (event.GUIEvent.EventType == EGET_ELEMENT_FOCUSED)
{
gui->setFocused(true);
trackball->release();
}
}
else if (event.EventType == EET_KEY_INPUT_EVENT &&
event.KeyInput.PressedDown && !gui->getFocused())
{
switch (event.KeyInput.Key)
{
case KEY_HOME:
{
scene->setRotation(vector3df(0,0,0));
fov = fov_home;
setProjection();
break;
}
case KEY_SPACE:
{
u32 now_time = device->getTimer()->getTime() + 1000;
if (jump_time + 800 < now_time)
{
jump_time = now_time;
scene->jump();
}
break;
}
case KEY_LEFT:
scene->rotate(E_SCENE_AXIS_Y, 15);
break;
case KEY_RIGHT:
scene->rotate(E_SCENE_AXIS_Y, -15);
break;
case KEY_UP:
scene->rotate(E_SCENE_AXIS_X, -15);
break;
case KEY_DOWN:
scene->rotate(E_SCENE_AXIS_X, 15);
break;
case KEY_KEY_Z:
scene->rotate(E_SCENE_AXIS_Z, -15);
break;
case KEY_KEY_X:
scene->rotate(E_SCENE_AXIS_Z, 15);
break;
case KEY_PLUS:
fov = M_ZOOM_IN(fov);
setProjection();
break;
case KEY_MINUS:
fov = M_ZOOM_OUT(fov);
setProjection();
break;
case KEY_F5:
scene->refresh();
break;
default:
break;
}
}
else if (event.EventType == EET_MOUSE_INPUT_EVENT && !gui->getFocused())
{
switch (event.MouseInput.Event)
{
case EMIE_LMOUSE_LEFT_UP:
trackball->release();
break;
case EMIE_MOUSE_MOVED:
{
if (event.MouseInput.isLeftPressed() && !trackball->isClicked())
trackball->click();
trackball->setDragPos(event.MouseInput.X, event.MouseInput.Y);
break;
}
case EMIE_MOUSE_WHEEL:
{
if (event.MouseInput.Wheel < 0)
fov = M_ZOOM_OUT(fov);
else
fov = M_ZOOM_IN(fov);
setProjection();
return true;
}
default:
break;
}
}
return false;
}
AnimState::AnimState(IGUIEnvironment *env) :
env(env),
frame(0),
state(E_ANIM_STATE_PAUSED)
{}
void AnimState::load(ISceneNode *node)
{
IAnimatedMeshSceneNode *model = (IAnimatedMeshSceneNode*)node;
if (!model)
return;
s32 max = model->getEndFrame();
bool enabled = (max > 0);
state = E_ANIM_STATE_PAUSED;
model->setFrameLoop(0, 0);
initField(E_GUI_ID_ANIM_START, max, enabled);
initField(E_GUI_ID_ANIM_END, max, enabled);
initField(E_GUI_ID_ANIM_FRAME, max, enabled);
initField(E_GUI_ID_ANIM_SPEED, 60, enabled);
setField(E_GUI_ID_ANIM_START, 0);
setField(E_GUI_ID_ANIM_END, max);
setField(E_GUI_ID_ANIM_FRAME, 0);
IGUIElement *root = env->getRootGUIElement();
AnimCtrl *anim = (AnimCtrl*)
root->getElementFromId(E_GUI_ID_ANIM_CTRL, true);
anim->reset(enabled);
if (enabled)
{
IGUIButton *button =
(IGUIButton*)anim->getElementFromId(E_CTRL_ID_PAUSE, true);
button->setPressed(true);
}
}
void AnimState::update(ISceneNode *node)
{
IAnimatedMeshSceneNode *model = (IAnimatedMeshSceneNode*)node;
frame = model->getFrameNr();
setField(E_GUI_ID_ANIM_FRAME, frame);
}
void AnimState::initField(s32 id, const u32 &max, const bool &enabled)
{
IGUIElement *root = env->getRootGUIElement();
IGUISpinBox *spin = (IGUISpinBox*)root->getElementFromId(id, true);
spin->setRange(0, max);
spin->setEnabled(enabled);
}
u32 AnimState::getField(s32 id)
{
IGUIElement *root = env->getRootGUIElement();
IGUISpinBox *spin = (IGUISpinBox*)root->getElementFromId(id, true);
return spin->getValue();
}
void AnimState::setField(s32 id, const u32 &value)
{
IGUIElement *root = env->getRootGUIElement();
IGUISpinBox *spin = (IGUISpinBox*)root->getElementFromId(id, true);
spin->setValue(value);
}