Some weeks ago I started making a ship for based on NZ-43 (14C) and while I was happy with the basic result it helped me mostly to get an understanding how such ships were made.

I never cared much about ships before and had absolutely no idea about this topic at all. During my research to close that gap, which consisted mostly of reading into Flevobericht, 331 (ISBN 9036910862) and following the awesome actual model builds by @silverman834 at https://modelshipworld.com/topic/25300-a-small-cog-by-silverman834-scale-120-c-1410-finished/ did I decide to reapply the lessons learned and go for the famous so called “Bremen Cog” ship.

This is a very specific ship type that was widely used from the 12th century on for trade via the sea. It was between 15 to 25 meters long, had one square sail and was crewed by approximate 12 people that worked in shifts. Unlike other sailing ships it was very reliant on wind direction but it’s flat bottom permitted it to be beached without taking damage so it could be unloaded everywhere e.g. during low tide. The aftercastle on it’s stern deck makes for a very distinct impression and can be recognized from many period depictions of ships. Several full sized replica have been built to this date.

The “Bremen Cog” is the best preserved example of this ship type. It’s wreck from ~1380 was discovered in the Weser in 1962. It took almost 40 years until it could be presented to the public. It’s my understanding that the conservation techniques applied had to be developed first. It’s on display in the German Maritime Museum and the cog apparently played a huge role in the founding of the museum itself: https://www.dsm.museum/en/exhibition/exhibitions/bremen-cog

The museum is located in 27568 Bremerhaven / Germany (and closed during the wintertime). No idea when I’ll get the chance to visit this but the museum does also foster a YouTube channel and one of the most recent videos up on their channel is a drone flight around and through the Bremen Cog made by Dennis Vogt so make sure to check this out: https://www.youtube.com/watch?v=KjYigiyF014

So why did I take this effort on me to recreate this famous ship type once more in a game? Curiosity and hyper focus I guess. I can tell you it was cumbersome because the game does not support bend objects so everything is basically a block. It does also not feature tools for e.g. Bézier curves or something. I spent quite some hours trying to get the hull and proportions right and while the superstructures are kinda opinionated I’m really happy with the end result. I also saved a lot on the ropes and while I know in theory how this should work and look it’s just _too much work_ to get this right in the game. So I only went with the most important running ropes. I hope any navigators reading this can forgive me 😉

The final result of the Bremen Cog under sails

I also prepared a variant without a sail and maybe I’ll even create a version with a reefed sail and with another version of the bow someday. I don’t know yet for I feel very exhausted from this little side project for now.

The final result of the Bremen Cog without sails

It’s also very sparse on final details because I can imagine that the blueprints for this ship will see a lot of reuse on various servers of the game so it should be really easy to individualise each placement with different colours or textures or cargo.

Speaking of: The blueprints for Rising World (Unity) can be downloaded from here:

You’re free to share and adapt this work as indicated by the CC-BY-SA 4.0 license. Please do inform me if you make use of this simply because that would make me very happy. It’s not required though.

Working on a loosely based on NZ-43 (14C). It’s approximately 12m long 🙂

First time I’m trying my luck with a vessel and not a building. The curving is difficult to realise in tho.

It started life in the old Java version of the game because the new Unity version has no posters yet. I had to segment the plan of the cog (carved in a very bad resolution from a PDF) into several in-game posters that had to be aligned in-game again to get the proper measurements.

After that I moved the blueprint of the frame over to the new version and started putting planks on it. A cumbersome process during which I learned a lot. I’ll probably make another and more improved hull based on the gathered know how.

I also fell straight into another “not yet implemented” trap. RisingWorld has a flip command to mirror an object and I kinda assumed this would work with blueprints too. It does not. And I was really not looking forward to put plank on both sides of the frame.

Luckily most of the leg work to read the binary blueprints was done by @paulevs before who released https://github.com/paulevsGitch/BlueLib under the MIT license. It has been a while that I touched Java but I could come up with some code of my own that would flip the planks only (I used rounded cubes for the planks exclusively) making use of this lib and the very first try at it looked promising already.

Here is the source I came up with in case you wonder:

package blueprint.flip.maybe;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;

import paulevs.bluelib.blueprint.Blueprint;
import paulevs.bluelib.blueprint.BlueprintIO;
import paulevs.bluelib.blueprint.element.BlueprintElement;
import paulevs.bluelib.blueprint.element.BlueprintElementType;

