# 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.
This commit is contained in:
parent
2f53eea71a
commit
b6c1022566
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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<std::vector<AtlasMessage::ObjectID> > m_ObjectSelection;
|
||||
Observable<ObjectSettings> m_ObjectSettings;
|
||||
bool m_Wireframe;
|
||||
wxColour m_BackgroundColour;
|
||||
bool m_Walking;
|
||||
|
@ -31,6 +31,13 @@ typedef boost::signals::scoped_connection ObservableScopedConnection;
|
||||
template <typename T> class Observable : public T
|
||||
{
|
||||
public:
|
||||
Observable() {}
|
||||
|
||||
template <typename T1>
|
||||
explicit Observable(const T1& a1) : T(a1) {}
|
||||
template <typename T1, typename T2>
|
||||
explicit Observable(T1& a1, T2 a2) : T(a1, a2) {}
|
||||
|
||||
template<typename C> ObservableConnection RegisterObserver(int order, void (C::*callback) (const T&), C* obj)
|
||||
{
|
||||
return m_Signal.connect(order, boost::bind(std::mem_fun(callback), obj, _1));
|
||||
|
@ -135,11 +135,6 @@ LightControl::LightControl(wxWindow* parent, const wxSize& size, Observable<Atla
|
||||
m_Conn = environment.RegisterObserver(0, &LightControl::OnSettingsChange, this);
|
||||
}
|
||||
|
||||
LightControl::~LightControl()
|
||||
{
|
||||
m_Conn.disconnect();
|
||||
}
|
||||
|
||||
void LightControl::OnSettingsChange(const AtlasMessage::sEnvironmentSettings& settings)
|
||||
{
|
||||
m_Sphere->theta = settings.sunrotation;
|
||||
|
@ -11,7 +11,6 @@ class LightControl : public wxPanel
|
||||
{
|
||||
public:
|
||||
LightControl(wxWindow* parent, const wxSize& size, Observable<AtlasMessage::sEnvironmentSettings>& environment);
|
||||
~LightControl();
|
||||
|
||||
void OnSettingsChange(const AtlasMessage::sEnvironmentSettings& settings);
|
||||
|
||||
@ -19,7 +18,7 @@ public:
|
||||
|
||||
private:
|
||||
Observable<AtlasMessage::sEnvironmentSettings>& m_Environment;
|
||||
ObservableConnection m_Conn;
|
||||
ObservableScopedConnection m_Conn;
|
||||
LightSphere* m_Sphere;
|
||||
};
|
||||
|
||||
|
@ -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<wxWindow*> m_ComboBoxes;
|
||||
wxSizer* m_Sizer;
|
||||
|
||||
// Event handler shared by all the combo boxes created by this window
|
||||
void OnSelect(wxCommandEvent& evt)
|
||||
{
|
||||
std::set<wxString> 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<ObjectSettings::Group>& 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<ObjectSettings::Group>& variation = g_ObjectSettings.GetActorVariation();
|
||||
|
||||
// For each group, set the corresponding combobox's value to the chosen one
|
||||
size_t i = 0;
|
||||
for (std::vector<ObjectSettings::Group>::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));
|
||||
|
@ -0,0 +1,129 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "VariationControl.h"
|
||||
|
||||
#include "ScenarioEditor/Tools/Common/ObjectSettings.h"
|
||||
|
||||
VariationControl::VariationControl(wxWindow* parent, Observable<ObjectSettings>& 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<wxString> 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<ObjectSettings::Group>& 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<ObjectSettings::Group>& variation = m_ObjectSettings.GetActorVariation();
|
||||
|
||||
// For each group, set the corresponding combobox's value to the chosen one
|
||||
size_t i = 0;
|
||||
for (std::vector<ObjectSettings::Group>::const_iterator group = variation.begin();
|
||||
group != variation.end() && i < m_ComboBoxes.size();
|
||||
++group, ++i)
|
||||
{
|
||||
m_ComboBoxes[i]->SetValue(group->chosen);
|
||||
}
|
||||
}
|
@ -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>& objectSettings);
|
||||
|
||||
private:
|
||||
void OnSelect(wxCommandEvent& evt);
|
||||
void OnObjectSettingsChange(const ObjectSettings& settings);
|
||||
void RefreshObjectSettings();
|
||||
|
||||
ObservableScopedConnection m_Conn;
|
||||
|
||||
Observable<ObjectSettings>& m_ObjectSettings;
|
||||
std::vector<wxComboBox*> m_ComboBoxes;
|
||||
wxSizer* m_Sizer;
|
||||
};
|
||||
|
||||
#endif // VARIATIONCONTROL_H__
|
@ -5,17 +5,12 @@
|
||||
#include "GameInterface/Messages.h"
|
||||
#include "ScenarioEditor/Tools/Common/Tools.h"
|
||||
|
||||
Observable<ObjectSettings> g_ObjectSettings;
|
||||
Observable<ObjectSettings> g_ObjectSettings(g_SelectedObjects, AtlasMessage::eRenderView::GAME);
|
||||
|
||||
ObjectSettings::ObjectSettings()
|
||||
: m_PlayerID(0)
|
||||
ObjectSettings::ObjectSettings(Observable<std::vector<AtlasMessage::ObjectID> >& 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<AtlasMessage::ObjectID>
|
||||
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<AtlasMessage::ObjectID>
|
||||
|
||||
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()));
|
||||
}
|
@ -16,8 +16,7 @@ namespace AtlasMessage
|
||||
class ObjectSettings
|
||||
{
|
||||
public:
|
||||
ObjectSettings();
|
||||
~ObjectSettings();
|
||||
ObjectSettings(Observable<std::vector<AtlasMessage::ObjectID> >& selectedObjects, int view);
|
||||
|
||||
int GetPlayerID() const;
|
||||
void SetPlayerID(int playerID);
|
||||
@ -37,6 +36,10 @@ public:
|
||||
AtlasMessage::sObjectSettings GetSettings() const;
|
||||
|
||||
private:
|
||||
Observable<std::vector<AtlasMessage::ObjectID> >& m_SelectedObjects;
|
||||
|
||||
int m_View;
|
||||
|
||||
// 0 = gaia, 1..inf = normal players
|
||||
int m_PlayerID;
|
||||
|
||||
@ -49,7 +52,7 @@ private:
|
||||
std::vector<wxArrayString> m_VariantGroups;
|
||||
|
||||
// Observe changes to unit selection
|
||||
ObservableConnection m_Conn;
|
||||
ObservableScopedConnection m_Conn;
|
||||
void OnSelectionChange(const std::vector<AtlasMessage::ObjectID>& selection);
|
||||
|
||||
// Transfer current settings to the currently selected unit (if any)
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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,
|
||||
|
@ -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<CStr>& selections)
|
||||
{
|
||||
CUnit* unit = g_UnitMan.FindByID(msg->id);
|
||||
CUnit* unit = View::GetView(msg->view)->GetUnit(msg->id);
|
||||
if (! unit) return;
|
||||
|
||||
unit->SetPlayerID(player);
|
||||
|
@ -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))
|
||||
);
|
||||
|
@ -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())
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user