User:LindaB Helendale/meshLODschemeCalculator

From Second Life Wiki
Jump to navigation Jump to search

The script meshLODschemeCalculator prints a table of the Download cost/Streaming cost component of Land Impact for given LOD reduction scheme.

Source of the algorithm: http://wiki.secondlife.com/wiki/Mesh/Mesh_Streaming_Cost

Usage: drop this script in an object and see the usage on rez.

Download cost depends on the byte size of each LOD in the asset server, including the vertex and face data, UV vertices and faces, etc, and the data is compressed by gzip. In general it's not easy to know those numbers, but roughly the triangle counts are on the same order of magnitude as the numbers used in this script. The LI results should be taken as relative, showing the relative contribution of different LODs and the effect of the mesh scale for a given scheme, such as 2000 : 500 : 120: 60.

For the actual download costs of each LOD in an uploaded mesh see https://wiki.secondlife.com/wiki/User:LindaB_Helendale/meshLODanalyzer

Partly the same information this script prints can be found in Drongle McMahon's graphs http://community.secondlife.com/t5/Mesh/Download-weight-and-size-Some-graphs/m-p/1058609#M5642 while this script lets you display any LOD scheme.

 
// This script prints out a table of mesh land impact download cost for each LOD 
// (c) LindaB Helendale, permission to use this script in any way granted.
//
// See state_entry for more info.
//
// BUGS: alignment of the column headers and numeric data depends on the UI size setting,
//       this should look okish for default size 1.0
//
// To get the actual triangle counts (or rather mesh size in bytes/16) for an uploaded mesh, see
//    http://wiki.secondlife.com/wiki/User:LindaB_Helendale/getMeshLODsize
// This script is for seeing the effect of the LOD scheme on the LI as function of the radius.
 
 list LODweigths(float r) {
    float max_distance = 512;
    float dlowest = min(r/0.03, max_distance);
    float dlow = min(r/0.06, max_distance);
    float dmid = min(r/0.24, max_distance);
 
    float max_area = 102932;
    float min_area = 1;
 
    float high_area = min(PI*dmid*dmid, max_area);
    float mid_area = min(PI*dlow*dlow, max_area);
    float low_area = min(PI*dlowest*dlowest, max_area);
    float lowest_area = max_area;
 
    lowest_area = lowest_area - low_area;
    low_area = low_area - mid_area;
    mid_area = mid_area - high_area;
 
    high_area = llclamp(high_area, min_area, max_area);
    mid_area = llclamp(mid_area, min_area, max_area);
    low_area = llclamp(low_area, min_area, max_area);
    lowest_area = llclamp(lowest_area, min_area, max_area);
 
    float total_area = high_area + mid_area + low_area + lowest_area;
    high_area = high_area / total_area;
    mid_area = mid_area / total_area;
    low_area = low_area / total_area;
    lowest_area = lowest_area / total_area;
 
    list weights=[high_area, mid_area, low_area, lowest_area];
    return weights ;
}
 
float min(float a, float b) {
    if (a<b) return a;
    else     return b;
}
float llclamp(float b,float bmin,float bmax) {
    if (b<bmin) b=bmin;
    if (b>bmax) b=bmax;
    return b;    
}
 
integer CMDCHANNEL=22;

