Sort cloud sprites using std::sort, based on projected Z.
A "cleanup" of cloud sorting.
This commit is contained in:
parent
25c08e56c2
commit
8b57ed46b1
@ -19,6 +19,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <osgDB/Registry>
|
||||
#include <osgDB/Input>
|
||||
#include <osgDB/ParameterOutput>
|
||||
@ -31,6 +33,17 @@ using namespace osg;
|
||||
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
|
||||
@ -38,80 +51,93 @@ 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;
|
||||
@ -143,7 +169,7 @@ bool CloudShaderGeometry_readLocalData(Object& obj, Input& fr)
|
||||
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;
|
||||
}
|
||||
@ -165,11 +191,11 @@ bool CloudShaderGeometry_writeLocalData(const Object& obj, Output& fw)
|
||||
= 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;
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include <osg/RenderInfo>
|
||||
#include <osg/Vec3>
|
||||
#include <osg/Vec4>
|
||||
#include <osg/buffered_value>
|
||||
|
||||
#include <simgear/math/SGMath.hxx>
|
||||
#include <simgear/math/sg_random.h>
|
||||
@ -49,14 +50,12 @@ class CloudShaderGeometry : public osg::Drawable
|
||||
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);
|
||||
@ -69,14 +68,6 @@ class CloudShaderGeometry : public osg::Drawable
|
||||
|
||||
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)
|
||||
@ -91,17 +82,17 @@ class CloudShaderGeometry : public osg::Drawable
|
||||
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;
|
||||
@ -115,23 +106,8 @@ class CloudShaderGeometry : public osg::Drawable
|
||||
_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;
|
||||
@ -140,15 +116,30 @@ class CloudShaderGeometry : public osg::Drawable
|
||||
// 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;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -137,7 +137,7 @@ bool SGCloudField::reposition( const SGVec3f& p, const SGVec3f& up, double lon,
|
||||
field_transform->setMatrix( LAT*LON*T );
|
||||
}
|
||||
|
||||
field_root->getStateSet()->setRenderBinDetails(asl, "RenderBin");
|
||||
field_root->getStateSet()->setRenderBinDetails(asl, "DepthSortedBin");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user