예제 #1
0
        /// <inheritdoc />
        public override unsafe void Apply(ref ApplyParams p)
        {
            var strength      = p.Strength * -10.0f;
            var brushPosition = p.Gizmo.CursorPosition;
            var tempBuffer    = (byte *)p.TempBuffer;

            // Apply brush modification
            Profiler.BeginEvent("Apply Brush");
            for (int z = 0; z < p.ModifiedSize.Y; z++)
            {
                var zz = z + p.ModifiedOffset.Y;
                for (int x = 0; x < p.ModifiedSize.X; x++)
                {
                    var xx         = x + p.ModifiedOffset.X;
                    var sourceMask = p.SourceHolesMask[zz * p.HeightmapSize + xx] != 0 ? 1.0f : 0.0f;

                    var samplePositionLocal = p.PatchPositionLocal + new Vector3(xx * FlaxEngine.Terrain.UnitsPerVertex, 0, zz * FlaxEngine.Terrain.UnitsPerVertex);
                    Vector3.Transform(ref samplePositionLocal, ref p.TerrainWorld, out Vector3 samplePositionWorld);
                    samplePositionWorld.Y = brushPosition.Y;

                    var paintAmount = p.Brush.Sample(ref brushPosition, ref samplePositionWorld);

                    tempBuffer[z * p.ModifiedSize.X + x] = (byte)((sourceMask + paintAmount * strength) < 0.8f ? 0 : 255);
                }
            }
            Profiler.EndEvent();

            // Update terrain patch
            TerrainTools.ModifyHolesMask(p.Terrain, ref p.PatchCoord, tempBuffer, ref p.ModifiedOffset, ref p.ModifiedSize);
        }
예제 #2
0
        /// <inheritdoc />
        public override unsafe void Apply(ref ApplyParams p)
        {
            // Prepare
            var brushPosition = p.Gizmo.CursorPosition;
            var noise         = new PerlinNoise(0, NoiseScale, p.Strength * NoiseAmount);
            var chunkSize     = p.Terrain.ChunkSize;
            var patchSize     = chunkSize * FlaxEngine.Terrain.PatchEdgeChunksCount;
            var patchOffset   = p.PatchCoord * patchSize;

            // Apply brush modification
            Profiler.BeginEvent("Apply Brush");
            for (int z = 0; z < p.ModifiedSize.Y; z++)
            {
                var zz = z + p.ModifiedOffset.Y;
                for (int x = 0; x < p.ModifiedSize.X; x++)
                {
                    var xx           = x + p.ModifiedOffset.X;
                    var sourceHeight = p.SourceHeightMap[zz * p.HeightmapSize + xx];

                    var samplePositionLocal = p.PatchPositionLocal + new Vector3(xx * FlaxEngine.Terrain.UnitsPerVertex, sourceHeight, zz * FlaxEngine.Terrain.UnitsPerVertex);
                    Vector3.Transform(ref samplePositionLocal, ref p.TerrainWorld, out Vector3 samplePositionWorld);

                    var noiseSample = noise.Sample(xx + patchOffset.X, zz + patchOffset.Y);
                    var paintAmount = p.Brush.Sample(ref brushPosition, ref samplePositionWorld);

                    p.TempBuffer[z * p.ModifiedSize.X + x] = sourceHeight + noiseSample * paintAmount;
                }
            }
            Profiler.EndEvent();

            // Update terrain patch
            TerrainTools.ModifyHeightMap(p.Terrain, ref p.PatchCoord, p.TempBuffer, ref p.ModifiedOffset, ref p.ModifiedSize);
        }
