Skip to content

Commit 707f242

Browse files
committed
- add cube tube prism mesh primitive
1 parent 9cb0238 commit 707f242

2 files changed

Lines changed: 321 additions & 7 deletions

File tree

plugins/ecs_examples/src/geometry_primitives.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
///
22
/// Geometry Primitives
3-
///
3+
///
44
55
use crate::prelude::*;
66

@@ -66,14 +66,18 @@ pub fn setup_geometry_primitives(
6666
(hotline_rs::primitives::create_tube_prism_mesh(&mut device.0, 4, 0, 4, false, true, 0.33, 0.9, 1.0), MeshType::Normal),
6767
(hotline_rs::primitives::create_tube_prism_mesh(&mut device.0, 5, 0, 4, false, true, 0.33, 0.33, 1.0), MeshType::Normal),
6868
(hotline_rs::primitives::create_teapot_mesh(&mut device.0, 4), MeshType::Normal),
69+
70+
(hotline_rs::primitives::create_cube_tube_prism_mesh(&mut device.0, 16, 0, 16, true, true, 1.0, 0.66, 1.0), MeshType::Normal),
71+
(hotline_rs::primitives::create_cube_tube_prism_mesh(&mut device.0, 16, 0, 8, true, true, 1.0, 0.66, 1.0), MeshType::Normal),
72+
(hotline_rs::primitives::create_cube_tube_prism_mesh(&mut device.0, 16, 0, 16, true, true, 1.0, 0.3, 1.0), MeshType::Normal),
6973
];
7074

7175
// square number of rows and columns
7276
let rc = ceil(sqrt(meshes.len() as f32));
73-
let irc = (rc + 0.5) as i32;
77+
let irc = (rc + 0.5) as i32;
7478

7579
let size = 10.0;
76-
let half_size = size * 0.5;
80+
let half_size = size * 0.5;
7781
let step = size * half_size;
7882
let half_extent = rc * half_size;
7983
let start_pos = vec3f(-half_extent * 4.0, size * 1.8, -half_extent * 4.0);
@@ -127,11 +131,11 @@ pub fn setup_geometry_primitives(
127131
/// Rotate all meshes continuously about the y-axis by delta time
128132
#[export_update_fn]
129133
pub fn rotate_meshes(
130-
time: Res<TimeRes>,
134+
time: Res<TimeRes>,
131135
mut mesh_query: Query<&mut Rotation, Without<Billboard>>) -> Result<(), hotline_rs::Error> {
132136
for mut rotation in &mut mesh_query {
133137
rotation.0 *= Quat::from_euler_angles(0.0, f32::pi() * time.0.delta, 0.0);
134138
}
135139

136140
Ok(())
137-
}
141+
}

src/primitives.rs

Lines changed: 312 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ pub fn subdivide_triangle(t0: &Vertex3D, t1: &Vertex3D, t2: &Vertex3D, order: u3
119119
}
120120

