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