Merge branch 'master' into activation

pull/541/head
Andrei Kortunov 5 years ago committed by GitHub
commit 649a14dfae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -139,11 +139,13 @@
Bug #5137: Textures with Clamp Mode set to Clamp instead of Wrap are too dark outside the boundaries Bug #5137: Textures with Clamp Mode set to Clamp instead of Wrap are too dark outside the boundaries
Bug #5149: Failing lock pick attempts isn't always a crime Bug #5149: Failing lock pick attempts isn't always a crime
Bug #5188: Objects without a name don't fallback to their ID Bug #5188: Objects without a name don't fallback to their ID
Bug #5161: Creature companions can't be activated when they are knocked down
Feature #1774: Handle AvoidNode Feature #1774: Handle AvoidNode
Feature #2229: Improve pathfinding AI Feature #2229: Improve pathfinding AI
Feature #3025: Analogue gamepad movement controls Feature #3025: Analogue gamepad movement controls
Feature #3442: Default values for fallbacks from ini file Feature #3442: Default values for fallbacks from ini file
Feature #3610: Option to invert X axis Feature #3610: Option to invert X axis
Feature #3871: Editor: Terrain Selection
Feature #3893: Implicit target for "set" function in console Feature #3893: Implicit target for "set" function in console
Feature #3980: In-game option to disable controller Feature #3980: In-game option to disable controller
Feature #3999: Shift + Double Click should maximize/restore menu size Feature #3999: Shift + Double Click should maximize/restore menu size

@ -89,7 +89,7 @@ opencs_units (view/render
scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget
previewwidget editmode instancemode instanceselectionmode instancemovemode previewwidget editmode instancemode instanceselectionmode instancemovemode
orbitcameramode pathgridmode selectionmode pathgridselectionmode cameracontroller orbitcameramode pathgridmode selectionmode pathgridselectionmode cameracontroller
cellwater terraintexturemode actor cellwater terraintexturemode actor terrainselection
) )
opencs_units_noqt (view/render opencs_units_noqt (view/render

@ -8,13 +8,6 @@
#include <components/esm/loadland.hpp> #include <components/esm/loadland.hpp>
#include <components/misc/constants.hpp> #include <components/misc/constants.hpp>
namespace
{
const int cellSize {ESM::Land::REAL_SIZE};
const int landSize {ESM::Land::LAND_SIZE};
const int landTextureSize {ESM::Land::LAND_TEXTURE_SIZE};
}
CSMWorld::CellCoordinates::CellCoordinates() : mX (0), mY (0) {} CSMWorld::CellCoordinates::CellCoordinates() : mX (0), mY (0) {}
CSMWorld::CellCoordinates::CellCoordinates (int x, int y) : mX (x), mY (y) {} CSMWorld::CellCoordinates::CellCoordinates (int x, int y) : mX (x), mY (y) {}
@ -76,10 +69,10 @@ std::pair<int, int> CSMWorld::CellCoordinates::coordinatesToCellIndex (float x,
return std::make_pair (std::floor (x / Constants::CellSizeInUnits), std::floor (y / Constants::CellSizeInUnits)); return std::make_pair (std::floor (x / Constants::CellSizeInUnits), std::floor (y / Constants::CellSizeInUnits));
} }
std::pair<int, int> CSMWorld::CellCoordinates::toTextureCoords(osg::Vec3d worldPos) std::pair<int, int> CSMWorld::CellCoordinates::toTextureCoords(const osg::Vec3d& worldPos)
{ {
const auto xd = static_cast<float>(worldPos.x() * landTextureSize / cellSize - 0.25f); const auto xd = static_cast<float>(worldPos.x() * ESM::Land::LAND_TEXTURE_SIZE / ESM::Land::REAL_SIZE - 0.25f);
const auto yd = static_cast<float>(worldPos.y() * landTextureSize / cellSize + 0.25f); const auto yd = static_cast<float>(worldPos.y() * ESM::Land::LAND_TEXTURE_SIZE / ESM::Land::REAL_SIZE + 0.25f);
const auto x = static_cast<int>(std::floor(xd)); const auto x = static_cast<int>(std::floor(xd));
const auto y = static_cast<int>(std::floor(yd)); const auto y = static_cast<int>(std::floor(yd));
@ -87,10 +80,10 @@ std::pair<int, int> CSMWorld::CellCoordinates::toTextureCoords(osg::Vec3d worldP
return std::make_pair(x, y); return std::make_pair(x, y);
} }
std::pair<int, int> CSMWorld::CellCoordinates::toVertexCoords(osg::Vec3d worldPos) std::pair<int, int> CSMWorld::CellCoordinates::toVertexCoords(const osg::Vec3d& worldPos)
{ {
const auto xd = static_cast<float>(worldPos.x() * (landSize - 1) / cellSize + 0.5f); const auto xd = static_cast<float>(worldPos.x() * (ESM::Land::LAND_SIZE - 1) / ESM::Land::REAL_SIZE + 0.5f);
const auto yd = static_cast<float>(worldPos.y() * (landSize - 1) / cellSize + 0.5f); const auto yd = static_cast<float>(worldPos.y() * (ESM::Land::LAND_SIZE - 1) / ESM::Land::REAL_SIZE + 0.5f);
const auto x = static_cast<int>(std::floor(xd)); const auto x = static_cast<int>(std::floor(xd));
const auto y = static_cast<int>(std::floor(yd)); const auto y = static_cast<int>(std::floor(yd));
@ -98,25 +91,32 @@ std::pair<int, int> CSMWorld::CellCoordinates::toVertexCoords(osg::Vec3d worldPo
return std::make_pair(x, y); return std::make_pair(x, y);
} }
float CSMWorld::CellCoordinates::textureSelectionToWorldCoords(int pos) float CSMWorld::CellCoordinates::textureGlobalToWorldCoords(int textureGlobal)
{ {
return cellSize * static_cast<float>(pos) / landTextureSize; return ESM::Land::REAL_SIZE * static_cast<float>(textureGlobal) / ESM::Land::LAND_TEXTURE_SIZE;
} }
float CSMWorld::CellCoordinates::vertexSelectionToWorldCoords(int pos) float CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(int vertexGlobal)
{ {
return cellSize * static_cast<float>(pos) / (landSize - 1); return ESM::Land::REAL_SIZE * static_cast<float>(vertexGlobal) / (ESM::Land::LAND_SIZE - 1);
} }
int CSMWorld::CellCoordinates::vertexSelectionToInCellCoords(int pos) int CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(int vertexGlobal)
{ {
return static_cast<int>(pos - std::floor(static_cast<float>(pos) / (landSize - 1)) * (landSize - 1)); return static_cast<int>(vertexGlobal - std::floor(static_cast<float>(vertexGlobal) / (ESM::Land::LAND_SIZE - 1)) * (ESM::Land::LAND_SIZE - 1));
}
std::string CSMWorld::CellCoordinates::textureGlobalToCellId(const std::pair<int, int>& textureGlobal)
{
int x = std::floor(static_cast<float>(textureGlobal.first) / ESM::Land::LAND_TEXTURE_SIZE);
int y = std::floor(static_cast<float>(textureGlobal.second) / ESM::Land::LAND_TEXTURE_SIZE);
return generateId(x, y);
} }
std::string CSMWorld::CellCoordinates::vertexGlobalToCellId(std::pair<int, int> vertexGlobal) std::string CSMWorld::CellCoordinates::vertexGlobalToCellId(const std::pair<int, int>& vertexGlobal)
{ {
int x = std::floor(static_cast<float>(vertexGlobal.first) / (landSize - 1)); int x = std::floor(static_cast<float>(vertexGlobal.first) / (ESM::Land::LAND_SIZE - 1));
int y = std::floor(static_cast<float>(vertexGlobal.second) / (landSize - 1)); int y = std::floor(static_cast<float>(vertexGlobal.second) / (ESM::Land::LAND_SIZE - 1));
return generateId(x, y); return generateId(x, y);
} }

@ -49,22 +49,25 @@ namespace CSMWorld
static std::pair<int, int> coordinatesToCellIndex (float x, float y); static std::pair<int, int> coordinatesToCellIndex (float x, float y);
///Converts worldspace coordinates to global texture selection, taking in account the texture offset. ///Converts worldspace coordinates to global texture selection, taking in account the texture offset.
static std::pair<int, int> toTextureCoords(osg::Vec3d worldPos); static std::pair<int, int> toTextureCoords(const osg::Vec3d& worldPos);
///Converts worldspace coordinates to global vertex selection. ///Converts worldspace coordinates to global vertex selection.
static std::pair<int, int> toVertexCoords(osg::Vec3d worldPos); static std::pair<int, int> toVertexCoords(const osg::Vec3d& worldPos);
///Converts global texture coordinate to worldspace coordinate that is at the upper left corner of the selected texture. ///Converts global texture coordinate to worldspace coordinate that is at the upper left corner of the selected texture.
static float textureSelectionToWorldCoords(int); static float textureGlobalToWorldCoords(int textureGlobal);
///Converts global vertex coordinate to worldspace coordinate ///Converts global vertex coordinate to worldspace coordinate
static float vertexSelectionToWorldCoords(int); static float vertexGlobalToWorldCoords(int vertexGlobal);
///Converts local cell's heightmap coordinates from the global vertex coordinate ///Converts global vertex coordinate to local cell's heightmap coordinates
static int vertexSelectionToInCellCoords(int); static int vertexGlobalToInCellCoords(int vertexGlobal);
///Converts global texture coordinates to cell id
static std::string textureGlobalToCellId(const std::pair<int, int>& textureGlobal);
///Converts global vertex coordinates to cell id ///Converts global vertex coordinates to cell id
static std::string vertexGlobalToCellId(std::pair<int, int>); static std::string vertexGlobalToCellId(const std::pair<int, int>& vertexGlobal);
}; };
bool operator== (const CellCoordinates& left, const CellCoordinates& right); bool operator== (const CellCoordinates& left, const CellCoordinates& right);

@ -140,7 +140,7 @@ void CSVRender::PagedWorldspaceWidget::addEditModeSelectorButtons (
new EditMode (this, QIcon (":placeholder"), Mask_Reference, "Terrain shape editing"), new EditMode (this, QIcon (":placeholder"), Mask_Reference, "Terrain shape editing"),
"terrain-shape"); "terrain-shape");
tool->addButton ( tool->addButton (
new TerrainTextureMode (this, tool), new TerrainTextureMode (this, mRootNode, tool),
"terrain-texture"); "terrain-texture");
tool->addButton ( tool->addButton (
new EditMode (this, QIcon (":placeholder"), Mask_Reference, "Terrain vertex paint editing"), new EditMode (this, QIcon (":placeholder"), Mask_Reference, "Terrain vertex paint editing"),

@ -0,0 +1,261 @@
#include "terrainselection.hpp"
#include <algorithm>
#include <osg/Group>
#include <osg/Geometry>
#include <osg/PositionAttitudeTransform>
#include <components/esm/loadland.hpp>
#include "../../model/world/cellcoordinates.hpp"
#include "../../model/world/columnimp.hpp"
#include "../../model/world/idtable.hpp"
#include "cell.hpp"
#include "worldspacewidget.hpp"
CSVRender::TerrainSelection::TerrainSelection(osg::Group* parentNode, WorldspaceWidget *worldspaceWidget, TerrainSelectionType type):
mParentNode(parentNode), mWorldspaceWidget (worldspaceWidget), mDraggedOperationFlag(false), mSelectionType(type)
{
mGeometry = new osg::Geometry();
mSelectionNode = new osg::Group();
mSelectionNode->addChild(mGeometry);
activate();
}
CSVRender::TerrainSelection::~TerrainSelection()
{
deactivate();
}
std::vector<std::pair<int, int>> CSVRender::TerrainSelection::getTerrainSelection() const
{
return mSelection;
}
void CSVRender::TerrainSelection::onlySelect(const std::vector<std::pair<int, int>> &localPositions)
{
mSelection = localPositions;
update();
}
void CSVRender::TerrainSelection::addSelect(const std::pair<int, int> &localPos)
{
if (std::find(mSelection.begin(), mSelection.end(), localPos) == mSelection.end())
{
mSelection.emplace_back(localPos);
update();
}
}
void CSVRender::TerrainSelection::toggleSelect(const std::vector<std::pair<int, int>> &localPositions, bool toggleInProgress)
{
if (toggleInProgress)
{
for(auto const& localPos: localPositions)
{
auto iterTemp = std::find(mTemporarySelection.begin(), mTemporarySelection.end(), localPos);
mDraggedOperationFlag = true;
if (iterTemp == mTemporarySelection.end())
{
auto iter = std::find(mSelection.begin(), mSelection.end(), localPos);
if (iter != mSelection.end())
{
mSelection.erase(iter);
}
else
{
mSelection.emplace_back(localPos);
}
}
mTemporarySelection.push_back(localPos);
}
}
else if (mDraggedOperationFlag == false)
{
for(auto const& localPos: localPositions)
{
const auto iter = std::find(mSelection.begin(), mSelection.end(), localPos);
if (iter != mSelection.end())
{
mSelection.erase(iter);
}
else
{
mSelection.emplace_back(localPos);
}
}
}
else
{
mDraggedOperationFlag = false;
mTemporarySelection.clear();
}
update();
}
void CSVRender::TerrainSelection::activate()
{
if (!mParentNode->containsNode(mSelectionNode)) mParentNode->addChild(mSelectionNode);
}
void CSVRender::TerrainSelection::deactivate()
{
mParentNode->removeChild(mSelectionNode);
}
void CSVRender::TerrainSelection::update()
{
mSelectionNode->removeChild(mGeometry);
mGeometry = new osg::Geometry();
const osg::ref_ptr<osg::Vec3Array> vertices (new osg::Vec3Array);
switch (mSelectionType)
{
case TerrainSelectionType::Texture : drawTextureSelection(vertices);
break;
case TerrainSelectionType::Shape : drawShapeSelection(vertices);
break;
}
mGeometry->setVertexArray(vertices);
osg::ref_ptr<osg::DrawArrays> drawArrays = new osg::DrawArrays(osg::PrimitiveSet::LINES);
drawArrays->setCount(vertices->size());
if (vertices->size() != 0) mGeometry->addPrimitiveSet(drawArrays);
mSelectionNode->addChild(mGeometry);
}
void CSVRender::TerrainSelection::drawShapeSelection(const osg::ref_ptr<osg::Vec3Array> vertices)
{
if (!mSelection.empty())
{
for (std::pair<int, int> &localPos : mSelection)
{
int x (localPos.first);
int y (localPos.second);
float xWorldCoord(CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(x));
float yWorldCoord(CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(y));
osg::Vec3f pointXY(xWorldCoord, yWorldCoord, calculateLandHeight(x, y) + 2);
vertices->push_back(pointXY);
vertices->push_back(osg::Vec3f(xWorldCoord, CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(y - 1), calculateLandHeight(x, y - 1) + 2));
vertices->push_back(pointXY);
vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(x - 1), yWorldCoord, calculateLandHeight(x - 1, y) + 2));
const auto north = std::find(mSelection.begin(), mSelection.end(), std::make_pair(x, y + 1));
if (north == mSelection.end())
{
vertices->push_back(pointXY);
vertices->push_back(osg::Vec3f(xWorldCoord, CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(y + 1), calculateLandHeight(x, y + 1) + 2));
}
const auto east = std::find(mSelection.begin(), mSelection.end(), std::make_pair(x + 1, y));
if (east == mSelection.end())
{
vertices->push_back(pointXY);
vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(x + 1), yWorldCoord, calculateLandHeight(x + 1, y) + 2));
}
}
}
}
void CSVRender::TerrainSelection::drawTextureSelection(const osg::ref_ptr<osg::Vec3Array> vertices)
{
if (!mSelection.empty())
{
// Nudge selection by 1/4th of a texture size, similar how blendmaps are nudged
const float nudgePercentage = 0.25f;
const int nudgeOffset = (ESM::Land::REAL_SIZE / ESM::Land::LAND_TEXTURE_SIZE) * nudgePercentage;
const int landHeightsNudge = (ESM::Land::REAL_SIZE / ESM::Land::LAND_SIZE) / (ESM::Land::LAND_SIZE - 1); // Does this work with all land size configurations?
const int textureSizeToLandSizeModifier = (ESM::Land::LAND_SIZE - 1) / ESM::Land::LAND_TEXTURE_SIZE;
for (std::pair<int, int> &localPos : mSelection)
{
int x (localPos.first);
int y (localPos.second);
// convert texture selection to global vertex coordinates at selection box corners
int x1 = x * textureSizeToLandSizeModifier + landHeightsNudge;
int x2 = x * textureSizeToLandSizeModifier + textureSizeToLandSizeModifier + landHeightsNudge;
int y1 = y * textureSizeToLandSizeModifier - landHeightsNudge;
int y2 = y * textureSizeToLandSizeModifier + textureSizeToLandSizeModifier - landHeightsNudge;
// Draw edges (check all sides, draw lines between vertices, +1 height to keep lines above ground)
// Check adjancent selections, draw lines only to edges of the selection
const auto north = std::find(mSelection.begin(), mSelection.end(), std::make_pair(x, y + 1));
if (north == mSelection.end())
{
for(int i = 1; i < (textureSizeToLandSizeModifier + 1); i++)
{
float drawPreviousX = CSMWorld::CellCoordinates::textureGlobalToWorldCoords(x) + (i - 1) * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1));
float drawCurrentX = CSMWorld::CellCoordinates::textureGlobalToWorldCoords(x) + i * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1));
vertices->push_back(osg::Vec3f(drawPreviousX + nudgeOffset, CSMWorld::CellCoordinates::textureGlobalToWorldCoords(y + 1) - nudgeOffset, calculateLandHeight(x1+(i-1), y2)+2));
vertices->push_back(osg::Vec3f(drawCurrentX + nudgeOffset, CSMWorld::CellCoordinates::textureGlobalToWorldCoords(y + 1) - nudgeOffset, calculateLandHeight(x1+i, y2)+2));
}
}
const auto south = std::find(mSelection.begin(), mSelection.end(), std::make_pair(x, y - 1));
if (south == mSelection.end())
{
for(int i = 1; i < (textureSizeToLandSizeModifier + 1); i++)
{
float drawPreviousX = CSMWorld::CellCoordinates::textureGlobalToWorldCoords(x) + (i - 1) *(ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1));
float drawCurrentX = CSMWorld::CellCoordinates::textureGlobalToWorldCoords(x) + i * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1));
vertices->push_back(osg::Vec3f(drawPreviousX + nudgeOffset, CSMWorld::CellCoordinates::textureGlobalToWorldCoords(y) - nudgeOffset, calculateLandHeight(x1+(i-1), y1)+2));
vertices->push_back(osg::Vec3f(drawCurrentX + nudgeOffset, CSMWorld::CellCoordinates::textureGlobalToWorldCoords(y) - nudgeOffset, calculateLandHeight(x1+i, y1)+2));
}
}
const auto east = std::find(mSelection.begin(), mSelection.end(), std::make_pair(x + 1, y));
if (east == mSelection.end())
{
for(int i = 1; i < (textureSizeToLandSizeModifier + 1); i++)
{
float drawPreviousY = CSMWorld::CellCoordinates::textureGlobalToWorldCoords(y) + (i - 1) * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1));
float drawCurrentY = CSMWorld::CellCoordinates::textureGlobalToWorldCoords(y) + i * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1));
vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::textureGlobalToWorldCoords(x + 1) + nudgeOffset, drawPreviousY - nudgeOffset, calculateLandHeight(x2, y1+(i-1))+2));
vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::textureGlobalToWorldCoords(x + 1) + nudgeOffset, drawCurrentY - nudgeOffset, calculateLandHeight(x2, y1+i)+2));
}
}
const auto west = std::find(mSelection.begin(), mSelection.end(), std::make_pair(x - 1, y));
if (west == mSelection.end())
{
for(int i = 1; i < (textureSizeToLandSizeModifier + 1); i++)
{
float drawPreviousY = CSMWorld::CellCoordinates::textureGlobalToWorldCoords(y) + (i - 1) * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1));
float drawCurrentY = CSMWorld::CellCoordinates::textureGlobalToWorldCoords(y) + i * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1));
vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::textureGlobalToWorldCoords(x) + nudgeOffset, drawPreviousY - nudgeOffset, calculateLandHeight(x1, y1+(i-1))+2));
vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::textureGlobalToWorldCoords(x) + nudgeOffset, drawCurrentY - nudgeOffset, calculateLandHeight(x1, y1+i)+2));
}
}
}
}
}
int CSVRender::TerrainSelection::calculateLandHeight(int x, int y) // global vertex coordinates
{
int cellX = std::floor((1.0f*x / (ESM::Land::LAND_SIZE - 1)));
int cellY = std::floor((1.0f*y / (ESM::Land::LAND_SIZE - 1)));
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);
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>();
return mPointer[localY*ESM::Land::LAND_SIZE + localX];
}

