fyroxed_base 1.0.0

A scene editor for Fyrox game engine
Documentation
// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

use crate::command::CommandContext;
use crate::fyrox::core::parking_lot::RwLockWriteGuard;
use crate::fyrox::{
    core::{
        algebra::Vector3,
        math::{TriangleDefinition, TriangleEdge},
        pool::Handle,
    },
    scene::node::Node,
    utils::navmesh::Navmesh,
};
use crate::{
    command::CommandTrait,
    interaction::navmesh::selection::{NavmeshEntity, NavmeshSelection},
    scene::{commands::GameSceneContext, Selection},
};

#[derive(Debug)]
pub struct AddNavmeshEdgeCommand {
    navmesh_node: Handle<Node>,
    opposite_edge: TriangleEdge,
    state: AddNavmeshEdgeCommandState,
    select: bool,
    new_selection: Selection,
}

fn fetch_navmesh(ctx: &mut GameSceneContext, node: Handle<Node>) -> RwLockWriteGuard<Navmesh> {
    ctx.scene.graph[node]
        .as_navigational_mesh_mut()
        .navmesh_mut()
}

#[derive(Debug)]
enum AddNavmeshEdgeCommandState {
    Undefined,
    NonExecuted { edge: (Vector3<f32>, Vector3<f32>) },
    Executed,
    Reverted { edge: (Vector3<f32>, Vector3<f32>) },
}

impl AddNavmeshEdgeCommand {
    pub fn new(
        navmesh_node: Handle<Node>,
        edge: (Vector3<f32>, Vector3<f32>),
        opposite_edge: TriangleEdge,
        select: bool,
    ) -> Self {
        Self {
            navmesh_node,
            opposite_edge,
            state: AddNavmeshEdgeCommandState::NonExecuted { edge },
            select,
            new_selection: Default::default(),
        }
    }
}

impl CommandTrait for AddNavmeshEdgeCommand {
    fn name(&mut self, _context: &dyn CommandContext) -> String {
        "Add Navmesh Edge".to_owned()
    }

    fn execute(&mut self, context: &mut dyn CommandContext) {
        let context = context.get_mut::<GameSceneContext>();
        let mut navmesh = fetch_navmesh(context, self.navmesh_node);

        match std::mem::replace(&mut self.state, AddNavmeshEdgeCommandState::Undefined) {
            AddNavmeshEdgeCommandState::NonExecuted { edge }
            | AddNavmeshEdgeCommandState::Reverted { edge } => {
                let mut ctx = navmesh.modify();
                let begin = ctx.add_vertex(edge.0);
                let end = ctx.add_vertex(edge.1);
                ctx.add_triangle(TriangleDefinition([
                    self.opposite_edge.a,
                    begin,
                    self.opposite_edge.b,
                ]));
                ctx.add_triangle(TriangleDefinition([begin, end, self.opposite_edge.b]));
                self.state = AddNavmeshEdgeCommandState::Executed;
                let navmesh_selection = NavmeshSelection::new(
                    self.navmesh_node,
                    vec![NavmeshEntity::Edge(TriangleEdge { a: begin, b: end })],
                );

                self.new_selection = Selection::new(navmesh_selection);
            }
            _ => unreachable!(),
        }

        drop(navmesh);

        if self.select {
            std::mem::swap(context.selection, &mut self.new_selection);
        }
    }

    fn revert(&mut self, context: &mut dyn CommandContext) {
        let context = context.get_mut::<GameSceneContext>();
        if self.select {
            std::mem::swap(context.selection, &mut self.new_selection);
        }

        let mut navmesh = fetch_navmesh(context, self.navmesh_node);

        match std::mem::replace(&mut self.state, AddNavmeshEdgeCommandState::Undefined) {
            AddNavmeshEdgeCommandState::Executed => {
                let mut ctx = navmesh.modify();
                ctx.pop_triangle();
                ctx.pop_triangle();
                let va = ctx.pop_vertex().unwrap();
                let vb = ctx.pop_vertex().unwrap();
                self.state = AddNavmeshEdgeCommandState::Reverted { edge: (vb, va) };
            }
            _ => unreachable!(),
        }
    }
}

#[derive(Debug)]
pub enum ConnectNavmeshEdgesCommandState {
    Undefined,
    NonExecuted { edges: [TriangleEdge; 2] },
    Executed,
    Reverted { triangles: [TriangleDefinition; 2] },
}

#[derive(Debug)]
pub struct ConnectNavmeshEdgesCommand {
    navmesh_node: Handle<Node>,
    state: ConnectNavmeshEdgesCommandState,
}

impl ConnectNavmeshEdgesCommand {
    pub fn new(navmesh_node: Handle<Node>, edges: [TriangleEdge; 2]) -> Self {
        Self {
            navmesh_node,
            state: ConnectNavmeshEdgesCommandState::NonExecuted { edges },
        }
    }
}

impl CommandTrait for ConnectNavmeshEdgesCommand {
    fn name(&mut self, _context: &dyn CommandContext) -> String {
        "Connect Navmesh Edges".to_owned()
    }

