1
0
forked from 0ad/0ad

Atlas: Bits of tool-related code.

Game: Large screenshots (with ctrl+alt+F2).

This was SVN commit r2994.
This commit is contained in:
Ykkrosh 2005-10-24 01:53:03 +00:00
parent 034985c71f
commit 52a8793450
21 changed files with 242 additions and 49 deletions

View File

@ -110,6 +110,7 @@ sound.mastergain = 0.5
; > SYSTEM SETTINGS
hotkey.exit = Escape, "Ctrl+F4" ; Exit to desktop.
hotkey.screenshot = F2 ; Take PNG screenshot.
hotkey.bigscreenshot = "Ctrl+Alt+F2" ; Take large BMP screenshot.
hotkey.screenshot.watermark = "W" ; Toggle product/company watermark for official screenshots.
hotkey.wireframe = "Alt+W" ; Toggle wireframe mode.

View File

@ -30,27 +30,37 @@ CCamera::CCamera ()
CCamera::~CCamera ()
{
}
void CCamera::SetProjection (float nearp, float farp, float fov)
{
float h, w, Q;
m_NearPlane = nearp;
m_FarPlane = farp;
m_FOV = fov;
float Aspect = (float)m_ViewPort.m_Width/(float)m_ViewPort.m_Height;
w = 1/tanf (fov*0.5f*Aspect);
h = 1/tanf (fov*0.5f);
Q = m_FarPlane / (m_FarPlane - m_NearPlane);
m_ProjMat.SetZero ();
m_ProjMat._11 = w;
m_ProjMat._22 = h;
m_ProjMat._33 = (m_FarPlane+m_NearPlane)/(m_FarPlane-m_NearPlane);;
m_ProjMat._34 = -2*m_FarPlane*m_NearPlane/(m_FarPlane-m_NearPlane);
m_ProjMat._43 = 1.0f;
float w = tanf (m_FOV*0.5f*Aspect);
float h = tanf (m_FOV*0.5f);
m_ProjMat.SetZero ();
m_ProjMat._11 = 1/w;
m_ProjMat._22 = 1/h;
m_ProjMat._33 = (m_FarPlane+m_NearPlane)/(m_FarPlane-m_NearPlane);
m_ProjMat._34 = -2*m_FarPlane*m_NearPlane/(m_FarPlane-m_NearPlane);
m_ProjMat._43 = 1.0f;
}
void CCamera::SetProjectionTile (int tiles, int tile_x, int tile_y)
{
float Aspect = (float)m_ViewPort.m_Width/(float)m_ViewPort.m_Height;
float w = tanf (m_FOV*0.5f*Aspect) / tiles;
float h = tanf (m_FOV*0.5f) / tiles;
m_ProjMat._11 = 1/w;
m_ProjMat._22 = 1/h;
m_ProjMat._13 = -(1-tiles + 2*tile_x);
m_ProjMat._23 = -(1-tiles + 2*tile_y);
}
//Updates the frustum planes. Should be called
@ -183,7 +193,7 @@ void CCamera::GetScreenCoordinates( const CVector3D& world, float& x, float& y )
CVector3D screenspace = transform.Transform( world );
x = screenspace.X / screenspace.Z;
x = screenspace.X / screenspace.Z;
y = screenspace.Y / screenspace.Z;
x = ( x + 1 ) * 0.5f * g_Renderer.GetWidth();
y = ( 1 - y ) * 0.5f * g_Renderer.GetHeight();

View File

@ -37,6 +37,7 @@ class CCamera
//Methods for projection
void SetProjection (CMatrix3D *proj) { m_ProjMat = *proj; }
void SetProjection (float nearp, float farp, float fov);
void SetProjectionTile (int tiles, int tile_x, int tile_y);
CMatrix3D GetProjection () { return m_ProjMat; }
//Updates the frustum planes. Should be called

View File

