Merge branch 'master' into appveyor

This commit is contained in:
Alexander "Ace" Olofsson 2015-10-28 14:50:20 +01:00
commit 0d2bd31f8b
83 changed files with 1936 additions and 471 deletions

View file

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

View file

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

View file

@ -371,6 +371,57 @@ void CSMSettings::UserSettings::buildSettingModelDefaults()
"list go to the first/last item"); "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: * There are three types of values:

View file

@ -32,6 +32,23 @@ std::string CSMWorld::CellCoordinates::getId (const std::string& worldspace) con
return stream.str(); 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) bool CSMWorld::operator== (const CellCoordinates& left, const CellCoordinates& right)
{ {
return left.getX()==right.getX() && left.getY()==right.getY(); return left.getX()==right.getX() && left.getY()==right.getY();

View file

@ -28,6 +28,12 @@ namespace CSMWorld
std::string getId (const std::string& worldspace) const; std::string getId (const std::string& worldspace) const;
///< Return the ID for the cell at these coordinates. ///< 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); bool operator== (const CellCoordinates& left, const CellCoordinates& right);

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -83,7 +83,7 @@ CSMWorld::RefIdCollection::RefIdCollection()
mColumns.back().addColumn( mColumns.back().addColumn(
new NestedChildColumn (Columns::ColumnId_EffectId, ColumnBase::Display_EffectId)); new NestedChildColumn (Columns::ColumnId_EffectId, ColumnBase::Display_EffectId));
mColumns.back().addColumn( mColumns.back().addColumn(
new NestedChildColumn (Columns::ColumnId_SkillImpact, ColumnBase::Display_SkillImpact)); new NestedChildColumn (Columns::ColumnId_Skill, ColumnBase::Display_SkillId));
mColumns.back().addColumn( mColumns.back().addColumn(
new NestedChildColumn (Columns::ColumnId_Attribute, ColumnBase::Display_Attribute)); new NestedChildColumn (Columns::ColumnId_Attribute, ColumnBase::Display_Attribute));
mColumns.back().addColumn( mColumns.back().addColumn(
@ -193,8 +193,24 @@ CSMWorld::RefIdCollection::RefIdCollection()
new RefIdColumn (Columns::ColumnId_AiDuration, CSMWorld::ColumnBase::Display_Integer)); new RefIdColumn (Columns::ColumnId_AiDuration, CSMWorld::ColumnBase::Display_Integer));
mColumns.back().addColumn( mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_AiWanderToD, CSMWorld::ColumnBase::Display_Integer)); new RefIdColumn (Columns::ColumnId_AiWanderToD, CSMWorld::ColumnBase::Display_Integer));
mColumns.back().addColumn( mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_AiWanderIdle, CSMWorld::ColumnBase::Display_Integer)); 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_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( mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_AiWanderRepeat, CSMWorld::ColumnBase::Display_Boolean)); new RefIdColumn (Columns::ColumnId_AiWanderRepeat, CSMWorld::ColumnBase::Display_Boolean));
mColumns.back().addColumn( mColumns.back().addColumn(
@ -491,7 +507,7 @@ CSMWorld::RefIdCollection::RefIdCollection()
skillsMap.insert(std::make_pair(UniversalId::Type_Npc, new NpcSkillsRefIdAdapter())); skillsMap.insert(std::make_pair(UniversalId::Type_Npc, new NpcSkillsRefIdAdapter()));
mNestedAdapters.push_back (std::make_pair(&mColumns.back(), skillsMap)); mNestedAdapters.push_back (std::make_pair(&mColumns.back(), skillsMap));
mColumns.back().addColumn( 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( mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_UChar, CSMWorld::ColumnBase::Display_Integer)); new RefIdColumn (Columns::ColumnId_UChar, CSMWorld::ColumnBase::Display_Integer));

View file

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

View file

@ -97,7 +97,7 @@ CSVDoc::ViewManager::ViewManager (CSMDoc::DocumentManager& documentManager)
{ CSMWorld::ColumnBase::Display_Gender, CSMWorld::Columns::ColumnId_Gender, true }, { CSMWorld::ColumnBase::Display_Gender, CSMWorld::Columns::ColumnId_Gender, true },
{ CSMWorld::ColumnBase::Display_SoundGeneratorType, CSMWorld::Columns::ColumnId_SoundGeneratorType, false }, { CSMWorld::ColumnBase::Display_SoundGeneratorType, CSMWorld::Columns::ColumnId_SoundGeneratorType, false },
{ CSMWorld::ColumnBase::Display_School, CSMWorld::Columns::ColumnId_School, 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_EffectRange, CSMWorld::Columns::ColumnId_EffectRange, false },
{ CSMWorld::ColumnBase::Display_EffectId, CSMWorld::Columns::ColumnId_EffectId, false }, { CSMWorld::ColumnBase::Display_EffectId, CSMWorld::Columns::ColumnId_EffectId, false },
{ CSMWorld::ColumnBase::Display_PartRefType, CSMWorld::Columns::ColumnId_PartRefType, false }, { CSMWorld::ColumnBase::Display_PartRefType, CSMWorld::Columns::ColumnId_PartRefType, false },

View file

@ -5,14 +5,17 @@
#include <QApplication> #include <QApplication>
#include "../../model/world/data.hpp" #include "../../model/world/data.hpp"
#include "../../model/world/idtablebase.hpp"
#include "../../model/world/columns.hpp"
CSVFilter::EditWidget::EditWidget (CSMWorld::Data& data, QWidget *parent) CSVFilter::EditWidget::EditWidget (CSMWorld::Data& data, QWidget *parent)
: QLineEdit (parent), mParser (data) : QLineEdit (parent), mParser (data), mIsEmpty(true)
{ {
mPalette = palette(); mPalette = palette();
connect (this, SIGNAL (textChanged (const QString&)), this, SLOT (textChanged (const QString&))); 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&)), connect (model, SIGNAL (dataChanged (const QModelIndex &, const QModelIndex&)),
this, SLOT (filterDataChanged (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)), connect (model, SIGNAL (rowsInserted (const QModelIndex&, int, int)),
this, SLOT (filterRowsInserted (const QModelIndex&, int, int)), this, SLOT (filterRowsInserted (const QModelIndex&, int, int)),
Qt::QueuedConnection); Qt::QueuedConnection);
mStateColumnIndex = model->findColumnIndex(CSMWorld::Columns::ColumnId_Modification);
mDescColumnIndex = model->findColumnIndex(CSMWorld::Columns::ColumnId_Description);
} }
void CSVFilter::EditWidget::textChanged (const QString& text) 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())) if (mParser.parse (text.toUtf8().constData()))
{ {
setPalette (mPalette); setPalette (mPalette);
@ -45,6 +61,8 @@ void CSVFilter::EditWidget::textChanged (const QString& text)
void CSVFilter::EditWidget::filterDataChanged (const QModelIndex& topLeft, void CSVFilter::EditWidget::filterDataChanged (const QModelIndex& topLeft,
const QModelIndex& bottomRight) const QModelIndex& bottomRight)
{ {
for (int i = topLeft.column(); i <= bottomRight.column(); ++i)
if (i != mStateColumnIndex && i != mDescColumnIndex)
textChanged (text()); textChanged (text());
} }

View file

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

View file

@ -9,6 +9,7 @@
#include "../../model/world/columns.hpp" #include "../../model/world/columns.hpp"
#include "../../model/world/data.hpp" #include "../../model/world/data.hpp"
#include "../../model/world/refcollection.hpp" #include "../../model/world/refcollection.hpp"
#include "../../model/world/cellcoordinates.hpp"
#include "elements.hpp" #include "elements.hpp"
#include "terrainstorage.hpp" #include "terrainstorage.hpp"
@ -50,12 +51,20 @@ bool CSVRender::Cell::addObjects (int start, int end)
return modified; return modified;
} }
CSVRender::Cell::Cell (CSMWorld::Data& data, osg::Group* rootNode, const std::string& id) CSVRender::Cell::Cell (CSMWorld::Data& data, osg::Group* rootNode, const std::string& id,
: mData (data), mId (Misc::StringUtils::lowerCase (id)), mX(0), mY(0) 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; mCellNode = new osg::Group;
rootNode->addChild(mCellNode); rootNode->addChild(mCellNode);
if (!mDeleted)
{
CSMWorld::IdTable& references = dynamic_cast<CSMWorld::IdTable&> ( CSMWorld::IdTable& references = dynamic_cast<CSMWorld::IdTable&> (
*mData.getTableModel (CSMWorld::UniversalId::Type_References)); *mData.getTableModel (CSMWorld::UniversalId::Type_References));
@ -74,9 +83,7 @@ CSVRender::Cell::Cell (CSMWorld::Data& data, osg::Group* rootNode, const std::st
mTerrain.reset(new Terrain::TerrainGrid(mCellNode, data.getResourceSystem().get(), NULL, new TerrainStorage(mData), Element_Terrain<<1)); mTerrain.reset(new Terrain::TerrainGrid(mCellNode, data.getResourceSystem().get(), NULL, new TerrainStorage(mData), Element_Terrain<<1));
mTerrain->loadCell(esmLand.mX, mTerrain->loadCell(esmLand.mX,
esmLand.mY); esmLand.mY);
}
mX = esmLand.mX;
mY = esmLand.mY;
} }
} }
} }
@ -122,6 +129,9 @@ bool CSVRender::Cell::referenceableAboutToBeRemoved (const QModelIndex& parent,
bool CSVRender::Cell::referenceDataChanged (const QModelIndex& topLeft, bool CSVRender::Cell::referenceDataChanged (const QModelIndex& topLeft,
const QModelIndex& bottomRight) const QModelIndex& bottomRight)
{ {
if (mDeleted)
return false;
CSMWorld::IdTable& references = dynamic_cast<CSMWorld::IdTable&> ( CSMWorld::IdTable& references = dynamic_cast<CSMWorld::IdTable&> (
*mData.getTableModel (CSMWorld::UniversalId::Type_References)); *mData.getTableModel (CSMWorld::UniversalId::Type_References));
@ -189,6 +199,9 @@ bool CSVRender::Cell::referenceAboutToBeRemoved (const QModelIndex& parent, int
if (parent.isValid()) if (parent.isValid())
return false; return false;
if (mDeleted)
return false;
CSMWorld::IdTable& references = dynamic_cast<CSMWorld::IdTable&> ( CSMWorld::IdTable& references = dynamic_cast<CSMWorld::IdTable&> (
*mData.getTableModel (CSMWorld::UniversalId::Type_References)); *mData.getTableModel (CSMWorld::UniversalId::Type_References));
@ -209,5 +222,57 @@ bool CSVRender::Cell::referenceAdded (const QModelIndex& parent, int start, int
if (parent.isValid()) if (parent.isValid())
return false; return false;
if (mDeleted)
return false;
return addObjects (start, end); 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;
}

View file

@ -14,6 +14,7 @@
#endif #endif
#include "object.hpp" #include "object.hpp"
#include "cellarrow.hpp"
class QModelIndex; class QModelIndex;
@ -25,6 +26,7 @@ namespace osg
namespace CSMWorld namespace CSMWorld
{ {
class Data; class Data;
class CellCoordinates;
} }
namespace CSVRender namespace CSVRender
@ -36,8 +38,9 @@ namespace CSVRender
osg::ref_ptr<osg::Group> mCellNode; osg::ref_ptr<osg::Group> mCellNode;
std::map<std::string, Object *> mObjects; std::map<std::string, Object *> mObjects;
std::auto_ptr<Terrain::TerrainGrid> mTerrain; std::auto_ptr<Terrain::TerrainGrid> mTerrain;
int mX; CSMWorld::CellCoordinates mCoordinates;
int mY; std::auto_ptr<CellArrow> mCellArrows[4];
bool mDeleted;
/// Ignored if cell does not have an object with the given ID. /// Ignored if cell does not have an object with the given ID.
/// ///
@ -51,7 +54,19 @@ namespace CSVRender
public: 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(); ~Cell();
@ -75,6 +90,15 @@ namespace CSVRender
/// \return Did this call result in a modification of the visual representation of /// \return Did this call result in a modification of the visual representation of
/// this cell? /// this cell?
bool referenceAdded (const QModelIndex& parent, int start, int end); 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;
}; };
} }

