Heightmap editor + saving

This commit is contained in:
2025-12-07 01:31:25 +03:00
parent e0db570581
commit 44896ed0d9
13 changed files with 1901 additions and 172 deletions

56
src/editor/CMakeLists.txt Normal file
View File

@@ -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)

View File

@@ -0,0 +1,76 @@
#include <OgreMaterialManager.h>
#include <OgreMaterial.h>
#include <OgreTechnique.h>
#include <OgrePass.h>
#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<EditorGizmoModule>();
ecs.component<EditorDebugMaterial>().add(flecs::Singleton);
ecs.component<EditorGizmo>().add(flecs::Singleton);
ecs.observer<EditorDebugMaterial>("SetupDebugMaterial")
.event(flecs::OnAdd)
.each([](EditorDebugMaterial &mdbg) {
mdbg.material = createVertexColorNoDepthMaterial(
"Debug/vcolor");
});
ecs.observer<const EditorDebugMaterial, EditorGizmo>("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();
});
}
}

View File

@@ -0,0 +1,18 @@
#ifndef _EDITOR_GIZMO_MODULE_H_
#define _EDITOR_GIZMO_MODULE_H_
#include <flecs.h>
#include <Ogre.h>
namespace ECS
{
struct EditorDebugMaterial {
Ogre::MaterialPtr material;
};
struct EditorGizmo {
std::shared_ptr<Ogre::ManualObject> gizmo;
Ogre::SceneNode *sceneNode;
};
struct EditorGizmoModule {
EditorGizmoModule(flecs::world &ecs);
};
}
#endif

View File

@@ -0,0 +1,172 @@
#include <iostream>
#include <Ogre.h>
#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<EditorInputModule>();
ecs.system<const EngineData, Input, Camera>("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<ECS::Input>();
});
}
}

View File

@@ -0,0 +1,10 @@
#ifndef _EDITOR_INPUT_MODULE_H_
#define _EDITOR_INPUT_MODULE_H_
#include <flecs.h>
namespace ECS
{
struct EditorInputModule {
EditorInputModule(flecs::world &ecs);
};
}
#endif

791
src/editor/main.cpp Normal file
View File

@@ -0,0 +1,791 @@
#include <flecs.h>
#include <iostream>
#include <Ogre.h>
#include <OgreApplicationContext.h>
#include <OgreOverlaySystem.h>
#include <OgreOverlayManager.h>
#include <OgreImGuiOverlay.h>
#include <OgreImGuiInputListener.h>
#include <OgreTrays.h>
#include <OgreTimer.h>
#include <OgreMeshLodGenerator.h>
// #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<Ogre::ManualObject> 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<Ogre::ManualObject>("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<ECS::GUI>())
return false;
return (ECS::get().get<ECS::GUI>().enabled);
}
void setGuiEnabled(bool value)
{
if (!ECS::get().has<ECS::GUI>())
return;
ECS::get().get_mut<ECS::GUI>().enabled = value;
ECS::get().modified<ECS::GUI>();
}
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<ECS::GUI>(), "");
setGuiEnabled(true);
if (ECS::get().has<ECS::GUI>())
ECS::get<ECS::GUI>().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<ECS::GUI>())
return;
/* Update window grab */
ECS::GUI &gui = ECS::get().get_mut<ECS::GUI>();
if (gui.grabChanged) {
gui.grab = false;
setWindowGrab(gui.grab);
gui.grabChanged = false;
ECS::get().modified<ECS::GUI>();
}
ECS::update(delta);
}
class InputListenerChainFlexible : public OgreBites::InputListener {
protected:
std::vector<OgreBites::InputListener *> mListenerChain;
public:
InputListenerChainFlexible()
{
}
InputListenerChainFlexible(std::vector<InputListener *> 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::EditorGizmoModule>();
ECS::get().import <ECS::EditorInputModule>();
ECS::get().set<ECS::RenderWindow>(
{ getRenderWindow(), getDisplayDPI() });
ECS::get()
.observer<ECS::GUI>("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<ECS::App>("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<ECS::App>(
{ initialiseImGui(),
nullptr,
{ getImGuiInputListener(), &mKbd } });
ECS::get().add<ECS::EditorDebugMaterial>();
std::shared_ptr<Ogre::ManualObject> manualObj(
mScnMgr->createManualObject("EditorGizmo"));
Ogre::SceneNode *gizmoNode =
mScnMgr->getRootSceneNode()->createChildSceneNode(
"EditorGizmoNode");
gizmoNode->attachObject(manualObj.get());
ECS::get().set<ECS::EditorGizmo>({ manualObj, gizmoNode });
#if 0
ECS::get()
.system<const ECS::EngineData, ECS::Camera,
const ECS::Input>("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<ECS::GUI>().enabled;
}
void set_gui_active(bool active)
{
ECS::get().get_mut<ECS::GUI>().enabled = active;
ECS::get().modified<ECS::GUI>();
}
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<ECS::EngineData>().enableDbgDraw = enable;
ECS::modified<ECS::EngineData>();
}
bool isEnabledDbgDraw() const
{
return ECS::get<ECS::EngineData>().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>()) {
ECS::Input &input = ECS::get().get_mut<ECS::Input>();
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<ECS::Input>();
} else {
ECS::Input &input = ECS::get().get_mut<ECS::Input>();
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<ECS::Input>();
}
}
int main()
{
App ctx;
ctx.configure();
ctx.enableDbgDraw(false);
ctx.getRoot()->startRendering();
ctx.setWindowGrab(false);
ctx.closeApp();
return 0;
}