Browse Source

Use vertex array and geometry shader to render asteroids

master
Bergmann89 3 years ago
parent
commit
5ac3940358
6 changed files with 187 additions and 70 deletions
  1. +6
    -6
      space-crush-app/resources/shader/asteroid/frag.glsl
  2. +46
    -0
      space-crush-app/resources/shader/asteroid/geom.glsl
  3. +10
    -0
      space-crush-app/resources/shader/asteroid/shared.glsl
  4. +9
    -8
      space-crush-app/resources/shader/asteroid/vert.glsl
  5. +113
    -55
      space-crush-app/src/render/asteroids.rs
  6. +3
    -1
      space-crush-common/src/components/asteroid.rs

+ 6
- 6
space-crush-app/resources/shader/asteroid/frag.glsl View File

@@ -12,17 +12,17 @@ const GlowArgs GLOW_ARGS = {
/* pulseTime */ 2.000,
};

in FragmentData fragmentData;
in FragmentData fragmentData;
flat in int textureId;

uniform vec4 uGlowColor;
uniform sampler2D uTexture;
uniform sampler2D uTexture[2];

out vec4 outColor;

void main() {
float alpha = glow(GLOW_ARGS, fragmentData.texCoords - vec2(0.5), uGlobal.time);
vec4 glow = vec4(uGlowColor.rgb, uGlowColor.a * alpha);
vec4 tex = texture(uTexture, fragmentData.texCoords);
float alpha = glow(GLOW_ARGS, fragmentData.texCoords, uGlobal.time);
vec4 glow = vec4(fragmentData.color.rgb, fragmentData.color.a * alpha);
vec4 tex = texture(uTexture[textureId], 0.5 * GLOW_SIZE * fragmentData.texCoords + vec2(0.5));

outColor = tex * tex.a + glow * (1.0 - tex.a);
}

+ 46
- 0
space-crush-app/resources/shader/asteroid/geom.glsl View File

@@ -0,0 +1,46 @@
#version 450 core

#pragma include ./shared.glsl
#pragma include ../misc/camera.glsl

in VertexData vertexData[];

layout (points) in;
layout (triangle_strip, max_vertices = 4) out;

out FragmentData fragmentData;
flat out int textureId;

void main() {
VertexData d = vertexData[0];

vec2 pos = d.pos;
float size = d.size * GLOW_SIZE;

mat4 m = uCamera.projection * uCamera.view * mat4(
vec4( size, 0.0, 0.0, 0.0),
vec4( 0.0, size, 0.0, 0.0),
vec4( 0.0, 0.0, 0.0, 0.0),
vec4(pos.x, pos.y, 0.0, 1.0));

textureId = d.texture;
fragmentData.color = d.color;

gl_Position = m * vec4(-1.0, -1.0, 0.0, 1.0);
fragmentData.texCoords = vec2(-1.0, 1.0);
EmitVertex();

gl_Position = m * vec4(-1.0, 1.0, 0.0, 1.0);
fragmentData.texCoords = vec2(-1.0, -1.0);
EmitVertex();

gl_Position = m * vec4( 1.0, -1.0, 0.0, 1.0);
fragmentData.texCoords = vec2( 1.0, 1.0);
EmitVertex();

gl_Position = m * vec4( 1.0, 1.0, 0.0, 1.0);
fragmentData.texCoords = vec2( 1.0, -1.0);
EmitVertex();

EndPrimitive();
}

+ 10
- 0
space-crush-app/resources/shader/asteroid/shared.glsl View File

@@ -1,3 +1,13 @@
struct FragmentData {
vec2 texCoords;
vec4 color;
};

struct VertexData {
vec2 pos;
float size;
vec4 color;
int texture;
};

const float GLOW_SIZE = 2.0; // relative to planet size

+ 9
- 8
space-crush-app/resources/shader/asteroid/vert.glsl View File

@@ -3,15 +3,16 @@
#pragma include ./shared.glsl
#pragma include ../misc/camera.glsl

const float GLOW_SIZE_FACTOR = 2.00;
layout (location = 0) in vec2 inPosition;
layout (location = 1) in float inSize;
layout (location = 2) in vec4 inColor;
layout (location = 3) in int inTexture;

in vec3 inPosition;

uniform mat4 uModel;

out FragmentData fragmentData;
out VertexData vertexData;

