fyrox_impl/scene/terrain/brushstroke/
mod.rs

1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21//! The brushstroke module contains tools for modifying terrain textures.
22//! It uses a triple-buffer system to separate the UI mouse movements
23//! from the update of the data within the actual textures.
24//! 1. The first buffer is a [std::sync::mpsc::channel] that is used to send
25//! messages to control a thread that processes brush strokes.
26//! These messages are [BrushThreadMessage]. Some of the messages
27//! are processed as soon as they are received, but [BrushThreadMessage::Pixel]
28//! messages are sent to the next buffer.
29//! 2. The pixel message buffer holds a limited number of pixel messages.
30//! It serves to merge redundent pixel messages to spare the thread from
31//! repeating work. It is expected that brush operations will paint multiple
32//! times to the same pixel in quick succession.
33//! Once the new value for a pixel has been calculated, the value is stored
34//! in the third buffer.
35//! 3. The [StrokeData] buffer stores every change that a particular brush stroke
36//! has made to the texture. Because modifying a texture is a nontrivial operation,
37//! modified pixels are allowed to accumulate to some quantity before the new pixel
38//! values are actually written to the textures of the terrain.
39//! [StrokeChunks] is used to keep track of which pixels are waiting to be written
40//! to which terrain chunks.
41use super::Chunk;
42use crate::asset::ResourceDataRef;
43use crate::core::{
44    algebra::{Matrix2, Vector2},
45    log::Log,
46    math::Rect,
47    pool::Handle,
48    reflect::prelude::*,
49};
50use crate::fxhash::FxHashMap;
51use crate::resource::texture::{Texture, TextureResource};
52use crate::scene::node::Node;
53use fyrox_core::uuid_provider;
54use std::collections::VecDeque;
55use std::sync::mpsc::{Receiver, SendError, Sender};
56
57pub mod brushraster;
58use brushraster::*;
59pub mod strokechunks;
60use strokechunks::*;
61
62/// The number of pixel messages we can accept at once before we must start processing them.
63/// Often later messages will cause earlier messages to be unnecessary, so it can be more efficient
64/// to let some messages accumulate rather than process each message one-at-a-time.
65const MESSAGE_BUFFER_SIZE: usize = 40;
66/// The number of processed pixels we can hold before we must write the pixels to the targetted textures.
67/// Modifying a texture is expensive, so it is important to do it in batches of multiple pixels.
68const PIXEL_BUFFER_SIZE: usize = 40;
69/// The maximum number of pixels that are allowed to be involved in a single step of a brushstroke.
70/// This limit is arbitrarily chosen, but there should be some limit to prevent the editor
71/// from freezing as a result of an excessively large brush.
72const BRUSH_PIXEL_SANITY_LIMIT: i32 = 1000000;
73
74#[inline]
75fn mask_raise(original: u8, amount: f32) -> u8 {
76    (original as f32 + amount * 255.0).clamp(0.0, 255.0) as u8
77}
78
79#[inline]
80fn mask_lerp(original: u8, value: f32, t: f32) -> u8 {
81    let original = original as f32;
82    let value = value * 255.0;
83    (original * (1.0 - t) + value * t).clamp(0.0, 255.0) as u8
84}
85
86/// A message that can be sent to the terrain painting thread to control the painting.
87#[derive(Debug, Clone)]
88pub enum BrushThreadMessage {
89    /// Set the brush that will be used for future pixels and the textures that will be modified.
90    StartStroke(Brush, Handle<Node>, TerrainTextureData),
91    /// No futher pixels will be sent for the current stroke.
92    EndStroke,
93    /// Paint the given pixel as part of the current stroke.
94    Pixel(BrushPixelMessage),
95}
96
97/// A message that can be sent to indicate that the pixel at the given coordinates
98/// should be painted with the given alpha.
99#[derive(Debug, Clone)]
100pub struct BrushPixelMessage {
101    /// The coordinates of the pixel to paint.
102    pub position: Vector2<i32>,
103    /// The transparency of the brush, from 0.0 for transparent to 1.0 for opaque.
104    pub alpha: f32,
105    /// A value whose meaning depends on the brush.
106    /// For flatten brushes, this is the target height.
107    pub value: f32,
108}
109
110/// A queue that stores pixels that are waiting to be drawn by the brush.
111pub struct PixelMessageBuffer {
112    data: VecDeque<BrushPixelMessage>,
113    max_size: usize,
114}
115
116impl PixelMessageBuffer {
117    /// Create a new buffer with the given size.
118    #[inline]
119    pub fn new(max_size: usize) -> Self {
120        Self {
121            data: VecDeque::with_capacity(max_size),
122            max_size,
123        }
124    }
125    /// True if the buffer has reached its maximum size.
126    #[inline]
127    pub fn is_full(&self) -> bool {
128        self.max_size == self.data.len()
129    }
130    /// True if there is nothing to pop from the queue.
131    #[inline]
132    pub fn is_empty(&self) -> bool {
133        self.data.is_empty()
134    }
135    /// Remove the message from the front of the queue and return it, if the queue is not empty.
136    #[inline]
137    pub fn pop(&mut self) -> Option<BrushPixelMessage> {
138        self.data.pop_front()
139    }
140    /// Push a message onto the back of the queue, or panic of the queue is full.
141    pub fn push(&mut self, message: BrushPixelMessage) {
142        assert!(self.data.len() < self.max_size);
143        if let Some(m) = self
144            .data
145            .iter_mut()
146            .find(|m| m.position == message.position)
147        {
148            if message.alpha > m.alpha {
149                m.alpha = message.alpha;
150                m.value = message.value;
151            }
152        } else {
153            self.data.push_back(message);
154        }
155    }
156}
157
158/// Object to send to painting thread to control which textures are modified.
159#[derive(Debug, Clone)]
160pub struct TerrainTextureData {
161    /// The height and width of the texture in pixels.
162    pub chunk_size: Vector2<u32>,
163    /// The kind of texture.
164    pub kind: TerrainTextureKind,
165    /// The texture resources, organized by chunk grid position.
166    pub resources: FxHashMap<Vector2<i32>, TextureResource>,
167}
168
169/// Terrain textures come in multiple kinds.
170/// Height textures contain f32 values for each vertex of the terrain.
171/// Mask textures contain u8 values indicating transparency.
172/// Coordinates are interpreted differently between the two kinds of texture
173/// because the data in height textures overlaps with the data in neighboring chunks,
174/// so the pixels along each edge are duplicated and must be kept in sync
175/// so that the chunks do not disconnect.
176#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
177pub enum TerrainTextureKind {
178    #[default]
179    /// Height texture with f32 height values and overlapping edges between chunks.
180    Height,
181    /// Mask texture with u8 oppacity values.
182    Mask,
183}
184
185/// Sender with methods for sending the messages which control a brush painting thread.
186pub struct BrushSender(Sender<BrushThreadMessage>);
187
188impl BrushSender {
189    /// Create a new BrushSender using the given Sender.
190    pub fn new(sender: Sender<BrushThreadMessage>) -> Self {
191        Self(sender)
192    }
193    /// Begin a new stroke using the given brush.
194    pub fn start_stroke(&self, brush: Brush, node: Handle<Node>, data: TerrainTextureData) {
195        self.0
196            .send(BrushThreadMessage::StartStroke(brush, node, data))
197            .unwrap_or_else(on_send_failure);
198    }
199    /// End the current stroke.
200    pub fn end_stroke(&self) {
201        self.0
202            .send(BrushThreadMessage::EndStroke)
203            .unwrap_or_else(on_send_failure);
204    }
205    /// Draw a pixel using the brush that was set in the most recent call to [BrushSender::start_stroke].
206    #[inline]
207    pub fn draw_pixel(&self, position: Vector2<i32>, alpha: f32, value: f32) {
208        if alpha == 0.0 {
209            return;
210        }
211        self.0
212            .send(BrushThreadMessage::Pixel(BrushPixelMessage {
213                position,
214                alpha,
215                value,
216            }))
217            .unwrap_or_else(on_send_failure);
218    }
219}
220
221fn on_send_failure(error: SendError<BrushThreadMessage>) {
222    Log::err(format!("A brush painting message was not sent. {error:?}"));
223}
224
225/// Type for a callback that delivers the original data of textures that have been modified
226/// by the brush so that changes might be undone.
227pub type UndoChunkHandler = dyn FnMut(UndoData) + Send;
228
229/// A record of original data data for chunks that have been modified by a brushstroke.
230pub struct UndoData {
231    /// The handle of the terrain being edited
232    pub node: Handle<Node>,
233    /// The data of the chunks as they were before the brushstroke.
234    pub chunks: Vec<ChunkData>,
235    /// The kind of data within the terrain that is being edited.
236    pub target: BrushTarget,
237}
238
239#[derive(Default)]
240/// Data for an in-progress terrain painting operation
241pub struct BrushStroke {
242    /// The brush that is currently being used. This determines how the terrain textures are edited.
243    brush: Brush,
244    /// The textures for the terrain that is currently being edited.
245    textures: FxHashMap<Vector2<i32>, TextureResource>,
246    /// The node of the terrain being edted
247    node: Handle<Node>,
248    /// Callback to handle the saved original chunk data after each stroke.
249    /// This is called when [BrushThreadMessage::EndStroke] is received.
250    undo_chunk_handler: Option<Box<UndoChunkHandler>>,
251    /// A record of which pixels have been modified in each chunk since the last UpdateTextures.
252    /// This is cleared after [BrushThreadMessage::UpdateTextures] is received,
253    /// when the pixel data is transferred into the textures.
254    chunks: StrokeChunks,
255    /// Data copied from chunks that have been edited by the current brush stroke.
256    /// This preserves the textures as they were before the stroke began, so that
257    /// an undo command can be created at the end of the stroke.
258    undo_chunks: Vec<ChunkData>,
259    /// A record of every pixel of the stroke, including the strength of the brush at that pixel,
260    /// the original value before the stroke began, and the current value.
261    height_pixels: StrokeData<f32>,
262    /// A record of every pixel of the stroke, including the strength of the brush at that pixel,
263    /// the original value before the stroke began, and the current value.
264    mask_pixels: StrokeData<u8>,
265}
266
267/// Stores pixels that have been modified by a brush during a stroke.
268/// It remembers the strength of the brush, the value of the painted pixel,
269/// and the value of the original pixel before the brushstroke.
270/// This should be cleared after each stroke using [StrokeData::clear].
271///
272/// `V` is the type of data stored in the pixel being edited.
273#[derive(Debug, Default)]
274pub struct StrokeData<V>(FxHashMap<Vector2<i32>, StrokeElement<V>>);
275
276/// A single pixel data of a brush stroke
277#[derive(Debug, Copy, Clone)]
278pub struct StrokeElement<V> {
279    /// The intensity of the brush stroke, with 0.0 indicating a pixel that brush has not touched
280    /// and 1.0 indicates a pixel fully covered by the brush.
281    pub strength: f32,
282    /// The value of the pixel before the stroke began.
283    pub original_value: V,
284    /// The current value of the pixel.
285    pub latest_value: V,
286}
287
288impl BrushStroke {
289    /// Create a BrushStroke with the given handler for saving undo data for chunks.
290    pub fn with_chunk_handler(undo_chunk_handler: Box<UndoChunkHandler>) -> Self {
291        Self {
292            undo_chunk_handler: Some(undo_chunk_handler),
293            ..Default::default()
294        }
295    }
296    /// The brush that this stroke is using. This is immutable access only, because
297    /// the brush's target may only be changed through [BrushStroke::start_stroke] or
298    /// [BrushStroke::accept_messages].
299    ///
300    /// Mutable access to the brush's other properties is available through
301    /// [BrushStroke::shape], [BrushStroke::mode], [BrushStroke::hardness],
302    /// and [BrushStroke::alpha].
303    pub fn brush(&self) -> &Brush {
304        &self.brush
305    }
306    /// Mutable access to the brush's shape
307    pub fn shape(&mut self) -> &mut BrushShape {
308        &mut self.brush.shape
309    }
310    /// Mutable access to the brush's mode
311    pub fn mode(&mut self) -> &mut BrushMode {
312        &mut self.brush.mode
313    }
314    /// Mutable access to the brush's hardness. The hardness controls how the edges
315    /// of the brush are blended with the original value of the texture.
316    pub fn hardness(&mut self) -> &mut f32 {
317        &mut self.brush.hardness
318    }
319    /// Mutable access to the brush's alpha. The alpha value controls how
320    /// the operation's result is blended with the original value of the texture.
321    pub fn alpha(&mut self) -> &mut f32 {
322        &mut self.brush.alpha
323    }
324    /// Insert a stamp of the brush at the given position with the given texture scale and the given value.
325    /// - `position`: The center of the stamp.
326    /// - `scale`: The size of each pixel in 2D local space. This is used to convert the brush's shape from local space to texture space.
327    /// - `value`: A value that is a parameter for the brush operation.
328    pub fn stamp(&mut self, position: Vector2<f32>, scale: Vector2<f32>, value: f32) {
329        let brush = self.brush.clone();
330        brush.stamp(position, scale, |position, alpha| {
331            self.draw_pixel(BrushPixelMessage {
332                position,
333                alpha,
334                value,
335            })
336        });
337    }
338    /// Insert a smear of the brush at the given position with the given texture scale and the given value.
339    /// - `start`: The center of the smear's start.
340    /// - `end`: The center of the smear's end.
341    /// - `scale`: The size of each pixel in 2D local space. This is used to convert the brush's shape from local space to texture space.
342    /// - `value`: A value that is a parameter for the brush operation.
343    pub fn smear(
344        &mut self,
345        start: Vector2<f32>,
346        end: Vector2<f32>,
347        scale: Vector2<f32>,
348        value: f32,
349    ) {
350        let brush = self.brush.clone();
351        brush.smear(start, end, scale, |position, alpha| {
352            self.draw_pixel(BrushPixelMessage {
353                position,
354                alpha,
355                value,
356            })
357        });
358    }
359    /// Prepare this object for a new brushstroke.
360    pub fn clear(&mut self) {
361        self.height_pixels.clear();
362        self.mask_pixels.clear();
363        self.chunks.clear();
364    }
365    /// Access the data in the textures to find the value for the pixel at the given position.
366    pub fn data_pixel<V>(&self, position: Vector2<i32>) -> Option<V>
367    where
368        V: Clone,
369    {
370        // Determine which texture holds the data for the position.
371        let grid_pos = self.chunks.pixel_position_to_grid_position(position);
372        // Determine which pixel within the texture corresponds to the given position.
373        let origin = self.chunks.chunk_to_origin(grid_pos);
374        let p = position - origin;
375        // Access the texture and extract the data.
376        let texture = self.textures.get(&grid_pos)?;
377        let index = self.chunks.pixel_index(p);
378        let data = texture.data_ref();
379        Some(data.data_of_type::<V>().unwrap()[index].clone())
380    }
381    /// Block on the given receiver until its messages are exhausted and perform the painting
382    /// operations according to the messages.
383    /// It does not return until the receiver's channel no longer has senders.
384    pub fn accept_messages(&mut self, receiver: Receiver<BrushThreadMessage>) {
385        let mut message_buffer = PixelMessageBuffer::new(MESSAGE_BUFFER_SIZE);
386        loop {
387            // Collect all waiting messages, until the buffer is full.
388            while !message_buffer.is_full() {
389                if let Ok(message) = receiver.try_recv() {
390                    // Act on the message, potentially adding it to the message buffer.
391                    self.handle_message(message, &mut message_buffer);
392                } else {
393                    break;
394                }
395            }
396            if let Some(pixel) = message_buffer.pop() {
397                // Perform the drawing operation for the current pixel message.
398                self.handle_pixel_message(pixel);
399            } else if self.chunks.count() > 0 {
400                // We have run out of pixels to process, so before we block to wait for more,
401                // write the currently processed pixels to the terrain textures.
402                self.flush();
403            } else {
404                // If the message buffer is empty, we cannot proceed, so block until a message is available.
405                // Block until either a message arrives or the channel is closed.
406                if let Ok(message) = receiver.recv() {
407                    // Act on the message, potentially adding it to the message buffer.
408                    self.handle_message(message, &mut message_buffer);
409                } else {
410                    // The message buffer is empty and the channel is closed, so we're finished.
411                    // Flush pixels to the textures.
412                    self.end_stroke();
413                    return;
414                }
415            }
416        }
417    }
418    fn handle_message(
419        &mut self,
420        message: BrushThreadMessage,
421        message_buffer: &mut PixelMessageBuffer,
422    ) {
423        match message {
424            BrushThreadMessage::StartStroke(brush, node, textures) => {
425                self.brush = brush;
426                self.node = node;
427                self.textures = textures.resources;
428                self.chunks.set_layout(textures.kind, textures.chunk_size);
429            }
430            BrushThreadMessage::EndStroke => {
431                // The stroke has ended, so finish processing all buffered pixel messages.
432                while let Some(p) = message_buffer.pop() {
433                    self.handle_pixel_message(p);
434                }
435                // Apply buffered pixels to the terrain textures.
436                self.end_stroke();
437            }
438            BrushThreadMessage::Pixel(pixel) => {
439                message_buffer.push(pixel);
440            }
441        }
442    }
443    /// -`brush`: The brush to paint with
444    /// -`node`: The handle of the terrain being modified. It is not used except to pass to `undo_chunk_handler`,
445    /// so it can be safely [Handle::NONE] if `undo_chunk_handler` is None, or if `undo_chunk_handler` is prepared for NONE.
446    /// -`textures`: Hash map of texture resources that this stroke will edit.
447    pub fn start_stroke(&mut self, brush: Brush, node: Handle<Node>, textures: TerrainTextureData) {
448        self.brush = brush;
449        self.node = node;
450        self.chunks.set_layout(textures.kind, textures.chunk_size);
451        self.textures = textures.resources;
452    }
453    /// Send the textures that have been touched by the brush to the undo handler,
454    /// then write the current changes to the textures and clear the stroke to prepare
455    /// for starting a new stroke.
456    pub fn end_stroke(&mut self) {
457        if let Some(handler) = &mut self.undo_chunk_handler {
458            // Copy the textures that are about to be modified so that the modifications can be undone.
459            self.chunks
460                .copy_texture_data(&self.textures, &mut self.undo_chunks);
461            // Send the saved textures to the handler so that an undo command might be created.
462            handler(UndoData {
463                node: self.node,
464                chunks: std::mem::take(&mut self.undo_chunks),
465                target: self.brush.target,
466            });
467        }
468        // Flush pixels to the terrain textures
469        self.apply();
470        self.clear();
471    }
472    /// Insert a pixel with the given texture-space coordinates and strength.
473    pub fn draw_pixel(&mut self, pixel: BrushPixelMessage) {
474        let pixel = BrushPixelMessage {
475            alpha: 0.5 * (1.0 - (pixel.alpha * std::f32::consts::PI).cos()),
476            ..pixel
477        };
478        let position = pixel.position;
479        match self.chunks.kind() {
480            TerrainTextureKind::Height => self.accept_pixel_height(pixel),
481            TerrainTextureKind::Mask => self.accept_pixel_mask(pixel),
482        }
483        self.chunks.write(position);
484    }
485    fn handle_pixel_message(&mut self, pixel: BrushPixelMessage) {
486        self.draw_pixel(pixel);
487        if self.chunks.count() >= PIXEL_BUFFER_SIZE {
488            self.flush();
489        }
490    }
491    fn smooth_height(
492        &self,
493        position: Vector2<i32>,
494        kernel_radius: u32,
495        original: f32,
496        alpha: f32,
497    ) -> f32 {
498        let radius = kernel_radius as i32;
499        let diameter = kernel_radius * 2 + 1;
500        let area = (diameter * diameter - 1) as f32;
501        let mut total = 0.0;
502        for x in -radius..=radius {
503            for y in -radius..=radius {
504                if x == 0 && y == 0 {
505                    continue;
506                }
507                let pos = position + Vector2::new(x, y);
508                let value = self
509                    .height_pixels
510                    .original_pixel_value(pos)
511                    .copied()
512                    .or_else(|| self.data_pixel(position))
513                    .unwrap_or_default();
514                total += value;
515            }
516        }
517        let smoothed = total / area;
518        original * (1.0 - alpha) + smoothed * alpha
519    }
520    fn smooth_mask(
521        &self,
522        position: Vector2<i32>,
523        kernel_radius: u32,
524        original: u8,
525        alpha: f32,
526    ) -> u8 {
527        let radius = kernel_radius as i32;
528        let diameter = kernel_radius as u64 * 2 + 1;
529        let area = diameter * diameter - 1;
530        let mut total: u64 = 0;
531        for x in -radius..=radius {
532            for y in -radius..=radius {
533                if x == 0 && y == 0 {
534                    continue;
535                }
536                let pos = position + Vector2::new(x, y);
537                let value = self
538                    .mask_pixels
539                    .original_pixel_value(pos)
540                    .copied()
541                    .or_else(|| self.data_pixel(position))
542                    .unwrap_or_default();
543                total += value as u64;
544            }
545        }
546        let smoothed = total / area;
547        (original as f32 * (1.0 - alpha) + smoothed as f32 * alpha).clamp(0.0, 255.0) as u8
548    }
549    fn accept_pixel_height(&mut self, pixel: BrushPixelMessage) {
550        let position = pixel.position;
551        let mut pixels = std::mem::take(&mut self.height_pixels);
552        let original = pixels.update_strength(position, pixel.alpha, || self.data_pixel(position));
553        self.height_pixels = pixels;
554        let Some(original) = original else {
555            return;
556        };
557        let alpha = self.brush.alpha * pixel.alpha;
558        let result: f32 = match self.brush.mode {
559            BrushMode::Raise { amount } => original + amount * alpha,
560            BrushMode::Flatten => original * (1.0 - alpha) + pixel.value * alpha,
561            BrushMode::Assign { value } => original * (1.0 - alpha) + value * alpha,
562            BrushMode::Smooth { kernel_radius } => {
563                self.smooth_height(position, kernel_radius, original, alpha)
564            }
565        };
566        self.height_pixels.set_latest(position, result);
567    }
568    fn accept_pixel_mask(&mut self, pixel: BrushPixelMessage) {
569        let position = pixel.position;
570        let mut pixels = std::mem::take(&mut self.mask_pixels);
571        let original = pixels.update_strength(position, pixel.alpha, || self.data_pixel(position));
572        self.mask_pixels = pixels;
573        let Some(original) = original else {
574            return;
575        };
576        let alpha = self.brush.alpha * pixel.alpha;
577        let result: u8 = match self.brush.mode {
578            BrushMode::Raise { amount } => mask_raise(original, amount * alpha),
579            BrushMode::Flatten => mask_lerp(original, pixel.value, alpha),
580            BrushMode::Assign { value } => mask_lerp(original, value, alpha),
581            BrushMode::Smooth { kernel_radius } => {
582                self.smooth_mask(position, kernel_radius, original, alpha)
583            }
584        };
585        self.mask_pixels.set_latest(position, result);
586    }
587    /// Update the texture resources to match the current state of this stroke.
588    pub fn flush(&mut self) {
589        // chunks stores the pixels that we have modified but not yet written to the textures.
590        // If we have an undo handler, we must inform the handler of the textures we are writing to
591        // because we are about to forget that we have written to them.
592        if self.undo_chunk_handler.is_some() {
593            // Copy the textures that are about to be modified so that the modifications can be undone.
594            self.chunks
595                .copy_texture_data(&self.textures, &mut self.undo_chunks);
596        }
597        // Do the actual texture modification.
598        self.apply();
599        // Erase our memory of having modified these pixels, since they have already been finalized
600        // by writing them to the textures.
601        self.chunks.clear();
602    }
603    fn apply(&self) {
604        match self.chunks.kind() {
605            TerrainTextureKind::Height => {
606                self.chunks.apply(&self.height_pixels, &self.textures);
607            }
608            TerrainTextureKind::Mask => {
609                self.chunks.apply(&self.mask_pixels, &self.textures);
610            }
611        }
612    }
613}
614
615impl<V> StrokeData<V> {
616    /// Reset the brush stroke so it is ready to begin a new stroke.
617    #[inline]
618    pub fn clear(&mut self) {
619        self.0.clear()
620    }
621    /// For every pixel that is modified by the stroke, the original values
622    /// is stored as it was before the stroke began.
623    #[inline]
624    pub fn original_pixel_value(&self, position: Vector2<i32>) -> Option<&V> {
625        self.0.get(&position).map(|x| &x.original_value)
626    }
627    /// The updated pixel value based on whatever editing operation the stroke is performing.
628    #[inline]
629    pub fn latest_pixel_value(&self, position: Vector2<i32>) -> Option<&V> {
630        self.0.get(&position).map(|x| &x.latest_value)
631    }
632    /// Update the stroke with a new value at the given pixel position.
633    /// This must only be called after calling [StrokeData::update_strength]
634    /// to ensure that this stroke contains data for the position.
635    /// Otherwise this method may panic.
636    #[inline]
637    pub fn set_latest(&mut self, position: Vector2<i32>, value: V) {
638        if let Some(el) = self.0.get_mut(&position) {
639            el.latest_value = value;
640        } else {
641            panic!("Setting latest value of missing element");
642        }
643    }
644    /// Stores or modifies the StrokeElement at the given position.
645    /// If the element is updated, return the original pixel value of the element.
646    /// - `position`: The position of the data to modify within the terrain.
647    /// - `strength`: The strength of the brush at the position, from 0.0 to 1.0.
648    /// The element is updated if the stored strength is less than the given strength.
649    /// If there is no stored strength, that is treated as a strength of 0.0.
650    /// - `pixel_value`: The current value of the data.
651    /// This may be stored in the StrokeData if no pixel value is currently recorded for the given position.
652    /// Otherwise, this value is ignored.
653    #[inline]
654    pub fn update_strength<F>(
655        &mut self,
656        position: Vector2<i32>,
657        strength: f32,
658        pixel_value: F,
659    ) -> Option<V>
660    where
661        V: Clone,
662        F: FnOnce() -> Option<V>,
663    {
664        if strength == 0.0 {
665            None
666        } else if let Some(element) = self.0.get_mut(&position) {
667            if element.strength < strength {
668                element.strength = strength;
669                Some(element.original_value.clone())
670            } else {
671                None
672            }
673        } else {
674            let value = pixel_value()?;
675            let element = StrokeElement {
676                strength,
677                latest_value: value.clone(),
678                original_value: value.clone(),
679            };
680            self.0.insert(position, element);
681            Some(value)
682        }
683    }
684}
685
686/// Shape of a brush.
687#[derive(Copy, Clone, Reflect, Debug)]
688pub enum BrushShape {
689    /// Circle with given radius.
690    Circle {
691        /// Radius of the circle.
692        radius: f32,
693    },
694    /// Rectangle with given width and height.
695    Rectangle {
696        /// Width of the rectangle.
697        width: f32,
698        /// Length of the rectangle.
699        length: f32,
700    },
701}
702
703uuid_provider!(BrushShape = "a4dbfba0-077c-4658-9972-38384a8432f9");
704
705impl Default for BrushShape {
706    fn default() -> Self {
707        BrushShape::Circle { radius: 1.0 }
708    }
709}
710
711impl BrushShape {
712    /// Return true if the given point is within the shape when positioned at the given center point.
713    pub fn contains(&self, brush_center: Vector2<f32>, pixel_position: Vector2<f32>) -> bool {
714        match *self {
715            BrushShape::Circle { radius } => (brush_center - pixel_position).norm() < radius,
716            BrushShape::Rectangle { width, length } => Rect::new(
717                brush_center.x - width * 0.5,
718                brush_center.y - length * 0.5,
719                width,
720                length,
721            )
722            .contains(pixel_position),
723        }
724    }
725}
726
727/// Paint mode of a brush. It defines operation that will be performed on the terrain.
728#[derive(Clone, PartialEq, PartialOrd, Reflect, Debug)]
729pub enum BrushMode {
730    /// Raise or lower the value
731    Raise {
732        /// An offset to change the value by
733        amount: f32,
734    },
735    /// Flattens value of the terrain data
736    Flatten,
737    /// Assigns a particular value to anywhere the brush touches.
738    Assign {
739        /// Fixed value to paint into the data
740        value: f32,
741    },
742    /// Reduce sharp changes in the data.
743    Smooth {
744        /// Determines the size of each pixel's neighborhood in terms of
745        /// distance from the pixel.
746        /// 0 means no smoothing at all.
747        /// 1 means taking the mean of the 3x3 square of pixels surrounding each smoothed pixel.
748        /// 2 means using a 5x5 square of pixels. And so on.
749        kernel_radius: u32,
750    },
751}
752
753uuid_provider!(BrushMode = "48ad4cac-05f3-485a-b2a3-66812713841f");
754
755impl Default for BrushMode {
756    fn default() -> Self {
757        BrushMode::Raise { amount: 1.0 }
758    }
759}
760
761/// Paint target of a brush. It defines the data that the brush will operate on.
762#[derive(Copy, Default, Clone, Reflect, Debug, PartialEq, Eq)]
763pub enum BrushTarget {
764    #[default]
765    /// Modifies the height map
766    HeightMap,
767    /// Draws on a given layer
768    LayerMask {
769        /// The number of the layer to modify
770        layer: usize,
771    },
772    /// Modifies the terrain's holes
773    HoleMask,
774}
775
776uuid_provider!(BrushTarget = "461c1be7-189e-44ee-b8fd-00b8fdbc668f");
777
778/// Brush is used to modify terrain. It supports multiple shapes and modes.
779#[derive(Clone, Reflect, Debug)]
780pub struct Brush {
781    /// Shape of the brush.
782    pub shape: BrushShape,
783    /// Paint mode of the brush.
784    pub mode: BrushMode,
785    /// The data to modify with the brush
786    pub target: BrushTarget,
787    /// Transform that can modify the shape of the brush
788    pub transform: Matrix2<f32>,
789    /// The softness of the edges of the brush.
790    /// 0.0 means that the brush fades very gradually from opaque to transparent.
791    /// 1.0 means that the edges of the brush do not fade.
792    pub hardness: f32,
793    /// The transparency of the brush, allowing the values beneath the brushstroke to show through.
794    /// 0.0 means the brush is fully transparent and does not draw.
795    /// 1.0 means the brush is fully opaque.
796    pub alpha: f32,
797}
798
799impl Default for Brush {
800    fn default() -> Self {
801        Self {
802            transform: Matrix2::identity(),
803            hardness: 0.0,
804            alpha: 1.0,
805            shape: Default::default(),
806            mode: Default::default(),
807            target: Default::default(),
808        }
809    }
810}
811
812/// Verify that the brush operation is not so big that it could cause the editor to freeze.
813/// The user can type in any size of brush they please, even disastrous sizes, and
814/// this check prevents the editor from breaking.
815fn within_size_limit(bounds: &Rect<i32>) -> bool {
816    let size = bounds.size;
817    let area = size.x * size.y;
818    let accepted = area <= BRUSH_PIXEL_SANITY_LIMIT;
819    if !accepted {
820        Log::warn(format!(
821            "Terrain brush operation dropped due to sanity limit: {area}"
822        ))
823    }
824    accepted
825}
826
827impl Brush {
828    /// Send the pixels for this brush to the brush thread.
829    /// - `position`: The position of the brush in texture pixels.
830    /// - `scale`: The size of each pixel in local 2D space. This is used
831    /// to convert the brush's radius from local 2D to pixels.
832    /// - `value`: The brush's value. The meaning of this number depends on the brush.
833    /// - `draw_pixel`: The function that will draw the pixels to the terrain.
834    pub fn stamp<F>(&self, position: Vector2<f32>, scale: Vector2<f32>, mut draw_pixel: F)
835    where
836        F: FnMut(Vector2<i32>, f32),
837    {
838        let mut transform = self.transform;
839        let x_factor = scale.y / scale.x;
840        transform.m11 *= x_factor;
841        transform.m12 *= x_factor;
842        match self.shape {
843            BrushShape::Circle { radius } => {
844                let iter = StampPixels::new(
845                    CircleRaster(radius / scale.y),
846                    position,
847                    self.hardness,
848                    transform,
849                );
850                if !within_size_limit(&iter.bounds()) {
851                    return;
852                }
853                for BrushPixel { position, strength } in iter {
854                    draw_pixel(position, strength);
855                }
856            }
857            BrushShape::Rectangle { width, length } => {
858                let iter = StampPixels::new(
859                    RectRaster(width * 0.5 / scale.y, length * 0.5 / scale.y),
860                    position,
861                    self.hardness,
862                    transform,
863                );
864                if !within_size_limit(&iter.bounds()) {
865                    return;
866                }
867                for BrushPixel { position, strength } in iter {
868                    draw_pixel(position, strength);
869                }
870            }
871        }
872    }
873    /// Send the pixels for this brush to the brush thread.
874    /// - `start`: The position of the brush when it started the smear in texture pixels.
875    /// - `end`: The current position of the brush in texture pixels.
876    /// - `scale`: The size of each pixel in local 2D space. This is used
877    /// to convert the brush's radius from local 2D to pixels.
878    /// - `draw_pixel`: The function that will draw the pixels to the terrain.
879    pub fn smear<F>(
880        &self,
881        start: Vector2<f32>,
882        end: Vector2<f32>,
883        scale: Vector2<f32>,
884        mut draw_pixel: F,
885    ) where
886        F: FnMut(Vector2<i32>, f32),
887    {
888        let mut transform = self.transform;
889        let x_factor = scale.y / scale.x;
890        transform.m11 *= x_factor;
891        transform.m12 *= x_factor;
892        match self.shape {
893            BrushShape::Circle { radius } => {
894                let iter = SmearPixels::new(
895                    CircleRaster(radius / scale.y),
896                    start,
897                    end,
898                    self.hardness,
899                    transform,
900                );
901                if !within_size_limit(&iter.bounds()) {
902                    return;
903                }
904                for BrushPixel { position, strength } in iter {
905                    draw_pixel(position, strength);
906                }
907            }
908            BrushShape::Rectangle { width, length } => {
909                let iter = SmearPixels::new(
910                    RectRaster(width * 0.5 / scale.y, length * 0.5 / scale.y),
911                    start,
912                    end,
913                    self.hardness,
914                    transform,
915                );
916                if !within_size_limit(&iter.bounds()) {
917                    return;
918                }
919                for BrushPixel { position, strength } in iter {
920                    draw_pixel(position, strength);
921                }
922            }
923        }
924    }
925}
926
927/// A copy of a layer of data from a chunk.
928/// It can be height data or mask data, since the type is erased.
929/// The layer that this data represents must be remembered externally.
930pub struct ChunkData {
931    /// The grid position of the original chunk.
932    pub grid_position: Vector2<i32>,
933    /// The size of the original chunk, to confirm that the chunk's size has not changed since the data was copied.
934    pub size: Vector2<u32>,
935    /// The type-erased data from either the height or one of the layers of the chunk.
936    pub content: Box<[u8]>,
937}
938
939impl std::fmt::Debug for ChunkData {
940    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
941        f.debug_struct("ChunkData")
942            .field("grid_position", &self.grid_position)
943            .field("content", &format!("[..](len: {})", &self.content.len()))
944            .finish()
945    }
946}
947
948fn size_from_texture(texture: &ResourceDataRef<'_, Texture>) -> Vector2<u32> {
949    match texture.kind() {
950        crate::resource::texture::TextureKind::Rectangle { width, height } => {
951            Vector2::new(width, height)
952        }
953        _ => unreachable!("Terrain texture was not rectangle."),
954    }
955}
956
957impl ChunkData {
958    /// Extract the size from the given texture and return true if that size matches
959    /// the size required by this data. Log an error message and return false otherwise.
960    fn verify_texture_size(&self, texture: &ResourceDataRef<'_, Texture>) -> bool {
961        let size = size_from_texture(texture);
962        if size != self.size {
963            Log::err("Command swap failed due to texture size mismatch");
964            false
965        } else {
966            true
967        }
968    }
969    /// Create a ChunkData for the given texture at the given position.
970    pub fn from_texture(grid_position: Vector2<i32>, texture: &TextureResource) -> Self {
971        let data_ref = texture.data_ref();
972        let size = size_from_texture(&data_ref);
973        let data = Box::<[u8]>::from(data_ref.data());
974        Self {
975            grid_position,
976            size,
977            content: data,
978        }
979    }
980    /// Swap the content of this data with the content of the given chunk's height map.
981    pub fn swap_height(&mut self, chunk: &mut Chunk) {
982        let mut data_ref = chunk.heightmap().data_ref();
983        if !self.verify_texture_size(&data_ref) {
984            return;
985        }
986        let mut modify = data_ref.modify();
987        for (a, b) in modify.data_mut().iter_mut().zip(self.content.iter_mut()) {
988            std::mem::swap(a, b);
989        }
990    }
991    /// Swap the content of this data with the content of the given chunk's hole mask.
992    pub fn swap_holes(&mut self, chunk: &mut Chunk) {
993        let Some(mut data_ref) = chunk.hole_mask.as_ref().map(|t| t.data_ref()) else {
994            return;
995        };
996        if !self.verify_texture_size(&data_ref) {
997            return;
998        }
999        let mut modify = data_ref.modify();
1000        for (a, b) in modify.data_mut().iter_mut().zip(self.content.iter_mut()) {
1001            std::mem::swap(a, b);
1002        }
1003    }
1004    /// Swap the content of this data with the content of the given chunk's mask layer.
1005    pub fn swap_layer_mask(&mut self, chunk: &mut Chunk, layer: usize) {
1006        let mut data_ref = chunk.layer_masks[layer].data_ref();
1007        if !self.verify_texture_size(&data_ref) {
1008            return;
1009        }
1010        let mut modify = data_ref.modify();
1011        for (a, b) in modify.data_mut().iter_mut().zip(self.content.iter_mut()) {
1012            std::mem::swap(a, b);
1013        }
1014    }
1015    /// Swap the height data of the a chunk from the list with the height data in this object.
1016    /// The given list of chunks will be searched to find the chunk that matches `grid_position`.
1017    pub fn swap_height_from_list(&mut self, chunks: &mut [Chunk]) {
1018        for c in chunks {
1019            if c.grid_position == self.grid_position {
1020                self.swap_height(c);
1021                break;
1022            }
1023        }
1024    }
1025    /// Swap the hole data of the a chunk from the list with the hole data in this object.
1026    /// The given list of chunks will be searched to find the chunk that matches `grid_position`.
1027    pub fn swap_holes_from_list(&mut self, chunks: &mut [Chunk]) {
1028        for c in chunks {
1029            if c.grid_position == self.grid_position {
1030                self.swap_holes(c);
1031                break;
1032            }
1033        }
1034    }
1035    /// Swap the layer mask data of a particular layer of a chunk from the list with the data in this object.
1036    /// The given list of chunks will be searched to find the chunk that matches `grid_position`.
1037    pub fn swap_layer_mask_from_list(&mut self, chunks: &mut [Chunk], layer: usize) {
1038        for c in chunks {
1039            if c.grid_position == self.grid_position {
1040                self.swap_layer_mask(c, layer);
1041                break;
1042            }
1043        }
1044    }
1045}