Skip to main content

three_d/renderer/
geometry.rs

1#![macro_use]
2//!
3//! A collection of geometries implementing the [Geometry] trait.
4//!
5//! A geometry together with a [material] can be rendered directly, or combined into an [object] (see [Gm]) that can be used in a render call, for example [RenderTarget::render].
6//!
7
8macro_rules! impl_geometry_body {
9    ($inner:ident) => {
10        fn draw(&self, viewer: &dyn Viewer, program: &Program, render_states: RenderStates) {
11            self.$inner().draw(viewer, program, render_states)
12        }
13
14        fn vertex_shader_source(&self) -> String {
15            self.$inner().vertex_shader_source()
16        }
17
18        fn id(&self) -> GeometryId {
19            self.$inner().id()
20        }
21
22        fn render_with_material(
23            &self,
24            material: &dyn Material,
25            viewer: &dyn Viewer,
26            lights: &[&dyn Light],
27        ) {
28            self.$inner().render_with_material(material, viewer, lights)
29        }
30
31        fn render_with_effect(
32            &self,
33            material: &dyn Effect,
34            viewer: &dyn Viewer,
35            lights: &[&dyn Light],
36            color_texture: Option<ColorTexture>,
37            depth_texture: Option<DepthTexture>,
38        ) {
39            self.$inner()
40                .render_with_effect(material, viewer, lights, color_texture, depth_texture)
41        }
42
43        fn aabb(&self) -> AxisAlignedBoundingBox {
44            self.$inner().aabb()
45        }
46    };
47}
48
49mod mesh;
50#[doc(inline)]
51pub use mesh::*;
52
53mod instanced_mesh;
54#[doc(inline)]
55pub use instanced_mesh::*;
56
57mod sprites;
58#[doc(inline)]
59pub use sprites::*;
60
61mod particles;
62#[doc(inline)]
63pub use particles::*;
64
65mod bounding_box;
66#[doc(inline)]
67pub use bounding_box::*;
68
69mod line;
70#[doc(inline)]
71pub use line::*;
72
73mod rectangle;
74#[doc(inline)]
75pub use rectangle::*;
76
77mod circle;
78#[doc(inline)]
79pub use circle::*;
80
81use crate::core::*;
82use crate::renderer::*;
83
84pub use three_d_asset::{
85    Geometry as CpuGeometry, Indices, KeyFrameAnimation, KeyFrames, PointCloud, Positions,
86    TriMesh as CpuMesh,
87};
88
89///
90/// Represents a 3D geometry that, together with a [material], can be rendered using [Geometry::render_with_material].
91/// Alternatively, a geometry and a material can be combined in a [Gm],
92/// thereby creating an [Object] which can be used in a render call, for example [RenderTarget::render].
93///
94/// If requested by the material, the geometry has to support the following attributes in the vertex shader source code.
95/// - position: `out vec3 pos;` (must be in world space)
96/// - normal: `out vec3 nor;`
97/// - tangent: `out vec3 tang;`
98/// - bitangent: `out vec3 bitang;`
99/// - uv coordinates: `out vec2 uvs;` (must be flipped in v compared to standard uv coordinates, ie. do `uvs = vec2(uvs.x, 1.0 - uvs.y);` in the vertex shader or do the flip before constructing the uv coordinates vertex buffer)
100/// - color: `out vec4 col;`
101///
102/// In addition, for the geometry to be pickable using the [pick] or [ray_intersect] methods (ie. combined with the [IntersectionMaterial]),
103/// it needs to support `flat out int instance_id;`. Simply set it to the built-in glsl variable: `gl_InstanceID`.
104///
105pub trait Geometry {
106    ///
107    /// Draw this geometry.
108    ///
109    fn draw(&self, viewer: &dyn Viewer, program: &Program, render_states: RenderStates);
110
111    ///
112    /// Returns the vertex shader source for this geometry given that the fragment shader needs the given vertex attributes.
113    ///
114    fn vertex_shader_source(&self) -> String;
115
116    ///
117    /// Returns a unique ID for each variation of the shader source returned from `Geometry::vertex_shader_source`.
118    ///
119    /// **Note:** The last bit is reserved to internally implemented geometries, so if implementing the `Geometry` trait
120    /// outside of this crate, always return an id in the public use range as defined by [GeometryId].
121    ///
122    fn id(&self) -> GeometryId;
123
124    ///
125    /// Render the geometry with the given [Material].
126    /// Must be called in the callback given as input to a [RenderTarget], [ColorTarget] or [DepthTarget] write method.
127    /// Use an empty array for the `lights` argument, if the material does not require lights to be rendered.
128    ///
129    fn render_with_material(
130        &self,
131        material: &dyn Material,
132        viewer: &dyn Viewer,
133        lights: &[&dyn Light],
134    );
135
136    ///
137    /// Render the geometry with the given [Effect].
138    /// Must be called in the callback given as input to a [RenderTarget], [ColorTarget] or [DepthTarget] write method.
139    /// Use an empty array for the `lights` argument, if the material does not require lights to be rendered.
140    ///
141    fn render_with_effect(
142        &self,
143        material: &dyn Effect,
144        viewer: &dyn Viewer,
145        lights: &[&dyn Light],
146        color_texture: Option<ColorTexture>,
147        depth_texture: Option<DepthTexture>,
148    );
149
150    ///
151    /// Returns the [AxisAlignedBoundingBox] for this geometry in the global coordinate system.
152    ///
153    fn aabb(&self) -> AxisAlignedBoundingBox;
154
155    ///
156    /// For updating the animation of this geometry if it is animated, if not, this method does nothing.
157    /// The time parameter should be some continious time, for example the time since start.
158    ///
159    fn animate(&mut self, _time: f32) {}
160}
161
162use std::ops::Deref;
163impl<T: Geometry + ?Sized> Geometry for &T {
164    impl_geometry_body!(deref);
165}
166
167impl<T: Geometry + ?Sized> Geometry for &mut T {
168    impl_geometry_body!(deref);
169
170    fn animate(&mut self, time: f32) {
171        self.deref().animate(time)
172    }
173}
174
175impl<T: Geometry> Geometry for Box<T> {
176    impl_geometry_body!(as_ref);
177}
178
179impl<T: Geometry> Geometry for std::rc::Rc<T> {
180    impl_geometry_body!(as_ref);
181}
182
183impl<T: Geometry> Geometry for std::sync::Arc<T> {
184    impl_geometry_body!(as_ref);
185}
186
187impl<T: Geometry> Geometry for std::cell::RefCell<T> {
188    impl_geometry_body!(borrow);
189
190    fn animate(&mut self, time: f32) {
191        self.borrow_mut().animate(time)
192    }
193}
194
195impl<T: Geometry> Geometry for std::sync::RwLock<T> {
196    fn draw(&self, viewer: &dyn Viewer, program: &Program, render_states: RenderStates) {
197        self.read().unwrap().draw(viewer, program, render_states)
198    }
199
200    fn vertex_shader_source(&self) -> String {
201        self.read().unwrap().vertex_shader_source()
202    }
203
204    fn id(&self) -> GeometryId {
205        self.read().unwrap().id()
206    }
207
208    fn render_with_material(
209        &self,
210        material: &dyn Material,
211        viewer: &dyn Viewer,
212        lights: &[&dyn Light],
213    ) {
214        self.read()
215            .unwrap()
216            .render_with_material(material, viewer, lights)
217    }
218
219    fn render_with_effect(
220        &self,
221        material: &dyn Effect,
222        viewer: &dyn Viewer,
223        lights: &[&dyn Light],
224        color_texture: Option<ColorTexture>,
225        depth_texture: Option<DepthTexture>,
226    ) {
227        self.read().unwrap().render_with_effect(
228            material,
229            viewer,
230            lights,
231            color_texture,
232            depth_texture,
233        )
234    }
235
236    fn aabb(&self) -> AxisAlignedBoundingBox {
237        self.read().unwrap().aabb()
238    }
239
240    fn animate(&mut self, time: f32) {
241        self.write().unwrap().animate(time)
242    }
243}
244
245///
246/// Buffer that defines the three vertices for each triangle in a mesh.
247///
248pub enum TriangleBuffer {
249    /// Each triangle is defined by three consequitive elements in the [VertexBuffer]s.
250    /// All elements in the [VertexBuffer]s are used exactly once.
251    Unindexed {
252        /// The number of vertices to draw
253        number_of_vertices: u32,
254    },
255    /// Use an index buffer, with indices defined in `u8` format, to index into the [VertexBuffer]s.
256    /// Each triangle is defined by three consequitive indices in the [ElementBuffer].
257    /// Elements in the [VertexBuffer]s can be used multiple times.
258    IndexedU8(ElementBuffer<u8>),
259    /// Use an index buffer, with indices defined in `u16` format, to index into the [VertexBuffer]s.
260    /// Each triangle is defined by three consequitive indices in the [ElementBuffer].
261    /// Elements in the [VertexBuffer]s can be used multiple times.
262    IndexedU16(ElementBuffer<u16>),
263    /// Use an index buffer, with indices defined in `u32` format, to index into the [VertexBuffer]s.
264    /// Each triangle is defined by three consequitive indices in the [ElementBuffer].
265    /// Elements in the [VertexBuffer]s can be used multiple times.
266    IndexedU32(ElementBuffer<u32>),
267}
268
269impl TriangleBuffer {
270    /// Create a new triangle buffer from a [CpuMesh].
271    pub fn new(context: &Context, cpu_mesh: &CpuMesh) -> Self {
272        match &cpu_mesh.indices {
273            Indices::U8(ind) => Self::IndexedU8(ElementBuffer::new_with_data(context, ind)),
274            Indices::U16(ind) => Self::IndexedU16(ElementBuffer::new_with_data(context, ind)),
275            Indices::U32(ind) => Self::IndexedU32(ElementBuffer::new_with_data(context, ind)),
276            Indices::None => Self::Unindexed {
277                number_of_vertices: cpu_mesh.vertex_count() as u32,
278            },
279        }
280    }
281
282    /// Draw the triangles defined by this buffer.
283    pub fn draw(&self, program: &Program, render_states: RenderStates, viewer: &dyn Viewer) {
284        match self {
285            Self::Unindexed { number_of_vertices } => {
286                program.draw_arrays(render_states, viewer.viewport(), *number_of_vertices)
287            }
288            Self::IndexedU8(element_buffer) => {
289                program.draw_elements(render_states, viewer.viewport(), element_buffer)
290            }
291            Self::IndexedU16(element_buffer) => {
292                program.draw_elements(render_states, viewer.viewport(), element_buffer)
293            }
294            Self::IndexedU32(element_buffer) => {
295                program.draw_elements(render_states, viewer.viewport(), element_buffer)
296            }
297        }
298    }
299
300    /// Draw multiple instances of the triangles defined by this buffer.
301    pub fn draw_instanced(
302        &self,
303        program: &Program,
304        render_states: RenderStates,
305        viewer: &dyn Viewer,
306        instance_count: u32,
307    ) {
308        match self {
309            Self::Unindexed { number_of_vertices } => program.draw_arrays_instanced(
310                render_states,
311                viewer.viewport(),
312                *number_of_vertices,
313                instance_count,
314            ),
315            Self::IndexedU8(element_buffer) => program.draw_elements_instanced(
316                render_states,
317                viewer.viewport(),
318                element_buffer,
319                instance_count,
320            ),
321            Self::IndexedU16(element_buffer) => program.draw_elements_instanced(
322                render_states,
323                viewer.viewport(),
324                element_buffer,
325                instance_count,
326            ),
327            Self::IndexedU32(element_buffer) => program.draw_elements_instanced(
328                render_states,
329                viewer.viewport(),
330                element_buffer,
331                instance_count,
332            ),
333        }
334    }
335
336    /// Returns the number of vertices defined by this buffer.
337    pub fn vertex_count(&self) -> u32 {
338        match self {
339            Self::Unindexed { number_of_vertices } => *number_of_vertices,
340            Self::IndexedU8(element_buffer) => element_buffer.count(),
341            Self::IndexedU16(element_buffer) => element_buffer.count(),
342            Self::IndexedU32(element_buffer) => element_buffer.count(),
343        }
344    }
345
346    /// Returns the number of triangles defined by this buffer.
347    pub fn triangle_count(&self) -> u32 {
348        self.vertex_count() / 3
349    }
350}
351
352struct BaseMesh {
353    indices: TriangleBuffer,
354    positions: VertexBuffer<Vec3>,
355    normals: Option<VertexBuffer<Vec3>>,
356    tangents: Option<VertexBuffer<Vec4>>,
357    uvs: Option<VertexBuffer<Vec2>>,
358    colors: Option<VertexBuffer<Vec4>>,
359}
360
361impl BaseMesh {
362    pub fn new(context: &Context, cpu_mesh: &CpuMesh) -> Self {
363        #[cfg(debug_assertions)]
364        cpu_mesh.validate().expect("invalid cpu mesh");
365
366        Self {
367            indices: TriangleBuffer::new(context, cpu_mesh),
368            positions: VertexBuffer::new_with_data(context, &cpu_mesh.positions.to_f32()),
369            normals: cpu_mesh
370                .normals
371                .as_ref()
372                .map(|data| VertexBuffer::new_with_data(context, data)),
373            tangents: cpu_mesh
374                .tangents
375                .as_ref()
376                .map(|data| VertexBuffer::new_with_data(context, data)),
377            uvs: cpu_mesh.uvs.as_ref().map(|data| {
378                VertexBuffer::new_with_data(
379                    context,
380                    &data
381                        .iter()
382                        .map(|uv| vec2(uv.x, 1.0 - uv.y))
383                        .collect::<Vec<_>>(),
384                )
385            }),
386            colors: cpu_mesh.colors.as_ref().map(|data| {
387                VertexBuffer::new_with_data(
388                    context,
389                    &data.iter().map(|c| c.to_linear_srgb()).collect::<Vec<_>>(),
390                )
391            }),
392        }
393    }
394
395    pub fn draw(&self, program: &Program, render_states: RenderStates, viewer: &dyn Viewer) {
396        self.use_attributes(program);
397        self.indices.draw(program, render_states, viewer);
398    }
399
400    pub fn draw_instanced(
401        &self,
402        program: &Program,
403        render_states: RenderStates,
404        viewer: &dyn Viewer,
405        instance_count: u32,
406    ) {
407        self.use_attributes(program);
408        self.indices
409            .draw_instanced(program, render_states, viewer, instance_count);
410    }
411
412    fn use_attributes(&self, program: &Program) {
413        program.use_vertex_attribute("position", &self.positions);
414
415        if program.requires_attribute("normal") {
416            if let Some(normals) = &self.normals {
417                program.use_vertex_attribute("normal", normals);
418            }
419        }
420
421        if program.requires_attribute("tangent") {
422            if let Some(tangents) = &self.tangents {
423                program.use_vertex_attribute("tangent", tangents);
424            }
425        }
426
427        if program.requires_attribute("uv_coordinates") {
428            if let Some(uvs) = &self.uvs {
429                program.use_vertex_attribute("uv_coordinates", uvs);
430            }
431        }
432
433        if program.requires_attribute("color") {
434            if let Some(colors) = &self.colors {
435                program.use_vertex_attribute("color", colors);
436            }
437        }
438    }
439
440    fn vertex_shader_source(&self) -> String {
441        format!(
442            "{}{}{}{}{}{}",
443            if self.normals.is_some() {
444                "#define USE_NORMALS\n"
445            } else {
446                ""
447            },
448            if self.tangents.is_some() {
449                "#define USE_TANGENTS\n"
450            } else {
451                ""
452            },
453            if self.uvs.is_some() {
454                "#define USE_UVS\n"
455            } else {
456                ""
457            },
458            if self.colors.is_some() {
459                "#define USE_VERTEX_COLORS\n"
460            } else {
461                ""
462            },
463            include_str!("../core/shared.frag"),
464            include_str!("geometry/shaders/mesh.vert"),
465        )
466    }
467}