示例#1
0
            private void OnPatchEdit(FlaxEngine.Terrain terrain, ref BoundingBox patchBounds)
            {
                Editor.Instance.Scene.MarkSceneEdited(terrain.Scene);

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

                // Auto NavMesh rebuild
                if (!isPlayMode && editorOptions.General.AutoRebuildNavMesh)
                {
                    if (terrain.Scene && terrain.HasStaticFlag(StaticFlags.Navigation))
                    {
                        Navigation.BuildNavMesh(terrain.Scene, patchBounds, editorOptions.General.AutoRebuildNavMeshTimeoutMs);
                    }
                }
            }
示例#2
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.HasStaticFlag(StaticFlags.Navigation))
                {
                    Navigation.BuildNavMesh(terrain.Scene, brushBounds, editorOptions.General.AutoRebuildNavMeshTimeoutMs);
                }
            }
        }