mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-01-28 14:53:58 +00:00
20da0892ef
Slowly moving through the open-cs errors Good progress in openCS Very good progress on openCS Getting closer with openCS OpenCS compiles and runs! Didn't have time to test it all though ix openMW everything compiles on windows?? Fix gcc Fix Clang
810 lines
23 KiB
C++
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.getRefIdString());
|
|
|
|
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();
|
|
}
|