]> git.mxchange.org Git - simgear.git/blob - simgear/scene/sky/CloudShaderGeometry.cxx
Revert "Use simgear internal stuff for the singleton class."
[simgear.git] / simgear / scene / sky / CloudShaderGeometry.cxx
1 /* -*-c++-*-
2  *
3  * Copyright (C) 2008 Stuart Buchanan
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 #include <algorithm>
23
24 #include <osgDB/Registry>
25 #include <osgDB/Input>
26 #include <osgDB/ParameterOutput>
27
28 #include "CloudShaderGeometry.hxx"
29
30 #include <simgear/props/props.hxx>
31
32 using namespace osg;
33 using namespace osgDB;
34 using namespace simgear;
35
36 namespace
37 {
38 struct SpriteComp
39 {
40     bool operator() (const CloudShaderGeometry::SortData::SortItem& lhs,
41                      const CloudShaderGeometry::SortData::SortItem& rhs) const
42     {
43         return lhs.depth > rhs.depth;
44     }
45 };
46 }
47 namespace simgear
48 {
49 void CloudShaderGeometry::drawImplementation(RenderInfo& renderInfo) const
50 {
51     if (!_cloudsprites.size()) return;
52     
53     osg::State& state = *renderInfo.getState();
54     
55     int frameNumber = state.getFrameStamp()->getFrameNumber();
56     unsigned int contextID = state.getContextID();    
57     SortData& sortData = _sortData[contextID];
58     Geometry* g = _geometry->asGeometry();
59     
60     // If the cloud is already sorted, then it is likely to still be sorted.
61     // Therefore we can avoid re-sorting it for a period. If it is still
62     // sorted after that period, then we can wait for a longer period before
63     // checking again. In this way, only clouds that are changing regularly
64     // are sorted.        
65     osg::Vec3Array* v = dynamic_cast<osg::Vec3Array*>(g->getVertexArray());
66     if ((v->size() > 4) && 
67         (frameNumber - sortData.skip_limit >= sortData.frameSorted)) {
68         Matrix mvp = state.getModelViewMatrix() * state.getProjectionMatrix();
69         
70         osg::Vec4Array* c = dynamic_cast<osg::Vec4Array*>(g->getColorArray());
71         osg::Vec2Array* t = dynamic_cast<osg::Vec2Array*>(g->getTexCoordArray(0));
72         Vec3f av[4];
73         Vec4f ac[4];
74         Vec2f at[4];        
75         
76         // Perform a single pass bubble sort of the array, 
77         // keeping track of whether we've had to make any changes
78         bool sorted = true;                      
79         for (unsigned int i = 4; i < v->size(); i = i + 4) {
80             // The position of the sprite is stored in the colour
81             // array, with the exception of the w() coordinate
82             // which is the z-scaling parameter.
83             Vec4f a = (*c)[i-4];
84             Vec4f aPos = Vec4f(a.x(), a.y(), a.z(), 1.0f) * mvp;
85             Vec4f b = (*c)[i];
86             Vec4f bPos = Vec4f(b.x(), b.y(), b.z(), 1.0f) * mvp;
87             
88             if ((aPos.z()/aPos.w()) < (bPos.z()/bPos.w() - 0.0001)) {
89                 // a is non-trivially closer than b, so should be rendered
90                 // later. Swap them around
91                 for (int j = 0; j < 4; j++) {
92                     av[j] = (*v)[i+j-4];
93                     ac[j] = (*c)[i+j-4];
94                     at[j] = (*t)[i+j-4];
95                     
96                     (*v)[i+j -4] = (*v)[i+j];
97                     (*c)[i+j -4] = (*c)[i+j];
98                     (*t)[i+j -4] = (*t)[i+j];
99                     
100                     (*v)[i+j] = av[j];
101                     (*c)[i+j] = ac[j];
102                     (*t)[i+j] = at[j];
103                 }
104                 
105                 // Indicate that the arrays were not sorted
106                 // so we should check them next iteration
107                 sorted = false;
108             }
109         }
110         
111         if (sorted) {
112             // This cloud is sorted, so no need to re-sort.
113             
114             sortData.skip_limit = sortData.skip_limit * 2;
115             if (sortData.skip_limit > 30) {
116                 // Jitter the skip frames to avoid synchronized sorts
117                 // which will cause periodic frame-rate drops
118                 sortData.skip_limit += sg_random() * 10;
119             }
120             if (sortData.skip_limit > 500) {
121                 // Maximum of every 500 frames (10-20 seconds)
122                 sortData.skip_limit = 500 + sg_random() * 10;
123             }
124         } else {
125             sortData.skip_limit = 1;
126         }
127         
128         sortData.frameSorted = frameNumber;
129     }
130     
131     const Extensions* extensions = getExtensions(state.getContextID(),true);
132     GLfloat ua1[3] = { (GLfloat) 1.0f,
133                        (GLfloat) shade_factor,
134                        (GLfloat) cloud_height };
135     GLfloat ua2[3] = { (GLfloat) bottom_factor,
136                        (GLfloat) middle_factor,
137                        (GLfloat) top_factor };
138                        
139     extensions->glVertexAttrib3fv(USR_ATTR_1, ua1 );
140     extensions->glVertexAttrib3fv(USR_ATTR_2, ua2 );
141     _geometry->draw(renderInfo);    
142 }
143
144 void CloudShaderGeometry::addSprite(const SGVec3f& p, int tx, int ty,
145                                     float w, float h, float cull)
146 {
147     // Only add the sprite if it is further than the cull distance to all other sprites
148     // except for the center sprite.    
149     for (CloudShaderGeometry::CloudSpriteList::iterator iter = _cloudsprites.begin();
150          iter != _cloudsprites.end();
151          ++iter) 
152     {
153         if ((iter != _cloudsprites.begin()) &&
154             (distSqr(iter->position, p) < cull)) {
155             // Too close - cull it
156             return;
157         }
158     }
159     
160     _cloudsprites.push_back(CloudSprite(p, tx, ty, w, h));
161 }
162
163 void CloudShaderGeometry::generateGeometry()
164 {
165     // Generate a set of geometries as a QuadStrip based on the list of sprites
166     int numsprites = _cloudsprites.size();
167     
168     // Create front and back polygons so we don't need to screw around
169     // with two-sided lighting in the shader.
170     osg::Vec3Array& v = *(new osg::Vec3Array(4 * numsprites));
171     osg::Vec4Array& c = *(new osg::Vec4Array(4 * numsprites));
172     osg::Vec2Array& t = *(new osg::Vec2Array(4 * numsprites));
173     
174     int idx = 0;
175
176     for (CloudShaderGeometry::CloudSpriteList::iterator iter = _cloudsprites.begin();
177          iter != _cloudsprites.end();
178          ++iter) 
179     {
180     
181         float cw = 0.5f * iter->width;
182         float ch = 0.5f * iter->height;        
183         
184         // Create the vertices
185         v[4*idx  ].set(0.0f, -cw, -ch);
186         v[4*idx+1].set(0.0f,  cw, -ch);
187         v[4*idx+2].set(0.0f,  cw, ch);
188         v[4*idx+3].set(0.0f, -cw, ch);
189         
190         // Set the texture coords for each vertex
191         // from the texture index, and the number
192         // of textures in the image    
193         int x = iter->texture_index_x;
194         int y = iter->texture_index_y;
195         
196         t[4*idx  ].set( (float) x       / varieties_x, (float) y / varieties_y);
197         t[4*idx+1].set( (float) (x + 1) / varieties_x, (float) y / varieties_y);
198         t[4*idx+2].set( (float) (x + 1) / varieties_x, (float) (y + 1) / varieties_y);
199         t[4*idx+3].set( (float) x       / varieties_x, (float) (y + 1) / varieties_y);
200
201         // The color isn't actually use in lighting, but instead to indicate the center of rotation
202         c[4*idx  ].set(iter->position.x(), iter->position.y(), iter->position.z(), zscale);
203         c[4*idx+1].set(iter->position.x(), iter->position.y(), iter->position.z(), zscale);
204         c[4*idx+2].set(iter->position.x(), iter->position.y(), iter->position.z(), zscale);
205         c[4*idx+3].set(iter->position.x(), iter->position.y(), iter->position.z(), zscale);
206         
207         idx++;      
208     }
209     
210     //Quads now created, add it to the geometry.
211     osg::Geometry* geom = new osg::Geometry;
212     geom->setVertexArray(&v);
213     geom->setTexCoordArray(0, &t);
214     
215     // The normal isn't actually use in lighting, so we simply bind overall.
216     osg::Vec3Array& n = *(new osg::Vec3Array(4));
217     n[0].set(1.0f, -1.0f, -1.0f);
218     n[1].set(1.0f,  1.0f, -1.0f);
219     n[2].set(1.0f,  1.0f,  1.0f);
220     n[3].set(1.0f, -1.0f,  1.0f);
221     
222     geom->setNormalArray(&n);
223     geom->setNormalBinding(Geometry::BIND_OVERALL);
224     geom->setColorArray(&c);
225     geom->setColorBinding(Geometry::BIND_PER_VERTEX);
226     geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,numsprites*4));
227     _geometry = geom;
228 }
229
230 bool CloudShaderGeometry_readLocalData(Object& obj, Input& fr)
231 {
232     bool iteratorAdvanced = false;
233
234     CloudShaderGeometry& geom = static_cast<CloudShaderGeometry&>(obj);
235
236     if ((fr[0].matchWord("geometry"))) {
237         ++fr;
238         iteratorAdvanced = true;
239         osg::Drawable* drawable = fr.readDrawable();
240         if (drawable) {
241             geom._geometry = drawable;
242         }
243     }
244     if ((fr.matchSequence("instances %i"))) {
245         int entry = fr[0].getNoNestedBrackets();
246         int capacity;
247         fr[1].getInt(capacity);
248         geom._cloudsprites.reserve(capacity);
249         fr += 3;
250         iteratorAdvanced = true;
251         // skip {
252         while (!fr.eof() && fr[0].getNoNestedBrackets() > entry) {
253             SGVec3f v;
254             int tx, ty;
255             float w, h;
256             if (fr[0].getFloat(v.x()) && fr[1].getFloat(v.y())
257                 && fr[2].getFloat(v.z()) && fr[3].getInt(tx) && fr[4].getInt(ty) &&  
258                 fr[5].getFloat(w) && fr[6].getFloat(h)) {
259                     fr += 5;
260                     //SGVec3f* v = new SGVec3f(v.x(), v.y(), v.z());
261                     geom._cloudsprites.push_back(CloudShaderGeometry::CloudSprite(v, tx, ty, w, h));
262             } else {
263                 ++fr;
264             }
265         }
266         geom.generateGeometry();
267     }
268     return iteratorAdvanced;
269 }
270
271 bool CloudShaderGeometry_writeLocalData(const Object& obj, Output& fw)
272 {
273     const CloudShaderGeometry& geom = static_cast<const CloudShaderGeometry&>(obj);
274
275     fw.indent() << "geometry" << std::endl;
276     fw.writeObject(*geom._geometry);
277     fw.indent() << "instances " << geom._cloudsprites.size() << std::endl;
278     fw.indent() << "{" << std::endl;
279     fw.moveIn();
280     for (CloudShaderGeometry::CloudSpriteList::const_iterator itr
281              = geom._cloudsprites.begin();
282          itr != geom._cloudsprites.end();
283          ++itr) {
284              fw.indent() << itr->position.x() << " " << itr->position.y() << " " 
285                      << itr->position.z() << " " << itr->texture_index_x << " "
286                      << itr->texture_index_y << " "  << itr->width << " " 
287                      << itr->height << " " << std::endl;
288     }
289     fw.moveOut();
290     fw.indent() << "}" << std::endl;
291     return true;
292 }
293
294
295 osgDB::RegisterDotOsgWrapperProxy cloudShaderGeometryProxy
296 (
297     new CloudShaderGeometry,
298     "CloudShaderGeometry",
299     "Object Drawable CloudShaderGeometry",
300     &CloudShaderGeometry_readLocalData,
301     &CloudShaderGeometry_writeLocalData
302     );
303 }