コード例 #1
0
ファイル: FileLoader.cs プロジェクト: PBaccurate/SumoVizUnity
    void loadGeometryFile(string filename)
    {
        var sr           = new StreamReader(Application.dataPath + "/" + filename);
        var fileContents = sr.ReadToEnd();

        sr.Close();

        string[] lines = fileContents.Split("\n"[0]);
        foreach (string line in lines)
        {
            string[] v = line.Split(' ');
            if (v.Length >= 3)
            {
                float height          = 0.0f;
                int   indexCorrection = 0;
                if (!float.TryParse(v[v.Length - 1], out height))
                {
                    indexCorrection = 1;
                }

                if ((v.Length + indexCorrection) % 2 == 0)
                {
                    string         name = v[v.Length - 3 + indexCorrection];
                    List <Vector2> list = new List <Vector2>();
                    for (int i = 1; i < v.Length - 4 + indexCorrection; i = i + 2)
                    {
                        float x;
                        float y;
                        if (float.TryParse(v[i], out x) && float.TryParse(v[i + 1], out y))
                        {
                            list.Add(new Vector2(x, y));
                        }
                    }

                    string type = v[v.Length - 2 + indexCorrection];
                    if (type == "wall")
                    {
                        WallExtrudeGeometry.create(name, list, height, -0.2f);
                    }
                    else if (type == "obstacle")
                    {
                        ObstacleExtrudeGeometry.create(name, list, height);
                    }
                    else if (type == "tree")
                    {
                        TreeGeometry.create(name, list[0]);
                    }
                    else
                    {
                        AreaGeometry.create(name, list);
                    }
                }
            }
        }
    }
コード例 #2
0
        internal void AddCircleVisualization(Transform parent, float radius)
        {
            var scale        = TreeGeometry.SizeToScale(radius, DefaultCirclePlaneRadius, 1);
            var cirvleObject = InstantiateObject(CircleName, CirclePlane, parent,
                                                 localScale: new Vector3(scale, 1, scale));

            cirvleObject.AddComponent <CircleInputHandler>();
            var disposable = ApplicationManager.Instance.AppState.Settings.VisualizeCircles.Subscribe(visualize =>
            {
                cirvleObject.SetActive(visualize);
            });

            cirvleObject.OnDestroyAsObservable().Subscribe(_ => disposable.Dispose());
        }
コード例 #3
0
        /// <summary>
        /// This method must be called whenever the camera changes.  It gets the new
        ///  camera dependent rendering arguments from SpeedTree.
        /// </summary>
        protected void UpdateRenderArgs()
        {
            // let speedtree compute the LOD for this tree
            speedTree.ComputeLodLevel();

            // update geometry for all parts
            TreeGeometry geometry = group.Geometry;

            speedTree.GetGeometry(geometry, SpeedTreeWrapper.GeometryFlags.AllGeometry, -1, -1, -1);

            // copy out branch args
            branchRenderArgs.AlphaTestValue = (int)geometry.BranchAlphaTestValue;
            branchRenderArgs.LOD            = geometry.Branches.DiscreteLodLevel;
            branchRenderArgs.Active         = branchRenderArgs.AlphaTestValue < 255;

            // copy out frond args
            frondRenderArgs.AlphaTestValue = (int)geometry.FrondAlphaTestValue;
            frondRenderArgs.LOD            = geometry.Fronds.DiscreteLodLevel;
            frondRenderArgs.Active         = frondRenderArgs.AlphaTestValue < 255;

            // copy out leaf args
            leaf0RenderArgs.AlphaTestValue = (int)geometry.Leaves0.AlphaTestValue;
            leaf0RenderArgs.LOD            = geometry.Leaves0.DiscreteLodLevel;
            leaf0RenderArgs.Active         = geometry.Leaves0.IsActive;
            leaf1RenderArgs.AlphaTestValue = (int)geometry.Leaves1.AlphaTestValue;
            leaf1RenderArgs.LOD            = geometry.Leaves1.DiscreteLodLevel;
            leaf1RenderArgs.Active         = geometry.Leaves1.IsActive;

            // copy out billboard args
            // NOTE - billboards dont have LOD
            billboard0RenderArgs.AlphaTestValue = (int)geometry.Billboard0.AlphaTestValue;
            billboard0RenderArgs.Active         = geometry.Billboard0.IsActive;
            billboard0RenderArgs.LOD            = 0;
            billboard1RenderArgs.AlphaTestValue = (int)geometry.Billboard1.AlphaTestValue;
            billboard1RenderArgs.Active         = geometry.Billboard1.IsActive;
            billboard1RenderArgs.LOD            = 0;

            if (billboard0RenderArgs.Active)
            {
                FillBillboardBuffer(billboard0, geometry.Billboard0);
            }
            if (billboard1RenderArgs.Active)
            {
                FillBillboardBuffer(billboard1, geometry.Billboard1);
            }

            group.AddVisible(this, branchRenderArgs.Active, frondRenderArgs.Active, leaf0RenderArgs.Active || leaf1RenderArgs.Active, billboard0RenderArgs.Active || billboard1RenderArgs.Active);
        }
