mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Added pipeline components
This commit is contained in:
parent
d8c87984d5
commit
d559d633ab
25
voxygen/shaders/skybox.frag
Normal file
25
voxygen/shaders/skybox.frag
Normal file
@ -0,0 +1,25 @@
|
||||
#version 330 core
|
||||
|
||||
in vec3 f_pos;
|
||||
|
||||
layout (std140)
|
||||
uniform u_locals {
|
||||
mat4 model_mat;
|
||||
};
|
||||
|
||||
layout (std140)
|
||||
uniform u_globals {
|
||||
mat4 view_mat;
|
||||
mat4 proj_mat;
|
||||
vec4 cam_pos;
|
||||
vec4 focus_pos;
|
||||
vec4 view_distance;
|
||||
vec4 tod;
|
||||
vec4 time;
|
||||
};
|
||||
|
||||
out vec4 tgt_color;
|
||||
|
||||
void main() {
|
||||
tgt_color = vec4(1.0, 0.0, 0.0, 1.0);
|
||||
}
|
30
voxygen/shaders/skybox.vert
Normal file
30
voxygen/shaders/skybox.vert
Normal file
@ -0,0 +1,30 @@
|
||||
#version 330 core
|
||||
|
||||
in vec3 v_pos;
|
||||
|
||||
layout (std140)
|
||||
uniform u_locals {
|
||||
mat4 model_mat;
|
||||
};
|
||||
|
||||
layout (std140)
|
||||
uniform u_globals {
|
||||
mat4 view_mat;
|
||||
mat4 proj_mat;
|
||||
vec4 cam_pos;
|
||||
vec4 focus_pos;
|
||||
vec4 view_distance;
|
||||
vec4 tod;
|
||||
vec4 time;
|
||||
};
|
||||
|
||||
out vec3 f_pos;
|
||||
|
||||
void main() {
|
||||
f_pos = v_pos;
|
||||
|
||||
gl_Position =
|
||||
proj_mat *
|
||||
view_mat *
|
||||
vec4(3000 * v_pos + cam_pos.xyz, 1);
|
||||
}
|
@ -16,14 +16,22 @@ use failure;
|
||||
use crate::{
|
||||
menu::title::TitleState,
|
||||
window::Window,
|
||||
render::RenderErr,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum VoxygenErr {
|
||||
BackendErr(Box<any::Any>),
|
||||
RenderErr(RenderErr),
|
||||
Other(failure::Error),
|
||||
}
|
||||
|
||||
impl From<RenderErr> for VoxygenErr {
|
||||
fn from(err: RenderErr) -> Self {
|
||||
VoxygenErr::RenderErr(err)
|
||||
}
|
||||
}
|
||||
|
||||
// A type used to store state that is shared between all play states
|
||||
pub struct GlobalState {
|
||||
window: Window,
|
||||
|
@ -17,6 +17,8 @@ impl TitleState {
|
||||
}
|
||||
}
|
||||
|
||||
const BG_COLOR: Rgba<f32> = Rgba { r: 0.0, g: 0.3, b: 1.0, a: 1.0 };
|
||||
|
||||
impl PlayState for TitleState {
|
||||
fn play(&mut self, global_state: &mut GlobalState) -> PlayStateResult {
|
||||
'eventloop: loop {
|
||||
@ -27,12 +29,7 @@ impl PlayState for TitleState {
|
||||
}
|
||||
}
|
||||
|
||||
global_state.window.renderer_mut().clear(Rgba::new(
|
||||
0.0,
|
||||
0.3,
|
||||
1.0,
|
||||
1.0,
|
||||
));
|
||||
global_state.window.renderer_mut().clear(BG_COLOR);
|
||||
global_state.window.renderer_mut().flush();
|
||||
global_state.window.display()
|
||||
.expect("Failed to display window");
|
||||
|
33
voxygen/src/render/consts.rs
Normal file
33
voxygen/src/render/consts.rs
Normal file
@ -0,0 +1,33 @@
|
||||
// Library
|
||||
use gfx::{
|
||||
self,
|
||||
traits::FactoryExt,
|
||||
};
|
||||
|
||||
// Local
|
||||
use super::{
|
||||
RenderErr,
|
||||
gfx_backend,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Consts<T: Copy + gfx::traits::Pod> {
|
||||
pub buf: gfx::handle::Buffer<gfx_backend::Resources, T>,
|
||||
}
|
||||
|
||||
impl<T: Copy + gfx::traits::Pod> Consts<T> {
|
||||
pub fn new(factory: &mut gfx_backend::Factory) -> Self {
|
||||
Self {
|
||||
buf: factory.create_constant_buffer(1),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(
|
||||
&mut self,
|
||||
encoder: &mut gfx::Encoder<gfx_backend::Resources, gfx_backend::CommandBuffer>,
|
||||
data: T,
|
||||
) -> Result<(), RenderErr> {
|
||||
encoder.update_buffer(&self.buf, &[data], 0)
|
||||
.map_err(|err| RenderErr::UpdateErr(err))
|
||||
}
|
||||
}
|
@ -7,15 +7,46 @@ pub struct Mesh<P: Pipeline> {
|
||||
}
|
||||
|
||||
impl<P: Pipeline> Mesh<P> {
|
||||
pub fn empty() -> Self {
|
||||
pub fn new() -> Self {
|
||||
Self { verts: vec![] }
|
||||
}
|
||||
|
||||
pub fn verts(&self) -> &[P::Vertex] {
|
||||
pub fn vertices(&self) -> &[P::Vertex] {
|
||||
&self.verts
|
||||
}
|
||||
|
||||
pub fn push(&mut self, vert: P::Vertex) {
|
||||
self.verts.push(vert);
|
||||
}
|
||||
|
||||
pub fn push_quad(&mut self, quad: Quad<P>) {
|
||||
// Tri 1
|
||||
self.verts.push(quad.a.clone());
|
||||
self.verts.push(quad.b);
|
||||
self.verts.push(quad.c.clone());
|
||||
|
||||
// Tri 2
|
||||
self.verts.push(quad.c);
|
||||
self.verts.push(quad.d);
|
||||
self.verts.push(quad.a);
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a quad
|
||||
pub struct Quad<P: Pipeline> {
|
||||
a: P::Vertex,
|
||||
b: P::Vertex,
|
||||
c: P::Vertex,
|
||||
d: P::Vertex,
|
||||
}
|
||||
|
||||
impl<P: Pipeline> Quad<P> {
|
||||
pub fn new(
|
||||
a: P::Vertex,
|
||||
b: P::Vertex,
|
||||
c: P::Vertex,
|
||||
d: P::Vertex,
|
||||
) -> Self {
|
||||
Self { a, b, c, d }
|
||||
}
|
||||
}
|
||||
|
@ -1,25 +1,38 @@
|
||||
mod consts;
|
||||
mod mesh;
|
||||
mod model;
|
||||
mod renderer;
|
||||
mod pipelines;
|
||||
mod shader_set;
|
||||
mod renderer;
|
||||
|
||||
// Reexports
|
||||
pub use self::{
|
||||
mesh::Mesh,
|
||||
consts::Consts,
|
||||
mesh::{Mesh, Quad},
|
||||
model::Model,
|
||||
shader_set::ShaderSet,
|
||||
renderer::{Renderer, TgtColorFmt, TgtDepthFmt},
|
||||
pipelines::{
|
||||
character::CharacterPipeline,
|
||||
skybox::SkyboxPipeline,
|
||||
},
|
||||
};
|
||||
|
||||
#[cfg(feature = "gl")]
|
||||
use gfx_device_gl as gfx_backend;
|
||||
|
||||
// Library
|
||||
use gfx;
|
||||
|
||||
/// Used to represent one of many possible errors that may be omitted by the rendering code
|
||||
#[derive(Debug)]
|
||||
pub enum RenderErr {}
|
||||
pub enum RenderErr {
|
||||
PipelineErr(gfx::PipelineStateError<String>),
|
||||
UpdateErr(gfx::UpdateError<usize>),
|
||||
}
|
||||
|
||||
/// Used to represent a specific rendering configuration
|
||||
pub trait Pipeline {
|
||||
type Vertex;
|
||||
type Vertex:
|
||||
Clone +
|
||||
gfx::traits::Pod +
|
||||
gfx::pso::buffer::Structure<gfx::format::Format>;
|
||||
}
|
||||
|
@ -1,18 +1,36 @@
|
||||
// Standard
|
||||
use std::marker::PhantomData;
|
||||
// Library
|
||||
use gfx::{
|
||||
self,
|
||||
traits::FactoryExt,
|
||||
};
|
||||
|
||||
// Local
|
||||
use super::Pipeline;
|
||||
use super::{
|
||||
mesh::Mesh,
|
||||
Pipeline,
|
||||
gfx_backend,
|
||||
};
|
||||
|
||||
/// Represents a mesh that has been sent to the CPU
|
||||
pub struct Model<P: Pipeline> {
|
||||
phantom: PhantomData<P>,
|
||||
pub vbuf: gfx::handle::Buffer<gfx_backend::Resources, P::Vertex>,
|
||||
pub slice: gfx::Slice<gfx_backend::Resources>,
|
||||
}
|
||||
|
||||
impl<P: Pipeline> Model<P> {
|
||||
pub fn new() -> Self {
|
||||
pub fn new(
|
||||
factory: &mut gfx_backend::Factory,
|
||||
mesh: &Mesh<P>,
|
||||
) -> Self {
|
||||
Self {
|
||||
phantom: PhantomData,
|
||||
vbuf: factory.create_vertex_buffer(mesh.vertices()),
|
||||
slice: gfx::Slice {
|
||||
start: 0,
|
||||
end: mesh.vertices().len() as u32,
|
||||
base_vertex: 0,
|
||||
instances: None,
|
||||
buffer: gfx::IndexBuffer::Auto,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,6 @@
|
||||
// Library
|
||||
use gfx::{
|
||||
self,
|
||||
VertexBuffer,
|
||||
ConstantBuffer,
|
||||
RenderTarget,
|
||||
DepthTarget,
|
||||
preset::depth::LESS_EQUAL_WRITE,
|
||||
// Macros
|
||||
gfx_defines,
|
||||
gfx_vertex_struct_meta,
|
||||
@ -16,8 +11,13 @@ use gfx::{
|
||||
};
|
||||
|
||||
// Local
|
||||
use super::super::{
|
||||
renderer::{TgtColorFmt, TgtDepthFmt},
|
||||
use super::{
|
||||
Globals,
|
||||
super::{
|
||||
Pipeline,
|
||||
TgtColorFmt,
|
||||
TgtDepthFmt,
|
||||
},
|
||||
};
|
||||
|
||||
gfx_defines! {
|
||||
@ -27,13 +27,20 @@ gfx_defines! {
|
||||
}
|
||||
|
||||
constant Locals {
|
||||
model: [[f32; 4]; 4] = "u_model",
|
||||
model_mat: [[f32; 4]; 4] = "model_mat",
|
||||
}
|
||||
|
||||
pipeline pipe {
|
||||
vbuf: VertexBuffer<Vertex> = (),
|
||||
locals: ConstantBuffer<Locals> = "locals",
|
||||
tgt_color: RenderTarget<TgtColorFmt> = "tgt",
|
||||
tgt_depth: DepthTarget<TgtDepthFmt> = LESS_EQUAL_WRITE,
|
||||
vbuf: gfx::VertexBuffer<Vertex> = (),
|
||||
locals: gfx::ConstantBuffer<Locals> = "u_locals",
|
||||
globals: gfx::ConstantBuffer<Globals> = "u_globals",
|
||||
tgt_color: gfx::RenderTarget<TgtColorFmt> = "tgt_color",
|
||||
tgt_depth: gfx::DepthTarget<TgtDepthFmt> = gfx::preset::depth::LESS_EQUAL_WRITE,
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CharacterPipeline;
|
||||
|
||||
impl Pipeline for CharacterPipeline {
|
||||
type Vertex = Vertex;
|
||||
}
|
||||
|
@ -1 +1,26 @@
|
||||
mod character;
|
||||
pub mod character;
|
||||
pub mod skybox;
|
||||
|
||||
// Library
|
||||
use gfx::{
|
||||
self,
|
||||
// Macros
|
||||
gfx_defines,
|
||||
gfx_vertex_struct_meta,
|
||||
gfx_constant_struct_meta,
|
||||
gfx_impl_struct_meta,
|
||||
gfx_pipeline,
|
||||
gfx_pipeline_inner,
|
||||
};
|
||||
|
||||
gfx_defines! {
|
||||
constant Globals {
|
||||
view_mat: [[f32; 4]; 4] = "view_mat",
|
||||
proj_mat: [[f32; 4]; 4] = "proj_mat",
|
||||
cam_pos: [f32; 4] = "cam_pos",
|
||||
focus_pos: [f32; 4] = "focus_pos",
|
||||
view_distance: [f32; 4] = "view_distance",
|
||||
tod: [f32; 4] = "tod",
|
||||
time: [f32; 4] = "time",
|
||||
}
|
||||
}
|
||||
|
102
voxygen/src/render/pipelines/skybox.rs
Normal file
102
voxygen/src/render/pipelines/skybox.rs
Normal file
@ -0,0 +1,102 @@
|
||||
// Library
|
||||
use gfx::{
|
||||
self,
|
||||
// Macros
|
||||
gfx_defines,
|
||||
gfx_vertex_struct_meta,
|
||||
gfx_constant_struct_meta,
|
||||
gfx_impl_struct_meta,
|
||||
gfx_pipeline,
|
||||
gfx_pipeline_inner,
|
||||
};
|
||||
|
||||
// Local
|
||||
use super::{
|
||||
Globals,
|
||||
super::{
|
||||
Pipeline,
|
||||
TgtColorFmt,
|
||||
TgtDepthFmt,
|
||||
Mesh,
|
||||
Quad,
|
||||
},
|
||||
};
|
||||
|
||||
gfx_defines! {
|
||||
vertex Vertex {
|
||||
pos: [f32; 3] = "v_pos",
|
||||
}
|
||||
|
||||
constant Locals {
|
||||
model_mat: [[f32; 4]; 4] = "model_mat",
|
||||
}
|
||||
|
||||
pipeline pipe {
|
||||
vbuf: gfx::VertexBuffer<Vertex> = (),
|
||||
locals: gfx::ConstantBuffer<Locals> = "u_locals",
|
||||
globals: gfx::ConstantBuffer<Globals> = "u_globals",
|
||||
tgt_color: gfx::RenderTarget<TgtColorFmt> = "tgt_color",
|
||||
tgt_depth: gfx::DepthTarget<TgtDepthFmt> = gfx::preset::depth::LESS_EQUAL_WRITE,
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SkyboxPipeline;
|
||||
|
||||
impl Pipeline for SkyboxPipeline {
|
||||
type Vertex = Vertex;
|
||||
}
|
||||
|
||||
pub fn create_mesh() -> Mesh<SkyboxPipeline> {
|
||||
let mut mesh = Mesh::new();
|
||||
|
||||
// -x
|
||||
#[rustfmt::skip]
|
||||
mesh.push_quad(Quad::new(
|
||||
Vertex { pos: [-1.0, -1.0, -1.0] },
|
||||
Vertex { pos: [-1.0, 1.0, -1.0] },
|
||||
Vertex { pos: [-1.0, 1.0, 1.0] },
|
||||
Vertex { pos: [-1.0, -1.0, 1.0] },
|
||||
));
|
||||
// +x
|
||||
#[rustfmt::skip]
|
||||
mesh.push_quad(Quad::new(
|
||||
Vertex { pos: [ 1.0, -1.0, -1.0] },
|
||||
Vertex { pos: [ 1.0, 1.0, 1.0] },
|
||||
Vertex { pos: [ 1.0, 1.0, -1.0] },
|
||||
Vertex { pos: [ 1.0, -1.0, 1.0] },
|
||||
));
|
||||
// -y
|
||||
#[rustfmt::skip]
|
||||
mesh.push_quad(Quad::new(
|
||||
Vertex { pos: [ 1.0, -1.0, -1.0] },
|
||||
Vertex { pos: [-1.0, -1.0, -1.0] },
|
||||
Vertex { pos: [-1.0, -1.0, 1.0] },
|
||||
Vertex { pos: [ 1.0, -1.0, 1.0] },
|
||||
));
|
||||
// +y
|
||||
#[rustfmt::skip]
|
||||
mesh.push_quad(Quad::new(
|
||||
Vertex { pos: [ 1.0, 1.0, -1.0] },
|
||||
Vertex { pos: [-1.0, 1.0, 1.0] },
|
||||
Vertex { pos: [-1.0, 1.0, -1.0] },
|
||||
Vertex { pos: [ 1.0, 1.0, 1.0] },
|
||||
));
|
||||
// -z
|
||||
#[rustfmt::skip]
|
||||
mesh.push_quad(Quad::new(
|
||||
Vertex { pos: [-1.0, -1.0, -1.0] },
|
||||
Vertex { pos: [ 1.0, -1.0, -1.0] },
|
||||
Vertex { pos: [ 1.0, 1.0, -1.0] },
|
||||
Vertex { pos: [-1.0, 1.0, -1.0] },
|
||||
));
|
||||
// +z
|
||||
#[rustfmt::skip]
|
||||
mesh.push_quad(Quad::new(
|
||||
Vertex { pos: [-1.0, -1.0, 1.0] },
|
||||
Vertex { pos: [ 1.0, 1.0, 1.0] },
|
||||
Vertex { pos: [ 1.0, -1.0, 1.0] },
|
||||
Vertex { pos: [-1.0, 1.0, 1.0] },
|
||||
));
|
||||
|
||||
mesh
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
use vek::*;
|
||||
use gfx::{
|
||||
self,
|
||||
traits::Device,
|
||||
traits::{Device, FactoryExt},
|
||||
};
|
||||
|
||||
// Crate
|
||||
@ -10,12 +10,17 @@ use crate::VoxygenErr;
|
||||
|
||||
// Local
|
||||
use super::{
|
||||
consts::Consts,
|
||||
model::Model,
|
||||
mesh::Mesh,
|
||||
shader_set::ShaderSet,
|
||||
Pipeline,
|
||||
RenderErr,
|
||||
gfx_backend,
|
||||
pipelines::{
|
||||
Globals,
|
||||
character,
|
||||
skybox,
|
||||
},
|
||||
};
|
||||
|
||||
pub type TgtColorFmt = gfx::format::Srgba8;
|
||||
@ -31,6 +36,9 @@ pub struct Renderer {
|
||||
|
||||
tgt_color_view: TgtColorView,
|
||||
tgt_depth_view: TgtDepthView,
|
||||
|
||||
skybox_pipeline: GfxPipeline<skybox::pipe::Init<'static>>,
|
||||
//character_pipeline: GfxPipeline<character::pipe::Init<'static>>,
|
||||
}
|
||||
|
||||
impl Renderer {
|
||||
@ -39,13 +47,35 @@ impl Renderer {
|
||||
mut factory: gfx_backend::Factory,
|
||||
tgt_color_view: TgtColorView,
|
||||
tgt_depth_view: TgtDepthView,
|
||||
) -> Result<Self, VoxygenErr> {
|
||||
) -> Result<Self, RenderErr> {
|
||||
// Construct a pipeline for rendering skyboxes
|
||||
let skybox_pipeline = Self::create_pipeline(
|
||||
&mut factory,
|
||||
skybox::pipe::new(),
|
||||
include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/shaders/skybox.vert")),
|
||||
include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/shaders/skybox.frag")),
|
||||
)?;
|
||||
|
||||
// Construct a pipeline for rendering characters
|
||||
/*
|
||||
let character_pipeline = Self::new_pipeline(
|
||||
&mut factory,
|
||||
character::pipe::new(),
|
||||
include_bytes!("shaders/character.vert"),
|
||||
include_bytes!("shaders/character.frag"),
|
||||
)?;
|
||||
*/
|
||||
|
||||
Ok(Self {
|
||||
device,
|
||||
encoder: factory.create_command_buffer().into(),
|
||||
factory,
|
||||
|
||||
tgt_color_view,
|
||||
tgt_depth_view,
|
||||
|
||||
skybox_pipeline,
|
||||
//character_pipeline,
|
||||
})
|
||||
}
|
||||
|
||||
@ -54,8 +84,76 @@ impl Renderer {
|
||||
self.encoder.clear_depth(&self.tgt_depth_view, 1.0);
|
||||
}
|
||||
|
||||
/// Perform all queued draw calls for this frame and clean up discarded items
|
||||
pub fn flush(&mut self) {
|
||||
self.encoder.flush(&mut self.device);
|
||||
self.device.cleanup();
|
||||
}
|
||||
|
||||
/// Create a new pipeline from the provided vertex shader and fragment shader
|
||||
fn create_pipeline<'a, P: gfx::pso::PipelineInit>(
|
||||
factory: &mut gfx_backend::Factory,
|
||||
pipe: P,
|
||||
vs: &[u8],
|
||||
fs: &[u8],
|
||||
) -> Result<GfxPipeline<P>, RenderErr> {
|
||||
let program = factory
|
||||
.link_program(vs, fs)
|
||||
.map_err(|err| RenderErr::PipelineErr(gfx::PipelineStateError::Program(err)))?;
|
||||
|
||||
Ok(GfxPipeline {
|
||||
pso: factory.create_pipeline_from_program(
|
||||
&program,
|
||||
gfx::Primitive::TriangleList,
|
||||
gfx::state::Rasterizer {
|
||||
front_face: gfx::state::FrontFace::CounterClockwise,
|
||||
cull_face: gfx::state::CullFace::Back,
|
||||
method: gfx::state::RasterMethod::Fill,
|
||||
offset: None,
|
||||
samples: Some(gfx::state::MultiSample),
|
||||
},
|
||||
pipe,
|
||||
)
|
||||
// Do some funky things to work around an oddity in gfx's error ownership rules
|
||||
.map_err(|err| RenderErr::PipelineErr(match err {
|
||||
gfx::PipelineStateError::Program(err) => gfx::PipelineStateError::Program(err),
|
||||
gfx::PipelineStateError::DescriptorInit(err) => gfx::PipelineStateError::DescriptorInit(err.into()),
|
||||
gfx::PipelineStateError::DeviceCreate(err) => gfx::PipelineStateError::DeviceCreate(err),
|
||||
}))?,
|
||||
program,
|
||||
})
|
||||
}
|
||||
|
||||
/// Create a new model from the provided mesh
|
||||
pub fn create_model<P: Pipeline>(&mut self, mesh: &Mesh<P>) -> Result<Model<P>, RenderErr> {
|
||||
Ok(Model::new(
|
||||
&mut self.factory,
|
||||
mesh,
|
||||
))
|
||||
}
|
||||
|
||||
/// Queue the rendering of the provided skybox model in the upcoming frame
|
||||
pub fn render_skybox(
|
||||
&mut self,
|
||||
model: &Model<skybox::SkyboxPipeline>,
|
||||
locals: &Consts<skybox::Locals>,
|
||||
globals: &Consts<Globals>,
|
||||
) {
|
||||
self.encoder.draw(
|
||||
&model.slice,
|
||||
&self.skybox_pipeline.pso,
|
||||
&skybox::pipe::Data {
|
||||
vbuf: model.vbuf.clone(),
|
||||
locals: locals.buf.clone(),
|
||||
globals: globals.buf.clone(),
|
||||
tgt_color: self.tgt_color_view.clone(),
|
||||
tgt_depth: self.tgt_depth_view.clone(),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GfxPipeline<P: gfx::pso::PipelineInit> {
|
||||
program: gfx::handle::Program<gfx_backend::Resources>,
|
||||
pso: gfx::pso::PipelineState<gfx_backend::Resources, P::Meta>,
|
||||
}
|
||||
|
@ -1,23 +0,0 @@
|
||||
// Standard
|
||||
use std::marker::PhantomData;
|
||||
|
||||
// Local
|
||||
use super::{
|
||||
Pipeline,
|
||||
RenderErr
|
||||
};
|
||||
|
||||
pub struct ShaderSet<P: Pipeline> {
|
||||
phantom: PhantomData<P>,
|
||||
}
|
||||
|
||||
impl<P: Pipeline> ShaderSet<P> {
|
||||
pub fn new(
|
||||
vs: &[u8],
|
||||
fs: &[u8],
|
||||
) -> Result<Self, RenderErr> {
|
||||
Ok(Self {
|
||||
phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user