forked from Mirrors/openclonk
WIP First step in introducing rope links
parent
f10f7a4d48
commit
db68467d91
|
@ -736,8 +736,8 @@ bool C4Game::Execute() // Returns true if the game is over
|
||||||
|
|
||||||
// Game
|
// Game
|
||||||
|
|
||||||
EXEC_S( Ropes.Execute(); , ExecRopesStat )
|
|
||||||
EXEC_S( ExecObjects(); , ExecObjectsStat )
|
EXEC_S( ExecObjects(); , ExecObjectsStat )
|
||||||
|
EXEC_S( Ropes.Execute(); , ExecRopesStat )
|
||||||
if (pGlobalEffects)
|
if (pGlobalEffects)
|
||||||
EXEC_S_DR( pGlobalEffects->Execute(NULL); , GEStats , "GEEx\0");
|
EXEC_S_DR( pGlobalEffects->Execute(NULL); , GEStats , "GEEx\0");
|
||||||
EXEC_S_DR( PXS.Execute(); , PXSStat , "PXSEx")
|
EXEC_S_DR( PXS.Execute(); , PXSStat , "PXSEx")
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
#include <C4Landscape.h>
|
#include <C4Landscape.h>
|
||||||
#include <C4Rope.h>
|
#include <C4Rope.h>
|
||||||
|
|
||||||
//#define C4ROPE_DRAW_DEBUG
|
#define C4ROPE_DRAW_DEBUG
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
@ -147,18 +147,30 @@ namespace
|
||||||
}
|
}
|
||||||
|
|
||||||
C4RopeElement::C4RopeElement(C4Object* obj, bool fixed):
|
C4RopeElement::C4RopeElement(C4Object* obj, bool fixed):
|
||||||
Fixed(fixed), fx(Fix0), fy(Fix0), rx(Fix0), ry(Fix0), rdt(Fix0), fcx(Fix0), fcy(Fix0),
|
Fixed(fixed), oldx(obj->fix_x), oldy(obj->fix_y), fx(Fix0), fy(Fix0),
|
||||||
Next(NULL), Prev(NULL), Object(obj), LastContactVertex(-1)
|
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):
|
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),
|
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)
|
void C4RopeElement::AddForce(C4Real x, C4Real y)
|
||||||
{
|
{
|
||||||
fx += x;
|
fx += x;
|
||||||
|
@ -194,6 +206,116 @@ C4Real C4RopeElement::GetTargetY() const
|
||||||
return obj->fix_y + itofix(obj->Shape.VtxY[LastContactVertex]);
|
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)
|
void C4RopeElement::Execute(const C4Rope* rope, C4Real dt)
|
||||||
{
|
{
|
||||||
ResetForceRedirection(dt);
|
ResetForceRedirection(dt);
|
||||||
|
@ -214,9 +336,9 @@ void C4RopeElement::Execute(const C4Rope* rope, C4Real dt)
|
||||||
int iy = fixtoi(y);
|
int iy = fixtoi(y);
|
||||||
if(GBackSolid(ix+1, iy) || GBackSolid(ix-1, iy) || GBackSolid(ix, iy-1) || GBackSolid(ix, iy+1))
|
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;
|
fx = fy = Fix0;
|
||||||
vx = vy = Fix0;
|
vx = vy = Fix0;
|
||||||
|
@ -248,19 +370,25 @@ void C4RopeElement::Execute(const C4Rope* rope, C4Real dt)
|
||||||
// Execute movement
|
// Execute movement
|
||||||
if(!Target)
|
if(!Target)
|
||||||
{
|
{
|
||||||
|
// Compute old and new coordinates
|
||||||
int old_x = fixtoi(x);
|
int old_x = fixtoi(x);
|
||||||
int old_y = fixtoi(y);
|
int old_y = fixtoi(y);
|
||||||
int new_x = fixtoi(x + dt * vx);
|
int new_x = fixtoi(x + dt * vx);
|
||||||
int new_y = fixtoi(y + dt * vy);
|
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));
|
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_x = old_x;
|
||||||
int prev_y = old_y;
|
int prev_y = old_y;
|
||||||
bool hit = false;
|
bool hit = false;
|
||||||
for(int i = 1; i <= max_p; ++i)
|
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_x = old_x + i * (new_x - old_x) / max_p;
|
||||||
int inter_y = old_y + i * (new_y - old_y) / max_p;
|
int inter_y = old_y + i * (new_y - old_y) / max_p;
|
||||||
|
|
||||||
|
// Collision check
|
||||||
if(GBackSolid(inter_x, inter_y))
|
if(GBackSolid(inter_x, inter_y))
|
||||||
{
|
{
|
||||||
x = itofix(prev_x);
|
x = itofix(prev_x);
|
||||||
|
@ -683,6 +811,28 @@ void C4Rope::Execute()
|
||||||
for(C4RopeElement* cur = Front; cur != NULL; cur = cur->Next)
|
for(C4RopeElement* cur = Front; cur != NULL; cur = cur->Next)
|
||||||
cur->Execute(this, dt);
|
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)
|
void C4Rope::ClearPointers(C4Object* obj)
|
||||||
|
@ -820,6 +970,18 @@ void C4Rope::Draw(C4TargetFacet& cgo, C4BltTransform* pTransform)
|
||||||
glEnd();
|
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 vx = fixtof(cur->GetVx());
|
||||||
const float vy = fixtof(cur->GetVy());
|
const float vy = fixtof(cur->GetVy());
|
||||||
const float v = sqrt(vx*vx + vy*vy);
|
const float v = sqrt(vx*vx + vy*vy);
|
||||||
|
|
|
@ -29,6 +29,16 @@ public:
|
||||||
C4RopeError(const std::string& message): std::runtime_error(message) {}
|
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 C4Rope;
|
||||||
class C4RopeElement
|
class C4RopeElement
|
||||||
{
|
{
|
||||||
|
@ -36,6 +46,7 @@ class C4RopeElement
|
||||||
public:
|
public:
|
||||||
C4RopeElement(C4Object* obj, bool fixed);
|
C4RopeElement(C4Object* obj, bool fixed);
|
||||||
C4RopeElement(C4Real x, C4Real y, C4Real m, bool fixed);
|
C4RopeElement(C4Real x, C4Real y, C4Real m, bool fixed);
|
||||||
|
~C4RopeElement();
|
||||||
|
|
||||||
C4Real GetX() const { return Object ? GetTargetX() : x; }
|
C4Real GetX() const { return Object ? GetTargetX() : x; }
|
||||||
C4Real GetY() const { return Object ? GetTargetY() : y; }
|
C4Real GetY() const { return Object ? GetTargetY() : y; }
|
||||||
|
@ -47,23 +58,34 @@ public:
|
||||||
C4Real GetTargetX() const;
|
C4Real GetTargetX() const;
|
||||||
C4Real GetTargetY() 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 AddForce(C4Real x, C4Real y);
|
||||||
void Execute(const C4Rope* rope, C4Real dt);
|
void Execute(const C4Rope* rope, C4Real dt);
|
||||||
private:
|
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 ResetForceRedirection(C4Real dt);
|
||||||
void SetForceRedirection(const C4Rope* rope, int ox, int oy);
|
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 SetForceRedirectionByLookAround(const C4Rope* rope, int ox, int oy, C4Real dx, C4Real dy, C4Real l, C4Real angle);
|
||||||
|
|
||||||
bool Fixed; // Apply rope forces to this element?
|
bool Fixed; // Apply rope forces to this element?
|
||||||
C4Real x, y; // pos
|
C4Real x, y; // pos; ignored if Object != NULL
|
||||||
C4Real vx, vy; // velocity
|
C4Real oldx, oldy; // position in the previous frame. Used for linking
|
||||||
C4Real m; // mass
|
C4Real vx, vy; // velocity; ignored if Object != NULL
|
||||||
|
C4Real m; // mass; ignored if Object != NULL
|
||||||
C4Real fx, fy; // force
|
C4Real fx, fy; // force
|
||||||
C4Real rx, ry; // force redirection
|
C4Real rx, ry; // force redirection
|
||||||
C4Real rdt; // force redirection timeout
|
C4Real rdt; // force redirection timeout
|
||||||
C4Real fcx, fcy; // force after solve -- for debug output only
|
C4Real fcx, fcy; // force after solve -- for debug output only
|
||||||
C4RopeElement* Next; // next rope element, or NULL
|
C4RopeElement* Next; // next rope element, or NULL
|
||||||
C4RopeElement* Prev; // prev 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.
|
C4Object* Object; // Connected object. If set, x/y/vx/vy/m are ignored.
|
||||||
int LastContactVertex; // Vertex which most recently had collision with landscape
|
int LastContactVertex; // Vertex which most recently had collision with landscape
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue