Ландшафт с Геометрией Clipmaps в XNA (Перевод) Часть 3

Оригинал: http://www.ziggyware.com/readarticle.php?article_id=220

Почти все. Следующий шаг – логика обновления индексов. Как я говорил прежде, мы должны восстановить полностью массив индексов, потому что наш массив вершин постоянно изменяет данные и исходные данные также всегда меняется. Таким образом, мы должны повторно формировать индексы, чтобы представить правильно ландшафт.

Чтобы обновить уровень, мы должны знать вложенные уровни данного уровня. У уровня L=0 нет внутреннего уровня, но у всех остальных они имеются. Все остальные скрывают вершины, которые определяют область, покрываемую внутренним уровнем, поэтому мы не используем целый массив индексов. При каждом обновлении мы начинаем с нуля массива индексов и увеличиваем счетчик каждый раз, когда добавляем другой видимый индекс к массиву. Все вершины вокруг невидимой области, вложенной области, будут объединены в окружность ландшафта, которая создает большую треугольную полосу. Треугольные полосы – это самый эффективный способ изображения большого количество треугольников. Кроме того, помните, что я сказал разрезать уровень на куски. Вот точная идея, как мы сделаем это:

Внешний уровень – разрезается на 12 блоков MxM, вот почему нам нужно значение М. Это не покроет всю окружность, поэтому нам нужно 4 фиксировнных блока. Мы видели, как уровни перемещаются друг за другом. В то время как одни уровни стоят, а внутренний уровень перемещается, утечка должна быть закрыта. Это можно сделать с помощью двух полос, которые определяют область формы L, называемой внутренней отделкой. Внутренний уровень, у которого нет вложенного уровня, должен закрыть внутреннюю область. Это делается с помощью 4х других блоков.

Вот картина, проясняющая это.

В то же самое время мы хотим немного оптимизировать технику. Мы не хотим добавлять блоки, которые не видимы. Поэтому мы передаем границы усеченного конуса в команду перерисовки и делаем простой тест для границ. Это может быть оптимизировано более эффективным тестом на границы, таким как AABB, но тест, который я использую, работает хорошо, и его легко читать.

Треугольники определяются большой треугольной полосой. Они заполняются ряд за рядом. Каждый ряд должен добавить некоторые невидимые фиктивные треугольники, чтобы соединить конец ряда с началом следующего. Изображение показывает порядок обхода вершин и где фиктивные треугольники расположены. Код объясняет, как мы добавляем эти невидимые треугольники.

