Add start of a engine-side rope implementation

basic rope physics works, but many things remain to be done:
	* collision with landscape
	* graphics
	* savegames
	* ClearPointers
	* Proper script interface
	* You name it
rope
Armin Burgmeier 2012-04-26 22:56:24 +02:00
parent cd31b0e6e4
commit b83cc3c6d2
10 changed files with 424 additions and 0 deletions

View File

@ -289,6 +289,8 @@ set(OC_CLONK_SOURCES
src/game/object/C4ObjectPtr.cpp
src/game/object/C4ObjectPtr.h
src/game/object/C4ObjectScript.cpp
src/game/object/C4Rope.cpp
src/game/object/C4Rope.h
src/game/object/C4Sector.cpp
src/game/object/C4Sector.h
src/game/object/C4Shape.cpp

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,6 @@
func Initialize()
{
var a = CreateObject(Rock, 100, 100, NO_OWNER);
var b = CreateObject(Rock, 200, 100, NO_OWNER);
a->CreateRope2(a, b, 100);
}

View File

@ -674,6 +674,7 @@ bool C4Game::GameOverCheck()
C4ST_NEW(ControlRcvStat, "C4Game::Execute ReceiveControl")
C4ST_NEW(ControlStat, "C4Game::Execute ExecuteControl")
C4ST_NEW(ExecRopesStat, "C4Game::Execute ExecRopes")
C4ST_NEW(ExecObjectsStat, "C4Game::Execute ExecObjects")
C4ST_NEW(GEStats, "C4Game::Execute pGlobalEffects->Execute")
C4ST_NEW(PXSStat, "C4Game::Execute PXS.Execute")
@ -731,6 +732,7 @@ bool C4Game::Execute() // Returns true if the game is over
// Game
EXEC_S( Ropes.Execute(); , ExecRopesStat )
EXEC_S( ExecObjects(); , ExecObjectsStat )
if (pGlobalEffects)
EXEC_S_DR( pGlobalEffects->Execute(NULL); , GEStats , "GEEx\0");

View File

@ -31,6 +31,7 @@
#include "C4Scoreboard.h"
#include <C4VideoPlayback.h>
#include <C4PlayerControl.h>
#include <C4Rope.h>
class C4Game
{
@ -90,6 +91,8 @@ public:
C4FileMonitor *pFileMonitor;
C4GameSec1Timer *pSec1Timer;
C4RopeList Ropes;
char CurrentScenarioSection[C4MaxName+1];
char ScenarioFilename[_MAX_PATH+1];
StdCopyStrBuf ScenarioTitle;

View File

@ -2125,6 +2125,20 @@ static bool FnSetMeshMaterial(C4AulObjectContext* ctx, C4String* Material, int i
return true;
}
static bool FnCreateRope(C4AulObjectContext *cthr, C4Object* First, C4Object* Second, int iSegments)
{
try
{
Game.Ropes.CreateRope(First, Second, iSegments);
return true;
}
catch(const C4RopeError& err)
{
DebugLogF("Failed to create rope: %s", err.what());
return false;
}
}
//=========================== C4Script Function Map ===================================
C4ScriptConstDef C4ScriptObjectConstMap[]=
@ -2462,6 +2476,7 @@ void InitObjectFunctionMap(C4AulScriptEngine *pEngine)
AddFunc(pEngine, "SetAttachTransform", FnSetAttachTransform);
AddFunc(pEngine, "GetMeshMaterial", FnGetMeshMaterial);
AddFunc(pEngine, "SetMeshMaterial", FnSetMeshMaterial);
AddFunc(pEngine, "CreateRope2", FnCreateRope);
AddFunc(pEngine, "ChangeDef", FnChangeDef);
AddFunc(pEngine, "GrabContents", FnGrabContents);
AddFunc(pEngine, "Punch", FnPunch);

View File

@ -0,0 +1,262 @@
/*
* OpenClonk, http://www.openclonk.org
*
* Copyright (c) 2012 Armin Burgmeier
*
* Portions might be copyrighted by other authors who have contributed
* to OpenClonk.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
* See isc_license.txt for full license and disclaimer.
*
* "Clonk" is a registered trademark of Matthes Bender.
* See clonk_trademark_license.txt for full license.
*/
#include <C4Include.h>
#include <C4Landscape.h>
#include <C4Rope.h>
namespace
{
C4Real ObjectDistance(C4Object* first, C4Object* second)
{
C4Real dx = second->fix_x - first->fix_x;
C4Real dy = second->fix_y - first->fix_y;
return ftofix(sqrt(fixtof(dx*dx + dy*dy))); // TODO: Replace by integer sqrt
}
}
C4RopeSegment::C4RopeSegment(C4Real x, C4Real y, C4Real m):
x(x), y(y), vx(Fix0), vy(Fix0), fx(Fix0), fy(Fix0),
m(m), next(NULL), prev(NULL)
{
}
void C4RopeSegment::AddForce(C4Real x, C4Real y)
{
fx += x;
fy += y;
}
void C4RopeSegment::Execute(C4Real dt)
{
vx += dt * fx / m;
vy += dt * fy / m;
// TODO: Collision detection
x += dt * vx;
y += dt * vy;
fx = fy = Fix0;
}
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)
{
this->end.x = x;
this->end.y = y;
this->end.vx = Fix0;
this->end.vy = Fix0;
this->end.m = m;
}
void C4RopeEnd::AddForce(C4Real x, C4Real y)
{
fx += x;
fy += y;
}
void C4RopeEnd::Execute(C4Real dt)
{
if(!fixed)
{
if(has_object)
{
obj->xdir += dt * fx / obj->Mass;
obj->ydir += dt * fy / obj->Mass;
}
else
{
// TODO: Share code for landscape collision with C4RopeSegment
end.vx += dt * fx / end.m;
end.vy += dt * fy / end.m;
end.x += dt * end.vx;
end.y += dt * end.vy;
}
}
fx = fy = Fix0;
}
C4Rope::C4Rope(C4Object* first_obj, C4Object* second_obj, int32_t n_segments):
n_segments(n_segments), l(ObjectDistance(first_obj, second_obj) / (n_segments + 1)),
k(Fix1), rho(Fix0), /* TODO: proper default values for k and rho */ n_iterations(5)
{
if(!PathFree(first_obj->GetX(), first_obj->GetY(), second_obj->GetX(), second_obj->GetY()))
throw C4RopeError("Failed to create rope: Path between objects is blocked");
if(n_segments < 1)
throw C4RopeError("Failed to create rope: Segments < 1 given");
// 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;
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;
prev_seg = seg;
}
front = new C4RopeEnd(first_seg, first_obj, true);
back = new C4RopeEnd(prev_seg, second_obj, false);
}
C4Rope::~C4Rope()
{
for(C4RopeSegment* cur = front->segment, *next; cur != NULL; 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)
{
C4Real fx = Fix0, fy = Fix0;
// 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)
{
fx += (dx / d) * k * (d - l);
fy += (dy / d) * k * (d - l);
}
// Inner friction
fx += -(prev->GetVx() - next->GetVx()) * rho;
fy += -(prev->GetVy() - next->GetVy()) * rho;
// TODO: Don't apply gravity to objects, it is applied automatically.
fy += ::Landscape.Gravity;
// Apply forces to masses
prev->AddForce(-fx, -fy);
next->AddForce(fx, fy);
}
void C4Rope::Execute()
{
C4Real dt = itofix(1, n_iterations);
for(unsigned int i = 0; i < n_iterations; ++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(C4RopeSegment* cur = front->segment; cur != NULL; cur = cur->next)
cur->Execute(dt);
front->Execute(dt);
back->Execute(dt);
}
}
void C4Rope::Draw(C4Facet& cgo)
{
struct Vertex
{
float x, y;
};
// TODO: Change this so that it draws a quad strip
Vertex Vertices[n_segments*4+4]; unsigned int i = 0;
for(C4RopeSegment* cur = front->segment; cur != NULL; cur = cur->next)
{
float prev_x = fixtof(cur->prev ? cur->prev->GetX() : front->GetX());
float prev_y = fixtof(cur->prev ? cur->prev->GetY() : front->GetY());
float x = fixtof(cur->GetX());
float y = fixtof(cur->GetY());
float l = sqrt( (prev_x - x) * (prev_x - x) + (prev_y - y) * (prev_y - y));
float x1 = prev_x + 2.5 * (prev_y - y) / l;
float x2 = prev_x - 2.5 * (prev_y - y) / l;
float x3 = x - 2.5 * (prev_y - y) / l;
float x4 = x + 2.5 * (prev_y - y) / l;
float y1 = prev_y + 2.5 * (prev_x - x) / l;
float y2 = prev_y - 2.5 * (prev_x - x) / l;
float y3 = y - 2.5 * (prev_x - x) / l;
float y4 = y + 2.5 * (prev_x - x) / l;
Vertices[i].x = x1;
Vertices[i].y = y1;
Vertices[i+1].x = x2;
Vertices[i+1].y = y2;
Vertices[i+2].x = x3;
Vertices[i+2].y = y3;
Vertices[i+3].x = x4;
Vertices[i+3].y = y4;
i += 4;
}
glColor4f(1.0f, 1.0f, 0.0f, 1.0f);
glVertexPointer(2, GL_FLOAT, 0, Vertices);
glEnableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
glDrawArrays(GL_QUADS, 0, n_segments*4);
}
C4RopeList::C4RopeList()
{
for(unsigned int i = 0; i < Ropes.size(); ++i)
delete Ropes[i];
}
C4Rope* C4RopeList::CreateRope(C4Object* first_obj, C4Object* second_obj, int32_t n_segments)
{
Ropes.push_back(new C4Rope(first_obj, second_obj, n_segments));
return Ropes.back();
}
void C4RopeList::Execute()
{
for(unsigned int i = 0; i < Ropes.size(); ++i)
Ropes[i]->Execute();
}
void C4RopeList::Draw(C4Facet& cgo)
{
for(unsigned int i = 0; i < Ropes.size(); ++i)
Ropes[i]->Draw(cgo);
}

View File

@ -0,0 +1,127 @@
/*
* OpenClonk, http://www.openclonk.org
*
* Copyright (c) 2012 Armin Burgmeier
*
* Portions might be copyrighted by other authors who have contributed
* to OpenClonk.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
* See isc_license.txt for full license and disclaimer.
*
* "Clonk" is a registered trademark of Matthes Bender.
* See clonk_trademark_license.txt for full license.
*/
#ifndef INC_C4Rope
#define INC_C4Rope
#include <stdexcept>
#include <C4Object.h>
// All units in pixels and frames
class C4RopeError: public std::runtime_error
{
public:
C4RopeError(const std::string& message): std::runtime_error(message) {}
};
class C4RopeSegment
{
friend class C4Rope;
public:
C4RopeSegment(C4Real x, C4Real y, C4Real m);
C4Real GetX() const { return x; }
C4Real GetY() const { return y; }
C4Real GetVx() const { return vx; }
C4Real GetVy() const { return vy; }
void AddForce(C4Real x, C4Real y);
void Execute(C4Real dt);
private:
C4Real x, y; // pos
C4Real vx, vy; // velocity
C4Real fx, fy; // force
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);
C4Real GetX() const { return has_object ? obj->fix_x : end.x; }
C4Real GetY() const { return has_object ? obj->fix_y : end.y; }
C4Real GetVx() const { return has_object ? obj->xdir : end.vx; }
C4Real GetVy() const { return has_object ? obj->ydir : end.vy; }
void AddForce(C4Real fx, C4Real fy);
void Execute(C4Real dt);
private:
C4RopeSegment* segment;
bool has_object;
bool fixed;
C4Real fx, fy;
union
{
struct {
C4Real x, y; // pos
C4Real vx, vy; // velocity
C4Real m; // mass
} end;
C4Object* obj;
};
};
class C4Rope
{
public:
C4Rope(C4Object* first_obj, C4Object* second_obj, int32_t n_segments);
~C4Rope();
void Draw(C4Facet& cgo);
void Execute();
private:
template<typename TRopeType1, typename TRopeType2>
void Solve(TRopeType1* prev, TRopeType2* next);
int32_t n_segments;
C4Real l; // spring length in equilibrium
C4Real k; // spring constant
C4Real rho; // friction constant
C4RopeEnd* front;
C4RopeEnd* back;
unsigned int n_iterations;
};
class C4RopeList
{
public:
C4RopeList();
void Execute();
void Draw(C4Facet& cgo);
C4Rope* CreateRope(C4Object* first_obj, C4Object* second_obj, int32_t n_segments);
private:
std::vector<C4Rope*> Ropes;
};
#endif // INC_C4Rope

View File

@ -255,6 +255,11 @@ void C4Viewport::Draw(C4TargetFacet &cgo0, bool fDrawOverlay)
::Objects.Draw(cgo, Player, 1, 2147483647 /* INT32_MAX */);
C4ST_STOP(ObjStat)
// draw ropes
C4ST_STARTNEW(ObjStat, "C4Viewport::Draw: Ropes")
::Game.Ropes.Draw(cgo);
C4ST_STOP(ObjStat)
// draw global particles
C4ST_STARTNEW(PartStat, "C4Viewport::Draw: Particles")
::Particles.GlobalParticles.Draw(cgo,NULL);

View File

@ -333,6 +333,7 @@ inline C4Real C4REAL10(int x) { return float(x) / 10; }
#endif
// define 0
const C4Real Fix0 = itofix(0);
const C4Real Fix1 = itofix(1);
// conversion...
// note: keep out! really dirty casts!