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::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));
176 for (CloudShaderGeometry::CloudSpriteList::iterator iter = _cloudsprites.begin();
177 iter != _cloudsprites.end();
181 float cw = 0.5f * iter->width;
182 float ch = 0.5f * iter->height;
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);
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;
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);
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);
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);
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);
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));
230 bool CloudShaderGeometry_readLocalData(Object& obj, Input& fr)
232 bool iteratorAdvanced = false;
234 CloudShaderGeometry& geom = static_cast<CloudShaderGeometry&>(obj);
236 if ((fr[0].matchWord("geometry"))) {
238 iteratorAdvanced = true;
239 osg::Drawable* drawable = fr.readDrawable();
241 geom._geometry = drawable;
244 if ((fr.matchSequence("instances %i"))) {
245 int entry = fr[0].getNoNestedBrackets();
247 fr[1].getInt(capacity);
248 geom._cloudsprites.reserve(capacity);
250 iteratorAdvanced = true;
252 while (!fr.eof() && fr[0].getNoNestedBrackets() > entry) {
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)) {
260 //SGVec3f* v = new SGVec3f(v.x(), v.y(), v.z());
261 geom._cloudsprites.push_back(CloudShaderGeometry::CloudSprite(v, tx, ty, w, h));
266 geom.generateGeometry();
268 return iteratorAdvanced;
271 bool CloudShaderGeometry_writeLocalData(const Object& obj, Output& fw)
273 const CloudShaderGeometry& geom = static_cast<const CloudShaderGeometry&>(obj);
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;
280 for (CloudShaderGeometry::CloudSpriteList::const_iterator itr
281 = geom._cloudsprites.begin();
282 itr != geom._cloudsprites.end();
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;
290 fw.indent() << "}" << std::endl;
295 osgDB::RegisterDotOsgWrapperProxy cloudShaderGeometryProxy
297 new CloudShaderGeometry,
298 "CloudShaderGeometry",
299 "Object Drawable CloudShaderGeometry",
300 &CloudShaderGeometry_readLocalData,
301 &CloudShaderGeometry_writeLocalData