User:LindaB Helendale/getMeshLODsize

From Second Life Wiki
Jump to navigation Jump to search

Script to find out the size of a mesh on all LOD levels, used in Land Impact calculation. (c) LindaB Helendale. Permission to use this script in any way granted.

After the triangle count estimation script there's also demo code to calculate the download/streaming cost for any mesh radius, given the triangle counts.

For full working script to get the download costs of each LOD in an uploaded mesh see https://wiki.secondlife.com/wiki/User:LindaB_Helendale/meshLODanalyzer

 
/********************************************************************************
Script to find out the size of a mesh on all LOD levels, used in Land Impact calculation.
(c) LindaB Helendale. Permission to use this script in any way granted.

The size (and thus LI download/streaming cost) depends on the number of vertices and 
number of triangles. (Though for typical mesh they correlate heavily with two triangles per vertex).
The mesh size depends also on the UV map, and maybe on some other attributes.
In addition there's gzip compression in storing the mesh data.

This script can be used to measure the effect of various mesh properties and attributes
on the size that's used in Land Impact calculation.

Usage:

Drop this script in the (non-linked) mesh and touch.
The script prints the triangles values for high, mid, low and lowest LOD
that are used in the Land Impact Download cost equations.


How it works:
 
See http://wiki.secondlife.com/wiki/Mesh/Mesh_Streaming_Cost for the equations.
The Streaming Cost / Downdload cost is weighted sum of the mesh size in asset 
server on each LOD, weighted by the area where the LOD is visible. The weights
are variables 'high_area', 'mid_area', 'low_area' and 'lowest_area' in the 
algorithm on the wiki page. 
See http://community.secondlife.com/t5/Mesh/Download-weight-and-size-Some-graphs/m-p/1058609#M5642
for graphs of the weights as function of mesh radius.

For a set of radii, the Streaming Cost is computed from a matrix 
containing the LOD weights as columns multiplied by the triangles vector.
The columns are the LOD curves in Drongle's graph image, sampled at the radius values.

          | high_area(r0) mid_area(r0) low_area(r0) lowest_area(r0) |    | tri_high  |
          | high_area(r1) mid_area(r1) low_area(r1) lowest_area(r1) | *  | tri_mid   |
          | ...                                                     |    | tri_low   |
          | high_area(rn) mid_area(rn) low_area(rn) lowest_area(rn) |    | tri_loest |

The result is multiplied by 15000 and divided by MeshTriangleBudget (250000) to give the LI.
The byte size can be calculated from the triangle count using the equations
    F32 triangles_low = llmax((F32) bytes_low-METADATA_DISCOUNT, MINIMUM_SIZE)/bytes_per_triangle;
with MeshBytesPerTriangle=16 and MeshMetaDataDiscount=384 (see viewer debug settings for the values)
but for comparing the LOD sizes the triangle count is what matters.

To estimate the LOD mesh sizes we invert the weight matrix W, 
    LI=W*tri => tri=inv(W)*LI
assuming we have 4 radii so W is a square matrix. (For more than 4 radii, use pseudo-inverse to get
least squares fitting tri=inv(W'*W)*W'*LI; but with properly chosen radii four samples is sufficient 
to find the triangle counts.)

The most informative radii are those when each LOD becomes irrelevant (that is, the next higher LOD
visibility range reaches the max_area, to be visible in the whole sim.)
The LOD is capped when (r/LODdivider)^2*PI=max_area, where LODdivider is [0.24 0.06 0.03] and
max_area is 102932 m^2, thus the radii are 
    float X=llSqrt(102932.0/PI);
    list radius=[X*0.24, X*0.06, X*0.03, 0.01]; 
The last one is a small mesh, where the lowest LOD has the largest effect. 
The weight matrix W with these four radii is

    1.0000    0.0000    0.0000    0.0000
    0.0625    0.9375    0.0000    0.0000
    0.0156    0.2344    0.7500    0.0000
    0.0000    0.0000    0.0000    1.0000

and the streaming cost is LI=15000/250000 * W*triangles. The inverse matrix 
inv(W) is

    1.0000    0.0000    0.0000    0.0000
   -0.0667    1.0667    0.0000    0.0000
    0.0000   -0.3333    1.3334    0.0000
    0.0000    0.0000    0.0000    1.0000

and triangles=inv(W)*LI * 250/15, which is implemented below.

*******************************************************************************/


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
    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;
}

default
{
    touch_start(integer p) {
        if (llDetectedKey(0)!=llGetOwner()) return;
        list tri=getLODtriangles();
        llOwnerSay("LOD triangles:  " + llDumpList2String(tri," "));
    }
}


Given the triangle counts, the following demo code shows how to calculate the download cost part of the land impact for any mesh radius

 
    // store the triangle count in list TRIANGLES, for example by 
    //   list TRIANGLES=getLODtriangles();
    float r=getRadius(); // or use user input
    float DLcost_hat=0; // estimate of the DLcost
    integer i;
    list W=LODweigths(r);
    string txt;
    for(i=0;i<4;i++) {
        float li = llList2Float(TRIANGLES,i)*llList2Float(W,i) * 3.0/50.0;   // = 15000/MeshTriangleBudget
        DLcost_hat += li;
        txt += "LOD " + (string)i + " : " + (string)li + "    ";
    }
  
    // get the actual value for comparison
    float DLcost=getObjectStreamingCost();

    llOwnerSay("Download cost= " + (string)DLcost + "  Calculated= " + (string)DLcost_hat + "  Error= " + (string)(DLcost-DLcost_hat )); 
    llOwnerSay(txt);

The LODweigths routine as imported from https://wiki.secondlife.com/wiki/Mesh/Mesh_Streaming_Cost

 
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;    
}