1 /* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
2  *
3  * This library is open source and may be redistributed and/or modified under
4  * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
5  * (at your option) any later version.  The full license is in LICENSE file
6  * included with this distribution, and on the openscenegraph.org website.
7  *
8  * This library is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * OpenSceneGraph Public License for more details.
12 */
13 
14 #include <osgViewer/config/SphericalDisplay>
15 #include <osgViewer/Renderer>
16 #include <osgViewer/View>
17 #include <osgViewer/GraphicsWindow>
18 
19 #include <osg/io_utils>
20 
21 #include <osg/TextureCubeMap>
22 #include <osg/TextureRectangle>
23 #include <osg/Texture1D>
24 #include <osg/Texture2D>
25 #include <osg/TexMat>
26 #include <osg/Stencil>
27 #include <osg/PolygonStipple>
28 #include <osg/ValueObject>
29 
30 using namespace osgViewer;
31 
create3DSphericalDisplayDistortionMesh(const osg::Vec3 & origin,const osg::Vec3 & widthVector,const osg::Vec3 & heightVector,double sphere_radius,double collar_radius,osg::Image * intensityMap,const osg::Matrix & projectorMatrix) const32 osg::Geometry* SphericalDisplay::create3DSphericalDisplayDistortionMesh(const osg::Vec3& origin, const osg::Vec3& widthVector, const osg::Vec3& heightVector, double sphere_radius, double collar_radius,osg::Image* intensityMap, const osg::Matrix& projectorMatrix) const
33 {
34     osg::Vec3d center(0.0,0.0,0.0);
35     osg::Vec3d eye(0.0,0.0,0.0);
36 
37     double distance = sqrt(sphere_radius*sphere_radius - collar_radius*collar_radius);
38 
39     bool centerProjection = false;
40 
41     osg::Vec3d projector = eye - osg::Vec3d(0.0,0.0, distance);
42 
43     OSG_INFO<<"create3DSphericalDisplayDistortionMesh : Projector position = "<<projector<<std::endl;
44     OSG_INFO<<"create3DSphericalDisplayDistortionMesh : distance = "<<distance<<std::endl;
45 
46 
47     // create the quad to visualize.
48     osg::Geometry* geometry = new osg::Geometry();
49 
50     geometry->setSupportsDisplayList(false);
51 
52     osg::Vec3 xAxis(widthVector);
53     float width = widthVector.length();
54     xAxis /= width;
55 
56     osg::Vec3 yAxis(heightVector);
57     float height = heightVector.length();
58     yAxis /= height;
59 
60     int noSteps = 50;
61 
62     osg::Vec3Array* vertices = new osg::Vec3Array;
63     osg::Vec3Array* texcoords0 = new osg::Vec3Array;
64     osg::Vec2Array* texcoords1 = intensityMap==0 ? new osg::Vec2Array : 0;
65     osg::Vec4Array* colors = new osg::Vec4Array;
66 
67     osg::Vec3 bottom = origin;
68     osg::Vec3 dx = xAxis*(width/((float)(noSteps-1)));
69     osg::Vec3 dy = yAxis*(height/((float)(noSteps-1)));
70 
71     osg::Vec3d screenCenter = origin + widthVector*0.5f + heightVector*0.5f;
72     float screenRadius = heightVector.length() * 0.5f;
73 
74     int i,j;
75 
76     if (centerProjection)
77     {
78         for(i=0;i<noSteps;++i)
79         {
80             osg::Vec3 cursor = bottom+dy*(float)i;
81             for(j=0;j<noSteps;++j)
82             {
83                 osg::Vec2 delta(cursor.x() - screenCenter.x(), cursor.y() - screenCenter.y());
84                 double theta = atan2(-delta.y(), delta.x());
85                 double phi = osg::PI_2 * delta.length() / screenRadius;
86                 if (phi > osg::PI_2) phi = osg::PI_2;
87 
88                 phi *= 2.0;
89 
90                 if (theta<0.0) theta += 2.0*osg::PI;
91 
92                 // OSG_NOTICE<<"theta = "<<theta<< "phi="<<phi<<std::endl;
93 
94                 osg::Vec3 texcoord(sin(phi) * cos(theta),
95                                    sin(phi) * sin(theta),
96                                    cos(phi));
97 
98                 vertices->push_back(cursor);
99                 texcoords0->push_back(texcoord * projectorMatrix);
100 
101                 osg::Vec2 texcoord1(theta/(2.0*osg::PI), 1.0f - phi/osg::PI_2);
102                 if (intensityMap)
103                 {
104                     colors->push_back(intensityMap->getColor(texcoord1));
105                 }
106                 else
107                 {
108                     colors->push_back(osg::Vec4(1.0f,1.0f,1.0f,1.0f));
109                     if (texcoords1) texcoords1->push_back( texcoord1 );
110                 }
111 
112                 cursor += dx;
113             }
114             // OSG_NOTICE<<std::endl;
115         }
116     }
117     else
118     {
119         for(i=0;i<noSteps;++i)
120         {
121             osg::Vec3 cursor = bottom+dy*(float)i;
122             for(j=0;j<noSteps;++j)
123             {
124                 osg::Vec2 delta(cursor.x() - screenCenter.x(), cursor.y() - screenCenter.y());
125                 double theta = atan2(-delta.y(), delta.x());
126                 double phi = osg::PI_2 * delta.length() / screenRadius;
127                 if (phi > osg::PI_2) phi = osg::PI_2;
128                 if (theta<0.0) theta += 2.0*osg::PI;
129 
130                 // OSG_NOTICE<<"theta = "<<theta<< "phi="<<phi<<std::endl;
131 
132                 double f = distance * sin(phi);
133                 double e = distance * cos(phi) + sqrt( sphere_radius*sphere_radius - f*f);
134                 double l = e * cos(phi);
135                 double h = e * sin(phi);
136                 double z = l - distance;
137 
138                 osg::Vec3 texcoord(h * cos(theta) / sphere_radius,
139                                    h * sin(theta) / sphere_radius,
140                                    z / sphere_radius);
141 
142                 vertices->push_back(cursor);
143                 texcoords0->push_back(texcoord * projectorMatrix);
144 
145                 osg::Vec2 texcoord1(theta/(2.0*osg::PI), 1.0f - phi/osg::PI_2);
146                 if (intensityMap)
147                 {
148                     colors->push_back(intensityMap->getColor(texcoord1));
149                 }
150                 else
151                 {
152                     colors->push_back(osg::Vec4(1.0f,1.0f,1.0f,1.0f));
153                     if (texcoords1) texcoords1->push_back( texcoord1 );
154                 }
155 
156                 cursor += dx;
157             }
158             // OSG_NOTICE<<std::endl;
159         }
160     }
161 
162     // pass the created vertex array to the points geometry object.
163     geometry->setVertexArray(vertices);
164 
165     geometry->setColorArray(colors, osg::Array::BIND_PER_VERTEX);
166 
167     geometry->setTexCoordArray(0,texcoords0);
168     if (texcoords1) geometry->setTexCoordArray(1,texcoords1);
169 
170     for(i=0;i<noSteps-1;++i)
171     {
172         osg::DrawElementsUShort* elements = new osg::DrawElementsUShort(osg::PrimitiveSet::QUAD_STRIP);
173         for(j=0;j<noSteps;++j)
174         {
175             elements->push_back(j+(i+1)*noSteps);
176             elements->push_back(j+(i)*noSteps);
177         }
178         geometry->addPrimitiveSet(elements);
179     }
180 
181     return geometry;
182 }
183 
configure(osgViewer::View & view) const184 void SphericalDisplay::configure(osgViewer::View& view) const
185 {
186     OSG_INFO<<"SphericalDisplay::configure(rad="<<_radius<<", cllr="<<_collar<<", sn="<<_screenNum<<", im="<<_intensityMap<<")"<<std::endl;
187     osg::GraphicsContext::WindowingSystemInterface* wsi = osg::GraphicsContext::getWindowingSystemInterface();
188     if (!wsi)
189     {
190         OSG_NOTICE<<"Error, no WindowSystemInterface available, cannot create windows."<<std::endl;
191         return;
192     }
193 
194     osg::GraphicsContext::ScreenIdentifier si;
195     si.readDISPLAY();
196 
197     // displayNum has not been set so reset it to 0.
198     if (si.displayNum<0) si.displayNum = 0;
199 
200     si.screenNum = _screenNum;
201 
202     unsigned int width, height;
203     wsi->getScreenResolution(si, width, height);
204 
205 
206     osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;
207     traits->hostName = si.hostName;
208     traits->displayNum = si.displayNum;
209     traits->screenNum = si.screenNum;
210     traits->x = 0;
211     traits->y = 0;
212     traits->width = width;
213     traits->height = height;
214     traits->windowDecoration = false;
215     traits->doubleBuffer = true;
216     traits->sharedContext = 0;
217 
218 
219     osg::ref_ptr<osg::GraphicsContext> gc = osg::GraphicsContext::createGraphicsContext(traits.get());
220     if (!gc)
221     {
222         OSG_NOTICE<<"GraphicsWindow has not been created successfully."<<std::endl;
223         return;
224     }
225 
226     bool applyIntensityMapAsColours = true;
227 
228     int tex_width = 512;
229     int tex_height = 512;
230 
231     int camera_width = tex_width;
232     int camera_height = tex_height;
233 
234     osg::TextureCubeMap* texture = new osg::TextureCubeMap;
235 
236     texture->setTextureSize(tex_width, tex_height);
237     texture->setInternalFormat(GL_RGB);
238     texture->setFilter(osg::Texture::MIN_FILTER,osg::Texture::LINEAR);
239     texture->setFilter(osg::Texture::MAG_FILTER,osg::Texture::LINEAR);
240     texture->setWrap(osg::Texture::WRAP_S,osg::Texture::CLAMP_TO_EDGE);
241     texture->setWrap(osg::Texture::WRAP_T,osg::Texture::CLAMP_TO_EDGE);
242     texture->setWrap(osg::Texture::WRAP_R,osg::Texture::CLAMP_TO_EDGE);
243 
244 #if 0
245     osg::Camera::RenderTargetImplementation renderTargetImplementation = osg::Camera::SEPERATE_WINDOW;
246     GLenum buffer = GL_FRONT;
247 #else
248     osg::Camera::RenderTargetImplementation renderTargetImplementation = osg::Camera::FRAME_BUFFER_OBJECT;
249     GLenum buffer = GL_FRONT;
250 #endif
251 
252     // front face
253     {
254         osg::ref_ptr<osg::Camera> camera = new osg::Camera;
255         camera->setName("Front face camera");
256         camera->setGraphicsContext(gc.get());
257         camera->setViewport(new osg::Viewport(0,0,camera_width, camera_height));
258         camera->setDrawBuffer(buffer);
259         camera->setReadBuffer(buffer);
260         camera->setAllowEventFocus(false);
261         // tell the camera to use OpenGL frame buffer object where supported.
262         camera->setRenderTargetImplementation(renderTargetImplementation);
263 
264         // attach the texture and use it as the color buffer.
265         camera->attach(osg::Camera::COLOR_BUFFER, texture, 0, osg::TextureCubeMap::POSITIVE_Y);
266 
267         view.addSlave(camera.get(), osg::Matrixd(), osg::Matrixd());
268     }
269 
270 
271     // top face
272     {
273         osg::ref_ptr<osg::Camera> camera = new osg::Camera;
274         camera->setName("Top face camera");
275         camera->setGraphicsContext(gc.get());
276         camera->setViewport(new osg::Viewport(0,0,camera_width, camera_height));
277         camera->setDrawBuffer(buffer);
278         camera->setReadBuffer(buffer);
279         camera->setAllowEventFocus(false);
280 
281         // tell the camera to use OpenGL frame buffer object where supported.
282         camera->setRenderTargetImplementation(renderTargetImplementation);
283 
284         // attach the texture and use it as the color buffer.
285         camera->attach(osg::Camera::COLOR_BUFFER, texture, 0, osg::TextureCubeMap::POSITIVE_Z);
286 
287         view.addSlave(camera.get(), osg::Matrixd(), osg::Matrixd::rotate(osg::inDegrees(-90.0f), 1.0,0.0,0.0));
288     }
289 
290     // left face
291     {
292         osg::ref_ptr<osg::Camera> camera = new osg::Camera;
293         camera->setName("Left face camera");
294         camera->setGraphicsContext(gc.get());
295         camera->setViewport(new osg::Viewport(0,0,camera_width, camera_height));
296         camera->setDrawBuffer(buffer);
297         camera->setReadBuffer(buffer);
298         camera->setAllowEventFocus(false);
299 
300         // tell the camera to use OpenGL frame buffer object where supported.
301         camera->setRenderTargetImplementation(renderTargetImplementation);
302 
303         // attach the texture and use it as the color buffer.
304         camera->attach(osg::Camera::COLOR_BUFFER, texture, 0, osg::TextureCubeMap::NEGATIVE_X);
305 
306         view.addSlave(camera.get(), osg::Matrixd(), osg::Matrixd::rotate(osg::inDegrees(-90.0f), 0.0,1.0,0.0) * osg::Matrixd::rotate(osg::inDegrees(-90.0f), 0.0,0.0,1.0));
307     }
308 
309     // right face
310     {
311         osg::ref_ptr<osg::Camera> camera = new osg::Camera;
312         camera->setName("Right face camera");
313         camera->setGraphicsContext(gc.get());
314         camera->setViewport(new osg::Viewport(0,0,camera_width, camera_height));
315         camera->setDrawBuffer(buffer);
316         camera->setReadBuffer(buffer);
317         camera->setAllowEventFocus(false);
318 
319         // tell the camera to use OpenGL frame buffer object where supported.
320         camera->setRenderTargetImplementation(renderTargetImplementation);
321 
322         // attach the texture and use it as the color buffer.
323         camera->attach(osg::Camera::COLOR_BUFFER, texture, 0, osg::TextureCubeMap::POSITIVE_X);
324 
325         view.addSlave(camera.get(), osg::Matrixd(), osg::Matrixd::rotate(osg::inDegrees(90.0f), 0.0,1.0,0.0 ) * osg::Matrixd::rotate(osg::inDegrees(90.0f), 0.0,0.0,1.0));
326     }
327 
328     // bottom face
329     {
330         osg::ref_ptr<osg::Camera> camera = new osg::Camera;
331         camera->setGraphicsContext(gc.get());
332         camera->setName("Bottom face camera");
333         camera->setViewport(new osg::Viewport(0,0,camera_width, camera_height));
334         camera->setDrawBuffer(buffer);
335         camera->setReadBuffer(buffer);
336         camera->setAllowEventFocus(false);
337 
338         // tell the camera to use OpenGL frame buffer object where supported.
339         camera->setRenderTargetImplementation(renderTargetImplementation);
340 
341         // attach the texture and use it as the color buffer.
342         camera->attach(osg::Camera::COLOR_BUFFER, texture, 0, osg::TextureCubeMap::NEGATIVE_Z);
343 
344         view.addSlave(camera.get(), osg::Matrixd(), osg::Matrixd::rotate(osg::inDegrees(90.0f), 1.0,0.0,0.0) * osg::Matrixd::rotate(osg::inDegrees(180.0f), 0.0,0.0,1.0));
345     }
346 
347     // back face
348     {
349         osg::ref_ptr<osg::Camera> camera = new osg::Camera;
350         camera->setName("Back face camera");
351         camera->setGraphicsContext(gc.get());
352         camera->setViewport(new osg::Viewport(0,0,camera_width, camera_height));
353         camera->setDrawBuffer(buffer);
354         camera->setReadBuffer(buffer);
355         camera->setAllowEventFocus(false);
356 
357         // tell the camera to use OpenGL frame buffer object where supported.
358         camera->setRenderTargetImplementation(renderTargetImplementation);
359 
360         // attach the texture and use it as the color buffer.
361         camera->attach(osg::Camera::COLOR_BUFFER, texture, 0, osg::TextureCubeMap::NEGATIVE_Y);
362 
363         view.addSlave(camera.get(), osg::Matrixd(), osg::Matrixd::rotate(osg::inDegrees(180.0f), 1.0,0.0,0.0));
364     }
365 
366     view.getCamera()->setProjectionMatrixAsPerspective(90.0f, 1.0, 1, 1000.0);
367 
368     // distortion correction set up.
369     {
370         osg::Geode* geode = new osg::Geode();
371         geode->addDrawable(create3DSphericalDisplayDistortionMesh(osg::Vec3(0.0f,0.0f,0.0f), osg::Vec3(width,0.0f,0.0f), osg::Vec3(0.0f,height,0.0f), _radius, _collar, applyIntensityMapAsColours ? _intensityMap.get() : 0, _projectorMatrix));
372 
373         // new we need to add the texture to the mesh, we do so by creating a
374         // StateSet to contain the Texture StateAttribute.
375         osg::StateSet* stateset = geode->getOrCreateStateSet();
376         stateset->setTextureAttributeAndModes(0, texture,osg::StateAttribute::ON);
377         stateset->setMode(GL_LIGHTING,osg::StateAttribute::OFF);
378 
379         if (!applyIntensityMapAsColours && _intensityMap.valid())
380         {
381             stateset->setTextureAttributeAndModes(1, new osg::Texture2D(_intensityMap.get()), osg::StateAttribute::ON);
382         }
383 
384         osg::ref_ptr<osg::Camera> camera = new osg::Camera;
385         camera->setGraphicsContext(gc.get());
386         camera->setClearMask(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT );
387         camera->setClearColor( osg::Vec4(0.0,0.0,0.0,1.0) );
388         camera->setViewport(new osg::Viewport(0, 0, width, height));
389         GLenum buffer = traits->doubleBuffer ? GL_BACK : GL_FRONT;
390         camera->setDrawBuffer(buffer);
391         camera->setReadBuffer(buffer);
392         camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF);
393         camera->setAllowEventFocus(true);
394         camera->setInheritanceMask(camera->getInheritanceMask() & ~osg::CullSettings::CLEAR_COLOR & ~osg::CullSettings::COMPUTE_NEAR_FAR_MODE);
395         //camera->setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR);
396 
397         camera->setProjectionMatrixAsOrtho2D(0,width,0,height);
398         camera->setViewMatrix(osg::Matrix::identity());
399 
400         // add subgraph to render
401         camera->addChild(geode);
402 
403         camera->setName("DistortionCorrectionCamera");
404 
405         view.addSlave(camera.get(), osg::Matrixd(), osg::Matrixd(), false);
406     }
407 
408     view.getCamera()->setNearFarRatio(0.0001f);
409 
410     if (view.getLightingMode()==osg::View::HEADLIGHT)
411     {
412         // set a local light source for headlight to ensure that lighting is consistent across sides of cube.
413         view.getLight()->setPosition(osg::Vec4(0.0f,0.0f,0.0f,1.0f));
414     }
415 }
416