Fix memory leaks
[libs/vr.git] / source / stereoview.cpp
1 #include <msp/gl/renderer.h>
2 #include "headtrackingcamera.h"
3 #include "motioncontroller.h"
4 #include "stereocombiner.h"
5 #include "stereoview.h"
6
7 using namespace std;
8
9 namespace Msp {
10 namespace VR {
11
12 StereoView::StereoView(const StereoCombiner &c, const GL::Camera &a):
13         combiner(c),
14         base_camera(a),
15         head_camera(0)
16 {
17         init();
18 }
19
20 StereoView::StereoView(const StereoCombiner &c, HeadTrackingCamera &a):
21         combiner(c),
22         base_camera(a),
23         head_camera(&a)
24 {
25         init();
26 }
27
28 void StereoView::init()
29 {
30         content = 0;
31
32         unsigned w = combiner.get_target_width();
33         unsigned h = combiner.get_target_height();
34         left.create_target(w, h);
35         right.create_target(w, h);
36
37         set_eye_spacing(0.064);
38         set_strabismus(Geometry::Angle<float>::zero());
39 }
40
41 void StereoView::set_content(const GL::Renderable *r)
42 {
43         content = r;
44 }
45
46 void StereoView::set_eye_spacing(float s)
47 {
48         GL::Vector3 offset(s/2, 0, 0);
49         set_eye_matrices(GL::Matrix::translation(-offset), GL::Matrix::translation(offset));
50 }
51
52 void StereoView::set_eye_matrices(const GL::Matrix &left_matrix, const GL::Matrix &right_matrix)
53 {
54         left.offset_matrix = left_matrix;
55         right.offset_matrix = right_matrix;
56 }
57
58 void StereoView::set_strabismus(const Geometry::Angle<float> &s)
59 {
60         strabismus = s;
61 }
62
63 void StereoView::add_controller(MotionController &controller)
64 {
65         if(find(controllers.begin(), controllers.end(), &controller)==controllers.end())
66                 controllers.push_back(&controller);
67 }
68
69 void StereoView::remove_controller(MotionController &controller)
70 {
71         vector<MotionController *>::iterator i = find(controllers.begin(), controllers.end(), &controller);
72         if(i!=controllers.end())
73                 controllers.erase(i);
74 }
75
76 void StereoView::setup_frame() const
77 {
78         if(head_camera)
79                 head_camera->update();
80
81         for(vector<MotionController *>::const_iterator i=controllers.begin(); i!=controllers.end(); ++i)
82                 (*i)->update();
83
84         EyeParams params;
85         params.fov = combiner.get_field_of_view();
86         if(params.fov==Geometry::Angle<float>::zero())
87                 params.fov = base_camera.get_field_of_view();
88
89         params.aspect = combiner.get_render_aspect();
90         params.near_clip = base_camera.get_near_clip();
91         params.far_clip = base_camera.get_far_clip();
92
93         float frustum_skew = combiner.get_frustum_skew();
94         float halfw = tan(params.fov/2.0f)*params.aspect;
95         frustum_skew = tan(Geometry::atan<float>(frustum_skew*halfw)+strabismus)/halfw;
96
97         left.setup_frame(base_camera, frustum_skew, params);
98         right.setup_frame(base_camera, -frustum_skew, params);
99 }
100
101 void StereoView::render() const
102 {
103         combiner.prepare();
104         setup_frame();
105         if(content)
106         {
107                 content->setup_frame();
108                 left.render(*content);
109                 right.render(*content);
110                 content->finish_frame();
111         }
112         combiner.render(left.target->color, right.target->color);
113 }
114
115
116 StereoView::RenderTarget::RenderTarget(unsigned width, unsigned height)
117 {
118         color.set_min_filter(GL::LINEAR);
119         color.set_wrap(GL::CLAMP_TO_EDGE);
120         color.storage(GL::RGB, width, height);
121         fbo.attach(GL::COLOR_ATTACHMENT0, color);
122
123         depth.storage(GL::DEPTH_COMPONENT, width, height);
124         fbo.attach(GL::DEPTH_ATTACHMENT, depth);
125 }
126
127
128 StereoView::Eye::Eye():
129         target(0)
130 { }
131
132 StereoView::Eye::~Eye()
133 {
134         delete target;
135 }
136
137 void StereoView::Eye::create_target(unsigned w, unsigned h)
138 {
139         delete target;
140         target = new RenderTarget(w, h);
141 }
142
143 void StereoView::Eye::setup_frame(const GL::Camera &base_camera, float frustum_skew, const EyeParams &params) const
144 {
145         GL::Matrix matrix = base_camera.get_object_matrix()*offset_matrix;
146         camera.set_position(matrix*GL::Vector3());
147         camera.set_up_direction((matrix*GL::Vector4(0, 1, 0, 0)).slice<3>(0));
148         camera.set_look_direction((matrix*GL::Vector4(0, 0, -1, 0)).slice<3>(0));
149
150         camera.set_field_of_view(params.fov);
151         camera.set_aspect(params.aspect);
152         camera.set_depth_clip(params.near_clip, params.far_clip);
153         camera.set_frustum_axis(frustum_skew, 0);
154 }
155
156 void StereoView::Eye::render(const GL::Renderable &renderable) const
157 {
158         GL::Bind bind_fbo(target->fbo);
159         target->fbo.clear(GL::COLOR_BUFFER_BIT|GL::DEPTH_BUFFER_BIT);
160         GL::Renderer renderer(&camera);
161         renderable.render(renderer);
162 }
163
164 } // namespace VR
165 } // namespace Msp