In the following tutorial you will learn how to manage and generate bicubic Bezier surfaces and how to concatenate multiple patches using the vl::BezierPatch and vl::BezierSurface classes.
As you will se the actual code dealing with the Bezier patches is less than ten lines of code! The majority of the code is dedicated to generate the patch data, to setup the effects, to manage the user input and so on.
class App_BezierSurfaces: public BaseDemo
{
public:
App_BezierSurfaces(): mDetail(10) {}
virtual String appletInfo()
{
return BaseDemo::appletInfo() +
"- Up/Down Arrow = increase/decrease the Bezier surface tessellation detail.\n" +
"- Space = toggle control points visibility.\n" +
"\n";
}
void initEvent()
{
ref<Effect> fx = new Effect;
fx->shader()->gocLightModel()->setTwoSide(true);
fx->shader()->gocLight(0)->setLinearAttenuation(0.025f);
fx->shader()->gocMaterial()->setDiffuse(royalblue);
#if defined(VL_OPENGL)
fx->lod(0)->push_back( new Shader);
fx->shader(0,1)->gocPolygonOffset()->set(-1.0f, -1.0f);
fx->shader(0,1)->gocDepthMask()->set(false);
fx->shader(0,1)->gocColor()->setValue(gold);
#endif
std::string teapot_txt = String::loadText("models/newell_teaset/teapot").toStdString();
std::stringstream sstr(teapot_txt);
int patchnum = 0;
sstr >> patchnum;
std::vector<int> patch_idx;
for(int i=0; i<patchnum; ++i)
{
for(int j=0; j<16; j++)
{
int p = 0;
sstr >> p;
patch_idx.push_back(p);
}
}
int vertnum;
sstr >> vertnum;
std::vector<vec3> verts;
verts.resize(vertnum);
for(int i=0; i<vertnum; ++i)
{
float x, y, z;
sstr >> x >> y >> z;
verts[i] =
vec3(x, z, -y);
}
mBezier = new BezierSurface;
for(int i=0; i<patchnum; ++i)
{
ref<BezierPatch> patch = new BezierPatch(4,4);
for(int j=0; j<16; ++j)
{
int idx = patch_idx[j+16*i]-1;
patch->points()[j] = verts[ idx ];
}
mBezier->patches().push_back(patch.get());
}
mBezier->setDetail(mDetail);
mBezier->updateBezierSurface(false);
mBezier->computeNormals();
sceneManager()->tree()->addActor(mBezier.get(), fx.get(),
NULL);
#if defined(VL_OPENGL)
showPatchControlPoints(mBezier.get());
#endif
trackball()->adjustView( sceneManager(),
vec3(0,0,1),
vec3(0,1,0), 1.0f );
}
void keyPressEvent(
unsigned short ch,
EKey key)
{
BaseDemo::keyPressEvent(ch,key);
{
++mDetail;
mDetail =
clamp(mDetail, 2, 64);
mBezier->setDetail(mDetail);
mBezier->updateBezierSurface(false);
mBezier->computeNormals();
mBezier->setBufferObjectDirty(true);
}
else
{
--mDetail;
mDetail =
clamp(mDetail, 2, 64);
mBezier->setDetail(mDetail);
mBezier->updateBezierSurface(false);
mBezier->computeNormals();
mBezier->setBufferObjectDirty(true);
}
else
{
if (sceneManager()->tree()->actors()->find(mCtrlPoints_Actor.get()) == Collection<void>::not_found)
sceneManager()->tree()->actors()->push_back(mCtrlPoints_Actor.get());
else
sceneManager()->tree()->actors()->erase(mCtrlPoints_Actor.get());
}
}
void showPatchControlPoints(BezierSurface* bezier)
{
ref<Effect> fx = new Effect;
fx->shader()->gocLineWidth()->set(1.0f);
fx->shader()->gocPointSize()->set(5.0f);
ref<Geometry> geom = new Geometry;
int istart = 0;
std::vector<fvec3> verts;
std::vector<fvec4> colos;
for(unsigned ipatch=0; ipatch<bezier->patches().size(); ++ipatch)
{
const BezierPatch* p = bezier->patches()[ipatch].get();
for(int ix=0; ix<p->x()-3; ix+=3)
for(int iy=0; iy<p->y()-3; iy+=3, istart+=16)
{
verts.push_back((
fvec3)p->at(ix+0,iy+0)); colos.push_back(red);
verts.push_back((
fvec3)p->at(ix+0,iy+1)); colos.push_back(white);
verts.push_back((
fvec3)p->at(ix+0,iy+2)); colos.push_back(white);
verts.push_back((
fvec3)p->at(ix+0,iy+3)); colos.push_back(red);
verts.push_back((
fvec3)p->at(ix+1,iy+0)); colos.push_back(white);
verts.push_back((
fvec3)p->at(ix+1,iy+1)); colos.push_back(white);
verts.push_back((
fvec3)p->at(ix+1,iy+2)); colos.push_back(white);
verts.push_back((
fvec3)p->at(ix+1,iy+3)); colos.push_back(white);
verts.push_back((
fvec3)p->at(ix+2,iy+0)); colos.push_back(white);
verts.push_back((
fvec3)p->at(ix+2,iy+1)); colos.push_back(white);
verts.push_back((
fvec3)p->at(ix+2,iy+2)); colos.push_back(white);
verts.push_back((
fvec3)p->at(ix+2,iy+3)); colos.push_back(white);
verts.push_back((
fvec3)p->at(ix+3,iy+0)); colos.push_back(red);
verts.push_back((
fvec3)p->at(ix+3,iy+1)); colos.push_back(white);
verts.push_back((
fvec3)p->at(ix+3,iy+2)); colos.push_back(white);
verts.push_back((
fvec3)p->at(ix+3,iy+3)); colos.push_back(red);
ref<DrawArrays> da =
new DrawArrays(
PT_POINTS, istart,16);
ref<DrawElementsUInt> de =
new DrawElementsUInt(
PT_QUADS);
de->indexBuffer()->resize(4*9);
unsigned int quads[] = { 0,1,5,4, 4,5,9,8, 8,9,13,12, 1,2,6,5, 5,6,10,9, 9,10,14,13, 2,3,7,6, 6,7,11,10, 10,11,15,14 };
for(int q=0; q<4*9; ++q)
quads[q] += istart;
memcpy(de->indexBuffer()->ptr(), quads, sizeof(quads));
geom->drawCalls().push_back(de.get());
geom->drawCalls().push_back(da.get());
}
}
ref<ArrayFloat3> vert_array = new ArrayFloat3;
geom->setVertexArray(vert_array.get());
vert_array->initFrom(verts);
ref<ArrayFloat4> cols_array = new ArrayFloat4;
geom->setColorArray(cols_array.get());
cols_array->initFrom(colos);
geom->makeGLESFriendly();
mCtrlPoints_Actor = sceneManager()->tree()->addActor(geom.get(), fx.get(),
NULL);
}
protected:
ref<BezierSurface> mBezier;
ref<Actor> mCtrlPoints_Actor;
int mDetail;
};