mirror of
				https://github.com/TES3MP/openmw-tes3mp.git
				synced 2025-10-31 20:26:48 +00:00 
			
		
		
		
	Transient land shape editing
This commit is contained in:
		
							parent
							
								
									262d87846c
								
							
						
					
					
						commit
						16138fc896
					
				
					 19 changed files with 2168 additions and 128 deletions
				
			
		|  | @ -184,6 +184,8 @@ | |||
|     Feature #4784: Launcher: Duplicate Content Lists | ||||
|     Feature #4812: Support NiSwitchNode | ||||
|     Feature #4836: Daytime node switch | ||||
|     Feature #4840: Editor: Transient terrain change support | ||||
|     Feature #????: Editor: Land shape editing, land selection | ||||
|     Feature #4859: Make water reflections more configurable | ||||
|     Feature #4882: Support for NiPalette node | ||||
|     Feature #4887: Add openmw command option to set initial random seed | ||||
|  |  | |||
|  | @ -82,14 +82,14 @@ opencs_units_noqt (view/world | |||
| 
 | ||||
| opencs_units (view/widget | ||||
|     scenetoolbar scenetool scenetoolmode pushbutton scenetooltoggle scenetoolrun modebutton | ||||
|     scenetooltoggle2 scenetooltexturebrush completerpopup coloreditor colorpickerpopup droplineedit | ||||
|     scenetooltoggle2 scenetooltexturebrush scenetoolshapebrush completerpopup coloreditor colorpickerpopup droplineedit | ||||
|     ) | ||||
| 
 | ||||
| opencs_units (view/render | ||||
|     scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget | ||||
|     previewwidget editmode instancemode instanceselectionmode instancemovemode | ||||
|     orbitcameramode pathgridmode selectionmode pathgridselectionmode cameracontroller | ||||
|     cellwater terraintexturemode actor terrainselection | ||||
|     cellwater terraintexturemode actor terrainselection terrainshapemode | ||||
|     ) | ||||
| 
 | ||||
| opencs_units_noqt (view/render | ||||
|  |  | |||
|  | @ -170,7 +170,7 @@ void CSMPrefs::State::declare() | |||
|         "list go to the first/last item"); | ||||
| 
 | ||||
|     declareCategory ("3D Scene Input"); | ||||
|      | ||||
| 
 | ||||
|     declareDouble ("navi-wheel-factor", "Camera Zoom Sensitivity", 8).setRange(-100.0, 100.0); | ||||
|     declareDouble ("s-navi-sensitivity", "Secondary Camera Movement Sensitivity", 50.0).setRange(-1000.0, 1000.0); | ||||
|     declareSeparator(); | ||||
|  | @ -178,7 +178,7 @@ void CSMPrefs::State::declare() | |||
|     declareDouble ("p-navi-free-sensitivity", "Free Camera Sensitivity", 1/650.).setPrecision(5).setRange(0.0, 1.0); | ||||
|     declareBool ("p-navi-free-invert", "Invert Free Camera Mouse Input", false); | ||||
|     declareDouble ("navi-free-lin-speed", "Free Camera Linear Speed", 1000.0).setRange(1.0, 10000.0); | ||||
|     declareDouble ("navi-free-rot-speed", "Free Camera Rotational Speed", 3.14 / 2).setRange(0.001, 6.28);     | ||||
|     declareDouble ("navi-free-rot-speed", "Free Camera Rotational Speed", 3.14 / 2).setRange(0.001, 6.28); | ||||
|     declareDouble ("navi-free-speed-mult", "Free Camera Speed Multiplier (from Modifier)", 8).setRange(0.001, 1000.0); | ||||
|     declareSeparator(); | ||||
| 
 | ||||
|  | @ -248,6 +248,8 @@ void CSMPrefs::State::declare() | |||
|         addValues (landeditOutsideVisibleCell); | ||||
|     declareInt ("texturebrush-maximumsize", "Maximum texture brush size", 50). | ||||
|         setMin (1); | ||||
|     declareInt ("shapebrush-maximumsize", "Maximum texture brush size", 100). | ||||
|         setMin (1); | ||||
|     declareBool ("open-list-view", "Open displays list view", false). | ||||
|         setTooltip ("When opening a reference from the scene view, it will open the" | ||||
|         " instance list view instead of the individual instance record view."); | ||||
|  |  | |||
|  | @ -134,7 +134,7 @@ void CSVRender::Cell::updateLand() | |||
|             else | ||||
|             { | ||||
|                 mTerrain.reset(new Terrain::TerrainGrid(mCellNode, mCellNode, | ||||
|                     mData.getResourceSystem().get(), new TerrainStorage(mData), Mask_Terrain)); | ||||
|                     mData.getResourceSystem().get(), mTerrainStorage, Mask_Terrain)); | ||||
|             } | ||||
| 
 | ||||