    fn execute(&mut self, context: &mut dyn CommandContext) {
        let context = context.get_mut::<GameSceneContext>();
        let mut navmesh = fetch_navmesh(context, self.navmesh_node);
        let mut ctx = navmesh.modify();

        match std::mem::replace(&mut self.state, ConnectNavmeshEdgesCommandState::Undefined) {
            ConnectNavmeshEdgesCommandState::NonExecuted { edges } => {
                ctx.add_triangle(TriangleDefinition([edges[0].a, edges[0].b, edges[1].a]));
                ctx.add_triangle(TriangleDefinition([edges[1].a, edges[1].b, edges[0].a]));

                self.state = ConnectNavmeshEdgesCommandState::Executed;
            }
            ConnectNavmeshEdgesCommandState::Reverted { triangles } => {
                let [a, b] = triangles;
                ctx.add_triangle(a);
                ctx.add_triangle(b);
                self.state = ConnectNavmeshEdgesCommandState::Executed;
            }
            _ => unreachable!(),
        }
    }

    fn revert(&mut self, context: &mut dyn CommandContext) {
        let context = context.get_mut::<GameSceneContext>();
        let mut navmesh = fetch_navmesh(context, self.navmesh_node);
        let mut ctx = navmesh.modify();

        match std::mem::replace(&mut self.state, ConnectNavmeshEdgesCommandState::Undefined) {
            ConnectNavmeshEdgesCommandState::Executed => {
                self.state = ConnectNavmeshEdgesCommandState::Reverted {
                    triangles: [ctx.pop_triangle().unwrap(), ctx.pop_triangle().unwrap()],
                }
            }
            _ => unreachable!(),
        }
    }
}

#[derive(Debug)]
pub struct DeleteNavmeshVertexCommand {
    navmesh_node: Handle<Node>,
    state: DeleteNavmeshVertexCommandState,
}

#[derive(Debug)]
pub enum DeleteNavmeshVertexCommandState {
    Undefined,
    NonExecuted {
        vertex: usize,
    },
    Executed {
        vertex: Vector3<f32>,
        vertex_index: usize,
        triangles: Vec<TriangleDefinition>,
    },
    Reverted {
        vertex: usize,
    },
}

impl DeleteNavmeshVertexCommand {
    pub fn new(navmesh_node: Handle<Node>, vertex: usize) -> Self {
        Self {
            navmesh_node,
            state: DeleteNavmeshVertexCommandState::NonExecuted { vertex },
        }
    }
}

impl CommandTrait for DeleteNavmeshVertexCommand {
    fn name(&mut self, _context: &dyn CommandContext) -> String {
        "Delete Navmesh Vertex".to_owned()
    }

    fn execute(&mut self, context: &mut dyn CommandContext) {
        let context = context.get_mut::<GameSceneContext>();
        let mut navmesh = fetch_navmesh(context, self.navmesh_node);

        match std::mem::replace(&mut self.state, DeleteNavmeshVertexCommandState::Undefined) {
            DeleteNavmeshVertexCommandState::NonExecuted { vertex }
            | DeleteNavmeshVertexCommandState::Reverted { vertex } => {
                let mut triangles = Vec::new();

                for triangle in navmesh.triangles() {
                    if triangle.indices().contains(&(vertex as u32)) {
                        triangles.push(*triangle);
                    }
                }

                self.state = DeleteNavmeshVertexCommandState::Executed {
                    vertex: navmesh.modify().remove_vertex(vertex),
                    triangles,
                    vertex_index: vertex,
                };
            }
            _ => unreachable!(),
        }
    }

    fn revert(&mut self, context: &mut dyn CommandContext) {
        let context = context.get_mut::<GameSceneContext>();
        let mut navmesh = fetch_navmesh(context, self.navmesh_node);

        match std::mem::replace(&mut self.state, DeleteNavmeshVertexCommandState::Undefined) {
            DeleteNavmeshVertexCommandState::Executed {
                vertex,
                vertex_index,
                triangles,
            } => {
                let mut ctx = navmesh.modify();

                ctx.insert_vertex(vertex_index as u32, vertex);

                for triangle in triangles {
                    ctx.add_triangle(triangle);
                }

                self.state = DeleteNavmeshVertexCommandState::Reverted {
                    vertex: vertex_index,
                };
            }
            _ => unreachable!(),
        }
    }
}

#[derive(Debug)]
pub struct MoveNavmeshVertexCommand {
    navmesh_node: Handle<Node>,
    vertex: usize,
    old_position: Vector3<f32>,
    new_position: Vector3<f32>,
}

impl MoveNavmeshVertexCommand {
    pub fn new(
        navmesh_node: Handle<Node>,
        vertex: usize,
        old_position: Vector3<f32>,
        new_position: Vector3<f32>,
    ) -> Self {
        Self {
            navmesh_node,
            vertex,
            old_position,
            new_position,
        }
    }

    fn swap(&mut self) -> Vector3<f32> {
        let position = self.new_position;
        std::mem::swap(&mut self.new_position, &mut self.old_position);
        position
    }

    fn set_position(&self, mut navmesh: RwLockWriteGuard<Navmesh>, position: Vector3<f32>) {
        navmesh.modify().vertices_mut()[self.vertex] = position;
    }
}

impl CommandTrait for MoveNavmeshVertexCommand {
    fn name(&mut self, _context: &dyn CommandContext) -> String {
        "Move Navmesh Vertex".to_owned()
    }

    fn execute(&mut self, context: &mut dyn CommandContext) {
        let context = context.get_mut::<GameSceneContext>();
        let position = self.swap();
        self.set_position(fetch_navmesh(context, self.navmesh_node), position);
    }

    fn revert(&mut self, context: &mut dyn CommandContext) {
        let context = context.get_mut::<GameSceneContext>();
        let position = self.swap();
        self.set_position(fetch_navmesh(context, self.navmesh_node), position);
    }
}