mirror of
https://gitea.wildfiregames.com/0ad/0ad
synced 2026-06-16 05:13:58 -07:00
Color: Moved SColor* structs into SColor.h, so they can be used without indirectly including CVector[34]D. Terrain: Added 'base colour', for the Actor Viewer to be able to modulate the colour of normally-white terrain. Removed some "using namespace std" (because it doesn't make the code easier to read, and it defeats the point of namespaces, and the rest of the code doesn't do it). This was SVN commit r4392.
376 lines
11 KiB
C++
376 lines
11 KiB
C++
#include "stdafx.h"
|
|
|
|
#include "ActorViewer.h"
|
|
|
|
#include "wx/treectrl.h"
|
|
|
|
#include "General/Datafile.h"
|
|
|
|
#include "ScenarioEditor/Tools/Common/Tools.h"
|
|
#include "ScenarioEditor/ScenarioEditor.h"
|
|
#include "ScenarioEditor/Sections/Environment/LightControl.h"
|
|
|
|
#include "GameInterface/Messages.h"
|
|
|
|
#include "CustomControls/Canvas/Canvas.h"
|
|
#include "CustomControls/ColourDialog/ColourDialog.h"
|
|
#include "CustomControls/SnapSplitterWindow/SnapSplitterWindow.h"
|
|
|
|
#include "ActorEditor/ActorEditor.h"
|
|
|
|
using namespace AtlasMessage;
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
wxWindow* Tooltipped(wxWindow* window, const wxString& tip)
|
|
{
|
|
window->SetToolTip(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)
|
|
{
|
|
}
|
|
|
|
void PostLookAt()
|
|
{
|
|
POST_MESSAGE(LookAt, (eRenderView::ACTOR,
|
|
Position(m_Distance*sin(m_Angle), m_Distance/2.f, m_Distance*cos(m_Angle)),
|
|
Position(0, 0, 0)));
|
|
}
|
|
|
|
protected:
|
|
virtual void HandleMouseEvent(wxMouseEvent& evt)
|
|
{
|
|
bool camera_changed = false;
|
|
|
|
if (evt.GetWheelRotation())
|
|
{
|
|
float speed = -1.f * ScenarioEditor::GetSpeedModifier();
|
|
|
|
m_Distance += evt.GetWheelRotation() * speed / evt.GetWheelDelta();
|
|
|
|
camera_changed = true;
|
|
}
|
|
|
|
if (evt.ButtonDown(wxMOUSE_BTN_LEFT))
|
|
{
|
|
m_LastX = evt.GetX();
|
|
m_LastY = evt.GetY();
|
|
m_LastIsValid = true;
|
|
}
|
|
else if (evt.Dragging() && evt.ButtonIsDown(wxMOUSE_BTN_LEFT) && m_LastIsValid)
|
|
{
|
|
int dx = evt.GetX() - m_LastX;
|
|
int dy = evt.GetY() - m_LastY;
|
|
m_LastX = evt.GetX();
|
|
m_LastY = evt.GetY();
|
|
|
|
m_Angle += dx * M_PI/256.f * ScenarioEditor::GetSpeedModifier();
|
|
m_Distance += dy / 8.f * ScenarioEditor::GetSpeedModifier();
|
|
camera_changed = true;
|
|
}
|
|
else if (evt.ButtonUp(wxMOUSE_BTN_ANY))
|
|
{
|
|
// 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
|
|
// the last buttondown.
|
|
// (TODO: does this affect the scenario editor too?)
|
|
m_LastIsValid = false;
|
|
}
|
|
|
|
m_Distance = std::max(m_Distance, 1/64.f); // don't let people fly through the origin
|
|
|
|
if (camera_changed)
|
|
PostLookAt();
|
|
}
|
|
|
|
private:
|
|
float m_Distance;
|
|
float m_Angle;
|
|
int m_LastX, m_LastY;
|
|
bool m_LastIsValid;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
class StringTreeItemData : public wxTreeItemData
|
|
{
|
|
public:
|
|
StringTreeItemData(const wxString& string) : m_String(string) {}
|
|
wxString m_String;
|
|
};
|
|
|
|
enum
|
|
{
|
|
ID_Actors,
|
|
ID_Animations,
|
|
ID_Play,
|
|
ID_Pause,
|
|
ID_Slow,
|
|
ID_Edit,
|
|
ID_Wireframe,
|
|
ID_Background,
|
|
ID_Walking
|
|
};
|
|
|
|
BEGIN_EVENT_TABLE(ActorViewer, wxFrame)
|
|
EVT_CLOSE(ActorViewer::OnClose)
|
|
EVT_TREE_SEL_CHANGED(ID_Actors, ActorViewer::OnTreeSelection)
|
|
EVT_COMBOBOX(ID_Animations, ActorViewer::OnAnimationSelection)
|
|
EVT_TEXT_ENTER(ID_Animations, ActorViewer::OnAnimationSelection)
|
|
EVT_BUTTON(ID_Play, ActorViewer::OnSpeedButton)
|
|
EVT_BUTTON(ID_Pause, ActorViewer::OnSpeedButton)
|
|
EVT_BUTTON(ID_Slow, ActorViewer::OnSpeedButton)
|
|
EVT_BUTTON(ID_Edit, ActorViewer::OnEditButton)
|
|
EVT_BUTTON(ID_Wireframe, ActorViewer::OnWireframeButton)
|
|
EVT_BUTTON(ID_Background, ActorViewer::OnBackgroundButton)
|
|
EVT_BUTTON(ID_Walking, ActorViewer::OnWalkingButton)
|
|
END_EVENT_TABLE()
|
|
|
|
static void SendToGame(const AtlasMessage::sEnvironmentSettings& settings)
|
|
{
|
|
POST_COMMAND(SetEnvironmentSettings, (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)
|
|
{
|
|
SetIcon(wxIcon(_T("ICON_ActorEditor")));
|
|
|
|
SnapSplitterWindow* splitter = new SnapSplitterWindow(this, 0);
|
|
splitter->SetDefaultSashPosition(250);
|
|
|
|
wxPanel* sidePanel = new wxPanel(splitter);
|
|
|
|
// TODO: don't have this duplicated from ScenarioEditor.cpp
|
|
int glAttribList[] = {
|
|
WX_GL_RGBA,
|
|
WX_GL_DOUBLEBUFFER,
|
|
WX_GL_DEPTH_SIZE, 24,
|
|
WX_GL_BUFFER_SIZE, 24,
|
|
WX_GL_MIN_ALPHA, 8,
|
|
0
|
|
};
|
|
|
|
ActorCanvas* canvas = new ActorCanvas(splitter, glAttribList);
|
|
|
|
splitter->SplitVertically(sidePanel, canvas);
|
|
|
|
wglMakeCurrent(NULL, NULL);
|
|
POST_MESSAGE(SetContext, (canvas->GetContext()));
|
|
|
|
POST_MESSAGE(Init, (false));
|
|
|
|
canvas->InitSize();
|
|
canvas->PostLookAt();
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
// Construct a tree containing all the available actors
|
|
|
|
qGetObjectsList qry;
|
|
qry.Post();
|
|
std::vector<sObjectsListItem> objects = *qry.objects;
|
|
|
|
m_TreeCtrl = new wxTreeCtrl(sidePanel, ID_Actors);
|
|
wxTreeItemId root = m_TreeCtrl->AddRoot(_("Actors"));
|
|
|
|
std::map<std::wstring, wxTreeItemId> treeEntries;
|
|
|
|
wxRegEx stripDirs (_T("^([^/]+)/"), wxRE_ADVANCED); // the non-empty string up to the first slash
|
|
|
|
for (std::vector<sObjectsListItem>::iterator it = objects.begin(); it != objects.end(); ++it)
|
|
{
|
|
if (it->type != 1)
|
|
continue;
|
|
|
|
wxString name = it->name.c_str();
|
|
// Loop through the directory components of the name, stripping them
|
|
// off and search down the tree hierarchy
|
|
wxString path = _T("");
|
|
wxTreeItemId treeItem = root;
|
|
while (stripDirs.Matches(name))
|
|
{
|
|
wxString dir = stripDirs.GetMatch(name, 1);
|
|
path += dir + _T("/");
|
|
|
|
// If we've got 'path' in the tree already, use it
|
|
std::map<std::wstring, wxTreeItemId>::iterator entry = treeEntries.find(path.c_str());
|
|
if (entry != treeEntries.end())
|
|
{
|
|
treeItem = entry->second;
|
|
}
|
|
else
|
|
{
|
|
// Add this new path into the tree
|
|
treeItem = m_TreeCtrl->AppendItem(treeItem, dir);
|
|
treeEntries.insert(std::make_pair(path, treeItem));
|
|
}
|
|
|
|
// Remove the leading directory name from the full filename
|
|
stripDirs.Replace(&name, _T(""));
|
|
}
|
|
|
|
m_TreeCtrl->AppendItem(treeItem, name, -1, -1, new StringTreeItemData(it->name.c_str()));
|
|
}
|
|
|
|
m_TreeCtrl->Expand(root);
|
|
|
|
|
|
wxArrayString animations;
|
|
|
|
AtObj animationsList (Datafile::ReadList("animations"));
|
|
for (AtIter it = animationsList["item"]; it.defined(); ++it)
|
|
animations.Add(it);
|
|
|
|
m_AnimationBox = new wxComboBox(sidePanel, ID_Animations, _T("Idle"), wxDefaultPosition, wxDefaultSize, animations);
|
|
|
|
m_EnvironmentSettings.sunelevation = 45 * M_PI/180;
|
|
m_EnvironmentSettings.sunrotation = 315 * M_PI/180;
|
|
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);
|
|
m_EnvConn = m_EnvironmentSettings.RegisterObserver(0, &SendToGame);
|
|
SendToGame(m_EnvironmentSettings);
|
|
|
|
wxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
|
|
wxSizer* bottomSizer = new wxBoxSizer(wxHORIZONTAL);
|
|
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));
|
|
|
|
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));
|
|
|
|
sidePanel->SetSizer(mainSizer);
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
// Start by displaying the default non-existent actor
|
|
m_CurrentActor = _T("structures/fndn_1x1.xml");
|
|
SetActorView();
|
|
|
|
POST_MESSAGE(RenderEnable, (eRenderView::ACTOR));
|
|
}
|
|
|
|
void ActorViewer::OnClose(wxCloseEvent& WXUNUSED(event))
|
|
{
|
|
POST_MESSAGE(Shutdown, ());
|
|
|
|
AtlasMessage::qExit().Post();
|
|
// blocks until engine has noticed the message, so we won't be
|
|
// destroying the GLCanvas while it's still rendering
|
|
|
|
Destroy();
|
|
}
|
|
|
|
void ActorViewer::SetActorView(bool flushCache)
|
|
{
|
|
POST_MESSAGE(SetActorViewer, (m_CurrentActor.c_str(), m_AnimationBox->GetValue().c_str(), m_CurrentSpeed, flushCache));
|
|
}
|
|
|
|
void ActorViewer::OnTreeSelection(wxTreeEvent& event)
|
|
{
|
|
wxTreeItemData* data = m_TreeCtrl->GetItemData(event.GetItem());
|
|
if (! data)
|
|
return;
|
|
|
|
m_CurrentActor = static_cast<StringTreeItemData*>(data)->m_String;
|
|
SetActorView();
|
|
}
|
|
|
|
void ActorViewer::OnAnimationSelection(wxCommandEvent& WXUNUSED(event))
|
|
{
|
|
SetActorView();
|
|
}
|
|
|
|
void ActorViewer::OnSpeedButton(wxCommandEvent& event)
|
|
{
|
|
if (event.GetId() == ID_Play)
|
|
m_CurrentSpeed = 1.f;
|
|
else if (event.GetId() == ID_Pause)
|
|
m_CurrentSpeed = 0.f;
|
|
else if (event.GetId() == ID_Slow)
|
|
m_CurrentSpeed = 0.1f;
|
|
else
|
|
{
|
|
wxLogDebug(_T("Invalid OnSpeedButton (%d)"), event.GetId());
|
|
m_CurrentSpeed = 1.f;
|
|
}
|
|
|
|
SetActorView();
|
|
}
|
|
|
|
void ActorViewer::OnActorEdited()
|
|
{
|
|
SetActorView(true);
|
|
}
|
|
|
|
void ActorViewer::OnEditButton(wxCommandEvent& WXUNUSED(event))
|
|
{
|
|
wxFileName dir (_T("mods/official/art/actors/") + m_CurrentActor, wxPATH_UNIX);
|
|
dir.MakeAbsolute(Datafile::GetDataDirectory());
|
|
|
|
ActorEditor* ed = new ActorEditor(NULL);
|
|
ed->OpenFile(dir.GetFullPath());
|
|
ed->Show();
|
|
|
|
m_ActorConns.Add(ed->sig_FileSaved.connect(
|
|
boost::bind(std::mem_fun(&ActorViewer::OnActorEdited), this)
|
|
));
|
|
}
|
|
|
|
void ActorViewer::OnWireframeButton(wxCommandEvent& WXUNUSED(event))
|
|
{
|
|
m_Wireframe = !m_Wireframe;
|
|
POST_MESSAGE(SetViewParamB, (eRenderView::ACTOR, L"wireframe", m_Wireframe));
|
|
}
|
|
|
|
void ActorViewer::OnBackgroundButton(wxCommandEvent& WXUNUSED(event))
|
|
{
|
|
ColourDialog dlg (this, _T("Actor Viewer/BackgroundColour"), m_BackgroundColour);
|
|
|
|
if (dlg.ShowModal() == wxID_OK)
|
|
{
|
|
wxColour& c = dlg.GetColourData().GetColour();
|
|
m_BackgroundColour = c;
|
|
POST_MESSAGE(SetViewParamC, (eRenderView::ACTOR, L"background",
|
|
AtlasMessage::Colour(c.Red(), c.Green(), c.Blue())));
|
|
}
|
|
}
|
|
|
|
void ActorViewer::OnWalkingButton(wxCommandEvent& WXUNUSED(event))
|
|
{
|
|
wxToolTip::Enable(true);
|
|
SetToolTip(L"Hello world!");
|
|
SetHelpText(L"Help text");
|
|
m_Walking = !m_Walking;
|
|
POST_MESSAGE(SetViewParamB, (eRenderView::ACTOR, L"walk", m_Walking));
|
|
}
|