View file

@ -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;
}

View file

@ -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

View file

@ -1,7 +1,13 @@
#include "editmode.hpp" #include "editmode.hpp"
#include "tagbase.hpp"
#include "worldspacewidget.hpp" #include "worldspacewidget.hpp"
CSVRender::WorldspaceWidget& CSVRender::EditMode::getWorldspaceWidget()
{
return *mWorldspaceWidget;
}
CSVRender::EditMode::EditMode (WorldspaceWidget *worldspaceWidget, const QIcon& icon, CSVRender::EditMode::EditMode (WorldspaceWidget *worldspaceWidget, const QIcon& icon,
unsigned int mask, const QString& tooltip, QWidget *parent) unsigned int mask, const QString& tooltip, QWidget *parent)
: ModeButton (icon, tooltip, parent), mWorldspaceWidget (worldspaceWidget), mMask (mask) : 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) void CSVRender::EditMode::activate (CSVWidget::SceneToolbar *toolbar)
{ {
mWorldspaceWidget->setInteractionMask (mMask); 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) {}

View file

@ -1,11 +1,14 @@
#ifndef CSV_RENDER_EDITMODE_H #ifndef CSV_RENDER_EDITMODE_H
#define CSV_RENDER_EDITMODE_H #define CSV_RENDER_EDITMODE_H
#include <osg/ref_ptr>
#include "../widget/modebutton.hpp" #include "../widget/modebutton.hpp"
namespace CSVRender namespace CSVRender
{ {
class WorldspaceWidget; class WorldspaceWidget;
class TagBase;
class EditMode : public CSVWidget::ModeButton class EditMode : public CSVWidget::ModeButton
{ {
@ -14,6 +17,10 @@ namespace CSVRender
WorldspaceWidget *mWorldspaceWidget; WorldspaceWidget *mWorldspaceWidget;
unsigned int mMask; unsigned int mMask;
protected:
WorldspaceWidget& getWorldspaceWidget();
public: public:
EditMode (WorldspaceWidget *worldspaceWidget, const QIcon& icon, unsigned int mask, EditMode (WorldspaceWidget *worldspaceWidget, const QIcon& icon, unsigned int mask,
@ -22,6 +29,51 @@ namespace CSVRender
unsigned int getInteractionMask() const; unsigned int getInteractionMask() const;
virtual void activate (CSVWidget::SceneToolbar *toolbar); 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);
}; };
} }

View file

@ -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);
}

