#include #include "Engine.h" #include "UserInterface.h" #include "Utility.h" #include "View.h" using std::cout; using std::endl; using std::wcerr; using std::wstring; using std::wstringstream; using namespace irr; using namespace irr::core; using namespace irr::scene; using namespace irr::video; using namespace irr::gui; /* ////////////////////////////////////////////////////////////////////////// PRIVATE METHODS /////////////////////////////////////////////////////////////////////// */ void Engine::setupScene() { // Setup Light m_SceneLight = m_Scene->addLightSceneNode(); m_SceneLight->setID(SIID_LIGHT); m_SceneLight->setLightType(ELT_DIRECTIONAL); m_SceneLight->getLightData().AmbientColor = SColorf(0.2f, 0.2f, 0.2f); m_SceneLight->getLightData().DiffuseColor = SColorf(0.8f, 0.8f, 0.8f); m_Scene->setAmbientLight(SColorf(0.2f, 0.2f, 0.2f)); // Setup Camera // (so z-forward characters face camera partially (formerly vector3df(0, 0, -10), vector3df()) m_CamPos = vector3df(4.5, 3, 9); m_CamTarget = vector3df(0, 3, 0); ICameraSceneNode* camera = m_Scene->addCameraSceneNode(nullptr, m_CamPos, m_CamTarget); // this will be overridden by View m_Yaw and m_Pitch--see "calculate m_Yaw" further down camera->setAspectRatio(static_cast(m_Driver->getScreenSize().Width) / static_cast(m_Driver->getScreenSize().Height)); } IGUIEnvironment* Engine::getGUIEnvironment() const { return m_Device->getGUIEnvironment(); } void Engine::drawAxisLines() { SMaterial* lineX = new SMaterial(); lineX->Lighting = false; lineX->EmissiveColor = SColor(255, 255, 0, 0); lineX->Thickness = 1.0f; SMaterial* lineY = new SMaterial(*lineX); lineY->EmissiveColor = SColor(255, 0, 255, 0); SMaterial* lineZ = new SMaterial(*lineX); lineZ->EmissiveColor = SColor(255, 0, 0, 255); m_Driver->setTransform(ETS_WORLD, matrix4()); m_Driver->setMaterial(*lineX); m_Driver->draw3DLine(vector3df(), vector3df(5, 0, 0), SColor(255, 255, 0, 0)); position2d textPos = m_Scene->getSceneCollisionManager()->getScreenCoordinatesFrom3DPosition(vector3df(5.2f, 0, 0)); dimension2d textSize; if (m_AxisFont != nullptr) { textSize = m_AxisFont->getDimension(L"X+"); m_AxisFont->draw(L"X+", rect(textPos, textSize), SColor(255, 255, 0, 0), true, true); } m_Driver->setMaterial(*lineY); m_Driver->draw3DLine(vector3df(), vector3df(0, 5, 0), SColor(255, 0, 255, 0)); textPos = m_Scene->getSceneCollisionManager()->getScreenCoordinatesFrom3DPosition(vector3df(0, 5.2f, 0)); if (m_AxisFont != nullptr) { textSize = m_AxisFont->getDimension(L"Y+"); m_AxisFont->draw(L"Y+", rect(textPos, textSize), SColor(255, 0, 255, 0), true, true); } m_Driver->setMaterial(*lineZ); m_Driver->draw3DLine(vector3df(), vector3df(0, 0, 5), SColor(255, 0, 0, 255)); textPos = m_Scene->getSceneCollisionManager()->getScreenCoordinatesFrom3DPosition(vector3df(0, 0, 5.2f)); if (m_AxisFont != nullptr) { textSize = m_AxisFont->getDimension(L"Z+"); m_AxisFont->draw(L"Z+", rect(textPos, textSize), SColor(255, 0, 0, 255), true, true); } delete lineX; delete lineY; delete lineZ; } void Engine::drawBackground() { dimension2d screenSize = m_Driver->getScreenSize(); m_Driver->draw2DRectangle(rect(0, 0, static_cast(screenSize.Width), static_cast(screenSize.Height)), SColor(255, 128, 128, 255), SColor(255, 128, 128, 255), SColor(255, 224, 224, 255), SColor(255, 224, 224, 255)); } void Engine::checkResize() { if ((m_WindowSize.Width != m_Driver->getScreenSize().Width) || (m_WindowSize.Height != m_Driver->getScreenSize().Height)) { m_WindowSize.Width = m_Driver->getScreenSize().Width; m_WindowSize.Height = m_Driver->getScreenSize().Height; // Send custom event IEventReceiver* eventReceiver = m_Device->getEventReceiver(); SEvent event; event.EventType = EET_USER_EVENT; event.UserEvent.UserData1 = UEI_WINDOWSIZECHANGED; eventReceiver->OnEvent(event); //m_UserInterface-> } } s32 Engine::getNumberOfVertices() { IMesh* mesh = m_LoadedMesh->getMesh()->getMesh(0, 255, -1, -1); int vertices = 0; for (irr::u32 bufferIndex = 0; bufferIndex < mesh->getMeshBufferCount(); bufferIndex++) vertices += mesh->getMeshBuffer(bufferIndex)->getVertexCount(); cout << vertices << endl; return vertices; } /* ////////////////////////////////////////////////////////////////////////// PUBLIC METHODS /////////////////////////////////////////////////////////////////////// */ Engine::Engine() { // For monitoring single press: see // 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; this->worldFPS = 60; this->prevFPS = 30; this->textureExtensions.push_back(L"png"); this->textureExtensions.push_back(L"jpg"); this->textureExtensions.push_back(L"bmp"); #if WIN32 m_Device = createDevice(EDT_DIRECT3D9, dimension2d(1024, 768), 32, false, false, false, nullptr); #else m_Device = createDevice(EDT_OPENGL, dimension2d(1024, 768), 32, false, false, false, nullptr); #endif m_Device->setResizable(true); m_EventHandler = new EventHandler(m_Device); m_Device->setEventReceiver(m_EventHandler); m_Driver = m_Device->getVideoDriver(); m_Scene = m_Device->getSceneManager(); wstringstream windowTitle; windowTitle << L"b3view (Blitz3D/Irrlicht Viewer) [" << m_Driver->getName() << L"]"; m_Device->setWindowCaption(windowTitle.str().c_str()); setupScene(); // Setup User Interface m_UserInterface = new UserInterface(this); m_EventHandler->addEventReceiver(ERT_USERINTERFACE, m_UserInterface); // Setup 3D View m_View = new View(this); m_EventHandler->addEventReceiver(ERT_3DVIEW, m_View); // Load font for displaying Axis names m_AxisFontFace = new CGUITTFace(); // NOTE: m_FontPath is modified y UserInterface constructor above if font was missing if (m_AxisFontFace->load(m_FontPath.c_str())) { m_AxisFont = new CGUITTFont(m_UserInterface->getGUIEnvironment()); m_AxisFont->attach(m_AxisFontFace, 14); m_AxisFont->AntiAlias = false; } else { delete m_AxisFontFace; m_AxisFontFace = nullptr; } // Set Engine enabled m_RunEngine = true; m_LoadedMesh = nullptr; // Store actual window size m_WindowSize.Width = m_Driver->getScreenSize().Width; m_WindowSize.Height = m_Driver->getScreenSize().Height; // (do not calculate m_Yaw and m_Pitch here--see View constructor) this->playAnimation(); } Engine::~Engine() { m_Device->drop(); delete m_AxisFont; delete m_AxisFontFace; } void Engine::loadMesh(const wstring& fileName) { 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) { m_LoadedMesh = m_Scene->addAnimatedMeshSceneNode(mesh); Utility::dumpMeshInfoToConsole(m_LoadedMesh); if (Utility::toLower(Utility::extensionOf(fileName)) == L"3ds") { m_View->setZUp(true); } else { m_View->setZUp(false); } if (m_LoadedMesh != nullptr) { ICameraSceneNode* camera = this->m_Scene->getActiveCamera(); aabbox3d box = m_LoadedMesh->getTransformedBoundingBox(); //vector3d extents = box.getExtent(); if (m_View->zUp()) { float oldDist = m_CamPos.getDistanceFrom(m_CamTarget); float newDist = oldDist; if (Utility::equalsApprox(oldDist, 0.0f)) { vector3d center = box.getCenter(); vector3df edges[8]; box.getEdges(edges); /* /3--------/7 / | / | / | / | 1---------5 | | 2- - -| -6 | / | / |/ | / 0---------4/ */ newDist = 0; for (int i = 0; i < 8; i++) { float tryDist = center.getDistanceFrom(edges[i]); if (tryDist > newDist) newDist = tryDist; } newDist *= 2; // so camera doesn't touch model if (!Utility::equalsApprox(newDist, oldDist)) { float scale = newDist / oldDist; // already checked 0 vector3df oldCamPos = camera->getPosition(); m_CamPos = oldCamPos; m_CamPos.X = m_CamPos.X * scale; m_CamPos.Y = m_CamPos.Y * scale; m_CamPos.Z = m_CamPos.Z * scale; oldCamPos = m_CamPos; m_View->setCameraDistance(m_CamPos.getDistanceFrom(m_CamTarget)); camera->setPosition(m_CamPos); } } } m_LoadedMesh->setMaterialType(video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF); // EMT_TRANSPARENT_ALPHA_CHANNEL: constant transparency } } } void Engine::reloadMesh() { if (this->m_PreviousPath.length() > 0) { loadMesh(this->m_PreviousPath); } } void Engine::reloadTexture() { if (this->m_PrevTexturePath.length() > 0) { if (wcslen(this->m_UserInterface->texturePathEditBox->getText()) == 0) loadTexture(this->m_UserInterface->texturePathEditBox->getText()); else loadTexture(this->m_PrevTexturePath); } } bool Engine::loadTexture(const wstring& fileName) { ITexture* texture = this->m_Driver->getTexture(fileName.c_str()); bool ret = false; if (texture != nullptr) { m_LoadedMesh->setMaterialTexture(0, texture); ret = true; } this->m_PrevTexturePath = fileName; this->m_UserInterface->texturePathEditBox->setText(this->m_PrevTexturePath.c_str()); return ret; } void Engine::setMeshDisplayMode(bool wireframe, bool lighting, bool textureInterpolation) { if (m_LoadedMesh != nullptr) { for (u32 materialIndex = 0; materialIndex < m_LoadedMesh->getMaterialCount(); materialIndex++) { // Set Wireframe display m_LoadedMesh->getMaterial(materialIndex).Wireframe = wireframe; // Set Lighting if (!lighting) { m_LoadedMesh->getMaterial(materialIndex).Lighting = false; m_LoadedMesh->getMaterial(materialIndex).EmissiveColor = SColor(255, 255, 255, 255); } else { m_LoadedMesh->getMaterial(materialIndex).Lighting = true; m_LoadedMesh->getMaterial(materialIndex).EmissiveColor = SColor(255, 0, 0, 0); } // m_LoadedMesh->setMaterialType(video::EMT_TRANSPARENT_ALPHA_CHANNEL); //already done on load // m_LoadedMesh->setMaterialFlag(video::E_ALPHA_SOURCE, true); // requires EMT_ONETEXTURE if (textureInterpolation) { m_LoadedMesh->setMaterialFlag(video::EMF_BILINEAR_FILTER, true); m_LoadedMesh->setMaterialFlag(video::EMF_TRILINEAR_FILTER, true); } else { m_LoadedMesh->setMaterialFlag(video::EMF_BILINEAR_FILTER, false); m_LoadedMesh->setMaterialFlag(video::EMF_TRILINEAR_FILTER, false); //m_LoadedMesh->setMaterialFlag(video::E_ALPHA_SOURCE, true); // below doesn't work for some reason: // video::SMaterial mat = m_LoadedMesh->getMaterial(materialIndex); // mat.UseMipMaps = false; // mat.setFlag(video::EMF_BILINEAR_FILTER, false); // mat.setFlag(video::EMF_TRILINEAR_FILTER, false); // below would require patching Irrlicht: // GLint filteringMipMaps = GL_NEAREST_MIPMAP_NEAREST // // above is used by glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filteringMipMaps); } } } else debug() << "WARNING in setMeshDisplayMode: No mesh is loaded " << endl; } bool Engine::isAnimating() { 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); } } void Engine::setZUp(bool zUp) { if (this->m_View != nullptr) { this->m_View->setZUp(zUp); } } u32 Engine::animationFPS() { u32 ret = 0; if (this->m_LoadedMesh != nullptr) { ret = this->m_LoadedMesh->getAnimationSpeed(); } return ret; } void Engine::run() { u32 timePerFrame = 1000.0f; if (this->worldFPS > 0) { timePerFrame = static_cast(1000.0f / this->worldFPS); } ITimer* timer = m_Device->getTimer(); // Run the Device with fps frames/sec while (m_Device->run() && m_RunEngine) { u32 startTime = timer->getRealTime(); checkResize(); if (this->m_LoadedMesh != nullptr) { if (isPlaying) { this->m_LoadedMesh->setLoopMode(true); this->m_UserInterface->playbackSetFrameEditBox->setText(Utility::toWstring(this->m_LoadedMesh->getFrameNr()).c_str()); } else { this->m_LoadedMesh->setLoopMode(false); } } m_Driver->beginScene(); drawBackground(); // Draw Background drawAxisLines(); // Draw XYZ Axis m_Scene->drawAll(); // Draw Scenegraph m_UserInterface->getGUIEnvironment()->drawAll(); m_UserInterface->drawStatusLine(); m_Driver->endScene(); u32 sleepTime = timePerFrame - (timer->getRealTime() - startTime); if (sleepTime > 0 && sleepTime < timePerFrame) m_Device->sleep(sleepTime, false); } }