コード例 #4
0
        /// <summary>
        /// Sets position of node according to the circle property and adjust edge properly
        /// </summary>
        /// <param name="circle"></param>
        /// <param name="node"></param>
        /// <param name="edge"></param>
        /// <param name="centralLength"></param>
        internal void SubscribeNodePosition(Circle circle, GameObject node, GameObject edge, float centralLength)
        {
            circle.Position.Subscribe(v =>
            {
                var y = node.transform.localPosition.y;
                node.transform.localPosition = new Vector3(v.x, y, v.y);

                var phi   = TreeGeometry.CalcAlpha(v.x, v.y);
                var theta = TreeGeometry.CalcTheta(centralLength, v.magnitude);
                edge.transform.localEulerAngles = new Vector3(theta, phi, 0);

                var diameter = edge.transform.localScale.x;
                var l        = TreeGeometry.CalcEdgeLength(centralLength, theta);
                edge.transform.localScale = new Vector3(diameter,
                                                        TreeGeometry.SizeToScale(l, DefaultEdgeHeight, DefaultEdgeScale.y),
                                                        diameter);
            });
        }
コード例 #5
0
        public void FillBillboardBuffer(float [] buffer, TreeGeometry.Billboard billboard)
        {
            unsafe
            {
                float * srcCoord = billboard.Coords;
                float * srcTexCoord = billboard.TexCoords;

                // Position
                buffer[0] = srcCoord[0] + location.x;
                buffer[1] = srcCoord[1] + location.y;
                buffer[2] = srcCoord[2] + location.z;

                // Texture
                buffer[3] = srcTexCoord[0];
                buffer[4] = srcTexCoord[1];

                // Position
                buffer[5] = srcCoord[3] + location.x;
                buffer[6] = srcCoord[4] + location.y;
                buffer[7] = srcCoord[5] + location.z;

                // Texture
                buffer[8] = srcTexCoord[2];
                buffer[9] = srcTexCoord[3];

                // Position
                buffer[10] = srcCoord[6] + location.x;
                buffer[11] = srcCoord[7] + location.y;
                buffer[12] = srcCoord[8] + location.z;

                // Texture
                buffer[13] = srcTexCoord[4];
                buffer[14] = srcTexCoord[5];

                // Position
                buffer[15] = srcCoord[9] + location.x;
                buffer[16] = srcCoord[10] + location.y;
                buffer[17] = srcCoord[11] + location.z;

                // Texture
                buffer[18] = srcTexCoord[6];
                buffer[19] = srcTexCoord[7];

            }

            return;
        }
