mirror of
https://github.com/storytold/storyteller-bevy.git
synced 2026-10-17 00:13:17 +00:00
cba70a62ae
This commit implements a Sketchfab-style single-object viewer as a minimal proof-of-concept. ### `@storyteller/studio-web` This is a new package to house web components for interfacing with the `@storyteller/studio` WASM library. It currently includes a single component, `<sts-viewer />`, which is exported from `@storyteller/studio-web/viewer`. ### `react-frontend` This is a new application intended as a proof-of-concept and testbed for React interop with `@storyteller/studio-web`. Web components are currently a little bit fiddly to use in a React application. `react-frontend/src/app/studio-viewer.tsx` demonstrates how a React-idiomatic component can be written as a thin wrapper around a web component, and `react-frontend/src/app/app.tsx` demonstrates that wrapper component in use.
163 lines
3.8 KiB
Rust
163 lines
3.8 KiB
Rust
use bevy::{
|
|
math::vec3,
|
|
prelude::*,
|
|
render::{mesh::Indices, primitives::Aabb, render_resource::PrimitiveTopology},
|
|
};
|
|
|
|
pub struct Cone {
|
|
pub radius: f32,
|
|
pub length: f32,
|
|
pub resolution: u32,
|
|
}
|
|
|
|
impl From<Cone> for Mesh {
|
|
fn from(c: Cone) -> Self {
|
|
use std::f32::consts::PI;
|
|
|
|
let num_verts = c.resolution + 2;
|
|
let num_tris = c.resolution * 2;
|
|
let num_indices = num_tris * 3;
|
|
|
|
let mut positions = Vec::with_capacity(num_verts as usize);
|
|
let mut indices = Vec::with_capacity(num_indices as usize);
|
|
|
|
let rotator = Mat3::from_axis_angle(Vec3::Y, PI * 2. / c.resolution as f32);
|
|
|
|
positions.push(Vec3::ZERO);
|
|
|
|
let v_base = 0;
|
|
let v_tip = num_verts - 1;
|
|
|
|
let mut vert = vec3(0., 0., c.radius);
|
|
for i in 0..c.resolution {
|
|
positions.push(vert);
|
|
vert = rotator * vert;
|
|
|
|
let v1 = i + 1;
|
|
let v2 = ((i + 2) % (c.resolution + 1)).max(1);
|
|
|
|
#[rustfmt::skip]
|
|
indices.extend([
|
|
v2, v1, v_base,
|
|
v1, v2, v_tip,
|
|
]);
|
|
}
|
|
|
|
positions.push(vec3(0., c.length, 0.));
|
|
|
|
let mut mesh = Mesh::new(PrimitiveTopology::TriangleList);
|
|
mesh.set_indices(Some(Indices::U32(indices)));
|
|
mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, positions);
|
|
|
|
mesh
|
|
}
|
|
}
|
|
|
|
pub struct Plane2Sided {
|
|
pub size: f32,
|
|
}
|
|
|
|
impl Default for Plane2Sided {
|
|
fn default() -> Self {
|
|
Self { size: 1. }
|
|
}
|
|
}
|
|
|
|
impl From<Plane2Sided> for Mesh {
|
|
fn from(p: Plane2Sided) -> Self {
|
|
let offset = p.size / 2.;
|
|
|
|
// [x][ ][x]
|
|
// [ ][ ][ ]
|
|
// [x][ ][x]
|
|
#[rustfmt::skip]
|
|
let positions = vec![
|
|
// 0:
|
|
// [x][ ][ ]
|
|
// [ ][ ][ ]
|
|
// [ ][ ][ ]
|
|
vec3(-offset, 0., -offset),
|
|
// 1:
|
|
// [ ][ ][x]
|
|
// [ ][ ][ ]
|
|
// [ ][ ][ ]
|
|
vec3( offset, 0., -offset),
|
|
// 2:
|
|
// [ ][ ][ ]
|
|
// [ ][ ][ ]
|
|
// [ ][ ][x]
|
|
vec3( offset, 0., offset),
|
|
// 3:
|
|
// [ ][ ][ ]
|
|
// [ ][ ][ ]
|
|
// [x][ ][ ]
|
|
vec3(-offset, 0., offset),
|
|
];
|
|
|
|
#[rustfmt::skip]
|
|
let indices = vec![
|
|
// Top side
|
|
0, 3, 1,
|
|
1, 3, 2,
|
|
// Bottom side
|
|
1, 3, 0,
|
|
2, 3, 1,
|
|
];
|
|
|
|
let mut mesh = Mesh::new(PrimitiveTopology::TriangleList);
|
|
mesh.set_indices(Some(Indices::U32(indices)));
|
|
mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, positions);
|
|
|
|
mesh
|
|
}
|
|
}
|
|
|
|
pub trait Extrema {
|
|
type Output: Sized;
|
|
|
|
fn extrema(&self) -> Self::Output;
|
|
}
|
|
|
|
impl Extrema for Aabb {
|
|
type Output = [Vec3; 8];
|
|
|
|
fn extrema(&self) -> Self::Output {
|
|
let min = Vec3::from(self.min());
|
|
let max = Vec3::from(self.max());
|
|
|
|
[
|
|
min,
|
|
vec3(min.x, min.y, max.z),
|
|
vec3(min.x, max.y, min.z),
|
|
vec3(max.x, min.y, min.z),
|
|
vec3(min.x, max.y, max.z),
|
|
vec3(max.x, min.y, max.z),
|
|
vec3(max.x, max.y, min.z),
|
|
max,
|
|
]
|
|
}
|
|
}
|
|
|
|
pub trait Combinable {
|
|
type IterItem<'a>;
|
|
type Output;
|
|
|
|
fn combine<'a>(elements: impl Iterator<Item = Self::IterItem<'a>>) -> Self::Output;
|
|
}
|
|
|
|
impl Combinable for Aabb {
|
|
type IterItem<'a> = (&'a Self, &'a GlobalTransform);
|
|
type Output = Option<Self>;
|
|
|
|
fn combine<'a>(elements: impl Iterator<Item = Self::IterItem<'a>>) -> Self::Output {
|
|
let points = elements.flat_map(|(aabb, world_xform)| {
|
|
let min = world_xform.transform_point(aabb.min().into());
|
|
let max = world_xform.transform_point(aabb.max().into());
|
|
|
|
[min, max]
|
|
});
|
|
|
|
Aabb::enclosing(points)
|
|
}
|
|
}
|