Compare commits
2 Commits
2638243856
...
91f67b395f
| Author | SHA1 | Date | |
|---|---|---|---|
| 91f67b395f | |||
| 5a28820b5e |
19
.vscode/launch.json
vendored
Normal file
19
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
// 使用 IntelliSense 了解相关属性。
|
||||||
|
// 悬停以查看现有属性的描述。
|
||||||
|
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug",
|
||||||
|
"program": "${workspaceFolder}/target/debug/radar-g",
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}",
|
||||||
|
"env": {
|
||||||
|
"RUST_LOG": "info"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
763
Cargo.lock
generated
763
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
62
Cargo.toml
62
Cargo.toml
@ -1,37 +1,51 @@
|
|||||||
[package]
|
[package]
|
||||||
|
edition = "2021"
|
||||||
name = "radar-g"
|
name = "radar-g"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|
||||||
imgui = "0.12.0"
|
aligned-vec = "0.6.0"
|
||||||
|
anyhow = "1.0.86"
|
||||||
|
bytemuck = {version = "1.16.1", features = ["derive"]}
|
||||||
|
byteorder = "1.5.0"
|
||||||
|
cgmath = "0.18.0"
|
||||||
|
chrono = "0.4.38"
|
||||||
|
copypasta = "0.10.1"
|
||||||
|
dirs = "5.0.1"
|
||||||
|
env_logger = "0.11.3"
|
||||||
|
flate2 = "1.0.30"
|
||||||
|
# font-kit = {version = "0.14.1"}
|
||||||
|
freetype-rs = "0.37.0"
|
||||||
|
geo = "0.28.0"
|
||||||
glow = "0.13.1"
|
glow = "0.13.1"
|
||||||
imgui-glow-renderer = "0.12.0"
|
|
||||||
imgui-winit-support = "0.12.0"
|
|
||||||
glutin = "0.31.3"
|
glutin = "0.31.3"
|
||||||
glutin-winit = "0.4.2"
|
glutin-winit = "0.4.2"
|
||||||
copypasta = "0.10.1"
|
image = "0.25.2"
|
||||||
raw-window-handle = "0.5.2"
|
imgui = {version="0.12.0",features = ["tables-api"]}
|
||||||
winit = "0.29.3"
|
imgui-glow-renderer = "0.12.0"
|
||||||
cgmath = "0.18.0"
|
imgui-winit-support = "0.12.0"
|
||||||
nalgebra-glm = "0.18.0"
|
|
||||||
regex = "1.10.5"
|
|
||||||
once_cell = "1.19.0"
|
|
||||||
include_dir = "0.7.4"
|
include_dir = "0.7.4"
|
||||||
nom = "7.1.3"
|
|
||||||
thiserror = "1.0.61"
|
|
||||||
log = "0.4.22"
|
log = "0.4.22"
|
||||||
env_logger = "0.11.3"
|
lru = "0.12.4"
|
||||||
bytemuck = { version = "1.16.1", features = ["derive"] }
|
|
||||||
nalgebra = "0.33.0"
|
nalgebra = "0.33.0"
|
||||||
nom-derive = "0.10.1"
|
nalgebra-glm = "0.18.0"
|
||||||
serde = { version = "1.0.204", features = ["derive"] }
|
|
||||||
byteorder = "1.5.0"
|
|
||||||
chrono = "0.4.38"
|
|
||||||
flate2 = "1.0.30"
|
|
||||||
anyhow = "1.0.86"
|
|
||||||
serde_json = "1.0.120"
|
|
||||||
geo = "0.28.0"
|
|
||||||
ndarray = "0.15.6"
|
ndarray = "0.15.6"
|
||||||
aligned-vec = "0.6.0"
|
nom = "7.1.3"
|
||||||
|
nom-derive = "0.10.1"
|
||||||
|
once_cell = "1.19.0"
|
||||||
|
pathfinder_geometry = "0.5.1"
|
||||||
|
raw-window-handle = "0.5.2"
|
||||||
|
regex = "1.10.5"
|
||||||
|
serde = {version = "1.0.204", features = ["derive"]}
|
||||||
|
serde_json = "1.0.120"
|
||||||
|
thiserror = "1.0.61"
|
||||||
|
winit = "0.29.3"
|
||||||
|
tinyfiledialogs = "3.0"
|
||||||
|
tracker = "0.2.2"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["sdf_font"]
|
||||||
|
|
||||||
|
normal_font = []
|
||||||
|
sdf_font = []
|
||||||
|
|||||||
5
check_sdf.py
Normal file
5
check_sdf.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
with open("resources/Dokdo-Regular/0-255.pbf","rb") as f:
|
||||||
|
|
||||||
|
pass
|
||||||
65
shaders/font.frag
Normal file
65
shaders/font.frag
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
uniform sampler2D atlas_data;
|
||||||
|
in vec2 v_texCoord;
|
||||||
|
|
||||||
|
uniform vec4 uClipUV;
|
||||||
|
// uniform vec2 uSdfUV;
|
||||||
|
uniform vec4 uSdfConfig;
|
||||||
|
uniform int uMode;
|
||||||
|
uniform vec4 uBorder;
|
||||||
|
uniform vec4 uStroke;
|
||||||
|
uniform vec4 uFill;
|
||||||
|
|
||||||
|
float getUVScale(vec2 sdfUV) {
|
||||||
|
float dx = dFdx(sdfUV.x);
|
||||||
|
float dy = dFdy(sdfUV.y);
|
||||||
|
return (sqrt(dx * dx + dy * dy) + sqrt(dy * dy + dx * dx)) * 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SDF {
|
||||||
|
float outer;
|
||||||
|
float inner;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
vec4 getTexture(vec2 uv) {
|
||||||
|
return texture(atlas_data, uv);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 getMask(vec4 color, vec2 uv, vec2 st) {
|
||||||
|
return color; // 默认不修改颜色
|
||||||
|
}
|
||||||
|
|
||||||
|
out vec4 fragColor;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec4 fillColor = uFill;
|
||||||
|
vec4 strokeColor = uStroke;
|
||||||
|
|
||||||
|
float scale = getUVScale(v_texCoord);
|
||||||
|
|
||||||
|
vec4 texture = getTexture(v_texCoord);
|
||||||
|
float sdfRaw = 0.0;
|
||||||
|
float mark = 0.0;
|
||||||
|
|
||||||
|
float sdf;
|
||||||
|
|
||||||
|
float sdfRadius = uSdfConfig.x;
|
||||||
|
float expand = uBorder.x;
|
||||||
|
float bleed = uBorder.y;
|
||||||
|
|
||||||
|
float d = (texture.r - 0.75) * sdfRadius;
|
||||||
|
float s = (d + expand / uSdfConfig.y) / scale + 0.5 + bleed;
|
||||||
|
sdf = s; // Assuming SDF returns a single float, adjust as necessary
|
||||||
|
|
||||||
|
if (uMode == -2) {
|
||||||
|
fillColor = vec4(texture.rgb, fillColor.a);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gl_FragCoord.x < uClipUV.x || gl_FragCoord.y < uClipUV.y || gl_FragCoord.x > uClipUV.z || gl_FragCoord.y > uClipUV.w) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
// Compute mask based on SDF
|
||||||
|
float mask = clamp(sdf, 0.0, 1.0);
|
||||||
|
// Final color blending logic here
|
||||||
|
fragColor = vec4(fillColor.rgb + mark, fillColor.a * mask + mark);
|
||||||
|
}
|
||||||
28
shaders/font.vert
Normal file
28
shaders/font.vert
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
layout(location = 0) in vec3 input_position;
|
||||||
|
layout(location = 1) in vec2 texcoord;
|
||||||
|
|
||||||
|
|
||||||
|
uniform vec2 atlas_shape;
|
||||||
|
|
||||||
|
out float v_scale;
|
||||||
|
out vec2 v_texCoord;
|
||||||
|
out vec4 v_color;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const vec2 verts[4] = vec2[4](
|
||||||
|
vec2(-0.5f, 0.5f),
|
||||||
|
vec2(0.5f, 0.5f),
|
||||||
|
vec2(0.5f,-0.5f),
|
||||||
|
vec2(-0.5, -0.5f)
|
||||||
|
);
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
|
||||||
|
// vec4 _position = <transform(input_position)>;
|
||||||
|
gl_Position = vec4(verts[gl_VertexID],0.0,1.0);
|
||||||
|
v_texCoord = texcoord / atlas_shape;
|
||||||
|
|
||||||
|
v_scale = 1.0;
|
||||||
|
v_color = vec4(1.0, 1.0, 1.0, 1.0);
|
||||||
|
}
|
||||||
289
shaders/misc/spatial-filters.frag
Normal file
289
shaders/misc/spatial-filters.frag
Normal file
@ -0,0 +1,289 @@
|
|||||||
|
const float kernel_bias = -0.234377;
|
||||||
|
const float kernel_scale = 1.241974;
|
||||||
|
uniform sampler2D u_kernel;
|
||||||
|
|
||||||
|
vec4 filter1D_radius1(sampler2D kernel, float index, float x, vec4 c0, vec4 c1) {
|
||||||
|
float w;
|
||||||
|
float w_sum = 0.0;
|
||||||
|
vec4 r = vec4(0.0,0.0,0.0,0.0);
|
||||||
|
w = texture(kernel, vec2(0.000000+(x/1.0),index) ).r;
|
||||||
|
w = w*kernel_scale + kernel_bias;
|
||||||
|
r = r+ c0 * w;
|
||||||
|
w = texture(kernel, vec2(1.000000-(x/1.0),index) ).r;
|
||||||
|
w = w*kernel_scale + kernel_bias;
|
||||||
|
r = r + c1 * w;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
vec4 filter2D_radius1(sampler2D _texture, sampler2D kernel, float index, vec2 uv, vec2 pixel) {
|
||||||
|
vec2 texel = uv/pixel - vec2(0.5,0.5) ;
|
||||||
|
vec2 f = fract(texel);
|
||||||
|
texel = (texel-fract(texel)+vec2(0.001,0.001))*pixel;
|
||||||
|
vec4 t0 = filter1D_radius1(kernel, index, f.x,
|
||||||
|
texture(_texture, texel + vec2(0,0)*pixel),
|
||||||
|
texture(_texture, texel + vec2(1,0)*pixel));
|
||||||
|
vec4 t1 = filter1D_radius1(kernel, index, f.x,
|
||||||
|
texture(_texture, texel + vec2(0,1)*pixel),
|
||||||
|
texture(_texture, texel + vec2(1,1)*pixel));
|
||||||
|
return filter1D_radius1(kernel, index, f.y, t0, t1);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 filter1D_radius2(sampler2D kernel, float index, float x, vec4 c0, vec4 c1, vec4 c2, vec4 c3) {
|
||||||
|
float w;
|
||||||
|
float w_sum = 0.0;
|
||||||
|
vec4 r = vec4(0.0,0.0,0.0,0.0);
|
||||||
|
w = texture(kernel, vec2(0.500000+(x/2.0),index) ).r;
|
||||||
|
w = w*kernel_scale + kernel_bias;
|
||||||
|
r = r+ c0 * w;
|
||||||
|
w = texture(kernel, vec2(0.500000-(x/2.0),index) ).r;
|
||||||
|
w = w*kernel_scale + kernel_bias;
|
||||||
|
r = r+ c2 * w;
|
||||||
|
w = texture(kernel, vec2(0.000000+(x/2.0),index) ).r;
|
||||||
|
w = w*kernel_scale + kernel_bias;
|
||||||
|
r = r+ c1 * w;
|
||||||
|
w = texture(kernel, vec2(1.000000-(x/2.0),index) ).r;
|
||||||
|
w = w*kernel_scale + kernel_bias;
|
||||||
|
r = r+ c3 * w;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
vec4 filter2D_radius2(sampler2D _texture, sampler2D kernel, float index, vec2 uv, vec2 pixel) {
|
||||||
|
vec2 texel = uv/pixel - vec2(0.5,0.5) ;
|
||||||
|
vec2 f = fract(texel);
|
||||||
|
texel = (texel-fract(texel)+vec2(0.001,0.001))*pixel;
|
||||||
|
vec4 t0 = filter1D_radius2(kernel, index, f.x,
|
||||||
|
texture(_texture, texel + vec2(-1,-1)*pixel),
|
||||||
|
texture(_texture, texel + vec2(0,-1)*pixel),
|
||||||
|
texture(_texture, texel + vec2(1,-1)*pixel),
|
||||||
|
texture(_texture, texel + vec2(2,-1)*pixel));
|
||||||
|
vec4 t1 = filter1D_radius2(kernel, index, f.x,
|
||||||
|
texture(_texture, texel + vec2(-1,0)*pixel),
|
||||||
|
texture(_texture, texel + vec2(0,0)*pixel),
|
||||||
|
texture(_texture, texel + vec2(1,0)*pixel),
|
||||||
|
texture(_texture, texel + vec2(2,0)*pixel));
|
||||||
|
vec4 t2 = filter1D_radius2(kernel, index, f.x,
|
||||||
|
texture(_texture, texel + vec2(-1,1)*pixel),
|
||||||
|
texture(_texture, texel + vec2(0,1)*pixel),
|
||||||
|
texture(_texture, texel + vec2(1,1)*pixel),
|
||||||
|
texture(_texture, texel + vec2(2,1)*pixel));
|
||||||
|
vec4 t3 = filter1D_radius2(kernel, index, f.x,
|
||||||
|
texture(_texture, texel + vec2(-1,2)*pixel),
|
||||||
|
texture(_texture, texel + vec2(0,2)*pixel),
|
||||||
|
texture(_texture, texel + vec2(1,2)*pixel),
|
||||||
|
texture(_texture, texel + vec2(2,2)*pixel));
|
||||||
|
return filter1D_radius2(kernel, index, f.y, t0, t1, t2, t3);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 filter1D_radius3(sampler2D kernel, float index, float x, vec4 c0, vec4 c1, vec4 c2, vec4 c3, vec4 c4, vec4 c5) {
|
||||||
|
float w;
|
||||||
|
float w_sum = 0.0;
|
||||||
|
vec4 r = vec4(0.0,0.0,0.0,0.0);
|
||||||
|
w = texture(kernel, vec2(0.666667+(x/3.0),index) ).r;
|
||||||
|
w = w*kernel_scale + kernel_bias;
|
||||||
|
r = r+ c0 * w;
|
||||||
|
w = texture(kernel, vec2(0.333333-(x/3.0),index) ).r;
|
||||||
|
w = w*kernel_scale + kernel_bias;
|
||||||
|
r = r+ c3 * w;
|
||||||
|
w = texture(kernel, vec2(0.333333+(x/3.0),index) ).r;
|
||||||
|
w = w*kernel_scale + kernel_bias;
|
||||||
|
r = r+ c1 * w;
|
||||||
|
w = texture(kernel, vec2(0.666667-(x/3.0),index) ).r;
|
||||||
|
w = w*kernel_scale + kernel_bias;
|
||||||
|
r = r+ c4 * w;
|
||||||
|
w = texture(kernel, vec2(0.000000+(x/3.0),index) ).r;
|
||||||
|
w = w*kernel_scale + kernel_bias;
|
||||||
|
r = r+ c2 * w;
|
||||||
|
w = texture(kernel, vec2(1.000000-(x/3.0),index) ).r;
|
||||||
|
w = w*kernel_scale + kernel_bias;
|
||||||
|
r = r+ c5 * w;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
vec4 filter2D_radius3(sampler2D _texture, sampler2D kernel, float index, vec2 uv, vec2 pixel) {
|
||||||
|
vec2 texel = uv/pixel - vec2(0.5,0.5) ;
|
||||||
|
vec2 f = fract(texel);
|
||||||
|
texel = (texel-fract(texel)+vec2(0.001,0.001))*pixel;
|
||||||
|
vec4 t0 = filter1D_radius3(kernel, index, f.x,
|
||||||
|
texture(_texture, texel + vec2(-2,-2)*pixel),
|
||||||
|
texture(_texture, texel + vec2(-1,-2)*pixel),
|
||||||
|
texture(_texture, texel + vec2(0,-2)*pixel),
|
||||||
|
texture(_texture, texel + vec2(1,-2)*pixel),
|
||||||
|
texture(_texture, texel + vec2(2,-2)*pixel),
|
||||||
|
texture(_texture, texel + vec2(3,-2)*pixel));
|
||||||
|
vec4 t1 = filter1D_radius3(kernel, index, f.x,
|
||||||
|
texture(_texture, texel + vec2(-2,-1)*pixel),
|
||||||
|
texture(_texture, texel + vec2(-1,-1)*pixel),
|
||||||
|
texture(_texture, texel + vec2(0,-1)*pixel),
|
||||||
|
texture(_texture, texel + vec2(1,-1)*pixel),
|
||||||
|
texture(_texture, texel + vec2(2,-1)*pixel),
|
||||||
|
texture(_texture, texel + vec2(3,-1)*pixel));
|
||||||
|
vec4 t2 = filter1D_radius3(kernel, index, f.x,
|
||||||
|
texture(_texture, texel + vec2(-2,0)*pixel),
|
||||||
|
texture(_texture, texel + vec2(-1,0)*pixel),
|
||||||
|
texture(_texture, texel + vec2(0,0)*pixel),
|
||||||
|
texture(_texture, texel + vec2(1,0)*pixel),
|
||||||
|
texture(_texture, texel + vec2(2,0)*pixel),
|
||||||
|
texture(_texture, texel + vec2(3,0)*pixel));
|
||||||
|
vec4 t3 = filter1D_radius3(kernel, index, f.x,
|
||||||
|
texture(_texture, texel + vec2(-2,1)*pixel),
|
||||||
|
texture(_texture, texel + vec2(-1,1)*pixel),
|
||||||
|
texture(_texture, texel + vec2(0,1)*pixel),
|
||||||
|
texture(_texture, texel + vec2(1,1)*pixel),
|
||||||
|
texture(_texture, texel + vec2(2,1)*pixel),
|
||||||
|
texture(_texture, texel + vec2(3,1)*pixel));
|
||||||
|
vec4 t4 = filter1D_radius3(kernel, index, f.x,
|
||||||
|
texture(_texture, texel + vec2(-2,2)*pixel),
|
||||||
|
texture(_texture, texel + vec2(-1,2)*pixel),
|
||||||
|
texture(_texture, texel + vec2(0,2)*pixel),
|
||||||
|
texture(_texture, texel + vec2(1,2)*pixel),
|
||||||
|
texture(_texture, texel + vec2(2,2)*pixel),
|
||||||
|
texture(_texture, texel + vec2(3,2)*pixel));
|
||||||
|
vec4 t5 = filter1D_radius3(kernel, index, f.x,
|
||||||
|
texture(_texture, texel + vec2(-2,3)*pixel),
|
||||||
|
texture(_texture, texel + vec2(-1,3)*pixel),
|
||||||
|
texture(_texture, texel + vec2(0,3)*pixel),
|
||||||
|
texture(_texture, texel + vec2(1,3)*pixel),
|
||||||
|
texture(_texture, texel + vec2(2,3)*pixel),
|
||||||
|
texture(_texture, texel + vec2(3,3)*pixel));
|
||||||
|
return filter1D_radius3(kernel, index, f.y, t0, t1, t2, t3, t4, t5);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 filter1D_radius4(sampler2D kernel, float index, float x, vec4 c0, vec4 c1, vec4 c2, vec4 c3, vec4 c4, vec4 c5, vec4 c6, vec4 c7) {
|
||||||
|
float w;
|
||||||
|
float w_sum = 0.0;
|
||||||
|
vec4 r = vec4(0.0,0.0,0.0,0.0);
|
||||||
|
w = texture(kernel, vec2(0.750000+(x/4.0),index) ).r;
|
||||||
|
w = w*kernel_scale + kernel_bias;
|
||||||
|
r = r+ c0 * w;
|
||||||
|
w = texture(kernel, vec2(0.250000-(x/4.0),index) ).r;
|
||||||
|
w = w*kernel_scale + kernel_bias;
|
||||||
|
r = r+ c4 * w;
|
||||||
|
w = texture(kernel, vec2(0.500000+(x/4.0),index) ).r;
|
||||||
|
w = w*kernel_scale + kernel_bias;
|
||||||
|
r = r+ c1 * w;
|
||||||
|
w = texture(kernel, vec2(0.500000-(x/4.0),index) ).r;
|
||||||
|
w = w*kernel_scale + kernel_bias;
|
||||||
|
r = r+ c5 * w;
|
||||||
|
w = texture(kernel, vec2(0.250000+(x/4.0),index) ).r;
|
||||||
|
w = w*kernel_scale + kernel_bias;
|
||||||
|
r = r+ c2 * w;
|
||||||
|
w = texture(kernel, vec2(0.750000-(x/4.0),index) ).r;
|
||||||
|
w = w*kernel_scale + kernel_bias;
|
||||||
|
r = r+ c6 * w;
|
||||||
|
w = texture(kernel, vec2(0.000000+(x/4.0),index) ).r;
|
||||||
|
w = w*kernel_scale + kernel_bias;
|
||||||
|
r = r+ c3 * w;
|
||||||
|
w = texture(kernel, vec2(1.000000-(x/4.0),index) ).r;
|
||||||
|
w = w*kernel_scale + kernel_bias;
|
||||||
|
r = r+ c7 * w;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
vec4 filter2D_radius4(sampler2D _texture, sampler2D kernel, float index, vec2 uv, vec2 pixel) {
|
||||||
|
vec2 texel = uv/pixel - vec2(0.5,0.5) ;
|
||||||
|
vec2 f = fract(texel);
|
||||||
|
texel = (texel-fract(texel)+vec2(0.001,0.001))*pixel;
|
||||||
|
vec4 t0 = filter1D_radius4(kernel, index, f.x,
|
||||||
|
texture(_texture, texel + vec2(-3,-3)*pixel),
|
||||||
|
texture(_texture, texel + vec2(-2,-3)*pixel),
|
||||||
|
texture(_texture, texel + vec2(-1,-3)*pixel),
|
||||||
|
texture(_texture, texel + vec2(0,-3)*pixel),
|
||||||
|
texture(_texture, texel + vec2(1,-3)*pixel),
|
||||||
|
texture(_texture, texel + vec2(2,-3)*pixel),
|
||||||
|
texture(_texture, texel + vec2(3,-3)*pixel),
|
||||||
|
texture(_texture, texel + vec2(4,-3)*pixel));
|
||||||
|
vec4 t1 = filter1D_radius4(kernel, index, f.x,
|
||||||
|
texture(_texture, texel + vec2(-3,-2)*pixel),
|
||||||
|
texture(_texture, texel + vec2(-2,-2)*pixel),
|
||||||
|
texture(_texture, texel + vec2(-1,-2)*pixel),
|
||||||
|
texture(_texture, texel + vec2(0,-2)*pixel),
|
||||||
|
texture(_texture, texel + vec2(1,-2)*pixel),
|
||||||
|
texture(_texture, texel + vec2(2,-2)*pixel),
|
||||||
|
texture(_texture, texel + vec2(3,-2)*pixel),
|
||||||
|
texture(_texture, texel + vec2(4,-2)*pixel));
|
||||||
|
vec4 t2 = filter1D_radius4(kernel, index, f.x,
|
||||||
|
texture(_texture, texel + vec2(-3,-1)*pixel),
|
||||||
|
texture(_texture, texel + vec2(-2,-1)*pixel),
|
||||||
|
texture(_texture, texel + vec2(-1,-1)*pixel),
|
||||||
|
texture(_texture, texel + vec2(0,-1)*pixel),
|
||||||
|
texture(_texture, texel + vec2(1,-1)*pixel),
|
||||||
|
texture(_texture, texel + vec2(2,-1)*pixel),
|
||||||
|
texture(_texture, texel + vec2(3,-1)*pixel),
|
||||||
|
texture(_texture, texel + vec2(4,-1)*pixel));
|
||||||
|
vec4 t3 = filter1D_radius4(kernel, index, f.x,
|
||||||
|
texture(_texture, texel + vec2(-3,0)*pixel),
|
||||||
|
texture(_texture, texel + vec2(-2,0)*pixel),
|
||||||
|
texture(_texture, texel + vec2(-1,0)*pixel),
|
||||||
|
texture(_texture, texel + vec2(0,0)*pixel),
|
||||||
|
texture(_texture, texel + vec2(1,0)*pixel),
|
||||||
|
texture(_texture, texel + vec2(2,0)*pixel),
|
||||||
|
texture(_texture, texel + vec2(3,0)*pixel),
|
||||||
|
texture(_texture, texel + vec2(4,0)*pixel));
|
||||||
|
vec4 t4 = filter1D_radius4(kernel, index, f.x,
|
||||||
|
texture(_texture, texel + vec2(-3,1)*pixel),
|
||||||
|
texture(_texture, texel + vec2(-2,1)*pixel),
|
||||||
|
texture(_texture, texel + vec2(-1,1)*pixel),
|
||||||
|
texture(_texture, texel + vec2(0,1)*pixel),
|
||||||
|
texture(_texture, texel + vec2(1,1)*pixel),
|
||||||
|
texture(_texture, texel + vec2(2,1)*pixel),
|
||||||
|
texture(_texture, texel + vec2(3,1)*pixel),
|
||||||
|
texture(_texture, texel + vec2(4,1)*pixel));
|
||||||
|
vec4 t5 = filter1D_radius4(kernel, index, f.x,
|
||||||
|
texture(_texture, texel + vec2(-3,2)*pixel),
|
||||||
|
texture(_texture, texel + vec2(-2,2)*pixel),
|
||||||
|
texture(_texture, texel + vec2(-1,2)*pixel),
|
||||||
|
texture(_texture, texel + vec2(0,2)*pixel),
|
||||||
|
texture(_texture, texel + vec2(1,2)*pixel),
|
||||||
|
texture(_texture, texel + vec2(2,2)*pixel),
|
||||||
|
texture(_texture, texel + vec2(3,2)*pixel),
|
||||||
|
texture(_texture, texel + vec2(4,2)*pixel));
|
||||||
|
vec4 t6 = filter1D_radius4(kernel, index, f.x,
|
||||||
|
texture(_texture, texel + vec2(-3,3)*pixel),
|
||||||
|
texture(_texture, texel + vec2(-2,3)*pixel),
|
||||||
|
texture(_texture, texel + vec2(-1,3)*pixel),
|
||||||
|
texture(_texture, texel + vec2(0,3)*pixel),
|
||||||
|
texture(_texture, texel + vec2(1,3)*pixel),
|
||||||
|
texture(_texture, texel + vec2(2,3)*pixel),
|
||||||
|
texture(_texture, texel + vec2(3,3)*pixel),
|
||||||
|
texture(_texture, texel + vec2(4,3)*pixel));
|
||||||
|
vec4 t7 = filter1D_radius4(kernel, index, f.x,
|
||||||
|
texture(_texture, texel + vec2(-3,4)*pixel),
|
||||||
|
texture(_texture, texel + vec2(-2,4)*pixel),
|
||||||
|
texture(_texture, texel + vec2(-1,4)*pixel),
|
||||||
|
texture(_texture, texel + vec2(0,4)*pixel),
|
||||||
|
texture(_texture, texel + vec2(1,4)*pixel),
|
||||||
|
texture(_texture, texel + vec2(2,4)*pixel),
|
||||||
|
texture(_texture, texel + vec2(3,4)*pixel),
|
||||||
|
texture(_texture, texel + vec2(4,4)*pixel));
|
||||||
|
return filter1D_radius4(kernel, index, f.y, t0, t1, t2, t3, t4, t5, t6, t7);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 Nearest(sampler2D _texture, vec2 shape, vec2 uv) { return texture(_texture,uv); }
|
||||||
|
|
||||||
|
vec4 Bilinear(sampler2D _texture, vec2 shape, vec2 uv) { return filter2D_radius1(_texture, u_kernel, 0.031250, uv, 1.0/shape); }
|
||||||
|
|
||||||
|
vec4 Hanning(sampler2D _texture, vec2 shape, vec2 uv) { return filter2D_radius1(_texture, u_kernel, 0.093750, uv, 1.0/shape); }
|
||||||
|
|
||||||
|
vec4 Hamming(sampler2D _texture, vec2 shape, vec2 uv) { return filter2D_radius1(_texture, u_kernel, 0.156250, uv, 1.0/shape); }
|
||||||
|
|
||||||
|
vec4 Hermite(sampler2D _texture, vec2 shape, vec2 uv) { return filter2D_radius1(_texture, u_kernel, 0.218750, uv, 1.0/shape); }
|
||||||
|
|
||||||
|
vec4 Kaiser(sampler2D _texture, vec2 shape, vec2 uv) { return filter2D_radius1(_texture, u_kernel, 0.281250, uv, 1.0/shape); }
|
||||||
|
|
||||||
|
vec4 Quadric(sampler2D _texture, vec2 shape, vec2 uv) { return filter2D_radius2(_texture, u_kernel, 0.343750, uv, 1.0/shape); }
|
||||||
|
|
||||||
|
vec4 Bicubic(sampler2D _texture, vec2 shape, vec2 uv) { return filter2D_radius2(_texture, u_kernel, 0.406250, uv, 1.0/shape); }
|
||||||
|
|
||||||
|
vec4 CatRom(sampler2D _texture, vec2 shape, vec2 uv) { return filter2D_radius2(_texture, u_kernel, 0.468750, uv, 1.0/shape); }
|
||||||
|
|
||||||
|
vec4 Mitchell(sampler2D _texture, vec2 shape, vec2 uv) { return filter2D_radius2(_texture, u_kernel, 0.531250, uv, 1.0/shape); }
|
||||||
|
|
||||||
|
vec4 Spline16(sampler2D _texture, vec2 shape, vec2 uv) { return filter2D_radius2(_texture, u_kernel, 0.593750, uv, 1.0/shape); }
|
||||||
|
|
||||||
|
vec4 Spline36(sampler2D _texture, vec2 shape, vec2 uv) { return filter2D_radius3(_texture, u_kernel, 0.656250, uv, 1.0/shape); }
|
||||||
|
|
||||||
|
vec4 Gaussian(sampler2D _texture, vec2 shape, vec2 uv) { return filter2D_radius2(_texture, u_kernel, 0.718750, uv, 1.0/shape); }
|
||||||
|
|
||||||
|
vec4 Bessel(sampler2D _texture, vec2 shape, vec2 uv) { return filter2D_radius4(_texture, u_kernel, 0.781250, uv, 1.0/shape); }
|
||||||
|
|
||||||
|
vec4 Sinc(sampler2D _texture, vec2 shape, vec2 uv) { return filter2D_radius4(_texture, u_kernel, 0.843750, uv, 1.0/shape); }
|
||||||
|
|
||||||
|
vec4 Lanczos(sampler2D _texture, vec2 shape, vec2 uv) { return filter2D_radius4(_texture, u_kernel, 0.906250, uv, 1.0/shape); }
|
||||||
|
|
||||||
|
vec4 Blackman(sampler2D _texture, vec2 shape, vec2 uv) { return filter2D_radius4(_texture, u_kernel, 0.968750, uv, 1.0/shape); }
|
||||||
@ -5,6 +5,6 @@ layout(location = 0) in vec3 position;
|
|||||||
out float in_value;
|
out float in_value;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
gl_Position = vec4(position.x, position.y, 0.0, 1.0);
|
gl_Position = vec4(position.x, position.y, 0.5, 1.0);
|
||||||
in_value = position.z;
|
in_value = position.z;
|
||||||
}
|
}
|
||||||
745
src/_pg.rs
Normal file
745
src/_pg.rs
Normal file
@ -0,0 +1,745 @@
|
|||||||
|
use crate::components::Program;
|
||||||
|
use crate::font_manager::FontManager;
|
||||||
|
use crate::graphics::colormap::linear::LinearColormap;
|
||||||
|
use crate::graphics::font::Text;
|
||||||
|
use crate::graphics::ppi::PPIConfig;
|
||||||
|
use crate::graphics::threed::ThreeD;
|
||||||
|
use crate::graphics::transforms::viewport::Viewport;
|
||||||
|
use crate::graphics::{ppi::PPI, Graphics};
|
||||||
|
use crate::graphics::{AttaWithProgram, AttachWithMouse, Config, MouseState};
|
||||||
|
use crate::utils::resources::{
|
||||||
|
RcGlFramebuffer, RcGlRcResource, RcGlResource, RcGlTexture, Resource,
|
||||||
|
};
|
||||||
|
// use crate::{
|
||||||
|
// errors::*,
|
||||||
|
// ui::{State, GUI},
|
||||||
|
// };
|
||||||
|
use glow::{HasContext, NativeBuffer, NativeFramebuffer, NativeTexture, NativeVertexArray};
|
||||||
|
|
||||||
|
use imgui::{ImString, Ui};
|
||||||
|
use log::info;
|
||||||
|
use std::marker::PhantomPinned;
|
||||||
|
use std::pin::Pin;
|
||||||
|
use std::ptr::NonNull;
|
||||||
|
use std::{cell::RefCell, collections::HashMap, rc::Rc};
|
||||||
|
type RcGraphic<T> = Rc<RefCell<T>>;
|
||||||
|
type ProgramId = &'static str;
|
||||||
|
|
||||||
|
// pub struct App<'a> {
|
||||||
|
// gui: Option<GUI>,
|
||||||
|
// gl: GL,
|
||||||
|
// viewport: Viewport,
|
||||||
|
// programs: Option<Programs<'a>>,
|
||||||
|
// }
|
||||||
|
|
||||||
|
// impl<'a> App<'a> {
|
||||||
|
// pub fn new(gl: GL) -> Result<Self> {
|
||||||
|
// let viewport = Viewport::new()?;
|
||||||
|
|
||||||
|
// Ok(Self {
|
||||||
|
// gui: None,
|
||||||
|
// viewport,
|
||||||
|
// programs: None,
|
||||||
|
// gl,
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
// fn render_for_program<P: Graphics>(
|
||||||
|
// gl: &'a glow::Context,
|
||||||
|
// program_with_window: &HashMap<ProgramId, Vec<ImString>>,
|
||||||
|
// _windows: &mut HashMap<ImString, WindowData<'a>>,
|
||||||
|
// p: &mut P,
|
||||||
|
// ) where
|
||||||
|
// P::Config: From<Config>,
|
||||||
|
// {
|
||||||
|
// // let mut p = program.borrow_mut();
|
||||||
|
// if program_with_window.len() == 0 {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// p.mount(&gl).unwrap();
|
||||||
|
// program_with_window.get(&P::id).map(|windows| {
|
||||||
|
// for window in windows.iter() {
|
||||||
|
// let window_info = &mut *(_windows.get_mut(window).unwrap());
|
||||||
|
// // Skip the window if it doesn't need to be redrawn
|
||||||
|
// if !window_info.need_redraw {
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
// // window_info.re_init = false;
|
||||||
|
// // If the window needs to be reinitialized, set the config
|
||||||
|
// for conf in window_info.confs.iter() {
|
||||||
|
// p.set_config(&gl, &(conf.1.clone()).into()).unwrap();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Attach the modifer to the program
|
||||||
|
// if let Some(motion) = window_info.modifer.as_ref() {
|
||||||
|
// motion.attach_with_program(&gl, p.program_ref()).unwrap();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Attach the data to the program
|
||||||
|
// unsafe {
|
||||||
|
// if window_info.gl_fb_resources.is_none() {
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// let framebuffer = window_info.gl_fb_resources.as_ref().unwrap().native();
|
||||||
|
|
||||||
|
// gl.bind_framebuffer(glow::FRAMEBUFFER, Some(framebuffer));
|
||||||
|
// let attach = window_info.attach.get(P::id);
|
||||||
|
|
||||||
|
// if attach.is_some() {
|
||||||
|
// let vao = attach.unwrap().vao.native();
|
||||||
|
// gl.bind_vertex_array(Some(vao));
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if attach.is_some() {
|
||||||
|
// let window_size = window_info.size;
|
||||||
|
// gl.viewport(0, 0, window_size[0] as i32, window_size[1] as i32);
|
||||||
|
// p.draw(&gl, attach.as_ref().unwrap().len).unwrap();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if attach.is_some() {
|
||||||
|
// gl.bind_vertex_array(None);
|
||||||
|
// }
|
||||||
|
// gl.bind_framebuffer(glow::FRAMEBUFFER, None);
|
||||||
|
// window_info.need_redraw = false;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
|
// // Unmount the program
|
||||||
|
// p.unmount(&gl).unwrap();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn render(&mut self) {
|
||||||
|
// // Self::render_for_program(
|
||||||
|
// // &self.gl,
|
||||||
|
// // &self.windows_manager.program_with_window,
|
||||||
|
// // &mut self.windows_manager.windows,
|
||||||
|
// // &mut self.programs.ppi_module,
|
||||||
|
// // );
|
||||||
|
|
||||||
|
// // Self::render_for_program(
|
||||||
|
// // &self.gl,
|
||||||
|
// // &self.windows_manager.program_with_window,
|
||||||
|
// // &mut self.windows_manager.windows,
|
||||||
|
// // &mut self.programs.text_module,
|
||||||
|
// // );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // pub fn render_ui(&mut self, ui: &Ui, window: &winit::window::Window, run: &mut bool) {
|
||||||
|
// // self.gui.render(ui);
|
||||||
|
// // self.windows_manager.show_window(ui);
|
||||||
|
// // // self.windows_manager.destroy_window();
|
||||||
|
// // }
|
||||||
|
|
||||||
|
// pub fn programs(&mut self) -> &mut Programs<'a> {
|
||||||
|
// self.programs.as_mut().unwrap()
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn prepare(&mut self) {
|
||||||
|
// self.programs = Some(Programs::new(self.gl.gl(), &self.viewport).unwrap());
|
||||||
|
// self.programs().prepare();
|
||||||
|
// unsafe {
|
||||||
|
// // self.gl.enable(glow::DEPTH_TEST);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn destroy(&mut self) {
|
||||||
|
// self.programs().destroy();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub struct WindowsManager<'a> {
|
||||||
|
// gl: &'a glow::Context,
|
||||||
|
// gl_resource_dispatcher: GlResourceDispatcher,
|
||||||
|
// windows: HashMap<ImString, WindowData<'a>>,
|
||||||
|
|
||||||
|
// program_with_window: HashMap<ProgramId, Vec<ImString>>,
|
||||||
|
// }
|
||||||
|
|
||||||
|
// impl<'a> WindowsManager<'a> {
|
||||||
|
// fn new(gl: &'a glow::Context, gl_resource_dispatcher: GlResourceDispatcher) -> Self {
|
||||||
|
// Self {
|
||||||
|
// gl,
|
||||||
|
// gl_resource_dispatcher,
|
||||||
|
// windows: HashMap::with_capacity(30),
|
||||||
|
// program_with_window: HashMap::with_capacity(30),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// fn destroy_window(&mut self) {
|
||||||
|
// for (id, window) in self.windows.iter() {
|
||||||
|
// if !window.open {
|
||||||
|
// self.program_with_window.iter_mut().for_each(|(_, v)| {
|
||||||
|
// v.retain(|v| v != id);
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// self.windows.retain(|_, v| v.open == true);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn create_framebuffer(
|
||||||
|
// &mut self,
|
||||||
|
// id: &str,
|
||||||
|
// size: [i32; 2],
|
||||||
|
// ) -> Result<(RcGlFramebuffer<'a>, RcGlTexture<'a>)> {
|
||||||
|
// let id = &ImString::new(id);
|
||||||
|
|
||||||
|
// if self.windows.contains_key(id) {
|
||||||
|
// let info = self.windows.get(id).unwrap();
|
||||||
|
// if info.gl_fb_resources.is_some() && info.gl_tex_resources.is_some() {
|
||||||
|
// return Ok((
|
||||||
|
// info.gl_fb_resources.as_ref().unwrap().clone(),
|
||||||
|
// info.gl_tex_resources.as_ref().unwrap().clone(),
|
||||||
|
// ));
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// let gl = self.gl;
|
||||||
|
// let tex = unsafe {
|
||||||
|
// let framebuffer = self
|
||||||
|
// .gl_resource_dispatcher
|
||||||
|
// .create_new::<NativeFramebuffer>();
|
||||||
|
// // let framebuffer = gl.create_framebuffer().unwrap();
|
||||||
|
// gl.bind_framebuffer(glow::FRAMEBUFFER, Some(framebuffer.native()));
|
||||||
|
|
||||||
|
// let texture = self.gl_resource_dispatcher.create_new::<NativeTexture>();
|
||||||
|
// gl.bind_texture(glow::TEXTURE_2D, Some(texture.native()));
|
||||||
|
// gl.tex_image_2d(
|
||||||
|
// glow::TEXTURE_2D,
|
||||||
|
// 0,
|
||||||
|
// glow::RGB8 as i32,
|
||||||
|
// size[0],
|
||||||
|
// size[1],
|
||||||
|
// 0,
|
||||||
|
// glow::RGB,
|
||||||
|
// glow::UNSIGNED_BYTE,
|
||||||
|
// None,
|
||||||
|
// );
|
||||||
|
// gl.tex_parameter_i32(
|
||||||
|
// glow::TEXTURE_2D,
|
||||||
|
// glow::TEXTURE_MIN_FILTER,
|
||||||
|
// glow::LINEAR as i32,
|
||||||
|
// );
|
||||||
|
// gl.tex_parameter_i32(
|
||||||
|
// glow::TEXTURE_2D,
|
||||||
|
// glow::TEXTURE_MAG_FILTER,
|
||||||
|
// glow::LINEAR as i32,
|
||||||
|
// );
|
||||||
|
|
||||||
|
// gl.framebuffer_texture_2d(
|
||||||
|
// glow::FRAMEBUFFER,
|
||||||
|
// glow::COLOR_ATTACHMENT0,
|
||||||
|
// glow::TEXTURE_2D,
|
||||||
|
// Some(texture.native()),
|
||||||
|
// 0,
|
||||||
|
// );
|
||||||
|
|
||||||
|
// assert_eq!(
|
||||||
|
// gl.check_framebuffer_status(glow::FRAMEBUFFER),
|
||||||
|
// glow::FRAMEBUFFER_COMPLETE
|
||||||
|
// );
|
||||||
|
|
||||||
|
// gl.bind_framebuffer(glow::FRAMEBUFFER, None);
|
||||||
|
// gl.bind_texture(glow::TEXTURE_2D, None);
|
||||||
|
// (framebuffer, texture)
|
||||||
|
// };
|
||||||
|
|
||||||
|
// Ok(tex)
|
||||||
|
// }
|
||||||
|
// pub fn create_render_window(
|
||||||
|
// &mut self,
|
||||||
|
// title: &str,
|
||||||
|
// size: [f32; 2],
|
||||||
|
// ) -> Result<&mut WindowData<'a>> {
|
||||||
|
// // Insert the window data into the windows hashmap
|
||||||
|
// let id = ImString::new(title);
|
||||||
|
// let mut data = WindowData::new(&self.gl, id.clone(), size, None);
|
||||||
|
// let (fb, tex) =
|
||||||
|
// self.create_framebuffer(title, [size[0].floor() as i32, size[1].floor() as i32])?;
|
||||||
|
// data.gl_fb_resources = Some(fb);
|
||||||
|
// data.gl_tex_resources = Some(tex);
|
||||||
|
// self.windows.insert(id.clone(), data);
|
||||||
|
// let window = self.windows.get_mut(&id).unwrap();
|
||||||
|
// Ok(window)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// fn show_window(&mut self, ui: &Ui) {
|
||||||
|
// let mut need_resize = vec![];
|
||||||
|
|
||||||
|
// for (id, window) in self.windows.iter_mut() {
|
||||||
|
// let mut window = window;
|
||||||
|
// ui.window(&window.title)
|
||||||
|
// .size(window.size, imgui::Condition::FirstUseEver)
|
||||||
|
// .opened(&mut window.open)
|
||||||
|
// .flags(imgui::WindowFlags::NO_SCROLLBAR)
|
||||||
|
// .build(|| {
|
||||||
|
// if ui.is_mouse_clicked(imgui::MouseButton::Left) {
|
||||||
|
// let io = ui.io();
|
||||||
|
// let pos = io.mouse_pos;
|
||||||
|
|
||||||
|
// let window_pos = ui.window_pos();
|
||||||
|
// window.last_mouse_position =
|
||||||
|
// [pos[0] - window_pos[0], pos[1] - window_pos[1]];
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if ui.is_mouse_dragging(imgui::MouseButton::Left) {
|
||||||
|
// if ui.is_window_hovered() {
|
||||||
|
// let delta = ui.mouse_drag_delta();
|
||||||
|
// window.last_mouse_delta = delta;
|
||||||
|
// window.accmulate_mouse_delta = [
|
||||||
|
// window.accmulate_mouse_delta[0] + delta[0],
|
||||||
|
// window.accmulate_mouse_delta[1] + delta[1],
|
||||||
|
// ];
|
||||||
|
// window.motion = Some(MouseState::Drag {
|
||||||
|
// from: window.last_mouse_position,
|
||||||
|
// delta: delta,
|
||||||
|
// });
|
||||||
|
|
||||||
|
// println!(
|
||||||
|
// "Dragging: {:?} {:?}",
|
||||||
|
// window.last_mouse_position, window.accmulate_mouse_delta
|
||||||
|
// );
|
||||||
|
// window.modifer.as_mut().map(|v| {
|
||||||
|
// v.exec(window.motion.as_ref().unwrap());
|
||||||
|
// });
|
||||||
|
// window.need_redraw = true;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if ui.is_mouse_released(imgui::MouseButton::Left) {
|
||||||
|
// if window.size != ui.window_size() {
|
||||||
|
// window.size = ui.window_size();
|
||||||
|
// info!("resized: {:?}", window.size);
|
||||||
|
// need_resize.push((window.title.clone(), ui.window_size()));
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if let Some(texture) = window.gl_tex_resources.as_ref() {
|
||||||
|
// let cursor = ui.cursor_pos();
|
||||||
|
// imgui::Image::new(texture.native2imguiid(), ui.window_size()).build(ui);
|
||||||
|
// ui.set_cursor_pos(cursor);
|
||||||
|
// if ui.invisible_button(&window.title, ui.window_size()) {
|
||||||
|
// let io = ui.io();
|
||||||
|
// let pos = io.mouse_pos;
|
||||||
|
// let window_pos = ui.window_pos();
|
||||||
|
// let related_pos = [pos[0] - window_pos[0], pos[1] - window_pos[1]];
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// for (id, size) in need_resize.iter() {
|
||||||
|
// self.reset_window_size(id, *size);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// fn reset_window_size(&mut self, id: &ImString, size: [f32; 2]) {
|
||||||
|
// let window_info = self.windows.get_mut(id).unwrap();
|
||||||
|
// window_info.need_redraw = true;
|
||||||
|
|
||||||
|
// info!("resize: {:?}", size);
|
||||||
|
// let tex = unsafe {
|
||||||
|
// self.gl.bind_framebuffer(
|
||||||
|
// glow::FRAMEBUFFER,
|
||||||
|
// window_info.gl_fb_resources.as_ref().map(|v| v.native()),
|
||||||
|
// );
|
||||||
|
// let texture = self.gl.create_texture().unwrap();
|
||||||
|
// self.gl.bind_texture(glow::TEXTURE_2D, Some(texture));
|
||||||
|
// self.gl.tex_image_2d(
|
||||||
|
// glow::TEXTURE_2D,
|
||||||
|
// 0,
|
||||||
|
// glow::RGB8 as i32,
|
||||||
|
// size[0].floor() as i32,
|
||||||
|
// size[1].floor() as i32,
|
||||||
|
// 0,
|
||||||
|
// glow::RGB,
|
||||||
|
// glow::UNSIGNED_BYTE,
|
||||||
|
// None,
|
||||||
|
// );
|
||||||
|
// self.gl.tex_parameter_i32(
|
||||||
|
// glow::TEXTURE_2D,
|
||||||
|
// glow::TEXTURE_MIN_FILTER,
|
||||||
|
// glow::LINEAR as i32,
|
||||||
|
// );
|
||||||
|
// self.gl.tex_parameter_i32(
|
||||||
|
// glow::TEXTURE_2D,
|
||||||
|
// glow::TEXTURE_MAG_FILTER,
|
||||||
|
// glow::LINEAR as i32,
|
||||||
|
// );
|
||||||
|
|
||||||
|
// self.gl.framebuffer_texture_2d(
|
||||||
|
// glow::FRAMEBUFFER,
|
||||||
|
// glow::COLOR_ATTACHMENT0,
|
||||||
|
// glow::TEXTURE_2D,
|
||||||
|
// Some(texture),
|
||||||
|
// 0,
|
||||||
|
// );
|
||||||
|
|
||||||
|
// assert_eq!(
|
||||||
|
// self.gl.check_framebuffer_status(glow::FRAMEBUFFER),
|
||||||
|
// glow::FRAMEBUFFER_COMPLETE
|
||||||
|
// );
|
||||||
|
|
||||||
|
// self.gl.bind_framebuffer(glow::FRAMEBUFFER, None);
|
||||||
|
// self.gl.bind_texture(glow::TEXTURE_2D, None);
|
||||||
|
|
||||||
|
// texture
|
||||||
|
// };
|
||||||
|
|
||||||
|
// window_info.gl_tex_resources = Some(RcGlResource::new(self.gl, tex));
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn set_window_program<P: Graphics>(&mut self, window: ImString) {
|
||||||
|
// self.program_with_window
|
||||||
|
// .entry(P::id)
|
||||||
|
// .and_modify(|v| v.push(window.clone()))
|
||||||
|
// .or_insert(vec![window]);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub struct Programs<'a> {
|
||||||
|
// gl: &'a glow::Context,
|
||||||
|
// pub text_module: NonNull<Text<'a>>,
|
||||||
|
// pub ppi_module: PPI,
|
||||||
|
// }
|
||||||
|
|
||||||
|
impl<'a> Programs<'a> {
|
||||||
|
fn new(gl: &'a glow::Context, viewport: &Viewport) -> Result<Self> {
|
||||||
|
let font_manager = FontManager::new()?;
|
||||||
|
let mut cmap = LinearColormap::new()?;
|
||||||
|
cmap.set_colors(vec![
|
||||||
|
[170, 170, 170, 255],
|
||||||
|
[0, 34, 255, 255],
|
||||||
|
[1, 160, 246, 255],
|
||||||
|
[0, 236, 236, 255],
|
||||||
|
[0, 216, 0, 255],
|
||||||
|
[1, 144, 0, 255],
|
||||||
|
[255, 255, 0, 255],
|
||||||
|
[231, 192, 0, 255],
|
||||||
|
[255, 144, 0, 255],
|
||||||
|
[255, 0, 0, 255],
|
||||||
|
[214, 0, 0, 255],
|
||||||
|
[192, 0, 0, 255],
|
||||||
|
[255, 0, 240, 255],
|
||||||
|
[150, 0, 180, 255],
|
||||||
|
]);
|
||||||
|
cmap.set_range(0.0, 70.0);
|
||||||
|
let cmap = Box::new(cmap);
|
||||||
|
|
||||||
|
let mut ppi = PPI::new()?;
|
||||||
|
ppi.set_viewport(&viewport);
|
||||||
|
ppi.set_colormap(cmap);
|
||||||
|
|
||||||
|
let mut text_module = Text::new(gl, font_manager)?;
|
||||||
|
text_module.set_viewport(&viewport);
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
gl,
|
||||||
|
ppi_module: ppi,
|
||||||
|
text_module,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prepare(&mut self) {
|
||||||
|
self.ppi_module.compile(&self.gl).unwrap();
|
||||||
|
self.text_module.compile(&self.gl).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn destroy(&mut self) {
|
||||||
|
self.ppi_module.destroy(&self.gl).unwrap();
|
||||||
|
self.text_module.destroy(&self.gl).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_ppi_render(&self, config: Option<PPIConfig>) -> Attach<'a> {
|
||||||
|
self.create_render(&self.ppi_module)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_text_render(&self) -> Attach<'a> {
|
||||||
|
self.create_render(&self.text_module)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_render<P: Graphics>(&self, p: &P) -> Attach<'a> {
|
||||||
|
let (vao, vbo, ebo) = p.init(&self.gl);
|
||||||
|
Attach {
|
||||||
|
vao: RcGlResource::new(&self.gl, vao),
|
||||||
|
vbo: RcGlResource::new(&self.gl, vbo),
|
||||||
|
ebo: ebo.map(|ebo| RcGlResource::new(&self.gl, ebo)),
|
||||||
|
len: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pub struct WindowProgram<'a, 'sl, 'pro, P: Graphics> {
|
||||||
|
// window: &'sl mut WindowData<'a>,
|
||||||
|
// program: &'pro P,
|
||||||
|
// }
|
||||||
|
|
||||||
|
// impl<'a, 's, 'b, P: Graphics> WindowProgram<'a, 's, 'b, P> {
|
||||||
|
// pub fn bind_data(&mut self, data: &P::Data) -> Result {
|
||||||
|
// use bytemuck::cast_slice;
|
||||||
|
// let data = self.program.bake(data)?;
|
||||||
|
// let attach = self.window.attach.get_mut(P::id).unwrap();
|
||||||
|
// attach.len = data.2;
|
||||||
|
|
||||||
|
// let gl = self.window.gl;
|
||||||
|
// unsafe {
|
||||||
|
// gl.bind_vertex_array(Some(attach.vao.native()));
|
||||||
|
// // gl.bind_buffer(glow::VERTEX_ARRAY, Some(attach.vbo.native()));
|
||||||
|
// gl.buffer_data_u8_slice(
|
||||||
|
// glow::ARRAY_BUFFER,
|
||||||
|
// cast_slice(data.0.as_slice()),
|
||||||
|
// glow::STATIC_DRAW,
|
||||||
|
// );
|
||||||
|
// if let Some(_) = attach.ebo.as_ref() {
|
||||||
|
// // gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(ebo.native()));
|
||||||
|
// gl.buffer_data_u8_slice(
|
||||||
|
// glow::ELEMENT_ARRAY_BUFFER,
|
||||||
|
// cast_slice(&data.1.as_ref().unwrap()),
|
||||||
|
// glow::STATIC_DRAW,
|
||||||
|
// );
|
||||||
|
|
||||||
|
// // gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, None);
|
||||||
|
// }
|
||||||
|
// // gl.bind_buffer(glow::VERTEX_ARRAY, None);
|
||||||
|
|
||||||
|
// gl.bind_vertex_array(None);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Ok(())
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn set_config(&mut self, config: P::Config)
|
||||||
|
// where
|
||||||
|
// Config: From<P::Config>,
|
||||||
|
// {
|
||||||
|
// let config = Config::from(config);
|
||||||
|
// self.window
|
||||||
|
// .confs
|
||||||
|
// .entry(P::id)
|
||||||
|
// .and_modify(|v| *v = config.clone())
|
||||||
|
// .or_insert(config);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub struct WindowData<'a> {
|
||||||
|
// gl: &'a glow::Context,
|
||||||
|
// pub title: ImString,
|
||||||
|
// pub open: bool,
|
||||||
|
// pub copy_from: Option<ImString>,
|
||||||
|
// pub size: [f32; 2],
|
||||||
|
// gl_fb_resources: Option<RcGlResource<'a, NativeFramebuffer>>,
|
||||||
|
// gl_tex_resources: Option<RcGlResource<'a, NativeTexture>>,
|
||||||
|
// need_redraw: bool,
|
||||||
|
// attach: HashMap<ProgramId, Attach<'a>>,
|
||||||
|
// confs: HashMap<ProgramId, Config>,
|
||||||
|
|
||||||
|
// re_init: bool,
|
||||||
|
// last_mouse_position: [f32; 2],
|
||||||
|
// last_mouse_delta: [f32; 2],
|
||||||
|
// accmulate_mouse_delta: [f32; 2],
|
||||||
|
// mouse_position: [f32; 2],
|
||||||
|
// motion: Option<MouseState>,
|
||||||
|
// modifer: Option<ModiferType>,
|
||||||
|
// }
|
||||||
|
|
||||||
|
// impl<'a> WindowData<'a> {
|
||||||
|
// fn new(
|
||||||
|
// gl: &'a glow::Context,
|
||||||
|
// title: ImString,
|
||||||
|
// size: [f32; 2],
|
||||||
|
// modifer: Option<ModiferType>,
|
||||||
|
// ) -> Self {
|
||||||
|
// Self {
|
||||||
|
// gl,
|
||||||
|
// title,
|
||||||
|
// open: true,
|
||||||
|
// copy_from: None,
|
||||||
|
// size,
|
||||||
|
// last_mouse_position: [0.0, 0.0],
|
||||||
|
// last_mouse_delta: [0.0, 0.0],
|
||||||
|
// accmulate_mouse_delta: [0.0, 0.0],
|
||||||
|
// mouse_position: [0.0, 0.0],
|
||||||
|
// motion: None,
|
||||||
|
|
||||||
|
// gl_fb_resources: None,
|
||||||
|
// gl_tex_resources: None,
|
||||||
|
// need_redraw: true,
|
||||||
|
// attach: HashMap::with_capacity(10),
|
||||||
|
// re_init: true,
|
||||||
|
// confs: HashMap::with_capacity(5),
|
||||||
|
// modifer,
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// fn set<F>(&mut self, f: F)
|
||||||
|
// where
|
||||||
|
// F: FnOnce(&mut Self),
|
||||||
|
// {
|
||||||
|
// f(self);
|
||||||
|
// self.need_redraw = true;
|
||||||
|
// // let v = self.program_with_window.entry(PPI::id).or_insert(vec![]);
|
||||||
|
// // v.push(id.clone());
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn set_attach<P: Graphics>(&mut self, attach: Attach<'a>) {
|
||||||
|
// self.set(move |w| {
|
||||||
|
// w.attach.insert(P::id, attach);
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn with_program<'b, 's, P: Graphics>(
|
||||||
|
// &'b mut self,
|
||||||
|
// program: &'s P,
|
||||||
|
// ) -> WindowProgram<'a, 'b, 's, P>
|
||||||
|
// where
|
||||||
|
// 's: 'b,
|
||||||
|
// 'a: 's,
|
||||||
|
// {
|
||||||
|
// WindowProgram {
|
||||||
|
// program,
|
||||||
|
// window: self,
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn set_window_modifer(&mut self, modifier: Option<ModiferType>) {
|
||||||
|
// self.set(|w| w.modifer = modifier);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn id(&self) -> &ImString {
|
||||||
|
// &self.title
|
||||||
|
// }
|
||||||
|
|
||||||
|
// fn set_current_mouse_delta(&mut self, delta: [f32; 2]) {
|
||||||
|
// self.last_mouse_delta = delta;
|
||||||
|
// self.accmulate_mouse_delta = [
|
||||||
|
// self.accmulate_mouse_delta[0] + delta[0],
|
||||||
|
// self.accmulate_mouse_delta[1] + delta[1],
|
||||||
|
// ];
|
||||||
|
// }
|
||||||
|
|
||||||
|
// fn set_mouse_postion(&mut self, pos: [f32; 2]) {
|
||||||
|
// self.mouse_position = pos;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// fn set_motion(&mut self, motion: MouseState) {
|
||||||
|
// self.motion = Some(motion);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// fn set_need_redraw(&mut self) {
|
||||||
|
// self.need_redraw = true;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// fn set_re_init(&mut self) {
|
||||||
|
// self.re_init = true;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// fn on_mouse_drag(&mut self) {
|
||||||
|
// let state = MouseState::Drag {
|
||||||
|
// from: self.last_mouse_position,
|
||||||
|
// delta: self.last_mouse_delta,
|
||||||
|
// };
|
||||||
|
// self.set_motion(state.clone());
|
||||||
|
|
||||||
|
// self.modifer.as_mut().map(|m| {});
|
||||||
|
|
||||||
|
// self.set_need_redraw();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// macro_rules! modifer_exec {
|
||||||
|
// ($(($t:ty => $b:tt),)+) => {
|
||||||
|
// impl ModiferType {
|
||||||
|
// pub fn exec(&mut self, motion: &MouseState) {
|
||||||
|
// match self {
|
||||||
|
// $(
|
||||||
|
// ModiferType::$b(b) => {
|
||||||
|
// b.attach_with_mouse(motion);
|
||||||
|
// }
|
||||||
|
// )+
|
||||||
|
// _ => {}
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// impl AttaWithProgram for ModiferType {
|
||||||
|
// fn attach_with_program(&self, gl: &glow::Context, program: &Program) -> Result {
|
||||||
|
// match self {
|
||||||
|
// $(
|
||||||
|
|
||||||
|
// ModiferType::$b(b) => {
|
||||||
|
// b.attach_with_program(gl, program)?;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// )+
|
||||||
|
// _ => {
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// Ok(())
|
||||||
|
// }
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
|
// $(
|
||||||
|
// impl From<$t> for ModiferType {
|
||||||
|
// fn from(t: $t) -> Self {
|
||||||
|
// ModiferType::$b(t)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// )+
|
||||||
|
// }
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub enum ModiferType {
|
||||||
|
// ThreeD(ThreeD),
|
||||||
|
// TwoD,
|
||||||
|
// }
|
||||||
|
|
||||||
|
// modifer_exec!((ThreeD => ThreeD),);
|
||||||
|
|
||||||
|
// #[derive(Debug, Clone)]
|
||||||
|
// pub struct GlResourceDispatcher {
|
||||||
|
// gl: Rc<glow::Context>,
|
||||||
|
// }
|
||||||
|
|
||||||
|
// impl GlResourceDispatcher {
|
||||||
|
// pub fn new(gl: Rc<glow::Context>) -> Self {
|
||||||
|
// Self { gl }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn create_new<'a, T: Resource>(&'a self) -> RcGlResource<'a, T> {
|
||||||
|
// RcGlResource::new(&self.gl, T::create(&self.gl))
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn create_new_rc<T: Resource>(&self) -> RcGlRcResource<T> {
|
||||||
|
// RcGlRcResource::new(self.gl.clone(), T::create(&self.gl))
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn gl(&self) -> &Rc<glow::Context> {
|
||||||
|
// &self.gl
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub struct Manager {
|
||||||
|
// pub gl_resource_dispatcher: GlResourceDispatcher,
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub struct GL {
|
||||||
|
// gl: Rc<glow::Context>,
|
||||||
|
// }
|
||||||
|
|
||||||
|
// impl GL {
|
||||||
|
// pub fn new(gl: Rc<glow::Context>) -> Self {
|
||||||
|
// Self { gl }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// fn gl(&self) -> &glow::Context {
|
||||||
|
// &self.gl
|
||||||
|
// }
|
||||||
|
|
||||||
|
// fn rc_gl(&self) -> Rc<glow::Context> {
|
||||||
|
// self.gl.clone()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
@ -47,6 +47,16 @@ impl Camera {
|
|||||||
|
|
||||||
pub fn get_view_matrix(&self) -> Mat4x4 {
|
pub fn get_view_matrix(&self) -> Mat4x4 {
|
||||||
let l = self.pos + self.front;
|
let l = self.pos + self.front;
|
||||||
look_at(&self.pos, &l, &self.upward)
|
look_at(&l, &self.pos, &self.upward)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Camera {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
pos: Vec3::new(0.0, 0.0, 0.0),
|
||||||
|
upward: Vec3::new(0.0, 1.0, 0.0),
|
||||||
|
front: Vec3::new(0.0, 0.0, -1.0),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -41,6 +41,10 @@ impl Program {
|
|||||||
&self.fragment
|
&self.fragment
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn geometry(&self) -> Option<&Shader> {
|
||||||
|
self.geometry.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_hook(&mut self, hook: &str, code: &Snippet) {
|
pub fn set_hook(&mut self, hook: &str, code: &Snippet) {
|
||||||
self.vertex.set_hook(hook, code);
|
self.vertex.set_hook(hook, code);
|
||||||
self.fragment.set_hook(hook, code);
|
self.fragment.set_hook(hook, code);
|
||||||
|
|||||||
@ -30,7 +30,7 @@ impl Shader {
|
|||||||
let code = match code {
|
let code = match code {
|
||||||
CodeType::Code(code) => code.borrow().to_string(),
|
CodeType::Code(code) => code.borrow().to_string(),
|
||||||
CodeType::Path(path) => {
|
CodeType::Path(path) => {
|
||||||
let code = find_file(path).expect("Failed to find file");
|
let code = find_file(path).expect(&format!("Failed to find file {}", path));
|
||||||
code
|
code
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -217,7 +217,13 @@ mod utils {
|
|||||||
body.push_str("}\n\n");
|
body.push_str("}\n\n");
|
||||||
let input = header + &body;
|
let input = header + &body;
|
||||||
|
|
||||||
let result = Snippet::new("fetch_uniform", CodeType::Code(input), false, None).unwrap();
|
let result = Snippet::new(
|
||||||
|
"fetch_uniform",
|
||||||
|
CodeType::<String>::Code(input),
|
||||||
|
false,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@ use std::{
|
|||||||
cell::{Ref, RefCell},
|
cell::{Ref, RefCell},
|
||||||
collections::{HashMap, HashSet},
|
collections::{HashMap, HashSet},
|
||||||
ops::Add,
|
ops::Add,
|
||||||
|
path::Path,
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
sync::atomic::{AtomicUsize, Ordering},
|
sync::atomic::{AtomicUsize, Ordering},
|
||||||
};
|
};
|
||||||
@ -26,9 +27,9 @@ pub enum InputType {
|
|||||||
Other(Variable),
|
Other(Variable),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum CodeType<S: std::borrow::Borrow<str>> {
|
pub enum CodeType<S: std::borrow::Borrow<str> = &'static str, P: AsRef<Path> = &'static str> {
|
||||||
Code(S),
|
Code(S),
|
||||||
Path(std::path::PathBuf),
|
Path(P),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@ -43,9 +44,9 @@ pub struct Snippet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Snippet {
|
impl Snippet {
|
||||||
pub fn new<S: std::borrow::Borrow<str>>(
|
pub fn new<S: std::borrow::Borrow<str>, P: AsRef<Path>>(
|
||||||
name: &'static str,
|
name: &'static str,
|
||||||
code: CodeType<S>,
|
code: CodeType<S, P>,
|
||||||
mangling: bool,
|
mangling: bool,
|
||||||
main: Option<String>,
|
main: Option<String>,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
@ -120,7 +121,11 @@ impl Snippet {
|
|||||||
pub fn call(&self, paras: &Vec<String>) -> Option<String> {
|
pub fn call(&self, paras: &Vec<String>) -> Option<String> {
|
||||||
(self.main.as_ref()).map(|name| {
|
(self.main.as_ref()).map(|name| {
|
||||||
if let Some(link) = &self.link {
|
if let Some(link) = &self.link {
|
||||||
let call_name = self.alias.as_ref().map(|a| a.get(name).unwrap()).unwrap_or(name);
|
let call_name = self
|
||||||
|
.alias
|
||||||
|
.as_ref()
|
||||||
|
.map(|a| a.get(name).unwrap())
|
||||||
|
.unwrap_or(name);
|
||||||
let c = link.call(paras);
|
let c = link.call(paras);
|
||||||
if let Some(c) = c {
|
if let Some(c) = c {
|
||||||
return format!("{}({})", call_name, c);
|
return format!("{}({})", call_name, c);
|
||||||
@ -128,7 +133,11 @@ impl Snippet {
|
|||||||
return format!("{}()", call_name);
|
return format!("{}()", call_name);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let call_name = self.alias.as_ref().map(|a| a.get(name).unwrap()).unwrap_or(name);
|
let call_name = self
|
||||||
|
.alias
|
||||||
|
.as_ref()
|
||||||
|
.map(|a| a.get(name).unwrap())
|
||||||
|
.unwrap_or(name);
|
||||||
// let call_name = self.alias.get(name).unwrap();
|
// let call_name = self.alias.get(name).unwrap();
|
||||||
format!("{}({})", call_name, paras.join(", "))
|
format!("{}({})", call_name, paras.join(", "))
|
||||||
}
|
}
|
||||||
@ -164,7 +173,13 @@ impl Add for Snippet {
|
|||||||
let code = rhs.parsed.to_string();
|
let code = rhs.parsed.to_string();
|
||||||
|
|
||||||
raw_code.push_str(&code);
|
raw_code.push_str(&code);
|
||||||
Snippet::new(self.name, CodeType::Code(raw_code),false, None).unwrap()
|
Snippet::new(
|
||||||
|
self.name,
|
||||||
|
CodeType::<std::string::String>::Code(raw_code),
|
||||||
|
false,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,9 +207,11 @@ mod tests {
|
|||||||
}
|
}
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
let snippet = Snippet::new("polar", CodeType::Code(code), true, None).unwrap();
|
let snippet =
|
||||||
|
Snippet::new("polar", CodeType::<&'static str>::Code(code), true, None).unwrap();
|
||||||
|
|
||||||
let snippet2 = Snippet::new("polar2", CodeType::Code(code),true, None).unwrap();
|
let snippet2 =
|
||||||
|
Snippet::new("polar2", CodeType::<&'static str>::Code(code), true, None).unwrap();
|
||||||
|
|
||||||
let snippet3 = snippet.clone() + snippet2.clone();
|
let snippet3 = snippet.clone() + snippet2.clone();
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T = ()> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
@ -21,4 +21,10 @@ pub enum Error {
|
|||||||
|
|
||||||
#[error("Invalid CoordType")]
|
#[error("Invalid CoordType")]
|
||||||
InvalidDataType,
|
InvalidDataType,
|
||||||
|
|
||||||
|
#[error("Init Error, cause of {0}")]
|
||||||
|
InitError(anyhow::Error),
|
||||||
|
|
||||||
|
#[error("Invalid Font {0}")]
|
||||||
|
FontError(String),
|
||||||
}
|
}
|
||||||
|
|||||||
215
src/font_manager/mod.rs
Normal file
215
src/font_manager/mod.rs
Normal file
@ -0,0 +1,215 @@
|
|||||||
|
use crate::errors::*;
|
||||||
|
use dirs::font_dir;
|
||||||
|
use freetype::face::LoadFlag;
|
||||||
|
use freetype::{Face, GlyphMetrics, Library};
|
||||||
|
|
||||||
|
use image::GrayImage;
|
||||||
|
use image::ImageBuffer;
|
||||||
|
use log::*;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
pub struct FontManager {
|
||||||
|
library: Library,
|
||||||
|
fonts: HashMap<String, Font>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FontManager {
|
||||||
|
pub fn new() -> Result<Self> {
|
||||||
|
// let source = fk::source::SystemSource::new();
|
||||||
|
let library = Library::init().map_err(|e| Error::InitError(e.into()))?;
|
||||||
|
|
||||||
|
let root_path = font_dir().map_or_else(
|
||||||
|
|| {
|
||||||
|
error!("Font dir not found");
|
||||||
|
Err(Error::InitError(anyhow::anyhow!("Font dir not found")))
|
||||||
|
},
|
||||||
|
|font_dir| {
|
||||||
|
info!("Font dir: {:?}", font_dir);
|
||||||
|
Ok(font_dir)
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
library,
|
||||||
|
fonts: HashMap::new(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_font_or_insert(&mut self, path: &str) -> Option<&Font> {
|
||||||
|
if self.fonts.contains_key(path) {
|
||||||
|
return self.fonts.get(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
let font = self.library.new_face(path, 0).ok()?;
|
||||||
|
let font = Font::new(font).ok()?;
|
||||||
|
self.fonts.insert(path.to_string(), font);
|
||||||
|
|
||||||
|
self.fonts.get(path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Font {
|
||||||
|
font: Face,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CharImg {
|
||||||
|
img: Vec<u8>,
|
||||||
|
width: usize,
|
||||||
|
height: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CharImg {
|
||||||
|
pub fn to_image(&self) -> GrayImage {
|
||||||
|
ImageBuffer::from_raw(self.width as u32, self.height as u32, self.img.clone())
|
||||||
|
.map(|img| img.into())
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pixels(&self) -> &[u8] {
|
||||||
|
&self.img
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn width(&self) -> usize {
|
||||||
|
self.width
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn height(&self) -> usize {
|
||||||
|
self.height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Font {
|
||||||
|
fn new(font: Face) -> Result<Self> {
|
||||||
|
Ok(Self { font })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_char(&self, c: char, size: isize) -> Result<CharImg> {
|
||||||
|
self.font.set_char_size(0, size * 64, 0, 96).unwrap();
|
||||||
|
|
||||||
|
if let Err(_) = self.font.load_char(c as usize, LoadFlag::RENDER) {
|
||||||
|
self.font.load_char(0, LoadFlag::RENDER).unwrap();
|
||||||
|
}
|
||||||
|
let glyph = self.font.glyph();
|
||||||
|
let bitmap = glyph.bitmap();
|
||||||
|
if bitmap.width() == 0 || bitmap.rows() == 0 {
|
||||||
|
return Err(Error::FontError("bitmap is empty".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(CharImg {
|
||||||
|
img: bitmap.buffer().to_vec(),
|
||||||
|
width: bitmap.width() as usize,
|
||||||
|
height: bitmap.rows() as usize,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_char_size(&self, size: isize) {
|
||||||
|
self.font.set_char_size(0, size * 64, 0, 96).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_char(&self, c: char) {
|
||||||
|
self.font.load_char(c as usize, LoadFlag::RENDER).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn glyph(&self) -> &freetype::GlyphSlot {
|
||||||
|
self.font.glyph()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_advance(&self) -> (f32, f32) {
|
||||||
|
let advance = self.font.glyph().advance();
|
||||||
|
(advance.x as f32 / 64.0, advance.y as f32 / 64.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_kerning(&self, prev: char, c: char) -> (f32, f32) {
|
||||||
|
let prev = self.font.get_char_index(prev as usize).unwrap();
|
||||||
|
let curr = self.font.get_char_index(c as usize).unwrap();
|
||||||
|
let kerning = self
|
||||||
|
.font
|
||||||
|
.get_kerning(prev, curr, freetype::face::KerningMode::KerningDefault)
|
||||||
|
.unwrap();
|
||||||
|
(kerning.x as f32 / 64.0, kerning.y as f32 / 64.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_line_height(&self) -> i16 {
|
||||||
|
self.font.height()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_metrics(&self, c: char) -> GlyphMetrics {
|
||||||
|
self.font.glyph().metrics()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum FontSize {
|
||||||
|
Absolute(f32),
|
||||||
|
WindowScale(f32),
|
||||||
|
DistanceScale(f32),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct FontStyle {
|
||||||
|
pub postscript_name: String,
|
||||||
|
pub size: FontSize,
|
||||||
|
pub color: [f32; 4],
|
||||||
|
pub style: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for FontStyle {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
postscript_name: "resources/Roboto-Regular.ttf".to_string(),
|
||||||
|
size: FontSize::Absolute(12.0),
|
||||||
|
color: [1.0, 1.0, 1.0, 1.0],
|
||||||
|
style: 0.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct FontStyleBuilder {
|
||||||
|
style: FontStyle,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FontStyleBuilder {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(self) -> FontStyle {
|
||||||
|
self.style
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn postscript_name(mut self, name: &str) -> Self {
|
||||||
|
self.style.postscript_name = name.to_string();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn size(mut self, size: FontSize) -> Self {
|
||||||
|
self.style.size = size;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn color(mut self, color: [f32; 4]) -> Self {
|
||||||
|
self.style.color = color;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FontStyle {
|
||||||
|
pub fn buider() -> FontStyleBuilder {
|
||||||
|
FontStyleBuilder::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod test {
|
||||||
|
use super::{FontManager, FontStyle};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test() {
|
||||||
|
let mut fon = FontManager::new().unwrap();
|
||||||
|
let font =
|
||||||
|
fon.get_font_or_insert("/Users/tsuki/projects/radar-gi/resources/Dokdo-Regular.ttf");
|
||||||
|
|
||||||
|
if let Some(font) = font {
|
||||||
|
font.get_char('g', 64).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -36,7 +36,7 @@ impl AggFastPath {
|
|||||||
|
|
||||||
let input_snippet = Snippet::new(
|
let input_snippet = Snippet::new(
|
||||||
"input",
|
"input",
|
||||||
CodeType::Code(
|
CodeType::<&'static str>::Code(
|
||||||
"
|
"
|
||||||
layout(location = 0) in vec3 prev;
|
layout(location = 0) in vec3 prev;
|
||||||
layout(location = 1) in vec3 curr;
|
layout(location = 1) in vec3 curr;
|
||||||
@ -126,6 +126,8 @@ impl Colletion for AggFastPath {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Graphics for AggFastPath {
|
impl Graphics for AggFastPath {
|
||||||
|
const id: &'static str = "AggPath";
|
||||||
|
type Config = ();
|
||||||
fn compile(&mut self, gl: &glow::Context) -> Result<()> {
|
fn compile(&mut self, gl: &glow::Context) -> Result<()> {
|
||||||
use bytemuck::cast_slice;
|
use bytemuck::cast_slice;
|
||||||
self.program.compile(gl)?;
|
self.program.compile(gl)?;
|
||||||
@ -214,7 +216,7 @@ impl Graphics for AggFastPath {
|
|||||||
&mut self.program
|
&mut self.program
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_config(&mut self, gl: &glow::Context, config: Option<&Config>) -> Result<()> {
|
fn set_config(&mut self, gl: &glow::Context, config: &Self::Config) -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -237,7 +239,11 @@ impl Graphics for AggFastPath {
|
|||||||
impl AttaWithBuffer for AggFastPath {
|
impl AttaWithBuffer for AggFastPath {
|
||||||
type Data = ();
|
type Data = ();
|
||||||
|
|
||||||
fn bake(&self, data: &Self::Data) -> Result<(Vec<f32>, Option<Vec<u32>>, i32)> {
|
fn bake(
|
||||||
|
&self,
|
||||||
|
data: &Self::Data,
|
||||||
|
config: &<Self as Graphics>::Config,
|
||||||
|
) -> Result<(Vec<f32>, Option<Vec<u32>>, i32)> {
|
||||||
Ok((vec![], None, 0))
|
Ok((vec![], None, 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
740
src/graphics/font/esdt/esdt.rs
Normal file
740
src/graphics/font/esdt/esdt.rs
Normal file
@ -0,0 +1,740 @@
|
|||||||
|
//! Rust port of the ESDT ("Euclidean Subpixel Distance Transform") algorithm.
|
||||||
|
//!
|
||||||
|
//!
|
||||||
|
//! This algorithm was originally published as the [`@use-gpu/glyph`](https://www.npmjs.com/package/@use-gpu/glyph)
|
||||||
|
//! `npm` package, and was described in <https://acko.net/blog/subpixel-distance-transform/>.
|
||||||
|
|
||||||
|
use super::img::{Bitmap, Image2d, NDCursor, NDCursorExt as _, Unorm8};
|
||||||
|
|
||||||
|
// HACK(eddyb) only exists to allow toggling precision for testing purposes.
|
||||||
|
#[cfg(sdfer_use_f64_instead_of_f32)]
|
||||||
|
type f32 = f64;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub struct Params {
|
||||||
|
pub pad: usize,
|
||||||
|
pub radius: f32,
|
||||||
|
pub cutoff: f32,
|
||||||
|
pub solidify: bool,
|
||||||
|
pub preprocess: bool,
|
||||||
|
// FIXME(eddyb) implement.
|
||||||
|
// pub postprocess: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Params {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
pad: 4,
|
||||||
|
radius: 3.0,
|
||||||
|
cutoff: 0.25,
|
||||||
|
solidify: true,
|
||||||
|
preprocess: false,
|
||||||
|
// FIXME(eddyb) implement.
|
||||||
|
// postprocess: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Opaque `struct` allowing buffer reuse between SDF computations, instead of
|
||||||
|
/// reallocating all the buffers every time.
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct ReusableBuffers(ReusableBuffers2d, ReusableBuffers1d);
|
||||||
|
|
||||||
|
// Convert grayscale glyph to SDF
|
||||||
|
pub fn glyph_to_sdf(
|
||||||
|
glyph: &mut Image2d<Unorm8, impl AsMut<[Unorm8]> + AsRef<[Unorm8]>>,
|
||||||
|
params: Params,
|
||||||
|
reuse_bufs: Option<ReusableBuffers>,
|
||||||
|
) -> (Image2d<Unorm8>, ReusableBuffers) {
|
||||||
|
if params.solidify {
|
||||||
|
solidify_alpha(glyph.reborrow_mut());
|
||||||
|
}
|
||||||
|
glyph_to_esdt(glyph.reborrow_mut(), params, reuse_bufs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Solidify semi-transparent areas
|
||||||
|
fn solidify_alpha(mut glyph: Image2d<Unorm8, &mut [Unorm8]>) {
|
||||||
|
let (w, h) = (glyph.width(), glyph.height());
|
||||||
|
|
||||||
|
let mut mask: Image2d<u8> = Image2d::new(w, h);
|
||||||
|
|
||||||
|
let get_data = |x: isize, y: isize| {
|
||||||
|
if x >= 0 && (x as usize) < w && y >= 0 && (y as usize) < h {
|
||||||
|
glyph[(x as usize, y as usize)]
|
||||||
|
} else {
|
||||||
|
Unorm8::MIN
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut masked = 0;
|
||||||
|
|
||||||
|
// Mask pixels whose alpha matches their 4 adjacent neighbors (within 16 steps)
|
||||||
|
// and who don't have black or white neighbors.
|
||||||
|
for y in 0..(h as isize) {
|
||||||
|
for x in 0..(w as isize) {
|
||||||
|
let a = get_data(x, y);
|
||||||
|
// FIXME(eddyb) audit all comparisons with `254` and try removing them.
|
||||||
|
if a == Unorm8::MIN || a >= Unorm8::from_bits(254) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let l = get_data(x - 1, y);
|
||||||
|
let r = get_data(x + 1, y);
|
||||||
|
let t = get_data(x, y - 1);
|
||||||
|
let b = get_data(x, y + 1);
|
||||||
|
|
||||||
|
let (min, max) = [a, l, r, t, b]
|
||||||
|
.into_iter()
|
||||||
|
.map(|x| (x, x))
|
||||||
|
.reduce(|(a_min, a_max), (b_min, b_max)| (a_min.min(b_min), a_max.max(b_max)))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let [a, min, max] = [a, min, max].map(Unorm8::to_bits);
|
||||||
|
|
||||||
|
// FIXME(eddyb) audit all comparisons with `254` and try removing them.
|
||||||
|
if (max - min) < 16 && min > 0 && max < 254 {
|
||||||
|
// NOTE(eddyb) `min > 0` guarantees all neighbors are in-bounds.
|
||||||
|
let (x, y) = (x as usize, y as usize);
|
||||||
|
|
||||||
|
// Spread to 4 neighbors with max
|
||||||
|
mask[(x - 1, y)] = mask[(x - 1, y)].max(a);
|
||||||
|
mask[(x, y - 1)] = mask[(x, y - 1)].max(a);
|
||||||
|
mask[(x, y)] = a;
|
||||||
|
mask[(x + 1, y)] = mask[(x + 1, y)].max(a);
|
||||||
|
mask[(x, y + 1)] = mask[(x, y + 1)].max(a);
|
||||||
|
masked += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if masked == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let get_mask = |x: isize, y: isize| {
|
||||||
|
if x >= 0 && (x as usize) < w && y >= 0 && (y as usize) < h {
|
||||||
|
mask[(x as usize, y as usize)]
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Sample 3x3 area for alpha normalization factor
|
||||||
|
for y in 0..(h as isize) {
|
||||||
|
for x in 0..(w as isize) {
|
||||||
|
let a = &mut glyph[(x as usize, y as usize)];
|
||||||
|
// FIXME(eddyb) audit all comparisons with `254` and try removing them.
|
||||||
|
if *a == Unorm8::MIN || *a >= Unorm8::from_bits(254) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let c = get_mask(x, y);
|
||||||
|
|
||||||
|
let l = get_mask(x - 1, y);
|
||||||
|
let r = get_mask(x + 1, y);
|
||||||
|
let t = get_mask(x, y - 1);
|
||||||
|
let b = get_mask(x, y + 1);
|
||||||
|
|
||||||
|
let tl = get_mask(x - 1, y - 1);
|
||||||
|
let tr = get_mask(x + 1, y - 1);
|
||||||
|
let bl = get_mask(x - 1, y + 1);
|
||||||
|
let br = get_mask(x + 1, y + 1);
|
||||||
|
|
||||||
|
if let Some(m) = [c, l, r, t, b, tl, tr, bl, br]
|
||||||
|
.into_iter()
|
||||||
|
.find(|&x| x != 0)
|
||||||
|
{
|
||||||
|
*a = Unorm8::from_bits((a.to_bits() as f32 / m as f32 * 255.0) as u8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert grayscale or color glyph to SDF using subpixel distance transform
|
||||||
|
fn glyph_to_esdt(
|
||||||
|
mut glyph: Image2d<Unorm8, &mut [Unorm8]>,
|
||||||
|
params: Params,
|
||||||
|
reuse_bufs: Option<ReusableBuffers>,
|
||||||
|
) -> (Image2d<Unorm8>, ReusableBuffers) {
|
||||||
|
// FIXME(eddyb) use `Params` itself directly in more places.
|
||||||
|
let Params {
|
||||||
|
pad,
|
||||||
|
radius,
|
||||||
|
cutoff,
|
||||||
|
solidify: _,
|
||||||
|
preprocess,
|
||||||
|
} = params;
|
||||||
|
|
||||||
|
let wp = glyph.width() + pad * 2;
|
||||||
|
let hp = glyph.height() + pad * 2;
|
||||||
|
|
||||||
|
let mut state = State::from_glyph(glyph.reborrow_mut(), params, reuse_bufs);
|
||||||
|
|
||||||
|
state.esdt_outer_and_inner(wp, hp);
|
||||||
|
|
||||||
|
// FIXME(eddyb) implement.
|
||||||
|
// if postprocess { state.relax_subpixel_offsets(glyph, pad); }
|
||||||
|
|
||||||
|
let mut sdf = Image2d::from_fn(wp, hp, |x, y| {
|
||||||
|
let i = y * wp + x;
|
||||||
|
let ReusableBuffers2d { xo, yo, xi, yi, .. } = &state.bufs_2d;
|
||||||
|
let outer = ((xo[i].powi(2) + yo[i].powi(2)).sqrt() - 0.5).max(0.0);
|
||||||
|
let inner = ((xi[i].powi(2) + yi[i].powi(2)).sqrt() - 0.5).max(0.0);
|
||||||
|
let d = if outer >= inner { outer } else { -inner };
|
||||||
|
Unorm8::encode(1.0 - (d / radius + cutoff))
|
||||||
|
});
|
||||||
|
|
||||||
|
if !preprocess {
|
||||||
|
paint_into_distance_field(&mut sdf, glyph.reborrow(), params);
|
||||||
|
}
|
||||||
|
|
||||||
|
(sdf, ReusableBuffers(state.bufs_2d, state.reuse_bufs_1d))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helpers
|
||||||
|
fn is_black(x: f32) -> bool {
|
||||||
|
x == 0.0
|
||||||
|
}
|
||||||
|
fn is_white(x: f32) -> bool {
|
||||||
|
x == 1.0
|
||||||
|
}
|
||||||
|
fn is_solid(x: f32) -> bool {
|
||||||
|
x == 0.0 || x == 1.0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Paint original alpha channel into final SDF when gray
|
||||||
|
fn paint_into_distance_field(
|
||||||
|
sdf: &mut Image2d<Unorm8>,
|
||||||
|
glyph: Image2d<Unorm8, &[Unorm8]>,
|
||||||
|
params: Params,
|
||||||
|
) {
|
||||||
|
let Params {
|
||||||
|
pad,
|
||||||
|
radius,
|
||||||
|
cutoff,
|
||||||
|
..
|
||||||
|
} = params;
|
||||||
|
|
||||||
|
for y in 0..glyph.height() {
|
||||||
|
for x in 0..glyph.width() {
|
||||||
|
let a = glyph[(x, y)].decode();
|
||||||
|
if !is_solid(a) {
|
||||||
|
let d = 0.5 - a;
|
||||||
|
sdf[(x + pad, y + pad)] = Unorm8::encode(1.0 - (d / radius + cutoff));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 2D buffers, which get reused (see also `ReusableBuffers` itself).
|
||||||
|
#[derive(Default)]
|
||||||
|
struct ReusableBuffers2d {
|
||||||
|
// FIXME(eddyb) group `outer` with `{x,y}o`.
|
||||||
|
outer: Bitmap,
|
||||||
|
// FIXME(eddyb) group `inner` with `{x,y}i``.
|
||||||
|
inner: Bitmap,
|
||||||
|
|
||||||
|
xo: Vec<f32>,
|
||||||
|
yo: Vec<f32>,
|
||||||
|
xi: Vec<f32>,
|
||||||
|
yi: Vec<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct State {
|
||||||
|
// FIXME(eddyb) do the grouping suggested in `ReusableBuffers2d`, to have
|
||||||
|
// `outer` and `inner` fields in here, to use instead of `ReusableBuffers2d`.
|
||||||
|
bufs_2d: ReusableBuffers2d,
|
||||||
|
reuse_bufs_1d: ReusableBuffers1d,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl State {
|
||||||
|
fn from_glyph(
|
||||||
|
mut glyph: Image2d<Unorm8, &mut [Unorm8]>,
|
||||||
|
params: Params,
|
||||||
|
reuse_bufs: Option<ReusableBuffers>,
|
||||||
|
) -> Self {
|
||||||
|
let Params {
|
||||||
|
pad,
|
||||||
|
// FIXME(eddyb) should this still be taken as a separate `bool`?
|
||||||
|
preprocess: relax,
|
||||||
|
..
|
||||||
|
} = params;
|
||||||
|
|
||||||
|
let wp = glyph.width() + pad * 2;
|
||||||
|
let hp = glyph.height() + pad * 2;
|
||||||
|
let np = wp * hp;
|
||||||
|
|
||||||
|
let ReusableBuffers(bufs_2d, reuse_bufs_1d) = reuse_bufs.unwrap_or_default();
|
||||||
|
let mut state = Self {
|
||||||
|
bufs_2d,
|
||||||
|
reuse_bufs_1d,
|
||||||
|
};
|
||||||
|
let ReusableBuffers2d {
|
||||||
|
outer,
|
||||||
|
inner,
|
||||||
|
xo,
|
||||||
|
yo,
|
||||||
|
xi,
|
||||||
|
yi,
|
||||||
|
} = &mut state.bufs_2d;
|
||||||
|
|
||||||
|
outer.resize_and_fill_with(wp, hp, true);
|
||||||
|
inner.resize_and_fill_with(wp, hp, false);
|
||||||
|
for buf2d in [&mut *xo, yo, xi, yi] {
|
||||||
|
buf2d.clear();
|
||||||
|
buf2d.resize(np, 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
for y in 0..glyph.height() {
|
||||||
|
for x in 0..glyph.width() {
|
||||||
|
let a = &mut glyph[(x, y)];
|
||||||
|
if *a == Unorm8::MIN {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME(eddyb) audit all comparisons with `254` and try removing them,
|
||||||
|
// especially this step that modifies the `glyph` itself.
|
||||||
|
if *a >= Unorm8::from_bits(254) {
|
||||||
|
// Fix for bad rasterizer rounding
|
||||||
|
*a = Unorm8::MAX;
|
||||||
|
|
||||||
|
outer.at(x + pad, y + pad).set(false);
|
||||||
|
inner.at(x + pad, y + pad).set(true);
|
||||||
|
} else {
|
||||||
|
outer.at(x + pad, y + pad).set(false);
|
||||||
|
inner.at(x + pad, y + pad).set(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Generate subpixel offsets for all border pixels
|
||||||
|
//
|
||||||
|
|
||||||
|
let get_data = |x: isize, y: isize| {
|
||||||
|
if x >= 0 && (x as usize) < glyph.width() && y >= 0 && (y as usize) < glyph.height() {
|
||||||
|
glyph[(x as usize, y as usize)].decode()
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Make vector from pixel center to nearest boundary
|
||||||
|
for y in 0..(glyph.height() as isize) {
|
||||||
|
for x in 0..(glyph.width() as isize) {
|
||||||
|
let c = get_data(x, y);
|
||||||
|
// NOTE(eddyb) `j - 1` (X-) / `j - wp` (Y-) positive (`pad >= 1`).
|
||||||
|
let j = ((y as usize) + pad) * wp + (x as usize) + pad;
|
||||||
|
|
||||||
|
if !is_solid(c) {
|
||||||
|
let dc = c - 0.5;
|
||||||
|
|
||||||
|
// NOTE(eddyb) l(eft) r(ight) t(op) b(ottom)
|
||||||
|
let l = get_data(x - 1, y);
|
||||||
|
let r = get_data(x + 1, y);
|
||||||
|
let t = get_data(x, y - 1);
|
||||||
|
let b = get_data(x, y + 1);
|
||||||
|
|
||||||
|
let tl = get_data(x - 1, y - 1);
|
||||||
|
let tr = get_data(x + 1, y - 1);
|
||||||
|
let bl = get_data(x - 1, y + 1);
|
||||||
|
let br = get_data(x + 1, y + 1);
|
||||||
|
|
||||||
|
let ll = (tl + l * 2.0 + bl) / 4.0;
|
||||||
|
let rr = (tr + r * 2.0 + br) / 4.0;
|
||||||
|
let tt = (tl + t * 2.0 + tr) / 4.0;
|
||||||
|
let bb = (bl + b * 2.0 + br) / 4.0;
|
||||||
|
|
||||||
|
let (min, max) = [l, r, t, b, tl, tr, bl, br]
|
||||||
|
.into_iter()
|
||||||
|
.map(|x| (x, x))
|
||||||
|
.reduce(|(a_min, a_max), (b_min, b_max)| {
|
||||||
|
(a_min.min(b_min), a_max.max(b_max))
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
if min > 0.0 {
|
||||||
|
// Interior creases
|
||||||
|
inner.at(x as usize + pad, y as usize + pad).set(true);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if max < 1.0 {
|
||||||
|
// Exterior creases
|
||||||
|
outer.at(x as usize + pad, y as usize + pad).set(true);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut dx = rr - ll;
|
||||||
|
let mut dy = bb - tt;
|
||||||
|
let dl = 1.0 / (dx.powi(2) + dy.powi(2)).sqrt();
|
||||||
|
dx *= dl;
|
||||||
|
dy *= dl;
|
||||||
|
|
||||||
|
xo[j] = -dc * dx;
|
||||||
|
yo[j] = -dc * dy;
|
||||||
|
} else if is_white(c) {
|
||||||
|
// NOTE(eddyb) l(eft) r(ight) t(op) b(ottom)
|
||||||
|
let l = get_data(x - 1, y);
|
||||||
|
let r = get_data(x + 1, y);
|
||||||
|
let t = get_data(x, y - 1);
|
||||||
|
let b = get_data(x, y + 1);
|
||||||
|
|
||||||
|
if is_black(l) {
|
||||||
|
xo[j - 1] = 0.4999;
|
||||||
|
outer.at(x as usize + pad - 1, y as usize + pad).set(false);
|
||||||
|
inner.at(x as usize + pad - 1, y as usize + pad).set(false);
|
||||||
|
}
|
||||||
|
if is_black(r) {
|
||||||
|
xo[j + 1] = -0.4999;
|
||||||
|
outer.at(x as usize + pad + 1, y as usize + pad).set(false);
|
||||||
|
inner.at(x as usize + pad + 1, y as usize + pad).set(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_black(t) {
|
||||||
|
yo[j - wp] = 0.4999;
|
||||||
|
outer.at(x as usize + pad, y as usize + pad - 1).set(false);
|
||||||
|
inner.at(x as usize + pad, y as usize + pad - 1).set(false);
|
||||||
|
}
|
||||||
|
if is_black(b) {
|
||||||
|
yo[j + wp] = -0.4999;
|
||||||
|
outer.at(x as usize + pad, y as usize + pad + 1).set(false);
|
||||||
|
inner.at(x as usize + pad, y as usize + pad + 1).set(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Blend neighboring offsets but preserve normal direction
|
||||||
|
// Uses xo as input, xi as output
|
||||||
|
// Improves quality slightly, but slows things down.
|
||||||
|
if relax {
|
||||||
|
let check_cross = |nx, ny, dc, dl, dr, dxl, dyl, dxr, dyr| {
|
||||||
|
((dxl * nx + dyl * ny) * (dc * dl) > 0.0)
|
||||||
|
&& ((dxr * nx + dyr * ny) * (dc * dr) > 0.0)
|
||||||
|
&& ((dxl * dxr + dyl * dyr) * (dl * dr) > 0.0)
|
||||||
|
};
|
||||||
|
|
||||||
|
for y in 0..(glyph.height() as isize) {
|
||||||
|
for x in 0..(glyph.width() as isize) {
|
||||||
|
// NOTE(eddyb) `j - 1` (X-) / `j - wp` (Y-) positive (`pad >= 1`).
|
||||||
|
let j = ((y as usize) + pad) * wp + (x as usize) + pad;
|
||||||
|
|
||||||
|
let nx = xo[j];
|
||||||
|
let ny = yo[j];
|
||||||
|
if nx == 0.0 && ny == 0.0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE(eddyb) c(enter) l(eft) r(ight) t(op) b(ottom)
|
||||||
|
let c = get_data(x, y);
|
||||||
|
let l = get_data(x - 1, y);
|
||||||
|
let r = get_data(x + 1, y);
|
||||||
|
let t = get_data(x, y - 1);
|
||||||
|
let b = get_data(x, y + 1);
|
||||||
|
|
||||||
|
let dxl = xo[j - 1];
|
||||||
|
let dxr = xo[j + 1];
|
||||||
|
let dxt = xo[j - wp];
|
||||||
|
let dxb = xo[j + wp];
|
||||||
|
|
||||||
|
let dyl = yo[j - 1];
|
||||||
|
let dyr = yo[j + 1];
|
||||||
|
let dyt = yo[j - wp];
|
||||||
|
let dyb = yo[j + wp];
|
||||||
|
|
||||||
|
let mut dx = nx;
|
||||||
|
let mut dy = ny;
|
||||||
|
let mut dw = 1.0;
|
||||||
|
|
||||||
|
let dc = c - 0.5;
|
||||||
|
let dl = l - 0.5;
|
||||||
|
let dr = r - 0.5;
|
||||||
|
let dt = t - 0.5;
|
||||||
|
let db = b - 0.5;
|
||||||
|
|
||||||
|
if !is_solid(l) && !is_solid(r) {
|
||||||
|
if check_cross(nx, ny, dc, dl, dr, dxl, dyl, dxr, dyr) {
|
||||||
|
dx += (dxl + dxr) / 2.0;
|
||||||
|
dy += (dyl + dyr) / 2.0;
|
||||||
|
dw += 1.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !is_solid(t) && !is_solid(b) {
|
||||||
|
if check_cross(nx, ny, dc, dt, db, dxt, dyt, dxb, dyb) {
|
||||||
|
dx += (dxt + dxb) / 2.0;
|
||||||
|
dy += (dyt + dyb) / 2.0;
|
||||||
|
dw += 1.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !is_solid(l) && !is_solid(t) {
|
||||||
|
if check_cross(nx, ny, dc, dl, dt, dxl, dyl, dxt, dyt) {
|
||||||
|
dx += (dxl + dxt - 1.0) / 2.0;
|
||||||
|
dy += (dyl + dyt - 1.0) / 2.0;
|
||||||
|
dw += 1.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !is_solid(r) && !is_solid(t) {
|
||||||
|
if check_cross(nx, ny, dc, dr, dt, dxr, dyr, dxt, dyt) {
|
||||||
|
dx += (dxr + dxt + 1.0) / 2.0;
|
||||||
|
dy += (dyr + dyt - 1.0) / 2.0;
|
||||||
|
dw += 1.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !is_solid(l) && !is_solid(b) {
|
||||||
|
if check_cross(nx, ny, dc, dl, db, dxl, dyl, dxb, dyb) {
|
||||||
|
dx += (dxl + dxb - 1.0) / 2.0;
|
||||||
|
dy += (dyl + dyb + 1.0) / 2.0;
|
||||||
|
dw += 1.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !is_solid(r) && !is_solid(b) {
|
||||||
|
if check_cross(nx, ny, dc, dr, db, dxr, dyr, dxb, dyb) {
|
||||||
|
dx += (dxr + dxb + 1.0) / 2.0;
|
||||||
|
dy += (dyr + dyb + 1.0) / 2.0;
|
||||||
|
dw += 1.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let nn = (nx * nx + ny * ny).sqrt();
|
||||||
|
let ll = (dx * nx + dy * ny) / nn;
|
||||||
|
|
||||||
|
dx = nx * ll / dw / nn;
|
||||||
|
dy = ny * ll / dw / nn;
|
||||||
|
|
||||||
|
xi[j] = dx;
|
||||||
|
yi[j] = dy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Produce zero points for positive and negative DF, at +0.5 / -0.5.
|
||||||
|
// Splits xs into xo/xi
|
||||||
|
for y in 0..(glyph.height() as isize) {
|
||||||
|
for x in 0..(glyph.width() as isize) {
|
||||||
|
// NOTE(eddyb) `j - 1` (X-) / `j - wp` (Y-) positive (`pad >= 1`).
|
||||||
|
let j = ((y as usize) + pad) * wp + (x as usize) + pad;
|
||||||
|
|
||||||
|
// NOTE(eddyb) `if relax` above changed `xs`/`ys` in the original.
|
||||||
|
let (nx, ny) = if relax {
|
||||||
|
(xi[j], yi[j])
|
||||||
|
} else {
|
||||||
|
(xo[j], yo[j])
|
||||||
|
};
|
||||||
|
if nx == 0.0 && ny == 0.0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let nn = (nx.powi(2) + ny.powi(2)).sqrt();
|
||||||
|
|
||||||
|
let sx = if ((nx / nn).abs() - 0.5) > 0.0 {
|
||||||
|
nx.signum() as isize
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
let sy = if ((ny / nn).abs() - 0.5) > 0.0 {
|
||||||
|
ny.signum() as isize
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
let c = get_data(x, y);
|
||||||
|
let d = get_data(x + sx, y + sy);
|
||||||
|
// FIXME(eddyb) is this inefficient? (was `Math.sign(d - c)`)
|
||||||
|
let s = (d - c).total_cmp(&0.0) as i8 as f32;
|
||||||
|
|
||||||
|
let dlo = (nn + 0.4999 * s) / nn;
|
||||||
|
let dli = (nn - 0.4999 * s) / nn;
|
||||||
|
|
||||||
|
xo[j] = nx * dlo;
|
||||||
|
yo[j] = ny * dlo;
|
||||||
|
xi[j] = nx * dli;
|
||||||
|
yi[j] = ny * dli;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
state
|
||||||
|
}
|
||||||
|
|
||||||
|
fn esdt_outer_and_inner(&mut self, w: usize, h: usize) {
|
||||||
|
{
|
||||||
|
let Self {
|
||||||
|
bufs_2d:
|
||||||
|
ReusableBuffers2d {
|
||||||
|
outer,
|
||||||
|
inner,
|
||||||
|
xo,
|
||||||
|
yo,
|
||||||
|
xi,
|
||||||
|
yi,
|
||||||
|
},
|
||||||
|
reuse_bufs_1d,
|
||||||
|
} = self;
|
||||||
|
esdt(outer, xo, yo, w, h, reuse_bufs_1d);
|
||||||
|
esdt(inner, xi, yi, w, h, reuse_bufs_1d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2D subpixel distance transform by unconed
|
||||||
|
// extended from Felzenszwalb & Huttenlocher https://cs.brown.edu/~pff/papers/dt-final.pdf
|
||||||
|
fn esdt(
|
||||||
|
mask: &mut Bitmap,
|
||||||
|
xs: &mut [f32],
|
||||||
|
ys: &mut [f32],
|
||||||
|
w: usize,
|
||||||
|
h: usize,
|
||||||
|
reuse_bufs_1d: &mut ReusableBuffers1d,
|
||||||
|
) {
|
||||||
|
reuse_bufs_1d.critical_minima.clear();
|
||||||
|
reuse_bufs_1d.critical_minima.reserve(w.max(h));
|
||||||
|
|
||||||
|
let mut xs = Image2d::from_storage(w, h, xs);
|
||||||
|
let mut ys = Image2d::from_storage(w, h, ys);
|
||||||
|
|
||||||
|
for x in 0..w {
|
||||||
|
let mut mask_xy_cursor = mask
|
||||||
|
.cursor_at(0, 0)
|
||||||
|
.zip(
|
||||||
|
// FIXME(eddyb) combine `xs` and `ys` into the same `Image2d`.
|
||||||
|
ys.cursor_at(0, 0).zip(xs.cursor_at(0, 0)),
|
||||||
|
)
|
||||||
|
.map_abs_and_rel(move |y| (x, y), |dy| (0, dy));
|
||||||
|
mask_xy_cursor.reset(0);
|
||||||
|
|
||||||
|
esdt1d(mask_xy_cursor, h, reuse_bufs_1d)
|
||||||
|
}
|
||||||
|
for y in 0..h {
|
||||||
|
let mut mask_xy_cursor = mask
|
||||||
|
.cursor_at(0, 0)
|
||||||
|
.zip(
|
||||||
|
// FIXME(eddyb) combine `xs` and `ys` into the same `Image2d`.
|
||||||
|
xs.cursor_at(0, 0).zip(ys.cursor_at(0, 0)),
|
||||||
|
)
|
||||||
|
.map_abs_and_rel(move |x| (x, y), |dx| (dx, 0));
|
||||||
|
mask_xy_cursor.reset(0);
|
||||||
|
|
||||||
|
esdt1d(mask_xy_cursor, w, reuse_bufs_1d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 1D buffers (for `esdt1d`), which get reused between calls.
|
||||||
|
//
|
||||||
|
// FIXME(eddyb) the name is outdated now that there's only one buffer.
|
||||||
|
#[derive(Default)]
|
||||||
|
struct ReusableBuffers1d {
|
||||||
|
critical_minima: Vec<CriticalMinimum>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME(eddyb) clean up the names after all the refactors.
|
||||||
|
struct CriticalMinimum {
|
||||||
|
// FIXME(eddyb) this is really just a position, since it's not used to
|
||||||
|
// index anything indirectly anymore, but rather indicates the original `q`,
|
||||||
|
// and is used to compare against it in the second iteration of `esdt1d`.
|
||||||
|
v: usize, // Array index
|
||||||
|
|
||||||
|
z: f32, // Voronoi threshold
|
||||||
|
f: f32, // Squared distance
|
||||||
|
b: f32, // Subpixel offset parallel
|
||||||
|
t: f32, // Subpixel offset perpendicular
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1D subpixel distance transform
|
||||||
|
fn esdt1d(
|
||||||
|
mut mask_xy_cursor: impl for<'a> NDCursor<
|
||||||
|
'a,
|
||||||
|
usize,
|
||||||
|
RefMut = (super::img::BitmapEntry<'a>, (&'a mut f32, &'a mut f32)),
|
||||||
|
>,
|
||||||
|
// FIXME(eddyb) provide this through the cursor, maybe?
|
||||||
|
length: usize,
|
||||||
|
reuse_bufs_1d: &mut ReusableBuffers1d,
|
||||||
|
) {
|
||||||
|
// FIXME(eddyb) this is a pretty misleading name.
|
||||||
|
const INF: f32 = 1e10;
|
||||||
|
|
||||||
|
let cm = &mut reuse_bufs_1d.critical_minima;
|
||||||
|
cm.clear();
|
||||||
|
|
||||||
|
{
|
||||||
|
let (mask, (&mut dx, &mut dy)) = mask_xy_cursor.get_mut();
|
||||||
|
cm.push(CriticalMinimum {
|
||||||
|
v: 0,
|
||||||
|
z: -INF,
|
||||||
|
f: if mask.get() { INF } else { dy.powi(2) },
|
||||||
|
|
||||||
|
b: dx,
|
||||||
|
t: dy,
|
||||||
|
});
|
||||||
|
mask_xy_cursor.advance(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan along array and build list of critical minima
|
||||||
|
for q in 1..length {
|
||||||
|
// Perpendicular
|
||||||
|
let (mask, (&mut dx, &mut dy)) = mask_xy_cursor.get_mut();
|
||||||
|
let fq = if mask.get() { INF } else { dy.powi(2) };
|
||||||
|
mask_xy_cursor.advance(1);
|
||||||
|
|
||||||
|
// Parallel
|
||||||
|
let qs = q as f32 + dx;
|
||||||
|
let q2 = qs.powi(2);
|
||||||
|
|
||||||
|
// Remove any minima eclipsed by this one
|
||||||
|
let mut s;
|
||||||
|
loop {
|
||||||
|
let r = &cm[cm.len() - 1];
|
||||||
|
|
||||||
|
s = (fq - r.f + q2 - r.b.powi(2)) / (qs - r.b) / 2.0;
|
||||||
|
|
||||||
|
if !(s <= r.z) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
cm.pop();
|
||||||
|
if cm.len() == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add to minima list
|
||||||
|
cm.push(CriticalMinimum {
|
||||||
|
v: q,
|
||||||
|
z: s,
|
||||||
|
f: fq,
|
||||||
|
b: qs,
|
||||||
|
t: dy,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
mask_xy_cursor.reset(0);
|
||||||
|
|
||||||
|
// Resample array based on critical minima
|
||||||
|
{
|
||||||
|
let mut k = 0;
|
||||||
|
for q in 0..length {
|
||||||
|
// Skip eclipsed minima
|
||||||
|
while k + 1 < cm.len() && cm[k + 1].z < q as f32 {
|
||||||
|
k += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let r = &cm[k];
|
||||||
|
|
||||||
|
// Distance from integer index to subpixel location of minimum
|
||||||
|
let rq = r.b - q as f32;
|
||||||
|
|
||||||
|
let (mut mask, (dx, dy)) = mask_xy_cursor.get_mut();
|
||||||
|
*dx = rq;
|
||||||
|
*dy = r.t;
|
||||||
|
// Mark cell as having propagated
|
||||||
|
if r.v != q {
|
||||||
|
mask.set(false);
|
||||||
|
}
|
||||||
|
mask_xy_cursor.advance(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
411
src/graphics/font/esdt/img.rs
Normal file
411
src/graphics/font/esdt/img.rs
Normal file
@ -0,0 +1,411 @@
|
|||||||
|
// NOTE(eddyb) this is a separate module so that privacy affects sibling modules.
|
||||||
|
// FIXME(eddyb) deduplicate with `image` crate?
|
||||||
|
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
use std::ops::{Index, IndexMut};
|
||||||
|
|
||||||
|
// HACK(eddyb) only exists to allow toggling precision for testing purposes.
|
||||||
|
#[cfg(sdfer_use_f64_instead_of_f32)]
|
||||||
|
type f32 = f64;
|
||||||
|
|
||||||
|
/// `[0, 1]` represented by uniformly spaced `u8` values (`0..=255`),
|
||||||
|
/// i.e. `Unorm8(byte)` corresponds to the `f32` value `byte as f32 / 255.0`.
|
||||||
|
#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct Unorm8(u8);
|
||||||
|
|
||||||
|
impl Unorm8 {
|
||||||
|
pub const MIN: Self = Self::from_bits(0);
|
||||||
|
pub const MAX: Self = Self::from_bits(u8::MAX);
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn encode(x: f32) -> Self {
|
||||||
|
// NOTE(eddyb) manual `clamp` not needed, `(_: f32) as u8` will saturate:
|
||||||
|
// https://doc.rust-lang.org/reference/expressions/operator-expr.html#numeric-cast
|
||||||
|
Self((x * 255.0).round() as u8)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn decode(self) -> f32 {
|
||||||
|
self.0 as f32 / 255.0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn from_bits(bits: u8) -> Self {
|
||||||
|
Self(bits)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn to_bits(self) -> u8 {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Copy, Clone)]
|
||||||
|
pub struct Image2d<T, Storage: AsRef<[T]> = Vec<T>> {
|
||||||
|
width: usize,
|
||||||
|
height: usize,
|
||||||
|
data: Storage,
|
||||||
|
_marker: PhantomData<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, Storage: AsRef<[T]>> Image2d<T, Storage> {
|
||||||
|
pub fn new(width: usize, height: usize) -> Self
|
||||||
|
where
|
||||||
|
T: Default,
|
||||||
|
Storage: FromIterator<T>,
|
||||||
|
{
|
||||||
|
Self::from_fn(width, height, |_, _| T::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_fn(width: usize, height: usize, mut f: impl FnMut(usize, usize) -> T) -> Self
|
||||||
|
where
|
||||||
|
Storage: FromIterator<T>,
|
||||||
|
{
|
||||||
|
Self::from_storage(
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
(0..height)
|
||||||
|
.flat_map(|y| (0..width).map(move |x| (x, y)))
|
||||||
|
.map(|(x, y)| f(x, y))
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_storage(width: usize, height: usize, storage: Storage) -> Self {
|
||||||
|
assert_eq!(storage.as_ref().len(), width * height);
|
||||||
|
Self {
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
data: storage,
|
||||||
|
_marker: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn width(&self) -> usize {
|
||||||
|
self.width
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn height(&self) -> usize {
|
||||||
|
self.height
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reborrow(&self) -> Image2d<T, &[T]> {
|
||||||
|
Image2d {
|
||||||
|
width: self.width,
|
||||||
|
height: self.height,
|
||||||
|
data: self.data.as_ref(),
|
||||||
|
_marker: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reborrow_mut(&mut self) -> Image2d<T, &mut [T]>
|
||||||
|
where
|
||||||
|
Storage: AsMut<[T]>,
|
||||||
|
{
|
||||||
|
Image2d {
|
||||||
|
width: self.width,
|
||||||
|
height: self.height,
|
||||||
|
data: self.data.as_mut(),
|
||||||
|
_marker: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cursor_at(&mut self, x: usize, y: usize) -> Image2dCursor<'_, T>
|
||||||
|
where
|
||||||
|
Storage: AsMut<[T]>,
|
||||||
|
{
|
||||||
|
let mut cursor = Image2dCursor {
|
||||||
|
image: self.reborrow_mut(),
|
||||||
|
xy_offset: 0,
|
||||||
|
};
|
||||||
|
cursor.reset((x, y));
|
||||||
|
cursor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, Storage: AsRef<[T]>> Index<(usize, usize)> for Image2d<T, Storage> {
|
||||||
|
type Output = T;
|
||||||
|
|
||||||
|
fn index(&self, (x, y): (usize, usize)) -> &T {
|
||||||
|
&self.data.as_ref()[y * self.width..][..self.width][x]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, Storage: AsMut<[T]> + AsRef<[T]>> IndexMut<(usize, usize)> for Image2d<T, Storage> {
|
||||||
|
fn index_mut(&mut self, (x, y): (usize, usize)) -> &mut T {
|
||||||
|
&mut self.data.as_mut()[y * self.width..][..self.width][x]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<image::GrayImage> for Image2d<Unorm8> {
|
||||||
|
fn from(img: image::GrayImage) -> Self {
|
||||||
|
Self {
|
||||||
|
width: img.width().try_into().unwrap(),
|
||||||
|
height: img.height().try_into().unwrap(),
|
||||||
|
// HACK(eddyb) this should be a noop if the right specializations
|
||||||
|
// all kick in, and LLVM optimizes out the in-place transformation.
|
||||||
|
data: img.into_vec().into_iter().map(Unorm8::from_bits).collect(),
|
||||||
|
_marker: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Image2d<Unorm8>> for image::GrayImage {
|
||||||
|
fn from(img: Image2d<Unorm8>) -> Self {
|
||||||
|
image::GrayImage::from_vec(
|
||||||
|
img.width().try_into().unwrap(),
|
||||||
|
img.height().try_into().unwrap(),
|
||||||
|
// HACK(eddyb) this should be a noop if the right specializations
|
||||||
|
// all kick in, and LLVM optimizes out the in-place transformation.
|
||||||
|
img.data.into_iter().map(Unorm8::to_bits).collect(),
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Image2d<Unorm8>> for ndarray::Array2<u8> {
|
||||||
|
fn from(value: Image2d<Unorm8>) -> Self {
|
||||||
|
ndarray::Array2::from_shape_vec(
|
||||||
|
[value.height(), value.width()],
|
||||||
|
value.data.into_iter().map(Unorm8::to_bits).collect(),
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Copy> Image2d<T> {
|
||||||
|
fn resize_and_fill_with(&mut self, width: usize, height: usize, initial: T) {
|
||||||
|
self.width = width;
|
||||||
|
self.height = height;
|
||||||
|
self.data.clear();
|
||||||
|
self.data.resize(width * height, initial);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Bitmap {
|
||||||
|
width: usize,
|
||||||
|
height: usize,
|
||||||
|
bit_8x8_blocks: Image2d<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct BitmapEntry<'a> {
|
||||||
|
bit_8x8_block: &'a mut u64,
|
||||||
|
mask: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Bitmap {
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn new(width: usize, height: usize) -> Self {
|
||||||
|
let mut r = Self::default();
|
||||||
|
r.resize_and_fill_with(width, height, false);
|
||||||
|
r
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub(crate) fn resize_and_fill_with(&mut self, width: usize, height: usize, initial: bool) {
|
||||||
|
self.width = width;
|
||||||
|
self.height = height;
|
||||||
|
self.bit_8x8_blocks.resize_and_fill_with(
|
||||||
|
width.div_ceil(8),
|
||||||
|
height.div_ceil(8),
|
||||||
|
if initial { !0 } else { 0 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn width(&self) -> usize {
|
||||||
|
self.width
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn height(&self) -> usize {
|
||||||
|
self.height
|
||||||
|
}
|
||||||
|
|
||||||
|
const BW: usize = 8;
|
||||||
|
const BH: usize = 8;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
const fn bit_8x8_block_xy_and_mask(x: usize, y: usize) -> ((usize, usize), u64) {
|
||||||
|
(
|
||||||
|
(x / Self::BW, y / Self::BH),
|
||||||
|
1 << ((y % Self::BH) * Self::BW + x % Self::BW),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn get(&self, x: usize, y: usize) -> bool {
|
||||||
|
let (block_xy, mask) = Self::bit_8x8_block_xy_and_mask(x, y);
|
||||||
|
(self.bit_8x8_blocks[block_xy] & mask) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn at(&mut self, x: usize, y: usize) -> BitmapEntry<'_> {
|
||||||
|
let (block_xy, mask) = Self::bit_8x8_block_xy_and_mask(x, y);
|
||||||
|
BitmapEntry {
|
||||||
|
bit_8x8_block: &mut self.bit_8x8_blocks[block_xy],
|
||||||
|
mask,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn cursor_at(&mut self, x: usize, y: usize) -> BitmapCursor<'_> {
|
||||||
|
let mut cursor = BitmapCursor {
|
||||||
|
bit_8x8_blocks: self.bit_8x8_blocks.cursor_at(0, 0),
|
||||||
|
intra_block_xy: (0, 0),
|
||||||
|
};
|
||||||
|
cursor.reset((x, y));
|
||||||
|
cursor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BitmapEntry<'_> {
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn get(&self) -> bool {
|
||||||
|
(*self.bit_8x8_block & self.mask) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn set(&mut self, value: bool) {
|
||||||
|
if value {
|
||||||
|
*self.bit_8x8_block |= self.mask;
|
||||||
|
} else {
|
||||||
|
*self.bit_8x8_block &= !self.mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME(eddyb) this doesn't really belong here, and should use GATs.
|
||||||
|
pub trait NDCursor<'a, P> {
|
||||||
|
type RefMut;
|
||||||
|
fn reset(&'a mut self, position: P);
|
||||||
|
fn get_mut(&'a mut self) -> Self::RefMut;
|
||||||
|
fn advance(&'a mut self, delta: P);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait NDCursorExt<P>: for<'a> NDCursor<'a, P> {
|
||||||
|
fn zip<C2: NDCursorExt<P>>(self, other: C2) -> NDCursorZip<Self, C2>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
NDCursorZip(self, other)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME(eddyb) this is a really bad API but a whole coordinate system would be overkill.
|
||||||
|
fn map_abs_and_rel<P2, FA: Fn(P2) -> P, FR: Fn(P2) -> P>(
|
||||||
|
self,
|
||||||
|
fa: FA,
|
||||||
|
fr: FR,
|
||||||
|
) -> NDCursorMapPos<Self, FA, FR>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
NDCursorMapPos(self, fa, fr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<P, C: for<'a> NDCursor<'a, P>> NDCursorExt<P> for C {}
|
||||||
|
|
||||||
|
pub struct NDCursorZip<C1, C2>(C1, C2);
|
||||||
|
impl<'a, P: Copy, C1: NDCursor<'a, P>, C2: NDCursor<'a, P>> NDCursor<'a, P>
|
||||||
|
for NDCursorZip<C1, C2>
|
||||||
|
{
|
||||||
|
type RefMut = (C1::RefMut, C2::RefMut);
|
||||||
|
#[inline(always)]
|
||||||
|
fn reset(&'a mut self, position: P) {
|
||||||
|
self.0.reset(position);
|
||||||
|
self.1.reset(position);
|
||||||
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
fn get_mut(&'a mut self) -> Self::RefMut {
|
||||||
|
(self.0.get_mut(), self.1.get_mut())
|
||||||
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
fn advance(&'a mut self, delta: P) {
|
||||||
|
self.0.advance(delta);
|
||||||
|
self.1.advance(delta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct NDCursorMapPos<C, FA, FR>(C, FA, FR);
|
||||||
|
impl<'a, C: NDCursor<'a, P>, P, P2, FA: Fn(P2) -> P, FR: Fn(P2) -> P> NDCursor<'a, P2>
|
||||||
|
for NDCursorMapPos<C, FA, FR>
|
||||||
|
{
|
||||||
|
type RefMut = C::RefMut;
|
||||||
|
#[inline(always)]
|
||||||
|
fn reset(&'a mut self, position: P2) {
|
||||||
|
self.0.reset((self.1)(position));
|
||||||
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
fn get_mut(&'a mut self) -> Self::RefMut {
|
||||||
|
self.0.get_mut()
|
||||||
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
fn advance(&'a mut self, delta: P2) {
|
||||||
|
self.0.advance((self.2)(delta));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Image2dCursor<'a, T> {
|
||||||
|
// FIXME(eddyb) find a way to use something closer to `slice::IterMut` here.
|
||||||
|
image: Image2d<T, &'a mut [T]>,
|
||||||
|
xy_offset: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: 'a> NDCursor<'a, (usize, usize)> for Image2dCursor<'_, T> {
|
||||||
|
type RefMut = &'a mut T;
|
||||||
|
#[inline(always)]
|
||||||
|
fn reset(&'a mut self, (x, y): (usize, usize)) {
|
||||||
|
self.xy_offset = y * self.image.width + x;
|
||||||
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
fn get_mut(&'a mut self) -> Self::RefMut {
|
||||||
|
&mut self.image.data[self.xy_offset]
|
||||||
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
fn advance(&'a mut self, (dx, dy): (usize, usize)) {
|
||||||
|
// FIXME(eddyb) check for edge conditions? (should be more like an iterator)
|
||||||
|
self.xy_offset += dy * self.image.width + dx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct BitmapCursor<'a> {
|
||||||
|
bit_8x8_blocks: Image2dCursor<'a, u64>,
|
||||||
|
// FIXME(eddyb) because of this we can't just use `bit_8x8_block_xy_and_mask`.
|
||||||
|
intra_block_xy: (u8, u8),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> NDCursor<'a, (usize, usize)> for BitmapCursor<'_> {
|
||||||
|
type RefMut = BitmapEntry<'a>;
|
||||||
|
#[inline(always)]
|
||||||
|
fn reset(&'a mut self, (x, y): (usize, usize)) {
|
||||||
|
self.bit_8x8_blocks.reset((x / Bitmap::BW, y / Bitmap::BH));
|
||||||
|
self.intra_block_xy = ((x % Bitmap::BW) as u8, (y % Bitmap::BH) as u8);
|
||||||
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
fn get_mut(&'a mut self) -> Self::RefMut {
|
||||||
|
let bxy = self.intra_block_xy;
|
||||||
|
let (_, mask) = Bitmap::bit_8x8_block_xy_and_mask(bxy.0 as usize, bxy.1 as usize);
|
||||||
|
BitmapEntry {
|
||||||
|
bit_8x8_block: self.bit_8x8_blocks.get_mut(),
|
||||||
|
mask,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
fn advance(&'a mut self, (dx, dy): (usize, usize)) {
|
||||||
|
// FIXME(eddyb) check for edge conditions? (should be more like an iterator)
|
||||||
|
let bxy = self.intra_block_xy;
|
||||||
|
let new_bxy = (bxy.0 as usize + dx, bxy.1 as usize + dy);
|
||||||
|
|
||||||
|
let whole_block_dxy = (new_bxy.0 / Bitmap::BW, new_bxy.1 / Bitmap::BH);
|
||||||
|
if whole_block_dxy != (0, 0) {
|
||||||
|
self.bit_8x8_blocks.advance(whole_block_dxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.intra_block_xy = (
|
||||||
|
(new_bxy.0 % Bitmap::BW) as u8,
|
||||||
|
(new_bxy.1 % Bitmap::BH) as u8,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
5
src/graphics/font/esdt/mod.rs
Normal file
5
src/graphics/font/esdt/mod.rs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
mod esdt;
|
||||||
|
mod img;
|
||||||
|
|
||||||
|
pub use esdt::*;
|
||||||
|
pub use img::{Image2d, Unorm8};
|
||||||
316
src/graphics/font/mod.rs
Normal file
316
src/graphics/font/mod.rs
Normal file
@ -0,0 +1,316 @@
|
|||||||
|
mod esdt;
|
||||||
|
use esdt::{Image2d, Unorm8};
|
||||||
|
use glow::{HasContext, TEXTURE_2D};
|
||||||
|
use std::{cell::RefCell, collections::HashMap};
|
||||||
|
use text_items::{LineStyle, Text as TextTrait};
|
||||||
|
mod text_items;
|
||||||
|
pub use text_items::{Anchor, PositionText, TextLine};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
components::{CodeType, Program, Shader},
|
||||||
|
errors::*,
|
||||||
|
font_manager::{FontManager, FontStyle},
|
||||||
|
utils::resources::RcGlTexture,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{threed::ThreeD, transforms::viewport::Viewport, AttaWithBuffer, Config, Graphics};
|
||||||
|
pub struct Text<'a> {
|
||||||
|
gl: &'a glow::Context,
|
||||||
|
font_manager: RefCell<FontManager>,
|
||||||
|
cache: RefCell<HashMap<String, Cache<'a>>>,
|
||||||
|
items: Vec<PositionText>,
|
||||||
|
program: Program,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Cache<'a> {
|
||||||
|
gl: &'a glow::Context,
|
||||||
|
cache: HashMap<char, TextVType>,
|
||||||
|
// cache_tex: Vec<u8>,
|
||||||
|
width: usize,
|
||||||
|
height: usize,
|
||||||
|
last_pos: [usize; 2],
|
||||||
|
tex: RcGlTexture<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Cache<'a> {
|
||||||
|
pub fn new(gl: &'a glow::Context) -> Self {
|
||||||
|
let tex = unsafe {
|
||||||
|
let tex = gl.create_texture().unwrap();
|
||||||
|
gl.bind_texture(glow::TEXTURE_2D, Some(tex));
|
||||||
|
gl.tex_parameter_i32(
|
||||||
|
glow::TEXTURE_2D,
|
||||||
|
glow::TEXTURE_MIN_FILTER,
|
||||||
|
glow::LINEAR as i32,
|
||||||
|
);
|
||||||
|
gl.tex_parameter_i32(
|
||||||
|
glow::TEXTURE_2D,
|
||||||
|
glow::TEXTURE_MAG_FILTER,
|
||||||
|
glow::LINEAR as i32,
|
||||||
|
);
|
||||||
|
|
||||||
|
gl.tex_image_2d(
|
||||||
|
glow::TEXTURE_2D,
|
||||||
|
0,
|
||||||
|
glow::R8 as i32,
|
||||||
|
1024,
|
||||||
|
1024,
|
||||||
|
0,
|
||||||
|
glow::RED,
|
||||||
|
glow::UNSIGNED_BYTE,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
tex
|
||||||
|
};
|
||||||
|
|
||||||
|
Self {
|
||||||
|
gl,
|
||||||
|
cache: HashMap::new(),
|
||||||
|
// cache_tex: vec![0; 1024 * 1024],
|
||||||
|
width: 1024,
|
||||||
|
height: 1024,
|
||||||
|
last_pos: [0, 0],
|
||||||
|
tex: RcGlTexture::new(gl, tex),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(&self, c: char) -> Option<&TextVType> {
|
||||||
|
self.cache.get(&c)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn insert_glyph(&mut self, tex: Image2d<Unorm8>, c: char) -> &TextVType {
|
||||||
|
// use image::GrayImage;
|
||||||
|
|
||||||
|
use ndarray::{s, Array2};
|
||||||
|
let width = tex.width();
|
||||||
|
let height = tex.height();
|
||||||
|
|
||||||
|
let data: Array2<u8> = tex.into();
|
||||||
|
|
||||||
|
let x = self.last_pos[0];
|
||||||
|
let y = self.last_pos[1];
|
||||||
|
|
||||||
|
use glow::PixelUnpackData;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
self.gl
|
||||||
|
.bind_texture(glow::TEXTURE_2D, Some(self.tex.native()));
|
||||||
|
self.gl.pixel_store_i32(glow::UNPACK_ALIGNMENT, 1);
|
||||||
|
|
||||||
|
self.gl.tex_sub_image_2d(
|
||||||
|
glow::TEXTURE_2D,
|
||||||
|
0,
|
||||||
|
x as i32,
|
||||||
|
y as i32,
|
||||||
|
width as i32,
|
||||||
|
height as i32,
|
||||||
|
glow::RED,
|
||||||
|
glow::UNSIGNED_BYTE,
|
||||||
|
PixelUnpackData::Slice(&data.as_slice().unwrap()),
|
||||||
|
);
|
||||||
|
println!("{} {} {} {}", x, y, width, height);
|
||||||
|
println!("size: {}", &data.len());
|
||||||
|
self.gl.bind_texture(glow::TEXTURE_2D, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.cache.insert(
|
||||||
|
c,
|
||||||
|
TextVType {
|
||||||
|
tex_coords: [
|
||||||
|
x as f32,
|
||||||
|
(y + height - 1) as f32,
|
||||||
|
(x + width - 1) as f32,
|
||||||
|
(y + height - 1) as f32,
|
||||||
|
(x + width - 1) as f32,
|
||||||
|
y as f32,
|
||||||
|
x as f32,
|
||||||
|
y as f32,
|
||||||
|
],
|
||||||
|
// tex_coords: [0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if x + width >= 1024 {
|
||||||
|
self.last_pos[0] = 0;
|
||||||
|
self.last_pos[1] += height;
|
||||||
|
} else {
|
||||||
|
self.last_pos[0] += width;
|
||||||
|
}
|
||||||
|
|
||||||
|
if y + height > self.height {
|
||||||
|
self.height += 1024;
|
||||||
|
// self.cache_tex.extend(vec![0; 1024 * 1024]);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.cache.get(&c).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct TextVType {
|
||||||
|
tex_coords: [f32; 8],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Text<'a> {
|
||||||
|
pub fn new(gl: &'a glow::Context, font_manager: FontManager) -> Result<Self> {
|
||||||
|
let vertex = Shader::new(
|
||||||
|
glow::VERTEX_SHADER,
|
||||||
|
crate::components::CodeType::<&str>::Path("font.vert"),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let fragment = Shader::new(glow::FRAGMENT_SHADER, CodeType::<&str>::Path("font.frag"))?;
|
||||||
|
|
||||||
|
let transform = ThreeD::new(1.0, 0.1, 100.0, 45.0)?;
|
||||||
|
let mut program = Program::new(vertex, fragment, None, "330 core");
|
||||||
|
program.set_transform(&transform);
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
gl,
|
||||||
|
font_manager: RefCell::new(font_manager),
|
||||||
|
cache: RefCell::new(HashMap::new()),
|
||||||
|
items: Vec::new(),
|
||||||
|
program,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_viewport(&mut self, viewport: &Viewport) {
|
||||||
|
self.program.set_viewport(viewport);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_uniforms(&self) {
|
||||||
|
let conf = self.program.get_uniform_location(&self.gl, "uSdfConfig");
|
||||||
|
let u_mode = self.program.get_uniform_location(&self.gl, "uMode");
|
||||||
|
let u_border = self.program.get_uniform_location(&self.gl, "uBorder");
|
||||||
|
let u_stroke = self.program.get_uniform_location(&self.gl, "uStroke");
|
||||||
|
let u_fill = self.program.get_uniform_location(&self.gl, "uFill");
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
self.gl.uniform_4_f32(conf.as_ref(), 5.0, 0.0, 0.0, 0.0);
|
||||||
|
self.gl.uniform_1_i32(u_mode.as_ref(), -1);
|
||||||
|
self.gl.uniform_4_f32(u_border.as_ref(), 0.0, 0.0, 0.0, 0.0);
|
||||||
|
self.gl.uniform_4_f32(u_stroke.as_ref(), 1.0, 1.0, 1.0, 1.0);
|
||||||
|
self.gl.uniform_4_f32(u_fill.as_ref(), 1.0, 1.0, 1.0, 1.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Graphics for Text<'a> {
|
||||||
|
const id: &'static str = "Text";
|
||||||
|
type Config = FontConfig;
|
||||||
|
fn compile(&mut self, gl: &glow::Context) -> Result<()> {
|
||||||
|
self.program.compile(gl)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn destroy(&mut self, gl: &glow::Context) -> Result<()> {
|
||||||
|
self.program.destroy(gl);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(&self, gl: &glow::Context, count: i32) -> Result<()> {
|
||||||
|
unsafe {
|
||||||
|
gl.clear(glow::COLOR_BUFFER_BIT);
|
||||||
|
// gl.polygon_mode(glow::FRONT_AND_BACK, glow::LINE);
|
||||||
|
|
||||||
|
let loc = self.program.get_uniform_location(gl, "atlas_data");
|
||||||
|
gl.uniform_1_i32(loc.as_ref(), 0);
|
||||||
|
|
||||||
|
gl.active_texture(glow::TEXTURE0);
|
||||||
|
gl.bind_texture(
|
||||||
|
glow::TEXTURE_2D,
|
||||||
|
self.cache
|
||||||
|
.borrow()
|
||||||
|
.get("resources/Roboto-Regular.ttf")
|
||||||
|
.map(|v| v.tex.native()),
|
||||||
|
);
|
||||||
|
|
||||||
|
let width_loc = self.program.get_uniform_location(gl, "atlas_shape");
|
||||||
|
gl.uniform_2_f32(width_loc.as_ref(), 1024.0, 1024.0);
|
||||||
|
|
||||||
|
self.set_uniforms();
|
||||||
|
|
||||||
|
gl.draw_elements(glow::TRIANGLES, count / 2 * 3, glow::UNSIGNED_INT, 0);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn program_mut(&mut self) -> &mut Program {
|
||||||
|
&mut self.program
|
||||||
|
}
|
||||||
|
|
||||||
|
fn program_ref(&self) -> &Program {
|
||||||
|
&self.program
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_config(&mut self, gl: &glow::Context, config: &Self::Config) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> AttaWithBuffer for Text<'a> {
|
||||||
|
type Data = PositionText;
|
||||||
|
|
||||||
|
fn bake(
|
||||||
|
&self,
|
||||||
|
data: &Self::Data,
|
||||||
|
config: &<Self as Graphics>::Config,
|
||||||
|
) -> Result<(Vec<f32>, Option<Vec<u32>>, i32)> {
|
||||||
|
let v = data.bake(
|
||||||
|
&self.gl,
|
||||||
|
&mut *self.font_manager.borrow_mut(),
|
||||||
|
&mut *self.cache.borrow_mut(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let mut ebos = Vec::with_capacity(v.len() / 2 * 3);
|
||||||
|
|
||||||
|
for i in 0..v.len() / 4 {
|
||||||
|
let i = i as u32;
|
||||||
|
ebos.push(i * 4);
|
||||||
|
ebos.push(i * 4 + 1);
|
||||||
|
ebos.push(i * 4 + 3);
|
||||||
|
|
||||||
|
ebos.push(i * 4 + 1);
|
||||||
|
ebos.push(i * 4 + 2);
|
||||||
|
ebos.push(i * 4 + 3);
|
||||||
|
}
|
||||||
|
Ok((v.vertex(), Some(ebos), v.len() as i32))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(
|
||||||
|
&self,
|
||||||
|
gl: &glow::Context,
|
||||||
|
) -> (
|
||||||
|
glow::NativeVertexArray,
|
||||||
|
glow::NativeBuffer,
|
||||||
|
Option<glow::NativeBuffer>,
|
||||||
|
) {
|
||||||
|
unsafe {
|
||||||
|
let vao = gl.create_vertex_array().unwrap();
|
||||||
|
gl.bind_vertex_array(Some(vao));
|
||||||
|
let vbo = gl.create_buffer().unwrap();
|
||||||
|
gl.bind_buffer(glow::ARRAY_BUFFER, Some(vbo));
|
||||||
|
gl.enable_vertex_attrib_array(0);
|
||||||
|
gl.vertex_attrib_pointer_f32(0, 3, glow::FLOAT, false, 20, 0);
|
||||||
|
|
||||||
|
gl.enable_vertex_attrib_array(1);
|
||||||
|
gl.vertex_attrib_pointer_f32(1, 2, glow::FLOAT, false, 20, 12);
|
||||||
|
|
||||||
|
let ebo = gl.create_buffer().unwrap();
|
||||||
|
gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(ebo));
|
||||||
|
|
||||||
|
gl.bind_vertex_array(None);
|
||||||
|
|
||||||
|
(vao, vbo, Some(ebo))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum FontConfig {
|
||||||
|
Textline(LineStyle, FontStyle),
|
||||||
|
}
|
||||||
|
|
||||||
|
mod test {
|
||||||
|
#[test]
|
||||||
|
fn test() {
|
||||||
|
use super::*;
|
||||||
|
let mut font_manager = FontManager::new().unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
256
src/graphics/font/text_items.rs
Normal file
256
src/graphics/font/text_items.rs
Normal file
@ -0,0 +1,256 @@
|
|||||||
|
use super::esdt::{Image2d, Params, Unorm8};
|
||||||
|
use super::TextVType;
|
||||||
|
use super::{esdt::glyph_to_sdf, Cache};
|
||||||
|
use crate::components::merge_includes;
|
||||||
|
use crate::font_manager::{CharImg, FontManager, FontSize};
|
||||||
|
use crate::{errors::*, font_manager::FontStyle};
|
||||||
|
use bytemuck::{Pod, Zeroable};
|
||||||
|
use geo::kernels;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
const SDF_PARAM: Params = Params {
|
||||||
|
radius: 5.0,
|
||||||
|
pad: 4,
|
||||||
|
cutoff: 0.25,
|
||||||
|
solidify: true,
|
||||||
|
preprocess: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Clone)]
|
||||||
|
pub struct LineStyle {
|
||||||
|
line_height: f32,
|
||||||
|
line_spacing: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct PositionText {
|
||||||
|
item: TextLine,
|
||||||
|
position: [f32; 3],
|
||||||
|
anchor: Anchor,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PositionText {
|
||||||
|
pub fn new(item: TextLine, position: [f32; 3], anchor: Anchor) -> Self {
|
||||||
|
Self {
|
||||||
|
item,
|
||||||
|
position,
|
||||||
|
anchor,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Text for PositionText {
|
||||||
|
fn bake<'a>(
|
||||||
|
&self,
|
||||||
|
gl: &'a glow::Context,
|
||||||
|
font_manager: &mut FontManager,
|
||||||
|
cache: &mut HashMap<String, Cache<'a>>,
|
||||||
|
) -> Result<TextVertexArray> {
|
||||||
|
self.item.bake(gl, font_manager, cache)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct TextLine {
|
||||||
|
text: String,
|
||||||
|
font_style: FontStyle,
|
||||||
|
line_style: LineStyle,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TextLine {
|
||||||
|
pub fn new<P: Into<String>>(
|
||||||
|
text: P,
|
||||||
|
font_style: Option<FontStyle>,
|
||||||
|
line_style: Option<LineStyle>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
text: text.into(),
|
||||||
|
font_style: font_style.unwrap_or_default(),
|
||||||
|
line_style: line_style.unwrap_or_default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum Anchor {
|
||||||
|
TopLeft,
|
||||||
|
TopCenter,
|
||||||
|
TopRight,
|
||||||
|
CenterLeft,
|
||||||
|
Center,
|
||||||
|
CenterRight,
|
||||||
|
BottomLeft,
|
||||||
|
BottomCenter,
|
||||||
|
BottomRight,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum TextAlign {
|
||||||
|
Left,
|
||||||
|
Center,
|
||||||
|
Right,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for TextAlign {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Left
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Zeroable, Pod)]
|
||||||
|
pub struct TextVertexItem {
|
||||||
|
position: [f32; 3],
|
||||||
|
tex_coords: [f32; 2],
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TextVertexArray {
|
||||||
|
points: Vec<TextVertexItem>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TextVertexArray {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
points: Vec::with_capacity(30),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.points.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push(&mut self, item: TextVertexItem) {
|
||||||
|
self.points.push(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_text(&mut self, tex_coords: TextVType, positions: [[f32; 3]; 4]) {
|
||||||
|
for i in 0..4 {
|
||||||
|
self.push(TextVertexItem {
|
||||||
|
position: positions[i],
|
||||||
|
tex_coords: [
|
||||||
|
tex_coords.tex_coords[i * 2],
|
||||||
|
tex_coords.tex_coords[i * 2 + 1],
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn vertex(&self) -> Vec<f32> {
|
||||||
|
let mut result = Vec::with_capacity(self.len() * 6);
|
||||||
|
self.points.iter().for_each(|v| {
|
||||||
|
result.extend_from_slice(&v.position);
|
||||||
|
result.extend_from_slice(&v.tex_coords);
|
||||||
|
});
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_bits(&self) -> &[u8] {
|
||||||
|
bytemuck::cast_slice(&self.points)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Text: Sized {
|
||||||
|
fn bake<'a>(
|
||||||
|
&self,
|
||||||
|
gl: &'a glow::Context,
|
||||||
|
font_manager: &mut FontManager,
|
||||||
|
cache: &mut HashMap<String, Cache<'a>>,
|
||||||
|
) -> Result<TextVertexArray>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Text for TextLine {
|
||||||
|
fn bake<'a>(
|
||||||
|
&self,
|
||||||
|
gl: &'a glow::Context,
|
||||||
|
font_manager: &mut FontManager,
|
||||||
|
cache: &mut HashMap<String, Cache<'a>>,
|
||||||
|
) -> Result<TextVertexArray> {
|
||||||
|
let font_style = &self.font_style;
|
||||||
|
|
||||||
|
let font = font_manager.get_font_or_insert(&font_style.postscript_name);
|
||||||
|
|
||||||
|
if let Some(font) = font {
|
||||||
|
cache
|
||||||
|
.entry(font_style.postscript_name.clone())
|
||||||
|
.or_insert_with(|| Cache::new(&gl));
|
||||||
|
|
||||||
|
let cache = cache.get_mut(&font_style.postscript_name).unwrap();
|
||||||
|
let mut baked = TextVertexArray::new();
|
||||||
|
let mut pen = [0.0, 0.0];
|
||||||
|
|
||||||
|
let mut prev: Option<char> = None;
|
||||||
|
|
||||||
|
for char in self.text.chars() {
|
||||||
|
if char == '\n' {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if char == ' ' {
|
||||||
|
pen[0] += 10.0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let font_size = match &font_style.size {
|
||||||
|
FontSize::Absolute(s) => *s,
|
||||||
|
FontSize::DistanceScale(_) => panic!(""),
|
||||||
|
FontSize::WindowScale(_) => panic!(""),
|
||||||
|
};
|
||||||
|
|
||||||
|
font.set_char_size(font_size.floor() as isize);
|
||||||
|
font.set_char(char);
|
||||||
|
|
||||||
|
let (x_advanced, y_advanced) = font.get_advance();
|
||||||
|
let (x_kerning, _) = prev.map_or((0.0, 0.0), |v| {
|
||||||
|
let kerning = font.get_kerning(v, char);
|
||||||
|
kerning
|
||||||
|
});
|
||||||
|
|
||||||
|
let metrics = font.get_metrics(char);
|
||||||
|
let bear_x = metrics.horiBearingX;
|
||||||
|
let bear_y = metrics.horiBearingY;
|
||||||
|
|
||||||
|
let x0 = pen[0] + bear_x as f32 + x_kerning;
|
||||||
|
let y0 = pen[1] - bear_y as f32;
|
||||||
|
let x1 = pen[0] + metrics.width as f32 + x_kerning;
|
||||||
|
let y1 = pen[1] + metrics.height as f32;
|
||||||
|
|
||||||
|
let position = [[x0, y0, 0.0], [x1, y0, 0.0], [x1, y1, 0.0], [x1, y0, 0.0]];
|
||||||
|
|
||||||
|
if let Some(cache) = cache.get(char) {
|
||||||
|
baked.insert_text(cache.to_owned(), position);
|
||||||
|
} else {
|
||||||
|
let char_glyph = font.get_char(char, 64)?;
|
||||||
|
let mut img = char_glyph.into();
|
||||||
|
let (result, _) = glyph_to_sdf(&mut img, SDF_PARAM, None);
|
||||||
|
let b = cache.insert_glyph(result, char);
|
||||||
|
baked.insert_text(b.to_owned(), position);
|
||||||
|
}
|
||||||
|
|
||||||
|
pen[0] += x_advanced;
|
||||||
|
pen[1] += y_advanced;
|
||||||
|
prev = Some(char);
|
||||||
|
}
|
||||||
|
Ok(baked)
|
||||||
|
} else {
|
||||||
|
Err(Error::FontError(format!(
|
||||||
|
"Font {} not found",
|
||||||
|
font_style.postscript_name
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CharImg> for Image2d<Unorm8> {
|
||||||
|
fn from(value: CharImg) -> Self {
|
||||||
|
// let img = Image2d::new(value.width() as u32, value.height() as u32, value.pixels);
|
||||||
|
let img = Image2d::from_storage(
|
||||||
|
value.width(),
|
||||||
|
value.height(),
|
||||||
|
value
|
||||||
|
.pixels()
|
||||||
|
.iter()
|
||||||
|
.map(|v| Unorm8::from_bits(*v))
|
||||||
|
.collect(),
|
||||||
|
);
|
||||||
|
img
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -53,6 +53,8 @@ impl Hello {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Graphics for Hello {
|
impl Graphics for Hello {
|
||||||
|
const id: &'static str = "Hello";
|
||||||
|
type Config = ();
|
||||||
fn compile(&mut self, gl: &glow::Context) -> Result<()> {
|
fn compile(&mut self, gl: &glow::Context) -> Result<()> {
|
||||||
self.program.compile(gl)?;
|
self.program.compile(gl)?;
|
||||||
unsafe {
|
unsafe {
|
||||||
@ -124,7 +126,7 @@ impl Graphics for Hello {
|
|||||||
&mut self.program
|
&mut self.program
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_config(&mut self, gl: &glow::Context, config: Option<&Config>) -> Result<()> {
|
fn set_config(&mut self, gl: &glow::Context, config: &()) -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,7 +149,11 @@ impl Graphics for Hello {
|
|||||||
impl AttaWithBuffer for Hello {
|
impl AttaWithBuffer for Hello {
|
||||||
type Data = ();
|
type Data = ();
|
||||||
|
|
||||||
fn bake(&self, data: &Self::Data) -> Result<(Vec<f32>, Option<Vec<u32>>, i32)> {
|
fn bake(
|
||||||
|
&self,
|
||||||
|
data: &Self::Data,
|
||||||
|
config: &<Self as Graphics>::Config,
|
||||||
|
) -> Result<(Vec<f32>, Option<Vec<u32>>, i32)> {
|
||||||
return Ok((vec![], None, 0));
|
return Ok((vec![], None, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,18 +1,23 @@
|
|||||||
pub mod collections;
|
pub mod collections;
|
||||||
pub mod colormap;
|
pub mod colormap;
|
||||||
mod colormesh;
|
mod colormesh;
|
||||||
|
pub mod font;
|
||||||
pub mod hello;
|
pub mod hello;
|
||||||
pub mod ppi;
|
pub mod ppi;
|
||||||
pub mod threed;
|
pub mod threed;
|
||||||
pub mod tools;
|
pub mod tools;
|
||||||
pub mod transforms;
|
pub mod transforms;
|
||||||
pub mod ty;
|
pub mod ty;
|
||||||
use crate::{components::Program, errors::*};
|
|
||||||
|
|
||||||
use glow::{NativeBuffer, NativeVertexArray};
|
use crate::{components::Program, errors::*, graphics::font::FontConfig};
|
||||||
|
|
||||||
|
use glow::{HasContext, NativeBuffer, NativeVertexArray};
|
||||||
use ppi::PPIConfig;
|
use ppi::PPIConfig;
|
||||||
|
|
||||||
pub trait Graphics: AttaWithBuffer {
|
pub trait Graphics {
|
||||||
|
const id: &'static str;
|
||||||
|
type Config;
|
||||||
|
|
||||||
fn draw(&self, gl: &glow::Context, count: i32) -> Result<()>;
|
fn draw(&self, gl: &glow::Context, count: i32) -> Result<()>;
|
||||||
|
|
||||||
fn compile(&mut self, gl: &glow::Context) -> Result<()>;
|
fn compile(&mut self, gl: &glow::Context) -> Result<()>;
|
||||||
@ -23,21 +28,37 @@ pub trait Graphics: AttaWithBuffer {
|
|||||||
|
|
||||||
fn program_mut(&mut self) -> &mut Program;
|
fn program_mut(&mut self) -> &mut Program;
|
||||||
|
|
||||||
fn mount(&mut self, gl: &glow::Context) -> Result<()>;
|
fn mount(&mut self, gl: &glow::Context) -> Result<()> {
|
||||||
|
unsafe {
|
||||||
|
gl.use_program(self.program_ref().native_program.clone());
|
||||||
|
}
|
||||||
|
|
||||||
fn unmount(&mut self, gl: &glow::Context) -> Result<()>;
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn set_config(&mut self, gl: &glow::Context, config: Option<&Config>) -> Result<()>;
|
fn unmount(&mut self, gl: &glow::Context) -> Result<()> {
|
||||||
|
unsafe {
|
||||||
|
gl.use_program(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_config(&mut self, gl: &glow::Context, config: &Self::Config) -> Result<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait AttaWithProgram {
|
pub trait AttaWithProgram {
|
||||||
fn attach_with_program(&self, gl: &glow::Context, program: &Program) -> Result<()>;
|
fn attach_with_program(&self, gl: &glow::Context, program: &Program) -> Result<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait AttaWithBuffer {
|
pub trait AttaWithBuffer: Graphics {
|
||||||
type Data;
|
type Data;
|
||||||
|
|
||||||
fn bake(&self, data: &Self::Data) -> Result<(Vec<f32>, Option<Vec<u32>>, i32)>;
|
fn bake(
|
||||||
|
&self,
|
||||||
|
data: &Self::Data,
|
||||||
|
config: &<Self as Graphics>::Config,
|
||||||
|
) -> Result<(Vec<f32>, Option<Vec<u32>>, i32)>;
|
||||||
fn init(&self, gl: &glow::Context) -> (NativeVertexArray, NativeBuffer, Option<NativeBuffer>);
|
fn init(&self, gl: &glow::Context) -> (NativeVertexArray, NativeBuffer, Option<NativeBuffer>);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,23 +70,54 @@ macro_rules! config_for_everyitem {
|
|||||||
Self::$name(value)
|
Self::$name(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Config> for $conf {
|
||||||
|
fn from(value: Config) -> Self {
|
||||||
|
if let Config::$name(value) = value {
|
||||||
|
value
|
||||||
|
} else {
|
||||||
|
panic!("error transfer");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a Config> for &'a $conf {
|
||||||
|
fn from(value: &'a Config) -> &'a $conf {
|
||||||
|
if let Config::$name(value) = value {
|
||||||
|
&value
|
||||||
|
} else {
|
||||||
|
panic!("error transfer");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
)+
|
)+
|
||||||
|
|
||||||
|
impl From<()> for Config {
|
||||||
|
fn from(_: ()) -> Self {
|
||||||
|
Self::None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Config {
|
pub enum Config {
|
||||||
PPI(PPIConfig),
|
PPI(PPIConfig),
|
||||||
|
Font(FontConfig),
|
||||||
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
config_for_everyitem!({PPIConfig => PPI},);
|
config_for_everyitem!({PPIConfig => PPI},{FontConfig => Font}, );
|
||||||
|
|
||||||
pub trait AttachWithMouse: AttaWithProgram {
|
pub trait AttachWithMouse {
|
||||||
fn attach_with_mouse(&mut self, state: &MouseState);
|
fn attach_with_mouse(&mut self, state: &MouseState);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum MouseState {
|
pub enum MouseState {
|
||||||
Drag { from: [f32; 2], delta: [f32; 2] },
|
Drag { from: [f32; 2], delta: [f32; 2] },
|
||||||
|
None,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,17 +1,16 @@
|
|||||||
use glow::{HasContext, NativeBuffer, NativeVertexArray};
|
|
||||||
|
|
||||||
use crate::components::{CodeType, Program, Shader};
|
|
||||||
use crate::data_loader::{CoordType, Data, DataType};
|
|
||||||
use crate::errors::*;
|
|
||||||
|
|
||||||
use super::colormap::ColorMap;
|
use super::colormap::ColorMap;
|
||||||
use super::threed::ThreeD;
|
use super::threed::ThreeD;
|
||||||
use super::transforms::viewport::Viewport;
|
use super::transforms::viewport::Viewport;
|
||||||
use super::{transforms, AttaWithBuffer, AttaWithProgram, AttachWithMouse, Config, Graphics};
|
use super::{transforms, AttaWithBuffer, AttaWithProgram, AttachWithMouse, Config, Graphics};
|
||||||
|
use crate::components::{CodeType, Program, Shader};
|
||||||
|
use crate::data_loader::{CoordType, Data, DataType};
|
||||||
|
use crate::errors::*;
|
||||||
|
use crate::graphics::colormap::linear::LinearColormap;
|
||||||
|
use crate::graphics::transforms::Transform;
|
||||||
|
use glow::{HasContext, NativeBuffer, NativeVertexArray};
|
||||||
|
|
||||||
pub struct PPI {
|
pub struct PPI {
|
||||||
program: Program,
|
program: Program,
|
||||||
layer: isize,
|
|
||||||
cmap: Option<Box<dyn ColorMap>>,
|
cmap: Option<Box<dyn ColorMap>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,15 +31,34 @@ impl PPI {
|
|||||||
CodeType::<String>::Path("ppi.frag".into()),
|
CodeType::<String>::Path("ppi.frag".into()),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let transform = ThreeD::new(1.0, 2.0, 1000.0, 45.0)?;
|
let mut cmap = LinearColormap::new()?;
|
||||||
|
cmap.set_colors(vec![
|
||||||
|
[170, 170, 170, 255],
|
||||||
|
[0, 34, 255, 255],
|
||||||
|
[1, 160, 246, 255],
|
||||||
|
[0, 236, 236, 255],
|
||||||
|
[0, 216, 0, 255],
|
||||||
|
[1, 144, 0, 255],
|
||||||
|
[255, 255, 0, 255],
|
||||||
|
[231, 192, 0, 255],
|
||||||
|
[255, 144, 0, 255],
|
||||||
|
[255, 0, 0, 255],
|
||||||
|
[214, 0, 0, 255],
|
||||||
|
[192, 0, 0, 255],
|
||||||
|
[255, 0, 240, 255],
|
||||||
|
[150, 0, 180, 255],
|
||||||
|
]);
|
||||||
|
cmap.set_range(0.0, 70.0);
|
||||||
|
|
||||||
|
let transform = ThreeD::new(1.0, 0.1, 100.0, 45.0)?;
|
||||||
|
|
||||||
let mut program = Program::new(vertex, fragment, Some(geom), "330 core");
|
let mut program = Program::new(vertex, fragment, Some(geom), "330 core");
|
||||||
program.set_transform(&transform);
|
program.set_transform(&transform);
|
||||||
|
program.set_hook("colormap", cmap.snippet_ref());
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
program,
|
program,
|
||||||
layer: 0,
|
cmap: Some(Box::new(cmap)),
|
||||||
cmap: None,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,11 +66,6 @@ impl PPI {
|
|||||||
self.program.set_viewport(viewport);
|
self.program.set_viewport(viewport);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_colormap(&mut self, colormap: Box<dyn ColorMap>) {
|
|
||||||
self.program.set_hook("colormap", colormap.snippet_ref());
|
|
||||||
self.cmap = Some(colormap);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn program(&mut self) -> &mut Program {
|
pub fn program(&mut self) -> &mut Program {
|
||||||
&mut self.program
|
&mut self.program
|
||||||
}
|
}
|
||||||
@ -64,37 +77,9 @@ impl PPI {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bake_data(&self, data: &Data) -> Result<(Vec<f32>, i32)> {
|
// fn bake_data(&self, data: &Data, layer: usize) -> Result<(Vec<f32>, i32)> {
|
||||||
let first_block = data.blocks.get(0).unwrap();
|
|
||||||
let first_block_data = first_block.data.view();
|
|
||||||
if let CoordType::Polar {
|
|
||||||
r_range,
|
|
||||||
azimuth,
|
|
||||||
r,
|
|
||||||
..
|
|
||||||
} = &first_block.coord_type
|
|
||||||
{
|
|
||||||
let azimuth_len = azimuth.len();
|
|
||||||
let r_len = r.len();
|
|
||||||
|
|
||||||
let mut vertices = Vec::with_capacity(azimuth_len * r_len);
|
// }
|
||||||
|
|
||||||
for azi_idx in 0..azimuth_len {
|
|
||||||
for r_idx in 0..r_len {
|
|
||||||
let azi = azimuth.get(azi_idx).unwrap();
|
|
||||||
let r = r.get(r_idx).unwrap() / r_range[1] as f32;
|
|
||||||
let dt = first_block_data
|
|
||||||
.get([self.layer as usize, azi_idx, r_idx])
|
|
||||||
.unwrap();
|
|
||||||
vertices.extend([r, *azi, *dt]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let len = vertices.len() as i32 / 3;
|
|
||||||
return Ok((vertices, len));
|
|
||||||
} else {
|
|
||||||
return Err(Error::InvalidDataType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn data_info(&self, data: &Data) -> Result<(f32, f32, DataType)> {
|
pub fn data_info(&self, data: &Data) -> Result<(f32, f32, DataType)> {
|
||||||
let first_block = data.blocks.get(0).unwrap();
|
let first_block = data.blocks.get(0).unwrap();
|
||||||
@ -162,6 +147,8 @@ fn max_step(data: &Vec<f32>) -> f32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Graphics for PPI {
|
impl Graphics for PPI {
|
||||||
|
const id: &'static str = "PPI";
|
||||||
|
type Config = PPIConfig;
|
||||||
fn compile(&mut self, gl: &glow::Context) -> Result<()> {
|
fn compile(&mut self, gl: &glow::Context) -> Result<()> {
|
||||||
self.program.compile(gl)?;
|
self.program.compile(gl)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -194,15 +181,8 @@ impl Graphics for PPI {
|
|||||||
&mut self.program
|
&mut self.program
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_config(&mut self, gl: &glow::Context, config: Option<&Config>) -> Result<()> {
|
fn set_config(&mut self, gl: &glow::Context, config: &Self::Config) -> Result<()> {
|
||||||
if let Some(config) = config {
|
self.init(gl, config);
|
||||||
if let Config::PPI(config) = config {
|
|
||||||
self.init(gl, config);
|
|
||||||
} else {
|
|
||||||
panic!("Errr config type");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,10 +204,39 @@ impl Graphics for PPI {
|
|||||||
impl AttaWithBuffer for PPI {
|
impl AttaWithBuffer for PPI {
|
||||||
type Data = Data;
|
type Data = Data;
|
||||||
|
|
||||||
fn bake(&self, data: &Self::Data) -> Result<(Vec<f32>, Option<Vec<u32>>, i32)> {
|
fn bake(
|
||||||
// let (rdpi, adpi, data_type) = self.data_info(data)?;
|
&self,
|
||||||
let baked_buffer = self.bake_data(data)?;
|
data: &Self::Data,
|
||||||
Ok((baked_buffer.0, None, baked_buffer.1))
|
config: &<Self as Graphics>::Config,
|
||||||
|
) -> Result<(Vec<f32>, Option<Vec<u32>>, i32)> {
|
||||||
|
let layer = config.layer;
|
||||||
|
let first_block = data.blocks.get(0).unwrap();
|
||||||
|
let first_block_data = first_block.data.view();
|
||||||
|
if let CoordType::Polar {
|
||||||
|
r_range,
|
||||||
|
azimuth,
|
||||||
|
r,
|
||||||
|
..
|
||||||
|
} = &first_block.coord_type
|
||||||
|
{
|
||||||
|
let azimuth_len = azimuth.len();
|
||||||
|
let r_len = r.len();
|
||||||
|
|
||||||
|
let mut vertices = Vec::with_capacity(azimuth_len * r_len);
|
||||||
|
|
||||||
|
for azi_idx in 0..azimuth_len {
|
||||||
|
for r_idx in 0..r_len {
|
||||||
|
let azi = azimuth.get(azi_idx).unwrap();
|
||||||
|
let r = r.get(r_idx).unwrap() / r_range[1] as f32;
|
||||||
|
let dt = first_block_data.get([layer, azi_idx, r_idx]).unwrap();
|
||||||
|
vertices.extend([r, *azi, *dt]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let len = vertices.len() as i32 / 3;
|
||||||
|
return Ok((vertices, None, len));
|
||||||
|
} else {
|
||||||
|
return Err(Error::InvalidDataType);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init(&self, gl: &glow::Context) -> (NativeVertexArray, NativeBuffer, Option<NativeBuffer>) {
|
fn init(&self, gl: &glow::Context) -> (NativeVertexArray, NativeBuffer, Option<NativeBuffer>) {
|
||||||
@ -245,7 +254,7 @@ impl AttaWithBuffer for PPI {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Clone)]
|
#[derive(Default, Clone, Debug)]
|
||||||
pub struct PPIConfig {
|
pub struct PPIConfig {
|
||||||
pub layer: usize,
|
pub layer: usize,
|
||||||
pub rdpi: f32,
|
pub rdpi: f32,
|
||||||
|
|||||||
@ -17,17 +17,17 @@ pub struct ThreeD {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ThreeD {
|
impl ThreeD {
|
||||||
pub fn new(aspect: f32, z_far: f32, z_near: f32, fov: f32) -> Result<Self> {
|
pub fn new(aspect: f32, z_near: f32, z_far: f32, fov: f32) -> Result<Self> {
|
||||||
let trackball = Trackball::new()?;
|
let trackball = Trackball::new()?;
|
||||||
let transform = ChainedTransform::from(&trackball).chain(&Position::new()?);
|
let transform = ChainedTransform::from(&trackball).chain(&Position::new()?);
|
||||||
|
|
||||||
let camera = Camera::new(
|
let camera = Camera::new(
|
||||||
Vec3::new(0.0, 0.0, 0.0),
|
Vec3::new(0.0, -1.0, 0.0),
|
||||||
Vec3::new(0.0, 0.0, -1.0),
|
Vec3::new(0.0, 1.0, 0.0),
|
||||||
Vec3::new(0.0, 1.0, 0.0),
|
Vec3::new(0.0, 1.0, 0.0),
|
||||||
);
|
);
|
||||||
|
|
||||||
let projection = nalgebra_glm::perspective(aspect, fov, z_near, z_far);
|
let projection = nalgebra_glm::perspective(aspect, fov.to_radians(), z_near, z_far);
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
transform: Rc::new(transform),
|
transform: Rc::new(transform),
|
||||||
@ -36,11 +36,27 @@ impl ThreeD {
|
|||||||
projection,
|
projection,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_aspect(&mut self, aspect: f32) {
|
||||||
|
self.projection = nalgebra_glm::perspective(aspect, 45.0f32.to_radians(), 0.1, 100.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_fov(&mut self, fov: f32) {
|
||||||
|
self.projection = nalgebra_glm::perspective(1.0, fov.to_radians(), 0.1, 100.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_z_near(&mut self, z_near: f32) {
|
||||||
|
self.projection = nalgebra_glm::perspective(1.0, 45.0f32.to_radians(), z_near, 100.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_z_far(&mut self, z_far: f32) {
|
||||||
|
self.projection = nalgebra_glm::perspective(1.0, 45.0f32.to_radians(), 0.1, z_far);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ThreeD {
|
impl Default for ThreeD {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::new(1.0, 2.0, 1000.0, 45.0).unwrap()
|
Self::new(16.0 / 9.0, 0.1, 1000.0, 45.0).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,14 +69,54 @@ impl AttaWithProgram for ThreeD {
|
|||||||
unsafe {
|
unsafe {
|
||||||
let view = program.get_uniform_location(gl, "trackball_view");
|
let view = program.get_uniform_location(gl, "trackball_view");
|
||||||
let projection = program.get_uniform_location(gl, "trackball_projection");
|
let projection = program.get_uniform_location(gl, "trackball_projection");
|
||||||
self.trackball.attach_with_program(gl, program)?;
|
let model = program.get_uniform_location(gl, "trackball_model");
|
||||||
|
|
||||||
|
let view_mat = nalgebra_glm::translation(&nalgebra_glm::vec3(0.0, 0.0, -3.0));
|
||||||
|
|
||||||
gl.uniform_matrix_4_f32_slice(
|
gl.uniform_matrix_4_f32_slice(
|
||||||
view.as_ref(),
|
view.as_ref(),
|
||||||
false,
|
false,
|
||||||
self.camera.get_view_matrix().as_slice(),
|
// nalgebra::Matrix4::identity().as_slice(),
|
||||||
|
view_mat.as_slice(),
|
||||||
|
// self.camera.get_view_matrix().as_slice(),
|
||||||
);
|
);
|
||||||
gl.uniform_matrix_4_f32_slice(projection.as_ref(), false, self.projection.as_slice());
|
|
||||||
|
// println!("projection: {:?}", self.projection);
|
||||||
|
|
||||||
|
// let scale_factor = nalgebra_glm::vec3(1.5, 1.5, 1.0);
|
||||||
|
// let res = nalgebra_glm::scaling(&scale_factor);
|
||||||
|
let ident: nalgebra_glm::Mat4 = nalgebra_glm::identity();
|
||||||
|
gl.uniform_matrix_4_f32_slice(
|
||||||
|
model.as_ref(),
|
||||||
|
false,
|
||||||
|
ident.as_slice(),
|
||||||
|
// nalgebra::Matrix4::identity().as_slice(),
|
||||||
|
);
|
||||||
|
|
||||||
|
gl.uniform_matrix_4_f32_slice(
|
||||||
|
view.as_ref(),
|
||||||
|
false,
|
||||||
|
ident.as_slice(),
|
||||||
|
// nalgebra::Matrix4::identity().as_slice(),
|
||||||
|
);
|
||||||
|
|
||||||
|
gl.uniform_matrix_4_f32_slice(
|
||||||
|
projection.as_ref(),
|
||||||
|
false,
|
||||||
|
ident.as_slice(),
|
||||||
|
// nalgebra::Matrix4::identity().as_slice(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// let rotate =
|
||||||
|
// nalgebra_glm::rotation(45.0f32.to_radians(), &nalgebra_glm::vec3(1.0, 0.0, 0.0));
|
||||||
|
|
||||||
|
// gl.uniform_matrix_4_f32_slice(
|
||||||
|
// model.as_ref(),
|
||||||
|
// false,
|
||||||
|
// rotate.as_slice(), // nalgebra::Matrix4::identity().as_slice(),
|
||||||
|
// );
|
||||||
|
|
||||||
|
// gl.uniform_matrix_4_f32_slice(projection.as_ref(), false, self.projection.as_slice());
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1 @@
|
|||||||
|
|
||||||
@ -14,7 +14,7 @@ impl Position {
|
|||||||
let snippets = Snippet::new(
|
let snippets = Snippet::new(
|
||||||
"position",
|
"position",
|
||||||
CodeType::<&'static str>::Path("transform/position.glsl".into()),
|
CodeType::<&'static str>::Path("transform/position.glsl".into()),
|
||||||
true,
|
false,
|
||||||
None,
|
None,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
|||||||
@ -140,7 +140,7 @@ impl Trackball {
|
|||||||
let snippets = Snippet::new(
|
let snippets = Snippet::new(
|
||||||
"trackball",
|
"trackball",
|
||||||
CodeType::<&'static str>::Path("transform/trackball.glsl".into()),
|
CodeType::<&'static str>::Path("transform/trackball.glsl".into()),
|
||||||
true,
|
false,
|
||||||
None,
|
None,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@ -170,8 +170,7 @@ impl Transform for Trackball {
|
|||||||
impl AttaWithProgram for Trackball {
|
impl AttaWithProgram for Trackball {
|
||||||
fn attach_with_program(&self, gl: &glow::Context, program: &Program) -> Result<()> {
|
fn attach_with_program(&self, gl: &glow::Context, program: &Program) -> Result<()> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let model = self.snippet.find_symbol("trackball_model").unwrap();
|
let l = program.get_uniform_location(gl, "trackball_model");
|
||||||
let l = program.get_uniform_location(gl, &model);
|
|
||||||
gl.uniform_matrix_4_f32_slice(l.as_ref(), false, self.model.model().as_slice());
|
gl.uniform_matrix_4_f32_slice(l.as_ref(), false, self.model.model().as_slice());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,13 +1,16 @@
|
|||||||
mod ui;
|
#![allow(unused)]
|
||||||
use pg::App;
|
|
||||||
mod camera;
|
mod camera;
|
||||||
mod components;
|
mod components;
|
||||||
mod data_loader;
|
mod data_loader;
|
||||||
mod errors;
|
mod errors;
|
||||||
|
mod font_manager;
|
||||||
mod graphics;
|
mod graphics;
|
||||||
mod pg;
|
mod pg;
|
||||||
mod support;
|
mod support;
|
||||||
|
mod ui;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
|
use pg::App;
|
||||||
use support::supporter::run;
|
use support::supporter::run;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|||||||
839
src/pg.rs
839
src/pg.rs
@ -1,839 +0,0 @@
|
|||||||
use crate::components::Program;
|
|
||||||
use crate::data_loader::Data;
|
|
||||||
use crate::graphics::colormap::linear::LinearColormap;
|
|
||||||
use crate::graphics::ppi::PPIConfig;
|
|
||||||
use crate::graphics::threed::ThreeD;
|
|
||||||
use crate::graphics::transforms::position::Position;
|
|
||||||
use crate::graphics::transforms::viewport::Viewport;
|
|
||||||
use crate::graphics::transforms::ChainedTransform;
|
|
||||||
use crate::graphics::{ppi::PPI, Graphics};
|
|
||||||
use crate::graphics::{AttaWithBuffer, AttaWithProgram, AttachWithMouse, Config, MouseState};
|
|
||||||
use crate::{errors::*, ui::base};
|
|
||||||
use glow::{HasContext, NativeBuffer, NativeFramebuffer, NativeTexture, NativeVertexArray};
|
|
||||||
use imgui::{ImStr, ImString, Textures, Ui};
|
|
||||||
use log::info;
|
|
||||||
use serde::de;
|
|
||||||
use std::collections::HashSet;
|
|
||||||
use std::{cell::RefCell, collections::HashMap, rc::Rc};
|
|
||||||
use winit::window;
|
|
||||||
pub type Graphic<Data> = Rc<RefCell<dyn Graphics<Data = Data>>>;
|
|
||||||
type RcGraphic<T> = Rc<RefCell<T>>;
|
|
||||||
|
|
||||||
pub struct App<'a> {
|
|
||||||
pub ui_state: State,
|
|
||||||
gl: &'a glow::Context,
|
|
||||||
viewport: Viewport,
|
|
||||||
windows: HashMap<ImString, WindowData>,
|
|
||||||
programs: [Graphic<Data>; 1],
|
|
||||||
pub ppi_module: RcGraphic<PPI>,
|
|
||||||
program_with_window: HashMap<usize, Vec<ImString>>,
|
|
||||||
// Auto clean up
|
|
||||||
all_vaos: HashMap<NativeVertexArray, usize>,
|
|
||||||
all_other_buffers: HashMap<NativeBuffer, usize>,
|
|
||||||
all_framebuffers: HashMap<NativeFramebuffer, usize>,
|
|
||||||
all_frametextures: HashMap<NativeTexture, usize>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> App<'a> {
|
|
||||||
pub fn new(gl: &'a glow::Context) -> Result<Self> {
|
|
||||||
let viewport = Viewport::new()?;
|
|
||||||
|
|
||||||
let mut cmap = LinearColormap::new().unwrap();
|
|
||||||
cmap.set_colors(vec![
|
|
||||||
[170, 170, 170, 255],
|
|
||||||
[0, 34, 255, 255],
|
|
||||||
[1, 160, 246, 255],
|
|
||||||
[0, 236, 236, 255],
|
|
||||||
[0, 216, 0, 255],
|
|
||||||
[1, 144, 0, 255],
|
|
||||||
[255, 255, 0, 255],
|
|
||||||
[231, 192, 0, 255],
|
|
||||||
[255, 144, 0, 255],
|
|
||||||
[255, 0, 0, 255],
|
|
||||||
[214, 0, 0, 255],
|
|
||||||
[192, 0, 0, 255],
|
|
||||||
[255, 0, 240, 255],
|
|
||||||
[150, 0, 180, 255],
|
|
||||||
]);
|
|
||||||
cmap.set_range(0.0, 70.0);
|
|
||||||
let cmap = Box::new(cmap);
|
|
||||||
|
|
||||||
let mut ppi = PPI::new()?;
|
|
||||||
ppi.set_viewport(&viewport);
|
|
||||||
ppi.set_colormap(cmap);
|
|
||||||
|
|
||||||
let ppi = Rc::new(RefCell::new(ppi));
|
|
||||||
let programs = [ppi.clone() as Graphic<Data>];
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
ui_state: State {},
|
|
||||||
viewport,
|
|
||||||
programs,
|
|
||||||
windows: HashMap::new(),
|
|
||||||
ppi_module: ppi,
|
|
||||||
gl,
|
|
||||||
program_with_window: HashMap::new(),
|
|
||||||
all_vaos: HashMap::with_capacity(30),
|
|
||||||
all_other_buffers: HashMap::with_capacity(30),
|
|
||||||
all_framebuffers: HashMap::with_capacity(30),
|
|
||||||
all_frametextures: HashMap::with_capacity(30),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn render(&mut self) {
|
|
||||||
let mut need_clean = false;
|
|
||||||
for (id, program) in self.programs.iter().enumerate() {
|
|
||||||
let mut p = program.borrow_mut();
|
|
||||||
|
|
||||||
if self.program_with_window.len() == 0 {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
p.mount(&self.gl).unwrap();
|
|
||||||
self.program_with_window.get(&id).map(|windows| {
|
|
||||||
for window in windows.iter() {
|
|
||||||
let window_info = self.windows.get_mut(window).unwrap();
|
|
||||||
if !window_info.need_redraw {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let conf = if window_info.re_init {
|
|
||||||
window_info.config.as_ref()
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
{
|
|
||||||
p.set_config(self.gl, conf).unwrap();
|
|
||||||
window_info.re_init = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(motion) = window_info.modifer.as_ref() {
|
|
||||||
motion.attach_with_program(&self.gl, p.program_ref()).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
self.gl
|
|
||||||
.bind_framebuffer(glow::FRAMEBUFFER, window_info.framebuffer);
|
|
||||||
|
|
||||||
let attach = window_info.attach.as_ref();
|
|
||||||
|
|
||||||
if attach.is_some() {
|
|
||||||
self.gl.bind_vertex_array(Some(attach.unwrap().vao));
|
|
||||||
}
|
|
||||||
|
|
||||||
if attach.is_some() {
|
|
||||||
let window_size = window_info.size;
|
|
||||||
|
|
||||||
self.gl
|
|
||||||
.viewport(0, 0, window_size[0] as i32, window_size[1] as i32);
|
|
||||||
p.draw(&self.gl, attach.as_ref().unwrap().len).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
if attach.is_some() {
|
|
||||||
self.gl.bind_vertex_array(None);
|
|
||||||
}
|
|
||||||
self.gl.bind_framebuffer(glow::FRAMEBUFFER, None);
|
|
||||||
window_info.need_redraw = false;
|
|
||||||
need_clean = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
p.unmount(&self.gl).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
if need_clean {
|
|
||||||
self.clean();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn render_ui(&mut self, ui: &Ui, window: &winit::window::Window, run: &mut bool) {
|
|
||||||
base(ui, window, run, self);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_framebuffer(
|
|
||||||
&mut self,
|
|
||||||
id: &str,
|
|
||||||
size: (i32, i32),
|
|
||||||
) -> Result<(NativeFramebuffer, NativeTexture)> {
|
|
||||||
let id = &ImString::new(id);
|
|
||||||
let gl = self.gl;
|
|
||||||
let tex = unsafe {
|
|
||||||
let already = self.windows.contains_key(id)
|
|
||||||
&& self.windows.get(id).unwrap().framebuffer.is_some();
|
|
||||||
|
|
||||||
if already {
|
|
||||||
return Ok((
|
|
||||||
self.windows[id].framebuffer.unwrap(),
|
|
||||||
self.windows[id].frametexture.unwrap(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let framebuffer = gl.create_framebuffer().unwrap();
|
|
||||||
gl.bind_framebuffer(glow::FRAMEBUFFER, Some(framebuffer));
|
|
||||||
|
|
||||||
let texture = gl.create_texture().unwrap();
|
|
||||||
gl.bind_texture(glow::TEXTURE_2D, Some(texture));
|
|
||||||
gl.tex_image_2d(
|
|
||||||
glow::TEXTURE_2D,
|
|
||||||
0,
|
|
||||||
glow::RGB8 as i32,
|
|
||||||
size.0,
|
|
||||||
size.1,
|
|
||||||
0,
|
|
||||||
glow::RGB,
|
|
||||||
glow::UNSIGNED_BYTE,
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
gl.tex_parameter_i32(
|
|
||||||
glow::TEXTURE_2D,
|
|
||||||
glow::TEXTURE_MIN_FILTER,
|
|
||||||
glow::LINEAR as i32,
|
|
||||||
);
|
|
||||||
gl.tex_parameter_i32(
|
|
||||||
glow::TEXTURE_2D,
|
|
||||||
glow::TEXTURE_MAG_FILTER,
|
|
||||||
glow::LINEAR as i32,
|
|
||||||
);
|
|
||||||
|
|
||||||
gl.framebuffer_texture_2d(
|
|
||||||
glow::FRAMEBUFFER,
|
|
||||||
glow::COLOR_ATTACHMENT0,
|
|
||||||
glow::TEXTURE_2D,
|
|
||||||
Some(texture),
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
gl.check_framebuffer_status(glow::FRAMEBUFFER),
|
|
||||||
glow::FRAMEBUFFER_COMPLETE
|
|
||||||
);
|
|
||||||
|
|
||||||
gl.bind_framebuffer(glow::FRAMEBUFFER, None);
|
|
||||||
gl.bind_texture(glow::TEXTURE_2D, None);
|
|
||||||
|
|
||||||
self.all_framebuffers_add(&framebuffer);
|
|
||||||
self.all_frametextures_add(&texture);
|
|
||||||
(framebuffer, texture)
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(tex)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn prepare(&mut self) {
|
|
||||||
for program in self.programs.iter() {
|
|
||||||
let mut p = program.borrow_mut();
|
|
||||||
p.compile(&self.gl).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn destroy(&mut self) {
|
|
||||||
for p in self.programs.iter() {
|
|
||||||
let mut p = p.borrow_mut();
|
|
||||||
p.unmount(&self.gl).unwrap();
|
|
||||||
p.destroy(&self.gl).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
info!("Cleaning up all resources");
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
for vao in self.all_vaos.keys() {
|
|
||||||
self.gl.delete_vertex_array(*vao);
|
|
||||||
}
|
|
||||||
|
|
||||||
for vbo in self.all_other_buffers.keys() {
|
|
||||||
self.gl.delete_buffer(*vbo);
|
|
||||||
}
|
|
||||||
|
|
||||||
for framebuffer in self.all_framebuffers.keys() {
|
|
||||||
self.gl.delete_framebuffer(*framebuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
for texture in self.all_frametextures.keys() {
|
|
||||||
self.gl.delete_texture(*texture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_ppi_render(&mut self, id: &str, config: Option<PPIConfig>) {
|
|
||||||
let id = &ImString::new(id);
|
|
||||||
let (vao, vbo, ebo) = self.ppi_module.borrow().init(&self.gl);
|
|
||||||
self.windows.get_mut(id).map(|w| {
|
|
||||||
w.attach = Some(Attach {
|
|
||||||
vao,
|
|
||||||
vbo,
|
|
||||||
ebo,
|
|
||||||
len: 0,
|
|
||||||
});
|
|
||||||
w.need_redraw = true;
|
|
||||||
w.program = 0;
|
|
||||||
w.config = Some(config.unwrap_or_default().into());
|
|
||||||
});
|
|
||||||
|
|
||||||
let v = self.program_with_window.entry(0).or_insert(vec![]);
|
|
||||||
v.push(id.clone());
|
|
||||||
|
|
||||||
self.all_vaos_add(&vao);
|
|
||||||
self.all_other_buffers_add(&vbo);
|
|
||||||
ebo.map(|ebo| self.all_other_buffers_add(&ebo));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn bind_data(&mut self, id: &str, data: &Data) -> Result<()> {
|
|
||||||
let id = &ImString::new(id);
|
|
||||||
use bytemuck::cast_slice;
|
|
||||||
|
|
||||||
let window = self.windows.get_mut(id).unwrap();
|
|
||||||
|
|
||||||
let program = window.program;
|
|
||||||
let program = self.programs[program].borrow();
|
|
||||||
|
|
||||||
let data = program.bake(data)?;
|
|
||||||
|
|
||||||
assert!(window.attach.is_some());
|
|
||||||
let attach = window.attach.as_mut().unwrap();
|
|
||||||
|
|
||||||
attach.len = data.2;
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
self.gl.bind_buffer(glow::VERTEX_ARRAY, Some(attach.vbo));
|
|
||||||
self.gl.buffer_data_u8_slice(
|
|
||||||
glow::ARRAY_BUFFER,
|
|
||||||
cast_slice(data.0.as_slice()),
|
|
||||||
glow::STATIC_DRAW,
|
|
||||||
);
|
|
||||||
if let Some(ebo) = attach.ebo {
|
|
||||||
self.gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, attach.ebo);
|
|
||||||
self.gl.buffer_data_u8_slice(
|
|
||||||
glow::ELEMENT_ARRAY_BUFFER,
|
|
||||||
cast_slice(&data.1.as_ref().unwrap()),
|
|
||||||
glow::STATIC_DRAW,
|
|
||||||
);
|
|
||||||
|
|
||||||
self.gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, None);
|
|
||||||
}
|
|
||||||
self.gl.bind_buffer(glow::VERTEX_ARRAY, None);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn all_framebuffers_add(&mut self, framebuffer: &NativeFramebuffer) {
|
|
||||||
self.all_framebuffers
|
|
||||||
.entry(*framebuffer)
|
|
||||||
.and_modify(|v| *v += 1)
|
|
||||||
.or_insert(1);
|
|
||||||
|
|
||||||
info!(
|
|
||||||
"Framebuffer: {:?} + 1, Framebuffer {:?}: {}",
|
|
||||||
framebuffer, framebuffer, self.all_framebuffers[framebuffer]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn all_framebuffers_minus(&mut self, framebuffer: &NativeFramebuffer) {
|
|
||||||
self.all_framebuffers
|
|
||||||
.entry(*framebuffer)
|
|
||||||
.and_modify(|v| *v -= 1);
|
|
||||||
|
|
||||||
info!(
|
|
||||||
"Framebuffer: {:?} - 1, Framebuffer {:?}: {}",
|
|
||||||
framebuffer, framebuffer, self.all_framebuffers[framebuffer]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn all_frametextures_add(&mut self, texture: &NativeTexture) {
|
|
||||||
self.all_frametextures
|
|
||||||
.entry(*texture)
|
|
||||||
.and_modify(|v| *v += 1)
|
|
||||||
.or_insert(1);
|
|
||||||
|
|
||||||
info!(
|
|
||||||
"Texture: {:?} + 1, Frametexture {:?}: {}",
|
|
||||||
texture, texture, self.all_frametextures[texture]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn all_frametextures_minus(&mut self, texture: &NativeTexture) {
|
|
||||||
self.all_frametextures
|
|
||||||
.entry(*texture)
|
|
||||||
.and_modify(|v| *v -= 1);
|
|
||||||
|
|
||||||
info!(
|
|
||||||
"Texture: {:?} - 1, Frametexture {:?}: {}",
|
|
||||||
texture, texture, self.all_frametextures[texture]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn all_vaos_add(&mut self, vao: &NativeVertexArray) {
|
|
||||||
self.all_vaos
|
|
||||||
.entry(*vao)
|
|
||||||
.and_modify(|v| *v += 1)
|
|
||||||
.or_insert(1);
|
|
||||||
|
|
||||||
info!("Vao: {:?} + 1, Vao {:?}: {}", vao, vao, self.all_vaos[vao]);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn all_vaos_minus(&mut self, vao: &NativeVertexArray) {
|
|
||||||
self.all_vaos.entry(*vao).and_modify(|v| *v -= 1);
|
|
||||||
|
|
||||||
info!("Vao: {:?} - 1, Vao {:?}: {}", vao, vao, self.all_vaos[vao]);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn all_other_buffers_add(&mut self, buffer: &NativeBuffer) {
|
|
||||||
self.all_other_buffers
|
|
||||||
.entry(*buffer)
|
|
||||||
.and_modify(|v| *v += 1)
|
|
||||||
.or_insert(1);
|
|
||||||
|
|
||||||
info!(
|
|
||||||
"Buffer: {:?} + 1, Buffer {:?}: {}",
|
|
||||||
buffer, buffer, self.all_other_buffers[buffer]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn all_other_buffers_minus(&mut self, buffer: &NativeBuffer) {
|
|
||||||
self.all_other_buffers
|
|
||||||
.entry(*buffer)
|
|
||||||
.and_modify(|v| *v -= 1);
|
|
||||||
|
|
||||||
info!(
|
|
||||||
"Buffer: {:?} - 1, Buffer {:?}: {}",
|
|
||||||
buffer, buffer, self.all_other_buffers[buffer]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn show_window(&mut self, ui: &Ui) {
|
|
||||||
let mut need_resize = vec![];
|
|
||||||
|
|
||||||
for (id, window) in self.windows.iter_mut() {
|
|
||||||
ui.window(&window.title)
|
|
||||||
.size(window.size, imgui::Condition::FirstUseEver)
|
|
||||||
.opened(&mut window.open)
|
|
||||||
.flags(imgui::WindowFlags::NO_SCROLLBAR)
|
|
||||||
.build(|| {
|
|
||||||
if ui.is_mouse_clicked(imgui::MouseButton::Left) {
|
|
||||||
let io = ui.io();
|
|
||||||
let pos = io.mouse_pos;
|
|
||||||
window.last_mouse_position = pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ui.is_mouse_dragging(imgui::MouseButton::Left) {
|
|
||||||
let delta = ui.mouse_drag_delta();
|
|
||||||
window.last_mouse_delta = delta;
|
|
||||||
window.accmulate_mouse_delta = [
|
|
||||||
window.accmulate_mouse_delta[0] + delta[0],
|
|
||||||
window.accmulate_mouse_delta[1] + delta[1],
|
|
||||||
];
|
|
||||||
window.motion = Some(MouseState::Drag {
|
|
||||||
from: window.last_mouse_position,
|
|
||||||
delta: delta,
|
|
||||||
});
|
|
||||||
window.modifer.as_mut().map(|v| {
|
|
||||||
v.exec(window.motion.as_ref().unwrap());
|
|
||||||
});
|
|
||||||
window.need_redraw = true;
|
|
||||||
}
|
|
||||||
if ui.is_mouse_released(imgui::MouseButton::Left) {
|
|
||||||
if window.size != ui.window_size() {
|
|
||||||
window.size = ui.window_size();
|
|
||||||
println!("resized: {:?}", window.size);
|
|
||||||
need_resize.push((window.title.clone(), ui.window_size()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(texture) = window.frametexture {
|
|
||||||
let cursor = ui.cursor_pos();
|
|
||||||
imgui::Image::new(
|
|
||||||
imgui::TextureId::new(texture.0.get() as usize),
|
|
||||||
ui.window_size(),
|
|
||||||
)
|
|
||||||
.build(ui);
|
|
||||||
ui.set_cursor_pos(cursor);
|
|
||||||
if ui.invisible_button(&window.title, ui.window_size()) {
|
|
||||||
let io = ui.io();
|
|
||||||
let pos = io.mouse_pos;
|
|
||||||
let window_pos = ui.window_pos();
|
|
||||||
let related_pos = [pos[0] - window_pos[0], pos[1] - window_pos[1]];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
for (id, size) in need_resize.iter() {
|
|
||||||
self.reset_window_size(id, *size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// pub fn copy_window_resource(
|
|
||||||
// &mut self,
|
|
||||||
// src: &str,
|
|
||||||
// dst: &str,
|
|
||||||
// stick: bool,
|
|
||||||
// ) -> Option<NativeTexture> {
|
|
||||||
// let src = &ImString::new(src);
|
|
||||||
// let window_info = self.windows.get(src).unwrap();
|
|
||||||
|
|
||||||
// let new_texture = if stick {
|
|
||||||
// let new_framebuffer_tex = self.create_framebuffer(dst, (300, 300)).unwrap();
|
|
||||||
// unsafe {
|
|
||||||
// self.gl
|
|
||||||
// .bind_framebuffer(glow::READ_FRAMEBUFFER, window_info.framebuffer);
|
|
||||||
// self.gl
|
|
||||||
// .bind_framebuffer(glow::DRAW_FRAMEBUFFER, Some(self.framebuffers[dst].0));
|
|
||||||
// self.gl.blit_framebuffer(
|
|
||||||
// 0,
|
|
||||||
// 0,
|
|
||||||
// 300,
|
|
||||||
// 300,
|
|
||||||
// 0,
|
|
||||||
// 0,
|
|
||||||
// 300,
|
|
||||||
// 300,
|
|
||||||
// glow::COLOR_BUFFER_BIT,
|
|
||||||
// glow::NEAREST,
|
|
||||||
// );
|
|
||||||
// self.gl.bind_framebuffer(glow::READ_FRAMEBUFFER, None);
|
|
||||||
// self.gl.bind_framebuffer(glow::DRAW_FRAMEBUFFER, None);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// self.framebuffers
|
|
||||||
// .get_mut(dst)
|
|
||||||
// .map(|(_, redraw, program, atta)| {
|
|
||||||
// *atta = new_framebuffer.as_ref().map(|v| v.3.clone()).flatten();
|
|
||||||
// *redraw = true;
|
|
||||||
// *program = new_framebuffer.as_ref().map(|v| v.2).unwrap();
|
|
||||||
// });
|
|
||||||
|
|
||||||
// Some(new_framebuffer_tex)
|
|
||||||
// } else {
|
|
||||||
// let texture = self.frametextures.get(src).map(|texture| *texture);
|
|
||||||
// if let Some(ref framebuffer) = new_framebuffer {
|
|
||||||
// if let Some(ref texture) = texture {
|
|
||||||
// self.all_framebuffers_add(&framebuffer.0);
|
|
||||||
// self.all_frametextures_add(&texture);
|
|
||||||
// self.framebuffers
|
|
||||||
// .insert(ImString::new(dst), framebuffer.clone());
|
|
||||||
// self.frametextures
|
|
||||||
// .insert(ImString::new(dst), texture.clone());
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// texture
|
|
||||||
// };
|
|
||||||
|
|
||||||
// new_framebuffer.map(|framebuffer| {
|
|
||||||
// new_texture.map(|_| {
|
|
||||||
// self.all_vaos_add(&framebuffer.3.as_ref().unwrap().vao);
|
|
||||||
// self.all_other_buffers_add(&framebuffer.3.as_ref().unwrap().vbo);
|
|
||||||
// framebuffer.3.as_ref().unwrap().ebo.map(|ebo| {
|
|
||||||
// self.all_other_buffers_add(&ebo);
|
|
||||||
// });
|
|
||||||
|
|
||||||
// self.program_with_window.get_mut(&framebuffer.2).map(|v| {
|
|
||||||
// v.push(ImString::new(dst));
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
|
|
||||||
// new_texture
|
|
||||||
// }
|
|
||||||
pub fn create_render_window(&mut self, title: &str, size: [f32; 2]) -> Result<()> {
|
|
||||||
// Insert the window data into the windows hashmap
|
|
||||||
let id = ImString::new(title);
|
|
||||||
let mut data = WindowData::new(id.clone(), size, None);
|
|
||||||
let (fb, tex) =
|
|
||||||
self.create_framebuffer(title, (size[0].floor() as i32, size[1].floor() as i32))?;
|
|
||||||
|
|
||||||
data.framebuffer = Some(fb);
|
|
||||||
data.frametexture = Some(tex);
|
|
||||||
self.windows.insert(id, data);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_config(&mut self, id: &str) -> Option<&mut Config> {
|
|
||||||
let id = &ImString::new(id);
|
|
||||||
self.windows
|
|
||||||
.get_mut(id)
|
|
||||||
.map(|v| {
|
|
||||||
v.re_init = true;
|
|
||||||
v.config.as_mut()
|
|
||||||
})
|
|
||||||
.flatten()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn reset_window_size(&mut self, id: &ImString, size: [f32; 2]) {
|
|
||||||
let window_info = self.windows.get_mut(id).unwrap();
|
|
||||||
window_info.need_redraw = true;
|
|
||||||
let tex = unsafe {
|
|
||||||
self.gl
|
|
||||||
.bind_framebuffer(glow::FRAMEBUFFER, window_info.framebuffer);
|
|
||||||
let texture = self.gl.create_texture().unwrap();
|
|
||||||
self.gl.bind_texture(glow::TEXTURE_2D, Some(texture));
|
|
||||||
self.gl.tex_image_2d(
|
|
||||||
glow::TEXTURE_2D,
|
|
||||||
0,
|
|
||||||
glow::RGB8 as i32,
|
|
||||||
size[0].floor() as i32,
|
|
||||||
size[1].floor() as i32,
|
|
||||||
0,
|
|
||||||
glow::RGB,
|
|
||||||
glow::UNSIGNED_BYTE,
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
self.gl.tex_parameter_i32(
|
|
||||||
glow::TEXTURE_2D,
|
|
||||||
glow::TEXTURE_MIN_FILTER,
|
|
||||||
glow::LINEAR as i32,
|
|
||||||
);
|
|
||||||
self.gl.tex_parameter_i32(
|
|
||||||
glow::TEXTURE_2D,
|
|
||||||
glow::TEXTURE_MAG_FILTER,
|
|
||||||
glow::LINEAR as i32,
|
|
||||||
);
|
|
||||||
|
|
||||||
self.gl.framebuffer_texture_2d(
|
|
||||||
glow::FRAMEBUFFER,
|
|
||||||
glow::COLOR_ATTACHMENT0,
|
|
||||||
glow::TEXTURE_2D,
|
|
||||||
Some(texture),
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
self.gl.check_framebuffer_status(glow::FRAMEBUFFER),
|
|
||||||
glow::FRAMEBUFFER_COMPLETE
|
|
||||||
);
|
|
||||||
|
|
||||||
self.gl.bind_framebuffer(glow::FRAMEBUFFER, None);
|
|
||||||
self.gl.bind_texture(glow::TEXTURE_2D, None);
|
|
||||||
|
|
||||||
texture
|
|
||||||
};
|
|
||||||
let raw_tex = window_info.frametexture.as_ref().unwrap().to_owned();
|
|
||||||
self.all_frametextures_minus(&raw_tex);
|
|
||||||
self.all_frametextures_add(&tex);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn destroy_window(&mut self) {
|
|
||||||
let ids: Vec<_> = self
|
|
||||||
.windows
|
|
||||||
.iter()
|
|
||||||
.filter(|v| v.1.open == false)
|
|
||||||
.map(|v| v.0.clone())
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
for id in ids.iter() {
|
|
||||||
let window = self.windows.remove(id).unwrap();
|
|
||||||
|
|
||||||
window.framebuffer.map(|framebuffer| {
|
|
||||||
self.all_framebuffers_minus(&framebuffer);
|
|
||||||
});
|
|
||||||
|
|
||||||
window.frametexture.map(|texture| {
|
|
||||||
self.all_frametextures_minus(&texture);
|
|
||||||
});
|
|
||||||
|
|
||||||
if let Some(atta) = window.attach {
|
|
||||||
self.all_vaos_minus(&atta.vao);
|
|
||||||
self.all_other_buffers_minus(&atta.vbo);
|
|
||||||
atta.ebo.map(|ebo| {
|
|
||||||
self.all_other_buffers_minus(&ebo);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn clean(&mut self) {
|
|
||||||
info!("Cleaning up unused resources");
|
|
||||||
unsafe {
|
|
||||||
self.all_framebuffers
|
|
||||||
.iter()
|
|
||||||
.filter(|v| *v.1 == 0)
|
|
||||||
.for_each(|(bf, _)| {
|
|
||||||
info!("Deleting framebuffer: {:?}", bf);
|
|
||||||
self.gl.delete_framebuffer(*bf);
|
|
||||||
});
|
|
||||||
|
|
||||||
self.all_frametextures
|
|
||||||
.iter()
|
|
||||||
.filter(|v| *v.1 == 0)
|
|
||||||
.for_each(|(bf, _)| {
|
|
||||||
info!("Deleting texture: {:?}", bf);
|
|
||||||
self.gl.delete_texture(*bf);
|
|
||||||
});
|
|
||||||
|
|
||||||
self.all_vaos
|
|
||||||
.iter()
|
|
||||||
.filter(|v| *v.1 == 0)
|
|
||||||
.for_each(|(bf, _)| {
|
|
||||||
info!("Deleting vao: {:?}", bf);
|
|
||||||
self.gl.delete_vertex_array(*bf);
|
|
||||||
});
|
|
||||||
|
|
||||||
self.all_other_buffers
|
|
||||||
.iter()
|
|
||||||
.filter(|v| *v.1 == 0)
|
|
||||||
.for_each(|(bf, _)| {
|
|
||||||
info!("Deleting buffer: {:?}", bf);
|
|
||||||
self.gl.delete_buffer(*bf);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
self.all_framebuffers.retain(|_, v| *v > 0);
|
|
||||||
self.all_frametextures.retain(|_, v| *v > 0);
|
|
||||||
self.all_vaos.retain(|_, v| *v > 0);
|
|
||||||
self.all_other_buffers.retain(|_, v| *v > 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Attach {
|
|
||||||
pub vao: NativeVertexArray,
|
|
||||||
pub vbo: NativeBuffer,
|
|
||||||
pub ebo: Option<NativeBuffer>,
|
|
||||||
pub len: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct State {}
|
|
||||||
|
|
||||||
pub struct WindowData {
|
|
||||||
pub title: ImString,
|
|
||||||
pub open: bool,
|
|
||||||
pub copy_from: Option<ImString>,
|
|
||||||
pub size: [f32; 2],
|
|
||||||
framebuffer: Option<NativeFramebuffer>,
|
|
||||||
frametexture: Option<NativeTexture>,
|
|
||||||
need_redraw: bool,
|
|
||||||
program: usize,
|
|
||||||
attach: Option<Attach>,
|
|
||||||
|
|
||||||
re_init: bool,
|
|
||||||
last_mouse_position: [f32; 2],
|
|
||||||
last_mouse_delta: [f32; 2],
|
|
||||||
accmulate_mouse_delta: [f32; 2],
|
|
||||||
mouse_position: [f32; 2],
|
|
||||||
motion: Option<MouseState>,
|
|
||||||
config: Option<Config>,
|
|
||||||
modifer: Option<ModiferType>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WindowData {
|
|
||||||
pub fn new(title: ImString, size: [f32; 2], modifer: Option<ModiferType>) -> Self {
|
|
||||||
Self {
|
|
||||||
title,
|
|
||||||
open: true,
|
|
||||||
copy_from: None,
|
|
||||||
size,
|
|
||||||
last_mouse_position: [0.0, 0.0],
|
|
||||||
last_mouse_delta: [0.0, 0.0],
|
|
||||||
accmulate_mouse_delta: [0.0, 0.0],
|
|
||||||
mouse_position: [0.0, 0.0],
|
|
||||||
motion: None,
|
|
||||||
|
|
||||||
framebuffer: None,
|
|
||||||
frametexture: None,
|
|
||||||
need_redraw: false,
|
|
||||||
program: 0,
|
|
||||||
attach: None,
|
|
||||||
re_init: false,
|
|
||||||
config: None,
|
|
||||||
modifer,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_current_mouse_delta(&mut self, delta: [f32; 2]) {
|
|
||||||
self.last_mouse_delta = delta;
|
|
||||||
self.accmulate_mouse_delta = [
|
|
||||||
self.accmulate_mouse_delta[0] + delta[0],
|
|
||||||
self.accmulate_mouse_delta[1] + delta[1],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_mouse_postion(&mut self, pos: [f32; 2]) {
|
|
||||||
self.mouse_position = pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_motion(&mut self, motion: MouseState) {
|
|
||||||
self.motion = Some(motion);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_need_redraw(&mut self) {
|
|
||||||
self.need_redraw = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_config(&mut self, config: Config) {
|
|
||||||
self.config = Some(config);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_re_init(&mut self) {
|
|
||||||
self.re_init = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn on_mouse_drag(&mut self) {
|
|
||||||
let state = MouseState::Drag {
|
|
||||||
from: self.last_mouse_position,
|
|
||||||
delta: self.last_mouse_delta,
|
|
||||||
};
|
|
||||||
self.set_motion(state.clone());
|
|
||||||
|
|
||||||
self.modifer.as_mut().map(|m| {});
|
|
||||||
|
|
||||||
self.set_need_redraw();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! modifer_exec {
|
|
||||||
($(($t:ty => $b:tt),)+) => {
|
|
||||||
impl ModiferType {
|
|
||||||
pub fn exec(&mut self, motion: &MouseState) {
|
|
||||||
match self {
|
|
||||||
$(
|
|
||||||
|
|
||||||
ModiferType::$b(b) => {
|
|
||||||
b.attach_with_mouse(motion);
|
|
||||||
}
|
|
||||||
|
|
||||||
)+
|
|
||||||
|
|
||||||
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AttaWithProgram for ModiferType {
|
|
||||||
fn attach_with_program(&self, gl: &glow::Context, program: &Program) -> Result<()> {
|
|
||||||
match self {
|
|
||||||
$(
|
|
||||||
|
|
||||||
ModiferType::$b(b) => {
|
|
||||||
b.attach_with_program(gl, program)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
)+
|
|
||||||
_ => {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
$(
|
|
||||||
impl From<$t> for ModiferType {
|
|
||||||
fn from(t: $t) -> Self {
|
|
||||||
ModiferType::$b(t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)+
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum ModiferType {
|
|
||||||
ThreeD(ThreeD),
|
|
||||||
TwoD,
|
|
||||||
}
|
|
||||||
|
|
||||||
modifer_exec!((ThreeD => ThreeD),);
|
|
||||||
110
src/pg/app.rs
Normal file
110
src/pg/app.rs
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
use log::*;
|
||||||
|
use std::{cell::RefCell, path::PathBuf, rc::Rc};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
data_loader::Data,
|
||||||
|
errors::*,
|
||||||
|
graphics::{
|
||||||
|
colormap::linear::LinearColormap, ppi::PPI, threed::ThreeD, AttaWithBuffer,
|
||||||
|
AttaWithProgram, Graphics,
|
||||||
|
},
|
||||||
|
ui::{State, GUI},
|
||||||
|
utils::{
|
||||||
|
cache::{Cache, CachedData},
|
||||||
|
resources::{
|
||||||
|
ManagedResource, RcGlBuffer, RcGlFramebuffer, RcGlRcFramebuffer, RcGlRcRenderbuffer,
|
||||||
|
RcGlRcResource, RcGlRenderbuffer, RcGlResource, RcGlVertexArray, GL,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use glow::HasContext;
|
||||||
|
use imgui::Ui;
|
||||||
|
|
||||||
|
use super::app_type::{self, AppType};
|
||||||
|
use super::{ModulePackage, Programs};
|
||||||
|
use crate::{font_manager::FontManager, graphics::font::Text};
|
||||||
|
|
||||||
|
pub struct App<'gl> {
|
||||||
|
gl: &'gl GL,
|
||||||
|
context: Context<'gl>,
|
||||||
|
gui: GUI,
|
||||||
|
app_type: AppType<'gl>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'gl> App<'gl> {
|
||||||
|
pub fn new(gl: &'gl GL) -> Result<Self> {
|
||||||
|
let programs = Programs::new(gl).unwrap();
|
||||||
|
let mut default_state = State::default();
|
||||||
|
|
||||||
|
let mut gui = GUI::new(State::default());
|
||||||
|
let app_type = gui.apptype(gl);
|
||||||
|
|
||||||
|
let context = Context::new(gl, Cache::new(), programs);
|
||||||
|
Ok(Self {
|
||||||
|
gui,
|
||||||
|
gl,
|
||||||
|
context,
|
||||||
|
app_type,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init<'a>(&'a mut self) -> AppType {
|
||||||
|
self.gui.apptype(&self.gl)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn prepare(&mut self) {
|
||||||
|
if let Err(e) = self.context.programs.prepare() {
|
||||||
|
error!("prepare failed: {:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gl(&self) -> &GL {
|
||||||
|
&self.context.gl
|
||||||
|
}
|
||||||
|
|
||||||
|
fn datapool(&mut self) -> &mut Cache<PathBuf, CachedData<Data>> {
|
||||||
|
&mut self.context.datapool
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render<'a>(&'a mut self) {
|
||||||
|
if let Err(e) = self.app_type.draw_program(&mut self.context) {
|
||||||
|
error!("draw_program failed: {:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render_ui<'a>(&'a mut self, ui: &Ui, window: &winit::window::Window, run: &mut bool) {
|
||||||
|
*run = self.gui.render(ui, &mut self.context, &mut self.app_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroy(&mut self) {
|
||||||
|
self.context.programs.destroy().unwrap();
|
||||||
|
}
|
||||||
|
pub fn resize(&mut self, size: [f32; 2]) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Context<'gl> {
|
||||||
|
pub gl: &'gl GL,
|
||||||
|
pub programs: Programs<'gl>,
|
||||||
|
pub datapool: Cache<PathBuf, CachedData<Data>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'gl> Context<'gl> {
|
||||||
|
fn new(
|
||||||
|
gl: &'gl GL,
|
||||||
|
datapool: Cache<PathBuf, CachedData<Data>>,
|
||||||
|
programs: Programs<'gl>,
|
||||||
|
) -> Self {
|
||||||
|
let context = Context {
|
||||||
|
datapool,
|
||||||
|
programs,
|
||||||
|
gl,
|
||||||
|
};
|
||||||
|
context
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_data<P: Into<PathBuf>>(&mut self, path: P) -> Result<ModulePackage<'gl>> {
|
||||||
|
let data = self.datapool.get_or_insert(path.into())?;
|
||||||
|
let module_cursor = self.programs.load_data(data)?;
|
||||||
|
Ok(module_cursor)
|
||||||
|
}
|
||||||
|
}
|
||||||
145
src/pg/app_type.rs
Normal file
145
src/pg/app_type.rs
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
use crate::errors::*;
|
||||||
|
use crate::graphics::ty;
|
||||||
|
use crate::pg::{Context, ModulePackage};
|
||||||
|
use crate::ui::typ::{LayoutAppType, MainLoadTyp};
|
||||||
|
use crate::utils::resources::{ManagedResource, RcGlRcFramebuffer, RcGlRcRenderbuffer, GL};
|
||||||
|
use glow::HasContext;
|
||||||
|
|
||||||
|
const RBO_WIDTH: i32 = 1920;
|
||||||
|
const RBO_HEIGHT: i32 = 1080;
|
||||||
|
|
||||||
|
pub struct ViewPort {
|
||||||
|
renderer_size: [f32; 2],
|
||||||
|
main_fbo: RcGlRcFramebuffer,
|
||||||
|
_main_rbo: RcGlRcRenderbuffer,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ViewPort {
|
||||||
|
pub fn new(gl: &GL) -> Self {
|
||||||
|
let main_fbo: RcGlRcFramebuffer = gl.create_resource_rc();
|
||||||
|
let main_rbo: RcGlRcRenderbuffer = gl.create_resource_rc();
|
||||||
|
|
||||||
|
main_fbo.bind(glow::FRAMEBUFFER);
|
||||||
|
main_rbo.bind(glow::RENDERBUFFER);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
gl.renderbuffer_storage(glow::RENDERBUFFER, glow::RGBA8, RBO_WIDTH, RBO_HEIGHT);
|
||||||
|
gl.framebuffer_renderbuffer(
|
||||||
|
glow::FRAMEBUFFER,
|
||||||
|
glow::COLOR_ATTACHMENT0,
|
||||||
|
glow::RENDERBUFFER,
|
||||||
|
Some(main_rbo.native()),
|
||||||
|
);
|
||||||
|
// gl.clear_color(1.0, 0.0, 0.0, 1.0);
|
||||||
|
// gl.clear(glow::COLOR_BUFFER_BIT);
|
||||||
|
// 检查帧缓冲是否完整
|
||||||
|
if gl.check_framebuffer_status(glow::FRAMEBUFFER) != glow::FRAMEBUFFER_COMPLETE {
|
||||||
|
panic!("Framebuffer is not complete!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
main_fbo.unbind(glow::FRAMEBUFFER);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
renderer_size: [0.0, 0.0],
|
||||||
|
main_fbo,
|
||||||
|
_main_rbo: main_rbo,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bind(&self, gl: &glow::Context) {
|
||||||
|
self.main_fbo.bind(glow::FRAMEBUFFER);
|
||||||
|
unsafe {
|
||||||
|
gl.viewport(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
self.renderer_size[0] as i32,
|
||||||
|
self.renderer_size[1] as i32,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unbind(&self) {
|
||||||
|
self.main_fbo.unbind(glow::FRAMEBUFFER);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_size(&mut self, size: [f32; 2]) -> bool {
|
||||||
|
if size != self.renderer_size {
|
||||||
|
self.renderer_size = size;
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fbo(&self) -> &RcGlRcFramebuffer {
|
||||||
|
&self.main_fbo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! app_type_into {
|
||||||
|
($({$t: ty => $b:tt},)+) => {
|
||||||
|
$(
|
||||||
|
impl<'gl> From<$t> for AppType<'gl> {
|
||||||
|
fn from(t: $t) -> Self {
|
||||||
|
Self::$b(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a,'gl> From<&'a AppType<'gl>> for &'a $t {
|
||||||
|
fn from(t: &'a AppType<'gl>) -> Self {
|
||||||
|
if let AppType::$b(t) = t {
|
||||||
|
t
|
||||||
|
} else {
|
||||||
|
panic!("AppType is not {}", stringify!($b));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a,'gl> From<&'a mut AppType<'gl>> for &'a mut $t {
|
||||||
|
fn from(t: &'a mut AppType<'gl>) -> Self {
|
||||||
|
if let AppType::$b(t) = t {
|
||||||
|
t
|
||||||
|
} else {
|
||||||
|
panic!("AppType is not {}", stringify!($b));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
)+
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum AppType<'gl> {
|
||||||
|
MainLoad(MainLoadTyp<'gl>),
|
||||||
|
Other,
|
||||||
|
}
|
||||||
|
|
||||||
|
app_type_into!({
|
||||||
|
MainLoadTyp<'gl> => MainLoad
|
||||||
|
},);
|
||||||
|
|
||||||
|
impl<'gl> AppType<'gl> {
|
||||||
|
pub fn draw_program<'b>(&mut self, mut context: &mut Context<'gl>) -> Result<()>
|
||||||
|
where
|
||||||
|
'gl: 'b,
|
||||||
|
{
|
||||||
|
let programs = &mut context.programs;
|
||||||
|
let gl = programs.gl;
|
||||||
|
match self {
|
||||||
|
Self::MainLoad(typ) => {
|
||||||
|
programs.draw(typ)?;
|
||||||
|
}
|
||||||
|
Self::Other => {}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deal_with_cursor(&mut self, package: ModulePackage<'gl>) {
|
||||||
|
match self {
|
||||||
|
Self::MainLoad(typ) => {
|
||||||
|
typ.deal_with_cursor(package);
|
||||||
|
}
|
||||||
|
Self::Other => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
166
src/pg/mod.rs
Normal file
166
src/pg/mod.rs
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
mod app;
|
||||||
|
pub mod app_type;
|
||||||
|
mod modules;
|
||||||
|
|
||||||
|
use crate::font_manager::FontManager;
|
||||||
|
use crate::graphics::font::Text;
|
||||||
|
use crate::graphics::ppi::PPI;
|
||||||
|
use crate::graphics::threed::ThreeD;
|
||||||
|
use crate::ui::typ::LayoutAppType;
|
||||||
|
use crate::utils::cache::CachedData;
|
||||||
|
use crate::utils::resources::GL;
|
||||||
|
use crate::{errors::*, graphics::Graphics};
|
||||||
|
pub use app::{App, Context};
|
||||||
|
pub use modules::{Module, ModuleCursor, ModuleData, PPIModule, PPIPackage};
|
||||||
|
|
||||||
|
pub(super) struct Programs<'gl> {
|
||||||
|
gl: &'gl GL,
|
||||||
|
_ppi: PPI,
|
||||||
|
_text: Text<'gl>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'gl> Programs<'gl> {
|
||||||
|
pub fn new(gl: &'gl GL) -> Result<Self> {
|
||||||
|
let font_manager = FontManager::new()?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
gl,
|
||||||
|
_ppi: PPI::new()?,
|
||||||
|
_text: Text::new(gl, font_manager)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn prepare(&mut self) -> Result<()> {
|
||||||
|
self._ppi.program().compile(&self.gl)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ppi<'b>(&'b mut self) -> modules::PPIModule<'b, 'gl> {
|
||||||
|
modules::PPIModule::new(&self.gl, &mut self._ppi, &mut self._text).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroy(&mut self) -> Result<()> {
|
||||||
|
self._ppi.destroy(&self.gl)?;
|
||||||
|
self._text.destroy(&self.gl)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_data(
|
||||||
|
&mut self,
|
||||||
|
data: &CachedData<crate::data_loader::Data>,
|
||||||
|
) -> Result<ModulePackage<'gl>> {
|
||||||
|
let cdata = data.borrow();
|
||||||
|
if cdata.blocks.len() == 0 {
|
||||||
|
return Err(Error::InvalidDataType);
|
||||||
|
}
|
||||||
|
let first_block = cdata.blocks.first().unwrap();
|
||||||
|
|
||||||
|
match first_block.coord_type {
|
||||||
|
crate::data_loader::CoordType::Polar { .. } => {
|
||||||
|
let package: PPIPackage<'gl> = self.ppi().load_data(data)?;
|
||||||
|
return Ok(package.into());
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
unimplemented!("Only polar data is supported for now");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw_modules(
|
||||||
|
&mut self,
|
||||||
|
modules: &mut ModulePackage<'gl>,
|
||||||
|
init_info: &ThreeD,
|
||||||
|
) -> Result<()> {
|
||||||
|
// if !modules.need_update {
|
||||||
|
// return Ok(());
|
||||||
|
// }
|
||||||
|
match &mut modules.modules {
|
||||||
|
_ModulePackage::PPI(ppi) => {
|
||||||
|
self.ppi().start();
|
||||||
|
self.ppi().init(init_info);
|
||||||
|
self.ppi().render(ppi)?;
|
||||||
|
self.ppi().end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
modules.need_update = false;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw<M: LayoutAppType<'gl>>(&mut self, ty: &mut M) -> Result<()> {
|
||||||
|
ty.draw_program(self.gl, self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Data<'a> {
|
||||||
|
Data(&'a crate::data_loader::Data),
|
||||||
|
Other,
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_module_data {
|
||||||
|
($({$t:ty => $b: tt}),+) => {
|
||||||
|
$(
|
||||||
|
impl ModuleData for $t {
|
||||||
|
fn to_data(&self) -> Data {
|
||||||
|
Data::$b(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
)+
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_module_package {
|
||||||
|
($({$t:ty => $b: tt}),+) => {
|
||||||
|
$(
|
||||||
|
|
||||||
|
impl<'a> From<$t> for _ModulePackage<'a> {
|
||||||
|
fn from(t: $t) -> Self {
|
||||||
|
_ModulePackage::$b(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
)+
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_module_data!({
|
||||||
|
crate::data_loader::Data => Data
|
||||||
|
});
|
||||||
|
|
||||||
|
pub struct ModulePackage<'gl> {
|
||||||
|
need_update: bool,
|
||||||
|
modules: _ModulePackage<'gl>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum _ModulePackage<'gl> {
|
||||||
|
PPI(PPIPackage<'gl>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_module_package!({
|
||||||
|
PPIPackage<'a> => PPI
|
||||||
|
});
|
||||||
|
|
||||||
|
impl<'gl, T> From<T> for ModulePackage<'gl>
|
||||||
|
where
|
||||||
|
T: ModuleCursor + Into<_ModulePackage<'gl>>,
|
||||||
|
{
|
||||||
|
fn from(t: T) -> Self {
|
||||||
|
Self {
|
||||||
|
modules: t.into(),
|
||||||
|
need_update: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ModulePackage<'_> {
|
||||||
|
pub fn ui_build(&mut self, ui: &imgui::Ui) -> Result<()> {
|
||||||
|
match &mut self.modules {
|
||||||
|
_ModulePackage::PPI(ppi) => {
|
||||||
|
ppi.ui_build(ui);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
92
src/pg/modules/mod.rs
Normal file
92
src/pg/modules/mod.rs
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
use crate::utils::{
|
||||||
|
cache::CachedData,
|
||||||
|
resources::{ManagedResource, RcGlBuffer, RcGlVertexArray},
|
||||||
|
};
|
||||||
|
use glow::{HasContext, NativeBuffer, NativeVertexArray};
|
||||||
|
mod ppi;
|
||||||
|
use crate::errors::*;
|
||||||
|
pub use ppi::{PPIModule, PPIPackage};
|
||||||
|
|
||||||
|
use super::Data;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct Attach<'a> {
|
||||||
|
gl: &'a glow::Context,
|
||||||
|
pub vao: RcGlVertexArray<'a>,
|
||||||
|
pub vbo: RcGlBuffer<'a>,
|
||||||
|
pub ebo: Option<RcGlBuffer<'a>>,
|
||||||
|
pub len: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Attach<'a> {
|
||||||
|
fn new(
|
||||||
|
gl: &'a glow::Context,
|
||||||
|
vao: NativeVertexArray,
|
||||||
|
vbo: NativeBuffer,
|
||||||
|
ebo: Option<NativeBuffer>,
|
||||||
|
len: Option<i32>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
gl,
|
||||||
|
vao: RcGlVertexArray::new(gl, vao),
|
||||||
|
vbo: RcGlBuffer::new(gl, vbo),
|
||||||
|
ebo: ebo.map(|ebo| RcGlBuffer::new(gl, ebo)),
|
||||||
|
len: len.unwrap_or(0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bind_data(&mut self, vbo: &Vec<f32>, ebo: Option<&Vec<u32>>, len: i32) {
|
||||||
|
use bytemuck::cast_slice;
|
||||||
|
self.vao.bind(glow::VERTEX_ARRAY);
|
||||||
|
unsafe {
|
||||||
|
self.gl
|
||||||
|
.buffer_data_u8_slice(glow::ARRAY_BUFFER, cast_slice(&vbo), glow::STATIC_DRAW);
|
||||||
|
if let Some(ebo) = ebo {
|
||||||
|
self.ebo.as_ref().unwrap().bind(glow::ELEMENT_ARRAY_BUFFER);
|
||||||
|
self.gl.buffer_data_u8_slice(
|
||||||
|
glow::ELEMENT_ARRAY_BUFFER,
|
||||||
|
cast_slice(&ebo),
|
||||||
|
glow::STATIC_DRAW,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.vao.unbind(glow::VERTEX_ARRAY);
|
||||||
|
self.len = len;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn len(&self) -> i32 {
|
||||||
|
self.len
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Module: Sized {
|
||||||
|
type Cursor: ModuleCursor;
|
||||||
|
type Data;
|
||||||
|
type InitInfo;
|
||||||
|
|
||||||
|
fn render(&mut self, cursor: &mut Self::Cursor) -> Result<()>;
|
||||||
|
fn resize(&self, size: [f32; 2]);
|
||||||
|
fn destroy(&self);
|
||||||
|
|
||||||
|
fn start(&mut self);
|
||||||
|
fn init(&mut self, info: &Self::InitInfo) -> Result<()>;
|
||||||
|
fn end(&mut self);
|
||||||
|
fn load_data<'dt>(&self, data: &CachedData<Self::Data>) -> Result<Self::Cursor>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ModuleData: Sized {
|
||||||
|
fn to_data(&self) -> Data;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ModuleCursor {
|
||||||
|
type Module: Module;
|
||||||
|
type Data: ModuleData;
|
||||||
|
type Config;
|
||||||
|
|
||||||
|
fn set_config<F>(&mut self, f: F)
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut Self::Config);
|
||||||
|
|
||||||
|
fn ui_build(&mut self, ui: &imgui::Ui);
|
||||||
|
}
|
||||||
159
src/pg/modules/ppi.rs
Normal file
159
src/pg/modules/ppi.rs
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
use std::rc::Rc;
|
||||||
|
use tracker::track;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
data_loader::Data,
|
||||||
|
errors::*,
|
||||||
|
font_manager::FontManager,
|
||||||
|
ui::typ,
|
||||||
|
utils::{cache::CachedData, resources::ManagedResource},
|
||||||
|
};
|
||||||
|
use imgui::VerticalSlider;
|
||||||
|
use ppi::{PPIConfig, PPI};
|
||||||
|
use threed::ThreeD;
|
||||||
|
|
||||||
|
use crate::graphics::{font::Text, *};
|
||||||
|
|
||||||
|
use super::{Attach, Module, ModuleCursor};
|
||||||
|
pub struct PPIModule<'b, 'a: 'b> {
|
||||||
|
gl: &'a glow::Context,
|
||||||
|
ppi_program: &'b mut PPI,
|
||||||
|
text_program: &'b mut Text<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'b, 'a: 'b> PPIModule<'b, 'a> {
|
||||||
|
pub fn new(gl: &'a glow::Context, ppi: &'b mut PPI, text: &'b mut Text<'a>) -> Result<Self> {
|
||||||
|
let config = PPIConfig::default();
|
||||||
|
Ok(Self {
|
||||||
|
gl,
|
||||||
|
ppi_program: ppi,
|
||||||
|
text_program: text,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rebind(&self, attach: &mut Attach, data: &Data, config: &PPIModuleConfig) -> Result<()> {
|
||||||
|
let (vbo, ebo, len) = self.ppi_program.bake(
|
||||||
|
data,
|
||||||
|
&PPIConfig {
|
||||||
|
layer: config.layer,
|
||||||
|
rdpi: config.rdpi,
|
||||||
|
adpi: config.adpi,
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
attach.bind_data(&vbo, ebo.as_ref(), len);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'b, 'a: 'b> Module for PPIModule<'b, 'a> {
|
||||||
|
type Cursor = PPIPackage<'a>;
|
||||||
|
type Data = Data;
|
||||||
|
type InitInfo = ThreeD;
|
||||||
|
|
||||||
|
fn start(&mut self) {
|
||||||
|
self.ppi_program.mount(&self.gl).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn end(&mut self) {
|
||||||
|
self.ppi_program.unmount(&self.gl).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render<'dt>(&mut self, cursor: &mut Self::Cursor) -> Result<()> {
|
||||||
|
let attach = &mut cursor.ppi_attach;
|
||||||
|
let data = &cursor.ppi_data.borrow();
|
||||||
|
let config = &mut cursor.ppi_config;
|
||||||
|
self.ppi_program.set_config(
|
||||||
|
&self.gl,
|
||||||
|
&PPIConfig {
|
||||||
|
layer: config.layer,
|
||||||
|
rdpi: config.rdpi,
|
||||||
|
adpi: config.adpi,
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
if config.changed_layer() {
|
||||||
|
self.rebind(attach, data, config);
|
||||||
|
config.reset();
|
||||||
|
}
|
||||||
|
attach.vao.bind(glow::VERTEX_ARRAY);
|
||||||
|
self.ppi_program.draw(&self.gl, attach.len())?;
|
||||||
|
attach.vao.unbind(glow::VERTEX_ARRAY);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resize(&self, size: [f32; 2]) {}
|
||||||
|
|
||||||
|
fn destroy(&self) {}
|
||||||
|
|
||||||
|
fn load_data<'dt>(&self, data: &CachedData<Self::Data>) -> Result<Self::Cursor> {
|
||||||
|
let _data = data.borrow();
|
||||||
|
if _data.blocks.len() == 0 {
|
||||||
|
return Err(Error::InvalidDataType);
|
||||||
|
}
|
||||||
|
let (vao, vbo, ebo) = self.ppi_program.init(&self.gl);
|
||||||
|
let mut attach = Attach::new(&self.gl, vao, vbo, ebo, None);
|
||||||
|
let (r, a, t) = self.ppi_program.data_info(&_data)?;
|
||||||
|
let config = PPIModuleConfig {
|
||||||
|
layer: 0,
|
||||||
|
rdpi: r,
|
||||||
|
adpi: a,
|
||||||
|
tracker: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.rebind(&mut attach, &data.borrow(), &config);
|
||||||
|
|
||||||
|
Ok(PPIPackage::new(config, attach, data))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(&mut self, info: &Self::InitInfo) -> Result<()> {
|
||||||
|
info.attach_with_program(&self.gl, &self.ppi_program.program());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[track]
|
||||||
|
pub struct PPIModuleConfig {
|
||||||
|
pub layer: usize,
|
||||||
|
#[do_not_track]
|
||||||
|
pub rdpi: f32,
|
||||||
|
#[do_not_track]
|
||||||
|
pub adpi: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PPIPackage<'gl> {
|
||||||
|
ppi_config: PPIModuleConfig,
|
||||||
|
ppi_attach: Attach<'gl>,
|
||||||
|
ppi_data: CachedData<Data>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'gl> PPIPackage<'gl> {
|
||||||
|
fn new(ppi_config: PPIModuleConfig, ppi_attach: Attach<'gl>, data: &CachedData<Data>) -> Self {
|
||||||
|
Self {
|
||||||
|
ppi_config,
|
||||||
|
ppi_attach,
|
||||||
|
ppi_data: Rc::clone(data),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'gl> ModuleCursor for PPIPackage<'gl> {
|
||||||
|
type Module = PPIModule<'gl, 'gl>;
|
||||||
|
type Data = Data;
|
||||||
|
type Config = PPIModuleConfig;
|
||||||
|
|
||||||
|
fn set_config<F>(&mut self, f: F)
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut Self::Config),
|
||||||
|
{
|
||||||
|
f(&mut self.ppi_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ui_build(&mut self, ui: &imgui::Ui) {
|
||||||
|
let mut layer = self.ppi_config.layer;
|
||||||
|
ui.text("PPI Data Config");
|
||||||
|
ui.slider("Layer", 0, 10, &mut layer);
|
||||||
|
ui.separator();
|
||||||
|
|
||||||
|
self.ppi_config.set_layer(layer);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,13 +1,5 @@
|
|||||||
use crate::pg::App;
|
use crate::{pg::App, utils::resources::GL};
|
||||||
use crate::{
|
|
||||||
graphics::{
|
|
||||||
collections::agg_fast_path::AggFastPath,
|
|
||||||
transforms::{
|
|
||||||
polar::Polar, position::Position, trackball::Trackball, viewport::Viewport, Transform,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
utils::Triangler,
|
|
||||||
};
|
|
||||||
use glow::HasContext;
|
use glow::HasContext;
|
||||||
use glutin::{
|
use glutin::{
|
||||||
config::ConfigTemplateBuilder,
|
config::ConfigTemplateBuilder,
|
||||||
@ -15,24 +7,21 @@ use glutin::{
|
|||||||
display::{GetGlDisplay, GlDisplay},
|
display::{GetGlDisplay, GlDisplay},
|
||||||
surface::{GlSurface, Surface, SurfaceAttributesBuilder, WindowSurface},
|
surface::{GlSurface, Surface, SurfaceAttributesBuilder, WindowSurface},
|
||||||
};
|
};
|
||||||
use imgui::Ui;
|
|
||||||
use imgui_winit_support::{
|
use imgui_winit_support::{
|
||||||
winit::{
|
winit::{
|
||||||
dpi::{LogicalSize, PhysicalPosition},
|
dpi::LogicalSize,
|
||||||
event_loop::EventLoop,
|
event_loop::EventLoop,
|
||||||
window::{Window, WindowBuilder},
|
window::{Window, WindowBuilder},
|
||||||
},
|
},
|
||||||
HiDpiMode, WinitPlatform,
|
HiDpiMode, WinitPlatform,
|
||||||
};
|
};
|
||||||
use nalgebra_glm::perspective;
|
|
||||||
use nalgebra_glm::Vec3;
|
|
||||||
use raw_window_handle::HasRawWindowHandle;
|
use raw_window_handle::HasRawWindowHandle;
|
||||||
use std::num::NonZeroU32;
|
use std::num::NonZeroU32;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
pub fn run<F>(mut app: F)
|
pub fn run<F>(mut app: F)
|
||||||
where
|
where
|
||||||
F: for<'b> FnMut(&'b glow::Context) -> App<'b>,
|
F: for<'b> FnMut(&'b GL) -> App<'b>,
|
||||||
{
|
{
|
||||||
// Create Window
|
// Create Window
|
||||||
let (event_loop, window, surface, context) = create_window();
|
let (event_loop, window, surface, context) = create_window();
|
||||||
@ -47,10 +36,10 @@ where
|
|||||||
|
|
||||||
let gl_app = ig_renderer.gl_context().clone();
|
let gl_app = ig_renderer.gl_context().clone();
|
||||||
let gl_context = ig_renderer.gl_context().clone();
|
let gl_context = ig_renderer.gl_context().clone();
|
||||||
|
let gl = GL::new(gl_app);
|
||||||
|
|
||||||
// let mut run = true;
|
// App instance
|
||||||
let mut app = app(&gl_app);
|
let mut app = app(&gl);
|
||||||
|
|
||||||
let mut run = true;
|
let mut run = true;
|
||||||
|
|
||||||
// Prepare
|
// Prepare
|
||||||
@ -82,10 +71,14 @@ where
|
|||||||
// Render your custom scene, note we need to borrow the OpenGL
|
// Render your custom scene, note we need to borrow the OpenGL
|
||||||
// context from the `AutoRenderer`, which takes ownership of it.
|
// context from the `AutoRenderer`, which takes ownership of it.
|
||||||
unsafe {
|
unsafe {
|
||||||
gl_context.clear(glow::COLOR_BUFFER_BIT | glow::DEPTH_BUFFER_BIT);
|
gl_context.clear_color(0.0, 0.0, 0.0, 1.0);
|
||||||
|
gl_context.clear(glow::COLOR_BUFFER_BIT);
|
||||||
|
gl_context.clear(glow::COLOR_BUFFER_BIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Render Offscreen
|
||||||
app.render();
|
app.render();
|
||||||
|
|
||||||
let ui = imgui_context.frame();
|
let ui = imgui_context.frame();
|
||||||
app.render_ui(&ui, &window, &mut run);
|
app.render_ui(&ui, &window, &mut run);
|
||||||
|
|
||||||
@ -120,6 +113,7 @@ where
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
winit_platform.handle_event(imgui_context.io_mut(), &window, &event);
|
winit_platform.handle_event(imgui_context.io_mut(), &window, &event);
|
||||||
|
app.resize([new_size.width as f32, new_size.height as f32]);
|
||||||
}
|
}
|
||||||
winit::event::Event::LoopExiting => {
|
winit::event::Event::LoopExiting => {
|
||||||
app.destroy();
|
app.destroy();
|
||||||
@ -142,6 +136,7 @@ fn create_window() -> (
|
|||||||
|
|
||||||
let window_builder = WindowBuilder::new()
|
let window_builder = WindowBuilder::new()
|
||||||
.with_title("TEST")
|
.with_title("TEST")
|
||||||
|
.with_maximized(true)
|
||||||
.with_inner_size(LogicalSize::new(1024, 768));
|
.with_inner_size(LogicalSize::new(1024, 768));
|
||||||
|
|
||||||
let (window, cfg) = glutin_winit::DisplayBuilder::new()
|
let (window, cfg) = glutin_winit::DisplayBuilder::new()
|
||||||
@ -211,7 +206,9 @@ fn imgui_init(window: &Window) -> (WinitPlatform, imgui::Context) {
|
|||||||
.fonts()
|
.fonts()
|
||||||
.add_font(&[imgui::FontSource::DefaultFontData { config: None }]);
|
.add_font(&[imgui::FontSource::DefaultFontData { config: None }]);
|
||||||
|
|
||||||
imgui_context.io_mut().font_global_scale = (1.0 / winit_platform.hidpi_factor()) as f32;
|
println!("imgui global scale: {}", winit_platform.hidpi_factor());
|
||||||
|
|
||||||
|
imgui_context.io_mut().font_global_scale = (1.0 / 1.5) as f32;
|
||||||
|
|
||||||
(winit_platform, imgui_context)
|
(winit_platform, imgui_context)
|
||||||
}
|
}
|
||||||
|
|||||||
45
src/ui.rs
45
src/ui.rs
@ -1,45 +0,0 @@
|
|||||||
use crate::errors::*;
|
|
||||||
use crate::graphics::threed::ThreeD;
|
|
||||||
use crate::graphics::Config;
|
|
||||||
use crate::pg::App;
|
|
||||||
use crate::{data_loader::Data, pg::WindowData};
|
|
||||||
use glow::NativeTexture;
|
|
||||||
use imgui::{ImString, Ui};
|
|
||||||
|
|
||||||
pub fn base(ui: &Ui, window: &winit::window::Window, run: &mut bool, app: &mut App) {
|
|
||||||
ui.window("test")
|
|
||||||
.size([300.0, 200.0], imgui::Condition::FirstUseEver)
|
|
||||||
.build(|| {
|
|
||||||
if ui.button("PPI") {
|
|
||||||
let data =
|
|
||||||
load_data(r#"C:\Users\qwin7\Downloads\ZJSXAA_20230113070200_R.dat.gz"#).unwrap();
|
|
||||||
app.create_render_window("ppi", [300.0, 300.0]).unwrap();
|
|
||||||
app.create_ppi_render("ppi", None);
|
|
||||||
app.bind_data("ppi", &data).unwrap();
|
|
||||||
|
|
||||||
let (rdpi, adpi, _) = {
|
|
||||||
let ppi = app.ppi_module.borrow_mut();
|
|
||||||
ppi.data_info(&data).unwrap()
|
|
||||||
};
|
|
||||||
|
|
||||||
app.set_config("ppi").map(|config| {
|
|
||||||
if let Config::PPI(config) = config {
|
|
||||||
config.rdpi = rdpi;
|
|
||||||
config.adpi = adpi;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
app.show_window(ui);
|
|
||||||
app.destroy_window();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_display_window(title: &str, size: [f32; 2], copy_from: Option<ImString>) -> WindowData {
|
|
||||||
WindowData::new(ImString::new(title), size, Some(ThreeD::default().into()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_data(path: impl AsRef<std::path::Path>) -> Result<Data> {
|
|
||||||
let data = Data::from_path(path)?;
|
|
||||||
Ok(data)
|
|
||||||
}
|
|
||||||
43
src/ui/io.rs
Normal file
43
src/ui/io.rs
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
use imgui::Ui;
|
||||||
|
|
||||||
|
pub struct MouseIO {
|
||||||
|
pub position: [f32; 2], // 鼠标当前位置
|
||||||
|
pub drag_delta: Option<[f32; 2]>, // 拖动开始时的鼠标位置
|
||||||
|
pub is_dragging: bool, // 是否正在拖动
|
||||||
|
pub left_button_pressed: bool, // 左键是否被按下
|
||||||
|
pub right_button_pressed: bool, // 右键是否被按下
|
||||||
|
pub wheel_delta: f32, // 鼠标滚轮变化值
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct KeyboardIO {
|
||||||
|
pub keys: [bool; 512], // 键盘按键状态
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct IO {
|
||||||
|
pub mouse: MouseIO,
|
||||||
|
pub keyboard: KeyboardIO,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IO {
|
||||||
|
pub fn new(ui: &Ui) -> Self {
|
||||||
|
let io = ui.io();
|
||||||
|
|
||||||
|
let delta = if ui.is_mouse_dragging(imgui::MouseButton::Left) {
|
||||||
|
Some(ui.mouse_drag_delta_with_button(imgui::MouseButton::Left))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
IO {
|
||||||
|
mouse: MouseIO {
|
||||||
|
position: io.mouse_pos,
|
||||||
|
drag_delta: delta,
|
||||||
|
is_dragging: ui.is_mouse_dragging(imgui::MouseButton::Left),
|
||||||
|
left_button_pressed: io.mouse_down[0],
|
||||||
|
right_button_pressed: io.mouse_down[1],
|
||||||
|
wheel_delta: io.mouse_wheel,
|
||||||
|
},
|
||||||
|
keyboard: KeyboardIO { keys: [false; 512] },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
165
src/ui/layout.rs
Normal file
165
src/ui/layout.rs
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
use imgui::{Condition, Ui};
|
||||||
|
|
||||||
|
pub struct Layout {
|
||||||
|
size: [f32; 2],
|
||||||
|
origin: [f32; 2],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Layout {
|
||||||
|
pub(super) fn new() -> Self {
|
||||||
|
Layout {
|
||||||
|
size: [0.0, 0.0],
|
||||||
|
origin: [0.0, 0.0],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn set_size(&mut self, size: [f32; 2]) {
|
||||||
|
self.size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn set_origin(&mut self, origin: [f32; 2]) {
|
||||||
|
self.origin = origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn grid(&self, cols: usize, rows: usize, gap: [f32; 2]) -> GridLayout {
|
||||||
|
GridLayout::new(self.size, self.origin, cols, rows, gap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Size {
|
||||||
|
Percent(f32),
|
||||||
|
Fixed(f32),
|
||||||
|
Fill,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! size {
|
||||||
|
($float:literal %) => {
|
||||||
|
Size::Percent($float)
|
||||||
|
};
|
||||||
|
|
||||||
|
($float:literal px) => {
|
||||||
|
Size::Fixed($float)
|
||||||
|
};
|
||||||
|
|
||||||
|
(fill) => {
|
||||||
|
Size::Fill
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct GridLayout {
|
||||||
|
size: [f32; 2],
|
||||||
|
origin: [f32; 2],
|
||||||
|
cols: usize,
|
||||||
|
rows: usize,
|
||||||
|
gap: [f32; 2],
|
||||||
|
cursor: Vec<[f32; 2]>,
|
||||||
|
widths: Vec<f32>,
|
||||||
|
hgts: Vec<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GridLayout {
|
||||||
|
fn new(size: [f32; 2], origin: [f32; 2], cols: usize, rows: usize, gap: [f32; 2]) -> Self {
|
||||||
|
GridLayout {
|
||||||
|
size,
|
||||||
|
origin,
|
||||||
|
cols,
|
||||||
|
rows,
|
||||||
|
gap,
|
||||||
|
cursor: vec![[0.0, 0.0]; cols * rows],
|
||||||
|
widths: vec![0.0; cols],
|
||||||
|
hgts: vec![0.0; rows],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build(&mut self, sizes: &[Size], idx: usize) {
|
||||||
|
let mut start = 0.0;
|
||||||
|
let s = sizes
|
||||||
|
.iter()
|
||||||
|
.filter(|v| !matches!(v, Size::Fill))
|
||||||
|
.fold(0.0, |start, v| match v {
|
||||||
|
Size::Percent(p) => start + self.size[idx] * p / 100.0,
|
||||||
|
Size::Fixed(f) => start + *f,
|
||||||
|
_ => start,
|
||||||
|
});
|
||||||
|
|
||||||
|
let fill = self.size[idx] - s;
|
||||||
|
|
||||||
|
for (i, v) in sizes.iter().enumerate() {
|
||||||
|
let hgt = match v {
|
||||||
|
Size::Percent(p) => self.size[idx] * p / 100.0,
|
||||||
|
Size::Fixed(f) => *f,
|
||||||
|
Size::Fill => fill,
|
||||||
|
};
|
||||||
|
|
||||||
|
if idx == 0 {
|
||||||
|
for j in 0..self.rows {
|
||||||
|
self.cursor[j * self.cols + i][0] = start;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.widths[i] = hgt;
|
||||||
|
} else {
|
||||||
|
self.cursor[i * self.cols..(i + 1) * self.cols]
|
||||||
|
.iter_mut()
|
||||||
|
.for_each(|v| {
|
||||||
|
v[1] = start;
|
||||||
|
});
|
||||||
|
|
||||||
|
self.hgts[i] = hgt;
|
||||||
|
}
|
||||||
|
|
||||||
|
start += hgt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn set_row_size(&mut self, sizes: &[Size]) {
|
||||||
|
assert_eq!(sizes.len(), self.rows);
|
||||||
|
assert!(sizes.iter().filter(|v| matches!(v, Size::Fill)).count() <= 1);
|
||||||
|
self.build(sizes, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn set_col_size(&mut self, sizes: &[Size]) {
|
||||||
|
assert_eq!(sizes.len(), self.cols);
|
||||||
|
assert!(sizes.iter().filter(|v| matches!(v, Size::Fill)).count() <= 1);
|
||||||
|
self.build(sizes, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn with(&mut self, row: usize, col: usize, span: [usize; 2]) -> GridBlock {
|
||||||
|
let curr = self.cursor[row * self.cols + col];
|
||||||
|
let width: f32 = self.widths.iter().skip(col).take(span[1]).sum();
|
||||||
|
let hgt: f32 = self.hgts.iter().skip(row).take(span[0]).sum();
|
||||||
|
|
||||||
|
GridBlock {
|
||||||
|
size: [width, hgt],
|
||||||
|
origin: [curr[0] + self.origin[0], curr[1] + self.origin[1]],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct GridBlock {
|
||||||
|
size: [f32; 2],
|
||||||
|
origin: [f32; 2],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GridBlock {
|
||||||
|
fn pos(&self, x: f32, y: f32) -> [f32; 2] {
|
||||||
|
[self.origin[0] + x, self.origin[1] + y]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build<F>(&self, ui: &Ui, f: F)
|
||||||
|
where
|
||||||
|
F: FnOnce(&Ui, [f32; 2], [f32; 2]),
|
||||||
|
{
|
||||||
|
f(ui, self.size, self.origin);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start_window<'a, Label: AsRef<str>>(
|
||||||
|
&self,
|
||||||
|
ui: &'a Ui,
|
||||||
|
title: Label,
|
||||||
|
) -> imgui::Window<'a, 'a, Label> {
|
||||||
|
ui.window(title)
|
||||||
|
.size(self.size, Condition::Always)
|
||||||
|
.position(self.origin, Condition::Always)
|
||||||
|
}
|
||||||
|
}
|
||||||
102
src/ui/mod.rs
Normal file
102
src/ui/mod.rs
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use log::*;
|
||||||
|
|
||||||
|
use crate::data_loader::Data;
|
||||||
|
use crate::errors::*;
|
||||||
|
use crate::pg::app_type::AppType;
|
||||||
|
use crate::pg::{ModulePackage, Programs};
|
||||||
|
use crate::utils::cache::Cache;
|
||||||
|
use crate::utils::resources::{RcGlRcFramebuffer, RcGlRcRenderbuffer, RcGlRcResource, GL};
|
||||||
|
use glow::HasContext;
|
||||||
|
use glow::{NativeFramebuffer, NativeRenderbuffer};
|
||||||
|
use imgui::*;
|
||||||
|
#[macro_use]
|
||||||
|
mod layout;
|
||||||
|
pub mod io;
|
||||||
|
mod state;
|
||||||
|
pub mod typ;
|
||||||
|
pub use layout::*;
|
||||||
|
pub use state::State;
|
||||||
|
|
||||||
|
pub struct GUI {
|
||||||
|
state: State,
|
||||||
|
layout: layout::Layout,
|
||||||
|
renderer_size: [f32; 2],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GUI {
|
||||||
|
pub fn new(state: State) -> Self {
|
||||||
|
GUI {
|
||||||
|
state,
|
||||||
|
layout: layout::Layout::new(),
|
||||||
|
renderer_size: [0.0, 0.0],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn apptype<'a, 'gl>(&'a mut self, gl: &'gl GL) -> AppType<'gl> {
|
||||||
|
self.state.app_type.init_apptype(gl)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render<'b, 'gl>(
|
||||||
|
&mut self,
|
||||||
|
ui: &Ui,
|
||||||
|
context: &'b mut crate::pg::Context<'gl>,
|
||||||
|
app_type: &'b mut AppType<'gl>,
|
||||||
|
) -> bool {
|
||||||
|
// Menu bar
|
||||||
|
self.menu_bar(ui, context, app_type);
|
||||||
|
let menu_bar_height = ui.frame_height();
|
||||||
|
let display_size = ui.io().display_size;
|
||||||
|
|
||||||
|
// Layout Size Reset
|
||||||
|
self.layout.set_origin([0.0, menu_bar_height]);
|
||||||
|
self.layout
|
||||||
|
.set_size([display_size[0], display_size[1] - menu_bar_height]);
|
||||||
|
|
||||||
|
// Render
|
||||||
|
self.state
|
||||||
|
.app_type
|
||||||
|
.render(ui, context, &mut self.layout, app_type);
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn menu_bar<'b, 'gl>(
|
||||||
|
&self,
|
||||||
|
ui: &Ui,
|
||||||
|
context: &'b mut crate::pg::Context<'gl>,
|
||||||
|
app_type: &mut AppType<'gl>,
|
||||||
|
) {
|
||||||
|
if let Some(top_menu) = ui.begin_main_menu_bar() {
|
||||||
|
if let Some(menu) = ui.begin_menu("File") {
|
||||||
|
if ui.menu_item("Open") {
|
||||||
|
match tinyfiledialogs::open_file_dialog("Open", "password.txt", None) {
|
||||||
|
Some(file) => match context.load_data(file.clone()) {
|
||||||
|
Ok(data) => {
|
||||||
|
info!("Data loaded: {:?}", file);
|
||||||
|
app_type.deal_with_cursor(data);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("Failed to get or insert data: {:?}", e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
error!("No file selected");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ui.menu_item("Save") {
|
||||||
|
println!("Save clicked");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ui.menu_item("Exit") {}
|
||||||
|
|
||||||
|
menu.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
top_menu.end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
60
src/ui/state.rs
Normal file
60
src/ui/state.rs
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
use super::typ::{LayoutMod, MainLoad};
|
||||||
|
use crate::pg::ModulePackage;
|
||||||
|
use crate::utils::resources::GL;
|
||||||
|
|
||||||
|
macro_rules! uit {
|
||||||
|
($s:ident,$({$b: tt => $exp:ident($($gl: ident),+)},)*) => {
|
||||||
|
{
|
||||||
|
match $s {
|
||||||
|
$(
|
||||||
|
Self::$b(v) => v.$exp($($gl),+),
|
||||||
|
)*
|
||||||
|
_ => {
|
||||||
|
panic!("UiType not found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct State {
|
||||||
|
pub example: u32,
|
||||||
|
pub i32_value: i32,
|
||||||
|
pub u8_value: u8,
|
||||||
|
pub f32_value: f32,
|
||||||
|
pub f64_formatted: f64,
|
||||||
|
pub array: [u8; 4],
|
||||||
|
pub render_closable: bool,
|
||||||
|
pub notify_text: &'static str,
|
||||||
|
pub app_type: UiType,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum UiType {
|
||||||
|
Mainload(MainLoad),
|
||||||
|
Other,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UiType {
|
||||||
|
pub(super) fn render<'b, 'gl: 'b>(
|
||||||
|
&mut self,
|
||||||
|
ui: &imgui::Ui,
|
||||||
|
context: &mut crate::pg::Context<'gl>,
|
||||||
|
layout: &mut crate::ui::layout::Layout,
|
||||||
|
app_typ: &mut crate::pg::app_type::AppType<'gl>,
|
||||||
|
) {
|
||||||
|
let gl = context.gl;
|
||||||
|
let app_typ = app_typ.into();
|
||||||
|
uit!(self, {Mainload => render(ui,context,layout,app_typ)},);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init_apptype<'gl>(&mut self, gl: &'gl GL) -> crate::pg::app_type::AppType<'gl> {
|
||||||
|
uit!(self, {Mainload => init(gl)},).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for UiType {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Mainload(MainLoad::new())
|
||||||
|
}
|
||||||
|
}
|
||||||
158
src/ui/typ/main_load.rs
Normal file
158
src/ui/typ/main_load.rs
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
use super::{CameraOP, Layout, LayoutAppType, LayoutMod, Size};
|
||||||
|
use crate::graphics::threed::ThreeD;
|
||||||
|
use crate::graphics::{AttaWithProgram, AttachWithMouse, MouseState};
|
||||||
|
use crate::pg::ModulePackage;
|
||||||
|
use crate::pg::{_ModulePackage, app_type::ViewPort};
|
||||||
|
use crate::ui::io::IO;
|
||||||
|
use crate::utils::resources::GL;
|
||||||
|
use glow::HasContext;
|
||||||
|
use imgui::{Condition, Ui, WindowFlags};
|
||||||
|
|
||||||
|
pub struct MainLoad {}
|
||||||
|
|
||||||
|
impl LayoutMod for MainLoad {
|
||||||
|
type AppType<'gl> = MainLoadTyp<'gl>;
|
||||||
|
fn render<'b, 'gl>(
|
||||||
|
&mut self,
|
||||||
|
ui: &imgui::Ui,
|
||||||
|
context: &mut crate::pg::Context<'gl>,
|
||||||
|
layout: &mut Layout,
|
||||||
|
apptyp: &mut Self::AppType<'gl>,
|
||||||
|
) where
|
||||||
|
'gl: 'b,
|
||||||
|
{
|
||||||
|
let mut grid = layout.grid(2, 1, [0.0, 0.0]);
|
||||||
|
grid.set_col_size(&[size!(20.0%), size!(fill)]);
|
||||||
|
grid.set_row_size(&[size!(fill)]);
|
||||||
|
let io = ui.io();
|
||||||
|
|
||||||
|
let window_flag = WindowFlags::empty()
|
||||||
|
| WindowFlags::NO_MOVE
|
||||||
|
| WindowFlags::NO_RESIZE
|
||||||
|
| WindowFlags::NO_COLLAPSE
|
||||||
|
| WindowFlags::NO_TITLE_BAR;
|
||||||
|
|
||||||
|
grid.with(0, 0, [1, 1])
|
||||||
|
.start_window(ui, "MainLoad")
|
||||||
|
.build(|| {
|
||||||
|
for package in apptyp.packages.iter_mut() {
|
||||||
|
package.ui_build(ui);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
grid.with(0, 1, [1, 1])
|
||||||
|
.build(ui, |ui, window_size, origin| {
|
||||||
|
let window_size = [window_size[0], window_size[0] / 16.0 * 9.0];
|
||||||
|
ui.window("Renderer")
|
||||||
|
.size(window_size, Condition::Always)
|
||||||
|
.flags(window_flag)
|
||||||
|
.position(origin, Condition::Always)
|
||||||
|
.build(|| {
|
||||||
|
let gl = context.gl.gl_rc();
|
||||||
|
let scale = ui.io().display_framebuffer_scale;
|
||||||
|
let pos = ui.cursor_screen_pos();
|
||||||
|
let dsp_size = ui.io().display_size;
|
||||||
|
|
||||||
|
let content_size = ui.content_region_avail();
|
||||||
|
apptyp.main_viewport.set_size(content_size);
|
||||||
|
|
||||||
|
// ui.set_cursor_screen_pos([pos[0] + size[0], pos[1] + size[1]]);
|
||||||
|
let draws = ui.get_window_draw_list();
|
||||||
|
let fbo = apptyp.main_viewport.fbo();
|
||||||
|
let fbo = fbo.native();
|
||||||
|
|
||||||
|
draws
|
||||||
|
.add_callback({
|
||||||
|
move || unsafe {
|
||||||
|
let x = pos[0] * scale[0];
|
||||||
|
let y = (dsp_size[1] - pos[1]) * scale[1];
|
||||||
|
|
||||||
|
// gl.enable(glow::SCISSOR_TEST);
|
||||||
|
// gl.scissor(0, 0, size[0] as i32, size[1] as i32);
|
||||||
|
gl.bind_framebuffer(glow::READ_FRAMEBUFFER, Some(fbo));
|
||||||
|
gl.blit_framebuffer(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
(content_size[0]) as i32,
|
||||||
|
(content_size[1]) as i32,
|
||||||
|
x as i32,
|
||||||
|
y as i32,
|
||||||
|
(x + content_size[0] * scale[0]) as i32,
|
||||||
|
(y - content_size[1] * scale[1]) as i32,
|
||||||
|
glow::COLOR_BUFFER_BIT,
|
||||||
|
glow::NEAREST,
|
||||||
|
);
|
||||||
|
gl.bind_framebuffer(glow::READ_FRAMEBUFFER, None);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.build();
|
||||||
|
|
||||||
|
if ui.is_window_hovered() {
|
||||||
|
let cio = IO::new(ui);
|
||||||
|
apptyp.deal_with_camera(&cio);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init<'gl>(&mut self, gl: &'gl GL) -> Self::AppType<'gl> {
|
||||||
|
let typ = MainLoadTyp {
|
||||||
|
main_viewport: ViewPort::new(gl),
|
||||||
|
packages: Vec::new(),
|
||||||
|
camera_op: ThreeD::default(),
|
||||||
|
};
|
||||||
|
typ
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MainLoad {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
MainLoad {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_window(&mut self, ui: &Ui, context: &mut crate::pg::Context) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MainLoadTyp<'gl> {
|
||||||
|
main_viewport: ViewPort,
|
||||||
|
camera_op: ThreeD,
|
||||||
|
packages: Vec<ModulePackage<'gl>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CameraOP for MouseState {
|
||||||
|
fn from_context(context: &IO) -> Self {
|
||||||
|
if context.mouse.is_dragging {
|
||||||
|
Self::Drag {
|
||||||
|
from: context.mouse.position,
|
||||||
|
delta: context.mouse.drag_delta.unwrap(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Self::None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'gl> LayoutAppType<'gl> for MainLoadTyp<'gl> {
|
||||||
|
type CameraOP = MouseState;
|
||||||
|
fn deal_with_cursor(&mut self, package: ModulePackage<'gl>) {
|
||||||
|
self.packages.push(package);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_program(
|
||||||
|
&mut self,
|
||||||
|
gl: &glow::Context,
|
||||||
|
programs: &mut crate::pg::Programs<'gl>,
|
||||||
|
) -> crate::errors::Result<()> {
|
||||||
|
self.main_viewport.bind(gl);
|
||||||
|
for package in self.packages.iter_mut() {
|
||||||
|
programs.draw_modules(package, &self.camera_op)?;
|
||||||
|
}
|
||||||
|
self.main_viewport.unbind();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _deal_with_camera(&mut self, camera_op: Self::CameraOP) {
|
||||||
|
// println!("Camera Op: {:?}", camera_op);
|
||||||
|
self.camera_op.attach_with_mouse(&camera_op);
|
||||||
|
}
|
||||||
|
}
|
||||||
49
src/ui/typ/mod.rs
Normal file
49
src/ui/typ/mod.rs
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
use super::io::IO;
|
||||||
|
use super::layout::{Layout, Size};
|
||||||
|
mod main_load;
|
||||||
|
use crate::pg::app_type::AppType;
|
||||||
|
use crate::pg::{self, ModuleCursor, ModulePackage, Programs};
|
||||||
|
use crate::utils::resources::GL;
|
||||||
|
pub use main_load::*;
|
||||||
|
|
||||||
|
pub trait LayoutMod {
|
||||||
|
type AppType<'gl>: Into<AppType<'gl>> + LayoutAppType<'gl>;
|
||||||
|
fn render<'dt, 'b, 'gl>(
|
||||||
|
&mut self,
|
||||||
|
ui: &imgui::Ui,
|
||||||
|
context: &mut crate::pg::Context<'gl>,
|
||||||
|
layout: &mut Layout,
|
||||||
|
apptyp: &mut Self::AppType<'gl>,
|
||||||
|
) where
|
||||||
|
'gl: 'b;
|
||||||
|
fn init<'dt, 'gl>(&mut self, gl: &'gl GL) -> Self::AppType<'gl>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait LayoutAppType<'gl> {
|
||||||
|
type CameraOP: CameraOP + Debug;
|
||||||
|
fn deal_with_cursor(&mut self, package: ModulePackage<'gl>);
|
||||||
|
|
||||||
|
fn deal_with_camera(&mut self, camera_op: &IO) {
|
||||||
|
self._deal_with_camera(CameraOP::from_context(camera_op));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _deal_with_camera(&mut self, camera_op: Self::CameraOP);
|
||||||
|
|
||||||
|
fn draw_program(
|
||||||
|
&mut self,
|
||||||
|
gl: &glow::Context,
|
||||||
|
programs: &mut Programs<'gl>,
|
||||||
|
) -> crate::errors::Result<()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait CameraOP {
|
||||||
|
fn from_context(context: &IO) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CameraOP for () {
|
||||||
|
fn from_context(context: &IO) -> Self {
|
||||||
|
()
|
||||||
|
}
|
||||||
|
}
|
||||||
61
src/utils/cache.rs
Normal file
61
src/utils/cache.rs
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
use crate::errors::*;
|
||||||
|
use std::{cell::RefCell, hash::Hash, num::NonZeroUsize, rc::Rc};
|
||||||
|
|
||||||
|
use lru::LruCache;
|
||||||
|
|
||||||
|
use crate::data_loader::Data;
|
||||||
|
pub struct Cache<K: Hash + Eq, V> {
|
||||||
|
cache: LruCache<K, V>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K: Hash + Eq, V> Cache<K, V> {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
cache: LruCache::new(NonZeroUsize::new(10).unwrap()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn get(&mut self, key: &K) -> Option<&V> {
|
||||||
|
self.cache.get(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_mut(&mut self, key: &K) -> Option<&mut V> {
|
||||||
|
self.cache.get_mut(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K, V> Cache<K, V>
|
||||||
|
where
|
||||||
|
K: AsRef<std::path::Path> + Hash + Eq + Clone,
|
||||||
|
V: CacheData,
|
||||||
|
{
|
||||||
|
pub fn insert(&mut self, key: K) -> Result<()> {
|
||||||
|
let value = V::from_path(&key)?;
|
||||||
|
self.cache.put(key, value);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_or_insert(&mut self, key: K) -> Result<&V> {
|
||||||
|
if !self.cache.contains(&key) {
|
||||||
|
self.insert(key.clone())?;
|
||||||
|
}
|
||||||
|
Ok(self.cache.get(&key).unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type CachedData<T> = Rc<RefCell<T>>;
|
||||||
|
|
||||||
|
pub trait CacheData: Sized {
|
||||||
|
fn from_path(path: impl AsRef<std::path::Path>) -> Result<Self>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CacheData for Data {
|
||||||
|
fn from_path(path: impl AsRef<std::path::Path>) -> Result<Self> {
|
||||||
|
Data::from_path(path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: CacheData> CacheData for CachedData<T> {
|
||||||
|
fn from_path(path: impl AsRef<std::path::Path>) -> Result<Self> {
|
||||||
|
Ok(Rc::new(RefCell::new(T::from_path(path)?)))
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,7 +1,9 @@
|
|||||||
mod parser;
|
mod parser;
|
||||||
|
pub mod resources;
|
||||||
use include_dir::{include_dir, Dir};
|
use include_dir::{include_dir, Dir};
|
||||||
pub use parser::*;
|
pub use parser::*;
|
||||||
use std::{num::NonZeroU32, path::Path};
|
use std::{num::NonZeroU32, path::Path};
|
||||||
|
pub mod cache;
|
||||||
pub mod geo_tools;
|
pub mod geo_tools;
|
||||||
|
|
||||||
use glow::HasContext;
|
use glow::HasContext;
|
||||||
|
|||||||
@ -514,6 +514,9 @@ impl Code {
|
|||||||
Code::PreprocessorDirective(Rc::new(RefCell::new(pre)), Box::new(inner))
|
Code::PreprocessorDirective(Rc::new(RefCell::new(pre)), Box::new(inner))
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
map(terminated(parse_struct, pair(space0, char(';'))), |v| {
|
||||||
|
Code::Struct(Rc::new(RefCell::new(v)))
|
||||||
|
}),
|
||||||
map(parse_macro, |v| Code::Macro(Rc::new(RefCell::new(v)))),
|
map(parse_macro, |v| Code::Macro(Rc::new(RefCell::new(v)))),
|
||||||
map(include, |v| Code::Include(Rc::new(RefCell::new(v)))),
|
map(include, |v| Code::Include(Rc::new(RefCell::new(v)))),
|
||||||
map(function, |v| Code::Function(Rc::new(RefCell::new(v)))),
|
map(function, |v| Code::Function(Rc::new(RefCell::new(v)))),
|
||||||
@ -1037,6 +1040,7 @@ fn parse_struct_member_access(input: &str) -> IResult<&str, Expression> {
|
|||||||
// parse_struct_pointer_access,
|
// parse_struct_pointer_access,
|
||||||
map(hook, Expression::Hook),
|
map(hook, Expression::Hook),
|
||||||
parse_array_access,
|
parse_array_access,
|
||||||
|
parse_function_call,
|
||||||
map(identifier, |v| Expression::Variable(v.to_string())),
|
map(identifier, |v| Expression::Variable(v.to_string())),
|
||||||
)),
|
)),
|
||||||
char('.'),
|
char('.'),
|
||||||
@ -1244,6 +1248,7 @@ pub struct SnippetCode {
|
|||||||
pub includes: Vec<RFC<Include>>,
|
pub includes: Vec<RFC<Include>>,
|
||||||
pub variables: Vec<RFC<ShareVariable>>,
|
pub variables: Vec<RFC<ShareVariable>>,
|
||||||
pub functions: Vec<RFC<Function>>,
|
pub functions: Vec<RFC<Function>>,
|
||||||
|
pub structs: Vec<RFC<Struct>>,
|
||||||
pub preprocessor_directives: HashMap<SnippetPreprocessorKey, Box<SnippetCode>>,
|
pub preprocessor_directives: HashMap<SnippetPreprocessorKey, Box<SnippetCode>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1562,6 +1567,14 @@ impl SnippetCode {
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
let structs = codes
|
||||||
|
.iter()
|
||||||
|
.filter_map(|v| match v {
|
||||||
|
Code::Struct(v) => Some(v.clone()),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
let mut preprocessor_directives = vec![];
|
let mut preprocessor_directives = vec![];
|
||||||
|
|
||||||
for code in codes.into_iter() {
|
for code in codes.into_iter() {
|
||||||
@ -1579,6 +1592,7 @@ impl SnippetCode {
|
|||||||
includes: includes,
|
includes: includes,
|
||||||
variables: variables,
|
variables: variables,
|
||||||
functions: functions,
|
functions: functions,
|
||||||
|
structs: structs,
|
||||||
preprocessor_directives: preprocessor_directives.into_iter().collect(),
|
preprocessor_directives: preprocessor_directives.into_iter().collect(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -1838,60 +1852,173 @@ impl SnippetCode {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test() {
|
fn test() {
|
||||||
let input = r#"
|
let input = r#"
|
||||||
#include "transform/polar.glsl"
|
uniform sampler2D atlas_data;
|
||||||
|
uniform vec2 v_texCoord;
|
||||||
|
|
||||||
layout(points) in;
|
uniform vec4 uClipUV;
|
||||||
layout(triangle_strip, max_vertices = 4) out;
|
uniform vec2 uSdfUV;
|
||||||
|
uniform vec4 uSdfConfig;
|
||||||
|
uniform int uRepeat;
|
||||||
|
uniform int uMode;
|
||||||
|
uniform vec4 uShape;
|
||||||
|
uniform vec4 uRadius;
|
||||||
|
uniform vec4 uBorder;
|
||||||
|
uniform vec4 uStroke;
|
||||||
|
uniform vec4 uFill;
|
||||||
|
|
||||||
// conf: Elevation, Range, Resolution, 0.0
|
float getUVScale(vec2 sdfUV) {
|
||||||
uniform vec4 conf;
|
float dx = dFdx(sdfUV.x);
|
||||||
|
float dy = dFdy(sdfUV.y);
|
||||||
|
return (sqrt(dx * dx + dy * dy) + sqrt(dy * dy + dx * dx)) * 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
out float eth;
|
struct SDF {
|
||||||
out float rng;
|
float outer;
|
||||||
|
float inner;
|
||||||
|
};
|
||||||
|
|
||||||
out vec4 vrange;
|
float getBoxCorner(vec2 xy, vec2 offset) {
|
||||||
|
vec2 clip = xy - offset;
|
||||||
|
return min(clip.x, clip.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
float getCircleCorner(vec2 xy, float r) {
|
||||||
|
vec2 clip = max(vec2(0.0), r - xy);
|
||||||
|
float neg = min(0.0, max(-xy.x, -xy.y) + r);
|
||||||
|
return r - length(clip) - neg;
|
||||||
|
}
|
||||||
|
|
||||||
void main() {
|
SDF getBoxSDF(vec2 box, vec2 uv, float scale) {
|
||||||
|
vec2 nearest = round(uv);
|
||||||
|
vec2 xy = (abs(uv - 0.5) - 0.5) * box;
|
||||||
|
float d1 = max(xy.x, xy.y);
|
||||||
|
float outer = -d1;
|
||||||
|
float o = outer / scale;
|
||||||
|
return SDF(o, o);
|
||||||
|
}
|
||||||
|
|
||||||
// Resolution
|
SDF getBorderBoxSDF(vec2 box, vec4 border, vec2 uv, float scale) {
|
||||||
vec4 reso = vec4(conf.x/2.0, conf.y/2.0, 0.0, 0.0);
|
vec2 nearest = round(uv);
|
||||||
float c = cos(reso.x);
|
vec2 xy = (abs(uv - 0.5) - 0.5) * box;
|
||||||
|
float d1 = max(xy.x, xy.y);
|
||||||
|
float outer = -d1;
|
||||||
|
|
||||||
vec4 po = gl_in[0].gl_Position;
|
vec4 flipUV = vec4(uv, 1.0 - uv);
|
||||||
|
vec2 tl = flipUV.xy * box;
|
||||||
|
vec2 tr = flipUV.zy * box;
|
||||||
|
vec2 bl = flipUV.xw * box;
|
||||||
|
vec2 br = flipUV.zw * box;
|
||||||
|
|
||||||
vrange = vec4(po.x - reso.x, po.y - reso.y, po.x + reso.x, po.y + reso.y);
|
vec4 i4 = vec4(
|
||||||
|
getBoxCorner(tl, vec2(border.x, border.y)),
|
||||||
|
getBoxCorner(tr, vec2(border.z, border.y)),
|
||||||
|
getBoxCorner(bl, vec2(border.x, border.w)),
|
||||||
|
getBoxCorner(br, vec2(border.z, border.w))
|
||||||
|
);
|
||||||
|
float inner = min(min(i4.x, i4.y), min(i4.z, i4.w));
|
||||||
|
|
||||||
gl_Position = po - reso;
|
return SDF(outer / scale, inner / scale);
|
||||||
eth = gl_Position.x;
|
}
|
||||||
rng = gl_Position.y;
|
|
||||||
gl_Position = <transform(forward(gl_Position))>;
|
|
||||||
EmitVertex();
|
|
||||||
|
|
||||||
gl_Position = po + vec4(-reso.x, reso.y, 0.0, 0.0);
|
SDF getRoundedBorderBoxSDF(vec2 box, vec4 border, vec4 radius, vec2 uv, float scale) {
|
||||||
gl_Position.y = gl_Position.y / c;
|
vec4 flipUV = vec4(uv, 1.0 - uv);
|
||||||
eth = gl_Position.x;
|
vec2 tl = flipUV.xy * box;
|
||||||
rng = gl_Position.y;
|
vec2 tr = flipUV.zy * box;
|
||||||
gl_Position = <transform(forward(gl_Position))>;
|
vec2 bl = flipUV.xw * box;
|
||||||
EmitVertex();
|
vec2 br = flipUV.zw * box;
|
||||||
|
|
||||||
gl_Position = po + reso;
|
vec4 o4 = vec4(
|
||||||
gl_Position.y = gl_Position.y / c;
|
getCircleCorner(tl, radius.x),
|
||||||
eth = gl_Position.x;
|
getCircleCorner(tr, radius.y),
|
||||||
rng = gl_Position.y;
|
getCircleCorner(bl, radius.w),
|
||||||
gl_Position = <transform(forward(gl_Position))>;
|
getCircleCorner(br, radius.z)
|
||||||
EmitVertex();
|
);
|
||||||
|
float outer = min(min(o4.x, o4.y), min(o4.z, o4.w));
|
||||||
|
|
||||||
gl_Position = po + vec4(reso.x, -reso.y, 0.0, 0.0);
|
tl = tl - border.xy;
|
||||||
eth = gl_Position.x;
|
tr = tl - border.zy;
|
||||||
rng = gl_Position.y;
|
bl = tl - border.xw;
|
||||||
gl_Position = <transform(forward(gl_Position))>;
|
br = tl - border.zw;
|
||||||
EmitVertex();
|
|
||||||
|
|
||||||
EndPrimitive();
|
vec4 innerRadius = max(
|
||||||
|
vec4(0.0),
|
||||||
|
radius - vec4(
|
||||||
|
max(border.x, border.y),
|
||||||
|
max(border.z, border.y),
|
||||||
|
max(border.x, border.w),
|
||||||
|
max(border.z, border.w)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
vec4 i4 = vec4(
|
||||||
|
getCircleCorner(tl, innerRadius.x),
|
||||||
|
getCircleCorner(tr, innerRadius.y),
|
||||||
|
getCircleCorner(bl, innerRadius.w),
|
||||||
|
getCircleCorner(br, innerRadius.z)
|
||||||
|
);
|
||||||
|
float inner = min(min(i4.x, i4.y), min(i4.z, i4.w));
|
||||||
|
|
||||||
|
return SDF(outer / scale, inner / scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 getTexture(vec2 uv) {
|
||||||
|
return texture(uTexture, uv);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 getMask(vec4 color, vec2 uv, vec2 st) {
|
||||||
|
return color; // 默认不修改颜色
|
||||||
|
}
|
||||||
|
|
||||||
|
out vec4 fragColor;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec4 fillColor = uFill;
|
||||||
|
vec4 strokeColor = uStroke;
|
||||||
|
|
||||||
|
float scale = getUVScale(uSdfUV);
|
||||||
|
|
||||||
|
vec4 texture = getTexture(uTextureUV);
|
||||||
|
float sdfRaw = 0.0;
|
||||||
|
float mark = 0.0;
|
||||||
|
|
||||||
|
if (uMode == -1 || uMode == -2) {
|
||||||
|
float sdfRadius = uSdfConfig.x;
|
||||||
|
float expand = uBorder.x;
|
||||||
|
float bleed = uBorder.y;
|
||||||
|
|
||||||
|
float d = (texture.a - 0.75) * sdfRadius;
|
||||||
|
float s = (d + expand / uSdfConfig.y) / scale + 0.5 + bleed;
|
||||||
|
float sdf = s; // Assuming SDF returns a single float, adjust as necessary
|
||||||
|
|
||||||
|
if (uMode == -2) {
|
||||||
|
fillColor = vec4(texture.rgb, fillColor.a);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// More complex logic for other modes
|
||||||
|
if (texture.a > 0.0) {
|
||||||
|
if (((uRepeat == 0 || uRepeat == 1) && (uTextureUV.x < 0.0 || uTextureUV.x > 1.0)) ||
|
||||||
|
((uRepeat == 0 || uRepeat == 2) && (uTextureUV.y < 0.0 || uTextureUV.y > 1.0))) {
|
||||||
|
texture.a = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (texture.a > 0.0) {
|
||||||
|
fillColor = vec4(mix(fillColor.rgb * (1.0 - texture.a), texture.rgb, texture.a), mix(fillColor.a, 1.0, texture.a));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fillColor = fillColor; // Use premultiply function if needed
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (gl_FragCoord.x < uClipUV.x || gl_FragCoord.y < uClipUV.y || gl_FragCoord.x > uClipUV.z || gl_FragCoord.y > uClipUV.w) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute mask based on SDF
|
||||||
|
float mask = clamp(sdf, 0.0, 1.0);
|
||||||
|
|
||||||
|
// Final color blending logic here
|
||||||
|
fragColor = vec4(fillColor.rgb + mark, fillColor.a * mask + mark); // Simplified for clarity
|
||||||
|
}
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = CodeBlock::new(input).unwrap();
|
let result = CodeBlock::new(input).unwrap();
|
||||||
@ -1974,15 +2101,49 @@ fn test_shared_variable() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_function() {
|
fn test_function() {
|
||||||
let input = r#"
|
let input = r#"
|
||||||
float norm_rad(float rad) {
|
|
||||||
float result = mod(angle, M_2_PI);
|
|
||||||
if(result < 0.0) {
|
|
||||||
result = M_2_PI + result;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
SDF getRoundedBorderBoxSDF(vec2 box, vec4 border, vec4 radius, vec2 uv, float scale) {
|
||||||
|
vec4 flipUV = vec4(uv, 1.0 - uv);
|
||||||
|
vec2 tl = flipUV.xy * box;
|
||||||
|
vec2 tr = flipUV.zy * box;
|
||||||
|
vec2 bl = flipUV.xw * box;
|
||||||
|
vec2 br = flipUV.zw * box;
|
||||||
|
|
||||||
|
vec4 o4 = vec4(
|
||||||
|
getCircleCorner(tl, radius.x),
|
||||||
|
getCircleCorner(tr, radius.y),
|
||||||
|
getCircleCorner(bl, radius.w),
|
||||||
|
getCircleCorner(br, radius.z)
|
||||||
|
);
|
||||||
|
float outer = min(min(o4.x, o4.y), min(o4.z, o4.w));
|
||||||
|
|
||||||
|
tl = tl - border.xy;
|
||||||
|
tr = tl - border.zy;
|
||||||
|
bl = tl - border.xw;
|
||||||
|
br = tl - border.zw;
|
||||||
|
|
||||||
|
vec4 innerRadius = max(
|
||||||
|
vec4(0.0),
|
||||||
|
radius - vec4(
|
||||||
|
max(border.x, border.y),
|
||||||
|
max(border.z, border.y),
|
||||||
|
max(border.x, border.w),
|
||||||
|
max(border.z, border.w)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
vec4 i4 = vec4(
|
||||||
|
getCircleCorner(tl, innerRadius.x),
|
||||||
|
getCircleCorner(tr, innerRadius.y),
|
||||||
|
getCircleCorner(bl, innerRadius.w),
|
||||||
|
getCircleCorner(br, innerRadius.z)
|
||||||
|
);
|
||||||
|
float inner = min(min(i4.x, i4.y), min(i4.z, i4.w));
|
||||||
|
|
||||||
|
return SDF(outer / scale, inner / scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = ws(function)(input).unwrap();
|
let result = ws(function)(input).unwrap();
|
||||||
|
|||||||
314
src/utils/resources.rs
Normal file
314
src/utils/resources.rs
Normal file
@ -0,0 +1,314 @@
|
|||||||
|
use glow::{
|
||||||
|
HasContext, NativeBuffer, NativeFramebuffer, NativeRenderbuffer, NativeTexture,
|
||||||
|
NativeVertexArray,
|
||||||
|
};
|
||||||
|
use log::info;
|
||||||
|
use std::{borrow::Borrow, ops::Deref, rc::Rc};
|
||||||
|
|
||||||
|
pub type RcGlFramebuffer<'a> = RcGlResource<'a, NativeFramebuffer>;
|
||||||
|
pub type RcGlRenderbuffer<'a> = RcGlResource<'a, NativeRenderbuffer>;
|
||||||
|
pub type RcGlRcFramebuffer = RcGlRcResource<NativeFramebuffer>;
|
||||||
|
pub type RcGlRcRenderbuffer = RcGlRcResource<NativeRenderbuffer>;
|
||||||
|
pub type RcGlTexture<'a> = RcGlResource<'a, NativeTexture>;
|
||||||
|
pub type RcGlVertexArray<'a> = RcGlResource<'a, NativeVertexArray>;
|
||||||
|
pub type RcGlBuffer<'a> = RcGlResource<'a, NativeBuffer>;
|
||||||
|
|
||||||
|
// pub type RcGlResource<'a, T> = Rc<GlResource<'a, T>>;
|
||||||
|
pub trait ManagedResource {
|
||||||
|
fn bind(&self, target: u32);
|
||||||
|
fn unbind(&self, target: u32);
|
||||||
|
}
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct RcGlResource<'a, T: Resource>(Rc<GlResource<'a, T>>);
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct RcGlRcResource<T: Resource>(Rc<GlRcResource<T>>);
|
||||||
|
|
||||||
|
impl<'a, T: Resource> RcGlResource<'a, T> {
|
||||||
|
pub fn new(gl: &'a glow::Context, resource: T) -> Self {
|
||||||
|
Self(Rc::new(GlResource::new(resource, gl)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn native(&self) -> T {
|
||||||
|
self.0.resource.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> From<&'a GlRcResource<T>> for GlResource<'a, T>
|
||||||
|
where
|
||||||
|
T: Resource,
|
||||||
|
{
|
||||||
|
fn from(value: &'a GlRcResource<T>) -> Self {
|
||||||
|
GlResource::new(value.resource.clone(), &value.gl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: Resource> Deref for RcGlResource<'a, T> {
|
||||||
|
type Target = T;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
self.0.deref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Resource> RcGlRcResource<T> {
|
||||||
|
pub fn new(gl: Rc<glow::Context>, resource: T) -> Self {
|
||||||
|
Self(Rc::new(GlRcResource::new(resource, gl)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn native(&self) -> T {
|
||||||
|
self.0.resource.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Resource> Deref for RcGlRcResource<T> {
|
||||||
|
type Target = T;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
self.0.deref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait Resource: Clone + std::fmt::Debug {
|
||||||
|
fn drop_self(&self, gl: &glow::Context);
|
||||||
|
|
||||||
|
fn create(gl: &glow::Context) -> Self;
|
||||||
|
|
||||||
|
fn bind(&self, gl: &glow::Context, target: u32);
|
||||||
|
|
||||||
|
fn unbind(&self, gl: &glow::Context, target: u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Resource for NativeVertexArray {
|
||||||
|
fn drop_self(&self, gl: &glow::Context) {
|
||||||
|
unsafe {
|
||||||
|
gl.delete_vertex_array(*self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create(gl: &glow::Context) -> Self {
|
||||||
|
unsafe { gl.create_vertex_array().unwrap() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bind(&self, gl: &glow::Context, target: u32) {
|
||||||
|
unsafe {
|
||||||
|
gl.bind_vertex_array(Some(*self));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unbind(&self, gl: &glow::Context, target: u32) {
|
||||||
|
unsafe {
|
||||||
|
gl.bind_vertex_array(Some(*self));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Resource for NativeBuffer {
|
||||||
|
fn drop_self(&self, gl: &glow::Context) {
|
||||||
|
unsafe {
|
||||||
|
gl.delete_buffer(*self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create(gl: &glow::Context) -> Self {
|
||||||
|
unsafe { gl.create_buffer().unwrap() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bind(&self, gl: &glow::Context, target: u32) {
|
||||||
|
unsafe {
|
||||||
|
gl.bind_buffer(target, Some(*self));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unbind(&self, gl: &glow::Context, target: u32) {
|
||||||
|
unsafe {
|
||||||
|
gl.bind_buffer(target, None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Resource for NativeFramebuffer {
|
||||||
|
fn drop_self(&self, gl: &glow::Context) {
|
||||||
|
unsafe {
|
||||||
|
gl.delete_framebuffer(*self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create(gl: &glow::Context) -> Self {
|
||||||
|
unsafe { gl.create_framebuffer().unwrap() }
|
||||||
|
}
|
||||||
|
fn bind(&self, gl: &glow::Context, target: u32) {
|
||||||
|
unsafe {
|
||||||
|
gl.bind_framebuffer(target, Some(*self));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn unbind(&self, gl: &glow::Context, target: u32) {
|
||||||
|
unsafe {
|
||||||
|
gl.bind_framebuffer(target, None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Resource for NativeRenderbuffer {
|
||||||
|
fn drop_self(&self, gl: &glow::Context) {
|
||||||
|
unsafe {
|
||||||
|
gl.delete_renderbuffer(*self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create(gl: &glow::Context) -> Self {
|
||||||
|
unsafe { gl.create_renderbuffer().unwrap() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bind(&self, gl: &glow::Context, target: u32) {
|
||||||
|
unsafe {
|
||||||
|
gl.bind_renderbuffer(target, Some(*self));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn unbind(&self, gl: &glow::Context, target: u32) {
|
||||||
|
unsafe {
|
||||||
|
gl.bind_renderbuffer(target, None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Resource for NativeTexture {
|
||||||
|
fn drop_self(&self, gl: &glow::Context) {
|
||||||
|
unsafe {
|
||||||
|
gl.delete_texture(*self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create(gl: &glow::Context) -> Self {
|
||||||
|
unsafe { gl.create_texture().unwrap() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bind(&self, gl: &glow::Context, target: u32) {
|
||||||
|
unsafe {
|
||||||
|
gl.bind_texture(target, Some(*self));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unbind(&self, gl: &glow::Context, target: u32) {
|
||||||
|
unsafe {
|
||||||
|
gl.bind_texture(target, None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct GlResource<'a, T: Resource> {
|
||||||
|
gl: &'a glow::Context,
|
||||||
|
resource: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct GlRcResource<T: Resource> {
|
||||||
|
gl: Rc<glow::Context>,
|
||||||
|
resource: T,
|
||||||
|
}
|
||||||
|
impl<T: Resource> Deref for GlRcResource<T> {
|
||||||
|
type Target = T;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.resource
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> AsRef<GlResource<'a, T>> for RcGlResource<'a, T>
|
||||||
|
where
|
||||||
|
T: Resource,
|
||||||
|
{
|
||||||
|
fn as_ref(&self) -> &GlResource<'a, T> {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Resource> GlRcResource<T> {
|
||||||
|
pub fn new(resource: T, gl: Rc<glow::Context>) -> Self {
|
||||||
|
Self { resource, gl }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Resource> Drop for GlRcResource<T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.resource.drop_self(&*self.gl);
|
||||||
|
info!("Dropping resource: {:?}", self.resource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Resource> Deref for GlResource<'_, T> {
|
||||||
|
type Target = T;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.resource
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: Resource> GlResource<'a, T> {
|
||||||
|
pub fn new(resource: T, gl: &'a glow::Context) -> Self {
|
||||||
|
Self { resource, gl }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Resource> Drop for GlResource<'_, T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.resource.drop_self(self.gl);
|
||||||
|
info!("Dropping resource: {:?}", self.resource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> RcGlResource<'a, NativeTexture> {
|
||||||
|
pub fn native2imguiid(&self) -> imgui::TextureId {
|
||||||
|
imgui::TextureId::new(self.native().0.get() as usize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct GL {
|
||||||
|
gl: Rc<glow::Context>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GL {
|
||||||
|
pub fn new(gl: Rc<glow::Context>) -> Self {
|
||||||
|
Self { gl }
|
||||||
|
}
|
||||||
|
pub fn gl<'a>(&'a self) -> &'a glow::Context {
|
||||||
|
&self.gl
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gl_rc(&self) -> Rc<glow::Context> {
|
||||||
|
Rc::clone(&self.gl)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_resource<'a, T: Resource>(&'a self) -> RcGlResource<'a, T> {
|
||||||
|
RcGlResource::new(self.gl(), T::create(self.gl()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_resource_rc<T: Resource>(&self) -> RcGlRcResource<T> {
|
||||||
|
RcGlRcResource::new(self.gl_rc(), T::create(self.gl()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for GL {
|
||||||
|
type Target = glow::Context;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.gl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Resource> ManagedResource for RcGlResource<'_, T> {
|
||||||
|
fn bind(&self, target: u32) {
|
||||||
|
self.0.resource.bind(self.0.gl, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unbind(&self, target: u32) {
|
||||||
|
self.0.resource.unbind(&self.0.gl, target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Resource> ManagedResource for RcGlRcResource<T> {
|
||||||
|
fn bind(&self, target: u32) {
|
||||||
|
self.0.resource.bind(&self.0.gl, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unbind(&self, target: u32) {
|
||||||
|
self.0.resource.unbind(&self.0.gl, target);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user