public class App {
    public static BlueprintElement cloneBlueprintElement(BlueprintElement el) {
        BlueprintElement element = new BlueprintElement(el.type);
        element.setPosition(el.posX, el.posY, el.posZ);
        element.setSize(el.sizeX, el.sizeY, el.sizeZ);
        element.rgba = el.rgba;
        element.setRotation(el.rotX, el.rotY, el.rotZ, el.rotW);
        element.setSurfaceOffset(el.surfaceOffsetX, el.surfaceOffsetY, el.surfaceOffsetZ);
        element.texture = el.texture;
        return element;
    }

    public static Blueprint readBlueprint(String pathname) {
        File file = new File(pathname);
        Blueprint blueprint = null;
        try {
            blueprint = BlueprintIO.read(file);
        }
        catch (IOException exception) {
            exception.printStackTrace();
        }

        return blueprint;
    }

    public static void main(String[] args) {
        final Blueprint blueprint = App.readBlueprint("/path/to/Blueprint-flip-maybe/cog_base_split_1670726695.blueprint");
        System.out.println("Opened " + blueprint.name);
        System.out.println("Blueprint has " + blueprint.elements.size() + " elements");

        ArrayList<BlueprintElement> elements = new ArrayList<BlueprintElement>();

        blueprint.elements.forEach(element -> {
            if(element.type == BlueprintElementType.ROUNDED_BLOCK) {
                System.out.println("T: " + BlueprintElementType.getElementName(element.type));
                System.out.println("pX: " + element.posX + "pY: " + element.posY + "pZ: " + element.posZ + " rX: " + element.rotX + "rY: " + element.rotY + "rZ: " + element.rotZ);
                BlueprintElement el = App.cloneBlueprintElement(element);
                el.posX = el.posX * -1;
                el.rotY = el.rotY * -1;
                el.rotZ = el.rotZ * -1;
                System.out.println("pX: " + el.posX + "pY: " + el.posY + "pZ: " + el.posZ + " rX: " + el.rotX + "rY: " + el.rotY + "rZ: " + el.rotZ);
                elements.add(el);
            }
        });

        elements.forEach(element -> {
            blueprint.elements.add(element);
        });

        blueprint.name += "_flipped_X";

        File outputFile = new File("/path/to/Blueprint-flip-maybe/" + blueprint.name + ".blueprint");
        try {
            BlueprintIO.write(blueprint, outputFile);
        }
        catch (IOException exception) {
            exception.printStackTrace();
        }
    }
}

I’m kinda happy with the result. This Lib also allows me to change the texture of the elements so I don’t have to worry how the used texture during the construction may look in the end.

Now onwards to improve the curves. I really wish for a bend mode where the beginning would snap on to an existing object and the opposite plane could be moved around individually.

With (Unity) improving a lot lately we’re feature wise almost on par with the old Java version again. Due to my hobbies I’m playing on the server https://medievalrealms.co.uk/ where I usually construct buildings based on specific periods according to my understanding of timber-framed constructions. Which may not be the best to rely on but hey, it’s a game after all.

One of the features still missing is an ingame map. We do have the compass already though and with debug enabled we even get an exact position on the current map. And the new maps are huge! And since we’re building here in multiplayer it’s no wonder that this is a dire missed feature to get an idea where the others are and what they are building, because it’s not fun navigating with X,Y,Z alone to visit other players (and keep note of where the own spot is located).

So I was intrigued to see that the player @Bamse did what gamers tend to do when a feature is missing. They start some sort of helper app (or wiki or whatever). This resulted in a Cloud map project at https://qgiscloud.com/Bamse/MapMedievalRealms/ where players from the same server may add POIs and do the leg work of surveying the “new” world.

The only drawback (haha. sorry.) is: It’s a PITA to do the surveying because stopping every few meters to note down a bunch of coordinates takes hours! Someone had to do this though, because “my” isle – a piece of rock I randomly stumbled over after the latest server reset – was still missing! And while I clocked roughly ~700h on this game already I was not going to do that. I’m a programmer – which equals to lazy in my opinion. So I started scripting and after a few minutes came up with the following still crude solution:

