// For a 1024 cubed asteroid, it takes approximately 6.5Gb of system memory. public static MyVoxelMap ReadModelAsteroidVolmetic(Model3DGroup model, IList <MyMeshModel> mappedMesh, ScaleTransform3D scale, Transform3D rotateTransform, TraceType traceType, TraceCount traceCount, TraceDirection traceDirection, Action <double, double> resetProgress, Action incrementProgress, Func <bool> checkCancel, Action complete) { var traceDirectionCount = 0; var materials = new List <byte>(); var faceMaterials = new List <byte>(); foreach (var mesh in mappedMesh) { materials.Add(mesh.MaterialIndex ?? SpaceEngineersConsts.EmptyVoxelMaterial); faceMaterials.Add(mesh.FaceMaterialIndex ?? SpaceEngineersConsts.EmptyVoxelMaterial); } // How far to check in from the proposed Volumetric edge. // This number is just made up, but small enough that it still represents the corner edge of the Volumetric space. // But still large enough that it isn't the exact corner. const double offset = 0.0000045f; if (scale.ScaleX > 0 && scale.ScaleY > 0 && scale.ScaleZ > 0 && scale.ScaleX != 1.0f && scale.ScaleY != 1.0f && scale.ScaleZ != 1.0f) { model.TransformScale(scale.ScaleX, scale.ScaleY, scale.ScaleZ); } // Attempt to offset the model, so it's only caulated from zero (0) and up, instead of using zero (0) as origin. //model.Transform = new TranslateTransform3D(-model.Bounds.X, -model.Bounds.Y, -model.Bounds.Z); var tbounds = model.Bounds; Matrix3D?rotate = null; if (rotateTransform != null) { rotate = rotateTransform.Value; tbounds = rotateTransform.TransformBounds(tbounds); } //model.Transform = new TranslateTransform3D(-tbounds.X, -tbounds.Y, -tbounds.Z); // Add 2 to either side, to allow for material padding to expose internal materials. const int bufferSize = 2; var xMin = (int)Math.Floor(tbounds.X) - bufferSize; var yMin = (int)Math.Floor(tbounds.Y) - bufferSize; var zMin = (int)Math.Floor(tbounds.Z) - bufferSize; var xMax = (int)Math.Ceiling(tbounds.X + tbounds.SizeX) + bufferSize; var yMax = (int)Math.Ceiling(tbounds.Y + tbounds.SizeY) + bufferSize; var zMax = (int)Math.Ceiling(tbounds.Z + tbounds.SizeZ) + bufferSize; // Do not rounnd up the array size, as this really isn't required, and it increases the calculation time. var xCount = (xMax - xMin); var yCount = (yMax - yMin); var zCount = (zMax - zMin); Debug.WriteLine("Approximate Size: {0}x{1}x{2}", Math.Ceiling(tbounds.X + tbounds.SizeX) - Math.Floor(tbounds.X), Math.Ceiling(tbounds.Y + tbounds.SizeY) - Math.Floor(tbounds.Y), Math.Ceiling(tbounds.Z + tbounds.SizeZ) - Math.Floor(tbounds.Z)); Debug.WriteLine("Bounds Size: {0}x{1}x{2}", xCount, yCount, zCount); var finalCubic = ArrayHelper.Create <byte>(xCount, yCount, zCount); var finalMater = ArrayHelper.Create <byte>(xCount, yCount, zCount); if (resetProgress != null) { long triangles = (from GeometryModel3D gm in model.Children select gm.Geometry as MeshGeometry3D).Aggregate <MeshGeometry3D, long>(0, (current, g) => current + (g.TriangleIndices.Count / 3)); long rays = 0; if ((traceDirection & TraceDirection.X) == TraceDirection.X) { rays += ((yMax - yMin) * (zMax - zMin)); } if ((traceDirection & TraceDirection.Y) == TraceDirection.Y) { rays += ((xMax - xMin) * (zMax - zMin)); } if ((traceDirection & TraceDirection.Z) == TraceDirection.Z) { rays += ((xMax - xMin) * (yMax - yMin)); } resetProgress.Invoke(0, rays * triangles); } if (checkCancel != null && checkCancel.Invoke()) { complete?.Invoke(); return(null); } #region basic ray trace of every individual triangle. // Start from the last mesh, which represents the bottom of the UI stack, and overlay each other mesh on top of it. for (var modelIdx = mappedMesh.Count - 1; modelIdx >= 0; modelIdx--) { Debug.WriteLine("Model {0}", modelIdx); var modelCubic = new byte[xCount][][]; var modelMater = new byte[xCount][][]; for (var x = 0; x < xCount; x++) { modelCubic[x] = new byte[yCount][]; modelMater[x] = new byte[yCount][]; for (var y = 0; y < yCount; y++) { modelCubic[x][y] = new byte[zCount]; modelMater[x][y] = new byte[zCount]; } } var meshes = mappedMesh[modelIdx]; var threadCounter = 0; var geometries = new GeometeryDetail[meshes.Geometery.Length]; for (var i = 0; i < meshes.Geometery.Length; i++) { geometries[i] = new GeometeryDetail(meshes.Geometery[i]); } var startOffset = 0.5f; var endOffset = 0.5f; var volumeOffset = 0.5f; Func <double, int> roundFunc = null; if (traceType == TraceType.Odd) { startOffset = 0.5f; endOffset = 0.5f; volumeOffset = 0.5f; roundFunc = delegate(double d) { return((int)Math.Round(d, 0)); }; } else if (traceType == TraceType.Even) { startOffset = 0.0f; endOffset = 1.0f; volumeOffset = 1.0f; roundFunc = delegate(double d) { return((int)Math.Floor(d)); }; } #region X ray trace if ((traceDirection & TraceDirection.X) == TraceDirection.X) { Debug.WriteLine("X Rays"); traceDirectionCount++; threadCounter = (yMax - yMin) * (zMax - zMin); for (var y = yMin; y < yMax; y++) { for (var z = zMin; z < zMax; z++) { if (checkCancel != null && checkCancel.Invoke()) { complete?.Invoke(); return(null); } List <Point3D[]> testRays = null; if (traceType == TraceType.Odd) { testRays = new List <Point3D[]> { new [] { new Point3D(xMin, y + offset, z + offset), new Point3D(xMax, y + offset, z + offset) }, new [] { new Point3D(xMin, y - 0.5f + offset, z - 0.5f + offset), new Point3D(xMax, y - 0.5f + offset, z - 0.5f + offset) }, new [] { new Point3D(xMin, y + 0.5f - offset, z - 0.5f + offset), new Point3D(xMax, y + 0.5f - offset, z - 0.5f + offset) }, new [] { new Point3D(xMin, y - 0.5f + offset, z + 0.5f - offset), new Point3D(xMax, y - 0.5f + offset, z + 0.5f - offset) }, new [] { new Point3D(xMin, y + 0.5f - offset, z + 0.5f - offset), new Point3D(xMax, y + 0.5f - offset, z + 0.5f - offset) } } } ; else if (traceType == TraceType.Even) { testRays = new List <Point3D[]> { new [] { new Point3D(xMin, y + 0.5f - offset, z + 0.5f - offset), new Point3D(xMax, y + 0.5f - offset, z + 0.5f - offset) }, new [] { new Point3D(xMin, y + offset, z + offset), new Point3D(xMax, y + offset, z + offset) }, new [] { new Point3D(xMin, y + 1.0f - offset, z + offset), new Point3D(xMax, y + 1.0f - offset, z + offset) }, new [] { new Point3D(xMin, y + offset, z + 1.0f - offset), new Point3D(xMax, y + offset, z + 1.0f - offset) }, new [] { new Point3D(xMin, y + 1.0f - offset, z + 1.0f - offset), new Point3D(xMax, y + 1.0f - offset, z + 1.0f - offset) } } } ; var task = new Task(obj => { var bgw = (RayTracerTaskWorker)obj; var tracers = new List <Trace>(); foreach (var geometery in geometries) { for (var t = 0; t < geometery.Triangles.Length; t += 3) { if (checkCancel != null && checkCancel.Invoke()) { return; } if (incrementProgress != null) { lock (Locker) { incrementProgress.Invoke(); } } var p1 = geometery.Positions[geometery.Triangles[t]]; var p2 = geometery.Positions[geometery.Triangles[t + 1]]; var p3 = geometery.Positions[geometery.Triangles[t + 2]]; if (rotate.HasValue) { p1 = rotate.Value.Transform(p1); p2 = rotate.Value.Transform(p2); p3 = rotate.Value.Transform(p3); } foreach (var ray in testRays) { if ((p1.Y < ray[0].Y && p2.Y < ray[0].Y && p3.Y < ray[0].Y) || (p1.Y > ray[0].Y && p2.Y > ray[0].Y && p3.Y > ray[0].Y) || (p1.Z < ray[0].Z && p2.Z < ray[0].Z && p3.Z < ray[0].Z) || (p1.Z > ray[0].Z && p2.Z > ray[0].Z && p3.Z > ray[0].Z)) { continue; } Point3D intersect; int normal; if (MeshHelper.RayIntersetTriangleRound(p1, p2, p3, ray[0], ray[1], out intersect, out normal)) { tracers.Add(new Trace(intersect, normal)); } } } } if (tracers.Count > 1) { var order = tracers.GroupBy(t => new { t.Point, t.Face }).Select(g => g.First()).OrderBy(k => k.Point.X).ToArray(); var startCoord = roundFunc(order[0].Point.X); var endCoord = roundFunc(order[order.Length - 1].Point.X); var surfaces = 0; for (var x = startCoord; x <= endCoord; x++) { var points = order.Where(p => p.Point.X > x - startOffset && p.Point.X < x + endOffset).ToArray(); var volume = (byte)(0xff / testRays.Count * surfaces); foreach (var point in points) { if (point.Face == MeshFace.Farside) { volume += (byte)(Math.Round(Math.Abs(x + volumeOffset - point.Point.X) * 255 / testRays.Count, 0)); surfaces++; } else if (point.Face == MeshFace.Nearside) { volume -= (byte)(Math.Round(Math.Abs(x + volumeOffset - point.Point.X) * 255 / testRays.Count, 0)); surfaces--; } } // TODO: retest detailed model export. //volume = volume.RoundUpToNearest(8); modelCubic[x - xMin][bgw.Y - yMin][bgw.Z - zMin] = volume; modelMater[x - xMin][bgw.Y - yMin][bgw.Z - zMin] = materials[bgw.ModelIdx]; } if (faceMaterials[bgw.ModelIdx] != 0xff) { for (var i = 1; i < 6; i++) { if (xMin < startCoord - i && modelCubic[startCoord - i - xMin][bgw.Y - yMin][bgw.Z - zMin] == 0) { modelMater[startCoord - i - xMin][bgw.Y - yMin][bgw.Z - zMin] = faceMaterials[bgw.ModelIdx]; } if (endCoord + i < xMax && modelCubic[endCoord + i - xMin][bgw.Y - yMin][bgw.Z - zMin] == 0) { modelMater[endCoord + i - xMin][bgw.Y - yMin][bgw.Z - zMin] = faceMaterials[bgw.ModelIdx]; } } } } lock (Locker) { threadCounter--; } }, new RayTracerTaskWorker(modelIdx, 0, y, z)); task.Start(); } } // Wait for Multithread parts to finish. while (threadCounter > 0) { System.Windows.Forms.Application.DoEvents(); if (checkCancel != null && checkCancel.Invoke()) { break; } } GC.Collect(); } #endregion #region Y rays trace if ((traceDirection & TraceDirection.Y) == TraceDirection.Y) { Debug.WriteLine("Y Rays"); traceDirectionCount++; threadCounter = (xMax - xMin) * (zMax - zMin); for (var x = xMin; x < xMax; x++) { for (var z = zMin; z < zMax; z++) { if (checkCancel != null && checkCancel.Invoke()) { if (complete != null) { complete.Invoke(); } return(null); } List <Point3D[]> testRays = null; if (traceType == TraceType.Odd) { testRays = new List <Point3D[]> { new [] { new Point3D(x + offset, yMin, z + offset), new Point3D(x + offset, yMax, z + offset) }, new [] { new Point3D(x - 0.5f + offset, yMin, z - 0.5f + offset), new Point3D(x - 0.5f + offset, yMax, z - 0.5f + offset) }, new [] { new Point3D(x + 0.5f - offset, yMin, z - 0.5f + offset), new Point3D(x + 0.5f - offset, yMax, z - 0.5f + offset) }, new [] { new Point3D(x - 0.5f + offset, yMin, z + 0.5f - offset), new Point3D(x - 0.5f + offset, yMax, z + 0.5f - offset) }, new [] { new Point3D(x + 0.5f - offset, yMin, z + 0.5f - offset), new Point3D(x + 0.5f - offset, yMax, z + 0.5f - offset) } } } ; else if (traceType == TraceType.Even) { testRays = new List <Point3D[]> { new [] { new Point3D(x + 0.5f - offset, yMin, z + 0.5f - offset), new Point3D(x + 0.5f - offset, yMax, z + 0.5f - offset) }, new [] { new Point3D(x + offset, yMin, z + offset), new Point3D(x + offset, yMax, z + offset) }, new [] { new Point3D(x + 1.0f - offset, yMin, z + offset), new Point3D(x + 1.0f - offset, yMax, z + offset) }, new [] { new Point3D(x + offset, yMin, z + 1.0f - offset), new Point3D(x + offset, yMax, z + 1.0f - offset) }, new [] { new Point3D(x + 1.0f - offset, yMin, z + 1.0f - offset), new Point3D(x + 1.0f - offset, yMax, z + 1.0f - offset) } } } ; var task = new Task(obj => { var bgw = (RayTracerTaskWorker)obj; var tracers = new List <Trace>(); foreach (var geometery in geometries) { for (var t = 0; t < geometery.Triangles.Length; t += 3) { if (checkCancel != null && checkCancel.Invoke()) { return; } if (incrementProgress != null) { lock (Locker) { incrementProgress.Invoke(); } } var p1 = geometery.Positions[geometery.Triangles[t]]; var p2 = geometery.Positions[geometery.Triangles[t + 1]]; var p3 = geometery.Positions[geometery.Triangles[t + 2]]; if (rotate.HasValue) { p1 = rotate.Value.Transform(p1); p2 = rotate.Value.Transform(p2); p3 = rotate.Value.Transform(p3); } foreach (var ray in testRays) { if ((p1.X < ray[0].X && p2.X < ray[0].X && p3.X < ray[0].X) || (p1.X > ray[0].X && p2.X > ray[0].X && p3.X > ray[0].X) || (p1.Z < ray[0].Z && p2.Z < ray[0].Z && p3.Z < ray[0].Z) || (p1.Z > ray[0].Z && p2.Z > ray[0].Z && p3.Z > ray[0].Z)) { continue; } Point3D intersect; int normal; if (MeshHelper.RayIntersetTriangleRound(p1, p2, p3, ray[0], ray[1], out intersect, out normal)) { tracers.Add(new Trace(intersect, normal)); } } } } if (tracers.Count > 1) { var order = tracers.GroupBy(t => new { t.Point, t.Face }).Select(g => g.First()).OrderBy(k => k.Point.Y).ToArray(); var startCoord = roundFunc(order[0].Point.Y); var endCoord = roundFunc(order[order.Length - 1].Point.Y); var surfaces = 0; for (var y = startCoord; y <= endCoord; y++) { var points = order.Where(p => p.Point.Y > y - startOffset && p.Point.Y < y + endOffset).ToArray(); var volume = (byte)(0xff / testRays.Count * surfaces); foreach (var point in points) { if (point.Face == MeshFace.Farside) { volume += (byte)(Math.Round(Math.Abs(y + volumeOffset - point.Point.Y) * 255 / testRays.Count, 0)); surfaces++; } else if (point.Face == MeshFace.Nearside) { volume -= (byte)(Math.Round(Math.Abs(y + volumeOffset - point.Point.Y) * 255 / testRays.Count, 0)); surfaces--; } } if (traceDirectionCount > 1) { var prevolumme = modelCubic[bgw.X - xMin][y - yMin][bgw.Z - zMin]; if (prevolumme != 0) { // average with the pre-existing X volume. volume = (byte)Math.Round(((float)prevolumme + (float)volume) / (float)traceDirectionCount, 0); } } //volume = volume.RoundUpToNearest(8); modelCubic[bgw.X - xMin][y - yMin][bgw.Z - zMin] = volume; modelMater[bgw.X - xMin][y - yMin][bgw.Z - zMin] = materials[bgw.ModelIdx]; } if (faceMaterials[bgw.ModelIdx] != 0xff) { for (var i = 1; i < 6; i++) { if (yMin < startCoord - i && modelCubic[bgw.X - xMin][startCoord - i - yMin][bgw.Z - zMin] == 0) { modelMater[bgw.X - xMin][startCoord - i - yMin][bgw.Z - zMin] = faceMaterials[bgw.ModelIdx]; } if (endCoord + i < yMax && modelCubic[bgw.X - xMin][endCoord + i - yMin][bgw.Z - zMin] == 0) { modelMater[bgw.X - xMin][endCoord + i - yMin][bgw.Z - zMin] = faceMaterials[bgw.ModelIdx]; } } } } lock (Locker) { threadCounter--; } }, new RayTracerTaskWorker(modelIdx, x, 0, z)); task.Start(); } } // Wait for Multithread parts to finish. while (threadCounter > 0) { System.Windows.Forms.Application.DoEvents(); if (checkCancel != null && checkCancel.Invoke()) { break; } } GC.Collect(); } #endregion #region Z ray trace if ((traceDirection & TraceDirection.Z) == TraceDirection.Z) { Debug.WriteLine("Z Rays"); traceDirectionCount++; threadCounter = (xMax - xMin) * (yMax - yMin); for (var x = xMin; x < xMax; x++) { for (var y = yMin; y < yMax; y++) { if (checkCancel != null && checkCancel.Invoke()) { if (complete != null) { complete.Invoke(); } return(null); } List <Point3D[]> testRays = null; if (traceType == TraceType.Odd) { testRays = new List <Point3D[]> { new [] { new Point3D(x + offset, y + offset, zMin), new Point3D(x + offset, y + offset, zMax) }, new [] { new Point3D(x - 0.5f + offset, y - 0.5f + offset, zMin), new Point3D(x - 0.5f + offset, y - 0.5f + offset, zMax) }, new [] { new Point3D(x + 0.5f - offset, y - 0.5f + offset, zMin), new Point3D(x + 0.5f - offset, y - 0.5f + offset, zMax) }, new [] { new Point3D(x - 0.5f + offset, y + 0.5f - offset, zMin), new Point3D(x - 0.5f + offset, y + 0.5f - offset, zMax) }, new [] { new Point3D(x + 0.5f - offset, y + 0.5f - offset, zMin), new Point3D(x + 0.5f - offset, y + 0.5f - offset, zMax) } } } ; else if (traceType == TraceType.Even) { testRays = new List <Point3D[]> { new [] { new Point3D(x + 0.5f - offset, y + 0.5f - offset, zMin), new Point3D(x + 0.5f - offset, y + 0.5f - offset, zMax) }, new [] { new Point3D(x + offset, y + offset, zMin), new Point3D(x + offset, y + offset, zMax) }, new [] { new Point3D(x + 1.0f - offset, y + offset, zMin), new Point3D(x + 1.0f - offset, y + offset, zMax) }, new [] { new Point3D(x + offset, y + 1.0f - offset, zMin), new Point3D(x + offset, y + 1.0f - offset, zMax) }, new [] { new Point3D(x + 1.0f - offset, y + 1.0f - offset, zMin), new Point3D(x + 1.0f - offset, y + 1.0f - offset, zMax) } } } ; var task = new Task(obj => { var bgw = (RayTracerTaskWorker)obj; var tracers = new List <Trace>(); foreach (var geometery in geometries) { for (var t = 0; t < geometery.Triangles.Length; t += 3) { if (checkCancel != null && checkCancel.Invoke()) { return; } if (incrementProgress != null) { lock (Locker) { incrementProgress.Invoke(); } } var p1 = geometery.Positions[geometery.Triangles[t]]; var p2 = geometery.Positions[geometery.Triangles[t + 1]]; var p3 = geometery.Positions[geometery.Triangles[t + 2]]; if (rotate.HasValue) { p1 = rotate.Value.Transform(p1); p2 = rotate.Value.Transform(p2); p3 = rotate.Value.Transform(p3); } foreach (var ray in testRays) { if ((p1.X < ray[0].X && p2.X < ray[0].X && p3.X < ray[0].X) || (p1.X > ray[0].X && p2.X > ray[0].X && p3.X > ray[0].X) || (p1.Y < ray[0].Y && p2.Y < ray[0].Y && p3.Y < ray[0].Y) || (p1.Y > ray[0].Y && p2.Y > ray[0].Y && p3.Y > ray[0].Y)) { continue; } Point3D intersect; int normal; if (MeshHelper.RayIntersetTriangleRound(p1, p2, p3, ray[0], ray[1], out intersect, out normal)) { tracers.Add(new Trace(intersect, normal)); } } } } if (tracers.Count > 1) { var order = tracers.GroupBy(t => new { t.Point, t.Face }).Select(g => g.First()).OrderBy(k => k.Point.Z).ToArray(); var startCoord = roundFunc(order[0].Point.Z); var endCoord = roundFunc(order[order.Length - 1].Point.Z); var surfaces = 0; for (var z = startCoord; z <= endCoord; z++) { var points = order.Where(p => p.Point.Z > z - startOffset && p.Point.Z < z + endOffset).ToArray(); var volume = (byte)(0xff / testRays.Count * surfaces); foreach (var point in points) { if (point.Face == MeshFace.Farside) { volume += (byte)(Math.Round(Math.Abs(z + volumeOffset - point.Point.Z) * 255 / testRays.Count, 0)); surfaces++; } else if (point.Face == MeshFace.Nearside) { volume -= (byte)(Math.Round(Math.Abs(z + volumeOffset - point.Point.Z) * 255 / testRays.Count, 0)); surfaces--; } } if (traceDirectionCount > 1) { var prevolumme = modelCubic[bgw.X - xMin][bgw.Y - yMin][z - zMin]; if (prevolumme != 0) { // average with the pre-existing X and Y volumes. volume = (byte)Math.Round((((float)prevolumme * (traceDirectionCount - 1)) + (float)volume) / (float)traceDirectionCount, 0); } } //volume = volume.RoundUpToNearest(8); modelCubic[bgw.X - xMin][bgw.Y - yMin][z - zMin] = volume; modelMater[bgw.X - xMin][bgw.Y - yMin][z - zMin] = materials[bgw.ModelIdx]; } if (faceMaterials[bgw.ModelIdx] != 0xff) { for (var i = 1; i < 6; i++) { if (zMin < startCoord - i && modelCubic[bgw.X - xMin][bgw.Y - yMin][startCoord - i - zMin] == 0) { modelMater[bgw.X - xMin][bgw.Y - yMin][startCoord - i - zMin] = faceMaterials[bgw.ModelIdx]; } if (endCoord + i < zMax && modelCubic[bgw.X - xMin][bgw.Y - yMin][endCoord + i - zMin] == 0) { modelMater[bgw.X - xMin][bgw.Y - yMin][endCoord + i - zMin] = faceMaterials[bgw.ModelIdx]; } } } } lock (Locker) { threadCounter--; } }, new RayTracerTaskWorker(modelIdx, x, y, 0)); task.Start(); } } // Wait for Multithread parts to finish. while (threadCounter > 0) { System.Windows.Forms.Application.DoEvents(); if (checkCancel != null && checkCancel.Invoke()) { break; } } GC.Collect(); } #endregion if (checkCancel != null && checkCancel.Invoke()) { complete?.Invoke(); return(null); } #region merge individual model results into final for (var x = 0; x < xCount; x++) { for (var y = 0; y < yCount; y++) { for (var z = 0; z < zCount; z++) { if (modelMater[x][y][z] == 0xff && modelCubic[x][y][z] != 0) { finalCubic[x][y][z] = (byte)Math.Max(finalCubic[x][y][z] - modelCubic[x][y][z], 0); } else if (modelCubic[x][y][z] != 0) { finalCubic[x][y][z] = Math.Max(finalCubic[x][y][z], modelCubic[x][y][z]); finalMater[x][y][z] = modelMater[x][y][z]; } else if (finalCubic[x][y][z] == 0 && finalMater[x][y][z] == 0 && modelMater[x][y][z] != 0xff) { finalMater[x][y][z] = modelMater[x][y][z]; } } } } #endregion } // end models #endregion if (checkCancel != null && checkCancel.Invoke()) { complete?.Invoke(); return(null); } var size = new Vector3I(xCount, yCount, zCount); // TODO: at the moment the Mesh list is not complete, so the faceMaterial setting is kind of vague. var defaultMaterial = mappedMesh[0].MaterialIndex; // Use the FaceMaterial from the first Mesh in the object list. var faceMaterial = mappedMesh[0].FaceMaterialIndex; // Use the FaceMaterial from the first Mesh in the object list. var action = (Action <MyVoxelBuilderArgs>) delegate(MyVoxelBuilderArgs e) { // center the finalCubic structure within the voxel Volume. Vector3I pointOffset = (e.Size - size) / 2; // the model is only shaped according to its volume, not the voxel which is cubic. if (e.CoordinatePoint.X >= pointOffset.X && e.CoordinatePoint.X < pointOffset.X + xCount && e.CoordinatePoint.Y >= pointOffset.Y && e.CoordinatePoint.Y < pointOffset.Y + yCount && e.CoordinatePoint.Z >= pointOffset.Z && e.CoordinatePoint.Z < pointOffset.Z + zCount) { e.Volume = finalCubic[e.CoordinatePoint.X - pointOffset.X][e.CoordinatePoint.Y - pointOffset.Y][e.CoordinatePoint.Z - pointOffset.Z]; e.MaterialIndex = finalMater[e.CoordinatePoint.X - pointOffset.X][e.CoordinatePoint.Y - pointOffset.Y][e.CoordinatePoint.Z - pointOffset.Z]; } }; var voxelMap = MyVoxelBuilder.BuildAsteroid(true, size, defaultMaterial.Value, faceMaterial, action); complete?.Invoke(); return(voxelMap); }
// For a 1024 cubed asteroid, it takes approximately 6.5Gb of system memory. public static MyVoxelMap ReadModelAsteroidVolmetic(Model3DGroup model, IList<MyMeshModel> mappedMesh, ScaleTransform3D scale, Transform3D rotateTransform, TraceType traceType, TraceCount traceCount, TraceDirection traceDirection, Action<double, double> resetProgress, Action incrementProgress, Func<bool> checkCancel, Action complete) { var traceDirectionCount = 0; var materials = new List<byte>(); var faceMaterials = new List<byte>(); foreach (var mesh in mappedMesh) { if (string.IsNullOrEmpty(mesh.Material)) materials.Add(0xff); // represent empty materials. else materials.Add(SpaceEngineersCore.Resources.GetMaterialIndex(mesh.Material)); if (string.IsNullOrEmpty(mesh.FaceMaterial)) faceMaterials.Add(0xff); // represent empty materials. else faceMaterials.Add(SpaceEngineersCore.Resources.GetMaterialIndex(mesh.FaceMaterial)); } // How far to check in from the proposed Volumetric edge. // This number is just made up, but small enough that it still represents the corner edge of the Volumetric space. // But still large enough that it isn't the exact corner. const double offset = 0.0000045f; if (scale.ScaleX > 0 && scale.ScaleY > 0 && scale.ScaleZ > 0 && scale.ScaleX != 1.0f && scale.ScaleY != 1.0f && scale.ScaleZ != 1.0f) { model.TransformScale(scale.ScaleX, scale.ScaleY, scale.ScaleZ); } // Attempt to offset the model, so it's only caulated from zero (0) and up, instead of using zero (0) as origin. //model.Transform = new TranslateTransform3D(-model.Bounds.X, -model.Bounds.Y, -model.Bounds.Z); var tbounds = model.Bounds; Matrix3D? rotate = null; if (rotateTransform != null) { rotate = rotateTransform.Value; tbounds = rotateTransform.TransformBounds(tbounds); } //model.Transform = new TranslateTransform3D(-tbounds.X, -tbounds.Y, -tbounds.Z); // Add 2 to either side, to allow for material padding to expose internal materials. var xMin = (int)Math.Floor(tbounds.X) - 2; var yMin = (int)Math.Floor(tbounds.Y) - 2; var zMin = (int)Math.Floor(tbounds.Z) - 2; var xMax = (int)Math.Ceiling(tbounds.X + tbounds.SizeX) + 2; var yMax = (int)Math.Ceiling(tbounds.Y + tbounds.SizeY) + 2; var zMax = (int)Math.Ceiling(tbounds.Z + tbounds.SizeZ) + 2; var xCount = (xMax - xMin).RoundUpToNearest(64); var yCount = (yMax - yMin).RoundUpToNearest(64); var zCount = (zMax - zMin).RoundUpToNearest(64); Debug.WriteLine("Approximate Size: {0}x{1}x{2}", Math.Ceiling(tbounds.X + tbounds.SizeX) - Math.Floor(tbounds.X), Math.Ceiling(tbounds.Y + tbounds.SizeY) - Math.Floor(tbounds.Y), Math.Ceiling(tbounds.Z + tbounds.SizeZ) - Math.Floor(tbounds.Z)); Debug.WriteLine("Bounds Size: {0}x{1}x{2}", xCount, yCount, zCount); var finalCubic = ArrayHelper.Create<byte>(xCount, yCount, zCount); var finalMater = ArrayHelper.Create<byte>(xCount, yCount, zCount); if (resetProgress != null) { long triangles = (from GeometryModel3D gm in model.Children select gm.Geometry as MeshGeometry3D).Aggregate<MeshGeometry3D, long>(0, (current, g) => current + (g.TriangleIndices.Count / 3)); long rays = 0; if ((traceDirection & TraceDirection.X) == TraceDirection.X) rays += ((yMax - yMin) * (zMax - zMin)); if ((traceDirection & TraceDirection.Y) == TraceDirection.Y) rays += ((xMax - xMin) * (zMax - zMin)); if ((traceDirection & TraceDirection.Z) == TraceDirection.Z) rays += ((xMax - xMin) * (yMax - yMin)); resetProgress.Invoke(0, rays * triangles); } if (checkCancel != null && checkCancel.Invoke()) { if (complete != null) complete.Invoke(); return null; } #region basic ray trace of every individual triangle. // Start from the last mesh, which represents the bottom of the UI stack, and overlay each other mesh on top of it. for (var modelIdx = mappedMesh.Count - 1; modelIdx >= 0; modelIdx--) { Debug.WriteLine("Model {0}", modelIdx); var modelCubic = new byte[xCount][][]; var modelMater = new byte[xCount][][]; for (var x = 0; x < xCount; x++) { modelCubic[x] = new byte[yCount][]; modelMater[x] = new byte[yCount][]; for (var y = 0; y < yCount; y++) { modelCubic[x][y] = new byte[zCount]; modelMater[x][y] = new byte[zCount]; } } var meshes = mappedMesh[modelIdx]; var threadCounter = 0; var geometries = new GeometeryDetail[meshes.Geometery.Length]; for (var i = 0; i < meshes.Geometery.Length; i++) geometries[i] = new GeometeryDetail(meshes.Geometery[i]); var startOffset = 0.5f; var endOffset = 0.5f; var volumeOffset = 0.5f; Func<double, int> roundFunc = null; if (traceType == TraceType.Odd) { startOffset = 0.5f; endOffset = 0.5f; volumeOffset = 0.5f; roundFunc = delegate(double d) { return (int)Math.Round(d, 0); }; } else if (traceType == TraceType.Even) { startOffset = 0.0f; endOffset = 1.0f; volumeOffset = 1.0f; roundFunc = delegate(double d) { return (int)Math.Floor(d); }; } #region X ray trace if ((traceDirection & TraceDirection.X) == TraceDirection.X) { Debug.WriteLine("X Rays"); traceDirectionCount++; threadCounter = (yMax - yMin) * (zMax - zMin); for (var y = yMin; y < yMax; y++) { for (var z = zMin; z < zMax; z++) { if (checkCancel != null && checkCancel.Invoke()) { if (complete != null) complete.Invoke(); return null; } List<Point3D[]> testRays = null; if (traceType == TraceType.Odd) testRays = new List<Point3D[]> { new [] {new Point3D(xMin, y + offset, z + offset), new Point3D(xMax, y + offset, z + offset)}, new [] {new Point3D(xMin, y -0.5f + offset, z -0.5f + offset), new Point3D(xMax, y -0.5f + offset, z -0.5f + offset)}, new [] {new Point3D(xMin, y + 0.5f - offset, z -0.5f + offset), new Point3D(xMax, y + 0.5f - offset, z -0.5f + offset)}, new [] {new Point3D(xMin, y -0.5f + offset, z + 0.5f - offset), new Point3D(xMax, y -0.5f + offset, z + 0.5f - offset)}, new [] {new Point3D(xMin, y + 0.5f - offset, z + 0.5f - offset), new Point3D(xMax, y + 0.5f - offset, z + 0.5f - offset)} }; else if (traceType == TraceType.Even) testRays = new List<Point3D[]> { new [] {new Point3D(xMin, y + 0.5f - offset, z + 0.5f - offset), new Point3D(xMax, y + 0.5f - offset, z + 0.5f - offset)}, new [] {new Point3D(xMin, y + offset, z + offset), new Point3D(xMax, y + offset, z + offset)}, new [] {new Point3D(xMin, y + 1.0f - offset, z + offset), new Point3D(xMax, y + 1.0f - offset, z + offset)}, new [] {new Point3D(xMin, y + offset, z + 1.0f - offset), new Point3D(xMax, y + offset, z + 1.0f - offset)}, new [] {new Point3D(xMin, y + 1.0f - offset, z + 1.0f - offset), new Point3D(xMax, y + 1.0f - offset, z + 1.0f - offset)} }; var task = new Task(obj => { var bgw = (RayTracerTaskWorker)obj; var tracers = new List<Trace>(); foreach (var geometery in geometries) { for (var t = 0; t < geometery.Triangles.Length; t += 3) { if (checkCancel != null && checkCancel.Invoke()) return; if (incrementProgress != null) { lock (Locker) { incrementProgress.Invoke(); } } var p1 = geometery.Positions[geometery.Triangles[t]]; var p2 = geometery.Positions[geometery.Triangles[t + 1]]; var p3 = geometery.Positions[geometery.Triangles[t + 2]]; if (rotate.HasValue) { p1 = rotate.Value.Transform(p1); p2 = rotate.Value.Transform(p2); p3 = rotate.Value.Transform(p3); } foreach (var ray in testRays) { if ((p1.Y < ray[0].Y && p2.Y < ray[0].Y && p3.Y < ray[0].Y) || (p1.Y > ray[0].Y && p2.Y > ray[0].Y && p3.Y > ray[0].Y) || (p1.Z < ray[0].Z && p2.Z < ray[0].Z && p3.Z < ray[0].Z) || (p1.Z > ray[0].Z && p2.Z > ray[0].Z && p3.Z > ray[0].Z)) continue; Point3D intersect; int normal; if (MeshHelper.RayIntersetTriangleRound(p1, p2, p3, ray[0], ray[1], out intersect, out normal)) { tracers.Add(new Trace(intersect, normal)); } } } } if (tracers.Count > 1) { var order = tracers.GroupBy(t => new { t.Point, t.Face }).Select(g => g.First()).OrderBy(k => k.Point.X).ToArray(); var startCoord = roundFunc(order[0].Point.X); var endCoord = roundFunc(order[order.Length - 1].Point.X); var surfaces = 0; for (var x = startCoord; x <= endCoord; x++) { var points = order.Where(p => p.Point.X > x - startOffset && p.Point.X < x + endOffset).ToArray(); var volume = (byte)(0xff / testRays.Count * surfaces); foreach (var point in points) { if (point.Face == MeshFace.Farside) { volume += (byte)(Math.Round(Math.Abs(x + volumeOffset - point.Point.X) * 255 / testRays.Count, 0)); surfaces++; } else if (point.Face == MeshFace.Nearside) { volume -= (byte)(Math.Round(Math.Abs(x + volumeOffset - point.Point.X) * 255 / testRays.Count, 0)); surfaces--; } } // TODO: retest detailed model export. //volume = volume.RoundUpToNearest(8); modelCubic[x - xMin][bgw.Y - yMin][bgw.Z - zMin] = volume; modelMater[x - xMin][bgw.Y - yMin][bgw.Z - zMin] = materials[bgw.ModelIdx]; } if (faceMaterials[bgw.ModelIdx] != 0xff) { for (var i = 1; i < 6; i++) { if (xMin < startCoord - i && modelCubic[startCoord - i - xMin][bgw.Y - yMin][bgw.Z - zMin] == 0) { modelMater[startCoord - i - xMin][bgw.Y - yMin][bgw.Z - zMin] = faceMaterials[bgw.ModelIdx]; } if (endCoord + i < xMax && modelCubic[endCoord + i - xMin][bgw.Y - yMin][bgw.Z - zMin] == 0) { modelMater[endCoord + i - xMin][bgw.Y - yMin][bgw.Z - zMin] = faceMaterials[bgw.ModelIdx]; } } } } lock (Locker) { threadCounter--; } }, new RayTracerTaskWorker(modelIdx, 0, y, z)); task.Start(); } } // Wait for Multithread parts to finish. while (threadCounter > 0) { System.Windows.Forms.Application.DoEvents(); if (checkCancel != null && checkCancel.Invoke()) break; } GC.Collect(); } #endregion #region Y rays trace if ((traceDirection & TraceDirection.Y) == TraceDirection.Y) { Debug.WriteLine("Y Rays"); traceDirectionCount++; threadCounter = (xMax - xMin) * (zMax - zMin); for (var x = xMin; x < xMax; x++) { for (var z = zMin; z < zMax; z++) { if (checkCancel != null && checkCancel.Invoke()) { if (complete != null) complete.Invoke(); return null; } List<Point3D[]> testRays = null; if (traceType == TraceType.Odd) testRays = new List<Point3D[]> { new [] {new Point3D(x + offset, yMin, z + offset), new Point3D(x + offset, yMax, z + offset)}, new [] {new Point3D(x -0.5f + offset, yMin, z -0.5f + offset), new Point3D(x -0.5f + offset, yMax, z -0.5f + offset)}, new [] {new Point3D(x + 0.5f - offset, yMin, z -0.5f + offset), new Point3D(x + 0.5f - offset, yMax, z -0.5f + offset)}, new [] {new Point3D(x -0.5f + offset, yMin, z + 0.5f - offset), new Point3D(x -0.5f + offset, yMax, z + 0.5f - offset)}, new [] {new Point3D(x + 0.5f - offset, yMin, z + 0.5f - offset), new Point3D(x + 0.5f - offset, yMax, z + 0.5f - offset)} }; else if (traceType == TraceType.Even) testRays = new List<Point3D[]> { new [] {new Point3D(x + 0.5f - offset, yMin, z + 0.5f - offset), new Point3D(x + 0.5f - offset, yMax, z + 0.5f - offset)}, new [] {new Point3D(x + offset, yMin, z + offset), new Point3D(x + offset, yMax, z + offset)}, new [] {new Point3D(x + 1.0f - offset, yMin, z + offset), new Point3D(x + 1.0f - offset, yMax, z + offset)}, new [] {new Point3D(x + offset, yMin, z + 1.0f - offset), new Point3D(x + offset, yMax, z + 1.0f - offset)}, new [] {new Point3D(x + 1.0f - offset, yMin, z + 1.0f - offset), new Point3D(x + 1.0f - offset, yMax, z + 1.0f - offset)} }; var task = new Task(obj => { var bgw = (RayTracerTaskWorker)obj; var tracers = new List<Trace>(); foreach (var geometery in geometries) { for (var t = 0; t < geometery.Triangles.Length; t += 3) { if (checkCancel != null && checkCancel.Invoke()) return; if (incrementProgress != null) { lock (Locker) { incrementProgress.Invoke(); } } var p1 = geometery.Positions[geometery.Triangles[t]]; var p2 = geometery.Positions[geometery.Triangles[t + 1]]; var p3 = geometery.Positions[geometery.Triangles[t + 2]]; if (rotate.HasValue) { p1 = rotate.Value.Transform(p1); p2 = rotate.Value.Transform(p2); p3 = rotate.Value.Transform(p3); } foreach (var ray in testRays) { if ((p1.X < ray[0].X && p2.X < ray[0].X && p3.X < ray[0].X) || (p1.X > ray[0].X && p2.X > ray[0].X && p3.X > ray[0].X) || (p1.Z < ray[0].Z && p2.Z < ray[0].Z && p3.Z < ray[0].Z) || (p1.Z > ray[0].Z && p2.Z > ray[0].Z && p3.Z > ray[0].Z)) continue; Point3D intersect; int normal; if (MeshHelper.RayIntersetTriangleRound(p1, p2, p3, ray[0], ray[1], out intersect, out normal)) { tracers.Add(new Trace(intersect, normal)); } } } } if (tracers.Count > 1) { var order = tracers.GroupBy(t => new { t.Point, t.Face }).Select(g => g.First()).OrderBy(k => k.Point.Y).ToArray(); var startCoord = roundFunc(order[0].Point.Y); var endCoord = roundFunc(order[order.Length - 1].Point.Y); var surfaces = 0; for (var y = startCoord; y <= endCoord; y++) { var points = order.Where(p => p.Point.Y > y - startOffset && p.Point.Y < y + endOffset).ToArray(); var volume = (byte)(0xff / testRays.Count * surfaces); foreach (var point in points) { if (point.Face == MeshFace.Farside) { volume += (byte)(Math.Round(Math.Abs(y + volumeOffset - point.Point.Y) * 255 / testRays.Count, 0)); surfaces++; } else if (point.Face == MeshFace.Nearside) { volume -= (byte)(Math.Round(Math.Abs(y + volumeOffset - point.Point.Y) * 255 / testRays.Count, 0)); surfaces--; } } if (traceDirectionCount > 1) { var prevolumme = modelCubic[bgw.X - xMin][y - yMin][bgw.Z - zMin]; if (prevolumme != 0) { // average with the pre-existing X volume. volume = (byte)Math.Round(((float)prevolumme + (float)volume) / (float)traceDirectionCount, 0); } } //volume = volume.RoundUpToNearest(8); modelCubic[bgw.X - xMin][y - yMin][bgw.Z - zMin] = volume; modelMater[bgw.X - xMin][y - yMin][bgw.Z - zMin] = materials[bgw.ModelIdx]; } if (faceMaterials[bgw.ModelIdx] != 0xff) { for (var i = 1; i < 6; i++) { if (yMin < startCoord - i && modelCubic[bgw.X - xMin][startCoord - i - yMin][bgw.Z - zMin] == 0) { modelMater[bgw.X - xMin][startCoord - i - yMin][bgw.Z - zMin] = faceMaterials[bgw.ModelIdx]; } if (endCoord + i < yMax && modelCubic[bgw.X - xMin][endCoord + i - yMin][bgw.Z - zMin] == 0) { modelMater[bgw.X - xMin][endCoord + i - yMin][bgw.Z - zMin] = faceMaterials[bgw.ModelIdx]; } } } } lock (Locker) { threadCounter--; } }, new RayTracerTaskWorker(modelIdx, x, 0, z)); task.Start(); } } // Wait for Multithread parts to finish. while (threadCounter > 0) { System.Windows.Forms.Application.DoEvents(); if (checkCancel != null && checkCancel.Invoke()) break; } GC.Collect(); } #endregion #region Z ray trace if ((traceDirection & TraceDirection.Z) == TraceDirection.Z) { Debug.WriteLine("Z Rays"); traceDirectionCount++; threadCounter = (xMax - xMin) * (yMax - yMin); for (var x = xMin; x < xMax; x++) { for (var y = yMin; y < yMax; y++) { if (checkCancel != null && checkCancel.Invoke()) { if (complete != null) complete.Invoke(); return null; } List<Point3D[]> testRays = null; if (traceType == TraceType.Odd) testRays = new List<Point3D[]> { new [] {new Point3D(x + offset, y + offset, zMin), new Point3D(x + offset, y + offset, zMax)}, new [] {new Point3D(x -0.5f + offset, y -0.5f + offset, zMin), new Point3D(x -0.5f + offset, y -0.5f + offset, zMax)}, new [] {new Point3D(x + 0.5f - offset, y -0.5f + offset, zMin), new Point3D(x + 0.5f - offset, y -0.5f + offset, zMax)}, new [] {new Point3D(x -0.5f + offset, y + 0.5f - offset, zMin), new Point3D(x -0.5f + offset, y + 0.5f - offset, zMax)}, new [] {new Point3D(x + 0.5f - offset, y + 0.5f - offset, zMin), new Point3D(x + 0.5f - offset, y + 0.5f - offset, zMax)} }; else if (traceType == TraceType.Even) testRays = new List<Point3D[]> { new [] {new Point3D(x + 0.5f - offset, y + 0.5f - offset, zMin), new Point3D(x + 0.5f - offset, y + 0.5f - offset, zMax)}, new [] {new Point3D(x + offset, y + offset, zMin), new Point3D(x + offset, y + offset, zMax)}, new [] {new Point3D(x + 1.0f - offset, y + offset, zMin), new Point3D(x + 1.0f - offset, y + offset, zMax)}, new [] {new Point3D(x + offset, y + 1.0f - offset, zMin), new Point3D(x + offset, y + 1.0f - offset, zMax)}, new [] {new Point3D(x + 1.0f - offset, y + 1.0f - offset, zMin), new Point3D(x + 1.0f - offset, y + 1.0f - offset, zMax)} }; var task = new Task(obj => { var bgw = (RayTracerTaskWorker)obj; var tracers = new List<Trace>(); foreach (var geometery in geometries) { for (var t = 0; t < geometery.Triangles.Length; t += 3) { if (checkCancel != null && checkCancel.Invoke()) return; if (incrementProgress != null) { lock (Locker) { incrementProgress.Invoke(); } } var p1 = geometery.Positions[geometery.Triangles[t]]; var p2 = geometery.Positions[geometery.Triangles[t + 1]]; var p3 = geometery.Positions[geometery.Triangles[t + 2]]; if (rotate.HasValue) { p1 = rotate.Value.Transform(p1); p2 = rotate.Value.Transform(p2); p3 = rotate.Value.Transform(p3); } foreach (var ray in testRays) { if ((p1.X < ray[0].X && p2.X < ray[0].X && p3.X < ray[0].X) || (p1.X > ray[0].X && p2.X > ray[0].X && p3.X > ray[0].X) || (p1.Y < ray[0].Y && p2.Y < ray[0].Y && p3.Y < ray[0].Y) || (p1.Y > ray[0].Y && p2.Y > ray[0].Y && p3.Y > ray[0].Y)) continue; Point3D intersect; int normal; if (MeshHelper.RayIntersetTriangleRound(p1, p2, p3, ray[0], ray[1], out intersect, out normal)) { tracers.Add(new Trace(intersect, normal)); } } } } if (tracers.Count > 1) { var order = tracers.GroupBy(t => new { t.Point, t.Face }).Select(g => g.First()).OrderBy(k => k.Point.Z).ToArray(); var startCoord = roundFunc(order[0].Point.Z); var endCoord = roundFunc(order[order.Length - 1].Point.Z); var surfaces = 0; for (var z = startCoord; z <= endCoord; z++) { var points = order.Where(p => p.Point.Z > z - startOffset && p.Point.Z < z + endOffset).ToArray(); var volume = (byte)(0xff / testRays.Count * surfaces); foreach (var point in points) { if (point.Face == MeshFace.Farside) { volume += (byte)(Math.Round(Math.Abs(z + volumeOffset - point.Point.Z) * 255 / testRays.Count, 0)); surfaces++; } else if (point.Face == MeshFace.Nearside) { volume -= (byte)(Math.Round(Math.Abs(z + volumeOffset - point.Point.Z) * 255 / testRays.Count, 0)); surfaces--; } } if (traceDirectionCount > 1) { var prevolumme = modelCubic[bgw.X - xMin][bgw.Y - yMin][z - zMin]; if (prevolumme != 0) { // average with the pre-existing X and Y volumes. volume = (byte)Math.Round((((float)prevolumme * (traceDirectionCount - 1)) + (float)volume) / (float)traceDirectionCount, 0); } } //volume = volume.RoundUpToNearest(8); modelCubic[bgw.X - xMin][bgw.Y - yMin][z - zMin] = volume; modelMater[bgw.X - xMin][bgw.Y - yMin][z - zMin] = materials[bgw.ModelIdx]; } if (faceMaterials[bgw.ModelIdx] != 0xff) { for (var i = 1; i < 6; i++) { if (zMin < startCoord - i && modelCubic[bgw.X - xMin][bgw.Y - yMin][startCoord - i - zMin] == 0) { modelMater[bgw.X - xMin][bgw.Y - yMin][startCoord - i - zMin] = faceMaterials[bgw.ModelIdx]; } if (endCoord + i < zMax && modelCubic[bgw.X - xMin][bgw.Y - yMin][endCoord + i - zMin] == 0) { modelMater[bgw.X - xMin][bgw.Y - yMin][endCoord + i - zMin] = faceMaterials[bgw.ModelIdx]; } } } } lock (Locker) { threadCounter--; } }, new RayTracerTaskWorker(modelIdx, x, y, 0)); task.Start(); } } // Wait for Multithread parts to finish. while (threadCounter > 0) { System.Windows.Forms.Application.DoEvents(); if (checkCancel != null && checkCancel.Invoke()) break; } GC.Collect(); } #endregion if (checkCancel != null && checkCancel.Invoke()) { if (complete != null) complete.Invoke(); return null; } #region merge individual model results into final for (var x = 0; x < xCount; x++) { for (var y = 0; y < yCount; y++) { for (var z = 0; z < zCount; z++) { if (modelMater[x][y][z] == 0xff && modelCubic[x][y][z] != 0) { finalCubic[x][y][z] = (byte)Math.Max(finalCubic[x][y][z] - modelCubic[x][y][z], 0); } else if (modelCubic[x][y][z] != 0) { finalCubic[x][y][z] = Math.Max(finalCubic[x][y][z], modelCubic[x][y][z]); finalMater[x][y][z] = modelMater[x][y][z]; } else if (finalCubic[x][y][z] == 0 && finalMater[x][y][z] == 0 && modelMater[x][y][z] != 0xff) { finalMater[x][y][z] = modelMater[x][y][z]; } } } } #endregion } // end models #endregion if (checkCancel != null && checkCancel.Invoke()) { if (complete != null) complete.Invoke(); return null; } var size = new Vector3I(xCount, yCount, zCount); // TODO: at the moment the Mesh list is not complete, so the faceMaterial setting is kind of vague. var defaultMaterial = mappedMesh[0].Material; // Use the FaceMaterial from the first Mesh in the object list. var faceMaterial = mappedMesh[0].FaceMaterial; // Use the FaceMaterial from the first Mesh in the object list. var action = (Action<MyVoxelBuilderArgs>)delegate(MyVoxelBuilderArgs e) { e.Volume = finalCubic[e.CoordinatePoint.X][e.CoordinatePoint.Y][e.CoordinatePoint.Z]; e.Material = SpaceEngineersCore.Resources.GetMaterialName(finalMater[e.CoordinatePoint.X][e.CoordinatePoint.Y][e.CoordinatePoint.Z]); }; var voxelMap = MyVoxelBuilder.BuildAsteroid(true, size, defaultMaterial, faceMaterial, action); if (complete != null) complete.Invoke(); return voxelMap; }