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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
use crate::{
    c::{c_void, spMeshAttachment_updateRegion, spSkeletonClipping_clipTriangles},
    BlendMode, Color, Skeleton, SkeletonClipping,
};

use super::{ColorSpace, CullDirection};

#[allow(unused_imports)]
use crate::extension;

/// Renderables generated from [`SimpleDrawer::draw`].
#[derive(Clone)]
pub struct SimpleRenderable {
    /// The index of the slot in [`Skeleton`] that this renderable represents.
    pub slot_index: i32,
    /// A list of vertex attributes for a mesh.
    pub vertices: Vec<[f32; 2]>,
    /// A list of UV attributes for a mesh.
    pub uvs: Vec<[f32; 2]>,
    /// A list of indices for a mesh.
    pub indices: Vec<u16>,
    /// The color tint of the mesh.
    pub color: Color,
    /// The dark color tint of the mesh.
    /// See the [Spine User Guide](http://en.esotericsoftware.com/spine-slots#Tint-black).
    pub dark_color: Color,
    /// The blend mode to use when drawing this mesh.
    pub blend_mode: BlendMode,
    /// The attachment's renderer object as a raw pointer. Usually represents the texture created
    /// from [`extension::set_create_texture_cb`].
    pub attachment_renderer_object: Option<*const c_void>,
}

/// A simple drawer with no optimizations.
///
/// Assumes use of the default atlas attachment loader.
///
/// See [`SimpleDrawer::draw`]
pub struct SimpleDrawer {
    /// The cull direction to use for the vertices.
    pub cull_direction: CullDirection,
    /// Set to `true` if the textures are expected to have premultiplied alpha.
    pub premultiplied_alpha: bool,
    /// The color space to use for the colors returned in [`SimpleRenderable`].
    pub color_space: ColorSpace,
}

impl SimpleDrawer {
    /// This function returns a list of [`SimpleRenderable`] structs containing all the necessary
    /// data to create and render meshes. One renderable is created for each visible attachment on
    /// the skeleton. If a [`SkeletonClipping`] is provided, meshes will be properly clipped. The
    /// renderables are expected to be rendered in the order provided with the first renderable
    /// being drawn behind all the others.
    pub fn draw(
        &self,
        skeleton: &mut Skeleton,
        mut clipper: Option<&mut SkeletonClipping>,
    ) -> Vec<SimpleRenderable> {
        let mut renderables = vec![];
        let mut world_vertices = vec![];
        world_vertices.resize(1000, 0.);
        for slot_index in 0..skeleton.slots_count() {
            let slot = skeleton.draw_order_at_index(slot_index).unwrap();
            if !slot.bone().active() {
                if let Some(clipper) = clipper.as_deref_mut() {
                    clipper.clip_end(&slot);
                }
                continue;
            }

            let mut vertices = vec![];
            let mut indices = vec![];
            let mut uvs = vec![];
            let mut color;

            if let Some(mesh_attachment) = slot.attachment().and_then(|a| a.as_mesh()) {
                unsafe {
                    spMeshAttachment_updateRegion(mesh_attachment.c_ptr());
                };
                color = mesh_attachment.color();

                unsafe {
                    mesh_attachment.compute_world_vertices(
                        &slot,
                        0,
                        mesh_attachment.world_vertices_length(),
                        &mut world_vertices,
                        0,
                        2,
                    );
                }

                vertices.reserve(mesh_attachment.world_vertices_length() as usize);
                uvs.reserve(mesh_attachment.world_vertices_length() as usize);
                uvs.resize(mesh_attachment.world_vertices_length() as usize, [0., 0.]);
                for i in 0..mesh_attachment.world_vertices_length() {
                    vertices.push([
                        world_vertices[i as usize * 2],
                        world_vertices[i as usize * 2 + 1],
                    ]);
                }

                // UVs need to be copied from the indices. I'm not entirely sure why, but it can lead to crashes otherwise.
                macro_rules! copy_uvs {
                    ($i:ident) => {
                        let index = *mesh_attachment.triangles().offset($i);
                        uvs[index as usize] = [
                            *mesh_attachment.c_ptr_mut().uvs.offset(index as isize * 2),
                            *mesh_attachment
                                .c_ptr_mut()
                                .uvs
                                .offset(index as isize * 2 + 1),
                        ];
                        let index = *mesh_attachment.triangles().offset($i + 1);
                        uvs[index as usize] = [
                            *mesh_attachment.c_ptr_mut().uvs.offset(index as isize * 2),
                            *mesh_attachment
                                .c_ptr_mut()
                                .uvs
                                .offset(index as isize * 2 + 1),
                        ];
                        let index = *mesh_attachment.triangles().offset($i + 2);
                        uvs[index as usize] = [
                            *mesh_attachment.c_ptr_mut().uvs.offset(index as isize * 2),
                            *mesh_attachment
                                .c_ptr_mut()
                                .uvs
                                .offset(index as isize * 2 + 1),
                        ];
                    };
                }

                indices.reserve(mesh_attachment.triangles_count() as usize);
                if matches!(self.cull_direction, CullDirection::CounterClockwise) {
                    for i in (0..mesh_attachment.triangles_count() as isize).step_by(3) {
                        unsafe {
                            indices.push(*mesh_attachment.triangles().offset(i + 2));
                            indices.push(*mesh_attachment.triangles().offset(i + 1));
                            indices.push(*mesh_attachment.triangles().offset(i));
                            copy_uvs!(i);
                        }
                    }
                } else {
                    for i in (0..mesh_attachment.triangles_count() as isize).step_by(3) {
                        unsafe {
                            indices.push(*mesh_attachment.triangles().offset(i));
                            indices.push(*mesh_attachment.triangles().offset(i + 1));
                            indices.push(*mesh_attachment.triangles().offset(i + 2));
                            copy_uvs!(i);
                        }
                    }
                }
            } else if let Some(region_attachment) = slot.attachment().and_then(|a| a.as_region()) {
                color = region_attachment.color();

                let mut world_vertices = vec![];
                world_vertices.resize(1000, 0.);
                unsafe {
                    region_attachment.compute_world_vertices(&slot, &mut world_vertices, 0, 2);
                }

                vertices.reserve(4);
                uvs.reserve(4);
                for i in 0..4 {
                    vertices.push([
                        world_vertices[i as usize * 2],
                        world_vertices[i as usize * 2 + 1],
                    ]);

                    uvs.push([
                        region_attachment.uvs()[i as usize * 2],
                        region_attachment.uvs()[i as usize * 2 + 1],
                    ]);
                }

                indices.reserve(6);
                if matches!(self.cull_direction, CullDirection::CounterClockwise) {
                    indices.push(2);
                    indices.push(1);
                    indices.push(0);
                    indices.push(0);
                    indices.push(3);
                    indices.push(2);
                } else {
                    indices.push(0);
                    indices.push(1);
                    indices.push(2);
                    indices.push(2);
                    indices.push(3);
                    indices.push(0);
                }
            } else if let Some(clipping_attachment) =
                slot.attachment().and_then(|a| a.as_clipping())
            {
                if let Some(clipper) = clipper.as_deref_mut() {
                    clipper.clip_start(&slot, &clipping_attachment);
                }
                continue;
            } else {
                if let Some(clipper) = clipper.as_deref_mut() {
                    clipper.clip_end(&slot);
                }
                continue;
            }

            if let Some(clipper) = clipper.as_deref_mut() {
                if clipper.is_clipping() {
                    unsafe {
                        spSkeletonClipping_clipTriangles(
                            clipper.c_ptr(),
                            vertices.as_mut_ptr() as *mut f32,
                            vertices.len() as i32,
                            indices.as_mut_ptr(),
                            indices.len() as i32,
                            uvs.as_mut_ptr() as *mut f32,
                            2,
                        );
                    }
                    unsafe {
                        let clipped_vertices_size =
                            (*clipper.c_ptr_ref().clippedVertices).size as usize;
                        vertices.resize(clipped_vertices_size / 2, [0., 0.]);
                        std::ptr::copy_nonoverlapping(
                            (*clipper.c_ptr_ref().clippedVertices).items,
                            vertices.as_mut_ptr() as *mut f32,
                            clipped_vertices_size,
                        );
                        let clipped_triangles_size =
                            (*clipper.c_ptr_ref().clippedTriangles).size as usize;
                        indices.resize(clipped_triangles_size, 0);
                        std::ptr::copy_nonoverlapping(
                            (*clipper.c_ptr_ref().clippedTriangles).items,
                            indices.as_mut_ptr() as *mut u16,
                            clipped_triangles_size,
                        );
                        let clipped_uvs_size = (*clipper.c_ptr_ref().clippedUVs).size as usize;
                        uvs.resize(clipped_uvs_size / 2, [0., 0.]);
                        std::ptr::copy_nonoverlapping(
                            (*clipper.c_ptr_ref().clippedUVs).items,
                            uvs.as_mut_ptr() as *mut f32,
                            clipped_uvs_size,
                        );
                    }
                }
            }

            let attachment_renderer_object =
                if let Some(mesh_attachment) = slot.attachment().and_then(|a| a.as_mesh()) {
                    Some(unsafe {
                        mesh_attachment
                            .renderer_object()
                            .get_atlas_region()
                            .unwrap()
                            .page()
                            .c_ptr_ref()
                            .rendererObject as *const c_void
                    })
                } else {
                    slot.attachment()
                        .and_then(|a| a.as_region())
                        .map(|region_attachment| unsafe {
                            region_attachment
                                .renderer_object()
                                .get_atlas_region()
                                .unwrap()
                                .page()
                                .c_ptr_ref()
                                .rendererObject as *const c_void
                        })
                };

            color *= slot.color() * skeleton.color();
            if self.premultiplied_alpha {
                color.premultiply_alpha();
            }
            color = match self.color_space {
                ColorSpace::SRGB => color,
                ColorSpace::Linear => color.nonlinear_to_linear(),
            };

            let mut dark_color = slot
                .dark_color()
                .unwrap_or_else(|| Color::new_rgba(0.0, 0.0, 0.0, 0.0));
            if self.premultiplied_alpha {
                dark_color.a = 1.0;
                dark_color.premultiply_alpha();
            }
            dark_color = match self.color_space {
                ColorSpace::SRGB => dark_color,
                ColorSpace::Linear => dark_color.nonlinear_to_linear(),
            };

            renderables.push(SimpleRenderable {
                slot_index,
                vertices,
                uvs,
                indices,
                color,
                dark_color,
                blend_mode: slot.data().blend_mode(),
                attachment_renderer_object,
            });
            if let Some(clipper) = clipper.as_deref_mut() {
                clipper.clip_end(&slot);
            }
        }

        if let Some(clipper) = clipper {
            clipper.clip_end2();
        }
        renderables
    }
}