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 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);
    }
    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 static NPVoxModel SocketTransform(NPVoxModel sourceModel, NPVoxModel targetModel, NPVoxSocket sourceSocket, NPVoxSocket targetSocket, ResolveConflictMethodType resolveConflictMethod = ResolveConflictMethodType.CLOSEST, NPVoxModel reuse = null)
    {
        NPVoxToUnity sourceVox2Unity = new NPVoxToUnity(sourceModel, Vector3.one);
        NPVoxToUnity targetVox2Unity = new NPVoxToUnity(targetModel, Vector3.one);

        Vector3    sourceAnchorPos = sourceVox2Unity.ToUnityPosition(sourceSocket.Anchor);
        Quaternion sourceRotation  = Quaternion.Euler(sourceSocket.EulerAngles);
        Vector3    targetAnchorPos = targetVox2Unity.ToUnityPosition(targetSocket.Anchor);
        Quaternion targetRotation  = Quaternion.Euler(targetSocket.EulerAngles);
        Vector3    diff            = sourceVox2Unity.ToSaveVoxDirection(targetAnchorPos - sourceAnchorPos);

        Matrix4x4 A = Matrix4x4.TRS(sourceVox2Unity.ToSaveVoxCoord(sourceAnchorPos), Quaternion.identity, Vector3.one);
        Matrix4x4 B = Matrix4x4.TRS(Vector3.zero, sourceRotation, Vector3.one).inverse *Matrix4x4.TRS(Vector3.zero, targetRotation, Vector3.one);
        Matrix4x4 C = Matrix4x4.TRS(-sourceVox2Unity.ToSaveVoxCoord(sourceAnchorPos), Quaternion.identity, Vector3.one);
        Matrix4x4 D = Matrix4x4.TRS(diff, Quaternion.identity, Vector3.one);

        Matrix4x4 transformMatrix = D * A * B * C;

        return(Transform(sourceModel, sourceModel.BoundingBox, transformMatrix, resolveConflictMethod, reuse));
    }