1
0
forked from 0ad/0ad

Atlas: Vaguely usable object placement code. Bug fixes.

This was SVN commit r3186.
This commit is contained in:
Ykkrosh 2005-12-02 21:08:10 +00:00
parent 0a252de08c
commit 547293ee49
18 changed files with 268 additions and 96 deletions

View File

@ -216,6 +216,23 @@ CVector3D CCamera::GetWorldCoordinates( int px, int py )
return( origin + dir * ( ( 50.0f - origin.Y ) / dir.Y ) );
}
CVector3D CCamera::GetWorldCoordinates(int px, int py, float h)
{
CPlane plane;
plane.Set(CVector3D(0.f, 1.f, 0.f), CVector3D(0.f, h, 0.f)); // upwards normal, passes through h
CVector3D origin, dir, delta, currentTarget;
BuildCameraRay(px, py, origin, dir);
if (plane.FindRayIntersection(origin, dir, &currentTarget))
return currentTarget;
// No intersection with the infinite plane - nothing sensible can be returned,
// so just choose an arbitrary point on the plane
return CVector3D(0.f, h, 0.f);
}
CVector3D CCamera::GetFocus()
{
// Basically the same as GetWorldCoordinates

View File

@ -18,7 +18,7 @@
extern int g_mouse_x, g_mouse_y;
//view port
// view port
struct SViewPort
{
unsigned int m_X;
@ -34,22 +34,22 @@ class CCamera
CCamera ();
~CCamera ();
//Methods for projection
// 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
//everytime the view or projection matrices are
//altered.
// Updates the frustum planes. Should be called
// everytime the view or projection matrices are
// altered.
void UpdateFrustum ();
CFrustum GetFrustum () { return m_ViewFrustum; }
void SetViewPort (SViewPort *viewport);
SViewPort GetViewPort () { return m_ViewPort; }
//getters
// getters
float GetNearPlane() const { return m_NearPlane; }
float GetFarPlane() const { return m_FarPlane; }
float GetFOV() const { return m_FOV; }
@ -62,40 +62,42 @@ class CCamera
// Build a ray passing through the screen coordinate (px, py) and the camera
/////////////////////////////////////////////////////////////////////////////////////////
// BuildCameraRay: calculate origin and ray direction of a ray through
// the pixel (px,py) on the screen
void BuildCameraRay( int px, int py, CVector3D& origin, CVector3D& dir );
// BuildCameraRay: as previous, using global mouse position
void BuildCameraRay( CVector3D& origin, CVector3D& dir )
// BuildCameraRay: calculate origin and ray direction of a ray through
// the pixel (px,py) on the screen
void BuildCameraRay(int px, int py, CVector3D& origin, CVector3D& dir);
// BuildCameraRay: as previous, using global mouse position
void BuildCameraRay(CVector3D& origin, CVector3D& dir)
{
BuildCameraRay( g_mouse_x, g_mouse_y, origin, dir );
BuildCameraRay(g_mouse_x, g_mouse_y, origin, dir);
}
// General helpers that seem to fit here
// General helpers that seem to fit here
// Get the screen-space coordinates corresponding to a given world-space position
void GetScreenCoordinates( const CVector3D& world, float& x, float& y );
// Get the screen-space coordinates corresponding to a given world-space position
void GetScreenCoordinates(const CVector3D& world, float& x, float& y);
// Get the point on the terrain corresponding to pixel (px,py) (or the mouse coordinates)
CVector3D GetWorldCoordinates( int px, int py );
CVector3D GetWorldCoordinates() { return( GetWorldCoordinates( g_mouse_x, g_mouse_y ) ); }
// Get the point on the terrain the camera is pointing towards
// Get the point on the terrain corresponding to pixel (px,py) (or the mouse coordinates)
CVector3D GetWorldCoordinates(int px, int py);
CVector3D GetWorldCoordinates() { return GetWorldCoordinates(g_mouse_x, g_mouse_y); }
// Get the point on the plane at height h corresponding to pixel (px,py)
CVector3D GetWorldCoordinates(int px, int py, float h);
// Get the point on the terrain the camera is pointing towards
CVector3D GetFocus();
// Build an orientation matrix from camera position, camera focus point, and up-vector
void LookAt( const CVector3D& camera, const CVector3D& orientation, const CVector3D& up );
void LookAt(const CVector3D& camera, const CVector3D& orientation, const CVector3D& up);
// Build an orientation matrix from camera position, camera orientation, and up-vector
void LookAlong( CVector3D camera, CVector3D focus, CVector3D up );
void LookAlong(CVector3D camera, CVector3D focus, CVector3D up);
public:
//This is the orientation matrix. The inverse of this
//is the view matrix
// This is the orientation matrix. The inverse of this
// is the view matrix
CMatrix3D m_Orientation;
private:
//keep the projection matrix private
//so we can't fiddle with it.
// keep the projection matrix private
// so we can't fiddle with it.
CMatrix3D m_ProjMat;
float m_NearPlane;

View File

@ -181,8 +181,11 @@ void CEntityManager::renderAll()
void CEntityManager::destroy( u16 handle )
{
m_reaper.push_back( m_entities[handle].m_entity );
m_entities[handle].m_entity->me.m_handle = INVALID_HANDLE;
if (m_entities[handle].m_entity->me.m_handle != INVALID_HANDLE)
{
m_reaper.push_back( m_entities[handle].m_entity );
m_entities[handle].m_entity->me.m_handle = INVALID_HANDLE;
}
}
bool CEntityManager::m_extant = false;

View File

@ -65,7 +65,7 @@ void CSimulation::Update(double frameTime)
{
m_DeltaTime += frameTime;
if( m_DeltaTime >= 0.0 )
if( m_DeltaTime >= 0.0 && frameTime )
{
PROFILE( "simulation turn" );
// A new simulation frame is required.

View File

@ -37,6 +37,8 @@ public:
int Initialize(CGameAttributes *pGameAttributes);
// Perform all CSimulation updates for the specified elapsed time.
// (If frameTime=0, no simulation updates are done, but the graphics
// are interpolated.)
void Update(double frameTime);
// Calculate the message mask of a message to be queued

View File

@ -4,6 +4,7 @@
#include "Buttons/ActionButton.h"
#include "General/Datafile.h"
#include "ScenarioEditor/Tools/Common/Tools.h"
#include "GameInterface/Messages.h"
@ -18,6 +19,12 @@ static void LoadMap(void*)
{
// TODO: Work when the map is not in .../maps/scenarios/
std::wstring map = dlg.GetFilename().c_str();
// Deactivate tools, so they don't carry forwards into the new CWorld
// and crash.
SetCurrentTool(_T(""));
// TODO: clear the undo buffer, etc
POST_MESSAGE(LoadMap(map));
}

View File

@ -53,8 +53,9 @@ void ObjectSidebar::OnFirstDisplay()
qry.Post();
for (std::vector<AtlasMessage::sEntitiesListItem>::iterator it = qry.entities.begin(); it != qry.entities.end(); ++it)
{
wxString id = it->id.c_str();
wxString name = it->name.c_str();
m_ObjectListBox->Append(name, new wxStringClientData(name));
m_ObjectListBox->Append(name, new wxStringClientData(id));
}
}

View File

@ -80,7 +80,7 @@ bool WorldCommand::Merge(AtlasWindowCommand* p)
if (! prev)
return false;
if (m_Command->GetType() != prev->m_Command->GetType()) // comparing char* pointers, because they're unique-per-class constants
if (m_Command->GetName() != prev->m_Command->GetName()) // comparing char* pointers, because they're unique-per-class constants
return false;
if (! m_Command->IsMergeable())

View File

@ -14,7 +14,7 @@ class PlaceObject : public StateDrivenTool<PlaceObject>
DECLARE_DYNAMIC_CLASS(PlaceObject);
Position m_ScreenPos, m_ObjPos, m_Target;
wxString m_ObjectName;
wxString m_ObjectID;
public:
PlaceObject()
@ -22,21 +22,24 @@ public:
SetState(&Waiting);
}
void SendPreviewCommand()
void SendObjectMsg(bool preview)
{
int dragDistSq =
(m_ScreenPos.type1.x-m_Target.type1.x)*(m_ScreenPos.type1.x-m_Target.type1.x)
+ (m_ScreenPos.type1.y-m_Target.type1.y)*(m_ScreenPos.type1.y-m_Target.type1.y);
bool useTarget = (dragDistSq >= 8*8);
POST_MESSAGE(EntityPreview(m_ObjectName.c_str(), m_ObjPos, useTarget, m_Target, g_DefaultAngle));
bool useTarget = (dragDistSq >= 16*16);
if (preview)
POST_MESSAGE(EntityPreview(m_ObjectID.c_str(), m_ObjPos, useTarget, m_Target, g_DefaultAngle));
else
POST_COMMAND(CreateEntity, (m_ObjectID.c_str(), m_ObjPos, useTarget, m_Target, g_DefaultAngle));
}
virtual void Init(void* initData)
{
wxASSERT(initData);
wxString& name = *static_cast<wxString*>(initData);
m_ObjectName = name;
SendPreviewCommand();
wxString& id = *static_cast<wxString*>(initData);
m_ObjectID = id;
SendObjectMsg(true);
}
void OnEnable()
@ -45,54 +48,65 @@ public:
void OnDisable()
{
m_ObjectName = _T("");
SendPreviewCommand();
m_ObjectID = _T("");
SendObjectMsg(true);
}
/*
Object placement:
* Select unit from list
* Move mouse around screen; preview of unit follows mouse
* Left mouse down -> remember position, fix preview to point
* Mouse move -> if moved > 8px, rotate unit to face mouse; else default orientation
* Left mouse release -> finalise placement of object on map
Object placement:
* Select unit from list
* Move mouse around screen; preview of unit follows mouse
* Left mouse down -> remember position, fix preview to point
* Mouse move -> if moved > [limit], rotate unit to face mouse; else default orientation
* Left mouse release -> finalise placement of object on map
* Scroll wheel -> rotate default orientation
* Scroll wheel -> rotate default orientation
* Escape -> cancel placement tool
* Escape -> cancel placement tool
TOOD: what happens if somebody saves while the preview is active?
TOOD: what happens if somebody saves while the preview is active?
*/
bool OnMouseOverride(wxMouseEvent& evt)
bool OnMouseOverride(wxMouseEvent& WXUNUSED(evt))
{
if (evt.GetWheelRotation())
// This used to let the scroll-wheel rotate units, but that overrides
// the camera zoom and makes navigation very awkward, so it doesn't
// any more.
return false;
}
bool OnKeyOverride(wxKeyEvent& evt, KeyEventType dir)
{
switch (dir)
{
float speed = M_PI/36.f;
case KEY_CHAR:
if (evt.GetKeyCode() == WXK_ESCAPE)
{
SetState(&Disabled);
return true;
}
break;
}
return false;
}
void RotateTick(float dt)
{
int dir = 0;
if (wxGetKeyState(WXK_NEXT)) ++dir; // page-down key
if (wxGetKeyState(WXK_PRIOR)) --dir; // page-up key
if (dir)
{
float speed = M_PI/2.f; // radians per second
if (wxGetKeyState(WXK_SHIFT) && wxGetKeyState(WXK_CONTROL))
speed /= 64.f;
else if (wxGetKeyState(WXK_CONTROL))
speed /= 4.f;
else if (wxGetKeyState(WXK_SHIFT))
speed *= 4.f;
g_DefaultAngle += (evt.GetWheelRotation() * speed / evt.GetWheelDelta());
SendPreviewCommand();
return true;
g_DefaultAngle += (dir * dt * speed);
SendObjectMsg(true);
}
else
return false;
}
bool OnKeyOverride(wxKeyEvent& evt, KeyEventType dir)
{
if (dir == KEY_CHAR && evt.GetKeyCode() == WXK_ESCAPE)
{
SetState(&Disabled);
return true;
}
// TODO: arrow keys for rotation?
else
return false;
}
@ -105,7 +119,7 @@ public:
else if (evt.LeftDown())
{
obj->m_ObjPos = obj->m_ScreenPos = obj->m_Target = Position(evt.GetPosition());
obj->SendPreviewCommand();
obj->SendObjectMsg(true);
obj->m_ObjPos = Position::Unchanged(); // make sure object is stationary even if the camera moves
SET_STATE(Placing);
return true;
@ -113,7 +127,7 @@ public:
else if (evt.Moving())
{
obj->m_ObjPos = obj->m_ScreenPos = obj->m_Target = Position(evt.GetPosition());
obj->SendPreviewCommand();
obj->SendObjectMsg(true);
return true;
}
else
@ -123,6 +137,10 @@ public:
{
return obj->OnKeyOverride(evt, dir);
}
void OnTick(PlaceObject* obj, float dt)
{
obj->RotateTick(dt);
}
}
Waiting;
@ -135,16 +153,18 @@ public:
else if (evt.LeftUp())
{
obj->m_Target = Position(evt.GetPosition());
// TODO: createobject command, so you can actually place an object
// Create the actual object
obj->SendObjectMsg(false);
// Go back to preview mode
SET_STATE(Waiting);
obj->m_ObjPos = obj->m_ScreenPos = obj->m_Target;
obj->SendPreviewCommand();
obj->SendObjectMsg(true);
return true;
}
else if (evt.Moving())
else if (evt.Dragging())
{
obj->m_Target = Position(evt.GetPosition());
obj->SendPreviewCommand();
obj->SendObjectMsg(true);
return true;
}
else
@ -154,6 +174,10 @@ public:
{
return obj->OnKeyOverride(evt, dir);
}
void OnTick(PlaceObject* obj, float dt)
{
obj->RotateTick(dt);
}
}
Placing;
};

View File

@ -14,8 +14,10 @@
#include "lib/timer.h"
#include "lib/res/file/vfs.h"
#include "ps/CLogger.h"
#include "ps/GameSetup/GameSetup.h" // Render()
#include "ps/GameSetup/GameSetup.h"
#include "ps/Game.h"
#include "simulation/Simulation.h"
#include "simulation/EntityManager.h"
using namespace AtlasMessage;
@ -75,6 +77,7 @@ bool BeginAtlas(int argc, char* argv[], void* dll)
state.argv = argv;
state.running = true;
state.rendering = false;
state.worldloaded = false;
state.glContext = NULL;
double last_activity = get_time();
@ -168,7 +171,15 @@ bool BeginAtlas(int argc, char* argv[], void* dll)
//////////////////////////////////////////////////////////////////////////
// Do per-frame processing:
vfs_reload_changed_files();
if (state.worldloaded)
{
g_EntityManager.updateAll(0);
g_Game->GetSimulation()->Update(0.0);
}
if (state.rendering)
{

View File

@ -9,6 +9,7 @@ struct GameLoopState
char** argv;
bool running;
bool rendering;
bool worldloaded;
const void* glContext;
float frameLength; // smoothed to avoid large jumps

View File

@ -108,6 +108,6 @@ BEGIN_COMMAND(AlterElevation)
prev->m_TerrainDelta.OverlayWith(m_TerrainDelta);
}
END_COMMAND(AlterElevation);
END_COMMAND(AlterElevation)
}

View File

@ -1,6 +1,7 @@
#include "precompiled.h"
#include "MessageHandler.h"
#include "../GameLoop.h"
#include "graphics/Patch.h"
#include "graphics/TextureManager.h"
@ -30,6 +31,8 @@ static void InitGame(std::wstring map)
// Initialise the game:
g_Game = new CGame();
g_GameLoop->worldloaded = true;
}
static void StartGame()

View File

@ -1,12 +1,18 @@
#include "precompiled.h"
#include "MessageHandler.h"
#include "../CommandProc.h"
#include "simulation/BaseEntityCollection.h"
#include "simulation/EntityManager.h"
#include "graphics/Unit.h"
#include "graphics/UnitManager.h"
#include "graphics/Model.h"
#include "maths/Matrix3D.h"
#include "ps/CLogger.h"
#include "ps/Game.h"
#define LOG_CATEGORY "editor"
namespace AtlasMessage {
@ -16,19 +22,28 @@ QUERYHANDLER(GetEntitiesList)
g_EntityTemplateCollection.getBaseEntityNames(names);
for (std::vector<CStrW>::iterator it = names.begin(); it != names.end(); ++it)
{
//CBaseEntity* baseent = g_EntityTemplateCollection.getTemplate(*it);
sEntitiesListItem e;
e.name = *it;
e.id = *it;
e.name = *it; //baseent->m_Tag
msg->entities.push_back(e);
}
}
static CUnit* g_PreviewUnit = NULL;
static CStrW g_PreviewUnitName;
static CStrW g_PreviewUnitID;
static CVector3D GetUnitPos(const Position& pos)
{
static CVector3D vec;
pos.GetWorldSpace(vec, vec); // if msg->pos is 'Unchanged', use the previous pos
return vec;
}
MESSAGEHANDLER(EntityPreview)
{
if (msg->name != g_PreviewUnitName)
if (msg->id != g_PreviewUnitID)
{
// Delete old unit
if (g_PreviewUnit)
@ -38,11 +53,11 @@ MESSAGEHANDLER(EntityPreview)
g_PreviewUnit = NULL;
}
if (msg->name.length())
if (msg->id.length())
{
// Create new unit
CBaseEntity* base = g_EntityTemplateCollection.getTemplate(msg->name);
if (base)
CBaseEntity* base = g_EntityTemplateCollection.getTemplate(msg->id);
if (base) // (ignore errors)
{
g_PreviewUnit = g_UnitMan.CreateUnit(base->m_actorName, 0);
// TODO: set player (for colour)
@ -50,24 +65,25 @@ MESSAGEHANDLER(EntityPreview)
}
}
g_PreviewUnitName = msg->name;
g_PreviewUnitID = msg->id;
}
// Position/orient unit
if (g_PreviewUnit)
{
static CVector3D pos;
msg->pos.GetWorldSpace(pos, pos); // if msg->pos is 'Unchanged', use the previous pos
CVector3D pos = GetUnitPos(msg->pos);
float s, c;
/*
if (msg->usetarget)
{
// TODO
s=1; c=0;
CVector3D target;
msg->target.GetWorldSpace(target, pos.Y);
CVector2D dir(target.X-pos.X, target.Z-pos.Z);
dir = dir.normalize();
s = dir.x;
c = dir.y;
}
else
*/
{
s = sin(msg->angle);
c = cos(msg->angle);
@ -80,8 +96,68 @@ MESSAGEHANDLER(EntityPreview)
m._41 = 0.0f; m._42 = 0.0f; m._43 = 0.0f; m._44 = 1.0f;
g_PreviewUnit->GetModel()->SetTransform(m);
}
}
BEGIN_COMMAND(CreateEntity)
HEntity m_Entity;
CVector3D m_Pos;
float m_Angle;
void Construct()
{
}
void Destruct()
{
}
void Do()
{
m_Pos = GetUnitPos(d->pos);
if (d->usetarget)
{
CVector3D target;
d->target.GetWorldSpace(target, m_Pos.Y);
CVector2D dir(target.X-m_Pos.X, target.Z-m_Pos.Z);
m_Angle = atan2(dir.x, dir.y);
}
else
{
m_Angle = d->angle;
}
Redo();
}
void Redo()
{
CBaseEntity* base = g_EntityTemplateCollection.getTemplate(d->id);
if (! base)
LOG(ERROR, LOG_CATEGORY, "Failed to load entity template '%ls'", d->id.c_str());
else
{
HEntity ent = g_EntityManager.create(base, m_Pos, m_Angle);
if (! ent)
LOG(ERROR, LOG_CATEGORY, "Failed to create entity of type '%ls'", d->id.c_str());
else
{
// TODO: player ID
ent->SetPlayer(g_Game->GetLocalPlayer());
m_Entity = ent;
}
}
}
void Undo()
{
m_Entity->kill();
m_Entity = HEntity();
}
END_COMMAND(CreateEntity)
}

