Skip to content

Commit

Permalink
Merge #46
Browse files Browse the repository at this point in the history
46: Blend shape animation r=kvark

cc @vitvakatu
  • Loading branch information
bors[bot] committed Jul 5, 2017
2 parents 0446131 + 01c31b1 commit 0412456
Show file tree
Hide file tree
Showing 8 changed files with 342 additions and 62 deletions.
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ name = "sprite"
[[example]]
name = "group"

[[example]]
name = "anim"

[[example]]
name = "aviator"
path = "examples/aviator/main.rs"
137 changes: 137 additions & 0 deletions examples/anim.rs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion examples/aviator/plane.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ impl AirPlane {

let cockpit = {
let mut geo = three::Geometry::new_box(80.0, 50.0, 50.0);
for v in geo.vertices.iter_mut() {
for v in geo.base_shape.vertices.iter_mut() {
if v.x < 0.0 {
v.z += if v.y > 0.0 {-20.0} else {20.0};
v.y += if v.y > 0.0 {-10.0} else {30.0};
Expand Down
2 changes: 1 addition & 1 deletion examples/group.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ fn create_cubes(factory: &mut three::Factory,
-> Vec<Cube>
{
let mut geometry = three::Geometry::new_box(2.0, 2.0, 2.0);
for v in geometry.vertices.iter_mut() {
for v in geometry.base_shape.vertices.iter_mut() {
v.z += 1.0;
}

Expand Down
188 changes: 146 additions & 42 deletions src/factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::io::BufReader;
use std::fs::File;
use std::path::Path;

use cgmath::Transform as Transform_;
use cgmath::{Vector3, Transform as Transform_};
use genmesh::{Polygon, EmitTriangles, Triangulate, Vertex as GenVertex};
use genmesh::generators::{self, IndexedPolygon, SharedVertex};
use gfx;
Expand All @@ -16,11 +16,12 @@ use mint;
use obj;

use camera::{Orthographic, Perspective};
use render::{BackendFactory, BackendResources, GpuData, Vertex, ShadowFormat};
use scene::{Color, Background, Group, Mesh, Sprite, Material,
use render::{BackendFactory, BackendResources, GpuData, DynamicData,
Vertex, ShadowFormat};
use scene::{Color, Background, Group, Sprite, Material,
AmbientLight, DirectionalLight, HemisphereLight, PointLight};
use {Hub, HubPtr, SubLight, Node, SubNode,
LightData, Object, Scene, Camera};
LightData, Object, Scene, Camera, Mesh, DynamicMesh};


const NORMAL_Z: [I8Norm; 4] = [I8Norm(0), I8Norm(0), I8Norm(1), I8Norm(0)];
Expand Down Expand Up @@ -179,35 +180,76 @@ impl Factory {
Group::new(self.hub.lock().unwrap().spawn_empty())
}

/// Create new `Mesh` with desired `Geometry` and `Material`.
pub fn mesh(&mut self, geom: Geometry, mat: Material) -> Mesh {
let vertices: Vec<_> = if geom.normals.is_empty() {
geom.vertices.iter().map(|v| Vertex {
fn mesh_vertices(shape: &GeometryShape) -> Vec<Vertex> {
if shape.normals.is_empty() {
shape.vertices.iter().map(|v| Vertex {
pos: [v.x, v.y, v.z, 1.0],
uv: [0.0, 0.0], //TODO
normal: NORMAL_Z,
}).collect()
} else {
let f2i = |x: f32| I8Norm(cmp::min(cmp::max((x * 127.) as isize, -128), 127) as i8);
geom.vertices.iter().zip(geom.normals.iter()).map(|(v, n)| Vertex {
shape.vertices.iter().zip(shape.normals.iter()).map(|(v, n)| Vertex {
pos: [v.x, v.y, v.z, 1.0],
uv: [0.0, 0.0], //TODO
normal: [f2i(n.x), f2i(n.y), f2i(n.z), I8Norm(0)],
}).collect()
};
//TODO: dynamic geometry
}
}

/// Create new `Mesh` with desired `Geometry` and `Material`.
pub fn mesh(&mut self, geometry: Geometry, mat: Material) -> Mesh {
let vertices = Self::mesh_vertices(&geometry.base_shape);
let cbuf = self.backend.create_constant_buffer(1);
let (vbuf, slice) = if geom.faces.is_empty() {
let (vbuf, slice) = if geometry.faces.is_empty() {
self.backend.create_vertex_buffer_with_slice(&vertices, ())
} else {
let faces: &[u16] = gfx::memory::cast_slice(&geom.faces);
let faces: &[u16] = gfx::memory::cast_slice(&geometry.faces);
self.backend.create_vertex_buffer_with_slice(&vertices, faces)
};
Mesh::new(self.hub.lock().unwrap().spawn_visual(mat, GpuData {
slice,
vertices: vbuf,
constants: cbuf,
}))
Mesh {
object: self.hub.lock().unwrap().spawn_visual(mat, GpuData {
slice,
vertices: vbuf,
constants: cbuf,
pending: None,
}),
}
}

/// Create a new `DynamicMesh` with desired `Geometry` and `Material`.
pub fn mesh_dynamic(&mut self, geometry: Geometry, mat: Material) -> DynamicMesh {
let slice = {
let data: &[u16] = gfx::memory::cast_slice(&geometry.faces);
gfx::Slice {
start: 0,
end: data.len() as u32,
base_vertex: 0,
instances: None,
buffer: self.backend.create_index_buffer(data),
}
};
let (num_vertices, vertices) = {
let data = Self::mesh_vertices(&geometry.base_shape);
let buf = self.backend.create_buffer_immutable(&data,
gfx::buffer::Role::Vertex, gfx::memory::TRANSFER_DST).unwrap();
(data.len(), buf)
};
let constants = self.backend.create_constant_buffer(1);

DynamicMesh {
object: self.hub.lock().unwrap().spawn_visual(mat, GpuData {
slice,
vertices,
constants,
pending: None,
}),
geometry,
dynamic: DynamicData {
num_vertices,
buffer: self.backend.create_upload_buffer(num_vertices).unwrap(),
},
}
}

/// Create a `Mesh` sharing the geometry with another one.
Expand All @@ -221,7 +263,9 @@ impl Factory {
},
_ => unreachable!()
};
Mesh::new(hub.spawn_visual(mat, gpu_data))
Mesh {
object: hub.spawn_visual(mat, gpu_data),
}
}

/// Create new sprite from `Material`.
Expand All @@ -230,6 +274,7 @@ impl Factory {
slice: gfx::Slice::new_match_vertex_buffer(&self.quad_buf),
vertices: self.quad_buf.clone(),
constants: self.backend.create_constant_buffer(1),
pending: None,
}))
}

Expand Down Expand Up @@ -285,35 +330,55 @@ impl Factory {
}
}

/// A collection of vertices, their normals, and faces that defines the
/// shape of a polyhedral object.

/// A shape of geometry that is used for mesh blending.
#[derive(Clone, Debug)]
pub struct Geometry {
pub struct GeometryShape {
/// Vertices.
pub vertices: Vec<mint::Point3<f32>>,
/// Normals.
pub normals: Vec<mint::Vector3<f32>>,
}

impl GeometryShape {
/// Create an empty shape.
pub fn empty() -> Self {
GeometryShape {
vertices: Vec::new(),
normals: Vec::new(),
}
}
}

/// A collection of vertices, their normals, and faces that defines the
/// shape of a polyhedral object.
#[derive(Clone, Debug)]
pub struct Geometry {
/// The original shape of geometry.
pub base_shape: GeometryShape,
/// A map containing blend shapes and their names.
pub shapes: HashMap<String, GeometryShape>,
/// Faces.
pub faces: Vec<[u16; 3]>,
/// Whether geometry is dynamic or not.
pub is_dynamic: bool,
}

impl Geometry {
/// Create new `Geometry` without any data in it.
pub fn empty() -> Geometry {
pub fn empty() -> Self {
Geometry {
vertices: Vec::new(),
normals: Vec::new(),
base_shape: GeometryShape::empty(),
shapes: HashMap::new(),
faces: Vec::new(),
is_dynamic: false,
}
}

/// Create `Geometry` from vector of vertices.
pub fn from_vertices(verts: Vec<mint::Point3<f32>>) -> Geometry {
pub fn from_vertices(vertices: Vec<mint::Point3<f32>>) -> Self {
Geometry {
vertices: verts,
base_shape: GeometryShape {
vertices,
normals: Vec::new(),
},
.. Geometry::empty()
}
}
Expand All @@ -325,17 +390,19 @@ impl Geometry {
Fnor: Fn(GenVertex) -> mint::Vector3<f32>,
{
Geometry {
vertices: gen.shared_vertex_iter()
.map(fpos)
.collect(),
normals: gen.shared_vertex_iter()
.map(fnor)
.collect(),
base_shape: GeometryShape {
vertices: gen.shared_vertex_iter()
.map(fpos)
.collect(),
normals: gen.shared_vertex_iter()
.map(fnor)
.collect(),
},
shapes: HashMap::new(),
faces: gen.indexed_polygon_iter()
.triangulate()
.map(|t| [t.x as u16, t.y as u16, t.z as u16])
.collect(),
is_dynamic: false,
}
}

Expand Down Expand Up @@ -390,6 +457,7 @@ impl Geometry {
}
}


/// An image applied (mapped) to the surface of a shape or polygon.
#[derive(Clone, Debug)]
pub struct Texture<T> {
Expand Down Expand Up @@ -579,11 +647,14 @@ impl Factory {

let (vbuf, slice) = self.backend.create_vertex_buffer_with_slice(&vertices, &indices[..]);
let cbuf = self.backend.create_constant_buffer(1);
let mesh = Mesh::new(hub.spawn_visual(material, GpuData {
slice,
vertices: vbuf,
constants: cbuf,
}));
let mesh = Mesh {
object: hub.spawn_visual(material, GpuData {
slice,
vertices: vbuf,
constants: cbuf,
pending: None,
}),
};
group.add(&mesh);
meshes.push(mesh);
}
Expand All @@ -593,4 +664,37 @@ impl Factory {

(groups, meshes)
}

/// Update the geometry of `DynamicMesh`.
pub fn mix(&mut self, mesh: &DynamicMesh, shapes: &[(&str, f32)]) {
let f2i = |x: f32| I8Norm(cmp::min(cmp::max((x * 127.) as isize, -128), 127) as i8);

self.hub.lock().unwrap().update_mesh(mesh);
let shapes: Vec<_> = shapes.iter().map(|&(name, k)|
(&mesh.geometry.shapes[name], k)
).collect();
let mut mapping = self.backend.write_mapping(&mesh.dynamic.buffer).unwrap();

for i in 0 .. mesh.geometry.base_shape.vertices.len() {
let (mut pos, ksum) = shapes.iter().fold((Vector3::new(0.0, 0.0, 0.0), 0.0), |(pos, ksum), &(ref shape, k)| {
let p: [f32; 3] = shape.vertices[i].into();
(pos + k * Vector3::from(p), ksum + k)
});
if ksum != 1.0 {
let p: [f32; 3] = mesh.geometry.base_shape.vertices[i].into();
pos += (1.0 - ksum) * Vector3::from(p);
}
let normal = if mesh.geometry.base_shape.normals.is_empty() {
NORMAL_Z
} else {
let n = mesh.geometry.base_shape.normals[i];
[f2i(n.x), f2i(n.y), f2i(n.z), I8Norm(0)]
};
mapping[i] = Vertex {
pos: [pos.x, pos.y, pos.z, 1.0],
uv: [0.0, 0.0], //TODO
normal,
};
}
}
}
27 changes: 24 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@ mod scene;
mod window;

pub use camera::{OrbitControls, Orthographic, Perspective};
pub use factory::{Factory, Geometry, ShadowMap, Texture};
pub use factory::{Factory, Geometry, GeometryShape, ShadowMap, Texture};
pub use input::{Button, KeyAxis, Timer, Input,
KEY_ESCAPE, KEY_SPACE, MOUSE_LEFT, MOUSE_RIGHT,
AXIS_LEFT_RIGHT, AXIS_DOWN_UP};
pub use render::{ColorFormat, DepthFormat, Renderer, ShadowType, DebugQuadHandle};
pub use scene::{Color, Background, Material, NodeTransform, NodeInfo,
Group, Mesh, Sprite,
Group, Sprite,
AmbientLight, DirectionalLight, HemisphereLight, PointLight};
#[cfg(feature = "opengl")]
pub use window::Window;
Expand All @@ -47,7 +47,7 @@ use std::sync::{mpsc, Arc, Mutex};

use cgmath::Transform as Transform_;
use factory::SceneId;
use render::GpuData;
use render::{DynamicData, GpuData};

/// Pointer to a Node
pub type NodePointer = froggy::Pointer<Node>;
Expand Down Expand Up @@ -218,6 +218,14 @@ impl Hub {
item.world_transform = transform;
}
}

fn update_mesh(&mut self, mesh: &DynamicMesh) {
match self.nodes[&mesh.node].sub_node {
SubNode::Visual(_, ref mut gpu_data) =>
gpu_data.pending = Some(mesh.dynamic.clone()),
_ => unreachable!()
}
}
}

/// Game scene contains game objects and can be rendered by [`Camera`](struct.Camera.html).
Expand All @@ -229,3 +237,16 @@ pub struct Scene {
/// See [`Background`](struct.Background.html).
pub background: scene::Background,
}

/// [`Geometry`](struct.Geometry.html) with some [`Material`](struct.Material.html).
pub struct Mesh {
object: Object,
}

/// A dynamic version of a mesh allows changing the geometry on CPU side
/// in order to animate the mesh.
pub struct DynamicMesh {
object: Object,
geometry: Geometry,
dynamic: DynamicData,
}
Loading

0 comments on commit 0412456

Please sign in to comment.