#include "pagedworldspacewidget.hpp" #include #include #include #include #include #include "../../model/prefs/shortcut.hpp" #include "../../model/world/tablemimedata.hpp" #include "../../model/world/idtable.hpp" #include "../widget/scenetooltoggle2.hpp" #include "../widget/scenetoolmode.hpp" #include "../widget/scenetooltoggle2.hpp" #include "editmode.hpp" #include "mask.hpp" #include "cameracontroller.hpp" #include "cellarrow.hpp" bool CSVRender::PagedWorldspaceWidget::adjustCells() { bool modified = false; const CSMWorld::IdCollection& cells = mDocument.getData().getCells(); { // remove/update std::map::iterator iter (mCells.begin()); while (iter!=mCells.end()) { if (!mSelection.has (iter->first)) { // remove delete iter->second; mCells.erase (iter++); modified = true; } else { // update int index = cells.searchId (iter->first.getId (mWorldspace)); bool deleted = index==-1 || cells.getRecord (index).mState==CSMWorld::RecordBase::State_Deleted; if (deleted!=iter->second->isDeleted()) { modified = true; std::unique_ptr cell (new Cell (mDocument.getData(), mRootNode, iter->first.getId (mWorldspace), deleted)); delete iter->second; iter->second = cell.release(); } else if (!deleted) { // delete state has not changed -> just update // TODO check if name or region field has changed (cell marker) // FIXME: config setting //std::string name = cells.getRecord(index).get().mName; //std::string region = cells.getRecord(index).get().mRegion; modified = true; } ++iter; } } } // add for (CSMWorld::CellSelection::Iterator iter (mSelection.begin()); iter!=mSelection.end(); ++iter) { if (mCells.find (*iter)==mCells.end()) { addCellToScene (*iter); modified = true; } } if (modified) { for (std::map::const_iterator iter (mCells.begin()); iter!=mCells.end(); ++iter) { int mask = 0; for (int i=CellArrow::Direction_North; i<=CellArrow::Direction_East; i *= 2) { CSMWorld::CellCoordinates coordinates (iter->second->getCoordinates()); switch (i) { case CellArrow::Direction_North: coordinates = coordinates.move (0, 1); break; case CellArrow::Direction_West: coordinates = coordinates.move (-1, 0); break; case CellArrow::Direction_South: coordinates = coordinates.move (0, -1); break; case CellArrow::Direction_East: coordinates = coordinates.move (1, 0); break; } if (!mSelection.has (coordinates)) mask |= i; } iter->second->setCellArrows (mask); } } return modified; } void CSVRender::PagedWorldspaceWidget::addVisibilitySelectorButtons ( CSVWidget::SceneToolToggle2 *tool) { WorldspaceWidget::addVisibilitySelectorButtons (tool); tool->addButton (Button_Terrain, Mask_Terrain, "Terrain"); tool->addButton (Button_Fog, Mask_Fog, "Fog", "", true); } void CSVRender::PagedWorldspaceWidget::addEditModeSelectorButtons ( CSVWidget::SceneToolMode *tool) { WorldspaceWidget::addEditModeSelectorButtons (tool); /// \todo replace EditMode with suitable subclasses tool->addButton ( new EditMode (this, QIcon (":placeholder"), Mask_Reference, "Terrain shape editing"), "terrain-shape"); tool->addButton ( new EditMode (this, QIcon (":placeholder"), Mask_Reference, "Terrain texture editing"), "terrain-texture"); tool->addButton ( new EditMode (this, QIcon (":placeholder"), Mask_Reference, "Terrain vertex paint editing"), "terrain-vertex"); tool->addButton ( new EditMode (this, QIcon (":placeholder"), Mask_Reference, "Terrain movement"), "terrain-move"); } void CSVRender::PagedWorldspaceWidget::handleInteractionPress (const WorldspaceHitResult& hit, InteractionType type) { if (hit.tag && hit.tag->getMask()==Mask_CellArrow) { if (CellArrowTag *cellArrowTag = dynamic_cast (hit.tag.get())) { CellArrow *arrow = cellArrowTag->getCellArrow(); CSMWorld::CellCoordinates coordinates = arrow->getCoordinates(); CellArrow::Direction direction = arrow->getDirection(); int x = 0; int y = 0; switch (direction) { case CellArrow::Direction_North: y = 1; break; case CellArrow::Direction_West: x = -1; break; case CellArrow::Direction_South: y = -1; break; case CellArrow::Direction_East: x = 1; break; } bool modified = false; if (type == InteractionType_PrimarySelect) { addCellSelection (x, y); modified = true; } else if (type == InteractionType_SecondarySelect) { moveCellSelection (x, y); modified = true; } else // Primary/SecondaryEdit { CSMWorld::CellCoordinates newCoordinates = coordinates.move (x, y); if (mCells.find (newCoordinates)==mCells.end()) { addCellToScene (newCoordinates); mSelection.add (newCoordinates); modified = true; } if (type == InteractionType_SecondaryEdit) { if (mCells.find (coordinates)!=mCells.end()) { removeCellFromScene (coordinates); mSelection.remove (coordinates); modified = true; } } } if (modified) adjustCells(); return; } } WorldspaceWidget::handleInteractionPress (hit, type); } void CSVRender::PagedWorldspaceWidget::referenceableDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) { for (std::map::iterator iter (mCells.begin()); iter!=mCells.end(); ++iter) if (iter->second->referenceableDataChanged (topLeft, bottomRight)) flagAsModified(); } void CSVRender::PagedWorldspaceWidget::referenceableAboutToBeRemoved ( const QModelIndex& parent, int start, int end) { for (std::map::iterator iter (mCells.begin()); iter!=mCells.end(); ++iter) if (iter->second->referenceableAboutToBeRemoved (parent, start, end)) flagAsModified(); } void CSVRender::PagedWorldspaceWidget::referenceableAdded (const QModelIndex& parent, int start, int end) { CSMWorld::IdTable& referenceables = dynamic_cast ( *mDocument.getData().getTableModel (CSMWorld::UniversalId::Type_Referenceables)); for (std::map::iterator iter (mCells.begin()); iter!=mCells.end(); ++iter) { QModelIndex topLeft = referenceables.index (start, 0); QModelIndex bottomRight = referenceables.index (end, referenceables.columnCount()); if (iter->second->referenceableDataChanged (topLeft, bottomRight)) flagAsModified(); } } void CSVRender::PagedWorldspaceWidget::referenceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) { for (std::map::iterator iter (mCells.begin()); iter!=mCells.end(); ++iter) if (iter->second->referenceDataChanged (topLeft, bottomRight)) flagAsModified(); } void CSVRender::PagedWorldspaceWidget::referenceAboutToBeRemoved (const QModelIndex& parent, int start, int end) { for (std::map::iterator iter (mCells.begin()); iter!=mCells.end(); ++iter) if (iter->second->referenceAboutToBeRemoved (parent, start, end)) flagAsModified(); } void CSVRender::PagedWorldspaceWidget::referenceAdded (const QModelIndex& parent, int start, int end) { for (std::map::iterator iter (mCells.begin()); iter!=mCells.end(); ++iter) if (iter->second->referenceAdded (parent, start, end)) flagAsModified(); } void CSVRender::PagedWorldspaceWidget::pathgridDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) { const CSMWorld::SubCellCollection& pathgrids = mDocument.getData().getPathgrids(); 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::Pathgrid& pathgrid = pathgrids.getRecord(row).get(); CSMWorld::CellCoordinates coords = CSMWorld::CellCoordinates(pathgrid.mData.mX, pathgrid.mData.mY); std::map::iterator searchResult = mCells.find(coords); if (searchResult != mCells.end()) { searchResult->second->pathgridModified(); flagAsModified(); } } } void CSVRender::PagedWorldspaceWidget::pathgridAboutToBeRemoved (const QModelIndex& parent, int start, int end) { const CSMWorld::SubCellCollection& pathgrids = mDocument.getData().getPathgrids(); if (!parent.isValid()) { // Pathgrid going to be deleted for (int row = start; row <= end; ++row) { const CSMWorld::Pathgrid& pathgrid = pathgrids.getRecord(row).get(); CSMWorld::CellCoordinates coords = CSMWorld::CellCoordinates(pathgrid.mData.mX, pathgrid.mData.mY); std::map::iterator searchResult = mCells.find(coords); if (searchResult != mCells.end()) { searchResult->second->pathgridRemoved(); flagAsModified(); } } } } void CSVRender::PagedWorldspaceWidget::pathgridAdded(const QModelIndex& parent, int start, int end) { const CSMWorld::SubCellCollection& pathgrids = mDocument.getData().getPathgrids(); if (!parent.isValid()) { for (int row = start; row <= end; ++row) { const CSMWorld::Pathgrid& pathgrid = pathgrids.getRecord(row).get(); CSMWorld::CellCoordinates coords = CSMWorld::CellCoordinates(pathgrid.mData.mX, pathgrid.mData.mY); std::map::iterator searchResult = mCells.find(coords); if (searchResult != mCells.end()) { searchResult->second->pathgridModified(); flagAsModified(); } } } } std::string CSVRender::PagedWorldspaceWidget::getStartupInstruction() { osg::Vec3d eye, center, up; mView->getCamera()->getViewMatrixAsLookAt(eye, center, up); osg::Vec3d position = eye; std::ostringstream stream; stream << "player->position " << position.x() << ", " << position.y() << ", " << position.z() << ", 0"; return stream.str(); } void CSVRender::PagedWorldspaceWidget::addCellToScene ( const CSMWorld::CellCoordinates& coordinates) { const CSMWorld::IdCollection& cells = mDocument.getData().getCells(); int index = cells.searchId (coordinates.getId (mWorldspace)); bool deleted = index==-1 || cells.getRecord (index).mState==CSMWorld::RecordBase::State_Deleted; std::unique_ptr cell ( new Cell (mDocument.getData(), mRootNode, coordinates.getId (mWorldspace), deleted)); EditMode *editMode = getEditMode(); cell->setSubMode (editMode->getSubMode(), editMode->getInteractionMask()); mCells.insert (std::make_pair (coordinates, cell.release())); } void CSVRender::PagedWorldspaceWidget::removeCellFromScene ( const CSMWorld::CellCoordinates& coordinates) { std::map::iterator iter = mCells.find (coordinates); if (iter!=mCells.end()) { delete iter->second; mCells.erase (iter); } } void CSVRender::PagedWorldspaceWidget::addCellSelection (int x, int y) { CSMWorld::CellSelection newSelection = mSelection; newSelection.move (x, y); for (CSMWorld::CellSelection::Iterator iter (newSelection.begin()); iter!=newSelection.end(); ++iter) { if (mCells.find (*iter)==mCells.end()) { addCellToScene (*iter); mSelection.add (*iter); } } } void CSVRender::PagedWorldspaceWidget::moveCellSelection (int x, int y) { CSMWorld::CellSelection newSelection = mSelection; newSelection.move (x, y); for (CSMWorld::CellSelection::Iterator iter (mSelection.begin()); iter!=mSelection.end(); ++iter) { if (!newSelection.has (*iter)) removeCellFromScene (*iter); } for (CSMWorld::CellSelection::Iterator iter (newSelection.begin()); iter!=newSelection.end(); ++iter) { if (!mSelection.has (*iter)) addCellToScene (*iter); } mSelection = newSelection; } void CSVRender::PagedWorldspaceWidget::addCellToSceneFromCamera (int offsetX, int offsetY) { const int CellSize = 8192; osg::Vec3f eye, center, up; getCamera()->getViewMatrixAsLookAt(eye, center, up); int cellX = (int)std::floor(center.x() / CellSize) + offsetX; int cellY = (int)std::floor(center.y() / CellSize) + offsetY; CSMWorld::CellCoordinates cellCoordinates(cellX, cellY); if (!mSelection.has(cellCoordinates)) { addCellToScene(cellCoordinates); mSelection.add(cellCoordinates); adjustCells(); } } CSVRender::PagedWorldspaceWidget::PagedWorldspaceWidget (QWidget* parent, CSMDoc::Document& document) : WorldspaceWidget (document, parent), mDocument (document), mWorldspace ("std::default"), mControlElements(NULL), mDisplayCellCoord(true) { QAbstractItemModel *cells = document.getData().getTableModel (CSMWorld::UniversalId::Type_Cells); connect (cells, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), this, SLOT (cellDataChanged (const QModelIndex&, const QModelIndex&))); connect (cells, SIGNAL (rowsRemoved (const QModelIndex&, int, int)), this, SLOT (cellRemoved (const QModelIndex&, int, int))); connect (cells, SIGNAL (rowsInserted (const QModelIndex&, int, int)), this, SLOT (cellAdded (const QModelIndex&, int, int))); // Shortcuts CSMPrefs::Shortcut* loadCameraCellShortcut = new CSMPrefs::Shortcut("scene-load-cam-cell", this); connect(loadCameraCellShortcut, SIGNAL(activated()), this, SLOT(loadCameraCell())); CSMPrefs::Shortcut* loadCameraEastCellShortcut = new CSMPrefs::Shortcut("scene-load-cam-eastcell", this); connect(loadCameraEastCellShortcut, SIGNAL(activated()), this, SLOT(loadEastCell())); CSMPrefs::Shortcut* loadCameraNorthCellShortcut = new CSMPrefs::Shortcut("scene-load-cam-northcell", this); connect(loadCameraNorthCellShortcut, SIGNAL(activated()), this, SLOT(loadNorthCell())); CSMPrefs::Shortcut* loadCameraWestCellShortcut = new CSMPrefs::Shortcut("scene-load-cam-westcell", this); connect(loadCameraWestCellShortcut, SIGNAL(activated()), this, SLOT(loadWestCell())); CSMPrefs::Shortcut* loadCameraSouthCellShortcut = new CSMPrefs::Shortcut("scene-load-cam-southcell", this); connect(loadCameraSouthCellShortcut, SIGNAL(activated()), this, SLOT(loadSouthCell())); } CSVRender::PagedWorldspaceWidget::~PagedWorldspaceWidget() { for (std::map::iterator iter (mCells.begin()); iter!=mCells.end(); ++iter) { delete iter->second; } } void CSVRender::PagedWorldspaceWidget::useViewHint (const std::string& hint) { if (!hint.empty()) { CSMWorld::CellSelection selection; if (hint[0]=='c') { // syntax: c:#x1 y1; #x2 y2 (number of coordinate pairs can be 0 or larger) char ignore; std::istringstream stream (hint.c_str()); if (stream >> ignore) { char ignore1; // : or ; char ignore2; // # // Current coordinate int x, y; // Loop through all the coordinates to add them to selection while (stream >> ignore1 >> ignore2 >> x >> y) selection.add (CSMWorld::CellCoordinates (x, y)); // Mark that camera needs setup mCamPositionSet=false; } } else if (hint[0]=='r') { /// \todo implement 'r' type hints } setCellSelection (selection); } } void CSVRender::PagedWorldspaceWidget::setCellSelection (const CSMWorld::CellSelection& selection) { mSelection = selection; if (adjustCells()) flagAsModified(); emit cellSelectionChanged (mSelection); } const CSMWorld::CellSelection& CSVRender::PagedWorldspaceWidget::getCellSelection() const { return mSelection; } std::pair< int, int > CSVRender::PagedWorldspaceWidget::getCoordinatesFromId (const std::string& record) const { std::istringstream stream (record.c_str()); char ignore; int x, y; stream >> ignore >> x >> y; return std::make_pair(x, y); } bool CSVRender::PagedWorldspaceWidget::handleDrop ( const std::vector< CSMWorld::UniversalId >& universalIdData, DropType type) { if (WorldspaceWidget::handleDrop (universalIdData, type)) return true; if (type!=Type_CellsExterior) return false; bool selectionChanged = false; for (unsigned i = 0; i < universalIdData.size(); ++i) { std::pair coordinates(getCoordinatesFromId(universalIdData[i].getId())); if (mSelection.add(CSMWorld::CellCoordinates(coordinates.first, coordinates.second))) { selectionChanged = true; } } if (selectionChanged) { if (adjustCells()) flagAsModified(); emit cellSelectionChanged(mSelection); } return true; } CSVRender::WorldspaceWidget::dropRequirments CSVRender::PagedWorldspaceWidget::getDropRequirements (CSVRender::WorldspaceWidget::DropType type) const { dropRequirments requirements = WorldspaceWidget::getDropRequirements (type); if (requirements!=ignored) return requirements; switch (type) { case Type_CellsExterior: return canHandle; case Type_CellsInterior: return needUnpaged; default: return ignored; } } unsigned int CSVRender::PagedWorldspaceWidget::getVisibilityMask() const { return WorldspaceWidget::getVisibilityMask() | mControlElements->getSelectionMask(); } void CSVRender::PagedWorldspaceWidget::clearSelection (int elementMask) { for (std::map::iterator iter = mCells.begin(); iter!=mCells.end(); ++iter) iter->second->setSelection (elementMask, Cell::Selection_Clear); flagAsModified(); } void CSVRender::PagedWorldspaceWidget::invertSelection (int elementMask) { for (std::map::iterator iter = mCells.begin(); iter!=mCells.end(); ++iter) iter->second->setSelection (elementMask, Cell::Selection_Invert); flagAsModified(); } void CSVRender::PagedWorldspaceWidget::selectAll (int elementMask) { for (std::map::iterator iter = mCells.begin(); iter!=mCells.end(); ++iter) iter->second->setSelection (elementMask, Cell::Selection_All); flagAsModified(); } void CSVRender::PagedWorldspaceWidget::selectAllWithSameParentId (int elementMask) { for (std::map::iterator iter = mCells.begin(); iter!=mCells.end(); ++iter) iter->second->selectAllWithSameParentId (elementMask); flagAsModified(); } std::string CSVRender::PagedWorldspaceWidget::getCellId (const osg::Vec3f& point) const { const int cellSize = 8192; CSMWorld::CellCoordinates cellCoordinates ( static_cast (std::floor (point.x()/cellSize)), static_cast (std::floor (point.y()/cellSize))); return cellCoordinates.getId (mWorldspace); } CSVRender::Cell* CSVRender::PagedWorldspaceWidget::getCell(const osg::Vec3d& point) const { const int cellSize = 8192; CSMWorld::CellCoordinates coords( static_cast (std::floor (point.x()/cellSize)), static_cast (std::floor (point.y()/cellSize))); std::map::const_iterator searchResult = mCells.find(coords); if (searchResult != mCells.end()) return searchResult->second; else return 0; } std::vector > CSVRender::PagedWorldspaceWidget::getSelection ( unsigned int elementMask) const { std::vector > result; for (std::map::const_iterator iter = mCells.begin(); iter!=mCells.end(); ++iter) { std::vector > cellResult = iter->second->getSelection (elementMask); result.insert (result.end(), cellResult.begin(), cellResult.end()); } return result; } std::vector > CSVRender::PagedWorldspaceWidget::getEdited ( unsigned int elementMask) const { std::vector > result; for (std::map::const_iterator iter = mCells.begin(); iter!=mCells.end(); ++iter) { std::vector > cellResult = iter->second->getEdited (elementMask); result.insert (result.end(), cellResult.begin(), cellResult.end()); } return result; } void CSVRender::PagedWorldspaceWidget::setSubMode (int subMode, unsigned int elementMask) { for (std::map::const_iterator iter = mCells.begin(); iter!=mCells.end(); ++iter) iter->second->setSubMode (subMode, elementMask); } void CSVRender::PagedWorldspaceWidget::reset (unsigned int elementMask) { for (std::map::const_iterator iter = mCells.begin(); iter!=mCells.end(); ++iter) iter->second->reset (elementMask); } CSVWidget::SceneToolToggle2 *CSVRender::PagedWorldspaceWidget::makeControlVisibilitySelector ( CSVWidget::SceneToolbar *parent) { mControlElements = new CSVWidget::SceneToolToggle2 (parent, "Controls & Guides Visibility", ":scenetoolbar/scene-view-marker-c", ":scenetoolbar/scene-view-marker-"); mControlElements->addButton (1, Mask_CellMarker, "Cell Marker"); mControlElements->addButton (2, Mask_CellArrow, "Cell Arrows"); mControlElements->addButton (4, Mask_CellBorder, "Cell Border"); mControlElements->setSelectionMask (0xffffffff); connect (mControlElements, SIGNAL (selectionChanged()), this, SLOT (elementSelectionChanged())); return mControlElements; } void CSVRender::PagedWorldspaceWidget::cellDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) { /// \todo check if no selected cell is affected and do not update, if that is the case if (adjustCells()) flagAsModified(); } void CSVRender::PagedWorldspaceWidget::cellRemoved (const QModelIndex& parent, int start, int end) { if (adjustCells()) flagAsModified(); } void CSVRender::PagedWorldspaceWidget::cellAdded (const QModelIndex& index, int start, int end) { /// \todo check if no selected cell is affected and do not update, if that is the case if (adjustCells()) flagAsModified(); } void CSVRender::PagedWorldspaceWidget::loadCameraCell() { addCellToSceneFromCamera(0, 0); } void CSVRender::PagedWorldspaceWidget::loadEastCell() { addCellToSceneFromCamera(1, 0); } void CSVRender::PagedWorldspaceWidget::loadNorthCell() { addCellToSceneFromCamera(0, 1); } void CSVRender::PagedWorldspaceWidget::loadWestCell() { addCellToSceneFromCamera(-1, 0); } void CSVRender::PagedWorldspaceWidget::loadSouthCell() { addCellToSceneFromCamera(0, -1); }