コード例 #6
0
        public void BuildLeafBuffer(TreeGeometry.Leaf leaf, float leafAdjust, out VertexData vertexData, out IndexData indexData)
        {
            if (leaf.LeafCount == 0)
            {
                vertexData = null;
                indexData = null;
            }
            else
            {
                //
                // Build the vertex buffer
                //
                vertexData = new VertexData();

                vertexData.vertexCount = leaf.LeafCount * 4;
                vertexData.vertexStart = 0;

                // destroy the original vertex declaration, and use the common one for leaves
                HardwareBufferManager.Instance.DestroyVertexDeclaration(vertexData.vertexDeclaration);
                vertexData.vertexDeclaration = leafVertexDeclaration;

                // create the hardware vertex buffer and set up the buffer binding
                HardwareVertexBuffer hvBuffer = HardwareBufferManager.Instance.CreateVertexBuffer(
                    vertexData.vertexDeclaration.GetVertexSize(0), vertexData.vertexCount,
                    BufferUsage.StaticWriteOnly, false);

                vertexData.vertexBufferBinding.SetBinding(0, hvBuffer);

                // lock the vertex buffer
                IntPtr ipBuf = hvBuffer.Lock(BufferLocking.Discard);

                int bufferOff = 0;

                int maxindex = 0;

                unsafe
                {
                    float* buffer = (float*)ipBuf.ToPointer();
                    float* srcCenter = leaf.CenterCoords;
                    float* srcNormal = leaf.Normals;
                    byte* srcWindIndices = leaf.WindMatrixIndices;
                    float* srcWindWeights = leaf.WindWeights;
                    byte* srcLeafClusterIndices = leaf.LeafClusterIndices;

                    for (int l = 0; l < leaf.LeafCount; l++)
                    {
                        float* srcOffset = leaf.LeafMapCoords[l];
                        float* srcTexCoord = leaf.LeafMapTexCoords[l];
                        int leafClusterIndex = *srcLeafClusterIndices;

                        //if (leafClusterIndex % 4 == 0)
                        //{
                        //    leafClusterIndex++;
                        //}

                        if (leafClusterIndex > maxindex)
                        {
                            maxindex = leafClusterIndex;
                        }

                        // Position
                        buffer[bufferOff++] = srcCenter[0];
                        buffer[bufferOff++] = srcCenter[1];
                        buffer[bufferOff++] = srcCenter[2];

                        // normals
                        buffer[bufferOff++] = srcNormal[0];
                        buffer[bufferOff++] = srcNormal[1];
                        buffer[bufferOff++] = srcNormal[2];

                        // Texture
                        buffer[bufferOff++] = srcTexCoord[0];
                        buffer[bufferOff++] = srcTexCoord[1];

                        // Wind Attributes
                        buffer[bufferOff++] = *srcWindIndices;
                        buffer[bufferOff++] = *srcWindWeights;

                        // Leaf Placement Data
                        buffer[bufferOff++] = leafClusterIndex * 4;
                        buffer[bufferOff++] = leafAdjust;
                        buffer[bufferOff++] = (float)(leafClusterIndex % speedWind.NumWindMatrices);

                        // Position
                        buffer[bufferOff++] = srcCenter[0];
                        buffer[bufferOff++] = srcCenter[1];
                        buffer[bufferOff++] = srcCenter[2];

                        // normals
                        buffer[bufferOff++] = srcNormal[0];
                        buffer[bufferOff++] = srcNormal[1];
                        buffer[bufferOff++] = srcNormal[2];

                        // Texture
                        buffer[bufferOff++] = srcTexCoord[2];
                        buffer[bufferOff++] = srcTexCoord[3];

                        // Wind Attributes
                        buffer[bufferOff++] = *srcWindIndices;
                        buffer[bufferOff++] = *srcWindWeights;

                        // Leaf Placement Data
                        buffer[bufferOff++] = leafClusterIndex * 4 + 1;
                        buffer[bufferOff++] = leafAdjust;
                        buffer[bufferOff++] = (float)(leafClusterIndex % speedWind.NumWindMatrices);

                        // Position
                        buffer[bufferOff++] = srcCenter[0];
                        buffer[bufferOff++] = srcCenter[1];
                        buffer[bufferOff++] = srcCenter[2];

                        // normals
                        buffer[bufferOff++] = srcNormal[0];
                        buffer[bufferOff++] = srcNormal[1];
                        buffer[bufferOff++] = srcNormal[2];

                        // Texture
                        buffer[bufferOff++] = srcTexCoord[4];
                        buffer[bufferOff++] = srcTexCoord[5];

                        // Wind Attributes
                        buffer[bufferOff++] = *srcWindIndices;
                        buffer[bufferOff++] = *srcWindWeights;

                        // Leaf Placement Data
                        buffer[bufferOff++] = leafClusterIndex * 4 + 2;
                        buffer[bufferOff++] = leafAdjust;
                        buffer[bufferOff++] = (float)(leafClusterIndex % speedWind.NumWindMatrices);

                        // Position
                        buffer[bufferOff++] = srcCenter[0];
                        buffer[bufferOff++] = srcCenter[1];
                        buffer[bufferOff++] = srcCenter[2];

                        // normals
                        buffer[bufferOff++] = srcNormal[0];
                        buffer[bufferOff++] = srcNormal[1];
                        buffer[bufferOff++] = srcNormal[2];

                        // Texture
                        buffer[bufferOff++] = srcTexCoord[6];
                        buffer[bufferOff++] = srcTexCoord[7];

                        // Wind Attributes
                        buffer[bufferOff++] = *srcWindIndices++;
                        buffer[bufferOff++] = *srcWindWeights++;

                        // Leaf Placement Data
                        buffer[bufferOff++] = leafClusterIndex * 4 + 3;
                        buffer[bufferOff++] = leafAdjust;
                        buffer[bufferOff++] = (float)(leafClusterIndex % speedWind.NumWindMatrices);

                        srcCenter += 3;
                        srcNormal += 3;
                        srcLeafClusterIndices++;
                    }
                }
                hvBuffer.Unlock();

                // Turned off this output because it kills performance when editing terrain
            //                Console.WriteLine("max leaf cluster index: {0}", maxindex);

                //
                // build the index buffer
                //
                indexData = new IndexData();
                int numIndices;

                numIndices = leaf.LeafCount * 6;

                indexData.indexBuffer = HardwareBufferManager.Instance.CreateIndexBuffer(
                    IndexType.Size16, numIndices, BufferUsage.StaticWriteOnly);

                IntPtr indexBufferPtr = indexData.indexBuffer.Lock(0, indexData.indexBuffer.Size, BufferLocking.Discard);

                unsafe
                {
                    ushort* indexBuffer = (ushort*)indexBufferPtr.ToPointer();
                    int bufOff = 0;
                    int vert = 0;
                    for (int i = 0; i < leaf.LeafCount; i++)
                    {
                        indexBuffer[bufOff++] = (ushort)vert;
                        indexBuffer[bufOff++] = (ushort)(vert + 1);
                        indexBuffer[bufOff++] = (ushort)(vert + 2);

                        indexBuffer[bufOff++] = (ushort)vert;
                        indexBuffer[bufOff++] = (ushort)(vert + 2);
                        indexBuffer[bufOff++] = (ushort)(vert + 3);

                        vert += 4;
                    }
                }

                indexData.indexBuffer.Unlock();
                indexData.indexCount = numIndices;
                indexData.indexStart = 0;
            }

            return;
        }
コード例 #7
0
        public void BuildIndexedBuffer(TreeGeometry.Indexed indexed, bool normalMapped, out VertexData vertexData, out IndexData indexData)
        {
            //
            // Build the vertex buffer
            //

            if (indexed.VertexCount == 0)
            {
                vertexData = null;
                indexData = null;
            }
            else
            {

                vertexData = new VertexData();

                vertexData.vertexCount = indexed.VertexCount;
                vertexData.vertexStart = 0;

                // free the vertex declaration to avoid a leak.
                HardwareBufferManager.Instance.DestroyVertexDeclaration(vertexData.vertexDeclaration);

                // pick from the common vertex declarations based on whether we are normal mapped or not
                if (normalMapped)
                {
                    vertexData.vertexDeclaration = indexedNormalMapVertexDeclaration;
                }
                else
                {
                    vertexData.vertexDeclaration = indexedVertexDeclaration;
                }

                // create the hardware vertex buffer and set up the buffer binding
                HardwareVertexBuffer hvBuffer = HardwareBufferManager.Instance.CreateVertexBuffer(
                    vertexData.vertexDeclaration.GetVertexSize(0), vertexData.vertexCount,
                    BufferUsage.StaticWriteOnly, false);

                vertexData.vertexBufferBinding.SetBinding(0, hvBuffer);

                // lock the vertex buffer
                IntPtr ipBuf = hvBuffer.Lock(BufferLocking.Discard);

                int bufferOff = 0;

                unsafe
                {
                    float* buffer = (float*)ipBuf.ToPointer();
                    float* srcPosition = indexed.Coords;
                    float* srcNormal = indexed.Normals;
                    float* srcTexCoord = indexed.TexCoords0;
                    float* srcShadowTexCoord = indexed.TexCoords1;
                    byte* srcWindIndices = indexed.WindMatrixIndices;
                    float* srcWindWeights = indexed.WindWeights;
                    float* srcBinormal = indexed.Binormals;
                    float* srcTangent = indexed.Tangents;

                    for (int v = 0; v < vertexData.vertexCount; v++)
                    {

                        // Position
                        buffer[bufferOff++] = srcPosition[0];
                        buffer[bufferOff++] = srcPosition[1];
                        buffer[bufferOff++] = srcPosition[2];
                        srcPosition += 3;

                        // normals
                        buffer[bufferOff++] = *srcNormal++;
                        buffer[bufferOff++] = *srcNormal++;
                        buffer[bufferOff++] = *srcNormal++;

                        // Texture
                        buffer[bufferOff++] = *srcTexCoord++;
                        buffer[bufferOff++] = *srcTexCoord++;

                        // Self Shadow Texture
                        buffer[bufferOff++] = *srcShadowTexCoord++;
                        buffer[bufferOff++] = *srcShadowTexCoord++;

                        // wind params
                        buffer[bufferOff++] = *srcWindIndices++;
                        buffer[bufferOff++] = *srcWindWeights++;

                        if (normalMapped)
                        {
                            // Binormal
                            buffer[bufferOff++] = *srcBinormal++;
                            buffer[bufferOff++] = *srcBinormal++;
                            buffer[bufferOff++] = *srcBinormal++;

                            // Tangent
                            buffer[bufferOff++] = *srcTangent++;
                            buffer[bufferOff++] = *srcTangent++;
                            buffer[bufferOff++] = *srcTangent++;
                        }
                    }
                }
                hvBuffer.Unlock();

                //
                // build the index buffer
                //
                if (indexed.NumStrips == 0)
                {
                    indexData = null;
                }
                else
                {
                    indexData = new IndexData();
                    int numIndices;

                    unsafe
                    {
                        numIndices = indexed.StripLengths[0];
                    }

                    indexData.indexBuffer = HardwareBufferManager.Instance.CreateIndexBuffer(
                        IndexType.Size16, numIndices, BufferUsage.StaticWriteOnly);

                    IntPtr indexBufferPtr = indexData.indexBuffer.Lock(0, indexData.indexBuffer.Size, BufferLocking.Discard);

                    unsafe
                    {
                        ushort* strip = null;
                        if (numIndices != 0)
                        {
                            strip = indexed.Strips[0];
                        }

                        ushort* indexBuffer = (ushort*)indexBufferPtr.ToPointer();
                        for (int i = 0; i < numIndices; i++)
                        {
                            indexBuffer[i] = strip[i];
                        }
                    }

                    indexData.indexBuffer.Unlock();

                    indexData.indexCount = numIndices;
                    indexData.indexStart = 0;
                }

                return;
            }
        }