View file

@ -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

View file

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

View file

@ -8,8 +8,9 @@
#include <osg/ref_ptr> #include <osg/ref_ptr>
#include <osg/Referenced> #include <osg/Referenced>
class QModelIndex; #include "tagbase.hpp"
class QModelIndex;
namespace osg namespace osg
{ {
@ -35,21 +36,19 @@ namespace CSMWorld
namespace CSVRender namespace CSVRender
{ {
class Object; 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 // 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: public:
ObjectHolder(Object* obj)
: mObject(obj) ObjectTag (Object* object);
{
}
Object* mObject; Object* mObject;
}; };
class Object class Object
{ {
const CSMWorld::Data& mData; const CSMWorld::Data& mData;

View file

@ -1,8 +1,10 @@
#include "pagedworldspacewidget.hpp" #include "pagedworldspacewidget.hpp"
#include <memory>
#include <sstream> #include <sstream>
#include <QMouseEvent> #include <QMouseEvent>
#include <QApplication>
#include <osgGA/TrackballManipulator> #include <osgGA/TrackballManipulator>
@ -21,20 +23,19 @@
bool CSVRender::PagedWorldspaceWidget::adjustCells() bool CSVRender::PagedWorldspaceWidget::adjustCells()
{ {
bool modified = false; bool modified = false;
bool wasEmpty = mCells.empty();
const CSMWorld::IdCollection<CSMWorld::Cell>& cells = mDocument.getData().getCells(); 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()); std::map<CSMWorld::CellCoordinates, Cell *>::iterator iter (mCells.begin());
while (iter!=mCells.end()) while (iter!=mCells.end())
{ {
int index = cells.searchId (iter->first.getId (mWorldspace)); if (!mSelection.has (iter->first))
if (!mSelection.has (iter->first) || index==-1 ||
cells.getRecord (index).mState==CSMWorld::RecordBase::State_Deleted)
{ {
// remove
delete iter->second; delete iter->second;
mCells.erase (iter++); mCells.erase (iter++);
@ -42,12 +43,33 @@ bool CSVRender::PagedWorldspaceWidget::adjustCells()
} }
else else
{ {
// check if name or region field has changed // 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
// TODO check if name or region field has changed (cell marker)
// FIXME: config setting // FIXME: config setting
//std::string name = cells.getRecord(index).get().mName; //std::string name = cells.getRecord(index).get().mName;
//std::string region = cells.getRecord(index).get().mRegion; //std::string region = cells.getRecord(index).get().mRegion;
// cell marker update goes here modified = true;
}
++iter; ++iter;
} }
@ -58,20 +80,43 @@ bool CSVRender::PagedWorldspaceWidget::adjustCells()
for (CSMWorld::CellSelection::Iterator iter (mSelection.begin()); iter!=mSelection.end(); for (CSMWorld::CellSelection::Iterator iter (mSelection.begin()); iter!=mSelection.end();
++iter) ++iter)
{ {
int index = cells.searchId (iter->getId (mWorldspace)); if (mCells.find (*iter)==mCells.end())
if (index > 0 && cells.getRecord (index).mState!=CSMWorld::RecordBase::State_Deleted &&
mCells.find (*iter)==mCells.end())
{ {
Cell *cell = new Cell (mDocument.getData(), mRootNode, addCellToScene (*iter);
iter->getId (mWorldspace));
mCells.insert (std::make_pair (*iter, cell));
modified = true; modified = true;
} }
} }
if (modified) 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); mView->setCameraManipulator(new osgGA::TrackballManipulator);
return modified; return modified;
@ -105,6 +150,76 @@ void CSVRender::PagedWorldspaceWidget::addEditModeSelectorButtons (
"terrain-move"); "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, void CSVRender::PagedWorldspaceWidget::referenceableDataChanged (const QModelIndex& topLeft,
const QModelIndex& bottomRight) const QModelIndex& bottomRight)
{ {
@ -184,6 +299,72 @@ std::string CSVRender::PagedWorldspaceWidget::getStartupInstruction()
return stream.str(); 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) CSVRender::PagedWorldspaceWidget::PagedWorldspaceWidget (QWidget* parent, CSMDoc::Document& document)
: WorldspaceWidget (document, parent), mDocument (document), mWorldspace ("std::default"), : WorldspaceWidget (document, parent), mDocument (document), mWorldspace ("std::default"),
mControlElements(NULL), mDisplayCellCoord(true) mControlElements(NULL), mDisplayCellCoord(true)
@ -314,6 +495,15 @@ unsigned int CSVRender::PagedWorldspaceWidget::getVisibilityMask() const
return WorldspaceWidget::getVisibilityMask() | mControlElements->getSelection(); 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::SceneToolToggle *CSVRender::PagedWorldspaceWidget::makeControlVisibilitySelector (
CSVWidget::SceneToolbar *parent) CSVWidget::SceneToolbar *parent)
{ {

View file

@ -53,6 +53,20 @@ namespace CSVRender
virtual std::string getStartupInstruction(); 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: public:
PagedWorldspaceWidget (QWidget *parent, CSMDoc::Document& document); PagedWorldspaceWidget (QWidget *parent, CSMDoc::Document& document);
@ -79,12 +93,17 @@ namespace CSVRender
virtual unsigned int getVisibilityMask() const; virtual unsigned int getVisibilityMask() const;
/// \param elementMask Elements to be affected by the clear operation
virtual void clearSelection (int elementMask);
protected: protected:
virtual void addVisibilitySelectorButtons (CSVWidget::SceneToolToggle2 *tool); virtual void addVisibilitySelectorButtons (CSVWidget::SceneToolToggle2 *tool);
virtual void addEditModeSelectorButtons (CSVWidget::SceneToolMode *tool); virtual void addEditModeSelectorButtons (CSVWidget::SceneToolMode *tool);
virtual void handleMouseClick (osg::ref_ptr<TagBase> tag, const std::string& button, bool shift);
signals: signals:
void cellSelectionChanged (const CSMWorld::CellSelection& selection); void cellSelectionChanged (const CSMWorld::CellSelection& selection);

View file

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

View file

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

View file

@ -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

View file

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

View file

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

View file

@ -9,6 +9,7 @@
#include <QDropEvent> #include <QDropEvent>
#include <QMouseEvent> #include <QMouseEvent>
#include <QKeyEvent> #include <QKeyEvent>
#include <QApplication>
#include <osgGA/TrackballManipulator> #include <osgGA/TrackballManipulator>
#include <osgGA/FirstPersonManipulator> #include <osgGA/FirstPersonManipulator>
@ -18,6 +19,8 @@
#include "../../model/world/universalid.hpp" #include "../../model/world/universalid.hpp"
#include "../../model/world/idtable.hpp" #include "../../model/world/idtable.hpp"
#include "../../model/settings/usersettings.hpp"
#include "../widget/scenetoolmode.hpp" #include "../widget/scenetoolmode.hpp"
#include "../widget/scenetooltoggle2.hpp" #include "../widget/scenetooltoggle2.hpp"
#include "../widget/scenetoolrun.hpp" #include "../widget/scenetoolrun.hpp"
@ -25,10 +28,22 @@
#include "object.hpp" #include "object.hpp"
#include "elements.hpp" #include "elements.hpp"
#include "editmode.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) CSVRender::WorldspaceWidget::WorldspaceWidget (CSMDoc::Document& document, QWidget* parent)
: SceneWidget (document.getData().getResourceSystem(), parent), mSceneElements(0), mRun(0), mDocument(document), : SceneWidget (document.getData().getResourceSystem(), parent), mSceneElements(0), mRun(0), mDocument(document),
mInteractionMask (0) mInteractionMask (0), mEditMode (0), mLocked (false), mDragging (false)
{ {
setAcceptDrops(true); setAcceptDrops(true);
@ -59,6 +74,17 @@ CSVRender::WorldspaceWidget::WorldspaceWidget (CSMDoc::Document& document, QWidg
this, SLOT (debugProfileDataChanged (const QModelIndex&, const QModelIndex&))); this, SLOT (debugProfileDataChanged (const QModelIndex&, const QModelIndex&)));
connect (debugProfiles, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)), connect (debugProfiles, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)),
this, SLOT (debugProfileAboutToBeRemoved (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 () CSVRender::WorldspaceWidget::~WorldspaceWidget ()
@ -178,11 +204,14 @@ CSVWidget::SceneToolRun *CSVRender::WorldspaceWidget::makeRunTool (
CSVWidget::SceneToolMode *CSVRender::WorldspaceWidget::makeEditModeSelector ( CSVWidget::SceneToolMode *CSVRender::WorldspaceWidget::makeEditModeSelector (
CSVWidget::SceneToolbar *parent) 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 ( CSVRender::WorldspaceWidget::DropType CSVRender::WorldspaceWidget::getDropType (
@ -254,6 +283,26 @@ unsigned int CSVRender::WorldspaceWidget::getInteractionMask() const
return mInteractionMask & getVisibilityMask(); 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 ( void CSVRender::WorldspaceWidget::addVisibilitySelectorButtons (
CSVWidget::SceneToolToggle2 *tool) CSVWidget::SceneToolToggle2 *tool)
{ {
@ -265,9 +314,7 @@ void CSVRender::WorldspaceWidget::addVisibilitySelectorButtons (
void CSVRender::WorldspaceWidget::addEditModeSelectorButtons (CSVWidget::SceneToolMode *tool) void CSVRender::WorldspaceWidget::addEditModeSelectorButtons (CSVWidget::SceneToolMode *tool)
{ {
/// \todo replace EditMode with suitable subclasses /// \todo replace EditMode with suitable subclasses
tool->addButton ( tool->addButton (new InstanceMode (this, tool), "object");
new EditMode (this, QIcon (":placeholder"), Element_Reference, "Instance editing"),
"object");
tool->addButton ( tool->addButton (
new EditMode (this, QIcon (":placeholder"), Element_Pathgrid, "Pathgrid editing"), new EditMode (this, QIcon (":placeholder"), Element_Pathgrid, "Pathgrid editing"),
"pathgrid"); "pathgrid");
@ -288,6 +335,95 @@ void CSVRender::WorldspaceWidget::dragMoveEvent(QDragMoveEvent *event)
event->accept(); 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) void CSVRender::WorldspaceWidget::dropEvent (QDropEvent* event)
{ {
const CSMWorld::TableMimeData* mime = dynamic_cast<const CSMWorld::TableMimeData*> (event->mimeData()); 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() void CSVRender::WorldspaceWidget::elementSelectionChanged()
{ {
setVisibilityMask (getVisibilityMask()); setVisibilityMask (getVisibilityMask());
@ -365,74 +507,97 @@ void CSVRender::WorldspaceWidget::updateOverlay()
void CSVRender::WorldspaceWidget::mouseMoveEvent (QMouseEvent *event) void CSVRender::WorldspaceWidget::mouseMoveEvent (QMouseEvent *event)
{ {
if(event->buttons() & Qt::RightButton) if (!mDragging)
{ {
//mMouse->mouseMoveEvent(event); if (mDragMode=="p-navi" || mDragMode=="s-navi")
{
}
else if (mDragMode=="p-edit" || mDragMode=="s-edit" || mDragMode=="select")
{
osg::ref_ptr<TagBase> tag = mousePick (event);
EditMode& editMode = dynamic_cast<CSVRender::EditMode&> (*mEditMode->getCurrent());
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);
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;
mDragX = event->x();
mDragY = height() - event->y();
double factor = mDragFactor;
if (event->modifiers() & Qt::ShiftModifier)
factor *= mDragShiftFactor;
EditMode& editMode = dynamic_cast<CSVRender::EditMode&> (*mEditMode->getCurrent());
editMode.drag (diffX, diffY, factor);
} }
RenderWidget::mouseMoveEvent(event);
} }
void CSVRender::WorldspaceWidget::mousePressEvent (QMouseEvent *event) void CSVRender::WorldspaceWidget::mousePressEvent (QMouseEvent *event)
{ {
if (event->button() != Qt::RightButton) std::string button = mapButton (event);
return;
// (0,0) is considered the lower left corner of an OpenGL window if (!mDragging)
int x = event->x(); mDragMode = button;
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 (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;
}
}
// must be terrain, report coordinates
std::cout << "Terrain hit at " << intersection.getWorldIntersectPoint().x() << " " << intersection.getWorldIntersectPoint().y() << std::endl;
return;
}
} }
void CSVRender::WorldspaceWidget::mouseReleaseEvent (QMouseEvent *event) 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")
if(!getViewport())
{ {
SceneWidget::mouseReleaseEvent(event);
return;
} }
*/ else if (mDragMode=="p-edit" || mDragMode=="s-edit" || mDragMode=="select")
//mMouse->mouseReleaseEvent(event); {
EditMode& editMode = dynamic_cast<CSVRender::EditMode&> (*mEditMode->getCurrent());
editMode.dragCompleted();
mDragging = false;
} }
RenderWidget::mouseReleaseEvent(event); }
else
{
if (button=="p-navi" || button=="s-navi")
{
}
else if (button=="p-edit" || button=="s-edit" || button=="select")
{
osg::ref_ptr<TagBase> tag = mousePick (event);
handleMouseClick (tag, button, event->modifiers() & Qt::ShiftModifier);
}
}
mDragMode.clear();
} }
void CSVRender::WorldspaceWidget::mouseDoubleClickEvent (QMouseEvent *event) void CSVRender::WorldspaceWidget::mouseDoubleClickEvent (QMouseEvent *event)
@ -441,21 +606,47 @@ void CSVRender::WorldspaceWidget::mouseDoubleClickEvent (QMouseEvent *event)
{ {
//mMouse->mouseDoubleClickEvent(event); //mMouse->mouseDoubleClickEvent(event);
} }
//SceneWidget::mouseDoubleClickEvent(event);
} }
void CSVRender::WorldspaceWidget::wheelEvent (QWheelEvent *event) void CSVRender::WorldspaceWidget::wheelEvent (QWheelEvent *event)
{ {
//if(!mMouse->wheelEvent(event)) if (mDragging)
RenderWidget::wheelEvent(event); {
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) void CSVRender::WorldspaceWidget::keyPressEvent (QKeyEvent *event)
{ {
if(event->key() == Qt::Key_Escape) if(event->key() == Qt::Key_Escape)
{ {
//mMouse->cancelDrag(); if (mDragging)
{
EditMode& editMode = dynamic_cast<CSVRender::EditMode&> (*mEditMode->getCurrent());
editMode.dragAborted();
mDragging = false;
}
} }
else else
RenderWidget::keyPressEvent(event); 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);
}

View file

@ -1,12 +1,15 @@
#ifndef OPENCS_VIEW_WORLDSPACEWIDGET_H #ifndef OPENCS_VIEW_WORLDSPACEWIDGET_H
#define OPENCS_VIEW_WORLDSPACEWIDGET_H #define OPENCS_VIEW_WORLDSPACEWIDGET_H
#include <map>
#include <boost/shared_ptr.hpp> #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 "scenewidget.hpp"
#include <apps/opencs/model/world/tablemimedata.hpp> #include "elements.hpp"
namespace CSMWorld namespace CSMWorld
{ {
@ -23,6 +26,9 @@ namespace CSVWidget
namespace CSVRender namespace CSVRender
{ {
class TagBase;
class CellArrow;
class WorldspaceWidget : public SceneWidget class WorldspaceWidget : public SceneWidget
{ {
Q_OBJECT Q_OBJECT
@ -31,6 +37,16 @@ namespace CSVRender
CSVWidget::SceneToolRun *mRun; CSVWidget::SceneToolRun *mRun;
CSMDoc::Document& mDocument; CSMDoc::Document& mDocument;
unsigned int mInteractionMask; 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: public:
@ -93,14 +109,21 @@ namespace CSVRender
/// marked for interaction. /// marked for interaction.
unsigned int getInteractionMask() const; 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: protected:
virtual void addVisibilitySelectorButtons (CSVWidget::SceneToolToggle2 *tool); virtual void addVisibilitySelectorButtons (CSVWidget::SceneToolToggle2 *tool);
virtual void addEditModeSelectorButtons (CSVWidget::SceneToolMode *tool); virtual void addEditModeSelectorButtons (CSVWidget::SceneToolMode *tool);
CSMDoc::Document& getDocument();
virtual void updateOverlay(); virtual void updateOverlay();
virtual void mouseMoveEvent (QMouseEvent *event); virtual void mouseMoveEvent (QMouseEvent *event);
@ -110,6 +133,9 @@ namespace CSVRender
virtual void wheelEvent (QWheelEvent *event); virtual void wheelEvent (QWheelEvent *event);
virtual void keyPressEvent (QKeyEvent *event); virtual void keyPressEvent (QKeyEvent *event);
virtual void handleMouseClick (osg::ref_ptr<TagBase> tag, const std::string& button,
bool shift);
private: private:
void dragEnterEvent(QDragEnterEvent *event); void dragEnterEvent(QDragEnterEvent *event);
@ -118,6 +144,13 @@ namespace CSVRender
void dragMoveEvent(QDragMoveEvent *event); 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; virtual std::string getStartupInstruction() = 0;
private slots: private slots:
@ -144,6 +177,7 @@ namespace CSVRender
void debugProfileAboutToBeRemoved (const QModelIndex& parent, int start, int end); void debugProfileAboutToBeRemoved (const QModelIndex& parent, int start, int end);
void editModeChanged (const std::string& id);
protected slots: protected slots:

View file

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

View file

@ -41,6 +41,9 @@ namespace CSVWidget
/// The ownership of \a button is transferred to *this. /// The ownership of \a button is transferred to *this.
void addButton (ModeButton *button, const std::string& id); 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: signals:
void modeChanged (const std::string& id); void modeChanged (const std::string& id);

View file

@ -47,6 +47,16 @@ std::string CSVWorld::GenericCreator::getId() const
return mId->text().toUtf8().constData(); 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::configureCreateCommand (CSMWorld::CreateCommand& command) const {}
void CSVWorld::GenericCreator::pushCommand (std::auto_ptr<CSMWorld::CreateCommand> command, void CSVWorld::GenericCreator::pushCommand (std::auto_ptr<CSMWorld::CreateCommand> command,

View file

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

View file

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

View file

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

View file

@ -198,16 +198,33 @@ void CSVWorld::ScriptSubView::useHint (const std::string& hint)
if (hint.empty()) if (hint.empty())
return; return;
if (hint[0]=='l') unsigned line = 0, column = 0;
{ char c;
std::istringstream stream (hint.c_str()+1); std::istringstream stream (hint.c_str()+1);
switch(hint[0]){
char ignore; case 'R':
int line; case 'r':
int column;
if (stream >> ignore >> line >> column)
{ {
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(); QTextCursor cursor = mEditor->textCursor();
cursor.movePosition (QTextCursor::Start); cursor.movePosition (QTextCursor::Start);
@ -216,8 +233,6 @@ void CSVWorld::ScriptSubView::useHint (const std::string& hint)
mEditor->setFocus(); mEditor->setFocus();
mEditor->setTextCursor (cursor); mEditor->setTextCursor (cursor);
}
}
} }
void CSVWorld::ScriptSubView::textChanged() void CSVWorld::ScriptSubView::textChanged()

View file

@ -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;
}

View file

@ -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

View file

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

View file

@ -234,6 +234,11 @@ namespace MWClass
std::pair<int, std::string> Light::canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const 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::InventoryStore& invStore = npc.getClass().getInventoryStore(npc);
MWWorld::ContainerStoreIterator weapon = invStore.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); MWWorld::ContainerStoreIterator weapon = invStore.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);

View file

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

View file

@ -170,25 +170,25 @@ namespace MWGui
mCommandLine->setFontName(fntName); 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) void Console::printOK(const std::string &msg)
{ {
print("#FF00FF" + msg + "\n"); print(msg + "\n", "#FF00FF");
} }
void Console::printError(const std::string &msg) void Console::printError(const std::string &msg)
{ {
print("#FF2222" + msg + "\n"); print(msg + "\n", "#FF2222");
} }
void Console::execute (const std::string& command) void Console::execute (const std::string& command)
{ {
// Log the command // Log the command
print("#FFFFFF> " + command + "\n"); print("> " + command + "\n");
Compiler::Locals locals; Compiler::Locals locals;
Compiler::Output output (locals); Compiler::Output output (locals);

View file

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

View file

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

View file

@ -235,13 +235,38 @@ namespace MWGui
draw(); 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)
{ {
// 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;
}
bool showWallpaper = (MWBase::Environment::get().getStateManager()->getState() bool showWallpaper = (MWBase::Environment::get().getStateManager()->getState()
== MWBase::StateManager::State_NoGame); == MWBase::StateManager::State_NoGame);
if (!showWallpaper && diff < initialDelay*1000)
return false;
return true;
}
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) if (showWallpaper && mTimer.time_m() > mLastWallpaperChangeTime + 5000*1)
{ {
mLastWallpaperChangeTime = mTimer.time_m(); mLastWallpaperChangeTime = mTimer.time_m();
@ -269,6 +294,5 @@ namespace MWGui
mLastRenderTime = mTimer.time_m(); mLastRenderTime = mTimer.time_m();
} }
}
} }

View file

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

View file

@ -1,5 +1,7 @@
#include "spellmodel.hpp" #include "spellmodel.hpp"
#include <iostream>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
@ -79,8 +81,13 @@ namespace MWGui
const std::string enchantId = item.getClass().getEnchantment(item); const std::string enchantId = item.getClass().getEnchantment(item);
if (enchantId.empty()) if (enchantId.empty())
continue; continue;
const ESM::Enchantment* enchant = const ESM::Enchantment* enchant = esmStore.get<ESM::Enchantment>().search(enchantId);
esmStore.get<ESM::Enchantment>().find(item.getClass().getEnchantment(item)); 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) if (enchant->mData.mType != ESM::Enchantment::WhenUsed && enchant->mData.mType != ESM::Enchantment::CastOnce)
continue; continue;

View file

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

View file

@ -20,8 +20,9 @@ namespace MWMechanics
struct AiFollowStorage : AiTemporaryBase struct AiFollowStorage : AiTemporaryBase
{ {
float mTimer; float mTimer;
bool mMoving;
AiFollowStorage() : mTimer(0.f) {} AiFollowStorage() : mTimer(0.f), mMoving(false) {}
}; };
int AiFollow::mFollowIndexCounter = 0; int AiFollow::mFollowIndexCounter = 0;
@ -64,10 +65,11 @@ bool AiFollow::execute (const MWWorld::Ptr& actor, CharacterController& characte
actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Nothing); 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 // AiFollow requires the target to be in range and within sight for the initial activation
if (!mActive) if (!mActive)
{ {
AiFollowStorage& storage = state.get<AiFollowStorage>();
storage.mTimer -= duration; storage.mTimer -= duration;
if (storage.mTimer < 0) if (storage.mTimer < 0)
@ -126,7 +128,15 @@ bool AiFollow::execute (const MWWorld::Ptr& actor, CharacterController& characte
//Set the target destination from the actor //Set the target destination from the actor
ESM::Pathgrid::Point dest = target.getRefData().getPosition().pos; 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; 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 //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 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 actor.getClass().getCreatureStats(actor).setMovementFlag(MWMechanics::CreatureStats::Flag_Run, false); //make NPC walk
return false; return false;

View file

@ -88,7 +88,8 @@ void MWMechanics::AiPackage::evadeObstacles(const MWWorld::Ptr& actor, float dur
MWWorld::Ptr door = getNearbyDoor(actor); MWWorld::Ptr door = getNearbyDoor(actor);
if (door != MWWorld::Ptr()) // NOTE: checks interior cells only 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); MWBase::Environment::get().getWorld()->activateDoor(door, 1);
} }
} }

View file

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

View file

@ -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) if (spell->mData.mType != ESM::Spell::ST_Spell)
return 100; return 100;
@ -817,6 +820,10 @@ namespace MWMechanics
sndMgr->playSound3D(mCaster, "Spell Failure " + schools[school], 1.0f, 1.0f); sndMgr->playSound3D(mCaster, "Spell Failure " + schools[school], 1.0f, 1.0f);
return false; 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)) if (mCaster == getPlayer() && spellIncreasesSkill(spell))

View file

@ -312,7 +312,7 @@ namespace MWMechanics
bool Spells::canUsePower(const std::string &power) const 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()) if (it == mUsedPowers.end() || it->second + 24 <= MWBase::Environment::get().getWorld()->getTimeStamp())
return true; return true;
else else
@ -321,7 +321,7 @@ namespace MWMechanics
void Spells::usePower(const std::string &power) 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) void Spells::readState(const ESM::SpellState &state)

View file

@ -1275,15 +1275,26 @@ namespace MWRender
material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,1)); material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,1));
stateset->setAttributeAndModes(material, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); 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); mObjectRoot->setStateSet(stateset);
} }
else else
{ {
mObjectRoot->setStateSet(NULL); 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) void Animation::setLightEffect(float effect)
@ -1320,6 +1331,8 @@ namespace MWRender
{ {
mHeadController = NULL; mHeadController = NULL;
if (mPtr.getClass().isBipedal(mPtr))
{
NodeMap::iterator found = mNodeMap.find("bip01 head"); NodeMap::iterator found = mNodeMap.find("bip01 head");
if (found != mNodeMap.end() && dynamic_cast<osg::MatrixTransform*>(found->second.get())) if (found != mNodeMap.end() && dynamic_cast<osg::MatrixTransform*>(found->second.get()))
{ {
@ -1329,6 +1342,7 @@ namespace MWRender
mActiveControllers.insert(std::make_pair(node, mHeadController)); mActiveControllers.insert(std::make_pair(node, mHeadController));
} }
} }
}
void Animation::setHeadPitch(float pitchRadians) void Animation::setHeadPitch(float pitchRadians)
{ {

View file

@ -307,6 +307,9 @@ protected:
void addGlow(osg::ref_ptr<osg::Node> node, osg::Vec4f glowColor); 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: public:
Animation(const MWWorld::Ptr &ptr, osg::ref_ptr<osg::Group> parentNode, Resource::ResourceSystem* resourceSystem); Animation(const MWWorld::Ptr &ptr, osg::ref_ptr<osg::Group> parentNode, Resource::ResourceSystem* resourceSystem);

View file

@ -2,6 +2,9 @@
#include <osg/UserDataContainer> #include <osg/UserDataContainer>
#include <osg/MatrixTransform> #include <osg/MatrixTransform>
#include <osg/Depth>
#include <osgUtil/RenderBin>
#include <components/misc/rng.hpp> #include <components/misc/rng.hpp>
@ -28,6 +31,7 @@
#include "camera.hpp" #include "camera.hpp"
#include "rotatecontroller.hpp" #include "rotatecontroller.hpp"
#include "renderbin.hpp"
namespace namespace
{ {
@ -303,6 +307,50 @@ void NpcAnimation::setViewMode(NpcAnimation::ViewMode viewMode)
mViewMode = viewMode; mViewMode = viewMode;
rebuild(); 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() void NpcAnimation::rebuild()

View file

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

View file

@ -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. /// 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 enum RenderBins
{ {
RenderBin_Sky = -1, RenderBin_Sky = -1,
RenderBin_Default = 0, RenderBin_Default = 0, // osg::StateSet::OPAQUE_BIN
RenderBin_Water = 9, RenderBin_Water = 9,
RenderBin_OcclusionQuery = 10, RenderBin_DepthSorted = 10, // osg::StateSet::TRANSPARENT_BIN
RenderBin_SunGlare = 11 RenderBin_OcclusionQuery = 11,
RenderBin_FirstPerson = 12,
RenderBin_SunGlare = 13
}; };
} }

View file

@ -493,6 +493,8 @@ namespace MWRender
texture->setInternalFormat(GL_RGB); texture->setInternalFormat(GL_RGB);
texture->setTextureSize(w, h); texture->setTextureSize(w, h);
texture->setResizeNonPowerOfTwoHint(false); 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); rttCamera->attach(osg::Camera::COLOR_BUFFER, texture);
image->setDataType(GL_UNSIGNED_BYTE); image->setDataType(GL_UNSIGNED_BYTE);

View file

@ -724,22 +724,30 @@ private:
, mTimeOfDayFade(1.f) , mTimeOfDayFade(1.f)
, mGlareView(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) virtual void operator ()(osg::Node* node, osg::NodeVisitor* nv)
{ {
osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>(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()); 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); float value = 1.f - std::min(1.f, angleRadians / angleMaxRadians);
float fade = value * mSunGlareFaderMax;
const float sunGlareFaderMax = 0.5f;
float fade = value * sunGlareFaderMax;
fade *= mTimeOfDayFade * mGlareView * visibleRatio; fade *= mTimeOfDayFade * mGlareView * visibleRatio;
@ -754,17 +762,8 @@ private:
osg::ref_ptr<osg::Material> mat (createUnlitMaterial()); 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->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); stateset->setAttributeAndModes(mat, osg::StateAttribute::ON);
@ -785,10 +784,10 @@ private:
} }
private: private:
float getAngleToSunInRadians(osg::Camera* camera) const float getAngleToSunInRadians(const osg::Matrix& viewMatrix) const
{ {
osg::Vec3d eye, center, up; osg::Vec3d eye, center, up;
camera->getViewMatrixAsLookAt(eye, center, up); viewMatrix.getLookAt(eye, center, up);
osg::Vec3d forward = center - eye; osg::Vec3d forward = center - eye;
osg::Vec3d sun = mSunTransform->getPosition(); osg::Vec3d sun = mSunTransform->getPosition();
@ -802,6 +801,9 @@ private:
osg::ref_ptr<osg::PositionAttitudeTransform> mSunTransform; osg::ref_ptr<osg::PositionAttitudeTransform> mSunTransform;
float mTimeOfDayFade; float mTimeOfDayFade;
float mGlareView; float mGlareView;
osg::Vec4f mColor;
float mSunGlareFaderMax;
float mSunGlareFaderAngleMax;
}; };
osg::ref_ptr<Updater> mUpdater; osg::ref_ptr<Updater> mUpdater;

View file

@ -12,6 +12,7 @@
#include "../mwworld/ptr.hpp" #include "../mwworld/ptr.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "../mwworld/manualref.hpp"
namespace MWScript namespace MWScript
{ {
@ -42,9 +43,9 @@ namespace MWScript
} }
else 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; reference = true;
} }
@ -82,7 +83,8 @@ namespace MWScript
store.get<ESM::Probe>().search (name) || store.get<ESM::Probe>().search (name) ||
store.get<ESM::Repair>().search (name) || store.get<ESM::Repair>().search (name) ||
store.get<ESM::Static>().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 bool CompilerContext::isJournalId (const std::string& name) const

View file

@ -140,7 +140,11 @@ void MWWorld::ContainerStore::unstack(const Ptr &ptr, const Ptr& container)
{ {
if (ptr.getRefData().getCount() <= 1) if (ptr.getRefData().getCount() <= 1)
return; 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); remove(ptr, ptr.getRefData().getCount()-1, container);
} }
@ -411,6 +415,7 @@ void MWWorld::ContainerStore::fill (const ESM::InventoryList& items, const std::
void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std::string& owner, void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std::string& owner,
int count, bool topLevel, const std::string& levItem) int count, bool topLevel, const std::string& levItem)
{ {
try {
ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), id, count); ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), id, count);
if (ref.getPtr().getTypeName()==typeid (ESM::ItemLevList).name()) if (ref.getPtr().getTypeName()==typeid (ESM::ItemLevList).name())
@ -446,6 +451,12 @@ void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std::
ref.getPtr().getCellRef().setOwner(owner); ref.getPtr().getCellRef().setOwner(owner);
addImp (ref.getPtr(), count); addImp (ref.getPtr(), count);
} }
}
catch (const std::exception& e)
{
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) void MWWorld::ContainerStore::restock (const ESM::InventoryList& items, const MWWorld::Ptr& ptr, const std::string& owner)

