secondlife/viewer-issues#43: Proof-of-concept PBR terrain normal gen feature flag. Final implementation would likely be very different.
parent
00c65b6270
commit
118996225b
|
|
@ -10932,6 +10932,17 @@
|
|||
<key>Value</key>
|
||||
<real>8.0</real>
|
||||
</map>
|
||||
<key>RenderTerrainPBRNormalsEnabled</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
<string>EXPERIMENTAL: Change normal gen for PBR Terrain.</string>
|
||||
<key>Persist</key>
|
||||
<integer>1</integer>
|
||||
<key>Type</key>
|
||||
<string>Boolean</string>
|
||||
<key>Value</key>
|
||||
<integer>0</integer>
|
||||
</map>
|
||||
<key>RenderTrackerBeacon</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
|
|
|
|||
|
|
@ -643,17 +643,18 @@ void LLSurface::updatePatchVisibilities(LLAgent &agent)
|
|||
}
|
||||
}
|
||||
|
||||
BOOL LLSurface::idleUpdate(F32 max_update_time)
|
||||
template<bool PBR>
|
||||
bool LLSurface::idleUpdate(F32 max_update_time)
|
||||
{
|
||||
if (!gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_TERRAIN))
|
||||
{
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Perform idle time update of non-critical stuff.
|
||||
// In this case, texture and normal updates.
|
||||
LLTimer update_timer;
|
||||
BOOL did_update = FALSE;
|
||||
bool did_update = false;
|
||||
|
||||
// If the Z height data has changed, we need to rebuild our
|
||||
// property line vertex arrays.
|
||||
|
|
@ -669,13 +670,13 @@ BOOL LLSurface::idleUpdate(F32 max_update_time)
|
|||
{
|
||||
std::set<LLSurfacePatch *>::iterator curiter = iter++;
|
||||
LLSurfacePatch *patchp = *curiter;
|
||||
patchp->updateNormals();
|
||||
patchp->updateNormals<PBR>();
|
||||
patchp->updateVerticalStats();
|
||||
if (max_update_time == 0.f || update_timer.getElapsedTimeF32() < max_update_time)
|
||||
{
|
||||
if (patchp->updateTexture())
|
||||
{
|
||||
did_update = TRUE;
|
||||
did_update = true;
|
||||
patchp->clearDirty();
|
||||
mDirtyPatchList.erase(curiter);
|
||||
}
|
||||
|
|
@ -691,6 +692,9 @@ BOOL LLSurface::idleUpdate(F32 max_update_time)
|
|||
return did_update;
|
||||
}
|
||||
|
||||
template bool LLSurface::idleUpdate</*PBR=*/false>(F32 max_update_time);
|
||||
template bool LLSurface::idleUpdate</*PBR=*/true>(F32 max_update_time);
|
||||
|
||||
void LLSurface::decompressDCTPatch(LLBitPack &bitpack, LLGroupHeader *gopp, BOOL b_large_patch)
|
||||
{
|
||||
|
||||
|
|
|
|||
|
|
@ -112,7 +112,8 @@ public:
|
|||
LLSurfacePatch *resolvePatchGlobal(const LLVector3d &position_global) const;
|
||||
|
||||
// Update methods (called during idle, normally)
|
||||
BOOL idleUpdate(F32 max_update_time);
|
||||
template<bool PBR>
|
||||
bool idleUpdate(F32 max_update_time);
|
||||
|
||||
BOOL containsPosition(const LLVector3 &position);
|
||||
|
||||
|
|
@ -224,6 +225,9 @@ private:
|
|||
static S32 sTextureSize; // Size of the surface texture
|
||||
};
|
||||
|
||||
extern template bool LLSurface::idleUpdate</*PBR=*/false>(F32 max_update_time);
|
||||
extern template bool LLSurface::idleUpdate</*PBR=*/true>(F32 max_update_time);
|
||||
|
||||
|
||||
|
||||
// . __.
|
||||
|
|
|
|||
|
|
@ -243,7 +243,8 @@ void LLSurfacePatch::eval(const U32 x, const U32 y, const U32 stride, LLVector3
|
|||
}
|
||||
|
||||
|
||||
void LLSurfacePatch::calcNormal(const U32 x, const U32 y, const U32 stride)
|
||||
template<>
|
||||
void LLSurfacePatch::calcNormal</*PBR=*/false>(const U32 x, const U32 y, const U32 stride)
|
||||
{
|
||||
U32 patch_width = mSurfacep->mPVArray.mPatchWidth;
|
||||
U32 surface_stride = mSurfacep->getGridsPerEdge();
|
||||
|
|
@ -356,6 +357,166 @@ void LLSurfacePatch::calcNormal(const U32 x, const U32 y, const U32 stride)
|
|||
*(mDataNorm + surface_stride * y + x) = normal;
|
||||
}
|
||||
|
||||
template<>
|
||||
void LLSurfacePatch::calcNormal</*PBR=*/true>(const U32 x, const U32 y, const U32 stride)
|
||||
{
|
||||
llassert(mDataNorm);
|
||||
constexpr U32 index = 0;
|
||||
|
||||
const U32 surface_stride = mSurfacep->getGridsPerEdge();
|
||||
LLVector3& normal_out = *(mDataNorm + surface_stride * y + x);
|
||||
calcNormalFlat(normal_out, x, y, index);
|
||||
}
|
||||
|
||||
// Calculate the flat normal of a triangle whose least coordinate is specified by the given x,y values.
|
||||
// If index = 0, calculate the normal of the first triangle, otherwise calculate the normal of the second.
|
||||
void LLSurfacePatch::calcNormalFlat(LLVector3& normal_out, const U32 x, const U32 y, const U32 index)
|
||||
{
|
||||
llassert(index == 0 || index == 1);
|
||||
|
||||
U32 patch_width = mSurfacep->mPVArray.mPatchWidth;
|
||||
U32 surface_stride = mSurfacep->getGridsPerEdge();
|
||||
|
||||
// Vertex stride is always 1 because we want the flat surface of the current triangle face
|
||||
constexpr U32 stride = 1;
|
||||
|
||||
const F32 mpg = mSurfacep->getMetersPerGrid() * stride;
|
||||
|
||||
S32 poffsets[2][2][2];
|
||||
poffsets[0][0][0] = x;
|
||||
poffsets[0][0][1] = y;
|
||||
|
||||
poffsets[0][1][0] = x;
|
||||
poffsets[0][1][1] = y + stride;
|
||||
|
||||
poffsets[1][0][0] = x + stride;
|
||||
poffsets[1][0][1] = y;
|
||||
|
||||
poffsets[1][1][0] = x + stride;
|
||||
poffsets[1][1][1] = y + stride;
|
||||
|
||||
const LLSurfacePatch *ppatches[2][2];
|
||||
|
||||
// LLVector3 p1, p2, p3, p4;
|
||||
|
||||
ppatches[0][0] = this;
|
||||
ppatches[0][1] = this;
|
||||
ppatches[1][0] = this;
|
||||
ppatches[1][1] = this;
|
||||
|
||||
U32 i, j;
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
for (j = 0; j < 2; j++)
|
||||
{
|
||||
if (poffsets[i][j][0] < 0)
|
||||
{
|
||||
if (!ppatches[i][j]->getNeighborPatch(WEST))
|
||||
{
|
||||
poffsets[i][j][0] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
poffsets[i][j][0] += patch_width;
|
||||
ppatches[i][j] = ppatches[i][j]->getNeighborPatch(WEST);
|
||||
}
|
||||
}
|
||||
if (poffsets[i][j][1] < 0)
|
||||
{
|
||||
if (!ppatches[i][j]->getNeighborPatch(SOUTH))
|
||||
{
|
||||
poffsets[i][j][1] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
poffsets[i][j][1] += patch_width;
|
||||
ppatches[i][j] = ppatches[i][j]->getNeighborPatch(SOUTH);
|
||||
}
|
||||
}
|
||||
if (poffsets[i][j][0] >= (S32)patch_width)
|
||||
{
|
||||
if (!ppatches[i][j]->getNeighborPatch(EAST))
|
||||
{
|
||||
poffsets[i][j][0] = patch_width - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
poffsets[i][j][0] -= patch_width;
|
||||
ppatches[i][j] = ppatches[i][j]->getNeighborPatch(EAST);
|
||||
}
|
||||
}
|
||||
if (poffsets[i][j][1] >= (S32)patch_width)
|
||||
{
|
||||
if (!ppatches[i][j]->getNeighborPatch(NORTH))
|
||||
{
|
||||
poffsets[i][j][1] = patch_width - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
poffsets[i][j][1] -= patch_width;
|
||||
ppatches[i][j] = ppatches[i][j]->getNeighborPatch(NORTH);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LLVector3 p00(-mpg,-mpg,
|
||||
*(ppatches[0][0]->mDataZ
|
||||
+ poffsets[0][0][0]
|
||||
+ poffsets[0][0][1]*surface_stride));
|
||||
LLVector3 p01(-mpg,+mpg,
|
||||
*(ppatches[0][1]->mDataZ
|
||||
+ poffsets[0][1][0]
|
||||
+ poffsets[0][1][1]*surface_stride));
|
||||
LLVector3 p10(+mpg,-mpg,
|
||||
*(ppatches[1][0]->mDataZ
|
||||
+ poffsets[1][0][0]
|
||||
+ poffsets[1][0][1]*surface_stride));
|
||||
LLVector3 p11(+mpg,+mpg,
|
||||
*(ppatches[1][1]->mDataZ
|
||||
+ poffsets[1][1][0]
|
||||
+ poffsets[1][1][1]*surface_stride));
|
||||
|
||||
// Triangle index / coordinate convention
|
||||
// for a single surface patch
|
||||
//
|
||||
// p01 p11
|
||||
//
|
||||
// ^ ._____.
|
||||
// | |\ |
|
||||
// | | \ 1 |
|
||||
// | | \ |
|
||||
// | 0 \ |
|
||||
// y |____\|
|
||||
//
|
||||
// p00 x ---> p10
|
||||
//
|
||||
// (z up / out of the screen due to right-handed coordinate system)
|
||||
|
||||
LLVector3 normal;
|
||||
if (index == 0)
|
||||
{
|
||||
LLVector3 c1 = p10 - p00;
|
||||
LLVector3 c2 = p01 - p00;
|
||||
|
||||
normal = c1;
|
||||
normal %= c2;
|
||||
normal.normVec();
|
||||
}
|
||||
else // index == 1
|
||||
{
|
||||
LLVector3 c1 = p11 - p01;
|
||||
LLVector3 c2 = p11 - p10;
|
||||
|
||||
normal = c1;
|
||||
normal %= c2;
|
||||
normal.normVec();
|
||||
}
|
||||
|
||||
llassert(&normal_out);
|
||||
normal_out = normal;
|
||||
}
|
||||
|
||||
const LLVector3 &LLSurfacePatch::getNormal(const U32 x, const U32 y) const
|
||||
{
|
||||
U32 surface_stride = mSurfacep->getGridsPerEdge();
|
||||
|
|
@ -453,6 +614,7 @@ void LLSurfacePatch::updateVerticalStats()
|
|||
}
|
||||
|
||||
|
||||
template<bool PBR>
|
||||
void LLSurfacePatch::updateNormals()
|
||||
{
|
||||
if (mSurfacep->mType == 'w')
|
||||
|
|
@ -470,9 +632,9 @@ void LLSurfacePatch::updateNormals()
|
|||
{
|
||||
for (j = 0; j <= grids_per_patch_edge; j++)
|
||||
{
|
||||
calcNormal(grids_per_patch_edge, j, 2);
|
||||
calcNormal(grids_per_patch_edge - 1, j, 2);
|
||||
calcNormal(grids_per_patch_edge - 2, j, 2);
|
||||
calcNormal<PBR>(grids_per_patch_edge, j, 2);
|
||||
calcNormal<PBR>(grids_per_patch_edge - 1, j, 2);
|
||||
calcNormal<PBR>(grids_per_patch_edge - 2, j, 2);
|
||||
}
|
||||
|
||||
dirty_patch = TRUE;
|
||||
|
|
@ -483,9 +645,9 @@ void LLSurfacePatch::updateNormals()
|
|||
{
|
||||
for (i = 0; i <= grids_per_patch_edge; i++)
|
||||
{
|
||||
calcNormal(i, grids_per_patch_edge, 2);
|
||||
calcNormal(i, grids_per_patch_edge - 1, 2);
|
||||
calcNormal(i, grids_per_patch_edge - 2, 2);
|
||||
calcNormal<PBR>(i, grids_per_patch_edge, 2);
|
||||
calcNormal<PBR>(i, grids_per_patch_edge - 1, 2);
|
||||
calcNormal<PBR>(i, grids_per_patch_edge - 2, 2);
|
||||
}
|
||||
|
||||
dirty_patch = TRUE;
|
||||
|
|
@ -496,8 +658,8 @@ void LLSurfacePatch::updateNormals()
|
|||
{
|
||||
for (j = 0; j < grids_per_patch_edge; j++)
|
||||
{
|
||||
calcNormal(0, j, 2);
|
||||
calcNormal(1, j, 2);
|
||||
calcNormal<PBR>(0, j, 2);
|
||||
calcNormal<PBR>(1, j, 2);
|
||||
}
|
||||
dirty_patch = TRUE;
|
||||
}
|
||||
|
|
@ -507,8 +669,8 @@ void LLSurfacePatch::updateNormals()
|
|||
{
|
||||
for (i = 0; i < grids_per_patch_edge; i++)
|
||||
{
|
||||
calcNormal(i, 0, 2);
|
||||
calcNormal(i, 1, 2);
|
||||
calcNormal<PBR>(i, 0, 2);
|
||||
calcNormal<PBR>(i, 1, 2);
|
||||
}
|
||||
dirty_patch = TRUE;
|
||||
}
|
||||
|
|
@ -584,10 +746,10 @@ void LLSurfacePatch::updateNormals()
|
|||
// We've got a northeast patch in the same surface.
|
||||
// The z and normals will be handled by that patch.
|
||||
}
|
||||
calcNormal(grids_per_patch_edge, grids_per_patch_edge, 2);
|
||||
calcNormal(grids_per_patch_edge, grids_per_patch_edge - 1, 2);
|
||||
calcNormal(grids_per_patch_edge - 1, grids_per_patch_edge, 2);
|
||||
calcNormal(grids_per_patch_edge - 1, grids_per_patch_edge - 1, 2);
|
||||
calcNormal<PBR>(grids_per_patch_edge, grids_per_patch_edge, 2);
|
||||
calcNormal<PBR>(grids_per_patch_edge, grids_per_patch_edge - 1, 2);
|
||||
calcNormal<PBR>(grids_per_patch_edge - 1, grids_per_patch_edge, 2);
|
||||
calcNormal<PBR>(grids_per_patch_edge - 1, grids_per_patch_edge - 1, 2);
|
||||
dirty_patch = TRUE;
|
||||
}
|
||||
|
||||
|
|
@ -598,7 +760,7 @@ void LLSurfacePatch::updateNormals()
|
|||
{
|
||||
for (i=2; i < grids_per_patch_edge - 2; i++)
|
||||
{
|
||||
calcNormal(i, j, 2);
|
||||
calcNormal<PBR>(i, j, 2);
|
||||
}
|
||||
}
|
||||
dirty_patch = TRUE;
|
||||
|
|
@ -615,6 +777,9 @@ void LLSurfacePatch::updateNormals()
|
|||
}
|
||||
}
|
||||
|
||||
template void LLSurfacePatch::updateNormals</*PBR=*/false>();
|
||||
template void LLSurfacePatch::updateNormals</*PBR=*/true>();
|
||||
|
||||
void LLSurfacePatch::updateEastEdge()
|
||||
{
|
||||
U32 grids_per_patch_edge = mSurfacep->getGridsPerPatchEdge();
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@ public:
|
|||
|
||||
void updateVerticalStats();
|
||||
void updateCompositionStats();
|
||||
template<bool PBR>
|
||||
void updateNormals();
|
||||
|
||||
void updateEastEdge();
|
||||
|
|
@ -102,9 +103,18 @@ public:
|
|||
LLVector3 getPointAgent(const U32 x, const U32 y) const; // get the point at the offset.
|
||||
LLVector2 getTexCoords(const U32 x, const U32 y) const;
|
||||
|
||||
// Per-vertex normals
|
||||
// *TODO: PBR=true is a test implementation solely for proof-of-concept.
|
||||
// Final implementation would likely be very different and may not even use
|
||||
// this function. If we decide to keep calcNormalFlat, remove index as it
|
||||
// is a debug parameter for testing.
|
||||
template<bool PBR>
|
||||
void calcNormal(const U32 x, const U32 y, const U32 stride);
|
||||
const LLVector3 &getNormal(const U32 x, const U32 y) const;
|
||||
|
||||
// Per-triangle normals for flat edges
|
||||
void calcNormalFlat(LLVector3& normal_out, const U32 x, const U32 y, const U32 index /* 0 or 1 */);
|
||||
|
||||
void eval(const U32 x, const U32 y, const U32 stride,
|
||||
LLVector3 *vertex, LLVector3 *normal, LLVector2 *tex0, LLVector2 *tex1);
|
||||
|
||||
|
|
@ -181,5 +191,8 @@ protected:
|
|||
LLSurface *mSurfacep; // Pointer to "parent" surface
|
||||
};
|
||||
|
||||
extern template void LLSurfacePatch::updateNormals</*PBR=*/false>();
|
||||
extern template void LLSurfacePatch::updateNormals</*PBR=*/true>();
|
||||
|
||||
|
||||
#endif // LL_LLSURFACEPATCH_H
|
||||
|
|
|
|||
|
|
@ -2122,6 +2122,20 @@ class LLAdvancedPurgeShaderCache : public view_listener_t
|
|||
}
|
||||
};
|
||||
|
||||
/////////////////////
|
||||
// REBUILD TERRAIN //
|
||||
/////////////////////
|
||||
|
||||
|
||||
class LLAdvancedRebuildTerrain : public view_listener_t
|
||||
{
|
||||
bool handleEvent(const LLSD& userdata)
|
||||
{
|
||||
gPipeline.rebuildTerrain();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////
|
||||
// EVENT Recorder //
|
||||
///////////////////
|
||||
|
|
@ -9492,6 +9506,10 @@ void initialize_menus()
|
|||
view_listener_t::addMenu(new LLAdvancedClickRenderProfile(), "Advanced.ClickRenderProfile");
|
||||
view_listener_t::addMenu(new LLAdvancedClickRenderBenchmark(), "Advanced.ClickRenderBenchmark");
|
||||
view_listener_t::addMenu(new LLAdvancedPurgeShaderCache(), "Advanced.ClearShaderCache");
|
||||
if (gSavedSettings.get<bool>("RenderTerrainPBREnabled"))
|
||||
{
|
||||
view_listener_t::addMenu(new LLAdvancedRebuildTerrain(), "Advanced.RebuildTerrain");
|
||||
}
|
||||
|
||||
#ifdef TOGGLE_HACKED_GODLIKE_VIEWER
|
||||
view_listener_t::addMenu(new LLAdvancedHandleToggleHackedGodmode(), "Advanced.HandleToggleHackedGodmode");
|
||||
|
|
|
|||
|
|
@ -1105,6 +1105,11 @@ void LLViewerRegion::dirtyHeights()
|
|||
}
|
||||
}
|
||||
|
||||
void LLViewerRegion::dirtyAllPatches()
|
||||
{
|
||||
getLand().dirtyAllPatches();
|
||||
}
|
||||
|
||||
//physically delete the cache entry
|
||||
void LLViewerRegion::killCacheEntry(LLVOCacheEntry* entry, bool for_rendering)
|
||||
{
|
||||
|
|
@ -1605,7 +1610,19 @@ void LLViewerRegion::idleUpdate(F32 max_update_time)
|
|||
|
||||
mLastUpdate = LLViewerOctreeEntryData::getCurrentFrame();
|
||||
|
||||
mImpl->mLandp->idleUpdate(max_update_time);
|
||||
static bool pbr_terrain_enabled = gSavedSettings.get<bool>("RenderTerrainPBREnabled");
|
||||
static LLCachedControl<bool> pbr_terrain_experimental_normals(gSavedSettings, "RenderTerrainPBRNormalsEnabled", FALSE);
|
||||
bool pbr_material = mImpl->mCompositionp && (mImpl->mCompositionp->getMaterialType() == LLTerrainMaterials::Type::PBR);
|
||||
bool pbr_land = pbr_material && pbr_terrain_enabled && pbr_terrain_experimental_normals;
|
||||
|
||||
if (!pbr_land)
|
||||
{
|
||||
mImpl->mLandp->idleUpdate</*PBR=*/false>(max_update_time);
|
||||
}
|
||||
else
|
||||
{
|
||||
mImpl->mLandp->idleUpdate</*PBR=*/true>(max_update_time);
|
||||
}
|
||||
|
||||
if (mParcelOverlay)
|
||||
{
|
||||
|
|
@ -1906,7 +1923,21 @@ LLViewerObject* LLViewerRegion::updateCacheEntry(U32 local_id, LLViewerObject* o
|
|||
// As above, but forcibly do the update.
|
||||
void LLViewerRegion::forceUpdate()
|
||||
{
|
||||
mImpl->mLandp->idleUpdate(0.f);
|
||||
constexpr F32 max_update_time = 0.f;
|
||||
|
||||
static bool pbr_terrain_enabled = gSavedSettings.get<BOOL>("RenderTerrainPBREnabled");
|
||||
static LLCachedControl<BOOL> pbr_terrain_experimental_normals(gSavedSettings, "RenderTerrainPBRNormalsEnabled", FALSE);
|
||||
bool pbr_material = mImpl->mCompositionp && (mImpl->mCompositionp->getMaterialType() == LLTerrainMaterials::Type::PBR);
|
||||
bool pbr_land = pbr_material && pbr_terrain_enabled && pbr_terrain_experimental_normals;
|
||||
|
||||
if (!pbr_land)
|
||||
{
|
||||
mImpl->mLandp->idleUpdate</*PBR=*/false>(max_update_time);
|
||||
}
|
||||
else
|
||||
{
|
||||
mImpl->mLandp->idleUpdate</*PBR=*/true>(max_update_time);
|
||||
}
|
||||
|
||||
if (mParcelOverlay)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -163,6 +163,9 @@ public:
|
|||
// Call this whenever you change the height data in the region.
|
||||
// (Automatically called by LLSurfacePatch's update routine)
|
||||
void dirtyHeights();
|
||||
// Call this whenever you want to force all terrain to rebuild.
|
||||
// (For example, if a global terrain config option has changed)
|
||||
void dirtyAllPatches();
|
||||
|
||||
LLViewerParcelOverlay *getParcelOverlay() const
|
||||
{ return mParcelOverlay; }
|
||||
|
|
|
|||
|
|
@ -214,6 +214,7 @@ BOOL LLVOSurfacePatch::updateGeometry(LLDrawable *drawable)
|
|||
|
||||
void LLVOSurfacePatch::updateFaceSize(S32 idx)
|
||||
{
|
||||
LL_PROFILE_ZONE_SCOPED;
|
||||
if (idx != 0)
|
||||
{
|
||||
LL_WARNS() << "Terrain partition requested invalid face!!!" << LL_ENDL;
|
||||
|
|
|
|||
|
|
@ -10870,3 +10870,12 @@ void LLPipeline::rebuildDrawInfo()
|
|||
}
|
||||
}
|
||||
|
||||
void LLPipeline::rebuildTerrain()
|
||||
{
|
||||
for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin();
|
||||
iter != LLWorld::getInstance()->getRegionList().end(); ++iter)
|
||||
{
|
||||
LLViewerRegion* region = *iter;
|
||||
region->dirtyAllPatches();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -134,6 +134,8 @@ public:
|
|||
|
||||
// rebuild all LLVOVolume render batches
|
||||
void rebuildDrawInfo();
|
||||
// Rebuild all terrain
|
||||
void rebuildTerrain();
|
||||
|
||||
// Clear LLFace mVertexBuffer pointers
|
||||
void resetVertexBuffers(LLDrawable* drawable);
|
||||
|
|
|
|||
|
|
@ -3297,6 +3297,13 @@ function="World.EnvPreset"
|
|||
<menu_item_call.on_click
|
||||
function="Advanced.ClearShaderCache" />
|
||||
</menu_item_call>
|
||||
<menu_item_call
|
||||
enabled="true"
|
||||
label="Rebuild Terrain"
|
||||
name="Rebuild Terrain">
|
||||
<menu_item_call.on_click
|
||||
function="Advanced.RebuildTerrain" />
|
||||
</menu_item_call>
|
||||
<menu_item_separator />
|
||||
<menu_item_call
|
||||
enabled="true"
|
||||
|
|
|
|||
Loading…
Reference in New Issue