]> git.tdb.fi Git - libs/gl.git/blob - tools/viewer.cpp
Use an Options struct to process command line options in viewer
[libs/gl.git] / tools / viewer.cpp
1 #include <cmath>
2 #include <msp/core/application.h>
3 #include <msp/core/getopt.h>
4 #include <msp/datafile/directorysource.h>
5 #include <msp/datafile/packsource.h>
6 #include <msp/fs/stat.h>
7 #include <msp/fs/utils.h>
8 #include <msp/graphics/simplewindow.h>
9 #include <msp/gl/animatedobject.h>
10 #include <msp/gl/animation.h>
11 #include <msp/gl/animationplayer.h>
12 #include <msp/gl/blend.h>
13 #include <msp/gl/camera.h>
14 #include <msp/gl/framebuffer.h>
15 #include <msp/gl/light.h>
16 #include <msp/gl/lighting.h>
17 #include <msp/gl/mesh.h>
18 #include <msp/gl/object.h>
19 #include <msp/gl/pipeline.h>
20 #include <msp/gl/renderer.h>
21 #include <msp/gl/resources.h>
22 #include <msp/gl/simplescene.h>
23 #include <msp/gl/technique.h>
24 #include <msp/gl/tests.h>
25 #include <msp/gl/windowview.h>
26 #include <msp/input/mouse.h>
27 #include <msp/io/print.h>
28 #include <msp/time/timestamp.h>
29 #include <msp/time/utils.h>
30
31 using namespace std;
32 using namespace Msp;
33
34 class Viewer: public RegisteredApplication<Viewer>
35 {
36 private:
37         struct Options
38         {
39                 list<string> resource_locations;
40                 string animation_name;
41                 string renderable_name;
42
43                 Options(int, char **);
44         };
45
46         class Resources: public GL::Resources
47         {
48         private:
49                 DataFile::DirectorySource dir_source;
50                 DataFile::PackSource pack_source;
51
52         public:
53                 Resources();
54
55                 void add_directory(const string &);
56                 void add_pack(const string &);
57         };
58
59         Options opts;
60         Graphics::SimpleGLWindow window;
61         Input::Mouse mouse;
62         Resources resources;
63         GL::WindowView view;
64         GL::Pipeline pipeline;
65         GL::Renderable *renderable;
66         GL::AnimatedObject *anim_object;
67         GL::AnimationPlayer *anim_player;
68         GL::Light light;
69         GL::Lighting lighting;
70         GL::Camera camera;
71         float yaw;
72         float pitch;
73         float distance;
74         float light_yaw;
75         float light_pitch;
76         unsigned dragging;
77         Time::TimeStamp last_tick;
78
79 public:
80         Viewer(int, char **);
81 private:
82         template<typename T>
83         T *load(const string &);
84 public:
85         ~Viewer();
86
87         virtual int main();
88 private:
89         virtual void tick();
90
91         void button_press(unsigned);
92         void button_release(unsigned);
93         void axis_motion(unsigned, float, float);
94
95         void update_camera();
96         void update_light();
97 };
98
99
100 Viewer::Options::Options(int argc, char **argv)
101 {
102         GetOpt getopt;
103         getopt.add_option('r', "resources", resource_locations, GetOpt::REQUIRED_ARG);
104         getopt.add_option('a', "animation", animation_name, GetOpt::REQUIRED_ARG);
105         getopt.add_argument("renderable", renderable_name);
106         getopt(argc, argv);
107 }
108
109 Viewer::Viewer(int argc, char **argv):
110         opts(argc, argv),
111         window(1024, 768, false),
112         mouse(window),
113         view(window, window.get_gl_context()),
114         pipeline(view),
115         renderable(0),
116         anim_object(0),
117         anim_player(0),
118         yaw(0),
119         pitch(0),
120         distance(10),
121         light_yaw(0),
122         light_pitch(0),
123         dragging(0)
124 {
125         for(list<string>::const_iterator i=opts.resource_locations.begin(); i!=opts.resource_locations.end(); ++i)
126         {
127                 if(FS::is_dir(*i))
128                         resources.add_directory(*i);
129                 else
130                 {
131                         string ext = FS::extpart(*i);
132                         if(ext==".mdp")
133                                 resources.add_pack(*i);
134                         else
135                                 DataFile::load(resources, *i);
136                 }
137         }
138
139         GL::Object *object = 0;
140
141         string ext = FS::extpart(opts.renderable_name);
142         if(ext==".mesh")
143         {
144                 GL::Mesh *mesh = 0;
145                 if(FS::exists(opts.renderable_name))
146                 {
147                         mesh = new GL::Mesh;
148                         DataFile::load(*mesh, opts.renderable_name);
149                         resources.add("__"+opts.renderable_name, mesh);
150                 }
151                 else
152                         mesh = &resources.get<GL::Mesh>(opts.renderable_name);
153
154                 object = new GL::Object;
155                 GL::Technique *tech = new GL::Technique;
156                 tech->add_pass(0);
157                 object->set_mesh(mesh);
158                 object->set_technique(tech);
159                 renderable = object;
160
161                 resources.add("__.tech", tech);
162                 resources.add("__.object", object);
163         }
164         else if(ext==".object")
165                 renderable = load<GL::Object>(opts.renderable_name);
166         else if(ext==".scene")
167         {
168                 GL::SimpleScene *scene = new GL::SimpleScene;
169                 DataFile::load(*scene, opts.renderable_name, resources);
170                 renderable = scene;
171         }
172         else
173                 throw usage_error("Unknown renderable type");
174
175         if(!opts.animation_name.empty())
176         {
177                 if(!object)
178                         throw usage_error("Must have an object to animate");
179
180                 GL::Animation *anim = load<GL::Animation>(opts.animation_name);
181                 anim_player = new GL::AnimationPlayer;
182                 anim_object = new GL::AnimatedObject(*object);
183                 anim_player->play(*anim_object, *anim);
184                 renderable = anim_object;
185         }
186
187         window.signal_close.connect(sigc::bind(sigc::mem_fun(this, &Viewer::exit), 0));
188         mouse.signal_button_press.connect(sigc::bind_return(sigc::mem_fun(this, &Viewer::button_press), false));
189         mouse.signal_button_release.connect(sigc::bind_return(sigc::mem_fun(this, &Viewer::button_release), false));
190         mouse.signal_axis_motion.connect(sigc::bind_return(sigc::mem_fun(this, &Viewer::axis_motion), false));
191
192         light.set_position(GL::Vector4(0, 0, 1, 0));
193         lighting.attach(0, light);
194
195         camera.set_up_direction(GL::Vector3(0, 0, 1));
196         update_camera();
197
198         GL::Pipeline::Pass &pass = pipeline.add_pass(0, *renderable);
199         pass.set_lighting(&lighting);
200         pass.set_depth_test(&GL::DepthTest::lequal());
201         pass.set_blend(&GL::Blend::alpha());
202
203         view.set_content(&pipeline);
204         view.set_camera(&camera);
205 }
206
207 template<typename T>
208 T *Viewer::load(const string &name)
209 {
210         if(FS::exists(name))
211         {
212                 T *thing = new T;
213                 DataFile::load(*thing, name, resources);
214                 resources.add("__"+name, thing);
215                 return thing;
216         }
217         else
218                 return &resources.get<T>(name);
219 }
220
221 Viewer::~Viewer()
222 {
223         delete anim_player;
224         delete anim_object;
225 }
226
227 int Viewer::main()
228 {
229         window.show();
230         return Application::main();
231 }
232
233 void Viewer::tick()
234 {
235         if(anim_player)
236         {
237                 Time::TimeStamp t = Time::now();
238                 Time::TimeDelta dt;
239                 if(last_tick)
240                         dt = t-last_tick;
241                 last_tick = t;
242
243                 anim_player->tick(dt);
244         }
245
246         window.tick();
247         view.render();
248 }
249
250 void Viewer::button_press(unsigned btn)
251 {
252         if(btn==1)
253         {
254                 dragging = 1;
255         }
256         else if(btn==3)
257         {
258                 dragging = 3;
259                 axis_motion(0, 0, 0);
260         }
261         else if(btn==4)
262         {
263                 distance *= 0.9;
264                 update_camera();
265         }
266         else if(btn==5)
267         {
268                 distance *= 1.1;
269                 update_camera();
270         }
271 }
272
273 void Viewer::button_release(unsigned btn)
274 {
275         if(btn==dragging)
276                 dragging = 0;
277 }
278
279 void Viewer::axis_motion(unsigned axis, float, float delta)
280 {
281         if(dragging==1)
282         {
283                 float dx = (axis==0 ? delta : 0);
284                 float dy = (axis==1 ? delta : 0);
285
286                 yaw -= dx*M_PI*2;
287                 while(yaw>M_PI)
288                         yaw -= M_PI*2;
289                 while(yaw<-M_PI)
290                         yaw += M_PI*2;
291
292                 pitch += dy*M_PI;
293                 if(pitch>M_PI*0.49)
294                         pitch = M_PI*0.49;
295                 if(pitch<-M_PI*0.49)
296                         pitch = -M_PI*0.49;
297
298                 update_camera();
299         }
300         else if(dragging==3)
301         {
302                 float x = mouse.get_axis_value(0);
303                 float y = mouse.get_axis_value(1);
304
305                 light_yaw = yaw+x*M_PI;
306                 light_pitch = pitch-y*M_PI;
307
308                 update_light();
309         }
310 }
311
312 void Viewer::update_camera()
313 {
314         float cy = cos(yaw);
315         float sy = sin(yaw);
316         float cp = cos(pitch);
317         float sp = sin(pitch);
318         camera.set_position(GL::Vector3(-cy*cp*distance, -sy*cp*distance, -sp*distance));
319         camera.set_depth_clip(distance*0.02, distance*50);
320         camera.look_at(GL::Vector3(0, 0, 0));
321 }
322
323 void Viewer::update_light()
324 {
325         float cy = cos(light_yaw);
326         float sy = sin(light_yaw);
327         float cp = cos(light_pitch);
328         float sp = sin(light_pitch);
329         light.set_position(GL::Vector4(-cy*cp, -sy*cp, -sp, 0));
330 }
331
332
333 Viewer::Resources::Resources()
334 {
335         add_source(dir_source);
336         add_source(pack_source);
337 }
338
339 void Viewer::Resources::add_directory(const string &dir)
340 {
341         dir_source.add_directory(dir);
342 }
343
344 void Viewer::Resources::add_pack(const string &pack)
345 {
346         pack_source.add_pack_file(pack);
347 }