View file

@ -645,8 +645,15 @@ void MWWorld::InventoryStore::updateRechargingItems()
{ {
if (it->getClass().getEnchantment(*it) != "") if (it->getClass().getEnchantment(*it) != "")
{ {
const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().find( std::string enchantmentId = it->getClass().getEnchantment(*it);
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 if (enchantment->mData.mType == ESM::Enchantment::WhenUsed
|| enchantment->mData.mType == ESM::Enchantment::WhenStrikes) || enchantment->mData.mType == ESM::Enchantment::WhenStrikes)
mRechargingItems.push_back(std::make_pair(it, static_cast<float>(enchantment->mData.mCharge))); mRechargingItems.push_back(std::make_pair(it, static_cast<float>(enchantment->mData.mCharge)));

View file

@ -1029,7 +1029,8 @@ namespace MWWorld
// Script // 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; ESM::Script scpt;
scpt.load(esm); scpt.load(esm);
Misc::StringUtils::toLower(scpt.mId); Misc::StringUtils::toLower(scpt.mId);
@ -1045,7 +1046,8 @@ namespace MWWorld
// StartScript // 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; ESM::StartScript s;
s.load(esm); s.load(esm);

View file

@ -204,6 +204,10 @@ namespace MWWorld
mLevitationEnabled = true; mLevitationEnabled = true;
mTeleportEnabled = true; mTeleportEnabled = true;
mGodMode = false;
mScriptsEnabled = true;
mSky = true;
// Rebuild player // Rebuild player
setupPlayer(); setupPlayer();
@ -297,9 +301,6 @@ namespace MWWorld
mDoorStates.clear(); mDoorStates.clear();
mGodMode = false;
mScriptsEnabled = true;
mSky = true;
mTeleportEnabled = true; mTeleportEnabled = true;
mLevitationEnabled = true; mLevitationEnabled = true;
@ -2659,10 +2660,6 @@ namespace MWWorld
{ {
const ESM::Spell* spell = getStore().get<ESM::Spell>().find(selectedSpell); 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); cast.cast(spell);
} }
else if (actor.getClass().hasInventoryStore(actor)) else if (actor.getClass().hasInventoryStore(actor))

View file

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

View file

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

View file

@ -555,7 +555,7 @@ namespace Compiler
} }
if (mAllowExpression && mState==BeginState && 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); scanner.putbackSpecial (code, loc);
parseExpression (scanner, loc); parseExpression (scanner, loc);

View file

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

View file

@ -132,7 +132,34 @@ namespace Interpreter
throw std::runtime_error (error.str()); 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() Interpreter::~Interpreter()
@ -202,6 +229,10 @@ namespace Interpreter
{ {
assert (codeSize>=4); assert (codeSize>=4);
begin();
try
{
mRuntime.configure (code, codeSize, context); mRuntime.configure (code, codeSize, context);
int opcodes = static_cast<int> (code[0]); int opcodes = static_cast<int> (code[0]);
@ -214,7 +245,13 @@ namespace Interpreter
mRuntime.setPC (mRuntime.getPC()+1); mRuntime.setPC (mRuntime.getPC()+1);
execute (code); execute (code);
} }
}
catch (...)
{
end();
throw;
}
mRuntime.clear(); end();
} }
} }

