User:LindaB Helendale/meshLODanalyzer

From Second Life Wiki
Jump to navigation Jump to search

Mesh LOD and Land Impact analyzer (c) Lindab Helendale. Permission to use this script in any way granted.


The script is combination of mesh asset server size script https://wiki.secondlife.com/wiki/User:LindaB_Helendale/getMeshLODsize and mesh land impact script https://wiki.secondlife.com/wiki/User:LindaB_Helendale/meshLODschemeCalculator

Drop the script in a mesh to see the contribution of LODs on the Land Impact and the effect of the scale of the mesh on the Land Impact.


<lsl> // Drop this script in a mesh object to see how the streaming (download) cost component of land impact // is calculated for that mesh. The script also shows how the download cost changes when the size of // the mesh is changed, with breakdown of the cost to each LOD. // // To test the performance of different LOD reduction schemes, see // http://wiki.secondlife.com/wiki/User:LindaB_Helendale/meshLODschemeCalculator // // The script is activated by dropping it in an object, resetting the script, rezzing the object, // or touching the object. // // (c) LindaB Helendale, permission to use this script in any way granted.


float getObjectStreamingCost() {

   return llList2Float(llGetObjectDetails(llGetKey(),[OBJECT_STREAMING_COST]),0);

}

float getRadius() {

   return llVecMag(llGetScale())/2.0 ;

}

setRadius(float R) {

   llSetScale(llGetScale()*R/getRadius());
   if (llFabs(getRadius()-R)>0.01)
       llOwnerSay("Warning: failed to set radius to " + (string)R + " The result was " + (string)getRadius());

}

list getLODtriangles() {

   // Measure the triangle count for mesh LODs used in calculating the Streaming Cost/Download Cost
   // component of the mesh Land Impact. Permission to use this script in any way granted.
   // (c) LindaB Helendale
   // See http://wiki.secondlife.com/wiki/User:LindaB_Helendale/getMeshLODsize for more info
   vector S=llGetScale();
   llSetScale(<1,1,1>); // we set equal scales to make sure the biggest radius is reached
   float MeshTriangleBudgetScaler=50.0/3.0; // = MeshTriangleBudget(250000)/15000
   list radius=[43.4422,   10.8605,    5.43027,    0.01];    
   list C;
   integer i;
   for(i=0;i<4;i++) {
       setRadius(llList2Float(radius,i));
       C += getObjectStreamingCost() * MeshTriangleBudgetScaler;
   }
   list tri;
   tri = [ llList2Float(C,0), 
           16.0/15.0*llList2Float(C,1) - 1.0/15.0*llList2Float(C,0),
             4.0/3.0*llList2Float(C,2) -  1.0/3.0*llList2Float(C,1),
           llList2Float(C,3)
         ];
   llSetScale(S);
   return tri;

}

float getLODbytesize(float triangles) {

   float MeshBytesPerTriangle=16;
   float MeshMetaDataDiscount=384;
   return triangles*MeshBytesPerTriangle + MeshMetaDataDiscount;

}

list LODdistances(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);
   return [dlowest, dlow, dmid];

}

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;    

}

list get_LI_per_LOD(list triangles, float r) {

   integer i;
   list W=LODweigths(r); 
   list LI;
   for(i=0;i<4;i++) {
       LI += llList2Float(triangles,i)*llList2Float(W,i) * 3.0/50.0;   // = 15000/MeshTriangleBudget
   }
   return LI;

}

print_LODs(list triangles, float r, integer percentage, integer showByteCounts) {

   integer i;
   float LItotal;

   list LI = get_LI_per_LOD(triangles,r);
   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);
   if (showByteCounts) {
       llOwnerSay(" ");
       txt="";
       for(i=0;i<4;i++) {
           float tri = llList2Float(triangles,i);
           txt += "       " + llGetSubString("        " + (string)llRound(tri),-8,-1) ;
       }
       llOwnerSay("LOD sizes in triangles:"  + txt);
       txt="";
       for(i=0;i<4;i++) {
           float byt = getLODbytesize(llList2Float(triangles,i));
           txt += "       " + llGetSubString("        " + (string)llRound(byt),-8,-1) ;
       }
       llOwnerSay("LOD sizes in bytes:  "  + txt);
       llOwnerSay(" ");
       llOwnerSay("Note that the 'size in triangles' is lower than the actual triangle count, due to gzip compression of the data in asset server.");
   }

}


show_landimpact_table() {

   integer percentage = FALSE;
   if (llGetNumberOfPrims()>1) {
       llOwnerSay("Use this script with an unlinked mesh.");
       return;
   }
   float radius=getRadius();
   list triangles=getLODtriangles();
   list LIcosts = llGetObjectDetails(llGetKey(),[OBJECT_SERVER_COST, OBJECT_STREAMING_COST,OBJECT_PHYSICS_COST]);
   llOwnerSay("Mesh land impact analysis");
   llOwnerSay("====================");
   llOwnerSay("Server cost: " + (string)llList2Float(LIcosts,0));
   llOwnerSay("Physics cost: " + (string)llList2Float(LIcosts,2));
   llOwnerSay("Streaming (download) cost: " + (string)llList2Float(LIcosts,1));
   float LIestimate =  llListStatistics(LIST_STAT_SUM,get_LI_per_LOD(triangles,radius));
   llOwnerSay("        Estimated DL cost:   " + (string)LIestimate);
   llOwnerSay("        Error of this model: " + (string)(llList2Float(LIcosts,1)-LIestimate));
   llOwnerSay(" ");
   llOwnerSay("LOD switch distances at the currect radius of " + (string)radius + " m with the default renderVolumeLODfactor debug settings:");
   list LODdist=LODdistances(radius);
   llOwnerSay("  Lowest to low: " + (string)llList2Float(LODdist,0) + " m");
   llOwnerSay("  Low to mid: " + (string)llList2Float(LODdist,1) + " m");
   llOwnerSay("  Mid to high: " + (string)llList2Float(LODdist,2) + " m");
   llOwnerSay(" ");
   llOwnerSay("Contribution of each LOD on the download cost:");
   llOwnerSay("Radius       Total LI        highest LOD   mid LOD     low LOD     lowest LOD");
   print_LODs(triangles,radius,percentage,TRUE);
   
   llOwnerSay(" ");
   llOwnerSay("Effect of the mesh bounding sphere radius on the download cost:");
   llOwnerSay("Radius       Total LI        highest LOD   mid LOD     low LOD     lowest LOD");
   print_LODs(triangles,0.1,percentage,FALSE);
   print_LODs(triangles,0.25,percentage,FALSE);
   print_LODs(triangles,0.5,percentage,FALSE);
   for(radius=1;radius<20;radius++) print_LODs(triangles,radius,percentage,FALSE);
   for(radius=20;radius<=55;radius+=5) print_LODs(triangles,radius,percentage,FALSE);

}

default {

   state_entry()
   {
       show_landimpact_table();
   }

   on_rez(integer p) 
   {
       show_landimpact_table();
   }
   touch_start(integer num) {
       show_landimpact_table();
   }

} </lsl>