Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions arcade/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
Contains pre-loaded programs
"""

from array import array
from collections.abc import Iterable, Sequence
from pathlib import Path
from typing import Any
Expand Down Expand Up @@ -99,6 +100,20 @@ def __init__(
self.sprite_list_program_cull["sprite_texture"] = 0
self.sprite_list_program_cull["uv_texture"] = 1

self.sprite_list_program_no_geo = self.load_program(
vertex_shader=":system:shaders/sprites/sprite_list_simple_vs.glsl",
fragment_shader=":system:shaders/sprites/sprite_list_simple_fs.glsl",
)
self.sprite_list_program_no_geo["sprite_texture"] = 0
self.sprite_list_program_no_geo["uv_texture"] = 1
# Per-instance data
self.sprite_list_program_no_geo["pos_data"] = 2
self.sprite_list_program_no_geo["size_data"] = 3
self.sprite_list_program_no_geo["color_data"] = 4
self.sprite_list_program_no_geo["texture_id_data"] = 5
self.sprite_list_program_no_geo["index_data"] = 6

# Geo shader single sprite program
self.sprite_program_single = self.load_program(
vertex_shader=":system:shaders/sprites/sprite_single_vs.glsl",
geometry_shader=":system:shaders/sprites/sprite_list_geometry_no_cull_geo.glsl",
Expand All @@ -107,6 +122,34 @@ def __init__(
self.sprite_program_single["sprite_texture"] = 0
self.sprite_program_single["uv_texture"] = 1
self.sprite_program_single["spritelist_color"] = 1.0, 1.0, 1.0, 1.0
# Non-geometry shader single sprite program
self.sprite_program_single_simple = self.load_program(
vertex_shader=":system:shaders/sprites/sprite_single_simple_vs.glsl",
fragment_shader=":system:shaders/sprites/sprite_list_simple_fs.glsl",
)
self.sprite_program_single_simple["sprite_texture"] = 0
self.sprite_program_single_simple["uv_texture"] = 1
self.sprite_program_single_simple["spritelist_color"] = 1.0, 1.0, 1.0, 1.0

# fmt: off
self.spritelist_geometry_simple = self.geometry(
[
BufferDescription(
self.buffer(
data=array("f", [
-0.5, +0.5, # Upper left
-0.5, -0.5, # lower left
+0.5, +0.5, # upper right
+0.5, -0.5, # lower right
])
),
"2f",
["in_pos"]
),
],
mode=self.TRIANGLE_STRIP,
)
# fmt: on

# Shapes
self.shape_line_program: Program = self.load_program(
Expand Down Expand Up @@ -144,11 +187,22 @@ def __init__(
self.atlas_resize_program["texcoords_old"] = 2
self.atlas_resize_program["texcoords_new"] = 3

# NOTE: These should not be created when WebGL is used
# SpriteList collision resources
# Buffer version of the collision detection program.
self.collision_detection_program = self.load_program(
vertex_shader=":system:shaders/collision/col_trans_vs.glsl",
geometry_shader=":system:shaders/collision/col_trans_gs.glsl",
)
# Texture version of the collision detection program.
self.collision_detection_program_simple = self.load_program(
vertex_shader=":system:shaders/collision/col_tex_trans_vs.glsl",
geometry_shader=":system:shaders/collision/col_tex_trans_gs.glsl",
)
self.collision_detection_program_simple["pos_angle_data"] = 0
self.collision_detection_program_simple["size_data"] = 1
self.collision_detection_program_simple["index_data"] = 2

self.collision_buffer = self.buffer(reserve=1024 * 4)
self.collision_query = self.query(samples=False, time=False, primitives=True)

Expand Down
9 changes: 4 additions & 5 deletions arcade/draw/rect.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def draw_texture_rect(
ctx.disable(ctx.BLEND)

atlas = atlas or ctx.default_atlas
program = ctx.sprite_program_single
program = ctx.sprite_program_single_simple

texture_id, _ = atlas.add(texture)
if pixelated:
Expand All @@ -68,14 +68,13 @@ def draw_texture_rect(
atlas.use_uv_texture(unit=1)

geometry = ctx.geometry_empty
program["pos"] = rect.x, rect.y, 0
program["pos_rot"] = rect.x, rect.y, 0, angle
program["color"] = color.normalized
program["size"] = rect.width, rect.height
program["angle"] = angle
program["texture_id"] = float(texture_id)
program["texture_id"] = texture_id
program["spritelist_color"] = 1.0, 1.0, 1.0, alpha_normalized

geometry.render(program, mode=gl.POINTS, vertices=1)
geometry.render(program, mode=gl.TRIANGLE_STRIP, vertices=4)

if blend:
ctx.disable(ctx.BLEND)
Expand Down
4 changes: 2 additions & 2 deletions arcade/examples/sections_demo_3.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ def draw_button(self):
def on_resize(self, width: int, height: int):
"""set position on screen resize"""
self.left = width // 3
self.bottom = (height // 2) - self.height // 2
self.bottom = (height // 2) - self.height // 2 # type: ignore
pos = self.left + self.width / 2, self.bottom + self.height / 2
self.button.position = pos

Expand Down Expand Up @@ -203,7 +203,7 @@ def on_mouse_press(self, x: float, y: float, button: int, modifiers: int):

def on_resize(self, width: int, height: int):
# stick to the right
self.left = width - self.width
self.left = width - self.width # type: ignore
self.height = height - self.view.info_bar.height
self.button_stop.position = self.left + self.width / 2, self.top - 80

Expand Down
33 changes: 33 additions & 0 deletions arcade/resources/system/shaders/collision/col_tex_trans_gs.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#version 330
// Texture version if collision shader

layout(points) in;
layout(points, max_vertices=1) out;

uniform vec2 check_pos;
uniform vec2 check_size;

in vec2 pos[];
in vec2 size[];

out int spriteIndex;

void main() {
// Calculate the distance between the sprite center
// and the point we want to check
float dist = distance(pos[0], check_pos);

// Get the maximum x and y size
// max() works per component
vec2 size = max(size[0], check_size);

// Destroy the sprite if too far away
if (dist < max(size.x, size.y) * 1.42) {
// Set the sprite index to the current primitive id
// We are only processing points, so it will match
// the spritelist index
spriteIndex = int(gl_PrimitiveIDIn);
EmitVertex();
}

}
20 changes: 20 additions & 0 deletions arcade/resources/system/shaders/collision/col_tex_trans_vs.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#version 330
// Texture version if collision shader

#include :system:shaders/lib/sprite.glsl

uniform sampler2D pos_angle_data;
uniform sampler2D size_data;
uniform isampler2D index_data;

out vec2 pos;
out vec2 size;

void main() {
int index = getInstanceIndex(index_data, gl_VertexID);
vec4 _pos_rot = getInstancePosRot(pos_angle_data, index);
vec2 _size = getInstanceSize(size_data, index);

pos = _pos_rot.xy;
size = _size.xy;
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#version 330
// Buffer version if collision shader

layout(points) in;
layout(points, max_vertices=1) out;
Expand Down
4 changes: 2 additions & 2 deletions arcade/resources/system/shaders/collision/col_trans_vs.glsl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#version 330
// A simple passthrough shader forwarding data to the geomtry shader
// Buffer version if collision shader

in vec3 in_pos;
in vec4 in_pos;
in vec2 in_size;

out vec2 pos;
Expand Down
25 changes: 24 additions & 1 deletion arcade/resources/system/shaders/lib/sprite.glsl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Fetch texture coordiantes from uv texture
// Fetch texture coordinates from uv texture
void getSpriteUVs(sampler2D uvData, int texture_id, out vec2 uv0, out vec2 uv1, out vec2 uv2, out vec2 uv3) {
texture_id *= 2;
// Calculate the position in the texture. Basic "line wrapping".
Expand All @@ -14,3 +14,26 @@ void getSpriteUVs(sampler2D uvData, int texture_id, out vec2 uv0, out vec2 uv1,
uv2 = data_2.xy;
uv3 = data_2.zw;
}

// Functions for fetching per-instance data from textures.
// These are used with the shader program that uses instancing to render sprites
// meaning there is no geo shader involved. This should work for WebGL.
vec4 getInstancePosRot(sampler2D posData, int index) {
return texelFetch(posData, ivec2(index % 256, index / 256), 0);
}

vec2 getInstanceSize(sampler2D sizeData, int index) {
return texelFetch(sizeData, ivec2(index % 256, index / 256), 0).xy;
}

vec4 getInstanceColor(sampler2D colorData, int index) {
return texelFetch(colorData, ivec2(index % 256, index / 256), 0);
}

int getInstanceTextureId(sampler2D textureIdData, int index) {
return int(texelFetch(textureIdData, ivec2(index % 256, index / 256), 0).x);
}

int getInstanceIndex(isampler2D indexData, int index) {
return texelFetch(indexData, ivec2(index % 256, index / 256), 0).x;
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ void main() {
mat4 mvp = window.projection * window.view;
// Do viewport culling for sprites.
// We do this in normalized device coordinates to make it simple
// apply projection to the center point. This is important so we get zooming/scrollig right
// apply projection to the center point. This is important so we get zooming/scrolling right
vec2 ct = (mvp * vec4(center.xy, 0.0, 1.0)).xy;
// We can get away with cheaper calculation of size
// The length of the diagonal is the cheapest estimation in case rotation is applied
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ in vec4 gs_color;
out vec4 f_color;

void main() {
vec4 basecolor = texture(sprite_texture, gs_uv);
basecolor *= gs_color * spritelist_color;
vec4 base_color = texture(sprite_texture, gs_uv);
base_color *= gs_color * spritelist_color;
// Alpha test
if (basecolor.a == 0.0) {
if (base_color.a == 0.0) {
discard;
}
f_color = basecolor;
f_color = base_color;
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#version 330

in vec3 in_pos;
in float in_angle;
in vec4 in_pos;
in vec2 in_size;
in float in_texture;
in vec4 in_color;
Expand All @@ -12,8 +11,8 @@ out vec2 v_size;
out float v_texture;

void main() {
gl_Position = vec4(in_pos, 1.0);
v_angle = in_angle;
gl_Position = vec4(in_pos.xyz, 1.0);
v_angle = in_pos.w;
v_color = in_color;
v_size = in_size;
v_texture = in_texture;
Expand Down
23 changes: 23 additions & 0 deletions arcade/resources/system/shaders/sprites/sprite_list_simple_fs.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#version 330
// vert/frag only version of the sprite list shader

// Texture atlas
uniform sampler2D sprite_texture;
// Global color set on the sprite list
uniform vec4 spritelist_color;

in vec2 v_uv;
in vec4 v_color;

out vec4 f_color;

void main() {
// vec4 base_color = v_color;
vec4 base_color = texture(sprite_texture, v_uv);
base_color *= v_color * spritelist_color;
// Alpha test
if (base_color.a == 0.0) {
discard;
}
f_color = base_color;
}
75 changes: 75 additions & 0 deletions arcade/resources/system/shaders/sprites/sprite_list_simple_vs.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#version 330
// vert/frag only version of the sprite list shader

uniform WindowBlock {
mat4 projection;
mat4 view;
} window;

// Texture atlas
uniform sampler2D sprite_texture;
// Texture containing UVs for the entire atlas
uniform sampler2D uv_texture;

// Per instance data
uniform sampler2D pos_data;
uniform sampler2D size_data;
uniform sampler2D color_data;
uniform sampler2D texture_id_data;
uniform isampler2D index_data;

// How much half-pixel offset to apply to the UVs.
// 0.0 is no offset, 1.0 is half a pixel offset
uniform float uv_offset_bias;

// Instanced geometry (rectangle as triangle strip)
in vec2 in_pos;

// Output to frag shader
out vec2 v_uv;
out vec4 v_color;

#include :system:shaders/lib/sprite.glsl

void main() {
// Reading per-instance data from textures.
// First we need take the index texture into account to get the correct rendering order.
int index = getInstanceIndex(index_data, gl_InstanceID);
vec4 pos_rot = getInstancePosRot(pos_data, index);
vec2 size = getInstanceSize(size_data, index);
vec4 color = getInstanceColor(color_data, index);
int texture_id = getInstanceTextureId(texture_id_data, index);
// Read texture coordinates from UV texture here
vec2 uv0, uv1, uv2, uv3;
getSpriteUVs(uv_texture, texture_id, uv0, uv1, uv2, uv3);

vec3 center = pos_rot.xyz;
float angle = radians(pos_rot.w);
mat2 rot = mat2(
cos(angle), -sin(angle),
sin(angle), cos(angle)
);

mat4 mvp = window.projection * window.view;

// Apply half pixel offset modified by bias.
// What bias to set depends on the texture filtering mode.
// Linear can have 1.0 bias while nearest should have 0.0 (unless scale is 1:1)
// uvs (
// 0.0, 0.0,
// 1.0, 0.0,
// 0.0, 1.0,
// 1.0, 1.0
// )
vec2 hp = 0.5 / vec2(textureSize(sprite_texture, 0)) * uv_offset_bias;
uv0 += hp;
uv1 += vec2(-hp.x, hp.y);
uv2 += vec2(hp.x, -hp.y);
uv3 += -hp;

int vertex_id = gl_VertexID % 4;
vec2 uvs[4] = vec2[4](uv0, uv2, uv1, uv3);
v_color = color;
gl_Position = mvp * vec4(rot * (in_pos * size) + center.xy, 0.0, 1.0);
v_uv = uvs[vertex_id];
}
Loading
Loading