public NPVoxBox GetAffectedArea(uint boneMask) { NPVoxBox affectedArea = null; foreach (NPVoxCoord coord in Enumerate()) { if (IsInBoneMask(coord, boneMask)) { if (affectedArea == null) { affectedArea = new NPVoxBox(coord, coord); } else { affectedArea.EnlargeToInclude(coord); } } } if (affectedArea == null) { Debug.Log("Bone Mask did not produce any valid affected area"); affectedArea = NPVoxBox.INVALID; } return(affectedArea); }
override protected NPVoxModel CreateProduct(NPVoxModel reuse = null) { if (Input == null) { return(NPVoxModel.NewInvalidInstance(reuse, "No Input Setup")); } NPVoxModel model = ((NPVoxIModelFactory)Input).GetProduct(); // backwards compatibility if (TryToResolveConflicts == false) { TryToResolveConflicts = true; ResolveConflictMethod = NPVoxModelTransformationUtil.ResolveConflictMethodType.NONE; } // shift afftected area if the parent bounding box changed { NPVoxBox parentBounds = model.BoundingBox; if (!lastParentModelBounds.Equals(NPVoxBox.INVALID) && !lastParentModelBounds.Equals(parentBounds)) { sbyte deltaX = (sbyte)((parentBounds.Right - lastParentModelBounds.Right) / 2); sbyte deltaY = (sbyte)((parentBounds.Up - lastParentModelBounds.Up) / 2); sbyte deltaZ = (sbyte)((parentBounds.Forward - lastParentModelBounds.Forward) / 2); NPVoxCoord delta = new NPVoxCoord(deltaX, deltaY, deltaZ); AffectedArea = new NPVoxBox(AffectedArea.LeftDownBack + delta, AffectedArea.RightUpForward + delta); // Debug.Log("Moving affected area by + " + deltaX + " " + deltaY + " " + deltaZ); } lastParentModelBounds = parentBounds; } return(NPVoxModelTransformationUtil.MatrixTransform(model, AffectedArea, Matrix, PivotOffset, ResolveConflictMethod, reuse)); }
public static NPVoxBoneModel MatrixTransform(NPVoxBoneModel sourceModel, NPVoxBox affectedArea, uint boneMask, Matrix4x4 matrix, Vector3 pivot, ResolveConflictMethodType resolveConflictMethod = ResolveConflictMethodType.CLOSEST, NPVoxModel reuse = null, byte markColor = 0) { Vector3 pivotPoint = affectedArea.SaveCenter + pivot; Matrix4x4 transformMatrix = (Matrix4x4.TRS(pivotPoint, Quaternion.identity, Vector3.one) * matrix) * Matrix4x4.TRS(-pivotPoint, Quaternion.identity, Vector3.one); return(Transform(sourceModel, affectedArea, boneMask, transformMatrix, resolveConflictMethod, reuse, markColor)); }
public void DeleteBox(NPVoxBone bone, NPVoxBox box) { Undo.RecordObject(this, "Delete Box"); List <NPVoxBox> boxes = GetBoxes(bone); boxes.Remove(box); }
private NPVoxModel CreateSlicedModel(NPVoxModel source, NPVoxModel reuse) { NPVoxBox targetBox = slice.Clone(); NPVoxBox sourceBounds = source.BoundingBox; targetBox.Clamp(source.BoundingBox); NPVoxCoord origin = targetBox.LeftDownBack; NPVoxModel model = NPVoxModel.NewInstance(source, targetBox.Size, reuse); int numVoxels = 0; foreach (NPVoxCoord coord in targetBox.Enumerate()) { if (source.HasVoxel(coord)) { numVoxels++; model.SetVoxel(coord - origin, source.GetVoxel(coord)); } } model.NumVoxels = numVoxels; model.Colortable = source.Colortable; return(model); }
public void Contains_ShouldReturnFalseWhenInBox() { NPVoxBox sut = NPVoxBox.FromCenterSize(new NPVoxCoord(2, 2, 2), new NPVoxCoord(3, 3, 3)); Assert.IsFalse(sut.Contains(new NPVoxCoord(0, 0, 0))); Assert.IsFalse(sut.Contains(new NPVoxCoord(4, 4, 4))); }
public void Clamp(NPVoxBox max) { if (Left < max.Left) { Left = max.Left; } if (Right > max.Right) { Right = max.Right; } if (Down < max.Down) { Down = max.Down; } if (Up > max.Up) { Up = max.Up; } if (Back < max.Back) { Back = max.Back; } if (Forward > max.Forward) { Forward = max.Forward; } }
public void Size_ShouldReturnCorrectSize() { NPVoxBox sut = new NPVoxBox(new NPVoxCoord(1, 1, 1), new NPVoxCoord(4, 4, 4)); Assert.AreEqual(4, sut.Size.X); Assert.AreEqual(4, sut.Size.Y); Assert.AreEqual(4, sut.Size.Z); }
public void Contains_ShouldReturnTrueWhenInBox() { NPVoxBox sut = NPVoxBox.FromCenterSize(new NPVoxCoord(2, 2, 2), new NPVoxCoord(3, 3, 3)); Assert.IsTrue(sut.Contains(new NPVoxCoord(2, 2, 2))); Assert.IsTrue(sut.Contains(new NPVoxCoord(1, 1, 1))); Assert.IsTrue(sut.Contains(new NPVoxCoord(3, 3, 3))); }
public void Clamp_ShouldReturnClampedVoxBox() { NPVoxModel sut = NPVoxModel.NewInstance(new NPVoxCoord(3, 3, 3)); NPVoxBox box = sut.Clamp(new NPVoxBox(new NPVoxCoord(-2, -2, -2), new NPVoxCoord(6, 6, 6))); Assert.AreEqual(new NPVoxCoord(2, 2, 2), box.RightUpForward); Assert.AreEqual(new NPVoxCoord(0, 0, 0), box.LeftDownBack); }
public void Center_ShouldReturnCorrectCenter() { NPVoxBox sut = new NPVoxBox(new NPVoxCoord(1, 1, 1), new NPVoxCoord(3, 3, 3)); Assert.AreEqual(2, sut.Center.X); Assert.AreEqual(2, sut.Center.Y); Assert.AreEqual(2, sut.Center.Z); }
public override bool Equals(System.Object other) { NPVoxBox o = other as NPVoxBox; if (o == null) { return(false); } return(o.LeftDownBack.Equals(this.leftDownBack) && o.RightUpForward.Equals(this.rightUpForward)); }
public NPVoxBox Clamp(NPVoxBox box) { if (IsInside(box.LeftDownBack) && IsInside(box.RightUpForward)) { return(box); } else { return(new NPVoxBox(Clamp(box.LeftDownBack), Clamp(box.RightUpForward))); } }
private bool DrawBoxSelection() { if (viewModel.PreviousModelFactory == null) { return(false); } NPVoxModel previousTransformedModel = viewModel.PreviousModelFactory.GetProduct(); NPVoxToUnity npVoxToUnity = new NPVoxToUnity(previousTransformedModel, viewModel.Animation.MeshFactory.VoxelSize); List <NPVoxBox> boxes = viewModel.GetNonEditableBoxes(); if (boxes != null) { foreach (NPVoxBox b in boxes) { NPVoxHandles.DrawBoxSelection(npVoxToUnity, b, false); } } if (!viewModel.IsAreaSelectionActive()) { return(false); } NPVoxBox box = viewModel.GetAffectedBox(); if (Event.current.shift) { // affected area picker NPVoxCoord someCoord = box.RoundedCenter; NPVoxCoord someNewCoord = NPVoxHandles.VoxelPicker(new NPVoxToUnity(previousTransformedModel, viewModel.Animation.MeshFactory.VoxelSize), someCoord, 0, ((NPVoxAnimationEditorSession)target).previewFilter.transform); if (!someCoord.Equals(someNewCoord)) { viewModel.ChangeAffectedBox(new NPVoxBox(someNewCoord, someNewCoord)); } } else { // affected area box NPVoxBox newBox = NPVoxHandles.DrawBoxSelection(npVoxToUnity, box); if (!newBox.Equals(box)) { viewModel.ChangeAffectedBox(newBox); } } return(true); }
public void FromCenterSize_ShouldConstructCorrectBox() { NPVoxBox sut = NPVoxBox.FromCenterSize(new NPVoxCoord(2, 2, 2), new NPVoxCoord(3, 3, 3)); Assert.AreEqual(2, sut.Center.X); Assert.AreEqual(2, sut.Center.Y); Assert.AreEqual(2, sut.Center.Z); Assert.AreEqual(3, sut.Size.X); Assert.AreEqual(3, sut.Size.Y); Assert.AreEqual(3, sut.Size.Z); Assert.AreEqual(1, sut.LeftDownBack.X); Assert.AreEqual(1, sut.LeftDownBack.Y); Assert.AreEqual(1, sut.LeftDownBack.Z); Assert.AreEqual(3, sut.RightUpForward.X); Assert.AreEqual(3, sut.RightUpForward.Y); Assert.AreEqual(3, sut.RightUpForward.Z); }
public static NPVoxCoord GetNearbyCoord(NPVoxModel model, Vector3 saveCoord, ResolveConflictMethodType resolveConflictMethod) { NPVoxCoord favoriteCoord = NPVoxCoordUtil.ToCoord(saveCoord); if (model.HasVoxelFast(favoriteCoord)) { NPVoxBox box = new NPVoxBox(favoriteCoord - NPVoxCoord.ONE, favoriteCoord + NPVoxCoord.ONE); favoriteCoord = NPVoxCoord.INVALID; float nearestDistance = 9999f; int favoriteEnclosingVoxelCount = -1; foreach (NPVoxCoord currentTestCoord in box.Enumerate()) { if (model.IsInside(currentTestCoord) && !model.HasVoxelFast(currentTestCoord)) { if (resolveConflictMethod == ResolveConflictMethodType.CLOSEST) { float distance = Vector3.Distance(NPVoxCoordUtil.ToVector(currentTestCoord), saveCoord); if (distance < nearestDistance) { nearestDistance = distance; favoriteCoord = currentTestCoord; } } else { int enclosingVoxelCount = 0; NPVoxBox enclosingBoxCheck = new NPVoxBox(currentTestCoord - NPVoxCoord.ONE, currentTestCoord + NPVoxCoord.ONE); foreach (NPVoxCoord enclosingTestCoord in enclosingBoxCheck.Enumerate()) { if (model.IsInside(currentTestCoord) && model.HasVoxelFast(currentTestCoord)) { enclosingVoxelCount++; } } if (enclosingVoxelCount > favoriteEnclosingVoxelCount) { enclosingVoxelCount = favoriteEnclosingVoxelCount; favoriteCoord = currentTestCoord; } } } } } return(favoriteCoord); }
public static NPVoxModel CreateWithNewSize(NPVoxModel source, NPVoxBox newBounds, NPVoxModel reuse = null) { NPVoxCoord delta; NPVoxCoord newSize; CalculateResizeOffset(source.BoundingBox, newBounds, out delta, out newSize); NPVoxModel newModel = NPVoxModel.NewInstance(source, newSize, reuse); newModel.NumVoxels = source.NumVoxels; newModel.NumVoxelGroups = source.NumVoxelGroups; newModel.Colortable = source.Colortable != null ? (Color32[])source.Colortable.Clone() : null; newModel.Sockets = source.Sockets != null ? (NPVoxSocket[])source.Sockets.Clone() : null; if (newModel.Sockets != null) { for (int i = 0; i < newModel.Sockets.Length; i++) { newModel.Sockets[i].Anchor = newModel.Sockets[i].Anchor + delta; } } bool hasVoxelGroups = source.HasVoxelGroups(); if (hasVoxelGroups) { newModel.InitVoxelGroups(); newModel.NumVoxelGroups = source.NumVoxelGroups; } foreach (NPVoxCoord coord in source.EnumerateVoxels()) { NPVoxCoord targetCoord = coord + delta; newModel.SetVoxel(targetCoord, source.GetVoxel(coord)); if (hasVoxelGroups) { newModel.SetVoxelGroup(targetCoord, source.GetVoxelGroup(coord)); } } // newModel.InvalidateVoxelCache(); return(newModel); }
public static void CalculateResizeOffset(NPVoxBox parentBounds, NPVoxBox thisBounds, out NPVoxCoord delta, out NPVoxCoord size) { if (!thisBounds.Equals(parentBounds)) { size = parentBounds.Size; bool isOverflow = false; sbyte deltaX = (sbyte)(Mathf.Max(parentBounds.Left - thisBounds.Left, thisBounds.Right - parentBounds.Right)); if ((int)deltaX * 2 + (int)size.X > 126) // check for overflow { deltaX = (sbyte)((float)deltaX - Mathf.Ceil(((float)deltaX * 2f + (float)size.X) - 126) / 2f); isOverflow = true; } sbyte deltaY = (sbyte)(Mathf.Max(parentBounds.Down - thisBounds.Down, thisBounds.Up - parentBounds.Up)); if ((int)deltaY * 2 + (int)size.Y > 126) // check for overflow { deltaY = (sbyte)((float)deltaY - Mathf.Ceil(((float)deltaY * 2f + (float)size.Y) - 126) / 2f); isOverflow = true; } sbyte deltaZ = (sbyte)(Mathf.Max(parentBounds.Back - thisBounds.Back, thisBounds.Forward - parentBounds.Forward)); if ((int)deltaZ * 2 + (int)size.Z > 126) // check for overflow { deltaZ = (sbyte)((float)deltaZ - Mathf.Ceil(((float)deltaZ * 2f + (float)size.Z) - 126) / 2f); isOverflow = true; } delta = new NPVoxCoord(deltaX, deltaY, deltaZ); size = size + delta + delta; if (isOverflow) { Debug.LogWarning("Transformed Model is large, clamped to " + size); } } else { size = parentBounds.Size; delta = NPVoxCoord.ZERO; } }
public void ChangeAffectedBox(NPVoxBox newBox) { ChangeTransformation((NPVoxISceneEditable transformer) => { if (transformer is NPVoxSkeletonBuilder) { if (((NPVoxSkeletonBuilder)transformer).CurrentEditedBox != null) { ((NPVoxSkeletonBuilder)transformer).CurrentEditedBox.LeftDownBack = newBox.LeftDownBack; ((NPVoxSkeletonBuilder)transformer).CurrentEditedBox.RightUpForward = newBox.RightUpForward; } return(false); } else { ((NPVoxModelTransformer)transformer).AffectedArea = newBox; return(true); } } ); }
override protected NPVoxModel CreateProduct(NPVoxModel reuse = null) { if (Input == null) { return(NPVoxModel.NewInvalidInstance(reuse, "No Input Setup")); } NPVoxBoneModel model = ((NPVoxIModelFactory)Input).GetProduct() as NPVoxBoneModel; if (model == null) { return(NPVoxModel.NewInvalidInstance(reuse, "Can only transform bone models")); } // hack to center pivot on selected bones if (regenerateName) { RecenterBonePivot(model); } RegenerateName(model); NPVoxBox affectedBox = GetAffectedBox(); if (affectedBox.Equals(NPVoxBox.INVALID)) { NPVoxModel newInstance = NPVoxModel.NewInstance(model, reuse); newInstance.CopyOver(model); newInstance.RecalculateNumVoxels(true); return(newInstance); } else { reuse = NPVoxModelTransformationUtil.MatrixTransform(model, affectedBox, boneMask, Matrix, PivotOffset, ResolveConflictMethod, reuse); reuse.RecalculateNumVoxels(true); return(reuse); } }
public static Bounds DrawBoundsSelection(Bounds previous, Vector3 cellOffset, float cellSize) { NPVoxToUnity npVoxToUnity = new NPVoxToUnity(null, Vector3.one * cellSize, cellOffset - Vector3.one * 0.5f * cellSize); NPVoxBox previousBox = new NPVoxBox( npVoxToUnity.ToVoxCoord(previous.min + Vector3.one * cellSize / 2), npVoxToUnity.ToVoxCoord(previous.max - Vector3.one * cellSize / 2) ); NPVoxBox newBox = NPVoxHandles.DrawBoxSelection(npVoxToUnity, previousBox, true); if (!previousBox.Equals(newBox)) { Bounds bounds = new Bounds(Vector3.zero, Vector3.zero); bounds.SetMinMax( npVoxToUnity.ToUnityPosition(newBox.LeftDownBack) - Vector3.one * cellSize / 2, npVoxToUnity.ToUnityPosition(newBox.RightUpForward) + Vector3.one * cellSize / 2 ); return(bounds); } else { return(previous); } }
public NPVoxBox GetAffectedBox() { if (Input == null) { Debug.Log("Input was NULL"); return(NPVoxBox.INVALID); } NPVoxBoneModel affectedAreaSourceModel = (Input as NPVoxIModelFactory).GetProduct() as NPVoxBoneModel; if (affectedAreaSourceModel == null) { Debug.Log("Input did not procue a bone model"); return(NPVoxBox.INVALID); } if (affectedAreaSourceModel == this.lastAffectedAreaSourceModel && this.lastAffectedAreaSourceModelVersion == affectedAreaSourceModel.GetVersion() && lastAffectedAreaBoneMask == this.boneMask) { return(lastAffectedArea); } NPVoxBox affectedArea = affectedAreaSourceModel.GetAffectedArea(boneMask); this.lastAffectedArea = affectedArea; this.lastAffectedAreaSourceModelVersion = affectedAreaSourceModel.GetVersion(); this.lastAffectedAreaSourceModel = affectedAreaSourceModel; this.lastAffectedAreaBoneMask = this.boneMask; if (lastAffectedArea == null) { lastAffectedArea = NPVoxBox.INVALID; } // Debug.Log("Recalculate Affected Area"); return(lastAffectedArea); }
public void AddTransformation(NPVoxCompositeProcessorBase <NPVoxIModelFactory, NPVoxModel> template = null) { Undo.RecordObjects(new Object[] { this, animation }, "Add Transformation "); NPVoxCompositeProcessorBase <NPVoxIModelFactory, NPVoxModel> transformer; if (template != null) { transformer = (NPVoxCompositeProcessorBase <NPVoxIModelFactory, NPVoxModel>)template.Clone(); } else { transformer = (NPVoxModelTransformer)NPVoxModelTransformer.CreateInstance(typeof(NPVoxModelTransformer)); if (this.CurrentModelFactory != null && ((NPVoxModelTransformer)transformer)) { NPVoxModel model = this.CurrentModelFactory.GetProduct(); UnityEngine.Assertions.Assert.IsNotNull(model); ((NPVoxModelTransformer)transformer).AffectedArea = NPVoxBox.FromCenterSize(model.BoundingBox.RoundedCenter, new NPVoxCoord(3, 3, 3)); } } SelectedFrame.AppendTransformer(transformer); InvalidateOutputMeshes(); SelectTransformation(Transformers.Length - 1, true); this.SetCurrentTool(Tool.AREA); }
public static NPVoxBox DrawBoxSelection(NPVoxToUnity npVoxToUnity, NPVoxBox box, bool editable = true) { Vector3 leftDownBack = npVoxToUnity.ToUnityPosition(box.LeftDownBack) - npVoxToUnity.VoxeSize * 0.5f; Vector3 rightUpForward = npVoxToUnity.ToUnityPosition(box.RightUpForward) + npVoxToUnity.VoxeSize * 0.5f; Vector3 rightDownBack = new Vector3(rightUpForward.x, leftDownBack.y, leftDownBack.z); Vector3 leftUpBack = new Vector3(leftDownBack.x, rightUpForward.y, leftDownBack.z); Vector3 rightUpBack = new Vector3(rightUpForward.x, rightUpForward.y, leftDownBack.z); Vector3 leftDownForward = new Vector3(leftDownBack.x, leftDownBack.y, rightUpForward.z); Vector3 rightDownForward = new Vector3(rightUpForward.x, leftDownBack.y, rightUpForward.z); Vector3 leftUpForward = new Vector3(leftDownBack.x, rightUpForward.y, rightUpForward.z); Handles.DrawDottedLine(leftDownBack, rightDownBack, 1f); Handles.DrawDottedLine(leftDownBack, leftUpBack, 1f); Handles.DrawDottedLine(leftUpBack, rightUpBack, 1f); Handles.DrawDottedLine(rightDownBack, rightUpBack, 1f); Handles.DrawDottedLine(leftDownForward, rightDownForward, 1f); Handles.DrawDottedLine(leftDownForward, leftUpForward, 1f); Handles.DrawDottedLine(leftUpForward, rightUpForward, 1f); Handles.DrawDottedLine(rightDownForward, rightUpForward, 1f); Handles.DrawDottedLine(leftDownBack, leftDownForward, 1f); Handles.DrawDottedLine(rightDownBack, rightDownForward, 1f); Handles.DrawDottedLine(leftUpBack, leftUpForward, 1f); Handles.DrawDottedLine(rightUpBack, rightUpForward, 1f); if (!editable) { return(box); } NPVoxBox newBox = new NPVoxBox(box.LeftDownBack, box.RightUpForward); if (SceneView.currentDrawingSceneView.camera.orthographic) { NPVoxCoord oldCoord; NPVoxCoord newCoord; oldCoord = box.LeftDownBack; newCoord = NPVoxHandles.DrawVoxelHandle(leftDownBack, oldCoord, npVoxToUnity); if (!newCoord.Equals(oldCoord)) { newBox.LeftDownBack = newCoord; } oldCoord = box.RightDownBack; newCoord = NPVoxHandles.DrawVoxelHandle(rightDownBack, oldCoord, npVoxToUnity); if (!newCoord.Equals(oldCoord)) { newBox.RightDownBack = newCoord; } oldCoord = box.LeftUpBack; newCoord = NPVoxHandles.DrawVoxelHandle(leftUpBack, oldCoord, npVoxToUnity); if (!newCoord.Equals(oldCoord)) { newBox.LeftUpBack = newCoord; } oldCoord = box.RightUpBack; newCoord = NPVoxHandles.DrawVoxelHandle(rightUpBack, oldCoord, npVoxToUnity); if (!newCoord.Equals(oldCoord)) { newBox.RightUpBack = newCoord; } oldCoord = box.LeftDownForward; newCoord = NPVoxHandles.DrawVoxelHandle(leftDownForward, oldCoord, npVoxToUnity); if (!newCoord.Equals(oldCoord)) { newBox.LeftDownForward = newCoord; } oldCoord = box.RightDownForward; newCoord = NPVoxHandles.DrawVoxelHandle(rightDownForward, oldCoord, npVoxToUnity); if (!newCoord.Equals(oldCoord)) { newBox.RightDownForward = newCoord; } oldCoord = box.LeftUpForward; newCoord = NPVoxHandles.DrawVoxelHandle(leftUpForward, oldCoord, npVoxToUnity); if (!newCoord.Equals(oldCoord)) { newBox.LeftUpForward = newCoord; } oldCoord = box.RightUpForward; newCoord = NPVoxHandles.DrawVoxelHandle(rightUpForward, oldCoord, npVoxToUnity); if (!newCoord.Equals(oldCoord)) { newBox.RightUpForward = newCoord; } // center mover oldCoord = box.LeftDownBack; newCoord = NPVoxHandles.DrawVoxelHandle(npVoxToUnity.ToUnityPosition(box.SaveCenter), oldCoord, npVoxToUnity); if (!newCoord.Equals(oldCoord)) { newBox.SaveCenter += NPVoxCoordUtil.ToVector(newCoord - oldCoord); } } else { sbyte oldPos; sbyte newPos; Vector3 handlePos; handlePos = new Vector3(leftDownBack.x + (rightUpForward.x - leftDownBack.x) / 2, leftDownBack.y + (rightUpForward.y - leftDownBack.y) / 2, leftDownBack.z); oldPos = box.Back; newPos = NPVoxHandles.DrawAxisHandle(handlePos, oldPos, npVoxToUnity, Vector3.forward); if (oldPos != newPos) { newBox.Back = newPos; } handlePos = new Vector3(leftDownBack.x + (rightUpForward.x - leftDownBack.x) / 2, leftDownBack.y + (rightUpForward.y - leftDownBack.y) / 2, rightUpForward.z); oldPos = box.Forward; newPos = NPVoxHandles.DrawAxisHandle(handlePos, oldPos, npVoxToUnity, Vector3.forward); if (oldPos != newPos) { newBox.Forward = newPos; } handlePos = new Vector3(leftDownBack.x + (rightUpForward.x - leftDownBack.x) / 2, leftDownBack.y, leftDownBack.z + (rightUpForward.z - leftDownBack.z) / 2); oldPos = box.Down; newPos = NPVoxHandles.DrawAxisHandle(handlePos, oldPos, npVoxToUnity, Vector3.up); if (oldPos != newPos) { newBox.Down = newPos; } handlePos = new Vector3(leftDownBack.x + (rightUpForward.x - leftDownBack.x) / 2, rightUpForward.y, leftDownBack.z + (rightUpForward.z - leftDownBack.z) / 2); oldPos = box.Up; newPos = NPVoxHandles.DrawAxisHandle(handlePos, oldPos, npVoxToUnity, Vector3.up); if (oldPos != newPos) { newBox.Up = newPos; } handlePos = new Vector3(leftDownBack.x, leftDownBack.y + (rightUpForward.y - leftDownBack.y) / 2, leftDownBack.z + (rightUpForward.z - leftDownBack.z) / 2); oldPos = box.Left; newPos = NPVoxHandles.DrawAxisHandle(handlePos, oldPos, npVoxToUnity, Vector3.right); if (oldPos != newPos) { newBox.Left = newPos; } handlePos = new Vector3(rightUpForward.x, leftDownBack.y + (rightUpForward.y - leftDownBack.y) / 2, leftDownBack.z + (rightUpForward.z - leftDownBack.z) / 2); oldPos = box.Right; newPos = NPVoxHandles.DrawAxisHandle(handlePos, oldPos, npVoxToUnity, Vector3.right); if (oldPos != newPos) { newBox.Right = newPos; } } return(newBox); }
public static void CreateMesh( NPVoxModel model, Mesh mesh, Vector3 cubeSize, Vector3 NormalVariance, int NormalVarianceSeed = 0, NPVoxOptimization optimization = NPVoxOptimization.OFF, NPVoxNormalMode NormalMode = NPVoxNormalMode.SMOOTH, int BloodColorIndex = 0, NPVoxFaces loop = null, NPVoxFaces cutout = null, NPVoxFaces include = null, int MinVertexGroups = 1, NPVoxNormalMode[] NormalModePerVoxelGroup = null, NPVoxNormalProcessorList normalProcessors = null ) { bool hasVoxelGroups = model.HasVoxelGroups(); var vertices = new Vector3[model.NumVoxels * 8]; byte vertexGroupCount = model.NumVoxelGroups; var triangles = new int[vertexGroupCount, model.NumVoxels * 36]; var normals = new Vector3[model.NumVoxels * 8]; var tangents = new Vector4[model.NumVoxels * 8]; var colors = new Color[model.NumVoxels * 8]; var tmp = new NPVoxMeshTempData[model.NumVoxels]; int currentVertexIndex = 0; var currentTriangleIndex = new int[vertexGroupCount]; for (int i = 0; i < vertexGroupCount; i++) { currentTriangleIndex[i] = 0; } UnityEngine.Random.InitState(NormalVarianceSeed); if (loop == null) { loop = new NPVoxFaces(); } if (include == null) { include = new NPVoxFaces(1, 1, 1, 1, 1, 1); } NPVoxBox voxelsToInclude = model.BoundingBox; Vector3 cutoutOffset = Vector3.zero; if (cutout != null) { Vector3 originalCenter = voxelsToInclude.SaveCenter; voxelsToInclude.Left = (sbyte)Mathf.Abs(cutout.Left); voxelsToInclude.Down = (sbyte)Mathf.Abs(cutout.Down); voxelsToInclude.Back = (sbyte)Mathf.Abs(cutout.Back); voxelsToInclude.Right = (sbyte)(voxelsToInclude.Right - (sbyte)Mathf.Abs(cutout.Right)); voxelsToInclude.Up = (sbyte)(voxelsToInclude.Up - (sbyte)Mathf.Abs(cutout.Up)); voxelsToInclude.Forward = (sbyte)(voxelsToInclude.Forward - (sbyte)Mathf.Abs(cutout.Forward)); cutoutOffset = Vector3.Scale(originalCenter - voxelsToInclude.SaveCenter, cubeSize); } NPVoxToUnity npVoxToUnity = new NPVoxToUnity(model, cubeSize); Vector3 size = new Vector3( voxelsToInclude.Size.X * cubeSize.x, voxelsToInclude.Size.Y * cubeSize.y, voxelsToInclude.Size.Z * cubeSize.z ); NPVoxBox voxelNormalNeighbours = new NPVoxBox(new NPVoxCoord(-1, -1, -1), new NPVoxCoord(1, 1, 1)); // Collect temporary data to use for model generation int voxIndex = 0; foreach (NPVoxCoord voxCoord in voxelsToInclude.Enumerate()) { if (model.HasVoxel(voxCoord)) { tmp[voxIndex] = new NPVoxMeshTempData(); tmp[voxIndex].loop = loop; tmp[voxIndex].cutout = cutout; tmp[voxIndex].include = include; // Compute voxel center tmp[voxIndex].voxelCenter = npVoxToUnity.ToUnityPosition(voxCoord) + cutoutOffset; tmp[voxIndex].voxCoord = voxCoord; tmp[voxIndex].voxToUnity = npVoxToUnity; // Determine vertex group index tmp[voxIndex].vertexGroupIndex = 0; if (hasVoxelGroups) { tmp[voxIndex].vertexGroupIndex = model.GetVoxelGroup(voxCoord); } // Determine normal Mode if (NormalModePerVoxelGroup != null && NormalModePerVoxelGroup.Length > tmp[voxIndex].vertexGroupIndex) { tmp[voxIndex].normalMode = NormalModePerVoxelGroup[tmp[voxIndex].vertexGroupIndex]; } else { tmp[voxIndex].normalMode = NormalMode; } // do we have this side tmp[voxIndex].hasLeft = !model.HasVoxel(model.LoopCoord(voxCoord + NPVoxCoord.LEFT, loop)); tmp[voxIndex].hasRight = !model.HasVoxel(model.LoopCoord(voxCoord + NPVoxCoord.RIGHT, loop)); tmp[voxIndex].hasDown = !model.HasVoxel(model.LoopCoord(voxCoord + NPVoxCoord.DOWN, loop)); tmp[voxIndex].hasUp = !model.HasVoxel(model.LoopCoord(voxCoord + NPVoxCoord.UP, loop)); tmp[voxIndex].hasForward = !model.HasVoxel(model.LoopCoord(voxCoord + NPVoxCoord.FORWARD, loop)); tmp[voxIndex].hasBack = !model.HasVoxel(model.LoopCoord(voxCoord + NPVoxCoord.BACK, loop)); // do we actually want to include this side in our mesh // NOTE: cutout < 0 means we still render the mesh even though it is cutout // cutout > 0 means we don't render the mesh when cutout tmp[voxIndex].includeLeft = (tmp[voxIndex].hasLeft || (cutout.Left < 0 && voxCoord.X == voxelsToInclude.Left)) && include.Left == 1; tmp[voxIndex].includeRight = (tmp[voxIndex].hasRight || (cutout.Right < 0 && voxCoord.X == voxelsToInclude.Right)) && include.Right == 1; tmp[voxIndex].includeUp = (tmp[voxIndex].hasUp || (cutout.Up < 0 && voxCoord.Y == voxelsToInclude.Up)) && include.Up == 1; tmp[voxIndex].includeDown = (tmp[voxIndex].hasDown || (cutout.Down < 0 && voxCoord.Y == voxelsToInclude.Down)) && include.Down == 1; tmp[voxIndex].includeBack = (tmp[voxIndex].hasBack || (cutout.Back < 0 && voxCoord.Z == voxelsToInclude.Back)) && include.Back == 1; tmp[voxIndex].includeForward = (tmp[voxIndex].hasForward || (cutout.Forward < 0 && voxCoord.Z == voxelsToInclude.Forward)) && include.Forward == 1; tmp[voxIndex].isHidden = !tmp[voxIndex].hasForward && !tmp[voxIndex].hasBack && !tmp[voxIndex].hasLeft && !tmp[voxIndex].hasRight && !tmp[voxIndex].hasUp && !tmp[voxIndex].hasDown; if (tmp[voxIndex].isHidden && optimization == NPVoxOptimization.PER_VOXEL) { continue; } if (tmp[voxIndex].isHidden && BloodColorIndex > 0) { model.SetVoxel(voxCoord, (byte)BloodColorIndex); // WTF WTF WTF?!? we should not modify the MODEL in here !!!! elfapo: AAAAHHH NOOOO!!!! :O j.k. ;) } Color color = model.GetColor(voxCoord); // prepare cube vertices tmp[voxIndex].numVertices = 0; tmp[voxIndex].vertexIndexOffsetBegin = currentVertexIndex; if (optimization != NPVoxOptimization.PER_FACE || tmp[voxIndex].includeBack || tmp[voxIndex].includeLeft || tmp[voxIndex].includeDown) { tmp[voxIndex].vertexIndexOffsets[0] = tmp[voxIndex].numVertices++; tmp[voxIndex].vertexPositionOffsets[tmp[voxIndex].vertexIndexOffsets[0]] = new Vector3(-0.5f, -0.5f, -0.5f); } if (optimization != NPVoxOptimization.PER_FACE || tmp[voxIndex].includeBack || tmp[voxIndex].includeRight || tmp[voxIndex].includeDown) { tmp[voxIndex].vertexIndexOffsets[1] = tmp[voxIndex].numVertices++; tmp[voxIndex].vertexPositionOffsets[tmp[voxIndex].vertexIndexOffsets[1]] = new Vector3(0.5f, -0.5f, -0.5f); } if (optimization != NPVoxOptimization.PER_FACE || tmp[voxIndex].includeBack || tmp[voxIndex].includeLeft || tmp[voxIndex].includeUp) { tmp[voxIndex].vertexIndexOffsets[2] = tmp[voxIndex].numVertices++; tmp[voxIndex].vertexPositionOffsets[tmp[voxIndex].vertexIndexOffsets[2]] = new Vector3(-0.5f, 0.5f, -0.5f); } if (optimization != NPVoxOptimization.PER_FACE || tmp[voxIndex].includeBack || tmp[voxIndex].includeRight || tmp[voxIndex].includeUp) { tmp[voxIndex].vertexIndexOffsets[3] = tmp[voxIndex].numVertices++; tmp[voxIndex].vertexPositionOffsets[tmp[voxIndex].vertexIndexOffsets[3]] = new Vector3(0.5f, 0.5f, -0.5f); } if (optimization != NPVoxOptimization.PER_FACE || tmp[voxIndex].includeForward || tmp[voxIndex].includeLeft || tmp[voxIndex].includeDown) { tmp[voxIndex].vertexIndexOffsets[4] = tmp[voxIndex].numVertices++; tmp[voxIndex].vertexPositionOffsets[tmp[voxIndex].vertexIndexOffsets[4]] = new Vector3(-0.5f, -0.5f, 0.5f); } if (optimization != NPVoxOptimization.PER_FACE || tmp[voxIndex].includeForward || tmp[voxIndex].includeRight || tmp[voxIndex].includeDown) { tmp[voxIndex].vertexIndexOffsets[5] = tmp[voxIndex].numVertices++; tmp[voxIndex].vertexPositionOffsets[tmp[voxIndex].vertexIndexOffsets[5]] = new Vector3(0.5f, -0.5f, 0.5f); } if (optimization != NPVoxOptimization.PER_FACE || tmp[voxIndex].includeForward || tmp[voxIndex].includeLeft || tmp[voxIndex].includeUp) { tmp[voxIndex].vertexIndexOffsets[6] = tmp[voxIndex].numVertices++; tmp[voxIndex].vertexPositionOffsets[tmp[voxIndex].vertexIndexOffsets[6]] = new Vector3(-0.5f, 0.5f, 0.5f); } if (optimization != NPVoxOptimization.PER_FACE || tmp[voxIndex].includeForward || tmp[voxIndex].includeRight || tmp[voxIndex].includeUp) { tmp[voxIndex].vertexIndexOffsets[7] = tmp[voxIndex].numVertices++; tmp[voxIndex].vertexPositionOffsets[tmp[voxIndex].vertexIndexOffsets[7]] = new Vector3(0.5f, 0.5f, 0.5f); } // add cube faces int i = currentTriangleIndex[tmp[voxIndex].vertexGroupIndex]; // back if (optimization != NPVoxOptimization.PER_FACE || tmp[voxIndex].includeBack) { triangles[tmp[voxIndex].vertexGroupIndex, i++] = tmp[voxIndex].vertexIndexOffsetBegin + tmp[voxIndex].vertexIndexOffsets[0]; triangles[tmp[voxIndex].vertexGroupIndex, i++] = tmp[voxIndex].vertexIndexOffsetBegin + tmp[voxIndex].vertexIndexOffsets[2]; triangles[tmp[voxIndex].vertexGroupIndex, i++] = tmp[voxIndex].vertexIndexOffsetBegin + tmp[voxIndex].vertexIndexOffsets[1]; triangles[tmp[voxIndex].vertexGroupIndex, i++] = tmp[voxIndex].vertexIndexOffsetBegin + tmp[voxIndex].vertexIndexOffsets[2]; triangles[tmp[voxIndex].vertexGroupIndex, i++] = tmp[voxIndex].vertexIndexOffsetBegin + tmp[voxIndex].vertexIndexOffsets[3]; triangles[tmp[voxIndex].vertexGroupIndex, i++] = tmp[voxIndex].vertexIndexOffsetBegin + tmp[voxIndex].vertexIndexOffsets[1]; } // Forward if (optimization != NPVoxOptimization.PER_FACE || tmp[voxIndex].includeForward) { triangles[tmp[voxIndex].vertexGroupIndex, i++] = tmp[voxIndex].vertexIndexOffsetBegin + tmp[voxIndex].vertexIndexOffsets[6]; triangles[tmp[voxIndex].vertexGroupIndex, i++] = tmp[voxIndex].vertexIndexOffsetBegin + tmp[voxIndex].vertexIndexOffsets[4]; triangles[tmp[voxIndex].vertexGroupIndex, i++] = tmp[voxIndex].vertexIndexOffsetBegin + tmp[voxIndex].vertexIndexOffsets[5]; triangles[tmp[voxIndex].vertexGroupIndex, i++] = tmp[voxIndex].vertexIndexOffsetBegin + tmp[voxIndex].vertexIndexOffsets[7]; triangles[tmp[voxIndex].vertexGroupIndex, i++] = tmp[voxIndex].vertexIndexOffsetBegin + tmp[voxIndex].vertexIndexOffsets[6]; triangles[tmp[voxIndex].vertexGroupIndex, i++] = tmp[voxIndex].vertexIndexOffsetBegin + tmp[voxIndex].vertexIndexOffsets[5]; } // right if (optimization != NPVoxOptimization.PER_FACE || tmp[voxIndex].includeRight) { triangles[tmp[voxIndex].vertexGroupIndex, i++] = tmp[voxIndex].vertexIndexOffsetBegin + tmp[voxIndex].vertexIndexOffsets[1]; triangles[tmp[voxIndex].vertexGroupIndex, i++] = tmp[voxIndex].vertexIndexOffsetBegin + tmp[voxIndex].vertexIndexOffsets[3]; triangles[tmp[voxIndex].vertexGroupIndex, i++] = tmp[voxIndex].vertexIndexOffsetBegin + tmp[voxIndex].vertexIndexOffsets[5]; triangles[tmp[voxIndex].vertexGroupIndex, i++] = tmp[voxIndex].vertexIndexOffsetBegin + tmp[voxIndex].vertexIndexOffsets[3]; triangles[tmp[voxIndex].vertexGroupIndex, i++] = tmp[voxIndex].vertexIndexOffsetBegin + tmp[voxIndex].vertexIndexOffsets[7]; triangles[tmp[voxIndex].vertexGroupIndex, i++] = tmp[voxIndex].vertexIndexOffsetBegin + tmp[voxIndex].vertexIndexOffsets[5]; } // left if (optimization != NPVoxOptimization.PER_FACE || tmp[voxIndex].includeLeft) { triangles[tmp[voxIndex].vertexGroupIndex, i++] = tmp[voxIndex].vertexIndexOffsetBegin + tmp[voxIndex].vertexIndexOffsets[0]; triangles[tmp[voxIndex].vertexGroupIndex, i++] = tmp[voxIndex].vertexIndexOffsetBegin + tmp[voxIndex].vertexIndexOffsets[4]; triangles[tmp[voxIndex].vertexGroupIndex, i++] = tmp[voxIndex].vertexIndexOffsetBegin + tmp[voxIndex].vertexIndexOffsets[2]; triangles[tmp[voxIndex].vertexGroupIndex, i++] = tmp[voxIndex].vertexIndexOffsetBegin + tmp[voxIndex].vertexIndexOffsets[2]; triangles[tmp[voxIndex].vertexGroupIndex, i++] = tmp[voxIndex].vertexIndexOffsetBegin + tmp[voxIndex].vertexIndexOffsets[4]; triangles[tmp[voxIndex].vertexGroupIndex, i++] = tmp[voxIndex].vertexIndexOffsetBegin + tmp[voxIndex].vertexIndexOffsets[6]; } // up if (optimization != NPVoxOptimization.PER_FACE || tmp[voxIndex].includeUp) { triangles[tmp[voxIndex].vertexGroupIndex, i++] = tmp[voxIndex].vertexIndexOffsetBegin + tmp[voxIndex].vertexIndexOffsets[2]; triangles[tmp[voxIndex].vertexGroupIndex, i++] = tmp[voxIndex].vertexIndexOffsetBegin + tmp[voxIndex].vertexIndexOffsets[6]; triangles[tmp[voxIndex].vertexGroupIndex, i++] = tmp[voxIndex].vertexIndexOffsetBegin + tmp[voxIndex].vertexIndexOffsets[3]; triangles[tmp[voxIndex].vertexGroupIndex, i++] = tmp[voxIndex].vertexIndexOffsetBegin + tmp[voxIndex].vertexIndexOffsets[3]; triangles[tmp[voxIndex].vertexGroupIndex, i++] = tmp[voxIndex].vertexIndexOffsetBegin + tmp[voxIndex].vertexIndexOffsets[6]; triangles[tmp[voxIndex].vertexGroupIndex, i++] = tmp[voxIndex].vertexIndexOffsetBegin + tmp[voxIndex].vertexIndexOffsets[7]; } // down if (optimization != NPVoxOptimization.PER_FACE || tmp[voxIndex].includeDown) { triangles[tmp[voxIndex].vertexGroupIndex, i++] = tmp[voxIndex].vertexIndexOffsetBegin + tmp[voxIndex].vertexIndexOffsets[0]; triangles[tmp[voxIndex].vertexGroupIndex, i++] = tmp[voxIndex].vertexIndexOffsetBegin + tmp[voxIndex].vertexIndexOffsets[1]; triangles[tmp[voxIndex].vertexGroupIndex, i++] = tmp[voxIndex].vertexIndexOffsetBegin + tmp[voxIndex].vertexIndexOffsets[4]; triangles[tmp[voxIndex].vertexGroupIndex, i++] = tmp[voxIndex].vertexIndexOffsetBegin + tmp[voxIndex].vertexIndexOffsets[1]; triangles[tmp[voxIndex].vertexGroupIndex, i++] = tmp[voxIndex].vertexIndexOffsetBegin + tmp[voxIndex].vertexIndexOffsets[5]; triangles[tmp[voxIndex].vertexGroupIndex, i++] = tmp[voxIndex].vertexIndexOffsetBegin + tmp[voxIndex].vertexIndexOffsets[4]; } // Tangents for (int t = 0; t < tmp[voxIndex].numVertices; t++) { // store voxel center for shader usage Vector4 tangent = new Vector4(); tangent.x = tmp[voxIndex].voxelCenter.x; tangent.y = tmp[voxIndex].voxelCenter.y; tangent.z = tmp[voxIndex].voxelCenter.z; // encode model size tangent.w = ((voxelsToInclude.Size.X & 0x7F) << 14) | ((voxelsToInclude.Size.Y & 0x7F) << 7) | (voxelsToInclude.Size.Z & 0x7F); tangents[tmp[voxIndex].vertexIndexOffsetBegin + t] = tangent; } // UVs for (int t = 0; t < tmp[voxIndex].numVertices; t++) { colors[tmp[voxIndex].vertexIndexOffsetBegin + t] = color; } // translate & scale vertices to voxel position for (int t = 0; t < tmp[voxIndex].numVertices; t++) { vertices[tmp[voxIndex].vertexIndexOffsetBegin + t] = tmp[voxIndex].voxelCenter + Vector3.Scale(tmp[voxIndex].vertexPositionOffsets[t], cubeSize); } currentTriangleIndex[tmp[voxIndex].vertexGroupIndex] = i; currentVertexIndex += tmp[voxIndex].numVertices; voxIndex++; } } // elfapo: Remove invalid voxel information Array.Resize(ref tmp, voxIndex); ////////////////////////////////////// NORMAL STAGES //////////////////////////////////////// // elfapo TODO: Test area 'Normal Processor' Move Normal Processor stages to Normal Processor Pipeline: //NPVoxNormalProcessor_Voxel generator = ScriptableObject.CreateInstance<NPVoxNormalProcessor_Voxel>(); //NPVoxNormalProcessor_Variance processor = ScriptableObject.CreateInstance<NPVoxNormalProcessor_Variance>(); //generator.InitOutputBuffer(normals); //processor.InitOutputBuffer(normals); //processor.NormalVariance = NormalVariance; //processor.NormalVarianceSeed = NormalVarianceSeed; //if (NormalModePerVoxelGroup != null && NormalModePerVoxelGroup.Length > 0) //{ // for (int i = 0; i < NormalModePerVoxelGroup.Length; i++) // { // generator.ClearVoxelGroupFilters(); // generator.AddVoxelGroupFilter(i); // generator.NormalMode = NormalModePerVoxelGroup[i]; // generator.Process(model, tmp, normals, normals); // } // processor.Process(model, tmp, normals, normals); //} //else //{ // generator.NormalMode = NormalMode; // generator.Process(model, tmp, normals, normals); // processor.Process(model, tmp, normals, normals); //} //ScriptableObject.DestroyImmediate(generator); //ScriptableObject.DestroyImmediate(processor); if (normalProcessors != null) { normalProcessors.Run(model, tmp, normals, normals); } ////////////////////////////////////////////////////////////////////////////////////////////// // shrink arrays as needed if (optimization != NPVoxOptimization.OFF) { Array.Resize(ref vertices, currentVertexIndex); Array.Resize(ref normals, currentVertexIndex); Array.Resize(ref tangents, currentVertexIndex); Array.Resize(ref colors, currentVertexIndex); } mesh.vertices = vertices; if (hasVoxelGroups) { mesh.subMeshCount = Math.Max(vertexGroupCount, MinVertexGroups); for (int i = 0; i < vertexGroupCount; i++) { int numberOfTrianglesForVertexGroup = currentTriangleIndex[i]; int[] trianglesForVertexGroup = new int[numberOfTrianglesForVertexGroup]; for (int j = 0; j < numberOfTrianglesForVertexGroup; j++) { trianglesForVertexGroup[j] = triangles[i, j]; } mesh.SetTriangles(trianglesForVertexGroup, i); } } else { int numberOfTrianglesForVertexGroup = currentTriangleIndex[0]; int[] trianglesForVertexGroup = new int[numberOfTrianglesForVertexGroup]; Buffer.BlockCopy(triangles, 0, trianglesForVertexGroup, 0, numberOfTrianglesForVertexGroup * sizeof(int)); if (MinVertexGroups < 2) { mesh.triangles = trianglesForVertexGroup; } else { mesh.subMeshCount = MinVertexGroups; mesh.SetTriangles(trianglesForVertexGroup, 0); } } mesh.normals = normals; mesh.tangents = tangents; mesh.colors = colors; mesh.bounds = new Bounds(Vector3.zero, size); if (NormalMode == NPVoxNormalMode.AUTO) { mesh.RecalculateNormals(); } }
public override void Process(NPVoxModel model, NPVoxMeshTempData tempdata, Vector3[] inNormals, ref Vector3[] outNormals) { if (meshReference != null) { Vector3 sizeVoxel = tempdata.voxToUnity.VoxeSize; if (!dataAlreadyCollected) { Vector3 sizeVoxModel = tempdata.voxToUnity.UnityVoxModelSize; NPVoxBox boundsVoxModel = model.BoundingBox; Bounds boundsMesh = meshReference.bounds; offset = boundsMesh.min; scale = new Vector3( boundsMesh.size.x / sizeVoxModel.x, boundsMesh.size.y / sizeVoxModel.z, boundsMesh.size.z / sizeVoxModel.y ); meshReference.GetNormals(meshNormals); meshReference.GetVertices(meshVertices); triangleList = meshReference.GetTriangles(0); } Vector3 x = new Vector3( (tempdata.voxCoord.X + 0.5f) * sizeVoxel.x, (tempdata.voxCoord.Y + 0.5f) * sizeVoxel.y, (tempdata.voxCoord.Z + 0.5f) * sizeVoxel.z ); x = new Vector3( x.x * scale.x, x.z * scale.z, x.y * scale.y ); x += offset; int bestTriangle = -1; float bestDistanceSquared = float.PositiveInfinity; Vector3 bestNormal = Vector3.zero; Vector3 bestPoint = Vector3.zero; for (int triangle = 0; triangle < triangleList.Length; triangle += 3) { Vector3 v1 = meshVertices[triangleList[triangle + 0]]; Vector3 v2 = meshVertices[triangleList[triangle + 1]]; Vector3 v3 = meshVertices[triangleList[triangle + 2]]; Bounds boundTest = new Bounds(v1, Vector3.zero); boundTest.Encapsulate(v2); boundTest.Encapsulate(v3); boundTest.Expand(0.1f); if (!boundTest.Contains(x)) { continue; } Vector3 n = LinearAlgebra.ComputePlaneNormal(v1, v2, v3); Vector3 xProjected; LinearAlgebra.ProjectPointToPlane(v1, n, x, out xProjected); Vector3 xBarycentric = LinearAlgebra.WorldToBarycentric3(v1, v2, v3, xProjected); if (xBarycentric.x < 0.0) { xProjected = LinearAlgebra.ClampToLine(v2, v3, xProjected); } else if (xBarycentric.y < 0.0) { xProjected = LinearAlgebra.ClampToLine(v3, v1, xProjected); } else if (xBarycentric.z < 0.0) { xProjected = LinearAlgebra.ClampToLine(v1, v2, xProjected); } float sqaredDistance = (xProjected - x).sqrMagnitude; if (!float.IsNaN(sqaredDistance) && sqaredDistance < bestDistanceSquared) { bestDistanceSquared = sqaredDistance; bestTriangle = triangle; bestNormal = n; bestPoint = xProjected; } } Vector3 average = Vector3.zero; bool smoothNormals = true; if (bestTriangle != -1) { if (smoothNormals) { average = LinearAlgebra.BarycentricToWorld( meshNormals[triangleList[bestTriangle + 0]], meshNormals[triangleList[bestTriangle + 1]], meshNormals[triangleList[bestTriangle + 2]], LinearAlgebra.WorldToBarycentric3( meshVertices[triangleList[bestTriangle + 0]], meshVertices[triangleList[bestTriangle + 1]], meshVertices[triangleList[bestTriangle + 2]], bestPoint) ); } else { average = meshNormals[triangleList[bestTriangle]]; //average = new Vector3( // bestNormal.x, // bestNormal.z, // bestNormal.y // ); } average = new Vector3( average.x / scale.x, average.z / scale.z, average.y / scale.y ); } for (int t = 0; t < tempdata.numVertices; t++) { outNormals[tempdata.vertexIndexOffsetBegin + t] = average; } } else { for (int t = 0; t < tempdata.numVertices; t++) { outNormals[tempdata.vertexIndexOffsetBegin + t] = inNormals[tempdata.vertexIndexOffsetBegin + t]; } } }
public static NPVoxModel Transform(NPVoxModel sourceModel, NPVoxBox affectedArea, Matrix4x4 transformMatrix, ResolveConflictMethodType resolveConflictMethod = ResolveConflictMethodType.CLOSEST, NPVoxModel reuse = null) { NPVoxBox clampedBox = sourceModel.Clamp(affectedArea); // calculate size & offset for new model NPVoxCoord size = sourceModel.Size; NPVoxCoord offset = NPVoxCoord.ZERO; { NPVoxBox parentBounds = sourceModel.BoundingBox; NPVoxBox thisBounds = parentBounds.Clone(); // transform voxels foreach (NPVoxCoord coord in clampedBox.Enumerate()) { Vector3 saveCoord = transformMatrix.MultiplyPoint(NPVoxCoordUtil.ToVector(coord)); NPVoxCoord newCoord = NPVoxCoordUtil.ToCoord(saveCoord); if (!sourceModel.IsInside(newCoord)) { thisBounds.EnlargeToInclude(newCoord); } } // transform sockets foreach (NPVoxSocket socket in sourceModel.Sockets) { NPVoxCoord newCoord = NPVoxCoordUtil.ToCoord(transformMatrix.MultiplyPoint(NPVoxCoordUtil.ToVector(socket.Anchor))); if (clampedBox.Contains(socket.Anchor) && !sourceModel.IsInside(newCoord)) { thisBounds.EnlargeToInclude(newCoord); } } CalculateResizeOffset(parentBounds, thisBounds, out offset, out size); } bool hasVoxelGroups = sourceModel.HasVoxelGroups(); NPVoxBoneModel sourceBoneModel = sourceModel as NPVoxBoneModel; bool hasBoneGropus = sourceBoneModel != null; NPVoxModel transformedModel = NPVoxModel.NewInstance(sourceModel, size, reuse); NPVoxBoneModel transformedBoneModel = transformedModel as NPVoxBoneModel; if (hasVoxelGroups) { transformedModel.InitVoxelGroups(); transformedModel.NumVoxelGroups = sourceModel.NumVoxelGroups; } if (hasBoneGropus) { transformedBoneModel.AllBones = NPVoxBone.CloneBones(sourceBoneModel.AllBones); } // 1. copy all voxels over that are not affected by the transformation transformedModel.NumVoxels = sourceModel.NumVoxels; transformedModel.Colortable = sourceModel.Colortable; foreach (NPVoxCoord coord in sourceModel.EnumerateVoxels()) { NPVoxCoord movedCoord = coord + offset; if (!clampedBox.Contains(coord)) { transformedModel.SetVoxel(movedCoord, sourceModel.GetVoxel(coord)); if (hasVoxelGroups) { transformedModel.SetVoxelGroup(movedCoord, sourceModel.GetVoxelGroup(coord)); } if (hasBoneGropus) { transformedBoneModel.SetBoneMask(movedCoord, sourceBoneModel.GetBoneMask(coord)); } } } // 2. copy all voxels that can be tranformed without conflict, Dictionary <NPVoxCoord, Vector3> conflictVoxels = new Dictionary <NPVoxCoord, Vector3>(); foreach (NPVoxCoord sourceCoord in clampedBox.Enumerate()) { if (sourceModel.HasVoxelFast(sourceCoord)) { Vector3 saveCoord = transformMatrix.MultiplyPoint(NPVoxCoordUtil.ToVector(sourceCoord)); Vector3 targetCoordSave = saveCoord + NPVoxCoordUtil.ToVector(offset); NPVoxCoord targetCoord = NPVoxCoordUtil.ToCoord(targetCoordSave); if (!transformedModel.HasVoxelFast(targetCoord)) { transformedModel.SetVoxel(targetCoord, sourceModel.GetVoxel(sourceCoord)); if (hasVoxelGroups) { transformedModel.SetVoxelGroup(targetCoord, sourceModel.GetVoxelGroup(sourceCoord)); } if (hasBoneGropus) { transformedBoneModel.SetBoneMask(targetCoord, sourceBoneModel.GetBoneMask(sourceCoord)); } } else { conflictVoxels[sourceCoord] = targetCoordSave; } } } // 3. try to fit in voxels that had conflicts int numberOfConflictsSolved = 0; if (resolveConflictMethod != ResolveConflictMethodType.NONE) { foreach (NPVoxCoord sourceCoord in conflictVoxels.Keys) { if (sourceModel.HasVoxelFast(sourceCoord)) { Vector3 targetSaveCoord = conflictVoxels[sourceCoord]; NPVoxCoord nearbyCoord = GetNearbyCoord(transformedModel, targetSaveCoord, resolveConflictMethod); if (!nearbyCoord.Equals(NPVoxCoord.INVALID)) { transformedModel.SetVoxel(nearbyCoord, sourceModel.GetVoxel(sourceCoord)); if (hasVoxelGroups) { transformedModel.SetVoxelGroup(nearbyCoord, sourceModel.GetVoxelGroup(sourceCoord)); } if (hasBoneGropus) { transformedBoneModel.SetBoneMask(nearbyCoord, sourceBoneModel.GetBoneMask(sourceCoord)); } numberOfConflictsSolved++; } } } if (numberOfConflictsSolved != conflictVoxels.Count) { Debug.Log(string.Format("transformation has resolved {0}/{1} conflicting voxels", numberOfConflictsSolved, conflictVoxels.Count)); } } // 4. transform all sockets NPVoxSocket[] sockets = new NPVoxSocket[sourceModel.Sockets.Length]; for (int i = 0; i < sockets.Length; i++) { NPVoxSocket socket = sourceModel.Sockets[i]; if (clampedBox.Contains(socket.Anchor)) { // transform anchor Vector3 saveOriginalAnchor = NPVoxCoordUtil.ToVector(socket.Anchor); Vector3 saveTargetAnchor = transformMatrix.MultiplyPoint(saveOriginalAnchor) + NPVoxCoordUtil.ToVector(offset); socket.Anchor = NPVoxCoordUtil.ToCoord(saveTargetAnchor); // transform Quaternion Quaternion originalRotation = Quaternion.Euler(socket.EulerAngles); Matrix4x4 rotated = (Matrix4x4.TRS(Vector3.zero, originalRotation, Vector3.one) * transformMatrix); socket.EulerAngles = Matrix4x4Util.GetRotation(rotated).eulerAngles; } else { socket.Anchor = socket.Anchor + offset; } sockets[i] = socket; } transformedModel.Sockets = sockets; // 5. count all voxels transformedModel.NumVoxels = transformedModel.NumVoxels - (conflictVoxels.Count - numberOfConflictsSolved); transformedModel.RecalculateNumVoxels(true); return(transformedModel); }
override protected NPVoxModel CreateProduct(NPVoxModel reuse = null) { if (Input == null) { return(NPVoxModel.NewInvalidInstance(reuse, "Input was null")); } NPVoxModel inputModel = ((NPVoxIModelFactory)Input).GetProduct(); bool hasVoxelGroups = inputModel.HasVoxelGroups(); if (AffectedArea.Equals(NPVoxBox.INVALID)) { NPVoxModel model = NPVoxModel.NewInstance(inputModel, reuse); model.CopyOver(inputModel); return(model); } NPVoxBox clampedBox = inputModel.Clamp(AffectedArea); NPVoxModel transformedModel = null; transformedModel = NPVoxModel.NewInstance(inputModel, reuse); if (hasVoxelGroups) { transformedModel.InitVoxelGroups(); } transformedModel.NumVoxelGroups = inputModel.NumVoxelGroups; transformedModel.NumVoxels = inputModel.NumVoxels; transformedModel.Colortable = inputModel.Colortable != null ? (Color32[])inputModel.Colortable.Clone() : null; transformedModel.Sockets = inputModel.Sockets != null ? (NPVoxSocket[])inputModel.Sockets.Clone() : null; NPVoxBoneModel transformedBoneModel = transformedModel as NPVoxBoneModel; NPVoxBoneModel inputBoneModel = inputModel as NPVoxBoneModel; bool isBoneModel = false; if (transformedBoneModel != null) { transformedBoneModel.AllBones = NPVoxBone.CloneBones(inputBoneModel.AllBones); isBoneModel = true; } byte brightenedColor = NPVoxModelUtils.FindUnusedColor(inputModel); if (brightenedColor == 0) { Debug.LogWarning("could not find a free color to brighten the model"); } Color32 brightenColor32 = inputModel.Colortable[brightenedColor]; foreach (NPVoxCoord coord in inputModel.EnumerateVoxels()) { if (!isBoneModel) { if (clampedBox.Contains(coord) && brightenedColor != 0) { brightenColor32 = inputModel.Colortable[inputModel.GetVoxel(coord)]; transformedModel.SetVoxel(coord, brightenedColor); } else { transformedModel.SetVoxel(coord, inputModel.GetVoxel(coord)); } } else { if (hiddenBonesMask == 0 || !inputBoneModel.IsInBoneMask(coord, hiddenBonesMask)) { if (clampedBox.Contains(coord) && brightenedColor != 0 && inputBoneModel.IsInBoneMask(coord, boneMask)) { brightenColor32 = inputModel.Colortable[inputModel.GetVoxel(coord)]; transformedModel.SetVoxel(coord, brightenedColor); } else { transformedModel.SetVoxel(coord, inputModel.GetVoxel(coord)); } } transformedBoneModel.SetBoneMask(coord, inputBoneModel.GetBoneMask(coord)); } if (hasVoxelGroups) { transformedModel.SetVoxelGroup(coord, inputModel.GetVoxelGroup(coord)); } } if (brightenedColor != 0) { transformedModel.Colortable[brightenedColor] = NPVoxModelUtils.BrightenColor(brightenColor32); } transformedModel.RecalculateNumVoxels(true); return(transformedModel); }
public void ChangeAffectedArea(NPVoxBox newBox) { this.AffectedArea = newBox; }
override protected Mesh CreateProduct(Mesh mesh = null) { // Debug.Log("create product"); NPVoxMeshOutput meshOutput = (Input as NPVoxMeshOutput); if (meshOutput && meshOutput.GetProduct() && TextureAtlas) { TextureAtlas.GetMaterial(SourceMaterial); NPVoxFaces includedFaces = GetIncludedFaces(); NPVoxToUnity npVoxToUnity = InputMeshFactory.GetNPVoxToUnity(); int faceCount = GetFaceCount(); NPVoxBox originalBox = InputMeshFactory.GetVoxModel().BoundingBox; NPVoxBox cutoutBox = originalBox.Clone(); NPVoxFaces cutout = InputMeshFactory.Cutout; Vector3 cutoutOffset = Vector3.zero; if (cutout != null) { Vector3 originalCenter = cutoutBox.SaveCenter; cutoutBox.Left = (sbyte)Mathf.Abs(cutout.Left); cutoutBox.Down = (sbyte)Mathf.Abs(cutout.Down); cutoutBox.Back = (sbyte)Mathf.Abs(cutout.Back); cutoutBox.Right = (sbyte)(cutoutBox.Right - (sbyte)Mathf.Abs(cutout.Right)); cutoutBox.Up = (sbyte)(cutoutBox.Up - (sbyte)Mathf.Abs(cutout.Up)); cutoutBox.Forward = (sbyte)(cutoutBox.Forward - (sbyte)Mathf.Abs(cutout.Forward)); cutoutOffset = Vector3.Scale(originalCenter - cutoutBox.SaveCenter, InputMeshFactory.VoxelSize); } // we have to be careful. Unlike cutout, which is already removed from the mesh we want to render, the inset is not yet applied and // also won't result in a "move" of the object. So it's important that we calculate a correct offset for our final mesh. NPVoxBox insetBox = cutoutBox.Clone(); Vector3 insetOffset = Vector3.zero; if (inset != null) { Vector3 cutoutCenter = cutoutBox.SaveCenter; insetBox.Left += (sbyte)Mathf.Abs(inset.Left); insetBox.Right -= (sbyte)Mathf.Abs(inset.Right); insetBox.Down += (sbyte)Mathf.Abs(inset.Down); insetBox.Up -= (sbyte)Mathf.Abs(inset.Up); insetBox.Back += (sbyte)Mathf.Abs(inset.Back); insetBox.Forward -= (sbyte)Mathf.Abs(inset.Forward); insetOffset = Vector3.Scale(cutoutCenter - insetBox.SaveCenter, InputMeshFactory.VoxelSize); } Vector3 insetCenter = insetBox.SaveCenter; if (Baked45Angle) { backSlot = CreateTexture(backSlot, includedFaces.Back != 0, insetBox.Size.X, insetBox.Size.Y, Quaternion.Euler(-45, 0, 0), npVoxToUnity.ToUnityPosition(new Vector3(insetCenter.x, insetCenter.y, insetBox.Back)), npVoxToUnity.ToUnityDirection(new Vector2(insetBox.Size.X, ((float)insetBox.Size.Y) / Mathf.Sqrt(2))) * 0.5f, Quaternion.Euler(+45, 0, 0) ); downSlot = CreateTexture(downSlot, includedFaces.Down != 0, insetBox.Size.X, insetBox.Size.Z * 3, Quaternion.Euler(-45, 0, 0), npVoxToUnity.ToUnityPosition(new Vector3(insetCenter.x, insetBox.Down, insetCenter.z)), npVoxToUnity.ToUnityDirection(new Vector2(insetBox.Size.X, ((float)insetBox.Size.Z) / Mathf.Sqrt(2))) * 0.5f, Quaternion.Euler(-45, 0, 0) ); leftSlot = CreateTexture(leftSlot, false, 0, 0, Quaternion.identity, Vector3.zero, Vector2.zero, Quaternion.identity); rightSlot = CreateTexture(rightSlot, false, 0, 0, Quaternion.identity, Vector3.zero, Vector2.zero, Quaternion.identity); upSlot = CreateTexture(upSlot, false, 0, 0, Quaternion.identity, Vector3.zero, Vector2.zero, Quaternion.identity); forwardSlot = CreateTexture(forwardSlot, false, 0, 0, Quaternion.identity, Vector3.zero, Vector2.zero, Quaternion.identity); } else { leftSlot = CreateTexture(leftSlot, includedFaces.Left != 0, insetBox.Size.Z, insetBox.Size.Y, Quaternion.Euler(0, 90, 0), npVoxToUnity.ToUnityPosition(new Vector3(insetBox.Left, insetCenter.y, insetCenter.z)), npVoxToUnity.ToUnityDirection(new Vector2(insetBox.Size.Z, insetBox.Size.Y)) * 0.5f, Quaternion.identity ); rightSlot = CreateTexture(rightSlot, includedFaces.Right != 0, insetBox.Size.Z, insetBox.Size.Y, Quaternion.Euler(0, -90, 0), npVoxToUnity.ToUnityPosition(new Vector3(insetBox.Right, insetCenter.y, insetCenter.z)), npVoxToUnity.ToUnityDirection(new Vector2(insetBox.Size.Z, insetBox.Size.Y)) * 0.5f, Quaternion.identity ); downSlot = CreateTexture(downSlot, includedFaces.Down != 0, insetBox.Size.X, insetBox.Size.Z, Quaternion.Euler(-90, 0, 0), npVoxToUnity.ToUnityPosition(new Vector3(insetCenter.x, insetBox.Down, insetCenter.z)), npVoxToUnity.ToUnityDirection(new Vector2(insetBox.Size.X, insetBox.Size.Z)) * 0.5f, Quaternion.identity ); upSlot = CreateTexture(upSlot, includedFaces.Up != 0, insetBox.Size.X, insetBox.Size.Z, Quaternion.Euler(90, 0, 180), npVoxToUnity.ToUnityPosition(new Vector3(insetCenter.x, insetBox.Up, insetCenter.z)), npVoxToUnity.ToUnityDirection(new Vector2(insetBox.Size.X, insetBox.Size.Z)) * 0.5f, Quaternion.identity ); backSlot = CreateTexture(backSlot, includedFaces.Back != 0, insetBox.Size.X, insetBox.Size.Y, Quaternion.Euler(0, 0, 0), npVoxToUnity.ToUnityPosition(new Vector3(insetCenter.x, insetCenter.y, insetBox.Back)), npVoxToUnity.ToUnityDirection(new Vector2(insetBox.Size.X, insetBox.Size.Y)) * 0.5f, Quaternion.identity ); forwardSlot = CreateTexture(forwardSlot, includedFaces.Forward != 0, insetBox.Size.X, insetBox.Size.Y, Quaternion.Euler(-180, 0, 0), npVoxToUnity.ToUnityPosition(new Vector3(insetCenter.x, insetCenter.y, insetBox.Forward)), npVoxToUnity.ToUnityDirection(new Vector2(insetBox.Size.X, insetBox.Size.Y)) * 0.5f, Quaternion.identity ); } slotsAllocatedAtTA = TextureAtlas; if (mesh == null) { mesh = new Mesh(); } else { mesh.Clear(); } mesh.name = "zzz Cube Simplifier Mesh"; int border = 1; var vertices = new Vector3[faceCount * 4]; var uvs = new Vector2[faceCount * 4]; var tris = new int[faceCount * 3 * 2]; var normals = new Vector3[faceCount * 4]; int v = 0; int t = 0; int v0 = 0; System.Action <Vector3, NPVoxTextureAtlas.Slot> addQuad = (Vector3 dir, NPVoxTextureAtlas.Slot theSlot) => { normals[v0] = dir; normals[v0 + 1] = dir; normals[v0 + 2] = dir; normals[v0 + 3] = dir; tris[t++] = v0; tris[t++] = v0 + 1; tris[t++] = v0 + 2; tris[t++] = v0; tris[t++] = v0 + 2; tris[t++] = v0 + 3; Vector2 uvMax = theSlot.GetUVmax(border); Vector2 uvMin = theSlot.GetUVmin(border); uvs[v0].x = uvMin.x; uvs[v0].y = uvMax.y; uvs[v0 + 1].x = uvMax.x; uvs[v0 + 1].y = uvMax.y; uvs[v0 + 2].x = uvMax.x; uvs[v0 + 2].y = uvMin.y; uvs[v0 + 3].x = uvMin.x; uvs[v0 + 3].y = uvMin.y; }; NPVoxBox bounds = insetBox; Vector3 LDB = cutoutOffset + npVoxToUnity.ToUnityPosition(bounds.LeftDownBack) + (Vector3.left * npVoxToUnity.VoxeSize.x + Vector3.down * npVoxToUnity.VoxeSize.y + Vector3.back * npVoxToUnity.VoxeSize.z) * 0.5f; Vector3 RDB = cutoutOffset + npVoxToUnity.ToUnityPosition(bounds.RightDownBack) + (Vector3.right * npVoxToUnity.VoxeSize.x + Vector3.down * npVoxToUnity.VoxeSize.y + Vector3.back * npVoxToUnity.VoxeSize.z) * 0.5f; Vector3 LUB = cutoutOffset + npVoxToUnity.ToUnityPosition(bounds.LeftUpBack) + (Vector3.left * npVoxToUnity.VoxeSize.x + Vector3.up * npVoxToUnity.VoxeSize.y + Vector3.back * npVoxToUnity.VoxeSize.z) * 0.5f; Vector3 RUB = cutoutOffset + npVoxToUnity.ToUnityPosition(bounds.RightUpBack) + (Vector3.right * npVoxToUnity.VoxeSize.x + Vector3.up * npVoxToUnity.VoxeSize.y + Vector3.back * npVoxToUnity.VoxeSize.z) * 0.5f; Vector3 LDF = cutoutOffset + npVoxToUnity.ToUnityPosition(bounds.LeftDownForward) + (Vector3.left * npVoxToUnity.VoxeSize.x + Vector3.down * npVoxToUnity.VoxeSize.y + Vector3.forward * npVoxToUnity.VoxeSize.z) * 0.5f; Vector3 RDF = cutoutOffset + npVoxToUnity.ToUnityPosition(bounds.RightDownForward) + (Vector3.right * npVoxToUnity.VoxeSize.x + Vector3.down * npVoxToUnity.VoxeSize.y + Vector3.forward * npVoxToUnity.VoxeSize.z) * 0.5f; Vector3 LUF = cutoutOffset + npVoxToUnity.ToUnityPosition(bounds.LeftUpForward) + (Vector3.left * npVoxToUnity.VoxeSize.x + Vector3.up * npVoxToUnity.VoxeSize.y + Vector3.forward * npVoxToUnity.VoxeSize.z) * 0.5f; Vector3 RUF = cutoutOffset + npVoxToUnity.ToUnityPosition(bounds.RightUpForward) + (Vector3.right * npVoxToUnity.VoxeSize.x + Vector3.up * npVoxToUnity.VoxeSize.y + Vector3.forward * npVoxToUnity.VoxeSize.z) * 0.5f; if (downSlot != null) { v0 = v; vertices[v++] = LDB; vertices[v++] = RDB; vertices[v++] = RDF; vertices[v++] = LDF; addQuad(Vector3.down, downSlot); } if (upSlot != null) { v0 = v; vertices[v++] = RUB; vertices[v++] = LUB; vertices[v++] = LUF; vertices[v++] = RUF; addQuad(Vector3.up, upSlot); } if (forwardSlot != null) { v0 = v; vertices[v++] = LDF; vertices[v++] = RDF; vertices[v++] = RUF; vertices[v++] = LUF; addQuad(Vector3.forward, forwardSlot); } if (backSlot != null) { v0 = v; vertices[v++] = LUB; vertices[v++] = RUB; vertices[v++] = RDB; vertices[v++] = LDB; addQuad(Vector3.back, backSlot); } if (leftSlot != null) { v0 = v; vertices[v++] = LUF; vertices[v++] = LUB; vertices[v++] = LDB; vertices[v++] = LDF; addQuad(Vector3.left, leftSlot); } if (rightSlot != null) { v0 = v; vertices[v++] = RUB; vertices[v++] = RUF; vertices[v++] = RDF; vertices[v++] = RDB; addQuad(Vector3.right, rightSlot); } mesh.vertices = vertices; mesh.triangles = tris; mesh.uv = uvs; mesh.RecalculateBounds(); mesh.normals = normals; TangentSolver.Solve(mesh); // mesh.bounds = new Bounds( // insetOffset, // new Vector3( // bounds.Size.X * npVoxToUnity.VoxeSize.x, // bounds.Size.Y * npVoxToUnity.VoxeSize.y, // bounds.Size.Z * npVoxToUnity.VoxeSize.z // ) // ); Mesh sourceMesh = meshOutput.GetProduct(); mesh.bounds = sourceMesh.bounds; mesh.name = "zzz Cube Mesh "; return(mesh); } else { Debug.LogWarning("No Input set up"); if (mesh == null) { mesh = new Mesh(); } else { mesh.Clear(); } return(mesh); } }