]> git.tdb.fi Git - libs/gl.git/blob - tools/viewer.cpp
Move ProgramData to materials
[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
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         GL::Device gl_device;
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::DirectionalLight 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 }
130
131 Viewer::Viewer(int argc, char **argv):
132         opts(argc, argv),
133         window(display, opts.wnd_opts),
134         gl_device(window),
135         mouse(window),
136         view(window),
137         sequence(0),
138         renderable(0),
139         anim_object(0),
140         anim_player(0),
141         yaw(0),
142         pitch(0),
143         distance(10),
144         light_yaw(0),
145         light_pitch(0),
146         dragging(0)
147 {
148         for(list<string>::const_iterator i=opts.resource_locations.begin(); i!=opts.resource_locations.end(); ++i)
149         {
150                 if(FS::is_dir(*i))
151                         resources.add_directory(*i);
152                 else
153                 {
154                         string ext = FS::extpart(*i);
155                         if(ext==".mdp")
156                                 resources.add_pack(*i);
157                         else
158                                 DataFile::load(resources, *i);
159                 }
160         }
161
162         GL::Object *object = 0;
163
164         string ext = FS::extpart(opts.renderable_name);
165         if(ext==".mesh")
166         {
167                 GL::Mesh *mesh = 0;
168                 if(FS::exists(opts.renderable_name))
169                 {
170                         mesh = new GL::Mesh;
171                         DataFile::load(*mesh, opts.renderable_name);
172                         resources.add("__"+opts.renderable_name, mesh);
173                 }
174                 else
175                         mesh = &resources.get<GL::Mesh>(opts.renderable_name);
176
177                 object = new GL::Object;
178                 GL::Technique *tech = new GL::Technique;
179                 tech->add_method(0);
180                 object->set_mesh(mesh);
181                 object->set_technique(tech);
182                 renderable = object;
183
184                 resources.add("__.tech", tech);
185                 resources.add("__.object", object);
186         }
187         else if(ext==".object")
188                 renderable = load<GL::Object>(opts.renderable_name);
189         else if(ext==".scene")
190         {
191                 if(FS::exists(opts.renderable_name))
192                 {
193                         GL::Scene::GenericLoader ldr(resources);
194                         IO::BufferedFile in(opts.renderable_name);
195                         DataFile::Parser parser(in, opts.renderable_name);
196                         ldr.load(parser);
197                         renderable = ldr.get_object();
198                 }
199                 else
200                         renderable = &resources.get<GL::Scene>(opts.renderable_name);
201         }
202         else if(ext==".seq")
203         {
204                 GL::SequenceTemplate *tmpl = load<GL::SequenceTemplate>(opts.renderable_name);
205                 GL::SequenceBuilder bld(*tmpl);
206                 bld.set_debug_name(FS::basename(opts.renderable_name));
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_direction(GL::Vector3(0, 0, -1));
230         lighting.attach(light);
231
232         camera.set_debug_name("Camera");
233         camera.set_up_direction(GL::Vector3(0, 0, 1));
234         update_camera();
235
236         if(!sequence)
237         {
238                 sequence = new GL::Sequence();
239                 sequence->set_debug_name("Sequence");
240                 sequence->set_clear_enabled(true);
241                 GL::Sequence::Step &step = sequence->add_step(0, *renderable);
242                 step.set_lighting(&lighting);
243                 step.set_depth_test(GL::LEQUAL);
244         }
245
246         view.set_content(sequence);
247         view.set_camera(&camera);
248 }
249
250 template<typename T>
251 T *Viewer::load(const string &name)
252 {
253         if(FS::exists(name))
254         {
255                 T *thing = new T;
256                 DataFile::load(*thing, name, resources);
257                 resources.add("__"+name, thing);
258                 return thing;
259         }
260         else
261                 return &resources.get<T>(name);
262 }
263
264 Viewer::~Viewer()
265 {
266         delete anim_player;
267         delete anim_object;
268         delete sequence;
269 }
270
271 int Viewer::main()
272 {
273         window.show();
274         return Application::main();
275 }
276
277 void Viewer::tick()
278 {
279         if(anim_player)
280         {
281                 Time::TimeStamp t = Time::now();
282                 Time::TimeDelta dt;
283                 if(last_tick)
284                         dt = t-last_tick;
285                 last_tick = t;
286
287                 anim_player->tick(dt);
288         }
289
290         display.tick();
291         view.render();
292 }
293
294 void Viewer::button_press(unsigned btn)
295 {
296         if(btn==1)
297         {
298                 dragging = 1;
299         }
300         else if(btn==3)
301         {
302                 dragging = 3;
303                 axis_motion(0, 0, 0);
304         }
305         else if(btn==4)
306         {
307                 distance *= 0.9;
308                 update_camera();
309         }
310         else if(btn==5)
311         {
312                 distance *= 1.1;
313                 update_camera();
314         }
315 }
316
317 void Viewer::button_release(unsigned btn)
318 {
319         if(btn==dragging)
320                 dragging = 0;
321 }
322
323 void Viewer::axis_motion(unsigned axis, float, float delta)
324 {
325         if(dragging==1)
326         {
327                 float dx = (axis==0 ? delta : 0);
328                 float dy = (axis==1 ? delta : 0);
329
330                 yaw -= dx*M_PI*2;
331                 while(yaw>M_PI)
332                         yaw -= M_PI*2;
333                 while(yaw<-M_PI)
334                         yaw += M_PI*2;
335
336                 pitch += dy*M_PI;
337                 if(pitch>M_PI*0.49)
338                         pitch = M_PI*0.49;
339                 if(pitch<-M_PI*0.49)
340                         pitch = -M_PI*0.49;
341
342                 update_camera();
343         }
344         else if(dragging==3)
345         {
346                 float x = mouse.get_axis_value(0);
347                 float y = mouse.get_axis_value(1);
348
349                 light_yaw = yaw+x*M_PI;
350                 light_pitch = pitch-y*M_PI;
351
352                 update_light();
353         }
354 }
355
356 void Viewer::update_camera()
357 {
358         float cy = cos(yaw);
359         float sy = sin(yaw);
360         float cp = cos(pitch);
361         float sp = sin(pitch);
362         camera.set_position(GL::Vector3(-cy*cp*distance, -sy*cp*distance, -sp*distance));
363         camera.set_depth_clip(distance*0.02, distance*50);
364         camera.look_at(GL::Vector3(0, 0, 0));
365 }
366
367 void Viewer::update_light()
368 {
369         float cy = cos(light_yaw);
370         float sy = sin(light_yaw);
371         float cp = cos(light_pitch);
372         float sp = sin(light_pitch);
373         light.set_direction(GL::Vector3(cy*cp, sy*cp, sp));
374 }
375
376
377 Viewer::Resources::Resources()
378 {
379         add_source(dir_source);
380         add_source(pack_source);
381 }
382
383 void Viewer::Resources::add_directory(const string &dir)
384 {
385         dir_source.add_directory(dir);
386 }
387
388 void Viewer::Resources::add_pack(const string &pack)
389 {
390         pack_source.add_pack_file(pack);
391 }