diff options
Diffstat (limited to '')
-rw-r--r-- | OpenSim/Region/Framework/Interfaces/ITerrainChannel.cs | 8 | ||||
-rw-r--r-- | OpenSim/Region/Framework/Scenes/TerrainChannel.cs | 142 |
2 files changed, 150 insertions, 0 deletions
diff --git a/OpenSim/Region/Framework/Interfaces/ITerrainChannel.cs b/OpenSim/Region/Framework/Interfaces/ITerrainChannel.cs index f660b8d..78db02a 100644 --- a/OpenSim/Region/Framework/Interfaces/ITerrainChannel.cs +++ b/OpenSim/Region/Framework/Interfaces/ITerrainChannel.cs | |||
@@ -59,5 +59,13 @@ namespace OpenSim.Region.Framework.Interfaces | |||
59 | void LoadFromXmlString(string data); | 59 | void LoadFromXmlString(string data); |
60 | // Merge some terrain into this channel | 60 | // Merge some terrain into this channel |
61 | void Merge(ITerrainChannel newTerrain, Vector3 displacement, float radianRotation, Vector2 rotationDisplacement); | 61 | void Merge(ITerrainChannel newTerrain, Vector3 displacement, float radianRotation, Vector2 rotationDisplacement); |
62 | |||
63 | /// </summary> | ||
64 | /// <param name="newTerrain"></param> | ||
65 | /// <param name="displacement"><x, y, z></param> | ||
66 | /// <param name="rotationDegrees"></param> | ||
67 | /// <param name="boundingOrigin"><x, y></param> | ||
68 | /// <param name="boundingSize"><x, y></param> | ||
69 | void MergeWithBounding(ITerrainChannel newTerrain, Vector3 displacement, float rotationDegrees, Vector2 boundingOrigin, Vector2 boundingSize); | ||
62 | } | 70 | } |
63 | } | 71 | } |
diff --git a/OpenSim/Region/Framework/Scenes/TerrainChannel.cs b/OpenSim/Region/Framework/Scenes/TerrainChannel.cs index 506ad24..ce5586c 100644 --- a/OpenSim/Region/Framework/Scenes/TerrainChannel.cs +++ b/OpenSim/Region/Framework/Scenes/TerrainChannel.cs | |||
@@ -283,6 +283,148 @@ namespace OpenSim.Region.Framework.Scenes | |||
283 | } | 283 | } |
284 | } | 284 | } |
285 | 285 | ||
286 | /// <summary> | ||
287 | /// A new version of terrain merge that processes the terrain in a specific order and corrects the problems with rotated terrains | ||
288 | /// having 'holes' in that need to be smoothed. The correct way to rotate something is to iterate over the target, taking data from | ||
289 | /// the source, not the other way around. This ensures that the target has no holes in it. | ||
290 | /// The processing order of an incoming terrain is: | ||
291 | /// 1. Apply rotation | ||
292 | /// 2. Apply bounding rectangle | ||
293 | /// 3. Apply displacement | ||
294 | /// rotationCenter is no longer needed and has been discarded. | ||
295 | /// </summary> | ||
296 | /// <param name="newTerrain"></param> | ||
297 | /// <param name="displacement"><x, y, z></param> | ||
298 | /// <param name="rotationDegrees"></param> | ||
299 | /// <param name="boundingOrigin"><x, y></param> | ||
300 | /// <param name="boundingSize"><x, y></param> | ||
301 | public void MergeWithBounding(ITerrainChannel newTerrain, Vector3 displacement, float rotationDegrees, Vector2 boundingOrigin, Vector2 boundingSize) | ||
302 | { | ||
303 | m_log.DebugFormat("{0} MergeWithBounding: inSize=<{1},{2}>, rot={3}, boundingOrigin={4}, boundingSize={5}, disp={6}, outSize=<{7},{8}>", | ||
304 | LogHeader, newTerrain.Width, newTerrain.Height, rotationDegrees, boundingOrigin.ToString(), | ||
305 | boundingSize.ToString(), displacement, m_terrainData.SizeX, m_terrainData.SizeY); | ||
306 | |||
307 | // get the size of the incoming terrain | ||
308 | int baseX = newTerrain.Width; | ||
309 | int baseY = newTerrain.Height; | ||
310 | |||
311 | // create an intermediate terrain map that is 25% bigger on each side that we can work with to handle rotation | ||
312 | int offsetX = baseX / 4; // the original origin will now be at these coordinates so now we can have imaginary negative coordinates ;) | ||
313 | int offsetY = baseY / 4; | ||
314 | int tmpX = baseX + baseX / 2; | ||
315 | int tmpY = baseY + baseY / 2; | ||
316 | int centreX = tmpX / 2; | ||
317 | int centreY = tmpY / 2; | ||
318 | TerrainData terrain_tmp = new HeightmapTerrainData(tmpX, tmpY, (int)Constants.RegionHeight); | ||
319 | for (int xx = 0; xx < tmpX; xx++) | ||
320 | for (int yy = 0; yy < tmpY; yy++) | ||
321 | terrain_tmp[xx, yy] = -65535f; //use this height like an 'alpha' mask channel | ||
322 | |||
323 | double radianRotation = Math.PI * rotationDegrees / 180f; | ||
324 | double cosR = Math.Cos(radianRotation); | ||
325 | double sinR = Math.Sin(radianRotation); | ||
326 | if (rotationDegrees < 0f) rotationDegrees += 360f; //-90=270 -180=180 -270=90 | ||
327 | |||
328 | // So first we apply the rotation to the incoming terrain, storing the result in terrain_tmp | ||
329 | // We special case orthogonal rotations for accuracy because even using double precision math, Math.Cos(90 degrees) is never fully 0 | ||
330 | // and we can never rotate around a centre 'pixel' because the 'bitmap' size is always even | ||
331 | |||
332 | int x, y, sx, sy; | ||
333 | for (y = 0; y <= tmpY; y++) | ||
334 | { | ||
335 | for (x = 0; x <= tmpX; x++) | ||
336 | { | ||
337 | if (rotationDegrees == 0f) | ||
338 | { | ||
339 | sx = x - offsetX; | ||
340 | sy = y - offsetY; | ||
341 | } | ||
342 | else if (rotationDegrees == 90f) | ||
343 | { | ||
344 | sx = y - offsetX; | ||
345 | sy = tmpY - 1 - x - offsetY; | ||
346 | } | ||
347 | else if (rotationDegrees == 180f) | ||
348 | { | ||
349 | sx = tmpX - 1 - x - offsetX; | ||
350 | sy = tmpY - 1 - y - offsetY; | ||
351 | } | ||
352 | else if (rotationDegrees == 270f) | ||
353 | { | ||
354 | sx = tmpX - 1 - y - offsetX; | ||
355 | sy = x - offsetY; | ||
356 | } | ||
357 | else | ||
358 | { | ||
359 | // arbitary rotation: hmmm should I be using (centreX - 0.5) and (centreY - 0.5) and round cosR and sinR to say only 5 decimal places? | ||
360 | sx = centreX + (int)Math.Round((((double)x - centreX) * cosR) + (((double)y - centreY) * sinR)) - offsetX; | ||
361 | sy = centreY + (int)Math.Round((((double)y - centreY) * cosR) - (((double)x - centreX) * sinR)) - offsetY; | ||
362 | } | ||
363 | |||
364 | if (sx >= 0 && sx < baseX && sy >= 0 && sy < baseY) | ||
365 | { | ||
366 | try | ||
367 | { | ||
368 | terrain_tmp[x, y] = (float)newTerrain[sx, sy]; | ||
369 | } | ||
370 | catch (Exception) //just in case we've still not taken care of every way the arrays might go out of bounds! ;) | ||
371 | { | ||
372 | m_log.DebugFormat("{0} MergeWithBounding - Rotate: Out of Bounds sx={1} sy={2} dx={3} dy={4}", sx, sy, x, y); | ||
373 | } | ||
374 | } | ||
375 | } | ||
376 | } | ||
377 | |||
378 | // We could also incorporate the next steps, bounding-rectangle and displacement in the loop above, but it's simpler to visualise if done separately | ||
379 | // and will also make it much easier when later I want the option for maybe a circular or oval bounding shape too ;). | ||
380 | |||
381 | int newX = m_terrainData.SizeX; | ||
382 | int newY = m_terrainData.SizeY; | ||
383 | // displacement is relative to <0,0> in the destination region and defines where the origin of the data selected by the bounding-rectangle is placed | ||
384 | int dispX = (int)Math.Floor(displacement.X); | ||
385 | int dispY = (int)Math.Floor(displacement.Y); | ||
386 | |||
387 | // startX/Y and endX/Y are coordinates in bitmap_tmp | ||
388 | int startX = (int)Math.Floor(boundingOrigin.X) + offsetX; | ||
389 | if (startX > tmpX) startX = tmpX; | ||
390 | if (startX < 0) startX = 0; | ||
391 | int startY = (int)Math.Floor(boundingOrigin.Y) + offsetY; | ||
392 | if (startY > tmpY) startY = tmpY; | ||
393 | if (startY < 0) startY = 0; | ||
394 | |||
395 | int endX = (int)Math.Floor(boundingOrigin.X + boundingSize.X) + offsetX; | ||
396 | if (endX > tmpX) endX = tmpX; | ||
397 | if (endX < 0) endX = 0; | ||
398 | int endY = (int)Math.Floor(boundingOrigin.Y + boundingSize.Y) + offsetY; | ||
399 | if (endY > tmpY) endY = tmpY; | ||
400 | if (endY < 0) endY = 0; | ||
401 | |||
402 | //m_log.DebugFormat("{0} MergeWithBounding: inSize=<{1},{2}>, disp=<{3},{4}> rot={5}, offset=<{6},{7}>, boundingStart=<{8},{9}>, boundingEnd=<{10},{11}>, cosR={12}, sinR={13}, outSize=<{14},{15}>", LogHeader, | ||
403 | // baseX, baseY, dispX, dispY, radianRotation, offsetX, offsetY, startX, startY, endX, endY, cosR, sinR, newX, newY); | ||
404 | |||
405 | int dx, dy; | ||
406 | for (y = startY; y < endY; y++) | ||
407 | { | ||
408 | for (x = startX; x < endX; x++) | ||
409 | { | ||
410 | dx = x - startX + dispX; | ||
411 | dy = y - startY + dispY; | ||
412 | if (dx >= 0 && dx < newX && dy >= 0 && dy < newY) | ||
413 | { | ||
414 | try | ||
415 | { | ||
416 | float newHeight = (float)terrain_tmp[x, y]; //use 'alpha' mask | ||
417 | if (newHeight != -65535f) m_terrainData[dx, dy] = newHeight + displacement.Z; | ||
418 | } | ||
419 | catch (Exception) //just in case we've still not taken care of every way the arrays might go out of bounds! ;) | ||
420 | { | ||
421 | m_log.DebugFormat("{0} MergeWithBounding - Bound & Displace: Out of Bounds sx={1} sy={2} dx={3} dy={4}", x, y, dx, dy); | ||
422 | } | ||
423 | } | ||
424 | } | ||
425 | } | ||
426 | } | ||
427 | |||
286 | #endregion | 428 | #endregion |
287 | 429 | ||
288 | public TerrainChannel Copy() | 430 | public TerrainChannel Copy() |