From 5674e0da24de98cc4d781d6e74bfd17f6603dd72 Mon Sep 17 00:00:00 2001 From: Aesylwinn Date: Thu, 4 Aug 2016 22:58:55 -0400 Subject: [PATCH] Render water in editor. --- apps/opencs/CMakeLists.txt | 1 + apps/opencs/view/render/cell.cpp | 2 + apps/opencs/view/render/cell.hpp | 2 + apps/opencs/view/render/cellmarker.cpp | 1 + apps/opencs/view/render/cellwater.cpp | 191 +++++++++++++++++++++++++ apps/opencs/view/render/cellwater.hpp | 69 +++++++++ 6 files changed, 266 insertions(+) create mode 100644 apps/opencs/view/render/cellwater.cpp create mode 100644 apps/opencs/view/render/cellwater.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 6e19c03b2..8a830f2cb 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -87,6 +87,7 @@ opencs_units (view/render scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget previewwidget editmode instancemode instanceselectionmode instancemovemode orbitcameramode pathgridmode selectionmode pathgridselectionmode cameracontroller + cellwater ) opencs_units_noqt (view/render diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp index dc22fd511..395fbf95f 100644 --- a/apps/opencs/view/render/cell.cpp +++ b/apps/opencs/view/render/cell.cpp @@ -16,6 +16,7 @@ #include "../../model/world/refcollection.hpp" #include "../../model/world/cellcoordinates.hpp" +#include "cellwater.hpp" #include "mask.hpp" #include "pathgrid.hpp" #include "terrainstorage.hpp" @@ -111,6 +112,7 @@ CSVRender::Cell::Cell (CSMWorld::Data& data, osg::Group* rootNode, const std::st } mPathgrid.reset(new Pathgrid(mData, mCellNode, mId, mCoordinates)); + mCellWater.reset(new CellWater(mData, mCellNode, mId, mCoordinates)); } } diff --git a/apps/opencs/view/render/cell.hpp b/apps/opencs/view/render/cell.hpp index a5b581d24..8f68e9f53 100644 --- a/apps/opencs/view/render/cell.hpp +++ b/apps/opencs/view/render/cell.hpp @@ -35,6 +35,7 @@ namespace CSMWorld namespace CSVRender { + class CellWater; class Pathgrid; class TagBase; @@ -49,6 +50,7 @@ namespace CSVRender std::auto_ptr mCellArrows[4]; std::auto_ptr mCellMarker; std::auto_ptr mCellBorder; + std::auto_ptr mCellWater; std::auto_ptr mPathgrid; bool mDeleted; int mSubMode; diff --git a/apps/opencs/view/render/cellmarker.cpp b/apps/opencs/view/render/cellmarker.cpp index e0d270f85..09690190d 100644 --- a/apps/opencs/view/render/cellmarker.cpp +++ b/apps/opencs/view/render/cellmarker.cpp @@ -75,6 +75,7 @@ CSVRender::CellMarker::CellMarker( mMarkerNode->setAutoRotateMode(osg::AutoTransform::ROTATE_TO_SCREEN); mMarkerNode->setAutoScaleToScreen(true); mMarkerNode->getOrCreateStateSet()->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF); + mMarkerNode->getOrCreateStateSet()->setRenderBinDetails(2000, "RenderBin"); mMarkerNode->setUserData(new CellMarkerTag(this)); mMarkerNode->setNodeMask(Mask_CellMarker); diff --git a/apps/opencs/view/render/cellwater.cpp b/apps/opencs/view/render/cellwater.cpp new file mode 100644 index 000000000..956864d7b --- /dev/null +++ b/apps/opencs/view/render/cellwater.cpp @@ -0,0 +1,191 @@ +#include "cellwater.hpp" + +#include +#include +#include +#include +#include + +#include +#include + +#include "../../model/world/cell.hpp" +#include "../../model/world/cellcoordinates.hpp" +#include "../../model/world/data.hpp" + +#include "mask.hpp" + +namespace CSVRender +{ + const int CellWater::CellSize = ESM::Land::REAL_SIZE; + + CellWater::CellWater(CSMWorld::Data& data, osg::Group* cellNode, const std::string& id, + const CSMWorld::CellCoordinates& cellCoords) + : mData(data) + , mId(id) + , mParentNode(cellNode) + , mWaterTransform(0) + , mWaterNode(0) + , mWaterGeometry(0) + , mExterior(false) + , mHasWater(false) + , mWaterHeight(0) + { + mWaterTransform = new osg::PositionAttitudeTransform(); + mWaterTransform->setPosition(osg::Vec3f(cellCoords.getX() * CellSize, cellCoords.getY() * CellSize, 0)); + mWaterTransform->setNodeMask(Mask_Water); + mParentNode->addChild(mWaterTransform); + + mWaterNode = new osg::Geode(); + mWaterTransform->addChild(mWaterNode); + + int cellIndex = mData.getCells().searchId(mId); + if (cellIndex > -1) + { + updateCellData(mData.getCells().getRecord(cellIndex).get()); + } + + // Keep water existance/height up to date + QAbstractItemModel* cells = mData.getTableModel(CSMWorld::UniversalId::Type_Cells); + connect(cells, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), + this, SLOT(cellDataChanged(const QModelIndex&, const QModelIndex&))); + } + + CellWater::~CellWater() + { + mParentNode->removeChild(mWaterTransform); + } + + void CellWater::updateCellData(const CSMWorld::Cell& cell) + { + int cellIndex = mData.getCells().searchId(mId); + if (cellIndex > -1) + { + const CSMWorld::Record& cellRecord = mData.getCells().getRecord(cellIndex); + + mDeleted = cellRecord.isDeleted(); + if (!mDeleted) + { + mExterior = cellRecord.get().isExterior(); + + mHasWater = cellRecord.get().hasWater(); + mWaterHeight = cellRecord.get().mWater; + } + } + else + { + mDeleted = true; + } + + update(); + } + + void CellWater::cellDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight) + { + const CSMWorld::Collection& cells = mData.getCells(); + + int rowStart = -1; + int rowEnd = -1; + + if (topLeft.parent().isValid()) + { + rowStart = topLeft.parent().row(); + rowEnd = bottomRight.parent().row(); + } + else + { + rowStart = topLeft.row(); + rowEnd = bottomRight.row(); + } + + for (int row = rowStart; row <= rowEnd; ++row) + { + const CSMWorld::Cell& cell = cells.getRecord(row).get(); + + if (Misc::StringUtils::lowerCase(cell.mId) == mId) + updateCellData(cell); + } + } + + void CellWater::update() + { + const int InteriorSize = CellSize * 10; + + const size_t NumPoints = 4; + const size_t NumIndices = 6; + + const osg::Vec3f ExteriorPoints[] = + { + osg::Vec3f(0, 0, mWaterHeight), + osg::Vec3f(0, CellSize, mWaterHeight), + osg::Vec3f(CellSize, CellSize, mWaterHeight), + osg::Vec3f(CellSize, 0, mWaterHeight) + }; + + const osg::Vec3f InteriorPoints[] = + { + osg::Vec3f(-InteriorSize, -InteriorSize, mWaterHeight), + osg::Vec3f(-InteriorSize, InteriorSize, mWaterHeight), + osg::Vec3f( InteriorSize, InteriorSize, mWaterHeight), + osg::Vec3f( InteriorSize, -InteriorSize, mWaterHeight) + }; + + const unsigned short TriangleStrip[] = + { + 0, 1, 2, 3, 0, 1 + }; + + const osg::Vec4f Color = osg::Vec4f(0.6f, 0.7f, 1.f, 0.5f); + + if (mWaterGeometry) + { + mWaterNode->removeDrawable(mWaterGeometry); + mWaterGeometry = 0; + } + + if (mDeleted || !mHasWater) + return; + + mWaterGeometry = new osg::Geometry(); + + osg::ref_ptr vertices = new osg::Vec3Array(); + osg::ref_ptr colors = new osg::Vec4Array(); + osg::ref_ptr indices = new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLE_STRIP, + NumIndices); + + for (size_t i = 0; i < NumPoints; ++i) + { + if (mExterior) + vertices->push_back(ExteriorPoints[i]); + else + vertices->push_back(InteriorPoints[i]); + } + + colors->push_back(Color); + + for (size_t i = 0; i < NumIndices; ++i) + { + indices->setElement(i, TriangleStrip[i]); + } + + mWaterGeometry->setVertexArray(vertices); + mWaterGeometry->setColorArray(colors, osg::Array::BIND_OVERALL); + mWaterGeometry->addPrimitiveSet(indices); + + // Transparency + mWaterGeometry->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); + mWaterGeometry->getOrCreateStateSet()->setMode(GL_BLEND, osg::StateAttribute::ON ); + mWaterGeometry->getOrCreateStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); + mWaterGeometry->getOrCreateStateSet()->setRenderBinDetails(1000, "RenderBin"); + + // Reduce some z-fighting + osg::ref_ptr polygonOffset = new osg::PolygonOffset(); + polygonOffset->setFactor(0.2f); + polygonOffset->setUnits(0.2f); + + mWaterGeometry->getOrCreateStateSet()->setAttributeAndModes(polygonOffset, + osg::StateAttribute::OVERRIDE | osg::StateAttribute::ON); + + mWaterNode->addDrawable(mWaterGeometry); + } +} diff --git a/apps/opencs/view/render/cellwater.hpp b/apps/opencs/view/render/cellwater.hpp new file mode 100644 index 000000000..6d533e291 --- /dev/null +++ b/apps/opencs/view/render/cellwater.hpp @@ -0,0 +1,69 @@ +#ifndef CSV_RENDER_CELLWATER_H +#define CSV_RENDER_CELLWATER_H + +#include + +#include + +#include + +namespace osg +{ + class Geode; + class Geometry; + class Group; + class PositionAttitudeTransform; +} + +namespace CSMWorld +{ + class Cell; + class CellCoordinates; + class Data; +} + +namespace CSVRender +{ + /// For exterior cells, this adds a patch of water to fit the size of the cell. For interior cells with water, this + /// adds a large patch of water much larger than the typical size of a cell. + class CellWater : public QObject + { + Q_OBJECT + + public: + + CellWater(CSMWorld::Data& data, osg::Group* cellNode, const std::string& id, + const CSMWorld::CellCoordinates& cellCoords); + + ~CellWater(); + + void updateCellData(const CSMWorld::Cell& cell); + + private slots: + + void cellDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight); + + private: + + void update(); + + static const int CellSize; + + CSMWorld::Data& mData; + std::string mId; + + osg::Group* mParentNode; + + osg::ref_ptr mWaterTransform; + osg::ref_ptr mWaterNode; + osg::ref_ptr mWaterGeometry; + + bool mDeleted; + bool mExterior; + + bool mHasWater; + float mWaterHeight; + }; +} + +#endif