User:LindaB Helendale/meshLODanalyzer
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>