]> git.tdb.fi Git - libs/gl.git/blob - tools/viewer.cpp
Make Scenes loadable from collection files too
[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/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         GetOpt getopt;
109         getopt.add_option('r', "resources", resource_locations, GetOpt::REQUIRED_ARG);
110         getopt.add_option('a', "animation", animation_name, GetOpt::REQUIRED_ARG);
111         getopt.add_argument("renderable", renderable_name);
112         getopt(argc, argv);
113
114         wnd_opts.width = 1024;
115         wnd_opts.height = 768;
116         gl_opts.gl_version_major = Graphics::GLOptions::LATEST_VERSION;
117         gl_opts.core_profile = true;
118 }
119
120 Viewer::Viewer(int argc, char **argv):
121         opts(argc, argv),
122         window(display, opts.wnd_opts),
123         gl_ctx(window, opts.gl_opts),
124         mouse(window),
125         view(window, gl_ctx),
126         sequence(0),
127         renderable(0),
128         anim_object(0),
129         anim_player(0),
130         yaw(0),
131         pitch(0),
132         distance(10),
133         light_yaw(0),
134         light_pitch(0),
135         dragging(0)
136 {
137         for(list<string>::const_iterator i=opts.resource_locations.begin(); i!=opts.resource_locations.end(); ++i)
138         {
139                 if(FS::is_dir(*i))
140                         resources.add_directory(*i);
141                 else
142                 {
143                         string ext = FS::extpart(*i);
144                         if(ext==".mdp")
145                                 resources.add_pack(*i);
146                         else
147                                 DataFile::load(resources, *i);
148                 }
149         }
150
151         GL::Object *object = 0;
152
153         string ext = FS::extpart(opts.renderable_name);
154         if(ext==".mesh")
155         {
156                 GL::Mesh *mesh = 0;
157                 if(FS::exists(opts.renderable_name))
158                 {
159                         mesh = new GL::Mesh;
160                         DataFile::load(*mesh, opts.renderable_name);
161                         resources.add("__"+opts.renderable_name, mesh);
162                 }
163                 else
164                         mesh = &resources.get<GL::Mesh>(opts.renderable_name);
165
166                 object = new GL::Object;
167                 GL::Technique *tech = new GL::Technique;
168                 tech->add_pass(0);
169                 object->set_mesh(mesh);
170                 object->set_technique(tech);
171                 renderable = object;
172
173                 resources.add("__.tech", tech);
174                 resources.add("__.object", object);
175         }
176         else if(ext==".object")
177                 renderable = load<GL::Object>(opts.renderable_name);
178         else if(ext==".scene")
179         {
180                 if(FS::exists(opts.renderable_name))
181                 {
182                         GL::Scene::GenericLoader ldr(resources);
183                         IO::BufferedFile in(opts.renderable_name);
184                         DataFile::Parser parser(in, opts.renderable_name);
185                         ldr.load(parser);
186                         renderable = ldr.get_scene();
187                 }
188                 else
189                         renderable = &resources.get<GL::Scene>(opts.renderable_name);
190         }
191         else if(ext==".seq")
192         {
193                 GL::SequenceTemplate *tmpl = load<GL::SequenceTemplate>(opts.renderable_name);
194                 GL::SequenceBuilder bld(*tmpl);
195                 sequence = bld.build(view);
196         }
197         else
198                 throw usage_error("Unknown renderable type");
199
200         if(!opts.animation_name.empty())
201         {
202                 if(!object)
203                         throw usage_error("Must have an object to animate");
204
205                 GL::Animation *anim = load<GL::Animation>(opts.animation_name);
206                 anim_player = new GL::AnimationPlayer;
207                 anim_object = new GL::AnimatedObject(*object);
208                 anim_player->play(*anim_object, *anim);
209                 renderable = anim_object;
210         }
211
212         window.signal_close.connect(sigc::bind(sigc::mem_fun(this, &Viewer::exit), 0));
213         mouse.signal_button_press.connect(sigc::bind_return(sigc::mem_fun(this, &Viewer::button_press), false));
214         mouse.signal_button_release.connect(sigc::bind_return(sigc::mem_fun(this, &Viewer::button_release), false));
215         mouse.signal_axis_motion.connect(sigc::bind_return(sigc::mem_fun(this, &Viewer::axis_motion), false));
216
217         light.set_position(GL::Vector4(0, 0, 1, 0));
218         lighting.attach(0, light);
219
220         camera.set_up_direction(GL::Vector3(0, 0, 1));
221         update_camera();
222
223         if(!sequence)
224         {
225                 sequence = new GL::Sequence(view);
226                 GL::Sequence::Step &step = sequence->add_step(0, *renderable);
227                 step.set_lighting(&lighting);
228                 step.set_depth_test(&GL::DepthTest::lequal());
229                 step.set_blend(&GL::Blend::alpha());
230         }
231
232         view.set_content(sequence);
233         view.set_camera(&camera);
234 }
235
236 template<typename T>
237 T *Viewer::load(const string &name)
238 {
239         if(FS::exists(name))
240         {
241                 T *thing = new T;
242                 DataFile::load(*thing, name, resources);
243                 resources.add("__"+name, thing);
244                 return thing;
245         }
246         else
247                 return &resources.get<T>(name);
248 }
249
250 Viewer::~Viewer()
251 {
252         delete anim_player;
253         delete anim_object;
254         delete sequence;
255 }
256
257 int Viewer::main()
258 {
259         window.show();
260         return Application::main();
261 }
262
263 void Viewer::tick()
264 {
265         if(anim_player)
266         {
267                 Time::TimeStamp t = Time::now();
268                 Time::TimeDelta dt;
269                 if(last_tick)
270                         dt = t-last_tick;
271                 last_tick = t;
272
273                 anim_player->tick(dt);
274         }
275
276         display.tick();
277         view.render();
278 }
279
280 void Viewer::button_press(unsigned btn)
281 {
282         if(btn==1)
283         {
284                 dragging = 1;
285         }
286         else if(btn==3)
287         {
288                 dragging = 3;
289                 axis_motion(0, 0, 0);
290         }
291         else if(btn==4)
292         {
293                 distance *= 0.9;
294                 update_camera();
295         }
296         else if(btn==5)
297         {
298                 distance *= 1.1;
299                 update_camera();
300         }
301 }
302
303 void Viewer::button_release(unsigned btn)
304 {
305         if(btn==dragging)
306                 dragging = 0;
307 }
308
309 void Viewer::axis_motion(unsigned axis, float, float delta)
310 {
311         if(dragging==1)
312         {
313                 float dx = (axis==0 ? delta : 0);
314                 float dy = (axis==1 ? delta : 0);
315
316                 yaw -= dx*M_PI*2;
317                 while(yaw>M_PI)
318                         yaw -= M_PI*2;
319                 while(yaw<-M_PI)
320                         yaw += M_PI*2;
321
322                 pitch += dy*M_PI;
323                 if(pitch>M_PI*0.49)
324                         pitch = M_PI*0.49;
325                 if(pitch<-M_PI*0.49)
326                         pitch = -M_PI*0.49;
327
328                 update_camera();
329         }
330         else if(dragging==3)
331         {
332                 float x = mouse.get_axis_value(0);
333                 float y = mouse.get_axis_value(1);
334
335                 light_yaw = yaw+x*M_PI;
336                 light_pitch = pitch-y*M_PI;
337
338                 update_light();
339         }
340 }
341
342 void Viewer::update_camera()
343 {
344         float cy = cos(yaw);
345         float sy = sin(yaw);
346         float cp = cos(pitch);
347         float sp = sin(pitch);
348         camera.set_position(GL::Vector3(-cy*cp*distance, -sy*cp*distance, -sp*distance));
349         camera.set_depth_clip(distance*0.02, distance*50);
350         camera.look_at(GL::Vector3(0, 0, 0));
351 }
352
353 void Viewer::update_light()
354 {
355         float cy = cos(light_yaw);
356         float sy = sin(light_yaw);
357         float cp = cos(light_pitch);
358         float sp = sin(light_pitch);
359         light.set_position(GL::Vector4(-cy*cp, -sy*cp, -sp, 0));
360 }
361
362
363 Viewer::Resources::Resources()
364 {
365         add_source(dir_source);
366         add_source(pack_source);
367 }
368
369 void Viewer::Resources::add_directory(const string &dir)
370 {
371         dir_source.add_directory(dir);
372 }
373
374 void Viewer::Resources::add_pack(const string &pack)
375 {
376         pack_source.add_pack_file(pack);
377 }