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