123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678 |
- /**
- * @file llcloud.cpp
- * @brief Implementation of viewer cloud classes
- *
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- * Copyright (c) 2009-2024, Henri Beauchamp.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
- #include "llviewerprecompiledheaders.h"
- #include "llcloud.h"
- #include "llgl.h"
- #include "llpatch_code.h"
- #include "llagent.h"
- #include "llappviewer.h" // For gFrameTimeSeconds
- #include "lldrawpool.h"
- #include "llpipeline.h"
- #include "llsky.h"
- #include "llstartup.h" // For LLStartUp::isLoggedIn()
- #include "llsurfacepatch.h" // For gDirOpposite
- #include "llviewercamera.h"
- #include "llviewercontrol.h"
- #include "llviewerobjectlist.h"
- #include "llviewerregion.h"
- #include "llvoclouds.h"
- #include "llvosky.h"
- #include "llworld.h"
- constexpr F32 CLOUD_GROW_RATE = 0.05f;
- constexpr F32 CLOUD_DECAY_RATE = -0.05f;
- constexpr F32 CLOUD_VELOCITY_SCALE = 0.6f;
- constexpr F32 CLOUD_DENSITY = 25.f;
- constexpr S32 CLOUD_COUNT_MAX = 20;
- constexpr F32 CLOUD_HEIGHT_RANGE = 48.f;
- //static
- F32 LLCloudLayer::sCloudsAltitude = 192.f; // Default at login
- enum
- {
- LL_PUFF_GROWING = 0,
- LL_PUFF_DYING = 1
- };
- // Used for patch decoder
- S32 gBuffer[256];
- LLCloudPuff::LLCloudPuff()
- : mAlpha(0.01f),
- mRate(CLOUD_GROW_RATE),
- mLifeState(LL_PUFF_GROWING)
- {
- }
- LLCloudGroup::LLCloudGroup()
- : mCloudLayerp(NULL),
- mDensity(0.f),
- mTargetPuffCount(0),
- mLastAltitudeUpdate(0.f),
- mVOCloudsp(NULL)
- {
- }
- void LLCloudGroup::cleanup()
- {
- if (mVOCloudsp)
- {
- if (!mVOCloudsp->isDead())
- {
- gObjectList.killObject(mVOCloudsp);
- }
- mVOCloudsp = NULL;
- }
- }
- void LLCloudGroup::setCenterRegion(F32 x, F32 y)
- {
- mLastAltitudeUpdate = gFrameTimeSeconds;
- mCenterRegion.set(x, y, LLCloudLayer::getCloudsAltitude());
- if (mVOCloudsp)
- {
- mVOCloudsp->setPositionRegion(mCenterRegion);
- }
- }
- void LLCloudGroup::updatePuffs(F32 dt)
- {
- mDensity = mCloudLayerp->getDensityRegion(mCenterRegion);
- LLViewerRegion* regionp = mCloudLayerp->getRegion();
- if (!regionp) return; // Paranoia
- if (!mVOCloudsp || gFrameTimeSeconds - mLastAltitudeUpdate >= 10.f)
- {
- // Account for possible altitude change:
- setCenterRegion(mCenterRegion.mV[VX], mCenterRegion.mV[VY]);
- }
- if (!mVOCloudsp)
- {
- mVOCloudsp =
- (LLVOClouds*)gObjectList.createObjectViewer(LLViewerObject::LL_VO_CLOUDS,
- regionp);
- mVOCloudsp->setCloudGroup(this);
- mVOCloudsp->setPositionRegion(mCenterRegion);
- F32 hsize = mCloudLayerp->getMetersPerEdge() / CLOUD_GROUPS_PER_EDGE +
- CLOUD_PUFF_WIDTH;
- mVOCloudsp->setScale(LLVector3(hsize, hsize,
- CLOUD_HEIGHT_RANGE + CLOUD_PUFF_HEIGHT) * 0.5f);
- gPipeline.createObject(mVOCloudsp);
- }
- LLVector3 velocity;
- LLVector3d vel_d;
- S32 count = mCloudPuffs.size();
- // Update the positions of all of the clouds
- #if LL_OPENMP
- # pragma omp parallel for private(velocity, vel_d)
- #endif
- // NOTE: VS2017 OpenMP requires a signed integer loop index... HB
- for (S32 i = 0; i < count; ++i)
- {
- LLCloudPuff& puff = mCloudPuffs[i];
- velocity =
- regionp->mWind.getCloudVelocity(regionp->getPosRegionFromGlobal(puff.mPositionGlobal));
- velocity *= CLOUD_VELOCITY_SCALE * dt;
- vel_d.set(velocity);
- puff.mPositionGlobal += vel_d;
- puff.mAlpha += puff.mRate * dt;
- puff.mAlpha = llclamp(puff.mAlpha, 0.f, 1.f);
- }
- }
- void LLCloudGroup::updatePuffOwnership()
- {
- U32 i = 0;
- while (i < mCloudPuffs.size())
- {
- if (mCloudPuffs[i].getLifeState() == LL_PUFF_DYING)
- {
- ++i;
- continue;
- }
- if (inGroup(mCloudPuffs[i]))
- {
- ++i;
- continue;
- }
- LLCloudGroup* new_cgp = gWorld.findCloudGroup(mCloudPuffs[i]);
- if (!new_cgp)
- {
- mCloudPuffs[i].setLifeState(LL_PUFF_DYING);
- mCloudPuffs[i++].mRate = CLOUD_DECAY_RATE;
- continue;
- }
- LLCloudPuff puff;
- puff.mPositionGlobal = mCloudPuffs[i].mPositionGlobal;
- puff.mAlpha = mCloudPuffs[i].mAlpha;
- mCloudPuffs.erase(mCloudPuffs.begin() + i);
- new_cgp->mCloudPuffs.push_back(puff);
- }
- }
- void LLCloudGroup::updatePuffCount()
- {
- if (!mVOCloudsp)
- {
- return;
- }
- S32 target_puff_count = ll_roundp(CLOUD_DENSITY * mDensity);
- target_puff_count = llmax(0, target_puff_count);
- target_puff_count = llmin(CLOUD_COUNT_MAX, target_puff_count);
- S32 current_puff_count = (S32)mCloudPuffs.size();
- // Create a new cloud if we need one
- if (current_puff_count < target_puff_count)
- {
- F32 hsize = mCloudLayerp->getMetersPerEdge() / CLOUD_GROUPS_PER_EDGE;
- LLVector3d puff_pos_global;
- mCloudPuffs.resize(target_puff_count);
- for (S32 i = current_puff_count; i < target_puff_count; ++i)
- {
- puff_pos_global = mVOCloudsp->getPositionGlobal();
- F32 x = ll_frand(hsize) - 0.5f * hsize;
- F32 y = ll_frand(hsize) - 0.5f * hsize;
- F32 z = ll_frand(CLOUD_HEIGHT_RANGE) - 0.5f * CLOUD_HEIGHT_RANGE;
- puff_pos_global += LLVector3d(x, y, z);
- mCloudPuffs[i].mPositionGlobal = puff_pos_global;
- mCloudPuffs[i].mAlpha = 0.01f;
- }
- }
- // Count the number of live puffs
- S32 live_puff_count = 0;
- for (S32 i = 0, count = mCloudPuffs.size(); i < count; ++i)
- {
- if (mCloudPuffs[i].getLifeState() != LL_PUFF_DYING)
- {
- ++live_puff_count;
- }
- }
- // Start killing enough puffs so the live puff count == target puff count
- S32 new_dying_count = llmax(0, live_puff_count - target_puff_count);
- S32 i = 0;
- while (new_dying_count > 0)
- {
- if (mCloudPuffs[i].getLifeState() != LL_PUFF_DYING)
- {
- mCloudPuffs[i].setLifeState(LL_PUFF_DYING);
- mCloudPuffs[i].mRate = CLOUD_DECAY_RATE;
- --new_dying_count;
- }
- ++i;
- }
- // Remove fully dead puffs
- i = 0;
- while (i < (S32)mCloudPuffs.size())
- {
- if (mCloudPuffs[i].isDead())
- {
- mCloudPuffs.erase(mCloudPuffs.begin() + i);
- }
- else
- {
- ++i;
- }
- }
- }
- bool LLCloudGroup::inGroup(const LLCloudPuff& puff) const
- {
- LLViewerRegion* regionp = mCloudLayerp->getRegion();
- if (!regionp) return false; // Paranoia
- // Do min/max check on center of the cloud puff
- F32 min_x, min_y, max_x, max_y;
- F32 delta = mCloudLayerp->getMetersPerEdge() /
- CLOUD_GROUPS_PER_EDGE * 0.5f;
- min_x = mCenterRegion.mV[VX] - delta;
- min_y = mCenterRegion.mV[VY] - delta;
- max_x = mCenterRegion.mV[VX] + delta;
- max_y = mCenterRegion.mV[VY] + delta;
- LLVector3 pos_region =
- regionp->getPosRegionFromGlobal(puff.getPositionGlobal());
- if (pos_region.mV[VX] < min_x || pos_region.mV[VY] < min_y ||
- pos_region.mV[VX] > max_x || pos_region.mV[VY] > max_y)
- {
- return false;
- }
- return true;
- }
- LLCloudLayer::LLCloudLayer()
- : mOriginGlobal(0.f, 0.f, 0.f),
- mMetersPerEdge(1.f),
- mMetersPerGrid(1.f),
- mWindp(NULL),
- mDensityp(NULL),
- mLastDensityUpdate(0.f)
- {
- for (S32 i = 0; i < 4; ++i)
- {
- mNeighbors[i] = NULL;
- }
- for (S32 i = 0; i < CLOUD_GROUPS_PER_EDGE; ++i)
- {
- for (S32 j = 0; j < CLOUD_GROUPS_PER_EDGE; ++j)
- {
- mCloudGroups[i][j].setCloudLayerp(this);
- }
- }
- }
- LLCloudLayer::~LLCloudLayer()
- {
- destroy();
- }
- //static
- F32 LLCloudLayer::getCloudsAltitude()
- {
- static LLCachedControl<S32> clouds_altitude(gSavedSettings,
- "ClassicCloudsAvgAlt");
- static LLCachedControl<U32> max_clouds_alt(gSavedSettings,
- "ClassicCloudsMaxAlt");
- if (clouds_altitude > 0.f)
- {
- sCloudsAltitude = clouds_altitude;
- }
- // Wait until fully logged in before using the agent altitude (which is 0
- // until we get the region data and the correct agent position).
- else if (LLStartUp::isLoggedIn())
- {
- sCloudsAltitude = gAgent.getPositionAgent().mV[VZ] - clouds_altitude;
- }
- constexpr F32 MIN_ALT = CLOUD_HEIGHT_RANGE + CLOUD_PUFF_HEIGHT * 0.5f;
- sCloudsAltitude = llclamp(sCloudsAltitude, MIN_ALT, (F32)max_clouds_alt);
- return sCloudsAltitude;
- }
- //static
- bool LLCloudLayer::needClassicClouds()
- {
- // Do not use clouds if they are not wanted or when the camera is
- // underwater (in the latter case, clouds flicker when looking up at
- // the water surface and when in non-deferred rendering mode). HB
- static LLCachedControl<bool> use_classic_clouds(gSavedSettings,
- "SkyUseClassicClouds");
- if (!use_classic_clouds || gViewerCamera.cameraUnderWater())
- {
- return false;
- }
- // Do not use clouds if they are beyond the draw distance. HB
- static LLCachedControl<F32> draw_distance(gSavedSettings, "RenderFarClip");
- F32 delta = fabsf(sCloudsAltitude - gViewerCamera.getOrigin().mV[VZ]);
- return delta < draw_distance + CLOUD_HEIGHT_RANGE;
- }
- void LLCloudLayer::create(LLViewerRegion* regionp)
- {
- llassert(regionp);
- setRegion(regionp);
- mDensityp = new F32[CLOUD_GRIDS_PER_EDGE * CLOUD_GRIDS_PER_EDGE];
- for (U32 i = 0; i < CLOUD_GRIDS_PER_EDGE * CLOUD_GRIDS_PER_EDGE; ++i)
- {
- mDensityp[i] = 0.f;
- }
- }
- void LLCloudLayer::setRegion(LLViewerRegion* regionp)
- {
- mRegionp = regionp;
- if (regionp)
- {
- // Variable region size support. HB
- mMetersPerEdge = regionp->getWidth();
- mMetersPerGrid = mMetersPerEdge / CLOUD_GRIDS_PER_EDGE;
- for (S32 i = 0; i < CLOUD_GROUPS_PER_EDGE; ++i)
- {
- F32 y = (0.5f + i) * mMetersPerEdge / CLOUD_GROUPS_PER_EDGE;
- for (S32 j = 0; j < CLOUD_GROUPS_PER_EDGE; ++j)
- {
- F32 x = (0.5f + j) * mMetersPerEdge / CLOUD_GROUPS_PER_EDGE;
- mCloudGroups[i][j].setCenterRegion(x, y);
- }
- }
- }
- }
- void LLCloudLayer::destroy()
- {
- reset();
- delete[] mDensityp;
- mDensityp = NULL;
- mWindp = NULL;
- }
- void LLCloudLayer::reset()
- {
- // Kill all of the existing puffs
- for (S32 i = 0; i < CLOUD_GROUPS_PER_EDGE; ++i)
- {
- for (S32 j = 0; j < CLOUD_GROUPS_PER_EDGE; ++j)
- {
- mCloudGroups[i][j].cleanup();
- }
- }
- }
- void LLCloudLayer::setWindPointer(LLWind* windp)
- {
- if (mWindp)
- {
- mWindp->setCloudDensityPointer(NULL);
- }
- mWindp = windp;
- if (mWindp)
- {
- mWindp->setCloudDensityPointer(mDensityp);
- }
- }
- F32 LLCloudLayer::getDensityRegion(const LLVector3& pos_region)
- {
- // "position" is region-local
- S32 i, j, ii, jj;
- i = lltrunc(pos_region.mV[VX] / mMetersPerGrid);
- j = lltrunc(pos_region.mV[VY] / mMetersPerGrid);
- ii = i + 1;
- jj = j + 1;
- // clamp
- if (i >= (S32)CLOUD_GRIDS_PER_EDGE)
- {
- i = CLOUD_GRIDS_PER_EDGE - 1;
- ii = i;
- }
- else if (i < 0)
- {
- i = 0;
- ii = i;
- }
- else if (ii >= (S32)CLOUD_GRIDS_PER_EDGE || ii < 0)
- {
- ii = i;
- }
- if (j >= (S32)CLOUD_GRIDS_PER_EDGE)
- {
- j = CLOUD_GRIDS_PER_EDGE - 1;
- jj = j;
- }
- else if (j < 0)
- {
- j = 0;
- jj = j;
- }
- else if (jj >= (S32)CLOUD_GRIDS_PER_EDGE || jj < 0)
- {
- jj = j;
- }
- F32 dx = (pos_region.mV[VX] - (F32)i * mMetersPerGrid) / mMetersPerGrid;
- F32 dy = (pos_region.mV[VY] - (F32)j * mMetersPerGrid) / mMetersPerGrid;
- F32 omdx = 1.f - dx;
- F32 omdy = 1.f - dy;
- F32 density = dx * dy * *(mDensityp + ii + jj * CLOUD_GRIDS_PER_EDGE) +
- dx * omdy * *(mDensityp + i + jj * CLOUD_GRIDS_PER_EDGE) +
- omdx * dy * *(mDensityp + ii + j * CLOUD_GRIDS_PER_EDGE) +
- omdx * omdy * *(mDensityp + i + j * CLOUD_GRIDS_PER_EDGE);
- return density;
- }
- bool LLCloudLayer::shouldUpdateDensity()
- {
- // Not more often than once every second
- return gFrameTimeSeconds - mLastDensityUpdate >= 1.f;
- }
- // This method is called for regions not sending classic clouds layer data
- // (i.e. in SL's new servers). It gets called each time the viewer receives
- // a wind layer data packet (which happens once every second for each region
- // with SL's new sim servers). It generates (the first time it gets called) and
- // updates (the rest of the time) a local (viewer-side) cloud density matrix
- // in replacement for the missing data layer updates. HB
- void LLCloudLayer::generateDensity()
- {
- if (mLastDensityUpdate == 0.f)
- {
- for (U32 i = 0; i < CLOUD_GRIDS_PER_EDGE * CLOUD_GRIDS_PER_EDGE; ++i)
- {
- // Limits deduced from values sampled in old, classic-clouds
- // enabled SL sim servers. HB
- mDensityp[i] = ll_frand(4.f) - 1.f;
- }
- mLastDensityUpdate = gFrameTimeSeconds;
- }
- // Not more often than once every second
- else if (shouldUpdateDensity())
- {
- // Update the density probability matrix by averaging the value of
- // surrounding cells for each cell, with a weight factor of 2 for
- // the cell value itself and by adding a small random factor, i.e.:
- // average = (2 * this_cell + neighbors_total) / (neighbors + 2) + rand
- // For edges, we "wrap" around north/south west/east rows/columns. HB
- static F32 bufferp[CLOUD_GRIDS_PER_EDGE * CLOUD_GRIDS_PER_EDGE];
- #if LL_OPENMP
- # pragma omp parallel for
- #endif
- for (S32 x = 0; x < (S32)CLOUD_GRIDS_PER_EDGE; ++x)
- {
- S32 west = x - 1;
- if (west < 0)
- {
- west = CLOUD_GRIDS_PER_EDGE - 2; // wrap edges
- }
- S32 east = x + 1;
- if (east >= (S32)CLOUD_GRIDS_PER_EDGE)
- {
- east = 0; // wrap edges
- }
- for (S32 y = 0; y < (S32)CLOUD_GRIDS_PER_EDGE; ++y)
- {
- S32 south = y - 1;
- if (south < 0)
- {
- south = CLOUD_GRIDS_PER_EDGE - 2; // wrap edges
- }
- S32 north = y + 1;
- if (north >= (S32)CLOUD_GRIDS_PER_EDGE)
- {
- north = 0; // wrap edges
- }
- north *= CLOUD_GRIDS_PER_EDGE;
- south *= CLOUD_GRIDS_PER_EDGE;
- S32 here = y * CLOUD_GRIDS_PER_EDGE;
- F32 average = 2.f * mDensityp[here + x];
- average += mDensityp[north + west] + mDensityp[north + x];
- average += mDensityp[north + east] + mDensityp[here + east];
- average += mDensityp[south + east] + mDensityp[south + x];
- average += mDensityp[south + west] + mDensityp[here + west];
- average = llclamp(average * 0.1f + ll_frand(0.5f) - 0.25f,
- -1.f, 3.f);
- bufferp[here + x] = average;
- }
- }
- memcpy((void*)mDensityp, (void*)bufferp,
- sizeof(F32) * CLOUD_GRIDS_PER_EDGE * CLOUD_GRIDS_PER_EDGE);
- mLastDensityUpdate = gFrameTimeSeconds;
- }
- }
- void LLCloudLayer::resetDensity()
- {
- if (mLastDensityUpdate > 0.f)
- {
- for (U32 i = 0; i < CLOUD_GRIDS_PER_EDGE * CLOUD_GRIDS_PER_EDGE; ++i)
- {
- mDensityp[i] = 0.f;
- }
- reset();
- mLastDensityUpdate = 0.f;
- }
- }
- void LLCloudLayer::decompress(LLBitPack& bitpack, LLGroupHeader* group_headerp)
- {
- init_patch_decompressor(group_headerp->patch_size);
- // Do not use the packed group_header stride because the strides used on
- // simulator and viewer are not equal. Offset required to step up one row.
- group_headerp->stride = group_headerp->patch_size;
- LLPatchHeader patch_header;
- set_group_of_patch_header(group_headerp);
- decode_patch_header(bitpack, &patch_header);
- decode_patch(bitpack, gBuffer);
- decompress_patch(mDensityp, gBuffer, &patch_header);
- mLastDensityUpdate = gFrameTimeSeconds;
- }
- void LLCloudLayer::updatePuffs(F32 dt)
- {
- // We want to iterate through all of the cloud groups and update their
- // density targets
- for (S32 i = 0; i < CLOUD_GROUPS_PER_EDGE; ++i)
- {
- for (S32 j = 0; j < CLOUD_GROUPS_PER_EDGE; ++j)
- {
- mCloudGroups[i][j].updatePuffs(dt);
- }
- }
- }
- void LLCloudLayer::updatePuffOwnership()
- {
- for (S32 i = 0; i < CLOUD_GROUPS_PER_EDGE; ++i)
- {
- for (S32 j = 0; j < CLOUD_GROUPS_PER_EDGE; ++j)
- {
- mCloudGroups[i][j].updatePuffOwnership();
- }
- }
- }
- void LLCloudLayer::updatePuffCount()
- {
- for (S32 i = 0; i < CLOUD_GROUPS_PER_EDGE; ++i)
- {
- for (S32 j = 0; j < CLOUD_GROUPS_PER_EDGE; ++j)
- {
- mCloudGroups[i][j].updatePuffCount();
- }
- }
- }
- LLCloudGroup* LLCloudLayer::findCloudGroup(const LLCloudPuff& puff)
- {
- for (S32 i = 0; i < CLOUD_GROUPS_PER_EDGE; ++i)
- {
- for (S32 j = 0; j < CLOUD_GROUPS_PER_EDGE; ++j)
- {
- if (mCloudGroups[i][j].inGroup(puff))
- {
- return &(mCloudGroups[i][j]);
- }
- }
- }
- return NULL;
- }
- void LLCloudLayer::connectNeighbor(LLCloudLayer* cloudp, U32 direction)
- {
- if (direction >= 4)
- {
- // Only care about cardinal 4 directions.
- return;
- }
- if (!cloudp && mNeighbors[direction])
- {
- mNeighbors[direction]->mNeighbors[gDirOpposite[direction]] = NULL;
- }
- mNeighbors[direction] = cloudp;
- if (cloudp)
- {
- cloudp->mNeighbors[gDirOpposite[direction]] = this;
- }
- }
- void LLCloudLayer::disconnectNeighbor(U32 direction)
- {
- if (direction >= 4)
- {
- // Only care about cardinal 4 directions.
- return;
- }
- LLCloudLayer* cloudp = mNeighbors[direction];
- if (cloudp)
- {
- cloudp->mNeighbors[gDirOpposite[direction]] = NULL;
- mNeighbors[direction] = NULL;
- }
- }
- void LLCloudLayer::disconnectAllNeighbors()
- {
- for (S32 i = 0; i < 4; ++i)
- {
- disconnectNeighbor(i);
- }
- }
|