|             mTerrain->loadCell(esmLand.mX, esmLand.mY); | ||||
|  | @ -169,6 +169,8 @@ CSVRender::Cell::Cell (CSMWorld::Data& data, osg::Group* rootNode, const std::st | |||
| { | ||||
|     std::pair<CSMWorld::CellCoordinates, bool> result = CSMWorld::CellCoordinates::fromId (id); | ||||
| 
 | ||||
|     mTerrainStorage = new TerrainStorage(mData); | ||||
| 
 | ||||
|     if (result.second) | ||||
|         mCoordinates = result.first; | ||||
| 
 | ||||
|  | @ -347,6 +349,33 @@ bool CSVRender::Cell::referenceAdded (const QModelIndex& parent, int start, int | |||
|     return addObjects (start, end); | ||||
| } | ||||
| 
 | ||||
| void CSVRender::Cell::setAlteredHeight(int inCellX, int inCellY, float height) | ||||
| { | ||||
|     mTerrainStorage->setAlteredHeight(inCellX, inCellY, height); | ||||
|     mUpdateLand = true; | ||||
| } | ||||
| 
 | ||||
| float CSVRender::Cell::getSumOfAlteredAndTrueHeight(int cellX, int cellY, int inCellX, int inCellY) | ||||
| { | ||||
|     return mTerrainStorage->getSumOfAlteredAndTrueHeight(cellX, cellY, inCellX, inCellY); | ||||
| } | ||||
| 
 | ||||
| float* CSVRender::Cell::getAlteredHeights() | ||||
| { | ||||
|     return mTerrainStorage->getAlteredHeights(); | ||||
| } | ||||
| 
 | ||||
| float* CSVRender::Cell::getAlteredHeight(int inCellX, int inCellY) | ||||
| { | ||||
|     return mTerrainStorage->getAlteredHeight(inCellX, inCellY); | ||||
| } | ||||
| 
 | ||||
| void CSVRender::Cell::resetAlteredHeights() | ||||
| { | ||||
|     mTerrainStorage->resetHeights(); | ||||
|     mUpdateLand = true; | ||||
| } | ||||
| 
 | ||||
| void CSVRender::Cell::pathgridModified() | ||||
| { | ||||
|     if (mPathgrid) | ||||
|  |  | |||
|  | @ -9,6 +9,7 @@ | |||
| #include <osg/ref_ptr> | ||||
| 
 | ||||
| #include "../../model/world/cellcoordinates.hpp" | ||||
| #include "terrainstorage.hpp" | ||||
| 
 | ||||
| class QModelIndex; | ||||
| 
 | ||||
|  | @ -58,6 +59,7 @@ namespace CSVRender | |||
|             int mSubMode; | ||||
|             unsigned int mSubModeElementMask; | ||||
|             bool mUpdateLand, mLandDeleted; | ||||
|             TerrainStorage *mTerrainStorage; | ||||
| 
 | ||||
|             /// Ignored if cell does not have an object with the given ID.
 | ||||
|             ///
 | ||||
|  | @ -118,6 +120,16 @@ namespace CSVRender | |||
|             /// this cell?
 | ||||
|             bool referenceAdded (const QModelIndex& parent, int start, int end); | ||||
| 
 | ||||
|             void setAlteredHeight(int inCellX, int inCellY, float height); | ||||
| 
 | ||||
|             float getSumOfAlteredAndTrueHeight(int cellX, int cellY, int inCellX, int inCellY); | ||||
| 
 | ||||
|             float* getAlteredHeights(); | ||||
| 
 | ||||
|             float* getAlteredHeight(int inCellX, int inCellY); | ||||
| 
 | ||||
|             void resetAlteredHeights(); | ||||
| 
 | ||||
|             void pathgridModified(); | ||||
| 
 | ||||
|             void pathgridRemoved(); | ||||
|  |  | |||
|  | @ -25,6 +25,7 @@ | |||
| #include "cameracontroller.hpp" | ||||
| #include "cellarrow.hpp" | ||||
| #include "terraintexturemode.hpp" | ||||
| #include "terrainshapemode.hpp" | ||||
| 
 | ||||
| bool CSVRender::PagedWorldspaceWidget::adjustCells() | ||||
| { | ||||
|  | @ -137,11 +138,9 @@ void CSVRender::PagedWorldspaceWidget::addEditModeSelectorButtons ( | |||
| 
 | ||||
|     /// \todo replace EditMode with suitable subclasses
 | ||||
|     tool->addButton ( | ||||
|         new EditMode (this, QIcon (":placeholder"), Mask_Reference, "Terrain shape editing"), | ||||
|         "terrain-shape"); | ||||
|         new TerrainShapeMode (this, mRootNode, tool), "terrain-shape"); | ||||
|     tool->addButton ( | ||||
|         new TerrainTextureMode (this, mRootNode, tool), | ||||
|         "terrain-texture"); | ||||
|         new TerrainTextureMode (this, mRootNode, tool), "terrain-texture"); | ||||
|     tool->addButton ( | ||||
|         new EditMode (this, QIcon (":placeholder"), Mask_Reference, "Terrain vertex paint editing"), | ||||
|         "terrain-vertex"); | ||||
|  | @ -791,6 +790,46 @@ CSVRender::Cell* CSVRender::PagedWorldspaceWidget::getCell(const osg::Vec3d& poi | |||
|         return 0; | ||||
| } | ||||
| 
 | ||||
| CSVRender::Cell* CSVRender::PagedWorldspaceWidget::getCell(const CSMWorld::CellCoordinates& coords) const | ||||
| { | ||||
|     std::map<CSMWorld::CellCoordinates, Cell*>::const_iterator searchResult = mCells.find(coords); | ||||
|     if (searchResult != mCells.end()) | ||||
|         return searchResult->second; | ||||
|     else | ||||
|         return 0; | ||||
| } | ||||
| 
 | ||||
| void CSVRender::PagedWorldspaceWidget::setCellAlteredHeight(const CSMWorld::CellCoordinates& coords, int inCellX, int inCellY, float height) | ||||
| { | ||||
|     std::map<CSMWorld::CellCoordinates, Cell*>::iterator searchResult = mCells.find(coords); | ||||
|     if (searchResult != mCells.end()) searchResult->second->setAlteredHeight(inCellX, inCellY, height); | ||||
| } | ||||
| 
 | ||||
| float* CSVRender::PagedWorldspaceWidget::getCellAlteredHeights(const CSMWorld::CellCoordinates& coords) | ||||
| { | ||||
|     std::map<CSMWorld::CellCoordinates, Cell*>::iterator searchResult = mCells.find(coords); | ||||
|     if (searchResult != mCells.end()) return searchResult->second->getAlteredHeights(); | ||||
|     return nullptr; | ||||
| } | ||||
| 
 | ||||
| float* CSVRender::PagedWorldspaceWidget::getCellAlteredHeight(const CSMWorld::CellCoordinates& coords, int inCellX, int inCellY) | ||||
| { | ||||
|     std::map<CSMWorld::CellCoordinates, Cell*>::iterator searchResult = mCells.find(coords); | ||||
|     if (searchResult != mCells.end()) return searchResult->second->getAlteredHeight(inCellX, inCellY); | ||||
|     return nullptr; | ||||
| } | ||||
| 
 | ||||
| void CSVRender::PagedWorldspaceWidget::resetAllAlteredHeights() | ||||
| { | ||||
|     std::map<CSMWorld::CellCoordinates, Cell *>::iterator iter (mCells.begin()); | ||||
| 
 | ||||
|     while (iter!=mCells.end()) | ||||
|     { | ||||
|         iter->second->resetAlteredHeights(); | ||||
|         ++iter; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| std::vector<osg::ref_ptr<CSVRender::TagBase> > CSVRender::PagedWorldspaceWidget::getSelection ( | ||||
|     unsigned int elementMask) const | ||||
| { | ||||
|  |  | |||
|  | @ -124,6 +124,16 @@ namespace CSVRender | |||
| 
 | ||||
|             virtual Cell* getCell(const osg::Vec3d& point) const; | ||||
| 
 | ||||
|             virtual Cell* getCell(const CSMWorld::CellCoordinates& coords) const; | ||||
| 
 | ||||
|             void setCellAlteredHeight(const CSMWorld::CellCoordinates& coords, int inCellX, int inCellY, float height); | ||||
| 
 | ||||
|             float* getCellAlteredHeights(const CSMWorld::CellCoordinates& coords); | ||||
| 
 | ||||
|             float* getCellAlteredHeight(const CSMWorld::CellCoordinates& coords, int inCellX, int inCellY); | ||||
| 
 | ||||
|             void resetAllAlteredHeights(); | ||||
| 
 | ||||
|             virtual std::vector<osg::ref_ptr<TagBase> > getSelection (unsigned int elementMask) | ||||
|                 const; | ||||
| 
 | ||||
|  |  | |||
|  | @ -249,13 +249,11 @@ int CSVRender::TerrainSelection::calculateLandHeight(int x, int y) // global ver | |||
|     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); | ||||
|     CSMWorld::CellCoordinates coords (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>(); | ||||
|     float landHeight = 0.f; | ||||
|     if (CSVRender::Cell* cell = dynamic_cast<CSVRender::Cell*>(mWorldspaceWidget->getCell(coords))) | ||||
|         landHeight = cell->getSumOfAlteredAndTrueHeight(cellX, cellY, localX, localY); | ||||
| 
 | ||||
|     return mPointer[localY*ESM::Land::LAND_SIZE + localX]; | ||||
|     return landHeight; | ||||
| } | ||||
|  |  | |||
							
								
								
									
										1156
									
								
								apps/opencs/view/render/terrainshapemode.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1156
									
								
								apps/opencs/view/render/terrainshapemode.cpp
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										157
									
								
								apps/opencs/view/render/terrainshapemode.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										157
									
								
								apps/opencs/view/render/terrainshapemode.hpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,157 @@ | |||
| #ifndef CSV_RENDER_TERRAINSHAPEMODE_H | ||||
| #define CSV_RENDER_TERRAINSHAPEMODE_H | ||||
| 
 | ||||
| #include "editmode.hpp" | ||||
| 
 | ||||
| #include <string> | ||||
| #include <memory> | ||||
| 
 | ||||
| #include <QWidget> | ||||
| #include <QEvent> | ||||
| 
 | ||||
| #ifndef Q_MOC_RUN | ||||
| #include "../../model/world/data.hpp" | ||||
| #include "../../model/world/land.hpp" | ||||
| 
 | ||||
| #include "../../model/doc/document.hpp" | ||||
| #include "../../model/world/commands.hpp" | ||||
| #include "../../model/world/idtable.hpp" | ||||
| #include "../../model/world/landtexture.hpp" | ||||
| #endif | ||||
| 
 | ||||
| #include "terrainselection.hpp" | ||||
| 
 | ||||
| namespace CSVWidget | ||||
| { | ||||
|     class SceneToolShapeBrush; | ||||
| } | ||||
| 
 | ||||
| namespace CSVRender | ||||
| { | ||||
|     class PagedWorldspaceWidget; | ||||
| 
 | ||||
|     /// \brief EditMode for handling the terrain shape editing
 | ||||
|     class TerrainShapeMode : public EditMode | ||||
|     { | ||||
|         Q_OBJECT | ||||
| 
 | ||||
|         public: | ||||
| 
 | ||||
|             enum InteractionType | ||||
|             { | ||||
|                 InteractionType_PrimaryEdit, | ||||
|                 InteractionType_PrimarySelect, | ||||
|                 InteractionType_SecondaryEdit, | ||||
|                 InteractionType_SecondarySelect, | ||||
|                 InteractionType_None | ||||
|             }; | ||||
| 
 | ||||
|             /// Editmode for terrain shape grid
 | ||||
|             TerrainShapeMode(WorldspaceWidget*, osg::Group* parentNode, QWidget* parent = nullptr); | ||||
| 
 | ||||
|             void primaryOpenPressed (const WorldspaceHitResult& hit); | ||||
| 
 | ||||
|             /// Create single command for one-click shape editing
 | ||||
|             void primaryEditPressed (const WorldspaceHitResult& hit); | ||||
| 
 | ||||
|             /// Open brush settings window
 | ||||
|             void primarySelectPressed(const WorldspaceHitResult&); | ||||
| 
 | ||||
|             void secondarySelectPressed(const WorldspaceHitResult&); | ||||
| 
 | ||||
|             void activate(CSVWidget::SceneToolbar*); | ||||
|             void deactivate(CSVWidget::SceneToolbar*); | ||||
| 
 | ||||
|             /// Start shape editing command macro
 | ||||
|             virtual bool primaryEditStartDrag (const QPoint& pos); | ||||
| 
 | ||||
|             virtual bool secondaryEditStartDrag (const QPoint& pos); | ||||
|             virtual bool primarySelectStartDrag (const QPoint& pos); | ||||
|             virtual bool secondarySelectStartDrag (const QPoint& pos); | ||||
| 
 | ||||
|             /// Handle shape edit behavior during dragging
 | ||||
|             virtual void drag (const QPoint& pos, int diffX, int diffY, double speedFactor); | ||||
| 
 | ||||
|             /// End shape editing command macro
 | ||||
|             virtual void dragCompleted(const QPoint& pos); | ||||
| 
 | ||||
|             /// Cancel shape editing, and reset all pending changes
 | ||||
|             virtual void dragAborted(); | ||||
| 
 | ||||
|             virtual void dragWheel (int diff, double speedFactor); | ||||
|             virtual void dragMoveEvent (QDragMoveEvent *event); | ||||
| 
 | ||||
|             /// Handle brush mechanics for shape editing
 | ||||
|             void editTerrainShapeGrid (const std::pair<int, int>& vertexCoords, bool dragOperation); | ||||
| 
 | ||||
|             /// Do a single height alteration for transient shape edit map
 | ||||
|             void alterHeight(const CSMWorld::CellCoordinates& cellCoords, int inCellX, int inCellY, float alteredHeight, bool useTool = true); | ||||
| 
 | ||||
|             /// Do a single smoothing height alteration for transient shape edit map
 | ||||
|             void smoothHeight(const CSMWorld::CellCoordinates& cellCoords, int inCellX, int inCellY, int toolStrength); | ||||
| 
 | ||||
|             /// Do a single flattening height alteration for transient shape edit map
 | ||||
|             void flattenHeight(const CSMWorld::CellCoordinates& cellCoords, int inCellX, int inCellY, int toolStrength, int targetHeight); | ||||
| 
 | ||||
|             /// Update the key values used in height calculations
 | ||||
|             void updateKeyHeightValues(const CSMWorld::CellCoordinates& cellCoords, int inCellX, int inCellY, float* thisHeight, | ||||
|                 float* thisAlteredHeight, float* leftHeight, float* leftAlteredHeight, float* upHeight, float* upAlteredHeight); | ||||
| 
 | ||||
|             /// Bind edge vertices to next cells
 | ||||
|             void fixEdges(const CSMWorld::CellCoordinates& cellCoords); | ||||
| 
 | ||||
|             /// Check that the edit doesn't break save format limits, fix if necessary
 | ||||
|             void limitAlteredHeights(const CSMWorld::CellCoordinates& cellCoords); | ||||
| 
 | ||||
|             /// Handle brush mechanics for terrain shape selection
 | ||||
|             void selectTerrainShapes (const std::pair<int, int>& vertexCoords, unsigned char selectMode, bool dragOperation); | ||||
| 
 | ||||
|             /// Push terrain shape edits to command macro
 | ||||
|             void pushEditToCommand (const CSMWorld::LandHeightsColumn::DataType& newLandGrid, CSMDoc::Document& document, | ||||
|                 CSMWorld::IdTable& landTable, std::string cellId); | ||||
| 
 | ||||
|             /// Push land normals edits to command macro
 | ||||
|             void pushNormalsEditToCommand(const CSMWorld::LandNormalsColumn::DataType& newLandGrid, CSMDoc::Document& document, | ||||
|                 CSMWorld::IdTable& landTable, std::string cellId); | ||||
| 
 | ||||
|             /// Create new cell and land if needed
 | ||||
|             bool allowLandShapeEditing(std::string textureFileName); | ||||
| 
 | ||||
|         private: | ||||
|             std::string mCellId; | ||||
|             std::string mBrushTexture; | ||||
|             int mBrushSize; | ||||
|             int mBrushShape; | ||||
|             std::vector<std::pair<int, int>> mCustomBrushShape; | ||||
|             CSVWidget::SceneToolShapeBrush *mShapeBrushScenetool; | ||||
|             int mDragMode; | ||||
|             osg::Group* mParentNode; | ||||
|             bool mIsEditing; | ||||
|             std::unique_ptr<TerrainSelection> mTerrainShapeSelection; | ||||
|             int mTotalDiffY; | ||||
|             std::vector<CSMWorld::CellCoordinates> mAlteredCells; | ||||
|             osg::Vec3d mEditingPos; | ||||
|             int mShapeEditTool; | ||||
|             int mShapeEditToolStrength; | ||||
|             int mTargetHeight; | ||||
| 
 | ||||
|             const int cellSize {ESM::Land::REAL_SIZE}; | ||||
|             const int landSize {ESM::Land::LAND_SIZE}; | ||||
|             const int landTextureSize {ESM::Land::LAND_TEXTURE_SIZE}; | ||||
| 
 | ||||
|             PagedWorldspaceWidget& getPagedWorldspaceWidget(); | ||||
| 
 | ||||
|         signals: | ||||
|             void passBrushTexture(std::string brushTexture); | ||||
| 
 | ||||
|         public slots: | ||||
|             //void handleDropEvent(QDropEvent *event);
 | ||||
|             void setBrushSize(int brushSize); | ||||
|             void setBrushShape(int brushShape); | ||||
|             void setShapeEditTool(int shapeEditTool); | ||||
|             void setShapeEditToolStrength(int shapeEditToolStrength); | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| #endif | ||||
|  | @ -1,15 +1,25 @@ | |||
| #include "terrainstorage.hpp" | ||||
| 
 | ||||
| #include <set> | ||||
| #include <memory> | ||||
| 
 | ||||
| #include "../../model/world/land.hpp" | ||||
| #include "../../model/world/landtexture.hpp" | ||||
| 
 | ||||
| #include <components/esmterrain/storage.hpp> | ||||
| #include <components/debug/debuglog.hpp> | ||||
| #include <components/misc/resourcehelpers.hpp> | ||||
| #include <components/vfs/manager.hpp> | ||||
| 
 | ||||
| namespace CSVRender | ||||
| { | ||||
|     const float defaultHeight = ESM::Land::DEFAULT_HEIGHT; | ||||
| 
 | ||||
|     TerrainStorage::TerrainStorage(const CSMWorld::Data &data) | ||||
|         : ESMTerrain::Storage(data.getResourceSystem()->getVFS()) | ||||
|         , mData(data) | ||||
|     { | ||||
|         resetHeights(); | ||||
|     } | ||||
| 
 | ||||
|     osg::ref_ptr<const ESMTerrain::LandObject> TerrainStorage::getLand(int cellX, int cellY) | ||||
|  | @ -33,6 +43,209 @@ namespace CSVRender | |||
|         return &mData.getLandTextures().getRecord(row).get(); | ||||
|     } | ||||
| 
 | ||||
|     void TerrainStorage::setAlteredHeight(int inCellX, int inCellY, float height) | ||||
|     { | ||||
|         mAlteredHeight[inCellY*ESM::Land::LAND_SIZE + inCellX] = height - fmod(height, 8); //Limit to divisible by 8 to avoid cell seam breakage
 | ||||
|     } | ||||
| 
 | ||||
|     void TerrainStorage::resetHeights() | ||||
|     { | ||||
|         for (int x = 0; x < ESM::Land::LAND_SIZE; ++x) | ||||
|         { | ||||
|             for (int y = 0; y < ESM::Land::LAND_SIZE; ++y) | ||||
|             { | ||||
|                 mAlteredHeight[y*ESM::Land::LAND_SIZE + x] = 0; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     float TerrainStorage::getSumOfAlteredAndTrueHeight(int cellX, int cellY, int inCellX, int inCellY) | ||||
|     { | ||||
|         float height = 0.f; | ||||
|         osg::ref_ptr<const ESMTerrain::LandObject> land = getLand (cellX, cellY); | ||||
|         if (land) | ||||
|         { | ||||
|             const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VHGT) : 0; | ||||
|             if (data) height = getVertexHeight(data, inCellX, inCellY); | ||||
|         } | ||||
|         else return height; | ||||
|         return mAlteredHeight[inCellY*ESM::Land::LAND_SIZE + inCellX] + height; | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     float* TerrainStorage::getAlteredHeights() | ||||
|     { | ||||
|         return mAlteredHeight; | ||||
|     } | ||||
| 
 | ||||
|     float* TerrainStorage::getAlteredHeight(int inCellX, int inCellY) | ||||
|     { | ||||
|         return &mAlteredHeight[inCellY*ESM::Land::LAND_SIZE + inCellX]; | ||||
|     } | ||||
| 
 | ||||
|     void TerrainStorage::fillVertexBuffers (int lodLevel, float size, const osg::Vec2f& center, | ||||
|                                             osg::ref_ptr<osg::Vec3Array> positions, | ||||
|                                             osg::ref_ptr<osg::Vec3Array> normals, | ||||
|                                             osg::ref_ptr<osg::Vec4ubArray> colours) | ||||
|     { | ||||
|         // LOD level n means every 2^n-th vertex is kept
 | ||||
|         size_t increment = static_cast<size_t>(1) << lodLevel; | ||||
| 
 | ||||
|         osg::Vec2f origin = center - osg::Vec2f(size/2.f, size/2.f); | ||||
| 
 | ||||
|         int startCellX = static_cast<int>(std::floor(origin.x())); | ||||
|         int startCellY = static_cast<int>(std::floor(origin.y())); | ||||
| 
 | ||||
|         size_t numVerts = static_cast<size_t>(size*(ESM::Land::LAND_SIZE - 1) / increment + 1); | ||||
| 
 | ||||
|         positions->resize(numVerts*numVerts); | ||||
|         normals->resize(numVerts*numVerts); | ||||
|         colours->resize(numVerts*numVerts); | ||||
| 
 | ||||
|         osg::Vec3f normal; | ||||
|         osg::Vec4ub color; | ||||
| 
 | ||||
|         float vertY = 0; | ||||
|         float vertX = 0; | ||||
| 
 | ||||
|         ESMTerrain::LandCache cache; | ||||
| 
 | ||||
|         float vertY_ = 0; // of current cell corner
 | ||||
|         for (int cellY = startCellY; cellY < startCellY + std::ceil(size); ++cellY) | ||||
|         { | ||||
|             float vertX_ = 0; // of current cell corner
 | ||||
|             for (int cellX = startCellX; cellX < startCellX + std::ceil(size); ++cellX) | ||||
|             { | ||||
|                 const ESMTerrain::LandObject* land = ESMTerrain::Storage::getLand(cellX, cellY, cache); | ||||
|                 const ESM::Land::LandData *heightData = 0; | ||||
|                 const ESM::Land::LandData *normalData = 0; | ||||
|                 const ESM::Land::LandData *colourData = 0; | ||||
|                 if (land) | ||||
|                 { | ||||
|                     heightData = land->getData(ESM::Land::DATA_VHGT); | ||||
|                     normalData = land->getData(ESM::Land::DATA_VNML); | ||||
|                     colourData = land->getData(ESM::Land::DATA_VCLR); | ||||
|                 } | ||||
| 
 | ||||
|                 int rowStart = 0; | ||||
|                 int colStart = 0; | ||||
|                 // Skip the first row / column unless we're at a chunk edge,
 | ||||
|                 // since this row / column is already contained in a previous cell
 | ||||
|                 // This is only relevant if we're creating a chunk spanning multiple cells
 | ||||
|                 if (vertY_ != 0) | ||||
|                     colStart += increment; | ||||
|                 if (vertX_ != 0) | ||||
|                     rowStart += increment; | ||||
| 
 | ||||
|                 // Only relevant for chunks smaller than (contained in) one cell
 | ||||
|                 rowStart += (origin.x() - startCellX) * ESM::Land::LAND_SIZE; | ||||
|                 colStart += (origin.y() - startCellY) * ESM::Land::LAND_SIZE; | ||||
|                 int rowEnd = std::min(static_cast<int>(rowStart + std::min(1.f, size) * (ESM::Land::LAND_SIZE-1) + 1), static_cast<int>(ESM::Land::LAND_SIZE)); | ||||
|                 int colEnd = std::min(static_cast<int>(colStart + std::min(1.f, size) * (ESM::Land::LAND_SIZE-1) + 1), static_cast<int>(ESM::Land::LAND_SIZE)); | ||||
| 
 | ||||
|                 vertY = vertY_; | ||||
|                 for (int col=colStart; col<colEnd; col += increment) | ||||
|                 { | ||||
|                     vertX = vertX_; | ||||
|                     for (int row=rowStart; row<rowEnd; row += increment) | ||||
|                     { | ||||
|                         int srcArrayIndex = col*ESM::Land::LAND_SIZE*3+row*3; | ||||
| 
 | ||||
|                         assert(row >= 0 && row < ESM::Land::LAND_SIZE); | ||||
|                         assert(col >= 0 && col < ESM::Land::LAND_SIZE); | ||||
| 
 | ||||
|                         assert (vertX < numVerts); | ||||
|                         assert (vertY < numVerts); | ||||
| 
 | ||||
|                         float height = defaultHeight; | ||||
|                         if (heightData) | ||||
|                             height = heightData->mHeights[col*ESM::Land::LAND_SIZE + row]; | ||||
| 
 | ||||
|                         (*positions)[static_cast<unsigned int>(vertX*numVerts + vertY)] | ||||
|                             = osg::Vec3f((vertX / float(numVerts - 1) - 0.5f) * size * Constants::CellSizeInUnits, | ||||
|                                          (vertY / float(numVerts - 1) - 0.5f) * size * Constants::CellSizeInUnits, | ||||
|                                          height + mAlteredHeight[static_cast<unsigned int>(col*ESM::Land::LAND_SIZE + row)]); | ||||
| 
 | ||||
|                         if (normalData) | ||||
|                         { | ||||
|                             for (int i=0; i<3; ++i) | ||||
|                                 normal[i] = normalData->mNormals[srcArrayIndex+i]; | ||||
| 
 | ||||
|                             normal.normalize(); | ||||
|                         } | ||||
|                         else | ||||
|                             normal = osg::Vec3f(0,0,1); | ||||
| 
 | ||||
|                         // Normals apparently don't connect seamlessly between cells
 | ||||
|                         if (col == ESM::Land::LAND_SIZE-1 || row == ESM::Land::LAND_SIZE-1) | ||||
|                             fixNormal(normal, cellX, cellY, col, row, cache); | ||||
| 
 | ||||
|                         // some corner normals appear to be complete garbage (z < 0)
 | ||||
|                         if ((row == 0 || row == ESM::Land::LAND_SIZE-1) && (col == 0 || col == ESM::Land::LAND_SIZE-1)) | ||||
|                             averageNormal(normal, cellX, cellY, col, row, cache); | ||||
| 
 | ||||
|                         assert(normal.z() > 0); | ||||
| 
 | ||||
|                         (*normals)[static_cast<unsigned int>(vertX*numVerts + vertY)] = normal; | ||||
| 
 | ||||
|                         if (colourData) | ||||
|                         { | ||||
|                             for (int i=0; i<3; ++i) | ||||
|                                 color[i] = colourData->mColours[srcArrayIndex+i]; | ||||
|                         } | ||||
|                         else | ||||
|                         { | ||||
|                             color.r() = 255; | ||||
|                             color.g() = 255; | ||||
|                             color.b() = 255; | ||||
|                         } | ||||
| 
 | ||||
|                         // Highlight broken height changes
 | ||||
|                         if ( ((col > 0 && row > 0) && | ||||
|                             ((abs(heightData->mHeights[col*ESM::Land::LAND_SIZE + row] + | ||||
|                             mAlteredHeight[static_cast<unsigned int>(col*ESM::Land::LAND_SIZE + row)] - | ||||
|                             (heightData->mHeights[(col)*ESM::Land::LAND_SIZE + row - 1] + | ||||
|                             mAlteredHeight[static_cast<unsigned int>((col)*ESM::Land::LAND_SIZE + row - 1)])) >= 1024 ) || | ||||
|                             abs(heightData->mHeights[col*ESM::Land::LAND_SIZE + row] + | ||||
|                             mAlteredHeight[static_cast<unsigned int>(col*ESM::Land::LAND_SIZE + row)] - | ||||
|                             (heightData->mHeights[(col - 1)*ESM::Land::LAND_SIZE + row] + | ||||
|                             mAlteredHeight[static_cast<unsigned int>((col - 1)*ESM::Land::LAND_SIZE + row)]))  >= 1024 )) || | ||||
|                             ((col < ESM::Land::LAND_SIZE - 1 && row < ESM::Land::LAND_SIZE - 1) && | ||||
|                             ((abs(heightData->mHeights[col*ESM::Land::LAND_SIZE + row] + | ||||
|                             mAlteredHeight[static_cast<unsigned int>(col*ESM::Land::LAND_SIZE + row)] - | ||||
|                             (heightData->mHeights[(col)*ESM::Land::LAND_SIZE + row + 1] + | ||||
|                             mAlteredHeight[static_cast<unsigned int>((col)*ESM::Land::LAND_SIZE + row + 1)])) >= 1024 ) || | ||||
|                             abs(heightData->mHeights[col*ESM::Land::LAND_SIZE + row] + | ||||
|                             mAlteredHeight[static_cast<unsigned int>(col*ESM::Land::LAND_SIZE + row)] - | ||||
|                             (heightData->mHeights[(col + 1)*ESM::Land::LAND_SIZE + row] + | ||||
|                             mAlteredHeight[static_cast<unsigned int>((col + 1)*ESM::Land::LAND_SIZE + row)]))  >= 1024 ))) | ||||
|                         { | ||||
|                             color.r() = 255; | ||||
|                             color.g() = 0; | ||||
|                             color.b() = 0; | ||||
|                         } | ||||
| 
 | ||||
|                         // Unlike normals, colors mostly connect seamlessly between cells, but not always...
 | ||||
|                         if (col == ESM::Land::LAND_SIZE-1 || row == ESM::Land::LAND_SIZE-1) | ||||
|                             fixColour(color, cellX, cellY, col, row, cache); | ||||
| 
 | ||||
|                         color.a() = 255; | ||||
| 
 | ||||
|                         (*colours)[static_cast<unsigned int>(vertX*numVerts + vertY)] = color; | ||||
| 
 | ||||
|                         ++vertX; | ||||
|                     } | ||||
|                     ++vertY; | ||||
|                 } | ||||
|                 vertX_ = vertX; | ||||
|             } | ||||
|             vertY_ = vertY; | ||||
| 
 | ||||
|             assert(vertX_ == numVerts); // Ensure we covered whole area
 | ||||
|         } | ||||
|         assert(vertY_ == numVerts);  // Ensure we covered whole area*/
 | ||||
|     } | ||||
| 
 | ||||
|     void TerrainStorage::getBounds(float &minX, float &maxX, float &minY, float &maxY) | ||||
|     { | ||||
|         // not needed at the moment - this returns the bounds of the whole world, but we only edit individual cells
 | ||||
|  |  | |||
|  | @ -7,6 +7,7 @@ | |||
| 
 | ||||
| namespace CSVRender | ||||
| { | ||||
|     class LandCache; | ||||
| 
 | ||||
|     /**
 | ||||
|      * @brief A bridge between the terrain component and OpenCS's terrain data storage. | ||||
|  | @ -15,12 +16,25 @@ namespace CSVRender | |||
|     { | ||||
|     public: | ||||
|         TerrainStorage(const CSMWorld::Data& data); | ||||
|         float mAlteredHeight[ESM::Land::LAND_SIZE * ESM::Land::LAND_SIZE + ESM::Land::LAND_SIZE]; | ||||
|         void setAlteredHeight(int inCellX, int inCellY, float heightMap); | ||||
|         void resetHeights(); | ||||
|         float getSumOfAlteredAndTrueHeight(int cellX, int cellY, int inCellX, int inCellY); | ||||
|         float* getAlteredHeights(); | ||||
|         float* getAlteredHeight(int inCellX, int inCellY); | ||||
| 
 | ||||
|     private: | ||||
|         const CSMWorld::Data& mData; | ||||
| 
 | ||||
|         virtual osg::ref_ptr<const ESMTerrain::LandObject> getLand (int cellX, int cellY) override; | ||||
|         virtual const ESM::LandTexture* getLandTexture(int index, short plugin) override; | ||||
| 
 | ||||
|         /// Draws temporarily altered land (transient change support)
 | ||||
|         void fillVertexBuffers (int lodLevel, float size, const osg::Vec2f& center, | ||||
|                         osg::ref_ptr<osg::Vec3Array> positions, | ||||
|                         osg::ref_ptr<osg::Vec3Array> normals, | ||||
|                         osg::ref_ptr<osg::Vec4ubArray> colours) override; | ||||
| 
 | ||||
|         virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY) override; | ||||
|     }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -150,6 +150,11 @@ CSVRender::Cell* CSVRender::UnpagedWorldspaceWidget::getCell(const osg::Vec3d& p | |||
|     return mCell.get(); | ||||
| } | ||||
| 
 | ||||
| CSVRender::Cell* CSVRender::UnpagedWorldspaceWidget::getCell(const CSMWorld::CellCoordinates& coords) const | ||||
| { | ||||
|     return mCell.get(); | ||||
| } | ||||
| 
 | ||||
| std::vector<osg::ref_ptr<CSVRender::TagBase> > CSVRender::UnpagedWorldspaceWidget::getSelection ( | ||||
|     unsigned int elementMask) const | ||||
| { | ||||
|  |  | |||
|  | @ -17,6 +17,7 @@ namespace CSMDoc | |||
| namespace CSMWorld | ||||
| { | ||||
|     class IdTable; | ||||
|     class CellCoordinates; | ||||
| } | ||||
| 
 | ||||
| namespace CSVRender | ||||
|  | @ -63,6 +64,8 @@ namespace CSVRender | |||
| 
 | ||||
|             virtual Cell* getCell(const osg::Vec3d& point) const; | ||||
| 
 | ||||
|             virtual Cell* getCell(const CSMWorld::CellCoordinates& coords) const; | ||||
| 
 | ||||
|             virtual std::vector<osg::ref_ptr<TagBase> > getSelection (unsigned int elementMask) | ||||
|                 const; | ||||
| 
 | ||||
|  |  | |||
|  | @ -17,6 +17,7 @@ namespace CSMPrefs | |||
| 
 | ||||
| namespace CSMWorld | ||||
| { | ||||
|     class CellCoordinates; | ||||
|     class UniversalId; | ||||
| } | ||||
| 
 | ||||
|  | @ -170,6 +171,8 @@ namespace CSVRender | |||
|             /// \note Returns the cell if it exists, otherwise a null pointer
 | ||||
|             virtual Cell* getCell(const osg::Vec3d& point) const = 0; | ||||
| 
 | ||||
|             virtual Cell* getCell(const CSMWorld::CellCoordinates& coords) const = 0; | ||||
| 
 | ||||
|             virtual std::vector<osg::ref_ptr<TagBase> > getSelection (unsigned int elementMask) | ||||
|                 const = 0; | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										265
									
								
								apps/opencs/view/widget/scenetoolshapebrush.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										265
									
								
								apps/opencs/view/widget/scenetoolshapebrush.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,265 @@ | |||
| #include "scenetoolshapebrush.hpp" | ||||
| 
 | ||||
| #include <QFrame> | ||||
| #include <QIcon> | ||||
| #include <QTableWidget> | ||||
| #include <QHBoxLayout> | ||||
| 
 | ||||
| #include <QWidget> | ||||
| #include <QComboBox> | ||||
| #include <QSpinBox> | ||||
| #include <QGroupBox> | ||||
| #include <QSlider> | ||||
| #include <QEvent> | ||||
| #include <QDropEvent> | ||||
| #include <QButtonGroup> | ||||
| #include <QVBoxLayout> | ||||
| #include <QDragEnterEvent> | ||||
| #include <QDrag> | ||||
| #include <QTableWidget> | ||||
| #include <QHeaderView> | ||||
| #include <QApplication> | ||||
| #include <QSizePolicy> | ||||
| 
 | ||||
| #include "scenetool.hpp" | ||||
| 
 | ||||
| #include "../../model/doc/document.hpp" | ||||
| #include "../../model/prefs/state.hpp" | ||||
| #include "../../model/world/commands.hpp" | ||||
| #include "../../model/world/data.hpp" | ||||
| #include "../../model/world/idcollection.hpp" | ||||
| #include "../../model/world/idtable.hpp" | ||||
| #include "../../model/world/landtexture.hpp" | ||||
| #include "../../model/world/universalid.hpp" | ||||
| 
 | ||||
| 
 | ||||
| CSVWidget::ShapeBrushSizeControls::ShapeBrushSizeControls(const QString &title, QWidget *parent) | ||||
|     : QGroupBox(title, parent) | ||||
| { | ||||
|     mBrushSizeSlider = new QSlider(Qt::Horizontal); | ||||
|     mBrushSizeSlider->setTickPosition(QSlider::TicksBothSides); | ||||
|     mBrushSizeSlider->setTickInterval(10); | ||||
|     mBrushSizeSlider->setRange(1, CSMPrefs::get()["3D Scene Editing"]["shapebrush-maximumsize"].toInt()); | ||||
|     mBrushSizeSlider->setSingleStep(1); | ||||
| 
 | ||||
|     mBrushSizeSpinBox = new QSpinBox; | ||||
|     mBrushSizeSpinBox->setRange(1, CSMPrefs::get()["3D Scene Editing"]["shapebrush-maximumsize"].toInt()); | ||||
|     mBrushSizeSpinBox->setSingleStep(1); | ||||
| 
 | ||||
|     mLayoutSliderSize = new QHBoxLayout; | ||||
|     mLayoutSliderSize->addWidget(mBrushSizeSlider); | ||||
|     mLayoutSliderSize->addWidget(mBrushSizeSpinBox); | ||||
| 
 | ||||
|     connect(mBrushSizeSlider, SIGNAL(valueChanged(int)), mBrushSizeSpinBox, SLOT(setValue(int))); | ||||
|     connect(mBrushSizeSpinBox, SIGNAL(valueChanged(int)), mBrushSizeSlider, SLOT(setValue(int))); | ||||
| 
 | ||||
|     setLayout(mLayoutSliderSize); | ||||
| } | ||||
| 
 | ||||
| CSVWidget::ShapeBrushWindow::ShapeBrushWindow(CSMDoc::Document& document, QWidget *parent) | ||||
|     : QFrame(parent, Qt::Popup), | ||||
|     mBrushShape(0), | ||||
|     mBrushSize(0), | ||||
|     mDocument(document) | ||||
| { | ||||
|     mButtonPoint = new QPushButton(QIcon (QPixmap (":scenetoolbar/brush-point")), "", this); | ||||
|     mButtonSquare = new QPushButton(QIcon (QPixmap (":scenetoolbar/brush-square")), "", this); | ||||
|     mButtonCircle = new QPushButton(QIcon (QPixmap (":scenetoolbar/brush-circle")), "", this); | ||||
|     mButtonCustom = new QPushButton(QIcon (QPixmap (":scenetoolbar/brush-custom")), "", this); | ||||
| 
 | ||||
|     mSizeSliders = new ShapeBrushSizeControls("Brush size", this); | ||||
| 
 | ||||
|     QVBoxLayout *layoutMain = new QVBoxLayout; | ||||
|     layoutMain->setSpacing(0); | ||||
|     layoutMain->setContentsMargins(4,0,4,4); | ||||
| 
 | ||||
|     QHBoxLayout *layoutHorizontal = new QHBoxLayout; | ||||
|     layoutHorizontal->setSpacing(0); | ||||
|     layoutHorizontal->setContentsMargins (QMargins (0, 0, 0, 0)); | ||||
| 
 | ||||
|     configureButtonInitialSettings(mButtonPoint); | ||||
|     configureButtonInitialSettings(mButtonSquare); | ||||
|     configureButtonInitialSettings(mButtonCircle); | ||||
|     configureButtonInitialSettings(mButtonCustom); | ||||
| 
 | ||||
|     mButtonPoint->setToolTip (toolTipPoint); | ||||
|     mButtonSquare->setToolTip (toolTipSquare); | ||||
|     mButtonCircle->setToolTip (toolTipCircle); | ||||
|     mButtonCustom->setToolTip (toolTipCustom); | ||||
| 
 | ||||
|     QButtonGroup* brushButtonGroup = new QButtonGroup(this); | ||||
|     brushButtonGroup->addButton(mButtonPoint); | ||||
|     brushButtonGroup->addButton(mButtonSquare); | ||||
|     brushButtonGroup->addButton(mButtonCircle); | ||||
|     brushButtonGroup->addButton(mButtonCustom); | ||||
| 
 | ||||
|     brushButtonGroup->setExclusive(true); | ||||
| 
 | ||||
|     layoutHorizontal->addWidget(mButtonPoint, 0, Qt::AlignTop); | ||||
|     layoutHorizontal->addWidget(mButtonSquare, 0, Qt::AlignTop); | ||||
|     layoutHorizontal->addWidget(mButtonCircle, 0, Qt::AlignTop); | ||||
|     layoutHorizontal->addWidget(mButtonCustom, 0, Qt::AlignTop); | ||||
| 
 | ||||
|     mHorizontalGroupBox = new QGroupBox(tr("")); | ||||
|     mHorizontalGroupBox->setLayout(layoutHorizontal); | ||||
| 
 | ||||
|     mToolSelector = new QComboBox(this); | ||||
|     mToolSelector->addItem(tr("Height (drag)")); | ||||
|     mToolSelector->addItem(tr("Height, raise (paint)")); | ||||
|     mToolSelector->addItem(tr("Height, lower (paint)")); | ||||
|     mToolSelector->addItem(tr("Smooth (paint)")); | ||||
|     mToolSelector->addItem(tr("Flatten (paint)")); | ||||
| 
 | ||||
|     QLabel *brushStrengthLabel = new QLabel(this); | ||||
|     brushStrengthLabel->setText("Brush strength:"); | ||||
| 
 | ||||
|     mToolStrengthSlider = new QSlider(Qt::Horizontal); | ||||
|     mToolStrengthSlider->setTickPosition(QSlider::TicksBothSides); | ||||
|     mToolStrengthSlider->setTickInterval(8); | ||||
|     mToolStrengthSlider->setRange(8, 128); | ||||
|     mToolStrengthSlider->setSingleStep(8); | ||||
| 
 | ||||
|     layoutMain->addWidget(mHorizontalGroupBox); | ||||
|     layoutMain->addWidget(mSizeSliders); | ||||
|     layoutMain->addWidget(mToolSelector); | ||||
|     layoutMain->addWidget(brushStrengthLabel); | ||||
|     layoutMain->addWidget(mToolStrengthSlider); | ||||
| 
 | ||||
|     setLayout(layoutMain); | ||||
| 
 | ||||
|     connect(mButtonPoint, SIGNAL(clicked()), this, SLOT(setBrushShape())); | ||||
|     connect(mButtonSquare, SIGNAL(clicked()), this, SLOT(setBrushShape())); | ||||
|     connect(mButtonCircle, SIGNAL(clicked()), this, SLOT(setBrushShape())); | ||||
|     connect(mButtonCustom, SIGNAL(clicked()), this, SLOT(setBrushShape())); | ||||
| } | ||||
| 
 | ||||
| void CSVWidget::ShapeBrushWindow::configureButtonInitialSettings(QPushButton *button) | ||||
| { | ||||
|   button->setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed)); | ||||
|   button->setContentsMargins (QMargins (0, 0, 0, 0)); | ||||
|   button->setIconSize (QSize (48-6, 48-6)); | ||||
|   button->setFixedSize (48, 48); | ||||
|   button->setCheckable(true); | ||||
| } | ||||
| 
 | ||||
| void CSVWidget::ShapeBrushWindow::setBrushSize(int brushSize) | ||||
| { | ||||
|     mBrushSize = brushSize; | ||||
|     emit passBrushSize(mBrushSize); | ||||
| } | ||||
| 
 | ||||
| void CSVWidget::ShapeBrushWindow::setBrushShape() | ||||
| { | ||||
|     if(mButtonPoint->isChecked()) mBrushShape = 0; | ||||
|     if(mButtonSquare->isChecked()) mBrushShape = 1; | ||||
|     if(mButtonCircle->isChecked()) mBrushShape = 2; | ||||
|     if(mButtonCustom->isChecked()) mBrushShape = 3; | ||||
|     emit passBrushShape(mBrushShape); | ||||
| } | ||||
| 
 | ||||
| void CSVWidget::SceneToolShapeBrush::adjustToolTips() | ||||
| { | ||||
| } | ||||
| 
 | ||||
| CSVWidget::SceneToolShapeBrush::SceneToolShapeBrush (SceneToolbar *parent, const QString& toolTip, CSMDoc::Document& document) | ||||
| : SceneTool (parent, Type_TopAction), | ||||
|     mToolTip (toolTip), | ||||
|     mDocument (document), | ||||
|     mShapeBrushWindow(new ShapeBrushWindow(document, this)) | ||||
| { | ||||
|     setAcceptDrops(true); | ||||
|     connect(mShapeBrushWindow, SIGNAL(passBrushShape(int)), this, SLOT(setButtonIcon(int))); | ||||
|     setButtonIcon(mShapeBrushWindow->mBrushShape); | ||||
| 
 | ||||
|     mPanel = new QFrame (this, Qt::Popup); | ||||
| 
 | ||||
|     QHBoxLayout *layout = new QHBoxLayout (mPanel); | ||||
| 
 | ||||
|     layout->setContentsMargins (QMargins (0, 0, 0, 0)); | ||||
| 
 | ||||
|     mTable = new QTableWidget (0, 2, this); | ||||
| 
 | ||||
|     mTable->setShowGrid (true); | ||||
|     mTable->verticalHeader()->hide(); | ||||
|     mTable->horizontalHeader()->hide(); | ||||
| #if QT_VERSION >= QT_VERSION_CHECK(5,0,0) | ||||
|     mTable->horizontalHeader()->setSectionResizeMode (0, QHeaderView::Stretch); | ||||
|     mTable->horizontalHeader()->setSectionResizeMode (1, QHeaderView::Stretch); | ||||
| #else | ||||
|     mTable->horizontalHeader()->setResizeMode (0, QHeaderView::Stretch); | ||||
|     mTable->horizontalHeader()->setResizeMode (1, QHeaderView::Stretch); | ||||
| #endif | ||||
|     mTable->setSelectionMode (QAbstractItemView::NoSelection); | ||||
| 
 | ||||
|     layout->addWidget (mTable); | ||||
| 
 | ||||
|     connect (mTable, SIGNAL (clicked (const QModelIndex&)), | ||||
|         this, SLOT (clicked (const QModelIndex&))); | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| void CSVWidget::SceneToolShapeBrush::setButtonIcon (int brushShape) | ||||
| { | ||||
|     QString tooltip = "Change brush settings <p>Currently selected: "; | ||||
| 
 | ||||
|     switch (brushShape) | ||||
|     { | ||||
|         case 0: | ||||
| 
 | ||||
|             setIcon (QIcon (QPixmap (":scenetoolbar/brush-point"))); | ||||
|             tooltip += mShapeBrushWindow->toolTipPoint; | ||||
|             break; | ||||
| 
 | ||||
|         case 1: | ||||
| 
 | ||||
|             setIcon (QIcon (QPixmap (":scenetoolbar/brush-square"))); | ||||
|             tooltip += mShapeBrushWindow->toolTipSquare; | ||||
|             break; | ||||
| 
 | ||||
|         case 2: | ||||
| 
 | ||||
|             setIcon (QIcon (QPixmap (":scenetoolbar/brush-circle"))); | ||||
|             tooltip += mShapeBrushWindow->toolTipCircle; | ||||
|             break; | ||||
| 
 | ||||
|         case 3: | ||||
| 
 | ||||
|             setIcon (QIcon (QPixmap (":scenetoolbar/brush-custom"))); | ||||
|             tooltip += mShapeBrushWindow->toolTipCustom; | ||||
|             break; | ||||
|     } | ||||
| 
 | ||||
|     setToolTip (tooltip); | ||||
| } | ||||
| 
 | ||||
| void CSVWidget::SceneToolShapeBrush::showPanel (const QPoint& position) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| void CSVWidget::SceneToolShapeBrush::updatePanel () | ||||
| { | ||||
| } | ||||
| 
 | ||||
| void CSVWidget::SceneToolShapeBrush::clicked (const QModelIndex& index) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| void CSVWidget::SceneToolShapeBrush::activate () | ||||
| { | ||||
|     QPoint position = QCursor::pos(); | ||||
|     mShapeBrushWindow->mSizeSliders->mBrushSizeSlider->setRange(1, CSMPrefs::get()["3D Scene Editing"]["shapebrush-maximumsize"].toInt()); | ||||
|     mShapeBrushWindow->mSizeSliders->mBrushSizeSpinBox->setRange(1, CSMPrefs::get()["3D Scene Editing"]["shapebrush-maximumsize"].toInt()); | ||||
|     mShapeBrushWindow->move (position); | ||||
|     mShapeBrushWindow->show(); | ||||
| } | ||||
| 
 | ||||
| void CSVWidget::SceneToolShapeBrush::dragEnterEvent (QDragEnterEvent *event) | ||||
| { | ||||
|     emit passEvent(event); | ||||
|     event->accept(); | ||||
| } | ||||
| void CSVWidget::SceneToolShapeBrush::dropEvent (QDropEvent *event) | ||||
| { | ||||
|     emit passEvent(event); | ||||
|     event->accept(); | ||||
| } | ||||
							
								
								
									
										130
									
								
								apps/opencs/view/widget/scenetoolshapebrush.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								apps/opencs/view/widget/scenetoolshapebrush.hpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,130 @@ | |||
| #ifndef CSV_WIDGET_SCENETOOLSHAPEBRUSH_H | ||||
| #define CSV_WIDGET_SCENETOOLSHAPEBRUSH_H | ||||
| 
 | ||||
| #include <QIcon> | ||||
| #include <QFrame> | ||||
| #include <QModelIndex> | ||||
| 
 | ||||
| #include <QWidget> | ||||
| #include <QLabel> | ||||
| #include <QComboBox> | ||||
| #include <QSpinBox> | ||||
| #include <QGroupBox> | ||||
| #include <QSlider> | ||||
| #include <QEvent> | ||||
| #include <QHBoxLayout> | ||||
| #include <QPushButton> | ||||
| 
 | ||||
| #ifndef Q_MOC_RUN | ||||
| #include "scenetool.hpp" | ||||
| 
 | ||||
| #include "../../model/doc/document.hpp" | ||||
| #endif | ||||
| 
 | ||||
| class QTableWidget; | ||||
| 
 | ||||
| namespace CSVRender | ||||
| { | ||||
|     class TerrainShapeMode; | ||||
| } | ||||
| 
 | ||||
| namespace CSVWidget | ||||
| { | ||||
|     class SceneToolShapeBrush; | ||||
| 
 | ||||
|     /// \brief Layout-box for some brush button settings
 | ||||
|     class ShapeBrushSizeControls : public QGroupBox | ||||
|     { | ||||
|         Q_OBJECT | ||||
| 
 | ||||
|         public: | ||||
|             ShapeBrushSizeControls(const QString &title, QWidget *parent); | ||||
| 
 | ||||
|         private: | ||||
|             QHBoxLayout *mLayoutSliderSize; | ||||
|             QSlider *mBrushSizeSlider; | ||||
|             QSpinBox *mBrushSizeSpinBox; | ||||
| 
 | ||||
|         friend class SceneToolShapeBrush; | ||||
|         friend class CSVRender::TerrainShapeMode; | ||||
|     }; | ||||
| 
 | ||||
|     class SceneToolShapeBrush; | ||||
| 
 | ||||
|     /// \brief Brush settings window
 | ||||
|     class ShapeBrushWindow : public QFrame | ||||
|     { | ||||
|         Q_OBJECT | ||||
| 
 | ||||
|         public: | ||||
|             ShapeBrushWindow(CSMDoc::Document& document, QWidget *parent = 0); | ||||
|             void configureButtonInitialSettings(QPushButton *button); | ||||
| 
 | ||||
|             const QString toolTipPoint = "Paint single point"; | ||||
|             const QString toolTipSquare = "Paint with square brush"; | ||||
|             const QString toolTipCircle = "Paint with circle brush"; | ||||
|             const QString toolTipCustom = "Paint custom selection (not implemented yet)"; | ||||
| 
 | ||||
|         private: | ||||
|             int mBrushShape; | ||||
|             int mBrushSize; | ||||
|             CSMDoc::Document& mDocument; | ||||
|             QGroupBox *mHorizontalGroupBox; | ||||
|             QComboBox *mToolSelector; | ||||
|             QSlider *mToolStrengthSlider; | ||||
|             QPushButton *mButtonPoint; | ||||
|             QPushButton *mButtonSquare; | ||||
|             QPushButton *mButtonCircle; | ||||
|             QPushButton *mButtonCustom; | ||||
|             ShapeBrushSizeControls* mSizeSliders; | ||||
| 
 | ||||
|         friend class SceneToolShapeBrush; | ||||
|         friend class CSVRender::TerrainShapeMode; | ||||
| 
 | ||||
|         public slots: | ||||
|             void setBrushShape(); | ||||
|             void setBrushSize(int brushSize); | ||||
| 
 | ||||
|         signals: | ||||
|             void passBrushSize (int brushSize); | ||||
|             void passBrushShape(int brushShape); | ||||
|     }; | ||||
| 
 | ||||
|     class SceneToolShapeBrush : public SceneTool | ||||
|     { | ||||
|             Q_OBJECT | ||||
| 
 | ||||
|             QString mToolTip; | ||||
|             CSMDoc::Document& mDocument; | ||||
|             QFrame *mPanel; | ||||
|             QTableWidget *mTable; | ||||
|             ShapeBrushWindow *mShapeBrushWindow; | ||||
| 
 | ||||
|         private: | ||||
| 
 | ||||
|             void adjustToolTips(); | ||||
| 
 | ||||
|         public: | ||||
| 
 | ||||
|             SceneToolShapeBrush (SceneToolbar *parent, const QString& toolTip, CSMDoc::Document& document); | ||||
| 
 | ||||
|             virtual void showPanel (const QPoint& position); | ||||
|             void updatePanel (); | ||||
| 
 | ||||
|             void dropEvent (QDropEvent *event); | ||||
|             void dragEnterEvent (QDragEnterEvent *event); | ||||
| 
 | ||||
|         friend class CSVRender::TerrainShapeMode; | ||||
| 
 | ||||
|         public slots: | ||||
|             void setButtonIcon(int brushShape); | ||||
|             void clicked (const QModelIndex& index); | ||||
|             virtual void activate(); | ||||
| 
 | ||||
|         signals: | ||||
|             void passEvent(QDropEvent *event); | ||||
|             void passEvent(QDragEnterEvent *event); | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
|  | @ -16,13 +16,6 @@ | |||
| namespace ESMTerrain | ||||
| { | ||||
| 
 | ||||
|     class LandCache | ||||
|     { | ||||
|     public: | ||||
|         typedef std::map<std::pair<int, int>, osg::ref_ptr<const LandObject> > Map; | ||||
|         Map mMap; | ||||
|     }; | ||||
| 
 | ||||
|     LandObject::LandObject() | ||||
|         : mLand(nullptr) | ||||
|         , mLoadFlags(0) | ||||
|  | @ -98,82 +91,6 @@ namespace ESMTerrain | |||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     void Storage::fixNormal (osg::Vec3f& normal, int cellX, int cellY, int col, int row, LandCache& cache) | ||||
|     { | ||||
|         while (col >= ESM::Land::LAND_SIZE-1) | ||||
|         { | ||||
|             ++cellY; | ||||
|             col -= ESM::Land::LAND_SIZE-1; | ||||
|         } | ||||
|         while (row >= ESM::Land::LAND_SIZE-1) | ||||
|         { | ||||
|             ++cellX; | ||||
|             row -= ESM::Land::LAND_SIZE-1; | ||||
|         } | ||||
|         while (col < 0) | ||||
|         { | ||||
|             --cellY; | ||||
|             col += ESM::Land::LAND_SIZE-1; | ||||
|         } | ||||
|         while (row < 0) | ||||
|         { | ||||
|             --cellX; | ||||
|             row += ESM::Land::LAND_SIZE-1; | ||||
|         } | ||||
| 
 | ||||
|         const LandObject* land = getLand(cellX, cellY, cache); | ||||
|         const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VNML) : 0; | ||||
|         if (data) | ||||
|         { | ||||
|             normal.x() = data->mNormals[col*ESM::Land::LAND_SIZE*3+row*3]; | ||||
|             normal.y() = data->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+1]; | ||||
|             normal.z() = data->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+2]; | ||||
|             normal.normalize(); | ||||
|         } | ||||
|         else | ||||
|             normal = osg::Vec3f(0,0,1); | ||||
|     } | ||||
| 
 | ||||
|     void Storage::averageNormal(osg::Vec3f &normal, int cellX, int cellY, int col, int row, LandCache& cache) | ||||
|     { | ||||
|         osg::Vec3f n1,n2,n3,n4; | ||||
|         fixNormal(n1, cellX, cellY, col+1, row, cache); | ||||
|         fixNormal(n2, cellX, cellY, col-1, row, cache); | ||||
|         fixNormal(n3, cellX, cellY, col, row+1, cache); | ||||
|         fixNormal(n4, cellX, cellY, col, row-1, cache); | ||||
|         normal = (n1+n2+n3+n4); | ||||
|         normal.normalize(); | ||||
|     } | ||||
| 
 | ||||
|     void Storage::fixColour (osg::Vec4ub& color, int cellX, int cellY, int col, int row, LandCache& cache) | ||||
|     { | ||||
|         if (col == ESM::Land::LAND_SIZE-1) | ||||
|         { | ||||
|             ++cellY; | ||||
|             col = 0; | ||||
|         } | ||||
|         if (row == ESM::Land::LAND_SIZE-1) | ||||
|         { | ||||
|             ++cellX; | ||||
|             row = 0; | ||||
|         } | ||||
| 
 | ||||
|         const LandObject* land = getLand(cellX, cellY, cache); | ||||
|         const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VCLR) : 0; | ||||
|         if (data) | ||||
|         { | ||||
|             color.r() = data->mColours[col*ESM::Land::LAND_SIZE*3+row*3]; | ||||
|             color.g() = data->mColours[col*ESM::Land::LAND_SIZE*3+row*3+1]; | ||||
|             color.b() = data->mColours[col*ESM::Land::LAND_SIZE*3+row*3+2]; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             color.r() = 255; | ||||
|             color.g() = 255; | ||||
|             color.b() = 255; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void Storage::fillVertexBuffers (int lodLevel, float size, const osg::Vec2f& center, | ||||
|                                             osg::ref_ptr<osg::Vec3Array> positions, | ||||
|                                             osg::ref_ptr<osg::Vec3Array> normals, | ||||
|  | @ -521,25 +438,6 @@ namespace ESMTerrain | |||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     float Storage::getVertexHeight(const ESM::Land::LandData* data, int x, int y) | ||||
|     { | ||||
|         assert(x < ESM::Land::LAND_SIZE); | ||||
|         assert(y < ESM::Land::LAND_SIZE); | ||||
|         return data->mHeights[y * ESM::Land::LAND_SIZE + x]; | ||||
|     } | ||||
| 
 | ||||
|     const LandObject* Storage::getLand(int cellX, int cellY, LandCache& cache) | ||||
|     { | ||||
|         LandCache::Map::iterator found = cache.mMap.find(std::make_pair(cellX, cellY)); | ||||
|         if (found != cache.mMap.end()) | ||||
|             return found->second; | ||||
|         else | ||||
|         { | ||||
|             found = cache.mMap.insert(std::make_pair(std::make_pair(cellX, cellY), getLand(cellX, cellY))).first; | ||||
|             return found->second; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     Terrain::LayerInfo Storage::getLayerInfo(const std::string& texture) | ||||
|     { | ||||
|         OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mLayerInfoMutex); | ||||
|  |  | |||
|  | @ -1,8 +1,13 @@ | |||
| #ifndef COMPONENTS_ESM_TERRAIN_STORAGE_H | ||||
| #define COMPONENTS_ESM_TERRAIN_STORAGE_H | ||||
| 
 | ||||
| #include <cassert> | ||||
| 
 | ||||
| #include <OpenThreads/Mutex> | ||||
| 
 | ||||
| #include <osg/Image> | ||||
| #include <osg/Plane> | ||||
| 
 | ||||
| #include <components/terrain/storage.hpp> | ||||
| 
 | ||||
| #include <components/esm/loadland.hpp> | ||||
|  | @ -13,11 +18,14 @@ namespace VFS | |||
|     class Manager; | ||||
| } | ||||
| 
 | ||||
| namespace CSVRender | ||||
| { | ||||
|     class TerrainStorage; | ||||
| } | ||||
| 
 | ||||
| namespace ESMTerrain | ||||
| { | ||||
| 
 | ||||
|     class LandCache; | ||||
| 
 | ||||
|     /// @brief Wrapper around Land Data with reference counting. The wrapper needs to be held as long as the data is still in use
 | ||||
|     class LandObject : public osg::Object | ||||
|     { | ||||
|  | @ -48,6 +56,13 @@ namespace ESMTerrain | |||
|         ESM::Land::LandData mData; | ||||
|     }; | ||||
| 
 | ||||
|     class LandCache | ||||
|     { | ||||
|     public: | ||||
|         typedef std::map<std::pair<int, int>, osg::ref_ptr<const LandObject> > Map; | ||||
|         Map mMap; | ||||
|     }; | ||||
|      | ||||
|     /// @brief Feeds data from ESM terrain records (ESM::Land, ESM::LandTexture)
 | ||||
|     ///        into the terrain component, converting it on the fly as needed.
 | ||||
|     class Storage : public Terrain::Storage | ||||
|  | @ -110,14 +125,6 @@ namespace ESMTerrain | |||
|     private: | ||||
|         const VFS::Manager* mVFS; | ||||
| 
 | ||||
|         inline void fixNormal (osg::Vec3f& normal, int cellX, int cellY, int col, int row, LandCache& cache); | ||||
|         inline void fixColour (osg::Vec4ub& colour, int cellX, int cellY, int col, int row, LandCache& cache); | ||||
|         inline void averageNormal (osg::Vec3f& normal, int cellX, int cellY, int col, int row, LandCache& cache); | ||||
| 
 | ||||
|         inline float getVertexHeight (const ESM::Land::LandData* data, int x, int y); | ||||
| 
 | ||||
|         inline const LandObject* getLand(int cellX, int cellY, LandCache& cache); | ||||
| 
 | ||||
|         // Since plugins can define new texture palettes, we need to know the plugin index too
 | ||||
|         // in order to retrieve the correct texture name.
 | ||||
|         // pair  <texture id, plugin id>
 | ||||
|  | @ -137,6 +144,103 @@ namespace ESMTerrain | |||
|         bool mAutoUseSpecularMaps; | ||||
| 
 | ||||
|         Terrain::LayerInfo getLayerInfo(const std::string& texture); | ||||
| 
 | ||||
|     protected: | ||||
| 
 | ||||
|         inline void fixNormal (osg::Vec3f& normal, int cellX, int cellY, int col, int row, LandCache& cache) | ||||
|         { | ||||
|             while (col >= ESM::Land::LAND_SIZE-1) | ||||
|             { | ||||
|                 ++cellY; | ||||
|                 col -= ESM::Land::LAND_SIZE-1; | ||||
|             } | ||||
|             while (row >= ESM::Land::LAND_SIZE-1) | ||||
|             { | ||||
|                 ++cellX; | ||||
|                 row -= ESM::Land::LAND_SIZE-1; | ||||
|             } | ||||
|             while (col < 0) | ||||
|             { | ||||
|                 --cellY; | ||||
|                 col += ESM::Land::LAND_SIZE-1; | ||||
|             } | ||||
|             while (row < 0) | ||||
|             { | ||||
|                 --cellX; | ||||
|                 row += ESM::Land::LAND_SIZE-1; | ||||
|             } | ||||
| 
 | ||||
|             const LandObject* land = getLand(cellX, cellY, cache); | ||||
|             const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VNML) : 0; | ||||
|             if (data) | ||||
|             { | ||||
|                 normal.x() = data->mNormals[col*ESM::Land::LAND_SIZE*3+row*3]; | ||||
|                 normal.y() = data->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+1]; | ||||
|                 normal.z() = data->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+2]; | ||||
|                 normal.normalize(); | ||||
|             } | ||||
|             else | ||||
|                 normal = osg::Vec3f(0,0,1); | ||||
|         }; | ||||
| 
 | ||||
|         inline void fixColour (osg::Vec4ub& color, int cellX, int cellY, int col, int row, LandCache& cache) | ||||
|         { | ||||
|             if (col == ESM::Land::LAND_SIZE-1) | ||||
|             { | ||||
|                 ++cellY; | ||||
|                 col = 0; | ||||
|             } | ||||
|             if (row == ESM::Land::LAND_SIZE-1) | ||||
|             { | ||||
|                 ++cellX; | ||||
|                 row = 0; | ||||
|             } | ||||
| 
 | ||||
|             const LandObject* land = getLand(cellX, cellY, cache); | ||||
|             const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VCLR) : 0; | ||||
|             if (data) | ||||
|             { | ||||
|                 color.r() = data->mColours[col*ESM::Land::LAND_SIZE*3+row*3]; | ||||
|                 color.g() = data->mColours[col*ESM::Land::LAND_SIZE*3+row*3+1]; | ||||
|                 color.b() = data->mColours[col*ESM::Land::LAND_SIZE*3+row*3+2]; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 color.r() = 255; | ||||
|                 color.g() = 255; | ||||
|                 color.b() = 255; | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         inline void averageNormal (osg::Vec3f& normal, int cellX, int cellY, int col, int row, LandCache& cache) | ||||
|         { | ||||
|             osg::Vec3f n1,n2,n3,n4; | ||||
|             fixNormal(n1, cellX, cellY, col+1, row, cache); | ||||
|             fixNormal(n2, cellX, cellY, col-1, row, cache); | ||||
|             fixNormal(n3, cellX, cellY, col, row+1, cache); | ||||
|             fixNormal(n4, cellX, cellY, col, row-1, cache); | ||||
|             normal = (n1+n2+n3+n4); | ||||
|             normal.normalize(); | ||||
|         }; | ||||
| 
 | ||||
|         inline float getVertexHeight (const ESM::Land::LandData* data, int x, int y) | ||||
|         { | ||||
|             assert(x < ESM::Land::LAND_SIZE); | ||||
|             assert(y < ESM::Land::LAND_SIZE); | ||||
|             return data->mHeights[y * ESM::Land::LAND_SIZE + x]; | ||||
|         }; | ||||
| 
 | ||||
|         inline const LandObject* getLand(int cellX, int cellY, LandCache& cache) | ||||
|         { | ||||
|             LandCache::Map::iterator found = cache.mMap.find(std::make_pair(cellX, cellY)); | ||||
|             if (found != cache.mMap.end()) | ||||
|                 return found->second; | ||||
|             else | ||||
|             { | ||||
|                 found = cache.mMap.insert(std::make_pair(std::make_pair(cellX, cellY), getLand(cellX, cellY))).first; | ||||
|                 return found->second; | ||||
|             } | ||||
|         }; | ||||
|     }; | ||||
| 
 | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue