public bool TerrainLoaded(UnityEngine.Terrain setting, float detailDistance)
        {
            var data = setting.terrainData;

            var detailResolution    = data.detailResolution;
            var heightMapResolution = data.heightmapResolution;
            var prototypes          = data.detailPrototypes;
            var detailCount         = prototypes.Length;

            var division = Mathf.CeilToInt(detailResolution / (float)_detailNodeSize);

            if (!Helper.AlmostEqual(detailResolution, division * _detailNodeSize))
            {
                return(false);
            }

            var detailData = new List <int[, ]>();

            if (_instancingDraw == null)
            {
                _instancingDraw = new DetailInstancingDraw[detailCount];
            }

            List <DividedDetailProperty> detailProps = new List <DividedDetailProperty>();

            for (int i = 0; i < detailCount; ++i)
            {
                detailData.Add(data.GetDetailLayer(0, 0, detailResolution, detailResolution, i));
                detailProps.Add(new DividedDetailProperty(data, prototypes[i], _detailNodeSize, i));

                if (_instancingDraw[i] == null)
                {
                    var renderer = new InstancingRenderer(prototypes[i]);
                    _instancingDraw[i] = new DetailInstancingDraw(renderer, VisibilityShader, setting, data);
                }
            }

            var nodeSize = new Vector2(data.size.x / division, data.size.z / division);

            var cluster = new GpuInstancingNodeCluster <GpuInstancingDetailNode>();

            cluster.InitDivision(setting.transform.position, nodeSize, division, division, detailDistance);
            cluster.InitHeightMap(data.GetHeights(0, 0, heightMapResolution, heightMapResolution), data.size.y);

            cluster.TerrainName = setting.name;

            _data.SetGridParam(data.size.x, detailDistance, nodeSize);

            _terrainProperty = new TerrainProperty(setting, data, detailDistance);

            for (int x = 0; x < division; ++x)
            {
                for (int z = 0; z < division; ++z)
                {
                    var node = new GpuInstancingDetailNode();

                    node.TerrainName = setting.name;
                    node.X           = x;
                    node.Z           = z;

                    node.SetInstantiationShader(_instantiationShader);
                    node.SetShaderProperty(_terrainProperty, detailProps);
                    node.InitCountInUnit(detailData, x, z, _detailNodeSize);

                    cluster.AddNode(x, z, node);
                    cluster.UpdateMaxCountInLayer(node);
                }
            }

            _data.AddCluster(cluster);
            return(true);
        }
        public bool TerrainLoaded(UnityEngine.Terrain setting, float detailDistance, Vector3 basePos, float[,] heightMap)
        {
            var data = setting.terrainData;

            var detailResolution = data.detailResolution;
            var prototypes       = data.detailPrototypes;
            var detailCount      = prototypes.Length;

            var division = Mathf.CeilToInt(detailResolution / (float)_nodeSize);

            if (!Helper.AlmostEqual(detailResolution, division * _nodeSize))
            {
                _logger.WarnFormat("unmatch param in detail: detailResolution -> {0}, unitSize -> {1}, division -> {2}",
                                   detailResolution, _nodeSize, division);
                return(false);
            }

            if (detailCount == 0)
            {
                _logger.Warn("no grass prototypes exists");
                return(false);
            }

            var detailData = new List <int[, ]>();

            if (_instancingDraw == null)
            {
                _instancingDraw = new DetailInstancingDraw[detailCount];
            }

            List <DividedDetailProperty> detailProps = new List <DividedDetailProperty>();

            float nodeMargin = float.MinValue;

            for (int i = 0; i < detailCount; ++i)
            {
                detailData.Add(data.GetDetailLayer(0, 0, detailResolution, detailResolution, i));
                detailProps.Add(new DividedDetailProperty(prototypes[i], _nodeSize, i));

                if (_instancingDraw[i] == null)
                {
                    var renderer = new InstancingRenderer(prototypes[i]);
                    nodeMargin         = Mathf.Max(nodeMargin, renderer.SphereRadius);
                    _instancingDraw[i] = new DetailInstancingDraw(renderer, VisibilityShader, SortShader, setting, data, basePos);
                }
            }

            var nodeSize = new Vector2(data.size.x / division, data.size.z / division);

            var cluster = new GpuInstancingNodeCluster <GpuInstancingDetailNode>();

            cluster.InitDivision(basePos, nodeSize, division, division, detailDistance);
            cluster.InitHeightMap(heightMap, data.size.y, nodeMargin);

            cluster.TerrainName = setting.name;

            _data.SetGridParam(data.size.x, detailDistance, nodeSize);

            _terrainProperty = new TerrainProperty();
            _terrainProperty.InitForDetail(setting, data, detailDistance, basePos);

            for (int x = 0; x < division; ++x)
            {
                for (int z = 0; z < division; ++z)
                {
                    var node = new GpuInstancingDetailNode();

                    node.TerrainName = setting.name;
                    node.X           = x;
                    node.Z           = z;

                    node.SetInstantiationShader(_instantiationShader);
                    node.SetShaderProperty(_terrainProperty, detailProps);
                    node.InitCountInUnit(detailData, x, z, _nodeSize);

                    cluster.AddNode(x, z, node);
                    cluster.UpdateMaxCountInLayer(node);
                }
            }

            _data.AddCluster(cluster);
            return(true);
        }