Merge branch 'master' into appveyor

coverity_scan
Alexander "Ace" Olofsson 9 years ago
commit 0d2bd31f8b

@ -19,6 +19,7 @@ Programmers
Alexander Nadeau (wareya)
Alexander Olofsson (Ace)
Artem Kotsynyak (greye)
artemutin
Arthur Moore (EmperorArthur)
athile
Bret Curtis (psi29a)

@ -67,7 +67,7 @@ opencs_hdrs_noqt (view/doc
opencs_units (view/world
table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator
cellcreator referenceablecreator referencecreator scenesubview
cellcreator referenceablecreator startscriptcreator referencecreator scenesubview
infocreator scriptedit dialoguesubview previewsubview regionmap dragrecordtable nestedtable
dialoguespinbox recordbuttonbar tableeditidaction scripterrortable extendedcommandconfigurator
)
@ -85,12 +85,12 @@ opencs_units (view/widget
opencs_units (view/render
scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget
previewwidget editmode
previewwidget editmode instancemode
)
opencs_units_noqt (view/render
lighting lightingday lightingnight
lightingbright object cell terrainstorage
lightingbright object cell terrainstorage tagbase cellarrow
)
opencs_hdrs_noqt (view/render

@ -371,6 +371,57 @@ void CSMSettings::UserSettings::buildSettingModelDefaults()
"list go to the first/last item");
}
declareSection ("scene-input", "3D Scene Input");
{
QString left ("Left Mouse-Button");
QString cLeft ("Ctrl-Left Mouse-Button");
QString right ("Right Mouse-Button");
QString cRight ("Ctrl-Right Mouse-Button");
QString middle ("Middle Mouse-Button");
QString cMiddle ("Ctrl-Middle Mouse-Button");
QStringList values;
values << left << cLeft << right << cRight << middle << cMiddle;
Setting *primaryNavigation = createSetting (Type_ComboBox, "p-navi", "Primary Camera Navigation Button");
primaryNavigation->setDeclaredValues (values);
primaryNavigation->setDefaultValue (left);
Setting *secondaryNavigation = createSetting (Type_ComboBox, "s-navi", "Secondary Camera Navigation Button");
secondaryNavigation->setDeclaredValues (values);
secondaryNavigation->setDefaultValue (cLeft);
Setting *primaryEditing = createSetting (Type_ComboBox, "p-edit", "Primary Editing Button");
primaryEditing->setDeclaredValues (values);
primaryEditing->setDefaultValue (right);
Setting *secondaryEditing = createSetting (Type_ComboBox, "s-edit", "Secondary Editing Button");
secondaryEditing->setDeclaredValues (values);
secondaryEditing->setDefaultValue (cRight);
Setting *selection = createSetting (Type_ComboBox, "select", "Selection Button");
selection->setDeclaredValues (values);
selection->setDefaultValue (middle);
Setting *contextSensitive = createSetting (Type_CheckBox, "context-select", "Context Sensitive Selection");
contextSensitive->setDefaultValue ("false");
Setting *dragMouseSensitivity = createSetting (Type_DoubleSpinBox, "drag-factor",
"Mouse sensitivity during drag operations");
dragMouseSensitivity->setDefaultValue (1.0);
dragMouseSensitivity->setRange (0.001, 100.0);
Setting *dragWheelSensitivity = createSetting (Type_DoubleSpinBox, "drag-wheel-factor",
"Mouse wheel sensitivity during drag operations");
dragWheelSensitivity->setDefaultValue (1.0);
dragWheelSensitivity->setRange (0.001, 100.0);
Setting *dragShiftFactor = createSetting (Type_DoubleSpinBox, "drag-shift-factor",
"Acceleration factor during drag operations while holding down shift");
dragShiftFactor->setDefaultValue (4.0);
dragShiftFactor->setRange (0.001, 100.0);
}
{
/******************************************************************
* There are three types of values:

@ -32,6 +32,23 @@ std::string CSMWorld::CellCoordinates::getId (const std::string& worldspace) con
return stream.str();
}
std::pair<CSMWorld::CellCoordinates, bool> CSMWorld::CellCoordinates::fromId (
const std::string& id)
{
// no worldspace for now, needs to be changed for 1.1
if (!id.empty() && id[0]=='#')
{
int x, y;
char ignore;
std::istringstream stream (id);
if (stream >> ignore >> x >> y)
return std::make_pair (CellCoordinates (x, y), true);
}
return std::make_pair (CellCoordinates(), false);
}
bool CSMWorld::operator== (const CellCoordinates& left, const CellCoordinates& right)
{
return left.getX()==right.getX() && left.getY()==right.getY();

@ -28,6 +28,12 @@ namespace CSMWorld
std::string getId (const std::string& worldspace) const;
///< Return the ID for the cell at these coordinates.
/// \return first: CellCoordinates (or 0, 0 if cell does not have coordinates),
/// second: is cell paged?
///
/// \note The worldspace part of \a id is ignored
static std::pair<CellCoordinates, bool> fromId (const std::string& id);
};
bool operator== (const CellCoordinates& left, const CellCoordinates& right);

@ -77,7 +77,7 @@ bool CSMWorld::ColumnBase::isId (Display display)
Display_Video,
Display_Id,
Display_SkillImpact,
Display_SkillId,
Display_EffectRange,
Display_EffectId,
Display_PartRefType,
@ -85,7 +85,6 @@ bool CSMWorld::ColumnBase::isId (Display display)
Display_InfoCondFunc,
Display_InfoCondVar,
Display_InfoCondComp,
Display_RaceSkill,
Display_None
};

@ -113,7 +113,7 @@ namespace CSMWorld
Display_SoundGeneratorType,
Display_School,
Display_Id,
Display_SkillImpact,
Display_SkillId,
Display_EffectRange,
Display_EffectId,
Display_PartRefType,
@ -121,7 +121,6 @@ namespace CSMWorld
Display_InfoCondFunc,
Display_InfoCondVar,
Display_InfoCondComp,
Display_RaceSkill,
Display_String32,
Display_LongString256,

@ -249,7 +249,7 @@ namespace CSMWorld
{ ColumnId_AiWanderDist, "Wander Dist" },
{ ColumnId_AiDuration, "Ai Duration" },
{ ColumnId_AiWanderToD, "Wander ToD" },
{ ColumnId_AiWanderIdle, "Wander Idle" },
//{ ColumnId_AiWanderIdle, "Wander Idle" },
{ ColumnId_AiWanderRepeat, "Wander Repeat" },
{ ColumnId_AiActivateName, "Activate" },
{ ColumnId_AiTargetId, "Target ID" },
@ -268,7 +268,7 @@ namespace CSMWorld
{ ColumnId_LevelledItemChanceNone, "Chance None" },
{ ColumnId_PowerList, "Powers" },
{ ColumnId_SkillImpact, "Skill" },
{ ColumnId_Skill, "Skill" },
{ ColumnId_InfoList, "Info List" },
{ ColumnId_InfoCondition, "Info Conditions" },
@ -293,8 +293,7 @@ namespace CSMWorld
{ ColumnId_NpcPersistence, "Persistent" },
{ ColumnId_RaceAttributes, "Race Attributes" },
{ ColumnId_RaceMaleValue, "Male Attrib" },
{ ColumnId_RaceFemaleValue, "Female Attrib" },
{ ColumnId_Male, "Male" },
{ ColumnId_RaceSkillBonus, "Skill Bonus" },
{ ColumnId_RaceBonus, "Bonus" },
@ -317,6 +316,15 @@ namespace CSMWorld
{ ColumnId_MaxAttack, "Max Attack" },
{ ColumnId_CreatureMisc, "Creature Misc" },
{ ColumnId_Idle1, "Idle 1" },
{ ColumnId_Idle2, "Idle 2" },
{ ColumnId_Idle3, "Idle 3" },
{ ColumnId_Idle4, "Idle 4" },
{ ColumnId_Idle5, "Idle 5" },
{ ColumnId_Idle6, "Idle 6" },
{ ColumnId_Idle7, "Idle 7" },
{ ColumnId_Idle8, "Idle 8" },
{ ColumnId_UseValue1, "Use value 1" },
{ ColumnId_UseValue2, "Use value 2" },
{ ColumnId_UseValue3, "Use value 3" },
@ -572,7 +580,7 @@ namespace
case CSMWorld::Columns::ColumnId_MeshType: return sMeshTypes;
case CSMWorld::Columns::ColumnId_SoundGeneratorType: return sSoundGeneratorType;
case CSMWorld::Columns::ColumnId_School: return sSchools;
case CSMWorld::Columns::ColumnId_SkillImpact: return sSkills;
case CSMWorld::Columns::ColumnId_Skill: return sSkills;
case CSMWorld::Columns::ColumnId_EffectRange: return sEffectRange;
case CSMWorld::Columns::ColumnId_EffectId: return sEffectId;
case CSMWorld::Columns::ColumnId_PartRefType: return sPartRefType;

@ -241,7 +241,7 @@ namespace CSMWorld
ColumnId_AiWanderDist = 221,
ColumnId_AiDuration = 222,
ColumnId_AiWanderToD = 223,
ColumnId_AiWanderIdle = 224,
// unused
ColumnId_AiWanderRepeat = 225,
ColumnId_AiActivateName = 226,
// use ColumnId_PosX, etc for AI destinations
@ -261,7 +261,7 @@ namespace CSMWorld
ColumnId_LevelledItemChanceNone = 238,
ColumnId_PowerList = 239,
ColumnId_SkillImpact = 240, // impact from magic effects
ColumnId_Skill = 240,
ColumnId_InfoList = 241,
ColumnId_InfoCondition = 242,
@ -288,8 +288,8 @@ namespace CSMWorld
ColumnId_NpcPersistence = 261,
ColumnId_RaceAttributes = 262,
ColumnId_RaceMaleValue = 263,
ColumnId_RaceFemaleValue = 264,
ColumnId_Male = 263,
// unused
ColumnId_RaceSkillBonus = 265,
// unused
ColumnId_RaceBonus = 267,
@ -316,6 +316,15 @@ namespace CSMWorld
ColumnId_MaxAttack = 284,
ColumnId_CreatureMisc = 285,
ColumnId_Idle1 = 286,
ColumnId_Idle2 = 287,
ColumnId_Idle3 = 288,
ColumnId_Idle4 = 289,
ColumnId_Idle5 = 290,
ColumnId_Idle6 = 291,
ColumnId_Idle7 = 292,
ColumnId_Idle8 = 293,
// Allocated to a separate value range, so we don't get a collision should we ever need
// to extend the number of use values.
ColumnId_UseValue1 = 0x10000,

@ -143,15 +143,15 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
new NestedChildColumn (Columns::ColumnId_Attribute, ColumnBase::Display_Attribute,
ColumnBase::Flag_Dialogue, false));
mRaces.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_RaceMaleValue, ColumnBase::Display_Integer));
new NestedChildColumn (Columns::ColumnId_Male, ColumnBase::Display_Integer));
mRaces.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_RaceFemaleValue, ColumnBase::Display_Integer));
new NestedChildColumn (Columns::ColumnId_Female, ColumnBase::Display_Integer));
// Race skill bonus
mRaces.addColumn (new NestedParentColumn<ESM::Race> (Columns::ColumnId_RaceSkillBonus));
index = mRaces.getColumns()-1;
mRaces.addAdapter (std::make_pair(&mRaces.getColumn(index), new RaceSkillsBonusAdapter()));
mRaces.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_SkillImpact, ColumnBase::Display_SkillImpact));
new NestedChildColumn (Columns::ColumnId_Skill, ColumnBase::Display_SkillId));
mRaces.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_RaceBonus, ColumnBase::Display_Integer));
@ -213,7 +213,7 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
mSpells.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_EffectId, ColumnBase::Display_EffectId));
mSpells.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_SkillImpact, ColumnBase::Display_SkillImpact));
new NestedChildColumn (Columns::ColumnId_Skill, ColumnBase::Display_SkillId));
mSpells.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_Attribute, ColumnBase::Display_Attribute));
mSpells.getNestableColumn(index)->addColumn(
@ -329,7 +329,7 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
mEnchantments.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_EffectId, ColumnBase::Display_EffectId));
mEnchantments.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_SkillImpact, ColumnBase::Display_SkillImpact));
new NestedChildColumn (Columns::ColumnId_Skill, ColumnBase::Display_SkillId));
mEnchantments.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_Attribute, ColumnBase::Display_Attribute));
mEnchantments.getNestableColumn(index)->addColumn(

@ -1532,6 +1532,8 @@ namespace CSMWorld
virtual ~ActorAiRefIdAdapter() {}
// FIXME: should check if the AI package type is already in the list and use a default
// that wasn't used already (in extreme case do not add anything at all?
virtual void addNestedRow (const RefIdColumn *column,
RefIdData& data, int index, int position) const
{
@ -1615,6 +1617,7 @@ namespace CSMWorld
switch (subColIndex)
{
case 0:
// FIXME: should more than one AI package type be allowed? Check vanilla
switch (content.mType)
{
case ESM::AI_Wander: return 0;
@ -1642,47 +1645,52 @@ namespace CSMWorld
else
return QVariant();
case 4: // wander idle
case 5:
case 6:
case 7:
case 8:
case 9:
case 10:
case 11:
if (content.mType == ESM::AI_Wander)
{
return static_cast<int>(content.mWander.mIdle[0]); // FIXME:
}
return static_cast<int>(content.mWander.mIdle[subColIndex-4]);
else
return QVariant();
case 5: // wander repeat
case 12: // wander repeat
if (content.mType == ESM::AI_Wander)
return content.mWander.mShouldRepeat != 0;
else
return QVariant();
case 6: // activate name
case 13: // activate name
if (content.mType == ESM::AI_Activate)
return QString(content.mActivate.mName.toString().c_str());
else
return QVariant();
case 7: // target id
case 14: // target id
if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort)
return QString(content.mTarget.mId.toString().c_str());
else
return QVariant();
case 8: // target cell
case 15: // target cell
if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort)
return QString::fromUtf8(content.mCellName.c_str());
else
return QVariant();
case 9:
case 16:
if (content.mType == ESM::AI_Travel)
return content.mTravel.mX;
else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort)
return content.mTarget.mX;
else
return QVariant();
case 10:
case 17:
if (content.mType == ESM::AI_Travel)
return content.mTravel.mY;
else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort)
return content.mTarget.mY;
else
return QVariant();
case 11:
case 18:
if (content.mType == ESM::AI_Travel)
return content.mTravel.mZ;
else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort)
@ -1712,11 +1720,12 @@ namespace CSMWorld
case 0: // ai package type
switch (value.toInt())
{
case 0: content.mType = ESM::AI_Wander;
case 1: content.mType = ESM::AI_Travel;
case 2: content.mType = ESM::AI_Follow;
case 3: content.mType = ESM::AI_Escort;
case 4: content.mType = ESM::AI_Activate;
case 0: content.mType = ESM::AI_Wander; break;
case 1: content.mType = ESM::AI_Travel; break;
case 2: content.mType = ESM::AI_Follow; break;
case 3: content.mType = ESM::AI_Escort; break;
case 4: content.mType = ESM::AI_Activate; break;
default: return; // return without saving
}
break; // always save
@ -1725,6 +1734,8 @@ namespace CSMWorld
content.mWander.mDistance = static_cast<short>(value.toInt());
else
return; // return without saving
break; // always save
case 2:
if (content.mType == ESM::AI_Wander ||
content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort)
@ -1736,62 +1747,77 @@ namespace CSMWorld
content.mWander.mTimeOfDay = static_cast<unsigned char>(value.toInt());
else
return; // return without saving
break; // always save
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
case 10:
case 11:
if (content.mType == ESM::AI_Wander)
break; // FIXME: idle
content.mWander.mIdle[subColIndex-4] = static_cast<unsigned char>(value.toInt());
else
return; // return without saving
case 5:
break; // always save
case 12:
if (content.mType == ESM::AI_Wander)
{
content.mWander.mShouldRepeat = static_cast<unsigned char>(value.toInt());
break;
}
case 6: // NAME32
else
return; // return without saving
break; // always save
case 13: // NAME32
if (content.mType == ESM::AI_Activate)
{
content.mActivate.mName.assign(value.toString().toUtf8().constData());
break;
}
else
return; // return without saving
case 7: // NAME32
break; // always save
case 14: // NAME32
if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort)
{
content.mTarget.mId.assign(value.toString().toUtf8().constData());
break;
}
else
return; // return without saving
case 8:
break; // always save
case 15:
if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort)
{
content.mCellName = std::string(value.toString().toUtf8().constData());
break;
}
else
return; // return without saving
case 9:
break; // always save
case 16:
if (content.mType == ESM::AI_Travel)
content.mTravel.mZ = value.toFloat();
else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort)
content.mTarget.mZ = value.toFloat();
else
return; // return without saving
case 10:
break; // always save
case 17:
if (content.mType == ESM::AI_Travel)
content.mTravel.mZ = value.toFloat();
else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort)
content.mTarget.mZ = value.toFloat();
else
return; // return without saving
case 11:
break; // always save
case 18:
if (content.mType == ESM::AI_Travel)
content.mTravel.mZ = value.toFloat();
else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort)
content.mTarget.mZ = value.toFloat();
else
return; // return without saving
break; // always save
default:
throw std::runtime_error("Trying to access non-existing column in the nested table!");
}
@ -1801,7 +1827,7 @@ namespace CSMWorld
virtual int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const
{
return 12;
return 19;
}
virtual int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const

@ -83,7 +83,7 @@ CSMWorld::RefIdCollection::RefIdCollection()
mColumns.back().addColumn(
new NestedChildColumn (Columns::ColumnId_EffectId, ColumnBase::Display_EffectId));
mColumns.back().addColumn(
new NestedChildColumn (Columns::ColumnId_SkillImpact, ColumnBase::Display_SkillImpact));
new NestedChildColumn (Columns::ColumnId_Skill, ColumnBase::Display_SkillId));
mColumns.back().addColumn(
new NestedChildColumn (Columns::ColumnId_Attribute, ColumnBase::Display_Attribute));
mColumns.back().addColumn(
@ -193,8 +193,24 @@ CSMWorld::RefIdCollection::RefIdCollection()
new RefIdColumn (Columns::ColumnId_AiDuration, CSMWorld::ColumnBase::Display_Integer));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_AiWanderToD, CSMWorld::ColumnBase::Display_Integer));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_Idle1, CSMWorld::ColumnBase::Display_Integer));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_Idle2, CSMWorld::ColumnBase::Display_Integer));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_Idle3, CSMWorld::ColumnBase::Display_Integer));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_Idle4, CSMWorld::ColumnBase::Display_Integer));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_AiWanderIdle, CSMWorld::ColumnBase::Display_Integer));
new RefIdColumn (Columns::ColumnId_Idle5, CSMWorld::ColumnBase::Display_Integer));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_Idle6, CSMWorld::ColumnBase::Display_Integer));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_Idle7, CSMWorld::ColumnBase::Display_Integer));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_Idle8, CSMWorld::ColumnBase::Display_Integer));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_AiWanderRepeat, CSMWorld::ColumnBase::Display_Boolean));
mColumns.back().addColumn(
@ -491,7 +507,7 @@ CSMWorld::RefIdCollection::RefIdCollection()
skillsMap.insert(std::make_pair(UniversalId::Type_Npc, new NpcSkillsRefIdAdapter()));
mNestedAdapters.push_back (std::make_pair(&mColumns.back(), skillsMap));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_SkillImpact, CSMWorld::ColumnBase::Display_SkillImpact, false, false));
new RefIdColumn (Columns::ColumnId_Skill, CSMWorld::ColumnBase::Display_SkillId, false, false));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_UChar, CSMWorld::ColumnBase::Display_Integer));

@ -435,6 +435,8 @@ CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int to
mOperations = new Operations;
addDockWidget (Qt::BottomDockWidgetArea, mOperations);
setContextMenuPolicy(Qt::NoContextMenu);
updateTitle();
setupUi();

@ -97,7 +97,7 @@ CSVDoc::ViewManager::ViewManager (CSMDoc::DocumentManager& documentManager)
{ CSMWorld::ColumnBase::Display_Gender, CSMWorld::Columns::ColumnId_Gender, true },
{ CSMWorld::ColumnBase::Display_SoundGeneratorType, CSMWorld::Columns::ColumnId_SoundGeneratorType, false },
{ CSMWorld::ColumnBase::Display_School, CSMWorld::Columns::ColumnId_School, false },
{ CSMWorld::ColumnBase::Display_SkillImpact, CSMWorld::Columns::ColumnId_SkillImpact, true },
{ CSMWorld::ColumnBase::Display_SkillId, CSMWorld::Columns::ColumnId_Skill, true },
{ CSMWorld::ColumnBase::Display_EffectRange, CSMWorld::Columns::ColumnId_EffectRange, false },
{ CSMWorld::ColumnBase::Display_EffectId, CSMWorld::Columns::ColumnId_EffectId, false },
{ CSMWorld::ColumnBase::Display_PartRefType, CSMWorld::Columns::ColumnId_PartRefType, false },

@ -5,14 +5,17 @@
#include <QApplication>
#include "../../model/world/data.hpp"
#include "../../model/world/idtablebase.hpp"
#include "../../model/world/columns.hpp"
CSVFilter::EditWidget::EditWidget (CSMWorld::Data& data, QWidget *parent)
: QLineEdit (parent), mParser (data)
: QLineEdit (parent), mParser (data), mIsEmpty(true)
{
mPalette = palette();
connect (this, SIGNAL (textChanged (const QString&)), this, SLOT (textChanged (const QString&)));
QAbstractItemModel *model = data.getTableModel (CSMWorld::UniversalId::Type_Filters);
const CSMWorld::IdTableBase *model =
static_cast<const CSMWorld::IdTableBase *> (data.getTableModel (CSMWorld::UniversalId::Type_Filters));
connect (model, SIGNAL (dataChanged (const QModelIndex &, const QModelIndex&)),
this, SLOT (filterDataChanged (const QModelIndex &, const QModelIndex&)),
@ -23,10 +26,23 @@ CSVFilter::EditWidget::EditWidget (CSMWorld::Data& data, QWidget *parent)
connect (model, SIGNAL (rowsInserted (const QModelIndex&, int, int)),
this, SLOT (filterRowsInserted (const QModelIndex&, int, int)),
Qt::QueuedConnection);
mStateColumnIndex = model->findColumnIndex(CSMWorld::Columns::ColumnId_Modification);
mDescColumnIndex = model->findColumnIndex(CSMWorld::Columns::ColumnId_Description);
}
void CSVFilter::EditWidget::textChanged (const QString& text)
{
//no need to parse and apply filter if it was empty and now is empty too.
//e.g. - we modifiing content of filter with already opened some other (big) tables.
if (text.length() == 0){
if (mIsEmpty)
return;
else
mIsEmpty = true;
}else
mIsEmpty = false;
if (mParser.parse (text.toUtf8().constData()))
{
setPalette (mPalette);
@ -45,7 +61,9 @@ void CSVFilter::EditWidget::textChanged (const QString& text)
void CSVFilter::EditWidget::filterDataChanged (const QModelIndex& topLeft,
const QModelIndex& bottomRight)
{
textChanged (text());
for (int i = topLeft.column(); i <= bottomRight.column(); ++i)
if (i != mStateColumnIndex && i != mDescColumnIndex)
textChanged (text());
}
void CSVFilter::EditWidget::filterRowsRemoved (const QModelIndex& parent, int start, int end)

@ -25,6 +25,9 @@ namespace CSVFilter
CSMFilter::Parser mParser;
QPalette mPalette;
bool mIsEmpty;
int mStateColumnIndex;
int mDescColumnIndex;
public:

@ -9,6 +9,7 @@
#include "../../model/world/columns.hpp"
#include "../../model/world/data.hpp"
#include "../../model/world/refcollection.hpp"
#include "../../model/world/cellcoordinates.hpp"
#include "elements.hpp"
#include "terrainstorage.hpp"
@ -50,33 +51,39 @@ bool CSVRender::Cell::addObjects (int start, int end)
return modified;
}
CSVRender::Cell::Cell (CSMWorld::Data& data, osg::Group* rootNode, const std::string& id)
: mData (data), mId (Misc::StringUtils::lowerCase (id)), mX(0), mY(0)
CSVRender::Cell::Cell (CSMWorld::Data& data, osg::Group* rootNode, const std::string& id,
bool deleted)
: mData (data), mId (Misc::StringUtils::lowerCase (id)), mDeleted (deleted)
{
std::pair<CSMWorld::CellCoordinates, bool> result = CSMWorld::CellCoordinates::fromId (id);
if (result.second)
mCoordinates = result.first;
mCellNode = new osg::Group;
rootNode->addChild(mCellNode);
CSMWorld::IdTable& references = dynamic_cast<CSMWorld::IdTable&> (
*mData.getTableModel (CSMWorld::UniversalId::Type_References));
if (!mDeleted)
{
CSMWorld::IdTable& references = dynamic_cast<CSMWorld::IdTable&> (
*mData.getTableModel (CSMWorld::UniversalId::Type_References));
int rows = references.rowCount();
int rows = references.rowCount();
addObjects (0, rows-1);
addObjects (0, rows-1);
const CSMWorld::IdCollection<CSMWorld::Land>& land = mData.getLand();
int landIndex = land.searchId(mId);
if (landIndex != -1)
{
const ESM::Land& esmLand = land.getRecord(mId).get();
if (esmLand.getLandData (ESM::Land::DATA_VHGT))
const CSMWorld::IdCollection<CSMWorld::Land>& land = mData.getLand();
int landIndex = land.searchId(mId);
if (landIndex != -1)
{
mTerrain.reset(new Terrain::TerrainGrid(mCellNode, data.getResourceSystem().get(), NULL, new TerrainStorage(mData), Element_Terrain<<1));
mTerrain->loadCell(esmLand.mX,
esmLand.mY);
const ESM::Land& esmLand = land.getRecord(mId).get();
mX = esmLand.mX;
mY = esmLand.mY;
if (esmLand.getLandData (ESM::Land::DATA_VHGT))
{
mTerrain.reset(new Terrain::TerrainGrid(mCellNode, data.getResourceSystem().get(), NULL, new TerrainStorage(mData), Element_Terrain<<1));
mTerrain->loadCell(esmLand.mX,
esmLand.mY);
}
}
}
}
@ -122,6 +129,9 @@ bool CSVRender::Cell::referenceableAboutToBeRemoved (const QModelIndex& parent,
bool CSVRender::Cell::referenceDataChanged (const QModelIndex& topLeft,
const QModelIndex& bottomRight)
{
if (mDeleted)
return false;
CSMWorld::IdTable& references = dynamic_cast<CSMWorld::IdTable&> (
*mData.getTableModel (CSMWorld::UniversalId::Type_References));
@ -189,6 +199,9 @@ bool CSVRender::Cell::referenceAboutToBeRemoved (const QModelIndex& parent, int
if (parent.isValid())
return false;
if (mDeleted)
return false;
CSMWorld::IdTable& references = dynamic_cast<CSMWorld::IdTable&> (
*mData.getTableModel (CSMWorld::UniversalId::Type_References));
@ -209,5 +222,57 @@ bool CSVRender::Cell::referenceAdded (const QModelIndex& parent, int start, int
if (parent.isValid())
return false;
if (mDeleted)
return false;
return addObjects (start, end);
}
void CSVRender::Cell::setSelection (int elementMask, Selection mode)
{
if (elementMask & Element_Reference)
{
for (std::map<std::string, Object *>::const_iterator iter (mObjects.begin());
iter!=mObjects.end(); ++iter)
{
bool selected = false;
switch (mode)
{
case Selection_Clear: selected = false; break;
case Selection_All: selected = true; break;
case Selection_Invert: selected = !iter->second->getSelected(); break;
}
iter->second->setSelected (selected);
}
}
}
void CSVRender::Cell::setCellArrows (int mask)
{
for (int i=0; i<4; ++i)
{
CellArrow::Direction direction = static_cast<CellArrow::Direction> (1<<i);
bool enable = mask & direction;
if (enable!=(mCellArrows[i].get()!=0))
{
if (enable)
mCellArrows[i].reset (new CellArrow (mCellNode, direction, mCoordinates));
else
mCellArrows[i].reset (0);
}
}
}
CSMWorld::CellCoordinates CSVRender::Cell::getCoordinates() const
{
return mCoordinates;
}
bool CSVRender::Cell::isDeleted() const
{
return mDeleted;
}

@ -14,6 +14,7 @@
#endif
#include "object.hpp"
#include "cellarrow.hpp"
class QModelIndex;
@ -25,6 +26,7 @@ namespace osg
namespace CSMWorld
{
class Data;
class CellCoordinates;
}
namespace CSVRender
@ -36,8 +38,9 @@ namespace CSVRender
osg::ref_ptr<osg::Group> mCellNode;
std::map<std::string, Object *> mObjects;
std::auto_ptr<Terrain::TerrainGrid> mTerrain;
int mX;
int mY;
CSMWorld::CellCoordinates mCoordinates;
std::auto_ptr<CellArrow> mCellArrows[4];
bool mDeleted;
/// Ignored if cell does not have an object with the given ID.
///
@ -51,7 +54,19 @@ namespace CSVRender
public:
Cell (CSMWorld::Data& data, osg::Group* rootNode, const std::string& id);
enum Selection
{
Selection_Clear,
Selection_All,
Selection_Invert
};
public:
/// \note Deleted covers both cells that are deleted and cells that don't exist in
/// the first place.
Cell (CSMWorld::Data& data, osg::Group* rootNode, const std::string& id,
bool deleted = false);
~Cell();
@ -75,6 +90,15 @@ namespace CSVRender
/// \return Did this call result in a modification of the visual representation of
/// this cell?
bool referenceAdded (const QModelIndex& parent, int start, int end);
void setSelection (int elementMask, Selection mode);
void setCellArrows (int mask);
/// Returns 0, 0 in case of an unpaged cell.
CSMWorld::CellCoordinates getCoordinates() const;
bool isDeleted() const;
};
}

@ -0,0 +1,161 @@
#include "cellarrow.hpp"
#include <osg/Group>
#include <osg/PositionAttitudeTransform>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/PrimitiveSet>
#include "elements.hpp"
CSVRender::CellArrowTag::CellArrowTag (CellArrow *arrow)
: TagBase (Element_CellArrow), mArrow (arrow)
{}
CSVRender::CellArrow *CSVRender::CellArrowTag::getCellArrow() const
{
return mArrow;
}
void CSVRender::CellArrow::adjustTransform()
{
// position
const int cellSize = 8192;
const int offset = cellSize / 2 + 800;
int x = mCoordinates.getX()*cellSize + cellSize/2;
int y = mCoordinates.getY()*cellSize + cellSize/2;
float xr = 0;
float yr = 0;
float zr = 0;
float angle = osg::DegreesToRadians (90.0f);
switch (mDirection)
{
case Direction_North: y += offset; xr = -angle; zr = angle; break;
case Direction_West: x -= offset; yr = -angle; break;
case Direction_South: y -= offset; xr = angle; zr = angle; break;
case Direction_East: x += offset; yr = angle; break;
};
mBaseNode->setPosition (osg::Vec3f (x, y, 0));
// orientation
osg::Quat xr2 (xr, osg::Vec3f (1,0,0));
osg::Quat yr2 (yr, osg::Vec3f (0,1,0));
osg::Quat zr2 (zr, osg::Vec3f (0,0,1));
mBaseNode->setAttitude (zr2*yr2*xr2);
}
void CSVRender::CellArrow::buildShape()
{
osg::ref_ptr<osg::Geometry> geometry (new osg::Geometry);
const int arrowWidth = 4000;
const int arrowLength = 1500;
const int arrowHeight = 500;
osg::Vec3Array *vertices = new osg::Vec3Array;
for (int i2=0; i2<2; ++i2)
for (int i=0; i<2; ++i)
{
float height = i ? -arrowHeight/2 : arrowHeight/2;
vertices->push_back (osg::Vec3f (height, -arrowWidth/2, 0));
vertices->push_back (osg::Vec3f (height, arrowWidth/2, 0));
vertices->push_back (osg::Vec3f (height, 0, arrowLength));
}
geometry->setVertexArray (vertices);
osg::DrawElementsUShort *primitives = new osg::DrawElementsUShort (osg::PrimitiveSet::TRIANGLES, 0);
// top
primitives->push_back (0);
primitives->push_back (1);
primitives->push_back (2);
// bottom
primitives->push_back (5);
primitives->push_back (4);
primitives->push_back (3);
// back
primitives->push_back (3+6);
primitives->push_back (4+6);
primitives->push_back (1+6);
primitives->push_back (3+6);
primitives->push_back (1+6);
primitives->push_back (0+6);
// sides
primitives->push_back (0+6);
primitives->push_back (2+6);
primitives->push_back (5+6);
primitives->push_back (0+6);
primitives->push_back (5+6);
primitives->push_back (3+6);
primitives->push_back (4+6);
primitives->push_back (5+6);
primitives->push_back (2+6);
primitives->push_back (4+6);
primitives->push_back (2+6);
primitives->push_back (1+6);
geometry->addPrimitiveSet (primitives);
osg::Vec4Array *colours = new osg::Vec4Array;
for (int i=0; i<6; ++i)
colours->push_back (osg::Vec4f (1.0f, 0.0f, 0.0f, 1.0f));
for (int i=0; i<6; ++i)
colours->push_back (osg::Vec4f (0.8f, (i==2 || i==5) ? 0.6f : 0.4f, 0.0f, 1.0f));
geometry->setColorArray (colours, osg::Array::BIND_PER_VERTEX);
geometry->getOrCreateStateSet()->setMode (GL_LIGHTING, osg::StateAttribute::OFF);
osg::ref_ptr<osg::Geode> geode (new osg::Geode);
geode->addDrawable (geometry);
mBaseNode->addChild (geode);
}
CSVRender::CellArrow::CellArrow (osg::Group *cellNode, Direction direction,
const CSMWorld::CellCoordinates& coordinates)
: mDirection (direction), mParentNode (cellNode), mCoordinates (coordinates)
{
mBaseNode = new osg::PositionAttitudeTransform;
mBaseNode->setUserData (new CellArrowTag (this));
mParentNode->addChild (mBaseNode);
// 0x1 reserved for separating cull and update visitors
mBaseNode->setNodeMask (Element_CellArrow<<1);
adjustTransform();
buildShape();
}
CSVRender::CellArrow::~CellArrow()
{
mParentNode->removeChild (mBaseNode);
}
CSMWorld::CellCoordinates CSVRender::CellArrow::getCoordinates() const
{
return mCoordinates;
}
CSVRender::CellArrow::Direction CSVRender::CellArrow::getDirection() const
{
return mDirection;
}

@ -0,0 +1,72 @@
#ifndef OPENCS_VIEW_CELLARROW_H
#define OPENCS_VIEW_CELLARROW_H
#include "tagbase.hpp"
#include <osg/ref_ptr>
#include "../../model/world/cellcoordinates.hpp"
namespace osg
{
class PositionAttitudeTransform;
class Group;
}
namespace CSVRender
{
class CellArrow;
class CellArrowTag : public TagBase
{
CellArrow *mArrow;
public:
CellArrowTag (CellArrow *arrow);
CellArrow *getCellArrow() const;
};
class CellArrow
{
public:
enum Direction
{
Direction_North = 1,
Direction_West = 2,
Direction_South = 4,
Direction_East = 8
};
private:
// not implemented
CellArrow (const CellArrow&);
CellArrow& operator= (const CellArrow&);
Direction mDirection;
osg::Group* mParentNode;
osg::ref_ptr<osg::PositionAttitudeTransform> mBaseNode;
CSMWorld::CellCoordinates mCoordinates;
void adjustTransform();
void buildShape();
public:
CellArrow (osg::Group *cellNode, Direction direction,
const CSMWorld::CellCoordinates& coordinates);
~CellArrow();
CSMWorld::CellCoordinates getCoordinates() const;
Direction getDirection() const;
};
}
#endif

@ -1,7 +1,13 @@
#include "editmode.hpp"
#include "tagbase.hpp"
#include "worldspacewidget.hpp"
CSVRender::WorldspaceWidget& CSVRender::EditMode::getWorldspaceWidget()
{
return *mWorldspaceWidget;
}
CSVRender::EditMode::EditMode (WorldspaceWidget *worldspaceWidget, const QIcon& icon,
unsigned int mask, const QString& tooltip, QWidget *parent)
: ModeButton (icon, tooltip, parent), mWorldspaceWidget (worldspaceWidget), mMask (mask)
@ -15,4 +21,44 @@ unsigned int CSVRender::EditMode::getInteractionMask() const
void CSVRender::EditMode::activate (CSVWidget::SceneToolbar *toolbar)
{
mWorldspaceWidget->setInteractionMask (mMask);
mWorldspaceWidget->clearSelection (~mMask);
}
void CSVRender::EditMode::updateUserSetting (const QString& name, const QStringList& value)
{
}
void CSVRender::EditMode::setEditLock (bool locked)
{
}
void CSVRender::EditMode::primaryEditPressed (osg::ref_ptr<TagBase> tag) {}
void CSVRender::EditMode::secondaryEditPressed (osg::ref_ptr<TagBase> tag) {}
void CSVRender::EditMode::selectPressed (osg::ref_ptr<TagBase> tag) {}
bool CSVRender::EditMode::primaryEditStartDrag (osg::ref_ptr<TagBase> tag)
{
return false;
}
bool CSVRender::EditMode::secondaryEditStartDrag (osg::ref_ptr<TagBase> tag)
{
return false;
}
bool CSVRender::EditMode::selectStartDrag (osg::ref_ptr<TagBase> tag)
{
return false;
}
void CSVRender::EditMode::drag (int diffX, int diffY, double speedFactor) {}
void CSVRender::EditMode::dragCompleted() {}
void CSVRender::EditMode::dragAborted() {}
void CSVRender::EditMode::dragWheel (int diff, double speedFactor) {}

@ -1,11 +1,14 @@
#ifndef CSV_RENDER_EDITMODE_H
#define CSV_RENDER_EDITMODE_H
#include <osg/ref_ptr>
#include "../widget/modebutton.hpp"
namespace CSVRender
{
class WorldspaceWidget;
class TagBase;
class EditMode : public CSVWidget::ModeButton
{
@ -14,6 +17,10 @@ namespace CSVRender
WorldspaceWidget *mWorldspaceWidget;
unsigned int mMask;
protected:
WorldspaceWidget& getWorldspaceWidget();
public:
EditMode (WorldspaceWidget *worldspaceWidget, const QIcon& icon, unsigned int mask,
@ -22,6 +29,51 @@ namespace CSVRender
unsigned int getInteractionMask() const;
virtual void activate (CSVWidget::SceneToolbar *toolbar);
/// Default-implementation: Do nothing.
virtual void updateUserSetting (const QString& name, const QStringList& value);
/// Default-implementation: Ignored.
virtual void setEditLock (bool locked);
/// Default-implementation: Ignored.
virtual void primaryEditPressed (osg::ref_ptr<TagBase> tag);
/// Default-implementation: Ignored.
virtual void secondaryEditPressed (osg::ref_ptr<TagBase> tag);
/// Default-implementation: Ignored.
virtual void selectPressed (osg::ref_ptr<TagBase> tag);
/// Default-implementation: ignore and return false
///
/// \return Drag accepted?
virtual bool primaryEditStartDrag (osg::ref_ptr<TagBase> tag);
/// Default-implementation: ignore and return false
///
/// \return Drag accepted?
virtual bool secondaryEditStartDrag (osg::ref_ptr<TagBase> tag);
/// Default-implementation: ignore and return false
///
/// \return Drag accepted?
virtual bool selectStartDrag (osg::ref_ptr<TagBase> tag);
/// Default-implementation: ignored
virtual void drag (int diffX, int diffY, double speedFactor);
/// Default-implementation: ignored
virtual void dragCompleted();
/// Default-implementation: ignored
///
/// \note dragAborted will not be called, if the drag is aborted via changing
/// editing mode
virtual void dragAborted();
/// Default-implementation: ignored
virtual void dragWheel (int diff, double speedFactor);
};
}

@ -0,0 +1,56 @@
#include "instancemode.hpp"
#include "../../model/settings/usersettings.hpp"
#include "elements.hpp"
#include "object.hpp"
#include "worldspacewidget.hpp"
CSVRender::InstanceMode::InstanceMode (WorldspaceWidget *worldspaceWidget, QWidget *parent)
: EditMode (worldspaceWidget, QIcon (":placeholder"), Element_Reference, "Instance editing",
parent), mContextSelect (false)
{
}
void CSVRender::InstanceMode::activate (CSVWidget::SceneToolbar *toolbar)
{
EditMode::activate (toolbar);
mContextSelect = CSMSettings::UserSettings::instance().setting ("scene-input/context-select")=="true";
}
void CSVRender::InstanceMode::updateUserSetting (const QString& name, const QStringList& value)
{
if (name=="scene-input/context-select")
mContextSelect = value.at (0)=="true";
}
void CSVRender::InstanceMode::primaryEditPressed (osg::ref_ptr<TagBase> tag)
{
if (mContextSelect)
selectPressed (tag);
}
void CSVRender::InstanceMode::secondaryEditPressed (osg::ref_ptr<TagBase> tag)
{
if (mContextSelect)
selectPressed (tag);
}
void CSVRender::InstanceMode::selectPressed (osg::ref_ptr<TagBase> tag)
{
if (tag)
{
if (CSVRender::ObjectTag *objectTag = dynamic_cast<CSVRender::ObjectTag *> (tag.get()))
{
// hit an Object, toggle its selection state
CSVRender::Object* object = objectTag->mObject;
object->setSelected (!object->getSelected());
return;
}
}
getWorldspaceWidget().clearSelection (Element_Reference);
}

@ -0,0 +1,30 @@
#ifndef CSV_RENDER_INSTANCEMODE_H
#define CSV_RENDER_INSTANCEMODE_H
#include "editmode.hpp"
namespace CSVRender
{
class InstanceMode : public EditMode
{
Q_OBJECT
bool mContextSelect;
public:
InstanceMode (WorldspaceWidget *worldspaceWidget, QWidget *parent = 0);
virtual void activate (CSVWidget::SceneToolbar *toolbar);
virtual void updateUserSetting (const QString& name, const QStringList& value);
virtual void primaryEditPressed (osg::ref_ptr<TagBase> tag);
virtual void secondaryEditPressed (osg::ref_ptr<TagBase> tag);
virtual void selectPressed (osg::ref_ptr<TagBase> tag);
};
}
#endif

@ -38,6 +38,11 @@ namespace
}
CSVRender::ObjectTag::ObjectTag (Object* object)
: TagBase (Element_Reference), mObject (object)
{}
void CSVRender::Object::clear()
{
}
@ -124,7 +129,7 @@ CSVRender::Object::Object (CSMWorld::Data& data, osg::Group* parentNode,
mOutline = new osgFX::Scribe;
mOutline->addChild(mBaseNode);
mBaseNode->setUserData(new ObjectHolder(this));
mBaseNode->setUserData(new ObjectTag(this));
parentNode->addChild(mBaseNode);

@ -8,8 +8,9 @@
#include <osg/ref_ptr>
#include <osg/Referenced>
class QModelIndex;
#include "tagbase.hpp"
class QModelIndex;
namespace osg
{
@ -35,21 +36,19 @@ namespace CSMWorld
namespace CSVRender
{
class Object;
// An object to attach as user data to the osg::Node, allows us to get an Object back from a Node when we are doing a ray query
class ObjectHolder : public osg::Referenced
class ObjectTag : public TagBase
{
public:
ObjectHolder(Object* obj)
: mObject(obj)
{
}
public:
ObjectTag (Object* object);
Object* mObject;
Object* mObject;
};
class Object
{
const CSMWorld::Data& mData;

@ -1,8 +1,10 @@
#include "pagedworldspacewidget.hpp"
#include <memory>
#include <sstream>
#include <QMouseEvent>
#include <QApplication>
#include <osgGA/TrackballManipulator>
@ -21,20 +23,19 @@
bool CSVRender::PagedWorldspaceWidget::adjustCells()
{
bool modified = false;
bool wasEmpty = mCells.empty();
const CSMWorld::IdCollection<CSMWorld::Cell>& cells = mDocument.getData().getCells();
{
// remove (or name/region modified)
// remove/update
std::map<CSMWorld::CellCoordinates, Cell *>::iterator iter (mCells.begin());
while (iter!=mCells.end())
{
int index = cells.searchId (iter->first.getId (mWorldspace));
if (!mSelection.has (iter->first) || index==-1 ||
cells.getRecord (index).mState==CSMWorld::RecordBase::State_Deleted)
if (!mSelection.has (iter->first))
{
// remove
delete iter->second;
mCells.erase (iter++);
@ -42,12 +43,33 @@ bool CSVRender::PagedWorldspaceWidget::adjustCells()
}
else
{
// check if name or region field has changed
// FIXME: config setting
//std::string name = cells.getRecord(index).get().mName;
//std::string region = cells.getRecord(index).get().mRegion;
// update
int index = cells.searchId (iter->first.getId (mWorldspace));
bool deleted = index==-1 ||
cells.getRecord (index).mState==CSMWorld::RecordBase::State_Deleted;
if (deleted!=iter->second->isDeleted())
{
modified = true;
std::auto_ptr<Cell> cell (new Cell (mDocument.getData(), mRootNode,
iter->first.getId (mWorldspace), deleted));
delete iter->second;
iter->second = cell.release();
}
else if (!deleted)
{
// delete state has not changed -> just update
// cell marker update goes here
// TODO check if name or region field has changed (cell marker)
// FIXME: config setting
//std::string name = cells.getRecord(index).get().mName;
//std::string region = cells.getRecord(index).get().mRegion;
modified = true;
}
++iter;
}
@ -58,20 +80,43 @@ bool CSVRender::PagedWorldspaceWidget::adjustCells()
for (CSMWorld::CellSelection::Iterator iter (mSelection.begin()); iter!=mSelection.end();
++iter)
{
int index = cells.searchId (iter->getId (mWorldspace));
if (index > 0 && cells.getRecord (index).mState!=CSMWorld::RecordBase::State_Deleted &&
mCells.find (*iter)==mCells.end())
if (mCells.find (*iter)==mCells.end())
{
Cell *cell = new Cell (mDocument.getData(), mRootNode,
iter->getId (mWorldspace));
mCells.insert (std::make_pair (*iter, cell));
addCellToScene (*iter);
modified = true;
}
}
if (modified)
{
for (std::map<CSMWorld::CellCoordinates, Cell *>::const_iterator iter (mCells.begin());
iter!=mCells.end(); ++iter)
{
int mask = 0;
for (int i=CellArrow::Direction_North; i<=CellArrow::Direction_East; i *= 2)
{
CSMWorld::CellCoordinates coordinates (iter->second->getCoordinates());
switch (i)
{
case CellArrow::Direction_North: coordinates = coordinates.move (0, 1); break;
case CellArrow::Direction_West: coordinates = coordinates.move (-1, 0); break;
case CellArrow::Direction_South: coordinates = coordinates.move (0, -1); break;
case CellArrow::Direction_East: coordinates = coordinates.move (1, 0); break;
}
if (!mSelection.has (coordinates))
mask |= i;
}
iter->second->setCellArrows (mask);
}
}
/// \todo do not overwrite manipulator object
/// \todo move code to useViewHint function
if (modified && wasEmpty)
mView->setCameraManipulator(new osgGA::TrackballManipulator);
return modified;
@ -105,6 +150,76 @@ void CSVRender::PagedWorldspaceWidget::addEditModeSelectorButtons (
"terrain-move");
}
void CSVRender::PagedWorldspaceWidget::handleMouseClick (osg::ref_ptr<TagBase> tag, const std::string& button, bool shift)
{
if (tag && tag->getElement()==Element_CellArrow)
{
if (button=="p-edit" || button=="s-edit")
{
if (CellArrowTag *cellArrowTag =
dynamic_cast<CSVRender::CellArrowTag *> (tag.get()))
{
CellArrow *arrow = cellArrowTag->getCellArrow();
CSMWorld::CellCoordinates coordinates = arrow->getCoordinates();
CellArrow::Direction direction = arrow->getDirection();
int x = 0;
int y = 0;
switch (direction)
{
case CellArrow::Direction_North: y = 1; break;
case CellArrow::Direction_West: x = -1; break;
case CellArrow::Direction_South: y = -1; break;
case CellArrow::Direction_East: x = 1; break;
}
bool modified = false;
if (shift)
{
if (button=="p-edit")
addCellSelection (x, y);
else
moveCellSelection (x, y);
modified = true;
}
else
{
CSMWorld::CellCoordinates newCoordinates = coordinates.move (x, y);
if (mCells.find (newCoordinates)==mCells.end())
{
addCellToScene (newCoordinates);
mSelection.add (newCoordinates);
modified = true;
}
if (button=="s-edit")
{
if (mCells.find (coordinates)!=mCells.end())
{
removeCellFromScene (coordinates);
mSelection.remove (coordinates);
modified = true;
}
}
}
if (modified)
adjustCells();
return;
}
}
}
WorldspaceWidget::handleMouseClick (tag, button, shift);
}
void CSVRender::PagedWorldspaceWidget::referenceableDataChanged (const QModelIndex& topLeft,
const QModelIndex& bottomRight)
{
@ -184,6 +299,72 @@ std::string CSVRender::PagedWorldspaceWidget::getStartupInstruction()
return stream.str();
}
void CSVRender::PagedWorldspaceWidget::addCellToScene (
const CSMWorld::CellCoordinates& coordinates)
{
const CSMWorld::IdCollection<CSMWorld::Cell>& cells = mDocument.getData().getCells();
int index = cells.searchId (coordinates.getId (mWorldspace));
bool deleted = index==-1 ||
cells.getRecord (index).mState==CSMWorld::RecordBase::State_Deleted;
Cell *cell = new Cell (mDocument.getData(), mRootNode, coordinates.getId (mWorldspace),
deleted);
mCells.insert (std::make_pair (coordinates, cell));
}
void CSVRender::PagedWorldspaceWidget::removeCellFromScene (
const CSMWorld::CellCoordinates& coordinates)
{
std::map<CSMWorld::CellCoordinates, Cell *>::iterator iter = mCells.find (coordinates);
if (iter!=mCells.end())
{
delete iter->second;
mCells.erase (iter);
}
}
void CSVRender::PagedWorldspaceWidget::addCellSelection (int x, int y)
{
CSMWorld::CellSelection newSelection = mSelection;
newSelection.move (x, y);
for (CSMWorld::CellSelection::Iterator iter (newSelection.begin()); iter!=newSelection.end();
++iter)
{
if (mCells.find (*iter)==mCells.end())
{
addCellToScene (*iter);
mSelection.add (*iter);
}
}
}
void CSVRender::PagedWorldspaceWidget::moveCellSelection (int x, int y)
{
CSMWorld::CellSelection newSelection = mSelection;
newSelection.move (x, y);
for (CSMWorld::CellSelection::Iterator iter (mSelection.begin()); iter!=mSelection.end();
++iter)
{
if (!newSelection.has (*iter))
removeCellFromScene (*iter);
}
for (CSMWorld::CellSelection::Iterator iter (newSelection.begin()); iter!=newSelection.end();
++iter)
{
if (!mSelection.has (*iter))
addCellToScene (*iter);
}
mSelection = newSelection;
}
CSVRender::PagedWorldspaceWidget::PagedWorldspaceWidget (QWidget* parent, CSMDoc::Document& document)
: WorldspaceWidget (document, parent), mDocument (document), mWorldspace ("std::default"),
mControlElements(NULL), mDisplayCellCoord(true)
@ -314,6 +495,15 @@ unsigned int CSVRender::PagedWorldspaceWidget::getVisibilityMask() const
return WorldspaceWidget::getVisibilityMask() | mControlElements->getSelection();
}
void CSVRender::PagedWorldspaceWidget::clearSelection (int elementMask)
{
for (std::map<CSMWorld::CellCoordinates, Cell *>::iterator iter = mCells.begin();
iter!=mCells.end(); ++iter)
iter->second->setSelection (elementMask, Cell::Selection_Clear);
flagAsModified();
}
CSVWidget::SceneToolToggle *CSVRender::PagedWorldspaceWidget::makeControlVisibilitySelector (
CSVWidget::SceneToolbar *parent)
{

@ -53,6 +53,20 @@ namespace CSVRender
virtual std::string getStartupInstruction();
/// \note Does not update the view or any cell marker
void addCellToScene (const CSMWorld::CellCoordinates& coordinates);
/// \note Does not update the view or any cell marker
///
/// \note Calling this function for a cell that is not in the selection is a no-op.
void removeCellFromScene (const CSMWorld::CellCoordinates& coordinates);
/// \note Does not update the view or any cell marker
void addCellSelection (int x, int y);
/// \note Does not update the view or any cell marker
void moveCellSelection (int x, int y);
public:
PagedWorldspaceWidget (QWidget *parent, CSMDoc::Document& document);
@ -79,12 +93,17 @@ namespace CSVRender
virtual unsigned int getVisibilityMask() const;
/// \param elementMask Elements to be affected by the clear operation
virtual void clearSelection (int elementMask);
protected:
virtual void addVisibilitySelectorButtons (CSVWidget::SceneToolToggle2 *tool);
virtual void addEditModeSelectorButtons (CSVWidget::SceneToolMode *tool);
virtual void handleMouseClick (osg::ref_ptr<TagBase> tag, const std::string& button, bool shift);
signals:
void cellSelectionChanged (const CSMWorld::CellSelection& selection);

@ -110,6 +110,8 @@ bool RenderWidget::eventFilter(QObject* obj, QEvent* event)
keyPressEvent(static_cast<QKeyEvent*>(event));
if (event->type() == QEvent::KeyRelease)
keyReleaseEvent(static_cast<QKeyEvent*>(event));
if (event->type() == QEvent::Wheel)
wheelEvent(static_cast<QWheelEvent *>(event));
// Always pass the event on to GLWidget, i.e. to OSG event queue
return QObject::eventFilter(obj, event);

@ -0,0 +1,9 @@
#include "tagbase.hpp"
CSVRender::TagBase::TagBase (Elements element) : mElement (element) {}
CSVRender::Elements CSVRender::TagBase::getElement() const
{
return mElement;
}

@ -0,0 +1,22 @@
#ifndef OPENCS_VIEW_TAGBASE_H
#define OPENCS_VIEW_TAGBASE_H
#include <osg/Referenced>
#include "elements.hpp"
namespace CSVRender
{
class TagBase : public osg::Referenced
{
Elements mElement;
public:
TagBase (Elements element);
Elements getElement() const;
};
}
#endif

@ -102,6 +102,12 @@ bool CSVRender::UnpagedWorldspaceWidget::handleDrop (const std::vector<CSMWorld:
return true;
}
void CSVRender::UnpagedWorldspaceWidget::clearSelection (int elementMask)
{
mCell->setSelection (elementMask, Cell::Selection_Clear);
flagAsModified();
}
void CSVRender::UnpagedWorldspaceWidget::referenceableDataChanged (const QModelIndex& topLeft,
const QModelIndex& bottomRight)
{

@ -43,6 +43,9 @@ namespace CSVRender
virtual bool handleDrop (const std::vector<CSMWorld::UniversalId>& data,
DropType type);
/// \param elementMask Elements to be affected by the clear operation
virtual void clearSelection (int elementMask);
private:
virtual void referenceableDataChanged (const QModelIndex& topLeft,

@ -9,6 +9,7 @@
#include <QDropEvent>
#include <QMouseEvent>
#include <QKeyEvent>
#include <QApplication>
#include <osgGA/TrackballManipulator>
#include <osgGA/FirstPersonManipulator>
@ -18,6 +19,8 @@
#include "../../model/world/universalid.hpp"
#include "../../model/world/idtable.hpp"
#include "../../model/settings/usersettings.hpp"
#include "../widget/scenetoolmode.hpp"
#include "../widget/scenetooltoggle2.hpp"
#include "../widget/scenetoolrun.hpp"
@ -25,10 +28,22 @@
#include "object.hpp"
#include "elements.hpp"
#include "editmode.hpp"
#include "instancemode.hpp"
namespace
{
static const char * const sMappingSettings[] =
{
"p-navi", "s-navi",
"p-edit", "s-edit",
"select",
0
};
}
CSVRender::WorldspaceWidget::WorldspaceWidget (CSMDoc::Document& document, QWidget* parent)
: SceneWidget (document.getData().getResourceSystem(), parent), mSceneElements(0), mRun(0), mDocument(document),
mInteractionMask (0)
mInteractionMask (0), mEditMode (0), mLocked (false), mDragging (false)
{
setAcceptDrops(true);
@ -59,6 +74,17 @@ CSVRender::WorldspaceWidget::WorldspaceWidget (CSMDoc::Document& document, QWidg
this, SLOT (debugProfileDataChanged (const QModelIndex&, const QModelIndex&)));
connect (debugProfiles, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)),
this, SLOT (debugProfileAboutToBeRemoved (const QModelIndex&, int, int)));
for (int i=0; sMappingSettings[i]; ++i)
{
QString key ("scene-input/");
key += sMappingSettings[i];
storeMappingSetting (key, CSMSettings::UserSettings::instance().settingValue (key));
}
mDragFactor = CSMSettings::UserSettings::instance().settingValue ("scene-input/drag-factor").toDouble();
mDragWheelFactor = CSMSettings::UserSettings::instance().settingValue ("scene-input/drag-wheel-factor").toDouble();
mDragShiftFactor = CSMSettings::UserSettings::instance().settingValue ("scene-input/drag-shift-factor").toDouble();
}
CSVRender::WorldspaceWidget::~WorldspaceWidget ()
@ -178,11 +204,14 @@ CSVWidget::SceneToolRun *CSVRender::WorldspaceWidget::makeRunTool (
CSVWidget::SceneToolMode *CSVRender::WorldspaceWidget::makeEditModeSelector (
CSVWidget::SceneToolbar *parent)
{
CSVWidget::SceneToolMode *tool = new CSVWidget::SceneToolMode (parent, "Edit Mode");
mEditMode = new CSVWidget::SceneToolMode (parent, "Edit Mode");
addEditModeSelectorButtons (tool);
addEditModeSelectorButtons (mEditMode);
return tool;
connect (mEditMode, SIGNAL (modeChanged (const std::string&)),
this, SLOT (editModeChanged (const std::string&)));
return mEditMode;
}
CSVRender::WorldspaceWidget::DropType CSVRender::WorldspaceWidget::getDropType (
@ -254,6 +283,26 @@ unsigned int CSVRender::WorldspaceWidget::getInteractionMask() const
return mInteractionMask & getVisibilityMask();
}
void CSVRender::WorldspaceWidget::updateUserSetting (const QString& name, const QStringList& value)
{
if (!value.isEmpty() && storeMappingSetting (name, value.first()))
return;
if (name=="scene-input/drag-factor")
mDragFactor = value.at (0).toDouble();
else if (name=="scene-input/drag-wheel-factor")
mDragWheelFactor = value.at (0).toDouble();
else if (name=="scene-input/drag-shift-factor")
mDragShiftFactor = value.at (0).toDouble();
else
dynamic_cast<CSVRender::EditMode&> (*mEditMode->getCurrent()).updateUserSetting (name, value);
}
void CSVRender::WorldspaceWidget::setEditLock (bool locked)
{
dynamic_cast<CSVRender::EditMode&> (*mEditMode->getCurrent()).setEditLock (locked);
}
void CSVRender::WorldspaceWidget::addVisibilitySelectorButtons (
CSVWidget::SceneToolToggle2 *tool)
{
@ -265,9 +314,7 @@ void CSVRender::WorldspaceWidget::addVisibilitySelectorButtons (
void CSVRender::WorldspaceWidget::addEditModeSelectorButtons (CSVWidget::SceneToolMode *tool)
{
/// \todo replace EditMode with suitable subclasses
tool->addButton (
new EditMode (this, QIcon (":placeholder"), Element_Reference, "Instance editing"),
"object");
tool->addButton (new InstanceMode (this, tool), "object");
tool->addButton (
new EditMode (this, QIcon (":placeholder"), Element_Pathgrid, "Pathgrid editing"),
"pathgrid");
@ -288,6 +335,95 @@ void CSVRender::WorldspaceWidget::dragMoveEvent(QDragMoveEvent *event)
event->accept();
}
bool CSVRender::WorldspaceWidget::storeMappingSetting (const QString& key, const QString& value)
{
const QString prefix = "scene-input/";
if (key.startsWith (prefix))
{
QString key2 (key.mid (prefix.length()));
for (int i=0; sMappingSettings[i]; ++i)
if (key2==sMappingSettings[i])
{
Qt::MouseButton button = Qt::NoButton;
if (value.endsWith ("Left Mouse-Button"))
button = Qt::LeftButton;
else if (value.endsWith ("Right Mouse-Button"))
button = Qt::RightButton;
else if (value.endsWith ("Middle Mouse-Button"))
button = Qt::MiddleButton;
else
return false;
bool ctrl = value.startsWith ("Ctrl-");
mButtonMapping[std::make_pair (button, ctrl)] = sMappingSettings[i];
return true;
}
}
return false;
}
osg::ref_ptr<CSVRender::TagBase> CSVRender::WorldspaceWidget::mousePick (QMouseEvent *event)
{
// (0,0) is considered the lower left corner of an OpenGL window
int x = event->x();
int y = height() - event->y();
osg::ref_ptr<osgUtil::LineSegmentIntersector> intersector (new osgUtil::LineSegmentIntersector(osgUtil::Intersector::WINDOW, x, y));
intersector->setIntersectionLimit(osgUtil::LineSegmentIntersector::NO_LIMIT);
osgUtil::IntersectionVisitor visitor(intersector);
visitor.setTraversalMask(getInteractionMask() << 1);
mView->getCamera()->accept(visitor);
for (osgUtil::LineSegmentIntersector::Intersections::iterator it = intersector->getIntersections().begin();
it != intersector->getIntersections().end(); ++it)
{
osgUtil::LineSegmentIntersector::Intersection intersection = *it;
// reject back-facing polygons
osg::Vec3f normal = intersection.getWorldIntersectNormal();
normal = osg::Matrix::transform3x3(normal, mView->getCamera()->getViewMatrix());
if (normal.z() < 0)
continue;
for (std::vector<osg::Node*>::iterator it = intersection.nodePath.begin(); it != intersection.nodePath.end(); ++it)
{
osg::Node* node = *it;
if (osg::ref_ptr<CSVRender::TagBase> tag = dynamic_cast<CSVRender::TagBase *>(node->getUserData()))
return tag;
}
// ignoring terrain for now
// must be terrain, report coordinates
// std::cout << "Terrain hit at " << intersection.getWorldIntersectPoint().x() << " " << intersection.getWorldIntersectPoint().y() << std::endl;
// return;
}
return osg::ref_ptr<CSVRender::TagBase>();
}
std::string CSVRender::WorldspaceWidget::mapButton (QMouseEvent *event)
{
std::pair<Qt::MouseButton, bool> phyiscal (
event->button(), event->modifiers() & Qt::ControlModifier);
std::map<std::pair<Qt::MouseButton, bool>, std::string>::const_iterator iter =
mButtonMapping.find (phyiscal);
if (iter!=mButtonMapping.end())
return iter->second;
return "";
}
void CSVRender::WorldspaceWidget::dropEvent (QDropEvent* event)
{
const CSMWorld::TableMimeData* mime = dynamic_cast<const CSMWorld::TableMimeData*> (event->mimeData());
@ -352,6 +488,12 @@ void CSVRender::WorldspaceWidget::debugProfileAboutToBeRemoved (const QModelInde
}
}
void CSVRender::WorldspaceWidget::editModeChanged (const std::string& id)
{
dynamic_cast<CSVRender::EditMode&> (*mEditMode->getCurrent()).setEditLock (mLocked);
mDragging = false;
}
void CSVRender::WorldspaceWidget::elementSelectionChanged()
{
setVisibilityMask (getVisibilityMask());
@ -365,74 +507,97 @@ void CSVRender::WorldspaceWidget::updateOverlay()
void CSVRender::WorldspaceWidget::mouseMoveEvent (QMouseEvent *event)
{
if(event->buttons() & Qt::RightButton)
if (!mDragging)
{
//mMouse->mouseMoveEvent(event);
}
RenderWidget::mouseMoveEvent(event);
}
void CSVRender::WorldspaceWidget::mousePressEvent (QMouseEvent *event)
{
if (event->button() != Qt::RightButton)
return;
if (mDragMode=="p-navi" || mDragMode=="s-navi")
{
// (0,0) is considered the lower left corner of an OpenGL window
int x = event->x();
int y = height() - event->y();
}
else if (mDragMode=="p-edit" || mDragMode=="s-edit" || mDragMode=="select")
{
osg::ref_ptr<TagBase> tag = mousePick (event);
osg::ref_ptr<osgUtil::LineSegmentIntersector> intersector (new osgUtil::LineSegmentIntersector(osgUtil::Intersector::WINDOW, x, y));
EditMode& editMode = dynamic_cast<CSVRender::EditMode&> (*mEditMode->getCurrent());
intersector->setIntersectionLimit(osgUtil::LineSegmentIntersector::NO_LIMIT);
osgUtil::IntersectionVisitor visitor(intersector);
if (mDragMode=="p-edit")
mDragging = editMode.primaryEditStartDrag (tag);
else if (mDragMode=="s-edit")
mDragging = editMode.secondaryEditStartDrag (tag);
else if (mDragMode=="select")
mDragging = editMode.selectStartDrag (tag);
visitor.setTraversalMask(getInteractionMask() << 1);
if (mDragging)
{
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
mDragX = event->localPos().x();
mDragY = height() - event->localPos().y();
#else
mDragX = event->posF().x();
mDragY = height() - event->posF().y();
#endif
}
}
}
else
{
int diffX = event->x() - mDragX;
int diffY = (height() - event->y()) - mDragY;
mView->getCamera()->accept(visitor);
mDragX = event->x();
mDragY = height() - event->y();
for (osgUtil::LineSegmentIntersector::Intersections::iterator it = intersector->getIntersections().begin();
it != intersector->getIntersections().end(); ++it)
{
osgUtil::LineSegmentIntersector::Intersection intersection = *it;
double factor = mDragFactor;
// reject back-facing polygons
osg::Vec3f normal = intersection.getWorldIntersectNormal();
normal = osg::Matrix::transform3x3(normal, mView->getCamera()->getViewMatrix());
if (normal.z() < 0)
continue;
if (event->modifiers() & Qt::ShiftModifier)
factor *= mDragShiftFactor;
for (std::vector<osg::Node*>::iterator it = intersection.nodePath.begin(); it != intersection.nodePath.end(); ++it)
{
osg::Node* node = *it;
if (CSVRender::ObjectHolder* holder = dynamic_cast<CSVRender::ObjectHolder*>(node->getUserData()))
{
// hit an Object, toggle its selection state
CSVRender::Object* obj = holder->mObject;
obj->setSelected(!obj->getSelected());
return;
}
}
EditMode& editMode = dynamic_cast<CSVRender::EditMode&> (*mEditMode->getCurrent());
// must be terrain, report coordinates
std::cout << "Terrain hit at " << intersection.getWorldIntersectPoint().x() << " " << intersection.getWorldIntersectPoint().y() << std::endl;
return;
editMode.drag (diffX, diffY, factor);
}
}
void CSVRender::WorldspaceWidget::mousePressEvent (QMouseEvent *event)
{
std::string button = mapButton (event);
if (!mDragging)
mDragMode = button;
}
void CSVRender::WorldspaceWidget::mouseReleaseEvent (QMouseEvent *event)
{
if(event->button() == Qt::RightButton)
std::string button = mapButton (event);
if (mDragging)
{
if (mDragMode=="p-navi" || mDragMode=="s-navi")
{
}
else if (mDragMode=="p-edit" || mDragMode=="s-edit" || mDragMode=="select")
{
EditMode& editMode = dynamic_cast<CSVRender::EditMode&> (*mEditMode->getCurrent());
editMode.dragCompleted();
mDragging = false;
}
}
else
{
/*
if(!getViewport())
if (button=="p-navi" || button=="s-navi")
{
}
else if (button=="p-edit" || button=="s-edit" || button=="select")
{
SceneWidget::mouseReleaseEvent(event);
return;
osg::ref_ptr<TagBase> tag = mousePick (event);
handleMouseClick (tag, button, event->modifiers() & Qt::ShiftModifier);
}
*/
//mMouse->mouseReleaseEvent(event);
}
RenderWidget::mouseReleaseEvent(event);
mDragMode.clear();
}
void CSVRender::WorldspaceWidget::mouseDoubleClickEvent (QMouseEvent *event)
@ -441,21 +606,47 @@ void CSVRender::WorldspaceWidget::mouseDoubleClickEvent (QMouseEvent *event)
{
//mMouse->mouseDoubleClickEvent(event);
}
//SceneWidget::mouseDoubleClickEvent(event);
}
void CSVRender::WorldspaceWidget::wheelEvent (QWheelEvent *event)
{
//if(!mMouse->wheelEvent(event))
RenderWidget::wheelEvent(event);
if (mDragging)
{
double factor = mDragWheelFactor;
if (event->modifiers() & Qt::ShiftModifier)
factor *= mDragShiftFactor;
EditMode& editMode = dynamic_cast<CSVRender::EditMode&> (*mEditMode->getCurrent());
editMode.dragWheel (event->delta(), factor);
}
}
void CSVRender::WorldspaceWidget::keyPressEvent (QKeyEvent *event)
{
if(event->key() == Qt::Key_Escape)
{
//mMouse->cancelDrag();
if (mDragging)
{
EditMode& editMode = dynamic_cast<CSVRender::EditMode&> (*mEditMode->getCurrent());
editMode.dragAborted();
mDragging = false;
}
}
else
RenderWidget::keyPressEvent(event);
}
void CSVRender::WorldspaceWidget::handleMouseClick (osg::ref_ptr<TagBase> tag, const std::string& button, bool shift)
{
EditMode& editMode = dynamic_cast<CSVRender::EditMode&> (*mEditMode->getCurrent());
if (button=="p-edit")
editMode.primaryEditPressed (tag);
else if (button=="s-edit")
editMode.secondaryEditPressed (tag);
else if (button=="select")
editMode.selectPressed (tag);
}

