00001
00032 #include "llviewerprecompiledheaders.h"
00033
00034 #include "llvlcomposition.h"
00035
00036 #include "imageids.h"
00037 #include "llerror.h"
00038 #include "v3math.h"
00039 #include "llsurface.h"
00040 #include "lltextureview.h"
00041 #include "llviewerimage.h"
00042 #include "llviewerimagelist.h"
00043 #include "llviewerregion.h"
00044 #include "noise.h"
00045 #include "llregionhandle.h"
00046 #include "llviewercontrol.h"
00047
00048
00049
00050 F32 bilinear(const F32 v00, const F32 v01, const F32 v10, const F32 v11, const F32 x_frac, const F32 y_frac)
00051 {
00052
00053
00054 F32 result;
00055
00056 const F32 inv_x_frac = 1.f - x_frac;
00057 const F32 inv_y_frac = 1.f - y_frac;
00058 result = inv_x_frac*inv_y_frac*v00
00059 + x_frac*inv_y_frac*v10
00060 + inv_x_frac*y_frac*v01
00061 + x_frac*y_frac*v11;
00062
00063 return result;
00064 }
00065
00066
00067 LLVLComposition::LLVLComposition(LLSurface *surfacep, const U32 width, const F32 scale) :
00068 LLViewerLayer(width, scale),
00069 mParamsReady(FALSE)
00070 {
00071 mSurfacep = surfacep;
00072
00073
00074 setDetailTextureID(0, TERRAIN_DIRT_DETAIL);
00075 setDetailTextureID(1, TERRAIN_GRASS_DETAIL);
00076 setDetailTextureID(2, TERRAIN_MOUNTAIN_DETAIL);
00077 setDetailTextureID(3, TERRAIN_ROCK_DETAIL);
00078
00079
00080 for (S32 i = 0; i < CORNER_COUNT; ++i)
00081 {
00082 mStartHeight[i] = gSavedSettings.getF32("TerrainColorStartHeight");
00083 mHeightRange[i] = gSavedSettings.getF32("TerrainColorHeightRange");
00084 }
00085 mTexScaleX = 16.f;
00086 mTexScaleY = 16.f;
00087 mTexturesLoaded = FALSE;
00088 }
00089
00090
00091 LLVLComposition::~LLVLComposition()
00092 {
00093 }
00094
00095
00096 void LLVLComposition::setSurface(LLSurface *surfacep)
00097 {
00098 mSurfacep = surfacep;
00099 }
00100
00101
00102 void LLVLComposition::setDetailTextureID(S32 corner, const LLUUID& id)
00103 {
00104 if(id.isNull())
00105 {
00106 return;
00107 }
00108 mDetailTextures[corner] = gImageList.getImage(id);
00109 mRawImages[corner] = NULL;
00110 }
00111
00112 BOOL LLVLComposition::generateHeights(const F32 x, const F32 y,
00113 const F32 width, const F32 height)
00114 {
00115 if (!mParamsReady)
00116 {
00117
00118 return FALSE;
00119 }
00120
00121 llassert(mSurfacep);
00122
00123 if (!mSurfacep || !mSurfacep->getRegion())
00124 {
00125
00126 return FALSE;
00127 }
00128
00129 S32 x_begin, y_begin, x_end, y_end;
00130
00131 x_begin = llround( x * mScaleInv );
00132 y_begin = llround( y * mScaleInv );
00133 x_end = llround( (x + width) * mScaleInv );
00134 y_end = llround( (y + width) * mScaleInv );
00135
00136 if (x_end > mWidth)
00137 {
00138 x_end = mWidth;
00139 }
00140 if (y_end > mWidth)
00141 {
00142 y_end = mWidth;
00143 }
00144
00145 LLVector3d origin_global = from_region_handle(mSurfacep->getRegion()->getHandle());
00146
00147
00148 const F32 slope_squared = 1.5f*1.5f;
00149 const F32 xyScale = 4.9215f;
00150 const F32 zScale = 4;
00151 const F32 z_offset = 0.f;
00152 const F32 noise_magnitude = 2.f;
00153
00154
00155
00156
00157 const S32 NUM_TEXTURES = 4;
00158
00159 const F32 xyScaleInv = (1.f / xyScale);
00160 const F32 zScaleInv = (1.f / zScale);
00161
00162 const F32 inv_width = 1.f/mWidth;
00163
00164
00165 for (S32 j = y_begin; j < y_end; j++)
00166 {
00167 for (S32 i = x_begin; i < x_end; i++)
00168 {
00169
00170 F32 vec[3];
00171 F32 vec1[3];
00172 F32 twiddle;
00173
00174
00175 F32 start_height = bilinear(mStartHeight[SOUTHWEST],
00176 mStartHeight[SOUTHEAST],
00177 mStartHeight[NORTHWEST],
00178 mStartHeight[NORTHEAST],
00179 i*inv_width, j*inv_width);
00180 F32 height_range = bilinear(mHeightRange[SOUTHWEST],
00181 mHeightRange[SOUTHEAST],
00182 mHeightRange[NORTHWEST],
00183 mHeightRange[NORTHEAST],
00184 i*inv_width, j*inv_width);
00185
00186 LLVector3 location(i*mScale, j*mScale, 0.f);
00187
00188 F32 height = mSurfacep->resolveHeightRegion(location) + z_offset;
00189
00190
00191 vec[0] = (F32)(origin_global.mdV[VX]+location.mV[VX])*xyScaleInv;
00192 vec[1] = (F32)(origin_global.mdV[VY]+location.mV[VY])*xyScaleInv;
00193 vec[2] = height*zScaleInv;
00194
00195
00196
00197 vec1[0] = vec[0]*(0.2222222222f);
00198 vec1[1] = vec[1]*(0.2222222222f);
00199 vec1[2] = vec[2]*(0.2222222222f);
00200 twiddle = noise2(vec1)*6.5f;
00201
00202 twiddle += turbulence2(vec, 2)*slope_squared;
00203 twiddle *= noise_magnitude;
00204
00205 F32 scaled_noisy_height = (height + twiddle - start_height) * F32(NUM_TEXTURES) / height_range;
00206
00207 scaled_noisy_height = llmax(0.f, scaled_noisy_height);
00208 scaled_noisy_height = llmin(3.f, scaled_noisy_height);
00209 *(mDatap + i + j*mWidth) = scaled_noisy_height;
00210 }
00211 }
00212 return TRUE;
00213 }
00214
00215 static const S32 BASE_SIZE = 128;
00216
00217 BOOL LLVLComposition::generateComposition()
00218 {
00219
00220 if (!mParamsReady)
00221 {
00222
00223 return FALSE;
00224 }
00225
00226 for (S32 i = 0; i < 4; i++)
00227 {
00228 if (mDetailTextures[i]->getDiscardLevel() < 0)
00229 {
00230 mDetailTextures[i]->setBoostLevel(LLViewerImage::BOOST_TERRAIN);
00231 mDetailTextures[i]->addTextureStats(BASE_SIZE*BASE_SIZE);
00232 return FALSE;
00233 }
00234 if ((mDetailTextures[i]->getDiscardLevel() != 0 &&
00235 (mDetailTextures[i]->getWidth() < BASE_SIZE ||
00236 mDetailTextures[i]->getHeight() < BASE_SIZE)))
00237 {
00238 S32 width = mDetailTextures[i]->getWidth(0);
00239 S32 height = mDetailTextures[i]->getHeight(0);
00240 S32 min_dim = llmin(width, height);
00241 S32 ddiscard = 0;
00242 while (min_dim > BASE_SIZE && ddiscard < MAX_DISCARD_LEVEL)
00243 {
00244 ddiscard++;
00245 min_dim /= 2;
00246 }
00247 mDetailTextures[i]->setBoostLevel(LLViewerImage::BOOST_TERRAIN);
00248 mDetailTextures[i]->setMinDiscardLevel(ddiscard);
00249 return FALSE;
00250 }
00251 }
00252
00253 return TRUE;
00254 }
00255
00256 BOOL LLVLComposition::generateTexture(const F32 x, const F32 y,
00257 const F32 width, const F32 height)
00258 {
00259 llassert(mSurfacep);
00260 llassert(x >= 0.f);
00261 llassert(y >= 0.f);
00262
00263 LLTimer gen_timer;
00264
00266
00267
00268
00269
00270
00271
00272 U8* st_data[4];
00273 S32 st_data_size[4];
00274
00275 for (S32 i = 0; i < 4; i++)
00276 {
00277 if (mRawImages[i].isNull())
00278 {
00279
00280 mRawImages[i] = new LLImageRaw;
00281 S32 min_dim = llmin(mDetailTextures[i]->getWidth(0), mDetailTextures[i]->getHeight(0));
00282 S32 ddiscard = 0;
00283 while (min_dim > BASE_SIZE && ddiscard < MAX_DISCARD_LEVEL)
00284 {
00285 ddiscard++;
00286 min_dim /= 2;
00287 }
00288 if (!mDetailTextures[i]->readBackRaw(ddiscard, mRawImages[i], false))
00289 {
00290 llwarns << "Unable to read raw data for terrain detail texture: " << mDetailTextures[i]->getID() << llendl;
00291 mRawImages[i] = NULL;
00292 return FALSE;
00293 }
00294 if (mDetailTextures[i]->getWidth(ddiscard) != BASE_SIZE ||
00295 mDetailTextures[i]->getHeight(ddiscard) != BASE_SIZE ||
00296 mDetailTextures[i]->getComponents() != 3)
00297 {
00298 LLPointer<LLImageRaw> newraw = new LLImageRaw(BASE_SIZE, BASE_SIZE, 3);
00299 newraw->composite(mRawImages[i]);
00300 mRawImages[i] = newraw;
00301 }
00302 }
00303 st_data[i] = mRawImages[i]->getData();
00304 st_data_size[i] = mRawImages[i]->getDataSize();
00305 }
00306
00308
00309
00310
00311
00312
00313 S32 x_begin, y_begin, x_end, y_end;
00314 x_begin = (S32)(x * mScaleInv);
00315 y_begin = (S32)(y * mScaleInv);
00316 x_end = llround( (x + width) * mScaleInv );
00317 y_end = llround( (y + width) * mScaleInv );
00318
00319 if (x_end > mWidth)
00320 {
00321 llwarns << "x end > width" << llendl;
00322 x_end = mWidth;
00323 }
00324 if (y_end > mWidth)
00325 {
00326 llwarns << "y end > width" << llendl;
00327 y_end = mWidth;
00328 }
00329
00330
00332
00333
00334
00335
00336
00337 LLViewerImage *texturep;
00338 U32 tex_width, tex_height, tex_comps;
00339 U32 tex_stride;
00340 F32 tex_x_scalef, tex_y_scalef;
00341 S32 tex_x_begin, tex_y_begin, tex_x_end, tex_y_end;
00342 F32 tex_x_ratiof, tex_y_ratiof;
00343
00344 texturep = mSurfacep->getSTexture();
00345 tex_width = texturep->getWidth();
00346 tex_height = texturep->getHeight();
00347 tex_comps = texturep->getComponents();
00348 tex_stride = tex_width * tex_comps;
00349
00350 S32 st_comps = 3;
00351 S32 st_width = BASE_SIZE;
00352 S32 st_height = BASE_SIZE;
00353
00354 if (tex_comps != st_comps)
00355 {
00356 llwarns << "Base texture comps != input texture comps" << llendl;
00357 return FALSE;
00358 }
00359
00360 tex_x_scalef = (F32)tex_width / (F32)mWidth;
00361 tex_y_scalef = (F32)tex_height / (F32)mWidth;
00362 tex_x_begin = (S32)((F32)x_begin * tex_x_scalef);
00363 tex_y_begin = (S32)((F32)y_begin * tex_y_scalef);
00364 tex_x_end = (S32)((F32)x_end * tex_x_scalef);
00365 tex_y_end = (S32)((F32)y_end * tex_y_scalef);
00366
00367 tex_x_ratiof = (F32)mWidth*mScale / (F32)tex_width;
00368 tex_y_ratiof = (F32)mWidth*mScale / (F32)tex_height;
00369
00370 LLPointer<LLImageRaw> raw = new LLImageRaw(tex_width, tex_height, tex_comps);
00371 U8 *rawp = raw->getData();
00372
00373 F32 tex_width_inv = 1.f/tex_width;
00374 F32 tex_height_inv = 1.f/tex_height;
00375
00376 F32 st_x_stride, st_y_stride;
00377 st_x_stride = ((F32)st_width / (F32)mTexScaleX)*((F32)mWidth / (F32)tex_width);
00378 st_y_stride = ((F32)st_height / (F32)mTexScaleY)*((F32)mWidth / (F32)tex_height);
00379
00380 llassert(st_x_stride > 0.f);
00381 llassert(st_y_stride > 0.f);
00383
00384
00385
00386
00387
00388
00389 F32 sti, stj;
00390 S32 st_offset;
00391 sti = (tex_x_begin * st_x_stride) - st_width*(llfloor((tex_x_begin * st_x_stride)/st_width));
00392 stj = (tex_y_begin * st_y_stride) - st_height*(llfloor((tex_y_begin * st_y_stride)/st_height));
00393
00394 st_offset = (llfloor(stj * st_width) + llfloor(sti)) * st_comps;
00395 for (S32 j = tex_y_begin; j < tex_y_end; j++)
00396 {
00397 U32 offset = j * tex_stride + tex_x_begin * tex_comps;
00398 sti = (tex_x_begin * st_x_stride) - st_width*((U32)(tex_x_begin * st_x_stride)/st_width);
00399 for (S32 i = tex_x_begin; i < tex_x_end; i++)
00400 {
00401 S32 tex0, tex1;
00402 F32 composition = getValueScaled(i*tex_x_ratiof, j*tex_y_ratiof);
00403
00404 tex0 = llfloor( composition );
00405 tex0 = llclamp(tex0, 0, 3);
00406 composition -= tex0;
00407 tex1 = tex0 + 1;
00408 tex1 = llclamp(tex1, 0, 3);
00409
00410 F32 xy_int_i, xy_int_j;
00411
00412 xy_int_i = i * tex_width_inv;
00413 xy_int_j = j * tex_height_inv;
00414
00415 st_offset = (lltrunc(sti) + lltrunc(stj)*st_width) * st_comps;
00416 for (U32 k = 0; k < tex_comps; k++)
00417 {
00418
00419 if (st_offset >= st_data_size[tex0] || st_offset >= st_data_size[tex1])
00420 {
00421
00422
00423
00424 }
00425 else
00426 {
00427 F32 a = *(st_data[tex0] + st_offset);
00428 F32 b = *(st_data[tex1] + st_offset);
00429 rawp[ offset ] = (U8)lltrunc( a + composition * (b - a) );
00430 }
00431 offset++;
00432 st_offset++;
00433 }
00434
00435 sti += st_x_stride;
00436 if (sti >= st_width)
00437 {
00438 sti -= st_width;
00439 }
00440 }
00441
00442 stj += st_y_stride;
00443 if (stj >= st_height)
00444 {
00445 stj -= st_height;
00446 }
00447 }
00448
00449 texturep->setSubImage(raw, tex_x_begin, tex_y_begin, tex_x_end - tex_x_begin, tex_y_end - tex_y_begin);
00450 LLSurface::sTextureUpdateTime += gen_timer.getElapsedTimeF32();
00451 LLSurface::sTexelsUpdated += (tex_x_end - tex_x_begin) * (tex_y_end - tex_y_begin);
00452
00453 for (S32 i = 0; i < 4; i++)
00454 {
00455
00456 mDetailTextures[i]->setBoostLevel(LLViewerImage::BOOST_NONE);
00457 mDetailTextures[i]->setMinDiscardLevel(MAX_DISCARD_LEVEL + 1);
00458 }
00459
00460 return TRUE;
00461 }
00462
00463 LLUUID LLVLComposition::getDetailTextureID(S32 corner)
00464 {
00465 return mDetailTextures[corner]->getID();
00466 }
00467
00468 LLViewerImage* LLVLComposition::getDetailTexture(S32 corner)
00469 {
00470 return mDetailTextures[corner];
00471 }
00472
00473 F32 LLVLComposition::getStartHeight(S32 corner)
00474 {
00475 return mStartHeight[corner];
00476 }
00477
00478 void LLVLComposition::setStartHeight(S32 corner, const F32 start_height)
00479 {
00480 mStartHeight[corner] = start_height;
00481 }
00482
00483 F32 LLVLComposition::getHeightRange(S32 corner)
00484 {
00485 return mHeightRange[corner];
00486 }
00487
00488 void LLVLComposition::setHeightRange(S32 corner, const F32 range)
00489 {
00490 mHeightRange[corner] = range;
00491 }