forked from Mirrors/openclonk
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 itrope
parent
cd31b0e6e4
commit
b83cc3c6d2
|
@ -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
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
|
|
@ -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);
|
||||
}
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
|
|
|
@ -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!
|
||||
|
|
Loading…
Reference in New Issue