forked from Mirrors/openclonk
Merge default into rope
commit
4f2aedd635
|
@ -475,6 +475,9 @@ set(OC_CLONK_SOURCES
|
|||
src/object/C4ObjectPtr.cpp
|
||||
src/object/C4ObjectPtr.h
|
||||
src/object/C4ObjectScript.cpp
|
||||
src/object/C4Rope.cpp
|
||||
src/object/C4Rope.h
|
||||
src/object/C4RopeScript.cpp
|
||||
src/object/C4Sector.cpp
|
||||
src/object/C4Sector.h
|
||||
src/object/C4Shape.cpp
|
||||
|
|
|
@ -30,7 +30,7 @@ func Construction(object creator)
|
|||
func Initialize()
|
||||
{
|
||||
CreateCase();
|
||||
CreateRope();
|
||||
CreateCaseRope();
|
||||
|
||||
if (partner)
|
||||
{
|
||||
|
@ -53,7 +53,7 @@ func CreateCase()
|
|||
case->Connect(this);
|
||||
}
|
||||
|
||||
func CreateRope()
|
||||
func CreateCaseRope()
|
||||
{
|
||||
rope = CreateObject(ElevatorRope, -19 * GetCalcDir(), -11, GetOwner());
|
||||
rope->SetAction("Be", case);
|
||||
|
@ -162,4 +162,4 @@ func Definition(def) {
|
|||
}
|
||||
local Name = "$Name$";
|
||||
local Description = "$Description$";
|
||||
local BlastIncinerate = 100;
|
||||
local BlastIncinerate = 100;
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
[DefCore]
|
||||
id=LiftTower_Rope
|
||||
Version=5,2,0,1
|
||||
Category=C4D_StaticBack
|
||||
Vertices=2
|
||||
Width=2
|
||||
Height=11
|
||||
Offset=-1,-5
|
Binary file not shown.
After Width: | Height: | Size: 3.5 KiB |
|
@ -0,0 +1 @@
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
|
||||
|
||||
func Initialize()
|
||||
{
|
||||
var a = CreateObject(Rock, 100, 100, NO_OWNER);
|
||||
var b = CreateObject(Rock, 200, 100, NO_OWNER);
|
||||
//a->SetCategory(C4D_StaticBack);
|
||||
//b->SetCategory(C4D_StaticBack);
|
||||
Scenario.rope = CreateRope(a, b, 20, LiftTower_Rope);
|
||||
//CreateRope(a, b, 1, LiftTower_Rope);
|
||||
}
|
||||
|
||||
func InitializePlayer(int plr)
|
||||
{
|
||||
var clonk = GetCrew(plr, 0);
|
||||
var a = CreateObject(Rock, clonk->GetX(), clonk->GetY()-100, NO_OWNER);
|
||||
a->SetCategory(C4D_StaticBack);
|
||||
var rope = CreateRope(a, clonk, 20, LiftTower_Rope);
|
||||
|
||||
rope->SetFrontAutoSegmentation(200);
|
||||
}
|
|
@ -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");
|
||||
|
@ -912,6 +914,7 @@ void C4Game::ClearPointers(C4Object * pObj)
|
|||
TransferZones.ClearPointers(pObj);
|
||||
if (pGlobalEffects)
|
||||
pGlobalEffects->ClearPointers(pObj);
|
||||
Ropes.ClearPointers(pObj);
|
||||
}
|
||||
|
||||
bool C4Game::TogglePause()
|
||||
|
@ -2186,6 +2189,7 @@ bool C4Game::InitScriptEngine()
|
|||
InitCoreFunctionMap(&ScriptEngine);
|
||||
InitObjectFunctionMap(&ScriptEngine);
|
||||
InitGameFunctionMap(&ScriptEngine);
|
||||
Ropes.InitFunctionMap(&ScriptEngine);
|
||||
|
||||
// system functions: check if system group is open
|
||||
if (!Application.OpenSystemGroup())
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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, NULL);
|
||||
C4ST_STOP(ObjStat)
|
||||
|
||||
// draw global particles
|
||||
C4ST_STARTNEW(PartStat, "C4Viewport::Draw: Particles")
|
||||
::Particles.GlobalParticles.Draw(cgo,NULL);
|
||||
|
|
|
@ -2125,6 +2125,23 @@ static bool FnSetMeshMaterial(C4AulObjectContext* ctx, C4String* Material, int i
|
|||
return true;
|
||||
}
|
||||
|
||||
static C4PropList* FnCreateRope(C4AulContext *cthr, C4Object* First, C4Object* Second, int iSegments, C4PropList* Graphics)
|
||||
{
|
||||
try
|
||||
{
|
||||
if(!Graphics) return false;
|
||||
C4Def* Def = Graphics->GetDef();
|
||||
if(!Def) return false;
|
||||
|
||||
return Game.Ropes.CreateRope(First, Second, iSegments, &Def->Graphics);
|
||||
}
|
||||
catch(const C4RopeError& err)
|
||||
{
|
||||
DebugLogF("Failed to create rope: %s", err.what());
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
//=========================== C4Script Function Map ===================================
|
||||
|
||||
C4ScriptConstDef C4ScriptObjectConstMap[]=
|
||||
|
@ -2462,6 +2479,7 @@ void InitObjectFunctionMap(C4AulScriptEngine *pEngine)
|
|||
AddFunc(pEngine, "SetAttachTransform", FnSetAttachTransform);
|
||||
AddFunc(pEngine, "GetMeshMaterial", FnGetMeshMaterial);
|
||||
AddFunc(pEngine, "SetMeshMaterial", FnSetMeshMaterial);
|
||||
AddFunc(pEngine, "CreateRope", FnCreateRope);
|
||||
AddFunc(pEngine, "ChangeDef", FnChangeDef);
|
||||
AddFunc(pEngine, "GrabContents", FnGrabContents);
|
||||
AddFunc(pEngine, "Punch", FnPunch);
|
||||
|
|
|
@ -0,0 +1,806 @@
|
|||
/*
|
||||
* 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>
|
||||
|
||||
// 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 {
|
||||
Vertex() {}
|
||||
Vertex(float x, float y): x(x), y(y) {}
|
||||
|
||||
float x;
|
||||
float y;
|
||||
};
|
||||
|
||||
struct DrawVertex: Vertex {
|
||||
float u;
|
||||
float v;
|
||||
};
|
||||
|
||||
// For use in initializer list
|
||||
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
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
const float l = sqrt( (v1.x - v2.x)*(v1.x - v2.x) + (v1.y - v2.y)*(v1.y - v2.y));
|
||||
|
||||
out1.x = v1.x + w/2.0f * (v1.y - v2.y) / l;
|
||||
out1.y = v1.y - w/2.0f * (v1.x - v2.x) / l;
|
||||
out2.x = v1.x - w/2.0f * (v1.y - v2.y) / l;
|
||||
out2.y = v1.y + w/2.0f * (v1.x - v2.x) / l;
|
||||
out3.x = v2.x + w/2.0f * (v1.y - v2.y) / l;
|
||||
out3.y = v2.y - w/2.0f * (v1.x - v2.x) / l;
|
||||
out4.x = v2.x - w/2.0f * (v1.y - v2.y) / l;
|
||||
out4.y = v2.y + w/2.0f * (v1.x - v2.x) / l;
|
||||
}
|
||||
|
||||
// Copied from StdGL.cpp... actually the rendering code should be moved
|
||||
// there so we don't need to duplicate it here.
|
||||
bool ApplyZoomAndTransform(float ZoomX, float ZoomY, float Zoom, C4BltTransform* pTransform)
|
||||
{
|
||||
// Apply zoom
|
||||
glTranslatef(ZoomX, ZoomY, 0.0f);
|
||||
glScalef(Zoom, Zoom, 1.0f);
|
||||
glTranslatef(-ZoomX, -ZoomY, 0.0f);
|
||||
|
||||
// Apply transformation
|
||||
if (pTransform)
|
||||
{
|
||||
const GLfloat transform[16] = { pTransform->mat[0], pTransform->mat[3], 0, pTransform->mat[6], pTransform->mat[1], pTransform->mat[4], 0, pTransform->mat[7], 0, 0, 1, 0, pTransform->mat[2], pTransform->mat[5], 0, pTransform->mat[8] };
|
||||
glMultMatrixf(transform);
|
||||
|
||||
// Compute parity of the transformation matrix - if parity is swapped then
|
||||
// we need to cull front faces instead of back faces.
|
||||
const float det = transform[0]*transform[5]*transform[15]
|
||||
+ transform[4]*transform[13]*transform[3]
|
||||
+ transform[12]*transform[1]*transform[7]
|
||||
- transform[0]*transform[13]*transform[7]
|
||||
- transform[4]*transform[1]*transform[15]
|
||||
- transform[12]*transform[5]*transform[3];
|
||||
return det > 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// PathFinder callback function. This saves the first and last waypoint which
|
||||
// determine the direction toward which to pull.
|
||||
struct PullPathInfo
|
||||
{
|
||||
bool FirstSeg;
|
||||
const int32_t bx, by;
|
||||
const int32_t ex, ey;
|
||||
int32_t px, py;
|
||||
int32_t nx, ny;
|
||||
C4Real d;
|
||||
};
|
||||
|
||||
bool PullPathAccumulator(int32_t iX, int32_t iY, intptr_t iTransferTarget, intptr_t PathInfoPtr)
|
||||
{
|
||||
PullPathInfo* Info = (PullPathInfo*)PathInfoPtr;
|
||||
|
||||
// Ignore points which are very close (~7 pixels) to start or end. Such
|
||||
// points are often misleading because the PathFinder first pulls away a
|
||||
// bit from the ground before actually moving toward the destination.
|
||||
if(Info->px != iX || Info->py != iY)
|
||||
{
|
||||
if(Info->FirstSeg)
|
||||
{
|
||||
if( (Info->ex - iX) * (Info->ex - iX) + (Info->ey - iY) * (Info->ey - iY) > 50)
|
||||
{
|
||||
Info->nx = iX;
|
||||
Info->ny = iY;
|
||||
Info->FirstSeg = false;
|
||||
}
|
||||
}
|
||||
|
||||
if( (Info->bx - iX) * (Info->bx - iX) + (Info->by - iY) * (Info->by - iY) > 50)
|
||||
{
|
||||
C4Real dx = itofix(Info->px - iX);
|
||||
C4Real dy = itofix(Info->py - iY);
|
||||
Info->px = iX;
|
||||
Info->py = iY;
|
||||
Info->d += ftofix(sqrt(fixtof(dx*dx + dy*dy)));
|
||||
}
|
||||
}
|
||||
|
||||
return true; // note return value is ignored
|
||||
}
|
||||
}
|
||||
|
||||
C4RopeElement::C4RopeElement(C4Object* obj, bool fixed):
|
||||
Fixed(fixed), fx(Fix0), fy(Fix0), rx(Fix0), ry(Fix0), rdt(Fix0),
|
||||
Next(NULL), Prev(NULL), Object(obj)
|
||||
{
|
||||
}
|
||||
|
||||
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 C4RopeElement::Execute(const C4Rope* rope, C4Real dt)
|
||||
{
|
||||
ResetForceRedirection(dt);
|
||||
|
||||
// If attached object is contained, apply force to container
|
||||
C4Object* Target = Object;
|
||||
if(Target)
|
||||
while(Target->Contained)
|
||||
Target = Target->Contained;
|
||||
|
||||
// Apply stricking friction
|
||||
if(!Target)
|
||||
{
|
||||
// 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(vx*vx + vy*vy < Fix1/4)
|
||||
{
|
||||
if(fx*fx + fy*fy < Fix1/4)
|
||||
{
|
||||
fx = fy = Fix0;
|
||||
vx = vy = Fix0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply forces
|
||||
if(!Fixed)
|
||||
{
|
||||
if(!Target)
|
||||
{
|
||||
vx += dt * fx / m;
|
||||
vy += dt * fy / m;
|
||||
}
|
||||
else if( (Target->Category & C4D_StaticBack) == 0)
|
||||
{
|
||||
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, 0, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
prev_x = inter_x;
|
||||
prev_y = inter_y;
|
||||
}
|
||||
|
||||
if(!hit)
|
||||
{
|
||||
x += dt * vx;
|
||||
y += dt * vy;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!Fixed)
|
||||
{
|
||||
// Object Force redirection if object has no contact attachment (if it
|
||||
// has then the procedure takes care of moving the object around
|
||||
// O(pixel) obstacles in the landscape).
|
||||
if(!Target->Action.t_attach && 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;
|
||||
int iContactVertex = -1;
|
||||
for (int i = 0; i < Target->Shape.VtxNum; ++i)
|
||||
if(Target->Shape.GetVertexContact(i, dwCNATCheck, Target->GetX(), Target->GetY()))
|
||||
iContactVertex = i;
|
||||
|
||||
if(iContactVertex != -1)
|
||||
SetForceRedirection(rope, Target->Shape.VtxX[iContactVertex], Target->Shape.VtxY[iContactVertex]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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, int ox, int oy)
|
||||
{
|
||||
SetForceRedirectionByLookAround(rope, ox, oy, GetVx(), GetVy(), itofix(5), itofix(75));
|
||||
|
||||
#if 0
|
||||
if(!SetForceRedirectionByLookAround(rope, ox, oy, GetVx(), GetVy(), itofix(5), itofix(15)))
|
||||
if(!SetForceRedirectionByLookAround(rope, ox, oy, GetVx(), GetVy(), itofix(5), itofix(30)))
|
||||
SetForceRedirectionByLookAround(rope, ox, oy, GetVx(), GetVy(), itofix(5), itofix(45));
|
||||
//SetForceRedirectionByLookAround(rope, ox, oy, GetVx(), GetVy(), itofix(5), itofix(60));
|
||||
#endif
|
||||
}
|
||||
|
||||
bool C4RopeElement::SetForceRedirectionByLookAround(const C4Rope* rope, int ox, int oy, C4Real dx, C4Real dy, C4Real l, C4Real angle)
|
||||
{
|
||||
// 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(angle);
|
||||
const C4Real Sin75 = Sin(angle);
|
||||
|
||||
C4Real vx1 = Cos75 * dx + Sin75 * dy;
|
||||
C4Real vy1 = -Sin75 * dx + Cos75 * dy;
|
||||
C4Real vx2 = Cos75 * dx - Sin75 * dy;
|
||||
C4Real vy2 = Sin75 * dx + Cos75 * dy;
|
||||
const C4Real v = ftofix(sqrt(fixtof(dx*dx + dy*dy)));
|
||||
|
||||
// TODO: We should check more than a single pixel. There's some more potential for optimization here.
|
||||
if(v != Fix0 && !GBackSolid(ox + fixtoi(GetX() + vx1*l/v), oy + fixtoi(GetY() + vy1*l/v)))
|
||||
//if(v != Fix0 && PathFree(ox + fixtoi(GetX()), oy + fixtoi(GetY()), ox + fixtoi(GetX() + vx1*l/v), oy + 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(ox + fixtoi(GetX() + vx2/v), oy + fixtoi(GetY() + vy2/v)))
|
||||
//else if(v != Fix0 && PathFree(ox + fixtoi(GetX()), oy + fixtoi(GetY()), ox + fixtoi(GetX() + vx2/v), oy + fixtoi(GetY() + vy2/v)))
|
||||
{ rx = vx2/v; ry = vy2/v; rdt = Fix1/4; } // Enable force redirection for 1/4th of a frame
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
C4Rope::C4Rope(C4PropList* Prototype, C4Object* first_obj, C4Object* second_obj, int32_t n_segments, C4DefGraphics* graphics):
|
||||
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(10),
|
||||
FrontAutoSegmentation(Fix0), BackAutoSegmentation(Fix0)
|
||||
{
|
||||
if(!PathFree(first_obj->GetX(), first_obj->GetY(), second_obj->GetX(), second_obj->GetY()))
|
||||
throw C4RopeError("Path between objects is blocked");
|
||||
if(n_segments < 1)
|
||||
throw C4RopeError("Segments < 1 given");
|
||||
if(Graphics->Type != C4DefGraphics::TYPE_Bitmap)
|
||||
throw C4RopeError("Can only use bitmap as rope graphics");
|
||||
|
||||
Front = new C4RopeElement(first_obj, false);
|
||||
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)
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Link back segment
|
||||
prev_seg->Next = Back;
|
||||
Back->Prev = prev_seg;
|
||||
}
|
||||
|
||||
C4Rope::~C4Rope()
|
||||
{
|
||||
for(C4RopeElement* cur = Front, *next; cur != NULL; cur = next)
|
||||
{
|
||||
next = cur->Next;
|
||||
delete cur;
|
||||
}
|
||||
}
|
||||
|
||||
C4Real C4Rope::GetL(const C4RopeElement* prev, const C4RopeElement* next) const
|
||||
{
|
||||
// Normally the segment length is fixed at l, however if auto segmentation
|
||||
// is enabled then the first or last segments can be shorter.
|
||||
const C4Real dx = next->GetX() - prev->GetX();
|
||||
const C4Real dy = next->GetY() - prev->GetY();
|
||||
|
||||
if(FrontAutoSegmentation > Fix0)
|
||||
if(prev == Front || next == Front)
|
||||
return Min(itofix(5), ftofix(sqrt(fixtof(dx*dx+dy*dy))));
|
||||
if(BackAutoSegmentation > Fix0)
|
||||
if(prev == Back || next == Back)
|
||||
return Min(itofix(5), ftofix(sqrt(fixtof(dx*dx+dy*dy))));
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
void C4Rope::DoAutoSegmentation(C4RopeElement* fixed, C4RopeElement* first, C4Real max)
|
||||
{
|
||||
// TODO: Should add a timeout to prevent oscillations: After one segment was
|
||||
// inserted do not allow segments to be removed in the same frame or couple
|
||||
// of frames, and vice versa. This gives the system a chance to get into
|
||||
// equilibrium before continuing with auto-segmentation.
|
||||
|
||||
// Auto segmentation enabled?
|
||||
if(max > Fix0)
|
||||
{
|
||||
const C4Real dx = first->GetX() - fixed->GetX();
|
||||
const C4Real dy = first->GetY() - fixed->GetY();
|
||||
const C4Real lf = dx*dx+dy*dy;
|
||||
|
||||
if(lf > l*l*itofix(15,10)*itofix(15,10) && l * SegmentCount < max)
|
||||
{
|
||||
const C4Real x = fixed->GetX() + itofix(5,10)*dx;
|
||||
const C4Real y = fixed->GetY() + itofix(5,10)*dy;
|
||||
C4RopeElement* new_elem = new C4RopeElement(x, y, first->GetMass(), false);
|
||||
new_elem->vx = (fixed->GetVx() + first->GetVx())/itofix(2);
|
||||
new_elem->vy = (fixed->GetVy() + first->GetVy())/itofix(2);
|
||||
|
||||
// Link the new element
|
||||
if(fixed->Next == first)
|
||||
{
|
||||
new_elem->Prev = fixed;
|
||||
new_elem->Next = first;
|
||||
fixed->Next = new_elem;
|
||||
first->Prev = new_elem;
|
||||
}
|
||||
else
|
||||
{
|
||||
new_elem->Prev = first;
|
||||
new_elem->Next = fixed;
|
||||
fixed->Prev = new_elem;
|
||||
first->Next = new_elem;
|
||||
}
|
||||
++SegmentCount;
|
||||
}
|
||||
else if(SegmentCount > 0) // Rope cannot be shorter than just Beginning and End segment
|
||||
{
|
||||
// To find out whether we can shorten the rope we do the following:
|
||||
// We go through all elements and if at some point the nominal rope length
|
||||
// is shorter than the distance between that element and the fixpoint
|
||||
// and the path between the two is free, then the rope is shortened.
|
||||
unsigned int i = 1;
|
||||
for(C4RopeElement* cur = fixed->Next; cur != NULL; cur = (fixed->Next == first ? cur->Next : cur->Prev), ++i)
|
||||
{
|
||||
// We use integers, not reals here, to protect for overflows. This works
|
||||
// because these numbers are large enough so we don't need to take care
|
||||
// about subpixel precision.
|
||||
const unsigned int nd = fixtoi(l*itofix(i));
|
||||
|
||||
const unsigned int dx = fixtoi(cur->GetX() - fixed->GetX());
|
||||
const unsigned int dy = fixtoi(cur->GetY() - fixed->GetY());
|
||||
const unsigned int d2 = dx*dx+dy*dy;
|
||||
|
||||
if(d2 > nd*nd*15/10*15/10)
|
||||
break;
|
||||
else if(d2 < nd*nd*8/10*8/10)
|
||||
{
|
||||
// TODO: Check whether all elements have PathFree, and stop if one hasn't?
|
||||
if(PathFree(fixtoi(fixed->GetX()), fixtoi(fixed->GetY()), fixtoi(cur->GetX()), fixtoi(cur->GetY())))
|
||||
{
|
||||
C4RopeElement* second = ((fixed->Next == first) ? first->Next : first->Prev);
|
||||
assert(second != NULL);
|
||||
|
||||
// Remove first, relink fixed and second
|
||||
C4RopeElement* Del = first;
|
||||
|
||||
if(fixed->Next == first)
|
||||
{
|
||||
fixed->Next = second;
|
||||
second->Prev = fixed;
|
||||
}
|
||||
else
|
||||
{
|
||||
fixed->Prev = second;
|
||||
second->Next = fixed;
|
||||
}
|
||||
|
||||
delete Del;
|
||||
--SegmentCount;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void C4Rope::Solve(C4RopeElement* prev, C4RopeElement* next)
|
||||
{
|
||||
// Rope forces
|
||||
const C4Real dx = prev->GetX() - next->GetX();
|
||||
const C4Real dy = prev->GetY() - next->GetY();
|
||||
|
||||
if(dx*dx + dy*dy > Fix0)
|
||||
{
|
||||
// Get segment length between prev and next
|
||||
const C4Real l = GetL(prev, next);
|
||||
|
||||
// Compute forces between these points. If there is no material between the
|
||||
// rope segments, this is just a straight line. Otherwise, run the
|
||||
// pathfinder to find out in which direction to pull.
|
||||
// If the PathFinder doesn't find a path... then well.. no idea. Maybe
|
||||
// the rope got buried by an earthquake, or someone built a loam bridge
|
||||
// over a rope segment. In that case just apply the normal "straight"
|
||||
// forces and hope the best...
|
||||
|
||||
// TODO: Avoid any of these expensive computations if both prev and next
|
||||
// have force redirection active
|
||||
|
||||
int pix = fixtoi(prev->GetX());
|
||||
int piy = fixtoi(prev->GetY());
|
||||
int nix = fixtoi(next->GetX());
|
||||
int niy = fixtoi(next->GetY());
|
||||
|
||||
// TODO: For objects, run PathFree and PathFinder from vertex which has
|
||||
// had contact to the landscape previously.
|
||||
|
||||
C4Real dx1, dy1, dx2, dy2, d;
|
||||
PullPathInfo Info = { true, pix, piy, nix, niy, nix, niy, pix, piy, Fix0 };
|
||||
if(dx*dx+dy*dy > 4*l && !PathFree(pix, piy, nix, niy) && Game.PathFinder.Find(pix, piy, nix, niy, PullPathAccumulator, (intptr_t)&Info))
|
||||
{
|
||||
C4Real dpx = itofix(Info.px - pix);
|
||||
C4Real dpy = itofix(Info.py - piy);
|
||||
C4Real dp = ftofix(sqrt(fixtof(dpx*dpx + dpy*dpy))); // TODO: Could be computed in accumulator
|
||||
|
||||
C4Real dnx = itofix(Info.nx - nix);
|
||||
C4Real dny = itofix(Info.ny - niy);
|
||||
C4Real dn = ftofix(sqrt(fixtof(dnx*dnx + dny*dny))); // TODO: Could be computed in accumulator
|
||||
|
||||
dx1 = dpx / dp;
|
||||
dy1 = dpy / dp;
|
||||
dx2 = dnx / dn;
|
||||
dy2 = dny / dn;
|
||||
d = itofix(Info.d) + dp;
|
||||
|
||||
/*printf("Solved %p via PathFinder, from %d/%d to %d/%d\n", this, pix, piy, nix, niy);
|
||||
printf(" --> p to %d/%d, n to %d/%d\n", Info.px, Info.py, Info.nx, Info.ny);
|
||||
printf(" --> vec_p=%f/%f, vec_n=%f/%f, d=%f\n", fixtof(dx1), fixtof(dy1), fixtof(dx2), fixtof(dy2), fixtof(d));*/
|
||||
}
|
||||
else
|
||||
{
|
||||
d = ftofix(sqrt(fixtof(dx*dx + dy*dy)));
|
||||
dx1 = -(dx / d);
|
||||
dy1 = -(dy / d);
|
||||
dx2 = -dx1;
|
||||
dy2 = -dy1;
|
||||
}
|
||||
|
||||
if(ApplyRepulsive || d > l)
|
||||
{
|
||||
if(prev->rdt != Fix0) { dx1 = prev->rx; dy1 = prev->ry; }
|
||||
if(next->rdt != Fix0) { dx2 = next->rx; dy2 = next->ry; }
|
||||
|
||||
prev->AddForce(dx1 * k * (d - l), dy1 * k * (d - l));
|
||||
next->AddForce(dx2 * k * (d - l), dy2 * k * (d - l));
|
||||
}
|
||||
}
|
||||
|
||||
// Inner friction
|
||||
// TODO: This is very sensitive to numerical instabilities for mid-to-high
|
||||
// eta values. We might want to prevent a sign change of either F or V induced
|
||||
// by this factor.
|
||||
// TODO: Don't apply inner friction for segments connected to fixed rope ends?
|
||||
C4Real fx = (prev->GetVx() - next->GetVx()) * eta;
|
||||
C4Real fy = (prev->GetVy() - next->GetVy()) * eta;
|
||||
prev->AddForce(-fx, -fy);
|
||||
next->AddForce(fx, fy);
|
||||
|
||||
// Could add air/water friction here
|
||||
|
||||
// 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, (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, NumIterations);
|
||||
for(unsigned int i = 0; i < NumIterations; ++i)
|
||||
{
|
||||
// Execute auto-segmentation
|
||||
DoAutoSegmentation(Front, Front->Next, FrontAutoSegmentation);
|
||||
DoAutoSegmentation(Back, Back->Prev, BackAutoSegmentation);
|
||||
|
||||
// Compute forces
|
||||
for(C4RopeElement* cur = Front; cur->Next != NULL; cur = cur->Next)
|
||||
Solve(cur, cur->Next);
|
||||
|
||||
// Apply forces
|
||||
for(C4RopeElement* cur = Front; cur != NULL; cur = cur->Next)
|
||||
cur->Execute(this, dt);
|
||||
}
|
||||
}
|
||||
|
||||
void C4Rope::ClearPointers(C4Object* obj)
|
||||
{
|
||||
for(C4RopeElement* cur = Front; cur != NULL; cur = cur->Next)
|
||||
{
|
||||
if(cur->GetObject() == obj)
|
||||
{
|
||||
cur->x = obj->fix_x;
|
||||
cur->y = obj->fix_y;
|
||||
cur->vx = obj->xdir;
|
||||
cur->vy = obj->ydir;
|
||||
cur->m = obj->Mass;
|
||||
|
||||
cur->Object = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Move this to StdGL
|
||||
void C4Rope::Draw(C4TargetFacet& cgo, C4BltTransform* pTransform)
|
||||
{
|
||||
#if 1
|
||||
Vertex Tmp[4];
|
||||
DrawVertex* Vertices = new DrawVertex[SegmentCount*2+4]; // TODO: Use a vbo and map it into memory instead?
|
||||
|
||||
VertexPos(Vertices[0], Vertices[1], Tmp[0], Tmp[1],
|
||||
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;
|
||||
Vertices[1].u = 1.0f;
|
||||
Vertices[1].v = 0.0f;
|
||||
|
||||
const float rsl = 1.0f/Width * Graphics->GetBitmap()->Wdt / Graphics->GetBitmap()->Hgt; // rope segment length mapped to Gfx bitmap
|
||||
float accl = 0.0f;
|
||||
|
||||
unsigned int i = 2;
|
||||
bool parity = true;
|
||||
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->GetX()),// ? cur->Next->GetX() : Back->GetX()),
|
||||
fixtof(cur->Next->GetY()));// ? cur->Next->GetY() : Back->GetY()));
|
||||
Vertex v3(fixtof(cur->Prev->GetX()),// ? cur->Prev->GetX() : Front->GetX()),
|
||||
fixtof(cur->Prev->GetY()));// ? cur->Prev->GetY() : Front->GetY()));
|
||||
|
||||
//const C4Real l = GetL(cur->Prev, cur);
|
||||
//const float rsl = fixtof(l)/fixtof(Width) * Graphics->GetBitmap()->Wdt / Graphics->GetBitmap()->Hgt; // rope segment length mapped to Gfx bitmap
|
||||
|
||||
// Parity -- parity swaps for each pointed angle (<90 deg)
|
||||
float cx = v1.x - v3.x;
|
||||
float cy = v1.y - v3.y;
|
||||
float ex = v1.x - v2.x;
|
||||
float ey = v1.y - v2.y;
|
||||
|
||||
// TODO: Another way to draw this would be to insert a "pseudo" segment so that there are no pointed angles at all
|
||||
if(cx*ex+cy*ey > 0)
|
||||
parity = !parity;
|
||||
|
||||
// Obtain vertex positions
|
||||
if(parity)
|
||||
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, Width);
|
||||
|
||||
Tmp[2].x = (Tmp[0].x + Tmp[2].x)/2.0f;
|
||||
Tmp[2].y = (Tmp[0].y + Tmp[2].y)/2.0f;
|
||||
Tmp[3].x = (Tmp[1].x + Tmp[3].x)/2.0f;
|
||||
Tmp[3].y = (Tmp[1].y + Tmp[3].y)/2.0f;
|
||||
|
||||
// renormalize
|
||||
float dx = Tmp[3].x - Tmp[2].x;
|
||||
float dy = Tmp[3].y - Tmp[2].y;
|
||||
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, -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;
|
||||
|
||||
accl += fixtof(GetL(cur->Prev, cur));
|
||||
Vertices[i].u = 0.0f; //parity ? 0.0f : 1.0f;
|
||||
Vertices[i].v = accl * rsl;
|
||||
Vertices[i+1].u = 1.0f; //parity ? 1.0f : 0.0f;
|
||||
Vertices[i+1].v = accl * rsl;
|
||||
|
||||
Tmp[0] = Vertices[i+2];
|
||||
Tmp[1] = Vertices[i+3];
|
||||
}
|
||||
|
||||
accl += fixtof(GetL(Back->Prev, Back));
|
||||
Vertices[i].u = 0.0f; //parity ? 0.0f : 1.0f;
|
||||
Vertices[i].v = accl * rsl;
|
||||
Vertices[i+1].u = 1.0f; //parity ? 1.0f : 0.0f;
|
||||
Vertices[i+1].v = accl * rsl;
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glBindTexture(GL_TEXTURE_2D, (*Graphics->GetBitmap()->ppTex)->texName);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
|
||||
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
glVertexPointer(2, GL_FLOAT, sizeof(DrawVertex), &Vertices->x);
|
||||
glTexCoordPointer(2, GL_FLOAT, sizeof(DrawVertex), &Vertices->u);
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
glDisableClientState(GL_COLOR_ARRAY);
|
||||
glDrawArrays(GL_QUAD_STRIP, 0, SegmentCount*2+4);
|
||||
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
//glDisable(GL_BLEND);
|
||||
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
|
||||
delete[] Vertices;
|
||||
|
||||
#else
|
||||
// Debug:
|
||||
for(C4RopeElement* cur = Front; cur != NULL; cur = cur->Next)
|
||||
{
|
||||
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->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->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();
|
||||
}
|
||||
|
||||
const float rx = fixtof(cur->rx);
|
||||
const float ry = fixtof(cur->ry);
|
||||
const float r = sqrt(rx*rx + ry*ry);
|
||||
if(r > 0.1)
|
||||
{
|
||||
glBegin(GL_TRIANGLES);
|
||||
glColor3f(0.0f, BoundBy(r/2.5f, 0.0f, 1.0f), 1.0f);
|
||||
glVertex2f(fixtof(cur->x) + rx/r*4.0f, fixtof(cur->y) + ry/r*4.0f);
|
||||
glVertex2f(fixtof(cur->x) - ry/r*1.5f, fixtof(cur->y) + rx/r*1.5f);
|
||||
glVertex2f(fixtof(cur->x) + ry/r*1.5f, fixtof(cur->y) - rx/r*1.5f);
|
||||
glEnd();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
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, C4DefGraphics* graphics)
|
||||
{
|
||||
Ropes.push_back(new C4Rope(RopeAul.GetPropList(), first_obj, second_obj, n_segments, graphics));
|
||||
return Ropes.back();
|
||||
}
|
||||
|
||||
void C4RopeList::RemoveRope(C4Rope* rope)
|
||||
{
|
||||
for(std::vector<C4Rope*>::iterator iter = Ropes.begin(); iter != Ropes.end(); ++iter)
|
||||
{
|
||||
if(*iter == rope)
|
||||
{
|
||||
Ropes.erase(iter);
|
||||
delete rope;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void C4RopeList::Execute()
|
||||
{
|
||||
for(unsigned int i = 0; i < Ropes.size(); ++i)
|
||||
Ropes[i]->Execute();
|
||||
}
|
||||
|
||||
void C4RopeList::Draw(C4TargetFacet& cgo, C4BltTransform* pTransform)
|
||||
{
|
||||
ZoomData z;
|
||||
pDraw->GetZoom(&z);
|
||||
|
||||
glPushMatrix();
|
||||
ApplyZoomAndTransform(z.X, z.Y, z.Zoom, pTransform);
|
||||
glTranslatef(cgo.X-cgo.TargetX, cgo.Y-cgo.TargetY, 0.0f);
|
||||
|
||||
for(unsigned int i = 0; i < Ropes.size(); ++i)
|
||||
Ropes[i]->Draw(cgo, pTransform);
|
||||
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
void C4RopeList::ClearPointers(C4Object* obj)
|
||||
{
|
||||
for(unsigned int i = 0; i < Ropes.size(); ++i)
|
||||
Ropes[i]->ClearPointers(obj);
|
||||
}
|
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
* 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 C4Rope;
|
||||
class C4RopeElement
|
||||
{
|
||||
friend class C4Rope;
|
||||
public:
|
||||
C4RopeElement(C4Object* obj, bool fixed);
|
||||
C4RopeElement(C4Real x, C4Real y, C4Real m, bool fixed);
|
||||
|
||||
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(const C4Rope* rope, C4Real dt);
|
||||
private:
|
||||
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 fx, fy; // force
|
||||
C4Real rx, ry; // force redirection
|
||||
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
|
||||
{
|
||||
public:
|
||||
C4Rope(C4PropList* Prototype, C4Object* first_obj, C4Object* second_obj, int32_t n_segments, C4DefGraphics* graphics);
|
||||
~C4Rope();
|
||||
|
||||
void Draw(C4TargetFacet& cgo, C4BltTransform* pTransform);
|
||||
void Execute();
|
||||
|
||||
void ClearPointers(C4Object* obj);
|
||||
|
||||
C4Real GetSegmentLength() const { return l; }
|
||||
C4Real GetOuterFriction() const { return mu; }
|
||||
|
||||
C4RopeElement* GetFront() const { return Front; }
|
||||
C4RopeElement* GetBack() const { return Back; }
|
||||
void SetFront(C4Object* obj, C4Real x, C4Real y) { Front->Object = obj; Front->x = x; Front->y = y; }
|
||||
void SetBack(C4Object* obj, C4Real x, C4Real y) { Back->Object = obj; Back->x = x; Back->y = y; }
|
||||
|
||||
C4Real GetFrontAutoSegmentation() const { return FrontAutoSegmentation; }
|
||||
C4Real GetBackAutoSegmentation() const { return BackAutoSegmentation; }
|
||||
void SetFrontAutoSegmentation(C4Real max) { FrontAutoSegmentation = max; }
|
||||
void SetBackAutoSegmentation(C4Real max) { BackAutoSegmentation = max; }
|
||||
|
||||
bool GetFrontFixed() const { return Front->Fixed; }
|
||||
bool GetBackFixed() const { return Back->Fixed; }
|
||||
void SetFrontFixed(bool fixed) { Front->Fixed = fixed; }
|
||||
void SetBackFixed(bool fixed) { Back->Fixed = fixed; }
|
||||
private:
|
||||
C4Real GetL(const C4RopeElement* prev, const C4RopeElement* next) const;
|
||||
|
||||
void DoAutoSegmentation(C4RopeElement* fixed, C4RopeElement* first, C4Real max);
|
||||
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;
|
||||
|
||||
unsigned int NumIterations; // Number of iterations per frame
|
||||
const float Width; // Width of rope
|
||||
C4DefGraphics* Graphics;
|
||||
int32_t SegmentCount;
|
||||
|
||||
C4Real l; // spring length in equilibrium
|
||||
C4Real k; // spring constant
|
||||
C4Real mu; // outer friction constant
|
||||
C4Real eta; // inner friction constant
|
||||
|
||||
C4RopeElement* Front;
|
||||
C4RopeElement* Back;
|
||||
|
||||
C4Real FrontAutoSegmentation;
|
||||
C4Real BackAutoSegmentation;
|
||||
};
|
||||
|
||||
class C4RopeAul: public C4AulScript
|
||||
{
|
||||
public:
|
||||
C4RopeAul();
|
||||
virtual ~C4RopeAul();
|
||||
|
||||
virtual bool Delete() { return false; }
|
||||
virtual C4PropList* GetPropList() { return RopeDef; }
|
||||
|
||||
void InitFunctionMap(C4AulScriptEngine* pEngine);
|
||||
|
||||
protected:
|
||||
C4PropList* RopeDef;
|
||||
};
|
||||
|
||||
class C4RopeList
|
||||
{
|
||||
public:
|
||||
C4RopeList();
|
||||
|
||||
void InitFunctionMap(C4AulScriptEngine* pEngine) { RopeAul.InitFunctionMap(pEngine); }
|
||||
|
||||
void Execute();
|
||||
void Draw(C4TargetFacet& cgo, C4BltTransform* pTransform);
|
||||
|
||||
C4Rope* CreateRope(C4Object* first_obj, C4Object* second_obj, int32_t n_segments, C4DefGraphics* graphics);
|
||||
void RemoveRope(C4Rope* rope);
|
||||
|
||||
void ClearPointers(C4Object* obj);
|
||||
|
||||
private:
|
||||
C4RopeAul RopeAul;
|
||||
std::vector<C4Rope*> Ropes;
|
||||
};
|
||||
|
||||
#endif // INC_C4Rope
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* 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 <C4Rope.h>
|
||||
#include <C4AulDefFunc.h>
|
||||
|
||||
static C4Void FnRemove(C4AulContext* Context)
|
||||
{
|
||||
Game.Ropes.RemoveRope(static_cast<C4Rope*>(Context->Def));
|
||||
}
|
||||
|
||||
static C4Object* FnGetFront(C4AulContext* Context)
|
||||
{
|
||||
return static_cast<C4Rope*>(Context->Def)->GetFront()->GetObject();
|
||||
}
|
||||
|
||||
static C4Object* FnGetBack(C4AulContext* Context)
|
||||
{
|
||||
return static_cast<C4Rope*>(Context->Def)->GetBack()->GetObject();
|
||||
}
|
||||
|
||||
static C4Void FnSetFront(C4AulContext* Context, C4Object* obj, Nillable<int> x, Nillable<int> y)
|
||||
{
|
||||
static_cast<C4Rope*>(Context->Def)->SetFront(obj, x.IsNil() ? Fix0 : itofix(x), y.IsNil() ? Fix0 : itofix(y));
|
||||
return C4Void();
|
||||
}
|
||||
|
||||
static C4Void FnSetBack(C4AulContext* Context, C4Object* obj, Nillable<int> x, Nillable<int> y)
|
||||
{
|
||||
static_cast<C4Rope*>(Context->Def)->SetBack(obj, x.IsNil() ? Fix0 : itofix(x), y.IsNil() ? Fix0 : itofix(y));
|
||||
return C4Void();
|
||||
}
|
||||
|
||||
static C4Void FnSetFrontAutoSegmentation(C4AulContext* Context, int max)
|
||||
{
|
||||
static_cast<C4Rope*>(Context->Def)->SetFrontAutoSegmentation(itofix(max));
|
||||
return C4Void();
|
||||
}
|
||||
|
||||
static C4Void FnSetBackAutoSegmentation(C4AulContext* Context, int max)
|
||||
{
|
||||
static_cast<C4Rope*>(Context->Def)->SetBackAutoSegmentation(itofix(max));
|
||||
return C4Void();
|
||||
}
|
||||
|
||||
static C4Void FnSetFrontFixed(C4AulContext* Context, bool fixed)
|
||||
{
|
||||
static_cast<C4Rope*>(Context->Def)->SetFrontFixed(fixed);
|
||||
return C4Void();
|
||||
}
|
||||
|
||||
static C4Void FnSetBackFixed(C4AulContext* Context, bool fixed)
|
||||
{
|
||||
static_cast<C4Rope*>(Context->Def)->SetBackFixed(fixed);
|
||||
return C4Void();
|
||||
}
|
||||
|
||||
C4RopeAul::C4RopeAul():
|
||||
RopeDef(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
C4RopeAul::~C4RopeAul()
|
||||
{
|
||||
delete RopeDef;
|
||||
}
|
||||
|
||||
void C4RopeAul::InitFunctionMap(C4AulScriptEngine* pEngine)
|
||||
{
|
||||
delete RopeDef;
|
||||
RopeDef = C4PropList::NewScen();
|
||||
RopeDef->SetName("C4Rope");
|
||||
|
||||
Reg2List(pEngine);
|
||||
|
||||
::AddFunc(this, "Remove", FnRemove);
|
||||
::AddFunc(this, "GetFront", FnGetFront);
|
||||
::AddFunc(this, "GetBack", FnGetBack);
|
||||
::AddFunc(this, "SetFront", FnSetFront);
|
||||
::AddFunc(this, "SetBack", FnSetBack);
|
||||
::AddFunc(this, "SetFrontAutoSegmentation", FnSetFrontAutoSegmentation);
|
||||
::AddFunc(this, "SetBackAutoSegmentation", FnSetBackAutoSegmentation);
|
||||
::AddFunc(this, "SetFrontFixed", FnSetFrontFixed);
|
||||
::AddFunc(this, "SetBackFixed", FnSetBackFixed);
|
||||
RopeDef->Freeze();
|
||||
}
|
Loading…
Reference in New Issue