コード例 #8
0
        public TreeGroup(String filename, float size, float sizeVariance, SpeedWindWrapper speedWind, Forest forest, List<Vector3> locations)
        {
            if (!initialized)
            {
                Initialize();
            }

            name = String.Format("Forest: {0} File: {1} Instances: {2}", forest.Name, filename, locations.Count);
            this.forest = forest;

            this.speedWind = speedWind;

            speedTree = new SpeedTreeWrapper();

            speedTree.TextureFlip = true;
            LoadTree(filename);
            speedTree.BranchWindMethod = WindMethod.WindGPU;
            speedTree.FrondWindMethod = WindMethod.WindGPU;
            speedTree.LeafWindMethod = WindMethod.WindGPU;

            float originalSize = 0f;
            float variance = 0f;
            speedTree.GetTreeSize(ref originalSize, ref variance);
            speedTree.OriginalSize = originalSize;
            speedTree.SetTreeSize(size, sizeVariance);

            treeTextures = speedTree.Textures;

            // make sure the tree doesn't have too many leaf texture groups
            Debug.Assert(treeTextures.LeafTextureFilenames.Length <= 3);

            // for trees with 3 leaf textures, reduce the number of rocking groups to 2
            // (from the default of 3), so that we don't overflow the number of available shader
            // param registers
            if (treeTextures.LeafTextureFilenames.Length == 3)
            {
                speedTree.NumLeafRockingGroups = 2;
            }

            speedTree.Compute(SpeedTreeUtil.ToSpeedTree(Matrix4.Identity), 1, true);

            speedTree.TreePosition = SpeedTreeUtil.ToSpeedTree(Vector3.Zero);

            // set lod limits
            speedTree.SetLodLimits(nearLOD, farLOD);

            // create the geometry object
            geometry = new TreeGeometry();

            //
            // Setup branches
            //

            // create the render operation
            branchRenderOp = new RenderOperation();
            branchRenderOp.operationType = OperationType.TriangleStrip;
            branchRenderOp.useIndices = true;

            // set up the material.
            branchMaterial = SetupTreeMaterial("SpeedTree/Branch", treeTextures.BranchTextureFilename, treeTextures.SelfShadowFilename, GenerateNormalMapTextureName(treeTextures.BranchTextureFilename), speedTree.BranchMaterial, !normalMapped, branchTechnique);

            // get number of branch LODs
            uint nBranchLODs = speedTree.NumBranchLodLevels;

            // allocate branch buffers
            branchVertexBuffers = new VertexData[nBranchLODs];
            branchIndexBuffers = new IndexData[nBranchLODs];

            for (short i = 0; i < nBranchLODs; i++)
            {
                // set up the vertex and index buffers for the branch geometry
                speedTree.GetGeometry(geometry, SpeedTreeWrapper.GeometryFlags.BranchGeometry, i, -1, -1);
                BuildIndexedBuffer(geometry.Branches, true, out branchVertexBuffers[i], out branchIndexBuffers[i]);
            }
            //
            // Setup fronds
            //

            // create the render operation
            frondRenderOp = new RenderOperation();
            frondRenderOp.operationType = OperationType.TriangleStrip;
            frondRenderOp.useIndices = true;

            // set up the material
            frondMaterial = SetupTreeMaterial("SpeedTree/Frond", treeTextures.CompositeFilename, treeTextures.SelfShadowFilename, null, speedTree.FrondMaterial, true, 0);

            uint nFrondLODs = speedTree.NumFrondLodLevels;

            // allocate frond buffer arrays
            frondVertexBuffers = new VertexData[nFrondLODs];
            frondIndexBuffers = new IndexData[nFrondLODs];

            for ( short i = 0; i < nFrondLODs; i++ )
            {
                // build the frond geometry for each LOD
                speedTree.GetGeometry(geometry, SpeedTreeWrapper.GeometryFlags.FrondGeometry, -1, i, -1);
                BuildIndexedBuffer(geometry.Fronds, false, out frondVertexBuffers[i], out frondIndexBuffers[i]);
            }

            //
            // Setup Leaves
            //

            TreeCamera saveCam = SpeedTreeWrapper.Camera;

            TreeCamera treeCamera = new TreeCamera();
            treeCamera.position = SpeedTreeUtil.ToSpeedTree(Vector3.Zero);
            treeCamera.direction = SpeedTreeUtil.ToSpeedTree(new Vector3(1,0,0));
            SpeedTreeWrapper.Camera = treeCamera;

            // set up render ops
            leaf0RenderOp = new RenderOperation();
            leaf0RenderOp.operationType = OperationType.TriangleList;
            leaf0RenderOp.useIndices = true;

            leaf1RenderOp = new RenderOperation();
            leaf1RenderOp.operationType = OperationType.TriangleList;
            leaf1RenderOp.useIndices = true;

            // set up the material
            leafMaterial = SetupTreeMaterial("SpeedTree/Leaf", treeTextures.CompositeFilename, null, null, speedTree.LeafMaterial, true, 0);

            uint nLeafLODs = speedTree.NumLeafLodLevels;

            // allocate leaf buffer arrays
            leafVertexBuffers = new VertexData[nLeafLODs];
            leafIndexBuffers = new IndexData[nLeafLODs];

            float [] lodLeafAdjust = speedTree.LeafLodSizeAdjustments;

            for ( short i = 0; i < nLeafLODs; i++ )
            {
                // build the leaf geometry for each LOD
                speedTree.GetGeometry(geometry, SpeedTreeWrapper.GeometryFlags.LeafGeometry, -1, -1, i);
                BuildLeafBuffer(geometry.Leaves0, lodLeafAdjust[i], out leafVertexBuffers[i], out leafIndexBuffers[i]);
            }

            // restore the camera afte getting leaf buffers
            SpeedTreeWrapper.Camera = saveCam;

            bounds = new AxisAlignedBox();

            // build all the trees and accumulate bounds
            foreach (Vector3 loc in locations)
            {
                SpeedTreeWrapper treeInstance = speedTree.MakeInstance();
                treeInstance.OriginalSize = originalSize;
                Tree t = new Tree(this, treeInstance, loc);

                bounds.Merge(t.Bounds);

                trees.Add(t);
            }

            boundingRadius = (bounds.Maximum - bounds.Minimum).Length / 2;

            // create the buckets
            branchBuckets = new Dictionary<int, List<Tree>>[nBranchLODs];
            frondBuckets = new Dictionary<int, List<Tree>>[nFrondLODs];
            leafBuckets = new Dictionary<int, List<Tree>>[nLeafLODs];
            billboardBucket = new Dictionary<int, List<Tree>>[1];

            // initialize the bucket dictionaries
            for (int i = 0; i < nBranchLODs; i++)
            {
                branchBuckets[i] = new Dictionary<int, List<Tree>>();
            }
            for (int i = 0; i < nFrondLODs; i++)
            {
                frondBuckets[i] = new Dictionary<int, List<Tree>>();
            }
            for (int i = 0; i < nLeafLODs; i++)
            {
                leafBuckets[i] = new Dictionary<int, List<Tree>>();
            }
            billboardBucket[0] = new Dictionary<int, List<Tree>>();
            allGroups.Add(this);
        }