mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-16 19:19:56 +00:00
Merge pull request #2520 from unelsson/transientlandshapeedit
[Review phase] Editor: Transient land shape editing
This commit is contained in:
commit
9f039fac87
22 changed files with 2317 additions and 35 deletions
|
@ -187,6 +187,7 @@
|
|||
Feature #4784: Launcher: Duplicate Content Lists
|
||||
Feature #4812: Support NiSwitchNode
|
||||
Feature #4836: Daytime node switch
|
||||
Feature #4840: Editor: Transient terrain change support
|
||||
Feature #4859: Make water reflections more configurable
|
||||
Feature #4882: Support for NiPalette node
|
||||
Feature #4887: Add openmw command option to set initial random seed
|
||||
|
@ -213,6 +214,7 @@
|
|||
Feature #5132: Unique animations for different weapon types
|
||||
Feature #5146: Safe Dispose corpse
|
||||
Feature #5147: Show spell magicka cost in spell buying window
|
||||
Feature #5170: Editor: Land shape editing, land selection
|
||||
Feature #5193: Weapon sheathing
|
||||
Task #4686: Upgrade media decoder to a more current FFmpeg API
|
||||
Task #4695: Optimize Distant Terrain memory consumption
|
||||
|
|
|
@ -40,6 +40,8 @@ New Features:
|
|||
|
||||
New Editor Features:
|
||||
- "Faction Ranks" table for "Faction" records (#4209)
|
||||
- Changes to height editing can be cancelled without changes to data (press esc to cancel) (#4840)
|
||||
- Land heightmap/shape editing and vertex selection (#5170)
|
||||
|
||||
Bug Fixes:
|
||||
- Scripted Items cannot be stacked anymore to avoid multiple script execution (#2969)
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
@ -242,12 +242,24 @@ void CSMPrefs::State::declare()
|
|||
addValues (insertOutsideCell);
|
||||
declareEnum ("outside-visible-drop", "Handling drops outside of visible cells", showAndInsert).
|
||||
addValues (insertOutsideVisibleCell);
|
||||
declareEnum ("outside-landedit", "Handling land edit outside of cells", createAndLandEdit).
|
||||
declareEnum ("outside-landedit", "Handling terrain edit outside of cells", createAndLandEdit).
|
||||
setTooltip("Behavior of terrain editing, if land editing brush reaches an area without cell record.").
|
||||
addValues (landeditOutsideCell);
|
||||
declareEnum ("outside-visible-landedit", "Handling land edit outside of visible cells", showAndLandEdit).
|
||||
declareEnum ("outside-visible-landedit", "Handling terrain edit outside of visible cells", showAndLandEdit).
|
||||
setTooltip("Behavior of terrain editing, if land editing brush reaches an area that is not currently visible.").
|
||||
addValues (landeditOutsideVisibleCell);
|
||||
declareInt ("texturebrush-maximumsize", "Maximum texture brush size", 50).
|
||||
setMin (1);
|
||||
declareInt ("shapebrush-maximumsize", "Maximum height edit brush size", 100).
|
||||
setTooltip("Setting for the slider range of brush size in terrain height editing.").
|
||||
setMin (1);
|
||||
declareBool ("landedit-post-smoothpainting", "Smooth land after painting height", false).
|
||||
setTooltip("Raise and lower tools will leave bumpy finish without this option");
|
||||
declareDouble ("landedit-post-smoothstrength", "Smoothing strength (post-edit)", 0.25).
|
||||
setTooltip("If smoothing land after painting height is used, this is the percentage of smooth applied afterwards. "
|
||||
"Negative values may be used to roughen instead of smooth.").
|
||||
setMin (-1).
|
||||
setMax (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.");
|
||||
|
|
|
@ -89,7 +89,7 @@ namespace CSMWorld
|
|||
|
||||
DataType values(Size, 0);
|
||||
|
||||
if (land.isDataLoaded(Land::DATA_WNAM))
|
||||
if (land.mDataTypes & Land::DATA_WNAM)
|
||||
{
|
||||
for (int i = 0; i < Size; ++i)
|
||||
values[i] = land.mWnam[i];
|
||||
|
|
|
@ -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);
|
||||
|
@ -149,7 +149,6 @@ void CSVRender::Cell::updateLand()
|
|||
}
|
||||
|
||||
// No land data
|
||||
mLandDeleted = true;
|
||||
unloadLand();
|
||||
}
|
||||
|
||||
|
@ -169,6 +168,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 +348,28 @@ 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::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,14 @@ 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* 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,36 @@ 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 nullptr;
|
||||
}
|
||||
|
||||
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::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()
|
||||
{
|
||||
for (const auto& cell : mCells)
|
||||
cell.second->resetAlteredHeights();
|
||||
}
|
||||
|
||||
std::vector<osg::ref_ptr<CSVRender::TagBase> > CSVRender::PagedWorldspaceWidget::getSelection (
|
||||
unsigned int elementMask) const
|
||||
{
|
||||
|
|
|
@ -124,6 +124,14 @@ 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* 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;
|
||||
}
|
||||
|
|
1447
apps/opencs/view/render/terrainshapemode.cpp
Normal file
1447
apps/opencs/view/render/terrainshapemode.cpp
Normal file
File diff suppressed because it is too large
Load diff
190
apps/opencs/view/render/terrainshapemode.hpp
Normal file
190
apps/opencs/view/render/terrainshapemode.hpp
Normal file
|
@ -0,0 +1,190 @@
|
|||
#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"
|
||||
#include "../widget/brushshapes.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
|
||||
};
|
||||
|
||||
enum ShapeEditTool
|
||||
{
|
||||
ShapeEditTool_Drag = 0,
|
||||
ShapeEditTool_PaintToRaise = 1,
|
||||
ShapeEditTool_PaintToLower = 2,
|
||||
ShapeEditTool_Smooth = 3,
|
||||
ShapeEditTool_Flatten = 4
|
||||
};
|
||||
|
||||
/// Editmode for terrain shape grid
|
||||
TerrainShapeMode(WorldspaceWidget*, osg::Group* parentNode, QWidget* parent = nullptr);
|
||||
|
||||
void primaryOpenPressed (const WorldspaceHitResult& hit) final;
|
||||
|
||||
/// Create single command for one-click shape editing
|
||||
void primaryEditPressed (const WorldspaceHitResult& hit) final;
|
||||
|
||||
/// Open brush settings window
|
||||
void primarySelectPressed(const WorldspaceHitResult&) final;
|
||||
|
||||
void secondarySelectPressed(const WorldspaceHitResult&) final;
|
||||
|
||||
void activate(CSVWidget::SceneToolbar*) final;
|
||||
void deactivate(CSVWidget::SceneToolbar*) final;
|
||||
|
||||
/// Start shape editing command macro
|
||||
bool primaryEditStartDrag (const QPoint& pos) final;
|
||||
|
||||
bool secondaryEditStartDrag (const QPoint& pos) final;
|
||||
bool primarySelectStartDrag (const QPoint& pos) final;
|
||||
bool secondarySelectStartDrag (const QPoint& pos) final;
|
||||
|
||||
/// Handle shape edit behavior during dragging
|
||||
void drag (const QPoint& pos, int diffX, int diffY, double speedFactor) final;
|
||||
|
||||
/// End shape editing command macro
|
||||
void dragCompleted(const QPoint& pos) final;
|
||||
|
||||
/// Cancel shape editing, and reset all pending changes
|
||||
void dragAborted() final;
|
||||
|
||||
void dragWheel (int diff, double speedFactor) final;
|
||||
void dragMoveEvent (QDragMoveEvent *event) final;
|
||||
|
||||
private:
|
||||
|
||||
/// Remove duplicates and sort mAlteredCells, then limitAlteredHeights forward and reverse
|
||||
void sortAndLimitAlteredCells();
|
||||
|
||||
/// Move pending alteredHeights changes to omwgame/omwaddon -data
|
||||
void applyTerrainEditChanges();
|
||||
|
||||
/// Handle brush mechanics for shape editing
|
||||
void editTerrainShapeGrid (const std::pair<int, int>& vertexCoords, bool dragOperation);
|
||||
|
||||
/// Calculate height, when aiming for bump-shaped terrain change
|
||||
float calculateBumpShape(float distance, int radius, float height);
|
||||
|
||||
/// set the target height for flatten tool
|
||||
void setFlattenToolTargetHeight(const WorldspaceHitResult& hit);
|
||||
|
||||
/// 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);
|
||||
|
||||
/// Get altered height values around one vertex
|
||||
void updateKeyHeightValues(const CSMWorld::CellCoordinates& cellCoords, int inCellX, int inCellY, float* thisHeight,
|
||||
float* thisAlteredHeight, float* leftHeight, float* leftAlteredHeight, float* upHeight, float* upAlteredHeight,
|
||||
float* rightHeight, float* rightAlteredHeight, float* downHeight, float* downAlteredHeight);
|
||||
|
||||
///Limit steepness based on either X or Y and return false if steepness is limited
|
||||
void compareAndLimit(const CSMWorld::CellCoordinates& cellCoords, int inCellX, int inCellY, float* limitedAlteredHeightXAxis,
|
||||
float* limitedAlteredHeightYAxis, bool* steepnessIsWithinLimits);
|
||||
|
||||
/// Check that the edit doesn't break save format limits, fix if necessary, return true if slope steepness is within limits
|
||||
bool limitAlteredHeights(const CSMWorld::CellCoordinates& cellCoords, bool reverseMode = false);
|
||||
|
||||
/// 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, const std::string& cellId);
|
||||
|
||||
/// Push land normals edits to command macro
|
||||
void pushNormalsEditToCommand(const CSMWorld::LandNormalsColumn::DataType& newLandGrid, CSMDoc::Document& document,
|
||||
CSMWorld::IdTable& landTable, const std::string& cellId);
|
||||
|
||||
/// Generate new land map LOD
|
||||
void pushLodToCommand(const CSMWorld::LandMapLodColumn::DataType& newLandMapLod, CSMDoc::Document& document,
|
||||
CSMWorld::IdTable& landTable, const std::string& cellId);
|
||||
|
||||
bool noCell(const std::string& cellId);
|
||||
|
||||
bool noLand(const std::string& cellId);
|
||||
|
||||
bool noLandLoaded(const std::string& cellId);
|
||||
|
||||
bool isLandLoaded(const std::string& cellId);
|
||||
|
||||
/// Create new blank height record and new normals, if there are valid adjancent cell, take sample points and set the average height based on that
|
||||
void createNewLandData(const CSMWorld::CellCoordinates& cellCoords);
|
||||
|
||||
/// Create new cell and land if needed, only user tools may ask for opening new cells (useTool == false is for automated land changes)
|
||||
bool allowLandShapeEditing(const std::string& textureFileName, bool useTool = true);
|
||||
|
||||
/// Bind the edging vertice to the values of the adjancent cells
|
||||
void fixEdges(CSMWorld::CellCoordinates cellCoords);
|
||||
|
||||
std::string mBrushTexture;
|
||||
int mBrushSize = 1;
|
||||
CSVWidget::BrushShape mBrushShape = CSVWidget::BrushShape_Point;
|
||||
std::vector<std::pair<int, int>> mCustomBrushShape;
|
||||
CSVWidget::SceneToolShapeBrush *mShapeBrushScenetool = nullptr;
|
||||
int mDragMode = InteractionType_None;
|
||||
osg::Group* mParentNode;
|
||||
bool mIsEditing = false;
|
||||
std::unique_ptr<TerrainSelection> mTerrainShapeSelection;
|
||||
int mTotalDiffY = 0;
|
||||
std::vector<CSMWorld::CellCoordinates> mAlteredCells;
|
||||
osg::Vec3d mEditingPos;
|
||||
int mShapeEditTool = ShapeEditTool_Drag;
|
||||
int mShapeEditToolStrength = 8;
|
||||
int mTargetHeight = 0;
|
||||
|
||||
PagedWorldspaceWidget& getPagedWorldspaceWidget();
|
||||
|
||||
public slots:
|
||||
void setBrushSize(int brushSize);
|
||||
void setBrushShape(CSVWidget::BrushShape brushShape);
|
||||
void setShapeEditTool(int shapeEditTool);
|
||||
void setShapeEditToolStrength(int shapeEditToolStrength);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#endif
|
|
@ -3,13 +3,15 @@
|
|||
#include "../../model/world/land.hpp"
|
||||
#include "../../model/world/landtexture.hpp"
|
||||
|
||||
#include <components/esmterrain/storage.hpp>
|
||||
|
||||
namespace CSVRender
|
||||
{
|
||||
|
||||
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,10 +35,118 @@ 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()
|
||||
{
|
||||
std::fill(std::begin(mAlteredHeight), std::end(mAlteredHeight), 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) : nullptr;
|
||||
if (data) height = getVertexHeight(data, inCellX, inCellY);
|
||||
}
|
||||
else return height;
|
||||
return mAlteredHeight[inCellY*ESM::Land::LAND_SIZE + inCellX] + height;
|
||||
|
||||
}
|
||||
|
||||
float* TerrainStorage::getAlteredHeight(int inCellX, int inCellY)
|
||||
{
|
||||
return &mAlteredHeight[inCellY*ESM::Land::LAND_SIZE + inCellX];
|
||||
}
|
||||
|
||||
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
|
||||
throw std::runtime_error("getBounds not implemented");
|
||||
}
|
||||
|
||||
int TerrainStorage::getThisHeight(int col, int row, const ESM::Land::LandData *heightData) const
|
||||
{
|
||||
return heightData->mHeights[col*ESM::Land::LAND_SIZE + row] +
|
||||
mAlteredHeight[static_cast<unsigned int>(col*ESM::Land::LAND_SIZE + row)];
|
||||
}
|
||||
|
||||
int TerrainStorage::getLeftHeight(int col, int row, const ESM::Land::LandData *heightData) const
|
||||
{
|
||||
return heightData->mHeights[(col)*ESM::Land::LAND_SIZE + row - 1] +
|
||||
mAlteredHeight[static_cast<unsigned int>((col)*ESM::Land::LAND_SIZE + row - 1)];
|
||||
}
|
||||
|
||||
int TerrainStorage::getRightHeight(int col, int row, const ESM::Land::LandData *heightData) const
|
||||
{
|
||||
return heightData->mHeights[col*ESM::Land::LAND_SIZE + row + 1] +
|
||||
mAlteredHeight[static_cast<unsigned int>(col*ESM::Land::LAND_SIZE + row + 1)];
|
||||
}
|
||||
|
||||
int TerrainStorage::getUpHeight(int col, int row, const ESM::Land::LandData *heightData) const
|
||||
{
|
||||
return heightData->mHeights[(col - 1)*ESM::Land::LAND_SIZE + row] +
|
||||
mAlteredHeight[static_cast<unsigned int>((col - 1)*ESM::Land::LAND_SIZE + row)];
|
||||
}
|
||||
|
||||
int TerrainStorage::getDownHeight(int col, int row, const ESM::Land::LandData *heightData) const
|
||||
{
|
||||
return heightData->mHeights[(col + 1)*ESM::Land::LAND_SIZE + row] +
|
||||
mAlteredHeight[static_cast<unsigned int>((col + 1)*ESM::Land::LAND_SIZE + row)];
|
||||
}
|
||||
|
||||
int TerrainStorage::getHeightDifferenceToLeft(int col, int row, const ESM::Land::LandData *heightData) const
|
||||
{
|
||||
return abs(getThisHeight(col, row, heightData) - getLeftHeight(col, row, heightData));
|
||||
}
|
||||
|
||||
int TerrainStorage::getHeightDifferenceToRight(int col, int row, const ESM::Land::LandData *heightData) const
|
||||
{
|
||||
return abs(getThisHeight(col, row, heightData) - getRightHeight(col, row, heightData));
|
||||
}
|
||||
|
||||
int TerrainStorage::getHeightDifferenceToUp(int col, int row, const ESM::Land::LandData *heightData) const
|
||||
{
|
||||
return abs(getThisHeight(col, row, heightData) - getUpHeight(col, row, heightData));
|
||||
}
|
||||
|
||||
int TerrainStorage::getHeightDifferenceToDown(int col, int row, const ESM::Land::LandData *heightData) const
|
||||
{
|
||||
return abs(getThisHeight(col, row, heightData) - getDownHeight(col, row, heightData));
|
||||
}
|
||||
|
||||
bool TerrainStorage::leftOrUpIsOverTheLimit(int col, int row, int heightWarningLimit, const ESM::Land::LandData *heightData) const
|
||||
{
|
||||
return getHeightDifferenceToLeft(col, row, heightData) >= heightWarningLimit ||
|
||||
getHeightDifferenceToUp(col, row, heightData) >= heightWarningLimit;
|
||||
}
|
||||
|
||||
bool TerrainStorage::rightOrDownIsOverTheLimit(int col, int row, int heightWarningLimit, const ESM::Land::LandData *heightData) const
|
||||
{
|
||||
return getHeightDifferenceToRight(col, row, heightData) >= heightWarningLimit ||
|
||||
getHeightDifferenceToDown(col, row, heightData) >= heightWarningLimit;
|
||||
}
|
||||
|
||||
void TerrainStorage::adjustColor(int col, int row, const ESM::Land::LandData *heightData, osg::Vec4ub& color) const
|
||||
{
|
||||
// Highlight broken height changes
|
||||
int heightWarningLimit = 1024;
|
||||
if (((col > 0 && row > 0) && leftOrUpIsOverTheLimit(col, row, heightWarningLimit, heightData)) ||
|
||||
((col < ESM::Land::LAND_SIZE - 1 && row < ESM::Land::LAND_SIZE - 1) && rightOrDownIsOverTheLimit(col, row, heightWarningLimit, heightData)))
|
||||
{
|
||||
color.r() = 255;
|
||||
color.g() = 0;
|
||||
color.b() = 0;
|
||||
}
|
||||
}
|
||||
|
||||
float TerrainStorage::getAlteredHeight(int col, int row) const
|
||||
{
|
||||
return mAlteredHeight[static_cast<unsigned int>(col*ESM::Land::LAND_SIZE + row)];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
#ifndef OPENCS_RENDER_TERRAINSTORAGE_H
|
||||
#define OPENCS_RENDER_TERRAINSTORAGE_H
|
||||
|
||||
#include <array>
|
||||
|
||||
#include <components/esmterrain/storage.hpp>
|
||||
|
||||
#include "../../model/world/data.hpp"
|
||||
|
||||
namespace CSVRender
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief A bridge between the terrain component and OpenCS's terrain data storage.
|
||||
*/
|
||||
|
@ -15,13 +16,34 @@ namespace CSVRender
|
|||
{
|
||||
public:
|
||||
TerrainStorage(const CSMWorld::Data& data);
|
||||
void setAlteredHeight(int inCellX, int inCellY, float heightMap);
|
||||
void resetHeights();
|
||||
float getSumOfAlteredAndTrueHeight(int cellX, int cellY, int inCellX, int inCellY);
|
||||
float* getAlteredHeight(int inCellX, int inCellY);
|
||||
|
||||
private:
|
||||
const CSMWorld::Data& mData;
|
||||
std::array<float, ESM::Land::LAND_SIZE * ESM::Land::LAND_SIZE> mAlteredHeight;
|
||||
|
||||
virtual osg::ref_ptr<const ESMTerrain::LandObject> getLand (int cellX, int cellY) override;
|
||||
virtual const ESM::LandTexture* getLandTexture(int index, short plugin) override;
|
||||
osg::ref_ptr<const ESMTerrain::LandObject> getLand (int cellX, int cellY) final;
|
||||
const ESM::LandTexture* getLandTexture(int index, short plugin) final;
|
||||
|
||||
virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY) override;
|
||||
void getBounds(float& minX, float& maxX, float& minY, float& maxY) final;
|
||||
|
||||
int getThisHeight(int col, int row, const ESM::Land::LandData *heightData) const;
|
||||
int getLeftHeight(int col, int row, const ESM::Land::LandData *heightData) const;
|
||||
int getRightHeight(int col, int row, const ESM::Land::LandData *heightData) const;
|
||||
int getUpHeight(int col, int row, const ESM::Land::LandData *heightData) const;
|
||||
int getDownHeight(int col, int row, const ESM::Land::LandData *heightData) const;
|
||||
int getHeightDifferenceToLeft(int col, int row, const ESM::Land::LandData *heightData) const;
|
||||
int getHeightDifferenceToRight(int col, int row, const ESM::Land::LandData *heightData) const;
|
||||
int getHeightDifferenceToUp(int col, int row, const ESM::Land::LandData *heightData) const;
|
||||
int getHeightDifferenceToDown(int col, int row, const ESM::Land::LandData *heightData) const;
|
||||
bool leftOrUpIsOverTheLimit(int col, int row, int heightWarningLimit, const ESM::Land::LandData *heightData) const;
|
||||
bool rightOrDownIsOverTheLimit(int col, int row, int heightWarningLimit, const ESM::Land::LandData *heightData) const;
|
||||
|
||||
void adjustColor(int col, int row, const ESM::Land::LandData *heightData, osg::Vec4ub& color) const final;
|
||||
float getAlteredHeight(int col, int row) const final;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
14
apps/opencs/view/widget/brushshapes.hpp
Normal file
14
apps/opencs/view/widget/brushshapes.hpp
Normal file
|
@ -0,0 +1,14 @@
|
|||
#ifndef CSV_WIDGET_BRUSHSHAPES_H
|
||||
#define CSV_WIDGET_BRUSHSHAPES_H
|
||||
|
||||
namespace CSVWidget
|
||||
{
|
||||
enum BrushShape
|
||||
{
|
||||
BrushShape_Point,
|
||||
BrushShape_Square,
|
||||
BrushShape_Circle,
|
||||
BrushShape_Custom
|
||||
};
|
||||
}
|
||||
#endif
|
263
apps/opencs/view/widget/scenetoolshapebrush.cpp
Normal file
263
apps/opencs/view/widget/scenetoolshapebrush.cpp
Normal file
|
@ -0,0 +1,263 @@
|
|||
#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 "brushshapes.hpp"
|
||||
#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->setTickPosition(QSlider::TicksBothSides);
|
||||
mBrushSizeSlider->setTickInterval(10);
|
||||
mBrushSizeSlider->setRange(1, CSMPrefs::get()["3D Scene Editing"]["shapebrush-maximumsize"].toInt());
|
||||
mBrushSizeSlider->setSingleStep(1);
|
||||
|
||||
mBrushSizeSpinBox->setRange(1, CSMPrefs::get()["3D Scene Editing"]["shapebrush-maximumsize"].toInt());
|
||||
mBrushSizeSpinBox->setSingleStep(1);
|
||||
|
||||
QHBoxLayout *layoutSliderSize = new QHBoxLayout;
|
||||
layoutSliderSize->addWidget(mBrushSizeSlider);
|
||||
layoutSliderSize->addWidget(mBrushSizeSpinBox);
|
||||
|
||||
connect(mBrushSizeSlider, SIGNAL(valueChanged(int)), mBrushSizeSpinBox, SLOT(setValue(int)));
|
||||
connect(mBrushSizeSpinBox, SIGNAL(valueChanged(int)), mBrushSizeSlider, SLOT(setValue(int)));
|
||||
|
||||
setLayout(layoutSliderSize);
|
||||
}
|
||||
|
||||
CSVWidget::ShapeBrushWindow::ShapeBrushWindow(CSMDoc::Document& document, QWidget *parent)
|
||||
: QFrame(parent, Qt::Popup),
|
||||
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);
|
||||
mToolStrengthSlider->setValue(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 = BrushShape_Point;
|
||||
if(mButtonSquare->isChecked()) mBrushShape = BrushShape_Square;
|
||||
if(mButtonCircle->isChecked()) mBrushShape = BrushShape_Circle;
|
||||
if(mButtonCustom->isChecked()) mBrushShape = BrushShape_Custom;
|
||||
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(CSVWidget::BrushShape)), this, SLOT(setButtonIcon(CSVWidget::BrushShape)));
|
||||
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 (CSVWidget::BrushShape brushShape)
|
||||
{
|
||||
QString tooltip = "Change brush settings <p>Currently selected: ";
|
||||
|
||||
switch (brushShape)
|
||||
{
|
||||
case BrushShape_Point:
|
||||
|
||||
setIcon (QIcon (QPixmap (":scenetoolbar/brush-point")));
|
||||
tooltip += mShapeBrushWindow->toolTipPoint;
|
||||
break;
|
||||
|
||||
case BrushShape_Square:
|
||||
|
||||
setIcon (QIcon (QPixmap (":scenetoolbar/brush-square")));
|
||||
tooltip += mShapeBrushWindow->toolTipSquare;
|
||||
break;
|
||||
|
||||
case BrushShape_Circle:
|
||||
|
||||
setIcon (QIcon (QPixmap (":scenetoolbar/brush-circle")));
|
||||
tooltip += mShapeBrushWindow->toolTipCircle;
|
||||
break;
|
||||
|
||||
case BrushShape_Custom:
|
||||
|
||||
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();
|
||||
}
|
127
apps/opencs/view/widget/scenetoolshapebrush.hpp
Normal file
127
apps/opencs/view/widget/scenetoolshapebrush.hpp
Normal file
|
@ -0,0 +1,127 @@
|
|||
#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 "brushshapes.hpp"
|
||||
#include "scenetool.hpp"
|
||||
|
||||
#include "../../model/doc/document.hpp"
|
||||
#endif
|
||||
|
||||
class QTableWidget;
|
||||
|
||||
namespace CSVRender
|
||||
{
|
||||
class TerrainShapeMode;
|
||||
}
|
||||
|
||||
namespace CSVWidget
|
||||
{
|
||||
/// \brief Layout-box for some brush button settings
|
||||
class ShapeBrushSizeControls : public QGroupBox
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ShapeBrushSizeControls(const QString &title, QWidget *parent);
|
||||
|
||||
private:
|
||||
QSlider *mBrushSizeSlider = new QSlider(Qt::Horizontal);
|
||||
QSpinBox *mBrushSizeSpinBox = new QSpinBox;
|
||||
|
||||
friend class SceneToolShapeBrush;
|
||||
friend class CSVRender::TerrainShapeMode;
|
||||
};
|
||||
|
||||
/// \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 with custom brush, defined by terrain selection";
|
||||
|
||||
private:
|
||||
CSVWidget::BrushShape mBrushShape = CSVWidget::BrushShape_Point;
|
||||
int mBrushSize = 1;
|
||||
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(CSVWidget::BrushShape 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(CSVWidget::BrushShape brushShape);
|
||||
void clicked (const QModelIndex& index);
|
||||
virtual void activate();
|
||||
|
||||
signals:
|
||||
void passEvent(QDropEvent *event);
|
||||
void passEvent(QDragEnterEvent *event);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -255,7 +255,7 @@ namespace ESMTerrain
|
|||
(*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);
|
||||
height + getAlteredHeight(col, row));
|
||||
|
||||
if (normalData)
|
||||
{
|
||||
|
@ -291,6 +291,8 @@ namespace ESMTerrain
|
|||
color.b() = 255;
|
||||
}
|
||||
|
||||
adjustColor(col, row, heightData, color); //Does nothing by default, override in OpenMW-CS
|
||||
|
||||
// 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);
|
||||
|
@ -521,13 +523,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));
|
||||
|
@ -540,6 +535,15 @@ namespace ESMTerrain
|
|||
}
|
||||
}
|
||||
|
||||
void Storage::adjustColor(int col, int row, const ESM::Land::LandData *heightData, osg::Vec4ub& color) const
|
||||
{
|
||||
}
|
||||
|
||||
float Storage::getAlteredHeight(int col, int row) const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
Terrain::LayerInfo Storage::getLayerInfo(const std::string& texture)
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mLayerInfoMutex);
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#ifndef COMPONENTS_ESM_TERRAIN_STORAGE_H
|
||||
#define COMPONENTS_ESM_TERRAIN_STORAGE_H
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include <OpenThreads/Mutex>
|
||||
|
||||
#include <components/terrain/storage.hpp>
|
||||
|
@ -107,6 +109,13 @@ namespace ESMTerrain
|
|||
|
||||
virtual int getBlendmapScale(float chunkSize);
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
private:
|
||||
const VFS::Manager* mVFS;
|
||||
|
||||
|
@ -114,10 +123,11 @@ namespace ESMTerrain
|
|||
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);
|
||||
|
||||
virtual void adjustColor(int col, int row, const ESM::Land::LandData *heightData, osg::Vec4ub& color) const;
|
||||
virtual float getAlteredHeight(int col, int row) const;
|
||||
|
||||
// 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>
|
||||
|
|
Loading…
Reference in a new issue