@ -1,12 +1,15 @@
#ifndef OPENCS_VIEW_WORLDSPACEWIDGET_H
#define OPENCS_VIEW_WORLDSPACEWIDGET_H
#include <map>
#include <boost/shared_ptr.hpp>
#include "scenewidget.hpp"
#include "../../model/doc/document.hpp"
#include "../../model/world/tablemimedata.hpp"
#include <apps/opencs/model/doc/document.hpp>
#include <apps/opencs/model/world/tablemimedata.hpp>
#include "scenewidget.hpp"
#include "elements.hpp"
namespace CSMWorld
{
@ -23,6 +26,9 @@ namespace CSVWidget
namespace CSVRender
{
class TagBase;
class CellArrow;
class WorldspaceWidget : public SceneWidget
{
Q_OBJECT
@ -31,6 +37,16 @@ namespace CSVRender
CSVWidget::SceneToolRun *mRun;
CSMDoc::Document& mDocument;
unsigned int mInteractionMask;
std::map<std::pair<Qt::MouseButton, bool>, std::string> mButtonMapping;
CSVWidget::SceneToolMode *mEditMode;
bool mLocked;
std::string mDragMode;
bool mDragging;
int mDragX;
int mDragY;
double mDragFactor;
double mDragWheelFactor;
double mDragShiftFactor;
public:
@ -93,14 +109,21 @@ namespace CSVRender
/// marked for interaction.
unsigned int getInteractionMask() const;
virtual void updateUserSetting (const QString& name, const QStringList& value);
virtual void setEditLock (bool locked);
CSMDoc::Document& getDocument();
/// \param elementMask Elements to be affected by the clear operation
virtual void clearSelection (int elementMask) = 0;
protected:
virtual void addVisibilitySelectorButtons (CSVWidget::SceneToolToggle2 *tool);
virtual void addEditModeSelectorButtons (CSVWidget::SceneToolMode *tool);
CSMDoc::Document& getDocument();
virtual void updateOverlay();
virtual void mouseMoveEvent (QMouseEvent *event);
@ -110,6 +133,9 @@ namespace CSVRender
virtual void wheelEvent (QWheelEvent *event);
virtual void keyPressEvent (QKeyEvent *event);
virtual void handleMouseClick (osg::ref_ptr<TagBase> tag, const std::string& button,
bool shift);
private:
void dragEnterEvent(QDragEnterEvent *event);
@ -118,6 +144,13 @@ namespace CSVRender
void dragMoveEvent(QDragMoveEvent *event);
/// \return Is \a key a button mapping setting? (ignored otherwise)
bool storeMappingSetting (const QString& key, const QString& value);
osg::ref_ptr<TagBase> mousePick (QMouseEvent *event);
std::string mapButton (QMouseEvent *event);
virtual std::string getStartupInstruction() = 0;
private slots:
@ -144,6 +177,7 @@ namespace CSVRender
void debugProfileAboutToBeRemoved (const QModelIndex& parent, int start, int end);
void editModeChanged (const std::string& id);
protected slots:

@ -71,6 +71,11 @@ void CSVWidget::SceneToolMode::addButton (ModeButton *button, const std::string&
}
}
CSVWidget::ModeButton *CSVWidget::SceneToolMode::getCurrent()
{
return mCurrent;
}
void CSVWidget::SceneToolMode::selected()
{
std::map<ModeButton *, std::string>::const_iterator iter =

@ -41,6 +41,9 @@ namespace CSVWidget
/// The ownership of \a button is transferred to *this.
void addButton (ModeButton *button, const std::string& id);
/// Will return a 0-pointer only if the mode does not have any buttons yet.
ModeButton *getCurrent();
signals:
void modeChanged (const std::string& id);

@ -47,6 +47,16 @@ std::string CSVWorld::GenericCreator::getId() const
return mId->text().toUtf8().constData();
}
std::string CSVWorld::GenericCreator::getIdValidatorResult() const
{
std::string errors;
if (!mId->hasAcceptableInput())
errors = mValidator->getError();
return errors;
}
void CSVWorld::GenericCreator::configureCreateCommand (CSMWorld::CreateCommand& command) const {}
void CSVWorld::GenericCreator::pushCommand (std::auto_ptr<CSMWorld::CreateCommand> command,

@ -60,6 +60,8 @@ namespace CSVWorld
virtual std::string getId() const;
virtual std::string getIdValidatorResult() const;
/// Allow subclasses to add additional data to \a command.
virtual void configureCreateCommand (CSMWorld::CreateCommand& command) const;

@ -15,6 +15,7 @@
#include "../render/pagedworldspacewidget.hpp"
#include "../render/unpagedworldspacewidget.hpp"
#include "../render/editmode.hpp"
#include "../widget/scenetoolbar.hpp"
#include "../widget/scenetoolmode.hpp"
@ -121,15 +122,14 @@ CSVWidget::SceneToolbar* CSVWorld::SceneSubView::makeToolbar (CSVRender::Worldsp
CSVWidget::SceneToolRun *runTool = widget->makeRunTool (toolbar);
toolbar->addTool (runTool);
CSVWidget::SceneToolMode *editModeTool = widget->makeEditModeSelector (toolbar);
toolbar->addTool (editModeTool);
toolbar->addTool (widget->makeEditModeSelector (toolbar));
return toolbar;
}
void CSVWorld::SceneSubView::setEditLock (bool locked)
{
mScene->setEditLock (locked);
}
void CSVWorld::SceneSubView::setStatusBar (bool show)
@ -147,6 +147,12 @@ std::string CSVWorld::SceneSubView::getTitle() const
return mTitle;
}
void CSVWorld::SceneSubView::updateUserSetting (const QString& name, const QStringList& value)
{
mScene->updateUserSetting (name, value);
CSVDoc::SubView::updateUserSetting (name, value);
}
void CSVWorld::SceneSubView::cellSelectionChanged (const CSMWorld::UniversalId& id)
{
setUniversalId(id);

@ -27,6 +27,7 @@ namespace CSVRender
namespace CSVWidget
{
class SceneToolbar;
class SceneToolMode;
}
namespace CSVWorld
@ -58,6 +59,8 @@ namespace CSVWorld
virtual std::string getTitle() const;
virtual void updateUserSetting (const QString& name, const QStringList& value);
private:
void makeConnections(CSVRender::PagedWorldspaceWidget* widget);

@ -198,26 +198,41 @@ void CSVWorld::ScriptSubView::useHint (const std::string& hint)
if (hint.empty())
return;
if (hint[0]=='l')
{
std::istringstream stream (hint.c_str()+1);
char ignore;
int line;
int column;
if (stream >> ignore >> line >> column)
unsigned line = 0, column = 0;
char c;
std::istringstream stream (hint.c_str()+1);
switch(hint[0]){
case 'R':
case 'r':
{
QTextCursor cursor = mEditor->textCursor();
cursor.movePosition (QTextCursor::Start);
if (cursor.movePosition (QTextCursor::Down, QTextCursor::MoveAnchor, line))
cursor.movePosition (QTextCursor::Right, QTextCursor::MoveAnchor, column);
mEditor->setFocus();
mEditor->setTextCursor (cursor);
QModelIndex index = mModel->getModelIndex (getUniversalId().getId(), mColumn);
QString source = mModel->data (index).toString();
unsigned pos, dummy;
if (!(stream >> c >> dummy >> pos) )
return;
for (unsigned i = 0; i <= pos; ++i){
if (source[i] == '\n'){
++line;
column = i+1;
}
}
column = pos - column;
break;
}
case 'l':
if (!(stream >> c >> line >> column))
return;
}
QTextCursor cursor = mEditor->textCursor();
cursor.movePosition (QTextCursor::Start);
if (cursor.movePosition (QTextCursor::Down, QTextCursor::MoveAnchor, line))
cursor.movePosition (QTextCursor::Right, QTextCursor::MoveAnchor, column);
mEditor->setFocus();
mEditor->setTextCursor (cursor);
}
void CSVWorld::ScriptSubView::textChanged()

@ -0,0 +1,20 @@
#include "startscriptcreator.hpp"
CSVWorld::StartScriptCreator::StartScriptCreator(CSMWorld::Data &data, QUndoStack &undoStack, const CSMWorld::UniversalId &id, bool relaxedIdRules):
GenericCreator (data, undoStack, id, true)
{}
std::string CSVWorld::StartScriptCreator::getErrors() const
{
std::string errors;
errors = getIdValidatorResult();
if (errors.length() > 0)
return errors;
else if (getData().getScripts().searchId(getId()) == -1)
errors = "Script ID not found";
else if (getData().getStartScripts().searchId(getId()) > -1 )
errors = "Script with this ID already registered as Start Script";
return errors;
}

@ -0,0 +1,25 @@
#ifndef STARTSCRIPTCREATOR_HPP
#define STARTSCRIPTCREATOR_HPP
#include "genericcreator.hpp"
namespace CSVWorld {
class StartScriptCreator : public GenericCreator
{
Q_OBJECT
public:
StartScriptCreator(CSMWorld::Data& data, QUndoStack& undoStack,
const CSMWorld::UniversalId& id, bool relaxedIdRules = false);
virtual std::string getErrors() const;
///< Return formatted error descriptions for the current state of the creator. if an empty
/// string is returned, there is no error.
};
}
#endif // STARTSCRIPTCREATOR_HPP

@ -10,6 +10,7 @@
#include "cellcreator.hpp"
#include "referenceablecreator.hpp"
#include "referencecreator.hpp"
#include "startscriptcreator.hpp"
#include "scenesubview.hpp"
#include "dialoguecreator.hpp"
#include "infocreator.hpp"
@ -42,7 +43,6 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager)
CSMWorld::UniversalId::Type_BodyParts,
CSMWorld::UniversalId::Type_SoundGens,
CSMWorld::UniversalId::Type_Pathgrids,
CSMWorld::UniversalId::Type_StartScripts,
CSMWorld::UniversalId::Type_None // end marker
};
@ -51,6 +51,9 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager)
manager.add (sTableTypes[i],
new CSVDoc::SubViewFactoryWithCreator<TableSubView, CreatorFactory<GenericCreator> >);
manager.add (CSMWorld::UniversalId::Type_StartScripts,
new CSVDoc::SubViewFactoryWithCreator<TableSubView, CreatorFactory<StartScriptCreator> >);
manager.add (CSMWorld::UniversalId::Type_Cells,
new CSVDoc::SubViewFactoryWithCreator<TableSubView, CreatorFactory<CellCreator> >);
@ -123,7 +126,6 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager)
CSMWorld::UniversalId::Type_BodyPart,
CSMWorld::UniversalId::Type_SoundGen,
CSMWorld::UniversalId::Type_Pathgrid,
CSMWorld::UniversalId::Type_StartScript,
CSMWorld::UniversalId::Type_None // end marker
};
@ -133,6 +135,10 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager)
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView,
CreatorFactory<GenericCreator> > (false));
manager.add (CSMWorld::UniversalId::Type_StartScript,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView,
CreatorFactory<StartScriptCreator> > (false));
manager.add (CSMWorld::UniversalId::Type_Skill,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, NullCreatorFactory > (false));

