Visualization Library v1.0.3

A lightweight C++ OpenGL middleware for 2D/3D graphics

VL     Star     Watch     Fork     Issue

[Download] [Tutorials] [All Classes] [Grouped Classes]

Geometry LOD and Animation

In this tutorial you will learn how to define a set of LOD (level of detail) geometries using the vl::DistanceLODEvaluator class and how to animate your geometry using the vl::ActorEventCallback class.

pagGeomLodAnim.jpg

This demo creates a scene containing a central animated wave geometry and a few objects surrounding it in a circle. The central wave geometry defines 3 level of detail which will be animated in realtime using a subclass of vl::ActorEventCallback created ad-hoc. The surrounding objects are there to demonstrate even more clearly how to use geomety LOD. Each objects is rendered as a sphere at LOD level #0, as a cube at LOD level #1 and as a wireframe pyramid at LOD level #2. Note that the demo also uses the same LOD evaluator to perform Effect lod, demonstrating how simple it is to synchronize geometry and effect LOD.

[From App_GeomLODAnim.cpp]

#include "BaseDemo.hpp"
#include <vlCore/Colors.hpp>
#include <vlGraphics/GeometryPrimitives.hpp>
#include <vlGraphics/DistanceLODEvaluator.hpp>
#include <vlGraphics/PixelLODEvaluator.hpp>
#include <vlGraphics/Light.hpp>

/*
Implements an animated wave geometry.
*/
class WaveActorAnimator: public vl::ActorEventCallback
{
public:
  // initialize the 3 gometry LODs and installs them into the specified Actor
  WaveActorAnimator(vl::Actor* actor)
  {
    mLastUpdate = 0;
    mLastUpdatedLod = NULL;

    vl::ref<vl::Geometry> geom;
    const float side = 40;
    const int detail = 60;

    // LOD 0
    geom = vl::makeGrid( vl::vec3(0,0,0), side, side, detail, detail );
    // geom->setColor(vl::royalblue);
    actor->setLod(0, geom.get());

    geom->setBufferObjectEnabled(true);
    if (vl::Has_GL_ARB_vertex_buffer_object)
    {
      geom->vertexArray()->bufferObject()->setBufferData(vl::BU_DYNAMIC_DRAW,false);
    }

    // LOD 1
    geom = vl::makeGrid( vl::vec3(0,0,0), side, side, detail/2, detail/2 );
    // geom->setColor(vl::green);
    actor->setLod(1, geom.get());

    geom->setBufferObjectEnabled(true);
    if (vl::Has_GL_ARB_vertex_buffer_object)
    {
      geom->vertexArray()->bufferObject()->setBufferData(vl::BU_DYNAMIC_DRAW,false);
    }

    // LOD 2
    geom = vl::makeGrid( vl::vec3(0,0,0), side, side, detail/4, detail/4 );
    // geom->setColor(vl::yellow);
    actor->setLod(2, geom.get());

    geom->setBufferObjectEnabled(true);
    if (vl::Has_GL_ARB_vertex_buffer_object)
    {
      geom->vertexArray()->bufferObject()->setBufferData(vl::BU_DYNAMIC_DRAW,false);
    }
  }

  // respond to onActorRenderStarted() event by animating the wave
  virtual void onActorRenderStarted(vl::Actor*, vl::real frame_clock, const vl::Camera*, vl::Renderable* renderable, const vl::Shader*, int pass)
  {
    /* the beauty of this function is that in a few lines of code we update 3 different LOD levels!  */

    // update the geometry only the first time it is drawn
    if (pass > 0)
      return;

    // clamp animation to 30 FPS
    const vl::real fps = 30.0f;

    if ( frame_clock - mLastUpdate > 1.0f/fps || mLastUpdatedLod != renderable )
    {
      mLastUpdate = frame_clock;
      mLastUpdatedLod = renderable;

      // note: this returns the current LOD geometry
      vl::ref<vl::Geometry> geom = vl::cast<vl::Geometry>( renderable );
      vl::ref<vl::ArrayFloat3> vecarr3 = vl::cast<vl::ArrayFloat3>( geom->vertexArray() );
      vl::fvec3* vec = vecarr3->begin();
      vl::vec3 center = renderable->boundingBox().center();

      float phi = 0.5;
      float theta = 1.5;
      for(size_t i=0; i<vecarr3->size(); ++i)
      {
        // flatten to xz plane and compute distance
        vec[i].y() = 0;
        vl::real d = (vl::vec3(vec[i])-center).length();
        vec[i].y() = (float)cos( -frame_clock * vl::fPi * theta + d * phi ) * 2.0f;
      }

      if (vl::Has_GL_ARB_vertex_buffer_object)
      {
        geom->vertexArray()->bufferObject()->setBufferData(vl::BU_DYNAMIC_DRAW, false);
      }

      // when modifying the vertices of a geometry always remember to update the bounding volumes!
      geom->setBoundsDirty(true);
    }
  }

  // don't respond to onActorDelete() event.
  virtual void onActorDelete(vl::Actor*) { }

protected:
  vl::real mLastUpdate;
  vl::Renderable* mLastUpdatedLod;
};

