From 24c8b32d4cefa0bf65bb51e98a58c643058139a1 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari <unelsson@gmail.com> Date: Fri, 7 Feb 2020 19:04:28 +0200 Subject: [PATCH] Implement brush outline for terrainshapemode --- apps/opencs/CMakeLists.txt | 2 +- apps/opencs/view/render/brushdraw.cpp | 136 ++++++++++++++++++ apps/opencs/view/render/brushdraw.hpp | 31 ++++ apps/opencs/view/render/editmode.cpp | 2 + apps/opencs/view/render/editmode.hpp | 2 + apps/opencs/view/render/terrainshapemode.cpp | 17 +++ apps/opencs/view/render/terrainshapemode.hpp | 4 + .../opencs/view/render/terraintexturemode.cpp | 2 + .../opencs/view/render/terraintexturemode.hpp | 2 + apps/opencs/view/render/worldspacewidget.cpp | 2 + 10 files changed, 199 insertions(+), 1 deletion(-) create mode 100644 apps/opencs/view/render/brushdraw.cpp create mode 100644 apps/opencs/view/render/brushdraw.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index af8bf8d559..6d0f2ad9f9 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -89,7 +89,7 @@ opencs_units (view/render scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget previewwidget editmode instancemode instanceselectionmode instancemovemode orbitcameramode pathgridmode selectionmode pathgridselectionmode cameracontroller - cellwater terraintexturemode actor terrainselection terrainshapemode + cellwater terraintexturemode actor terrainselection terrainshapemode brushdraw ) opencs_units_noqt (view/render diff --git a/apps/opencs/view/render/brushdraw.cpp b/apps/opencs/view/render/brushdraw.cpp new file mode 100644 index 0000000000..a2874bd31e --- /dev/null +++ b/apps/opencs/view/render/brushdraw.cpp @@ -0,0 +1,136 @@ +#include "brushdraw.hpp" + +#include <osg/Group> +#include <osg/Geometry> +#include <osg/Array> +#include <osg/BlendFunc> +#include <osg/CullFace> +#include <osg/Material> + +#include <osgUtil/LineSegmentIntersector> + +#include "mask.hpp" + +CSVRender::BrushDraw::BrushDraw(osg::Group* parentNode) : + mParentNode(parentNode) +{ + mBrushDrawNode = new osg::Group(); + mGeometry = new osg::Geometry(); + mBrushDrawNode->addChild(mGeometry); + mParentNode->addChild(mBrushDrawNode); +} + +CSVRender::BrushDraw::~BrushDraw() +{ + if (mBrushDrawNode->containsNode(mGeometry)) mBrushDrawNode->removeChild(mGeometry); + if (mParentNode->containsNode(mBrushDrawNode)) mParentNode->removeChild(mBrushDrawNode); +} + +float CSVRender::BrushDraw::getIntersectionHeight (const osg::Vec3d& point) +{ + osg::Vec3d start = point; + osg::Vec3d end = point; + start.z() += 8000.0f; // these numbers need fixing + end.z() -= 8000.0f; + osg::Vec3d direction = end - start; + + // Get intersection + osg::ref_ptr<osgUtil::LineSegmentIntersector> intersector (new osgUtil::LineSegmentIntersector( + osgUtil::Intersector::MODEL, start, end) ); + intersector->setIntersectionLimit(osgUtil::LineSegmentIntersector::NO_LIMIT); + osgUtil::IntersectionVisitor visitor(intersector); + + visitor.setTraversalMask(Mask_Terrain); + + mParentNode->accept(visitor); + + for (osgUtil::LineSegmentIntersector::Intersections::iterator it = intersector->getIntersections().begin(); + it != intersector->getIntersections().end(); ++it) + { + osgUtil::LineSegmentIntersector::Intersection intersection = *it; + + // reject back-facing polygons + if (direction * intersection.getWorldIntersectNormal() > 0) + { + continue; + } + + return intersection.getWorldIntersectPoint().z(); + } + return 0.0f; +} + + +void CSVRender::BrushDraw::buildGeometry(const float& radius, const osg::Vec3d& point, int amountOfPoints) +{ + osg::ref_ptr<osg::Geometry> geom = new osg::Geometry(); + osg::ref_ptr<osg::Vec3Array> vertices (new osg::Vec3Array()); + osg::ref_ptr<osg::Vec4Array> colors (new osg::Vec4Array()); + const float step ((osg::PI * 2.0f) / static_cast<float>(amountOfPoints)); + + for (int i = 0; i < amountOfPoints + 2; i++) + { + float angle (static_cast<float>(i) * step); + vertices->push_back(osg::Vec3d( + point.x() + radius * cosf(angle), + point.y() + radius * sinf(angle), + getIntersectionHeight(osg::Vec3d( + point.x() + radius * cosf(angle), + point.y() + radius * sinf(angle), + point.z()) ))); + colors->push_back(osg::Vec4f( + 50.0f, + 50.0f, + 50.0f, + 100.0f)); + angle = static_cast<float>(i + 1) * step; + vertices->push_back(osg::Vec3d( + point.x() + radius * cosf(angle), + point.y() + radius * sinf(angle), + getIntersectionHeight(osg::Vec3d( + point.x() + radius * cosf(angle), + point.y() + radius * sinf(angle), + point.z()) ) + 200.0f)); + colors->push_back(osg::Vec4f( + 50.0f, + 50.0f, + 50.0f, + 100.0f)); + } + + geom->setVertexArray(vertices); + geom->setColorArray(colors, osg::Array::BIND_PER_VERTEX); + geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::TRIANGLE_STRIP, 0, (amountOfPoints + 2) * 2 - 2)); + mGeometry = geom; +} + +void CSVRender::BrushDraw::update(osg::Vec3d point, int brushSize) +{ + if (mBrushDrawNode->containsNode(mGeometry)) mBrushDrawNode->removeChild(mGeometry); + mBrushDrawNode->setNodeMask (Mask_EditModeCursor); + float radius = static_cast<float>(brushSize * mLandSizeFactor); + int amountOfPoints = (osg::PI * 2.0f) * radius / 20; + + buildGeometry(radius, point, amountOfPoints); + + osg::BlendFunc* blendFunc = new osg::BlendFunc(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE_MINUS_SRC_ALPHA); + mGeometry->getOrCreateStateSet()->setAttributeAndModes(blendFunc); + + mGeometry->getOrCreateStateSet()->setMode( GL_CULL_FACE, osg::StateAttribute::OFF ); + + osg::ref_ptr<osg::Material> material = new osg::Material; + material->setColorMode(osg::Material::AMBIENT); + material->setAmbient (osg::Material::FRONT_AND_BACK, osg::Vec4(0.3, 0.3, 0.3, 1)); + material->setAlpha(osg::Material::FRONT_AND_BACK, 0.5); + + mGeometry->getOrCreateStateSet()->setAttributeAndModes(material.get(), osg::StateAttribute::ON); + mGeometry->getOrCreateStateSet()->setMode(GL_BLEND, osg::StateAttribute::ON); + mGeometry->getOrCreateStateSet()->setRenderingHint (osg::StateSet::TRANSPARENT_BIN); + + mBrushDrawNode->addChild(mGeometry); +} + +void CSVRender::BrushDraw::hide() +{ + if (mBrushDrawNode->containsNode(mGeometry)) mBrushDrawNode->removeChild(mGeometry); +} diff --git a/apps/opencs/view/render/brushdraw.hpp b/apps/opencs/view/render/brushdraw.hpp new file mode 100644 index 0000000000..5e0a095cef --- /dev/null +++ b/apps/opencs/view/render/brushdraw.hpp @@ -0,0 +1,31 @@ +#ifndef CSV_RENDER_BRUSHDRAW_H +#define CSV_RENDER_BRUSHDRAW_H + +#include <osg/Group> +#include <osg/Geometry> + +#include <components/esm/loadland.hpp> + +namespace CSVRender +{ + class BrushDraw + { + public: + BrushDraw(osg::Group* parentNode); + ~BrushDraw(); + + void update(osg::Vec3d point, int brushSize); + void hide(); + + private: + void buildGeometry(const float& radius, const osg::Vec3d& point, int amountOfPoints); + float getIntersectionHeight (const osg::Vec3d& point); + + osg::Group* mParentNode; + osg::ref_ptr<osg::Group> mBrushDrawNode; + osg::ref_ptr<osg::Geometry> mGeometry; + float mLandSizeFactor = ESM::Land::REAL_SIZE / ESM::Land::LAND_SIZE / 2; + }; +} + +#endif diff --git a/apps/opencs/view/render/editmode.cpp b/apps/opencs/view/render/editmode.cpp index 03451bc1b1..ca4aa0fd55 100644 --- a/apps/opencs/view/render/editmode.cpp +++ b/apps/opencs/view/render/editmode.cpp @@ -73,6 +73,8 @@ void CSVRender::EditMode::dropEvent (QDropEvent *event) {} void CSVRender::EditMode::dragMoveEvent (QDragMoveEvent *event) {} +void CSVRender::EditMode::mouseMoveEvent (QMouseEvent *event) {} + int CSVRender::EditMode::getSubMode() const { return -1; diff --git a/apps/opencs/view/render/editmode.hpp b/apps/opencs/view/render/editmode.hpp index 9f3b289578..9115943273 100644 --- a/apps/opencs/view/render/editmode.hpp +++ b/apps/opencs/view/render/editmode.hpp @@ -98,6 +98,8 @@ namespace CSVRender /// Default-implementation: ignored virtual void dragMoveEvent (QDragMoveEvent *event); + virtual void mouseMoveEvent (QMouseEvent *event); + /// Default: return -1 virtual int getSubMode() const; }; diff --git a/apps/opencs/view/render/terrainshapemode.cpp b/apps/opencs/view/render/terrainshapemode.cpp index 2a6d7f33f4..53fe92f8e2 100644 --- a/apps/opencs/view/render/terrainshapemode.cpp +++ b/apps/opencs/view/render/terrainshapemode.cpp @@ -37,6 +37,7 @@ #include "../../model/world/tablemimedata.hpp" #include "../../model/world/universalid.hpp" +#include "brushdraw.hpp" #include "editmode.hpp" #include "pagedworldspacewidget.hpp" #include "tagbase.hpp" @@ -49,6 +50,12 @@ CSVRender::TerrainShapeMode::TerrainShapeMode (WorldspaceWidget *worldspaceWidge { } +CSVRender::TerrainShapeMode::~TerrainShapeMode () +{ + if (mBrushDraw) + mBrushDraw.reset(); +} + void CSVRender::TerrainShapeMode::activate(CSVWidget::SceneToolbar* toolbar) { if (!mTerrainShapeSelection) @@ -67,6 +74,9 @@ void CSVRender::TerrainShapeMode::activate(CSVWidget::SceneToolbar* toolbar) connect(mShapeBrushScenetool->mShapeBrushWindow->mToolStrengthSlider, SIGNAL(valueChanged(int)), this, SLOT(setShapeEditToolStrength(int))); } + if (!mBrushDraw) + mBrushDraw.reset(new BrushDraw(mParentNode)); + EditMode::activate(toolbar); toolbar->addTool (mShapeBrushScenetool); } @@ -1382,6 +1392,13 @@ void CSVRender::TerrainShapeMode::dragMoveEvent (QDragMoveEvent *event) { } +void CSVRender::TerrainShapeMode::mouseMoveEvent (QMouseEvent *event) +{ + WorldspaceHitResult hit = getWorldspaceWidget().mousePick(event->pos(), getInteractionMask()); + if (hit.hit && mBrushDraw && !(mShapeEditTool == ShapeEditTool_Drag && mIsEditing)) mBrushDraw->update(hit.worldPos, mBrushSize); + if (!hit.hit && !(mShapeEditTool == ShapeEditTool_Drag && mIsEditing)) mBrushDraw->hide(); +} + void CSVRender::TerrainShapeMode::setBrushSize(int brushSize) { mBrushSize = brushSize; diff --git a/apps/opencs/view/render/terrainshapemode.hpp b/apps/opencs/view/render/terrainshapemode.hpp index 68f2fbf9da..fb80e79af4 100644 --- a/apps/opencs/view/render/terrainshapemode.hpp +++ b/apps/opencs/view/render/terrainshapemode.hpp @@ -19,6 +19,7 @@ #include "../widget/brushshapes.hpp" #endif +#include "brushdraw.hpp" #include "terrainselection.hpp" namespace CSVWidget @@ -57,6 +58,7 @@ namespace CSVRender /// Editmode for terrain shape grid TerrainShapeMode(WorldspaceWidget*, osg::Group* parentNode, QWidget* parent = nullptr); + ~TerrainShapeMode(); void primaryOpenPressed (const WorldspaceHitResult& hit) final; @@ -89,6 +91,7 @@ namespace CSVRender void dragWheel (int diff, double speedFactor) final; void dragMoveEvent (QDragMoveEvent *event) final; + void mouseMoveEvent (QMouseEvent *event) final; private: @@ -168,6 +171,7 @@ namespace CSVRender std::string mBrushTexture; int mBrushSize = 1; CSVWidget::BrushShape mBrushShape = CSVWidget::BrushShape_Point; + std::unique_ptr<BrushDraw> mBrushDraw; std::vector<std::pair<int, int>> mCustomBrushShape; CSVWidget::SceneToolShapeBrush *mShapeBrushScenetool = nullptr; int mDragMode = InteractionType_None; diff --git a/apps/opencs/view/render/terraintexturemode.cpp b/apps/opencs/view/render/terraintexturemode.cpp index efdb600b87..e25d65a892 100644 --- a/apps/opencs/view/render/terraintexturemode.cpp +++ b/apps/opencs/view/render/terraintexturemode.cpp @@ -707,6 +707,8 @@ void CSVRender::TerrainTextureMode::dragMoveEvent (QDragMoveEvent *event) { } +void CSVRender::TerrainTextureMode::mouseMoveEvent (QMouseEvent *event) {} + void CSVRender::TerrainTextureMode::setBrushSize(int brushSize) { mBrushSize = brushSize; diff --git a/apps/opencs/view/render/terraintexturemode.hpp b/apps/opencs/view/render/terraintexturemode.hpp index 4176abefea..3b94097ee9 100644 --- a/apps/opencs/view/render/terraintexturemode.hpp +++ b/apps/opencs/view/render/terraintexturemode.hpp @@ -81,6 +81,8 @@ namespace CSVRender void dragWheel (int diff, double speedFactor) final; void dragMoveEvent (QDragMoveEvent *event) final; + void mouseMoveEvent (QMouseEvent *event) final; + private: /// \brief Handle brush mechanics, maths regarding worldspace hit etc. void editTerrainTextureGrid (const WorldspaceHitResult& hit); diff --git a/apps/opencs/view/render/worldspacewidget.cpp b/apps/opencs/view/render/worldspacewidget.cpp index 6ab4b041b9..4755de97bf 100644 --- a/apps/opencs/view/render/worldspacewidget.cpp +++ b/apps/opencs/view/render/worldspacewidget.cpp @@ -613,6 +613,8 @@ void CSVRender::WorldspaceWidget::updateOverlay() void CSVRender::WorldspaceWidget::mouseMoveEvent (QMouseEvent *event) { + dynamic_cast<CSVRender::EditMode&> (*mEditMode->getCurrent()).mouseMoveEvent (event); + if (mDragging) { int diffX = event->x() - mDragX;