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..abc337ce2 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(osg::StateSet::TRANSPARENT_BIN + 1, "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..b8975da49 --- /dev/null +++ b/apps/opencs/view/render/cellwater.cpp @@ -0,0 +1,177 @@ +#include "cellwater.hpp" + +#include +#include +#include +#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) + , mDeleted(false) + , mExterior(false) + , mHasWater(false) + { + mWaterTransform = new osg::PositionAttitudeTransform(); + mWaterTransform->setPosition(osg::Vec3f(cellCoords.getX() * CellSize + CellSize / 2.f, + cellCoords.getY() * CellSize + CellSize / 2.f, 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)); + } + + // 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::Record& cellRecord) + { + mDeleted = cellRecord.isDeleted(); + if (!mDeleted) + { + const CSMWorld::Cell& cell = cellRecord.get(); + + if (mExterior != cell.isExterior() || mHasWater != cell.hasWater()) + { + mExterior = cellRecord.get().isExterior(); + mHasWater = cellRecord.get().hasWater(); + + recreate(); + } + + float waterHeight = -1; + if (!mExterior) + { + waterHeight = cellRecord.get().mWater; + } + + osg::Vec3d pos = mWaterTransform->getPosition(); + pos.z() = waterHeight; + mWaterTransform->setPosition(pos); + } + else + { + recreate(); + } + } + + 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::Record& cellRecord = cells.getRecord(row); + + if (Misc::StringUtils::lowerCase(cellRecord.get().mId) == mId) + updateCellData(cellRecord); + } + } + + void CellWater::recreate() + { + const int InteriorScalar = 20; + const int SegmentsPerCell = 1; + const int TextureRepeatsPerCell = 6; + + const float Alpha = 0.5f; + + const int RenderBin = osg::StateSet::TRANSPARENT_BIN - 1; + + if (mWaterGeometry) + { + mWaterNode->removeDrawable(mWaterGeometry); + mWaterGeometry = 0; + } + + if (mDeleted || !mHasWater) + return; + + float size; + int segments; + float textureRepeats; + + if (mExterior) + { + size = CellSize; + segments = SegmentsPerCell; + textureRepeats = TextureRepeatsPerCell; + } + else + { + size = CellSize * InteriorScalar; + segments = SegmentsPerCell * InteriorScalar; + textureRepeats = TextureRepeatsPerCell * InteriorScalar; + } + + mWaterGeometry = SceneUtil::createWaterGeometry(size, segments, textureRepeats); + mWaterGeometry->setStateSet(SceneUtil::createSimpleWaterStateSet(Alpha, RenderBin)); + + // Add water texture + std::string textureName = mData.getFallbackMap()->getFallbackString("Water_SurfaceTexture"); + textureName = "textures/water/" + textureName + "00.dds"; + + Resource::ImageManager* imageManager = mData.getResourceSystem()->getImageManager(); + + osg::ref_ptr waterTexture = new osg::Texture2D(); + waterTexture->setImage(imageManager->getImage(textureName)); + waterTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT); + waterTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT); + + mWaterGeometry->getStateSet()->setTextureAttributeAndModes(0, waterTexture, 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..d2ed9b458 --- /dev/null +++ b/apps/opencs/view/render/cellwater.hpp @@ -0,0 +1,70 @@ +#ifndef CSV_RENDER_CELLWATER_H +#define CSV_RENDER_CELLWATER_H + +#include + +#include + +#include +#include + +#include "../../model/world/record.hpp" + +namespace osg +{ + class Geode; + class Geometry; + class Group; + class PositionAttitudeTransform; +} + +namespace CSMWorld +{ + struct 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::Record& cellRecord); + + private slots: + + void cellDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight); + + private: + + void recreate(); + + 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; + }; +} + +#endif diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index ab828a3ad..532e5cedc 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -24,6 +24,8 @@ #include #include +#include + #include #include @@ -40,58 +42,6 @@ #include "renderbin.hpp" #include "util.hpp" -namespace -{ - - osg::ref_ptr createWaterGeometry(float size, int segments, float textureRepeats) - { - osg::ref_ptr verts (new osg::Vec3Array); - osg::ref_ptr texcoords (new osg::Vec2Array); - - // some drivers don't like huge triangles, so we do some subdivisons - // a paged solution would be even better - const float step = size/segments; - const float texCoordStep = textureRepeats / segments; - for (int x=0; xpush_back(osg::Vec3f(x1, y2, 0.f)); - verts->push_back(osg::Vec3f(x1, y1, 0.f)); - verts->push_back(osg::Vec3f(x2, y1, 0.f)); - verts->push_back(osg::Vec3f(x2, y2, 0.f)); - - float u1 = x*texCoordStep; - float v1 = y*texCoordStep; - float u2 = u1 + texCoordStep; - float v2 = v1 + texCoordStep; - - texcoords->push_back(osg::Vec2f(u1, v2)); - texcoords->push_back(osg::Vec2f(u1, v1)); - texcoords->push_back(osg::Vec2f(u2, v1)); - texcoords->push_back(osg::Vec2f(u2, v2)); - } - } - - osg::ref_ptr waterGeom (new osg::Geometry); - waterGeom->setVertexArray(verts); - waterGeom->setTexCoordArray(0, texcoords); - - osg::ref_ptr normal (new osg::Vec3Array); - normal->push_back(osg::Vec3f(0,0,1)); - waterGeom->setNormalArray(normal, osg::Array::BIND_OVERALL); - - waterGeom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,verts->size())); - return waterGeom; - } - -} - namespace MWRender { @@ -465,7 +415,7 @@ Water::Water(osg::Group *parent, osg::Group* sceneRoot, Resource::ResourceSystem { mSimulation.reset(new RippleSimulation(parent, resourceSystem, fallback)); - mWaterGeom = createWaterGeometry(CELL_SIZE*150, 40, 900); + mWaterGeom = SceneUtil::createWaterGeometry(CELL_SIZE*150, 40, 900); mWaterGeom->setDrawCallback(new DepthClampCallback); mWaterGeom->setNodeMask(Mask_Water); @@ -527,26 +477,11 @@ void Water::updateWaterMaterial() void Water::createSimpleWaterStateSet(osg::Node* node, float alpha) { - osg::ref_ptr stateset (new osg::StateSet); - - osg::ref_ptr material (new osg::Material); - material->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 1.f)); - material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(1.f, 1.f, 1.f, alpha)); - material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1.f, 1.f, 1.f, 1.f)); - material->setColorMode(osg::Material::OFF); - stateset->setAttributeAndModes(material, osg::StateAttribute::ON); - - stateset->setMode(GL_BLEND, osg::StateAttribute::ON); - stateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF); - - osg::ref_ptr depth (new osg::Depth); - depth->setWriteMask(false); - stateset->setAttributeAndModes(depth, osg::StateAttribute::ON); - - stateset->setRenderBinDetails(MWRender::RenderBin_Water, "RenderBin"); + osg::ref_ptr stateset = SceneUtil::createSimpleWaterStateSet(alpha, MWRender::RenderBin_Water); node->setStateSet(stateset); + // Add animated textures std::vector > textures; int frameCount = mFallback->getFallbackInt("Water_SurfaceFrameCount"); std::string texture = mFallback->getFallbackString("Water_SurfaceTexture"); diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index e5292eb6b..5e4387789 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -50,7 +50,7 @@ add_component_dir (shader add_component_dir (sceneutil clone attach visitor util statesetupdater controller skeleton riggeometry lightcontroller - lightmanager lightutil positionattitudetransform workqueue unrefqueue pathgridutil + lightmanager lightutil positionattitudetransform workqueue unrefqueue pathgridutil waterutil ) add_component_dir (nif diff --git a/components/sceneutil/waterutil.cpp b/components/sceneutil/waterutil.cpp new file mode 100644 index 000000000..562b0ee73 --- /dev/null +++ b/components/sceneutil/waterutil.cpp @@ -0,0 +1,79 @@ +#include "waterutil.hpp" + +#include +#include +#include +#include + +namespace SceneUtil +{ + osg::ref_ptr createWaterGeometry(float size, int segments, float textureRepeats) + { + osg::ref_ptr verts (new osg::Vec3Array); + osg::ref_ptr texcoords (new osg::Vec2Array); + + // some drivers don't like huge triangles, so we do some subdivisons + // a paged solution would be even better + const float step = size/segments; + const float texCoordStep = textureRepeats / segments; + for (int x=0; xpush_back(osg::Vec3f(x1, y2, 0.f)); + verts->push_back(osg::Vec3f(x1, y1, 0.f)); + verts->push_back(osg::Vec3f(x2, y1, 0.f)); + verts->push_back(osg::Vec3f(x2, y2, 0.f)); + + float u1 = x*texCoordStep; + float v1 = y*texCoordStep; + float u2 = u1 + texCoordStep; + float v2 = v1 + texCoordStep; + + texcoords->push_back(osg::Vec2f(u1, v2)); + texcoords->push_back(osg::Vec2f(u1, v1)); + texcoords->push_back(osg::Vec2f(u2, v1)); + texcoords->push_back(osg::Vec2f(u2, v2)); + } + } + + osg::ref_ptr waterGeom (new osg::Geometry); + waterGeom->setVertexArray(verts); + waterGeom->setTexCoordArray(0, texcoords); + + osg::ref_ptr normal (new osg::Vec3Array); + normal->push_back(osg::Vec3f(0,0,1)); + waterGeom->setNormalArray(normal, osg::Array::BIND_OVERALL); + + waterGeom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,verts->size())); + return waterGeom; + } + + osg::ref_ptr createSimpleWaterStateSet(float alpha, int renderBin) + { + osg::ref_ptr stateset (new osg::StateSet); + + osg::ref_ptr material (new osg::Material); + material->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 1.f)); + material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(1.f, 1.f, 1.f, alpha)); + material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1.f, 1.f, 1.f, 1.f)); + material->setColorMode(osg::Material::OFF); + stateset->setAttributeAndModes(material, osg::StateAttribute::ON); + + stateset->setMode(GL_BLEND, osg::StateAttribute::ON); + stateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF); + + osg::ref_ptr depth (new osg::Depth); + depth->setWriteMask(false); + stateset->setAttributeAndModes(depth, osg::StateAttribute::ON); + + stateset->setRenderBinDetails(renderBin, "RenderBin"); + + return stateset; + } +} diff --git a/components/sceneutil/waterutil.hpp b/components/sceneutil/waterutil.hpp new file mode 100644 index 000000000..7b8c38010 --- /dev/null +++ b/components/sceneutil/waterutil.hpp @@ -0,0 +1,19 @@ +#ifndef OPENMW_COMPONENTS_WATERUTIL_H +#define OPENMW_COMPONENTS_WATERUTIL_H + +#include + +namespace osg +{ + class Geometry; + class StateSet; +} + +namespace SceneUtil +{ + osg::ref_ptr createWaterGeometry(float size, int segments, float textureRepeats); + + osg::ref_ptr createSimpleWaterStateSet(float alpha, int renderBin); +} + +#endif