//traverses the tree, but on a thread task
            void DrawOnThread(ThreadDrawer thread, DrawState state, IDraw[] children, BinaryNode[] nodes, bool treeIsOptimized)
            {
                if (left == 0)
                {
                    ThreadDrawnInstance inst;
                    inst.childCount = this.childCount;
                    inst.firstChild = this.firstChild;
                    if (thread.instanceCount == thread.instances.Length)
                    {
                        Array.Resize(ref thread.instances, thread.instances.Length * 2);
                    }
                    thread.instances[thread.instanceCount] = inst;
                    thread.instanceCount++;
                }
                else
                {
                    if (treeIsOptimized)
                    {
                        uint firstChild = this.firstChild;
                        firstChild <<= ChildCountShift;
                        uint lastChild = this.childCount;
                        lastChild <<= ChildCountShift;                         // actually lastChild + 1

                        uint count = lastChild - firstChild;

                        ushort firstChildIndex = this.firstChild;
                        ThreadDrawnInstance inst;

                        while (count > ushort.MaxValue)
                        {
                            if (thread.instanceCount == thread.instances.Length)
                            {
                                Array.Resize(ref thread.instances, thread.instances.Length * 2);
                            }

                            firstChildIndex += ushort.MaxValue / ChildCount;
                            count           -= ushort.MaxValue;

                            inst.childCount = ushort.MaxValue;
                            inst.firstChild = firstChildIndex;

                            thread.instances[thread.instanceCount++] = inst;
                        }

                        if (thread.instanceCount == thread.instances.Length)
                        {
                            Array.Resize(ref thread.instances, thread.instances.Length * 2);
                        }

                        inst.childCount = (ushort)count;
                        inst.firstChild = firstChildIndex;

                        thread.instances[thread.instanceCount++] = inst;
                    }
                    else
                    {
                        nodes[left].DrawOnThread(thread, state, children, nodes, treeIsOptimized);
                        nodes[right].DrawOnThread(thread, state, children, nodes, treeIsOptimized);
                    }
                }
            }
            //traverses the tree, but on a thread task
            public void DrawOnThread(ThreadDrawer thread, DrawState state, float[] boundsMin, float[] boundsMax, int axis, IDraw[] children, BinaryNode[] nodes, bool isIdentityMatrix, bool treeIsOptimized)
            {
                boundsMin[axis] = min;
                boundsMax[axis] = max;

                Vector3 localMin = new Vector3(), localMax = new Vector3();

                localMin.X = boundsMin[0];
                localMin.Y = boundsMin[1];
                localMin.Z = boundsMin[2];

                localMax.X = boundsMax[0];
                localMax.Y = boundsMax[1];
                localMax.Z = boundsMax[2];

                ContainmentType type;

                if (!isIdentityMatrix)
                {
                    type = state.Culler.IntersectBox(ref localMin, ref localMax);
                }
                else
                {
                    type = state.Culler.IntersectWorldBox(ref localMin, ref localMax);
                }

                switch (type)
                {
                case ContainmentType.Contains:
                    DrawOnThread(thread, state, children, nodes, treeIsOptimized);
                    break;

                case ContainmentType.Intersects:
                    if (left == 0)
                    {
                        ThreadDrawnInstance inst;
                        inst.childCount = this.childCount;
                        inst.firstChild = this.firstChild;
                        if (thread.cullTestInstanceCount == thread.cullTestInstances.Length)
                        {
                            Array.Resize(ref thread.cullTestInstances, thread.cullTestInstances.Length * 2);
                        }
                        thread.cullTestInstances[thread.cullTestInstanceCount] = inst;
                        thread.cullTestInstanceCount++;
                    }
                    else
                    {
                        nodes[left].DrawOnThread(thread, state, boundsMin, boundsMax, axis == 2 ? 0 : axis + 1, children, nodes, isIdentityMatrix, treeIsOptimized);

                        boundsMin[0] = localMin.X;
                        boundsMin[1] = localMin.Y;
                        boundsMin[2] = localMin.Z;

                        boundsMax[0] = localMax.X;
                        boundsMax[1] = localMax.Y;
                        boundsMax[2] = localMax.Z;

                        nodes[right].DrawOnThread(thread, state, boundsMin, boundsMax, axis == 2 ? 0 : axis + 1, children, nodes, isIdentityMatrix, treeIsOptimized);

                        boundsMin[0] = localMin.X;
                        boundsMin[1] = localMin.Y;
                        boundsMin[2] = localMin.Z;

                        boundsMax[0] = localMax.X;
                        boundsMax[1] = localMax.Y;
                        boundsMax[2] = localMax.Z;
                    }
                    break;
                }
            }
        //similar to a normal draw, but stops when depth == threadLevel, and then runs a thread process from there
        private void DrawItemsThread(DrawState state, ushort node, int depth, ref int index, float[] boundsMin, float[] boundsMax)
        {
            boundsMin[depth % 3] = allNodes[node].min;
            boundsMax[depth % 3] = allNodes[node].max;

            Vector3 localMin = new Vector3(), localMax = new Vector3();

            localMin.X = boundsMin[0];
            localMin.Y = boundsMin[1];
            localMin.Z = boundsMin[2];

            localMax.X = boundsMax[0];
            localMax.Y = boundsMax[1];
            localMax.Z = boundsMax[2];

            ContainmentType type;

            if (!threads[0].idenityMatrix)
            {
                type = state.Culler.IntersectBox(ref localMin, ref localMax);
            }
            else
            {
                type = state.Culler.IntersectWorldBox(ref localMin, ref localMax);
            }

            switch (type)
            {
            case ContainmentType.Contains:
                allNodes[node].Draw(state, allChildren, allNodes, IsOptimizedState);
                break;

            case ContainmentType.Intersects:
                if (allNodes[node].left == 0 || depth == threadLevel)
                {
                    ThreadDrawer thread = this.threads[index++];
                    for (int i = 0; i < 3; i++)
                    {
                        thread.minBuffer[i] = boundsMin[i];
                        thread.maxBuffer[i] = boundsMax[i];
                    }

                    //carry on from here on a thread task
                    thread.startIndex = node;
                    thread.callback   = state.Application.ThreadPool.QueueTask(thread, state);
                }
                else
                {
                    DrawItemsThread(state, allNodes[node].left, depth + 1, ref index, boundsMin, boundsMax);

                    boundsMin[0] = localMin.X;
                    boundsMin[1] = localMin.Y;
                    boundsMin[2] = localMin.Z;

                    boundsMax[0] = localMax.X;
                    boundsMax[1] = localMax.Y;
                    boundsMax[2] = localMax.Z;


                    DrawItemsThread(state, allNodes[node].right, depth + 1, ref index, boundsMin, boundsMax);

                    boundsMin[0] = localMin.X;
                    boundsMin[1] = localMin.Y;
                    boundsMin[2] = localMin.Z;

                    boundsMax[0] = localMax.X;
                    boundsMax[1] = localMax.Y;
                    boundsMax[2] = localMax.Z;
                }
                break;
            }
        }