121121
/// Utility to create faceted meshes with varying index sizes depending on the index requirements
122-
fn create_mesh_3d<D: gfx::Device>(dev: &mut D, vertices: Vec<Vertex3D>, indices: Vec<usize>) -> pmfx::Mesh<D> {
122+
pub fn create_mesh_3d<D: gfx::Device>(dev: &mut D, vertices: Vec<Vertex3D>, indices: Vec<usize>) -> pmfx::Mesh<D> {
123123
let max_index = vertices.len();
124124
let index_buffer = if max_index > 65535 {
125125
let mut indices32 : Vec<u32> = Vec::new();
@@ -180,7 +180,7 @@ fn create_mesh_3d<D: gfx::Device>(dev: &mut D, vertices: Vec<Vertex3D>, indices:
180180
}
181181

182182
/// Utility to create a facent mesh which will have hard edged normals and automatically generate and index buffer from vertices
183-
fn create_faceted_mesh_3d<D: gfx::Device>(dev: &mut D, vertices: Vec<Vertex3D>) -> pmfx::Mesh<D> {
183+
pub fn create_faceted_mesh_3d<D: gfx::Device>(dev: &mut D, vertices: Vec<Vertex3D>) -> pmfx::Mesh<D> {
184184
let mut indices = Vec::new();
185185
for i in 0..vertices.len() {
186186
indices.push(i);
@@ -2764,6 +2764,316 @@ pub fn create_tube_prism_mesh<D: gfx::Device>(
27642764
}
27652765
}
27662766

2767+
/// Create an `segments` sided prism tube where the exterior is extruded out to a cube, if `smooth` the prism is a cylinder with smooth normals
2768+
/// convert to a trapezoid using `taper` with a value between 0-1 to taper the top cap inward where 1 is no taper and 0 makes a pyramid
2769+
/// use thickness to control the size of the inner hole
2770+
#[allow(clippy::too_many_arguments)]
2771+
pub fn create_cube_tube_prism_mesh<D: gfx::Device>(
2772+
dev: &mut D, segments: usize, trunc_start: usize, trunc_end: usize, smooth: bool, cap: bool, height: f32, thickness: f32, taper: f32) -> pmfx::Mesh<D> {
2773+
let axis = Vec3f::unit_y();
2774+
let right = Vec3f::unit_x();
2775+
let up = cross(axis, right);
2776+
let right = cross(axis, up);
2777+
2778+
// add an extra segment at the end to make uv's wrap nicely
2779+
let vertex_segments = segments + 1;
2780+
2781+
let prism_vertices = |radius: f32, flip: f32| -> (Vec<Vertex3D>, Vec<Vertex3D>) {
2782+
let mut vertices = Vec::new();
2783+
let mut cap_vertices = Vec::new();
2784+
let mut points = Vec::new();
2785+
let mut bottom_points = Vec::new();
2786+
let mut top_points = Vec::new();
2787+
let mut tangents = Vec::new();
2788+
2789+
// rotate around up axis and extract some data we can lookup to build vb and ib
2790+
let mut angle = 0.0;
2791+
let angle_step = f32::two_pi() / segments as f32;
2792+
for i in 0..vertex_segments {
2793+
// current
2794+
let mut x = cos(angle);
2795+
let mut y = -sin(angle);
2796+
let v1 = right * x + up * y;
2797+
2798+
// next
2799+
angle += angle_step;
2800+
x = cos(angle);
2801+
y = -sin(angle);
2802+
let v2 = right * x + up * y;
2803+
2804+
points.push(v1 * radius);
2805+
tangents.push(v2 - v1);
2806+
2807+
bottom_points.push(points[i] - Vec3f::unit_y() * height);
2808+
top_points.push(points[i] * taper + Vec3f::unit_y() * height);
2809+
}
2810+
2811+
// bottom ring
2812+
for i in 0..vertex_segments {
2813+
let u = 0.5 + atan2(bottom_points[i].z, bottom_points[i].x) / f32::two_pi();
2814+
let u = if i == segments { 0.0 } else { u };
2815+
let bt = cross(tangents[i], points[i]);
2816+
vertices.push(Vertex3D{
2817+
position: bottom_points[i],
2818+
normal: points[i] * flip,
2819+
tangent: tangents[i],
2820+
bitangent: bt,
2821+
texcoord: Vec2f::new(u * 3.0, 0.0)
2822+
});
2823+
}
2824+
2825+
// top ring
2826+
for i in 0..vertex_segments {
2827+
let u = 0.5 + atan2(top_points[i].z, top_points[i].x) / f32::two_pi();
2828+
let u = if i == segments { 0.0 } else { u };
2829+
let bt = cross(tangents[i], points[i]);
2830+
vertices.push(Vertex3D{
2831+
position: top_points[i],
2832+
normal: points[i] * flip,
2833+
tangent: tangents[i],
2834+
bitangent: bt,
2835+
texcoord: Vec2f::new(u * 3.0, 1.0)
2836+
});
2837+
}
2838+
2839+
// bottom cap
2840+
for point in bottom_points.iter().take(vertex_segments) {
2841+
cap_vertices.push(Vertex3D{
2842+
position: point.xyz(),
2843+
normal: -Vec3f::unit_y(),
2844+
tangent: Vec3f::unit_x(),
2845+
bitangent: Vec3f::unit_z(),
2846+
texcoord: point.xz() * 0.5 + 0.5
2847+
});
2848+
}
2849+
2850+
// top cap
2851+
for point in top_points.iter().take(vertex_segments) {
2852+
cap_vertices.push(Vertex3D{
2853+
position: point.xyz(),
2854+
normal: Vec3f::unit_y(),
2855+
tangent: Vec3f::unit_x(),
2856+
bitangent: Vec3f::unit_z(),
2857+
texcoord: point.xz() * 0.5 + 0.5
2858+
});
2859+
}
2860+
2861+
(vertices, cap_vertices)
2862+
};
2863+
2864+
let end_cap = |loop_start: usize, v: &Vec<Vertex3D>, inner_offset: usize, flip: f32| -> Vec<Vertex3D> {
2865+
// start
2866+
let bottom = loop_start;
2867+
let top = loop_start + vertex_segments;
2868+
let inner_bottom = bottom + inner_offset;
2869+
let inner_top = top + inner_offset;
2870+
2871+
let mut verts = vec![
2872+
v[bottom].clone(),
2873+
v[top].clone(),
2874+
v[inner_bottom].clone(),
2875+
v[inner_top].clone(),
2876+
];
2877+
2878+
let b = normalize(verts[1].position - verts[0].position);
2879+
let t = normalize(verts[2].position - verts[0].position);
2880+
let n = cross(b, t) * flip;
2881+
2882+
for v in &mut verts {
2883+
v.normal = n;
2884+
v.tangent = t;
2885+
v.bitangent = b;
2886+
}
2887+
2888+
verts[0].texcoord = Vec2f::zero();
2889+
verts[1].texcoord = vec2f(0.0, 1.0);
2890+
verts[2].texcoord = vec2f(1.0 - thickness, 0.0);
2891+
verts[3].texcoord = vec2f(1.0 - thickness, 1.0);
2892+
2893+
verts
2894+
};
2895+
2896+
let (mut outer_vertices, mut outer_cap_vertices) = prism_vertices(1.0, 1.0);
2897+
let (inner_vertices, inner_cap_vertices) = prism_vertices(thickness, -1.0);
2898+
2899+
// project outer vertices onto cube, normals / tangent also need flattening
2900+
for v in &mut outer_vertices {
2901+
// keep y
2902+
let y = v.position.y;
2903+
2904+
// normalize xz onto cube
2905+
v.position.y = 0.0;
2906+
v.position = chebyshev_normalize(v.position);
2907+
2908+
// reset y
2909+
v.position.y = y;
2910+
}
2911+
2912+
// project outer cap vertices onto cube, normals stay the same
2913+
for v in &mut outer_cap_vertices {
2914+
// keep y
2915+
let y = v.position.y;
2916+
2917+
// normalize xz onto cube
2918+
v.position.y = 0.0;
2919+
v.position = chebyshev_normalize(v.position);
2920+
2921+
// reset y
2922+
v.position.y = y;
2923+
}
2924+
2925+
let mut indices = Vec::new();
2926+
let mut vertices = Vec::new();
2927+
2928+
// 2 tris per segment for outer (always faceted)
2929+
for i in trunc_start..trunc_end {
2930+
let bottom = i;
2931+
let top = i + vertex_segments;
2932+
let next = i + 1;
2933+
let top_next = i + 1 + vertex_segments;
2934+
2935+
let face_index = vertices.len();
2936+
vertices.extend(vec![
2937+
outer_vertices[bottom].clone(),
2938+
outer_vertices[top].clone(),
2939+
outer_vertices[next].clone(),
2940+
outer_vertices[top].clone(),
2941+
outer_vertices[top_next].clone(),
2942+
outer_vertices[next].clone()
2943+
]);
2944+
2945+
let v = face_index;
2946+
let n = get_triangle_normal(
2947+
vertices[v].position,
2948+
vertices[v+2].position,
2949+
vertices[v+1].position
2950+
);
2951+
2952+
// set hard face normals
2953+
for vertex in vertices.iter_mut().skip(face_index) {
2954+
vertex.normal = n;
2955+
}
2956+
}
2957+
2958+
// faceted inner
2959+
if !smooth {
2960+
for i in trunc_start..trunc_end {
2961+
let bottom = i;
2962+
let top = i + vertex_segments;
2963+
let next = i + 1;
2964+
let top_next = i + 1 + vertex_segments;
2965+
2966+
let face_index = vertices.len();
2967+
vertices.extend(vec![
2968+
inner_vertices[bottom].clone(),
2969+
inner_vertices[next].clone(),
2970+
inner_vertices[top].clone(),
2971+
inner_vertices[top].clone(),
2972+
inner_vertices[next].clone(),
2973+
inner_vertices[top_next].clone()
2974+
]);
2975+
2976+
let v = face_index;
2977+
let n = get_triangle_normal(
2978+
vertices[v].position,
2979+
vertices[v+2].position,
2980+
vertices[v+1].position,
2981+
);
2982+
2983+
// set hard face normals
2984+
for vertex in vertices.iter_mut().skip(face_index) {
2985+
vertex.normal = n;
2986+
}
2987+
}
2988+
}
2989+
2990+
// top / bottom cap
2991+
if cap {
2992+
for i in trunc_start..trunc_end {
2993+
let bottom = i;
2994+
let top = i + vertex_segments;
2995+
let next = i + 1;
2996+
let top_next = i + 1 + vertex_segments;
2997+
2998+
vertices.extend(vec![
2999+
outer_cap_vertices[top].clone(),
3000+
inner_cap_vertices[top].clone(),
3001+
inner_cap_vertices[top_next].clone(),
3002+
outer_cap_vertices[top].clone(),
3003+
inner_cap_vertices[top_next].clone(),
3004+
outer_cap_vertices[top_next].clone()
3005+
]);
3006+
3007+
vertices.extend(vec![
3008+
inner_cap_vertices[bottom].clone(),
3009+
outer_cap_vertices[bottom].clone(),
3010+
outer_cap_vertices[next].clone(),
3011+
inner_cap_vertices[bottom].clone(),
3012+
outer_cap_vertices[next].clone(),
3013+
inner_cap_vertices[next].clone()
3014+
]);
3015+
}
3016+
}
3017+
3018+
// end cap
3019+
if trunc_start != 0 || trunc_end != segments {
3020+
let mut cap_vertices = outer_vertices.to_vec();
3021+
let inner_offset = cap_vertices.len();
3022+
cap_vertices.extend(inner_vertices.clone());
3023+
3024+
let start_verts = end_cap(trunc_start, &cap_vertices, inner_offset, 1.0);
3025+
vertices.extend(vec![
3026+
start_verts[0].clone(),
3027+
start_verts[3].clone(),
3028+
start_verts[1].clone(),
3029+
start_verts[0].clone(),
3030+
start_verts[2].clone(),
3031+
start_verts[3].clone(),
3032+
]);
3033+
3034+
let end_verts = end_cap(trunc_end, &cap_vertices, inner_offset, -1.0);
3035+
vertices.extend(vec![
3036+
end_verts[0].clone(),
3037+
end_verts[1].clone(),
3038+
end_verts[3].clone(),
3039+
end_verts[0].clone(),
3040+
end_verts[3].clone(),
3041+
end_verts[2].clone(),
3042+
]);
3043+
}
3044+
3045+
// create facet indices
3046+
for i in 0..vertices.len() {
3047+
indices.push(i);
3048+
}
3049+
3050+
// smooth inner appeneded
3051+
if smooth {
3052+
let inner_offset = vertices.len();
3053+
vertices.extend(inner_vertices);
3054+
3055+
// sides
3056+
for i in trunc_start..trunc_end {
3057+
let bottom = i;
3058+
let top = i + vertex_segments;
3059+
let next = i + 1;
3060+
let top_next = i + 1 + vertex_segments;
3061+
3062+
// inner
3063+
indices.extend(vec![
3064+
bottom + inner_offset,
3065+
next + inner_offset,
3066+
top + inner_offset,
3067+
top + inner_offset,
3068+
next + inner_offset,
3069+
top_next + inner_offset,
3070+
]);
3071+
}
3072+
}
3073+
3074+
create_mesh_3d(dev, vertices, indices)
3075+
}
3076+
27673077
fn cubic_interpolate(p1: Vec3f, p2: Vec3f, p3: Vec3f, p4: Vec3f, t: f32) -> Vec3f {
27683078
p1 * (1.0 - t) * (1.0 - t) * (1.0 - t) +
27693079
p2 * 3.0 * t * (1.0 - t) * (1.0 - t) +

0 commit comments

Comments
 (0)