1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-01-29 09:45:32 +00:00

Merge pull request #2520 from unelsson/transientlandshapeedit

[Review phase] Editor: Transient land shape editing
This commit is contained in:
Bret Curtis 2019-10-25 00:26:43 +02:00 committed by GitHub
commit 9f039fac87
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 2317 additions and 35 deletions

View file

@ -187,6 +187,7 @@
Feature #4784: Launcher: Duplicate Content Lists Feature #4784: Launcher: Duplicate Content Lists
Feature #4812: Support NiSwitchNode Feature #4812: Support NiSwitchNode
Feature #4836: Daytime node switch Feature #4836: Daytime node switch
Feature #4840: Editor: Transient terrain change support
Feature #4859: Make water reflections more configurable Feature #4859: Make water reflections more configurable
Feature #4882: Support for NiPalette node Feature #4882: Support for NiPalette node
Feature #4887: Add openmw command option to set initial random seed Feature #4887: Add openmw command option to set initial random seed
@ -213,6 +214,7 @@
Feature #5132: Unique animations for different weapon types Feature #5132: Unique animations for different weapon types
Feature #5146: Safe Dispose corpse Feature #5146: Safe Dispose corpse
Feature #5147: Show spell magicka cost in spell buying window Feature #5147: Show spell magicka cost in spell buying window
Feature #5170: Editor: Land shape editing, land selection
Feature #5193: Weapon sheathing Feature #5193: Weapon sheathing
Task #4686: Upgrade media decoder to a more current FFmpeg API Task #4686: Upgrade media decoder to a more current FFmpeg API
Task #4695: Optimize Distant Terrain memory consumption Task #4695: Optimize Distant Terrain memory consumption

View file

@ -40,6 +40,8 @@ New Features:
New Editor Features: New Editor Features:
- "Faction Ranks" table for "Faction" records (#4209) - "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: Bug Fixes:
- Scripted Items cannot be stacked anymore to avoid multiple script execution (#2969) - Scripted Items cannot be stacked anymore to avoid multiple script execution (#2969)

View file

@ -82,14 +82,14 @@ opencs_units_noqt (view/world
opencs_units (view/widget opencs_units (view/widget
scenetoolbar scenetool scenetoolmode pushbutton scenetooltoggle scenetoolrun modebutton scenetoolbar scenetool scenetoolmode pushbutton scenetooltoggle scenetoolrun modebutton
scenetooltoggle2 scenetooltexturebrush completerpopup coloreditor colorpickerpopup droplineedit scenetooltoggle2 scenetooltexturebrush scenetoolshapebrush completerpopup coloreditor colorpickerpopup droplineedit
) )
opencs_units (view/render opencs_units (view/render
scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget
previewwidget editmode instancemode instanceselectionmode instancemovemode previewwidget editmode instancemode instanceselectionmode instancemovemode
orbitcameramode pathgridmode selectionmode pathgridselectionmode cameracontroller orbitcameramode pathgridmode selectionmode pathgridselectionmode cameracontroller
cellwater terraintexturemode actor terrainselection cellwater terraintexturemode actor terrainselection terrainshapemode
) )
opencs_units_noqt (view/render opencs_units_noqt (view/render

View file

@ -170,7 +170,7 @@ void CSMPrefs::State::declare()
"list go to the first/last item"); "list go to the first/last item");
declareCategory ("3D Scene Input"); declareCategory ("3D Scene Input");
declareDouble ("navi-wheel-factor", "Camera Zoom Sensitivity", 8).setRange(-100.0, 100.0); 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); declareDouble ("s-navi-sensitivity", "Secondary Camera Movement Sensitivity", 50.0).setRange(-1000.0, 1000.0);
declareSeparator(); 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); 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); 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-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); declareDouble ("navi-free-speed-mult", "Free Camera Speed Multiplier (from Modifier)", 8).setRange(0.001, 1000.0);
declareSeparator(); declareSeparator();
@ -242,12 +242,24 @@ void CSMPrefs::State::declare()
addValues (insertOutsideCell); addValues (insertOutsideCell);
declareEnum ("outside-visible-drop", "Handling drops outside of visible cells", showAndInsert). declareEnum ("outside-visible-drop", "Handling drops outside of visible cells", showAndInsert).
addValues (insertOutsideVisibleCell); 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); 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); addValues (landeditOutsideVisibleCell);
declareInt ("texturebrush-maximumsize", "Maximum texture brush size", 50). declareInt ("texturebrush-maximumsize", "Maximum texture brush size", 50).
setMin (1); 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). declareBool ("open-list-view", "Open displays list view", false).
setTooltip ("When opening a reference from the scene view, it will open the" setTooltip ("When opening a reference from the scene view, it will open the"
" instance list view instead of the individual instance record view."); " instance list view instead of the individual instance record view.");