@ -0,0 +1,70 @@
#ifndef CSV_RENDER_TERRAINSELECTION_H
#define CSV_RENDER_TERRAINSELECTION_H
#include <utility>
#include <vector>
#include <osg/Vec3d>
#include <osg/ref_ptr>
#include <osg/PositionAttitudeTransform>
#include <components/esm/loadland.hpp>
#include "../../model/world/cellcoordinates.hpp"
namespace osg
{
class Group;
}
namespace CSVRender
{
struct WorldspaceHitResult;
class WorldspaceWidget;
enum class TerrainSelectionType
{
Texture,
Shape
};
/// \brief Class handling the terrain selection data and rendering
class TerrainSelection
{
public:
TerrainSelection(osg::Group* parentNode, WorldspaceWidget *worldspaceWidget, TerrainSelectionType type);
~TerrainSelection();
void onlySelect(const std::vector<std::pair<int, int>> &localPositions);
void addSelect(const std::pair<int, int> &localPos);
void toggleSelect(const std::vector<std::pair<int, int>> &localPositions, bool toggleInProgress);
void activate();
void deactivate();
std::vector<std::pair<int, int>> getTerrainSelection() const;
protected:
void update();
void drawShapeSelection(const osg::ref_ptr<osg::Vec3Array> vertices);
void drawTextureSelection(const osg::ref_ptr<osg::Vec3Array> vertices);
int calculateLandHeight(int x, int y);
private:
osg::Group* mParentNode;
WorldspaceWidget *mWorldspaceWidget;
osg::ref_ptr<osg::PositionAttitudeTransform> mBaseNode;
osg::ref_ptr<osg::Geometry> mGeometry;
osg::ref_ptr<osg::Group> mSelectionNode;
std::vector<std::pair<int, int>> mSelection; // Global terrain selection coordinate in either vertex or texture units
std::vector<std::pair<int, int>> mTemporarySelection; // Used during toggle to compare the most recent drag operation
bool mDraggedOperationFlag; //true during drag operation, false when click-operation
TerrainSelectionType mSelectionType;
};
}
#endif

