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