1 /* -*-c++-*-
2  *
3  * Copyright (C) 2006-2007 Mathias Froehlich
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of the
8  * License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18  * MA 02110-1301, USA.
19  *
20  */
21 
22 #ifdef HAVE_CONFIG_H
23 #  include <simgear_config.h>
24 #endif
25 
26 #include <simgear/compiler.h>
27 
28 #include "SGVasiDrawable.hxx"
29 
30 #include <simgear/scene/util/OsgMath.hxx>
31 
32 struct SGVasiDrawable::LightData {
LightDataSGVasiDrawable::LightData33   LightData(const SGVec3f& p, const SGVec3f& n, const SGVec3f& up) :
34     position(p),
35     normal(n),
36     horizontal(normalize(cross(up, n))),
37     normalCrossHorizontal(normalize(cross(n, horizontal)))
38   { }
39 
drawSGVasiDrawable::LightData40   void draw(const SGVec4f& color) const
41   {
42     glBegin(GL_POINTS);
43     glColor4fv(color.data());
44     glNormal3fv(normal.data());
45     glVertex3fv(position.data());
46     glEnd();
47   }
48 
49   SGVec3f position;
50   SGVec3f normal;
51   SGVec3f horizontal;
52   SGVec3f normalCrossHorizontal;
53 };
54 
SGVasiDrawable(const SGVasiDrawable & vd,const osg::CopyOp &)55 SGVasiDrawable::SGVasiDrawable(const SGVasiDrawable& vd, const osg::CopyOp&) :
56   _lights(vd._lights),
57   _red(vd._red),
58   _white(vd._white)
59 {
60   setUseDisplayList(false);
61   setSupportsDisplayList(false);
62 }
63 
SGVasiDrawable(const SGVec4f & red,const SGVec4f & white)64 SGVasiDrawable::SGVasiDrawable(const SGVec4f& red, const SGVec4f& white) :
65   _red(red),
66   _white(white)
67 {
68   setUseDisplayList(false);
69   setSupportsDisplayList(false);
70 }
71 
72 void
addLight(const SGVec3f & position,const SGVec3f & normal,const SGVec3f & up,float azimutDeg)73 SGVasiDrawable::addLight(const SGVec3f& position, const SGVec3f& normal,
74                          const SGVec3f& up, float azimutDeg)
75 {
76   SGVec3f horizontal(normalize(cross(up, normal)));
77   SGVec3f zeroGlideSlope = normalize(cross(horizontal, up));
78   SGQuatf rotation = SGQuatf::fromAngleAxisDeg(azimutDeg, horizontal);
79   SGVec3f azimutGlideSlope = rotation.transform(zeroGlideSlope);
80   addLight(position, azimutGlideSlope, up);
81 }
82 
83 void
addLight(const SGVec3f & position,const SGVec3f & normal,const SGVec3f & up)84 SGVasiDrawable::addLight(const SGVec3f& position, const SGVec3f& normal,
85                          const SGVec3f& up)
86 {
87   _lights.push_back(LightData(position, normal, up));
88 }
89 
90 void
drawImplementation(osg::RenderInfo & renderInfo) const91 SGVasiDrawable::drawImplementation(osg::RenderInfo& renderInfo) const
92 {
93   // Make sure we have the current state set
94 //   renderInfo.getState()->apply();
95 
96   // Retrieve the eye point in local coords
97   osg::Matrix m;
98   m.invert(renderInfo.getState()->getModelViewMatrix());
99   SGVec3f eyePoint(toSG(m.preMult(osg::Vec3(0, 0, 0))));
100 
101   // paint the points
102   for (unsigned i = 0; i < _lights.size(); ++i)
103     draw(eyePoint, _lights[i]);
104 }
105 
106 osg::BoundingBox
107 #if OSG_VERSION_LESS_THAN(3,3,2)
computeBound() const108 SGVasiDrawable::computeBound()
109 #else
110 SGVasiDrawable::computeBoundingBox()
111 #endif
112 const
113 {
114   osg::BoundingBox bb;
115   for (unsigned i = 0; i < _lights.size(); ++i)
116     bb.expandBy(toOsg(_lights[i].position));
117 
118   // blow up to avoid being victim to small feature culling ...
119   bb.expandBy(bb._min - osg::Vec3(1, 1, 1));
120   bb.expandBy(bb._max + osg::Vec3(1, 1, 1));
121   return bb;
122 }
123 
124 SGVec4f
getColor(float angleDeg) const125 SGVasiDrawable::getColor(float angleDeg) const
126 {
127   float transDeg = 0.05f;
128   if (angleDeg < -transDeg) {
129     return _red;
130   } else if (angleDeg < transDeg) {
131     float fac = angleDeg*0.5f/transDeg + 0.5f;
132     return _red + fac*(_white - _red);
133   } else {
134     return _white;
135   }
136 }
137 
138 void
draw(const SGVec3f & eyePoint,const LightData & light) const139 SGVasiDrawable::draw(const SGVec3f& eyePoint, const LightData& light) const
140 {
141   // vector pointing from the light position to the eye
142   SGVec3f lightToEye = eyePoint - light.position;
143 
144   // dont' draw, we are behind it
145   if (dot(lightToEye, light.normal) < SGLimitsf::min())
146     return;
147 
148   // Now project the eye point vector into the plane defined by the
149   // glideslope direction and the up direction
150   SGVec3f projLightToEye = lightToEye
151     - light.horizontal*dot(lightToEye, light.horizontal);
152 
153   // dont' draw, if we are to near, looks like we are already behind
154   float sqrProjLightToEyeLength = dot(projLightToEye, projLightToEye);
155   if (sqrProjLightToEyeLength < 1e-3*1e-3)
156     return;
157 
158   // the scalar product of the glide slope up direction with the eye vector
159   float dotProd = dot(projLightToEye, light.normalCrossHorizontal);
160   float sinAngle = dotProd/sqrt(sqrProjLightToEyeLength);
161   if (sinAngle < -1)
162     sinAngle = -1;
163   if (1 < sinAngle)
164     sinAngle = 1;
165 
166   float angleDeg = SGMiscf::rad2deg(asin(sinAngle));
167   light.draw(getColor(angleDeg));
168 }
169 
170