*
*/
+#include <algorithm>
+
#include <osgDB/Registry>
#include <osgDB/Input>
#include <osgDB/ParameterOutput>
using namespace osgDB;
using namespace simgear;
+namespace
+{
+struct SpriteComp
+{
+ bool operator() (const CloudShaderGeometry::SortData::SortItem& lhs,
+ const CloudShaderGeometry::SortData::SortItem& rhs) const
+ {
+ return lhs.depth < rhs.depth;
+ }
+};
+}
namespace simgear
{
void CloudShaderGeometry::drawImplementation(RenderInfo& renderInfo) const
if (!_cloudsprites.size()) return;
osg::State& state = *renderInfo.getState();
-
+ unsigned int contextID = state.getContextID();
+ SortData& sortData = _sortData[contextID];
+ int frameNumber = state.getFrameStamp()->getFrameNumber();
+
+ if (!sortData.spriteIdx)
+ sortData.spriteIdx = new SortData::SortItemList;
+ if (sortData.spriteIdx->size() < _cloudsprites.size()) {
+ for (int i = sortData.spriteIdx->size(); i < _cloudsprites.size(); ++i)
+ sortData.spriteIdx->push_back(SortData::SortItem(i, 0.0f));
+ sortData.frameSorted = frameNumber - (sortData.skip_limit + 1);
+ }
// If the cloud is already sorted, then it is likely to still be sorted.
// Therefore we can avoid re-sorting it for a period. If it is still
// sorted after that period, then we can wait for a longer period before
// checking again. In this way, only clouds that are changing regularly
// are sorted.
-
- skip_info->skip_count = (skip_info->skip_count + 1) % skip_info->skip_limit;
-
- if (skip_info->skip_count == 0)
- {
- osg::Matrix vm = state.getModelViewMatrix();
- bool sorted = true;
-
- // Transform the viewing direction, represented by the eye space vector (0,0,-1, 0), into model-space
- // (here we simply take the opposite direction and reverse the ordering when sorting)
- osg::Vec3f view_dir(vm(0, 2), vm(1, 2), vm(2, 2)); // Caveat: OpenSceneGraph matrices are transposed!
-
- float p = view_dir*_cloudsprites[0]->position.osg();
- // Do a single iteration of a bubble sort, sorting
- // back to front.
- for(unsigned int i = 0; i < _cloudsprites.size() - 1; i++)
- {
- float q = view_dir*_cloudsprites[i+1]->position.osg();
- if (p > q) {
- CloudSprite c = *_cloudsprites[i];
- *_cloudsprites[i] = *_cloudsprites[i+1];
- *_cloudsprites[i+1] = c;
-
- sorted = false;
- }
- else
- p = q;
+ if (frameNumber - sortData.skip_limit >= sortData.frameSorted) {
+ Matrix mvp = state.getModelViewMatrix() * state.getProjectionMatrix();
+ for (SortData::SortItemList::iterator itr = sortData.spriteIdx->begin(),
+ end = sortData.spriteIdx->end();
+ itr != end;
+ ++itr) {
+ Vec4f projPos
+ = Vec4f(_cloudsprites[itr->idx].position.osg(), 1.0f) * mvp;
+ itr->depth = projPos.z() / projPos.w();
}
-
-
- if (sorted)
- {
+ // Already sorted?
+ if (std::adjacent_find(sortData.spriteIdx->rbegin(),
+ sortData.spriteIdx->rend(), SpriteComp())
+ == sortData.spriteIdx->rend()) {
// This cloud is sorted, so no need to re-sort.
- skip_info->skip_limit = skip_info->skip_limit * 2;
-
- if (skip_info->skip_limit > 30)
- {
+ sortData.skip_limit = sortData.skip_limit * 2;
+ if (sortData.skip_limit > 30) {
// Jitter the skip frames to avoid synchronized sorts
// which will cause periodic frame-rate drops
- skip_info->skip_limit += sg_random() * 10;
+ sortData.skip_limit += sg_random() * 10;
}
-
- if (skip_info->skip_limit > 128)
- {
+ if (sortData.skip_limit > 128) {
// Maximum of every 128 frames (2 - 4 seconds)
- skip_info->skip_limit = 128 + sg_random() * 10;
+ sortData.skip_limit = 128 + sg_random() * 10;
}
+
+ } else {
+ std::sort(sortData.spriteIdx->begin(), sortData.spriteIdx->end(),
+ SpriteComp());
+ sortData.skip_limit = 1;
}
- else
- {
- // This cloud is unsorted, so we need to sort next frame
- skip_info->skip_limit = 1;
- }
+ sortData.frameSorted = frameNumber;
}
const Extensions* extensions = getExtensions(state.getContextID(),true);
- for(CloudSpriteList::const_iterator t = _cloudsprites.begin(); t != _cloudsprites.end(); ++t)
- {
- GLfloat ua1[3] = { (GLfloat) (*t)->texture_index_x/varieties_x, (GLfloat) (*t)->texture_index_y/varieties_y, (*t)->width };
- GLfloat ua2[3] = { (GLfloat) (*t)->height, (*t)->shade, (GLfloat) (*t)->cloud_height };
+ for(SortData::SortItemList::const_iterator itr = sortData.spriteIdx->begin(),
+ end = sortData.spriteIdx->end();
+ itr != end;
+ ++itr) {
+ const CloudSprite& t = _cloudsprites[itr->idx];
+ GLfloat ua1[3] = { (GLfloat)t.texture_index_x/varieties_x,
+ (GLfloat)t.texture_index_y/varieties_y,
+ t.width };
+ GLfloat ua2[3] = { (GLfloat)t.height,
+ t.shade,
+ (GLfloat) t.cloud_height };
extensions->glVertexAttrib3fv(USR_ATTR_1, ua1 );
extensions->glVertexAttrib3fv(USR_ATTR_2, ua2 );
- glColor4f((*t)->position.x(), (*t)->position.y(), (*t)->position.z(), 1.0);
+ glColor4f(t.position.x(), t.position.y(), t.position.z(), 1.0);
_geometry->draw(renderInfo);
}
}
-
+
+void CloudShaderGeometry::addSprite(SGVec3f& p, int tx, int ty,
+ float w, float h,
+ float s, float cull, float cloud_height)
+{
+ // Only add the sprite if it is further than the cull distance to all other sprites
+ for (CloudShaderGeometry::CloudSpriteList::iterator iter = _cloudsprites.begin();
+ iter != _cloudsprites.end();
+ ++iter)
+ {
+ if (distSqr(iter->position, p) < cull) {
+ // Too close - cull it
+ return;
+ }
+ }
+ _cloudsprites.push_back(CloudSprite(p, tx, ty, w, h, s, cloud_height));
+}
+
bool CloudShaderGeometry_readLocalData(Object& obj, Input& fr)
{
bool iteratorAdvanced = false;
fr[5].getFloat(w) && fr[6].getFloat(h)&& fr[7].getFloat(s) && fr[8].getFloat(ch)) {
fr += 5;
//SGVec3f* v = new SGVec3f(v.x(), v.y(), v.z());
- geom._cloudsprites.push_back(new CloudShaderGeometry::CloudSprite(v, tx, ty, w, h,s,ch));
+ geom._cloudsprites.push_back(CloudShaderGeometry::CloudSprite(v, tx, ty, w, h,s,ch));
} else {
++fr;
}
= geom._cloudsprites.begin();
itr != geom._cloudsprites.end();
++itr) {
- fw.indent() << (*itr)->position.x() << " " << (*itr)->position.y() << " "
- << (*itr)->position.z() << " " << (*itr)->texture_index_x << " "
- << (*itr)->texture_index_y << " " << (*itr)->width << " "
- << (*itr)->height << " " << (*itr)->shade
- << (*itr)->cloud_height << " "<< std::endl;
+ fw.indent() << itr->position.x() << " " << itr->position.y() << " "
+ << itr->position.z() << " " << itr->texture_index_x << " "
+ << itr->texture_index_y << " " << itr->width << " "
+ << itr->height << " " << itr->shade
+ << itr->cloud_height << " "<< std::endl;
}
fw.moveOut();
fw.indent() << "}" << std::endl;
#include <osg/RenderInfo>
#include <osg/Vec3>
#include <osg/Vec4>
+#include <osg/buffered_value>
#include <simgear/math/SGMath.hxx>
#include <simgear/math/sg_random.h>
CloudShaderGeometry()
{
setUseDisplayList(false);
- skip_info = new SkipInfo();
}
CloudShaderGeometry(int vx, int vy, float width, float height) :
varieties_x(vx), varieties_y(vy)
{
setUseDisplayList(false);
- skip_info = new SkipInfo();
float x = width/2.0f;
float z = height/2.0f;
_bbox.expandBy(-x, -x, -z);
META_Object(flightgear, CloudShaderGeometry);
- struct SkipInfo {
- SkipInfo() : skip_count(0), skip_limit(1) {}
- int skip_count;
- int skip_limit;
- };
-
- SkipInfo* skip_info;
-
struct CloudSprite {
CloudSprite(SGVec3f& p, int tx, int ty, float w, float h, float s, float ch) :
position(p), texture_index_x(tx), texture_index_y(ty), width(w), height(h), shade(s), cloud_height(ch)
float cloud_height;
};
- typedef std::vector<CloudSprite*> CloudSpriteList;
+ typedef std::vector<CloudSprite> CloudSpriteList;
CloudSpriteList _cloudsprites;
- void insert(CloudSprite* t)
+ void insert(const CloudSprite& t)
{ _cloudsprites.push_back(t); }
void insert(SGVec3f& p, int tx, int ty, float w, float h, float s, float ch)
- { insert(new CloudSprite(p, tx, ty, w, h, s, ch)); }
+ { insert(CloudSprite(p, tx, ty, w, h, s, ch)); }
unsigned getNumCloudSprite() const
{ return _cloudsprites.size(); }
- CloudSprite* getCloudSprite(unsigned i) const
+ CloudSprite& getCloudSprite(unsigned i)
{ return _cloudsprites[i]; }
virtual void drawImplementation(osg::RenderInfo& renderInfo) const;
_geometry = geometry;
}
- void addSprite(SGVec3f& p, int tx, int ty, float w, float h, float s, float cull, float cloud_height)
- {
- // Only add the sprite if it is further than the cull distance to all other sprites
- for (CloudShaderGeometry::CloudSpriteList::iterator iter = _cloudsprites.begin();
- iter != _cloudsprites.end();
- ++iter)
- {
- if (distSqr((*iter)->position, p) < cull)
- {
- // Too close - cull it
- return;
- }
- }
-
- _cloudsprites.push_back(new CloudSprite(p, tx, ty, w, h, s, cloud_height));
- }
-
+ void addSprite(SGVec3f& p, int tx, int ty, float w, float h, float s, float cull, float cloud_height);
+
osg::ref_ptr<osg::Drawable> _geometry;
int varieties_x;
// Bounding box extents.
osg::BoundingBox _bbox;
- protected:
+ struct SortData
+ {
+ struct SortItem
+ {
+ SortItem(size_t idx_, float depth_) : idx(idx_), depth(depth_) {}
+ SortItem() : idx(0), depth(0.0f) {}
+ size_t idx;
+ float depth;
+ };
+ SortData() : frameSorted(0), skip_limit(1), spriteIdx(0) {}
+ int frameSorted;
+ int skip_limit;
+ // This will be sorted by Z distance
+ typedef std::vector<SortItem> SortItemList;
+ SortItemList* spriteIdx;
+ };
+protected:
+ mutable osg::buffered_object<SortData> _sortData;
- virtual ~CloudShaderGeometry() {
- delete skip_info;
- for (unsigned int i = 0; i < _cloudsprites.size(); i++)
- {
- delete _cloudsprites[i];
- }
- }
+ virtual ~CloudShaderGeometry()
+ {
+ for (unsigned int i = 0; i < _sortData.size(); ++i)
+ delete _sortData[i].spriteIdx;
+ }
};
}