@ -234,6 +234,11 @@ namespace MWClass
std::pair<int, std::string> Light::canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const
{
MWWorld::LiveCellRef<ESM::Light> *ref =
ptr.get<ESM::Light>();
if (!(ref->mBase->mData.mFlags & ESM::Light::Carry))
return std::make_pair(0,"");
MWWorld::InventoryStore& invStore = npc.getClass().getInventoryStore(npc);
MWWorld::ContainerStoreIterator weapon = invStore.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);

@ -139,9 +139,6 @@ namespace MWDialogue
win->startDialogue(actor, actor.getClass().getName (actor), resetHistory);
//setup the list of topics known by the actor. Topics who are also on the knownTopics list will be added to the GUI
updateTopics();
//greeting
const MWWorld::Store<ESM::Dialogue> &dialogs =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>();
@ -165,12 +162,19 @@ namespace MWDialogue
// TODO play sound
}
// first topics update so that parseText knows the keywords to highlight
updateTopics();
parseText (info->mResponse);
MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor);
win->addResponse (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext));
executeScript (info->mResultScript);
mLastTopic = Misc::StringUtils::lowerCase(it->mId);
// update topics again to accomodate changes resulting from executeScript
updateTopics();
return;
}
}
@ -420,7 +424,6 @@ namespace MWDialogue
{
if(mDialogueMap.find(keyword) != mDialogueMap.end())
{
ESM::Dialogue ndialogue = mDialogueMap[keyword];
if (mDialogueMap[keyword].mType == ESM::Dialogue::Topic)
{
executeTopic (keyword);

@ -170,25 +170,25 @@ namespace MWGui
mCommandLine->setFontName(fntName);
}
void Console::print(const std::string &msg)
void Console::print(const std::string &msg, const std::string& color)
{
mHistory->addText(msg);
mHistory->addText(color + MyGUI::TextIterator::toTagsString(msg));
}
void Console::printOK(const std::string &msg)
{
print("#FF00FF" + msg + "\n");
print(msg + "\n", "#FF00FF");
}
void Console::printError(const std::string &msg)
{
print("#FF2222" + msg + "\n");
print(msg + "\n", "#FF2222");
}
void Console::execute (const std::string& command)
{
// Log the command
print("#FFFFFF> " + command + "\n");
print("> " + command + "\n");
Compiler::Locals locals;
Compiler::Output output (locals);

@ -48,9 +48,8 @@ namespace MWGui
void onResChange(int width, int height);
// Print a message to the console. Messages may contain color
// code, eg. "#FFFFFF this is white".
void print(const std::string &msg);
// Print a message to the console, in specified color.
void print(const std::string &msg, const std::string& color = "#FFFFFF");
// These are pre-colored versions that you should use.

@ -167,11 +167,12 @@ namespace MWGui
MyGUI::IntSize size(static_cast<int>(Settings::Manager::getFloat(setting + " w", "Windows") * viewSize.width),
static_cast<int>(Settings::Manager::getFloat(setting + " h", "Windows") * viewSize.height));
mMainWidget->setPosition(pos);
mMainWidget->setSize(size);
if (size.width != mMainWidget->getWidth() || size.height != mMainWidget->getHeight())
updatePreviewSize();
mMainWidget->setPosition(pos);
mMainWidget->setSize(size);
adjustPanes();
}

@ -235,40 +235,64 @@ namespace MWGui
draw();
}
void LoadingScreen::draw()
bool LoadingScreen::needToDrawLoadingScreen()
{
if (mTimer.time_m() > mLastRenderTime + (1.0/mTargetFrameRate) * 1000.0)
if ( mTimer.time_m() <= mLastRenderTime + (1.0/mTargetFrameRate) * 1000.0)
return false;
// the minimal delay before a loading screen shows
const float initialDelay = 0.05;
bool alreadyShown = (mLastRenderTime > mLoadingOnTime);
float diff = (mTimer.time_m() - mLoadingOnTime);
if (!alreadyShown)
{
bool showWallpaper = (MWBase::Environment::get().getStateManager()->getState()
== MWBase::StateManager::State_NoGame);
// bump the delay by the current progress - i.e. if during the initial delay the loading
// has almost finished, no point showing the loading screen now
diff -= mProgress / static_cast<float>(mProgressBar->getScrollRange()) * 100.f;
}
if (showWallpaper && mTimer.time_m() > mLastWallpaperChangeTime + 5000*1)
{
mLastWallpaperChangeTime = mTimer.time_m();
changeWallpaper();
}
bool showWallpaper = (MWBase::Environment::get().getStateManager()->getState()
== MWBase::StateManager::State_NoGame);
if (!showWallpaper && diff < initialDelay*1000)
return false;
return true;
}
// Turn off rendering except the GUI
int oldUpdateMask = mViewer->getUpdateVisitor()->getTraversalMask();
int oldCullMask = mViewer->getCamera()->getCullMask();
mViewer->getUpdateVisitor()->setTraversalMask(MWRender::Mask_GUI);
mViewer->getCamera()->setCullMask(MWRender::Mask_GUI);
void LoadingScreen::draw()
{
if (!needToDrawLoadingScreen())
return;
bool showWallpaper = (MWBase::Environment::get().getStateManager()->getState()
== MWBase::StateManager::State_NoGame);
if (showWallpaper && mTimer.time_m() > mLastWallpaperChangeTime + 5000*1)
{
mLastWallpaperChangeTime = mTimer.time_m();
changeWallpaper();
}
MWBase::Environment::get().getInputManager()->update(0, true, true);
// Turn off rendering except the GUI
int oldUpdateMask = mViewer->getUpdateVisitor()->getTraversalMask();
int oldCullMask = mViewer->getCamera()->getCullMask();
mViewer->getUpdateVisitor()->setTraversalMask(MWRender::Mask_GUI);
mViewer->getCamera()->setCullMask(MWRender::Mask_GUI);
//osg::Timer timer;
mViewer->frame(mViewer->getFrameStamp()->getSimulationTime());
//std::cout << "frame took " << timer.time_m() << std::endl;
MWBase::Environment::get().getInputManager()->update(0, true, true);
//if (mViewer->getIncrementalCompileOperation())
//std::cout << "num to compile " << mViewer->getIncrementalCompileOperation()->getToCompile().size() << std::endl;
//osg::Timer timer;
mViewer->frame(mViewer->getFrameStamp()->getSimulationTime());
//std::cout << "frame took " << timer.time_m() << std::endl;
// resume 3d rendering
mViewer->getUpdateVisitor()->setTraversalMask(oldUpdateMask);
mViewer->getCamera()->setCullMask(oldCullMask);
//if (mViewer->getIncrementalCompileOperation())
//std::cout << "num to compile " << mViewer->getIncrementalCompileOperation()->getToCompile().size() << std::endl;
mLastRenderTime = mTimer.time_m();
}
// resume 3d rendering
mViewer->getUpdateVisitor()->setTraversalMask(oldUpdateMask);
mViewer->getCamera()->setCullMask(oldCullMask);
mLastRenderTime = mTimer.time_m();
}
}

