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