This is an automated email from the git hooks/post-receive script. sebastic pushed a commit to branch experimental in repository osgearth.
commit 39eb669428d93e6081e68a95bc30e84c94b66ee1 Author: Bas Couwenberg <[email protected]> Date: Wed Sep 14 08:56:22 2016 +0200 Imported Upstream version 2.8~rc3+dfsg --- src/osgEarthDrivers/cache_rocksdb/CMakeLists.txt | 4 +- src/osgEarthDrivers/engine_mp/MPGeometry.cpp | 17 +-- src/osgEarthDrivers/engine_rex/TileNode.cpp | 15 +-- src/osgEarthFeatures/MVT.cpp | 16 +-- src/osgEarthSplat/Splat.Noise.glsl | 3 +- src/osgEarthSplat/Splat.frag.glsl | 126 +++++++++-------------- src/osgEarthSplat/Splat.util.glsl | 39 +------ src/osgEarthSplat/Splat.vert.model.glsl | 3 +- src/osgEarthSplat/Splat.vert.view.glsl | 9 +- src/osgEarthUtil/Graticule.vert.glsl | 4 +- 10 files changed, 96 insertions(+), 140 deletions(-) diff --git a/src/osgEarthDrivers/cache_rocksdb/CMakeLists.txt b/src/osgEarthDrivers/cache_rocksdb/CMakeLists.txt index 10132d5..a626c8f 100644 --- a/src/osgEarthDrivers/cache_rocksdb/CMakeLists.txt +++ b/src/osgEarthDrivers/cache_rocksdb/CMakeLists.txt @@ -16,7 +16,9 @@ SET(TARGET_SRC SET(TARGET_LIBRARIES_VARS ROCKSDB_LIBRARY) -SET(TARGET_EXTERNAL_LIBRARIES ws2_32 winmm rpcrt4) +IF(WIN32) + SET(TARGET_EXTERNAL_LIBRARIES ws2_32 winmm rpcrt4) +ENDIF(WIN32) SETUP_PLUGIN(osgearth_cache_rocksdb) diff --git a/src/osgEarthDrivers/engine_mp/MPGeometry.cpp b/src/osgEarthDrivers/engine_mp/MPGeometry.cpp index 141065c..b6ccfc6 100644 --- a/src/osgEarthDrivers/engine_mp/MPGeometry.cpp +++ b/src/osgEarthDrivers/engine_mp/MPGeometry.cpp @@ -92,16 +92,19 @@ _supportsGLSL(false) { _supportsGLSL = Registry::capabilities().supportsGLSL(); - // Encode the tile key in a uniform. Note! The X and Y components are scaled - // to that we don't use precision on the GPU when the values get large. - // If you need the raw X and Y, you must scale them back up on the GPU. + // Encode the tile key in a uniform. Note! The X and Y components are presented + // modulo 2^16 form so they don't overrun single-precision space. unsigned tw, th; key.getProfile()->getNumTiles(key.getLOD(), tw, th); - - const float tileXYScale = 0.0625f; // 1/16 + + const double m = pow(2.0, 16.0); + + double x = (double)key.getTileX(); + double y = (double)(th - key.getTileY()-1); + _tileKeyValue.set( - tileXYScale * (float)(key.getTileX()), - tileXYScale * (float)(th-key.getTileY()-1.0f), + (float)fmod(x, m), + (float)fmod(y, m), (float)key.getLOD(), -1.0f); diff --git a/src/osgEarthDrivers/engine_rex/TileNode.cpp b/src/osgEarthDrivers/engine_rex/TileNode.cpp index 40c5e39..23aaae9 100644 --- a/src/osgEarthDrivers/engine_rex/TileNode.cpp +++ b/src/osgEarthDrivers/engine_rex/TileNode.cpp @@ -211,21 +211,22 @@ TileNode::updateTileUniforms(const SelectionInfo& selectionInfo) float width = std::max( (bbox.xMax()-bbox.xMin()), (bbox.yMax()-bbox.yMin()) ); - // Encode the tile key in a uniform. Note! The X and Y components are scaled - // to that we don't use precision on the GPU when the values get large. - // If you need the raw X and Y, you must scale them back up on the GPU. + // Encode the tile key in a uniform. Note! The X and Y components are presented + // modulo 2^16 form so they don't overrun single-precision space. unsigned tw, th; _key.getProfile()->getNumTiles(_key.getLOD(), tw, th); - const float tileXYScale = 0.0625f; // 1/16 + const double m = pow(2.0, 16.0); + + double x = (double)_key.getTileX(); + double y = (double)(th - _key.getTileY()-1); _tileKeyUniform->set(osg::Vec4f( - tileXYScale * (float)(_key.getTileX()), - tileXYScale * (float)(th-_key.getTileY()-1.0f), + (float)fmod(x, m), + (float)fmod(y, m), (float)_key.getLOD(), width)); - // update the morph constants float start = (float)selectionInfo.visParameters(_key.getLOD())._fMorphStart; diff --git a/src/osgEarthFeatures/MVT.cpp b/src/osgEarthFeatures/MVT.cpp index fea0267..ae2d3fe 100644 --- a/src/osgEarthFeatures/MVT.cpp +++ b/src/osgEarthFeatures/MVT.cpp @@ -90,11 +90,12 @@ bool { const mapnik::vector::tile_layer &layer = tile.layers().Get(i); + for (unsigned int j = 0; j < layer.features().size(); j++) { const mapnik::vector::tile_feature &feature = layer.features().Get(j); - osg::ref_ptr< osgEarth::Symbology::Geometry > geometry; + osg::ref_ptr< osgEarth::Symbology::Geometry > geometry; eGeomType geomType = static_cast<eGeomType>(feature.type()); if (geomType == ::Polygon) @@ -115,7 +116,10 @@ bool } osg::ref_ptr< Feature > oeFeature = new Feature(geometry, key.getProfile()->getSRS()); - features.push_back(oeFeature.get()); + features.push_back(oeFeature.get()); + + // Set the layer name as "mvt_layer" so we can filter it later + oeFeature->set("mvt_layer", layer.name()); // Read attributes for (unsigned int k = 0; k < feature.tags().size(); k+=2) @@ -159,7 +163,7 @@ bool StringTokenizer tok("=>"); StringVector tized; - tok.tokenize(other_tags, tized); + tok.tokenize(other_tags, tized); if (tized.size() == 3) { if (tized[0] == "height") @@ -169,7 +173,7 @@ bool float height = as<float>(value, FLT_MAX); if (height != FLT_MAX) { - oeFeature->set("height", height); + oeFeature->set("height", height); } } } @@ -193,7 +197,7 @@ bool unsigned int cmd_length = feature.geometry(k++); cmd = cmd_length & ((1 << cmd_bits) - 1); length = cmd_length >> cmd_bits; - } + } if (length > 0) { length--; @@ -223,7 +227,7 @@ bool if (geometry->getType() == Geometry::TYPE_POLYGON) { - geometry->rewind(osgEarth::Symbology::Geometry::ORIENTATION_CCW); + geometry->rewind(osgEarth::Symbology::Geometry::ORIENTATION_CCW); } } } diff --git a/src/osgEarthSplat/Splat.Noise.glsl b/src/osgEarthSplat/Splat.Noise.glsl index 39c599b..fccd2d3 100644 --- a/src/osgEarthSplat/Splat.Noise.glsl +++ b/src/osgEarthSplat/Splat.Noise.glsl @@ -1,4 +1,5 @@ -#version 110 +#version $GLSL_VERSION_STR + // // Description : Array and textureless GLSL 2D/3D/4D simplex // noise functions. diff --git a/src/osgEarthSplat/Splat.frag.glsl b/src/osgEarthSplat/Splat.frag.glsl index 8af4f79..ebb2b2d 100644 --- a/src/osgEarthSplat/Splat.frag.glsl +++ b/src/osgEarthSplat/Splat.frag.glsl @@ -1,4 +1,8 @@ -#version 330 +#version $GLSL_VERSION_STR + +#if(__VERSION__ < 400) +#extension GL_ARB_gpu_shader5 : enable // textureGather +#endif #pragma vp_entryPoint oe_splat_complex #pragma vp_location fragment_coloring @@ -15,32 +19,27 @@ #pragma include Splat.types.glsl #pragma include Splat.frag.common.glsl -// ref: Splat.getRenderInfo.frag.glsl -//oe_SplatRenderInfo oe_splat_getRenderInfo(in float value, inout oe_SplatEnv env); - // from: Splat.util.glsl void oe_splat_getLodBlend(in float range, out float lod0, out float rangeOuter, out float rangeInner, out float clampedRange); -vec2 oe_splat_getSplatCoords(in vec2 coords, in float lod); + +// from terrain SDK: +vec2 oe_terrain_scaleCoordsToRefLOD(in vec2 tc, in float refLOD); // from the terrain engine: -in vec4 oe_layer_tilec; -uniform vec4 oe_tile_key; +in vec4 oe_layer_tilec; // unit tile coords // from the vertex shader: -in vec2 oe_splat_covtc; -in float oe_splat_range; +in vec2 oe_splat_covtc; // coverage texture coords +in float oe_splat_range; // distance from camera to vertex +flat in float oe_splat_coverageTexSize; // size of coverage texture // from SplatTerrainEffect: -uniform float oe_splat_warp; -uniform float oe_splat_blur; uniform sampler2D oe_splat_coverageTex; uniform sampler2DArray oe_splatTex; -//uniform float oe_splat_scaleOffset; uniform int oe_splat_scaleOffsetInt; uniform float oe_splat_detailRange; uniform float oe_splat_noiseScale; -uniform float oe_splat_useBilinear; // 1=true, -1=false #ifdef SPLAT_EDIT uniform float oe_splat_brightness; @@ -49,15 +48,16 @@ uniform float oe_splat_threshold; uniform float oe_splat_minSlope; #endif - +// lookup table containing the coverage value => texture index mappings uniform samplerBuffer oe_splat_coverageLUT; + // reads the encoded splatting render information for a coverage value. // this data was encoded in Surface::createLUTBUffer(). void oe_splat_getRenderInfo(in float value, in oe_SplatEnv env, out oe_SplatRenderInfo ri) { const int num_lods = 26; - const float inv255 = 1.0/255.0; + const float inv255 = 0.00392156862; int index = int(value)*num_lods + int(env.lod); @@ -76,15 +76,6 @@ void oe_splat_getRenderInfo(in float value, in oe_SplatEnv env, out oe_SplatRend ri.minSlope = fract(t[3])*100.0; } -// Warps the coverage sampling coordinates to mitigate blockiness. -vec2 oe_splat_warpCoverageCoords(in vec2 splat_tc, in oe_SplatEnv env) -{ - vec2 seed = oe_splat_covtc; - float n1 = 2.0*env.noise.y-1.0; - vec2 tc = oe_splat_covtc + n1*oe_splat_warp; - return clamp(tc, 0.0, 1.0); -} - vec4 oe_splat_getTexel(in float index, in vec2 tc) { return texture(oe_splatTex, vec3(tc, index)); @@ -94,7 +85,7 @@ vec4 oe_splat_getTexel(in float index, in vec2 tc) // Returns the weighting factor in the alpha channel. vec4 oe_splat_getDetailTexel(in oe_SplatRenderInfo ri, in vec2 tc, in oe_SplatEnv env) { - float hasDetail = clamp(ri.detailIndex+1.0, 0.0, 1.0); //ri.detailIndex >= 0.0 ? 1.0 : 0.0; + float hasDetail = clamp(ri.detailIndex+1.0, 0.0, 1.0); #ifdef SPLAT_EDIT float brightness = oe_splat_brightness; @@ -129,16 +120,14 @@ vec4 oe_splat_getDetailTexel(in oe_SplatRenderInfo ri, in vec2 tc, in oe_SplatEn n = n < threshold ? 0.0 : n; // sample the texel and return it. - vec4 result = oe_splat_getTexel( ri.detailIndex, tc); - //vec4 result = oe_splat_getTexel( max(ri.detailIndex,0), tc); + vec4 result = oe_splat_getTexel(ri.detailIndex, tc); return vec4(result.rgb, hasDetail*n); } // Generates a texel using nearest-neighbor coverage sampling. vec4 oe_splat_nearest(in vec2 splat_tc, inout oe_SplatEnv env) { - vec2 tc = oe_splat_covtc; //oe_splat_warpCoverageCoords(splat_tc, env); - float coverageValue = texture2D(oe_splat_coverageTex, tc).r; + float coverageValue = texture(oe_splat_coverageTex, oe_splat_covtc).r; oe_SplatRenderInfo ri; oe_splat_getRenderInfo(coverageValue, env, ri); vec4 primary = oe_splat_getTexel(ri.primaryIndex, splat_tc); @@ -152,47 +141,13 @@ vec4 oe_splat_bilinear(in vec2 splat_tc, inout oe_SplatEnv env) { vec4 texel = vec4(0,0,0,1); - //TODO: coverage warping is slow due to the noise function. Consider removing/reworking. - vec2 tc = oe_splat_covtc; //oe_splat_warpCoverageCoords(splat_tc, env); - - float a = oe_splat_blur; - float pixelWidth = a/256.0; // 256 = hard-coded cov tex size //TODO - float halfPixelWidth = 0.5*pixelWidth; - float pixelWidth2 = pixelWidth*pixelWidth; - - // Find the four quantized coverage coordinates that form a box around the actual - // coverage coordinates, where each quantized coord is at the center of a coverage texel. - vec2 rem = mod(tc, pixelWidth); - vec2 sw; - sw.x = tc.x - rem.x + (rem.x >= halfPixelWidth ? halfPixelWidth : -halfPixelWidth); - sw.y = tc.y - rem.y + (rem.y >= halfPixelWidth ? halfPixelWidth : -halfPixelWidth); - vec2 ne = sw + pixelWidth; - vec2 nw = vec2(sw.x, ne.y); - vec2 se = vec2(ne.x, sw.y); - - // Calculate the weighting for each corner. - vec2 dsw = tc-sw; - vec2 dse = tc-se; - vec2 dne = tc-ne; - vec2 dnw = tc-nw; - - float sw_weight = max(pixelWidth2-dot(dsw,dsw),0.0); - float se_weight = max(pixelWidth2-dot(dse,dse),0.0); - float ne_weight = max(pixelWidth2-dot(dne,dne),0.0); - float nw_weight = max(pixelWidth2-dot(dnw,dnw),0.0); - - // normalize the weights so they total 1.0 - float invTotalWeight = 1.0/(sw_weight+se_weight+ne_weight+nw_weight); - sw_weight *= invTotalWeight; - se_weight *= invTotalWeight; - ne_weight *= invTotalWeight; - nw_weight *= invTotalWeight; - - // Sample coverage values using quantized corner coords: - float value_sw = texture(oe_splat_coverageTex, clamp(sw, 0.0, 1.0)).r; - float value_se = texture(oe_splat_coverageTex, clamp(se, 0.0, 1.0)).r; - float value_ne = texture(oe_splat_coverageTex, clamp(ne, 0.0, 1.0)).r; - float value_nw = texture(oe_splat_coverageTex, clamp(nw, 0.0, 1.0)).r; + float size = oe_splat_coverageTexSize; + + vec4 value = textureGather(oe_splat_coverageTex, oe_splat_covtc, 0); + float value_sw = value.w; + float value_se = value.z; + float value_ne = value.y; + float value_nw = value.x; // Build the render info data for each corner: oe_SplatRenderInfo ri_sw; oe_splat_getRenderInfo(value_sw, env, ri_sw); @@ -215,6 +170,7 @@ vec4 oe_splat_bilinear(in vec2 splat_tc, inout oe_SplatEnv env) vec4 ne_detail = detailToggle * oe_splat_getDetailTexel(ri_ne, splat_tc, env); vec4 nw_detail = detailToggle * oe_splat_getDetailTexel(ri_nw, splat_tc, env); +#if 0 // Combine everything based on weighting: texel.rgb = sw_weight * mix(sw_primary, sw_detail.rgb, sw_detail.a) + @@ -222,6 +178,25 @@ vec4 oe_splat_bilinear(in vec2 splat_tc, inout oe_SplatEnv env) ne_weight * mix(ne_primary, ne_detail.rgb, ne_detail.a) + nw_weight * mix(nw_primary, nw_detail.rgb, nw_detail.a); +#else + + vec3 nw_mix = mix(nw_primary, nw_detail.rgb, nw_detail.a); + vec3 ne_mix = mix(ne_primary, ne_detail.rgb, ne_detail.a); + vec3 sw_mix = mix(sw_primary, sw_detail.rgb, sw_detail.a); + vec3 se_mix = mix(se_primary, se_detail.rgb, se_detail.a); + + //float cellSize = 1.0/size; + //vec2 g1 = fract(oe_splat_covtc*size); //(size-1.0)); + //vec2 g2 = fract(g1-0.5+pixelWidth); + vec2 weight = fract( oe_splat_covtc*size - 0.5+(1.0/size) ); //cellSize); + + vec3 temp0 = mix(nw_mix, ne_mix, weight.x); + vec3 temp1 = mix(sw_mix, se_mix, weight.x); + + texel.rgb = mix(temp1, temp0, weight.y); + +#endif + return texel; } @@ -254,7 +229,7 @@ vec4 oe_splat_getNoise(in vec2 tc) void oe_splat_simple(inout vec4 color) { float noiseLOD = floor(oe_splat_noiseScale); - vec2 noiseCoords = oe_splat_getSplatCoords(oe_layer_tilec.st, noiseLOD); + vec2 noiseCoords = oe_terrain_scaleCoordsToRefLOD(oe_layer_tilec.st, noiseLOD); oe_SplatEnv env; env.range = oe_splat_range; @@ -265,7 +240,7 @@ void oe_splat_simple(inout vec4 color) float lod0; float rangeOuter, rangeInner; oe_splat_getLodBlend(oe_splat_range, lod0, rangeOuter, rangeInner, env.range); - vec2 tc = oe_splat_getSplatCoords(oe_layer_tilec.st, lod0 + float(oe_splat_scaleOffsetInt)); + vec2 tc = oe_terrain_scaleCoordsToRefLOD(oe_layer_tilec.st, lod0 + float(oe_splat_scaleOffsetInt)); color = oe_splat_bilinear(tc, env); @@ -277,7 +252,7 @@ void oe_splat_complex(inout vec4 color) { // Noise coords. float noiseLOD = floor(oe_splat_noiseScale); - vec2 noiseCoords = oe_splat_getSplatCoords(oe_layer_tilec.st, noiseLOD); //TODO: move to VS for slight speedup + vec2 noiseCoords = oe_terrain_scaleCoordsToRefLOD(oe_layer_tilec.st, noiseLOD); //TODO: move to VS for slight speedup oe_SplatEnv env; env.range = oe_splat_range; @@ -286,7 +261,6 @@ void oe_splat_complex(inout vec4 color) env.elevation = 0.0; // quantize the scale offset so we take the hit in the FS - //float scaleOffset = oe_splat_scaleOffset >= 0.0 ? ceil(oe_splat_scaleOffset) : floor(oe_splat_scaleOffset); float scaleOffset = float(oe_splat_scaleOffsetInt); // Calculate the 2 LODs we need to blend. We have to do this in the FS because @@ -296,11 +270,11 @@ void oe_splat_complex(inout vec4 color) oe_splat_getLodBlend(oe_splat_range, lod0, rangeOuter, rangeInner, env.range); // Sample the two LODs: - vec2 tc0 = oe_splat_getSplatCoords(oe_layer_tilec.st, lod0 + scaleOffset); + vec2 tc0 = oe_terrain_scaleCoordsToRefLOD(oe_layer_tilec.st, lod0 + scaleOffset); env.lod = lod0; vec4 texel0 = oe_splat_bilinear(tc0, env); - vec2 tc1 = oe_splat_getSplatCoords(oe_layer_tilec.st, lod0 + 1.0 + scaleOffset); + vec2 tc1 = oe_terrain_scaleCoordsToRefLOD(oe_layer_tilec.st, lod0 + 1.0 + scaleOffset); env.lod = lod0+1.0; vec4 texel1 = oe_splat_bilinear(tc1, env); diff --git a/src/osgEarthSplat/Splat.util.glsl b/src/osgEarthSplat/Splat.util.glsl index 848dbd4..0dda668 100644 --- a/src/osgEarthSplat/Splat.util.glsl +++ b/src/osgEarthSplat/Splat.util.glsl @@ -1,7 +1,6 @@ -#version 120 -#pragma vp_location fragment_coloring +#version $GLSL_VERSION_STR -uniform vec4 oe_tile_key; // osgEarth TileKey +#pragma vp_location fragment_coloring // Number of LOD range. Do not increase this past 25; doing so will result in precision errors // and rendering artifacts when the camera is very close to the ground. @@ -64,37 +63,3 @@ oe_splat_getLodBlend(in float range, out float out_LOD0, out float out_rangeOute out_rangeOuter = oe_SplatRanges[int(out_LOD0)]; out_rangeInner = oe_SplatRanges[int(out_LOD0)+1]; } - -float -oe_splat_getRangeForLod(in float lod) -{ - return oe_SplatRanges[int(lod)]; -} - -/** - * Scales the incoming tile splat coordinates to match the requested - * LOD level. We offset the level from the current tile key's LOD (.z) - * because otherwise you run into single-precision jitter at high LODs. - */ -vec2 -oe_splat_getSplatCoords(in vec2 tc, float lod) -{ - float dL = oe_tile_key.z - lod; - float factor = exp2(dL); - float invFactor = 1.0/factor; - vec2 scale = vec2(invFactor); - vec2 result = tc * scale; - - // For upsampling we need to calculate an offset as well - vec2 a = floor(oe_tile_key.xy * invFactor); - vec2 b = a * factor; - vec2 c = (a+1.0) * factor; - vec2 offset = (oe_tile_key.xy-b)/(c-b); - - // only apply offset if factor >= 1.0 - float m = floor(clamp(factor,0.0,1.0)); - result += (m*offset); - - return result; -} - diff --git a/src/osgEarthSplat/Splat.vert.model.glsl b/src/osgEarthSplat/Splat.vert.model.glsl index a0b88cc..09f9dc2 100644 --- a/src/osgEarthSplat/Splat.vert.model.glsl +++ b/src/osgEarthSplat/Splat.vert.model.glsl @@ -1,4 +1,5 @@ -#version 330 +#version $GLSL_VERSION_STR + #pragma vp_entryPoint oe_splat_vertex_model #pragma vp_location vertex_model #pragma vp_order 0.5 diff --git a/src/osgEarthSplat/Splat.vert.view.glsl b/src/osgEarthSplat/Splat.vert.view.glsl index a4f9dd5..b13c1ab 100644 --- a/src/osgEarthSplat/Splat.vert.view.glsl +++ b/src/osgEarthSplat/Splat.vert.view.glsl @@ -1,4 +1,4 @@ -#version 330 +#version $GLSL_VERSION_STR #pragma vp_entryPoint oe_splat_vertex_view #pragma vp_location vertex_view @@ -10,6 +10,9 @@ out vec4 oe_layer_tilec; out float oe_splat_range; out vec2 oe_splat_covtc; +uniform sampler2D oe_splat_coverageTex; +flat out float oe_splat_coverageTexSize; + uniform mat4 COVERAGE_TEXTURE_MATRIX; // assigned at runtime @@ -21,4 +24,8 @@ void oe_splat_vertex_view(inout vec4 VertexVIEW) // calculate the coverage sampling coordinates. The texture matrix accounts // for any super-sampling that might be in effect for the current LOD. oe_splat_covtc = (COVERAGE_TEXTURE_MATRIX * oe_layer_tilec).st; + + // Precalculate the size of the coverage texture. This is faster than + // calling textureSize per pixel in the fragment shader. + oe_splat_coverageTexSize = textureSize(oe_splat_coverageTex, 0).x; } diff --git a/src/osgEarthUtil/Graticule.vert.glsl b/src/osgEarthUtil/Graticule.vert.glsl index 1e78ee8..85f985d 100644 --- a/src/osgEarthUtil/Graticule.vert.glsl +++ b/src/osgEarthUtil/Graticule.vert.glsl @@ -8,12 +8,10 @@ uniform vec4 oe_tile_key; out vec4 oe_layer_tilec; out vec2 oe_graticule_coord; -// oe_tile_key.xy are scaled by 1/16 for precision -#define TILE_XY_SCALE_FACTOR 16 void oe_graticule_vertex(inout vec4 vertex) { // calculate long and lat from [0..1] across the profile: - vec2 r = (oe_tile_key.xy*TILE_XY_SCALE_FACTOR + oe_layer_tilec.xy)/exp2(oe_tile_key.z); + vec2 r = (oe_tile_key.xy + oe_layer_tilec.xy)/exp2(oe_tile_key.z); oe_graticule_coord = vec2(0.5*r.x, r.y); } -- Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-grass/osgearth.git _______________________________________________ Pkg-grass-devel mailing list [email protected] http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/pkg-grass-devel