@ -52,6 +52,7 @@ namespace MWGui
private:
void findSplashScreens();
bool needToDrawLoadingScreen();
const VFS::Manager* mVFS;
osg::ref_ptr<osgViewer::Viewer> mViewer;

@ -1,5 +1,7 @@
#include "spellmodel.hpp"
#include <iostream>
#include <boost/lexical_cast.hpp>
#include "../mwbase/environment.hpp"
@ -79,8 +81,13 @@ namespace MWGui
const std::string enchantId = item.getClass().getEnchantment(item);
if (enchantId.empty())
continue;
const ESM::Enchantment* enchant =
esmStore.get<ESM::Enchantment>().find(item.getClass().getEnchantment(item));
const ESM::Enchantment* enchant = esmStore.get<ESM::Enchantment>().search(enchantId);
if (!enchant)
{
std::cerr << "Can't find enchantment '" << enchantId << "' on item " << item.getCellRef().getRefId() << std::endl;
continue;
}
if (enchant->mData.mType != ESM::Enchantment::WhenUsed && enchant->mData.mType != ESM::Enchantment::CastOnce)
continue;

@ -8,11 +8,6 @@
namespace MWGui
{
namespace Widgets
{
class MWScrollBar;
}
class WaitDialogProgressBar : public WindowBase
{
public:
@ -51,7 +46,7 @@ namespace MWGui
MyGUI::Button* mUntilHealedButton;
MyGUI::Button* mWaitButton;
MyGUI::Button* mCancelButton;
MWGui::Widgets::MWScrollBar* mHourSlider;
MyGUI::ScrollBar* mHourSlider;
TimeAdvancer mTimeAdvancer;
bool mSleeping;

@ -20,8 +20,9 @@ namespace MWMechanics
struct AiFollowStorage : AiTemporaryBase
{
float mTimer;
bool mMoving;
AiFollowStorage() : mTimer(0.f) {}
AiFollowStorage() : mTimer(0.f), mMoving(false) {}
};
int AiFollow::mFollowIndexCounter = 0;
@ -64,10 +65,11 @@ bool AiFollow::execute (const MWWorld::Ptr& actor, CharacterController& characte
actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Nothing);
AiFollowStorage& storage = state.get<AiFollowStorage>();
// AiFollow requires the target to be in range and within sight for the initial activation
if (!mActive)
{
AiFollowStorage& storage = state.get<AiFollowStorage>();
storage.mTimer -= duration;
if (storage.mTimer < 0)
@ -126,7 +128,15 @@ bool AiFollow::execute (const MWWorld::Ptr& actor, CharacterController& characte
//Set the target destination from the actor
ESM::Pathgrid::Point dest = target.getRefData().getPosition().pos;
if(distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]) < followDistance) //Stop when you get close
float dist = distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]);
const float threshold = 10;
if (storage.mMoving) //Stop when you get close
storage.mMoving = (dist > followDistance);
else
storage.mMoving = (dist > followDistance + threshold);
if(!storage.mMoving)
{
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
@ -141,9 +151,9 @@ bool AiFollow::execute (const MWWorld::Ptr& actor, CharacterController& characte
}
//Check if you're far away
if(distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]) > 450)
if(dist > 450)
actor.getClass().getCreatureStats(actor).setMovementFlag(MWMechanics::CreatureStats::Flag_Run, true); //Make NPC run
else if(distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]) < 325) //Have a bit of a dead zone, otherwise npc will constantly flip between running and not when right on the edge of the running threshhold
else if(dist < 325) //Have a bit of a dead zone, otherwise npc will constantly flip between running and not when right on the edge of the running threshhold
actor.getClass().getCreatureStats(actor).setMovementFlag(MWMechanics::CreatureStats::Flag_Run, false); //make NPC walk
return false;