예제 #3
0
        /// <inheritdoc />
        public override unsafe void Apply(ref ApplyParams p)
        {
            var strength      = p.Strength * 1000.0f;
            var brushPosition = p.Gizmo.CursorPosition;

            // Apply brush modification
            Profiler.BeginEvent("Apply Brush");
            for (int z = 0; z < p.ModifiedSize.Y; z++)
            {
                var zz = z + p.ModifiedOffset.Y;
                for (int x = 0; x < p.ModifiedSize.X; x++)
                {
                    var xx           = x + p.ModifiedOffset.X;
                    var sourceHeight = p.SourceHeightMap[zz * p.HeightmapSize + xx];

                    var samplePositionLocal = p.PatchPositionLocal + new Vector3(xx * FlaxEngine.Terrain.UnitsPerVertex, sourceHeight, zz * FlaxEngine.Terrain.UnitsPerVertex);
                    Vector3.Transform(ref samplePositionLocal, ref p.TerrainWorld, out Vector3 samplePositionWorld);

                    var paintAmount = p.Brush.Sample(ref brushPosition, ref samplePositionWorld);

                    p.TempBuffer[z * p.ModifiedSize.X + x] = sourceHeight + paintAmount * strength;
                }
            }
            Profiler.EndEvent();

            // Update terrain patch
            TerrainTools.ModifyHeightMap(p.Terrain, ref p.PatchCoord, new IntPtr(p.TempBuffer), ref p.ModifiedOffset, ref p.ModifiedSize);
        }
예제 #4
0
        /// <inheritdoc />
        public override unsafe void Apply(ref ApplyParams p)
        {
            // Prepare
            var brushPosition = p.Gizmo.CursorPosition;
            var radius        = Mathf.Max(Mathf.CeilToInt(FilterRadius * 0.01f * p.Brush.Size), 2);
            var max           = p.HeightmapSize - 1;
            var strength      = Mathf.Saturate(p.Strength);

            // Apply brush modification
            Profiler.BeginEvent("Apply Brush");
            for (int z = 0; z < p.ModifiedSize.Y; z++)
            {
                var zz = z + p.ModifiedOffset.Y;
                for (int x = 0; x < p.ModifiedSize.X; x++)
                {
                    var xx           = x + p.ModifiedOffset.X;
                    var sourceHeight = p.SourceHeightMap[zz * p.HeightmapSize + xx];

                    var samplePositionLocal = p.PatchPositionLocal + new Vector3(xx * FlaxEngine.Terrain.UnitsPerVertex, sourceHeight, zz * FlaxEngine.Terrain.UnitsPerVertex);
                    Vector3.Transform(ref samplePositionLocal, ref p.TerrainWorld, out Vector3 samplePositionWorld);

                    var paintAmount = p.Brush.Sample(ref brushPosition, ref samplePositionWorld) * strength;

                    if (paintAmount > 0)
                    {
                        // Sum the nearby values
                        float smoothValue        = 0;
                        int   smoothValueSamples = 0;
                        int   minX = Math.Max(x - radius + p.ModifiedOffset.X, 0);
                        int   minZ = Math.Max(z - radius + p.ModifiedOffset.Y, 0);
                        int   maxX = Math.Min(x + radius + p.ModifiedOffset.X, max);
                        int   maxZ = Math.Min(z + radius + p.ModifiedOffset.Y, max);
                        for (int dz = minZ; dz <= maxZ; dz++)
                        {
                            for (int dx = minX; dx <= maxX; dx++)
                            {
                                var height = p.SourceHeightMap[dz * p.HeightmapSize + dx];
                                smoothValue += height;
                                smoothValueSamples++;
                            }
                        }

                        // Normalize
                        smoothValue /= smoothValueSamples;

                        // Blend between the height and smooth value
                        p.TempBuffer[z * p.ModifiedSize.X + x] = Mathf.Lerp(sourceHeight, smoothValue, paintAmount);
                    }
                    else
                    {
                        p.TempBuffer[z * p.ModifiedSize.X + x] = sourceHeight;
                    }
                }
            }
            Profiler.EndEvent();

            // Update terrain patch
            TerrainTools.ModifyHeightMap(p.Terrain, ref p.PatchCoord, p.TempBuffer, ref p.ModifiedOffset, ref p.ModifiedSize);
        }
