示例#1
0
/*
  call-seq: new(drawable)
            new(buffer)
            new(w, h)
            new(w, h, pixel_format)

  A buffer can be created from a drawable or from another buffer. It'll
  simply contain the Drawable (after the creation, the two objects aren't
  related at all).

  Another way is to specify the buffer's dimension, and at your option, the
  pixel format (by default, 16 bits colors are used).
*/
VALUE wrap<Buffer>(int argc, VALUE *argv, VALUE info)
{
   VALUE arg1, arg2, arg3;
   rb_scan_args(argc, argv, "12", &arg1, &arg2, &arg3);

   Buffer *ptr = NULL;

   try {
      if (NIL_P(arg2)) {
	 if (rb_obj_is_kind_of(arg1, getClass("Drawable")) == Qtrue) {
	    RubyDrawable drawable(arg1);
	    ptr = new Buffer(drawable);
	 }
	 else if (rb_obj_is_kind_of(arg1, getClass("Buffer")) == Qtrue) {
	    ptr = new Buffer(getRef<Buffer>(arg1));
	 }
	 else {
	    rb_raise(rb_eTypeError, "Buffer or Drawable expected.");
	 }
      }
      else {
	 if (NIL_P(arg2))
	    rb_raise(rb_eArgError, "Another argument was expected.");
	 if (NIL_P(arg3))
	    arg3 = INT2FIX(OSL_PF_5650);
	 ptr = new Buffer(FIX2INT(arg1), FIX2INT(arg2), FIX2INT(arg3));
      }
   }
   catch (const RubyException &e) {
      e.rbRaise();
   }
   
   VALUE tdata = Data_Wrap_Struct(info, 0, wrapped_free<Buffer>, ptr);
   return tdata;
}
示例#2
0
/*
  call-seq: new(drawable)
            new(buffer)
            new(w, h)
            new(w, h, pixel_format)

  A buffer can be created from a drawable or from another buffer. It'll
  simply contain the Drawable (after the creation, the two objects aren't
  related at all).

  Another way is to specify the buffer's dimension, and at your option, the
  pixel format (by default, 16 bits colors are used).
*/
VALUE Buffer_initialize(int argc, VALUE *argv, VALUE self)
{
   VALUE arg1, arg2, arg3;
   rb_scan_args(argc, argv, "12", &arg1, &arg2, &arg3);

   Buffer &ref = getRef<Buffer>(self);

   try {
      if (NIL_P(arg2)) {
	 if (rb_obj_is_kind_of(arg1, getClass("Drawable")) == Qtrue) {
	    RubyDrawable drawable(arg1);
	    ref.createFromDrawable(drawable);
	 }
	 else if (rb_obj_is_kind_of(arg1, getClass("Buffer")) == Qtrue) {
	    ref.createFromBuffer(getRef<Buffer>(arg1));
	 }
	 else {
	    rb_raise(rb_eTypeError, "Buffer or Drawable expected.");
	 }
      }
      else {
	 if (NIL_P(arg2))
	    rb_raise(rb_eArgError, "Another argument was expected.");
	 if (NIL_P(arg3))
	    arg3 = INT2FIX(OSL_PF_5650);
      
	 ref.createFromGeom(FIX2INT(arg1), FIX2INT(arg2), FIX2INT(arg3));
      }
   }
   catch (const RubyException &e) {
      e.rbRaise();
   }
   
   return Qnil;
}
示例#3
0
文件: Box.cpp 项目: pontusdacke/Hoggy
Box::Box(b2World& world, float x, float y)
{
	std::shared_ptr<DrawableComponent> drawable(new DrawableComponent(this, "box.png"));
	components.push_back(drawable);

	std::shared_ptr<PhysicsComponent> physics(new PhysicsComponent(this, world, x, y));
	components.push_back(physics);
}
示例#4
0
void CPcbSegment::paint(QPainter* painter, const QStyleOptionGraphicsItem* /* option */, QWidget* /* widget */)
{
	if ( drawable() )
	{
		painter->setRenderHint(QPainter::Antialiasing);
		painter->scale(scale(),scale());
		painter->setPen(QPen(color(), width(), Qt::SolidLine,Qt::RoundCap));
		painter->drawPath(shape());
	}
}
示例#5
0
文件: model.cpp 项目: fabr1z10/glib
//! ignoring anim and frame
void StaticModel::draw() {
    if (!drawable())
        return;
    Shader* shader = App::getCurrentscene()->getCurrentShader();
    for (std::vector<std::shared_ptr<Mesh> >::iterator iter = m_meshes.begin(); iter != m_meshes.end(); iter++)
    {
        Mesh* currentMesh = iter->get();
        shader->draw(currentMesh, currentMesh->getNumberOfIndices(), 0);
    }

}
示例#6
0
/*
  Converts the drawable in a Buffer.
*/
VALUE Drawable_to_buf(VALUE self) {
   RubyDrawable drawable(self);
   try {
      return Data_Wrap_Struct(getClass("Buffer"), 0, wrapped_free<Buffer>,
			      new Buffer(drawable));
   }
   catch (const RubyException &e) {
      e.rbRaise();
      return Qnil;
   }
}
示例#7
0
void CPcbPlane::paint(QPainter* painter, const QStyleOptionGraphicsItem* /* option */, QWidget* /* widget */)
{
	if ( drawable() )
	{
		QPainterPath ppath = shape();
		painter->setRenderHint(QPainter::Antialiasing);
		painter->scale(scale(),scale());
		ppath.setFillRule(Qt::WindingFill);
		painter->setPen(QPen(color(), width(), Qt::SolidLine,Qt::RoundCap));
		painter->drawPath(ppath);
		painter->fillPath(ppath,color());
	}
}
示例#8
0
void Geometry::labels()
{
   //Print labels
   glColor3f(0,0,0);
   lucSetFontCharset(FONT_SMALL); //Bitmap fonts
   for (unsigned int i=0; i < geom.size(); i++)
   {
      if (drawable(i) && geom[i]->labels.size() > 0) 
      {
         for (unsigned int j=0; j < geom[i]->labels.size(); j++)
         {
            float* p = geom[i]->vertices[j];
            if (geom[i]->labels[j].size() > 0)
               lucPrint3d(p[0], p[1], p[2], geom[i]->labels[j].c_str());
         }
      }
   }
}
    static sk_sp<SkFlattenable> CreateProc(SkReadBuffer& buffer) {
        sk_sp<SkFlattenable> compoundDrawable(
                buffer.readFlattenable(SkFlattenable::kSkDrawable_Type));
        SkASSERT(compoundDrawable);
        SkASSERT(!strcmp("CompoundDrawable", compoundDrawable->getTypeName()));

        sk_sp<SkFlattenable> intDrawable(
                buffer.readFlattenable(SkFlattenable::kSkDrawable_Type));
        SkASSERT(intDrawable);
        SkASSERT(!strcmp("IntDrawable", intDrawable->getTypeName()));

        sk_sp<SkFlattenable> drawable(
                buffer.readFlattenable(SkFlattenable::kSkDrawable_Type));
        SkASSERT(drawable);

        return sk_sp<RootDrawable>(new RootDrawable((CompoundDrawable*) compoundDrawable.get(),
                                                    (IntDrawable*) intDrawable.get(),
                                                    (SkDrawable*) drawable.get()));
    }
