]> git.mxchange.org Git - simgear.git/blob - simgear/scene/sky/CloudShaderGeometry.cxx
Added some OSG headers for the correct evaluation of the OSG_VERSION_LESS_THAN macro.
[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.empty()) 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::ref_ptr<osg::Vec3Array> v = new osg::Vec3Array;
171     osg::ref_ptr<osg::Vec3Array> n = new osg::Vec3Array;
172     osg::ref_ptr<osg::Vec4Array> c = new osg::Vec4Array;
173     osg::ref_ptr<osg::Vec2Array> t = new osg::Vec2Array;
174     
175     int idx = 0;
176
177     for (CloudShaderGeometry::CloudSpriteList::iterator iter = _cloudsprites.begin();
178          iter != _cloudsprites.end();
179          ++iter) 
180     {
181     
182         float cw = 0.5f * iter->width;
183         float ch = 0.5f * iter->height;        
184         
185         // Create the vertices
186         v->push_back(osg::Vec3(0.0f, -cw, -ch));
187         v->push_back(osg::Vec3(0.0f,  cw, -ch));
188         v->push_back(osg::Vec3(0.0f,  cw, ch));
189         v->push_back(osg::Vec3(0.0f, -cw, ch));
190         
191         // The normals aren't actually used in lighting,
192         // but we set them per vertex as this is more
193         // efficient than an overall binding on some
194         // graphics cards.
195         n->push_back(osg::Vec3(1.0f, -1.0f, -1.0f));
196         n->push_back(osg::Vec3(1.0f,  1.0f, -1.0f));
197         n->push_back(osg::Vec3(1.0f,  1.0f,  1.0f));
198         n->push_back(osg::Vec3(1.0f, -1.0f,  1.0f));
199
200         // Set the texture coords for each vertex
201         // from the texture index, and the number
202         // of textures in the image    
203         int x = iter->texture_index_x;
204         int y = iter->texture_index_y;
205         
206         t->push_back(osg::Vec2( (float) x       / varieties_x, (float) y / varieties_y));
207         t->push_back(osg::Vec2( (float) (x + 1) / varieties_x, (float) y / varieties_y));
208         t->push_back(osg::Vec2( (float) (x + 1) / varieties_x, (float) (y + 1) / varieties_y));
209         t->push_back(osg::Vec2( (float) x       / varieties_x, (float) (y + 1) / varieties_y));
210         
211         // The color isn't actually use in lighting, but instead to indicate the center of rotation
212         c->push_back(osg::Vec4(iter->position.x(), iter->position.y(), iter->position.z(), zscale));
213         c->push_back(osg::Vec4(iter->position.x(), iter->position.y(), iter->position.z(), zscale));
214         c->push_back(osg::Vec4(iter->position.x(), iter->position.y(), iter->position.z(), zscale));
215         c->push_back(osg::Vec4(iter->position.x(), iter->position.y(), iter->position.z(), zscale));
216         
217         idx++;      
218     }
219     
220     //Quads now created, add it to the geometry.
221     osg::Geometry* geom = new osg::Geometry;
222     geom->setVertexArray(v);
223     geom->setTexCoordArray(0, t);
224     geom->setNormalArray(n);
225     geom->setNormalBinding(Geometry::BIND_PER_VERTEX);
226     geom->setColorArray(c);
227     geom->setColorBinding(Geometry::BIND_PER_VERTEX);
228     geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,numsprites*4));
229     _geometry = geom;
230 }
231
232 bool CloudShaderGeometry_readLocalData(Object& obj, Input& fr)
233 {
234     bool iteratorAdvanced = false;
235
236     CloudShaderGeometry& geom = static_cast<CloudShaderGeometry&>(obj);
237
238     if ((fr[0].matchWord("geometry"))) {
239         ++fr;
240         iteratorAdvanced = true;
241         osg::Drawable* drawable = fr.readDrawable();
242         if (drawable) {
243             geom._geometry = drawable;
244         }
245     }
246     if ((fr.matchSequence("instances %i"))) {
247         int entry = fr[0].getNoNestedBrackets();
248         int capacity;
249         fr[1].getInt(capacity);
250         geom._cloudsprites.reserve(capacity);
251         fr += 3;
252         iteratorAdvanced = true;
253         // skip {
254         while (!fr.eof() && fr[0].getNoNestedBrackets() > entry) {
255             SGVec3f v;
256             int tx, ty;
257             float w, h;
258             if (fr[0].getFloat(v.x()) && fr[1].getFloat(v.y())
259                 && fr[2].getFloat(v.z()) && fr[3].getInt(tx) && fr[4].getInt(ty) &&  
260                 fr[5].getFloat(w) && fr[6].getFloat(h)) {
261                     fr += 5;
262                     //SGVec3f* v = new SGVec3f(v.x(), v.y(), v.z());
263                     geom._cloudsprites.push_back(CloudShaderGeometry::CloudSprite(v, tx, ty, w, h));
264             } else {
265                 ++fr;
266             }
267         }
268         geom.generateGeometry();
269     }
270     return iteratorAdvanced;
271 }
272
273 bool CloudShaderGeometry_writeLocalData(const Object& obj, Output& fw)
274 {
275     const CloudShaderGeometry& geom = static_cast<const CloudShaderGeometry&>(obj);
276
277     fw.indent() << "geometry" << std::endl;
278     fw.writeObject(*geom._geometry);
279     fw.indent() << "instances " << geom._cloudsprites.size() << std::endl;
280     fw.indent() << "{" << std::endl;
281     fw.moveIn();
282     for (CloudShaderGeometry::CloudSpriteList::const_iterator itr
283              = geom._cloudsprites.begin();
284          itr != geom._cloudsprites.end();
285          ++itr) {
286              fw.indent() << itr->position.x() << " " << itr->position.y() << " " 
287                      << itr->position.z() << " " << itr->texture_index_x << " "
288                      << itr->texture_index_y << " "  << itr->width << " " 
289                      << itr->height << " " << std::endl;
290     }
291     fw.moveOut();
292     fw.indent() << "}" << std::endl;
293     return true;
294 }
295
296
297 osgDB::RegisterDotOsgWrapperProxy cloudShaderGeometryProxy
298 (
299     new CloudShaderGeometry,
300     "CloudShaderGeometry",
301     "Object Drawable CloudShaderGeometry",
302     &CloudShaderGeometry_readLocalData,
303     &CloudShaderGeometry_writeLocalData
304     );
305 }