X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=simgear%2Fscene%2Fsky%2Fnewcloud.cxx;h=6d35ad998b224074b7ebe08d8385f7c8bb38986a;hb=d04cf4d8978866eb80a1639b6d4ddfe387338c77;hp=1ee3c5b3305ced0f75d18d4a7e479b5de27a3b86;hpb=27de1e271e780a0e680ff34a61541e1d43f294b1;p=simgear.git diff --git a/simgear/scene/sky/newcloud.cxx b/simgear/scene/sky/newcloud.cxx index 1ee3c5b3..6d35ad99 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,16 @@ #include #include #include +#include #include +#include #include #include #include +#include #include +#include #include "cloudfield.hxx" #include "newcloud.hxx" @@ -57,85 +62,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" - "attribute float textureIndexX;\n" - "attribute float textureIndexY;\n" - "attribute float wScale;\n" - "attribute float hScale;\n" - "attribute float shade;\n" - "attribute float cloud_height;\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"; +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, @@ -161,84 +97,29 @@ 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.001f); - 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); - program->addBindAttribLocation("cloud_height", CloudShaderGeometry::CLOUD_HEIGHT); - - 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); + ref_ptr options + = makeOptionsFromPath(tex_path); + ref_ptr sgOptions + = new SGReaderWriterXMLOptions(*options.get()); + if ((effect = makeEffect(pcloudEffect, true, sgOptions.get()))) + 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() { @@ -288,14 +169,18 @@ 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() { - Geode* geode = new Geode; - CloudShaderGeometry* sg = new CloudShaderGeometry(num_textures_x, num_textures_y); +osg::ref_ptr SGNewCloud::genCloud() { + + 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. Note that we subtract // the sprite size because the width/height is used to define the limits of @@ -305,26 +190,21 @@ osg::ref_ptr SGNewCloud::genCloud() { // 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.05f; + 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. 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. + // 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 = 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; @@ -333,41 +213,50 @@ osg::ref_ptr SGNewCloud::genCloud() { 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; + z = height * cos(elev) * 0.5f; } - SGVec3f *pos = new SGVec3f(x, y, z); - - // Determine the height and width as scaling factors on the minimum size (used to create the quad) + // 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) { + + // 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; + + // 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, - bottom_shade, - cull_distance_squared, - height * 0.5f); + 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()); + geode->setEffect(effect.get()); + return geode; } +