diff --git a/src/editor/CMakeLists.txt b/src/editor/CMakeLists.txt new file mode 100644 index 0000000..ad9d5b1 --- /dev/null +++ b/src/editor/CMakeLists.txt @@ -0,0 +1,56 @@ +cmake_minimum_required(VERSION 3.13.0) +project(Editor) + +find_package(OGRE REQUIRED COMPONENTS Bites Paging Terrain CONFIG) +find_package(ZLIB) +find_package(SDL2) +find_package(assimp REQUIRED CONFIG) +find_package(OgreProcedural REQUIRED CONFIG) +find_package(pugixml REQUIRED CONFIG) +find_package(flecs REQUIRED CONFIG) + +set(COPY_DIRECTORIES resources skybox water) +set(INSTALL_DEPS ${CMAKE_CURRENT_BINARY_DIR}/resources.cfg) +foreach(DIR_NAME ${COPY_DIRECTORIES}) + add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${DIR_NAME} + COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_BINARY_DIR}/../../${DIR_NAME} ${CMAKE_CURRENT_BINARY_DIR}/${DIR_NAME} + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/../../${DIR_NAME} + ) + list(APPEND INSTALL_DEPS ${CMAKE_CURRENT_BINARY_DIR}/${DIR_NAME}) +endforeach() + +add_custom_target(install_resources DEPENDS ${INSTALL_DEPS}) +add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/resources.cfg + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/../../resources.cfg ${CMAKE_CURRENT_BINARY_DIR}/resources.cfg + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/../../resources.cfg +) + +add_library(editor STATIC EditorGizmoModule.cpp EditorInputModule.cpp) +target_link_libraries(editor PRIVATE + OgreMain + GameData +) + +add_executable(Editor main.cpp) +target_link_libraries(Editor PRIVATE + OgreMain + OgreBites + OgrePaging + OgreTerrain + OgreOverlay + OgreMeshLodGenerator + GameData + sound + sceneloader + editor + world-build + physics + lua + flecs::flecs_static +) +if(OGRE_STATIC) + target_link_options(Editor PRIVATE -static-libstdc++ -static-libgcc) +endif() +target_compile_definitions(Editor PRIVATE FLECS_CPP_NO_AUTO_REGISTRATION) +add_dependencies(Editor install_resources) + diff --git a/src/editor/EditorGizmoModule.cpp b/src/editor/EditorGizmoModule.cpp new file mode 100644 index 0000000..a2397a3 --- /dev/null +++ b/src/editor/EditorGizmoModule.cpp @@ -0,0 +1,76 @@ +#include +#include +#include +#include +#include "Components.h" +#include "GameData.h" +#include "EditorGizmoModule.h" + +namespace ECS +{ +static Ogre::MaterialPtr +createVertexColorNoDepthMaterial(const std::string &materialName) +{ + Ogre::MaterialPtr material = + Ogre::MaterialManager::getSingleton().create( + materialName, + Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); + + Ogre::Pass *pass = material->getTechnique(0)->getPass(0); + + // Disable dynamic lighting, so only vertex colors are used + pass->setLightingEnabled(false); + pass->setCullingMode(Ogre::CULL_NONE); + + // Set the material to use vertex colors for diffuse, ambient, and specular components + pass->setVertexColourTracking(Ogre::TVC_DIFFUSE | Ogre::TVC_AMBIENT | + Ogre::TVC_SPECULAR); + + // Disable depth checking (so it renders regardless of what's already in the depth buffer) + // pass->setDepthCheckEnabled(false); + pass->setDepthCheckEnabled(true); + + // Disable depth writing (so subsequent objects are not occluded by this one) + // pass->setDepthWriteEnabled(false); + pass->setDepthWriteEnabled(true); + + // Optional: Set scene blending to alpha blending if you use vertex alpha + // pass->setSceneBlending(Ogre::SBT_ALPHA_BLEND); + + // Load the material (important if resources are not automatically loaded) + material->load(); + + return material; +} +EditorGizmoModule::EditorGizmoModule(flecs::world &ecs) +{ + ecs.module(); + ecs.component().add(flecs::Singleton); + ecs.component().add(flecs::Singleton); + ecs.observer("SetupDebugMaterial") + .event(flecs::OnAdd) + .each([](EditorDebugMaterial &mdbg) { + mdbg.material = createVertexColorNoDepthMaterial( + "Debug/vcolor"); + }); + ecs.observer("SetupGizmo") + .event(flecs::OnSet) + .each([](const EditorDebugMaterial &mdbg, EditorGizmo &gizmo) { + gizmo.gizmo->begin(mdbg.material, + Ogre::RenderOperation::OT_LINE_LIST); + gizmo.gizmo->position(Ogre::Vector3(0, 0, 0)); + gizmo.gizmo->colour(Ogre::ColourValue(0, 0, 1, 1)); + gizmo.gizmo->position(Ogre::Vector3(0, 0, 1)); + gizmo.gizmo->colour(Ogre::ColourValue(0, 0, 1, 1)); + gizmo.gizmo->position(Ogre::Vector3(0, 0, 0)); + gizmo.gizmo->colour(Ogre::ColourValue(0, 1, 0, 1)); + gizmo.gizmo->position(Ogre::Vector3(0, 1, 0)); + gizmo.gizmo->colour(Ogre::ColourValue(0, 1, 0, 1)); + gizmo.gizmo->position(Ogre::Vector3(0, 0, 0)); + gizmo.gizmo->colour(Ogre::ColourValue(1, 0, 0, 1)); + gizmo.gizmo->position(Ogre::Vector3(1, 0, 0)); + gizmo.gizmo->colour(Ogre::ColourValue(1, 0, 0, 1)); + gizmo.gizmo->end(); + }); +} +} diff --git a/src/editor/EditorGizmoModule.h b/src/editor/EditorGizmoModule.h new file mode 100644 index 0000000..4e1409a --- /dev/null +++ b/src/editor/EditorGizmoModule.h @@ -0,0 +1,18 @@ +#ifndef _EDITOR_GIZMO_MODULE_H_ +#define _EDITOR_GIZMO_MODULE_H_ +#include +#include +namespace ECS +{ +struct EditorDebugMaterial { + Ogre::MaterialPtr material; +}; +struct EditorGizmo { + std::shared_ptr gizmo; + Ogre::SceneNode *sceneNode; +}; +struct EditorGizmoModule { + EditorGizmoModule(flecs::world &ecs); +}; +} +#endif \ No newline at end of file diff --git a/src/editor/EditorInputModule.cpp b/src/editor/EditorInputModule.cpp new file mode 100644 index 0000000..ba9cbea --- /dev/null +++ b/src/editor/EditorInputModule.cpp @@ -0,0 +1,172 @@ +#include +#include +#include "Components.h" +#include "GameData.h" +#include "EditorInputModule.h" + +namespace ECS +{ +static void updateCameraGoal(Camera &camera, Ogre::Real deltaYaw, + Ogre::Real deltaPitch, Ogre::Real deltaZoom) +{ + camera.mCameraPivot->yaw(Ogre::Degree(deltaYaw), Ogre::Node::TS_PARENT); + if (!(camera.mPivotPitch + deltaPitch > 25 && deltaPitch > 0) && + !(camera.mPivotPitch + deltaPitch < -60 && deltaPitch < 0)) { + camera.mCameraPivot->pitch(Ogre::Degree(deltaPitch), + Ogre::Node::TS_LOCAL); + camera.mPivotPitch += deltaPitch; + } + Ogre::Real dist = camera.mCameraGoal->_getDerivedPosition().distance( + camera.mCameraPivot->_getDerivedPosition()); + Ogre::Real distChange = deltaZoom * dist; + + // bound the zoom + if (!(dist + distChange < 8 && distChange < 0) && + !(dist + distChange > 25 && distChange > 0)) + camera.mCameraGoal->translate(0, 0, distChange, + Ogre::Node::TS_LOCAL); +} +EditorInputModule::EditorInputModule(flecs::world &ecs) +{ + ecs.module(); + ecs.system("HandleEditorInput") + .kind(flecs::OnUpdate) + .each([this](const EngineData &eng, Input &input, + Camera &camera) { + if (ECS::player.is_valid()) + return; + if (!camera.configured) { + // create a pivot at roughly the character's shoulder + camera.mCameraPivot = + eng.mScnMgr->getRootSceneNode() + ->createChildSceneNode(); + camera.mCameraGoal = + camera.mCameraPivot + ->createChildSceneNode( + Ogre::Vector3(0, 2, 0)); + camera.mCameraNode->setPosition( + camera.mCameraPivot->getPosition() + + camera.mCameraGoal->getPosition()); + camera.mCameraGoal->lookAt( + Ogre::Vector3(0, 0, -10), + Ogre::Node::TS_PARENT); + camera.mCameraPivot->setFixedYawAxis(true); + camera.mCameraGoal->setFixedYawAxis(true); + camera.mCameraNode->setFixedYawAxis(true); + // our model is quite small, so reduce the clipping planes + camera.mCamera->setNearClipDistance(0.1f); + camera.mCamera->setFarClipDistance(700); + + camera.mPivotPitch = 0; + camera.configured = true; + } + /* handle input */ + // if (input.control == input.control_prev) + // return; + uint32_t pressed = input.control & ~input.control_prev; + uint32_t released = input.control_prev & ~input.control; + uint32_t active = input.control; + float zaxis = input.motion.z; + zaxis *= 0.9f; + if (!camera.mCameraPivot || !camera.mCameraGoal) + return; + // OgreAssert(false, "EditorInput"); + if (pressed & 1) + std::cout << "W pressed\n"; + if (released & 1) + std::cout << "W released\n"; + if (active & 1) + zaxis -= 1.0f; + if (active & 4) + zaxis += 1.0f; + if (zaxis > -1.0f && zaxis < 1.0f) + zaxis = 0.0f; + else + zaxis = Ogre::Math::Sign(zaxis); + if (active & 32) + input.act = true; + else + input.act = false; + if (pressed & 32) + input.act_pressed = true; + else + input.act_pressed = false; + if (active & 64) + input.act2 = true; + else + input.act2 = false; + if (pressed & 64) + input.act2_pressed = true; + else + input.act2_pressed = false; + input.motion.z = zaxis; + float xaxis = input.motion.x; + xaxis *= 0.9f; + if (active & 2) + xaxis = -1.0f; + if (active & 8) + xaxis += 1.0f; + if (xaxis > -1.0f && xaxis < 1.0f) + xaxis = 0.0f; + else + xaxis = Ogre::Math::Sign(xaxis); + input.motion.x = xaxis; + if (active & 16) + input.fast = true; + else + input.fast = false; + if (input.control & 512) { + if (input.mouse_moved) { + updateCameraGoal(camera, + -0.18f * input.mouse.x, + -0.12f * input.mouse.y, + 0); + input.mouse_moved = false; + input.mouse.x = 0; + input.mouse.y = 0; + // move the camera smoothly to the goal + Ogre::Vector3 goalOffset = + camera.mCameraGoal + ->_getDerivedPosition() - + camera.mCameraNode + ->getPosition(); + camera.mCameraNode->translate( + goalOffset * eng.delta * 9.0f); + camera.mCameraNode->_setDerivedOrientation( + camera.mCameraGoal + ->_getDerivedOrientation()); + } + if ((input.control & 0xf)) { + Ogre::Vector3 forward = + camera.mCameraPivot + ->_getDerivedOrientation() * + Ogre::Vector3::UNIT_Z; + Ogre::Vector3 side = + camera.mCameraPivot + ->_getDerivedOrientation() * + Ogre::Vector3::UNIT_X; + forward.y = 0.0f; + side.y = 0.0f; + Ogre::Vector3 translation = + forward * input.motion.z + + side * input.motion.x; + camera.mCameraPivot->translate( + translation * 20.0f * eng.delta, + Ogre::Node::TS_WORLD); + } + } + input.control_prev = input.control; +#if 0 + if (input.wheel_moved) { + updateCameraGoal(camera, 0, 0, + -0.15f * input.wheel_y); + std::cout << "mouse moved: " << input.mouse_moved + << " " << std::endl; + input.wheel_moved = false; + input.wheel_y = 0; + } +#endif + ECS::get().modified(); + }); +} +} \ No newline at end of file diff --git a/src/editor/EditorInputModule.h b/src/editor/EditorInputModule.h new file mode 100644 index 0000000..b360c0c --- /dev/null +++ b/src/editor/EditorInputModule.h @@ -0,0 +1,10 @@ +#ifndef _EDITOR_INPUT_MODULE_H_ +#define _EDITOR_INPUT_MODULE_H_ +#include +namespace ECS +{ +struct EditorInputModule { + EditorInputModule(flecs::world &ecs); +}; +} +#endif \ No newline at end of file diff --git a/src/editor/main.cpp b/src/editor/main.cpp new file mode 100644 index 0000000..44843c8 --- /dev/null +++ b/src/editor/main.cpp @@ -0,0 +1,791 @@ +#include +#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 "AppModule.h" +#include "EditorGizmoModule.h" +#include "EditorInputModule.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(); + 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 0 + if (isGuiEnabled()) + return false; + if (evt.keysym.sym == OgreBites::SDLK_ESCAPE) { + OgreAssert(ECS::get().has(), ""); + setGuiEnabled(true); + if (ECS::get().has()) + ECS::get().setWindowGrab(false); + return true; + } +#endif + 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; + else if (key == 'e') + control |= 32; + else if (key == 'f') + control |= 64; + if (key == 'w' || key == 'a' || key == 's' || key == 'd' || + key == 'e' || key == OgreBites::SDLK_LSHIFT) + return true; + return false; + } + bool keyReleased(const OgreBites::KeyboardEvent &evt) override + { + OgreBites::Keycode key = evt.keysym.sym; +#if 0 + if (isGuiEnabled()) + return false; +#endif + 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 + { + mouse.x = evt.xrel; + mouse.y = evt.yrel; + mouse_moved = true; + /* no special mouse handling */ + return true; + } + bool mouseWheelRolled(const OgreBites::MouseWheelEvent &evt) override + { + /* 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; + if ((int)evt.button == 3) + control |= 512; + else + control &= ~512; + return false; + } + bool mouseReleased(const OgreBites::MouseButtonEvent &evt) override + { + std::cout << "Mouse release " << (int)evt.button << " " + << (int)evt.clicks << "\n"; + if ((int)evt.button == 1) + control &= ~256; + if ((int)evt.button == 3) + control &= ~512; + return false; + } + void frameRendered(const Ogre::FrameEvent &evt) override; +}; + +class App : public OgreBites::ApplicationContext { + Ogre::SceneNode *mCameraNode, *mCameraPivot, *mCameraGoal; + Ogre::Camera *mCamera; + Ogre::Real mPivotPitch; + Ogre::SceneManager *mScnMgr; + Ogre::Viewport *mViewport; + SkyBoxRenderer *sky; + bool mGrab; + KeyboardListener mKbd; + +public: + App() + : OgreBites::ApplicationContext("ChoroGame") + , mKbd(this) + , mGrab(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()); + } + bool isWindowGrab() + { + return mGrab; + } + void locateResources() override + { + Ogre::ResourceGroupManager::getSingleton().createResourceGroup( + "Water", true); + Ogre::ResourceGroupManager::getSingleton().createResourceGroup( + "LuaScripts", false); + Ogre::ResourceGroupManager::getSingleton().addResourceLocation( + "./lua-scripts", "FileSystem", "LuaScripts", true, + 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(false); + 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"; +#if 0 + mDbgDraw->setDebugMode(mDbgDraw->getDebugMode() | + btIDebugDraw::DBG_DrawContactPoints); +#endif + } + Ogre::SceneManager *getSceneManager() + { + return mScnMgr; + } + Ogre::Timer mTerrainUpd; + // 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) + { + if (!ECS::get().has()) + return; + /* Update window grab */ + ECS::GUI &gui = ECS::get().get_mut(); + if (gui.grabChanged) { + gui.grab = false; + setWindowGrab(gui.grab); + gui.grabChanged = false; + ECS::get().modified(); + } + ECS::update(delta); + } + 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::setupEditor(mScnMgr, /*mDynWorld.get(), */ mCameraNode, + mCamera, getRenderWindow()); + ECS::get().import (); + ECS::get().import (); + ECS::get().set( + { getRenderWindow(), getDisplayDPI() }); + ECS::get() + .observer("UpdateGrab") + .event(flecs::OnSet) + .each([this](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](ECS::App &app) { + if (app.mInput) + removeInputListener(app.mInput); + delete app.mInput; + app.mInput = + OGRE_NEW OgreBites::InputListenerChain( + app.listeners); + addInputListener(app.mInput); + }); + ECS::get().set( + { initialiseImGui(), + nullptr, + { getImGuiInputListener(), &mKbd } }); + ECS::get().add(); + std::shared_ptr manualObj( + mScnMgr->createManualObject("EditorGizmo")); + Ogre::SceneNode *gizmoNode = + mScnMgr->getRootSceneNode()->createChildSceneNode( + "EditorGizmoNode"); + gizmoNode->attachObject(manualObj.get()); + ECS::get().set({ manualObj, gizmoNode }); +#if 0 + ECS::get() + .system("UpdateEditorCamera") + .kind(flecs::OnUpdate) + .each([&](const ECS::EngineData &eng, + ECS::Camera &camera, + const ECS::Input &input) { + if (!camera.configured) { + // create a pivot at roughly the character's shoulder + camera.mCameraPivot = + eng.mScnMgr->getRootSceneNode() + ->createChildSceneNode(); + camera.mCameraGoal = + camera.mCameraPivot + ->createChildSceneNode( + Ogre::Vector3( + 0, 2, + 0)); + camera.mCameraNode->setPosition( + camera.mCameraPivot + ->getPosition() + + camera.mCameraGoal + ->getPosition()); + camera.mCameraGoal->lookAt( + Ogre::Vector3(0, 0, -10), + Ogre::Node::TS_PARENT); + camera.mCameraPivot->setFixedYawAxis( + true); + camera.mCameraGoal->setFixedYawAxis( + true); + camera.mCameraNode->setFixedYawAxis( + true); + // our model is quite small, so reduce the clipping planes + camera.mCamera->setNearClipDistance( + 0.1f); + camera.mCamera->setFarClipDistance(700); + + camera.mPivotPitch = 0; + camera.configured = true; + } else { + // place the camera pivot roughly at the character's shoulder +#if 0 + camera.mCameraPivot->setPosition( + ch.mBodyNode->getPosition() + + Ogre::Vector3::UNIT_Y * CAM_HEIGHT); +#endif + // move the camera smoothly to the goal + Ogre::Vector3 goalOffset = + camera.mCameraGoal + ->_getDerivedPosition() - + camera.mCameraNode + ->getPosition(); + camera.mCameraNode->translate( + goalOffset * eng.delta * 9.0f); + // always look at the pivot +#if 0 + camera.mCameraNode->lookAt( + camera.mCameraPivot + ->_getDerivedPosition(), + Ogre::Node::TS_PARENT); +#endif + camera.mCameraNode->_setDerivedOrientation( + camera.mCameraGoal + ->_getDerivedOrientation()); + } + if (input.control & 512 && input.mouse_moved) { + mCameraPivot->yaw( + Ogre::Radian(-input.mouse.x * + 3.0f * eng.delta)); + } + }); +#endif + } + 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) + { + ECS::get_mut().enableDbgDraw = enable; + ECS::modified(); + } + bool isEnabledDbgDraw() const + { + return ECS::get().enableDbgDraw; + } +}; +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(); + } + /* for editor we always update world */ + /* TODO: implement pause */ + 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; + ECS::modified(); + } else { + 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; + ECS::modified(); + } +} + +int main() +{ + App ctx; + ctx.configure(); + ctx.enableDbgDraw(false); + ctx.getRoot()->startRendering(); + ctx.setWindowGrab(false); + ctx.closeApp(); + return 0; +} diff --git a/src/gamedata/CharacterModule.cpp b/src/gamedata/CharacterModule.cpp index d4df303..b185aa6 100644 --- a/src/gamedata/CharacterModule.cpp +++ b/src/gamedata/CharacterModule.cpp @@ -41,6 +41,8 @@ CharacterModule::CharacterModule(flecs::world &ecs) ecs.system("HandleInput") .kind(flecs::OnUpdate) .each([this](Input &input, Camera &camera) { + if (!ECS::player.is_valid()) + return; /* handle input */ // if (input.control == input.control_prev) // return; diff --git a/src/gamedata/GUIModule.cpp b/src/gamedata/GUIModule.cpp index 7a8b837..c997ce8 100644 --- a/src/gamedata/GUIModule.cpp +++ b/src/gamedata/GUIModule.cpp @@ -8,10 +8,12 @@ #include #include #include +#include #include "GameData.h" #include "Components.h" #include "LuaData.h" #include "AppModule.h" +#include "TerrainModule.h" #include "GUIModule.h" namespace ECS { @@ -472,6 +474,9 @@ struct EditorGUIListener : public Ogre::RenderTargetListener { bool enableMapEditor; ImFont *smallFont, *midFont, *bigFont; Ogre::FontPtr _smallFont, _midFont, _bigFont; + Ogre::TexturePtr worldMap; + Ogre::Image worldMapImage; + EditorGUIListener(Ogre::ImGuiOverlay *overlay) : Ogre::RenderTargetListener() { @@ -488,13 +493,47 @@ struct EditorGUIListener : public Ogre::RenderTargetListener { OgreAssert(midFont, "Could not load font"); bigFont = overlay->addFont("bigFont", "General"); OgreAssert(bigFont, "Could not load font"); + worldMapImage.load( + "world_map.png", + Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); #if 0 - Ogre::FontPtr _midFont = createFont("midFont", "General", - "Kenney Bold.ttf", 28.0f); -#endif -#if 0 - ImGui::GetIO().Fonts->Build(); + int x, y, i, j; + for (y = 0; y < worldMapImage.getWidth(); y++) + for (x = 0; x < worldMapImage.getWidth(); x++) { + float r = 0.0f; + for (i = -2; i < 3; i++) + for (j = -2; j < 3; j++) { + int xj = Ogre::Math::Clamp( + x + j, 0, + (int)worldMapImage + .getWidth() - + 1); + int yi = Ogre::Math::Clamp( + y + i, 0, + (int)worldMapImage + .getHeight() - + 1); + r += worldMapImage + .getColourAt(xj, + yi, 0) + .r; + } + r /= 25.0f; + Ogre::ColourValue cv = + worldMapImage.getColourAt(x, y, 0); + cv.r = r; + worldMapImage.setColourAt(cv, x, y, 0); + } #endif + worldMap = Ogre::TextureManager::getSingleton().createManual( + "worldMap", + Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + Ogre::TEX_TYPE_2D, worldMapImage.getWidth(), + worldMapImage.getHeight(), + worldMapImage.getNumMipmaps(), + worldMapImage.getFormat(), Ogre::TU_DYNAMIC_WRITE_ONLY); + worldMap->loadImage(worldMapImage); + OgreAssert(worldMap, "could not load world map"); } Ogre::FontPtr createFont(const Ogre::String &name, const Ogre::String &group, @@ -533,6 +572,7 @@ struct EditorGUIListener : public Ogre::RenderTargetListener { Ogre::Root::getSingleton().queueEndRendering(); if (ImGui::Button("Return")) ECS::get().get().finish(); +#if 0 if (ImGui::Button("Enable/Disable item editor")) { enableEditor ^= true; if (enableEditor) @@ -544,6 +584,8 @@ struct EditorGUIListener : public Ogre::RenderTargetListener { enableEditor = false; } ImGui::Text("Text message..."); +#endif + ImGui::Checkbox("Enable Map", &enableMapEditor); ImGui::End(); } void create_entity_node(const Ogre::String &name, int key) @@ -573,6 +615,7 @@ struct EditorGUIListener : public Ogre::RenderTargetListener { ECS::get().get().finish(); } +#if 0 void buildings_editor() { int i; @@ -602,6 +645,7 @@ struct EditorGUIListener : public Ogre::RenderTargetListener { } ImGui::End(); } +#endif void position_editor(Ogre::SceneNode *node) { Ogre::Vector3 position = node->getPosition(); @@ -642,124 +686,561 @@ struct EditorGUIListener : public Ogre::RenderTargetListener { ImGui::Text("Name: %s", pname.c_str()); } } +#if 0 void map_editor() { } - void preview(const Ogre::RenderTargetViewportEvent &evt) +#endif + int pos_x = 0; + int pos_y = 0; + int top_x = 0; + int top_y = 0; + int selected_x = 0; + int selected_y = 0; + bool locationSelected = false; + float strength = 0.0f; + int size = 0; + void updateWorldTexture() { - int i; - Ogre::ImGuiOverlay::NewFrame(); - std::cout << "GUI enabled: " << ECS::get().get().enabled - << std::endl; - if (ECS::get().get().enabled) { - buttons_panel(); - buildings_editor(); - map_editor(); - ImVec2 size = ImGui::GetMainViewport()->Size; - float window_width = size.x * 0.2f; - if (window_width > panel_width) - window_width = panel_width; - float window_height = size.y * 0.5f - 20; - ImGui::SetNextWindowPos(ImVec2(size.x - window_width, - size.y * 0.5f + 20), - ImGuiCond_Always); - ImGui::SetNextWindowSize(ImVec2(window_width, - window_height), - ImGuiCond_Always); - // ImGui::Begin("Dumb and Stupid", &mKbd.gui_active); - ImGui::Begin("Panel..."); - std::deque tree_input_queue, - tree_output_queue; - std::vector tree_list; - tree_input_queue.push_back( - ECS::get() - .get() - .mScnMgr->getRootSceneNode()); - tree_input_queue.push_back(nullptr); - std::set visited; - while (true) { - int new_nodes_count = 0; - while (!tree_input_queue.empty()) { - int child; - Ogre::SceneNode *item = - tree_input_queue.front(); - tree_input_queue.pop_front(); - if (item && - visited.find(item) == - visited.end()) { // new node - new_nodes_count++; - tree_output_queue.push_back( - item); - visited.insert(item); - const Ogre::Node::ChildNodeMap - &children = - item->getChildren(); - for (child = 0; - child < children.size(); - child++) { - tree_output_queue.push_back( - static_cast< - Ogre::SceneNode - *>( - children[child])); - tree_output_queue - .push_back( - nullptr); - } - } else - tree_output_queue.push_back( - item); - } - if (new_nodes_count == 0) - break; - tree_input_queue = tree_output_queue; - tree_output_queue.clear(); - } - tree_list.insert(tree_list.begin(), - tree_output_queue.begin(), - tree_output_queue.end()); - int count = 0; - int depth = 0; - std::vector check_depth; - int max_depth = 0; - check_depth.push_back(0); - for (count = 0; count < tree_list.size(); count++) { - int t; + // Get the hardware pixel buffer + Ogre::HardwarePixelBufferSharedPtr pixelBuffer = + worldMap->getBuffer(); - Ogre::SceneNode *node = tree_list[count]; - if (node && max_depth >= depth) { - Ogre::String name = node->getName(); - if (name.length() == 0) { - name = "Node #" + - Ogre::StringConverter:: - toString(count); - } - if (ImGui::TreeNode(name.c_str())) { - check_depth.push_back( - max_depth); - max_depth++; - ImGui::Text("%s", - (name + "##caption") - .c_str()); - position_editor(node); - ImGui::Separator(); - orientation_editor(node); - ImGui::Separator(); - ImGui::Text("Attachments"); - attachments_editor(node); - } - } else if (!node && max_depth >= depth) { - max_depth = check_depth.back(); - check_depth.pop_back(); - ImGui::TreePop(); + // Lock the buffer for writing, discarding previous contents for performance + pixelBuffer->lock(Ogre::HardwareBuffer::HBL_DISCARD); + + // Get information about the locked region (PixelBox) + const Ogre::PixelBox &pixelBox = pixelBuffer->getCurrentLock(); + + // Ensure the image format matches the texture format + OgreAssert(pixelBox.format == worldMapImage.getFormat(), + "bad format"); + + // Copy the image data to the pixel box's data pointer + memcpy(pixelBox.data, worldMapImage.getData(), + worldMapImage.getSize()); + + // Unlock the buffer to apply changes to the GPU + pixelBuffer->unlock(); + } + void updateHeightmap() + { + Ogre::Vector3 worldPos = + ECS::get().mCameraPivot->_getDerivedPosition(); + TerrainModule::update_heightmap(worldMapImage); + long x, y; + ECS::get() + .mTerrainGroup->convertWorldPositionToTerrainSlot( + worldPos, &x, &y); + int i, j; + for (j = -1; j < 2; j++) + for (i = -1; i < 2; i++) { + Ogre::Terrain *terrain = + ECS::get() + .mTerrainGroup->getTerrain( + x + j, y + i); + if (terrain && terrain->isLoaded()) { + terrain->dirty(); + terrain->updateDerivedData(true); + terrain->updateGeometry(); } - if (tree_list[count]) - depth++; - else - depth--; } - ImGui::Spacing(); - ImGui::End(); + + ECS::get().mTerrainGroup->updateDerivedData(true); + ECS::get().mTerrainGroup->updateGeometry(); + ECS::get().mTerrainGroup->update(true); + } + void setCameraPos() + { + Ogre::Vector3 worldPos = + ECS::get().mCameraPivot->_getDerivedPosition(); + worldPos.x = TerrainModule::get_world_x(selected_x); + worldPos.z = TerrainModule::get_world_y(selected_y); + Ogre::Vector3 cameraPos = + ECS::get().mCameraPivot->_getDerivedPosition(); + cameraPos.x = worldPos.x; + cameraPos.z = worldPos.z; + cameraPos.y = + TerrainModule::get_height( + ECS::get().mTerrainGroup, cameraPos) + + 10.0f; + if (cameraPos.y < 0.0f) + cameraPos.y = 10.0f; + ECS::get().mCameraPivot->_setDerivedPosition(cameraPos); + cameraPos = + ECS::get().mCameraGoal->_getDerivedPosition(); + ECS::get().mCameraNode->_setDerivedPosition(cameraPos); + std::cout << cameraPos << std::endl; + std::cout << worldPos << std::endl; + std::cout << selected_x << " " << selected_y << std::endl; + ECS::get().mTerrainGroup->updateDerivedData(true); + ECS::get().mTerrainGroup->updateGeometry(); + ECS::get().mTerrainGroup->update(true); + } + void worldMapView() + { + OgreAssert(TerrainModule::get_img_x(0) == + worldMap->getWidth() / 2, + "get_img_x"); + OgreAssert(TerrainModule::get_img_y(0) == + worldMap->getHeight() / 2, + "get_img_x"); + OgreAssert(TerrainModule::get_world_x(worldMap->getWidth() / + 2) == 0.0f, + "get_world_x"); + OgreAssert(TerrainModule::get_world_y(worldMap->getHeight() / + 2) == 0.0f, + "get_world_y"); + if (ECS::get().mCameraPivot) { + Ogre::Vector3 worldPos = + ECS::get() + .mCameraPivot->_getDerivedPosition(); + selected_x = TerrainModule::get_img_x(worldPos.x); + selected_y = TerrainModule::get_img_y(worldPos.z); + locationSelected = true; + std::cout << worldPos << std::endl; + std::cout << selected_x << " " << selected_y + << std::endl; + OgreAssert(selected_x >= 0 && + selected_x < worldMap->getWidth(), + "mix width"); + OgreAssert(selected_y >= 0 && + selected_y < worldMap->getHeight(), + "mix height"); + } + ImGui::SetNextWindowSizeConstraints(ImVec2(512 + 20, 512 + 20), + ImVec2(768, 768)); + // ImGui::SetNextWindowScroll( + // ImVec2(worldMap->getWidth(), worldMap->getHeight())); + ImGui::Begin("WorldMap..."); + ImGui::Spacing(); + ImGui::BeginChild("WorldMap...", ImVec2(480, 480), + ImGuiChildFlags_None, + ImGuiWindowFlags_HorizontalScrollbar); + ImGui::Spacing(); + Ogre::ResourceHandle hdl = worldMap->getHandle(); + int w = worldMap->getWidth(); + int h = worldMap->getHeight(); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, + ImVec2((float)5, (float)5)); + ImGui::ImageButton("WorldMapPress", (ImTextureID)hdl, + ImVec2(w, h)); + ImVec2 mouse_absolute_pos = ImGui::GetMousePos(); + ImVec2 item_absolute_pos = ImGui::GetItemRectMin(); + top_x = item_absolute_pos.x + 5; + top_y = item_absolute_pos.y + 5; + bool hovered = false; +#if 0 + pos_x = mouse_absolute_pos.x - item_absolute_pos.x - 5; + pos_y = mouse_absolute_pos.y - item_absolute_pos.y - 5; +#endif + if (ImGui::IsItemHovered()) { + ImVec2 mouse_absolute_pos = ImGui::GetMousePos(); + ImVec2 item_absolute_pos = ImGui::GetItemRectMin(); + pos_x = mouse_absolute_pos.x - item_absolute_pos.x - 5; + pos_y = mouse_absolute_pos.y - item_absolute_pos.y - 5; + hovered = true; + } + pos_x = Ogre::Math::Clamp(pos_x, 0, + (int)worldMap->getWidth() - 1); + pos_y = Ogre::Math::Clamp(pos_y, 0, + (int)worldMap->getHeight() - 1); + if (pos_x < 0) + pos_x = 0; + if (pos_x >= worldMap->getWidth()) + pos_x = worldMap->getWidth() - 1; + if (pos_y < 0) + pos_y = 0; + if (pos_y >= worldMap->getHeight()) + pos_y = worldMap->getHeight() - 1; + if (ImGui::IsItemActivated()) { + locationSelected = true; + selected_x = pos_x; + selected_y = pos_y; + OgreAssert(selected_x >= 0 && + selected_x < worldMap->getWidth(), + "mix width"); + OgreAssert(selected_y >= 0 && + selected_y < worldMap->getHeight(), + "mix height"); + setCameraPos(); + std::cout << "worldClickPos: " << pos_x << " " << pos_y + << std::endl; + } + ImDrawList *draw_list = ImGui::GetWindowDrawList(); + draw_list->AddCircleFilled(ImVec2(top_x + pos_x, top_y + pos_y), + 1.0f, IM_COL32(0, 255, 0, 255)); + if (locationSelected) + draw_list->AddCircleFilled( + ImVec2(top_x + selected_x, top_y + selected_y), + 4.0f, IM_COL32(64, 255, 64, 255)); + ImGui::PopStyleVar(); + ImGui::Spacing(); + ImGui::EndChild(); + ImGui::SameLine(); + ImGui::BeginChild("WorldMap...", ImVec2(64, 480), + ImGuiChildFlags_None, + ImGuiWindowFlags_HorizontalScrollbar); + ImGui::EndChild(); + ImGui::Spacing(); + ImGui::BeginChild("WorldMap Bottom...", ImVec2(0, 0)); + ImGui::Text("Position: %d %d", pos_x, pos_y); + ImGui::Text("Selected Position: %d %d", selected_x, selected_y); + ImGui::Text("Height: %f", + worldMapImage.getColourAt(pos_x, pos_y, 0).r); + ImGui::Text( + "Selected height: %f", + worldMapImage.getColourAt(selected_x, selected_y, 0).r); + if (ImGui::Button("Update terrain")) { + ECS::get().mTerrainGroup->update(true); + ECS::get().mTerrainGroup->updateDerivedData( + true); + ECS::get().mTerrainGroup->updateGeometry(); + } + ImGui::SliderFloat("Strength...", &strength, 0.0f, 0.2f); + ImGui::SliderInt("Size", &size, 0, 8); + bool riseLower = false; + bool riseLower2 = false; + bool smooth = false; + bool setLevel = false; + float setLevelValue = 0.0f; + float riseLowerChange = 0.0f; + if (ImGui::Button("Elevate")) { + riseLower = true; + riseLowerChange = strength; + } + ImGui::SameLine(); + if (ImGui::Button("Lower")) { + riseLower = true; + riseLowerChange = -strength; + } + ImGui::SameLine(); + if (ImGui::Button("Smooth")) { + smooth = true; + } + ImGui::SameLine(); + if (ImGui::Button("Elevate2")) { + riseLower2 = true; + riseLowerChange = strength; + } + if (ImGui::Button("Deepest")) { + setLevel = true; + setLevelValue = 0.0f; + } + ImGui::SameLine(); + if (ImGui::Button("Highest")) { + setLevel = true; + setLevelValue = 1.0f; + } + ImGui::SameLine(); + if (ImGui::Button("Beach")) { + setLevel = true; + setLevelValue = 0.516f; + } + if (ImGui::Button("Shore1")) { + setLevel = true; + setLevelValue = 0.536f; + } + ImGui::SameLine(); + if (ImGui::Button("Shore2")) { + setLevel = true; + setLevelValue = 0.556f; + } + ImGui::SameLine(); + if (ImGui::Button("Shore3")) { + setLevel = true; + setLevelValue = 0.586f; + } + ImGui::SameLine(); + if (ImGui::Button("Shore4")) { + setLevel = true; + setLevelValue = 0.606f; + } + if (ImGui::Button("Shore5")) { + setLevel = true; + setLevelValue = 0.626f; + } + ImGui::SameLine(); + if (ImGui::Button("Shore6")) { + setLevel = true; + setLevelValue = 0.646f; + } + if (riseLower) { + int actualSize = 1 + size * 2; + int i, j; + for (i = -actualSize; i < actualSize + 1; i++) + for (j = -actualSize; j < actualSize + 1; j++) { + if (i * i + j * j > + actualSize * actualSize) + continue; + if (selected_x + j < 0 || + selected_x + j >= + worldMap->getWidth()) + continue; + if (selected_y + i < 0 || + selected_y + i >= + worldMap->getHeight()) + continue; + Ogre::ColourValue cv = + worldMapImage.getColourAt( + selected_x + j, + selected_y + i, 0); + float original = cv.r; + original += riseLowerChange; + original = Ogre::Math::Clamp( + original, 0.0f, 1.0f); + cv.r = original; + worldMapImage.setColourAt( + cv, selected_x + j, + selected_y + i, 0); + } + updateWorldTexture(); + updateHeightmap(); + } + if (riseLower2) { + int actualSize = 1 + size * 2; + int i, j; + Ogre::ColourValue maxcv = worldMapImage.getColourAt( + selected_x, selected_y, 0); + for (i = -actualSize; i < actualSize + 1; i++) + for (j = -actualSize; j < actualSize + 1; j++) { + if (i * i + j * j > + actualSize * actualSize) + continue; + if (selected_x + j < 0 || + selected_x + j >= + worldMap->getWidth()) + continue; + if (selected_y + i < 0 || + selected_y + i >= + worldMap->getHeight()) + continue; + float actualStrength = + riseLowerChange / + (1.0f + (float)(i * i + j * j)); + if (i * i + j * j == + actualSize * actualSize) { + std::cout << actualStrength + << std::endl; + // OgreAssert(false, "strength"); + } + Ogre::ColourValue cv = + worldMapImage.getColourAt( + selected_x + j, + selected_y + i, 0); + float original = + maxcv.r + actualStrength; + original = Ogre::Math::Clamp( + original, 0.0f, 1.0f); + if (cv.r >= original) + continue; + cv.r = original; + worldMapImage.setColourAt( + cv, selected_x + j, + selected_y + i, 0); + } + updateWorldTexture(); + updateHeightmap(); + } + if (smooth) { + int actualSize = 1 + size * 2; + int i, j, k, l; + for (i = -actualSize; i < actualSize + 1; i++) + for (j = -actualSize; j < actualSize + 1; j++) { + if (i * i + j * j > + actualSize * actualSize) + continue; + if (selected_x + j < 0 || + selected_x + j >= + worldMap->getWidth()) + continue; + if (selected_y + i < 0 || + selected_y + i >= + worldMap->getHeight()) + continue; + int kernel = 3; + float original = 0.0f; + Ogre::ColourValue cv; + float count = 0.0f; + for (k = -kernel; k < kernel + 1; k++) { + if (selected_y + i + k < 0 || + selected_y + i + k >= + worldMap->getHeight()) + continue; + for (l = -kernel; + l < kernel + 1; l++) { + if (selected_x + j + l < + 0 || + selected_x + j + + l >= + worldMap->getWidth()) + continue; + cv = worldMapImage.getColourAt( + selected_x + j, + selected_y + i, + 0); + original += cv.r; + count += 1.0f; + } + } + original /= count; + cv = worldMapImage.getColourAt( + selected_x + j, selected_y + i, + 0); + original = Ogre::Math::Clamp( + original, 0.0f, 1.0f); + cv.r = original; + worldMapImage.setColourAt( + cv, selected_x + j, + selected_y + i, 0); + } + updateWorldTexture(); + updateHeightmap(); + } + if (setLevel) { + int actualSize = 1 + size * 2; + int i, j; + float original = setLevelValue; + original = Ogre::Math::Clamp(original, 0.0f, 1.0f); + for (i = -actualSize; i < actualSize + 1; i++) + for (j = -actualSize; j < actualSize + 1; j++) { + if (i * i + j * j > + actualSize * actualSize) + continue; + if (selected_x + j < 0 || + selected_x + j >= + worldMap->getWidth()) + continue; + if (selected_y + i < 0 || + selected_y + i >= + worldMap->getHeight()) + continue; + Ogre::ColourValue cv = + worldMapImage.getColourAt( + selected_x + j, + selected_y + i, 0); + cv.r = original; + worldMapImage.setColourAt( + cv, selected_x + j, + selected_y + i, 0); + } + updateWorldTexture(); + updateHeightmap(); + } + if (ImGui::Button("Save heightmap")) { + updateWorldTexture(); + updateHeightmap(); + TerrainModule::save_heightmap(); + } + ImGui::EndChild(); + ImGui::Spacing(); + ImGui::End(); + } + void panel() + { + ImVec2 size = ImGui::GetMainViewport()->Size; + float window_width = size.x * 0.2f; + if (window_width > panel_width) + window_width = panel_width; + float window_height = size.y * 0.5f - 20; + ImGui::SetNextWindowPos(ImVec2(size.x - window_width, 20), + ImGuiCond_Always); + ImGui::SetNextWindowSize(ImVec2(window_width, window_height), + ImGuiCond_Always); + // ImGui::Begin("Dumb and Stupid", &mKbd.gui_active); + ImGui::Begin("Panel..."); + std::deque tree_input_queue, + tree_output_queue; + std::vector tree_list; + tree_input_queue.push_back( + ECS::get() + .get() + .mScnMgr->getRootSceneNode()); + tree_input_queue.push_back(nullptr); + std::set visited; + while (true) { + int new_nodes_count = 0; + while (!tree_input_queue.empty()) { + int child; + Ogre::SceneNode *item = + tree_input_queue.front(); + tree_input_queue.pop_front(); + if (item && visited.find(item) == + visited.end()) { // new node + new_nodes_count++; + tree_output_queue.push_back(item); + visited.insert(item); + const Ogre::Node::ChildNodeMap + &children = item->getChildren(); + for (child = 0; child < children.size(); + child++) { + tree_output_queue.push_back( + static_cast( + children[child])); + tree_output_queue.push_back( + nullptr); + } + } else + tree_output_queue.push_back(item); + } + if (new_nodes_count == 0) + break; + tree_input_queue = tree_output_queue; + tree_output_queue.clear(); + } + tree_list.insert(tree_list.begin(), tree_output_queue.begin(), + tree_output_queue.end()); + int count = 0; + int depth = 0; + std::vector check_depth; + int max_depth = 0; + check_depth.push_back(0); + for (count = 0; count < tree_list.size(); count++) { + int t; + + Ogre::SceneNode *node = tree_list[count]; + if (node && max_depth >= depth) { + Ogre::String name = node->getName(); + if (name.length() == 0) { + name = "Node #" + + Ogre::StringConverter::toString( + count); + } + if (ImGui::TreeNode(name.c_str())) { + check_depth.push_back(max_depth); + max_depth++; + ImGui::Text( + "%s", + (name + "##caption").c_str()); + position_editor(node); + ImGui::Separator(); + orientation_editor(node); + ImGui::Separator(); + if (ImGui::Button("From Cursor")) + std::cout << name + << " From Cursor" + << std::endl; + if (ImGui::Button("To Cursor")) + std::cout << name + << " To Cursor" + << std::endl; + ImGui::Separator(); + ImGui::Text("Attachments"); + attachments_editor(node); + } + } else if (!node && max_depth >= depth) { + max_depth = check_depth.back(); + check_depth.pop_back(); + ImGui::TreePop(); + } + if (tree_list[count]) + depth++; + else + depth--; + } + ImGui::Spacing(); + ImGui::End(); #if 0 if (ECS::get().get().narrationBox) { ImVec2 size = ImGui::GetMainViewport()->Size; @@ -990,6 +1471,20 @@ struct EditorGUIListener : public Ogre::RenderTargetListener { ImGui::End(); } #endif + } + void preview(const Ogre::RenderTargetViewportEvent &evt) + { + int i; + Ogre::ImGuiOverlay::NewFrame(); + if (ECS::get().get().enabled) { + buttons_panel(); +#if 0 + buildings_editor(); + map_editor(); +#endif + panel(); + if (enableMapEditor) + worldMapView(); } } }; diff --git a/src/gamedata/PhysicsModule.cpp b/src/gamedata/PhysicsModule.cpp index f9aa9ca..092a8cb 100644 --- a/src/gamedata/PhysicsModule.cpp +++ b/src/gamedata/PhysicsModule.cpp @@ -145,7 +145,6 @@ PhysicsModule::PhysicsModule(flecs::world &ecs) PhysicsShape &s = e.ensure(); s.shape = shape; e.modified(); - delete hfd.samples; e.remove(); }); #if 1 @@ -341,6 +340,16 @@ PhysicsModule::PhysicsModule(flecs::world &ecs) .each([this](const EngineData &eng) { ECS::get().set({}); }); +#if 0 + ecs.system("DebugData") + .kind(PhysicsPostUpdate) + .each([this](const EngineData &eng) { + std::cout << "TerrainReady: " + << ECS::get().has(); + std::cout << " WaterReady: " + << ECS::get().has() << std::endl; + }); +#endif ecs.system("update_water") .kind(PhysicsPostUpdate) .with() diff --git a/src/gamedata/SunModule.cpp b/src/gamedata/SunModule.cpp index a9f656a..326532b 100644 --- a/src/gamedata/SunModule.cpp +++ b/src/gamedata/SunModule.cpp @@ -1,3 +1,4 @@ +#include #include #include "Components.h" #include "SunModule.h" diff --git a/src/gamedata/TerrainModule.cpp b/src/gamedata/TerrainModule.cpp index 4eada02..7a635dc 100644 --- a/src/gamedata/TerrainModule.cpp +++ b/src/gamedata/TerrainModule.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include "GameData.h" #include "Components.h" @@ -71,11 +72,50 @@ struct HeightData { } return img_brushes.getColourAt(x, y + m, 0).r; } - float get_base_height(const Ogre::Vector2 &worldOffset, int x, int y) + float get_world_x(int x) + { + return (float)(x - (int)img.getWidth() / 2) * BRUSH_SIZE; + } + float get_world_y(int y) + { + return (float)(y - (int)img.getHeight() / 2) * BRUSH_SIZE; + } + int get_img_x(float world_x) + { + float world_img_x = world_x + img.getWidth() * BRUSH_SIZE / 2; + int ret = world_img_x / BRUSH_SIZE; + // ret = Ogre::Math::Clamp(ret, 0, (int)img.getWidth() - 1); + return ret; + } + int get_img_y(float world_z) + { + float world_img_y = world_z + img.getHeight() * BRUSH_SIZE / 2; + int ret = world_img_y / BRUSH_SIZE; + // ret = Ogre::Math::Clamp(ret, 0, (int)img.getHeight() - 1); + return ret; + } + void update_heightmap(const Ogre::Image &heightmap) + { + img = heightmap; + } + void save_heightmap() + { + Ogre::String group = + Ogre::ResourceGroupManager::getSingleton() + .findGroupContainingResource("world_map.png"); + Ogre::FileInfoListPtr fileInfoList( + Ogre::ResourceGroupManager::getSingleton() + .findResourceFileInfo(group, "world_map.png")); + OgreAssert(fileInfoList->size() == 1, + "worpd_map.png should be there and only once"); + Ogre::String path = fileInfoList->at(0).archive->getName() + + "/" + "world_map.png"; + Ogre::FileSystemLayer::removeFile(path); + img.save(path); + } + float get_base_height(long world_x, long world_y) { float height = 0.0f; - int world_x = worldOffset.x + x; - int world_y = worldOffset.y + y; int world_img_x = world_x + (int)img.getWidth() * BRUSH_SIZE / 2; int world_img_y = @@ -91,7 +131,7 @@ struct HeightData { world_img_x >= img.getWidth() * BRUSH_SIZE || world_img_y < 0 || world_img_y >= img.getWidth() * BRUSH_SIZE) { - height = -1.0f; + height = 0.0f; goto out; } color = img.getColourAt(map_img_x, map_img_y, 0); @@ -103,7 +143,7 @@ struct HeightData { out: return height; } - float get_noise_height(const Ogre::Vector2 &worldOffset, int x, int y) + float get_noise_height(long world_x, long world_y) { int h; Ogre::Vector2 noisePoint; @@ -122,7 +162,7 @@ out: for (h = 0; h < (int)sizeof(noise_values) / (int)sizeof(noise_values[0]); h++) { - noisePoint = (worldOffset + Ogre::Vector2(x, y) + + noisePoint = (Ogre::Vector2(world_x, world_y) + noise_pass[h].noiseOfft) * noise_pass[h].noiseMul; int noise_x = @@ -139,31 +179,44 @@ out: } return noise_values[0] + noise_values[1]; } - float get_height(Ogre::TerrainGroup *terrainGroup, long x, long y, - int j, int i) + float get_height(Ogre::TerrainGroup *terrainGroup, long world_x, + long world_y) { - uint16_t terrainSize = terrainGroup->getTerrainSize(); - Ogre::Vector2 worldOffset(Ogre::Real(x * (terrainSize - 1)), - Ogre::Real(y * (terrainSize - 1))); - float brush_height0 = - HeightData::get_singleton()->get_brush_height( - 0, j % BRUSH_SIZE, i % BRUSH_SIZE) - - 0.55f; - float brush_height1 = - HeightData::get_singleton()->get_brush_height( - 1, j % BRUSH_SIZE, i % BRUSH_SIZE) - - 0.55f; + long grid_center_x = img.getWidth() * BRUSH_SIZE / 2; + long grid_center_y = img.getHeight() * BRUSH_SIZE / 2; + long world_grid_x = world_x + grid_center_x; + long world_grid_y = world_y + grid_center_y; + float amplitude = 150.0f; + if (world_grid_x < 0 || world_grid_y < 0) { + std::cout << "world: " << world_x << " " << world_y + << " "; + std::cout << "grid: " << world_grid_x << " "; + std::cout << world_grid_y << std::endl; + return -amplitude; + } + OgreAssert(world_grid_x >= 0, "bad world x"); + OgreAssert(world_grid_y >= 0, "bad world y"); + long cell = 8; + long b1 = (world_grid_x / cell) * cell + cell / 2; + long b2 = (world_grid_y / cell) * cell + cell / 2; + long o1 = Ogre::Math::Abs(world_grid_x - b1); + long o2 = Ogre::Math::Abs(world_grid_y - b2); + // std::cout << "h: " << o1 << " " << o2 << std::endl; + // std::cout << "h: " << (o1 * o1 + o2 * o2) << std::endl; + float hbase = 1.0 / (1.0f + 0.02f * (o1 * o1 + o2 * o2)); + float h1 = hbase * 0.5f + 0.5f; + float h2 = (1.0f - hbase) * 0.5f; float mheight = - Ogre::Math::lerp( - brush_height1, brush_height0, - HeightData::get_singleton()->get_base_height( - worldOffset, j, i)) * - 120.0f; + (Ogre::Math::lerp(h2, h1, + get_base_height(world_x, world_y)) - + 0.51f) * + amplitude; float height = mheight; if (mheight > 0.5f) - height += 2.0f + get_noise_height(worldOffset, j, i); + height += 2.0f + get_noise_height(world_x, world_y); else if (mheight < -0.5f) - height -= 2.0f + get_noise_height(worldOffset, j, i); + height -= 2.0f + get_noise_height(world_x, world_y); + // OgreAssert(false, "height"); return height; } }; @@ -231,11 +284,29 @@ public: // Ogre::Vector2(img.getWidth(), img.getHeight()) * 0.5f; float chunk = 128.0f; Ogre::Vector2 revisedValuePoint; + Ogre::Vector3 worldPos; + terrainGroup->convertTerrainSlotToWorldPosition(x, y, + &worldPos); for (int i = 0; i < terrainSize; i++) for (int j = 0; j < terrainSize; j++) { - float height = - HeightData::get_singleton()->get_height( - terrainGroup, x, y, j, i); + long world_x = (long)(worldPos.x + j - + (terrainSize - 1) / 2); + long world_y = (long)(worldPos.z + i - + (terrainSize - 1) / 2); + float height = 0.0f; + int k, l; + for (l = -1; l < 2; l++) + for (k = -1; k < 2; k++) { + height += + HeightData::get_singleton() + ->get_height( + terrainGroup, + world_x + + 4 * k, + world_y + + 4 * l); + } + height /= 9.0f; // height = -2.0f; heightMap[i * terrainSize + j] = height; @@ -526,16 +597,16 @@ TerrainModule::TerrainModule(flecs::world &ecs) priv.mSunUpdate.reset(); } }); - ecs.system("UpdateTerrainStatus") + ecs.system("UpdateTerrainStatus") .kind(flecs::OnUpdate) .without() - .each([](const CharacterBase &ch, const Terrain &terrain) { + .each([](const ECS::Camera &cam, const Terrain &terrain) { std::cout << "mTerrainReady: " << terrain.mTerrainReady << "\n"; - std::cout << "mBodyNode: " << ch.mBodyNode << "\n"; - if (ch.mBodyNode && terrain.mTerrainReady) { + if (cam.mCameraNode && terrain.mTerrainReady) { long x, y; - Ogre::Vector3 pos = ch.mBodyNode->getPosition(); + Ogre::Vector3 pos = + cam.mCameraNode->getPosition(); terrain.mTerrainGroup ->convertWorldPositionToTerrainSlot( pos, &x, &y); @@ -560,8 +631,7 @@ TerrainModule::TerrainModule(flecs::world &ecs) item.position = Ogre::Vector3(0, 0, 0); float height = HeightData::get_singleton()->get_height( - terrain.mTerrainGroup, 0, 0, 0, - 0); + terrain.mTerrainGroup, 0, 0); item.position.y = height; placement.altar_items.push_back(item); for (i = -64000; i < 64000; i += 2000) @@ -586,14 +656,10 @@ TerrainModule::TerrainModule(flecs::world &ecs) HeightData::get_singleton() ->get_height( terrain.mTerrainGroup, - xslot, - yslot, - (int)offset.x + - (terrainSize - - 1) / 2, - (int)offset.z + - (terrainSize - - 1) / 2); + (long)position + .x, + (long)position + .z); #if 0 height = terrain.mTerrainGroup @@ -672,8 +738,31 @@ float TerrainModule::get_height(Ogre::TerrainGroup *group, group->convertTerrainSlotToWorldPosition(xslot, yslot, &slotpos); Ogre::Vector3 offset = (position - slotpos) * terrainSize / worldSize; float height = HeightData::get_singleton()->get_height( - group, xslot, yslot, (int)offset.x + (terrainSize - 1) / 2, - (int)offset.z + (terrainSize - 1) / 2); + group, (long)position.x, (long)position.z); return height; } +float TerrainModule::get_world_x(int x) +{ + return HeightData::get_singleton()->get_world_x(x); +} +float TerrainModule::get_world_y(int y) +{ + return HeightData::get_singleton()->get_world_y(y); +} +int TerrainModule::get_img_x(float world_x) +{ + return HeightData::get_singleton()->get_img_x(world_x); +} +int TerrainModule::get_img_y(float world_z) +{ + return HeightData::get_singleton()->get_img_y(world_z); +} +void TerrainModule::update_heightmap(const Ogre::Image &heightmap) +{ + return HeightData::get_singleton()->update_heightmap(heightmap); +} +void TerrainModule::save_heightmap() +{ + HeightData::get_singleton()->save_heightmap(); +} } \ No newline at end of file diff --git a/src/gamedata/TerrainModule.h b/src/gamedata/TerrainModule.h index 8019248..355050b 100644 --- a/src/gamedata/TerrainModule.h +++ b/src/gamedata/TerrainModule.h @@ -35,6 +35,12 @@ struct TerrainModule { TerrainModule(flecs::world &ecs); static float get_height(Ogre::TerrainGroup *group, const Ogre::Vector3 &position); + static float get_world_x(int x); + static float get_world_y(int y); + static int get_img_x(float world_x); + static int get_img_y(float world_z); + static void update_heightmap(const Ogre::Image &heightmap); + static void save_heightmap(); }; struct TerrainReady {}; } diff --git a/src/gamedata/WaterModule.cpp b/src/gamedata/WaterModule.cpp index dfba5a7..d0eb3bf 100644 --- a/src/gamedata/WaterModule.cpp +++ b/src/gamedata/WaterModule.cpp @@ -56,6 +56,7 @@ public: }; #endif static const uint32_t WATER_MASK = 0xF00; +static bool debugEnabled = false; WaterModule::WaterModule(flecs::world &ecs) { ecs.module(); @@ -163,15 +164,18 @@ WaterModule::WaterModule(flecs::world &ecs) Ogre::FT_MIP, Ogre::FO_LINEAR); } // create a frosted screen in front of the camera, using our dynamic texture to "thaw" certain areas - Ogre::Entity *ent = eng.mScnMgr->createEntity( - "WaterDebugPlane", - Ogre::SceneManager::PT_PLANE); - ent->setMaterialName("Water/Debug", "Water"); - ent->setVisibilityFlags(WATER_MASK); - Ogre::SceneNode *node = - camera.mCameraNode->createChildSceneNode(); - node->setPosition(-150, 60, -400); - node->attachObject(ent); + if (debugEnabled) { + Ogre::Entity *ent = eng.mScnMgr->createEntity( + "WaterDebugPlane", + Ogre::SceneManager::PT_PLANE); + ent->setMaterialName("Water/Debug", "Water"); + ent->setVisibilityFlags(WATER_MASK); + Ogre::SceneNode *node = + camera.mCameraNode + ->createChildSceneNode(); + node->setPosition(-150, 60, -400); + node->attachObject(ent); + } water.mReflectionTexture = reflectionTexture->getBuffer()