Difference between revisions of "Mesh/Download Weight"

From Second Life Wiki
Jump to navigation Jump to search
m (added intra-wiki links and applied cpp highlighting)
Line 1: Line 1:
{{Navbox/Mesh|tech}}
{{Navbox/Mesh|tech}}
== Motivation ==
== Motivation ==
Previously used methods of LOD enforcement and mesh cost have proved ineffective and difficult to adhere to.  Proposed here is an algorithm for determining cost of a mesh asset (in terms of prim parcel cost) that correlates strongly to the actual load of streaming and displaying a mesh in a general way, without making assumptions about triangle/vertex limits and ratios between levels of detail.  The artist need not adhere to any arbitrary restrictions with respect to what LODs must be supplied and what the parameters of those LODs are, but providing proper LODs will greatly reduce the cost of an object in terms of parcel limits, effectively allowing regions with efficient content to carry more content, while regions with inefficient content can carry less.  This should allow greater control from Linden Lab in terms of acceptable rendering and streaming budgets while also giving artists complete control over how they build.
Previously used methods of [http://wiki.secondlife.com/wiki/Mesh_and_LOD#Level_Of_Detail LOD] enforcement and mesh cost have proved ineffective and difficult to adhere to.  Proposed here is an algorithm for determining cost of a mesh asset (in terms of prim parcel cost) that correlates strongly to the actual load of streaming and displaying a mesh in a general way, without making assumptions about triangle/vertex limits and ratios between levels of detail.  The artist need not adhere to any arbitrary restrictions with respect to what LODs must be supplied and what the parameters of those [http://wiki.secondlife.com/wiki/Mesh_and_LOD#Level_Of_Detail LODs] are, but providing proper [http://wiki.secondlife.com/wiki/Mesh_and_LOD#Level_Of_Detail LODs] will greatly reduce the cost of an object in terms of parcel limits, effectively allowing regions with efficient content to carry more content, while regions with inefficient content can carry less.  This should allow greater control from Linden Lab in terms of acceptable rendering and streaming budgets while also giving artists complete control over how they build.


== Concept ==
== Concept ==
The streaming and rendering cost of a mesh is directly related to the number of bytes in a mesh asset LOD slot, and the likelihood that a given LOD will be downloaded and displayed can be computed based on the size of the object.  Imagine a set of 3 concentric circles centered on an object where each circle represents the transition boundary between LODs.  The streaming/rendering cost of that object can be determined by examining the size of those circles vs the number of bytes in the relevant LODs.  Uploading a high LOD only will result in the load of the high lod being applied to the entire 256m, while uploading appropriate LODs will result in the lion's share of 256m being applied to the lowest LOD
The streaming and rendering cost of a mesh is directly related to the number of bytes in a mesh asset [http://wiki.secondlife.com/wiki/Mesh_and_LOD#Level_Of_Detail LOD] slot, and the likelihood that a given [http://wiki.secondlife.com/wiki/Mesh_and_LOD#Level_Of_Detail LOD] will be downloaded and displayed can be computed based on the size of the object.  Imagine a set of 3 concentric circles centered on an object where each circle represents the transition boundary between [http://wiki.secondlife.com/wiki/Mesh_and_LOD#Level_Of_Detail LODs].  The streaming/rendering cost of that object can be determined by examining the size of those circles vs the number of bytes in the relevant [http://wiki.secondlife.com/wiki/Mesh_and_LOD#Level_Of_Detail LODs].  Uploading a high [http://wiki.secondlife.com/wiki/Mesh_and_LOD#Level_Of_Detail LOD] only will result in the load of the high lod being applied to the entire 256m, while uploading appropriate [http://wiki.secondlife.com/wiki/Mesh_and_LOD#Level_Of_Detail LODs] will result in the lion's share of 256m being applied to the lowest [http://wiki.secondlife.com/wiki/Mesh_and_LOD#Level_Of_Detail LOD].


== Equation ==
== Equation ==
# Compute the distance at which each LOD is displayed
# Compute the distance at which each [http://wiki.secondlife.com/wiki/Mesh_and_LOD#Level_Of_Detail LOD] is displayed
# Compute the area in which each LOD is relevant
# Compute the area in which each [http://wiki.secondlife.com/wiki/Mesh_and_LOD#Level_Of_Detail LOD] is relevant
# Adjust for missiing LODs
# Adjust for missiing [http://wiki.secondlife.com/wiki/Mesh_and_LOD#Level_Of_Detail LODs]
# Scale relative weights of each LOD based on what percentage of the region each LoD covers.
# Scale relative weights of each [http://wiki.secondlife.com/wiki/Mesh_and_LOD#Level_Of_Detail LOD] based on what percentage of the region each [http://wiki.secondlife.com/wiki/Mesh_and_LOD#Level_Of_Detail LOD] covers.
# Compute cost based on relevant range and bytes in LOD  
# Compute cost based on relevant range and bytes in [http://wiki.secondlife.com/wiki/Mesh_and_LOD#Level_Of_Detail LOD]


=== LOD Transition Distances ===
=== [http://wiki.secondlife.com/wiki/Mesh_and_LOD#Level_Of_Detail LOD] Transition Distances ===
To compute the distance at which each LOD is displayed, take the radius of the object's bounding box (R) and divide by the LOD ratios used in the viewer:
To compute the distance at which each [http://wiki.secondlife.com/wiki/Mesh_and_LOD#Level_Of_Detail LOD] is displayed, take the radius of the object's bounding box (R) and divide by the [http://wiki.secondlife.com/wiki/Mesh_and_LOD#Level_Of_Detail LOD] ratios used in the viewer:


* Dlowest = distance at which lowest LOD begins to be displayed
* Dlowest = distance at which lowest [http://wiki.secondlife.com/wiki/Mesh_and_LOD#Level_Of_Detail LOD] begins to be displayed
* Dlow = distance at which low LOD begins to be displayed
* Dlow = distance at which low [http://wiki.secondlife.com/wiki/Mesh_and_LOD#Level_Of_Detail LOD] begins to be displayed
* Dmid = distance at which mid LOD begins to be displayed
* Dmid = distance at which mid [http://wiki.secondlife.com/wiki/Mesh_and_LOD#Level_Of_Detail LOD] begins to be displayed
* Dhigh = distance at which high LOD begins to be displayed
* Dhigh = distance at which high [http://wiki.secondlife.com/wiki/Mesh_and_LOD#Level_Of_Detail LOD] begins to be displayed


* Dlowest = R / 0.03
* Dlowest = R / 0.03
Line 36: Line 36:
* high_area = high_circle
* high_area = high_circle


=== Relevant LOD Ranges ===
=== Relevant [http://wiki.secondlife.com/wiki/Mesh_and_LOD#Level_Of_Detail LOD] Ranges ===
The relevant range of each LOD is the distance between which that LOD becomes visible and the distance at which that LOD is no longer displayed, clamped to a 256m circle.
The relevant range of each [http://wiki.secondlife.com/wiki/Mesh_and_LOD#Level_Of_Detail LOD] is the distance between which that [http://wiki.secondlife.com/wiki/Mesh_and_LOD#Level_Of_Detail LOD] becomes visible and the distance at which that [http://wiki.secondlife.com/wiki/Mesh_and_LOD#Level_Of_Detail LOD] is no longer displayed, clamped to a 256m circle.


<pre>
<pre>
Line 48: Line 48:
</pre>
</pre>


==== Adjusting for missing LODs ====
==== Adjusting for missing [http://wiki.secondlife.com/wiki/Mesh_and_LOD#Level_Of_Detail LODs] ====
if any lod is missing, substitute bytes in next highest available LOD. That is, if BYTES_IN_MID is zero, substitute BYTES_IN_HIGH for BYTES_IN_MID, and so on
if any lod is missing, substitute bytes in next highest available [http://wiki.secondlife.com/wiki/Mesh_and_LOD#Level_Of_Detail LOD]. That is, if <code>BYTES_IN_MID</code> is zero, substitute <code>BYTES_IN_HIGH</code> for <code>BYTES_IN_MID</code>, and so on.


==== Computing Cost ====
==== Computing Cost ====
At the simplest level, streaming cost is computed as:
At the simplest level, streaming cost is computed as:


Streaming Cost =  
<pre>
( (lowest_area / total_area) * bytes_in_lowest + <br>
    Streaming Cost =
(low_area / total_area) * bytes_in_low + <br>
        (   (lowest_area / total_area) * bytes_in_lowest
(mid_area / total_area) * bytes_in_mid + <br>
          + (low_area   / total_area) * bytes_in_low
(high_area / total_area) * bytes_in_high ) * cost_scalar
          + (mid_area   / total_area) * bytes_in_mid
          + (high_area   / total_area) * bytes_in_high   ) * cost_scalar
</pre>


In the details of the implementation, the scalar is based on a target triangle budget, and efforts are made to convert bytes_in_foo to an estimated triangle count.
In the details of the implementation, the scalar is based on a target triangle budget, and efforts are made to convert bytes_in_foo to an estimated triangle count.


=== Implementation ===
=== Implementation ===
<pre>
<cpp>
// Get the streaming cost for the given mesh
// Get the streaming cost for the given mesh
// header -- header of mesh as described in mesh asset format
// header -- header of mesh as described in mesh asset format
Line 72: Line 74:
F32 LLMeshRepository::getStreamingCost(LLSD& header, F32 radius, S32* bytes, S32* bytes_visible, S32 lod)
F32 LLMeshRepository::getStreamingCost(LLSD& header, F32 radius, S32* bytes, S32* bytes_visible, S32 lod)
{
{
F32 max_distance = 512.f;
    F32 max_distance = 512.f;
 
    F32 dlowest = llmin(radius/0.03f, max_distance);
    F32 dlow    = llmin(radius/0.06f, max_distance);
    F32 dmid    = llmin(radius/0.24f, max_distance);


F32 dlowest = llmin(radius/0.03f, max_distance);
F32 dlow = llmin(radius/0.06f, max_distance);
F32 dmid = llmin(radius/0.24f, max_distance);
F32 METADATA_DISCOUNT = (F32) gSavedSettings.getU32("MeshMetaDataDiscount");  //discount 128 bytes to cover the cost of LLSD tags and compression domain overhead
F32 MINIMUM_SIZE = (F32) gSavedSettings.getU32("MeshMinimumByteSize"); //make sure nothing is "free"


F32 bytes_per_triangle = (F32) gSavedSettings.getU32("MeshBytesPerTriangle");
//  discount 128 bytes to cover the cost of LLSD tags and compression domain overhead
    F32 METADATA_DISCOUNT = (F32) gSavedSettings.getU32("MeshMetaDataDiscount");
//  make sure nothing is "free"
    F32 MINIMUM_SIZE = (F32) gSavedSettings.getU32("MeshMinimumByteSize");


S32 bytes_lowest = header["lowest_lod"]["size"].asInteger();
    F32 bytes_per_triangle = (F32) gSavedSettings.getU32("MeshBytesPerTriangle");
S32 bytes_low = header["low_lod"]["size"].asInteger();
S32 bytes_mid = header["medium_lod"]["size"].asInteger();
S32 bytes_high = header["high_lod"]["size"].asInteger();


if (bytes_high == 0)
    S32 bytes_lowest = header["lowest_lod"]["size"].asInteger();
{
    S32 bytes_low    = header["low_lod"]["size"].asInteger();
return 0.f;
    S32 bytes_mid    = header["medium_lod"]["size"].asInteger();
}
    S32 bytes_high  = header["high_lod"]["size"].asInteger();


if (bytes_mid == 0)
    if (bytes_high == 0)
{
    {
bytes_mid = bytes_high;
        return 0.f;
}
    }


if (bytes_low == 0)
    if (bytes_mid == 0)
{
    {
bytes_low = bytes_mid;
        bytes_mid = bytes_high;
}
    }


if (bytes_lowest == 0)
    if (bytes_low == 0)
{
    {
bytes_lowest = bytes_low;
        bytes_low = bytes_mid;
}
    }


F32 triangles_lowest = llmax((F32) bytes_lowest-METADATA_DISCOUNT, MINIMUM_SIZE)/bytes_per_triangle;
    if (bytes_lowest == 0)
F32 triangles_low = llmax((F32) bytes_low-METADATA_DISCOUNT, MINIMUM_SIZE)/bytes_per_triangle;
    {
F32 triangles_mid = llmax((F32) bytes_mid-METADATA_DISCOUNT, MINIMUM_SIZE)/bytes_per_triangle;
        bytes_lowest = bytes_low;
F32 triangles_high = llmax((F32) bytes_high-METADATA_DISCOUNT, MINIMUM_SIZE)/bytes_per_triangle;
    }


if (bytes)
    F32 triangles_lowest = llmax((F32) bytes_lowest-METADATA_DISCOUNT, MINIMUM_SIZE)/bytes_per_triangle;
{
    F32 triangles_low    = llmax((F32) bytes_low-METADATA_DISCOUNT, MINIMUM_SIZE)/bytes_per_triangle;
*bytes = 0;
    F32 triangles_mid    = llmax((F32) bytes_mid-METADATA_DISCOUNT, MINIMUM_SIZE)/bytes_per_triangle;
*bytes += header["lowest_lod"]["size"].asInteger();
    F32 triangles_high  = llmax((F32) bytes_high-METADATA_DISCOUNT, MINIMUM_SIZE)/bytes_per_triangle;
*bytes += header["low_lod"]["size"].asInteger();
*bytes += header["medium_lod"]["size"].asInteger();
*bytes += header["high_lod"]["size"].asInteger();
}


if (bytes_visible)
    if (bytes)
{
    {
lod = LLMeshRepository::getActualMeshLOD(header, lod);
        *bytes = 0;
if (lod >= 0 && lod <= 3)
        *bytes += header["lowest_lod"]["size"].asInteger();
{
        *bytes += header["low_lod"]["size"].asInteger();
*bytes_visible = header[header_lod[lod]]["size"].asInteger();
        *bytes += header["medium_lod"]["size"].asInteger();
}
        *bytes += header["high_lod"]["size"].asInteger();
}
    }


F32 max_area = 102932.f; //area of circle that encompasses region
    if (bytes_visible)
F32 min_area = 1.f;
    {
        lod = LLMeshRepository::getActualMeshLOD(header, lod);
        if (lod >= 0 && lod <= 3)
        {
            *bytes_visible = header[header_lod[lod]]["size"].asInteger();
        }
    }


F32 high_area = llmin(F_PI*dmid*dmid, max_area);
    F32 max_area = 102932.f; //area of circle that encompasses region
F32 mid_area = llmin(F_PI*dlow*dlow, max_area);
    F32 min_area = 1.f;
F32 low_area = llmin(F_PI*dlowest*dlowest, max_area);
F32 lowest_area = max_area;


lowest_area -= low_area;
    F32 high_area  = llmin(F_PI*dmid*dmid, max_area);
low_area -= mid_area;
    F32 mid_area    = llmin(F_PI*dlow*dlow, max_area);
mid_area -= high_area;
    F32 low_area   = llmin(F_PI*dlowest*dlowest, max_area);
    F32 lowest_area = max_area;


high_area = llclamp(high_area, min_area, max_area);
    lowest_area -= low_area;
mid_area = llclamp(mid_area, min_area, max_area);
    low_area    -= mid_area;
low_area = llclamp(low_area, min_area, max_area);
    mid_area    -= high_area;
lowest_area = llclamp(lowest_area, min_area, max_area);


F32 total_area = high_area + mid_area + low_area + lowest_area;
    high_area  = llclamp(high_area, min_area, max_area);
high_area /= total_area;
    mid_area    = llclamp(mid_area, min_area, max_area);
mid_area /= total_area;
    low_area   = llclamp(low_area, min_area, max_area);
low_area /= total_area;
    lowest_area = llclamp(lowest_area, min_area, max_area);
lowest_area /= total_area;


F32 weighted_avg = triangles_high*high_area +
    F32 total_area = high_area + mid_area + low_area + lowest_area;
  triangles_mid*mid_area +
    high_area  /= total_area;
  triangles_low*low_area +
    mid_area   /= total_area;
  triangles_lowest*lowest_area;
    low_area   /= total_area;
    lowest_area /= total_area;


return weighted_avg/gSavedSettings.getU32("MeshTriangleBudget")*15000.f;
    F32 weighted_avg = triangles_high*high_area +
                      triangles_mid*mid_area +
                      triangles_low*low_area +
                      triangles_lowest*lowest_area;
 
    return weighted_avg/gSavedSettings.getU32("MeshTriangleBudget")*15000.f;
}
}
</pre>
</cpp>


== Issues ==
== Issues ==
* Providing identical models for every LOD results in a cost identical to providing a single LOD, but results in 4x the bandwidth usage.
* Providing identical models for every [http://wiki.secondlife.com/wiki/Mesh_and_LOD#Level_Of_Detail LOD] results in a cost identical to providing a single [http://wiki.secondlife.com/wiki/Mesh_and_LOD#Level_Of_Detail LOD], but results in 4x the bandwidth usage.
* Changing the scale of an object changes its cost, which can be confusing.
* Changing the scale of an object changes its cost, which can be confusing.
* For viewers with a view distance greater than 256m, the clamping to 256m is unrealistic.
* For viewers with a view distance greater than 256m, the clamping to 256m is unrealistic.
* Some validating of LODs is still necessary.
* Some validating of [http://wiki.secondlife.com/wiki/Mesh_and_LOD#Level_Of_Detail LODs] is still necessary.
** The highest LOD must be specified
** The highest [http://wiki.secondlife.com/wiki/Mesh_and_LOD#Level_Of_Detail LOD] must be specified.
** Each LOD must have the same number of faces as the highest LOD
** Each [http://wiki.secondlife.com/wiki/Mesh_and_LOD#Level_Of_Detail LOD] must have the same number of faces as the highest [http://wiki.secondlife.com/wiki/Mesh_and_LOD#Level_Of_Detail LOD].


== Related Articles ==
== Related Articles ==
* [[Mesh Accounting Test]]
* [[Mesh Accounting Test]]
* [[Mesh/Mesh physics|Mesh physics]]
* [[Mesh/Mesh physics|Mesh physics]]

Revision as of 10:46, 2 January 2014

Motivation

Previously used methods of LOD enforcement and mesh cost have proved ineffective and difficult to adhere to. Proposed here is an algorithm for determining cost of a mesh asset (in terms of prim parcel cost) that correlates strongly to the actual load of streaming and displaying a mesh in a general way, without making assumptions about triangle/vertex limits and ratios between levels of detail. The artist need not adhere to any arbitrary restrictions with respect to what LODs must be supplied and what the parameters of those LODs are, but providing proper LODs will greatly reduce the cost of an object in terms of parcel limits, effectively allowing regions with efficient content to carry more content, while regions with inefficient content can carry less. This should allow greater control from Linden Lab in terms of acceptable rendering and streaming budgets while also giving artists complete control over how they build.

Concept

The streaming and rendering cost of a mesh is directly related to the number of bytes in a mesh asset LOD slot, and the likelihood that a given LOD will be downloaded and displayed can be computed based on the size of the object. Imagine a set of 3 concentric circles centered on an object where each circle represents the transition boundary between LODs. The streaming/rendering cost of that object can be determined by examining the size of those circles vs the number of bytes in the relevant LODs. Uploading a high LOD only will result in the load of the high lod being applied to the entire 256m, while uploading appropriate LODs will result in the lion's share of 256m being applied to the lowest LOD.

Equation

  1. Compute the distance at which each LOD is displayed
  2. Compute the area in which each LOD is relevant
  3. Adjust for missiing LODs
  4. Scale relative weights of each LOD based on what percentage of the region each LOD covers.
  5. Compute cost based on relevant range and bytes in LOD

LOD Transition Distances

To compute the distance at which each LOD is displayed, take the radius of the object's bounding box (R) and divide by the LOD ratios used in the viewer:

  • Dlowest = distance at which lowest LOD begins to be displayed
  • Dlow = distance at which low LOD begins to be displayed
  • Dmid = distance at which mid LOD begins to be displayed
  • Dhigh = distance at which high LOD begins to be displayed
  • Dlowest = R / 0.03
  • Dlow = R / 0.06
  • Dmid = R / 0.24
  • Dhigh = 0.0
  • lowest_circle = max area
  • low_circle = PI * Dlowest ^2
  • mid_circle = PI * Dlow ^2
  • high_circle = PI * Dmid^2
  • lowest_area = lowest_circle - low_circle
  • low_area = low_circle - mid_circle
  • mid_area = mid_circle - high_circle
  • high_area = high_circle

Relevant LOD Ranges

The relevant range of each LOD is the distance between which that LOD becomes visible and the distance at which that LOD is no longer displayed, clamped to a 256m circle.

Example:
For an object with a bounding box R of 10m,
The Dhigh LOD will be displayed while the camera is within 0m to 42m from the object's center.
The Dmid LOD will be displayed while the camera is within 42m to 166.67m (10/0.24) from the object's center.
The Dlow LOD will be displayed while the camera is within 166.67m to 333.3m from the object's center.
The Dlowest LOD will be displayed while the camera is further than 333.37m from the object's center.

Adjusting for missing LODs

if any lod is missing, substitute bytes in next highest available LOD. That is, if BYTES_IN_MID is zero, substitute BYTES_IN_HIGH for BYTES_IN_MID, and so on.

Computing Cost

At the simplest level, streaming cost is computed as:

    Streaming Cost =
        (   (lowest_area / total_area) * bytes_in_lowest
          + (low_area    / total_area) * bytes_in_low
          + (mid_area    / total_area) * bytes_in_mid
          + (high_area   / total_area) * bytes_in_high   ) * cost_scalar

In the details of the implementation, the scalar is based on a target triangle budget, and efforts are made to convert bytes_in_foo to an estimated triangle count.

Implementation

<cpp> // Get the streaming cost for the given mesh // header -- header of mesh as described in mesh asset format // radius -- magnitude of the mesh object scale, divided by two // bytes -- if not NULL, gets number of bytes in this mesh (for debugging) // bytes_visible -- if not NULL, gets number of bytes in specified lod (for debugging) // lod -- currently visible lod (for debugging) F32 LLMeshRepository::getStreamingCost(LLSD& header, F32 radius, S32* bytes, S32* bytes_visible, S32 lod) {

   F32 max_distance = 512.f;
   F32 dlowest = llmin(radius/0.03f, max_distance);
   F32 dlow    = llmin(radius/0.06f, max_distance);
   F32 dmid    = llmin(radius/0.24f, max_distance);


// discount 128 bytes to cover the cost of LLSD tags and compression domain overhead

   F32 METADATA_DISCOUNT = (F32) gSavedSettings.getU32("MeshMetaDataDiscount");

// make sure nothing is "free"

   F32 MINIMUM_SIZE = (F32) gSavedSettings.getU32("MeshMinimumByteSize");
   F32 bytes_per_triangle = (F32) gSavedSettings.getU32("MeshBytesPerTriangle");
   S32 bytes_lowest = header["lowest_lod"]["size"].asInteger();
   S32 bytes_low    = header["low_lod"]["size"].asInteger();
   S32 bytes_mid    = header["medium_lod"]["size"].asInteger();
   S32 bytes_high   = header["high_lod"]["size"].asInteger();
   if (bytes_high == 0)
   {
       return 0.f;
   }
   if (bytes_mid == 0)
   {
       bytes_mid = bytes_high;
   }
   if (bytes_low == 0)
   {
       bytes_low = bytes_mid;
   }
   if (bytes_lowest == 0)
   {
       bytes_lowest = bytes_low;
   }
   F32 triangles_lowest = llmax((F32) bytes_lowest-METADATA_DISCOUNT, MINIMUM_SIZE)/bytes_per_triangle;
   F32 triangles_low    = llmax((F32) bytes_low-METADATA_DISCOUNT, MINIMUM_SIZE)/bytes_per_triangle;
   F32 triangles_mid    = llmax((F32) bytes_mid-METADATA_DISCOUNT, MINIMUM_SIZE)/bytes_per_triangle;
   F32 triangles_high   = llmax((F32) bytes_high-METADATA_DISCOUNT, MINIMUM_SIZE)/bytes_per_triangle;
   if (bytes)
   {
       *bytes = 0;
       *bytes += header["lowest_lod"]["size"].asInteger();
       *bytes += header["low_lod"]["size"].asInteger();
       *bytes += header["medium_lod"]["size"].asInteger();
       *bytes += header["high_lod"]["size"].asInteger();
   }
   if (bytes_visible)
   {
       lod = LLMeshRepository::getActualMeshLOD(header, lod);
       if (lod >= 0 && lod <= 3)
       {
           *bytes_visible = header[header_lod[lod]]["size"].asInteger();
       }
   }
   F32 max_area = 102932.f; //area of circle that encompasses region
   F32 min_area = 1.f;
   F32 high_area   = llmin(F_PI*dmid*dmid, max_area);
   F32 mid_area    = llmin(F_PI*dlow*dlow, max_area);
   F32 low_area    = llmin(F_PI*dlowest*dlowest, max_area);
   F32 lowest_area = max_area;
   lowest_area -= low_area;
   low_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);
   F32 total_area = high_area + mid_area + low_area + lowest_area;
   high_area   /= total_area;
   mid_area    /= total_area;
   low_area    /= total_area;
   lowest_area /= total_area;
   F32 weighted_avg = triangles_high*high_area +
                      triangles_mid*mid_area +
                      triangles_low*low_area +
                     triangles_lowest*lowest_area;
   return weighted_avg/gSavedSettings.getU32("MeshTriangleBudget")*15000.f;

} </cpp>

Issues

  • Providing identical models for every LOD results in a cost identical to providing a single LOD, but results in 4x the bandwidth usage.
  • Changing the scale of an object changes its cost, which can be confusing.
  • For viewers with a view distance greater than 256m, the clamping to 256m is unrealistic.
  • Some validating of LODs is still necessary.
    • The highest LOD must be specified.
    • Each LOD must have the same number of faces as the highest LOD.

Related Articles