예제 #5
0
        /// <inheritdoc />
        public override unsafe void Apply(ref ApplyParams p)
        {
            var strength       = p.Strength;
            var layer          = (int)Layer;
            var brushPosition  = p.Gizmo.CursorPosition;
            var layerComponent = layer % 4;

            // Apply brush modification
            Profiler.BeginEvent("Apply Brush");
            for (int z = 0; z < p.ModifiedSize.Y; z++)
            {
                var zz = z + p.ModifiedOffset.Y;
                for (int x = 0; x < p.ModifiedSize.X; x++)
                {
                    var xx  = x + p.ModifiedOffset.X;
                    var src = p.SourceData[zz * p.HeightmapSize + xx];

                    var samplePositionLocal = p.PatchPositionLocal + new Vector3(xx * FlaxEngine.Terrain.UnitsPerVertex, 0, zz * FlaxEngine.Terrain.UnitsPerVertex);
                    Vector3.Transform(ref samplePositionLocal, ref p.TerrainWorld, out Vector3 samplePositionWorld);

                    var paintAmount = p.Brush.Sample(ref brushPosition, ref samplePositionWorld) * strength;

                    // Extract layer weight
                    byte *srcPtr    = &src.R;
                    var   srcWeight = *(srcPtr + layerComponent) / 255.0f;

                    // Accumulate weight
                    float dstWeight = srcWeight + paintAmount;

                    // Check for solid layer case
                    if (dstWeight >= 1.0f)
                    {
                        // Erase other layers
                        // TODO: maybe erase only the higher layers?
                        // TODO: need to erase also weights form the other splatmaps
                        src = Color32.Transparent;

                        // Use limit value
                        dstWeight = 1.0f;
                    }

                    // Modify packed weight
                    *(srcPtr + layerComponent) = (byte)(dstWeight * 255.0f);

                    // Write back
                    p.TempBuffer[z * p.ModifiedSize.X + x] = src;
                }
            }
            Profiler.EndEvent();

            // Update terrain patch
            TerrainTools.ModifySplatMap(p.Terrain, ref p.PatchCoord, p.SplatmapIndex, p.TempBuffer, ref p.ModifiedOffset, ref p.ModifiedSize);
        }
예제 #6
0
        /// <inheritdoc />
        public override unsafe void Apply(ref ApplyParams p)
        {
            // If used with invert mode pick the target height level
            if (p.Options.Invert)
            {
                var center = p.ModifiedOffset + p.ModifiedSize / 2;
                TargetHeight = p.SourceHeightMap[center.Y * p.HeightmapSize + center.X];
                return;
            }

            // Prepare
            var brushPosition = p.Gizmo.CursorPosition;
            var targetHeight  = TargetHeight;
            var strength      = Mathf.Saturate(p.Strength);

            // Apply brush modification
            Profiler.BeginEvent("Apply Brush");
            for (int z = 0; z < p.ModifiedSize.Y; z++)
            {
                var zz = z + p.ModifiedOffset.Y;
                for (int x = 0; x < p.ModifiedSize.X; x++)
                {
                    var xx           = x + p.ModifiedOffset.X;
                    var sourceHeight = p.SourceHeightMap[zz * p.HeightmapSize + xx];

                    var samplePositionLocal = p.PatchPositionLocal + new Vector3(xx * FlaxEngine.Terrain.UnitsPerVertex, sourceHeight, zz * FlaxEngine.Terrain.UnitsPerVertex);
                    Vector3.Transform(ref samplePositionLocal, ref p.TerrainWorld, out Vector3 samplePositionWorld);

                    var paintAmount = p.Brush.Sample(ref brushPosition, ref samplePositionWorld) * strength;

                    // Blend between the height and the target value
                    p.TempBuffer[z * p.ModifiedSize.X + x] = Mathf.Lerp(sourceHeight, targetHeight, paintAmount);
                }
            }
            Profiler.EndEvent();

            // Update terrain patch
            TerrainTools.ModifyHeightMap(p.Terrain, ref p.PatchCoord, new IntPtr(p.TempBuffer), ref p.ModifiedOffset, ref p.ModifiedSize);
        }