/*
This test shows how to enable shader-lod, geometry-lod and how to animate a simple wave geometry over different LODs.
*/
class App_GeomLODAnim: public BaseDemo
{
public:
  void initEvent()
  {
    vl::Log::notify(appletInfo());

    /* configure how many objects are there forming the ring */
    const int ring_obj_count = 20;

    /* define a LOD evaluator with 3 disance ranges: [0] --- 70 --- 150 --- [inf] */
    vl::ref<vl::DistanceLODEvaluator> lod_eval = new vl::DistanceLODEvaluator;
    lod_eval->distanceRangeSet().push_back(70);
    lod_eval->distanceRangeSet().push_back(150);

    /* to be used later */
    vl::ref<vl::Light> light = new vl::Light;
    vl::ref<vl::Shader> wire_sh = new vl::Shader;
    vl::ref<vl::Shader> fill_sh = new vl::Shader;
    vl::ref<vl::Shader> wave_sh = new vl::Shader;

    /* fill pass */
    fill_sh->enable(vl::EN_DEPTH_TEST);
    fill_sh->enable(vl::EN_CULL_FACE);
    fill_sh->enable(vl::EN_LIGHTING);
    fill_sh->gocMaterial()->setFrontDiffuse( vl::white );
    fill_sh->gocPolygonMode()->set(vl::PM_FILL, vl::PM_FILL); // note this is default
    fill_sh->setRenderState( light.get(), 0 );

    /* wire pass */
    wire_sh->enable(vl::EN_DEPTH_TEST);
    wire_sh->enable(vl::EN_LIGHTING);
    wire_sh->enable(vl::EN_BLEND); // for line smoothing
    wire_sh->enable(vl::EN_LINE_SMOOTH);
    wire_sh->enable(vl::EN_POLYGON_OFFSET_LINE);
    wire_sh->gocHint()->setLineSmoothHint(vl::HM_NICEST);
    wire_sh->gocMaterial()->setFlatColor( vl::royalblue );
    wire_sh->gocPolygonMode()->set(vl::PM_LINE, vl::PM_LINE);
    wire_sh->gocPolygonOffset()->set(-1.0f, -1.0f);
    wire_sh->setRenderState( light.get(), 0 );

    /* wave pass */
    wave_sh->enable(vl::EN_BLEND); // for line smoothing
    wave_sh->enable(vl::EN_LINE_SMOOTH);
    wave_sh->gocHint()->setLineSmoothHint(vl::HM_NICEST);
    wave_sh->gocPolygonMode()->set(vl::PM_LINE, vl::PM_LINE);

    /* animated wave actor has a single Effect LOD */
    vl::ref<vl::Effect> wave_fx = new vl::Effect;
    wave_fx->setLOD( 0, wave_sh.get() );

    /* add wave actor */
    vl::ref<vl::Actor> wave_act = sceneManager()->tree()->addActor(NULL, wave_fx.get(), NULL);
    /* install actor animation callback. */
    wave_act->actorEventCallbacks()->push_back( new WaveActorAnimator(wave_act.get()) );
    /* install the LOD evaluator */
    wave_act->setLODEvaluator(lod_eval.get());

    /* effect used by the objects forming a ring around the central wave */
    vl::ref<vl::Effect> ring_fx = new vl::Effect;
    ring_fx->setLOD( 0, fill_sh.get(), wire_sh.get() ); // LOD 0: pass #1 = fill_sh + pass #2 = wire_sh
    ring_fx->setLOD( 1, fill_sh.get() ); // LOD 1: pass #1 = fill_sh
    ring_fx->setLOD( 2, wire_sh.get() ); // LOD 2: pass #1 = wire_sh
    
    /* install the LOD evaluator to be used with the ring objects */
    ring_fx->setLODEvaluator(lod_eval.get());

    /* ring element lod #0 */
    vl::ref<vl::Geometry> geom_0 = vl::makeIcosphere(vl::vec3(0,0,0),10,1);
    geom_0->computeNormals();

    /* ring element lod #1 */
    vl::ref<vl::Geometry> geom_1 = vl::makeBox(vl::vec3(0,0,0),6,6,6);
    geom_1->computeNormals();

    /* ring element lod #2 */
    vl::ref<vl::Geometry> geom_2 = vl::makePyramid(vl::vec3(0,0,0),6,6);
    geom_2->computeNormals();

    /* generate the ring of objects */
    for(int i=0;i<ring_obj_count; i++)
    {
      /* define actor position and add it to the scene */
      vl::ref<vl::Transform> tr = new vl::Transform;
      vl::real t = 360.0f / ring_obj_count * i;
      vl::vec3 v = vl::mat4::getRotation(t,0,1,0) * vl::vec3(35,0,0);
      tr->setLocalMatrix( vl::mat4::getTranslation(v) );

      rendering()->as<vl::Rendering>()->transform()->addChild(tr.get());
      
      vl::ref<vl::Actor> act = sceneManager()->tree()->addActor( NULL, ring_fx.get(), tr.get() );

      /* define which geometry to use for each LOD */
      act->setLod(0, geom_0.get());
      act->setLod(1, geom_1.get());
      act->setLod(2, geom_2.get());

      /* install the LOD evaluator*/
      act->setLODEvaluator(lod_eval.get());
    }
  }

  void updateScene()
  {
    /* animate the camera to rotate around the scene and bounce near/far */
    float s = sin( vl::Time::currentTime() * vl::fPi * 2.0f / 10.0f );
    vl::real t = pow((s+1.0f)/2.0f,2);
    vl::real x = t * 200 + 5;
    vl::vec3 eye( x, 0, 0 );
    eye = vl::mat4::getRotation( vl::Time::currentTime() * 30.0f, 0, 1, 0 ) * eye;
    eye += vl::vec3(0,10+70*t,0);
    vl::mat4 m;
    m = vl::mat4::getLookAt( eye, vl::vec3(0,0,0), vl::vec3(0,1,0) );
    rendering()->as<vl::Rendering>()->camera()->setViewMatrix(m);
  }
};

// Have fun!


Visualization Library v1.0.3 Reference Documentation
Copyright Michele Bosi. All rights reserved.
Updated on Tue Feb 7 2017 00:55:04.
Permission is granted to use this page to write and publish articles regarding Visualization Library.