@ -10,6 +10,8 @@
#include <QDragEnterEvent> #include <QDragEnterEvent>
#include <QDrag> #include <QDrag>
#include <osg/Group>
#include <components/esm/loadland.hpp> #include <components/esm/loadland.hpp>
#include "../widget/modebutton.hpp" #include "../widget/modebutton.hpp"
@ -36,12 +38,15 @@
#include "object.hpp" // Something small needed regarding pointers from here () #include "object.hpp" // Something small needed regarding pointers from here ()
#include "worldspacewidget.hpp" #include "worldspacewidget.hpp"
CSVRender::TerrainTextureMode::TerrainTextureMode (WorldspaceWidget *worldspaceWidget, QWidget *parent) CSVRender::TerrainTextureMode::TerrainTextureMode (WorldspaceWidget *worldspaceWidget, osg::Group* parentNode, QWidget *parent)
: EditMode (worldspaceWidget, QIcon {":scenetoolbar/editing-terrain-texture"}, Mask_Terrain | Mask_Reference, "Terrain texture editing", parent), : EditMode (worldspaceWidget, QIcon {":scenetoolbar/editing-terrain-texture"}, Mask_Terrain | Mask_Reference, "Terrain texture editing", parent),
mBrushTexture("L0#0"), mBrushTexture("L0#0"),
mBrushSize(0), mBrushSize(0),
mBrushShape(0), mBrushShape(0),
mTextureBrushScenetool(0) mTextureBrushScenetool(0),
mDragMode(InteractionType_None),
mParentNode(parentNode),
mIsEditing(false)
{ {
} }
@ -62,6 +67,11 @@ void CSVRender::TerrainTextureMode::activate(CSVWidget::SceneToolbar* toolbar)
connect(this, SIGNAL(passBrushTexture(std::string)), mTextureBrushScenetool, SLOT(updateBrushHistory(std::string))); connect(this, SIGNAL(passBrushTexture(std::string)), mTextureBrushScenetool, SLOT(updateBrushHistory(std::string)));
} }
if (!mTerrainTextureSelection)
{
mTerrainTextureSelection.reset(new TerrainSelection(mParentNode, &getWorldspaceWidget(), TerrainSelectionType::Texture));
}
EditMode::activate(toolbar); EditMode::activate(toolbar);
toolbar->addTool (mTextureBrushScenetool); toolbar->addTool (mTextureBrushScenetool);
} }
@ -74,6 +84,12 @@ void CSVRender::TerrainTextureMode::deactivate(CSVWidget::SceneToolbar* toolbar)
delete mTextureBrushScenetool; delete mTextureBrushScenetool;
mTextureBrushScenetool = 0; mTextureBrushScenetool = 0;
} }
if (mTerrainTextureSelection)
{
mTerrainTextureSelection.reset();
}
EditMode::deactivate(toolbar); EditMode::deactivate(toolbar);
} }
@ -95,10 +111,10 @@ void CSVRender::TerrainTextureMode::primaryEditPressed(const WorldspaceHitResult
CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures(); CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures();
int index = landtexturesCollection.searchId(mBrushTexture); int index = landtexturesCollection.searchId(mBrushTexture);
if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted() && hit.hit == true) if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted() && hit.hit && hit.tag == 0)
{ {
undoStack.beginMacro ("Edit texture records"); undoStack.beginMacro ("Edit texture records");
if(allowLandTextureEditing(mCellId)==true) if(allowLandTextureEditing(mCellId))
{ {
undoStack.push (new CSMWorld::TouchLandCommand(landTable, ltexTable, mCellId)); undoStack.push (new CSMWorld::TouchLandCommand(landTable, ltexTable, mCellId));
editTerrainTextureGrid(hit); editTerrainTextureGrid(hit);
@ -109,10 +125,18 @@ void CSVRender::TerrainTextureMode::primaryEditPressed(const WorldspaceHitResult
void CSVRender::TerrainTextureMode::primarySelectPressed(const WorldspaceHitResult& hit) void CSVRender::TerrainTextureMode::primarySelectPressed(const WorldspaceHitResult& hit)
{ {
if(hit.hit && hit.tag == 0)
{
selectTerrainTextures(CSMWorld::CellCoordinates::toTextureCoords(hit.worldPos), 0, false);
}
} }
void CSVRender::TerrainTextureMode::secondarySelectPressed(const WorldspaceHitResult& hit) void CSVRender::TerrainTextureMode::secondarySelectPressed(const WorldspaceHitResult& hit)
{ {
if(hit.hit && hit.tag == 0)
{
selectTerrainTextures(CSMWorld::CellCoordinates::toTextureCoords(hit.worldPos), 1, false);
}
} }
bool CSVRender::TerrainTextureMode::primaryEditStartDrag (const QPoint& pos) bool CSVRender::TerrainTextureMode::primaryEditStartDrag (const QPoint& pos)
@ -129,13 +153,16 @@ bool CSVRender::TerrainTextureMode::primaryEditStartDrag (const QPoint& pos)
QUndoStack& undoStack = document.getUndoStack(); QUndoStack& undoStack = document.getUndoStack();
mDragMode = InteractionType_PrimaryEdit;
CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures(); CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures();
int index = landtexturesCollection.searchId(mBrushTexture); int index = landtexturesCollection.searchId(mBrushTexture);
if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted()) if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted() && hit.hit && hit.tag == 0)
{ {
undoStack.beginMacro ("Edit texture records"); undoStack.beginMacro ("Edit texture records");
if(allowLandTextureEditing(mCellId)==true && hit.hit == true) mIsEditing = true;
if(allowLandTextureEditing(mCellId))
{ {
undoStack.push (new CSMWorld::TouchLandCommand(landTable, ltexTable, mCellId)); undoStack.push (new CSMWorld::TouchLandCommand(landTable, ltexTable, mCellId));
editTerrainTextureGrid(hit); editTerrainTextureGrid(hit);
@ -152,29 +179,64 @@ bool CSVRender::TerrainTextureMode::secondaryEditStartDrag (const QPoint& pos)
bool CSVRender::TerrainTextureMode::primarySelectStartDrag (const QPoint& pos) bool CSVRender::TerrainTextureMode::primarySelectStartDrag (const QPoint& pos)
{ {
WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask());
mDragMode = InteractionType_PrimarySelect;
if (!hit.hit || hit.tag != 0)
{
mDragMode = InteractionType_None;
return false;
}
selectTerrainTextures(CSMWorld::CellCoordinates::toTextureCoords(hit.worldPos), 0, true);
return false; return false;
} }
bool CSVRender::TerrainTextureMode::secondarySelectStartDrag (const QPoint& pos) bool CSVRender::TerrainTextureMode::secondarySelectStartDrag (const QPoint& pos)
{ {
WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask());
mDragMode = InteractionType_SecondarySelect;
if (!hit.hit || hit.tag != 0)
{
mDragMode = InteractionType_None;
return false;
}
selectTerrainTextures(CSMWorld::CellCoordinates::toTextureCoords(hit.worldPos), 1, true);
return false; return false;
} }
void CSVRender::TerrainTextureMode::drag (const QPoint& pos, int diffX, int diffY, double speedFactor) void CSVRender::TerrainTextureMode::drag (const QPoint& pos, int diffX, int diffY, double speedFactor)
{ {
if (mDragMode == InteractionType_PrimaryEdit)
{
WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask()); WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask());
std::string cellId = getWorldspaceWidget().getCellId (hit.worldPos);
CSMDoc::Document& document = getWorldspaceWidget().getDocument(); CSMDoc::Document& document = getWorldspaceWidget().getDocument();
CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures(); CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures();
int index = landtexturesCollection.searchId(mBrushTexture); int index = landtexturesCollection.searchId(mBrushTexture);
if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted() && hit.hit == true) if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted() && hit.hit && hit.tag == 0)
{ {
editTerrainTextureGrid(hit); editTerrainTextureGrid(hit);
} }
}
if (mDragMode == InteractionType_PrimarySelect)
{
WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask());
if (hit.hit && hit.tag == 0) selectTerrainTextures(CSMWorld::CellCoordinates::toTextureCoords(hit.worldPos), 0, true);
}
if (mDragMode == InteractionType_SecondarySelect)
{
WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask());
if (hit.hit && hit.tag == 0) selectTerrainTextures(CSMWorld::CellCoordinates::toTextureCoords(hit.worldPos), 1, true);
}
} }
void CSVRender::TerrainTextureMode::dragCompleted(const QPoint& pos) { void CSVRender::TerrainTextureMode::dragCompleted(const QPoint& pos)
{
if (mDragMode == InteractionType_PrimaryEdit && mIsEditing)
{
CSMDoc::Document& document = getWorldspaceWidget().getDocument(); CSMDoc::Document& document = getWorldspaceWidget().getDocument();
QUndoStack& undoStack = document.getUndoStack(); QUndoStack& undoStack = document.getUndoStack();
@ -184,15 +246,21 @@ void CSVRender::TerrainTextureMode::dragCompleted(const QPoint& pos) {
if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted()) if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted())
{ {
undoStack.endMacro(); undoStack.endMacro();
mIsEditing = false;
}
} }
} }
void CSVRender::TerrainTextureMode::dragAborted() { void CSVRender::TerrainTextureMode::dragAborted()
{
} }
void CSVRender::TerrainTextureMode::dragWheel (int diff, double speedFactor) {} void CSVRender::TerrainTextureMode::dragWheel (int diff, double speedFactor)
{
}
void CSVRender::TerrainTextureMode::handleDropEvent (QDropEvent *event) { void CSVRender::TerrainTextureMode::handleDropEvent (QDropEvent *event)
{
const CSMWorld::TableMimeData* mime = dynamic_cast<const CSMWorld::TableMimeData*> (event->mimeData()); 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 if (!mime) // May happen when non-records (e.g. plain text) are dragged and dropped
@ -228,7 +296,7 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe
*document.getData().getTableModel (CSMWorld::UniversalId::Type_Land)); *document.getData().getTableModel (CSMWorld::UniversalId::Type_Land));
mCellId = getWorldspaceWidget().getCellId (hit.worldPos); mCellId = getWorldspaceWidget().getCellId (hit.worldPos);
if(allowLandTextureEditing(mCellId)==true) {} if(allowLandTextureEditing(mCellId)) {}
std::pair<CSMWorld::CellCoordinates, bool> cellCoordinates_pair = CSMWorld::CellCoordinates::fromId (mCellId); std::pair<CSMWorld::CellCoordinates, bool> cellCoordinates_pair = CSMWorld::CellCoordinates::fromId (mCellId);
@ -236,8 +304,8 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe
int cellY = cellCoordinates_pair.first.getY(); int cellY = cellCoordinates_pair.first.getY();
// The coordinates of hit in mCellId // The coordinates of hit in mCellId
int xHitInCell (float(((hit.worldPos.x() - (cellX* cellSize)) * landTextureSize / cellSize) - 0.5)); int xHitInCell (float(((hit.worldPos.x() - (cellX* cellSize)) * landTextureSize / cellSize) - 0.25));
int yHitInCell (float(((hit.worldPos.y() - (cellY* cellSize)) * landTextureSize / cellSize) + 0.5)); int yHitInCell (float(((hit.worldPos.y() - (cellY* cellSize)) * landTextureSize / cellSize) + 0.25));
if (xHitInCell < 0) if (xHitInCell < 0)
{ {
xHitInCell = xHitInCell + landTextureSize; xHitInCell = xHitInCell + landTextureSize;
@ -249,8 +317,8 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe
cellY = cellY + 1; cellY = cellY + 1;
} }
mCellId = "#" + std::to_string(cellX) + " " + std::to_string(cellY); mCellId = CSMWorld::CellCoordinates::generateId(cellX, cellY);
if(allowLandTextureEditing(mCellId)==true) {} if(allowLandTextureEditing(mCellId)) {}
std::string iteratedCellId; std::string iteratedCellId;
@ -266,13 +334,13 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe
if (mBrushShape == 0) if (mBrushShape == 0)
{ {
CSMWorld::LandTexturesColumn::DataType mPointer = landTable.data(landTable.getModelIndex(mCellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>(); CSMWorld::LandTexturesColumn::DataType newTerrainPointer = landTable.data(landTable.getModelIndex(mCellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>();
CSMWorld::LandTexturesColumn::DataType mNew(mPointer); CSMWorld::LandTexturesColumn::DataType newTerrain(newTerrainPointer);
if(allowLandTextureEditing(mCellId)==true) if(allowLandTextureEditing(mCellId))
{ {
mNew[yHitInCell*landTextureSize+xHitInCell] = brushInt; newTerrain[yHitInCell*landTextureSize+xHitInCell] = brushInt;
pushEditToCommand(mNew, document, landTable, mCellId); pushEditToCommand(newTerrain, document, landTable, mCellId);
} }
} }
@ -292,11 +360,11 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe
{ {
for(int j_cell = upperLeftCellY; j_cell <= lowerrightCellY; j_cell++) for(int j_cell = upperLeftCellY; j_cell <= lowerrightCellY; j_cell++)
{ {
iteratedCellId = "#" + std::to_string(i_cell) + " " + std::to_string(j_cell); iteratedCellId = CSMWorld::CellCoordinates::generateId(i_cell, j_cell);
if(allowLandTextureEditing(iteratedCellId)==true) if(allowLandTextureEditing(iteratedCellId))
{ {
CSMWorld::LandTexturesColumn::DataType mPointer = landTable.data(landTable.getModelIndex(iteratedCellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>(); CSMWorld::LandTexturesColumn::DataType newTerrainPointer = landTable.data(landTable.getModelIndex(iteratedCellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>();
CSMWorld::LandTexturesColumn::DataType mNew(mPointer); CSMWorld::LandTexturesColumn::DataType newTerrain(newTerrainPointer);
for(int i = 0; i < landTextureSize; i++) for(int i = 0; i < landTextureSize; i++)
{ {
for(int j = 0; j < landTextureSize; j++) for(int j = 0; j < landTextureSize; j++)
@ -304,7 +372,7 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe
if (i_cell == cellX && j_cell == cellY && abs(i-xHitInCell) < r && abs(j-yHitInCell) < r) if (i_cell == cellX && j_cell == cellY && abs(i-xHitInCell) < r && abs(j-yHitInCell) < r)
{ {
mNew[j*landTextureSize+i] = brushInt; newTerrain[j*landTextureSize+i] = brushInt;
} }
else else
{ {
@ -316,11 +384,11 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe
if (j_cell > cellY) distanceY = -yHitInCell + landTextureSize * abs(j_cell-cellY) + j; if (j_cell > cellY) distanceY = -yHitInCell + landTextureSize * abs(j_cell-cellY) + j;
if (i_cell == cellX) distanceX = abs(i-xHitInCell); if (i_cell == cellX) distanceX = abs(i-xHitInCell);
if (j_cell == cellY) distanceY = abs(j-yHitInCell); if (j_cell == cellY) distanceY = abs(j-yHitInCell);
if (distanceX < r && distanceY < r) mNew[j*landTextureSize+i] = brushInt; if (distanceX < r && distanceY < r) newTerrain[j*landTextureSize+i] = brushInt;
} }
} }
} }
pushEditToCommand(mNew, document, landTable, iteratedCellId); pushEditToCommand(newTerrain, document, landTable, iteratedCellId);
} }
} }
} }
@ -342,11 +410,11 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe
{ {
for(int j_cell = upperLeftCellY; j_cell <= lowerrightCellY; j_cell++) for(int j_cell = upperLeftCellY; j_cell <= lowerrightCellY; j_cell++)
{ {
iteratedCellId = "#" + std::to_string(i_cell) + " " + std::to_string(j_cell); iteratedCellId = CSMWorld::CellCoordinates::generateId(i_cell, j_cell);
if(allowLandTextureEditing(iteratedCellId)==true) if(allowLandTextureEditing(iteratedCellId))
{ {
CSMWorld::LandTexturesColumn::DataType mPointer = landTable.data(landTable.getModelIndex(iteratedCellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>(); CSMWorld::LandTexturesColumn::DataType newTerrainPointer = landTable.data(landTable.getModelIndex(iteratedCellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>();
CSMWorld::LandTexturesColumn::DataType mNew(mPointer); CSMWorld::LandTexturesColumn::DataType newTerrain(newTerrainPointer);
for(int i = 0; i < landTextureSize; i++) for(int i = 0; i < landTextureSize; i++)
{ {
for(int j = 0; j < landTextureSize; j++) for(int j = 0; j < landTextureSize; j++)
@ -363,7 +431,7 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe
if (i_cell == cellX) distanceX = abs(i-xHitInCell); if (i_cell == cellX) distanceX = abs(i-xHitInCell);
if (j_cell == cellY) distanceY = abs(j-yHitInCell); if (j_cell == cellY) distanceY = abs(j-yHitInCell);
distance = std::round(sqrt(pow(distanceX, 2)+pow(distanceY, 2))); distance = std::round(sqrt(pow(distanceX, 2)+pow(distanceY, 2)));
if (distance < rf) mNew[j*landTextureSize+i] = brushInt; if (distance < rf) newTerrain[j*landTextureSize+i] = brushInt;
} }
else else
{ {
@ -376,11 +444,104 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe
if (i_cell == cellX) distanceX = abs(i-xHitInCell); if (i_cell == cellX) distanceX = abs(i-xHitInCell);
if (j_cell == cellY) distanceY = abs(j-yHitInCell); if (j_cell == cellY) distanceY = abs(j-yHitInCell);
distance = std::round(sqrt(pow(distanceX, 2)+pow(distanceY, 2))); distance = std::round(sqrt(pow(distanceX, 2)+pow(distanceY, 2)));
if (distance < rf) mNew[j*landTextureSize+i] = brushInt; if (distance < rf) newTerrain[j*landTextureSize+i] = brushInt;
}
}
}
pushEditToCommand(newTerrain, document, landTable, iteratedCellId);
}
}
}
}
if (mBrushShape == 3)
{
CSMWorld::LandTexturesColumn::DataType newTerrainPointer = landTable.data(landTable.getModelIndex(mCellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>();
CSMWorld::LandTexturesColumn::DataType newTerrain(newTerrainPointer);
if(allowLandTextureEditing(mCellId) && !mCustomBrushShape.empty())
{
for(auto const& value: mCustomBrushShape)
{
if(yHitInCell + value.second >= 0 && yHitInCell + value.second <= 15 && xHitInCell + value.first >= 0 && xHitInCell + value.first <= 15)
{
newTerrain[(yHitInCell+value.second)*landTextureSize+xHitInCell+value.first] = brushInt;
}
else
{
int cellXDifference = std::floor(1.0f*(xHitInCell + value.first)/landTextureSize);
int cellYDifference = std::floor(1.0f*(yHitInCell + value.second)/landTextureSize);
int xInOtherCell = xHitInCell + value.first - cellXDifference * landTextureSize;
int yInOtherCell = yHitInCell + value.second - cellYDifference * landTextureSize;
std::string cellId = CSMWorld::CellCoordinates::generateId(cellX+cellXDifference, cellY+cellYDifference);
if (allowLandTextureEditing(cellId))
{
CSMWorld::LandTexturesColumn::DataType newTerrainPointerOtherCell = landTable.data(landTable.getModelIndex(cellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>();
CSMWorld::LandTexturesColumn::DataType newTerrainOtherCell(newTerrainPointerOtherCell);
newTerrainOtherCell[yInOtherCell*landTextureSize+xInOtherCell] = brushInt;
pushEditToCommand(newTerrainOtherCell, document, landTable, cellId);
}
}
}
pushEditToCommand(newTerrain, document, landTable, mCellId);
}
} }
}
bool CSVRender::TerrainTextureMode::isInCellSelection(int globalSelectionX, int globalSelectionY)
{
if (CSVRender::PagedWorldspaceWidget *paged = dynamic_cast<CSVRender::PagedWorldspaceWidget *> (&getWorldspaceWidget()))
{
std::pair<int, int> textureCoords = std::make_pair(globalSelectionX, globalSelectionY);
std::string cellId = CSMWorld::CellCoordinates::textureGlobalToCellId(textureCoords);
return paged->getCellSelection().has(CSMWorld::CellCoordinates::fromId(cellId).first);
} }
return false;
}
void CSVRender::TerrainTextureMode::selectTerrainTextures(const std::pair<int, int>& texCoords, unsigned char selectMode, bool dragOperation)
{
int r = mBrushSize / 2;
std::vector<std::pair<int, int>> selections;
if (mBrushShape == 0)
{
if (isInCellSelection(texCoords.first, texCoords.second)) selections.emplace_back(texCoords);
}
if (mBrushShape == 1)
{
for (int i = -r; i <= r; i++)
{
for (int j = -r; j <= r; j++)
{
int x = i + texCoords.first;
int y = j + texCoords.second;
if (isInCellSelection(x, y))
{
selections.emplace_back(x, y);
}
}
}
}
if (mBrushShape == 2)
{
for (int i = -r; i <= r; i++)
{
for (int j = -r; j <= r; j++)
{
osg::Vec2f coords(i,j);
if (std::round(coords.length()) < r)
{
int x = i + texCoords.first;
int y = j + texCoords.second;
if (isInCellSelection(x, y))
{
selections.emplace_back(x, y);
} }
pushEditToCommand(mNew, document, landTable, iteratedCellId);
} }
} }
} }
@ -388,9 +549,22 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe
if (mBrushShape == 3) if (mBrushShape == 3)
{ {
// Not implemented if(!mCustomBrushShape.empty())
{
for(auto const& value: mCustomBrushShape)
{
int x = texCoords.first + value.first;
int y = texCoords.second + value.second;
if (isInCellSelection(x, y))
{
selections.emplace_back(x, y);
}
}
}
} }
if(selectMode == 0) mTerrainTextureSelection->onlySelect(selections);
if(selectMode == 1) mTerrainTextureSelection->toggleSelect(selections, dragOperation);
} }
void CSVRender::TerrainTextureMode::pushEditToCommand(CSMWorld::LandTexturesColumn::DataType& newLandGrid, CSMDoc::Document& document, void CSVRender::TerrainTextureMode::pushEditToCommand(CSMWorld::LandTexturesColumn::DataType& newLandGrid, CSMDoc::Document& document,
@ -405,8 +579,8 @@ void CSVRender::TerrainTextureMode::pushEditToCommand(CSMWorld::LandTexturesColu
QModelIndex index(landTable.getModelIndex (cellId, landTable.findColumnIndex (CSMWorld::Columns::ColumnId_LandTexturesIndex))); QModelIndex index(landTable.getModelIndex (cellId, landTable.findColumnIndex (CSMWorld::Columns::ColumnId_LandTexturesIndex)));
QUndoStack& undoStack = document.getUndoStack(); QUndoStack& undoStack = document.getUndoStack();
undoStack.push (new CSMWorld::TouchLandCommand(landTable, ltexTable, cellId));
undoStack.push (new CSMWorld::ModifyCommand(landTable, index, changedLand)); undoStack.push (new CSMWorld::ModifyCommand(landTable, index, changedLand));
undoStack.push (new CSMWorld::TouchLandCommand(landTable, ltexTable, cellId));
} }
void CSVRender::TerrainTextureMode::createTexture(std::string textureFileName) void CSVRender::TerrainTextureMode::createTexture(std::string textureFileName)
@ -422,13 +596,15 @@ void CSVRender::TerrainTextureMode::createTexture(std::string textureFileName)
int counter=0; int counter=0;
bool freeIndexFound = false; bool freeIndexFound = false;
do { do
{
const size_t maxCounter = std::numeric_limits<uint16_t>::max() - 1; const size_t maxCounter = std::numeric_limits<uint16_t>::max() - 1;
try try
{ {
newId = CSMWorld::LandTexture::createUniqueRecordId(0, counter); newId = CSMWorld::LandTexture::createUniqueRecordId(0, counter);
if (ltexTable.getRecord(newId).isDeleted() == 0) counter = (counter + 1) % maxCounter; if (ltexTable.getRecord(newId).isDeleted() == 0) counter = (counter + 1) % maxCounter;
} catch (const std::exception& e) }
catch (const std::exception& e)
{ {
newId = CSMWorld::LandTexture::createUniqueRecordId(0, counter); newId = CSMWorld::LandTexture::createUniqueRecordId(0, counter);
freeIndexFound = true; freeIndexFound = true;
@ -527,7 +703,8 @@ bool CSVRender::TerrainTextureMode::allowLandTextureEditing(std::string cellId)
return true; return true;
} }
void CSVRender::TerrainTextureMode::dragMoveEvent (QDragMoveEvent *event) { void CSVRender::TerrainTextureMode::dragMoveEvent (QDragMoveEvent *event)
{
} }
void CSVRender::TerrainTextureMode::setBrushSize(int brushSize) void CSVRender::TerrainTextureMode::setBrushSize(int brushSize)
@ -538,6 +715,28 @@ void CSVRender::TerrainTextureMode::setBrushSize(int brushSize)
void CSVRender::TerrainTextureMode::setBrushShape(int brushShape) void CSVRender::TerrainTextureMode::setBrushShape(int brushShape)
{ {
mBrushShape = brushShape; mBrushShape = brushShape;
//Set custom brush shape
if (mBrushShape == 3 && !mTerrainTextureSelection->getTerrainSelection().empty())
{
auto terrainSelection = mTerrainTextureSelection->getTerrainSelection();
int selectionCenterX = 0;
int selectionCenterY = 0;
int selectionAmount = 0;
for(auto const& value: terrainSelection)
{
selectionCenterX += value.first;
selectionCenterY += value.second;
++selectionAmount;
}
selectionCenterX /= selectionAmount;
selectionCenterY /= selectionAmount;
mCustomBrushShape.clear();
for (auto const& value: terrainSelection)
mCustomBrushShape.emplace_back(value.first - selectionCenterX, value.second - selectionCenterY);
}
} }
void CSVRender::TerrainTextureMode::setBrushTexture(std::string brushTexture) void CSVRender::TerrainTextureMode::setBrushTexture(std::string brushTexture)

@ -4,6 +4,7 @@
#include "editmode.hpp" #include "editmode.hpp"
#include <string> #include <string>
#include <memory>
#include <QWidget> #include <QWidget>
#include <QEvent> #include <QEvent>
@ -18,6 +19,13 @@
#include "../../model/world/landtexture.hpp" #include "../../model/world/landtexture.hpp"
#endif #endif
#include "terrainselection.hpp"
namespace osg
{
class Group;
}
namespace CSVWidget namespace CSVWidget
{ {
class SceneToolTextureBrush; class SceneToolTextureBrush;
@ -25,15 +33,23 @@ namespace CSVWidget
namespace CSVRender namespace CSVRender
{ {
class TerrainTextureMode : public EditMode class TerrainTextureMode : public EditMode
{ {
Q_OBJECT Q_OBJECT
public: public:
enum InteractionType
{
InteractionType_PrimaryEdit,
InteractionType_PrimarySelect,
InteractionType_SecondaryEdit,
InteractionType_SecondarySelect,
InteractionType_None
};
/// \brief Editmode for terrain texture grid /// \brief Editmode for terrain texture grid
TerrainTextureMode(WorldspaceWidget*, QWidget* parent = nullptr); TerrainTextureMode(WorldspaceWidget*, osg::Group* parentNode, QWidget* parent = nullptr);
void primaryOpenPressed (const WorldspaceHitResult& hit); void primaryOpenPressed (const WorldspaceHitResult& hit);
@ -68,6 +84,12 @@ namespace CSVRender
/// \brief Handle brush mechanics, maths regarding worldspace hit etc. /// \brief Handle brush mechanics, maths regarding worldspace hit etc.
void editTerrainTextureGrid (const WorldspaceHitResult& hit); void editTerrainTextureGrid (const WorldspaceHitResult& hit);
/// \brief Check if global selection coordinate belongs to cell in view
bool isInCellSelection(int globalSelectionX, int globalSelectionY);
/// \brief Handle brush mechanics for texture selection
void selectTerrainTextures (const std::pair<int, int>& texCoords, unsigned char selectMode, bool dragOperation);
/// \brief Push texture edits to command macro /// \brief Push texture edits to command macro
void pushEditToCommand (CSMWorld::LandTexturesColumn::DataType& newLandGrid, CSMDoc::Document& document, void pushEditToCommand (CSMWorld::LandTexturesColumn::DataType& newLandGrid, CSMDoc::Document& document,
CSMWorld::IdTable& landTable, std::string cellId); CSMWorld::IdTable& landTable, std::string cellId);
@ -83,7 +105,12 @@ namespace CSVRender
std::string mBrushTexture; std::string mBrushTexture;
int mBrushSize; int mBrushSize;
int mBrushShape; int mBrushShape;
std::vector<std::pair<int, int>> mCustomBrushShape;
CSVWidget::SceneToolTextureBrush *mTextureBrushScenetool; CSVWidget::SceneToolTextureBrush *mTextureBrushScenetool;
int mDragMode;
osg::Group* mParentNode;
bool mIsEditing;
std::unique_ptr<TerrainSelection> mTerrainTextureSelection;
const int cellSize {ESM::Land::REAL_SIZE}; const int cellSize {ESM::Land::REAL_SIZE};
const int landSize {ESM::Land::LAND_SIZE}; const int landSize {ESM::Land::LAND_SIZE};

@ -455,18 +455,15 @@ namespace MWClass
// otherwise wait until death animation // otherwise wait until death animation
if(stats.isDeathAnimationFinished()) if(stats.isDeathAnimationFinished())
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr)); return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr));
// death animation is not finished, do nothing
return std::shared_ptr<MWWorld::Action> (new MWWorld::FailedAction(""));
} }
else if (!stats.getAiSequence().isInCombat() && !stats.getKnockedDown())
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionTalk(ptr));
if(stats.getAiSequence().isInCombat()) // Tribunal and some mod companions oddly enough must use open action as fallback
return std::shared_ptr<MWWorld::Action>(new MWWorld::FailedAction("")); if (!getScript(ptr).empty() && ptr.getRefData().getLocals().getIntVar(getScript(ptr), "companion"))
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr));
if(stats.getKnockedDown())
return std::shared_ptr<MWWorld::Action>(new MWWorld::FailedAction("")); return std::shared_ptr<MWWorld::Action>(new MWWorld::FailedAction(""));
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionTalk(ptr));
} }
MWWorld::ContainerStore& Creature::getContainerStore (const MWWorld::Ptr& ptr) const MWWorld::ContainerStore& Creature::getContainerStore (const MWWorld::Ptr& ptr) const

@ -886,24 +886,24 @@ namespace MWClass
// otherwise wait until death animation // otherwise wait until death animation
if(stats.isDeathAnimationFinished()) if(stats.isDeathAnimationFinished())
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr)); return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr));
// death animation is not finished, do nothing
return std::shared_ptr<MWWorld::Action> (new MWWorld::FailedAction(""));
} }
else if (!stats.getAiSequence().isInCombat())
if(stats.getAiSequence().isInCombat()) {
return std::shared_ptr<MWWorld::Action>(new MWWorld::FailedAction(""));
if(getCreatureStats(actor).getStance(MWMechanics::CreatureStats::Stance_Sneak) || stats.getKnockedDown()) if(getCreatureStats(actor).getStance(MWMechanics::CreatureStats::Stance_Sneak) || stats.getKnockedDown())
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr)); // stealing return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr)); // stealing
// Can't talk to werewolfs // Can't talk to werewolves
if(getNpcStats(ptr).isWerewolf()) if (!getNpcStats(ptr).isWerewolf())
return std::shared_ptr<MWWorld::Action> (new MWWorld::FailedAction(""));
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionTalk(ptr)); return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionTalk(ptr));
} }
// Tribunal and some mod companions oddly enough must use open action as fallback
if (!getScript(ptr).empty() && ptr.getRefData().getLocals().getIntVar(getScript(ptr), "companion"))
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr));
return std::shared_ptr<MWWorld::Action> (new MWWorld::FailedAction(""));
}
MWWorld::ContainerStore& Npc::getContainerStore (const MWWorld::Ptr& ptr) MWWorld::ContainerStore& Npc::getContainerStore (const MWWorld::Ptr& ptr)
const const
{ {

@ -208,18 +208,13 @@ namespace
void init(Nif::Extra& value) void init(Nif::Extra& value)
{ {
value.extra = Nif::ExtraPtr(nullptr); value.next = Nif::ExtraPtr(nullptr);
}
void init(Nif::Controlled& value)
{
init(static_cast<Nif::Extra&>(value));
value.controller = Nif::ControllerPtr(nullptr);
} }
void init(Nif::Named& value) void init(Nif::Named& value)
{ {
init(static_cast<Nif::Controlled&>(value)); value.extra = Nif::ExtraPtr(nullptr);
value.controller = Nif::ControllerPtr(nullptr);
} }
void init(Nif::Node& value) void init(Nif::Node& value)
@ -254,7 +249,7 @@ namespace
value.phase = 0; value.phase = 0;
value.timeStart = 0; value.timeStart = 0;
value.timeStop = 0; value.timeStop = 0;
value.target = Nif::ControlledPtr(nullptr); value.target = Nif::NamedPtr(nullptr);
} }
void copy(const btTransform& src, Nif::Transformation& dst) void copy(const btTransform& src, Nif::Transformation& dst)
@ -884,7 +879,7 @@ namespace
TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_not_first_extra_data_string_starting_with_nc_should_return_shape_with_null_collision_shape) TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_not_first_extra_data_string_starting_with_nc_should_return_shape_with_null_collision_shape)
{ {
mNiStringExtraData.extra = Nif::ExtraPtr(&mNiStringExtraData2); mNiStringExtraData.next = Nif::ExtraPtr(&mNiStringExtraData2);
mNiStringExtraData2.string = "NC___"; mNiStringExtraData2.string = "NC___";
mNiStringExtraData2.recType = Nif::RC_NiStringExtraData; mNiStringExtraData2.recType = Nif::RC_NiStringExtraData;
mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData); mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData);

