#include "water.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "vismask.hpp" #include "ripplesimulation.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); waterGeom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,verts->size())); return waterGeom; } void createWaterStateSet(Resource::ResourceSystem* resourceSystem, osg::ref_ptr node) { osg::ref_ptr stateset (new osg::StateSet); osg::ref_ptr material (new osg::Material); material->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(1.f, 1.f, 1.f, 1.f)); material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 0.7f)); material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.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(9, "RenderBin"); std::vector > textures; for (int i=0; i<32; ++i) { std::ostringstream texname; texname << "textures/water/water" << std::setw(2) << std::setfill('0') << i << ".dds"; textures.push_back(resourceSystem->getTextureManager()->getTexture2D(texname.str(), osg::Texture::REPEAT, osg::Texture::REPEAT)); } osg::ref_ptr controller (new NifOsg::FlipController(0, 2/32.f, textures)); controller->setSource(boost::shared_ptr(new SceneUtil::FrameTimeSource)); node->addUpdateCallback(controller); node->setStateSet(stateset); stateset->setTextureAttributeAndModes(0, textures[0], osg::StateAttribute::ON); } } namespace MWRender { // -------------------------------------------------------------------------------------------------------------------------------- Water::Water(osg::Group *parent, Resource::ResourceSystem *resourceSystem, osgUtil::IncrementalCompileOperation *ico, const MWWorld::Fallback* fallback) : mParent(parent) , mResourceSystem(resourceSystem) , mEnabled(true) , mToggled(true) , mTop(0) { mSimulation.reset(new RippleSimulation(parent, resourceSystem, fallback)); osg::ref_ptr waterGeom = createWaterGeometry(CELL_SIZE*150, 40, 900); osg::ref_ptr geode (new osg::Geode); geode->addDrawable(waterGeom); geode->setNodeMask(Mask_Water); if (ico) ico->add(geode); createWaterStateSet(mResourceSystem, geode); mWaterNode = new osg::PositionAttitudeTransform; mWaterNode->addChild(geode); mParent->addChild(mWaterNode); setHeight(mTop); } Water::~Water() { mParent->removeChild(mWaterNode); } void Water::setEnabled(bool enabled) { mEnabled = enabled; updateVisible(); } void Water::changeCell(const MWWorld::CellStore* store) { if (store->getCell()->isExterior()) mWaterNode->setPosition(getSceneNodeCoordinates(store->getCell()->mData.mX, store->getCell()->mData.mY)); else mWaterNode->setPosition(osg::Vec3f(0,0,mTop)); } void Water::setHeight(const float height) { mTop = height; mSimulation->setWaterHeight(height); osg::Vec3f pos = mWaterNode->getPosition(); pos.z() = height; mWaterNode->setPosition(pos); } void Water::update(float dt) { mSimulation->update(dt); } void Water::updateVisible() { mWaterNode->setNodeMask(mEnabled && mToggled ? ~0 : 0); } bool Water::toggle() { mToggled = !mToggled; updateVisible(); return mToggled; } bool Water::isUnderwater(const osg::Vec3f &pos) const { return pos.z() < mTop && mToggled && mEnabled; } osg::Vec3f Water::getSceneNodeCoordinates(int gridX, int gridY) { return osg::Vec3f(static_cast(gridX * CELL_SIZE + (CELL_SIZE / 2)), static_cast(gridY * CELL_SIZE + (CELL_SIZE / 2)), mTop); } void Water::addEmitter (const MWWorld::Ptr& ptr, float scale, float force) { mSimulation->addEmitter (ptr, scale, force); } void Water::removeEmitter (const MWWorld::Ptr& ptr) { mSimulation->removeEmitter (ptr); } void Water::updateEmitterPtr (const MWWorld::Ptr& old, const MWWorld::Ptr& ptr) { mSimulation->updateEmitterPtr(old, ptr); } void Water::removeCell(const MWWorld::CellStore *store) { mSimulation->removeCell(store); } void Water::clearRipples() { mSimulation->clear(); } }