X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;ds=sidebyside;f=simgear%2Fscene%2Fsky%2Fnewcloud.cxx;h=6d35ad998b224074b7ebe08d8385f7c8bb38986a;hb=d04cf4d8978866eb80a1639b6d4ddfe387338c77;hp=efdf0c559d5768d152851092167841d6dda1c345;hpb=006f90997a8eef6704de2511e38fcc786672308d;p=simgear.git diff --git a/simgear/scene/sky/newcloud.cxx b/simgear/scene/sky/newcloud.cxx index efdf0c55..6d35ad99 100644 --- a/simgear/scene/sky/newcloud.cxx +++ b/simgear/scene/sky/newcloud.cxx @@ -44,7 +44,9 @@ #include #include #include +#include #include +#include #include #include @@ -60,76 +62,14 @@ using namespace simgear; using namespace osg; -typedef std::map > StateSetMap; +namespace +{ +typedef std::map > EffectMap; +EffectMap effectMap; +} -StateSetMap cloudTextureMap; double SGNewCloud::sprite_density = 1.0; -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. Equally at large distances it also fades out. - " gl_FrontColor.a = min(smoothstep(10.0, 100.0, fogCoord), 1 - smoothstep(15000.0, 20000.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.rgb = mix(gl_Fog.color.rgb, finalColor.rgb, fogFactor );\n" - " gl_FragColor.a = finalColor.a;\n" - "}\n"; - SGNewCloud::SGNewCloud(string type, const SGPath &tex_path, string tex, @@ -160,75 +100,26 @@ 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 - stateSet->setAttributeAndModes(attribFactory->getSmoothShadeModel()); - stateSet->setAttributeAndModes(attribFactory->getStandardBlendFunc()); - - stateSet->setTextureAttributeAndModes(0, tex, osg::StateAttribute::ON ); - stateSet->setRenderBinDetails(osg::StateSet::TRANSPARENT_BIN, "DepthSortedBin"); - // Turn off z buffer writes. Standard hack for - // semi-transparent geometry to avoid sorting / flickering - // artifacts. - stateSet->setAttributeAndModes(attribFactory->getDepthWritesDisabled()); - 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.05f); - 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); + 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() { @@ -278,14 +169,16 @@ 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() { - osg::ref_ptr geode = new Geode; + osg::ref_ptr geode = new EffectGeode; CloudShaderGeometry* sg = new CloudShaderGeometry(num_textures_x, num_textures_y, max_width, max_height); @@ -299,27 +192,19 @@ osg::ref_ptr SGNewCloud::genCloud() { // 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; + // 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; @@ -328,29 +213,36 @@ 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, + sg->addSprite(SGVec3f(x, y, z), index_x, index_y, sprite_width, @@ -363,7 +255,7 @@ osg::ref_ptr SGNewCloud::genCloud() { sg->setGeometry(quad); geode->addDrawable(sg); geode->setName("3D cloud"); - geode->setStateSet(stateSet.get()); + geode->setEffect(effect.get()); return geode; }