Merge pull request #440 from OpenMW/master

Add OpenMW commits up to 27 May 2018
sol2-server-rewrite
David Cernat 6 years ago committed by GitHub
commit 669d4d3d7e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -6,4 +6,6 @@ DATE=`date +'%d%m%Y'`
SHORT_COMMIT=`git rev-parse --short ${TRAVIS_COMMIT}`
TARGET_FILENAME="OpenMW-${DATE}-${SHORT_COMMIT}.dmg"
curl --ssl --ftp-create-dirs -T *.dmg -u $OSX_FTP_USER:$OSX_FTP_PASSWORD "${OSX_FTP_URL}${TARGET_FILENAME}"
if ! curl --ssl -u $OSX_FTP_USER:$OSX_FTP_PASSWORD "${OSX_FTP_URL}" --silent | grep $SHORT_COMMIT > /dev/null; then
curl --ssl --ftp-create-dirs -T *.dmg -u $OSX_FTP_USER:$OSX_FTP_PASSWORD "${OSX_FTP_URL}${TARGET_FILENAME}"
fi

@ -381,6 +381,8 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clang)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-but-set-parameter")
endif()
elseif (MSVC)
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS} /Zi /bigobj")
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /DEBUG /OPT:REF /OPT:ICF /INCREMENTAL:NO")
# Enable link-time code generation globally for all linking
if (OPENMW_LTO_BUILD)
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /GL")

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

@ -222,7 +222,15 @@ void CSMPrefs::State::declare()
EnumValues insertOutsideVisibleCell;
insertOutsideVisibleCell.add (showAndInsert).add (dontInsert).add (insertAnyway);
declareCategory ("Scene Drops");
EnumValue createAndLandEdit ("Create cell and land, then edit");
EnumValue showAndLandEdit ("Show cell and edit");
EnumValue dontLandEdit ("Discard");
EnumValues landeditOutsideCell;
landeditOutsideCell.add (createAndLandEdit).add (dontLandEdit);
EnumValues landeditOutsideVisibleCell;
landeditOutsideVisibleCell.add (showAndLandEdit).add (dontLandEdit);
declareCategory ("3D Scene Editing");
declareInt ("distance", "Drop Distance", 50).
setTooltip ("If an instance drop can not be placed against another object at the "
"insert point, it will be placed by this distance from the insert point instead");
@ -230,6 +238,12 @@ void CSMPrefs::State::declare()
addValues (insertOutsideCell);
declareEnum ("outside-visible-drop", "Handling drops outside of visible cells", showAndInsert).
addValues (insertOutsideVisibleCell);
declareEnum ("outside-landedit", "Handling land edit outside of cells", createAndLandEdit).
addValues (landeditOutsideCell);
declareEnum ("outside-visible-landedit", "Handling land edit outside of visible cells", showAndLandEdit).
addValues (landeditOutsideVisibleCell);
declareInt ("texturebrush-maximumsize", "Maximum texture brush size", 50).
setMin (1);
declareCategory ("Key Bindings");

@ -28,9 +28,13 @@ void CSMTools::ClassCheckStage::perform (int stage, CSMDoc::Messages& messages)
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Class, class_.mId);
// test for empty name and description
// A class should have a name
if (class_.mName.empty())
messages.push_back (std::make_pair (id, class_.mId + " has an empty name"));
messages.push_back (std::make_pair (id, class_.mId + " doesn't have a name"));
// A playable class should have a description
if (class_.mData.mIsPlayable != 0 && class_.mDescription.empty())
messages.push_back (std::make_pair (id, class_.mId + " doesn't have a description and it's playable"));
// test for invalid attributes
for (int i=0; i<2; ++i)

@ -11,11 +11,6 @@ namespace CSMWorld
bool LandTextureTableProxyModel::filterAcceptsRow (int sourceRow, const QModelIndex& sourceParent) const
{
int columnIndex = mSourceModel->findColumnIndex(Columns::ColumnId_Modification);
QModelIndex index = mSourceModel->index(sourceRow, columnIndex);
if (mSourceModel->data(index).toInt() != RecordBase::State_ModifiedOnly)
return false;
return IdTableProxyModel::filterAcceptsRow(sourceRow, sourceParent);
}
}

