X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=simgear%2Fscene%2Fsky%2Fnewcloud.cxx;h=162f137a0c58c38941c449d99cbdd2d27fe88f1d;hb=5dfa4c0840b65377bba3224d24f2443d836e0782;hp=1ab787e6bcfaa09560b9dffff00e4baca20b45b0;hpb=ad6129816d1c1d064748382547a1bc9f3d9a67c3;p=simgear.git diff --git a/simgear/scene/sky/newcloud.cxx b/simgear/scene/sky/newcloud.cxx index 1ab787e6..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,87 +61,16 @@ using namespace simgear; using namespace osg; -typedef std::map > StateSetMap; - -static StateSetMap cloudTextureMap; - -static char vertexShaderSource[] = - "#version 120\n" - "\n" - "varying float fogFactor;\n" - "varying float alphaBlend;\n" - "attribute float textureIndexX;\n" - "attribute float textureIndexY;\n" - "attribute float wScale;\n" - "attribute float hScale;\n" - "attribute float shade;\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 sprites position from the -// center of the cloud. - " 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" -// 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);\n" -// This lighting normal is then used to mix between almost pure ambient (0) and diffuse (1.0) light - " vec4 backlight = 0.8 * gl_LightSource[0].ambient + 0.2 * gl_LightSource[0].diffuse;\n" - " gl_FrontColor = mix(backlight, gl_LightSource[0].diffuse, n);\n" - " gl_FrontColor += gl_FrontLightModelProduct.sceneColor;\n" - " 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);\n" - " fogFactor = clamp(fogFactor, 0.0, 1.0);\n" -// As we get within 100m of the sprite, it is faded out - " alphaBlend = smoothstep(10.0, 100.0, fogCoord);\n" - "}\n"; - -static char fragmentShaderSource[] = - "uniform sampler2D baseTexture; \n" - "varying float fogFactor;\n" - "varying float alphaBlend;\n" - "\n" - "void main(void)\n" - "{\n" - " vec4 base = texture2D( baseTexture, gl_TexCoord[0].st);\n" - " vec4 finalColor = base * gl_Color;\n" -// " finalColor.a = min(alphaBlend, finalColor.a);\n" - " gl_FragColor = mix(gl_Fog.color, finalColor, fogFactor );\n" - "}\n"; +namespace +{ +typedef std::map > EffectMap; +EffectMap effectMap; +} -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()); - } -}; +double SGNewCloud::sprite_density = 1.0; -SGNewCloud::SGNewCloud(const SGPath &tex_path, +SGNewCloud::SGNewCloud(string type, + const SGPath &tex_path, string tex, double min_w, double max_w, @@ -163,87 +96,33 @@ SGNewCloud::SGNewCloud(const SGPath &tex_path, num_sprites(n), num_textures_x(nt_x), num_textures_y(nt_y), - texture(tex) + texture(tex), + name(type) { - // Create a new StateSet for the texture, if required. - StateSetMap::iterator iter = cloudTextureMap.find(texture); - - if (iter == 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.002f); - program = new Program; - baseTextureSampler = new osg::Uniform("baseTexture", 0); - Shader* vertex_shader = new Shader(Shader::VERTEX, vertexShaderSource); - program->addShader(vertex_shader); - program->addBindAttribLocation("textureIndexX", CloudShaderGeometry::TEXTURE_INDEX_X); - program->addBindAttribLocation("textureIndexY", CloudShaderGeometry::TEXTURE_INDEX_Y); - program->addBindAttribLocation("wScale", CloudShaderGeometry::WIDTH); - program->addBindAttribLocation("hScale", CloudShaderGeometry::HEIGHT); - program->addBindAttribLocation("shade", CloudShaderGeometry::SHADE); - - 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. - 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); } SGNewCloud::~SGNewCloud() { } -osg::Geometry* createOrthQuad(float w, float h, int varieties_x, int varieties_y) +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. @@ -287,59 +166,94 @@ osg::Geometry* createOrthQuad(float w, float h, int varieties_x, int varieties_y 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() { - LOD* result = new LOD; - Geode* geode = new Geode; - CloudShaderGeometry* sg = new CloudShaderGeometry(num_textures_x, num_textures_y); +osg::ref_ptr SGNewCloud::genCloud() { - Geometry* quad = createOrthQuad(min_sprite_width, min_sprite_height, num_textures_x, num_textures_y); + osg::ref_ptr geode = new EffectGeode; + + CloudShaderGeometry* sg = new CloudShaderGeometry(num_textures_x, num_textures_y, max_width, max_height); - // Determine how big this specific cloud instance is. - float width = min_width + sg_random() * (max_width - min_width); - float height = min_height + sg_random() * (max_height - min_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; - for (int i = 0; i < num_sprites; i++) + // 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()); + + for (int i = 0; i < n_sprites; i++) { - // Determine the position of the sprite. - SGVec3f *pos = new SGVec3f(Rnd(width), - Rnd(width), - Rnd(height)); + // 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 - // 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; - - // The shade varies from bottom_shade to 1.0 non-linearly - float shade; - if (pos->z() > 0.0f) { - shade = 1.0f; + float x, y, z; + + if (i == 0) { + x = 0; + y = 0; + z = 0; } else { - shade = ((2 * pos->z() + height) / height) * (1 - bottom_shade) + bottom_shade; + 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; } - // Determine the sprite texture indexes; + // 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) + { + sprite_height = sprite_width * min_sprite_width / min_sprite_height; + } + + 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; + } + + // 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); + + // 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--; } - sg->addSprite(*pos, index_x, index_y, sprite_width, sprite_height, shade, cull_distance_squared); + 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->setStateSet(stateSet.get()); - result->addChild(geode, 0, 20000); - return result; + geode->setEffect(effect.get()); + + return geode; } +