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/// The index buffer used to determine the three vertices for each triangle in a mesh.
247/// A triangle is defined by three consequitive indices in the index buffer.
248/// Each index points to a position in the vertex buffers.
249///
250pub enum IndexBuffer {
251    /// No index buffer is used, ie. every triangle consist of three consequitive vertices.
252    None,
253    /// Use an index buffer with indices defined in `u8` format.
254    U8(ElementBuffer<u8>),
255    /// Use an index buffer with indices defined in `u16` format.
256    U16(ElementBuffer<u16>),
257    /// Use an index buffer with indices defined in `u32` format.
258    U32(ElementBuffer<u32>),
259}
260
261struct BaseMesh {
262    indices: IndexBuffer,
263    positions: VertexBuffer<Vec3>,
264    normals: Option<VertexBuffer<Vec3>>,
265    tangents: Option<VertexBuffer<Vec4>>,
266    uvs: Option<VertexBuffer<Vec2>>,
267    colors: Option<VertexBuffer<Vec4>>,
268}
269
270impl BaseMesh {
271    pub fn new(context: &Context, cpu_mesh: &CpuMesh) -> Self {
272        #[cfg(debug_assertions)]
273        cpu_mesh.validate().expect("invalid cpu mesh");
274
275        Self {
276            indices: match &cpu_mesh.indices {
277                Indices::U8(ind) => IndexBuffer::U8(ElementBuffer::new_with_data(context, ind)),
278                Indices::U16(ind) => IndexBuffer::U16(ElementBuffer::new_with_data(context, ind)),
279                Indices::U32(ind) => IndexBuffer::U32(ElementBuffer::new_with_data(context, ind)),
280                Indices::None => IndexBuffer::None,
281            },
282            positions: VertexBuffer::new_with_data(context, &cpu_mesh.positions.to_f32()),
283            normals: cpu_mesh
284                .normals
285                .as_ref()
286                .map(|data| VertexBuffer::new_with_data(context, data)),
287            tangents: cpu_mesh
288                .tangents
289                .as_ref()
290                .map(|data| VertexBuffer::new_with_data(context, data)),
291            uvs: cpu_mesh.uvs.as_ref().map(|data| {
292                VertexBuffer::new_with_data(
293                    context,
294                    &data
295                        .iter()
296                        .map(|uv| vec2(uv.x, 1.0 - uv.y))
297                        .collect::<Vec<_>>(),
298                )
299            }),
300            colors: cpu_mesh.colors.as_ref().map(|data| {
301                VertexBuffer::new_with_data(
302                    context,
303                    &data.iter().map(|c| c.to_linear_srgb()).collect::<Vec<_>>(),
304                )
305            }),
306        }
307    }
308
309    pub fn draw(&self, program: &Program, render_states: RenderStates, viewer: &dyn Viewer) {
310        self.use_attributes(program);
311
312        match &self.indices {
313            IndexBuffer::None => program.draw_arrays(
314                render_states,
315                viewer.viewport(),
316                self.positions.vertex_count(),
317            ),
318            IndexBuffer::U8(element_buffer) => {
319                program.draw_elements(render_states, viewer.viewport(), element_buffer)
320            }
321            IndexBuffer::U16(element_buffer) => {
322                program.draw_elements(render_states, viewer.viewport(), element_buffer)
323            }
324            IndexBuffer::U32(element_buffer) => {
325                program.draw_elements(render_states, viewer.viewport(), element_buffer)
326            }
327        }
328    }
329
330    pub fn draw_instanced(
331        &self,
332        program: &Program,
333        render_states: RenderStates,
334        viewer: &dyn Viewer,
335        instance_count: u32,
336    ) {
337        self.use_attributes(program);
338
339        match &self.indices {
340            IndexBuffer::None => program.draw_arrays_instanced(
341                render_states,
342                viewer.viewport(),
343                self.positions.vertex_count(),
344                instance_count,
345            ),
346            IndexBuffer::U8(element_buffer) => program.draw_elements_instanced(
347                render_states,
348                viewer.viewport(),
349                element_buffer,
350                instance_count,
351            ),
352            IndexBuffer::U16(element_buffer) => program.draw_elements_instanced(
353                render_states,
354                viewer.viewport(),
355                element_buffer,
356                instance_count,
357            ),
358            IndexBuffer::U32(element_buffer) => program.draw_elements_instanced(
359                render_states,
360                viewer.viewport(),
361                element_buffer,
362                instance_count,
363            ),
364        }
365    }
366
367    fn use_attributes(&self, program: &Program) {
368        program.use_vertex_attribute("position", &self.positions);
369
370        if program.requires_attribute("normal") {
371            if let Some(normals) = &self.normals {
372                program.use_vertex_attribute("normal", normals);
373            }
374        }
375
376        if program.requires_attribute("tangent") {
377            if let Some(tangents) = &self.tangents {
378                program.use_vertex_attribute("tangent", tangents);
379            }
380        }
381
382        if program.requires_attribute("uv_coordinates") {
383            if let Some(uvs) = &self.uvs {
384                program.use_vertex_attribute("uv_coordinates", uvs);
385            }
386        }
387
388        if program.requires_attribute("color") {
389            if let Some(colors) = &self.colors {
390                program.use_vertex_attribute("color", colors);
391            }
392        }
393    }
394
395    fn vertex_shader_source(&self) -> String {
396        format!(
397            "{}{}{}{}{}{}",
398            if self.normals.is_some() {
399                "#define USE_NORMALS\n"
400            } else {
401                ""
402            },
403            if self.tangents.is_some() {
404                "#define USE_TANGENTS\n"
405            } else {
406                ""
407            },
408            if self.uvs.is_some() {
409                "#define USE_UVS\n"
410            } else {
411                ""
412            },
413            if self.colors.is_some() {
414                "#define USE_VERTEX_COLORS\n"
415            } else {
416                ""
417            },
418            include_str!("../core/shared.frag"),
419            include_str!("geometry/shaders/mesh.vert"),
420        )
421    }
422}