mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-10-31 19:56:38 +00:00 
			
		
		
		
	Merge pull request #2517 from unelsson/terraintextureselection
[Final tests] Editor: Terrain texture selection
This commit is contained in:
		
						commit
						6128279dbf
					
				
					 9 changed files with 650 additions and 89 deletions
				
			
		|  | @ -143,6 +143,7 @@ | |||
|     Feature #3025: Analogue gamepad movement controls | ||||
|     Feature #3442: Default values for fallbacks from ini file | ||||
|     Feature #3610: Option to invert X axis | ||||
|     Feature #3871: Editor: Terrain Selection | ||||
|     Feature #3893: Implicit target for "set" function in console | ||||
|     Feature #3980: In-game option to disable controller | ||||
|     Feature #3999: Shift + Double Click should maximize/restore menu size | ||||
|  |  | |||
|  | @ -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 | ||||
|     cellwater terraintexturemode actor terrainselection | ||||
|     ) | ||||
| 
 | ||||
| opencs_units_noqt (view/render | ||||
|  |  | |||
|  | @ -8,13 +8,6 @@ | |||
| #include <components/esm/loadland.hpp> | ||||
| #include <components/misc/constants.hpp> | ||||
| 
 | ||||
| namespace | ||||
| { | ||||
|     const int cellSize {ESM::Land::REAL_SIZE}; | ||||
|     const int landSize {ESM::Land::LAND_SIZE}; | ||||
|     const int landTextureSize {ESM::Land::LAND_TEXTURE_SIZE}; | ||||
| } | ||||
| 
 | ||||
| CSMWorld::CellCoordinates::CellCoordinates() : mX (0), mY (0) {} | ||||
| 
 | ||||
| CSMWorld::CellCoordinates::CellCoordinates (int x, int y) : mX (x), mY (y) {} | ||||
|  | @ -76,10 +69,10 @@ std::pair<int, int> CSMWorld::CellCoordinates::coordinatesToCellIndex (float x, | |||
|     return std::make_pair (std::floor (x / Constants::CellSizeInUnits), std::floor (y / Constants::CellSizeInUnits)); | ||||
| } | ||||
| 
 | ||||
| std::pair<int, int> CSMWorld::CellCoordinates::toTextureCoords(osg::Vec3d worldPos) | ||||
| std::pair<int, int> CSMWorld::CellCoordinates::toTextureCoords(const osg::Vec3d& worldPos) | ||||
| { | ||||
|     const auto xd = static_cast<float>(worldPos.x() * landTextureSize / cellSize - 0.25f); | ||||
|     const auto yd = static_cast<float>(worldPos.y() * landTextureSize / cellSize + 0.25f); | ||||
|     const auto xd = static_cast<float>(worldPos.x() * ESM::Land::LAND_TEXTURE_SIZE / ESM::Land::REAL_SIZE - 0.25f); | ||||
|     const auto yd = static_cast<float>(worldPos.y() * ESM::Land::LAND_TEXTURE_SIZE / ESM::Land::REAL_SIZE + 0.25f); | ||||
| 
 | ||||
|     const auto x = static_cast<int>(std::floor(xd)); | ||||
|     const auto y = static_cast<int>(std::floor(yd)); | ||||
|  | @ -87,10 +80,10 @@ std::pair<int, int> CSMWorld::CellCoordinates::toTextureCoords(osg::Vec3d worldP | |||
|     return std::make_pair(x, y); | ||||
| } | ||||
| 
 | ||||
| std::pair<int, int> CSMWorld::CellCoordinates::toVertexCoords(osg::Vec3d worldPos) | ||||
| std::pair<int, int> CSMWorld::CellCoordinates::toVertexCoords(const osg::Vec3d& worldPos) | ||||
| { | ||||
|     const auto xd = static_cast<float>(worldPos.x() * (landSize - 1) / cellSize + 0.5f); | ||||
|     const auto yd = static_cast<float>(worldPos.y() * (landSize - 1) / cellSize + 0.5f); | ||||
|     const auto xd = static_cast<float>(worldPos.x() * (ESM::Land::LAND_SIZE - 1) / ESM::Land::REAL_SIZE + 0.5f); | ||||
|     const auto yd = static_cast<float>(worldPos.y() * (ESM::Land::LAND_SIZE - 1) / ESM::Land::REAL_SIZE + 0.5f); | ||||
| 
 | ||||
|     const auto x = static_cast<int>(std::floor(xd)); | ||||
|     const auto y = static_cast<int>(std::floor(yd)); | ||||
|  | @ -98,25 +91,32 @@ std::pair<int, int> CSMWorld::CellCoordinates::toVertexCoords(osg::Vec3d worldPo | |||
|     return std::make_pair(x, y); | ||||
| } | ||||
| 
 | ||||
| float CSMWorld::CellCoordinates::textureSelectionToWorldCoords(int pos) | ||||
| float CSMWorld::CellCoordinates::textureGlobalToWorldCoords(int textureGlobal) | ||||
| { | ||||
|     return cellSize * static_cast<float>(pos) / landTextureSize; | ||||
|     return ESM::Land::REAL_SIZE * static_cast<float>(textureGlobal) / ESM::Land::LAND_TEXTURE_SIZE; | ||||
| } | ||||
| 
 | ||||
| float CSMWorld::CellCoordinates::vertexSelectionToWorldCoords(int pos) | ||||
| float CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(int vertexGlobal) | ||||
| { | ||||
|     return cellSize * static_cast<float>(pos) / (landSize - 1); | ||||
|     return ESM::Land::REAL_SIZE * static_cast<float>(vertexGlobal) / (ESM::Land::LAND_SIZE - 1); | ||||
| } | ||||
| 
 | ||||
| int CSMWorld::CellCoordinates::vertexSelectionToInCellCoords(int pos) | ||||
| int CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(int vertexGlobal) | ||||
| { | ||||
|     return static_cast<int>(pos - std::floor(static_cast<float>(pos) / (landSize - 1)) * (landSize - 1)); | ||||
|     return static_cast<int>(vertexGlobal - std::floor(static_cast<float>(vertexGlobal) / (ESM::Land::LAND_SIZE - 1)) * (ESM::Land::LAND_SIZE - 1)); | ||||
| } | ||||
| 
 | ||||
| std::string CSMWorld::CellCoordinates::vertexGlobalToCellId(std::pair<int, int> vertexGlobal) | ||||
| std::string CSMWorld::CellCoordinates::textureGlobalToCellId(const std::pair<int, int>& textureGlobal) | ||||
| { | ||||
|     int x = std::floor(static_cast<float>(vertexGlobal.first) / (landSize - 1)); | ||||
|     int y = std::floor(static_cast<float>(vertexGlobal.second) / (landSize - 1)); | ||||
|     int x = std::floor(static_cast<float>(textureGlobal.first) / ESM::Land::LAND_TEXTURE_SIZE); | ||||
|     int y = std::floor(static_cast<float>(textureGlobal.second) / ESM::Land::LAND_TEXTURE_SIZE); | ||||
|     return generateId(x, y); | ||||
| } | ||||
| 
 | ||||
| std::string CSMWorld::CellCoordinates::vertexGlobalToCellId(const std::pair<int, int>& vertexGlobal) | ||||
| { | ||||
|     int x = std::floor(static_cast<float>(vertexGlobal.first) / (ESM::Land::LAND_SIZE - 1)); | ||||
|     int y = std::floor(static_cast<float>(vertexGlobal.second) / (ESM::Land::LAND_SIZE - 1)); | ||||
|     return generateId(x, y); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -49,22 +49,25 @@ namespace CSMWorld | |||
|             static std::pair<int, int> coordinatesToCellIndex (float x, float y); | ||||
| 
 | ||||
|             ///Converts worldspace coordinates to global texture selection, taking in account the texture offset.
 | ||||
|             static std::pair<int, int> toTextureCoords(osg::Vec3d worldPos); | ||||
|             static std::pair<int, int> toTextureCoords(const osg::Vec3d& worldPos); | ||||
| 
 | ||||
|             ///Converts worldspace coordinates to global vertex selection.
 | ||||
|             static std::pair<int, int> toVertexCoords(osg::Vec3d worldPos); | ||||
|             static std::pair<int, int> toVertexCoords(const osg::Vec3d& worldPos); | ||||
| 
 | ||||
|             ///Converts global texture coordinate to worldspace coordinate that is at the upper left corner of the selected texture.
 | ||||
|             static float textureSelectionToWorldCoords(int); | ||||
|             static float textureGlobalToWorldCoords(int textureGlobal); | ||||
| 
 | ||||
|             ///Converts global vertex coordinate to worldspace coordinate
 | ||||
|             static float vertexSelectionToWorldCoords(int); | ||||
|             static float vertexGlobalToWorldCoords(int vertexGlobal); | ||||
| 
 | ||||
|             ///Converts local cell's heightmap coordinates from the global vertex coordinate
 | ||||
|             static int vertexSelectionToInCellCoords(int); | ||||
|             ///Converts global vertex coordinate to local cell's heightmap coordinates
 | ||||
|             static int vertexGlobalToInCellCoords(int vertexGlobal); | ||||
| 
 | ||||
|             ///Converts global texture coordinates to cell id
 | ||||
|             static std::string textureGlobalToCellId(const std::pair<int, int>& textureGlobal); | ||||
| 
 | ||||
|             ///Converts global vertex coordinates to cell id
 | ||||
|             static std::string vertexGlobalToCellId(std::pair<int, int>); | ||||
|             static std::string vertexGlobalToCellId(const std::pair<int, int>& vertexGlobal); | ||||
|     }; | ||||
| 
 | ||||
|     bool operator== (const CellCoordinates& left, const CellCoordinates& right); | ||||
|  |  | |||
|  | @ -140,7 +140,7 @@ void CSVRender::PagedWorldspaceWidget::addEditModeSelectorButtons ( | |||
|         new EditMode (this, QIcon (":placeholder"), Mask_Reference, "Terrain shape editing"), | ||||
|         "terrain-shape"); | ||||
|     tool->addButton ( | ||||
|         new TerrainTextureMode (this, tool), | ||||
|         new TerrainTextureMode (this, mRootNode, tool), | ||||
|         "terrain-texture"); | ||||
|     tool->addButton ( | ||||
|         new EditMode (this, QIcon (":placeholder"), Mask_Reference, "Terrain vertex paint editing"), | ||||
|  |  | |||
							
								
								
									
										261
									
								
								apps/opencs/view/render/terrainselection.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										261
									
								
								apps/opencs/view/render/terrainselection.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,261 @@ | |||
| #include "terrainselection.hpp" | ||||
| 
 | ||||
| #include <algorithm> | ||||
| 
 | ||||
| #include <osg/Group> | ||||
| #include <osg/Geometry> | ||||
| #include <osg/PositionAttitudeTransform> | ||||
| 
 | ||||
| #include <components/esm/loadland.hpp> | ||||
| 
 | ||||
| #include "../../model/world/cellcoordinates.hpp" | ||||
| #include "../../model/world/columnimp.hpp" | ||||
| #include "../../model/world/idtable.hpp" | ||||
| 
 | ||||
| #include "cell.hpp" | ||||
| #include "worldspacewidget.hpp" | ||||
| 
 | ||||
| CSVRender::TerrainSelection::TerrainSelection(osg::Group* parentNode, WorldspaceWidget *worldspaceWidget, TerrainSelectionType type): | ||||
| mParentNode(parentNode), mWorldspaceWidget (worldspaceWidget), mDraggedOperationFlag(false), mSelectionType(type) | ||||
| { | ||||
|     mGeometry = new osg::Geometry(); | ||||
| 
 | ||||
|     mSelectionNode = new osg::Group(); | ||||
|     mSelectionNode->addChild(mGeometry); | ||||
| 
 | ||||
|     activate(); | ||||
| } | ||||
| 
 | ||||
| CSVRender::TerrainSelection::~TerrainSelection() | ||||
| { | ||||
|     deactivate(); | ||||
| } | ||||
| 
 | ||||
| std::vector<std::pair<int, int>> CSVRender::TerrainSelection::getTerrainSelection() const | ||||
| { | ||||
|     return mSelection; | ||||
| } | ||||
| 
 | ||||
| void CSVRender::TerrainSelection::onlySelect(const std::vector<std::pair<int, int>> &localPositions) | ||||
| { | ||||
|     mSelection = localPositions; | ||||
|     update(); | ||||
| } | ||||
| 
 | ||||
| void CSVRender::TerrainSelection::addSelect(const std::pair<int, int> &localPos) | ||||
| { | ||||
|     if (std::find(mSelection.begin(), mSelection.end(), localPos) == mSelection.end()) | ||||
|     { | ||||
|         mSelection.emplace_back(localPos); | ||||
|         update(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void CSVRender::TerrainSelection::toggleSelect(const std::vector<std::pair<int, int>> &localPositions, bool toggleInProgress) | ||||
| { | ||||
|     if (toggleInProgress) | ||||
|     { | ||||
|         for(auto const& localPos: localPositions) | ||||
|         { | ||||
|             auto iterTemp = std::find(mTemporarySelection.begin(), mTemporarySelection.end(), localPos); | ||||
|             mDraggedOperationFlag = true; | ||||
| 
 | ||||
|             if (iterTemp == mTemporarySelection.end()) | ||||
|             { | ||||
|                 auto iter = std::find(mSelection.begin(), mSelection.end(), localPos); | ||||
|                 if (iter != mSelection.end()) | ||||
|                 { | ||||
|                     mSelection.erase(iter); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     mSelection.emplace_back(localPos); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             mTemporarySelection.push_back(localPos); | ||||
|         } | ||||
|     } | ||||
|     else if (mDraggedOperationFlag == false) | ||||
|     { | ||||
|         for(auto const& localPos: localPositions) | ||||
|         { | ||||
|             const auto iter = std::find(mSelection.begin(), mSelection.end(), localPos); | ||||
|             if (iter != mSelection.end()) | ||||
|             { | ||||
|                 mSelection.erase(iter); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 mSelection.emplace_back(localPos); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         mDraggedOperationFlag = false; | ||||
|         mTemporarySelection.clear(); | ||||
|     } | ||||
|     update(); | ||||
| } | ||||
| 
 | ||||
| void CSVRender::TerrainSelection::activate() | ||||
| { | ||||
|     if (!mParentNode->containsNode(mSelectionNode)) mParentNode->addChild(mSelectionNode); | ||||
| } | ||||
| 
 | ||||
| void CSVRender::TerrainSelection::deactivate() | ||||
| { | ||||
|     mParentNode->removeChild(mSelectionNode); | ||||
| } | ||||
| 
 | ||||
| void CSVRender::TerrainSelection::update() | ||||
| { | ||||
|     mSelectionNode->removeChild(mGeometry); | ||||
|     mGeometry = new osg::Geometry(); | ||||
| 
 | ||||
|     const osg::ref_ptr<osg::Vec3Array> vertices (new osg::Vec3Array); | ||||
| 
 | ||||
|     switch (mSelectionType) | ||||
|     { | ||||
|         case TerrainSelectionType::Texture : drawTextureSelection(vertices); | ||||
|         break; | ||||
|         case TerrainSelectionType::Shape : drawShapeSelection(vertices); | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     mGeometry->setVertexArray(vertices); | ||||
|     osg::ref_ptr<osg::DrawArrays> drawArrays = new osg::DrawArrays(osg::PrimitiveSet::LINES); | ||||
|     drawArrays->setCount(vertices->size()); | ||||
|     if (vertices->size() != 0) mGeometry->addPrimitiveSet(drawArrays); | ||||
|     mSelectionNode->addChild(mGeometry); | ||||
| } | ||||
| 
 | ||||
| void CSVRender::TerrainSelection::drawShapeSelection(const osg::ref_ptr<osg::Vec3Array> vertices) | ||||
| { | ||||
|     if (!mSelection.empty()) | ||||
|     { | ||||
|         for (std::pair<int, int> &localPos : mSelection) | ||||
|         { | ||||
|             int x (localPos.first); | ||||
|             int y (localPos.second); | ||||
| 
 | ||||
|             float xWorldCoord(CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(x)); | ||||
|             float yWorldCoord(CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(y)); | ||||
| 
 | ||||
|             osg::Vec3f pointXY(xWorldCoord, yWorldCoord, calculateLandHeight(x, y) + 2); | ||||
| 
 | ||||
|             vertices->push_back(pointXY); | ||||
|             vertices->push_back(osg::Vec3f(xWorldCoord, CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(y - 1), calculateLandHeight(x, y - 1) + 2)); | ||||
|             vertices->push_back(pointXY); | ||||
|             vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(x - 1), yWorldCoord, calculateLandHeight(x - 1, y) + 2)); | ||||
| 
 | ||||
|             const auto north = std::find(mSelection.begin(), mSelection.end(), std::make_pair(x, y + 1)); | ||||
|             if (north == mSelection.end()) | ||||
|             { | ||||
|                 vertices->push_back(pointXY); | ||||
|                 vertices->push_back(osg::Vec3f(xWorldCoord, CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(y + 1), calculateLandHeight(x, y + 1) + 2)); | ||||
|             } | ||||
| 
 | ||||
|             const auto east = std::find(mSelection.begin(), mSelection.end(), std::make_pair(x + 1, y)); | ||||
|             if (east == mSelection.end()) | ||||
|             { | ||||
|                 vertices->push_back(pointXY); | ||||
|                 vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(x + 1), yWorldCoord, calculateLandHeight(x + 1, y) + 2)); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void CSVRender::TerrainSelection::drawTextureSelection(const osg::ref_ptr<osg::Vec3Array> vertices) | ||||
| { | ||||
|     if (!mSelection.empty()) | ||||
|     { | ||||
|         // Nudge selection by 1/4th of a texture size, similar how blendmaps are nudged
 | ||||
|         const float nudgePercentage = 0.25f; | ||||
|         const int nudgeOffset = (ESM::Land::REAL_SIZE / ESM::Land::LAND_TEXTURE_SIZE) * nudgePercentage; | ||||
|         const int landHeightsNudge = (ESM::Land::REAL_SIZE / ESM::Land::LAND_SIZE) / (ESM::Land::LAND_SIZE - 1); // Does this work with all land size configurations?
 | ||||
| 
 | ||||
|         const int textureSizeToLandSizeModifier = (ESM::Land::LAND_SIZE - 1) / ESM::Land::LAND_TEXTURE_SIZE; | ||||
| 
 | ||||
|         for (std::pair<int, int> &localPos : mSelection) | ||||
|         { | ||||
|             int x (localPos.first); | ||||
|             int y (localPos.second); | ||||
| 
 | ||||
|             // convert texture selection to global vertex coordinates at selection box corners
 | ||||
|             int x1 = x * textureSizeToLandSizeModifier + landHeightsNudge; | ||||
|             int x2 = x * textureSizeToLandSizeModifier + textureSizeToLandSizeModifier + landHeightsNudge; | ||||
|             int y1 = y * textureSizeToLandSizeModifier - landHeightsNudge; | ||||
|             int y2 = y * textureSizeToLandSizeModifier + textureSizeToLandSizeModifier - landHeightsNudge; | ||||
| 
 | ||||
|             // Draw edges (check all sides, draw lines between vertices, +1 height to keep lines above ground)
 | ||||
|             // Check adjancent selections, draw lines only to edges of the selection
 | ||||
|             const auto north = std::find(mSelection.begin(), mSelection.end(), std::make_pair(x, y + 1)); | ||||
|             if (north == mSelection.end()) | ||||
|             { | ||||
|                 for(int i = 1; i < (textureSizeToLandSizeModifier + 1); i++) | ||||
|                 { | ||||
|                     float drawPreviousX = CSMWorld::CellCoordinates::textureGlobalToWorldCoords(x) + (i - 1) * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1)); | ||||
|                     float drawCurrentX = CSMWorld::CellCoordinates::textureGlobalToWorldCoords(x) + i * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1)); | ||||
|                     vertices->push_back(osg::Vec3f(drawPreviousX + nudgeOffset, CSMWorld::CellCoordinates::textureGlobalToWorldCoords(y + 1) - nudgeOffset, calculateLandHeight(x1+(i-1), y2)+2)); | ||||
|                     vertices->push_back(osg::Vec3f(drawCurrentX + nudgeOffset, CSMWorld::CellCoordinates::textureGlobalToWorldCoords(y + 1) - nudgeOffset, calculateLandHeight(x1+i, y2)+2)); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             const auto south = std::find(mSelection.begin(), mSelection.end(), std::make_pair(x, y - 1)); | ||||
|             if (south == mSelection.end()) | ||||
|             { | ||||
|                 for(int i = 1; i < (textureSizeToLandSizeModifier + 1); i++) | ||||
|                 { | ||||
|                     float drawPreviousX = CSMWorld::CellCoordinates::textureGlobalToWorldCoords(x) + (i - 1) *(ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1)); | ||||
|                     float drawCurrentX = CSMWorld::CellCoordinates::textureGlobalToWorldCoords(x) + i * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1)); | ||||
|                     vertices->push_back(osg::Vec3f(drawPreviousX + nudgeOffset, CSMWorld::CellCoordinates::textureGlobalToWorldCoords(y) - nudgeOffset, calculateLandHeight(x1+(i-1), y1)+2)); | ||||
|                     vertices->push_back(osg::Vec3f(drawCurrentX + nudgeOffset, CSMWorld::CellCoordinates::textureGlobalToWorldCoords(y) - nudgeOffset, calculateLandHeight(x1+i, y1)+2)); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             const auto east = std::find(mSelection.begin(), mSelection.end(), std::make_pair(x + 1, y)); | ||||
|             if (east == mSelection.end()) | ||||
|             { | ||||
|                 for(int i = 1; i < (textureSizeToLandSizeModifier + 1); i++) | ||||
|                 { | ||||
|                     float drawPreviousY = CSMWorld::CellCoordinates::textureGlobalToWorldCoords(y) + (i - 1) * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1)); | ||||
|                     float drawCurrentY = CSMWorld::CellCoordinates::textureGlobalToWorldCoords(y) + i * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1)); | ||||
|                     vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::textureGlobalToWorldCoords(x + 1) + nudgeOffset, drawPreviousY - nudgeOffset, calculateLandHeight(x2, y1+(i-1))+2)); | ||||
|                     vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::textureGlobalToWorldCoords(x + 1) + nudgeOffset, drawCurrentY - nudgeOffset, calculateLandHeight(x2, y1+i)+2)); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             const auto west = std::find(mSelection.begin(), mSelection.end(), std::make_pair(x - 1, y)); | ||||
|             if (west == mSelection.end()) | ||||
|             { | ||||
|                 for(int i = 1; i < (textureSizeToLandSizeModifier + 1); i++) | ||||
|                 { | ||||
|                     float drawPreviousY = CSMWorld::CellCoordinates::textureGlobalToWorldCoords(y) + (i - 1) * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1)); | ||||
|                     float drawCurrentY = CSMWorld::CellCoordinates::textureGlobalToWorldCoords(y) + i * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1)); | ||||
|                     vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::textureGlobalToWorldCoords(x) + nudgeOffset, drawPreviousY - nudgeOffset, calculateLandHeight(x1, y1+(i-1))+2)); | ||||
|                     vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::textureGlobalToWorldCoords(x) + nudgeOffset, drawCurrentY - nudgeOffset, calculateLandHeight(x1, y1+i)+2)); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| int CSVRender::TerrainSelection::calculateLandHeight(int x, int y) // global vertex coordinates
 | ||||