#region Update Indices 
private BoundingFrustum frustum; 
private BoundingBox box; 
/// <summary> 
/// Updates the whole indexarray. 
/// </summary> 
/// <param name="nextFinerLevel"></param> 
/// <param name="frustum"></param> 
public void UpdateIndices(Clipmap nextFinerLevel, BoundingFrustum frustum) 
{ 
 this.frustum = frustum; 
 // set the stripindex to zero. We start count vertices from here. 
 // The stripindex will tell us how much of the array is used. 
 stripIndex = 0; 
 #region Fill MxM Blocks 
 // MxM Block 1 
 Fill_Block(clipRegion.Left, 
 clipRegion.Left + Mm1G, 
 clipRegion.Top, 
 clipRegion.Top + Mm1G); 
 // MxM Block 2 
 Fill_Block(clipRegion.Left + Mm1G, 
 clipRegion.Left + 2 * Mm1G, 
 clipRegion.Top, 
 clipRegion.Top + Mm1G); 
 // MxM Block 3 
 Fill_Block(clipRegion.Right - 2 * Mm1G, 
 clipRegion.Right - Mm1G, 
 clipRegion.Top, 
 clipRegion.Top + Mm1G); 
 // MxM Block 4 
 Fill_Block(clipRegion.Right - Mm1G, 
 clipRegion.Right, 
 clipRegion.Top, 
 clipRegion.Top + Mm1G); 
 // MxM Block 5 
 Fill_Block(clipRegion.Left, 
 clipRegion.Left + Mm1G, 
 clipRegion.Top + Mm1G, 
 clipRegion.Top + 2 * Mm1G); 
 // MxM Block 6 
 Fill_Block(clipRegion.Right - Mm1G, 
 clipRegion.Right, 
 clipRegion.Top + Mm1G, 
 clipRegion.Top + 2 * Mm1G); 
 // MxM Block 7 
 Fill_Block(clipRegion.Left, 
 clipRegion.Left + Mm1G, 
 clipRegion.Bottom - 2 * Mm1G, 
 clipRegion.Bottom - Mm1G); 
 // MxM Block 8 
 Fill_Block(clipRegion.Right - Mm1G, 
 clipRegion.Right, 
 clipRegion.Bottom - 2 * Mm1G, 
 clipRegion.Bottom - Mm1G); 
 // MxM Block 9 
 Fill_Block(clipRegion.Left, 
 clipRegion.Left + Mm1G, 
 clipRegion.Bottom - Mm1G, 
 clipRegion.Bottom); 
 // MxM Block 10 
 Fill_Block(clipRegion.Left + Mm1G, 
 clipRegion.Left + 2 * Mm1G, 
 clipRegion.Bottom - Mm1G, 
 clipRegion.Bottom); 
 // MxM Block 11 
 Fill_Block(clipRegion.Right - 2 * Mm1G, 
 clipRegion.Right - Mm1G, 
 clipRegion.Bottom - Mm1G, 
 clipRegion.Bottom); 
 // MxM Block 12 
 Fill_Block(clipRegion.Right - Mm1G, 
 clipRegion.Right, 
 clipRegion.Bottom - Mm1G, 
 clipRegion.Bottom); 
 #endregion 
 #region Fill Fixup Blocks 
 // Fixup Top 
 Fill_Block(clipRegion.Left + 2 * Mm1G, 
 clipRegion.Left + 2 * Mm1G + G2, 
 clipRegion.Top, 
 clipRegion.Top + Mm1G); 
 // Fixup Left 
 Fill_Block(clipRegion.Left, 
 clipRegion.Left + Mm1G, 
 clipRegion.Top + 2 * Mm1G, 
 clipRegion.Top + 2 * Mm1G + G2); 
 // Fixup Right 
 Fill_Block(clipRegion.Right - Mm1G, 
 clipRegion.Right, 
 clipRegion.Top + 2 * Mm1G, 
 clipRegion.Top + 2 * Mm1G + G2); 
 // Fixup Bottom 
 Fill_Block(clipRegion.Left + 2 * Mm1G, 
 clipRegion.Left + 2 * Mm1G + G2, 
 clipRegion.Bottom - Mm1G, 
 clipRegion.Bottom); 
 #endregion 
 #region Fill Interior Trim 
 if (nextFinerLevel != null) 
 { 
 if ((nextFinerLevel.clipRegion.X - clipRegion.X) / G == M) 
 { 
 if ((nextFinerLevel.clipRegion.Y - clipRegion.Y) / G == M) 
 { 
 // Upper Left L Shape 
 // Up 
 Fill_Block(clipRegion.Left + Mm1G, 
 clipRegion.Right - Mm1G, 
 clipRegion.Top + Mm1G, 
 clipRegion.Top + Mm1G + G); 
 // Left 
 Fill_Block(clipRegion.Left + Mm1G, 
 clipRegion.Left + Mm1G + G, 
 clipRegion.Top + Mm1G + G, 
 clipRegion.Bottom - Mm1G); 
 } 
 else 
 { 
 // Lower Left L Shape 
 // Left 
 Fill_Block(clipRegion.Left + Mm1G, 
 clipRegion.Left + Mm1G + G, 
 clipRegion.Top + Mm1G, 
 clipRegion.Bottom - Mm1G - G); 
 // Bottom 
 Fill_Block(clipRegion.Left + Mm1G, 
 clipRegion.Right - Mm1G, 
 clipRegion.Bottom - Mm1G - G, 
 clipRegion.Bottom - Mm1G); 
 } 
 } 
 else 
 { 
 if ((nextFinerLevel.clipRegion.Y - clipRegion.Y) / G == M) 
 { 
 // Upper Right L Shape 
 // Up 
 Fill_Block(clipRegion.Left + Mm1G, 
 clipRegion.Right - Mm1G, 
 clipRegion.Top + Mm1G, 
 clipRegion.Top + Mm1G + G); 
 // Right 
 Fill_Block(clipRegion.Right - Mm1G - G, 
 clipRegion.Right - Mm1G, 
 clipRegion.Top + Mm1G + G, 
 clipRegion.Bottom - Mm1G); 
 } 
 else 
 { 
 // Lower Right L Shape 
 // Right 
 Fill_Block(clipRegion.Right - Mm1G - G, 
 clipRegion.Right - Mm1G, 
 clipRegion.Top + Mm1G, 
 clipRegion.Bottom - Mm1G - G); 
 // Bottom 
 Fill_Block(clipRegion.Left + Mm1G, 
 clipRegion.Right - Mm1G, 
 clipRegion.Bottom - Mm1G - G, 
 clipRegion.Bottom - Mm1G); 
 } 
 } 
 } 
 #endregion 
 #region Fill Fine Inner Level 
 if (nextFinerLevel == null) 
 { 
 Fill_Block(clipRegion.Left + Mm1G, 
 clipRegion.Left + Mm1G + N / 2, 
 clipRegion.Top + Mm1G, 
 clipRegion.Top + Mm1G + N / 2); 
 Fill_Block(clipRegion.Left + Mm1G + N / 2, 
 clipRegion.Right - Mm1G, 
 clipRegion.Top + Mm1G, 
 clipRegion.Top + Mm1G + N / 2); 
 Fill_Block(clipRegion.Left + Mm1G, 
 clipRegion.Left + Mm1G + N / 2, 
 clipRegion.Top + Mm1G + N / 2, 
 clipRegion.Bottom - Mm1G); 
 Fill_Block(clipRegion.Left + Mm1G + N / 2, 
 clipRegion.Right - Mm1G, 
 clipRegion.Top + Mm1G + N / 2, 
 clipRegion.Bottom - Mm1G); 
 } 
 #endregion 
} 
/// <summary> 
/// Fills a specified area to indexarray. This will be added only after 
/// a bounding test pass. 
/// </summary> 
/// <param name="left"></param> 
/// <param name="right"></param> 
/// <param name="top"></param> 
/// <param name="bot"></param> 
private void Fill_Block(int left, int right, int top, int bot) 
{ 
 // Setup the boundingbox of the block to fill. 
 // The lowest value is zero, the highest is the scalesize. 
 box.Min.X = left; 
 box.Min.Y = 0; 
 box.Min.Z = top; 
 box.Max.X = right; 
 box.Max.Y = S; 
 box.Max.Z = bot; 
 if (frustum.Contains(box) != ContainmentType.Disjoint) 
 { 
 // Same moduloprocedure as when we updated the vertices. 
 // Maps the terrainposition to arrayposition. 
 left = (left / G) % N; 
 right = (right / G) % N; 
 top = (top / G) % N; 
 bot = (bot / G) % N; 
 left += left < 0 ? N : 0; 
 right += right < 0 ? N : 0; 
 top += top < 0 ? N : 0; 
 bot += bot < 0 ? N : 0; 
 // Now fill the block. 
 if (bot < top) 
 { 
 // Bottom border is positioned somwhere over the top border, 
 // we have a wrapover so we must split up the update in two parts. 
 // Go from top border to the end of the array and update every row 
 for (int z = top; z <= N - 2; z++) 
 { 
 FillRow(left, right, z, z + 1); 
 } 
 // Update the wrapover row 
 FillRow(left, right, N - 1, 0); 
 // Go from arraystart to the bottom border and update every row. 
 for (int z = 0; z <= bot - 1; z++) 
 { 
 FillRow(left, right, z, z + 1); 
 } 
 } 
 else 
 { 
 // Top boarder is over the bottom boarder. Update from top to bottom. 
 for (int z = top; z <= bot - 1; z++) 
 { 
 FillRow(left, right, z, z + 1); 
 } 
 } 
 } 
} 
/// <summary> 
/// Fills a strip of triangles that can be build between vertices row Zn and Zn1. 
/// </summary> 
/// <param name="x0">Start x-coordinate</param> 
/// <param name="xn">End x-coordinate</param> 
/// <param name="zn">Row n</param> 
/// <param name="zn1">Row n + 1</param> 
private void FillRow(int x0, int xn, int zn, int zn1) 
{ 
 // Rows are made of trianglestrips. All rows together build up a big single 
 // trianglestrip. The probloem is when a row ends, it spans a triangle to the 
 // start of the next row. We must hide that triangles. Therefore we add two 
 // dummy indices, Twice the starting index and twice the ending index. This 
 // will result in invisible triangles because when a triangle has two vertices 
 // that are at exactly the same place, there is no area that the triangle can 
 // cover. So four triangles between two rows look like this: 
 // (prev, END, END) (END, END, START') (END, START', START') and (START', START', next) 
 // so we have four invisible triangles but all rows in a single trianglestrip. 
 addIndex(x0, zn); // "START" dummyindex 
 if (x0 <= xn) 
 { 
 for (int x = x0; x <= xn; x++) 
 { 
 addIndex(x, zn); 
 addIndex(x, zn1); 
 } 
 } 
 else 
 { 
 for (int x = x0; x <= N - 1; x++) 
 { 
 addIndex(x, zn); 
 addIndex(x, zn1); 
 } 
 for (int x = 0; x <= xn; x++) 
 { 
 addIndex(x, zn); 
 addIndex(x, zn1); 
 } 
 } 
 addIndex(xn, zn1); // "END" dummyindex 
} 
/// <summary> 
/// Adds a specific index to indexarray. 
/// </summary> 
/// <param name="x"></param> 
/// <param name="z"></param> 
private void addIndex(int x, int z) 
{ 
 // calculate the index 
 int i = x + z * N; 
 // add the index and increment counter. 
 indices[stripIndex++] = (short)i; 
} 
#endregion 
Реклама
Запись опубликована в рубрике Uncategorized. Добавьте в закладки постоянную ссылку.

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s