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);
        }
        public bool TerrainLoaded(TextAsset asset, UnityEngine.Terrain setting, float detailDistance)
        {
            var data = setting.terrainData;
            // ignore version
            int dataIndex = 2;

            int        heightMapResolution = 0;
            int        detailResolution    = 0;
            int        detailCount         = 0;
            List <int> maxCountInLayer     = new List <int>();

            unsafe
            {
                var     head     = new IntPtr(asset.GetBytesIntPtr().ToInt64() + dataIndex);
                ushort *flowData = (ushort *)head.ToPointer();

                heightMapResolution = *flowData++;
                detailResolution    = *flowData++;
                _detailNodeSize     = *flowData++;
                detailCount         = *flowData++;
                for (int i = 0; i < detailCount; ++i)
                {
                    maxCountInLayer.Add(*flowData++);
                }

                dataIndex += 2 * (4 + detailCount);
            }

            var division   = detailResolution / _detailNodeSize;
            var prototypes = data.detailPrototypes;

            if (heightMapResolution != data.heightmapResolution || detailResolution != data.detailResolution ||
                detailCount != prototypes.Length)
            {
                return(false);
            }

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

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

            for (int i = 0; i < detailCount; ++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(asset, dataIndex, data.size.y, heightMapResolution);

            cluster.TerrainName = setting.name;

            dataIndex += heightMapResolution * heightMapResolution * 2;

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

            _terrainProperty = new TerrainProperty(setting, data, detailDistance);
            var layerLength = (detailResolution * detailResolution + division * division) * 2;

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

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

                    node.SetInstantiationShader(_instantiationShader);
                    node.SetShaderProperty(_terrainProperty, detailProps);
                    node.InitCountInUnit(x, z, _detailNodeSize, asset, maxCountInLayer, dataIndex, layerLength, division);

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

            cluster.SetMaxCountInLayer(maxCountInLayer);

            _data.AddCluster(cluster);
            return(true);
        }
        public bool TerrainLoaded(TextAsset asset, UnityEngine.Terrain setting, float detailDistance, Vector3 basePos)
        {
            var data = setting.terrainData;
            // ignore version
            int dataIndex = 2;

            int        heightMapResolution = 0;
            int        detailResolution    = 0;
            int        detailCount         = 0;
            List <int> maxCountInLayer     = new List <int>();

            unsafe
            {
                var     head     = new IntPtr(asset.GetBytesIntPtr().ToInt64() + dataIndex);
                ushort *flowData = (ushort *)head.ToPointer();

                heightMapResolution = *flowData++;
                detailResolution    = *flowData++;
                _nodeSize           = *flowData++;
                detailCount         = *flowData++;
                for (int i = 0; i < detailCount; ++i)
                {
                    maxCountInLayer.Add(*flowData++);
                }

                dataIndex += 2 * (4 + detailCount);
            }

            var division   = detailResolution / _nodeSize;
            var prototypes = data.detailPrototypes;

            if (heightMapResolution != data.heightmapResolution || detailResolution != data.detailResolution ||
                detailCount != prototypes.Length)
            {
                _logger.WarnFormat("unmatch param in detail: heightMapResolution -> {0}:{1}, detailResolution -> {2}:{3}, detailCount -> {4}:{5}",
                                   data.heightmapResolution, heightMapResolution, data.detailResolution, detailResolution, prototypes.Length, detailCount);
                return(false);
            }

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

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

            if (detailCount != _instancingDraw.Length)
            {
                _logger.ErrorFormat("unmatch grass count: unique->{0} other->{1}", detailCount, _instancingDraw);
                return(false);
            }

            List <DividedDetailProperty> detailProps = new List <DividedDetailProperty>();
            float nodeMargin = float.MinValue;

            for (int i = 0; i < detailCount; ++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);
                }
                else
                {
                    nodeMargin = Mathf.Max(nodeMargin, _instancingDraw[i].RendererSphereRadius);
                }
            }

            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(asset, dataIndex, data.size.y, heightMapResolution, nodeMargin);

            cluster.TerrainName = setting.name;

            dataIndex += heightMapResolution * heightMapResolution * 2;

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

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

            var layerLength = (detailResolution * detailResolution + division * division) * 2;

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

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

                    node.SetInstantiationShader(_instantiationShader);
                    node.SetShaderProperty(_terrainProperty, detailProps);
                    node.InitCountInUnit(x, z, _nodeSize, asset, maxCountInLayer, dataIndex, layerLength, division);

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

            cluster.SetMaxCountInLayer(maxCountInLayer);

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