three/template.rs
1//! Utilites for creating reusable templates for scene objects.
2//!
3//! It is often the case that you will want to have multiple instances of the same model or
4//! hierarchy of objects in your scene. While you could manually construct each instance yourself,
5//! three-rs provides a templating system to allow you to describe your model's hierarchy ahead
6//! of time, and then quickly create instances that three can efficiently batch render.
7//! [`Template`] describes the objects for a single model, and can be instantiated with
8//! [`Factory::instantiate_template`].
9//!
10//! The easiest way to create a template is to load one from a glTF file using
11//! [`Factory::load_gltf`].
12//!
13//! # Object Relations
14//!
15//! Often, one object needs to reference another object in the template, e.g. a bone needs
16//! to specify which skeleton it belongs to, and any object can specify that it belongs to
17//! a group in the template. When doing so, objects reference each other by their index in
18//! their respective arrays in [`Template`]. When such indices are used, the documentation
19//! will specify which array the index refers to.
20//!
21//! # Object Templates
22//!
23//! The [`objects`] field of [`Template`] provides a flattened, type-erased list of all objects
24//! defined in the template. Each type of object provides its type-specific data in that type's
25//! array, and then specifies the index of an [`ObjectTemplate`] in [`objects`]. Every object
26//! in the template must be represented in [`objects`] exactly once.
27//!
28//! The full, flattened list of objects is primarily used by [`AnimationTemplate`] to allow
29//! tracks in the animation to reference the object targeted by the track regardless of the
30//! target object's concrete type.
31//!
32//! # Animations
33//!
34//! Templates can also describe animations that apply to the objects in a template.
35//! When instantiated, the resulting animation clips will be unique to that instance of of the
36//! template. This allows for instances of the template to be animated independently of each
37//! other, without requiring you to manually setup animations for each instance.
38//!
39//! An animation in a template can target any of the objects described in the template. It does
40//! this by specifying the index of the objects in [`objects`]. See
41//! [`AnimationTemplate::tracks`] for more information.
42//!
43//! # Mesh Instancing
44//!
45//! When setting up a mesh in a template, you must first upload your [`Geometry`] to the GPU
46//! using [`Factory::upload_geometry`]. This will give you an [`InstancedGeometry`] object
47//! that acts as a shared handle to the GPU resources for that geometry. By uploading the
48//! data to the GPU ahead of time, we can ensure that all mesh nodes that reference that
49//! geometry, and all [`Mesh`] instances created from the template, will share a single copy
50//! of the data on the GPU. This reduces GPU resource usage and, for any meshes that also share
51//! a material, allows three to render many objects at once.
52//!
53//! [`Factory::instantiate_template`]: ../struct.Factory.html#method.instantiate_template
54//! [`Factory::load_gltf`]: ../struct.Factory.html#method.load_gltf
55//! [`Factory::upload_geometry`]: ../struct.Factory.html#method.upload_geometry
56//! [`Object`]: ../trait.Object.html
57//! [`Group`]: ../struct.Group.html
58//! [`Geometry`]: ../struct.Geometry.html
59//! [`Mesh`]: ../struct.Mesh.html
60//! [`Template`]: ./struct.Template.html
61//! [`ObjectTemplate`]: ./struct.ObjectTemplate.html
62//! [`AnimationTemplate`]: ./struct.AnimationTemplate.html
63//! [`AnimationTemplate::tracks`]: ./struct.AnimationTemplate.html#structfield.tracks
64//! [`nodes`]: ./struct.Template.html#structfield.nodes
65//! [`cameras`]: ./struct.Template.html#structfield.cameras
66//! [`meshes`]: ./struct.Template.html#structfield.meshes
67//! [`roots`]: ./struct.Template.html#structfield.roots
68//! [`objects`]: ./struct.Template.html#structfield.objects
69//! [`InstancedGeometry`]: ./struct.InstancedGeometry.html
70
71use animation::Track;
72use camera::Projection;
73use color::Color;
74use material::Material;
75use node::Transform;
76use render::GpuData;
77use skeleton::InverseBindMatrix;
78
79/// A template representing a hierarchy of objects.
80///
81/// To create an instance of the template that can be added to your scene, use
82/// [`Factory::instantiate_template`]. For more information about the templating system and how
83/// to use it, see the [module documentation].
84///
85/// [`Factory::instantiate_template`]: ../struct.Factory.html#method.instantiate_template
86/// [module documentation]: ./index.html
87#[derive(Debug, Clone, Default)]
88pub struct Template {
89 /// An optional name for the template.
90 pub name: Option<String>,
91
92 /// The base object data for all objects defined in the template.
93 ///
94 /// The index into this array is used to uniquely identify each object in the template. Each
95 /// object, regardless of its concrete type, will be represented in this array exactly once.
96 /// These indices are primarily used in [`AnimationTemplate`] to define the target of each
97 /// track of the animation.
98 ///
99 /// [`AnimationTemplate`]: ./struct.AnimationTemplate.html
100 pub objects: Vec<ObjectTemplate>,
101
102 /// Definitions for all [`Group`] objects in the template, given as indices into [`objects`].
103 ///
104 /// Groups carry no data beyond the common object data, so groups are defined soley by their
105 /// [`ObjectTemplate`].
106 ///
107 /// [`objects`]: #structfield.objects
108 /// [`Group`]: ../struct.Group.html
109 /// [`ObjectTemplate`]: ./struct.ObjectTemplate.html
110 pub groups: Vec<usize>,
111
112 /// Projection data used by cameras defined in the template.
113 pub cameras: Vec<CameraTemplate>,
114
115 /// The meshes defined in this template.
116 pub meshes: Vec<MeshTemplate>,
117
118 /// Data for the lights described by this template.
119 pub lights: Vec<LightTemplate>,
120
121 /// Data for the bones described by this template.
122 pub bones: Vec<BoneTemplate>,
123
124 /// Definitions for all [`Skeleton`] objects in the template, given as indices into
125 /// [`objects`].
126 ///
127 /// Skeletons carry no data beyond the common object data, so groups are defined soley by
128 /// their [`ObjectTemplate`].
129 ///
130 /// [`objects`]: #structfield.objects
131 /// [`Skeleton`]: ../skeleton/struct.Skeleton.html
132 /// [`ObjectTemplate`]: ./struct.ObjectTemplate.html
133 pub skeletons: Vec<usize>,
134
135 /// Templates for animation clips that target objects instantiated from this template.
136 pub animations: Vec<AnimationTemplate>,
137}
138
139impl Template {
140 /// Creates an empty template.
141 ///
142 /// # Examples
143 ///
144 /// Create an empty template and then instantiate it, effectively the most verbose way to
145 /// call [`Factory::group`]:
146 ///
147 /// ```no_run
148 /// use three::template::Template;
149 ///
150 /// # let mut window = three::Window::new("Three-rs");
151 /// let template = Template::new();
152 /// let (group, animations) = window.factory.instantiate_template(&template);
153 /// ```
154 ///
155 /// [`Factory::group`]: ../struct.Factory.html#method.group
156 pub fn new() -> Template { Default::default() }
157}
158
159/// Common data used by all object types.
160///
161/// All objects (i.e. three-rs types that implement the [`Object`] trait) have common data
162/// that the user can set at runtime. `ObjectTemplate` encapsultes these fields, and the
163/// various template types have a way to reference an `ObjectTemplate` to specify the object
164/// data for that template.
165///
166/// See the [module documentation] for more information on how object data is defined in
167/// templates.
168///
169/// [`Object`]: ../trait.Object.html
170/// [module documentation]: ./index.html#object-templates
171#[derive(Debug, Clone, Default)]
172pub struct ObjectTemplate {
173 /// An optional name for the object.
174 pub name: Option<String>,
175
176 /// The parent [`Group`] of the object, given as an index into the [`groups`] array of the
177 /// parent [`Template`].
178 ///
179 /// If `parent` is `None`, then the object is added to the root [`Group`] returned from
180 /// [`Factory::instantiate_template`].
181 ///
182 /// [`Group`]: ../struct.Group.html
183 /// [`Template`]: ./struct.Template.html
184 pub parent: Option<usize>,
185
186 /// The local transform for the object.
187 pub transform: Transform,
188}
189
190impl ObjectTemplate {
191 /// Creates a new `ObjectTemplate` with default values.
192 ///
193 /// The new object template will have no name, no parent (i.e. it will be treated as a root
194 /// object of the template), and a default transform.
195 ///
196 /// # Examples
197 ///
198 /// ```
199 /// use three::template::{ObjectTemplate, Template};
200 ///
201 /// let mut template = Template::new();
202 ///
203 /// let mut object = ObjectTemplate::new();
204 /// object.name = Some("My Node".into());
205 /// object.transform.position = [1.0, 2.0, 3.0].into();
206 ///
207 /// template.objects.push(object);
208 /// ```
209 pub fn new() -> ObjectTemplate {
210 Default::default()
211 }
212}
213
214/// Information for instantiating a [`Mesh`].
215///
216/// See the [module documentation] for more information on mesh instancing and how mesh
217/// data is setup for templates.
218///
219/// [`Mesh`]: ../struct.Mesh.html
220/// [module documentation]: ./index.html#mesh-instancing
221#[derive(Debug, Clone)]
222pub struct MeshTemplate {
223 /// The object data for the mesh, given as an index in the [`objects`] array of the parent
224 /// [`Template`].
225 ///
226 /// [`Template`]: ./struct.Template.html
227 /// [`objects`]: ./struct.Template.html#structfield.objects
228 pub object: usize,
229
230 /// The geometry used in the mesh.
231 pub geometry: InstancedGeometry,
232
233 /// The index of the material for the mesh in the [`meshes`] array of the parent [`Template`].
234 ///
235 /// [`Template`]: ./struct.Template.html
236 /// [`meshes`]: ./struct.Template.html#structfield.meshes
237 pub material: Material,
238
239 /// The skeleton used to render the mesh, if it's a skinned mesh.
240 pub skeleton: Option<usize>,
241}
242
243/// A template for a [`Camera`] object.
244///
245/// [`Camera`]: ../struct.Camera.html
246#[derive(Debug, Clone)]
247pub struct CameraTemplate {
248 /// The object data for the camera, given as an index in the [`objects`] array of the parent
249 /// [`Template`].
250 ///
251 /// [`Template`]: ./struct.Template.html
252 /// [`objects`]: ./struct.Template.html#structfield.objects
253 pub object: usize,
254
255 /// The projection used by the camera.
256 pub projection: Projection,
257}
258
259/// A template for a [`Bone`] object.
260///
261/// For more information about creating a [`Bone`], see [`Factory::bone`].
262///
263/// [`Bone`]: ../skeleton/struct.Bone.html
264/// [`Factory::bone`]: ../struct.Factory.html#method.bone
265#[derive(Debug, Clone)]
266pub struct BoneTemplate {
267 /// The object data for the bone, given as an index in the [`objects`] array of the parent
268 /// [`Template`].
269 ///
270 /// [`Template`]: ./struct.Template.html
271 /// [`objects`]: ./struct.Template.html#structfield.objects
272 pub object: usize,
273
274 /// The index of the bone within its skeleton.
275 pub index: usize,
276
277 /// The inverse bind matrix used to bind vertices of the mesh to the bone.
278 pub inverse_bind_matrix: InverseBindMatrix,
279
280 /// The skeleton that this bone is a part of, given as an index into the [`skeletons`]
281 /// array of the parent [`Template`].
282 ///
283 /// [`Template`]: ./struct.Template.html
284 /// [`skeletons`]: ./struct.Template.html#structfield.skeletons
285 pub skeleton: usize,
286}
287
288/// The definition for an animation targeting objects in a [`Template`].
289///
290/// See the [module documentation] for more information on template animations and how they
291/// are used.
292///
293/// [`Template`]: ./struct.Template.html
294/// [module documentation]: ./index.html#animations
295#[derive(Debug, Clone)]
296pub struct AnimationTemplate {
297 /// An optional name for the animation.
298 pub name: Option<String>,
299
300 /// The tracks making up the animation.
301 ///
302 /// Each track is composed of a [`Track`], containing the data for the track, and the node
303 /// that the track targetes, specified as an index into the [`objects`] array of the
304 /// parent [`Template`].
305 ///
306 /// [`Track`]: ../animation/struct.Track.html
307 /// [`Template`]: ./struct.Template.html
308 /// [`objects`]: ./struct.Template.html#structfield.nodes
309 pub tracks: Vec<(Track, usize)>,
310}
311
312/// Common information for instantiating the various types of lights.
313///
314/// See the [module documentation] for information on how templates are setup and how objects
315/// are added to the template.
316///
317/// [module documentation]: ./index.html
318#[derive(Clone, Copy, Debug)]
319pub struct LightTemplate {
320 /// The object data for the light, given as an index into the [`objects`] array of the parent
321 /// [`Template`].
322 ///
323 /// [`Template`]: ./struct.Template.html
324 /// [`objects`]: ./struct.Template.html#structfield.objects
325 pub object: usize,
326
327 /// The base color of the light.
328 pub color: Color,
329
330 /// The intensity of the light.
331 pub intensity: f32,
332
333 /// The specific type of light represented by the template.
334 pub sub_light: SubLightTemplate,
335}
336
337impl LightTemplate {
338 /// Creates a new template for an ambient light, analogous to [`Factory::ambient_light`].
339 ///
340 /// # Examples
341 ///
342 /// ```
343 /// use three::template::{LightTemplate, ObjectTemplate, Template};
344 ///
345 /// let mut template = Template::new();
346 /// template.objects.push(ObjectTemplate::new());
347 /// let light = LightTemplate::ambient(
348 /// template.objects.len() - 1,
349 /// three::color::RED,
350 /// 0.5,
351 /// );
352 /// template.lights.push(light);
353 /// ```
354 ///
355 /// [`Factory::ambient_light`]: ../struct.Factory.html#method.ambient_light
356 pub fn ambient(object: usize, color: Color, intensity: f32) -> LightTemplate {
357 LightTemplate {
358 object,
359
360 color,
361 intensity,
362 sub_light: SubLightTemplate::Ambient,
363 }
364 }
365
366 /// Creates a new template for a directional light, analogous to [`Factory::directional_light`].
367 ///
368 /// # Examples
369 ///
370 /// ```
371 /// use three::template::{LightTemplate, ObjectTemplate, Template};
372 ///
373 /// let mut template = Template::new();
374 /// template.objects.push(ObjectTemplate::new());
375 /// let light = LightTemplate::directional(
376 /// template.objects.len() - 1,
377 /// three::color::RED,
378 /// 0.5,
379 /// );
380 /// template.lights.push(light);
381 /// ```
382 ///
383 /// [`Factory::directional_light`]: ../struct.Factory.html#method.directional_light
384 pub fn directional(object: usize, color: Color, intensity: f32) -> LightTemplate {
385 LightTemplate {
386 object,
387
388 color,
389 intensity,
390 sub_light: SubLightTemplate::Directional,
391 }
392 }
393
394 /// Creates a new template for a point light, analogous to [`Factory::point_light`].
395 ///
396 /// # Examples
397 ///
398 /// ```
399 /// use three::template::{LightTemplate, ObjectTemplate, Template};
400 ///
401 /// let mut template = Template::new();
402 /// template.objects.push(ObjectTemplate::new());
403 /// let light = LightTemplate::point(
404 /// template.objects.len() - 1,
405 /// three::color::RED,
406 /// 0.5,
407 /// );
408 /// template.lights.push(light);
409 /// ```
410 ///
411 /// [`Factory::point_light`]: ../struct.Factory.html#method.point_light
412 pub fn point(object: usize, color: Color, intensity: f32) -> LightTemplate {
413 LightTemplate {
414 object,
415
416 color,
417 intensity,
418 sub_light: SubLightTemplate::Point,
419 }
420 }
421
422 /// Creates a new template for a hemisphere light, analogous to [`Factory::hemisphere_light`].
423 ///
424 /// # Examples
425 ///
426 /// ```
427 /// use three::template::{LightTemplate, ObjectTemplate, Template};
428 ///
429 /// let mut template = Template::new();
430 /// template.objects.push(ObjectTemplate::new());
431 /// let light = LightTemplate::hemisphere(
432 /// template.objects.len() - 1,
433 /// three::color::RED,
434 /// three::color::BLUE,
435 /// 0.5,
436 /// );
437 /// template.lights.push(light);
438 /// ```
439 ///
440 /// [`Factory::hemisphere_light`]: ../struct.Factory.html#method.hemisphere_light
441 pub fn hemisphere(
442 object: usize,
443 sky_color: Color,
444 ground_color: Color,
445 intensity: f32,
446 ) -> LightTemplate {
447 LightTemplate {
448 object,
449
450 color: sky_color,
451 intensity,
452 sub_light: SubLightTemplate::Hemisphere { ground: ground_color },
453 }
454 }
455}
456
457/// Template information about the different sub-types for light.
458///
459/// See [`LightTemplate`] for more more information on settings up light templates, and
460/// utilities for doing so.
461///
462/// [`LightTemplate`]: ./struct.LightTemplate.html
463#[derive(Clone, Copy, Debug)]
464pub enum SubLightTemplate {
465 /// Represents an ambient light, instantiated as an [`Ambient`].
466 ///
467 /// [`Ambient`]: ../light/struct.Ambient.html
468 Ambient,
469
470 /// Represents a directional light, instantiated as a [`Directional`].
471 ///
472 /// [`Directional`]: ../light/struct.Directional.html
473 Directional,
474
475 /// Represents a hemisphere light, instantiated as a [`Hemisphere`].
476 ///
477 /// [`Hemisphere`]: ../light/struct.Hemisphere.html
478 Hemisphere {
479 /// The ground color for the light.
480 ground: Color,
481 },
482
483 /// Represents a point light, instantiated as a [`Point`].
484 ///
485 /// [`Point`]: ../light/struct.Point.html
486 Point,
487}
488
489/// Geometry data that has been loaded to the GPU.
490///
491/// [`Mesh`] objects instantiated with this data will share GPU resources, allowing for more
492/// efficient instanced rendering. Use [`Factory::upload_geometry`] to upload [`Geometry`]
493/// to the GPU and get an `InstancedGeometry`. You can use an `InstancedGeometry` to create
494/// a [`MeshTemplate`] for use in a [`Template`], or you can use [`Factory::create_instanced_mesh`]
495/// to create a [`Mesh`] directly.
496///
497/// [`Factory::upload_geometry`]: ../struct.Factory.html#method.upload_geometry
498/// [`Factory::create_instanced_mesh`]: ../struct.Factory.html#method.create_instanced_mesh
499/// [`Mesh`]: ../struct.Mesh.html
500/// [`Geometry`]: ../struct.Geometry.html
501/// [`Template`]: ./struct.Template.html
502/// [`MeshTemplate`]: ./struct.MeshTemplate.html
503#[derive(Debug, Clone)]
504pub struct InstancedGeometry {
505 pub(crate) gpu_data: GpuData,
506}