render: implement sprite pivot, fix sprite centering in ortho projection
This commit is contained in:
parent
d1aee610cc
commit
a06c065337
|
@ -17,20 +17,18 @@ use crate::{
|
||||||
render::{
|
render::{
|
||||||
graph::{Node, NodeDesc, NodeType, SlotAttribute},
|
graph::{Node, NodeDesc, NodeType, SlotAttribute},
|
||||||
resource::{FragmentState, RenderPipeline, RenderPipelineDescriptor, VertexState},
|
resource::{FragmentState, RenderPipeline, RenderPipelineDescriptor, VertexState},
|
||||||
transform_buffer_storage::{TransformBuffers, TransformIndex},
|
|
||||||
vertex::Vertex2D,
|
vertex::Vertex2D,
|
||||||
},
|
},
|
||||||
sprite::{AtlasSprite, Sprite},
|
sprite::{AtlasSprite, Sprite},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{BasePassSlots, RenderAssets};
|
use super::{BasePassSlots, InterpTransform, RenderAssets};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct RenderJob {
|
pub struct RenderJob {
|
||||||
pub entity: Entity,
|
pub entity: Entity,
|
||||||
pub shader_id: u64,
|
pub shader_id: u64,
|
||||||
pub asset_uuid: uuid::Uuid,
|
pub asset_uuid: uuid::Uuid,
|
||||||
pub transform_id: TransformIndex,
|
|
||||||
pub atlas_frame_id: u64,
|
pub atlas_frame_id: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,11 +62,8 @@ pub struct SpritePass {
|
||||||
pipeline: Option<RenderPipeline>,
|
pipeline: Option<RenderPipeline>,
|
||||||
texture_bgl: Option<Arc<wgpu::BindGroupLayout>>,
|
texture_bgl: Option<Arc<wgpu::BindGroupLayout>>,
|
||||||
jobs: VecDeque<RenderJob>,
|
jobs: VecDeque<RenderJob>,
|
||||||
/// Buffer that stores a `Rect` with `min` and `max` set to zero.
|
sprite_instances_buf: Option<Arc<wgpu::Buffer>>,
|
||||||
/// This can be used for sprites that are not from an atlas.
|
|
||||||
atlas_frames_buf: Option<Arc<wgpu::Buffer>>,
|
|
||||||
|
|
||||||
transform_buffers: Option<ResourceData>,
|
|
||||||
texture_store: Option<ResourceData>,
|
texture_store: Option<ResourceData>,
|
||||||
buffer_store: Option<ResourceData>,
|
buffer_store: Option<ResourceData>,
|
||||||
}
|
}
|
||||||
|
@ -175,7 +170,7 @@ impl SpritePass {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
let frames = self.atlas_frames_buf.as_ref().unwrap();
|
let sprite_instances = self.sprite_instances_buf.as_ref().unwrap();
|
||||||
let bgl = self.texture_bgl.as_ref().unwrap();
|
let bgl = self.texture_bgl.as_ref().unwrap();
|
||||||
let tex_bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
let tex_bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||||
label: Some(&format!("sprite_texture_bg_{}", uuid_str)),
|
label: Some(&format!("sprite_texture_bg_{}", uuid_str)),
|
||||||
|
@ -192,7 +187,7 @@ impl SpritePass {
|
||||||
wgpu::BindGroupEntry {
|
wgpu::BindGroupEntry {
|
||||||
binding: 2,
|
binding: 2,
|
||||||
resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
|
resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
|
||||||
buffer: &frames,
|
buffer: &sprite_instances,
|
||||||
offset: 0,
|
offset: 0,
|
||||||
size: None,
|
size: None,
|
||||||
}),
|
}),
|
||||||
|
@ -211,16 +206,13 @@ impl Node for SpritePass {
|
||||||
) -> crate::render::graph::NodeDesc {
|
) -> crate::render::graph::NodeDesc {
|
||||||
let device = &graph.device;
|
let device = &graph.device;
|
||||||
|
|
||||||
let atlas_frames = device.create_buffer(&wgpu::BufferDescriptor {
|
let sprite_instances = device.create_buffer(&wgpu::BufferDescriptor {
|
||||||
label: Some("default_sprite_atlas_frame"),
|
label: Some("sprite_instances"),
|
||||||
size: std::mem::size_of::<URect>() as u64 * 1000,
|
size: std::mem::size_of::<SpriteInstance>() as u64 * 1000,
|
||||||
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
|
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
|
||||||
mapped_at_creation: false,
|
mapped_at_creation: false,
|
||||||
});
|
});
|
||||||
// write the rect for sprites that aren't part of the texture atlas.
|
self.sprite_instances_buf = Some(Arc::new(sprite_instances));
|
||||||
graph
|
|
||||||
.queue
|
|
||||||
.write_buffer(&atlas_frames, 0, bytemuck::bytes_of(&URect::ZERO));
|
|
||||||
|
|
||||||
let bgl = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
let bgl = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||||
label: Some("bgl_sprite_main"),
|
label: Some("bgl_sprite_main"),
|
||||||
|
@ -243,7 +235,7 @@ impl Node for SpritePass {
|
||||||
},
|
},
|
||||||
wgpu::BindGroupLayoutEntry {
|
wgpu::BindGroupLayoutEntry {
|
||||||
binding: 2,
|
binding: 2,
|
||||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
|
||||||
ty: wgpu::BindingType::Buffer {
|
ty: wgpu::BindingType::Buffer {
|
||||||
ty: wgpu::BufferBindingType::Storage { read_only: true },
|
ty: wgpu::BufferBindingType::Storage { read_only: true },
|
||||||
has_dynamic_offset: false,
|
has_dynamic_offset: false,
|
||||||
|
@ -254,7 +246,6 @@ impl Node for SpritePass {
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
self.texture_bgl = Some(Arc::new(bgl));
|
self.texture_bgl = Some(Arc::new(bgl));
|
||||||
self.atlas_frames_buf = Some(Arc::new(atlas_frames));
|
|
||||||
|
|
||||||
let mut desc = NodeDesc::new(NodeType::Render, None, vec![]);
|
let mut desc = NodeDesc::new(NodeType::Render, None, vec![]);
|
||||||
|
|
||||||
|
@ -282,16 +273,12 @@ impl Node for SpritePass {
|
||||||
|
|
||||||
let diffuse_bgl = self.texture_bgl.clone().unwrap();
|
let diffuse_bgl = self.texture_bgl.clone().unwrap();
|
||||||
let camera_bgl = graph.bind_group_layout(BasePassSlots::Camera).clone();
|
let camera_bgl = graph.bind_group_layout(BasePassSlots::Camera).clone();
|
||||||
let transforms = world
|
|
||||||
.get_resource::<TransformBuffers>()
|
|
||||||
.expect("Missing transform buffers");
|
|
||||||
let transform_bgl = transforms.bindgroup_layout.clone();
|
|
||||||
|
|
||||||
self.pipeline = Some(RenderPipeline::create(
|
self.pipeline = Some(RenderPipeline::create(
|
||||||
device,
|
device,
|
||||||
&RenderPipelineDescriptor {
|
&RenderPipelineDescriptor {
|
||||||
label: Some("sprite_pass".into()),
|
label: Some("sprite_pass".into()),
|
||||||
layouts: vec![diffuse_bgl, transform_bgl, camera_bgl],
|
layouts: vec![diffuse_bgl, camera_bgl],
|
||||||
push_constant_ranges: vec![],
|
push_constant_ranges: vec![],
|
||||||
vertex: VertexState {
|
vertex: VertexState {
|
||||||
module: shader.clone(),
|
module: shader.clone(),
|
||||||
|
@ -317,7 +304,6 @@ impl Node for SpritePass {
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
|
|
||||||
drop(transforms);
|
|
||||||
world.add_resource_default_if_absent::<RenderAssets<SpriteTexture>>();
|
world.add_resource_default_if_absent::<RenderAssets<SpriteTexture>>();
|
||||||
let texture_store = world
|
let texture_store = world
|
||||||
.get_resource_data::<RenderAssets<SpriteTexture>>().unwrap();
|
.get_resource_data::<RenderAssets<SpriteTexture>>().unwrap();
|
||||||
|
@ -327,22 +313,17 @@ impl Node for SpritePass {
|
||||||
let buffer_store = world
|
let buffer_store = world
|
||||||
.get_resource_data::<FxHashMap<Entity, SpriteBuffers>>().unwrap();
|
.get_resource_data::<FxHashMap<Entity, SpriteBuffers>>().unwrap();
|
||||||
self.buffer_store = Some(buffer_store.clone());
|
self.buffer_store = Some(buffer_store.clone());
|
||||||
|
|
||||||
let transforms = world
|
|
||||||
.get_resource_data::<TransformBuffers>()
|
|
||||||
.expect("Missing transform buffers");
|
|
||||||
self.transform_buffers = Some(transforms.clone());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut v = Vec::with_capacity(500);
|
let mut sprite_instances = Vec::with_capacity(500);
|
||||||
|
|
||||||
let world_tick = world.current_tick();
|
let world_tick = world.current_tick();
|
||||||
let queue = &graph.queue;
|
let queue = &graph.queue;
|
||||||
for (entity, (sprite, atlas_sprite), transform_idx, mut texture_store, mut buffer_store) in world
|
for (entity, (sprite, atlas_sprite), interp_transform, mut texture_store, mut buffer_store) in world
|
||||||
.view::<(
|
.view::<(
|
||||||
Entities,
|
Entities,
|
||||||
Or<&Sprite, (&AtlasSprite, TickOf<AtlasSprite>)>,
|
Or<&Sprite, (&AtlasSprite, TickOf<AtlasSprite>)>,
|
||||||
&TransformIndex,
|
&InterpTransform,
|
||||||
ResMut<RenderAssets<SpriteTexture>>,
|
ResMut<RenderAssets<SpriteTexture>>,
|
||||||
ResMut<FxHashMap<Entity, SpriteBuffers>>,
|
ResMut<FxHashMap<Entity, SpriteBuffers>>,
|
||||||
)>()
|
)>()
|
||||||
|
@ -374,13 +355,14 @@ impl Node for SpritePass {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !buffer_store.contains_key(&entity) {
|
let dim = rect
|
||||||
let dim = rect.unwrap_or_else(|| {
|
.map(|r| r.dimensions())
|
||||||
|
.unwrap_or_else(|| {
|
||||||
let i = image.dimensions();
|
let i = image.dimensions();
|
||||||
URect::new(0, 0, i.0, i.1)
|
UVec2::new(i.0, i.1)
|
||||||
});
|
});
|
||||||
debug!("storing rect: {dim:?}");
|
|
||||||
let dim = dim.dimensions();
|
if !buffer_store.contains_key(&entity) {
|
||||||
let (vertex, index) = self.create_vertex_index_buffers(device, dim);
|
let (vertex, index) = self.create_vertex_index_buffers(device, dim);
|
||||||
buffer_store.insert(entity, SpriteBuffers { vertex_buffers: vertex, index_buffers: index });
|
buffer_store.insert(entity, SpriteBuffers { vertex_buffers: vertex, index_buffers: index });
|
||||||
} else if let Some((ats, tick)) = &atlas_sprite {
|
} else if let Some((ats, tick)) = &atlas_sprite {
|
||||||
|
@ -393,29 +375,36 @@ impl Node for SpritePass {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let frame_id = match rect {
|
let pivot = atlas_sprite.map(|ats| ats.0.pivot)
|
||||||
Some(r) => {
|
// unwrap is safe since its either AtlasSprite or Sprite.
|
||||||
v.push(r);
|
.unwrap_or_else(|| sprite.unwrap().pivot)
|
||||||
// No -1 here since the gpu buffer is already offset by 1
|
.as_vec();
|
||||||
// to store the default rect at the front
|
|
||||||
v.len() as u64
|
let pivot_pos = dim.as_vec2() * (pivot - Vec2::splat(0.5));
|
||||||
}
|
let transform = interp_transform.last_transform
|
||||||
None => 0
|
+ lyra_math::Transform::from_translation(Vec3::new(pivot_pos.x, pivot_pos.y, 0.0));
|
||||||
|
|
||||||
|
let inst = SpriteInstance {
|
||||||
|
atlas_frame: rect.unwrap_or(URect::ZERO),
|
||||||
|
transform: transform.calculate_mat4(),
|
||||||
|
pivot,
|
||||||
|
_padding: [0; 2],
|
||||||
};
|
};
|
||||||
|
sprite_instances.push(inst);
|
||||||
|
let inst_id = sprite_instances.len() as u64 - 1;
|
||||||
|
|
||||||
self.jobs.push_back(RenderJob {
|
self.jobs.push_back(RenderJob {
|
||||||
entity,
|
entity,
|
||||||
shader_id: 0,
|
shader_id: 0,
|
||||||
asset_uuid: texture_uuid,
|
asset_uuid: texture_uuid,
|
||||||
transform_id: *transform_idx,
|
atlas_frame_id: inst_id,
|
||||||
atlas_frame_id: frame_id,
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let buf = self.atlas_frames_buf.as_ref().unwrap();
|
let buf = self.sprite_instances_buf.as_ref().unwrap();
|
||||||
// skip default rect
|
// skip default rect
|
||||||
queue.write_buffer(buf, std::mem::size_of::<URect>() as _, bytemuck::cast_slice(&v));
|
queue.write_buffer(buf, 0, bytemuck::cast_slice(&sprite_instances));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(
|
fn execute(
|
||||||
|
@ -430,8 +419,6 @@ impl Node for SpritePass {
|
||||||
let texture_store: AtomicRef<RenderAssets<SpriteTexture>> = texture_store.get();
|
let texture_store: AtomicRef<RenderAssets<SpriteTexture>> = texture_store.get();
|
||||||
let buffer_store = self.buffer_store.clone().unwrap();
|
let buffer_store = self.buffer_store.clone().unwrap();
|
||||||
let buffer_store: AtomicRef<FxHashMap<Entity, SpriteBuffers>> = buffer_store.get();
|
let buffer_store: AtomicRef<FxHashMap<Entity, SpriteBuffers>> = buffer_store.get();
|
||||||
let transforms = self.transform_buffers.clone().unwrap();
|
|
||||||
let transforms: AtomicRef<TransformBuffers> = transforms.get();
|
|
||||||
|
|
||||||
let vt = graph.view_target();
|
let vt = graph.view_target();
|
||||||
let view = vt.render_view();
|
let view = vt.render_view();
|
||||||
|
@ -468,12 +455,7 @@ impl Node for SpritePass {
|
||||||
.expect("failed to find SpriteTexture for job asset_uuid");
|
.expect("failed to find SpriteTexture for job asset_uuid");
|
||||||
pass.set_bind_group(0, &tex.texture_bg, &[]);
|
pass.set_bind_group(0, &tex.texture_bg, &[]);
|
||||||
|
|
||||||
// Get the bindgroup for job's transform and bind to it using an offset.
|
pass.set_bind_group(1, camera_bg, &[]);
|
||||||
let bindgroup = transforms.bind_group(job.transform_id);
|
|
||||||
let offset = transforms.buffer_offset(job.transform_id);
|
|
||||||
pass.set_bind_group(1, bindgroup, &[offset]);
|
|
||||||
|
|
||||||
pass.set_bind_group(2, camera_bg, &[]);
|
|
||||||
|
|
||||||
// set vertex and index buffers
|
// set vertex and index buffers
|
||||||
let bufs = buffer_store
|
let bufs = buffer_store
|
||||||
|
@ -489,3 +471,12 @@ impl Node for SpritePass {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
||||||
|
struct SpriteInstance {
|
||||||
|
atlas_frame: URect,
|
||||||
|
transform: glam::Mat4,
|
||||||
|
pivot: glam::Vec2,
|
||||||
|
_padding: [u32; 2],
|
||||||
|
}
|
|
@ -25,8 +25,8 @@ use crate::{
|
||||||
/// transform is updated less often than rendering.
|
/// transform is updated less often than rendering.
|
||||||
#[derive(Clone, Debug, Component)]
|
#[derive(Clone, Debug, Component)]
|
||||||
pub struct InterpTransform {
|
pub struct InterpTransform {
|
||||||
last_transform: Transform,
|
pub last_transform: Transform,
|
||||||
alpha: f32,
|
pub alpha: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone, Copy, Hash, RenderGraphLabel)]
|
#[derive(Default, Debug, Clone, Copy, Hash, RenderGraphLabel)]
|
||||||
|
|
|
@ -23,6 +23,12 @@ struct URect {
|
||||||
max: vec2<u32>,
|
max: vec2<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct SpriteInstance {
|
||||||
|
atlas_frame: URect,
|
||||||
|
transform: mat4x4<f32>,
|
||||||
|
pivot: vec2<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
struct CameraUniform {
|
struct CameraUniform {
|
||||||
view: mat4x4<f32>,
|
view: mat4x4<f32>,
|
||||||
inverse_projection: mat4x4<f32>,
|
inverse_projection: mat4x4<f32>,
|
||||||
|
@ -33,21 +39,21 @@ struct CameraUniform {
|
||||||
}
|
}
|
||||||
|
|
||||||
@group(1) @binding(0)
|
@group(1) @binding(0)
|
||||||
var<uniform> u_model_transform_data: TransformData;
|
|
||||||
|
|
||||||
@group(2) @binding(0)
|
|
||||||
var<uniform> u_camera: CameraUniform;
|
var<uniform> u_camera: CameraUniform;
|
||||||
|
|
||||||
@vertex
|
@vertex
|
||||||
fn vs_main(
|
fn vs_main(
|
||||||
in: VertexInput,
|
in: VertexInput,
|
||||||
) -> VertexOutput {
|
) -> VertexOutput {
|
||||||
|
let transform = u_sprite_instances[in.instance_index].transform;
|
||||||
|
var world_position: vec4<f32> = transform * vec4<f32>(in.position, 1.0);
|
||||||
|
|
||||||
var out: VertexOutput;
|
var out: VertexOutput;
|
||||||
var world_position: vec4<f32> = u_model_transform_data.transform * vec4<f32>(in.position, 1.0);
|
|
||||||
out.world_position = world_position.xyz;
|
out.world_position = world_position.xyz;
|
||||||
out.tex_coords = in.tex_coords;
|
out.tex_coords = in.tex_coords;
|
||||||
out.clip_position = u_camera.view_projection * world_position;
|
out.clip_position = u_camera.view_projection * world_position;
|
||||||
out.instance_index = in.instance_index;
|
out.instance_index = in.instance_index;
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,11 +64,12 @@ var t_diffuse: texture_2d<f32>;
|
||||||
var s_diffuse: sampler;
|
var s_diffuse: sampler;
|
||||||
|
|
||||||
@group(0) @binding(2)
|
@group(0) @binding(2)
|
||||||
var<storage, read> u_sprite_atlas_frames: array<URect>;
|
var<storage, read> u_sprite_instances: array<SpriteInstance>;
|
||||||
|
|
||||||
@fragment
|
@fragment
|
||||||
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||||
let frame = u_sprite_atlas_frames[in.instance_index];
|
let sprite = u_sprite_instances[in.instance_index];
|
||||||
|
let frame = sprite.atlas_frame;
|
||||||
var region_coords = in.tex_coords;
|
var region_coords = in.tex_coords;
|
||||||
|
|
||||||
if (frame.min.x != 0 || frame.min.y != 0 || frame.max.x != 0 || frame.max.y != 0) {
|
if (frame.min.x != 0 || frame.min.y != 0 || frame.max.x != 0 || frame.max.y != 0) {
|
||||||
|
|
|
@ -34,6 +34,7 @@ impl Default for ScaleMode {
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Reflect)]
|
#[derive(Debug, Clone, Copy, PartialEq, Reflect)]
|
||||||
pub struct OrthographicProjection {
|
pub struct OrthographicProjection {
|
||||||
|
pub viewport_origin: Vec2,
|
||||||
pub scale_mode: ScaleMode,
|
pub scale_mode: ScaleMode,
|
||||||
pub scale: f32,
|
pub scale: f32,
|
||||||
pub znear: f32,
|
pub znear: f32,
|
||||||
|
@ -43,6 +44,7 @@ pub struct OrthographicProjection {
|
||||||
impl Default for OrthographicProjection {
|
impl Default for OrthographicProjection {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
viewport_origin: Vec2::new(0.5, 0.5),
|
||||||
scale_mode: Default::default(),
|
scale_mode: Default::default(),
|
||||||
scale: 1.0,
|
scale: 1.0,
|
||||||
znear: 0.0,
|
znear: 0.0,
|
||||||
|
@ -53,52 +55,44 @@ impl Default for OrthographicProjection {
|
||||||
|
|
||||||
impl OrthographicProjection {
|
impl OrthographicProjection {
|
||||||
fn get_rect(&self, viewport_size: Vec2) -> Rect {
|
fn get_rect(&self, viewport_size: Vec2) -> Rect {
|
||||||
let origin;
|
//let origin;
|
||||||
let size;
|
let size;
|
||||||
|
|
||||||
match self.scale_mode {
|
match self.scale_mode {
|
||||||
ScaleMode::Viewport => {
|
ScaleMode::Viewport => {
|
||||||
origin = viewport_size * 0.5;
|
|
||||||
size = viewport_size;
|
size = viewport_size;
|
||||||
},
|
},
|
||||||
ScaleMode::Width(width) => {
|
ScaleMode::Width(width) => {
|
||||||
let aspect = viewport_size.x / viewport_size.y;
|
let aspect = viewport_size.x / viewport_size.y;
|
||||||
let origin_x = width * 0.5;
|
|
||||||
let scaled_height = width / aspect;
|
let scaled_height = width / aspect;
|
||||||
let origin_y = scaled_height * 0.5;
|
|
||||||
|
|
||||||
origin = Vec2::new(origin_x, origin_y);
|
|
||||||
size = Vec2::new(width, scaled_height);
|
size = Vec2::new(width, scaled_height);
|
||||||
},
|
},
|
||||||
ScaleMode::Height(height) => {
|
ScaleMode::Height(height) => {
|
||||||
let aspect = viewport_size.x / viewport_size.y;
|
let aspect = viewport_size.x / viewport_size.y;
|
||||||
let origin_y = height * 0.5;
|
|
||||||
let scaled_width = height * aspect;
|
let scaled_width = height * aspect;
|
||||||
let origin_x = scaled_width * 0.5;
|
|
||||||
|
|
||||||
origin = Vec2::new(origin_x, origin_y);
|
|
||||||
size = Vec2::new(scaled_width, height);
|
size = Vec2::new(scaled_width, height);
|
||||||
},
|
},
|
||||||
ScaleMode::Size(s) => {
|
ScaleMode::Size(s) => {
|
||||||
origin = s * 0.5;
|
|
||||||
size = s;
|
size = s;
|
||||||
},
|
},
|
||||||
ScaleMode::MaxSize(s) => {
|
ScaleMode::MaxSize(s) => {
|
||||||
let clamped = s.min(viewport_size);
|
let clamped = s.min(viewport_size);
|
||||||
origin = clamped * 0.5;
|
|
||||||
size = clamped;
|
size = clamped;
|
||||||
},
|
},
|
||||||
ScaleMode::MinSize(s) => {
|
ScaleMode::MinSize(s) => {
|
||||||
let clamped = s.max(viewport_size);
|
let clamped = s.max(viewport_size);
|
||||||
origin = clamped * 0.5;
|
|
||||||
size = clamped;
|
size = clamped;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let origin = size * self.viewport_origin;
|
||||||
|
|
||||||
Rect::new(self.scale * -origin.x,
|
Rect::new(self.scale * -origin.x,
|
||||||
self.scale * -origin.y,
|
self.scale * -origin.y,
|
||||||
self.scale * size.x - origin.x,
|
self.scale * (size.x - origin.x),
|
||||||
self.scale * size.y - origin.y)
|
self.scale * (size.y - origin.y))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_mat(&self, viewport_size: Vec2) -> Mat4 {
|
pub fn to_mat(&self, viewport_size: Vec2) -> Mat4 {
|
||||||
|
|
|
@ -12,7 +12,7 @@ use tracing::error;
|
||||||
|
|
||||||
use crate::DeltaTime;
|
use crate::DeltaTime;
|
||||||
|
|
||||||
use super::{AtlasSprite, TextureAtlas};
|
use super::{AtlasSprite, Pivot, TextureAtlas};
|
||||||
|
|
||||||
/// A struct describing an animation of a Sprite.
|
/// A struct describing an animation of a Sprite.
|
||||||
///
|
///
|
||||||
|
@ -266,6 +266,7 @@ fn system_animation_entity_impl(
|
||||||
let sprite = AtlasSprite {
|
let sprite = AtlasSprite {
|
||||||
atlas: animations.atlas.clone(),
|
atlas: animations.atlas.clone(),
|
||||||
sprite: rect,
|
sprite: rect,
|
||||||
|
pivot: Pivot::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
commands.insert(en, sprite);
|
commands.insert(en, sprite);
|
||||||
|
@ -286,6 +287,7 @@ fn system_animation_entity_impl(
|
||||||
let new_sprite = AtlasSprite {
|
let new_sprite = AtlasSprite {
|
||||||
atlas: animations.atlas.clone(),
|
atlas: animations.atlas.clone(),
|
||||||
sprite: rect,
|
sprite: rect,
|
||||||
|
pivot: Pivot::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let sprite = sprite.as_mut().unwrap();
|
let sprite = sprite.as_mut().unwrap();
|
||||||
|
|
|
@ -24,8 +24,9 @@ pub enum Pivot {
|
||||||
BottomLeft,
|
BottomLeft,
|
||||||
BottomRight,
|
BottomRight,
|
||||||
BottomCenter,
|
BottomCenter,
|
||||||
/// A custom anchor point relative to top left.
|
/// A custom anchor point.
|
||||||
/// Top left is `(0.0, 0.0)`.
|
///
|
||||||
|
/// Top left is (-0.5, 0.5), center is (0.0, 0.0).
|
||||||
Custom(Vec2)
|
Custom(Vec2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,15 +36,15 @@ impl Pivot {
|
||||||
/// The point is offset from the top left `(0.0, 0.0)`.
|
/// The point is offset from the top left `(0.0, 0.0)`.
|
||||||
pub fn as_vec(&self) -> Vec2 {
|
pub fn as_vec(&self) -> Vec2 {
|
||||||
match self {
|
match self {
|
||||||
Pivot::Center => Vec2::new(0.5, 0.5),
|
Pivot::Center => Vec2::ZERO,
|
||||||
Pivot::CenterLeft => Vec2::new(0.0, 0.5),
|
Pivot::CenterLeft => Vec2::new(-0.5, 0.0),
|
||||||
Pivot::CenterRight => Vec2::new(1.0, 0.5),
|
Pivot::CenterRight => Vec2::new(0.5, 0.0),
|
||||||
Pivot::TopLeft => Vec2::ZERO,
|
Pivot::TopLeft => Vec2::new(-0.5, 0.5),
|
||||||
Pivot::TopRight => Vec2::new(1.0, 0.0),
|
Pivot::TopRight => Vec2::new(0.5, 0.5),
|
||||||
Pivot::TopCenter => Vec2::new(0.0, 0.5),
|
Pivot::TopCenter => Vec2::new(0.0, 0.5),
|
||||||
Pivot::BottomLeft => Vec2::new(0.0, 1.0),
|
Pivot::BottomLeft => Vec2::new(-0.5, -0.5),
|
||||||
Pivot::BottomRight => Vec2::new(1.0, 1.0),
|
Pivot::BottomRight => Vec2::new(0.5, -0.5),
|
||||||
Pivot::BottomCenter => Vec2::new(0.5, 1.0),
|
Pivot::BottomCenter => Vec2::new(0.0, -0.5),
|
||||||
Pivot::Custom(v) => *v,
|
Pivot::Custom(v) => *v,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,8 @@ use lyra_math::URect;
|
||||||
use lyra_reflect::Reflect;
|
use lyra_reflect::Reflect;
|
||||||
use lyra_resource::ResHandle;
|
use lyra_resource::ResHandle;
|
||||||
|
|
||||||
|
use super::Pivot;
|
||||||
|
|
||||||
/// A texture atlas of multiple sprites.
|
/// A texture atlas of multiple sprites.
|
||||||
#[derive(Clone, Component, Reflect)]
|
#[derive(Clone, Component, Reflect)]
|
||||||
pub struct TextureAtlas {
|
pub struct TextureAtlas {
|
||||||
|
@ -60,17 +62,19 @@ impl lyra_resource::ResourceData for TextureAtlas {
|
||||||
pub struct AtlasSprite {
|
pub struct AtlasSprite {
|
||||||
pub atlas: ResHandle<TextureAtlas>,
|
pub atlas: ResHandle<TextureAtlas>,
|
||||||
pub sprite: URect,
|
pub sprite: URect,
|
||||||
|
pub pivot: Pivot,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AtlasSprite {
|
impl AtlasSprite {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn from_atlas_index(atlas: ResHandle<TextureAtlas>, i: u32) -> Self {
|
pub fn from_atlas_index(atlas: ResHandle<TextureAtlas>, i: u32) -> Self {
|
||||||
let a = atlas.data_ref().unwrap();
|
let a = atlas.data_ref().unwrap();
|
||||||
let rect = a.frames.get(i as usize).cloned().unwrap(); //index_rect(i);
|
let rect = a.frames.get(i as usize).cloned().unwrap();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
atlas: atlas.clone(),
|
atlas: atlas.clone(),
|
||||||
sprite: rect,
|
sprite: rect,
|
||||||
|
pivot: Pivot::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -136,4 +136,26 @@ impl std::ops::Add for Transform {
|
||||||
self.scale *= rhs.scale;
|
self.scale *= rhs.scale;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Sub for Transform {
|
||||||
|
type Output = Transform;
|
||||||
|
|
||||||
|
fn sub(mut self, rhs: Self) -> Self::Output {
|
||||||
|
self.translation -= rhs.translation;
|
||||||
|
self.rotation *= rhs.rotation;
|
||||||
|
self.scale *= rhs.scale;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Mul for Transform {
|
||||||
|
type Output = Transform;
|
||||||
|
|
||||||
|
fn mul(mut self, rhs: Self) -> Self::Output {
|
||||||
|
self.translation *= rhs.translation;
|
||||||
|
self.rotation *= rhs.rotation;
|
||||||
|
self.scale *= rhs.scale;
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue