public void DeleteBox(NPVoxBone bone, NPVoxBox box)
    {
        Undo.RecordObject(this, "Delete Box");
        List <NPVoxBox> boxes = GetBoxes(bone);

        boxes.Remove(box);
    }
예제 #2
0
 public override void Initialize(NPVoxCoord size)
 {
     base.Initialize(size);
     boneMasks = new uint[size.X * size.Y * size.Z];
     RootBone  = new NPVoxBone("Root", 0, null);
     AllBones  = new NPVoxBone[] { };
 }
    public void SetBoneMask(uint mask, bool includingDescendants)
    {
        NPVoxBoneModel transformedModel = CurrentModelFactory.GetProduct() as NPVoxBoneModel;

        NPVoxBone[] allBones = ((NPVoxBoneModel)transformedModel).AllBones;
        SetBoneMask(includingDescendants ? NPVoxBone.GetMaskWithDescendants(ref allBones, mask) : mask);
    }
    public NPVoxBox AddBox(NPVoxBone bone)
    {
        Undo.RecordObject(this, "Add Box");
        List <NPVoxBox> boxes = GetBoxes(bone);

        boxes.Add(new NPVoxBox(NPVoxCoordUtil.ToCoord(bone.Anchor), NPVoxCoordUtil.ToCoord(bone.Anchor) + NPVoxCoord.ONE));
        return(boxes[boxes.Count - 1]);
    }
예제 #5
0
    // ===================================================================================================
    // Tools
    // ===================================================================================================

    public void RecenterBonePivot(NPVoxBoneModel model)
    {
        NPVoxBone[] bones = NPVoxBone.GetRootBones(ref model.AllBones, NPVoxBone.GetBonesInMask(ref model.AllBones, boneMask));
        if (bones.Length == 1)
        {
            Vector3 pivotOrigin        = GetAffectedBox().SaveCenter;
            Vector3 pivotForSingleBone = model.GetAffectedArea(1u << (bones[0].ID - 1)).SaveCenter;
            PivotOffset = pivotForSingleBone - pivotOrigin;
        }
    }
예제 #6
0
 public override void CopyOver(NPVoxModel source)
 {
     base.CopyOver(source);
     if (source is NPVoxBoneModel)
     {
         NPVoxBoneModel boneModel = (source as NPVoxBoneModel);
         boneMasks     = (uint[])boneModel.boneMasks.Clone();
         this.AllBones = NPVoxBone.CloneBones(boneModel.AllBones);
     }
 }
    public List <NPVoxBox> GetBoxes(NPVoxBone bone)
    {
        if (bone.ID == 0)
        {
            return(new List <NPVoxBox>());
        }

        List <NPVoxBox> boxes = this.AllBoxes[bone.ID - 1].Boxes;

        if (boxes == null)
        {
            boxes = this.AllBoxes[bone.ID - 1].Boxes = new List <NPVoxBox>();
        }
        return(boxes);
    }
예제 #8
0
 public NPVoxBone(string name, int id, NPVoxBone parent)
 {
     Name = name;
     ID   = id;
     if (parent != null)
     {
         ParentID = parent.ID;
         Mask     = parent.Mask | (1u << (id - 1));
     }
     else
     {
         Mask     = ID != 0 ? (1u << (id - 1)) : 0;
         ParentID = 0;
     }
 }
