1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-01-22 17:53:53 +00:00
openmw/apps/opencs/view/render/object.cpp
florent.teppe 65cdd489fb create a specific esm reader function for RefID to avoid allocation for string and then again for RefId
Fixed some types

removed useless header

applied clang format

fixed compile tests

fixed clang tidy, and closer to logic before this MR

Removed hardcoded refids

unless there is a returned value we don't use static RefIds
can use == between RefId and hardcoded string

Fix clang format

Fixed a few instances where std::string was used, when only const std::string& was needed

removed unused variable
2022-12-27 19:15:57 +01:00

810 lines
23 KiB
C++

#include "object.hpp"
#include <algorithm>
#include <cmath>
#include <exception>
#include <stddef.h>
#include <stdexcept>
#include <string>
#include <utility>
#include <apps/opencs/model/prefs/category.hpp>
#include <apps/opencs/model/prefs/setting.hpp>
#include <apps/opencs/model/world/columns.hpp>
#include <apps/opencs/model/world/record.hpp>
#include <apps/opencs/model/world/ref.hpp>
#include <apps/opencs/model/world/refcollection.hpp>
#include <apps/opencs/model/world/refidcollection.hpp>
#include <apps/opencs/model/world/universalid.hpp>
#include <apps/opencs/view/render/tagbase.hpp>
#include <osg/Array>
#include <osg/BoundingSphere>
#include <osg/GL>
#include <osg/Geometry>
#include <osg/Group>
#include <osg/Math>
#include <osg/Node>
#include <osg/PositionAttitudeTransform>
#include <osg/PrimitiveSet>
#include <osg/Quat>
#include <osg/Shape>
#include <osg/ShapeDrawable>
#include <osg/StateAttribute>
#include <osg/StateSet>
#include <osg/Vec3>
#include <osg/Vec4f>
#include <osgFX/Scribe>
#include "../../model/prefs/state.hpp"
#include "../../model/world/cellcoordinates.hpp"
#include "../../model/world/commandmacro.hpp"
#include "../../model/world/commands.hpp"
#include "../../model/world/data.hpp"
#include <components/debug/debuglog.hpp>
#include <components/resource/resourcesystem.hpp>
#include <components/resource/scenemanager.hpp>
#include <components/sceneutil/lightmanager.hpp>
#include <components/sceneutil/lightutil.hpp>
#include "actor.hpp"
#include "mask.hpp"
namespace CSVRender
{
struct WorldspaceHitResult;
}
namespace ESM
{
struct Light;
}
const float CSVRender::Object::MarkerShaftWidth = 30;
const float CSVRender::Object::MarkerShaftBaseLength = 70;
const float CSVRender::Object::MarkerHeadWidth = 50;
const float CSVRender::Object::MarkerHeadLength = 50;
namespace
{
osg::ref_ptr<osg::Group> createErrorCube()
{
osg::ref_ptr<osg::Box> shape(new osg::Box(osg::Vec3f(0, 0, 0), 50.f));
osg::ref_ptr<osg::ShapeDrawable> shapedrawable(new osg::ShapeDrawable);
shapedrawable->setShape(shape);
osg::ref_ptr<osg::Group> group(new osg::Group);
group->addChild(shapedrawable);
return group;
}
}
CSVRender::ObjectTag::ObjectTag(Object* object)
: TagBase(Mask_Reference)
, mObject(object)
{
}
QString CSVRender::ObjectTag::getToolTip(bool /*hideBasics*/, const WorldspaceHitResult& /*hit*/) const
{
return QString::fromUtf8(mObject->getReferenceableId().c_str());
}
CSVRender::ObjectMarkerTag::ObjectMarkerTag(Object* object, int axis)
: ObjectTag(object)
, mAxis(axis)
{
}
void CSVRender::Object::clear() {}
void CSVRender::Object::update()
{
clear();
const CSMWorld::RefIdCollection& referenceables = mData.getReferenceables();
const int TypeIndex = referenceables.findColumnIndex(CSMWorld::Columns::ColumnId_RecordType);
const int ModelIndex = referenceables.findColumnIndex(CSMWorld::Columns::ColumnId_Model);
int index = referenceables.searchId(mReferenceableId);
const ESM::Light* light = nullptr;
mBaseNode->removeChildren(0, mBaseNode->getNumChildren());
if (index == -1)
{
mBaseNode->addChild(createErrorCube());
return;
}
/// \todo check for Deleted state (error 1)
int recordType = referenceables.getData(index, TypeIndex).toInt();
std::string model = referenceables.getData(index, ModelIndex).toString().toUtf8().constData();
if (recordType == CSMWorld::UniversalId::Type_Light)
{
light = &dynamic_cast<const CSMWorld::Record<ESM::Light>&>(referenceables.getRecord(index)).get();
if (model.empty())
model = "marker_light.nif";
}
if (recordType == CSMWorld::UniversalId::Type_CreatureLevelledList)
{
if (model.empty())
model = "marker_creature.nif";
}
try
{
if (recordType == CSMWorld::UniversalId::Type_Npc || recordType == CSMWorld::UniversalId::Type_Creature)
{
if (!mActor)
mActor = std::make_unique<Actor>(mReferenceableId, mData);
mActor->update();
mBaseNode->addChild(mActor->getBaseNode());
}
else if (!model.empty())
{
std::string path = "meshes\\" + model;
mResourceSystem->getSceneManager()->getInstance(path, mBaseNode);
}
else
{
throw std::runtime_error(mReferenceableId.getRefIdString() + " has no model");
}
}
catch (std::exception& e)
{
// TODO: use error marker mesh
Log(Debug::Error) << e.what();
}
if (light)
{
bool isExterior = false; // FIXME
SceneUtil::addLight(mBaseNode, light, Mask_Lighting, isExterior);
}
}
void CSVRender::Object::adjustTransform()
{
if (mReferenceId.empty())
return;
ESM::Position position = getPosition();
// position
mRootNode->setPosition(
mForceBaseToZero ? osg::Vec3() : osg::Vec3f(position.pos[0], position.pos[1], position.pos[2]));
// orientation
osg::Quat xr(-position.rot[0], osg::Vec3f(1, 0, 0));
osg::Quat yr(-position.rot[1], osg::Vec3f(0, 1, 0));
osg::Quat zr(-position.rot[2], osg::Vec3f(0, 0, 1));
mBaseNode->setAttitude(zr * yr * xr);
float scale = getScale();
mBaseNode->setScale(osg::Vec3(scale, scale, scale));
}
const CSMWorld::CellRef& CSVRender::Object::getReference() const
{
if (mReferenceId.empty())
throw std::logic_error("object does not represent a reference");
return mData.getReferences().getRecord(mReferenceId).get();
}
void CSVRender::Object::updateMarker()
{
for (int i = 0; i < 3; ++i)
{
if (mMarker[i])
{
mRootNode->removeChild(mMarker[i]);
mMarker[i] = osg::ref_ptr<osg::Node>();
}
if (mSelected)
{
if (mSubMode == 0)
{
mMarker[i] = makeMoveOrScaleMarker(i);
mMarker[i]->setUserData(new ObjectMarkerTag(this, i));
mRootNode->addChild(mMarker[i]);
}
else if (mSubMode == 1)
{
mMarker[i] = makeRotateMarker(i);
mMarker[i]->setUserData(new ObjectMarkerTag(this, i));
mRootNode->addChild(mMarker[i]);
}
else if (mSubMode == 2)
{
mMarker[i] = makeMoveOrScaleMarker(i);
mMarker[i]->setUserData(new ObjectMarkerTag(this, i));
mRootNode->addChild(mMarker[i]);
}
}
}
}
osg::ref_ptr<osg::Node> CSVRender::Object::makeMoveOrScaleMarker(int axis)
{
osg::ref_ptr<osg::Geometry> geometry(new osg::Geometry);
float shaftLength = MarkerShaftBaseLength + mBaseNode->getBound().radius();
// shaft
osg::Vec3Array* vertices = new osg::Vec3Array;
for (int i = 0; i < 2; ++i)
{
float length = i ? shaftLength : MarkerShaftWidth;
vertices->push_back(getMarkerPosition(-MarkerShaftWidth / 2, -MarkerShaftWidth / 2, length, axis));
vertices->push_back(getMarkerPosition(-MarkerShaftWidth / 2, MarkerShaftWidth / 2, length, axis));
vertices->push_back(getMarkerPosition(MarkerShaftWidth / 2, MarkerShaftWidth / 2, length, axis));
vertices->push_back(getMarkerPosition(MarkerShaftWidth / 2, -MarkerShaftWidth / 2, length, axis));
}
// head backside
vertices->push_back(getMarkerPosition(-MarkerHeadWidth / 2, -MarkerHeadWidth / 2, shaftLength, axis));
vertices->push_back(getMarkerPosition(-MarkerHeadWidth / 2, MarkerHeadWidth / 2, shaftLength, axis));
vertices->push_back(getMarkerPosition(MarkerHeadWidth / 2, MarkerHeadWidth / 2, shaftLength, axis));
vertices->push_back(getMarkerPosition(MarkerHeadWidth / 2, -MarkerHeadWidth / 2, shaftLength, axis));
// head
vertices->push_back(getMarkerPosition(0, 0, shaftLength + MarkerHeadLength, axis));
geometry->setVertexArray(vertices);
osg::DrawElementsUShort* primitives = new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLES, 0);
// shaft
for (int i = 0; i < 4; ++i)
{
int i2 = i == 3 ? 0 : i + 1;
primitives->push_back(i);
primitives->push_back(4 + i);
primitives->push_back(i2);
primitives->push_back(4 + i);
primitives->push_back(4 + i2);
primitives->push_back(i2);
}
// cap
primitives->push_back(0);
primitives->push_back(1);
primitives->push_back(2);
primitives->push_back(2);
primitives->push_back(3);
primitives->push_back(0);
// head, backside
primitives->push_back(0 + 8);
primitives->push_back(1 + 8);
primitives->push_back(2 + 8);
primitives->push_back(2 + 8);
primitives->push_back(3 + 8);
primitives->push_back(0 + 8);
for (int i = 0; i < 4; ++i)
{
primitives->push_back(12);
primitives->push_back(8 + (i == 3 ? 0 : i + 1));
primitives->push_back(8 + i);
}
geometry->addPrimitiveSet(primitives);
osg::Vec4Array* colours = new osg::Vec4Array;
for (int i = 0; i < 8; ++i)
colours->push_back(
osg::Vec4f(axis == 0 ? 1.0f : 0.2f, axis == 1 ? 1.0f : 0.2f, axis == 2 ? 1.0f : 0.2f, mMarkerTransparency));
for (int i = 8; i < 8 + 4 + 1; ++i)
colours->push_back(
osg::Vec4f(axis == 0 ? 1.0f : 0.0f, axis == 1 ? 1.0f : 0.0f, axis == 2 ? 1.0f : 0.0f, mMarkerTransparency));
geometry->setColorArray(colours, osg::Array::BIND_PER_VERTEX);
setupCommonMarkerState(geometry);
osg::ref_ptr<osg::Group> group(new osg::Group);
group->addChild(geometry);
return group;
}
osg::ref_ptr<osg::Node> CSVRender::Object::makeRotateMarker(int axis)
{
const float InnerRadius = std::max(MarkerShaftBaseLength, mBaseNode->getBound().radius());
const float OuterRadius = InnerRadius + MarkerShaftWidth;
const float SegmentDistance = 100.f;
const size_t SegmentCount = std::clamp<int>(OuterRadius * 2 * osg::PI / SegmentDistance, 24, 64);
const size_t VerticesPerSegment = 4;
const size_t IndicesPerSegment = 24;
const size_t VertexCount = SegmentCount * VerticesPerSegment;
const size_t IndexCount = SegmentCount * IndicesPerSegment;
const float Angle = 2 * osg::PI / SegmentCount;
const unsigned short IndexPattern[IndicesPerSegment]
= { 0, 4, 5, 0, 5, 1, 2, 6, 4, 2, 4, 0, 3, 7, 6, 3, 6, 2, 1, 5, 7, 1, 7, 3 };
osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry();
osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array(VertexCount);
osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array(1);
osg::ref_ptr<osg::DrawElementsUShort> primitives
= new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLES, IndexCount);
// prevent some depth collision issues from overlaps
osg::Vec3f offset = getMarkerPosition(0, MarkerShaftWidth / 4, 0, axis);
for (size_t i = 0; i < SegmentCount; ++i)
{
size_t index = i * VerticesPerSegment;
float innerX = InnerRadius * std::cos(i * Angle);
float innerY = InnerRadius * std::sin(i * Angle);
float outerX = OuterRadius * std::cos(i * Angle);
float outerY = OuterRadius * std::sin(i * Angle);
vertices->at(index++) = getMarkerPosition(innerX, innerY, MarkerShaftWidth / 2, axis) + offset;
vertices->at(index++) = getMarkerPosition(innerX, innerY, -MarkerShaftWidth / 2, axis) + offset;
vertices->at(index++) = getMarkerPosition(outerX, outerY, MarkerShaftWidth / 2, axis) + offset;
vertices->at(index++) = getMarkerPosition(outerX, outerY, -MarkerShaftWidth / 2, axis) + offset;
}
colors->at(0)
= osg::Vec4f(axis == 0 ? 1.0f : 0.2f, axis == 1 ? 1.0f : 0.2f, axis == 2 ? 1.0f : 0.2f, mMarkerTransparency);
for (size_t i = 0; i < SegmentCount; ++i)
{
size_t indices[IndicesPerSegment];
for (size_t j = 0; j < IndicesPerSegment; ++j)
{
indices[j] = i * VerticesPerSegment + j;
if (indices[j] >= VertexCount)
indices[j] -= VertexCount;
}
size_t elementOffset = i * IndicesPerSegment;
for (size_t j = 0; j < IndicesPerSegment; ++j)
{
primitives->setElement(elementOffset++, indices[IndexPattern[j]]);
}
}
geometry->setVertexArray(vertices);
geometry->setColorArray(colors, osg::Array::BIND_OVERALL);
geometry->addPrimitiveSet(primitives);
setupCommonMarkerState(geometry);
osg::ref_ptr<osg::Group> group = new osg::Group();
group->addChild(geometry);
return group;
}
void CSVRender::Object::setupCommonMarkerState(osg::ref_ptr<osg::Geometry> geometry)
{
osg::ref_ptr<osg::StateSet> state = geometry->getOrCreateStateSet();
state->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
state->setMode(GL_BLEND, osg::StateAttribute::ON);
state->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
}
osg::Vec3f CSVRender::Object::getMarkerPosition(float x, float y, float z, int axis)
{
switch (axis)
{
case 2:
return osg::Vec3f(x, y, z);
case 0:
return osg::Vec3f(z, x, y);
case 1:
return osg::Vec3f(y, z, x);
default:
throw std::logic_error("invalid axis for marker geometry");
}
}
CSVRender::Object::Object(
CSMWorld::Data& data, osg::Group* parentNode, const std::string& id, bool referenceable, bool forceBaseToZero)
: mData(data)
, mBaseNode(nullptr)
, mSelected(false)
, mParentNode(parentNode)
, mResourceSystem(data.getResourceSystem().get())
, mForceBaseToZero(forceBaseToZero)
, mScaleOverride(1)
, mOverrideFlags(0)
, mSubMode(-1)
, mMarkerTransparency(0.5f)
{
mRootNode = new osg::PositionAttitudeTransform;
mBaseNode = new osg::PositionAttitudeTransform;
mBaseNode->addCullCallback(new SceneUtil::LightListCallback);
mOutline = new osgFX::Scribe;
mBaseNode->setUserData(new ObjectTag(this));
mRootNode->addChild(mBaseNode);
parentNode->addChild(mRootNode);
mRootNode->setNodeMask(Mask_Reference);
ESM::RefId refId = ESM::RefId::stringRefId(id);
if (referenceable)
{
mReferenceableId = refId;
}
else
{
mReferenceId = refId;
mReferenceableId = getReference().mRefID;
}
adjustTransform();
update();
updateMarker();
}
CSVRender::Object::~Object()
{
clear();
mParentNode->removeChild(mRootNode);
}
void CSVRender::Object::setSelected(bool selected)
{
mSelected = selected;
if (mSnapTarget)
{
setSnapTarget(false);
}
mOutline->removeChild(mBaseNode);
mRootNode->removeChild(mOutline);
mRootNode->removeChild(mBaseNode);
if (selected)
{
mOutline->setWireframeColor(osg::Vec4f(1, 1, 1, 1));
mOutline->addChild(mBaseNode);
mRootNode->addChild(mOutline);
}
else
mRootNode->addChild(mBaseNode);
mMarkerTransparency = CSMPrefs::get()["Rendering"]["object-marker-alpha"].toDouble();
updateMarker();
}
bool CSVRender::Object::getSelected() const
{
return mSelected;
}
void CSVRender::Object::setSnapTarget(bool isSnapTarget)
{
mSnapTarget = isSnapTarget;
if (mSelected)
{
setSelected(false);
}
mOutline->removeChild(mBaseNode);
mRootNode->removeChild(mOutline);
mRootNode->removeChild(mBaseNode);
if (isSnapTarget)
{
mOutline->setWireframeColor(osg::Vec4f(1, 1, 0, 1));
mOutline->addChild(mBaseNode);
mRootNode->addChild(mOutline);
}
else
mRootNode->addChild(mBaseNode);
mMarkerTransparency = CSMPrefs::get()["Rendering"]["object-marker-alpha"].toDouble();
updateMarker();
}
bool CSVRender::Object::getSnapTarget() const
{
return mSnapTarget;
}
osg::ref_ptr<osg::Group> CSVRender::Object::getRootNode()
{
return mRootNode;
}
osg::ref_ptr<osg::Group> CSVRender::Object::getBaseNode()
{
return mBaseNode;
}
bool CSVRender::Object::referenceableDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight)
{
const CSMWorld::RefIdCollection& referenceables = mData.getReferenceables();
int index = referenceables.searchId(mReferenceableId);
if (index != -1 && index >= topLeft.row() && index <= bottomRight.row())
{
adjustTransform();
update();
updateMarker();
return true;
}
return false;
}
bool CSVRender::Object::referenceableAboutToBeRemoved(const QModelIndex& parent, int start, int end)
{
const CSMWorld::RefIdCollection& referenceables = mData.getReferenceables();
int index = referenceables.searchId(mReferenceableId);
if (index != -1 && index >= start && index <= end)
{
// Deletion of referenceable-type objects is handled outside of Object.
if (!mReferenceId.empty())
{
adjustTransform();
update();
return true;
}
}
return false;
}
bool CSVRender::Object::referenceDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight)
{
if (mReferenceId.empty())
return false;
const CSMWorld::RefCollection& references = mData.getReferences();
int index = references.searchId(mReferenceId);
if (index != -1 && index >= topLeft.row() && index <= bottomRight.row())
{
int columnIndex = references.findColumnIndex(CSMWorld::Columns::ColumnId_ReferenceableId);
adjustTransform();
if (columnIndex >= topLeft.column() && columnIndex <= bottomRight.row())
{
mReferenceableId
= ESM::RefId::stringRefId(references.getData(index, columnIndex).toString().toUtf8().constData());
update();
updateMarker();
}
return true;
}
return false;
}
void CSVRender::Object::reloadAssets()
{
update();
updateMarker();
}
std::string CSVRender::Object::getReferenceId() const
{
return mReferenceId.getRefIdString();
}
std::string CSVRender::Object::getReferenceableId() const
{
return mReferenceableId.getRefIdString();
}
osg::ref_ptr<CSVRender::TagBase> CSVRender::Object::getTag() const
{
return static_cast<CSVRender::TagBase*>(mBaseNode->getUserData());
}
bool CSVRender::Object::isEdited() const
{
return mOverrideFlags;
}
void CSVRender::Object::setEdited(int flags)
{
bool discard = mOverrideFlags & ~flags;
int added = flags & ~mOverrideFlags;
mOverrideFlags = flags;
if (added & Override_Position)
for (int i = 0; i < 3; ++i)
mPositionOverride.pos[i] = getReference().mPos.pos[i];
if (added & Override_Rotation)
for (int i = 0; i < 3; ++i)
mPositionOverride.rot[i] = getReference().mPos.rot[i];
if (added & Override_Scale)
mScaleOverride = getReference().mScale;
if (discard)
adjustTransform();
}
ESM::Position CSVRender::Object::getPosition() const
{
ESM::Position position = getReference().mPos;
if (mOverrideFlags & Override_Position)
for (int i = 0; i < 3; ++i)
position.pos[i] = mPositionOverride.pos[i];
if (mOverrideFlags & Override_Rotation)
for (int i = 0; i < 3; ++i)
position.rot[i] = mPositionOverride.rot[i];
return position;
}
float CSVRender::Object::getScale() const
{
return (mOverrideFlags & Override_Scale) ? mScaleOverride : getReference().mScale;
}
void CSVRender::Object::setPosition(const float position[3])
{
mOverrideFlags |= Override_Position;
for (int i = 0; i < 3; ++i)
mPositionOverride.pos[i] = position[i];
adjustTransform();
}
void CSVRender::Object::setRotation(const float rotation[3])
{
mOverrideFlags |= Override_Rotation;
for (int i = 0; i < 3; ++i)
mPositionOverride.rot[i] = rotation[i];
adjustTransform();
}
void CSVRender::Object::setScale(float scale)
{
mOverrideFlags |= Override_Scale;
mScaleOverride = scale;
adjustTransform();
}
void CSVRender::Object::setMarkerTransparency(float value)
{
mMarkerTransparency = value;
updateMarker();
}
void CSVRender::Object::apply(CSMWorld::CommandMacro& commands)
{
const CSMWorld::RefCollection& collection = mData.getReferences();
QAbstractItemModel* model = mData.getTableModel(CSMWorld::UniversalId::Type_References);
int recordIndex = collection.getIndex(mReferenceId);
if (mOverrideFlags & Override_Position)
{
// Do cell check first so positions can be compared
const CSMWorld::CellRef& ref = collection.getRecord(recordIndex).get();
if (CSMWorld::CellCoordinates::isExteriorCell(ref.mCell.getRefIdString()))
{
// Find cell index at new position
std::pair<int, int> cellIndex
= CSMWorld::CellCoordinates::coordinatesToCellIndex(mPositionOverride.pos[0], mPositionOverride.pos[1]);
std::pair<int, int> originalIndex = ref.getCellIndex();
int cellColumn = collection.findColumnIndex(
static_cast<CSMWorld::Columns::ColumnId>(CSMWorld::Columns::ColumnId_Cell));
int origCellColumn = collection.findColumnIndex(
static_cast<CSMWorld::Columns::ColumnId>(CSMWorld::Columns::ColumnId_OriginalCell));
if (cellIndex != originalIndex)
{
/// \todo figure out worldspace (not important until multiple worldspaces are supported)
std::string origCellId = CSMWorld::CellCoordinates(originalIndex).getId("");
std::string cellId = CSMWorld::CellCoordinates(cellIndex).getId("");
commands.push(new CSMWorld::ModifyCommand(
*model, model->index(recordIndex, origCellColumn), QString::fromUtf8(origCellId.c_str())));
commands.push(new CSMWorld::ModifyCommand(
*model, model->index(recordIndex, cellColumn), QString::fromUtf8(cellId.c_str())));
// NOTE: refnum is not modified for moving a reference to another cell
}
}
for (int i = 0; i < 3; ++i)
{
int column = collection.findColumnIndex(
static_cast<CSMWorld::Columns::ColumnId>(CSMWorld::Columns::ColumnId_PositionXPos + i));
commands.push(
new CSMWorld::ModifyCommand(*model, model->index(recordIndex, column), mPositionOverride.pos[i]));
}
}
if (mOverrideFlags & Override_Rotation)
{
for (int i = 0; i < 3; ++i)
{
int column = collection.findColumnIndex(
static_cast<CSMWorld::Columns::ColumnId>(CSMWorld::Columns::ColumnId_PositionXRot + i));
commands.push(new CSMWorld::ModifyCommand(
*model, model->index(recordIndex, column), osg::RadiansToDegrees(mPositionOverride.rot[i])));
}
}
if (mOverrideFlags & Override_Scale)
{
int column = collection.findColumnIndex(CSMWorld::Columns::ColumnId_Scale);
commands.push(new CSMWorld::ModifyCommand(*model, model->index(recordIndex, column), mScaleOverride));
}
mOverrideFlags = 0;
}
void CSVRender::Object::setSubMode(int subMode)
{
if (subMode != mSubMode)
{
mSubMode = subMode;
updateMarker();
}
}
void CSVRender::Object::reset()
{
mOverrideFlags = 0;
adjustTransform();
updateMarker();
}