예제 #7
0
        /// <summary>
        /// Applies the modification to the terrain.
        /// </summary>
        /// <param name="brush">The brush.</param>
        /// <param name="options">The options.</param>
        /// <param name="gizmo">The gizmo.</param>
        /// <param name="terrain">The terrain.</param>
        public unsafe void Apply(Brush brush, ref Options options, SculptTerrainGizmoMode gizmo, FlaxEngine.Terrain terrain)
        {
            // Combine final apply strength
            float strength = Strength * options.Strength * options.DeltaTime;

            if (strength <= 0.0f)
            {
                return;
            }
            if (options.Invert && SupportsNegativeApply)
            {
                strength *= -1;
            }

            // Prepare
            var         chunkSize         = terrain.ChunkSize;
            var         heightmapSize     = chunkSize * FlaxEngine.Terrain.PatchEdgeChunksCount + 1;
            var         heightmapLength   = heightmapSize * heightmapSize;
            var         patchSize         = chunkSize * FlaxEngine.Terrain.UnitsPerVertex * FlaxEngine.Terrain.PatchEdgeChunksCount;
            var         tempBuffer        = (float *)gizmo.GetHeightmapTempBuffer(heightmapLength * sizeof(float)).ToPointer();
            var         unitsPerVertexInv = 1.0f / FlaxEngine.Terrain.UnitsPerVertex;
            ApplyParams p = new ApplyParams
            {
                Terrain       = terrain,
                Brush         = brush,
                Gizmo         = gizmo,
                Options       = options,
                Strength      = strength,
                HeightmapSize = heightmapSize,
                TempBuffer    = tempBuffer,
            };

            // Get brush bounds in terrain local space
            var brushBounds = gizmo.CursorBrushBounds;

            terrain.GetLocalToWorldMatrix(out p.TerrainWorld);
            terrain.GetWorldToLocalMatrix(out var terrainInvWorld);
            BoundingBox.Transform(ref brushBounds, ref terrainInvWorld, out var brushBoundsLocal);

            // TODO: try caching brush weights before apply to reduce complexity and batch brush sampling

            // Process all the patches under the cursor
            for (int patchIndex = 0; patchIndex < gizmo.PatchesUnderCursor.Count; patchIndex++)
            {
                var patch = gizmo.PatchesUnderCursor[patchIndex];
                var patchPositionLocal = new Vector3(patch.PatchCoord.X * patchSize, 0, patch.PatchCoord.Y * patchSize);

                // Transform brush bounds from local terrain space into local patch vertex space
                var brushBoundsPatchLocalMin = (brushBoundsLocal.Minimum - patchPositionLocal) * unitsPerVertexInv;
                var brushBoundsPatchLocalMax = (brushBoundsLocal.Maximum - patchPositionLocal) * unitsPerVertexInv;

                // Calculate patch heightmap area to modify by brush
                var brushPatchMin  = new Int2(Mathf.FloorToInt(brushBoundsPatchLocalMin.X), Mathf.FloorToInt(brushBoundsPatchLocalMin.Z));
                var brushPatchMax  = new Int2(Mathf.CeilToInt(brushBoundsPatchLocalMax.X), Mathf.FloorToInt(brushBoundsPatchLocalMax.Z));
                var modifiedOffset = brushPatchMin;
                var modifiedSize   = brushPatchMax - brushPatchMin;

                // Expand the modification area by one vertex in each direction to ensure normal vectors are updated for edge cases, also clamp to prevent overflows
                if (modifiedOffset.X < 0)
                {
                    modifiedSize.X  += modifiedOffset.X;
                    modifiedOffset.X = 0;
                }
                if (modifiedOffset.Y < 0)
                {
                    modifiedSize.Y  += modifiedOffset.Y;
                    modifiedOffset.Y = 0;
                }
                modifiedSize.X = Mathf.Min(modifiedSize.X + 2, heightmapSize - modifiedOffset.X);
                modifiedSize.Y = Mathf.Min(modifiedSize.Y + 2, heightmapSize - modifiedOffset.Y);

                // Skip patch won't be modified at all
                if (modifiedSize.X <= 0 || modifiedSize.Y <= 0)
                {
                    continue;
                }

                // Get the patch data (cached internally by the c++ core in editor)
                float *sourceHeights = EditHoles ? null : TerrainTools.GetHeightmapData(terrain, ref patch.PatchCoord);
                byte * sourceHoles   = EditHoles ? TerrainTools.GetHolesMaskData(terrain, ref patch.PatchCoord) : null;
                if (sourceHeights == null && sourceHoles == null)
                {
                    throw new FlaxException("Cannot modify terrain. Loading heightmap failed. See log for more info.");
                }

                // Record patch data before editing it
                if (!gizmo.CurrentEditUndoAction.HashPatch(ref patch.PatchCoord))
                {
                    gizmo.CurrentEditUndoAction.AddPatch(ref patch.PatchCoord);
                }

                // Apply modification
                p.ModifiedOffset     = modifiedOffset;
                p.ModifiedSize       = modifiedSize;
                p.PatchCoord         = patch.PatchCoord;
                p.PatchPositionLocal = patchPositionLocal;
                p.SourceHeightMap    = sourceHeights;
                p.SourceHolesMask    = sourceHoles;
                Apply(ref p);
            }

            var  editorOptions = Editor.Instance.Options.Options;
            bool isPlayMode    = Editor.Instance.StateMachine.IsPlayMode;

            // Auto NavMesh rebuild
            if (!isPlayMode && editorOptions.General.AutoRebuildNavMesh)
            {
                if (terrain.Scene && (terrain.StaticFlags & StaticFlags.Navigation) == StaticFlags.Navigation)
                {
                    Navigation.BuildNavMesh(terrain.Scene, brushBounds, editorOptions.General.AutoRebuildNavMeshTimeoutMs);
                }
            }
        }