@ -10,17 +10,19 @@
namespace Nif namespace Nif
{ {
/** A record that can have extra data. The extra data objects // An extra data record. All the extra data connected to an object form a linked list.
themselves descend from the Extra class, and all the extra data
connected to an object form a linked list
*/
class Extra : public Record class Extra : public Record
{ {
public: public:
ExtraPtr extra; ExtraPtr next; // Next extra data record in the list
void read(NIFStream *nif)
{
next.read(nif);
nif->getUInt(); // Size of the record
}
void read(NIFStream *nif) { extra.read(nif); } void post(NIFFile *nif) { next.post(nif); }
void post(NIFFile *nif) { extra.post(nif); }
}; };
class Controller : public Record class Controller : public Record
@ -30,43 +32,33 @@ public:
int flags; int flags;
float frequency, phase; float frequency, phase;
float timeStart, timeStop; float timeStart, timeStop;
ControlledPtr target; NamedPtr target;
void read(NIFStream *nif); void read(NIFStream *nif);
void post(NIFFile *nif); void post(NIFFile *nif);
}; };
/// Anything that has a controller /// Has name, extra-data and controller
class Controlled : public Extra class Named : public Record
{ {
public: public:
std::string name;
ExtraPtr extra;
ControllerPtr controller; ControllerPtr controller;
void read(NIFStream *nif) void read(NIFStream *nif)
{ {
Extra::read(nif); name = nif->getString();
extra.read(nif);
controller.read(nif); controller.read(nif);
} }
void post(NIFFile *nif) void post(NIFFile *nif)
{ {
Extra::post(nif); extra.post(nif);
controller.post(nif); controller.post(nif);
} }
}; };
/// Has name, extra-data and controller
class Named : public Controlled
{
public:
std::string name;
void read(NIFStream *nif)
{
name = nif->getString();
Controlled::read(nif);
}
};
typedef Named NiSequenceStreamHelper; typedef Named NiSequenceStreamHelper;
} // Namespace } // Namespace

