/// <summary> /// Block から最大 LOD の InterBlock を作成します。 /// </summary> /// <param name="block">Block。</param> /// <returns>生成された InterBlock。</returns> static InterBlock CreateMaxDetailLevelInterBlock(Block block) { var interBlock = new InterBlock(); // 最大 LOD として作成します。 interBlock.GridSize = MaxLodGridSize; // Block の Material をそのままコピーします。 interBlock.Materials = new List <Material>(block.Materials.Count); foreach (var material in block.Materials) { interBlock.Materials.Add(material); } // Block の Element をそのままコピーします。 interBlock.Elements = new InterElementCollection(); foreach (var element in block.Elements) { interBlock.Elements.Add(element); } // 最大 LOD の Element サイズを設定します。 interBlock.ElementSize = MaxLodElementSize; return(interBlock); }
/// <summary> /// InterBlockMesh を生成します。 /// </summary> /// <param name="block">Block。</param> /// <param name="lodSize">LOD のサイズ。</param> /// <returns>生成された InterBlockMesh。</returns> public static InterBlockMesh InterBlockMesh(Block block, int lodCount) { if (block == null) { throw new ArgumentNullException("block"); } if (lodCount < 1 || InterBlock.MaxLodCount < lodCount) { throw new ArgumentOutOfRangeException("lodCount"); } // 中間データを作成します。 var interBlocks = InterBlock.CreateInterBlock(block, lodCount); // 中間データから InterBlockMesh を作成します。 return(Create(interBlocks)); }
/// <summary> /// 指定された数の LOD でそれぞれの LOD を持つ InterBlock の配列を生成します。 /// </summary> /// <param name="block">Block。</param> /// <param name="lodCount">LOD の数。</param> /// <returns>生成された InterBlock の配列。</returns> public static InterBlock[] CreateInterBlock(Block block, int lodCount) { if (lodCount < 1 || MaxLodCount < lodCount) { throw new ArgumentOutOfRangeException("lodCount"); } var interBlocks = new InterBlock[lodCount]; // インデックス 0 は常に最大 LOD です。 interBlocks[0] = CreateMaxDetailLevelInterBlock(block); // 要求された分の下位 LOD を生成します。 for (int i = 1; i < lodCount; i++) { interBlocks[i] = CreateLowDetailLevelInterBlock(interBlocks[i - 1]); } return(interBlocks); }
/// <summary> /// InterBlockMesh を生成します。 /// </summary> /// <param name="lodBlocks">各 LOD の InterBlock を要素とした配列。</param> /// <returns>生成された BlockMesh。</returns> static InterBlockMesh Create(InterBlock[] lodBlocks) { // InterBlockMesh を生成します。 var mesh = new InterBlockMesh(); // InterBlockEffect を生成します。 // LOD 間で Material は共有しているので、最大 LOD の Material から生成します。 mesh.MeshMaterials = new BlockMeshMaterial[lodBlocks[0].Materials.Count]; for (int i = 0; i < mesh.MeshMaterials.Length; i++) { var block = lodBlocks[0]; mesh.MeshMaterials[i] = new BlockMeshMaterial { DiffuseColor = block.Materials[i].DiffuseColor.ToVector3(), EmissiveColor = block.Materials[i].EmissiveColor.ToVector3(), SpecularColor = block.Materials[i].SpecularColor.ToVector3(), SpecularPower = block.Materials[i].SpecularPower }; } // 実際に必要となる LOD 数をもとめます。 int actualLodCount = 0; for (int lod = 0; lod < lodBlocks.Length; lod++) { // 要素数 0 の InterBlock は、それ以上粒度を荒くできなかったことを表します。 if (lodBlocks[lod].Elements.Count == 0) break; actualLodCount++; } // 実際の LOD 数の分だけ InterBlockMeshLod 領域を確保します。 mesh.MeshLods = new InterBlockMeshLod[actualLodCount]; var meshPartVS = new VertexSource<VertexPositionNormal, ushort>(); // LOD ごとに InterBlockMeshPart を生成します。 for (int lod = 0; lod < actualLodCount; lod++) { var block = lodBlocks[lod]; // Element を分類します。 var elementClassifier = ElementClassifier.Classify(block.Elements); var cubeSurfaceVS = cubeSurfaceVertexSourceMap[block.ElementSize]; int meshPartCount = elementClassifier.Parts.Count; var meshLod = new InterBlockMeshLod { MeshParts = new InterBlockMeshPart[meshPartCount] }; mesh.MeshLods[lod] = meshLod; // InterBlockMeshPart を生成して登録します。 for (int i = 0; i < meshPartCount; i++) { var part = elementClassifier.Parts[i]; // 頂点データを作成します。 meshPartVS.Clear(); MakeMeshPartVertexSource(meshPartVS, part, cubeSurfaceVS, block.ElementSize); // InterBlockMeshPart を生成します。 meshLod.MeshParts[i] = new InterBlockMeshPart { MeshMaterialIndex = part.MaterialIndex, Vertices = meshPartVS.Vertices.ToArray(), Indices = meshPartVS.Indices.ToArray() }; } } return mesh; }
/// <summary> /// 指定された数の LOD でそれぞれの LOD を持つ InterBlock の配列を生成します。 /// </summary> /// <param name="block">Block。</param> /// <param name="lodCount">LOD の数。</param> /// <returns>生成された InterBlock の配列。</returns> public static InterBlock[] CreateInterBlock(Block block, int lodCount) { if (lodCount < 1 || MaxLodCount < lodCount) throw new ArgumentOutOfRangeException("lodCount"); var interBlocks = new InterBlock[lodCount]; // インデックス 0 は常に最大 LOD です。 interBlocks[0] = CreateMaxDetailLevelInterBlock(block); // 要求された分の下位 LOD を生成します。 for (int i = 1; i < lodCount; i++) interBlocks[i] = CreateLowDetailLevelInterBlock(interBlocks[i - 1]); return interBlocks; }
/// <summary> /// Block から最大 LOD の InterBlock を作成します。 /// </summary> /// <param name="block">Block。</param> /// <returns>生成された InterBlock。</returns> static InterBlock CreateMaxDetailLevelInterBlock(Block block) { var interBlock = new InterBlock(); // 最大 LOD として作成します。 interBlock.GridSize = MaxLodGridSize; // Block の Material をそのままコピーします。 interBlock.Materials = new List<Material>(block.Materials.Count); foreach (var material in block.Materials) interBlock.Materials.Add(material); // Block の Element をそのままコピーします。 interBlock.Elements = new InterElementCollection(); foreach (var element in block.Elements) interBlock.Elements.Add(element); // 最大 LOD の Element サイズを設定します。 interBlock.ElementSize = MaxLodElementSize; return interBlock; }
/// <summary> /// 指定された InterBlock の LOD の 1 レベル下位の詳細情報を持つ InterBlock を作成します。 /// 生成可能な最小の詳細情報を持つ InterBlock のグリッド サイズは 2 です。 /// </summary> /// <param name="highBlock">生成する InterBlock の 1 レベル上位の詳細情報を持つ InterBlock。</param> /// <returns>生成された InterBlock。</returns> static InterBlock CreateLowDetailLevelInterBlock(InterBlock highBlock) { if (highBlock.GridSize == 2) throw new ArgumentException("A specified InterBlock has a minimum LOD."); var interBlock = new InterBlock(); // 上位の半分のグリッド数で作成します。 interBlock.GridSize = highBlock.GridSize / 2; interBlock.Materials = highBlock.Materials; interBlock.Elements = new InterElementCollection(); // グリッド位置の最大と最小を計算します。 int maxGrid = interBlock.GridSize / 2; int minGrid = -interBlock.GridSize / 2; // 下位は上位 8 グリッドを 1 つのグリッドとします。 // 判定中に使用する上位 8 グリッドの位置情報を一時的に格納する配列です。 Position[] hPositions = new Position[8]; // 判定中に使用する上位 8 グリッドの Material 情報を一時的に格納する配列です。 int[] hMaterials = new int[8]; // 上位 8 グリッドずつ判定しながら下位グリッドの情報を決定します。 for (int z = minGrid; z < maxGrid; z++) { for (int y = minGrid; y < maxGrid; y++) { for (int x = minGrid; x < maxGrid; x++) { int hX = x * 2; int hY = y * 2; int hZ = z * 2; hPositions[0] = new Position(hX, hY, hZ); hPositions[1] = new Position(hX + 1, hY, hZ); hPositions[2] = new Position(hX, hY + 1, hZ); hPositions[3] = new Position(hX + 1, hY + 1, hZ); hPositions[4] = new Position(hX, hY, hZ + 1); hPositions[5] = new Position(hX + 1, hY, hZ + 1); hPositions[6] = new Position(hX, hY + 1, hZ + 1); hPositions[7] = new Position(hX + 1, hY + 1, hZ + 1); for (int i = 0; i < 8; i++) { Element hElement; highBlock.Elements.TryGetItem(hPositions[i], out hElement); hMaterials[i] = (hElement != null) ? hElement.MaterialIndex : -1; } // 出現頻度が最大の Material を調べます。 Array.Sort(hMaterials); int maxMaterialIndex = -1; int maxCount = 0; int countingMaterialIndex = -1; int count = 0; for (int i = 0; i < 8; i++) { if (hMaterials[i] == countingMaterialIndex) { count++; } else { if (maxCount <= count) { maxCount = count; maxMaterialIndex = countingMaterialIndex; } countingMaterialIndex = hMaterials[i]; count = 1; } } // 最後の要素も有効にするために判定します。 if (maxCount <= count) { maxCount = count; maxMaterialIndex = countingMaterialIndex; } // 出現頻度が最大の Material で、このグリッドのための Element を作成します。 // maxMaterialIndex が -1 の場合はグリッドが空であることを表し、Element を作成しません。 if (maxMaterialIndex != -1) { var element = new Element() { MaterialIndex = maxMaterialIndex, Position = new Position(x, y, z) }; interBlock.Elements.Add(element); } } } } // Element サイズは上位 InterBlock の 2 倍です。 interBlock.ElementSize = highBlock.ElementSize * 2; return interBlock; }
/// <summary> /// 指定された InterBlock の LOD の 1 レベル下位の詳細情報を持つ InterBlock を作成します。 /// 生成可能な最小の詳細情報を持つ InterBlock のグリッド サイズは 2 です。 /// </summary> /// <param name="highBlock">生成する InterBlock の 1 レベル上位の詳細情報を持つ InterBlock。</param> /// <returns>生成された InterBlock。</returns> static InterBlock CreateLowDetailLevelInterBlock(InterBlock highBlock) { if (highBlock.GridSize == 2) { throw new ArgumentException("A specified InterBlock has a minimum LOD."); } var interBlock = new InterBlock(); // 上位の半分のグリッド数で作成します。 interBlock.GridSize = highBlock.GridSize / 2; interBlock.Materials = highBlock.Materials; interBlock.Elements = new InterElementCollection(); // グリッド位置の最大と最小を計算します。 int maxGrid = interBlock.GridSize / 2; int minGrid = -interBlock.GridSize / 2; // 下位は上位 8 グリッドを 1 つのグリッドとします。 // 判定中に使用する上位 8 グリッドの位置情報を一時的に格納する配列です。 Position[] hPositions = new Position[8]; // 判定中に使用する上位 8 グリッドの Material 情報を一時的に格納する配列です。 int[] hMaterials = new int[8]; // 上位 8 グリッドずつ判定しながら下位グリッドの情報を決定します。 for (int z = minGrid; z < maxGrid; z++) { for (int y = minGrid; y < maxGrid; y++) { for (int x = minGrid; x < maxGrid; x++) { int hX = x * 2; int hY = y * 2; int hZ = z * 2; hPositions[0] = new Position(hX, hY, hZ); hPositions[1] = new Position(hX + 1, hY, hZ); hPositions[2] = new Position(hX, hY + 1, hZ); hPositions[3] = new Position(hX + 1, hY + 1, hZ); hPositions[4] = new Position(hX, hY, hZ + 1); hPositions[5] = new Position(hX + 1, hY, hZ + 1); hPositions[6] = new Position(hX, hY + 1, hZ + 1); hPositions[7] = new Position(hX + 1, hY + 1, hZ + 1); for (int i = 0; i < 8; i++) { Element hElement; highBlock.Elements.TryGetItem(hPositions[i], out hElement); hMaterials[i] = (hElement != null) ? hElement.MaterialIndex : -1; } // 出現頻度が最大の Material を調べます。 Array.Sort(hMaterials); int maxMaterialIndex = -1; int maxCount = 0; int countingMaterialIndex = -1; int count = 0; for (int i = 0; i < 8; i++) { if (hMaterials[i] == countingMaterialIndex) { count++; } else { if (maxCount <= count) { maxCount = count; maxMaterialIndex = countingMaterialIndex; } countingMaterialIndex = hMaterials[i]; count = 1; } } // 最後の要素も有効にするために判定します。 if (maxCount <= count) { maxCount = count; maxMaterialIndex = countingMaterialIndex; } // 出現頻度が最大の Material で、このグリッドのための Element を作成します。 // maxMaterialIndex が -1 の場合はグリッドが空であることを表し、Element を作成しません。 if (maxMaterialIndex != -1) { var element = new Element() { MaterialIndex = maxMaterialIndex, Position = new Position(x, y, z) }; interBlock.Elements.Add(element); } } } } // Element サイズは上位 InterBlock の 2 倍です。 interBlock.ElementSize = highBlock.ElementSize * 2; return(interBlock); }