3 * Copyright (C) 2008 Stuart Buchanan
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.
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.
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,
24 #include <osgDB/Registry>
25 #include <osgDB/Input>
26 #include <osgDB/ParameterOutput>
28 #include "CloudShaderGeometry.hxx"
30 #include <simgear/props/props.hxx>
33 using namespace osgDB;
34 using namespace simgear;
40 bool operator() (const CloudShaderGeometry::SortData::SortItem& lhs,
41 const CloudShaderGeometry::SortData::SortItem& rhs) const
43 return lhs.depth > rhs.depth;
49 void CloudShaderGeometry::drawImplementation(RenderInfo& renderInfo) const
51 if (!_cloudsprites.size()) return;
53 osg::State& state = *renderInfo.getState();
55 int frameNumber = state.getFrameStamp()->getFrameNumber();
56 unsigned int contextID = state.getContextID();
57 SortData& sortData = _sortData[contextID];
58 Geometry* g = _geometry->asGeometry();
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
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();
70 osg::Vec4Array* c = dynamic_cast<osg::Vec4Array*>(g->getColorArray());
71 osg::Vec2Array* t = dynamic_cast<osg::Vec2Array*>(g->getTexCoordArray(0));
76 // Perform a single pass bubble sort of the array,
77 // keeping track of whether we've had to make any changes
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.
84 Vec4f aPos = Vec4f(a.x(), a.y(), a.z(), 1.0f) * mvp;
86 Vec4f bPos = Vec4f(b.x(), b.y(), b.z(), 1.0f) * mvp;
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++) {
96 (*v)[i+j -4] = (*v)[i+j];
97 (*c)[i+j -4] = (*c)[i+j];
98 (*t)[i+j -4] = (*t)[i+j];
105 // Indicate that the arrays were not sorted
106 // so we should check them next iteration
112 // This cloud is sorted, so no need to re-sort.
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;
120 if (sortData.skip_limit > 500) {
121 // Maximum of every 500 frames (10-20 seconds)
122 sortData.skip_limit = 500 + sg_random() * 10;
125 sortData.skip_limit = 1;
128 sortData.frameSorted = frameNumber;
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 };
139 extensions->glVertexAttrib3fv(USR_ATTR_1, ua1 );
140 extensions->glVertexAttrib3fv(USR_ATTR_2, ua2 );
141 _geometry->draw(renderInfo);
144 void CloudShaderGeometry::addSprite(const SGVec3f& p, int tx, int ty,
145 float w, float h, float cull)
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();
153 if ((iter != _cloudsprites.begin()) &&
154 (distSqr(iter->position, p) < cull)) {
155 // Too close - cull it
160 _cloudsprites.push_back(CloudSprite(p, tx, ty, w, h));
163 void CloudShaderGeometry::generateGeometry()
165 // Generate a set of geometries as a QuadStrip based on the list of sprites
166 int numsprites = _cloudsprites.size();
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;
177 for (CloudShaderGeometry::CloudSpriteList::iterator iter = _cloudsprites.begin();
178 iter != _cloudsprites.end();
182 float cw = 0.5f * iter->width;
183 float ch = 0.5f * iter->height;
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));
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
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));
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;
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));
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));
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));
232 bool CloudShaderGeometry_readLocalData(Object& obj, Input& fr)
234 bool iteratorAdvanced = false;
236 CloudShaderGeometry& geom = static_cast<CloudShaderGeometry&>(obj);
238 if ((fr[0].matchWord("geometry"))) {
240 iteratorAdvanced = true;
241 osg::Drawable* drawable = fr.readDrawable();
243 geom._geometry = drawable;
246 if ((fr.matchSequence("instances %i"))) {
247 int entry = fr[0].getNoNestedBrackets();
249 fr[1].getInt(capacity);
250 geom._cloudsprites.reserve(capacity);
252 iteratorAdvanced = true;
254 while (!fr.eof() && fr[0].getNoNestedBrackets() > entry) {
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)) {
262 //SGVec3f* v = new SGVec3f(v.x(), v.y(), v.z());
263 geom._cloudsprites.push_back(CloudShaderGeometry::CloudSprite(v, tx, ty, w, h));
268 geom.generateGeometry();
270 return iteratorAdvanced;
273 bool CloudShaderGeometry_writeLocalData(const Object& obj, Output& fw)
275 const CloudShaderGeometry& geom = static_cast<const CloudShaderGeometry&>(obj);
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;
282 for (CloudShaderGeometry::CloudSpriteList::const_iterator itr
283 = geom._cloudsprites.begin();
284 itr != geom._cloudsprites.end();
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;
292 fw.indent() << "}" << std::endl;
297 osgDB::RegisterDotOsgWrapperProxy cloudShaderGeometryProxy
299 new CloudShaderGeometry,
300 "CloudShaderGeometry",
301 "Object Drawable CloudShaderGeometry",
302 &CloudShaderGeometry_readLocalData,
303 &CloudShaderGeometry_writeLocalData