@ -88,7 +88,8 @@ void MWMechanics::AiPackage::evadeObstacles(const MWWorld::Ptr& actor, float dur
MWWorld::Ptr door = getNearbyDoor(actor);
if (door != MWWorld::Ptr()) // NOTE: checks interior cells only
{
if (!door.getCellRef().getTeleport() && door.getCellRef().getTrap().empty() && door.getClass().getDoorState(door) == 0) { //Open the door if untrapped
if (!door.getCellRef().getTeleport() && door.getCellRef().getTrap().empty()
&& door.getCellRef().getLockLevel() <= 0 && door.getClass().getDoorState(door) == 0) {
MWBase::Environment::get().getWorld()->activateDoor(door, 1);
}
}

@ -295,7 +295,7 @@ namespace MWMechanics
creatureStats.getSpells().add(*it);
// forced update and current value adjustments
//mActors.updateActor (ptr, 0);
mActors.updateActor (ptr, 0);
for (int i=0; i<3; ++i)
{

@ -123,6 +123,9 @@ namespace MWMechanics
}
}
if (spell->mData.mType == ESM::Spell::ST_Power)
return stats.getSpells().canUsePower(spell->mId) ? 100 : 0;
if (spell->mData.mType != ESM::Spell::ST_Spell)
return 100;
@ -817,6 +820,10 @@ namespace MWMechanics
sndMgr->playSound3D(mCaster, "Spell Failure " + schools[school], 1.0f, 1.0f);
return false;
}
// A power can be used once per 24h
if (spell->mData.mType == ESM::Spell::ST_Power)
stats.getSpells().usePower(spell->mId);
}
if (mCaster == getPlayer() && spellIncreasesSkill(spell))