예제 #9
0
 public static bool IsDescendant(ref NPVoxBone[] allBones, NPVoxBone parent, NPVoxBone descendant)
 {
     if (descendant.ParentID == parent.ID)
     {
         return(true);
     }
     else if (descendant.ParentID == 0)
     {
         return(false);
     }
     else
     {
         return(IsDescendant(ref allBones, parent, GetBoneByID(ref allBones, descendant.ParentID)));
     }
 }
    public NPVoxBone AddBone(NPVoxBone parent)
    {
        Undo.RecordObject(this, "Add Bone");
        NPVoxBone child = NPVoxBone.AddBone(ref AllBones, parent);

        if (child != null)
        {
            child.Name = parent.Name;
            if (Input != null)
            {
                NPVoxModel model = ((NPVoxIModelFactory)Input).GetProduct();
                child.Anchor = model.BoundingBox.SaveCenter;
            }
            return(child);
        }
        return(null);
    }
    public void ToggleBoneMask(uint mask, bool includingDescendants)
    {
        NPVoxSkeletonTransformer t = ((NPVoxSkeletonTransformer)SelectedTransformer);
        NPVoxBoneModel           transformedModel = CurrentModelFactory.GetProduct() as NPVoxBoneModel;

        NPVoxBone[] allBones   = ((NPVoxBoneModel)transformedModel).AllBones;
        uint        toggleMask = includingDescendants ? NPVoxBone.GetMaskWithDescendants(ref allBones, mask) : mask;

        if ((mask & t.BoneMask) != 0)
        {
            SetBoneMask(t.BoneMask & ~toggleMask);
        }
        else
        {
            SetBoneMask(t.BoneMask | toggleMask);
        }
    }
예제 #12
0
    public static NPVoxBone[] CloneBones(NPVoxBone[] allBones)
    {
        NPVoxBone[] bones = new NPVoxBone[allBones.Length];
        for (int i = 0; i < allBones.Length; i++)
        {
            if (allBones[i] != null && allBones[i].ID > 0)
            {
                NPVoxBone sourceBone = allBones[i];
                UnityEngine.Assertions.Assert.AreEqual(i, sourceBone.ID - 1);
                bones[i]             = new NPVoxBone(sourceBone.Name, sourceBone.ID, sourceBone.ParentID > 0 ? bones[sourceBone.ParentID - 1] : null);
                bones[i].Anchor      = sourceBone.Anchor;
                bones[i].EulerAngles = sourceBone.EulerAngles;
            }
        }

        return(bones);
    }
예제 #13
0
 // ===================================================================================================
 // Name
 // ===================================================================================================
 protected void RegenerateName(NPVoxBoneModel model)
 {
     if (regenerateName)
     {
         regenerateName = false;
         NPVoxBone[] bones = NPVoxBone.GetRootBones(ref model.AllBones, NPVoxBone.GetBonesInMask(ref model.AllBones, boneMask));
         string      name  = "";
         foreach (NPVoxBone bone in bones)
         {
             if (!string.IsNullOrEmpty(name))
             {
                 name += ", ";
             }
             name += bone.Name;
         }
         this.InstanceName = name;
     }
 }
예제 #14
0
    public static NPVoxBone AddBone(ref NPVoxBone[] allBones, NPVoxBone parent)
    {
        int nextBoneID = GetNextBoneID(allBones, parent.ID);

        if (nextBoneID < 0)
        {
            return(null);
        }
        NPVoxBone newBone = new NPVoxBone("new", nextBoneID, parent);

        if (allBones.Length > nextBoneID - 1)
        {
            allBones[nextBoneID - 1] = newBone;
        }
        else
        {
            UnityEditor.ArrayUtility.Add(ref allBones, newBone);
        }
        return(newBone);
    }
    public System.Func <NPVoxISceneEditable, bool> DrawSceneTool(NPVoxToUnity npVoxToUnity, UnityEngine.Transform transform, int tool)
    {
        // offset

        if (CurrentEditedBone == null)
        {
            return(null);
        }

        NPVoxBoneModel boneModel = GetProduct() as NPVoxBoneModel;

        if (boneModel == null)
        {
            return(null);
        }

        if (lastMask != 1u << (CurrentEditedBone.ID - 1))
        {
            lastMask     = 1u << (CurrentEditedBone.ID - 1);
            currentPivot = npVoxToUnity.ToUnityPosition(boneModel.GetAffectedArea(lastMask).SaveCenter);
        }

        Vector3 offset = npVoxToUnity.ToUnityDirection(CurrentEditedBone.Anchor);

        if (tool == 0)
        {
            offset = npVoxToUnity.ToSaveVoxDirection(Handles.PositionHandle(currentPivot + offset, Quaternion.identity) - currentPivot);
            if (offset != CurrentEditedBone.Anchor)
            {
                return((NPVoxISceneEditable t) =>
                {
                    NPVoxBone.GetBoneByID(ref ((NPVoxSkeletonBuilder)t).AllBones, CurrentEditedBone.ID).Anchor = offset;
                    return true;
                });
            }
        }

        return(null);
    }
    override protected NPVoxModel CreateProduct(NPVoxModel reuse = null)
    {
        if (Input == null)
        {
            return(NPVoxModel.NewInvalidInstance(reuse, "No Input Setup"));
        }

        NPVoxModel model = ((NPVoxIModelFactory)Input).GetProduct();

        if (model is NPVoxBoneModel)
        {
            Debug.LogError("cannot create bone model on top of another bone model");
            return(model);
        }

        NPVoxBoneModel newModel = NPVoxBoneModel.NewInstance(model.Size, reuse as NPVoxBoneModel);

        newModel.CopyOver(model);

        // setup bone masks
        newModel.AllBones = NPVoxBone.CloneBones(AllBones);
        for (int i = 0; i < AllBones.Length; i++)
        {
            NPVoxBone       bone  = AllBones[i];
            List <NPVoxBox> boxes = AllBoxes[i].Boxes;
            if (boxes != null)
            {
                foreach (NPVoxBox box in boxes)
                {
                    foreach (NPVoxCoord coord in box.Enumerate())
                    {
                        newModel.AddBoneMask(coord, 1u << (bone.ID - 1));
                    }
                }
            }
        }

        return(newModel);
    }
예제 #17
0
    public static uint GetMaskWithDescendants(ref NPVoxBone[] allBones, uint mask)
    {
        uint completeMask = mask;

        for (int i = 0; i < 32; i++)
        {
            int id = i + 1;
            if (((mask >> i) & 0x1) != 0)
            {
                NPVoxBone bone = GetBoneByID(ref allBones, id);
                if (bone != null)
                {
                    NPVoxBone[] descendants = bone.GetDescendants(allBones);
                    foreach (NPVoxBone descendant in descendants)
                    {
                        completeMask |= (1u << (descendant.ID - 1));
                    }
                }
            }
        }

        return(completeMask);
    }
예제 #18
0
    public NPVoxBone[] GetChildren(NPVoxBone[] allBones)
    {
        int numBones = 0;

        foreach (NPVoxBone bone in allBones)
        {
            if (bone != null && bone.ID > 0 && bone.ParentID == this.ID)
            {
                numBones++;
            }
        }

        NPVoxBone[] bones = new NPVoxBone[numBones];
        numBones = 0;
        foreach (NPVoxBone bone in allBones)
        {
            if (bone != null && bone.ID > 0 && bone.ParentID == this.ID)
            {
                bones[numBones++] = bone;
            }
        }

        return(bones);
    }
예제 #19
0
 public static void DeleteBone(ref NPVoxBone[] allBones, NPVoxBone bone)
 {
     allBones[bone.ID - 1].ID = -1;
 }
