User:LindaB Helendale/meshLODanalyzer
< User:LindaB Helendale
Jump to navigation
Jump to search
Revision as of 04:02, 7 September 2015 by LindaB Helendale (talk | contribs)
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.
// 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();
}
}