--- /dev/null
+#include <cmath>
+#include <msp/core/application.h>
+#include <msp/core/getopt.h>
+#include <msp/fs/utils.h>
+#include <msp/graphics/simplewindow.h>
+#include <msp/gl/blend.h>
+#include <msp/gl/camera.h>
+#include <msp/gl/framebuffer.h>
+#include <msp/gl/light.h>
+#include <msp/gl/lighting.h>
+#include <msp/gl/mesh.h>
+#include <msp/gl/object.h>
+#include <msp/gl/tests.h>
+#include <msp/input/mouse.h>
+#include <msp/io/print.h>
+
+using namespace std;
+using namespace Msp;
+
+class Viewer: public RegisteredApplication<Viewer>
+{
+private:
+ Graphics::SimpleGLWindow window;
+ Input::Mouse mouse;
+ GL::Object *object;
+ GL::Mesh *mesh;
+ GL::Light light;
+ GL::Lighting lighting;
+ GL::Camera camera;
+ float yaw;
+ float pitch;
+ float distance;
+ float light_yaw;
+ float light_pitch;
+ unsigned dragging;
+
+public:
+ Viewer(int, char **);
+ ~Viewer();
+
+ virtual int main();
+private:
+ virtual void tick();
+
+ void button_press(unsigned);
+ void button_release(unsigned);
+ void axis_motion(unsigned, float, float);
+
+ void update_camera();
+ void update_light();
+};
+
+
+Viewer::Viewer(int argc, char **argv):
+ window(1024, 768, false),
+ mouse(window),
+ object(0),
+ mesh(0),
+ yaw(0),
+ pitch(0),
+ distance(10),
+ light_yaw(0),
+ light_pitch(0),
+ dragging(0)
+{
+ if(argc<2)
+ throw usage_error("Filename must be provided");
+
+ string fn = argv[1];
+ string ext = FS::extpart(fn);
+
+ if(ext==".mesh")
+ {
+ mesh = new GL::Mesh;
+ DataFile::load(*mesh, fn);
+ }
+ else if(ext==".object")
+ {
+ object = new GL::Object;
+ DataFile::load(*object, fn);
+ }
+ else
+ throw usage_error("Don't know how to view this file");
+
+ window.signal_close.connect(sigc::bind(sigc::mem_fun(this, &Viewer::exit), 0));
+ mouse.signal_button_press.connect(sigc::mem_fun(this, &Viewer::button_press));
+ mouse.signal_button_release.connect(sigc::mem_fun(this, &Viewer::button_release));
+ mouse.signal_axis_motion.connect(sigc::mem_fun(this, &Viewer::axis_motion));
+
+ light.set_position(GL::Vector4(0, 0, 1, 0));
+ lighting.attach(0, light);
+
+ camera.set_up_direction(GL::Vector3(0, 0, 1));
+ update_camera();
+}
+
+Viewer::~Viewer()
+{
+ delete object;
+ delete mesh;
+}
+
+int Viewer::main()
+{
+ window.show();
+ return Application::main();
+}
+
+void Viewer::tick()
+{
+ window.tick();
+
+ GL::Framebuffer::system().clear(GL::COLOR_BUFFER_BIT|GL::DEPTH_BUFFER_BIT);
+
+ camera.apply();
+
+ GL::Bind bind_lighting(lighting);
+ GL::Bind bind_depth(GL::DepthTest::lequal());
+ GL::Bind bind_blend(GL::Blend::alpha());
+ if(object)
+ object->render();
+ else if(mesh)
+ mesh->draw();
+
+ window.swap_buffers();
+}
+
+void Viewer::button_press(unsigned btn)
+{
+ if(btn==1)
+ {
+ dragging = 1;
+ }
+ else if(btn==3)
+ {
+ dragging = 3;
+ axis_motion(0, 0, 0);
+ }
+ else if(btn==4)
+ {
+ distance *= 0.9;
+ update_camera();
+ }
+ else if(btn==5)
+ {
+ distance *= 1.1;
+ update_camera();
+ }
+}
+
+void Viewer::button_release(unsigned btn)
+{
+ if(btn==dragging)
+ dragging = 0;
+}
+
+void Viewer::axis_motion(unsigned axis, float, float delta)
+{
+ if(dragging==1)
+ {
+ float dx = (axis==0 ? delta : 0);
+ float dy = (axis==1 ? delta : 0);
+
+ yaw -= dx*M_PI*2;
+ while(yaw>M_PI)
+ yaw -= M_PI*2;
+ while(yaw<-M_PI)
+ yaw += M_PI*2;
+
+ pitch += dy*M_PI;
+ if(pitch>M_PI*0.49)
+ pitch = M_PI*0.49;
+ if(pitch<-M_PI*0.49)
+ pitch = -M_PI*0.49;
+
+ update_camera();
+ }
+ else if(dragging==3)
+ {
+ float x = mouse.get_axis_value(0);
+ float y = mouse.get_axis_value(1);
+
+ light_yaw = yaw+x*M_PI;
+ light_pitch = pitch-y*M_PI;
+
+ update_light();
+ }
+}
+
+void Viewer::update_camera()
+{
+ float cy = cos(yaw);
+ float sy = sin(yaw);
+ float cp = cos(pitch);
+ float sp = sin(pitch);
+ camera.set_position(GL::Vector3(-cy*cp*distance, -sy*cp*distance, -sp*distance));
+ camera.set_depth_clip(distance*0.02, distance*50);
+ camera.look_at(GL::Vector3(0, 0, 0));
+}
+
+void Viewer::update_light()
+{
+ float cy = cos(light_yaw);
+ float sy = sin(light_yaw);
+ float cp = cos(light_pitch);
+ float sp = sin(light_pitch);
+ light.set_position(GL::Vector4(-cy*cp, -sy*cp, -sp, 0));
+}