View File

@ -200,7 +200,7 @@ BEGIN_COMMAND(PaintTerrain)
prev->m_TerrainDelta.OverlayWith(m_TerrainDelta);
}
END_COMMAND(PaintTerrain);
END_COMMAND(PaintTerrain)
}

View File

@ -89,6 +89,7 @@ QUERY(GetTerrainGroupPreviews,
struct sEntitiesListItem
{
std::wstring id;
std::wstring name;
// ...
};
@ -98,13 +99,22 @@ QUERY(GetEntitiesList,
);
MESSAGE(EntityPreview,
((std::wstring, name)) // or empty string => disable
((std::wstring, id)) // or empty string => disable
((Position, pos))
((bool, usetarget)) // true => use 'target' for orientation; false => use 'angle'
((Position, target))
((float, angle))
);
COMMAND(CreateEntity, NOMERGE,
((std::wstring, id))
((Position, pos))
((bool, usetarget)) // true => use 'target' for orientation; false => use 'angle'
((Position, target))
((float, angle))
);
//////////////////////////////////////////////////////////////////////////
QUERY(Exit,,); // no inputs nor outputs

View File

@ -37,6 +37,7 @@ struct Position
// Only for use in the game, not the UI.
// Implementations in Misc.cpp.
void GetWorldSpace(CVector3D& vec) const;
void GetWorldSpace(CVector3D& vec, float h) const;
void GetWorldSpace(CVector3D& vec, const CVector3D& prev) const;
void GetScreenSpace(float& x, float& y) const;
};
@ -55,8 +56,8 @@ struct IMessage
#define MESSAGESTRUCT(t) \
struct m##t : public IMessage { \
const char* GetName() const { return #t; } \
Type GetType() const { return IMessage::Message; } \
virtual const char* GetName() const { return #t; } \
virtual Type GetType() const { return IMessage::Message; } \
private: \
const m##t& operator=(const m##t&); \
public:

View File

@ -30,6 +30,19 @@ void AtlasMessage::Position::GetWorldSpace(CVector3D& vec) const
}
}
void AtlasMessage::Position::GetWorldSpace(CVector3D& vec, float h) const
{
switch (type)
{
case 1:
vec = g_Game->GetView()->GetCamera()->GetWorldCoordinates(type1.x, type1.y, h);
break;
default:
GetWorldSpace(vec);
}
}
void AtlasMessage::Position::GetWorldSpace(CVector3D& vec, const CVector3D& prev) const
{
switch (type)
@ -37,6 +50,7 @@ void AtlasMessage::Position::GetWorldSpace(CVector3D& vec, const CVector3D& prev
case 2:
vec = prev;
break;
default:
GetWorldSpace(vec);
}