public static void Export(string path, BlockGroup blockGroup) { var blockMeshes = new BlockMeshMerger(); foreach (var block in blockGroup.GetAllBlocks()) { block.WriteToMesh(blockGroup, blockMeshes); } string dataName = Path.GetFileNameWithoutExtension(path); var writer = new StringWriter(); writer.WriteLine("#Created by Tsumiki Editor"); writer.WriteLine(""); writer.WriteLine("mtllib " + dataName + ".mtl"); writer.WriteLine(""); // Output Vertices foreach (Vector3 v in blockMeshes.vertexPos) { writer.WriteLine("v " + v.x + " " + v.y + " " + v.z); } writer.WriteLine("# " + blockMeshes.vertexPos.Count + " vertices"); // Output Texture Vertices foreach (Vector2 vt in blockMeshes.vertexUv) { writer.WriteLine("vt " + vt.x + " " + (1.0f - vt.y)); } writer.WriteLine("# " + blockMeshes.vertexUv.Count + " texture vertices"); // Output Faces writer.WriteLine("usemtl mat1"); int facesCount = blockMeshes.triangles.Count / 3; for (int i = 0; i < facesCount; i++) { int i0 = blockMeshes.triangles[i * 3 + 0] + 1; int i1 = blockMeshes.triangles[i * 3 + 1] + 1; int i2 = blockMeshes.triangles[i * 3 + 2] + 1; writer.WriteLine("f " + i0 + "/" + i0 + " " + i1 + "/" + i1 + " " + i2 + "/" + i2); } writer.WriteLine("# " + blockMeshes.vertexUv.Count + " texture vertices"); File.WriteAllText(path, writer.ToString(), Encoding.ASCII); writer.Dispose(); // Output Materials var mtlWriter = new StringWriter(); mtlWriter.WriteLine("newmtl mat1"); mtlWriter.WriteLine("Ka 0.00000 0.00000 0.00000"); mtlWriter.WriteLine("Kd 0.00000 0.00000 0.00000"); mtlWriter.WriteLine("Ks 0.00000 0.00000 0.00000"); mtlWriter.WriteLine("Ns 0.00000"); File.WriteAllText(Path.ChangeExtension(path, ".mtl"), mtlWriter.ToString(), Encoding.ASCII); mtlWriter.Dispose(); }
// ガイド用メッシュを出力 public void WriteToGuideMesh(BlockGroup blockGroup, BlockMeshMerger mesh) { BlockShape shape = this.shape; bool vertexHasWrote = false; for (int i = 0; i < 6; i++) { // 隣のブロックに完全に覆われていたら省略する if (this.IsOcculuded(blockGroup, (BlockDirection)i)) { continue; } // 壁タイプのガイドは限定的にする if (shape.wall > 0) { int index = this.ToLocalDirection(i); if (shape.wall >= 1 && (BlockDirection)index == BlockDirection.Zplus) { } else if (shape.wall >= 2 && (BlockDirection)index == BlockDirection.Xplus) { } else { continue; } } // 頂点が書き出されていなければここで書き出す if (!vertexHasWrote) { for (int j = 0; j < EditUtil.cubeVertices.Length; j++) { mesh.vertexPos.Add(this.position + EditUtil.cubeVertices[j]); } vertexHasWrote = true; } int offset = mesh.vertexPos.Count - EditUtil.cubeVertices.Length; mesh.triangles.Add(offset + EditUtil.cubeQuadIndices[i * 4 + 0]); mesh.triangles.Add(offset + EditUtil.cubeQuadIndices[i * 4 + 1]); mesh.triangles.Add(offset + EditUtil.cubeQuadIndices[i * 4 + 2]); mesh.triangles.Add(offset + EditUtil.cubeQuadIndices[i * 4 + 0]); mesh.triangles.Add(offset + EditUtil.cubeQuadIndices[i * 4 + 2]); mesh.triangles.Add(offset + EditUtil.cubeQuadIndices[i * 4 + 3]); } }
// 表示用メッシュを出力 public void WriteToMesh(BlockGroup blockGroup, BlockMeshMerger meshMerger) { BlockShape shape = this.shape; if (shape.autoPlacement) { // 自動配置ブロックの処理 WriteToMeshAutoPlacement(blockGroup, meshMerger); } else { // 6方向ブロックメッシュ WriteToMeshStandard(blockGroup, meshMerger); } }
// ルート用メッシュを出力 public void WriteToRouteMesh(BlockGroup blockGroup, BlockMeshMerger mesh) { if (!this.IsEnterable(blockGroup)) { return; } int offset = mesh.vertexPos.Count; for (int j = 0; j < 4; j++) { int index = EditUtil.ReversePanelVertexIndex(j, this.direction); Vector3 vertex = EditUtil.panelVertices[j]; vertex.y = this.shape.panelVertices[index] * 0.5f - 0.25f; vertex = EditManager.Instance.ToWorldCoordinate(vertex); mesh.vertexPos.Add(this.position + vertex); } if (this.direction == BlockDirection.Xplus || this.direction == BlockDirection.Xminus ) { mesh.triangles.Add(offset + 0); mesh.triangles.Add(offset + 2); mesh.triangles.Add(offset + 1); mesh.triangles.Add(offset + 1); mesh.triangles.Add(offset + 2); mesh.triangles.Add(offset + 3); } else { mesh.triangles.Add(offset + 0); mesh.triangles.Add(offset + 3); mesh.triangles.Add(offset + 1); mesh.triangles.Add(offset + 0); mesh.triangles.Add(offset + 2); mesh.triangles.Add(offset + 3); } }
// 6方向ブロックメッシュ public void WriteToMeshNormal(BlockGroup blockGroup, BlockMeshMerger meshMerger) { BlockShape shape = this.shape; for (int i = 0; i < shape.meshes.Length; i++) { int index = this.ToLocalDirection(i); var mesh = shape.meshes[index]; if (mesh == null) { continue; } // 隣のブロックに完全に覆われていたら省略する if (i < 6 && this.CheckOcculusion(blockGroup, (BlockDirection)i)) { continue; } meshMerger.Merge(mesh, this.position, this.direction, this.textureChips[index], i); } }
// 自動配置ブロックの処理 public void WriteToMeshAutoPlacement(BlockGroup blockGroup, BlockMeshMerger meshMerger) { BlockShape shape = this.shape; // 隣接ブロックを取得 var list = new Vector3[] { new Vector3(0, 0, 1), new Vector3(1, 0, 0), new Vector3(0, 0, -1), new Vector3(-1, 0, 0), new Vector3(1, 0, 1), new Vector3(1, 0, -1), new Vector3(-1, 0, -1), new Vector3(-1, 0, 1), new Vector3(0, 0.5f, 0), new Vector3(0, 0.5f, 1), new Vector3(1, 0.5f, 0), new Vector3(0, 0.5f, -1), new Vector3(-1, 0.5f, 0), }; int pattern = 0; for (int i = 0; i < list.Length; i++) { var block = blockGroup.GetBlock(position + EditManager.Instance.ToWorldCoordinate(list[i])); if (block != null) { pattern |= (1 << i); } } for (int i = 0; i < 4; i++) { bool s1 = (pattern & (1 << (i))) != 0; // 隣接1 bool s2 = (pattern & (1 << ((i + 1) % 4))) != 0; // 隣接2 bool s3 = (pattern & (1 << (i + 4))) != 0; // 斜め隣接 bool s4 = (pattern & (1 << 8)) != 0; // 上隣接 bool s5 = (pattern & (1 << (9 + i))) != 0; // 上横隣接1 bool s6 = (pattern & (1 << (9 + (i + 1) % 4))) != 0; // 上横隣接2 int meshOffset; if (s3) { if (s1 && s2) { meshOffset = (s4 && (s5 || s6)) ? -1 : 0; } else if (s1) { meshOffset = (s4) ? (s5) ? 24 : 32 : 8; } else if (s2) { meshOffset = (s4) ? (s6) ? 28 : 36 : 12; } else { meshOffset = (s4) ? 20 : 4; } } else { if (s1 && s2) { meshOffset = 16; } else if (s1) { meshOffset = (s4) ? (s5) ? 24 : 32 : 8; } else if (s2) { meshOffset = (s4) ? (s6) ? 28 : 36 : 12; } else { meshOffset = (s4) ? 20 : 4; } } if (meshOffset < 0) { continue; } var mesh = shape.meshes[6 + meshOffset + i]; if (mesh == null) { continue; } meshMerger.Merge(mesh, this.position, this.direction, Vector3.one, shape.divideChipVert, this.textureChips[6], 0); } }
// 6方向ブロックメッシュ public void WriteToMeshStandard(BlockGroup blockGroup, BlockMeshMerger meshMerger) { BlockShape shape = this.shape; for (int i = 0; i < shape.meshes.Length; i++) { int index = this.ToLocalDirection(i); var mesh = shape.meshes[index]; if (mesh == null) { continue; } // 隣のブロックに完全に覆われていたら省略する if (i < 6 && this.IsOcculuded(blockGroup, (BlockDirection)i)) { continue; } // 一番下の底面は省略する if ((BlockDirection)i == BlockDirection.Yminus && this.position.y == 0.0f) { continue; } Vector3 position = this.position; Vector3 scale = Vector3.one; // 側面パネルの場合は分割する bool divideChipVert = shape.divideChipVert && i < 6 && (BlockDirection)i != BlockDirection.Yplus && (BlockDirection)i != BlockDirection.Yminus; if (divideChipVert && this.IsCombinable((BlockDirection)i)) { BlockDirection direction = (BlockDirection)i; // 上下に2個続いているブロックをまとめる if (this.position.y - Math.Floor(this.position.y) >= 0.5f) { Block neighborBlock = blockGroup.GetBlock(this.position - new Vector3(0, 0.5f, 0)); if (neighborBlock != null && neighborBlock.IsCombinable(direction) && this.GetTextureChip(direction) == neighborBlock.GetTextureChip(direction)) { divideChipVert = false; position.y -= 0.25f; scale.y = 2.0f; } } else { Block neighborBlock = blockGroup.GetBlock(this.position + new Vector3(0, 0.5f, 0)); if (neighborBlock != null && neighborBlock.IsCombinable(direction) && this.GetTextureChip(direction) == neighborBlock.GetTextureChip(direction)) { if (!neighborBlock.IsOcculuded(blockGroup, direction)) { continue; } } } } meshMerger.Merge(mesh, position, this.direction, scale, divideChipVert, this.textureChips[index], i); } }