@ -120,9 +120,16 @@ void JSI_Camera::Camera_Info::FreshenTarget()
JSBool JSI_Camera::getCamera( JSContext* cx, JSObject* UNUSED(obj), jsval UNUSED(id), jsval* vp )
{
JSObject* camera = JS_NewObject( cx, &JSI_Camera::JSI_class, NULL, NULL );
JS_SetPrivate( cx, camera, new Camera_Info( g_Game->GetView()->GetCamera() ) );
*vp = OBJECT_TO_JSVAL( camera );
if( g_Game && g_Game->GetView()->GetCamera() )
{
JSObject* camera = JS_NewObject( cx, &JSI_Camera::JSI_class, NULL, NULL );
JS_SetPrivate( cx, camera, new Camera_Info( g_Game->GetView()->GetCamera() ) );
*vp = OBJECT_TO_JSVAL( camera );
}
else
{
*vp = JSVAL_NULL;
}
return( JS_TRUE );
}

View File

@ -64,6 +64,10 @@ static InReaction MainInputHandler(const SDL_Event* ev)
WriteScreenshot("png");
return IN_HANDLED;
case HOTKEY_BIGSCREENSHOT:
WriteBigScreenshot("bmp", 10);
return IN_HANDLED;
default:
break;
}

View File

@ -65,6 +65,7 @@ static SHotkeyInfo hotkeyInfo[] =
{
{ HOTKEY_EXIT, "exit", SDLK_ESCAPE, 0 },
{ HOTKEY_SCREENSHOT, "screenshot", SDLK_PRINT, 0 },
{ HOTKEY_BIGSCREENSHOT, "bigscreenshot", 0, 0 },
{ HOTKEY_WIREFRAME, "wireframe", SDLK_w, 0 },
{ HOTKEY_CAMERA_RESET, "camera.reset", 0, 0 },
{ HOTKEY_CAMERA_RESET_ORIGIN, "camera.reset.origin", SDLK_h, 0 },

View File

@ -29,6 +29,7 @@ enum
{
HOTKEY_EXIT,
HOTKEY_SCREENSHOT,
HOTKEY_BIGSCREENSHOT,
HOTKEY_WIREFRAME,
HOTKEY_CAMERA_RESET,
HOTKEY_CAMERA_RESET_ORIGIN,

View File

@ -8,6 +8,10 @@
#include "lib/res/graphics/tex.h"
#include "ps/GameSetup/Config.h"
#include "ps/GameSetup/GameSetup.h"
#include "ps/Game.h"
#include "renderer/Renderer.h"
#include "maths/MathUtil.h"
static std::string SplitExts(const char *exts)
{
@ -208,3 +212,118 @@ void WriteScreenshot(const char* extension)
(void)tex_free(&t);
mem_free_h(img_hm);
}
// Similar to WriteScreenshot, but generates an image of size 640*tiles x 480*tiles.
void WriteBigScreenshot(const char* extension, int tiles)
{
// determine next screenshot number.
char fn[VFS_MAX_PATH];
// %04d -> always 4 digits, so sorting by filename works correctly.
const char* file_format_string = "screenshots/screenshot%04d.%s";
static int next_num = 1;
do
sprintf(fn, file_format_string, next_num++, extension);
while(vfs_exists(fn));
// Slightly ugly and inflexible: Always draw 640*480 tiles onto the screen, and
// hope the screen is actually large enough for that.
const int tile_w = 640, tile_h = 480;
debug_assert(g_xres >= tile_w && g_yres >= tile_h);
const int img_w = tile_w*tiles, img_h = tile_h*tiles;
const int bpp = 24;
GLenum fmt = GL_RGB;
int flags = TEX_BOTTOM_UP;
// we want writing BMP to be as fast as possible,
// so read data from OpenGL in BMP format to obviate conversion.
if(!stricmp(extension, "bmp"))
{
fmt = GL_BGR;
flags |= TEX_BGR;
}
const size_t img_size = img_w * img_h * bpp/8;
const size_t tile_size = tile_w * tile_h * bpp/8;
const size_t hdr_size = tex_hdr_size(fn);
Handle tile_hm, img_hm;
void* tile_data = mem_alloc(tile_size, 1, 0, &tile_hm);
void* img_data = mem_alloc(hdr_size+img_size, FILE_BLOCK_SIZE, 0, &img_hm);
if(!tile_data || !img_data)
{
debug_warn("not enough memory to write screenshot");
if (tile_data) mem_free_h(tile_hm);
if (img_data) mem_free_h(img_hm);
return;
}
Tex t;
GLvoid* img = (u8*)img_data + hdr_size;
if(tex_wrap(img_w, img_h, bpp, flags, img, &t) < 0)
return;
oglCheck();
// Resize various things so that the sizes and aspect ratios are correct
{
g_Renderer.Resize(tile_w, tile_h);
SViewPort vp = { 0, 0, tile_w, tile_h };
g_Game->GetView()->GetCamera()->SetViewPort(&vp);
g_Game->GetView()->GetCamera()->SetProjection (1, 5000, DEGTORAD(20));
}
// Temporarily move everything onto the front buffer, so the user can
// see the exciting progress as it renders (and can tell when it's finished).
// (It doesn't just use SwapBuffers, because it doesn't know whether to
// call the SDL version or the Atlas version.)
GLint oldReadBuffer, oldDrawBuffer;
glGetIntegerv(GL_READ_BUFFER, &oldReadBuffer);
glGetIntegerv(GL_DRAW_BUFFER, &oldDrawBuffer);
glDrawBuffer(GL_FRONT);
glReadBuffer(GL_FRONT);
// Render each tile
for (int tile_y = 0; tile_y < tiles; ++tile_y)
{
for (int tile_x = 0; tile_x < tiles; ++tile_x)
{
// Adjust the camera to render the appropriate region
g_Game->GetView()->GetCamera()->SetProjectionTile(tiles, tile_x, tile_y);
Render();
// Copy the tile pixels into the main image
glReadPixels(0, 0, tile_w, tile_h, fmt, GL_UNSIGNED_BYTE, tile_data);
for (int y = 0; y < tile_h; ++y)
{
void* dest = (char*)img + ((tile_y*tile_h + y) * img_w + (tile_x*tile_w)) * bpp/8;
void* src = (char*)tile_data + y * tile_w * bpp/8;
memcpy(dest, src, tile_w * bpp/8);
}
}
}
// Restor the buffer settings
glDrawBuffer(oldDrawBuffer);
glReadBuffer(oldReadBuffer);
// Restore the viewport settings
{
g_Renderer.Resize(g_xres, g_yres);
SViewPort vp = { 0, 0, g_xres, g_yres };
g_Game->GetView()->GetCamera()->SetViewPort(&vp);
g_Game->GetView()->GetCamera()->SetProjection (1, 5000, DEGTORAD(20));
g_Game->GetView()->GetCamera()->SetProjectionTile(1, 0, 0);
}
(void)tex_write(&t, fn);
(void)tex_free(&t);
mem_free_h(tile_hm);
mem_free_h(img_hm);
}

View File

@ -3,3 +3,4 @@ extern void WriteSystemInfo();
extern const wchar_t* ErrorString(int err);
extern void WriteScreenshot(const char* extension);
extern void WriteBigScreenshot(const char* extension, int tiles);

View File

@ -6,7 +6,7 @@ public:
ActionButton(wxWindow *parent,
const wxString& label,
actionFun fun,
void* data,
void* data,
const wxSize& size = wxDefaultSize,
long style = 0)
: wxButton(parent, wxID_ANY, label, wxDefaultPosition, size, style),

View File

@ -34,11 +34,13 @@ public:
void OnResize(wxSizeEvent&)
{
#ifndef UI_ONLY
// Be careful not to send 'resize' messages to the game before we've
// told it that this canvas exists
if (! m_SuppressResize)
POST_COMMAND(ResizeScreen(GetClientSize().GetWidth(), GetClientSize().GetHeight()));
// TODO: fix flashing
#endif // UI_ONLY
}
void InitSize()
@ -49,6 +51,7 @@ public:
bool KeyScroll(wxKeyEvent& evt, bool enable)
{
#ifndef UI_ONLY
int dir;
switch (evt.GetKeyCode())
{
@ -79,6 +82,7 @@ public:
{
POST_INPUT(ScrollConstant(dir, enable ? speed : 0.0f));
}
#endif // UI_ONLY
return true;
}
@ -87,7 +91,7 @@ public:
if (KeyScroll(evt, true))
return;
g_CurrentTool->OnKey(evt, ITool::KEY_DOWN);
GetCurrentTool().OnKey(evt, ITool::KEY_DOWN);
evt.Skip();
}
@ -97,7 +101,7 @@ public:
if (KeyScroll(evt, false))
return;
g_CurrentTool->OnKey(evt, ITool::KEY_UP);
GetCurrentTool().OnKey(evt, ITool::KEY_UP);
evt.Skip();
}
@ -156,7 +160,9 @@ public:
m_LastMousePos = evt.GetPosition();
}
g_CurrentTool->OnMouse(evt);
#ifndef UI_ONLY
GetCurrentTool().OnMouse(evt);
// TODO: if the tool responded to the mouse action, should we avoid moving
// the camera too? (This is mostly avoided by not sharing buttons between
@ -208,7 +214,7 @@ public:
}
}
}
#endif // UI_ONLY
}
private:
@ -273,6 +279,7 @@ enum
ID_Wireframe,
ID_MessageTrace,
ID_Screenshot,
};
BEGIN_EVENT_TABLE(ScenarioEditor, wxFrame)
@ -285,6 +292,7 @@ BEGIN_EVENT_TABLE(ScenarioEditor, wxFrame)
EVT_MENU(ID_Wireframe, ScenarioEditor::OnWireframe)
EVT_MENU(ID_MessageTrace, ScenarioEditor::OnMessageTrace)
EVT_MENU(ID_Screenshot, ScenarioEditor::OnScreenshot)
EVT_IDLE(ScenarioEditor::OnIdle)
END_EVENT_TABLE()
@ -338,6 +346,7 @@ ScenarioEditor::ScenarioEditor(wxWindow* parent)
{
menuMisc->AppendCheckItem(ID_Wireframe, _("&Wireframe"));
menuMisc->AppendCheckItem(ID_MessageTrace, _("Message debug trace"));
menuMisc->Append(ID_Screenshot, _("&Screenshot"));
}
//////////////////////////////////////////////////////////////////////////
@ -389,9 +398,6 @@ ScenarioEditor::ScenarioEditor(wxWindow* parent)
POST_COMMAND(CommandString("render_enable"));
#endif
// XXX
USE_TOOL(AlterElevation);
// Set up a timer to make sure tool-updates happen frequently (in addition
// to the idle handler (which makes them happen more frequently if there's nothing
// else to do))
@ -407,7 +413,7 @@ void ScenarioEditor::OnClose(wxCloseEvent&)
#endif
POST_COMMAND(CommandString("exit"));
SetCurrentTool(NULL);
SetCurrentTool(_T(""));
// TODO: If it's still rendering while we're destroying the canvas, things
// often crash.
@ -427,7 +433,7 @@ static void UpdateTool()
// TODO: Smoother timing stuff?
static double last = g_Timer.GetTime();
double time = g_Timer.GetTime();
g_CurrentTool->OnTick(time-last);
GetCurrentTool().OnTick(time-last);
last = time;
}
}
@ -467,6 +473,11 @@ void ScenarioEditor::OnMessageTrace(wxCommandEvent& event)
POST_COMMAND(MessageTrace(event.IsChecked()));
}
void ScenarioEditor::OnScreenshot(wxCommandEvent& event)
{
POST_COMMAND(Screenshot(10));
}
//////////////////////////////////////////////////////////////////////////
AtlasMessage::Position::Position(const wxPoint& pt)

