Esempio n. 1
0
        public override void ProcessHeights(TerrainWrapper wrapper, LayerBase baseLayer, int stencilKey)
        {
            var layer = baseLayer as MMTerrainLayer;

            if (layer == null)
            {
                Debug.LogWarning(string.Format("Attempted to write {0} to incorrect layer type! Expected Layer {1} to be {2}, but it was {3}", name, baseLayer.name, GetLayerType(), baseLayer.GetType()), this);
                return;
            }
            if (!Network || Configuration == null)
            {
                return;
            }
            var config = Configuration.GetConfig <Config>();

            if (config == null)
            {
                Debug.LogError("Invalid configuration! Expected ConnectionTerrainHeightConfiguration");
                return;
            }
            var terrain     = wrapper.Terrain;
            var terrainPos  = wrapper.Terrain.GetPosition();
            var terrainSize = wrapper.Terrain.terrainData.size;
            var heightRes   = terrain.terrainData.heightmapResolution;

            var mainSpline   = NodeConnection.GetSpline();
            var radius       = config.Radius;
            var falloffCurve = config.Falloff;
            var heightCurve  = config.Height;

            // Create bounds to encapsulate spline (axis aligned)
            var bounds = mainSpline.GetApproximateBounds();

            bounds.Expand(radius * 2 * Vector3.one);

            // Create object bounds
            var objectBounds = mainSpline.GetApproximateXZObjectBounds();

            objectBounds.Expand(radius * 2 * Vector3.one);
            objectBounds.Expand(Vector3.up * 10000);

            // Early cull
            var axisBounds    = objectBounds.ToAxisBounds();
            var terrainBounds = terrain.GetComponent <Collider>().bounds;

            terrainBounds.Expand(Vector3.up * 10000);
            if (!terrainBounds.Intersects(axisBounds))
            {
                return;
            }

            // Get matrix space min/max
            var matrixMin = terrain.WorldToHeightmapCoord(bounds.min, TerrainX.RoundType.Floor) - Coord.One;

            matrixMin = matrixMin.Clamp(0, heightRes);
            var matrixMax = terrain.WorldToHeightmapCoord(bounds.max, TerrainX.RoundType.Ceil) + Coord.One;

            matrixMax = matrixMax.Clamp(0, heightRes);

            var xDelta = matrixMax.x - matrixMin.x;
            var zDelta = matrixMax.z - matrixMin.z;

            var floatArraySize = new Common.Coord(
                Mathf.Min(xDelta, terrain.terrainData.heightmapResolution - matrixMin.x),
                Mathf.Min(zDelta, terrain.terrainData.heightmapResolution - matrixMin.z));

            float planeGive = (wrapper.Terrain.terrainData.size.x / wrapper.Terrain.terrainData.heightmapResolution) * 0;
            Plane startPlane, endPlane;

            GenerateSplinePlanes(planeGive, mainSpline, out startPlane, out endPlane);

            var layerHeights = layer.GetHeights(matrixMin.x, matrixMin.z, floatArraySize.x, floatArraySize.z, heightRes) ??
                               new Serializable2DFloatArray(floatArraySize.x, floatArraySize.z);

            stencilKey = GetStencilKey();
            if (layer.Stencil == null || layer.Stencil.Width != heightRes || layer.Stencil.Height != heightRes)
            {
                layer.Stencil = new Stencil(heightRes, heightRes);
            }

            for (var dz = 0; dz < floatArraySize.z; ++dz)
            {
                for (var dx = 0; dx < floatArraySize.x; ++dx)
                {
                    var coordX = matrixMin.x + dx;
                    var coordZ = matrixMin.z + dz;

                    var worldPos = terrain.HeightmapCoordToWorldPos(new Common.Coord(coordX, coordZ));
                    worldPos = new Vector3(worldPos.x, objectBounds.center.y, worldPos.z);
                    if (!terrain.ContainsPointXZ(worldPos) ||
                        !objectBounds.Contains(worldPos) ||
                        !GeometryExtensions.BetweenPlanes(worldPos, startPlane, endPlane))
                    {
                        // Cull if we're outside of the approx bounds
                        continue;
                    }

                    var uniformT                   = mainSpline.GetClosestUniformTimeOnSplineXZ(worldPos.xz()); // Expensive!
                    var closestOnSpline            = mainSpline.GetUniformPointOnSpline(uniformT);
                    var normalizedFlatDistToSpline = (worldPos - closestOnSpline).xz().magnitude / (radius);
                    if (normalizedFlatDistToSpline >= 1)
                    {
                        continue;
                    }

                    var maskValue   = Mathf.Clamp01(falloffCurve.Evaluate(normalizedFlatDistToSpline));
                    var heightDelta = heightCurve.Evaluate(normalizedFlatDistToSpline);

                    float existingStencilStrength;
                    int   existingStencilKey;
                    MiscUtilities.DecompressStencil(layer.Stencil[coordX, coordZ], out existingStencilKey, out existingStencilStrength);

                    if (existingStencilKey != stencilKey &&
                        existingStencilKey > stencilKey &&
                        !(existingStencilStrength < maskValue && maskValue > 0))
                    {
                        continue;
                    }

                    // Refine our worldposition to be on the same XZ plane as the spline point
                    worldPos = new Vector3(worldPos.x, closestOnSpline.y, worldPos.z);

                    // Find the point on the spline closest to this given point
                    var naturalT = mainSpline.UniformToNaturalTime(uniformT);

                    // Get the upvec from the natural time
                    var up = mainSpline.GetUpVector(naturalT).normalized;

                    // Create a plane and cast against it
                    var plane = new Plane(up, closestOnSpline);

                    float dist    = 0;
                    var   castRay = new Ray(worldPos, Vector3.down);
                    plane.Raycast(castRay, out dist);
                    var castPoint     = castRay.GetPoint(dist);
                    var heightAtPoint = (castPoint.y + heightDelta);

                    heightAtPoint -= terrainPos.y;
                    heightAtPoint /= terrainSize.y;
                    heightAtPoint  = MiscUtilities.FloorToUshort(heightAtPoint);

                    var existingHeight = layerHeights[dx, dz];
                    var newHeight      = Mathf.Lerp(existingHeight, heightAtPoint, Mathf.Clamp01(maskValue));

                    layerHeights[dx, dz] = newHeight;

                    var key = maskValue > existingStencilStrength ? stencilKey : existingStencilKey;
                    var newRawStencilValue = MiscUtilities.CompressStencil(key, /*stencilKey == existingStencilKey ?*/ Mathf.Max(maskValue, existingStencilStrength) /* : maskValue + existingStencilStrength*/);
                    //newRawStencilValue = MiscUtilities.CompressStencil(key, 1);

                    layer.Stencil[coordX, coordZ] = newRawStencilValue;
                }
            }

            layer.SetHeights(matrixMin.x, matrixMin.z,
                             layerHeights, wrapper.Terrain.terrainData.heightmapResolution);
        }
        public override void ProcessVegetationStudio(TerrainWrapper wrapper, LayerBase baseLayer, int stencilKey)
        {
            var layer = baseLayer as MMTerrainLayer;

            if (layer == null)
            {
                Debug.LogWarning(string.Format("Attempted to write {0} to incorrect layer type! Expected Layer {1} to be {2}, but it was {3}", name, baseLayer.name, GetLayerType(), baseLayer.GetType()), this);
                return;
            }
            if (!Network)
            {
                Debug.LogError("Unable to find network! " + name, this);
                return;
            }
            if (Configuration == null)
            {
                return;
            }
            var config = Configuration.GetConfig <Config>();

            if (config == null)
            {
                Debug.LogError("Invalid configuration! Expected ConnectionTerrainHeightConfiguration");
                return;
            }

            var mainSpline = NodeConnection.GetSpline();
            var radius     = config.InstanceRemoveDistance;

            // Create bounds to encapsulate spline (axis aligned)
            var bounds = mainSpline.GetApproximateBounds();

            bounds.Expand(radius * 2 * Vector3.one);

            // Create object bounds
            var objectBounds = mainSpline.GetApproximateXZObjectBounds();

            objectBounds.Expand(radius * 2 * Vector3.one);
            objectBounds.Expand(Vector3.up * 10000);

            var flatCullbounds = objectBounds.ToAxisBounds();
            //DebugHelper.DrawCube(flatCullbounds.center, flatCullbounds.extents, Quaternion.identity, Color.yellow, 20);
            //DebugHelper.DrawCube(objectBounds.center, objectBounds.extents, objectBounds.Rotation, Color.cyan, 20);

            Plane startPlane, endPlane;

            GenerateSplinePlanes(0, mainSpline, out startPlane, out endPlane);

            var vsData = wrapper.GetCompoundVegetationStudioData(layer, true, flatCullbounds);

            for (int i = vsData.Count - 1; i >= 0; i--)
            {
                var vsInstance = vsData[i];
                if (config.IgnoredPrototypes.Contains(vsInstance.VSID))
                {
                    continue;
                }
                var wPos = wrapper.Terrain.TreeToWorldPos(vsInstance.Position);
                if (!objectBounds.Contains(wPos) || !GeometryExtensions.BetweenPlanes(wPos, startPlane, endPlane))
                {
                    continue;
                }

                var ut        = mainSpline.GetClosestUniformTimeOnSplineXZ(wPos.xz());
                var splinePos = mainSpline.GetUniformPointOnSpline(ut);
                var d         = splinePos.xz() - wPos.xz();

                if (d.sqrMagnitude < config.InstanceRemoveDistance * config.InstanceRemoveDistance && !layer.VSRemovals.Contains(vsInstance.Guid))
                {
                    layer.VSRemovals.Add(vsInstance.Guid);
                    //DebugHelper.DrawPoint(wPos, .5f, Color.red, 20);
                }
            }
        }
