2013年5月20日星期一

About Bezier Curve.


In my project I need a curve to follow. I created a bezier curve in blend file. But I found that the gkCurve don't support bezier curve. So I do a little work to make class gkCurve to support bezier.

First, in gkCurveProperties of gkSerialize.h:

class gkCurveProperties
{
public:
enum CurveType
{
CU_Bezier,
CU_Spline,
CU_Points
};

public:
gkCurveProperties() :m_type(CU_Points), m_isCyclic(false)
{

}

CurveType m_type;

utArray<gkVector3> m_points;
typedef utArray<utArray<gkVector3> > BezTriples;
BezTriples m_BezTriples;
bool m_isCyclic;
};

Second, in function gkBlenderSceneConverter::convertObjectCurve of gkBlenderSceneConverter:

void gkBlenderSceneConverter::convertObjectCurve(gkGameObject* gobj, Blender::Object* bobj)
{
GK_ASSERT(gobj->getType() == GK_CURVE && bobj->data);

gkCurve* obj = static_cast<gkCurve*>(gobj);

gkCurveProperties& props = obj->getCurveProperties();
Blender::Curve* curve = static_cast<Blender::Curve*>(bobj->data);
Blender::Nurb* nurb = (Blender::Nurb* )curve->nurb.first;
// if (nurb->type != CU_NURBS) {
// gkLogger::write("Only Nurb-Curves are supported, atm! ("+obj->getName()+")");
// return;
// }
props.m_isCyclic = (nurb->flagu & CU_CYCLIC) || (nurb->flagv & CU_CYCLIC);

switch (nurb->type)
{
case CU_NURBS:
{
for (int i=0;i<nurb->pntsu;i++){
short int nurbType = nurb->type;

const Blender::BPoint& pnt = nurb->bp[i];
// TODO: check if these are converted properly
gkVector3 point(pnt.vec[0],pnt.vec[1],pnt.vec[2]);
props.m_points.push_back(point);
}
break;
}
case CU_BEZIER:
{
for (int i = 0; i < nurb->pntsu; i++)
{
const Blender::BezTriple& trp = nurb->bezt[i];
utArray<gkVector3> bezTriple;
for (int j = 0; j < 3; j++)
{
gkVector3 bt;
bt.x = trp.vec[j][0];
bt.y = trp.vec[j][1];
bt.z = trp.vec[j][2];
bezTriple.push_back(bt);
printf("%f %f %f ", bt.x, bt.y, bt.z);
}
props.m_BezTriples.push_back(bezTriple);
}
printf("\n");
props.m_type = gkCurveProperties::CU_Bezier;
break;
}
default:
gkLogger::write("Only Nurb-Curves and Bezier-Curves are supported, atm! ("+obj->getName()+")");
return;
}
}

Third, in gkCurve.h:

private:
gkGameObject* clone(const gkString& name);
void makeBezier(gkScalar _t, utArray<gkVector3>& _first, utArray<gkVector3>& _second);

gkCurveProperties m_curveProps;

virtual void createInstanceImpl(void);
virtual void destroyInstanceImpl(void);
utArray<gkVector3> m_bezierPoints;

Fourth, in gkCurve.cpp I rewiter the follow functions:

const gkVector3 gkCurve::getPoint(int nr)
{
switch (m_curveProps.m_type)
{
case gkCurveProperties::CU_Points:
{
if (nr < (int)m_curveProps.m_points.size()) {
return getWorldTransform() * m_curveProps.m_points.at(nr);
}
else
{
// TODO: better error handling
return gkVector3::ZERO;
}
}
case gkCurveProperties::CU_Bezier:
{
if (nr < (int)m_bezierPoints.size()) {
return getWorldTransform() * m_bezierPoints.at(nr);
}
else
{
// TODO: better error handling
return gkVector3::ZERO;
}
}
}
}

int gkCurve::getPointCount()
{
switch (m_curveProps.m_type)
{
case gkCurveProperties::CU_Points:
return m_curveProps.m_points.size();
case gkCurveProperties::CU_Bezier:
return m_bezierPoints.size();
}
}

void gkCurve::generateBezierPoints(gkScalar _t)
{
m_bezierPoints.clear();
UTsize triCount = m_curveProps.m_BezTriples.size();
for (UTsize i = 0; i < triCount - 1; i++)
{
utArray<gkVector3>& bezFirst = m_curveProps.m_BezTriples.at(i);
utArray<gkVector3>& bezSecond = m_curveProps.m_BezTriples.at(i + 1);
makeBezier(_t, bezFirst, bezSecond);
}
if (m_curveProps.m_isCyclic)
{
utArray<gkVector3>& bezFirst = m_curveProps.m_BezTriples.at(triCount - 1);
utArray<gkVector3>& bezSecond = m_curveProps.m_BezTriples.at(0);
makeBezier(_t, bezFirst, bezSecond);
}
}

void gkCurve::makeBezier(gkScalar _t, utArray< gkVector3 >& _first, utArray< gkVector3 >& _second)
{
gkVector3& pt0 = _first.at(1);
gkVector3& pt1 = _first.at(2);
gkVector3& pt2 = _second.at(0);
gkVector3& pt3 = _second.at(1);
for (gkScalar t = 0.f; t < 1.f; t += _t)
{
gkScalar yt = 1 - t;
gkScalar t1 = yt * yt;
gkScalar t2 = 3 * yt * t;
gkVector3 pt = pt0 * t1 * yt + pt1 * t2 * yt + pt2 * t2 * t + pt3 * t * t * t;
m_bezierPoints.push_back(pt);
}
}

void gkCurve::showDebug()
{
if (m_scene)
{
gkDebugger* debug = m_scene->getDebugger();
if (!debug)
{
return;
}
unsigned int n = getPointCount();

if (n)
{
static const gkVector3 RED_COLOR(0.8f, 0.5f, 0);

gkVector3 oldPoint = getPoint(0);

for (unsigned int i = 1; i < n; i++)
{
gkVector3 point = getPoint(i);

debug->drawLine(
oldPoint,
point,
RED_COLOR
);

oldPoint = point;
}
}
}
}

In my code. I just use gkCurve like:

gkCurve* m_pathCurve = m_scene->getObject("PATH_CURVE”)->getCurve();
m_pathCurve->generateBezierPoints(0.05f);

Then you can use m_pathCurve->getPointCount() and m_pathCurve->getPoint(n) to manipulate the bezier.

Follow is a snapshot of blend file:


Follow is a snapshot of program: