add wip articles
BIN
assets/art/after-school.avif
Normal file
After Width: | Height: | Size: 74 KiB |
BIN
assets/art/alear.avif
Normal file
After Width: | Height: | Size: 64 KiB |
BIN
assets/art/bard.avif
Normal file
After Width: | Height: | Size: 99 KiB |
BIN
assets/art/bathing.avif
Normal file
After Width: | Height: | Size: 26 KiB |
BIN
assets/art/gamingforone.avif
Normal file
After Width: | Height: | Size: 161 KiB |
BIN
assets/art/goinghome.avif
Normal file
After Width: | Height: | Size: 98 KiB |
BIN
assets/art/grocery-shopping.avif
Normal file
After Width: | Height: | Size: 118 KiB |
BIN
assets/art/knockout-background2.avif
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
assets/art/marchsketch.avif
Normal file
After Width: | Height: | Size: 174 KiB |
BIN
assets/art/marchsketch4.avif
Normal file
After Width: | Height: | Size: 178 KiB |
BIN
assets/art/office-date.avif
Normal file
After Width: | Height: | Size: 166 KiB |
BIN
assets/art/orbit-correction.avif
Normal file
After Width: | Height: | Size: 38 KiB |
BIN
assets/art/short-hair.avif
Normal file
After Width: | Height: | Size: 27 KiB |
BIN
assets/art/sketchmarch2.avif
Normal file
After Width: | Height: | Size: 66 KiB |
BIN
assets/art/sword-leftlean.avif
Normal file
After Width: | Height: | Size: 54 KiB |
BIN
assets/art/warriors.avif
Normal file
After Width: | Height: | Size: 69 KiB |
BIN
assets/art/working-out.avif
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
assets/art/workingout.avif
Normal file
After Width: | Height: | Size: 112 KiB |
BIN
assets/art/xbc3.avif
Normal file
After Width: | Height: | Size: 85 KiB |
|
@ -45,14 +45,16 @@ permalinks:
|
||||||
art: 'art/:year/:month/:slugorfilename'
|
art: 'art/:year/:month/:slugorfilename'
|
||||||
arttags: 'art/tags/:title'
|
arttags: 'art/tags/:title'
|
||||||
characters: 'art/characters/:title'
|
characters: 'art/characters/:title'
|
||||||
characters_listpath: "art/characters/"
|
|
||||||
tags: 'blog/tags/:title'
|
tags: 'blog/tags/:title'
|
||||||
|
series: 'blog/series/:title'
|
||||||
|
projtags: 'software/tags/:title'
|
||||||
|
|
||||||
taxonomies:
|
taxonomies:
|
||||||
series: 'series'
|
series: 'series'
|
||||||
tag: 'tags'
|
tag: 'tags'
|
||||||
arttag: 'arttags'
|
arttag: 'arttags'
|
||||||
character: 'characters'
|
character: 'characters'
|
||||||
|
projtag: 'projtags'
|
||||||
|
|
||||||
menu:
|
menu:
|
||||||
main:
|
main:
|
||||||
|
|
|
@ -12,3 +12,5 @@ Rambles and writings usually consisting of [Linux](/blog/tags/linux/), [KDE](/bl
|
||||||
If you're interested, you can follow this blog using [RSS](/blog/index.xml).
|
If you're interested, you can follow this blog using [RSS](/blog/index.xml).
|
||||||
|
|
||||||
_Warning_: most of it is mindlessly technical.
|
_Warning_: most of it is mindlessly technical.
|
||||||
|
|
||||||
|
{{< blog-featured >}}
|
||||||
|
|
|
@ -4,6 +4,8 @@ date: 2022-05-04
|
||||||
draft: false
|
draft: false
|
||||||
aliases:
|
aliases:
|
||||||
- /notes/astra-0.4.0-release/
|
- /notes/astra-0.4.0-release/
|
||||||
|
tags:
|
||||||
|
- Astra
|
||||||
---
|
---
|
||||||
|
|
||||||
I'm very sorry about the delay, but a new release of Astra is upon us!
|
I'm very sorry about the delay, but a new release of Astra is upon us!
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
---
|
---
|
||||||
title: "Breakdown of my static art gallery"
|
title: "Breakdown of my static art gallery"
|
||||||
date: "2023-04-26"
|
date: "2023-04-26"
|
||||||
|
tags:
|
||||||
|
- Website
|
||||||
---
|
---
|
||||||
|
|
||||||
I realized today that I had inadvertently created a static art gallery. I think my system is interesting, so I wanted to write down how it works.
|
I realized today that I had inadvertently created a static art gallery. I think my system is interesting, so I wanted to write down how it works.
|
||||||
|
|
|
@ -1,17 +1,19 @@
|
||||||
---
|
---
|
||||||
title: "Drawing simple cubes"
|
title: "Graphics Dump: Drawing debug cubes"
|
||||||
date: 2023-06-10
|
date: 2023-06-27
|
||||||
draft: true
|
draft: true
|
||||||
summary: "When working on my engine, I wanted to clean up my debug gizmos a bit. The first thing to tackle is drawing bounding boxes!"
|
summary: "When working on my engine, I wanted to clean up my debug gizmos a bit. The first thing to tackle is drawing bounding boxes!"
|
||||||
tags:
|
tags:
|
||||||
- C++
|
- C++
|
||||||
|
- Graphics Dump
|
||||||
|
- Math
|
||||||
|
series:
|
||||||
|
- Graphics Dump
|
||||||
---
|
---
|
||||||
|
|
||||||
If you are writing graphical tools, one of the most common shapes you draw is boxes. They can represent areas (like the affected area of an environment capture) or a box collision.
|
If you are writing graphical tools, one of the most common shapes you draw is boxes. They can represent areas (like the affected area of an environment capture) or a box collision.
|
||||||
|
|
||||||
One common problem that I run into, is dealing with these diagonals:
|
One common problem that I run into, is dealing with these diagonals:
|
||||||
|
|
||||||
Of course this is natural due to the way that GPUs work, they work in triangles and when drawing the wireframe of a cube doesn't just draw the edges but everything in-between:
|
I found a pretty neat trick for removing these diagonals without having to change the vertex data. This doesn't work for anything that isn't a cube (I think), but whatever.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
title: "Figure Review: Atelier Ryza 2"
|
title: "Figure Review: Atelier Ryza 2"
|
||||||
date: 2022-10-05
|
date: 2023-06-23
|
||||||
draft: true
|
draft: true
|
||||||
tags:
|
tags:
|
||||||
- Figures
|
- Figures
|
||||||
|
|
|
@ -3,6 +3,9 @@ title: "Future of Silica Viewer"
|
||||||
date: 2023-03-27
|
date: 2023-03-27
|
||||||
draft: false
|
draft: false
|
||||||
summary: "Silica Viewer is leaving the App Store."
|
summary: "Silica Viewer is leaving the App Store."
|
||||||
|
tags:
|
||||||
|
- Procreate
|
||||||
|
- Apple
|
||||||
---
|
---
|
||||||
|
|
||||||
I will not be renewing my Apple Developer License for various reasons [I've already discussed elsewhere]({{<ref "/blog/breaking-even" >}}). It is due to expire sometime in the summer, so there's still a couple of months before it disappears off the App Store. I'm actually _losing_ money by selling my software on the App Store, and I don't really work on a Mac anymore. Plus more fun problems related to Apple infrastructure:
|
I will not be renewing my Apple Developer License for various reasons [I've already discussed elsewhere]({{<ref "/blog/breaking-even" >}}). It is due to expire sometime in the summer, so there's still a couple of months before it disappears off the App Store. I'm actually _losing_ money by selling my software on the App Store, and I don't really work on a Mac anymore. Plus more fun problems related to Apple infrastructure:
|
||||||
|
@ -11,6 +14,6 @@ I will not be renewing my Apple Developer License for various reasons [I've alre
|
||||||
|
|
||||||
If you already bought Silica Viewer, nothing will change - you can continue to re-download it into perpetuity. This just means you can't pay money for it now, although I wouldn't want you to because I don't keep up with active development. As always, Silica Viewer is open source and free to use still.
|
If you already bought Silica Viewer, nothing will change - you can continue to re-download it into perpetuity. This just means you can't pay money for it now, although I wouldn't want you to because I don't keep up with active development. As always, Silica Viewer is open source and free to use still.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
For people looking for alternative software, check out [Silicate](https://github.com/Avarel/silicate) by [Avarel](http://antran.io/). It works on more platforms than Silica Viewer, and has a better rendering engine, along with other cool features! Thank you all who have supported by using my software, as always :)
|
For people looking for alternative software, check out [Silicate](https://github.com/Avarel/silicate) by [Avarel](http://antran.io/). It works on more platforms than Silica Viewer, and has a better rendering engine, along with other cool features! Thank you all who have supported by using my software, as always :)
|
||||||
|
|
Before Width: | Height: | Size: 227 KiB |
BIN
content/blog/future-of-silica-viewer/macos.webp
Normal file
After Width: | Height: | Size: 37 KiB |
|
@ -2,7 +2,7 @@
|
||||||
title: "Making fail2ban work on systemd Gentoo"
|
title: "Making fail2ban work on systemd Gentoo"
|
||||||
date: 2022-11-03
|
date: 2022-11-03
|
||||||
draft: false
|
draft: false
|
||||||
tags:
|
blogtags:
|
||||||
- Linux
|
- Linux
|
||||||
- Gentoo
|
- Gentoo
|
||||||
---
|
---
|
||||||
|
|
17
content/blog/graphics-dump-dof/index.md
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
---
|
||||||
|
title: "Graphics Dump: Bokeh depth of field"
|
||||||
|
date: 2023-05-27
|
||||||
|
draft: true
|
||||||
|
series:
|
||||||
|
- Graphics Dump
|
||||||
|
---
|
||||||
|
|
||||||
|
Today I implemented Bart Wronski's fantastic Bokeh depth of field effect.
|
||||||
|
|
||||||
|
# Pitfalls
|
||||||
|
|
||||||
|
Here's some of the pitfalls I ran into while implementing this:
|
||||||
|
|
||||||
|
## Make sure the bokeh image doesn't repeat, or else it will start bleeding on the edge of triangles.
|
||||||
|
|
||||||
|
## Make sure your aperture texture has a black background.
|
|
@ -3,6 +3,8 @@ title: "Retrospective on three years of self-hosting"
|
||||||
date: 2023-03-27
|
date: 2023-03-27
|
||||||
draft: false
|
draft: false
|
||||||
summary: "I've been hosting my own infrastructure for almost three years now!"
|
summary: "I've been hosting my own infrastructure for almost three years now!"
|
||||||
|
tags:
|
||||||
|
- Website
|
||||||
---
|
---
|
||||||
|
|
||||||
This website has been running almost non-stop [since November 2020](https://web.archive.org/web/20201101074137/https://redstrate.com/), according to archive.org. Since then, I have been running this website and related services myself - not physically but still taking care of admin tasks.[^1]
|
This website has been running almost non-stop [since November 2020](https://web.archive.org/web/20201101074137/https://redstrate.com/), according to archive.org. Since then, I have been running this website and related services myself - not physically but still taking care of admin tasks.[^1]
|
||||||
|
|
BIN
content/blog/infra-review/bear.webp
Normal file
After Width: | Height: | Size: 542 KiB |
BIN
content/blog/infra-review/environment.webp
Normal file
After Width: | Height: | Size: 896 KiB |
79
content/blog/infra-review/index.md
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
---
|
||||||
|
title: "Game Review: INFRA"
|
||||||
|
date: 2023-06-28
|
||||||
|
draft: true
|
||||||
|
tags:
|
||||||
|
- Review
|
||||||
|
- Gaming
|
||||||
|
series:
|
||||||
|
- Game Reviews
|
||||||
|
---
|
||||||
|
|
||||||
|
A little while ago, people suggested that I play INFRA. The premise is simple, you are
|
||||||
|
a man walking around and taking pictures. I won't spoil plot details of the
|
||||||
|
game, but I will show screenshots from several places in the game. So if you wish
|
||||||
|
to go in blind, I _highly_ suggest playing the game first.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
The thing I absolutely love about this game is the environments, like the office
|
||||||
|
in the beginning of the game. INFRA is made in [Source](https://developer.valvesoftware.com/wiki/Source) (although I'm not sure
|
||||||
|
what branch it is) and the fantastic art direction combined with the outdated
|
||||||
|
Source lighting gives the game a unique vibe.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
You are "Markku Siltanen", but called "Mark" by your coworkers. As part of a
|
||||||
|
consulting group, you are equipped with the gripping task of _"structural survey"_.
|
||||||
|
What this really means is taking an obscene amount of pictures on your camera
|
||||||
|
with a battery that only lasts five seconds if you leave it on. While there
|
||||||
|
is dialogue in typical Source game fashion there is not a wealth of cutscenes
|
||||||
|
and you'll have control of your character for a large part of the game. In
|
||||||
|
some sections, there are visible NPCs but you'll be lonely for most of the
|
||||||
|
game except for the occasional phone or radio call.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
As you progress through the game you'll enter farther and farther into the
|
||||||
|
depths of the city. The gradual exploration aspect is really fascinating, and
|
||||||
|
there is a wealth of nature but also grimy underground facilities.
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
The game is not always a "walking simulator", but that's what you do for most
|
||||||
|
of the game. There is small puzzles here and there, and some segments
|
||||||
|
that are timed. At least for me, some of the obstacles were not straightforward
|
||||||
|
and I needed a walkthrough for later parts of the game. I never felt frustrated
|
||||||
|
though, but the game definitely does not hold your hand.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
I'm not sure if the environments are supposed to be liminal, but it gives
|
||||||
|
off that feeling through most of the game. The sound design is also fantastic,
|
||||||
|
and I felt really immersed while playing. A lot of the game is not "signposted"
|
||||||
|
(as in, it warns you what's about to happen) so there is some intentional and
|
||||||
|
unintentional jumpscares. That's on top of the general creepiness. One of my
|
||||||
|
favorite locations is the metro, which is empty once you get there of course.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
The later half of the game puts you (mostly) above ground, with a lot of city
|
||||||
|
and urban environments in a stark contrast to the first half of the game. I also
|
||||||
|
loved the gradual transistion into night time as well, which just added to the
|
||||||
|
creepiness and unease.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Despite being a Source game, the environment art is so good it really carries
|
||||||
|
the graphics - although it looks dated in some spots. For example, reflections
|
||||||
|
are really simple and some environments almost look like they have no lighting.
|
||||||
|
It's just a minor gripe though, this game released a while ago and probably
|
||||||
|
started development even farther back.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
If you're a fan of Source games, and urban exploration (like I am) then this
|
||||||
|
game is definitely up your alley. You can buy it [on Steam](https://store.steampowered.com/app/251110/INFRA/). Enjoy!
|
||||||
|
|
||||||
|

|
BIN
content/blog/infra-review/metro.webp
Normal file
After Width: | Height: | Size: 388 KiB |
BIN
content/blog/infra-review/npcs.webp
Normal file
After Width: | Height: | Size: 281 KiB |
BIN
content/blog/infra-review/office.webp
Normal file
After Width: | Height: | Size: 721 KiB |
BIN
content/blog/infra-review/pretty1.webp
Normal file
After Width: | Height: | Size: 362 KiB |
BIN
content/blog/infra-review/pretty2.webp
Normal file
After Width: | Height: | Size: 974 KiB |
BIN
content/blog/infra-review/reflections.webp
Normal file
After Width: | Height: | Size: 238 KiB |
BIN
content/blog/infra-review/sky.jpg
Executable file
After Width: | Height: | Size: 354 KiB |
BIN
content/blog/infra-review/sky.webp
Normal file
After Width: | Height: | Size: 176 KiB |
Before Width: | Height: | Size: 3.8 KiB |
BIN
content/blog/kde-april2023/character-count.webp
Normal file
After Width: | Height: | Size: 1.7 KiB |
|
@ -8,6 +8,8 @@ tags:
|
||||||
- KDE
|
- KDE
|
||||||
- Qt
|
- Qt
|
||||||
toc: true
|
toc: true
|
||||||
|
series:
|
||||||
|
- My Work in KDE
|
||||||
---
|
---
|
||||||
|
|
||||||
Here's my KDE contributions for this month! It's a little bit shorter than I'd like, but I'm still trying to find a nice balance between **$work** and KDE.
|
Here's my KDE contributions for this month! It's a little bit shorter than I'd like, but I'm still trying to find a nice balance between **$work** and KDE.
|
||||||
|
@ -37,21 +39,21 @@ I finally [merged the improved dark mode I started a while ago](https://invent.k
|
||||||
|
|
||||||
I added [support for the new Maximize component](https://invent.kde.org/network/tokodon/-/merge_requests/200), which doesn't change anything functionality-wise but makes our code leaner:
|
I added [support for the new Maximize component](https://invent.kde.org/network/tokodon/-/merge_requests/200), which doesn't change anything functionality-wise but makes our code leaner:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
I also [pushed along the MR to use the SearchPopupField component](https://invent.kde.org/network/tokodon/-/merge_requests/144) from Kirigami Add-ons too, which is another change that doesn't change anything functionally but will make the code much leaner.
|
I also [pushed along the MR to use the SearchPopupField component](https://invent.kde.org/network/tokodon/-/merge_requests/144) from Kirigami Add-ons too, which is another change that doesn't change anything functionally but will make the code much leaner.
|
||||||
|
|
||||||
[Interaction icons were overhauled, not just visually but also their layout has improved](https://invent.kde.org/network/tokodon/-/merge_requests/211). Below is a screenshot of a mobile sized window, where the icons now spread out which will make them easier to tap (I plan on increasing the size on mobile too!)
|
[Interaction icons were overhauled, not just visually but also their layout has improved](https://invent.kde.org/network/tokodon/-/merge_requests/211). Below is a screenshot of a mobile sized window, where the icons now spread out which will make them easier to tap (I plan on increasing the size on mobile too!)
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
The [amount of characters you have left is now shown in the composer](https://invent.kde.org/network/tokodon/-/merge_requests/212), which should load from your server (I don't have anything but a 500-char limit to test with though):
|
The [amount of characters you have left is now shown in the composer](https://invent.kde.org/network/tokodon/-/merge_requests/212), which should load from your server (I don't have anything but a 500-char limit to test with though):
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
It's not merged yet, but I'm adding [overhauling how you view posts on profile pages](https://invent.kde.org/network/tokodon/-/merge_requests/214)! It has the usual separation of _"Posts"_, _"Replies"_ and _"Media"_ tabs but you can hide boosts too:
|
It's not merged yet, but I'm adding [overhauling how you view posts on profile pages](https://invent.kde.org/network/tokodon/-/merge_requests/214)! It has the usual separation of _"Posts"_, _"Replies"_ and _"Media"_ tabs but you can hide boosts too:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
As a cherry on top, [I added icons to some of the menus](https://invent.kde.org/network/tokodon/-/merge_requests/217) which makes them easier to quickly navigate:
|
As a cherry on top, [I added icons to some of the menus](https://invent.kde.org/network/tokodon/-/merge_requests/217) which makes them easier to quickly navigate:
|
||||||
|
|
||||||
|
@ -66,7 +68,7 @@ Some more minor changes:
|
||||||
|
|
||||||
I did some touching up work for Keysmith late this month: I fixed [a bunch of binding loops](https://invent.kde.org/utilities/keysmith/-/merge_requests/112), and added better keyboard navigation. I also [added a passive notification when you copy the code to your clipboard](https://invent.kde.org/utilities/keysmith/-/merge_requests/113) too:
|
I did some touching up work for Keysmith late this month: I fixed [a bunch of binding loops](https://invent.kde.org/utilities/keysmith/-/merge_requests/112), and added better keyboard navigation. I also [added a passive notification when you copy the code to your clipboard](https://invent.kde.org/utilities/keysmith/-/merge_requests/113) too:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
There doesn't appear to be an active maintainer, so I'll get another reviewer and merge them next month. I also cleaned up some duplicate bugs, and moved some spam off of the tracker.
|
There doesn't appear to be an active maintainer, so I'll get another reviewer and merge them next month. I also cleaned up some duplicate bugs, and moved some spam off of the tracker.
|
||||||
|
|
||||||
|
|
Before Width: | Height: | Size: 168 KiB |
BIN
content/blog/kde-april2023/maximize.webp
Normal file
After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 133 KiB |
BIN
content/blog/kde-april2023/new-icons.webp
Normal file
After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 42 KiB |
BIN
content/blog/kde-april2023/passive.webp
Normal file
After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 19 KiB |
BIN
content/blog/kde-april2023/profile-posts.webp
Normal file
After Width: | Height: | Size: 7.8 KiB |
Before Width: | Height: | Size: 13 KiB |
BIN
content/blog/kde-feb2023/adding-polls.webp
Normal file
After Width: | Height: | Size: 5.7 KiB |
Before Width: | Height: | Size: 18 KiB |
BIN
content/blog/kde-feb2023/broken-toolbar.webp
Normal file
After Width: | Height: | Size: 9.9 KiB |
Before Width: | Height: | Size: 32 KiB |
BIN
content/blog/kde-feb2023/fixed-toolbar.webp
Normal file
After Width: | Height: | Size: 14 KiB |
|
@ -8,6 +8,8 @@ tags:
|
||||||
- KDE
|
- KDE
|
||||||
- Qt
|
- Qt
|
||||||
toc: true
|
toc: true
|
||||||
|
series:
|
||||||
|
- My Work in KDE
|
||||||
---
|
---
|
||||||
|
|
||||||
# New Gamepad KCM
|
# New Gamepad KCM
|
||||||
|
@ -53,7 +55,7 @@ See the [merge request](https://invent.kde.org/network/tokodon/-/merge_requests/
|
||||||
|
|
||||||
You can now select the language you're posting in! You can't view a post's language or filter them from within Tokodon yet, but it's a start[^1]. Your preferred languages (chosen through the Languages & Formats KCM if you're using KDE) is shown at the top of the list.
|
You can now select the language you're posting in! You can't view a post's language or filter them from within Tokodon yet, but it's a start[^1]. Your preferred languages (chosen through the Languages & Formats KCM if you're using KDE) is shown at the top of the list.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
See the [merge request](https://invent.kde.org/network/tokodon/-/merge_requests/156).
|
See the [merge request](https://invent.kde.org/network/tokodon/-/merge_requests/156).
|
||||||
|
|
||||||
|
@ -61,7 +63,7 @@ See the [merge request](https://invent.kde.org/network/tokodon/-/merge_requests/
|
||||||
|
|
||||||
Tokodon has supported viewing polls already, but not posting them. Now you can create polls! All of your usual options are exposed, including some not even shown on Mastodon Web. This is not merged yet, but will be soon.
|
Tokodon has supported viewing polls already, but not posting them. Now you can create polls! All of your usual options are exposed, including some not even shown on Mastodon Web. This is not merged yet, but will be soon.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
See the [merge request]https://invent.kde.org/network/tokodon/-/merge_requests/152).
|
See the [merge request]https://invent.kde.org/network/tokodon/-/merge_requests/152).
|
||||||
|
|
||||||
|
@ -84,7 +86,7 @@ Now the badge will actually update based on your current notification count, hur
|
||||||
|
|
||||||
Last month I redid the Window Decoration KCM, and this month I did the same for the PulseAudio KCM, although the changes needed where much smaller.
|
Last month I redid the Window Decoration KCM, and this month I did the same for the PulseAudio KCM, although the changes needed where much smaller.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
See the [merge request](https://invent.kde.org/plasma/plasma-pa/-/merge_requests/157).
|
See the [merge request](https://invent.kde.org/plasma/plasma-pa/-/merge_requests/157).
|
||||||
|
|
||||||
|
@ -100,11 +102,11 @@ For qqc2-desktop-style, I [fixed an odd case where you could select text in Text
|
||||||
|
|
||||||
Oliver landed the Plasma Welcome redesign for 6.0, which looks _wonderful_. As with all great features, this has had it's fair share of technical issues which I had the pleasure of helping with, including fixing this bug:
|
Oliver landed the Plasma Welcome redesign for 6.0, which looks _wonderful_. As with all great features, this has had it's fair share of technical issues which I had the pleasure of helping with, including fixing this bug:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Embedded KCMs create and manage their own QML engine. Objects like `Kirigami.ApplicationWindow` are separated though, because that exists in Plasma Welcome's engine and not the KCMs, which breaks a lot of things including the global toolbar. I figured out that you can inject your own QML engine to KDeclarative, which will fix most of the issues. While doing this, I also simplified embedded KCMs in Plasma Welcome which will help developers creating their custom pages.
|
Embedded KCMs create and manage their own QML engine. Objects like `Kirigami.ApplicationWindow` are separated though, because that exists in Plasma Welcome's engine and not the KCMs, which breaks a lot of things including the global toolbar. I figured out that you can inject your own QML engine to KDeclarative, which will fix most of the issues. While doing this, I also simplified embedded KCMs in Plasma Welcome which will help developers creating their custom pages.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
There's one lingering issue -- KDeclarative get's a little bit too greedy with the engine we give it and will try to destroy it to due to `std::shared_ptr` shenanigans, but fixing it will require changes to the framework. Right now we work around the issue by not assigning a parent, but I want to look into changing that soon.
|
There's one lingering issue -- KDeclarative get's a little bit too greedy with the engine we give it and will try to destroy it to due to `std::shared_ptr` shenanigans, but fixing it will require changes to the framework. Right now we work around the issue by not assigning a parent, but I want to look into changing that soon.
|
||||||
|
|
||||||
|
@ -138,13 +140,13 @@ See the [new repository](https://invent.kde.org/websites/kdevelop-org).
|
||||||
|
|
||||||
I opened an MR to update the matrix.to redirector hosted at [go.kde.org](go.kde.org/matrix) which should include a link to open in NeoChat! Once it's merged, I plan on replacing the links on the KDE Community Wiki with these instead of the WebChat links we have now.
|
I opened an MR to update the matrix.to redirector hosted at [go.kde.org](go.kde.org/matrix) which should include a link to open in NeoChat! Once it's merged, I plan on replacing the links on the KDE Community Wiki with these instead of the WebChat links we have now.
|
||||||
|
|
||||||
See the [merge request](https://invent.kde.org/websites/go-kde-org/-/merge_requests/1).
|
<!-- See the [merge request](https://invent.kde.org/websites/go-kde-org/-/merge_requests/1). -->
|
||||||
|
|
||||||
# Changing Notification History Order
|
# Changing Notification History Order
|
||||||
|
|
||||||
This is something that's been sitting on my backlog, but I'm super annoyed about the ordering of notification history. Whenever I miss a notification[^4] I scroll through my notification history. However, it's ordered seemingly randomly, because it's sorted by "type and urgency". That's not too helpful because neither criteria is really exposed in the UI. I propose changing it to "Date" which makes way more sense for a history view of notifications.
|
This is something that's been sitting on my backlog, but I'm super annoyed about the ordering of notification history. Whenever I miss a notification[^4] I scroll through my notification history. However, it's ordered seemingly randomly, because it's sorted by "type and urgency". That's not too helpful because neither criteria is really exposed in the UI. I propose changing it to "Date" which makes way more sense for a history view of notifications.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
See the [merge request](https://invent.kde.org/plasma/plasma-workspace/-/merge_requests/2659).
|
See the [merge request](https://invent.kde.org/plasma/plasma-workspace/-/merge_requests/2659).
|
||||||
|
|
||||||
|
|
Before Width: | Height: | Size: 108 KiB |
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 46 KiB |
BIN
content/blog/kde-feb2023/post-lang.webp
Normal file
After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 154 KiB |
BIN
content/blog/kde-feb2023/pulse-pa.webp
Normal file
After Width: | Height: | Size: 63 KiB |
|
@ -8,6 +8,8 @@ tags:
|
||||||
- KDE
|
- KDE
|
||||||
- Qt
|
- Qt
|
||||||
toc: true
|
toc: true
|
||||||
|
series:
|
||||||
|
- My Work in KDE
|
||||||
---
|
---
|
||||||
|
|
||||||
This is a non-comprehensive list of all of the major work I've done for KDE this month of January. I think I got a lot done this month! I also was accepted as a KDE Developer near the start of the month, so I'm pretty happy about that.
|
This is a non-comprehensive list of all of the major work I've done for KDE this month of January. I think I got a lot done this month! I also was accepted as a KDE Developer near the start of the month, so I'm pretty happy about that.
|
||||||
|
|
62
content/blog/kde-june2023/index.md
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
---
|
||||||
|
title: "My work in KDE for June 2023"
|
||||||
|
date: 2023-06-30
|
||||||
|
draft: false
|
||||||
|
tags:
|
||||||
|
- Linux
|
||||||
|
- Open Source
|
||||||
|
- KDE
|
||||||
|
- Qt
|
||||||
|
toc: true
|
||||||
|
series:
|
||||||
|
- My Work in KDE
|
||||||
|
---
|
||||||
|
|
||||||
|
We're over halfway through year! This update is a bit smaller than usual, and more focused on applications than Plasma. This isn't for lack of time or trying, but I made a conceited effort to clear out my MR backlog - fixing more issues than piling on features. This uh... didn't really work out and I'll be trying the same goal next month. I'm also going to explain a little bit of the reasoning behind this month's changes, so maybe it could inspire or help someone.
|
||||||
|
|
||||||
|
# Tokodon
|
||||||
|
|
||||||
|
The Android build (and CI) for Tokodon was broken ever since I replaced the video player with libmpv, so I spent a good chunk of this month making sure it's working again. This was quite a doozy, but I feel much more confident about improving Craft blueprints in the future.
|
||||||
|
|
||||||
|
To anyone who is not familiar with Craft, it's a meta build system created by KDE which is also used for the GitLab CI. Craft and it's blueprints are written in Python. Blueprints describe how to build the application or library, and has easy utilities for working with existing build systems (AutoTools, CMake, Meson, etc). It may be a little daunting, but these blueprints are extremely easy to write and maintain. More importantly, Craft enables easy cross-compilation since it contains blueprints for the underlying libraries for KDE applications: OpenSSL, zlib, and so on.
|
||||||
|
|
||||||
|
The problem is that Tokodon is - to my knowledge - the first KDE application to use libmpv on Android so I needed to break a lot of new ground to make this happen.
|
||||||
|
|
||||||
|
## zlib
|
||||||
|
|
||||||
|
zlib is already included in the Android NDK, but for some reason they don't ship pkgconfig files for it (for anything, really). This is troublesome, not for libraries or applications that link to zlib directly but as it turns out, other pkgconfig files. Libraries like freetype declare dependencies for zlib, and in turn pkgconfig will complain that it can't "find zlib" although the library itself exists, but not the pkgconfig file for it. Very annoying, I have some ideas on how we could solve this but for now, we now build it again on Android.
|
||||||
|
|
||||||
|
## kirigami-addons
|
||||||
|
|
||||||
|
This is a really simple fix, QtMultimedia was missing as a dependency for this library. The new FullscreenMaximizeDelegate component uses QtMultimedia under the hood to display video, but it wasn't declared in the blueprint.
|
||||||
|
|
||||||
|
## libarchive
|
||||||
|
|
||||||
|
When using libarchive for Android, you might encounter this error when including any of it's header:
|
||||||
|
|
||||||
|
```
|
||||||
|
<put error here>
|
||||||
|
```
|
||||||
|
|
||||||
|
But a quick search revels that this bug has been known and fixed, but there wasn't a new enough version in it's blueprint. Now it's bumped to 3.6.2, which includes the fix.
|
||||||
|
|
||||||
|
## ffmpeg
|
||||||
|
|
||||||
|
Tokodon uses libmpv to display videos from Mastodon and other services, which are served over HTTPS. libmpv uses ffmpeg to fetch video data along with playback, but the blueprint for ffmpeg did not enable any TLS backend yet. Now it enables the OpenSSL backend, since we already build it.
|
||||||
|
|
||||||
|
## fribidi and mpv
|
||||||
|
|
||||||
|
Here's probably the weirdest and honestly harder-than-it-should-be error I had to fix.
|
||||||
|
|
||||||
|
Here's a rundown of the necessary changes:
|
||||||
|
* Enable zlib on Android. This is necessary because Android NDK doesn't ship a pkgconfig for their built-in zlib.
|
||||||
|
* Kirigami-addons needed the QtMultimedia dependency (because of the new FullscreenImageDelegate component).
|
||||||
|
* libarchive needs to be bumped to 3.6.2, because of an Android build bug fixed in more recent versions.
|
||||||
|
* Enable OpenSSL support in ffmpeg, so there is HTTPs playback support.
|
||||||
|
* Build fribidi and mpv statically.
|
||||||
|
|
||||||
|
# Gamepad KCM
|
||||||
|
|
||||||
|
# Misc
|
||||||
|
|
||||||
|
* Two sticker fixes stickers in NeoChat
|
Before Width: | Height: | Size: 67 KiB |
BIN
content/blog/kde-march2023/image.webp
Normal file
After Width: | Height: | Size: 16 KiB |
|
@ -8,6 +8,8 @@ tags:
|
||||||
- KDE
|
- KDE
|
||||||
- Qt
|
- Qt
|
||||||
toc: true
|
toc: true
|
||||||
|
series:
|
||||||
|
- My Work in KDE
|
||||||
---
|
---
|
||||||
|
|
||||||
Another month in the year, another collection of bugfixes and features I contributed to KDE!
|
Another month in the year, another collection of bugfixes and features I contributed to KDE!
|
||||||
|
@ -25,7 +27,7 @@ too, although I haven't gotten around to opening up a merge request for that.
|
||||||
I opened a doxyqml[^1] [merge request to make it's output more useful](https://invent.kde.org/sdk/doxyqml/-/merge_requests/16), by automatically
|
I opened a doxyqml[^1] [merge request to make it's output more useful](https://invent.kde.org/sdk/doxyqml/-/merge_requests/16), by automatically
|
||||||
adding import statement hints to the page:
|
adding import statement hints to the page:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
# Plasma 6 Porting
|
# Plasma 6 Porting
|
||||||
|
|
||||||
|
|
Before Width: | Height: | Size: 115 KiB |
BIN
content/blog/kde-may2023/bad-docs.webp
Normal file
After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 413 KiB |
BIN
content/blog/kde-may2023/better-qr-text.webp
Normal file
After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 101 KiB |
BIN
content/blog/kde-may2023/close-action.webp
Normal file
After Width: | Height: | Size: 7.8 KiB |
Before Width: | Height: | Size: 102 KiB |
Before Width: | Height: | Size: 212 KiB |
BIN
content/blog/kde-may2023/cuttlefish-select.webp
Normal file
After Width: | Height: | Size: 56 KiB |
Before Width: | Height: | Size: 441 KiB |
BIN
content/blog/kde-may2023/gnome-tokodon.webp
Normal file
After Width: | Height: | Size: 76 KiB |
Before Width: | Height: | Size: 67 KiB |
BIN
content/blog/kde-may2023/good-docs.webp
Normal file
After Width: | Height: | Size: 12 KiB |
|
@ -8,6 +8,8 @@ tags:
|
||||||
- KDE
|
- KDE
|
||||||
- Qt
|
- Qt
|
||||||
toc: true
|
toc: true
|
||||||
|
series:
|
||||||
|
- My Work in KDE
|
||||||
---
|
---
|
||||||
|
|
||||||
I can't believe it's already the end of May! This month turned out a little meatier than last month I think, but I still have a large backlog of merge requests and TODOs to go through.
|
I can't believe it's already the end of May! This month turned out a little meatier than last month I think, but I still have a large backlog of merge requests and TODOs to go through.
|
||||||
|
@ -16,19 +18,19 @@ I can't believe it's already the end of May! This month turned out a little meat
|
||||||
|
|
||||||
Now when there isn't enough space to display the QR code in the clipboard applet, there [is a clearer message of what to do next](https://invent.kde.org/plasma/plasma-workspace/-/merge_requests/2952).
|
Now when there isn't enough space to display the QR code in the clipboard applet, there [is a clearer message of what to do next](https://invent.kde.org/plasma/plasma-workspace/-/merge_requests/2952).
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
On the topic of QR codes, the menu [is now a menu of radio buttons and not checkboxes](https://invent.kde.org/plasma/plasma-workspace/-/merge_requests/2953) which didn't make sense because it's an exclusive option.
|
On the topic of QR codes, the menu [is now a menu of radio buttons and not checkboxes](https://invent.kde.org/plasma/plasma-workspace/-/merge_requests/2953) which didn't make sense because it's an exclusive option.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
There [is now a separator above the "Close" action](https://invent.kde.org/plasma/kwin/-/merge_requests/4132) in the window menu!
|
There [is now a separator above the "Close" action](https://invent.kde.org/plasma/kwin/-/merge_requests/4132) in the window menu!
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
I added [a metadata extractor for Krita files](https://invent.kde.org/frameworks/kfilemetadata/-/merge_requests/97), which means certain information about your Krita artwork can show up in Dolphin, Baloo and other programs that can take advantage of it! This includes helpful information such as canvas width, height and creation date.
|
I added [a metadata extractor for Krita files](https://invent.kde.org/frameworks/kfilemetadata/-/merge_requests/97), which means certain information about your Krita artwork can show up in Dolphin, Baloo and other programs that can take advantage of it! This includes helpful information such as canvas width, height and creation date.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Soon, the Language and Region settings [will support the `$LANGUAGE` environment variable](https://invent.kde.org/plasma/plasma-workspace/-/merge_requests/2946). This only affects users who did not configure the language explicitly from KDE, like those coming from another computing environment. We already supported loading your pre-existing language from `$LANG`. Included in that merge request is a fix that stops an erroneous warning message telling you that your language isn't supported, even though it clearly is.
|
Soon, the Language and Region settings [will support the `$LANGUAGE` environment variable](https://invent.kde.org/plasma/plasma-workspace/-/merge_requests/2946). This only affects users who did not configure the language explicitly from KDE, like those coming from another computing environment. We already supported loading your pre-existing language from `$LANG`. Included in that merge request is a fix that stops an erroneous warning message telling you that your language isn't supported, even though it clearly is.
|
||||||
|
|
||||||
|
@ -43,7 +45,7 @@ An applet name or path must be specified, e.g. --applet org.kde.plasma.analogclo
|
||||||
|
|
||||||
I proposed [making the icon name selectable](https://invent.kde.org/plasma/plasma-sdk/-/merge_requests/76), because I can't stop myself from clicking on it!
|
I proposed [making the icon name selectable](https://invent.kde.org/plasma/plasma-sdk/-/merge_requests/76), because I can't stop myself from clicking on it!
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Gamepad KCM
|
## Gamepad KCM
|
||||||
|
|
||||||
|
@ -63,7 +65,7 @@ Playing videos and GIFs should be less crashy, but with worse scrolling performa
|
||||||
|
|
||||||
You can now [change certain account preferences](https://invent.kde.org/network/tokodon/-/merge_requests/238), but the selection is limited due to lack of a proper API. These are preferences that were supported before, but now you can change them from within Tokodon.
|
You can now [change certain account preferences](https://invent.kde.org/network/tokodon/-/merge_requests/238), but the selection is limited due to lack of a proper API. These are preferences that were supported before, but now you can change them from within Tokodon.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
And a whole slew of smaller stuff, some which are appearing in the next bugfix release:
|
And a whole slew of smaller stuff, some which are appearing in the next bugfix release:
|
||||||
|
|
||||||
|
@ -72,7 +74,7 @@ And a whole slew of smaller stuff, some which are appearing in the next bugfix r
|
||||||
* The duplicate account [bug should _finally_ be fixed](https://invent.kde.org/network/tokodon/-/merge_requests/229)!
|
* The duplicate account [bug should _finally_ be fixed](https://invent.kde.org/network/tokodon/-/merge_requests/229)!
|
||||||
* [Fix icons on non-KDE environments, such as GNOME](https://invent.kde.org/network/tokodon/-/merge_requests/228).
|
* [Fix icons on non-KDE environments, such as GNOME](https://invent.kde.org/network/tokodon/-/merge_requests/228).
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
For the current and future contributors, I started working on better and more detailed documentation. The first two areas I covered was [timeline models](https://invent.kde.org/network/tokodon/-/merge_requests/237) and the [account classes](https://invent.kde.org/network/tokodon/-/merge_requests/236)!
|
For the current and future contributors, I started working on better and more detailed documentation. The first two areas I covered was [timeline models](https://invent.kde.org/network/tokodon/-/merge_requests/237) and the [account classes](https://invent.kde.org/network/tokodon/-/merge_requests/236)!
|
||||||
|
|
||||||
|
@ -101,17 +103,17 @@ I started [replacing the old foreach macro](https://invent.kde.org/education/kit
|
||||||
|
|
||||||
The [go.kde.org Matrix redirector update](https://invent.kde.org/websites/go-kde-org/-/merge_requests/1) is now merged, which I started in February. This means NeoChat is now preferred right below Element Web (which is still pointed towards https://webchat.kde.org/). Thanks to Thiago Sueto, [the Community Wiki has been updated already](https://invent.kde.org/teams/web/wiki-sites/-/issues/2#note_652483) and I sent two merge requests to update [kde.org](https://invent.kde.org/websites/kde-org/-/merge_requests/200) and the [footer](https://invent.kde.org/websites/hugo-kde/-/merge_requests/4).
|
The [go.kde.org Matrix redirector update](https://invent.kde.org/websites/go-kde-org/-/merge_requests/1) is now merged, which I started in February. This means NeoChat is now preferred right below Element Web (which is still pointed towards https://webchat.kde.org/). Thanks to Thiago Sueto, [the Community Wiki has been updated already](https://invent.kde.org/teams/web/wiki-sites/-/issues/2#note_652483) and I sent two merge requests to update [kde.org](https://invent.kde.org/websites/kde-org/-/merge_requests/200) and the [footer](https://invent.kde.org/websites/hugo-kde/-/merge_requests/4).
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
To finish off more February work, I got around to working on the two big pieces of API documentation improvements for KDE Frameworks 6. If you don't remember, I wanted to add import statements for components meant to be used in Qt Quick. Doxygen already gives us hints for C++ headers, so QML users shouldn't be left in the dust. For example, how are you even supposed to use this component?
|
To finish off more February work, I got around to working on the two big pieces of API documentation improvements for KDE Frameworks 6. If you don't remember, I wanted to add import statements for components meant to be used in Qt Quick. Doxygen already gives us hints for C++ headers, so QML users shouldn't be left in the dust. For example, how are you even supposed to use this component?
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
In order to accomplish this, subclasses of `QQuickItem` need to have their doc comments modified. The first library to get this treatment is plasma-framework, see the merge requests for [PlasmaCore](https://invent.kde.org/frameworks/plasma-framework/-/merge_requests/783), [PlasmaQuick](https://invent.kde.org/frameworks/plasma-framework/-/merge_requests/785) and [hiding ToolTipDialog](https://invent.kde.org/frameworks/plasma-framework/-/merge_requests/786).
|
In order to accomplish this, subclasses of `QQuickItem` need to have their doc comments modified. The first library to get this treatment is plasma-framework, see the merge requests for [PlasmaCore](https://invent.kde.org/frameworks/plasma-framework/-/merge_requests/783), [PlasmaQuick](https://invent.kde.org/frameworks/plasma-framework/-/merge_requests/785) and [hiding ToolTipDialog](https://invent.kde.org/frameworks/plasma-framework/-/merge_requests/786).
|
||||||
|
|
||||||
For regular QML-based components, doxyqml (the tool to auto-generate QML documentation, because Doxygen lacks support for the language) needed to spit these out too. The [merge request to add import statements](https://invent.kde.org/sdk/doxyqml/-/merge_requests/16) is cleaned up, the tests fixed and ready for final review!
|
For regular QML-based components, doxyqml (the tool to auto-generate QML documentation, because Doxygen lacks support for the language) needed to spit these out too. The [merge request to add import statements](https://invent.kde.org/sdk/doxyqml/-/merge_requests/16) is cleaned up, the tests fixed and ready for final review!
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
I also spent some time cleaning up [the Community wiki](https://community.kde.org/), which just means I roam around and make sure links aren't dead and the formatting looks nice. If you're interested in some wiki improvement, join us in [#kde-www](https://go.kde.org/matrix/#/#kde-www:kde.org) and the [Issue board](https://invent.kde.org/teams/web/wiki-sites/-/issues)!
|
I also spent some time cleaning up [the Community wiki](https://community.kde.org/), which just means I roam around and make sure links aren't dead and the formatting looks nice. If you're interested in some wiki improvement, join us in [#kde-www](https://go.kde.org/matrix/#/#kde-www:kde.org) and the [Issue board](https://invent.kde.org/teams/web/wiki-sites/-/issues)!
|
||||||
|
|
||||||
|
|
Before Width: | Height: | Size: 366 KiB |
BIN
content/blog/kde-may2023/krita-extract.webp
Normal file
After Width: | Height: | Size: 98 KiB |
Before Width: | Height: | Size: 226 KiB |
BIN
content/blog/kde-may2023/qr-radio.webp
Normal file
After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 65 KiB |
BIN
content/blog/kde-may2023/tokodon-prefs.webp
Normal file
After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 202 KiB |
BIN
content/blog/kde-may2023/webchat-matrix.webp
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
content/blog/level-csg/banner.webp
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
content/blog/level-csg/cube-render.webp
Normal file
After Width: | Height: | Size: 4.5 KiB |
BIN
content/blog/level-csg/cube-viewport.webp
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
content/blog/level-csg/hammer.webp
Normal file
After Width: | Height: | Size: 75 KiB |
340
content/blog/level-csg/index.md
Normal file
|
@ -0,0 +1,340 @@
|
||||||
|
---
|
||||||
|
title: "Graphics Dump: Easy level building with tiny_csg"
|
||||||
|
date: 2023-06-25
|
||||||
|
draft: true
|
||||||
|
summary: "Have you ever played Half-Life 2 and infatuated with it's odd, cubic level design like I am? Well, you can use it at home!"
|
||||||
|
tags:
|
||||||
|
- C++- Math
|
||||||
|
toc: true
|
||||||
|
series:
|
||||||
|
- Graphics Dump
|
||||||
|
---
|
||||||
|
|
||||||
|
I've always been fascinated with CSG, as someone who has spent some time messing around in Valve's Hammer and Unreal Editor. However, when trying to implement CSG level geometry in my engine there's surprisingly little information! Here's a CSG tutorial I wish existed, so maybe it can help you too.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Much of the example code is ripped out of my engine, but this should give you a good enough walk-through to replicate it on your own.
|
||||||
|
|
||||||
|
# Constructive Solid Geometry
|
||||||
|
|
||||||
|
CSG stands for [Constructive Solid Geometry](https://en.wikipedia.org/wiki/Constructive_solid_geometry), a helpful technique to create complex geometry using simple primitives and boolean operators. Using CSG, it's possible to create a model in a procedural, non-destructive fashion and has been a popular tool in game engines (and CAD applications).
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
I like video games, so naturally my first-hand experience with CSG is Source games. This is how much of the level geometry was built in those games:
|
||||||
|
|
||||||
|
.](hammer.webp)
|
||||||
|
|
||||||
|
# Integration
|
||||||
|
|
||||||
|
I wanted to use CSG in my game engine, but searching online you'll get very few tutorials or even discussion about integrating it. If you have the enough prerequisite math knowledge, you might be able to get something going but I was hoping for a ready-made solution I could integrate.
|
||||||
|
|
||||||
|
Fortunately, there is one fantastic and almost-all-batteries-included library created by [Luka Aleksić](https://github.com/laleksic) called [tiny_csg](https://github.com/laleksic/tiny_csg). It's written in C++, and is perfect for the brush building tooling I wanted. Since the library uses CMake, it was easy enough to vendor into my source tree as well.
|
||||||
|
|
||||||
|
## Defining brushes
|
||||||
|
|
||||||
|
Before we can start using tiny_csg, we need some way to define the brushes in our map first. Brushes are defined by a list of planes that enclose a primitive. I'm focusing on cube primitives in this article, so we only need to worry about six planes: _front, back, left, right, bottom and top_.
|
||||||
|
|
||||||
|
Let's define an array of eight points, which correspond to each corner of the cube. Here is the points visualized:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
And it can be defined like this in C++:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
std::array<float3, 8> points = {
|
||||||
|
float3{1, 1, -1}, // A
|
||||||
|
float3{1, 1, 1}, // B
|
||||||
|
float3{-1, 1, 1}, // C
|
||||||
|
float3{-1, 1, -1}, // D
|
||||||
|
float3{1, -1, -1}, // E
|
||||||
|
float3{1, -1, 1}, // F
|
||||||
|
float3{-1, -1, 1}, // G
|
||||||
|
float3{-1, -1, -1}, // H
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Now that we have the points of our cube well-defined, let's move onto defining each plane that we need. Here is a list of the planes, and the points they contain:
|
||||||
|
|
||||||
|
* **Front:** B, A, E, F
|
||||||
|
* **Back:** D, C, G, H
|
||||||
|
* **Top:** A, B, C, D
|
||||||
|
* **Bottom:** H, G, F, E
|
||||||
|
* **Right:**: C, B, F, G
|
||||||
|
* **Left:** A, D, H, E
|
||||||
|
|
||||||
|
(Note that the ordering is important, and should be _counter-clockwise_.)
|
||||||
|
|
||||||
|
Using a group of these four points, you can calculate the center of a plane:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
center = (a + b + c + d) / 4.0f;
|
||||||
|
```
|
||||||
|
|
||||||
|
However a plane isn't defined by only a center, you also need to calculate a normal which is used to find the face direction. This is easy to do however, first you need a function that gives you the normal of a triangle:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
float3 triangle_normal(float3 p1, float3 p2, float3 p3) {
|
||||||
|
return normalize(cross(p1 - p2, p1 - p3));
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Given a plane (a, b, c, d), you can calculate two triangles: (c, b, a) and (a, d, c). Finally, average the normals of those two triangles and you completed the plane!
|
||||||
|
|
||||||
|
## Using tiny_csg
|
||||||
|
|
||||||
|
Now that we have our list of planes defined, we can start building the level geometry using tiny_csg. This library will do the heavy lifting for us, and give us a list of triangles to render with.
|
||||||
|
|
||||||
|
First, create the world and set the void volume:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
csg::world_t world{};
|
||||||
|
world.set_void_volume(AIR);
|
||||||
|
```
|
||||||
|
|
||||||
|
There is no predefined volume types, so you need to define your own:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
constexpr csg::volume_t AIR = 0;
|
||||||
|
constexpr csg::volume_t SOLID = 1;
|
||||||
|
constexpr csg::volume_t WATER = 2;
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
Create a brush_t with `csg::world_t::add()` and set the planes and the kind of volume it is:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
auto brush = world.add();
|
||||||
|
brush->set_planes(get_planes()); // a list of the planes we created earlier.
|
||||||
|
brush->set_volume_operation(csg::make_fill_operation(SOLID));
|
||||||
|
```
|
||||||
|
|
||||||
|
Next, call `world_t::rebuild()` to begin the level building process. The result is faces and fragments we can then use to rasterize the brushes! It's a linked list, so to iterate it start with `world_t::first()` and get the next brush in the list using `world_t::next()`:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
csg::brush_t* brush = world.first();
|
||||||
|
if (brush) {
|
||||||
|
while (brush) {
|
||||||
|
...
|
||||||
|
brush = world.next(brush);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
To start looping through the triangles, use `brush_t::get_faces()` and loop through the faces, and then the fragments of said faces:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
auto faces = brush->get_faces();
|
||||||
|
for (csg::face_t& face : faces) {
|
||||||
|
for (csg::fragment_t& fragment : face.fragments) {
|
||||||
|
const auto triangles = csg::triangulate(fragment);
|
||||||
|
|
||||||
|
// process the triangles...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You'll want to discard any fragments that are touching two of the same volume type. For example, air touching air would contribute a lot of unnecessary geometry:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
const csg::volume_t front = fragment.front_volume;
|
||||||
|
const csg::volume_t back = fragment.back_volume;
|
||||||
|
|
||||||
|
// Discard air/air and solid/solid polygons
|
||||||
|
if (front == back)
|
||||||
|
continue;
|
||||||
|
```
|
||||||
|
|
||||||
|
Now let's deep dive into how to process these triangles, and some pitfalls you might run into.
|
||||||
|
|
||||||
|
## Triangulation
|
||||||
|
|
||||||
|
tiny_csg will triangulate the brush fragments for you, but it's up to you to calculate things like texture coordinates, normal, tangents/bitangents and more. Those are all pretty easy though, so let's start with normals.
|
||||||
|
|
||||||
|
### Normals
|
||||||
|
|
||||||
|
To calculate normals, you can just take the normal of the plane the face is part of:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
for (auto triangle : triangles) {
|
||||||
|
float3 normal = face.plane->normal;
|
||||||
|
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
However, make sure that when the back is a void volume (like `AIR`), that you _flip_ the normal:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
for (auto triangle : triangles) {
|
||||||
|
float3 normal = face.plane->normal;
|
||||||
|
if (back == AIR) {
|
||||||
|
normal = -normal;
|
||||||
|
}
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
When you intersect a solid with a void brush, the new geometry created to fill the hole is calculated _from_ the void brush - meaning that the normals are facing the wrong direction. It's technically correct for the void brush, but not the hole.
|
||||||
|
|
||||||
|
If you are building an index list, I think you also want to flip it as well but it might not be needed.
|
||||||
|
|
||||||
|
### Texture coordinates
|
||||||
|
|
||||||
|
Texture coordinates are relatively easy to calculate as well, first set up a loop iterating through all the triangles.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
for (std::size_t i = 0; i < indices.size(); i += 3) {
|
||||||
|
const float3 v1 = vertices[indices[i]].position;
|
||||||
|
const float3 v2 = ...;
|
||||||
|
const float3 v3 = ...;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Calculate a face normal, and also check if all of the components do not equal zero:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
float3 normal = cross(v3 - v1, v2 - v1);
|
||||||
|
if (normal.x == 0.0f && normal.y == 0.0f && normal.z == 0.0f) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Next we want to create a rotation that looks "towards" this normal:
|
||||||
|
|
||||||
|
```
|
||||||
|
Quaternion rotation = look_rotation(float3{0, 0, 1}, normal);
|
||||||
|
```
|
||||||
|
|
||||||
|
Then for each vertex in this triangle, we can multiply it component-wise to create a pretty nice distribution. I included an extra `scale` variable that I can adjust on the whole brush.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
vertices[indices[i]].uv = float2{(rotation * v1).x, (rotation * v1).y} * scale;
|
||||||
|
vertices[indices[i + 1]].uv = float2{(rotation * v2).x, (rotation * v2).y} * scale;
|
||||||
|
vertices[indices[i + 2]].uv = float2{(rotation * v3).x, (rotation * v3).y} * scale;
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tangents and bitangents
|
||||||
|
|
||||||
|
This one is a little bit more difficult, and I ended up going with a widely used library called Mikktspace to calculate these. It's interface is a little obtuse at first too. You need to create a `SMikkTSpaceInterface` and assign a bunch of functions it will use to grab and store triangle data. I used lambdas for these, and assigning userdata:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
SMikkTSpaceInterface iface = {};
|
||||||
|
iface.m_getNumFaces = [](const SMikkTSpaceContext *ctx) -> int {
|
||||||
|
const CSGMesh *m = static_cast<const CSGMesh *>(ctx->m_pUserData);
|
||||||
|
return m->indices.size() / 3;
|
||||||
|
};
|
||||||
|
|
||||||
|
iface.m_getNumVerticesOfFace = [](const SMikkTSpaceContext *, const int) -> int {
|
||||||
|
return 3;
|
||||||
|
};
|
||||||
|
|
||||||
|
iface.m_getNormal = [](const SMikkTSpaceContext *ctx, float normals[],
|
||||||
|
const int iface, const int ivert) {
|
||||||
|
const CSGMesh *m = static_cast<const CSGMesh *>(ctx->m_pUserData);
|
||||||
|
|
||||||
|
int i = iface * 3 + ivert;
|
||||||
|
memcpy(normals, m->vertices[i].normal.ptr(), sizeof(float3));
|
||||||
|
};
|
||||||
|
|
||||||
|
iface.m_getTexCoord = [](const SMikkTSpaceContext *ctx, float texcoords[],
|
||||||
|
const int iface, const int ivert) {
|
||||||
|
const CSGMesh *m = static_cast<const CSGMesh *>(ctx->m_pUserData);
|
||||||
|
|
||||||
|
int i = iface * 3 + ivert;
|
||||||
|
memcpy(texcoords, m->vertices[i].uv.ptr(), sizeof(float2));
|
||||||
|
};
|
||||||
|
iface.m_getPosition = [](const SMikkTSpaceContext *ctx, float position[],
|
||||||
|
const int iface, const int ivert) {
|
||||||
|
const CSGMesh *m = static_cast<const CSGMesh *>(ctx->m_pUserData);
|
||||||
|
|
||||||
|
int i = iface * 3 + ivert;
|
||||||
|
memcpy(position, m->vertices[i].position.ptr(), sizeof(float3));
|
||||||
|
};
|
||||||
|
iface.m_setTSpaceBasic = [](const SMikkTSpaceContext *ctx, const float tangent[], const float sign,
|
||||||
|
const int iface, const int ivert) {
|
||||||
|
const CSGMesh *m = static_cast<const CSGMesh *>(ctx->m_pUserData);
|
||||||
|
|
||||||
|
int i = iface * 3 + ivert;
|
||||||
|
float4 t{tangent[0], tangent[1], tangent[2], sign};
|
||||||
|
memcpy(m->vertices[i].tangent.ptr(), t.ptr(), sizeof(float4));
|
||||||
|
};
|
||||||
|
```
|
||||||
|
(This code could absolutely be improved!)
|
||||||
|
|
||||||
|
After you created your interface, you can then create a context and assign your userdata and then start the tangent process.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
SMikkTSpaceContext ctx;
|
||||||
|
ctx.m_pUserData = &cgMesh;
|
||||||
|
ctx.m_pInterface = &iface;
|
||||||
|
genTangSpaceDefault(&ctx);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Bullet
|
||||||
|
|
||||||
|
It's also really easy to plug the triangle data straight into Bullet too, if that's the physics engine you're using. Here's the snippet if you also want Bullet integration:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
csg::brush_t* brush = scene.world->first();
|
||||||
|
if (brush != nullptr) {
|
||||||
|
while (brush) {
|
||||||
|
auto faces = brush->get_faces();
|
||||||
|
for (csg::face_t& face : faces) {
|
||||||
|
for (csg::fragment_t& fragment : face.fragments) {
|
||||||
|
const csg::volume_t front = fragment.front_volume;
|
||||||
|
const csg::volume_t back = fragment.back_volume;
|
||||||
|
|
||||||
|
if (front == back) // discard air/air and solid/solid polygons.
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const auto triangles = csg::triangulate(fragment);
|
||||||
|
|
||||||
|
for (auto triangle : triangles) {
|
||||||
|
brushes_positions.push_back(fragment.vertices[triangle.i].position.x);
|
||||||
|
brushes_positions.push_back(fragment.vertices[triangle.i].position.y);
|
||||||
|
brushes_positions.push_back(fragment.vertices[triangle.i].position.z);
|
||||||
|
brushes_indices.push_back(brushes_indices.size());
|
||||||
|
|
||||||
|
brushes_positions.push_back(fragment.vertices[triangle.j].position.x);
|
||||||
|
brushes_positions.push_back(fragment.vertices[triangle.j].position.y);
|
||||||
|
brushes_positions.push_back(fragment.vertices[triangle.j].position.z);
|
||||||
|
brushes_indices.push_back(brushes_indices.size());
|
||||||
|
|
||||||
|
brushes_positions.push_back(fragment.vertices[triangle.k].position.x);
|
||||||
|
brushes_positions.push_back(fragment.vertices[triangle.k].position.y);
|
||||||
|
brushes_positions.push_back(fragment.vertices[triangle.k].position.z);
|
||||||
|
brushes_indices.push_back(brushes_indices.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
brush = scene.world->next(brush);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto *index_vertex_array = new btTriangleIndexVertexArray(brushes_indices.size() / 3,
|
||||||
|
const_cast<int *>(reinterpret_cast<const int *>(brushes_indices.data())),
|
||||||
|
sizeof(int) * 3,
|
||||||
|
brushes_positions.size(),
|
||||||
|
const_cast<btScalar *>(brushes_positions.data()),
|
||||||
|
sizeof(btScalar) * 3);
|
||||||
|
|
||||||
|
const bool quantized_aabb_compression = false;
|
||||||
|
auto *shape = new btBvhTriangleMeshShape(index_vertex_array, quantized_aabb_compression);
|
||||||
|
```
|
||||||
|
|
||||||
|
# Results
|
||||||
|
|
||||||
|
The result is pretty amazing, here is a quick walk-through of a level I made using CSG:
|
||||||
|
|
||||||
|
{{< tube "https://tube.ryne.moe/videos/embed/b11d3bb5-cec9-4109-8a01-bc9b0e60d92c" >}}
|
||||||
|
|
||||||
|
# Credits
|
||||||
|
|
||||||
|
* Luka Aleksić for the amazing tiny_csg library!
|
||||||
|
* This [StackExchange answer](https://gamedev.stackexchange.com/a/147030) by "DMGregory" which suggested using Mikktspace for creating tangent and bitangent vectors.
|
||||||
|
* This [StackExchange answer](https://gamedev.stackexchange.com/a/139955) by "DMGregory" again which suggested how to calculate texture coordinates for arbitrary triangles.
|
||||||
|
|
||||||
|
|
BIN
content/blog/level-csg/positions.webp
Normal file
After Width: | Height: | Size: 15 KiB |
|
@ -2,10 +2,9 @@
|
||||||
title: "Graphics Dump: Mesa, Vulkan and DRM"
|
title: "Graphics Dump: Mesa, Vulkan and DRM"
|
||||||
date: 2022-11-15
|
date: 2022-11-15
|
||||||
draft: false
|
draft: false
|
||||||
tags:
|
blogtags:
|
||||||
- Vulkan
|
- Vulkan
|
||||||
- Linux
|
- Linux
|
||||||
- Graphics Dump
|
|
||||||
- KDE
|
- KDE
|
||||||
comments:
|
comments:
|
||||||
host: mastodon.art
|
host: mastodon.art
|
||||||
|
|