예제 #20
0
    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 DeleteBone(NPVoxBone bone)
 {
     Undo.RecordObject(this, "Delete Bone");
     NPVoxBone.DeleteBone(ref AllBones, bone);
 }
    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);
    }
    private bool DrawBone(int currentLevel, NPVoxBone bone, bool isRoot, int numSiblings, float lastY)
    {
        EditorGUILayout.BeginHorizontal();

        NPVoxBone[] bones = bone.GetChildren(AllBones);

        if (CurrentEditedBone == bone)
        {
            if (GUILayout.Button("Close", GUILayout.Width(40)))
            {
                CurrentEditedBone = null;
                CurrentEditedBox  = null;
                return(true);
            }
        }
        else
        {
            if (GUILayout.Button("Edit", GUILayout.Width(40)))
            {
                CurrentEditedBone = bone;
                CurrentEditedBox  = GetBoxes(bone).Count > 0 ? GetBoxes(bone)[0] : null;
                return(true);
            }
        }

        GUILayout.Space(10f * currentLevel);

        Rect rect = GUILayoutUtility.GetLastRect();

        if (numSiblings > 1)
        {
            float currentX = rect.xMin + 10f * currentLevel;
            Handles.color = Color.black;
            Handles.DrawLine(new Vector2(currentX, lastY + 18), new Vector2(currentX, rect.yMax + 10));
            Handles.DrawLine(new Vector2(currentX, rect.yMax + 10), new Vector2(currentX + 10, rect.yMax + 10));
        }

        if (bones.Length > 1)
        {
            lastY = rect.y;
        }

        EditorGUILayout.BeginVertical();
        string newName = EditorGUILayout.TextField(bone.Name);

        if (newName != bone.Name)
        {
            bone.Name = newName;
            return(true);
        }

        if (CurrentEditedBone == bone)
        {
            EditorGUILayout.BeginHorizontal();
            GUILayout.Space(10f);

            if (bone.ID != 0 && bones.Length == 0 && GUILayout.Button("Delete "))
            {
                DeleteBone(bone);
                CurrentEditedBox  = null;
                CurrentEditedBone = null;
                return(true);
            }

            if (GUILayout.Button("+ Bone"))
            {
                CurrentEditedBox  = null;
                CurrentEditedBone = AddBone(bone);
                return(true);
            }

            if (!isRoot)
            {
                if (GUILayout.Button("+ Box "))
                {
                    CurrentEditedBox = AddBox(bone);
                    return(true);
                }

                if (CurrentCopiedBox != null && GUILayout.Button("+ Paste"))
                {
                    CurrentEditedBox = AddBox(bone);
                    CurrentEditedBox.LeftDownBack   = CurrentCopiedBox.LeftDownBack;
                    CurrentEditedBox.RightUpForward = CurrentCopiedBox.RightUpForward;
                    return(true);
                }

                List <NPVoxBox> boxes = GetBoxes(bone);
                int             i     = 0;
                foreach (NPVoxBox box in boxes)
                {
                    EditorGUILayout.EndHorizontal();
                    EditorGUILayout.BeginHorizontal();
                    GUILayout.Space(10f);
                    EditorGUILayout.LabelField("(Box: " + i + ")", GUILayout.Width(100));

                    if (box != CurrentEditedBox)
                    {
                        if (GUILayout.Button("Edit"))
                        {
                            CurrentEditedBox = box;
                            return(true);
                        }
                    }
                    else
                    {
                        if (GUILayout.Button("Close"))
                        {
                            CurrentEditedBox = null;
                            return(true);
                        }
                    }
                    if (GUILayout.Button("Delete"))
                    {
                        DeleteBox(bone, box);
                        CurrentEditedBox = null;
                        return(true);
                    }
                    if (GUILayout.Button("Copy"))
                    {
                        CurrentCopiedBox = box;
                        return(true);
                    }
                    i++;
                }
            }
            EditorGUILayout.EndHorizontal();
        }
        EditorGUILayout.EndHorizontal();

        EditorGUILayout.LabelField("(ID: " + bone.ID + ")", GUILayout.Width(100));
        EditorGUILayout.EndHorizontal();

        for (int i = 0; i < bones.Length; i++)
        {
            NPVoxBone child = bones[i];
            if (child != null && DrawBone(currentLevel + 1, child, false, bones.Length, lastY))
            {
                return(true);
            }
        }

        return(false);
    }