Improved 3D clouds
1) Impostors 2) One drawable per cloud rather than a single drawable shared by all clouds.
This commit is contained in:
@@ -51,79 +51,94 @@ 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 (unsigned i = sortData.spriteIdx->size(); i < (unsigned)_cloudsprites.size(); ++i)
|
||||
sortData.spriteIdx->push_back(SortData::SortItem(i, 0.0f));
|
||||
sortData.frameSorted = frameNumber - (sortData.skip_limit + 1);
|
||||
}
|
||||
unsigned int contextID = state.getContextID();
|
||||
SortData& sortData = _sortData[contextID];
|
||||
Geometry* g = _geometry->asGeometry();
|
||||
|
||||
// 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.
|
||||
if (frameNumber - sortData.skip_limit >= sortData.frameSorted) {
|
||||
// are sorted.
|
||||
osg::Vec3Array* v = dynamic_cast<osg::Vec3Array*>(g->getVertexArray());
|
||||
if ((v->size() > 4) &&
|
||||
(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(toOsg(_cloudsprites[itr->idx].position), 1.0f) * mvp;
|
||||
itr->depth = projPos.z() / projPos.w();
|
||||
|
||||
osg::Vec4Array* c = dynamic_cast<osg::Vec4Array*>(g->getColorArray());
|
||||
osg::Vec2Array* t = dynamic_cast<osg::Vec2Array*>(g->getTexCoordArray(0));
|
||||
Vec3f av[4];
|
||||
Vec4f ac[4];
|
||||
Vec2f at[4];
|
||||
|
||||
// Perform a single pass bubble sort of the array,
|
||||
// keeping track of whether we've had to make any changes
|
||||
bool sorted = true;
|
||||
for (unsigned int i = 4; i < v->size(); i = i + 4) {
|
||||
// The position of the sprite is stored in the colour
|
||||
// array, with the exception of the w() coordinate
|
||||
// which is the z-scaling parameter.
|
||||
Vec4f a = (*c)[i-4];
|
||||
Vec4f aPos = Vec4f(a.x(), a.y(), a.z(), 1.0f) * mvp;
|
||||
Vec4f b = (*c)[i];
|
||||
Vec4f bPos = Vec4f(b.x(), b.y(), b.z(), 1.0f) * mvp;
|
||||
|
||||
if ((aPos.z()/aPos.w()) < (bPos.z()/bPos.w() - 0.0001)) {
|
||||
// a is non-trivially closer than b, so should be rendered
|
||||
// later. Swap them around
|
||||
for (int j = 0; j < 4; j++) {
|
||||
av[j] = (*v)[i+j-4];
|
||||
ac[j] = (*c)[i+j-4];
|
||||
at[j] = (*t)[i+j-4];
|
||||
|
||||
(*v)[i+j -4] = (*v)[i+j];
|
||||
(*c)[i+j -4] = (*c)[i+j];
|
||||
(*t)[i+j -4] = (*t)[i+j];
|
||||
|
||||
(*v)[i+j] = av[j];
|
||||
(*c)[i+j] = ac[j];
|
||||
(*t)[i+j] = at[j];
|
||||
}
|
||||
|
||||
// Indicate that the arrays were not sorted
|
||||
// so we should check them next iteration
|
||||
sorted = false;
|
||||
}
|
||||
}
|
||||
// Already sorted?
|
||||
if (std::adjacent_find(sortData.spriteIdx->rbegin(),
|
||||
sortData.spriteIdx->rend(), SpriteComp())
|
||||
== sortData.spriteIdx->rend()) {
|
||||
|
||||
if (sorted) {
|
||||
// This cloud is sorted, so no need to re-sort.
|
||||
|
||||
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
|
||||
sortData.skip_limit += sg_random() * 10;
|
||||
}
|
||||
if (sortData.skip_limit > 128) {
|
||||
// Maximum of every 128 frames (2 - 4 seconds)
|
||||
sortData.skip_limit = 128 + sg_random() * 10;
|
||||
if (sortData.skip_limit > 500) {
|
||||
// Maximum of every 500 frames (10-20 seconds)
|
||||
sortData.skip_limit = 500 + sg_random() * 10;
|
||||
}
|
||||
|
||||
} else {
|
||||
std::sort(sortData.spriteIdx->begin(), sortData.spriteIdx->end(),
|
||||
SpriteComp());
|
||||
sortData.skip_limit = 1;
|
||||
}
|
||||
|
||||
sortData.frameSorted = frameNumber;
|
||||
}
|
||||
|
||||
|
||||
const Extensions* extensions = getExtensions(state.getContextID(),true);
|
||||
|
||||
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,
|
||||
(GLfloat) t.width };
|
||||
GLfloat ua2[3] = { (GLfloat) t.height,
|
||||
(GLfloat) shade_factor,
|
||||
(GLfloat) cloud_height };
|
||||
GLfloat ua3[3] = { (GLfloat) bottom_factor,
|
||||
(GLfloat) middle_factor,
|
||||
(GLfloat) top_factor };
|
||||
|
||||
extensions->glVertexAttrib3fv(USR_ATTR_1, ua1 );
|
||||
extensions->glVertexAttrib3fv(USR_ATTR_2, ua2 );
|
||||
extensions->glVertexAttrib3fv(USR_ATTR_3, ua3 );
|
||||
glColor4f(t.position.x(), t.position.y(), t.position.z(), zscale);
|
||||
_geometry->draw(renderInfo);
|
||||
}
|
||||
GLfloat ua1[3] = { (GLfloat) 1.0f,
|
||||
(GLfloat) shade_factor,
|
||||
(GLfloat) cloud_height };
|
||||
GLfloat ua2[3] = { (GLfloat) bottom_factor,
|
||||
(GLfloat) middle_factor,
|
||||
(GLfloat) top_factor };
|
||||
|
||||
extensions->glVertexAttrib3fv(USR_ATTR_1, ua1 );
|
||||
extensions->glVertexAttrib3fv(USR_ATTR_2, ua2 );
|
||||
_geometry->draw(renderInfo);
|
||||
}
|
||||
|
||||
void CloudShaderGeometry::addSprite(const SGVec3f& p, int tx, int ty,
|
||||
@@ -145,6 +160,73 @@ void CloudShaderGeometry::addSprite(const SGVec3f& p, int tx, int ty,
|
||||
_cloudsprites.push_back(CloudSprite(p, tx, ty, w, h));
|
||||
}
|
||||
|
||||
void CloudShaderGeometry::generateGeometry()
|
||||
{
|
||||
// Generate a set of geometries as a QuadStrip based on the list of sprites
|
||||
int numsprites = _cloudsprites.size();
|
||||
|
||||
// Create front and back polygons so we don't need to screw around
|
||||
// with two-sided lighting in the shader.
|
||||
osg::Vec3Array& v = *(new osg::Vec3Array(4 * numsprites));
|
||||
osg::Vec4Array& c = *(new osg::Vec4Array(4 * numsprites));
|
||||
osg::Vec2Array& t = *(new osg::Vec2Array(4 * numsprites));
|
||||
|
||||
int idx = 0;
|
||||
|
||||
for (CloudShaderGeometry::CloudSpriteList::iterator iter = _cloudsprites.begin();
|
||||
iter != _cloudsprites.end();
|
||||
++iter)
|
||||
{
|
||||
|
||||
float cw = 0.5f * iter->width;
|
||||
float ch = 0.5f * iter->height;
|
||||
|
||||
// Create the vertices
|
||||
v[4*idx ].set(0.0f, -cw, -ch);
|
||||
v[4*idx+1].set(0.0f, cw, -ch);
|
||||
v[4*idx+2].set(0.0f, cw, ch);
|
||||
v[4*idx+3].set(0.0f, -cw, ch);
|
||||
|
||||
// Set the texture coords for each vertex
|
||||
// from the texture index, and the number
|
||||
// of textures in the image
|
||||
int x = iter->texture_index_x;
|
||||
int y = iter->texture_index_y;
|
||||
|
||||
t[4*idx ].set( (float) x / varieties_x, (float) y / varieties_y);
|
||||
t[4*idx+1].set( (float) (x + 1) / varieties_x, (float) y / varieties_y);
|
||||
t[4*idx+2].set( (float) (x + 1) / varieties_x, (float) (y + 1) / varieties_y);
|
||||
t[4*idx+3].set( (float) x / varieties_x, (float) (y + 1) / varieties_y);
|
||||
|
||||
// The color isn't actually use in lighting, but instead to indicate the center of rotation
|
||||
c[4*idx ].set(iter->position.x(), iter->position.y(), iter->position.z(), zscale);
|
||||
c[4*idx+1].set(iter->position.x(), iter->position.y(), iter->position.z(), zscale);
|
||||
c[4*idx+2].set(iter->position.x(), iter->position.y(), iter->position.z(), zscale);
|
||||
c[4*idx+3].set(iter->position.x(), iter->position.y(), iter->position.z(), zscale);
|
||||
|
||||
idx++;
|
||||
}
|
||||
|
||||
//Quads now created, add it to the geometry.
|
||||
osg::Geometry* geom = new osg::Geometry;
|
||||
geom->setVertexArray(&v);
|
||||
geom->setTexCoordArray(0, &t);
|
||||
|
||||
// The normal isn't actually use in lighting, so we simply bind overall.
|
||||
osg::Vec3Array& n = *(new osg::Vec3Array(4));
|
||||
n[0].set(1.0f, -1.0f, -1.0f);
|
||||
n[1].set(1.0f, 1.0f, -1.0f);
|
||||
n[2].set(1.0f, 1.0f, 1.0f);
|
||||
n[3].set(1.0f, -1.0f, 1.0f);
|
||||
|
||||
geom->setNormalArray(&n);
|
||||
geom->setNormalBinding(Geometry::BIND_OVERALL);
|
||||
geom->setColorArray(&c);
|
||||
geom->setColorBinding(Geometry::BIND_PER_VERTEX);
|
||||
geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,numsprites*4));
|
||||
_geometry = geom;
|
||||
}
|
||||
|
||||
bool CloudShaderGeometry_readLocalData(Object& obj, Input& fr)
|
||||
{
|
||||
bool iteratorAdvanced = false;
|
||||
@@ -181,6 +263,7 @@ bool CloudShaderGeometry_readLocalData(Object& obj, Input& fr)
|
||||
++fr;
|
||||
}
|
||||
}
|
||||
geom.generateGeometry();
|
||||
}
|
||||
return iteratorAdvanced;
|
||||
}
|
||||
|
||||
@@ -100,12 +100,9 @@ class CloudShaderGeometry : public osg::Drawable
|
||||
return _bbox;
|
||||
}
|
||||
|
||||
void setGeometry(osg::Drawable* geometry)
|
||||
{
|
||||
_geometry = geometry;
|
||||
}
|
||||
|
||||
void addSprite(const SGVec3f& p, int tx, int ty, float w, float h, float cull);
|
||||
void generateGeometry();
|
||||
void rebuildGeometry();
|
||||
|
||||
osg::ref_ptr<osg::Drawable> _geometry;
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <osg/Texture2D>
|
||||
#include <osg/PositionAttitudeTransform>
|
||||
#include <osg/Vec4f>
|
||||
#include <osgSim/Impostor>
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
|
||||
@@ -68,9 +69,15 @@ float SGCloudField::fieldSize = 50000.0f;
|
||||
double SGCloudField::timer_dt = 0.0;
|
||||
float SGCloudField::view_distance = 20000.0f;
|
||||
bool SGCloudField::wrap = true;
|
||||
float SGCloudField::RADIUS_LEVEL_1 = 5000.0f;
|
||||
float SGCloudField::RADIUS_LEVEL_2 = 2000.0f;
|
||||
float SGCloudField::MAX_CLOUD_DEPTH = 2000.0f;
|
||||
bool SGCloudField::use_impostors = true;
|
||||
float SGCloudField::lod1_range = 10000.0f;
|
||||
float SGCloudField::lod2_range = 5000.0f;
|
||||
float SGCloudField::impostor_distance = 10000.0f;
|
||||
|
||||
int impostorcount = 0;
|
||||
int lodcount = 0;
|
||||
int cloudcount = 0;
|
||||
|
||||
SGVec3f SGCloudField::view_vec, SGCloudField::view_X, SGCloudField::view_Y;
|
||||
|
||||
@@ -165,6 +172,9 @@ SGCloudField::SGCloudField() :
|
||||
field_transform->addChild(altitude_transform.get());
|
||||
placed_root = new osg::Group();
|
||||
altitude_transform->addChild(placed_root);
|
||||
impostorcount = 0;
|
||||
lodcount = 0;
|
||||
cloudcount = 0;
|
||||
}
|
||||
|
||||
SGCloudField::~SGCloudField() {
|
||||
@@ -182,12 +192,12 @@ void SGCloudField::clear(void) {
|
||||
cloud_hash.clear();
|
||||
}
|
||||
|
||||
void SGCloudField::applyVisRange(void)
|
||||
void SGCloudField::applyVisAndLoDRange(void)
|
||||
{
|
||||
for (unsigned int i = 0; i < placed_root->getNumChildren(); i++) {
|
||||
osg::ref_ptr<osg::LOD> lodnode1 = (osg::LOD*) placed_root->getChild(i);
|
||||
for (unsigned int j = 0; j < lodnode1->getNumChildren(); j++) {
|
||||
lodnode1->setRange(j, 0.0f, view_distance + RADIUS_LEVEL_1 + RADIUS_LEVEL_2 + MAX_CLOUD_DEPTH);
|
||||
lodnode1->setRange(j, 0.0f, lod1_range + view_distance + MAX_CLOUD_DEPTH);
|
||||
osg::ref_ptr<osg::LOD> lodnode2 = (osg::LOD*) lodnode1->getChild(j);
|
||||
for (unsigned int k = 0; k < lodnode2->getNumChildren(); k++) {
|
||||
lodnode2->setRange(k, 0.0f, view_distance + MAX_CLOUD_DEPTH);
|
||||
@@ -217,15 +227,19 @@ void SGCloudField::removeCloudFromTree(osg::ref_ptr<osg::PositionAttitudeTransfo
|
||||
{
|
||||
osg::ref_ptr<osg::Group> lodnode = transform->getParent(0);
|
||||
lodnode->removeChild(transform);
|
||||
cloudcount--;
|
||||
|
||||
// Clean up the LOD nodes if required
|
||||
if (lodnode->getNumChildren() == 0) {
|
||||
osg::ref_ptr<osg::Group> lodnode1 = lodnode->getParent(0);
|
||||
|
||||
osg::ref_ptr<osgSim::Impostor> impostornode = (osgSim::Impostor*) lodnode1->getParent(0);
|
||||
|
||||
lodnode1->removeChild(lodnode);
|
||||
|
||||
if (lodnode1->getNumChildren() == 0) {
|
||||
placed_root->removeChild(lodnode1);
|
||||
lodcount--;
|
||||
|
||||
if (lodnode1->getNumChildren() == 0) {
|
||||
impostornode->removeChild(lodnode1);
|
||||
placed_root->removeChild(impostornode);
|
||||
impostorcount--;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -291,39 +305,55 @@ void SGCloudField::addCloudToTree(osg::ref_ptr<osg::PositionAttitudeTransform> t
|
||||
bool found = false;
|
||||
osg::ref_ptr<osg::LOD> lodnode1;
|
||||
osg::ref_ptr<osg::LOD> lodnode;
|
||||
osg::ref_ptr<osgSim::Impostor> impostornode;
|
||||
|
||||
for (unsigned int i = 0; (!found) && (i < placed_root->getNumChildren()); i++) {
|
||||
lodnode1 = (osg::LOD*) placed_root->getChild(i);
|
||||
if ((lodnode1->getCenter() - pos).length2() < RADIUS_LEVEL_1*RADIUS_LEVEL_1) {
|
||||
// New cloud is within RADIUS_LEVEL_1 of the center of the LOD node.
|
||||
found = true;
|
||||
}
|
||||
if ((lodnode1->getCenter() - pos).length2() < lod1_range*lod1_range) {
|
||||
// New cloud is within RADIUS_LEVEL_1 of the center of the LOD node.
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
lodnode1 = new osg::LOD();
|
||||
placed_root->addChild(lodnode1.get());
|
||||
if (use_impostors) {
|
||||
impostornode = new osgSim::Impostor();
|
||||
impostornode->setImpostorThreshold(impostor_distance);
|
||||
//impostornode->setImpostorThresholdToBound();
|
||||
//impostornode->setCenter(pos);
|
||||
placed_root->addChild(impostornode.get());
|
||||
lodnode1 = (osg::ref_ptr<osg::LOD>) impostornode;
|
||||
} else {
|
||||
lodnode1 = new osg::LOD();
|
||||
placed_root->addChild(lodnode1.get());
|
||||
}
|
||||
impostorcount++;
|
||||
}
|
||||
|
||||
// Now check if there is a second level LOD node at an appropriate distance
|
||||
found = false;
|
||||
|
||||
for (unsigned int j = 0; (!found) && (j < lodnode1->getNumChildren()); j++) {
|
||||
|
||||
for (unsigned int j = 0; (!found) && (j < lodnode1->getNumChildren()); j++) {
|
||||
lodnode = (osg::LOD*) lodnode1->getChild(j);
|
||||
if ((lodnode->getCenter() - pos).length2() < RADIUS_LEVEL_2*RADIUS_LEVEL_2) {
|
||||
if ((lodnode->getCenter() - pos).length2() < lod2_range*lod2_range) {
|
||||
// We've found the right leaf LOD node
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
// No suitable leave node was found, so we need to add one.
|
||||
// No suitable leaf node was found, so we need to add one.
|
||||
lodnode = new osg::LOD();
|
||||
lodnode1->addChild(lodnode, 0.0f, view_distance + RADIUS_LEVEL_1 + RADIUS_LEVEL_2 + MAX_CLOUD_DEPTH);
|
||||
}
|
||||
|
||||
lodnode1->addChild(lodnode, 0.0f, lod1_range + view_distance + MAX_CLOUD_DEPTH);
|
||||
lodcount++;
|
||||
}
|
||||
|
||||
transform->setPosition(pos);
|
||||
lodnode->addChild(transform.get(), 0.0f, view_distance + MAX_CLOUD_DEPTH);
|
||||
lodnode->addChild(transform.get(), 0.0f, view_distance);
|
||||
cloudcount++;
|
||||
SG_LOG(SG_ENVIRONMENT, SG_DEBUG, "Impostors: " << impostorcount <<
|
||||
" LoD: " << lodcount <<
|
||||
" Clouds: " << cloudcount);
|
||||
|
||||
lodnode->dirtyBound();
|
||||
lodnode1->dirtyBound();
|
||||
|
||||
@@ -72,16 +72,12 @@ private:
|
||||
|
||||
float Rnd(float);
|
||||
|
||||
// Radius of the LoD nodes for the dynamic quadtrees.
|
||||
static float RADIUS_LEVEL_1;
|
||||
static float RADIUS_LEVEL_2;
|
||||
|
||||
// Theoretical maximum cloud depth, used for fudging the LoD
|
||||
// ranges to ensure that clouds become visible at maximum range
|
||||
static float MAX_CLOUD_DEPTH;
|
||||
|
||||
// this is a relative position only, with that we can move all clouds at once
|
||||
SGVec3f relative_position;
|
||||
// this is a relative position only, with that we can move all clouds at once
|
||||
SGVec3f relative_position;
|
||||
|
||||
osg::ref_ptr<osg::Group> field_root;
|
||||
osg::ref_ptr<osg::Group> placed_root;
|
||||
@@ -150,6 +146,10 @@ public:
|
||||
static SGVec3f view_vec, view_X, view_Y;
|
||||
|
||||
static float view_distance;
|
||||
static float impostor_distance;
|
||||
static float lod1_range;
|
||||
static float lod2_range;
|
||||
static bool use_impostors;
|
||||
static double timer_dt;
|
||||
static float fieldSize;
|
||||
static bool wrap;
|
||||
@@ -157,9 +157,18 @@ public:
|
||||
static bool getWrap(void) { return wrap; }
|
||||
static void setWrap(bool w) { wrap = w; }
|
||||
|
||||
static float getImpostorDistance(void) { return impostor_distance; }
|
||||
static void setImpostorDistance(float d) { impostor_distance = d; }
|
||||
static float getLoD1Range(void) { return lod1_range; }
|
||||
static void setLoD1Range(int d) { lod1_range = d; }
|
||||
static float getLoD2Range(void) { return lod2_range; }
|
||||
static void setLoD2Range(int d) { lod2_range = d; }
|
||||
static void setUseImpostors(bool b) { use_impostors = b; }
|
||||
static bool getUseImpostors(void) { return use_impostors; }
|
||||
|
||||
static float getVisRange(void) { return view_distance; }
|
||||
static void setVisRange(float d) { view_distance = d; }
|
||||
void applyVisRange(void);
|
||||
void applyVisAndLoDRange(void);
|
||||
|
||||
static osg::Fog* getFog()
|
||||
{
|
||||
|
||||
@@ -119,58 +119,11 @@ SGNewCloud::SGNewCloud(const SGPath &texture_root, const SGPropertyNode *cld_def
|
||||
} else {
|
||||
effect = iter->second.get();
|
||||
}
|
||||
quad = createOrthQuad(min_sprite_width, min_sprite_height,
|
||||
num_textures_x, num_textures_y);
|
||||
}
|
||||
|
||||
SGNewCloud::~SGNewCloud() {
|
||||
}
|
||||
|
||||
osg::Geometry* SGNewCloud::createOrthQuad(float w, float h, int varieties_x, int varieties_y)
|
||||
{
|
||||
// Create front and back polygons so we don't need to screw around
|
||||
// with two-sided lighting in the shader.
|
||||
osg::Vec3Array& v = *(new osg::Vec3Array(4));
|
||||
osg::Vec3Array& n = *(new osg::Vec3Array(4));
|
||||
osg::Vec2Array& t = *(new osg::Vec2Array(4));
|
||||
|
||||
float cw = w*0.5f;
|
||||
float ch = h*0.5f;
|
||||
|
||||
v[0].set(0.0f, -cw, -ch);
|
||||
v[1].set(0.0f, cw, -ch);
|
||||
v[2].set(0.0f, cw, ch);
|
||||
v[3].set(0.0f, -cw, ch);
|
||||
|
||||
// The texture coordinate range is not the
|
||||
// entire coordinate space - as the texture
|
||||
// has a number of different clouds on it.
|
||||
float tx = 1.0f/varieties_x;
|
||||
float ty = 1.0f/varieties_y;
|
||||
|
||||
t[0].set(0.0f, 0.0f);
|
||||
t[1].set( tx, 0.0f);
|
||||
t[2].set( tx, ty);
|
||||
t[3].set(0.0f, ty);
|
||||
|
||||
// The normal isn't actually use in lighting.
|
||||
n[0].set(1.0f, -1.0f, -1.0f);
|
||||
n[1].set(1.0f, 1.0f, -1.0f);
|
||||
n[2].set(1.0f, 1.0f, 1.0f);
|
||||
n[3].set(1.0f, -1.0f, 1.0f);
|
||||
|
||||
osg::Geometry *geom = new osg::Geometry;
|
||||
|
||||
geom->setVertexArray(&v);
|
||||
geom->setTexCoordArray(0, &t);
|
||||
geom->setNormalArray(&n);
|
||||
geom->setNormalBinding(Geometry::BIND_PER_VERTEX);
|
||||
// No color for now; that's used to pass the position.
|
||||
geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,4));
|
||||
|
||||
return geom;
|
||||
}
|
||||
|
||||
#if 0
|
||||
// return a random number between -n/2 and n/2, tending to 0
|
||||
static float Rnd(float n) {
|
||||
@@ -240,28 +193,28 @@ osg::ref_ptr<EffectGeode> SGNewCloud::genCloud() {
|
||||
z = height * cos(elev) * 0.5f + height * 0.5f;
|
||||
}
|
||||
|
||||
// Determine the height and width as scaling factors on the minimum size (used to create the quad).
|
||||
float sprite_width = 1.0f + sg_random() * (max_sprite_width - min_sprite_width) / min_sprite_width;
|
||||
float sprite_height = 1.0f + sg_random() * (max_sprite_height - min_sprite_height) / min_sprite_height;
|
||||
// Determine the height and width
|
||||
float sprite_width = min_sprite_width + sg_random() * (max_sprite_width - min_sprite_width);
|
||||
float sprite_height = min_sprite_height + sg_random() * (max_sprite_height - min_sprite_height);
|
||||
|
||||
// Sprites are never taller than square.
|
||||
if (sprite_height * min_sprite_height > sprite_width * min_sprite_width)
|
||||
if (sprite_height > sprite_width )
|
||||
{
|
||||
sprite_height = sprite_width * min_sprite_width / min_sprite_height;
|
||||
sprite_height = sprite_width;
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
// The center sprite is always maximum size to fill up any holes.
|
||||
sprite_width = 1.0f + (max_sprite_width - min_sprite_width) / min_sprite_width;
|
||||
sprite_height = 1.0f + (max_sprite_height - min_sprite_height) / min_sprite_height;
|
||||
sprite_width = max_sprite_width;
|
||||
sprite_height = max_sprite_height;
|
||||
}
|
||||
|
||||
// If the center of the sprite is less than half the sprite heightthe sprite will extend
|
||||
// below the bottom of the cloud and must be shifted upwards. This is particularly important
|
||||
// for cumulus clouds which have a very well defined base.
|
||||
if (z < 0.5f * sprite_height * min_sprite_height)
|
||||
if (z < 0.5f * sprite_height)
|
||||
{
|
||||
z = 0.5f * sprite_height * min_sprite_height;
|
||||
z = 0.5f * sprite_height;
|
||||
}
|
||||
|
||||
// Determine the sprite texture indexes.
|
||||
@@ -287,7 +240,7 @@ osg::ref_ptr<EffectGeode> SGNewCloud::genCloud() {
|
||||
cull_distance_squared);
|
||||
}
|
||||
|
||||
sg->setGeometry(quad);
|
||||
sg->generateGeometry();
|
||||
geode->addDrawable(sg);
|
||||
geode->setName("3D cloud");
|
||||
geode->setEffect(effect.get());
|
||||
|
||||
@@ -233,7 +233,43 @@ void SGSky::set_3dCloudVisRange(float vis)
|
||||
{
|
||||
SGCloudField::setVisRange(vis);
|
||||
for ( int i = 0; i < (int)cloud_layers.size(); ++i ) {
|
||||
cloud_layers[i]->get_layer3D()->applyVisRange();
|
||||
cloud_layers[i]->get_layer3D()->applyVisAndLoDRange();
|
||||
}
|
||||
}
|
||||
|
||||
float SGSky::get_3dCloudImpostorDistance() const {
|
||||
return SGCloudField::getImpostorDistance();
|
||||
}
|
||||
|
||||
void SGSky::set_3dCloudImpostorDistance(float vis)
|
||||
{
|
||||
SGCloudField::setImpostorDistance(vis);
|
||||
for ( int i = 0; i < (int)cloud_layers.size(); ++i ) {
|
||||
cloud_layers[i]->get_layer3D()->applyVisAndLoDRange();
|
||||
}
|
||||
}
|
||||
|
||||
float SGSky::get_3dCloudLoD1Range() const {
|
||||
return SGCloudField::getLoD1Range();
|
||||
}
|
||||
|
||||
void SGSky::set_3dCloudLoD1Range(float vis)
|
||||
{
|
||||
SGCloudField::setLoD1Range(vis);
|
||||
for ( int i = 0; i < (int)cloud_layers.size(); ++i ) {
|
||||
cloud_layers[i]->get_layer3D()->applyVisAndLoDRange();
|
||||
}
|
||||
}
|
||||
|
||||
float SGSky::get_3dCloudLoD2Range() const {
|
||||
return SGCloudField::getLoD2Range();
|
||||
}
|
||||
|
||||
void SGSky::set_3dCloudLoD2Range(float vis)
|
||||
{
|
||||
SGCloudField::setLoD2Range(vis);
|
||||
for ( int i = 0; i < (int)cloud_layers.size(); ++i ) {
|
||||
cloud_layers[i]->get_layer3D()->applyVisAndLoDRange();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,6 +282,15 @@ void SGSky::set_3dCloudWrap(bool wrap)
|
||||
SGCloudField::setWrap(wrap);
|
||||
}
|
||||
|
||||
bool SGSky::get_3dCloudUseImpostors() const {
|
||||
return SGCloudField::getUseImpostors();
|
||||
}
|
||||
|
||||
void SGSky::set_3dCloudUseImpostors(bool imp)
|
||||
{
|
||||
SGCloudField::setUseImpostors(imp);
|
||||
}
|
||||
|
||||
|
||||
void SGSky::texture_path( const string& path ) {
|
||||
tex_path = SGPath( path );
|
||||
|
||||
@@ -437,6 +437,38 @@ public:
|
||||
*/
|
||||
virtual void set_3dCloudVisRange(float vis);
|
||||
|
||||
/** Get 3D cloud impostor distance*/
|
||||
virtual float get_3dCloudImpostorDistance() const;
|
||||
|
||||
/** Set 3D cloud impostor distance
|
||||
* @param density 3D cloud impostor distance
|
||||
*/
|
||||
virtual void set_3dCloudImpostorDistance(float vis);
|
||||
|
||||
/** Get 3D cloud LoD1 Range*/
|
||||
virtual float get_3dCloudLoD1Range() const;
|
||||
|
||||
/** Set 3D cloud LoD1 Range
|
||||
* @param vis LoD1 Range
|
||||
*/
|
||||
virtual void set_3dCloudLoD1Range(float vis);
|
||||
|
||||
/** Get 3D cloud LoD2 Range*/
|
||||
virtual float get_3dCloudLoD2Range() const;
|
||||
|
||||
/** Set 3D cloud LoD2 Range
|
||||
* @param vis LoD2 Range
|
||||
*/
|
||||
virtual void set_3dCloudLoD2Range(float vis);
|
||||
|
||||
/** Get 3D cloud impostor usage */
|
||||
virtual bool get_3dCloudUseImpostors() const;
|
||||
|
||||
/** Set 3D cloud impostor usage
|
||||
* @param wrap whether use impostors for 3D clouds
|
||||
*/
|
||||
virtual void set_3dCloudUseImpostors(bool imp);
|
||||
|
||||
/** Get 3D cloud wrapping */
|
||||
virtual bool get_3dCloudWrap() const;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user