void main() {
fragmentData.texCoords = inPosition.xy * GLOW_SIZE_FACTOR + vec2(0.5);
gl_Position = uCamera.projection * uCamera.view * uModel * vec4(2.0 * inPosition * GLOW_SIZE_FACTOR, 1.0);
vertexData.pos = inPosition;
vertexData.size = inSize;
vertexData.color = inColor;
vertexData.texture = inTexture;
}

+ 113
- 55
space-crush-app/src/render/asteroids.rs View File

@@ -1,15 +1,18 @@
use std::mem::size_of;

use glc::{
matrix::Matrix4f,
buffer::{Buffer, Usage},
misc::{BindGuard, Bindable},
shader::{Program, Type, Uniform},
texture::Texture,
vector::Vector4f,
vector::{Vector2f, Vector4f},
vertex_array::{DataType, VertexArray},
};
use space_crush_common::{
components::{Asteroid, AsteroidType, Player, PlayerOwned, Position},
misc::LogResult,
misc::{ComponentEvent, LogResult, StorageHelper, StorageHelperMut},
};
use specs::{prelude::*, ReadExpect, ReadStorage, System, World};
use specs::{prelude::*, ReadStorage, System, World};

use crate::{
constants::{
@@ -17,7 +20,6 @@ use crate::{
UNIFORM_BUFFER_INDEX_UNIFORM,
},
misc::WorldHelper,
resources::Geometry,
Error,
};

@@ -25,47 +27,76 @@ pub struct Asteroids {
program: Program,
texture_metal: Texture,
texture_crystal: Texture,
location_model: gl::GLint,
location_glow_color: gl::GLint,
vertex_array: VertexArray,
reader_id: ReaderId<ComponentEvent<Asteroid>>,
asteroid_count: usize,
}

#[repr(C, packed)]
struct VertexData {
pos: Vector2f,
size: gl::GLfloat,
color: Vector4f,
texture: gl::GLint,
}

impl Asteroids {
pub fn new(world: &World) -> Result<Self, Error> {
pub fn new(world: &mut World) -> Result<Self, Error> {
WriteStorage::<Asteroid>::setup(world);

let program = world.load_program(vec![
(Type::Vertex, "resources/shader/asteroid/vert.glsl"),
(Type::Geometry, "resources/shader/asteroid/geom.glsl"),
(Type::Fragment, "resources/shader/asteroid/frag.glsl"),
])?;

program.uniform_block_binding("Camera", UNIFORM_BUFFER_INDEX_CAMERA)?;
program.uniform_block_binding("Global", UNIFORM_BUFFER_INDEX_UNIFORM)?;

let location_model = program.uniform_location("uModel")?;
let location_glow_color = program.uniform_location("uGlowColor")?;

program.bind();
program.uniform("uTexture", Uniform::Texture(0))?;
program.uniform("uTexture", Uniform::TextureVec(&[0, 1]))?;
program.unbind();

let texture_metal = world.load_texture("resources/textures/asteroid_metal.png")?;
let texture_crystal = world.load_texture("resources/textures/asteroid_crystal.png")?;

const STRIDE: gl::GLsizei = size_of::<VertexData>() as gl::GLsizei;

const OFFSET_POS: gl::GLsizei = 0;
const OFFSET_SIZ: gl::GLsizei = OFFSET_POS + size_of::<Vector2f>() as gl::GLsizei;
const OFFSET_CLR: gl::GLsizei = OFFSET_SIZ + size_of::<gl::GLfloat>() as gl::GLsizei;
const OFFSET_TEX: gl::GLsizei = OFFSET_CLR + size_of::<Vector4f>() as gl::GLsizei;

let vertex_array = VertexArray::builder()
.bind_buffer(Buffer::new()?)
.vertex_attrib_pointer(0, 2, DataType::Float, false, STRIDE, OFFSET_POS)?
.vertex_attrib_pointer(1, 1, DataType::Float, false, STRIDE, OFFSET_SIZ)?
.vertex_attrib_pointer(2, 4, DataType::Float, false, STRIDE, OFFSET_CLR)?
.vertex_attrib_pointer(3, 1, DataType::Int, false, STRIDE, OFFSET_TEX)?
.build()?;

let reader_id = world
.system_data::<WriteStorage<Asteroid>>()
.register_event_reader();
let asteroid_count = 0;

Ok(Self {
program,
texture_metal,
texture_crystal,
location_model,
location_glow_color,
vertex_array,
reader_id,
asteroid_count,
})
}
}

#[derive(SystemData)]
pub struct AsteroidsData<'a> {
geometry: ReadExpect<'a, Geometry>,
position: ReadStorage<'a, Position>,
asteroid: ReadStorage<'a, Asteroid>,
positions: ReadStorage<'a, Position>,
asteroids: ReadStorage<'a, Asteroid>,
players: ReadStorage<'a, Player>,
owned: ReadStorage<'a, PlayerOwned>,
player: ReadStorage<'a, Player>,
}

impl<'a> System<'a> for Asteroids {
@@ -73,50 +104,77 @@ impl<'a> System<'a> for Asteroids {

fn run(&mut self, data: Self::SystemData) {
let AsteroidsData {
geometry,
position,
asteroid,
positions,
asteroids,
players,
owned,
player,
} = data;

gl::enable(gl::BLEND);
gl::blend_func(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);
/* handle events */
let mut need_update = false;
let events = asteroids.channel().read(&mut self.reader_id);
for event in events {
match event {
ComponentEvent::Inserted(_, _) => {
need_update = true;

self.asteroid_count += 1;
}
ComponentEvent::Removed(_, _) => {
need_update = true;

self.asteroid_count -= 1;
}
ComponentEvent::Modified(_, _) => {
unreachable!("Updates of the asteroid component should not be tracked!")
}
}
}

/* update vertex array */
let buffer = &mut self.vertex_array.buffers_mut()[0];
if need_update {
buffer
.buffer_size(
Usage::StaticDraw,
self.asteroid_count * size_of::<VertexData>(),
)
.panic("Unable to change buffer size for asteroid data");

let data = (&positions, &asteroids, owned.maybe());
let mut buffer = buffer
.map_mut::<VertexData>(true)
.panic("Unable to map buffer for asteroid data");

for (i, (position, asteroid, owned)) in data.join().enumerate() {
let mut d = &mut buffer[i];

d.pos = *position.pos();
d.size = position.shape().circle().unwrap_or(ASTEROID_SIZE);
d.color = match owned.and_then(|owned| players.get(owned.owner())) {
Some(pv) => *pv.color(),
None => PLAYER_COLOR_DEFAULT,
};
d.texture = match asteroid.type_() {
AsteroidType::Metal => 0,
AsteroidType::Crystal => 1,
};
}
}

/* render asteroids */
let _guard = BindGuard::new(&self.program);
let _guard = BindGuard::new(&self.vertex_array);

for (position, asteroid, owned) in (&position, &asteroid, owned.maybe()).join() {
let p_x = position.pos().x;
let p_y = position.pos().y;
let s = position.shape().circle().unwrap_or(ASTEROID_SIZE);

let _guard = match asteroid.type_() {
AsteroidType::Metal => BindGuard::new(&self.texture_metal),
AsteroidType::Crystal => BindGuard::new(&self.texture_crystal),
};

let c = match owned.and_then(|owned| player.get(owned.owner())) {
Some(pv) => pv.color(),
None => &PLAYER_COLOR_DEFAULT,
};

let m = Matrix4f::new(
Vector4f::new(s, 0.0, 0.0, 0.0),
Vector4f::new(0.0, s, 0.0, 0.0),
Vector4f::new(0.0, 0.0, s, 0.0),
Vector4f::new(p_x, p_y, 0.0, 1.0),
);

self.program
.uniform(self.location_glow_color, Uniform::Vector4f(&c))
.error("Error while updating glow color");
self.program
.uniform(self.location_model, Uniform::Matrix4f(&m))
.error("Error while updating model matrix");

geometry.render_quad();
}
gl::active_texture(gl::TEXTURE1);
let _guard = BindGuard::new(&self.texture_crystal);

gl::active_texture(gl::TEXTURE0);
let _guard = BindGuard::new(&self.texture_metal);

gl::enable(gl::BLEND);
gl::blend_func(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);
gl::draw_arrays(gl::POINTS, 0, self.asteroid_count as _);
gl::disable(gl::BLEND);
}
}

+ 3
- 1
space-crush-common/src/components/asteroid.rs View File

@@ -1,6 +1,8 @@
use serde::{Deserialize, Serialize};
use specs::{Component, HashMapStorage};

use crate::misc::FlaggedStorage;

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Asteroid {
type_: Type,
@@ -25,5 +27,5 @@ impl Asteroid {
}

impl Component for Asteroid {
type Storage = HashMapStorage<Self>;
type Storage = FlaggedStorage<Self, HashMapStorage<Self>>;
}

Loading…
Cancel
Save