| { | ||||
|     int cellX = std::floor((1.0f*x / (ESM::Land::LAND_SIZE - 1))); | ||||
|     int cellY = std::floor((1.0f*y / (ESM::Land::LAND_SIZE - 1))); | ||||
|     int localX = x - cellX * (ESM::Land::LAND_SIZE - 1); | ||||
|     int localY = y - cellY * (ESM::Land::LAND_SIZE - 1); | ||||
| 
 | ||||
|     std::string cellId = CSMWorld::CellCoordinates::generateId(cellX, cellY); | ||||
| 
 | ||||
|     CSMDoc::Document& document = mWorldspaceWidget->getDocument(); | ||||
|     CSMWorld::IdTable& landTable = dynamic_cast<CSMWorld::IdTable&> ( | ||||
|         *document.getData().getTableModel (CSMWorld::UniversalId::Type_Land)); | ||||
|     int landshapeColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandHeightsIndex); | ||||
|     const CSMWorld::LandHeightsColumn::DataType mPointer = landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value<CSMWorld::LandHeightsColumn::DataType>(); | ||||
| 
 | ||||
|     return mPointer[localY*ESM::Land::LAND_SIZE + localX]; | ||||
| } | ||||
							
								
								
									
										70
									
								
								apps/opencs/view/render/terrainselection.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								apps/opencs/view/render/terrainselection.hpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,70 @@ | |||
