1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
use crate::*;

/// Conversion to [ɴsɪ](https:://crates.io/crates/nsi).
impl Polyhedron {
    /// Sends the polyhedron to the specified
    /// [ɴsɪ](https:://crates.io/crates/nsi) context.
    /// # Arguments
    /// * `handle` – Handle of the node being created. If omitted, the name of
    ///   the polyhedron will be used as a handle.
    ///
    /// * `crease_hardness` - The hardness of edges (default: 10).
    ///
    /// * `corner_hardness` - The hardness of vertices (default: 0).
    ///
    /// * `smooth_corners` - Whether to keep corners smooth where more than two
    ///   edges meet. When set to `false` these automatically form a hard corner
    ///   with the same hardness as `crease_hardness`.
    #[cfg(feature = "nsi")]
    pub fn to_nsi(
        &self,
        ctx: &nsi::Context,
        handle: Option<&str>,
        crease_hardness: Option<f32>,
        corner_hardness: Option<f32>,
        smooth_corners: Option<bool>,
    ) -> String {
        let handle = handle.unwrap_or(self.name.as_str()).to_string();
        // Create a new mesh node.
        ctx.create(handle.clone(), nsi::NodeType::Mesh, &[]);

        // Flatten point vector.
        // Fast, unsafe version. May exploce on some platforms.
        // If so, use commented out code below instead.
        let positions = unsafe {
            std::slice::from_raw_parts(
                self.positions.as_ptr().cast::<Float>(),
                3 * self.positions_len(),
            )
        };

        /*
        let positions: Vec<f32> = self
            .positions
            .into_par_iter()
            .flat_map(|p3| once(p3.x as _).chain(once(p3.y as _)).chain(once(p3.z as _)))
            .collect();
        */

        ctx.set_attribute(
            handle.clone(),
            &[
                // Positions.
                nsi::points!("P", positions),
                // VertexKey into the position array.
                nsi::integers!(
                    "P.indices",
                    bytemuck::cast_slice(
                        &self
                            .face_index
                            .par_iter()
                            .flat_map(|face| face.clone())
                            .collect::<Vec<_>>()
                    )
                ),
                // Arity of each face.
                nsi::integers!(
                    "nvertices",
                    &self
                        .face_index
                        .par_iter()
                        .map(|face| face.len() as i32)
                        .collect::<Vec<_>>()
                ),
                // Render this as a C-C subdivison surface.
                nsi::string!("subdivision.scheme", "catmull-clark"),
                // This saves us from having to reverse the mesh ourselves.
                nsi::integer!("clockwisewinding", true as _),
            ],
        );

        // Default: semi sharp creases.
        let crease_hardness = crease_hardness.unwrap_or(10.);

        // Crease each of our edges a bit?
        if 0.0 != crease_hardness {
            let edges = self
                .to_edges()
                .into_iter()
                .flat_map(|edge| edge.to_vec())
                .collect::<Vec<_>>();
            ctx.set_attribute(
                handle.clone(),
                &[
                    nsi::integers!(
                        "subdivision.creasevertices",
                        bytemuck::cast_slice(&edges)
                    ),
                    nsi::floats!(
                        "subdivision.creasesharpness",
                        &vec![crease_hardness; edges.len() / 2]
                    ),
                ],
            );
        }

        match corner_hardness {
            Some(hardness) => {
                if 0.0 < hardness {
                    let corners = self
                        .positions
                        .par_iter()
                        .enumerate()
                        .map(|(i, _)| i as u32)
                        .collect::<Vec<_>>();
                    ctx.set_attribute(
                        handle.clone(),
                        &[
                            nsi::integers!(
                                "subdivision.cornervertices",
                                bytemuck::cast_slice(&corners)
                            ),
                            nsi::floats!(
                                "subdivision.cornersharpness",
                                &vec![hardness; corners.len()]
                            ),
                        ],
                    );
                }
            }

            // Have the renderer semi create sharp corners automagically.
            None => ctx.set_attribute(
                handle.clone(),
                &[
                    // Disabling below flag activates the specific
                    // deRose extensions for the C-C creasing
                    // algorithm that causes any vertex with where
                    // more then three creased edges meet to forma a
                    // corner.
                    // See fig. 8c/d in this paper:
                    // http://graphics.pixar.com/people/derose/publications/Geri/paper.pdf
                    nsi::integer!(
                        "subdivision.smoothcreasecorners",
                        smooth_corners.unwrap_or(false) as _
                    ),
                ],
            ),
        };

        handle
    }
}