User:LindaB Helendale/getMeshLODsize
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;
}