echo "" > move.log
while true; do
	gnome-screenshot -w -f /tmp/snapshot.png && convert /tmp/snapshot.png -crop 165x30+905+975 /tmp/snapshot-cropped.tiff && tesseract /tmp/snapshot-cropped.tiff - -l eng --psm 13 quiet | awk 'match($0, /([[:digit:]]+[.][[:digit:]])+.([[:digit:]]+[.][[:digit:]]+).([[:digit:]]+[.][[:digit:]]+)/) { print substr($0, RSTART, RLENGTH)}' | awk '{ printf "%0.0f,%0.0f,%0.0f\n", $1, $2, $3}' >> move.log 
	sleep 2
done

This surely can be improved a lot but… minimum viable product. We’re still talking about a game. Here is what it does:

* Take a screenshot of the active window (Rising World while playing)

* Save it to /tmp (that’s in my RAM disk)

* Crop out the coordinates and convert it to tiff using `imagemagick`

* Run `tesseract` for OCR detection

* Pipe the result to awk and use a RegEx to identify three numbers

* Reformat the 3 numbers (remove the precision) and dump it in as csv-like log

* Sleep for 2 seconds and repeat until terminated

And in case you wonder why I used gnome-screenshot: I’m on and the usual suspects written for X do simply not work. I did recompile gnome-screenshots tho to disable the annoying flashing though so it’s silent now.

Why the awk program? Well, tesseract is good but the raw data looked something like this in the end and the RegEx cleans that up somewhat:

serene ep)
9295.2 95.4 2828.0 |
9295.2 95.4 2828.0 |
9296.4 95.4 2828.5 |
nn
9303.1 95.4 2838.5 |
9295.0 98.4 2857.65
9289.1 98.7 2868.1 (7
9296.5 96.7 2849.0 |»
9301.1 95.4 2835.5 |
9301.1 95.4 2835.5 |
nn

So I put this to a test and jogged around “my” isle and here are the results:

One(!) data point was misread during the ~15 minutes run. Not too shabby! That could easily be fixed manually and who knows… mebbe I’ll improve on the script to check for implausible spikes like that at some point.

I demoed the script to other players on the same server and some already started investigating into solutions to adapt this script to Windows. Just don’t ask me how to do that – I really wouldn’t know 😛

Updated 10th Dec 2022: A solution to do the same on Windows PC appeared on https://wiki.calarasi.net/en/public/medievalrealms/ocr-coordinates

Adulted much and did lots of mundane tasks in the house recently. Including some plumbing that was really overdue. Water starts to become my arch nemesis in our ageing building and leaks really do not improve over time.

On my list were 4 siphons, that needed replacement, a shower hose and a shower head. Some of this stuff was only held together by varnish and replacing all this was a mess.

Anyway, what I find really great is that some of the required parts could be obtained in environmental friendly packaging. Namely the shower head and three of the siphons. No further plastic was in the cartons. Really appreciate this.

Sadly that was not the case for the shower hose and the new kitchen armature. Especially the last came with formed foam. That could really do without too.

Oh yeah and remember the unexpected findings in the washing machine from https://beko.famkos.net/2022/06/21/6686269/ ? This time it was Siemens 0, Grass Trimmer 7 and I really wonder “will they ever learn?” This time the sieve was toast and needed replacement 😠

Bagged on GoG today and had a blast. Needed some fiddling to get my X52 Pro up and running since the game only supports _one_ Gamepad but that’s nothing that would stop my 😂

Video: Flight tutorial snippets from Rebel Galaxy Outlaw played on Linux PC

Update: I found _way later_ that there is a switch in the launcher to enable joystick support (which brings up my X52 just fiine). What a strange design decision.

A lot happened since my last update on the simpit – under it’s hood. Function wise it changed not so much so the older demonstration video is still better for a quick demo. I still assembled a new video from clips of the first evening with the new hardware:

Quick trip from Armstrong Orbital over to the huge crater on HIP 117029-4 and back

So what changed? I got rid of the CY-822A USB joystick controller that, while good, was also limiting. Especially in inputs and how they would react. The Raspberry Pi, that I used to drive the status indicators, is also gone. This is all replaced by one single Arduino Mega that is connected via serial over USB.

A custom joystick daemon written in Rust is listening for data from the and feeds back the flags of Elite Dangerous to drive the blinken lights. I also extended the source to add me some rotary encoders (with push button function) and I’m very happy with the result of this. That makes a whopping amount of 48 buttons and 6 axis (where 2 axis make one hat). And it feels _so good_ to have e.g. self destruct or eject cargo save under a protective cover now 😀

The panel also got an external PSU with enough ampere to drive as many LED as I may imagine so I no longer abuse a phone charger for that or risk frying of the PCB / USB.

With all that in place I streamlined my pre-flight check-list quite a lot because way less hardware is involved and most of this is automated by now. It wasn’t all fun n giggles tho and while the new hard- and software “just worked” in e.g. it was that gave me a hard time to actually use most of the new buttons.

Getting all the precious buttons into Elite as well (okay, limited to 32 thanks to an old dinput library but who is counting at this point – will simply set the rest to keyboard macros instead)

Turns out it had no idea about the device and model identifiers reported by the joystick daemon and that the kernel assumed a gamepad based on declaring e.g. ButtonNorth via the more recent xinput system really didn’t help – because that limited the amount of read buttons via xinput severe! In the end I set it’s identifier to a “vJoy” device. That I found in the DeviceMappings.xml of Elite and since this could be basically anything I gave it a try (and removed all “offending” magic gamepad buttons from the code) and sure enough Elite started accepting the inputs as expected and from there it was smooth sailing – got even the hat working.

Oh and for everyone who is wondering what exactly they are seeing on the “MFD” when I’m playing Elite: That’s basically a Website using the FUI framework. Find a quick demo video here. Without the cardboard covering up parts of the screen it looks basically like this:

I also started doodles for a version 2 – now that I have an idea what I really want.

Plans for another based on a Valkyrie Cockpit

Visited Blühendes Barock Ludwigsburg, 71640 Germany. Well, wasn’t much bloom going on any more. The dry summer shows everywhere.

Anyway, did see a lot of interesting stuff, like sand sculptures, a switchboard from 1959, fishes and exotic birds (well, exotic to me), a fable park, that the kids loved and at the end we also visited Circus Roncalli (https://www.roncalli.de/). A circus without animals, mind you. And it was awesome! We really had a blast. I may have blisters from applauding 🙃

Yesterday I tried various ships in StarCitizen (some “free” event). I launched at Everus Harbor, a station orbiting Everus. When I tested the Dragonfly Black it became old fast circling around on the small landing pad so… why should I now attempt an atmospheric decent instead?

(Sorry for the extra shaking – I had my DIY headtracker on :D)

StarCitizen – Dragonfly Diving (on Linux PC)

Fly Dangerous 5.0 was released and it is packed with new features like reflections on the ship, Steam leaderboards and ghosts! So you can basically race against yourself or others from the leaderboards!

As usual I gave it a spin and had a blast.

First things first though. The Linux version defaults to OpenGL and this resulted in like 25 FPS for me and the input of my X52 Pro (mapped as XBOX controller) was so laggy that I could sip coffee during each course correction. This was when I remembered the magic parameter -force-vulkan from other Unity games and from here it was smooth sailing. Eventually I ended up with the game start options obs-gamecapture for recording, mangohud for some FPS info and -force-vulkan for… well, FPS.

obs-gamecapture mangohud %command% -force-vulkan

As usual YMMV.

The flight mechanics changed a little bit and @jayleefaulkner explains this in great detail in the video Alpha 7: https://www.youtube.com/watch?v=I2sn26HVY7o so I’m having a learning curve ahead of me… again!

Anyway, I’m not doing too bad after some rounds. Only issue left is that I can not disable the flight assist for some reasons. Probably a bad binding but I was eager to play so I went with it.

Fly Dangerous (on Linux PC) – with Vulkan

Oh and did I mention that this little gem is still for free and even opensource?

I want more control over what my microphone picks up on screen share in video conferences or during streaming but I don’t want to buy a hardware mixer. I also want to be able to disable the microphone with a hotkey but it doesn’t have any physical switch. So achieve all this I utilise PipeWire to run a bunch of virtual devices that I can control via pavucontrol and obs later. Video conferences get this as “default device” so they don’t get a chance to mess up my audio setup (looking at you Teams). The steps are the same for PulseAudio if you don’t have PipeWire (yet).

#!/bin/sh
# setup virtual device intended for monitoring
pactl load-module module-null-sink sink_name="BekoBlaster" device.icon_name="audio-card-analog" node.nick="BekoBlaster" node.description="BekoBlaster-16" sink_properties=device.description="BekoBlaster-16"
# setup virtual MIC so intended monitoring device can be recorded from as MIC
pactl load-module module-remap-source master="BekoBlaster.monitor" node.nick="BekoMic" device.icon_name="audio-input-microphone" source_name="BekoMic-16" source_properties=device.description="BekoMic-16"
# IMPORTANT:
# RUN `pavucontrol` => Select Tab Record => Set BekoMic-16 input to "Monitor of BekoBlaster-16"

The 16 is not important. It’s just my kind of humour as my first Linux PC had a SoundBlaster16 😛 It also is a pattern sufficient enough so I don’t mix this up with the zoo of real microphones or audio sinks attached to my computer.

This is already sufficient enough so that everything played on the device BekoBlaster-16 can be recorded on the BekoMic-16 again, that I select as input microphone for Browser (video conferences) or Discord at this point. This can be done with pavucontrol – or later in obs.

This isn’t enough, of course. In case of e.g. playing music (or streaming a game) I’d also want to hear the sound myself too. For this I create an additional null sink and a combined sink. With this approach I can later fine tune in obs what gets recorded to which audio track (where audio track 1 is the one used for streaming) and what ends up on the BekoBlaster-16, that acts as my monitor and due to the remapped source also as virtual mic.

# setup virtual device for games (or whatever OBS should record)
pactl load-module module-null-sink sink_name="OBS-Blaster" device.icon_name="audio-card-analog" node.nick="OBS-Blaster" node.description="OBS-Blaster" sink_properties=device.description="OBS-Blaster"
# OPTIONAL setup a combined sink so I can enjoy game sound while OBS gets a copy
pactl load-module module-combine-sink slaves="OBS-Blaster,bluez_output.10_4F_A8_84_18_01.a2dp-sink" node.nick="OBS-Blaster-AND-Headphones" node.description="OBS-Blaster-AND-Headphones" sink_properties=device.description="OBS-Blaster-AND-Headphones"
# Important tools to manipulate: `pw-cli list-objects`, `pw-cli destroy $id`, `pactl list short | grep module`, `pactl unload-module $id`

With this (and my headset connected) it starts to get crowded in my device list.

As you can hear err… hopefully see: The sink OBS-Blaster-AND-Headphones is now selected for playing music which results in the music being played on the next virtual sink OBS-Blaster and my h.ear (MDR-100ABN) headphones. The same could be done with the BekoBlaster-16, of course, but bear with me. We still don’t have any real microphone added to the mix and while this can be done with PipeWire or PulseAudio alone too I need this usually with video included too so obs it is.

Here the most important setting is the monitoring device, which is the BekoBlaster-16 from the beginning, that can be used as microphone in e.g. Discord later again.

Next is the set-up of the mixer where I’m interested in 4 devices only:

  • The BekoMic-16 without monitor (it is the monitor so this would result in an echo chamber) and optional track 5 for recording (so I’ll know later how the mix sounded – but this is never used for video editing later).
  • The desktop audio without monitor, so random system sounds (or other Discord voices!) don’t make it to any stream. It can be recorded on it’s own track tho in case I fcked up or need a reference later on during editing.
  • The Mic/Aux, which represents the real microphone used. It is echoed on the monitor microphone and on track 1 (send to my streaming server) and on track 2 so I have a separate microphone track later to work with in post edit.
  • The OBS-Blaster, which usually represents the game I’m playing. It is echoed on the monitor microphone and on track 1 (send to my streaming server) and on track 4 so I have a separate game/music track later to work with in post edit.

This way I can control in great detail what ends up on the Discord / a video conference / game streaming, while I get the full power of obs scenes (where I also do my greenscreen mixing), mute microphones as I see fit and have some material to work with later when I decide to make a video on stuff. Here I did set up Discord to read from the virtual BekoMic-16 and output to my headphones only (where no recording in OBS is done) – so perfect for most Discord / video conference sessions.

Don’t mind the flipped video preview. That’s perfectly fine and will look right for the viewers later. This is by the way the virtual camera sink feature of obs and the v4l2loopback kernel driver that I also read from in video conferences instead of the real webcam. This way I can also control exactly what the webcam shows – zoom / crop included.

The whole mess looks like this visualised in helvum, a patchbay for PipeWire.

Most of this explains itself. The WEBRTC VoiceEngine is the recording of Discord. Other devices may float around but are not used at the moment of this snapshot.

More on this and proper documentation: https://gitlab.freedesktop.org/pipewire/pipewire/-/wikis/Virtual-Devices