@ -312,7 +312,7 @@ namespace MWMechanics
bool Spells::canUsePower(const std::string &power) const
{
std::map<std::string, MWWorld::TimeStamp>::const_iterator it = mUsedPowers.find(power);
std::map<std::string, MWWorld::TimeStamp>::const_iterator it = mUsedPowers.find(Misc::StringUtils::lowerCase(power));
if (it == mUsedPowers.end() || it->second + 24 <= MWBase::Environment::get().getWorld()->getTimeStamp())
return true;
else
@ -321,7 +321,7 @@ namespace MWMechanics
void Spells::usePower(const std::string &power)
{
mUsedPowers[power] = MWBase::Environment::get().getWorld()->getTimeStamp();
mUsedPowers[Misc::StringUtils::lowerCase(power)] = MWBase::Environment::get().getWorld()->getTimeStamp();
}
void Spells::readState(const ESM::SpellState &state)

@ -1275,15 +1275,26 @@ namespace MWRender
material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,1));
stateset->setAttributeAndModes(material, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
stateset->setRenderBinMode(osg::StateSet::OVERRIDE_RENDERBIN_DETAILS);
stateset->setNestRenderBins(false);
mObjectRoot->setStateSet(stateset);
}
else
{
mObjectRoot->setStateSet(NULL);
}
setRenderBin();
}
void Animation::setRenderBin()
{
if (mAlpha != 1.f)
{
osg::StateSet* stateset = mObjectRoot->getOrCreateStateSet();
stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
stateset->setRenderBinMode(osg::StateSet::OVERRIDE_RENDERBIN_DETAILS);
}
else if (osg::StateSet* stateset = mObjectRoot->getStateSet())
stateset->setRenderBinToInherit();
}
void Animation::setLightEffect(float effect)
@ -1320,13 +1331,16 @@ namespace MWRender
{
mHeadController = NULL;
NodeMap::iterator found = mNodeMap.find("bip01 head");
if (found != mNodeMap.end() && dynamic_cast<osg::MatrixTransform*>(found->second.get()))
if (mPtr.getClass().isBipedal(mPtr))
{
osg::Node* node = found->second;
mHeadController = new RotateController(mObjectRoot.get());
node->addUpdateCallback(mHeadController);
mActiveControllers.insert(std::make_pair(node, mHeadController));
NodeMap::iterator found = mNodeMap.find("bip01 head");
if (found != mNodeMap.end() && dynamic_cast<osg::MatrixTransform*>(found->second.get()))
{
osg::Node* node = found->second;
mHeadController = new RotateController(mObjectRoot.get());
node->addUpdateCallback(mHeadController);
mActiveControllers.insert(std::make_pair(node, mHeadController));
}
}
}

@ -307,6 +307,9 @@ protected:
void addGlow(osg::ref_ptr<osg::Node> node, osg::Vec4f glowColor);
/// Set the render bin for this animation's object root. May be customized by subclasses.
virtual void setRenderBin();
public:
Animation(const MWWorld::Ptr &ptr, osg::ref_ptr<osg::Group> parentNode, Resource::ResourceSystem* resourceSystem);

@ -2,6 +2,9 @@
#include <osg/UserDataContainer>
#include <osg/MatrixTransform>
#include <osg/Depth>
#include <osgUtil/RenderBin>
#include <components/misc/rng.hpp>
@ -28,6 +31,7 @@
#include "camera.hpp"
#include "rotatecontroller.hpp"
#include "renderbin.hpp"
namespace
{
@ -303,6 +307,50 @@ void NpcAnimation::setViewMode(NpcAnimation::ViewMode viewMode)
mViewMode = viewMode;
rebuild();
setRenderBin();
}
/// @brief A RenderBin callback to clear the depth buffer before rendering.
class DepthClearCallback : public osgUtil::RenderBin::DrawCallback
{
public:
DepthClearCallback()
{
mDepth = new osg::Depth;
mDepth->setWriteMask(true);
}
virtual void drawImplementation(osgUtil::RenderBin* bin, osg::RenderInfo& renderInfo, osgUtil::RenderLeaf*& previous)
{
renderInfo.getState()->applyAttribute(mDepth);
glClear(GL_DEPTH_BUFFER_BIT);
bin->drawImplementation(renderInfo, previous);
}
osg::ref_ptr<osg::Depth> mDepth;
};
void NpcAnimation::setRenderBin()
{
if (mViewMode == VM_FirstPerson)
{
static bool prototypeAdded = false;
if (!prototypeAdded)
{
osg::ref_ptr<osgUtil::RenderBin> depthClearBin (new osgUtil::RenderBin);
depthClearBin->setDrawCallback(new DepthClearCallback);
osgUtil::RenderBin::addRenderBinPrototype("DepthClear", depthClearBin);
prototypeAdded = true;
}
osg::StateSet* stateset = mObjectRoot->getOrCreateStateSet();
stateset->setRenderBinDetails(RenderBin_FirstPerson, "DepthClear", osg::StateSet::OVERRIDE_RENDERBIN_DETAILS);
}
else
Animation::setRenderBin();
}
void NpcAnimation::rebuild()

