From 965c8ccfd21f0266e2540035ca29ac77e4ac04b8 Mon Sep 17 00:00:00 2001 From: Stuart Buchanan Date: Sat, 17 Dec 2011 13:02:54 +0000 Subject: [PATCH] Improved 3D clouds 1) Impostors 2) One drawable per cloud rather than a single drawable shared by all clouds. --- simgear/scene/sky/CloudShaderGeometry.cxx | 187 ++++++++++++++++------ simgear/scene/sky/CloudShaderGeometry.hxx | 7 +- simgear/scene/sky/cloudfield.cxx | 76 ++++++--- simgear/scene/sky/cloudfield.hxx | 23 ++- simgear/scene/sky/newcloud.cxx | 67 ++------ simgear/scene/sky/sky.cxx | 47 +++++- simgear/scene/sky/sky.hxx | 32 ++++ 7 files changed, 294 insertions(+), 145 deletions(-) diff --git a/simgear/scene/sky/CloudShaderGeometry.cxx b/simgear/scene/sky/CloudShaderGeometry.cxx index b2ac9252..f3bb8c8a 100644 --- a/simgear/scene/sky/CloudShaderGeometry.cxx +++ b/simgear/scene/sky/CloudShaderGeometry.cxx @@ -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(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(g->getColorArray()); + osg::Vec2Array* t = dynamic_cast(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; } diff --git a/simgear/scene/sky/CloudShaderGeometry.hxx b/simgear/scene/sky/CloudShaderGeometry.hxx index 1b63f805..3825c843 100644 --- a/simgear/scene/sky/CloudShaderGeometry.hxx +++ b/simgear/scene/sky/CloudShaderGeometry.hxx @@ -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 _geometry; diff --git a/simgear/scene/sky/cloudfield.cxx b/simgear/scene/sky/cloudfield.cxx index 0ff07144..164f2b92 100644 --- a/simgear/scene/sky/cloudfield.cxx +++ b/simgear/scene/sky/cloudfield.cxx @@ -28,6 +28,7 @@ #include #include #include +#include #include @@ -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 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 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 lodnode = transform->getParent(0); lodnode->removeChild(transform); + cloudcount--; - // Clean up the LOD nodes if required if (lodnode->getNumChildren() == 0) { osg::ref_ptr lodnode1 = lodnode->getParent(0); - + osg::ref_ptr 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 t bool found = false; osg::ref_ptr lodnode1; osg::ref_ptr lodnode; + osg::ref_ptr 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) 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(); diff --git a/simgear/scene/sky/cloudfield.hxx b/simgear/scene/sky/cloudfield.hxx index 1199c9c5..28095c4b 100644 --- a/simgear/scene/sky/cloudfield.hxx +++ b/simgear/scene/sky/cloudfield.hxx @@ -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 field_root; osg::ref_ptr 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() { diff --git a/simgear/scene/sky/newcloud.cxx b/simgear/scene/sky/newcloud.cxx index d78380d2..b27704da 100644 --- a/simgear/scene/sky/newcloud.cxx +++ b/simgear/scene/sky/newcloud.cxx @@ -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 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 SGNewCloud::genCloud() { cull_distance_squared); } - sg->setGeometry(quad); + sg->generateGeometry(); geode->addDrawable(sg); geode->setName("3D cloud"); geode->setEffect(effect.get()); diff --git a/simgear/scene/sky/sky.cxx b/simgear/scene/sky/sky.cxx index f22a9a6a..84c446e3 100644 --- a/simgear/scene/sky/sky.cxx +++ b/simgear/scene/sky/sky.cxx @@ -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 ); diff --git a/simgear/scene/sky/sky.hxx b/simgear/scene/sky/sky.hxx index c4924100..2e84629f 100644 --- a/simgear/scene/sky/sky.hxx +++ b/simgear/scene/sky/sky.hxx @@ -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; -- 2.39.5