View file

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

View file

@ -57,8 +57,8 @@ namespace
} }
} }
// Collect all properties affecting the given node that should be applied to an osg::Material. // Collect all properties affecting the given drawable that should be handled on drawable basis rather than on the node hierarchy above it.
void collectMaterialProperties(const Nif::Node* nifNode, std::vector<const Nif::Property*>& out) void collectDrawableProperties(const Nif::Node* nifNode, std::vector<const Nif::Property*>& out)
{ {
const Nif::PropertyList& props = nifNode->props; const Nif::PropertyList& props = nifNode->props;
for (size_t i = 0; i <props.length();++i) for (size_t i = 0; i <props.length();++i)
@ -70,6 +70,7 @@ namespace
case Nif::RC_NiMaterialProperty: case Nif::RC_NiMaterialProperty:
case Nif::RC_NiVertexColorProperty: case Nif::RC_NiVertexColorProperty:
case Nif::RC_NiSpecularProperty: case Nif::RC_NiSpecularProperty:
case Nif::RC_NiAlphaProperty:
out.push_back(props[i].getPtr()); out.push_back(props[i].getPtr());
break; break;
default: default:
@ -78,7 +79,7 @@ namespace
} }
} }
if (nifNode->parent) if (nifNode->parent)
collectMaterialProperties(nifNode->parent, out); collectDrawableProperties(nifNode->parent, out);
} }
class FrameSwitch : public osg::Group 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 // 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. // 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 class BillboardCallback : public osg::NodeCallback
{ {
public: public:
@ -160,24 +162,34 @@ namespace
struct UpdateMorphGeometry : public osg::Drawable::CullCallback struct UpdateMorphGeometry : public osg::Drawable::CullCallback
{ {
UpdateMorphGeometry() UpdateMorphGeometry()
: mLastFrameNumber(0)
{ {
} }
UpdateMorphGeometry(const UpdateMorphGeometry& copy, const osg::CopyOp& copyop) UpdateMorphGeometry(const UpdateMorphGeometry& copy, const osg::CopyOp& copyop)
: osg::Drawable::CullCallback(copy, copyop) : osg::Drawable::CullCallback(copy, copyop)
, mLastFrameNumber(0)
{ {
} }
META_Object(NifOsg, UpdateMorphGeometry) 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); osgAnimation::MorphGeometry* geom = static_cast<osgAnimation::MorphGeometry*>(drw);
if (!geom) if (!geom)
return false; return false;
if (mLastFrameNumber == nv->getFrameStamp()->getFrameNumber())
return false;
mLastFrameNumber = nv->getFrameStamp()->getFrameNumber();
geom->transformSoftwareMethod(); geom->transformSoftwareMethod();
return false; 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 // 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); osg::ref_ptr<osg::Geode> geode (new osg::Geode);
geode->addDrawable(partsys); geode->addDrawable(partsys);
std::vector<const Nif::Property*> materialProps; std::vector<const Nif::Property*> drawableProps;
collectMaterialProperties(nifNode, materialProps); collectDrawableProperties(nifNode, drawableProps);
applyMaterialProperties(parentNode, materialProps, composite, true, animflags); applyDrawableProperties(parentNode, drawableProps, composite, true, animflags);
// Particles don't have normals, so can't be diffuse lit. // Particles don't have normals, so can't be diffuse lit.
osg::Material* mat = static_cast<osg::Material*>(parentNode->getStateSet()->getAttribute(osg::StateAttribute::MATERIAL)); 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. // - 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 // - there are 3 "overlapping" nif properties that all affect the osg::Material, handling them
// above the actual renderable would be tedious. // above the actual renderable would be tedious.
std::vector<const Nif::Property*> materialProps; std::vector<const Nif::Property*> drawableProps;
collectMaterialProperties(triShape, materialProps); collectDrawableProperties(triShape, drawableProps);
applyMaterialProperties(parentNode, materialProps, composite, !data->colors->empty(), animflags); 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) 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_NiVertexColorProperty:
case Nif::RC_NiSpecularProperty: 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; break;
} }
case Nif::RC_NiAlphaProperty: case Nif::RC_NiAlphaProperty:
{ {
const Nif::NiAlphaProperty* alphaprop = static_cast<const Nif::NiAlphaProperty*>(property); // Handled on drawable level to prevent RenderBin nesting issues
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);
break; break;
} }
case Nif::RC_NiTexturingProperty: 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) bool hasVertexColors, int animflags)
{ {
osg::StateSet* stateset = node->getOrCreateStateSet(); osg::StateSet* stateset = node->getOrCreateStateSet();
@ -1433,6 +1415,43 @@ namespace NifOsg
mat->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE); mat->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE);
break; 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;
} }
} }
} }

