From b6c102256656e5e9c1efe9537ff3850a22bf03c2 Mon Sep 17 00:00:00 2001 From: Ykkrosh Date: Wed, 27 Sep 2006 16:54:23 +0000 Subject: [PATCH] # Improved actor-viewer tool (more viewing controls, support for random actor variations) Also right-click-and-drag to rotate the camera vertically. Fixed LookAt code (the algorithm in the gluLookAt man page seems to be wrong). This was SVN commit r4394. --- source/graphics/Model.cpp | 17 ++ source/graphics/Model.h | 4 + source/graphics/Unit.cpp | 6 +- .../atlas/AtlasUI/ActorViewer/ActorViewer.cpp | 69 +++++--- .../atlas/AtlasUI/ActorViewer/ActorViewer.h | 3 + .../tools/atlas/AtlasUI/General/Observable.h | 7 + .../Sections/Environment/LightControl.cpp | 5 - .../Sections/Environment/LightControl.h | 3 +- .../ScenarioEditor/Sections/Object/Object.cpp | 150 +----------------- .../Sections/Object/VariationControl.cpp | 129 +++++++++++++++ .../Sections/Object/VariationControl.h | 25 +++ .../Tools/Common/ObjectSettings.cpp | 19 +-- .../Tools/Common/ObjectSettings.h | 9 +- .../tools/atlas/GameInterface/ActorViewer.cpp | 5 + .../tools/atlas/GameInterface/ActorViewer.h | 2 + .../Handlers/CameraCtrlHandlers.cpp | 6 +- .../GameInterface/Handlers/ObjectHandlers.cpp | 7 +- source/tools/atlas/GameInterface/Messages.h | 2 + source/tools/atlas/GameInterface/View.cpp | 11 ++ source/tools/atlas/GameInterface/View.h | 16 +- 20 files changed, 291 insertions(+), 204 deletions(-) create mode 100644 source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Object/VariationControl.cpp create mode 100644 source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Object/VariationControl.h diff --git a/source/graphics/Model.cpp b/source/graphics/Model.cpp index 22dc7c8dfa..93c7724c03 100644 --- a/source/graphics/Model.cpp +++ b/source/graphics/Model.cpp @@ -405,6 +405,23 @@ bool CModel::SetAnimation(CSkeletonAnim* anim, bool once, float speed, CSkeleton return true; } +///////////////////////////////////////////////////////////////////////////////////////////////////////////// +// CopyAnimation +void CModel::CopyAnimationFrom(CModel* source) +{ + m_Anim = source->m_Anim; + m_NextAnim = source->m_NextAnim; + m_AnimTime = source->m_AnimTime; + m_AnimSpeed = source->m_AnimSpeed; + + m_Flags &= ~MODELFLAG_CASTSHADOWS; + if (source->m_Flags & MODELFLAG_CASTSHADOWS) + m_Flags |= MODELFLAG_CASTSHADOWS; + + m_ObjectBounds.SetEmpty(); + InvalidateBounds(); +} + ///////////////////////////////////////////////////////////////////////////////////////////////////////////// // AddProp: add a prop to the model on the given point void CModel::AddProp(SPropPoint* point, CModel* model, CObjectEntry* objectentry) diff --git a/source/graphics/Model.h b/source/graphics/Model.h index a0737ee088..258e57ccea 100644 --- a/source/graphics/Model.h +++ b/source/graphics/Model.h @@ -78,6 +78,10 @@ public: // get the currently playing animation, if any CSkeletonAnim* GetAnimation() { return m_Anim; } + // set the animation state to be the same as from another; both models should + // be compatible types (same type of skeleton) + void CopyAnimationFrom(CModel* source); + // set object flags void SetFlags(u32 flags) { m_Flags=flags; } // get object flags diff --git a/source/graphics/Unit.cpp b/source/graphics/Unit.cpp index a41f054688..1997a4964e 100644 --- a/source/graphics/Unit.cpp +++ b/source/graphics/Unit.cpp @@ -172,14 +172,14 @@ void CUnit::ReloadObject() CObjectEntry* newObject = g_ObjMan.FindObjectVariation(m_Object->m_Base, selections); if (newObject != m_Object) { - // Clone the base model (lacking instance-specific data) + // Clone the new object's base (non-instance) model CModel* newModel = newObject->m_Model->Clone(); - // Copy the old instance-specific settings to the new model + // Copy the old instance-specific settings from the old model to the new instance newModel->SetTransform(m_Model->GetTransform()); if (m_PlayerID != -1) newModel->SetPlayerID(m_PlayerID); - // TODO: preserve selection of animation, anim offset, etc? + newModel->CopyAnimationFrom(m_Model); delete m_Model; m_Model = newModel; diff --git a/source/tools/atlas/AtlasUI/ActorViewer/ActorViewer.cpp b/source/tools/atlas/AtlasUI/ActorViewer/ActorViewer.cpp index 58dfaa210d..7dcb60d16f 100644 --- a/source/tools/atlas/AtlasUI/ActorViewer/ActorViewer.cpp +++ b/source/tools/atlas/AtlasUI/ActorViewer/ActorViewer.cpp @@ -9,6 +9,7 @@ #include "ScenarioEditor/Tools/Common/Tools.h" #include "ScenarioEditor/ScenarioEditor.h" #include "ScenarioEditor/Sections/Environment/LightControl.h" +#include "ScenarioEditor/Sections/Object/VariationControl.h" #include "GameInterface/Messages.h" @@ -28,19 +29,25 @@ wxWindow* Tooltipped(wxWindow* window, const wxString& tip) return window; } +////////////////////////////////////////////////////////////////////////// + class ActorCanvas : public Canvas { public: ActorCanvas(wxWindow* parent, int* attribList) : Canvas(parent, attribList, wxBORDER_SUNKEN), - m_Distance(20.f), m_Angle(0.f), m_LastIsValid(false) + m_Distance(20.f), m_Angle(0.f), m_Elevation(M_PI/6.f), m_LastIsValid(false) { } void PostLookAt() { + float offset = 0.3f; // slight fudge so we turn nicely when going over the top of the unit POST_MESSAGE(LookAt, (eRenderView::ACTOR, - Position(m_Distance*sin(m_Angle), m_Distance/2.f, m_Distance*cos(m_Angle)), + Position( + m_Distance*cos(m_Elevation)*sin(m_Angle) + offset*cos(m_Angle), + m_Distance*sin(m_Elevation), + m_Distance*cos(m_Elevation)*cos(m_Angle) - offset*sin(m_Angle)), Position(0, 0, 0))); } @@ -58,13 +65,15 @@ protected: camera_changed = true; } - if (evt.ButtonDown(wxMOUSE_BTN_LEFT)) + if (evt.ButtonDown(wxMOUSE_BTN_LEFT) || evt.ButtonDown(wxMOUSE_BTN_RIGHT)) { m_LastX = evt.GetX(); m_LastY = evt.GetY(); m_LastIsValid = true; } - else if (evt.Dragging() && evt.ButtonIsDown(wxMOUSE_BTN_LEFT) && m_LastIsValid) + else if (evt.Dragging() + && (evt.ButtonIsDown(wxMOUSE_BTN_LEFT) || evt.ButtonIsDown(wxMOUSE_BTN_RIGHT)) + && m_LastIsValid) { int dx = evt.GetX() - m_LastX; int dy = evt.GetY() - m_LastY; @@ -72,16 +81,23 @@ protected: m_LastY = evt.GetY(); m_Angle += dx * M_PI/256.f * ScenarioEditor::GetSpeedModifier(); - m_Distance += dy / 8.f * ScenarioEditor::GetSpeedModifier(); + + if (evt.ButtonIsDown(wxMOUSE_BTN_LEFT)) + m_Distance += dy / 8.f * ScenarioEditor::GetSpeedModifier(); + else // evt.ButtonIsDown(wxMOUSE_BTN_RIGHT)) + m_Elevation += dy * M_PI/256.f * ScenarioEditor::GetSpeedModifier(); + camera_changed = true; } - else if (evt.ButtonUp(wxMOUSE_BTN_ANY)) + else if (evt.ButtonUp(wxMOUSE_BTN_ANY) + && ! (evt.ButtonIsDown(wxMOUSE_BTN_LEFT) || evt.ButtonIsDown(wxMOUSE_BTN_RIGHT)) + ) { // In some situations (e.g. double-clicking the title bar to // maximise the window) we get a dragging event without the matching - // buttondown; so disallow dragging when there was a buttonup since + // buttondown; so disallow dragging when all buttons were released since // the last buttondown. - // (TODO: does this affect the scenario editor too?) + // (TODO: does this problem affect the scenario editor too?) m_LastIsValid = false; } @@ -94,6 +110,7 @@ protected: private: float m_Distance; float m_Angle; + float m_Elevation; int m_LastX, m_LastY; bool m_LastIsValid; }; @@ -142,7 +159,8 @@ static void SendToGame(const AtlasMessage::sEnvironmentSettings& settings) ActorViewer::ActorViewer(wxWindow* parent) : wxFrame(parent, wxID_ANY, _("Actor Viewer"), wxDefaultPosition, wxSize(800, 600)), m_CurrentSpeed(0.f), m_Wireframe(false), m_BackgroundColour(wxColour(255, 255, 255)), - m_Walking(true) + m_Walking(true), + m_ObjectSettings(m_ObjectSelection, AtlasMessage::eRenderView::ACTOR) { SetIcon(wxIcon(_T("ICON_ActorEditor"))); @@ -239,40 +257,48 @@ ActorViewer::ActorViewer(wxWindow* parent) m_EnvironmentSettings.suncolour = Colour(255, 255, 255); m_EnvironmentSettings.terraincolour = Colour(164, 164, 164); m_EnvironmentSettings.unitcolour = Colour(164, 164, 164); - LightControl* lightControl = new LightControl(sidePanel, wxSize(100, 100), m_EnvironmentSettings); + LightControl* lightControl = new LightControl(sidePanel, wxSize(90, 90), m_EnvironmentSettings); m_EnvConn = m_EnvironmentSettings.RegisterObserver(0, &SendToGame); SendToGame(m_EnvironmentSettings); wxSizer* mainSizer = new wxBoxSizer(wxVERTICAL); wxSizer* bottomSizer = new wxBoxSizer(wxHORIZONTAL); + wxSizer* bottomLeftSizer = new wxBoxSizer(wxVERTICAL); wxSizer* bottomRightSizer = new wxBoxSizer(wxVERTICAL); wxSizer* playButtonSizer = new wxBoxSizer(wxHORIZONTAL); - wxSizer* optionButtonSizer = new wxGridSizer(2); - - mainSizer->Add(m_TreeCtrl, wxSizerFlags().Expand().Proportion(1)); - mainSizer->Add(bottomSizer, wxSizerFlags().Expand()); - - bottomSizer->Add(lightControl, wxSizerFlags().Border(wxRIGHT, 5)); - bottomSizer->Add(bottomRightSizer, wxSizerFlags().Proportion(1)); + wxSizer* optionButtonSizer = new wxBoxSizer(wxVERTICAL); + wxSizer* variationSizer = new wxStaticBoxSizer(wxVERTICAL, sidePanel, _("Variation")); playButtonSizer->Add(new wxButton(sidePanel, ID_Play, _("Play")), wxSizerFlags().Proportion(1)); playButtonSizer->Add(new wxButton(sidePanel, ID_Pause, _("Pause")), wxSizerFlags().Proportion(1)); playButtonSizer->Add(new wxButton(sidePanel, ID_Slow, _("Slow")), wxSizerFlags().Proportion(1)); - bottomRightSizer->Add(m_AnimationBox, wxSizerFlags().Expand()); - bottomRightSizer->Add(playButtonSizer, wxSizerFlags().Expand()); - optionButtonSizer->Add(new wxButton(sidePanel, ID_Edit, _("Edit actor")), wxSizerFlags().Expand()); optionButtonSizer->Add(Tooltipped(new wxButton(sidePanel, ID_Wireframe, _("Wireframe")), _("Toggle wireframe / solid rendering")), wxSizerFlags().Expand()); optionButtonSizer->Add(Tooltipped(new wxButton(sidePanel, ID_Background, _("Background")), _("Change the background colour")), wxSizerFlags().Expand()); optionButtonSizer->Add(Tooltipped(new wxButton(sidePanel, ID_Walking, _("Move")), _("Toggle movement along ground when playing walk/run animations")), wxSizerFlags().Expand()); - bottomRightSizer->Add(optionButtonSizer, wxSizerFlags().Expand().Border(wxTOP, 4)); + variationSizer->Add(new VariationControl(sidePanel, m_ObjectSettings), wxSizerFlags().Expand().Proportion(1)); + + mainSizer->Add(m_TreeCtrl, wxSizerFlags().Expand().Proportion(1)); + mainSizer->Add(bottomSizer, wxSizerFlags().Expand()); + + bottomSizer->Add(bottomLeftSizer, wxSizerFlags().Expand().Border(wxRIGHT, 5)); + bottomSizer->Add(bottomRightSizer, wxSizerFlags().Expand().Proportion(1)); + + bottomLeftSizer->Add(lightControl, wxSizerFlags().Expand()); + bottomLeftSizer->Add(optionButtonSizer, wxSizerFlags().Expand().Border(wxTOP, 4)); + + bottomRightSizer->Add(m_AnimationBox, wxSizerFlags().Expand()); + bottomRightSizer->Add(playButtonSizer, wxSizerFlags().Expand()); + bottomRightSizer->Add(variationSizer, wxSizerFlags().Expand().Proportion(1)); sidePanel->SetSizer(mainSizer); ////////////////////////////////////////////////////////////////////////// + m_ObjectSelection.push_back(0); + // Start by displaying the default non-existent actor m_CurrentActor = _T("structures/fndn_1x1.xml"); SetActorView(); @@ -294,6 +320,7 @@ void ActorViewer::OnClose(wxCloseEvent& WXUNUSED(event)) void ActorViewer::SetActorView(bool flushCache) { POST_MESSAGE(SetActorViewer, (m_CurrentActor.c_str(), m_AnimationBox->GetValue().c_str(), m_CurrentSpeed, flushCache)); + m_ObjectSelection.NotifyObservers(); } void ActorViewer::OnTreeSelection(wxTreeEvent& event) diff --git a/source/tools/atlas/AtlasUI/ActorViewer/ActorViewer.h b/source/tools/atlas/AtlasUI/ActorViewer/ActorViewer.h index 3bc2d206f8..b86cb8787d 100644 --- a/source/tools/atlas/AtlasUI/ActorViewer/ActorViewer.h +++ b/source/tools/atlas/AtlasUI/ActorViewer/ActorViewer.h @@ -2,6 +2,7 @@ #include "GameInterface/Messages.h" #include "General/Observable.h" +#include "ScenarioEditor/Tools/Common/ObjectSettings.h" class wxTreeCtrl; @@ -29,6 +30,8 @@ private: wxString m_CurrentActor; float m_CurrentSpeed; + Observable > m_ObjectSelection; + Observable m_ObjectSettings; bool m_Wireframe; wxColour m_BackgroundColour; bool m_Walking; diff --git a/source/tools/atlas/AtlasUI/General/Observable.h b/source/tools/atlas/AtlasUI/General/Observable.h index 70d179706d..e1990d8e5b 100644 --- a/source/tools/atlas/AtlasUI/General/Observable.h +++ b/source/tools/atlas/AtlasUI/General/Observable.h @@ -31,6 +31,13 @@ typedef boost::signals::scoped_connection ObservableScopedConnection; template class Observable : public T { public: + Observable() {} + + template + explicit Observable(const T1& a1) : T(a1) {} + template + explicit Observable(T1& a1, T2 a2) : T(a1, a2) {} + template ObservableConnection RegisterObserver(int order, void (C::*callback) (const T&), C* obj) { return m_Signal.connect(order, boost::bind(std::mem_fun(callback), obj, _1)); diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Environment/LightControl.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Environment/LightControl.cpp index 388998c0fb..6e44b730f7 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Environment/LightControl.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Environment/LightControl.cpp @@ -135,11 +135,6 @@ LightControl::LightControl(wxWindow* parent, const wxSize& size, Observabletheta = settings.sunrotation; diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Environment/LightControl.h b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Environment/LightControl.h index 650fd98c5a..6a8327e119 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Environment/LightControl.h +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Environment/LightControl.h @@ -11,7 +11,6 @@ class LightControl : public wxPanel { public: LightControl(wxWindow* parent, const wxSize& size, Observable& environment); - ~LightControl(); void OnSettingsChange(const AtlasMessage::sEnvironmentSettings& settings); @@ -19,7 +18,7 @@ public: private: Observable& m_Environment; - ObservableConnection m_Conn; + ObservableScopedConnection m_Conn; LightSphere* m_Sphere; }; diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Object/Object.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Object/Object.cpp index 20079c10af..e020363728 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Object/Object.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Object/Object.cpp @@ -6,6 +6,7 @@ #include "ScenarioEditor/Tools/Common/Tools.h" #include "ScenarioEditor/Tools/Common/ObjectSettings.h" #include "ScenarioEditor/Tools/Common/MiscState.h" +#include "VariationControl.h" #include "GameInterface/Messages.h" @@ -160,152 +161,6 @@ BEGIN_EVENT_TABLE(PlayerComboBox, wxComboBox) EVT_COMBOBOX(wxID_ANY, OnSelect) END_EVENT_TABLE(); - -class VariationDisplay : public wxScrolledWindow -{ -public: - VariationDisplay(wxWindow* parent) - : wxScrolledWindow(parent, -1) - { - m_Conn = g_ObjectSettings.RegisterObserver(1, &VariationDisplay::OnObjectSettingsChange, this); - - SetMinSize(wxSize(160, wxDefaultCoord)); - - SetScrollRate(0, 5); - - m_Sizer = new wxBoxSizer(wxVERTICAL); - SetSizer(m_Sizer); - } - - ~VariationDisplay() - { - g_ObjectSettings.RemoveObserver(m_Conn); - } - -private: - - ObservableConnection m_Conn; - - std::vector m_ComboBoxes; - wxSizer* m_Sizer; - - // Event handler shared by all the combo boxes created by this window - void OnSelect(wxCommandEvent& evt) - { - std::set selections; - - // It's possible for a variant name to appear in multiple groups. - // If so, assume that all the names in each group are the same, so - // we don't have to worry about some impossible combinations (e.g. - // one group "a,b", a second "b,c", and a third "c,a", where's there's - // no set of selections that matches one (and only one) of each group). - // - // So... When a combo box is changed from 'a' to 'b', add 'b' to the new - // selections and make sure any other combo boxes containing both 'a' and - // 'b' no longer contain 'a'. - - wxComboBox* thisComboBox = wxDynamicCast(evt.GetEventObject(), wxComboBox); - wxString newValue = thisComboBox->GetValue(); - - selections.insert(newValue); - - for (size_t i = 0; i < m_ComboBoxes.size(); ++i) - { - wxComboBox* comboBox = wxDynamicCast(m_ComboBoxes[i], wxComboBox); - wxCHECK(comboBox != NULL, ); - // If our newly selected value is used in another combobox, we want - // that combobox to use the new value, so don't add its old value - // to the list of selections - if (comboBox->FindString(newValue) == wxNOT_FOUND) - selections.insert(comboBox->GetValue()); - } - - g_ObjectSettings.SetActorSelections(selections); - g_ObjectSettings.NotifyObserversExcept(m_Conn); - RefreshObjectSettings(); - } - - void OnObjectSettingsChange(const ObjectSettings& settings) - { - Freeze(); - - const std::vector& variation = settings.GetActorVariation(); - - // Creating combo boxes seems to be pretty expensive - so we create as - // few as possible, by never deleting any. - - size_t oldCount = m_ComboBoxes.size(); - size_t newCount = variation.size(); - - // If we have too many combo boxes, hide the excess ones - for (size_t i = newCount; i < oldCount; ++i) - { - m_ComboBoxes[i]->Show(false); - } - - for (size_t i = 0; i < variation.size(); ++i) - { - const ObjectSettings::Group& group = variation[i]; - - if (i < oldCount) - { - // Already got enough boxes available, so use an old one - wxComboBox* comboBox = wxDynamicCast(m_ComboBoxes[i], wxComboBox); - wxCHECK(comboBox != NULL, ); - // Replace the contents of the old combobox with the new data - comboBox->Freeze(); - comboBox->Clear(); - comboBox->Append(group.variants); - comboBox->SetValue(group.chosen); - comboBox->Show(true); - comboBox->Thaw(); - } - else - { - // Create an initially empty combobox, because we can fill it - // quicker than the default constructor can - wxComboBox* combo = new wxComboBox(this, -1, wxEmptyString, wxDefaultPosition, - wxSize(130, wxDefaultCoord), wxArrayString(), wxCB_READONLY); - // Freeze it before adding all the values - combo->Freeze(); - combo->Append(group.variants); - combo->SetValue(group.chosen); - combo->Thaw(); - // Add the on-select event handler - combo->Connect(wxID_ANY, wxEVT_COMMAND_COMBOBOX_SELECTED, - wxCommandEventHandler(VariationDisplay::OnSelect), NULL, this); - // Add box to sizer and list - m_Sizer->Add(combo); - m_ComboBoxes.push_back(combo); - } - } - - Layout(); - - // Make the scrollbars appear when appropriate - FitInside(); - - Thaw(); - } - - void RefreshObjectSettings() - { - const std::vector& variation = g_ObjectSettings.GetActorVariation(); - - // For each group, set the corresponding combobox's value to the chosen one - size_t i = 0; - for (std::vector::const_iterator group = variation.begin(); - group != variation.end() && i < m_ComboBoxes.size(); - ++group, ++i) - { - wxComboBox* comboBox = wxDynamicCast(m_ComboBoxes[i], wxComboBox); - wxCHECK(comboBox != NULL, ); - - comboBox->SetValue(group->chosen); - } - } -}; - ////////////////////////////////////////////////////////////////////////// ObjectBottomBar::ObjectBottomBar(wxWindow* parent) @@ -327,7 +182,8 @@ ObjectBottomBar::ObjectBottomBar(wxWindow* parent) wxComboBox* playerSelect = new PlayerComboBox(this, players); sizer->Add(playerSelect); - wxWindow* variationSelect = new VariationDisplay(this); + wxWindow* variationSelect = new VariationControl(this, g_ObjectSettings); + variationSelect->SetMinSize(wxSize(160, -1)); wxSizer* variationSizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Variation")); variationSizer->Add(variationSelect, wxSizerFlags().Proportion(1).Expand()); sizer->Add(variationSizer, wxSizerFlags().Proportion(1)); diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Object/VariationControl.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Object/VariationControl.cpp new file mode 100644 index 0000000000..c9eb7b65b0 --- /dev/null +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Object/VariationControl.cpp @@ -0,0 +1,129 @@ +#include "stdafx.h" + +#include "VariationControl.h" + +#include "ScenarioEditor/Tools/Common/ObjectSettings.h" + +VariationControl::VariationControl(wxWindow* parent, Observable& objectSettings) +: wxScrolledWindow(parent, -1), +m_ObjectSettings(objectSettings) +{ + m_Conn = m_ObjectSettings.RegisterObserver(1, &VariationControl::OnObjectSettingsChange, this); + + SetScrollRate(0, 5); + + m_Sizer = new wxBoxSizer(wxVERTICAL); + SetSizer(m_Sizer); +} + +// Event handler shared by all the combo boxes created by this window +void VariationControl::OnSelect(wxCommandEvent& evt) +{ + std::set selections; + + // It's possible for a variant name to appear in multiple groups. + // If so, assume that all the names in each group are the same, so + // we don't have to worry about some impossible combinations (e.g. + // one group "a,b", a second "b,c", and a third "c,a", where's there's + // no set of selections that matches one (and only one) of each group). + // + // So... When a combo box is changed from 'a' to 'b', add 'b' to the new + // selections and make sure any other combo boxes containing both 'a' and + // 'b' no longer contain 'a'. + + wxComboBox* thisComboBox = wxDynamicCast(evt.GetEventObject(), wxComboBox); + wxCHECK(thisComboBox != NULL, ); + wxString newValue = thisComboBox->GetValue(); + + selections.insert(newValue); + + for (size_t i = 0; i < m_ComboBoxes.size(); ++i) + { + wxComboBox* comboBox = m_ComboBoxes[i]; + // If our newly selected value is used in another combobox, we want + // that combobox to use the new value, so don't add its old value + // to the list of selections + if (comboBox->FindString(newValue) == wxNOT_FOUND) + selections.insert(comboBox->GetValue()); + } + + m_ObjectSettings.SetActorSelections(selections); + m_ObjectSettings.NotifyObserversExcept(m_Conn); + RefreshObjectSettings(); +} + +void VariationControl::OnObjectSettingsChange(const ObjectSettings& settings) +{ + Freeze(); + + const std::vector& variation = settings.GetActorVariation(); + + // Creating combo boxes seems to be pretty expensive - so we create as + // few as possible, by never deleting any. + + size_t oldCount = m_ComboBoxes.size(); + size_t newCount = variation.size(); + + // If we have too many combo boxes, hide the excess ones + for (size_t i = newCount; i < oldCount; ++i) + { + m_ComboBoxes[i]->Show(false); + } + + for (size_t i = 0; i < variation.size(); ++i) + { + const ObjectSettings::Group& group = variation[i]; + + if (i < oldCount) + { + // Already got enough boxes available, so use an old one + wxComboBox* comboBox = m_ComboBoxes[i]; + // Replace the contents of the old combobox with the new data + comboBox->Freeze(); + comboBox->Clear(); + comboBox->Append(group.variants); + comboBox->SetValue(group.chosen); + comboBox->Show(true); + comboBox->Thaw(); + } + else + { + // Create an initially empty combobox, because we can fill it + // quicker than the default constructor can + wxComboBox* combo = new wxComboBox(this, -1, wxEmptyString, wxDefaultPosition, + wxSize(80, wxDefaultCoord), wxArrayString(), wxCB_READONLY); + // Freeze it before adding all the values + combo->Freeze(); + combo->Append(group.variants); + combo->SetValue(group.chosen); + combo->Thaw(); + // Add the on-select event handler + combo->Connect(wxID_ANY, wxEVT_COMMAND_COMBOBOX_SELECTED, + wxCommandEventHandler(VariationControl::OnSelect), NULL, this); + // Add box to sizer and list + m_Sizer->Add(combo, wxSizerFlags().Expand()); + m_ComboBoxes.push_back(combo); + } + } + + Layout(); + + Thaw(); + + // Make the scrollbars appear when appropriate + FitInside(); +} + +void VariationControl::RefreshObjectSettings() +{ + const std::vector& variation = m_ObjectSettings.GetActorVariation(); + + // For each group, set the corresponding combobox's value to the chosen one + size_t i = 0; + for (std::vector::const_iterator group = variation.begin(); + group != variation.end() && i < m_ComboBoxes.size(); + ++group, ++i) + { + m_ComboBoxes[i]->SetValue(group->chosen); + } +} diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Object/VariationControl.h b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Object/VariationControl.h new file mode 100644 index 0000000000..5831c3fdca --- /dev/null +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Object/VariationControl.h @@ -0,0 +1,25 @@ +#ifndef VARIATIONCONTROL_H__ +#define VARIATIONCONTROL_H__ + +#include "General/Observable.h" + +class ObjectSettings; + +class VariationControl : public wxScrolledWindow +{ +public: + VariationControl(wxWindow* parent, Observable& objectSettings); + +private: + void OnSelect(wxCommandEvent& evt); + void OnObjectSettingsChange(const ObjectSettings& settings); + void RefreshObjectSettings(); + + ObservableScopedConnection m_Conn; + + Observable& m_ObjectSettings; + std::vector m_ComboBoxes; + wxSizer* m_Sizer; +}; + +#endif // VARIATIONCONTROL_H__ diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/ObjectSettings.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/ObjectSettings.cpp index 456a5829ff..739ba3b2c7 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/ObjectSettings.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/ObjectSettings.cpp @@ -5,17 +5,12 @@ #include "GameInterface/Messages.h" #include "ScenarioEditor/Tools/Common/Tools.h" -Observable g_ObjectSettings; +Observable g_ObjectSettings(g_SelectedObjects, AtlasMessage::eRenderView::GAME); -ObjectSettings::ObjectSettings() -: m_PlayerID(0) +ObjectSettings::ObjectSettings(Observable >& selectedObjects, int view) +: m_PlayerID(0), m_SelectedObjects(selectedObjects), m_View(view) { - m_Conn = g_SelectedObjects.RegisterObserver(0, &ObjectSettings::OnSelectionChange, this); -} - -ObjectSettings::~ObjectSettings() -{ - m_Conn.disconnect(); + m_Conn = m_SelectedObjects.RegisterObserver(0, &ObjectSettings::OnSelectionChange, this); } int ObjectSettings::GetPlayerID() const @@ -98,7 +93,7 @@ void ObjectSettings::OnSelectionChange(const std::vector if (selection.empty()) return; - AtlasMessage::qGetObjectSettings qry (selection[0]); + AtlasMessage::qGetObjectSettings qry (m_View, selection[0]); qry.Post(); m_PlayerID = qry.settings->player; @@ -136,8 +131,8 @@ void ObjectSettings::OnSelectionChange(const std::vector void ObjectSettings::PostToGame() { - if (g_SelectedObjects.empty()) + if (m_SelectedObjects.empty()) return; - POST_COMMAND(SetObjectSettings, (g_SelectedObjects[0], GetSettings())); + POST_COMMAND(SetObjectSettings, (m_View, m_SelectedObjects[0], GetSettings())); } \ No newline at end of file diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/ObjectSettings.h b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/ObjectSettings.h index ea6f521aca..3a7c12aef9 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/ObjectSettings.h +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/ObjectSettings.h @@ -16,8 +16,7 @@ namespace AtlasMessage class ObjectSettings { public: - ObjectSettings(); - ~ObjectSettings(); + ObjectSettings(Observable >& selectedObjects, int view); int GetPlayerID() const; void SetPlayerID(int playerID); @@ -37,6 +36,10 @@ public: AtlasMessage::sObjectSettings GetSettings() const; private: + Observable >& m_SelectedObjects; + + int m_View; + // 0 = gaia, 1..inf = normal players int m_PlayerID; @@ -49,7 +52,7 @@ private: std::vector m_VariantGroups; // Observe changes to unit selection - ObservableConnection m_Conn; + ObservableScopedConnection m_Conn; void OnSelectionChange(const std::vector& selection); // Transfer current settings to the currently selected unit (if any) diff --git a/source/tools/atlas/GameInterface/ActorViewer.cpp b/source/tools/atlas/GameInterface/ActorViewer.cpp index d84844d4dd..429a589cec 100644 --- a/source/tools/atlas/GameInterface/ActorViewer.cpp +++ b/source/tools/atlas/GameInterface/ActorViewer.cpp @@ -82,6 +82,11 @@ ActorViewer::~ActorViewer() delete &m; } +CUnit* ActorViewer::GetUnit() +{ + return m.Unit; +} + void ActorViewer::SetActor(const CStrW& id, const CStrW& animation) { bool needsAnimReload = false; diff --git a/source/tools/atlas/GameInterface/ActorViewer.h b/source/tools/atlas/GameInterface/ActorViewer.h index c3f4e59d68..776cc11fcc 100644 --- a/source/tools/atlas/GameInterface/ActorViewer.h +++ b/source/tools/atlas/GameInterface/ActorViewer.h @@ -3,6 +3,7 @@ struct ActorViewerImpl; struct SColor4ub; +class CUnit; class ActorViewer { @@ -11,6 +12,7 @@ public: ~ActorViewer(); void SetActor(const CStrW& id, const CStrW& animation); + CUnit* GetUnit(); void SetWalkEnabled(bool enabled); void SetBackgroundColour(const SColor4ub& colour); void Render(); diff --git a/source/tools/atlas/GameInterface/Handlers/CameraCtrlHandlers.cpp b/source/tools/atlas/GameInterface/Handlers/CameraCtrlHandlers.cpp index eabda967db..539b7e294a 100644 --- a/source/tools/atlas/GameInterface/Handlers/CameraCtrlHandlers.cpp +++ b/source/tools/atlas/GameInterface/Handlers/CameraCtrlHandlers.cpp @@ -151,14 +151,16 @@ MESSAGEHANDLER(LookAt) CVector3D tgt = msg->target->GetWorldSpace(); CVector3D eye = msg->pos->GetWorldSpace(); - tgt.Y = -tgt.Y; // ??? why is this needed? - eye.Y = -eye.Y; // ??? + tgt.Y = -tgt.Y; // ??? why is this needed? + eye.Y = -eye.Y; // ??? // Based on http://www.opengl.org/documentation/specs/man_pages/hardcopy/GL/html/glu/lookat.html CVector3D f = tgt - eye; f.Normalize(); CVector3D s = f.Cross(CVector3D(0, 1, 0)); CVector3D u = s.Cross(f); + s.Normalize(); // (not in that man page, but necessary for correctness, and done by Mesa) + u.Normalize(); CMatrix3D M ( s[0], s[1], s[2], 0, u[0], u[1], u[2], 0, diff --git a/source/tools/atlas/GameInterface/Handlers/ObjectHandlers.cpp b/source/tools/atlas/GameInterface/Handlers/ObjectHandlers.cpp index 7e9383cff1..10e68a4f8d 100644 --- a/source/tools/atlas/GameInterface/Handlers/ObjectHandlers.cpp +++ b/source/tools/atlas/GameInterface/Handlers/ObjectHandlers.cpp @@ -4,6 +4,7 @@ #include "MessageHandler.h" #include "../CommandProc.h" +#include "../View.h" #include "graphics/GameView.h" #include "graphics/Model.h" @@ -112,7 +113,7 @@ MESSAGEHANDLER(SetSelectionPreview) QUERYHANDLER(GetObjectSettings) { - CUnit* unit = g_UnitMan.FindByID(msg->id); + CUnit* unit = View::GetView(msg->view)->GetUnit(msg->id); if (! unit) return; sObjectSettings settings; @@ -165,7 +166,7 @@ BEGIN_COMMAND(SetObjectSettings) void Do() { - CUnit* unit = g_UnitMan.FindByID(msg->id); + CUnit* unit = View::GetView(msg->view)->GetUnit(msg->id); if (! unit) return; sObjectSettings settings = msg->settings; @@ -197,7 +198,7 @@ BEGIN_COMMAND(SetObjectSettings) private: void Set(int player, const std::set& selections) { - CUnit* unit = g_UnitMan.FindByID(msg->id); + CUnit* unit = View::GetView(msg->view)->GetUnit(msg->id); if (! unit) return; unit->SetPlayerID(player); diff --git a/source/tools/atlas/GameInterface/Messages.h b/source/tools/atlas/GameInterface/Messages.h index f857b38189..9c1f7e6e76 100644 --- a/source/tools/atlas/GameInterface/Messages.h +++ b/source/tools/atlas/GameInterface/Messages.h @@ -302,12 +302,14 @@ MESSAGE(SetSelectionPreview, ); QUERY(GetObjectSettings, + ((int, view)) // eRenderView ((ObjectID, id)) , ((sObjectSettings, settings)) ); COMMAND(SetObjectSettings, NOMERGE, + ((int, view)) // eRenderView ((ObjectID, id)) ((sObjectSettings, settings)) ); diff --git a/source/tools/atlas/GameInterface/View.cpp b/source/tools/atlas/GameInterface/View.cpp index fdd8a79602..29d9a3de62 100644 --- a/source/tools/atlas/GameInterface/View.cpp +++ b/source/tools/atlas/GameInterface/View.cpp @@ -7,6 +7,7 @@ #include "Messages.h" #include "graphics/SColor.h" +#include "graphics/UnitManager.h" #include "renderer/Renderer.h" #include "ps/Game.h" #include "ps/GameSetup/GameSetup.h" @@ -61,6 +62,11 @@ CCamera& ViewActor::GetCamera() return m_Camera; } +CUnit* ViewActor::GetUnit(AtlasMessage::ObjectID UNUSED(id)) +{ + return m_ActorViewer->GetUnit(); +} + bool ViewActor::WantsHighFramerate() { if (m_SpeedMultiplier != 0.f && m_ActorViewer->HasAnimation()) @@ -139,6 +145,11 @@ CCamera& ViewGame::GetCamera() return *g_Game->GetView()->GetCamera(); } +CUnit* ViewGame::GetUnit(AtlasMessage::ObjectID id) +{ + return g_UnitMan.FindByID(id); +} + bool ViewGame::WantsHighFramerate() { if (g_Game->GetView()->GetCinema()->IsPlaying()) diff --git a/source/tools/atlas/GameInterface/View.h b/source/tools/atlas/GameInterface/View.h index 82ccd8b005..9e86de1c1e 100644 --- a/source/tools/atlas/GameInterface/View.h +++ b/source/tools/atlas/GameInterface/View.h @@ -1,6 +1,8 @@ #ifndef VIEW_H__ #define VIEW_H__ +class CUnit; + class ViewGame; class ViewActor; @@ -8,10 +10,12 @@ class View { public: virtual ~View(); - virtual void Update(float frameLength) = 0; - virtual void Render() = 0; + virtual void Update(float UNUSED(frameLength)) { }; + virtual void Render() { }; virtual CCamera& GetCamera() = 0; - virtual bool WantsHighFramerate() = 0; + virtual CUnit* GetUnit(AtlasMessage::ObjectID UNUSED(id)) { return NULL; } + virtual bool WantsHighFramerate() { return false; } + virtual void SetParam(const std::wstring& name, bool value); virtual void SetParam(const std::wstring& name, const AtlasMessage::Colour& value); @@ -30,10 +34,7 @@ public: class ViewNone : public View { public: - virtual void Update(float) { } - virtual void Render() { } virtual CCamera& GetCamera() { return dummyCamera; } - virtual bool WantsHighFramerate() { return false; } private: CCamera dummyCamera; }; @@ -45,6 +46,7 @@ public: virtual void Update(float frameLength); virtual void Render(); virtual CCamera& GetCamera(); + virtual CUnit* GetUnit(AtlasMessage::ObjectID id); virtual bool WantsHighFramerate(); private: @@ -61,7 +63,9 @@ public: virtual void Update(float frameLength); virtual void Render(); virtual CCamera& GetCamera(); + virtual CUnit* GetUnit(AtlasMessage::ObjectID id); virtual bool WantsHighFramerate(); + virtual void SetParam(const std::wstring& name, bool value); virtual void SetParam(const std::wstring& name, const AtlasMessage::Colour& value);