@ -81,6 +81,8 @@ private:
void addPartGroup(int group, int priority, const std::vector<ESM::PartReference> &parts,
bool enchantedGlow=false, osg::Vec4f* glowColor=NULL);
virtual void setRenderBin();
osg::ref_ptr<NeckController> mFirstPersonNeckController;
protected:

@ -5,14 +5,15 @@ namespace MWRender
{
/// Defines the render bin numbers used in the OpenMW scene graph. The bin with the lowest number is rendered first.
/// Beware of RenderBin nesting, in most cases you will want to use setNestRenderBins(false).
enum RenderBins
{
RenderBin_Sky = -1,
RenderBin_Default = 0,
RenderBin_Default = 0, // osg::StateSet::OPAQUE_BIN
RenderBin_Water = 9,
RenderBin_OcclusionQuery = 10,
RenderBin_SunGlare = 11
RenderBin_DepthSorted = 10, // osg::StateSet::TRANSPARENT_BIN
RenderBin_OcclusionQuery = 11,
RenderBin_FirstPerson = 12,
RenderBin_SunGlare = 13
};
}

@ -493,6 +493,8 @@ namespace MWRender
texture->setInternalFormat(GL_RGB);
texture->setTextureSize(w, h);
texture->setResizeNonPowerOfTwoHint(false);
texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
texture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
rttCamera->attach(osg::Camera::COLOR_BUFFER, texture);
image->setDataType(GL_UNSIGNED_BYTE);

@ -724,22 +724,30 @@ private:
, mTimeOfDayFade(1.f)
, mGlareView(1.f)
{
const MWWorld::Fallback* fallback = MWBase::Environment::get().getWorld()->getFallback();
mColor = fallback->getFallbackColour("Weather_Sun_Glare_Fader_Color");
mSunGlareFaderMax = fallback->getFallbackFloat("Weather_Sun_Glare_Fader_Max");
mSunGlareFaderAngleMax = fallback->getFallbackFloat("Weather_Sun_Glare_Fader_Angle_Max");
// Replicating a design flaw in MW. The color was being set on both ambient and emissive properties, which multiplies the result by two,
// then finally gets clamped by the fixed function pipeline. With the default INI settings, only the red component gets clamped,
// so the resulting color looks more orange than red.
mColor *= 2;
for (int i=0; i<3; ++i)
mColor[i] = std::min(1.f, mColor[i]);
}
virtual void operator ()(osg::Node* node, osg::NodeVisitor* nv)
{
osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>(nv);
float angleRadians = getAngleToSunInRadians(cv->getCurrentCamera());
float angleRadians = getAngleToSunInRadians(*cv->getCurrentRenderStage()->getInitialViewMatrix());
float visibleRatio = getVisibleRatio(cv->getCurrentCamera());
const float angleMaxRadians = osg::DegreesToRadians(30.f); // Sun Glare Fader Angle Max
const float angleMaxRadians = osg::DegreesToRadians(mSunGlareFaderAngleMax);
float value = 1.f - std::min(1.f, angleRadians / angleMaxRadians);
const float sunGlareFaderMax = 0.5f;
float fade = value * sunGlareFaderMax;
float fade = value * mSunGlareFaderMax;
fade *= mTimeOfDayFade * mGlareView * visibleRatio;
@ -754,17 +762,8 @@ private:
osg::ref_ptr<osg::Material> mat (createUnlitMaterial());
osg::Vec4f sunGlareFaderColor (222/255.f, 95/255.f, 39/255.f, 1);
// Replicating a design flaw in MW. The color was being set on both ambient and emissive properties, which multiplies the result by two,
// then finally gets clamped by the fixed function pipeline. With the default INI settings, only the red component gets clamped,
// so the resulting color looks more orange than red.
sunGlareFaderColor *= 2;
for (int i=0; i<3; ++i)
sunGlareFaderColor[i] = std::min(1.f, sunGlareFaderColor[i]);
mat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0,0,0,fade));
mat->setEmission(osg::Material::FRONT_AND_BACK, sunGlareFaderColor);
mat->setEmission(osg::Material::FRONT_AND_BACK, mColor);
stateset->setAttributeAndModes(mat, osg::StateAttribute::ON);
@ -785,10 +784,10 @@ private:
}
private:
float getAngleToSunInRadians(osg::Camera* camera) const
float getAngleToSunInRadians(const osg::Matrix& viewMatrix) const
{
osg::Vec3d eye, center, up;
camera->getViewMatrixAsLookAt(eye, center, up);
viewMatrix.getLookAt(eye, center, up);
osg::Vec3d forward = center - eye;
osg::Vec3d sun = mSunTransform->getPosition();
@ -802,6 +801,9 @@ private:
osg::ref_ptr<osg::PositionAttitudeTransform> mSunTransform;
float mTimeOfDayFade;
float mGlareView;
osg::Vec4f mColor;
float mSunGlareFaderMax;
float mSunGlareFaderAngleMax;
};
osg::ref_ptr<Updater> mUpdater;

@ -12,6 +12,7 @@
#include "../mwworld/ptr.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/manualref.hpp"
namespace MWScript
{
@ -42,9 +43,9 @@ namespace MWScript
}
else
{
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPtr (id, false);
MWWorld::ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), id);
script = ptr.getClass().getScript (ptr);
script = ref.getPtr().getClass().getScript (ref.getPtr());
reference = true;
}
@ -82,7 +83,8 @@ namespace MWScript
store.get<ESM::Probe>().search (name) ||
store.get<ESM::Repair>().search (name) ||
store.get<ESM::Static>().search (name) ||
store.get<ESM::Weapon>().search (name);
store.get<ESM::Weapon>().search (name) ||
store.get<ESM::Script>().search (name);
}
bool CompilerContext::isJournalId (const std::string& name) const

@ -140,7 +140,11 @@ void MWWorld::ContainerStore::unstack(const Ptr &ptr, const Ptr& container)
{
if (ptr.getRefData().getCount() <= 1)
return;
addNewStack(ptr, ptr.getRefData().getCount()-1);
MWWorld::ContainerStoreIterator it = addNewStack(ptr, ptr.getRefData().getCount()-1);
const std::string script = it->getClass().getScript(*it);
if (!script.empty())
MWBase::Environment::get().getWorld()->getLocalScripts().add(script, *it);
remove(ptr, ptr.getRefData().getCount()-1, container);
}
@ -411,41 +415,48 @@ void MWWorld::ContainerStore::fill (const ESM::InventoryList& items, const std::
void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std::string& owner,
int count, bool topLevel, const std::string& levItem)
{
ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), id, count);
if (ref.getPtr().getTypeName()==typeid (ESM::ItemLevList).name())
{
const ESM::ItemLevList* levItem = ref.getPtr().get<ESM::ItemLevList>()->mBase;
try {
ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), id, count);
if (topLevel && std::abs(count) > 1 && levItem->mFlags & ESM::ItemLevList::Each)
if (ref.getPtr().getTypeName()==typeid (ESM::ItemLevList).name())
{
for (int i=0; i<std::abs(count); ++i)
addInitialItem(id, owner, count > 0 ? 1 : -1, true, levItem->mId);
return;
const ESM::ItemLevList* levItem = ref.getPtr().get<ESM::ItemLevList>()->mBase;
if (topLevel && std::abs(count) > 1 && levItem->mFlags & ESM::ItemLevList::Each)
{
for (int i=0; i<std::abs(count); ++i)
addInitialItem(id, owner, count > 0 ? 1 : -1, true, levItem->mId);
return;
}
else
{
std::string id = MWMechanics::getLevelledItem(ref.getPtr().get<ESM::ItemLevList>()->mBase, false);
if (id.empty())
return;
addInitialItem(id, owner, count, false, levItem->mId);
}
}
else
{
std::string id = MWMechanics::getLevelledItem(ref.getPtr().get<ESM::ItemLevList>()->mBase, false);
if (id.empty())
return;
addInitialItem(id, owner, count, false, levItem->mId);
// A negative count indicates restocking items
// For a restocking levelled item, remember what we spawned so we can delete it later when the merchant restocks
if (!levItem.empty() && count < 0)
{
if (mLevelledItemMap.find(id) == mLevelledItemMap.end())
mLevelledItemMap[id] = 0;
mLevelledItemMap[id] += std::abs(count);
}
count = std::abs(count);
ref.getPtr().getCellRef().setOwner(owner);
addImp (ref.getPtr(), count);
}
}
else
catch (const std::exception& e)
{
// A negative count indicates restocking items
// For a restocking levelled item, remember what we spawned so we can delete it later when the merchant restocks
if (!levItem.empty() && count < 0)
{
if (mLevelledItemMap.find(id) == mLevelledItemMap.end())
mLevelledItemMap[id] = 0;
mLevelledItemMap[id] += std::abs(count);
}
count = std::abs(count);
ref.getPtr().getCellRef().setOwner(owner);
addImp (ref.getPtr(), count);
std::cerr << "Error in MWWorld::ContainerStore::addInitialItem: " << e.what() << std::endl;
}
}
void MWWorld::ContainerStore::restock (const ESM::InventoryList& items, const MWWorld::Ptr& ptr, const std::string& owner)