View file

@ -64,6 +64,16 @@ namespace SceneUtil
std::vector<osg::ref_ptr<osg::Light> > mLights; 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. // 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. // 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 class CollectLightCallback : public osg::NodeCallback
@ -82,14 +92,8 @@ namespace SceneUtil
{ {
if (!mLightManager) if (!mLightManager)
{ {
for (unsigned int i=0;i<nv->getNodePath().size(); ++i) mLightManager = findLightManager(nv->getNodePath());
{
if (LightManager* lightManager = dynamic_cast<LightManager*>(nv->getNodePath()[i]))
{
mLightManager = lightManager;
break;
}
}
if (!mLightManager) if (!mLightManager)
throw std::runtime_error("can't find parent LightManager"); throw std::runtime_error("can't find parent LightManager");
} }
@ -126,15 +130,13 @@ namespace SceneUtil
}; };
LightManager::LightManager() LightManager::LightManager()
: mLightsInViewSpace(false) : mStartLight(0)
, mStartLight(0)
{ {
setUpdateCallback(new LightManagerUpdateCallback); setUpdateCallback(new LightManagerUpdateCallback);
} }
LightManager::LightManager(const LightManager &copy, const osg::CopyOp &copyop) LightManager::LightManager(const LightManager &copy, const osg::CopyOp &copyop)
: osg::Group(copy, copyop) : osg::Group(copy, copyop)
, mLightsInViewSpace(false)
, mStartLight(copy.mStartLight) , mStartLight(copy.mStartLight)
{ {
@ -142,8 +144,8 @@ namespace SceneUtil
void LightManager::update() void LightManager::update()
{ {
mLightsInViewSpace = false;
mLights.clear(); mLights.clear();
mLightsInViewSpace.clear();
// do an occasional cleanup for orphaned lights // do an occasional cleanup for orphaned lights
if (mStateSetCache.size() > 5000) if (mStateSetCache.size() > 5000)
@ -161,22 +163,6 @@ namespace SceneUtil
mLights.push_back(l); 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) 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) // 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; 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) void LightManager::setStartLight(int start)
{ {
mStartLight = 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; 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) if (!mLightManager)
{ {
for (unsigned int i=0;i<nv->getNodePath().size(); ++i) mLightManager = findLightManager(nv->getNodePath());
{
if (LightManager* lightManager = dynamic_cast<LightManager*>(nv->getNodePath()[i]))
{
mLightManager = lightManager;
break;
}
}
if (!mLightManager) if (!mLightManager)
{ {
traverse(node, nv); traverse(node, nv);
@ -273,13 +276,14 @@ namespace SceneUtil
} }
} }
mLightManager->prepareForCamera(cv->getCurrentCamera());
// Possible optimizations: // Possible optimizations:
// - cull list of lights by the camera frustum // - cull list of lights by the camera frustum
// - organize lights in a quad tree // - 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()) if (lights.size())
{ {
@ -288,10 +292,10 @@ namespace SceneUtil
osg::Matrixf mat = *cv->getModelViewMatrix(); osg::Matrixf mat = *cv->getModelViewMatrix();
transformBoundingSphere(mat, nodeBound); transformBoundingSphere(mat, nodeBound);
std::vector<const LightManager::LightSourceTransform*> lightList; LightManager::LightList lightList;
for (unsigned int i=0; i<lights.size(); ++i) 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)) if (l.mViewBound.intersects(nodeBound))
lightList.push_back(&l); lightList.push_back(&l);
} }

View file

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

View file

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

View file

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

View file

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