#include #include #include #include #include #include #include #include #include #include // #include "water/water.h" #include "GameData.h" #include "Components.h" #include "CharacterModule.h" #include "TerrainModule.h" #include "GUIModule.h" #include "sound.h" class App; class SkyRenderer : public Ogre::SceneManager::Listener { protected: Ogre::SceneManager *mSceneManager; virtual void _updateRenderQueue(Ogre::RenderQueue *queue) = 0; public: enum BoxPlane { BP_FRONT = 0, BP_BACK = 1, BP_LEFT = 2, BP_RIGHT = 3, BP_UP = 4, BP_DOWN = 5 }; SkyRenderer(Ogre::SceneManager *owner) : mSceneManager(owner) , mSceneNode(0) , mEnabled(false) { } virtual ~SkyRenderer() { setEnabled(false); if (mSceneNode) mSceneManager->destroySceneNode(mSceneNode); } Ogre::SceneNode *mSceneNode; bool mEnabled; void setEnabled(bool enable) { if (enable == mEnabled) return; mEnabled = enable; enable ? mSceneManager->addListener(this) : mSceneManager->removeListener(this); } void postFindVisibleObjects(Ogre::SceneManager *source, Ogre::SceneManager::IlluminationRenderStage irs, Ogre::Viewport *vp) override { // Queue skies, if viewport seems it if (!vp->getSkiesEnabled() || irs == Ogre::SceneManager::IRS_RENDER_TO_TEXTURE) return; if (!mEnabled || !mSceneNode) return; // Update nodes // Translate the box by the camera position (constant distance) mSceneNode->setPosition(vp->getCamera()->getDerivedPosition()); _updateRenderQueue(source->getRenderQueue()); } }; class SkyBoxRenderer : public SkyRenderer { std::unique_ptr mSkyBoxObj; Ogre::Quaternion mSkyBoxOrientation; void _updateRenderQueue(Ogre::RenderQueue *queue) override { if (mSkyBoxObj->isVisible()) { mSkyBoxObj->_updateRenderQueue(queue); } } public: SkyBoxRenderer(Ogre::SceneManager *owner) : SkyRenderer(owner) { } Ogre::SceneManager::SkyBoxGenParameters mSkyBoxGenParameters; void create(const Ogre::String &materialName, Ogre::Real distance, uint8_t renderQueue, const Ogre::Quaternion &orientation, const Ogre::String &groupName) { Ogre::MaterialPtr m = Ogre::MaterialManager::getSingleton().getByName( materialName, groupName); OgreAssert(m, "Sky box material '" + materialName + "' not found."); // Ensure loaded m->load(); bool valid = m->getBestTechnique() && m->getBestTechnique()->getNumPasses(); #if 0 if (valid) { Pass *pass = m->getBestTechnique()->getPass(0); valid = valid && pass->getNumTextureUnitStates() && pass->getTextureUnitState(0)->getTextureType() == TEX_TYPE_CUBE_MAP; } if (!valid) { LogManager::getSingleton().logWarning( "skybox material " + materialName + " is not supported, defaulting"); m = MaterialManager::getSingleton().getDefaultSettings(); } #endif OgreAssert(valid, "Bad material" + materialName); // Create node mSceneNode = mSceneManager->createSceneNode(); // Create object mSkyBoxObj = std::make_unique("SkyBox"); mSkyBoxObj->setCastShadows(false); mSceneNode->attachObject(mSkyBoxObj.get()); mSkyBoxObj->setRenderQueueGroup(renderQueue); mSkyBoxObj->begin(materialName, Ogre::RenderOperation::OT_TRIANGLE_STRIP, groupName); // rendering cube, only using 14 vertices const Ogre::Vector3 cube_strip[14] = { { -1.f, 1.f, 1.f }, // Front-top-left { 1.f, 1.f, 1.f }, // Front-top-right { -1.f, -1.f, 1.f }, // Front-bottom-left { 1.f, -1.f, 1.f }, // Front-bottom-right { 1.f, -1.f, -1.f }, // Back-bottom-right { 1.f, 1.f, 1.f }, // Front-top-right { 1.f, 1.f, -1.f }, // Back-top-right { -1.f, 1.f, 1.f }, // Front-top-left { -1.f, 1.f, -1.f }, // Back-top-left { -1.f, -1.f, 1.f }, // Front-bottom-left { -1.f, -1.f, -1.f }, // Back-bottom-left { 1.f, -1.f, -1.f }, // Back-bottom-right { -1.f, 1.f, -1.f }, // Back-top-left { 1.f, 1.f, -1.f } // Back-top-right }; for (const auto &vtx : cube_strip) { mSkyBoxObj->position(orientation * (vtx * distance)); // Note UVs mirrored front/back mSkyBoxObj->textureCoord(vtx.normalisedCopy() * Ogre::Vector3(1, 1, -1)); } mSkyBoxObj->end(); mSkyBoxGenParameters.skyBoxDistance = distance; } }; class App; class KeyboardListener : public OgreBites::InputListener { App *mApp; uint32_t control; ECS::Vector2 mouse; float wheel_y; bool mouse_moved, wheel_moved; float mInitDelay; public: Ogre::Timer fps_timer; bool fast; KeyboardListener(App *app) : OgreBites::InputListener() , mApp(app) , fast(false) , control(0) , mouse({ 0, 0 }) , wheel_y(0.0f) , mouse_moved(false) , wheel_moved(false) , mInitDelay(1.0) { } bool isGuiEnabled() { if (!ECS::get().has()) return false; return (ECS::get().get().enabled); } void setGuiEnabled(bool value) { if (!ECS::get().has()) return; ECS::get().get_mut().enabled = value; ECS::get().modified(); } bool keyPressed(const OgreBites::KeyboardEvent &evt) override { bool updated = false; if (isGuiEnabled()) return false; std::cout << "GUI not enabled\n"; if (evt.keysym.sym == OgreBites::SDLK_ESCAPE) { OgreAssert(ECS::get().has(), ""); setGuiEnabled(true); if (ECS::get().has()) ECS::get().setWindowGrab(false); return true; } OgreBites::Keycode key = evt.keysym.sym; if (key == 'w') control |= 1; else if (key == 'a') control |= 2; else if (key == 's') control |= 4; else if (key == 'd') control |= 8; else if (key == OgreBites::SDLK_LSHIFT) control |= 16; if (key == 'w' || key == 'a' || key == 's' || key == 'd' || key == OgreBites::SDLK_LSHIFT) return true; return false; } bool keyReleased(const OgreBites::KeyboardEvent &evt) override { OgreBites::Keycode key = evt.keysym.sym; if (isGuiEnabled()) return false; if (key == 'w') control &= ~1; else if (key == 'a') control &= ~2; else if (key == 's') control &= ~4; else if (key == 'd') control &= ~8; else if (key == OgreBites::SDLK_LSHIFT) control &= ~16; if (key == 'w' || key == 'a' || key == 's' || key == 'd' || key == OgreBites::SDLK_LSHIFT) return true; return false; } bool mouseMoved(const OgreBites::MouseMotionEvent &evt) override { if (isGuiEnabled()) return false; mouse.x = evt.xrel; mouse.y = evt.yrel; mouse_moved = true; /* no special mouse handling */ return true; } bool mouseWheelRolled(const OgreBites::MouseWheelEvent &evt) override { if (isGuiEnabled()) return false; /* no special mouse wheel handling */ wheel_y = evt.y; wheel_moved = true; return true; } bool mousePressed(const OgreBites::MouseButtonEvent &evt) override { std::cout << "Mouse press " << (int)evt.button << " " << (int)evt.clicks << "\n"; if ((int)evt.button == 1) control |= 256; else control &= ~256; return false; } void update(float delta) { return; } void frameRendered(const Ogre::FrameEvent &evt) override; }; class App : public OgreBites::ApplicationContext { std::unique_ptr mDynWorld; std::unique_ptr mDbgDraw; Ogre::SceneNode *mCameraNode, *mCameraPivot, *mCameraGoal; Ogre::Camera *mCamera; Ogre::Real mPivotPitch; Ogre::SceneManager *mScnMgr; Ogre::Viewport *mViewport; SkyBoxRenderer *sky; bool mGrab; KeyboardListener mKbd; bool enabldDbgDraw; public: App() : OgreBites::ApplicationContext("ChoroGame") , mKbd(this) , mDynWorld(new Ogre::Bullet::DynamicsWorld( Ogre::Vector3(0, -9.8, 0))) , mGrab(false) , enabldDbgDraw(false) { } virtual ~App() { } void setup() { OgreBites::ApplicationContext::setup(); Ogre::Root *root = getRoot(); Ogre::SceneManager *scnMgr = root->createSceneManager(); mScnMgr = scnMgr; Ogre::OverlaySystem *pOverlaySystem = getOverlaySystem(); mScnMgr->addRenderQueueListener(pOverlaySystem); // mTrayMgr = new OgreBites::TrayManager("AppTrays", // getRenderWindow()); mDbgDraw.reset(new Ogre::Bullet::DebugDrawer( mScnMgr->getRootSceneNode(), mDynWorld->getBtWorld())); } bool isWindowGrab() { return mGrab; } void locateResources() override { Ogre::ResourceGroupManager::getSingleton().createResourceGroup( "Water", true); OgreBites::ApplicationContext::locateResources(); } void loadResources() override { } void setWindowGrab(bool grab = true) { mGrab = grab; ApplicationContextBase::setWindowGrab(grab); } void initCamera() { mCameraNode = mScnMgr->getRootSceneNode()->createChildSceneNode( "CameraNode"); mCameraNode->setPosition(0, 2, 3); mCameraNode->lookAt(Ogre::Vector3(0, 1, -1), Ogre::Node::TS_PARENT); // create the camera mCamera = mScnMgr->createCamera("fps_camera"); mCamera->setNearClipDistance(0.05f); mCamera->setAutoAspectRatio(true); mCameraNode->attachObject(mCamera); // and tell it to render into the main window mViewport = getRenderWindow()->addViewport(mCamera); mCameraPivot = mScnMgr->getRootSceneNode()->createChildSceneNode( "FPSCameraPivot"); mCameraGoal = mCameraPivot->createChildSceneNode( "FPSCameraGoal", Ogre::Vector3(0, 2, 3)); mCameraNode->setPosition(mCameraPivot->getPosition() + mCameraGoal->getPosition()); mCameraPivot->setFixedYawAxis(true); mCameraGoal->setFixedYawAxis(true); mCameraNode->setFixedYawAxis(true); // our model is quite small, so reduce the clipping planes mCamera->setNearClipDistance(0.1f); mCamera->setFarClipDistance(800); mPivotPitch = 0; } void configure() { std::cout << "Startup" << "\n"; initApp(); std::cout << "Set up RTSS" << "\n"; Ogre::Root *root = getRoot(); Ogre::SceneManager *scnMgr = getSceneManager(); // register our scene with the RTSS Ogre::RTShader::ShaderGenerator *shadergen = Ogre::RTShader::ShaderGenerator::getSingletonPtr(); shadergen->addSceneManager(scnMgr); setWindowGrab(true); std::cout << "Init camera" << "\n"; initCamera(); std::cout << "Set up water" << "\n"; std::cout << "Set up cursor" << "\n"; Ogre::ResourceGroupManager::getSingleton() .initialiseAllResourceGroups(); // OgreBites::ApplicationContext::loadResources(); // setupCursor(); std::cout << "Setup input" << "\n"; setupInput(); std::cout << "Create content" << "\n"; createContent(); std::cout << "Setup done" << "\n"; mDbgDraw->setDebugMode(mDbgDraw->getDebugMode() | btIDebugDraw::DBG_DrawContactPoints); } Ogre::SceneManager *getSceneManager() { return mScnMgr; } Ogre::Timer mTerrainUpd; bool isTerrainReady() { if (ECS::get().has()) return ECS::get().get().mTerrainReady; return false; } // TODO: implement rough water level calculation float getWaterLevel(const Ogre::Vector3 &position) { Ogre::Vector3::UNIT_Y; float etime = Ogre::ControllerManager::getSingleton().getElapsedTime(); return 0.0f; } void updateWorld(float delta) { mDynWorld->getBtWorld()->stepSimulation(delta, 4); /* Update window grab */ if (ECS::get().has() && ECS::get().get().grabChanged) { setWindowGrab(ECS::get().get().grab); ECS::get().get_mut().grabChanged = false; ECS::get().modified(); } ECS::update(delta); if (enabldDbgDraw) mDbgDraw->update(); } class InputListenerChainFlexible : public OgreBites::InputListener { protected: std::vector mListenerChain; public: InputListenerChainFlexible() { } InputListenerChainFlexible(std::vector chain) : mListenerChain(chain) { } void add(OgreBites::InputListener *listener) { mListenerChain.push_back(listener); } void erase(OgreBites::InputListener *listener) { mListenerChain.erase(std::find(mListenerChain.begin(), mListenerChain.end(), listener)); } bool empty() const { return mListenerChain.empty(); } InputListenerChainFlexible & operator=(const InputListenerChainFlexible &o) { mListenerChain = o.mListenerChain; return *this; } void frameRendered(const Ogre::FrameEvent &evt) override { for (auto listener : mListenerChain) listener->frameRendered(evt); } bool keyPressed(const OgreBites::KeyboardEvent &evt) override { for (auto listner : mListenerChain) { if (listner->keyPressed(evt)) return true; } return false; } bool keyReleased(const OgreBites::KeyboardEvent &evt) override { for (auto listner : mListenerChain) { if (listner->keyReleased(evt)) return true; } return false; } bool touchMoved(const OgreBites::TouchFingerEvent &evt) override { for (auto listner : mListenerChain) { if (listner->touchMoved(evt)) return true; } return false; } bool touchPressed(const OgreBites::TouchFingerEvent &evt) override { for (auto listner : mListenerChain) { if (listner->touchPressed(evt)) return true; } return false; } bool touchReleased(const OgreBites::TouchFingerEvent &evt) override { for (auto listner : mListenerChain) { if (listner->touchReleased(evt)) return true; } return false; } bool mouseMoved(const OgreBites::MouseMotionEvent &evt) override { for (auto listner : mListenerChain) { if (listner->mouseMoved(evt)) return true; } return false; } bool mouseWheelRolled(const OgreBites::MouseWheelEvent &evt) override { for (auto listner : mListenerChain) { if (listner->mouseWheelRolled(evt)) return true; } return false; } bool mousePressed(const OgreBites::MouseButtonEvent &evt) override { for (auto listner : mListenerChain) { if (listner->mousePressed(evt)) return true; } return false; } bool mouseReleased(const OgreBites::MouseButtonEvent &evt) override { for (auto listner : mListenerChain) { if (listner->mouseReleased(evt)) return true; } return false; } bool textInput(const OgreBites::TextInputEvent &evt) override { for (auto listner : mListenerChain) { if (listner->textInput(evt)) return true; } return false; } }; flecs::entity input_update; flecs::entity find_wait_gui; void setupInput() { } void createContent() { int i; sky = new SkyBoxRenderer(getSceneManager()); bool drawFirst = true; uint8_t renderQueue = drawFirst ? Ogre::RENDER_QUEUE_SKIES_EARLY : Ogre::RENDER_QUEUE_SKIES_LATE; sky->create("Skybox/Dynamic", 450, renderQueue, Ogre::Quaternion::IDENTITY, Ogre::ResourceGroupManager:: AUTODETECT_RESOURCE_GROUP_NAME); sky->setEnabled(true); Ogre::MaterialPtr m = Ogre::MaterialManager::getSingleton().getByName( "Skybox/Dynamic", "General"); OgreAssert(m, "Sky box material not found."); m->load(); ECS::setup(mScnMgr, mDynWorld.get(), mCameraNode, mCamera, getRenderWindow()); ECS::get().set( { getRenderWindow(), getDisplayDPI() }); ECS::get() .observer("UpdateGrab") .event(flecs::OnSet) .each([this](flecs::entity e, ECS::GUI &gui) { if (gui.grabChanged) setWindowGrab(gui.grab); std::cout << "grab: " << gui.grab << "\n"; std::cout << "GUI enabled: " << gui.enabled << "\n"; }); ECS::get() .observer("UpdateInputListener") .event(flecs::OnSet) .each([this](flecs::entity e, ECS::App &app) { if (app.mInput) removeInputListener(app.mInput); delete app.mInput; app.mInput = OGRE_NEW OgreBites::InputListenerChain( app.listeners); addInputListener(app.mInput); }); #if 0 ECS::get() .observer("SetInputListener2") .event(flecs::OnSet) .each([this](ECS::GUI &gui, ECS::App &app) { if (gui.mGuiInpitListener && app.listeners.size() == 1) { app.listeners.clear(); app.listeners.push_back( gui.mGuiInpitListener); app.listeners.push_back(&mKbd); ECS::modified(); } }); #endif #if 0 input_update = ECS::get() .system("SetInputListener") .kind(flecs::OnUpdate) .each([this](ECS::GUI &gui, ECS::App &app) { if (app.listeners.size() < 2 && gui.mGuiInpitListener) { OgreBites::InputListener *guiListener = gui.mGuiInpitListener; if (guiListener) { app.listeners.clear(); app.listeners.push_back( guiListener); app.listeners.push_back( &mKbd); std::cout << "input update complete\n"; gui.mGuiInpitListener = guiListener; if (app.mInput) removeInputListener( app.mInput); delete app.mInput; app.mInput = new OgreBites:: InputListenerChain( app.listeners); addInputListener( app.mInput); std::cout << "update listeners: " << app.listeners .size() << "\n"; if (app.listeners .size() == 2) OgreAssert( app.listeners.size() == 2, ""); input_update.disable(); OgreAssert(false, ""); } else { app.listeners.clear(); app.listeners.push_back( &mKbd); } } else input_update.disable(); std::cout << "input update " << app.listeners.size() << "\n"; }); #endif ECS::get().set( { initialiseImGui(), nullptr, { getImGuiInputListener(), &mKbd } }); Sound::setup(); Sound::ding(); } void create_entity_node(const Ogre::String &name, int key) { Ogre::Entity *ent = mScnMgr->createEntity(name); Ogre::SceneNode *pnode = mScnMgr->getRootSceneNode()->createChildSceneNode( "ent:" + name + Ogre::StringConverter::toString(key), mCameraPivot->getPosition(), mCameraPivot->getOrientation()); pnode->attachObject(ent); Ogre::Quaternion q = pnode->getOrientation(); Ogre::Radian yaw = q.getYaw(); Ogre::Quaternion nq(yaw, Ogre::Vector3(0, 1, 0)); pnode->setOrientation(nq); set_gui_active(false); ECS::get().setWindowGrab(true); } bool get_gui_active() { return ECS::get().get().enabled; } void set_gui_active(bool active) { ECS::get().get_mut().enabled = active; ECS::get().modified(); } Ogre::Camera *getCamera() { return mCamera; } flecs::entity getPlayer() const { flecs::entity player = ECS::get().lookup("ECS::CharacterModule::player"); return player; } void enableDbgDraw(bool enable) { enabldDbgDraw = enable; } bool isEnabledDbgDraw() const { return enabldDbgDraw; } }; void KeyboardListener::frameRendered(const Ogre::FrameEvent &evt) { if (fps_timer.getMilliseconds() > 1000.0f) { std::cout << "FPS: " << mApp->getRenderWindow()->getStatistics().lastFPS << " "; std::cout << "Draw calls: " << mApp->getRenderWindow()->getStatistics().batchCount << " "; fps_timer.reset(); std::cout << "Drops: " << mApp->getRenderWindow() ->getStatistics() .vBlankMissCount << "\n"; fps_timer.reset(); } update(evt.timeSinceLastFrame); if (!isGuiEnabled() || (isGuiEnabled() && ECS::get().narrationBox)) { mApp->updateWorld(evt.timeSinceLastFrame); if (mInitDelay >= 0.0f) mInitDelay -= evt.timeSinceLastFrame; } if (!isGuiEnabled() && ECS::get().has()) { ECS::Input &input = ECS::get().get_mut(); input.control = control; input.mouse = mouse; mouse.x = 0; mouse.y = 0; input.wheel_y = wheel_y; wheel_y = 0; input.mouse_moved = mouse_moved; input.wheel_moved = wheel_moved; } } int main() { App ctx; ctx.configure(); // ctx.runRenderingSettingsDialog(); // get a pointer to the already created root // register for input events // KeyHandler keyHandler; // ctx.addInputListener(&keyHandler); ctx.enableDbgDraw(false); ctx.getRoot()->startRendering(); ctx.setWindowGrab(false); ctx.closeApp(); return 0; }