| #ifndef CSV_RENDER_TERRAINSELECTION_H | ||||
| #define CSV_RENDER_TERRAINSELECTION_H | ||||
| 
 | ||||
| #include <utility> | ||||
| #include <vector> | ||||
| 
 | ||||
| #include <osg/Vec3d> | ||||
| #include <osg/ref_ptr> | ||||
| #include <osg/PositionAttitudeTransform> | ||||
| 
 | ||||
| #include <components/esm/loadland.hpp> | ||||
| #include "../../model/world/cellcoordinates.hpp" | ||||
| 
 | ||||
| namespace osg | ||||
| { | ||||
|     class Group; | ||||
| } | ||||
| 
 | ||||
| namespace CSVRender | ||||
| { | ||||
|     struct WorldspaceHitResult; | ||||
|     class WorldspaceWidget; | ||||
| 
 | ||||
|     enum class TerrainSelectionType | ||||
|     { | ||||
|         Texture, | ||||
|         Shape | ||||
|     }; | ||||
| 
 | ||||
|     /// \brief Class handling the terrain selection data and rendering
 | ||||
|     class TerrainSelection | ||||
|     { | ||||
|         public: | ||||
| 
 | ||||
|             TerrainSelection(osg::Group* parentNode, WorldspaceWidget *worldspaceWidget, TerrainSelectionType type); | ||||
|             ~TerrainSelection(); | ||||
| 
 | ||||
|             void onlySelect(const std::vector<std::pair<int, int>> &localPositions); | ||||
|             void addSelect(const std::pair<int, int> &localPos); | ||||
|             void toggleSelect(const std::vector<std::pair<int, int>> &localPositions, bool toggleInProgress); | ||||
| 
 | ||||
|             void activate(); | ||||
|             void deactivate(); | ||||
| 
 | ||||
|             std::vector<std::pair<int, int>> getTerrainSelection() const; | ||||
| 
 | ||||
|         protected: | ||||
| 
 | ||||
|             void update(); | ||||
| 
 | ||||
|             void drawShapeSelection(const osg::ref_ptr<osg::Vec3Array> vertices); | ||||
|             void drawTextureSelection(const osg::ref_ptr<osg::Vec3Array> vertices); | ||||
| 
 | ||||
|             int calculateLandHeight(int x, int y); | ||||
| 
 | ||||
|         private: | ||||
| 
 | ||||
|             osg::Group* mParentNode; | ||||
|             WorldspaceWidget *mWorldspaceWidget; | ||||
|             osg::ref_ptr<osg::PositionAttitudeTransform> mBaseNode; | ||||
|             osg::ref_ptr<osg::Geometry> mGeometry; | ||||
|             osg::ref_ptr<osg::Group> mSelectionNode; | ||||
|             std::vector<std::pair<int, int>> mSelection; // Global terrain selection coordinate in either vertex or texture units
 | ||||
|             std::vector<std::pair<int, int>> mTemporarySelection; // Used during toggle to compare the most recent drag operation
 | ||||
|             bool mDraggedOperationFlag; //true during drag operation, false when click-operation
 | ||||
|             TerrainSelectionType mSelectionType; | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
|  | @ -10,6 +10,8 @@ | |||
| #include <QDragEnterEvent> | ||||
| #include <QDrag> | ||||
| 
 | ||||
| #include <osg/Group> | ||||
| 
 | ||||
| #include <components/esm/loadland.hpp> | ||||
| 
 | ||||
| #include "../widget/modebutton.hpp" | ||||
|  | @ -36,12 +38,15 @@ | |||
| #include "object.hpp" // Something small needed regarding pointers from here ()
 | ||||
| #include "worldspacewidget.hpp" | ||||
| 
 | ||||
| CSVRender::TerrainTextureMode::TerrainTextureMode (WorldspaceWidget *worldspaceWidget, QWidget *parent) | ||||
| CSVRender::TerrainTextureMode::TerrainTextureMode (WorldspaceWidget *worldspaceWidget, osg::Group* parentNode, QWidget *parent) | ||||
| : EditMode (worldspaceWidget, QIcon {":scenetoolbar/editing-terrain-texture"}, Mask_Terrain | Mask_Reference, "Terrain texture editing", parent), | ||||
|     mBrushTexture("L0#0"), | ||||
|     mBrushSize(0), | ||||
|     mBrushShape(0), | ||||
|     mTextureBrushScenetool(0) | ||||
|     mTextureBrushScenetool(0), | ||||
|     mDragMode(InteractionType_None), | ||||
|     mParentNode(parentNode), | ||||
|     mIsEditing(false) | ||||
| { | ||||
| } | ||||
| 
 | ||||
|  | @ -62,6 +67,11 @@ void CSVRender::TerrainTextureMode::activate(CSVWidget::SceneToolbar* toolbar) | |||
|         connect(this, SIGNAL(passBrushTexture(std::string)), mTextureBrushScenetool, SLOT(updateBrushHistory(std::string))); | ||||
|     } | ||||
| 
 | ||||
|     if (!mTerrainTextureSelection) | ||||
|     { | ||||
|         mTerrainTextureSelection.reset(new TerrainSelection(mParentNode, &getWorldspaceWidget(), TerrainSelectionType::Texture)); | ||||
|     } | ||||
| 
 | ||||
|     EditMode::activate(toolbar); | ||||
|     toolbar->addTool (mTextureBrushScenetool); | ||||
| } | ||||
|  | @ -74,6 +84,12 @@ void CSVRender::TerrainTextureMode::deactivate(CSVWidget::SceneToolbar* toolbar) | |||
|         delete mTextureBrushScenetool; | ||||
|         mTextureBrushScenetool = 0; | ||||
|     } | ||||
| 
 | ||||