@ -31,28 +31,40 @@ namespace Nif
data.post(nif); data.post(nif);
} }
void NiParticleModifier::read(NIFStream *nif)
{
next.read(nif);
controller.read(nif);
}
void NiParticleModifier::post(NIFFile *nif)
{
next.post(nif);
controller.post(nif);
}
void NiParticleGrowFade::read(NIFStream *nif) void NiParticleGrowFade::read(NIFStream *nif)
{ {
Controlled::read(nif); NiParticleModifier::read(nif);
growTime = nif->getFloat(); growTime = nif->getFloat();
fadeTime = nif->getFloat(); fadeTime = nif->getFloat();
} }
void NiParticleColorModifier::read(NIFStream *nif) void NiParticleColorModifier::read(NIFStream *nif)
{ {
Controlled::read(nif); NiParticleModifier::read(nif);
data.read(nif); data.read(nif);
} }
void NiParticleColorModifier::post(NIFFile *nif) void NiParticleColorModifier::post(NIFFile *nif)
{ {
Controlled::post(nif); NiParticleModifier::post(nif);
data.post(nif); data.post(nif);
} }
void NiGravity::read(NIFStream *nif) void NiGravity::read(NIFStream *nif)
{ {
Controlled::read(nif); NiParticleModifier::read(nif);
mDecay = nif->getFloat(); mDecay = nif->getFloat();
mForce = nif->getFloat(); mForce = nif->getFloat();
@ -61,11 +73,17 @@ namespace Nif
mDirection = nif->getVector3(); mDirection = nif->getVector3();
} }
void NiPlanarCollider::read(NIFStream *nif) void NiParticleCollider::read(NIFStream *nif)
{ {
Controlled::read(nif); NiParticleModifier::read(nif);
mBounceFactor = nif->getFloat(); mBounceFactor = nif->getFloat();
}
void NiPlanarCollider::read(NIFStream *nif)
{
NiParticleCollider::read(nif);
/*unknown*/nif->getFloat(); /*unknown*/nif->getFloat();
for (int i=0;i<10;++i) for (int i=0;i<10;++i)
@ -77,7 +95,7 @@ namespace Nif
void NiParticleRotation::read(NIFStream *nif) void NiParticleRotation::read(NIFStream *nif)
{ {
Controlled::read(nif); NiParticleModifier::read(nif);
/* /*
byte (0 or 1) byte (0 or 1)
@ -89,9 +107,8 @@ namespace Nif
void NiSphericalCollider::read(NIFStream* nif) void NiSphericalCollider::read(NIFStream* nif)
{ {
Controlled::read(nif); NiParticleCollider::read(nif);
mBounceFactor = nif->getFloat();
mRadius = nif->getFloat(); mRadius = nif->getFloat();
mCenter = nif->getVector3(); mCenter = nif->getVector3();
} }

@ -66,7 +66,16 @@ public:
void post(NIFFile *nif); void post(NIFFile *nif);
}; };
class NiParticleGrowFade : public Controlled struct NiParticleModifier : public Record
{
NiParticleModifierPtr next;
ControllerPtr controller;
void read(NIFStream *nif);
void post(NIFFile *nif);
};
class NiParticleGrowFade : public NiParticleModifier
{ {
public: public:
float growTime; float growTime;
@ -75,7 +84,7 @@ public:
void read(NIFStream *nif); void read(NIFStream *nif);
}; };
class NiParticleColorModifier : public Controlled class NiParticleColorModifier : public NiParticleModifier
{ {
public: public:
NiColorDataPtr data; NiColorDataPtr data;
@ -84,7 +93,7 @@ public:
void post(NIFFile *nif); void post(NIFFile *nif);
}; };
class NiGravity : public Controlled class NiGravity : public NiParticleModifier
{ {
public: public:
float mForce; float mForce;
@ -99,29 +108,32 @@ public:
void read(NIFStream *nif); void read(NIFStream *nif);
}; };
struct NiParticleCollider : public NiParticleModifier
{
float mBounceFactor;
void read(NIFStream *nif);
};
// NiPinaColada // NiPinaColada
class NiPlanarCollider : public Controlled class NiPlanarCollider : public NiParticleCollider
{ {
public: public:
void read(NIFStream *nif); void read(NIFStream *nif);
float mBounceFactor;
osg::Vec3f mPlaneNormal; osg::Vec3f mPlaneNormal;
float mPlaneDistance; float mPlaneDistance;
}; };
class NiSphericalCollider : public Controlled class NiSphericalCollider : public NiParticleCollider
{ {
public: public:
float mBounceFactor;
float mRadius; float mRadius;
osg::Vec3f mCenter; osg::Vec3f mCenter;
void read(NIFStream *nif); void read(NIFStream *nif);
}; };
class NiParticleRotation : public Controlled class NiParticleRotation : public NiParticleModifier
{ {
public: public:
void read(NIFStream *nif); void read(NIFStream *nif);

@ -72,8 +72,8 @@ public:
int activeCount; int activeCount;
std::vector<Particle> particles; std::vector<Particle> particles;
ExtraPtr affectors; NiParticleModifierPtr affectors;
ExtraPtr colliders; NiParticleModifierPtr colliders;
void read(NIFStream *nif); void read(NIFStream *nif);
void post(NIFFile *nif); void post(NIFFile *nif);

@ -6,8 +6,6 @@ namespace Nif
void NiStringExtraData::read(NIFStream *nif) void NiStringExtraData::read(NIFStream *nif)
{ {
Extra::read(nif); Extra::read(nif);
nif->getInt(); // size of string + 4. Really useful...
string = nif->getString(); string = nif->getString();
} }
@ -15,8 +13,6 @@ void NiTextKeyExtraData::read(NIFStream *nif)
{ {
Extra::read(nif); Extra::read(nif);
nif->getInt(); // 0
int keynum = nif->getInt(); int keynum = nif->getInt();
list.resize(keynum); list.resize(keynum);
for(int i=0; i<keynum; i++) for(int i=0; i<keynum; i++)
@ -30,12 +26,7 @@ void NiVertWeightsExtraData::read(NIFStream *nif)
{ {
Extra::read(nif); Extra::read(nif);
// We should have s*4+2 == i, for some reason. Might simply be the nif->skip(nif->getUShort() * sizeof(float)); // vertex weights I guess
// size of the rest of the record, unhelpful as that may be.
/*int i =*/ nif->getInt();
int s = nif->getUShort();
nif->skip(s * sizeof(float)); // vertex weights I guess
} }

@ -127,7 +127,7 @@ class NiUVData;
class NiPosData; class NiPosData;
class NiVisData; class NiVisData;
class Controller; class Controller;
class Controlled; class Named;
class NiSkinData; class NiSkinData;
class NiFloatData; class NiFloatData;
struct NiMorphData; struct NiMorphData;
@ -141,6 +141,7 @@ class NiSourceTexture;
class NiRotatingParticlesData; class NiRotatingParticlesData;
class NiAutoNormalParticlesData; class NiAutoNormalParticlesData;
class NiPalette; class NiPalette;
struct NiParticleModifier;
typedef RecordPtrT<Node> NodePtr; typedef RecordPtrT<Node> NodePtr;
typedef RecordPtrT<Extra> ExtraPtr; typedef RecordPtrT<Extra> ExtraPtr;
@ -148,7 +149,7 @@ typedef RecordPtrT<NiUVData> NiUVDataPtr;
typedef RecordPtrT<NiPosData> NiPosDataPtr; typedef RecordPtrT<NiPosData> NiPosDataPtr;
typedef RecordPtrT<NiVisData> NiVisDataPtr; typedef RecordPtrT<NiVisData> NiVisDataPtr;
typedef RecordPtrT<Controller> ControllerPtr; typedef RecordPtrT<Controller> ControllerPtr;
typedef RecordPtrT<Controlled> ControlledPtr; typedef RecordPtrT<Named> NamedPtr;
typedef RecordPtrT<NiSkinData> NiSkinDataPtr; typedef RecordPtrT<NiSkinData> NiSkinDataPtr;
typedef RecordPtrT<NiMorphData> NiMorphDataPtr; typedef RecordPtrT<NiMorphData> NiMorphDataPtr;
typedef RecordPtrT<NiPixelData> NiPixelDataPtr; typedef RecordPtrT<NiPixelData> NiPixelDataPtr;
@ -162,6 +163,7 @@ typedef RecordPtrT<NiSourceTexture> NiSourceTexturePtr;
typedef RecordPtrT<NiRotatingParticlesData> NiRotatingParticlesDataPtr; typedef RecordPtrT<NiRotatingParticlesData> NiRotatingParticlesDataPtr;
typedef RecordPtrT<NiAutoNormalParticlesData> NiAutoNormalParticlesDataPtr; typedef RecordPtrT<NiAutoNormalParticlesData> NiAutoNormalParticlesDataPtr;
typedef RecordPtrT<NiPalette> NiPalettePtr; typedef RecordPtrT<NiPalette> NiPalettePtr;
typedef RecordPtrT<NiParticleModifier> NiParticleModifierPtr;
typedef RecordListT<Node> NodeList; typedef RecordListT<Node> NodeList;
typedef RecordListT<Property> PropertyList; typedef RecordListT<Property> PropertyList;

@ -260,18 +260,13 @@ void BulletNifLoader::handleNode(const std::string& fileName, const Nif::Node *n
Log(Debug::Info) << "RootCollisionNode is not attached to the root node in " << fileName << ". Treating it as a common NiTriShape."; Log(Debug::Info) << "RootCollisionNode is not attached to the root node in " << fileName << ". Treating it as a common NiTriShape.";
// Check for extra data // Check for extra data
Nif::Extra const *e = node; for (Nif::ExtraPtr e = node->extra; !e.empty(); e = e->next)
while (!e->extra.empty())
{ {
// Get the next extra data in the list
e = e->extra.getPtr();
assert(e != nullptr);
if (e->recType == Nif::RC_NiStringExtraData) if (e->recType == Nif::RC_NiStringExtraData)
{ {
// String markers may contain important information // String markers may contain important information
// affecting the entire subtree of this node // affecting the entire subtree of this node
Nif::NiStringExtraData *sd = (Nif::NiStringExtraData*)e; Nif::NiStringExtraData *sd = (Nif::NiStringExtraData*)e.getPtr();
if (Misc::StringUtils::ciCompareLen(sd->string, "NC", 2) == 0) if (Misc::StringUtils::ciCompareLen(sd->string, "NC", 2) == 0)
{ {

@ -222,9 +222,9 @@ namespace NifOsg
extractTextKeys(static_cast<const Nif::NiTextKeyExtraData*>(extra.getPtr()), target.mTextKeys); extractTextKeys(static_cast<const Nif::NiTextKeyExtraData*>(extra.getPtr()), target.mTextKeys);
extra = extra->extra; extra = extra->next;
Nif::ControllerPtr ctrl = seq->controller; Nif::ControllerPtr ctrl = seq->controller;
for(;!extra.empty() && !ctrl.empty();(extra=extra->extra),(ctrl=ctrl->next)) for(;!extra.empty() && !ctrl.empty();(extra=extra->next),(ctrl=ctrl->next))
{ {
if(extra->recType != Nif::RC_NiStringExtraData || ctrl->recType != Nif::RC_NiKeyframeController) if(extra->recType != Nif::RC_NiStringExtraData || ctrl->recType != Nif::RC_NiKeyframeController)
{ {
@ -524,7 +524,7 @@ namespace NifOsg
node->getOrCreateUserDataContainer()->addUserObject( node->getOrCreateUserDataContainer()->addUserObject(
new NodeUserData(nifNode->recIndex, nifNode->trafo.scale, nifNode->trafo.rotation)); new NodeUserData(nifNode->recIndex, nifNode->trafo.scale, nifNode->trafo.rotation));
for (Nif::ExtraPtr e = nifNode->extra; !e.empty(); e = e->extra) for (Nif::ExtraPtr e = nifNode->extra; !e.empty(); e = e->next)
{ {
if(e->recType == Nif::RC_NiTextKeyExtraData && textKeys) if(e->recType == Nif::RC_NiTextKeyExtraData && textKeys)
{ {
@ -802,13 +802,13 @@ namespace NifOsg
} }
} }
void handleParticlePrograms(Nif::ExtraPtr affectors, Nif::ExtraPtr colliders, osg::Group *attachTo, osgParticle::ParticleSystem* partsys, osgParticle::ParticleProcessor::ReferenceFrame rf) void handleParticlePrograms(Nif::NiParticleModifierPtr affectors, Nif::NiParticleModifierPtr colliders, osg::Group *attachTo, osgParticle::ParticleSystem* partsys, osgParticle::ParticleProcessor::ReferenceFrame rf)
{ {
osgParticle::ModularProgram* program = new osgParticle::ModularProgram; osgParticle::ModularProgram* program = new osgParticle::ModularProgram;
attachTo->addChild(program); attachTo->addChild(program);
program->setParticleSystem(partsys); program->setParticleSystem(partsys);
program->setReferenceFrame(rf); program->setReferenceFrame(rf);
for (; !affectors.empty(); affectors = affectors->extra) for (; !affectors.empty(); affectors = affectors->next)
{ {
if (affectors->recType == Nif::RC_NiParticleGrowFade) if (affectors->recType == Nif::RC_NiParticleGrowFade)
{ {
@ -833,7 +833,7 @@ namespace NifOsg
else else
Log(Debug::Info) << "Unhandled particle modifier " << affectors->recName << " in " << mFilename; Log(Debug::Info) << "Unhandled particle modifier " << affectors->recName << " in " << mFilename;
} }
for (; !colliders.empty(); colliders = colliders->extra) for (; !colliders.empty(); colliders = colliders->next)
{ {
if (colliders->recType == Nif::RC_NiPlanarCollider) if (colliders->recType == Nif::RC_NiPlanarCollider)
{ {

Loading…
Cancel
Save