@ -551,7 +551,7 @@ void CSVRender::InstanceMode::dropEvent (QDropEvent* event)
if (noCell)
{
std::string mode = CSMPrefs::get()["Scene Drops"]["outside-drop"].toString();
std::string mode = CSMPrefs::get()["3D Scene Editing"]["outside-drop"].toString();
// target cell does not exist
if (mode=="Discard")
@ -585,7 +585,7 @@ void CSVRender::InstanceMode::dropEvent (QDropEvent* event)
{
// target cell exists, but is not shown
std::string mode =
CSMPrefs::get()["Scene Drops"]["outside-visible-drop"].toString();
CSMPrefs::get()["3D Scene Editing"]["outside-visible-drop"].toString();
if (mode=="Discard")
return;

@ -21,6 +21,7 @@
#include "mask.hpp"
#include "cameracontroller.hpp"
#include "cellarrow.hpp"
#include "terraintexturemode.hpp"
bool CSVRender::PagedWorldspaceWidget::adjustCells()
{
@ -136,7 +137,7 @@ void CSVRender::PagedWorldspaceWidget::addEditModeSelectorButtons (
new EditMode (this, QIcon (":placeholder"), Mask_Reference, "Terrain shape editing"),
"terrain-shape");
tool->addButton (
new EditMode (this, QIcon (":placeholder"), Mask_Reference, "Terrain texture editing"),
new TerrainTextureMode (this, tool),
"terrain-texture");
tool->addButton (
new EditMode (this, QIcon (":placeholder"), Mask_Reference, "Terrain vertex paint editing"),

@ -0,0 +1,542 @@
#include "terraintexturemode.hpp"
#include <string>
#include <sstream>
#include <QWidget>
#include <QIcon>
#include <QEvent>
#include <QDropEvent>
#include <QDragEnterEvent>
#include <QDrag>
#include <components/esm/loadland.hpp>
#include "../widget/modebutton.hpp"
#include "../widget/scenetoolbar.hpp"
#include "../widget/scenetooltexturebrush.hpp"
#include "../../model/doc/document.hpp"
#include "../../model/prefs/state.hpp"
#include "../../model/world/columnbase.hpp"
#include "../../model/world/commandmacro.hpp"
#include "../../model/world/commands.hpp"
#include "../../model/world/data.hpp"
#include "../../model/world/idtable.hpp"
#include "../../model/world/idtree.hpp"
#include "../../model/world/land.hpp"
#include "../../model/world/landtexture.hpp"
#include "../../model/world/resourcetable.hpp"
#include "../../model/world/tablemimedata.hpp"
#include "../../model/world/universalid.hpp"
#include "editmode.hpp"
#include "pagedworldspacewidget.hpp"
#include "mask.hpp"
#include "object.hpp" // Something small needed regarding pointers from here ()
#include "worldspacewidget.hpp"
CSVRender::TerrainTextureMode::TerrainTextureMode (WorldspaceWidget *worldspaceWidget, QWidget *parent)
: EditMode (worldspaceWidget, QIcon {":scenetoolbar/editing-terrain-texture"}, Mask_Terrain | Mask_Reference, "Terrain texture editing", parent),
mBrushTexture("L0#0"),
mBrushSize(0),
mBrushShape(0),
mTextureBrushScenetool(0)
{
}
void CSVRender::TerrainTextureMode::activate(CSVWidget::SceneToolbar* toolbar)
{
if(!mTextureBrushScenetool)
{
mTextureBrushScenetool = new CSVWidget::SceneToolTextureBrush (toolbar, "scenetooltexturebrush", getWorldspaceWidget().getDocument());
connect(mTextureBrushScenetool, SIGNAL (clicked()), mTextureBrushScenetool, SLOT (activate()));
connect(mTextureBrushScenetool->mTextureBrushWindow, SIGNAL(passBrushSize(int)), this, SLOT(setBrushSize(int)));
connect(mTextureBrushScenetool->mTextureBrushWindow, SIGNAL(passBrushShape(int)), this, SLOT(setBrushShape(int)));
connect(mTextureBrushScenetool->mTextureBrushWindow->mSizeSliders->mBrushSizeSlider, SIGNAL(valueChanged(int)), this, SLOT(setBrushSize(int)));
connect(mTextureBrushScenetool, SIGNAL(passTextureId(std::string)), this, SLOT(setBrushTexture(std::string)));
connect(mTextureBrushScenetool->mTextureBrushWindow, SIGNAL(passTextureId(std::string)), this, SLOT(setBrushTexture(std::string)));
connect(mTextureBrushScenetool, SIGNAL(passEvent(QDropEvent*)), this, SLOT(handleDropEvent(QDropEvent*)));
connect(this, SIGNAL(passBrushTexture(std::string)), mTextureBrushScenetool->mTextureBrushWindow, SLOT(setBrushTexture(std::string)));
connect(this, SIGNAL(passBrushTexture(std::string)), mTextureBrushScenetool, SLOT(updateBrushHistory(std::string)));
}
EditMode::activate(toolbar);
toolbar->addTool (mTextureBrushScenetool);
}
void CSVRender::TerrainTextureMode::deactivate(CSVWidget::SceneToolbar* toolbar)
{
if(mTextureBrushScenetool)
{
toolbar->removeTool (mTextureBrushScenetool);
delete mTextureBrushScenetool;
mTextureBrushScenetool = 0;
}
EditMode::deactivate(toolbar);
}
void CSVRender::TerrainTextureMode::primaryEditPressed(const WorldspaceHitResult& hit) // Apply changes here
{
CSMDoc::Document& document = getWorldspaceWidget().getDocument();
CSMWorld::IdTable& landTable = dynamic_cast<CSMWorld::IdTable&> (
*document.getData().getTableModel (CSMWorld::UniversalId::Type_Land));
CSMWorld::IdTable& ltexTable = dynamic_cast<CSMWorld::IdTable&> (
*document.getData().getTableModel (CSMWorld::UniversalId::Type_LandTextures));
mCellId = getWorldspaceWidget().getCellId (hit.worldPos);
QUndoStack& undoStack = document.getUndoStack();
CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures();
int index = landtexturesCollection.searchId(mBrushTexture);
if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted() && hit.hit == true)
{
undoStack.beginMacro ("Edit texture records");
if(allowLandTextureEditing(mCellId)==true)
{
undoStack.push (new CSMWorld::TouchLandCommand(landTable, ltexTable, mCellId));
editTerrainTextureGrid(hit);
}
undoStack.endMacro();
}
}
void CSVRender::TerrainTextureMode::primarySelectPressed(const WorldspaceHitResult& hit)
{
}
void CSVRender::TerrainTextureMode::secondarySelectPressed(const WorldspaceHitResult& hit)
{
}
bool CSVRender::TerrainTextureMode::primaryEditStartDrag (const QPoint& pos)
{
WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask());
CSMDoc::Document& document = getWorldspaceWidget().getDocument();
CSMWorld::IdTable& landTable = dynamic_cast<CSMWorld::IdTable&> (
*document.getData().getTableModel (CSMWorld::UniversalId::Type_Land));
CSMWorld::IdTable& ltexTable = dynamic_cast<CSMWorld::IdTable&> (
*document.getData().getTableModel (CSMWorld::UniversalId::Type_LandTextures));
mCellId = getWorldspaceWidget().getCellId (hit.worldPos);
QUndoStack& undoStack = document.getUndoStack();
CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures();
int index = landtexturesCollection.searchId(mBrushTexture);
if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted())
{
undoStack.beginMacro ("Edit texture records");
if(allowLandTextureEditing(mCellId)==true && hit.hit == true)
{
undoStack.push (new CSMWorld::TouchLandCommand(landTable, ltexTable, mCellId));
editTerrainTextureGrid(hit);
}
}
return true;
}
bool CSVRender::TerrainTextureMode::secondaryEditStartDrag (const QPoint& pos)
{
return false;
}
bool CSVRender::TerrainTextureMode::primarySelectStartDrag (const QPoint& pos)
{
return false;
}
bool CSVRender::TerrainTextureMode::secondarySelectStartDrag (const QPoint& pos)
{
return false;
}
void CSVRender::TerrainTextureMode::drag (const QPoint& pos, int diffX, int diffY, double speedFactor)
{
WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask());
CSMDoc::Document& document = getWorldspaceWidget().getDocument();
CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures();
int index = landtexturesCollection.searchId(mBrushTexture);
if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted() && hit.hit == true)
{
editTerrainTextureGrid(hit);
}
}
void CSVRender::TerrainTextureMode::dragCompleted(const QPoint& pos) {
CSMDoc::Document& document = getWorldspaceWidget().getDocument();
QUndoStack& undoStack = document.getUndoStack();
CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures();
int index = landtexturesCollection.searchId(mBrushTexture);
if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted())
{
undoStack.endMacro();
}
}
void CSVRender::TerrainTextureMode::dragAborted() {
}
void CSVRender::TerrainTextureMode::dragWheel (int diff, double speedFactor) {}
void CSVRender::TerrainTextureMode::handleDropEvent (QDropEvent *event) {
const CSMWorld::TableMimeData* mime = dynamic_cast<const CSMWorld::TableMimeData*> (event->mimeData());
if (!mime) // May happen when non-records (e.g. plain text) are dragged and dropped
return;
if (mime->holdsType (CSMWorld::UniversalId::Type_LandTexture))
{
const std::vector<CSMWorld::UniversalId> ids = mime->getData();
for (const CSMWorld::UniversalId& uid : ids)
{
mBrushTexture = uid.getId();
emit passBrushTexture(mBrushTexture);
}
}
if (mime->holdsType (CSMWorld::UniversalId::Type_Texture))
{
const std::vector<CSMWorld::UniversalId> ids = mime->getData();
for (const CSMWorld::UniversalId& uid : ids)
{
std::string textureFileName = uid.toString();
createTexture(textureFileName);
emit passBrushTexture(mBrushTexture);
}
}
}
void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitResult& hit)
{
CSMDoc::Document& document = getWorldspaceWidget().getDocument();
CSMWorld::IdTable& landTable = dynamic_cast<CSMWorld::IdTable&> (
*document.getData().getTableModel (CSMWorld::UniversalId::Type_Land));
mCellId = getWorldspaceWidget().getCellId (hit.worldPos);
if(allowLandTextureEditing(mCellId)==true) {}
std::pair<CSMWorld::CellCoordinates, bool> cellCoordinates_pair = CSMWorld::CellCoordinates::fromId (mCellId);
int cellX = cellCoordinates_pair.first.getX();
int cellY = cellCoordinates_pair.first.getY();
// The coordinates of hit in mCellId
int xHitInCell (float(((hit.worldPos.x() - (cellX* cellSize)) * landTextureSize / cellSize) - 0.5));
int yHitInCell (float(((hit.worldPos.y() - (cellY* cellSize)) * landTextureSize / cellSize) + 0.5));
if (xHitInCell < 0)
{
xHitInCell = xHitInCell + landTextureSize;
cellX = cellX - 1;
}
if (yHitInCell > 15)
{
yHitInCell = yHitInCell - landTextureSize;
cellY = cellY + 1;
}
mCellId = "#" + std::to_string(cellX) + " " + std::to_string(cellY);
if(allowLandTextureEditing(mCellId)==true) {}
std::string iteratedCellId;
int textureColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandTexturesIndex);
std::size_t hashlocation = mBrushTexture.find("#");
std::string mBrushTextureInt = mBrushTexture.substr (hashlocation+1);
int brushInt = stoi(mBrushTexture.substr (hashlocation+1))+1; // All indices are offset by +1
float rf = mBrushSize/2;
int r = (mBrushSize/2)+1;
float distance = 0;
if (mBrushShape == 0)
{
CSMWorld::LandTexturesColumn::DataType mPointer = landTable.data(landTable.getModelIndex(mCellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>();
CSMWorld::LandTexturesColumn::DataType mNew(mPointer);
if(allowLandTextureEditing(mCellId)==true)
{
mNew[yHitInCell*landTextureSize+xHitInCell] = brushInt;
pushEditToCommand(mNew, document, landTable, mCellId);
}
}
if (mBrushShape == 1)
{
int upperLeftCellX = cellX - std::floor(r / landTextureSize);
int upperLeftCellY = cellY - std::floor(r / landTextureSize);
if (xHitInCell - (r % landTextureSize) < 0) upperLeftCellX--;
if (yHitInCell - (r % landTextureSize) < 0) upperLeftCellY--;
int lowerrightCellX = cellX + std::floor(r / landTextureSize);
int lowerrightCellY = cellY + std::floor(r / landTextureSize);
if (xHitInCell + (r % landTextureSize) > landTextureSize - 1) lowerrightCellX++;
if (yHitInCell + (r % landTextureSize) > landTextureSize - 1) lowerrightCellY++;
for(int i_cell = upperLeftCellX; i_cell <= lowerrightCellX; i_cell++)
{
for(int j_cell = upperLeftCellY; j_cell <= lowerrightCellY; j_cell++)
{
iteratedCellId = "#" + std::to_string(i_cell) + " " + std::to_string(j_cell);
if(allowLandTextureEditing(iteratedCellId)==true)
{
CSMWorld::LandTexturesColumn::DataType mPointer = landTable.data(landTable.getModelIndex(iteratedCellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>();
CSMWorld::LandTexturesColumn::DataType mNew(mPointer);
for(int i = 0; i < landTextureSize; i++)
{
for(int j = 0; j < landTextureSize; j++)
{
if (i_cell == cellX && j_cell == cellY && abs(i-xHitInCell) < r && abs(j-yHitInCell) < r)
{
mNew[j*landTextureSize+i] = brushInt;
}
else
{
int distanceX(0);
int distanceY(0);
if (i_cell < cellX) distanceX = xHitInCell + landTextureSize * abs(i_cell-cellX) - i;
if (j_cell < cellY) distanceY = yHitInCell + landTextureSize * abs(j_cell-cellY) - j;
if (i_cell > cellX) distanceX = -xHitInCell + landTextureSize * abs(i_cell-cellX) + i;
if (j_cell > cellY) distanceY = -yHitInCell + landTextureSize * abs(j_cell-cellY) + j;
if (i_cell == cellX) distanceX = abs(i-xHitInCell);
if (j_cell == cellY) distanceY = abs(j-yHitInCell);
if (distanceX < r && distanceY < r) mNew[j*landTextureSize+i] = brushInt;
}
}
}
pushEditToCommand(mNew, document, landTable, iteratedCellId);
}
}
}
}
if (mBrushShape == 2)
{
int upperLeftCellX = cellX - std::floor(r / landTextureSize);
int upperLeftCellY = cellY - std::floor(r / landTextureSize);
if (xHitInCell - (r % landTextureSize) < 0) upperLeftCellX--;
if (yHitInCell - (r % landTextureSize) < 0) upperLeftCellY--;
int lowerrightCellX = cellX + std::floor(r / landTextureSize);
int lowerrightCellY = cellY + std::floor(r / landTextureSize);
if (xHitInCell + (r % landTextureSize) > landTextureSize - 1) lowerrightCellX++;
if (yHitInCell + (r % landTextureSize) > landTextureSize - 1) lowerrightCellY++;
for(int i_cell = upperLeftCellX; i_cell <= lowerrightCellX; i_cell++)
{
for(int j_cell = upperLeftCellY; j_cell <= lowerrightCellY; j_cell++)
{
iteratedCellId = "#" + std::to_string(i_cell) + " " + std::to_string(j_cell);
if(allowLandTextureEditing(iteratedCellId)==true)
{
CSMWorld::LandTexturesColumn::DataType mPointer = landTable.data(landTable.getModelIndex(iteratedCellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>();
CSMWorld::LandTexturesColumn::DataType mNew(mPointer);
for(int i = 0; i < landTextureSize; i++)
{
for(int j = 0; j < landTextureSize; j++)
{
if (i_cell == cellX && j_cell == cellY && abs(i-xHitInCell) < r && abs(j-yHitInCell) < r)
{
int distanceX(0);
int distanceY(0);
if (i_cell < cellX) distanceX = xHitInCell + landTextureSize * abs(i_cell-cellX) - i;
if (j_cell < cellY) distanceY = yHitInCell + landTextureSize * abs(j_cell-cellY) - j;
if (i_cell > cellX) distanceX = -xHitInCell + landTextureSize* abs(i_cell-cellX) + i;
if (j_cell > cellY) distanceY = -yHitInCell + landTextureSize * abs(j_cell-cellY) + j;
if (i_cell == cellX) distanceX = abs(i-xHitInCell);
if (j_cell == cellY) distanceY = abs(j-yHitInCell);
distance = std::round(sqrt(pow(distanceX, 2)+pow(distanceY, 2)));
if (distance < rf) mNew[j*landTextureSize+i] = brushInt;
}
else
{
int distanceX(0);
int distanceY(0);
if (i_cell < cellX) distanceX = xHitInCell + landTextureSize * abs(i_cell-cellX) - i;
if (j_cell < cellY) distanceY = yHitInCell + landTextureSize * abs(j_cell-cellY) - j;
if (i_cell > cellX) distanceX = -xHitInCell + landTextureSize * abs(i_cell-cellX) + i;
if (j_cell > cellY) distanceY = -yHitInCell + landTextureSize * abs(j_cell-cellY) + j;
if (i_cell == cellX) distanceX = abs(i-xHitInCell);
if (j_cell == cellY) distanceY = abs(j-yHitInCell);
distance = std::round(sqrt(pow(distanceX, 2)+pow(distanceY, 2)));
if (distance < rf) mNew[j*landTextureSize+i] = brushInt;
}
}
}
pushEditToCommand(mNew, document, landTable, iteratedCellId);
}
}
}
}
if (mBrushShape == 3)
{
// Not implemented
}
}
void CSVRender::TerrainTextureMode::pushEditToCommand(CSMWorld::LandTexturesColumn::DataType& newLandGrid, CSMDoc::Document& document,
CSMWorld::IdTable& landTable, std::string cellId)
{
CSMWorld::IdTable& ltexTable = dynamic_cast<CSMWorld::IdTable&> (
*document.getData().getTableModel (CSMWorld::UniversalId::Type_LandTextures));
QVariant changedLand;
changedLand.setValue(newLandGrid);
QModelIndex index(landTable.getModelIndex (cellId, landTable.findColumnIndex (CSMWorld::Columns::ColumnId_LandTexturesIndex)));
QUndoStack& undoStack = document.getUndoStack();
undoStack.push (new CSMWorld::TouchLandCommand(landTable, ltexTable, cellId));
undoStack.push (new CSMWorld::ModifyCommand(landTable, index, changedLand));
}
void CSVRender::TerrainTextureMode::createTexture(std::string textureFileName)
{
CSMDoc::Document& document = getWorldspaceWidget().getDocument();
CSMWorld::IdTable& ltexTable = dynamic_cast<CSMWorld::IdTable&> (
*document.getData().getTableModel (CSMWorld::UniversalId::Type_LandTextures));
QUndoStack& undoStack = document.getUndoStack();
std::string newId;
int counter=0;
bool freeIndexFound = false;
do {
const size_t maxCounter = std::numeric_limits<uint16_t>::max() - 1;
try
{
newId = CSMWorld::LandTexture::createUniqueRecordId(0, counter);
if (ltexTable.getRecord(newId).isDeleted() == 0) counter = (counter + 1) % maxCounter;
} catch (const std::exception& e)
{
newId = CSMWorld::LandTexture::createUniqueRecordId(0, counter);
freeIndexFound = true;
}
} while (freeIndexFound == false);
std::size_t idlocation = textureFileName.find("Texture: ");
textureFileName = textureFileName.substr (idlocation + 9);
QVariant textureNameVariant;
QVariant textureFileNameVariant;
textureFileNameVariant.setValue(QString::fromStdString(textureFileName));
undoStack.beginMacro ("Add land texture record");
undoStack.push (new CSMWorld::CreateCommand (ltexTable, newId));
QModelIndex index(ltexTable.getModelIndex (newId, ltexTable.findColumnIndex (CSMWorld::Columns::ColumnId_Texture)));
undoStack.push (new CSMWorld::ModifyCommand(ltexTable, index, textureFileNameVariant));
undoStack.endMacro();
mBrushTexture = newId;
}
bool CSVRender::TerrainTextureMode::allowLandTextureEditing(std::string cellId)
{
CSMDoc::Document& document = getWorldspaceWidget().getDocument();
CSMWorld::IdTable& landTable = dynamic_cast<CSMWorld::IdTable&> (
*document.getData().getTableModel (CSMWorld::UniversalId::Type_Land));
CSMWorld::IdTree& cellTable = dynamic_cast<CSMWorld::IdTree&> (
*document.getData().getTableModel (CSMWorld::UniversalId::Type_Cells));
bool noCell = document.getData().getCells().searchId (cellId)==-1;
bool noLand = document.getData().getLand().searchId (cellId)==-1;
if (noCell)
{
std::string mode = CSMPrefs::get()["3D Scene Editing"]["outside-landedit"].toString();
// target cell does not exist
if (mode=="Discard")
return false;
if (mode=="Create cell and land, then edit")
{
std::unique_ptr<CSMWorld::CreateCommand> createCommand (
new CSMWorld::CreateCommand (cellTable, cellId));
int parentIndex = cellTable.findColumnIndex (CSMWorld::Columns::ColumnId_Cell);
int index = cellTable.findNestedColumnIndex (parentIndex, CSMWorld::Columns::ColumnId_Interior);
createCommand->addNestedValue (parentIndex, index, false);
document.getUndoStack().push (createCommand.release());
if (CSVRender::PagedWorldspaceWidget *paged =
dynamic_cast<CSVRender::PagedWorldspaceWidget *> (&getWorldspaceWidget()))
{
CSMWorld::CellSelection selection = paged->getCellSelection();
selection.add (CSMWorld::CellCoordinates::fromId (cellId).first);
paged->setCellSelection (selection);
}
}
}
else if (CSVRender::PagedWorldspaceWidget *paged =
dynamic_cast<CSVRender::PagedWorldspaceWidget *> (&getWorldspaceWidget()))
{
CSMWorld::CellSelection selection = paged->getCellSelection();
if (!selection.has (CSMWorld::CellCoordinates::fromId (cellId).first))
{
// target cell exists, but is not shown
std::string mode =
CSMPrefs::get()["3D Scene Editing"]["outside-visible-landedit"].toString();
if (mode=="Discard")
return false;
if (mode=="Show cell and edit")
{
selection.add (CSMWorld::CellCoordinates::fromId (cellId).first);
paged->setCellSelection (selection);
}
}
}
if (noLand)
{
std::string mode = CSMPrefs::get()["3D Scene Editing"]["outside-landedit"].toString();
// target cell does not exist
if (mode=="Discard")
return false;
if (mode=="Create cell and land, then edit")
{
document.getUndoStack().push (new CSMWorld::CreateCommand (landTable, cellId));
}
}
return true;
}
void CSVRender::TerrainTextureMode::dragMoveEvent (QDragMoveEvent *event) {
}
void CSVRender::TerrainTextureMode::setBrushSize(int brushSize)
{
mBrushSize = brushSize;
}
void CSVRender::TerrainTextureMode::setBrushShape(int brushShape)
{
mBrushShape = brushShape;
}
void CSVRender::TerrainTextureMode::setBrushTexture(std::string brushTexture)
{
mBrushTexture = brushTexture;
}

@ -0,0 +1,100 @@
#ifndef CSV_RENDER_TERRAINTEXTUREMODE_H
#define CSV_RENDER_TERRAINTEXTUREMODE_H
#include "editmode.hpp"
#include <string>
#include <QWidget>
#include <QEvent>
#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"
namespace CSVWidget
{
class SceneToolTextureBrush;
}
namespace CSVRender
{
class TerrainTextureMode : public EditMode
{
Q_OBJECT
public:
/// \brief Editmode for terrain texture grid
TerrainTextureMode(WorldspaceWidget*, QWidget* parent = nullptr);
/// \brief Create single command for one-click texture editing
void primaryEditPressed (const WorldspaceHitResult& hit);
/// \brief Open brush settings window
void primarySelectPressed(const WorldspaceHitResult&);
void secondarySelectPressed(const WorldspaceHitResult&);
void activate(CSVWidget::SceneToolbar*);
void deactivate(CSVWidget::SceneToolbar*);
/// \brief Start texture 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);
/// \brief Handle texture edit behavior during dragging
virtual void drag (const QPoint& pos, int diffX, int diffY, double speedFactor);
/// \brief End texture editing command macro
virtual void dragCompleted(const QPoint& pos);
virtual void dragAborted();
virtual void dragWheel (int diff, double speedFactor);
virtual void dragMoveEvent (QDragMoveEvent *event);
/// \brief Handle brush mechanics, maths regarding worldspace hit etc.
void editTerrainTextureGrid (const WorldspaceHitResult& hit);
/// \brief Push texture edits to command macro
void pushEditToCommand (CSMWorld::LandTexturesColumn::DataType& newLandGrid, CSMDoc::Document& document,
CSMWorld::IdTable& landTable, std::string cellId);
/// \brief Create new land texture record from texture asset
void createTexture(std::string textureFileName);
/// \brief Create new cell and land if needed
bool allowLandTextureEditing(std::string textureFileName);
private:
std::string mCellId;
std::string mBrushTexture;
int mBrushSize;
int mBrushShape;
CSVWidget::SceneToolTextureBrush *mTextureBrushScenetool;
const int cellSize {ESM::Land::REAL_SIZE};
const int landSize {ESM::Land::LAND_SIZE};
const int landTextureSize {ESM::Land::LAND_TEXTURE_SIZE};
signals:
void passBrushTexture(std::string brushTexture);
public slots:
void handleDropEvent(QDropEvent *event);
void setBrushSize(int brushSize);
void setBrushShape(int brushShape);
void setBrushTexture(std::string brushShape);
};
}
#endif

@ -445,7 +445,7 @@ CSVRender::WorldspaceHitResult CSVRender::WorldspaceWidget::mousePick (const QPo
// Default placement
direction.normalize();
direction *= CSMPrefs::get()["Scene Drops"]["distance"].toInt();
direction *= CSMPrefs::get()["3D Scene Editing"]["distance"].toInt();
WorldspaceHitResult hit = { false, 0, 0, 0, 0, start + direction };
return hit;
@ -648,6 +648,12 @@ void CSVRender::WorldspaceWidget::mouseMoveEvent (QMouseEvent *event)
mDragX = event->posF().x();
mDragY = height() - event->posF().y();
#endif
if (mDragMode == InteractionType_PrimaryEdit)
{
EditMode& editMode = dynamic_cast<CSVRender::EditMode&> (*mEditMode->getCurrent());
editMode.drag (event->pos(), mDragX, mDragY, mDragFactor); // note: terraintexturemode only uses pos
}
}
}
else

@ -0,0 +1,379 @@
#include "scenetooltexturebrush.hpp"
#include <QFrame>
#include <QIcon>
#include <QTableWidget>
#include <QHBoxLayout>
#include <QWidget>
#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::BrushSizeControls::BrushSizeControls(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"]["texturebrush-maximumsize"].toInt());
mBrushSizeSlider->setSingleStep(1);
mBrushSizeSpinBox = new QSpinBox;
mBrushSizeSpinBox->setRange(1, CSMPrefs::get()["3D Scene Editing"]["texturebrush-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::TextureBrushWindow::TextureBrushWindow(CSMDoc::Document& document, QWidget *parent)
: QFrame(parent, Qt::Popup),
mBrushShape(0),
mBrushSize(0),
mBrushTexture("L0#0"),
mDocument(document)
{
mBrushTextureLabel = "Selected texture: " + mBrushTexture + " ";
CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = mDocument.getData().getLandTextures();
int landTextureFilename = landtexturesCollection.findColumnIndex(CSMWorld::Columns::ColumnId_Texture);
int index = landtexturesCollection.searchId(mBrushTexture);
if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted())
{
mSelectedBrush = new QLabel(QString::fromStdString(mBrushTextureLabel) + landtexturesCollection.getData(index, landTextureFilename).value<QString>());
} else
{
mBrushTextureLabel = "No selected texture or invalid texture";
mSelectedBrush = new QLabel(QString::fromStdString(mBrushTextureLabel));
}
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 BrushSizeControls("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);
layoutMain->addWidget(mHorizontalGroupBox);
layoutMain->addWidget(mSizeSliders);
layoutMain->addWidget(mSelectedBrush);
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::TextureBrushWindow::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::TextureBrushWindow::setBrushTexture(std::string brushTexture)
{
mBrushTexture = brushTexture;
CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = mDocument.getData().getLandTextures();
int landTextureFilename = landtexturesCollection.findColumnIndex(CSMWorld::Columns::ColumnId_Texture);
int columnModification = landtexturesCollection.findColumnIndex(CSMWorld::Columns::ColumnId_Modification);
int index = landtexturesCollection.searchId(mBrushTexture);
// Check if texture exists in current plugin
if(landtexturesCollection.getData(index, columnModification).value<int>() == 0)
{
CSMWorld::IdTable& ltexTable = dynamic_cast<CSMWorld::IdTable&> (
*mDocument.getData().getTableModel (CSMWorld::UniversalId::Type_LandTextures));
QUndoStack& undoStack = mDocument.getUndoStack();
QVariant textureFileNameVariant;
textureFileNameVariant.setValue(landtexturesCollection.getData(index, landTextureFilename).value<QString>());
std::size_t hashlocation = mBrushTexture.find("#");
std::string mBrushTexturePlugin = "L0#" + mBrushTexture.substr (hashlocation+1);
int indexPlugin = landtexturesCollection.searchId(mBrushTexturePlugin);
// Reindex texture if needed
if (indexPlugin != -1 && !landtexturesCollection.getRecord(indexPlugin).isDeleted())
{
int counter=0;
bool freeIndexFound = false;
do {
const size_t maxCounter = std::numeric_limits<uint16_t>::max() - 1;
mBrushTexturePlugin = CSMWorld::LandTexture::createUniqueRecordId(0, counter);
if (landtexturesCollection.searchId(mBrushTexturePlugin) != -1 && landtexturesCollection.getRecord(mBrushTexturePlugin).isDeleted() == 0) counter = (counter + 1) % maxCounter;
else freeIndexFound = true;
} while (freeIndexFound == false);
}
undoStack.beginMacro ("Add land texture record");
undoStack.push (new CSMWorld::CloneCommand (ltexTable, mBrushTexture, mBrushTexturePlugin, CSMWorld::UniversalId::Type_LandTexture));
undoStack.endMacro();
mBrushTexture = mBrushTexturePlugin;
emit passTextureId(mBrushTexture);
}
if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted())
{
mBrushTextureLabel = "Selected texture: " + mBrushTexture + " ";
mSelectedBrush->setText(QString::fromStdString(mBrushTextureLabel) + landtexturesCollection.getData(index, landTextureFilename).value<QString>());
} else
{
mBrushTextureLabel = "No selected texture or invalid texture";
mSelectedBrush->setText(QString::fromStdString(mBrushTextureLabel));
}
emit passBrushShape(mBrushShape); // update icon
}
void CSVWidget::TextureBrushWindow::setBrushSize(int brushSize)
{
mBrushSize = brushSize;
emit passBrushSize(mBrushSize);
}
void CSVWidget::TextureBrushWindow::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::SceneToolTextureBrush::adjustToolTips()
{
}
CSVWidget::SceneToolTextureBrush::SceneToolTextureBrush (SceneToolbar *parent, const QString& toolTip, CSMDoc::Document& document)
: SceneTool (parent, Type_TopAction),
mToolTip (toolTip),
mDocument (document),
mTextureBrushWindow(new TextureBrushWindow(document, this))
{
mBrushHistory.resize(1);
mBrushHistory[0] = "L0#0";
setAcceptDrops(true);
connect(mTextureBrushWindow, SIGNAL(passBrushShape(int)), this, SLOT(setButtonIcon(int)));
setButtonIcon(mTextureBrushWindow->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::SceneToolTextureBrush::setButtonIcon (int brushShape)
{
QString tooltip = "Change brush settings <p>Currently selected: ";
switch (brushShape)
{
case 0:
setIcon (QIcon (QPixmap (":scenetoolbar/brush-point")));
tooltip += mTextureBrushWindow->toolTipPoint;
break;
case 1:
setIcon (QIcon (QPixmap (":scenetoolbar/brush-square")));
tooltip += mTextureBrushWindow->toolTipSquare;
break;
case 2:
setIcon (QIcon (QPixmap (":scenetoolbar/brush-circle")));
tooltip += mTextureBrushWindow->toolTipCircle;
break;
case 3:
setIcon (QIcon (QPixmap (":scenetoolbar/brush-custom")));
tooltip += mTextureBrushWindow->toolTipCustom;
break;
}
tooltip += "<p>(right click to access of previously used brush settings)";
CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = mDocument.getData().getLandTextures();
int landTextureFilename = landtexturesCollection.findColumnIndex(CSMWorld::Columns::ColumnId_Texture);
int index = landtexturesCollection.searchId(mTextureBrushWindow->mBrushTexture);
if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted())
{
tooltip += "<p>Selected texture: " + QString::fromStdString(mTextureBrushWindow->mBrushTexture) + " ";
tooltip += landtexturesCollection.getData(index, landTextureFilename).value<QString>();
} else
{
tooltip += "<p>No selected texture or invalid texture";
}
tooltip += "<br>(drop texture here to change)";
setToolTip (tooltip);
}
void CSVWidget::SceneToolTextureBrush::showPanel (const QPoint& position)
{
updatePanel();
mPanel->move (position);
mPanel->show();
}
void CSVWidget::SceneToolTextureBrush::updatePanel()
{
mTable->setRowCount (mBrushHistory.size());
for (int i = mBrushHistory.size()-1; i >= 0; --i)
{
CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = mDocument.getData().getLandTextures();
int landTextureFilename = landtexturesCollection.findColumnIndex(CSMWorld::Columns::ColumnId_Texture);
int index = landtexturesCollection.searchId(mBrushHistory[i]);
if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted())
{
mTable->setItem (i, 1, new QTableWidgetItem (landtexturesCollection.getData(index, landTextureFilename).value<QString>()));
mTable->setItem (i, 0, new QTableWidgetItem (QString::fromStdString(mBrushHistory[i])));
} else
{
mTable->setItem (i, 1, new QTableWidgetItem ("Invalid/deleted texture"));
mTable->setItem (i, 0, new QTableWidgetItem (QString::fromStdString(mBrushHistory[i])));
}
}
}
void CSVWidget::SceneToolTextureBrush::updateBrushHistory (const std::string& brushTexture)
{
mBrushHistory.insert(mBrushHistory.begin(), brushTexture);
if(mBrushHistory.size() > 5) mBrushHistory.pop_back();
}
void CSVWidget::SceneToolTextureBrush::clicked (const QModelIndex& index)
{
if (index.column()==0 || index.column()==1)
{
std::string brushTexture = mBrushHistory[index.row()];
std::swap(mBrushHistory[index.row()], mBrushHistory[0]);
mTextureBrushWindow->setBrushTexture(brushTexture);
emit passTextureId(brushTexture);
updatePanel();
mPanel->hide();
}
}
void CSVWidget::SceneToolTextureBrush::activate ()
{
QPoint position = QCursor::pos();
mTextureBrushWindow->mSizeSliders->mBrushSizeSlider->setRange(1, CSMPrefs::get()["3D Scene Editing"]["texturebrush-maximumsize"].toInt());
mTextureBrushWindow->mSizeSliders->mBrushSizeSpinBox->setRange(1, CSMPrefs::get()["3D Scene Editing"]["texturebrush-maximumsize"].toInt());
mTextureBrushWindow->move (position);
mTextureBrushWindow->show();
}
void CSVWidget::SceneToolTextureBrush::dragEnterEvent (QDragEnterEvent *event)
{
emit passEvent(event);
event->accept();
}
void CSVWidget::SceneToolTextureBrush::dropEvent (QDropEvent *event)
{
emit passEvent(event);
event->accept();
}

@ -0,0 +1,133 @@
#ifndef CSV_WIDGET_SCENETOOLTEXTUREBRUSH_H
#define CSV_WIDGET_SCENETOOLTEXTUREBRUSH_H
#include <QIcon>
#include <QFrame>
#include <QModelIndex>
#include <QWidget>
#include <QLabel>
#include <QSpinBox>
#include <QGroupBox>
#include <QSlider>
#include <QEvent>
#include <QHBoxLayout>
#include <QPushButton>
#include "scenetool.hpp"
#include "../../model/doc/document.hpp"
class QTableWidget;
namespace CSVRender
{
class TerrainTextureMode;
}
namespace CSVWidget
{
class SceneToolTextureBrush;
/// \brief Layout-box for some brush button settings
class BrushSizeControls : public QGroupBox
{
Q_OBJECT
public:
BrushSizeControls(const QString &title, QWidget *parent);
private:
QHBoxLayout *mLayoutSliderSize;
QSlider *mBrushSizeSlider;
QSpinBox *mBrushSizeSpinBox;
friend class SceneToolTextureBrush;
friend class CSVRender::TerrainTextureMode;
};
class SceneToolTextureBrush;
/// \brief Brush settings window
class TextureBrushWindow : public QFrame
{
Q_OBJECT
public:
TextureBrushWindow(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;
std::string mBrushTexture;
CSMDoc::Document& mDocument;
QLabel *mSelectedBrush;
QGroupBox *mHorizontalGroupBox;
std::string mBrushTextureLabel;
QPushButton *mButtonPoint;
QPushButton *mButtonSquare;
QPushButton *mButtonCircle;
QPushButton *mButtonCustom;
BrushSizeControls* mSizeSliders;
friend class SceneToolTextureBrush;
friend class CSVRender::TerrainTextureMode;
public slots:
void setBrushTexture(std::string brushTexture);
void setBrushShape();
void setBrushSize(int brushSize);
signals:
void passBrushSize (int brushSize);
void passBrushShape(int brushShape);
void passTextureId(std::string brushTexture);
};
class SceneToolTextureBrush : public SceneTool
{
Q_OBJECT
QString mToolTip;
CSMDoc::Document& mDocument;
QFrame *mPanel;
QTableWidget *mTable;
std::vector<std::string> mBrushHistory;
TextureBrushWindow *mTextureBrushWindow;
private:
void adjustToolTips();
public:
SceneToolTextureBrush (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::TerrainTextureMode;
public slots:
void setButtonIcon(int brushShape);
void updateBrushHistory (const std::string& mBrushTexture);
void clicked (const QModelIndex& index);
virtual void activate();
signals:
void passEvent(QDropEvent *event);
void passEvent(QDragEnterEvent *event);
void passTextureId(std::string brushTexture);
};
}
#endif

@ -5,9 +5,6 @@
#include <map>
#include <set>
#include <osg/ref_ptr>
#include <osg/Node>
#include <components/esm/cellid.hpp>
#include "../mwworld/ptr.hpp"
@ -457,7 +454,6 @@ namespace MWBase
virtual bool isUnderwater(const MWWorld::ConstPtr &object, const float heightRatio) const = 0;
virtual bool isWaterWalkingCastableOnTarget(const MWWorld::ConstPtr &target) const = 0;
virtual bool isOnGround(const MWWorld::Ptr &ptr) const = 0;
virtual bool isIdle(const MWWorld::Ptr &ptr) const = 0;
virtual osg::Matrixf getActorHeadTransform(const MWWorld::ConstPtr& actor) const = 0;
@ -624,7 +620,7 @@ namespace MWBase
/// Spawn a blood effect for \a ptr at \a worldPosition
virtual void spawnBloodEffect (const MWWorld::Ptr& ptr, const osg::Vec3f& worldPosition) = 0;
virtual void spawnEffect (const std::string& model, const std::string& textureOverride, const osg::Vec3f& worldPos, float scale = 1.f, bool isMagicVFX = true) = 0;
virtual void spawnEffect (const std::string& model, const std::string& textureOverride, const osg::Vec3f& worldPos) = 0;
virtual void explodeSpell(const osg::Vec3f& origin, const ESM::EffectList& effects, const MWWorld::Ptr& caster,
const MWWorld::Ptr& ignore, ESM::RangeType rangeType, const std::string& id,
@ -666,8 +662,6 @@ namespace MWBase
/// Preload VFX associated with this effect list
virtual void preloadEffects(const ESM::EffectList* effectList) = 0;
virtual osg::ref_ptr<osg::Node> getInstance (const std::string& modelName) = 0;
};
}

@ -244,7 +244,7 @@ BookTypesetter::Ptr JournalBooks::createLatinJournalIndex ()
const MWGui::TextColours& textColours = MWBase::Environment::get().getWindowManager()->getTextColours();
BookTypesetter::Style* style = typesetter->createHotStyle (body, textColours.journalTopic,
textColours.journalTopicOver,
textColours.journalTopicPressed, (uint32_t) ch);
textColours.journalTopicPressed, (Utf8Stream::UnicodeChar) ch);
if (i == 13)
typesetter->sectionBreak ();
@ -274,7 +274,7 @@ BookTypesetter::Ptr JournalBooks::createCyrillicJournalIndex ()
sprintf(buffer, "( %c%c )", ch[0], ch[1]);
Utf8Stream stream ((char*) ch);
uint32_t first = stream.peek();
Utf8Stream::UnicodeChar first = stream.peek();
const MWGui::TextColours& textColours = MWBase::Environment::get().getWindowManager()->getTextColours();
BookTypesetter::Style* style = typesetter->createHotStyle (body, textColours.journalTopic,

@ -7,7 +7,6 @@
#include <components/translation/translation.hpp>
#include <components/misc/stringops.hpp>
#include <components/misc/utf8stream.hpp>
#include "../mwbase/world.hpp"
#include "../mwbase/journal.hpp"
@ -307,39 +306,22 @@ struct JournalViewModelImpl : JournalViewModel
visitor (toUtf8Span (topic.getName()));
}
void visitTopicNamesStartingWith (uint32_t character, std::function < void (const std::string&) > visitor) const
void visitTopicNamesStartingWith (Utf8Stream::UnicodeChar character, std::function < void (const std::string&) > visitor) const
{
MWBase::Journal * journal = MWBase::Environment::get().getJournal();
for (MWBase::Journal::TTopicIter i = journal->topicBegin (); i != journal->topicEnd (); ++i)
{
Utf8Stream stream (i->first.c_str());
uint32_t first = toUpper(stream.peek());
Utf8Stream::UnicodeChar first = Misc::StringUtils::toLowerUtf8(stream.peek());
if (first != character)
if (first != Misc::StringUtils::toLowerUtf8(character))
continue;
visitor (i->second.getName());
}
}
static uint32_t toUpper(uint32_t ch)
{
// Russian alphabet
if (ch >= 0x0430 && ch < 0x0450)
ch -= 0x20;
// Cyrillic IO character
if (ch == 0x0451)
ch -= 0x50;
// Latin alphabet
if (ch >= 0x61 && ch < 0x80)
ch -= 0x20;
return ch;
}
struct TopicEntryImpl : BaseEntry <MWDialogue::Topic::TEntryIter, TopicEntry>
{
MWDialogue::Topic const & mTopic;

@ -6,6 +6,8 @@
#include <functional>
#include <stdint.h>
#include <components/misc/utf8stream.hpp>
namespace MWGui
{
/// View-Model for the journal GUI
@ -76,7 +78,7 @@ namespace MWGui
virtual void visitTopicName (TopicId topicId, std::function <void (Utf8Span)> visitor) const = 0;
/// walks over the topics whose names start with the character
virtual void visitTopicNamesStartingWith (uint32_t character, std::function < void (const std::string&) > visitor) const = 0;
virtual void visitTopicNamesStartingWith (Utf8Stream::UnicodeChar character, std::function < void (const std::string&) > visitor) const = 0;
/// walks over the topic entries for the topic specified by its identifier
virtual void visitTopicEntries (TopicId topicId, std::function <void (TopicEntry const &)> visitor) const = 0;

@ -14,6 +14,9 @@ namespace MWGui
bool shouldAcceptKeyFocus(MyGUI::Widget* w)
{
if (w && w->getUserString("IgnoreTabKey") == "y")
return false;
return w && !w->castType<MyGUI::Window>(false) && w->getInheritedEnabled() && w->getInheritedVisible() && w->getVisible() && w->getEnabled();
}

@ -32,10 +32,14 @@ namespace
namespace MWGui
{
SpellModel::SpellModel(const MWWorld::Ptr &actor, const std::string& filter)
: mActor(actor), mFilter(filter)
{
}
SpellModel::SpellModel(const MWWorld::Ptr &actor)
: mActor(actor)
{
}
void SpellModel::update()
@ -48,12 +52,19 @@ namespace MWGui
const MWWorld::ESMStore &esmStore =
MWBase::Environment::get().getWorld()->getStore();
std::string filter = Misc::StringUtils::lowerCaseUtf8(mFilter);
for (MWMechanics::Spells::TIterator it = spells.begin(); it != spells.end(); ++it)
{
const ESM::Spell* spell = it->first;
if (spell->mData.mType != ESM::Spell::ST_Power && spell->mData.mType != ESM::Spell::ST_Spell)
continue;
std::string name = Misc::StringUtils::lowerCaseUtf8(spell->mName);
if (name.find(filter) == std::string::npos)
continue;
Spell newSpell;
newSpell.mName = spell->mName;
if (spell->mData.mType == ESM::Spell::ST_Spell)
@ -89,6 +100,11 @@ namespace MWGui
if (enchant->mData.mType != ESM::Enchantment::WhenUsed && enchant->mData.mType != ESM::Enchantment::CastOnce)
continue;
std::string name = Misc::StringUtils::lowerCaseUtf8(item.getClass().getName(item));
if (name.find(filter) == std::string::npos)
continue;
Spell newSpell;
newSpell.mItem = item;
newSpell.mId = item.getCellRef().getRefId();

@ -35,6 +35,7 @@ namespace MWGui
class SpellModel
{
public:
SpellModel(const MWWorld::Ptr& actor, const std::string& filter);
SpellModel(const MWWorld::Ptr& actor);
typedef int ModelIndex;
@ -50,6 +51,8 @@ namespace MWGui
MWWorld::Ptr mActor;
std::vector<Spell> mSpells;
std::string mFilter;
};
}

@ -2,6 +2,7 @@
#include <boost/format.hpp>
#include <MyGUI_EditBox.h>
#include <MyGUI_InputManager.h>
#include <components/settings/settings.hpp>
@ -49,8 +50,12 @@ namespace MWGui
getWidget(mSpellView, "SpellView");
getWidget(mEffectBox, "EffectsBox");
getWidget(mFilterEdit, "FilterEdit");
mFilterEdit->setUserString("IgnoreTabKey", "y");
mSpellView->eventSpellClicked += MyGUI::newDelegate(this, &SpellWindow::onModelIndexSelected);
mFilterEdit->eventEditTextChange += MyGUI::newDelegate(this, &SpellWindow::onFilterChanged);
setCoord(498, 300, 302, 300);
}
@ -75,6 +80,11 @@ namespace MWGui
void SpellWindow::onOpen()
{
// Reset the filter focus when opening the window
MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget();
if (focus == mFilterEdit)
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(NULL);
updateSpells();
}
@ -93,7 +103,7 @@ namespace MWGui
{
mSpellIcons->updateWidgets(mEffectBox, false);
mSpellView->setModel(new SpellModel(MWMechanics::getPlayer()));
mSpellView->setModel(new SpellModel(MWMechanics::getPlayer(), mFilterEdit->getCaption()));
}
void SpellWindow::onEnchantedItemSelected(MWWorld::Ptr item, bool alreadyEquipped)
@ -178,6 +188,11 @@ namespace MWGui
}
}
void SpellWindow::onFilterChanged(MyGUI::EditBox *sender)
{
mSpellView->setModel(new SpellModel(MWMechanics::getPlayer(), sender->getCaption()));
}
void SpellWindow::onSpellSelected(const std::string& spellId)
{
MWWorld::Ptr player = MWMechanics::getPlayer();
@ -233,7 +248,7 @@ namespace MWGui
if (stats.isParalyzed() || stats.getKnockedDown() || stats.isDead() || stats.getHitRecovery())
return;
mSpellView->setModel(new SpellModel(MWMechanics::getPlayer()));
mSpellView->setModel(new SpellModel(MWMechanics::getPlayer(), ""));
SpellModel::ModelIndex selected = 0;
for (SpellModel::ModelIndex i = 0; i<int(mSpellView->getModel()->getItemCount()); ++i)

@ -32,6 +32,7 @@ namespace MWGui
void onEnchantedItemSelected(MWWorld::Ptr item, bool alreadyEquipped);
void onSpellSelected(const std::string& spellId);
void onModelIndexSelected(SpellModel::ModelIndex index);
void onFilterChanged(MyGUI::EditBox *sender);
void onDeleteSpellAccept();
void askDeleteSpell(const std::string& spellId);
@ -41,6 +42,7 @@ namespace MWGui
SpellView* mSpellView;
SpellIcons* mSpellIcons;
MyGUI::EditBox* mFilterEdit;
private:
float mUpdateTimer;

@ -866,7 +866,8 @@ namespace MWMechanics
}
// place above to prevent moving inside objects, e.g. stairs, because a vector between pathgrids can be underground.
dest.mZ += 80;
// Adding 20 in adjustPosition() is not enough.
dest.mZ += 60;
ToWorldCoordinates(dest, actor.getCell()->getCell());

@ -863,7 +863,6 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim
refreshCurrentAnims(mIdleState, mMovementState, mJumpState, true);
mAnimation->runAnimation(0.f);
mAnimation->updateEffects(0.f);
unpersistAnimationState();
}
@ -2118,13 +2117,6 @@ void CharacterController::update(float duration)
}
osg::Vec3f moved = mAnimation->runAnimation(mSkipAnim ? 0.f : duration);
// treat player specifically since he is not in rendering mObjects
if (mPtr == getPlayer())
{
mAnimation->updateEffects(mSkipAnim ? 0.f : duration);
}
if(duration > 0.0f)
moved /= duration;
else
@ -2157,8 +2149,7 @@ void CharacterController::update(float duration)
moved.z() = 1.0;
// Update movement
// We should not apply movement for standing actors
if(mMovementAnimationControlled && mPtr.getClass().isActor() && (movement.length2() > 0.f || !world->isIdle(mPtr)))
if(mMovementAnimationControlled && mPtr.getClass().isActor())
world->queueMovement(mPtr, moved);
mSkipAnim = false;
@ -2339,7 +2330,6 @@ void CharacterController::forceStateUpdate()
}
mAnimation->runAnimation(0.f);
mAnimation->updateEffects(0.f);
}
CharacterController::KillResult CharacterController::kill()

@ -6,12 +6,8 @@
#include <boost/format.hpp>
#include <osg/BoundingBox>
#include <osg/ComputeBoundsVisitor>
#include <components/misc/rng.hpp>
#include <components/settings/settings.hpp>
#include <components/sceneutil/positionattitudetransform.hpp>
/*
Start of tes3mp addition
@ -44,7 +40,6 @@
#include "../mwworld/inventorystore.hpp"
#include "../mwrender/animation.hpp"
#include "../mwrender/vismask.hpp"
#include "npcstats.hpp"
#include "actorutil.hpp"
@ -243,45 +238,42 @@ namespace MWMechanics
magicEffects = effects;
float resisted = 0;
if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful)
{
// Effects with no resistance attribute belonging to them can not be resisted
if (ESM::MagicEffect::getResistanceEffect(effectId) == -1)
return 0.f;
// Effects with no resistance attribute belonging to them can not be resisted
if (ESM::MagicEffect::getResistanceEffect(effectId) == -1)
return 0.f;
float resistance = getEffectResistanceAttribute(effectId, magicEffects);
float resistance = getEffectResistanceAttribute(effectId, magicEffects);
int willpower = stats.getAttribute(ESM::Attribute::Willpower).getModified();
float luck = static_cast<float>(stats.getAttribute(ESM::Attribute::Luck).getModified());
float x = (willpower + 0.1f * luck) * stats.getFatigueTerm();
int willpower = stats.getAttribute(ESM::Attribute::Willpower).getModified();
float luck = static_cast<float>(stats.getAttribute(ESM::Attribute::Luck).getModified());
float x = (willpower + 0.1f * luck) * stats.getFatigueTerm();
// This makes spells that are easy to cast harder to resist and vice versa
float castChance = 100.f;
if (spell != NULL && !caster.isEmpty() && caster.getClass().isActor())
{
castChance = getSpellSuccessChance(spell, caster, NULL, false); // Uncapped casting chance
}
if (castChance > 0)
x *= 50 / castChance;
// This makes spells that are easy to cast harder to resist and vice versa
float castChance = 100.f;
if (spell != NULL && !caster.isEmpty() && caster.getClass().isActor())
{
castChance = getSpellSuccessChance(spell, caster, NULL, false); // Uncapped casting chance
}
if (castChance > 0)
x *= 50 / castChance;
float roll = Misc::Rng::rollClosedProbability() * 100;
if (magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude)
roll -= resistance;
float roll = Misc::Rng::rollClosedProbability() * 100;
if (magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude)
roll -= resistance;
if (x <= roll)
x = 0;
if (x <= roll)
x = 0;
else
{
if (magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude)
x = 100;
else
{
if (magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude)
x = 100;
else
x = roll / std::min(x, 100.f);
}
x = roll / std::min(x, 100.f);
}
x = std::min(x + resistance, 100.f);
x = std::min(x + resistance, 100.f);
resisted = x;
}
resisted = x;
return resisted;
}
@ -479,13 +471,11 @@ namespace MWMechanics
}
float magnitudeMult = 1;
if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful && target.getClass().isActor())
{
if (absorbed)
continue;
// Try reflecting
if (!reflected && !caster.isEmpty() && caster != target && !(magicEffect->mData.mFlags & ESM::MagicEffect::Unreflectable))
if (!absorbed)
{
// Reflect harmful effects
if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful && !reflected && !caster.isEmpty() && caster != target && !(magicEffect->mData.mFlags & ESM::MagicEffect::Unreflectable))
{
float reflect = target.getClass().getCreatureStats(target).getMagicEffects().get(ESM::MagicEffect::Reflect).getMagnitude();
bool isReflected = (Misc::Rng::roll0to99() < reflect);
@ -509,16 +499,17 @@ namespace MWMechanics
else if (castByPlayer)
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicTargetResisted}");
}
else if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful && castByPlayer && target != caster)
{
// If player is attempting to cast a harmful spell and it wasn't fully resisted, show the target's HP bar
MWBase::Environment::get().getWindowManager()->setEnemy(target);
}
if (target == getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState())
magnitudeMult = 0;
// If player is attempting to cast a harmful spell, show the target's HP bar
if (castByPlayer && target != caster)
MWBase::Environment::get().getWindowManager()->setEnemy(target);
// Notify the target actor they've been hit
if (target != caster && !caster.isEmpty())
if (target != caster && !caster.isEmpty() && magicEffect->mData.mFlags & ESM::MagicEffect::Harmful)
target.getClass().onHit(target, 0.0f, true, MWWorld::Ptr(), caster, osg::Vec3f(), true);
}
@ -1071,13 +1062,11 @@ namespace MWMechanics
return true;
}
void CastSpell::playSpellCastingEffects(const std::string &spellid)
{
void CastSpell::playSpellCastingEffects(const std::string &spellid){
const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();
const ESM::Spell *spell = store.get<ESM::Spell>().find(spellid);
std::vector<std::string> addedEffects;
for (std::vector<ESM::ENAMstruct>::const_iterator iter = spell->mEffects.mList.begin();
iter != spell->mEffects.mList.end(); ++iter)
{
@ -1086,72 +1075,18 @@ namespace MWMechanics
MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(mCaster);
const ESM::Static* castStatic;
if (!effect->mCasting.empty())
castStatic = store.get<ESM::Static>().find (effect->mCasting);
else
castStatic = store.get<ESM::Static>().find ("VFX_DefaultCast");
// check if the effect was already added
if (std::find(addedEffects.begin(), addedEffects.end(), "meshes\\" + castStatic->mModel) != addedEffects.end())
continue;
std::string texture = effect->mParticle;
float scale = 1.0f;
osg::Vec3f pos (mCaster.getRefData().getPosition().asVec3());
if (animation && mCaster.getClass().isNpc())
{
// For NOC we should take race height as scaling factor
const ESM::NPC *npc = mCaster.get<ESM::NPC>()->mBase;
const MWWorld::ESMStore &esmStore =
MWBase::Environment::get().getWorld()->getStore();
const ESM::Race *race =
esmStore.get<ESM::Race>().find(npc->mRace);
scale = npc->isMale() ? race->mData.mHeight.mMale : race->mData.mHeight.mFemale;
}
else
if (animation && mCaster.getClass().isActor()) // TODO: Non-actors should also create a spell cast vfx even if they are disabled (animation == NULL)
{
std::string casterModel = mCaster.getClass().getModel(mCaster);
osg::ref_ptr<osg::Node> model = MWBase::Environment::get().getWorld()->getInstance(casterModel);
const ESM::Static* castStatic;
osg::ComputeBoundsVisitor computeBoundsVisitor;
computeBoundsVisitor.setTraversalMask(~(MWRender::Mask_ParticleSystem|MWRender::Mask_Effect));
model->accept(computeBoundsVisitor);
osg::BoundingBox bounds = computeBoundsVisitor.getBoundingBox();
if (bounds.valid())
{
float meshSizeX = std::abs(bounds.xMax() - bounds.xMin());
float meshSizeY = std::abs(bounds.yMax() - bounds.yMin());
float meshSizeZ = std::abs(bounds.zMax() - bounds.zMin());
// TODO: take a size of particle or NPC with height and weight = 1.0 as scale = 1.0
float scaleX = meshSizeX/60.f;
float scaleY = meshSizeY/60.f;
float scaleZ = meshSizeZ/120.f;
scale = std::max({ scaleX, scaleY, scaleZ });
if (!effect->mCasting.empty())
castStatic = store.get<ESM::Static>().find (effect->mCasting);
else
castStatic = store.get<ESM::Static>().find ("VFX_DefaultCast");
//pos = bounds.center();
//pos[2] = bounds.zMin();
}
}
std::string texture = effect->mParticle;
// If the caster has no animation, add the effect directly to the effectManager
if (animation)
{
animation->addEffect("meshes\\" + castStatic->mModel, effect->mIndex, false, "", texture, scale);
}
else
{
// We should set scale for effect manager manually
float meshScale = !mCaster.getClass().isActor() ? mCaster.getCellRef().getScale() : 1.0f;
MWBase::Environment::get().getWorld()->spawnEffect("meshes\\" + castStatic->mModel, effect->mParticle, pos, scale * meshScale);
animation->addEffect("meshes\\" + castStatic->mModel, effect->mIndex, false, "", texture);
}
if (animation && !mCaster.getClass().isActor())
@ -1161,8 +1096,6 @@ namespace MWMechanics
"alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration"
};
addedEffects.push_back("meshes\\" + castStatic->mModel);
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
if(!effect->mCastSound.empty())
sndMgr->playSound3D(mCaster, effect->mCastSound, 1.0f, 1.0f);

@ -31,7 +31,7 @@ namespace MWPhysics
Actor::Actor(const MWWorld::Ptr& ptr, osg::ref_ptr<const Resource::BulletShape> shape, btCollisionWorld* world)
: mCanWaterWalk(false), mWalkingOnWater(false)
, mCollisionObject(nullptr), mForce(0.f, 0.f, 0.f), mOnGround(true), mOnSlope(false), mIdle(true)
, mCollisionObject(nullptr), mForce(0.f, 0.f, 0.f), mOnGround(true), mOnSlope(false)
, mInternalCollisionMode(true)
, mExternalCollisionMode(true)
, mCollisionWorld(world)
@ -229,11 +229,6 @@ void Actor::setOnSlope(bool slope)
mOnSlope = slope;
}
void Actor::setIdle(bool idle)
{
mIdle = idle;
}
bool Actor::isWalkingOnWater() const
{
return mWalkingOnWater;

@ -139,13 +139,6 @@ namespace MWPhysics
return mInternalCollisionMode && mOnSlope;
}
void setIdle(bool idle);
bool getIdle() const
{
return mIdle;
}
btCollisionObject* getCollisionObject() const
{
return mCollisionObject.get();
@ -186,7 +179,6 @@ namespace MWPhysics
osg::Vec3f mForce;
bool mOnGround;
bool mOnSlope;
bool mIdle;
bool mInternalCollisionMode;
bool mExternalCollisionMode;

@ -249,7 +249,7 @@ namespace MWPhysics
// Check if we actually found a valid spawn point (use an infinitely thin ray this time).
// Required for some broken door destinations in Morrowind.esm, where the spawn point
// intersects with other geometry if the actor's base is taken into account
btVector3 from = toBullet(position + offset);
btVector3 from = toBullet(position);
btVector3 to = from - btVector3(0,0,maxHeight);
btCollisionWorld::ClosestRayResultCallback resultCallback1(from, to);
@ -683,7 +683,6 @@ namespace MWPhysics
, mWaterEnabled(false)
, mParentNode(parentNode)
, mPhysicsDt(1.f / 60.f)
, mIdleUpdateTimer(0)
{
mResourceSystem->addResourceManager(mShapeManager.get());
@ -740,18 +739,6 @@ namespace MWPhysics
delete mBroadphase;
}
void PhysicsSystem::updateIdle()
{
for (ActorMap::iterator it = mActors.begin(); it != mActors.end(); ++it)
{
osg::Vec3f pos(it->second->getCollisionObjectPosition());
RayResult result = castRay(pos, pos - osg::Vec3f(0, 0, it->second->getHalfExtents().z() + 2), it->second->getPtr(), std::vector<MWWorld::Ptr>(), CollisionType_World|CollisionType_HeightMap|CollisionType_Door);
it->second->setIdle(result.mHit);
}
}
void PhysicsSystem::setUnrefQueue(SceneUtil::UnrefQueue *unrefQueue)
{
mUnrefQueue = unrefQueue;
@ -1080,11 +1067,6 @@ namespace MWPhysics
return physactor && physactor->getOnGround();
}
bool PhysicsSystem::isIdle(const MWWorld::Ptr &actor)
{
Actor* physactor = getActor(actor);
return physactor && physactor->getIdle();
}
bool PhysicsSystem::canMoveToWaterSurface(const MWWorld::ConstPtr &actor, const float waterlevel)
{
const Actor* physicActor = getActor(actor);
@ -1377,10 +1359,6 @@ namespace MWPhysics
cmode = !cmode;
found->second->enableCollisionMode(cmode);
found->second->enableCollisionBody(cmode);
if (cmode)
queueObjectMovement(MWMechanics::getPlayer(), osg::Vec3f(0, 0, -0.1f));
return cmode;
}
@ -1500,13 +1478,6 @@ namespace MWPhysics
for (std::set<Object*>::iterator it = mAnimatedObjects.begin(); it != mAnimatedObjects.end(); ++it)
(*it)->animateCollisionShapes(mCollisionWorld);
mIdleUpdateTimer -= dt;
if (mIdleUpdateTimer <= 0.f)
{
mIdleUpdateTimer = 0.5f;
updateIdle();
}
#ifndef BT_NO_PROFILE
CProfileManager::Reset();
CProfileManager::Increment_Frame_Counter();

@ -124,7 +124,6 @@ namespace MWPhysics
bool getLineOfSight(const MWWorld::ConstPtr& actor1, const MWWorld::ConstPtr& actor2) const;
bool isOnGround (const MWWorld::Ptr& actor);
bool isIdle (const MWWorld::Ptr& actor);
bool canMoveToWaterSurface (const MWWorld::ConstPtr &actor, const float waterlevel);
@ -184,7 +183,6 @@ namespace MWPhysics
private:
void updateWater();
void updateIdle();
osg::ref_ptr<SceneUtil::UnrefQueue> mUnrefQueue;
@ -233,7 +231,6 @@ namespace MWPhysics
osg::ref_ptr<osg::Group> mParentNode;
float mPhysicsDt;
float mIdleUpdateTimer;
PhysicsSystem (const PhysicsSystem&);
PhysicsSystem& operator= (const PhysicsSystem&);

@ -8,8 +8,6 @@
#include <osg/MatrixTransform>
#include <osg/BlendFunc>
#include <osg/Material>
#include <osg/ComputeBoundsVisitor>
#include <osg/PositionAttitudeTransform>
#include <osgParticle/ParticleSystem>
#include <osgParticle/ParticleProcessor>
@ -1117,6 +1115,8 @@ namespace MWRender
++stateiter;
}
updateEffects(duration);
if (mHeadController)
{
const float epsilon = 0.001f;
@ -1366,7 +1366,7 @@ namespace MWRender
useQuadratic, quadraticValue, quadraticRadiusMult, useLinear, linearRadiusMult, linearValue);
}
void Animation::addEffect (const std::string& model, int effectId, bool loop, const std::string& bonename, const std::string& texture, float scale)
void Animation::addEffect (const std::string& model, int effectId, bool loop, const std::string& bonename, const std::string& texture)
{
if (!mObjectRoot.get())
return;
@ -1417,13 +1417,7 @@ namespace MWRender
overrideFirstRootTexture(texture, mResourceSystem, node);
osg::Vec3f scale3f (scale, scale, scale);
osg::ref_ptr<osg::PositionAttitudeTransform> trans = new osg::PositionAttitudeTransform;
trans->setScale(scale3f);
trans->addChild(node);
parentNode->removeChild(node);
parentNode->addChild(trans);
// TODO: in vanilla morrowind the effect is scaled based on the host object's bounding box.
mEffects.push_back(params);
}

@ -364,7 +364,7 @@ public:
* @param texture override the texture specified in the model's materials - if empty, do not override
* @note Will not add an effect twice.
*/
void addEffect (const std::string& model, int effectId, bool loop = false, const std::string& bonename = "", const std::string& texture = "", float scale = 1.0f);
void addEffect (const std::string& model, int effectId, bool loop = false, const std::string& bonename = "", const std::string& texture = "");
void removeEffect (int effectId);
void getLoopingEffects (std::vector<int>& out) const;
@ -446,6 +446,7 @@ public:
void setLoopingEnabled(const std::string &groupname, bool enabled);
/// This is typically called as part of runAnimation, but may be called manually if needed.
void updateEffects(float duration);
/// Return a node with the specified name, or NULL if not existing.

@ -26,13 +26,6 @@ EffectManager::~EffectManager()
}
void EffectManager::addEffect(const std::string &model, const std::string& textureOverride, const osg::Vec3f &worldPosition, float scale, bool isMagicVFX)
{
osg::Vec3f scale3f (scale, scale, scale);
addEffect(model, textureOverride, worldPosition, scale3f, isMagicVFX);
}
void EffectManager::addEffect(const std::string &model, const std::string& textureOverride, const osg::Vec3f &worldPosition, const osg::Vec3f &scale, bool isMagicVFX)
{
osg::ref_ptr<osg::Node> node = mResourceSystem->getSceneManager()->getInstance(model);
@ -47,7 +40,7 @@ void EffectManager::addEffect(const std::string &model, const std::string& textu
osg::ref_ptr<osg::PositionAttitudeTransform> trans = new osg::PositionAttitudeTransform;
trans->setPosition(worldPosition);
trans->setScale(scale);
trans->setScale(osg::Vec3f(scale, scale, scale));
trans->addChild(node);
SceneUtil::AssignControllerSourcesVisitor assignVisitor(effect.mAnimTime);

@ -33,7 +33,6 @@ namespace MWRender
/// Add an effect. When it's finished playing, it will be removed automatically.
void addEffect (const std::string& model, const std::string& textureOverride, const osg::Vec3f& worldPosition, float scale, bool isMagicVFX = true);
void addEffect (const std::string &model, const std::string& textureOverride, const osg::Vec3f &worldPosition, const osg::Vec3f &scale3f, bool isMagicVFX);
void update(float dt);

@ -138,14 +138,6 @@ bool Objects::removeObject (const MWWorld::Ptr& ptr)
return false;
}
void Objects::updateEffects(float duration)
{
for(PtrAnimationMap::iterator iter = mObjects.begin();iter != mObjects.end();)
{
iter->second->updateEffects(duration);
++iter;
}
}
void Objects::removeCell(const MWWorld::CellStore* store)
{

@ -88,8 +88,6 @@ public:
bool removeObject (const MWWorld::Ptr& ptr);
///< \return found?
void updateEffects(float duration);
void removeCell(const MWWorld::CellStore* store);
/// Updates containing cell for object rendering data

@ -557,7 +557,6 @@ namespace MWRender
mEffectManager->update(dt);
mSky->update(dt);
mWater->update(dt);
mObjects->updateEffects(dt);
}
mCamera->update(dt, paused);
@ -842,11 +841,9 @@ namespace MWRender
mObjects->updatePtr(old, updated);
}
void RenderingManager::spawnEffect(const std::string &model, const std::string& texture, const osg::Vec3f &worldPosition, float scale, bool isMagicVFX)
void RenderingManager::spawnEffect(const std::string &model, const std::string &texture, const osg::Vec3f &worldPosition, float scale, bool isMagicVFX)
{
osg::Vec3f scale3f (scale, scale, scale);
mEffectManager->addEffect(model, texture, worldPosition, scale3f, isMagicVFX);
mEffectManager->addEffect(model, texture, worldPosition, scale, isMagicVFX);
}
void RenderingManager::notifyWorldSpaceChanged()
@ -1125,12 +1122,6 @@ namespace MWRender
updateProjectionMatrix();
}
}
osg::ref_ptr<osg::Node> RenderingManager::getInstance(const std::string& modelName)
{
return mResourceSystem->getSceneManager()->getInstance(modelName);
}
void RenderingManager::exportSceneGraph(const MWWorld::Ptr &ptr, const std::string &filename, const std::string &format)
{
osg::Node* node = mViewer->getSceneData();

@ -205,8 +205,6 @@ namespace MWRender
LandManager* getLandManager() const;
osg::ref_ptr<osg::Node> getInstance(const std::string& modelName);
private:
void updateProjectionMatrix();
void updateTextureFiltering();

@ -26,7 +26,6 @@
#include <components/esm/loadcrea.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/scriptmanager.hpp"
#include "../mwbase/world.hpp"
@ -771,8 +770,6 @@ namespace MWScript
virtual void execute (Interpreter::Runtime& runtime)
{
MWWorld::Ptr ptr = R()(runtime);
MWBase::Environment::get().getWorld()->queueMovement(ptr, osg::Vec3f(0, 0, -0.1f));
}
};
@ -1124,7 +1121,6 @@ namespace MWScript
MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr (targetId, false);
MWMechanics::CastSpell cast(ptr, target);
cast.playSpellCastingEffects(spell);
cast.mHitPosition = target.getRefData().getPosition().asVec3();
cast.mAlwaysSucceed = true;
cast.cast(spell);

