Compare commits
No commits in common. "2995a830a2b65bef95a2c36720f1d90f20b7b641" and "96e5c78a59d421bb6630fa9788718b64c2c39fde" have entirely different histories.
2995a830a2
...
96e5c78a59
52
src/lib.rs
52
src/lib.rs
|
|
@ -9,13 +9,12 @@ use crate::{
|
|||
camera::OrbitalCamera,
|
||||
ray::Ray,
|
||||
render::lines::{LookParams, Mesh, Pipeline, Vertex},
|
||||
trace::{Hit, Lambertian, Reflective, Reflector, Scene, Source, Sphere},
|
||||
trace::{Hit, Lambertian, Reflector, Scene, Source, Sphere},
|
||||
};
|
||||
|
||||
mod camera;
|
||||
mod ray;
|
||||
mod render;
|
||||
mod shape;
|
||||
mod trace;
|
||||
|
||||
const OUTPUT_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Bgra8UnormSrgb;
|
||||
|
|
@ -28,13 +27,6 @@ pub struct SphericalPosition {
|
|||
pub distance: f32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[repr(u8)]
|
||||
pub enum UseNormal {
|
||||
Light,
|
||||
Camera,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[repr(C)]
|
||||
pub struct RedrawArgs {
|
||||
|
|
@ -44,8 +36,7 @@ pub struct RedrawArgs {
|
|||
pub light_spread: f32,
|
||||
pub accum_sigma: f32,
|
||||
pub accum_scale: f32,
|
||||
pub reflections: u8,
|
||||
pub use_normal: UseNormal,
|
||||
pub reflections: u32,
|
||||
pub show_axes: bool,
|
||||
pub show_shapes: bool,
|
||||
pub show_hit_emission: bool,
|
||||
|
|
@ -67,7 +58,6 @@ pub struct Core {
|
|||
surface: wgpu::Surface<'static>,
|
||||
|
||||
pipeline: Pipeline,
|
||||
mesh_pipe: render::faces::Pipeline,
|
||||
tripod: Mesh,
|
||||
}
|
||||
|
||||
|
|
@ -131,7 +121,6 @@ impl Core {
|
|||
} = gpu;
|
||||
|
||||
let pipeline = Pipeline::new(&device, OUTPUT_FORMAT);
|
||||
let mesh_pipe = render::faces::Pipeline::new(&device, OUTPUT_FORMAT);
|
||||
let tripod = new_tripod(&device);
|
||||
queue.submit([]); // flush buffer updates
|
||||
|
||||
|
|
@ -141,7 +130,6 @@ impl Core {
|
|||
surface,
|
||||
pipeline,
|
||||
tripod,
|
||||
mesh_pipe,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -164,12 +152,6 @@ impl Core {
|
|||
m: perspective * camera.transform(),
|
||||
},
|
||||
);
|
||||
self.mesh_pipe.set_look(
|
||||
&self.queue,
|
||||
render::faces::LookParams {
|
||||
m: perspective * camera.transform(),
|
||||
},
|
||||
);
|
||||
self.queue.submit([]); // flush buffer updates
|
||||
|
||||
let view = output.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
|
|
@ -232,26 +214,6 @@ impl Core {
|
|||
sphere(vec3(0.1, 0.3, 0.1)),
|
||||
],
|
||||
};
|
||||
if args.show_shapes {
|
||||
let mut meshes = Vec::new();
|
||||
for obj in &scene.objects {
|
||||
let mesh = shape::sphere((obj.radius * 15.) as usize + 7);
|
||||
let obj_mesh = render::faces::Mesh::new(
|
||||
&self.device,
|
||||
&mesh
|
||||
.vertices
|
||||
.iter()
|
||||
.map(|v| render::faces::Vertex {
|
||||
pos: obj.position + obj.radius * v,
|
||||
color: 0.5 * (v + 1.),
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
bytemuck::cast_slice(&mesh.indices),
|
||||
);
|
||||
meshes.push(obj_mesh);
|
||||
}
|
||||
self.mesh_pipe.render(&mut pass, &meshes);
|
||||
}
|
||||
|
||||
let mut prng = rand_pcg::Pcg64::new(42, 0);
|
||||
let source_rays: Vec<Ray> = (0..10240).map(|_| source.make_ray(&mut prng)).collect();
|
||||
|
|
@ -343,15 +305,13 @@ impl Core {
|
|||
if d2 > 9. * sigma2 {
|
||||
continue;
|
||||
}
|
||||
let normal = match args.use_normal {
|
||||
UseNormal::Light => light_hit.normal,
|
||||
UseNormal::Camera => hit.normal,
|
||||
};
|
||||
assert!(normal.is_normalized());
|
||||
assert!(hit.normal.is_normalized());
|
||||
assert!(hit.incident.dir.is_normalized());
|
||||
let reflector = Lambertian;
|
||||
let in_lm = 1.0;
|
||||
let out_cd = in_lm * reflector.brdf(normal, light_hit.incident.dir, -ray.dir);
|
||||
let out_cd = in_lm
|
||||
* hit.normal.dot(-hit.incident.dir)
|
||||
* reflector.brdf(hit.normal, hit.incident.dir, -ray.dir);
|
||||
let weight = accum_normalizator * (-0.5 * d2 / sigma2).exp();
|
||||
total_cd += weight * out_cd;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use std::{f32::consts::PI, sync::Arc};
|
||||
|
||||
use glam::uvec2;
|
||||
use photon_light::{Core, RedrawArgs, SphericalPosition, UseNormal, init_gpu_inner};
|
||||
use photon_light::{Core, RedrawArgs, SphericalPosition, init_gpu_inner};
|
||||
use winit::{
|
||||
application::ApplicationHandler,
|
||||
event::WindowEvent,
|
||||
|
|
@ -53,7 +53,6 @@ impl MainWindow {
|
|||
accum_sigma: 0.025,
|
||||
accum_scale: 0.01,
|
||||
reflections: 2,
|
||||
use_normal: UseNormal::Light,
|
||||
show_axes: true,
|
||||
show_shapes: true,
|
||||
show_hit_emission: false,
|
||||
|
|
|
|||
|
|
@ -1,161 +0,0 @@
|
|||
use std::mem::offset_of;
|
||||
|
||||
use bytemuck::{Pod, Zeroable, bytes_of, cast_slice};
|
||||
use glam::{Mat4, Vec3};
|
||||
use wgpu::util::DeviceExt as _;
|
||||
|
||||
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
|
||||
#[repr(C)]
|
||||
pub struct LookParams {
|
||||
pub m: Mat4,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
|
||||
#[repr(C)]
|
||||
pub struct Vertex {
|
||||
pub pos: Vec3,
|
||||
pub color: Vec3,
|
||||
}
|
||||
|
||||
impl Vertex {
|
||||
pub fn new(pos: Vec3, color: Vec3) -> Self {
|
||||
Self { pos, color }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Mesh {
|
||||
vertex_buffer: wgpu::Buffer,
|
||||
index_buffer: wgpu::Buffer,
|
||||
index_count: u32,
|
||||
}
|
||||
|
||||
impl Mesh {
|
||||
pub fn new(device: &wgpu::Device, vertices: &[Vertex], indices: &[u16]) -> Self {
|
||||
if vertices.len() >= 1 << 16 {
|
||||
panic!("too many vertices");
|
||||
}
|
||||
for index in indices {
|
||||
if *index as usize >= vertices.len() {
|
||||
panic!(
|
||||
"vertex index out of bounds: {index} out of {}",
|
||||
vertices.len()
|
||||
);
|
||||
}
|
||||
}
|
||||
let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: None,
|
||||
usage: wgpu::BufferUsages::VERTEX,
|
||||
contents: cast_slice(vertices),
|
||||
});
|
||||
let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: None,
|
||||
usage: wgpu::BufferUsages::INDEX,
|
||||
contents: cast_slice(indices),
|
||||
});
|
||||
Self {
|
||||
vertex_buffer,
|
||||
index_buffer,
|
||||
index_count: indices.len().try_into().expect("too many indices"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Pipeline {
|
||||
look_buf: wgpu::Buffer,
|
||||
bindings: wgpu::BindGroup,
|
||||
pipeline: wgpu::RenderPipeline,
|
||||
}
|
||||
|
||||
impl Pipeline {
|
||||
pub fn new(device: &wgpu::Device, format: wgpu::TextureFormat) -> Self {
|
||||
let look_buf = device.create_buffer(&wgpu::BufferDescriptor {
|
||||
label: None,
|
||||
size: size_of::<LookParams>() as u64,
|
||||
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||
mapped_at_creation: false,
|
||||
});
|
||||
|
||||
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||
label: None,
|
||||
source: wgpu::ShaderSource::Wgsl(super::SIMPLE_SHADER.into()),
|
||||
});
|
||||
let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||
label: None,
|
||||
layout: None,
|
||||
vertex: wgpu::VertexState {
|
||||
module: &shader,
|
||||
entry_point: None,
|
||||
compilation_options: wgpu::PipelineCompilationOptions::default(),
|
||||
buffers: &[wgpu::VertexBufferLayout {
|
||||
array_stride: size_of::<Vertex>() as u64,
|
||||
step_mode: wgpu::VertexStepMode::Vertex,
|
||||
attributes: &[
|
||||
wgpu::VertexAttribute {
|
||||
shader_location: 0,
|
||||
offset: offset_of!(Vertex, pos) as u64,
|
||||
format: wgpu::VertexFormat::Float32x3,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
shader_location: 1,
|
||||
offset: offset_of!(Vertex, color) as u64,
|
||||
format: wgpu::VertexFormat::Float32x3,
|
||||
},
|
||||
],
|
||||
}],
|
||||
},
|
||||
primitive: wgpu::PrimitiveState {
|
||||
topology: wgpu::PrimitiveTopology::TriangleList,
|
||||
..Default::default()
|
||||
},
|
||||
depth_stencil: None,
|
||||
multisample: wgpu::MultisampleState {
|
||||
count: 1,
|
||||
mask: !0,
|
||||
alpha_to_coverage_enabled: false,
|
||||
},
|
||||
fragment: Some(wgpu::FragmentState {
|
||||
module: &shader,
|
||||
entry_point: None,
|
||||
compilation_options: wgpu::PipelineCompilationOptions::default(),
|
||||
targets: &[Some(wgpu::ColorTargetState {
|
||||
format,
|
||||
blend: Some(wgpu::BlendState::REPLACE),
|
||||
write_mask: wgpu::ColorWrites::ALL,
|
||||
})],
|
||||
}),
|
||||
multiview: None,
|
||||
cache: None,
|
||||
});
|
||||
let bindings = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
label: None,
|
||||
layout: &pipeline.get_bind_group_layout(0),
|
||||
entries: &[wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: look_buf.as_entire_binding(),
|
||||
}],
|
||||
});
|
||||
Self {
|
||||
look_buf,
|
||||
bindings,
|
||||
pipeline,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_look(&self, queue: &wgpu::Queue, look: LookParams) {
|
||||
queue.write_buffer(&self.look_buf, 0, bytes_of(&look));
|
||||
}
|
||||
|
||||
pub fn render<'a>(
|
||||
&self,
|
||||
pass: &mut wgpu::RenderPass,
|
||||
meshes: impl IntoIterator<Item = &'a Mesh>,
|
||||
) {
|
||||
pass.set_pipeline(&self.pipeline);
|
||||
pass.set_bind_group(0, &self.bindings, &[]);
|
||||
for mesh in meshes {
|
||||
pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
|
||||
pass.set_index_buffer(mesh.index_buffer.slice(..), wgpu::IndexFormat::Uint16);
|
||||
pass.draw_indexed(0..mesh.index_count, 0, 0..1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -57,9 +57,10 @@ impl Pipeline {
|
|||
mapped_at_creation: false,
|
||||
});
|
||||
|
||||
let shader = std::fs::read_to_string("shaders/line.wgsl").unwrap();
|
||||
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||
label: None,
|
||||
source: wgpu::ShaderSource::Wgsl(super::SIMPLE_SHADER.into()),
|
||||
source: wgpu::ShaderSource::Wgsl(shader.into()),
|
||||
});
|
||||
let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||
label: None,
|
||||
|
|
|
|||
|
|
@ -1,4 +1 @@
|
|||
pub mod faces;
|
||||
pub mod lines;
|
||||
|
||||
static SIMPLE_SHADER: &str = include_str!("simple.wgsl");
|
||||
|
|
|
|||
140
src/shape.rs
140
src/shape.rs
|
|
@ -1,140 +0,0 @@
|
|||
use std::f32::consts::PI;
|
||||
|
||||
use glam::{Vec3, vec3};
|
||||
|
||||
pub type Face = [u16; 3];
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Mesh {
|
||||
pub vertices: Vec<Vec3>,
|
||||
pub indices: Vec<Face>,
|
||||
}
|
||||
|
||||
pub fn sphere(subdiv: usize) -> Mesh {
|
||||
assert!(subdiv >= 2);
|
||||
assert!(2 * subdiv * (subdiv - 1) + 2 <= 1 << 16);
|
||||
|
||||
let mut vertices = Vec::new();
|
||||
vertices.push(Vec3::Z);
|
||||
for j in 1..subdiv {
|
||||
let v = j as f32 / subdiv as f32;
|
||||
let theta = PI * v;
|
||||
let (xy, z) = theta.sin_cos();
|
||||
for i in 0..2 * subdiv {
|
||||
let u = i as f32 / (2 * subdiv) as f32;
|
||||
let phi = 2. * PI * u;
|
||||
let (y, x) = phi.sin_cos();
|
||||
vertices.push(vec3(xy * x, xy * y, z));
|
||||
}
|
||||
}
|
||||
vertices.push(-Vec3::Z);
|
||||
assert_eq!(vertices.len(), 2 * subdiv * (subdiv - 1) + 2);
|
||||
|
||||
let mut indices = Vec::new();
|
||||
let subdiv = subdiv as u16;
|
||||
for i in 0..2 * subdiv - 1 {
|
||||
indices.push([0, i + 1, i + 2]);
|
||||
}
|
||||
indices.push([0, 2 * subdiv, 1]);
|
||||
for j in 0..subdiv - 2 {
|
||||
let k1 = j * 2 * subdiv + 1;
|
||||
let k2 = k1 + 2 * subdiv;
|
||||
for i in 0..2 * subdiv - 1 {
|
||||
let a = k1 + i;
|
||||
let b = k2 + i;
|
||||
indices.push([a, b, b + 1]);
|
||||
indices.push([a, b + 1, a + 1]);
|
||||
}
|
||||
let a = k1 + 2 * subdiv - 1;
|
||||
let b = k2 + 2 * subdiv - 1;
|
||||
indices.push([a, b, k2]);
|
||||
indices.push([a, k2, k1]);
|
||||
}
|
||||
let k1 = 2 * subdiv * (subdiv - 2) + 1;
|
||||
let k2 = 2 * subdiv * (subdiv - 1) + 1;
|
||||
for i in 0..2 * subdiv - 1 {
|
||||
indices.push([k1 + i, k2, k1 + i + 1]);
|
||||
}
|
||||
indices.push([k1 + 2 * subdiv - 1, k2, k1]);
|
||||
|
||||
Mesh { vertices, indices }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use approx::abs_diff_eq;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_sphere_2_topology() {
|
||||
assert_eq!(
|
||||
sphere(2).indices,
|
||||
vec![
|
||||
[0, 1, 2],
|
||||
[0, 2, 3],
|
||||
[0, 3, 4],
|
||||
[0, 4, 1],
|
||||
[1, 5, 2],
|
||||
[2, 5, 3],
|
||||
[3, 5, 4],
|
||||
[4, 5, 1],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
fn are_equal_eps(left: &[Vec3], right: &[Vec3], eps: f32) -> bool {
|
||||
if left.len() != right.len() {
|
||||
return false;
|
||||
}
|
||||
left.iter()
|
||||
.zip(right.iter())
|
||||
.all(|(a, b)| abs_diff_eq!(a, b, epsilon = eps))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sphere_2_geometry() {
|
||||
let left = sphere(2).vertices;
|
||||
let right = [Vec3::Z, Vec3::X, Vec3::Y, -Vec3::X, -Vec3::Y, -Vec3::Z];
|
||||
assert!(
|
||||
are_equal_eps(&left, &right, 1e-6),
|
||||
"assertion `left == right` failed\n left: {left:?}\n right: {right:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sphere_3_topology() {
|
||||
assert_eq!(
|
||||
sphere(3).indices,
|
||||
vec![
|
||||
// top
|
||||
[0, 1, 2],
|
||||
[0, 2, 3],
|
||||
[0, 3, 4],
|
||||
[0, 4, 5],
|
||||
[0, 5, 6],
|
||||
[0, 6, 1],
|
||||
// belt
|
||||
[1, 7, 8],
|
||||
[1, 8, 2],
|
||||
[2, 8, 9],
|
||||
[2, 9, 3],
|
||||
[3, 9, 10],
|
||||
[3, 10, 4],
|
||||
[4, 10, 11],
|
||||
[4, 11, 5],
|
||||
[5, 11, 12],
|
||||
[5, 12, 6],
|
||||
[6, 12, 7],
|
||||
[6, 7, 1],
|
||||
// bottom
|
||||
[7, 13, 8],
|
||||
[8, 13, 9],
|
||||
[9, 13, 10],
|
||||
[10, 13, 11],
|
||||
[11, 13, 12],
|
||||
[12, 13, 7],
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -13,11 +13,6 @@ struct SphericalPosition {
|
|||
float distance = 1.;
|
||||
};
|
||||
|
||||
enum class UseNormal: std::uint8_t {
|
||||
Light,
|
||||
Camera,
|
||||
};
|
||||
|
||||
struct RedrawArgs {
|
||||
SphericalPosition camera_position;
|
||||
SphericalPosition light_position;
|
||||
|
|
@ -25,8 +20,7 @@ struct RedrawArgs {
|
|||
float light_spread = 0.;
|
||||
float accum_sigma = 1.;
|
||||
float accum_scale = 1.;
|
||||
std::uint8_t reflections = 0;
|
||||
UseNormal use_normal = UseNormal::Light;
|
||||
std::uint32_t reflections = 0;
|
||||
bool show_axes = true;
|
||||
bool show_shapes = true;
|
||||
bool show_hit_emission = true;
|
||||
|
|
@ -37,7 +31,6 @@ struct RedrawArgs {
|
|||
};
|
||||
} // namespace ffi
|
||||
|
||||
using ffi::UseNormal;
|
||||
using ffi::RedrawArgs;
|
||||
using ffi::SphericalPosition;
|
||||
|
||||
|
|
|
|||
|
|
@ -16,11 +16,6 @@ float deg_to_rad(float val) {
|
|||
}
|
||||
|
||||
void PhotonLight::updateView() {
|
||||
UseNormal use_normal = {};
|
||||
if (m_ui->normalFromLight->isChecked())
|
||||
use_normal = UseNormal::Light;
|
||||
else if (m_ui->normalFromCamera->isChecked())
|
||||
use_normal = UseNormal::Camera;
|
||||
RedrawArgs args{
|
||||
.camera_position = SphericalPosition{
|
||||
.yaw = deg_to_rad(m_ui->cameraYaw->value()),
|
||||
|
|
@ -36,8 +31,7 @@ void PhotonLight::updateView() {
|
|||
.light_spread = 0.125,
|
||||
.accum_sigma = exp10f(m_ui->accumSigma->value() / 25.0),
|
||||
.accum_scale = exp10f(m_ui->accumScale->value() / 25.0),
|
||||
.reflections = std::uint8_t(m_ui->reflections->value()),
|
||||
.use_normal = use_normal,
|
||||
.reflections = std::uint32_t(m_ui->reflections->value()),
|
||||
.show_axes = m_ui->displayAxes->isChecked(),
|
||||
.show_shapes = m_ui->displayShapes->isChecked(),
|
||||
.show_hit_emission = m_ui->displayEmitted->isChecked(),
|
||||
|
|
@ -57,9 +51,4 @@ void PhotonLight::updateView() {
|
|||
m_ui->viewport->setView(args);
|
||||
}
|
||||
|
||||
void PhotonLight::updateViewIf(bool update) {
|
||||
if (update)
|
||||
updateView();
|
||||
}
|
||||
|
||||
#include "moc_main_window.cpp"
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ public:
|
|||
|
||||
public slots:
|
||||
void updateView();
|
||||
void updateViewIf(bool update); // for radio buttons
|
||||
|
||||
private:
|
||||
const std::unique_ptr<Ui::MainWindow> m_ui;
|
||||
|
|
|
|||
|
|
@ -298,30 +298,6 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Use normal at</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="normalFromLight">
|
||||
<property name="text">
|
||||
<string>Hit position</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="normalFromCamera">
|
||||
<property name="text">
|
||||
<string>Show position</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
@ -661,41 +637,8 @@
|
|||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>normalFromLight</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>updateViewIf(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>1429</x>
|
||||
<y>787</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>1381</x>
|
||||
<y>783</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>normalFromCamera</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>updateViewIf(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>1932</x>
|
||||
<y>816</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>1600</x>
|
||||
<y>874</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
<slots>
|
||||
<slot>updateView()</slot>
|
||||
<slot>updateViewIf(bool)</slot>
|
||||
</slots>
|
||||
</ui>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user