DEF_TEST(FlattenDrawable, r) {
    // Create and serialize the test drawable
    sk_sp<SkDrawable> drawable(new IntDrawable(1, 2, 3, 4));
    SkPaint paint;
    paint.setColor(SK_ColorBLUE);
    sk_sp<RootDrawable> root(new RootDrawable(5, 6, 7, 8, paint, 9, 10, 11, 12, drawable.get()));
    SkBinaryWriteBuffer writeBuffer;
    writeBuffer.writeFlattenable(root.get());

    // Copy the contents of the write buffer into a read buffer
    sk_sp<SkData> data = SkData::MakeUninitialized(writeBuffer.bytesWritten());
    writeBuffer.writeToMemory(data->writable_data());
    SkReadBuffer readBuffer(data->data(), data->size());
    register_test_drawables(readBuffer);

    // Deserialize and verify the drawable
    sk_sp<SkDrawable> out((SkDrawable*)readBuffer.readFlattenable(SkFlattenable::kSkDrawable_Type));
    REPORTER_ASSERT(r, out);
    REPORTER_ASSERT(r, !strcmp("RootDrawable", out->getTypeName()));

    RootDrawable* rootOut = (RootDrawable*) out.get();
    REPORTER_ASSERT(r, 5 == rootOut->compoundDrawable()->intDrawable()->a());
    REPORTER_ASSERT(r, 6 == rootOut->compoundDrawable()->intDrawable()->b());
    REPORTER_ASSERT(r, 7 == rootOut->compoundDrawable()->intDrawable()->c());
    REPORTER_ASSERT(r, 8 == rootOut->compoundDrawable()->intDrawable()->d());
    REPORTER_ASSERT(r, SK_ColorBLUE ==
            rootOut->compoundDrawable()->paintDrawable()->paint().getColor());
    REPORTER_ASSERT(r, 9 == rootOut->intDrawable()->a());
    REPORTER_ASSERT(r, 10 == rootOut->intDrawable()->b());
    REPORTER_ASSERT(r, 11 == rootOut->intDrawable()->c());
    REPORTER_ASSERT(r, 12 == rootOut->intDrawable()->d());

    // Note that we can still recognize the generic drawable as an IntDrawable
    SkDrawable* generic = rootOut->drawable();
    REPORTER_ASSERT(r, !strcmp("IntDrawable", generic->getTypeName()));
    IntDrawable* integer = (IntDrawable*) generic;
    REPORTER_ASSERT(r, 1 == integer->a());
    REPORTER_ASSERT(r, 2 == integer->b());
    REPORTER_ASSERT(r, 3 == integer->c());
    REPORTER_ASSERT(r, 4 == integer->d());
}
DEF_TEST(FlattenRecordedDrawable, r) {
    // Record a set of canvas draw commands
    SkPictureRecorder recorder;
    SkCanvas* canvas = recorder.beginRecording(1000.0f, 1000.0f);
    SkPaint paint;
    paint.setColor(SK_ColorGREEN);
    canvas->drawPoint(42.0f, 17.0f, paint);
    paint.setColor(SK_ColorRED);
    canvas->drawPaint(paint);
    SkPaint textPaint;
    textPaint.setColor(SK_ColorBLUE);
    canvas->drawString("TEXT", 467.0f, 100.0f, textPaint);

    // Draw some drawables as well
    sk_sp<SkDrawable> drawable(new IntDrawable(1, 2, 3, 4));
    sk_sp<RootDrawable> root(new RootDrawable(5, 6, 7, 8, paint, 9, 10, 11, 12, drawable.get()));
    canvas->drawDrawable(root.get(), 747.0f, 242.0f);
    sk_sp<PaintDrawable> paintDrawable(new PaintDrawable(paint));
    canvas->drawDrawable(paintDrawable.get(), 500.0, 500.0f);
    sk_sp<CompoundDrawable> comDrawable(new CompoundDrawable(13, 14, 15, 16, textPaint));
    canvas->drawDrawable(comDrawable.get(), 10.0f, 10.0f);

    // Serialize the recorded drawable
    sk_sp<SkDrawable> recordedDrawable = recorder.finishRecordingAsDrawable();
    SkBinaryWriteBuffer writeBuffer;
    writeBuffer.writeFlattenable(recordedDrawable.get());

    // Copy the contents of the write buffer into a read buffer
    sk_sp<SkData> data = SkData::MakeUninitialized(writeBuffer.bytesWritten());
    writeBuffer.writeToMemory(data->writable_data());
    SkReadBuffer readBuffer(data->data(), data->size());
    register_test_drawables(readBuffer);

    // Deserialize and verify the drawable
    sk_sp<SkDrawable> out((SkDrawable*)readBuffer.readFlattenable(SkFlattenable::kSkDrawable_Type));
    REPORTER_ASSERT(r, out);
    REPORTER_ASSERT(r, !strcmp("SkRecordedDrawable", out->getTypeName()));
}
ParticleSystems::ParticleSystems(const FileReader& reader) :
  m_systems(),
  m_drawables()
{
  std::string filename;
  Vector2f    pos;
  reader.get("name", filename);
  reader.get("pos",  pos);

  { // Load the ParticleSystems
    FileReader root_reader = FileReader::parse(Pathname(filename));
    if(root_reader.get_name() != "particle-systems") 
    {
      std::ostringstream msg;
      msg << "'" << filename << "' is not a particle-system file";
      throw std::runtime_error(msg.str());
    }

    std::vector<FileReader> sections = root_reader.get_sections();
    for(std::vector<FileReader>::iterator i = sections.begin(); i != sections.end(); ++i)
    { 
      if (i->get_name() == "particle-system")
      {
        boost::shared_ptr<ParticleSystem> particle_system(new ParticleSystem(*i));
        particle_system->set_pos(pos);
        m_systems.push_back(particle_system);
      }
    }
  }

  for(Systems::iterator i = m_systems.begin(); i != m_systems.end(); ++i)
  {
    boost::shared_ptr<ParticleSystemDrawable> drawable(new ParticleSystemDrawable(**i));

    m_drawables.push_back(drawable);
    Sector::current()->get_scene_graph().add_drawable(drawable);
  }
}
示例#13
0
/*
  call-seq: draw
            draw(object)

  If no arguments are given, the buffer is drawn. If either a buffer or
  a drawable is given, then it is drawn on the buffer.
*/
VALUE Buffer_draw(int argc, VALUE *argv, VALUE self) {
   Buffer &ref = getRef<Buffer>(self);

   VALUE arg;
   rb_scan_args(argc, argv, "01", &arg);

   if (NIL_P(arg)) // We'll render the buffer.
      ref.draw();
   else {
      if (rb_obj_is_kind_of(arg, getClass("Drawable")) == Qtrue) {
	 RubyDrawable drawable(arg);
	 ref.draw(drawable);
      }
      else if (rb_obj_is_kind_of(arg, getClass("Buffer")) == Qtrue) {
	 Buffer &obj = getRef<Buffer>(arg);
	 ref.draw(obj);
      }
      else {
	 rb_raise(rb_eTypeError, "Buffer or Drawable expected.");
      }
   }
   return Qnil;
}
示例#14
0
文件: Lines.cpp 项目: OKaluza/LavaVu
void Lines::draw()
{
  //Draw, calls update when required
  Geometry::draw();
  if (drawcount == 0) return;

  // Undo any scaling factor for arrow drawing...
  glPushMatrix();
  if (view->scale[0] != 1.0 || view->scale[1] != 1.0 || view->scale[2] != 1.0)
    glScalef(1.0/view->scale[0], 1.0/view->scale[1], 1.0/view->scale[2]);

  //Draw any 3d rendered tubes
  tris->draw();

  // Re-Apply scaling factors
  glPopMatrix();

  // Draw using vertex buffer object
  glPushAttrib(GL_ENABLE_BIT);
  clock_t t0 = clock();
  double time;
  int stride = 3 * sizeof(float) + sizeof(Colour);   //3+3+2 vertices, normals, texCoord + 32-bit colour
  int offset = 0;
  if (geom.size() > 0 && elements > 0 && glIsBuffer(vbo))
  {
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glVertexPointer(3, GL_FLOAT, stride, (GLvoid*)0); // Load vertex x,y,z only
    glColorPointer(4, GL_UNSIGNED_BYTE, stride, (GLvoid*)(3*sizeof(float)));   // Load rgba, offset 3 float
    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_COLOR_ARRAY);

    //Disable depth test on 2d models
    if (view->is3d)
      glEnable(GL_DEPTH_TEST);
    else
      glDisable(GL_DEPTH_TEST);

    for (unsigned int i=0; i<geom.size(); i++)
    {
      Properties& props = geom[i]->draw->properties;
      if (drawable(i) && props.getBool("flat", true) && !props["tubes"])
      {
        //Set draw state
        setState(i, drawstate.prog[lucLineType]);

        //Lines specific state
        float scaling = props["scalelines"];
        //Don't apply object scaling to internal lines objects
        if (!internal) scaling *= (float)props["scaling"];
        float lineWidth = (float)props["linewidth"] * scaling * view->scale2d; //Include 2d scale factor
        if (lineWidth <= 0) lineWidth = scaling;
        glLineWidth(lineWidth);

        if (props["link"])
          glDrawArrays(GL_LINE_STRIP, offset, counts[i]);
        else
          glDrawArrays(GL_LINES, offset, counts[i]);
      }

      offset += counts[i];
    }

    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_COLOR_ARRAY);
  }
  glBindBuffer(GL_ARRAY_BUFFER, 0);
  GL_Error_Check;

  //Restore state
  glPopAttrib();
  glBindTexture(GL_TEXTURE_2D, 0);

  time = ((clock()-t0)/(double)CLOCKS_PER_SEC);
  if (time > 0.05)
    debug_print("  %.4lf seconds to draw %d lines\n", time, offset);
  GL_Error_Check;
}
示例#15
0
inline void dump_png(const char * filename, const Bitmap & bmp)
{
    Drawable drawable(bmp.cx(), bmp.cy());
    dump_png(filename, drawable);
}
示例#16
0
void Shapes::update()
{
   Geometry::update();

   for (unsigned int i=0; i<geom.size(); i++) 
   {
      glNewList(displaylists[i], GL_COMPILE);
      if (!drawable(i)) {glEndList(); continue;}   ////

      //Calibrate colour map (not yet available)
      geom[i]->colourCalibrate();

      //Set draw state
      setState(i);

      float scaling = geom[i]->draw->scaling;

      //Load constant scaling factors from properties
      float dims[3];
      dims[0] = geom[i]->draw->props.Float("width", FLT_MIN);
      dims[1] = geom[i]->draw->props.Float("height", FLT_MIN);
      dims[2] = geom[i]->draw->props.Float("length", FLT_MIN);
      int shape = geom[i]->draw->props.Int("shape");

      if (scaling <= 0) scaling = 1.0;

      for (int v=0; v < geom[i]->count; v++) 
      {
         //Calculate colour
         geom[i]->setColour(v);

         //Scale the dimensions by variables (dynamic range options? by setting max/min?)
         float sdims[3] = {dims[0], dims[1], dims[2]};
         if (geom[i]->xWidths.size() > 0) sdims[0] = geom[i]->xWidths[v];
         if (geom[i]->yHeights.size() > 0) sdims[1] = geom[i]->yHeights[v]; else sdims[1] = sdims[0];
         if (geom[i]->zLengths.size() > 0) sdims[2] = geom[i]->zLengths[v]; else sdims[2] = sdims[1];

         //Multiply by constant scaling factors if present
         if (dims[0] != FLT_MIN) sdims[0] *= dims[0];
         if (dims[1] != FLT_MIN) sdims[1] *= dims[1];
         if (dims[2] != FLT_MIN) sdims[2] *= dims[2];

         //Apply scaling
         sdims[0] *= scaling * scale;
         sdims[1] *= scaling * scale;
         sdims[2] *= scaling * scale;

         float pos[3];
         memcpy(pos, geom[i]->vertices[v], 3 * sizeof(float));
         //Scale manually (as global scaling is disabled to avoid distorting glyphs)
         for (int d=0; d<3; d++)
            pos[d] *= view->scale[d];

         // Translate to centre position
         glPushMatrix();
         glTranslatef(pos[0], pos[1], pos[2]);

         //Orient view to the alignment vector
         if (geom[i]->vectors.size() > 0)
         {
            Vec3d vec(geom[i]->vectors[v]);
            vec *= Vec3d(view->scale); //Scale
            // Rotate to orient the cone
            //...Want to align our z-axis to point along arrow vector:
            // axis of rotation = (z x vec)
            // cosine of angle between vector and z-axis = (z . vec) / |z|.|vec| */
            vec.normalise();
            float rangle = RAD2DEG * vec.angle(Vec3d(0.0, 0.0, 1.0));
            //Axis of rotation = vec x [0,0,1] = -vec[1],vec[0],0
            glRotatef(rangle, -vec[1], vec[0], 0);
         }

         //Draw shape
         float zpos[3] = {0};
         if (shape == 1)
            //drawCuboid(min, max, true);
            drawCuboid(zpos, sdims[0], sdims[1], sdims[2], true);
         else
            drawEllipsoid(zpos, sdims[0], sdims[1], sdims[2], 24, NULL);

         //Restore model view
         glPopMatrix();
      }
      glEndList();
   }
}
示例#17
0
int main(int argc, char **argv)
{
    demoLoad(argc, argv);
    Window window = createWindow("Teapots");
    Graphics graphics(window);
    Graphics::Flags flags;
    if (!graphics.init(flags)) {
        return 1;
    }
    il_pos pos = il_pos_new(&graphics.space);
    il_pos_setPosition(&pos, il_vec3_new(0, -4, 0));
    Teapot teapot;
    Teapots drawable(pos.id, teapot);
    graphics.drawables.push_back(&drawable);

    char *error;
    if (!teapot.build(graphics.rm, &error)) {
        il_error("Teapot: %s", error);
        free(error);
        return 1;
    }

    il_pos_setPosition(&graphics.space.camera, il_vec3_new(0, 0, 20));

    il_pos lightp = il_pos_new(&graphics.space);
    il_pos_setPosition(&lightp, il_vec3_new(20, 3, 20));

    ilG_light lightl;
    lightl.color = il_vec3_new(.8f*2, .7f*2, .2f*2);
    lightl.radius = 50;

    State state;

    unsigned lightp_id = lightp.id;
    state.sunlight_locs = &lightp_id;
    state.sunlight_lights = &lightl;
    state.sunlight_count = 1;

    typedef std::chrono::steady_clock clock;
    typedef std::chrono::duration<double> duration;

    clock::time_point start = clock::now();
    while (1) {
        SDL_Event ev;
        while (SDL_PollEvent(&ev)) {
            switch (ev.type) {
            case SDL_QUIT:
                il_log("Stopping");
                window.close();
                return 0;
            }
        }

        duration delta = clock::now() - start;
        const double secs = 5.0;
        const int scale = 20;
        il_vec3 v;
        v.x = float(sin(delta.count() * M_PI * 2.0 / secs) * scale);
        v.y = 0.f;
        v.z = float(cos(delta.count() * M_PI * 2.0 / secs) * scale);
        il_quat q = il_quat_fromAxisAngle(0, 1, 0, float(delta.count() * M_PI * 2.0 / secs));
        il_pos_setPosition(&graphics.space.camera, v);
        il_pos_setRotation(&graphics.space.camera, q);

        graphics.draw(state);
    }
}
示例#18
0
void QuadSurfaces::update()
{
  clock_t t1,t2,tt;
  t1 = tt = clock();
  // Update and depth sort surfaces..
  //Calculate distances from view plane
  float maxdist, mindist;
  view->getMinMaxDistance(&mindist, &maxdist);

  Geometry::update();

  tt=clock();
  if (geom.size() == 0) return;

  //Get element/quad count
  debug_print("Reloading and sorting %d quad surfaces...\n", geom.size());
  total = 0;
  hiddencache.resize(geom.size());
  surf_sort.clear();
  int quadverts = 0;
  for (unsigned int i=0; i<geom.size(); i++)
  {
    int quads = (geom[i]->width-1) * (geom[i]->height-1);
    quadverts += quads * 4;
    total += geom[i]->count; //Actual vertices

    hiddencache[i] = !drawable(i); //Save flags
    debug_print("Surface %d, quads %d hidden? %s\n", i, quadverts/4, (hiddencache[i] ? "yes" : "no"));

    //Get corners of strip
    float* posmin = geom[i]->vertices[0];
    float* posmax = geom[i]->vertices[geom[i]->count - 1];
    float pos[3] = {posmin[0] + (posmax[0] - posmin[0]) * 0.5f,
                    posmin[1] + (posmax[1] - posmin[1]) * 0.5f,
                    posmin[2] + (posmax[2] - posmin[2]) * 0.5f
                   };

    //Calculate distance from viewing plane
    geom[i]->distance = eyeDistance(view->modelView, pos);
    if (geom[i]->distance < mindist) mindist = geom[i]->distance;
    if (geom[i]->distance > maxdist) maxdist = geom[i]->distance;
    //printf("%d)  %f %f %f distance = %f\n", i, pos[0], pos[1], pos[2], geom[i]->distance);
    surf_sort.push_back(Distance(i, geom[i]->distance));

    //Disable triangle sorting for these surfaces
    geom[i]->opaque = true;
  }
  if (total == 0) return;
  t2 = clock();
  debug_print("  %.4lf seconds to calculate distances\n", (t2-t1)/(double)CLOCKS_PER_SEC);
  t1 = clock();

  //Sort
  std::sort(surf_sort.begin(), surf_sort.end());
  t2 = clock();
  debug_print("  %.4lf seconds to sort\n", (t2-t1)/(double)CLOCKS_PER_SEC);
  t1 = clock();

  //Only reload the vbo data when required
  //Not needed when objects hidden/shown but required if colours changed
  //To force, use Geometry->reset() which sets elements to -1
  if (elements < 0 || elements != quadverts)
  {
    //Clear buffers
    close();
    elements = quadverts;
    //Load & optimise the mesh data
    render();
    //Send the data to the GPU via VBO
    loadBuffers();
  }
}