|     if (mTerrainTextureSelection) | ||||
|     { | ||||
|         mTerrainTextureSelection.reset(); | ||||
|     } | ||||
| 
 | ||||
|     EditMode::deactivate(toolbar); | ||||
| } | ||||
| 
 | ||||
|  | @ -95,10 +111,10 @@ void CSVRender::TerrainTextureMode::primaryEditPressed(const WorldspaceHitResult | |||
|     CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures(); | ||||
|     int index = landtexturesCollection.searchId(mBrushTexture); | ||||
| 
 | ||||
|     if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted() && hit.hit == true) | ||||
|     if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted() && hit.hit && hit.tag == 0) | ||||
|     { | ||||
|         undoStack.beginMacro ("Edit texture records"); | ||||
|         if(allowLandTextureEditing(mCellId)==true) | ||||
|         if(allowLandTextureEditing(mCellId)) | ||||
|         { | ||||
|             undoStack.push (new CSMWorld::TouchLandCommand(landTable, ltexTable, mCellId)); | ||||
|             editTerrainTextureGrid(hit); | ||||
|  | @ -109,10 +125,18 @@ void CSVRender::TerrainTextureMode::primaryEditPressed(const WorldspaceHitResult | |||
| 
 | ||||
| void CSVRender::TerrainTextureMode::primarySelectPressed(const WorldspaceHitResult& hit) | ||||
| { | ||||
|     if(hit.hit && hit.tag == 0) | ||||
|     { | ||||
|         selectTerrainTextures(CSMWorld::CellCoordinates::toTextureCoords(hit.worldPos), 0, false); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void CSVRender::TerrainTextureMode::secondarySelectPressed(const WorldspaceHitResult& hit) | ||||
| { | ||||
|     if(hit.hit && hit.tag == 0) | ||||
|     { | ||||
|         selectTerrainTextures(CSMWorld::CellCoordinates::toTextureCoords(hit.worldPos), 1, false); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool CSVRender::TerrainTextureMode::primaryEditStartDrag (const QPoint& pos) | ||||
|  | @ -129,13 +153,16 @@ bool CSVRender::TerrainTextureMode::primaryEditStartDrag (const QPoint& pos) | |||
| 
 | ||||
|     QUndoStack& undoStack = document.getUndoStack(); | ||||
| 
 | ||||
|     mDragMode = InteractionType_PrimaryEdit; | ||||
| 
 | ||||
|     CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures(); | ||||
|     int index = landtexturesCollection.searchId(mBrushTexture); | ||||
| 
 | ||||
|     if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted()) | ||||
|     if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted() && hit.hit && hit.tag == 0) | ||||
|     { | ||||
|         undoStack.beginMacro ("Edit texture records"); | ||||
|         if(allowLandTextureEditing(mCellId)==true && hit.hit == true) | ||||
|         mIsEditing = true; | ||||
|         if(allowLandTextureEditing(mCellId)) | ||||
|         { | ||||
|             undoStack.push (new CSMWorld::TouchLandCommand(landTable, ltexTable, mCellId)); | ||||
|             editTerrainTextureGrid(hit); | ||||
|  | @ -152,47 +179,88 @@ bool CSVRender::TerrainTextureMode::secondaryEditStartDrag (const QPoint& pos) | |||
| 
 | ||||
| bool CSVRender::TerrainTextureMode::primarySelectStartDrag (const QPoint& pos) | ||||
| { | ||||
|     WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask()); | ||||
|     mDragMode = InteractionType_PrimarySelect; | ||||
|     if (!hit.hit || hit.tag != 0) | ||||
|     { | ||||
|         mDragMode = InteractionType_None; | ||||
|         return false; | ||||
|     } | ||||
|     selectTerrainTextures(CSMWorld::CellCoordinates::toTextureCoords(hit.worldPos), 0, true); | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| bool CSVRender::TerrainTextureMode::secondarySelectStartDrag (const QPoint& pos) | ||||
| { | ||||
|     WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask()); | ||||
|     mDragMode = InteractionType_SecondarySelect; | ||||
|     if (!hit.hit || hit.tag != 0) | ||||
|     { | ||||
|         mDragMode = InteractionType_None; | ||||
|         return false; | ||||
|     } | ||||
|     selectTerrainTextures(CSMWorld::CellCoordinates::toTextureCoords(hit.worldPos), 1, true); | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| void CSVRender::TerrainTextureMode::drag (const QPoint& pos, int diffX, int diffY, double speedFactor) | ||||
| { | ||||
|     WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask()); | ||||
|     CSMDoc::Document& document = getWorldspaceWidget().getDocument(); | ||||
| 
 | ||||
|     CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures(); | ||||
|     int index = landtexturesCollection.searchId(mBrushTexture); | ||||
| 
 | ||||
|     if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted() && hit.hit == true) | ||||
|     if (mDragMode == InteractionType_PrimaryEdit) | ||||
|     { | ||||
|         editTerrainTextureGrid(hit); | ||||
|         WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask()); | ||||
|         std::string cellId = getWorldspaceWidget().getCellId (hit.worldPos); | ||||
|         CSMDoc::Document& document = getWorldspaceWidget().getDocument(); | ||||
| 
 | ||||
|         CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures(); | ||||
|         int index = landtexturesCollection.searchId(mBrushTexture); | ||||
| 
 | ||||
|         if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted() && hit.hit && hit.tag == 0) | ||||
|         { | ||||
|             editTerrainTextureGrid(hit); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if (mDragMode == InteractionType_PrimarySelect) | ||||
|     { | ||||
|         WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask()); | ||||
|         if (hit.hit && hit.tag == 0) selectTerrainTextures(CSMWorld::CellCoordinates::toTextureCoords(hit.worldPos), 0, true); | ||||
|     } | ||||
| 
 | ||||
|     if (mDragMode == InteractionType_SecondarySelect) | ||||
|     { | ||||
|         WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask()); | ||||
|         if (hit.hit && hit.tag == 0) selectTerrainTextures(CSMWorld::CellCoordinates::toTextureCoords(hit.worldPos), 1, true); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void CSVRender::TerrainTextureMode::dragCompleted(const QPoint& pos) { | ||||
|     CSMDoc::Document& document = getWorldspaceWidget().getDocument(); | ||||
|     QUndoStack& undoStack = document.getUndoStack(); | ||||
| 
 | ||||
|     CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures(); | ||||
|     int index = landtexturesCollection.searchId(mBrushTexture); | ||||
| 
 | ||||
|     if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted()) | ||||
| void CSVRender::TerrainTextureMode::dragCompleted(const QPoint& pos) | ||||
| { | ||||
|     if (mDragMode == InteractionType_PrimaryEdit && mIsEditing) | ||||
|     { | ||||
|          undoStack.endMacro(); | ||||
|         CSMDoc::Document& document = getWorldspaceWidget().getDocument(); | ||||
|         QUndoStack& undoStack = document.getUndoStack(); | ||||
| 
 | ||||
|         CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures(); | ||||
|         int index = landtexturesCollection.searchId(mBrushTexture); | ||||
| 
 | ||||
|         if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted()) | ||||
|         { | ||||
|              undoStack.endMacro(); | ||||
|              mIsEditing = false; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void CSVRender::TerrainTextureMode::dragAborted() { | ||||
| void CSVRender::TerrainTextureMode::dragAborted() | ||||
| { | ||||
| } | ||||
| 
 | ||||
| void CSVRender::TerrainTextureMode::dragWheel (int diff, double speedFactor) {} | ||||
| void CSVRender::TerrainTextureMode::dragWheel (int diff, double speedFactor) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| void CSVRender::TerrainTextureMode::handleDropEvent (QDropEvent *event) { | ||||
| void CSVRender::TerrainTextureMode::handleDropEvent (QDropEvent *event) | ||||
| { | ||||
|   const CSMWorld::TableMimeData* mime = dynamic_cast<const CSMWorld::TableMimeData*> (event->mimeData()); | ||||
| 
 | ||||
|   if (!mime) // May happen when non-records (e.g. plain text) are dragged and dropped
 | ||||
|  | @ -228,7 +296,7 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe | |||
|         *document.getData().getTableModel (CSMWorld::UniversalId::Type_Land)); | ||||
| 
 | ||||
|     mCellId = getWorldspaceWidget().getCellId (hit.worldPos); | ||||
|     if(allowLandTextureEditing(mCellId)==true) {} | ||||
|     if(allowLandTextureEditing(mCellId)) {} | ||||
| 
 | ||||
|     std::pair<CSMWorld::CellCoordinates, bool> cellCoordinates_pair = CSMWorld::CellCoordinates::fromId (mCellId); | ||||
| 
 | ||||
|  | @ -236,8 +304,8 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe | |||
|     int cellY = cellCoordinates_pair.first.getY(); | ||||
| 
 | ||||
|     // The coordinates of hit in mCellId
 | ||||
|     int xHitInCell (float(((hit.worldPos.x() - (cellX* cellSize)) * landTextureSize / cellSize) - 0.5)); | ||||
|     int yHitInCell (float(((hit.worldPos.y() - (cellY* cellSize)) * landTextureSize / cellSize) + 0.5)); | ||||
|     int xHitInCell (float(((hit.worldPos.x() - (cellX* cellSize)) * landTextureSize / cellSize) - 0.25)); | ||||
|     int yHitInCell (float(((hit.worldPos.y() - (cellY* cellSize)) * landTextureSize / cellSize) + 0.25)); | ||||
|     if (xHitInCell < 0) | ||||
|     { | ||||
|         xHitInCell = xHitInCell + landTextureSize; | ||||
|  | @ -249,8 +317,8 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe | |||
|         cellY = cellY + 1; | ||||
|     } | ||||
| 
 | ||||
|     mCellId = "#" + std::to_string(cellX) + " " + std::to_string(cellY); | ||||
|     if(allowLandTextureEditing(mCellId)==true) {} | ||||
|     mCellId = CSMWorld::CellCoordinates::generateId(cellX, cellY); | ||||
|     if(allowLandTextureEditing(mCellId)) {} | ||||
| 
 | ||||
|     std::string iteratedCellId; | ||||
| 
 | ||||
|  | @ -266,13 +334,13 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe | |||
| 
 | ||||
|     if (mBrushShape == 0) | ||||
|     { | ||||
|         CSMWorld::LandTexturesColumn::DataType mPointer = landTable.data(landTable.getModelIndex(mCellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>(); | ||||
|         CSMWorld::LandTexturesColumn::DataType mNew(mPointer); | ||||
|         CSMWorld::LandTexturesColumn::DataType newTerrainPointer = landTable.data(landTable.getModelIndex(mCellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>(); | ||||
|         CSMWorld::LandTexturesColumn::DataType newTerrain(newTerrainPointer); | ||||
| 
 | ||||
|         if(allowLandTextureEditing(mCellId)==true) | ||||
|         if(allowLandTextureEditing(mCellId)) | ||||
|         { | ||||
|             mNew[yHitInCell*landTextureSize+xHitInCell] = brushInt; | ||||
|             pushEditToCommand(mNew, document, landTable, mCellId); | ||||
|             newTerrain[yHitInCell*landTextureSize+xHitInCell] = brushInt; | ||||
|             pushEditToCommand(newTerrain, document, landTable, mCellId); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | @ -292,19 +360,19 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe | |||
|         { | ||||
|             for(int j_cell = upperLeftCellY; j_cell <= lowerrightCellY; j_cell++) | ||||
|             { | ||||
|                 iteratedCellId = "#" + std::to_string(i_cell) + " " + std::to_string(j_cell); | ||||
|                 if(allowLandTextureEditing(iteratedCellId)==true) | ||||
|                 iteratedCellId = CSMWorld::CellCoordinates::generateId(i_cell, j_cell); | ||||
|                 if(allowLandTextureEditing(iteratedCellId)) | ||||
|                 { | ||||
|                     CSMWorld::LandTexturesColumn::DataType mPointer = landTable.data(landTable.getModelIndex(iteratedCellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>(); | ||||
|                     CSMWorld::LandTexturesColumn::DataType mNew(mPointer); | ||||
|                     CSMWorld::LandTexturesColumn::DataType newTerrainPointer = landTable.data(landTable.getModelIndex(iteratedCellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>(); | ||||
|                     CSMWorld::LandTexturesColumn::DataType newTerrain(newTerrainPointer); | ||||
|                     for(int i = 0; i < landTextureSize; i++) | ||||
|                     { | ||||
|                         for(int j = 0; j < landTextureSize; j++) | ||||
|                         { | ||||
| 
 | ||||
|                            if (i_cell == cellX && j_cell == cellY && abs(i-xHitInCell) < r && abs(j-yHitInCell) < r) | ||||
|                             if (i_cell == cellX && j_cell == cellY && abs(i-xHitInCell) < r && abs(j-yHitInCell) < r) | ||||
|                             { | ||||
|                                 mNew[j*landTextureSize+i] = brushInt; | ||||
|                                 newTerrain[j*landTextureSize+i] = brushInt; | ||||
|                             } | ||||
|                             else | ||||
|                             { | ||||
|  | @ -316,11 +384,11 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe | |||
|                                 if (j_cell > cellY) distanceY = -yHitInCell + landTextureSize * abs(j_cell-cellY) + j; | ||||
|                                 if (i_cell == cellX) distanceX = abs(i-xHitInCell); | ||||
|                                 if (j_cell == cellY) distanceY = abs(j-yHitInCell); | ||||
|                                 if (distanceX < r && distanceY < r) mNew[j*landTextureSize+i] = brushInt; | ||||
|                                 if (distanceX < r && distanceY < r) newTerrain[j*landTextureSize+i] = brushInt; | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                     pushEditToCommand(mNew, document, landTable, iteratedCellId); | ||||
|                     pushEditToCommand(newTerrain, document, landTable, iteratedCellId); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | @ -342,11 +410,11 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe | |||
|         { | ||||
|             for(int j_cell = upperLeftCellY; j_cell <= lowerrightCellY; j_cell++) | ||||
|             { | ||||
|                 iteratedCellId = "#" + std::to_string(i_cell) + " " + std::to_string(j_cell); | ||||
|                 if(allowLandTextureEditing(iteratedCellId)==true) | ||||
|                 iteratedCellId = CSMWorld::CellCoordinates::generateId(i_cell, j_cell); | ||||
|                 if(allowLandTextureEditing(iteratedCellId)) | ||||
|                 { | ||||
|                     CSMWorld::LandTexturesColumn::DataType mPointer = landTable.data(landTable.getModelIndex(iteratedCellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>(); | ||||
|                     CSMWorld::LandTexturesColumn::DataType mNew(mPointer); | ||||
|                     CSMWorld::LandTexturesColumn::DataType newTerrainPointer = landTable.data(landTable.getModelIndex(iteratedCellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>(); | ||||
|                     CSMWorld::LandTexturesColumn::DataType newTerrain(newTerrainPointer); | ||||
|                     for(int i = 0; i < landTextureSize; i++) | ||||
|                     { | ||||
|                         for(int j = 0; j < landTextureSize; j++) | ||||
|  | @ -363,7 +431,7 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe | |||
|                                 if (i_cell == cellX) distanceX = abs(i-xHitInCell); | ||||
|                                 if (j_cell == cellY) distanceY = abs(j-yHitInCell); | ||||
|                                 distance = std::round(sqrt(pow(distanceX, 2)+pow(distanceY, 2))); | ||||
|                                 if (distance < rf) mNew[j*landTextureSize+i] = brushInt; | ||||
|                                 if (distance < rf) newTerrain[j*landTextureSize+i] = brushInt; | ||||
|                             } | ||||
|                             else | ||||
|                             { | ||||
|  | @ -376,11 +444,11 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe | |||
|                                 if (i_cell == cellX) distanceX = abs(i-xHitInCell); | ||||
|                                 if (j_cell == cellY) distanceY = abs(j-yHitInCell); | ||||
|                                 distance = std::round(sqrt(pow(distanceX, 2)+pow(distanceY, 2))); | ||||
|                                 if (distance < rf) mNew[j*landTextureSize+i] = brushInt; | ||||
|                                 if (distance < rf) newTerrain[j*landTextureSize+i] = brushInt; | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                     pushEditToCommand(mNew, document, landTable, iteratedCellId); | ||||
|                     pushEditToCommand(newTerrain, document, landTable, iteratedCellId); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | @ -388,9 +456,115 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe | |||
| 
 | ||||
|     if (mBrushShape == 3) | ||||
|     { | ||||
|       // Not implemented
 | ||||
|         CSMWorld::LandTexturesColumn::DataType newTerrainPointer = landTable.data(landTable.getModelIndex(mCellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>(); | ||||
|         CSMWorld::LandTexturesColumn::DataType newTerrain(newTerrainPointer); | ||||
| 
 | ||||
|         if(allowLandTextureEditing(mCellId) && !mCustomBrushShape.empty()) | ||||
|         { | ||||
|             for(auto const& value: mCustomBrushShape) | ||||
|             { | ||||
|                 if(yHitInCell + value.second >= 0 && yHitInCell + value.second <= 15 && xHitInCell + value.first >= 0 && xHitInCell + value.first <= 15) | ||||
|                 { | ||||
|                     newTerrain[(yHitInCell+value.second)*landTextureSize+xHitInCell+value.first] = brushInt; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     int cellXDifference = std::floor(1.0f*(xHitInCell + value.first)/landTextureSize); | ||||
|                     int cellYDifference = std::floor(1.0f*(yHitInCell + value.second)/landTextureSize); | ||||
|                     int xInOtherCell = xHitInCell + value.first - cellXDifference * landTextureSize; | ||||
|                     int yInOtherCell = yHitInCell + value.second - cellYDifference * landTextureSize; | ||||
| 
 | ||||
|                     std::string cellId = CSMWorld::CellCoordinates::generateId(cellX+cellXDifference, cellY+cellYDifference); | ||||
|                     if (allowLandTextureEditing(cellId)) | ||||
|                     { | ||||
|                         CSMWorld::LandTexturesColumn::DataType newTerrainPointerOtherCell = landTable.data(landTable.getModelIndex(cellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>(); | ||||
|                         CSMWorld::LandTexturesColumn::DataType newTerrainOtherCell(newTerrainPointerOtherCell); | ||||
|                         newTerrainOtherCell[yInOtherCell*landTextureSize+xInOtherCell] = brushInt; | ||||
|                         pushEditToCommand(newTerrainOtherCell, document, landTable, cellId); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             pushEditToCommand(newTerrain, document, landTable, mCellId); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool CSVRender::TerrainTextureMode::isInCellSelection(int globalSelectionX, int globalSelectionY) | ||||
| { | ||||
|     if (CSVRender::PagedWorldspaceWidget *paged = dynamic_cast<CSVRender::PagedWorldspaceWidget *> (&getWorldspaceWidget())) | ||||
|     { | ||||
|         std::pair<int, int> textureCoords = std::make_pair(globalSelectionX, globalSelectionY); | ||||
|         std::string cellId = CSMWorld::CellCoordinates::textureGlobalToCellId(textureCoords); | ||||
|         return paged->getCellSelection().has(CSMWorld::CellCoordinates::fromId(cellId).first); | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void CSVRender::TerrainTextureMode::selectTerrainTextures(const std::pair<int, int>& texCoords, unsigned char selectMode, bool dragOperation) | ||||
| { | ||||
|     int r = mBrushSize / 2; | ||||
|     std::vector<std::pair<int, int>> selections; | ||||
| 
 | ||||
|     if (mBrushShape == 0) | ||||
|     { | ||||
|         if (isInCellSelection(texCoords.first, texCoords.second)) selections.emplace_back(texCoords); | ||||
|     } | ||||
| 
 | ||||
|     if (mBrushShape == 1) | ||||
|     { | ||||
|         for (int i = -r; i <= r; i++) | ||||
|         { | ||||
|             for (int j = -r; j <= r; j++) | ||||
|             { | ||||
|                 int x = i + texCoords.first; | ||||
|                 int y = j + texCoords.second; | ||||
|                 if (isInCellSelection(x, y)) | ||||
|                 { | ||||
|                     selections.emplace_back(x, y); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if (mBrushShape == 2) | ||||
|     { | ||||
|         for (int i = -r; i <= r; i++) | ||||
|         { | ||||
|             for (int j = -r; j <= r; j++) | ||||
|             { | ||||
|                 osg::Vec2f coords(i,j); | ||||
|                 if (std::round(coords.length()) < r) | ||||
|                 { | ||||
|                     int x = i + texCoords.first; | ||||
|                     int y = j + texCoords.second; | ||||
|                     if (isInCellSelection(x, y)) | ||||
|                     { | ||||
|                         selections.emplace_back(x, y); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if (mBrushShape == 3) | ||||
|     { | ||||
|         if(!mCustomBrushShape.empty()) | ||||
|         { | ||||
|             for(auto const& value: mCustomBrushShape) | ||||
|             { | ||||
|                 int x = texCoords.first + value.first; | ||||
|                 int y = texCoords.second + value.second; | ||||
|                 if (isInCellSelection(x, y)) | ||||
|                 { | ||||
|                     selections.emplace_back(x, y); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if(selectMode == 0) mTerrainTextureSelection->onlySelect(selections); | ||||
|     if(selectMode == 1) mTerrainTextureSelection->toggleSelect(selections, dragOperation); | ||||
| } | ||||
| 
 | ||||
| void CSVRender::TerrainTextureMode::pushEditToCommand(CSMWorld::LandTexturesColumn::DataType& newLandGrid, CSMDoc::Document& document, | ||||
|  | @ -405,8 +579,8 @@ void CSVRender::TerrainTextureMode::pushEditToCommand(CSMWorld::LandTexturesColu | |||
|     QModelIndex index(landTable.getModelIndex (cellId, landTable.findColumnIndex (CSMWorld::Columns::ColumnId_LandTexturesIndex))); | ||||
| 
 | ||||
|     QUndoStack& undoStack = document.getUndoStack(); | ||||
|     undoStack.push (new CSMWorld::TouchLandCommand(landTable, ltexTable, cellId)); | ||||
|     undoStack.push (new CSMWorld::ModifyCommand(landTable, index, changedLand)); | ||||
|     undoStack.push (new CSMWorld::TouchLandCommand(landTable, ltexTable, cellId)); | ||||
| } | ||||
| 
 | ||||
| void CSVRender::TerrainTextureMode::createTexture(std::string textureFileName) | ||||
|  | @ -422,13 +596,15 @@ void CSVRender::TerrainTextureMode::createTexture(std::string textureFileName) | |||
| 
 | ||||
|     int counter=0; | ||||
|     bool freeIndexFound = false; | ||||
|     do { | ||||
|     do | ||||
|     { | ||||
|         const size_t maxCounter = std::numeric_limits<uint16_t>::max() - 1; | ||||
|         try | ||||
|         { | ||||
|             newId = CSMWorld::LandTexture::createUniqueRecordId(0, counter); | ||||
|             if (ltexTable.getRecord(newId).isDeleted() == 0) counter = (counter + 1) % maxCounter; | ||||
|         } catch (const std::exception& e) | ||||
|         } | ||||
|         catch (const std::exception& e) | ||||
|         { | ||||
|             newId = CSMWorld::LandTexture::createUniqueRecordId(0, counter); | ||||
|             freeIndexFound = true; | ||||
|  | @ -527,7 +703,8 @@ bool CSVRender::TerrainTextureMode::allowLandTextureEditing(std::string cellId) | |||
|     return true; | ||||
| } | ||||
| 
 | ||||
| void CSVRender::TerrainTextureMode::dragMoveEvent (QDragMoveEvent *event) { | ||||
| void CSVRender::TerrainTextureMode::dragMoveEvent (QDragMoveEvent *event) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| void CSVRender::TerrainTextureMode::setBrushSize(int brushSize) | ||||
|  | @ -538,6 +715,28 @@ void CSVRender::TerrainTextureMode::setBrushSize(int brushSize) | |||
| void CSVRender::TerrainTextureMode::setBrushShape(int brushShape) | ||||
| { | ||||
|     mBrushShape = brushShape; | ||||
| 
 | ||||
|     //Set custom brush shape
 | ||||
|     if (mBrushShape == 3 && !mTerrainTextureSelection->getTerrainSelection().empty()) | ||||
|     { | ||||
|         auto terrainSelection = mTerrainTextureSelection->getTerrainSelection(); | ||||
|         int selectionCenterX = 0; | ||||
|         int selectionCenterY = 0; | ||||
|         int selectionAmount = 0; | ||||
| 
 | ||||
|         for(auto const& value: terrainSelection) | ||||
|         { | ||||
|             selectionCenterX += value.first; | ||||
|             selectionCenterY += value.second; | ||||
|             ++selectionAmount; | ||||
|         } | ||||
|         selectionCenterX /= selectionAmount; | ||||
|         selectionCenterY /= selectionAmount; | ||||
| 
 | ||||
|         mCustomBrushShape.clear(); | ||||
|         for (auto const& value: terrainSelection) | ||||
|             mCustomBrushShape.emplace_back(value.first - selectionCenterX, value.second - selectionCenterY); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void CSVRender::TerrainTextureMode::setBrushTexture(std::string brushTexture) | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ | |||
| #include "editmode.hpp" | ||||
| 
 | ||||
| #include <string> | ||||
| #include <memory> | ||||
| 
 | ||||
| #include <QWidget> | ||||
| #include <QEvent> | ||||
|  | @ -18,6 +19,13 @@ | |||
| #include "../../model/world/landtexture.hpp" | ||||
| #endif | ||||
| 
 | ||||
| #include "terrainselection.hpp" | ||||
| 
 | ||||
| namespace osg | ||||
| { | ||||
|     class Group; | ||||
| } | ||||
| 
 | ||||
| namespace CSVWidget | ||||
| { | ||||
|     class SceneToolTextureBrush; | ||||
|  | @ -25,15 +33,23 @@ namespace CSVWidget | |||
| 
 | ||||
| namespace CSVRender | ||||
| { | ||||
| 
 | ||||
|     class TerrainTextureMode : public EditMode | ||||
|     { | ||||
|         Q_OBJECT | ||||
| 
 | ||||
|         public: | ||||
| 
 | ||||
|             enum InteractionType | ||||
|             { | ||||
|                 InteractionType_PrimaryEdit, | ||||
|                 InteractionType_PrimarySelect, | ||||
|                 InteractionType_SecondaryEdit, | ||||
|                 InteractionType_SecondarySelect, | ||||
|                 InteractionType_None | ||||
|             }; | ||||
| 
 | ||||
|             /// \brief Editmode for terrain texture grid
 | ||||
|             TerrainTextureMode(WorldspaceWidget*, QWidget* parent = nullptr); | ||||
|             TerrainTextureMode(WorldspaceWidget*, osg::Group* parentNode, QWidget* parent = nullptr); | ||||
| 
 | ||||
|             void primaryOpenPressed (const WorldspaceHitResult& hit); | ||||
| 
 | ||||
|  | @ -68,6 +84,12 @@ namespace CSVRender | |||
|             /// \brief Handle brush mechanics, maths regarding worldspace hit etc.
 | ||||
|             void editTerrainTextureGrid (const WorldspaceHitResult& hit); | ||||
| 
 | ||||
|             /// \brief Check if global selection coordinate belongs to cell in view
 | ||||
|             bool isInCellSelection(int globalSelectionX, int globalSelectionY); | ||||
| 
 | ||||
|             /// \brief Handle brush mechanics for texture selection
 | ||||
|             void selectTerrainTextures (const std::pair<int, int>& texCoords, unsigned char selectMode, bool dragOperation); | ||||
| 
 | ||||
|             /// \brief Push texture edits to command macro
 | ||||
|             void pushEditToCommand (CSMWorld::LandTexturesColumn::DataType& newLandGrid, CSMDoc::Document& document, | ||||
|                 CSMWorld::IdTable& landTable, std::string cellId); | ||||
|  | @ -83,7 +105,12 @@ namespace CSVRender | |||
|             std::string mBrushTexture; | ||||
|             int mBrushSize; | ||||
|             int mBrushShape; | ||||
|             std::vector<std::pair<int, int>> mCustomBrushShape; | ||||
|             CSVWidget::SceneToolTextureBrush *mTextureBrushScenetool; | ||||
|             int mDragMode; | ||||
|             osg::Group* mParentNode; | ||||
|             bool mIsEditing; | ||||
|             std::unique_ptr<TerrainSelection> mTerrainTextureSelection; | ||||
| 
 | ||||
|             const int cellSize {ESM::Land::REAL_SIZE}; | ||||
|             const int landSize {ESM::Land::LAND_SIZE}; | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue