Rope: Clean up and simplify the code a bit

rope
Armin Burgmeier 2012-05-01 16:19:09 +02:00
parent 889be3d2e4
commit 4306ba7648
3 changed files with 249 additions and 337 deletions

View File

@ -19,6 +19,11 @@
#include <C4Landscape.h>
#include <C4Rope.h>
// TODO: For all square roots, we must avoid floating point by using an
// integer-based Sqrt algorithm
// TODO: Could also use an approximation which works without Sqrt, especially
// if this becomes a performance bottleneck. http://www.azillionmonkeys.com/qed/sqroot.html
namespace
{
struct Vertex {
@ -42,10 +47,6 @@ namespace
return ftofix(sqrt(fixtof(dx*dx + dy*dy))); // TODO: Replace by integer sqrt
}
// To be used by the Solve() template function:
C4Object* GetObject(C4RopeSegment* segment) { return NULL; }
C4Object* GetObject(C4RopeEnd* end) { return end->GetObject(); }
// Helper function for Draw: determines vertex positions for one segment
void VertexPos(Vertex& out1, Vertex& out2, Vertex& out3, Vertex& out4,
const Vertex& v1, const Vertex& v2, float w)
@ -90,230 +91,181 @@ namespace
return true;
}
// Computes force redirection for a segment at x/y with velocity vx/vy. This
// is used to get around obstacles on landscapes.
bool ForceRedirection(C4Real x, C4Real y, C4Real vx, C4Real vy, C4Real& rx, C4Real& ry)
{
// The procedure is the following:
// it seems like our way is blocked by something, so we try manuevering around
// it, either left or right. Which way we take is determined by checking whether
// or not there is solid material in a 75 degree angle relative to the direction
// of movement.
// TODO: A much better way might be to use the pathfinder to find the way to one of
// the adjacent segments (whichever is further away <-> stronger force in that direction)
// and set force redirection into the direction of the path toward it.
const C4Real Cos75 = Cos(itofix(75));
const C4Real Sin75 = Sin(itofix(75));
C4Real vx1 = Cos75 * vx + Sin75 * vy;
C4Real vy1 = -Sin75 * vx + Cos75 * vy;
C4Real vx2 = Cos75 * vx - Sin75 * vy;
C4Real vy2 = Sin75 * vx + Cos75 * vy;
const C4Real v = ftofix(sqrt(fixtof(vx*vx + vy*vy)));
// TODO: This should maybe not be hardcoded but depend on the magnitude of the force applied,
// the segment length, the actual distance to the adjacent segments, or a combination of them.
const C4Real l = itofix(5);
// TODO: We should check more than a single pixel. There's some more potential for optimization here.
if(v != Fix0 && !GBackSolid(fixtoi(x + vx1*l/v), fixtoi(y + vy1*l/v)))
{ rx = vx1/v; ry = vy1/v; }
else if(v != Fix0 && !GBackSolid(fixtoi(x + vx2/v), fixtoi(y + vy2/v)))
{ rx = vx2/v; ry = vy2/v; }
else
return false;
return true;
}
}
C4RopeSegment::C4RopeSegment(C4Real x, C4Real y, C4Real m):
x(x), y(y), vx(Fix0), vy(Fix0), fx(Fix0), fy(Fix0),
rx(Fix0), ry(Fix0), rdt(Fix0), //rrx(Fix0), rry(Fix0),
m(m), next(NULL), prev(NULL)
C4RopeElement::C4RopeElement(C4Object* obj, bool fixed):
Fixed(fixed), fx(Fix0), fy(Fix0), rx(Fix0), ry(Fix0), rdt(Fix0),
Next(NULL), Prev(NULL), Object(obj)
{
}
void C4RopeSegment::AddForce(C4Real x, C4Real y)
C4RopeElement::C4RopeElement(C4Real x, C4Real y, C4Real m, bool fixed):
Fixed(fixed), x(x), y(y), vx(Fix0), vy(Fix0), m(m),
fx(Fix0), fy(Fix0), rx(Fix0), ry(Fix0), rdt(Fix0),
Next(NULL), Prev(NULL), Object(NULL)
{
}
void C4RopeElement::AddForce(C4Real x, C4Real y)
{
fx += x;
fy += y;
}
void C4RopeSegment::Execute(C4Real dt, C4Real mu)
void C4RopeElement::Execute(const C4Rope* rope, C4Real dt)
{
// Reset force redirection
if(rdt != Fix0)
{
if(dt > rdt)
{
rx = ry = Fix0;
rdt = Fix0;
}
else
rdt -= dt;
}
ResetForceRedirection(dt);
int old_x = fixtoi(x);
int old_y = fixtoi(y);
// If attached object is contained, apply force to container
C4Object* Target = Object;
if(Target)
while(Target->Contained)
Target = Target->Contained;
// Sticking friction: If an object has contact with the landscape and it
// is at rest then one needs to exceed a certain threshold force until it
// starts moving.
if(GBackSolid(old_x+1, old_y) || GBackSolid(old_x-1, old_y) || GBackSolid(old_x, old_y-1) || GBackSolid(old_x, old_y+1))
// Apply stricking friction
if(!Target)
{
if(vx*vx + vy*vy < Fix1/4)
// Sticking friction: If a segment has contact with the landscape and it
// is at rest then one needs to exceed a certain threshold force until it
// starts moving.
int ix = fixtoi(x);
int iy = fixtoi(y);
if(GBackSolid(ix+1, iy) || GBackSolid(ix-1, iy) || GBackSolid(ix, iy-1) || GBackSolid(ix, iy+1))
{
if(fx*fx + fy*fy < Fix1/4)
if(vx*vx + vy*vy < Fix1/4)
{
fx = fy = Fix0;
vx = vy = Fix0;
return;
}
}
}
vx += dt * fx / m;
vy += dt * fy / m;
fx = fy = Fix0;
int new_x = fixtoi(x + dt * vx);
int new_y = fixtoi(y + dt * vy);
int max_p = Max(abs(new_x - old_x), abs(new_y - old_y));
int prev_x = old_x;
int prev_y = old_y;
bool hit = false;
for(int i = 1; i <= max_p; ++i)
{
int inter_x = old_x + i * (new_x - old_x) / max_p;
int inter_y = old_y + i * (new_y - old_y) / max_p;
if(GBackSolid(inter_x, inter_y))
{
x = itofix(prev_x);
y = itofix(prev_y);
hit = true;
// Apply friction force
fx -= mu * vx; fy -= mu * vy;
// Force redirection so that not every single pixel on a
// chunky landscape is an obstacle for the rope.
if(ForceRedirection(x, y, vx, vy, rx, ry))
rdt = Fix1/4; // Enable force redirection for 1/4th of a frame
/* else
{ vx = vy = Fix0; }*/
break;
}
prev_x = inter_x;
prev_y = inter_y;
}
if(!hit)
{
x += dt * vx;
y += dt * vy;
}
}
C4RopeEnd::C4RopeEnd(C4RopeSegment* segment, C4Object* obj, bool fixed):
segment(segment), has_object(true), fixed(fixed), fx(Fix0), fy(Fix0)
{
this->obj = obj;
}
C4RopeEnd::C4RopeEnd(C4RopeSegment* segment, C4Real x, C4Real y, C4Real m, bool fixed):
segment(segment), has_object(false), fixed(fixed), fx(Fix0), fy(Fix0),
rx(Fix0), ry(Fix0), rdt(Fix0) //rrx(Fix0), rry(Fix0)
{
mass = new Mass;
mass->x = x;
mass->y = y;
mass->vx = Fix0;
mass->vy = Fix0;
mass->m = m;
}
C4RopeEnd::~C4RopeEnd()
{
if(!has_object)
delete mass;
}
void C4RopeEnd::AddForce(C4Real x, C4Real y)
{
fx += x;
fy += y;
}
void C4RopeEnd::Execute(C4Real dt)
{
// Reset force redirection
if(rdt != Fix0)
{
if(dt > rdt)
{
rx = ry = Fix0;
rdt = Fix0;
}
else
rdt -= dt;
}
if(!fixed)
{
if(has_object)
{
// If attached object is contained, apply force to container
C4Object* target = obj;
while(target->Contained) target = target->Contained;
// StaticBacks don't move, so don't apply xdir/ydir to them
// or the rope will behave weird
if( (target->Category & C4D_StaticBack) == 0)
{
target->xdir += dt * fx / obj->Mass;
target->ydir += dt * fy / obj->Mass;
// TODO: Remove contact attachment if there is much force in opposite direction
// Force redirection
if(target->xdir*target->xdir + target->ydir*target->ydir >= Fix1)
if(fx*fx + fy*fy < Fix1/4)
{
// Check if the object has contact to the landscape
// TODO: Check if contact is in direction of movement
long iResult = 0;
for (int i = 0; i < target->Shape.VtxNum; ++i)
iResult |= target->Shape.GetVertexContact(i, CNAT_Left | CNAT_Right | CNAT_Top | CNAT_Bottom, target->GetX(), target->GetY());
if(iResult)
if(ForceRedirection(target->fix_x, target->fix_y, target->xdir, target->ydir, rx, ry))
rdt = Fix1/4; // Enable force redirection for 1/4th of a frame
fx = fy = Fix0;
vx = vy = Fix0;
return;
}
}
}
else
{
// TODO: Share code for landscape collision with C4RopeSegment
mass->vx += dt * fx / mass->m;
mass->vy += dt * fy / mass->m;
mass->x += dt * mass->vx;
mass->y += dt * mass->vy;
}
}
// Apply forces
if(!Fixed)
{
if(!Target)
{
vx += dt * fx / m;
vy += dt * fy / m;
}
else
{
Target->xdir += dt * fx / Target->Mass;
Target->ydir += dt * fy / Target->Mass;
}
}
fx = fy = Fix0;
// Execute movement
if(!Target)
{
int old_x = fixtoi(x);
int old_y = fixtoi(y);
int new_x = fixtoi(x + dt * vx);
int new_y = fixtoi(y + dt * vy);
int max_p = Max(abs(new_x - old_x), abs(new_y - old_y));
int prev_x = old_x;
int prev_y = old_y;
bool hit = false;
for(int i = 1; i <= max_p; ++i)
{
int inter_x = old_x + i * (new_x - old_x) / max_p;
int inter_y = old_y + i * (new_y - old_y) / max_p;
if(GBackSolid(inter_x, inter_y))
{
x = itofix(prev_x);
y = itofix(prev_y);
hit = true;
// Apply friction force
fx -= rope->GetOuterFriction() * vx; fy -= rope->GetOuterFriction() * vy;
// Force redirection so that not every single pixel on a
// chunky landscape is an obstacle for the rope.
SetForceRedirection(rope);
break;
}
prev_x = inter_x;
prev_y = inter_y;
}
if(!hit)
{
x += dt * vx;
y += dt * vy;
}
}
else
{
if(!Fixed)
{
// Object Force redirection
if(Target->xdir*Target->xdir + Target->ydir*Target->ydir >= Fix1)
{
// Check if the object has contact to the landscape
long iResult = 0;
const DWORD dwCNATCheck = CNAT_Left | CNAT_Right | CNAT_Top | CNAT_Bottom;
for (int i = 0; i < Target->Shape.VtxNum; ++i)
iResult |= Target->Shape.GetVertexContact(i, dwCNATCheck, Target->GetX(), Target->GetY());
// TODO: Check force redirection at the vertex which has contact
if(iResult)
SetForceRedirection(rope);
}
}
}
}
void C4RopeElement::ResetForceRedirection(C4Real dt)
{
// countdown+reset force redirection
if(rdt != Fix0)
{
if(dt > rdt)
{
rx = ry = Fix0;
rdt = Fix0;
}
else
{
rdt -= dt;
}
}
}
void C4RopeElement::SetForceRedirection(const C4Rope* rope)
{
// The procedure is the following: we try manuevering around
// the obstacle, either left or right. Which way we take is determined by
// checking whether or not there is solid material in a 75 degree angle
// relative to the direction of movement.
const C4Real Cos75 = Cos(itofix(75));
const C4Real Sin75 = Sin(itofix(75));
C4Real vx1 = Cos75 * GetVx() + Sin75 * GetVy();
C4Real vy1 = -Sin75 * GetVx() + Cos75 * GetVy();
C4Real vx2 = Cos75 * GetVx() - Sin75 * GetVy();
C4Real vy2 = Sin75 * GetVx() + Cos75 * GetVy();
const C4Real v = ftofix(sqrt(fixtof(GetVx()*GetVx() + GetVy()*GetVy())));
const C4Real l = rope->GetSegmentLength();
// TODO: We should check more than a single pixel. There's some more potential for optimization here.
if(v != Fix0 && !GBackSolid(fixtoi(GetX() + vx1*l/v), fixtoi(GetY() + vy1*l/v)))
{ rx = vx1/v; ry = vy1/v; rdt = Fix1/4; } // Enable force redirection for 1/4th of a frame
else if(v != Fix0 && !GBackSolid(fixtoi(GetX() + vx2/v), fixtoi(GetY() + vy2/v)))
{ rx = vx2/v; ry = vy2/v; rdt = Fix1/4; } // Enable force redirection for 1/4th of a frame
}
C4Rope::C4Rope(C4PropList* Prototype, C4Object* first_obj, C4Object* second_obj, int32_t n_segments, C4DefGraphics* graphics):
C4PropListNumbered(Prototype), w(5.0f), Graphics(graphics), n_segments(n_segments),
l(ObjectDistance(first_obj, second_obj) / (n_segments + 1)), k(Fix1*3), mu(Fix1*3), eta(Fix1*3), n_iterations(20)
C4PropListNumbered(Prototype), Width(5.0f), Graphics(graphics), SegmentCount(n_segments),
l(ObjectDistance(first_obj, second_obj) / (n_segments + 1)), k(Fix1*3), mu(Fix1*3), eta(Fix1*3), NumIterations(20)
{
if(!PathFree(first_obj->GetX(), first_obj->GetY(), second_obj->GetX(), second_obj->GetY()))
throw C4RopeError("Path between objects is blocked");
@ -322,47 +274,45 @@ C4Rope::C4Rope(C4PropList* Prototype, C4Object* first_obj, C4Object* second_obj,
if(Graphics->Type != C4DefGraphics::TYPE_Bitmap)
throw C4RopeError("Can only use bitmap as rope graphics");
// TODO: Have this as an array, not as a linked list -- it's ~static after all!
const C4Real m(Fix1);
C4RopeSegment* first_seg = NULL;
C4RopeSegment* prev_seg = NULL;
Front = new C4RopeElement(first_obj, true);
Back = new C4RopeElement(second_obj, false);
const C4Real m(Fix1); // TODO: This should be a property
C4RopeElement* prev_seg = Front;
for(int32_t i = 0; i < n_segments; ++i)
{
C4RopeSegment* seg = new C4RopeSegment(first_obj->fix_x + (second_obj->fix_x - first_obj->fix_x) * (i+1) / (n_segments+1),
first_obj->fix_y + (second_obj->fix_y - first_obj->fix_y) * (i+1) / (n_segments+1),
/*l * ,*/ m);
seg->prev = prev_seg;
if(!prev_seg) first_seg = seg;
else prev_seg->next = seg;
// Create new element
C4Real seg_x = first_obj->fix_x + (second_obj->fix_x - first_obj->fix_x) * (i+1) / (n_segments+1);
C4Real seg_y = first_obj->fix_y + (second_obj->fix_y - first_obj->fix_y) * (i+1) / (n_segments+1);
C4RopeElement* seg = new C4RopeElement(seg_x, seg_y, m, false);
// Link it
seg->Prev = prev_seg;
prev_seg->Next = seg;
prev_seg = seg;
}
front = new C4RopeEnd(first_seg, first_obj, true);
back = new C4RopeEnd(prev_seg, second_obj, false);
// Link back segment
prev_seg->Next = Back;
Back->Prev = prev_seg;
}
C4Rope::~C4Rope()
{
for(C4RopeSegment* cur = front->segment, *next; cur != NULL; cur = next)
for(C4RopeElement* cur = Front, *next; cur != NULL; cur = next)
{
next = cur->next;
next = cur->Next;
delete cur;
}
delete front;
delete back;
}
template<typename TRopeType1, typename TRopeType2>
void C4Rope::Solve(TRopeType1* prev, TRopeType2* next) //C4RopeSegment* prev, C4RopeSegment* next)
void C4Rope::Solve(C4RopeElement* prev, C4RopeElement* next)
{
// Rope forces
C4Real dx = prev->GetX() - next->GetX();
C4Real dy = prev->GetY() - next->GetY();
// TODO: Avoid floating point here by using an integer-based Sqrt algorithm
// TODO: Could also use an approximation which works without Sqrt, especially
// if this becomes a performance bottleneck. http://www.azillionmonkeys.com/qed/sqroot.html
C4Real d = ftofix(sqrt(fixtof(dx*dx + dy*dy)));
if(d != 0 && (ApplyRepulsive || d > l))
{
@ -370,7 +320,7 @@ void C4Rope::Solve(TRopeType1* prev, TRopeType2* next) //C4RopeSegment* prev, C4
C4Real fy = (dy / d) * k * (d - l);
// If force redirection is active, move into direction of redirected force
// instaed. This is used to pull around corners in the landscape.
// instead. This is used to pull around corners in the landscape.
if(prev->rdt == Fix0)
prev->AddForce(-fx, -fy);
@ -395,31 +345,24 @@ void C4Rope::Solve(TRopeType1* prev, TRopeType2* next) //C4RopeSegment* prev, C4
// Could add air/water friction here
// TODO: Apply gravity separately to every segment exactly once
// TODO: Apply gravity separately. This is applied twice now for
// non-end rope segments!
// Don't apply gravity to objects since it's applied already in C4Object execution.
prev->AddForce(Fix0, (::GetObject(prev) ? Fix0 : prev->GetMass() * ::Landscape.Gravity/5));
next->AddForce(Fix0, (::GetObject(next) ? Fix0 : next->GetMass() * ::Landscape.Gravity/5));
prev->AddForce(Fix0, (prev->GetObject() ? Fix0 : prev->GetMass() * ::Landscape.Gravity/5));
next->AddForce(Fix0, (prev->GetObject() ? Fix0 : next->GetMass() * ::Landscape.Gravity/5));
}
void C4Rope::Execute()
{
C4Real dt = itofix(1, n_iterations);
for(unsigned int i = 0; i < n_iterations; ++i)
C4Real dt = itofix(1, NumIterations);
for(unsigned int i = 0; i < NumIterations; ++i)
{
Solve(front, front->segment);
for(C4RopeSegment* cur = front->segment; cur != NULL; cur = cur->next)
{
if(cur->next)
Solve(cur, cur->next);
else
Solve(cur, back);
}
for(C4RopeElement* cur = Front; cur->Next != NULL; cur = cur->Next)
Solve(cur, cur->Next);
for(C4RopeSegment* cur = front->segment; cur != NULL; cur = cur->next)
cur->Execute(dt, mu);
front->Execute(dt);
back->Execute(dt);
for(C4RopeElement* cur = Front; cur != NULL; cur = cur->Next)
cur->Execute(this, dt);
}
}
@ -428,12 +371,12 @@ void C4Rope::Draw(C4TargetFacet& cgo, C4BltTransform* pTransform)
{
#if 1
Vertex Tmp[4];
DrawVertex* Vertices = new DrawVertex[n_segments*2+4]; // TODO: Use a vbo and map it into memory instead?
DrawVertex* Vertices = new DrawVertex[SegmentCount*2+4]; // TODO: Use a vbo and map it into memory instead?
const float rsl = fixtof(l)/5.0 * Graphics->GetBitmap()->Wdt / Graphics->GetBitmap()->Hgt; // rope segment length mapped to Gfx bitmap
VertexPos(Vertices[0], Vertices[1], Tmp[0], Tmp[1],
Vertex(fixtof(front->GetX()), fixtof(front->GetY())),
Vertex(fixtof(front->segment->GetX()), fixtof(front->segment->GetY())), w);
Vertex(fixtof(Front->GetX()), fixtof(Front->GetY())),
Vertex(fixtof(Front->Next->GetX()), fixtof(Front->Next->GetY())), Width);
Vertices[0].u = 0.0f;
Vertices[0].v = 0.0f;
@ -442,14 +385,14 @@ void C4Rope::Draw(C4TargetFacet& cgo, C4BltTransform* pTransform)
unsigned int i = 2;
bool parity = true;
for(C4RopeSegment* cur = front->segment; cur != NULL; cur = cur->next, i += 2)
for(C4RopeElement* cur = Front->Next; cur->Next != NULL; cur = cur->Next, i += 2)
{
Vertex v1(fixtof(cur->GetX()),
fixtof(cur->GetY()));
Vertex v2(fixtof(cur->next ? cur->next->GetX() : back->GetX()),
fixtof(cur->next ? cur->next->GetY() : back->GetY()));
Vertex v3(fixtof(cur->prev ? cur->prev->GetX() : front->GetX()),
fixtof(cur->prev ? cur->prev->GetY() : front->GetY()));
Vertex v2(fixtof(cur->Next ? cur->Next->GetX() : Back->GetX()),
fixtof(cur->Next ? cur->Next->GetY() : Back->GetY()));
Vertex v3(fixtof(cur->Prev ? cur->Prev->GetX() : Front->GetX()),
fixtof(cur->Prev ? cur->Prev->GetY() : Front->GetY()));
// Parity -- parity swaps for each pointed angle (<90 deg)
float cx = v1.x - v3.x;
@ -463,9 +406,9 @@ void C4Rope::Draw(C4TargetFacet& cgo, C4BltTransform* pTransform)
// Obtain vertex positions
if(parity)
VertexPos(Tmp[2], Tmp[3], Vertices[i+2], Vertices[i+3], v1, v2, w);
VertexPos(Tmp[2], Tmp[3], Vertices[i+2], Vertices[i+3], v1, v2, Width);
else
VertexPos(Tmp[3], Tmp[2], Vertices[i+3], Vertices[i+2], v1, v2, w);
VertexPos(Tmp[3], Tmp[2], Vertices[i+3], Vertices[i+2], v1, v2, Width);
Tmp[2].x = (Tmp[0].x + Tmp[2].x)/2.0f;
Tmp[2].y = (Tmp[0].y + Tmp[2].y)/2.0f;
@ -478,10 +421,10 @@ void C4Rope::Draw(C4TargetFacet& cgo, C4BltTransform* pTransform)
float dx2 = Vertices[i-1].x - Vertices[i-2].x;
float dy2 = Vertices[i-1].y - Vertices[i-2].y;
const float d = (dx2*dx2+dy2*dy2)/(dx*dx2+dy*dy2);
Vertices[i ].x = ( (Tmp[2].x + Tmp[3].x)/2.0f) - BoundBy((Tmp[3].x - Tmp[2].x)*d, -w, w)/2.0f;
Vertices[i ].y = ( (Tmp[2].y + Tmp[3].y)/2.0f) - BoundBy((Tmp[3].y - Tmp[2].y)*d, -w, w)/2.0f;
Vertices[i+1].x = ( (Tmp[2].x + Tmp[3].x)/2.0f) + BoundBy((Tmp[3].x - Tmp[2].x)*d, -w, w)/2.0f;
Vertices[i+1].y = ( (Tmp[2].y + Tmp[3].y)/2.0f) + BoundBy((Tmp[3].y - Tmp[2].y)*d, -w, w)/2.0f;
Vertices[i ].x = ( (Tmp[2].x + Tmp[3].x)/2.0f) - BoundBy((Tmp[3].x - Tmp[2].x)*d, -Width, Width)/2.0f;
Vertices[i ].y = ( (Tmp[2].y + Tmp[3].y)/2.0f) - BoundBy((Tmp[3].y - Tmp[2].y)*d, -Width, Width)/2.0f;
Vertices[i+1].x = ( (Tmp[2].x + Tmp[3].x)/2.0f) + BoundBy((Tmp[3].x - Tmp[2].x)*d, -Width, Width)/2.0f;
Vertices[i+1].y = ( (Tmp[2].y + Tmp[3].y)/2.0f) + BoundBy((Tmp[3].y - Tmp[2].y)*d, -Width, Width)/2.0f;
Vertices[i].u = 0.0f; //parity ? 0.0f : 1.0f;
Vertices[i].v = i/2 * rsl;
@ -511,7 +454,7 @@ void C4Rope::Draw(C4TargetFacet& cgo, C4BltTransform* pTransform)
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
glDrawArrays(GL_QUAD_STRIP, 0, n_segments*2+4);
glDrawArrays(GL_QUAD_STRIP, 0, SegmentCount*2+4);
glDisable(GL_TEXTURE_2D);
//glDisable(GL_BLEND);
@ -521,26 +464,29 @@ void C4Rope::Draw(C4TargetFacet& cgo, C4BltTransform* pTransform)
#else
// Debug:
for(C4RopeSegment* cur = front->segment; cur != NULL; cur = cur->next)
for(C4RopeElement* cur = Front; cur != NULL; cur = cur->Next)
{
glBegin(GL_QUADS);
glColor3f(1.0f, 0.0f, 0.0f);
glVertex2f(fixtof(cur->x)-1.5f, fixtof(cur->y)-1.5f);
glVertex2f(fixtof(cur->x)-1.5f, fixtof(cur->y)+1.5f);
glVertex2f(fixtof(cur->x)+1.5f, fixtof(cur->y)+1.5f);
glVertex2f(fixtof(cur->x)+1.5f, fixtof(cur->y)-1.5f);
glEnd();
if(!cur->GetObject())
{
glBegin(GL_QUADS);
glColor3f(1.0f, 0.0f, 0.0f);
glVertex2f(fixtof(cur->x)-1.5f, fixtof(cur->y)-1.5f);
glVertex2f(fixtof(cur->x)-1.5f, fixtof(cur->y)+1.5f);
glVertex2f(fixtof(cur->x)+1.5f, fixtof(cur->y)+1.5f);
glVertex2f(fixtof(cur->x)+1.5f, fixtof(cur->y)-1.5f);
glEnd();
}
const float vx = fixtof(cur->vx);
const float vy = fixtof(cur->vy);
const float vx = fixtof(cur->GetVx());
const float vy = fixtof(cur->GetVy());
const float v = sqrt(vx*vx + vy*vy);
if(v > 0.1)
{
glBegin(GL_TRIANGLES);
glColor3f(BoundBy(v/2.5f, 0.0f, 1.0f), 0.0f, 1.0f);
glVertex2f(fixtof(cur->x) + vx/v*4.0f, fixtof(cur->y) + vy/v*4.0f);
glVertex2f(fixtof(cur->x) - vy/v*1.5f, fixtof(cur->y) + vx/v*1.5f);
glVertex2f(fixtof(cur->x) + vy/v*1.5f, fixtof(cur->y) - vx/v*1.5f);
glVertex2f(fixtof(cur->GetX()) + vx/v*4.0f, fixtof(cur->GetY()) + vy/v*4.0f);
glVertex2f(fixtof(cur->GetX()) - vy/v*1.5f, fixtof(cur->GetY()) + vx/v*1.5f);
glVertex2f(fixtof(cur->GetX()) + vy/v*1.5f, fixtof(cur->GetY()) - vx/v*1.5f);
glEnd();
}

View File

@ -29,70 +29,37 @@ public:
C4RopeError(const std::string& message): std::runtime_error(message) {}
};
class C4RopeSegment
class C4Rope;
class C4RopeElement
{
friend class C4Rope;
public:
C4RopeSegment(C4Real x, C4Real y, C4Real m);
C4RopeElement(C4Object* obj, bool fixed);
C4RopeElement(C4Real x, C4Real y, C4Real m, bool fixed);
C4Real GetX() const { return x; }
C4Real GetY() const { return y; }
C4Real GetVx() const { return vx; }
C4Real GetVy() const { return vy; }
C4Real GetMass() const { return m; }
C4Real GetX() const { return Object ? Object->fix_x : x; }
C4Real GetY() const { return Object ? Object->fix_y : y; }
C4Real GetVx() const { return Object ? Object->xdir : vx; }
C4Real GetVy() const { return Object ? Object->ydir : vy; }
C4Real GetMass() const { return Object ? itofix(Object->Mass) : m; }
C4Object* GetObject() const { return Object; }
void AddForce(C4Real x, C4Real y);
void Execute(C4Real dt, C4Real mu);
void Execute(const C4Rope* rope, C4Real dt);
private:
void ResetForceRedirection(C4Real dt);
void SetForceRedirection(const C4Rope* rope);
bool Fixed; // Apply rope forces to this element?
C4Real x, y; // pos
C4Real vx, vy; // velocity
C4Real m; // mass
C4Real fx, fy; // force
C4Real rx, ry; // force redirection
C4Real rdt; //rx, rry; // force redirection reference
C4Real m; // mass
C4RopeSegment* next;
C4RopeSegment* prev;
};
class C4RopeEnd
{
friend class C4Rope;
public:
C4RopeEnd(C4RopeSegment* segment, C4Object* obj, bool fixed);
C4RopeEnd(C4RopeSegment* segment, C4Real x, C4Real y, C4Real m, bool fixed);
~C4RopeEnd();
C4Real GetX() const { return has_object ? obj->fix_x : mass->x; }
C4Real GetY() const { return has_object ? obj->fix_y : mass->y; }
C4Real GetVx() const { return has_object ? obj->xdir : mass->vx; }
C4Real GetVy() const { return has_object ? obj->ydir : mass->vy; }
C4Real GetMass() const { return has_object ? itofix(obj->Mass) : mass->m; }
C4Object* GetObject() const { return has_object ? obj : NULL; }
void AddForce(C4Real fx, C4Real fy);
void Execute(C4Real dt);
private:
C4RopeSegment* segment;
bool has_object;
bool fixed;
C4Real fx, fy;
C4Real rx, ry;
C4Real rdt;//rrx, rry;
struct Mass {
C4Real x, y; // pos
C4Real vx, vy; // velocity
C4Real m; // mass
};
union {
Mass* mass;
C4Object* obj;
};
C4Real rdt; // force redirection timeout
C4RopeElement* Next; // next rope element, or NULL
C4RopeElement* Prev; // prev rope element, or NULL
C4Object* Object; // Connected object. If set, x/y/vx/vy/m are ignored.
};
class C4Rope: public C4PropListNumbered
@ -102,23 +69,24 @@ public:
~C4Rope();
void Draw(C4TargetFacet& cgo, C4BltTransform* pTransform);
void Execute();
C4Object* GetFront() const { return front->GetObject(); }
C4Object* GetBack() const { return back->GetObject(); }
C4Real GetSegmentLength() const { return l; }
C4Real GetOuterFriction() const { return mu; }
C4RopeElement* GetFront() const { return Front; }
C4RopeElement* GetBack() const { return Back; }
private:
template<typename TRopeType1, typename TRopeType2>
void Solve(TRopeType1* prev, TRopeType2* next);
void Solve(C4RopeElement* prev, C4RopeElement* next);
// Whether to apply repulsive forces between rope segments.
// TODO: Could be made a property...
static const bool ApplyRepulsive = false;
const float w; // Width of rope
unsigned int NumIterations; // Number of iterations per frame
const float Width; // Width of rope
C4DefGraphics* Graphics;
int32_t n_segments;
int32_t SegmentCount;
// TODO: Add a "dynlength" feature which adapts the spring length to the
// distance of the two ends, up to a maximum... and/or callbacks to script
@ -130,10 +98,8 @@ private:
C4Real mu; // outer friction constant
C4Real eta; // inner friction constant
C4RopeEnd* front;
C4RopeEnd* back;
unsigned int n_iterations;
C4RopeElement* Front;
C4RopeElement* Back;
};
class C4RopeAul: public C4AulScript

View File

@ -21,12 +21,12 @@
static C4Object* FnGetFront(C4AulContext* Context)
{
return static_cast<C4Rope*>(Context->Def)->GetFront();
return static_cast<C4Rope*>(Context->Def)->GetFront()->GetObject();
}
static C4Object* FnGetBack(C4AulContext* Context)
{
return static_cast<C4Rope*>(Context->Def)->GetBack();
return static_cast<C4Rope*>(Context->Def)->GetBack()->GetObject();
}
C4RopeAul::C4RopeAul():