diff options
Diffstat (limited to 'linden/indra/newview/llvlcomposition.cpp')
-rw-r--r-- | linden/indra/newview/llvlcomposition.cpp | 479 |
1 files changed, 479 insertions, 0 deletions
diff --git a/linden/indra/newview/llvlcomposition.cpp b/linden/indra/newview/llvlcomposition.cpp new file mode 100644 index 0000000..f2fa1e6 --- /dev/null +++ b/linden/indra/newview/llvlcomposition.cpp | |||
@@ -0,0 +1,479 @@ | |||
1 | /** | ||
2 | * @file llvlcomposition.cpp | ||
3 | * @brief Viewer-side representation of a composition layer... | ||
4 | * | ||
5 | * Copyright (c) 2001-2007, Linden Research, Inc. | ||
6 | * | ||
7 | * The source code in this file ("Source Code") is provided by Linden Lab | ||
8 | * to you under the terms of the GNU General Public License, version 2.0 | ||
9 | * ("GPL"), unless you have obtained a separate licensing agreement | ||
10 | * ("Other License"), formally executed by you and Linden Lab. Terms of | ||
11 | * the GPL can be found in doc/GPL-license.txt in this distribution, or | ||
12 | * online at http://secondlife.com/developers/opensource/gplv2 | ||
13 | * | ||
14 | * There are special exceptions to the terms and conditions of the GPL as | ||
15 | * it is applied to this Source Code. View the full text of the exception | ||
16 | * in the file doc/FLOSS-exception.txt in this software distribution, or | ||
17 | * online at http://secondlife.com/developers/opensource/flossexception | ||
18 | * | ||
19 | * By copying, modifying or distributing this software, you acknowledge | ||
20 | * that you have read and understood your obligations described above, | ||
21 | * and agree to abide by those obligations. | ||
22 | * | ||
23 | * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO | ||
24 | * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, | ||
25 | * COMPLETENESS OR PERFORMANCE. | ||
26 | */ | ||
27 | |||
28 | #include "llviewerprecompiledheaders.h" | ||
29 | |||
30 | #include "llvlcomposition.h" | ||
31 | #include "llerror.h" | ||
32 | #include "v3math.h" | ||
33 | #include "llsurface.h" | ||
34 | #include "lltextureview.h" | ||
35 | #include "llviewerimage.h" | ||
36 | #include "llviewerimagelist.h" | ||
37 | #include "llviewerregion.h" | ||
38 | #include "noise.h" | ||
39 | #include "llregionhandle.h" // for from_region_handle | ||
40 | #include "llviewercontrol.h" | ||
41 | |||
42 | |||
43 | |||
44 | F32 bilinear(const F32 v00, const F32 v01, const F32 v10, const F32 v11, const F32 x_frac, const F32 y_frac) | ||
45 | { | ||
46 | // Not sure if this is the right math... | ||
47 | // Take weighted average of all four points (bilinear interpolation) | ||
48 | F32 result; | ||
49 | |||
50 | const F32 inv_x_frac = 1.f - x_frac; | ||
51 | const F32 inv_y_frac = 1.f - y_frac; | ||
52 | result = inv_x_frac*inv_y_frac*v00 | ||
53 | + x_frac*inv_y_frac*v10 | ||
54 | + inv_x_frac*y_frac*v01 | ||
55 | + x_frac*y_frac*v11; | ||
56 | |||
57 | return result; | ||
58 | } | ||
59 | |||
60 | |||
61 | LLVLComposition::LLVLComposition(LLSurface *surfacep, const U32 width, const F32 scale) : | ||
62 | LLViewerLayer(width, scale), | ||
63 | mParamsReady(FALSE) | ||
64 | { | ||
65 | mSurfacep = surfacep; | ||
66 | |||
67 | // Load Terrain Textures - Original ones | ||
68 | LLUUID id; | ||
69 | // Dirt | ||
70 | id.set( gViewerArt.getString("terrain_dirt_detail.tga") ); | ||
71 | setDetailTextureID(0, id); | ||
72 | |||
73 | // Grass | ||
74 | id.set( gViewerArt.getString("terrain_grass_detail.tga") ); | ||
75 | setDetailTextureID(1, id); | ||
76 | |||
77 | // Rock mountain | ||
78 | id.set( gViewerArt.getString("terrain_mountain_detail.tga") ); | ||
79 | setDetailTextureID(2, id); | ||
80 | |||
81 | // Rock face | ||
82 | id.set( gViewerArt.getString("terrain_rock_detail.tga") ); | ||
83 | setDetailTextureID(3, id); | ||
84 | |||
85 | // Initialize the texture matrix to defaults. | ||
86 | for (S32 i = 0; i < CORNER_COUNT; ++i) | ||
87 | { | ||
88 | mStartHeight[i] = gSavedSettings.getF32("TerrainColorStartHeight"); | ||
89 | mHeightRange[i] = gSavedSettings.getF32("TerrainColorHeightRange"); | ||
90 | } | ||
91 | mTexScaleX = 16.f; | ||
92 | mTexScaleY = 16.f; | ||
93 | mTexturesLoaded = FALSE; | ||
94 | } | ||
95 | |||
96 | |||
97 | LLVLComposition::~LLVLComposition() | ||
98 | { | ||
99 | } | ||
100 | |||
101 | |||
102 | void LLVLComposition::setSurface(LLSurface *surfacep) | ||
103 | { | ||
104 | mSurfacep = surfacep; | ||
105 | } | ||
106 | |||
107 | |||
108 | void LLVLComposition::setDetailTextureID(S32 corner, const LLUUID& id) | ||
109 | { | ||
110 | if(id.isNull()) | ||
111 | { | ||
112 | return; | ||
113 | } | ||
114 | mDetailTextures[corner] = gImageList.getImage(id); | ||
115 | mRawImages[corner] = NULL; | ||
116 | } | ||
117 | |||
118 | BOOL LLVLComposition::generateHeights(const F32 x, const F32 y, | ||
119 | const F32 width, const F32 height) | ||
120 | { | ||
121 | if (!mParamsReady) | ||
122 | { | ||
123 | // All the parameters haven't been set yet (we haven't gotten the message from the sim) | ||
124 | return FALSE; | ||
125 | } | ||
126 | |||
127 | llassert(mSurfacep); | ||
128 | S32 x_begin, y_begin, x_end, y_end; | ||
129 | |||
130 | x_begin = llround( x * mScaleInv ); | ||
131 | y_begin = llround( y * mScaleInv ); | ||
132 | x_end = llround( (x + width) * mScaleInv ); | ||
133 | y_end = llround( (y + width) * mScaleInv ); | ||
134 | |||
135 | if (x_end > mWidth) | ||
136 | { | ||
137 | x_end = mWidth; | ||
138 | } | ||
139 | if (y_end > mWidth) | ||
140 | { | ||
141 | y_end = mWidth; | ||
142 | } | ||
143 | |||
144 | LLVector3d origin_global = from_region_handle(mSurfacep->getRegion()->getHandle()); | ||
145 | |||
146 | // For perlin noise generation... | ||
147 | const F32 slope_squared = 1.5f*1.5f; | ||
148 | const F32 xyScale = 4.9215f; //0.93284f; | ||
149 | const F32 zScale = 4; //0.92165f; | ||
150 | const F32 z_offset = 0.f; | ||
151 | const F32 noise_magnitude = 2.f; // Degree to which noise modulates composition layer (versus | ||
152 | // simple height) | ||
153 | |||
154 | // Heights map into textures as 0-1 = first, 1-2 = second, etc. | ||
155 | // So we need to compress heights into this range. | ||
156 | const S32 NUM_TEXTURES = 4; | ||
157 | |||
158 | const F32 xyScaleInv = (1.f / xyScale); | ||
159 | const F32 zScaleInv = (1.f / zScale); | ||
160 | |||
161 | const F32 inv_width = 1.f/mWidth; | ||
162 | |||
163 | // OK, for now, just have the composition value equal the height at the point. | ||
164 | for (S32 j = y_begin; j < y_end; j++) | ||
165 | { | ||
166 | for (S32 i = x_begin; i < x_end; i++) | ||
167 | { | ||
168 | |||
169 | F32 vec[3]; | ||
170 | F32 vec1[3]; | ||
171 | F32 twiddle; | ||
172 | |||
173 | // Bilinearly interpolate the start height and height range of the textures | ||
174 | F32 start_height = bilinear(mStartHeight[SOUTHWEST], | ||
175 | mStartHeight[SOUTHEAST], | ||
176 | mStartHeight[NORTHWEST], | ||
177 | mStartHeight[NORTHEAST], | ||
178 | i*inv_width, j*inv_width); // These will be bilinearly interpolated | ||
179 | F32 height_range = bilinear(mHeightRange[SOUTHWEST], | ||
180 | mHeightRange[SOUTHEAST], | ||
181 | mHeightRange[NORTHWEST], | ||
182 | mHeightRange[NORTHEAST], | ||
183 | i*inv_width, j*inv_width); // These will be bilinearly interpolated | ||
184 | |||
185 | LLVector3 location(i*mScale, j*mScale, 0.f); | ||
186 | |||
187 | F32 height = mSurfacep->resolveHeightRegion(location) + z_offset; | ||
188 | |||
189 | // Step 0: Measure the exact height at this texel | ||
190 | vec[0] = (F32)(origin_global.mdV[VX]+location.mV[VX])*xyScaleInv; // Adjust to non-integer lattice | ||
191 | vec[1] = (F32)(origin_global.mdV[VY]+location.mV[VY])*xyScaleInv; | ||
192 | vec[2] = height*zScaleInv; | ||
193 | // | ||
194 | // Choose material value by adding to the exact height a random value | ||
195 | // | ||
196 | vec1[0] = vec[0]*(0.2222222222f); | ||
197 | vec1[1] = vec[1]*(0.2222222222f); | ||
198 | vec1[2] = vec[2]*(0.2222222222f); | ||
199 | twiddle = noise2(vec1)*6.5f; // Low freq component for large divisions | ||
200 | |||
201 | twiddle += turbulence2(vec, 2)*slope_squared; // High frequency component | ||
202 | twiddle *= noise_magnitude; | ||
203 | |||
204 | F32 scaled_noisy_height = (height + twiddle - start_height) * F32(NUM_TEXTURES) / height_range; | ||
205 | |||
206 | scaled_noisy_height = llmax(0.f, scaled_noisy_height); | ||
207 | scaled_noisy_height = llmin(3.f, scaled_noisy_height); | ||
208 | *(mDatap + i + j*mWidth) = scaled_noisy_height; | ||
209 | } | ||
210 | } | ||
211 | return TRUE; | ||
212 | } | ||
213 | |||
214 | static const S32 BASE_SIZE = 128; | ||
215 | |||
216 | BOOL LLVLComposition::generateComposition() | ||
217 | { | ||
218 | |||
219 | if (!mParamsReady) | ||
220 | { | ||
221 | // All the parameters haven't been set yet (we haven't gotten the message from the sim) | ||
222 | return FALSE; | ||
223 | } | ||
224 | |||
225 | for (S32 i = 0; i < 4; i++) | ||
226 | { | ||
227 | if (mDetailTextures[i]->getDiscardLevel() < 0) | ||
228 | { | ||
229 | mDetailTextures[i]->setBoostLevel(LLViewerImage::BOOST_TERRAIN); // in case we are at low detail | ||
230 | mDetailTextures[i]->addTextureStats(BASE_SIZE*BASE_SIZE); | ||
231 | return FALSE; | ||
232 | } | ||
233 | if ((mDetailTextures[i]->getDiscardLevel() != 0 && | ||
234 | (mDetailTextures[i]->getWidth() < BASE_SIZE || | ||
235 | mDetailTextures[i]->getHeight() < BASE_SIZE))) | ||
236 | { | ||
237 | S32 width = mDetailTextures[i]->getWidth(0); | ||
238 | S32 height = mDetailTextures[i]->getHeight(0); | ||
239 | S32 min_dim = llmin(width, height); | ||
240 | S32 ddiscard = 0; | ||
241 | while (min_dim > BASE_SIZE && ddiscard < MAX_DISCARD_LEVEL) | ||
242 | { | ||
243 | ddiscard++; | ||
244 | min_dim /= 2; | ||
245 | } | ||
246 | mDetailTextures[i]->setBoostLevel(LLViewerImage::BOOST_TERRAIN); // in case we are at low detail | ||
247 | mDetailTextures[i]->setMinDiscardLevel(ddiscard); | ||
248 | return FALSE; | ||
249 | } | ||
250 | } | ||
251 | |||
252 | return TRUE; | ||
253 | } | ||
254 | |||
255 | BOOL LLVLComposition::generateTexture(const F32 x, const F32 y, | ||
256 | const F32 width, const F32 height) | ||
257 | { | ||
258 | llassert(mSurfacep); | ||
259 | llassert(x >= 0.f); | ||
260 | llassert(y >= 0.f); | ||
261 | |||
262 | LLTimer gen_timer; | ||
263 | |||
264 | /////////////////////////// | ||
265 | // | ||
266 | // Generate raw data arrays for surface textures | ||
267 | // | ||
268 | // | ||
269 | |||
270 | // These have already been validated by generateComposition. | ||
271 | U8* st_data[4]; | ||
272 | |||
273 | for (S32 i = 0; i < 4; i++) | ||
274 | { | ||
275 | if (mRawImages[i].isNull()) | ||
276 | { | ||
277 | // Read back a raw image for this discard level, if it exists | ||
278 | mRawImages[i] = new LLImageRaw; | ||
279 | S32 min_dim = llmin(mDetailTextures[i]->getWidth(0), mDetailTextures[i]->getHeight(0)); | ||
280 | S32 ddiscard = 0; | ||
281 | while (min_dim > BASE_SIZE && ddiscard < MAX_DISCARD_LEVEL) | ||
282 | { | ||
283 | ddiscard++; | ||
284 | min_dim /= 2; | ||
285 | } | ||
286 | if (!mDetailTextures[i]->readBackRaw(ddiscard, mRawImages[i])) | ||
287 | { | ||
288 | llwarns << "Unable to read raw data for terrain detail texture: " << mDetailTextures[i]->getID() << llendl; | ||
289 | mRawImages[i] = NULL; | ||
290 | return FALSE; | ||
291 | } | ||
292 | if (mDetailTextures[i]->getWidth(ddiscard) != BASE_SIZE || | ||
293 | mDetailTextures[i]->getHeight(ddiscard) != BASE_SIZE || | ||
294 | mDetailTextures[i]->getComponents() != 3) | ||
295 | { | ||
296 | LLPointer<LLImageRaw> newraw = new LLImageRaw(BASE_SIZE, BASE_SIZE, 3); | ||
297 | newraw->composite(mRawImages[i]); | ||
298 | mRawImages[i] = newraw; // deletes old | ||
299 | } | ||
300 | } | ||
301 | st_data[i] = mRawImages[i]->getData(); | ||
302 | } | ||
303 | |||
304 | /////////////////////////////////////// | ||
305 | // | ||
306 | // Generate and clamp x/y bounding box. | ||
307 | // | ||
308 | // | ||
309 | |||
310 | S32 x_begin, y_begin, x_end, y_end; | ||
311 | x_begin = (S32)(x * mScaleInv); | ||
312 | y_begin = (S32)(y * mScaleInv); | ||
313 | x_end = llround( (x + width) * mScaleInv ); | ||
314 | y_end = llround( (y + width) * mScaleInv ); | ||
315 | |||
316 | if (x_end > mWidth) | ||
317 | { | ||
318 | llwarns << "x end > width" << llendl; | ||
319 | x_end = mWidth; | ||
320 | } | ||
321 | if (y_end > mWidth) | ||
322 | { | ||
323 | llwarns << "y end > width" << llendl; | ||
324 | y_end = mWidth; | ||
325 | } | ||
326 | |||
327 | |||
328 | /////////////////////////////////////////// | ||
329 | // | ||
330 | // Generate target texture information, stride ratios. | ||
331 | // | ||
332 | // | ||
333 | |||
334 | LLViewerImage *texturep; | ||
335 | U32 tex_width, tex_height, tex_comps; | ||
336 | U32 tex_stride; | ||
337 | F32 tex_x_scalef, tex_y_scalef; | ||
338 | S32 tex_x_begin, tex_y_begin, tex_x_end, tex_y_end; | ||
339 | F32 tex_x_ratiof, tex_y_ratiof; | ||
340 | |||
341 | texturep = mSurfacep->getSTexture(); | ||
342 | tex_width = texturep->getWidth(); | ||
343 | tex_height = texturep->getHeight(); | ||
344 | tex_comps = texturep->getComponents(); | ||
345 | tex_stride = tex_width * tex_comps; | ||
346 | |||
347 | S32 st_comps = 3; | ||
348 | S32 st_width = BASE_SIZE; | ||
349 | S32 st_height = BASE_SIZE; | ||
350 | |||
351 | if (tex_comps != st_comps) | ||
352 | { | ||
353 | llwarns << "Base texture comps != input texture comps" << llendl; | ||
354 | return FALSE; | ||
355 | } | ||
356 | |||
357 | tex_x_scalef = (F32)tex_width / (F32)mWidth; | ||
358 | tex_y_scalef = (F32)tex_height / (F32)mWidth; | ||
359 | tex_x_begin = (S32)((F32)x_begin * tex_x_scalef); | ||
360 | tex_y_begin = (S32)((F32)y_begin * tex_y_scalef); | ||
361 | tex_x_end = (S32)((F32)x_end * tex_x_scalef); | ||
362 | tex_y_end = (S32)((F32)y_end * tex_y_scalef); | ||
363 | |||
364 | tex_x_ratiof = (F32)mWidth*mScale / (F32)tex_width; | ||
365 | tex_y_ratiof = (F32)mWidth*mScale / (F32)tex_height; | ||
366 | |||
367 | LLPointer<LLImageRaw> raw = new LLImageRaw(tex_width, tex_height, tex_comps); | ||
368 | U8 *rawp = raw->getData(); | ||
369 | |||
370 | F32 tex_width_inv = 1.f/tex_width; | ||
371 | F32 tex_height_inv = 1.f/tex_height; | ||
372 | |||
373 | F32 st_x_stride, st_y_stride; | ||
374 | st_x_stride = ((F32)st_width / (F32)mTexScaleX)*((F32)mWidth / (F32)tex_width); | ||
375 | st_y_stride = ((F32)st_height / (F32)mTexScaleY)*((F32)mWidth / (F32)tex_height); | ||
376 | |||
377 | llassert(st_x_stride > 0.f); | ||
378 | llassert(st_y_stride > 0.f); | ||
379 | //////////////////////////////// | ||
380 | // | ||
381 | // Iterate through the target texture, striding through the | ||
382 | // subtextures and interpolating appropriately. | ||
383 | // | ||
384 | // | ||
385 | |||
386 | F32 sti, stj; | ||
387 | S32 st_offset; | ||
388 | sti = (tex_x_begin * st_x_stride) - st_width*(llfloor((tex_x_begin * st_x_stride)/st_width)); | ||
389 | stj = (tex_y_begin * st_y_stride) - st_height*(llfloor((tex_y_begin * st_y_stride)/st_height)); | ||
390 | |||
391 | st_offset = (llfloor(stj * st_width) + llfloor(sti)) * st_comps; | ||
392 | for (S32 j = tex_y_begin; j < tex_y_end; j++) | ||
393 | { | ||
394 | U32 offset = j * tex_stride + tex_x_begin * tex_comps; | ||
395 | sti = (tex_x_begin * st_x_stride) - st_width*((U32)(tex_x_begin * st_x_stride)/st_width); | ||
396 | for (S32 i = tex_x_begin; i < tex_x_end; i++) | ||
397 | { | ||
398 | S32 tex0, tex1; | ||
399 | F32 composition = getValueScaled(i*tex_x_ratiof, j*tex_y_ratiof); | ||
400 | |||
401 | tex0 = llfloor( composition ); | ||
402 | tex0 = llclamp(tex0, 0, 3); | ||
403 | composition -= tex0; | ||
404 | tex1 = tex0 + 1; | ||
405 | tex1 = llclamp(tex1, 0, 3); | ||
406 | |||
407 | F32 xy_int_i, xy_int_j; | ||
408 | |||
409 | xy_int_i = i * tex_width_inv; | ||
410 | xy_int_j = j * tex_height_inv; | ||
411 | |||
412 | st_offset = (lltrunc(sti) + lltrunc(stj)*st_width) * st_comps; | ||
413 | for (U32 k = 0; k < tex_comps; k++) | ||
414 | { | ||
415 | // Linearly interpolate based on composition. | ||
416 | F32 a = *(st_data[tex0] + st_offset); | ||
417 | F32 b = *(st_data[tex1] + st_offset); | ||
418 | rawp[ offset ] = (U8)lltrunc( a + composition * (b - a) ); | ||
419 | offset++; | ||
420 | st_offset++; | ||
421 | } | ||
422 | |||
423 | sti += st_x_stride; | ||
424 | if (sti >= st_width) | ||
425 | { | ||
426 | sti -= st_width; | ||
427 | } | ||
428 | } | ||
429 | |||
430 | stj += st_y_stride; | ||
431 | if (stj >= st_height) | ||
432 | { | ||
433 | stj -= st_height; | ||
434 | } | ||
435 | } | ||
436 | |||
437 | texturep->setSubImage(raw, tex_x_begin, tex_y_begin, tex_x_end - tex_x_begin, tex_y_end - tex_y_begin); | ||
438 | LLSurface::sTextureUpdateTime += gen_timer.getElapsedTimeF32(); | ||
439 | LLSurface::sTexelsUpdated += (tex_x_end - tex_x_begin) * (tex_y_end - tex_y_begin); | ||
440 | |||
441 | for (S32 i = 0; i < 4; i++) | ||
442 | { | ||
443 | // Un-boost detatil textures (will get re-boosted if rendering in high detail) | ||
444 | mDetailTextures[i]->setBoostLevel(LLViewerImage::BOOST_NONE); | ||
445 | mDetailTextures[i]->setMinDiscardLevel(MAX_DISCARD_LEVEL + 1); | ||
446 | } | ||
447 | |||
448 | return TRUE; | ||
449 | } | ||
450 | |||
451 | LLUUID LLVLComposition::getDetailTextureID(S32 corner) | ||
452 | { | ||
453 | return mDetailTextures[corner]->getID(); | ||
454 | } | ||
455 | |||
456 | LLViewerImage* LLVLComposition::getDetailTexture(S32 corner) | ||
457 | { | ||
458 | return mDetailTextures[corner]; | ||
459 | } | ||
460 | |||
461 | F32 LLVLComposition::getStartHeight(S32 corner) | ||
462 | { | ||
463 | return mStartHeight[corner]; | ||
464 | } | ||
465 | |||
466 | void LLVLComposition::setStartHeight(S32 corner, const F32 start_height) | ||
467 | { | ||
468 | mStartHeight[corner] = start_height; | ||
469 | } | ||
470 | |||
471 | F32 LLVLComposition::getHeightRange(S32 corner) | ||
472 | { | ||
473 | return mHeightRange[corner]; | ||
474 | } | ||
475 | |||
476 | void LLVLComposition::setHeightRange(S32 corner, const F32 range) | ||
477 | { | ||
478 | mHeightRange[corner] = range; | ||
479 | } | ||