Esempio n. 3
0
        public override void ProcessSplats(TerrainWrapper wrapper, LayerBase baseLayer, int stencilKey)
        {
            var layer = baseLayer as MMTerrainLayer;

            if (layer == null)
            {
                Debug.LogWarning(string.Format("Attempted to write {0} to incorrect layer type! Expected Layer {1} to be {2}, but it was {3}", name, baseLayer.name, GetLayerType(), baseLayer.GetType()), this);
                return;
            }
            if (!Network || Configuration == null)
            {
                return;
            }
            var config = Configuration.GetConfig <Config>();

            if (config == null)
            {
                Debug.LogError("Invalid configuration! Expected ConnectionTerrainHeightConfiguration");
                return;
            }

            var terrain    = wrapper.Terrain;
            var splatRes   = wrapper.Terrain.terrainData.alphamapResolution;
            var mainSpline = NodeConnection.GetSpline();
            var radius     = config.Radius;

            // Create bounds to encapsulate spline (axis aligned)
            var bounds = mainSpline.GetApproximateBounds();

            bounds.Expand(radius * 2 * Vector3.one);

            // Create object bounds
            var objectBounds = mainSpline.GetApproximateXZObjectBounds();

            objectBounds.Expand(radius * 2 * Vector3.one);
            objectBounds.Expand(Vector3.up * 10000);

            // Early cull
            var axisBounds    = objectBounds.ToAxisBounds();
            var terrainBounds = wrapper.Terrain.GetComponent <Collider>().bounds;

            if (!terrainBounds.Intersects(axisBounds))
            {
                return;
            }

            float planeGive = -(wrapper.Terrain.terrainData.size.x / wrapper.Terrain.terrainData.alphamapResolution) * SplatOffset;
            Plane startPlane, endPlane;

            GenerateSplinePlanes(planeGive, mainSpline, out startPlane, out endPlane);

            // Get matrix space min/max
            var matrixMin = terrain.WorldToSplatCoord(bounds.min, TerrainX.RoundType.Floor);
            var matrixMax = terrain.WorldToSplatCoord(bounds.max, TerrainX.RoundType.Ceil);

            matrixMin = new Common.Coord(Mathf.Clamp(matrixMin.x, 0, terrain.terrainData.alphamapResolution), Mathf.Clamp(matrixMin.z, 0, terrain.terrainData.alphamapResolution));
            matrixMax = new Common.Coord(Mathf.Clamp(matrixMax.x, 0, terrain.terrainData.alphamapResolution), Mathf.Clamp(matrixMax.z, 0, terrain.terrainData.alphamapResolution));

            var floatArraySize = new Common.Coord(matrixMax.x - matrixMin.x, matrixMax.z - matrixMin.z);

            // Get all the existing compound splats
            var currentPrototypes = wrapper.GetCompoundSplatPrototypes(layer, true);
            var baseData          = layer.GetSplatMaps(matrixMin.x, matrixMin.z, floatArraySize.x, floatArraySize.z, splatRes);

            stencilKey = GetStencilKey();
            Serializable2DFloatArray thisPatchStencil = new Serializable2DFloatArray(floatArraySize.x,
                                                                                     floatArraySize.z);

            foreach (var splatConfiguration in config.SplatConfigurations)
            {
                var splatPrototypeWrapper = splatConfiguration.SplatPrototype;
                Serializable2DByteArray baseLayerSplat;
                if (!baseData.TryGetValue(splatPrototypeWrapper, out baseLayerSplat))
                {
                    baseLayerSplat = new Serializable2DByteArray(floatArraySize.x, floatArraySize.z);
                    baseData[splatPrototypeWrapper] = baseLayerSplat;
                }

                for (var dz = 0; dz < floatArraySize.z; ++dz)
                {
                    for (var dx = 0; dx < floatArraySize.x; ++dx)
                    {
                        var coordX   = matrixMin.x + dx;
                        var coordZ   = matrixMin.z + dz;
                        var worldPos = terrain.SplatCoordToWorldPos(new Common.Coord(coordX, coordZ));
                        worldPos = new Vector3(worldPos.x, objectBounds.center.y, worldPos.z);

                        if (terrain.ContainsPointXZ(worldPos) &&
                            objectBounds.Contains(worldPos) &&
                            GeometryExtensions.BetweenPlanes(worldPos, startPlane, endPlane))
                        {
                            var uniformT                   = mainSpline.GetClosestUniformTimeOnSplineXZ(worldPos.xz()); // Expensive!
                            var closestOnSpline            = mainSpline.GetUniformPointOnSpline(uniformT);
                            var normalizedFlatDistToSpline = (worldPos - closestOnSpline).xz().magnitude / (config.Radius);
                            if (normalizedFlatDistToSpline != Mathf.Clamp01(normalizedFlatDistToSpline))
                            {
                                continue;
                            }
                            var maskValue       = config.SplatFalloff.Evaluate(normalizedFlatDistToSpline);
                            var writeFloatValue = splatConfiguration.SplatStrength * maskValue;
                            var writeValue      = (byte)Mathf.RoundToInt(Mathf.Clamp(writeFloatValue * 255f, 0, 255));
                            var mainRead        = baseLayerSplat[dx, dz];
                            var newVal          = (byte)Mathf.Clamp(Mathf.Max(writeValue, mainRead), 0, 255);
                            var delta           = newVal - mainRead;

                            if (delta < 1 / 255f)
                            {
                                continue;
                            }

                            foreach (var currentPrototype in currentPrototypes)
                            {
                                if (!baseData.ContainsKey(currentPrototype))
                                {
                                    continue;
                                }
                                if (currentPrototype == splatPrototypeWrapper)
                                {
                                    continue;
                                }
                                var otherSplatFloatValue    = baseData[currentPrototype][dx, dz] / 255f;
                                var otherSplatFloatWriteVal = (otherSplatFloatValue * (1 - (delta / 255f)));
                                var write = (byte)Mathf.Clamp(Mathf.RoundToInt(otherSplatFloatWriteVal * 255), 0, 255);
                                baseData[currentPrototype][dx, dz] = write;
                            }
                            //DebugHelper.DrawPoint(worldPos, 1, Color.red, 10);
                            baseLayerSplat[dx, dz]        = newVal;
                            layer.Stencil[coordX, coordZ] = MiscUtilities.CompressStencil(stencilKey, 1);
                            thisPatchStencil[dx, dz]      = 1;
                        }
                        else
                        {
                            thisPatchStencil[dx, dz] = 0;
                        }
                    }
                }
            }

            foreach (var existingSplatPrototype in baseData)
            {
                var splat = existingSplatPrototype.Key;
                var data  = existingSplatPrototype.Value;
                layer.SetSplatmap(splat, matrixMin.x, matrixMin.z, data, wrapper.Terrain.terrainData.alphamapResolution, thisPatchStencil);
            }
        }