@ -506,7 +506,7 @@ void MWState::StateManager::loadGame (const Character *character, const std::str
if (firstPersonCam != MWBase::Environment::get().getWorld()->isFirstPerson())
MWBase::Environment::get().getWorld()->togglePOV();
const MWWorld::Ptr ptr = MWMechanics::getPlayer();
MWWorld::ConstPtr ptr = MWMechanics::getPlayer();
if (ptr.isInCell())
{
@ -538,9 +538,6 @@ void MWState::StateManager::loadGame (const Character *character, const std::str
// Since we passed "changeEvent=false" to changeCell, we shouldn't have triggered the cell change flag.
// But make sure the flag is cleared anyway in case it was set from an earlier game.
MWBase::Environment::get().getWorld()->markCellAsUnchanged();
// Workaround to fix camera position upon game load
MWBase::Environment::get().getWorld()->queueMovement(ptr, osg::Vec3f(0, 0, 0));
}
catch (const std::exception& e)
{

@ -1439,6 +1439,8 @@ namespace MWWorld
if (pos.z() < terrainHeight)
pos.z() = terrainHeight;
pos.z() += 20; // place slightly above. will snap down to ground with code below
if (force || !isFlying(ptr))
{
osg::Vec3f traced = mPhysics->traceDown(ptr, pos, 500);
@ -1684,11 +1686,6 @@ namespace MWWorld
}
}
osg::ref_ptr<osg::Node> World::getInstance (const std::string& modelName)
{
return mRendering->getInstance(modelName);
}
const ESM::Potion *World::createRecord (const ESM::Potion& record)
{
return mStore.insert(record);
@ -2316,11 +2313,6 @@ namespace MWWorld
return mPhysics->isOnGround(ptr);
}
bool World::isIdle(const MWWorld::Ptr &ptr) const
{
return mPhysics->isIdle(ptr);
}
void World::togglePOV()
{
mRendering->togglePOV();
@ -3589,9 +3581,9 @@ namespace MWWorld
mRendering->spawnEffect(model, texture, worldPosition, 1.0f, false);
}
void World::spawnEffect(const std::string &model, const std::string &textureOverride, const osg::Vec3f &worldPos, float scale, bool isMagicVFX)
void World::spawnEffect(const std::string &model, const std::string &textureOverride, const osg::Vec3f &worldPos)
{
mRendering->spawnEffect(model, textureOverride, worldPos, scale, isMagicVFX);
mRendering->spawnEffect(model, textureOverride, worldPos);
}
void World::explodeSpell(const osg::Vec3f& origin, const ESM::EffectList& effects, const Ptr& caster, const Ptr& ignore, ESM::RangeType rangeType,

@ -553,7 +553,6 @@ namespace MWWorld
bool isWading(const MWWorld::ConstPtr &object) const override;
bool isWaterWalkingCastableOnTarget(const MWWorld::ConstPtr &target) const override;
bool isOnGround(const MWWorld::Ptr &ptr) const override;
bool isIdle(const MWWorld::Ptr &ptr) const override;
osg::Matrixf getActorHeadTransform(const MWWorld::ConstPtr& actor) const override;
@ -725,7 +724,7 @@ namespace MWWorld
/// Spawn a blood effect for \a ptr at \a worldPosition
void spawnBloodEffect (const MWWorld::Ptr& ptr, const osg::Vec3f& worldPosition) override;
void spawnEffect (const std::string& model, const std::string& textureOverride, const osg::Vec3f& worldPos, float scale = 1.f, bool isMagicVFX = true) override;
void spawnEffect (const std::string& model, const std::string& textureOverride, const osg::Vec3f& worldPos) override;
void explodeSpell(const osg::Vec3f& origin, const ESM::EffectList& effects, const MWWorld::Ptr& caster, const MWWorld::Ptr& ignore,
ESM::RangeType rangeType, const std::string& id, const std::string& sourceName,
@ -765,8 +764,6 @@ namespace MWWorld
/// Preload VFX associated with this effect list
void preloadEffects(const ESM::EffectList* effectList) override;
osg::ref_ptr<osg::Node> getInstance (const std::string& modelName);
};
}

@ -18,8 +18,8 @@ platform:
- x64
configuration:
- Debug
# - Release
# - Debug
- Release
# For the Qt, Boost, CMake, etc installs
#os: Visual Studio 2017
@ -55,6 +55,10 @@ build_script:
- cmd: if %PLATFORM%==x64 set build=MSVC%msvc%_64
- cmd: msbuild %build%\OpenMW.sln /t:Build /p:Configuration=%configuration% /m:2 /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
after_build:
- cmd: if %PLATFORM%==x64 7z a OpenMW_MSVC%msvc%_x64.zip %APPVEYOR_BUILD_FOLDER%\MSVC%msvc%_64\Release\ -xr"!*.pdb"
- cmd: if %PLATFORM%==x64 7z a OpenMW_MSVC%msvc%_x64_pdb.zip %APPVEYOR_BUILD_FOLDER%\MSVC%msvc%_64\Release\*.pdb
test: off
#notifications:
@ -63,3 +67,9 @@ test: off
# -
# on_build_failure: true
# on_build_status_changed: true
artifacts:
- path: OpenMW_MSVC%msvc%_x64.zip
name: OpenMW_MSVC%msvc%_x64
- path: OpenMW_MSVC%msvc%_x64_pdb.zip
name: OpenMW_MSVC%msvc%_x64_pdb

@ -6,6 +6,8 @@
#include <string>
#include <algorithm>
#include "utf8stream.hpp"
namespace Misc
{
class StringUtils
@ -56,6 +58,70 @@ public:
};
}
static Utf8Stream::UnicodeChar toLowerUtf8(Utf8Stream::UnicodeChar ch)
{
// Russian alphabet
if (ch >= 0x0410 && ch < 0x0430)
return ch += 0x20;
// Cyrillic IO character
if (ch == 0x0401)
return ch += 0x50;
// Latin alphabet
if (ch >= 0x41 && ch < 0x60)
return ch += 0x20;
// Deutch characters
if (ch == 0xc4 || ch == 0xd6 || ch == 0xdc)
return ch += 0x20;
if (ch == 0x1e9e)
return 0xdf;
// TODO: probably we will need to support characters from other languages
return ch;
}
static std::string lowerCaseUtf8(const std::string str)
{
if (str.empty())
return str;
// Decode string as utf8 characters, convert to lower case and pack them to string
std::string out;
Utf8Stream stream (str.c_str());
while (!stream.eof ())
{
Utf8Stream::UnicodeChar character = toLowerUtf8(stream.peek());
if (character <= 0x7f)
out.append(1, static_cast<char>(character));
else if (character <= 0x7ff)
{
out.append(1, static_cast<char>(0xc0 | ((character >> 6) & 0x1f)));
out.append(1, static_cast<char>(0x80 | (character & 0x3f)));
}
else if (character <= 0xffff)
{
out.append(1, static_cast<char>(0xe0 | ((character >> 12) & 0x0f)));
out.append(1, static_cast<char>(0x80 | ((character >> 6) & 0x3f)));
out.append(1, static_cast<char>(0x80 | (character & 0x3f)));
}
else
{
out.append(1, static_cast<char>(0xf0 | ((character >> 18) & 0x07)));
out.append(1, static_cast<char>(0x80 | ((character >> 12) & 0x3f)));
out.append(1, static_cast<char>(0x80 | ((character >> 6) & 0x3f)));
out.append(1, static_cast<char>(0x80 | (character & 0x3f)));
}
stream.consume();
}
return out;
}
static bool ciLess(const std::string &x, const std::string &y) {
return std::lexicographical_compare(x.begin(), x.end(), y.begin(), y.end(), ci());
}

@ -1,6 +1,7 @@
#ifndef MISC_UTF8ITER_HPP
#define MISC_UTF8ITER_HPP
#include <cstring>
#include <tuple>
class Utf8Stream

@ -10,7 +10,11 @@
</Widget>
<!-- Spell list -->
<Widget type="SpellView" skin="MW_SpellView" position="8 38 268 518" align="Left Top Stretch" name="SpellView">
<Widget type="SpellView" skin="MW_SpellView" position="8 38 268 490" align="Left Top Stretch" name="SpellView">
</Widget>
<!-- Search box-->
<Widget type="EditBox" skin="MW_TextBoxEditWithBorder" position="8 535 268 23" align="Left Bottom HStretch" name="FilterEdit">
</Widget>
</Widget>

@ -68,7 +68,7 @@
<file alias="edit-preview">record-preview.png</file>
<file alias="edit-clone">record-clone.png</file>
<file alias="edit-add">record-add.png</file>
<file alias="resources-icon">resources-icon.png</file>
<file alias="resources-icon">resources-icon.png</file>
<file alias="resources-mesh">resources-mesh.png</file>
<file alias="resources-music">resources-music.png</file>
<file alias="resources-sound">resources-sound.png</file>
@ -149,6 +149,10 @@
<file alias="transform-scale">transform-scale.png</file>
<file alias="selection-mode-cube">selection-mode-cube.png</file>
<file alias="selection-mode-cube-corner">selection-mode-cube-corner.png</file>
<file alias="selection-mode-cube-sphere">selection-mode-cube-sphere.png</file>
<file alias="selection-mode-cube-sphere">selection-mode-cube-sphere.png</file>
<file alias="brush-point">brush-point.png</file>
<file alias="brush-square">brush-square.png</file>
<file alias="brush-circle">brush-circle.png</file>
<file alias="brush-custom">brush-custom.png</file>
</qresource>
</RCC>

@ -217,7 +217,7 @@ followers attack on sight = false
can loot during death animation = true
# Makes the value of filled soul gems dependent only on soul magnitude (with formula from the Morrowind Code Patch)
rebalance soul gem values = true
rebalance soul gem values = false
[General]

@ -112,7 +112,7 @@
<item>
<widget class="QCheckBox" name="rebalanceSoulGemValuesCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If this setting is true, the value of filled soul gems is dependent only on soul magnitude.&lt;/p&gt;&lt;p&gt;The default value is true.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If this setting is true, the value of filled soul gems is dependent only on soul magnitude.&lt;/p&gt;&lt;p&gt;The default value is false.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Rebalance soul gem values</string>

Loading…
Cancel
Save