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