public static FinalizeAction finalizeAction = Finalize; //class identified for FinalizeData public static void Finalize(TileData data, StopToken stop) { if (stop != null && stop.stop) { return; } int upscale = 1; int margins = data.area.Margins; int matrixRes = (data.heights.rect.size.x - margins * 2 - 1) * upscale + margins * 2 * upscale + 1; if (stop != null && stop.stop) { return; } Matrix matrix = data.heights; //clamping heights to 0-1 (otherwise culing issues can occur) matrix.Clamp01(); //2Darray resolution (this should still match our 69x69 size input as well) int arrRes = matrix.rect.size.x - margins * upscale * 2; //splits number (used for SetHeightsDelayLOD and Texture) int splitSize = SplitSizeRef = data.globals.heightSplit; int numSplits = arrRes / splitSize; if (arrRes % splitSize != 0) { numSplits++; } IApplyData applyData; // we do some comparisons on this float[,] heights2Dfull = new float[arrRes, arrRes]; // we do some our data operation on this bool[,] bool2Dfull = new bool[arrRes - 1, arrRes - 1]; // We need to honour multiple outputs // sigh. the horror story negative logic. // Why did unity make FALSE = hole is TRUE. //And, yes, I know why, but still. bool firstPass = true; foreach ((HoleOutMark1 output, MatrixWorld product, MatrixWorld biomeMask) in data.Outputs <HoleOutMark1, MatrixWorld, MatrixWorld>(typeof(HoleOutMark1), inSubs: true)) { Matrix othermatrix = product; // This is our holes then // product. Coord heightsOffset = othermatrix.rect.offset + margins * upscale; Matrix matrixbool = othermatrix; matrix.ExportHeights(heights2Dfull, matrix.rect.offset + margins * upscale); Coord heightsSize = new Coord(heights2Dfull.GetLength(1), heights2Dfull.GetLength(0)); //x and z swapped CoordRect heightsRect = new CoordRect(heightsOffset, heightsSize); CoordRect intersection = CoordRect.Intersected(matrixbool.rect, heightsRect); Coord min = intersection.Min; Coord max = intersection.Max; for (int x = min.x; x < max.x - 1; x++) { for (int z = min.z; z < max.z - 1; z++) { int matrixPos = (z - matrixbool.rect.offset.z) * matrixbool.rect.size.x + x - matrixbool.rect.offset.x; int heightsPosX = x - heightsRect.offset.x; int heightsPosZ = z - heightsRect.offset.z; float val = matrixbool.arr[matrixPos]; bool result = true; // TERRAIN // The samples are represented as bool values: true for surface and false for hole bool test1 = bool2Dfull[heightsPosZ, heightsPosX]; bool test2 = val < float.Epsilon; // It is already true if (test1 || firstPass) // just slap in the value { result = test2; } else // Last time it was false or we didn't set it. and FALSE means ADD A HOLE { result = test1 && test2; } bool2Dfull[heightsPosZ, heightsPosX] = result; } } firstPass = false; } applyData = new ApplySetData() { heights2D = heights2Dfull, height = data.globals.height, bools2D = bool2Dfull }; //pushing to apply if (stop != null && stop.stop) { return; } Graph.OnOutputFinalized?.Invoke(typeof(HoleOutMark1), data, applyData, stop); data.MarkApply(applyData); #if MM_DEBUG Log.Add("HOLEOut Finalized"); #endif }
public static FinalizeAction finalizeAction = Finalize; //class identified for FinalizeData public static void Finalize(TileData data, StopToken stop) { //blending all biomes in data.height matrix if (data.heights == null || data.heights.rect.size != data.area.full.rect.size || data.heights.worldPos != data.area.full.worldPos || data.heights.worldSize != data.area.full.worldSize) { data.heights = new MatrixWorld(data.area.full.rect, data.area.full.worldPos, data.area.full.worldSize, data.globals.height); } data.heights.worldSize.y = data.globals.height; data.heights.Fill(0); foreach ((HeightOutput200 output, MatrixWorld product, MatrixWorld biomeMask) in data.finalize.ProductSets <HeightOutput200, MatrixWorld, MatrixWorld>(finalizeAction, data.subDatas)) { if (stop != null && stop.stop) { return; } for (int a = 0; a < data.heights.arr.Length; a++) { float val = product.arr[a]; float biomeVal = biomeMask != null ? biomeMask.arr[a] : 1; data.heights.arr[a] += val * biomeVal; } } //creating upscaled/blurred height matrix Interpolation interpolation = data.globals.heightInterpolation; Matrix matrix = GetInterpolated(data.heights, interpolation); //determining resolutions int upscale = matrix.rect.size.x / data.heights.rect.size.x; int margins = data.area.Margins * upscale; int res = matrix.rect.size.x - margins * 2; int splitSize = data.globals.heightSplit; int numSplits = res / splitSize; if (res % splitSize != 0) { numSplits++; } //getting apply data ApplyType applyType = data.isDraft ? data.globals.heightDraftApply : data.globals.heightMainApply; IApplyData applyData; if (applyType == ApplyType.SetHeights) { float[,] heights2Dfull = new float[res, res]; matrix.ExportHeights(heights2Dfull, matrix.rect.offset + margins); applyData = new ApplySetData() { heights2D = heights2Dfull, height = data.globals.height }; } else if (applyType == ApplyType.SetHeightsDelayLOD) { float[][,] height2DSplits = new float[numSplits][, ]; int offset = 0; for (int i = 0; i < numSplits; i++) { int spaceLeft = res - offset; int currSplitSize = Mathf.Min(splitSize, res - offset); float[,] heights2D = new float[currSplitSize, res]; Coord heights2Dcoord = new Coord( matrix.rect.offset.x + margins, matrix.rect.offset.z + margins + offset); matrix.ExportHeights(heights2D, heights2Dcoord); height2DSplits[i] = heights2D; offset += currSplitSize; } applyData = new ApplySplitData() { heights2DSplits = height2DSplits, height = data.globals.height }; } #if UNITY_2019_1_OR_NEWER else //if TextureToHeightmap { byte[] bytes = new byte[res * res * 4]; matrix.ExportRawFloat(bytes, matrix.rect.offset + margins, new Coord(res, res), mult: 0.5f); //not coord(margins) since matrix rect has -margins offset //somehow requires halved values applyData = new ApplyTexData() { res = res, margins = margins, splitSize = splitSize, height = data.globals.height, texBytes = bytes }; }
public static FinalizeAction finalizeAction = Finalize; //class identified for FinalizeData public static void Finalize(TileData data, StopToken stop) { //blending all biomes in data.height matrix if (data.heights == null || data.heights.rect.size != data.area.full.rect.size || data.heights.worldPos != (Vector3)data.area.full.worldPos || data.heights.worldSize != (Vector3)data.area.full.worldSize) { data.heights = new MatrixWorld(data.area.full.rect, data.area.full.worldPos, data.area.full.worldSize, data.globals.height); } data.heights.worldSize.y = data.globals.height; data.heights.Fill(0); foreach ((HeightOutput200 output, MatrixWorld product, MatrixWorld biomeMask) in data.Outputs <HeightOutput200, MatrixWorld, MatrixWorld> (typeof(HeightOutput200), inSubs:true)) { if (data.heights == null) //height output not generated or received null result { return; } for (int a = 0; a < data.heights.arr.Length; a++) { if (stop != null && stop.stop) { return; } float val = product.arr[a]; float biomeVal = biomeMask != null ? biomeMask.arr[a] : 1; data.heights.arr[a] += val * biomeVal; } } //determining resolutions if (stop != null && stop.stop) { return; } Interpolation interpolation = data.globals.heightInterpolation; int upscale = GetUpscale(interpolation); int margins = data.area.Margins; int matrixRes = (data.heights.rect.size.x - margins * 2 - 1) * upscale + margins * 2 * upscale + 1; //creating upscaled/blurred height matrix if (stop != null && stop.stop) { return; } Matrix matrix; switch (interpolation) { default: matrix = data.heights; break; case Interpolation.Smooth: matrix = new Matrix(data.heights); MatrixOps.GaussianBlur(matrix, 0.5f); break; //case Interpolation.Scale2X: // matrix = new Matrix( new CoordRect(data.heights.rect.offset, new Coord(matrixRes)) ); // MatrixOps.UpscaleFast(data.heights, matrix); // MatrixOps.GaussianBlur(matrix, 0.5f); //upscaleFast interpolates linear, so each new vert is exactly between the old ones // break; //nah, summary effect is better with classic resize case Interpolation.Scale4X: case Interpolation.Scale2X: matrix = new Matrix(new CoordRect(data.heights.rect.offset, new Coord(matrixRes))); MatrixOps.Resize(data.heights, matrix); break; } //clamping heights to 0-1 (otherwise culing issues can occur) matrix.Clamp01(); //2Darray resolution and int arrRes = matrix.rect.size.x - margins * upscale * 2; //splits number (used for SetHeightsDelayLOD and Texture) int splitSize = data.globals.heightSplit; int numSplits = arrRes / splitSize; if (arrRes % splitSize != 0) { numSplits++; } //getting apply data ApplyType applyType = data.isDraft ? data.globals.heightDraftApply : data.globals.heightMainApply; IApplyData applyData; if (applyType == ApplyType.SetHeights) { float[,] heights2Dfull = new float[arrRes, arrRes]; matrix.ExportHeights(heights2Dfull, matrix.rect.offset + margins * upscale); applyData = new ApplySetData() { heights2D = heights2Dfull, height = data.globals.height }; } else if (applyType == ApplyType.SetHeightsDelayLOD) { float[][,] height2DSplits = new float[numSplits][, ]; int offset = 0; for (int i = 0; i < numSplits; i++) { int spaceLeft = arrRes - offset; int currSplitSize = Mathf.Min(splitSize, arrRes - offset); float[,] heights2D = new float[currSplitSize, arrRes]; Coord heights2Dcoord = new Coord( matrix.rect.offset.x + margins * upscale, matrix.rect.offset.z + margins * upscale + offset); matrix.ExportHeights(heights2D, heights2Dcoord); height2DSplits[i] = heights2D; offset += currSplitSize; } applyData = new ApplySplitData() { heights2DSplits = height2DSplits, height = data.globals.height }; } #if UNITY_2019_1_OR_NEWER else //if TextureToHeightmap { byte[] bytes = new byte[arrRes * arrRes * 4]; float ushortEpsilon = 1f / 65535; //since setheights is using not full ushort range, but range-1 matrix.ExportRawFloat(bytes, matrix.rect.offset + margins * upscale, new Coord(arrRes, arrRes), mult: 0.5f - ushortEpsilon); //not coord(margins) since matrix rect has -margins offset //somehow requires halved values applyData = new ApplyTexData() { res = arrRes, margins = margins, splitSize = splitSize, height = data.globals.height, texBytes = bytes }; }