View file

@ -89,7 +89,7 @@ namespace CSMWorld
DataType values(Size, 0); DataType values(Size, 0);
if (land.isDataLoaded(Land::DATA_WNAM)) if (land.mDataTypes & Land::DATA_WNAM)
{ {
for (int i = 0; i < Size; ++i) for (int i = 0; i < Size; ++i)
values[i] = land.mWnam[i]; values[i] = land.mWnam[i];

View file

@ -134,7 +134,7 @@ void CSVRender::Cell::updateLand()
else else
{ {
mTerrain.reset(new Terrain::TerrainGrid(mCellNode, mCellNode, 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); mTerrain->loadCell(esmLand.mX, esmLand.mY);
@ -149,7 +149,6 @@ void CSVRender::Cell::updateLand()
} }
// No land data // No land data
mLandDeleted = true;
unloadLand(); 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); std::pair<CSMWorld::CellCoordinates, bool> result = CSMWorld::CellCoordinates::fromId (id);
mTerrainStorage = new TerrainStorage(mData);
if (result.second) if (result.second)
mCoordinates = result.first; mCoordinates = result.first;
@ -347,6 +348,28 @@ bool CSVRender::Cell::referenceAdded (const QModelIndex& parent, int start, int
return addObjects (start, end); 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() void CSVRender::Cell::pathgridModified()
{ {
if (mPathgrid) if (mPathgrid)

View file

@ -9,6 +9,7 @@
#include <osg/ref_ptr> #include <osg/ref_ptr>
#include "../../model/world/cellcoordinates.hpp" #include "../../model/world/cellcoordinates.hpp"
#include "terrainstorage.hpp"
class QModelIndex; class QModelIndex;
@ -58,6 +59,7 @@ namespace CSVRender
int mSubMode; int mSubMode;
unsigned int mSubModeElementMask; unsigned int mSubModeElementMask;
bool mUpdateLand, mLandDeleted; bool mUpdateLand, mLandDeleted;
TerrainStorage *mTerrainStorage;
/// Ignored if cell does not have an object with the given ID. /// Ignored if cell does not have an object with the given ID.
/// ///
@ -118,6 +120,14 @@ namespace CSVRender
/// this cell? /// this cell?
bool referenceAdded (const QModelIndex& parent, int start, int end); 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 pathgridModified();
void pathgridRemoved(); void pathgridRemoved();

View file

@ -25,6 +25,7 @@
#include "cameracontroller.hpp" #include "cameracontroller.hpp"
#include "cellarrow.hpp" #include "cellarrow.hpp"
#include "terraintexturemode.hpp" #include "terraintexturemode.hpp"
#include "terrainshapemode.hpp"
bool CSVRender::PagedWorldspaceWidget::adjustCells() bool CSVRender::PagedWorldspaceWidget::adjustCells()
{ {
@ -137,11 +138,9 @@ void CSVRender::PagedWorldspaceWidget::addEditModeSelectorButtons (
/// \todo replace EditMode with suitable subclasses /// \todo replace EditMode with suitable subclasses
tool->addButton ( tool->addButton (
new EditMode (this, QIcon (":placeholder"), Mask_Reference, "Terrain shape editing"), new TerrainShapeMode (this, mRootNode, tool), "terrain-shape");
"terrain-shape");
tool->addButton ( tool->addButton (
new TerrainTextureMode (this, mRootNode, tool), new TerrainTextureMode (this, mRootNode, tool), "terrain-texture");
"terrain-texture");
tool->addButton ( tool->addButton (
new EditMode (this, QIcon (":placeholder"), Mask_Reference, "Terrain vertex paint editing"), new EditMode (this, QIcon (":placeholder"), Mask_Reference, "Terrain vertex paint editing"),
"terrain-vertex"); "terrain-vertex");
@ -791,6 +790,36 @@ CSVRender::Cell* CSVRender::PagedWorldspaceWidget::getCell(const osg::Vec3d& poi
return 0; 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 ( std::vector<osg::ref_ptr<CSVRender::TagBase> > CSVRender::PagedWorldspaceWidget::getSelection (
unsigned int elementMask) const unsigned int elementMask) const
{ {

View file

@ -124,6 +124,14 @@ namespace CSVRender
virtual Cell* getCell(const osg::Vec3d& point) const; 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) virtual std::vector<osg::ref_ptr<TagBase> > getSelection (unsigned int elementMask)
const; const;

View file

@ -249,13 +249,11 @@ int CSVRender::TerrainSelection::calculateLandHeight(int x, int y) // global ver
int localX = x - cellX * (ESM::Land::LAND_SIZE - 1); int localX = x - cellX * (ESM::Land::LAND_SIZE - 1);
int localY = y - cellY * (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(); float landHeight = 0.f;
CSMWorld::IdTable& landTable = dynamic_cast<CSMWorld::IdTable&> ( if (CSVRender::Cell* cell = dynamic_cast<CSVRender::Cell*>(mWorldspaceWidget->getCell(coords)))
*document.getData().getTableModel (CSMWorld::UniversalId::Type_Land)); landHeight = cell->getSumOfAlteredAndTrueHeight(cellX, cellY, localX, localY);
int landshapeColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandHeightsIndex);
const CSMWorld::LandHeightsColumn::DataType mPointer = landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value<CSMWorld::LandHeightsColumn::DataType>();
return mPointer[localY*ESM::Land::LAND_SIZE + localX]; return landHeight;
} }

File diff suppressed because it is too large Load diff

View 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

View file

@ -3,13 +3,15 @@
#include "../../model/world/land.hpp" #include "../../model/world/land.hpp"
#include "../../model/world/landtexture.hpp" #include "../../model/world/landtexture.hpp"
#include <components/esmterrain/storage.hpp>
namespace CSVRender namespace CSVRender
{ {
TerrainStorage::TerrainStorage(const CSMWorld::Data &data) TerrainStorage::TerrainStorage(const CSMWorld::Data &data)
: ESMTerrain::Storage(data.getResourceSystem()->getVFS()) : ESMTerrain::Storage(data.getResourceSystem()->getVFS())
, mData(data) , mData(data)
{ {
resetHeights();
} }
osg::ref_ptr<const ESMTerrain::LandObject> TerrainStorage::getLand(int cellX, int cellY) osg::ref_ptr<const ESMTerrain::LandObject> TerrainStorage::getLand(int cellX, int cellY)
@ -33,10 +35,118 @@ namespace CSVRender
return &mData.getLandTextures().getRecord(row).get(); 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) 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 // 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"); 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)];
}
} }

View file

@ -1,13 +1,14 @@
#ifndef OPENCS_RENDER_TERRAINSTORAGE_H #ifndef OPENCS_RENDER_TERRAINSTORAGE_H
#define OPENCS_RENDER_TERRAINSTORAGE_H #define OPENCS_RENDER_TERRAINSTORAGE_H
#include <array>
#include <components/esmterrain/storage.hpp> #include <components/esmterrain/storage.hpp>
#include "../../model/world/data.hpp" #include "../../model/world/data.hpp"
namespace CSVRender namespace CSVRender
{ {
/** /**
* @brief A bridge between the terrain component and OpenCS's terrain data storage. * @brief A bridge between the terrain component and OpenCS's terrain data storage.
*/ */
@ -15,13 +16,34 @@ namespace CSVRender
{ {
public: public:
TerrainStorage(const CSMWorld::Data& data); 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: private:
const CSMWorld::Data& mData; 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; osg::ref_ptr<const ESMTerrain::LandObject> getLand (int cellX, int cellY) final;
virtual const ESM::LandTexture* getLandTexture(int index, short plugin) override; 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;
}; };
} }

View file

@ -150,6 +150,11 @@ CSVRender::Cell* CSVRender::UnpagedWorldspaceWidget::getCell(const osg::Vec3d& p
return mCell.get(); 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 ( std::vector<osg::ref_ptr<CSVRender::TagBase> > CSVRender::UnpagedWorldspaceWidget::getSelection (
unsigned int elementMask) const unsigned int elementMask) const
{ {

View file

@ -17,6 +17,7 @@ namespace CSMDoc
namespace CSMWorld namespace CSMWorld
{ {
class IdTable; class IdTable;
class CellCoordinates;
} }
namespace CSVRender namespace CSVRender
@ -63,6 +64,8 @@ namespace CSVRender
virtual Cell* getCell(const osg::Vec3d& point) const; 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) virtual std::vector<osg::ref_ptr<TagBase> > getSelection (unsigned int elementMask)
const; const;

View file

@ -17,6 +17,7 @@ namespace CSMPrefs
namespace CSMWorld namespace CSMWorld
{ {
class CellCoordinates;
class UniversalId; class UniversalId;
} }
@ -170,6 +171,8 @@ namespace CSVRender
/// \note Returns the cell if it exists, otherwise a null pointer /// \note Returns the cell if it exists, otherwise a null pointer
virtual Cell* getCell(const osg::Vec3d& point) const = 0; 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) virtual std::vector<osg::ref_ptr<TagBase> > getSelection (unsigned int elementMask)
const = 0; const = 0;

View 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

View 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();
}

View 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

View file

@ -255,7 +255,7 @@ namespace ESMTerrain
(*positions)[static_cast<unsigned int>(vertX*numVerts + vertY)] (*positions)[static_cast<unsigned int>(vertX*numVerts + vertY)]
= osg::Vec3f((vertX / float(numVerts - 1) - 0.5f) * size * Constants::CellSizeInUnits, = osg::Vec3f((vertX / float(numVerts - 1) - 0.5f) * size * Constants::CellSizeInUnits,
(vertY / float(numVerts - 1) - 0.5f) * size * Constants::CellSizeInUnits, (vertY / float(numVerts - 1) - 0.5f) * size * Constants::CellSizeInUnits,
height); height + getAlteredHeight(col, row));
if (normalData) if (normalData)
{ {
@ -291,6 +291,8 @@ namespace ESMTerrain
color.b() = 255; 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... // Unlike normals, colors mostly connect seamlessly between cells, but not always...
if (col == ESM::Land::LAND_SIZE-1 || row == ESM::Land::LAND_SIZE-1) if (col == ESM::Land::LAND_SIZE-1 || row == ESM::Land::LAND_SIZE-1)
fixColour(color, cellX, cellY, col, row, cache); 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) const LandObject* Storage::getLand(int cellX, int cellY, LandCache& cache)
{ {
LandCache::Map::iterator found = cache.mMap.find(std::make_pair(cellX, cellY)); 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) Terrain::LayerInfo Storage::getLayerInfo(const std::string& texture)
{ {
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mLayerInfoMutex); OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mLayerInfoMutex);

View file

@ -1,6 +1,8 @@
#ifndef COMPONENTS_ESM_TERRAIN_STORAGE_H #ifndef COMPONENTS_ESM_TERRAIN_STORAGE_H
#define COMPONENTS_ESM_TERRAIN_STORAGE_H #define COMPONENTS_ESM_TERRAIN_STORAGE_H
#include <cassert>
#include <OpenThreads/Mutex> #include <OpenThreads/Mutex>
#include <components/terrain/storage.hpp> #include <components/terrain/storage.hpp>
@ -107,6 +109,13 @@ namespace ESMTerrain
virtual int getBlendmapScale(float chunkSize); 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: private:
const VFS::Manager* mVFS; 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 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 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); 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 // Since plugins can define new texture palettes, we need to know the plugin index too
// in order to retrieve the correct texture name. // in order to retrieve the correct texture name.
// pair <texture id, plugin id> // pair <texture id, plugin id>