@ -645,8 +645,15 @@ void MWWorld::InventoryStore::updateRechargingItems()
{
if (it->getClass().getEnchantment(*it) != "")
{
const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().find(
it->getClass().getEnchantment(*it));
std::string enchantmentId = it->getClass().getEnchantment(*it);
const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().search(
enchantmentId);
if (!enchantment)
{
std::cerr << "Can't find enchantment '" << enchantmentId << "' on item " << it->getCellRef().getRefId() << std::endl;
continue;
}
if (enchantment->mData.mType == ESM::Enchantment::WhenUsed
|| enchantment->mData.mType == ESM::Enchantment::WhenStrikes)
mRechargingItems.push_back(std::make_pair(it, static_cast<float>(enchantment->mData.mCharge)));

@ -1029,7 +1029,8 @@ namespace MWWorld
// Script
//=========================================================================
inline void Store<ESM::Script>::load(ESM::ESMReader &esm, const std::string &id) {
template <>
void Store<ESM::Script>::load(ESM::ESMReader &esm, const std::string &id) {
ESM::Script scpt;
scpt.load(esm);
Misc::StringUtils::toLower(scpt.mId);
@ -1045,7 +1046,8 @@ namespace MWWorld
// StartScript
//=========================================================================
inline void Store<ESM::StartScript>::load(ESM::ESMReader &esm, const std::string &id)
template <>
void Store<ESM::StartScript>::load(ESM::ESMReader &esm, const std::string &id)
{
ESM::StartScript s;
s.load(esm);

@ -204,6 +204,10 @@ namespace MWWorld
mLevitationEnabled = true;
mTeleportEnabled = true;
mGodMode = false;
mScriptsEnabled = true;
mSky = true;
// Rebuild player
setupPlayer();
@ -297,9 +301,6 @@ namespace MWWorld
mDoorStates.clear();
mGodMode = false;
mScriptsEnabled = true;
mSky = true;
mTeleportEnabled = true;
mLevitationEnabled = true;
@ -2659,10 +2660,6 @@ namespace MWWorld
{
const ESM::Spell* spell = getStore().get<ESM::Spell>().find(selectedSpell);
// A power can be used once per 24h
if (spell->mData.mType == ESM::Spell::ST_Power)
stats.getSpells().usePower(spell->mId);
cast.cast(spell);
}
else if (actor.getClass().hasInventoryStore(actor))

@ -652,6 +652,13 @@ namespace Compiler
return true;
}
if (code ==Scanner::S_plus && mNextOperand)
{
// Also unary, but +, just ignore it
mTokenLoc = loc;
return true;
}
if (code==Scanner::S_open)
{
if (mNextOperand)

@ -179,7 +179,7 @@ namespace Compiler
extensions.registerInstruction ("setjournalindex", "cl", opcodeSetJournalIndex);
extensions.registerFunction ("getjournalindex", 'l', "c", opcodeGetJournalIndex);
extensions.registerInstruction ("addtopic", "S" , opcodeAddTopic);
extensions.registerInstruction ("choice", "j/SlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSl", opcodeChoice);
extensions.registerInstruction ("choice", "j/SlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSl", opcodeChoice);
extensions.registerInstruction("forcegreeting","",opcodeForceGreeting,
opcodeForceGreetingExplicit);
extensions.registerInstruction("goodbye", "", opcodeGoodbye);

@ -555,7 +555,7 @@ namespace Compiler
}
if (mAllowExpression && mState==BeginState &&
(code==Scanner::S_open || code==Scanner::S_minus))
(code==Scanner::S_open || code==Scanner::S_minus || code==Scanner::S_plus))
{
scanner.putbackSpecial (code, loc);
parseExpression (scanner, loc);

@ -175,7 +175,7 @@ namespace Compiler
{
value += c;
}
else if (isStringCharacter (c))
else if (c!='-' && isStringCharacter (c))
{
error = true;
value += c;

@ -132,7 +132,34 @@ namespace Interpreter
throw std::runtime_error (error.str());
}
Interpreter::Interpreter()
void Interpreter::begin()
{
if (mRunning)
{
mCallstack.push (mRuntime);
mRuntime.clear();
}
else
{
mRunning = true;
}
}
void Interpreter::end()
{
if (mCallstack.empty())
{
mRuntime.clear();
mRunning = false;
}
else
{
mRuntime = mCallstack.top();
mCallstack.pop();
}
}
Interpreter::Interpreter() : mRunning (false)
{}
Interpreter::~Interpreter()
@ -202,19 +229,29 @@ namespace Interpreter
{
assert (codeSize>=4);
mRuntime.configure (code, codeSize, context);
begin();
int opcodes = static_cast<int> (code[0]);
try
{
mRuntime.configure (code, codeSize, context);
const Type_Code *codeBlock = code + 4;
int opcodes = static_cast<int> (code[0]);
while (mRuntime.getPC()>=0 && mRuntime.getPC()<opcodes)
const Type_Code *codeBlock = code + 4;
while (mRuntime.getPC()>=0 && mRuntime.getPC()<opcodes)
{
Type_Code code = codeBlock[mRuntime.getPC()];
mRuntime.setPC (mRuntime.getPC()+1);
execute (code);
}
}
catch (...)
{
Type_Code code = codeBlock[mRuntime.getPC()];
mRuntime.setPC (mRuntime.getPC()+1);
execute (code);
end();
throw;
}
mRuntime.clear();
end();
}
}

@ -2,6 +2,7 @@
#define INTERPRETER_INTERPRETER_H_INCLUDED
#include <map>
#include <stack>
#include "runtime.hpp"
#include "types.hpp"
@ -14,6 +15,8 @@ namespace Interpreter
class Interpreter
{
std::stack<Runtime> mCallstack;
bool mRunning;
Runtime mRuntime;
std::map<int, Opcode1 *> mSegment0;
std::map<int, Opcode2 *> mSegment1;
@ -32,6 +35,10 @@ namespace Interpreter
void abortUnknownSegment (Type_Code code);
void begin();
void end();
public:
Interpreter();

@ -57,8 +57,8 @@ namespace
}
}
// Collect all properties affecting the given node that should be applied to an osg::Material.
void collectMaterialProperties(const Nif::Node* nifNode, std::vector<const Nif::Property*>& out)
// Collect all properties affecting the given drawable that should be handled on drawable basis rather than on the node hierarchy above it.
void collectDrawableProperties(const Nif::Node* nifNode, std::vector<const Nif::Property*>& out)
{
const Nif::PropertyList& props = nifNode->props;
for (size_t i = 0; i <props.length();++i)
@ -70,6 +70,7 @@ namespace
case Nif::RC_NiMaterialProperty:
case Nif::RC_NiVertexColorProperty:
case Nif::RC_NiSpecularProperty:
case Nif::RC_NiAlphaProperty:
out.push_back(props[i].getPtr());
break;
default:
@ -78,7 +79,7 @@ namespace
}
}
if (nifNode->parent)
collectMaterialProperties(nifNode->parent, out);
collectDrawableProperties(nifNode->parent, out);
}
class FrameSwitch : public osg::Group
@ -113,6 +114,7 @@ namespace
// NodeCallback used to have a transform always oriented towards the camera. Can have translation and scale
// set just like a regular MatrixTransform, but the rotation set will be overridden in order to face the camera.
// Must be set as a cull callback.
class BillboardCallback : public osg::NodeCallback
{
public:
@ -160,24 +162,34 @@ namespace
struct UpdateMorphGeometry : public osg::Drawable::CullCallback
{
UpdateMorphGeometry()
: mLastFrameNumber(0)
{
}
UpdateMorphGeometry(const UpdateMorphGeometry& copy, const osg::CopyOp& copyop)
: osg::Drawable::CullCallback(copy, copyop)
, mLastFrameNumber(0)
{
}
META_Object(NifOsg, UpdateMorphGeometry)
virtual bool cull(osg::NodeVisitor *, osg::Drawable * drw, osg::State *) const
virtual bool cull(osg::NodeVisitor* nv, osg::Drawable * drw, osg::State *) const
{
osgAnimation::MorphGeometry* geom = static_cast<osgAnimation::MorphGeometry*>(drw);
if (!geom)
return false;
if (mLastFrameNumber == nv->getFrameStamp()->getFrameNumber())
return false;
mLastFrameNumber = nv->getFrameStamp()->getFrameNumber();
geom->transformSoftwareMethod();
return false;
}
private:
mutable unsigned int mLastFrameNumber;
};
// Callback to return a static bounding box for a MorphGeometry. The idea is to not recalculate the bounding box
@ -861,9 +873,9 @@ namespace NifOsg
osg::ref_ptr<osg::Geode> geode (new osg::Geode);
geode->addDrawable(partsys);
std::vector<const Nif::Property*> materialProps;
collectMaterialProperties(nifNode, materialProps);
applyMaterialProperties(parentNode, materialProps, composite, true, animflags);
std::vector<const Nif::Property*> drawableProps;
collectDrawableProperties(nifNode, drawableProps);
applyDrawableProperties(parentNode, drawableProps, composite, true, animflags);
// Particles don't have normals, so can't be diffuse lit.
osg::Material* mat = static_cast<osg::Material*>(parentNode->getStateSet()->getAttribute(osg::StateAttribute::MATERIAL));
@ -923,9 +935,9 @@ namespace NifOsg
// - if there are no vertex colors, we need to disable colorMode.
// - there are 3 "overlapping" nif properties that all affect the osg::Material, handling them
// above the actual renderable would be tedious.
std::vector<const Nif::Property*> materialProps;
collectMaterialProperties(triShape, materialProps);
applyMaterialProperties(parentNode, materialProps, composite, !data->colors->empty(), animflags);
std::vector<const Nif::Property*> drawableProps;
collectDrawableProperties(triShape, drawableProps);
applyDrawableProperties(parentNode, drawableProps, composite, !data->colors->empty(), animflags);
}
void handleTriShape(const Nif::NiTriShape* triShape, osg::Group* parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector<int>& boundTextures, int animflags)
@ -1212,42 +1224,12 @@ namespace NifOsg
case Nif::RC_NiVertexColorProperty:
case Nif::RC_NiSpecularProperty:
{
// Handled in handleTriShape so we know whether vertex colors are available
// Handled on drawable level so we know whether vertex colors are available
break;
}
case Nif::RC_NiAlphaProperty:
{
const Nif::NiAlphaProperty* alphaprop = static_cast<const Nif::NiAlphaProperty*>(property);
osg::BlendFunc* blendfunc = new osg::BlendFunc;
osg::StateSet* stateset = node->getOrCreateStateSet();
if (alphaprop->flags&1)
{
blendfunc->setFunction(getBlendMode((alphaprop->flags>>1)&0xf),
getBlendMode((alphaprop->flags>>5)&0xf));
stateset->setAttributeAndModes(blendfunc, osg::StateAttribute::ON);
bool noSort = (alphaprop->flags>>13)&1;
if (!noSort)
{
stateset->setNestRenderBins(false);
stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
}
}
else
{
stateset->setAttributeAndModes(blendfunc, osg::StateAttribute::OFF);
stateset->setNestRenderBins(false);
stateset->setRenderingHint(osg::StateSet::OPAQUE_BIN);
}
osg::AlphaFunc* alphafunc = new osg::AlphaFunc;
if((alphaprop->flags>>9)&1)
{
alphafunc->setFunction(getTestMode((alphaprop->flags>>10)&0x7), alphaprop->data.threshold/255.f);
stateset->setAttributeAndModes(alphafunc, osg::StateAttribute::ON);
}
else
stateset->setAttributeAndModes(alphafunc, osg::StateAttribute::OFF);
// Handled on drawable level to prevent RenderBin nesting issues
break;
}
case Nif::RC_NiTexturingProperty:
@ -1377,7 +1359,7 @@ namespace NifOsg
}
}
void applyMaterialProperties(osg::Node* node, const std::vector<const Nif::Property*>& properties, SceneUtil::CompositeStateSetUpdater* composite,
void applyDrawableProperties(osg::Node* node, const std::vector<const Nif::Property*>& properties, SceneUtil::CompositeStateSetUpdater* composite,
bool hasVertexColors, int animflags)
{
osg::StateSet* stateset = node->getOrCreateStateSet();
@ -1433,6 +1415,43 @@ namespace NifOsg
mat->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE);
break;
}
break;
}
case Nif::RC_NiAlphaProperty:
{
const Nif::NiAlphaProperty* alphaprop = static_cast<const Nif::NiAlphaProperty*>(property);
osg::BlendFunc* blendfunc = new osg::BlendFunc;
if (alphaprop->flags&1)
{
blendfunc->setFunction(getBlendMode((alphaprop->flags>>1)&0xf),
getBlendMode((alphaprop->flags>>5)&0xf));
stateset->setAttributeAndModes(blendfunc, osg::StateAttribute::ON);
bool noSort = (alphaprop->flags>>13)&1;
if (!noSort)
stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
else
stateset->setRenderBinToInherit();
}
else
{
stateset->removeAttribute(osg::StateAttribute::BLENDFUNC);
stateset->removeMode(GL_BLEND);
stateset->setRenderBinToInherit();
}
osg::AlphaFunc* alphafunc = new osg::AlphaFunc;
if((alphaprop->flags>>9)&1)
{
alphafunc->setFunction(getTestMode((alphaprop->flags>>10)&0x7), alphaprop->data.threshold/255.f);
stateset->setAttributeAndModes(alphafunc, osg::StateAttribute::ON);
}
else
{
stateset->removeAttribute(osg::StateAttribute::ALPHAFUNC);
stateset->removeMode(GL_ALPHA_TEST);
}
break;
}
}
}

@ -64,6 +64,16 @@ namespace SceneUtil
std::vector<osg::ref_ptr<osg::Light> > mLights;
};
LightManager* findLightManager(const osg::NodePath& path)
{
for (unsigned int i=0;i<path.size(); ++i)
{
if (LightManager* lightManager = dynamic_cast<LightManager*>(path[i]))
return lightManager;
}
return NULL;
}
// Set on a LightSource. Adds the light source to its light manager for the current frame.
// This allows us to keep track of the current lights in the scene graph without tying creation & destruction to the manager.
class CollectLightCallback : public osg::NodeCallback
@ -82,14 +92,8 @@ namespace SceneUtil
{
if (!mLightManager)
{
for (unsigned int i=0;i<nv->getNodePath().size(); ++i)
{
if (LightManager* lightManager = dynamic_cast<LightManager*>(nv->getNodePath()[i]))
{
mLightManager = lightManager;
break;
}
}
mLightManager = findLightManager(nv->getNodePath());
if (!mLightManager)
throw std::runtime_error("can't find parent LightManager");
}
@ -126,15 +130,13 @@ namespace SceneUtil
};
LightManager::LightManager()
: mLightsInViewSpace(false)
, mStartLight(0)
: mStartLight(0)
{
setUpdateCallback(new LightManagerUpdateCallback);
}
LightManager::LightManager(const LightManager &copy, const osg::CopyOp &copyop)
: osg::Group(copy, copyop)
, mLightsInViewSpace(false)
, mStartLight(copy.mStartLight)
{
@ -142,8 +144,8 @@ namespace SceneUtil
void LightManager::update()
{
mLightsInViewSpace = false;
mLights.clear();
mLightsInViewSpace.clear();
// do an occasional cleanup for orphaned lights
if (mStateSetCache.size() > 5000)
@ -161,22 +163,6 @@ namespace SceneUtil
mLights.push_back(l);
}
void LightManager::prepareForCamera(osg::Camera *cam)
{
// later on we need to store this per camera
if (!mLightsInViewSpace)
{
for (std::vector<LightSourceTransform>::iterator it = mLights.begin(); it != mLights.end(); ++it)
{
LightSourceTransform& l = *it;
osg::Matrix worldViewMat = l.mWorldMatrix * cam->getViewMatrix();
l.mViewBound = osg::BoundingSphere(osg::Vec3f(0,0,0), l.mLightSource->getRadius());
transformBoundingSphere(worldViewMat, l.mViewBound);
}
mLightsInViewSpace = true;
}
}
osg::ref_ptr<osg::StateSet> LightManager::getLightListStateSet(const LightList &lightList)
{
// possible optimization: return a StateSet containing all requested lights plus some extra lights (if a suitable one exists)
@ -212,6 +198,30 @@ namespace SceneUtil
return mLights;
}
const std::vector<LightManager::LightSourceViewBound>& LightManager::getLightsInViewSpace(osg::Camera *camera, const osg::RefMatrix* viewMatrix)
{
osg::observer_ptr<osg::Camera> camPtr (camera);
std::map<osg::observer_ptr<osg::Camera>, LightSourceViewBoundCollection>::iterator it = mLightsInViewSpace.find(camPtr);
if (it == mLightsInViewSpace.end())
{
it = mLightsInViewSpace.insert(std::make_pair(camPtr, LightSourceViewBoundCollection())).first;
for (std::vector<LightSourceTransform>::iterator lightIt = mLights.begin(); lightIt != mLights.end(); ++lightIt)
{
osg::Matrix worldViewMat = lightIt->mWorldMatrix * (*viewMatrix);
osg::BoundingSphere viewBound = osg::BoundingSphere(osg::Vec3f(0,0,0), lightIt->mLightSource->getRadius());
transformBoundingSphere(worldViewMat, viewBound);
LightSourceViewBound l;
l.mLightSource = lightIt->mLightSource;
l.mViewBound = viewBound;
it->second.push_back(l);
}
}
return it->second;
}
void LightManager::setStartLight(int start)
{
mStartLight = start;
@ -241,7 +251,7 @@ namespace SceneUtil
}
bool sortLights (const LightManager::LightSourceTransform* left, const LightManager::LightSourceTransform* right)
bool sortLights (const LightManager::LightSourceViewBound* left, const LightManager::LightSourceViewBound* right)
{
return left->mViewBound.center().length2() - left->mViewBound.radius2()/4.f < right->mViewBound.center().length2() - right->mViewBound.radius2()/4.f;
}
@ -258,14 +268,7 @@ namespace SceneUtil
if (!mLightManager)
{
for (unsigned int i=0;i<nv->getNodePath().size(); ++i)
{
if (LightManager* lightManager = dynamic_cast<LightManager*>(nv->getNodePath()[i]))
{
mLightManager = lightManager;
break;
}
}
mLightManager = findLightManager(nv->getNodePath());
if (!mLightManager)
{
traverse(node, nv);
@ -273,13 +276,14 @@ namespace SceneUtil
}
}
mLightManager->prepareForCamera(cv->getCurrentCamera());
// Possible optimizations:
// - cull list of lights by the camera frustum
// - organize lights in a quad tree
const std::vector<LightManager::LightSourceTransform>& lights = mLightManager->getLights();
// Don't use Camera::getViewMatrix, that one might be relative to another camera!
const osg::RefMatrix* viewMatrix = cv->getCurrentRenderStage()->getInitialViewMatrix();
const std::vector<LightManager::LightSourceViewBound>& lights = mLightManager->getLightsInViewSpace(cv->getCurrentCamera(), viewMatrix);
if (lights.size())
{
@ -288,10 +292,10 @@ namespace SceneUtil
osg::Matrixf mat = *cv->getModelViewMatrix();
transformBoundingSphere(mat, nodeBound);
std::vector<const LightManager::LightSourceTransform*> lightList;
LightManager::LightList lightList;
for (unsigned int i=0; i<lights.size(); ++i)
{
const LightManager::LightSourceTransform& l = lights[i];
const LightManager::LightSourceViewBound& l = lights[i];
if (l.mViewBound.intersects(nodeBound))
lightList.push_back(&l);
}

@ -74,18 +74,23 @@ namespace SceneUtil
// Called automatically by the LightSource's UpdateCallback
void addLight(LightSource* lightSource, osg::Matrix worldMat);
void prepareForCamera(osg::Camera* cam);
struct LightSourceTransform
{
LightSource* mLightSource;
osg::Matrix mWorldMatrix;
osg::BoundingSphere mViewBound;
};
const std::vector<LightSourceTransform>& getLights() const;
typedef std::vector<const LightSourceTransform*> LightList;
struct LightSourceViewBound
{
LightSource* mLightSource;
osg::BoundingSphere mViewBound;
};
const std::vector<LightSourceViewBound>& getLightsInViewSpace(osg::Camera* camera, const osg::RefMatrix* viewMatrix);
typedef std::vector<const LightSourceViewBound*> LightList;
osg::ref_ptr<osg::StateSet> getLightListStateSet(const LightList& lightList);
@ -98,7 +103,8 @@ namespace SceneUtil
// Lights collected from the scene graph. Only valid during the cull traversal.
std::vector<LightSourceTransform> mLights;
bool mLightsInViewSpace;
typedef std::vector<LightSourceViewBound> LightSourceViewBoundCollection;
std::map<osg::observer_ptr<osg::Camera>, LightSourceViewBoundCollection> mLightsInViewSpace;
// < Light list hash , StateSet >
typedef std::map<size_t, osg::ref_ptr<osg::StateSet> > LightStateSetMap;

@ -60,7 +60,7 @@ public:
RigGeometry::RigGeometry()
: mSkeleton(NULL)
, mFirstFrame(true)
, mLastFrameNumber(0)
, mBoundsFirstFrame(true)
{
setCullCallback(new UpdateRigGeometry);
@ -72,7 +72,7 @@ RigGeometry::RigGeometry(const RigGeometry &copy, const osg::CopyOp &copyop)
: osg::Geometry(copy, copyop)
, mSkeleton(NULL)
, mInfluenceMap(copy.mInfluenceMap)
, mFirstFrame(copy.mFirstFrame)
, mLastFrameNumber(0)
, mBoundsFirstFrame(copy.mBoundsFirstFrame)
{
setSourceGeometry(copy.mSourceGeometry);
@ -206,9 +206,12 @@ void RigGeometry::update(osg::NodeVisitor* nv)
return;
}
if (!mSkeleton->getActive() && !mFirstFrame)
if (!mSkeleton->getActive() && mLastFrameNumber != 0)
return;
mFirstFrame = false;
if (mLastFrameNumber == nv->getFrameStamp()->getFrameNumber())
return;
mLastFrameNumber = nv->getFrameStamp()->getFrameNumber();
mSkeleton->updateBoneMatrices(nv);

@ -64,7 +64,7 @@ namespace SceneUtil
BoneSphereMap mBoneSphereMap;
bool mFirstFrame;
unsigned int mLastFrameNumber;
bool mBoundsFirstFrame;
bool initFromParentSkeleton(osg::NodeVisitor* nv);

@ -13,6 +13,8 @@
</Widget>
<Widget type="MWScrollBar" skin="MW_HScroll" position="7 61 578 18" align="Left Top HStretch" name="CountSlider">
<Property key="MoveToClick" value="true"/>
<Property key="Page" value="1"/>
<Property key="WheelPage" value="1"/>
</Widget>
<Widget type="HBox" skin="" position="0 88 585 24" align="Right Bottom">
<Widget type="Widget" skin="" position="0 12 0 0">

Loading…
Cancel
Save