WIP First step in introducing rope links

rope
Armin Burgmeier 2012-12-30 14:26:47 +01:00
parent f10f7a4d48
commit db68467d91
3 changed files with 195 additions and 11 deletions

View File

@ -736,8 +736,8 @@ bool C4Game::Execute() // Returns true if the game is over
// Game
EXEC_S( Ropes.Execute(); , ExecRopesStat )
EXEC_S( ExecObjects(); , ExecObjectsStat )
EXEC_S( Ropes.Execute(); , ExecRopesStat )
if (pGlobalEffects)
EXEC_S_DR( pGlobalEffects->Execute(NULL); , GEStats , "GEEx\0");
EXEC_S_DR( PXS.Execute(); , PXSStat , "PXSEx")

View File

@ -19,7 +19,7 @@
#include <C4Landscape.h>
#include <C4Rope.h>
//#define C4ROPE_DRAW_DEBUG
#define C4ROPE_DRAW_DEBUG
namespace
{
@ -147,18 +147,30 @@ namespace
}
C4RopeElement::C4RopeElement(C4Object* obj, bool fixed):
Fixed(fixed), fx(Fix0), fy(Fix0), rx(Fix0), ry(Fix0), rdt(Fix0), fcx(Fix0), fcy(Fix0),
Next(NULL), Prev(NULL), Object(obj), LastContactVertex(-1)
Fixed(fixed), oldx(obj->fix_x), oldy(obj->fix_y), fx(Fix0), fy(Fix0),
rx(Fix0), ry(Fix0), rdt(Fix0), fcx(Fix0), fcy(Fix0),
Next(NULL), Prev(NULL), FirstLink(NULL), LastLink(NULL),
Object(obj), LastContactVertex(-1)
{
}
C4RopeElement::C4RopeElement(C4Real x, C4Real y, C4Real m, bool fixed):
Fixed(fixed), x(x), y(y), vx(Fix0), vy(Fix0), m(m),
Fixed(fixed), x(x), y(y), oldx(x), oldy(y), vx(Fix0), vy(Fix0), m(m),
fx(Fix0), fy(Fix0), rx(Fix0), ry(Fix0), rdt(Fix0), fcx(Fix0), fcy(Fix0),
Next(NULL), Prev(NULL), Object(NULL), LastContactVertex(-1)
Next(NULL), Prev(NULL), FirstLink(NULL), LastLink(NULL),
Object(NULL), LastContactVertex(-1)
{
}
C4RopeElement::~C4RopeElement()
{
for(C4RopeLink* link = FirstLink, *next; link != NULL; link = next)
{
next = link->Next;
delete link;
}
}
void C4RopeElement::AddForce(C4Real x, C4Real y)
{
fx += x;
@ -194,6 +206,116 @@ C4Real C4RopeElement::GetTargetY() const
return obj->fix_y + itofix(obj->Shape.VtxY[LastContactVertex]);
}
bool C4RopeElement::InsertLinkPosition(int from_x, int from_y, int to_x, int to_y, int link_x, int link_y, int& insert_x, int& insert_y)
{
if(PathFree(to_x, to_y, link_x, link_y)) return false;
// Sanity check, these might be violated if the landscape changed.
// In that case the rope might end up buried in earth, in which case we
// do not want to do any maneuvering around, but the rope is just stuck.
if(!PathFree(from_x, from_y, link_x, link_y)) return false;
// If the element has been moved through solid material, only take the part
// of the way into account that is free.
int stop_x, stop_y;
if(!PathFree(from_x, from_y, to_x, to_y, &stop_x, &stop_y))
{
to_x = stop_x;
to_y = stop_y;
}
// Find the position between from_x,from_y, and to_x,to_y
// where there is no path free to link_x,link_y anymore.
int prev_x = from_x;
int prev_y = from_y;
int coll_x = -1;
int coll_y = -1;
int max_p = Max(abs(to_x - from_x), abs(to_y - from_y));
for(int i = 1; i <= max_p; ++i)
{
int inter_x = from_x + i * (to_x - from_x) / max_p;
int inter_y = from_y + i * (to_y - from_y) / max_p;
if(!PathFree(inter_x, inter_y, link_x, link_y))
{
coll_x = inter_x;
coll_y = inter_y;
break;
}
prev_x = inter_x;
prev_y = inter_y;
}
// Such a position must have been found, because of the initial
// PathFree(to_x, to_y, link_x, link_y) check.
assert(coll_x != -1 && coll_y != -1);
// Now we have:
// prev_x,prev_y: Last position between from_x,from_y and to_x,to_y
// where the path to link_x,link_y is still free.
// coll_x,coll_y: First position between from_x,from_y and to_x,to_y
// where the path to link_x,link_y is no longer free.
// prev_x,prev_y and coll_x,coll_y are 1 pixel apart from each other.
// Now the idea is to insert a new link somewhere on the line from
// prev_x,prev_y to link_x,link_y such that
// PathFree(coll_x, coll_y, insert_x, insert_y) holds and the point is
// as close to link_x, link_y as possible.
max_p = Max(abs(link_x - prev_x), abs(link_y - prev_y));
for(int i = 1; i <= max_p; ++i)
{
int inter_x = link_x + i * (prev_x - link_x) / max_p;
int inter_y = link_y + i * (prev_y - link_y) / max_p;
// Again a sanity check, to account for pixel rounding mismatches
if(!PathFree(inter_x, inter_y, link_x, link_y)) continue;
if(PathFree(coll_x, coll_y, inter_x, inter_y))
{
insert_x = inter_x;
insert_y = inter_y;
return true;
}
}
// The final point in the loop is prev_x, prev_y, and there should be a
// free path from it, as per the sanity check precondition.
assert(false);
return false;
}
void C4RopeElement::InsertLink(C4RopeElement* from, C4RopeElement* to, int insert_x, int insert_y)
{
assert(this == from || this == to);
C4RopeLink* Link = new C4RopeLink;
Link->x = itofix(insert_x);
Link->y = itofix(insert_y);
if(this == from)
{
assert(from->Next == to);
Link->Next = from->FirstLink;
from->FirstLink = Link;
Link->Prev = NULL;
if(to->LastLink == NULL)
to->LastLink = Link;
}
else
{
assert(to->Prev == from);
Link->Prev = to->LastLink;
to->LastLink = Link;
Link->Next = NULL;
if(from->FirstLink == NULL)
from->FirstLink = Link;
}
}
void C4RopeElement::Execute(const C4Rope* rope, C4Real dt)
{
ResetForceRedirection(dt);
@ -214,9 +336,9 @@ void C4RopeElement::Execute(const C4Rope* rope, C4Real dt)
int iy = fixtoi(y);
if(GBackSolid(ix+1, iy) || GBackSolid(ix-1, iy) || GBackSolid(ix, iy-1) || GBackSolid(ix, iy+1))
{
if(vx*vx + vy*vy < Fix1/4)
if(vx*vx + vy*vy < Fix1/4) // TODO: Threshold should be made a property
{
if(fx*fx + fy*fy < Fix1/4)
if(fx*fx + fy*fy < Fix1/4) // TODO: Threshold should be made a property
{
fx = fy = Fix0;
vx = vy = Fix0;
@ -248,19 +370,25 @@ void C4RopeElement::Execute(const C4Rope* rope, C4Real dt)
// Execute movement
if(!Target)
{
// Compute old and new coordinates
int old_x = fixtoi(x);
int old_y = fixtoi(y);
int new_x = fixtoi(x + dt * vx);
int new_y = fixtoi(y + dt * vy);
// Maximum distance in pixels in either X or Y
int max_p = Max(abs(new_x - old_x), abs(new_y - old_y));
// Check all pixels between old and new position
int prev_x = old_x;
int prev_y = old_y;
bool hit = false;
for(int i = 1; i <= max_p; ++i)
{
// Intermediate pixel position
int inter_x = old_x + i * (new_x - old_x) / max_p;
int inter_y = old_y + i * (new_y - old_y) / max_p;
// Collision check
if(GBackSolid(inter_x, inter_y))
{
x = itofix(prev_x);
@ -683,6 +811,28 @@ void C4Rope::Execute()
for(C4RopeElement* cur = Front; cur != NULL; cur = cur->Next)
cur->Execute(this, dt);
}
// Insert/remove links between rope elements. We rely that object movement
// is executed before rope movement at this point, so this needs to stay in
// sync with C4Game::Execute().
for(C4RopeElement* cur = Front; cur != NULL; cur = cur->Next)
{
int insert_x, insert_y;
if(cur->Prev && cur->InsertLinkPosition(cur->oldx, cur->oldy, cur->GetX(), cur->GetY(), cur->GetPrevLinkX(), cur->GetPrevLinkY(), insert_x, insert_y))
cur->InsertLink(cur->Prev, cur, insert_x, insert_y);
if(cur->Next && cur->InsertLinkPosition(cur->oldx, cur->oldy, cur->GetX(), cur->GetY(), cur->GetNextLinkX(), cur->GetNextLinkY(), insert_x, insert_y))
cur->InsertLink(cur, cur->Next, insert_x, insert_y);
// TODO: removal
}
// Update old coordinates for next iteration. Don't do this in the loop above,
// so that GetPrevLinkX/GetPrevLinkY still return the old values.
for(C4RopeElement* cur = Front; cur != NULL; cur = cur->Next)
{
cur->oldx = cur->GetX();
cur->oldy = cur->GetY();
}
}
void C4Rope::ClearPointers(C4Object* obj)
@ -820,6 +970,18 @@ void C4Rope::Draw(C4TargetFacet& cgo, C4BltTransform* pTransform)
glEnd();
}
// Draw links
for(C4RopeLink* link = cur->FirstLink; link != NULL; link = link->Next)
{
glBegin(GL_QUADS);
glColor3f(1.0f, 1.0f, 1.0f);
glVertex2f(fixtof(cur->x)-1.0f, fixtof(cur->y)-1.0f);
glVertex2f(fixtof(cur->x)-1.0f, fixtof(cur->y)+1.0f);
glVertex2f(fixtof(cur->x)+1.0f, fixtof(cur->y)+1.0f);
glVertex2f(fixtof(cur->x)+1.0f, fixtof(cur->y)-1.0f);
glEnd();
}
const float vx = fixtof(cur->GetVx());
const float vy = fixtof(cur->GetVy());
const float v = sqrt(vx*vx + vy*vy);

View File

@ -29,6 +29,16 @@ public:
C4RopeError(const std::string& message): std::runtime_error(message) {}
};
// C4RopeLinks are intermediate rope elements that are inserted and removed
// such that
class C4RopeLink
{
public:
C4Real x, y; // pos
C4RopeLink* Next;
C4RopeLink* Prev;
};
class C4Rope;
class C4RopeElement
{
@ -36,6 +46,7 @@ class C4RopeElement
public:
C4RopeElement(C4Object* obj, bool fixed);
C4RopeElement(C4Real x, C4Real y, C4Real m, bool fixed);
~C4RopeElement();
C4Real GetX() const { return Object ? GetTargetX() : x; }
C4Real GetY() const { return Object ? GetTargetY() : y; }
@ -47,23 +58,34 @@ public:
C4Real GetTargetX() const;
C4Real GetTargetY() const;
C4Real GetPrevLinkX() const { assert(Prev); return LastLink ? LastLink->x : Prev->oldx; }
C4Real GetPrevLinkY() const { assert(Prev); return LastLink ? LastLink->y : Prev->oldy; }
C4Real GetNextLinkX() const { assert(Next); return FirstLink ? FirstLink->x : Next->oldx; }
C4Real GetNextLinkY() const { assert(Next); return FirstLink ? FirstLink->y : Next->oldy; }
void AddForce(C4Real x, C4Real y);
void Execute(const C4Rope* rope, C4Real dt);
private:
bool InsertLinkPosition(int from_x, int from_y, int to_x, int to_y, int link_x, int link_y, int& insert_x, int& insert_y);
void InsertLink(C4RopeElement* from, C4RopeElement* to, int insert_x, int insert_y);
void ResetForceRedirection(C4Real dt);
void SetForceRedirection(const C4Rope* rope, int ox, int oy);
bool SetForceRedirectionByLookAround(const C4Rope* rope, int ox, int oy, C4Real dx, C4Real dy, C4Real l, C4Real angle);
bool Fixed; // Apply rope forces to this element?
C4Real x, y; // pos
C4Real vx, vy; // velocity
C4Real m; // mass
C4Real x, y; // pos; ignored if Object != NULL
C4Real oldx, oldy; // position in the previous frame. Used for linking
C4Real vx, vy; // velocity; ignored if Object != NULL
C4Real m; // mass; ignored if Object != NULL
C4Real fx, fy; // force
C4Real rx, ry; // force redirection
C4Real rdt; // force redirection timeout
C4Real fcx, fcy; // force after solve -- for debug output only
C4RopeElement* Next; // next rope element, or NULL
C4RopeElement* Prev; // prev rope element, or NULL
C4RopeLink* FirstLink; // first rope link between this and next, or NULL
C4RopeLink* LastLink; // last rope link between this and prev, or NULL
C4Object* Object; // Connected object. If set, x/y/vx/vy/m are ignored.
int LastContactVertex; // Vertex which most recently had collision with landscape
};