X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=simgear%2Fscene%2Fsky%2Fnewcloud.cxx;h=162f137a0c58c38941c449d99cbdd2d27fe88f1d;hb=5dfa4c0840b65377bba3224d24f2443d836e0782;hp=62e75501917c15209d53477c222ec057ebfd57f7;hpb=2d77178ba3758707a581c21cdf142ebf2692460e;p=simgear.git diff --git a/simgear/scene/sky/newcloud.cxx b/simgear/scene/sky/newcloud.cxx index 62e75501..162f137a 100644 --- a/simgear/scene/sky/newcloud.cxx +++ b/simgear/scene/sky/newcloud.cxx @@ -25,6 +25,7 @@ #endif #include +#include #include #include #include @@ -43,12 +44,15 @@ #include #include #include +#include #include #include #include #include +#include #include +#include #include "cloudfield.hxx" #include "newcloud.hxx" @@ -57,90 +61,13 @@ using namespace simgear; using namespace osg; -typedef std::map > StateSetMap; -typedef std::vector< osg::ref_ptr > GeodeList; -typedef std::map CloudMap; +namespace +{ +typedef std::map > EffectMap; +EffectMap effectMap; +} -StateSetMap cloudTextureMap; -static CloudMap cloudMap; double SGNewCloud::sprite_density = 1.0; -unsigned int SGNewCloud::num_flavours = 10; - -static char vertexShaderSource[] = - "#version 120\n" - "\n" - "varying float fogFactor;\n" - "attribute vec3 usrAttr1;\n" - "attribute vec3 usrAttr2;\n" - "float textureIndexX = usrAttr1.r;\n" - "float textureIndexY = usrAttr1.g;\n" - "float wScale = usrAttr1.b;\n" - "float hScale = usrAttr2.r;\n" - "float shade = usrAttr2.g;\n" - "float cloud_height = usrAttr2.b;\n" - "void main(void)\n" - "{\n" - " gl_TexCoord[0] = gl_MultiTexCoord0 + vec4(textureIndexX, textureIndexY, 0.0, 0.0);\n" - " vec4 ep = gl_ModelViewMatrixInverse * vec4(0.0,0.0,0.0,1.0);\n" - " vec4 l = gl_ModelViewMatrixInverse * vec4(0.0,0.0,1.0,1.0);\n" - " vec3 u = normalize(ep.xyz - l.xyz);\n" -// Find a rotation matrix that rotates 1,0,0 into u. u, r and w are -// the columns of that matrix. - " vec3 absu = abs(u);\n" - " vec3 r = normalize(vec3(-u.y, u.x, 0));\n" - " vec3 w = cross(u, r);\n" -// Do the matrix multiplication by [ u r w pos]. Assume no -// scaling in the homogeneous component of pos. - " gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n" - " gl_Position.xyz = gl_Vertex.x * u * wScale;\n" - " gl_Position.xyz += gl_Vertex.y * r * hScale;\n" - " gl_Position.xyz += gl_Vertex.z * w;\n" - " gl_Position.xyz += gl_Color.xyz;\n" -// Determine a lighting normal based on the vertex position from the -// center of the cloud, so that sprite on the opposite side of the cloud to the sun are darker. - " float n = dot(normalize(gl_LightSource[0].position.xyz), normalize(mat3x3(gl_ModelViewMatrix) * gl_Position.xyz));\n" -// Determine the position - used for fog and shading calculations - " vec3 ecPosition = vec3(gl_ModelViewMatrix * gl_Position);\n" - " float fogCoord = abs(ecPosition.z);\n" - " float fract = smoothstep(0.0, cloud_height, gl_Position.z + cloud_height);\n" -// Final position of the sprite - " gl_Position = gl_ModelViewProjectionMatrix * gl_Position;\n" -// Limit the normal range from [0,1.0], and apply the shading (vertical factor) - " n = min(smoothstep(-0.5, 0.5, n), shade * (1.0 - fract) + fract);\n" -// This lighting normal is then used to mix between almost pure ambient (0) and diffuse (1.0) light - " vec4 backlight = 0.9 * gl_LightSource[0].ambient + 0.1 * gl_LightSource[0].diffuse;\n" - " gl_FrontColor = mix(backlight, gl_LightSource[0].diffuse, n);\n" - " gl_FrontColor += gl_FrontLightModelProduct.sceneColor;\n" -// As we get within 100m of the sprite, it is faded out - " gl_FrontColor.a = smoothstep(10.0, 100.0, fogCoord);\n" - " gl_BackColor = gl_FrontColor;\n" -// Fog doesn't affect clouds as much as other objects. - " fogFactor = exp( -gl_Fog.density * fogCoord * 0.5);\n" - " fogFactor = clamp(fogFactor, 0.0, 1.0);\n" - "}\n"; - -static char fragmentShaderSource[] = - "uniform sampler2D baseTexture; \n" - "varying float fogFactor;\n" - "\n" - "void main(void)\n" - "{\n" - " vec4 base = texture2D( baseTexture, gl_TexCoord[0].st);\n" - " vec4 finalColor = base * gl_Color;\n" - " gl_FragColor = mix(gl_Fog.color, finalColor, fogFactor );\n" - "}\n"; - -class SGCloudFogUpdateCallback : public osg::StateAttribute::Callback { - public: - virtual void operator () (osg::StateAttribute* sa, osg::NodeVisitor* nv) - { - SGUpdateVisitor* updateVisitor = static_cast(nv); - osg::Fog* fog = static_cast(sa); - fog->setMode(osg::Fog::EXP); - fog->setColor(updateVisitor->getFogColor().osg()); - fog->setDensity(updateVisitor->getFogExpDensity()); - } -}; SGNewCloud::SGNewCloud(string type, const SGPath &tex_path, @@ -172,77 +99,24 @@ SGNewCloud::SGNewCloud(string type, texture(tex), name(type) { - // Create a new StateSet for the texture, if required. - StateSetMap::iterator iter = SGCloudField::cloudTextureMap.find(texture); - - if (iter == SGCloudField::cloudTextureMap.end()) { - stateSet = new osg::StateSet; - - osg::ref_ptr options = makeOptionsFromPath(tex_path); - - osg::Texture2D *tex = new osg::Texture2D; - tex->setWrap( osg::Texture2D::WRAP_S, osg::Texture2D::CLAMP ); - tex->setWrap( osg::Texture2D::WRAP_T, osg::Texture2D::CLAMP ); - tex->setImage(osgDB::readImageFile(texture, options.get())); - - StateAttributeFactory* attribFactory = StateAttributeFactory::instance(); - - stateSet->setMode(GL_LIGHTING, osg::StateAttribute::ON); - stateSet->setMode(GL_CULL_FACE, osg::StateAttribute::OFF); - - // Fog handling - osg::Fog* fog = new osg::Fog; - fog->setUpdateCallback(new SGCloudFogUpdateCallback); - stateSet->setAttributeAndModes(fog); - stateSet->setDataVariance(osg::Object::DYNAMIC); - - stateSet->setAttributeAndModes(attribFactory->getSmoothShadeModel()); - stateSet->setAttributeAndModes(attribFactory->getStandardBlendFunc()); - - stateSet->setTextureAttributeAndModes(0, tex, osg::StateAttribute::ON ); - stateSet->setRenderBinDetails(osg::StateSet::TRANSPARENT_BIN, "DepthSortedBin"); - - static ref_ptr alphaFunc; - static ref_ptr program; - static ref_ptr baseTextureSampler; - static ref_ptr material; - - // Generate the shader etc, if we don't already have one. - if (!program.valid()) { - alphaFunc = new AlphaFunc; - alphaFunc->setFunction(AlphaFunc::GREATER,0.001f); - program = new Program; - baseTextureSampler = new osg::Uniform("baseTexture", 0); - Shader* vertex_shader = new Shader(Shader::VERTEX, vertexShaderSource); - program->addShader(vertex_shader); - program->addBindAttribLocation("usrAttr1", CloudShaderGeometry::USR_ATTR_1); - program->addBindAttribLocation("usrAttr2", CloudShaderGeometry::USR_ATTR_2); - Shader* fragment_shader = new Shader(Shader::FRAGMENT, fragmentShaderSource); - program->addShader(fragment_shader); - material = new Material; - // Don´t track vertex color - material->setColorMode(Material::OFF); - - // We don't actually use the material information either - see shader. - material->setAmbient(Material::FRONT_AND_BACK, - Vec4(0.5f, 0.5f, 0.5f, 1.0f)); - material->setDiffuse(Material::FRONT_AND_BACK, - Vec4(0.5f, 0.5f, 0.5f, 1.0f)); - } - - stateSet->setAttributeAndModes(alphaFunc.get()); - stateSet->setAttribute(program.get()); - stateSet->addUniform(baseTextureSampler.get()); - stateSet->setMode(GL_VERTEX_PROGRAM_TWO_SIDE, StateAttribute::ON); - stateSet->setAttribute(material.get()); - - // Add the newly created texture to the map for use later. - SGCloudField::cloudTextureMap.insert(StateSetMap::value_type(texture, stateSet)); + // Create a new Effect for the texture, if required. + EffectMap::iterator iter = effectMap.find(texture); + if (iter == effectMap.end()) { + SGPropertyNode_ptr pcloudEffect = new SGPropertyNode; + makeChild(pcloudEffect, "inherits-from")->setValue("Effects/cloud"); + setValue(makeChild(makeChild(makeChild(pcloudEffect, "parameters"), + "texture"), + "image"), + texture); + osg::ref_ptr options + = makeOptionsFromPath(tex_path); + if ((effect = makeEffect(pcloudEffect, true, options))) + effectMap.insert(EffectMap::value_type(texture, effect)); } else { - stateSet = iter->second.get(); + effect = iter->second.get(); } - - quad = createOrthQuad(min_sprite_width, min_sprite_height, num_textures_x, num_textures_y); + quad = createOrthQuad(min_sprite_width, min_sprite_height, + num_textures_x, num_textures_y); } SGNewCloud::~SGNewCloud() { @@ -292,126 +166,94 @@ osg::Geometry* SGNewCloud::createOrthQuad(float w, float h, int varieties_x, int return geom; } +#if 0 // return a random number between -n/2 and n/2, tending to 0 static float Rnd(float n) { return n * (-0.5f + (sg_random() + sg_random()) / 2.0f); } +#endif -osg::ref_ptr SGNewCloud::genCloud() { +osg::ref_ptr SGNewCloud::genCloud() { - CloudMap::iterator iter = cloudMap.find(name); - osg::ref_ptr geode; + osg::ref_ptr geode = new EffectGeode; + + CloudShaderGeometry* sg = new CloudShaderGeometry(num_textures_x, num_textures_y, max_width, max_height); - // We generate up to num_flavours of different versions - // of the same cloud before we start re-using them. This - // allows us to strike a balance between performance and - // visual complexity. + // Determine how big this specific cloud instance is. Note that we subtract + // the sprite size because the width/height is used to define the limits of + // the center of the sprites, not their edges. + float width = min_width + sg_random() * (max_width - min_width) - min_sprite_width; + float height = min_height + sg_random() * (max_height - min_height) - min_sprite_height; - if (iter == cloudMap.end() || (*iter).second->size() < num_flavours) - { - - geode = new Geode; - - CloudShaderGeometry* sg = new CloudShaderGeometry(num_textures_x, num_textures_y, max_width, max_height); - - // Determine how big this specific cloud instance is. Note that we subtract - // the sprite size because the width/height is used to define the limits of - // the center of the sprites, not their edges. - float width = min_width + sg_random() * (max_width - min_width) - min_sprite_width; - float height = min_height + sg_random() * (max_height - min_height) - min_sprite_height; - - // Determine the cull distance. This is used to remove sprites that are too close together. - // The value is squared as we use vector calculations. - float cull_distance_squared = min_sprite_height * min_sprite_height * 0.1f; - - // The number of sprites we actually used is a function of the (user-controlled) density - int n_sprites = num_sprites * sprite_density; - - for (int i = 0; i < n_sprites; i++) - { - // Determine the position of the sprite. Rather than being completely random, - // we place them on the surface of a distorted sphere. However, we place - // the first and second sprites on the top and bottom, and the third in the - // center of the sphere (and at maximum size) to ensure good coverage and - // reduce the chance of there being "holes" in our cloud. - float x, y, z; - - if (i == 0) { - x = 0; - y = 0; - z = height * 0.5f; - } else if (i == 1) { - x = 0; - y = 0; - z = - height * 0.5f; - } else if (i == 2) { - x = 0; - y = 0; - z = 0; - } else { - double theta = sg_random() * SGD_2PI; - double elev = sg_random() * SGD_PI; - x = width * cos(theta) * 0.5f * sin(elev); - y = width * sin(theta) * 0.5f * sin(elev); - z = height * cos(elev) * 0.5f; - } - - SGVec3f *pos = new SGVec3f(x, y, z); + // Determine the cull distance. This is used to remove sprites that are too close together. + // The value is squared as we use vector calculations. + float cull_distance_squared = min_sprite_height * min_sprite_height * 0.1f; + + // The number of sprites we actually use is a function of the (user-controlled) density + int n_sprites = num_sprites * sprite_density * (0.5 + sg_random()); - // 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; - - if (i == 2) { - // 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; - } - - // Determine the sprite texture indexes; - int index_x = (int) floor(sg_random() * num_textures_x); - if (index_x == num_textures_x) { index_x--; } - - int index_y = (int) floor(sg_random() * num_textures_y); - if (index_y == num_textures_y) { index_y--; } - - sg->addSprite(*pos, - index_x, - index_y, - sprite_width, - sprite_height, - bottom_shade, - cull_distance_squared, - height * 0.5f); + for (int i = 0; i < n_sprites; i++) + { + // Determine the position of the sprite. Rather than being completely random, + // we place them on the surface of a distorted sphere. However, we place + // the first sprite in the center of the sphere (and at maximum size) to + // ensure good coverage and reduce the chance of there being "holes" in our + + float x, y, z; + + if (i == 0) { + x = 0; + y = 0; + z = 0; + } else { + double theta = sg_random() * SGD_2PI; + double elev = sg_random() * SGD_PI; + x = width * cos(theta) * 0.5f * sin(elev); + y = width * sin(theta) * 0.5f * sin(elev); + z = height * cos(elev) * 0.5f; } - - sg->setGeometry(quad); - geode->addDrawable(sg); - geode->setName("3D cloud"); - geode->setStateSet(stateSet.get()); - - if (iter == cloudMap.end()) + // 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; + + // Sprites are never taller than square. + if (sprite_height * min_sprite_height > sprite_width * min_sprite_width) { - // This is the first of this cloud to be generated. - GeodeList* geodelist = new GeodeList; - geodelist->push_back(geode); - cloudMap.insert(CloudMap::value_type(name, geodelist)); + sprite_height = sprite_width * min_sprite_width / min_sprite_height; } - else - { - // Add the new cloud to the list of geodes - (*iter).second->push_back(geode); + + 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; } - - } else { - - int index = sg_random() * num_flavours; - if (index == num_flavours) index--; + + // Determine the sprite texture indexes. + int index_x = (int) floor(sg_random() * num_textures_x); + if (index_x == num_textures_x) { index_x--; } + + // The y index depends on the positing of the sprite within the cloud. + // This allows cloud designers to have particular sprites for the base + // and tops of the cloud. + int index_y = (int) floor((z / height + 0.5f) * num_textures_y); + if (index_y == num_textures_y) { index_y--; } - geode = iter->second->at(index); + sg->addSprite(SGVec3f(x, y, z), + index_x, + index_y, + sprite_width, + sprite_height, + bottom_shade, + cull_distance_squared, + height * 0.5f); } + sg->setGeometry(quad); + geode->addDrawable(sg); + geode->setName("3D cloud"); + geode->setEffect(effect.get()); + return geode; }