View File

@ -17,6 +17,7 @@ public:
void OnWireframe(wxCommandEvent& event);
void OnMessageTrace(wxCommandEvent& event);
void OnScreenshot(wxCommandEvent& event);
static AtlasWindowCommandProc& GetCommandProc();

View File

@ -3,8 +3,10 @@
#include "Terrain.h"
#include "Buttons/ActionButton.h"
#include "Buttons/ToolButton.h"
#include "General/Datafile.h"
#include "ScenarioEditor/Tools/Common/Brushes.h"
#include "ScenarioEditor/Tools/Common/Tools.h"
#include "GameInterface/Messages.h"
@ -14,15 +16,14 @@ TerrainSidebar::TerrainSidebar(wxWindow* parent)
: Sidebar(parent)
{
// TODO: Less ugliness
// TODO: Intercept arrow keys and send them to the GL window
m_MainSizer->Add(new wxStaticText(this, wxID_ANY, _T("TODO: Make this much less ugly\n")));
{
wxSizer* sizer = new wxStaticBoxSizer(wxHORIZONTAL, this, _("Elevation tools"));
sizer->Add(new wxButton(this, wxID_ANY, _("Modify"), wxDefaultPosition, wxSize(50,20)));
sizer->Add(new wxButton(this, wxID_ANY, _("Smooth"), wxDefaultPosition, wxSize(50,20)));
sizer->Add(new wxButton(this, wxID_ANY, _("Sample"), wxDefaultPosition, wxSize(50,20)));
sizer->Add(new ToolButton(this, _("Modify"), _T("AlterElevation"), wxSize(50,20)));
sizer->Add(new ToolButton(this, _("Smooth"), _T(""), wxSize(50,20)));
sizer->Add(new ToolButton(this, _("Sample"), _T(""), wxSize(50,20)));
m_MainSizer->Add(sizer);
}

View File

@ -8,7 +8,7 @@ using AtlasMessage::Position;
class AlterElevation : public StateDrivenTool<AlterElevation>
{
private:
DECLARE_DYNAMIC_CLASS(AlterElevation);
int m_Direction; // +1 = raise, -1 = lower
Position m_Pos;
@ -118,5 +118,4 @@ public:
Lowering;
};
DECLARE_TOOL(AlterElevation);
IMPLEMENT_DYNAMIC_CLASS(AlterElevation, StateDrivenTool<AlterElevation>);

View File

@ -5,7 +5,7 @@
#include "GameInterface/Messages.h"
Brush::Brush()
: m_Shape(SQUARE), m_Size(4), m_Strength(1.f), m_IsActive(false)
: m_Shape(CIRCLE), m_Size(4), m_Strength(1.f), m_IsActive(false)
{
}
@ -172,8 +172,8 @@ END_EVENT_TABLE()
void Brush::CreateUI(wxWindow* parent, wxSizer* sizer)
{
wxArrayString shapes; // Must match order of BrushShape enum
shapes.Add(_("Square"));
shapes.Add(_("Circle"));
shapes.Add(_("Square"));
// TODO (maybe): get rid of the extra static box, by not using wxRadioBox
sizer->Add(new BrushShapeCtrl(parent, shapes, *this));

View File

@ -30,7 +30,7 @@ private:
// If active, send SetBrush message to the game
void Send();
enum BrushShape { SQUARE = 0, CIRCLE };
enum BrushShape { CIRCLE = 0, SQUARE};
BrushShape m_Shape;
int m_Size;
float m_Strength;

View File

@ -5,17 +5,33 @@
class DummyTool : public ITool
{
void Shutdown() {}
void OnMouse(wxMouseEvent& evt) { evt.Skip(); }
void OnKey(wxKeyEvent& evt, KeyEventType) { evt.Skip(); }
void OnTick(float) {}
} dummy;
ITool* g_CurrentTool = &dummy;
static ITool* g_CurrentTool = &dummy;
void SetCurrentTool(ITool* tool)
ITool& GetCurrentTool()
{
return *g_CurrentTool;
}
void SetCurrentTool(const wxString& name)
{
if (g_CurrentTool != &dummy)
{
g_CurrentTool->Shutdown();
delete g_CurrentTool;
}
ITool* tool = NULL;
if (name.Len())
{
tool = wxDynamicCast(wxCreateDynamicObject(name), ITool);
wxASSERT(tool);
}
if (tool == NULL)
g_CurrentTool = &dummy;

View File

@ -7,11 +7,12 @@
class wxMouseEvent;
class wxKeyEvent;
class ITool
class ITool : public wxObject
{
public:
enum KeyEventType { KEY_DOWN, KEY_UP, KEY_CHAR };
virtual void Shutdown() = 0;
virtual void OnMouse(wxMouseEvent& evt) = 0;
virtual void OnKey(wxKeyEvent& evt, KeyEventType dir) = 0;
virtual void OnTick(float dt) = 0; // dt in seconds
@ -19,13 +20,8 @@ public:
virtual ~ITool() {};
};
#define DECLARE_TOOL(name) ITool* CreateTool_##name() { return new name(); }
#define USE_TOOL(name) { extern ITool* CreateTool_##name(); SetCurrentTool(CreateTool_##name()); }
extern ITool* g_CurrentTool;
extern void SetCurrentTool(ITool*); // for internal use only
extern ITool& GetCurrentTool();
extern void SetCurrentTool(const wxString& name); // should usually only be used by tool buttons
//////////////////////////////////////////////////////////////////////////
@ -62,8 +58,11 @@ public:
{
}
~StateDrivenTool()
virtual void Shutdown()
{
// This can't be done in the destructor, because ~StateDrivenTool
// is not called until after the subclass has been destroyed and its
// vtable (containing OnDisable) has been removed.
SetState(&Disabled);
}

View File

@ -6,6 +6,8 @@
#include "renderer/Renderer.h"
#include "gui/GUI.h"
#include "ps/CConsole.h"
#include "ps/Game.h"
#include "maths/MathUtil.h"
#include "ps/GameSetup/Config.h"
#include "ps/GameSetup/GameSetup.h"
@ -71,8 +73,14 @@ MESSAGEHANDLER(ResizeScreen)
if (g_yres <= 2) g_yres = 2;
SViewPort vp = { 0, 0, g_xres, g_yres };
g_Renderer.SetViewport(vp);
if (g_Game)
{
g_Game->GetView()->GetCamera()->SetViewPort(&vp);
g_Game->GetView()->GetCamera()->SetProjection (1, 5000, DEGTORAD(20));
}
g_Renderer.SetViewport(vp);
g_Renderer.Resize(g_xres, g_yres);
g_GUI.UpdateResolution();

View File

@ -3,6 +3,9 @@
#include "MessageHandler.h"
#include "../MessagePasserImpl.h"
#include "ps/GameSetup/Config.h"
#include "ps/Util.h"
namespace AtlasMessage {
MESSAGEHANDLER(MessageTrace)
@ -11,4 +14,10 @@ MESSAGEHANDLER(MessageTrace)
((MessagePasserImpl<mInput>*)g_MessagePasser_Input)->SetTrace(msg->enable);
}
MESSAGEHANDLER(Screenshot)
{
// TODO: allow non-big screenshots too
WriteBigScreenshot("bmp", msg->tiles);
}
}

View File

@ -40,6 +40,10 @@ COMMAND(MessageTrace,
((bool, enable))
);
COMMAND(Screenshot,
((int, tiles)) // the final image will be (640*tiles)x(480*tiles)
);
//////////////////////////////////////////////////////////////////////////
COMMAND(Brush,