]> git.tdb.fi Git - libs/gl.git/blob - tools/viewer.cpp
Enable clearing in the viewer's default sequence
[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/display.h>
9 #include <msp/graphics/window.h>
10 #include <msp/gl/animatedobject.h>
11 #include <msp/gl/animation.h>
12 #include <msp/gl/animationplayer.h>
13 #include <msp/gl/blend.h>
14 #include <msp/gl/camera.h>
15 #include <msp/gl/device.h>
16 #include <msp/gl/directionallight.h>
17 #include <msp/gl/framebuffer.h>
18 #include <msp/gl/lighting.h>
19 #include <msp/gl/mesh.h>
20 #include <msp/gl/object.h>
21 #include <msp/gl/sequence.h>
22 #include <msp/gl/sequencebuilder.h>
23 #include <msp/gl/sequencetemplate.h>
24 #include <msp/gl/renderer.h>
25 #include <msp/gl/resources.h>
26 #include <msp/gl/simplescene.h>
27 #include <msp/gl/technique.h>
28 #include <msp/gl/windowview.h>
29 #include <msp/input/mouse.h>
30 #include <msp/io/print.h>
31 #include <msp/strings/regex.h>
32 #include <msp/time/timestamp.h>
33 #include <msp/time/utils.h>
34
35 using namespace std;
36 using namespace Msp;
37
38 class Viewer: public RegisteredApplication<Viewer>
39 {
40 private:
41         struct Options
42         {
43                 list<string> resource_locations;
44                 string animation_name;
45                 string renderable_name;
46                 Graphics::WindowOptions wnd_opts;
47                 Graphics::GLOptions gl_opts;
48
49                 Options(int, char **);
50         };
51
52         class Resources: public GL::Resources
53         {
54         private:
55                 DataFile::DirectorySource dir_source;
56                 DataFile::PackSource pack_source;
57
58         public:
59                 Resources();
60
61                 void add_directory(const string &);
62                 void add_pack(const string &);
63         };
64
65         Options opts;
66         Graphics::Display display;
67         Graphics::Window window;
68         GL::Device gl_device;
69         Input::Mouse mouse;
70         Resources resources;
71         GL::WindowView view;
72         GL::Sequence *sequence;
73         GL::Renderable *renderable;
74         GL::AnimatedObject *anim_object;
75         GL::AnimationPlayer *anim_player;
76         GL::DirectionalLight light;
77         GL::Lighting lighting;
78         GL::Camera camera;
79         float yaw;
80         float pitch;
81         float distance;
82         float light_yaw;
83         float light_pitch;
84         unsigned dragging;
85         Time::TimeStamp last_tick;
86
87 public:
88         Viewer(int, char **);
89 private:
90         template<typename T>
91         T *load(const string &);
92 public:
93         ~Viewer();
94
95         virtual int main();
96 private:
97         virtual void tick();
98
99         void button_press(unsigned);
100         void button_release(unsigned);
101         void axis_motion(unsigned, float, float);
102
103         void update_camera();
104         void update_light();
105 };
106
107
108 Viewer::Options::Options(int argc, char **argv)
109 {
110         string window_size;
111
112         GetOpt getopt;
113         getopt.add_option('r', "resources", resource_locations, GetOpt::REQUIRED_ARG);
114         getopt.add_option('a', "animation", animation_name, GetOpt::REQUIRED_ARG);
115         getopt.add_option('w', "window-size", window_size, GetOpt::REQUIRED_ARG);
116         getopt.add_argument("renderable", renderable_name);
117         getopt(argc, argv);
118
119         wnd_opts.width = 1024;
120         wnd_opts.height = 768;
121         if (!window_size.empty())
122         {
123                 RegMatch m = Regex("^([1-9][0-9]*)x([1-9][0-9]*)$").match(window_size);
124                 if(!m)
125                         throw usage_error("Invalid window size");
126
127                 wnd_opts.width = lexical_cast<unsigned>(m[1].str);
128                 wnd_opts.height = lexical_cast<unsigned>(m[2].str);
129         }
130         gl_opts.gl_version_major = Graphics::GLOptions::LATEST_VERSION;
131         gl_opts.core_profile = true;
132 }
133
134 Viewer::Viewer(int argc, char **argv):
135         opts(argc, argv),
136         window(display, opts.wnd_opts),
137         gl_device(window, opts.gl_opts),
138         mouse(window),
139         view(window),
140         sequence(0),
141         renderable(0),
142         anim_object(0),
143         anim_player(0),
144         yaw(0),
145         pitch(0),
146         distance(10),
147         light_yaw(0),
148         light_pitch(0),
149         dragging(0)
150 {
151         for(list<string>::const_iterator i=opts.resource_locations.begin(); i!=opts.resource_locations.end(); ++i)
152         {
153                 if(FS::is_dir(*i))
154                         resources.add_directory(*i);
155                 else
156                 {
157                         string ext = FS::extpart(*i);
158                         if(ext==".mdp")
159                                 resources.add_pack(*i);
160                         else
161                                 DataFile::load(resources, *i);
162                 }
163         }
164
165         GL::Object *object = 0;
166
167         string ext = FS::extpart(opts.renderable_name);
168         if(ext==".mesh")
169         {
170                 GL::Mesh *mesh = 0;
171                 if(FS::exists(opts.renderable_name))
172                 {
173                         mesh = new GL::Mesh;
174                         DataFile::load(*mesh, opts.renderable_name);
175                         resources.add("__"+opts.renderable_name, mesh);
176                 }
177                 else
178                         mesh = &resources.get<GL::Mesh>(opts.renderable_name);
179
180                 object = new GL::Object;
181                 GL::Technique *tech = new GL::Technique;
182                 tech->add_method(0);
183                 object->set_mesh(mesh);
184                 object->set_technique(tech);
185                 renderable = object;
186
187                 resources.add("__.tech", tech);
188                 resources.add("__.object", object);
189         }
190         else if(ext==".object")
191                 renderable = load<GL::Object>(opts.renderable_name);
192         else if(ext==".scene")
193         {
194                 if(FS::exists(opts.renderable_name))
195                 {
196                         GL::Scene::GenericLoader ldr(resources);
197                         IO::BufferedFile in(opts.renderable_name);
198                         DataFile::Parser parser(in, opts.renderable_name);
199                         ldr.load(parser);
200                         renderable = ldr.get_object();
201                 }
202                 else
203                         renderable = &resources.get<GL::Scene>(opts.renderable_name);
204         }
205         else if(ext==".seq")
206         {
207                 GL::SequenceTemplate *tmpl = load<GL::SequenceTemplate>(opts.renderable_name);
208                 GL::SequenceBuilder bld(*tmpl);
209                 bld.set_debug_name(FS::basename(opts.renderable_name));
210                 sequence = bld.build(view);
211         }
212         else
213                 throw usage_error("Unknown renderable type");
214
215         if(!opts.animation_name.empty())
216         {
217                 if(!object)
218                         throw usage_error("Must have an object to animate");
219
220                 GL::Animation *anim = load<GL::Animation>(opts.animation_name);
221                 anim_player = new GL::AnimationPlayer;
222                 anim_object = new GL::AnimatedObject(*object);
223                 anim_player->play(*anim_object, *anim);
224                 renderable = anim_object;
225         }
226
227         window.signal_close.connect(sigc::bind(sigc::mem_fun(this, &Viewer::exit), 0));
228         mouse.signal_button_press.connect(sigc::bind_return(sigc::mem_fun(this, &Viewer::button_press), false));
229         mouse.signal_button_release.connect(sigc::bind_return(sigc::mem_fun(this, &Viewer::button_release), false));
230         mouse.signal_axis_motion.connect(sigc::bind_return(sigc::mem_fun(this, &Viewer::axis_motion), false));
231
232         light.set_direction(GL::Vector3(0, 0, -1));
233         lighting.attach(light);
234
235         camera.set_debug_name("Camera");
236         camera.set_up_direction(GL::Vector3(0, 0, 1));
237         update_camera();
238
239         if(!sequence)
240         {
241                 sequence = new GL::Sequence();
242                 sequence->set_debug_name("Sequence");
243                 sequence->set_clear_enabled(true);
244                 GL::Sequence::Step &step = sequence->add_step(0, *renderable);
245                 step.set_lighting(&lighting);
246                 step.set_depth_test(GL::LEQUAL);
247         }
248
249         view.set_content(sequence);
250         view.set_camera(&camera);
251 }
252
253 template<typename T>
254 T *Viewer::load(const string &name)
255 {
256         if(FS::exists(name))
257         {
258                 T *thing = new T;
259                 DataFile::load(*thing, name, resources);
260                 resources.add("__"+name, thing);
261                 return thing;
262         }
263         else
264                 return &resources.get<T>(name);
265 }
266
267 Viewer::~Viewer()
268 {
269         delete anim_player;
270         delete anim_object;
271         delete sequence;
272 }
273
274 int Viewer::main()
275 {
276         window.show();
277         return Application::main();
278 }
279
280 void Viewer::tick()
281 {
282         if(anim_player)
283         {
284                 Time::TimeStamp t = Time::now();
285                 Time::TimeDelta dt;
286                 if(last_tick)
287                         dt = t-last_tick;
288                 last_tick = t;
289
290                 anim_player->tick(dt);
291         }
292
293         display.tick();
294         view.render();
295 }
296
297 void Viewer::button_press(unsigned btn)
298 {
299         if(btn==1)
300         {
301                 dragging = 1;
302         }
303         else if(btn==3)
304         {
305                 dragging = 3;
306                 axis_motion(0, 0, 0);
307         }
308         else if(btn==4)
309         {
310                 distance *= 0.9;
311                 update_camera();
312         }
313         else if(btn==5)
314         {
315                 distance *= 1.1;
316                 update_camera();
317         }
318 }
319
320 void Viewer::button_release(unsigned btn)
321 {
322         if(btn==dragging)
323                 dragging = 0;
324 }
325
326 void Viewer::axis_motion(unsigned axis, float, float delta)
327 {
328         if(dragging==1)
329         {
330                 float dx = (axis==0 ? delta : 0);
331                 float dy = (axis==1 ? delta : 0);
332
333                 yaw -= dx*M_PI*2;
334                 while(yaw>M_PI)
335                         yaw -= M_PI*2;
336                 while(yaw<-M_PI)
337                         yaw += M_PI*2;
338
339                 pitch += dy*M_PI;
340                 if(pitch>M_PI*0.49)
341                         pitch = M_PI*0.49;
342                 if(pitch<-M_PI*0.49)
343                         pitch = -M_PI*0.49;
344
345                 update_camera();
346         }
347         else if(dragging==3)
348         {
349                 float x = mouse.get_axis_value(0);
350                 float y = mouse.get_axis_value(1);
351
352                 light_yaw = yaw+x*M_PI;
353                 light_pitch = pitch-y*M_PI;
354
355                 update_light();
356         }
357 }
358
359 void Viewer::update_camera()
360 {
361         float cy = cos(yaw);
362         float sy = sin(yaw);
363         float cp = cos(pitch);
364         float sp = sin(pitch);
365         camera.set_position(GL::Vector3(-cy*cp*distance, -sy*cp*distance, -sp*distance));
366         camera.set_depth_clip(distance*0.02, distance*50);
367         camera.look_at(GL::Vector3(0, 0, 0));
368 }
369
370 void Viewer::update_light()
371 {
372         float cy = cos(light_yaw);
373         float sy = sin(light_yaw);
374         float cp = cos(light_pitch);
375         float sp = sin(light_pitch);
376         light.set_direction(GL::Vector3(cy*cp, sy*cp, sp));
377 }
378
379
380 Viewer::Resources::Resources()
381 {
382         add_source(dir_source);
383         add_source(pack_source);
384 }
385
386 void Viewer::Resources::add_directory(const string &dir)
387 {
388         dir_source.add_directory(dir);
389 }
390
391 void Viewer::Resources::add_pack(const string &pack)
392 {
393         pack_source.add_pack_file(pack);
394 }