print_LODs(list triangles, float r, integer percentage) {    
    integer i;
    list W=LODweigths(r);
    float LItotal;

    list LI;
    for(i=0;i<4;i++) {
        LI += llList2Float(triangles,i)*llList2Float(W,i) * 3.0/50.0;   // = 15000/MeshTriangleBudget
    }
    LItotal = llListStatistics(LIST_STAT_SUM, LI);
    string txt;
    for(i=0;i<4;i++) {
        float li = llList2Float(LI,i);
        if (percentage) li *= 100.0/LItotal;
        txt +=  "      " + llGetSubString((string)li,0,7) ;
    }
    llOwnerSay(llGetSubString((string)r,0,7) + "    " + llGetSubString((string)LItotal,0,7) + txt);
}

 
default
{
    state_entry()
    {
        llListen(CMDCHANNEL,"",llGetOwner(),"");
        llOwnerSay("==== Prints out a table of mesh land impact download cost for each LOD ======");
        llOwnerSay("This script can be used to find a LOD scheme that gives low LI when you have some");
        llOwnerSay("idea of the scale of the mesh in-world.");
        llOwnerSay("  ");
        llOwnerSay("Usage: /22 LOD <highest_lod> <mid_lod> <low_lod> <lowest_lod>");
        llOwnerSay("            /22 LOD <highest_lod> <mid_lod> <low_lod> <lowest_lod>  R <radius>");
        llOwnerSay("where <number> means that you replace it with value you want to use.");
        llOwnerSay("  ");
        llOwnerSay("The numbers above are the sizes of the gzipped mesh data in asset server divided by 16");
        llOwnerSay("but the face counts are roughly the same order of magnitude. ");
        llOwnerSay("As an example, a mesh with 7186 faces in 5 uv map islands had the size value of 8808.");
        llOwnerSay("To find the actual numbers see http://wiki.secondlife.com/wiki/User:LindaB_Helendale/getMeshLODsize");
        llOwnerSay("but you don't need them to design theLOD reduction scheme.");
        llOwnerSay("  ");
        llOwnerSay("To see the LOD effects for LOD optimization, you can use any relative counts there,");
        llOwnerSay("like /22 LOD 2000 500 120 60 and you will see the relative effect of each LOD on the LI.");
        llOwnerSay("  ");
        llOwnerSay("If you give the radius the LI is calculated for that radius only, otherwise for a set of radii.");
        llOwnerSay("Radius is the radius of the bounding sphere enclosing the mesh, that is, llVecMag(llGetScale())/2.0");
        llOwnerSay("  ");
        llOwnerSay("If you add % at the end of the command line, the LOD contributions are shown as percentages");
        llOwnerSay("  ");
        llOwnerSay("Example:");
        llOwnerSay("/22 LOD 4000 1000 250 100");
        llOwnerSay("/22 LOD 4000 1000 250 20");
        llOwnerSay("      Note how reducing the lowest LOD poly count decreases the LI on small meshes considerably.");
        llOwnerSay("/22 LOD 1000  250 100 20 %");
        llOwnerSay("/22 LOD 1000  250 100 20 R 6.5 ");
    }
    
    on_rez(integer p) 
    {
        llResetScript();
    }
    
    listen( integer channel, string name, key id, string message)
    {
        list triangles; 
        
        integer percentage = llGetSubString(message,-1,-1)=="%" ;
        if (percentage) message = llGetSubString(message,0,-2);
        
        list    argv=llParseString2List(message,[" "],[]);
        integer argc=llGetListLength(argv);
        string  cmd=llList2String(argv,0);
        float radius=-1;
        if (cmd=="LOD") {
            triangles=[];
            integer i;
            for(i=1;i<=4;i++) {
                float s=(float)llList2String(argv,i);
                if (s<=0) {
                    llOwnerSay("Illegal LOD size [" + llList2String(argv,i) + "], must be positive value.");
                    return;
                }
                triangles += s;
            }
        }
        if (llGetListLength(argv)>5) {
            if (llList2String(argv,5)=="R") {
                radius=(float)llList2String(argv,6);
            }else{
                llOwnerSay("Extra stuff at the end of the command line ignored: [" + 
                    llDumpList2String(llList2List(argv,5,-1)," ") + "]");
            }
        }
        
        llOwnerSay("Radius       Total LI        highest LOD   mid LOD     low LOD     lowest LOD");
        if (radius<0) {
            print_LODs(triangles,0.1,percentage);
            print_LODs(triangles,0.25,percentage);
            print_LODs(triangles,0.5,percentage);
            for(radius=1;radius<20;radius++) print_LODs(triangles,radius,percentage);
            for(radius=20;radius<=64;radius+=5) print_LODs(triangles,radius,percentage);
            print_LODs(triangles,64,percentage);
        }else{
            print_LODs(triangles,radius,percentage);
        }      
    }
}