예제 #8
0
 /// <summary>
 /// Applies the modification to the terrain.
 /// </summary>
 /// <param name="p">The parameters to use.</param>
 public abstract void Apply(ref ApplyParams p);
예제 #9
0
파일: Mode.cs 프로젝트: stefnotch/FlaxAPI
        /// <summary>
        /// Applies the modification to the terrain.
        /// </summary>
        /// <param name="brush">The brush.</param>
        /// <param name="options">The options.</param>
        /// <param name="gizmo">The gizmo.</param>
        /// <param name="terrain">The terrain.</param>
        public unsafe void Apply(Brush brush, ref Options options, PaintTerrainGizmoMode gizmo, FlaxEngine.Terrain terrain)
        {
            // Combine final apply strength
            float strength = Strength * options.Strength * options.DeltaTime * 10.0f;

            if (strength <= 0.0f)
            {
                return;
            }
            if (options.Invert && SupportsNegativeApply)
            {
                strength *= -1;
            }

            // Prepare
            var         splatmapIndex     = ActiveSplatmapIndex;
            var         chunkSize         = terrain.ChunkSize;
            var         heightmapSize     = chunkSize * FlaxEngine.Terrain.PatchEdgeChunksCount + 1;
            var         heightmapLength   = heightmapSize * heightmapSize;
            var         patchSize         = chunkSize * FlaxEngine.Terrain.UnitsPerVertex * FlaxEngine.Terrain.PatchEdgeChunksCount;
            var         tempBuffer        = (Color32 *)gizmo.GetSplatmapTempBuffer(heightmapLength * Color32.SizeInBytes).ToPointer();
            var         unitsPerVertexInv = 1.0f / FlaxEngine.Terrain.UnitsPerVertex;
            ApplyParams p = new ApplyParams
            {
                Terrain       = terrain,
                Brush         = brush,
                Gizmo         = gizmo,
                Options       = options,
                Strength      = strength,
                SplatmapIndex = splatmapIndex,
                HeightmapSize = heightmapSize,
                TempBuffer    = tempBuffer,
            };

            // Get brush bounds in terrain local space
            var brushBounds = gizmo.CursorBrushBounds;

            terrain.GetLocalToWorldMatrix(out p.TerrainWorld);
            terrain.GetWorldToLocalMatrix(out var terrainInvWorld);
            BoundingBox.Transform(ref brushBounds, ref terrainInvWorld, out var brushBoundsLocal);

            // TODO: try caching brush weights before apply to reduce complexity and batch brush sampling

            // Process all the patches under the cursor
            for (int patchIndex = 0; patchIndex < gizmo.PatchesUnderCursor.Count; patchIndex++)
            {
                var patch = gizmo.PatchesUnderCursor[patchIndex];
                var patchPositionLocal = new Vector3(patch.PatchCoord.X * patchSize, 0, patch.PatchCoord.Y * patchSize);

                // Transform brush bounds from local terrain space into local patch vertex space
                var brushBoundsPatchLocalMin = (brushBoundsLocal.Minimum - patchPositionLocal) * unitsPerVertexInv;
                var brushBoundsPatchLocalMax = (brushBoundsLocal.Maximum - patchPositionLocal) * unitsPerVertexInv;

                // Calculate patch heightmap area to modify by brush
                var brushPatchMin  = new Int2(Mathf.FloorToInt(brushBoundsPatchLocalMin.X), Mathf.FloorToInt(brushBoundsPatchLocalMin.Z));
                var brushPatchMax  = new Int2(Mathf.CeilToInt(brushBoundsPatchLocalMax.X), Mathf.FloorToInt(brushBoundsPatchLocalMax.Z));
                var modifiedOffset = brushPatchMin;
                var modifiedSize   = brushPatchMax - brushPatchMin;

                // Clamp to prevent overflows
                if (modifiedOffset.X < 0)
                {
                    modifiedSize.X  += modifiedOffset.X;
                    modifiedOffset.X = 0;
                }
                if (modifiedOffset.Y < 0)
                {
                    modifiedSize.Y  += modifiedOffset.Y;
                    modifiedOffset.Y = 0;
                }
                modifiedSize.X = Mathf.Min(modifiedSize.X, heightmapSize - modifiedOffset.X);
                modifiedSize.Y = Mathf.Min(modifiedSize.Y, heightmapSize - modifiedOffset.Y);

                // Skip patch won't be modified at all
                if (modifiedSize.X <= 0 || modifiedSize.Y <= 0)
                {
                    continue;
                }

                // Get the patch data (cached internally by the c++ core in editor)
                var sourceDataPtr = TerrainTools.GetSplatMapData(terrain, ref patch.PatchCoord, splatmapIndex);
                if (sourceDataPtr == IntPtr.Zero)
                {
                    throw new FlaxException("Cannot modify terrain. Loading splatmap failed. See log for more info.");
                }
                var sourceData = (Color32 *)sourceDataPtr.ToPointer();

                // Record patch data before editing it
                if (!gizmo.CurrentEditUndoAction.HashPatch(ref patch.PatchCoord))
                {
                    gizmo.CurrentEditUndoAction.AddPatch(ref patch.PatchCoord, splatmapIndex);
                }

                // Apply modification
                p.ModifiedOffset     = modifiedOffset;
                p.ModifiedSize       = modifiedSize;
                p.PatchCoord         = patch.PatchCoord;
                p.PatchPositionLocal = patchPositionLocal;
                p.SourceData         = sourceData;
                Apply(ref p);
            }
        }